Skip to content

Commit d4858e2

Browse files
sdsrssclaude
andcommitted
feat(plugin): switch hooks from advisory suggestions to proactive result injection
Replace 3 advisory PreToolUse hooks (pre-search-guide, pre-glob-guide, pre-explore-guide) that had near-zero adoption with a push-based model: - UserPromptSubmit: per-type cooldowns (impact 30s / overview 5min / callgraph 1min / search 1min) replace single global cooldown; add intentModify detection with word boundaries; add 8+ char plain-word fallback for symbol extraction; add semantic output prefixes - PreToolUse(Edit): rewrite from one-time reminder to auto-impact injection — parses function signatures from old_string, runs `impact --json`, injects result when callers >= 2 - hooks.json: 6 hook entries → 4; adjusted timeouts Bump version to 0.7.11. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent c0607d8 commit d4858e2

17 files changed

Lines changed: 191 additions & 206 deletions

File tree

.claude-plugin/marketplace.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@
55
},
66
"metadata": {
77
"description": "AST knowledge graph plugin for Claude Code — semantic search, call graph, HTTP tracing, impact analysis",
8-
"version": "0.7.10"
8+
"version": "0.7.11"
99
},
1010
"plugins": [
1111
{
1212
"name": "code-graph-mcp",
1313
"source": "./claude-plugin",
1414
"description": "AST knowledge graph for intelligent code navigation — auto-indexes your codebase and provides semantic search, call graph traversal, HTTP route tracing, and impact analysis via MCP tools",
15-
"version": "0.7.10",
15+
"version": "0.7.11",
1616
"author": {
1717
"name": "sdsrs"
1818
},

CLAUDE.md

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -61,16 +61,13 @@ Code graph tools are available via MCP. The MCP server injects `instructions` at
6161

6262
<claude-mem-context>
6363
### Last Session
64-
Request: Debug and fix code-graph-mcp CLI command errors related to symbol lookup and callgraph functionality
65-
Completed: Identified two distinct error categories in cli.rs: (1) Symbol not found error in cmd_refs function, (2) Usage/callgrap…
66-
Remaining: Fix cmd_refs symbol resolution in cli.rs; Fix callgraph error handling for empty/invalid queries; Verify both commands …
67-
Next: Review cmd_refs implementation for symbol lookup logic; Add empty query validation before callgraph execution; Run inte…
64+
Request: 提交并push代码,发布新版本,包括release版本,查看github上有没有错误。
6865

6966
### Key Context
67+
- [bugfix] Error: session-init.test.js: TAP version 13 # Subtest: syncLifecycleConfig is … (#5202)
68+
- [bugfix] Error: user-prompt-context.js, session-init.js, mcp-launcher.js: TAP version 13… (#5200)
69+
- [bugfix] Error: cli.rs: struct Database src/storage/db.rs:30-33 fn McpSe… (#5096)
7070
- [bugfix] Error: cli.rs: code-graph Symbol not found: --- code-graph No r… (#5079)
7171
- [bugfix] Error: cli.rs: Error: Usage: code-graph-mcp search <query> --jso… (#5077)
72-
- [refactor] Test and refine code-graph-mcp CLI argument validation (#5045)
73-
- [bugfix] Error: cli.rs: Error: Symbol 'run_ful' not found in index. EXIT:1 (#5044)
74-
- [bugfix] Error: queries.rs: in_progress fix(clippy): replace needless range … (#5011)
7572

7673
</claude-mem-context>

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "code-graph-mcp"
3-
version = "0.7.10"
3+
version = "0.7.11"
44
edition = "2021"
55

66
[features]

claude-plugin/.claude-plugin/plugin.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"author": {
55
"name": "sdsrs"
66
},
7-
"version": "0.7.10",
7+
"version": "0.7.11",
88
"keywords": [
99
"code-graph",
1010
"ast",

claude-plugin/hooks/hooks.json

Lines changed: 5 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -2,48 +2,15 @@
22
"hooks": {
33
"PreToolUse": [
44
{
5-
"matcher": "tool == \"Grep\"",
6-
"hooks": [
7-
{
8-
"type": "command",
9-
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/pre-search-guide.js\"",
10-
"timeout": 2
11-
}
12-
],
13-
"description": "Suggest code-graph alternatives on first Grep call"
14-
},
15-
{
16-
"matcher": "tool == \"Glob\"",
17-
"hooks": [
18-
{
19-
"type": "command",
20-
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/pre-glob-guide.js\"",
21-
"timeout": 2
22-
}
23-
],
24-
"description": "Suggest code-graph alternatives on first Glob call"
25-
},
26-
{
27-
"matcher": "tool == \"Agent\"",
28-
"hooks": [
29-
{
30-
"type": "command",
31-
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/pre-explore-guide.js\"",
32-
"timeout": 2
33-
}
34-
],
35-
"description": "Suggest code-graph tools before spawning Explore agents"
36-
},
37-
{
38-
"matcher": "tool == \"Edit\" || tool == \"Write\"",
5+
"matcher": "tool == \"Edit\"",
396
"hooks": [
407
{
418
"type": "command",
429
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/pre-edit-guide.js\"",
43-
"timeout": 2
10+
"timeout": 4
4411
}
4512
],
46-
"description": "Remind about impact_analysis before first code modification"
13+
"description": "Auto-inject impact analysis when editing function definitions with 2+ callers"
4714
}
4815
],
4916
"PostToolUse": [
@@ -66,10 +33,10 @@
6633
{
6734
"type": "command",
6835
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/user-prompt-context.js\"",
69-
"timeout": 4
36+
"timeout": 5
7037
}
7138
],
72-
"description": "Inject relevant code-graph context based on user's question"
39+
"description": "Inject code-graph structural context (impact, overview, callgraph) based on user intent"
7340
}
7441
],
7542
"SessionStart": [
Lines changed: 97 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,105 @@
11
#!/usr/bin/env node
22
'use strict';
3-
// PreToolUse hook: On FIRST Edit/Write call per session window, remind Claude
4-
// to check impact analysis before modifying functions. Fast and non-blocking.
3+
// PreToolUse(Edit) hook: auto-inject impact analysis when editing function definitions.
4+
// Only fires when:
5+
// 1. The old_string contains a function/method definition (signature being modified)
6+
// 2. The symbol has 2+ production callers (high impact)
7+
// 3. Same symbol not queried in last 2 minutes
8+
// Silently exits otherwise — zero noise for normal edits.
9+
const { execFileSync } = require('child_process');
510
const fs = require('fs');
611
const path = require('path');
712
const os = require('os');
813

9-
const flag = path.join(os.tmpdir(), '.code-graph-edit-guided');
10-
const WINDOW_MS = 2 * 60 * 60 * 1000; // 2 hours
14+
const cwd = process.cwd();
15+
const dbPath = path.join(cwd, '.code-graph', 'index.db');
16+
if (!fs.existsSync(dbPath)) process.exit(0);
1117

18+
// --- Parse tool input ---
19+
let input;
1220
try {
13-
const stat = fs.statSync(flag);
14-
if (Date.now() - stat.mtimeMs < WINDOW_MS) process.exit(0);
15-
} catch { /* first time */ }
16-
17-
fs.writeFileSync(flag, '');
18-
process.stdout.write(
19-
'[code-graph] Before modifying functions, consider checking blast radius:\n' +
20-
' code-graph-mcp impact <function_name>\n'
21-
);
21+
input = JSON.parse(fs.readFileSync('/dev/stdin', 'utf8'));
22+
} catch { process.exit(0); }
23+
24+
const oldStr = (input.tool_input && input.tool_input.old_string) || '';
25+
if (!oldStr || oldStr.length < 10) process.exit(0);
26+
27+
// --- Extract function/method signature from the edited text ---
28+
// Match function definitions across languages: Rust, JS/TS, Python, Go, Java/C#/Kotlin, Ruby, PHP
29+
const fnPatterns = [
30+
/(?:pub\s+)?(?:async\s+)?fn\s+(\w+)/, // Rust
31+
/(?:export\s+)?(?:async\s+)?function\s+(\w+)/, // JS/TS
32+
/(?:const|let|var)\s+(\w+)\s*=\s*(?:async\s+)?(?:\([^)]*\)|_)\s*=>/, // JS arrow
33+
/(?:async\s+)?(\w+)\s*\([^)]*\)\s*\{/, // JS method / Go func
34+
/def\s+(\w+)/, // Python/Ruby
35+
/func\s+(\w+)/, // Go/Swift
36+
/(?:public|private|protected|static|override|virtual|abstract|internal)\s+\S+\s+(\w+)\s*\(/, // Java/C#/Kotlin
37+
/(?:public\s+)?function\s+(\w+)/, // PHP
38+
];
39+
40+
let symbol = null;
41+
for (const pat of fnPatterns) {
42+
const m = oldStr.match(pat);
43+
if (m) {
44+
// Find the first captured group
45+
symbol = m[1] || m[2];
46+
break;
47+
}
48+
}
49+
50+
if (!symbol || symbol.length < 3) process.exit(0);
51+
52+
// Skip common patterns that aren't real function names
53+
if (/^(if|for|while|switch|catch|else|return|new|get|set|try)$/i.test(symbol)) {
54+
process.exit(0);
55+
}
56+
57+
// --- Per-symbol cooldown: 2 minutes ---
58+
const cooldownFile = path.join(os.tmpdir(), `.cg-impact-${symbol}`);
59+
try {
60+
if (Date.now() - fs.statSync(cooldownFile).mtimeMs < 120000) process.exit(0);
61+
} catch { /* first time for this symbol */ }
62+
63+
// --- Run impact analysis (JSON mode for programmatic parsing) ---
64+
let jsonResult;
65+
try {
66+
const raw = execFileSync('code-graph-mcp', ['impact', symbol, '--json'], {
67+
cwd,
68+
timeout: 2500,
69+
encoding: 'utf8',
70+
stdio: ['pipe', 'pipe', 'pipe'],
71+
});
72+
jsonResult = JSON.parse(raw);
73+
} catch {
74+
// Symbol not found, timeout, or parse error — exit silently
75+
process.exit(0);
76+
}
77+
78+
// --- Only inject if high-impact (2+ production callers) ---
79+
const directCallers = jsonResult.direct_callers || 0;
80+
const totalCallers = jsonResult.total_callers || 0;
81+
const affectedFiles = jsonResult.affected_files || 0;
82+
const risk = jsonResult.risk || 'low';
83+
84+
if (directCallers < 2) process.exit(0);
85+
86+
// Mark cooldown
87+
try { fs.writeFileSync(cooldownFile, ''); } catch { /* ok */ }
88+
89+
// --- Inject compact impact summary ---
90+
const routeCount = jsonResult.affected_routes || 0;
91+
const testCount = jsonResult.tests_affected || 0;
92+
93+
let summary = `[code-graph:impact] ${symbol}() — Risk: ${risk}\n`;
94+
summary += ` ${directCallers} direct callers, ${totalCallers} total across ${affectedFiles} files`;
95+
if (routeCount > 0) summary += `, ${routeCount} routes affected`;
96+
if (testCount > 0) summary += ` (${testCount} tests)`;
97+
summary += '\n';
98+
99+
// List direct callers compactly
100+
const callers = (jsonResult.callers || []).filter(c => c.depth === 1);
101+
if (callers.length > 0) {
102+
summary += ' Callers: ' + callers.map(c => `${c.name} (${c.file})`).join(', ') + '\n';
103+
}
104+
105+
process.stdout.write(summary);

claude-plugin/scripts/pre-explore-guide.js

Lines changed: 0 additions & 24 deletions
This file was deleted.

claude-plugin/scripts/pre-glob-guide.js

Lines changed: 0 additions & 38 deletions
This file was deleted.

claude-plugin/scripts/pre-search-guide.js

Lines changed: 0 additions & 39 deletions
This file was deleted.

0 commit comments

Comments
 (0)