@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.
- package/claude-plugin/.claude-plugin/plugin.json +1 -1
- package/claude-plugin/scripts/lifecycle.js +39 -1
- package/claude-plugin/scripts/mcp-launcher.js +1 -13
- package/claude-plugin/scripts/session-init.js +38 -1
- package/claude-plugin/scripts/session-init.test.js +17 -1
- package/claude-plugin/scripts/user-prompt-context.js +4 -2
- package/package.json +6 -6
|
@@ -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
|
|
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 {
|
|
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
|
-
|
|
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.
|
|
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.
|
|
37
|
-
"@sdsrs/code-graph-linux-arm64": "0.7.
|
|
38
|
-
"@sdsrs/code-graph-darwin-x64": "0.7.
|
|
39
|
-
"@sdsrs/code-graph-darwin-arm64": "0.7.
|
|
40
|
-
"@sdsrs/code-graph-win32-x64": "0.7.
|
|
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
|
}
|