graphify 的 Git Hook 与 CLAUDE.md 常驻集成:让知识图谱随提交自动保鲜
本文围绕 graphify/skills/copilot/references/hooks.md 这一参考文档展开,讲清 graphify 的两条"常驻化"集成路径:一是通过 graphify hook 安装 git 钩子,让知识图谱在每次 commit(以及分支切换)后自动增量重建;二是通过 graphify claude 把使用规则写进项目 CLAUDE.md,让 Claude Code 会话无需手动触发命令即可始终"先查图、再答问题"。读完后你将掌握两套命令的完整用法、钩子脚本的触发条件与跳过逻辑、跨平台 Python 解释器探测原理,以及 .graphifyrc 等配套配置项,并能在自己的仓库中验证安装状态。
一、两条集成路径解决什么问题
graphify 把代码库、文档、SQL schema 与配置文件解析成可查询的知识图谱,产物默认落在 graphify-out/ 目录(graph.json、GRAPH_REPORT.md 等)。问题在于:代码一直在变,图谱会过期。hooks.md 文档给出的答案是两种互补机制:
- git commit hook:安装一个 post-commit 钩子,在每次提交后自动重建图谱。文档原文强调其特点——"No background process needed - triggers once per commit, works with any editor"(无需常驻后台进程,每次提交触发一次,适用于任何编辑器)。
- 原生 CLAUDE.md 集成:每个项目运行一次
graphify claude install,把使用 graphify 的规则写进本地CLAUDE.md,让 Claude Code 在未来所有会话中都"先查图谱再回答",后续无需再手动输入/graphify。
下面分别深入。
二、Git commit hook:命令与行为
2.1 三个基本命令
文档给出的操作面非常小,只有三条子命令:
graphify hook install # 安装
graphify hook uninstall # 卸载
graphify hook status # 查看状态
CLI 侧的分发逻辑在 cli.py:graphify hook 子命令被路由到 hooks.py 中的 install / uninstall / status 三个函数;传错子命令会打印 Usage: graphify hook [install|uninstall|status] 并以退出码 1 结束。
2.2 每次提交后实际发生了什么
文档描述的流程是:每次 git commit 之后,钩子通过 git diff HEAD~1 检测哪些代码文件发生了变化,仅对这些文件重跑 AST 抽取,然后重建 graph.json 与 GRAPH_REPORT.md;文档和图片类的变更会被钩子忽略——这类变更需要手动运行 /graphify --update。
对照 hooks.py 中生成的 post-commit 脚本模板,可以看到完整的判定链(任何一步命中就静默退出、不做重建):
- rebase/merge/cherry-pick 期间跳过:检查
$GIT_DIR下的rebase-merge、rebase-apply、MERGE_HEAD、CHERRY_PICK_HEAD,避免阻塞git rebase --continue。源码注释还特意指出优先读取 git 导出的GIT_DIR,手动执行时才回落到git rev-parse,因为"在装有杀毒软件的 Windows 机器上每次 git 执行开销超过 1 秒"。 GRAPHIFY_SKIP_HOOK=1手动跳过:环境变量一票否决。- linked worktree 跳过(worktree guard):通过比较
git rev-parse --git-dir与--git-common-dir(都先cd解析成绝对路径)判断当前是否处于git worktree add出来的附属工作树;是则直接退出,避免在主检出的graphify-out/上产生无主增量图并与 CI 的git clean竞态。 - 取变更列表:
CHANGED=$(git diff --name-only HEAD~1 HEAD ...),为空则退出;若变更只有graphify-out/产物(图谱输出被纳入了版本控制时),也直接退出,避免"重建产物又触发重建"的循环。 - 探测可用的 Python 解释器(下一节详述),然后把变更列表通过
GRAPHIFY_CHANGED环境变量传给一个脱离 git 进程的后台 Python 任务。
后台任务的核心在 hooks.py 内嵌的 Python 片段:它从 GRAPHIFY_CHANGED 解析出路径列表,调用 graphify.watch._rebuild_code(root, changed_paths=changed, force=...) 做增量重建,随后 best-effort 刷新工作记忆(graphify.reflect)。几个可配置点值得注意:
GRAPHIFY_REBUILD_TIMEOUT:重建超时(秒),默认 600。POSIX 下用SIGALRM实现,无SIGALRM的平台(Windows)退回threading.Timer看门狗。GRAPHIFY_FORCE:置为1/true/yes时强制全量重建。GRAPHIFY_OUT:输出目录名,默认graphify-out;若该目录下存在.graphify_root标记文件,则以其中记录的路径作为重建根。PYTHONHASHSEED=0:脚本开头显式导出。源码注释解释了原因——networkx 的 louvain 聚类迭代的字符串键集合顺序受 hash 随机化影响,固定种子才能让graphify-out的输出跨次运行可复现。
2.3 跨平台 Python 解释器探测:钩子最"重"的部分
graphify 推荐用 uv tool install 安装,此时解释器藏在隔离 venv 里,PATH 上的只是启动器;而 GUI git 客户端和 CI runner 的 PATH 常常精简到找不到 ~/.local/bin。因此安装钩子时会把执行 graphify hook install 的解释器绝对路径钉死进脚本(_PINNED_PYTHON__ 占位符在 hooks.py 的 _pinned_python() 中替换,仅当路径通过字符白名单校验——不含 $、反引号、引号等 shell 元字符——才允许写入,防止生成脚本被注入)。
钩子运行时按优先级逐级探测(hooks.py 的 _PYTHON_DETECT 模板):
| 优先级 | 探测方式 | 覆盖场景 |
|---|---|---|
| 1 | 安装时钉死的 _PINNED 解释器 |
最常见路径,GUI 客户端/CI 下依然有效 |
| 2 | 读取 graphify-out/.graphify_python 文件 |
跨 uv tool 重装存活;读取值同样走字符白名单 |
| 3 | PATH 上的 graphify 启动器:Windows 布局下尝试 Scripts 目录相邻的 python.exe;POSIX 布局下解析 shebang(head -c 256 + tr -d '\000' 先剥 NUL,且要求首行以 #! 开头,防止把无 shebang 的二进制 trampoline 的字节读进 shell) |
uv tool 在 Windows 上是二进制启动器,无 shebang 可解析 |
| 4 | 扫描 uv tool 环境目录:${UV_TOOL_DIR}、~/.local/share/uv/tools、~/AppData/Roaming/uv/tools 下的 */bin/python 或 */Scripts/python.exe |
前三级全部落空时的 uv 兜底;只有其 python 能 import graphify 才采用 |
| 5 | python3 / python |
系统或 venv 直装 |
每一级都用 importlib.util.find_spec('graphify') 探测可用性而非真正 import——源码注释说明这是性能教训:整体 import 会触发整个包加载(在 AV 扫描的大 site-packages 机器上冷启动 10 秒以上),且旧版本曾同步探测多达四次,"卡住每次 commit"。全部失败时钩子大声报错并以退出码 0 结束(不阻塞 commit),提示"把 graphify 的 bin 目录加入 PATH 或在正确的环境里重跑 graphify hook install"。
2.4 为什么不用 nohup &:脱离启动器
大仓库的全量重建可能非常耗时,post-commit 钩子必须立刻返回。旧实现用 nohup ... & 后台化,但 Git for Windows 自带的 MSYS shell 没有 nohup(也没有 setsid),该行直接失败且 git 仍返回 0——图谱静默过期。现在的方案(hooks.py 的 _LAUNCHER_TEMPLATE)是让 Python 自己做脱离:外层小进程用 subprocess.Popen 拉起真正的重建任务,POSIX 用 start_new_session=True(等价 setsid),Windows 用 CREATE_NO_WINDOW | CREATE_NEW_PROCESS_GROUP(并尝试附加 CREATE_BREAKAWAY_FROM_JOB)。注释里还记录了一个 Windows 11 细节:不能把 CREATE_NO_WINDOW "简化"回 DETACHED_PROCESS,否则每次 commit 都会闪出一个空 Terminal 窗口。
重建子进程的输出统一追加到 ~/.cache/graphify-rebuild.log(可用 GRAPHIFY_REBUILD_LOG 覆盖);commit 时终端会打印一行提示:
[graphify hook] launching background rebuild (log: ~/.cache/graphify-rebuild.log)
这是排查"钩子装了但图谱没更新"的第一现场。
2.5 附带的 post-checkout 钩子与合并驱动
graphify hook install 除了 post-commit 还会安装 post-checkout 钩子(hooks.py):切换分支且新旧 HEAD 不同、graphify-out/ 已存在时,触发一次全量重建(分支切换可能触及任意文件,增量路径不适用;_rebuild_code 内部的 flock 防止 commit 与 checkout 背靠背触发时排队堆积)。它与 post-commit 共享同一套跳过逻辑(rebase/merge、GRAPHIFY_SKIP_HOOK、worktree guard)。
此外,install 还会注册 git 合并驱动(hooks.py 的 _register_merge_driver):
- 写入
git config的merge.graphify.name与merge.graphify.driver(解释器路径同样钉死并双引号包裹,兼容用户名含空格的 Windows 路径); - 在
.gitattributes追加一行<输出目录>/graph.json merge=graphify,实现graph.json的 union 合并;已有条目会保留,重复安装幂等。
graphify hook uninstall 对称地移除两个钩子的 graphify 区段(按 # graphify-hook-start/end 等标记精确切除,其余钩子内容保留)、卸载合并驱动的两条 config 与 .gitattributes 行。
2.6 共存、幂等与配置
文档特别承诺"If a post-commit hook already exists, graphify appends to it rather than replacing it"(已有 post-commit 钩子时是追加而非替换)。源码 hooks.py 的 _install_hook 落实为三种情况:钩子文件不存在则创建(chmod 0o755);存在且含 graphify 标记则原地更新该区段;存在但无标记则追加到文件末尾。重复安装同一版本时返回 already installed at ...。
状态查询 graphify hook status 输出形如:
post-commit: installed
post-checkout: installed
merge driver: registered (graphify-out/graph.json merge=graphify)
viz node limit: 3000
它还承担"配置漂移检测":若 .graphifyrc 里的 viz_node_limit 与已写入钩子的烘焙值不一致,会提示 installed (out of date: ...)。
.graphifyrc(仓库根目录)目前支持 viz_node_limit(非负整数)配置,解析逻辑在 hooks.py。安装时它被烘焙成 export GRAPHIFY_VIZ_NODE_LIMIT="${GRAPHIFY_VIZ_NODE_LIMIT:-<n>}"——:- 形式保证每次提交前手动 GRAPHIFY_VIZ_NODE_LIMIT=... git commit 的临时覆盖优先于项目默认值。
2.7 环境参数速查
| 变量 | 作用 | 默认 |
|---|---|---|
GRAPHIFY_SKIP_HOOK |
置 1 时 commit 与 checkout 钩子都跳过 |
未设置 |
GRAPHIFY_REBUILD_TIMEOUT |
后台重建超时(秒),<=0 关闭 |
600 |
GRAPHIFY_FORCE |
1/true/yes 时强制全量重建 |
未设置 |
GRAPHIFY_OUT |
输出目录名 | graphify-out |
GRAPHIFY_REBUILD_LOG |
重建日志路径 | ~/.cache/graphify-rebuild.log |
GRAPHIFY_VIZ_NODE_LIMIT |
可视化节点上限(项目默认来自 .graphifyrc) |
无 |
GRAPHIFY_MAX_WORKERS |
并行 worker 数;Windows/MSYS 下钩子默认强制为 1(避免 GUI 客户端继承的脆弱管道句柄),显式设置则覆盖 |
平台相关 |
UV_TOOL_DIR |
覆盖 uv tool 环境扫描位置 | 平台默认 |
测试侧对以上行为有大量断言,见 tests/test_hooks.py:幂等安装、追加到已有钩子、钉死解释器路径、不含 nohup、跨平台脱离启动、worktree 守卫、.graphifyrc 解析与覆盖优先级、合并驱动注册/卸载等均有独立用例;tests/test_hook_guard.py 与 tests/test_read_hook.py 则覆盖 agent 端钩子的行为。
三、CLAUDE.md 原生集成:让 Claude Code 常驻使用图谱
3.1 安装与写入的内容
文档给出的命令是:
graphify claude install
其效果是把一个 ## graphify 区段写进项目本地 CLAUDE.md,"instructs Claude to check the graph before answering codebase questions and rebuild it after code changes"。卸载用:
graphify claude uninstall # 移除该区段
实现位于 install.py 的 claude_install(),两个关键设计:
- 幂等的区段管理:通过
_replace_or_append_section(install.py)以## graphify为标记——已存在该区段就原地替换,不存在就追加到文件末尾,不动其他内容。内容完全相同则打印graphify already configured in ... (no change)。 - 区段内容来自打包文件:写入的是 graphify/always_on/claude-md.md 的逐字内容,实际注入的规则是:
## graphify
This project has a knowledge graph at graphify-out/ with god nodes, community structure, and cross-file relationships.
Rules:
- For codebase questions, first run `graphify query "<question>"` when graphify-out/graph.json exists. Use `graphify path "<A>" "<B>"` for relationships and `graphify explain "<concept>"` for focused concepts. These return a scoped subgraph, usually much smaller than GRAPH_REPORT.md or raw grep output.
- If graphify-out/wiki/index.md exists, use it for broad navigation instead of raw source browsing.
- Read graphify-out/GRAPH_REPORT.md only for broad architecture review or when query/path/explain do not surface enough context.
- After modifying code, run `graphify update .` to keep the graph current (AST-only, no API cost).
即一套明确的使用次序:代码库问题先 graphify query(关系用 graphify path,概念用 graphify explain,返回的是远小于全量报告的子图)→ 有 wiki 索引时用它做广度导航 → GRAPH_REPORT.md 只用于宏观架构审查 → 改完代码跑 graphify update .(纯 AST,无 API 开销)保持图谱新鲜。
3.2 顺带安装的 PreToolUse 钩子
claude_install() 在写完 CLAUDE.md 后总是再装一次 Claude Code 的 PreToolUse 钩子(install.py 的 _install_claude_hook):在 .claude/settings.json 的 hooks.PreToolUse 数组中先剔除旧的 graphify 条目(matcher 为 Bash|Grep/Glob|Grep/Read|Glob 且含 "graphify" 字样),再写入新载荷,最后带备份写回。注释说明了为什么"每次重装"——为了让升级时旧版提示词(如 issue #580 之前的措辞)被替换。
两个可选行为值得了解:
- 项目级安装:
--project模式下写的是裸命令graphify ...而不是本机绝对路径,因为此时.claude/settings.json会被提交进共享仓库,写死安装机器的路径对协作者是错的(对应 issue #3129 的注释)。 - strict 模式:开启后每个会话第一次"绕过图谱直接读原始文件"的行为会被钩子拦截,直到先执行过一次
graphify query;也可用GRAPHIFY_HOOK_STRICT=0关闭(解析逻辑见 cli.py,环境变量优先于安装时烘焙的标记)。
claude_uninstall(install.py 起)对称地移除:删除 CLAUDE.md(及其 local-only 变体)中的 ## graphify 区段——删空后整个文件会被删除;同时清理 settings.json 与 settings.local.json 中的 graphify PreToolUse 条目(用户可能把钩子挪到 settings.local.json 以免提交,见 issue #1731 注释)。
四、落地检查清单
graphify hook status:确认 post-commit / post-checkout / merge driver 三行均为 installed/registered,且无out of date提示;- commit 一次代码变更,观察终端打印
launching background rebuild,随后查看~/.cache/graphify-rebuild.log与刷新后的graphify-out/graph.json、GRAPH_REPORT.md; graphify-out/.graphify_python与钩子脚本内的_PINNED路径是否指向一个真正能 import graphify 的解释器(GUI 客户端/CI 环境重点检查);- 文档、图片变更不会触发钩子重建,需要时手动
/graphify --update。
两条路径的定位不同:git 钩子解决"图谱数据的新鲜度"(无人值守、纯 AST、零 API 成本),CLAUDE.md 集成解决"使用行为的新鲜度"(让 agent 每次都走 query/path/explain 这条最短路径)。二者叠加,项目里的知识图谱就能做到"提交即更新、提问即命中"。
参考文件
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0624
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00