원클릭으로
writing-macros
How to write .csx macros for Visual Studio automation in the Macros repo
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
How to write .csx macros for Visual Studio automation in the Macros repo
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
{what this skill teaches agents}
Troubleshoot Macros .csx scripts: command names, null state, timing, and runtime vs IntelliSense
Use EnvDTE80.DTE2 inside Macros .csx files to inspect and automate Visual Studio
Use the Community.VisualStudio.Toolkit facade from inside Macros .csx files
Trigger syntax for Macros .csx files: events, commands, filters, and header edits
{what this skill teaches agents}
| name | writing-macros |
| description | How to write .csx macros for Visual Studio automation in the Macros repo |
| domain | visual-studio-macros |
| confidence | high |
| source | derived from Macros.Engine codegen, helpers, globals, and docs |
Write or edit Macros .csx files that automate Visual Studio from inside the Macros extension.
Use this skill when the user wants a new macro, wants to edit an existing macro, or asks what a valid .csx macro file should look like in this repo.
Do not use this skill for VSIX authoring, MEF parts, tool windows, commands, or other Visual Studio extensibility patterns.
The generator emits this header shape before the script body:
// Macro: My Macro
// Recorded: 2026-05-02T17:10:39Z
// Steps: 3
// Generated by Macros — edit freely.
//
// To make this macro run automatically, add ONE OR MORE of these lines
// to the comment block above (remove the leading "// EXAMPLE: " prefix):
// EXAMPLE: // @trigger Build.SolutionBuildDone
// EXAMPLE: // @trigger Document.Saved when filename=*.cs
// EXAMPLE: // @trigger BeforeCommand File.Save
// EXAMPLE: // @trigger AfterCommand Build.BuildSolution
// See Tools → Options → Macros → Available Triggers for the full list.
// IntelliSense + script globals come from this shim (auto-generated by the Macros VSIX).
#load ".intellisense/Macros.Intellisense.csx"
await TypeAsync("Hello from a macro");
await ExecuteCommandAsync("Edit.FormatDocument");
Keep trigger comments in the top block. Put executable code after #load.
#load directiveAlways keep this line near the top:
#load ".intellisense/Macros.Intellisense.csx"
What it does:
Context, and TriggerRuntime behavior:
#load and returns empty source#r or using lines neededDo not add standard #r directives for EnvDTE, EnvDTE80, Toolkit, or Macros.Engine.
Do not add standard using statements just to get macro basics working.
The shim and script host already provide the common references/imports.
These helpers are available without extra imports:
await TypeAsync("text");
await MoveCaretAsync(3, 1);
await SelectAsync(3, 1, 3, 10);
await ExecuteCommandAsync("Edit.FormatDocument");
await RunCommandAsync(new Guid("00000000-0000-0000-0000-000000000000"), 42);
await OpenFileAsync(@"C:\Repos\MySolution\Program.cs");
await WaitAsync(250);
Use them as the default vocabulary for text insertion, selection, command replay, file opening, and timing.
The macro host exposes these names directly:
DTE — EnvDTE80.DTE2VS — the Community.VisualStudio.Toolkit facadeContext — IMacroContextTrigger — the current macro trigger objectif (DTE.ActiveDocument != null)
{
await VS.StatusBar.ShowMessageAsync($"Running {Context.MacroName}");
}
await TypeAsync("// TODO: follow up\n");
await ExecuteCommandAsync("Edit.FormatDocument");
await ExecuteCommandAsync("File.SaveSelectedItems");
await OpenFileAsync(@"C:\Repos\MySolution\README.md");
await MoveCaretAsync(1, 1);
await TypeAsync("# Notes\n\n");
var sel = DTE.ActiveDocument?.Selection as EnvDTE.TextSelection;
if (sel?.IsEmpty == false)
{
sel.Insert(sel.Text.ToUpperInvariant(), (int)EnvDTE.vsInsertFlags.vsInsertFlagsInsertAtStart);
}
Main() neededawait with every async helper callDTE or VSMacros run in-process inside Visual Studio through Roslyn C# scripting.
await Microsoft.CodeAnalysis.CSharp.Scripting.CSharpScript.RunAsync(
source,
globals: new MacroGlobals(dte, context));
That means top-level await works, real globals are injected at runtime, and helper methods can marshal to the UI thread before touching the IDE.
#load line#r and standard using linesTypeAsync, ExecuteCommandAsync, OpenFileAsync, and WaitAsyncDTE and VS only when the helper vocabulary is not enough