grepmax 0.7.23 → 0.7.25
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/dist/commands/mcp.js
CHANGED
|
@@ -463,54 +463,37 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
463
463
|
_indexing = false;
|
|
464
464
|
_indexProgress = "";
|
|
465
465
|
_indexChildPid = null;
|
|
466
|
-
// Re-check if index now exists
|
|
467
|
-
try {
|
|
468
|
-
const db = getVectorDb();
|
|
469
|
-
if (yield db.hasRowsForPath(projectRoot)) {
|
|
470
|
-
_indexReady = true;
|
|
471
|
-
console.log("[MCP] Background indexing complete.");
|
|
472
|
-
return;
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
catch (_b) { }
|
|
476
466
|
}
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
console.error(`[MCP] Background indexing failed (exit code: ${code})`);
|
|
503
|
-
}
|
|
504
|
-
});
|
|
467
|
+
// Check project registry — more reliable than querying the DB.
|
|
468
|
+
// Avoids false negatives from lock contention and cascade re-indexing.
|
|
469
|
+
const projects = (0, project_registry_1.listProjects)();
|
|
470
|
+
const isRegistered = projects.some((p) => p.root === projectRoot);
|
|
471
|
+
if (isRegistered) {
|
|
472
|
+
_indexReady = true;
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
// Truly first-time: no registry entry at all
|
|
476
|
+
if (_indexing)
|
|
477
|
+
return;
|
|
478
|
+
_indexing = true;
|
|
479
|
+
_indexProgress = "starting...";
|
|
480
|
+
console.log("[MCP] First-time index for this project...");
|
|
481
|
+
const child = (0, node_child_process_1.spawn)(process.argv[0], [process.argv[1], "index", "--path", projectRoot], { detached: true, stdio: "ignore" });
|
|
482
|
+
_indexChildPid = (_a = child.pid) !== null && _a !== void 0 ? _a : null;
|
|
483
|
+
child.unref();
|
|
484
|
+
_indexProgress = `PID ${_indexChildPid}`;
|
|
485
|
+
child.on("exit", (code) => {
|
|
486
|
+
_indexing = false;
|
|
487
|
+
_indexProgress = "";
|
|
488
|
+
_indexChildPid = null;
|
|
489
|
+
if (code === 0) {
|
|
490
|
+
_indexReady = true;
|
|
491
|
+
console.log("[MCP] First-time indexing complete.");
|
|
505
492
|
}
|
|
506
493
|
else {
|
|
507
|
-
console.
|
|
508
|
-
_indexReady = true;
|
|
494
|
+
console.error(`[MCP] Indexing failed (exit code: ${code})`);
|
|
509
495
|
}
|
|
510
|
-
}
|
|
511
|
-
catch (e) {
|
|
512
|
-
console.error("[MCP] Index sync failed:", e);
|
|
513
|
-
}
|
|
496
|
+
});
|
|
514
497
|
});
|
|
515
498
|
}
|
|
516
499
|
// --- Background watcher ---
|
|
@@ -575,7 +558,6 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
575
558
|
if (!query)
|
|
576
559
|
return err("Missing required parameter: query");
|
|
577
560
|
const limit = Math.min(Math.max(Number(args.limit) || 3, 1), 50);
|
|
578
|
-
yield ensureIndexReady();
|
|
579
561
|
ensureWatcher();
|
|
580
562
|
if (_indexing) {
|
|
581
563
|
return ok(`Indexing in progress (${_indexProgress}). Results may be incomplete or empty — try again shortly.`);
|
package/dist/commands/search.js
CHANGED
|
@@ -357,7 +357,7 @@ Examples:
|
|
|
357
357
|
gmax "handler" --name "handle.*" --exclude tests/
|
|
358
358
|
`)
|
|
359
359
|
.action((pattern, exec_path, _options, cmd) => __awaiter(void 0, void 0, void 0, function* () {
|
|
360
|
-
var _a, _b, _c, _d;
|
|
360
|
+
var _a, _b, _c, _d, _e;
|
|
361
361
|
const options = cmd.optsWithGlobals();
|
|
362
362
|
const root = process.cwd();
|
|
363
363
|
const minScore = Number.isFinite(Number.parseFloat(options.minScore))
|
|
@@ -497,10 +497,23 @@ Examples:
|
|
|
497
497
|
throw e;
|
|
498
498
|
}
|
|
499
499
|
}
|
|
500
|
+
// Ensure a watcher is running for live reindexing
|
|
501
|
+
if (!process.env.VITEST && !((_c = process.env.NODE_ENV) === null || _c === void 0 ? void 0 : _c.includes("test"))) {
|
|
502
|
+
try {
|
|
503
|
+
const { execFileSync } = yield Promise.resolve().then(() => __importStar(require("node:child_process")));
|
|
504
|
+
execFileSync("gmax", ["watch", "-b", "--path", projectRoot], {
|
|
505
|
+
timeout: 5000,
|
|
506
|
+
stdio: "ignore",
|
|
507
|
+
});
|
|
508
|
+
}
|
|
509
|
+
catch (_f) {
|
|
510
|
+
// Watcher may already be running — ignore
|
|
511
|
+
}
|
|
512
|
+
}
|
|
500
513
|
const searcher = new searcher_1.Searcher(vectorDb);
|
|
501
514
|
// Use --root or fall back to project root
|
|
502
515
|
const effectiveRoot = options.root
|
|
503
|
-
? (
|
|
516
|
+
? (_d = (0, project_root_1.findProjectRoot)(path.resolve(options.root))) !== null && _d !== void 0 ? _d : path.resolve(options.root)
|
|
504
517
|
: projectRoot;
|
|
505
518
|
const searchPathPrefix = exec_path
|
|
506
519
|
? path.resolve(exec_path)
|
|
@@ -519,7 +532,7 @@ Examples:
|
|
|
519
532
|
if (options.role)
|
|
520
533
|
searchFilters.role = options.role;
|
|
521
534
|
const searchResult = yield searcher.search(pattern, parseInt(options.m, 10), { rerank: true }, Object.keys(searchFilters).length > 0 ? searchFilters : undefined, pathFilter);
|
|
522
|
-
if ((
|
|
535
|
+
if ((_e = searchResult.warnings) === null || _e === void 0 ? void 0 : _e.length) {
|
|
523
536
|
for (const w of searchResult.warnings) {
|
|
524
537
|
console.warn(`Warning: ${w}`);
|
|
525
538
|
}
|
|
@@ -536,7 +549,7 @@ Examples:
|
|
|
536
549
|
return defs.some((d) => regex.test(d));
|
|
537
550
|
});
|
|
538
551
|
}
|
|
539
|
-
catch (
|
|
552
|
+
catch (_g) {
|
|
540
553
|
// Invalid regex — skip
|
|
541
554
|
}
|
|
542
555
|
}
|
|
@@ -621,7 +634,7 @@ Examples:
|
|
|
621
634
|
console.log(lines.join("\n"));
|
|
622
635
|
}
|
|
623
636
|
}
|
|
624
|
-
catch (
|
|
637
|
+
catch (_h) {
|
|
625
638
|
// Trace failed — skip silently
|
|
626
639
|
}
|
|
627
640
|
}
|
|
@@ -48,6 +48,7 @@ const fs = __importStar(require("node:fs"));
|
|
|
48
48
|
const path = __importStar(require("node:path"));
|
|
49
49
|
const chokidar_1 = require("chokidar");
|
|
50
50
|
const filter_builder_1 = require("../utils/filter-builder");
|
|
51
|
+
const config_1 = require("../../config");
|
|
51
52
|
const file_utils_1 = require("../utils/file-utils");
|
|
52
53
|
const logger_1 = require("../utils/logger");
|
|
53
54
|
const lock_1 = require("../utils/lock");
|
|
@@ -135,8 +136,13 @@ function startWatcher(opts) {
|
|
|
135
136
|
const stats = yield fs.promises.stat(absPath);
|
|
136
137
|
if (!(0, file_utils_1.isIndexableFile)(absPath, stats.size))
|
|
137
138
|
continue;
|
|
138
|
-
//
|
|
139
|
+
// Quick mtime/size check — skip worker pool if unchanged
|
|
139
140
|
const cached = metaCache.get(absPath);
|
|
141
|
+
if (cached &&
|
|
142
|
+
cached.mtimeMs === stats.mtimeMs &&
|
|
143
|
+
cached.size === stats.size) {
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
140
146
|
const result = yield pool.processFile({
|
|
141
147
|
path: absPath,
|
|
142
148
|
absolutePath: absPath,
|
|
@@ -285,8 +291,12 @@ function startWatcher(opts) {
|
|
|
285
291
|
const onFileEvent = (event, absPath) => {
|
|
286
292
|
if (closed)
|
|
287
293
|
return;
|
|
288
|
-
if (event !== "unlink"
|
|
289
|
-
|
|
294
|
+
if (event !== "unlink") {
|
|
295
|
+
const ext = path.extname(absPath).toLowerCase();
|
|
296
|
+
const bn = path.basename(absPath).toLowerCase();
|
|
297
|
+
if (!config_1.INDEXABLE_EXTENSIONS.has(ext) && !config_1.INDEXABLE_EXTENSIONS.has(bn))
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
290
300
|
pending.set(absPath, event);
|
|
291
301
|
scheduleBatch();
|
|
292
302
|
};
|
package/package.json
CHANGED
|
@@ -130,11 +130,31 @@ The watcher auto-starts when the MCP server connects — it detects file changes
|
|
|
130
130
|
|
|
131
131
|
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.
|
|
132
132
|
|
|
133
|
+
## CLI vs MCP — when to use which
|
|
134
|
+
|
|
135
|
+
**Prefer CLI (`Bash(gmax ...)`) for repeated searches.** The CLI is ~2x more token-efficient because MCP tool schemas add ~800 tokens of overhead per call. Every CLI flag maps to an MCP param:
|
|
136
|
+
|
|
137
|
+
```
|
|
138
|
+
Bash(gmax "auth handler" --role ORCHESTRATION --lang ts --plain -m 3)
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
is equivalent to `semantic_search` with `role: "ORCHESTRATION", language: "ts", limit: 3` — but costs half the tokens.
|
|
142
|
+
|
|
143
|
+
**CLI commands for all MCP tools:**
|
|
144
|
+
- `gmax "query" --plain` → `semantic_search`
|
|
145
|
+
- `gmax trace <symbol> -d 2` → `trace_calls` with depth
|
|
146
|
+
- `gmax skeleton <target> --json` → `code_skeleton`
|
|
147
|
+
- `gmax project` → `summarize_project`
|
|
148
|
+
- `gmax related <file>` → `related_files`
|
|
149
|
+
- `gmax recent` → `recent_changes`
|
|
150
|
+
|
|
151
|
+
**Use MCP tools when:** first exploring (tool descriptions guide usage), or when you need pointer mode output (more structured than CLI).
|
|
152
|
+
|
|
133
153
|
## Tips
|
|
134
154
|
|
|
135
155
|
- **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.
|
|
136
|
-
- **
|
|
156
|
+
- **Use `--plain` for CLI searches** — agent-friendly output without ANSI codes.
|
|
157
|
+
- **ORCH results contain the logic** — use `--role ORCHESTRATION` to filter noise.
|
|
137
158
|
- **Summaries tell you what the code does** without reading it. Use them to decide what to `Read`.
|
|
138
|
-
- **Use `
|
|
139
|
-
- **Use `max_per_file`** when results cluster in one file but you need diversity.
|
|
159
|
+
- **Use `--symbol` on CLI** to get search results + call graph in one shot.
|
|
140
160
|
- **Don't search for exact strings** — use grep/Grep for that. gmax finds concepts, not literals.
|