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 CHANGED
@@ -1 +1 @@
1
- 13ab9a5
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
- // windowless: on Windows ≥1809 wrap node in `conhost.exe --headless` so Cursor
379
- // doesn't flash a console window on every event (incl. session start). `& ` still
380
- // works in front of conhost; the detector strips `& ` then the conhost prefix, so
381
- // the form stays recognised as current (no churn). Mac/old-Windows: unchanged.
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, windowless: true, ctx, shell })
384
- : captureCommand({ useTilde: effectiveUseTilde, windowless: true, ctx, shell });
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): when the freshly
1093
- // regenerated `expected` command carries the `conhost.exe --headless` wrapper
1094
- // (Cursor/Claude on conhost-capable Windows) but the stored command does NOT,
1095
- // flag it outdated so init upgrades it otherwise a pre-conhost install stays
1096
- // `current` forever and keeps flashing a console window on every event. We
1097
- // compare ONLY the windowless dimension (not the whole command), so legitimate
1098
- // path/node/install-mode variation never false-flags. `expected` already bakes
1099
- // in tool+platform+conhost-support (makeCursorHookDefs/makeClaudeHookDefs pass
1100
- // windowless:true; makeCodexHookDefs does not), so this self-scopes: Codex,
1101
- // macOS, and old Windows produce a non-conhost `expected` and never trip it.
1102
- // Committed (tracked) files are exempt via allowPlatformRewrite — they carry one
1103
- // OS-agnostic syntax and can't bake a Windows-only wrapper; the per-machine
1104
- // overlay provides the windowless path.
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 && !isConhostHeadlessCommand(cmd));
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);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-lens",
3
- "version": "0.8.97",
3
+ "version": "0.8.98",
4
4
  "type": "module",
5
5
  "description": "Centralized session analytics for AI coding tools",
6
6
  "bin": {