mcpField
A
ast-code-graph

ast-code-graph

github.com/rolandbrecht开发449 次安装5 周前更新
代码仓库

关于 ast-code-graph

这是供编码 Agent 使用的代码结构分析技能,结合 ast-grep、解析脚本和图构建脚本处理结构化搜索与依赖分析。适用于重构、迁移及代码影响排查,而非普通文本查找。

主要功能

  1. 01按 AST 结构搜索代码
  2. 02预览并应用结构化重写
  3. 03分析调用与模块依赖
  4. 04检测未使用代码和循环导入
  5. 05导出 JSON 供脚本处理

适用场景

  1. 01安全重命名代码符号
  2. 02评估函数变更影响范围
  3. 03跨项目批量迁移代码模式
  4. 04查找未使用导出或循环依赖

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/rolandbrecht/agent-skills --skill ast-code-graph

安装后在 Claude Code 中输入/ast-code-graph确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

package-lock.json
package.json
references/
ast-grep-cheatsheet.md
graph-schema.md
query-patterns.md
scripts/
build-graph.py
parse-js.mjs
tests/
test_build_graph.py
test-parse-js.mjs
tests/fixtures/
sample_helper.py
sample_module.py
sample-cjs.js
sample-esm.mjs
sample-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,会有哪些内容受到影响?)
  1. 搜索:使用 ast-grep 查找目标符号 X 的定义,并确认其确切名称和模块路径。
  2. 查找直接调用方:使用 ast-grep 查找所有导入 X 的 import 语句,以及所有对 X() 的函数调用。将这些结果保存到 JSON 文件中。
  3. 分析:如果代码库很大,编写一个简短的脚本来解析 JSON,并列出调用 X 的文件/函数。
  4. 迭代(传递调用方):如有必要,对调用 X 的函数重复此过程,以构建完整的调用图。(或者,如果这是一个 Python 项目,请使用阶段 4 中所示的捆绑脚本 build-graph.py。)
工作流 B:安全重构 / 迁移
  1. 搜索:运行 ast-grep -p '<pattern>' src/ 查找所有符合旧模式的匹配项。
  2. 检查:添加 --json 以检查匹配详情,并确认模式没有捕获非预期的代码边界(例如,确认代码块使用的是 $$$BODY 而不是 $BODY)。
  3. 预览:添加 -r '<rewrite>',查看打印到 stdout 的替换结果。检查一些边缘情况。
  4. 应用:添加 --interactive 以选择性地应用更改;如果你有 100% 的把握,也可以移除 --interactive(不过通常还是应该信任但验证)。
  5. 验证:重新运行搜索模式 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 转换为 constast-grep -p 'var $N = $V' -r 'const $N = $V' --interactive src/
输出 JSON 供脚本处理ast-grep -p '<pattern>' --json src/ > out.json
检查变更影响范围构建图 → 查找所有传递调用者(反向 BFS)

配套文件

评论

支持 Markdown