grepmax 0.6.4 → 0.7.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.
package/README.md CHANGED
@@ -106,6 +106,7 @@ In our public benchmarks, `grepmax` can save about 20% of your LLM tokens and de
106
106
  | `trace_calls` | Call graph — who calls a symbol and what it calls (unscoped, crosses project boundaries) |
107
107
  | `list_symbols` | List indexed functions, classes, and types with definition locations |
108
108
  | `index_status` | Check index health: chunk counts, indexed directories, model info |
109
+ | `summarize_directory` | Generate LLM summaries for indexed chunks. Summaries appear in search results. |
109
110
 
110
111
  ## Commands
111
112
 
@@ -193,6 +194,17 @@ gmax skeleton "auth logic" # Search, skeletonize top matches
193
194
 
194
195
  **Supported Languages:** TypeScript, JavaScript, Python, Go, Rust, Java, C#, C++, C, Ruby, PHP, Swift, Kotlin.
195
196
 
197
+ ### `gmax config`
198
+
199
+ View or update configuration without the full interactive setup.
200
+
201
+ ```bash
202
+ gmax config # Show current settings
203
+ gmax config --embed-mode cpu # Switch to CPU embeddings
204
+ gmax config --embed-mode gpu # Switch to GPU (MLX)
205
+ gmax config --model-tier standard # Switch to standard model (768d)
206
+ ```
207
+
196
208
  ### `gmax doctor`
197
209
 
198
210
  Checks installation health, model paths, and database integrity.
@@ -244,9 +256,31 @@ handleAuth [exported ORCH C:8] src/auth/handler.ts:45-90
244
256
 
245
257
  ## Configuration
246
258
 
259
+ ### Config File
260
+
261
+ Settings are stored in `~/.gmax/config.json`:
262
+
263
+ ```json
264
+ {
265
+ "modelTier": "small",
266
+ "vectorDim": 384,
267
+ "embedMode": "gpu",
268
+ "mlxModel": "ibm-granite/granite-embedding-small-english-r2"
269
+ }
270
+ ```
271
+
272
+ View and change settings with `gmax config` or run `gmax setup` for interactive configuration.
273
+
247
274
  ### Ignoring Files
248
275
 
249
- gmax respects `.gitignore` and `.gmaxignore` files. Create a `.gmaxignore` in your directory root to exclude additional patterns.
276
+ gmax respects `.gitignore` and `.gmaxignore` files. Create a `.gmaxignore` in your directory root to exclude additional patterns:
277
+
278
+ ```gitignore
279
+ # .gmaxignore — same syntax as .gitignore
280
+ docs/generated/
281
+ *.test.ts
282
+ fixtures/
283
+ ```
250
284
 
251
285
  ### Index Management
252
286
 
@@ -255,15 +289,21 @@ gmax respects `.gitignore` and `.gmaxignore` files. Create a `.gmaxignore` in yo
255
289
  - **Clean up:** `gmax index --reset` re-indexes the current directory from scratch
256
290
  - **Full reset:** `rm -rf ~/.gmax/lancedb ~/.gmax/cache` to start completely fresh
257
291
 
258
- ## Development
292
+ ### Environment Variables
259
293
 
260
- ```bash
261
- pnpm install
262
- pnpm build
263
- pnpm test # vitest
264
- pnpm format # biome check
265
- just deploy # publish latest tag to npm
266
- ```
294
+ | Variable | Description | Default |
295
+ | --- | --- | --- |
296
+ | `GMAX_WORKER_THREADS` | Number of worker threads for embedding | 50% of CPU cores |
297
+ | `GMAX_EMBED_MODE` | Force `cpu` or `gpu` embedding mode | Auto-detect |
298
+ | `GMAX_DEBUG` | Enable debug logging (`1` to enable) | Off |
299
+ | `GMAX_VERBOSE` | Enable verbose output (`1` to enable) | Off |
300
+ | `GMAX_WORKER_TASK_TIMEOUT_MS` | Worker task timeout in ms | `120000` |
301
+ | `GMAX_MAX_WORKER_MEMORY_MB` | Max worker memory in MB | 50% of system RAM |
302
+ | `GMAX_MAX_PER_FILE` | Default max results per file in search | `3` |
303
+
304
+ ## Contributing
305
+
306
+ See [CLAUDE.md](CLAUDE.md) for development setup, commands, and architecture details.
267
307
 
268
308
  ## Troubleshooting
269
309
 
@@ -0,0 +1,80 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.config = void 0;
13
+ const commander_1 = require("commander");
14
+ const config_1 = require("../config");
15
+ const index_config_1 = require("../lib/index/index-config");
16
+ const exit_1 = require("../lib/utils/exit");
17
+ const project_root_1 = require("../lib/utils/project-root");
18
+ exports.config = new commander_1.Command("config")
19
+ .description("View or update gmax configuration")
20
+ .option("--embed-mode <mode>", "Set embedding mode: cpu or gpu")
21
+ .option("--model-tier <tier>", "Set model tier: small (384d) or standard (768d)")
22
+ .addHelpText("after", `
23
+ Examples:
24
+ gmax config Show current configuration
25
+ gmax config --embed-mode cpu Switch to CPU embeddings
26
+ gmax config --model-tier standard Switch to standard model (768d)
27
+ `)
28
+ .action((_opts, cmd) => __awaiter(void 0, void 0, void 0, function* () {
29
+ var _a, _b, _c, _d;
30
+ const options = cmd.optsWithGlobals();
31
+ const globalConfig = (0, index_config_1.readGlobalConfig)();
32
+ const paths = (0, project_root_1.ensureProjectPaths)(process.cwd());
33
+ const indexConfig = (0, index_config_1.readIndexConfig)(paths.configPath);
34
+ const hasUpdates = options.embedMode !== undefined || options.modelTier !== undefined;
35
+ if (!hasUpdates) {
36
+ // Show current config
37
+ const tier = (_a = config_1.MODEL_TIERS[globalConfig.modelTier]) !== null && _a !== void 0 ? _a : config_1.MODEL_TIERS.small;
38
+ console.log("gmax configuration (~/.gmax/config.json)\n");
39
+ console.log(` Model tier: ${globalConfig.modelTier} (${tier.vectorDim}d, ${tier.params})`);
40
+ console.log(` Embed mode: ${globalConfig.embedMode}`);
41
+ console.log(` Embed model: ${globalConfig.embedMode === "gpu" ? tier.mlxModel : tier.onnxModel}`);
42
+ if (indexConfig === null || indexConfig === void 0 ? void 0 : indexConfig.indexedAt) {
43
+ console.log(` Last indexed: ${indexConfig.indexedAt}`);
44
+ }
45
+ console.log(`\nTo change: gmax config --embed-mode <cpu|gpu> --model-tier <small|standard>`);
46
+ yield (0, exit_1.gracefulExit)();
47
+ return;
48
+ }
49
+ // Validate inputs
50
+ if (options.embedMode && !["cpu", "gpu"].includes(options.embedMode)) {
51
+ console.error(`Invalid embed mode: ${options.embedMode} (use cpu or gpu)`);
52
+ yield (0, exit_1.gracefulExit)(1);
53
+ return;
54
+ }
55
+ if (options.modelTier && !config_1.MODEL_TIERS[options.modelTier]) {
56
+ console.error(`Invalid model tier: ${options.modelTier} (use ${Object.keys(config_1.MODEL_TIERS).join(" or ")})`);
57
+ yield (0, exit_1.gracefulExit)(1);
58
+ return;
59
+ }
60
+ const newTier = (_b = options.modelTier) !== null && _b !== void 0 ? _b : globalConfig.modelTier;
61
+ const newMode = (_c = options.embedMode) !== null && _c !== void 0 ? _c : globalConfig.embedMode;
62
+ const tier = (_d = config_1.MODEL_TIERS[newTier]) !== null && _d !== void 0 ? _d : config_1.MODEL_TIERS.small;
63
+ const tierChanged = newTier !== globalConfig.modelTier;
64
+ (0, index_config_1.writeGlobalConfig)({
65
+ modelTier: newTier,
66
+ vectorDim: tier.vectorDim,
67
+ embedMode: newMode,
68
+ mlxModel: newMode === "gpu" ? tier.mlxModel : undefined,
69
+ });
70
+ (0, index_config_1.writeSetupConfig)(paths.configPath, {
71
+ embedMode: newMode,
72
+ mlxModel: newMode === "gpu" ? tier.mlxModel : undefined,
73
+ modelTier: newTier,
74
+ });
75
+ console.log(`Updated: embed-mode=${newMode}, model-tier=${newTier} (${tier.vectorDim}d)`);
76
+ if (tierChanged) {
77
+ console.log("⚠️ Model tier changed — run `gmax index --reset` to rebuild with new dimensions.");
78
+ }
79
+ yield (0, exit_1.gracefulExit)();
80
+ }));
@@ -52,7 +52,7 @@ const index_config_1 = require("../lib/index/index-config");
52
52
  const exit_1 = require("../lib/utils/exit");
53
53
  const project_root_1 = require("../lib/utils/project-root");
54
54
  exports.doctor = new commander_1.Command("doctor")
55
- .description("Check gmax health and paths")
55
+ .description("Check installation health, models, and index status")
56
56
  .action(() => __awaiter(void 0, void 0, void 0, function* () {
57
57
  var _a;
58
58
  console.log("🏥 gmax Doctor\n");
@@ -136,7 +136,7 @@ child.unref();
136
136
  `;
137
137
  const stopScript = `
138
138
  const { spawnSync, execSync } = require("child_process");
139
- try { execSync("pkill -f 'gmax serve'"); } catch {}
139
+ try { execSync("gmax serve stop", { stdio: "ignore" }); } catch {}
140
140
  `;
141
141
  writeFileIfChanged(startJsPath, startScript.trim());
142
142
  writeFileIfChanged(stopJsPath, stopScript.trim());
@@ -60,6 +60,13 @@ exports.index = new commander_1.Command("index")
60
60
  .option("-p, --path <dir>", "Path to index (defaults to current directory)", "")
61
61
  .option("-r, --reset", "Remove existing index and re-index from scratch", false)
62
62
  .option("-v, --verbose", "Show detailed progress with file names", false)
63
+ .addHelpText("after", `
64
+ Examples:
65
+ gmax index Index current directory
66
+ gmax index --path ~/workspace Index a specific directory
67
+ gmax index --dry-run Preview what would be indexed
68
+ gmax index --reset Full re-index from scratch
69
+ `)
63
70
  .action((_args, cmd) => __awaiter(void 0, void 0, void 0, function* () {
64
71
  var _a;
65
72
  const options = cmd.optsWithGlobals();
@@ -239,7 +239,7 @@ function err(text) {
239
239
  // Command
240
240
  // ---------------------------------------------------------------------------
241
241
  exports.mcp = new commander_1.Command("mcp")
242
- .description("Start MCP server for gmax")
242
+ .description("Start MCP server (stdio, auto-started by plugins)")
243
243
  .action((_optsArg, _cmd) => __awaiter(void 0, void 0, void 0, function* () {
244
244
  // --- Lifecycle ---
245
245
  let _vectorDb = null;
@@ -705,6 +705,6 @@ exports.mcp = new commander_1.Command("mcp")
705
705
  }));
706
706
  yield server.connect(transport);
707
707
  // Kick off index readiness check and watcher in background
708
- ensureIndexReady();
708
+ ensureIndexReady().catch((e) => console.error("[MCP] Index readiness check failed:", e));
709
709
  ensureWatcher();
710
710
  }));
@@ -325,7 +325,7 @@ function outputSkeletons(results, projectRoot, limit, db) {
325
325
  });
326
326
  }
327
327
  exports.search = new commander_1.Command("search")
328
- .description("File pattern searcher")
328
+ .description("Search code by meaning (default command)")
329
329
  .option("-m <max_count>, --max-count <max_count>", "The maximum number of results to return (total)", "5")
330
330
  .option("-c, --content", "Show full chunk content instead of snippets", false)
331
331
  .option("--per-file <n>", "Number of matches to show per file", "3")
@@ -336,8 +336,15 @@ exports.search = new commander_1.Command("search")
336
336
  .option("-s, --sync", "Syncs the local files to the store before searching", false)
337
337
  .option("-d, --dry-run", "Show what would be indexed without actually indexing", false)
338
338
  .option("--skeleton", "Show code skeleton for matching files instead of snippets", false)
339
- .argument("<pattern>", "The pattern to search for")
340
- .argument("[path]", "The path to search in")
339
+ .argument("<pattern>", "Natural language query (e.g. \"where do we handle auth?\")")
340
+ .argument("[path]", "Restrict search to this path prefix")
341
+ .addHelpText("after", `
342
+ Examples:
343
+ gmax "where do we handle authentication?"
344
+ gmax "database connection pooling" -m 10
345
+ gmax "error handling" --compact --min-score 0.5
346
+ gmax "validation logic" --skeleton
347
+ `)
341
348
  .action((pattern, exec_path, _options, cmd) => __awaiter(void 0, void 0, void 0, function* () {
342
349
  var _a, _b, _c;
343
350
  const options = cmd.optsWithGlobals();
@@ -100,7 +100,7 @@ function startMlxServer(mlxModel) {
100
100
  return child;
101
101
  }
102
102
  exports.serve = new commander_1.Command("serve")
103
- .description("Run gmax as a background server with live indexing")
103
+ .description("HTTP search server with live file watching")
104
104
  .option("-p, --port <port>", "Port to listen on", process.env.GMAX_PORT || "4444")
105
105
  .option("-b, --background", "Run in background", false)
106
106
  .option("--cpu", "Use CPU-only embeddings (skip MLX GPU server)", false)
@@ -65,7 +65,7 @@ exports.setup = new commander_1.Command("setup")
65
65
  catch (error) {
66
66
  p.cancel("Setup failed");
67
67
  console.error(error);
68
- process.exit(1);
68
+ yield (0, exit_1.gracefulExit)(1);
69
69
  }
70
70
  // Download grammars
71
71
  const grammarSpinner = p.spinner();
@@ -112,6 +112,12 @@ exports.skeleton = new commander_1.Command("skeleton")
112
112
  .option("--json", "Output as JSON", false)
113
113
  .option("--no-summary", "Omit call/complexity summary in bodies", false)
114
114
  .option("-s, --sync", "Sync index before searching", false)
115
+ .addHelpText("after", `
116
+ Examples:
117
+ gmax skeleton src/lib/auth.ts Show file structure
118
+ gmax skeleton AuthService Find symbol, show its file
119
+ gmax skeleton "auth logic" Search, skeletonize top matches
120
+ `)
115
121
  .action((target, options, _cmd) => __awaiter(void 0, void 0, void 0, function* () {
116
122
  var _a, _b;
117
123
  let vectorDb = null;
package/dist/index.js CHANGED
@@ -39,6 +39,7 @@ const path = __importStar(require("node:path"));
39
39
  const commander_1 = require("commander");
40
40
  const claude_code_1 = require("./commands/claude-code");
41
41
  const codex_1 = require("./commands/codex");
42
+ const config_1 = require("./commands/config");
42
43
  const doctor_1 = require("./commands/doctor");
43
44
  const droid_1 = require("./commands/droid");
44
45
  const index_1 = require("./commands/index");
@@ -54,6 +55,8 @@ const symbols_1 = require("./commands/symbols");
54
55
  const trace_1 = require("./commands/trace");
55
56
  const watch_1 = require("./commands/watch");
56
57
  commander_1.program
58
+ .name("gmax")
59
+ .description("Semantic code search — finds code by meaning, not just strings")
57
60
  .version(JSON.parse(fs.readFileSync(path.join(__dirname, "../package.json"), {
58
61
  encoding: "utf-8",
59
62
  })).version)
@@ -67,22 +70,29 @@ if (legacyProjectData) {
67
70
  console.log(" gmax now uses a centralized index at ~/.gmax/lancedb/.");
68
71
  console.log(" Run 'gmax index' to re-index into the centralized store.");
69
72
  }
73
+ // Core commands
70
74
  commander_1.program.addCommand(search_1.search, { isDefault: true });
71
75
  commander_1.program.addCommand(index_1.index);
72
76
  commander_1.program.addCommand(list_1.list);
73
77
  commander_1.program.addCommand(skeleton_1.skeleton);
74
78
  commander_1.program.addCommand(symbols_1.symbols);
75
79
  commander_1.program.addCommand(trace_1.trace);
76
- commander_1.program.addCommand(setup_1.setup);
80
+ // Services
77
81
  commander_1.program.addCommand(serve_1.serve);
78
82
  commander_1.program.addCommand(watch_1.watch);
79
83
  commander_1.program.addCommand(mcp_1.mcp);
84
+ commander_1.program.addCommand(summarize_1.summarize);
85
+ // Setup & diagnostics
86
+ commander_1.program.addCommand(setup_1.setup);
87
+ commander_1.program.addCommand(config_1.config);
88
+ commander_1.program.addCommand(doctor_1.doctor);
89
+ // Plugin installers
80
90
  commander_1.program.addCommand(claude_code_1.installClaudeCode);
81
91
  commander_1.program.addCommand(codex_1.installCodex);
82
92
  commander_1.program.addCommand(droid_1.installDroid);
93
+ droid_1.uninstallDroid._hidden = true;
83
94
  commander_1.program.addCommand(droid_1.uninstallDroid);
84
95
  commander_1.program.addCommand(opencode_1.installOpencode);
96
+ opencode_1.uninstallOpencode._hidden = true;
85
97
  commander_1.program.addCommand(opencode_1.uninstallOpencode);
86
- commander_1.program.addCommand(summarize_1.summarize);
87
- commander_1.program.addCommand(doctor_1.doctor);
88
98
  commander_1.program.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "grepmax",
3
- "version": "0.6.4",
3
+ "version": "0.7.1",
4
4
  "author": "Robert Owens <robowens@me.com>",
5
5
  "homepage": "https://github.com/reowens/grepmax",
6
6
  "bugs": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "grepmax",
3
- "version": "0.6.4",
3
+ "version": "0.7.1",
4
4
  "description": "Semantic code search for Claude Code. Automatically indexes your project and provides intelligent search capabilities.",
5
5
  "author": {
6
6
  "name": "Robert Owens",
@@ -35,10 +35,10 @@ handleAuth [exported ORCH C:8] src/auth/handler.ts:45-90
35
35
  ```
36
36
 
37
37
  Parameters:
38
- - `query` (required): Natural language. More words = better results.
38
+ - `query` (required): Natural language. Be specific — 5+ words gives much better results than 1-2 words.
39
39
  - `limit` (optional): Max results (default 3, max 50)
40
- - `root` (optional): Directory to search. Use `root: "../"` to search a parent directory.
41
- - `path` (optional): Restrict to path prefix (e.g. "src/auth/")
40
+ - `root` (optional): Absolute path to search a different indexed directory.
41
+ - `path` (optional): Restrict to path prefix (e.g. "src/auth/"). Relative to the search root.
42
42
  - `detail` (optional): `"pointer"` (default) or `"code"`
43
43
  - `min_score` (optional): Filter by minimum relevance score (0-1)
44
44
  - `max_per_file` (optional): Cap results per file for diversity
@@ -48,7 +48,9 @@ Parameters:
48
48
  - `code` — comparing implementations, finding duplicates, checking syntax
49
49
 
50
50
  ### search_all
51
- Search ALL indexed code across every directory. Same modes as semantic_search.
51
+ Search ALL indexed code across every directory. Same parameters as semantic_search (query, limit, detail, min_score, max_per_file) but without `root` or `path` — searches everything.
52
+
53
+ Use sparingly. Prefer `semantic_search` when you know which directory to search.
52
54
 
53
55
  ### code_skeleton
54
56
  File structure — signatures with bodies collapsed (~4x fewer tokens).
@@ -60,16 +62,17 @@ Call graph — who calls a symbol and what it calls. Unscoped — follows calls
60
62
 
61
63
  ### list_symbols
62
64
  List indexed symbols with definition locations.
63
- - `pattern` (optional): Filter by name
64
- - `limit` (optional): Max results (default 20)
65
+ - `pattern` (optional): Filter by name (case-insensitive substring match)
66
+ - `limit` (optional): Max results (default 20, max 100)
65
67
  - `path` (optional): Only symbols under this path prefix
66
68
 
67
69
  ### index_status
68
- Check centralized index health — chunks, files, indexed directories, model info.
70
+ Check centralized index health — chunks, files, indexed directories, model info, watcher status.
69
71
 
70
72
  ### summarize_directory
71
- Generate LLM summaries for indexed code in a directory. Summaries are stored and returned in search results. Run after indexing a new directory.
73
+ Generate LLM summaries for indexed code in a directory. Summaries are stored and returned in search results. Requires the summarizer server (auto-started by the plugin hook).
72
74
  - `path` (optional): Directory to summarize. Defaults to project root.
75
+ - `limit` (optional): Max chunks to summarize per call (default 200, max 5000). Run again to continue.
73
76
 
74
77
  ## Workflow
75
78
 
@@ -81,15 +84,22 @@ Generate LLM summaries for indexed code in a directory. Summaries are stored and
81
84
 
82
85
  ## If results seem stale
83
86
 
84
- 1. Check `index_status`if watcher shows "syncing", results may be incomplete. Wait for it.
85
- 2. To force a re-index: `Bash(gmax index)` (indexes current directory)
87
+ The watcher auto-starts when the MCP server connects it detects file changes and re-indexes in the background. Usually results are fresh without manual intervention.
88
+
89
+ 1. Check `index_status` — if watcher shows "syncing", wait for it to finish.
90
+ 2. To force a full re-index: `Bash(gmax index)` (indexes current directory)
86
91
  3. To add summaries without re-indexing: `Bash(gmax summarize)`
87
92
  4. Do NOT use `gmax reindex` — it doesn't exist.
88
93
 
94
+ ## Search warnings
95
+
96
+ If search results include a warning like "Full-text search unavailable", results may be less precise. This resolves automatically — the index retries FTS every 5 minutes.
97
+
89
98
  ## Tips
90
99
 
91
- - More words = better results. "auth" is vague. "where does the server validate JWT tokens" is specific.
92
- - ORCH results contain the logic — prioritize over DEF/IMPL.
93
- - Summaries tell you what the code does without reading it. Use them to decide what to Read.
94
- - Use `root` to search parent directories (monorepo, workspace).
95
- - Use `search_all` sparingly it searches everything indexed.
100
+ - **Be specific.** "auth" returns noise. "where does the server validate JWT tokens from the Authorization header" returns exactly what you need. Aim for 5+ words.
101
+ - **ORCH results contain the logic** — prioritize over DEF/IMPL results.
102
+ - **Summaries tell you what the code does** without reading it. Use them to decide what to `Read`.
103
+ - **Use `root` for cross-project search** absolute path to another indexed directory.
104
+ - **Use `max_per_file`** when results cluster in one file but you need diversity.
105
+ - **Don't search for exact strings** — use grep/Grep for that. gmax finds concepts, not literals.