grepmax 0.7.32 → 0.7.34

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.
@@ -84,260 +84,110 @@ const watcher_registry_1 = require("../lib/utils/watcher-registry");
84
84
  const TOOLS = [
85
85
  {
86
86
  name: "semantic_search",
87
- 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).",
87
+ description: "Search code by meaning. Use scope:'all' for cross-project. Prefer CLI: gmax \"query\" --plain",
88
88
  inputSchema: {
89
89
  type: "object",
90
90
  properties: {
91
- query: {
92
- type: "string",
93
- description: "Natural language search query. Be specific more words give better results.",
94
- },
95
- limit: {
96
- type: "number",
97
- description: "Max results to return (default 3, max 50)",
98
- },
99
- root: {
100
- type: "string",
101
- 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.",
102
- },
103
- path: {
104
- type: "string",
105
- description: "Restrict search to files under this path prefix (e.g. 'src/auth/'). Relative to the search root.",
106
- },
107
- detail: {
108
- type: "string",
109
- description: "Output detail: 'pointer' (default, metadata only), 'code' (4-line snippets), or 'full' (complete chunk content with line numbers)",
110
- },
111
- min_score: {
112
- type: "number",
113
- description: "Minimum relevance score (0-1). Results below this threshold are filtered out. Default: 0 (no filtering)",
114
- },
115
- max_per_file: {
116
- type: "number",
117
- description: "Max results per file (default: no cap). Useful to get diversity across files.",
118
- },
119
- file: {
120
- type: "string",
121
- description: "Filter to files matching this name (e.g. 'syncer.ts'). Matches the filename, not the full path.",
122
- },
123
- exclude: {
124
- type: "string",
125
- description: "Exclude files under this path prefix (e.g. 'tests/' or 'dist/').",
126
- },
127
- language: {
128
- type: "string",
129
- description: "Filter by file extension (e.g. 'ts', 'py', 'go'). Omit the dot.",
130
- },
131
- role: {
132
- type: "string",
133
- description: "Filter by chunk role: 'ORCHESTRATION' (logic/flow), 'DEFINITION' (types/classes), or 'IMPLEMENTATION'.",
134
- },
135
- context_lines: {
136
- type: "number",
137
- description: "Include N lines before and after the chunk (like grep -C). Only with detail 'code' or 'full'. Max 20.",
138
- },
139
- mode: {
140
- type: "string",
141
- description: "Search mode: 'default' (semantic only) or 'symbol' (semantic + call graph trace appended). Use 'symbol' when query is a function/class name.",
142
- },
143
- include_imports: {
144
- type: "boolean",
145
- description: "Prepend the file's import/require statements to each result. Deduped per file.",
146
- },
147
- name_pattern: {
148
- type: "string",
149
- description: "Regex to filter by symbol name (e.g. 'handle.*Auth'). Case-insensitive. Applied after search.",
150
- },
151
- },
152
- required: ["query"],
153
- },
154
- },
155
- {
156
- name: "search_all",
157
- 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.",
158
- inputSchema: {
159
- type: "object",
160
- properties: {
161
- query: {
162
- type: "string",
163
- description: "Natural language search query.",
164
- },
165
- limit: {
166
- type: "number",
167
- description: "Max results to return (default 3, max 50)",
168
- },
169
- detail: {
170
- type: "string",
171
- description: "Output detail: 'pointer' (default), 'code' (snippets), or 'full' (complete content)",
172
- },
173
- min_score: {
174
- type: "number",
175
- description: "Minimum relevance score (0-1). Default: 0",
176
- },
177
- max_per_file: {
178
- type: "number",
179
- description: "Max results per file (default: no cap).",
180
- },
181
- file: {
182
- type: "string",
183
- description: "Filter to files matching this name (e.g. 'syncer.ts').",
184
- },
185
- exclude: {
186
- type: "string",
187
- description: "Exclude files under this path prefix (e.g. 'tests/').",
188
- },
189
- language: {
190
- type: "string",
191
- description: "Filter by file extension (e.g. 'ts', 'py').",
192
- },
193
- role: {
194
- type: "string",
195
- description: "Filter by role: 'ORCHESTRATION', 'DEFINITION', or 'IMPLEMENTATION'.",
196
- },
197
- projects: {
198
- type: "string",
199
- description: "Comma-separated project names to include (e.g. 'platform,osgrep'). Use index_status to see names.",
200
- },
201
- exclude_projects: {
202
- type: "string",
203
- description: "Comma-separated project names to exclude (e.g. 'capstone,power').",
204
- },
205
- context_lines: {
206
- type: "number",
207
- description: "Include N lines before/after chunk. Only with detail 'code' or 'full'. Max 20.",
208
- },
209
- include_imports: {
210
- type: "boolean",
211
- description: "Prepend file's import statements to each result.",
212
- },
213
- name_pattern: {
214
- type: "string",
215
- description: "Regex to filter by symbol name (e.g. 'handle.*Auth').",
216
- },
91
+ query: { type: "string", description: "Natural language query (5+ words recommended)" },
92
+ limit: { type: "number", description: "Max results (default 3, max 50)" },
93
+ root: { type: "string", description: "Search a different directory (absolute path)" },
94
+ path: { type: "string", description: "Path prefix filter (e.g. 'src/auth/')" },
95
+ detail: { type: "string", description: "'pointer' (default), 'code', or 'full'" },
96
+ min_score: { type: "number", description: "Min score 0-1 (default 0)" },
97
+ max_per_file: { type: "number", description: "Max results per file" },
98
+ file: { type: "string", description: "Filename filter (e.g. 'syncer.ts')" },
99
+ exclude: { type: "string", description: "Exclude path prefix (e.g. 'tests/')" },
100
+ language: { type: "string", description: "Extension filter (e.g. 'ts', 'py')" },
101
+ role: { type: "string", description: "'ORCHESTRATION', 'DEFINITION', or 'IMPLEMENTATION'" },
102
+ context_lines: { type: "number", description: "Lines before/after chunk (max 20)" },
103
+ mode: { type: "string", description: "'default' or 'symbol' (appends call graph)" },
104
+ include_imports: { type: "boolean", description: "Prepend file imports to results" },
105
+ name_pattern: { type: "string", description: "Regex filter on symbol name" },
106
+ scope: { type: "string", description: "'project' (default) or 'all' (search everything)" },
107
+ projects: { type: "string", description: "Project names to include (comma-separated)" },
108
+ exclude_projects: { type: "string", description: "Project names to exclude (comma-separated)" },
217
109
  },
218
110
  required: ["query"],
219
111
  },
220
112
  },
221
113
  {
222
114
  name: "code_skeleton",
223
- description: "Show the structure of source files — signatures with bodies collapsed (~4x fewer tokens). Accepts a file path, a directory path, or comma-separated file paths.",
115
+ description: "File structure with bodies collapsed (~4x fewer tokens). Accepts file, directory, or comma-separated paths.",
224
116
  inputSchema: {
225
117
  type: "object",
226
118
  properties: {
227
- target: {
228
- type: "string",
229
- description: "File path, directory path (e.g. 'src/lib/search/'), or comma-separated files (e.g. 'src/a.ts,src/b.ts'). Relative to project root.",
230
- },
231
- limit: {
232
- type: "number",
233
- description: "Max files for directory mode (default 10, max 20). Ignored for single files.",
234
- },
235
- format: {
236
- type: "string",
237
- description: "Output format: 'text' (default) or 'json' (structured symbol list with names, lines, signatures).",
238
- },
119
+ target: { type: "string", description: "File, directory, or comma-separated paths" },
120
+ limit: { type: "number", description: "Max files for directory mode (default 10)" },
121
+ format: { type: "string", description: "'text' (default) or 'json'" },
239
122
  },
240
123
  required: ["target"],
241
124
  },
242
125
  },
243
126
  {
244
127
  name: "trace_calls",
245
- 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.",
128
+ description: "Call graph: importers, callers (multi-hop), callees with file:line.",
246
129
  inputSchema: {
247
130
  type: "object",
248
131
  properties: {
249
- symbol: {
250
- type: "string",
251
- description: "The function, method, or class name to trace (e.g. 'handleAuth')",
252
- },
253
- depth: {
254
- type: "number",
255
- description: "Traversal depth for callers (default 1, max 3). depth: 2 shows callers-of-callers.",
256
- },
132
+ symbol: { type: "string", description: "Function/class name to trace" },
133
+ depth: { type: "number", description: "Caller depth (default 1, max 3)" },
257
134
  },
258
135
  required: ["symbol"],
259
136
  },
260
137
  },
261
138
  {
262
139
  name: "list_symbols",
263
- description: "List indexed symbols (functions, classes, types) with their definition locations. Useful for finding where things are defined without knowing exact names.",
140
+ description: "List indexed symbols with role and export status.",
264
141
  inputSchema: {
265
142
  type: "object",
266
143
  properties: {
267
- pattern: {
268
- type: "string",
269
- description: "Filter symbols by name (case-insensitive substring match)",
270
- },
271
- limit: {
272
- type: "number",
273
- description: "Max symbols to return (default 20, max 100)",
274
- },
275
- path: {
276
- type: "string",
277
- description: "Only include symbols defined under this path prefix",
278
- },
144
+ pattern: { type: "string", description: "Name filter (case-insensitive)" },
145
+ limit: { type: "number", description: "Max results (default 20)" },
146
+ path: { type: "string", description: "Path prefix filter" },
279
147
  },
280
148
  },
281
149
  },
282
150
  {
283
151
  name: "index_status",
284
- description: "Check the status of the gmax index. Returns indexed directories, chunk counts, embed mode, index age, and watcher status.",
285
- inputSchema: {
286
- type: "object",
287
- properties: {},
288
- },
152
+ description: "Index health: chunks, files, projects, watcher status.",
153
+ inputSchema: { type: "object", properties: {} },
289
154
  },
290
155
  {
291
156
  name: "summarize_directory",
292
- description: "Generate LLM summaries for indexed code in a directory. Run after indexing. Summaries are stored and returned in search results. Requires the summarizer server on port 8101.",
157
+ description: "Generate LLM summaries for indexed chunks.",
293
158
  inputSchema: {
294
159
  type: "object",
295
160
  properties: {
296
- path: {
297
- type: "string",
298
- description: "Directory to summarize (absolute or relative). Defaults to current project root.",
299
- },
300
- limit: {
301
- type: "number",
302
- description: "Max chunks to summarize per call (default 200, max 5000). Run again to continue.",
303
- },
161
+ path: { type: "string", description: "Directory to summarize (default: project root)" },
162
+ limit: { type: "number", description: "Max chunks (default 200, max 5000)" },
304
163
  },
305
164
  },
306
165
  },
307
166
  {
308
167
  name: "summarize_project",
309
- description: "High-level overview of an indexed project — languages, directory structure, role distribution, key symbols, and entry points. Use when first exploring a codebase.",
168
+ description: "Project overview: languages, structure, roles, key symbols, entry points.",
310
169
  inputSchema: {
311
170
  type: "object",
312
171
  properties: {
313
- root: {
314
- type: "string",
315
- description: "Project root (absolute path). Defaults to current project.",
316
- },
172
+ root: { type: "string", description: "Project root (default: current)" },
317
173
  },
318
174
  },
319
175
  },
320
176
  {
321
177
  name: "related_files",
322
- description: "Find files related to a given file by shared symbol references. Shows dependencies (what this file calls) and dependents (what calls this file).",
178
+ description: "Find dependencies and dependents of a file by shared symbols.",
323
179
  inputSchema: {
324
180
  type: "object",
325
181
  properties: {
326
- file: {
327
- type: "string",
328
- description: "File path relative to project root (e.g. 'src/lib/index/syncer.ts')",
329
- },
330
- limit: {
331
- type: "number",
332
- description: "Max related files per direction (default 10)",
333
- },
182
+ file: { type: "string", description: "File path relative to project root" },
183
+ limit: { type: "number", description: "Max results per direction (default 10)" },
334
184
  },
335
185
  required: ["file"],
336
186
  },
337
187
  },
338
188
  {
339
189
  name: "recent_changes",
340
- description: "Show recently modified files in the index. Useful after pulls or merges to see what changed.",
190
+ description: "Recently modified indexed files with timestamps.",
341
191
  inputSchema: {
342
192
  type: "object",
343
193
  properties: {
@@ -513,11 +363,12 @@ exports.mcp = new commander_1.Command("mcp")
513
363
  }
514
364
  // --- Tool handlers ---
515
365
  function handleSemanticSearch(args_1) {
516
- return __awaiter(this, arguments, void 0, function* (args, searchAll = false) {
366
+ return __awaiter(this, arguments, void 0, function* (args, isSearchAll = false) {
517
367
  var _a;
518
368
  const query = String(args.query || "");
519
369
  if (!query)
520
370
  return err("Missing required parameter: query");
371
+ const searchAll = isSearchAll || args.scope === "all";
521
372
  const limit = Math.min(Math.max(Number(args.limit) || 3, 1), 50);
522
373
  ensureWatcher();
523
374
  if (_indexing) {
@@ -380,6 +380,7 @@ exports.serve = new commander_1.Command("serve")
380
380
  // Ensure we exit if server fails to start
381
381
  process.exit(1);
382
382
  });
383
+ server.setTimeout(60000); // 60s request timeout
383
384
  server.listen(port, () => {
384
385
  const address = server.address();
385
386
  const actualPort = typeof address === "object" && address ? address.port : port;
@@ -105,7 +105,12 @@ function startWatcher(opts) {
105
105
  clearTimeout(debounceTimer);
106
106
  debounceTimer = setTimeout(() => processBatch(), DEBOUNCE_MS);
107
107
  };
108
- const BATCH_TIMEOUT_MS = 120000;
108
+ const taskTimeoutMs = (() => {
109
+ var _a;
110
+ const fromEnv = Number.parseInt((_a = process.env.GMAX_WORKER_TASK_TIMEOUT_MS) !== null && _a !== void 0 ? _a : "", 10);
111
+ return Number.isFinite(fromEnv) && fromEnv > 0 ? fromEnv : 120000;
112
+ })();
113
+ const BATCH_TIMEOUT_MS = Math.max(Math.ceil(taskTimeoutMs * 1.5), 120000);
109
114
  const processBatch = () => __awaiter(this, void 0, void 0, function* () {
110
115
  var _a;
111
116
  if (closed || processing || pending.size === 0)
@@ -95,13 +95,9 @@ function postJSON(path, body) {
95
95
  /**
96
96
  * Check if MLX server is reachable. Caches result for CHECK_INTERVAL_MS.
97
97
  */
98
- function isMlxUp() {
98
+ function checkHealth() {
99
99
  return __awaiter(this, void 0, void 0, function* () {
100
- const now = Date.now();
101
- if (mlxAvailable !== null && now - lastCheck < CHECK_INTERVAL_MS) {
102
- return mlxAvailable;
103
- }
104
- const result = yield new Promise((resolve) => {
100
+ return new Promise((resolve) => {
105
101
  const req = http.get({ hostname: MLX_HOST, port: MLX_PORT, path: "/health", timeout: 2000 }, (res) => {
106
102
  res.resume();
107
103
  resolve(res.statusCode === 200);
@@ -112,6 +108,20 @@ function isMlxUp() {
112
108
  resolve(false);
113
109
  });
114
110
  });
111
+ });
112
+ }
113
+ function isMlxUp() {
114
+ return __awaiter(this, void 0, void 0, function* () {
115
+ const now = Date.now();
116
+ if (mlxAvailable !== null && now - lastCheck < CHECK_INTERVAL_MS) {
117
+ return mlxAvailable;
118
+ }
119
+ let result = yield checkHealth();
120
+ // On first check (cold start), retry once after 3s — server may still be loading
121
+ if (!result && mlxAvailable === null) {
122
+ yield new Promise((r) => setTimeout(r, 3000));
123
+ result = yield checkHealth();
124
+ }
115
125
  mlxAvailable = result;
116
126
  lastCheck = now;
117
127
  return result;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "grepmax",
3
- "version": "0.7.32",
3
+ "version": "0.7.34",
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.7.32",
3
+ "version": "0.7.34",
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",
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: grepmax
3
3
  description: Semantic code search. Use alongside grep - grep for exact strings, gmax for concepts.
4
- allowed-tools: "mcp__grepmax__semantic_search, mcp__grepmax__search_all, mcp__grepmax__code_skeleton, mcp__grepmax__trace_calls, mcp__grepmax__list_symbols, mcp__grepmax__index_status, mcp__grepmax__summarize_directory, mcp__grepmax__summarize_project, mcp__grepmax__related_files, mcp__grepmax__recent_changes, Bash(gmax:*), Read"
4
+ allowed-tools: "mcp__grepmax__semantic_search, mcp__grepmax__code_skeleton, mcp__grepmax__trace_calls, mcp__grepmax__list_symbols, mcp__grepmax__index_status, mcp__grepmax__summarize_directory, mcp__grepmax__summarize_project, mcp__grepmax__related_files, mcp__grepmax__recent_changes, Bash(gmax:*), Read"
5
5
  ---
6
6
 
7
7
  ## What gmax does
@@ -85,14 +85,9 @@ gmax doctor # health check
85
85
  6. **Context** — `Bash(gmax related <file>)` to see what else to look at
86
86
  7. **Changes** — `Bash(gmax recent)` after pulls
87
87
 
88
- ## MCP tools (only when CLI isn't suitable)
88
+ ## MCP tools
89
89
 
90
- MCP tools are available but consume more tokens. Use them only for:
91
- - `index_status` — quick health check (no CLI equivalent that's cheaper)
92
- - `summarize_directory` — LLM summary generation
93
- - `semantic_search` with `detail: "pointer"` — when you need the structured pointer format
94
-
95
- Full MCP tool documentation: semantic_search (16 params), search_all, code_skeleton, trace_calls, list_symbols, index_status, summarize_project, related_files, recent_changes, summarize_directory.
90
+ Use MCP only for `index_status` and `summarize_directory`. Use CLI for everything else. For cross-project search, use `scope: "all"` on semantic_search (replaces search_all).
96
91
 
97
92
  ## Tips
98
93