@sdsrs/code-graph 0.5.27 → 0.5.28

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,180 +1,66 @@
1
1
  #!/usr/bin/env node
2
2
  'use strict';
3
- const { execFileSync } = require('child_process');
4
- const fs = require('fs');
3
+ const { spawn } = require('child_process');
5
4
  const path = require('path');
6
- const os = require('os');
7
- const { findBinary } = require('./find-binary');
8
- const { install, update, readManifest, getPluginVersion, checkScopeConflict } = require('./lifecycle');
9
- const { checkForUpdate, readState: readUpdateState } = require('./auto-update');
5
+ const {
6
+ install, update, readManifest, getPluginVersion, checkScopeConflict,
7
+ cleanupDisabledStatusline, isPluginInactive,
8
+ } = require('./lifecycle');
10
9
 
11
- let BIN = findBinary();
12
-
13
- // --- 0b. Retry pending binary update from previous failed auto-update ---
14
- {
15
- const updateState = readUpdateState();
16
- if (updateState.pendingBinaryUpdate) {
17
- const pendingVer = updateState.pendingBinaryUpdate;
18
- try {
19
- execFileSync('npm', ['install', '-g', `@sdsrs/code-graph@${pendingVer}`], {
20
- timeout: 30000, stdio: 'pipe'
21
- });
22
- try { fs.unlinkSync(path.join(os.homedir(), '.cache', 'code-graph', 'binary-path')); } catch {}
23
- // Clear pending flag
24
- const { writeJsonAtomic, CACHE_DIR } = require('./lifecycle');
25
- const s = readUpdateState();
26
- delete s.pendingBinaryUpdate;
27
- writeJsonAtomic(path.join(CACHE_DIR, 'update-state.json'), s);
28
- process.stderr.write(`[code-graph] Binary retry succeeded: v${pendingVer}\n`);
29
- BIN = findBinary(); // refresh
30
- } catch { /* npm still not available — will retry next session */ }
31
- }
32
- }
33
-
34
- // --- 0. Auto-install binary if missing ---
35
- if (!BIN) {
36
- const version = getPluginVersion();
37
- process.stderr.write(`[code-graph] Binary not found, installing @sdsrs/code-graph@${version}...\n`);
10
+ function launchBackgroundAutoUpdate(spawnFn = spawn, env = process.env) {
38
11
  try {
39
- execFileSync('npm', ['install', '-g', `@sdsrs/code-graph@${version}`], {
40
- timeout: 60000, stdio: 'pipe'
12
+ const child = spawnFn(process.execPath, [path.join(__dirname, 'auto-update.js'), 'check', '--silent'], {
13
+ detached: true,
14
+ stdio: 'ignore',
15
+ env: { ...env, CODE_GRAPH_AUTO_UPDATE_SILENT: '1' },
41
16
  });
42
- // Clear cached path so findBinary picks up the new install
43
- try { fs.unlinkSync(path.join(os.homedir(), '.cache', 'code-graph', 'binary-path')); } catch {}
44
- BIN = findBinary();
45
- if (BIN) {
46
- process.stderr.write(`[code-graph] Installed v${version} at ${BIN}\n`);
47
- } else {
48
- process.stderr.write('[code-graph] Install succeeded but binary not found in PATH. Try: npx @sdsrs/code-graph@latest\n');
49
- }
17
+ if (child && typeof child.unref === 'function') child.unref();
18
+ return true;
50
19
  } catch {
51
- process.stderr.write(
52
- `[code-graph] Auto-install failed. Run manually: npm install -g @sdsrs/code-graph@${version}\n`
53
- );
20
+ return false;
54
21
  }
55
22
  }
56
23
 
57
- // --- 1. Health check (always runs) ---
58
- let healthNodes = -1;
59
- if (BIN) {
60
- try {
61
- const out = execFileSync(BIN, ['health-check', '--format', 'oneline'], {
62
- timeout: 2000,
63
- stdio: ['pipe', 'pipe', 'pipe']
64
- }).toString().trim();
65
- if (out) process.stdout.write(out);
66
- // Parse node count for empty-index detection
67
- const m = out.match(/(\d+)\s*nodes/);
68
- if (m) healthNodes = parseInt(m[1], 10);
69
- } catch { /* timeout — silent */ }
70
- }
24
+ function syncLifecycleConfig() {
25
+ const manifest = readManifest();
26
+ const currentVersion = getPluginVersion();
71
27
 
72
- // --- 1a. Auto-index empty databases (fallback if MCP hasn't triggered indexing) ---
73
- if (BIN && healthNodes === 0) {
74
- const dbExists = fs.existsSync(path.join(process.cwd(), '.code-graph', 'index.db'));
75
- if (dbExists) {
76
- // DB exists but empty — MCP server likely hasn't received notifications/initialized yet.
77
- // Trigger CLI indexing as fallback so the index is ready before first tool call.
78
- try {
79
- process.stderr.write('[code-graph] Empty index detected, running initial indexing...\n');
80
- const result = execFileSync(BIN, ['incremental-index', '--quiet'], {
81
- timeout: 15000, // 15s max (SessionStart hook has 20s budget)
82
- stdio: ['pipe', 'pipe', 'pipe'],
83
- }).toString().trim();
84
- if (result) process.stderr.write(`[code-graph] ${result}\n`);
85
- // Re-run health check to update statusline with new counts
86
- try {
87
- const out2 = execFileSync(BIN, ['health-check', '--format', 'oneline'], {
88
- timeout: 2000, stdio: ['pipe', 'pipe', 'pipe']
89
- }).toString().trim();
90
- if (out2) process.stdout.write(`\n${out2}`);
91
- } catch { /* ok */ }
92
- } catch (e) {
93
- process.stderr.write(`[code-graph] Auto-index failed: ${e.message || e}\n`);
94
- }
28
+ if (!manifest.version) {
29
+ install();
30
+ return 'installed';
31
+ }
32
+ if (manifest.version !== currentVersion) {
33
+ update();
34
+ return 'updated';
95
35
  }
36
+ return 'noop';
96
37
  }
97
38
 
98
- // --- 1b. Suggest project_map as first action ---
99
- if (BIN) {
100
- process.stdout.write(
101
- '\n[code-graph] TIP: Call project_map first to get a full architecture overview ' +
102
- '(modules, dependencies, hot functions, entry points) in one call.\n'
103
- );
104
- }
39
+ function runSessionInit() {
40
+ if (isPluginInactive()) {
41
+ cleanupDisabledStatusline();
42
+ return { inactive: true, lifecycle: 'noop', autoUpdateLaunched: false };
43
+ }
105
44
 
106
- // --- 1c. Binary version sync (plugin may update before npm binary) ---
107
- if (BIN) {
108
- try {
109
- const binOut = execFileSync(BIN, ['--version'], { timeout: 2000, stdio: 'pipe' }).toString().trim();
110
- const binVersion = binOut.replace(/^code-graph-mcp\s+/, '');
111
- const pluginVersion = getPluginVersion();
112
- if (binVersion && pluginVersion && /^\d+\.\d+\.\d+$/.test(binVersion)) {
113
- const bv = binVersion.split('.').map(Number);
114
- const pv = pluginVersion.split('.').map(Number);
115
- const pluginNewer = (pv[0] > bv[0]) ||
116
- (pv[0] === bv[0] && pv[1] > bv[1]) ||
117
- (pv[0] === bv[0] && pv[1] === bv[1] && pv[2] > bv[2]);
118
- if (pluginNewer) {
119
- process.stderr.write(`[code-graph] Binary v${binVersion} < plugin v${pluginVersion}, updating...\n`);
120
- let binarySynced = false;
121
- try {
122
- execFileSync('npm', ['install', '-g', `@sdsrs/code-graph@${pluginVersion}`], {
123
- timeout: 30000, stdio: 'pipe'
124
- });
125
- // Clear cached binary path so next lookup finds the new binary
126
- try { fs.unlinkSync(path.join(os.homedir(), '.cache', 'code-graph', 'binary-path')); } catch {}
127
- process.stderr.write(`[code-graph] Binary updated to v${pluginVersion}\n`);
128
- binarySynced = true;
129
- } catch {
130
- process.stderr.write(
131
- `[code-graph] Auto-update failed. Run: npm install -g @sdsrs/code-graph@${pluginVersion}\n`
132
- );
133
- }
134
- if (binarySynced) {
135
- // MCP server is still running old binary — prompt user to reconnect
136
- process.stdout.write(
137
- `\n\u26A0\uFE0F [code-graph] Binary updated v${binVersion} \u2192 v${pluginVersion}. ` +
138
- `Run /mcp to reconnect MCP server with new version.\n`
139
- );
140
- }
141
- }
142
- }
143
- } catch { /* version check failed — not critical */ }
144
- }
45
+ const conflict = checkScopeConflict();
46
+ if (conflict) {
47
+ process.stderr.write(
48
+ `[code-graph] Warning: conflicting install detected — ${conflict.existingId} (${conflict.scope || 'unknown'} scope). ` +
49
+ `Use /plugin to remove one to avoid config conflicts.\n`
50
+ );
51
+ }
145
52
 
146
- // --- 2. Scope conflict warning ---
147
- const conflict = checkScopeConflict();
148
- if (conflict) {
149
- process.stderr.write(
150
- `[code-graph] Warning: conflicting install detected — ${conflict.existingId} (${conflict.scope || 'unknown'} scope). ` +
151
- `Use /plugin to remove one to avoid config conflicts.\n`
152
- );
53
+ const lifecycle = syncLifecycleConfig();
54
+ const autoUpdateLaunched = launchBackgroundAutoUpdate();
55
+ return { inactive: false, lifecycle, autoUpdateLaunched };
153
56
  }
154
57
 
155
- // --- 3. Lifecycle: install or update config (idempotent) ---
156
- const manifest = readManifest();
157
- const currentVersion = getPluginVersion();
58
+ module.exports = {
59
+ launchBackgroundAutoUpdate,
60
+ syncLifecycleConfig,
61
+ runSessionInit,
62
+ };
158
63
 
159
- if (!manifest.version) {
160
- install();
161
- } else if (manifest.version !== currentVersion) {
162
- update();
64
+ if (require.main === module) {
65
+ runSessionInit();
163
66
  }
164
-
165
- // --- 4. Auto-update (throttled, non-blocking) ---
166
- (async () => {
167
- const result = await checkForUpdate();
168
- if (result && result.updated) {
169
- process.stderr.write(`[code-graph] Updated: v${result.from} \u2192 v${result.to}\n`);
170
- process.stdout.write(
171
- `\n\uD83D\uDD04 [code-graph] Auto-updated v${result.from} \u2192 v${result.to}. ` +
172
- `Run /mcp to use the new version.\n`
173
- );
174
- } else if (result && result.updateAvailable) {
175
- process.stderr.write(
176
- `[code-graph] Update available: v${result.from} \u2192 v${result.to}. ` +
177
- `Run: npx @sdsrs/code-graph@latest\n`
178
- );
179
- }
180
- })();
@@ -0,0 +1,35 @@
1
+ 'use strict';
2
+ const test = require('node:test');
3
+ const assert = require('node:assert/strict');
4
+
5
+ const { launchBackgroundAutoUpdate, syncLifecycleConfig } = require('./session-init');
6
+
7
+ test('syncLifecycleConfig is exported as a callable helper', () => {
8
+ assert.equal(typeof syncLifecycleConfig, 'function');
9
+ });
10
+
11
+ test('launchBackgroundAutoUpdate spawns detached silent updater', () => {
12
+ const calls = [];
13
+
14
+ const ok = launchBackgroundAutoUpdate((command, args, options) => {
15
+ const record = { command, args, options, unrefCalled: false };
16
+ calls.push(record);
17
+ return {
18
+ unref() {
19
+ record.unrefCalled = true;
20
+ },
21
+ };
22
+ }, { HOME: '/tmp/fake-home' });
23
+
24
+ assert.equal(ok, true);
25
+ assert.equal(calls.length, 1);
26
+ assert.equal(calls[0].command, process.execPath);
27
+ assert.match(calls[0].args[0], /auto-update\.js$/);
28
+ assert.equal(calls[0].args[1], 'check');
29
+ assert.equal(calls[0].args[2], '--silent');
30
+ assert.equal(calls[0].options.detached, true);
31
+ assert.equal(calls[0].options.stdio, 'ignore');
32
+ assert.equal(calls[0].options.env.CODE_GRAPH_AUTO_UPDATE_SILENT, '1');
33
+ assert.equal(calls[0].unrefCalled, true);
34
+ });
35
+
@@ -7,10 +7,13 @@
7
7
  */
8
8
  const { execFileSync } = require('child_process');
9
9
  const path = require('path');
10
- const { readRegistry } = require('./lifecycle');
10
+ const { cleanupDisabledStatusline, readRegistry } = require('./lifecycle');
11
11
 
12
12
  const SEPARATOR = ' \x1b[2m|\x1b[0m ';
13
13
 
14
+ const disabledCleanup = cleanupDisabledStatusline();
15
+ if (disabledCleanup.cleaned) process.exit(0);
16
+
14
17
  // Collect stdin (Claude Code pipes JSON context)
15
18
  let stdinData = '';
16
19
  let ran = false;
@@ -4,6 +4,10 @@ const { execFileSync } = require('child_process');
4
4
  const fs = require('fs');
5
5
  const path = require('path');
6
6
  const { findBinary } = require('./find-binary');
7
+ const { cleanupDisabledStatusline } = require('./lifecycle');
8
+
9
+ const disabledCleanup = cleanupDisabledStatusline();
10
+ if (disabledCleanup.cleaned) process.exit(0);
7
11
 
8
12
  // Only show status in projects that have a code-graph index.
9
13
  // The statusLine config is global, so we must exit silently for
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sdsrs/code-graph",
3
- "version": "0.5.27",
3
+ "version": "0.5.28",
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.5.27",
37
- "@sdsrs/code-graph-linux-arm64": "0.5.27",
38
- "@sdsrs/code-graph-darwin-x64": "0.5.27",
39
- "@sdsrs/code-graph-darwin-arm64": "0.5.27",
40
- "@sdsrs/code-graph-win32-x64": "0.5.27"
36
+ "@sdsrs/code-graph-linux-x64": "0.5.28",
37
+ "@sdsrs/code-graph-linux-arm64": "0.5.28",
38
+ "@sdsrs/code-graph-darwin-x64": "0.5.28",
39
+ "@sdsrs/code-graph-darwin-arm64": "0.5.28",
40
+ "@sdsrs/code-graph-win32-x64": "0.5.28"
41
41
  }
42
42
  }