ai-lens 0.8.95 → 0.8.97
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 +6 -0
- package/cli/hooks.js +51 -5
- package/package.json +1 -1
package/.commithash
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
13ab9a5
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
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.97 — 2026-06-17
|
|
6
|
+
- 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
|
+
|
|
8
|
+
## 0.8.96 — 2026-06-17
|
|
9
|
+
- fix: `ai-lens status` now reports a Windows Cursor (or Claude Code) hook that lacks the windowless `conhost.exe --headless` wrapper as outdated, so a normal re-run of `ai-lens init` (or `/setup`) upgrades it in place. Previously the console-flash fix from 0.8.95 only applied to brand-new installs; existing hooks were considered up-to-date and never rewritten. macOS/Linux, older Windows, and Codex hooks are unaffected
|
|
10
|
+
|
|
5
11
|
## 0.8.95 — 2026-06-17
|
|
6
12
|
- fix: on Windows, Cursor hooks no longer flash a console window on every event (including new-session start) — the hook command is now wrapped in `conhost.exe --headless` (Windows 10 1809+), the same windowless form Claude Code hooks already use. Older Windows builds and macOS/Linux are unchanged. Re-run `ai-lens init` to apply
|
|
7
13
|
- feat: `ai-lens init` now warns when the configured `projects` filter does not cover the workspace you're setting up — a stale filter silently drops all capture (hooks look configured but record nothing)
|
package/cli/hooks.js
CHANGED
|
@@ -1088,9 +1088,26 @@ function isCurrentAiLensHook(entry, expected, opts = {}) {
|
|
|
1088
1088
|
// Exception (allowPlatformRewrite, set for untracked/per-machine files): a
|
|
1089
1089
|
// $CLAUDE_PROJECT_DIR/%CLAUDE_PROJECT_DIR% hook written for the OTHER OS won't
|
|
1090
1090
|
// expand on this platform, so flag it outdated to let init rewrite it.
|
|
1091
|
+
//
|
|
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.
|
|
1091
1105
|
const { platform = process.platform, allowPlatformRewrite = false } = opts;
|
|
1106
|
+
const expectedCmd = expected?.command ?? expected?.hooks?.[0]?.command ?? '';
|
|
1107
|
+
const expectedWindowless = isConhostHeadlessCommand(expectedCmd);
|
|
1092
1108
|
const ok = (cmd) => isAcceptableHookCommand(cmd)
|
|
1093
|
-
&& !(allowPlatformRewrite && isWrongPlatformProjectDirCommand(cmd, platform))
|
|
1109
|
+
&& !(allowPlatformRewrite && isWrongPlatformProjectDirCommand(cmd, platform))
|
|
1110
|
+
&& !(allowPlatformRewrite && expectedWindowless && !isConhostHeadlessCommand(cmd));
|
|
1094
1111
|
// Flat format (Cursor): single command per entry.
|
|
1095
1112
|
if (entry?.command != null) {
|
|
1096
1113
|
return ok(entry.command);
|
|
@@ -1295,8 +1312,15 @@ export function buildStrippedConfig(tool, existingConfig) {
|
|
|
1295
1312
|
// If no hooks remain, clean up
|
|
1296
1313
|
if (Object.keys(base.hooks).length === 0) {
|
|
1297
1314
|
delete base.hooks;
|
|
1298
|
-
//
|
|
1299
|
-
|
|
1315
|
+
// Keep the file only if REAL user settings remain — i.e. keys beyond the tool's
|
|
1316
|
+
// OWN scaffolding (e.g. Cursor's `version: 1`, which we wrote ourselves). Leaving
|
|
1317
|
+
// a Cursor hooks.json as just `{ "version": 1 }` is an invalid hookless config:
|
|
1318
|
+
// Cursor flags it as an error and stops running hooks. In that case return null so
|
|
1319
|
+
// the caller deletes the file. Real settings (Claude settings.json env/statusLine,
|
|
1320
|
+
// not in topLevelFields) are still preserved.
|
|
1321
|
+
const scaffolding = new Set(Object.keys(tool.topLevelFields || {}));
|
|
1322
|
+
const meaningful = Object.keys(base).filter((k) => !scaffolding.has(k));
|
|
1323
|
+
if (meaningful.length > 0) {
|
|
1300
1324
|
return base;
|
|
1301
1325
|
}
|
|
1302
1326
|
return null;
|
|
@@ -1783,7 +1807,19 @@ export function cleanupOppositeScope(activeTools) {
|
|
|
1783
1807
|
try { config = JSON.parse(raw); } catch { continue; }
|
|
1784
1808
|
|
|
1785
1809
|
const hooks = config.hooks;
|
|
1786
|
-
if (!hooks || typeof hooks !== 'object')
|
|
1810
|
+
if (!hooks || typeof hooks !== 'object') {
|
|
1811
|
+
// Repair: an older CLI could strip the opposite-scope hooks but leave behind an
|
|
1812
|
+
// invalid hookless scaffolding file (e.g. Cursor's `{ "version": 1 }`), which the
|
|
1813
|
+
// tool then flags as an error and stops running hooks. If a non-shared opposite
|
|
1814
|
+
// file has no hooks and only our own scaffolding keys, delete it.
|
|
1815
|
+
const scaffold = new Set(Object.keys(global.topLevelFields || {}));
|
|
1816
|
+
const onlyScaffold = Object.keys(config).every((k) => scaffold.has(k));
|
|
1817
|
+
if (!global.sharedConfig && onlyScaffold) {
|
|
1818
|
+
try { unlinkSync(oppositeConfigPath); } catch { /* already gone */ }
|
|
1819
|
+
results.push({ path: oppositeConfigPath, action: 'removed', toolName: oppositeToolName });
|
|
1820
|
+
}
|
|
1821
|
+
continue;
|
|
1822
|
+
}
|
|
1787
1823
|
|
|
1788
1824
|
let hasAiLens = false;
|
|
1789
1825
|
for (const entries of Object.values(hooks)) {
|
|
@@ -1799,8 +1835,18 @@ export function cleanupOppositeScope(activeTools) {
|
|
|
1799
1835
|
if (stripped) {
|
|
1800
1836
|
writeHooksConfig({ configPath: oppositeConfigPath }, stripped);
|
|
1801
1837
|
results.push({ path: oppositeConfigPath, action: 'cleaned', toolName: oppositeToolName });
|
|
1838
|
+
} else if (global.sharedConfig) {
|
|
1839
|
+
// Shared config (Claude settings.json): drop the hooks but keep the file —
|
|
1840
|
+
// it may carry the user's other settings.
|
|
1841
|
+
writeHooksConfig({ configPath: oppositeConfigPath }, {});
|
|
1842
|
+
results.push({ path: oppositeConfigPath, action: 'cleaned', toolName: oppositeToolName });
|
|
1843
|
+
} else {
|
|
1844
|
+
// Non-shared (Cursor/Codex): the file held only AI Lens hooks plus our own
|
|
1845
|
+
// scaffolding (e.g. Cursor's `version: 1`). Delete it rather than leave an
|
|
1846
|
+
// invalid hookless `{ "version": 1 }` that breaks the tool's hook loading.
|
|
1847
|
+
try { unlinkSync(oppositeConfigPath); } catch { /* already gone */ }
|
|
1848
|
+
results.push({ path: oppositeConfigPath, action: 'removed', toolName: oppositeToolName });
|
|
1802
1849
|
}
|
|
1803
|
-
// Don't delete the file — global settings.json may have other settings
|
|
1804
1850
|
}
|
|
1805
1851
|
return results;
|
|
1806
1852
|
}
|