grepmax 0.26.44 → 0.26.45

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.
@@ -10,7 +10,7 @@
10
10
  "name": "grepmax",
11
11
  "source": "./plugins/grepmax",
12
12
  "description": "Semantic code search for Claude Code. Automatically indexes your project and provides intelligent search capabilities.",
13
- "version": "0.26.44",
13
+ "version": "0.26.45",
14
14
  "author": {
15
15
  "name": "Robert Owens",
16
16
  "email": "robowens@me.com"
package/README.md CHANGED
@@ -130,6 +130,8 @@ MCP is local-only: the client spawns it and communicates through stdin/stdout pi
130
130
 
131
131
  `gmax mcp` starts a stdio-based MCP server for clients that support MCP but can't run shell commands (Cursor, Windsurf, custom agents). Search-backed tools use the singleton daemon when available, avoiding a separate embedding worker per MCP session, and use an in-process worker for supported offline/secondary-store access. An unsupported daemon command requires `gmax watch restart`.
132
132
 
133
+ `semantic_search`, `trace_calls`, `dead`, and `index_status` also advertise output schemas and return `structuredContent` with `schemaVersion: 1`, alongside the existing text. Search matches follow the same scope and display filters and include paths, one-based start/end lines, symbols, scores (null when unavailable), and warnings. Trace results preserve caller ancestry and edge confidence; unresolved callees have no location, and omitted callee/importer counts are explicit. Graph conclusions carry `approximate: true`. Index health includes embedding identity, watcher/reconciliation state, and the last compaction result; unavailable health is null or unobserved. These four tools advertise read-only, non-destructive, closed-world annotations. Other tools retain their current contracts.
134
+
133
135
  | Tool | Description |
134
136
  | --- | --- |
135
137
  | `semantic_search` | Search by meaning. Pointer mode matches CLI `--agent` output; `detail=code/full` returns snippets. |
@@ -85,6 +85,7 @@ const index_config_1 = require("../lib/index/index-config");
85
85
  const syncer_1 = require("../lib/index/syncer");
86
86
  const agent_search_formatter_1 = require("../lib/output/agent-search-formatter");
87
87
  const index_state_footer_1 = require("../lib/output/index-state-footer");
88
+ const mcp_results_1 = require("../lib/output/mcp-results");
88
89
  const searcher_1 = require("../lib/search/searcher");
89
90
  const annotator_1 = require("../lib/skeleton/annotator");
90
91
  const skeletonizer_1 = require("../lib/skeleton/skeletonizer");
@@ -123,8 +124,8 @@ function toStringArray(val) {
123
124
  }
124
125
  return [];
125
126
  }
126
- function ok(text) {
127
- return { content: [{ type: "text", text }] };
127
+ function ok(text, structuredContent) {
128
+ return Object.assign({ content: [{ type: "text", text }] }, (structuredContent ? { structuredContent } : {}));
128
129
  }
129
130
  function err(text) {
130
131
  return { content: [{ type: "text", text }], isError: true };
@@ -256,13 +257,13 @@ function searchResultPath(r) {
256
257
  }
257
258
  function searchResultStartLine(r) {
258
259
  var _a;
259
- var _b, _c;
260
- return Number((_c = (_b = r === null || r === void 0 ? void 0 : r.start_line) !== null && _b !== void 0 ? _b : (_a = r === null || r === void 0 ? void 0 : r.generated_metadata) === null || _a === void 0 ? void 0 : _a.start_line) !== null && _c !== void 0 ? _c : 0);
260
+ var _b, _c, _d;
261
+ return Number((_d = (_c = (_b = r === null || r === void 0 ? void 0 : r.startLine) !== null && _b !== void 0 ? _b : r === null || r === void 0 ? void 0 : r.start_line) !== null && _c !== void 0 ? _c : (_a = r === null || r === void 0 ? void 0 : r.generated_metadata) === null || _a === void 0 ? void 0 : _a.start_line) !== null && _d !== void 0 ? _d : 0);
261
262
  }
262
263
  function searchResultEndLine(r, fallbackStart = 0) {
263
264
  var _a;
264
- var _b, _c;
265
- return Number((_c = (_b = r === null || r === void 0 ? void 0 : r.end_line) !== null && _b !== void 0 ? _b : (_a = r === null || r === void 0 ? void 0 : r.generated_metadata) === null || _a === void 0 ? void 0 : _a.end_line) !== null && _c !== void 0 ? _c : fallbackStart);
265
+ var _b, _c, _d;
266
+ return Number((_d = (_c = (_b = r === null || r === void 0 ? void 0 : r.endLine) !== null && _b !== void 0 ? _b : r === null || r === void 0 ? void 0 : r.end_line) !== null && _c !== void 0 ? _c : (_a = r === null || r === void 0 ? void 0 : r.generated_metadata) === null || _a === void 0 ? void 0 : _a.end_line) !== null && _d !== void 0 ? _d : fallbackStart);
266
267
  }
267
268
  function filterMcpSearchResults(data, options = {}) {
268
269
  var _a, _b;
@@ -772,6 +773,26 @@ exports.mcp = new commander_1.Command("mcp")
772
773
  return (candidate.length > 0 &&
773
774
  allowedRoots.some((root) => (0, path_containment_1.isPathWithin)(root, candidate)));
774
775
  });
776
+ const structuredSearch = (matches) => { var _a; return ({
777
+ schemaVersion: 1,
778
+ query,
779
+ roots: allowedRoots,
780
+ warnings: ((_a = result.warnings) !== null && _a !== void 0 ? _a : []).filter(Boolean),
781
+ matches: matches.map((r) => {
782
+ var _a;
783
+ const startLine = searchResultStartLine(r);
784
+ return {
785
+ path: chunkAbsPath(r),
786
+ startLine: startLine + 1,
787
+ endLine: searchResultEndLine(r, startLine) + 1,
788
+ symbols: chunkSymbols(r),
789
+ role: String((_a = r.role) !== null && _a !== void 0 ? _a : "IMPLEMENTATION"),
790
+ score: typeof r.score === "number" && Number.isFinite(r.score)
791
+ ? r.score
792
+ : null,
793
+ };
794
+ }),
795
+ }); };
775
796
  // Prepend any searcher warnings to whatever body we return.
776
797
  const prefixNotes = (body) => {
777
798
  var _a;
@@ -779,7 +800,7 @@ exports.mcp = new commander_1.Command("mcp")
779
800
  return notes.length ? `${notes.join("\n")}\n\n${body}` : body;
780
801
  };
781
802
  if (!result.data || result.data.length === 0) {
782
- return ok(prefixNotes("No matches found. Try broadening your query, using fewer keywords, or check `gmax status` to verify the project is indexed."));
803
+ return ok(prefixNotes("No matches found. Try broadening your query, using fewer keywords, or check `gmax status` to verify the project is indexed."), structuredSearch([]));
783
804
  }
784
805
  const minScore = typeof args.min_score === "number" ? args.min_score : 0;
785
806
  const maxPerFile = typeof args.max_per_file === "number" ? args.max_per_file : 0;
@@ -809,21 +830,25 @@ exports.mcp = new commander_1.Command("mcp")
809
830
  getImportsForFile,
810
831
  query,
811
832
  });
812
- return ok(prefixNotes(output));
833
+ return ok(prefixNotes(output), structuredSearch(filterMcpSearchResults(result.data, {
834
+ minScore,
835
+ maxPerFile,
836
+ namePattern,
837
+ })));
813
838
  }
814
839
  let results = result.data.map((r) => {
815
- var _a, _b, _c;
816
- var _d, _e, _f, _g, _h, _j, _k, _l, _m;
817
- const absPath = (_e = (_d = r.path) !== null && _d !== void 0 ? _d : (_a = r.metadata) === null || _a === void 0 ? void 0 : _a.path) !== null && _e !== void 0 ? _e : "";
840
+ var _a;
841
+ var _b, _c, _d, _e, _f;
842
+ const absPath = (_c = (_b = r.path) !== null && _b !== void 0 ? _b : (_a = r.metadata) === null || _a === void 0 ? void 0 : _a.path) !== null && _c !== void 0 ? _c : "";
818
843
  const relPath = absPath.startsWith(displayRoot)
819
844
  ? absPath.slice(displayRoot.length + 1)
820
845
  : absPath;
821
- const startLine = (_g = (_f = r.startLine) !== null && _f !== void 0 ? _f : (_b = r.generated_metadata) === null || _b === void 0 ? void 0 : _b.start_line) !== null && _g !== void 0 ? _g : 0;
822
- const endLine = (_j = (_h = r.endLine) !== null && _h !== void 0 ? _h : (_c = r.generated_metadata) === null || _c === void 0 ? void 0 : _c.end_line) !== null && _j !== void 0 ? _j : 0;
823
- const defs = toStringArray((_k = r.definedSymbols) !== null && _k !== void 0 ? _k : r.defined_symbols);
824
- const refs = toStringArray((_l = r.referenced_symbols) !== null && _l !== void 0 ? _l : r.referencedSymbols);
846
+ const startLine = searchResultStartLine(r);
847
+ const endLine = searchResultEndLine(r, startLine);
848
+ const defs = toStringArray((_d = r.definedSymbols) !== null && _d !== void 0 ? _d : r.defined_symbols);
849
+ const refs = toStringArray((_e = r.referenced_symbols) !== null && _e !== void 0 ? _e : r.referencedSymbols);
825
850
  const symbol = defs[0] || "(anonymous)";
826
- const role = ((_m = r.role) !== null && _m !== void 0 ? _m : "IMPL").slice(0, 4).toUpperCase();
851
+ const role = ((_f = r.role) !== null && _f !== void 0 ? _f : "IMPL").slice(0, 4).toUpperCase();
827
852
  const exported = r.is_exported ? "exported " : "";
828
853
  const complexity = typeof r.complexity === "number" && r.complexity > 0
829
854
  ? ` C:${Math.round(r.complexity)}`
@@ -855,7 +880,7 @@ exports.mcp = new commander_1.Command("mcp")
855
880
  .map((l, i) => `${ctxStart + i + 1}│${l}`)
856
881
  .join("\n");
857
882
  }
858
- catch (_o) {
883
+ catch (_g) {
859
884
  // Fall through to chunk content
860
885
  }
861
886
  }
@@ -885,6 +910,7 @@ exports.mcp = new commander_1.Command("mcp")
885
910
  }
886
911
  }
887
912
  return {
913
+ row: r,
888
914
  absPath,
889
915
  text,
890
916
  score: typeof r.score === "number" ? r.score : 0,
@@ -957,7 +983,7 @@ exports.mcp = new commander_1.Command("mcp")
957
983
  // Trace failed — return search results without trace
958
984
  }
959
985
  }
960
- return ok(prefixNotes(output));
986
+ return ok(prefixNotes(output), structuredSearch(results.map((r) => r.row)));
961
987
  }
962
988
  catch (e) {
963
989
  return toolError("Search failed", e);
@@ -1105,8 +1131,9 @@ exports.mcp = new commander_1.Command("mcp")
1105
1131
  render: (resp) => resp.graph,
1106
1132
  inProcess: () => localStore((deps) => (0, graph_handler_1.runGraphTrace)(deps.vectorDb, { symbol, hops: depth, scope })),
1107
1133
  });
1134
+ const structured = (0, mcp_results_1.traceResult)(symbol, root, graph);
1108
1135
  if (!graph.center) {
1109
- return ok(`Symbol '${symbol}' not found in the index. Check \`gmax status\` to see which projects are indexed, or try \`gmax search ${symbol}\` to find similar symbols.`);
1136
+ return ok(`Symbol '${symbol}' not found in the index. Check \`gmax status\` to see which projects are indexed, or try \`gmax search ${symbol}\` to find similar symbols.`, structured);
1110
1137
  }
1111
1138
  const lines = [];
1112
1139
  // Center
@@ -1165,7 +1192,7 @@ exports.mcp = new commander_1.Command("mcp")
1165
1192
  else {
1166
1193
  lines.push("Calls: none");
1167
1194
  }
1168
- return ok(lines.join("\n"));
1195
+ return ok(lines.join("\n"), structured);
1169
1196
  }
1170
1197
  catch (e) {
1171
1198
  return toolError("Trace failed", e);
@@ -1406,16 +1433,17 @@ exports.mcp = new commander_1.Command("mcp")
1406
1433
  render: (resp) => resp.dead,
1407
1434
  inProcess: () => localStore((deps) => (0, graph_handler_1.runGraphDead)(deps.vectorDb, { symbol, scope })),
1408
1435
  });
1436
+ const structured = (0, mcp_results_1.deadResult)(symbol, root, facts);
1409
1437
  if (!facts.found) {
1410
- return ok(`Symbol '${symbol}' not found in the index. Check \`gmax status\` to see which projects are indexed, or try \`gmax search ${symbol}\` to find similar symbols.`);
1438
+ return ok(`Symbol '${symbol}' not found in the index. Check \`gmax status\` to see which projects are indexed, or try \`gmax search ${symbol}\` to find similar symbols.`, structured);
1411
1439
  }
1412
1440
  const rel = (p) => p.startsWith(root) ? p.slice(root.length + 1) : p;
1413
1441
  const defLoc = `${rel(facts.defPath)}:${facts.defLine + 1}`;
1414
1442
  if (facts.callerCount === 0) {
1415
1443
  if (facts.isExported) {
1416
- return ok(`PUBLIC EXPORT ${defLoc} defines ${symbol} — no internal callers found; check external usage`);
1444
+ return ok(`PUBLIC EXPORT ${defLoc} defines ${symbol} — no internal callers found; check external usage`, structured);
1417
1445
  }
1418
- return ok(`DEAD ${defLoc} defines ${symbol}`);
1446
+ return ok(`DEAD ${defLoc} defines ${symbol}`, structured);
1419
1447
  }
1420
1448
  const top = facts.topCallers;
1421
1449
  const lines = [
@@ -1424,7 +1452,7 @@ exports.mcp = new commander_1.Command("mcp")
1424
1452
  for (const c of top) {
1425
1453
  lines.push(` ${rel(c.file)}:${c.line + 1}`);
1426
1454
  }
1427
- return ok(lines.join("\n"));
1455
+ return ok(lines.join("\n"), structured);
1428
1456
  }
1429
1457
  catch (e) {
1430
1458
  return toolError("Dead check failed", e);
@@ -1782,7 +1810,7 @@ exports.mcp = new commander_1.Command("mcp")
1782
1810
  }
1783
1811
  function handleIndexStatus() {
1784
1812
  return __awaiter(this, void 0, void 0, function* () {
1785
- var _a, _b;
1813
+ var _a, _b, _c, _d, _e;
1786
1814
  try {
1787
1815
  const globalConfig = storeConfig();
1788
1816
  const projects = storeProjects();
@@ -1834,7 +1862,36 @@ exports.mcp = new commander_1.Command("mcp")
1834
1862
  (0, compaction_result_1.formatCompactionStatus)(daemonStatus.compaction),
1835
1863
  `Projects: ${projects.length} indexed (call list_projects for names + per-project chunk counts)`,
1836
1864
  ].filter(Boolean);
1837
- return ok(lines.join("\n"));
1865
+ const compaction = mcp_results_1.compactionSchema.safeParse(daemonStatus.compaction);
1866
+ return ok(lines.join("\n"), {
1867
+ schemaVersion: 1,
1868
+ root: currentRoot(),
1869
+ store: storeDir(),
1870
+ secondary: secondary(),
1871
+ chunks,
1872
+ files,
1873
+ projects: projects.length,
1874
+ embedding: {
1875
+ state: identity.state,
1876
+ configured: {
1877
+ tier: identity.configured.tier,
1878
+ vectorDim: identity.configured.vectorDim,
1879
+ fingerprint: identity.configured.fingerprint,
1880
+ },
1881
+ built: identity.built
1882
+ ? {
1883
+ tier: identity.built.tier,
1884
+ vectorDim: identity.built.vectorDim,
1885
+ fingerprint: identity.built.fingerprint,
1886
+ }
1887
+ : null,
1888
+ },
1889
+ watcher: {
1890
+ status: (_d = (_c = daemonProject === null || daemonProject === void 0 ? void 0 : daemonProject.status) !== null && _c !== void 0 ? _c : watcher === null || watcher === void 0 ? void 0 : watcher.status) !== null && _d !== void 0 ? _d : (secondary() || !daemonStatus.ok ? "unobserved" : "not_running"),
1891
+ indexState: (_e = daemonProject === null || daemonProject === void 0 ? void 0 : daemonProject.indexState) !== null && _e !== void 0 ? _e : null,
1892
+ },
1893
+ compaction: compaction.success ? compaction.data : null,
1894
+ });
1838
1895
  }
1839
1896
  catch (e) {
1840
1897
  return toolError("Status check failed", e);
@@ -2620,7 +2677,16 @@ exports.mcp = new commander_1.Command("mcp")
2620
2677
  // shapes give us free input validation (the SDK rejects calls that violate
2621
2678
  // the schema before the handler runs); handlers stay defensively coded.
2622
2679
  function tool(name, config, handler) {
2623
- server.registerTool(name, Object.assign(Object.assign({}, config), { inputSchema: Object.assign({ root: zod_1.z.string().optional() }, config.inputSchema) }), (rawArgs) => __awaiter(this, void 0, void 0, function* () {
2680
+ server.registerTool(name, Object.assign(Object.assign(Object.assign({}, config), (mcp_results_1.MCP_READ_OUTPUT_SCHEMAS[name]
2681
+ ? {
2682
+ outputSchema: mcp_results_1.MCP_READ_OUTPUT_SCHEMAS[name],
2683
+ annotations: {
2684
+ readOnlyHint: true,
2685
+ destructiveHint: false,
2686
+ openWorldHint: false,
2687
+ },
2688
+ }
2689
+ : {})), { inputSchema: Object.assign({ root: zod_1.z.string().optional() }, config.inputSchema) }), (rawArgs) => __awaiter(this, void 0, void 0, function* () {
2624
2690
  let args = (rawArgs !== null && rawArgs !== void 0 ? rawArgs : {});
2625
2691
  const startMs = Date.now();
2626
2692
  let target = projectRoot;
@@ -0,0 +1,177 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MCP_READ_OUTPUT_SCHEMAS = exports.compactionSchema = void 0;
4
+ exports.traceResult = traceResult;
5
+ exports.deadResult = deadResult;
6
+ const zod_1 = require("zod");
7
+ const location = zod_1.z.object({
8
+ path: zod_1.z.string(),
9
+ line: zod_1.z.number().int().positive().describe("One-based source line"),
10
+ });
11
+ const graphNode = zod_1.z.object({
12
+ symbol: zod_1.z.string(),
13
+ location: location.nullable(),
14
+ role: zod_1.z.string(),
15
+ resolution: zod_1.z.enum(["indexed", "not_indexed", "ambiguous_member"]),
16
+ edgeKind: zod_1.z.enum(["free", "member", "type"]).optional(),
17
+ confidence: zod_1.z.enum(["EXTRACTED", "INFERRED"]).optional(),
18
+ });
19
+ const search = {
20
+ schemaVersion: zod_1.z.literal(1),
21
+ query: zod_1.z.string(),
22
+ roots: zod_1.z.array(zod_1.z.string()),
23
+ warnings: zod_1.z.array(zod_1.z.string()),
24
+ matches: zod_1.z
25
+ .array(zod_1.z.object({
26
+ path: zod_1.z.string(),
27
+ startLine: zod_1.z.number().int().positive(),
28
+ endLine: zod_1.z.number().int().positive(),
29
+ symbols: zod_1.z.array(zod_1.z.string()),
30
+ role: zod_1.z.string(),
31
+ score: zod_1.z
32
+ .number()
33
+ .nullable()
34
+ .describe("Search score; null when unavailable"),
35
+ }))
36
+ .max(50),
37
+ };
38
+ exports.compactionSchema = zod_1.z.object({
39
+ status: zod_1.z.enum(["completed", "skipped", "failed"]),
40
+ at: zod_1.z.number(),
41
+ attempts: zod_1.z.number(),
42
+ elapsedMs: zod_1.z.number(),
43
+ reason: zod_1.z.string().optional(),
44
+ logicalBytes: zod_1.z.number().optional(),
45
+ diskBytesBefore: zod_1.z.number().optional(),
46
+ diskBytesAfter: zod_1.z.number().optional(),
47
+ freeBytesBefore: zod_1.z.number().optional(),
48
+ freeBytesAfter: zod_1.z.number().optional(),
49
+ bytesReclaimed: zod_1.z.number().optional(),
50
+ });
51
+ const embedding = zod_1.z.object({
52
+ tier: zod_1.z.string(),
53
+ vectorDim: zod_1.z.number(),
54
+ fingerprint: zod_1.z.string(),
55
+ });
56
+ /** Only tools with a complete success contract advertise an output schema. */
57
+ exports.MCP_READ_OUTPUT_SCHEMAS = {
58
+ semantic_search: search,
59
+ trace_calls: {
60
+ schemaVersion: zod_1.z.literal(1),
61
+ symbol: zod_1.z.string(),
62
+ root: zod_1.z.string(),
63
+ found: zod_1.z.boolean(),
64
+ center: graphNode.nullable(),
65
+ callers: zod_1.z.array(zod_1.z.object({
66
+ node: graphNode,
67
+ depth: zod_1.z.number().int().positive(),
68
+ parentIndex: zod_1.z
69
+ .number()
70
+ .int()
71
+ .nonnegative()
72
+ .nullable()
73
+ .describe("Index of the parent in callers; null for a direct caller of center"),
74
+ })),
75
+ callees: zod_1.z.array(graphNode).max(15),
76
+ importers: zod_1.z.array(zod_1.z.string()).max(10),
77
+ omittedCallees: zod_1.z.number().int().nonnegative(),
78
+ omittedImporters: zod_1.z.number().int().nonnegative(),
79
+ approximate: zod_1.z.literal(true),
80
+ },
81
+ dead: {
82
+ schemaVersion: zod_1.z.literal(1),
83
+ symbol: zod_1.z.string(),
84
+ root: zod_1.z.string(),
85
+ status: zod_1.z.enum(["not_found", "public_export", "dead", "live"]),
86
+ definition: location.nullable(),
87
+ isExported: zod_1.z.boolean(),
88
+ callerCount: zod_1.z.number().int().nonnegative(),
89
+ callers: zod_1.z.array(location),
90
+ approximate: zod_1.z.literal(true),
91
+ },
92
+ index_status: {
93
+ schemaVersion: zod_1.z.literal(1),
94
+ root: zod_1.z.string(),
95
+ store: zod_1.z.string(),
96
+ secondary: zod_1.z.boolean(),
97
+ chunks: zod_1.z.number().int().nonnegative(),
98
+ files: zod_1.z.number().int().nonnegative(),
99
+ projects: zod_1.z.number().int().nonnegative(),
100
+ embedding: zod_1.z.object({
101
+ state: zod_1.z.string(),
102
+ configured: embedding,
103
+ built: embedding.nullable(),
104
+ }),
105
+ watcher: zod_1.z.object({
106
+ status: zod_1.z.string(),
107
+ indexState: zod_1.z
108
+ .object({
109
+ indexing: zod_1.z.boolean(),
110
+ pendingFiles: zod_1.z.number(),
111
+ verifying: zod_1.z.boolean().optional(),
112
+ failedFiles: zod_1.z.number().optional(),
113
+ degraded: zod_1.z.boolean().optional(),
114
+ watcherMode: zod_1.z.enum(["native", "polling", "recovering"]).optional(),
115
+ catchupRunning: zod_1.z.boolean().optional(),
116
+ lastReconciledAt: zod_1.z.number().optional(),
117
+ overflowCount: zod_1.z.number().optional(),
118
+ catchupMs: zod_1.z.number().optional(),
119
+ })
120
+ .nullable(),
121
+ }),
122
+ compaction: exports.compactionSchema.nullable(),
123
+ },
124
+ };
125
+ function nodeResult(node) {
126
+ return Object.assign(Object.assign({ symbol: node.symbol, location: node.file ? { path: node.file, line: node.line + 1 } : null, role: node.role, resolution: node.file
127
+ ? "indexed"
128
+ : node.resolution === "ambiguous-member"
129
+ ? "ambiguous_member"
130
+ : "not_indexed" }, (node.edgeKind ? { edgeKind: node.edgeKind } : {})), (node.confidence ? { confidence: node.confidence } : {}));
131
+ }
132
+ function traceResult(symbol, root, graph) {
133
+ const callers = [];
134
+ function visit(trees, depth, parentIndex) {
135
+ for (const tree of trees) {
136
+ const index = callers.length;
137
+ callers.push({ node: nodeResult(tree.node), depth, parentIndex });
138
+ visit(tree.callers, depth + 1, index);
139
+ }
140
+ }
141
+ visit(graph.callerTree, 1, null);
142
+ const importers = graph.importers.filter((p) => { var _a; return p !== ((_a = graph.center) === null || _a === void 0 ? void 0 : _a.file); });
143
+ return {
144
+ schemaVersion: 1,
145
+ symbol,
146
+ root,
147
+ found: graph.center !== null,
148
+ center: graph.center ? nodeResult(graph.center) : null,
149
+ callers,
150
+ callees: graph.callees.slice(0, 15).map(nodeResult),
151
+ importers: importers.slice(0, 10),
152
+ omittedCallees: Math.max(0, graph.callees.length - 15),
153
+ omittedImporters: Math.max(0, importers.length - 10),
154
+ approximate: true,
155
+ };
156
+ }
157
+ function deadResult(symbol, root, facts) {
158
+ return {
159
+ schemaVersion: 1,
160
+ symbol,
161
+ root,
162
+ status: !facts.found
163
+ ? "not_found"
164
+ : facts.callerCount > 0
165
+ ? "live"
166
+ : facts.isExported
167
+ ? "public_export"
168
+ : "dead",
169
+ definition: facts.found
170
+ ? { path: facts.defPath, line: facts.defLine + 1 }
171
+ : null,
172
+ isExported: facts.isExported,
173
+ callerCount: facts.callerCount,
174
+ callers: facts.topCallers.map((c) => ({ path: c.file, line: c.line + 1 })),
175
+ approximate: true,
176
+ };
177
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "grepmax",
3
- "version": "0.26.44",
3
+ "version": "0.26.45",
4
4
  "author": "Robert Owens <78518764+reowens@users.noreply.github.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.26.44",
3
+ "version": "0.26.45",
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",