grepmax 0.26.50 → 0.26.52
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/.claude-plugin/marketplace.json +1 -1
- package/README.md +4 -2
- package/dist/commands/claude-code.js +64 -32
- package/dist/commands/codex.js +26 -8
- package/dist/commands/plugin.js +2 -1
- package/dist/commands/watch.js +8 -0
- package/dist/lib/utils/autostart.js +1 -1
- package/dist/lib/utils/codex-home.js +44 -0
- package/mlx-embed-server/pyproject.toml +21 -1
- package/mlx-embed-server/uv.lock +288 -217
- package/package.json +5 -3
- package/plugins/grepmax/.claude-plugin/plugin.json +1 -1
- package/plugins/grepmax/hooks/activity.js +12 -0
- package/plugins/grepmax/hooks/cwd-changed.js +2 -15
- package/plugins/grepmax/hooks/start.js +2 -35
- package/plugins/grepmax/hooks/watch-lease.js +53 -7
- package/plugins/grepmax/hooks.json +29 -6
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "grepmax",
|
|
3
|
-
"version": "0.26.
|
|
3
|
+
"version": "0.26.52",
|
|
4
4
|
"author": "Robert Owens <78518764+reowens@users.noreply.github.com>",
|
|
5
5
|
"homepage": "https://github.com/reowens/grepmax",
|
|
6
6
|
"bugs": {
|
|
@@ -41,10 +41,12 @@
|
|
|
41
41
|
"typecheck": "tsc --noEmit",
|
|
42
42
|
"test:typecheck": "tsc -p tsconfig.tests.json",
|
|
43
43
|
"prepublishOnly": "pnpm run test:typecheck && pnpm build",
|
|
44
|
-
"preversion": "pnpm audit
|
|
44
|
+
"preversion": "pnpm audit && pnpm run audit:python && pnpm test && pnpm run test:python && pnpm typecheck && pnpm run test:typecheck && pnpm run format:check && pnpm build && pnpm run audit:consumer",
|
|
45
45
|
"version": "bash scripts/sync-versions.sh && git add -A",
|
|
46
46
|
"postversion": "bash scripts/postrelease.sh",
|
|
47
|
-
"audit:consumer": "node scripts/audit-consumer.cjs"
|
|
47
|
+
"audit:consumer": "node scripts/audit-consumer.cjs",
|
|
48
|
+
"audit:python": "python3 scripts/audit-python.py",
|
|
49
|
+
"test:python": "python3 -m unittest discover -s tests/python"
|
|
48
50
|
},
|
|
49
51
|
"keywords": [
|
|
50
52
|
"grepmax",
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
const { ensureSessionLease, readHookInput } = require("./watch-lease");
|
|
2
|
+
|
|
3
|
+
// Bounded renewal on turns and Bash activity keeps long CLI sessions watched.
|
|
4
|
+
// No detached timer, global watcher or additional MCP process is needed.
|
|
5
|
+
async function main() {
|
|
6
|
+
const input = await readHookInput();
|
|
7
|
+
await ensureSessionLease(input, input.cwd || process.cwd(), {
|
|
8
|
+
allowStart: true,
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
main();
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
const { execFileSync } = require("node:child_process");
|
|
2
1
|
const {
|
|
3
|
-
|
|
2
|
+
ensureSessionLease,
|
|
4
3
|
readHookInput,
|
|
5
4
|
registeredRootFor,
|
|
6
5
|
releaseSessionLeases,
|
|
@@ -20,19 +19,7 @@ async function main() {
|
|
|
20
19
|
}
|
|
21
20
|
}
|
|
22
21
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
try {
|
|
26
|
-
execFileSync("gmax", ["watch", "--daemon", "-b"], {
|
|
27
|
-
timeout: 5000,
|
|
28
|
-
stdio: "ignore",
|
|
29
|
-
});
|
|
30
|
-
} catch {
|
|
31
|
-
try {
|
|
32
|
-
execFileSync("gmax", ["watch", "-b"], { timeout: 5000, stdio: "ignore" });
|
|
33
|
-
} catch {}
|
|
34
|
-
}
|
|
35
|
-
await acquireSessionLease(input, newCwd);
|
|
22
|
+
await ensureSessionLease(input, newCwd, { allowStart: true });
|
|
36
23
|
}
|
|
37
24
|
|
|
38
25
|
main();
|
|
@@ -2,7 +2,7 @@ const fs = require("node:fs");
|
|
|
2
2
|
const _path = require("node:path");
|
|
3
3
|
const { execFileSync } = require("node:child_process");
|
|
4
4
|
const {
|
|
5
|
-
|
|
5
|
+
ensureSessionLease,
|
|
6
6
|
readHookInput,
|
|
7
7
|
registeredRootFor,
|
|
8
8
|
} = require("./watch-lease");
|
|
@@ -70,43 +70,10 @@ function isProjectRegistered(dir) {
|
|
|
70
70
|
return registeredRootFor(dir) !== null;
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
// A kill switch for the auto-start. Create ~/.gmax/autostart-disabled (or export
|
|
74
|
-
// GMAX_NO_AUTOSTART=1) to keep sessions from reviving the daemon — needed when the
|
|
75
|
-
// host is quarantined after a kernel-zone incident and the daemon's write volume is
|
|
76
|
-
// the thing under investigation. Delete the file to restore normal behavior.
|
|
77
|
-
//
|
|
78
|
-
// The CLI honors the same switch via src/lib/utils/autostart.ts (PATHS.autostartDisabledFile).
|
|
79
|
-
// This copy exists because a hook cannot import from dist — keep the two in sync.
|
|
80
|
-
function isAutostartDisabled() {
|
|
81
|
-
if (process.env.GMAX_NO_AUTOSTART === "1") return true;
|
|
82
|
-
try {
|
|
83
|
-
return require("node:fs").existsSync(
|
|
84
|
-
_path.join(require("node:os").homedir(), ".gmax", "autostart-disabled"),
|
|
85
|
-
);
|
|
86
|
-
} catch {
|
|
87
|
-
return false;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
73
|
async function startWatcher(input) {
|
|
92
|
-
if (isAutostartDisabled()) return;
|
|
93
74
|
const dir = input.cwd || process.cwd();
|
|
94
75
|
if (!isProjectRegistered(dir)) return;
|
|
95
|
-
|
|
96
|
-
execFileSync("gmax", ["watch", "--daemon", "-b"], {
|
|
97
|
-
timeout: 5000,
|
|
98
|
-
stdio: "ignore",
|
|
99
|
-
});
|
|
100
|
-
} catch {
|
|
101
|
-
// Fallback to per-project mode (older gmax without --daemon)
|
|
102
|
-
try {
|
|
103
|
-
execFileSync("gmax", ["watch", "-b"], { timeout: 5000, stdio: "ignore" });
|
|
104
|
-
} catch {
|
|
105
|
-
// Watcher may already be running or gmax not in PATH — ignore
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
// The daemon only watches projects a session holds a lease on.
|
|
109
|
-
await acquireSessionLease(input, dir);
|
|
76
|
+
await ensureSessionLease(input, dir, { allowStart: true });
|
|
110
77
|
}
|
|
111
78
|
|
|
112
79
|
async function main() {
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
// Session watch leases for the hooks.
|
|
2
2
|
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
|
|
7
|
-
// speaks the daemon's one-line JSON protocol directly.
|
|
3
|
+
// Claude's CLI integration takes a lease at startup, renews it during activity,
|
|
4
|
+
// and releases it at SessionEnd. Other MCP clients own separate leases. A hook
|
|
5
|
+
// cannot import from dist, so this speaks the one-line Unix socket protocol.
|
|
6
|
+
const { spawn } = require("node:child_process");
|
|
8
7
|
const fs = require("node:fs");
|
|
9
8
|
const net = require("node:net");
|
|
10
9
|
const os = require("node:os");
|
|
@@ -13,10 +12,18 @@ const path = require("node:path");
|
|
|
13
12
|
const GMAX_DIR = path.join(os.homedir(), ".gmax");
|
|
14
13
|
const SOCKET = path.join(GMAX_DIR, "daemon.sock");
|
|
15
14
|
|
|
16
|
-
//
|
|
17
|
-
// early, and the daemon caps any lease at 12h.
|
|
15
|
+
// Activity refreshes this TTL; idle sessions do not watch forever.
|
|
18
16
|
const SESSION_LEASE_TTL_MS = 4 * 60 * 60 * 1000;
|
|
19
17
|
|
|
18
|
+
function isAutostartDisabled() {
|
|
19
|
+
if (process.env.GMAX_NO_AUTOSTART === "1") return true;
|
|
20
|
+
try {
|
|
21
|
+
return fs.existsSync(path.join(GMAX_DIR, "autostart-disabled"));
|
|
22
|
+
} catch {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
20
27
|
function readHookInput(timeoutMs = 1000) {
|
|
21
28
|
return new Promise((resolve) => {
|
|
22
29
|
let data = "";
|
|
@@ -102,6 +109,7 @@ function sendOnce(cmd, timeoutMs) {
|
|
|
102
109
|
socket.on("error", (err) =>
|
|
103
110
|
finish({
|
|
104
111
|
ok: false,
|
|
112
|
+
absent: err.code === "ENOENT" || err.code === "ECONNREFUSED",
|
|
105
113
|
retry: err.code === "ENOENT" || err.code === "ECONNREFUSED",
|
|
106
114
|
}),
|
|
107
115
|
);
|
|
@@ -143,6 +151,42 @@ async function acquireSessionLease(input, dir) {
|
|
|
143
151
|
return resp !== null;
|
|
144
152
|
}
|
|
145
153
|
|
|
154
|
+
/** Reuse a lease-capable daemon; start one only after a definite absent socket. */
|
|
155
|
+
async function ensureSessionLease(input, dir, { allowStart = false } = {}) {
|
|
156
|
+
if (isAutostartDisabled() || !sessionHolder(input) || !registeredRootFor(dir))
|
|
157
|
+
return false;
|
|
158
|
+
const ping = await sendOnce({ cmd: "ping" }, 500);
|
|
159
|
+
if (ping.ok) {
|
|
160
|
+
if (!ping.resp.capabilities?.watchLeases) return false;
|
|
161
|
+
return acquireSessionLease(input, dir);
|
|
162
|
+
}
|
|
163
|
+
// Busy, initializing, denied and timed-out sockets belong to an existing
|
|
164
|
+
// daemon. Never start a per-project writer or replace that process here.
|
|
165
|
+
if (!allowStart || !ping.absent) return false;
|
|
166
|
+
try {
|
|
167
|
+
const child = spawn("gmax", ["watch", "--daemon", "-b"], {
|
|
168
|
+
detached: true,
|
|
169
|
+
stdio: "ignore",
|
|
170
|
+
cwd: dir,
|
|
171
|
+
// Another session can win the race after the absent-socket probe. The
|
|
172
|
+
// launcher must reuse that peer, even if its package version differs.
|
|
173
|
+
env: { ...process.env, GMAX_DAEMON_START_ONLY: "1" },
|
|
174
|
+
});
|
|
175
|
+
await new Promise((resolve, reject) => {
|
|
176
|
+
child.once("spawn", resolve);
|
|
177
|
+
child.once("error", reject);
|
|
178
|
+
});
|
|
179
|
+
child.unref();
|
|
180
|
+
} catch {
|
|
181
|
+
return false;
|
|
182
|
+
}
|
|
183
|
+
// A cold daemon may take longer than a hook's budget to become ready. Later
|
|
184
|
+
// activity retries ownership instead of opening another watcher.
|
|
185
|
+
const ready = await sendDaemonLine({ cmd: "ping" }, 1500);
|
|
186
|
+
if (!ready?.capabilities?.watchLeases) return false;
|
|
187
|
+
return acquireSessionLease(input, dir);
|
|
188
|
+
}
|
|
189
|
+
|
|
146
190
|
async function releaseSessionLeases(input, dir) {
|
|
147
191
|
const holder = sessionHolder(input);
|
|
148
192
|
const root = registeredRootFor(dir);
|
|
@@ -157,6 +201,8 @@ async function releaseSessionLeases(input, dir) {
|
|
|
157
201
|
|
|
158
202
|
module.exports = {
|
|
159
203
|
acquireSessionLease,
|
|
204
|
+
ensureSessionLease,
|
|
205
|
+
isAutostartDisabled,
|
|
160
206
|
readHookInput,
|
|
161
207
|
registeredRootFor,
|
|
162
208
|
releaseSessionLeases,
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"hooks": [
|
|
6
6
|
{
|
|
7
7
|
"type": "command",
|
|
8
|
-
"command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/start.js",
|
|
8
|
+
"command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/start.js\"",
|
|
9
9
|
"timeout": 10
|
|
10
10
|
}
|
|
11
11
|
]
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"hooks": [
|
|
17
17
|
{
|
|
18
18
|
"type": "command",
|
|
19
|
-
"command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/stop.js",
|
|
19
|
+
"command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/stop.js\"",
|
|
20
20
|
"timeout": 10
|
|
21
21
|
}
|
|
22
22
|
]
|
|
@@ -27,8 +27,8 @@
|
|
|
27
27
|
"hooks": [
|
|
28
28
|
{
|
|
29
29
|
"type": "command",
|
|
30
|
-
"command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/cwd-changed.js",
|
|
31
|
-
"timeout":
|
|
30
|
+
"command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/cwd-changed.js\"",
|
|
31
|
+
"timeout": 10
|
|
32
32
|
}
|
|
33
33
|
]
|
|
34
34
|
}
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"hooks": [
|
|
39
39
|
{
|
|
40
40
|
"type": "command",
|
|
41
|
-
"command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/subagent-start.js",
|
|
41
|
+
"command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/subagent-start.js\"",
|
|
42
42
|
"timeout": 3
|
|
43
43
|
}
|
|
44
44
|
]
|
|
@@ -50,11 +50,34 @@
|
|
|
50
50
|
"hooks": [
|
|
51
51
|
{
|
|
52
52
|
"type": "command",
|
|
53
|
-
"command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/pre-grep.js",
|
|
53
|
+
"command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/pre-grep.js\"",
|
|
54
54
|
"timeout": 2
|
|
55
55
|
}
|
|
56
56
|
]
|
|
57
57
|
}
|
|
58
|
+
],
|
|
59
|
+
"UserPromptSubmit": [
|
|
60
|
+
{
|
|
61
|
+
"hooks": [
|
|
62
|
+
{
|
|
63
|
+
"type": "command",
|
|
64
|
+
"command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/activity.js\"",
|
|
65
|
+
"timeout": 10
|
|
66
|
+
}
|
|
67
|
+
]
|
|
68
|
+
}
|
|
69
|
+
],
|
|
70
|
+
"PostToolUse": [
|
|
71
|
+
{
|
|
72
|
+
"matcher": "Bash",
|
|
73
|
+
"hooks": [
|
|
74
|
+
{
|
|
75
|
+
"type": "command",
|
|
76
|
+
"command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/activity.js\"",
|
|
77
|
+
"timeout": 10
|
|
78
|
+
}
|
|
79
|
+
]
|
|
80
|
+
}
|
|
58
81
|
]
|
|
59
82
|
}
|
|
60
83
|
}
|