{/* This page is auto-generated from the skill's SKILL.md by website/scripts/generate-skill-docs.py. Edit the source SKILL.md, not this page. */}
Ast Grep
通过 ast-grep 做 AST 感知的结构化代码搜索与重写。
Skill 元数据
| 来源 | 可选——使用 hermes skills install official/software-development/ast-grep 安装 |
| 路径 | optional-skills/software-development/ast-grep |
| 版本 | 1.0.0 |
| 作者 | Yeongyu Kim (code-yeongyu), adapted by Hermes Agent |
| 许可证 | MIT |
| 平台 | linux, macos, windows |
| 标签 | ast, codemod, refactoring, structural-search, code-search, rewrite, tree-sitter |
| 相关 skill | simplify-code、systematic-debugging |
参考:完整 SKILL.md
以下是 Hermes 在触发此 skill 时加载的完整 skill 定义。这是 skill 激活时 agent 所看到的指令内容。
ast-grep
ast-grep(二进制也叫 sg)是一个跨 25 种语言的 AST 感知搜索与重写工具。它把你的 pattern 当作代码,用和解析项目相同的方式解析它,然后做结构化匹配。当你的问题取决于代码形状而非文本字节时,它就是对的工具。
本 skill 在 scripts/ast_grep_helper.py 附带一个 Python 封装,并在 install.sh(POSIX)和 install.ps1(Windows)附带平台安装脚本。封装增加了离线 pattern 校验、两遍写入技巧和二进制自动解析。把它作为默认入口。
上游来源:vendored 自 code-yeongyu/ast-grep-skill(MIT),随 oh-my-openagent 的 shared-skills bundle 发布。
何时使用此 skill
当问题关于代码结构而非字节时使用:
- "找出每个接受
Request参数的函数。" - "把每个
console.log(x)重写为logger.info(x)。" - "去掉每个
as any强转。" - "在整个仓库里把
require(...)替换为import。" - "找出空 catch 块。"
- "把
Optional[X]迁移为X | None。" - "把这个 codemod 应用到这 200 个文件。"
- "跑我们的 YAML lint 规则并列出违规。"
当问题是文本形状(字符串字面量内容、注释、license 头、文件名、跨语言 regex)时,切到 search_files(或直接 rg)。拿不准时问一句:"答案取决于语言的语法树,还是只取决于文件字节?"前者用 ast-grep,后者用 search_files。
Hermes 集成注意:
- 通过
terminal工具运行 helper 和sg。每个 pattern 用单引号包起来,让 shell 永远不展开$VAR。 - 对于围绕匹配的 find→read 链,用
--json-out并配合execute_code处理,而不是管道给解释器。 - 它补充(不替代)Hermes 的
patch工具:patch用于你亲手写的定点编辑;ast-grep 用于跨多处的 pattern 驱动批量重写。
agent 必须内化的三件事
1. ast-grep 不是 regex
通配符是 $VAR(一个 AST 节点)和 $$$(零个或多个节点)。regex 语法会静默失败:
| 你写的 | ast-grep 看到的 | 你想要的 |
|---|---|---|
foo\|bar | foo 和 bar 的按位或 | 跑两次独立搜索 |
.*foo | 无法解析 | $$$ foo(若 $$$ 是节点列表)或用 rg |
\w+ | 无法解析 | $VAR 捕获任意标识符 |
[a-z] | 字符类,无法解析 | 切到 rg |
完整反模式表见 references/pitfalls.md §1。helper 的 validate 子命令会机械地捕获这些——在手工调试"无匹配"之前先调它。
2. pattern 必须是合法代码
pattern 本身必须能解析。def $FN($$$): 会失败,因为末尾的 : 让它不完整;用 def $FN($$$)。没有参数/函数体的 function $NAME 会失败;用 function $NAME($$$) { $$$ }。各语言完整表见 references/pitfalls.md §2。
3. --update-all 和 --json 互斥(静默)
这是脚本化时最大的坑。sg run -p P -r R --json --update-all 返回 JSON 但不改动文件。要既预览又应用,跑两遍:
sg run -p P -r R --json=compact . # 第 1 遍:看会改什么
sg run -p P -r R --update-all . # 第 2 遍:真正应用
helper 在你调 replace --apply 时自动做这件事。读 references/pitfalls.md §9。
封装脚本——scripts/ast_grep_helper.py
单文件 Python 3 标准库封装。每个 OS 都一样。agent 的默认入口。
search——找 pattern 的所有匹配
python scripts/ast_grep_helper.py search 'console.log($MSG)' --lang ts src/
先离线校验 pattern。如果 pattern 看起来像 regex(\w、.*、| 等),helper 带提示退出,绝不调用 sg——省一次往返。传 --force 跳过校验。
标志:
--lang ts(或 25 种语言之一;接受js、py、rs、kt等别名)--globs '!**/*.test.ts'(可重复;前缀!表示排除)-C 3(上下文行数)--json-out(原始 JSON 而非人类可读格式)
replace——按 pattern 重写,默认 dry-run
# Dry-run 预览(默认——不改文件)
python scripts/ast_grep_helper.py replace 'console.log($MSG)' 'logger.info($MSG)' --lang ts src/
# 真正应用
python scripts/ast_grep_helper.py replace 'console.log($MSG)' 'logger.info($MSG)' --lang ts src/ --apply
helper:
- 校验
pattern和rewrite中可提示检测的错误。 - 用
--json=compact跑第 1 遍收集匹配并展示预览。 - 若设了
--apply,用--update-all跑第 2 遍改动文件。
scan——跑 YAML 规则
# 从 cwd 发现 sgconfig.yml 并跑所有规则
python scripts/ast_grep_helper.py scan src/
# 跑单个规则文件
python scripts/ast_grep_helper.py scan -r rules/no-console.yml src/
# 应用自动修复
python scripts/ast_grep_helper.py scan -U src/
# CI 友好的 GitHub 注解
python scripts/ast_grep_helper.py scan --report-style short src/
validate——离线 pattern 检查(不调用 sg)
适用于 CI lint、pre-commit 钩子和快速健全性检查:
python scripts/ast_grep_helper.py validate '\w+' --lang ts
# → exit 2: regex \w not supported. Use $VAR for identifiers.
python scripts/ast_grep_helper.py validate 'console.log($MSG)' --lang ts
# → exit 0: pattern looks plausible for ast-grep.
langs / doctor / install
python scripts/ast_grep_helper.py langs # 列出 25 种支持语言和别名
python scripts/ast_grep_helper.py doctor # 检查 ast-grep 二进制可用性
python scripts/ast_grep_helper.py install # 委托给 install.sh / install.ps1
new 和 test 子命令直接代理到 sg new 和 sg test。
直接用 sg(helper 不够用时)
helper 是有主见的。要完全控制就降到 sg。本 skill 在 references/cli.md 附了一张 CLI 速查表。最小惯用法:
# 搜索
sg run -p 'console.log($MSG)' --lang ts src/
# 带 JSON 搜索,用于脚本
sg run -p 'console.log($MSG)' --lang ts --json=compact src/
# 重写,dry-run
sg run -p 'console.log($MSG)' -r 'logger.info($MSG)' --lang ts --json=compact src/
# 重写,应用
sg run -p 'console.log($MSG)' -r 'logger.info($MSG)' --lang ts --update-all src/
# 从 stdin 读 pattern(适合临时实验)
echo 'console.log("hi")' | sg run -p 'console.log($MSG)' --lang js --stdin
# 调试一个返回 0 匹配的 pattern
sg run -p '<your pattern>' --lang <lang> --debug-query=ast --stdin <<< '<sample-code>'
# 跑 YAML 规则
sg scan src/
# 内联 YAML 规则(一次性)
sg scan --inline-rules '
id: no-todo
language: TypeScript
severity: warning
rule: { pattern: TODO }' src/
在 shell 里直接用 sg 时,始终单引号 pattern,让 $VAR 不被 shell 展开。
决策树——何时用什么
USER asks for "find/rewrite/codemod"
│
├─ structural pattern (function shape, call, class, import, control flow)
│ └→ ast-grep (this skill)
│
├─ text pattern (regex, alternation, character classes, file names)
│ └→ search_files / rg
│
├─ semantic question (what variable does this refer to? does this throw?)
│ └→ LSP tools, TypeScript compiler, Pyright, Semgrep with type inference
│
└─ multiple repos / federated search
└→ a search engine + then ast-grep / rg / LSP per-repo
如果用户说"找出所有"或"每一个",当目标是形状化的(函数、类、调用、import、语句)时默认用 ast-grep。当目标是文本(字符串内容、注释、license 头、文件名、标识符子串)时默认用 search_files。
重写时始终先 dry-run
坏 pattern 会静默改错东西。helper 的 replace 默认 dry-run 就是为此。流程:
- 搜索确认匹配:
helper search '<pattern>' --lang X . - Dry-run 重写:
helper replace '<pattern>' '<rewrite>' --lang X .(不加--apply) - 检查 dry-run 摘要:匹配数、受影响文件、逐处预览。
- 错了:细化 pattern,回到第 1 步。
- 对了:
helper replace '<pattern>' '<rewrite>' --lang X . --apply。
绝不应用你没先 dry-run 过的重写。在 git 仓库里 --apply 后,提交前用 git diff --stat 审查。
当 sg 返回 0 匹配但你知道代码就在那
按优先级:
- 跑
helper validate '<pattern>' --lang <lang>——捕获 regex 误用、缺函数体、Python 末尾冒号。 - 检查
--lang——sg从扩展名推断;如果你传一个.tsx文件却用--lang ts(而非tsx),JSX 解析不了。 - 检查解析后的 pattern:
sg run -p '<pattern>' --lang <lang> --debug-query=ast --stdin <<< '<sample>'。如果显示ERROR节点,pattern 有问题。 - 检查目标文件的 AST:
sg run -p '$_' --lang <lang> --debug-query=cst path/to/file | head -40——找到你想匹配的kind。 - 试试 playground:<https://ast-grep.github.io/playground.html>——贴代码 + pattern,看发生了什么。
不要盲目换变体重试。每次失败都有原因;把它暴露出来。
何时用 YAML 规则 vs 内联 -p pattern
用内联 -p 当:
- 一次性临时查询。
- pattern 简单(无约束、无修复模板)。
- 你在探索。
用 YAML 规则(放在 rules/ 下的文件,通过 sg scan 跑)当:
- pattern 会复用(lint 规则、在 CI 里跑的 codemod)。
- 你需要
constraints、transform、复杂的inside/has或组合逻辑。 - 你想要自动修复(
fix:字段)。 - 你想测试规则(通过
sg test做快照测试)。
完整 YAML 规则 schema 见 references/yaml-rules.md。项目配置(sgconfig.yml、ruleDirs、utilDirs)见 references/sgconfig.md。
输出纪律
sg run --json=compact产生匹配对象数组:{ file, range: {start, end}, text, replacement?, lines, language, ... }。- 不带
--json,sg产生适合终端的人类可读彩色输出。 - helper 默认输出人类可读(file:line:column + 匹配预览)。传
--json-out拿原始 JSON。 - helper 的
replace总会摘要:匹配数、文件数、逐处预览。
向用户摘要时,务必带上受影响文件数,而不只是匹配数。用户关心影响半径。
必读(按优先级)
references/patterns.md——元变量、命名规则、严格度层级。不确定 pattern 为何不匹配时读。references/pitfalls.md——失败模式实战手册。0 匹配让你意外时读。references/recipes.md——按语言复制粘贴的 pattern。开新任务时先读。references/cli.md——sg run、sg scan、sg test、sg new、sg lsp。helper 不够用时读。references/yaml-rules.md——YAML 规则 schema。内联 pattern 不够用时读。references/sgconfig.md——项目级配置。为真实项目配sg scan时读。references/install.md——各 OS 安装方法。仅当install.sh/install.ps1失败时读。
不变量(不要破坏)
- 搜索前先校验。 编程生成 pattern 时,先调
helper validate。它捕获占"0 匹配"调试会话约 70% 的 regex 误用类错误。 - 应用前先 dry-run。 绝不不看匹配就跑
sg run -r ... --update-all。helper 的replace默认强制这一点。 - 两遍写入。 直接用
sg既预览又应用时,跑两次调用——--json会忽略--update-all。 - shell 里单引号 pattern。 用
'$VAR'而非"$VAR"。双引号里 shell 把$VAR展开成空串,破坏 pattern。 - pattern 是代码,不是 regex。 当 pattern 需要
|、.*、\w或[a-z]时,切到 search_files。不要把 ast-grep 硬掰成 regex 形状。 - stdin 必须给
--lang。 用--stdin管道时,显式设--lang;sg无法从扩展名推断。 - Linux:优先用
ast-grep而非sg,因为sg与 util-linux 的setgroups冲突。helper 处理了这一点;如果你直接调sg,alias 一下:alias sg=ast-grep。