Knip-driven cleanup. All changes verified with tsc --noEmit, lint, and
build.
Removed (zero consumers):
- VERIFICATION_PROMPT constant + its barrel re-export
- createInitialOrchestratorPersistState factory
- transcriptWatcher singleton export
- createAnsiPatternFull / createAnsiPatternSimple factories
- TimerInfo interface + unused AiCheckResult/AiPlanCheckResult imports
in respawn-controller.ts
- 35 unused Zod z.infer \`*Input\` types in schemas.ts
- Dead re-exports: SessionMode from session.ts, AuthSessionRecord from
web/ports/index.ts, EnhancedPlanTask/CheckpointReview from
ralph-tracker.ts, 7 unused entries in utils/index.ts
- 14 event/config interfaces that lived only as JSDoc hints (no TS type
position usage): Session/Respawn/RalphLoop/RalphTracker/
SessionManager/SessionAutoOps/Subagent/TaskQueue/TaskTracker/
TranscriptWatcher/Image/OrchestratorLoop Events + RespawnPreset +
SessionOutput
Narrowed to module scope (kept but no longer exported):
- buildPermissionArgs in session-cli-builder.ts
- 28 type/interface declarations used only within their own file:
Ai{Idle,Plan}Check{Config,State}, BashToolParser{Events,Config},
FileStream/CreateStream{Options,Result}, PlanSubagentEvent,
SubagentCallback, RalphLoopConfig, RalphLoop{Events,Options},
ActiveTimerInfo, DetectionStatus, ActionLogEntry, AutoOpsCallbacks,
TunnelStatus, Timer/LRUMap/StaleExpirationMap Options, AuthState,
SessionListenerDeps, SseStreamManagerDeps, and 8 more
Docs: CLAUDE.md advice for global-regex `lastIndex` now points to the
remaining `execPattern()` helper instead of the deleted factories.
Knip delta: unused files 42→0, unused exports 161→16, unused types 92→0.
The 16 remaining exports are a mobile-test helper toolkit intentionally
kept for upcoming tests.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
POST /api/clipboard with { text } broadcasts to all connected browsers
via SSE. Browser attempts navigator.clipboard.writeText, falling back
to a modal with manual copy button if blocked.
Enables remote clipboard workflows: CLI tools can push text to the
user's browser clipboard across the network.
- Active tab: bright green border with color-matched glow per session color
- Tab number badges (1-9) showing Alt+N shortcut hints
- Badges update on tab reorder and re-render
On Android tablets, pressing Shift+A produces "AA" because the input
event listener re-sends characters that xterm already processed via
its keydown handler. Track keydown timestamps and skip input events
that fire within 50ms of a handled keydown.
Only affects touch devices (listener gated by isTouchDevice()).
/api/quick-start was always resolving caseName against CASES_DIR,
ignoring any entries in ~/.codeman/linked-cases.json. This caused
sessions to start in ~/codeman-cases/<name> even when the case was
linked to an external project directory.
Fix: read linked-cases.json first and prefer that path, falling back
to validatePathWithinBase only when no link is found.
Add marked.js (39KB) for rich text display in the response viewer.
Renders headings, code blocks, lists, tables, blockquotes, and
inline formatting with dark theme styling.
Falls back to escaped plain text if marked.js fails to load.
Claude Code's Ink framework uses alternate screen buffer + VPA cursor
positioning, resulting in near-zero xterm.js scrollback on mobile.
Instead of fighting terminal scrollback, this adds a native scrollable
overlay that reads structured responses from Claude's JSONL transcripts.
- New API: GET /api/sessions/:id/last-response reads transcript JSONL
- ?context=full returns full conversation thread (user + assistant)
- Fallback to terminal buffer with ANSI stripping if no transcript
- Response viewer panel: bottom sheet with native iOS/Android scroll
- "More" button loads full conversation context as threaded view
- Eye icon in header bar (mobile only), no toolbar space impact
Add app icons, update manifest with icon entries, and add app-shell
caching to service worker for offline/instant startup.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Show Codeman logo on mobile as compact home button (was hidden)
- Add "Show More" button for history sessions (initial 4, expand all)
- Deduplicate by projectKey instead of workingDir (lossy decode fix)
- Fix project key decoding: handle '_' encoded as '-' with look-ahead
- Pre-validate resumeSessionId before passing to Claude CLI
- Apply content validation to all session files regardless of size
The model validation regex rejected brackets, silently dropping models
like opus[1m]. Also quote the model flag to prevent bash glob expansion.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Filter readdir and linked-case names through /^[a-zA-Z0-9_-]+$/ before
returning them from GET /api/cases. Prevents XSS via maliciously-named
directories reaching frontend inline onclick handlers where escapeHtml
is insufficient (HTML-decoded back to quotes before JS execution).
Also fix misleading "Drag or use arrows" hint (no drag-and-drop exists).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add a "Manage" tab to the create-case modal with up/down reorder
buttons and delete for each case. Linked cases are unlinked (folder
preserved); CASES_DIR cases are permanently deleted.
Backend:
- DELETE /api/cases/:name — unlink or delete
- PUT /api/cases/order — persist ordering to settings.json
- GET /api/cases now respects saved caseOrder
Frontend:
- Third "Manage" tab in createCaseModal with case list
- Delete button in mobile case picker bottom sheet
- SSE events: case:deleted, case:order-changed
Previously, restoreMuxSessions() only created Session objects without
attaching PTY processes. Sessions stayed at pid=null until the client
manually selected them, causing terminals to appear "closed" after deploy.
Now the server calls startInteractive() for each recovered session during
startup, so all sessions resume capturing output immediately. The frontend
auto-attach condition is also relaxed from (pid===null && status==='idle')
to (pid===null && !_ended) as a safety net for edge cases.
Three fixes for macOS deployments:
1. HTML cache bug: @fastify/static with preCompressed serves .html.br/.html.gz
files, so path.endsWith('.html') missed them — HTML got 1-year immutable
cache headers instead of no-cache, causing stale pages after deploys.
2. Installer launchd support: macOS now gets proper LaunchAgent setup (like
systemd on Linux). Removes competing LaunchDaemons to prevent duplicate
services fighting over the port. Update/uninstall also handle launchd.
3. Trust dialog auto-accept: Claude CLI 2.x shows a workspace trust prompt
on first launch per directory. Sessions detect "trust this folder" in PTY
output and auto-send Enter, preventing sessions from hanging on startup.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Three fixes from the WIP flicker branch that were lost during master merges:
1. Move viewport clear (\x1b[3J\x1b[H\x1b[2J) inside the dimension-change
guard so it only fires when cols/rows actually change. Previously every
resize event cleared the screen even at identical dimensions, causing
visible flicker with no subsequent Ink redraw to repaint.
2. Sync _lastResizeDims in sendResize() so restoreTerminalSize() doesn't
trigger a redundant viewport clear on the next throttledResize tick.
3. Add didScroll tracking to touch events — tap (no scroll) now refocuses
xterm's hidden textarea, fixing mobile keyboard input routing after
tapping the terminal area.
Mobile users cannot press Shift+Tab on virtual keyboards. Add a ⇧Tab
button that sends the escape sequence (\x1b[Z) to the PTY, enabling
mode switching on mobile devices.
Also fix accessory bar overflow on narrow screens by making it
horizontally scrollable with hidden scrollbar.
Allow users to leave the default model unset, so sessions use whatever
the Claude CLI defaults to rather than forcing a specific model.
- Add empty-value "Default (CLI default)" option to the model dropdown
- Treat empty string as undefined when passing model to Session
- Apply consistently across session creation, quick-start, and Ralph
1. Rewrote getActiveChildProcesses() to use a single `ps --ppid` call
instead of two-level pgrep. The pane PID is typically claude itself
(bash exec'd into it), not a bash wrapper — so direct children of
pane_pid ARE the tool processes.
2. Added timer restart in tryStartAiCheck() when skipping due to child
processes. Without this, the pre-filter and no-output timers (both
one-shot) would never fire again, permanently stalling idle detection
for sessions with silent long-running processes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When Claude Code spawns bash tools (test suites, builds, servers), the
respawn controller could falsely detect idle if terminal output paused.
Now checks the process tree for active children of the Claude process
before triggering AI idle checks or confirming idle state.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Claude CLI's --output-format stream-json now returns "result": "" in the result
message. The actual response text lives in assistant message text blocks, which
_textOutput correctly accumulates. runPrompt() was returning the empty
resultMsg.result without falling back to _textOutput.value.
Also improved plan-orchestrator JSON extraction to try code-block-wrapped JSON
first (```json {...} ```) before the greedy regex, plus debug logging.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Thank you for the clean fix! The root cause analysis in the PR description was excellent — the strict Zod schema rejecting modelConfig during the GET-then-PUT pattern was a subtle bug.
Allow overriding MAX_TERMINAL_BUFFER_SIZE, TRIM_TERMINAL_TO, MAX_TEXT_OUTPUT_SIZE,
TRIM_TEXT_TO, and MAX_MESSAGES via CODEMAN_* env vars, falling back to existing
defaults. Enables users with fewer sessions or more RAM to tune buffer sizes
without patching source.
Closes#48
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Terminal flicker:
- Skip buffer-recovered/clear-terminal events during active buffer load
to prevent competing clear+rewrite cycles (app.js)
- Move viewport+scrollback clear inside dimension-change guard so resize
without actual SIGWINCH doesn't blank the terminal (terminal-ui.js)
- Sync _lastResizeDims on explicit resize to prevent redundant clears
CJK input rewrite (input-cjk.js):
- Use InputEvent.inputType to distinguish insertText (final) from
insertCompositionText (tentative) — fixes Chinese punctuation and
English text being swallowed during Android IME composition
- Remove isComposing guard on Enter so it always sends
- Phantom character (U+200B) keeps textarea non-empty so Android
long-press backspace generates continuous deleteContentBackward
events at the keyboard's native repeat rate
CJK input settings:
- Add "CJK Input" toggle in Settings > Input (index.html, settings-ui.js)
- Store as device-specific setting (cjkInputEnabled), not synced to server
- Replace INPUT_CJK_FORM env var dependency with user-controlled setting
(env var still works as server override)
Mobile layout:
- Fix welcome screen overflow on phones by constraining .welcome-content
to calc(100vw - 1.5rem) (mobile.css)
- Move xterm helper textarea on-screen for touch devices to fix iOS
keyboard input (styles.css)
- Focus terminal synchronously in user-gesture context for iOS Safari
keyboard activation (session-ui.js, app.js)
- Refocus terminal on tap (not scroll) in touch handler (terminal-ui.js)
Tailed terminal buffers contain multiple CUP-positioned Ink frames from
different time points. When replayed in xterm, old frames at viewport
positions not covered by the latest frame persist as ghost content
(e.g. duplicate "bypass permissions" bars). After buffer load, send
Ctrl+L via the session input API to trigger a full Ink redraw, which
overwrites all stale frame content with the correct current state.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Two fixes for the tab-switching corruption bug:
1. _finishBufferLoad() now discards queued SSE events instead of flushing
them. The loaded API buffer is the source of truth — queued events
overlap with it, and flushing them writes duplicate Ink cursor-up
redraws that corrupt the terminal display (garbled text, wrong cursor
positions).
2. Skip stale cache write for busy sessions. When a session is actively
working, the cache is always outdated — writing it first and then
rewriting with the fresh API buffer caused a jarring double-render
flash. Now busy sessions get a single clean clear+write transition.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>