grepmax 0.17.23 → 0.18.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/dist/commands/audit.js +10 -2
- package/dist/commands/dead.js +3 -0
- package/dist/commands/doctor.js +103 -10
- package/dist/commands/impact.js +3 -0
- package/dist/commands/peek.js +3 -0
- package/dist/commands/related.js +3 -0
- package/dist/commands/search-output.js +531 -0
- package/dist/commands/search.js +46 -462
- package/dist/commands/similar.js +3 -0
- package/dist/commands/test-find.js +3 -0
- package/dist/commands/trace.js +3 -0
- package/dist/config.js +80 -5
- package/dist/eval-graph-nav.js +374 -0
- package/dist/eval-graph-sanity.js +138 -19
- package/dist/lib/daemon/daemon.js +29 -643
- package/dist/lib/daemon/mlx-server-manager.js +191 -0
- package/dist/lib/daemon/process-manager.js +139 -0
- package/dist/lib/daemon/watcher-manager.js +484 -0
- package/dist/lib/graph/graph-builder.js +30 -8
- package/dist/lib/graph/impact.js +5 -3
- package/dist/lib/index/chunker.js +149 -4
- package/dist/lib/llm/tools.js +58 -16
- package/dist/lib/store/vector-db.js +43 -13
- package/dist/lib/utils/stale-hint.js +164 -0
- package/dist/lib/workers/orchestrator.js +2 -1
- package/package.json +17 -3
- package/plugins/grepmax/.claude-plugin/plugin.json +1 -1
|
@@ -1,20 +1,28 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* Graph sanity checks.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
5
|
+
* Default mode — caller-count guard (regression net for the chunker-v2 /
|
|
6
|
+
* call-site-dedup fix). Before that fix, `splitIfTooBig` copied a parent
|
|
7
|
+
* chunk's full `referenced_symbols` onto every sub-chunk, so `getCallers`
|
|
8
|
+
* multiplied chunk rows: a symbol with 3 real call sites reported 66 callers
|
|
9
|
+
* (22x). For a set of known gmax symbols this asserts `getCallers`' raw count
|
|
10
|
+
* stays within a small multiple of grep-truth (word-boundary line matches
|
|
11
|
+
* across tracked source). Exits nonzero if any symbol blows past the ceiling.
|
|
12
|
+
* Run after a reindex so the index matches the working tree.
|
|
9
13
|
*
|
|
10
|
-
*
|
|
11
|
-
* (the seed chunk's defined symbol points at the target via the call
|
|
12
|
-
* graph) → worth building the recovery layer.
|
|
14
|
+
* npx tsx src/eval-graph-sanity.ts
|
|
13
15
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
+
* Platform mode (--platform) — the original Phase 0 probe for Bundle B G1'
|
|
17
|
+
* (graph-as-recall-recovery). For each platform hard-miss case (BeyondError,
|
|
18
|
+
* ErrorCodes, resolveActor, errorHandler), mirror the searcher's dense+FTS+RRF
|
|
19
|
+
* pipeline to the post-fusion top-200 pool, then count how many of those chunks
|
|
20
|
+
* carry the target in `referenced_symbols`. ≥1 hit = a 1-hop outbound graph
|
|
21
|
+
* walk can recover the target → worth building the recovery layer; 0 across all
|
|
22
|
+
* 4 = the signal isn't in the graph at this depth. Requires
|
|
23
|
+
* ~/Development/beyond/platform to be indexed.
|
|
16
24
|
*
|
|
17
|
-
*
|
|
25
|
+
* npx tsx src/eval-graph-sanity.ts --platform
|
|
18
26
|
*/
|
|
19
27
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
20
28
|
if (k2 === undefined) k2 = k;
|
|
@@ -62,12 +70,15 @@ var _a, _b;
|
|
|
62
70
|
var _c;
|
|
63
71
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
64
72
|
(_a = (_c = process.env).GMAX_WORKER_COUNT) !== null && _a !== void 0 ? _a : (_c.GMAX_WORKER_COUNT = "1");
|
|
73
|
+
const node_child_process_1 = require("node:child_process");
|
|
65
74
|
const path = __importStar(require("node:path"));
|
|
66
75
|
const config_1 = require("./config");
|
|
76
|
+
const callsites_1 = require("./lib/graph/callsites");
|
|
77
|
+
const graph_builder_1 = require("./lib/graph/graph-builder");
|
|
67
78
|
const vector_db_1 = require("./lib/store/vector-db");
|
|
68
79
|
const exit_1 = require("./lib/utils/exit");
|
|
69
|
-
const pool_1 = require("./lib/workers/pool");
|
|
70
80
|
const filter_builder_1 = require("./lib/utils/filter-builder");
|
|
81
|
+
const pool_1 = require("./lib/workers/pool");
|
|
71
82
|
const PLATFORM_ROOT = path.join((_b = process.env.HOME) !== null && _b !== void 0 ? _b : "", "Development/beyond/platform");
|
|
72
83
|
const HARD_MISS_TARGETS = [
|
|
73
84
|
"BeyondError",
|
|
@@ -104,7 +115,9 @@ function probe(target) {
|
|
|
104
115
|
const table = yield db.ensureTable();
|
|
105
116
|
const pool = (0, pool_1.getWorkerPool)();
|
|
106
117
|
const { dense } = yield pool.encodeQuery(target);
|
|
107
|
-
const pathPrefix = PLATFORM_ROOT.endsWith("/")
|
|
118
|
+
const pathPrefix = PLATFORM_ROOT.endsWith("/")
|
|
119
|
+
? PLATFORM_ROOT
|
|
120
|
+
: `${PLATFORM_ROOT}/`;
|
|
108
121
|
const where = `path LIKE '${(0, filter_builder_1.escapeSqlString)(pathPrefix)}%'`;
|
|
109
122
|
const columns = [
|
|
110
123
|
"id",
|
|
@@ -136,14 +149,12 @@ function probe(target) {
|
|
|
136
149
|
const candidateScores = new Map();
|
|
137
150
|
const docMap = new Map();
|
|
138
151
|
vectorRows.forEach((doc, rank) => {
|
|
139
|
-
const key = doc.id ||
|
|
140
|
-
`${doc.path}:${doc.chunk_index}`;
|
|
152
|
+
const key = doc.id || `${doc.path}:${doc.chunk_index}`;
|
|
141
153
|
docMap.set(key, doc);
|
|
142
154
|
candidateScores.set(key, (candidateScores.get(key) || 0) + 1.0 / (RRF_K + rank + 1));
|
|
143
155
|
});
|
|
144
156
|
ftsRows.forEach((doc, rank) => {
|
|
145
|
-
const key = doc.id ||
|
|
146
|
-
`${doc.path}:${doc.chunk_index}`;
|
|
157
|
+
const key = doc.id || `${doc.path}:${doc.chunk_index}`;
|
|
147
158
|
if (!docMap.has(key))
|
|
148
159
|
docMap.set(key, doc);
|
|
149
160
|
candidateScores.set(key, (candidateScores.get(key) || 0) + 1.0 / (RRF_K + rank + 1));
|
|
@@ -189,7 +200,7 @@ function probe(target) {
|
|
|
189
200
|
};
|
|
190
201
|
});
|
|
191
202
|
}
|
|
192
|
-
function
|
|
203
|
+
function platformProbe() {
|
|
193
204
|
return __awaiter(this, void 0, void 0, function* () {
|
|
194
205
|
console.log(`Phase 0 sanity check — graph reachability on platform hard-miss cases`);
|
|
195
206
|
console.log(`pathPrefix: ${PLATFORM_ROOT}`);
|
|
@@ -214,7 +225,115 @@ function main() {
|
|
|
214
225
|
console.log(`\nVerdict: ${anyReachable ? "BUILD (≥1 case has graph signal in top-200)" : "ABORT (graph is empty at this depth — pick a different mechanism)"}`);
|
|
215
226
|
console.log(`\nJSON:`);
|
|
216
227
|
console.log(JSON.stringify(summary, null, 2));
|
|
217
|
-
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
// ---------------------------------------------------------------------------
|
|
231
|
+
// Caller-count guard (default mode)
|
|
232
|
+
// ---------------------------------------------------------------------------
|
|
233
|
+
const GMAX_ROOT = path.resolve(__dirname, "..");
|
|
234
|
+
/**
|
|
235
|
+
* Distinctive gmax symbols with a spread of real usage. All sit comfortably
|
|
236
|
+
* below getCallers' internal `.limit(100)`, so the cap can never silently mask
|
|
237
|
+
* an explosion — if the chunker regresses, the raw count blows past the
|
|
238
|
+
* ceiling rather than getting clamped to 100. Names are intentionally unusual
|
|
239
|
+
* (no bare words) so `git grep -w` truth isn't polluted by unrelated matches.
|
|
240
|
+
*/
|
|
241
|
+
const GUARD_SYMBOLS = [
|
|
242
|
+
"resolveCallSites",
|
|
243
|
+
"findCallSiteSnippet",
|
|
244
|
+
"withQueryTimeout",
|
|
245
|
+
"isFileCached",
|
|
246
|
+
"getWorkerPool",
|
|
247
|
+
"isBuiltinCallee",
|
|
248
|
+
"findDependents",
|
|
249
|
+
"buildFileSubgraph",
|
|
250
|
+
];
|
|
251
|
+
/**
|
|
252
|
+
* getCallers returns one chunk-row per referencing chunk. Healthy, that count
|
|
253
|
+
* is ≤ the number of source lines mentioning the symbol (several mentions in
|
|
254
|
+
* one chunk collapse to one row; def/import lines aren't call references). So
|
|
255
|
+
* the healthy ratio is ≤ ~1. A small multiple absorbs index-vs-worktree skew
|
|
256
|
+
* and chunk-boundary splits while still catching the 22x chunker-v2 blowup.
|
|
257
|
+
*/
|
|
258
|
+
const CEILING_MULTIPLE = 3;
|
|
259
|
+
/** Mirror of getCallers' internal `.limit(100)` — at the cap the guard is blind. */
|
|
260
|
+
const CALLER_LIMIT = 100;
|
|
261
|
+
/**
|
|
262
|
+
* grep-truth: word-boundary line + file counts for `symbol` across tracked
|
|
263
|
+
* `.ts` (repo-wide, so experiments/ and scripts/ count too — they're indexed).
|
|
264
|
+
* `git grep` skips gitignored paths (dist/), matching the indexed corpus.
|
|
265
|
+
*/
|
|
266
|
+
function grepTruth(symbol) {
|
|
267
|
+
let out;
|
|
268
|
+
try {
|
|
269
|
+
out = (0, node_child_process_1.execFileSync)("git", ["grep", "-Fwc", symbol, "--", "*.ts"], {
|
|
270
|
+
cwd: GMAX_ROOT,
|
|
271
|
+
encoding: "utf-8",
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
catch (_a) {
|
|
275
|
+
// git grep exits 1 when there are zero matches.
|
|
276
|
+
return { lines: 0, files: 0 };
|
|
277
|
+
}
|
|
278
|
+
let lines = 0;
|
|
279
|
+
let files = 0;
|
|
280
|
+
for (const row of out.split("\n")) {
|
|
281
|
+
const idx = row.lastIndexOf(":");
|
|
282
|
+
if (idx < 0)
|
|
283
|
+
continue;
|
|
284
|
+
const n = Number(row.slice(idx + 1));
|
|
285
|
+
if (Number.isFinite(n) && n > 0) {
|
|
286
|
+
lines += n;
|
|
287
|
+
files++;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
return { lines, files };
|
|
291
|
+
}
|
|
292
|
+
function callerCountGuard() {
|
|
293
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
294
|
+
const db = new vector_db_1.VectorDB(config_1.PATHS.lancedbDir);
|
|
295
|
+
yield db.ensureTable();
|
|
296
|
+
const builder = new graph_builder_1.GraphBuilder(db, GMAX_ROOT);
|
|
297
|
+
const fileCache = new Map();
|
|
298
|
+
console.log(`Caller-count guard — getCallers vs grep-truth call sites`);
|
|
299
|
+
console.log(`root: ${GMAX_ROOT}`);
|
|
300
|
+
console.log(`ceiling = ${CEILING_MULTIPLE}x grep lines · getCallers cap = ${CALLER_LIMIT}`);
|
|
301
|
+
console.log(`(run after a reindex so the index matches the working tree)\n`);
|
|
302
|
+
let allOK = true;
|
|
303
|
+
for (const sym of GUARD_SYMBOLS) {
|
|
304
|
+
const { lines: grepLines, files: grepFiles } = grepTruth(sym);
|
|
305
|
+
const callers = yield builder.getCallers(sym);
|
|
306
|
+
const raw = callers.length;
|
|
307
|
+
const deduped = (0, callsites_1.resolveCallSites)(callers.map((c) => ({ symbol: c.symbol, file: c.file, line: c.line })), sym, fileCache).length;
|
|
308
|
+
const ceiling = CEILING_MULTIPLE * Math.max(1, grepLines);
|
|
309
|
+
const capped = raw >= CALLER_LIMIT;
|
|
310
|
+
const ratio = grepLines > 0 ? raw / grepLines : raw;
|
|
311
|
+
// capped is a failure too: the fixture grew too common, so the cap now
|
|
312
|
+
// hides explosions — swap it for a lower-usage symbol.
|
|
313
|
+
const ok = !capped && raw <= ceiling;
|
|
314
|
+
if (!ok)
|
|
315
|
+
allOK = false;
|
|
316
|
+
console.log(`${ok ? "✓" : "✗"} ${sym.padEnd(20)} ` +
|
|
317
|
+
`callers=${String(raw).padStart(3)} (dedup ${String(deduped).padStart(3)}) ` +
|
|
318
|
+
`grep=${String(grepLines).padStart(3)}L/${String(grepFiles).padStart(2)}f ` +
|
|
319
|
+
`ratio=${ratio.toFixed(2)} ceiling=${ceiling}` +
|
|
320
|
+
(capped ? " [CAPPED — fixture too common]" : ""));
|
|
321
|
+
}
|
|
322
|
+
console.log(`\nVerdict: ${allOK ? "PASS (no caller-count explosion)" : "FAIL (getCallers exceeds grep-truth ceiling — chunker symbol dedup regressed)"}`);
|
|
323
|
+
yield db.close();
|
|
324
|
+
return allOK;
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
function main() {
|
|
328
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
329
|
+
const platform = process.argv.includes("--platform");
|
|
330
|
+
if (platform) {
|
|
331
|
+
yield platformProbe();
|
|
332
|
+
yield (0, exit_1.gracefulExit)(0);
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
const ok = yield callerCountGuard();
|
|
336
|
+
yield (0, exit_1.gracefulExit)(ok ? 0 : 1);
|
|
218
337
|
});
|
|
219
338
|
}
|
|
220
339
|
if (require.main === module) {
|