让 Claude Code 会话启动时静默预热 CodeGraph

缘起

CodeGraph 对代码理解很有用,但它依赖项目本地索引。如果每次进入项目后才想起手动初始化,既容易遗漏,也会把“环境准备”混进正常对话。更糟的是,初始化命令的输出可能进入上下文,干扰模型判断

我的目标很简单:Claude Code 会话开始时自动准备 CodeGraph,但这件事不应该打扰人,也不应该打扰模型

方案

把初始化放进 Claude Code 的全局 SessionStart hook。这样每次启动会话、清空上下文、压缩上下文后,Claude 都会先静默尝试一次 codegraph init。

全局配置只需要关注 hook 片段

{
  "hooks": {
    "SessionStart": [
      {
        "matcher": "startup",
        "hooks": [
          {
            "type": "command",
            "command": "python3 /home/xxx/.claude/hooks/codegraph-init.py",
            "timeout": 10
          }
        ]
      },
      {
        "matcher": "clear",
        "hooks": [
          {
            "type": "command",
            "command": "python3 /home/xxx/.claude/hooks/codegraph-init.py",
            "timeout": 10
          }
        ]
      },
      {
        "matcher": "compact",
        "hooks": [
          {
            "type": "command",
            "command": "python3 /home/xxx/.claude/hooks/codegraph-init.py",
            "timeout": 10
          }
        ]
      }
    ]
  }
}

脚本

脚本只做一件事:判断当前目录是否是项目目录,如果有 .git,就在该目录运行 codegraph init

#!/usr/bin/env python3
from __future__ import annotations

import json

import os
import subprocess
import sys
from pathlib import Path
from typing import Any


def read_hook_input() -> dict[str, Any]:
    try:
        value = json.loads(sys.stdin.read())
    except (json.JSONDecodeError, OSError):
        return {}
    return value if isinstance(value, dict) else {}


def resolve_project_dir(hook_input: dict[str, Any]) -> Path:
    env_value = os.environ.get("CLAUDE_PROJECT_DIR")
    if env_value:
        return Path(env_value).resolve()

    cwd = hook_input.get("cwd")
    if isinstance(cwd, str) and cwd:
        return Path(cwd).resolve()

    return Path.cwd().resolve()


def run_codegraph_init(project_dir: Path) -> None:
    if not (project_dir / ".git").exists():
        return

    try:
        subprocess.run(
            ["codegraph", "init"],
            stdin=subprocess.DEVNULL,
            stdout=subprocess.DEVNULL,
            stderr=subprocess.DEVNULL,
            timeout=45,
            cwd=str(project_dir),
            check=False,
        )
    except (subprocess.TimeoutExpired, FileNotFoundError, PermissionError, OSError):
        pass


def main() -> None:
    hook_input = read_hook_input()
    project_dir = resolve_project_dir(hook_input)
    run_codegraph_init(project_dir)


if __name__ == "__main__":
    main()

全局 hook 会在很多地方触发:home 目录、临时目录、配置目录、非代码目录。如果它过于主动,就可能在不该初始化的地方创建.codegraph

所以这里选择保守策略:只有明确站在一个 Git 项目目录时才初始化

9 个赞