grepmax 0.7.19 → 0.7.20

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.
@@ -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 "database connection pooling" -m 10
345
- gmax "error handling" --compact --min-score 0.5
346
- gmax "validation logic" --skeleton
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 absolute path prefix for filtering
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
- : projectRoot;
507
+ : effectiveRoot;
494
508
  const pathFilter = searchPathPrefix.endsWith("/")
495
509
  ? searchPathPrefix
496
510
  : `${searchPathPrefix}/`;
497
- const searchResult = yield searcher.search(pattern, parseInt(options.m, 10), { rerank: true }, undefined, pathFilter);
498
- if ((_c = searchResult.warnings) === null || _c === void 0 ? void 0 : _c.length) {
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
- const filteredData = searchResult.data.filter((r) => typeof r.score !== "number" || r.score >= minScore);
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";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "grepmax",
3
- "version": "0.7.19",
3
+ "version": "0.7.20",
4
4
  "author": "Robert Owens <robowens@me.com>",
5
5
  "homepage": "https://github.com/reowens/grepmax",
6
6
  "bugs": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "grepmax",
3
- "version": "0.7.19",
3
+ "version": "0.7.20",
4
4
  "description": "Semantic code search for Claude Code. Automatically indexes your project and provides intelligent search capabilities.",
5
5
  "author": {
6
6
  "name": "Robert Owens",