@sdsrs/code-graph 0.7.16 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/claude-plugin/.claude-plugin/plugin.json +1 -1
- package/claude-plugin/hooks/hooks.json +3 -14
- package/claude-plugin/scripts/adopt.js +110 -0
- package/claude-plugin/scripts/adopt.test.js +114 -0
- package/claude-plugin/scripts/auto-update.js +1 -26
- package/claude-plugin/scripts/doctor.js +387 -0
- package/claude-plugin/scripts/doctor.test.js +47 -0
- package/claude-plugin/scripts/lifecycle.js +99 -3
- package/claude-plugin/scripts/lifecycle.test.js +78 -0
- package/claude-plugin/scripts/session-init.js +110 -3
- package/claude-plugin/scripts/session-init.test.js +33 -0
- package/claude-plugin/scripts/user-prompt-context.js +4 -0
- package/claude-plugin/scripts/user-prompt-context.test.js +14 -0
- package/claude-plugin/scripts/version-utils.js +49 -0
- package/claude-plugin/scripts/version-utils.test.js +83 -0
- package/claude-plugin/templates/plugin_code_graph_mcp.md +70 -0
- package/package.json +6 -6
|
@@ -6,8 +6,10 @@ const os = require('os');
|
|
|
6
6
|
const fs = require('fs');
|
|
7
7
|
const {
|
|
8
8
|
install, update, readManifest, getPluginVersion, checkScopeConflict,
|
|
9
|
-
cleanupDisabledStatusline, isPluginInactive, readJson,
|
|
9
|
+
cleanupDisabledStatusline, isPluginInactive, readJson, CACHE_DIR,
|
|
10
|
+
clearStalePluginCacheHooks, findStalePluginHooksJson,
|
|
10
11
|
} = require('./lifecycle');
|
|
12
|
+
const { readBinaryVersion, isDevMode, getNewestMtime } = require('./version-utils');
|
|
11
13
|
|
|
12
14
|
function launchBackgroundAutoUpdate(spawnFn = spawn, env = process.env) {
|
|
13
15
|
try {
|
|
@@ -161,6 +163,94 @@ function verifyBinary() {
|
|
|
161
163
|
return { available: true, binary };
|
|
162
164
|
}
|
|
163
165
|
|
|
166
|
+
/**
|
|
167
|
+
* Lightweight consistency checks — called from runSessionInit().
|
|
168
|
+
* Returns an array of issue objects: { id, msg, fix }.
|
|
169
|
+
* Empty array = all consistent (silent).
|
|
170
|
+
*/
|
|
171
|
+
function consistencyCheck(binary) {
|
|
172
|
+
const issues = [];
|
|
173
|
+
|
|
174
|
+
// Check 1: Binary version vs plugin version
|
|
175
|
+
try {
|
|
176
|
+
const pluginVersion = getPluginVersion();
|
|
177
|
+
const binaryVersion = readBinaryVersion(binary);
|
|
178
|
+
if (binaryVersion && binaryVersion !== pluginVersion) {
|
|
179
|
+
issues.push({
|
|
180
|
+
id: 'version-mismatch',
|
|
181
|
+
msg: `Binary v${binaryVersion}, plugin expects v${pluginVersion}`,
|
|
182
|
+
fix: isDevMode() ? 'cargo build --release' : 'code-graph-mcp doctor',
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
} catch { /* skip check on error */ }
|
|
186
|
+
|
|
187
|
+
// Check 2: Source freshness (dev mode only)
|
|
188
|
+
try {
|
|
189
|
+
if (isDevMode()) {
|
|
190
|
+
const srcDir = path.resolve(__dirname, '..', '..', 'src');
|
|
191
|
+
const binaryMtime = fs.statSync(binary).mtimeMs;
|
|
192
|
+
const latestSrcMtime = getNewestMtime(srcDir, '.rs');
|
|
193
|
+
if (latestSrcMtime > binaryMtime) {
|
|
194
|
+
const deltaMin = Math.round((latestSrcMtime - binaryMtime) / 60000);
|
|
195
|
+
issues.push({
|
|
196
|
+
id: 'binary-stale',
|
|
197
|
+
msg: `src/ modified ${deltaMin}min after last build`,
|
|
198
|
+
fix: 'cargo build --release',
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
} catch { /* skip check on error */ }
|
|
203
|
+
|
|
204
|
+
// Check 3: Auto-update incomplete
|
|
205
|
+
try {
|
|
206
|
+
const statePath = path.join(CACHE_DIR, 'update-state.json');
|
|
207
|
+
const state = readJson(statePath);
|
|
208
|
+
if (state && state.updateAvailable && state.binaryUpdated === false) {
|
|
209
|
+
issues.push({
|
|
210
|
+
id: 'update-incomplete',
|
|
211
|
+
msg: `Plugin updated to v${state.latestVersion}, binary not updated`,
|
|
212
|
+
fix: 'code-graph-mcp doctor',
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
} catch { /* skip check on error */ }
|
|
216
|
+
|
|
217
|
+
// Output warnings to stderr
|
|
218
|
+
if (issues.length > 0) {
|
|
219
|
+
const lines = [`[code-graph] ${issues.length} consistency issue(s):`];
|
|
220
|
+
issues.forEach((issue, i) => {
|
|
221
|
+
lines.push(` ${i + 1}. ${issue.msg}`);
|
|
222
|
+
lines.push(` → ${issue.fix}`);
|
|
223
|
+
});
|
|
224
|
+
process.stderr.write(lines.join('\n') + '\n');
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return issues;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Self-heal: Claude Code auto-update can re-populate cache hooks.json from the
|
|
232
|
+
* marketplace source, which would double-fire every hook we registered to
|
|
233
|
+
* settings.json. If our hooks are already in settings.json (install has run),
|
|
234
|
+
* any non-empty cache/marketplace hooks.json is stale — clear it.
|
|
235
|
+
* Gated on settings.json registration so pure plugin-only users (no install
|
|
236
|
+
* script run; cache hooks.json is their only registration) are not broken.
|
|
237
|
+
*/
|
|
238
|
+
function healStaleCacheHooks() {
|
|
239
|
+
try {
|
|
240
|
+
const settings = readJson(path.join(os.homedir(), '.claude', 'settings.json')) || {};
|
|
241
|
+
const manifest = readManifest();
|
|
242
|
+
if (!manifest || !manifest.version) return { checked: false, cleared: 0 };
|
|
243
|
+
const serialized = JSON.stringify(settings.hooks || {});
|
|
244
|
+
if (!serialized.includes('code-graph')) return { checked: false, cleared: 0 };
|
|
245
|
+
const stale = findStalePluginHooksJson();
|
|
246
|
+
if (stale.length === 0) return { checked: true, cleared: 0 };
|
|
247
|
+
const cleared = clearStalePluginCacheHooks();
|
|
248
|
+
return { checked: true, cleared: cleared.length };
|
|
249
|
+
} catch {
|
|
250
|
+
return { checked: false, cleared: 0 };
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
164
254
|
function runSessionInit() {
|
|
165
255
|
if (isPluginInactive()) {
|
|
166
256
|
cleanupDisabledStatusline();
|
|
@@ -177,13 +267,28 @@ function runSessionInit() {
|
|
|
177
267
|
|
|
178
268
|
const lifecycle = syncLifecycleConfig();
|
|
179
269
|
|
|
270
|
+
// Self-heal stale plugin cache hooks.json (prevents double-firing after auto-update).
|
|
271
|
+
// syncLifecycleConfig's install/update path already clears; this catches the
|
|
272
|
+
// 'noop' case where version matches but cache was re-populated externally.
|
|
273
|
+
const cacheHookHeal = healStaleCacheHooks();
|
|
274
|
+
|
|
180
275
|
// Verify binary availability — catch issues early with actionable diagnostics
|
|
181
276
|
const binaryCheck = verifyBinary();
|
|
182
277
|
|
|
183
278
|
const autoUpdateLaunched = launchBackgroundAutoUpdate();
|
|
184
279
|
const indexFreshness = binaryCheck.available ? ensureIndexFresh() : 'skipped';
|
|
185
|
-
|
|
186
|
-
|
|
280
|
+
// CODE_GRAPH_QUIET_HOOKS=1 → skip the 60-line project-map injection; rely
|
|
281
|
+
// on MEMORY.md pointer + on-demand `project_map` tool call instead.
|
|
282
|
+
const quietHooks = process.env.CODE_GRAPH_QUIET_HOOKS === '1';
|
|
283
|
+
const mapInjected = binaryCheck.available && !quietHooks ? injectProjectMap() : false;
|
|
284
|
+
const consistencyIssues = binaryCheck.available
|
|
285
|
+
? consistencyCheck(binaryCheck.binary)
|
|
286
|
+
: [];
|
|
287
|
+
return {
|
|
288
|
+
inactive: false, lifecycle, cacheHookHeal,
|
|
289
|
+
autoUpdateLaunched, indexFreshness, mapInjected, binaryCheck, consistencyIssues,
|
|
290
|
+
quietHooks,
|
|
291
|
+
};
|
|
187
292
|
}
|
|
188
293
|
|
|
189
294
|
/**
|
|
@@ -221,6 +326,8 @@ module.exports = {
|
|
|
221
326
|
ensureIndexFresh,
|
|
222
327
|
injectProjectMap,
|
|
223
328
|
verifyBinary,
|
|
329
|
+
consistencyCheck,
|
|
330
|
+
healStaleCacheHooks,
|
|
224
331
|
runSessionInit,
|
|
225
332
|
};
|
|
226
333
|
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
const test = require('node:test');
|
|
3
3
|
const assert = require('node:assert/strict');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
4
6
|
|
|
5
7
|
const { launchBackgroundAutoUpdate, syncLifecycleConfig, ensureIndexFresh, verifyBinary } = require('./session-init');
|
|
6
8
|
|
|
@@ -70,3 +72,34 @@ test('launchBackgroundAutoUpdate spawns detached silent updater', () => {
|
|
|
70
72
|
assert.equal(calls[0].unrefCalled, true);
|
|
71
73
|
});
|
|
72
74
|
|
|
75
|
+
const { consistencyCheck } = require('./session-init');
|
|
76
|
+
|
|
77
|
+
test('consistencyCheck is exported as a function', () => {
|
|
78
|
+
assert.equal(typeof consistencyCheck, 'function');
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test('consistencyCheck returns empty array when binary version matches plugin', () => {
|
|
82
|
+
const result = consistencyCheck('/tmp/nonexistent-binary');
|
|
83
|
+
assert.ok(Array.isArray(result));
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
test('consistencyCheck returns version-mismatch when versions differ', () => {
|
|
87
|
+
const os = require('os');
|
|
88
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'cc-'));
|
|
89
|
+
const bin = path.join(dir, 'code-graph-mcp');
|
|
90
|
+
fs.writeFileSync(bin, [
|
|
91
|
+
'#!/usr/bin/env bash',
|
|
92
|
+
'if [ "$1" = "--version" ]; then',
|
|
93
|
+
' echo "code-graph-mcp 0.0.1"',
|
|
94
|
+
' exit 0',
|
|
95
|
+
'fi',
|
|
96
|
+
'exit 0',
|
|
97
|
+
].join('\n'));
|
|
98
|
+
fs.chmodSync(bin, 0o755);
|
|
99
|
+
|
|
100
|
+
const issues = consistencyCheck(bin);
|
|
101
|
+
const versionIssue = issues.find(i => i.id === 'version-mismatch');
|
|
102
|
+
assert.ok(versionIssue, 'should detect version mismatch');
|
|
103
|
+
assert.ok(versionIssue.msg.includes('0.0.1'));
|
|
104
|
+
});
|
|
105
|
+
|
|
@@ -53,6 +53,10 @@ function markCooldown(type) {
|
|
|
53
53
|
} catch { /* ok */ }
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
+
// CODE_GRAPH_QUIET_HOOKS=1 → skip passive per-prompt injection entirely.
|
|
57
|
+
// Users opt in to this mode when MEMORY.md + explicit tool calls cover their needs.
|
|
58
|
+
if (process.env.CODE_GRAPH_QUIET_HOOKS === '1') process.exit(0);
|
|
59
|
+
|
|
56
60
|
// --- Read user message ---
|
|
57
61
|
let message;
|
|
58
62
|
try {
|
|
@@ -464,3 +464,17 @@ test('skills: only expected skills exist', () => {
|
|
|
464
464
|
const files = fs.readdirSync(skillsDir).filter(f => f.endsWith('.md')).sort();
|
|
465
465
|
assert.deepEqual(files, ['explore.md', 'index.md']);
|
|
466
466
|
});
|
|
467
|
+
|
|
468
|
+
test('CODE_GRAPH_QUIET_HOOKS=1 short-circuits before reading stdin', () => {
|
|
469
|
+
const { execFileSync } = require('node:child_process');
|
|
470
|
+
const script = path.join(__dirname, 'user-prompt-context.js');
|
|
471
|
+
const out = execFileSync(process.execPath, [script], {
|
|
472
|
+
input: JSON.stringify({ message: 'impact analysis for fn_that_would_trigger_search' }),
|
|
473
|
+
env: { ...process.env, CODE_GRAPH_QUIET_HOOKS: '1' },
|
|
474
|
+
encoding: 'utf8',
|
|
475
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
476
|
+
timeout: 2000,
|
|
477
|
+
});
|
|
478
|
+
// Quiet mode must produce no stdout — no [code-graph:*] prefix, nothing.
|
|
479
|
+
assert.equal(out, '');
|
|
480
|
+
});
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const { execFileSync } = require('child_process');
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
|
|
6
|
+
const VERSION_OUTPUT_RE = /^code-graph-mcp\s+(\d+\.\d+\.\d+)$/;
|
|
7
|
+
|
|
8
|
+
function readBinaryVersion(binaryPath) {
|
|
9
|
+
try {
|
|
10
|
+
const out = execFileSync(binaryPath, ['--version'], {
|
|
11
|
+
timeout: 2000,
|
|
12
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
13
|
+
}).toString().trim();
|
|
14
|
+
const match = out.match(VERSION_OUTPUT_RE);
|
|
15
|
+
return match ? match[1] : null;
|
|
16
|
+
} catch {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function isDevMode() {
|
|
22
|
+
// Always derive from __dirname — CLAUDE_PLUGIN_ROOT can leak from other plugins
|
|
23
|
+
const pluginRoot = path.resolve(__dirname, '..');
|
|
24
|
+
// Dev mode: running from source repo (has Cargo.toml nearby)
|
|
25
|
+
if (fs.existsSync(path.join(pluginRoot, '..', 'Cargo.toml'))) return true;
|
|
26
|
+
// Dev mode: plugin root is a symlink
|
|
27
|
+
try { if (fs.lstatSync(pluginRoot).isSymbolicLink()) return true; } catch { /* ok */ }
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function getNewestMtime(dir, ext = '.rs') {
|
|
32
|
+
let newest = 0;
|
|
33
|
+
try {
|
|
34
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
35
|
+
for (const entry of entries) {
|
|
36
|
+
const full = path.join(dir, entry.name);
|
|
37
|
+
if (entry.isDirectory()) {
|
|
38
|
+
const sub = getNewestMtime(full, ext);
|
|
39
|
+
if (sub > newest) newest = sub;
|
|
40
|
+
} else if (entry.name.endsWith(ext)) {
|
|
41
|
+
const mt = fs.statSync(full).mtimeMs;
|
|
42
|
+
if (mt > newest) newest = mt;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
} catch { /* dir doesn't exist or not readable */ }
|
|
46
|
+
return newest;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
module.exports = { readBinaryVersion, isDevMode, getNewestMtime, VERSION_OUTPUT_RE };
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const test = require('node:test');
|
|
3
|
+
const assert = require('node:assert/strict');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const os = require('os');
|
|
6
|
+
const path = require('path');
|
|
7
|
+
|
|
8
|
+
function mkDir(prefix) {
|
|
9
|
+
return fs.mkdtempSync(path.join(os.tmpdir(), prefix));
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// ── readBinaryVersion ──
|
|
13
|
+
|
|
14
|
+
test('readBinaryVersion returns version from valid binary', () => {
|
|
15
|
+
const { readBinaryVersion } = require('./version-utils');
|
|
16
|
+
const dir = mkDir('vu-');
|
|
17
|
+
const bin = path.join(dir, 'code-graph-mcp');
|
|
18
|
+
fs.writeFileSync(bin, [
|
|
19
|
+
'#!/usr/bin/env bash',
|
|
20
|
+
'if [ "$1" = "--version" ]; then',
|
|
21
|
+
' echo "code-graph-mcp 1.2.3"',
|
|
22
|
+
' exit 0',
|
|
23
|
+
'fi',
|
|
24
|
+
'exit 0',
|
|
25
|
+
].join('\n'));
|
|
26
|
+
fs.chmodSync(bin, 0o755);
|
|
27
|
+
assert.equal(readBinaryVersion(bin), '1.2.3');
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test('readBinaryVersion returns null for non-existent binary', () => {
|
|
31
|
+
const { readBinaryVersion } = require('./version-utils');
|
|
32
|
+
assert.equal(readBinaryVersion('/tmp/does-not-exist-binary'), null);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test('readBinaryVersion returns null for binary with unexpected output', () => {
|
|
36
|
+
const { readBinaryVersion } = require('./version-utils');
|
|
37
|
+
const dir = mkDir('vu-');
|
|
38
|
+
const bin = path.join(dir, 'code-graph-mcp');
|
|
39
|
+
fs.writeFileSync(bin, '#!/usr/bin/env bash\necho "something else"');
|
|
40
|
+
fs.chmodSync(bin, 0o755);
|
|
41
|
+
assert.equal(readBinaryVersion(bin), null);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// ── isDevMode ──
|
|
45
|
+
|
|
46
|
+
test('isDevMode returns true in source repo (Cargo.toml nearby)', () => {
|
|
47
|
+
const { isDevMode } = require('./version-utils');
|
|
48
|
+
// Running from source repo: __dirname/../.. has Cargo.toml → true
|
|
49
|
+
assert.equal(isDevMode(), true);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// ── getNewestMtime ──
|
|
53
|
+
|
|
54
|
+
test('getNewestMtime returns 0 for non-existent directory', () => {
|
|
55
|
+
const { getNewestMtime } = require('./version-utils');
|
|
56
|
+
assert.equal(getNewestMtime('/tmp/no-such-dir-xyz'), 0);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test('getNewestMtime finds newest .rs file mtime', () => {
|
|
60
|
+
const { getNewestMtime } = require('./version-utils');
|
|
61
|
+
const dir = mkDir('vu-mtime-');
|
|
62
|
+
const sub = path.join(dir, 'sub');
|
|
63
|
+
fs.mkdirSync(sub);
|
|
64
|
+
|
|
65
|
+
const older = path.join(dir, 'old.rs');
|
|
66
|
+
const newer = path.join(sub, 'new.rs');
|
|
67
|
+
fs.writeFileSync(older, 'fn old() {}');
|
|
68
|
+
|
|
69
|
+
fs.writeFileSync(newer, 'fn new() {}');
|
|
70
|
+
const futureMs = Date.now() + 1000;
|
|
71
|
+
fs.utimesSync(newer, futureMs / 1000, futureMs / 1000);
|
|
72
|
+
|
|
73
|
+
const newerMtime = fs.statSync(newer).mtimeMs;
|
|
74
|
+
const result = getNewestMtime(dir, '.rs');
|
|
75
|
+
assert.equal(result, newerMtime, 'should return exactly the newest file mtime');
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test('getNewestMtime ignores non-matching extensions', () => {
|
|
79
|
+
const { getNewestMtime } = require('./version-utils');
|
|
80
|
+
const dir = mkDir('vu-ext-');
|
|
81
|
+
fs.writeFileSync(path.join(dir, 'file.js'), 'hello');
|
|
82
|
+
assert.equal(getNewestMtime(dir, '.rs'), 0);
|
|
83
|
+
});
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: code-graph-mcp 插件契约
|
|
3
|
+
description: code-graph-mcp 工具调度规则 — 何时用 MCP/CLI 替代 Grep/Read,invited-memory 模式
|
|
4
|
+
type: reference
|
|
5
|
+
---
|
|
6
|
+
# code-graph-mcp 插件契约
|
|
7
|
+
|
|
8
|
+
> Invited-memory 模式:MCP `instructions` 仅留指针,决策细则集中在此。
|
|
9
|
+
> 启用条件:`CODE_GRAPH_QUIET_HOOKS=1`(在 `~/.claude/settings.json` 的 `env` 中设置)。
|
|
10
|
+
|
|
11
|
+
## 何时调用 MCP/CLI(替代多步 Grep/Read)
|
|
12
|
+
|
|
13
|
+
| 意图 | 工具 | 关键参数 / 例子 |
|
|
14
|
+
|------|------|----------------|
|
|
15
|
+
| "谁调用 X?" / "X 调了啥?" | `get_call_graph` / `callgraph X` | 替代 `grep "X("` |
|
|
16
|
+
| "改 X 会炸啥?" | `impact_analysis` / `impact X` | 修改函数签名前必跑 |
|
|
17
|
+
| "Y 模块长啥样?" | `module_overview` / `overview Y/` | 替代逐文件 Read |
|
|
18
|
+
| "找做 Z 的代码"(概念) | `semantic_code_search` / `search "Z"` | 不知道精确名 |
|
|
19
|
+
| "返回 T 类型的函数" | `ast_search --returns T` | 结构化筛选 |
|
|
20
|
+
| "X 在哪被引用?" | `find_references` / `refs X` | 含 callers/importers |
|
|
21
|
+
| "未使用的代码" | `find_dead_code` / `dead-code [path]` | 清理 exports |
|
|
22
|
+
| "相似/重复函数" | `find_similar_code` / `similar X` | 需 embedding |
|
|
23
|
+
| "X 文件依赖谁?" | `dependency_graph` / `deps X` | file 级别 |
|
|
24
|
+
| "看 X 的源码 / 签名" | `get_ast_node` / `show X` | `--include-impact` 含影响面 |
|
|
25
|
+
| "项目结构总览" | `project_map` / `map` | 起手势用 `--compact` |
|
|
26
|
+
| HTTP 路由 → handler 链路 | `trace_http_chain` / `trace ROUTE` | API 调试 |
|
|
27
|
+
|
|
28
|
+
## 不要替代
|
|
29
|
+
|
|
30
|
+
- 精确字符串 / 常量 / 正则 → 仍用 `Grep`
|
|
31
|
+
- 非代码文件(README/JSON/log) → 仍用 `Grep`
|
|
32
|
+
- 即将编辑的具体文件 → 仍用 `Read`
|
|
33
|
+
|
|
34
|
+
## 工作流惯例
|
|
35
|
+
|
|
36
|
+
1. 起手 `project_map --compact` 看架构
|
|
37
|
+
2. `semantic_code_search` 默认带 `compact=true`,省 token
|
|
38
|
+
3. 展开节点:`get_ast_node node_id=N compact=true` 看签名 / 不带 compact 看全文
|
|
39
|
+
4. 改前必跑 `impact_analysis`
|
|
40
|
+
5. 搜不到结果 → `code-graph-mcp health-check` 检查索引与 embedding 覆盖率
|
|
41
|
+
|
|
42
|
+
可用 prompts:`impact-analysis`、`understand-module`、`trace-request`
|
|
43
|
+
|
|
44
|
+
## CLI 速查(替 Bash)
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
code-graph-mcp grep "pattern" [path] # ripgrep + AST 上下文
|
|
48
|
+
code-graph-mcp search "concept" # FTS5 语义搜索
|
|
49
|
+
code-graph-mcp ast-search "q" --type fn # 结构化筛选
|
|
50
|
+
code-graph-mcp map # 项目架构
|
|
51
|
+
code-graph-mcp overview src/mcp/ # 模块总览
|
|
52
|
+
code-graph-mcp callgraph SYMBOL # 调用图
|
|
53
|
+
code-graph-mcp impact SYMBOL # 影响面
|
|
54
|
+
code-graph-mcp show SYMBOL # 节点详情
|
|
55
|
+
code-graph-mcp refs SYMBOL --relation calls # 引用筛选
|
|
56
|
+
code-graph-mcp dead-code [path] # 未使用代码
|
|
57
|
+
code-graph-mcp health-check # 索引健康
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
完整列表:`code-graph-mcp --help`。
|
|
61
|
+
|
|
62
|
+
## 质量门槛
|
|
63
|
+
|
|
64
|
+
- `compact=true` 一般够用;要看完整代码再去掉
|
|
65
|
+
- `impact` 在 `--change-type signature` 时返回最严格的破坏面
|
|
66
|
+
- 索引陈旧 → SessionStart 自带 `ensureIndexFresh`;手动跑 `incremental-index`
|
|
67
|
+
|
|
68
|
+
## 卸载
|
|
69
|
+
|
|
70
|
+
`code-graph-mcp unadopt` 精确移除 sentinel 段 + 本文件;或取消 `CODE_GRAPH_QUIET_HOOKS` 即恢复原注入。
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sdsrs/code-graph",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "MCP server that indexes codebases into an AST knowledge graph with semantic search, call graph traversal, and HTTP route tracing",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -34,10 +34,10 @@
|
|
|
34
34
|
"node": ">=16"
|
|
35
35
|
},
|
|
36
36
|
"optionalDependencies": {
|
|
37
|
-
"@sdsrs/code-graph-linux-x64": "0.
|
|
38
|
-
"@sdsrs/code-graph-linux-arm64": "0.
|
|
39
|
-
"@sdsrs/code-graph-darwin-x64": "0.
|
|
40
|
-
"@sdsrs/code-graph-darwin-arm64": "0.
|
|
41
|
-
"@sdsrs/code-graph-win32-x64": "0.
|
|
37
|
+
"@sdsrs/code-graph-linux-x64": "0.8.0",
|
|
38
|
+
"@sdsrs/code-graph-linux-arm64": "0.8.0",
|
|
39
|
+
"@sdsrs/code-graph-darwin-x64": "0.8.0",
|
|
40
|
+
"@sdsrs/code-graph-darwin-arm64": "0.8.0",
|
|
41
|
+
"@sdsrs/code-graph-win32-x64": "0.8.0"
|
|
42
42
|
}
|
|
43
43
|
}
|