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
@@ -17,29 +17,29 @@ const node_fs_1 = __importDefault(require("node:fs"));
17
17
  const node_os_1 = __importDefault(require("node:os"));
18
18
  const node_path_1 = __importDefault(require("node:path"));
19
19
  const commander_1 = require("commander");
20
- const TOOL_PATH = node_path_1.default.join(node_os_1.default.homedir(), ".config", "opencode", "tool", "osgrep.ts");
21
- const PLUGIN_PATH = node_path_1.default.join(node_os_1.default.homedir(), ".config", "opencode", "plugin", "osgrep.ts");
20
+ const TOOL_PATH = node_path_1.default.join(node_os_1.default.homedir(), ".config", "opencode", "tool", "gmax.ts");
21
+ const PLUGIN_PATH = node_path_1.default.join(node_os_1.default.homedir(), ".config", "opencode", "plugin", "gmax.ts");
22
22
  const CONFIG_PATH = node_path_1.default.join(node_os_1.default.homedir(), ".config", "opencode", "opencode.json");
23
23
  const SHIM_CONTENT = `
24
24
  import { tool } from "@opencode-ai/plugin";
25
25
 
26
26
  const SKILL = \`
27
27
  ---
28
- name: osgrep
29
- description: Semantic code search. Use alongside grep - grep for exact strings, osgrep for concepts.
30
- allowed-tools: "Bash(osgrep:*), Read"
28
+ name: gmax
29
+ description: Semantic code search. Use alongside grep - grep for exact strings, gmax for concepts.
30
+ allowed-tools: "Bash(gmax:*), Read"
31
31
  ---
32
32
 
33
- ## What osgrep does
33
+ ## What gmax does
34
34
 
35
- Finds code by meaning. When you'd ask a colleague "where do we handle auth?", use osgrep.
35
+ Finds code by meaning. When you'd ask a colleague "where do we handle auth?", use gmax.
36
36
 
37
37
  - grep/ripgrep: exact string match, fast
38
- - osgrep: concept match, finds code you couldn't grep for
38
+ - gmax: concept match, finds code you couldn't grep for
39
39
 
40
40
  ## Primary command
41
41
 
42
- osgrep "where do we validate user permissions"
42
+ gmax "where do we validate user permissions"
43
43
 
44
44
 
45
45
  Returns ~10 results with code snippets (15+ lines each). Usually enough to understand what's happening.
@@ -65,7 +65,7 @@ export async function handleAuth(req: Request) {
65
65
 
66
66
  The snippet often has enough context. But if you need more:
67
67
 
68
- # osgrep found src/auth/handler.ts:45-90 as ORCH
68
+ # gmax found src/auth/handler.ts:45-90 as ORCH
69
69
  Read src/auth/handler.ts:45-120
70
70
 
71
71
 
@@ -74,32 +74,32 @@ Read the specific line range, not the whole file.
74
74
  ## Other commands
75
75
 
76
76
  # Trace call graph (who calls X, what X calls)
77
- osgrep trace handleAuth
77
+ gmax trace handleAuth
78
78
 
79
79
  # Skeleton of a huge file (to find which ranges to read)
80
- osgrep skeleton src/giant-2000-line-file.ts
80
+ gmax skeleton src/giant-2000-line-file.ts
81
81
 
82
82
  # Just file paths when you only need locations
83
- osgrep "authentication" --compact
83
+ gmax "authentication" --compact
84
84
 
85
85
 
86
86
  ## Workflow: architecture questions
87
87
 
88
88
  # 1. Find entry points
89
- osgrep "where do requests enter the server"
89
+ gmax "where do requests enter the server"
90
90
  # Review the ORCH results - code is shown
91
91
 
92
92
  # 2. If you need deeper context on a specific function
93
93
  Read src/server/handler.ts:45-120
94
94
 
95
95
  # 3. Trace to understand call flow
96
- osgrep trace handleRequest
96
+ gmax trace handleRequest
97
97
 
98
98
  ## Tips
99
99
 
100
100
  - More words = better results. "auth" is vague. "where does the server validate JWT tokens" is specific.
101
101
  - ORCH results contain the logic - prioritize these
102
- - Don't read entire files. Use the line ranges osgrep gives you.
102
+ - Don't read entire files. Use the line ranges gmax gives you.
103
103
  - If results seem off, rephrase your query like you'd ask a teammate
104
104
 
105
105
  \`;
@@ -108,16 +108,16 @@ export default tool({
108
108
  description: SKILL,
109
109
  args: {
110
110
  argv: tool.schema.array(tool.schema.string())
111
- .describe("Arguments for osgrep, e.g. ['search', 'user auth']")
111
+ .describe("Arguments for gmax, e.g. ['search', 'user auth']")
112
112
  },
113
113
  async execute({ argv }) {
114
114
  try {
115
115
  // @ts-ignore
116
- const out = await Bun.spawn(["osgrep", ...argv], { stdout: "pipe" }).stdout;
116
+ const out = await Bun.spawn(["gmax", ...argv], { stdout: "pipe" }).stdout;
117
117
  const text = await new Response(out).text();
118
118
  return text.trim();
119
119
  } catch (err) {
120
- return \`Error running osgrep: \${err}\`;
120
+ return \`Error running gmax: \${err}\`;
121
121
  }
122
122
  },
123
123
  })`;
@@ -148,14 +148,14 @@ function install() {
148
148
  config.$schema = "https://opencode.ai/config.json";
149
149
  if (!config.mcp)
150
150
  config.mcp = {};
151
- config.mcp.osgrep = {
151
+ config.mcp.gmax = {
152
152
  type: "local",
153
- command: ["osgrep", "mcp"],
153
+ command: ["gmax", "mcp"],
154
154
  enabled: true,
155
155
  };
156
156
  node_fs_1.default.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2));
157
157
  console.log("✅ Registered MCP server in", CONFIG_PATH);
158
- console.log(" Command: check proper path if 'osgrep' is not in PATH of OpenCode.");
158
+ console.log(" Command: check proper path if 'gmax' is not in PATH of OpenCode.");
159
159
  }
160
160
  catch (err) {
161
161
  console.error("❌ Installation failed:", err);
@@ -174,8 +174,8 @@ function uninstall() {
174
174
  // 2. Unregister MCP
175
175
  if (node_fs_1.default.existsSync(CONFIG_PATH)) {
176
176
  const config = JSON.parse(node_fs_1.default.readFileSync(CONFIG_PATH, "utf-8") || "{}");
177
- if ((_a = config.mcp) === null || _a === void 0 ? void 0 : _a.osgrep) {
178
- delete config.mcp.osgrep;
177
+ if ((_a = config.mcp) === null || _a === void 0 ? void 0 : _a.gmax) {
178
+ delete config.mcp.gmax;
179
179
  node_fs_1.default.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2));
180
180
  console.log("✅ Unregistered MCP server.");
181
181
  }
@@ -192,8 +192,8 @@ function uninstall() {
192
192
  });
193
193
  }
194
194
  exports.installOpencode = new commander_1.Command("install-opencode")
195
- .description("Install osgrep as an OpenCode plugin (Daemon + Tool)")
195
+ .description("Install gmax as an OpenCode plugin (Daemon + Tool)")
196
196
  .action(install);
197
197
  exports.uninstallOpencode = new commander_1.Command("uninstall-opencode")
198
- .description("Remove the osgrep OpenCode plugin")
198
+ .description("Remove the gmax OpenCode plugin")
199
199
  .action(uninstall);
@@ -178,7 +178,7 @@ function formatCompactTSV(hits, projectRoot, query) {
178
178
  if (!hits.length)
179
179
  return "No matches found.";
180
180
  const lines = [];
181
- lines.push(`osgrep hits\tquery=${query}\tcount=${hits.length}`);
181
+ lines.push(`gmax hits\tquery=${query}\tcount=${hits.length}`);
182
182
  lines.push("path\tlines\tscore\trole\tconf\tdefined");
183
183
  for (const hit of hits) {
184
184
  const relPath = path.isAbsolute(hit.path)
@@ -206,7 +206,7 @@ function formatCompactPretty(hits, projectRoot, query, termWidth, useAnsi) {
206
206
  const gutters = 5;
207
207
  const fixed = wLines + wScore + wRole + wConf + wDef + gutters;
208
208
  const wPath = Math.max(24, Math.min(64, termWidth - fixed));
209
- const header = `osgrep hits count=${hits.length} query="${query}"`;
209
+ const header = `gmax hits count=${hits.length} query="${query}"`;
210
210
  const cols = [
211
211
  padR("path", wPath),
212
212
  padR("lines", wLines),
@@ -277,13 +277,17 @@ function outputSkeletons(results, projectRoot, limit, db) {
277
277
  }
278
278
  const skeletonOpts = { includeSummary: true };
279
279
  const skeletonResults = [];
280
- for (const relPath of filesToProcess) {
280
+ for (const filePath of filesToProcess) {
281
+ // Paths from search results are now absolute (centralized index)
282
+ const absPath = path.isAbsolute(filePath)
283
+ ? filePath
284
+ : path.resolve(projectRoot, filePath);
281
285
  // 1. Try DB cache
282
286
  if (db) {
283
- const cached = yield (0, retriever_1.getStoredSkeleton)(db, relPath);
287
+ const cached = yield (0, retriever_1.getStoredSkeleton)(db, absPath);
284
288
  if (cached) {
285
289
  skeletonResults.push({
286
- file: relPath,
290
+ file: filePath,
287
291
  skeleton: cached,
288
292
  tokens: Math.ceil(cached.length / 4), // Rough estimate
289
293
  });
@@ -292,20 +296,19 @@ function outputSkeletons(results, projectRoot, limit, db) {
292
296
  }
293
297
  // 2. Fallback to fresh generation
294
298
  yield globalSkeletonizer.init();
295
- const absPath = path.resolve(projectRoot, relPath);
296
299
  if (!fs.existsSync(absPath)) {
297
300
  skeletonResults.push({
298
- file: relPath,
299
- skeleton: `// File not found: ${relPath}`,
301
+ file: filePath,
302
+ skeleton: `// File not found: ${filePath}`,
300
303
  tokens: 0,
301
304
  error: "File not found",
302
305
  });
303
306
  continue;
304
307
  }
305
308
  const content = fs.readFileSync(absPath, "utf-8");
306
- const res = yield globalSkeletonizer.skeletonizeFile(relPath, content, skeletonOpts);
309
+ const res = yield globalSkeletonizer.skeletonizeFile(absPath, content, skeletonOpts);
307
310
  skeletonResults.push({
308
- file: relPath,
311
+ file: filePath,
309
312
  skeleton: res.skeleton,
310
313
  tokens: res.tokenEstimate,
311
314
  error: res.error,
@@ -425,11 +428,11 @@ exports.search = new commander_1.Command("search")
425
428
  const projectRoot = (_b = (0, project_root_1.findProjectRoot)(searchRoot)) !== null && _b !== void 0 ? _b : searchRoot;
426
429
  const paths = (0, project_root_1.ensureProjectPaths)(projectRoot);
427
430
  // Propagate project root to worker processes
428
- process.env.OSGREP_PROJECT_ROOT = projectRoot;
431
+ process.env.GMAX_PROJECT_ROOT = projectRoot;
429
432
  vectorDb = new vector_db_1.VectorDB(paths.lancedbDir);
430
433
  // Check for active indexing lock and warn if present
431
434
  // This allows agents (via shim) to know results might be partial.
432
- if ((0, lock_1.isLocked)(paths.osgrepDir)) {
435
+ if ((0, lock_1.isLocked)(paths.dataDir)) {
433
436
  console.warn("⚠️ Warning: Indexing in progress... search results may be incomplete.");
434
437
  }
435
438
  const hasRows = yield vectorDb.hasAnyRows();
@@ -475,7 +478,14 @@ exports.search = new commander_1.Command("search")
475
478
  }
476
479
  }
477
480
  const searcher = new searcher_1.Searcher(vectorDb);
478
- const searchResult = yield searcher.search(pattern, parseInt(options.m, 10), { rerank: true }, undefined, exec_path ? path.relative(projectRoot, path.resolve(exec_path)) : "");
481
+ // Use absolute path prefix for filtering
482
+ const searchPathPrefix = exec_path
483
+ ? path.resolve(exec_path)
484
+ : projectRoot;
485
+ const pathFilter = searchPathPrefix.endsWith("/")
486
+ ? searchPathPrefix
487
+ : `${searchPathPrefix}/`;
488
+ const searchResult = yield searcher.search(pattern, parseInt(options.m, 10), { rerank: true }, undefined, pathFilter);
479
489
  const filteredData = searchResult.data.filter((r) => typeof r.score !== "number" || r.score >= minScore);
480
490
  if (options.skeleton) {
481
491
  yield outputSkeletons(filteredData, projectRoot, parseInt(options.m, 10), vectorDb);
@@ -49,8 +49,8 @@ const http = __importStar(require("node:http"));
49
49
  const path = __importStar(require("node:path"));
50
50
  const commander_1 = require("commander");
51
51
  const config_1 = require("../config");
52
- const index_config_1 = require("../lib/index/index-config");
53
52
  const grammar_loader_1 = require("../lib/index/grammar-loader");
53
+ const index_config_1 = require("../lib/index/index-config");
54
54
  const sync_helpers_1 = require("../lib/index/sync-helpers");
55
55
  const syncer_1 = require("../lib/index/syncer");
56
56
  const watcher_1 = require("../lib/index/watcher");
@@ -64,13 +64,19 @@ const server_registry_1 = require("../lib/utils/server-registry");
64
64
  function isMlxServerUp() {
65
65
  const port = parseInt(process.env.MLX_EMBED_PORT || "8100", 10);
66
66
  return new Promise((resolve) => {
67
- const req = http.get({ hostname: "127.0.0.1", port, path: "/health", timeout: 2000 }, (res) => { res.resume(); resolve(res.statusCode === 200); });
67
+ const req = http.get({ hostname: "127.0.0.1", port, path: "/health", timeout: 2000 }, (res) => {
68
+ res.resume();
69
+ resolve(res.statusCode === 200);
70
+ });
68
71
  req.on("error", () => resolve(false));
69
- req.on("timeout", () => { req.destroy(); resolve(false); });
72
+ req.on("timeout", () => {
73
+ req.destroy();
74
+ resolve(false);
75
+ });
70
76
  });
71
77
  }
72
78
  function startMlxServer(mlxModel) {
73
- // Look for mlx-embed-server relative to the osgrep package
79
+ // Look for mlx-embed-server relative to the grepmax package
74
80
  const candidates = [
75
81
  path.resolve(__dirname, "../../mlx-embed-server"),
76
82
  path.resolve(__dirname, "../mlx-embed-server"),
@@ -94,8 +100,8 @@ function startMlxServer(mlxModel) {
94
100
  return child;
95
101
  }
96
102
  exports.serve = new commander_1.Command("serve")
97
- .description("Run osgrep as a background server with live indexing")
98
- .option("-p, --port <port>", "Port to listen on", process.env.OSGREP_PORT || "4444")
103
+ .description("Run gmax as a background server with live indexing")
104
+ .option("-p, --port <port>", "Port to listen on", process.env.GMAX_PORT || "4444")
99
105
  .option("-b, --background", "Run in background", false)
100
106
  .option("--cpu", "Use CPU-only embeddings (skip MLX GPU server)", false)
101
107
  .option("--no-idle-timeout", "Disable the 30-minute idle shutdown", false)
@@ -117,7 +123,9 @@ exports.serve = new commander_1.Command("serve")
117
123
  .filter((arg) => arg !== "-b" && arg !== "--background");
118
124
  const logDir = path.join(config_1.PATHS.globalRoot, "logs");
119
125
  fs.mkdirSync(logDir, { recursive: true });
120
- const safeName = path.basename(projectRoot).replace(/[^a-zA-Z0-9._-]/g, "_");
126
+ const safeName = path
127
+ .basename(projectRoot)
128
+ .replace(/[^a-zA-Z0-9._-]/g, "_");
121
129
  const logFile = path.join(logDir, `server-${safeName}.log`);
122
130
  const out = fs.openSync(logFile, "a");
123
131
  const err = fs.openSync(logFile, "a");
@@ -125,7 +133,7 @@ exports.serve = new commander_1.Command("serve")
125
133
  detached: true,
126
134
  stdio: ["ignore", out, err],
127
135
  cwd: process.cwd(),
128
- env: Object.assign(Object.assign({}, process.env), { OSGREP_BACKGROUND: "true" }),
136
+ env: Object.assign(Object.assign({}, process.env), { GMAX_BACKGROUND: "true" }),
129
137
  });
130
138
  child.unref();
131
139
  console.log(`Started background server (PID: ${child.pid})`);
@@ -134,7 +142,7 @@ exports.serve = new commander_1.Command("serve")
134
142
  const paths = (0, project_root_1.ensureProjectPaths)(projectRoot);
135
143
  const projectName = path.basename(projectRoot);
136
144
  // Propagate project root to worker processes
137
- process.env.OSGREP_PROJECT_ROOT = projectRoot;
145
+ process.env.GMAX_PROJECT_ROOT = projectRoot;
138
146
  // Determine embed mode: --cpu flag overrides, then config, then default
139
147
  // Default to GPU on Apple Silicon, CPU everywhere else
140
148
  const isAppleSilicon = process.arch === "arm64" && process.platform === "darwin";
@@ -183,7 +191,7 @@ exports.serve = new commander_1.Command("serve")
183
191
  yield (0, setup_helpers_1.ensureSetup)();
184
192
  yield (0, grammar_loader_1.ensureGrammars)(console.log, { silent: true });
185
193
  // Initial sync is self-contained (creates+closes its own VectorDB+MetaCache).
186
- if (!process.env.OSGREP_BACKGROUND) {
194
+ if (!process.env.GMAX_BACKGROUND) {
187
195
  const { spinner, onProgress } = (0, sync_helpers_1.createIndexingSpinner)(projectRoot, "Indexing before starting server...");
188
196
  try {
189
197
  yield (0, syncer_1.initialSync)({ projectRoot, onProgress });
@@ -206,7 +214,7 @@ exports.serve = new commander_1.Command("serve")
206
214
  projectRoot,
207
215
  vectorDb,
208
216
  metaCache,
209
- osgrepDir: paths.osgrepDir,
217
+ dataDir: paths.dataDir,
210
218
  onReindex: (files, durationMs) => {
211
219
  console.log(`[watch:${projectName}] Reindexed ${files} file${files !== 1 ? "s" : ""} (${(durationMs / 1000).toFixed(1)}s)`);
212
220
  },
@@ -242,7 +250,7 @@ exports.serve = new commander_1.Command("serve")
242
250
  const cfg = (0, index_config_1.readIndexConfig)(paths.configPath);
243
251
  const stats = {
244
252
  files: dbStats.chunks > 0
245
- ? (yield vectorDb.getDistinctFileCount())
253
+ ? yield vectorDb.getDistinctFileCount()
246
254
  : 0,
247
255
  chunks: dbStats.chunks,
248
256
  totalBytes: dbStats.totalBytes,
@@ -260,7 +268,9 @@ exports.serve = new commander_1.Command("serve")
260
268
  catch (err) {
261
269
  res.statusCode = 500;
262
270
  res.setHeader("Content-Type", "application/json");
263
- res.end(JSON.stringify({ error: (err === null || err === void 0 ? void 0 : err.message) || "stats_failed" }));
271
+ res.end(JSON.stringify({
272
+ error: (err === null || err === void 0 ? void 0 : err.message) || "stats_failed",
273
+ }));
264
274
  }
265
275
  return;
266
276
  }
@@ -292,23 +302,13 @@ exports.serve = new commander_1.Command("serve")
292
302
  : {};
293
303
  const query = typeof body.query === "string" ? body.query : "";
294
304
  const limit = typeof body.limit === "number" ? body.limit : 10;
295
- let searchPath = "";
305
+ // Use absolute path prefix for search filtering
306
+ let searchPath = `${projectRoot}/`;
296
307
  if (typeof body.path === "string") {
297
308
  const resolvedPath = path.resolve(projectRoot, body.path);
298
- const rootPrefix = projectRoot.endsWith(path.sep)
299
- ? projectRoot
300
- : `${projectRoot}${path.sep}`;
301
- // Normalize paths for consistency (Windows/Linux)
302
- const normalizedRootPrefix = path.normalize(rootPrefix);
303
- const normalizedResolvedPath = path.normalize(resolvedPath);
304
- if (normalizedResolvedPath !== projectRoot &&
305
- !normalizedResolvedPath.startsWith(normalizedRootPrefix)) {
306
- res.statusCode = 400;
307
- res.setHeader("Content-Type", "application/json");
308
- res.end(JSON.stringify({ error: "invalid_path" }));
309
- return;
310
- }
311
- searchPath = path.relative(projectRoot, resolvedPath);
309
+ searchPath = resolvedPath.endsWith("/")
310
+ ? resolvedPath
311
+ : `${resolvedPath}/`;
312
312
  }
313
313
  // Add AbortController for cancellation
314
314
  const ac = new AbortController();
@@ -383,8 +383,8 @@ exports.serve = new commander_1.Command("serve")
383
383
  server.listen(port, () => {
384
384
  const address = server.address();
385
385
  const actualPort = typeof address === "object" && address ? address.port : port;
386
- if (!process.env.OSGREP_BACKGROUND) {
387
- console.log(`osgrep server listening on http://localhost:${actualPort} (${projectRoot})`);
386
+ if (!process.env.GMAX_BACKGROUND) {
387
+ console.log(`gmax server listening on http://localhost:${actualPort} (${projectRoot})`);
388
388
  }
389
389
  (0, server_registry_1.registerServer)({
390
390
  pid: process.pid,
@@ -51,19 +51,13 @@ const config_1 = require("../config");
51
51
  const grammar_loader_1 = require("../lib/index/grammar-loader");
52
52
  const index_config_1 = require("../lib/index/index-config");
53
53
  const setup_helpers_1 = require("../lib/setup/setup-helpers");
54
- const project_root_1 = require("../lib/utils/project-root");
55
54
  const exit_1 = require("../lib/utils/exit");
56
- const MLX_MODELS = [
57
- {
58
- value: "ibm-granite/granite-embedding-small-english-r2",
59
- label: "Granite Small (general purpose, 384-dim)",
60
- },
61
- ];
55
+ const project_root_1 = require("../lib/utils/project-root");
62
56
  exports.setup = new commander_1.Command("setup")
63
57
  .description("Interactive setup: download models, choose embedding mode")
64
58
  .action(() => __awaiter(void 0, void 0, void 0, function* () {
65
- var _a, _b;
66
- p.intro("osgrep setup");
59
+ var _a, _b, _c, _d;
60
+ p.intro("gmax setup");
67
61
  // Step 1: Download ONNX models + grammars (existing behavior)
68
62
  try {
69
63
  yield (0, setup_helpers_1.ensureSetup)();
@@ -84,9 +78,9 @@ exports.setup = new commander_1.Command("setup")
84
78
  const modelPath = path.join(config_1.PATHS.models, ...id.split("/"));
85
79
  return { id, exists: fs.existsSync(modelPath) };
86
80
  });
87
- modelStatuses.forEach(({ id, exists }) => {
81
+ for (const { id, exists } of modelStatuses) {
88
82
  p.log.info(`${exists ? "✓" : "✗"} ${id}`);
89
- });
83
+ }
90
84
  // Check skiplist
91
85
  const colbertPath = path.join(config_1.PATHS.models, ...config_1.MODEL_IDS.colbert.split("/"));
92
86
  const skiplistPath = path.join(colbertPath, "skiplist.json");
@@ -100,13 +94,31 @@ exports.setup = new commander_1.Command("setup")
100
94
  p.log.success("Skiplist downloaded");
101
95
  }
102
96
  }
103
- catch (_c) {
97
+ catch (_e) {
104
98
  p.log.warn("Skiplist download failed (will use fallback)");
105
99
  }
106
100
  }
107
- // Step 3: Interactive embed mode selection
101
+ // Step 3: Read existing config
108
102
  const paths = (0, project_root_1.ensureProjectPaths)(process.cwd());
109
103
  const existingConfig = (0, index_config_1.readIndexConfig)(paths.configPath);
104
+ const globalConfig = (0, index_config_1.readGlobalConfig)();
105
+ // Step 4: Model tier selection
106
+ const modelTier = yield p.select({
107
+ message: "Model size",
108
+ options: Object.values(config_1.MODEL_TIERS).map((tier) => ({
109
+ value: tier.id,
110
+ label: tier.label,
111
+ hint: tier.id === "standard" ? "32GB+ RAM recommended" : "recommended",
112
+ })),
113
+ initialValue: (_b = (_a = existingConfig === null || existingConfig === void 0 ? void 0 : existingConfig.modelTier) !== null && _a !== void 0 ? _a : globalConfig.modelTier) !== null && _b !== void 0 ? _b : "small",
114
+ });
115
+ if (p.isCancel(modelTier)) {
116
+ p.cancel("Setup cancelled");
117
+ yield (0, exit_1.gracefulExit)();
118
+ return;
119
+ }
120
+ const selectedTier = config_1.MODEL_TIERS[modelTier];
121
+ // Step 5: Embed mode selection
110
122
  const embedMode = yield p.select({
111
123
  message: "Embedding mode",
112
124
  options: [
@@ -121,40 +133,39 @@ exports.setup = new commander_1.Command("setup")
121
133
  hint: "Apple Silicon only, faster indexing + search",
122
134
  },
123
135
  ],
124
- initialValue: (_a = existingConfig === null || existingConfig === void 0 ? void 0 : existingConfig.embedMode) !== null && _a !== void 0 ? _a : (process.arch === "arm64" && process.platform === "darwin" ? "gpu" : "cpu"),
136
+ initialValue: (_c = existingConfig === null || existingConfig === void 0 ? void 0 : existingConfig.embedMode) !== null && _c !== void 0 ? _c : (process.arch === "arm64" && process.platform === "darwin"
137
+ ? "gpu"
138
+ : "cpu"),
125
139
  });
126
140
  if (p.isCancel(embedMode)) {
127
141
  p.cancel("Setup cancelled");
128
142
  yield (0, exit_1.gracefulExit)();
129
143
  return;
130
144
  }
131
- let mlxModel;
132
- if (embedMode === "gpu") {
133
- const modelChoice = yield p.select({
134
- message: "MLX embedding model",
135
- options: MLX_MODELS.map((m) => ({
136
- value: m.value,
137
- label: m.label,
138
- })),
139
- initialValue: (_b = existingConfig === null || existingConfig === void 0 ? void 0 : existingConfig.mlxModel) !== null && _b !== void 0 ? _b : MLX_MODELS[0].value,
140
- });
141
- if (p.isCancel(modelChoice)) {
142
- p.cancel("Setup cancelled");
143
- yield (0, exit_1.gracefulExit)();
144
- return;
145
+ const mlxModel = embedMode === "gpu" ? selectedTier.mlxModel : undefined;
146
+ // Step 6: Write configs
147
+ (0, index_config_1.writeSetupConfig)(paths.configPath, {
148
+ embedMode,
149
+ mlxModel,
150
+ modelTier,
151
+ });
152
+ (0, index_config_1.writeGlobalConfig)({
153
+ modelTier,
154
+ vectorDim: selectedTier.vectorDim,
155
+ embedMode,
156
+ mlxModel,
157
+ });
158
+ // Step 7: Warn about reindex if tier/mode changed
159
+ if (existingConfig === null || existingConfig === void 0 ? void 0 : existingConfig.indexedAt) {
160
+ const tierChanged = existingConfig.modelTier !== modelTier;
161
+ const modeChanged = existingConfig.embedMode !== embedMode;
162
+ if (tierChanged) {
163
+ p.log.warn(`Model tier changed (${(_d = existingConfig.vectorDim) !== null && _d !== void 0 ? _d : 384}d → ${selectedTier.vectorDim}d). Existing indexes will be rebuilt on next use.`);
164
+ }
165
+ else if (modeChanged) {
166
+ p.log.warn("Embedding mode changed. Run `gmax serve` to apply the new settings.");
145
167
  }
146
- mlxModel = modelChoice;
147
- }
148
- // Step 4: Write config
149
- (0, index_config_1.writeSetupConfig)(paths.configPath, { embedMode, mlxModel });
150
- // Step 5: Warn about reindex if mode/model changed
151
- if ((existingConfig === null || existingConfig === void 0 ? void 0 : existingConfig.indexedAt) &&
152
- (existingConfig.embedMode !== embedMode ||
153
- existingConfig.mlxModel !== mlxModel)) {
154
- p.log.warn("Embedding mode changed. Run `osgrep serve` to reindex with the new settings.");
155
168
  }
156
- p.outro(embedMode === "gpu"
157
- ? `Ready — GPU mode with ${mlxModel}`
158
- : "Ready — CPU mode");
169
+ p.outro(`Ready — ${selectedTier.label}, ${embedMode === "gpu" ? "GPU" : "CPU"} mode`);
159
170
  yield (0, exit_1.gracefulExit)();
160
171
  }));
@@ -1,11 +1,11 @@
1
1
  "use strict";
2
2
  /**
3
- * osgrep skeleton - Show code skeleton (signatures without implementation)
3
+ * gmax skeleton - Show code skeleton (signatures without implementation)
4
4
  *
5
5
  * Usage:
6
- * osgrep skeleton <file> # Skeleton of a file
7
- * osgrep skeleton <symbol> # Find symbol and skeleton its file
8
- * osgrep skeleton "query" # Search and skeleton top results
6
+ * gmax skeleton <file> # Skeleton of a file
7
+ * gmax skeleton <symbol> # Find symbol and skeleton its file
8
+ * gmax skeleton "query" # Search and skeleton top results
9
9
  */
10
10
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
11
11
  if (k2 === undefined) k2 = k;
@@ -143,8 +143,8 @@ exports.skeleton = new commander_1.Command("skeleton")
143
143
  return;
144
144
  }
145
145
  if (vectorDb) {
146
- const relativeToProject = path.relative(projectRoot, filePath);
147
- const cached = yield (0, retriever_1.getStoredSkeleton)(vectorDb, relativeToProject);
146
+ // Use absolute path for DB lookup (centralized index stores absolute paths)
147
+ const cached = yield (0, retriever_1.getStoredSkeleton)(vectorDb, filePath);
148
148
  if (cached) {
149
149
  outputResult({
150
150
  success: true,
@@ -163,17 +163,20 @@ exports.skeleton = new commander_1.Command("skeleton")
163
163
  const filePath = yield findFileBySymbol(target, vectorDb);
164
164
  if (!filePath) {
165
165
  console.error(`Symbol not found in index: ${target}`);
166
- console.error("Try running 'osgrep index' first or use a search query.");
166
+ console.error("Try running 'gmax index' first or use a search query.");
167
167
  process.exitCode = 1;
168
168
  return;
169
169
  }
170
- const absolutePath = path.resolve(projectRoot, filePath);
170
+ // filePath from DB is absolute (centralized index)
171
+ const absolutePath = path.isAbsolute(filePath)
172
+ ? filePath
173
+ : path.resolve(projectRoot, filePath);
171
174
  if (!fs.existsSync(absolutePath)) {
172
175
  console.error(`File not found: ${absolutePath}`);
173
176
  process.exitCode = 1;
174
177
  return;
175
178
  }
176
- const cached = yield (0, retriever_1.getStoredSkeleton)(vectorDb, filePath);
179
+ const cached = yield (0, retriever_1.getStoredSkeleton)(vectorDb, absolutePath);
177
180
  if (cached) {
178
181
  outputResult({
179
182
  success: true,
@@ -183,7 +186,7 @@ exports.skeleton = new commander_1.Command("skeleton")
183
186
  return;
184
187
  }
185
188
  const content = fs.readFileSync(absolutePath, "utf-8");
186
- const result = yield skeletonizer.skeletonizeFile(filePath, content, skeletonOpts);
189
+ const result = yield skeletonizer.skeletonizeFile(absolutePath, content, skeletonOpts);
187
190
  outputResult(result, options);
188
191
  }
189
192
  else {
@@ -211,7 +214,10 @@ exports.skeleton = new commander_1.Command("skeleton")
211
214
  // Skeletonize each file
212
215
  const results = [];
213
216
  for (const filePath of filePaths) {
214
- const absolutePath = path.resolve(projectRoot, filePath);
217
+ // Paths from search results are absolute (centralized index)
218
+ const absolutePath = path.isAbsolute(filePath)
219
+ ? filePath
220
+ : path.resolve(projectRoot, filePath);
215
221
  if (!fs.existsSync(absolutePath)) {
216
222
  results.push({
217
223
  file: filePath,
@@ -222,7 +228,7 @@ exports.skeleton = new commander_1.Command("skeleton")
222
228
  continue;
223
229
  }
224
230
  // Try cache first
225
- const cached = yield (0, retriever_1.getStoredSkeleton)(vectorDb, filePath);
231
+ const cached = yield (0, retriever_1.getStoredSkeleton)(vectorDb, absolutePath);
226
232
  if (cached) {
227
233
  results.push({
228
234
  file: filePath,
@@ -232,7 +238,7 @@ exports.skeleton = new commander_1.Command("skeleton")
232
238
  continue;
233
239
  }
234
240
  const content = fs.readFileSync(absolutePath, "utf-8");
235
- const result = yield skeletonizer.skeletonizeFile(filePath, content, skeletonOpts);
241
+ const result = yield skeletonizer.skeletonizeFile(absolutePath, content, skeletonOpts);
236
242
  results.push({
237
243
  file: filePath,
238
244
  skeleton: result.skeleton,