Collected sources and patterns will appear here. Add from search or the patterns library.
Self-building Claude skill: search and rewrite code by its syntax-tree STRUCTURE, not text — 'every call to X inside a try block', 'functions with more than N params', structural codemods and lint that regex cannot express. Python reference uses stdlib ast; tree-sitter for other languages. Ships markdown only; the host agent builds the matcher and proves it against an embedded stdlib-only test (verified: catches structure-blind regex builds). Distilled from tree-sitter.
---
name: ast-surgeon
description: >
Search and rewrite code by its STRUCTURE, not its text. Use when the user wants to find
or transform code in ways regex cannot express: "every call to X inside a try block",
"all functions with more than N parameters", "replace this deprecated call shape
everywhere", "find bare excepts", structural lint, or a codemod across a codebase.
SELF-BUILDING: ships no runnable code; on first use it directs the host agent to
construct the matcher in its own sandbox and prove it against a shipped test. Do NOT use
for plain text find/replace (use grep) or for explaining architecture (use a diagram skill).
spec_version: 1
---
# AST Surgeon — self-building payload
Regex matches characters; this matches program structure. "Find calls to foo that sit
inside a try" is invisible to text search because nesting is a tree property, not a string
property. The host agent builds a syntax-tree matcher that respects structure, then proves
it green before use. Ships markdown only. Distilled from S-expression tree pattern matching
(tree-sitter/tree-sitter).
## When to use
Structural code search, structural lint, and codemods: locate nodes by kind and context,
count structural properties, or rewrite a matched shape. Python reference uses stdlib ast;
other languages use tree-sitter grammars with the same query/capture model.
## How this skill runs (self-build loop)
On trigger: (1) check ./.surgeon_cache/v{spec_version}/ and reuse if unchanged; (2) else
build surgeon.py per the Build Brief; (3) write the Acceptance Harness verbatim and run it
— unusable until green; (4) then run the real query/rewrite.
## Build Brief
Construct surgeon.py exposing: find(source: str, query: dict) -> list[dict]
query keys: call (function name at call sites), optional inside (an AST node type, e.g.
"Try"/"For"/"FunctionDef") constraining matches to nodes having an ANCESTOR of that type.
Return one dict per matching call: {line, col, inside} where inside is True iff the match
has an ancestor of the inside type; when inside is given, only such matches are returned.
Implementation: ast.parse; walk tracking ancestor chains (attach parents yourself). A call
to X is an ast.Call whose func is X via ast.Name (X(...)) or ast.Attribute (obj.X(...)).
Sort by (line, col).
Invariants: (1) find(src, {call:name}) returns exactly name's call sites; (2) with inside
set, a match returns IFF it has an ancestor of that type — same-name calls outside are
excluded, inside are included (the load-bearing structural-context invariant regex gets
wrong); (3) absent name -> []; (4) deterministic, sorted by position.
Rewrite (beyond tested core): transform matches with ast.NodeTransformer + ast.unparse, or
for tree-sitter apply text edits at captured byte ranges; always re-parse output to confirm
it compiles.
## Acceptance Harness
Write verbatim to test_surgeon.py and run. Pure, stdlib-only, no network.
from surgeon import find
SRC = '''
def a():
foo()
try:
foo()
bar()
except Exception:
foo()
'''
def run():
all_foo = find(SRC, {"call": "foo"}); assert len(all_foo) == 3
in_try = find(SRC, {"call": "foo", "inside": "Try"}); assert len(in_try) == 2
top = min(m["line"] for m in all_foo)
assert all(m["line"] != top for m in in_try)
assert all(m["inside"] for m in in_try)
assert len(find(SRC, {"call": "bar"})) == 1
assert len(find(SRC, {"call": "bar", "inside": "Try"})) == 1
assert find(SRC, {"call": "nonexistent"}) == []
assert find(SRC, {"call": "foo"}) == all_foo
lines=[m["line"] for m in all_foo]; assert lines == sorted(lines)
print(f"PASS — {len(all_foo)} foo calls, {len(in_try)} inside Try, structure respected")
if __name__ == "__main__": run()
Verified offline: PASS — 3 foo calls, 2 inside Try. A regex-style build that ignores
structure returns 3 for the inside=Try query and fails the harness, confirming teeth.
## Notes
- Polyglot: swap ast for tree-sitter per language; query/capture/rewrite model is identical.
- Safety: always re-parse rewritten output before writing back.
- Anti-staleness: bump spec_version to rebuild against current grammars.
- "Done" means the harness prints PASS.Generated with Gerolamo — competitive technical intelligence
gerolamo.org