grepmax 0.7.12 → 0.7.14
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
CHANGED
|
@@ -226,6 +226,10 @@ const TOOLS = [
|
|
|
226
226
|
type: "string",
|
|
227
227
|
description: "The function, method, or class name to trace (e.g. 'handleAuth')",
|
|
228
228
|
},
|
|
229
|
+
depth: {
|
|
230
|
+
type: "number",
|
|
231
|
+
description: "Traversal depth for callers (default 1, max 3). depth: 2 shows callers-of-callers.",
|
|
232
|
+
},
|
|
229
233
|
},
|
|
230
234
|
required: ["symbol"],
|
|
231
235
|
},
|
|
@@ -289,6 +293,24 @@ const TOOLS = [
|
|
|
289
293
|
},
|
|
290
294
|
},
|
|
291
295
|
},
|
|
296
|
+
{
|
|
297
|
+
name: "related_files",
|
|
298
|
+
description: "Find files related to a given file by shared symbol references. Shows dependencies (what this file calls) and dependents (what calls this file).",
|
|
299
|
+
inputSchema: {
|
|
300
|
+
type: "object",
|
|
301
|
+
properties: {
|
|
302
|
+
file: {
|
|
303
|
+
type: "string",
|
|
304
|
+
description: "File path relative to project root (e.g. 'src/lib/index/syncer.ts')",
|
|
305
|
+
},
|
|
306
|
+
limit: {
|
|
307
|
+
type: "number",
|
|
308
|
+
description: "Max related files per direction (default 10)",
|
|
309
|
+
},
|
|
310
|
+
},
|
|
311
|
+
required: ["file"],
|
|
312
|
+
},
|
|
313
|
+
},
|
|
292
314
|
];
|
|
293
315
|
// ---------------------------------------------------------------------------
|
|
294
316
|
// Helpers
|
|
@@ -861,20 +883,29 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
861
883
|
try {
|
|
862
884
|
const db = getVectorDb();
|
|
863
885
|
const builder = new graph_builder_1.GraphBuilder(db);
|
|
864
|
-
const
|
|
886
|
+
const depth = Math.min(Math.max(Number(args.depth) || 1, 1), 3);
|
|
887
|
+
const graph = yield builder.buildGraphMultiHop(symbol, depth);
|
|
865
888
|
if (!graph.center) {
|
|
866
889
|
return ok(`Symbol '${symbol}' not found in the index.`);
|
|
867
890
|
}
|
|
868
891
|
const lines = [];
|
|
869
892
|
// Center
|
|
870
893
|
lines.push(`${graph.center.symbol} [${graph.center.role}] ${graph.center.file}:${graph.center.line + 1}`);
|
|
871
|
-
// Callers
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
894
|
+
// Callers (recursive tree)
|
|
895
|
+
function formatCallerTree(trees, indent) {
|
|
896
|
+
for (const t of trees) {
|
|
897
|
+
const rel = t.node.file.startsWith(projectRoot)
|
|
898
|
+
? t.node.file.slice(projectRoot.length + 1)
|
|
899
|
+
: t.node.file;
|
|
900
|
+
const pad = " ".repeat(indent);
|
|
901
|
+
lines.push(`${pad}<- ${t.node.symbol} ${rel}:${t.node.line + 1}`);
|
|
902
|
+
formatCallerTree(t.callers, indent + 1);
|
|
876
903
|
}
|
|
877
904
|
}
|
|
905
|
+
if (graph.callerTree.length > 0) {
|
|
906
|
+
lines.push("Callers:");
|
|
907
|
+
formatCallerTree(graph.callerTree, 1);
|
|
908
|
+
}
|
|
878
909
|
else {
|
|
879
910
|
lines.push("Callers: none");
|
|
880
911
|
}
|
|
@@ -1197,6 +1228,107 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
1197
1228
|
}
|
|
1198
1229
|
});
|
|
1199
1230
|
}
|
|
1231
|
+
function handleRelatedFiles(args) {
|
|
1232
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1233
|
+
const file = String(args.file || "");
|
|
1234
|
+
if (!file)
|
|
1235
|
+
return err("Missing required parameter: file");
|
|
1236
|
+
const limit = Math.min(Math.max(Number(args.limit) || 10, 1), 25);
|
|
1237
|
+
const absPath = path.resolve(projectRoot, file);
|
|
1238
|
+
try {
|
|
1239
|
+
const db = getVectorDb();
|
|
1240
|
+
const table = yield db.ensureTable();
|
|
1241
|
+
const fileChunks = yield table
|
|
1242
|
+
.query()
|
|
1243
|
+
.select(["defined_symbols", "referenced_symbols"])
|
|
1244
|
+
.where(`path = '${(0, filter_builder_1.escapeSqlString)(absPath)}'`)
|
|
1245
|
+
.toArray();
|
|
1246
|
+
if (fileChunks.length === 0) {
|
|
1247
|
+
return ok(`File not found in index: ${file}`);
|
|
1248
|
+
}
|
|
1249
|
+
const definedHere = new Set();
|
|
1250
|
+
const referencedHere = new Set();
|
|
1251
|
+
for (const chunk of fileChunks) {
|
|
1252
|
+
for (const s of toStringArray(chunk.defined_symbols))
|
|
1253
|
+
definedHere.add(s);
|
|
1254
|
+
for (const s of toStringArray(chunk.referenced_symbols))
|
|
1255
|
+
referencedHere.add(s);
|
|
1256
|
+
}
|
|
1257
|
+
// Dependencies: files that DEFINE symbols this file REFERENCES
|
|
1258
|
+
const depCounts = new Map();
|
|
1259
|
+
for (const sym of referencedHere) {
|
|
1260
|
+
if (definedHere.has(sym))
|
|
1261
|
+
continue;
|
|
1262
|
+
const rows = yield table
|
|
1263
|
+
.query()
|
|
1264
|
+
.select(["path"])
|
|
1265
|
+
.where(`array_contains(defined_symbols, '${(0, filter_builder_1.escapeSqlString)(sym)}')`)
|
|
1266
|
+
.limit(3)
|
|
1267
|
+
.toArray();
|
|
1268
|
+
for (const row of rows) {
|
|
1269
|
+
const p = String(row.path || "");
|
|
1270
|
+
if (p === absPath)
|
|
1271
|
+
continue;
|
|
1272
|
+
depCounts.set(p, (depCounts.get(p) || 0) + 1);
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
// Dependents: files that REFERENCE symbols this file DEFINES
|
|
1276
|
+
const revCounts = new Map();
|
|
1277
|
+
for (const sym of definedHere) {
|
|
1278
|
+
const rows = yield table
|
|
1279
|
+
.query()
|
|
1280
|
+
.select(["path"])
|
|
1281
|
+
.where(`array_contains(referenced_symbols, '${(0, filter_builder_1.escapeSqlString)(sym)}')`)
|
|
1282
|
+
.limit(20)
|
|
1283
|
+
.toArray();
|
|
1284
|
+
for (const row of rows) {
|
|
1285
|
+
const p = String(row.path || "");
|
|
1286
|
+
if (p === absPath)
|
|
1287
|
+
continue;
|
|
1288
|
+
revCounts.set(p, (revCounts.get(p) || 0) + 1);
|
|
1289
|
+
}
|
|
1290
|
+
}
|
|
1291
|
+
const lines = [];
|
|
1292
|
+
lines.push(`Related files for ${file}:\n`);
|
|
1293
|
+
const topDeps = Array.from(depCounts.entries())
|
|
1294
|
+
.sort((a, b) => b[1] - a[1])
|
|
1295
|
+
.slice(0, limit);
|
|
1296
|
+
if (topDeps.length > 0) {
|
|
1297
|
+
lines.push("Dependencies (files this imports/calls):");
|
|
1298
|
+
for (const [p, count] of topDeps) {
|
|
1299
|
+
const rel = p.startsWith(`${projectRoot}/`)
|
|
1300
|
+
? p.slice(projectRoot.length + 1)
|
|
1301
|
+
: p;
|
|
1302
|
+
lines.push(` ${rel.padEnd(40)} (${count} shared symbol${count > 1 ? "s" : ""})`);
|
|
1303
|
+
}
|
|
1304
|
+
}
|
|
1305
|
+
else {
|
|
1306
|
+
lines.push("Dependencies: none found");
|
|
1307
|
+
}
|
|
1308
|
+
lines.push("");
|
|
1309
|
+
const topRevs = Array.from(revCounts.entries())
|
|
1310
|
+
.sort((a, b) => b[1] - a[1])
|
|
1311
|
+
.slice(0, limit);
|
|
1312
|
+
if (topRevs.length > 0) {
|
|
1313
|
+
lines.push("Dependents (files that call this):");
|
|
1314
|
+
for (const [p, count] of topRevs) {
|
|
1315
|
+
const rel = p.startsWith(`${projectRoot}/`)
|
|
1316
|
+
? p.slice(projectRoot.length + 1)
|
|
1317
|
+
: p;
|
|
1318
|
+
lines.push(` ${rel.padEnd(40)} (${count} shared symbol${count > 1 ? "s" : ""})`);
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
else {
|
|
1322
|
+
lines.push("Dependents: none found");
|
|
1323
|
+
}
|
|
1324
|
+
return ok(lines.join("\n"));
|
|
1325
|
+
}
|
|
1326
|
+
catch (e) {
|
|
1327
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
1328
|
+
return err(`Related files failed: ${msg}`);
|
|
1329
|
+
}
|
|
1330
|
+
});
|
|
1331
|
+
}
|
|
1200
1332
|
// --- MCP server setup ---
|
|
1201
1333
|
const transport = new stdio_js_1.StdioServerTransport();
|
|
1202
1334
|
const server = new index_js_1.Server({
|
|
@@ -1232,6 +1364,8 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
1232
1364
|
return handleSummarizeDirectory(toolArgs);
|
|
1233
1365
|
case "summarize_project":
|
|
1234
1366
|
return handleSummarizeProject(toolArgs);
|
|
1367
|
+
case "related_files":
|
|
1368
|
+
return handleRelatedFiles(toolArgs);
|
|
1235
1369
|
default:
|
|
1236
1370
|
return err(`Unknown tool: ${name}`);
|
|
1237
1371
|
}
|
|
@@ -105,6 +105,40 @@ class GraphBuilder {
|
|
|
105
105
|
return { center, callers, callees: calleeNodes };
|
|
106
106
|
});
|
|
107
107
|
}
|
|
108
|
+
buildGraphMultiHop(symbol, depth) {
|
|
109
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
110
|
+
const graph = yield this.buildGraph(symbol);
|
|
111
|
+
if (depth <= 1 || !graph.center) {
|
|
112
|
+
return {
|
|
113
|
+
center: graph.center,
|
|
114
|
+
callerTree: graph.callers.map((c) => ({ node: c, callers: [] })),
|
|
115
|
+
callees: graph.callees,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
const visited = new Set([symbol]);
|
|
119
|
+
const callerTree = yield this.expandCallers(graph.callers, depth - 1, visited);
|
|
120
|
+
return { center: graph.center, callerTree, callees: graph.callees };
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
expandCallers(callers, remainingDepth, visited) {
|
|
124
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
125
|
+
const trees = [];
|
|
126
|
+
for (const caller of callers) {
|
|
127
|
+
if (visited.has(caller.symbol)) {
|
|
128
|
+
trees.push({ node: caller, callers: [] });
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
visited.add(caller.symbol);
|
|
132
|
+
let subCallers = [];
|
|
133
|
+
if (remainingDepth > 0) {
|
|
134
|
+
const upstreamCallers = yield this.getCallers(caller.symbol);
|
|
135
|
+
subCallers = yield this.expandCallers(upstreamCallers, remainingDepth - 1, visited);
|
|
136
|
+
}
|
|
137
|
+
trees.push({ node: caller, callers: subCallers });
|
|
138
|
+
}
|
|
139
|
+
return trees;
|
|
140
|
+
});
|
|
141
|
+
}
|
|
108
142
|
mapRowToNode(row, targetSymbol, type) {
|
|
109
143
|
// Helper to convert Arrow Vector to array if needed
|
|
110
144
|
const toArray = (val) => {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: grepmax
|
|
3
3
|
description: Semantic code search. Use alongside grep - grep for exact strings, gmax for concepts.
|
|
4
|
-
allowed-tools: "mcp__grepmax__semantic_search, mcp__grepmax__search_all, mcp__grepmax__code_skeleton, mcp__grepmax__trace_calls, mcp__grepmax__list_symbols, mcp__grepmax__index_status, mcp__grepmax__summarize_directory, mcp__grepmax__summarize_project, Bash(gmax:*), Read"
|
|
4
|
+
allowed-tools: "mcp__grepmax__semantic_search, mcp__grepmax__search_all, mcp__grepmax__code_skeleton, mcp__grepmax__trace_calls, mcp__grepmax__list_symbols, mcp__grepmax__index_status, mcp__grepmax__summarize_directory, mcp__grepmax__summarize_project, mcp__grepmax__related_files, Bash(gmax:*), Read"
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
## What gmax does
|
|
@@ -71,6 +71,7 @@ File or directory structure — signatures with bodies collapsed (~4x fewer toke
|
|
|
71
71
|
### trace_calls
|
|
72
72
|
Call graph — who calls a symbol and what it calls. Callers and callees include file:line locations. Unscoped — follows calls across all indexed directories.
|
|
73
73
|
- `symbol` (required): Function/method/class name
|
|
74
|
+
- `depth` (optional): Traversal depth for callers (default 1, max 3). depth: 2 shows callers-of-callers with indentation.
|
|
74
75
|
|
|
75
76
|
### list_symbols
|
|
76
77
|
List indexed symbols with definition locations, role, and export status.
|
|
@@ -84,6 +85,11 @@ Output: `symbolName [ORCH] exported src/path/file.ts:42`
|
|
|
84
85
|
High-level project overview — languages, directory structure, role distribution, key symbols, entry points. Use when first exploring a new codebase.
|
|
85
86
|
- `root` (optional): Project root path. Defaults to current project.
|
|
86
87
|
|
|
88
|
+
### related_files
|
|
89
|
+
Find files related to a given file by shared symbol references. Shows dependencies (what this file calls) and dependents (what calls this file).
|
|
90
|
+
- `file` (required): File path relative to project root
|
|
91
|
+
- `limit` (optional): Max results per direction (default 10)
|
|
92
|
+
|
|
87
93
|
### index_status
|
|
88
94
|
Check centralized index health — chunks, files, indexed directories, model info, watcher status.
|
|
89
95
|
|