grepmax 0.1.0 → 0.2.0

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 +161 -142
  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
@@ -44,14 +44,19 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
44
44
  Object.defineProperty(exports, "__esModule", { value: true });
45
45
  exports.list = void 0;
46
46
  const fs = __importStar(require("node:fs"));
47
+ const os = __importStar(require("node:os"));
47
48
  const path = __importStar(require("node:path"));
48
49
  const commander_1 = require("commander");
50
+ const index_config_1 = require("../lib/index/index-config");
49
51
  const exit_1 = require("../lib/utils/exit");
52
+ const project_registry_1 = require("../lib/utils/project-registry");
50
53
  const project_root_1 = require("../lib/utils/project-root");
51
54
  const style = {
52
55
  bold: (s) => `\x1b[1m${s}\x1b[22m`,
53
56
  dim: (s) => `\x1b[2m${s}\x1b[22m`,
54
57
  green: (s) => `\x1b[32m${s}\x1b[39m`,
58
+ yellow: (s) => `\x1b[33m${s}\x1b[39m`,
59
+ red: (s) => `\x1b[31m${s}\x1b[39m`,
55
60
  };
56
61
  function formatSize(bytes) {
57
62
  if (bytes < 1024)
@@ -95,26 +100,84 @@ function getDirectorySize(dirPath) {
95
100
  catch (_a) { }
96
101
  return totalSize;
97
102
  }
103
+ function shortenPath(p) {
104
+ const home = os.homedir();
105
+ if (p.startsWith(home))
106
+ return `~${p.slice(home.length)}`;
107
+ return p;
108
+ }
109
+ function pad(s, len) {
110
+ return s.length >= len ? s : s + " ".repeat(len - s.length);
111
+ }
98
112
  exports.list = new commander_1.Command("list")
99
- .description("Show the current project's .osgrep contents")
100
- .action(() => __awaiter(void 0, void 0, void 0, function* () {
101
- var _a;
102
- const projectRoot = (_a = (0, project_root_1.findProjectRoot)(process.cwd())) !== null && _a !== void 0 ? _a : process.cwd();
103
- const paths = (0, project_root_1.ensureProjectPaths)(projectRoot);
104
- const entries = [
105
- { label: "LanceDB", dir: paths.lancedbDir },
106
- { label: "Cache", dir: paths.cacheDir },
107
- ];
108
- console.log(`\n${style.bold("Project")}: ${style.green(projectRoot)}`);
109
- console.log(`${style.dim("Data directory")}: ${paths.osgrepDir}\n`);
110
- for (const entry of entries) {
111
- if (!fs.existsSync(entry.dir)) {
112
- console.log(`${entry.label}: ${style.dim("not created yet")}`);
113
- continue;
114
- }
115
- const stats = fs.statSync(entry.dir);
116
- const size = getDirectorySize(entry.dir);
117
- console.log(`${entry.label}: ${style.green(formatSize(size))} ${style.dim(`(updated ${formatDate(stats.mtime)})`)}`);
113
+ .description("Show indexed projects")
114
+ .option("--all", "Show all known projects across the system")
115
+ .action((options) => __awaiter(void 0, void 0, void 0, function* () {
116
+ if (options.all) {
117
+ yield showAllProjects();
118
+ }
119
+ else {
120
+ yield showCurrentProject();
118
121
  }
119
122
  yield (0, exit_1.gracefulExit)();
120
123
  }));
124
+ function showCurrentProject() {
125
+ return __awaiter(this, void 0, void 0, function* () {
126
+ var _a, _b, _c, _d;
127
+ const projectRoot = (_a = (0, project_root_1.findProjectRoot)(process.cwd())) !== null && _a !== void 0 ? _a : process.cwd();
128
+ const paths = (0, project_root_1.ensureProjectPaths)(projectRoot);
129
+ const entries = [
130
+ { label: "LanceDB", dir: paths.lancedbDir },
131
+ { label: "Cache", dir: paths.cacheDir },
132
+ ];
133
+ console.log(`\n${style.bold("Project")}: ${style.green(projectRoot)}`);
134
+ console.log(`${style.dim("Data directory")}: ${paths.dataDir}\n`);
135
+ const config = (0, index_config_1.readIndexConfig)(paths.configPath);
136
+ if (config) {
137
+ console.log(`${style.dim("Model")}: ${(_b = config.modelTier) !== null && _b !== void 0 ? _b : "small"} (${(_c = config.vectorDim) !== null && _c !== void 0 ? _c : 384}d)`);
138
+ console.log(`${style.dim("Mode")}: ${(_d = config.embedMode) !== null && _d !== void 0 ? _d : "cpu"}`);
139
+ if (config.indexedAt) {
140
+ console.log(`${style.dim("Indexed")}: ${formatDate(new Date(config.indexedAt))}`);
141
+ }
142
+ console.log();
143
+ }
144
+ for (const entry of entries) {
145
+ if (!fs.existsSync(entry.dir)) {
146
+ console.log(`${entry.label}: ${style.dim("not created yet")}`);
147
+ continue;
148
+ }
149
+ const stats = fs.statSync(entry.dir);
150
+ const size = getDirectorySize(entry.dir);
151
+ console.log(`${entry.label}: ${style.green(formatSize(size))} ${style.dim(`(updated ${formatDate(stats.mtime)})`)}`);
152
+ }
153
+ });
154
+ }
155
+ function showAllProjects() {
156
+ return __awaiter(this, void 0, void 0, function* () {
157
+ const projects = (0, project_registry_1.listProjects)();
158
+ const globalConfig = (0, index_config_1.readGlobalConfig)();
159
+ if (projects.length === 0) {
160
+ console.log("\nNo projects registered yet. Index a project to see it here.");
161
+ return;
162
+ }
163
+ console.log();
164
+ // Column widths
165
+ const nameWidth = Math.max(8, ...projects.map((p) => p.name.length));
166
+ const pathWidth = Math.max(12, ...projects.map((p) => shortenPath(p.root).length));
167
+ // Header
168
+ console.log(`${style.bold(pad("Project", nameWidth))} ${style.bold(pad("Path", pathWidth))} ${style.bold("Dims")} ${style.bold("Status")}`);
169
+ for (const project of projects) {
170
+ const dimMatch = project.vectorDim === globalConfig.vectorDim;
171
+ const dimsStr = `${project.vectorDim}d`;
172
+ const status = dimMatch
173
+ ? style.green("ok")
174
+ : style.yellow("reindex needed");
175
+ console.log(`${pad(project.name, nameWidth)} ${style.dim(pad(shortenPath(project.root), pathWidth))} ${pad(dimsStr, 4)} ${status}`);
176
+ }
177
+ console.log(`\n${style.dim("Global config")}: ${globalConfig.modelTier} (${globalConfig.vectorDim}d), ${globalConfig.embedMode}`);
178
+ const needsReindex = projects.filter((p) => p.vectorDim !== globalConfig.vectorDim);
179
+ if (needsReindex.length > 0) {
180
+ console.log(style.yellow(`\n${needsReindex.length} project(s) need reindexing. Search will auto-reindex on first use.`));
181
+ }
182
+ });
183
+ }
@@ -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,65 +274,77 @@ 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;
333
+ let compact = result.data.map((r) => {
334
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
318
335
  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,
336
+ path: (_c = (_a = r.path) !== null && _a !== void 0 ? _a : (_b = r.metadata) === null || _b === void 0 ? void 0 : _b.path) !== null && _c !== void 0 ? _c : "",
337
+ startLine: (_f = (_d = r.startLine) !== null && _d !== void 0 ? _d : (_e = r.generated_metadata) === null || _e === void 0 ? void 0 : _e.start_line) !== null && _f !== void 0 ? _f : 0,
338
+ 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
339
  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 : "",
340
+ role: (_k = r.role) !== null && _k !== void 0 ? _k : "IMPLEMENTATION",
341
+ confidence: (_l = r.confidence) !== null && _l !== void 0 ? _l : "Unknown",
342
+ definedSymbols: toStringArray((_m = r.definedSymbols) !== null && _m !== void 0 ? _m : r.defined_symbols).slice(0, 5),
343
+ snippet: typeof r.content === "string"
344
+ ? r.content
345
+ : typeof r.text === "string"
346
+ ? r.text
347
+ : "",
327
348
  });
328
349
  });
329
350
  if (minScore > 0) {
@@ -339,11 +360,11 @@ exports.mcp = new commander_1.Command("mcp")
339
360
  return true;
340
361
  });
341
362
  }
342
- return ok(JSON.stringify(compact, null, 2));
363
+ return ok(JSON.stringify(compact));
343
364
  }
344
365
  catch (e) {
345
366
  const msg = e instanceof Error ? e.message : String(e);
346
- return err(`Search request failed: ${msg}`);
367
+ return err(`Search failed: ${msg}`);
347
368
  }
348
369
  });
349
370
  }
@@ -353,21 +374,16 @@ exports.mcp = new commander_1.Command("mcp")
353
374
  if (!target)
354
375
  return err("Missing required parameter: target");
355
376
  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
377
  if (!fs.existsSync(absPath)) {
362
378
  return err(`File not found: ${target}`);
363
379
  }
364
- // Try cached skeleton first
380
+ // Try cached skeleton first (stored with absolute path)
365
381
  try {
366
- const db = yield getVectorDb();
367
- const cached = yield (0, retriever_1.getStoredSkeleton)(db, relPath);
382
+ const db = getVectorDb();
383
+ const cached = yield (0, retriever_1.getStoredSkeleton)(db, absPath);
368
384
  if (cached) {
369
385
  const tokens = Math.ceil(cached.length / 4);
370
- return ok(`// ${relPath} (~${tokens} tokens)\n\n${cached}`);
386
+ return ok(`// ${target} (~${tokens} tokens)\n\n${cached}`);
371
387
  }
372
388
  }
373
389
  catch (_a) {
@@ -377,11 +393,11 @@ exports.mcp = new commander_1.Command("mcp")
377
393
  try {
378
394
  const content = fs.readFileSync(absPath, "utf-8");
379
395
  const skel = yield getSkeletonizer();
380
- const result = yield skel.skeletonizeFile(relPath, content);
396
+ const result = yield skel.skeletonizeFile(absPath, content);
381
397
  if (!result.success && result.error) {
382
398
  return err(`Skeleton generation failed: ${result.error}`);
383
399
  }
384
- return ok(`// ${relPath} (~${result.tokenEstimate} tokens)\n\n${result.skeleton}`);
400
+ return ok(`// ${target} (~${result.tokenEstimate} tokens)\n\n${result.skeleton}`);
385
401
  }
386
402
  catch (e) {
387
403
  const msg = e instanceof Error ? e.message : String(e);
@@ -395,7 +411,7 @@ exports.mcp = new commander_1.Command("mcp")
395
411
  if (!symbol)
396
412
  return err("Missing required parameter: symbol");
397
413
  try {
398
- const db = yield getVectorDb();
414
+ const db = getVectorDb();
399
415
  const builder = new graph_builder_1.GraphBuilder(db);
400
416
  const graph = yield builder.buildGraph(symbol);
401
417
  if (!graph.center) {
@@ -442,7 +458,7 @@ exports.mcp = new commander_1.Command("mcp")
442
458
  const limit = Math.min(Math.max(Number(args.limit) || 20, 1), 100);
443
459
  const pathPrefix = typeof args.path === "string" ? args.path : undefined;
444
460
  try {
445
- const db = yield getVectorDb();
461
+ const db = getVectorDb();
446
462
  const table = yield db.ensureTable();
447
463
  let query = table
448
464
  .query()
@@ -450,7 +466,11 @@ exports.mcp = new commander_1.Command("mcp")
450
466
  .where("array_length(defined_symbols) > 0")
451
467
  .limit(pattern ? 10000 : Math.max(limit * 50, 2000));
452
468
  if (pathPrefix) {
453
- query = query.where(`path LIKE '${(0, filter_builder_1.escapeSqlString)((0, filter_builder_1.normalizePath)(pathPrefix))}%'`);
469
+ // Support both absolute and relative path prefixes
470
+ const absPrefix = path.isAbsolute(pathPrefix)
471
+ ? pathPrefix
472
+ : path.resolve(projectRoot, pathPrefix);
473
+ query = query.where(`path LIKE '${(0, filter_builder_1.escapeSqlString)((0, filter_builder_1.normalizePath)(absPrefix))}%'`);
454
474
  }
455
475
  const rows = yield query.toArray();
456
476
  const map = new Map();
@@ -467,7 +487,12 @@ exports.mcp = new commander_1.Command("mcp")
467
487
  existing.count += 1;
468
488
  }
469
489
  else {
470
- map.set(sym, { symbol: sym, count: 1, path: rowPath, line: Math.max(1, line + 1) });
490
+ map.set(sym, {
491
+ symbol: sym,
492
+ count: 1,
493
+ path: rowPath,
494
+ line: Math.max(1, line + 1),
495
+ });
471
496
  }
472
497
  }
473
498
  }
@@ -479,9 +504,9 @@ exports.mcp = new commander_1.Command("mcp")
479
504
  })
480
505
  .slice(0, limit);
481
506
  if (entries.length === 0) {
482
- return ok("No symbols found. Run 'osgrep index' to build the index.");
507
+ return ok("No symbols found. Run 'gmax index' to build the index.");
483
508
  }
484
- return ok(JSON.stringify(entries, null, 2));
509
+ return ok(JSON.stringify(entries));
485
510
  }
486
511
  catch (e) {
487
512
  const msg = e instanceof Error ? e.message : String(e);
@@ -491,46 +516,39 @@ exports.mcp = new commander_1.Command("mcp")
491
516
  }
492
517
  function handleIndexStatus() {
493
518
  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
- }
519
+ var _a, _b, _c, _d;
514
520
  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));
521
+ const config = (0, index_config_1.readIndexConfig)(config_1.PATHS.configPath);
522
+ const projects = (0, project_registry_1.listProjects)();
523
+ const db = getVectorDb();
524
+ const stats = yield db.getStats();
525
+ const fileCount = yield db.getDistinctFileCount();
526
+ return ok(JSON.stringify({
527
+ store: "centralized (~/.gmax/lancedb)",
528
+ totalChunks: stats.chunks,
529
+ totalFiles: fileCount,
530
+ totalBytes: stats.totalBytes,
531
+ embedMode: (_a = config === null || config === void 0 ? void 0 : config.embedMode) !== null && _a !== void 0 ? _a : "unknown",
532
+ model: (_b = config === null || config === void 0 ? void 0 : config.embedModel) !== null && _b !== void 0 ? _b : null,
533
+ vectorDim: (_c = config === null || config === void 0 ? void 0 : config.vectorDim) !== null && _c !== void 0 ? _c : null,
534
+ indexedAt: (_d = config === null || config === void 0 ? void 0 : config.indexedAt) !== null && _d !== void 0 ? _d : null,
535
+ indexedDirectories: projects.map((p) => ({
536
+ name: p.name,
537
+ root: p.root,
538
+ lastIndexed: p.lastIndexed,
539
+ })),
540
+ }));
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
  }));