@sdsrs/code-graph 0.7.14 → 0.7.16

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.
@@ -0,0 +1,466 @@
1
+ 'use strict';
2
+ const test = require('node:test');
3
+ const assert = require('node:assert/strict');
4
+ const path = require('node:path');
5
+ const fs = require('node:fs');
6
+
7
+ const {
8
+ shouldSkip,
9
+ extractFilePaths,
10
+ extractSymbols,
11
+ detectIntents,
12
+ determineQueryType,
13
+ } = require('./user-prompt-context');
14
+
15
+ // ── shouldSkip ──────────────────────────────────────────
16
+
17
+ test('shouldSkip: simple confirmations (EN)', () => {
18
+ for (const msg of ['yes', 'no', 'ok', 'done', 'y', 'n', 'commit', 'push', 'thanks']) {
19
+ assert.ok(shouldSkip(msg), `should skip "${msg}"`);
20
+ }
21
+ });
22
+
23
+ test('shouldSkip: simple confirmations (ZH)', () => {
24
+ for (const msg of ['继续', '确认', '好的', '好', '是的', '不', '可以', '行', '对', '提交', '推送', '没问题', '谢谢', '发布', '更新', '清理']) {
25
+ assert.ok(shouldSkip(msg), `should skip "${msg}"`);
26
+ }
27
+ });
28
+
29
+ test('shouldSkip: with trailing punctuation', () => {
30
+ assert.ok(shouldSkip('好的。'));
31
+ assert.ok(shouldSkip('ok!'));
32
+ assert.ok(shouldSkip('确认?'));
33
+ });
34
+
35
+ test('shouldSkip: action-only without code entities', () => {
36
+ assert.equal(shouldSkip('修复这些问题'), 'action-only');
37
+ assert.equal(shouldSkip('按优先级实施'), 'action-only');
38
+ assert.equal(shouldSkip('执行这个方案'), 'action-only');
39
+ assert.equal(shouldSkip('开始吧'), 'action-only');
40
+ });
41
+
42
+ test('shouldSkip: action with 3+ Latin chars passes through', () => {
43
+ assert.equal(shouldSkip('修复 parse_code 里的bug'), false);
44
+ assert.equal(shouldSkip('修复这段逻辑的bug'), false); // "bug" = 3 chars
45
+ assert.equal(shouldSkip('修复 API 的问题'), false); // "API" = 3 chars
46
+ });
47
+
48
+ test('shouldSkip: NOT skip legitimate code tasks', () => {
49
+ assert.equal(shouldSkip('帮我写一个工具函数'), false);
50
+ assert.equal(shouldSkip('帮我优化一下这个查询'), false);
51
+ assert.equal(shouldSkip('优化 parse_code 的性能'), false);
52
+ assert.equal(shouldSkip('看看 src/mcp/ 模块的代码结构'), false);
53
+ assert.equal(shouldSkip('重构一下这个模块'), false);
54
+ });
55
+
56
+ test('shouldSkip: messages below length threshold exit early in main', () => {
57
+ // The 8-char minimum is checked in the main block, not in shouldSkip
58
+ // shouldSkip itself doesn't enforce length
59
+ assert.equal(shouldSkip('短消息很短'), false); // passes shouldSkip but would exit in main
60
+ });
61
+
62
+ // ── extractFilePaths ────────────────────────────────────
63
+
64
+ test('extractFilePaths: extracts src/ paths', () => {
65
+ assert.deepEqual(extractFilePaths('看看 src/mcp/server.rs'), ['src/mcp/server.rs']);
66
+ assert.deepEqual(extractFilePaths('修改 src/parser/relations.rs 和 src/storage/db.rs'), ['src/parser/relations.rs', 'src/storage/db.rs']);
67
+ });
68
+
69
+ test('extractFilePaths: extracts lib/test/pkg paths', () => {
70
+ assert.deepEqual(extractFilePaths('check lib/utils/helpers.js'), ['lib/utils/helpers.js']);
71
+ assert.deepEqual(extractFilePaths('test/integration.rs is failing'), ['test/integration.rs']);
72
+ });
73
+
74
+ test('extractFilePaths: limits to 2 paths', () => {
75
+ const result = extractFilePaths('src/a.rs src/b.rs src/c.rs');
76
+ assert.equal(result.length, 2);
77
+ });
78
+
79
+ test('extractFilePaths: no match for non-code paths', () => {
80
+ assert.deepEqual(extractFilePaths('这个函数有问题'), []);
81
+ assert.deepEqual(extractFilePaths('update the readme'), []);
82
+ });
83
+
84
+ // ── extractSymbols ──────────────────────────────────────
85
+
86
+ test('extractSymbols: snake_case', () => {
87
+ const r = extractSymbols('修改 parse_code 函数');
88
+ assert.deepEqual(r.symbols, ['parse_code']);
89
+ assert.equal(r.lowConfidence, false);
90
+ });
91
+
92
+ test('extractSymbols: camelCase', () => {
93
+ const r = extractSymbols('fix the handleMessage function');
94
+ assert.ok(r.symbols.includes('handleMessage'));
95
+ assert.equal(r.lowConfidence, false);
96
+ });
97
+
98
+ test('extractSymbols: PascalCase compound', () => {
99
+ const r = extractSymbols('implement McpServer class');
100
+ assert.ok(r.symbols.includes('McpServer'));
101
+ });
102
+
103
+ test('extractSymbols: qualified names (Foo::bar)', () => {
104
+ const r = extractSymbols('check Foo::bar::baz');
105
+ assert.ok(r.symbols.some(s => s.includes('::')));
106
+ });
107
+
108
+ test('extractSymbols: backtick-quoted fallback', () => {
109
+ const r = extractSymbols('修改 `parse` 函数');
110
+ assert.ok(r.symbols.includes('parse'));
111
+ });
112
+
113
+ test('extractSymbols: backtick with longer name', () => {
114
+ const r = extractSymbols('看看 `fts5_search` 怎么实现的');
115
+ assert.ok(r.symbols.includes('fts5_search'));
116
+ });
117
+
118
+ test('extractSymbols: plain word fallback (low confidence)', () => {
119
+ const r = extractSymbols('write tests for the embedding module');
120
+ assert.ok(r.symbols.includes('embedding'));
121
+ assert.equal(r.lowConfidence, true);
122
+ });
123
+
124
+ test('extractSymbols: plain words excluded (common English verbs)', () => {
125
+ const r = extractSymbols('help me understand the refactor approach');
126
+ // "understand" and "refactor" are excluded, "approach" is excluded
127
+ assert.equal(r.symbols.length, 0);
128
+ });
129
+
130
+ test('extractSymbols: stop words filtered', () => {
131
+ const r = extractSymbols('fix the default function');
132
+ // "default" and "function" are stop words
133
+ assert.equal(r.symbols.length, 0);
134
+ });
135
+
136
+ test('extractSymbols: limits to 3 symbols', () => {
137
+ const r = extractSymbols('modify parse_code and run_full_index and extract_relations and hash_file');
138
+ assert.ok(r.symbols.length <= 3);
139
+ });
140
+
141
+ // ── detectIntents ───────────────────────────────────────
142
+
143
+ // --- Impact intent ---
144
+ test('detectIntents: impact (EN)', () => {
145
+ assert.ok(detectIntents('what is the impact of this change').impact);
146
+ assert.ok(detectIntents('check the risk of modifying this').impact);
147
+ assert.ok(detectIntents('this bug is critical').impact);
148
+ });
149
+
150
+ test('detectIntents: impact (ZH)', () => {
151
+ assert.ok(detectIntents('这个改动有什么影响').impact);
152
+ assert.ok(detectIntents('改动范围有多大').impact);
153
+ assert.ok(detectIntents('会不会跟其他模块冲突').impact);
154
+ assert.ok(detectIntents('修改前先看看').impact);
155
+ assert.ok(detectIntents('有什么风险').impact);
156
+ assert.ok(detectIntents('这个bug怎么回事').impact);
157
+ });
158
+
159
+ // --- Modify intent ---
160
+ test('detectIntents: modify (EN)', () => {
161
+ assert.ok(detectIntents('refactor this module').modify);
162
+ assert.ok(detectIntents('rename the function').modify);
163
+ assert.ok(detectIntents('fix the broken test').modify);
164
+ assert.ok(detectIntents('update the config').modify);
165
+ assert.ok(detectIntents('remove deprecated code').modify);
166
+ assert.ok(detectIntents('replace with new impl').modify);
167
+ });
168
+
169
+ test('detectIntents: modify (ZH)', () => {
170
+ const words = ['修改', '修复', '重构', '优化', '简化', '精简', '适配', '统一', '修正', '调整', '去掉', '整理', '清理', '解耦', '更新', '升级', '迁移', '拆分', '合并', '提取'];
171
+ for (const w of words) {
172
+ assert.ok(detectIntents(`${w}这个模块`).modify, `"${w}" should trigger modify`);
173
+ }
174
+ });
175
+
176
+ test('detectIntents: modify (ZH compound)', () => {
177
+ assert.ok(detectIntents('把这个函数改成异步的').modify);
178
+ assert.ok(detectIntents('把返回值类型换成 Result').modify);
179
+ assert.ok(detectIntents('把同步改成异步').modify);
180
+ });
181
+
182
+ // --- Implement intent ---
183
+ test('detectIntents: implement (EN)', () => {
184
+ assert.ok(detectIntents('add a new tool').implement);
185
+ assert.ok(detectIntents('implement error handling').implement);
186
+ assert.ok(detectIntents('create a helper function').implement);
187
+ assert.ok(detectIntents('build the CI pipeline').implement);
188
+ assert.ok(detectIntents('write unit tests').implement);
189
+ });
190
+
191
+ test('detectIntents: implement (ZH)', () => {
192
+ const words = ['新增', '添加', '实现', '创建', '编写', '开发', '增加', '加上', '加个', '搭建', '补充', '引入', '支持', '封装', '接入', '对接', '配置'];
193
+ for (const w of words) {
194
+ assert.ok(detectIntents(`${w}一个功能`).implement, `"${w}" should trigger implement`);
195
+ }
196
+ });
197
+
198
+ test('detectIntents: implement - "写" variants', () => {
199
+ assert.ok(detectIntents('写个测试').implement);
200
+ assert.ok(detectIntents('写一个工具函数').implement);
201
+ assert.ok(detectIntents('帮我写一个函数').implement);
202
+ });
203
+
204
+ // --- Understand intent ---
205
+ test('detectIntents: understand (EN)', () => {
206
+ assert.ok(detectIntents('how does this module work').understand);
207
+ assert.ok(detectIntents('explain the architecture').understand);
208
+ });
209
+
210
+ test('detectIntents: understand (ZH)', () => {
211
+ const words = ['看看', '看一下', '理解', '了解', '分析', '评估', '检查', '审核', '审查', '验证', '诊断', '深入思考'];
212
+ for (const w of words) {
213
+ assert.ok(detectIntents(`${w}这段代码`).understand, `"${w}" should trigger understand`);
214
+ }
215
+ });
216
+
217
+ test('detectIntents: understand (ZH question patterns)', () => {
218
+ assert.ok(detectIntents('这个模块是干什么的').understand);
219
+ assert.ok(detectIntents('工作原理是什么').understand);
220
+ assert.ok(detectIntents('整个流程是怎么走的').understand);
221
+ assert.ok(detectIntents('这个功能怎么实现的').understand);
222
+ });
223
+
224
+ // --- Callgraph intent ---
225
+ test('detectIntents: callgraph (EN)', () => {
226
+ assert.ok(detectIntents('who calls this function').callgraph);
227
+ assert.ok(detectIntents('what calls parse_code').callgraph);
228
+ assert.ok(detectIntents('trace the request flow').callgraph);
229
+ });
230
+
231
+ test('detectIntents: callgraph (ZH)', () => {
232
+ assert.ok(detectIntents('这个函数被谁调了').callgraph);
233
+ assert.ok(detectIntents('看看调用链路').callgraph);
234
+ assert.ok(detectIntents('追踪一下请求路径').callgraph);
235
+ assert.ok(detectIntents('上下游依赖关系是什么').callgraph);
236
+ assert.ok(detectIntents('这个事件怎么触发的').callgraph);
237
+ });
238
+
239
+ // --- Search intent ---
240
+ test('detectIntents: search (EN)', () => {
241
+ assert.ok(detectIntents('where is the config defined').search);
242
+ assert.ok(detectIntents('find the error handling code').search);
243
+ assert.ok(detectIntents('search for all usages').search);
244
+ });
245
+
246
+ test('detectIntents: search (ZH)', () => {
247
+ assert.ok(detectIntents('这个函数定义在哪').search);
248
+ assert.ok(detectIntents('找一下处理错误的代码').search);
249
+ assert.ok(detectIntents('搜索所有用到这个类型的地方').search);
250
+ assert.ok(detectIntents('在哪里用了这个常量').search);
251
+ });
252
+
253
+ // --- No false positives ---
254
+ test('detectIntents: simple confirmations have no code intent', () => {
255
+ const r = detectIntents('好的');
256
+ // "什么" would match in some words, but "好的" shouldn't trigger understand
257
+ assert.equal(r.modify, false);
258
+ assert.equal(r.implement, false);
259
+ assert.equal(r.callgraph, false);
260
+ assert.equal(r.search, false);
261
+ });
262
+
263
+ // ── determineQueryType (priority logic) ─────────────────
264
+
265
+ test('priority: impact/modify + strict symbol → impact', () => {
266
+ const intents = { impact: true, modify: false, implement: false, understand: false, callgraph: false, search: false };
267
+ const symbols = { symbols: ['parse_code'], lowConfidence: false };
268
+ const result = determineQueryType(intents, symbols, []);
269
+ assert.equal(result.type, 'impact');
270
+ assert.equal(result.symbol, 'parse_code');
271
+ });
272
+
273
+ test('priority: modify + strict symbol → impact', () => {
274
+ const intents = { impact: false, modify: true, implement: false, understand: false, callgraph: false, search: false };
275
+ const symbols = { symbols: ['handleMessage'], lowConfidence: false };
276
+ const result = determineQueryType(intents, symbols, []);
277
+ assert.equal(result.type, 'impact');
278
+ });
279
+
280
+ test('priority: modify + low-confidence symbol → NOT impact (falls to overview/search)', () => {
281
+ const intents = { impact: false, modify: true, implement: false, understand: false, callgraph: false, search: false };
282
+ const symbols = { symbols: ['embedding'], lowConfidence: true };
283
+ const result = determineQueryType(intents, symbols, ['src/embed/']);
284
+ // Should fall through to overview (file paths exist)
285
+ assert.equal(result.type, 'overview');
286
+ });
287
+
288
+ test('priority: callgraph + strict symbol → callgraph', () => {
289
+ const intents = { impact: false, modify: false, implement: false, understand: false, callgraph: true, search: false };
290
+ const symbols = { symbols: ['parse_code'], lowConfidence: false };
291
+ const result = determineQueryType(intents, symbols, []);
292
+ assert.equal(result.type, 'callgraph');
293
+ });
294
+
295
+ test('priority: file paths → overview (regardless of intent)', () => {
296
+ const intents = { impact: false, modify: true, implement: false, understand: false, callgraph: false, search: false };
297
+ const symbols = { symbols: [], lowConfidence: false };
298
+ const result = determineQueryType(intents, symbols, ['src/storage/queries.rs']);
299
+ assert.equal(result.type, 'overview');
300
+ assert.equal(result.path, 'src/storage/');
301
+ });
302
+
303
+ test('priority: search intent + symbol → search', () => {
304
+ const intents = { impact: false, modify: false, implement: false, understand: false, callgraph: false, search: true };
305
+ const symbols = { symbols: ['parse_code'], lowConfidence: false };
306
+ const result = determineQueryType(intents, symbols, []);
307
+ assert.equal(result.type, 'search');
308
+ });
309
+
310
+ test('priority: implement intent + symbol → search', () => {
311
+ const intents = { impact: false, modify: false, implement: true, understand: false, callgraph: false, search: false };
312
+ const symbols = { symbols: ['embedding'], lowConfidence: true };
313
+ const result = determineQueryType(intents, symbols, []);
314
+ assert.equal(result.type, 'search');
315
+ });
316
+
317
+ test('priority: understand + symbol → search', () => {
318
+ const intents = { impact: false, modify: false, implement: false, understand: true, callgraph: false, search: false };
319
+ const symbols = { symbols: ['pipeline'], lowConfidence: true };
320
+ const result = determineQueryType(intents, symbols, []);
321
+ assert.equal(result.type, 'search');
322
+ });
323
+
324
+ test('priority: no intent, no symbol, no path → null', () => {
325
+ const intents = { impact: false, modify: false, implement: false, understand: false, callgraph: false, search: false };
326
+ const symbols = { symbols: [], lowConfidence: false };
327
+ const result = determineQueryType(intents, symbols, []);
328
+ assert.equal(result, null);
329
+ });
330
+
331
+ test('priority: cooldown blocks query', () => {
332
+ const intents = { impact: true, modify: false, implement: false, understand: false, callgraph: false, search: false };
333
+ const symbols = { symbols: ['parse_code'], lowConfidence: false };
334
+ const result = determineQueryType(intents, symbols, [], (type) => type === 'impact');
335
+ // Impact blocked by cooldown, falls through; no file path, no search intent → try search via understand fallback
336
+ // Actually: no understand intent and hasAny=true, so the last condition (!hasAny) is false → null
337
+ // But symbol exists and we have filePaths=[] → falls to search via implement/qualified check → no
338
+ // Actually it should return null since all fallbacks require conditions not met
339
+ assert.equal(result, null);
340
+ });
341
+
342
+ test('priority: cooldown on impact → falls to overview when file paths exist', () => {
343
+ const intents = { impact: true, modify: false, implement: false, understand: false, callgraph: false, search: false };
344
+ const symbols = { symbols: ['parse_code'], lowConfidence: false };
345
+ const result = determineQueryType(intents, symbols, ['src/parser/mod.rs'], (type) => type === 'impact');
346
+ assert.equal(result.type, 'overview');
347
+ });
348
+
349
+ // ── Full integration: message → query type ──────────────
350
+
351
+ function analyze(msg) {
352
+ if (shouldSkip(msg)) return { skipped: true };
353
+ const fp = extractFilePaths(msg);
354
+ const sym = extractSymbols(msg);
355
+ const intents = detectIntents(msg);
356
+ const query = determineQueryType(intents, sym, fp);
357
+ return { query, intents, symbols: sym, filePaths: fp };
358
+ }
359
+
360
+ test('integration: 修改 parse_code 函数增加错误处理 → impact', () => {
361
+ const r = analyze('修改 parse_code 函数增加错误处理');
362
+ assert.equal(r.query.type, 'impact');
363
+ assert.equal(r.query.symbol, 'parse_code');
364
+ });
365
+
366
+ test('integration: 看看 src/mcp/ 模块的代码结构 → overview', () => {
367
+ const r = analyze('看看 src/mcp/ 模块的代码结构');
368
+ assert.equal(r.query.type, 'overview');
369
+ });
370
+
371
+ test('integration: refactor src/storage/queries.rs → overview (not impact on "refactor")', () => {
372
+ const r = analyze('refactor src/storage/queries.rs to use parameterized queries');
373
+ assert.equal(r.query.type, 'overview');
374
+ assert.ok(r.query.path.includes('src/storage/'));
375
+ });
376
+
377
+ test('integration: help me understand the indexer pipeline → search', () => {
378
+ const r = analyze('help me understand the indexer pipeline');
379
+ assert.equal(r.query.type, 'search');
380
+ assert.equal(r.query.symbol, 'pipeline');
381
+ });
382
+
383
+ test('integration: write tests for the embedding module → search', () => {
384
+ const r = analyze('write tests for the embedding module');
385
+ assert.equal(r.query.type, 'search');
386
+ assert.equal(r.query.symbol, 'embedding');
387
+ });
388
+
389
+ test('integration: 修复这段逻辑的bug → not skipped (bug=3 chars)', () => {
390
+ const r = analyze('修复这段逻辑的bug');
391
+ assert.ok(!r.skipped);
392
+ assert.ok(r.intents.impact); // "bug"
393
+ assert.ok(r.intents.modify); // "修复"
394
+ });
395
+
396
+ test('integration: 按优先级修复这些问题 → skipped (no code entity)', () => {
397
+ const r = analyze('按优先级修复这些问题');
398
+ assert.ok(r.skipped);
399
+ });
400
+
401
+ test('integration: 帮我写一个工具函数 → implement intent', () => {
402
+ const r = analyze('帮我写一个工具函数');
403
+ assert.ok(!r.skipped);
404
+ assert.ok(r.intents.implement);
405
+ });
406
+
407
+ test('integration: 对整个项目进行一次完整的代码审核 → understand', () => {
408
+ const r = analyze('对整个项目进行一次完整的代码审核');
409
+ assert.ok(r.intents.understand);
410
+ });
411
+
412
+ test('integration: 更新一下readme.md → modify intent', () => {
413
+ const r = analyze('更新一下readme.md这个文件');
414
+ assert.ok(r.intents.modify);
415
+ });
416
+
417
+ test('integration: 配置 pre-commit hook → implement intent', () => {
418
+ const r = analyze('配置提交代码时的git pre-commit hook检查');
419
+ assert.ok(r.intents.implement);
420
+ });
421
+
422
+ test('integration: 检查下我们插件上下文token占用情况 → understand', () => {
423
+ const r = analyze('检查下我们插件上下文token占用情况');
424
+ assert.ok(r.intents.understand);
425
+ });
426
+
427
+ test('integration: 诊断一下性能问题 → understand', () => {
428
+ const r = analyze('诊断一下性能问题');
429
+ assert.ok(r.intents.understand);
430
+ });
431
+
432
+ test('integration: simple confirmation → skipped', () => {
433
+ assert.ok(analyze('好的').skipped);
434
+ assert.ok(analyze('继续').skipped);
435
+ assert.ok(analyze('ok').skipped);
436
+ });
437
+
438
+ // ── Skill files validation ──────────────────────────────
439
+
440
+ test('skills: explore.md has correct frontmatter', () => {
441
+ const content = fs.readFileSync(path.join(__dirname, '../skills/explore.md'), 'utf8');
442
+ assert.match(content, /^---\nname: explore/);
443
+ assert.match(content, /description:/);
444
+ });
445
+
446
+ test('skills: index.md has correct frontmatter', () => {
447
+ const content = fs.readFileSync(path.join(__dirname, '../skills/index.md'), 'utf8');
448
+ assert.match(content, /^---\nname: index/);
449
+ assert.match(content, /description:/);
450
+ });
451
+
452
+ test('skills: commands directory is empty (all converted to skills)', () => {
453
+ const commandsDir = path.join(__dirname, '../commands');
454
+ const exists = fs.existsSync(commandsDir);
455
+ if (exists) {
456
+ const files = fs.readdirSync(commandsDir).filter(f => f.endsWith('.md'));
457
+ assert.equal(files.length, 0, 'commands/ should have no .md files');
458
+ }
459
+ // Directory not existing is also valid
460
+ });
461
+
462
+ test('skills: only expected skills exist', () => {
463
+ const skillsDir = path.join(__dirname, '../skills');
464
+ const files = fs.readdirSync(skillsDir).filter(f => f.endsWith('.md')).sort();
465
+ assert.deepEqual(files, ['explore.md', 'index.md']);
466
+ });
@@ -0,0 +1,22 @@
1
+ ---
2
+ name: explore
3
+ description: |
4
+ Understand code structure efficiently using the AST index. Use BEFORE reading
5
+ files one by one — when starting work in unfamiliar code, exploring a module
6
+ before changes, or finding the right file to edit. One overview call replaces
7
+ 5+ Read calls and saves significant context.
8
+ ---
9
+
10
+ # Explore Code (indexed project)
11
+
12
+ Use these BEFORE reading individual files:
13
+
14
+ | Need | Command | Replaces |
15
+ |------|---------|----------|
16
+ | Module structure | `code-graph-mcp overview <dir>` | 5+ Read calls |
17
+ | Project architecture | `code-graph-mcp map --compact` | ls + README |
18
+ | Who calls / what calls | `code-graph-mcp callgraph <symbol>` | Grep + manual trace |
19
+ | Find by concept | `code-graph-mcp search "concept"` | 3+ Grep attempts |
20
+ | Impact before edit | `code-graph-mcp impact <symbol>` | Grep for callers |
21
+
22
+ **Workflow**: overview first → Read only the file you will edit.
@@ -0,0 +1,24 @@
1
+ ---
2
+ name: index
3
+ description: |
4
+ Diagnose and fix code-graph index issues. Use when: search returns unexpected/empty
5
+ results, or after major codebase restructuring. These management commands are NOT
6
+ exposed via MCP tools — this skill is the only way to access them.
7
+ ---
8
+
9
+ # Index Maintenance
10
+
11
+ ## Check health
12
+ ```bash
13
+ code-graph-mcp health-check
14
+ ```
15
+
16
+ ## Rebuild (incremental — only changed files)
17
+ ```bash
18
+ code-graph-mcp incremental-index
19
+ ```
20
+
21
+ ## Full rebuild (when incremental isn't enough)
22
+ ```bash
23
+ rm -rf .code-graph/ && code-graph-mcp incremental-index
24
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sdsrs/code-graph",
3
- "version": "0.7.14",
3
+ "version": "0.7.16",
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.7.14",
38
- "@sdsrs/code-graph-linux-arm64": "0.7.14",
39
- "@sdsrs/code-graph-darwin-x64": "0.7.14",
40
- "@sdsrs/code-graph-darwin-arm64": "0.7.14",
41
- "@sdsrs/code-graph-win32-x64": "0.7.14"
37
+ "@sdsrs/code-graph-linux-x64": "0.7.16",
38
+ "@sdsrs/code-graph-linux-arm64": "0.7.16",
39
+ "@sdsrs/code-graph-darwin-x64": "0.7.16",
40
+ "@sdsrs/code-graph-darwin-arm64": "0.7.16",
41
+ "@sdsrs/code-graph-win32-x64": "0.7.16"
42
42
  }
43
43
  }
@@ -1,9 +0,0 @@
1
- ---
2
- description: Analyze blast radius before modifying a symbol. Use when about to edit/rename/remove a function, or asked about change risk and affected callers.
3
- argument-hint: <symbol_name>
4
- ---
5
-
6
- ## Impact Analysis
7
- !`code-graph-mcp impact $ARGUMENTS 2>/dev/null || echo "Symbol not found or no index. Run: code-graph-mcp incremental-index"`
8
-
9
- Present the risk assessment and recommend whether it's safe to proceed.
@@ -1,7 +0,0 @@
1
- ---
2
- description: Force code-graph index rebuild. Use when search results seem stale or wrong, after major codebase restructuring, or when index health check reports issues.
3
- ---
4
-
5
- Run via Bash: `code-graph-mcp incremental-index`
6
- This updates the index incrementally (only changed files).
7
- For a full rebuild, delete `.code-graph/` first, then run the MCP server.
@@ -1,5 +0,0 @@
1
- ---
2
- description: Show code-graph index health and coverage. Use when search returns unexpected results, checking if index is current, or diagnosing code-graph issues.
3
- ---
4
-
5
- !`code-graph-mcp health-check --format json 2>/dev/null || echo '{"error":"No index found"}'`
@@ -1,12 +0,0 @@
1
- ---
2
- description: Trace call flow from a handler or route. Use when debugging API behavior, understanding request processing flow, or asked how an endpoint works.
3
- argument-hint: <handler_or_route>
4
- ---
5
-
6
- ## Call Graph (callees)
7
- !`code-graph-mcp callgraph $ARGUMENTS --direction callees --depth 4 2>/dev/null || echo "Symbol not found or no index."`
8
-
9
- ## Call Graph (callers)
10
- !`code-graph-mcp callgraph $ARGUMENTS --direction callers --depth 2 2>/dev/null`
11
-
12
- Map the flow and highlight error handling, auth checks, and data access points.
@@ -1,12 +0,0 @@
1
- ---
2
- description: Deep dive into a module's architecture. Use when starting work in an unfamiliar area, asked to explain how code works, or before implementing changes in a module.
3
- argument-hint: <file_or_dir_path>
4
- ---
5
-
6
- ## Module Overview
7
- !`code-graph-mcp overview $ARGUMENTS 2>/dev/null || echo "No index or no symbols found. Run: code-graph-mcp incremental-index"`
8
-
9
- ## Call Graph (top symbols)
10
- !`code-graph-mcp search "$ARGUMENTS" --limit 5 2>/dev/null`
11
-
12
- Analyze the above and summarize: purpose, public API, key internal helpers, and hot paths.
@@ -1,20 +0,0 @@
1
- ---
2
- name: code-navigation
3
- description: Code search and understanding via CLI. Use when exploring code structure, searching by concept, or checking impact before edits.
4
- ---
5
-
6
- # Code Graph CLI
7
-
8
- Indexed project. Use Bash — one command replaces multi-file Grep/Read:
9
-
10
- | Task | Command | Replaces |
11
- |------|---------|----------|
12
- | grep + AST context | `code-graph-mcp grep "pattern" [path]` | Grep |
13
- | search by concept | `code-graph-mcp search "query"` | Grep (no exact name needed) |
14
- | structural search | `code-graph-mcp ast-search "q" --type fn --returns Result` | — |
15
- | project map | `code-graph-mcp map` | Read multiple files |
16
- | module overview | `code-graph-mcp overview src/path/` | Read directory files |
17
- | call graph | `code-graph-mcp callgraph symbol` | Grep + Read tracing |
18
- | impact analysis | `code-graph-mcp impact symbol` | — |
19
-
20
- Still use Grep for exact strings/constants/regex. Still use Read for files you'll edit.