grepmax 0.1.0 → 0.2.1

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 (61) hide show
  1. package/LICENSE +1 -1
  2. package/NOTICE +2 -2
  3. package/README.md +72 -72
  4. package/dist/commands/claude-code.js +6 -6
  5. package/dist/commands/codex.js +17 -17
  6. package/dist/commands/doctor.js +6 -5
  7. package/dist/commands/droid.js +22 -22
  8. package/dist/commands/index.js +1 -1
  9. package/dist/commands/list.js +82 -19
  10. package/dist/commands/mcp.js +177 -158
  11. package/dist/commands/opencode.js +26 -26
  12. package/dist/commands/search.js +23 -13
  13. package/dist/commands/serve.js +30 -30
  14. package/dist/commands/setup.js +51 -40
  15. package/dist/commands/skeleton.js +19 -13
  16. package/dist/commands/symbols.js +40 -2
  17. package/dist/commands/verify.js +1 -1
  18. package/dist/commands/watch.js +206 -0
  19. package/dist/config.js +37 -7
  20. package/dist/eval.js +14 -14
  21. package/dist/index.js +11 -7
  22. package/dist/lib/core/languages.js +28 -0
  23. package/dist/lib/index/chunker.js +6 -3
  24. package/dist/lib/index/grammar-loader.js +2 -2
  25. package/dist/lib/index/ignore-patterns.js +1 -1
  26. package/dist/lib/index/index-config.js +50 -10
  27. package/dist/lib/index/sync-helpers.js +1 -1
  28. package/dist/lib/index/syncer.js +67 -45
  29. package/dist/lib/index/walker.js +3 -3
  30. package/dist/lib/index/watcher.js +4 -4
  31. package/dist/lib/output/formatter.js +1 -1
  32. package/dist/lib/search/searcher.js +9 -9
  33. package/dist/lib/setup/model-loader.js +3 -3
  34. package/dist/lib/setup/setup-helpers.js +2 -4
  35. package/dist/lib/skeleton/body-fields.js +20 -0
  36. package/dist/lib/skeleton/retriever.js +1 -1
  37. package/dist/lib/skeleton/skeletonizer.js +8 -2
  38. package/dist/lib/skeleton/summary-formatter.js +1 -4
  39. package/dist/lib/store/meta-cache.js +28 -3
  40. package/dist/lib/store/vector-db.js +17 -9
  41. package/dist/lib/utils/formatter.js +3 -3
  42. package/dist/lib/utils/lock.js +1 -1
  43. package/dist/lib/utils/project-registry.js +83 -0
  44. package/dist/lib/utils/project-root.js +32 -57
  45. package/dist/lib/utils/watcher-registry.js +100 -0
  46. package/dist/lib/workers/colbert-math.js +2 -2
  47. package/dist/lib/workers/download-worker.js +2 -2
  48. package/dist/lib/workers/embeddings/colbert.js +2 -2
  49. package/dist/lib/workers/embeddings/granite.js +4 -4
  50. package/dist/lib/workers/embeddings/mlx-client.js +1 -1
  51. package/dist/lib/workers/orchestrator.js +8 -8
  52. package/dist/lib/workers/pool.js +1 -1
  53. package/dist/lib/workers/worker.js +4 -1
  54. package/package.json +20 -21
  55. package/plugins/{osgrep → grepmax}/.claude-plugin/plugin.json +4 -4
  56. package/plugins/grepmax/hooks/start.js +63 -0
  57. package/plugins/grepmax/hooks/stop.js +3 -0
  58. package/plugins/{osgrep/skills/osgrep → grepmax/skills/gmax}/SKILL.md +11 -11
  59. package/plugins/osgrep/hooks/start.js +0 -90
  60. package/plugins/osgrep/hooks/stop.js +0 -3
  61. /package/plugins/{osgrep → grepmax}/hooks.json +0 -0
@@ -46,27 +46,30 @@ exports.mcp = void 0;
46
46
  exports.toStringArray = toStringArray;
47
47
  exports.ok = ok;
48
48
  exports.err = err;
49
- const node_child_process_1 = require("node:child_process");
50
49
  const fs = __importStar(require("node:fs"));
51
50
  const path = __importStar(require("node:path"));
52
51
  const index_js_1 = require("@modelcontextprotocol/sdk/server/index.js");
53
52
  const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
54
53
  const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
55
54
  const commander_1 = require("commander");
55
+ const config_1 = require("../config");
56
56
  const graph_builder_1 = require("../lib/graph/graph-builder");
57
+ const index_config_1 = require("../lib/index/index-config");
58
+ const syncer_1 = require("../lib/index/syncer");
59
+ const searcher_1 = require("../lib/search/searcher");
57
60
  const retriever_1 = require("../lib/skeleton/retriever");
58
61
  const skeletonizer_1 = require("../lib/skeleton/skeletonizer");
59
62
  const vector_db_1 = require("../lib/store/vector-db");
60
63
  const filter_builder_1 = require("../lib/utils/filter-builder");
64
+ const project_registry_1 = require("../lib/utils/project-registry");
61
65
  const project_root_1 = require("../lib/utils/project-root");
62
- const server_registry_1 = require("../lib/utils/server-registry");
63
66
  // ---------------------------------------------------------------------------
64
67
  // Tool definitions
65
68
  // ---------------------------------------------------------------------------
66
69
  const TOOLS = [
67
70
  {
68
71
  name: "semantic_search",
69
- description: "Search code by meaning. Use natural language queries like 'where do we validate permissions' or 'how does the booking flow work'. Returns ranked code snippets with file paths, line numbers, and relevance scores.",
72
+ description: "Search code by meaning within a directory. Use natural language queries like 'where do we validate permissions'. Searches the current project by default. Use `root` to search a different directory's index (e.g. a parent directory).",
70
73
  inputSchema: {
71
74
  type: "object",
72
75
  properties: {
@@ -78,9 +81,13 @@ const TOOLS = [
78
81
  type: "number",
79
82
  description: "Max results to return (default 10, max 50)",
80
83
  },
84
+ root: {
85
+ type: "string",
86
+ description: "Directory to search (absolute or relative path). Defaults to the current project root. Use to search a parent or sibling directory's indexed code.",
87
+ },
81
88
  path: {
82
89
  type: "string",
83
- description: "Restrict search to files under this path prefix (e.g. 'src/auth/')",
90
+ description: "Restrict search to files under this path prefix (e.g. 'src/auth/'). Relative to the search root.",
84
91
  },
85
92
  min_score: {
86
93
  type: "number",
@@ -94,6 +101,32 @@ const TOOLS = [
94
101
  required: ["query"],
95
102
  },
96
103
  },
104
+ {
105
+ name: "search_all",
106
+ description: "Search ALL indexed code across every directory. Use when you need to find code that could be anywhere. Returns results with full absolute paths so you know which project each result is from.",
107
+ inputSchema: {
108
+ type: "object",
109
+ properties: {
110
+ query: {
111
+ type: "string",
112
+ description: "Natural language search query.",
113
+ },
114
+ limit: {
115
+ type: "number",
116
+ description: "Max results to return (default 10, max 50)",
117
+ },
118
+ min_score: {
119
+ type: "number",
120
+ description: "Minimum relevance score (0-1). Default: 0",
121
+ },
122
+ max_per_file: {
123
+ type: "number",
124
+ description: "Max results per file (default: no cap).",
125
+ },
126
+ },
127
+ required: ["query"],
128
+ },
129
+ },
97
130
  {
98
131
  name: "code_skeleton",
99
132
  description: "Show the structure of a source file — all function/class/method signatures with bodies collapsed. Useful for understanding large files without reading every line. Returns ~4x fewer tokens than the full file.",
@@ -110,7 +143,7 @@ const TOOLS = [
110
143
  },
111
144
  {
112
145
  name: "trace_calls",
113
- description: "Trace the call graph for a symbol — who calls it (callers) and what it calls (callees). Useful for understanding how functions connect across files.",
146
+ description: "Trace the call graph for a symbol — who calls it (callers) and what it calls (callees). Searches across ALL indexed code to follow calls across project boundaries.",
114
147
  inputSchema: {
115
148
  type: "object",
116
149
  properties: {
@@ -145,7 +178,7 @@ const TOOLS = [
145
178
  },
146
179
  {
147
180
  name: "index_status",
148
- description: "Check the status of the osgrep index and serve daemon. Returns file count, chunk count, embed mode, index age, and whether live watching is active.",
181
+ description: "Check the status of the gmax index. Returns indexed directories, chunk counts, embed mode, index age, and watcher status.",
149
182
  inputSchema: {
150
183
  type: "object",
151
184
  properties: {},
@@ -153,37 +186,6 @@ const TOOLS = [
153
186
  },
154
187
  ];
155
188
  // ---------------------------------------------------------------------------
156
- // Daemon lifecycle
157
- // ---------------------------------------------------------------------------
158
- let _daemonReady = null;
159
- function ensureDaemon(projectRoot) {
160
- return __awaiter(this, void 0, void 0, function* () {
161
- const existing = (0, server_registry_1.getServerForProject)(projectRoot);
162
- if (existing && (0, server_registry_1.isProcessRunning)(existing.pid)) {
163
- console.log(`[MCP] Serve daemon already running (PID: ${existing.pid}, Port: ${existing.port})`);
164
- return true;
165
- }
166
- console.log("[MCP] Starting serve daemon...");
167
- const child = (0, node_child_process_1.spawn)("osgrep", ["serve", "-b"], {
168
- cwd: projectRoot,
169
- detached: true,
170
- stdio: "ignore",
171
- });
172
- child.unref();
173
- // Poll for readiness — daemon registers in ~/.osgrep/servers.json once listening
174
- for (let i = 0; i < 30; i++) {
175
- yield new Promise((r) => setTimeout(r, 2000));
176
- const server = (0, server_registry_1.getServerForProject)(projectRoot);
177
- if (server && (0, server_registry_1.isProcessRunning)(server.pid)) {
178
- console.log(`[MCP] Daemon ready (PID: ${server.pid}, Port: ${server.port})`);
179
- return true;
180
- }
181
- }
182
- console.error("[MCP] Daemon failed to become ready within 60s");
183
- return false;
184
- });
185
- }
186
- // ---------------------------------------------------------------------------
187
189
  // Helpers
188
190
  // ---------------------------------------------------------------------------
189
191
  function toStringArray(val) {
@@ -210,12 +212,13 @@ function err(text) {
210
212
  // Command
211
213
  // ---------------------------------------------------------------------------
212
214
  exports.mcp = new commander_1.Command("mcp")
213
- .description("Start MCP server for osgrep")
215
+ .description("Start MCP server for gmax")
214
216
  .action((_optsArg, _cmd) => __awaiter(void 0, void 0, void 0, function* () {
215
217
  // --- Lifecycle ---
216
- var _a;
217
218
  let _vectorDb = null;
219
+ let _searcher = null;
218
220
  let _skeletonizer = null;
221
+ let _indexReady = false;
219
222
  const cleanup = () => __awaiter(void 0, void 0, void 0, function* () {
220
223
  if (_vectorDb) {
221
224
  try {
@@ -223,6 +226,7 @@ exports.mcp = new commander_1.Command("mcp")
223
226
  }
224
227
  catch (_a) { }
225
228
  _vectorDb = null;
229
+ _searcher = null;
226
230
  }
227
231
  });
228
232
  const exit = () => __awaiter(void 0, void 0, void 0, function* () {
@@ -246,15 +250,20 @@ exports.mcp = new commander_1.Command("mcp")
246
250
  };
247
251
  console.debug = (..._args) => { };
248
252
  // --- Project context ---
249
- const projectRoot = (_a = (0, project_root_1.findProjectRoot)(process.cwd())) !== null && _a !== void 0 ? _a : process.cwd();
253
+ const projectRoot = (0, project_root_1.findProjectRoot)(process.cwd());
250
254
  const paths = (0, project_root_1.ensureProjectPaths)(projectRoot);
251
- // Lazy resource accessors
255
+ // Propagate project root to worker processes
256
+ process.env.GMAX_PROJECT_ROOT = paths.root;
257
+ // Lazy resource accessors — all use centralized store
252
258
  function getVectorDb() {
253
- return __awaiter(this, void 0, void 0, function* () {
254
- if (!_vectorDb)
255
- _vectorDb = new vector_db_1.VectorDB(paths.lancedbDir);
256
- return _vectorDb;
257
- });
259
+ if (!_vectorDb)
260
+ _vectorDb = new vector_db_1.VectorDB(paths.lancedbDir);
261
+ return _vectorDb;
262
+ }
263
+ function getSearcher() {
264
+ if (!_searcher)
265
+ _searcher = new searcher_1.Searcher(getVectorDb());
266
+ return _searcher;
258
267
  }
259
268
  function getSkeletonizer() {
260
269
  return __awaiter(this, void 0, void 0, function* () {
@@ -265,66 +274,88 @@ exports.mcp = new commander_1.Command("mcp")
265
274
  return _skeletonizer;
266
275
  });
267
276
  }
268
- // --- Tool handlers ---
269
- function ensureDaemonRunning() {
277
+ // --- Index sync ---
278
+ function ensureIndexReady() {
270
279
  return __awaiter(this, void 0, void 0, function* () {
271
- if (_daemonReady) {
272
- const ready = yield _daemonReady;
273
- if (!ready)
274
- return false;
280
+ if (_indexReady)
281
+ return;
282
+ try {
283
+ const db = getVectorDb();
284
+ const hasIndex = yield db.hasAnyRows();
285
+ if (!hasIndex) {
286
+ console.log("[MCP] No index found, running initial sync...");
287
+ yield (0, syncer_1.initialSync)({ projectRoot });
288
+ console.log("[MCP] Initial sync complete.");
289
+ }
290
+ else {
291
+ console.log("[MCP] Index exists, ready.");
292
+ }
293
+ _indexReady = true;
294
+ }
295
+ catch (e) {
296
+ console.error("[MCP] Index sync failed:", e);
275
297
  }
276
- const server = (0, server_registry_1.getServerForProject)(projectRoot);
277
- if (server && (0, server_registry_1.isProcessRunning)(server.pid))
278
- return true;
279
- // Daemon died — restart it
280
- console.log("[MCP] Daemon not running, restarting...");
281
- _daemonReady = ensureDaemon(projectRoot);
282
- return _daemonReady;
283
298
  });
284
299
  }
285
- function handleSemanticSearch(args) {
286
- return __awaiter(this, void 0, void 0, function* () {
300
+ // --- Tool handlers ---
301
+ function handleSemanticSearch(args_1) {
302
+ return __awaiter(this, arguments, void 0, function* (args, searchAll = false) {
287
303
  const query = String(args.query || "");
288
304
  if (!query)
289
305
  return err("Missing required parameter: query");
290
306
  const limit = Math.min(Math.max(Number(args.limit) || 10, 1), 50);
291
- const searchPath = typeof args.path === "string" ? args.path : undefined;
292
- if (!(yield ensureDaemonRunning())) {
293
- return err("Search daemon failed to start. Run 'osgrep serve -b' manually.");
294
- }
295
- const server = (0, server_registry_1.getServerForProject)(projectRoot);
296
- if (!server || !(0, server_registry_1.isProcessRunning)(server.pid)) {
297
- return err("Search daemon not running. Run 'osgrep serve -b' manually.");
298
- }
307
+ yield ensureIndexReady();
299
308
  try {
300
- const response = yield fetch(`http://localhost:${server.port}/search`, {
301
- method: "POST",
302
- headers: { "Content-Type": "application/json" },
303
- body: JSON.stringify({ query, limit, path: searchPath }),
304
- signal: AbortSignal.timeout(30000),
305
- });
306
- if (!response.ok) {
307
- const body = yield response.text();
308
- return err(`Search failed (${response.status}): ${body}`);
309
+ const searcher = getSearcher();
310
+ // Determine path prefix for scoping
311
+ let pathPrefix;
312
+ if (!searchAll) {
313
+ // Resolve search root — default to project root
314
+ const searchRoot = typeof args.root === "string"
315
+ ? path.resolve(args.root)
316
+ : path.resolve(projectRoot);
317
+ pathPrefix = searchRoot.endsWith("/")
318
+ ? searchRoot
319
+ : `${searchRoot}/`;
320
+ // If a sub-path is specified, append it
321
+ if (typeof args.path === "string") {
322
+ pathPrefix = path.join(searchRoot, args.path);
323
+ if (!pathPrefix.endsWith("/"))
324
+ pathPrefix += "/";
325
+ }
309
326
  }
310
- const { results } = (yield response.json());
311
- if (!results || results.length === 0) {
327
+ const result = yield searcher.search(query, limit, { rerank: true }, undefined, pathPrefix);
328
+ if (!result.data || result.data.length === 0) {
312
329
  return ok("No matches found.");
313
330
  }
314
331
  const minScore = typeof args.min_score === "number" ? args.min_score : 0;
315
332
  const maxPerFile = typeof args.max_per_file === "number" ? args.max_per_file : 0;
316
- let compact = results.map((r) => {
317
- var _a, _b, _c, _d, _e, _f, _g, _h, _j;
318
- return ({
319
- path: (_c = (_b = (_a = r.metadata) === null || _a === void 0 ? void 0 : _a.path) !== null && _b !== void 0 ? _b : r.path) !== null && _c !== void 0 ? _c : "",
320
- startLine: (_e = (_d = r.generated_metadata) === null || _d === void 0 ? void 0 : _d.start_line) !== null && _e !== void 0 ? _e : 0,
321
- endLine: (_g = (_f = r.generated_metadata) === null || _f === void 0 ? void 0 : _f.end_line) !== null && _g !== void 0 ? _g : 0,
333
+ const MAX_SNIPPET_LINES = 8;
334
+ let compact = result.data.map((r) => {
335
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
336
+ const startLine = (_c = (_a = r.startLine) !== null && _a !== void 0 ? _a : (_b = r.generated_metadata) === null || _b === void 0 ? void 0 : _b.start_line) !== null && _c !== void 0 ? _c : 0;
337
+ const raw = typeof r.content === "string"
338
+ ? r.content
339
+ : typeof r.text === "string"
340
+ ? r.text
341
+ : "";
342
+ // Add line numbers and cap at MAX_SNIPPET_LINES
343
+ const lines = raw.split("\n");
344
+ const capped = lines.slice(0, MAX_SNIPPET_LINES);
345
+ const numbered = capped.map((line, i) => `${startLine + i + 1}│${line}`);
346
+ const snippet = lines.length > MAX_SNIPPET_LINES
347
+ ? `${numbered.join("\n")}\n… (+${lines.length - MAX_SNIPPET_LINES} more lines)`
348
+ : numbered.join("\n");
349
+ return {
350
+ path: (_f = (_d = r.path) !== null && _d !== void 0 ? _d : (_e = r.metadata) === null || _e === void 0 ? void 0 : _e.path) !== null && _f !== void 0 ? _f : "",
351
+ startLine,
352
+ endLine: (_j = (_g = r.endLine) !== null && _g !== void 0 ? _g : (_h = r.generated_metadata) === null || _h === void 0 ? void 0 : _h.end_line) !== null && _j !== void 0 ? _j : 0,
322
353
  score: typeof r.score === "number" ? +r.score.toFixed(3) : 0,
323
- role: (_h = r.role) !== null && _h !== void 0 ? _h : "IMPLEMENTATION",
324
- confidence: (_j = r.confidence) !== null && _j !== void 0 ? _j : "Unknown",
325
- definedSymbols: toStringArray(r.defined_symbols).slice(0, 5),
326
- snippet: typeof r.text === "string" ? r.text : "",
327
- });
354
+ role: (_k = r.role) !== null && _k !== void 0 ? _k : "IMPLEMENTATION",
355
+ confidence: (_l = r.confidence) !== null && _l !== void 0 ? _l : "Unknown",
356
+ definedSymbols: toStringArray((_m = r.definedSymbols) !== null && _m !== void 0 ? _m : r.defined_symbols).slice(0, 5),
357
+ snippet,
358
+ };
328
359
  });
329
360
  if (minScore > 0) {
330
361
  compact = compact.filter((r) => r.score >= minScore);
@@ -339,11 +370,11 @@ exports.mcp = new commander_1.Command("mcp")
339
370
  return true;
340
371
  });
341
372
  }
342
- return ok(JSON.stringify(compact, null, 2));
373
+ return ok(JSON.stringify(compact));
343
374
  }
344
375
  catch (e) {
345
376
  const msg = e instanceof Error ? e.message : String(e);
346
- return err(`Search request failed: ${msg}`);
377
+ return err(`Search failed: ${msg}`);
347
378
  }
348
379
  });
349
380
  }
@@ -353,21 +384,16 @@ exports.mcp = new commander_1.Command("mcp")
353
384
  if (!target)
354
385
  return err("Missing required parameter: target");
355
386
  const absPath = path.resolve(projectRoot, target);
356
- const relPath = path.relative(projectRoot, absPath);
357
- // Security: ensure path is within project
358
- if (relPath.startsWith("..") || path.isAbsolute(relPath)) {
359
- return err("Path must be within the project root.");
360
- }
361
387
  if (!fs.existsSync(absPath)) {
362
388
  return err(`File not found: ${target}`);
363
389
  }
364
- // Try cached skeleton first
390
+ // Try cached skeleton first (stored with absolute path)
365
391
  try {
366
- const db = yield getVectorDb();
367
- const cached = yield (0, retriever_1.getStoredSkeleton)(db, relPath);
392
+ const db = getVectorDb();
393
+ const cached = yield (0, retriever_1.getStoredSkeleton)(db, absPath);
368
394
  if (cached) {
369
395
  const tokens = Math.ceil(cached.length / 4);
370
- return ok(`// ${relPath} (~${tokens} tokens)\n\n${cached}`);
396
+ return ok(`// ${target} (~${tokens} tokens)\n\n${cached}`);
371
397
  }
372
398
  }
373
399
  catch (_a) {
@@ -377,11 +403,11 @@ exports.mcp = new commander_1.Command("mcp")
377
403
  try {
378
404
  const content = fs.readFileSync(absPath, "utf-8");
379
405
  const skel = yield getSkeletonizer();
380
- const result = yield skel.skeletonizeFile(relPath, content);
406
+ const result = yield skel.skeletonizeFile(absPath, content);
381
407
  if (!result.success && result.error) {
382
408
  return err(`Skeleton generation failed: ${result.error}`);
383
409
  }
384
- return ok(`// ${relPath} (~${result.tokenEstimate} tokens)\n\n${result.skeleton}`);
410
+ return ok(`// ${target} (~${result.tokenEstimate} tokens)\n\n${result.skeleton}`);
385
411
  }
386
412
  catch (e) {
387
413
  const msg = e instanceof Error ? e.message : String(e);
@@ -395,38 +421,31 @@ exports.mcp = new commander_1.Command("mcp")
395
421
  if (!symbol)
396
422
  return err("Missing required parameter: symbol");
397
423
  try {
398
- const db = yield getVectorDb();
424
+ const db = getVectorDb();
399
425
  const builder = new graph_builder_1.GraphBuilder(db);
400
426
  const graph = yield builder.buildGraph(symbol);
401
427
  if (!graph.center) {
402
428
  return ok(`Symbol '${symbol}' not found in the index.`);
403
429
  }
404
430
  const lines = [];
431
+ // Center
432
+ lines.push(`${graph.center.symbol} [${graph.center.role}] ${graph.center.file}:${graph.center.line + 1}`);
405
433
  // Callers
406
434
  if (graph.callers.length > 0) {
407
- lines.push("Callers (who calls this?):");
435
+ lines.push("Callers:");
408
436
  for (const caller of graph.callers) {
409
- lines.push(` <- ${caller.symbol} (${caller.file}:${caller.line})`);
437
+ lines.push(` <- ${caller.symbol} ${caller.file}:${caller.line + 1}`);
410
438
  }
411
439
  }
412
440
  else {
413
- lines.push("No known callers.");
441
+ lines.push("Callers: none");
414
442
  }
415
- lines.push("");
416
- // Center
417
- lines.push(`${graph.center.symbol}`);
418
- lines.push(` Defined in ${graph.center.file}:${graph.center.line}`);
419
- lines.push(` Role: ${graph.center.role}`);
420
- lines.push("");
421
443
  // Callees
422
444
  if (graph.callees.length > 0) {
423
- lines.push("Callees (what does this call?):");
424
- for (const callee of graph.callees) {
425
- lines.push(` -> ${callee}`);
426
- }
445
+ lines.push(`Calls: ${graph.callees.join(", ")}`);
427
446
  }
428
447
  else {
429
- lines.push("No known callees.");
448
+ lines.push("Calls: none");
430
449
  }
431
450
  return ok(lines.join("\n"));
432
451
  }
@@ -442,7 +461,7 @@ exports.mcp = new commander_1.Command("mcp")
442
461
  const limit = Math.min(Math.max(Number(args.limit) || 20, 1), 100);
443
462
  const pathPrefix = typeof args.path === "string" ? args.path : undefined;
444
463
  try {
445
- const db = yield getVectorDb();
464
+ const db = getVectorDb();
446
465
  const table = yield db.ensureTable();
447
466
  let query = table
448
467
  .query()
@@ -450,7 +469,11 @@ exports.mcp = new commander_1.Command("mcp")
450
469
  .where("array_length(defined_symbols) > 0")
451
470
  .limit(pattern ? 10000 : Math.max(limit * 50, 2000));
452
471
  if (pathPrefix) {
453
- query = query.where(`path LIKE '${(0, filter_builder_1.escapeSqlString)((0, filter_builder_1.normalizePath)(pathPrefix))}%'`);
472
+ // Support both absolute and relative path prefixes
473
+ const absPrefix = path.isAbsolute(pathPrefix)
474
+ ? pathPrefix
475
+ : path.resolve(projectRoot, pathPrefix);
476
+ query = query.where(`path LIKE '${(0, filter_builder_1.escapeSqlString)((0, filter_builder_1.normalizePath)(absPrefix))}%'`);
454
477
  }
455
478
  const rows = yield query.toArray();
456
479
  const map = new Map();
@@ -467,7 +490,12 @@ exports.mcp = new commander_1.Command("mcp")
467
490
  existing.count += 1;
468
491
  }
469
492
  else {
470
- map.set(sym, { symbol: sym, count: 1, path: rowPath, line: Math.max(1, line + 1) });
493
+ map.set(sym, {
494
+ symbol: sym,
495
+ count: 1,
496
+ path: rowPath,
497
+ line: Math.max(1, line + 1),
498
+ });
471
499
  }
472
500
  }
473
501
  }
@@ -479,9 +507,10 @@ exports.mcp = new commander_1.Command("mcp")
479
507
  })
480
508
  .slice(0, limit);
481
509
  if (entries.length === 0) {
482
- return ok("No symbols found. Run 'osgrep index' to build the index.");
510
+ return ok("No symbols found. Run 'gmax index' to build the index.");
483
511
  }
484
- return ok(JSON.stringify(entries, null, 2));
512
+ const lines = entries.map((e) => `${e.symbol}\t${e.path}:${e.line}`);
513
+ return ok(lines.join("\n"));
485
514
  }
486
515
  catch (e) {
487
516
  const msg = e instanceof Error ? e.message : String(e);
@@ -491,46 +520,35 @@ exports.mcp = new commander_1.Command("mcp")
491
520
  }
492
521
  function handleIndexStatus() {
493
522
  return __awaiter(this, void 0, void 0, function* () {
494
- var _a, _b, _c, _d, _e;
495
- yield ensureDaemonRunning();
496
- const server = (0, server_registry_1.getServerForProject)(projectRoot);
497
- if (!server || !(0, server_registry_1.isProcessRunning)(server.pid)) {
498
- // Fall back to config file
499
- const configPath = path.join(projectRoot, ".osgrep", "config.json");
500
- try {
501
- const config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
502
- return ok(JSON.stringify({
503
- daemon: "stopped",
504
- embedMode: (_a = config.embedMode) !== null && _a !== void 0 ? _a : "unknown",
505
- model: (_c = (_b = config.embedModel) !== null && _b !== void 0 ? _b : config.mlxModel) !== null && _c !== void 0 ? _c : null,
506
- vectorDim: (_d = config.vectorDim) !== null && _d !== void 0 ? _d : null,
507
- indexedAt: (_e = config.indexedAt) !== null && _e !== void 0 ? _e : null,
508
- }, null, 2));
509
- }
510
- catch (_f) {
511
- return ok(JSON.stringify({ daemon: "stopped", indexed: false }));
512
- }
513
- }
523
+ var _a, _b, _c;
514
524
  try {
515
- const response = yield fetch(`http://localhost:${server.port}/stats`, {
516
- signal: AbortSignal.timeout(5000),
517
- });
518
- if (!response.ok) {
519
- return err(`Stats request failed (${response.status})`);
520
- }
521
- const stats = yield response.json();
522
- return ok(JSON.stringify(Object.assign({ daemon: "running", pid: server.pid, port: server.port }, stats), null, 2));
525
+ const config = (0, index_config_1.readIndexConfig)(config_1.PATHS.configPath);
526
+ const projects = (0, project_registry_1.listProjects)();
527
+ const db = getVectorDb();
528
+ const stats = yield db.getStats();
529
+ const fileCount = yield db.getDistinctFileCount();
530
+ const lines = [
531
+ `Index: ~/.gmax/lancedb (${stats.chunks} chunks, ${fileCount} files)`,
532
+ `Model: ${(_a = config === null || config === void 0 ? void 0 : config.embedModel) !== null && _a !== void 0 ? _a : "unknown"} (${(_b = config === null || config === void 0 ? void 0 : config.vectorDim) !== null && _b !== void 0 ? _b : "?"}d, ${(_c = config === null || config === void 0 ? void 0 : config.embedMode) !== null && _c !== void 0 ? _c : "unknown"})`,
533
+ (config === null || config === void 0 ? void 0 : config.indexedAt)
534
+ ? `Last indexed: ${config.indexedAt}`
535
+ : "",
536
+ "",
537
+ "Indexed directories:",
538
+ ...projects.map((p) => { var _a; return ` ${p.name}\t${p.root}\t${(_a = p.lastIndexed) !== null && _a !== void 0 ? _a : "unknown"}`; }),
539
+ ].filter(Boolean);
540
+ return ok(lines.join("\n"));
523
541
  }
524
542
  catch (e) {
525
543
  const msg = e instanceof Error ? e.message : String(e);
526
- return err(`Failed to get status: ${msg}`);
544
+ return err(`Status check failed: ${msg}`);
527
545
  }
528
546
  });
529
547
  }
530
548
  // --- MCP server setup ---
531
549
  const transport = new stdio_js_1.StdioServerTransport();
532
550
  const server = new index_js_1.Server({
533
- name: "osgrep",
551
+ name: "gmax",
534
552
  version: JSON.parse(fs.readFileSync(path.join(__dirname, "../../package.json"), {
535
553
  encoding: "utf-8",
536
554
  })).version,
@@ -547,7 +565,9 @@ exports.mcp = new commander_1.Command("mcp")
547
565
  const toolArgs = (args !== null && args !== void 0 ? args : {});
548
566
  switch (name) {
549
567
  case "semantic_search":
550
- return handleSemanticSearch(toolArgs);
568
+ return handleSemanticSearch(toolArgs, false);
569
+ case "search_all":
570
+ return handleSemanticSearch(toolArgs, true);
551
571
  case "code_skeleton":
552
572
  return handleCodeSkeleton(toolArgs);
553
573
  case "trace_calls":
@@ -561,7 +581,6 @@ exports.mcp = new commander_1.Command("mcp")
561
581
  }
562
582
  }));
563
583
  yield server.connect(transport);
564
- // Ensure the serve daemon is running (handles indexing, GPU, live reindex).
565
- // The MCP server owns daemon lifecycle — the SessionStart hook is read-only.
566
- _daemonReady = ensureDaemon(projectRoot);
584
+ // Kick off index readiness check in background
585
+ ensureIndexReady();
567
586
  }));