vexp-cli 2.0.30 → 2.0.32
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/doctor.js +20 -0
- package/dist/hook-template.js +41 -8
- package/mcp/mcp-server.cjs +41 -41
- package/package.json +6 -6
package/dist/doctor.js
CHANGED
|
@@ -102,6 +102,19 @@ export async function runDoctor() {
|
|
|
102
102
|
const ws = resolveWorkspace();
|
|
103
103
|
console.log(` cwd: ${process.cwd()}`);
|
|
104
104
|
line(OK, `resolves to: ${ws.root} (via ${ws.source})`);
|
|
105
|
+
// A *global* VEXP_WORKSPACE env var overrides the per-session signal
|
|
106
|
+
// (CLAUDE_PROJECT_DIR) for EVERY agent session, pinning Claude *and* Codex to
|
|
107
|
+
// one repo's daemon. Agents working in other repos then hit the wrong daemon
|
|
108
|
+
// and get [] / wrong-repo results from get_skeleton & run_pipeline. The
|
|
109
|
+
// ~/.claude.json / ~/.codex checks below only catch a pin written into the MCP
|
|
110
|
+
// *config*; a shell/OS env var is invisible to them — flag it here when it
|
|
111
|
+
// disagrees with this directory's natural target.
|
|
112
|
+
if (ws.source === "VEXP_WORKSPACE") {
|
|
113
|
+
const discovered = discoverWorkspaceRoot(process.cwd());
|
|
114
|
+
if (discovered.toLowerCase() !== ws.root.toLowerCase()) {
|
|
115
|
+
line(WARN, `global VEXP_WORKSPACE env var pins this session to ${ws.root}, but this directory's natural target is ${discovered} → agents here get [] / wrong-repo results. Unset the VEXP_WORKSPACE OS/shell env var for per-session targeting.`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
105
118
|
const sock = socketForWorkspace(ws.root);
|
|
106
119
|
const live = await reachable(sock);
|
|
107
120
|
line(live ? OK : WARN, `daemon socket ${live ? "reachable" : "NOT reachable"}: ${sock}`);
|
|
@@ -132,6 +145,13 @@ export async function runDoctor() {
|
|
|
132
145
|
const inReg = entries.some(([r]) => r.toLowerCase() === ws.root.toLowerCase());
|
|
133
146
|
if (!inReg)
|
|
134
147
|
line(WARN, `current workspace (${ws.root}) is NOT registered — a child here could mis-route`);
|
|
148
|
+
// Multi-daemon + global pin: catches the case the section-1 check misses
|
|
149
|
+
// (doctor run from the same dir VEXP_WORKSPACE points at). With several
|
|
150
|
+
// daemons registered, a global VEXP_WORKSPACE still routes EVERY agent
|
|
151
|
+
// session to just one of them.
|
|
152
|
+
if (ws.source === "VEXP_WORKSPACE" && entries.length >= 2) {
|
|
153
|
+
line(WARN, `${entries.length} daemons registered but a global VEXP_WORKSPACE pins EVERY agent session to ${ws.root}; agents in the other repos get [] / wrong-repo results. Unset the global VEXP_WORKSPACE env var.`);
|
|
154
|
+
}
|
|
135
155
|
}
|
|
136
156
|
// 3) License (fresh.jwt rolling token vs license.jwt).
|
|
137
157
|
console.log(chalk.bold("\nLicense tokens (~/.vexp)"));
|
package/dist/hook-template.js
CHANGED
|
@@ -2,19 +2,52 @@
|
|
|
2
2
|
* Claude Code PreToolUse hook that conditionally blocks Grep/Glob
|
|
3
3
|
* when the vexp daemon is available, falling back to allow them
|
|
4
4
|
* when the daemon is not running.
|
|
5
|
+
*
|
|
6
|
+
* Cross-platform: Unix/macOS expose the daemon as a domain socket file
|
|
7
|
+
* (.vexp/daemon.sock); Windows uses a named pipe with NO socket file, so the
|
|
8
|
+
* daemon writes a .vexp/daemon.pipe marker instead. The previous template only
|
|
9
|
+
* checked `[ -S "$SOCK" ]`, which is ALWAYS false on Windows → the hook failed
|
|
10
|
+
* open and never blocked Grep/Glob there. We now branch on `uname` so each
|
|
11
|
+
* platform uses the right "daemon is listening" signal. The healthy marker
|
|
12
|
+
* (removed by the daemon on graceful shutdown) gates both branches.
|
|
5
13
|
*/
|
|
6
14
|
export const VEXP_GUARD_HOOK = `#!/bin/bash
|
|
7
|
-
# vexp-guard: block Grep/Glob when vexp daemon is running AND
|
|
8
|
-
#
|
|
9
|
-
#
|
|
15
|
+
# vexp-guard: block Grep/Glob/Read when the vexp daemon is running AND healthy.
|
|
16
|
+
# Cross-platform: Unix/macOS use the domain socket file (.vexp/daemon.sock);
|
|
17
|
+
# Windows (Git Bash) has no socket file, so the daemon writes .vexp/daemon.pipe.
|
|
10
18
|
VEXP_DIR="\${CLAUDE_PROJECT_DIR:-.}/.vexp"
|
|
11
19
|
SOCK="$VEXP_DIR/daemon.sock"
|
|
20
|
+
PIPE="$VEXP_DIR/daemon.pipe"
|
|
12
21
|
HEALTHY="$VEXP_DIR/healthy"
|
|
13
22
|
PID_FILE="$VEXP_DIR/daemon.pid"
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
23
|
+
|
|
24
|
+
vexp_deny() {
|
|
25
|
+
printf '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":"vexp daemon is running. Use run_pipeline instead of Grep/Glob/Read."}}'
|
|
26
|
+
exit 0
|
|
27
|
+
}
|
|
28
|
+
vexp_allow() {
|
|
17
29
|
printf '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"allow","permissionDecisionReason":"vexp index not ready, allowing direct search fallback."}}'
|
|
18
|
-
|
|
19
|
-
|
|
30
|
+
exit 0
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
# Healthy marker is the portable "daemon is up" signal; the daemon removes it on
|
|
34
|
+
# graceful shutdown. Required on every platform — if it's missing, fail open.
|
|
35
|
+
[ -f "$HEALTHY" ] || vexp_allow
|
|
36
|
+
|
|
37
|
+
case "$(uname -s 2>/dev/null)" in
|
|
38
|
+
MINGW*|MSYS*|CYGWIN*|Windows*)
|
|
39
|
+
# Windows: no Unix socket to stat, and \`kill -0\` on a native Windows PID is
|
|
40
|
+
# unreliable under Git Bash. The pipe marker + healthy marker are the signal.
|
|
41
|
+
[ -f "$PIPE" ] && vexp_deny
|
|
42
|
+
vexp_allow
|
|
43
|
+
;;
|
|
44
|
+
*)
|
|
45
|
+
# Unix/macOS: require the live socket AND a live PID (the PID check catches
|
|
46
|
+
# stale files left behind after \`kill -9\`).
|
|
47
|
+
[ -S "$SOCK" ] || vexp_allow
|
|
48
|
+
[ -f "$PID_FILE" ] || vexp_allow
|
|
49
|
+
kill -0 "$(cat "$PID_FILE" 2>/dev/null)" 2>/dev/null && vexp_deny
|
|
50
|
+
vexp_allow
|
|
51
|
+
;;
|
|
52
|
+
esac
|
|
20
53
|
`;
|