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
|
@@ -0,0 +1,531 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
36
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
37
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
38
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
39
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
40
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
41
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
|
+
exports.executeServerSearch = executeServerSearch;
|
|
46
|
+
exports.renderSearchOutput = renderSearchOutput;
|
|
47
|
+
const path = __importStar(require("node:path"));
|
|
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
|
+
const cross_project_1 = require("../lib/utils/cross-project");
|
|
52
|
+
const formatter_1 = require("../lib/utils/formatter");
|
|
53
|
+
const import_extractor_1 = require("../lib/utils/import-extractor");
|
|
54
|
+
const search_skeletons_1 = require("./search-skeletons");
|
|
55
|
+
/**
|
|
56
|
+
* Standalone HTTP-server search path. The per-project server answers the query
|
|
57
|
+
* over HTTP and returns JSON; we render it here with the same presentation
|
|
58
|
+
* modes as the local path (minus the modes the server can't precompute).
|
|
59
|
+
*
|
|
60
|
+
* Returns `true` when the server answered and rendering is complete (the caller
|
|
61
|
+
* should return without touching the local path); `false` when the request
|
|
62
|
+
* failed (`!response.ok` or a thrown error) and the caller should fall back to
|
|
63
|
+
* the in-process / daemon-mediated path.
|
|
64
|
+
*
|
|
65
|
+
* Mirrors the original action's structure: a handled server search returns
|
|
66
|
+
* BEFORE the command's main try/finally, so it intentionally skips query
|
|
67
|
+
* logging and gracefulExit.
|
|
68
|
+
*/
|
|
69
|
+
function executeServerSearch(params) {
|
|
70
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
71
|
+
const { server, pattern, exec_path, projectRootForServer, options, minScore, } = params;
|
|
72
|
+
try {
|
|
73
|
+
const response = yield fetch(`http://localhost:${server.port}/search`, {
|
|
74
|
+
method: "POST",
|
|
75
|
+
headers: { "Content-Type": "application/json" },
|
|
76
|
+
body: JSON.stringify({
|
|
77
|
+
query: pattern,
|
|
78
|
+
limit: parseInt(options.m, 10),
|
|
79
|
+
path: exec_path
|
|
80
|
+
? path.relative(projectRootForServer, path.resolve(exec_path))
|
|
81
|
+
: undefined,
|
|
82
|
+
}),
|
|
83
|
+
});
|
|
84
|
+
if (response.ok) {
|
|
85
|
+
const body = (yield response.json());
|
|
86
|
+
const searchResult = { data: body.results };
|
|
87
|
+
const filteredData = searchResult.data.filter((r) => typeof r.score !== "number" || r.score >= minScore);
|
|
88
|
+
if (options.skeleton) {
|
|
89
|
+
yield (0, search_skeletons_1.outputSkeletons)(filteredData, projectRootForServer, parseInt(options.m, 10),
|
|
90
|
+
// Server doesn't easily expose DB instance here in HTTP client mode,
|
|
91
|
+
// but we are in client. Wait, this text implies "Server Search" block.
|
|
92
|
+
// Client talks to server. The server returns JSON.
|
|
93
|
+
// We don't have DB access here.
|
|
94
|
+
// So we pass null, and it will fallback to generating local skeleton (if file exists locally).
|
|
95
|
+
// This is acceptable for Phase 3.
|
|
96
|
+
null);
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
const compactHits = options.compact ? (0, compact_results_1.toCompactHits)(filteredData) : [];
|
|
100
|
+
if (options.compact) {
|
|
101
|
+
if (!compactHits.length) {
|
|
102
|
+
console.log("No matches found.");
|
|
103
|
+
console.log("\nTry: broaden your query, use fewer keywords, or check `gmax status` to verify the project is indexed.");
|
|
104
|
+
process.exitCode = 1;
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
console.log((0, compact_results_1.formatCompactTable)(compactHits, projectRootForServer, pattern, {
|
|
108
|
+
isTTY: !!process.stdout.isTTY,
|
|
109
|
+
plain: !!options.plain,
|
|
110
|
+
}));
|
|
111
|
+
}
|
|
112
|
+
return true; // EXIT
|
|
113
|
+
}
|
|
114
|
+
if (!filteredData.length) {
|
|
115
|
+
console.log("No matches found.");
|
|
116
|
+
console.log("\nTry: broaden your query, use fewer keywords, or check `gmax status` to verify the project is indexed.");
|
|
117
|
+
process.exitCode = 1;
|
|
118
|
+
return true; // EXIT
|
|
119
|
+
}
|
|
120
|
+
if (options.agent) {
|
|
121
|
+
const importCache = new Map();
|
|
122
|
+
const getImportsForFile = (absPath) => {
|
|
123
|
+
var _a;
|
|
124
|
+
if (!options.imports || !absPath)
|
|
125
|
+
return "";
|
|
126
|
+
if (!importCache.has(absPath)) {
|
|
127
|
+
importCache.set(absPath, (0, import_extractor_1.extractImports)(absPath));
|
|
128
|
+
}
|
|
129
|
+
return (_a = importCache.get(absPath)) !== null && _a !== void 0 ? _a : "";
|
|
130
|
+
};
|
|
131
|
+
console.log((0, agent_search_formatter_1.formatAgentSearchResults)(filteredData, projectRootForServer, {
|
|
132
|
+
includeImports: options.imports,
|
|
133
|
+
query: pattern,
|
|
134
|
+
getImportsForFile,
|
|
135
|
+
explain: options.explain,
|
|
136
|
+
}));
|
|
137
|
+
return true; // EXIT
|
|
138
|
+
}
|
|
139
|
+
const isTTY = process.stdout.isTTY;
|
|
140
|
+
const shouldBePlain = options.plain || !isTTY;
|
|
141
|
+
if (!options.agent && !options.compact) {
|
|
142
|
+
console.log((0, compact_results_1.resultCountHeader)(filteredData, parseInt(options.m, 10)));
|
|
143
|
+
console.log();
|
|
144
|
+
}
|
|
145
|
+
if (shouldBePlain) {
|
|
146
|
+
const mappedResults = (0, compact_results_1.toTextResults)(filteredData);
|
|
147
|
+
const output = (0, formatter_1.formatTextResults)(mappedResults, pattern, projectRootForServer, {
|
|
148
|
+
isPlain: true,
|
|
149
|
+
compact: options.compact,
|
|
150
|
+
content: options.content,
|
|
151
|
+
perFile: parseInt(options.perFile, 10),
|
|
152
|
+
showScores: options.scores,
|
|
153
|
+
});
|
|
154
|
+
console.log(output);
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
const { formatResults } = yield Promise.resolve().then(() => __importStar(require("../lib/output/formatter")));
|
|
158
|
+
const output = formatResults(filteredData, projectRootForServer, {
|
|
159
|
+
content: options.content,
|
|
160
|
+
explain: options.explain,
|
|
161
|
+
});
|
|
162
|
+
console.log(output);
|
|
163
|
+
}
|
|
164
|
+
return true; // EXIT successful server search
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
catch (e) {
|
|
168
|
+
if (process.env.DEBUG) {
|
|
169
|
+
console.error("[search] server request failed, falling back to local:", e);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return false;
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Presentation stage shared by the daemon-mediated and in-process search paths.
|
|
177
|
+
* Applies the min-score + name-regex post-filters, then dispatches to one of the
|
|
178
|
+
* seven presentation modes (cross-project, agent, skeleton, no-results, compact,
|
|
179
|
+
* context-for-llm, standard). Writes directly to stdout/stderr and may set
|
|
180
|
+
* `process.exitCode`. Returns the post-filter result count for query logging.
|
|
181
|
+
*/
|
|
182
|
+
function renderSearchOutput(params) {
|
|
183
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
184
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
185
|
+
const { searchResult, options, minScore, crossProject, pattern, effectiveRoot, projectRoot, vectorDb, precomputedSkeletons, precomputedGraph, indexState, } = params;
|
|
186
|
+
if (!options.agent && ((_a = searchResult.warnings) === null || _a === void 0 ? void 0 : _a.length)) {
|
|
187
|
+
for (const w of searchResult.warnings) {
|
|
188
|
+
console.warn(`Warning: ${w}`);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
// Partial-index signal (Phase 6): when the index is mid-catchup, results
|
|
192
|
+
// may be incomplete. Non-agent renders it now as a warning; agent mode
|
|
193
|
+
// appends a machine-readable footer after the results below.
|
|
194
|
+
if (!options.agent) {
|
|
195
|
+
const footer = (0, index_state_footer_1.formatIndexStateFooter)(indexState, { agent: false });
|
|
196
|
+
if (footer)
|
|
197
|
+
console.warn(footer);
|
|
198
|
+
}
|
|
199
|
+
let filteredData = searchResult.data.filter((r) => typeof r.score !== "number" || r.score >= minScore);
|
|
200
|
+
// Post-filter by symbol name regex
|
|
201
|
+
if (options.name) {
|
|
202
|
+
try {
|
|
203
|
+
const regex = new RegExp(options.name, "i");
|
|
204
|
+
filteredData = filteredData.filter((r) => {
|
|
205
|
+
const defs = Array.isArray(r.defined_symbols) ? r.defined_symbols : [];
|
|
206
|
+
return defs.some((d) => regex.test(d));
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
catch (_h) {
|
|
210
|
+
// Invalid regex — skip
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
// Build import cache when --imports is requested
|
|
214
|
+
const importCache = new Map();
|
|
215
|
+
const getImportsForFile = (absPath) => {
|
|
216
|
+
var _a;
|
|
217
|
+
if (!options.imports || !absPath)
|
|
218
|
+
return "";
|
|
219
|
+
if (!importCache.has(absPath)) {
|
|
220
|
+
importCache.set(absPath, (0, import_extractor_1.extractImports)(absPath));
|
|
221
|
+
}
|
|
222
|
+
return (_a = importCache.get(absPath)) !== null && _a !== void 0 ? _a : "";
|
|
223
|
+
};
|
|
224
|
+
// Agent mode: ultra-compact one-line-per-result output
|
|
225
|
+
const resultCount = filteredData.length;
|
|
226
|
+
// Cross-project (Phase 6): render grouped by owning project so idioms
|
|
227
|
+
// from different stacks don't blur into one flat list. Only the
|
|
228
|
+
// string-formatter modes reach here — skeleton/context-for-llm/symbol
|
|
229
|
+
// were rejected up front.
|
|
230
|
+
if (crossProject.active) {
|
|
231
|
+
const emitFooter = () => {
|
|
232
|
+
const footer = (0, index_state_footer_1.formatIndexStateFooter)(indexState, {
|
|
233
|
+
agent: !!options.agent,
|
|
234
|
+
});
|
|
235
|
+
if (footer) {
|
|
236
|
+
if (options.agent)
|
|
237
|
+
console.log(footer);
|
|
238
|
+
else
|
|
239
|
+
console.warn(footer);
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
if (!filteredData.length) {
|
|
243
|
+
console.log(options.agent ? "(none)" : "No matches found.");
|
|
244
|
+
process.exitCode = 1;
|
|
245
|
+
emitFooter();
|
|
246
|
+
return { resultCount };
|
|
247
|
+
}
|
|
248
|
+
const getPath = (r) => {
|
|
249
|
+
var _a, _b, _c;
|
|
250
|
+
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 : "");
|
|
251
|
+
};
|
|
252
|
+
const groups = (0, cross_project_1.groupResultsByProject)(filteredData, crossProject.roots, getPath);
|
|
253
|
+
const isTTY = process.stdout.isTTY;
|
|
254
|
+
const shouldBePlain = options.plain || !isTTY;
|
|
255
|
+
const blocks = [];
|
|
256
|
+
for (const g of groups) {
|
|
257
|
+
let body;
|
|
258
|
+
if (options.agent) {
|
|
259
|
+
body = (0, agent_search_formatter_1.formatAgentSearchResults)(g.items, g.root, {
|
|
260
|
+
includeImports: options.imports,
|
|
261
|
+
query: pattern,
|
|
262
|
+
getImportsForFile,
|
|
263
|
+
explain: options.explain,
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
else if (options.compact) {
|
|
267
|
+
body = (0, compact_results_1.formatCompactTable)((0, compact_results_1.toCompactHits)(g.items), g.root, pattern, {
|
|
268
|
+
isTTY: !!isTTY,
|
|
269
|
+
plain: !!options.plain,
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
else if (shouldBePlain) {
|
|
273
|
+
body = (0, formatter_1.formatTextResults)((0, compact_results_1.toTextResults)(g.items), pattern, g.root, {
|
|
274
|
+
isPlain: true,
|
|
275
|
+
compact: options.compact,
|
|
276
|
+
content: options.content,
|
|
277
|
+
perFile: parseInt(options.perFile, 10),
|
|
278
|
+
showScores: options.scores,
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
else {
|
|
282
|
+
const { formatResults } = yield Promise.resolve().then(() => __importStar(require("../lib/output/formatter")));
|
|
283
|
+
body = formatResults(g.items, g.root, {
|
|
284
|
+
content: options.content,
|
|
285
|
+
explain: options.explain,
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
const header = options.agent
|
|
289
|
+
? `## ${g.name} (${g.items.length})`
|
|
290
|
+
: `=== ${g.name} (${g.items.length}) ===`;
|
|
291
|
+
blocks.push(`${header}\n${body}`);
|
|
292
|
+
}
|
|
293
|
+
console.log(blocks.join("\n\n"));
|
|
294
|
+
emitFooter();
|
|
295
|
+
return { resultCount };
|
|
296
|
+
}
|
|
297
|
+
if (options.agent) {
|
|
298
|
+
if (!filteredData.length) {
|
|
299
|
+
console.log("(none)");
|
|
300
|
+
process.exitCode = 1;
|
|
301
|
+
}
|
|
302
|
+
else {
|
|
303
|
+
console.log((0, agent_search_formatter_1.formatAgentSearchResults)(filteredData, effectiveRoot, {
|
|
304
|
+
includeImports: options.imports,
|
|
305
|
+
query: pattern,
|
|
306
|
+
getImportsForFile,
|
|
307
|
+
explain: options.explain,
|
|
308
|
+
}));
|
|
309
|
+
}
|
|
310
|
+
// Agent trace (compact)
|
|
311
|
+
if (options.symbol && filteredData.length > 0) {
|
|
312
|
+
try {
|
|
313
|
+
let graph = precomputedGraph;
|
|
314
|
+
if (!graph) {
|
|
315
|
+
if (!vectorDb)
|
|
316
|
+
throw new Error("no graph source");
|
|
317
|
+
const { GraphBuilder } = yield Promise.resolve().then(() => __importStar(require("../lib/graph/graph-builder")));
|
|
318
|
+
const builder = new GraphBuilder(vectorDb, effectiveRoot);
|
|
319
|
+
graph = yield builder.buildGraphMultiHop(pattern, 1);
|
|
320
|
+
}
|
|
321
|
+
if (graph === null || graph === void 0 ? void 0 : graph.center) {
|
|
322
|
+
console.log("---");
|
|
323
|
+
for (const t of graph.callerTree) {
|
|
324
|
+
const rel = t.node.file.startsWith(effectiveRoot)
|
|
325
|
+
? t.node.file.slice(effectiveRoot.length + 1)
|
|
326
|
+
: t.node.file;
|
|
327
|
+
console.log(`<- ${t.node.symbol} ${rel}:${t.node.line + 1}`);
|
|
328
|
+
}
|
|
329
|
+
for (const c of graph.callees.slice(0, 10)) {
|
|
330
|
+
if (c.file) {
|
|
331
|
+
const rel = c.file.startsWith(effectiveRoot)
|
|
332
|
+
? c.file.slice(effectiveRoot.length + 1)
|
|
333
|
+
: c.file;
|
|
334
|
+
console.log(`-> ${c.symbol} ${rel}:${c.line + 1}`);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
catch (_j) { }
|
|
340
|
+
}
|
|
341
|
+
// Partial-index footer last, so it's the final line the agent reads —
|
|
342
|
+
// and emitted even on "(none)", where an empty result may just mean the
|
|
343
|
+
// relevant files aren't indexed yet.
|
|
344
|
+
const footer = (0, index_state_footer_1.formatIndexStateFooter)(indexState, { agent: true });
|
|
345
|
+
if (footer)
|
|
346
|
+
console.log(footer);
|
|
347
|
+
return { resultCount };
|
|
348
|
+
}
|
|
349
|
+
if (options.skeleton) {
|
|
350
|
+
yield (0, search_skeletons_1.outputSkeletons)(filteredData, projectRoot, parseInt(options.m, 10), vectorDb, precomputedSkeletons);
|
|
351
|
+
return { resultCount };
|
|
352
|
+
}
|
|
353
|
+
if (!filteredData.length) {
|
|
354
|
+
console.log("No matches found.");
|
|
355
|
+
console.log("\nTry: broaden your query, use fewer keywords, or check `gmax status` to verify the project is indexed.");
|
|
356
|
+
process.exitCode = 1;
|
|
357
|
+
return { resultCount };
|
|
358
|
+
}
|
|
359
|
+
if (options.compact) {
|
|
360
|
+
const compactHits = (0, compact_results_1.toCompactHits)(filteredData);
|
|
361
|
+
console.log((0, compact_results_1.formatCompactTable)(compactHits, projectRoot, pattern, {
|
|
362
|
+
isTTY: !!process.stdout.isTTY,
|
|
363
|
+
plain: !!options.plain,
|
|
364
|
+
}));
|
|
365
|
+
return { resultCount };
|
|
366
|
+
}
|
|
367
|
+
// Context-for-LLM mode: full function body + imports per result
|
|
368
|
+
if (options.contextForLlm) {
|
|
369
|
+
const fs = yield Promise.resolve().then(() => __importStar(require("node:fs")));
|
|
370
|
+
const { extractImportsFromContent } = yield Promise.resolve().then(() => __importStar(require("../lib/utils/import-extractor")));
|
|
371
|
+
const { packByBudget } = yield Promise.resolve().then(() => __importStar(require("../lib/utils/budget-pack")));
|
|
372
|
+
const budget = parseInt(options.budget, 10) || 8000;
|
|
373
|
+
console.log((0, compact_results_1.resultCountHeader)(filteredData, parseInt(options.m, 10)));
|
|
374
|
+
// Build every candidate blob up front (token cost needs the rendered
|
|
375
|
+
// text), then pack to budget. Token-aware packing skips an oversized
|
|
376
|
+
// chunk and keeps filling with smaller, still-relevant ones rather than
|
|
377
|
+
// aborting the loop — recovering budget the old greedy `break` wasted.
|
|
378
|
+
const candidates = filteredData.map((r, idx) => {
|
|
379
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
|
|
380
|
+
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 : "";
|
|
381
|
+
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;
|
|
382
|
+
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;
|
|
383
|
+
const relPath = absP.startsWith(projectRoot)
|
|
384
|
+
? absP.slice(projectRoot.length + 1)
|
|
385
|
+
: absP;
|
|
386
|
+
const role = r.role || "IMPLEMENTATION";
|
|
387
|
+
const symbol = Array.isArray(r.defined_symbols) &&
|
|
388
|
+
r.defined_symbols.length > 0
|
|
389
|
+
? r.defined_symbols[0]
|
|
390
|
+
: "";
|
|
391
|
+
let blobText;
|
|
392
|
+
try {
|
|
393
|
+
const content = fs.readFileSync(absP, "utf-8");
|
|
394
|
+
const allLines = content.split("\n");
|
|
395
|
+
const body = allLines
|
|
396
|
+
.slice(startLine, Math.min(endLine + 1, allLines.length))
|
|
397
|
+
.join("\n");
|
|
398
|
+
const imports = extractImportsFromContent(content);
|
|
399
|
+
const blob = [
|
|
400
|
+
`--- ${relPath}:${startLine + 1}${symbol ? ` ${symbol}` : ""} [${role}] ---`,
|
|
401
|
+
];
|
|
402
|
+
if (imports)
|
|
403
|
+
blob.push("[imports]", imports, "");
|
|
404
|
+
blob.push("[body]", body);
|
|
405
|
+
blobText = blob.join("\n");
|
|
406
|
+
}
|
|
407
|
+
catch (_m) {
|
|
408
|
+
blobText = `--- ${relPath} (file not readable) ---`;
|
|
409
|
+
}
|
|
410
|
+
// Preserve relevance order when scores are absent (rank-derived
|
|
411
|
+
// fallback) so the density tiebreaker never reshuffles arbitrarily.
|
|
412
|
+
const score = typeof r.score === "number"
|
|
413
|
+
? r.score
|
|
414
|
+
: (filteredData.length - idx) / filteredData.length;
|
|
415
|
+
return { blobText, tokens: Math.ceil(blobText.length / 4), score };
|
|
416
|
+
});
|
|
417
|
+
const pack = packByBudget(candidates.map((c) => ({ tokens: c.tokens, score: c.score })), budget);
|
|
418
|
+
for (const i of pack.selected) {
|
|
419
|
+
console.log(`\n${candidates[i].blobText}`);
|
|
420
|
+
}
|
|
421
|
+
if (pack.dropped > 0) {
|
|
422
|
+
console.log(`\n(budget: ~${pack.tokensUsed}/${budget} tokens, ${pack.dropped} lower-density result${pack.dropped > 1 ? "s" : ""} not shown)`);
|
|
423
|
+
}
|
|
424
|
+
return { resultCount };
|
|
425
|
+
}
|
|
426
|
+
const isTTY = process.stdout.isTTY;
|
|
427
|
+
const shouldBePlain = options.plain || !isTTY;
|
|
428
|
+
if (!options.agent && !options.compact) {
|
|
429
|
+
console.log((0, compact_results_1.resultCountHeader)(filteredData, parseInt(options.m, 10)));
|
|
430
|
+
console.log();
|
|
431
|
+
}
|
|
432
|
+
// Print imports per unique file before results when --imports is used
|
|
433
|
+
if (options.imports) {
|
|
434
|
+
const seenFiles = new Set();
|
|
435
|
+
for (const r of filteredData) {
|
|
436
|
+
const absP = (_d = (_b = r.path) !== null && _b !== void 0 ? _b : (_c = r.metadata) === null || _c === void 0 ? void 0 : _c.path) !== null && _d !== void 0 ? _d : "";
|
|
437
|
+
if (absP && !seenFiles.has(absP)) {
|
|
438
|
+
seenFiles.add(absP);
|
|
439
|
+
const imports = getImportsForFile(absP);
|
|
440
|
+
if (imports) {
|
|
441
|
+
const relP = absP.startsWith(effectiveRoot)
|
|
442
|
+
? absP.slice(effectiveRoot.length + 1)
|
|
443
|
+
: absP;
|
|
444
|
+
console.log(`--- imports: ${relP} ---\n${imports}\n`);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
if (shouldBePlain) {
|
|
450
|
+
const mappedResults = (0, compact_results_1.toTextResults)(filteredData);
|
|
451
|
+
const output = (0, formatter_1.formatTextResults)(mappedResults, pattern, projectRoot, {
|
|
452
|
+
isPlain: true,
|
|
453
|
+
compact: options.compact,
|
|
454
|
+
content: options.content,
|
|
455
|
+
perFile: parseInt(options.perFile, 10),
|
|
456
|
+
showScores: options.scores,
|
|
457
|
+
});
|
|
458
|
+
console.log(output);
|
|
459
|
+
if (options.explain) {
|
|
460
|
+
for (const r of filteredData) {
|
|
461
|
+
const b = r.scoreBreakdown;
|
|
462
|
+
if (b) {
|
|
463
|
+
const absP = (_g = (_e = r.path) !== null && _e !== void 0 ? _e : (_f = r.metadata) === null || _f === void 0 ? void 0 : _f.path) !== null && _g !== void 0 ? _g : "";
|
|
464
|
+
const relPath = absP.startsWith(projectRoot)
|
|
465
|
+
? absP.slice(projectRoot.length + 1)
|
|
466
|
+
: absP;
|
|
467
|
+
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)}`);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
else {
|
|
473
|
+
// Use new holographic formatter for TTY
|
|
474
|
+
const { formatResults } = yield Promise.resolve().then(() => __importStar(require("../lib/output/formatter")));
|
|
475
|
+
const output = formatResults(filteredData, projectRoot, {
|
|
476
|
+
content: options.content,
|
|
477
|
+
explain: options.explain,
|
|
478
|
+
});
|
|
479
|
+
console.log(output);
|
|
480
|
+
}
|
|
481
|
+
// Symbol mode: append call graph
|
|
482
|
+
if (options.symbol) {
|
|
483
|
+
try {
|
|
484
|
+
let graph = precomputedGraph;
|
|
485
|
+
if (!graph) {
|
|
486
|
+
if (!vectorDb)
|
|
487
|
+
throw new Error("no graph source");
|
|
488
|
+
const { GraphBuilder } = yield Promise.resolve().then(() => __importStar(require("../lib/graph/graph-builder")));
|
|
489
|
+
const builder = new GraphBuilder(vectorDb, effectiveRoot);
|
|
490
|
+
graph = yield builder.buildGraphMultiHop(pattern, 1);
|
|
491
|
+
}
|
|
492
|
+
if (graph === null || graph === void 0 ? void 0 : graph.center) {
|
|
493
|
+
const lines = ["\n--- Call graph ---"];
|
|
494
|
+
const centerRel = path.relative(effectiveRoot, graph.center.file);
|
|
495
|
+
lines.push(`${graph.center.symbol} [${graph.center.role}] ${centerRel}:${graph.center.line + 1}`);
|
|
496
|
+
if (graph.importers.length > 0) {
|
|
497
|
+
const filtered = graph.importers.filter((p) => p !== graph.center.file);
|
|
498
|
+
if (filtered.length > 0) {
|
|
499
|
+
lines.push("Imported by:");
|
|
500
|
+
for (const imp of filtered.slice(0, 10)) {
|
|
501
|
+
lines.push(` ${path.relative(effectiveRoot, imp)}`);
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
if (graph.callerTree.length > 0) {
|
|
506
|
+
lines.push("Callers:");
|
|
507
|
+
for (const t of graph.callerTree) {
|
|
508
|
+
lines.push(` <- ${t.node.symbol} ${path.relative(effectiveRoot, t.node.file)}:${t.node.line + 1}`);
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
if (graph.callees.length > 0) {
|
|
512
|
+
lines.push("Calls:");
|
|
513
|
+
for (const c of graph.callees.slice(0, 15)) {
|
|
514
|
+
if (c.file) {
|
|
515
|
+
lines.push(` -> ${c.symbol} ${path.relative(effectiveRoot, c.file)}:${c.line + 1}`);
|
|
516
|
+
}
|
|
517
|
+
else {
|
|
518
|
+
lines.push(` -> ${c.symbol} (not indexed)`);
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
console.log(lines.join("\n"));
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
catch (_k) {
|
|
526
|
+
// Trace failed — skip silently
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
return { resultCount };
|
|
530
|
+
});
|
|
531
|
+
}
|