grepmax 0.7.19 → 0.7.21
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/search.js
CHANGED
|
@@ -336,17 +336,28 @@ exports.search = new commander_1.Command("search")
|
|
|
336
336
|
.option("-s, --sync", "Syncs the local files to the store before searching", false)
|
|
337
337
|
.option("-d, --dry-run", "Show what would be indexed without actually indexing", false)
|
|
338
338
|
.option("--skeleton", "Show code skeleton for matching files instead of snippets", false)
|
|
339
|
+
.option("--root <dir>", "Search a different project directory")
|
|
340
|
+
.option("--file <name>", "Filter to files matching this name (e.g. 'syncer.ts')")
|
|
341
|
+
.option("--exclude <prefix>", "Exclude files under this path prefix (e.g. 'tests/')")
|
|
342
|
+
.option("--lang <ext>", "Filter by file extension (e.g. 'ts', 'py')")
|
|
343
|
+
.option("--role <role>", "Filter by role: ORCHESTRATION, DEFINITION, IMPLEMENTATION")
|
|
344
|
+
.option("--symbol", "Append call graph after search results", false)
|
|
345
|
+
.option("--imports", "Prepend file imports to each result", false)
|
|
346
|
+
.option("--name <regex>", "Filter results by symbol name regex")
|
|
347
|
+
.option("-C, --context <n>", "Include N lines before/after each result")
|
|
339
348
|
.argument("<pattern>", "Natural language query (e.g. \"where do we handle auth?\")")
|
|
340
349
|
.argument("[path]", "Restrict search to this path prefix")
|
|
341
350
|
.addHelpText("after", `
|
|
342
351
|
Examples:
|
|
343
352
|
gmax "where do we handle authentication?"
|
|
344
|
-
gmax "
|
|
345
|
-
gmax "
|
|
346
|
-
gmax "
|
|
353
|
+
gmax "auth handler" --role ORCHESTRATION --lang ts --plain
|
|
354
|
+
gmax "database" --file syncer.ts --plain
|
|
355
|
+
gmax "VectorDB" --symbol --plain
|
|
356
|
+
gmax "error handling" -C 5 --imports --plain
|
|
357
|
+
gmax "handler" --name "handle.*" --exclude tests/
|
|
347
358
|
`)
|
|
348
359
|
.action((pattern, exec_path, _options, cmd) => __awaiter(void 0, void 0, void 0, function* () {
|
|
349
|
-
var _a, _b, _c;
|
|
360
|
+
var _a, _b, _c, _d;
|
|
350
361
|
const options = cmd.optsWithGlobals();
|
|
351
362
|
const root = process.cwd();
|
|
352
363
|
const minScore = Number.isFinite(Number.parseFloat(options.minScore))
|
|
@@ -487,20 +498,48 @@ Examples:
|
|
|
487
498
|
}
|
|
488
499
|
}
|
|
489
500
|
const searcher = new searcher_1.Searcher(vectorDb);
|
|
490
|
-
// Use
|
|
501
|
+
// Use --root or fall back to project root
|
|
502
|
+
const effectiveRoot = options.root
|
|
503
|
+
? (_c = (0, project_root_1.findProjectRoot)(path.resolve(options.root))) !== null && _c !== void 0 ? _c : path.resolve(options.root)
|
|
504
|
+
: projectRoot;
|
|
491
505
|
const searchPathPrefix = exec_path
|
|
492
506
|
? path.resolve(exec_path)
|
|
493
|
-
:
|
|
507
|
+
: effectiveRoot;
|
|
494
508
|
const pathFilter = searchPathPrefix.endsWith("/")
|
|
495
509
|
? searchPathPrefix
|
|
496
510
|
: `${searchPathPrefix}/`;
|
|
497
|
-
|
|
498
|
-
|
|
511
|
+
// Build filters from CLI options
|
|
512
|
+
const searchFilters = {};
|
|
513
|
+
if (options.file)
|
|
514
|
+
searchFilters.file = options.file;
|
|
515
|
+
if (options.exclude)
|
|
516
|
+
searchFilters.exclude = options.exclude;
|
|
517
|
+
if (options.lang)
|
|
518
|
+
searchFilters.language = options.lang;
|
|
519
|
+
if (options.role)
|
|
520
|
+
searchFilters.role = options.role;
|
|
521
|
+
const searchResult = yield searcher.search(pattern, parseInt(options.m, 10), { rerank: true }, Object.keys(searchFilters).length > 0 ? searchFilters : undefined, pathFilter);
|
|
522
|
+
if ((_d = searchResult.warnings) === null || _d === void 0 ? void 0 : _d.length) {
|
|
499
523
|
for (const w of searchResult.warnings) {
|
|
500
524
|
console.warn(`Warning: ${w}`);
|
|
501
525
|
}
|
|
502
526
|
}
|
|
503
|
-
|
|
527
|
+
let filteredData = searchResult.data.filter((r) => typeof r.score !== "number" || r.score >= minScore);
|
|
528
|
+
// Post-filter by symbol name regex
|
|
529
|
+
if (options.name) {
|
|
530
|
+
try {
|
|
531
|
+
const regex = new RegExp(options.name, "i");
|
|
532
|
+
filteredData = filteredData.filter((r) => {
|
|
533
|
+
const defs = Array.isArray(r.defined_symbols)
|
|
534
|
+
? r.defined_symbols
|
|
535
|
+
: [];
|
|
536
|
+
return defs.some((d) => regex.test(d));
|
|
537
|
+
});
|
|
538
|
+
}
|
|
539
|
+
catch (_e) {
|
|
540
|
+
// Invalid regex — skip
|
|
541
|
+
}
|
|
542
|
+
}
|
|
504
543
|
if (options.skeleton) {
|
|
505
544
|
yield outputSkeletons(filteredData, projectRoot, parseInt(options.m, 10), vectorDb);
|
|
506
545
|
return;
|
|
@@ -543,6 +582,49 @@ Examples:
|
|
|
543
582
|
});
|
|
544
583
|
console.log(output);
|
|
545
584
|
}
|
|
585
|
+
// Symbol mode: append call graph
|
|
586
|
+
if (options.symbol && vectorDb) {
|
|
587
|
+
try {
|
|
588
|
+
const { GraphBuilder } = yield Promise.resolve().then(() => __importStar(require("../lib/graph/graph-builder")));
|
|
589
|
+
const builder = new GraphBuilder(vectorDb);
|
|
590
|
+
const graph = yield builder.buildGraphMultiHop(pattern, 1);
|
|
591
|
+
if (graph.center) {
|
|
592
|
+
const lines = ["\n--- Call graph ---"];
|
|
593
|
+
const centerRel = path.relative(effectiveRoot, graph.center.file);
|
|
594
|
+
lines.push(`${graph.center.symbol} [${graph.center.role}] ${centerRel}:${graph.center.line + 1}`);
|
|
595
|
+
if (graph.importers.length > 0) {
|
|
596
|
+
const filtered = graph.importers.filter((p) => p !== graph.center.file);
|
|
597
|
+
if (filtered.length > 0) {
|
|
598
|
+
lines.push("Imported by:");
|
|
599
|
+
for (const imp of filtered.slice(0, 10)) {
|
|
600
|
+
lines.push(` ${path.relative(effectiveRoot, imp)}`);
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
if (graph.callerTree.length > 0) {
|
|
605
|
+
lines.push("Callers:");
|
|
606
|
+
for (const t of graph.callerTree) {
|
|
607
|
+
lines.push(` <- ${t.node.symbol} ${path.relative(effectiveRoot, t.node.file)}:${t.node.line + 1}`);
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
if (graph.callees.length > 0) {
|
|
611
|
+
lines.push("Calls:");
|
|
612
|
+
for (const c of graph.callees.slice(0, 15)) {
|
|
613
|
+
if (c.file) {
|
|
614
|
+
lines.push(` -> ${c.symbol} ${path.relative(effectiveRoot, c.file)}:${c.line + 1}`);
|
|
615
|
+
}
|
|
616
|
+
else {
|
|
617
|
+
lines.push(` -> ${c.symbol} (not indexed)`);
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
console.log(lines.join("\n"));
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
catch (_f) {
|
|
625
|
+
// Trace failed — skip silently
|
|
626
|
+
}
|
|
627
|
+
}
|
|
546
628
|
}
|
|
547
629
|
catch (error) {
|
|
548
630
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
@@ -61,6 +61,7 @@ const setup_helpers_1 = require("../lib/setup/setup-helpers");
|
|
|
61
61
|
const retriever_1 = require("../lib/skeleton/retriever");
|
|
62
62
|
const skeletonizer_1 = require("../lib/skeleton/skeletonizer");
|
|
63
63
|
const vector_db_1 = require("../lib/store/vector-db");
|
|
64
|
+
const file_utils_1 = require("../lib/utils/file-utils");
|
|
64
65
|
const exit_1 = require("../lib/utils/exit");
|
|
65
66
|
const project_root_1 = require("../lib/utils/project-root");
|
|
66
67
|
/**
|
|
@@ -140,6 +141,48 @@ Examples:
|
|
|
140
141
|
includeSummary: !options.noSummary,
|
|
141
142
|
};
|
|
142
143
|
// Determine mode based on target
|
|
144
|
+
const resolvedTarget = path.resolve(target);
|
|
145
|
+
// Directory mode
|
|
146
|
+
if (fs.existsSync(resolvedTarget) &&
|
|
147
|
+
fs.statSync(resolvedTarget).isDirectory()) {
|
|
148
|
+
const entries = fs.readdirSync(resolvedTarget, {
|
|
149
|
+
withFileTypes: true,
|
|
150
|
+
});
|
|
151
|
+
const files = entries
|
|
152
|
+
.filter((e) => e.isFile() &&
|
|
153
|
+
(0, file_utils_1.isIndexableFile)(path.join(resolvedTarget, e.name)))
|
|
154
|
+
.map((e) => path.join(resolvedTarget, e.name))
|
|
155
|
+
.slice(0, Number.parseInt(options.limit, 10));
|
|
156
|
+
if (files.length === 0) {
|
|
157
|
+
console.error(`No indexable files in ${target}`);
|
|
158
|
+
process.exitCode = 1;
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
for (const filePath of files) {
|
|
162
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
163
|
+
const result = yield skeletonizer.skeletonizeFile(filePath, content, skeletonOpts);
|
|
164
|
+
outputResult(result, options);
|
|
165
|
+
}
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
// Batch mode (comma-separated)
|
|
169
|
+
if (target.includes(",")) {
|
|
170
|
+
const targets = target
|
|
171
|
+
.split(",")
|
|
172
|
+
.map((t) => t.trim())
|
|
173
|
+
.filter(Boolean);
|
|
174
|
+
for (const t of targets) {
|
|
175
|
+
const filePath = path.resolve(t);
|
|
176
|
+
if (!fs.existsSync(filePath)) {
|
|
177
|
+
console.error(`Not found: ${t}`);
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
181
|
+
const result = yield skeletonizer.skeletonizeFile(filePath, content, skeletonOpts);
|
|
182
|
+
outputResult(result, options);
|
|
183
|
+
}
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
143
186
|
if (isFilePath(target)) {
|
|
144
187
|
// === FILE MODE ===
|
|
145
188
|
const filePath = path.resolve(target);
|
package/dist/commands/trace.js
CHANGED
|
@@ -19,8 +19,10 @@ const project_root_1 = require("../lib/utils/project-root");
|
|
|
19
19
|
exports.trace = new commander_1.Command("trace")
|
|
20
20
|
.description("Trace the call graph for a symbol")
|
|
21
21
|
.argument("<symbol>", "The symbol to trace")
|
|
22
|
-
.
|
|
22
|
+
.option("-d, --depth <n>", "Caller traversal depth (default 1, max 3)", "1")
|
|
23
|
+
.action((symbol, opts) => __awaiter(void 0, void 0, void 0, function* () {
|
|
23
24
|
var _a;
|
|
25
|
+
const depth = Math.min(Math.max(Number.parseInt(opts.depth || "1", 10), 1), 3);
|
|
24
26
|
const root = process.cwd();
|
|
25
27
|
let vectorDb = null;
|
|
26
28
|
try {
|
|
@@ -28,7 +30,7 @@ exports.trace = new commander_1.Command("trace")
|
|
|
28
30
|
const paths = (0, project_root_1.ensureProjectPaths)(projectRoot);
|
|
29
31
|
vectorDb = new vector_db_1.VectorDB(paths.lancedbDir);
|
|
30
32
|
const graphBuilder = new graph_builder_1.GraphBuilder(vectorDb);
|
|
31
|
-
const graph = yield graphBuilder.
|
|
33
|
+
const graph = yield graphBuilder.buildGraphMultiHop(symbol, depth);
|
|
32
34
|
console.log((0, formatter_1.formatTrace)(graph));
|
|
33
35
|
}
|
|
34
36
|
catch (error) {
|
|
@@ -130,24 +130,40 @@ function formatTrace(graph) {
|
|
|
130
130
|
return style.dim("Symbol not found.");
|
|
131
131
|
}
|
|
132
132
|
const lines = [];
|
|
133
|
-
// 1.
|
|
134
|
-
if (graph.
|
|
133
|
+
// 1. Importers
|
|
134
|
+
if (graph.importers.length > 0) {
|
|
135
|
+
const filtered = graph.importers.filter((p) => p !== graph.center.file);
|
|
136
|
+
if (filtered.length > 0) {
|
|
137
|
+
lines.push(style.bold("Imported by:"));
|
|
138
|
+
for (const imp of filtered.slice(0, 10)) {
|
|
139
|
+
lines.push(` ${style.dim(imp)}`);
|
|
140
|
+
}
|
|
141
|
+
lines.push("");
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
// 2. Callers (Upstream, recursive tree)
|
|
145
|
+
function renderCallerTree(trees, depth) {
|
|
146
|
+
for (const t of trees) {
|
|
147
|
+
const pad = " ".repeat(depth);
|
|
148
|
+
lines.push(`${pad}${style.blue("↑")} ${style.green(t.node.symbol)} ${style.dim(`(${t.node.file}:${t.node.line})`)}`);
|
|
149
|
+
renderCallerTree(t.callers, depth + 1);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
if (graph.callerTree.length > 0) {
|
|
135
153
|
lines.push(style.bold("Callers (Who calls this?):"));
|
|
136
|
-
graph.
|
|
137
|
-
lines.push(` ${style.blue("↑")} ${style.green(caller.symbol)} ${style.dim(`(${caller.file}:${caller.line})`)}`);
|
|
138
|
-
});
|
|
154
|
+
renderCallerTree(graph.callerTree, 1);
|
|
139
155
|
lines.push("");
|
|
140
156
|
}
|
|
141
157
|
else {
|
|
142
158
|
lines.push(style.dim("No known callers."));
|
|
143
159
|
lines.push("");
|
|
144
160
|
}
|
|
145
|
-
//
|
|
161
|
+
// 3. Center (The Symbol)
|
|
146
162
|
lines.push(style.bold(`▶ ${graph.center.symbol}`));
|
|
147
163
|
lines.push(` ${style.dim(`Defined in ${graph.center.file}:${graph.center.line}`)}`);
|
|
148
164
|
lines.push(` ${style.dim(`Role: ${graph.center.role}`)}`);
|
|
149
165
|
lines.push("");
|
|
150
|
-
//
|
|
166
|
+
// 4. Callees (Downstream)
|
|
151
167
|
if (graph.callees.length > 0) {
|
|
152
168
|
lines.push(style.bold("Callees (What does this call?):"));
|
|
153
169
|
graph.callees.forEach((callee) => {
|
package/package.json
CHANGED