grepmax 0.26.43 → 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.
Files changed (93) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/NOTICE +6 -0
  3. package/README.md +5 -1
  4. package/dist/bin.js +4 -0
  5. package/dist/commands/doctor.js +2 -1
  6. package/dist/commands/mcp.js +234 -93
  7. package/dist/commands/project.js +0 -1
  8. package/dist/commands/similar.js +0 -1
  9. package/dist/commands/skeleton.js +0 -1
  10. package/dist/commands/status.js +32 -18
  11. package/dist/commands/surprises.js +0 -1
  12. package/dist/commands/symbols.js +0 -1
  13. package/dist/commands/watch.js +2 -1
  14. package/dist/eval-like-limit-probe.js +60 -41
  15. package/dist/lib/analysis/surprising-connections.js +4 -3
  16. package/dist/lib/daemon/daemon.js +14 -2
  17. package/dist/lib/daemon/graph-handler.js +33 -33
  18. package/dist/lib/daemon/rows-handler.js +13 -51
  19. package/dist/lib/daemon/vector-handler.js +18 -17
  20. package/dist/lib/graph/graph-builder.js +33 -16
  21. package/dist/lib/graph/impact.js +9 -8
  22. package/dist/lib/index/index-config.js +2 -2
  23. package/dist/lib/llm/config.js +9 -3
  24. package/dist/lib/llm/tools.js +5 -4
  25. package/dist/lib/output/mcp-results.js +177 -0
  26. package/dist/lib/search/pagerank.js +2 -1
  27. package/dist/lib/search/searcher.js +5 -4
  28. package/dist/lib/skeleton/retriever.js +2 -1
  29. package/dist/lib/store/lance-sdk.js +46 -0
  30. package/dist/lib/store/vector-db.js +57 -16
  31. package/dist/lib/utils/cross-project.js +2 -2
  32. package/dist/lib/utils/project-registry.js +8 -8
  33. package/dist/lib/utils/query-timeout.js +53 -13
  34. package/dist/lib/utils/store-access.js +6 -5
  35. package/dist/lib/utils/store-context.js +102 -0
  36. package/dist/lib/utils/stores.js +1 -1
  37. package/dist/vendor/lancedb/LICENSE +202 -0
  38. package/dist/vendor/lancedb/NODEJS_THIRD_PARTY_LICENSES.md +668 -0
  39. package/dist/vendor/lancedb/RUST_THIRD_PARTY_LICENSES.html +14607 -0
  40. package/dist/vendor/lancedb/arrow.d.ts +296 -0
  41. package/dist/vendor/lancedb/arrow.js +1145 -0
  42. package/dist/vendor/lancedb/arrow_type.d.ts +9 -0
  43. package/dist/vendor/lancedb/arrow_type.js +29 -0
  44. package/dist/vendor/lancedb/connection.d.ts +508 -0
  45. package/dist/vendor/lancedb/connection.js +298 -0
  46. package/dist/vendor/lancedb/embedding/embedding_function.d.ts +103 -0
  47. package/dist/vendor/lancedb/embedding/embedding_function.js +192 -0
  48. package/dist/vendor/lancedb/embedding/index.d.ts +37 -0
  49. package/dist/vendor/lancedb/embedding/index.js +108 -0
  50. package/dist/vendor/lancedb/embedding/openai.d.ts +16 -0
  51. package/dist/vendor/lancedb/embedding/openai.js +81 -0
  52. package/dist/vendor/lancedb/embedding/registry.d.ts +94 -0
  53. package/dist/vendor/lancedb/embedding/registry.js +221 -0
  54. package/dist/vendor/lancedb/embedding/transformers.d.ts +36 -0
  55. package/dist/vendor/lancedb/embedding/transformers.js +110 -0
  56. package/dist/vendor/lancedb/header.d.ts +162 -0
  57. package/dist/vendor/lancedb/header.js +217 -0
  58. package/dist/vendor/lancedb/index.d.ts +246 -0
  59. package/dist/vendor/lancedb/index.js +176 -0
  60. package/dist/vendor/lancedb/indices.d.ts +721 -0
  61. package/dist/vendor/lancedb/indices.js +166 -0
  62. package/dist/vendor/lancedb/license_header.txt +2 -0
  63. package/dist/vendor/lancedb/materialized_view.d.ts +69 -0
  64. package/dist/vendor/lancedb/materialized_view.js +112 -0
  65. package/dist/vendor/lancedb/merge.d.ts +104 -0
  66. package/dist/vendor/lancedb/merge.js +120 -0
  67. package/dist/vendor/lancedb/native.d.ts +1193 -0
  68. package/dist/vendor/lancedb/native.js +688 -0
  69. package/dist/vendor/lancedb/oauth.d.ts +66 -0
  70. package/dist/vendor/lancedb/oauth.js +15 -0
  71. package/dist/vendor/lancedb/otel.d.ts +26 -0
  72. package/dist/vendor/lancedb/otel.js +114 -0
  73. package/dist/vendor/lancedb/permutation.d.ts +143 -0
  74. package/dist/vendor/lancedb/permutation.js +184 -0
  75. package/dist/vendor/lancedb/provenance.json +6 -0
  76. package/dist/vendor/lancedb/query.d.ts +663 -0
  77. package/dist/vendor/lancedb/query.js +966 -0
  78. package/dist/vendor/lancedb/rerankers/index.d.ts +5 -0
  79. package/dist/vendor/lancedb/rerankers/index.js +19 -0
  80. package/dist/vendor/lancedb/rerankers/rrf.d.ts +14 -0
  81. package/dist/vendor/lancedb/rerankers/rrf.js +28 -0
  82. package/dist/vendor/lancedb/sanitize.d.ts +32 -0
  83. package/dist/vendor/lancedb/sanitize.js +544 -0
  84. package/dist/vendor/lancedb/scannable.d.ts +92 -0
  85. package/dist/vendor/lancedb/scannable.js +200 -0
  86. package/dist/vendor/lancedb/schema.d.ts +16 -0
  87. package/dist/vendor/lancedb/schema.js +387 -0
  88. package/dist/vendor/lancedb/table.d.ts +1069 -0
  89. package/dist/vendor/lancedb/table.js +498 -0
  90. package/dist/vendor/lancedb/util.d.ts +14 -0
  91. package/dist/vendor/lancedb/util.js +77 -0
  92. package/package.json +19 -7
  93. package/plugins/grepmax/.claude-plugin/plugin.json +1 -1
@@ -79,11 +79,13 @@ const graph_handler_1 = require("../lib/daemon/graph-handler");
79
79
  const rows_handler_1 = require("../lib/daemon/rows-handler");
80
80
  const vector_handler_1 = require("../lib/daemon/vector-handler");
81
81
  const test_hits_1 = require("../lib/graph/test-hits");
82
+ const embedding_generation_1 = require("../lib/index/embedding-generation");
82
83
  const embedding_status_1 = require("../lib/index/embedding-status");
83
84
  const index_config_1 = require("../lib/index/index-config");
84
85
  const syncer_1 = require("../lib/index/syncer");
85
86
  const agent_search_formatter_1 = require("../lib/output/agent-search-formatter");
86
87
  const index_state_footer_1 = require("../lib/output/index-state-footer");
88
+ const mcp_results_1 = require("../lib/output/mcp-results");
87
89
  const searcher_1 = require("../lib/search/searcher");
88
90
  const annotator_1 = require("../lib/skeleton/annotator");
89
91
  const skeletonizer_1 = require("../lib/skeleton/skeletonizer");
@@ -100,8 +102,11 @@ const project_registry_1 = require("../lib/utils/project-registry");
100
102
  const project_root_1 = require("../lib/utils/project-root");
101
103
  const scope_filter_1 = require("../lib/utils/scope-filter");
102
104
  const store_access_1 = require("../lib/utils/store-access");
105
+ const store_context_1 = require("../lib/utils/store-context");
106
+ const stores_1 = require("../lib/utils/stores");
103
107
  const watcher_launcher_1 = require("../lib/utils/watcher-launcher");
104
108
  const watcher_store_1 = require("../lib/utils/watcher-store");
109
+ const pool_1 = require("../lib/workers/pool");
105
110
  // ---------------------------------------------------------------------------
106
111
  // Helpers
107
112
  // ---------------------------------------------------------------------------
@@ -119,8 +124,8 @@ function toStringArray(val) {
119
124
  }
120
125
  return [];
121
126
  }
122
- function ok(text) {
123
- return { content: [{ type: "text", text }] };
127
+ function ok(text, structuredContent) {
128
+ return Object.assign({ content: [{ type: "text", text }] }, (structuredContent ? { structuredContent } : {}));
124
129
  }
125
130
  function err(text) {
126
131
  return { content: [{ type: "text", text }], isError: true };
@@ -143,9 +148,6 @@ function isMcpSessionFallback(error) {
143
148
  /**
144
149
  * MCP's read-tool fallback policy, in one object so every tool shares it.
145
150
  *
146
- * - `fallbackOnUnknownVerb` — a daemon older than this build does not know the
147
- * read verbs (the live 0.26.27 daemon does not); answer from the store rather
148
- * than failing the tool call. Removed one release after the verbs ship.
149
151
  * - `fallbackOnOversize` — the daemon's 2 MB response cap. See
150
152
  * `isOversizeError` for why MCP opts in and the CLI does not.
151
153
  * - `extraFallback` — the two session-readiness answers above.
@@ -161,7 +163,6 @@ function isMcpSessionFallback(error) {
161
163
  const MCP_WATCH_LEASE_RENEW_MS = 5 * 60 * 1000;
162
164
  const MCP_WATCH_LEASE_TTL_MS = 3 * MCP_WATCH_LEASE_RENEW_MS;
163
165
  const MCP_STORE_FALLBACK = {
164
- fallbackOnUnknownVerb: true,
165
166
  fallbackOnOversize: true,
166
167
  extraFallback: isMcpSessionFallback,
167
168
  };
@@ -172,7 +173,6 @@ const MCP_STORE_FALLBACK = {
172
173
  */
173
174
  function shouldFallbackMcpDaemonSearch(error) {
174
175
  return ((0, store_access_1.classifyDaemonError)(error) === "no-daemon" ||
175
- (0, store_access_1.isUnknownCommandError)(error) ||
176
176
  (0, store_access_1.isOversizeError)(error) ||
177
177
  isMcpSessionFallback(error));
178
178
  }
@@ -192,14 +192,30 @@ function resolveMcpProject(requested, currentRoot, projects = (0, project_regist
192
192
  if (byName.length > 1)
193
193
  return undefined;
194
194
  const resolved = path.resolve(value);
195
- return projects.find((project) => path.resolve(project.root) === resolved);
195
+ const physical = (value) => {
196
+ try {
197
+ return fs.realpathSync(value);
198
+ }
199
+ catch (_a) {
200
+ return path.resolve(value);
201
+ }
202
+ };
203
+ return projects.find((project) => physical(project.root) === physical(resolved));
196
204
  }
197
205
  const resolvedCurrent = path.resolve(currentRoot);
198
- const exact = projects.find((project) => path.resolve(project.root) === resolvedCurrent);
206
+ const physical = (value) => {
207
+ try {
208
+ return fs.realpathSync(value);
209
+ }
210
+ catch (_a) {
211
+ return path.resolve(value);
212
+ }
213
+ };
214
+ const exact = projects.find((project) => physical(project.root) === physical(resolvedCurrent));
199
215
  if (exact)
200
216
  return exact;
201
217
  return projects
202
- .filter((project) => (0, path_containment_1.isPathWithin)(project.root, resolvedCurrent))
218
+ .filter((project) => (0, path_containment_1.isPathWithin)(physical(project.root), physical(resolvedCurrent)))
203
219
  .sort((a, b) => b.root.length - a.root.length)[0];
204
220
  }
205
221
  function containMcpTarget(root, target) {
@@ -241,13 +257,13 @@ function searchResultPath(r) {
241
257
  }
242
258
  function searchResultStartLine(r) {
243
259
  var _a;
244
- var _b, _c;
245
- 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);
246
262
  }
247
263
  function searchResultEndLine(r, fallbackStart = 0) {
248
264
  var _a;
249
- var _b, _c;
250
- 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);
251
267
  }
252
268
  function filterMcpSearchResults(data, options = {}) {
253
269
  var _a, _b;
@@ -380,6 +396,11 @@ exports.mcp = new commander_1.Command("mcp")
380
396
  // --- Project context ---
381
397
  const projectRoot = (0, project_root_1.findProjectRoot)(process.cwd());
382
398
  const paths = (0, project_root_1.ensureProjectPaths)(projectRoot);
399
+ const currentRoot = () => { var _a; var _b; return (_b = (_a = (0, store_context_1.currentStoreContext)()) === null || _a === void 0 ? void 0 : _a.root) !== null && _b !== void 0 ? _b : projectRoot; };
400
+ const storeProjects = () => { var _a; var _b; return (_b = (_a = (0, store_context_1.currentStoreContext)()) === null || _a === void 0 ? void 0 : _a.projects) !== null && _b !== void 0 ? _b : (0, project_registry_1.listProjects)(); };
401
+ const storeConfig = () => { var _a; var _b; return (_b = (_a = (0, store_context_1.currentStoreContext)()) === null || _a === void 0 ? void 0 : _a.config) !== null && _b !== void 0 ? _b : (0, index_config_1.readGlobalConfig)(); };
402
+ const secondary = () => { var _a; var _b; return (_b = (_a = (0, store_context_1.currentStoreContext)()) === null || _a === void 0 ? void 0 : _a.secondary) !== null && _b !== void 0 ? _b : false; };
403
+ const storeDir = () => { var _a; var _b; return (_b = (_a = (0, store_context_1.currentStoreContext)()) === null || _a === void 0 ? void 0 : _a.lancedbDir) !== null && _b !== void 0 ? _b : paths.lancedbDir; };
383
404
  // Propagate project root to worker processes
384
405
  process.env.GMAX_PROJECT_ROOT = paths.root;
385
406
  // --- Store access ---
@@ -388,15 +409,15 @@ exports.mcp = new commander_1.Command("mcp")
388
409
  * `inProcess` branch is entered only when no daemon serves the verb.
389
410
  */
390
411
  function readStore(name, opts) {
391
- return (0, store_access_1.withStoreRead)(name, Object.assign(Object.assign({}, opts), MCP_STORE_FALLBACK));
412
+ return (0, store_access_1.withStoreRead)(name, Object.assign(Object.assign(Object.assign({}, opts), MCP_STORE_FALLBACK), { skipDaemon: secondary() }));
392
413
  }
393
414
  /** Open the shared store for the duration of one fallback, then close it. */
394
415
  function localStore(fn) {
395
- return (0, rows_handler_1.withLocalStore)(paths.lancedbDir, fn);
416
+ return (0, rows_handler_1.withLocalStore)(storeDir(), fn, storeConfig().vectorDim);
396
417
  }
397
418
  /** A graph verb with MCP's fallback allowances. */
398
419
  function graphVerb(verb, call) {
399
- return (0, graph_handler_1.callGraphVerb)(verb, Object.assign(Object.assign({}, call), { fallback: MCP_STORE_FALLBACK }));
420
+ return (0, graph_handler_1.callGraphVerb)(verb, Object.assign(Object.assign({}, call), { fallback: Object.assign(Object.assign({}, MCP_STORE_FALLBACK), (secondary() ? { skipDaemon: true } : {})) }));
400
421
  }
401
422
  /**
402
423
  * MCP tools take no `--in`/`--exclude`, so a tool's scope is always the
@@ -440,8 +461,16 @@ exports.mcp = new commander_1.Command("mcp")
440
461
  };
441
462
  },
442
463
  inProcess: () => localStore((deps) => __awaiter(this, void 0, void 0, function* () {
443
- const searcher = new searcher_1.Searcher(deps.vectorDb);
444
- return searcher.search(args.query, args.limit, { rerank: args.rerank, seeds: args.seeds }, args.filters, args.pathPrefix);
464
+ const pool = secondary()
465
+ ? new pool_1.WorkerPool((0, embedding_generation_1.resolveEmbeddingGeneration)(storeConfig()), storeConfig().embedMode)
466
+ : undefined;
467
+ try {
468
+ const searcher = new searcher_1.Searcher(deps.vectorDb, pool);
469
+ return yield searcher.search(args.query, args.limit, { rerank: args.rerank, seeds: args.seeds }, args.filters, args.pathPrefix);
470
+ }
471
+ finally {
472
+ yield pool === null || pool === void 0 ? void 0 : pool.destroy();
473
+ }
445
474
  })),
446
475
  daemonErrorMessage: (resp) => [resp.error, resp.hint]
447
476
  .filter((value) => typeof value === "string" && value)
@@ -509,7 +538,7 @@ exports.mcp = new commander_1.Command("mcp")
509
538
  * order. Batched so a per-symbol loop costs one round trip, not hundreds.
510
539
  */
511
540
  function locateRows(root, req) {
512
- return (0, rows_handler_1.readRows)(Object.assign({ name: "locate", projectRoot: root, lancedbDir: paths.lancedbDir, scope: projectScope(root), fallback: MCP_STORE_FALLBACK }, req));
541
+ return (0, rows_handler_1.readRows)(Object.assign({ name: "locate", projectRoot: root, lancedbDir: storeDir(), vectorDim: storeConfig().vectorDim, scope: projectScope(root), fallback: Object.assign(Object.assign({}, MCP_STORE_FALLBACK), { skipDaemon: secondary() }) }, req));
513
542
  }
514
543
  /**
515
544
  * Chunk and file totals for `index_status`, summed per registered project.
@@ -574,8 +603,8 @@ exports.mcp = new commander_1.Command("mcp")
574
603
  return __awaiter(this, void 0, void 0, function* () {
575
604
  if (_indexReady)
576
605
  return;
577
- const projects = (0, project_registry_1.listProjects)();
578
- const isRegistered = projects.some((p) => p.root === projectRoot);
606
+ const projects = storeProjects();
607
+ const isRegistered = projects.some((p) => p.root === currentRoot());
579
608
  if (isRegistered) {
580
609
  _indexReady = true;
581
610
  }
@@ -593,8 +622,10 @@ exports.mcp = new commander_1.Command("mcp")
593
622
  return __awaiter(this, void 0, void 0, function* () {
594
623
  var _a;
595
624
  try {
596
- const watchRoot = root !== null && root !== void 0 ? root : (_a = resolveMcpProject(undefined, projectRoot)) === null || _a === void 0 ? void 0 : _a.root;
597
- if (!watchRoot)
625
+ const watchRoot = root !== null && root !== void 0 ? root : (_a = resolveMcpProject(undefined, currentRoot(), storeProjects())) === null || _a === void 0 ? void 0 : _a.root;
626
+ if (!watchRoot ||
627
+ secondary() ||
628
+ (0, stores_1.matchStore)(watchRoot).kind !== "primary")
598
629
  return;
599
630
  const result = yield (0, watcher_launcher_1.launchWatcher)(watchRoot, WATCH_LEASE);
600
631
  if (result.ok && !result.reused) {
@@ -614,10 +645,10 @@ exports.mcp = new commander_1.Command("mcp")
614
645
  // covers the cwd, and scope to it instead of silently widening to the whole
615
646
  // index. `root` is the registered project root to scope to.
616
647
  function resolveRegisteredProject(requested) {
617
- const project = resolveMcpProject(requested, projectRoot);
648
+ const project = resolveMcpProject(requested, currentRoot(), storeProjects());
618
649
  if (project)
619
650
  return { proj: project, root: project.root };
620
- return { proj: undefined, root: projectRoot };
651
+ return { proj: undefined, root: currentRoot() };
621
652
  }
622
653
  // --- Tool handlers ---
623
654
  function handleSemanticSearch(args_1) {
@@ -648,7 +679,7 @@ exports.mcp = new commander_1.Command("mcp")
648
679
  if (typeof args.root === "string") {
649
680
  return err("Project not added to gmax yet. Run `gmax add` to index it first.");
650
681
  }
651
- const indexed = (0, project_registry_1.listProjects)().filter((p) => { var _a; return p.status !== "pending" && ((_a = p.chunkCount) !== null && _a !== void 0 ? _a : 0) > 0; });
682
+ const indexed = storeProjects().filter((p) => { var _a; return p.status !== "pending" && ((_a = p.chunkCount) !== null && _a !== void 0 ? _a : 0) > 0; });
652
683
  if (indexed.length === 0) {
653
684
  return err("No indexed projects yet. cd into a repo and run `gmax add` to index it first.");
654
685
  }
@@ -692,7 +723,7 @@ exports.mcp = new commander_1.Command("mcp")
692
723
  excludeProjects: typeof args.exclude_projects === "string"
693
724
  ? args.exclude_projects
694
725
  : undefined,
695
- });
726
+ }, storeProjects());
696
727
  if (crossScope.projectRoots.length === 0) {
697
728
  return err("No matching indexed projects.");
698
729
  }
@@ -709,7 +740,7 @@ exports.mcp = new commander_1.Command("mcp")
709
740
  const selectedRoots = searchAll
710
741
  ? new Set((_a = filters.projectRoots) !== null && _a !== void 0 ? _a : [])
711
742
  : new Set([resolvedRoot]);
712
- (0, embedding_status_1.assertEmbeddingSearchCompatible)((0, project_registry_1.listProjects)().filter((project) => selectedRoots.has(project.root)), (0, index_config_1.readGlobalConfig)());
743
+ (0, embedding_status_1.assertEmbeddingSearchCompatible)(storeProjects().filter((project) => selectedRoots.has(project.root)), storeConfig());
713
744
  // Aider-style seeding: the agent passes its open files / discussed
714
745
  // symbols; the searcher biases candidate generation toward them.
715
746
  const parseSeedList = (v) => {
@@ -742,6 +773,26 @@ exports.mcp = new commander_1.Command("mcp")
742
773
  return (candidate.length > 0 &&
743
774
  allowedRoots.some((root) => (0, path_containment_1.isPathWithin)(root, candidate)));
744
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
+ }); };
745
796
  // Prepend any searcher warnings to whatever body we return.
746
797
  const prefixNotes = (body) => {
747
798
  var _a;
@@ -749,7 +800,7 @@ exports.mcp = new commander_1.Command("mcp")
749
800
  return notes.length ? `${notes.join("\n")}\n\n${body}` : body;
750
801
  };
751
802
  if (!result.data || result.data.length === 0) {
752
- 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([]));
753
804
  }
754
805
  const minScore = typeof args.min_score === "number" ? args.min_score : 0;
755
806
  const maxPerFile = typeof args.max_per_file === "number" ? args.max_per_file : 0;
@@ -779,21 +830,25 @@ exports.mcp = new commander_1.Command("mcp")
779
830
  getImportsForFile,
780
831
  query,
781
832
  });
782
- return ok(prefixNotes(output));
833
+ return ok(prefixNotes(output), structuredSearch(filterMcpSearchResults(result.data, {
834
+ minScore,
835
+ maxPerFile,
836
+ namePattern,
837
+ })));
783
838
  }
784
839
  let results = result.data.map((r) => {
785
- var _a, _b, _c;
786
- var _d, _e, _f, _g, _h, _j, _k, _l, _m;
787
- 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 : "";
788
843
  const relPath = absPath.startsWith(displayRoot)
789
844
  ? absPath.slice(displayRoot.length + 1)
790
845
  : absPath;
791
- 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;
792
- 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;
793
- const defs = toStringArray((_k = r.definedSymbols) !== null && _k !== void 0 ? _k : r.defined_symbols);
794
- 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);
795
850
  const symbol = defs[0] || "(anonymous)";
796
- 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();
797
852
  const exported = r.is_exported ? "exported " : "";
798
853
  const complexity = typeof r.complexity === "number" && r.complexity > 0
799
854
  ? ` C:${Math.round(r.complexity)}`
@@ -825,7 +880,7 @@ exports.mcp = new commander_1.Command("mcp")
825
880
  .map((l, i) => `${ctxStart + i + 1}│${l}`)
826
881
  .join("\n");
827
882
  }
828
- catch (_o) {
883
+ catch (_g) {
829
884
  // Fall through to chunk content
830
885
  }
831
886
  }
@@ -855,6 +910,7 @@ exports.mcp = new commander_1.Command("mcp")
855
910
  }
856
911
  }
857
912
  return {
913
+ row: r,
858
914
  absPath,
859
915
  text,
860
916
  score: typeof r.score === "number" ? r.score : 0,
@@ -927,7 +983,7 @@ exports.mcp = new commander_1.Command("mcp")
927
983
  // Trace failed — return search results without trace
928
984
  }
929
985
  }
930
- return ok(prefixNotes(output));
986
+ return ok(prefixNotes(output), structuredSearch(results.map((r) => r.row)));
931
987
  }
932
988
  catch (e) {
933
989
  return toolError("Search failed", e);
@@ -1075,8 +1131,9 @@ exports.mcp = new commander_1.Command("mcp")
1075
1131
  render: (resp) => resp.graph,
1076
1132
  inProcess: () => localStore((deps) => (0, graph_handler_1.runGraphTrace)(deps.vectorDb, { symbol, hops: depth, scope })),
1077
1133
  });
1134
+ const structured = (0, mcp_results_1.traceResult)(symbol, root, graph);
1078
1135
  if (!graph.center) {
1079
- 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);
1080
1137
  }
1081
1138
  const lines = [];
1082
1139
  // Center
@@ -1135,7 +1192,7 @@ exports.mcp = new commander_1.Command("mcp")
1135
1192
  else {
1136
1193
  lines.push("Calls: none");
1137
1194
  }
1138
- return ok(lines.join("\n"));
1195
+ return ok(lines.join("\n"), structured);
1139
1196
  }
1140
1197
  catch (e) {
1141
1198
  return toolError("Trace failed", e);
@@ -1376,16 +1433,17 @@ exports.mcp = new commander_1.Command("mcp")
1376
1433
  render: (resp) => resp.dead,
1377
1434
  inProcess: () => localStore((deps) => (0, graph_handler_1.runGraphDead)(deps.vectorDb, { symbol, scope })),
1378
1435
  });
1436
+ const structured = (0, mcp_results_1.deadResult)(symbol, root, facts);
1379
1437
  if (!facts.found) {
1380
- 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);
1381
1439
  }
1382
1440
  const rel = (p) => p.startsWith(root) ? p.slice(root.length + 1) : p;
1383
1441
  const defLoc = `${rel(facts.defPath)}:${facts.defLine + 1}`;
1384
1442
  if (facts.callerCount === 0) {
1385
1443
  if (facts.isExported) {
1386
- 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);
1387
1445
  }
1388
- return ok(`DEAD ${defLoc} defines ${symbol}`);
1446
+ return ok(`DEAD ${defLoc} defines ${symbol}`, structured);
1389
1447
  }
1390
1448
  const top = facts.topCallers;
1391
1449
  const lines = [
@@ -1394,7 +1452,7 @@ exports.mcp = new commander_1.Command("mcp")
1394
1452
  for (const c of top) {
1395
1453
  lines.push(` ${rel(c.file)}:${c.line + 1}`);
1396
1454
  }
1397
- return ok(lines.join("\n"));
1455
+ return ok(lines.join("\n"), structured);
1398
1456
  }
1399
1457
  catch (e) {
1400
1458
  return toolError("Dead check failed", e);
@@ -1711,26 +1769,38 @@ exports.mcp = new commander_1.Command("mcp")
1711
1769
  });
1712
1770
  }
1713
1771
  function handleListProjects() {
1714
- return __awaiter(this, void 0, void 0, function* () {
1772
+ return __awaiter(this, arguments, void 0, function* (args = {}) {
1715
1773
  var _a;
1716
1774
  try {
1717
- const projects = (0, project_registry_1.listProjects)();
1718
- if (projects.length === 0) {
1775
+ const inventory = args.root === undefined ? (0, store_context_1.storeInventory)() : [];
1776
+ const projects = [
1777
+ ...storeProjects(),
1778
+ ...inventory
1779
+ .filter((store) => { var _a; var _b; return store.home !==
1780
+ ((_b = (_a = (0, store_context_1.currentStoreContext)()) === null || _a === void 0 ? void 0 : _a.home) !== null && _b !== void 0 ? _b : config_1.PATHS.globalRoot); })
1781
+ .flatMap((store) => store.projects),
1782
+ ];
1783
+ if (projects.length === 0 && inventory.length === 0) {
1719
1784
  return ok("No projects indexed yet. cd into a repo and run `gmax add` to index it.");
1720
1785
  }
1721
- const currentName = (_a = resolveMcpProject(undefined, projectRoot)) === null || _a === void 0 ? void 0 : _a.name;
1722
- const globalConfig = (0, index_config_1.readGlobalConfig)();
1786
+ const currentName = (_a = resolveMcpProject(undefined, currentRoot(), storeProjects())) === null || _a === void 0 ? void 0 : _a.name;
1787
+ const globalConfig = storeConfig();
1723
1788
  const lines = projects.map((p) => {
1724
1789
  const here = p.name === currentName ? " (current)" : "";
1725
1790
  const chunks = typeof p.chunkCount === "number"
1726
1791
  ? `\t(${p.chunkCount} chunks)`
1727
1792
  : "";
1728
- const identity = (0, embedding_status_1.projectEmbeddingStatus)(p, globalConfig);
1793
+ const matching = inventory.find((store) => store.projects.some((project) => project.root === p.root));
1794
+ const identity = (0, embedding_status_1.projectEmbeddingStatus)(p, matching ? (0, index_config_1.readGlobalConfig)(matching.home) : globalConfig);
1729
1795
  return `${p.name}${here}\t${p.root}\t${p.status}\tembedding=${identity.state}${chunks}`;
1730
1796
  });
1731
1797
  return ok(`${projects.length} indexed project(s). ` +
1732
1798
  `Pass a name to semantic_search via projects:"name", or use scope:"all".\n\n` +
1733
- lines.join("\n"));
1799
+ lines.join("\n") +
1800
+ inventory
1801
+ .filter((store) => store.state !== "mounted")
1802
+ .map((store) => `\nStore ${store.home}: ${store.state}${store.error ? ` — ${store.error}` : ""}`)
1803
+ .join(""));
1734
1804
  }
1735
1805
  catch (e) {
1736
1806
  const msg = e instanceof Error ? e.message : String(e);
@@ -1740,20 +1810,22 @@ exports.mcp = new commander_1.Command("mcp")
1740
1810
  }
1741
1811
  function handleIndexStatus() {
1742
1812
  return __awaiter(this, void 0, void 0, function* () {
1743
- var _a, _b;
1813
+ var _a, _b, _c, _d, _e;
1744
1814
  try {
1745
- const globalConfig = (0, index_config_1.readGlobalConfig)();
1746
- const projects = (0, project_registry_1.listProjects)();
1747
- const currentProject = resolveMcpProject(undefined, projectRoot);
1815
+ const globalConfig = storeConfig();
1816
+ const projects = storeProjects();
1817
+ const currentProject = resolveMcpProject(undefined, currentRoot(), storeProjects());
1748
1818
  const identity = (0, embedding_status_1.projectEmbeddingStatus)(currentProject, globalConfig);
1749
1819
  const { chunks, files } = yield indexTotals(projects);
1750
- const daemonStatus = yield (0, daemon_client_1.sendDaemonCommand)({ cmd: "status" });
1820
+ const daemonStatus = secondary()
1821
+ ? { ok: true, projects: [] }
1822
+ : yield (0, daemon_client_1.sendDaemonCommand)({ cmd: "status" });
1751
1823
  const watched = (Array.isArray(daemonStatus.projects) ? daemonStatus.projects : []);
1752
1824
  const daemonProject = watched.find((p) => p.root === (currentProject === null || currentProject === void 0 ? void 0 : currentProject.root));
1753
1825
  // Only use the registry when no daemon answered; sandboxed reads stay IPC-only.
1754
1826
  const watcher = daemonStatus.ok
1755
1827
  ? undefined
1756
- : (0, watcher_store_1.getWatcherCoveringPath)(projectRoot);
1828
+ : (0, watcher_store_1.getWatcherCoveringPath)(currentRoot());
1757
1829
  let watcherLine = "Watcher: not running";
1758
1830
  if (watcher) {
1759
1831
  const status = (_a = watcher.status) !== null && _a !== void 0 ? _a : "unknown";
@@ -1778,7 +1850,7 @@ exports.mcp = new commander_1.Command("mcp")
1778
1850
  // per-project chunk counts) lives in `list_projects` — keep this tool
1779
1851
  // focused on index health and avoid the N-project LanceDB scans.
1780
1852
  const lines = [
1781
- `Index: ~/.gmax/lancedb (${chunks} chunks, ${files} files)`,
1853
+ `Index: ${storeDir()} (${chunks} chunks, ${files} files)`,
1782
1854
  `Configured embedding: ${identity.configured.tier} ${identity.configured.vectorDim}d [${(0, embedding_status_1.embeddingFingerprintLabel)(identity.configured.fingerprint)}] (${globalConfig.embedMode})`,
1783
1855
  identity.built
1784
1856
  ? `Built embedding: ${identity.built.tier} ${identity.built.vectorDim}d [${(0, embedding_status_1.embeddingFingerprintLabel)(identity.built.fingerprint)}] (${identity.state})`
@@ -1790,7 +1862,36 @@ exports.mcp = new commander_1.Command("mcp")
1790
1862
  (0, compaction_result_1.formatCompactionStatus)(daemonStatus.compaction),
1791
1863
  `Projects: ${projects.length} indexed (call list_projects for names + per-project chunk counts)`,
1792
1864
  ].filter(Boolean);
1793
- 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
+ });
1794
1895
  }
1795
1896
  catch (e) {
1796
1897
  return toolError("Status check failed", e);
@@ -2001,6 +2102,8 @@ exports.mcp = new commander_1.Command("mcp")
2001
2102
  function handleRecentChanges(args) {
2002
2103
  return __awaiter(this, void 0, void 0, function* () {
2003
2104
  var _a, e_1, _b, _c;
2105
+ var _d;
2106
+ var _e;
2004
2107
  ensureWatcher();
2005
2108
  const limit = Math.min(Math.max(Number(args.limit) || 20, 1), 50);
2006
2109
  const { proj, root } = resolveRegisteredProject(args.root);
@@ -2008,13 +2111,13 @@ exports.mcp = new commander_1.Command("mcp")
2008
2111
  return err("Project not added to gmax yet.");
2009
2112
  const prefix = root.endsWith("/") ? root : `${root}/`;
2010
2113
  try {
2011
- const metaCache = new meta_cache_1.MetaCache(config_1.PATHS.lmdbPath);
2114
+ const metaCache = new meta_cache_1.MetaCache((_e = (_d = (0, store_context_1.currentStoreContext)()) === null || _d === void 0 ? void 0 : _d.lmdbPath) !== null && _e !== void 0 ? _e : config_1.PATHS.lmdbPath);
2012
2115
  try {
2013
2116
  const files = [];
2014
2117
  try {
2015
- for (var _d = true, _e = __asyncValues(metaCache.entries()), _f; _f = yield _e.next(), _a = _f.done, !_a; _d = true) {
2016
- _c = _f.value;
2017
- _d = false;
2118
+ for (var _f = true, _g = __asyncValues(metaCache.entries()), _h; _h = yield _g.next(), _a = _h.done, !_a; _f = true) {
2119
+ _c = _h.value;
2120
+ _f = false;
2018
2121
  const { path: p, entry } = _c;
2019
2122
  if (p.startsWith(prefix)) {
2020
2123
  files.push({ path: p, mtimeMs: entry.mtimeMs });
@@ -2024,7 +2127,7 @@ exports.mcp = new commander_1.Command("mcp")
2024
2127
  catch (e_1_1) { e_1 = { error: e_1_1 }; }
2025
2128
  finally {
2026
2129
  try {
2027
- if (!_d && !_a && (_b = _e.return)) yield _b.call(_e);
2130
+ if (!_f && !_a && (_b = _g.return)) yield _b.call(_g);
2028
2131
  }
2029
2132
  finally { if (e_1) throw e_1.error; }
2030
2133
  }
@@ -2069,7 +2172,7 @@ exports.mcp = new commander_1.Command("mcp")
2069
2172
  const { proj, root } = resolveRegisteredProject();
2070
2173
  if (!proj)
2071
2174
  return err("Project not added to gmax yet.");
2072
- (0, embedding_status_1.assertEmbeddingSearchCompatible)([proj], (0, index_config_1.readGlobalConfig)());
2175
+ (0, embedding_status_1.assertEmbeddingSearchCompatible)([proj], storeConfig());
2073
2176
  const changedFiles = getChangedFiles(ref, root);
2074
2177
  if (changedFiles.length === 0) {
2075
2178
  return ok(ref
@@ -2269,7 +2372,7 @@ exports.mcp = new commander_1.Command("mcp")
2269
2372
  const { proj, root } = resolveRegisteredProject();
2270
2373
  if (!proj)
2271
2374
  return err("Project not added to gmax yet.");
2272
- (0, embedding_status_1.assertEmbeddingSearchCompatible)([proj], (0, index_config_1.readGlobalConfig)());
2375
+ (0, embedding_status_1.assertEmbeddingSearchCompatible)([proj], storeConfig());
2273
2376
  const isFile = target.includes("/") ||
2274
2377
  (target.includes(".") && !target.includes(" "));
2275
2378
  // Resolved here: containment is this process's filesystem question.
@@ -2335,7 +2438,7 @@ exports.mcp = new commander_1.Command("mcp")
2335
2438
  const { proj, root } = resolveRegisteredProject();
2336
2439
  if (!proj)
2337
2440
  return err("Project not added to gmax yet.");
2338
- (0, embedding_status_1.assertEmbeddingSearchCompatible)([proj], (0, index_config_1.readGlobalConfig)());
2441
+ (0, embedding_status_1.assertEmbeddingSearchCompatible)([proj], storeConfig());
2339
2442
  const searchOptions = { rerank: process.env.GMAX_RERANK === "1" };
2340
2443
  const response = yield searchStore(Object.assign({ projectRoot: root, query: topic, limit, filters: {}, pathPrefix: root }, searchOptions));
2341
2444
  if (response.data.length === 0)
@@ -2405,7 +2508,11 @@ exports.mcp = new commander_1.Command("mcp")
2405
2508
  return err("LLM server not available. Run `gmax llm on && gmax llm start`.");
2406
2509
  }
2407
2510
  const { investigate } = yield Promise.resolve().then(() => __importStar(require("../lib/llm/investigate")));
2408
- const inv = yield investigate({ question, projectRoot, maxRounds });
2511
+ const inv = yield investigate({
2512
+ question,
2513
+ projectRoot: currentRoot(),
2514
+ maxRounds,
2515
+ });
2409
2516
  return ok(inv.answer);
2410
2517
  }
2411
2518
  catch (e) {
@@ -2429,12 +2536,15 @@ exports.mcp = new commander_1.Command("mcp")
2429
2536
  return err("LLM server not available. Run `gmax llm on && gmax llm start`.");
2430
2537
  }
2431
2538
  const { reviewCommit } = yield Promise.resolve().then(() => __importStar(require("../lib/llm/review")));
2432
- const rev = yield reviewCommit({ commitRef, projectRoot });
2539
+ const rev = yield reviewCommit({
2540
+ commitRef,
2541
+ projectRoot: currentRoot(),
2542
+ });
2433
2543
  if (rev.clean) {
2434
2544
  return ok(`Clean commit (${rev.commit}) — no issues found in ${rev.duration}s.`);
2435
2545
  }
2436
2546
  const { readReport } = yield Promise.resolve().then(() => __importStar(require("../lib/llm/report")));
2437
- const report = readReport(projectRoot);
2547
+ const report = readReport(currentRoot());
2438
2548
  const entry = report === null || report === void 0 ? void 0 : report.reviews.find((r) => r.commit === rev.commit);
2439
2549
  return ok(JSON.stringify({
2440
2550
  commit: rev.commit,
@@ -2459,26 +2569,26 @@ exports.mcp = new commander_1.Command("mcp")
2459
2569
  // returns the location.
2460
2570
  const { extractDiff, extractSymbols, fileChurn } = yield Promise.resolve().then(() => __importStar(require("../lib/llm/diff")));
2461
2571
  const { computeRiskTable, formatRiskTable } = yield Promise.resolve().then(() => __importStar(require("../lib/review/risk")));
2462
- const diff = extractDiff(commitRef, projectRoot);
2572
+ const diff = extractDiff(commitRef, currentRoot());
2463
2573
  const symbols = diff ? extractSymbols(diff) : [];
2464
2574
  if (symbols.length === 0) {
2465
2575
  return ok("(no changed symbols in this diff)");
2466
2576
  }
2467
- const scope = projectScope(projectRoot);
2577
+ const scope = projectScope(currentRoot());
2468
2578
  const facts = yield graphVerb("graph.risk", {
2469
- projectRoot,
2579
+ projectRoot: currentRoot(),
2470
2580
  scope,
2471
2581
  payload: { symbols },
2472
2582
  render: (resp) => { var _a; return (_a = resp.facts) !== null && _a !== void 0 ? _a : []; },
2473
2583
  inProcess: () => localStore((deps) => (0, graph_handler_1.runGraphRisk)(deps.vectorDb, {
2474
2584
  symbols,
2475
- queryRoot: projectRoot,
2585
+ queryRoot: currentRoot(),
2476
2586
  scope,
2477
2587
  })),
2478
2588
  });
2479
- const prefix = projectRoot.endsWith("/")
2480
- ? projectRoot
2481
- : `${projectRoot}/`;
2589
+ const prefix = currentRoot().endsWith("/")
2590
+ ? currentRoot()
2591
+ : `${currentRoot()}/`;
2482
2592
  const rows = computeRiskTable(facts.map((fact) => ({
2483
2593
  symbol: fact.symbol,
2484
2594
  file: fact.file
@@ -2489,7 +2599,7 @@ exports.mcp = new commander_1.Command("mcp")
2489
2599
  line: fact.line,
2490
2600
  callerCount: fact.callerCount,
2491
2601
  hasTests: fact.hasTests,
2492
- churn: fileChurn(fact.file, projectRoot),
2602
+ churn: fileChurn(fact.file, currentRoot()),
2493
2603
  })));
2494
2604
  return rows.length === 0
2495
2605
  ? ok("(no changed symbols in this diff)")
@@ -2504,7 +2614,7 @@ exports.mcp = new commander_1.Command("mcp")
2504
2614
  return __awaiter(this, void 0, void 0, function* () {
2505
2615
  try {
2506
2616
  const { readReport, formatReportText } = yield Promise.resolve().then(() => __importStar(require("../lib/llm/report")));
2507
- const report = readReport(projectRoot);
2617
+ const report = readReport(currentRoot());
2508
2618
  if (!report || report.reviews.length === 0) {
2509
2619
  return ok("No review findings yet.");
2510
2620
  }
@@ -2519,6 +2629,7 @@ exports.mcp = new commander_1.Command("mcp")
2519
2629
  });
2520
2630
  }
2521
2631
  // --- MCP server setup ---
2632
+ // Local-only MCP: client-owned pipes, no HTTP/SSE/TCP listener or host option.
2522
2633
  const transport = new stdio_js_1.StdioServerTransport();
2523
2634
  const server = new mcp_js_1.McpServer({
2524
2635
  name: "gmax",
@@ -2554,7 +2665,7 @@ exports.mcp = new commander_1.Command("mcp")
2554
2665
  source: "mcp",
2555
2666
  tool: name,
2556
2667
  query: mcpLogQuery(name, toolArgs),
2557
- project: projectRoot,
2668
+ project: currentRoot(),
2558
2669
  results: resultLines,
2559
2670
  ms: Date.now() - startMs,
2560
2671
  error: result.isError ? text.slice(0, 200) : undefined,
@@ -2566,21 +2677,51 @@ exports.mcp = new commander_1.Command("mcp")
2566
2677
  // shapes give us free input validation (the SDK rejects calls that violate
2567
2678
  // the schema before the handler runs); handlers stay defensively coded.
2568
2679
  function tool(name, config, handler) {
2569
- server.registerTool(name, config, (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* () {
2570
2690
  let args = (rawArgs !== null && rawArgs !== void 0 ? rawArgs : {});
2571
2691
  const startMs = Date.now();
2572
- if (typeof args.root === "string" && args.root.trim()) {
2573
- const selected = resolveMcpProject(args.root, projectRoot);
2574
- if (!selected) {
2575
- const result = err(`Unknown registered project: ${args.root}. Use list_projects to select a project name or exact root.`);
2692
+ let target = projectRoot;
2693
+ try {
2694
+ if (typeof args.root === "string" && args.root.trim()) {
2695
+ const raw = args.root.trim();
2696
+ if (path.isAbsolute(raw) || raw.includes(path.sep))
2697
+ target = path.resolve(raw);
2698
+ else {
2699
+ const projects = [
2700
+ ...(0, project_registry_1.listProjects)(),
2701
+ ...(0, store_context_1.storeInventory)().flatMap((store) => store.projects),
2702
+ ].filter((project, i, all) => all.findIndex((p) => p.root === project.root) === i);
2703
+ const selected = resolveMcpProject(raw, projectRoot, projects);
2704
+ if (!selected)
2705
+ return err(`Unknown or ambiguous registered project: ${raw}. Use list_projects or an exact root.`);
2706
+ target = selected.root;
2707
+ }
2708
+ }
2709
+ return yield (0, store_context_1.withStoreContext)(target, () => __awaiter(this, void 0, void 0, function* () {
2710
+ if (args.root !== undefined) {
2711
+ const selected = resolveMcpProject(args.root, currentRoot(), storeProjects());
2712
+ if (!selected)
2713
+ return err(`Unknown registered project: ${args.root}. Use list_projects to select a project name or exact root.`);
2714
+ args = Object.assign(Object.assign({}, args), { root: selected.root });
2715
+ (0, store_context_1.currentStoreContext)().root = selected.root;
2716
+ }
2717
+ const result = yield handler(args);
2576
2718
  yield logToolCall(name, args, startMs, result);
2577
2719
  return result;
2578
- }
2579
- args = Object.assign(Object.assign({}, args), { root: selected.root });
2720
+ }));
2721
+ }
2722
+ catch (error) {
2723
+ return toolError("Tool failed", error);
2580
2724
  }
2581
- const result = yield handler(args);
2582
- yield logToolCall(name, args, startMs, result);
2583
- return result;
2584
2725
  }));
2585
2726
  }
2586
2727
  tool("semantic_search", {