grepmax 0.17.24 → 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/dead.js +1 -0
- package/dist/commands/doctor.js +44 -0
- package/dist/commands/impact.js +1 -0
- package/dist/commands/peek.js +1 -0
- package/dist/commands/related.js +1 -0
- package/dist/commands/search-output.js +531 -0
- package/dist/commands/search.js +44 -462
- package/dist/commands/similar.js +1 -0
- package/dist/commands/test-find.js +1 -0
- package/dist/commands/trace.js +1 -0
- package/dist/config.js +34 -0
- 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/utils/stale-hint.js +115 -13
- package/package.json +1 -1
- package/plugins/grepmax/.claude-plugin/plugin.json +1 -1
package/dist/commands/search.js
CHANGED
|
@@ -45,20 +45,15 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
45
45
|
exports.search = void 0;
|
|
46
46
|
const path = __importStar(require("node:path"));
|
|
47
47
|
const commander_1 = require("commander");
|
|
48
|
-
const agent_search_formatter_1 = require("../lib/output/agent-search-formatter");
|
|
49
|
-
const compact_results_1 = require("../lib/output/compact-results");
|
|
50
|
-
const index_state_footer_1 = require("../lib/output/index-state-footer");
|
|
51
48
|
const setup_helpers_1 = require("../lib/setup/setup-helpers");
|
|
52
49
|
const cross_project_1 = require("../lib/utils/cross-project");
|
|
53
50
|
const exit_1 = require("../lib/utils/exit");
|
|
54
|
-
const formatter_1 = require("../lib/utils/formatter");
|
|
55
|
-
const import_extractor_1 = require("../lib/utils/import-extractor");
|
|
56
51
|
const project_registry_1 = require("../lib/utils/project-registry");
|
|
57
52
|
const project_root_1 = require("../lib/utils/project-root");
|
|
58
53
|
const server_registry_1 = require("../lib/utils/server-registry");
|
|
59
54
|
const stale_hint_1 = require("../lib/utils/stale-hint");
|
|
55
|
+
const search_output_1 = require("./search-output");
|
|
60
56
|
const search_run_1 = require("./search-run");
|
|
61
|
-
const search_skeletons_1 = require("./search-skeletons");
|
|
62
57
|
exports.search = new commander_1.Command("search")
|
|
63
58
|
.description("Search code by meaning (default command)")
|
|
64
59
|
.option("-m <max_count>, --max-count <max_count>", "The maximum number of results to return (total)", "5")
|
|
@@ -88,8 +83,8 @@ exports.search = new commander_1.Command("search")
|
|
|
88
83
|
.option("--name <regex>", "Filter results by symbol name regex")
|
|
89
84
|
.option("-C, --context <n>", "Include N lines before/after each result")
|
|
90
85
|
.option("--agent", "Ultra-compact output for AI agents (one line per result)", false)
|
|
91
|
-
.option("--seed-file <path>", "Bias results toward your working context (repeatable; comma-separated also accepted)", (value, prev) =>
|
|
92
|
-
.option("--seed-symbol <name>", "Bias results toward an identifier you're working with (repeatable; comma-separated also accepted)", (value, prev) =>
|
|
86
|
+
.option("--seed-file <path>", "Bias results toward your working context (repeatable; comma-separated also accepted)", (value, prev) => prev ? [...prev, value] : [value])
|
|
87
|
+
.option("--seed-symbol <name>", "Bias results toward an identifier you're working with (repeatable; comma-separated also accepted)", (value, prev) => prev ? [...prev, value] : [value])
|
|
93
88
|
.argument("<pattern>", 'Natural language query (e.g. "where do we handle auth?")')
|
|
94
89
|
.argument("[path]", "Restrict search to this path prefix")
|
|
95
90
|
.addHelpText("after", `
|
|
@@ -104,7 +99,7 @@ Examples:
|
|
|
104
99
|
gmax "auth middleware" --projects api,gateway --plain
|
|
105
100
|
`)
|
|
106
101
|
.action((pattern, exec_path, _options, cmd) => __awaiter(void 0, void 0, void 0, function* () {
|
|
107
|
-
var _a, _b, _c, _d, _e
|
|
102
|
+
var _a, _b, _c, _d, _e;
|
|
108
103
|
const options = cmd.optsWithGlobals();
|
|
109
104
|
const root = process.cwd();
|
|
110
105
|
const minScore = Number.isFinite(Number.parseFloat(options.minScore))
|
|
@@ -154,110 +149,20 @@ Examples:
|
|
|
154
149
|
const server = crossProject.active
|
|
155
150
|
? null
|
|
156
151
|
: (0, server_registry_1.getServerForProject)(projectRootForServer);
|
|
152
|
+
// The standalone HTTP-server path lives OUTSIDE the main try/finally below:
|
|
153
|
+
// a handled server search returns here, intentionally skipping query
|
|
154
|
+
// logging and gracefulExit (unchanged from before the extraction).
|
|
157
155
|
if (server) {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
}),
|
|
169
|
-
});
|
|
170
|
-
if (response.ok) {
|
|
171
|
-
const body = (yield response.json());
|
|
172
|
-
const searchResult = { data: body.results };
|
|
173
|
-
const filteredData = searchResult.data.filter((r) => typeof r.score !== "number" || r.score >= minScore);
|
|
174
|
-
if (options.skeleton) {
|
|
175
|
-
yield (0, search_skeletons_1.outputSkeletons)(filteredData, projectRootForServer, parseInt(options.m, 10),
|
|
176
|
-
// Server doesn't easily expose DB instance here in HTTP client mode,
|
|
177
|
-
// but we are in client. Wait, this text implies "Server Search" block.
|
|
178
|
-
// Client talks to server. The server returns JSON.
|
|
179
|
-
// We don't have DB access here.
|
|
180
|
-
// So we pass null, and it will fallback to generating local skeleton (if file exists locally).
|
|
181
|
-
// This is acceptable for Phase 3.
|
|
182
|
-
null);
|
|
183
|
-
return;
|
|
184
|
-
}
|
|
185
|
-
const compactHits = options.compact
|
|
186
|
-
? (0, compact_results_1.toCompactHits)(filteredData)
|
|
187
|
-
: [];
|
|
188
|
-
if (options.compact) {
|
|
189
|
-
if (!compactHits.length) {
|
|
190
|
-
console.log("No matches found.");
|
|
191
|
-
console.log("\nTry: broaden your query, use fewer keywords, or check `gmax status` to verify the project is indexed.");
|
|
192
|
-
process.exitCode = 1;
|
|
193
|
-
}
|
|
194
|
-
else {
|
|
195
|
-
console.log((0, compact_results_1.formatCompactTable)(compactHits, projectRootForServer, pattern, {
|
|
196
|
-
isTTY: !!process.stdout.isTTY,
|
|
197
|
-
plain: !!options.plain,
|
|
198
|
-
}));
|
|
199
|
-
}
|
|
200
|
-
return; // EXIT
|
|
201
|
-
}
|
|
202
|
-
if (!filteredData.length) {
|
|
203
|
-
console.log("No matches found.");
|
|
204
|
-
console.log("\nTry: broaden your query, use fewer keywords, or check `gmax status` to verify the project is indexed.");
|
|
205
|
-
process.exitCode = 1;
|
|
206
|
-
return; // EXIT
|
|
207
|
-
}
|
|
208
|
-
if (options.agent) {
|
|
209
|
-
const importCache = new Map();
|
|
210
|
-
const getImportsForFile = (absPath) => {
|
|
211
|
-
var _a;
|
|
212
|
-
if (!options.imports || !absPath)
|
|
213
|
-
return "";
|
|
214
|
-
if (!importCache.has(absPath)) {
|
|
215
|
-
importCache.set(absPath, (0, import_extractor_1.extractImports)(absPath));
|
|
216
|
-
}
|
|
217
|
-
return (_a = importCache.get(absPath)) !== null && _a !== void 0 ? _a : "";
|
|
218
|
-
};
|
|
219
|
-
console.log((0, agent_search_formatter_1.formatAgentSearchResults)(filteredData, projectRootForServer, {
|
|
220
|
-
includeImports: options.imports,
|
|
221
|
-
query: pattern,
|
|
222
|
-
getImportsForFile,
|
|
223
|
-
explain: options.explain,
|
|
224
|
-
}));
|
|
225
|
-
return; // EXIT
|
|
226
|
-
}
|
|
227
|
-
const isTTY = process.stdout.isTTY;
|
|
228
|
-
const shouldBePlain = options.plain || !isTTY;
|
|
229
|
-
_searchResultCount = filteredData.length;
|
|
230
|
-
if (!options.agent && !options.compact) {
|
|
231
|
-
console.log((0, compact_results_1.resultCountHeader)(filteredData, parseInt(options.m, 10)));
|
|
232
|
-
console.log();
|
|
233
|
-
}
|
|
234
|
-
if (shouldBePlain) {
|
|
235
|
-
const mappedResults = (0, compact_results_1.toTextResults)(filteredData);
|
|
236
|
-
const output = (0, formatter_1.formatTextResults)(mappedResults, pattern, projectRootForServer, {
|
|
237
|
-
isPlain: true,
|
|
238
|
-
compact: options.compact,
|
|
239
|
-
content: options.content,
|
|
240
|
-
perFile: parseInt(options.perFile, 10),
|
|
241
|
-
showScores: options.scores,
|
|
242
|
-
});
|
|
243
|
-
console.log(output);
|
|
244
|
-
}
|
|
245
|
-
else {
|
|
246
|
-
const { formatResults } = yield Promise.resolve().then(() => __importStar(require("../lib/output/formatter")));
|
|
247
|
-
const output = formatResults(filteredData, projectRootForServer, {
|
|
248
|
-
content: options.content,
|
|
249
|
-
explain: options.explain,
|
|
250
|
-
});
|
|
251
|
-
console.log(output);
|
|
252
|
-
}
|
|
253
|
-
return; // EXIT successful server search
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
catch (e) {
|
|
257
|
-
if (process.env.DEBUG) {
|
|
258
|
-
console.error("[search] server request failed, falling back to local:", e);
|
|
259
|
-
}
|
|
260
|
-
}
|
|
156
|
+
const handled = yield (0, search_output_1.executeServerSearch)({
|
|
157
|
+
server,
|
|
158
|
+
pattern,
|
|
159
|
+
exec_path,
|
|
160
|
+
projectRootForServer,
|
|
161
|
+
options,
|
|
162
|
+
minScore,
|
|
163
|
+
});
|
|
164
|
+
if (handled)
|
|
165
|
+
return;
|
|
261
166
|
}
|
|
262
167
|
try {
|
|
263
168
|
yield (0, setup_helpers_1.ensureSetup)();
|
|
@@ -287,6 +192,10 @@ Examples:
|
|
|
287
192
|
console.warn("This project is still being indexed. Results may be incomplete.\n");
|
|
288
193
|
}
|
|
289
194
|
(0, stale_hint_1.maybeWarnStaleChunker)(checkRoot, { agent: options.agent });
|
|
195
|
+
(0, stale_hint_1.maybeWarnStaleEmbedding)(checkRoot, { agent: options.agent });
|
|
196
|
+
if (crossProject.active) {
|
|
197
|
+
(0, stale_hint_1.maybeWarnCrossProjectDim)(crossProject.roots, { agent: options.agent });
|
|
198
|
+
}
|
|
290
199
|
// Compute effective paths + filters early — both the daemon-mediated
|
|
291
200
|
// and in-process search paths need them. Reuse the resolved checkRoot
|
|
292
201
|
// so --root <name> only resolves once per invocation.
|
|
@@ -351,7 +260,9 @@ Examples:
|
|
|
351
260
|
};
|
|
352
261
|
const seedFiles = splitSeeds(options.seedFile);
|
|
353
262
|
const seedSymbols = splitSeeds(options.seedSymbol);
|
|
354
|
-
const seeds = seedFiles || seedSymbols
|
|
263
|
+
const seeds = seedFiles || seedSymbols
|
|
264
|
+
? { files: seedFiles, symbols: seedSymbols }
|
|
265
|
+
: undefined;
|
|
355
266
|
// Acquire results: daemon-mediated first, in-process fallback otherwise.
|
|
356
267
|
// The render stage below is shared across both. `runSearch` reports the
|
|
357
268
|
// VectorDB it opens via the callback so the `finally` can close it.
|
|
@@ -369,353 +280,24 @@ Examples:
|
|
|
369
280
|
vectorDb = acquired.vectorDb;
|
|
370
281
|
if (acquired.kind === "dry-run")
|
|
371
282
|
return;
|
|
372
|
-
const { searchResult, precomputedSkeletons, precomputedGraph, indexState } = acquired;
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
const regex = new RegExp(options.name, "i");
|
|
391
|
-
filteredData = filteredData.filter((r) => {
|
|
392
|
-
const defs = Array.isArray(r.defined_symbols)
|
|
393
|
-
? r.defined_symbols
|
|
394
|
-
: [];
|
|
395
|
-
return defs.some((d) => regex.test(d));
|
|
396
|
-
});
|
|
397
|
-
}
|
|
398
|
-
catch (_o) {
|
|
399
|
-
// Invalid regex — skip
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
// Build import cache when --imports is requested
|
|
403
|
-
const importCache = new Map();
|
|
404
|
-
const getImportsForFile = (absPath) => {
|
|
405
|
-
var _a;
|
|
406
|
-
if (!options.imports || !absPath)
|
|
407
|
-
return "";
|
|
408
|
-
if (!importCache.has(absPath)) {
|
|
409
|
-
importCache.set(absPath, (0, import_extractor_1.extractImports)(absPath));
|
|
410
|
-
}
|
|
411
|
-
return (_a = importCache.get(absPath)) !== null && _a !== void 0 ? _a : "";
|
|
412
|
-
};
|
|
413
|
-
// Agent mode: ultra-compact one-line-per-result output
|
|
414
|
-
_searchResultCount = filteredData.length;
|
|
415
|
-
// Cross-project (Phase 6): render grouped by owning project so idioms
|
|
416
|
-
// from different stacks don't blur into one flat list. Only the
|
|
417
|
-
// string-formatter modes reach here — skeleton/context-for-llm/symbol
|
|
418
|
-
// were rejected up front.
|
|
419
|
-
if (crossProject.active) {
|
|
420
|
-
const emitFooter = () => {
|
|
421
|
-
const footer = (0, index_state_footer_1.formatIndexStateFooter)(indexState, {
|
|
422
|
-
agent: !!options.agent,
|
|
423
|
-
});
|
|
424
|
-
if (footer) {
|
|
425
|
-
if (options.agent)
|
|
426
|
-
console.log(footer);
|
|
427
|
-
else
|
|
428
|
-
console.warn(footer);
|
|
429
|
-
}
|
|
430
|
-
};
|
|
431
|
-
if (!filteredData.length) {
|
|
432
|
-
console.log(options.agent ? "(none)" : "No matches found.");
|
|
433
|
-
process.exitCode = 1;
|
|
434
|
-
emitFooter();
|
|
435
|
-
return;
|
|
436
|
-
}
|
|
437
|
-
const getPath = (r) => {
|
|
438
|
-
var _a, _b, _c;
|
|
439
|
-
return String((_c = (_a = r.path) !== null && _a !== void 0 ? _a : (_b = r.metadata) === null || _b === void 0 ? void 0 : _b.path) !== null && _c !== void 0 ? _c : "");
|
|
440
|
-
};
|
|
441
|
-
const groups = (0, cross_project_1.groupResultsByProject)(filteredData, crossProject.roots, getPath);
|
|
442
|
-
const isTTY = process.stdout.isTTY;
|
|
443
|
-
const shouldBePlain = options.plain || !isTTY;
|
|
444
|
-
const blocks = [];
|
|
445
|
-
for (const g of groups) {
|
|
446
|
-
let body;
|
|
447
|
-
if (options.agent) {
|
|
448
|
-
body = (0, agent_search_formatter_1.formatAgentSearchResults)(g.items, g.root, {
|
|
449
|
-
includeImports: options.imports,
|
|
450
|
-
query: pattern,
|
|
451
|
-
getImportsForFile,
|
|
452
|
-
explain: options.explain,
|
|
453
|
-
});
|
|
454
|
-
}
|
|
455
|
-
else if (options.compact) {
|
|
456
|
-
body = (0, compact_results_1.formatCompactTable)((0, compact_results_1.toCompactHits)(g.items), g.root, pattern, {
|
|
457
|
-
isTTY: !!isTTY,
|
|
458
|
-
plain: !!options.plain,
|
|
459
|
-
});
|
|
460
|
-
}
|
|
461
|
-
else if (shouldBePlain) {
|
|
462
|
-
body = (0, formatter_1.formatTextResults)((0, compact_results_1.toTextResults)(g.items), pattern, g.root, {
|
|
463
|
-
isPlain: true,
|
|
464
|
-
compact: options.compact,
|
|
465
|
-
content: options.content,
|
|
466
|
-
perFile: parseInt(options.perFile, 10),
|
|
467
|
-
showScores: options.scores,
|
|
468
|
-
});
|
|
469
|
-
}
|
|
470
|
-
else {
|
|
471
|
-
const { formatResults } = yield Promise.resolve().then(() => __importStar(require("../lib/output/formatter")));
|
|
472
|
-
body = formatResults(g.items, g.root, {
|
|
473
|
-
content: options.content,
|
|
474
|
-
explain: options.explain,
|
|
475
|
-
});
|
|
476
|
-
}
|
|
477
|
-
const header = options.agent
|
|
478
|
-
? `## ${g.name} (${g.items.length})`
|
|
479
|
-
: `=== ${g.name} (${g.items.length}) ===`;
|
|
480
|
-
blocks.push(`${header}\n${body}`);
|
|
481
|
-
}
|
|
482
|
-
console.log(blocks.join("\n\n"));
|
|
483
|
-
emitFooter();
|
|
484
|
-
return;
|
|
485
|
-
}
|
|
486
|
-
if (options.agent) {
|
|
487
|
-
if (!filteredData.length) {
|
|
488
|
-
console.log("(none)");
|
|
489
|
-
process.exitCode = 1;
|
|
490
|
-
}
|
|
491
|
-
else {
|
|
492
|
-
console.log((0, agent_search_formatter_1.formatAgentSearchResults)(filteredData, effectiveRoot, {
|
|
493
|
-
includeImports: options.imports,
|
|
494
|
-
query: pattern,
|
|
495
|
-
getImportsForFile,
|
|
496
|
-
explain: options.explain,
|
|
497
|
-
}));
|
|
498
|
-
}
|
|
499
|
-
// Agent trace (compact)
|
|
500
|
-
if (options.symbol && filteredData.length > 0) {
|
|
501
|
-
try {
|
|
502
|
-
let graph = precomputedGraph;
|
|
503
|
-
if (!graph) {
|
|
504
|
-
if (!vectorDb)
|
|
505
|
-
throw new Error("no graph source");
|
|
506
|
-
const { GraphBuilder } = yield Promise.resolve().then(() => __importStar(require("../lib/graph/graph-builder")));
|
|
507
|
-
const builder = new GraphBuilder(vectorDb, effectiveRoot);
|
|
508
|
-
graph = yield builder.buildGraphMultiHop(pattern, 1);
|
|
509
|
-
}
|
|
510
|
-
if (graph === null || graph === void 0 ? void 0 : graph.center) {
|
|
511
|
-
console.log("---");
|
|
512
|
-
for (const t of graph.callerTree) {
|
|
513
|
-
const rel = t.node.file.startsWith(effectiveRoot)
|
|
514
|
-
? t.node.file.slice(effectiveRoot.length + 1)
|
|
515
|
-
: t.node.file;
|
|
516
|
-
console.log(`<- ${t.node.symbol} ${rel}:${t.node.line + 1}`);
|
|
517
|
-
}
|
|
518
|
-
for (const c of graph.callees.slice(0, 10)) {
|
|
519
|
-
if (c.file) {
|
|
520
|
-
const rel = c.file.startsWith(effectiveRoot)
|
|
521
|
-
? c.file.slice(effectiveRoot.length + 1)
|
|
522
|
-
: c.file;
|
|
523
|
-
console.log(`-> ${c.symbol} ${rel}:${c.line + 1}`);
|
|
524
|
-
}
|
|
525
|
-
}
|
|
526
|
-
}
|
|
527
|
-
}
|
|
528
|
-
catch (_p) { }
|
|
529
|
-
}
|
|
530
|
-
// Partial-index footer last, so it's the final line the agent reads —
|
|
531
|
-
// and emitted even on "(none)", where an empty result may just mean the
|
|
532
|
-
// relevant files aren't indexed yet.
|
|
533
|
-
const footer = (0, index_state_footer_1.formatIndexStateFooter)(indexState, { agent: true });
|
|
534
|
-
if (footer)
|
|
535
|
-
console.log(footer);
|
|
536
|
-
return;
|
|
537
|
-
}
|
|
538
|
-
if (options.skeleton) {
|
|
539
|
-
yield (0, search_skeletons_1.outputSkeletons)(filteredData, projectRoot, parseInt(options.m, 10), vectorDb, precomputedSkeletons);
|
|
540
|
-
return;
|
|
541
|
-
}
|
|
542
|
-
if (!filteredData.length) {
|
|
543
|
-
console.log("No matches found.");
|
|
544
|
-
console.log("\nTry: broaden your query, use fewer keywords, or check `gmax status` to verify the project is indexed.");
|
|
545
|
-
process.exitCode = 1;
|
|
546
|
-
return;
|
|
547
|
-
}
|
|
548
|
-
if (options.compact) {
|
|
549
|
-
const compactHits = (0, compact_results_1.toCompactHits)(filteredData);
|
|
550
|
-
console.log((0, compact_results_1.formatCompactTable)(compactHits, projectRoot, pattern, {
|
|
551
|
-
isTTY: !!process.stdout.isTTY,
|
|
552
|
-
plain: !!options.plain,
|
|
553
|
-
}));
|
|
554
|
-
return;
|
|
555
|
-
}
|
|
556
|
-
_searchResultCount = filteredData.length;
|
|
557
|
-
// Context-for-LLM mode: full function body + imports per result
|
|
558
|
-
if (options.contextForLlm) {
|
|
559
|
-
const fs = yield Promise.resolve().then(() => __importStar(require("node:fs")));
|
|
560
|
-
const { extractImportsFromContent } = yield Promise.resolve().then(() => __importStar(require("../lib/utils/import-extractor")));
|
|
561
|
-
const { packByBudget } = yield Promise.resolve().then(() => __importStar(require("../lib/utils/budget-pack")));
|
|
562
|
-
const budget = parseInt(options.budget, 10) || 8000;
|
|
563
|
-
console.log((0, compact_results_1.resultCountHeader)(filteredData, parseInt(options.m, 10)));
|
|
564
|
-
// Build every candidate blob up front (token cost needs the rendered
|
|
565
|
-
// text), then pack to budget. Token-aware packing skips an oversized
|
|
566
|
-
// chunk and keeps filling with smaller, still-relevant ones rather than
|
|
567
|
-
// aborting the loop — recovering budget the old greedy `break` wasted.
|
|
568
|
-
const candidates = filteredData.map((r, idx) => {
|
|
569
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
|
|
570
|
-
const absP = (_c = (_a = r.path) !== null && _a !== void 0 ? _a : (_b = r.metadata) === null || _b === void 0 ? void 0 : _b.path) !== null && _c !== void 0 ? _c : "";
|
|
571
|
-
const startLine = (_g = (_e = (_d = r.startLine) !== null && _d !== void 0 ? _d : r.start_line) !== null && _e !== void 0 ? _e : (_f = r.generated_metadata) === null || _f === void 0 ? void 0 : _f.start_line) !== null && _g !== void 0 ? _g : 0;
|
|
572
|
-
const endLine = (_l = (_j = (_h = r.endLine) !== null && _h !== void 0 ? _h : r.end_line) !== null && _j !== void 0 ? _j : (_k = r.generated_metadata) === null || _k === void 0 ? void 0 : _k.end_line) !== null && _l !== void 0 ? _l : startLine;
|
|
573
|
-
const relPath = absP.startsWith(projectRoot)
|
|
574
|
-
? absP.slice(projectRoot.length + 1)
|
|
575
|
-
: absP;
|
|
576
|
-
const role = r.role || "IMPLEMENTATION";
|
|
577
|
-
const symbol = Array.isArray(r.defined_symbols) &&
|
|
578
|
-
r.defined_symbols.length > 0
|
|
579
|
-
? r.defined_symbols[0]
|
|
580
|
-
: "";
|
|
581
|
-
let blobText;
|
|
582
|
-
try {
|
|
583
|
-
const content = fs.readFileSync(absP, "utf-8");
|
|
584
|
-
const allLines = content.split("\n");
|
|
585
|
-
const body = allLines
|
|
586
|
-
.slice(startLine, Math.min(endLine + 1, allLines.length))
|
|
587
|
-
.join("\n");
|
|
588
|
-
const imports = extractImportsFromContent(content);
|
|
589
|
-
const blob = [
|
|
590
|
-
`--- ${relPath}:${startLine + 1}${symbol ? ` ${symbol}` : ""} [${role}] ---`,
|
|
591
|
-
];
|
|
592
|
-
if (imports)
|
|
593
|
-
blob.push("[imports]", imports, "");
|
|
594
|
-
blob.push("[body]", body);
|
|
595
|
-
blobText = blob.join("\n");
|
|
596
|
-
}
|
|
597
|
-
catch (_m) {
|
|
598
|
-
blobText = `--- ${relPath} (file not readable) ---`;
|
|
599
|
-
}
|
|
600
|
-
// Preserve relevance order when scores are absent (rank-derived
|
|
601
|
-
// fallback) so the density tiebreaker never reshuffles arbitrarily.
|
|
602
|
-
const score = typeof r.score === "number"
|
|
603
|
-
? r.score
|
|
604
|
-
: (filteredData.length - idx) / filteredData.length;
|
|
605
|
-
return { blobText, tokens: Math.ceil(blobText.length / 4), score };
|
|
606
|
-
});
|
|
607
|
-
const pack = packByBudget(candidates.map((c) => ({ tokens: c.tokens, score: c.score })), budget);
|
|
608
|
-
for (const i of pack.selected) {
|
|
609
|
-
console.log(`\n${candidates[i].blobText}`);
|
|
610
|
-
}
|
|
611
|
-
if (pack.dropped > 0) {
|
|
612
|
-
console.log(`\n(budget: ~${pack.tokensUsed}/${budget} tokens, ${pack.dropped} lower-density result${pack.dropped > 1 ? "s" : ""} not shown)`);
|
|
613
|
-
}
|
|
614
|
-
return;
|
|
615
|
-
}
|
|
616
|
-
const isTTY = process.stdout.isTTY;
|
|
617
|
-
const shouldBePlain = options.plain || !isTTY;
|
|
618
|
-
if (!options.agent && !options.compact) {
|
|
619
|
-
console.log((0, compact_results_1.resultCountHeader)(filteredData, parseInt(options.m, 10)));
|
|
620
|
-
console.log();
|
|
621
|
-
}
|
|
622
|
-
// Print imports per unique file before results when --imports is used
|
|
623
|
-
if (options.imports) {
|
|
624
|
-
const seenFiles = new Set();
|
|
625
|
-
for (const r of filteredData) {
|
|
626
|
-
const absP = (_h = (_f = r.path) !== null && _f !== void 0 ? _f : (_g = r.metadata) === null || _g === void 0 ? void 0 : _g.path) !== null && _h !== void 0 ? _h : "";
|
|
627
|
-
if (absP && !seenFiles.has(absP)) {
|
|
628
|
-
seenFiles.add(absP);
|
|
629
|
-
const imports = getImportsForFile(absP);
|
|
630
|
-
if (imports) {
|
|
631
|
-
const relP = absP.startsWith(effectiveRoot)
|
|
632
|
-
? absP.slice(effectiveRoot.length + 1)
|
|
633
|
-
: absP;
|
|
634
|
-
console.log(`--- imports: ${relP} ---\n${imports}\n`);
|
|
635
|
-
}
|
|
636
|
-
}
|
|
637
|
-
}
|
|
638
|
-
}
|
|
639
|
-
if (shouldBePlain) {
|
|
640
|
-
const mappedResults = (0, compact_results_1.toTextResults)(filteredData);
|
|
641
|
-
const output = (0, formatter_1.formatTextResults)(mappedResults, pattern, projectRoot, {
|
|
642
|
-
isPlain: true,
|
|
643
|
-
compact: options.compact,
|
|
644
|
-
content: options.content,
|
|
645
|
-
perFile: parseInt(options.perFile, 10),
|
|
646
|
-
showScores: options.scores,
|
|
647
|
-
});
|
|
648
|
-
console.log(output);
|
|
649
|
-
if (options.explain) {
|
|
650
|
-
for (const r of filteredData) {
|
|
651
|
-
const b = r.scoreBreakdown;
|
|
652
|
-
if (b) {
|
|
653
|
-
const absP = (_l = (_j = r.path) !== null && _j !== void 0 ? _j : (_k = r.metadata) === null || _k === void 0 ? void 0 : _k.path) !== null && _l !== void 0 ? _l : "";
|
|
654
|
-
const relPath = absP.startsWith(projectRoot)
|
|
655
|
-
? absP.slice(projectRoot.length + 1)
|
|
656
|
-
: absP;
|
|
657
|
-
console.log(` [explain ${relPath}] rerank=${b.rerank.toFixed(3)} fused=${b.fused.toFixed(3)} boost=${b.boost.toFixed(2)}x final=${b.normalized.toFixed(3)}`);
|
|
658
|
-
}
|
|
659
|
-
}
|
|
660
|
-
}
|
|
661
|
-
}
|
|
662
|
-
else {
|
|
663
|
-
// Use new holographic formatter for TTY
|
|
664
|
-
const { formatResults } = yield Promise.resolve().then(() => __importStar(require("../lib/output/formatter")));
|
|
665
|
-
const output = formatResults(filteredData, projectRoot, {
|
|
666
|
-
content: options.content,
|
|
667
|
-
explain: options.explain,
|
|
668
|
-
});
|
|
669
|
-
console.log(output);
|
|
670
|
-
}
|
|
671
|
-
// Symbol mode: append call graph
|
|
672
|
-
if (options.symbol) {
|
|
673
|
-
try {
|
|
674
|
-
let graph = precomputedGraph;
|
|
675
|
-
if (!graph) {
|
|
676
|
-
if (!vectorDb)
|
|
677
|
-
throw new Error("no graph source");
|
|
678
|
-
const { GraphBuilder } = yield Promise.resolve().then(() => __importStar(require("../lib/graph/graph-builder")));
|
|
679
|
-
const builder = new GraphBuilder(vectorDb, effectiveRoot);
|
|
680
|
-
graph = yield builder.buildGraphMultiHop(pattern, 1);
|
|
681
|
-
}
|
|
682
|
-
if (graph === null || graph === void 0 ? void 0 : graph.center) {
|
|
683
|
-
const lines = ["\n--- Call graph ---"];
|
|
684
|
-
const centerRel = path.relative(effectiveRoot, graph.center.file);
|
|
685
|
-
lines.push(`${graph.center.symbol} [${graph.center.role}] ${centerRel}:${graph.center.line + 1}`);
|
|
686
|
-
if (graph.importers.length > 0) {
|
|
687
|
-
const filtered = graph.importers.filter((p) => p !== graph.center.file);
|
|
688
|
-
if (filtered.length > 0) {
|
|
689
|
-
lines.push("Imported by:");
|
|
690
|
-
for (const imp of filtered.slice(0, 10)) {
|
|
691
|
-
lines.push(` ${path.relative(effectiveRoot, imp)}`);
|
|
692
|
-
}
|
|
693
|
-
}
|
|
694
|
-
}
|
|
695
|
-
if (graph.callerTree.length > 0) {
|
|
696
|
-
lines.push("Callers:");
|
|
697
|
-
for (const t of graph.callerTree) {
|
|
698
|
-
lines.push(` <- ${t.node.symbol} ${path.relative(effectiveRoot, t.node.file)}:${t.node.line + 1}`);
|
|
699
|
-
}
|
|
700
|
-
}
|
|
701
|
-
if (graph.callees.length > 0) {
|
|
702
|
-
lines.push("Calls:");
|
|
703
|
-
for (const c of graph.callees.slice(0, 15)) {
|
|
704
|
-
if (c.file) {
|
|
705
|
-
lines.push(` -> ${c.symbol} ${path.relative(effectiveRoot, c.file)}:${c.line + 1}`);
|
|
706
|
-
}
|
|
707
|
-
else {
|
|
708
|
-
lines.push(` -> ${c.symbol} (not indexed)`);
|
|
709
|
-
}
|
|
710
|
-
}
|
|
711
|
-
}
|
|
712
|
-
console.log(lines.join("\n"));
|
|
713
|
-
}
|
|
714
|
-
}
|
|
715
|
-
catch (_q) {
|
|
716
|
-
// Trace failed — skip silently
|
|
717
|
-
}
|
|
718
|
-
}
|
|
283
|
+
const { searchResult, precomputedSkeletons, precomputedGraph, indexState, } = acquired;
|
|
284
|
+
// Presentation stage (shared by daemon-mediated + in-process paths):
|
|
285
|
+
// min-score/name post-filters + the 7-mode render switch live in
|
|
286
|
+
// renderSearchOutput; it returns the post-filter count for query logging.
|
|
287
|
+
const { resultCount } = yield (0, search_output_1.renderSearchOutput)({
|
|
288
|
+
searchResult,
|
|
289
|
+
options,
|
|
290
|
+
minScore,
|
|
291
|
+
crossProject,
|
|
292
|
+
pattern,
|
|
293
|
+
effectiveRoot,
|
|
294
|
+
projectRoot,
|
|
295
|
+
vectorDb,
|
|
296
|
+
precomputedSkeletons,
|
|
297
|
+
precomputedGraph,
|
|
298
|
+
indexState,
|
|
299
|
+
});
|
|
300
|
+
_searchResultCount = resultCount;
|
|
719
301
|
}
|
|
720
302
|
catch (error) {
|
|
721
303
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
@@ -732,13 +314,13 @@ Examples:
|
|
|
732
314
|
source: "cli",
|
|
733
315
|
tool: "search",
|
|
734
316
|
query: pattern,
|
|
735
|
-
project: (
|
|
317
|
+
project: (_e = (0, project_root_1.findProjectRoot)(root)) !== null && _e !== void 0 ? _e : root,
|
|
736
318
|
results: _searchResultCount,
|
|
737
319
|
ms: Date.now() - _searchStartMs,
|
|
738
320
|
error: _searchError,
|
|
739
321
|
});
|
|
740
322
|
}
|
|
741
|
-
catch (
|
|
323
|
+
catch (_f) { }
|
|
742
324
|
if (vectorDb) {
|
|
743
325
|
try {
|
|
744
326
|
yield vectorDb.close();
|
package/dist/commands/similar.js
CHANGED
|
@@ -73,6 +73,7 @@ exports.similar = new commander_1.Command("similar")
|
|
|
73
73
|
return;
|
|
74
74
|
const projectRoot = (_a = (0, project_root_1.findProjectRoot)(root)) !== null && _a !== void 0 ? _a : root;
|
|
75
75
|
(0, stale_hint_1.maybeWarnStaleChunker)(projectRoot, { agent: opts.agent });
|
|
76
|
+
(0, stale_hint_1.maybeWarnStaleEmbedding)(projectRoot, { agent: opts.agent });
|
|
76
77
|
const paths = (0, project_root_1.ensureProjectPaths)(projectRoot);
|
|
77
78
|
vectorDb = new vector_db_1.VectorDB(paths.lancedbDir);
|
|
78
79
|
const table = yield vectorDb.ensureTable();
|
|
@@ -69,6 +69,7 @@ exports.testFind = new commander_1.Command("test")
|
|
|
69
69
|
return;
|
|
70
70
|
const projectRoot = (_a = (0, project_root_1.findProjectRoot)(root)) !== null && _a !== void 0 ? _a : root;
|
|
71
71
|
(0, stale_hint_1.maybeWarnStaleChunker)(projectRoot, { agent: opts.agent });
|
|
72
|
+
(0, stale_hint_1.maybeWarnStaleEmbedding)(projectRoot, { agent: opts.agent });
|
|
72
73
|
const paths = (0, project_root_1.ensureProjectPaths)(projectRoot);
|
|
73
74
|
vectorDb = new vector_db_1.VectorDB(paths.lancedbDir);
|
|
74
75
|
const { symbols, resolvedAsFile } = yield (0, impact_1.resolveTargetSymbols)(target, vectorDb, projectRoot);
|
package/dist/commands/trace.js
CHANGED
|
@@ -185,6 +185,7 @@ exports.trace = new commander_1.Command("trace")
|
|
|
185
185
|
try {
|
|
186
186
|
const projectRoot = (_a = (0, project_root_1.findProjectRoot)(root)) !== null && _a !== void 0 ? _a : root;
|
|
187
187
|
(0, stale_hint_1.maybeWarnStaleChunker)(projectRoot, { agent: opts.agent });
|
|
188
|
+
(0, stale_hint_1.maybeWarnStaleEmbedding)(projectRoot, { agent: opts.agent });
|
|
188
189
|
const paths = (0, project_root_1.ensureProjectPaths)(projectRoot);
|
|
189
190
|
vectorDb = new vector_db_1.VectorDB(paths.lancedbDir);
|
|
190
191
|
const { resolveScope } = yield Promise.resolve().then(() => __importStar(require("../lib/utils/scope-filter")));
|
package/dist/config.js
CHANGED
|
@@ -35,6 +35,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.INDEXABLE_EXTENSIONS = exports.FRAGMENT_COMPACT_THRESHOLD = exports.DISK_LOW_BYTES = exports.DISK_CRITICAL_BYTES = exports.MAX_FILE_SIZE_BYTES = exports.PATHS = exports.MAX_WORKER_MEMORY_MB = exports.WORKER_BOOT_TIMEOUT_MS = exports.WORKER_TIMEOUT_MS = exports.CHUNKER_VERSION_HISTORY = exports.CONFIG = exports.MODEL_IDS = exports.DEFAULT_MODEL_TIER = exports.MODEL_TIERS = void 0;
|
|
37
37
|
exports.describeChunkerGap = describeChunkerGap;
|
|
38
|
+
exports.describeEmbeddingGap = describeEmbeddingGap;
|
|
38
39
|
const os = __importStar(require("node:os"));
|
|
39
40
|
const path = __importStar(require("node:path"));
|
|
40
41
|
exports.MODEL_TIERS = {
|
|
@@ -123,6 +124,39 @@ function describeChunkerGap(indexedVersion) {
|
|
|
123
124
|
notes: missed.map((h) => h.note),
|
|
124
125
|
};
|
|
125
126
|
}
|
|
127
|
+
/**
|
|
128
|
+
* Describe the gap between an index's stored embedding identity (model tier +
|
|
129
|
+
* vector dim) and the current global config, or null when they already agree.
|
|
130
|
+
*
|
|
131
|
+
* A dimension change is `breaking`: vectors of differing widths cannot be
|
|
132
|
+
* compared, and the single fixed-dim `chunks` table silently pads/truncates a
|
|
133
|
+
* mismatched query embedding, yielding meaningless scores. A same-width model
|
|
134
|
+
* swap is `additive`: the stored vectors are a different model's output but the
|
|
135
|
+
* same width, so search keeps working with mixed-model quality until a re-embed.
|
|
136
|
+
*
|
|
137
|
+
* Mirrors describeChunkerGap so `gmax doctor` and the query-time hint render the
|
|
138
|
+
* same severity. Pure over MODEL_TIERS — callers pass the current identity (read
|
|
139
|
+
* from the global config) so this module stays free of config I/O.
|
|
140
|
+
*/
|
|
141
|
+
function describeEmbeddingGap(stored, current) {
|
|
142
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
143
|
+
const fromModel = (_a = stored.modelTier) !== null && _a !== void 0 ? _a : exports.DEFAULT_MODEL_TIER;
|
|
144
|
+
const toModel = (_b = current.modelTier) !== null && _b !== void 0 ? _b : exports.DEFAULT_MODEL_TIER;
|
|
145
|
+
const fromDim = (_e = (_c = stored.vectorDim) !== null && _c !== void 0 ? _c : (_d = exports.MODEL_TIERS[fromModel]) === null || _d === void 0 ? void 0 : _d.vectorDim) !== null && _e !== void 0 ? _e : exports.CONFIG.VECTOR_DIM;
|
|
146
|
+
const toDim = (_h = (_f = current.vectorDim) !== null && _f !== void 0 ? _f : (_g = exports.MODEL_TIERS[toModel]) === null || _g === void 0 ? void 0 : _g.vectorDim) !== null && _h !== void 0 ? _h : exports.CONFIG.VECTOR_DIM;
|
|
147
|
+
const dimChanged = fromDim !== toDim;
|
|
148
|
+
const modelChanged = fromModel !== toModel;
|
|
149
|
+
if (!dimChanged && !modelChanged)
|
|
150
|
+
return null;
|
|
151
|
+
return {
|
|
152
|
+
fromModel,
|
|
153
|
+
toModel,
|
|
154
|
+
fromDim,
|
|
155
|
+
toDim,
|
|
156
|
+
dimChanged,
|
|
157
|
+
severity: dimChanged ? "breaking" : "additive",
|
|
158
|
+
};
|
|
159
|
+
}
|
|
126
160
|
exports.WORKER_TIMEOUT_MS = Number.parseInt(process.env.GMAX_WORKER_TIMEOUT_MS || "60000", 10);
|
|
127
161
|
exports.WORKER_BOOT_TIMEOUT_MS = Number.parseInt(process.env.GMAX_WORKER_BOOT_TIMEOUT_MS || "300000", 10);
|
|
128
162
|
exports.MAX_WORKER_MEMORY_MB = (() => {
|