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.
- package/dist/commands/mcp.js +57 -6
- package/dist/lib/graph/graph-builder.js +34 -3
- package/dist/lib/output/formatter.js +6 -1
- package/dist/lib/search/searcher.js +13 -0
- package/package.json +1 -1
- package/plugins/grepmax/.claude-plugin/plugin.json +1 -1
- package/plugins/grepmax/skills/grepmax/SKILL.md +3 -1
package/dist/commands/mcp.js
CHANGED
|
@@ -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
|
|
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
|
|
596
|
+
// Callees with file paths
|
|
574
597
|
if (graph.callees.length > 0) {
|
|
575
|
-
|
|
576
|
-
const
|
|
577
|
-
|
|
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) =>
|
|
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
|
|
73
|
-
const
|
|
74
|
-
|
|
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
|
-
|
|
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
|
@@ -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
|