@sdsrs/code-graph 0.119.0 → 0.120.1
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/scripts/adopt.js +12 -2
- package/claude-plugin/scripts/auto-update.js +17 -2
- package/claude-plugin/scripts/find-binary.js +40 -6
- package/claude-plugin/scripts/lifecycle.js +99 -6
- package/claude-plugin/scripts/session-init.js +51 -8
- package/claude-plugin/templates/code-graph-snapshot.yml +1 -1
- package/package.json +6 -6
|
@@ -808,7 +808,17 @@ function unadopt({ cwd, home } = {}) {
|
|
|
808
808
|
// Also sweep any legacy memory-dir remnants (uninstall before auto-migration ran).
|
|
809
809
|
const migrated = migrateLegacyMemoryDir({ cwd, home });
|
|
810
810
|
|
|
811
|
-
|
|
811
|
+
// Deregister ONLY what we actually cleaned. The registry is the sole record
|
|
812
|
+
// of which repos carry a managed block — `uninstall --unadopt-all` and the
|
|
813
|
+
// uninstall-time sweep are both driven by it — so dropping the entry for a
|
|
814
|
+
// project whose CLAUDE.md we could not rewrite (root-owned file, read-only
|
|
815
|
+
// mount, EPERM dir) strands that block with nothing left pointing at it.
|
|
816
|
+
// Harmless while the caller only ever passed the current cwd; load-bearing
|
|
817
|
+
// since the uninstall sweep walks the whole list, because an unconditional
|
|
818
|
+
// deregister empties the file and removeCacheResidue() only preserves a
|
|
819
|
+
// NON-EMPTY registry — so the failed project's record died with the cache.
|
|
820
|
+
const cleanupFailed = claudeMdUnwritable || claudeMdUnreadable;
|
|
821
|
+
const registryUpdated = cleanupFailed ? false : removeAdopted(effectiveCwd, home);
|
|
812
822
|
return {
|
|
813
823
|
ok: true, fileRemoved, blockPruned, claudeMdRemoved,
|
|
814
824
|
claudeMdUnreadable, claudeMdUnwritable, registryUpdated,
|
|
@@ -889,7 +899,7 @@ if (require.main === module) {
|
|
|
889
899
|
|
|
890
900
|
module.exports = {
|
|
891
901
|
adopt, unadopt, memoryDir, formatResult, stripSentinelBlock,
|
|
892
|
-
readAdoptedProjects, recordAdopted, removeAdopted, adoptedRegistryFile,
|
|
902
|
+
readAdoptedProjects, readAdoptedResult, recordAdopted, removeAdopted, adoptedRegistryFile,
|
|
893
903
|
isAdopted, isPluginModeInstall, maybeAutoAdopt, needsRefresh, isProjectRoot,
|
|
894
904
|
detectProjectType, buildBlock, buildTriggerRows, migrateLegacyMemoryDir,
|
|
895
905
|
claudeMdPath, detailDir, detailPath,
|
|
@@ -1063,7 +1063,12 @@ async function checkForUpdate({ installMissing = false, force = false, requestJs
|
|
|
1063
1063
|
// that set it, and kept polling GitHub on the ordinary interval while
|
|
1064
1064
|
// already rate-limited. Dead code since the backoff was written.
|
|
1065
1065
|
saveState({ ...readState(), installedVersion, lastCheck: new Date().toISOString() });
|
|
1066
|
-
|
|
1066
|
+
// NOT null: null is the CLI's "nothing to do" and it prints "Up to date
|
|
1067
|
+
// (vX)". A failed fetch means the opposite — the update status is
|
|
1068
|
+
// UNKNOWN — and a user running this because they are stuck on an old
|
|
1069
|
+
// version was being told the old version is current (offline, captive
|
|
1070
|
+
// portal, proxy, GitHub 5xx all land here).
|
|
1071
|
+
return { noop: true, reason: 'fetch-failed', from: installedVersion };
|
|
1067
1072
|
}
|
|
1068
1073
|
|
|
1069
1074
|
// Compare versions
|
|
@@ -1079,7 +1084,9 @@ async function checkForUpdate({ installMissing = false, force = false, requestJs
|
|
|
1079
1084
|
// against our own parent.
|
|
1080
1085
|
if (process.env.CODE_GRAPH_INSTALL_LOCK_HELD !== '1') {
|
|
1081
1086
|
installLock = acquireLock(path.join(CACHE_DIR, 'install.lock'));
|
|
1082
|
-
|
|
1087
|
+
// Reason, not null — same misreport as the fetch failure above: the
|
|
1088
|
+
// holder is mid-update, so "Up to date (v<old>)" is exactly wrong.
|
|
1089
|
+
if (!installLock) return { noop: true, reason: 'install-lock-held', from: installedVersion };
|
|
1083
1090
|
}
|
|
1084
1091
|
|
|
1085
1092
|
if (hasUpdate) {
|
|
@@ -1266,6 +1273,14 @@ if (require.main === module) {
|
|
|
1266
1273
|
console.log(`Update available: v${result.to} (auto-install failed)`);
|
|
1267
1274
|
} else if (result && result.binaryUpdated) {
|
|
1268
1275
|
console.log(`Repaired binary cache (v${result.to})`);
|
|
1276
|
+
} else if (result && result.noop && result.reason === 'fetch-failed') {
|
|
1277
|
+
// Message only — the exit code stays 0 on purpose: an unreachable
|
|
1278
|
+
// GitHub is not a failure of this command, and the launcher's install
|
|
1279
|
+
// chain plus `doctor` both spawn it.
|
|
1280
|
+
console.log(`Could not reach GitHub — update status UNKNOWN (still on v${result.from}). ` +
|
|
1281
|
+
'Retried on the next session; run `code-graph-mcp doctor` if it persists.');
|
|
1282
|
+
} else if (result && result.noop && result.reason === 'install-lock-held') {
|
|
1283
|
+
console.log(`Another session is installing/updating right now — check skipped (on v${result.from}).`);
|
|
1269
1284
|
} else if (!installMissing && isAutoUpdateDisabled()) {
|
|
1270
1285
|
console.log('CODE_GRAPH_NO_AUTO_UPDATE=1 — auto-update skipped');
|
|
1271
1286
|
} else if (!installMissing && isDevMode()) {
|
|
@@ -230,6 +230,36 @@ function isPlausibleReleaseBinary(candidate) {
|
|
|
230
230
|
try { return fs.statSync(candidate).size > 1_000_000; } catch { return false; }
|
|
231
231
|
}
|
|
232
232
|
|
|
233
|
+
/**
|
|
234
|
+
* Both places npm may put the platform optionalDependency inside a
|
|
235
|
+
* `node_modules` root, most-likely first:
|
|
236
|
+
*
|
|
237
|
+
* <root>/@sdsrs/code-graph-<plat>-<arch>/ (hoisted)
|
|
238
|
+
* <root>/@sdsrs/code-graph/node_modules/@sdsrs/code-graph-…/ (nested)
|
|
239
|
+
*
|
|
240
|
+
* Hoisting is an npm implementation detail, not a contract: npm 12 leaves the
|
|
241
|
+
* optionalDependency nested under the shell package on a plain
|
|
242
|
+
* `npm install -g @sdsrs/code-graph`, while older npm hoists it (both layouts
|
|
243
|
+
* were observed on one machine, 2026-08-17). Probing only the hoisted spelling
|
|
244
|
+
* made a SUCCESSFUL npm install read as "did not yield a binary": the launcher
|
|
245
|
+
* then re-downloaded ~41MB from GitHub, and — the part that outlives the
|
|
246
|
+
* session — never called `recordGlobalInstall()`, so `uninstall` had no marker
|
|
247
|
+
* proving the plugin owns those global packages and left them on PATH forever.
|
|
248
|
+
*
|
|
249
|
+
* Discovery only. The global-package INVENTORY scanners (staleGlobalPkgs,
|
|
250
|
+
* installedGlobalPkgs, doctor) stay top-level-on-purpose: a nested
|
|
251
|
+
* optionalDependency is the shell package's private dependency, so
|
|
252
|
+
* `npm install -g`/`npm uninstall -g` on its name would introduce or orphan a
|
|
253
|
+
* top-level global the user never asked for.
|
|
254
|
+
*/
|
|
255
|
+
function platformPkgBinaries(nodeModulesRoot) {
|
|
256
|
+
const pkg = `code-graph-${PLATFORM}-${ARCH}`;
|
|
257
|
+
return [
|
|
258
|
+
path.join(nodeModulesRoot, '@sdsrs', pkg, BINARY_NAME),
|
|
259
|
+
path.join(nodeModulesRoot, '@sdsrs', 'code-graph', 'node_modules', '@sdsrs', pkg, BINARY_NAME),
|
|
260
|
+
];
|
|
261
|
+
}
|
|
262
|
+
|
|
233
263
|
function platformBinaryCandidates() {
|
|
234
264
|
const out = [];
|
|
235
265
|
// Fast path: standard module resolution.
|
|
@@ -239,10 +269,11 @@ function platformBinaryCandidates() {
|
|
|
239
269
|
if (isNativeBinary(bin) && isPlausibleReleaseBinary(bin)) out.push(bin);
|
|
240
270
|
} catch { /* not in node_modules walk-up */ }
|
|
241
271
|
|
|
242
|
-
// Slow path: explicit global node_modules probe.
|
|
272
|
+
// Slow path: explicit global node_modules probe, both npm layouts.
|
|
243
273
|
for (const globalRoot of globalNodeModulesCandidates()) {
|
|
244
|
-
const bin
|
|
245
|
-
|
|
274
|
+
for (const bin of platformPkgBinaries(globalRoot)) {
|
|
275
|
+
if (isNativeBinary(bin) && isPlausibleReleaseBinary(bin)) out.push(bin);
|
|
276
|
+
}
|
|
246
277
|
}
|
|
247
278
|
|
|
248
279
|
return out;
|
|
@@ -363,9 +394,12 @@ function findBinaryUncached() {
|
|
|
363
394
|
const npxDir = path.join(os.homedir(), '.npm', '_npx');
|
|
364
395
|
try {
|
|
365
396
|
for (const entry of fs.readdirSync(npxDir)) {
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
397
|
+
// Same hoisted-or-nested question as the global prefix — an npx tree is
|
|
398
|
+
// just another node_modules root, and npm decides the layout there too.
|
|
399
|
+
for (const bin of platformPkgBinaries(path.join(npxDir, entry, 'node_modules'))) {
|
|
400
|
+
const hit = gate.consider(bin);
|
|
401
|
+
if (hit) return hit;
|
|
402
|
+
}
|
|
369
403
|
}
|
|
370
404
|
} catch { /* no npx cache */ }
|
|
371
405
|
|
|
@@ -588,10 +588,92 @@ function cleanupDisabledStatusline() {
|
|
|
588
588
|
// still run after `/plugin uninstall` — Claude Code stops loading the
|
|
589
589
|
// plugin's hooks.json, so the SessionStart teardown in session-init.js never
|
|
590
590
|
// fires post-uninstall. Without this, the ~40MB cached binary leaked forever.
|
|
591
|
+
//
|
|
592
|
+
// Adoption comes off in the SAME breath, and BEFORE the wipe: install's
|
|
593
|
+
// auto-adopt writes a managed block into every project's CLAUDE.md, and until
|
|
594
|
+
// now nothing reachable removed it. session-init.js owned that step, on the
|
|
595
|
+
// branch its own comment concedes "usually never runs again" after a real
|
|
596
|
+
// uninstall — so the block (steering Claude at a CLI that is being deleted two
|
|
597
|
+
// lines below) survived forever in every adopted repo. The registry that names
|
|
598
|
+
// those projects lives inside CACHE_DIR, which is exactly why this must run
|
|
599
|
+
// first: same capture-before-cleanup ordering the rest of this teardown
|
|
600
|
+
// already learned. This branch fires at most once — the write above removes
|
|
601
|
+
// our composite from settings.json, so Claude Code stops invoking us.
|
|
591
602
|
let cacheRemoved = false;
|
|
592
|
-
|
|
603
|
+
let unadopted = [];
|
|
604
|
+
let registryUnusable = false;
|
|
605
|
+
if (uninstalled) {
|
|
606
|
+
({ unadopted, registryUnusable } = unadoptRegisteredProjects());
|
|
607
|
+
cacheRemoved = removeCacheResidue();
|
|
608
|
+
}
|
|
593
609
|
|
|
594
|
-
return { cleaned: true, settingsChanged, cacheRemoved };
|
|
610
|
+
return { cleaned: true, settingsChanged, cacheRemoved, unadopted, registryUnusable };
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
/**
|
|
614
|
+
* Strip the managed CLAUDE.md block + generated `.claude/plugin_code_graph_mcp.md`
|
|
615
|
+
* from every project in the adopted-projects registry. `unadopt` is
|
|
616
|
+
* sentinel-guarded, so user prose outside the managed block is preserved and a
|
|
617
|
+
* project that was already cleaned is a no-op.
|
|
618
|
+
*
|
|
619
|
+
* Fully swallowed per project AND overall: this runs inside a statusline render
|
|
620
|
+
* (statusline.js / statusline-composite.js call the caller at their top), where
|
|
621
|
+
* an uncaught throw blanks the user's status line — and an unreadable project
|
|
622
|
+
* must not cost the remaining ones their cleanup.
|
|
623
|
+
*/
|
|
624
|
+
function unadoptRegisteredProjects() {
|
|
625
|
+
const out = [];
|
|
626
|
+
let readAdoptedResult, unadopt;
|
|
627
|
+
try { ({ readAdoptedResult, unadopt } = require('./adopt')); }
|
|
628
|
+
catch { return { unadopted: out, registryUnusable: false }; } // POSIX-only helper unavailable — teardown continues
|
|
629
|
+
// readAdoptedResult, NOT the lenient readAdoptedProjects(): that wrapper
|
|
630
|
+
// collapses "unreadable / truncated / wrong shape" into `[]`, which here is
|
|
631
|
+
// indistinguishable from "no projects to clean" — so a corrupt registry would
|
|
632
|
+
// silently sweep nothing AND still be deleted by removeCacheResidue() below,
|
|
633
|
+
// taking the only record of which repos carry a managed block with it. adopt.js
|
|
634
|
+
// documents the same bit for the same reason: only a genuinely ABSENT file may
|
|
635
|
+
// be read as empty.
|
|
636
|
+
let res;
|
|
637
|
+
try { res = readAdoptedResult(); } catch { return { unadopted: out, registryUnusable: true }; }
|
|
638
|
+
if (res && res.unusable) return { unadopted: out, registryUnusable: true };
|
|
639
|
+
for (const project of (res && res.list) || []) {
|
|
640
|
+
try {
|
|
641
|
+
const r = unadopt({ cwd: project });
|
|
642
|
+
out.push({ project, cleaned: !!(r && (r.blockPruned || r.fileRemoved || r.claudeMdRemoved)) });
|
|
643
|
+
} catch (e) {
|
|
644
|
+
out.push({ project, cleaned: false, error: (e && e.message) || String(e) });
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
reportUnadoptSweep(out);
|
|
648
|
+
return { unadopted: out, registryUnusable: false };
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
/**
|
|
652
|
+
* Tell the user their repos were just edited. Everything else about this
|
|
653
|
+
* teardown is invisible by construction: it runs inside a statusline render,
|
|
654
|
+
* both callers `process.exit(0)` on `cleaned` and discard the return value, and
|
|
655
|
+
* Claude Code fires no uninstall hook — so without this line the first signal is
|
|
656
|
+
* unexplained `CLAUDE.md` diffs in `git status` across several repositories.
|
|
657
|
+
* stderr, because a statusline's stdout IS the status line.
|
|
658
|
+
*/
|
|
659
|
+
function reportUnadoptSweep(entries) {
|
|
660
|
+
try {
|
|
661
|
+
const cleaned = entries.filter((e) => e && e.cleaned).map((e) => e.project);
|
|
662
|
+
const failed = entries.filter((e) => e && !e.cleaned).map((e) => e.project);
|
|
663
|
+
if (!cleaned.length && !failed.length) return;
|
|
664
|
+
const lines = [];
|
|
665
|
+
if (cleaned.length) {
|
|
666
|
+
lines.push(`[code-graph] Plugin uninstalled — removed the managed CLAUDE.md block from ${cleaned.length} project(s):`);
|
|
667
|
+
for (const p of cleaned.slice(0, 10)) lines.push(` ${p}`);
|
|
668
|
+
if (cleaned.length > 10) lines.push(` …and ${cleaned.length - 10} more`);
|
|
669
|
+
lines.push(' Your own text outside the block was kept; .code-graph/ index dirs are untouched.');
|
|
670
|
+
}
|
|
671
|
+
if (failed.length) {
|
|
672
|
+
lines.push(`[code-graph] Could NOT clean ${failed.length} project(s) — remove the block by hand or run \`code-graph-mcp unadopt\` there:`);
|
|
673
|
+
for (const p of failed.slice(0, 10)) lines.push(` ${p}`);
|
|
674
|
+
}
|
|
675
|
+
process.stderr.write(lines.join('\n') + '\n');
|
|
676
|
+
} catch { /* a notice must never be able to fail a teardown */ }
|
|
595
677
|
}
|
|
596
678
|
|
|
597
679
|
// --- Scope Conflict Detection ---
|
|
@@ -1672,9 +1754,20 @@ function removeCacheResidue() {
|
|
|
1672
1754
|
try {
|
|
1673
1755
|
registryPath = require('./adopt').adoptedRegistryFile();
|
|
1674
1756
|
const raw = fs.existsSync(registryPath) ? fs.readFileSync(registryPath) : null;
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1757
|
+
if (raw) {
|
|
1758
|
+
let parsed = null;
|
|
1759
|
+
let usable = true;
|
|
1760
|
+
try { parsed = JSON.parse(raw.toString('utf8')); } catch { usable = false; }
|
|
1761
|
+
// Preserve a NON-EMPTY list (projects still carry a block) and anything we
|
|
1762
|
+
// could not READ as a list. The unusable case used to fall into the same
|
|
1763
|
+
// "nothing to preserve" catch as a missing file — the strictly worse
|
|
1764
|
+
// outcome, since a registry we cannot parse is the one whose contents we
|
|
1765
|
+
// are least able to reconstruct, and the sweep above deliberately skips it
|
|
1766
|
+
// rather than guessing. Only a genuinely EMPTY array strands nothing and
|
|
1767
|
+
// is allowed to go with the cache dir.
|
|
1768
|
+
if (!usable || !Array.isArray(parsed) || parsed.length) registry = raw;
|
|
1769
|
+
}
|
|
1770
|
+
} catch { /* POSIX-only helper or an unreadable path — nothing to preserve */ }
|
|
1678
1771
|
try {
|
|
1679
1772
|
fs.rmSync(CACHE_DIR, { recursive: true, force: true });
|
|
1680
1773
|
} catch { return false; }
|
|
@@ -1690,7 +1783,7 @@ function removeCacheResidue() {
|
|
|
1690
1783
|
module.exports = {
|
|
1691
1784
|
install, uninstall, update, healthCheck, scanForBrokenPaths, checkScopeConflict,
|
|
1692
1785
|
isPluginExplicitlyDisabled, isPluginInactive, isPluginUninstalled, removeCacheResidue,
|
|
1693
|
-
cleanupDisabledStatusline,
|
|
1786
|
+
cleanupDisabledStatusline, unadoptRegisteredProjects,
|
|
1694
1787
|
readManifest, readJson, readJsonResult, readSettingsForWrite, writeJsonAtomic,
|
|
1695
1788
|
readRegistry, readRegistryForWrite, writeRegistry,
|
|
1696
1789
|
getPluginVersion, cleanupOldCacheVersions,
|
|
@@ -406,6 +406,30 @@ function ensureIndexFresh() {
|
|
|
406
406
|
return 'refreshing';
|
|
407
407
|
}
|
|
408
408
|
|
|
409
|
+
/**
|
|
410
|
+
* What to tell a user whose binary is missing. A missing binary is the NORMAL
|
|
411
|
+
* state of the first session after `/plugin install` — nothing ships the ~40MB
|
|
412
|
+
* engine with the plugin — and both automatic install paths are already
|
|
413
|
+
* running by the time the user reads this: launchBackgroundAutoUpdate() below
|
|
414
|
+
* (a missing binary bypasses the check throttle) and the MCP launcher's own
|
|
415
|
+
* install chain. Telling that user "MCP server cannot start. Install: npm
|
|
416
|
+
* install -g" reads as a failed install and sends them to fix something that
|
|
417
|
+
* is already fixing itself.
|
|
418
|
+
*
|
|
419
|
+
* The manual instruction is still the right answer when the user has opted out
|
|
420
|
+
* of auto-update, because then nothing else will fetch it. Pure so both arms
|
|
421
|
+
* are testable without a binary-less machine.
|
|
422
|
+
*/
|
|
423
|
+
function missingBinaryMessage(env = process.env) {
|
|
424
|
+
if (env.CODE_GRAPH_NO_AUTO_UPDATE === '1') {
|
|
425
|
+
return '[code-graph] Binary not found, and auto-download is off (CODE_GRAPH_NO_AUTO_UPDATE=1).\n' +
|
|
426
|
+
' Install it yourself: npm install -g @sdsrs/code-graph\n';
|
|
427
|
+
}
|
|
428
|
+
return '[code-graph] Binary not found — fetching it in the background (~40MB, first run only).\n' +
|
|
429
|
+
' Tools appear as soon as it lands; no restart needed.\n' +
|
|
430
|
+
' Still missing next session? Run `code-graph-mcp doctor`.\n';
|
|
431
|
+
}
|
|
432
|
+
|
|
409
433
|
/**
|
|
410
434
|
* Verify binary is available and executable.
|
|
411
435
|
* On macOS, detect Gatekeeper quarantine (common after npm/GitHub download).
|
|
@@ -415,10 +439,7 @@ function verifyBinary() {
|
|
|
415
439
|
const { findBinary } = require('./find-binary');
|
|
416
440
|
const binary = findBinary();
|
|
417
441
|
if (!binary) {
|
|
418
|
-
process.stderr.write(
|
|
419
|
-
'[code-graph] Binary not found — MCP server cannot start.\n' +
|
|
420
|
-
'Install: npm install -g @sdsrs/code-graph\n'
|
|
421
|
-
);
|
|
442
|
+
process.stderr.write(missingBinaryMessage());
|
|
422
443
|
return { available: false, binary: null };
|
|
423
444
|
}
|
|
424
445
|
|
|
@@ -527,6 +548,18 @@ function consistencyCheck(binary) {
|
|
|
527
548
|
return issues;
|
|
528
549
|
}
|
|
529
550
|
|
|
551
|
+
/**
|
|
552
|
+
* Do two paths name the same project? The registry stores what `adopt` was
|
|
553
|
+
* given and `process.cwd()` is what the shell resolved, so a symlinked repo
|
|
554
|
+
* path (`/tmp` → `/private/tmp` on macOS) compares unequal as raw strings.
|
|
555
|
+
* realpath both, fall back to the resolved literal when a side no longer exists.
|
|
556
|
+
*/
|
|
557
|
+
function samePath(a, b) {
|
|
558
|
+
if (!a || !b) return false;
|
|
559
|
+
const real = (p) => { try { return fs.realpathSync(p); } catch { return path.resolve(p); } };
|
|
560
|
+
return real(a) === real(b);
|
|
561
|
+
}
|
|
562
|
+
|
|
530
563
|
function runSessionInit({ source } = {}) {
|
|
531
564
|
// GC the shared tmp dir before anything else, so it happens even on the
|
|
532
565
|
// inactive / non-project early returns below — those sessions still wrote
|
|
@@ -543,7 +576,8 @@ function runSessionInit({ source } = {}) {
|
|
|
543
576
|
// Third caller of the same unguarded teardown (statusline.js and
|
|
544
577
|
// statusline-composite.js are the others): a read-only ~/.claude turns this
|
|
545
578
|
// into an uncaught throw that takes down the whole SessionStart hook.
|
|
546
|
-
|
|
579
|
+
let cleanup = null;
|
|
580
|
+
try { cleanup = cleanupDisabledStatusline(); } catch { /* best-effort teardown */ }
|
|
547
581
|
// Genuine uninstall (not a temporary disable) leaves residue the settings-only
|
|
548
582
|
// self-heal can't reach: ~/.cache/code-graph (the ~40MB binary + state) and the
|
|
549
583
|
// current project's CLAUDE.md adoption block. CC fires no uninstall hook, AND it
|
|
@@ -570,9 +604,18 @@ function runSessionInit({ source } = {}) {
|
|
|
570
604
|
// other projects is the preservation inside removeCacheResidue(); this
|
|
571
605
|
// ordering is the belt to that pair of braces, and it is what keeps the
|
|
572
606
|
// single-project case from depending on the preservation at all.
|
|
573
|
-
|
|
607
|
+
//
|
|
608
|
+
// cleanupDisabledStatusline() now sweeps the WHOLE adopted-projects
|
|
609
|
+
// registry (it is the only teardown that still runs after a real
|
|
610
|
+
// uninstall), so this cwd is usually already clean by the time we get
|
|
611
|
+
// here. Fold that in rather than re-deriving it: with the work moved
|
|
612
|
+
// earlier, the local `unadopt` returns "nothing to clean" and reporting
|
|
613
|
+
// `unadopted:false` for a project whose block IS gone would be a false
|
|
614
|
+
// negative in the one field a caller could act on.
|
|
615
|
+
let unadopted = !!(cleanup && Array.isArray(cleanup.unadopted)
|
|
616
|
+
&& cleanup.unadopted.some((u) => u && u.cleaned && samePath(u.project, process.cwd())));
|
|
574
617
|
try {
|
|
575
|
-
if (!isNonProjectCwd(process.cwd())) {
|
|
618
|
+
if (!unadopted && !isNonProjectCwd(process.cwd())) {
|
|
576
619
|
const r = unadopt({ cwd: process.cwd() });
|
|
577
620
|
unadopted = !!(r && (r.blockPruned || r.fileRemoved || r.claudeMdRemoved));
|
|
578
621
|
}
|
|
@@ -924,7 +967,7 @@ module.exports = {
|
|
|
924
967
|
indexNeedsRevalidation,
|
|
925
968
|
injectProjectMap,
|
|
926
969
|
injectRecentImpact,
|
|
927
|
-
verifyBinary,
|
|
970
|
+
verifyBinary, missingBinaryMessage,
|
|
928
971
|
consistencyCheck,
|
|
929
972
|
runSessionInit,
|
|
930
973
|
computeQuietHooks,
|
|
@@ -35,7 +35,7 @@ jobs:
|
|
|
35
35
|
node-version: '20'
|
|
36
36
|
- name: Build snapshot
|
|
37
37
|
run: |
|
|
38
|
-
npx -y -p @sdsrs/code-graph@0.
|
|
38
|
+
npx -y -p @sdsrs/code-graph@0.120.1 code-graph-mcp snapshot create --out snapshot.db
|
|
39
39
|
zstd -9 snapshot.db -o snapshot.db.zst
|
|
40
40
|
mv snapshot.db.zst "code-graph-snapshot-${GITHUB_SHA:0:7}.db.zst"
|
|
41
41
|
- name: Upload to release
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sdsrs/code-graph",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.120.1",
|
|
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.120.1",
|
|
39
|
+
"@sdsrs/code-graph-linux-arm64": "0.120.1",
|
|
40
|
+
"@sdsrs/code-graph-darwin-x64": "0.120.1",
|
|
41
|
+
"@sdsrs/code-graph-darwin-arm64": "0.120.1",
|
|
42
|
+
"@sdsrs/code-graph-win32-x64": "0.120.1"
|
|
43
43
|
}
|
|
44
44
|
}
|