grepmax 0.7.16 → 0.7.18
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
|
@@ -41,6 +41,13 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
41
41
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
42
42
|
});
|
|
43
43
|
};
|
|
44
|
+
var __asyncValues = (this && this.__asyncValues) || function (o) {
|
|
45
|
+
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
|
|
46
|
+
var m = o[Symbol.asyncIterator], i;
|
|
47
|
+
return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
|
|
48
|
+
function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
|
|
49
|
+
function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
|
|
50
|
+
};
|
|
44
51
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
52
|
exports.mcp = void 0;
|
|
46
53
|
exports.toStringArray = toStringArray;
|
|
@@ -57,6 +64,7 @@ const config_1 = require("../config");
|
|
|
57
64
|
const graph_builder_1 = require("../lib/graph/graph-builder");
|
|
58
65
|
const index_config_1 = require("../lib/index/index-config");
|
|
59
66
|
const syncer_1 = require("../lib/index/syncer");
|
|
67
|
+
const meta_cache_1 = require("../lib/store/meta-cache");
|
|
60
68
|
const searcher_1 = require("../lib/search/searcher");
|
|
61
69
|
const retriever_1 = require("../lib/skeleton/retriever");
|
|
62
70
|
const skeletonizer_1 = require("../lib/skeleton/skeletonizer");
|
|
@@ -212,6 +220,10 @@ const TOOLS = [
|
|
|
212
220
|
type: "number",
|
|
213
221
|
description: "Max files for directory mode (default 10, max 20). Ignored for single files.",
|
|
214
222
|
},
|
|
223
|
+
format: {
|
|
224
|
+
type: "string",
|
|
225
|
+
description: "Output format: 'text' (default) or 'json' (structured symbol list with names, lines, signatures).",
|
|
226
|
+
},
|
|
215
227
|
},
|
|
216
228
|
required: ["target"],
|
|
217
229
|
},
|
|
@@ -311,6 +323,23 @@ const TOOLS = [
|
|
|
311
323
|
required: ["file"],
|
|
312
324
|
},
|
|
313
325
|
},
|
|
326
|
+
{
|
|
327
|
+
name: "recent_changes",
|
|
328
|
+
description: "Show recently modified files in the index. Useful after pulls or merges to see what changed.",
|
|
329
|
+
inputSchema: {
|
|
330
|
+
type: "object",
|
|
331
|
+
properties: {
|
|
332
|
+
limit: {
|
|
333
|
+
type: "number",
|
|
334
|
+
description: "Max files to return (default 20)",
|
|
335
|
+
},
|
|
336
|
+
root: {
|
|
337
|
+
type: "string",
|
|
338
|
+
description: "Project root (defaults to current project)",
|
|
339
|
+
},
|
|
340
|
+
},
|
|
341
|
+
},
|
|
342
|
+
},
|
|
314
343
|
];
|
|
315
344
|
// ---------------------------------------------------------------------------
|
|
316
345
|
// Helpers
|
|
@@ -821,13 +850,16 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
821
850
|
targets = [target];
|
|
822
851
|
}
|
|
823
852
|
}
|
|
853
|
+
const fmt = typeof args.format === "string" ? args.format : "text";
|
|
824
854
|
// Generate skeletons for all targets
|
|
825
855
|
const parts = [];
|
|
856
|
+
const jsonFiles = [];
|
|
826
857
|
const skel = yield getSkeletonizer();
|
|
827
858
|
for (const t of targets) {
|
|
828
859
|
const absPath = path.resolve(projectRoot, t);
|
|
829
860
|
if (!fs.existsSync(absPath)) {
|
|
830
|
-
|
|
861
|
+
if (fmt !== "json")
|
|
862
|
+
parts.push(`// ${t} — file not found`);
|
|
831
863
|
continue;
|
|
832
864
|
}
|
|
833
865
|
// Read source for line annotations
|
|
@@ -836,38 +868,92 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
836
868
|
sourceContent = fs.readFileSync(absPath, "utf-8");
|
|
837
869
|
}
|
|
838
870
|
catch (_a) { }
|
|
871
|
+
let skeleton = "";
|
|
872
|
+
let language = "";
|
|
873
|
+
let tokenEstimate = 0;
|
|
839
874
|
// Try cached skeleton first
|
|
840
875
|
try {
|
|
841
876
|
const db = getVectorDb();
|
|
842
877
|
const cached = yield (0, retriever_1.getStoredSkeleton)(db, absPath);
|
|
843
878
|
if (cached) {
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
? annotateSkeletonLines(cached, sourceContent)
|
|
847
|
-
: cached;
|
|
848
|
-
parts.push(`// ${t} (~${tokens} tokens)\n\n${annotated}`);
|
|
849
|
-
continue;
|
|
879
|
+
skeleton = cached;
|
|
880
|
+
tokenEstimate = Math.ceil(cached.length / 4);
|
|
850
881
|
}
|
|
851
882
|
}
|
|
852
|
-
catch (_b) {
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
883
|
+
catch (_b) { }
|
|
884
|
+
// Generate live if no cache
|
|
885
|
+
if (!skeleton) {
|
|
886
|
+
try {
|
|
887
|
+
const content = sourceContent || fs.readFileSync(absPath, "utf-8");
|
|
888
|
+
const result = yield skel.skeletonizeFile(absPath, content);
|
|
889
|
+
if (result.success) {
|
|
890
|
+
skeleton = result.skeleton;
|
|
891
|
+
tokenEstimate = result.tokenEstimate;
|
|
892
|
+
language = result.language;
|
|
893
|
+
}
|
|
894
|
+
else {
|
|
895
|
+
if (fmt !== "json")
|
|
896
|
+
parts.push(`// ${t} — skeleton failed: ${result.error}`);
|
|
897
|
+
continue;
|
|
898
|
+
}
|
|
862
899
|
}
|
|
863
|
-
|
|
864
|
-
|
|
900
|
+
catch (e) {
|
|
901
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
902
|
+
if (fmt !== "json")
|
|
903
|
+
parts.push(`// ${t} — error: ${msg}`);
|
|
904
|
+
continue;
|
|
865
905
|
}
|
|
866
906
|
}
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
907
|
+
if (fmt === "json") {
|
|
908
|
+
// Extract structured symbols from annotated skeleton
|
|
909
|
+
const annotated = sourceContent
|
|
910
|
+
? annotateSkeletonLines(skeleton, sourceContent)
|
|
911
|
+
: skeleton;
|
|
912
|
+
const symbols = annotated
|
|
913
|
+
.split("\n")
|
|
914
|
+
.filter((l) => /^\s*\d+│/.test(l))
|
|
915
|
+
.map((l) => {
|
|
916
|
+
var _a, _b, _c;
|
|
917
|
+
const m = l.match(/^\s*(\d+)│(.+)/);
|
|
918
|
+
if (!m)
|
|
919
|
+
return null;
|
|
920
|
+
const line = Number.parseInt(m[1], 10);
|
|
921
|
+
const sig = m[2].trim();
|
|
922
|
+
const exported = sig.includes("export ");
|
|
923
|
+
const type = ((_a = sig.match(/\b(class|interface|type|function|def|fn|func)\b/)) === null || _a === void 0 ? void 0 : _a[1]) || "other";
|
|
924
|
+
const name = ((_b = sig.match(/(?:function|class|interface|type|def|fn|func)\s+(\w+)/)) === null || _b === void 0 ? void 0 : _b[1]) ||
|
|
925
|
+
((_c = sig.match(/^(?:async\s+)?(\w+)\s*[(<]/)) === null || _c === void 0 ? void 0 : _c[1]) ||
|
|
926
|
+
"unknown";
|
|
927
|
+
return {
|
|
928
|
+
name,
|
|
929
|
+
line,
|
|
930
|
+
signature: sig
|
|
931
|
+
.replace(/\s*\{?\s*\/\/.*$/, "")
|
|
932
|
+
.trim(),
|
|
933
|
+
type,
|
|
934
|
+
exported,
|
|
935
|
+
};
|
|
936
|
+
})
|
|
937
|
+
.filter((s) => s !== null && s.name !== "unknown");
|
|
938
|
+
jsonFiles.push({
|
|
939
|
+
file: t,
|
|
940
|
+
language: language || path.extname(t).slice(1),
|
|
941
|
+
tokenEstimate,
|
|
942
|
+
symbols,
|
|
943
|
+
});
|
|
870
944
|
}
|
|
945
|
+
else {
|
|
946
|
+
const annotated = sourceContent
|
|
947
|
+
? annotateSkeletonLines(skeleton, sourceContent)
|
|
948
|
+
: skeleton;
|
|
949
|
+
parts.push(`// ${t} (~${tokenEstimate} tokens)\n\n${annotated}`);
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
if (fmt === "json") {
|
|
953
|
+
const output = jsonFiles.length === 1
|
|
954
|
+
? jsonFiles[0]
|
|
955
|
+
: { files: jsonFiles };
|
|
956
|
+
return ok(JSON.stringify(output, null, 2));
|
|
871
957
|
}
|
|
872
958
|
return ok(parts.join("\n\n---\n\n"));
|
|
873
959
|
});
|
|
@@ -1344,6 +1430,76 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
1344
1430
|
}
|
|
1345
1431
|
});
|
|
1346
1432
|
}
|
|
1433
|
+
function formatTimeAgo(ms) {
|
|
1434
|
+
const sec = Math.floor(ms / 1000);
|
|
1435
|
+
if (sec < 60)
|
|
1436
|
+
return `${sec}s ago`;
|
|
1437
|
+
const min = Math.floor(sec / 60);
|
|
1438
|
+
if (min < 60)
|
|
1439
|
+
return `${min}m ago`;
|
|
1440
|
+
const hr = Math.floor(min / 60);
|
|
1441
|
+
if (hr < 24)
|
|
1442
|
+
return `${hr}h ago`;
|
|
1443
|
+
const days = Math.floor(hr / 24);
|
|
1444
|
+
return `${days}d ago`;
|
|
1445
|
+
}
|
|
1446
|
+
function handleRecentChanges(args) {
|
|
1447
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1448
|
+
var _a, e_1, _b, _c;
|
|
1449
|
+
const limit = Math.min(Math.max(Number(args.limit) || 20, 1), 50);
|
|
1450
|
+
const root = typeof args.root === "string"
|
|
1451
|
+
? path.resolve(args.root)
|
|
1452
|
+
: projectRoot;
|
|
1453
|
+
const prefix = root.endsWith("/") ? root : `${root}/`;
|
|
1454
|
+
try {
|
|
1455
|
+
const metaCache = new meta_cache_1.MetaCache(config_1.PATHS.lmdbPath);
|
|
1456
|
+
try {
|
|
1457
|
+
const files = [];
|
|
1458
|
+
try {
|
|
1459
|
+
for (var _d = true, _e = __asyncValues(metaCache.entries()), _f; _f = yield _e.next(), _a = _f.done, !_a; _d = true) {
|
|
1460
|
+
_c = _f.value;
|
|
1461
|
+
_d = false;
|
|
1462
|
+
const { path: p, entry, } = _c;
|
|
1463
|
+
if (p.startsWith(prefix)) {
|
|
1464
|
+
files.push({ path: p, mtimeMs: entry.mtimeMs });
|
|
1465
|
+
}
|
|
1466
|
+
}
|
|
1467
|
+
}
|
|
1468
|
+
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
1469
|
+
finally {
|
|
1470
|
+
try {
|
|
1471
|
+
if (!_d && !_a && (_b = _e.return)) yield _b.call(_e);
|
|
1472
|
+
}
|
|
1473
|
+
finally { if (e_1) throw e_1.error; }
|
|
1474
|
+
}
|
|
1475
|
+
files.sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
1476
|
+
const top = files.slice(0, limit);
|
|
1477
|
+
if (top.length === 0) {
|
|
1478
|
+
return ok(`No indexed files found for ${root}`);
|
|
1479
|
+
}
|
|
1480
|
+
const now = Date.now();
|
|
1481
|
+
const lines = [
|
|
1482
|
+
`Recent changes in ${path.basename(root)} (${top.length} most recent):\n`,
|
|
1483
|
+
];
|
|
1484
|
+
for (const f of top) {
|
|
1485
|
+
const rel = f.path.startsWith(prefix)
|
|
1486
|
+
? f.path.slice(prefix.length)
|
|
1487
|
+
: f.path;
|
|
1488
|
+
const ago = formatTimeAgo(now - f.mtimeMs);
|
|
1489
|
+
lines.push(` ${ago.padEnd(10)} ${rel}`);
|
|
1490
|
+
}
|
|
1491
|
+
return ok(lines.join("\n"));
|
|
1492
|
+
}
|
|
1493
|
+
finally {
|
|
1494
|
+
yield metaCache.close();
|
|
1495
|
+
}
|
|
1496
|
+
}
|
|
1497
|
+
catch (e) {
|
|
1498
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
1499
|
+
return err(`Recent changes failed: ${msg}`);
|
|
1500
|
+
}
|
|
1501
|
+
});
|
|
1502
|
+
}
|
|
1347
1503
|
// --- MCP server setup ---
|
|
1348
1504
|
const transport = new stdio_js_1.StdioServerTransport();
|
|
1349
1505
|
const server = new index_js_1.Server({
|
|
@@ -1381,6 +1537,8 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
1381
1537
|
return handleSummarizeProject(toolArgs);
|
|
1382
1538
|
case "related_files":
|
|
1383
1539
|
return handleRelatedFiles(toolArgs);
|
|
1540
|
+
case "recent_changes":
|
|
1541
|
+
return handleRecentChanges(toolArgs);
|
|
1384
1542
|
default:
|
|
1385
1543
|
return err(`Unknown tool: ${name}`);
|
|
1386
1544
|
}
|
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, mcp__grepmax__related_files, 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, mcp__grepmax__recent_changes, Bash(gmax:*), Read"
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
## What gmax does
|
|
@@ -67,6 +67,7 @@ Use sparingly. Prefer `semantic_search` when you know which directory to search.
|
|
|
67
67
|
File or directory structure — signatures with bodies collapsed (~4x fewer tokens).
|
|
68
68
|
- `target` (required): File path, directory path (e.g. "src/lib/search/"), or comma-separated files
|
|
69
69
|
- `limit` (optional): Max files for directory mode (default 10, max 20)
|
|
70
|
+
- `format` (optional): `"text"` (default) or `"json"` (structured symbol list with name, line, signature, type, exported)
|
|
70
71
|
|
|
71
72
|
### trace_calls
|
|
72
73
|
Call graph — who imports a symbol, who calls it, and what it calls. Includes file:line locations. Unscoped — follows calls across all indexed directories.
|
|
@@ -92,6 +93,11 @@ Find files related to a given file by shared symbol references. Shows dependenci
|
|
|
92
93
|
- `file` (required): File path relative to project root
|
|
93
94
|
- `limit` (optional): Max results per direction (default 10)
|
|
94
95
|
|
|
96
|
+
### recent_changes
|
|
97
|
+
Show recently modified files in the index. Useful after pulls or merges to see what changed.
|
|
98
|
+
- `limit` (optional): Max files (default 20)
|
|
99
|
+
- `root` (optional): Project root (defaults to current project)
|
|
100
|
+
|
|
95
101
|
### index_status
|
|
96
102
|
Check centralized index health — chunks, files, indexed directories, model info, watcher status.
|
|
97
103
|
|