.agents/skills/ast-grep/SKILL.md
Guide for writing ast-grep rules for C# structural code search and analysis. Use when users need to search C# codebases using AST patterns, find declarations or invocations by syntax shape, or perform structural queries that go beyond text search.
npx skillsauth add woutervanranst/Arius7 ast-grepInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
This skill helps translate natural language queries into ast-grep rules for C# codebases. It is intentionally optimized for --lang csharp usage.
ast-grep is strongest when the question is about syntax shape rather than compiler semantics.
Use it for:
await, using, catch, and similar syntax formscatch blocksDo not treat it like Roslyn. It does not reliably answer semantic questions such as overload resolution, inheritance, symbol visibility, or type-flow analysis.
Use this skill when users:
logger.LogError(...)await but no try"ast-grep rule or a corrected rule that currently does not matchEstablish:
Write the smallest valid C# snippet that expresses the target shape.
Example target: methods containing await
class Sample
{
async Task LoadAsync()
{
await FetchAsync();
}
}
Prefer this progression:
pattern for direct syntax matcheskind when you need a specific node typehas or inside for relationshipsall, any, not for compositionFor relational rules, default to stopBy: end.
Bare member snippets often parse as local statements instead of top-level declarations.
Example:
async Task LoadAsync() { await FetchAsync(); } parses as local_function_statementclass C { async Task LoadAsync() { await FetchAsync(); } } contains a real method_declarationWhen you need declaration kinds such as method_declaration, prefer pattern.context with selector:
id: async-method-with-await
language: csharp
rule:
all:
- pattern:
context: |
class C
{
async Task M()
{
await FetchAsync();
}
}
selector: method_declaration
- has:
pattern: await $EXPR
stopBy: end
Use --debug-query=pattern to see how ast-grep interprets your pattern.
ast-grep run --pattern 'class C { async Task LoadAsync() { await FetchAsync(); } }' \
--lang csharp \
--debug-query=pattern
Use this to confirm:
kindmethod_declaration or local_function_statement.cs fileFor C#, testing against a temporary file is usually clearer than piping fragments through stdin.
ast-grep scan --rule rule.yml /path/to/test-file.cs
For simple searches:
ast-grep run --pattern 'logger.LogError($EXCEPTION, $MESSAGE)' --lang csharp src
For relational or composite rules:
ast-grep scan --rule rule.yml src
ast-grep run --pattern 'class C { void M() { logger.LogError(ex, message); } }' \
--lang csharp \
--debug-query=pattern
ast-grep run --pattern 'class C { void M() {} }' --lang csharp --debug-query=cst
ast-grep run --pattern 'class C { void M() {} }' --lang csharp --debug-query=ast
ast-grep run --pattern 'await $EXPR' --lang csharp src
ast-grep scan --rule my_rule.yml src
Inline rules are fine for short iterations, but YAML files are easier to debug once the rule uses all, has, inside, or not.
ast-grep scan --inline-rules 'id: log-error-in-catch
language: csharp
rule:
pattern: logger.LogError($EXCEPTION, $MESSAGE)
inside:
pattern:
context: |
try
{
DoWork();
}
catch (Exception ex)
{
logger.LogError(ex, "failed");
}
selector: catch_clause
stopBy: end' src
If matching declarations, wrap the example in a class or namespace and select the declaration node you actually care about.
Good:
pattern:
context: |
class C
{
public string Name { get; set; }
}
selector: property_declaration
Risky:
pattern: public string Name { get; set; }
Common kinds that are often useful in this repo style of work:
class_declarationmethod_declarationproperty_declarationinvocation_expressionargument_listattribute_listcatch_clauseawait_expressionlocal_declaration_statementusing_directiveAlways verify with --debug-query instead of trusting memory.
For many C# searches, a direct invocation pattern is enough:
rule:
pattern: logger.LogError($EXCEPTION, $MESSAGE)
Only add kind: invocation_expression when the simpler version is ambiguous or you need to compose more conditions.
has for body contentsFind methods that contain await:
id: methods-containing-await
language: csharp
rule:
all:
- kind: method_declaration
- has:
pattern: await $EXPR
stopBy: end
inside for contextFind LogError calls inside catch clauses:
id: log-error-in-catch
language: csharp
rule:
pattern: logger.LogError($EXCEPTION, $MESSAGE)
inside:
kind: catch_clause
stopBy: end
not carefullyFind async methods that await but do not contain a try block:
id: async-await-without-try
language: csharp
rule:
all:
- kind: method_declaration
- has:
pattern: await $EXPR
stopBy: end
- not:
has:
pattern:
context: |
try
{
DoWork();
}
catch (Exception ex)
{
Handle(ex);
}
selector: try_statement
stopBy: end
This is syntax-only. It does not prove exception handling is sufficient.
If a C# rule does not match:
--debug-query=patternpattern.context plus selectorstopBy: end on has or insideDetailed syntax reference lives in:
references/rule_reference.mdThat reference is also C#-first and uses --lang csharp examples throughout.
testing
Verify implementation matches change artifacts. Use when the user wants to validate that implementation is complete, correct, and coherent before archiving.
data-ai
Sync delta specs from a change to main specs. Use when the user wants to update main specs with changes from a delta spec, without archiving the change.
development
Guided onboarding for OpenSpec - walk through a complete workflow cycle with narration and real codebase work.
tools
Start a new OpenSpec change using the experimental artifact workflow. Use when the user wants to create a new feature, fix, or modification with a structured step-by-step approach.