@sdsrs/code-graph 0.156.0 → 0.158.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.
|
@@ -46,6 +46,8 @@ const {
|
|
|
46
46
|
extractCgFlags,
|
|
47
47
|
cgFlagSet,
|
|
48
48
|
extractSearchPath,
|
|
49
|
+
bareSourceTarget,
|
|
50
|
+
firstShellClause,
|
|
49
51
|
normalizeCommandPaths,
|
|
50
52
|
rebaseRelativePaths,
|
|
51
53
|
resolveProjectRoot,
|
|
@@ -263,6 +265,14 @@ function runMain() {
|
|
|
263
265
|
markCooldown(rawCmd, root);
|
|
264
266
|
|
|
265
267
|
const { segment, block } = found;
|
|
268
|
+
// D#73 — a bare dir names `<shell cwd>/src`. From a subdirectory shell (same
|
|
269
|
+
// guard as pre-grep-guide's runMain), or after anything earlier in the same
|
|
270
|
+
// command, that is not provably the root's src: round 2 found `cd x && grep`,
|
|
271
|
+
// round 3 reproduced 19 more forms that move the shell (`builtin cd`, `if cd`,
|
|
272
|
+
// `{ cd …; }`, `\cd`, `popd`, `eval`, a function). A denylist of those never
|
|
273
|
+
// closes, so a bare dir is answered only when the grep is the FIRST segment.
|
|
274
|
+
if (bareSourceTarget(firstShellClause(segment))
|
|
275
|
+
&& (relPrefix || splitTopLevelSegments(cmd)[0] !== segment)) return;
|
|
266
276
|
// Run the answer exactly like the deny path.
|
|
267
277
|
const rawPattern = pickBlockPattern(segment);
|
|
268
278
|
// Grep-response gate (2026-07-03 audit: 18/18 injects were 0 CONSUMED because they
|
|
@@ -89,6 +89,30 @@ const SRC_PREFIXES =
|
|
|
89
89
|
const SRC_PATH = new RegExp(`(?:^|\\s|["'])(${SRC_PREFIXES})/`);
|
|
90
90
|
// Anchored variant for whole-token matching in extractSearchPath.
|
|
91
91
|
const SRC_PATH_TOKEN = new RegExp(`^(?:\\./)?(${SRC_PREFIXES})/`);
|
|
92
|
+
// A path operand the rewrite grammar proved: a bare prefix word (`src`), or a
|
|
93
|
+
// `./`-rooted one with any subpath (`./src`, `./src/x/`), which SRC_PATH's
|
|
94
|
+
// lookbehind never matched.
|
|
95
|
+
const SRC_BARE_TOKEN = new RegExp(`^(?:(?:${SRC_PREFIXES})|\\./(?:${SRC_PREFIXES})(?:/.*)?)$`);
|
|
96
|
+
|
|
97
|
+
// D#73 — a source dir written as a bare word (`grep -rn X src`, `rg X tests`,
|
|
98
|
+
// `./src`): the shape models write when a prompt says "under src/", and it
|
|
99
|
+
// matched nothing before. Recognized ONLY where rewritePlan's grammar proves the
|
|
100
|
+
// word is the command's path operand. A regex over the text cannot tell: these
|
|
101
|
+
// prefixes are English words (`server`, `tasks`) and import-path fragments
|
|
102
|
+
// inside patterns, flag values (`rg -t cmd`, `ag --ignore tests`), halves of a
|
|
103
|
+
// two-path or `$(…)` search, or the pattern itself (`grep -n tasks f.py`) —
|
|
104
|
+
// pre-ship review round 1 reproduced each as a wrong hint or inject. Callers
|
|
105
|
+
// in a subdirectory shell must not use it on an un-rebased word: there a bare
|
|
106
|
+
// `src` is `<cwd>/src`, not the root's (see runMain). `.` and no path at all
|
|
107
|
+
// stay out: they reach non-source files, and grep -r ignores .gitignore where
|
|
108
|
+
// cg does not.
|
|
109
|
+
function bareSourceTarget(clause) {
|
|
110
|
+
const plan = rewritePlan(clause);
|
|
111
|
+
return plan && plan.target !== undefined && SRC_BARE_TOKEN.test(plan.target) ? plan.target : null;
|
|
112
|
+
}
|
|
113
|
+
function namesSourcePath(clause) {
|
|
114
|
+
return SRC_PATH.test(clause) || bareSourceTarget(clause) !== null;
|
|
115
|
+
}
|
|
92
116
|
const PIPE_INTO_GREP = new RegExp(`\\|\\s*(?:${GREP_VERB})\\b`);
|
|
93
117
|
const CG_INVOKED = /\bcode-graph-mcp\b/;
|
|
94
118
|
// File argument(s) that end in a config/lockfile/data extension. If, after removing
|
|
@@ -184,11 +208,11 @@ function shouldHint(cmd) {
|
|
|
184
208
|
// v0.96 — the source-path gate must see ONLY the grep's own args, not a path in
|
|
185
209
|
// a non-grep tail (`grep X skills/a.py; wc scripts/b` must not fire on scripts/b).
|
|
186
210
|
const clause = firstShellClause(cmd);
|
|
187
|
-
if (!
|
|
211
|
+
if (!namesSourcePath(clause)) return false; // not against indexed source tree
|
|
188
212
|
// If a config file appears AND no source path remains after stripping it, skip.
|
|
189
213
|
if (CONFIG_TARGET_ONLY.test(clause)) {
|
|
190
214
|
const stripped = clause.replace(CONFIG_TARGET_STRIP, ' ');
|
|
191
|
-
if (!
|
|
215
|
+
if (!namesSourcePath(stripped)) return false;
|
|
192
216
|
}
|
|
193
217
|
return true;
|
|
194
218
|
}
|
|
@@ -679,6 +703,13 @@ function extractSearchPath(cmd) {
|
|
|
679
703
|
// v0.96 — scope to the grep's own clause so the answer is never scoped to a
|
|
680
704
|
// path in a non-grep tail (the file the user actually grepped is the only one
|
|
681
705
|
// the "already ran for you" answer may claim to have searched).
|
|
706
|
+
// D#73 — when the grammar proves a bare/`./` source operand, it is the scope.
|
|
707
|
+
// It must win over the text scan below, which takes the first path-shaped
|
|
708
|
+
// token and so can take a PATTERN (`grep -rn "./src/x" tests`) or cut a
|
|
709
|
+
// quoted operand at its space (pre-ship review round 2 B1, B2). The grammar
|
|
710
|
+
// rejects `..`, so no traversal reaches this return.
|
|
711
|
+
const bare = bareSourceTarget(firstShellClause(cmd));
|
|
712
|
+
if (bare) return bare;
|
|
682
713
|
for (const raw of firstShellClause(cmd).split(/\s+/)) {
|
|
683
714
|
const token = raw.replace(/^["']|["']$/g, '');
|
|
684
715
|
if (!token || token.startsWith('-')) continue;
|
|
@@ -711,6 +742,11 @@ function countNamedPaths(cmd, patterns) {
|
|
|
711
742
|
if (pats.has(tok)) continue; // the search pattern, not a path
|
|
712
743
|
if (tok.includes('/') || /\.[A-Za-z0-9]{1,6}$/.test(tok)) n++; // dir-sep or file extension
|
|
713
744
|
}
|
|
745
|
+
// D#73 — the grammar's bare-dir operand is a named path too, counted like
|
|
746
|
+
// its `src/` spelling (whose loop pass counts it). The grammar admits one
|
|
747
|
+
// path operand, so this adds at most one.
|
|
748
|
+
const bare = bareSourceTarget(firstShellClause(cmd));
|
|
749
|
+
if (bare && !bare.includes('/') && !/\.[A-Za-z0-9]{1,6}$/.test(bare)) n++;
|
|
714
750
|
return n;
|
|
715
751
|
}
|
|
716
752
|
|
|
@@ -1183,6 +1219,9 @@ function runMain() {
|
|
|
1183
1219
|
let cmd = normalizeCommandPaths(rawCmd, root);
|
|
1184
1220
|
const relPrefix = path.relative(root, shellCwd);
|
|
1185
1221
|
if (relPrefix) cmd = rebaseRelativePaths(cmd, relPrefix, root);
|
|
1222
|
+
// D#73 — from a subdirectory, a bare dir the rebase left alone is `<cwd>/src`,
|
|
1223
|
+
// which the root-relative answer would not search (pre-ship review round 1 H1).
|
|
1224
|
+
if (relPrefix && bareSourceTarget(firstShellClause(cmd))) return;
|
|
1186
1225
|
if (!shouldHint(cmd)) return;
|
|
1187
1226
|
|
|
1188
1227
|
// v0.64 — fingerprint the grep's pattern once, shared by the emit points below.
|
|
@@ -1393,6 +1432,7 @@ module.exports = {
|
|
|
1393
1432
|
extractUnansweredTail, // v0.50 — compound-tail honesty in answered denies
|
|
1394
1433
|
extractPatterns, // v0.32.1 — exposed for tests
|
|
1395
1434
|
countNamedPaths, // v0.70 — multi-path deny→hint downgrade
|
|
1435
|
+
bareSourceTarget, // D#73 — a bare source dir the rewrite grammar proves is the path operand
|
|
1396
1436
|
isRevisionScopedGitGrep, // v0.71 — git grep --cached/treeish exclusion
|
|
1397
1437
|
extractSearchPath, // v0.47.0 — deny-with-answer
|
|
1398
1438
|
normalizeCommandPaths, // v0.47.1 — abs-path matcher fix
|
|
@@ -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.158.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.158.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.158.0",
|
|
39
|
+
"@sdsrs/code-graph-linux-arm64": "0.158.0",
|
|
40
|
+
"@sdsrs/code-graph-darwin-x64": "0.158.0",
|
|
41
|
+
"@sdsrs/code-graph-darwin-arm64": "0.158.0",
|
|
42
|
+
"@sdsrs/code-graph-win32-x64": "0.158.0"
|
|
43
43
|
}
|
|
44
44
|
}
|