Commit Graph
3155 Commits
Author SHA1 Message Date
Codeman maintainer ce809eac41 chore: version packages
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
codeman@1.42.0
2026-10-16 13:23:48 +02:00
Codeman maintainer 25bd792550 docs(changeset): add the multi-user admin-only settings fix
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 13:23:40 +02:00
Codeman maintainer 973943e942 docs(multi-user): document the settings read, system operations and own picks
- docs/api-reference.md: a non-admin's GET /api/settings omits
  voiceSettings.apiKey, caseOrder and lastUsedCase; a new "System
  operations" table for the admin-only routes and the scoped
  POST /api/auth/revoke; the error-code table now lists FORBIDDEN and the
  other multi-user codes (PASSWORD_CHANGE_REQUIRED, USER_NOT_FOUND,
  USER_EXISTS, LAST_ADMIN).
- docs/wiki/Multi-User-Mode.md: what a regular user reads, that their run
  mode and respawn presets stay theirs on the device, the slow sign-in
  case, and the admin-only system operations.
- docs/multi-user-plan.md: Settings and System ops rows marked implemented.
- CLAUDE.md: the Multi-user mode paragraph names the new pieces and tests.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 13:13:35 +02:00
Codeman maintainer 5a297cd3e4 fix(multi-user): read a refused settings write as admin-only in the browser
Two gaps in how a regular user's browser handled the admin-only settings
writes:

- The role comes from GET /api/me, which answers after page load, and on
  a failed request admin-ui.js falls back to a single-user admin. A save
  made then was sent, came back 403, and still said "Settings saved",
  while the refused server-wide change stayed on the device. A 403
  FORBIDDEN on a settings write now ends that save like a known
  non-admin's: synced keys back to the server's value, the one admin-only
  notice, and the page is read-only from then on (_noteSettingsWriteRefusal).
  The display-key list moved into one method (_displaySettingKeySet), so
  that split no longer depends on GET /api/settings having loaded.
- A regular user's run mode and respawn presets were skipped for the
  server but overwritten by the server's on the next page load, while
  "Saved preset" said otherwise. They are now kept as that user's own on
  the device and put back over the server's after the settings load and
  on codeman:me (_rememberOwnSetting, _applyOwnSettings).

Admins and single-user mode are unchanged.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 13:12:09 +02:00
Codeman maintainer 24a1c6c427 fix(multi-user): make the server-wide system operations admin-only
docs/multi-user-plan.md lists the system operations as admin-only, but
these routes had no role check, so any signed-in regular user could run
them in multi-user mode:

- POST /api/auth/revoke with an empty body cleared EVERY login session on
  the server, every admin's included. A non-admin now revokes only their
  own logins, and a named sessionToken only when it is one of theirs.
- POST /api/system/update (update and restart the server for everyone),
  POST /api/system/span-displays (a window on the server's desktop),
  POST /api/tunnel/qr/regenerate (clears every user's pending QR login
  code) and POST /api/cleanup-state (drops every user's stale session
  records) now answer 403 FORBIDDEN to a non-admin.
- GET /api/tunnel/info lists only a non-admin's own logins (it showed the
  address and browser of every user's sessions).

Admins and single-user mode are unchanged. The tunnel panel's Regenerate
QR button no longer reports success for a refused request.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 13:11:41 +02:00
Codeman maintainer 33f0bc423f fix(multi-user): keep the admin's Deepgram key out of a non-admin's settings read
GET /api/settings returned the whole settings.json to every signed-in user.
An admin's App Settings save stores the Deepgram key there
(voiceSettings.apiKey), so any regular user could read it, and their
browser copied it into its own voice config and dictated on the admin's
account. caseOrder and lastUsedCase also named the admin's cases.

In multi-user mode a non-admin now reads the file without those three
(settingsForNonAdmin). Admins and single-user mode get the file as it
is. Pinned by route tests and the live multi-user test.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 13:10:59 +02:00
Codeman maintainer 31c03ab62d docs(multi-user): server-wide settings writes are admin-only
api-reference gains a Settings section (GET/PUT /api/settings, the model
config, the case order and PUT /api/config, with the 403 FORBIDDEN a non-admin
gets in multi-user mode). The Multi-User Mode wiki page explains what a regular
user's App Settings save does now, Settings-Reference links it, the plan's
Settings row is marked implemented, and CLAUDE.md notes the gate for the
Multi-user paragraph and for anyone adding a settings write.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 12:18:20 +02:00
Codeman maintainer cb928aefe2 test(multi-user): pin admin-only settings writes, server and browser
- test/routes/system-routes-settings-admin-only.test.ts (app.inject): a
  non-admin PUT /api/settings is 403 FORBIDDEN before the body is parsed, and
  nothing is written, no watcher toggled, no tunnel started, no history limit
  set; the same gate on PUT /api/execution/model-config, PUT /api/config and
  PUT /api/cases/order; admins and single-user mode write as before; GET stays
  readable.
- test/settings-admin-only-multiuser.test.ts (live server, port 0, real
  multi-user auth): settings.json is byte-identical after a non-admin's PUTs,
  and a non-admin's quick-start or Ralph loop no longer records lastUsedCase.
- test/settings-admin-only-ui.test.ts (real index.html in JSDOM): a non-admin
  App Settings save sends no PUT, keeps per-device keys, keeps synced keys at
  the server value, and toasts once only on a real synced change; the
  background writes are skipped; admins still PUT; zh-CN for both toasts.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 12:16:34 +02:00
Codeman maintainer a798c461db fix(multi-user): skip server-wide settings writes for a non-admin browser
With PUT /api/settings admin-only in multi-user mode, a non-admin's page must
not run into the 403. `_settingsReadOnly()` (settings-ui.js) reads the role
from window.__codemanUser; single-user mode and admins are unchanged.

For a non-admin, App Settings Save/Apply still saves the per-device keys to
localStorage exactly as before, but sends neither the settings PUT nor the
model-config PUT, and keeps every synced key at the server's value (the next
page load would put it back anyway). Only when the user really changed a
synced setting do they get one toast that server-wide settings can only be
changed by an admin; a form baseline taken at open keeps an untouched default
that no admin ever stored from reading as a change. The background writes
(run mode, respawn presets and their load-time migration, last used case,
case order, the tunnel toggles) are not sent, and a 403 FORBIDDEN on a tunnel
enable is no longer mistaken for the no-password prompt. zh-CN for both toasts.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 12:12:50 +02:00
Codeman maintainer df0ece6fb4 fix(multi-user): make server-wide settings writes admin-only
In multi-user mode any signed-in non-admin could rewrite the server-wide
~/.codeman/settings.json through PUT /api/settings (feature switches, the
plan-usage collection switch, run mode, respawn presets, the tunnel), although
docs/multi-user-plan.md makes settings writes admin-only.

PUT /api/settings now answers 403 FORBIDDEN for a non-admin before anything is
parsed, written, toggled or started. The other request paths that write a
server-wide setting get the same gate: PUT /api/execution/model-config (the
modelConfig key), PUT /api/config (state.json app config) and
PUT /api/cases/order (the one global caseOrder list). Quick-start and
ralph-loop start no longer record a non-admin's lastUsedCase in settings.json,
where every user could read it. GET /api/settings stays readable by everyone;
single-user mode is unchanged (requireAdmin short-circuits there).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 12:06:05 +02:00
Codeman maintainer b4aed4e485 fix(release): stamp the codeman skill preamble 1.42.0
#642 stamped the preamble with the version it expected to ship in, 1.41.2. The release is 1.42.0, so the stamp would name a version that never existed. Any new value makes cached preambles from the broken version stale, which is what the stamp is for.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 11:43:47 +02:00
Codeman maintainer ab9fc1b424 docs(changeset): make it 1.42.0 (minor) and add #618
The owner chose a minor release. #618's own changeset is folded into the release changeset like the others.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 02:40:15 +02:00
Codeman maintainer 15a73dfa05 Merge pull request #618 from opticon454/feat/whats-new
feat: announce each release once with a toast generated from the changelog

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 02:39:49 +02:00
Codeman maintainer 123831660a docs(changeset): 1.41.2 release notes
One release changeset for #616, #617, #621, #622, #623, #624, #625, #628, #632, #634, #635 and #642. The changesets carried by #616, #617 and #642 are folded into it and removed (#617's was minor; 1.41.2 is a patch release).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 02:05:14 +02:00
Codeman maintainer 5483811753 Merge pull request #625 from Ark0N/feat/git-worktrees
feat(worktrees): create, open and remove git worktrees of a case

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 02:00:32 +02:00
Codeman maintainer c76600315e Merge pull request #624 from Ark0N/feat/codex-blocked-detection
feat(codex): raise codex approval, question and trust dialogs as BLOCKED

Conflict resolved against #634 in src/session.ts _probePaneWorking():
#624's assignment (a dialog on screen is never work) is kept, and #634's
trust-dialog verdict block (log the hold once, remember the reading)
follows it, before _readWatching().

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 02:00:29 +02:00
Codeman maintainer 9aa3083280 Merge pull request #634 from JDProfresh/fix/idle-not-on-trust-dialog
fix(idle): hold idle while the workspace trust dialog is on screen

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 01:59:55 +02:00
Codeman maintainer eec2895dd7 Merge pull request #635 from JDProfresh/fix/trust-dialog-deferred-read
fix(trust-dialog): re-read a throttled trust-dialog scan instead of dropping it

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 01:59:55 +02:00
Codeman maintainer d080004436 Merge pull request #617 from opticon454/feat/custom-keybindings
feat(shortcuts): rebindable previous/next session, duplicate-chord refusal, rebinds stay out of the PTY

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 01:59:51 +02:00
Codeman maintainer 5eccaca3bc Merge pull request #616 from opticon454/fix/deepseek-install-path
fix(deepseek): find pnpm when installing the terminal profile, and install it with dsh

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 01:59:51 +02:00
Codeman maintainer 16bbd260e2 Merge pull request #642 from Ark0N/fix/skill-positional-args
fix(skill): keep the §0 bootstrap intact when Claude Code loads the skill with arguments

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 01:59:46 +02:00
Codeman maintainer 8084cae0fe Merge pull request #628 from Randalix/fix/codex-submit-paste
fix(tmux): submit text as a bracketed paste so Codex does not swallow Enter

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 01:59:46 +02:00
Codeman maintainer c5417abd27 Merge pull request #632 from ludovicfourrage/fix/capture-pane-sgr-reset-629
fix(tmux): reset captured SGR before live output resumes

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 01:59:46 +02:00
Codeman maintainer 6e4181a2e6 Merge pull request #622 from JDProfresh/fix/attach-video-containers
fix(uploads): refuse an unsupported video before uploading it

Conflict resolved against #621 in src/web/public/image-input.js (the
_promptAttachKind doc comment only): #621's comment is kept with #622's
clause that a video MIME counts only for the containers the server
admits; #622's video MIME check and #621's document branch both stay.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 01:59:42 +02:00
Codeman maintainer 0866090bc3 Merge pull request #621 from JDProfresh/feat/prompt-upload-documents
feat(uploads): prompt uploads take documents

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 01:59:23 +02:00
Codeman maintainer 48637e9920 Merge pull request #623 from JDProfresh/fix/prompt-cache-report-bounds
fix(telemetry): gate the prompt cache like the model report, and keep the first 20 miss causes

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 01:59:23 +02:00
Codeman maintainer 4b73ec8417 fix(#624): read codex's trust prompt as BLOCKED when it ends in esc back
On codex-cli 0.162.0, with 0.163.0 released, every launch first shows the
"Update available" prompt, and choosing Skip leads to a variant of the
workspace trust prompt: "2. Back to Agent Command Center" over the key-hint
row "enter continue · esc back". The pattern only accepted cancel,
interrupt, quit, answer or all as the closing word, so a fresh git repo
launched from Codeman sat on that trust prompt reading idle, with no red
alert and no blocked signal (measured live on a throwaway instance).

Adding "back" is safe because the hint row must still START with one of
the anchored hints ("enter continue" here): the popups the user opens at
the keyboard (/model, /permissions, /review, Implement this plan?) end in
"esc back" too, but their row starts with "enter select", so they stay
unmatched, as every existing negative fixture confirms. The new fixture is
the live capture with the scratch path replaced.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 01:42:40 +02:00
Codeman maintainer 688bdfb970 fix(#616): install pnpm only when none is on PATH
The DeepSeek install command asked npm for dsh and pnpm in one go. On a
machine that already has pnpm through corepack (`corepack enable` puts a
pnpm shim in node's own bin directory, which is npm's global bin for nvm
and Homebrew installs), `npm install -g pnpm` refuses to overwrite that
shim with EEXIST, and because both packages were in the same command the
whole install failed: dsh was not installed either. Measured with npm 10
against a scratch prefix holding a corepack-style shim. On master the same
machine installed dsh fine and the profile install used the existing pnpm.

The command is now `npm install -g @deepseek-ai/dsh && (command -v pnpm
>/dev/null || npm install -g pnpm)`, so pnpm is installed only when the
install environment has none, and a failed dsh install still fails the
command. The two hints that embed it follow (the launch-table drift test
requires that), and the catalogue is regenerated; install.sh withholds
the command for a launcher-only CLI, so its block is unchanged.

The test now runs the real stock string through /bin/sh with a fake npm
on a PATH it controls, for the three cases: no pnpm, an existing pnpm,
and a failing dsh install.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 01:37:41 +02:00
Codeman maintainer bea9865cff fix(#625): never remove a worktree that is, or holds, a Docker case workspace
The Docker rule was applied to the CASE folder and its common git dir
only. A Docker case can be linked to any host folder, including a managed
worktree (`<repo>/.claude/worktrees/<name>`), which is a natural setup for
running one of the parallel agents in a container. Removing that worktree
from the local case's dialog, or through Close Session's "Kill & remove
worktree", ran `git worktree remove`, which runs a git status INSIDE the
tree (container-written files, checkout filters on this machine) and then
deletes the whole tree, ignored files included, while the container still
mounts it. Reproduced on an isolated instance: DELETE answered 200 and the
Docker case's workspace was gone.

That contradicts the invariant the PR itself documents in CLAUDE.md ("git
never runs ... at or inside a Docker workspace"). removeCaseWorktree now
refuses (400) a present worktree that is, sits inside, or holds a Docker
case workspace, before any git runs; the DELETE route passes the Docker
roots it already reads for the case check. A worktree whose folder is gone
is unaffected (git only drops its record there). Unit and route tests
cover both directions and an unrelated Docker workspace; docs updated.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 01:36:45 +02:00
Codeman maintainer a61db218ee fix(#618): translate the release toast for zh-CN
The toast every user sees once per release had no zh-CN entries, so a
Chinese interface showed it, button included, in English. The button
gets an entry and the frame a translateDynamic pattern that keeps the
version, the "(+N more)" count and the highlights (those come from
CHANGELOG.md and stay English). Tested through the real i18n.js in JSDOM,
plus a pin on the strings settings-ui.js builds, which the pattern
depends on.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 01:28:54 +02:00
Codeman maintainer 71439c525b fix(#618): document whats-new.json in the release procedure
docs/releasing.md step 3 lists everything `npm run version-packages`
writes, and the COM commit stages explicit paths, so the new generated
file has to be named there: committed with the release, the version
check in test/whats-new.test.ts that fails a hand bump (with the two
commands that regenerate it), and that a release without bold lead-ins
ships an empty list and no toast.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 01:27:54 +02:00
Codeman maintainer f2154a5d33 fix(#618): read lead-ins behind the changesets commit prefix, skip group labels
`changeset version` prefixes the first line of every committed changeset
with its commit ("- d18deed: **Lead.** text"), so a changeset that opens
with its bold lead-in, which is the shape the release notes ask for, was
never matched in a changesets-bot release. The pattern now allows that
prefix.

The release notes also put group labels in bold at the start of a line:
"**Fixes.**" and "**Fixes applied while landing.**" (1.41.1, 1.41.0,
1.35.0, 1.33.3). Those were taken as highlights, so a release with few
features announced "... · Fixes applied while landing". They are skipped
like the Thanks block. Tests cover both, and a release with no lead-ins.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 01:27:26 +02:00
Codeman maintainer 02ea9251f8 fix(#618): keep the release commit green when a release has no bold lead-ins
The shipped-json test asserted at least one highlight. Many releases are
written without bold lead-ins (1.32.1, 1.29.1 and 1.24.7 here, and every
changesets-bot release whose changesets are plain sentences, this PR's own
changeset included), and the generator handles that on purpose: it warns
and writes an empty list, and the client then stays quiet. Running
`npm run version-packages` in a scratch copy with only this PR's changeset
produced `{"version":"1.42.0","highlights":[]}` and turned the gate red
on exactly the release commit. The version check, which is what keeps a
hand bump from shipping the previous release's notes, stays.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 01:27:26 +02:00
Codeman maintainer 9117b183a1 fix(#621): correct three upload doc claims
The wiki said .codeman-uploads/ is the one path "a local agent, a container
and a remote host all see the same way", two sentences after saying uploads
are refused for remote (SSH) sessions. Only a local agent and a Docker case
(workspace bind-mounted at the same absolute path) share it, which is why the
route refuses remote sessions; say that.

The same paragraph said every image goes in after a re-encode on the device,
but _normalizeImageForUpload passes a GIF through untouched to keep its
frames.

CLAUDE.md and a test comment described the image fallback as behaving
"as before 1.36", a release that never existed (1.35.0 was followed by
1.40.0). It means the image path as it was before this change.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 01:23:28 +02:00
Codeman maintainer b6218c1490 docs(readme): link the Codeman Skill wiki page from both READMEs
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 00:46:29 +02:00
Codeman maintainer 4eef417da6 fix(skill): keep the §0 bootstrap intact when Claude Code loads the skill with arguments
Claude Code rewrites a skill body that it loads with arguments: every bare
$1, $2 ... becomes the matching word of the request (0-based). Nine lines of
the agent preamble used bare positional parameters, so an agent that pasted
the §0 bootstrap (every session without a pre-seeded preamble: Agent Skill
injection off, the default, or a remote or Docker session) wrote helpers
that queried session "5" with timeout=claude and looked up "5=" instead of
CODEMAN_PASSWORD= in the .env, sending every call unauthenticated.

- $1/$2 -> ${1}/${2} in preamble.sh and the identical §0 heredoc in
  SKILL.md; same bash, and Claude Code's pattern (\$\d+(?!\w)) skips it.
- CODEMAN_PREAMBLE 1.33.4 -> 1.41.2, so a cache file written by the broken
  bootstrap (which carries the old stamp) is rewritten on the next run.
- A comment at the top of the preamble states the rule.
- New test: Claude Code 2.1.300's substitution applied to SKILL.md with
  sample arguments must leave the body, and the pasted heredoc, unchanged.
  It fails on the old file with the exact sessions/5 and timeout=claude lines.
- plugins/codeman re-synced; patch changeset.

Verified with real Claude Code 2.1.300 (the delivered heredoc is now
byte-identical to preamble.sh; before, 9 lines differed) and by running the
pasted helpers against a local recording server.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 19:33:09 +02:00
Codeman maintainer df2bdc8a9e docs(wiki): add a Codeman Skill page with the skill GIFs
The skill only had a section inside "Driving Codeman From An Agent". It now
has its own page, docs/wiki/Codeman-Skill.md, built around three real runs:

- five Claude Code workers powering on in the tile grid (2026-10-15)
- eight worker tabs with lineage lines back to the lead (2026-10-15)
- a DeepSeek Harness worker and a Claude Code worker from one prompt
  (2026-10-10)

plus every install route, prompts that work as written, a run start to
finish, which workers it can drive, what it will and will not do on its
own, the Codex notes and what is in the skill. The page says that the grid
recording put each worker on the grid as it started, since sessions an
agent starts do not join the grid by themselves.

The agent page keeps a short skill section (the heading anchor stays) and
links to the new page; the sidebar, Home, FAQ and Settings Reference link
to it too. The FAQ answer no longer says the skill is enabled per case only.
docs/media-assets.md records the new embeds.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 19:10:38 +02:00
Codeman maintainer 11e8ac0b2d docs(readme): add two codeman skill GIFs (5 tiles, 8 tabs with lineage)
Two live captures of a Claude Code lead driving the codeman skill, added to
the agent skill section of both READMEs:

- Step 2: `use codeman to start 5 claude workers, one per TODO item`, five
  Claude Code workers power on as tiles in the grid (1120x630, 17 s, 10 MB).
- Step 3: the same prompt with 8 workers in the normal view, eight tabs
  power on with lineage lines back to the lead, then a click into a live
  worker (1120x630, 16 s, 8 MB).

Real sessions only, cut from the same raw takes as the promo videos; the
time-lapse stretches are labelled on screen. CRT beats play at 50 fps, the
busy time-lapses at 25 fps, one global palette, no lossy pass. Privacy OCR
on every second frame: clean. docs/media-assets.md lists both.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 18:17:34 +02:00
JD 2a5f5b30be fix(idle): hold idle while the workspace trust dialog is on screen
The dialog's own prompt glyph armed idle confirmation and the quiet dialog passed it, so a fresh case reported idle about 4 s in. An agent that waited for idle and then prompted typed into the dialog, and its Enter confirmed No, exit and killed the pane.
2026-10-14 22:16:49 -04:00
JD 892c2fc4df fix(trust-dialog): re-read a throttled trust-dialog scan instead of dropping it
The scan must schedule its own next read rather than wait for PTY output, but a call inside the throttle was simply dropped. The dialog's own paint is often the last output a launching pane produces and lands inside that throttle, so the session sat on the dialog with no keystroke ever sent. A stopped session no longer scans, since the kill's own output kept it answering a dialog in a dead pane.
2026-10-14 22:03:15 -04:00
Devvyn d18deed51d feat: announce each release once with a toast generated from the changelog 2026-10-15 04:22:26 +08:00
RandalixandClaude Opus 5.5 7fd76f06c3 fix(tmux): type a single-character submit so a dialog answer selects
Claude Code's and Codex's selection dialogs ignore a bracketed paste, so
after the previous commit a pasted `3` + Enter confirmed the highlighted
option instead of option 3. sendInput() now types a single character with
`send-keys -l` as master did (submitsAsKeys()). One character cannot form
the burst Codex reads as a paste, so the Codex fix is unaffected.

The trailing-`;` escape moves into escapeTmuxTrailingSemicolon() and now
also covers every `send-keys -l` call in sendInput() and
sendInputToPane(); tmux 3.5a types `x;` for `send-keys -l 'x\;'`.

Docs: the @fileoverview lines and orchestrator-loop-architecture.md still
described `send-keys -l`; CLAUDE.md, architecture-invariants.md and
cron-guide.md name the single-character exception.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 21:23:58 +02:00
Ludo Fourrage 96f6cdea8c fix(tmux): reset capture styles before live output resumes 2026-10-14 18:07:32 +00:00
RandalixandClaude Opus 5.5 344edc942b fix(tmux): submit text as a bracketed paste so Codex does not swallow Enter
sendInput() typed the text of a text+Enter pair with `send-keys -l` and
sent Enter 50 ms later. Codex detects an unmarked paste from a fast
character burst and folds an Enter inside that window into the paste as a
newline, so the prompt stays in the composer unsubmitted. Measured with
codex-cli 0.162.1 in a throwaway tmux pane: 1505 chars stuck 3/3 as
`[Pasted Content 1506 chars]`, a 98-char line stuck 1/4. SubmitVerifier
re-presses Enter for the short case, but it cannot match the long one: the
composer shows the placeholder instead of the prompt's head.

The text now goes in via set-buffer + `paste-buffer -p -d` (markers only
when the pane requested bracketed paste), followed by the separate Enter as
before. Same pane, same texts: 4/4 and 3/3 submitted. Each send gets its
own named buffer; a paste that fails (pane or server gone) deletes it,
since tmux never trims named buffers on its own.

Also escapes a trailing `;` in the text, which tmux treats as a command
separator (`send-keys -l 'x;'` typed `x`, a lone `;` typed nothing).

sendInputToPane() has no callers in src/ and is left as it was.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 10:20:01 +02:00
Codeman maintainer 71a9cc74c1 docs(codex): README and the skill's blocked-worker recipe know codex blocks
The README (both languages) still said only claude and deepseek sessions
emit `blocked` and that codex accepts only idle, working and exit, and the
skill's Flow 5 called `blocked` claude-mode only. Codex now raises it from
its screen-read dialogs; the recipe says to watch it with
`until=blocked,exit` (codex has no `stop`) and to answer in the terminal.
The plugin mirror is regenerated with scripts/sync-plugin.mjs.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 05:53:58 +02:00
Codeman maintainer 3ae6cde2d6 fix(wait): a retried send-and-wait finds a screen-read dialog
`GET .../wait` answers `blocked` at once for a codex pane showing a dialog,
because a screen-read dialog is a level the server is still reading. The
duplicate branch of `POST .../input` (a tagged retry after a dropped
connection, answered from current state) still used the plain
`currentSignalFor()`, so a retry with `wait: "blocked"` on a pane the first
delivery had already blocked waited out its whole timeout for an edge that
had passed. It now uses `currentSignalForWait()` like the GET route; a
caller that asked only for `idle` still gets `idle`.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 05:53:58 +02:00
Codeman maintainer 1c1658d039 fix(codex): skip the blocked re-read while the pane is quiet
While a codex dialog was up, the session re-read its screen every 2 s with
a synchronous `tmux capture-pane`, for as long as the dialog stayed up. That
read costs about 8 ms of blocked event loop (measured 2026-10-14), and an
approval can sit unanswered overnight: 1,800 execs an hour per waiting
session, measured as 300 in 10 quiet minutes in the unit test.

A dialog leaves the screen only through a repaint, and tmux sends every
repaint to the session's client as PTY output. So while the last reading
saw the dialog and nothing has been drawn since, it is still up and the
read is skipped; a safety-net read still runs every 30 s
(BLOCKED_QUIET_RECHECK_MS). The clear keeps its 2 to 4 s latency, because
the answer itself is output. Never skipped once a reading has missed the
dialog: tmux updates the pane before it sends the repaint, and the
confirming read must not wait for output that may already have arrived.

The session-state tests now change the fake screen the way a real pane
changes (with output), and two new tests pin the skip and the confirming
read (the second fails with the guard removed).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 05:53:58 +02:00
Codeman maintainer 28cbbad591 fix(worktrees): the injected agent skill no longer blocks removal
With Codeman Agent Skill on (agentSkillEnabled), every session create
writes the skill into the folder the session runs in, so a session in a
worktree leaves an untracked .claude/skills/codeman/ (claude) or
.agents/skills/codeman/ (codex, gemini and the rest) behind. git then
refuses `git worktree remove` for that worktree forever, and an agent
running `git add -A` there would commit Codeman's skill into its branch
(reproduced: the worktree's status showed "?? .agents/" and
"?? .claude/").

A create now excludes <skills dir>/codeman/ next to the hooks file, for
every skills folder the registry names (capabilities.agentSkillDir,
read at call time by the route and passed in as data, so no CLI id is
branched on). Like the other lines it is only added when git does not
already ignore the path, and only to the repository's local
info/exclude.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 05:37:48 +02:00
Codeman maintainer 64fea4ff84 fix(worktrees): a worktree whose folder was deleted can still be removed
When a managed worktree's folder is deleted by hand (a user in a file
manager, an agent's rm -rf), git keeps its record and lists it as
prunable. Codeman then had no way out: the dialog showed the row as
"missing" with no action, DELETE answered "No worktree named ...", and
the name could never be used again (the branch still existed, and with
another branch git refused "a missing but already registered
worktree"). Reproduced on git 2.43; git's own `git worktree remove` on
the missing path drops the record with exit 0.

Removal (only removal, never a session start) now accepts a worktree
whose folder is gone when git lists it at exactly the managed path, as
prunable or locked, and no component on the way is a symlink or a file.
The live-session and lock refusals still apply. The dialog offers
Remove... (not Open) on such a row, with a note saying what it does.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 05:34:49 +02:00
Codeman maintainer 0ce6e09725 fix(worktrees): a session spelled through the case's symlink blocks removal
The removal guard and the per-worktree session count compared each
session's workingDir lexically against the worktree paths git lists,
which are real paths. POST /api/sessions stores workingDir as it was
spelled, so a session started in a worktree of a linked case whose
folder is a symlink (a repository on another disk, macOS /tmp, a
symlinked cases folder), named through that link, counted for no
worktree, and DELETE /api/cases/:name/worktrees/:worktree removed the
folder under the running agent (reproduced: 200 instead of 409).

Each session folder under the case path is now respelled onto the
repository's real top level before the comparison (respellUnder, pure
and lexical, so no filesystem call per session).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 05:31:37 +02:00