@sdsrs/code-graph 0.45.3 → 0.46.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.
|
@@ -206,19 +206,28 @@ function runDiagnostics() {
|
|
|
206
206
|
try {
|
|
207
207
|
const settings = readJson(settingsPath()) || {};
|
|
208
208
|
const cov = surveyHookCoverage(settings);
|
|
209
|
-
if (cov.missing.length === 0) {
|
|
209
|
+
if (cov.missing.length === 0 && cov.stale.length === 0) {
|
|
210
210
|
results.push({
|
|
211
211
|
name: 'Hook coverage',
|
|
212
212
|
status: 'ok',
|
|
213
213
|
detail: `settings.json has all ${cov.expected.length} expected entries`,
|
|
214
214
|
});
|
|
215
|
-
} else {
|
|
215
|
+
} else if (cov.missing.length > 0) {
|
|
216
216
|
results.push({
|
|
217
217
|
name: 'Hook coverage',
|
|
218
218
|
status: 'warn',
|
|
219
219
|
detail: `missing ${cov.missing.length}/${cov.expected.length} settings.json entries: ${cov.missing.join(', ')}`,
|
|
220
220
|
fixId: 'missing-hooks-in-settings',
|
|
221
221
|
});
|
|
222
|
+
} else {
|
|
223
|
+
// Present but stale path(s) — re-register rewrites them to the current
|
|
224
|
+
// version. A stale PreToolUse hook can keep the conversion metric dark.
|
|
225
|
+
results.push({
|
|
226
|
+
name: 'Hook coverage',
|
|
227
|
+
status: 'warn',
|
|
228
|
+
detail: `${cov.stale.length}/${cov.expected.length} settings.json entries point at a stale path (re-register to current version): ${cov.stale.join(', ')}`,
|
|
229
|
+
fixId: 'missing-hooks-in-settings',
|
|
230
|
+
});
|
|
222
231
|
}
|
|
223
232
|
} catch { /* probe failed — skip */ }
|
|
224
233
|
|
|
@@ -230,26 +239,42 @@ function runDiagnostics() {
|
|
|
230
239
|
function surveyHookCoverage(settings) {
|
|
231
240
|
const desired = buildSettingsHookEntries();
|
|
232
241
|
const expected = [];
|
|
242
|
+
const desiredCmd = {}; // key -> command string we would write now
|
|
233
243
|
for (const [event, entries] of Object.entries(desired)) {
|
|
234
244
|
for (const e of entries) {
|
|
235
|
-
|
|
245
|
+
const key = `${event}:${e.matcher || '*'}`;
|
|
246
|
+
expected.push(key);
|
|
247
|
+
desiredCmd[key] = e.hooks && e.hooks[0] && e.hooks[0].command;
|
|
236
248
|
}
|
|
237
249
|
}
|
|
238
250
|
|
|
239
251
|
const present = new Set();
|
|
252
|
+
const presentCmd = {}; // key -> command currently registered
|
|
240
253
|
if (settings && settings.hooks) {
|
|
241
254
|
for (const [event, entries] of Object.entries(settings.hooks)) {
|
|
242
255
|
if (!Array.isArray(entries)) continue;
|
|
243
256
|
for (const entry of entries) {
|
|
244
257
|
if (isOurHookEntry(entry)) {
|
|
245
|
-
|
|
258
|
+
const key = `${event}:${entry.matcher || '*'}`;
|
|
259
|
+
present.add(key);
|
|
260
|
+
if (entry.hooks && entry.hooks[0] && entry.hooks[0].command) {
|
|
261
|
+
presentCmd[key] = entry.hooks[0].command;
|
|
262
|
+
}
|
|
246
263
|
}
|
|
247
264
|
}
|
|
248
265
|
}
|
|
249
266
|
}
|
|
250
267
|
|
|
251
268
|
const missing = expected.filter(k => !present.has(k));
|
|
252
|
-
|
|
269
|
+
// Stale = present but the registered command no longer matches what we'd write
|
|
270
|
+
// now (points at an old plugin-cache version dir / moved path). A stale path can
|
|
271
|
+
// run pre-recordRecommendation hook code, so the hook fires but the conversion
|
|
272
|
+
// metric stays dark — invisible to a present/absent check. This is the
|
|
273
|
+
// 0.45.1-registered-while-0.45.4-active case the RCA surfaced.
|
|
274
|
+
const stale = expected.filter(k =>
|
|
275
|
+
present.has(k) && desiredCmd[k] && presentCmd[k] && presentCmd[k] !== desiredCmd[k]
|
|
276
|
+
);
|
|
277
|
+
return { expected, present: [...present], missing, stale };
|
|
253
278
|
}
|
|
254
279
|
|
|
255
280
|
// ── Report Formatting ─────────────────────────────────────
|
|
@@ -449,7 +474,7 @@ function runDoctor(opts = {}) {
|
|
|
449
474
|
return { results, issueCount: issues.length };
|
|
450
475
|
}
|
|
451
476
|
|
|
452
|
-
module.exports = { runDiagnostics, formatReport, runRepairs, runDoctor };
|
|
477
|
+
module.exports = { runDiagnostics, formatReport, runRepairs, runDoctor, surveyHookCoverage };
|
|
453
478
|
|
|
454
479
|
if (require.main === module) {
|
|
455
480
|
const args = process.argv.slice(2);
|
|
@@ -2,7 +2,18 @@
|
|
|
2
2
|
const test = require('node:test');
|
|
3
3
|
const assert = require('node:assert/strict');
|
|
4
4
|
|
|
5
|
-
const { runDiagnostics, formatReport } = require('./doctor');
|
|
5
|
+
const { runDiagnostics, formatReport, surveyHookCoverage } = require('./doctor');
|
|
6
|
+
const { buildSettingsHookEntries } = require('./lifecycle');
|
|
7
|
+
|
|
8
|
+
// Build a settings.json whose hooks exactly mirror what we'd register now.
|
|
9
|
+
function settingsWithCurrentHooks() {
|
|
10
|
+
const desired = buildSettingsHookEntries();
|
|
11
|
+
const hooks = {};
|
|
12
|
+
for (const [event, entries] of Object.entries(desired)) {
|
|
13
|
+
hooks[event] = entries.map(e => JSON.parse(JSON.stringify(e)));
|
|
14
|
+
}
|
|
15
|
+
return { hooks };
|
|
16
|
+
}
|
|
6
17
|
|
|
7
18
|
test('runDiagnostics returns an array of check results', () => {
|
|
8
19
|
const results = runDiagnostics();
|
|
@@ -45,3 +56,27 @@ test('formatReport shows all-clear when no problems', () => {
|
|
|
45
56
|
const output = formatReport(results);
|
|
46
57
|
assert.ok(output.includes('All checks passed') || output.includes('0 issues'));
|
|
47
58
|
});
|
|
59
|
+
|
|
60
|
+
test('surveyHookCoverage reports clean when all entries are current', () => {
|
|
61
|
+
const cov = surveyHookCoverage(settingsWithCurrentHooks());
|
|
62
|
+
assert.equal(cov.missing.length, 0, 'no missing entries');
|
|
63
|
+
assert.equal(cov.stale.length, 0, 'no stale entries');
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test('surveyHookCoverage flags a present-but-stale hook path', () => {
|
|
67
|
+
const settings = settingsWithCurrentHooks();
|
|
68
|
+
// Repoint one PreToolUse entry at an old plugin-cache version dir — present,
|
|
69
|
+
// recognized as ours (description unchanged), but command no longer current.
|
|
70
|
+
const bash = settings.hooks.PreToolUse.find(e => e.matcher === 'Bash');
|
|
71
|
+
bash.hooks[0].command = bash.hooks[0].command.replace('/scripts/', '/0.0.1-old/scripts/');
|
|
72
|
+
const cov = surveyHookCoverage(settings);
|
|
73
|
+
assert.equal(cov.missing.length, 0, 'entry is present, not missing');
|
|
74
|
+
assert.ok(cov.stale.includes('PreToolUse:Bash'),
|
|
75
|
+
`stale Bash path should be flagged; got stale=${JSON.stringify(cov.stale)}`);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test('surveyHookCoverage flags missing entries when settings empty', () => {
|
|
79
|
+
const cov = surveyHookCoverage({});
|
|
80
|
+
assert.ok(cov.missing.length === cov.expected.length, 'all expected entries missing');
|
|
81
|
+
assert.equal(cov.stale.length, 0, 'nothing present to be stale');
|
|
82
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sdsrs/code-graph",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.46.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": {
|
|
@@ -35,10 +35,10 @@
|
|
|
35
35
|
"node": ">=16"
|
|
36
36
|
},
|
|
37
37
|
"optionalDependencies": {
|
|
38
|
-
"@sdsrs/code-graph-linux-x64": "0.
|
|
39
|
-
"@sdsrs/code-graph-linux-arm64": "0.
|
|
40
|
-
"@sdsrs/code-graph-darwin-x64": "0.
|
|
41
|
-
"@sdsrs/code-graph-darwin-arm64": "0.
|
|
42
|
-
"@sdsrs/code-graph-win32-x64": "0.
|
|
38
|
+
"@sdsrs/code-graph-linux-x64": "0.46.0",
|
|
39
|
+
"@sdsrs/code-graph-linux-arm64": "0.46.0",
|
|
40
|
+
"@sdsrs/code-graph-darwin-x64": "0.46.0",
|
|
41
|
+
"@sdsrs/code-graph-darwin-arm64": "0.46.0",
|
|
42
|
+
"@sdsrs/code-graph-win32-x64": "0.46.0"
|
|
43
43
|
}
|
|
44
44
|
}
|