diff --git a/CHANGELOG.md b/CHANGELOG.md index 57070eaa..b09a78e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # aicodeman +## 0.6.6 + +### Patch Changes + +- **Terminal scrollback significantly increased** — both the xterm.js viewport and the tmux backing buffer were bottlenecking how far back you could scroll. Three changes: + - `DEFAULT_SCROLLBACK` raised from 20000 → 50000 lines (xterm.js, main terminal). The previous bump from 5000 only helped users with empty localStorage; existing users were stuck on whatever value they first picked up. The loader now treats `DEFAULT_SCROLLBACK` as a floor — if your stored value is below the new minimum, you're raised to it automatically. + - Subagent / teammate terminals (`panels-ui.js`) were stuck at 5000; now use the same `DEFAULT_SCROLLBACK` constant (50000). + - New tmux sessions now run with `history-limit 50000` (tmux defaults to 2000). This matters for hard-reload / re-attach — without it, only the last ~2000 lines survive the round-trip back into a fresh xterm. + + **Tmux flicker on session re-attach fixed (PR #80 by @aakhter)**: the PTY now queries the existing tmux window size via `tmux display -p` before spawning, instead of hardcoding 120x40. Previously, every re-attach forced tmux to resize down to 120x40, causing a visible flicker and one frame of scrollback loss. The `-x 120 -y 40` flag was also dropped from `tmux new-session` so the initial size matches the first attaching client. Uses `execFileSync` (not shell) for safety and falls back to 120x40 on any error. + + **Docs**: CLAUDE.md now documents two recurring foot-guns — the `xterm-zerolag-input` overlay code is duplicated between `packages/xterm-zerolag-input/src/` and inline inside `src/web/public/app.js`, so any overlay change must touch both; and the COM workflow explicitly includes a post-push `gh run watch` step to confirm CI before considering the release done. + ## 0.6.5 ### Patch Changes diff --git a/CLAUDE.md b/CLAUDE.md index 1a876bad..ce08b2f5 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -52,10 +52,11 @@ When user says "COM": 3. **Consume the changeset**: `npm run version-packages` (auto-bumps `package.json` files, updates `CHANGELOG.md`, runs `npm install --package-lock-only`, and verifies lockfile sync via `scripts/check-lockfile-sync.mjs` — all in one command; never hand-edit `CHANGELOG.md` or `package-lock.json` versions) 4. **Sync CLAUDE.md version**: Update the `**Version**` line below to match the new version from `package.json` 5. **Commit and deploy**: `git add -A && git commit -m "chore: version packages" && git push && npm run build && systemctl --user restart codeman-web` +6. **Wait for CI**: after `git push`, find the run with `gh run list -L 1 --json databaseId,headBranch -q '.[0].databaseId'` and watch it with `gh run watch --exit-status`. Confirm all checks pass before considering the release done. 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**: 0.6.5 (must match `package.json`) +**Version**: 0.6.6 (must match `package.json`) ## Project Overview @@ -94,6 +95,7 @@ Codeman is a Claude Code session manager with web interface and autonomous Ralph - **Global regex `lastIndex`** — Shared `g`-flag patterns in loops must reset `lastIndex = 0` first, or use the `execPattern()` helper in `utils/regex-patterns.ts` (resets automatically) - **`envOverrides` flow `CLAUDE_CODE_*` / `OPENCODE_*` env vars** — Set via `POST /api/sessions { envOverrides }`, stored on `Session._envOverrides`, exported by `tmux-manager.buildEnvExports()` at spawn time, persisted in `SessionState.envOverrides`. **Do NOT** write these to `/.claude/settings.local.json` — that's the old path and creates UI/disk drift - **Zod `.optional()` rejects `null`** — accepts `undefined` only. When the frontend builds a request body with `JSON.stringify`, an explicit `null` field is preserved on the wire and fails validation with `INVALID_INPUT`. Convert `null` → `undefined` before stringifying (e.g. `field: value ?? undefined`), or declare the schema `.nullish()`. Real bugs caused: 0.6.4 (`durationMinutes` for ∞ respawn), and the same shape pattern hit `opusContext1mEnabled` in 0.6.3 +- **`xterm-zerolag-input` is duplicated** — the local-echo overlay lives in BOTH `packages/xterm-zerolag-input/src/` (published package) AND inline inside `src/web/public/app.js` (runtime copy used by the web UI). Any change to overlay behavior MUST be applied to both, or dev and prod diverge. Always test on mobile after touching it. **Import conventions**: Utils from `./utils`, types from `./types` (barrel), config from specific `./config/*` files. diff --git a/package-lock.json b/package-lock.json index de0e1ff6..847d1421 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "aicodeman", - "version": "0.6.5", + "version": "0.6.6", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "aicodeman", - "version": "0.6.5", + "version": "0.6.6", "hasInstallScript": true, "license": "MIT", "workspaces": [ diff --git a/package.json b/package.json index 4b36f7d9..c5072210 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "aicodeman", - "version": "0.6.5", + "version": "0.6.6", "description": "The missing control plane for AI coding agents - run 20 autonomous agents with real-time monitoring and session persistence", "type": "module", "main": "dist/index.js", diff --git a/src/tmux-manager.ts b/src/tmux-manager.ts index bb3abe7f..880dbf46 100644 --- a/src/tmux-manager.ts +++ b/src/tmux-manager.ts @@ -609,6 +609,13 @@ export class TmuxManager extends EventEmitter implements TerminalMultiplexer { .catch(() => { /* Already set globally as fallback */ }), + // Raise tmux scrollback from its 2000-line default so re-attach preserves + // more context. Matches the xterm-side default in constants.js. + execAsync(`tmux set-option -t "${muxName}" history-limit 50000`, { timeout: EXEC_TIMEOUT_MS }) + .then(() => {}) + .catch(() => { + /* Non-critical — falls back to tmux default */ + }), ]; // Enable 24-bit true color passthrough — server-wide, set once per lifetime diff --git a/src/web/public/constants.js b/src/web/public/constants.js index 25566321..b977662c 100644 --- a/src/web/public/constants.js +++ b/src/web/public/constants.js @@ -43,7 +43,7 @@ function urlBase64ToUint8Array(base64String) { // ═══════════════════════════════════════════════════════════════ // Default terminal scrollback (can be changed via settings) -const DEFAULT_SCROLLBACK = 20000; +const DEFAULT_SCROLLBACK = 50000; // Timing constants const STUCK_THRESHOLD_DEFAULT_MS = 600000; // 10 minutes - default for stuck detection diff --git a/src/web/public/panels-ui.js b/src/web/public/panels-ui.js index 81bc4e94..ac84cb8b 100644 --- a/src/web/public/panels-ui.js +++ b/src/web/public/panels-ui.js @@ -1575,7 +1575,7 @@ Object.assign(CodemanApp.prototype, { lineHeight: 1.2, cursorBlink: true, cursorStyle: 'block', - scrollback: 5000, + scrollback: DEFAULT_SCROLLBACK, allowTransparency: true, allowProposedApi: true, }); diff --git a/src/web/public/terminal-ui.js b/src/web/public/terminal-ui.js index e43a62df..f804098e 100644 --- a/src/web/public/terminal-ui.js +++ b/src/web/public/terminal-ui.js @@ -18,8 +18,10 @@ Object.assign(CodemanApp.prototype, { // ═══════════════════════════════════════════════════════════════ initTerminal() { - // Load scrollback setting from localStorage (default 5000) - const scrollback = parseInt(localStorage.getItem('codeman-scrollback')) || DEFAULT_SCROLLBACK; + // Load scrollback setting from localStorage, treating DEFAULT_SCROLLBACK as a floor + // so users who picked up the previous (smaller) default get the new minimum on upgrade. + const stored = parseInt(localStorage.getItem('codeman-scrollback')); + const scrollback = Number.isFinite(stored) && stored > 0 ? Math.max(stored, DEFAULT_SCROLLBACK) : DEFAULT_SCROLLBACK; this.terminal = new Terminal({ theme: {