@sdsrs/code-graph 0.4.3 → 0.4.5
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.
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
'use strict';
|
|
3
|
-
const {
|
|
3
|
+
const { execFileSync } = require('child_process');
|
|
4
4
|
const fs = require('fs');
|
|
5
5
|
const path = require('path');
|
|
6
6
|
const os = require('os');
|
|
7
|
-
const { CACHE_DIR, PLUGIN_ID, readManifest, readJson, writeJsonAtomic } = require('./lifecycle');
|
|
7
|
+
const { CACHE_DIR, PLUGIN_ID, MARKETPLACE_NAME, readManifest, readJson, writeJsonAtomic } = require('./lifecycle');
|
|
8
8
|
|
|
9
9
|
// ── Configuration ──────────────────────────────────────────
|
|
10
10
|
const GITHUB_REPO = 'sdsrss/code-graph-mcp';
|
|
@@ -87,6 +87,21 @@ async function fetchLatestRelease() {
|
|
|
87
87
|
} catch { return null; }
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
+
// ── Helpers ────────────────────────────────────────────────
|
|
91
|
+
|
|
92
|
+
function copyDirSync(src, dst) {
|
|
93
|
+
fs.mkdirSync(dst, { recursive: true });
|
|
94
|
+
for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
|
|
95
|
+
const srcPath = path.join(src, entry.name);
|
|
96
|
+
const dstPath = path.join(dst, entry.name);
|
|
97
|
+
if (entry.isDirectory()) {
|
|
98
|
+
copyDirSync(srcPath, dstPath);
|
|
99
|
+
} else {
|
|
100
|
+
fs.copyFileSync(srcPath, dstPath);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
90
105
|
// ── Download & Install ─────────────────────────────────────
|
|
91
106
|
|
|
92
107
|
async function downloadAndInstall(latest) {
|
|
@@ -94,25 +109,31 @@ async function downloadAndInstall(latest) {
|
|
|
94
109
|
try {
|
|
95
110
|
fs.mkdirSync(tmpDir, { recursive: true });
|
|
96
111
|
|
|
97
|
-
// 1. Download
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
112
|
+
// 1. Download tarball (safe: no shell interpolation)
|
|
113
|
+
const tarballPath = path.join(tmpDir, 'release.tar.gz');
|
|
114
|
+
execFileSync('curl', [
|
|
115
|
+
'-sL', '-o', tarballPath,
|
|
116
|
+
'-H', 'Accept: application/vnd.github+json',
|
|
117
|
+
latest.tarballUrl,
|
|
118
|
+
], { timeout: 30000, stdio: 'pipe' });
|
|
119
|
+
|
|
120
|
+
// 2. Extract tarball
|
|
121
|
+
execFileSync('tar', [
|
|
122
|
+
'xzf', tarballPath, '-C', tmpDir, '--strip-components=1',
|
|
123
|
+
], { timeout: 15000, stdio: 'pipe' });
|
|
102
124
|
|
|
103
|
-
//
|
|
125
|
+
// 3. Copy plugin files to cache (cross-platform)
|
|
104
126
|
const pluginSrc = path.join(tmpDir, 'claude-plugin');
|
|
105
127
|
const pluginDst = path.join(
|
|
106
|
-
os.homedir(), '.claude', 'plugins', 'cache',
|
|
128
|
+
os.homedir(), '.claude', 'plugins', 'cache', MARKETPLACE_NAME, 'code-graph', latest.version
|
|
107
129
|
);
|
|
108
130
|
|
|
109
131
|
if (fs.existsSync(pluginSrc)) {
|
|
110
132
|
fs.mkdirSync(pluginDst, { recursive: true });
|
|
111
|
-
|
|
112
|
-
execSync(`cp -r "${pluginSrc}/." "${pluginDst}/"`, { stdio: 'pipe' });
|
|
133
|
+
copyDirSync(pluginSrc, pluginDst);
|
|
113
134
|
}
|
|
114
135
|
|
|
115
|
-
//
|
|
136
|
+
// 4. Update installed_plugins.json to point to new version
|
|
116
137
|
const installedPath = path.join(os.homedir(), '.claude', 'plugins', 'installed_plugins.json');
|
|
117
138
|
try {
|
|
118
139
|
const installed = readJson(installedPath);
|
|
@@ -124,7 +145,7 @@ async function downloadAndInstall(latest) {
|
|
|
124
145
|
}
|
|
125
146
|
} catch { /* installed_plugins update failed — not fatal */ }
|
|
126
147
|
|
|
127
|
-
//
|
|
148
|
+
// 5. Update install manifest with tag version
|
|
128
149
|
try {
|
|
129
150
|
const manifest = readManifest();
|
|
130
151
|
manifest.version = latest.version;
|
|
@@ -132,9 +153,9 @@ async function downloadAndInstall(latest) {
|
|
|
132
153
|
writeJsonAtomic(path.join(CACHE_DIR, 'install-manifest.json'), manifest);
|
|
133
154
|
} catch { /* manifest update failed — not fatal */ }
|
|
134
155
|
|
|
135
|
-
//
|
|
156
|
+
// 6. Update npm binary (non-blocking, best-effort)
|
|
136
157
|
try {
|
|
137
|
-
|
|
158
|
+
execFileSync('npm', ['install', '-g', `${NPM_PACKAGE}@${latest.version}`], {
|
|
138
159
|
timeout: 60000,
|
|
139
160
|
stdio: 'pipe',
|
|
140
161
|
});
|
|
@@ -4,7 +4,9 @@ const fs = require('fs');
|
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const os = require('os');
|
|
6
6
|
|
|
7
|
-
const PLUGIN_ID = 'code-graph@sdsrss';
|
|
7
|
+
const PLUGIN_ID = 'code-graph@sdsrss-code-graph';
|
|
8
|
+
const OLD_PLUGIN_ID = 'code-graph@sdsrss'; // Legacy ID — kept for migration cleanup
|
|
9
|
+
const MARKETPLACE_NAME = 'sdsrss-code-graph';
|
|
8
10
|
const CACHE_DIR = path.join(os.homedir(), '.cache', 'code-graph');
|
|
9
11
|
const PLUGIN_ROOT = process.env.CLAUDE_PLUGIN_ROOT || path.resolve(__dirname, '..');
|
|
10
12
|
const MANIFEST_FILE = path.join(CACHE_DIR, 'install-manifest.json');
|
|
@@ -88,6 +90,45 @@ function unregisterStatuslineProvider(id) {
|
|
|
88
90
|
return true;
|
|
89
91
|
}
|
|
90
92
|
|
|
93
|
+
// --- Scope Conflict Detection ---
|
|
94
|
+
|
|
95
|
+
function checkScopeConflict() {
|
|
96
|
+
const installed = readJson(INSTALLED_PLUGINS_PATH);
|
|
97
|
+
if (!installed || !installed.plugins) return null;
|
|
98
|
+
for (const [key, entries] of Object.entries(installed.plugins)) {
|
|
99
|
+
if (key === PLUGIN_ID) continue;
|
|
100
|
+
if (key.startsWith('code-graph@')) {
|
|
101
|
+
return { existingId: key, scope: entries[0] && entries[0].scope, entries };
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// --- Migration: clean up old PLUGIN_ID remnants ---
|
|
108
|
+
|
|
109
|
+
function migrateOldPluginId(settings) {
|
|
110
|
+
let changed = false;
|
|
111
|
+
|
|
112
|
+
// Clean old ID from enabledPlugins
|
|
113
|
+
if (settings.enabledPlugins && OLD_PLUGIN_ID in settings.enabledPlugins) {
|
|
114
|
+
delete settings.enabledPlugins[OLD_PLUGIN_ID];
|
|
115
|
+
changed = true;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Clean old ID from installed_plugins.json
|
|
119
|
+
const installed = readJson(INSTALLED_PLUGINS_PATH);
|
|
120
|
+
if (installed && installed.plugins && OLD_PLUGIN_ID in installed.plugins) {
|
|
121
|
+
delete installed.plugins[OLD_PLUGIN_ID];
|
|
122
|
+
writeJsonAtomic(INSTALLED_PLUGINS_PATH, installed);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Clean old cache path (was using 'sdsrss' instead of 'sdsrss-code-graph')
|
|
126
|
+
const oldCacheDir = path.join(os.homedir(), '.claude', 'plugins', 'cache', 'sdsrss', 'code-graph');
|
|
127
|
+
try { fs.rmSync(oldCacheDir, { recursive: true, force: true }); } catch { /* ok */ }
|
|
128
|
+
|
|
129
|
+
return changed;
|
|
130
|
+
}
|
|
131
|
+
|
|
91
132
|
// --- Install (idempotent) ---
|
|
92
133
|
|
|
93
134
|
function install() {
|
|
@@ -96,6 +137,11 @@ function install() {
|
|
|
96
137
|
const settings = readJson(SETTINGS_PATH) || {};
|
|
97
138
|
let settingsChanged = false;
|
|
98
139
|
|
|
140
|
+
// 0. Migrate from old PLUGIN_ID
|
|
141
|
+
if (migrateOldPluginId(settings)) {
|
|
142
|
+
settingsChanged = true;
|
|
143
|
+
}
|
|
144
|
+
|
|
99
145
|
// 1. StatusLine — composite approach
|
|
100
146
|
// a. Capture existing statusline as a provider (if not already composite)
|
|
101
147
|
// b. Register code-graph as a provider
|
|
@@ -114,20 +160,16 @@ function install() {
|
|
|
114
160
|
// Register code-graph provider
|
|
115
161
|
registerStatuslineProvider('code-graph', codeGraphStatuslineCommand(), false);
|
|
116
162
|
|
|
117
|
-
//
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
settings.enabledPlugins[PLUGIN_ID] = true;
|
|
121
|
-
settingsChanged = true;
|
|
122
|
-
manifest.config.enabledPlugins = true;
|
|
123
|
-
}
|
|
163
|
+
// NOTE: enabledPlugins is managed by Claude Code's plugin system, not by lifecycle.
|
|
164
|
+
// Do NOT add enabledPlugins entries here — it causes phantom plugin entries
|
|
165
|
+
// when the ID doesn't match the marketplace name.
|
|
124
166
|
|
|
125
|
-
//
|
|
167
|
+
// 2. Write settings atomically if changed
|
|
126
168
|
if (settingsChanged) {
|
|
127
169
|
writeJsonAtomic(SETTINGS_PATH, settings);
|
|
128
170
|
}
|
|
129
171
|
|
|
130
|
-
//
|
|
172
|
+
// 3. Write manifest with version
|
|
131
173
|
manifest.version = version;
|
|
132
174
|
manifest.installedAt = manifest.installedAt || new Date().toISOString();
|
|
133
175
|
manifest.updatedAt = new Date().toISOString();
|
|
@@ -161,10 +203,14 @@ function uninstall() {
|
|
|
161
203
|
// else: other providers still using composite — leave it
|
|
162
204
|
}
|
|
163
205
|
|
|
164
|
-
// 2. Remove from enabledPlugins
|
|
165
|
-
if (settings.enabledPlugins
|
|
166
|
-
|
|
167
|
-
|
|
206
|
+
// 2. Remove both old and new IDs from enabledPlugins
|
|
207
|
+
if (settings.enabledPlugins) {
|
|
208
|
+
for (const id of [PLUGIN_ID, OLD_PLUGIN_ID]) {
|
|
209
|
+
if (id in settings.enabledPlugins) {
|
|
210
|
+
delete settings.enabledPlugins[id];
|
|
211
|
+
settingsChanged = true;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
168
214
|
}
|
|
169
215
|
|
|
170
216
|
// 3. Write settings if changed
|
|
@@ -173,19 +219,30 @@ function uninstall() {
|
|
|
173
219
|
}
|
|
174
220
|
}
|
|
175
221
|
|
|
176
|
-
// 4. Remove from installed_plugins.json
|
|
222
|
+
// 4. Remove both old and new IDs from installed_plugins.json
|
|
177
223
|
const installedPlugins = readJson(INSTALLED_PLUGINS_PATH);
|
|
178
|
-
if (installedPlugins && installedPlugins.plugins
|
|
179
|
-
|
|
180
|
-
|
|
224
|
+
if (installedPlugins && installedPlugins.plugins) {
|
|
225
|
+
let ipChanged = false;
|
|
226
|
+
for (const id of [PLUGIN_ID, OLD_PLUGIN_ID]) {
|
|
227
|
+
if (id in installedPlugins.plugins) {
|
|
228
|
+
delete installedPlugins.plugins[id];
|
|
229
|
+
ipChanged = true;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
if (ipChanged) writeJsonAtomic(INSTALLED_PLUGINS_PATH, installedPlugins);
|
|
181
233
|
}
|
|
182
234
|
|
|
183
235
|
// 5. Remove cache directory
|
|
184
236
|
try { fs.rmSync(CACHE_DIR, { recursive: true, force: true }); } catch { /* ok */ }
|
|
185
237
|
|
|
186
|
-
// 6. Remove plugin files from cache
|
|
187
|
-
const
|
|
188
|
-
|
|
238
|
+
// 6. Remove plugin files from cache (both old and new paths)
|
|
239
|
+
const pluginCacheDirs = [
|
|
240
|
+
path.join(os.homedir(), '.claude', 'plugins', 'cache', MARKETPLACE_NAME, 'code-graph'),
|
|
241
|
+
path.join(os.homedir(), '.claude', 'plugins', 'cache', 'sdsrss', 'code-graph'), // legacy
|
|
242
|
+
];
|
|
243
|
+
for (const dir of pluginCacheDirs) {
|
|
244
|
+
try { fs.rmSync(dir, { recursive: true, force: true }); } catch { /* ok */ }
|
|
245
|
+
}
|
|
189
246
|
|
|
190
247
|
return { settingsChanged };
|
|
191
248
|
}
|
|
@@ -199,6 +256,11 @@ function update() {
|
|
|
199
256
|
const settings = readJson(SETTINGS_PATH) || {};
|
|
200
257
|
let settingsChanged = false;
|
|
201
258
|
|
|
259
|
+
// 0. Migrate from old PLUGIN_ID
|
|
260
|
+
if (migrateOldPluginId(settings)) {
|
|
261
|
+
settingsChanged = true;
|
|
262
|
+
}
|
|
263
|
+
|
|
202
264
|
// 1. Update composite command path if version changed
|
|
203
265
|
if (isOurComposite(settings)) {
|
|
204
266
|
const cmd = compositeCommand();
|
|
@@ -211,23 +273,18 @@ function update() {
|
|
|
211
273
|
// 2. Update code-graph provider in registry
|
|
212
274
|
registerStatuslineProvider('code-graph', codeGraphStatuslineCommand(), false);
|
|
213
275
|
|
|
214
|
-
//
|
|
215
|
-
if (!settings.enabledPlugins) settings.enabledPlugins = {};
|
|
216
|
-
if (!(PLUGIN_ID in settings.enabledPlugins)) {
|
|
217
|
-
settings.enabledPlugins[PLUGIN_ID] = true;
|
|
218
|
-
settingsChanged = true;
|
|
219
|
-
}
|
|
276
|
+
// NOTE: enabledPlugins is managed by Claude Code's plugin system, not by lifecycle.
|
|
220
277
|
|
|
221
|
-
//
|
|
278
|
+
// 3. Write settings if changed
|
|
222
279
|
if (settingsChanged) {
|
|
223
280
|
writeJsonAtomic(SETTINGS_PATH, settings);
|
|
224
281
|
}
|
|
225
282
|
|
|
226
|
-
//
|
|
283
|
+
// 4. Clear update-check cache (force re-check after update)
|
|
227
284
|
const updateCache = path.join(CACHE_DIR, 'update-check');
|
|
228
285
|
try { fs.unlinkSync(updateCache); } catch { /* ok */ }
|
|
229
286
|
|
|
230
|
-
//
|
|
287
|
+
// 5. Update manifest
|
|
231
288
|
manifest.version = version;
|
|
232
289
|
manifest.updatedAt = new Date().toISOString();
|
|
233
290
|
writeManifest(manifest);
|
|
@@ -236,11 +293,11 @@ function update() {
|
|
|
236
293
|
}
|
|
237
294
|
|
|
238
295
|
module.exports = {
|
|
239
|
-
install, uninstall, update,
|
|
296
|
+
install, uninstall, update, checkScopeConflict,
|
|
240
297
|
readManifest, readJson, writeJsonAtomic,
|
|
241
298
|
readRegistry, writeRegistry,
|
|
242
299
|
getPluginVersion,
|
|
243
|
-
PLUGIN_ID, CACHE_DIR, REGISTRY_FILE,
|
|
300
|
+
PLUGIN_ID, OLD_PLUGIN_ID, MARKETPLACE_NAME, CACHE_DIR, REGISTRY_FILE,
|
|
244
301
|
};
|
|
245
302
|
|
|
246
303
|
// CLI: node lifecycle.js <install|uninstall|update>
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
'use strict';
|
|
3
3
|
const { execFileSync } = require('child_process');
|
|
4
4
|
const { findBinary } = require('./find-binary');
|
|
5
|
-
const { install, update, readManifest, getPluginVersion } = require('./lifecycle');
|
|
5
|
+
const { install, update, readManifest, getPluginVersion, checkScopeConflict } = require('./lifecycle');
|
|
6
6
|
const { checkForUpdate } = require('./auto-update');
|
|
7
7
|
|
|
8
8
|
const BIN = findBinary();
|
|
@@ -18,7 +18,16 @@ if (BIN) {
|
|
|
18
18
|
} catch { /* timeout — silent */ }
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
// --- 2.
|
|
21
|
+
// --- 2. Scope conflict warning ---
|
|
22
|
+
const conflict = checkScopeConflict();
|
|
23
|
+
if (conflict) {
|
|
24
|
+
process.stderr.write(
|
|
25
|
+
`[code-graph] Warning: conflicting install detected — ${conflict.existingId} (${conflict.scope || 'unknown'} scope). ` +
|
|
26
|
+
`Use /plugin to remove one to avoid config conflicts.\n`
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// --- 3. Lifecycle: install or update config (idempotent) ---
|
|
22
31
|
const manifest = readManifest();
|
|
23
32
|
const currentVersion = getPluginVersion();
|
|
24
33
|
|
|
@@ -28,7 +37,7 @@ if (!manifest.version) {
|
|
|
28
37
|
update();
|
|
29
38
|
}
|
|
30
39
|
|
|
31
|
-
// ---
|
|
40
|
+
// --- 4. Auto-update (throttled, non-blocking) ---
|
|
32
41
|
(async () => {
|
|
33
42
|
const result = await checkForUpdate();
|
|
34
43
|
if (result && result.updated) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sdsrs/code-graph",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.5",
|
|
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.4.
|
|
37
|
-
"@sdsrs/code-graph-linux-arm64": "0.4.
|
|
38
|
-
"@sdsrs/code-graph-darwin-x64": "0.4.
|
|
39
|
-
"@sdsrs/code-graph-darwin-arm64": "0.4.
|
|
40
|
-
"@sdsrs/code-graph-win32-x64": "0.4.
|
|
36
|
+
"@sdsrs/code-graph-linux-x64": "0.4.5",
|
|
37
|
+
"@sdsrs/code-graph-linux-arm64": "0.4.5",
|
|
38
|
+
"@sdsrs/code-graph-darwin-x64": "0.4.5",
|
|
39
|
+
"@sdsrs/code-graph-darwin-arm64": "0.4.5",
|
|
40
|
+
"@sdsrs/code-graph-win32-x64": "0.4.5"
|
|
41
41
|
}
|
|
42
42
|
}
|