@sdsrs/code-graph 0.94.0 → 0.95.1
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/auto-update.js +69 -9
- package/package.json +8 -7
- package/claude-plugin/scripts/adopt.test.js +0 -679
- package/claude-plugin/scripts/auto-update.test.js +0 -515
- package/claude-plugin/scripts/cg-answer.test.js +0 -309
- package/claude-plugin/scripts/claude-config.test.js +0 -58
- package/claude-plugin/scripts/covering-tests.test.js +0 -78
- package/claude-plugin/scripts/doctor.test.js +0 -215
- package/claude-plugin/scripts/find-binary.test.js +0 -246
- package/claude-plugin/scripts/hook-fire.test.js +0 -117
- package/claude-plugin/scripts/hooks.test.js +0 -230
- package/claude-plugin/scripts/incremental-index.test.js +0 -102
- package/claude-plugin/scripts/lifecycle.e2e.test.js +0 -179
- package/claude-plugin/scripts/lifecycle.test.js +0 -786
- package/claude-plugin/scripts/mcp-launcher.test.js +0 -162
- package/claude-plugin/scripts/mcp-stub.test.js +0 -207
- package/claude-plugin/scripts/post-grep-inject.test.js +0 -531
- package/claude-plugin/scripts/pr-impact-comment.test.js +0 -110
- package/claude-plugin/scripts/pre-edit-guide.test.js +0 -218
- package/claude-plugin/scripts/pre-grep-guide.test.js +0 -1682
- package/claude-plugin/scripts/pre-read-guide.test.js +0 -363
- package/claude-plugin/scripts/project-detect.test.js +0 -95
- package/claude-plugin/scripts/recommendation-log.test.js +0 -79
- package/claude-plugin/scripts/session-init.test.js +0 -479
- package/claude-plugin/scripts/statusline-composite.test.js +0 -65
- package/claude-plugin/scripts/statusline.test.js +0 -235
- package/claude-plugin/scripts/tmp-dir.test.js +0 -50
- package/claude-plugin/scripts/user-prompt-context.test.js +0 -743
- package/claude-plugin/scripts/version-utils.test.js +0 -141
|
@@ -1,218 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
const test = require('node:test');
|
|
3
|
-
const assert = require('node:assert/strict');
|
|
4
|
-
|
|
5
|
-
// Pre-edit-guide.js is a script with side effects (reads stdin, checks db).
|
|
6
|
-
// We test its PATTERNS directly without requiring the module.
|
|
7
|
-
|
|
8
|
-
// --- Function signature patterns (copied from pre-edit-guide.js) ---
|
|
9
|
-
const fnPatterns = [
|
|
10
|
-
/(?:pub\s+)?(?:async\s+)?fn\s+(\w+)/, // Rust
|
|
11
|
-
/(?:export\s+)?(?:async\s+)?function\s+(\w+)/, // JS/TS
|
|
12
|
-
/(?:const|let|var)\s+(\w+)\s*=\s*(?:async\s+)?(?:\([^)]*\)|_)\s*=>/, // JS arrow
|
|
13
|
-
/(?:async\s+)?(\w+)\s*\([^)]*\)\s*\{/, // JS method / Go func
|
|
14
|
-
/def\s+(\w+)/, // Python/Ruby
|
|
15
|
-
/func\s+(\w+)/, // Go/Swift
|
|
16
|
-
/(?:public|private|protected|static|override|virtual|abstract|internal)\s+\S+\s+(\w+)\s*\(/, // Java/C#/Kotlin
|
|
17
|
-
/(?:public\s+)?function\s+(\w+)/, // PHP
|
|
18
|
-
];
|
|
19
|
-
|
|
20
|
-
function extractFunctionName(code) {
|
|
21
|
-
for (const pat of fnPatterns) {
|
|
22
|
-
const m = code.match(pat);
|
|
23
|
-
if (m) return m[1] || m[2];
|
|
24
|
-
}
|
|
25
|
-
return null;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function isCommonKeyword(s) {
|
|
29
|
-
return /^(if|for|while|switch|catch|else|return|new|get|set|try)$/i.test(s);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// ── Rust ────────────────────────────────────────────────
|
|
33
|
-
|
|
34
|
-
test('fn-extract: Rust pub fn', () => {
|
|
35
|
-
assert.equal(extractFunctionName('pub fn parse_code(input: &str) -> Vec<Node> {'), 'parse_code');
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
test('fn-extract: Rust pub async fn', () => {
|
|
39
|
-
assert.equal(extractFunctionName('pub async fn handle_message(&self, msg: &str) -> Result<()> {'), 'handle_message');
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
test('fn-extract: Rust fn (no pub)', () => {
|
|
43
|
-
assert.equal(extractFunctionName('fn helper_func(x: i32) -> i32 {'), 'helper_func');
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
// ── JavaScript/TypeScript ───────────────────────────────
|
|
47
|
-
|
|
48
|
-
test('fn-extract: JS function', () => {
|
|
49
|
-
assert.equal(extractFunctionName('function handleRequest(req, res) {'), 'handleRequest');
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
test('fn-extract: JS export function', () => {
|
|
53
|
-
assert.equal(extractFunctionName('export function processData(input) {'), 'processData');
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
test('fn-extract: JS async function', () => {
|
|
57
|
-
assert.equal(extractFunctionName('async function fetchData(url) {'), 'fetchData');
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
test('fn-extract: JS export async function', () => {
|
|
61
|
-
assert.equal(extractFunctionName('export async function loadConfig(path) {'), 'loadConfig');
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
test('fn-extract: JS arrow function (const)', () => {
|
|
65
|
-
assert.equal(extractFunctionName('const handleError = (err) => {'), 'handleError');
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
test('fn-extract: JS arrow function (async)', () => {
|
|
69
|
-
assert.equal(extractFunctionName('const fetchUser = async (id) => {'), 'fetchUser');
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
test('fn-extract: JS method', () => {
|
|
73
|
-
assert.equal(extractFunctionName(' handleMessage(msg) {'), 'handleMessage');
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
// ── Python ──────────────────────────────────────────────
|
|
77
|
-
|
|
78
|
-
test('fn-extract: Python def', () => {
|
|
79
|
-
assert.equal(extractFunctionName('def process_data(self, items):'), 'process_data');
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
test('fn-extract: Python async def', () => {
|
|
83
|
-
assert.equal(extractFunctionName('async def fetch_data(url):'), 'fetch_data');
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
// ── Go ──────────────────────────────────────────────────
|
|
87
|
-
|
|
88
|
-
test('fn-extract: Go func', () => {
|
|
89
|
-
assert.equal(extractFunctionName('func HandleRequest(w http.ResponseWriter, r *http.Request) {'), 'HandleRequest');
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
// ── Java/C#/Kotlin ──────────────────────────────────────
|
|
93
|
-
|
|
94
|
-
test('fn-extract: Java public method', () => {
|
|
95
|
-
assert.equal(extractFunctionName('public void processItem(Item item) {'), 'processItem');
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
test('fn-extract: Java private method', () => {
|
|
99
|
-
assert.equal(extractFunctionName('private String formatOutput(Data data) {'), 'formatOutput');
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
test('fn-extract: C# static method', () => {
|
|
103
|
-
assert.equal(extractFunctionName('static int CalculateTotal(List<int> items) {'), 'CalculateTotal');
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
// ── PHP ─────────────────────────────────────────────────
|
|
107
|
-
|
|
108
|
-
test('fn-extract: PHP function', () => {
|
|
109
|
-
assert.equal(extractFunctionName('function handleUpload($file) {'), 'handleUpload');
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
test('fn-extract: PHP public function', () => {
|
|
113
|
-
assert.equal(extractFunctionName('public function getUser($id) {'), 'getUser');
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
// ── Ruby ────────────────────────────────────────────────
|
|
117
|
-
|
|
118
|
-
test('fn-extract: Ruby def', () => {
|
|
119
|
-
assert.equal(extractFunctionName('def process_request(params)'), 'process_request');
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
// ── Keyword filter ──────────────────────────────────────
|
|
123
|
-
|
|
124
|
-
test('keyword-filter: common keywords rejected', () => {
|
|
125
|
-
for (const kw of ['if', 'for', 'while', 'switch', 'catch', 'else', 'return', 'new', 'get', 'set', 'try']) {
|
|
126
|
-
assert.ok(isCommonKeyword(kw), `"${kw}" should be rejected`);
|
|
127
|
-
}
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
test('keyword-filter: real function names pass', () => {
|
|
131
|
-
for (const name of ['parse_code', 'handleMessage', 'process_data', 'fetchUser']) {
|
|
132
|
-
assert.ok(!isCommonKeyword(name), `"${name}" should pass`);
|
|
133
|
-
}
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
// ── No false positives ──────────────────────────────────
|
|
137
|
-
|
|
138
|
-
test('fn-extract: plain code body returns null', () => {
|
|
139
|
-
assert.equal(extractFunctionName('let x = 42;\nreturn x + 1;'), null);
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
test('fn-extract: comment returns null', () => {
|
|
143
|
-
assert.equal(extractFunctionName('// This is a comment about the function'), null);
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
test('fn-extract: short strings return null', () => {
|
|
147
|
-
assert.equal(extractFunctionName('x = 1'), null);
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
// ── Pattern consistency check ───────────────────────────
|
|
151
|
-
// Verify fnPatterns in this test match what's in pre-edit-guide.js
|
|
152
|
-
|
|
153
|
-
// ── Salience forcing (v0.63) ────────────────────────────
|
|
154
|
-
// pre-edit-guide.js top-level-exits on require (reads stdin / checks db), so we
|
|
155
|
-
// assert on the source text — same convention as pattern-sync below.
|
|
156
|
-
|
|
157
|
-
test('salience: impact summary forces a per-caller verdict before the edit', () => {
|
|
158
|
-
const fs = require('node:fs');
|
|
159
|
-
const path = require('node:path');
|
|
160
|
-
const source = fs.readFileSync(path.join(__dirname, 'pre-edit-guide.js'), 'utf8');
|
|
161
|
-
// mem lifts cite-recall to ~94% by making the model ACT on the injection; the
|
|
162
|
-
// impact summary must do the same rather than be passively skimmed. Wording
|
|
163
|
-
// references "each caller of X()" not "above" (finding #5) so it stays coherent
|
|
164
|
-
// when only the caller COUNT is shown (callers[] empty but directCallers>=1).
|
|
165
|
-
assert.match(source, /Before this edit: confirm each caller of/);
|
|
166
|
-
assert.match(source, /still holds with your change, or note why it is unaffected/);
|
|
167
|
-
assert.doesNotMatch(source, /caller\(s\) above you will update/); // old wording removed
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
test('pattern-sync: fnPatterns count matches source', () => {
|
|
171
|
-
const fs = require('node:fs');
|
|
172
|
-
const path = require('node:path');
|
|
173
|
-
const source = fs.readFileSync(path.join(__dirname, 'pre-edit-guide.js'), 'utf8');
|
|
174
|
-
// Count regex pattern lines in the fnPatterns array (lines containing // Language comment)
|
|
175
|
-
const sourcePatternCount = (source.match(/\/\/\s*(Rust|JS|Python|Go|Java|C#|PHP|Ruby|Swift|Kotlin)/g) || []).length;
|
|
176
|
-
assert.ok(fnPatterns.length === 8, `Expected 8 patterns, got ${fnPatterns.length}`);
|
|
177
|
-
assert.ok(sourcePatternCount >= 7, `Source should have >= 7 language comments, found ${sourcePatternCount}`);
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
// ── Covering-test targeting (edit-time PUSH) ────────────
|
|
181
|
-
// The pure formatter lives in covering-tests.js (unit-tested in covering-tests.test.js);
|
|
182
|
-
// these guard that the hook actually wires it in and records the forward signal.
|
|
183
|
-
// Source-grep, same convention as the salience/pattern-sync guards (hook exits on require).
|
|
184
|
-
|
|
185
|
-
test('covering-tests: hook requires and invokes the covering-tests formatter on test_callers', () => {
|
|
186
|
-
const fs = require('node:fs');
|
|
187
|
-
const path = require('node:path');
|
|
188
|
-
const source = fs.readFileSync(path.join(__dirname, 'pre-edit-guide.js'), 'utf8');
|
|
189
|
-
assert.match(source, /require\(['"]\.\/covering-tests['"]\)/);
|
|
190
|
-
assert.match(source, /formatCoveringTests\(/);
|
|
191
|
-
assert.match(source, /test_callers/);
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
test('covering-tests: edit injection records test_targets for the forward funnel', () => {
|
|
195
|
-
const fs = require('node:fs');
|
|
196
|
-
const path = require('node:path');
|
|
197
|
-
const source = fs.readFileSync(path.join(__dirname, 'pre-edit-guide.js'), 'utf8');
|
|
198
|
-
assert.match(source, /test_targets:/);
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
// ── Compound-grep sibling sweep: impact summary → additionalContext ──
|
|
202
|
-
// Bare `process.stdout.write(summary)` on a PreToolUse exit-0 lands in the debug
|
|
203
|
-
// log only and never reaches the model (CC docs v2026-06). The impact summary
|
|
204
|
-
// must ride the shared PreToolUse allow+additionalContext envelope instead.
|
|
205
|
-
// Source-grep, same convention as the salience/pattern-sync guards (hook exits
|
|
206
|
-
// on require: reads stdin, resolves the index).
|
|
207
|
-
|
|
208
|
-
test('emit: impact summary is delivered via the PreToolUse allow+additionalContext envelope', () => {
|
|
209
|
-
const fs = require('node:fs');
|
|
210
|
-
const path = require('node:path');
|
|
211
|
-
const source = fs.readFileSync(path.join(__dirname, 'pre-edit-guide.js'), 'utf8');
|
|
212
|
-
assert.match(source, /require\(['"]\.\/hook-emit['"]\)/,
|
|
213
|
-
'pre-edit-guide must use the shared hook-emit module (no inline envelope copy)');
|
|
214
|
-
assert.match(source, /emitPreToolAllowContext\(summary\)/,
|
|
215
|
-
'the impact summary must be carried inside additionalContext, not bare stdout');
|
|
216
|
-
assert.doesNotMatch(source, /process\.stdout\.write\(summary\)\s*;/,
|
|
217
|
-
'the bare stdout summary emission (debug-log-only) must be removed');
|
|
218
|
-
});
|