@sdsrs/code-graph 0.76.1 → 0.76.3

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.
@@ -4,7 +4,7 @@
4
4
  "author": {
5
5
  "name": "sdsrs"
6
6
  },
7
- "version": "0.76.1",
7
+ "version": "0.76.3",
8
8
  "keywords": [
9
9
  "code-graph",
10
10
  "ast",
@@ -354,9 +354,10 @@ async function downloadAndInstall(latest, {
354
354
  exec = execFileSync,
355
355
  downloadBin = downloadBinary,
356
356
  refreshMarketplace = refreshMarketplaceClone,
357
+ cmdExists = commandExists,
357
358
  } = {}) {
358
359
  // Pre-flight: check required CLI tools before attempting any download
359
- const missingTools = ['curl', 'tar'].filter(cmd => !commandExists(cmd));
360
+ const missingTools = ['curl', 'tar'].filter(cmd => !cmdExists(cmd));
360
361
  if (missingTools.length > 0) {
361
362
  console.error(`[code-graph] Auto-update skipped: missing required tools: ${missingTools.join(', ')}. Install them to enable auto-updates.`);
362
363
  return { pluginUpdated: false, binaryUpdated: false };
@@ -393,30 +394,35 @@ async function downloadAndInstall(latest, {
393
394
  pluginUpdated = true;
394
395
  }
395
396
 
396
- // Update installed_plugins.json to point to new version
397
- const installedPath = installedPluginsPath();
398
- try {
399
- const installed = readJson(installedPath);
400
- if (installed && installed.plugins && installed.plugins[PLUGIN_ID]) {
401
- installed.plugins[PLUGIN_ID][0].installPath = pluginDst;
402
- installed.plugins[PLUGIN_ID][0].version = latest.version;
403
- installed.plugins[PLUGIN_ID][0].lastUpdated = new Date().toISOString();
404
- writeJsonAtomic(installedPath, installed);
405
- }
406
- } catch { /* not fatal */ }
407
-
408
- // Update install manifest
409
- try {
410
- const manifest = readManifest();
411
- manifest.version = latest.version;
412
- manifest.updatedAt = new Date().toISOString();
413
- writeJsonAtomic(path.join(CACHE_DIR, 'install-manifest.json'), manifest);
414
- } catch { /* not fatal */ }
415
-
416
- // Run the NEW lifecycle.js to update settings.json hooks with new paths.
417
- // Without this, settings.json hooks still point to the old version directory
418
- // until the next session's self-heal corrects them.
397
+ // Repoint state at the new version ONLY if the plugin copy actually landed.
398
+ // Guarding on pluginUpdated: when the copy above was skipped (pluginSrc absent, or
399
+ // its plugin.json version drifted from the tag — the project's version sync is known
400
+ // fragile), pluginDst was never created. Advancing installPath/manifest to it anyway
401
+ // pointed Claude Code at a nonexistent install dir while state read "up to date".
419
402
  if (pluginUpdated) {
403
+ // Update installed_plugins.json to point to new version
404
+ const installedPath = installedPluginsPath();
405
+ try {
406
+ const installed = readJson(installedPath);
407
+ if (installed && installed.plugins && installed.plugins[PLUGIN_ID]) {
408
+ installed.plugins[PLUGIN_ID][0].installPath = pluginDst;
409
+ installed.plugins[PLUGIN_ID][0].version = latest.version;
410
+ installed.plugins[PLUGIN_ID][0].lastUpdated = new Date().toISOString();
411
+ writeJsonAtomic(installedPath, installed);
412
+ }
413
+ } catch { /* not fatal */ }
414
+
415
+ // Update install manifest
416
+ try {
417
+ const manifest = readManifest();
418
+ manifest.version = latest.version;
419
+ manifest.updatedAt = new Date().toISOString();
420
+ writeJsonAtomic(path.join(CACHE_DIR, 'install-manifest.json'), manifest);
421
+ } catch { /* not fatal */ }
422
+
423
+ // Run the NEW lifecycle.js to update settings.json hooks with new paths.
424
+ // Without this, settings.json hooks still point to the old version directory
425
+ // until the next session's self-heal corrects them.
420
426
  try {
421
427
  const newLifecycle = path.join(pluginDst, 'scripts', 'lifecycle.js');
422
428
  if (fs.existsSync(newLifecycle)) {
@@ -355,6 +355,7 @@ test('downloadAndInstall wires the marketplace refresh + binary download (orches
355
355
  let binDownloads = 0;
356
356
  const result = await downloadAndInstall(latest, {
357
357
  exec,
358
+ cmdExists: () => true, // don't depend on host curl/tar
358
359
  refreshMarketplace: () => { refreshed++; return true; },
359
360
  downloadBin: async () => { binDownloads++; return true; },
360
361
  });
@@ -376,3 +377,58 @@ test('downloadAndInstall wires the marketplace refresh + binary download (orches
376
377
  'code-graph-mcp', 'code-graph-mcp', '9.9.9', '.claude-plugin', 'plugin.json');
377
378
  assert.equal(fs.existsSync(dst), true, 'plugin copied into sandbox plugins cache');
378
379
  });
380
+
381
+ test('downloadAndInstall does NOT repoint install state when the plugin copy is skipped (version drift)', async (t) => {
382
+ // Guard for a silent-breakage bug: when the extracted tarball's plugin.json version
383
+ // doesn't match latest.version, the copy is skipped and pluginDst is never created.
384
+ // installed_plugins.json must NOT be advanced to that nonexistent dir, or Claude Code
385
+ // ends up pointed at a missing install while state reads "up to date".
386
+ const sandboxHome = mkDir(t, 'code-graph-dai-skip-');
387
+ const installedDir = path.join(sandboxHome, '.claude', 'plugins');
388
+ fs.mkdirSync(installedDir, { recursive: true });
389
+ const installedPath = path.join(installedDir, 'installed_plugins.json');
390
+ fs.writeFileSync(installedPath, JSON.stringify({
391
+ plugins: { 'code-graph-mcp@code-graph-mcp': [
392
+ { installPath: '/old/install/path', version: '0.0.1', lastUpdated: 'seed' },
393
+ ] },
394
+ }));
395
+
396
+ const script = `
397
+ const fs = require('fs');
398
+ const path = require('path');
399
+ const { downloadAndInstall } = require(${JSON.stringify(path.join(__dirname, 'auto-update.js'))});
400
+ const latest = { version: '9.9.9', tarballUrl: 'https://example/tar', binaryUrl: null };
401
+ const exec = (cmd, args) => {
402
+ if (cmd === 'tar') {
403
+ // Extract a claude-plugin/ whose version DRIFTS from latest → copy is skipped.
404
+ const tmpDir = args[args.indexOf('-C') + 1];
405
+ const mDir = path.join(tmpDir, 'claude-plugin', '.claude-plugin');
406
+ fs.mkdirSync(mDir, { recursive: true });
407
+ fs.writeFileSync(path.join(mDir, 'plugin.json'), JSON.stringify({ version: '0.0.0' }));
408
+ }
409
+ };
410
+ (async () => {
411
+ const result = await downloadAndInstall(latest, {
412
+ exec,
413
+ cmdExists: () => true, // don't depend on host curl/tar — exercise the guard deterministically
414
+ refreshMarketplace: () => true,
415
+ downloadBin: async () => true,
416
+ });
417
+ console.log(JSON.stringify({ result }));
418
+ })();
419
+ `;
420
+ const out = execGit(process.execPath, ['-e', script], {
421
+ env: { ...process.env, HOME: sandboxHome },
422
+ encoding: 'utf8',
423
+ });
424
+ const { result } = JSON.parse(out.trim().split('\n').pop());
425
+ assert.equal(result.pluginUpdated, false, 'version drift must skip the plugin copy');
426
+
427
+ // The pre-seeded record must be UNTOUCHED — not repointed to the 9.9.9 dir.
428
+ const rec = JSON.parse(fs.readFileSync(installedPath, 'utf8'))
429
+ .plugins['code-graph-mcp@code-graph-mcp'][0];
430
+ assert.equal(rec.installPath, '/old/install/path',
431
+ 'installPath must not be repointed when the copy was skipped');
432
+ assert.equal(rec.version, '0.0.1',
433
+ 'version must not be advanced when the copy was skipped');
434
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sdsrs/code-graph",
3
- "version": "0.76.1",
3
+ "version": "0.76.3",
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.76.1",
39
- "@sdsrs/code-graph-linux-arm64": "0.76.1",
40
- "@sdsrs/code-graph-darwin-x64": "0.76.1",
41
- "@sdsrs/code-graph-darwin-arm64": "0.76.1",
42
- "@sdsrs/code-graph-win32-x64": "0.76.1"
38
+ "@sdsrs/code-graph-linux-x64": "0.76.3",
39
+ "@sdsrs/code-graph-linux-arm64": "0.76.3",
40
+ "@sdsrs/code-graph-darwin-x64": "0.76.3",
41
+ "@sdsrs/code-graph-darwin-arm64": "0.76.3",
42
+ "@sdsrs/code-graph-win32-x64": "0.76.3"
43
43
  }
44
44
  }