@sdsrs/code-graph 0.8.3 → 0.8.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.
@@ -4,7 +4,7 @@
4
4
  "author": {
5
5
  "name": "sdsrs"
6
6
  },
7
- "version": "0.8.3",
7
+ "version": "0.8.4",
8
8
  "keywords": [
9
9
  "code-graph",
10
10
  "ast",
@@ -310,11 +310,18 @@ async function checkForUpdate() {
310
310
  if (isDevMode()) return null;
311
311
 
312
312
  const state = readState();
313
+ // manifest.version is authoritative — /plugin update writes it directly and
314
+ // bypasses auto-update.js, so re-sync state.installedVersion every call.
315
+ const installedVersion = readManifest().version || '0.0.0';
313
316
 
314
317
  // Time-based throttle
315
318
  if (!shouldCheck(state)) {
316
- if (state.updateAvailable && state.latestVersion) {
317
- return { updateAvailable: true, from: state.installedVersion, to: state.latestVersion };
319
+ if (state.installedVersion !== installedVersion) {
320
+ saveState({ ...state, installedVersion });
321
+ }
322
+ if (state.updateAvailable && state.latestVersion
323
+ && compareVersions(state.latestVersion, installedVersion) > 0) {
324
+ return { updateAvailable: true, from: installedVersion, to: state.latestVersion };
318
325
  }
319
326
  return null;
320
327
  }
@@ -322,21 +329,19 @@ async function checkForUpdate() {
322
329
  // Check GitHub for latest release
323
330
  const latest = await fetchLatestRelease();
324
331
  if (!latest) {
325
- saveState({ ...state, lastCheck: new Date().toISOString() });
332
+ saveState({ ...state, installedVersion, lastCheck: new Date().toISOString() });
326
333
  return null;
327
334
  }
328
335
 
329
336
  // Compare versions
330
- const manifest = readManifest();
331
- const currentVersion = manifest.version || '0.0.0';
332
- const hasUpdate = compareVersions(latest.version, currentVersion) > 0;
337
+ const hasUpdate = compareVersions(latest.version, installedVersion) > 0;
333
338
 
334
339
  if (hasUpdate) {
335
340
  const result = await downloadAndInstall(latest);
336
341
  const success = result.pluginUpdated;
337
342
  const newState = {
338
343
  lastCheck: new Date().toISOString(),
339
- installedVersion: success ? latest.version : currentVersion,
344
+ installedVersion: success ? latest.version : installedVersion,
340
345
  latestVersion: latest.version,
341
346
  updateAvailable: !success,
342
347
  lastUpdate: success ? new Date().toISOString() : state.lastUpdate,
@@ -349,7 +354,7 @@ async function checkForUpdate() {
349
354
  updateAvailable: !success,
350
355
  updated: success,
351
356
  binaryUpdated: result.binaryUpdated,
352
- from: currentVersion,
357
+ from: installedVersion,
353
358
  to: latest.version,
354
359
  };
355
360
  }
@@ -357,6 +362,7 @@ async function checkForUpdate() {
357
362
  // No update needed
358
363
  saveState({
359
364
  ...state,
365
+ installedVersion,
360
366
  lastCheck: new Date().toISOString(),
361
367
  latestVersion: latest.version,
362
368
  updateAvailable: false,
@@ -14,12 +14,14 @@ const {
14
14
  promoteVerifiedBinary,
15
15
  } = require('./auto-update');
16
16
 
17
- function mkDir(prefix) {
18
- return fs.mkdtempSync(path.join(os.tmpdir(), prefix));
17
+ function mkDir(t, prefix) {
18
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), prefix));
19
+ t.after(() => fs.rmSync(dir, { recursive: true, force: true }));
20
+ return dir;
19
21
  }
20
22
 
21
- test('getExtractedPluginVersion reads extracted plugin manifest version', () => {
22
- const root = mkDir('code-graph-plugin-');
23
+ test('getExtractedPluginVersion reads extracted plugin manifest version', (t) => {
24
+ const root = mkDir(t, 'code-graph-plugin-');
23
25
  const manifest = path.join(root, '.claude-plugin', 'plugin.json');
24
26
  fs.mkdirSync(path.dirname(manifest), { recursive: true });
25
27
  fs.writeFileSync(manifest, JSON.stringify({ version: '1.2.3' }, null, 2));
@@ -41,8 +43,8 @@ function writeFakeBinary(filePath, version) {
41
43
  fs.chmodSync(filePath, 0o755);
42
44
  }
43
45
 
44
- test('promoteVerifiedBinary accepts a runnable binary with the expected version', () => {
45
- const dir = mkDir('code-graph-bin-');
46
+ test('promoteVerifiedBinary accepts a runnable binary with the expected version', (t) => {
47
+ const dir = mkDir(t, 'code-graph-bin-');
46
48
  const tmp = path.join(dir, 'code-graph-mcp.tmp');
47
49
  const dst = path.join(dir, 'code-graph-mcp');
48
50
  writeFakeBinary(tmp, '1.2.3');
@@ -53,8 +55,8 @@ test('promoteVerifiedBinary accepts a runnable binary with the expected version'
53
55
  assert.equal(fs.existsSync(dst), true);
54
56
  });
55
57
 
56
- test('promoteVerifiedBinary rejects binaries with mismatched version', () => {
57
- const dir = mkDir('code-graph-bin-');
58
+ test('promoteVerifiedBinary rejects binaries with mismatched version', (t) => {
59
+ const dir = mkDir(t, 'code-graph-bin-');
58
60
  const tmp = path.join(dir, 'code-graph-mcp.tmp');
59
61
  const dst = path.join(dir, 'code-graph-mcp');
60
62
  writeFakeBinary(tmp, '1.2.2');
@@ -12,8 +12,10 @@ const lifecycleCli = path.join(__dirname, 'lifecycle.js');
12
12
  const compositeCli = path.join(__dirname, 'statusline-composite.js');
13
13
  const currentVersion = JSON.parse(fs.readFileSync(path.join(repoRoot, 'package.json'), 'utf8')).version;
14
14
 
15
- function mkHome() {
16
- return fs.mkdtempSync(path.join(os.tmpdir(), 'code-graph-e2e-'));
15
+ function mkHome(t) {
16
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'code-graph-e2e-'));
17
+ t.after(() => fs.rmSync(dir, { recursive: true, force: true }));
18
+ return dir;
17
19
  }
18
20
 
19
21
  function writeJson(filePath, value) {
@@ -38,8 +40,8 @@ function runScript(homeDir, scriptPath, args = [], options = {}) {
38
40
  }).toString();
39
41
  }
40
42
 
41
- test('lifecycle CLI handles install, disable self-heal, re-enable, and uninstall', () => {
42
- const homeDir = mkHome();
43
+ test('lifecycle CLI handles install, disable self-heal, re-enable, and uninstall', (t) => {
44
+ const homeDir = mkHome(t);
43
45
  const settingsPath = path.join(homeDir, '.claude', 'settings.json');
44
46
  const installedPath = path.join(homeDir, '.claude', 'plugins', 'installed_plugins.json');
45
47
  const registryPath = path.join(homeDir, '.cache', 'code-graph', 'statusline-registry.json');
@@ -9,8 +9,10 @@ const { execFileSync } = require('child_process');
9
9
  const lifecyclePath = path.join(__dirname, 'lifecycle.js');
10
10
  const statuslinePath = path.join(__dirname, 'statusline.js');
11
11
 
12
- function mkHome() {
13
- return fs.mkdtempSync(path.join(os.tmpdir(), 'code-graph-home-'));
12
+ function mkHome(t) {
13
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'code-graph-home-'));
14
+ t.after(() => fs.rmSync(dir, { recursive: true, force: true }));
15
+ return dir;
14
16
  }
15
17
 
16
18
  function writeJson(filePath, value) {
@@ -48,8 +50,8 @@ function seedOrphanedComposite(homeDir) {
48
50
  return { settingsPath, registryPath };
49
51
  }
50
52
 
51
- test('cleanupDisabledStatusline restores previous statusline and removes registry', () => {
52
- const homeDir = mkHome();
53
+ test('cleanupDisabledStatusline restores previous statusline and removes registry', (t) => {
54
+ const homeDir = mkHome(t);
53
55
  const { settingsPath, registryPath } = seedDisabledComposite(homeDir);
54
56
 
55
57
  const out = execFileSync(process.execPath, ['-e', `
@@ -63,10 +65,11 @@ test('cleanupDisabledStatusline restores previous statusline and removes registr
63
65
  assert.equal(fs.existsSync(registryPath), false);
64
66
  });
65
67
 
66
- test('statusline exits cleanly and self-heals when plugin is disabled', () => {
67
- const homeDir = mkHome();
68
+ test('statusline exits cleanly and self-heals when plugin is disabled', (t) => {
69
+ const homeDir = mkHome(t);
68
70
  const { settingsPath, registryPath } = seedDisabledComposite(homeDir);
69
71
  const projectDir = fs.mkdtempSync(path.join(os.tmpdir(), 'code-graph-project-'));
72
+ t.after(() => fs.rmSync(projectDir, { recursive: true, force: true }));
70
73
  fs.mkdirSync(path.join(projectDir, '.code-graph'), { recursive: true });
71
74
  fs.writeFileSync(path.join(projectDir, '.code-graph', 'index.db'), '');
72
75
 
@@ -81,8 +84,8 @@ test('statusline exits cleanly and self-heals when plugin is disabled', () => {
81
84
  assert.equal(fs.existsSync(registryPath), false);
82
85
  });
83
86
 
84
- test('cleanupDisabledStatusline also heals orphaned statusline after uninstall', () => {
85
- const homeDir = mkHome();
87
+ test('cleanupDisabledStatusline also heals orphaned statusline after uninstall', (t) => {
88
+ const homeDir = mkHome(t);
86
89
  const { settingsPath, registryPath } = seedOrphanedComposite(homeDir);
87
90
 
88
91
  const out = execFileSync(process.execPath, ['-e', `
@@ -175,8 +178,8 @@ test('removeHooksFromSettings strips our entries but keeps unrelated hooks', ()
175
178
  assert.ok(!s.hooks.PostToolUse, 'empty event key should be deleted');
176
179
  });
177
180
 
178
- test('install() removes legacy code-graph hooks from settings.json without re-registering', () => {
179
- const homeDir = mkHome();
181
+ test('install() removes legacy code-graph hooks from settings.json without re-registering', (t) => {
182
+ const homeDir = mkHome(t);
180
183
  const settingsPath = path.join(homeDir, '.claude', 'settings.json');
181
184
  writeJson(settingsPath, {
182
185
  statusLine: { type: 'command', command: 'echo previous-status' },
@@ -83,9 +83,10 @@ test('consistencyCheck returns empty array when binary version matches plugin',
83
83
  assert.ok(Array.isArray(result));
84
84
  });
85
85
 
86
- test('consistencyCheck returns version-mismatch when versions differ', () => {
86
+ test('consistencyCheck returns version-mismatch when versions differ', (t) => {
87
87
  const os = require('os');
88
88
  const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'cc-'));
89
+ t.after(() => fs.rmSync(dir, { recursive: true, force: true }));
89
90
  const bin = path.join(dir, 'code-graph-mcp');
90
91
  fs.writeFileSync(bin, [
91
92
  '#!/usr/bin/env bash',
@@ -5,15 +5,17 @@ const fs = require('fs');
5
5
  const os = require('os');
6
6
  const path = require('path');
7
7
 
8
- function mkDir(prefix) {
9
- return fs.mkdtempSync(path.join(os.tmpdir(), prefix));
8
+ function mkDir(t, prefix) {
9
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), prefix));
10
+ t.after(() => fs.rmSync(dir, { recursive: true, force: true }));
11
+ return dir;
10
12
  }
11
13
 
12
14
  // ── readBinaryVersion ──
13
15
 
14
- test('readBinaryVersion returns version from valid binary', () => {
16
+ test('readBinaryVersion returns version from valid binary', (t) => {
15
17
  const { readBinaryVersion } = require('./version-utils');
16
- const dir = mkDir('vu-');
18
+ const dir = mkDir(t, 'vu-');
17
19
  const bin = path.join(dir, 'code-graph-mcp');
18
20
  fs.writeFileSync(bin, [
19
21
  '#!/usr/bin/env bash',
@@ -32,9 +34,9 @@ test('readBinaryVersion returns null for non-existent binary', () => {
32
34
  assert.equal(readBinaryVersion('/tmp/does-not-exist-binary'), null);
33
35
  });
34
36
 
35
- test('readBinaryVersion returns null for binary with unexpected output', () => {
37
+ test('readBinaryVersion returns null for binary with unexpected output', (t) => {
36
38
  const { readBinaryVersion } = require('./version-utils');
37
- const dir = mkDir('vu-');
39
+ const dir = mkDir(t, 'vu-');
38
40
  const bin = path.join(dir, 'code-graph-mcp');
39
41
  fs.writeFileSync(bin, '#!/usr/bin/env bash\necho "something else"');
40
42
  fs.chmodSync(bin, 0o755);
@@ -56,9 +58,9 @@ test('getNewestMtime returns 0 for non-existent directory', () => {
56
58
  assert.equal(getNewestMtime('/tmp/no-such-dir-xyz'), 0);
57
59
  });
58
60
 
59
- test('getNewestMtime finds newest .rs file mtime', () => {
61
+ test('getNewestMtime finds newest .rs file mtime', (t) => {
60
62
  const { getNewestMtime } = require('./version-utils');
61
- const dir = mkDir('vu-mtime-');
63
+ const dir = mkDir(t, 'vu-mtime-');
62
64
  const sub = path.join(dir, 'sub');
63
65
  fs.mkdirSync(sub);
64
66
 
@@ -75,9 +77,9 @@ test('getNewestMtime finds newest .rs file mtime', () => {
75
77
  assert.equal(result, newerMtime, 'should return exactly the newest file mtime');
76
78
  });
77
79
 
78
- test('getNewestMtime ignores non-matching extensions', () => {
80
+ test('getNewestMtime ignores non-matching extensions', (t) => {
79
81
  const { getNewestMtime } = require('./version-utils');
80
- const dir = mkDir('vu-ext-');
82
+ const dir = mkDir(t, 'vu-ext-');
81
83
  fs.writeFileSync(path.join(dir, 'file.js'), 'hello');
82
84
  assert.equal(getNewestMtime(dir, '.rs'), 0);
83
85
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sdsrs/code-graph",
3
- "version": "0.8.3",
3
+ "version": "0.8.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": {
@@ -34,10 +34,10 @@
34
34
  "node": ">=16"
35
35
  },
36
36
  "optionalDependencies": {
37
- "@sdsrs/code-graph-linux-x64": "0.8.3",
38
- "@sdsrs/code-graph-linux-arm64": "0.8.3",
39
- "@sdsrs/code-graph-darwin-x64": "0.8.3",
40
- "@sdsrs/code-graph-darwin-arm64": "0.8.3",
41
- "@sdsrs/code-graph-win32-x64": "0.8.3"
37
+ "@sdsrs/code-graph-linux-x64": "0.8.4",
38
+ "@sdsrs/code-graph-linux-arm64": "0.8.4",
39
+ "@sdsrs/code-graph-darwin-x64": "0.8.4",
40
+ "@sdsrs/code-graph-darwin-arm64": "0.8.4",
41
+ "@sdsrs/code-graph-win32-x64": "0.8.4"
42
42
  }
43
43
  }