grepmax 0.26.43 → 0.26.44

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 (92) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/NOTICE +6 -0
  3. package/README.md +3 -1
  4. package/dist/bin.js +4 -0
  5. package/dist/commands/doctor.js +2 -1
  6. package/dist/commands/mcp.js +142 -67
  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/search/pagerank.js +2 -1
  26. package/dist/lib/search/searcher.js +5 -4
  27. package/dist/lib/skeleton/retriever.js +2 -1
  28. package/dist/lib/store/lance-sdk.js +46 -0
  29. package/dist/lib/store/vector-db.js +57 -16
  30. package/dist/lib/utils/cross-project.js +2 -2
  31. package/dist/lib/utils/project-registry.js +8 -8
  32. package/dist/lib/utils/query-timeout.js +53 -13
  33. package/dist/lib/utils/store-access.js +6 -5
  34. package/dist/lib/utils/store-context.js +102 -0
  35. package/dist/lib/utils/stores.js +1 -1
  36. package/dist/vendor/lancedb/LICENSE +202 -0
  37. package/dist/vendor/lancedb/NODEJS_THIRD_PARTY_LICENSES.md +668 -0
  38. package/dist/vendor/lancedb/RUST_THIRD_PARTY_LICENSES.html +14607 -0
  39. package/dist/vendor/lancedb/arrow.d.ts +296 -0
  40. package/dist/vendor/lancedb/arrow.js +1145 -0
  41. package/dist/vendor/lancedb/arrow_type.d.ts +9 -0
  42. package/dist/vendor/lancedb/arrow_type.js +29 -0
  43. package/dist/vendor/lancedb/connection.d.ts +508 -0
  44. package/dist/vendor/lancedb/connection.js +298 -0
  45. package/dist/vendor/lancedb/embedding/embedding_function.d.ts +103 -0
  46. package/dist/vendor/lancedb/embedding/embedding_function.js +192 -0
  47. package/dist/vendor/lancedb/embedding/index.d.ts +37 -0
  48. package/dist/vendor/lancedb/embedding/index.js +108 -0
  49. package/dist/vendor/lancedb/embedding/openai.d.ts +16 -0
  50. package/dist/vendor/lancedb/embedding/openai.js +81 -0
  51. package/dist/vendor/lancedb/embedding/registry.d.ts +94 -0
  52. package/dist/vendor/lancedb/embedding/registry.js +221 -0
  53. package/dist/vendor/lancedb/embedding/transformers.d.ts +36 -0
  54. package/dist/vendor/lancedb/embedding/transformers.js +110 -0
  55. package/dist/vendor/lancedb/header.d.ts +162 -0
  56. package/dist/vendor/lancedb/header.js +217 -0
  57. package/dist/vendor/lancedb/index.d.ts +246 -0
  58. package/dist/vendor/lancedb/index.js +176 -0
  59. package/dist/vendor/lancedb/indices.d.ts +721 -0
  60. package/dist/vendor/lancedb/indices.js +166 -0
  61. package/dist/vendor/lancedb/license_header.txt +2 -0
  62. package/dist/vendor/lancedb/materialized_view.d.ts +69 -0
  63. package/dist/vendor/lancedb/materialized_view.js +112 -0
  64. package/dist/vendor/lancedb/merge.d.ts +104 -0
  65. package/dist/vendor/lancedb/merge.js +120 -0
  66. package/dist/vendor/lancedb/native.d.ts +1193 -0
  67. package/dist/vendor/lancedb/native.js +688 -0
  68. package/dist/vendor/lancedb/oauth.d.ts +66 -0
  69. package/dist/vendor/lancedb/oauth.js +15 -0
  70. package/dist/vendor/lancedb/otel.d.ts +26 -0
  71. package/dist/vendor/lancedb/otel.js +114 -0
  72. package/dist/vendor/lancedb/permutation.d.ts +143 -0
  73. package/dist/vendor/lancedb/permutation.js +184 -0
  74. package/dist/vendor/lancedb/provenance.json +6 -0
  75. package/dist/vendor/lancedb/query.d.ts +663 -0
  76. package/dist/vendor/lancedb/query.js +966 -0
  77. package/dist/vendor/lancedb/rerankers/index.d.ts +5 -0
  78. package/dist/vendor/lancedb/rerankers/index.js +19 -0
  79. package/dist/vendor/lancedb/rerankers/rrf.d.ts +14 -0
  80. package/dist/vendor/lancedb/rerankers/rrf.js +28 -0
  81. package/dist/vendor/lancedb/sanitize.d.ts +32 -0
  82. package/dist/vendor/lancedb/sanitize.js +544 -0
  83. package/dist/vendor/lancedb/scannable.d.ts +92 -0
  84. package/dist/vendor/lancedb/scannable.js +200 -0
  85. package/dist/vendor/lancedb/schema.d.ts +16 -0
  86. package/dist/vendor/lancedb/schema.js +387 -0
  87. package/dist/vendor/lancedb/table.d.ts +1069 -0
  88. package/dist/vendor/lancedb/table.js +498 -0
  89. package/dist/vendor/lancedb/util.d.ts +14 -0
  90. package/dist/vendor/lancedb/util.js +77 -0
  91. package/package.json +19 -7
  92. package/plugins/grepmax/.claude-plugin/plugin.json +1 -1
@@ -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.43",
13
+ "version": "0.26.44",
14
14
  "author": {
15
15
  "name": "Robert Owens",
16
16
  "email": "robowens@me.com"
package/NOTICE CHANGED
@@ -31,3 +31,9 @@ THIRD-PARTY DEPENDENCIES
31
31
 
32
32
  This software depends on various open-source libraries. See package.json for
33
33
  a complete list of dependencies and their respective licenses.
34
+
35
+ The distribution includes the unmodified JavaScript runtime from
36
+ @lancedb/lancedb 0.38.0 (https://github.com/lancedb/lancedb), licensed under
37
+ Apache-2.0. Its license and build provenance are included in dist/vendor/lancedb/.
38
+ Its optional provider dependency manifest is not distributed. Platform native
39
+ binaries are installed as separately pinned @lancedb packages.
package/README.md CHANGED
@@ -126,7 +126,9 @@ Plugins auto-update when you run `npm install -g grepmax@latest` — no need to
126
126
 
127
127
  ### MCP Server
128
128
 
129
- `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 fall back to an in-process worker only when the daemon is unavailable or does not support the request.
129
+ MCP is local-only: the client spawns it and communicates through stdin/stdout pipes. It has no HTTP, SSE, or TCP listener and cannot be reached over the network. The daemon socket is restricted to your user account.
130
+
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`.
130
132
 
131
133
  | Tool | Description |
132
134
  | --- | --- |
package/dist/bin.js CHANGED
@@ -75,6 +75,10 @@ function selectHome() {
75
75
  if (process.env.GMAX_HOME)
76
76
  return;
77
77
  const argv = process.argv.slice(2);
78
+ // These enumerate/route stores per request rather than pinning the process
79
+ // to its launch cwd's store. In particular an MCP session can serve both.
80
+ if (argv[0] === "mcp" || argv[0] === "status")
81
+ return;
78
82
  const match = (0, stores_1.matchStore)(commandTarget(argv, process.cwd()));
79
83
  if (match.kind === "primary")
80
84
  return;
@@ -58,6 +58,7 @@ const lock_1 = require("../lib/utils/lock");
58
58
  const mlx_hf_cache_1 = require("../lib/utils/mlx-hf-cache");
59
59
  const project_registry_1 = require("../lib/utils/project-registry");
60
60
  const project_root_1 = require("../lib/utils/project-root");
61
+ const query_timeout_1 = require("../lib/utils/query-timeout");
61
62
  const root_availability_1 = require("../lib/utils/root-availability");
62
63
  const store_access_1 = require("../lib/utils/store-access");
63
64
  function formatSize(bytes) {
@@ -489,7 +490,7 @@ exports.doctor = new commander_1.Command("doctor")
489
490
  .query()
490
491
  .where("length(summary) > 5")
491
492
  .select(["id"])
492
- .toArray()).length;
493
+ .toArray(query_timeout_1.QUERY_EXECUTION_OPTIONS)).length;
493
494
  const coverage = (0, doctor_status_1.summaryCoverageStatus)(withSummary, totalChunks);
494
495
  console.log(`${coverage.symbol} ${coverage.message}`);
495
496
  }
@@ -79,6 +79,7 @@ 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");
@@ -100,8 +101,11 @@ const project_registry_1 = require("../lib/utils/project-registry");
100
101
  const project_root_1 = require("../lib/utils/project-root");
101
102
  const scope_filter_1 = require("../lib/utils/scope-filter");
102
103
  const store_access_1 = require("../lib/utils/store-access");
104
+ const store_context_1 = require("../lib/utils/store-context");
105
+ const stores_1 = require("../lib/utils/stores");
103
106
  const watcher_launcher_1 = require("../lib/utils/watcher-launcher");
104
107
  const watcher_store_1 = require("../lib/utils/watcher-store");
108
+ const pool_1 = require("../lib/workers/pool");
105
109
  // ---------------------------------------------------------------------------
106
110
  // Helpers
107
111
  // ---------------------------------------------------------------------------
@@ -143,9 +147,6 @@ function isMcpSessionFallback(error) {
143
147
  /**
144
148
  * MCP's read-tool fallback policy, in one object so every tool shares it.
145
149
  *
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
150
  * - `fallbackOnOversize` — the daemon's 2 MB response cap. See
150
151
  * `isOversizeError` for why MCP opts in and the CLI does not.
151
152
  * - `extraFallback` — the two session-readiness answers above.
@@ -161,7 +162,6 @@ function isMcpSessionFallback(error) {
161
162
  const MCP_WATCH_LEASE_RENEW_MS = 5 * 60 * 1000;
162
163
  const MCP_WATCH_LEASE_TTL_MS = 3 * MCP_WATCH_LEASE_RENEW_MS;
163
164
  const MCP_STORE_FALLBACK = {
164
- fallbackOnUnknownVerb: true,
165
165
  fallbackOnOversize: true,
166
166
  extraFallback: isMcpSessionFallback,
167
167
  };
@@ -172,7 +172,6 @@ const MCP_STORE_FALLBACK = {
172
172
  */
173
173
  function shouldFallbackMcpDaemonSearch(error) {
174
174
  return ((0, store_access_1.classifyDaemonError)(error) === "no-daemon" ||
175
- (0, store_access_1.isUnknownCommandError)(error) ||
176
175
  (0, store_access_1.isOversizeError)(error) ||
177
176
  isMcpSessionFallback(error));
178
177
  }
@@ -192,14 +191,30 @@ function resolveMcpProject(requested, currentRoot, projects = (0, project_regist
192
191
  if (byName.length > 1)
193
192
  return undefined;
194
193
  const resolved = path.resolve(value);
195
- return projects.find((project) => path.resolve(project.root) === resolved);
194
+ const physical = (value) => {
195
+ try {
196
+ return fs.realpathSync(value);
197
+ }
198
+ catch (_a) {
199
+ return path.resolve(value);
200
+ }
201
+ };
202
+ return projects.find((project) => physical(project.root) === physical(resolved));
196
203
  }
197
204
  const resolvedCurrent = path.resolve(currentRoot);
198
- const exact = projects.find((project) => path.resolve(project.root) === resolvedCurrent);
205
+ const physical = (value) => {
206
+ try {
207
+ return fs.realpathSync(value);
208
+ }
209
+ catch (_a) {
210
+ return path.resolve(value);
211
+ }
212
+ };
213
+ const exact = projects.find((project) => physical(project.root) === physical(resolvedCurrent));
199
214
  if (exact)
200
215
  return exact;
201
216
  return projects
202
- .filter((project) => (0, path_containment_1.isPathWithin)(project.root, resolvedCurrent))
217
+ .filter((project) => (0, path_containment_1.isPathWithin)(physical(project.root), physical(resolvedCurrent)))
203
218
  .sort((a, b) => b.root.length - a.root.length)[0];
204
219
  }
205
220
  function containMcpTarget(root, target) {
@@ -380,6 +395,11 @@ exports.mcp = new commander_1.Command("mcp")
380
395
  // --- Project context ---
381
396
  const projectRoot = (0, project_root_1.findProjectRoot)(process.cwd());
382
397
  const paths = (0, project_root_1.ensureProjectPaths)(projectRoot);
398
+ 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; };
399
+ 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)(); };
400
+ 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)(); };
401
+ 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; };
402
+ 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
403
  // Propagate project root to worker processes
384
404
  process.env.GMAX_PROJECT_ROOT = paths.root;
385
405
  // --- Store access ---
@@ -388,15 +408,15 @@ exports.mcp = new commander_1.Command("mcp")
388
408
  * `inProcess` branch is entered only when no daemon serves the verb.
389
409
  */
390
410
  function readStore(name, opts) {
391
- return (0, store_access_1.withStoreRead)(name, Object.assign(Object.assign({}, opts), MCP_STORE_FALLBACK));
411
+ return (0, store_access_1.withStoreRead)(name, Object.assign(Object.assign(Object.assign({}, opts), MCP_STORE_FALLBACK), { skipDaemon: secondary() }));
392
412
  }
393
413
  /** Open the shared store for the duration of one fallback, then close it. */
394
414
  function localStore(fn) {
395
- return (0, rows_handler_1.withLocalStore)(paths.lancedbDir, fn);
415
+ return (0, rows_handler_1.withLocalStore)(storeDir(), fn, storeConfig().vectorDim);
396
416
  }
397
417
  /** A graph verb with MCP's fallback allowances. */
398
418
  function graphVerb(verb, call) {
399
- return (0, graph_handler_1.callGraphVerb)(verb, Object.assign(Object.assign({}, call), { fallback: MCP_STORE_FALLBACK }));
419
+ return (0, graph_handler_1.callGraphVerb)(verb, Object.assign(Object.assign({}, call), { fallback: Object.assign(Object.assign({}, MCP_STORE_FALLBACK), (secondary() ? { skipDaemon: true } : {})) }));
400
420
  }
401
421
  /**
402
422
  * MCP tools take no `--in`/`--exclude`, so a tool's scope is always the
@@ -440,8 +460,16 @@ exports.mcp = new commander_1.Command("mcp")
440
460
  };
441
461
  },
442
462
  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);
463
+ const pool = secondary()
464
+ ? new pool_1.WorkerPool((0, embedding_generation_1.resolveEmbeddingGeneration)(storeConfig()), storeConfig().embedMode)
465
+ : undefined;
466
+ try {
467
+ const searcher = new searcher_1.Searcher(deps.vectorDb, pool);
468
+ return yield searcher.search(args.query, args.limit, { rerank: args.rerank, seeds: args.seeds }, args.filters, args.pathPrefix);
469
+ }
470
+ finally {
471
+ yield pool === null || pool === void 0 ? void 0 : pool.destroy();
472
+ }
445
473
  })),
446
474
  daemonErrorMessage: (resp) => [resp.error, resp.hint]
447
475
  .filter((value) => typeof value === "string" && value)
@@ -509,7 +537,7 @@ exports.mcp = new commander_1.Command("mcp")
509
537
  * order. Batched so a per-symbol loop costs one round trip, not hundreds.
510
538
  */
511
539
  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));
540
+ 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
541
  }
514
542
  /**
515
543
  * Chunk and file totals for `index_status`, summed per registered project.
@@ -574,8 +602,8 @@ exports.mcp = new commander_1.Command("mcp")
574
602
  return __awaiter(this, void 0, void 0, function* () {
575
603
  if (_indexReady)
576
604
  return;
577
- const projects = (0, project_registry_1.listProjects)();
578
- const isRegistered = projects.some((p) => p.root === projectRoot);
605
+ const projects = storeProjects();
606
+ const isRegistered = projects.some((p) => p.root === currentRoot());
579
607
  if (isRegistered) {
580
608
  _indexReady = true;
581
609
  }
@@ -593,8 +621,10 @@ exports.mcp = new commander_1.Command("mcp")
593
621
  return __awaiter(this, void 0, void 0, function* () {
594
622
  var _a;
595
623
  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)
624
+ const watchRoot = root !== null && root !== void 0 ? root : (_a = resolveMcpProject(undefined, currentRoot(), storeProjects())) === null || _a === void 0 ? void 0 : _a.root;
625
+ if (!watchRoot ||
626
+ secondary() ||
627
+ (0, stores_1.matchStore)(watchRoot).kind !== "primary")
598
628
  return;
599
629
  const result = yield (0, watcher_launcher_1.launchWatcher)(watchRoot, WATCH_LEASE);
600
630
  if (result.ok && !result.reused) {
@@ -614,10 +644,10 @@ exports.mcp = new commander_1.Command("mcp")
614
644
  // covers the cwd, and scope to it instead of silently widening to the whole
615
645
  // index. `root` is the registered project root to scope to.
616
646
  function resolveRegisteredProject(requested) {
617
- const project = resolveMcpProject(requested, projectRoot);
647
+ const project = resolveMcpProject(requested, currentRoot(), storeProjects());
618
648
  if (project)
619
649
  return { proj: project, root: project.root };
620
- return { proj: undefined, root: projectRoot };
650
+ return { proj: undefined, root: currentRoot() };
621
651
  }
622
652
  // --- Tool handlers ---
623
653
  function handleSemanticSearch(args_1) {
@@ -648,7 +678,7 @@ exports.mcp = new commander_1.Command("mcp")
648
678
  if (typeof args.root === "string") {
649
679
  return err("Project not added to gmax yet. Run `gmax add` to index it first.");
650
680
  }
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; });
681
+ const indexed = storeProjects().filter((p) => { var _a; return p.status !== "pending" && ((_a = p.chunkCount) !== null && _a !== void 0 ? _a : 0) > 0; });
652
682
  if (indexed.length === 0) {
653
683
  return err("No indexed projects yet. cd into a repo and run `gmax add` to index it first.");
654
684
  }
@@ -692,7 +722,7 @@ exports.mcp = new commander_1.Command("mcp")
692
722
  excludeProjects: typeof args.exclude_projects === "string"
693
723
  ? args.exclude_projects
694
724
  : undefined,
695
- });
725
+ }, storeProjects());
696
726
  if (crossScope.projectRoots.length === 0) {
697
727
  return err("No matching indexed projects.");
698
728
  }
@@ -709,7 +739,7 @@ exports.mcp = new commander_1.Command("mcp")
709
739
  const selectedRoots = searchAll
710
740
  ? new Set((_a = filters.projectRoots) !== null && _a !== void 0 ? _a : [])
711
741
  : 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)());
742
+ (0, embedding_status_1.assertEmbeddingSearchCompatible)(storeProjects().filter((project) => selectedRoots.has(project.root)), storeConfig());
713
743
  // Aider-style seeding: the agent passes its open files / discussed
714
744
  // symbols; the searcher biases candidate generation toward them.
715
745
  const parseSeedList = (v) => {
@@ -1711,26 +1741,38 @@ exports.mcp = new commander_1.Command("mcp")
1711
1741
  });
1712
1742
  }
1713
1743
  function handleListProjects() {
1714
- return __awaiter(this, void 0, void 0, function* () {
1744
+ return __awaiter(this, arguments, void 0, function* (args = {}) {
1715
1745
  var _a;
1716
1746
  try {
1717
- const projects = (0, project_registry_1.listProjects)();
1718
- if (projects.length === 0) {
1747
+ const inventory = args.root === undefined ? (0, store_context_1.storeInventory)() : [];
1748
+ const projects = [
1749
+ ...storeProjects(),
1750
+ ...inventory
1751
+ .filter((store) => { var _a; var _b; return store.home !==
1752
+ ((_b = (_a = (0, store_context_1.currentStoreContext)()) === null || _a === void 0 ? void 0 : _a.home) !== null && _b !== void 0 ? _b : config_1.PATHS.globalRoot); })
1753
+ .flatMap((store) => store.projects),
1754
+ ];
1755
+ if (projects.length === 0 && inventory.length === 0) {
1719
1756
  return ok("No projects indexed yet. cd into a repo and run `gmax add` to index it.");
1720
1757
  }
1721
- const currentName = (_a = resolveMcpProject(undefined, projectRoot)) === null || _a === void 0 ? void 0 : _a.name;
1722
- const globalConfig = (0, index_config_1.readGlobalConfig)();
1758
+ const currentName = (_a = resolveMcpProject(undefined, currentRoot(), storeProjects())) === null || _a === void 0 ? void 0 : _a.name;
1759
+ const globalConfig = storeConfig();
1723
1760
  const lines = projects.map((p) => {
1724
1761
  const here = p.name === currentName ? " (current)" : "";
1725
1762
  const chunks = typeof p.chunkCount === "number"
1726
1763
  ? `\t(${p.chunkCount} chunks)`
1727
1764
  : "";
1728
- const identity = (0, embedding_status_1.projectEmbeddingStatus)(p, globalConfig);
1765
+ const matching = inventory.find((store) => store.projects.some((project) => project.root === p.root));
1766
+ const identity = (0, embedding_status_1.projectEmbeddingStatus)(p, matching ? (0, index_config_1.readGlobalConfig)(matching.home) : globalConfig);
1729
1767
  return `${p.name}${here}\t${p.root}\t${p.status}\tembedding=${identity.state}${chunks}`;
1730
1768
  });
1731
1769
  return ok(`${projects.length} indexed project(s). ` +
1732
1770
  `Pass a name to semantic_search via projects:"name", or use scope:"all".\n\n` +
1733
- lines.join("\n"));
1771
+ lines.join("\n") +
1772
+ inventory
1773
+ .filter((store) => store.state !== "mounted")
1774
+ .map((store) => `\nStore ${store.home}: ${store.state}${store.error ? ` — ${store.error}` : ""}`)
1775
+ .join(""));
1734
1776
  }
1735
1777
  catch (e) {
1736
1778
  const msg = e instanceof Error ? e.message : String(e);
@@ -1742,18 +1784,20 @@ exports.mcp = new commander_1.Command("mcp")
1742
1784
  return __awaiter(this, void 0, void 0, function* () {
1743
1785
  var _a, _b;
1744
1786
  try {
1745
- const globalConfig = (0, index_config_1.readGlobalConfig)();
1746
- const projects = (0, project_registry_1.listProjects)();
1747
- const currentProject = resolveMcpProject(undefined, projectRoot);
1787
+ const globalConfig = storeConfig();
1788
+ const projects = storeProjects();
1789
+ const currentProject = resolveMcpProject(undefined, currentRoot(), storeProjects());
1748
1790
  const identity = (0, embedding_status_1.projectEmbeddingStatus)(currentProject, globalConfig);
1749
1791
  const { chunks, files } = yield indexTotals(projects);
1750
- const daemonStatus = yield (0, daemon_client_1.sendDaemonCommand)({ cmd: "status" });
1792
+ const daemonStatus = secondary()
1793
+ ? { ok: true, projects: [] }
1794
+ : yield (0, daemon_client_1.sendDaemonCommand)({ cmd: "status" });
1751
1795
  const watched = (Array.isArray(daemonStatus.projects) ? daemonStatus.projects : []);
1752
1796
  const daemonProject = watched.find((p) => p.root === (currentProject === null || currentProject === void 0 ? void 0 : currentProject.root));
1753
1797
  // Only use the registry when no daemon answered; sandboxed reads stay IPC-only.
1754
1798
  const watcher = daemonStatus.ok
1755
1799
  ? undefined
1756
- : (0, watcher_store_1.getWatcherCoveringPath)(projectRoot);
1800
+ : (0, watcher_store_1.getWatcherCoveringPath)(currentRoot());
1757
1801
  let watcherLine = "Watcher: not running";
1758
1802
  if (watcher) {
1759
1803
  const status = (_a = watcher.status) !== null && _a !== void 0 ? _a : "unknown";
@@ -1778,7 +1822,7 @@ exports.mcp = new commander_1.Command("mcp")
1778
1822
  // per-project chunk counts) lives in `list_projects` — keep this tool
1779
1823
  // focused on index health and avoid the N-project LanceDB scans.
1780
1824
  const lines = [
1781
- `Index: ~/.gmax/lancedb (${chunks} chunks, ${files} files)`,
1825
+ `Index: ${storeDir()} (${chunks} chunks, ${files} files)`,
1782
1826
  `Configured embedding: ${identity.configured.tier} ${identity.configured.vectorDim}d [${(0, embedding_status_1.embeddingFingerprintLabel)(identity.configured.fingerprint)}] (${globalConfig.embedMode})`,
1783
1827
  identity.built
1784
1828
  ? `Built embedding: ${identity.built.tier} ${identity.built.vectorDim}d [${(0, embedding_status_1.embeddingFingerprintLabel)(identity.built.fingerprint)}] (${identity.state})`
@@ -2001,6 +2045,8 @@ exports.mcp = new commander_1.Command("mcp")
2001
2045
  function handleRecentChanges(args) {
2002
2046
  return __awaiter(this, void 0, void 0, function* () {
2003
2047
  var _a, e_1, _b, _c;
2048
+ var _d;
2049
+ var _e;
2004
2050
  ensureWatcher();
2005
2051
  const limit = Math.min(Math.max(Number(args.limit) || 20, 1), 50);
2006
2052
  const { proj, root } = resolveRegisteredProject(args.root);
@@ -2008,13 +2054,13 @@ exports.mcp = new commander_1.Command("mcp")
2008
2054
  return err("Project not added to gmax yet.");
2009
2055
  const prefix = root.endsWith("/") ? root : `${root}/`;
2010
2056
  try {
2011
- const metaCache = new meta_cache_1.MetaCache(config_1.PATHS.lmdbPath);
2057
+ 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
2058
  try {
2013
2059
  const files = [];
2014
2060
  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;
2061
+ for (var _f = true, _g = __asyncValues(metaCache.entries()), _h; _h = yield _g.next(), _a = _h.done, !_a; _f = true) {
2062
+ _c = _h.value;
2063
+ _f = false;
2018
2064
  const { path: p, entry } = _c;
2019
2065
  if (p.startsWith(prefix)) {
2020
2066
  files.push({ path: p, mtimeMs: entry.mtimeMs });
@@ -2024,7 +2070,7 @@ exports.mcp = new commander_1.Command("mcp")
2024
2070
  catch (e_1_1) { e_1 = { error: e_1_1 }; }
2025
2071
  finally {
2026
2072
  try {
2027
- if (!_d && !_a && (_b = _e.return)) yield _b.call(_e);
2073
+ if (!_f && !_a && (_b = _g.return)) yield _b.call(_g);
2028
2074
  }
2029
2075
  finally { if (e_1) throw e_1.error; }
2030
2076
  }
@@ -2069,7 +2115,7 @@ exports.mcp = new commander_1.Command("mcp")
2069
2115
  const { proj, root } = resolveRegisteredProject();
2070
2116
  if (!proj)
2071
2117
  return err("Project not added to gmax yet.");
2072
- (0, embedding_status_1.assertEmbeddingSearchCompatible)([proj], (0, index_config_1.readGlobalConfig)());
2118
+ (0, embedding_status_1.assertEmbeddingSearchCompatible)([proj], storeConfig());
2073
2119
  const changedFiles = getChangedFiles(ref, root);
2074
2120
  if (changedFiles.length === 0) {
2075
2121
  return ok(ref
@@ -2269,7 +2315,7 @@ exports.mcp = new commander_1.Command("mcp")
2269
2315
  const { proj, root } = resolveRegisteredProject();
2270
2316
  if (!proj)
2271
2317
  return err("Project not added to gmax yet.");
2272
- (0, embedding_status_1.assertEmbeddingSearchCompatible)([proj], (0, index_config_1.readGlobalConfig)());
2318
+ (0, embedding_status_1.assertEmbeddingSearchCompatible)([proj], storeConfig());
2273
2319
  const isFile = target.includes("/") ||
2274
2320
  (target.includes(".") && !target.includes(" "));
2275
2321
  // Resolved here: containment is this process's filesystem question.
@@ -2335,7 +2381,7 @@ exports.mcp = new commander_1.Command("mcp")
2335
2381
  const { proj, root } = resolveRegisteredProject();
2336
2382
  if (!proj)
2337
2383
  return err("Project not added to gmax yet.");
2338
- (0, embedding_status_1.assertEmbeddingSearchCompatible)([proj], (0, index_config_1.readGlobalConfig)());
2384
+ (0, embedding_status_1.assertEmbeddingSearchCompatible)([proj], storeConfig());
2339
2385
  const searchOptions = { rerank: process.env.GMAX_RERANK === "1" };
2340
2386
  const response = yield searchStore(Object.assign({ projectRoot: root, query: topic, limit, filters: {}, pathPrefix: root }, searchOptions));
2341
2387
  if (response.data.length === 0)
@@ -2405,7 +2451,11 @@ exports.mcp = new commander_1.Command("mcp")
2405
2451
  return err("LLM server not available. Run `gmax llm on && gmax llm start`.");
2406
2452
  }
2407
2453
  const { investigate } = yield Promise.resolve().then(() => __importStar(require("../lib/llm/investigate")));
2408
- const inv = yield investigate({ question, projectRoot, maxRounds });
2454
+ const inv = yield investigate({
2455
+ question,
2456
+ projectRoot: currentRoot(),
2457
+ maxRounds,
2458
+ });
2409
2459
  return ok(inv.answer);
2410
2460
  }
2411
2461
  catch (e) {
@@ -2429,12 +2479,15 @@ exports.mcp = new commander_1.Command("mcp")
2429
2479
  return err("LLM server not available. Run `gmax llm on && gmax llm start`.");
2430
2480
  }
2431
2481
  const { reviewCommit } = yield Promise.resolve().then(() => __importStar(require("../lib/llm/review")));
2432
- const rev = yield reviewCommit({ commitRef, projectRoot });
2482
+ const rev = yield reviewCommit({
2483
+ commitRef,
2484
+ projectRoot: currentRoot(),
2485
+ });
2433
2486
  if (rev.clean) {
2434
2487
  return ok(`Clean commit (${rev.commit}) — no issues found in ${rev.duration}s.`);
2435
2488
  }
2436
2489
  const { readReport } = yield Promise.resolve().then(() => __importStar(require("../lib/llm/report")));
2437
- const report = readReport(projectRoot);
2490
+ const report = readReport(currentRoot());
2438
2491
  const entry = report === null || report === void 0 ? void 0 : report.reviews.find((r) => r.commit === rev.commit);
2439
2492
  return ok(JSON.stringify({
2440
2493
  commit: rev.commit,
@@ -2459,26 +2512,26 @@ exports.mcp = new commander_1.Command("mcp")
2459
2512
  // returns the location.
2460
2513
  const { extractDiff, extractSymbols, fileChurn } = yield Promise.resolve().then(() => __importStar(require("../lib/llm/diff")));
2461
2514
  const { computeRiskTable, formatRiskTable } = yield Promise.resolve().then(() => __importStar(require("../lib/review/risk")));
2462
- const diff = extractDiff(commitRef, projectRoot);
2515
+ const diff = extractDiff(commitRef, currentRoot());
2463
2516
  const symbols = diff ? extractSymbols(diff) : [];
2464
2517
  if (symbols.length === 0) {
2465
2518
  return ok("(no changed symbols in this diff)");
2466
2519
  }
2467
- const scope = projectScope(projectRoot);
2520
+ const scope = projectScope(currentRoot());
2468
2521
  const facts = yield graphVerb("graph.risk", {
2469
- projectRoot,
2522
+ projectRoot: currentRoot(),
2470
2523
  scope,
2471
2524
  payload: { symbols },
2472
2525
  render: (resp) => { var _a; return (_a = resp.facts) !== null && _a !== void 0 ? _a : []; },
2473
2526
  inProcess: () => localStore((deps) => (0, graph_handler_1.runGraphRisk)(deps.vectorDb, {
2474
2527
  symbols,
2475
- queryRoot: projectRoot,
2528
+ queryRoot: currentRoot(),
2476
2529
  scope,
2477
2530
  })),
2478
2531
  });
2479
- const prefix = projectRoot.endsWith("/")
2480
- ? projectRoot
2481
- : `${projectRoot}/`;
2532
+ const prefix = currentRoot().endsWith("/")
2533
+ ? currentRoot()
2534
+ : `${currentRoot()}/`;
2482
2535
  const rows = computeRiskTable(facts.map((fact) => ({
2483
2536
  symbol: fact.symbol,
2484
2537
  file: fact.file
@@ -2489,7 +2542,7 @@ exports.mcp = new commander_1.Command("mcp")
2489
2542
  line: fact.line,
2490
2543
  callerCount: fact.callerCount,
2491
2544
  hasTests: fact.hasTests,
2492
- churn: fileChurn(fact.file, projectRoot),
2545
+ churn: fileChurn(fact.file, currentRoot()),
2493
2546
  })));
2494
2547
  return rows.length === 0
2495
2548
  ? ok("(no changed symbols in this diff)")
@@ -2504,7 +2557,7 @@ exports.mcp = new commander_1.Command("mcp")
2504
2557
  return __awaiter(this, void 0, void 0, function* () {
2505
2558
  try {
2506
2559
  const { readReport, formatReportText } = yield Promise.resolve().then(() => __importStar(require("../lib/llm/report")));
2507
- const report = readReport(projectRoot);
2560
+ const report = readReport(currentRoot());
2508
2561
  if (!report || report.reviews.length === 0) {
2509
2562
  return ok("No review findings yet.");
2510
2563
  }
@@ -2519,6 +2572,7 @@ exports.mcp = new commander_1.Command("mcp")
2519
2572
  });
2520
2573
  }
2521
2574
  // --- MCP server setup ---
2575
+ // Local-only MCP: client-owned pipes, no HTTP/SSE/TCP listener or host option.
2522
2576
  const transport = new stdio_js_1.StdioServerTransport();
2523
2577
  const server = new mcp_js_1.McpServer({
2524
2578
  name: "gmax",
@@ -2554,7 +2608,7 @@ exports.mcp = new commander_1.Command("mcp")
2554
2608
  source: "mcp",
2555
2609
  tool: name,
2556
2610
  query: mcpLogQuery(name, toolArgs),
2557
- project: projectRoot,
2611
+ project: currentRoot(),
2558
2612
  results: resultLines,
2559
2613
  ms: Date.now() - startMs,
2560
2614
  error: result.isError ? text.slice(0, 200) : undefined,
@@ -2566,21 +2620,42 @@ exports.mcp = new commander_1.Command("mcp")
2566
2620
  // shapes give us free input validation (the SDK rejects calls that violate
2567
2621
  // the schema before the handler runs); handlers stay defensively coded.
2568
2622
  function tool(name, config, handler) {
2569
- server.registerTool(name, config, (rawArgs) => __awaiter(this, void 0, void 0, function* () {
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* () {
2570
2624
  let args = (rawArgs !== null && rawArgs !== void 0 ? rawArgs : {});
2571
2625
  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.`);
2626
+ let target = projectRoot;
2627
+ try {
2628
+ if (typeof args.root === "string" && args.root.trim()) {
2629
+ const raw = args.root.trim();
2630
+ if (path.isAbsolute(raw) || raw.includes(path.sep))
2631
+ target = path.resolve(raw);
2632
+ else {
2633
+ const projects = [
2634
+ ...(0, project_registry_1.listProjects)(),
2635
+ ...(0, store_context_1.storeInventory)().flatMap((store) => store.projects),
2636
+ ].filter((project, i, all) => all.findIndex((p) => p.root === project.root) === i);
2637
+ const selected = resolveMcpProject(raw, projectRoot, projects);
2638
+ if (!selected)
2639
+ return err(`Unknown or ambiguous registered project: ${raw}. Use list_projects or an exact root.`);
2640
+ target = selected.root;
2641
+ }
2642
+ }
2643
+ return yield (0, store_context_1.withStoreContext)(target, () => __awaiter(this, void 0, void 0, function* () {
2644
+ if (args.root !== undefined) {
2645
+ const selected = resolveMcpProject(args.root, currentRoot(), storeProjects());
2646
+ if (!selected)
2647
+ return err(`Unknown registered project: ${args.root}. Use list_projects to select a project name or exact root.`);
2648
+ args = Object.assign(Object.assign({}, args), { root: selected.root });
2649
+ (0, store_context_1.currentStoreContext)().root = selected.root;
2650
+ }
2651
+ const result = yield handler(args);
2576
2652
  yield logToolCall(name, args, startMs, result);
2577
2653
  return result;
2578
- }
2579
- args = Object.assign(Object.assign({}, args), { root: selected.root });
2654
+ }));
2655
+ }
2656
+ catch (error) {
2657
+ return toolError("Tool failed", error);
2580
2658
  }
2581
- const result = yield handler(args);
2582
- yield logToolCall(name, args, startMs, result);
2583
- return result;
2584
2659
  }));
2585
2660
  }
2586
2661
  tool("semantic_search", {
@@ -71,7 +71,6 @@ exports.project = new commander_1.Command("project")
71
71
  daemon: () => (0, daemon_client_1.sendDaemonCommand)({ cmd: "rows.project", projectRoot: root }, { timeoutMs: 120000 }),
72
72
  render: (resp) => resp.overview,
73
73
  inProcess: () => (0, rows_handler_1.withLocalStore)(paths.lancedbDir, (deps) => (0, rows_handler_1.runProject)(deps, root)),
74
- fallbackOnUnknownVerb: true,
75
74
  });
76
75
  if (overview.chunks === 0) {
77
76
  console.log(`No indexed data found for ${root}. Run: gmax index --path ${root}`);
@@ -112,7 +112,6 @@ exports.similar = new commander_1.Command("similar")
112
112
  limit,
113
113
  threshold,
114
114
  })),
115
- fallbackOnUnknownVerb: true,
116
115
  });
117
116
  if (result.status === "not-found") {
118
117
  console.log((isFile
@@ -158,7 +158,6 @@ function lookupSkeleton(projectRoot, lancedbDir, req) {
158
158
  skeleton: ((_b = resp.skeleton) !== null && _b !== void 0 ? _b : null),
159
159
  }); },
160
160
  inProcess: () => (0, rows_handler_1.withLocalStore)(lancedbDir, (deps) => (0, rows_handler_1.runSkeleton)(deps, Object.assign({ projectRoot }, req))),
161
- fallbackOnUnknownVerb: true,
162
161
  });
163
162
  });
164
163
  }