@ngockhoale/ukit 2.2.5 → 2.2.7
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/CHANGELOG.md +50 -0
- package/package.json +1 -1
- package/templates/.omp/hooks/pre/ukit-bridge.js +32 -3
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,56 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to UKit are documented here.
|
|
4
4
|
|
|
5
|
+
## 2.2.7 - 2026-08-28
|
|
6
|
+
|
|
7
|
+
Follow-up to 2.2.6: that release only made the wrong-runtime failure message *explain* the
|
|
8
|
+
`UKIT_NODE_PATH` workaround better, but a user still had to diagnose the error and export the
|
|
9
|
+
variable by hand. This release removes the manual step for the common case.
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- **The omp bridge now auto-detects a working Node.js binary instead of requiring
|
|
14
|
+
`UKIT_NODE_PATH` to be set by hand.** [`ukit-bridge.js`](.omp/hooks/pre/ukit-bridge.js) probes
|
|
15
|
+
`process.execPath` with `--version` before trusting it; if that fails (e.g. a Bun-hosted or
|
|
16
|
+
self-compiled omp reporting its own binary as `process.execPath`), it falls back to the bare
|
|
17
|
+
`node` command name and lets normal OS `PATH` resolution find a real Node.js binary — this
|
|
18
|
+
works the same way on macOS, Linux, and Windows, since Node's `child_process` spawn does `PATH`
|
|
19
|
+
lookup for bare executable names on all three. An explicit `UKIT_NODE_PATH` still always wins
|
|
20
|
+
and skips the probe. The probe result is cached for the life of the omp process, so it only
|
|
21
|
+
runs once, not on every tool call. The transport-failure message's `UKIT_NODE_PATH` hint now
|
|
22
|
+
only appears when both the probe and the `node` PATH fallback have failed.
|
|
23
|
+
|
|
24
|
+
### Added
|
|
25
|
+
|
|
26
|
+
- **1 regression test** in `tests/hooks/ompHookBridge.test.js` — with `UKIT_NODE_PATH` unset and
|
|
27
|
+
`process.execPath` mocked to fail the `--version` probe, the bridge falls back to invoking the
|
|
28
|
+
bare `node` command instead of blocking the tool call.
|
|
29
|
+
|
|
30
|
+
## 2.2.6 - 2026-08-27
|
|
31
|
+
|
|
32
|
+
Follow-up to a real-world report: a user on a Bun-hosted/self-compiled omp build kept getting
|
|
33
|
+
every Edit/Write blocked with `UKit OMP hook runner failed before producing a valid result
|
|
34
|
+
(killed=true, ..., runtime=/Users/.../bin/omp)`. The diagnostic added in 2.2.4 was already
|
|
35
|
+
honest about the failure, but it did not say *why* — `runtime=` pointed straight at omp's own
|
|
36
|
+
binary (proof that `process.execPath` inside that omp build resolves to omp itself, not a real
|
|
37
|
+
Node.js binary), yet the message never told the user the fix was to set `UKIT_NODE_PATH`.
|
|
38
|
+
|
|
39
|
+
### Fixed
|
|
40
|
+
|
|
41
|
+
- **The omp bridge's transport-failure message named the wrong-runtime symptom but never the
|
|
42
|
+
fix.** [`ukit-bridge.js`](.omp/hooks/pre/ukit-bridge.js) already resolved
|
|
43
|
+
`process.env.UKIT_NODE_PATH || process.execPath` and reported the resolved `runtime` on failure
|
|
44
|
+
(2.2.4), but a user seeing `runtime=/Users/lenk/.local/bin/omp` had no way to know that string
|
|
45
|
+
meant "wrong binary" or what to do about it. When `UKIT_NODE_PATH` is unset, the failure
|
|
46
|
+
message now appends: the reported runtime may not be a real Node.js binary, and the exact fix —
|
|
47
|
+
`export UKIT_NODE_PATH="$(command -v node)"`. No behavior change when `UKIT_NODE_PATH` is
|
|
48
|
+
already set.
|
|
49
|
+
|
|
50
|
+
### Added
|
|
51
|
+
|
|
52
|
+
- **2 regression tests** in `tests/hooks/ompHookBridge.test.js` (case 13) — the wrong-runtime
|
|
53
|
+
hint appears when `UKIT_NODE_PATH` is unset, and is omitted when it is already set.
|
|
54
|
+
|
|
5
55
|
## 2.2.5 - 2026-08-27
|
|
6
56
|
|
|
7
57
|
Follow-up to the user's report that UKit had become too heavy-handed: too many hook scripts firing per tool call, too much blocking, and a concrete false-positive in the destructive-command gate itself. This wave trims default hook wiring and fixes the false positive, while explicitly preserving `rm`/destructive-command protection as instructed.
|
package/package.json
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
import fs from 'node:fs';
|
|
9
9
|
import path from 'node:path';
|
|
10
10
|
import { fileURLToPath } from 'node:url';
|
|
11
|
+
import { spawnSync } from 'node:child_process';
|
|
11
12
|
import {
|
|
12
13
|
evaluateCompletion,
|
|
13
14
|
incrementContinuation,
|
|
@@ -181,6 +182,30 @@ function recordHookErrorDiagnostic(projectRoot, sessionId, diagnostic) {
|
|
|
181
182
|
}
|
|
182
183
|
}
|
|
183
184
|
|
|
185
|
+
// Cached across the omp process lifetime: resolution below spawns a probe process, and every
|
|
186
|
+
// tool call would otherwise pay that cost again.
|
|
187
|
+
let cachedNodeExecutable = null;
|
|
188
|
+
|
|
189
|
+
function isRealNodeBinary(candidate) {
|
|
190
|
+
try {
|
|
191
|
+
const probe = spawnSync(candidate, ['--version'], { timeout: 2000, encoding: 'utf8' });
|
|
192
|
+
return !probe.error && typeof probe.stdout === 'string' && /^v\d+\.\d+\.\d+/.test(probe.stdout.trim());
|
|
193
|
+
} catch {
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Do not blindly trust process.execPath: under a Bun-hosted or self-compiled omp, it can report
|
|
199
|
+
// omp's own binary path instead of a real Node.js one. Probe it first; if it fails, fall back to
|
|
200
|
+
// the bare "node" command name so PATH resolution (cross-platform: macOS/Linux/Windows) finds a
|
|
201
|
+
// real Node.js binary automatically, without requiring a manual UKIT_NODE_PATH export.
|
|
202
|
+
function resolveNodeExecutable() {
|
|
203
|
+
if (process.env.UKIT_NODE_PATH) return process.env.UKIT_NODE_PATH;
|
|
204
|
+
if (cachedNodeExecutable) return cachedNodeExecutable;
|
|
205
|
+
cachedNodeExecutable = isRealNodeBinary(process.execPath) ? process.execPath : 'node';
|
|
206
|
+
return cachedNodeExecutable;
|
|
207
|
+
}
|
|
208
|
+
|
|
184
209
|
export async function runScriptChain(
|
|
185
210
|
pi,
|
|
186
211
|
scripts,
|
|
@@ -195,8 +220,7 @@ export async function runScriptChain(
|
|
|
195
220
|
|
|
196
221
|
const runnerPath = path.join(projectRoot, '.claude', 'ukit', 'runtime', 'hook-chain-runner.mjs');
|
|
197
222
|
const scriptPaths = scripts.map((scriptName) => path.join(projectRoot, '.claude', 'hooks', scriptName));
|
|
198
|
-
|
|
199
|
-
const nodeExecutable = process.env.UKIT_NODE_PATH || process.execPath;
|
|
223
|
+
const nodeExecutable = resolveNodeExecutable();
|
|
200
224
|
const startedAt = Date.now();
|
|
201
225
|
let execResult;
|
|
202
226
|
try {
|
|
@@ -241,10 +265,15 @@ export async function runScriptChain(
|
|
|
241
265
|
wrapperError: chainResult?.wrapperError || null,
|
|
242
266
|
};
|
|
243
267
|
recordHookErrorDiagnostic(projectRoot, payload.session_id, diagnostic);
|
|
268
|
+
const nodePathHint = process.env.UKIT_NODE_PATH
|
|
269
|
+
? ''
|
|
270
|
+
: ` UKit already tried process.execPath and a "node" PATH lookup; neither resolved to a `
|
|
271
|
+
+ `working Node.js binary (runtime="${diagnostic.nodeExecutable}"). Set UKIT_NODE_PATH to `
|
|
272
|
+
+ `an explicit Node.js binary path to override, e.g.: export UKIT_NODE_PATH="$(command -v node)".`;
|
|
244
273
|
const reason = `UKit OMP hook runner failed before producing a valid result `
|
|
245
274
|
+ `(killed=${diagnostic.killed}, code=${diagnostic.code}, elapsedMs=${diagnostic.elapsedMs}, `
|
|
246
275
|
+ `runtime=${diagnostic.nodeExecutable}). No safety-gate verdict was available for [${scripts.join(', ')}]. `
|
|
247
|
-
+ `See .ukit/storage/cache/hook-errors
|
|
276
|
+
+ `See .ukit/storage/cache/hook-errors/.${nodePathHint}`;
|
|
248
277
|
if (failClosedOnTransportError) {
|
|
249
278
|
return { block: true, reason, context, invoked };
|
|
250
279
|
}
|