Commit Graph
3526 Commits
Author SHA1 Message Date
Codeman maintainer 4da6aee660 fix: apply the review items for #682 at merge
docs: architecture-invariants (arrow-key walk section) names the phone swipe beside the arrow keys as following the drawn order
docs: root CLAUDE.md (Owner tab layouts) carves the phone swipe out of "every other order consumer reads sessionOrder"
test: pin the grouped-tree branch of _stripSessionOrder() (sorted rail, header and web tab skipped, groups not interleaved)
test: the step-in case uses a drawn order (homelab, w1, w2) that master's canonical walk and the old arithmetic cannot satisfy

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-24 14:05:05 +02:00
Codeman maintainer d41ad16818 fix: apply the review items for #681 at merge
_notePaneHistory JSDoc names the responses it skips (an error envelope on any path, a refresh that lost its owner, a pull whose select generation moved)
selectSession records the pane count only for a non-error envelope (capture.json?.success !== false), pinned in the static guard of test/pane-history-memory.test.ts; the two invariant paragraphs now name the tab switch beside the refresh and the pull

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-24 14:02:21 +02:00
Codeman maintainer ac6d4aa2db fix: apply the review items for #680 at merge
Restore settings.json (caseOrder, lastUsedCase) when linked-cases.json cannot be written, with the rollback test extended
List Claude Code's per-project ~/.claude.json entry as not migrated in Core-Concepts.md and CLAUDE.md
Say in the 500 that the folder is now at the new path when moving it back fails
Describe the rename dialog's folder checkbox in Core-Concepts.md, carry stoppedSessions on errors, count 5 case SSE events

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-24 14:01:33 +02:00
Codeman maintainer f4f9001a4b fix: apply the review items for #678 at merge
docs: document the SSE connect timeout next to the staleness watchdog in src/web/CLAUDE.md and docs/architecture-invariants.md (clear order, replaced-EventSource guards, offline handler leaves the timer)
test: rename the offline case to "connectSSE() while offline cancels the pending timer" so it names the path it drives

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-24 13:59:17 +02:00
Codeman maintainer 0eb9255465 fix: apply the review items for #677 at merge
src/tmux-manager.ts: escapeTmuxTrailingSemicolon() JSDoc says every send-keys -l passes -- before the text, and its example uses the -l -- form
test/tmux-submit-paste.test.ts: the trailing-semicolon wiring test matches -l (?:-- )? so it pins only the semicolon rule
test/tmux-submit-paste.test.ts: master's two recorded-command tests for typed text expect the -l -- form (merge interaction with the inputRunner() refactor)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-24 13:57:37 +02:00
Codeman maintainer 886bf9fa0b Merge remote-tracking branch 'origin/master' into relprep/pr-678 2026-10-24 13:55:07 +02:00
Codeman maintainer effb1ef7a5 Merge remote-tracking branch 'origin/master' into relprep/pr-677
# Conflicts:
#	src/tmux-manager.ts
2026-10-24 13:54:44 +02:00
Codeman maintainer 3d6b48a4cd fix: apply the review items for #683 at merge
findPersistedSessionOrFail now requires req (dropped the `?` and the dead `req &&` guard), like findSessionOrFail
The AST test in test/ownership-scoping.test.ts pins both declarations and asserts req's type is exactly FastifyRequest

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-24 13:53:13 +02:00
Devvyn 23a9d78cdd fix(host-sensors): never wait on a cold sampler, back off failing readers, one stats request at a time 2026-10-24 10:17:20 +08:00
Devvyn 77eefce4f6 fix(terminal): carry leftover travel into a notch's page, document the new rule 2026-10-24 10:12:58 +08:00
Devvyn 372df864b9 fix(terminal): review of the Codex menu wheel guard 2026-10-24 10:10:38 +08:00
Devvyn 81beec7b5b fix(terminal): a mouse wheel notch pages the CLI's transcript at once 2026-10-24 08:46:38 +08:00
Devvyn 9fa6d98c7c fix(terminal): the wheel no longer moves a Codex selection menu's selection 2026-10-24 08:28:52 +08:00
Devvyn f15afe1de3 test(settings): poll again when settings.json is read half-written 2026-10-24 00:32:20 +08:00
Devvyn ce7d198b91 fix(host-sensors): one shared Windows sampler, guard late rows, address review 2026-10-24 00:05:21 +08:00
Aamer Akhter 27c93b20fb fix(terminal): stop URL continuations from swallowing prose 2026-10-23 10:00:38 -04:00
Devvyn e1d6471808 feat(header): opt-in GPU and CPU clock readings after CPU and MEM 2026-10-23 21:36:10 +08:00
Aamer Akhter 74a107cf4f fix(terminal): require path labels and preserve markdown context 2026-10-23 09:01:44 -04:00
Devvyn f3c90abeb0 fix(header): settle the divider when readings change, keep density to the header layout, restore the chosen width 2026-10-23 19:58:40 +08:00
Aamer Akhter 40a9528be1 fix(terminal): keep unrelated rows out of wrapped links 2026-10-23 07:26:15 -04:00
Aamer Akhter efa647d257 fix(terminal): exclude relative prose and trim spaced paths linearly 2026-10-23 07:26:15 -04:00
Devvyn fa2d75c951 feat(header): resizable toolbar divider that collapses the usage readings as it narrows 2026-10-23 19:01:06 +08:00
RandalixandClaude Opus 5.5 a1a33cf050 fix(agent-inbox): review round 1
- codeman agent post: accept text that starts with "-" (markdown from a
  file), --json and -h stay options.
- DELETE /api/sessions/:id on a persisted-only row drops the inbox a detach
  kept.
- inbox --wait: wait.ended is exit 3 only when the session list no longer has
  the session; a restart or detach ending the wait is exit 2 (wait again).
- A read whose client hung up during ?wait= marks nothing seen.
- InboxPostSchema bound is 4x the byte cap, so every oversize message gets
  the store's "post a file path" answer.
- Store: restore sorts oldest first; the TTL sweep checks every message,
  not only the first.
- Docs: admins can act as any session of the receiver's owner (now tested
  for admin and non-admin); a peek with ?wait= does not block; exit codes.
- Tests: partial PUT /api/settings keeps persistence on.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-23 12:45:48 +02:00
RandalixandClaude Opus 5.5 c6dfaa2cac feat(agent): per-session agent inbox (discussion #558)
A coordinator can leave text for a worker without typing into its pane.

- Store (src/web/agent-inbox.ts): read until ack by id, seen only for the
  receiver, 16 KiB/message, 512 KiB + 200 messages per inbox, full = 409
  CONFLICT (never drops the oldest), 12 h TTL from each message's createdAt.
- Routes (routes/inbox-routes.ts): POST/GET /api/sessions/:id/inbox
  (?wait= parks while nothing is unseen), POST .../inbox/ack (receiver
  only, 403 otherwise), GET /api/agent-inbox/summary (owner-scoped counts,
  not in toState()). Caller = resolveParentSessionId().
- Inbox waiters are the wait registry's third kind: same caps, ended by
  cancelAll/cancelEverything.
- Opt-in snapshot (agentInboxPersistEnabled, synced, default off):
  agent-inbox.json via dataPath(), 0600 tmp+rename, off deletes it, boot
  prune only after a successful restore. Dropped on delete, kept on detach.
- CLI: codeman agent post | inbox [--wait] | ack, INBOX column in ls.
- waitCapacityResponse/abortOnClientHangUp moved to route-helpers.ts
  unchanged.
- Docs: api-reference, README, CLAUDE.md, architecture-invariants, skill +
  plugin mirror.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-23 12:45:48 +02:00
DDTandDDT 9fc2773176 fix(tmux): bind nested prefix before unbind so an empty table cannot abort the chain
A user tmux config that leaves the prefix table empty makes
unbind-key -a -T prefix fail and drop later commands in the \; chain,
including send-prefix and remain-on-exit failed. Bind C-q send-prefix,
clear the table, then bind it again.

Also accept macOS ttys in the TUI switch-guard test without reading
/proc, and describe the client_pid hook escape as defensive on tmux 3.4.

Co-authored-by: DDT <1786035110@qq.com>
2026-10-23 09:08:48 +00:00
DDTandDDT daeea54495 fix(tmux): stop nested prefix keys and TUI Alt+digits from moving other clients
Docker and owned-remote tmux servers kept the stock prefix table, so Ctrl+Q
then )/(/s/w/L switch-client'd the inner client onto a sibling. Those create
chains now leave only C-q send-prefix.

codeman tui bound M-1..M-9 in the server-wide root table. The keys stay, but
switch-client runs only for the attaching client (tty plus a one-shot pid).
A browser tab's Esc-then-digit no longer jumps sessions, and a killed TUI
leaves a binding that matches no live client.

Co-authored-by: DDT <1786035110@qq.com>
2026-10-23 07:41:46 +00:00
Aamer Akhter 175e3b8320 fix(auth): keep the single-user login throttle at 10, widen only the multi-user pre-gate
Single-user mode has no per-username bucket, so its per-IP bucket is the
only limit on guesses against the one password, and behind a tunnel every
client shares 127.0.0.1. Put it back on AUTH_FAILURE_MAX (10).

AUTH_IP_FAILURE_MAX (50) now gates only the multi-user per-IP pre-gate,
which runs before the scrypt verify and so refuses even a correct password;
the per-username bucket still caps each account at 10.

Move the headroom test out of the single-user suite and add a multi-user
test that sprays wrong passwords across usernames (9 each) and expects 401
for the first 50, then 429 for the 51st and for a correct password.

Update the docs that still described 10 per IP for every mode, and resync
the plugin mirror of the codeman skill.
2026-10-22 21:21:49 -04:00
Aamer Akhter 32a1d544ab feat(terminal): link file paths that contain spaces as one path
Every path pattern ended a path at whitespace, so
"/mnt/c/OneDrive - Contoso/a.docx" linked "/mnt/c/OneDrive" plus a relative
"Contoso/a.docx", two links to the wrong files, in the terminal and the
response viewer alike.

findFilePathLinks() now takes a spaced path whole where the line bounds it:
the whole line is the path, or it follows a list marker and/or a short
"Label:". A relative spaced path needs the marker or label. The candidate
must read as one path (no earlier word ends on an extension, starts a new
"/" path or is an arrow). A spaced line that is one path but not linkable
(unknown root, "." or ".." segment) links nothing instead of a fragment.
2026-10-22 17:45:12 -04:00
Aamer Akhter 9f62da9ac3 feat(terminal): link URLs and paths that wrap across rows or sit in table cells
A CLI that word-wraps inside a markdown table cell or an indented paragraph
breaks a long URL or path short of the right edge and indents the rest, so
nothing is flagged isWrapped and no row is full width. Each row linked only
a fragment, and the fragment opened the wrong target.

- findTerminalUrls() (constants.js) is the URL grammar for the link provider:
  a ")" that closes a "(" inside the URL is kept, an unmatched one still ends
  it, so (https://...) and [text](https://...) stay clean.
- terminalUrlContinuationExtent() joins rows the CLI wrapped a URL across,
  only when the fragment ends on a wrap-break character, the next row is
  indented no further right than the URL started, and its first token looks
  like URL material. Table columns after the first fragment are left out.
- terminalLinkLine() widens terminalPathLine() to that extent; a URL that
  spans rows is emitted as one link per row, each opening the whole URL, so
  the indent and other columns between the pieces are not underlined.
- terminalCellWrappedPaths() follows an absolute path down its own table
  cell (space-separated or box-drawn), so two-column tables link both paths.
- A full-width table divider no longer counts as a hard-wrapped line.
- OSC 8 hyperlinks open through the same opener, web URLs only.
2026-10-22 17:44:46 -04:00
Aamer Akhter c6a5995ac6 feat(palette): choose the agent when starting a session from Cmd+K
The palette's "New session" row now shows the agent it will start and
lets Left/Right (or the row's arrows) switch it. The agents come from the
injected CLI catalog, filtered to enabled and installed entries, so no CLI
id list lives in the palette. The pick is a one-shot launch through run()
with a temporary _runMode swap: the Run button's mode is left unchanged.
2026-10-22 17:23:03 -04:00
Aamer Akhter 4f9274af3a fix(auth): require the request on findSessionOrFail
findSessionOrFail took `req` as optional, so the quiet default was the
unscoped lookup: a route that forgot to pass it compiled cleanly and, in
multi-user mode, returned another user's session. `req` is now required,
and a caller with no request in flight must use the explicit, greppable
findSessionUnscopedOrFail instead.

Every current caller in src/ already passes req (verified by grep and
tsc), so behaviour is unchanged; the new helper has no production
callers and exists so the next request-less caller does not make `req`
optional again.

Tests pin the unscoped helper's behaviour and assert, from the source
AST, that `req` is a required parameter (tsc does not typecheck test/).
2026-10-22 17:23:03 -04:00
Aamer Akhter ebb9e00de9 fix(auth): widen the per-IP login throttle to 50, keep the per-account lockout at 10
One AUTH_FAILURE_MAX (10 per 15 min) gated the single-user per-IP
bucket, the multi-user per-IP pre-gate, the per-username lockout and the
hook-secret bucket. Clients sharing one egress IP (NAT, a tunnel, an
automated client retrying) were throttled after 10 failures in total.

Add AUTH_IP_FAILURE_MAX = 50 and use it for the two per-IP login buckets
only. The per-username lockout and the hook-secret bucket keep
AUTH_FAILURE_MAX = 10, so guessing against any one account, or against
the hook secret, is no easier than before.
2026-10-22 17:23:03 -04:00
Devvyn 1cad4dd848 fix(cases): address review of the rename route
Only an admin's rename touches the server-wide settings.json, which is now
written before linked-cases.json with the registry restored on failure. The
Resume list follows a renamed folder, the owner's default session group is
carried over, remote sessions are never listed or stopped, errorCode maps
EXISTS and BUSY, and the unmigrated items are documented.
2026-10-23 04:15:46 +08:00
Devvyn 201603e57b feat(cases): rename in the app's own dialog instead of browser prompts 2026-10-23 04:12:37 +08:00
Devvyn ab0bc0aecd feat(cases): rename a case and its folder, with a lock and live-session handling 2026-10-23 04:12:37 +08:00
JD 84adb701d7 fix(terminal): load tmux history instead of paging Claude after a hollow tab switch
After a server restart the byte recording a tab switch replays (`?tail=`) holds only the tmux client's attach repaint, so an inline Claude session came back with an empty local buffer while tmux still held its scrollback. The hollow-buffer fallback then sent PageUp/PageDown to Claude, which moved the composer cursor instead of the transcript, until a page reload. Pre-existing on an unmodified build; every restart of the server showed it.

The paging fallback now stands down when the last capture reported tmux scrollback (`paneHistoryLines` > 0), dropping the gesture debt and any page keys already queued, so the gesture falls through to the local path, whose scroll-to-top pull fetches `full=1` and the gesture scrolls on from there. Zero and unknown counts keep paging (fullscreen Claude, codex, opencode, remote and Docker panes). The pull no longer inherits a downgrade refusal's 60 s latch for such a buffer, drops a completion that outlived a tab switch back to the same session (select generation, not only the session id), records no count from an error envelope (a JSON 5xx would otherwise forget it and hand the next gesture back to paging), and re-positions the local-echo overlay after its rewrite. The refresh path records its count only once it still owns the terminal, for the same reason. The `[scroll]` line prints `paneHistoryLines` so an unloaded buffer reads apart from a hollow one, and the invariant docs record the exception.

Measured with phone emulation after a restart: before, the second visit landed at baseY 0 and every swipe sent PageUp; after, the first swipe fetched the history once with nothing sent to Claude and the gesture scrolled 21 rows, with an unsent draft intact on the overlay.
2026-10-22 15:13:01 -04:00
JD 116961f3a8 fix(mobile): swipe through sessions in the order the strip draws them
nextSession/prevSession walk the canonical sessionOrder, but the case and state tab layouts, a sorted rail and named groups reorder the strip, so with any of them a swipe jumped to tabs that were not next to the current one on screen. The swipe now asks for the drawn order, read with the same walk the strip's arrow keys already use (factored out as _visibleTabRows); Alt+[ / Alt+], Ctrl+Tab and Alt+1..9 keep the canonical order. An active session the list does not draw steps in at the first or last row, and an empty drawn list goes nowhere instead of falling back to a hidden session.
2026-10-22 15:11:21 -04:00
Codeman maintainer 002de32a08 chore: version packages
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
codeman@1.44.0
2026-10-22 20:33:07 +02:00
Codeman maintainer a85da87378 Merge pull request #674: fix(mobile): give a sideways drag on the terminal to the session swipe alone
Second merge of #674: the author pushed the thumb-arc handback from the review themselves (e8fc08be), byte-identical in code to the merge-time fix already here, so this brings their round in and keeps the browser test and docs added at merge.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

# Conflicts:
#	docs/wiki/Mobile-Guide.md
#	src/web/public/terminal-ui.js
#	test/session-swipe-gesture.test.ts
2026-10-22 20:32:13 +02:00
Codeman maintainer 463a083a23 Merge pull request #676: feat(tabs): per-case default session group
Conflict in CLAUDE.md's State Files line resolved by keeping #661's new
config-backup entries and this PR's "case default groups" in state.json.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 19:54:54 +02:00
Codeman maintainer 3b6818449e Merge pull request #661: feat(config): timestamped config backups with a configurable folder and retention
Conflicts in CLAUDE.md, README.md, docs/api-reference.md and
src/web/server.ts were additions beside #662's Copilot quota read and
#666's keep-warm sections; both sides kept.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 19:54:40 +02:00
JD e8fc08beb3 fix(mobile): hand a swipe-locked drag back to the scroll past the swipe's drift limit
A scroll whose first move past the tap slop leaned sideways (a thumb arc) locked as 'swipe' for the whole contact, and SwipeHandler drops a swipe at 50 px of vertical drift, so the gesture did nothing. Past that limit the drag is now a 'y' scroll. Also brings the mobile-handlers fileoverview to the current 500 ms / 50 px limits and notes in the Mobile Guide that a selection pauses the swipe and a wide terminal pans instead.
2026-10-22 13:53:48 -04:00
Codeman maintainer 9045b18553 Merge pull request #660: fix(test): make a bare vitest invocation use the isolated CI config
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 19:53:43 +02:00
Codeman maintainer dda16d3e2a test(tabs): stub process.cwd instead of changing directory in the no-workingDir case
Merge-time fix for #676. The test for nit 7 changed the worker's real cwd,
which also throws under a threads pool; a process.cwd spy gives the route the
same case-folder cwd with nothing to restore by hand. It still fails without
the workingDir gate.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 19:37:36 +02:00
Codeman maintainer 3198a6972c fix(release): stamp the codeman skill preamble 1.44.0
#670 stamped the preamble with the version it expected to ship in, 1.43.1.
The release is 1.44.0, so the stamp would name a version that never
existed. Any new value makes cached preambles from before #670's sendwait
change stale, which is what the stamp is for.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 19:32:47 +02:00
DevvynandClaude Opus 5.5 33d697afe3 fix(custom-model): warn when parallel llama.cpp slots leave too little context per request (#658)
Squash of PR #658. Discovery records each llama.cpp model's parallel slot
count (from --parallel/-np, or /props total_slots when the launch command
states none), and a Claude launch warns when the context split across
those slots leaves less than 40K tokens per request. An explicit
--parallel beside a /props context records a slot count of 1, so a later
round overwrites an earlier count, and a non-2xx /props is remembered per
launch command while a network error is retried.

Merge-time fix by the maintainer: the architecture-invariants sentence on
the explicit --parallel case now says it records a slot count of 1.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 19:32:24 +02:00
Codeman maintainer 0c1578581f docs: case default groups in CLAUDE.md, and the Cases handler count
Merge-time fix for #676 (review finding 3 and nit 9). The State Files entry
for state.json names the new caseDefaultGroups key, and the Owner tab layouts
paragraph says where a case's default session group sits in the placement
order of sessionCreated() and that only TabLayoutService reads or writes it.
The wiki endpoint map counts the two new case routes (37 to 39).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 19:32:23 +02:00
DevvynandClaude Opus 5.5 34cf798b10 feat(copilot): plan-usage chip row for the monthly premium-request quota (#662)
Squash of PR #662. The header plan-usage chip gains a Copilot row: the
calendar-month premium-request quota, with the request counts and the
reset in the tooltip, read every 10 minutes from GitHub's
copilot_internal/user endpoint with the Copilot CLI's own sign-in token.
It rides showPlanUsageLimits and is on by default while the CLI is
installed and signed in; any failure leaves the chip without the row.

Merge-time fixes by the maintainer: the gates (setting, then token, then
the CLI probe) live in readCopilotPlanUsageIfEnabled() so a host with no
Copilot sign-in never runs the login-shell probe each tick, with a test
that Plan Usage Limits off means no request; the X-GitHub-Api-Version
header the Copilot CLI itself does not send is dropped; and CLAUDE.md,
architecture-invariants, README and copilot-integration describe the read.
The hooks-config import in server.ts was merged with #666's.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 19:32:17 +02:00
Codeman maintainer 9b8cb82c3b fix(tabs): leave the default-group options alone when the groups did not change
Merge-time fix for #676 (review nit 6). Once a Case settings popover had been
opened, every adopted tab layout (each create, delete and reorder) replaced
both selects' options, even with the popover hidden and the group list
unchanged, so a session created elsewhere could reset a native dropdown the
user had open. _refreshCaseDefaultGroupOptions now returns early when the ids
and names are the same. A vm case pins it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 19:32:12 +02:00
Codeman maintainer a3efc9b963 fix(tabs): keep the case-default GET read-only, forget a stale id outside the rollback
Merge-time fix for #676 (review nits 4 and 5). sessionCreated forgot a stale
case default inside the try whose catch rolls the registration back; it now
does so after the try, once the session is committed, so the cleanup can
never drop a committed session from the live map. caseDefaultGroup (behind
GET /api/cases/:name/default-group) no longer deletes a stale id as a side
effect: it reports Ungrouped and leaves the value for the next creation to
forget. The two tests that expected the read to clear it now expect it to
write nothing; the creation test still pins the cleanup.

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