grepmax 0.7.9 → 0.7.11
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
|
@@ -124,6 +124,10 @@ const TOOLS = [
|
|
|
124
124
|
type: "number",
|
|
125
125
|
description: "Include N lines before and after the chunk (like grep -C). Only with detail 'code' or 'full'. Max 20.",
|
|
126
126
|
},
|
|
127
|
+
mode: {
|
|
128
|
+
type: "string",
|
|
129
|
+
description: "Search mode: 'default' (semantic only) or 'symbol' (semantic + call graph trace appended). Use 'symbol' when query is a function/class name.",
|
|
130
|
+
},
|
|
127
131
|
},
|
|
128
132
|
required: ["query"],
|
|
129
133
|
},
|
|
@@ -264,6 +268,19 @@ const TOOLS = [
|
|
|
264
268
|
},
|
|
265
269
|
},
|
|
266
270
|
},
|
|
271
|
+
{
|
|
272
|
+
name: "summarize_project",
|
|
273
|
+
description: "High-level overview of an indexed project — languages, directory structure, role distribution, key symbols, and entry points. Use when first exploring a codebase.",
|
|
274
|
+
inputSchema: {
|
|
275
|
+
type: "object",
|
|
276
|
+
properties: {
|
|
277
|
+
root: {
|
|
278
|
+
type: "string",
|
|
279
|
+
description: "Project root (absolute path). Defaults to current project.",
|
|
280
|
+
},
|
|
281
|
+
},
|
|
282
|
+
},
|
|
283
|
+
},
|
|
267
284
|
];
|
|
268
285
|
// ---------------------------------------------------------------------------
|
|
269
286
|
// Helpers
|
|
@@ -609,7 +626,50 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
609
626
|
return true;
|
|
610
627
|
});
|
|
611
628
|
}
|
|
612
|
-
|
|
629
|
+
let output = results.map((r) => r.text).join("\n\n");
|
|
630
|
+
// Symbol mode: append call graph
|
|
631
|
+
const mode = typeof args.mode === "string" ? args.mode : "default";
|
|
632
|
+
if (mode === "symbol" && !searchAll) {
|
|
633
|
+
try {
|
|
634
|
+
const db = getVectorDb();
|
|
635
|
+
const builder = new graph_builder_1.GraphBuilder(db);
|
|
636
|
+
const graph = yield builder.buildGraph(query);
|
|
637
|
+
if (graph.center) {
|
|
638
|
+
const traceLines = ["", "--- Call graph ---"];
|
|
639
|
+
const centerRel = graph.center.file.startsWith(projectRoot)
|
|
640
|
+
? graph.center.file.slice(projectRoot.length + 1)
|
|
641
|
+
: graph.center.file;
|
|
642
|
+
traceLines.push(`${graph.center.symbol} [${graph.center.role}] ${centerRel}:${graph.center.line + 1}`);
|
|
643
|
+
if (graph.callers.length > 0) {
|
|
644
|
+
traceLines.push("Callers:");
|
|
645
|
+
for (const caller of graph.callers) {
|
|
646
|
+
const rel = caller.file.startsWith(projectRoot)
|
|
647
|
+
? caller.file.slice(projectRoot.length + 1)
|
|
648
|
+
: caller.file;
|
|
649
|
+
traceLines.push(` <- ${caller.symbol} ${rel}:${caller.line + 1}`);
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
if (graph.callees.length > 0) {
|
|
653
|
+
traceLines.push("Calls:");
|
|
654
|
+
for (const callee of graph.callees.slice(0, 15)) {
|
|
655
|
+
if (callee.file) {
|
|
656
|
+
const rel = callee.file.startsWith(projectRoot)
|
|
657
|
+
? callee.file.slice(projectRoot.length + 1)
|
|
658
|
+
: callee.file;
|
|
659
|
+
traceLines.push(` -> ${callee.symbol} ${rel}:${callee.line + 1}`);
|
|
660
|
+
}
|
|
661
|
+
else {
|
|
662
|
+
traceLines.push(` -> ${callee.symbol} (not indexed)`);
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
output += `\n${traceLines.join("\n")}`;
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
catch (_b) {
|
|
670
|
+
// Trace failed — return search results without trace
|
|
671
|
+
}
|
|
672
|
+
}
|
|
613
673
|
if ((_a = result.warnings) === null || _a === void 0 ? void 0 : _a.length) {
|
|
614
674
|
return ok(`${result.warnings.join("\n")}\n\n${output}`);
|
|
615
675
|
}
|
|
@@ -951,6 +1011,130 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
951
1011
|
}
|
|
952
1012
|
});
|
|
953
1013
|
}
|
|
1014
|
+
function handleSummarizeProject(args) {
|
|
1015
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1016
|
+
var _a;
|
|
1017
|
+
const root = typeof args.root === "string"
|
|
1018
|
+
? path.resolve(args.root)
|
|
1019
|
+
: projectRoot;
|
|
1020
|
+
const prefix = root.endsWith("/") ? root : `${root}/`;
|
|
1021
|
+
const projectName = path.basename(root);
|
|
1022
|
+
try {
|
|
1023
|
+
const db = getVectorDb();
|
|
1024
|
+
const table = yield db.ensureTable();
|
|
1025
|
+
const rows = yield table
|
|
1026
|
+
.query()
|
|
1027
|
+
.select([
|
|
1028
|
+
"path",
|
|
1029
|
+
"role",
|
|
1030
|
+
"is_exported",
|
|
1031
|
+
"complexity",
|
|
1032
|
+
"defined_symbols",
|
|
1033
|
+
"referenced_symbols",
|
|
1034
|
+
])
|
|
1035
|
+
.where(`path LIKE '${(0, filter_builder_1.escapeSqlString)(prefix)}%'`)
|
|
1036
|
+
.limit(200000)
|
|
1037
|
+
.toArray();
|
|
1038
|
+
if (rows.length === 0) {
|
|
1039
|
+
return ok(`No indexed data found for ${root}. Run: gmax index --path ${root}`);
|
|
1040
|
+
}
|
|
1041
|
+
const files = new Set();
|
|
1042
|
+
const extCounts = new Map();
|
|
1043
|
+
const dirCounts = new Map();
|
|
1044
|
+
const roleCounts = new Map();
|
|
1045
|
+
const symbolRefs = new Map();
|
|
1046
|
+
const entryPoints = [];
|
|
1047
|
+
for (const row of rows) {
|
|
1048
|
+
const p = String(row.path || "");
|
|
1049
|
+
const role = String(row.role || "IMPLEMENTATION");
|
|
1050
|
+
const exported = Boolean(row.is_exported);
|
|
1051
|
+
const complexity = Number(row.complexity || 0);
|
|
1052
|
+
const defs = toStringArray(row.defined_symbols);
|
|
1053
|
+
const refs = toStringArray(row.referenced_symbols);
|
|
1054
|
+
files.add(p);
|
|
1055
|
+
const ext = path.extname(p).toLowerCase() || path.basename(p);
|
|
1056
|
+
extCounts.set(ext, (extCounts.get(ext) || 0) + 1);
|
|
1057
|
+
const rel = p.startsWith(prefix)
|
|
1058
|
+
? p.slice(prefix.length)
|
|
1059
|
+
: p;
|
|
1060
|
+
const parts = rel.split("/");
|
|
1061
|
+
const dir = parts.length > 2
|
|
1062
|
+
? `${parts.slice(0, 2).join("/")}/`
|
|
1063
|
+
: parts.length > 1
|
|
1064
|
+
? `${parts[0]}/`
|
|
1065
|
+
: "(root)";
|
|
1066
|
+
if (!dirCounts.has(dir)) {
|
|
1067
|
+
dirCounts.set(dir, { files: new Set(), chunks: 0 });
|
|
1068
|
+
}
|
|
1069
|
+
const dc = dirCounts.get(dir);
|
|
1070
|
+
dc.files.add(p);
|
|
1071
|
+
dc.chunks++;
|
|
1072
|
+
roleCounts.set(role, (roleCounts.get(role) || 0) + 1);
|
|
1073
|
+
for (const ref of refs) {
|
|
1074
|
+
symbolRefs.set(ref, (symbolRefs.get(ref) || 0) + 1);
|
|
1075
|
+
}
|
|
1076
|
+
if (exported &&
|
|
1077
|
+
role === "ORCHESTRATION" &&
|
|
1078
|
+
complexity >= 5 &&
|
|
1079
|
+
defs.length > 0) {
|
|
1080
|
+
const relPath = p.startsWith(prefix)
|
|
1081
|
+
? p.slice(prefix.length)
|
|
1082
|
+
: p;
|
|
1083
|
+
entryPoints.push({ symbol: defs[0], path: relPath });
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
const lines = [];
|
|
1087
|
+
const projects = (0, project_registry_1.listProjects)();
|
|
1088
|
+
const proj = projects.find((p) => p.root === root);
|
|
1089
|
+
lines.push(`Project: ${projectName} (${root})`);
|
|
1090
|
+
lines.push(`Last indexed: ${(_a = proj === null || proj === void 0 ? void 0 : proj.lastIndexed) !== null && _a !== void 0 ? _a : "unknown"} • ${rows.length} chunks • ${files.size} files`);
|
|
1091
|
+
lines.push("");
|
|
1092
|
+
const extEntries = Array.from(extCounts.entries())
|
|
1093
|
+
.sort((a, b) => b[1] - a[1])
|
|
1094
|
+
.slice(0, 8);
|
|
1095
|
+
const langLine = extEntries
|
|
1096
|
+
.map(([ext, count]) => `${ext} (${Math.round((count / rows.length) * 100)}%)`)
|
|
1097
|
+
.join(", ");
|
|
1098
|
+
lines.push(`Languages: ${langLine}`);
|
|
1099
|
+
lines.push("");
|
|
1100
|
+
lines.push("Directory structure:");
|
|
1101
|
+
const dirEntries = Array.from(dirCounts.entries())
|
|
1102
|
+
.sort((a, b) => b[1].chunks - a[1].chunks)
|
|
1103
|
+
.slice(0, 12);
|
|
1104
|
+
for (const [dir, data] of dirEntries) {
|
|
1105
|
+
lines.push(` ${dir.padEnd(25)} (${data.files.size} files, ${data.chunks} chunks)`);
|
|
1106
|
+
}
|
|
1107
|
+
lines.push("");
|
|
1108
|
+
const roleEntries = Array.from(roleCounts.entries()).sort((a, b) => b[1] - a[1]);
|
|
1109
|
+
const roleLine = roleEntries
|
|
1110
|
+
.map(([role, count]) => `${Math.round((count / rows.length) * 100)}% ${role}`)
|
|
1111
|
+
.join(", ");
|
|
1112
|
+
lines.push(`Roles: ${roleLine}`);
|
|
1113
|
+
lines.push("");
|
|
1114
|
+
const topSymbols = Array.from(symbolRefs.entries())
|
|
1115
|
+
.sort((a, b) => b[1] - a[1])
|
|
1116
|
+
.slice(0, 8);
|
|
1117
|
+
if (topSymbols.length > 0) {
|
|
1118
|
+
lines.push("Key symbols (by reference count):");
|
|
1119
|
+
for (const [sym, count] of topSymbols) {
|
|
1120
|
+
lines.push(` ${sym.padEnd(25)} (referenced ${count}x)`);
|
|
1121
|
+
}
|
|
1122
|
+
lines.push("");
|
|
1123
|
+
}
|
|
1124
|
+
if (entryPoints.length > 0) {
|
|
1125
|
+
lines.push("Entry points (exported orchestration):");
|
|
1126
|
+
for (const ep of entryPoints.slice(0, 10)) {
|
|
1127
|
+
lines.push(` ${ep.symbol.padEnd(25)} ${ep.path}`);
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
return ok(lines.join("\n"));
|
|
1131
|
+
}
|
|
1132
|
+
catch (e) {
|
|
1133
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
1134
|
+
return err(`Project summary failed: ${msg}`);
|
|
1135
|
+
}
|
|
1136
|
+
});
|
|
1137
|
+
}
|
|
954
1138
|
// --- MCP server setup ---
|
|
955
1139
|
const transport = new stdio_js_1.StdioServerTransport();
|
|
956
1140
|
const server = new index_js_1.Server({
|
|
@@ -984,6 +1168,8 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
984
1168
|
return handleIndexStatus();
|
|
985
1169
|
case "summarize_directory":
|
|
986
1170
|
return handleSummarizeDirectory(toolArgs);
|
|
1171
|
+
case "summarize_project":
|
|
1172
|
+
return handleSummarizeProject(toolArgs);
|
|
987
1173
|
default:
|
|
988
1174
|
return err(`Unknown tool: ${name}`);
|
|
989
1175
|
}
|
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, 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, Bash(gmax:*), Read"
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
## What gmax does
|
|
@@ -47,6 +47,7 @@ Parameters:
|
|
|
47
47
|
- `exclude` (optional): Exclude files under this path prefix (e.g. "tests/" or "dist/")
|
|
48
48
|
- `language` (optional): Filter by file extension (e.g. "ts", "py", "go"). Omit the dot.
|
|
49
49
|
- `role` (optional): Filter by chunk role: "ORCHESTRATION" (logic/flow), "DEFINITION" (types), or "IMPLEMENTATION"
|
|
50
|
+
- `mode` (optional): `"default"` (semantic only) or `"symbol"` (semantic + call graph appended). Use "symbol" when query is a function or class name — gets search results + callers/callees in one call.
|
|
50
51
|
|
|
51
52
|
**When to use which mode:**
|
|
52
53
|
- `pointer` — navigation, finding locations, understanding architecture
|
|
@@ -78,6 +79,10 @@ List indexed symbols with definition locations, role, and export status.
|
|
|
78
79
|
|
|
79
80
|
Output: `symbolName [ORCH] exported src/path/file.ts:42`
|
|
80
81
|
|
|
82
|
+
### summarize_project
|
|
83
|
+
High-level project overview — languages, directory structure, role distribution, key symbols, entry points. Use when first exploring a new codebase.
|
|
84
|
+
- `root` (optional): Project root path. Defaults to current project.
|
|
85
|
+
|
|
81
86
|
### index_status
|
|
82
87
|
Check centralized index health — chunks, files, indexed directories, model info, watcher status.
|
|
83
88
|
|