Claude Code & Git Integration Guide¶
Integration guide for connecting CodeGraph’s CPG analysis pipeline with Claude Code hooks and Git workflows.
Table of Contents¶
- Overview
- Architecture
- Hook system
- Data flow
- Shared utilities
- Hooks Reference
- SessionStart: Project context
- UserPromptSubmit: Entity enrichment
- PreToolUse: Complexity gate
- PostToolUse: Commit analysis
- Stop: Post-analysis
- Git Integration
- GoCPG git hooks
- CPG freshness and automatic updates
- Configuration
- Claude Code settings
- Project configuration
- Setup
- Troubleshooting
Overview¶
CodeGraph integrates with Claude Code through five hook scripts that inject CPG analysis data into the conversation at different lifecycle points. Combined with GoCPG git hooks that keep the CPG database synchronized, this creates a continuous feedback loop:
- Session starts – Claude receives project context and CPG statistics
- User asks a question – mentioned code entities are resolved against the CPG
- Claude edits a file – high-complexity methods in that file trigger warnings
- Claude commits code – the commit is analyzed for quality metrics, blast radius, and before/after impact
- Claude finishes a response – files mentioned in the response are checked for quality issues
All hooks communicate via JSON on stdin/stdout and return {"additionalContext": "..."} to inject markdown context into the conversation, or {} to remain silent.
Architecture¶
Hook system¶
Claude Code hooks are configured in .claude/settings.json. Each hook fires at a specific lifecycle event and receives event-specific JSON on stdin.
Claude Code Lifecycle
|
+-- SessionStart ---------> session_context.py -----> Project name, language, CPG stats
|
+-- UserPromptSubmit -----> enrich_prompt.py -------> CPG entity lookups for mentioned code
|
+-- PreToolUse (Edit) ----> pre_tool_use.py --------> Complexity warnings before file edit
|
+-- PostToolUse (Bash) ---> commit_analysis.py -----> Commit quality + blast radius + delta
|
+-- Stop -----------------> post_analysis.py -------> Quality check on files in response
Data flow¶
All hooks query the CPG database through one of two paths:
-
GoCPG subprocess (
gocpg.exe query --sql "...") – used by most hooks via_utils.run_gocpg_query(). No Python dependency on DuckDB required. -
DuckDB direct (
import duckdb; con.execute(...)) – used bycommit_analysis.pyfor the full analysis pipeline (CommitAnalyzer,CPGFreshnessChecker). Falls back to subprocess if DuckDB import fails.
Hook Script
|
+-- _utils.get_active_project() --> config.yaml --> db_path
|
+-- _utils.run_gocpg_query(sql, db_path) --> gocpg.exe query --> stdout
| OR
+-- duckdb.connect(db_path) --> direct SQL
|
+-- safe_json_output(markdown) --> {"additionalContext": "..."} --> Claude Code
Shared utilities¶
All hooks import from .claude/hooks/_utils.py:
| Function | Purpose |
|---|---|
get_active_project() |
Reads config.yaml, returns active project dict (name, db_path, language, domain) |
run_gocpg_query(sql, db_path) |
Executes SQL via gocpg.exe query subprocess |
run_cli_query(query) |
Executes natural language query via python -m src.cli query |
extract_entities(text) |
Extracts CamelCase names, snake_case identifiers, and file paths from text |
safe_json_output(context) |
Writes {"additionalContext": context} or {} to stdout |
read_stdin_json() |
Parses JSON from stdin |
Constants: PROJECT_ROOT (two levels up from hooks dir), GOCPG_BINARY (gocpg/gocpg.exe), CONFIG_PATH (config.yaml).
Hooks Reference¶
SessionStart: Project context¶
File: .claude/hooks/session_context.py
Timeout: 10s
Fires: Once when a Claude Code session begins
Reads the active project from config.yaml and runs gocpg stats to get CPG size. Outputs:
## Project Context
Active project: codegraph (python)
Domain: python_generic
### CPG Statistics
- Methods: 52341
- Calls: 111234
- Files: 1847
- Total nodes: 312000
- Total edges: 890000
This gives Claude immediate awareness of the project scope and available CPG data.
UserPromptSubmit: Entity enrichment¶
File: .claude/hooks/enrich_prompt.py
Timeout: 15s
Fires: On every user message
Extracts code entities from the user’s message (CamelCase class names, snake_case function names, file paths) and looks them up in the CPG. Returns location and complexity data for up to 3 entities.
## CodeGraph CPG Context
### CommitAnalyzer
- `analyze_commit` at `src/dogfooding/commit_analyzer.py:298` (complexity: 12)
- `_deduplicate_methods` at `src/dogfooding/commit_analyzer.py:115` (complexity: 3)
### cpg_freshness
- `is_fresh` at `src/dogfooding/cpg_freshness.py:64` (complexity: 2)
- `ensure_fresh` at `src/dogfooding/cpg_freshness.py:98` (complexity: 5)
This helps Claude locate code entities without manual file searching.
Interface Exposure Detection (M1)¶
For each resolved entity, the hook additionally checks which interface layers call it:
### CommitAnalyzer
- `analyze_commit` at `src/dogfooding/commit_analyzer.py:298` (complexity: 12)
**Exposed via:** CLI, MCP, REST API
The lookup_interface_exposure() function searches for calls to the entity from the 5 interface layers (CLI, REST API, TUI, MCP, ACP) via the nodes_call table in the CPG. This helps Claude assess the potential impact of changes on external interfaces.
PreToolUse: Complexity gate¶
File: .claude/hooks/pre_tool_use.py
Timeout: 8s
Fires: Before any Edit or Write tool call
Checks the target file for methods with CC > 15, high fan-out (> 30), TODO/FIXME markers, or deprecated code. Warns Claude before it modifies complex code:
## File Quality Warning
- `classify` (CC: 17, fan_out: 39) -- high complexity, review carefully
- `_score_domain` (CC: 16, has TODO/FIXME)
Non-source files (.md, .json, .yaml, etc.) are silently skipped.
Registration Completeness Check (CR2)¶
When editing files in scenarios/, services/, analysis/, security/ directories, the hook additionally checks whether all public functions in the file are registered in interface layers:
## Registration Warning
**Registration check:** `analyze_security`, `scan_endpoints` not called from any interface layer (CLI/API/TUI/MCP/ACP)
The check_registration_completeness() function extracts public functions (not starting with _ or test_) from the target file via CPG, then checks whether they are called from any interface layer. If some functions are registered and some are not, a warning is produced.
Files in the interface layers themselves (src/cli/, src/api/routers/, src/tui/commands/, src/mcp/tools/, src/acp/) are skipped.
PostToolUse: Commit analysis¶
File: .claude/hooks/commit_analysis.py
Timeout: 60s
Fires: After any Bash tool call containing git commit
The most complex hook. Runs a multi-phase analysis pipeline:
- CPG freshness check (2s) – compares
cpg_git_state.commit_hashtogit rev-parse HEAD - Pre-update metrics capture (~1s) – snapshots current metrics for changed files (enables delta report)
- CPG update with force re-parse (40s) – runs
gocpg update --forcefor accurate metrics - Quality + blast radius analysis (16s) – queries
nodes_methodfor CC, fan-out, TODO/FIXME, deprecated; queriescall_containmentfor callers of changed methods - Delta computation – compares pre-update vs post-update metrics
Output includes before/after impact, quality warnings, and blast radius:
## Commit Analysis Report
**Summary:** 3 files, 45 methods, 2 high-CC, 1 TODO/FIXME, 128 affected callers
**CPG status:** fresh
**Impact of changes:**
- `_get_fallback_domain`: CC 29->8 (-21), FanOut 18->5 (-13)
**High complexity methods:**
- `classify` (CC: 17)
**Blast radius:** 128 callers affected
- `classify` called by: `run_intent_classifier`, `IntentBenchmark._evaluate_single` +126 more
The hook only fires for git commit commands. Non-commit Bash commands (ls, git status, etc.) produce empty {}.
Fallback path: If DuckDB import fails, falls back to subprocess-based analysis (simpler, without blast radius or delta report).
Extended Analysis Phases (Code Review v3)¶
Beyond the base pipeline, the hook performs extended analysis:
Interface Impact Detection (CR1). If changed files are in interface layers (CLI, REST API, TUI, MCP, ACP), the hook reports which layers are affected and how many methods changed:
**Interface Impact:**
- CLI: 3 methods affected in `src/cli/audit_commands.py`
- MCP: 1 method affected in `src/mcp/tools/search.py`
Cross-Module Dependency Alerts (CR3). The hook analyzes which adjacent interface layers may need updates when one layer changes.
Go CPG Blast Radius (L1). If a Go CPG database is configured (go_db_path), the hook queries call_containment in both databases for a combined Python + Go blast radius.
Story Coverage Delta (L4). If interface impact is detected, the hook checks for story coverage gaps – for example, a function removed from CLI but still present in API.
Stop: Post-analysis¶
File: .claude/hooks/post_analysis.py
Timeout: 10s
Fires: When Claude finishes a response (excluding error stops)
Scans Claude’s response text for source file paths (src/..., pkg/..., etc.) and checks each file for quality issues via CPG:
- TODO/FIXME markers
- Debug code
- Complexity spikes (methods with CC significantly above file average)
- Fan-out hotspots (fan_out > 30)
- Deprecated methods
## Post-Analysis
- `src/workflow/copilot.py`: complexity spikes: `classify` (CC:17), fan-out hotspots: `classify` (fan_out:39)
- `src/dogfooding/commit_analyzer.py`: 2 methods with TODO/FIXME markers
This surfaces quality issues in files that Claude discussed or modified during the conversation.
Post-Analysis Test & Registration Checks (L2)¶
The Stop hook additionally checks:
- Missing tests: whether changed files contain public functions without corresponding tests
- Unregistered interfaces: whether new functions are not called from any interface layer
Git Integration¶
GoCPG git hooks¶
GoCPG can install git post-commit hooks that trigger an incremental CPG update after every commit:
gocpg/gocpg.exe hooks install --repo=. --db=data/projects/codegraph.duckdb
This creates .git/hooks/post-commit which runs gocpg update in the background. The CPG stays synchronized with the latest code without manual intervention.
Check hook status:
gocpg/gocpg.exe hooks status --repo=.
Remove hooks:
gocpg/gocpg.exe hooks uninstall --repo=.
CPG freshness and automatic updates¶
The CPGFreshnessChecker (src/dogfooding/cpg_freshness.py) manages CPG synchronization:
is_fresh()– comparescpg_git_state.commit_hash(stored in DuckDB) againstgit rev-parse HEADcommits_behind()– counts how many commits the CPG is behind HEADensure_fresh(force=True)– runsgocpg update --forceif stale
The --force flag is critical: incremental updates (gocpg update without --force) skip MethodMetricsPass, leaving cyclomatic_complexity, fan_in, and fan_out at 0 for new/modified methods. Force re-parse runs the full pipeline including metrics computation.
Interaction between git hooks and Claude Code hooks:
Developer commits (terminal) Developer commits (Claude Code)
| |
v v
git post-commit hook PostToolUse hook fires
| |
v +-- CPG freshness check
gocpg update (incremental) | +-- Pre-update metrics capture
| +-- gocpg update --force (full re-parse)
v +-- Quality + blast radius analysis
CPG updated (CC=0 for new) +-- Delta report (before->after)
|
v
Report injected into conversation
When both hooks are active, the git post-commit hook may run concurrently with the Claude Code PostToolUse hook. The Claude Code hook handles this gracefully – if the DuckDB file is locked by another gocpg.exe process, it catches the error and falls back to subprocess queries or produces partial output.
Configuration¶
Claude Code settings¶
The hook configuration lives in .claude/settings.json:
{
"hooks": {
"SessionStart": [{
"matcher": "",
"hooks": [{
"type": "command",
"command": "python .claude/hooks/session_context.py",
"timeout": 10000
}]
}],
"UserPromptSubmit": [{
"matcher": "",
"hooks": [{
"type": "command",
"command": "python .claude/hooks/enrich_prompt.py",
"timeout": 15000
}]
}],
"PreToolUse": [{
"matcher": "",
"hooks": [{
"type": "command",
"command": "python .claude/hooks/pre_tool_use.py",
"timeout": 8000
}]
}],
"PostToolUse": [{
"matcher": "Bash",
"hooks": [{
"type": "command",
"command": "python .claude/hooks/commit_analysis.py",
"timeout": 60000
}]
}],
"Stop": [{
"matcher": "",
"hooks": [{
"type": "command",
"command": "python .claude/hooks/post_analysis.py",
"timeout": 10000
}]
}]
}
}
Matcher format: The matcher field is a regex string pattern, not an object. Use "" to match all events or "Bash" to match only the Bash tool. Object format ({"tools": ["Bash"]}) is no longer supported.
Timeout: In milliseconds. If a hook exceeds its timeout, Claude Code kills the process and continues without the hook’s output.
Project configuration¶
Hooks read the active project from config.yaml:
projects:
active: codegraph
registry:
codegraph:
db_path: data/projects/codegraph.duckdb
source_path: .
language: python
domain: python_generic
The db_path is resolved relative to the project root. All hooks use _utils.get_active_project() to look up this configuration.
Setup¶
One-command setup¶
python -m src.cli.import_commands dogfood setup --repo . --db data/projects/codegraph.duckdb
This installs GoCPG git hooks and verifies the Claude Code hook configuration.
Manual setup¶
-
Ensure GoCPG binary exists at
gocpg/gocpg.exe(or setGOCPG_PATHenvironment variable) -
Ensure CPG database exists – import the project if needed:
bash python -m src.cli import . --language python -
Install GoCPG git hooks:
bash gocpg/gocpg.exe hooks install --repo=. --db=data/projects/codegraph.duckdb -
Copy
.claude/settings.jsonwith hook configuration (see Claude Code settings above) -
Verify with
/doctorin Claude Code – should show no settings errors
Verify¶
# Check dogfooding pipeline status
python -m src.cli.import_commands dogfood status
# Check GoCPG hooks
gocpg/gocpg.exe hooks status --repo=.
# Test commit_analysis.py manually
echo '{"tool":"Bash","tool_input":{"command":"git commit -m \"test\""},"tool_result":"ok"}' | python .claude/hooks/commit_analysis.py
Troubleshooting¶
Hook produces empty {} for all events:
- Check that config.yaml has an active project with valid db_path
- Verify the DuckDB file exists at the configured path
- Ensure gocpg/gocpg.exe exists and is executable
Entity enrichment finds nothing:
- The CPG may not contain the queried entity. Run gocpg query --sql "SELECT COUNT(*) FROM nodes_method" to verify CPG is populated
- Entity extraction only matches CamelCase names (> 3 chars), snake_case names (> 5 chars), and source file paths
Complexity gate never fires:
- Only triggers for Edit and Write tools, not Bash
- Only checks source files (.py, .go, .ts, etc.)
- Threshold is CC > 15 – lower values are not reported
Commit analysis shows stale metrics (CC=0):
- The hook uses --force flag by default. If metrics show 0, the force re-parse may have timed out (40s budget)
- Check if another gocpg.exe process is running and holding a DuckDB lock
- Try manual force update: gocpg/gocpg.exe update --force --input=. --output=<db>
Delta report not appearing:
- Delta report only appears when CPG was stale before the update
- If GoCPG git hooks or gocpg watch already updated the CPG, there are no pre-update metrics to compare
DuckDB lock error:
- Another gocpg.exe process is holding the file. The hook handles this gracefully by falling back to subprocess queries
- In production, this is rare since the hook fires after the Bash command completes
/doctor shows settings errors:
- Ensure all matcher fields are strings ("" or "Bash"), not objects
- Check JSON syntax in .claude/settings.json