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.
@@ -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
- parts.push(`// ${t} — file not found`);
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
- const tokens = Math.ceil(cached.length / 4);
845
- const annotated = sourceContent
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
- // Index may not exist yet — fall through to live generation
854
- }
855
- // Generate live
856
- try {
857
- const content = sourceContent || fs.readFileSync(absPath, "utf-8");
858
- const result = yield skel.skeletonizeFile(absPath, content);
859
- if (result.success) {
860
- const annotated = annotateSkeletonLines(result.skeleton, content);
861
- parts.push(`// ${t} (~${result.tokenEstimate} tokens)\n\n${annotated}`);
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
- else {
864
- parts.push(`// ${t} skeleton failed: ${result.error}`);
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
- catch (e) {
868
- const msg = e instanceof Error ? e.message : String(e);
869
- parts.push(`// ${t} error: ${msg}`);
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "grepmax",
3
- "version": "0.7.16",
3
+ "version": "0.7.17",
4
4
  "author": "Robert Owens <robowens@me.com>",
5
5
  "homepage": "https://github.com/reowens/grepmax",
6
6
  "bugs": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "grepmax",
3
- "version": "0.7.16",
3
+ "version": "0.7.17",
4
4
  "description": "Semantic code search for Claude Code. Automatically indexes your project and provides intelligent search capabilities.",
5
5
  "author": {
6
6
  "name": "Robert Owens",
@@ -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.