关于 ast-code-graph
这是供编码 Agent 使用的代码结构分析技能,结合 ast-grep、解析脚本和图构建脚本处理结构化搜索与依赖分析。适用于重构、迁移及代码影响排查,而非普通文本查找。
主要功能
- 01按 AST 结构搜索代码
- 02预览并应用结构化重写
- 03分析调用与模块依赖
- 04检测未使用代码和循环导入
- 05导出 JSON 供脚本处理
适用场景
- 01安全重命名代码符号
- 02评估函数变更影响范围
- 03跨项目批量迁移代码模式
- 04查找未使用导出或循环依赖
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ rolandbrecht/ agent-skills --skill ast-code-graph安装后在 Claude Code 中输入/ast-code-graph确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
package-lock.jsonpackage.jsonreferences/ast-grep-cheatsheet.mdgraph-schema.mdquery-patterns.mdscripts/build-graph.pyparse-js.mjstests/test_build_graph.pytest-parse-js.mjstests/fixtures/sample_helper.pysample_module.pysample-cjs.jssample-esm.mjssample-no-exports.js详细介绍
AST 与代码图索引
概述
基于文本的搜索(grep、ripgrep)会查找字符串匹配项。基于 AST 的分析能够理解结构——它能区分函数定义、函数调用、注释和字符串字面量。
主要工具:ast-grep(sg / ast-grep)——一款快速的 Rust 结构化代码搜索、代码检查和重写命令行工具。它使用 tree-sitter 进行解析,并开箱即用地支持 20 多种语言。
**核心原则:**问题涉及代码结构时,使用 AST 分析;问题涉及文本内容时,使用 grep。
触发模式
以下是适合触发此技能的详细示例:
| 类别 | 示例查询 |
|---|---|
| 结构模式搜索 | “没有依赖数组的 useEffect”、“没有 try-catch 的异步函数”、“没有异常处理的 User.objects.get()”、“src/api/ 目录外的 fetch() 调用”、“类方法中的 console.log”、“缺少错误处理的路由处理程序” |
| 批量代码迁移 | 包装所有异步处理程序、将所有 X() 调用替换为 Y()、为所有路由处理程序添加错误处理、跨数百个文件重写已弃用的 API 模式 |
| 依赖项与影响分析 | “analyzePayment 依赖什么”、“更改 X 的影响范围”、“哪些模块依赖 ./lib/database.js”、“直接和传递依赖项” |
| 安全重命名 | “安全地将 fetchUserData 重命名为 getUserData”,在 TypeScript/JavaScript 代码库中跨文件重命名 |
| 死代码与循环导入 | “从未在任何地方导入的导出项”、“导致 webpack 打包问题的循环导入”、未使用的导出项、源代码实际使用了哪些 npm 依赖项 |
| ast-grep 规则 | 编写 ast-grep 规则、sg 模式、ast-grep 语法 |
适用场景
任务涉及以下内容时,请使用此技能:
| 任务 | AST 优于 grep 的原因 |
|---|---|
| 重构 / 重命名 | grep 也会找到注释和字符串中的内容;AST 只会找到符号 |
| 死代码检测 | grep 无法判断某个导出项是否在其他地方实际被导入 |
| 依赖追踪 | “X 依赖哪些模块?”需要理解 import/require |
| 影响分析 | “如果我更改函数 X,会有什么影响?”需要遍历调用图 |
| 循环依赖检测 | 需要构建并分析完整的导入图 |
| 代码迁移 | 在整个代码库中以结构化方式重写已弃用的 API 模式 |
| 自定义代码检查 | 强制执行标准代码检查工具未覆盖的项目特定模式 |
以下情况请勿使用此技能:
- 搜索特定字符串、错误消息或配置值 → 使用
grep - 按名称或扩展名查找文件 → 使用
find/fd - 代码库少于 5 个文件,且你可以全部阅读 → 直接阅读这些文件
前置条件
使用此工具搜索之前,必须检查系统是否已原生安装 ast-grep:
which ast-grep || which sg
如果已安装,请直接使用原生二进制文件进行语义搜索。除非确有必要,不要尝试通过 Docker 容器或复杂的包装脚本运行它。
如果尚未安装,可根据操作系统使用以下方法之一进行安装:
- Node.js:
npm install -g @ast-grep/cli - MacOS(Homebrew):
brew install ast-grep - Rust(Cargo):
cargo install ast-grep --locked
脚本依赖项
用于构建图和解析 AST 的捆绑脚本需要额外的依赖项:
- JS/TS 解析(
scripts/parse-js.mjs):需要 Node.js v14+、acorn和acorn-walk。运行前,请检查它们是否已全局安装,或安装在ast-code-graph目录中。如果尚未安装,可在ast-code-graph目录中运行npm install acorn acorn-walk,或全局运行npm install -g acorn acorn-walk进行安装。 - Python 图构建器(
scripts/build-graph.py):需要 Python 3.8+(仅使用标准库,无外部依赖项)。
阶段 1:搜索——按结构查找代码
ast-grep(推荐——适用于所有语言)
ast-grep 使用与待搜索代码完全相同的模式语法,并通过 $METAVAR 通配符匹配任意 AST 节点。
基本模式搜索:
# Find all calls to console.log
ast-grep -p 'console.log($MSG)' -l js src/
# Find all require() calls
ast-grep -p 'require($MOD)' -l js src/
# Find all async functions
ast-grep -p 'async function $NAME($$$PARAMS) { $$$BODY }' -l js src/
# Find if-else without braces
ast-grep -p 'if ($COND) $STMT' -l js src/
关键元变量语法:
$NAME— 匹配单个 AST 节点(类似正则表达式中的.)。示例:foo($ARG)会匹配foo(a),但不会匹配foo(a, b)。$$$ARGS— 匹配零个或多个节点(类似正则表达式中的.*)。示例:foo($$$ARGS)会匹配foo()、foo(a)和foo(a, b)。这是最常见的陷阱!匹配参数或代码块主体时,除非你明确只想匹配一个节点,否则默认使用$$$。$_— 匿名匹配(当你之后不需要引用它时)。
TypeScript 泛型调用需要单独的模式:
在 TypeScript/TSX 中,像 useState<string>('') 这样的调用会将 <string> 作为单独的 type_arguments AST 节点。模式 useState($$$ARGS) 不会匹配它。要完整覆盖,需要分两次处理:
# Non-generic calls (works for all languages)
ast-grep -p 'useState($$$ARGS)' src/
# Generic TypeScript calls like useState<T>()
ast-grep -p 'useState<$T>($$$ARGS)' -l tsx src/
ast-grep -p 'useState<$T>($$$ARGS)' -l ts src/
如果类型参数可能是 string | null 这样的联合类型,请使用 $$$T 而不是 $T。
搜索并预览重写结果:
# Preview replacing var with const
ast-grep -p 'var $NAME = $VALUE' -r 'const $NAME = $VALUE' -l js src/
# Apply interactively (prompts y/n per match)
ast-grep -p 'var $NAME = $VALUE' -r 'const $NAME = $VALUE' -l js --interactive src/
完整的模式参考请参阅 ast-grep-cheatsheet.md。
阶段 2:将管道 JSON 输出到脚本
通常,仅在终端打印匹配结果并不足够。对于复杂分析,你应该将匹配结果导出为 JSON,再用脚本处理。对于构建图、查找死代码或生成报告,强烈建议这样做。
生成并保存 JSON:
# Export all function definitions to a file for secondary analysis
ast-grep -p 'function $NAME($$$PARAMS) { $$$BODY }' -l typescript --json src/ > functions.json
然后,编写一个简短的 Python 或 Node.js 脚本来读取 functions.json,并提取你需要的特定节点文本、行号或关系!
(例如,解析 JSON,查找命名方式符合特定约定的函数,或提取所有 import 来源来构建依赖关系图。)
阶段 3:复杂工作流(分步操作)
以下是解决复杂结构问题的详细方法:
工作流 A:影响分析(如果我更改 X,会有哪些内容受到影响?)
- 搜索:使用
ast-grep查找目标符号X的定义,并确认其确切名称和模块路径。 - 查找直接调用方:使用
ast-grep查找所有导入X的 import 语句,以及所有对X()的函数调用。将这些结果保存到 JSON 文件中。 - 分析:如果代码库很大,编写一个简短的脚本来解析 JSON,并列出调用
X的文件/函数。 - 迭代(传递调用方):如有必要,对调用
X的函数重复此过程,以构建完整的调用图。(或者,如果这是一个 Python 项目,请使用阶段 4 中所示的捆绑脚本build-graph.py。)
工作流 B:安全重构 / 迁移
- 搜索:运行
ast-grep -p '<pattern>' src/查找所有符合旧模式的匹配项。 - 检查:添加
--json以检查匹配详情,并确认模式没有捕获非预期的代码边界(例如,确认代码块使用的是$$$BODY而不是$BODY)。 - 预览:添加
-r '<rewrite>',查看打印到 stdout 的替换结果。检查一些边缘情况。 - 应用:添加
--interactive以选择性地应用更改;如果你有 100% 的把握,也可以移除--interactive(不过通常还是应该信任但验证)。 - 验证:重新运行搜索模式
ast-grep -p '<pattern>',确认返回 0 个匹配项。
重命名符号需要分多次处理——函数定义、调用位置以及导入/导出语句在结构上属于不同的 AST 节点类型,因此单个模式无法捕获所有内容:
# Pass 1: rename the definition
ast-grep -p 'function oldName($$$P) { $$$B }' -r 'function newName($$$P) { $$$B }' -l ts src/
# Pass 2: rename all call sites (also catches bare references like arr.map(oldName))
ast-grep -p 'oldName($$$ARGS)' -r 'newName($$$ARGS)' -l ts src/
# Pass 3: rename named imports
ast-grep -p 'import { oldName } from $MOD' -r 'import { newName } from $MOD' -l ts src/
注意:AST 匹配会精确匹配标识符,因此 oldNameHelper 不会受到影响——这与简单粗暴的 sed 替换不同。
阶段 4:内置图构建器(备用方案)
虽然 ast-grep + JSON 管道功能强大,但当你需要超越简单模式匹配、完整遍历 AST 时,此技能也包含用于常见图构建任务的内置脚本。
在检查死代码或循环依赖时,尤其适合使用这些脚本。
🤖 给 AI Agent 的注意事项: 调用这些内置脚本时,必须通过将
<SKILL_DIR>替换为此SKILL.md文件所在目录的绝对路径来构造绝对路径。请勿从用户项目根目录使用相对路径执行这些脚本,否则会执行失败。
Python 代码库:
python3 <SKILL_DIR>/scripts/build-graph.py <directory> [flags]
# Common Flags:
# --callers <symbol> (find direct and transitive callers)
# --unused (find dead code / unreferenced symbols)
# --depends-on <mod> (find reverse dependencies)
# --cycles (detect circular imports)
JavaScript/TypeScript 代码库:
# Export a quick symbol list with line numbers
node <SKILL_DIR>/scripts/parse-js.mjs <file> --symbols
快速参考
| 我想要…… | ast-grep 命令 |
|---|---|
查找所有对 foo() 的调用 | ast-grep -p 'foo($$$ARGS)' src/ |
| 查找函数定义 | ast-grep -p 'function $NAME($$$P) { $$$B }' src/ |
| 查找未使用的导入 | ast-grep scan --inline-rules '...' src/ |
将 oldFn 重命名为 newFn | 分 3 轮处理:定义、调用位置、导入(参见工作流 B) |
将 var 转换为 const | ast-grep -p 'var $N = $V' -r 'const $N = $V' --interactive src/ |
| 输出 JSON 供脚本处理 | ast-grep -p '<pattern>' --json src/ > out.json |
| 检查变更影响范围 | 构建图 → 查找所有传递调用者(反向 BFS) |
配套文件
- ast-grep-cheatsheet.md — 完整的 ast-grep 模式和 CLI 参考
- graph-schema.md — 节点和边的类型定义及 ER 图
- query-patterns.md — 附带示例的详细查询模式手册
- scripts/parse-js.mjs — 用于解析 JS/TS AST 的跨平台 Node.js 脚本
- scripts/build-graph.py — Python 代码图构建器