diff --git a/CHANGELOG.md b/CHANGELOG.md index 8bde06d9..75e19c11 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ # aicodeman +## 1.9.3 + +### Patch Changes + +- Plan-usage chip now defaults ON on desktop, plus the reworked `xterm-zerolag-input` README. + - **Plan-usage chip defaults ON (desktop).** The `showPlanUsageLimits` chip (live 5-hour and weekly plan usage from the Claude statusline) used to be opt-in and default OFF, so most users never saw it. Desktop now defaults ON; handhelds still default OFF so the phone header stays minimal and the `mobile-header-buttons-policy` guard keeps passing. Devices with an explicitly stored preference keep whatever they chose, so nobody's OFF gets overridden. + - **One resolver behind the chip.** Added `planUsageChipEnabled()` in settings-ui.js and routed all three call sites through it: the App Settings checkbox, the chip's visibility, and the create-time `statusLineTelemetry` flag in session-ui.js. Those three had independent `?? false` / `=== true` defaults, and a chip revealed without the telemetry flag renders `—` forever, so a default flip on one site alone would have shipped a permanently empty chip. + - **Cron button comment corrected.** The App Settings comment claimed "Cron button defaults ON" while the code, the template (`btn-cron--hidden`) and the CSS all default it OFF. Verified against a fresh browser profile: the button is hidden and its checkbox unchecked out of the box. Comment now matches, and states why the two halves stay consistent. + - **Docs.** CLAUDE.md, `docs/architecture-invariants.md` and `docs/usage-limits-display-plan.md` updated for the new default and the single-resolver rule; the stale `styles.css` comment claiming the server strips the chip's hidden class at render was corrected (display is per-device, so the client reveals it). + - **`xterm-zerolag-input` README rework** (0.1.5 shipped the content; this republishes with the graphic and promo changes): replaced the misaligned 8-line keystroke-flow diagram with a two-line stock-vs-zerolag contrast, added a Codeman callout above the demo GIF with links to getcodeman.com and the repo, and rewrote the Origin section so it argues the extraction story instead of repeating the promo. + ## 1.9.2 ### Patch Changes diff --git a/CLAUDE.md b/CLAUDE.md index 8b1ac217..6e094048 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -74,7 +74,7 @@ When user says "COM": CI runs `npm run check:lockfile` on every push/PR, so lockfile drift fails the build even if the `version-packages` script is bypassed. -**Version**: 1.9.2 (must match `package.json`) +**Version**: 1.9.3 (must match `package.json`) ## Project Overview @@ -183,7 +183,7 @@ Codeman is a Claude Code session manager with web interface and autonomous Ralph **Auto-resume on usage limit** (opt-in per session, top of the Respawn tab): when Claude halts on a subscription limit, `usage-limit-patterns.ts` (pure, unit-tested) parses the reset time and `SessionAutoOps` arms a timer for reset+2min, then sends Esc + `continue`. ⚠️ Respawn cycles are blocked while paused (`isLimitPaused` guard in `onIdleDetected`), which is what prevents `/clear` from wiping the paused conversation. Claude-mode only. → [architecture-invariants#auto-resume-on-usage-limit](docs/architecture-invariants.md#auto-resume-on-usage-limit) -**Plan-usage chip** (statusLine telemetry, opt-in `showPlanUsageLimits`, default OFF): Codeman injects its own `statusLine.command` exporter which POSTs Claude's `rate_limits` blob to `POST /api/status-telemetry`. The exporter is identified by a marker, so it only ever adds/updates/removes a statusLine that is **ours**, never a user's hand-authored one, and it prints the footer through so the in-terminal statusline is not blanked. Claude-mode only; distinct from auto-resume, which reacts to the limit *message* rather than showing live %. → [architecture-invariants#plan-usage-chip-statusline-telemetry](docs/architecture-invariants.md#plan-usage-chip-statusline-telemetry), `docs/usage-limits-display-plan.md` +**Plan-usage chip** (statusLine telemetry, `showPlanUsageLimits`, per-device: desktop default **ON**, handhelds OFF via the mobile block in `getDefaultSettings()`): resolve it ONLY through `planUsageChipEnabled()` in settings-ui.js, which backs all three call sites (the App Settings checkbox, the chip's visibility, and the `statusLineTelemetry` flag on session create). A chip shown without telemetry renders `—` forever. Codeman injects its own `statusLine.command` exporter which POSTs Claude's `rate_limits` blob to `POST /api/status-telemetry`. The exporter is identified by a marker, so it only ever adds/updates/removes a statusLine that is **ours**, never a user's hand-authored one, and it prints the footer through so the in-terminal statusline is not blanked. Claude-mode only; distinct from auto-resume, which reacts to the limit *message* rather than showing live %. → [architecture-invariants#plan-usage-chip-statusline-telemetry](docs/architecture-invariants.md#plan-usage-chip-statusline-telemetry), `docs/usage-limits-display-plan.md` **Orchestrator**: State machine that turns a user goal into a phased plan and drives it to completion: `idle → planning → approval → executing → verifying → (replanning) → completed/failed`. `OrchestratorLoop` (engine) delegates plan generation to `orchestrator-planner` and per-phase verification gates to `orchestrator-verifier`, executing phases via team agents/`task-queue`. State persists under the `orchestrator` key in `state.json`. Distinct from Ralph (single-session autonomous loop) — orchestrator coordinates multi-phase, multi-agent execution. See `docs/orchestrator-loop-architecture.md`. diff --git a/docs/architecture-invariants.md b/docs/architecture-invariants.md index b981373f..3eee57b5 100644 --- a/docs/architecture-invariants.md +++ b/docs/architecture-invariants.md @@ -46,7 +46,7 @@ Implementation detail extracted from `CLAUDE.md` so that file stays small enough ### Plan-usage chip (statusLine telemetry) -**Plan-usage chip** (statusLine telemetry, opt-in `showPlanUsageLimits`, default OFF): Claude Code (v2.1.80+) pipes a JSON blob to a configured `statusLine.command` on each render; on Pro/Max it carries a `rate_limits` object (`five_hour`/`seven_day` windows only — no Opus weekly field — each `{used_percentage 0-100, resets_at epoch-SECONDS}`). Codeman injects its OWN statusLine exporter (`generateStatusLineCommand()` in `hooks-config.ts`, identified by the `/api/status-telemetry` marker — it only ever adds/updates/removes a statusLine that is _ours_, never a user's hand-authored one) that POSTs the blob to `POST /api/status-telemetry`. That route (auth-exempt like `/api/hook-event` — localhost-only, hook-secret-gated whenever auth is active, COD-91) parses via `usage-telemetry.ts` (pure, unit-tested), broadcasts SSE `session:statusTelemetry` (de-duped per session by `telemetrySignature` since the statusline fires on every assistant message), and returns a compact plain-text footer for the exporter to **print-through** (so injecting our statusLine doesn't blank the in-terminal footer). `plan-usage-latest.ts` holds the process-wide last value, replayed in the SSE init snapshot (`getLightState`) so the header chip (`#planUsageChip`, toggled by `showPlanUsageLimits` in settings-ui.js) renders immediately on page load / reconnect without per-browser localStorage. Claude-mode only. **Distinct from auto-resume** (which reacts to the limit _message_; this proactively shows the live %). Design: `docs/usage-limits-display-plan.md`. Tests: `test/usage-telemetry.test.ts`. +**Plan-usage chip** (statusLine telemetry, `showPlanUsageLimits`, per-device: desktop default **ON** since 1.9.3, handhelds OFF): Claude Code (v2.1.80+) pipes a JSON blob to a configured `statusLine.command` on each render; on Pro/Max it carries a `rate_limits` object (`five_hour`/`seven_day` windows only — no Opus weekly field — each `{used_percentage 0-100, resets_at epoch-SECONDS}`). Codeman injects its OWN statusLine exporter (`generateStatusLineCommand()` in `hooks-config.ts`, identified by the `/api/status-telemetry` marker — it only ever adds/updates/removes a statusLine that is _ours_, never a user's hand-authored one) that POSTs the blob to `POST /api/status-telemetry`. That route (auth-exempt like `/api/hook-event` — localhost-only, hook-secret-gated whenever auth is active, COD-91) parses via `usage-telemetry.ts` (pure, unit-tested), broadcasts SSE `session:statusTelemetry` (de-duped per session by `telemetrySignature` since the statusline fires on every assistant message), and returns a compact plain-text footer for the exporter to **print-through** (so injecting our statusLine doesn't blank the in-terminal footer). `plan-usage-latest.ts` holds the process-wide last value, replayed in the SSE init snapshot (`getLightState`) so the header chip (`#planUsageChip`, revealed by `planUsageChipEnabled()` in settings-ui.js, the single resolver behind the checkbox, the chip and the create-time `statusLineTelemetry` flag) renders immediately on page load / reconnect without per-browser localStorage. Claude-mode only. **Distinct from auto-resume** (which reacts to the limit _message_; this proactively shows the live %). Design: `docs/usage-limits-display-plan.md`. Tests: `test/usage-telemetry.test.ts`. ### Cron jobs diff --git a/docs/usage-limits-display-plan.md b/docs/usage-limits-display-plan.md index 24235961..fe351665 100644 --- a/docs/usage-limits-display-plan.md +++ b/docs/usage-limits-display-plan.md @@ -1,6 +1,6 @@ # Plan Usage Limits Display — Design & As-Built -> **Status: SHIPPED — deployed to prod + pushed to master, not yet released (2026-06-14).** Opt-in via App Settings → Display → **Plan Usage Limits** (`showPlanUsageLimits`, default OFF). Commits `c82f6c8` (feature) → `4d9d93d` (end-to-end fixes) → `eae225b` (per-user reconcile) → `95fb5fc` (init-snapshot replay). Full suite green (2869), CI green. No changeset/version bump yet. +> **Status: SHIPPED — deployed to prod + pushed to master, not yet released (2026-06-14).** App Settings → Display → **Plan Usage Limits** (`showPlanUsageLimits`). **Default changed in 1.9.3: desktop now defaults ON, handhelds stay OFF, resolved via `planUsageChipEnabled()`.** The per-device notes further down describing it as opt-in/synced record the original 2026-06-14 shape, not current behavior. Commits `c82f6c8` (feature) → `4d9d93d` (end-to-end fixes) → `eae225b` (per-user reconcile) → `95fb5fc` (init-snapshot replay). Full suite green (2869), CI green. No changeset/version bump yet. > > Two surfaces from one `statusLine` callback: > - **Header chip** (top-right) — account-wide **plan limits**: `5h 35% · 7d 38%`, per-window green/yellow/red. diff --git a/package-lock.json b/package-lock.json index 372953e7..dc203f7c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "aicodeman", - "version": "1.9.2", + "version": "1.9.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "aicodeman", - "version": "1.9.2", + "version": "1.9.3", "hasInstallScript": true, "license": "MIT", "workspaces": [ @@ -12333,7 +12333,7 @@ } }, "packages/xterm-zerolag-input": { - "version": "0.1.5", + "version": "0.1.6", "license": "MIT", "devDependencies": { "jsdom": "^24.1.3", diff --git a/package.json b/package.json index dabde2df..1ca86cc5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "aicodeman", - "version": "1.9.2", + "version": "1.9.3", "description": "Mission control for AI coding agents - run 20 autonomous agents with real-time monitoring and session persistence", "type": "module", "main": "dist/index.js", diff --git a/packages/xterm-zerolag-input/CHANGELOG.md b/packages/xterm-zerolag-input/CHANGELOG.md index 38869717..b272f832 100644 --- a/packages/xterm-zerolag-input/CHANGELOG.md +++ b/packages/xterm-zerolag-input/CHANGELOG.md @@ -1,5 +1,16 @@ # xterm-zerolag-input +## 0.1.6 + +### Patch Changes + +- Plan-usage chip now defaults ON on desktop, plus the reworked `xterm-zerolag-input` README. + - **Plan-usage chip defaults ON (desktop).** The `showPlanUsageLimits` chip (live 5-hour and weekly plan usage from the Claude statusline) used to be opt-in and default OFF, so most users never saw it. Desktop now defaults ON; handhelds still default OFF so the phone header stays minimal and the `mobile-header-buttons-policy` guard keeps passing. Devices with an explicitly stored preference keep whatever they chose, so nobody's OFF gets overridden. + - **One resolver behind the chip.** Added `planUsageChipEnabled()` in settings-ui.js and routed all three call sites through it: the App Settings checkbox, the chip's visibility, and the create-time `statusLineTelemetry` flag in session-ui.js. Those three had independent `?? false` / `=== true` defaults, and a chip revealed without the telemetry flag renders `—` forever, so a default flip on one site alone would have shipped a permanently empty chip. + - **Cron button comment corrected.** The App Settings comment claimed "Cron button defaults ON" while the code, the template (`btn-cron--hidden`) and the CSS all default it OFF. Verified against a fresh browser profile: the button is hidden and its checkbox unchecked out of the box. Comment now matches, and states why the two halves stay consistent. + - **Docs.** CLAUDE.md, `docs/architecture-invariants.md` and `docs/usage-limits-display-plan.md` updated for the new default and the single-resolver rule; the stale `styles.css` comment claiming the server strips the chip's hidden class at render was corrected (display is per-device, so the client reveals it). + - **`xterm-zerolag-input` README rework** (0.1.5 shipped the content; this republishes with the graphic and promo changes): replaced the misaligned 8-line keystroke-flow diagram with a two-line stock-vs-zerolag contrast, added a Codeman callout above the demo GIF with links to getcodeman.com and the repo, and rewrote the Origin section so it argues the extraction story instead of repeating the promo. + ## 0.1.5 ### Patch Changes diff --git a/packages/xterm-zerolag-input/package.json b/packages/xterm-zerolag-input/package.json index acb68637..d23a3567 100644 --- a/packages/xterm-zerolag-input/package.json +++ b/packages/xterm-zerolag-input/package.json @@ -1,6 +1,6 @@ { "name": "xterm-zerolag-input", - "version": "0.1.5", + "version": "0.1.6", "description": "Instant keystroke feedback overlay for xterm.js — eliminates perceived input latency over high-RTT connections", "type": "module", "main": "dist/index.cjs", diff --git a/src/web/public/session-ui.js b/src/web/public/session-ui.js index d3cbec61..87056507 100644 --- a/src/web/public/session-ui.js +++ b/src/web/public/session-ui.js @@ -716,7 +716,7 @@ Object.assign(CodemanApp.prototype, { // is shared by sibling sessions, so create-with-false must not yank it // — see the comment in session-routes create). Disabling the setting // removes it via the App Settings toggle path (system-routes), not here. - statusLineTelemetry: globalSettings.showPlanUsageLimits === true, + statusLineTelemetry: this.planUsageChipEnabled(globalSettings), }) }).then(r => r.json()) ); diff --git a/src/web/public/settings-ui.js b/src/web/public/settings-ui.js index 9e0c0d0c..2ea24c1d 100644 --- a/src/web/public/settings-ui.js +++ b/src/web/public/settings-ui.js @@ -325,9 +325,11 @@ Object.assign(CodemanApp.prototype, { document.getElementById('appSettingsUltracodeFloatingWindows').checked = settings.ultracodeFloatingWindows ?? defaults.ultracodeFloatingWindows ?? false; document.getElementById('appSettingsShowMultiMonitorButton').checked = settings.showMultiMonitorButton ?? defaults.showMultiMonitorButton ?? false; - document.getElementById('appSettingsShowPlanUsageLimits').checked = settings.showPlanUsageLimits ?? defaults.showPlanUsageLimits ?? false; + document.getElementById('appSettingsShowPlanUsageLimits').checked = this.planUsageChipEnabled(settings); document.getElementById('appSettingsShowRedrawButton').checked = settings.showRedrawButton ?? defaults.showRedrawButton ?? false; - // Session Manager + Away Digest buttons default OFF; Cron button defaults ON. + // Session Manager, Away Digest and Cron buttons all default OFF (opt-in under + // Display → Header Displays; the Cron button also ships with btn-cron--hidden + // in the template, so an unchecked box and a hidden button stay consistent). document.getElementById('appSettingsShowSessionButton').checked = settings.showSessionButton ?? defaults.showSessionButton ?? false; document.getElementById('appSettingsShowAwayDigestButton').checked = settings.showAwayDigestButton ?? defaults.showAwayDigestButton ?? false; document.getElementById('appSettingsShowCronButton').checked = settings.showCronButton ?? defaults.showCronButton ?? false; @@ -1798,6 +1800,9 @@ Object.assign(CodemanApp.prototype, { showUltracodeAgents: false, ultracodeFloatingWindows: false, showMultiMonitorButton: false, + // Desktop defaults this ON (see planUsageChipEnabled); handhelds keep it + // OFF so the phone header stays minimal and the mobile-header-buttons + // policy guard keeps passing. showPlanUsageLimits: false, showAttachmentsButton: false, showFileViewerButton: false, @@ -1889,6 +1894,18 @@ Object.assign(CodemanApp.prototype, { } }, + // Resolved per-device state of the plan-usage chip. Desktop defaults ON, + // handhelds default OFF (the mobile block in getDefaultSettings() sets false, + // and the mobile-header-buttons-policy guard depends on that staying false). + // Single source of truth for THREE call sites that must never disagree: the + // App Settings checkbox, the chip's visibility, and the statusLineTelemetry + // flag sent on session create. A chip shown without telemetry renders "—" + // forever, which is exactly the drift this helper prevents. + planUsageChipEnabled(settings = null) { + const s = settings ?? this.loadAppSettingsFromStorage(); + return s.showPlanUsageLimits ?? this.getDefaultSettings().showPlanUsageLimits ?? true; + }, + applyHeaderVisibilitySettings() { const settings = this.loadAppSettingsFromStorage(); const defaults = this.getDefaultSettings(); @@ -1967,11 +1984,13 @@ Object.assign(CodemanApp.prototype, { ultracodeBtn.classList.toggle('btn-ultracode-agents--hidden', !showUltracodeAgents); } - // Plan-usage chip — hidden by default (App Settings → Display → "Plan Usage - // Limits"). Server renders the initial state on reload; this handles a live - // toggle from a settings save. Marker class (base is display:inline-flex - // !important), matching the response-viewer/multimonitor pattern. - const showPlanUsageLimits = settings.showPlanUsageLimits ?? defaults.showPlanUsageLimits ?? false; + // Plan-usage chip — shown by default on desktop, OFF on handhelds (App + // Settings → Display → "Plan Usage Limits"). The template always ships it + // hidden because display is per-device and the server cannot know a + // localStorage value, so THIS is what reveals it on every load as well as + // on a live toggle. Marker class (base is display:inline-flex !important), + // matching the response-viewer/multimonitor pattern. + const showPlanUsageLimits = this.planUsageChipEnabled(settings); const planUsageChip = document.getElementById('planUsageChip'); if (planUsageChip) { planUsageChip.classList.toggle('header-plan-usage--hidden', !showPlanUsageLimits); @@ -2247,9 +2266,10 @@ Object.assign(CodemanApp.prototype, { 'terminalWheelLocalScrollback', 'showSessionButton', 'showAwayDigestButton', 'showCronButton', ]); - // The plan-usage chip is a PER-DEVICE display setting (default OFF): desktop - // can show it while mobile stays hidden. It used to sync, so an older - // server.json may still carry `true` — drop it so the server value is NEVER + // The plan-usage chip is a PER-DEVICE display setting (desktop default ON, + // handheld default OFF): desktop can show it while mobile stays hidden. It + // used to sync, so an older server.json may still carry a value — drop it + // so the server value is NEVER // seeded into a device that didn't explicitly enable it (collection is handled // separately via the statusLineTelemetry action, not this display flag). delete appSettings.showPlanUsageLimits; diff --git a/src/web/public/styles.css b/src/web/public/styles.css index ece3fe9c..4822390c 100644 --- a/src/web/public/styles.css +++ b/src/web/public/styles.css @@ -10248,9 +10248,10 @@ kbd { } /* Plan-usage chip (App Settings → Display → "Plan Usage Limits"). Shows the - live 5-hour + weekly plan limits parsed from the Claude statusline. Hidden by - default via the marker class below; the server strips it at render when the - setting is on and the client toggles it live on save. */ + live 5-hour + weekly plan limits parsed from the Claude statusline. Ships + hidden via the marker class below because display is PER-DEVICE and the + server cannot read localStorage; settings-ui.js reveals it on load (desktop + default ON, handhelds OFF) and on a live toggle. */ .header-plan-usage { display: inline-flex !important; align-items: center;