@sdsrs/code-graph 0.7.2 → 0.7.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.7.2",
7
+ "version": "0.7.4",
8
8
  "keywords": [
9
9
  "code-graph",
10
10
  "ast",
@@ -364,15 +364,53 @@ function update() {
364
364
  manifest.updatedAt = new Date().toISOString();
365
365
  writeManifest(manifest);
366
366
 
367
+ // 6. Clean up old cached versions (keep latest 3)
368
+ cleanupOldCacheVersions(3);
369
+
367
370
  return { oldVersion, version, settingsChanged };
368
371
  }
369
372
 
373
+ /**
374
+ * Remove old plugin cache versions, keeping the N most recent.
375
+ * Cache layout: ~/.claude/plugins/cache/<marketplace>/<plugin>/<version>/
376
+ */
377
+ function cleanupOldCacheVersions(keep = 3) {
378
+ const cacheParent = path.join(os.homedir(), '.claude', 'plugins', 'cache', MARKETPLACE_NAME);
379
+ try {
380
+ // List all subdirectories under the marketplace cache
381
+ const entries = fs.readdirSync(cacheParent, { withFileTypes: true });
382
+ for (const entry of entries) {
383
+ if (!entry.isDirectory()) continue;
384
+ const pluginDir = path.join(cacheParent, entry.name);
385
+ try {
386
+ const versions = fs.readdirSync(pluginDir, { withFileTypes: true })
387
+ .filter(d => d.isDirectory())
388
+ .map(d => ({
389
+ name: d.name,
390
+ path: path.join(pluginDir, d.name),
391
+ mtime: fs.statSync(path.join(pluginDir, d.name)).mtimeMs,
392
+ }))
393
+ .sort((a, b) => b.mtime - a.mtime); // newest first
394
+
395
+ if (versions.length <= keep) continue;
396
+
397
+ const toRemove = versions.slice(keep);
398
+ for (const v of toRemove) {
399
+ try {
400
+ fs.rmSync(v.path, { recursive: true, force: true });
401
+ } catch { /* permission error or in-use — skip */ }
402
+ }
403
+ } catch { /* can't read plugin dir — skip */ }
404
+ }
405
+ } catch { /* cache dir doesn't exist — nothing to clean */ }
406
+ }
407
+
370
408
  module.exports = {
371
409
  install, uninstall, update, checkScopeConflict,
372
410
  isPluginExplicitlyDisabled, isPluginInactive, cleanupDisabledStatusline,
373
411
  readManifest, readJson, writeJsonAtomic,
374
412
  readRegistry, writeRegistry,
375
- getPluginVersion,
413
+ getPluginVersion, cleanupOldCacheVersions,
376
414
  PLUGIN_ID, OLD_PLUGIN_IDS, MARKETPLACE_NAME, CACHE_DIR, REGISTRY_FILE,
377
415
  };
378
416
 
@@ -37,19 +37,7 @@ if (!binary) {
37
37
  process.stderr.write(`[code-graph] Installed at ${binary}\n`);
38
38
  }
39
39
  } catch {
40
- process.stderr.write('[code-graph] npm install failed. Trying direct download...\n');
41
- }
42
-
43
- // Fallback: direct binary download from GitHub release
44
- if (!binary) {
45
- try {
46
- const { downloadBinarySync } = require('./auto-update');
47
- if (typeof downloadBinarySync === 'function') {
48
- downloadBinarySync(version);
49
- clearCache();
50
- binary = findBinary();
51
- }
52
- } catch { /* not available */ }
40
+ process.stderr.write('[code-graph] npm install failed.\n');
53
41
  }
54
42
  }
55
43
 
@@ -47,6 +47,41 @@ function syncLifecycleConfig() {
47
47
  return 'noop';
48
48
  }
49
49
 
50
+ /**
51
+ * Check if the index is stale by comparing git HEAD timestamp vs index.db mtime.
52
+ * If stale, spawn background incremental-index to refresh.
53
+ * Returns 'fresh' | 'refreshing' | 'skipped'.
54
+ */
55
+ function ensureIndexFresh() {
56
+ const { findBinary } = require('./find-binary');
57
+ const bin = findBinary();
58
+ if (!bin) return 'skipped';
59
+
60
+ const cwd = process.cwd();
61
+ const dbPath = path.join(cwd, '.code-graph', 'index.db');
62
+ if (!fs.existsSync(dbPath)) return 'skipped';
63
+
64
+ try {
65
+ const dbMtime = fs.statSync(dbPath).mtimeMs;
66
+ // Compare with git HEAD commit timestamp
67
+ const gitTs = parseInt(
68
+ execSync('git log -1 --format=%ct', { cwd, timeout: 2000, encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] }).trim()
69
+ ) * 1000;
70
+ if (gitTs <= dbMtime) return 'fresh';
71
+
72
+ // Index is stale — run incremental-index in background
73
+ const child = spawn(bin, ['incremental-index', '--quiet'], {
74
+ cwd,
75
+ detached: true,
76
+ stdio: 'ignore',
77
+ });
78
+ if (child && typeof child.unref === 'function') child.unref();
79
+ return 'refreshing';
80
+ } catch {
81
+ return 'skipped';
82
+ }
83
+ }
84
+
50
85
  function runSessionInit() {
51
86
  if (isPluginInactive()) {
52
87
  cleanupDisabledStatusline();
@@ -63,8 +98,9 @@ function runSessionInit() {
63
98
 
64
99
  const lifecycle = syncLifecycleConfig();
65
100
  const autoUpdateLaunched = launchBackgroundAutoUpdate();
101
+ const indexFreshness = ensureIndexFresh();
66
102
  const mapInjected = injectProjectMap();
67
- return { inactive: false, lifecycle, autoUpdateLaunched, mapInjected };
103
+ return { inactive: false, lifecycle, autoUpdateLaunched, indexFreshness, mapInjected };
68
104
  }
69
105
 
70
106
  /**
@@ -99,6 +135,7 @@ function injectProjectMap() {
99
135
  module.exports = {
100
136
  launchBackgroundAutoUpdate,
101
137
  syncLifecycleConfig,
138
+ ensureIndexFresh,
102
139
  injectProjectMap,
103
140
  runSessionInit,
104
141
  };
@@ -2,12 +2,28 @@
2
2
  const test = require('node:test');
3
3
  const assert = require('node:assert/strict');
4
4
 
5
- const { launchBackgroundAutoUpdate, syncLifecycleConfig } = require('./session-init');
5
+ const { launchBackgroundAutoUpdate, syncLifecycleConfig, ensureIndexFresh } = require('./session-init');
6
6
 
7
7
  test('syncLifecycleConfig is exported as a callable helper', () => {
8
8
  assert.equal(typeof syncLifecycleConfig, 'function');
9
9
  });
10
10
 
11
+ test('ensureIndexFresh is exported as a callable helper', () => {
12
+ assert.equal(typeof ensureIndexFresh, 'function');
13
+ });
14
+
15
+ test('ensureIndexFresh returns skipped when no index exists', () => {
16
+ const origCwd = process.cwd();
17
+ const tmpDir = require('node:os').tmpdir();
18
+ process.chdir(tmpDir);
19
+ try {
20
+ const result = ensureIndexFresh();
21
+ assert.equal(result, 'skipped');
22
+ } finally {
23
+ process.chdir(origCwd);
24
+ }
25
+ });
26
+
11
27
  test('launchBackgroundAutoUpdate spawns detached silent updater', () => {
12
28
  const calls = [];
13
29
 
@@ -3,7 +3,7 @@
3
3
  // UserPromptSubmit hook: inject relevant code-graph context based on user's question.
4
4
  // Only activates when user message references code entities + has understanding intent.
5
5
  // This is a CODE INDEX, not a memory store — only inject structural code context.
6
- const { execSync } = require('child_process');
6
+ const { execFileSync } = require('child_process');
7
7
  const fs = require('fs');
8
8
  const path = require('path');
9
9
  const os = require('os');
@@ -106,7 +106,9 @@ if (result && result.trim()) {
106
106
  // --- Helpers ---
107
107
 
108
108
  function run(cmd) {
109
- return execSync(cmd, {
109
+ const parts = cmd.match(/(?:[^\s"]+|"[^"]*")+/g) || [];
110
+ const args = parts.slice(1).map(a => a.replace(/^"|"$/g, ''));
111
+ return execFileSync(parts[0], args, {
110
112
  cwd,
111
113
  timeout: 3000,
112
114
  encoding: 'utf8',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sdsrs/code-graph",
3
- "version": "0.7.2",
3
+ "version": "0.7.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": {
@@ -33,10 +33,10 @@
33
33
  "node": ">=16"
34
34
  },
35
35
  "optionalDependencies": {
36
- "@sdsrs/code-graph-linux-x64": "0.7.2",
37
- "@sdsrs/code-graph-linux-arm64": "0.7.2",
38
- "@sdsrs/code-graph-darwin-x64": "0.7.2",
39
- "@sdsrs/code-graph-darwin-arm64": "0.7.2",
40
- "@sdsrs/code-graph-win32-x64": "0.7.2"
36
+ "@sdsrs/code-graph-linux-x64": "0.7.4",
37
+ "@sdsrs/code-graph-linux-arm64": "0.7.4",
38
+ "@sdsrs/code-graph-darwin-x64": "0.7.4",
39
+ "@sdsrs/code-graph-darwin-arm64": "0.7.4",
40
+ "@sdsrs/code-graph-win32-x64": "0.7.4"
41
41
  }
42
42
  }