grepmax 0.7.16 → 0.7.17
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
|
@@ -212,6 +212,10 @@ const TOOLS = [
|
|
|
212
212
|
type: "number",
|
|
213
213
|
description: "Max files for directory mode (default 10, max 20). Ignored for single files.",
|
|
214
214
|
},
|
|
215
|
+
format: {
|
|
216
|
+
type: "string",
|
|
217
|
+
description: "Output format: 'text' (default) or 'json' (structured symbol list with names, lines, signatures).",
|
|
218
|
+
},
|
|
215
219
|
},
|
|
216
220
|
required: ["target"],
|
|
217
221
|
},
|
|
@@ -821,13 +825,16 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
821
825
|
targets = [target];
|
|
822
826
|
}
|
|
823
827
|
}
|
|
828
|
+
const fmt = typeof args.format === "string" ? args.format : "text";
|
|
824
829
|
// Generate skeletons for all targets
|
|
825
830
|
const parts = [];
|
|
831
|
+
const jsonFiles = [];
|
|
826
832
|
const skel = yield getSkeletonizer();
|
|
827
833
|
for (const t of targets) {
|
|
828
834
|
const absPath = path.resolve(projectRoot, t);
|
|
829
835
|
if (!fs.existsSync(absPath)) {
|
|
830
|
-
|
|
836
|
+
if (fmt !== "json")
|
|
837
|
+
parts.push(`// ${t} — file not found`);
|
|
831
838
|
continue;
|
|
832
839
|
}
|
|
833
840
|
// Read source for line annotations
|
|
@@ -836,39 +843,93 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
836
843
|
sourceContent = fs.readFileSync(absPath, "utf-8");
|
|
837
844
|
}
|
|
838
845
|
catch (_a) { }
|
|
846
|
+
let skeleton = "";
|
|
847
|
+
let language = "";
|
|
848
|
+
let tokenEstimate = 0;
|
|
839
849
|
// Try cached skeleton first
|
|
840
850
|
try {
|
|
841
851
|
const db = getVectorDb();
|
|
842
852
|
const cached = yield (0, retriever_1.getStoredSkeleton)(db, absPath);
|
|
843
853
|
if (cached) {
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
? annotateSkeletonLines(cached, sourceContent)
|
|
847
|
-
: cached;
|
|
848
|
-
parts.push(`// ${t} (~${tokens} tokens)\n\n${annotated}`);
|
|
849
|
-
continue;
|
|
854
|
+
skeleton = cached;
|
|
855
|
+
tokenEstimate = Math.ceil(cached.length / 4);
|
|
850
856
|
}
|
|
851
857
|
}
|
|
852
|
-
catch (_b) {
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
858
|
+
catch (_b) { }
|
|
859
|
+
// Generate live if no cache
|
|
860
|
+
if (!skeleton) {
|
|
861
|
+
try {
|
|
862
|
+
const content = sourceContent || fs.readFileSync(absPath, "utf-8");
|
|
863
|
+
const result = yield skel.skeletonizeFile(absPath, content);
|
|
864
|
+
if (result.success) {
|
|
865
|
+
skeleton = result.skeleton;
|
|
866
|
+
tokenEstimate = result.tokenEstimate;
|
|
867
|
+
language = result.language;
|
|
868
|
+
}
|
|
869
|
+
else {
|
|
870
|
+
if (fmt !== "json")
|
|
871
|
+
parts.push(`// ${t} — skeleton failed: ${result.error}`);
|
|
872
|
+
continue;
|
|
873
|
+
}
|
|
862
874
|
}
|
|
863
|
-
|
|
864
|
-
|
|
875
|
+
catch (e) {
|
|
876
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
877
|
+
if (fmt !== "json")
|
|
878
|
+
parts.push(`// ${t} — error: ${msg}`);
|
|
879
|
+
continue;
|
|
865
880
|
}
|
|
866
881
|
}
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
882
|
+
if (fmt === "json") {
|
|
883
|
+
// Extract structured symbols from annotated skeleton
|
|
884
|
+
const annotated = sourceContent
|
|
885
|
+
? annotateSkeletonLines(skeleton, sourceContent)
|
|
886
|
+
: skeleton;
|
|
887
|
+
const symbols = annotated
|
|
888
|
+
.split("\n")
|
|
889
|
+
.filter((l) => /^\s*\d+│/.test(l))
|
|
890
|
+
.map((l) => {
|
|
891
|
+
var _a, _b, _c;
|
|
892
|
+
const m = l.match(/^\s*(\d+)│(.+)/);
|
|
893
|
+
if (!m)
|
|
894
|
+
return null;
|
|
895
|
+
const line = Number.parseInt(m[1], 10);
|
|
896
|
+
const sig = m[2].trim();
|
|
897
|
+
const exported = sig.includes("export ");
|
|
898
|
+
const type = ((_a = sig.match(/\b(class|interface|type|function|def|fn|func)\b/)) === null || _a === void 0 ? void 0 : _a[1]) || "other";
|
|
899
|
+
const name = ((_b = sig.match(/(?:function|class|interface|type|def|fn|func)\s+(\w+)/)) === null || _b === void 0 ? void 0 : _b[1]) ||
|
|
900
|
+
((_c = sig.match(/^(?:async\s+)?(\w+)\s*[(<]/)) === null || _c === void 0 ? void 0 : _c[1]) ||
|
|
901
|
+
"unknown";
|
|
902
|
+
return {
|
|
903
|
+
name,
|
|
904
|
+
line,
|
|
905
|
+
signature: sig
|
|
906
|
+
.replace(/\s*\{?\s*\/\/.*$/, "")
|
|
907
|
+
.trim(),
|
|
908
|
+
type,
|
|
909
|
+
exported,
|
|
910
|
+
};
|
|
911
|
+
})
|
|
912
|
+
.filter((s) => s !== null && s.name !== "unknown");
|
|
913
|
+
jsonFiles.push({
|
|
914
|
+
file: t,
|
|
915
|
+
language: language || path.extname(t).slice(1),
|
|
916
|
+
tokenEstimate,
|
|
917
|
+
symbols,
|
|
918
|
+
});
|
|
919
|
+
}
|
|
920
|
+
else {
|
|
921
|
+
const annotated = sourceContent
|
|
922
|
+
? annotateSkeletonLines(skeleton, sourceContent)
|
|
923
|
+
: skeleton;
|
|
924
|
+
parts.push(`// ${t} (~${tokenEstimate} tokens)\n\n${annotated}`);
|
|
870
925
|
}
|
|
871
926
|
}
|
|
927
|
+
if (fmt === "json") {
|
|
928
|
+
const output = jsonFiles.length === 1
|
|
929
|
+
? jsonFiles[0]
|
|
930
|
+
: { files: jsonFiles };
|
|
931
|
+
return ok(JSON.stringify(output, null, 2));
|
|
932
|
+
}
|
|
872
933
|
return ok(parts.join("\n\n---\n\n"));
|
|
873
934
|
});
|
|
874
935
|
}
|
package/package.json
CHANGED
|
@@ -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.
|