grepmax 0.7.4 → 0.7.5

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.
@@ -103,6 +103,14 @@ const TOOLS = [
103
103
  type: "number",
104
104
  description: "Max results per file (default: no cap). Useful to get diversity across files.",
105
105
  },
106
+ file: {
107
+ type: "string",
108
+ description: "Filter to files matching this name (e.g. 'syncer.ts'). Matches the filename, not the full path.",
109
+ },
110
+ exclude: {
111
+ type: "string",
112
+ description: "Exclude files under this path prefix (e.g. 'tests/' or 'dist/').",
113
+ },
106
114
  },
107
115
  required: ["query"],
108
116
  },
@@ -133,6 +141,14 @@ const TOOLS = [
133
141
  type: "number",
134
142
  description: "Max results per file (default: no cap).",
135
143
  },
144
+ file: {
145
+ type: "string",
146
+ description: "Filter to files matching this name (e.g. 'syncer.ts').",
147
+ },
148
+ exclude: {
149
+ type: "string",
150
+ description: "Exclude files under this path prefix (e.g. 'tests/').",
151
+ },
136
152
  },
137
153
  required: ["query"],
138
154
  },
@@ -422,7 +438,14 @@ exports.mcp = new commander_1.Command("mcp")
422
438
  pathPrefix += "/";
423
439
  }
424
440
  }
425
- const result = yield searcher.search(query, limit, { rerank: true }, undefined, pathPrefix);
441
+ const filters = {};
442
+ if (typeof args.file === "string" && args.file) {
443
+ filters.file = args.file;
444
+ }
445
+ if (typeof args.exclude === "string" && args.exclude) {
446
+ filters.exclude = args.exclude;
447
+ }
448
+ const result = yield searcher.search(query, limit, { rerank: true }, Object.keys(filters).length > 0 ? filters : undefined, pathPrefix);
426
449
  if (!result.data || result.data.length === 0) {
427
450
  return ok("No matches found.");
428
451
  }
@@ -570,11 +593,23 @@ exports.mcp = new commander_1.Command("mcp")
570
593
  else {
571
594
  lines.push("Callers: none");
572
595
  }
573
- // Callees (cap at 15)
596
+ // Callees with file paths
574
597
  if (graph.callees.length > 0) {
575
- const capped = graph.callees.slice(0, 15);
576
- const suffix = graph.callees.length > 15 ? ` (+${graph.callees.length - 15} more)` : "";
577
- lines.push(`Calls: ${capped.join(", ")}${suffix}`);
598
+ lines.push("Calls:");
599
+ for (const callee of graph.callees.slice(0, 15)) {
600
+ if (callee.file) {
601
+ const rel = callee.file.startsWith(projectRoot)
602
+ ? callee.file.slice(projectRoot.length + 1)
603
+ : callee.file;
604
+ lines.push(` -> ${callee.symbol} ${rel}:${callee.line + 1}`);
605
+ }
606
+ else {
607
+ lines.push(` -> ${callee.symbol} (not indexed)`);
608
+ }
609
+ }
610
+ if (graph.callees.length > 15) {
611
+ lines.push(` (+${graph.callees.length - 15} more)`);
612
+ }
578
613
  }
579
614
  else {
580
615
  lines.push("Calls: none");
@@ -695,7 +730,23 @@ exports.mcp = new commander_1.Command("mcp")
695
730
  indexingLine,
696
731
  "",
697
732
  "Indexed directories:",
698
- ...projects.map((p) => { var _a; return ` ${p.name}\t${p.root}\t${(_a = p.lastIndexed) !== null && _a !== void 0 ? _a : "unknown"}`; }),
733
+ ...(yield Promise.all(projects.map((p) => __awaiter(this, void 0, void 0, function* () {
734
+ var _a, _b;
735
+ const prefix = p.root.endsWith("/") ? p.root : `${p.root}/`;
736
+ try {
737
+ const table = yield db.ensureTable();
738
+ const rows = yield table
739
+ .query()
740
+ .select(["id"])
741
+ .where(`path LIKE '${(0, filter_builder_1.escapeSqlString)(prefix)}%'`)
742
+ .limit(100000)
743
+ .toArray();
744
+ return ` ${p.name}\t${p.root}\t${(_a = p.lastIndexed) !== null && _a !== void 0 ? _a : "unknown"}\t(${rows.length} chunks)`;
745
+ }
746
+ catch (_c) {
747
+ return ` ${p.name}\t${p.root}\t${(_b = p.lastIndexed) !== null && _b !== void 0 ? _b : "unknown"}`;
748
+ }
749
+ })))),
699
750
  ].filter(Boolean);
700
751
  return ok(lines.join("\n"));
701
752
  }
@@ -69,9 +69,40 @@ class GraphBuilder {
69
69
  : null;
70
70
  // 2. Get Callers
71
71
  const callers = yield this.getCallers(symbol);
72
- // 3. Get Callees (from center)
73
- const callees = center ? center.calls : [];
74
- return { center, callers, callees };
72
+ // 3. Get Callees resolve each to a GraphNode with file:line
73
+ const calleeNames = center ? center.calls.slice(0, 15) : [];
74
+ const calleeNodes = [];
75
+ for (const name of calleeNames) {
76
+ const esc = (0, filter_builder_1.escapeSqlString)(name);
77
+ const rows = yield table
78
+ .query()
79
+ .where(`array_contains(defined_symbols, '${esc}')`)
80
+ .select([
81
+ "path",
82
+ "start_line",
83
+ "defined_symbols",
84
+ "referenced_symbols",
85
+ "role",
86
+ "parent_symbol",
87
+ "complexity",
88
+ ])
89
+ .limit(1)
90
+ .toArray();
91
+ if (rows.length > 0) {
92
+ calleeNodes.push(this.mapRowToNode(rows[0], name, "center"));
93
+ }
94
+ else {
95
+ calleeNodes.push({
96
+ symbol: name,
97
+ file: "",
98
+ line: 0,
99
+ role: "",
100
+ calls: [],
101
+ calledBy: [],
102
+ });
103
+ }
104
+ }
105
+ return { center, callers, callees: calleeNodes };
75
106
  });
76
107
  }
77
108
  mapRowToNode(row, targetSymbol, type) {
@@ -151,7 +151,12 @@ function formatTrace(graph) {
151
151
  if (graph.callees.length > 0) {
152
152
  lines.push(style.bold("Callees (What does this call?):"));
153
153
  graph.callees.forEach((callee) => {
154
- lines.push(` ${style.cyan("↓")} ${callee}`);
154
+ if (callee.file) {
155
+ lines.push(` ${style.cyan("↓")} ${style.green(callee.symbol)} ${style.dim(`(${callee.file}:${callee.line})`)}`);
156
+ }
157
+ else {
158
+ lines.push(` ${style.cyan("↓")} ${callee.symbol} ${style.dim("(not indexed)")}`);
159
+ }
155
160
  });
156
161
  }
157
162
  else {
@@ -277,6 +277,19 @@ class Searcher {
277
277
  if (pathPrefix) {
278
278
  whereClauseParts.push(`path LIKE '${(0, filter_builder_1.escapeSqlString)((0, filter_builder_1.normalizePath)(pathPrefix))}%'`);
279
279
  }
280
+ // Handle file name filter
281
+ const fileFilter = _filters === null || _filters === void 0 ? void 0 : _filters.file;
282
+ if (typeof fileFilter === "string" && fileFilter) {
283
+ whereClauseParts.push(`path LIKE '%/${(0, filter_builder_1.escapeSqlString)(fileFilter)}'`);
284
+ }
285
+ // Handle exclude filter
286
+ const excludeFilter = _filters === null || _filters === void 0 ? void 0 : _filters.exclude;
287
+ if (typeof excludeFilter === "string" && excludeFilter) {
288
+ const absExclude = pathPrefix
289
+ ? (0, filter_builder_1.normalizePath)(pathPrefix + excludeFilter)
290
+ : excludeFilter;
291
+ whereClauseParts.push(`path NOT LIKE '${(0, filter_builder_1.escapeSqlString)(absExclude)}%'`);
292
+ }
280
293
  // Handle --def (definition) filter
281
294
  const defFilter = _filters === null || _filters === void 0 ? void 0 : _filters.def;
282
295
  if (typeof defFilter === "string" && defFilter) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "grepmax",
3
- "version": "0.7.4",
3
+ "version": "0.7.5",
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.4",
3
+ "version": "0.7.5",
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",
@@ -42,6 +42,8 @@ Parameters:
42
42
  - `detail` (optional): `"pointer"` (default) or `"code"`
43
43
  - `min_score` (optional): Filter by minimum relevance score (0-1)
44
44
  - `max_per_file` (optional): Cap results per file for diversity
45
+ - `file` (optional): Filter to files matching this name (e.g. "syncer.ts"). Matches filename, not full path.
46
+ - `exclude` (optional): Exclude files under this path prefix (e.g. "tests/" or "dist/")
45
47
 
46
48
  **When to use which mode:**
47
49
  - `pointer` — navigation, finding locations, understanding architecture
@@ -57,7 +59,7 @@ File structure — signatures with bodies collapsed (~4x fewer tokens).
57
59
  - `target` (required): File path relative to project root
58
60
 
59
61
  ### trace_calls
60
- Call graph — who calls a symbol and what it calls. Unscoped — follows calls across all indexed directories.
62
+ Call graph — who calls a symbol and what it calls. Callers and callees include file:line locations. Unscoped — follows calls across all indexed directories.
61
63
  - `symbol` (required): Function/method/class name
62
64
 
63
65
  ### list_symbols