@sdsrs/code-graph 0.161.0 → 0.163.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/README.md +13 -0
- package/claude-plugin/.claude-plugin/plugin.json +1 -1
- package/claude-plugin/scripts/hook-emit.js +27 -1
- package/claude-plugin/scripts/hook-fail-open.js +5 -0
- package/claude-plugin/scripts/lifecycle.js +30 -2
- package/claude-plugin/scripts/post-grep-inject.js +28 -6
- package/claude-plugin/scripts/pre-edit-guide.js +21 -0
- package/claude-plugin/scripts/pre-grep-guide.js +435 -68
- package/claude-plugin/scripts/session-edits.js +141 -0
- package/claude-plugin/scripts/stop-impact.js +496 -0
- package/claude-plugin/scripts/subagent-start.js +103 -0
- package/claude-plugin/templates/code-graph-snapshot.yml +1 -1
- package/package.json +6 -6
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
// FIRST statement, before the other requires — same reason as pre-edit-guide.js.
|
|
4
|
+
if (require.main === module) require('./hook-fail-open').installHookFailOpen('SubagentStart');
|
|
5
|
+
|
|
6
|
+
// SubagentStart hook (P1 #3a), registered by lifecycle.js with the matcher
|
|
7
|
+
// `Explore|Plan|general-purpose`.
|
|
8
|
+
//
|
|
9
|
+
// Explore and Plan subagents do not load CLAUDE.md, so the adopt block that
|
|
10
|
+
// tells the main session about the index never reaches them, and they fall
|
|
11
|
+
// back to multi-round grep/Read for structural questions. This hands them the
|
|
12
|
+
// same facts at spawn: the repo has an index, how big and how fresh, and the
|
|
13
|
+
// three CLI commands that answer structural questions from it.
|
|
14
|
+
//
|
|
15
|
+
// Facts only — no "you must". The hooks reference says imperative text framed
|
|
16
|
+
// as out-of-band instructions can trip Claude's prompt-injection defenses
|
|
17
|
+
// (https://code.claude.com/docs/en/hooks, "Add context for Claude").
|
|
18
|
+
//
|
|
19
|
+
// Silent when: no index up the tree, no binary (the commands would not run),
|
|
20
|
+
// the health check fails or reports 0 files, or CODE_GRAPH_QUIET_HOOKS=1 (the
|
|
21
|
+
// same switch that skips the SessionStart project-map injection).
|
|
22
|
+
const { execFileSync } = require('child_process');
|
|
23
|
+
const fs = require('fs');
|
|
24
|
+
const { hidden } = require('./proc-opts');
|
|
25
|
+
|
|
26
|
+
// Hard ceiling on the injected text, in characters (spec: ≤400). Every
|
|
27
|
+
// subagent spawn pays it in context, and a subagent's context is small.
|
|
28
|
+
const MAX_CONTEXT_CHARS = 400;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* The text a subagent receives, or null when there is nothing true to say.
|
|
32
|
+
* @param {{files?: number, index_age?: string, index_version_stale?: boolean}} report
|
|
33
|
+
* `health-check --format json` output
|
|
34
|
+
* @param {number} [maxChars] ceiling; a parameter only so tests can reach the
|
|
35
|
+
* check — the builder's longest output (389) is below the real one
|
|
36
|
+
* @returns {string|null}
|
|
37
|
+
*/
|
|
38
|
+
function buildSubagentContext(report, maxChars = MAX_CONTEXT_CHARS) {
|
|
39
|
+
if (!report || typeof report !== 'object') return null;
|
|
40
|
+
const files = Number(report.files);
|
|
41
|
+
if (!Number.isFinite(files) || files <= 0) return null;
|
|
42
|
+
const age = typeof report.index_age === 'string' && /^[\w .-]{1,24}$/.test(report.index_age)
|
|
43
|
+
? report.index_age : null;
|
|
44
|
+
let text = `This repository has a code-graph AST index of ${files} files`;
|
|
45
|
+
text += age ? `, last updated ${age}.` : '.';
|
|
46
|
+
if (report.index_version_stale === true) text += ' A rebuild for a newer extractor is pending.';
|
|
47
|
+
text += ' Structural questions are answered from it through Bash:' +
|
|
48
|
+
' `code-graph-mcp callgraph <fn>` (callers and callees),' +
|
|
49
|
+
' `code-graph-mcp show <fn>` (a symbol\'s source and signature),' +
|
|
50
|
+
' `code-graph-mcp overview <dir>` (symbols of a module grouped by file).';
|
|
51
|
+
return text.length <= maxChars ? text : null;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function readStdinJson() {
|
|
55
|
+
try {
|
|
56
|
+
// fd 0, not '/dev/stdin' (ENXIO on socketpair stdin) — as the other hooks.
|
|
57
|
+
return JSON.parse(fs.readFileSync(0, 'utf8'));
|
|
58
|
+
} catch { return null; }
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function runMain() {
|
|
62
|
+
if (process.env.CODE_GRAPH_QUIET_HOOKS === '1') return;
|
|
63
|
+
const input = readStdinJson();
|
|
64
|
+
if (!input) return;
|
|
65
|
+
|
|
66
|
+
const { resolveProjectRoot } = require('./project-root');
|
|
67
|
+
const root = resolveProjectRoot(typeof input.cwd === 'string' && input.cwd ? input.cwd : process.cwd());
|
|
68
|
+
if (root === null) return;
|
|
69
|
+
|
|
70
|
+
const binary = require('./find-binary').findBinary();
|
|
71
|
+
if (!binary) return;
|
|
72
|
+
|
|
73
|
+
// Spend the registered budget, not a literal (hooks.test.js "armed must spend").
|
|
74
|
+
const budget = require('./hook-fail-open').remainingMs(1500);
|
|
75
|
+
if (budget === null) return;
|
|
76
|
+
let report;
|
|
77
|
+
try {
|
|
78
|
+
report = JSON.parse(execFileSync(binary, ['health-check', '--format', 'json'], hidden({
|
|
79
|
+
cwd: root,
|
|
80
|
+
timeout: budget,
|
|
81
|
+
encoding: 'utf8',
|
|
82
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
83
|
+
env: { ...process.env, CODE_GRAPH_INTERNAL: '1' },
|
|
84
|
+
})));
|
|
85
|
+
} catch { return; }
|
|
86
|
+
|
|
87
|
+
const text = buildSubagentContext(report);
|
|
88
|
+
if (!text) return;
|
|
89
|
+
const { emitEventContext } = require('./hook-emit');
|
|
90
|
+
process.stdout.write(emitEventContext('SubagentStart', text) + '\n');
|
|
91
|
+
// D#163: how many subagents were handed the facts. Whether they then used
|
|
92
|
+
// the index is in their transcripts, not here (scripts/subagent_share.py).
|
|
93
|
+
// The agent type is harness input; only a plain name is kept.
|
|
94
|
+
const agent = typeof input.agent_type === 'string' && /^[\w:.-]{1,64}$/.test(input.agent_type)
|
|
95
|
+
? input.agent_type : null;
|
|
96
|
+
require('./recommendation-log').recordRecommendation(root, {
|
|
97
|
+
hook: 'subagent', action: 'subagent_context', ...(agent ? { agent } : {}),
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (require.main === module) runMain();
|
|
102
|
+
|
|
103
|
+
module.exports = { buildSubagentContext, MAX_CONTEXT_CHARS };
|
|
@@ -35,7 +35,7 @@ jobs:
|
|
|
35
35
|
node-version: '20'
|
|
36
36
|
- name: Build snapshot
|
|
37
37
|
run: |
|
|
38
|
-
npx -y -p @sdsrs/code-graph@0.
|
|
38
|
+
npx -y -p @sdsrs/code-graph@0.163.0 code-graph-mcp snapshot create --out snapshot.db
|
|
39
39
|
zstd -9 snapshot.db -o snapshot.db.zst
|
|
40
40
|
mv snapshot.db.zst "code-graph-snapshot-${GITHUB_SHA:0:7}.db.zst"
|
|
41
41
|
- name: Upload to release
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sdsrs/code-graph",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.163.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.163.0",
|
|
39
|
+
"@sdsrs/code-graph-linux-arm64": "0.163.0",
|
|
40
|
+
"@sdsrs/code-graph-darwin-x64": "0.163.0",
|
|
41
|
+
"@sdsrs/code-graph-darwin-arm64": "0.163.0",
|
|
42
|
+
"@sdsrs/code-graph-win32-x64": "0.163.0"
|
|
43
43
|
}
|
|
44
44
|
}
|