@sdsrs/code-graph 0.7.8 → 0.7.10
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/mcp-launcher.js +23 -0
- package/claude-plugin/scripts/session-init.js +66 -4
- package/claude-plugin/scripts/session-init.test.js +22 -1
- package/claude-plugin/scripts/user-prompt-context.js +23 -0
- package/package.json +6 -6
|
@@ -49,6 +49,23 @@ if (!binary) {
|
|
|
49
49
|
process.exit(1);
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
+
// Pre-spawn: verify binary is executable (catches macOS quarantine, permission issues)
|
|
53
|
+
try {
|
|
54
|
+
fs.accessSync(binary, fs.constants.X_OK);
|
|
55
|
+
} catch {
|
|
56
|
+
process.stderr.write(`[code-graph] Binary not executable: ${binary}\n`);
|
|
57
|
+
if (process.platform === 'darwin') {
|
|
58
|
+
process.stderr.write(
|
|
59
|
+
'macOS may be quarantining the downloaded binary. Fix with:\n' +
|
|
60
|
+
` xattr -d com.apple.quarantine "${binary}"\n` +
|
|
61
|
+
` chmod +x "${binary}"\n`
|
|
62
|
+
);
|
|
63
|
+
} else {
|
|
64
|
+
process.stderr.write(`Fix: chmod +x "${binary}"\n`);
|
|
65
|
+
}
|
|
66
|
+
process.exit(1);
|
|
67
|
+
}
|
|
68
|
+
|
|
52
69
|
// Spawn binary with stdio inheritance for MCP JSON-RPC
|
|
53
70
|
const child = spawn(binary, ['serve'], {
|
|
54
71
|
stdio: 'inherit',
|
|
@@ -57,6 +74,12 @@ const child = spawn(binary, ['serve'], {
|
|
|
57
74
|
|
|
58
75
|
child.on('error', (err) => {
|
|
59
76
|
process.stderr.write(`[code-graph] Failed to start: ${err.message}\n`);
|
|
77
|
+
if (process.platform === 'darwin' && (err.code === 'EACCES' || err.code === 'EPERM')) {
|
|
78
|
+
process.stderr.write(
|
|
79
|
+
'macOS may be blocking this binary. Try:\n' +
|
|
80
|
+
` xattr -d com.apple.quarantine "${binary}"\n`
|
|
81
|
+
);
|
|
82
|
+
}
|
|
60
83
|
process.exit(1);
|
|
61
84
|
});
|
|
62
85
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
'use strict';
|
|
3
|
-
const { spawn, execSync } = require('child_process');
|
|
3
|
+
const { spawn, execSync, execFileSync } = require('child_process');
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const os = require('os');
|
|
6
6
|
const fs = require('fs');
|
|
@@ -82,6 +82,63 @@ function ensureIndexFresh() {
|
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
+
/**
|
|
86
|
+
* Verify binary is available and executable.
|
|
87
|
+
* On macOS, detect Gatekeeper quarantine (common after npm/GitHub download).
|
|
88
|
+
* Returns { available, binary, issue? }.
|
|
89
|
+
*/
|
|
90
|
+
function verifyBinary() {
|
|
91
|
+
const { findBinary } = require('./find-binary');
|
|
92
|
+
const binary = findBinary();
|
|
93
|
+
if (!binary) {
|
|
94
|
+
process.stderr.write(
|
|
95
|
+
'[code-graph] Binary not found — MCP server cannot start.\n' +
|
|
96
|
+
'Install: npm install -g @sdsrs/code-graph\n'
|
|
97
|
+
);
|
|
98
|
+
return { available: false, binary: null };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Check executable permission
|
|
102
|
+
try {
|
|
103
|
+
fs.accessSync(binary, fs.constants.X_OK);
|
|
104
|
+
} catch {
|
|
105
|
+
process.stderr.write(
|
|
106
|
+
`[code-graph] Binary not executable: ${binary}\n` +
|
|
107
|
+
`Fix: chmod +x "${binary}"\n`
|
|
108
|
+
);
|
|
109
|
+
if (process.platform === 'darwin') {
|
|
110
|
+
process.stderr.write(`Also try: xattr -d com.apple.quarantine "${binary}"\n`);
|
|
111
|
+
}
|
|
112
|
+
return { available: false, binary, issue: 'not-executable' };
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// On macOS, verify the binary can actually run (Gatekeeper may block it)
|
|
116
|
+
if (process.platform === 'darwin') {
|
|
117
|
+
try {
|
|
118
|
+
execFileSync(binary, ['--version'], { timeout: 3000, stdio: 'pipe' });
|
|
119
|
+
} catch (err) {
|
|
120
|
+
const msg = (err.message || '') + (err.stderr ? err.stderr.toString() : '');
|
|
121
|
+
if (msg.includes('quarantine') || msg.includes('not permitted') ||
|
|
122
|
+
msg.includes('killed') || err.status === 137 || err.signal === 'SIGKILL') {
|
|
123
|
+
process.stderr.write(
|
|
124
|
+
`[code-graph] macOS Gatekeeper is blocking the binary: ${binary}\n` +
|
|
125
|
+
`Fix: xattr -d com.apple.quarantine "${binary}"\n` +
|
|
126
|
+
`Then restart Claude Code to reconnect the MCP server.\n`
|
|
127
|
+
);
|
|
128
|
+
return { available: false, binary, issue: 'quarantine' };
|
|
129
|
+
}
|
|
130
|
+
// Other errors (e.g., missing libs) — still report
|
|
131
|
+
process.stderr.write(
|
|
132
|
+
`[code-graph] Binary found but failed to run: ${binary}\n` +
|
|
133
|
+
`Error: ${msg.slice(0, 200)}\n`
|
|
134
|
+
);
|
|
135
|
+
return { available: false, binary, issue: 'runtime-error' };
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return { available: true, binary };
|
|
140
|
+
}
|
|
141
|
+
|
|
85
142
|
function runSessionInit() {
|
|
86
143
|
if (isPluginInactive()) {
|
|
87
144
|
cleanupDisabledStatusline();
|
|
@@ -97,10 +154,14 @@ function runSessionInit() {
|
|
|
97
154
|
}
|
|
98
155
|
|
|
99
156
|
const lifecycle = syncLifecycleConfig();
|
|
157
|
+
|
|
158
|
+
// Verify binary availability — catch issues early with actionable diagnostics
|
|
159
|
+
const binaryCheck = verifyBinary();
|
|
160
|
+
|
|
100
161
|
const autoUpdateLaunched = launchBackgroundAutoUpdate();
|
|
101
|
-
const indexFreshness = ensureIndexFresh();
|
|
102
|
-
const mapInjected = injectProjectMap();
|
|
103
|
-
return { inactive: false, lifecycle, autoUpdateLaunched, indexFreshness, mapInjected };
|
|
162
|
+
const indexFreshness = binaryCheck.available ? ensureIndexFresh() : 'skipped';
|
|
163
|
+
const mapInjected = binaryCheck.available ? injectProjectMap() : false;
|
|
164
|
+
return { inactive: false, lifecycle, autoUpdateLaunched, indexFreshness, mapInjected, binaryCheck };
|
|
104
165
|
}
|
|
105
166
|
|
|
106
167
|
/**
|
|
@@ -137,6 +198,7 @@ module.exports = {
|
|
|
137
198
|
syncLifecycleConfig,
|
|
138
199
|
ensureIndexFresh,
|
|
139
200
|
injectProjectMap,
|
|
201
|
+
verifyBinary,
|
|
140
202
|
runSessionInit,
|
|
141
203
|
};
|
|
142
204
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
const test = require('node:test');
|
|
3
3
|
const assert = require('node:assert/strict');
|
|
4
4
|
|
|
5
|
-
const { launchBackgroundAutoUpdate, syncLifecycleConfig, ensureIndexFresh } = require('./session-init');
|
|
5
|
+
const { launchBackgroundAutoUpdate, syncLifecycleConfig, ensureIndexFresh, verifyBinary } = require('./session-init');
|
|
6
6
|
|
|
7
7
|
test('syncLifecycleConfig is exported as a callable helper', () => {
|
|
8
8
|
assert.equal(typeof syncLifecycleConfig, 'function');
|
|
@@ -24,6 +24,27 @@ test('ensureIndexFresh returns skipped when no index exists', () => {
|
|
|
24
24
|
}
|
|
25
25
|
});
|
|
26
26
|
|
|
27
|
+
test('verifyBinary returns available:true when binary is found and executable', () => {
|
|
28
|
+
const result = verifyBinary();
|
|
29
|
+
// In dev repo, binary should be found (target/release/code-graph-mcp)
|
|
30
|
+
if (result.available) {
|
|
31
|
+
assert.equal(typeof result.binary, 'string');
|
|
32
|
+
assert.ok(result.binary.length > 0);
|
|
33
|
+
} else {
|
|
34
|
+
// Binary not built — still verify the return shape
|
|
35
|
+
assert.equal(result.available, false);
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test('verifyBinary returns structured result with expected shape', () => {
|
|
40
|
+
const result = verifyBinary();
|
|
41
|
+
assert.equal(typeof result.available, 'boolean');
|
|
42
|
+
assert.ok('binary' in result);
|
|
43
|
+
if (!result.available && result.binary) {
|
|
44
|
+
assert.ok('issue' in result);
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
|
|
27
48
|
test('launchBackgroundAutoUpdate spawns detached silent updater', () => {
|
|
28
49
|
const calls = [];
|
|
29
50
|
|
|
@@ -8,6 +8,29 @@ const fs = require('fs');
|
|
|
8
8
|
const path = require('path');
|
|
9
9
|
const os = require('os');
|
|
10
10
|
|
|
11
|
+
// --- Mid-session install detection ---
|
|
12
|
+
// If hooks are running but lifecycle install() hasn't executed yet (no manifest),
|
|
13
|
+
// the plugin was installed mid-session and the MCP server isn't connected.
|
|
14
|
+
// Claude Code only starts MCP servers at session startup; /mcp reconnect cannot
|
|
15
|
+
// start servers that were never initialized.
|
|
16
|
+
const MANIFEST_PATH = path.join(os.homedir(), '.cache', 'code-graph', 'install-manifest.json');
|
|
17
|
+
if (!fs.existsSync(MANIFEST_PATH)) {
|
|
18
|
+
const noticeFile = path.join(os.tmpdir(), '.code-graph-mcp-restart-notice');
|
|
19
|
+
try {
|
|
20
|
+
// Show once per hour to avoid spam
|
|
21
|
+
if (Date.now() - fs.statSync(noticeFile).mtimeMs < 3600000) process.exit(0);
|
|
22
|
+
} catch { /* first notice */ }
|
|
23
|
+
try { fs.writeFileSync(noticeFile, ''); } catch { /* ok */ }
|
|
24
|
+
process.stdout.write(
|
|
25
|
+
'[code-graph] Plugin installed — MCP server requires a session restart to connect.\n' +
|
|
26
|
+
'MCP servers are only initialized at session startup. To activate:\n' +
|
|
27
|
+
' 1. Press Ctrl+C to exit the current session\n' +
|
|
28
|
+
' 2. Re-run `claude` to start a new session\n' +
|
|
29
|
+
'Meanwhile, CLI tools work directly: code-graph-mcp search <query>, code-graph-mcp map, etc.\n'
|
|
30
|
+
);
|
|
31
|
+
process.exit(0);
|
|
32
|
+
}
|
|
33
|
+
|
|
11
34
|
// --- Rate limiting ---
|
|
12
35
|
const flag = path.join(os.tmpdir(), '.code-graph-prompt-ctx');
|
|
13
36
|
const COOLDOWN_MS = 60 * 1000; // 1 minute between injections
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sdsrs/code-graph",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.10",
|
|
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.10",
|
|
37
|
+
"@sdsrs/code-graph-linux-arm64": "0.7.10",
|
|
38
|
+
"@sdsrs/code-graph-darwin-x64": "0.7.10",
|
|
39
|
+
"@sdsrs/code-graph-darwin-arm64": "0.7.10",
|
|
40
|
+
"@sdsrs/code-graph-win32-x64": "0.7.10"
|
|
41
41
|
}
|
|
42
42
|
}
|