ai-lens 0.8.97 → 0.8.98
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/.commithash +1 -1
- package/CHANGELOG.md +3 -0
- package/cli/hooks.js +27 -20
- package/package.json +1 -1
package/.commithash
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
5d8b7f1
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
History of changes to the `ai-lens` CLI package on npm. New entries go on top. Format: `## X.Y.Z — YYYY-MM-DD`, followed by user-facing bullets.
|
|
4
4
|
|
|
5
|
+
## 0.8.98 — 2026-06-17
|
|
6
|
+
- fix: Cursor hooks on Windows no longer use the `conhost.exe --headless` wrapper — it made Cursor report a "JSON Parse Error" on every hook (conhost emits terminal escape codes that Cursor tries to parse as the hook's output) and broke capture. Cursor returns to the plain working form; machines that briefly got the conhost'd Cursor hook are auto-downgraded on the next `ai-lens init`/`/setup`. Claude Code keeps the windowless wrapper (it tolerates the output). The minor Cursor console flash on Windows is upstream Cursor behaviour
|
|
7
|
+
|
|
5
8
|
## 0.8.97 — 2026-06-17
|
|
6
9
|
- fix: `ai-lens init` no longer leaves an invalid `{ "version": 1 }` (no hooks) in `~/.cursor/hooks.json` when migrating to project hooks — Cursor flagged such a file as an error and stopped running ALL hooks, silently killing capture even when project hooks were correct. init now deletes the empty file, and repairs machines already left in this state on the next run
|
|
7
10
|
|
package/cli/hooks.js
CHANGED
|
@@ -375,13 +375,17 @@ export function cursorCaptureCommand(opts = {}) {
|
|
|
375
375
|
// hint when building the underlying command (avoids the cmd.exe `call` prefix
|
|
376
376
|
// which PowerShell doesn't understand).
|
|
377
377
|
const shell = platform === 'win32' ? 'powershell' : null;
|
|
378
|
-
//
|
|
379
|
-
//
|
|
380
|
-
//
|
|
381
|
-
//
|
|
378
|
+
// NOTE: Cursor deliberately does NOT get the `conhost.exe --headless` windowless
|
|
379
|
+
// wrapper (unlike Claude Code). conhost runs a ConPTY and emits VT escape sequences
|
|
380
|
+
// (e.g. ESC[?9001h win32-input-mode) onto the hook's output stream, and Cursor
|
|
381
|
+
// parses a hook's stdout as JSON → "JSON Parse Error … is not valid JSON", which
|
|
382
|
+
// breaks the hook. Claude Code tolerates non-JSON hook stdout, so it keeps conhost.
|
|
383
|
+
// The residual Cursor console flash on Windows is upstream (how Cursor spawns the
|
|
384
|
+
// hook process) and is the lesser evil vs a broken hook. (ADR 0003 §3.2 deferred
|
|
385
|
+
// Cursor+conhost as unverified; verified incompatible — reverted here.)
|
|
382
386
|
const cmd = customPath != null
|
|
383
|
-
? captureCommand({ rawPath: true, customPath,
|
|
384
|
-
: captureCommand({ useTilde: effectiveUseTilde,
|
|
387
|
+
? captureCommand({ rawPath: true, customPath, ctx, shell })
|
|
388
|
+
: captureCommand({ useTilde: effectiveUseTilde, ctx, shell });
|
|
385
389
|
return platform === 'win32' ? `& ${cmd}` : cmd;
|
|
386
390
|
}
|
|
387
391
|
|
|
@@ -1089,25 +1093,28 @@ function isCurrentAiLensHook(entry, expected, opts = {}) {
|
|
|
1089
1093
|
// $CLAUDE_PROJECT_DIR/%CLAUDE_PROJECT_DIR% hook written for the OTHER OS won't
|
|
1090
1094
|
// expand on this platform, so flag it outdated to let init rewrite it.
|
|
1091
1095
|
//
|
|
1092
|
-
// Windowless exception (same allowPlatformRewrite gate):
|
|
1093
|
-
//
|
|
1094
|
-
//
|
|
1095
|
-
//
|
|
1096
|
-
//
|
|
1097
|
-
//
|
|
1098
|
-
//
|
|
1099
|
-
//
|
|
1100
|
-
// windowless
|
|
1101
|
-
//
|
|
1102
|
-
//
|
|
1103
|
-
//
|
|
1104
|
-
//
|
|
1096
|
+
// Windowless exception (same allowPlatformRewrite gate): flag a hook outdated when
|
|
1097
|
+
// its stored `conhost.exe --headless` wrapper DISAGREES with the freshly regenerated
|
|
1098
|
+
// `expected` command — in EITHER direction — so init re-syncs it:
|
|
1099
|
+
// - expected has conhost, stored doesn't → ADD it (Claude on conhost-capable
|
|
1100
|
+
// Windows that predates the windowless form);
|
|
1101
|
+
// - expected has NO conhost, stored DOES → REMOVE it (Cursor: conhost is
|
|
1102
|
+
// incompatible — its ConPTY VT output breaks Cursor's JSON parse of hook stdout
|
|
1103
|
+
// — so we downgrade machines that got the short-lived conhost'd Cursor form).
|
|
1104
|
+
// We compare ONLY the windowless dimension (not the whole command), so legitimate
|
|
1105
|
+
// path/node/install-mode variation never false-flags. `expected` already bakes in
|
|
1106
|
+
// tool+platform+conhost-support (makeClaudeHookDefs passes windowless:true;
|
|
1107
|
+
// makeCursorHookDefs and makeCodexHookDefs do NOT), so this self-scopes: Codex,
|
|
1108
|
+
// Cursor, macOS, and old Windows produce a non-conhost `expected`, and only Claude
|
|
1109
|
+
// on conhost-capable Windows produces a conhost `expected`. Committed (tracked)
|
|
1110
|
+
// files are exempt via allowPlatformRewrite — they carry one OS-agnostic syntax
|
|
1111
|
+
// and can't bake a Windows-only wrapper; the per-machine overlay provides it.
|
|
1105
1112
|
const { platform = process.platform, allowPlatformRewrite = false } = opts;
|
|
1106
1113
|
const expectedCmd = expected?.command ?? expected?.hooks?.[0]?.command ?? '';
|
|
1107
1114
|
const expectedWindowless = isConhostHeadlessCommand(expectedCmd);
|
|
1108
1115
|
const ok = (cmd) => isAcceptableHookCommand(cmd)
|
|
1109
1116
|
&& !(allowPlatformRewrite && isWrongPlatformProjectDirCommand(cmd, platform))
|
|
1110
|
-
&& !(allowPlatformRewrite && expectedWindowless
|
|
1117
|
+
&& !(allowPlatformRewrite && expectedWindowless !== isConhostHeadlessCommand(cmd));
|
|
1111
1118
|
// Flat format (Cursor): single command per entry.
|
|
1112
1119
|
if (entry?.command != null) {
|
|
1113
1120
|
return ok(entry.command);
|