@sdsrs/code-graph 0.32.3 → 0.33.0
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/adopt.js +15 -20
- package/claude-plugin/scripts/adopt.test.js +29 -0
- package/claude-plugin/scripts/mcp-launcher.js +17 -0
- package/claude-plugin/scripts/mcp-launcher.test.js +26 -2
- package/claude-plugin/scripts/project-detect.js +43 -0
- package/claude-plugin/scripts/project-detect.test.js +73 -0
- package/claude-plugin/scripts/session-init.js +11 -0
- package/claude-plugin/scripts/session-init.test.js +22 -1
- package/package.json +6 -6
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
const fs = require('fs');
|
|
9
9
|
const path = require('path');
|
|
10
10
|
const os = require('os');
|
|
11
|
+
const { PROJECT_MARKERS, isProjectRoot, isNonProjectCwd } = require('./project-detect');
|
|
11
12
|
|
|
12
13
|
const SENTINEL_BEGIN = '<!-- code-graph-mcp:begin v1 -->';
|
|
13
14
|
const SENTINEL_END = '<!-- code-graph-mcp:end -->';
|
|
@@ -314,32 +315,26 @@ function platformGuard() {
|
|
|
314
315
|
return null;
|
|
315
316
|
}
|
|
316
317
|
|
|
317
|
-
// Project-marker
|
|
318
|
-
//
|
|
319
|
-
//
|
|
320
|
-
const PROJECT_MARKERS = [
|
|
321
|
-
'.git', '.code-graph', 'package.json', 'Cargo.toml',
|
|
322
|
-
'pyproject.toml', 'go.mod', 'pom.xml', 'build.gradle',
|
|
323
|
-
];
|
|
324
|
-
function isProjectRoot(cwd) {
|
|
325
|
-
return PROJECT_MARKERS.some(m => fs.existsSync(path.join(cwd, m)));
|
|
326
|
-
}
|
|
318
|
+
// Project-marker detection (PROJECT_MARKERS / isProjectRoot / isNonProjectCwd)
|
|
319
|
+
// now lives in project-detect.js — the single activation gate shared with
|
|
320
|
+
// mcp-launcher.js and session-init.js. Imported above and re-exported below.
|
|
327
321
|
|
|
328
322
|
function adopt({ cwd, home, templatePath } = {}) {
|
|
329
323
|
const blocked = platformGuard();
|
|
330
324
|
if (blocked) return blocked;
|
|
331
325
|
|
|
332
326
|
const effectiveCwd = cwd || process.cwd();
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
}
|
|
341
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
327
|
+
// Gate adoption on a real-project cwd BEFORE touching the filesystem. The
|
|
328
|
+
// check must run even when the memory dir already exists: Claude Code
|
|
329
|
+
// pre-creates ~/.claude/projects/<slug>/memory for every session (including
|
|
330
|
+
// the ~2035 headless /tmp mem-lite calls), and the old guard — nested inside
|
|
331
|
+
// `if (!fs.existsSync(dir))` — was bypassed in exactly that case, letting
|
|
332
|
+
// /tmp get adopted (sentinel written into its MEMORY.md). See project-detect.js.
|
|
333
|
+
if (isNonProjectCwd(effectiveCwd)) {
|
|
334
|
+
return { ok: false, reason: 'not-a-project', dir: memoryDir(cwd, home), cwd: effectiveCwd };
|
|
342
335
|
}
|
|
336
|
+
const dir = memoryDir(cwd, home);
|
|
337
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
343
338
|
const target = path.join(dir, TARGET_NAME);
|
|
344
339
|
const tpl = templatePath || TEMPLATE_PATH;
|
|
345
340
|
if (!fs.existsSync(tpl)) {
|
|
@@ -549,5 +544,5 @@ module.exports = {
|
|
|
549
544
|
detectProjectType, buildIndexLine,
|
|
550
545
|
extractCargoRuntimeDeps, extractPyRuntimeDeps, extractGoDirectRequires,
|
|
551
546
|
SENTINEL_BEGIN, SENTINEL_END, INDEX_LINE, TEMPLATE_PATH, TARGET_NAME,
|
|
552
|
-
PROJECT_MARKERS, PROJECT_TYPES,
|
|
547
|
+
PROJECT_MARKERS, PROJECT_TYPES, isNonProjectCwd,
|
|
553
548
|
};
|
|
@@ -15,6 +15,10 @@ const {
|
|
|
15
15
|
function makeSandbox() {
|
|
16
16
|
const home = fs.mkdtempSync(path.join(os.tmpdir(), 'cg-adopt-home-'));
|
|
17
17
|
const cwd = fs.mkdtempSync(path.join(os.tmpdir(), 'cg-adopt-cwd-'));
|
|
18
|
+
// Mark the sandbox cwd as a real project — adopt() now gates on a project
|
|
19
|
+
// marker unconditionally (see project-detect.js), so a bare mkdtemp would be
|
|
20
|
+
// treated as a non-project and refused.
|
|
21
|
+
fs.mkdirSync(path.join(cwd, '.git'));
|
|
18
22
|
// Pre-create the memory dir (claude-mem convention — we don't create it).
|
|
19
23
|
const dir = memoryDir(cwd, home);
|
|
20
24
|
fs.mkdirSync(dir, { recursive: true });
|
|
@@ -86,6 +90,31 @@ test('adopt preserves existing MEMORY.md content and appends', () => {
|
|
|
86
90
|
} finally { sb.cleanup(); }
|
|
87
91
|
});
|
|
88
92
|
|
|
93
|
+
test('adopt refuses a non-project cwd even when the memory dir already exists (regression: /tmp adoption)', () => {
|
|
94
|
+
// Bug: the isProjectRoot guard was nested inside `if (!fs.existsSync(dir))`,
|
|
95
|
+
// so when Claude Code had already created ~/.claude/projects/<slug>/memory
|
|
96
|
+
// (it does this for every session, incl. the ~2035 headless /tmp mem-lite
|
|
97
|
+
// calls), adopt() sailed past the guard and wrote its sentinel into /tmp's
|
|
98
|
+
// MEMORY.md. Pre-fix this test FAILS (adopt returns ok:true and writes).
|
|
99
|
+
const home = fs.mkdtempSync(path.join(os.tmpdir(), 'cg-adopt-home-'));
|
|
100
|
+
const cwd = fs.mkdtempSync(path.join(os.tmpdir(), 'cg-adopt-cwd-')); // no project marker
|
|
101
|
+
const dir = memoryDir(cwd, home);
|
|
102
|
+
fs.mkdirSync(dir, { recursive: true }); // simulate CC pre-creating the memory dir
|
|
103
|
+
try {
|
|
104
|
+
const res = adopt({ cwd, home });
|
|
105
|
+
assert.strictEqual(res.ok, false);
|
|
106
|
+
assert.strictEqual(res.reason, 'not-a-project');
|
|
107
|
+
const indexPath = path.join(dir, 'MEMORY.md');
|
|
108
|
+
assert.ok(
|
|
109
|
+
!fs.existsSync(indexPath) || !fs.readFileSync(indexPath, 'utf8').includes(SENTINEL_BEGIN),
|
|
110
|
+
'must NOT write the code-graph sentinel into a non-project MEMORY.md'
|
|
111
|
+
);
|
|
112
|
+
} finally {
|
|
113
|
+
fs.rmSync(home, { recursive: true, force: true });
|
|
114
|
+
fs.rmSync(cwd, { recursive: true, force: true });
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
|
|
89
118
|
test('adopt fails gracefully when cwd is not a project root', () => {
|
|
90
119
|
// v0.16.9: behavior change — adopt now mkdir's the memory dir when cwd has
|
|
91
120
|
// a project marker (.git / Cargo.toml / package.json / ...). Bare mkdtemp
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
const { spawn, spawnSync } = require('child_process');
|
|
11
11
|
const path = require('path');
|
|
12
12
|
const fs = require('fs');
|
|
13
|
+
const { isNonProjectCwd } = require('./project-detect');
|
|
13
14
|
|
|
14
15
|
// Set plugin root so find-binary.js can locate bundled/dev binaries
|
|
15
16
|
// Always derive from __dirname — CLAUDE_PLUGIN_ROOT can leak from other plugins
|
|
@@ -88,6 +89,22 @@ if (process.env.CODE_GRAPH_FORCE_PLUGIN_MCP !== '1' && projectHasLocalCodeGraphM
|
|
|
88
89
|
return; // top-level function scope of mcp-launcher.js
|
|
89
90
|
}
|
|
90
91
|
|
|
92
|
+
// --- Non-project cwd gate ---------------------------------------------------
|
|
93
|
+
// In a non-project working directory (no .git/manifest — e.g. /tmp, where
|
|
94
|
+
// claude-mem-lite spawns ~2035 headless `claude -p` JSON-extraction calls that
|
|
95
|
+
// never use code-graph), don't spawn the binary at all: serve the same 0-tool
|
|
96
|
+
// stub. Eliminates the MCP-server spin-up + the ~780B `instructions` block +
|
|
97
|
+
// an empty .code-graph/index.db being created in throwaway dirs. Same
|
|
98
|
+
// CODE_GRAPH_FORCE_PLUGIN_MCP=1 override as the dedup gate above.
|
|
99
|
+
if (process.env.CODE_GRAPH_FORCE_PLUGIN_MCP !== '1' && isNonProjectCwd(process.cwd())) {
|
|
100
|
+
process.stderr.write(
|
|
101
|
+
'[code-graph] non-project cwd (no .git/manifest); plugin MCP serving 0 tools, ' +
|
|
102
|
+
'no index created. Set CODE_GRAPH_FORCE_PLUGIN_MCP=1 to override.\n'
|
|
103
|
+
);
|
|
104
|
+
serveEmptyMcpStub();
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
|
|
91
108
|
const { findBinary, clearCache } = require('./find-binary');
|
|
92
109
|
|
|
93
110
|
let binary = findBinary();
|
|
@@ -33,12 +33,12 @@ function hasBuiltBinary() {
|
|
|
33
33
|
* Run the launcher, send one MCP message on stdin, collect stdout/stderr,
|
|
34
34
|
* resolve once we either see a JSON-RPC response on stdout or hit timeout.
|
|
35
35
|
*/
|
|
36
|
-
function runLauncherInitialize(timeoutMs = 15000, extraEnv = {}) {
|
|
36
|
+
function runLauncherInitialize(timeoutMs = 15000, extraEnv = {}, cwd = REPO_ROOT) {
|
|
37
37
|
return new Promise((resolve, reject) => {
|
|
38
38
|
const child = spawn(process.execPath, [LAUNCHER], {
|
|
39
39
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
40
40
|
env: { ...process.env, ...extraEnv },
|
|
41
|
-
cwd
|
|
41
|
+
cwd,
|
|
42
42
|
});
|
|
43
43
|
|
|
44
44
|
let stdout = '';
|
|
@@ -115,6 +115,30 @@ test('mcp-launcher enters dedup stub when project .mcp.json registers a code-gra
|
|
|
115
115
|
`stderr should explain the dedup, got: ${stderr.slice(0, 400)}`);
|
|
116
116
|
});
|
|
117
117
|
|
|
118
|
+
test('mcp-launcher serves 0-tool stub in a non-project cwd (no binary spawn, no index created)', async (t) => {
|
|
119
|
+
const os = require('os');
|
|
120
|
+
// A bare temp dir with no .git/manifest → isNonProjectCwd → the launcher
|
|
121
|
+
// serves the 0-tool stub WITHOUT spawning the binary, so no .code-graph is
|
|
122
|
+
// created and no `instructions` block is injected. This is the fix for the
|
|
123
|
+
// ~2035 headless /tmp mem-lite calls that half-activated code-graph.
|
|
124
|
+
const cwd = fs.mkdtempSync(path.join(os.tmpdir(), 'cg-launcher-nonproj-'));
|
|
125
|
+
t.after(() => fs.rmSync(cwd, { recursive: true, force: true }));
|
|
126
|
+
|
|
127
|
+
const { stdout, stderr } = await runLauncherInitialize(15000, {}, cwd);
|
|
128
|
+
const respLine = stdout.trim().split('\n').find((l) => l.includes('"result"'));
|
|
129
|
+
assert.ok(respLine,
|
|
130
|
+
`expected stub JSON-RPC result on stdout, got: ${stdout.slice(0, 400)} | stderr: ${stderr.slice(0, 400)}`);
|
|
131
|
+
const resp = JSON.parse(respLine);
|
|
132
|
+
assert.match(resp.result.serverInfo.name, /stub/i,
|
|
133
|
+
`serverInfo.name should indicate stub mode, got ${JSON.stringify(resp.result.serverInfo)}`);
|
|
134
|
+
assert.equal(resp.result.instructions, undefined,
|
|
135
|
+
'stub initialize must NOT carry an instructions block (the ~780B NOISY tax)');
|
|
136
|
+
assert.match(stderr, /non-project cwd/,
|
|
137
|
+
`stderr should explain the non-project gate, got: ${stderr.slice(0, 400)}`);
|
|
138
|
+
assert.ok(!fs.existsSync(path.join(cwd, '.code-graph')),
|
|
139
|
+
'must NOT create .code-graph in a non-project cwd');
|
|
140
|
+
});
|
|
141
|
+
|
|
118
142
|
test('mcp-launcher sets _FIND_BINARY_ROOT from __dirname (does not trust CLAUDE_PLUGIN_ROOT)', () => {
|
|
119
143
|
// Static check: the source must derive _FIND_BINARY_ROOT from __dirname so a
|
|
120
144
|
// sibling plugin's CLAUDE_PLUGIN_ROOT can't redirect us to the wrong binary.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
// Shared "is this a real project?" detector for the plugin's activation gates.
|
|
4
|
+
//
|
|
5
|
+
// Why this exists: code-graph half-activates in non-project working
|
|
6
|
+
// directories — most visibly the ~2035 headless `claude -p` calls
|
|
7
|
+
// claude-mem-lite spawns with cwd=/tmp ("Return ONLY valid JSON"), none of
|
|
8
|
+
// which ever use code-graph. Each one paid an MCP-server spin-up + a ~780B
|
|
9
|
+
// `instructions` block + a SessionStart map probe + an empty
|
|
10
|
+
// /tmp/.code-graph/index.db, plus adopt() writing a decision-table sentinel
|
|
11
|
+
// into ~/.claude/projects/-tmp/memory/MEMORY.md. This module is the single
|
|
12
|
+
// gate the launcher (mcp-launcher.js), the SessionStart hook (session-init.js),
|
|
13
|
+
// and adopt (adopt.js) consult to fully no-op there.
|
|
14
|
+
//
|
|
15
|
+
// Detection is project-MARKER based, NOT a literal "is cwd under os.tmpdir()"
|
|
16
|
+
// check. Rationale: (1) /tmp and Claude Code's $TMPDIR have no .git/manifest,
|
|
17
|
+
// so the marker check already classifies every temp / headless cwd as
|
|
18
|
+
// non-project; (2) a literal under-tmpdir test would wrongly skip a real git
|
|
19
|
+
// repo that happens to be cloned under /tmp AND would break this repo's own
|
|
20
|
+
// tmpdir-based test sandboxes. Markers mirror what Claude Code itself
|
|
21
|
+
// recognizes. `.code-graph` is deliberately NOT a marker — it is created BY
|
|
22
|
+
// this tool, so counting it would let a once-polluted /tmp self-certify as a
|
|
23
|
+
// project on the next session (circular).
|
|
24
|
+
const fs = require('fs');
|
|
25
|
+
const path = require('path');
|
|
26
|
+
|
|
27
|
+
const PROJECT_MARKERS = [
|
|
28
|
+
'.git', 'package.json', 'Cargo.toml',
|
|
29
|
+
'pyproject.toml', 'go.mod', 'pom.xml', 'build.gradle',
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
function isProjectRoot(cwd) {
|
|
33
|
+
return PROJECT_MARKERS.some(m => fs.existsSync(path.join(cwd, m)));
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// A cwd is "non-project" when it carries none of the recognized project
|
|
37
|
+
// markers. The plugin's activation gates short-circuit there: no MCP tools,
|
|
38
|
+
// no index creation, no SessionStart map injection, no auto-adoption.
|
|
39
|
+
function isNonProjectCwd(cwd = process.cwd()) {
|
|
40
|
+
return !isProjectRoot(cwd);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
module.exports = { PROJECT_MARKERS, isProjectRoot, isNonProjectCwd };
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
// Tests for project-detect.js — the activation gate shared by mcp-launcher.js,
|
|
3
|
+
// session-init.js, and adopt.js. Run: node --test claude-plugin/scripts/project-detect.test.js
|
|
4
|
+
const test = require('node:test');
|
|
5
|
+
const assert = require('node:assert/strict');
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
const os = require('os');
|
|
9
|
+
|
|
10
|
+
const { PROJECT_MARKERS, isProjectRoot, isNonProjectCwd } = require('./project-detect');
|
|
11
|
+
|
|
12
|
+
function mkTmp(t) {
|
|
13
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'cg-pd-'));
|
|
14
|
+
t.after(() => fs.rmSync(dir, { recursive: true, force: true }));
|
|
15
|
+
return dir;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
test('isNonProjectCwd: bare tmp dir (no markers) → non-project', (t) => {
|
|
19
|
+
const dir = mkTmp(t);
|
|
20
|
+
assert.equal(isNonProjectCwd(dir), true);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test('isNonProjectCwd: /tmp root (the mem-lite headless cwd) → non-project', () => {
|
|
24
|
+
// claude-mem-lite spawns `claude -p` with cwd=/tmp; /tmp has no project marker.
|
|
25
|
+
assert.equal(isNonProjectCwd('/tmp'), true);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test('isNonProjectCwd: cwd with .git → project (false)', (t) => {
|
|
29
|
+
const dir = mkTmp(t);
|
|
30
|
+
fs.mkdirSync(path.join(dir, '.git'));
|
|
31
|
+
assert.equal(isNonProjectCwd(dir), false);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test('isNonProjectCwd: cwd with package.json → project (false)', (t) => {
|
|
35
|
+
const dir = mkTmp(t);
|
|
36
|
+
fs.writeFileSync(path.join(dir, 'package.json'), '{}');
|
|
37
|
+
assert.equal(isNonProjectCwd(dir), false);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test('isNonProjectCwd: a real git repo under /tmp is still a project (marker wins over location)', (t) => {
|
|
41
|
+
// Deliberate: we do NOT do a literal under-tmpdir check, so a repo cloned
|
|
42
|
+
// into /tmp/<x> with .git is correctly treated as a project.
|
|
43
|
+
const dir = mkTmp(t);
|
|
44
|
+
fs.mkdirSync(path.join(dir, '.git'));
|
|
45
|
+
assert.equal(isNonProjectCwd(dir), false);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test('isNonProjectCwd: cwd with only .code-graph → non-project (self-created dir is not a marker)', (t) => {
|
|
49
|
+
// Circularity guard: once code-graph (pre-fix) created /tmp/.code-graph, a
|
|
50
|
+
// naive marker set counting .code-graph would self-certify /tmp as a project.
|
|
51
|
+
const dir = mkTmp(t);
|
|
52
|
+
fs.mkdirSync(path.join(dir, '.code-graph'));
|
|
53
|
+
assert.equal(isProjectRoot(dir), false, '.code-graph alone must not qualify as a project');
|
|
54
|
+
assert.equal(isNonProjectCwd(dir), true);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test('PROJECT_MARKERS excludes .code-graph and includes the standard anchors', () => {
|
|
58
|
+
assert.ok(!PROJECT_MARKERS.includes('.code-graph'), '.code-graph must not be a project marker');
|
|
59
|
+
for (const m of ['.git', 'package.json', 'Cargo.toml', 'pyproject.toml', 'go.mod']) {
|
|
60
|
+
assert.ok(PROJECT_MARKERS.includes(m), `${m} should be a marker`);
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test('isProjectRoot detects each marker', (t) => {
|
|
65
|
+
for (const marker of PROJECT_MARKERS) {
|
|
66
|
+
const dir = mkTmp(t);
|
|
67
|
+
assert.equal(isProjectRoot(dir), false, 'bare cwd should not be a project');
|
|
68
|
+
const markerPath = path.join(dir, marker);
|
|
69
|
+
if (marker.startsWith('.')) fs.mkdirSync(markerPath);
|
|
70
|
+
else fs.writeFileSync(markerPath, '');
|
|
71
|
+
assert.equal(isProjectRoot(dir), true, `${marker} should make cwd a project`);
|
|
72
|
+
}
|
|
73
|
+
});
|
|
@@ -10,6 +10,7 @@ const {
|
|
|
10
10
|
} = require('./lifecycle');
|
|
11
11
|
const { readBinaryVersion, isDevMode, getNewestMtime } = require('./version-utils');
|
|
12
12
|
const { maybeAutoAdopt, isAdopted } = require('./adopt');
|
|
13
|
+
const { isNonProjectCwd } = require('./project-detect');
|
|
13
14
|
|
|
14
15
|
// v0.17.0 — quietHooks: unconditional quiet 默认。
|
|
15
16
|
// 项目地图与 MEMORY.md plugin contract + on-demand `project_map` 工具高度重叠,
|
|
@@ -268,6 +269,16 @@ function runSessionInit() {
|
|
|
268
269
|
return { inactive: true, lifecycle: 'noop', autoUpdateLaunched: false };
|
|
269
270
|
}
|
|
270
271
|
|
|
272
|
+
// Non-project cwd (no .git/manifest — e.g. /tmp, where claude-mem-lite
|
|
273
|
+
// spawns headless `claude -p` calls that never use code-graph): fully no-op.
|
|
274
|
+
// Returns BEFORE syncLifecycleConfig / verifyBinary / ensureIndexFresh /
|
|
275
|
+
// maybeAutoAdopt / injectProjectMap so the plugin leaves zero footprint
|
|
276
|
+
// (no incremental-index spawn, no map injection, no adoption). The MCP
|
|
277
|
+
// launcher applies the same gate — see project-detect.js.
|
|
278
|
+
if (isNonProjectCwd(process.cwd())) {
|
|
279
|
+
return { inactive: false, nonProject: true, lifecycle: 'noop', autoUpdateLaunched: false };
|
|
280
|
+
}
|
|
281
|
+
|
|
271
282
|
const conflict = checkScopeConflict();
|
|
272
283
|
if (conflict) {
|
|
273
284
|
process.stderr.write(
|
|
@@ -72,12 +72,33 @@ test('launchBackgroundAutoUpdate spawns detached silent updater', () => {
|
|
|
72
72
|
assert.equal(calls[0].unrefCalled, true);
|
|
73
73
|
});
|
|
74
74
|
|
|
75
|
-
const { consistencyCheck } = require('./session-init');
|
|
75
|
+
const { consistencyCheck, runSessionInit } = require('./session-init');
|
|
76
76
|
|
|
77
77
|
test('consistencyCheck is exported as a function', () => {
|
|
78
78
|
assert.equal(typeof consistencyCheck, 'function');
|
|
79
79
|
});
|
|
80
80
|
|
|
81
|
+
test('runSessionInit no-ops (nonProject) in a non-project cwd', (t) => {
|
|
82
|
+
// /tmp-style cwd (no .git/manifest) → the gate returns BEFORE
|
|
83
|
+
// syncLifecycleConfig / verifyBinary / ensureIndexFresh / maybeAutoAdopt /
|
|
84
|
+
// injectProjectMap, leaving zero footprint. Safe to call: the early return
|
|
85
|
+
// precedes every side-effectful step.
|
|
86
|
+
const os = require('os');
|
|
87
|
+
const origCwd = process.cwd();
|
|
88
|
+
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'cg-si-nonproj-'));
|
|
89
|
+
process.chdir(tmp);
|
|
90
|
+
try {
|
|
91
|
+
const res = runSessionInit();
|
|
92
|
+
if (res.inactive) { t.skip('plugin seen inactive in this env — gate not reached'); return; }
|
|
93
|
+
assert.equal(res.nonProject, true);
|
|
94
|
+
assert.equal(res.lifecycle, 'noop');
|
|
95
|
+
assert.equal(res.autoUpdateLaunched, false);
|
|
96
|
+
} finally {
|
|
97
|
+
process.chdir(origCwd);
|
|
98
|
+
fs.rmSync(tmp, { recursive: true, force: true });
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
|
|
81
102
|
test('consistencyCheck returns empty array when binary version matches plugin', () => {
|
|
82
103
|
const result = consistencyCheck('/tmp/nonexistent-binary');
|
|
83
104
|
assert.ok(Array.isArray(result));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sdsrs/code-graph",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.33.0",
|
|
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": {
|
|
@@ -35,10 +35,10 @@
|
|
|
35
35
|
"node": ">=16"
|
|
36
36
|
},
|
|
37
37
|
"optionalDependencies": {
|
|
38
|
-
"@sdsrs/code-graph-linux-x64": "0.
|
|
39
|
-
"@sdsrs/code-graph-linux-arm64": "0.
|
|
40
|
-
"@sdsrs/code-graph-darwin-x64": "0.
|
|
41
|
-
"@sdsrs/code-graph-darwin-arm64": "0.
|
|
42
|
-
"@sdsrs/code-graph-win32-x64": "0.
|
|
38
|
+
"@sdsrs/code-graph-linux-x64": "0.33.0",
|
|
39
|
+
"@sdsrs/code-graph-linux-arm64": "0.33.0",
|
|
40
|
+
"@sdsrs/code-graph-darwin-x64": "0.33.0",
|
|
41
|
+
"@sdsrs/code-graph-darwin-arm64": "0.33.0",
|
|
42
|
+
"@sdsrs/code-graph-win32-x64": "0.33.0"
|
|
43
43
|
}
|
|
44
44
|
}
|