codeman@1.14.2
Four reported bugs fixed, and the Codeman agent skill from 1.14.1 gets its first published build with the fixes below alongside it.
The Codeman agent skill
Introduced in 1.14.1 and the headline of this line. skills/codeman is a Claude Code skill that lets an agent running inside a Codeman session drive the HTTP API: start worker sessions, send them prompts, block until they finish, read their answers and clean up. It ships in the npm package and self-gates, so outside a Codeman session (CODEMAN_MUX unset) it refuses to act and costs unrelated sessions nothing.
Installing it
codeman skill install # ~/.claude/skills/codeman, every new Claude Code session sees it
codeman skill install --case myproject # just that case; linked cases resolve by name too
codeman skill uninstall # reverses either oneOr turn on App Settings > Agent Skill (agentSkillEnabled, synced, default off) and Codeman injects the skill into each case when a Claude session is created there.
Installs are marker-owned: a skills/codeman that Codeman did not write is never touched, a stale managed copy is refreshed in place, and a symlinked skill directory is refused rather than written through. Re-run codeman skill install after upgrading to refresh the copy. Turning agentSkillEnabled back off does not remove already-injected copies, because a create-time sweep would yank the skill out from under other live sessions sharing that .claude/ directory; remove them per case with codeman skill uninstall --case <name>.
Using it
Ask for orchestration in plain language ("spin up three workers, have them lint, typecheck and test in parallel, then report back") and the skill supplies the guard, the safety rules and the recipes. The flow it runs:
- Guard. Re-runs a preamble on every shell call that refuses outside
CODEMAN_MUX=1, readsCODEMAN_API_URLandCODEMAN_SESSION_ID, recovers a password from the data dir.envor the install's service definition if one is set, and defines a fail-closeddelete_session. It re-runs it every call because shell state does not survive between an agent's tool calls. - Start a worker with
POST /api/v1/quick-start(modeis any ofclaude,shell,opencode,codex,gemini,antigravity), checking.successbefore reading.data.sessionId. - Wait until it is really ready. A new session reports
idlebefore its CLI has spawned, and a brand-new case shows a trust dialog first, so the skill waits for the composer's own status bar and treats the dialog as a bounded fallback. - Send and wait in one call:
wait/waitTimeoutonPOST /api/v1/sessions/:id/input. It registers the waiter before typing, closing the race where a separate wait reports the previous turn's idle state as this turn's answer. Forclaudeworkers it resolves on thestophook, usually within seconds. - Read the answer from
GET /api/v1/sessions/:id/last-response, which returns clean transcript text rather than a screen scrape. - Clean up with
delete_session, for ids it created and nothing else.
Hook-less modes (shell and the external CLIs) have no stop signal and coarse lifecycle transitions, so the skill synchronizes those with a unique split marker and wait-output ... from=buffer. Worked fan-out flows, the per-mode signal table, error codes and the Docker/remote caveats live in the skill's reference/ files, loaded on demand.
The rules it encodes
Each of these silently wastes a run, which is why they are written down: every input must end with \r or Enter is never sent; input is single-line; a wait timeout is HTTP 200 with wait.timedOut, not an error; stop and blocked are claude-only; signals are edge-triggered with no history, so never fire-and-forget N prompts and then gather signal-waits one by one; a typed command echoes into the output stream, so markers must be split; a full-screen TUI stream is space-less, so match single tokens; and pid != null proves startup, not life, so wait?until=exit is the death check.
Bug fixes
- Web tabs: long-running proxied requests were aborted after 30 seconds with no server log (#237). The proxy wrapped each upstream fetch in a 30s
AbortSignal.timeout, which bounds the entire exchange rather than the wait for response headers, so a dashboard endpoint doing model inference and any actively streaming response both died at 30s as a generic unlogged 502 that read as an intermittent network error. The timeout now bounds time-to-headers only and is cleared the moment headers arrive, with the default raised to 300s (CODEMAN_WEBVIEW_TIMEOUT_MS). Header timeouts are logged with a sanitized identity (method plus origin plus path, never the query string, which can carry the dashboard's tokens). A browser that navigates away mid-request now aborts the upstream fetch, guarded bywritableFinishedso a completed response never triggers it. The WebSocket handshake keeps its own 30s budget via the newCODEMAN_WEBVIEW_WS_HANDSHAKE_TIMEOUT_MS, since a handshake is connection establishment and waiting minutes on one only delays the browser's reconnect logic. - Web tabs: sandbox incompatibility with cookie-authenticated reverse proxies documented (#238).
docs/web-tabs.mdnow covers cookie auth in front of Codeman itself (Cloudflare Access and similar), where a sandboxed frame's asset and API requests carry no auth cookie, bounce to the login provider, and leave the embedded app apparently unstyled while trusted mode works. The Test button's result now states its own scope: it verifies server-to-upstream reachability, not how the page behaves in a sandboxed frame. - A described session tab now shows just the description (#232). A session named
w2-foo-bar: some descriptionrendered both halves, so the generated id ate the width the chosen part needed. The tab shows the description alone, thew<n>-<case>id moves to the tooltip and stays in the session settings modal, andaria-labeldeliberately keeps the full name so screen readers still get the id. Undescribed tabs are unchanged. Right-click a tab to rename it inline. This also fixed a re-render loop: the incremental update compared against the full name, which a described tab never matched, so those tabs re-rendered on every pass. codeman statusnow probes the running server (#230). The command runs in its own fresh process and reported that process's always-stopped Ralph loop under a bare "Status:", which reads as "the server is down" while the service is running fine and agents are reachable. It now probes the real server (CODEMAN_API_URL, else https then http on the local port, overridable with--url) and reports reachability, version and live session state; any HTTP answer proves the server is up, including a 401 from a password-protected install. The Ralph loop keeps its owncodeman ralph status. This complementscodeman web --statusfrom the daemon work: that answers "did I start a daemon", this answers "is a server running at all".
Thanks
- @DodgyBadger for #237, a precise report with environment, repro and the "works outside the tab, intermittent through Codeman" framing that placed the bug immediately, and for #238 including the observation that a passing Test button was itself misleading.
- @mtiller for #232 and #230. #232 also surfaced a second bug nobody had noticed: described tabs were re-rendering on every pass.