grepmax 0.17.21 → 0.17.23
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/add.js +3 -2
- package/dist/commands/audit.js +19 -2
- package/dist/commands/context.js +34 -2
- package/dist/commands/doctor.js +18 -0
- package/dist/commands/impact.js +24 -14
- package/dist/commands/index.js +16 -6
- package/dist/commands/mcp.js +2 -0
- package/dist/commands/peek.js +94 -17
- package/dist/commands/project.js +17 -4
- package/dist/commands/related.js +5 -3
- package/dist/commands/search-run.js +227 -0
- package/dist/commands/search-skeletons.js +133 -0
- package/dist/commands/search.js +46 -450
- package/dist/commands/test-find.js +8 -14
- package/dist/commands/trace.js +2 -29
- package/dist/config.js +5 -0
- package/dist/eval-like-limit-probe.js +62 -0
- package/dist/lib/daemon/daemon.js +15 -102
- package/dist/lib/daemon/search-handler.js +159 -0
- package/dist/lib/graph/callsites.js +148 -0
- package/dist/lib/graph/graph-builder.js +7 -3
- package/dist/lib/graph/impact.js +24 -11
- package/dist/lib/graph/test-hits.js +54 -0
- package/dist/lib/help/agent-cheatsheet.js +5 -0
- package/dist/lib/index/chunker.js +36 -2
- package/dist/lib/output/agent-search-formatter.js +59 -3
- package/dist/lib/output/compact-results.js +244 -0
- package/dist/lib/search/searcher.js +2 -1
- package/dist/lib/utils/query-timeout.js +52 -0
- package/package.json +2 -2
- package/plugins/grepmax/.claude-plugin/plugin.json +1 -1
package/dist/commands/add.js
CHANGED
|
@@ -47,6 +47,7 @@ const fs = __importStar(require("node:fs"));
|
|
|
47
47
|
const os = __importStar(require("node:os"));
|
|
48
48
|
const path = __importStar(require("node:path"));
|
|
49
49
|
const commander_1 = require("commander");
|
|
50
|
+
const config_1 = require("../config");
|
|
50
51
|
const grammar_loader_1 = require("../lib/index/grammar-loader");
|
|
51
52
|
const index_config_1 = require("../lib/index/index-config");
|
|
52
53
|
const sync_helpers_1 = require("../lib/index/sync-helpers");
|
|
@@ -218,7 +219,7 @@ Examples:
|
|
|
218
219
|
if (!done.ok) {
|
|
219
220
|
throw new Error((_c = done.error) !== null && _c !== void 0 ? _c : "daemon add failed");
|
|
220
221
|
}
|
|
221
|
-
(0, project_registry_1.registerProject)(Object.assign(Object.assign({}, pendingEntry), { lastIndexed: new Date().toISOString(), chunkCount: (_d = done.indexed) !== null && _d !== void 0 ? _d : 0, status: "indexed" }));
|
|
222
|
+
(0, project_registry_1.registerProject)(Object.assign(Object.assign({}, pendingEntry), { lastIndexed: new Date().toISOString(), chunkCount: (_d = done.indexed) !== null && _d !== void 0 ? _d : 0, status: "indexed", chunkerVersion: config_1.CONFIG.CHUNKER_VERSION }));
|
|
222
223
|
const failedFiles = (_e = done.failedFiles) !== null && _e !== void 0 ? _e : 0;
|
|
223
224
|
const failedSuffix = failedFiles > 0 ? ` · ${failedFiles} failed` : "";
|
|
224
225
|
spinner.succeed(`Added ${projectName} (${done.total} files, ${done.indexed} chunks${failedSuffix})`);
|
|
@@ -239,7 +240,7 @@ Examples:
|
|
|
239
240
|
projectRoot,
|
|
240
241
|
onProgress,
|
|
241
242
|
});
|
|
242
|
-
(0, project_registry_1.registerProject)(Object.assign(Object.assign({}, pendingEntry), { lastIndexed: new Date().toISOString(), chunkCount: result.indexed, status: "indexed" }));
|
|
243
|
+
(0, project_registry_1.registerProject)(Object.assign(Object.assign({}, pendingEntry), { lastIndexed: new Date().toISOString(), chunkCount: result.indexed, status: "indexed", chunkerVersion: config_1.CONFIG.CHUNKER_VERSION }));
|
|
243
244
|
const failedSuffix = result.failedFiles > 0 ? ` · ${result.failedFiles} failed` : "";
|
|
244
245
|
spinner.succeed(`Added ${projectName} (${result.total} files, ${result.indexed} chunks${failedSuffix})`);
|
|
245
246
|
}
|
package/dist/commands/audit.js
CHANGED
|
@@ -45,6 +45,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
45
45
|
exports.audit = void 0;
|
|
46
46
|
exports.computeAudit = computeAudit;
|
|
47
47
|
const commander_1 = require("commander");
|
|
48
|
+
const callsites_1 = require("../lib/graph/callsites");
|
|
48
49
|
const arrow_1 = require("../lib/utils/arrow");
|
|
49
50
|
const exit_1 = require("../lib/utils/exit");
|
|
50
51
|
const project_registry_1 = require("../lib/utils/project-registry");
|
|
@@ -71,8 +72,12 @@ function rel(p, prefix) {
|
|
|
71
72
|
* `top` caps each list; `deadTotal` reports the full pre-cap dead count.
|
|
72
73
|
*/
|
|
73
74
|
function computeAudit(rows, prefix, top) {
|
|
75
|
+
var _a, _b;
|
|
74
76
|
// First definition of a symbol wins (matches GraphBuilder semantics).
|
|
75
77
|
const defs = new Map();
|
|
78
|
+
// Distinct files defining each name — name-based edges can't tell same-name
|
|
79
|
+
// symbols apart, so multi-file definitions get flagged in the output.
|
|
80
|
+
const defFileCounts = new Map();
|
|
76
81
|
// Distinct files that reference a symbol (cross-file inbound edges).
|
|
77
82
|
const inboundFiles = new Map();
|
|
78
83
|
const inboundTotal = new Map();
|
|
@@ -91,6 +96,9 @@ function computeAudit(rows, prefix, top) {
|
|
|
91
96
|
if (!defs.has(s)) {
|
|
92
97
|
defs.set(s, { file, line, exported, complexity: 0 });
|
|
93
98
|
}
|
|
99
|
+
if (!defFileCounts.has(s))
|
|
100
|
+
defFileCounts.set(s, new Set());
|
|
101
|
+
defFileCounts.get(s).add(file);
|
|
94
102
|
if (!fileDefs.has(file))
|
|
95
103
|
fileDefs.set(file, new Set());
|
|
96
104
|
fileDefs.get(file).add(s);
|
|
@@ -110,6 +118,10 @@ function computeAudit(rows, prefix, top) {
|
|
|
110
118
|
for (const [symbol, info] of defs) {
|
|
111
119
|
if (symbol.length < MIN_GOD_NAME_LEN)
|
|
112
120
|
continue;
|
|
121
|
+
// Builtin method names (get, set, push, …) leak in via prototype/member
|
|
122
|
+
// definitions and their inbound counts are meaningless name collisions.
|
|
123
|
+
if ((0, callsites_1.isBuiltinCallee)(symbol))
|
|
124
|
+
continue;
|
|
113
125
|
const refFiles = inboundFiles.get(symbol);
|
|
114
126
|
if (!refFiles)
|
|
115
127
|
continue;
|
|
@@ -125,6 +137,7 @@ function computeAudit(rows, prefix, top) {
|
|
|
125
137
|
line: info.line,
|
|
126
138
|
inboundFiles: external,
|
|
127
139
|
totalRefs: inboundTotal.get(symbol) || 0,
|
|
140
|
+
defFiles: (_b = (_a = defFileCounts.get(symbol)) === null || _a === void 0 ? void 0 : _a.size) !== null && _b !== void 0 ? _b : 1,
|
|
128
141
|
});
|
|
129
142
|
}
|
|
130
143
|
godNodes.sort((a, b) => b.inboundFiles - a.inboundFiles || b.totalRefs - a.totalRefs);
|
|
@@ -187,7 +200,10 @@ function formatHuman(r) {
|
|
|
187
200
|
}
|
|
188
201
|
else {
|
|
189
202
|
for (const g of r.godNodes) {
|
|
190
|
-
|
|
203
|
+
const ambiguous = g.defFiles > 1
|
|
204
|
+
? style.dim(` ~defined in ${g.defFiles} files, location is a guess`)
|
|
205
|
+
: "";
|
|
206
|
+
out.push(` ${style.cyan(g.symbol.padEnd(28))} ${style.dim(`${g.inboundFiles} files`)}, ${g.totalRefs} refs ${style.dim(`${g.file}:${g.line + 1}`)}${ambiguous}`);
|
|
191
207
|
}
|
|
192
208
|
}
|
|
193
209
|
out.push("");
|
|
@@ -224,7 +240,8 @@ function formatAgent(r) {
|
|
|
224
240
|
const lines = [];
|
|
225
241
|
lines.push(`scanned\t${r.scannedChunks}\t${r.scannedFiles}`);
|
|
226
242
|
for (const g of r.godNodes) {
|
|
227
|
-
|
|
243
|
+
const ambiguous = g.defFiles > 1 ? `\tdefs=${g.defFiles}` : "";
|
|
244
|
+
lines.push(`god\t${g.symbol}\t${g.file}:${g.line + 1}\t${g.inboundFiles}\t${g.totalRefs}${ambiguous}`);
|
|
228
245
|
}
|
|
229
246
|
for (const h of r.hubFiles) {
|
|
230
247
|
lines.push(`hub\t${h.file}\t${h.dependents}\t${h.defines}\t${h.fanOut}`);
|
package/dist/commands/context.js
CHANGED
|
@@ -43,6 +43,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
43
43
|
};
|
|
44
44
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
45
|
exports.context = void 0;
|
|
46
|
+
exports.findEnclosingSignature = findEnclosingSignature;
|
|
46
47
|
const fs = __importStar(require("node:fs"));
|
|
47
48
|
const path = __importStar(require("node:path"));
|
|
48
49
|
const commander_1 = require("commander");
|
|
@@ -82,6 +83,23 @@ function chunkEndLine(chunk) {
|
|
|
82
83
|
const start = chunkStartLine(chunk);
|
|
83
84
|
return Number((_d = (_b = (_a = chunk.end_line) !== null && _a !== void 0 ? _a : chunk.endLine) !== null && _b !== void 0 ? _b : (_c = chunk.generated_metadata) === null || _c === void 0 ? void 0 : _c.end_line) !== null && _d !== void 0 ? _d : start);
|
|
84
85
|
}
|
|
86
|
+
/**
|
|
87
|
+
* The definition line of `parentSymbol` nearest above `startLine` — used to
|
|
88
|
+
* give a mid-function sub-chunk extract its enclosing signature. Requires a
|
|
89
|
+
* definition-shaped line, not just any mention, so recursive calls or
|
|
90
|
+
* references between the definition and the chunk don't win.
|
|
91
|
+
*/
|
|
92
|
+
function findEnclosingSignature(lines, startLine, parentSymbol) {
|
|
93
|
+
if (!parentSymbol || !/^\w+$/.test(parentSymbol))
|
|
94
|
+
return null;
|
|
95
|
+
const defRe = new RegExp(`(?:\\b(?:class|function|interface|enum|struct|trait|impl|def|fn|type|const|let|var)\\b[^=]*\\b${parentSymbol}\\b|\\b${parentSymbol}\\b\\s*[(:=])`);
|
|
96
|
+
for (let i = Math.min(startLine, lines.length) - 1; i >= 0; i--) {
|
|
97
|
+
if (defRe.test(lines[i])) {
|
|
98
|
+
return { text: lines[i].trim(), line: i };
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
85
103
|
function resolveExistingPath(target, root, projectRoot) {
|
|
86
104
|
const candidates = [
|
|
87
105
|
path.isAbsolute(target) ? target : path.resolve(root, target),
|
|
@@ -196,7 +214,8 @@ exports.context = new commander_1.Command("context")
|
|
|
196
214
|
for (const r of entryPoints.slice(0, 5)) {
|
|
197
215
|
const p = chunkPath(r);
|
|
198
216
|
const line = chunkStartLine(r);
|
|
199
|
-
const
|
|
217
|
+
const parentSym = String(r.parent_symbol || "");
|
|
218
|
+
const sym = (_c = (_b = (0, arrow_1.toArr)(r.defined_symbols)) === null || _b === void 0 ? void 0 : _b[0]) !== null && _c !== void 0 ? _c : (parentSym ? `(in ${parentSym})` : "");
|
|
200
219
|
const role = String(r.role || "IMPLEMENTATION");
|
|
201
220
|
epSection.push(`${relPath(projectRoot, p)}:${line + 1} ${sym} [${role}]`);
|
|
202
221
|
}
|
|
@@ -215,13 +234,26 @@ exports.context = new commander_1.Command("context")
|
|
|
215
234
|
const startLine = chunkStartLine(r);
|
|
216
235
|
const endLine = chunkEndLine(r);
|
|
217
236
|
const sym = (_b = (_a = (0, arrow_1.toArr)(r.defined_symbols)) === null || _a === void 0 ? void 0 : _a[0]) !== null && _b !== void 0 ? _b : "";
|
|
237
|
+
const parentSym = String(r.parent_symbol || "");
|
|
218
238
|
try {
|
|
219
239
|
const content = fs.readFileSync(absP, "utf-8");
|
|
220
240
|
const allLines = content.split("\n");
|
|
221
241
|
const body = allLines
|
|
222
242
|
.slice(startLine, Math.min(endLine + 1, allLines.length))
|
|
223
243
|
.join("\n");
|
|
224
|
-
|
|
244
|
+
// A sub-chunk that starts mid-function has no defined symbol of its
|
|
245
|
+
// own — prepend the enclosing definition's signature so the extract
|
|
246
|
+
// isn't headless code.
|
|
247
|
+
let enclosing = "";
|
|
248
|
+
let label = sym;
|
|
249
|
+
if (!sym && parentSym) {
|
|
250
|
+
label = `(in ${parentSym})`;
|
|
251
|
+
const sig = findEnclosingSignature(allLines, startLine, parentSym);
|
|
252
|
+
if (sig) {
|
|
253
|
+
enclosing = `${sig.text} // :${sig.line + 1}\n// ...\n`;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
return `\n--- ${relPath(projectRoot, absP)}:${startLine + 1} ${label} ---\n${enclosing}${body}`;
|
|
225
257
|
}
|
|
226
258
|
catch (_c) {
|
|
227
259
|
return null; // File not readable — drop
|
package/dist/commands/doctor.js
CHANGED
|
@@ -237,6 +237,11 @@ exports.doctor = new commander_1.Command("doctor")
|
|
|
237
237
|
diskLevel = availBytes < config_1.DISK_CRITICAL_BYTES ? "CRITICAL" : availBytes < config_1.DISK_LOW_BYTES ? "LOW" : "ok";
|
|
238
238
|
}
|
|
239
239
|
catch (_e) { }
|
|
240
|
+
const staleChunkerProjects = projects.filter((p) => {
|
|
241
|
+
var _a;
|
|
242
|
+
return p.status === "indexed" &&
|
|
243
|
+
((_a = p.chunkerVersion) !== null && _a !== void 0 ? _a : 1) < config_1.CONFIG.CHUNKER_VERSION;
|
|
244
|
+
});
|
|
240
245
|
if (opts.agent) {
|
|
241
246
|
const fields = [
|
|
242
247
|
"index_health",
|
|
@@ -251,8 +256,14 @@ exports.doctor = new commander_1.Command("doctor")
|
|
|
251
256
|
`lock=${lockStatus.split(" ")[0]}`,
|
|
252
257
|
`daemon=${daemonUp ? "running" : "stopped"}`,
|
|
253
258
|
`orphaned=${orphanedProjects.length}`,
|
|
259
|
+
`stale_chunker=${staleChunkerProjects.length}`,
|
|
254
260
|
];
|
|
255
261
|
console.log(fields.join("\t"));
|
|
262
|
+
if (staleChunkerProjects.length > 0) {
|
|
263
|
+
console.log(`stale_chunker_fix: run 'gmax index --reset' in: ${staleChunkerProjects
|
|
264
|
+
.map((p) => p.name || path.basename(p.root))
|
|
265
|
+
.join(", ")}`);
|
|
266
|
+
}
|
|
256
267
|
}
|
|
257
268
|
else {
|
|
258
269
|
console.log("\nIndex Health\n");
|
|
@@ -296,6 +307,13 @@ exports.doctor = new commander_1.Command("doctor")
|
|
|
296
307
|
}
|
|
297
308
|
// Daemon
|
|
298
309
|
console.log(`${daemonUp ? "ok" : "INFO"} Daemon: ${daemonUp ? "running" : "not running"}`);
|
|
310
|
+
// Index built by an older chunker — graph metadata fixes need a reindex
|
|
311
|
+
if (staleChunkerProjects.length > 0) {
|
|
312
|
+
console.log(`WARN Stale chunker: ${staleChunkerProjects.length} project(s) indexed before chunker v${config_1.CONFIG.CHUNKER_VERSION} — graph commands (peek/impact/trace) may overcount callers`);
|
|
313
|
+
for (const p of staleChunkerProjects) {
|
|
314
|
+
console.log(` - ${p.name || path.basename(p.root)}: run 'gmax index --reset' in ${p.root}`);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
299
317
|
// Projects
|
|
300
318
|
if (orphanedProjects.length > 0) {
|
|
301
319
|
console.log(`WARN Orphaned projects: ${orphanedProjects.length} (directories no longer exist)`);
|
package/dist/commands/impact.js
CHANGED
|
@@ -46,6 +46,7 @@ exports.impact = void 0;
|
|
|
46
46
|
const path = __importStar(require("node:path"));
|
|
47
47
|
const commander_1 = require("commander");
|
|
48
48
|
const impact_1 = require("../lib/graph/impact");
|
|
49
|
+
const test_hits_1 = require("../lib/graph/test-hits");
|
|
49
50
|
const vector_db_1 = require("../lib/store/vector-db");
|
|
50
51
|
const agent_errors_1 = require("../lib/utils/agent-errors");
|
|
51
52
|
const exit_1 = require("../lib/utils/exit");
|
|
@@ -58,10 +59,13 @@ exports.impact = new commander_1.Command("impact")
|
|
|
58
59
|
.option("--root <dir>", "Project root directory")
|
|
59
60
|
.option("--in <subpath>", "Restrict to a sub-path of the project (repeatable)", (value, prev) => (prev ? [...prev, value] : [value]))
|
|
60
61
|
.option("--exclude <subpath>", "Exclude a sub-path of the project (repeatable)", (value, prev) => (prev ? [...prev, value] : [value]))
|
|
62
|
+
.option("--no-tests", "Skip affected-test analysis; show production blast radius only")
|
|
61
63
|
.option("--agent", "Compact output for AI agents", false)
|
|
62
64
|
.action((target, opts) => __awaiter(void 0, void 0, void 0, function* () {
|
|
63
65
|
var _a;
|
|
64
66
|
const depth = Math.min(Math.max(Number.parseInt(opts.depth || "1", 10), 1), 3);
|
|
67
|
+
// commander maps --no-tests → opts.tests === false (defaults true).
|
|
68
|
+
const includeTests = opts.tests !== false;
|
|
65
69
|
let vectorDb = null;
|
|
66
70
|
try {
|
|
67
71
|
const root = (0, project_registry_1.resolveRootOrExit)(opts.root);
|
|
@@ -96,21 +100,26 @@ exports.impact = new commander_1.Command("impact")
|
|
|
96
100
|
const queryRoot = opts.in && opts.in.length > 0
|
|
97
101
|
? scope.pathPrefix.replace(/\/$/, "")
|
|
98
102
|
: projectRoot;
|
|
99
|
-
// Run dependents and tests in parallel
|
|
103
|
+
// Run dependents and tests in parallel. --no-tests skips the test
|
|
104
|
+
// traversal entirely so the affected-tests section is omitted (not just
|
|
105
|
+
// empty) below.
|
|
100
106
|
const [dependents, tests] = yield Promise.all([
|
|
101
107
|
(0, impact_1.findDependents)(symbols, vectorDb, queryRoot, excludePaths, undefined, scope.excludePrefixes),
|
|
102
|
-
|
|
108
|
+
includeTests
|
|
109
|
+
? (0, impact_1.findTests)(symbols, vectorDb, queryRoot, depth, scope.excludePrefixes)
|
|
110
|
+
: Promise.resolve([]),
|
|
103
111
|
]);
|
|
104
112
|
// Separate test files from non-test dependents
|
|
105
113
|
const nonTestDeps = dependents.filter((d) => !(0, impact_1.isTestPath)(d.file));
|
|
114
|
+
// One line per test file; caller symbols inside it become `via` detail.
|
|
115
|
+
const groupedTests = (0, test_hits_1.groupTestHitsByFile)(tests);
|
|
106
116
|
const rel = (p) => p.startsWith(`${projectRoot}/`) ? p.slice(projectRoot.length + 1) : p;
|
|
107
117
|
if (opts.agent) {
|
|
108
118
|
for (const d of nonTestDeps) {
|
|
109
119
|
console.log(`dep: ${rel(d.file)}\t${d.sharedSymbols}`);
|
|
110
120
|
}
|
|
111
|
-
for (const t of
|
|
112
|
-
|
|
113
|
-
console.log(`test: ${rel(t.file)}:${t.line + 1}\t${t.symbol}\t${hopLabel}`);
|
|
121
|
+
for (const t of groupedTests) {
|
|
122
|
+
console.log(`test: ${rel(t.file)}:${t.line + 1}\t${(0, test_hits_1.hopLabelAgent)(t.hops)}${(0, test_hits_1.formatViaAgent)(t.via)}`);
|
|
114
123
|
}
|
|
115
124
|
if (!nonTestDeps.length && !tests.length) {
|
|
116
125
|
console.log("(no impact detected)");
|
|
@@ -127,16 +136,17 @@ exports.impact = new commander_1.Command("impact")
|
|
|
127
136
|
else {
|
|
128
137
|
console.log("Direct dependents: none found");
|
|
129
138
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
const
|
|
135
|
-
|
|
139
|
+
if (includeTests) {
|
|
140
|
+
console.log("");
|
|
141
|
+
if (groupedTests.length > 0) {
|
|
142
|
+
console.log(`Affected tests (${groupedTests.length}):`);
|
|
143
|
+
for (const t of groupedTests) {
|
|
144
|
+
console.log(` ${rel(t.file)}:${t.line + 1} (${(0, test_hits_1.hopLabelHuman)(t.hops)}${(0, test_hits_1.formatViaHuman)(t.via)})`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
console.log("Affected tests: none found");
|
|
136
149
|
}
|
|
137
|
-
}
|
|
138
|
-
else {
|
|
139
|
-
console.log("Affected tests: none found");
|
|
140
150
|
}
|
|
141
151
|
}
|
|
142
152
|
}
|
package/dist/commands/index.js
CHANGED
|
@@ -45,6 +45,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
45
45
|
exports.index = void 0;
|
|
46
46
|
const path = __importStar(require("node:path"));
|
|
47
47
|
const commander_1 = require("commander");
|
|
48
|
+
const config_1 = require("../config");
|
|
48
49
|
const index_config_1 = require("../lib/index/index-config");
|
|
49
50
|
const grammar_loader_1 = require("../lib/index/grammar-loader");
|
|
50
51
|
const sync_helpers_1 = require("../lib/index/sync-helpers");
|
|
@@ -70,7 +71,7 @@ Examples:
|
|
|
70
71
|
gmax index --reset Full re-index from scratch
|
|
71
72
|
`)
|
|
72
73
|
.action((_args, cmd) => __awaiter(void 0, void 0, void 0, function* () {
|
|
73
|
-
var _a, _b, _c, _d;
|
|
74
|
+
var _a, _b, _c, _d, _e;
|
|
74
75
|
const options = cmd.optsWithGlobals();
|
|
75
76
|
let vectorDb = null;
|
|
76
77
|
const ac = new AbortController();
|
|
@@ -90,11 +91,18 @@ Examples:
|
|
|
90
91
|
: process.cwd();
|
|
91
92
|
const projectRoot = (_a = (0, project_root_1.findProjectRoot)(indexRoot)) !== null && _a !== void 0 ? _a : indexRoot;
|
|
92
93
|
// Project must be registered before reindexing
|
|
93
|
-
|
|
94
|
+
const existingEntry = (0, project_registry_1.getProject)(projectRoot);
|
|
95
|
+
if (!existingEntry) {
|
|
94
96
|
console.error(`This project hasn't been added yet.\n\nRun: gmax add ${projectRoot}\n`);
|
|
95
97
|
process.exitCode = 1;
|
|
96
98
|
return;
|
|
97
99
|
}
|
|
100
|
+
// Only a reset rechunks cached files, so only a reset may claim the
|
|
101
|
+
// current chunker version — a plain index would clear the doctor
|
|
102
|
+
// stale-chunker warning without regenerating any chunks.
|
|
103
|
+
const stampedChunkerVersion = options.reset
|
|
104
|
+
? config_1.CONFIG.CHUNKER_VERSION
|
|
105
|
+
: ((_b = existingEntry.chunkerVersion) !== null && _b !== void 0 ? _b : 1);
|
|
98
106
|
if (options.reset) {
|
|
99
107
|
console.log("Resetting index...");
|
|
100
108
|
}
|
|
@@ -115,7 +123,7 @@ Examples:
|
|
|
115
123
|
});
|
|
116
124
|
});
|
|
117
125
|
if (!done.ok) {
|
|
118
|
-
throw new Error((
|
|
126
|
+
throw new Error((_c = done.error) !== null && _c !== void 0 ? _c : "daemon index failed");
|
|
119
127
|
}
|
|
120
128
|
if (options.dryRun) {
|
|
121
129
|
spinner.succeed(`Dry run complete(${done.processed} / ${done.total}) • would have indexed ${done.indexed} `);
|
|
@@ -129,10 +137,11 @@ Examples:
|
|
|
129
137
|
modelTier: globalConfig.modelTier,
|
|
130
138
|
embedMode: globalConfig.embedMode,
|
|
131
139
|
lastIndexed: new Date().toISOString(),
|
|
132
|
-
chunkCount: (
|
|
140
|
+
chunkCount: (_d = done.indexed) !== null && _d !== void 0 ? _d : 0,
|
|
133
141
|
status: "indexed",
|
|
142
|
+
chunkerVersion: stampedChunkerVersion,
|
|
134
143
|
});
|
|
135
|
-
const failedFiles = (
|
|
144
|
+
const failedFiles = (_e = done.failedFiles) !== null && _e !== void 0 ? _e : 0;
|
|
136
145
|
const failedSuffix = failedFiles > 0 ? ` • ${failedFiles} failed` : "";
|
|
137
146
|
spinner.succeed(`Indexing complete(${done.processed} / ${done.total}) • indexed ${done.indexed}${failedSuffix} `);
|
|
138
147
|
}
|
|
@@ -152,7 +161,7 @@ Examples:
|
|
|
152
161
|
try {
|
|
153
162
|
process.kill(watcher.pid, "SIGTERM");
|
|
154
163
|
}
|
|
155
|
-
catch (
|
|
164
|
+
catch (_f) { }
|
|
156
165
|
for (let i = 0; i < 50; i++) {
|
|
157
166
|
if (!(0, watcher_store_1.isProcessRunning)(watcher.pid))
|
|
158
167
|
break;
|
|
@@ -192,6 +201,7 @@ Examples:
|
|
|
192
201
|
lastIndexed: new Date().toISOString(),
|
|
193
202
|
chunkCount: result.indexed,
|
|
194
203
|
status: "indexed",
|
|
204
|
+
chunkerVersion: stampedChunkerVersion,
|
|
195
205
|
});
|
|
196
206
|
const failedSuffix = result.failedFiles > 0 ? ` • ${result.failedFiles} failed` : "";
|
|
197
207
|
spinner.succeed(`Indexing complete(${result.processed} / ${result.total}) • indexed ${result.indexed}${failedSuffix} `);
|
package/dist/commands/mcp.js
CHANGED
|
@@ -623,6 +623,7 @@ function formatMcpPointerSearchResults(data, displayRoot, options = {}) {
|
|
|
623
623
|
return (0, agent_search_formatter_1.formatAgentSearchResults)(filterMcpSearchResults(data, options), displayRoot, {
|
|
624
624
|
includeImports: options.includeImports,
|
|
625
625
|
getImportsForFile: options.getImportsForFile,
|
|
626
|
+
query: options.query,
|
|
626
627
|
});
|
|
627
628
|
}
|
|
628
629
|
// ---------------------------------------------------------------------------
|
|
@@ -834,6 +835,7 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
834
835
|
namePattern,
|
|
835
836
|
includeImports,
|
|
836
837
|
getImportsForFile,
|
|
838
|
+
query,
|
|
837
839
|
});
|
|
838
840
|
if ((_a = result.warnings) === null || _a === void 0 ? void 0 : _a.length) {
|
|
839
841
|
return ok(`${result.warnings.join("\n")}\n\n${output}`);
|
package/dist/commands/peek.js
CHANGED
|
@@ -45,6 +45,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
45
45
|
exports.peek = void 0;
|
|
46
46
|
const fs = __importStar(require("node:fs"));
|
|
47
47
|
const commander_1 = require("commander");
|
|
48
|
+
const callsites_1 = require("../lib/graph/callsites");
|
|
48
49
|
const graph_builder_1 = require("../lib/graph/graph-builder");
|
|
49
50
|
const vector_db_1 = require("../lib/store/vector-db");
|
|
50
51
|
const agent_errors_1 = require("../lib/utils/agent-errors");
|
|
@@ -69,26 +70,45 @@ function extractSignature(filePath, startLine, endLine) {
|
|
|
69
70
|
const lines = content.split("\n");
|
|
70
71
|
const chunk = lines.slice(startLine, endLine + 1);
|
|
71
72
|
const bodyLines = chunk.length;
|
|
72
|
-
// Find the signature: everything up to and including the opening brace
|
|
73
|
+
// Find the signature: everything up to and including the opening brace.
|
|
74
|
+
// Only treat `{` / `=>` as the body boundary once the parameter list's
|
|
75
|
+
// parens are balanced — object-literal param types (`cached: { … }`)
|
|
76
|
+
// contain braces mid-signature and must not end it.
|
|
73
77
|
const sigLines = [];
|
|
78
|
+
let parenDepth = 0;
|
|
74
79
|
for (const line of chunk) {
|
|
75
80
|
sigLines.push(line);
|
|
76
|
-
|
|
81
|
+
for (const ch of line) {
|
|
82
|
+
if (ch === "(")
|
|
83
|
+
parenDepth++;
|
|
84
|
+
else if (ch === ")")
|
|
85
|
+
parenDepth--;
|
|
86
|
+
}
|
|
87
|
+
if (parenDepth <= 0 && (line.includes("{") || line.includes("=>"))) {
|
|
77
88
|
break;
|
|
89
|
+
}
|
|
90
|
+
if (sigLines.length >= 12)
|
|
91
|
+
break; // degenerate input — bail
|
|
78
92
|
}
|
|
79
93
|
// If we only got one line and it's the whole function, collapse it
|
|
80
94
|
if (sigLines.length >= bodyLines) {
|
|
81
|
-
|
|
95
|
+
const whole = chunk.join("\n");
|
|
96
|
+
return { signature: whole, signatureOnly: whole, bodyLines: 0 };
|
|
82
97
|
}
|
|
83
98
|
const sig = sigLines.join("\n");
|
|
84
99
|
const remaining = bodyLines - sigLines.length;
|
|
85
100
|
return {
|
|
86
101
|
signature: `${sig}\n // ... (${remaining} lines)\n }`,
|
|
102
|
+
signatureOnly: sig,
|
|
87
103
|
bodyLines,
|
|
88
104
|
};
|
|
89
105
|
}
|
|
90
106
|
catch (_a) {
|
|
91
|
-
return {
|
|
107
|
+
return {
|
|
108
|
+
signature: "(source not available)",
|
|
109
|
+
signatureOnly: "(source not available)",
|
|
110
|
+
bodyLines: 0,
|
|
111
|
+
};
|
|
92
112
|
}
|
|
93
113
|
}
|
|
94
114
|
exports.peek = new commander_1.Command("peek")
|
|
@@ -122,6 +142,9 @@ exports.peek = new commander_1.Command("peek")
|
|
|
122
142
|
// languages, refuse to silently pick one. The graph builder otherwise
|
|
123
143
|
// picks one chunk arbitrarily and lists callers from a different
|
|
124
144
|
// language — verified failure mode.
|
|
145
|
+
// Same-language multi-definition is reported as a note instead (below):
|
|
146
|
+
// the first definition still wins, but the agent learns it guessed.
|
|
147
|
+
let otherDefs = [];
|
|
125
148
|
{
|
|
126
149
|
const tableForCheck = yield vectorDb.ensureTable();
|
|
127
150
|
const allDefs = yield tableForCheck
|
|
@@ -134,6 +157,14 @@ exports.peek = new commander_1.Command("peek")
|
|
|
134
157
|
path: String(row.path || ""),
|
|
135
158
|
startLine: Number(row.start_line || 0),
|
|
136
159
|
}));
|
|
160
|
+
// Dedupe by file: split sub-chunks of one definition share a path,
|
|
161
|
+
// while genuine ambiguity (same name defined elsewhere) crosses files.
|
|
162
|
+
const distinct = new Map();
|
|
163
|
+
for (const c of chunks) {
|
|
164
|
+
if (!distinct.has(c.path))
|
|
165
|
+
distinct.set(c.path, c);
|
|
166
|
+
}
|
|
167
|
+
otherDefs = [...distinct.values()];
|
|
137
168
|
const byLang = (0, language_1.groupByLanguage)(chunks);
|
|
138
169
|
if (byLang.size >= 2) {
|
|
139
170
|
const rel = (p) => p.startsWith(projectRoot) ? p.slice(projectRoot.length + 1) : p;
|
|
@@ -198,7 +229,30 @@ exports.peek = new commander_1.Command("peek")
|
|
|
198
229
|
line: c.line,
|
|
199
230
|
}));
|
|
200
231
|
}
|
|
201
|
-
|
|
232
|
+
// Re-anchor chunk-level caller rows to actual call sites and dedupe —
|
|
233
|
+
// getCallers() returns one row per chunk, which multiplies callers for
|
|
234
|
+
// classes split across many chunks (verified: 3 real call sites → 66).
|
|
235
|
+
const resolvedCallers = (0, callsites_1.resolveCallSites)(callerList, symbol).map((c) => {
|
|
236
|
+
var _a;
|
|
237
|
+
return ({
|
|
238
|
+
symbol: c.symbol,
|
|
239
|
+
file: c.file,
|
|
240
|
+
line: (_a = c.snippetLine) !== null && _a !== void 0 ? _a : c.line,
|
|
241
|
+
});
|
|
242
|
+
});
|
|
243
|
+
// Builtins listed as "(not indexed)" callees (trunc, now, filter, …)
|
|
244
|
+
// are noise; project symbols always resolve so they're unaffected.
|
|
245
|
+
// Dedupe by symbol — repeated references arrive once per chunk.
|
|
246
|
+
const seenCallees = new Set();
|
|
247
|
+
const calleeList = graph.callees
|
|
248
|
+
.filter((c) => c.file || !(0, callsites_1.isBuiltinCallee)(c.symbol))
|
|
249
|
+
.filter((c) => {
|
|
250
|
+
if (seenCallees.has(c.symbol))
|
|
251
|
+
return false;
|
|
252
|
+
seenCallees.add(c.symbol);
|
|
253
|
+
return true;
|
|
254
|
+
})
|
|
255
|
+
.map((c) => ({
|
|
202
256
|
symbol: c.symbol,
|
|
203
257
|
file: c.file,
|
|
204
258
|
line: c.line,
|
|
@@ -207,16 +261,30 @@ exports.peek = new commander_1.Command("peek")
|
|
|
207
261
|
// Compact TSV output
|
|
208
262
|
const exportedStr = exported ? "exported" : "";
|
|
209
263
|
console.log(`${center.symbol}\t${rel(center.file)}:${center.line + 1}\t${center.role}\t${exportedStr}`);
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
264
|
+
if (otherDefs.length > 1) {
|
|
265
|
+
const others = otherDefs
|
|
266
|
+
.filter((d) => d.path !== center.file)
|
|
267
|
+
.slice(0, 4)
|
|
268
|
+
.map((d) => `${rel(d.path)}:${d.startLine + 1}`);
|
|
269
|
+
if (others.length > 0) {
|
|
270
|
+
console.log(`also-defined: ${others.join(", ")} — showing the first; pin with --in <subpath>`);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
// Signature — all lines up to the opening brace, collapsed to one
|
|
274
|
+
// line so parameters survive (first-line-only loses them).
|
|
275
|
+
const { signatureOnly } = extractSignature(center.file, startLine, endLine);
|
|
276
|
+
const sigOnly = signatureOnly
|
|
277
|
+
.split("\n")
|
|
278
|
+
.map((l) => l.trim())
|
|
279
|
+
.join(" ")
|
|
280
|
+
.replace(/\s+/g, " ");
|
|
281
|
+
console.log(`sig: ${sigOnly}`);
|
|
214
282
|
// Callers
|
|
215
|
-
for (const c of
|
|
283
|
+
for (const c of resolvedCallers.slice(0, MAX_CALLERS)) {
|
|
216
284
|
console.log(`<- ${c.symbol}\t${c.file ? `${rel(c.file)}:${c.line + 1}` : "(not indexed)"}`);
|
|
217
285
|
}
|
|
218
|
-
if (
|
|
219
|
-
console.log(`<- ... ${
|
|
286
|
+
if (resolvedCallers.length > MAX_CALLERS) {
|
|
287
|
+
console.log(`<- ... ${resolvedCallers.length - MAX_CALLERS} more`);
|
|
220
288
|
}
|
|
221
289
|
// Callees
|
|
222
290
|
for (const c of calleeList.slice(0, MAX_CALLEES)) {
|
|
@@ -239,6 +307,15 @@ exports.peek = new commander_1.Command("peek")
|
|
|
239
307
|
// Rich output
|
|
240
308
|
const exportedStr = exported ? ", exported" : "";
|
|
241
309
|
console.log(`${style.bold(`peek: ${center.symbol}`)} ${style.dim(`${rel(center.file)}:${center.line + 1}`)} ${style.dim(`[${center.role}${exportedStr}]`)}`);
|
|
310
|
+
if (otherDefs.length > 1) {
|
|
311
|
+
const others = otherDefs
|
|
312
|
+
.filter((d) => d.path !== center.file)
|
|
313
|
+
.slice(0, 4)
|
|
314
|
+
.map((d) => `${rel(d.path)}:${d.startLine + 1}`);
|
|
315
|
+
if (others.length > 0) {
|
|
316
|
+
console.log(style.dim(` also defined in: ${others.join(", ")} — showing the first; pin with --in <subpath>`));
|
|
317
|
+
}
|
|
318
|
+
}
|
|
242
319
|
console.log();
|
|
243
320
|
// Signature with collapsed body
|
|
244
321
|
const { signature } = extractSignature(center.file, startLine, endLine);
|
|
@@ -247,9 +324,9 @@ exports.peek = new commander_1.Command("peek")
|
|
|
247
324
|
}
|
|
248
325
|
console.log();
|
|
249
326
|
// Callers
|
|
250
|
-
if (
|
|
251
|
-
const shown =
|
|
252
|
-
console.log(style.bold(`callers (${
|
|
327
|
+
if (resolvedCallers.length > 0) {
|
|
328
|
+
const shown = resolvedCallers.slice(0, MAX_CALLERS);
|
|
329
|
+
console.log(style.bold(`callers (${resolvedCallers.length}):`));
|
|
253
330
|
for (const c of shown) {
|
|
254
331
|
if (c.file) {
|
|
255
332
|
console.log(` ${style.blue("\u2190")} ${style.green(c.symbol.padEnd(25))} ${style.dim(`${rel(c.file)}:${c.line + 1}`)}`);
|
|
@@ -258,8 +335,8 @@ exports.peek = new commander_1.Command("peek")
|
|
|
258
335
|
console.log(` ${style.blue("\u2190")} ${c.symbol.padEnd(25)} ${style.dim("(not indexed)")}`);
|
|
259
336
|
}
|
|
260
337
|
}
|
|
261
|
-
if (
|
|
262
|
-
console.log(style.dim(` ... and ${
|
|
338
|
+
if (resolvedCallers.length > MAX_CALLERS) {
|
|
339
|
+
console.log(style.dim(` ... and ${resolvedCallers.length - MAX_CALLERS} more`));
|
|
263
340
|
}
|
|
264
341
|
}
|
|
265
342
|
else {
|
package/dist/commands/project.js
CHANGED
|
@@ -45,6 +45,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
45
45
|
exports.project = void 0;
|
|
46
46
|
const path = __importStar(require("node:path"));
|
|
47
47
|
const commander_1 = require("commander");
|
|
48
|
+
const callsites_1 = require("../lib/graph/callsites");
|
|
48
49
|
const vector_db_1 = require("../lib/store/vector-db");
|
|
49
50
|
const filter_builder_1 = require("../lib/utils/filter-builder");
|
|
50
51
|
const exit_1 = require("../lib/utils/exit");
|
|
@@ -91,7 +92,9 @@ exports.project = new commander_1.Command("project")
|
|
|
91
92
|
const dirCounts = new Map();
|
|
92
93
|
const roleCounts = new Map();
|
|
93
94
|
const symbolRefs = new Map();
|
|
95
|
+
const definedInProject = new Set();
|
|
94
96
|
const entryPoints = [];
|
|
97
|
+
const seenEntryPoints = new Set();
|
|
95
98
|
for (const row of rows) {
|
|
96
99
|
const p = String(row.path || "");
|
|
97
100
|
const role = String(row.role || "IMPLEMENTATION");
|
|
@@ -115,13 +118,19 @@ exports.project = new commander_1.Command("project")
|
|
|
115
118
|
dc.files.add(p);
|
|
116
119
|
dc.chunks++;
|
|
117
120
|
roleCounts.set(role, (roleCounts.get(role) || 0) + 1);
|
|
121
|
+
for (const d of defs)
|
|
122
|
+
definedInProject.add(d);
|
|
118
123
|
for (const ref of refs)
|
|
119
124
|
symbolRefs.set(ref, (symbolRefs.get(ref) || 0) + 1);
|
|
120
125
|
if (exported && role === "ORCHESTRATION" && complexity >= 5 && defs.length > 0) {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
126
|
+
const epKey = `${defs[0]}:${p}`;
|
|
127
|
+
if (!seenEntryPoints.has(epKey)) {
|
|
128
|
+
seenEntryPoints.add(epKey);
|
|
129
|
+
entryPoints.push({
|
|
130
|
+
symbol: defs[0],
|
|
131
|
+
path: p.startsWith(prefix) ? p.slice(prefix.length) : p,
|
|
132
|
+
});
|
|
133
|
+
}
|
|
125
134
|
}
|
|
126
135
|
}
|
|
127
136
|
const projects = (0, project_registry_1.listProjects)();
|
|
@@ -129,7 +138,11 @@ exports.project = new commander_1.Command("project")
|
|
|
129
138
|
const extEntries = Array.from(extCounts.entries())
|
|
130
139
|
.sort((a, b) => b[1] - a[1])
|
|
131
140
|
.slice(0, 8);
|
|
141
|
+
// Key symbols must be the project's own: raw referenced_symbols counts
|
|
142
|
+
// are dominated by JS builtins (push, slice, map, …) which say nothing
|
|
143
|
+
// about the codebase.
|
|
132
144
|
const topSymbols = Array.from(symbolRefs.entries())
|
|
145
|
+
.filter(([s]) => definedInProject.has(s) && !(0, callsites_1.isBuiltinCallee)(s))
|
|
133
146
|
.sort((a, b) => b[1] - a[1])
|
|
134
147
|
.slice(0, 8);
|
|
135
148
|
if (opts.agent) {
|