grepmax 0.5.5 → 0.6.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/dist/commands/index.js +36 -0
- package/dist/commands/mcp.js +6 -23
- package/dist/commands/watch.js +4 -4
- package/dist/lib/index/ignore-patterns.js +21 -19
- package/dist/lib/store/vector-db.js +13 -0
- package/dist/lib/utils/lock.js +1 -1
- package/package.json +1 -1
- package/plugins/grepmax/.claude-plugin/plugin.json +1 -1
- package/plugins/grepmax/hooks/start.js +12 -0
- package/plugins/grepmax/hooks/stop.js +6 -3
package/dist/commands/index.js
CHANGED
|
@@ -43,6 +43,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
43
43
|
};
|
|
44
44
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
45
|
exports.index = void 0;
|
|
46
|
+
const node_child_process_1 = require("node:child_process");
|
|
46
47
|
const path = __importStar(require("node:path"));
|
|
47
48
|
const commander_1 = require("commander");
|
|
48
49
|
const grammar_loader_1 = require("../lib/index/grammar-loader");
|
|
@@ -52,6 +53,7 @@ const setup_helpers_1 = require("../lib/setup/setup-helpers");
|
|
|
52
53
|
const vector_db_1 = require("../lib/store/vector-db");
|
|
53
54
|
const exit_1 = require("../lib/utils/exit");
|
|
54
55
|
const project_root_1 = require("../lib/utils/project-root");
|
|
56
|
+
const watcher_registry_1 = require("../lib/utils/watcher-registry");
|
|
55
57
|
exports.index = new commander_1.Command("index")
|
|
56
58
|
.description("Index the current directory and create searchable store")
|
|
57
59
|
.option("-d, --dry-run", "Dry run the indexing process (no actual file syncing)", false)
|
|
@@ -77,6 +79,27 @@ exports.index = new commander_1.Command("index")
|
|
|
77
79
|
}
|
|
78
80
|
// Ensure grammars are present before indexing (silent if already exist)
|
|
79
81
|
yield (0, grammar_loader_1.ensureGrammars)(console.log, { silent: true });
|
|
82
|
+
// Stop any watcher that covers this project — it holds the shared lock
|
|
83
|
+
const watcher = (0, watcher_registry_1.getWatcherCoveringPath)(projectRoot);
|
|
84
|
+
let restartWatcher = null;
|
|
85
|
+
if (watcher) {
|
|
86
|
+
console.log(`Stopping watcher (PID: ${watcher.pid}) for ${path.basename(watcher.projectRoot)}...`);
|
|
87
|
+
try {
|
|
88
|
+
process.kill(watcher.pid, "SIGTERM");
|
|
89
|
+
}
|
|
90
|
+
catch (_b) { }
|
|
91
|
+
// Wait for process to exit (up to 5s)
|
|
92
|
+
for (let i = 0; i < 50; i++) {
|
|
93
|
+
if (!(0, watcher_registry_1.isProcessRunning)(watcher.pid))
|
|
94
|
+
break;
|
|
95
|
+
yield new Promise((r) => setTimeout(r, 100));
|
|
96
|
+
}
|
|
97
|
+
(0, watcher_registry_1.unregisterWatcher)(watcher.pid);
|
|
98
|
+
restartWatcher = {
|
|
99
|
+
pid: watcher.pid,
|
|
100
|
+
projectRoot: watcher.projectRoot,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
80
103
|
const { spinner, onProgress } = (0, sync_helpers_1.createIndexingSpinner)(projectRoot, "Indexing...", { verbose: options.verbose });
|
|
81
104
|
try {
|
|
82
105
|
const result = yield (0, syncer_1.initialSync)({
|
|
@@ -100,6 +123,19 @@ exports.index = new commander_1.Command("index")
|
|
|
100
123
|
spinner.fail("Indexing failed");
|
|
101
124
|
throw e;
|
|
102
125
|
}
|
|
126
|
+
finally {
|
|
127
|
+
// Restart the watcher if we stopped one
|
|
128
|
+
if (restartWatcher) {
|
|
129
|
+
try {
|
|
130
|
+
const child = (0, node_child_process_1.spawn)(process.argv[0], [process.argv[1], "watch", "--path", restartWatcher.projectRoot], { detached: true, stdio: "ignore" });
|
|
131
|
+
child.unref();
|
|
132
|
+
console.log(`Restarted watcher for ${path.basename(restartWatcher.projectRoot)} (PID: ${child.pid})`);
|
|
133
|
+
}
|
|
134
|
+
catch (_c) {
|
|
135
|
+
console.log(`Note: could not restart watcher. Run: gmax watch --path ${restartWatcher.projectRoot} -b`);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
103
139
|
}
|
|
104
140
|
catch (error) {
|
|
105
141
|
const message = error instanceof Error ? error.message : "Unknown error";
|
package/dist/commands/mcp.js
CHANGED
|
@@ -308,7 +308,7 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
308
308
|
return;
|
|
309
309
|
try {
|
|
310
310
|
const db = getVectorDb();
|
|
311
|
-
const hasIndex = yield db.
|
|
311
|
+
const hasIndex = yield db.hasRowsForPath(projectRoot);
|
|
312
312
|
if (!hasIndex) {
|
|
313
313
|
console.log("[MCP] No index found, running initial sync...");
|
|
314
314
|
yield (0, syncer_1.initialSync)({ projectRoot });
|
|
@@ -318,7 +318,6 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
318
318
|
console.log("[MCP] Index exists, ready.");
|
|
319
319
|
}
|
|
320
320
|
_indexReady = true;
|
|
321
|
-
ensureWatcher();
|
|
322
321
|
}
|
|
323
322
|
catch (e) {
|
|
324
323
|
console.error("[MCP] Index sync failed:", e);
|
|
@@ -326,33 +325,15 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
326
325
|
});
|
|
327
326
|
}
|
|
328
327
|
// --- Background watcher ---
|
|
329
|
-
function findIndexedParent(dir) {
|
|
330
|
-
const resolved = path.resolve(dir);
|
|
331
|
-
const projects = (0, project_registry_1.listProjects)();
|
|
332
|
-
// Find indexed directories that are parents of `dir`, pick broadest
|
|
333
|
-
let broadest;
|
|
334
|
-
for (const p of projects) {
|
|
335
|
-
if (resolved.startsWith(p.root) && p.root !== resolved) {
|
|
336
|
-
if (!broadest || p.root.length < broadest.length) {
|
|
337
|
-
broadest = p.root;
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
return broadest;
|
|
342
|
-
}
|
|
343
328
|
function ensureWatcher() {
|
|
344
|
-
var _a;
|
|
345
329
|
if ((0, watcher_registry_1.getWatcherCoveringPath)(projectRoot))
|
|
346
330
|
return;
|
|
347
|
-
const
|
|
348
|
-
if ((0, watcher_registry_1.getWatcherCoveringPath)(watchRoot))
|
|
349
|
-
return;
|
|
350
|
-
const child = (0, node_child_process_1.spawn)("gmax", ["watch", "-b", "--path", watchRoot], {
|
|
331
|
+
const child = (0, node_child_process_1.spawn)("gmax", ["watch", "-b", "--path", projectRoot], {
|
|
351
332
|
detached: true,
|
|
352
333
|
stdio: "ignore",
|
|
353
334
|
});
|
|
354
335
|
child.unref();
|
|
355
|
-
console.log(`[MCP] Started background watcher for ${
|
|
336
|
+
console.log(`[MCP] Started background watcher for ${projectRoot}`);
|
|
356
337
|
}
|
|
357
338
|
// --- Tool handlers ---
|
|
358
339
|
function handleSemanticSearch(args_1) {
|
|
@@ -362,6 +343,7 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
362
343
|
return err("Missing required parameter: query");
|
|
363
344
|
const limit = Math.min(Math.max(Number(args.limit) || 3, 1), 50);
|
|
364
345
|
yield ensureIndexReady();
|
|
346
|
+
ensureWatcher();
|
|
365
347
|
try {
|
|
366
348
|
const searcher = getSearcher();
|
|
367
349
|
// Determine path prefix and display root for relative paths
|
|
@@ -714,6 +696,7 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
714
696
|
}
|
|
715
697
|
}));
|
|
716
698
|
yield server.connect(transport);
|
|
717
|
-
// Kick off index readiness check in background
|
|
699
|
+
// Kick off index readiness check and watcher in background
|
|
718
700
|
ensureIndexReady();
|
|
701
|
+
ensureWatcher();
|
|
719
702
|
}));
|
package/dist/commands/watch.js
CHANGED
|
@@ -63,15 +63,15 @@ exports.watch = new commander_1.Command("watch")
|
|
|
63
63
|
.option("-p, --path <dir>", "Directory to watch (defaults to project root)")
|
|
64
64
|
.option("--no-idle-timeout", "Disable the 30-minute idle shutdown")
|
|
65
65
|
.action((options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
66
|
-
var _a;
|
|
66
|
+
var _a, _b;
|
|
67
67
|
const projectRoot = options.path
|
|
68
68
|
? path.resolve(options.path)
|
|
69
69
|
: (_a = (0, project_root_1.findProjectRoot)(process.cwd())) !== null && _a !== void 0 ? _a : process.cwd();
|
|
70
70
|
const projectName = path.basename(projectRoot);
|
|
71
|
-
// Check if watcher already running
|
|
72
|
-
const existing = (0, watcher_registry_1.getWatcherForProject)(projectRoot);
|
|
71
|
+
// Check if watcher already running (exact match or parent covering this dir)
|
|
72
|
+
const existing = (_b = (0, watcher_registry_1.getWatcherForProject)(projectRoot)) !== null && _b !== void 0 ? _b : (0, watcher_registry_1.getWatcherCoveringPath)(projectRoot);
|
|
73
73
|
if (existing && (0, watcher_registry_1.isProcessRunning)(existing.pid)) {
|
|
74
|
-
console.log(`Watcher already running for ${
|
|
74
|
+
console.log(`Watcher already running for ${path.basename(existing.projectRoot)} (PID: ${existing.pid})`);
|
|
75
75
|
return;
|
|
76
76
|
}
|
|
77
77
|
// Background spawn
|
|
@@ -14,25 +14,27 @@ exports.DEFAULT_IGNORE_PATTERNS = [
|
|
|
14
14
|
"*.log",
|
|
15
15
|
"*.csv",
|
|
16
16
|
// Safety nets for nested non-git folders
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
"
|
|
17
|
+
// Use bare names so the `ignore` library matches the directory itself
|
|
18
|
+
// (prevents descending into it), not just files inside it.
|
|
19
|
+
"node_modules",
|
|
20
|
+
"dist",
|
|
21
|
+
"build",
|
|
22
|
+
"out",
|
|
23
|
+
"target",
|
|
24
|
+
"__pycache__",
|
|
25
|
+
"coverage",
|
|
26
|
+
"venv",
|
|
27
|
+
".venv",
|
|
28
|
+
".tox",
|
|
29
|
+
".mypy_cache",
|
|
30
|
+
".pytest_cache",
|
|
31
|
+
".next",
|
|
32
|
+
".nuxt",
|
|
33
|
+
".gradle",
|
|
34
|
+
".m2",
|
|
35
|
+
"vendor",
|
|
36
|
+
".osgrep",
|
|
37
|
+
".gmax",
|
|
36
38
|
// Minified/generated assets
|
|
37
39
|
"*.min.js",
|
|
38
40
|
"*.min.css",
|
|
@@ -285,6 +285,19 @@ class VectorDB {
|
|
|
285
285
|
return rows.length > 0;
|
|
286
286
|
});
|
|
287
287
|
}
|
|
288
|
+
hasRowsForPath(pathPrefix) {
|
|
289
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
290
|
+
const table = yield this.ensureTable();
|
|
291
|
+
const prefix = pathPrefix.endsWith("/") ? pathPrefix : `${pathPrefix}/`;
|
|
292
|
+
const rows = yield table
|
|
293
|
+
.query()
|
|
294
|
+
.select(["id"])
|
|
295
|
+
.where(`path LIKE '${prefix.replace(/'/g, "''")}%'`)
|
|
296
|
+
.limit(1)
|
|
297
|
+
.toArray();
|
|
298
|
+
return rows.length > 0;
|
|
299
|
+
});
|
|
300
|
+
}
|
|
288
301
|
getStats() {
|
|
289
302
|
return __awaiter(this, void 0, void 0, function* () {
|
|
290
303
|
const table = yield this.ensureTable();
|
package/dist/lib/utils/lock.js
CHANGED
|
@@ -108,7 +108,7 @@ function acquireWriterLock(lockDir) {
|
|
|
108
108
|
const holderDesc = pid
|
|
109
109
|
? `${pid}${startedAt ? ` @ ${startedAt}` : ""}`
|
|
110
110
|
: "unknown";
|
|
111
|
-
throw new Error(`.gmax lock already held (${holderDesc}). Another
|
|
111
|
+
throw new Error(`.gmax lock already held (${holderDesc}). Another process is using the index. Try: gmax watch stop --all`);
|
|
112
112
|
}
|
|
113
113
|
}
|
|
114
114
|
return {
|
package/package.json
CHANGED
|
@@ -39,6 +39,15 @@ function startPythonServer(scriptName, logName) {
|
|
|
39
39
|
child.unref();
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
function startWatcher() {
|
|
43
|
+
try {
|
|
44
|
+
const { execFileSync } = require("node:child_process");
|
|
45
|
+
execFileSync("gmax", ["watch", "-b"], { timeout: 5000, stdio: "ignore" });
|
|
46
|
+
} catch {
|
|
47
|
+
// Watcher may already be running or gmax not in PATH — ignore
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
42
51
|
async function main() {
|
|
43
52
|
const embedMode =
|
|
44
53
|
process.env.GMAX_EMBED_MODE || process.env.OSGREP_EMBED_MODE || "auto";
|
|
@@ -55,6 +64,9 @@ async function main() {
|
|
|
55
64
|
}
|
|
56
65
|
}
|
|
57
66
|
|
|
67
|
+
// Start a file watcher for the current project (30-min idle timeout)
|
|
68
|
+
startWatcher();
|
|
69
|
+
|
|
58
70
|
const response = {
|
|
59
71
|
hookSpecificOutput: {
|
|
60
72
|
hookEventName: "SessionStart",
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
try {
|
|
2
|
+
const { execFileSync } = require("node:child_process");
|
|
3
|
+
execFileSync("gmax", ["watch", "stop"], { timeout: 5000, stdio: "ignore" });
|
|
4
|
+
} catch {
|
|
5
|
+
// Watcher may not be running or gmax not in PATH — ignore
|
|
6
|
+
}
|