@sdsrs/code-graph 0.85.3 → 0.85.4
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.
|
@@ -496,10 +496,21 @@ function runRepairs(results) {
|
|
|
496
496
|
case 'hooks-invalid': {
|
|
497
497
|
console.log('\n Repairing hooks...');
|
|
498
498
|
if (relicRepairGuard()) break;
|
|
499
|
-
const { install } = require('./lifecycle');
|
|
499
|
+
const { install, scanForBrokenPaths } = require('./lifecycle');
|
|
500
500
|
install();
|
|
501
|
-
|
|
502
|
-
|
|
501
|
+
// Diagnosis already ran install()+re-scan and the paths were STILL
|
|
502
|
+
// broken (that `repaired:false` is what raised hooks-invalid). Verify
|
|
503
|
+
// this second attempt actually cleared them before counting it fixed \u2014
|
|
504
|
+
// a blind fixed++ here would let doctor exit 0 ("healthy") while the
|
|
505
|
+
// hook paths stay broken.
|
|
506
|
+
const remaining = scanForBrokenPaths();
|
|
507
|
+
if (remaining.length === 0) {
|
|
508
|
+
console.log(' \u2705 Hooks repaired \u2014 restart Claude Code to apply');
|
|
509
|
+
fixed++;
|
|
510
|
+
} else {
|
|
511
|
+
console.log(` \u274c ${remaining.length} hook path(s) still invalid \u2014 plugin scripts may be missing.`);
|
|
512
|
+
console.log(' Reinstall: npm install -g @sdsrs/code-graph (or re-run the plugin installer)');
|
|
513
|
+
}
|
|
503
514
|
break;
|
|
504
515
|
}
|
|
505
516
|
|
|
@@ -532,25 +543,43 @@ function runRepairs(results) {
|
|
|
532
543
|
|
|
533
544
|
// ── Main ──────────────────────────────────────────────────
|
|
534
545
|
|
|
546
|
+
// Exit status for a doctor run reflects what remains BROKEN, not what was found:
|
|
547
|
+
// --check-only → every found issue is unresolved (report cleanliness, no repair).
|
|
548
|
+
// repair mode → issueCount minus what runRepairs resolved. A run that fixes
|
|
549
|
+
// everything ("N/N addressed") reports 0 so `doctor && …` and
|
|
550
|
+
// self-heal automation don't read a successful repair as a
|
|
551
|
+
// failure. runRepairs counts an issue fixed only when its repair
|
|
552
|
+
// reports success — and the hooks arm re-scans after install() to
|
|
553
|
+
// confirm, so a still-broken re-scan is NOT counted (stays
|
|
554
|
+
// unresolved → exit 1). An issue with no working repair
|
|
555
|
+
// (schema-mismatch is advisory only) likewise keeps this > 0.
|
|
556
|
+
function unresolvedCount({ checkOnly, issueCount, fixed }) {
|
|
557
|
+
return checkOnly ? issueCount : issueCount - fixed;
|
|
558
|
+
}
|
|
559
|
+
|
|
535
560
|
function runDoctor(opts = {}) {
|
|
536
561
|
const results = runDiagnostics();
|
|
537
562
|
console.log(formatReport(results, { checkOnly: opts.checkOnly }));
|
|
538
563
|
|
|
539
564
|
const issues = results.filter(r => r.status === 'warn' || r.status === 'error');
|
|
540
565
|
|
|
566
|
+
let fixed = 0;
|
|
541
567
|
if (issues.length > 0 && !opts.checkOnly) {
|
|
542
|
-
|
|
568
|
+
fixed = runRepairs(results);
|
|
543
569
|
console.log(`\n ${fixed}/${issues.length} issue(s) addressed.`);
|
|
544
570
|
}
|
|
545
571
|
|
|
546
|
-
|
|
572
|
+
const unresolved = unresolvedCount({
|
|
573
|
+
checkOnly: opts.checkOnly, issueCount: issues.length, fixed,
|
|
574
|
+
});
|
|
575
|
+
return { results, issueCount: issues.length, unresolved };
|
|
547
576
|
}
|
|
548
577
|
|
|
549
|
-
module.exports = { runDiagnostics, formatReport, runRepairs, runDoctor, surveyHookCoverage, relicRepairGuard, classifyEmbeddings, detectEmbedModel, devBuildCommand };
|
|
578
|
+
module.exports = { runDiagnostics, formatReport, runRepairs, runDoctor, unresolvedCount, surveyHookCoverage, relicRepairGuard, classifyEmbeddings, detectEmbedModel, devBuildCommand };
|
|
550
579
|
|
|
551
580
|
if (require.main === module) {
|
|
552
581
|
const args = process.argv.slice(2);
|
|
553
582
|
const checkOnly = args.includes('--check-only');
|
|
554
|
-
const {
|
|
555
|
-
process.exit(
|
|
583
|
+
const { unresolved } = runDoctor({ checkOnly });
|
|
584
|
+
process.exit(unresolved > 0 ? 1 : 0);
|
|
556
585
|
}
|
|
@@ -165,3 +165,51 @@ test('detectEmbedModel reads model_available from `health-check --json`; probe f
|
|
|
165
165
|
// no binary → null
|
|
166
166
|
assert.equal(detectEmbedModel(null), null);
|
|
167
167
|
});
|
|
168
|
+
|
|
169
|
+
test('unresolvedCount: repair mode exits 0 iff every found issue was fixed', () => {
|
|
170
|
+
const { unresolvedCount } = require('./doctor');
|
|
171
|
+
// Clean run — nothing found.
|
|
172
|
+
assert.equal(unresolvedCount({ checkOnly: false, issueCount: 0, fixed: 0 }), 0);
|
|
173
|
+
// Repair fixed everything ("N/N addressed") → 0, so `doctor && …` and
|
|
174
|
+
// self-heal automation don't read a successful repair as failure. This is
|
|
175
|
+
// the regression this contract guards: previously exited 1 on any issue found.
|
|
176
|
+
assert.equal(unresolvedCount({ checkOnly: false, issueCount: 3, fixed: 3 }), 0);
|
|
177
|
+
// Partial repair → the remainder is unresolved (nonzero → exit 1).
|
|
178
|
+
assert.equal(unresolvedCount({ checkOnly: false, issueCount: 3, fixed: 1 }), 2);
|
|
179
|
+
// Advisory-only issue with no working auto-repair (fixed stays 0) → unresolved.
|
|
180
|
+
assert.equal(unresolvedCount({ checkOnly: false, issueCount: 1, fixed: 0 }), 1);
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
test('unresolvedCount: --check-only reports every found issue (never repairs)', () => {
|
|
184
|
+
const { unresolvedCount } = require('./doctor');
|
|
185
|
+
// check-only performs no repair, so fixed is 0; a found issue must still
|
|
186
|
+
// surface as unresolved (exit 1) — check mode reports cleanliness.
|
|
187
|
+
assert.equal(unresolvedCount({ checkOnly: true, issueCount: 2, fixed: 0 }), 2);
|
|
188
|
+
assert.equal(unresolvedCount({ checkOnly: true, issueCount: 0, fixed: 0 }), 0);
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
test('runRepairs: hooks-invalid counts fixed only when the post-install re-scan is clean', () => {
|
|
192
|
+
// hooks-invalid is raised only after diagnosis already ran install()+re-scan
|
|
193
|
+
// and paths were STILL broken. The repair arm must re-verify, else it reports
|
|
194
|
+
// a false exit 0 ("healthy") while the hooks stay broken. Stub the lifecycle
|
|
195
|
+
// deps runRepairs pulls via require('./lifecycle') on the shared cached export
|
|
196
|
+
// object; restore in finally so no other test sees the stubs.
|
|
197
|
+
const { runRepairs } = require('./doctor');
|
|
198
|
+
const lc = require('./lifecycle');
|
|
199
|
+
const orig = { install: lc.install, scan: lc.scanForBrokenPaths, relic: lc.isStaleRelicContext };
|
|
200
|
+
const hooksInvalid = [{ name: 'Hooks', status: 'warn', fixId: 'hooks-invalid' }];
|
|
201
|
+
try {
|
|
202
|
+
lc.isStaleRelicContext = () => false; // not a relic → repair proceeds
|
|
203
|
+
lc.install = () => {}; // install() that cannot restore the paths
|
|
204
|
+
// Re-scan still broken → must NOT count as fixed (old code did fixed++ blindly).
|
|
205
|
+
lc.scanForBrokenPaths = () => [{ type: 'hook', event: 'PreToolUse:Edit', path: '/gone.js' }];
|
|
206
|
+
assert.equal(runRepairs(hooksInvalid), 0, 'still-broken after install must not count as fixed');
|
|
207
|
+
// Re-scan clean → the repair took effect → counts as fixed.
|
|
208
|
+
lc.scanForBrokenPaths = () => [];
|
|
209
|
+
assert.equal(runRepairs(hooksInvalid), 1, 'verified-clean after install counts as fixed');
|
|
210
|
+
} finally {
|
|
211
|
+
lc.install = orig.install;
|
|
212
|
+
lc.scanForBrokenPaths = orig.scan;
|
|
213
|
+
lc.isStaleRelicContext = orig.relic;
|
|
214
|
+
}
|
|
215
|
+
});
|
|
@@ -983,8 +983,10 @@ if (require.main === module) {
|
|
|
983
983
|
} else if (cmd === 'doctor') {
|
|
984
984
|
const { runDoctor } = require('./doctor');
|
|
985
985
|
const checkOnly = process.argv.includes('--check-only');
|
|
986
|
-
|
|
987
|
-
|
|
986
|
+
// Exit on issues that remain UNRESOLVED after repair, not issues found —
|
|
987
|
+
// a run that fixes everything exits 0. See unresolvedCount in doctor.js.
|
|
988
|
+
const { unresolved } = runDoctor({ checkOnly });
|
|
989
|
+
process.exit(unresolved > 0 ? 1 : 0);
|
|
988
990
|
} else if (cmd === 'verify-hooks-fire') {
|
|
989
991
|
// v0.67.0 — Layer-A firing self-test. Spawned detached by session-init
|
|
990
992
|
// (off the SessionStart budget); writes a small state file the next
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sdsrs/code-graph",
|
|
3
|
-
"version": "0.85.
|
|
3
|
+
"version": "0.85.4",
|
|
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.85.
|
|
39
|
-
"@sdsrs/code-graph-linux-arm64": "0.85.
|
|
40
|
-
"@sdsrs/code-graph-darwin-x64": "0.85.
|
|
41
|
-
"@sdsrs/code-graph-darwin-arm64": "0.85.
|
|
42
|
-
"@sdsrs/code-graph-win32-x64": "0.85.
|
|
38
|
+
"@sdsrs/code-graph-linux-x64": "0.85.4",
|
|
39
|
+
"@sdsrs/code-graph-linux-arm64": "0.85.4",
|
|
40
|
+
"@sdsrs/code-graph-darwin-x64": "0.85.4",
|
|
41
|
+
"@sdsrs/code-graph-darwin-arm64": "0.85.4",
|
|
42
|
+
"@sdsrs/code-graph-win32-x64": "0.85.4"
|
|
43
43
|
}
|
|
44
44
|
}
|