Commit Graph
212 Commits
Author SHA1 Message Date
Codeman maintainer 6fb8b27768 chore(web): remove frontend code and styles nothing reaches
The frontend JS is outside ESLint and knip, so this pass used an AST scan
(every method, function and top-level constant in src/web/public, counted
against identifiers, strings and index.html) plus a selector scan of both
stylesheets. Each hit was confirmed by text search before it went.

JavaScript
- The ten run<Cli>() one-line wrappers and the quickStart() alias: run()
  has dispatched to _runCliMode(mode) since the launcher consolidation and
  nothing called the wrappers except tests, which now call the launcher.
- The Agent Teams panel code that lost its event listeners in February:
  the team tasks panel, the teammate pane window, rebuildTeammateMap and
  the three maps that nothing filled any more.
- Unreferenced methods: redockSession, refreshOrchestratorStatus,
  toggleSubagentPanel, minimizeSubagentWindow,
  openAllActiveSubagentWindows, pickBetterPath, getTodoIcon, deletePreset,
  closeRunSummary, saveSessionOptions, getAgentParentSessionId,
  _isUltracodeAgentMinimized.
- Six unused constants and a few unused locals (an unused frame-time
  computation in the touch scroll loop among them).

CSS
- 62 classes and one id that no markup, script or test names: the old
  settings-item / settings-section layout, the pre-redesign respawn and
  context-settings rows, the existing-wizard card and others. 116 rules
  and about 770 lines. A dead class inside an :is() list only loses that
  alternative; the rule stays.

Checked in a browser against an isolated instance: no console errors on
desktop or phone, a shell session runs, and 96 screenshots (desktop,
tablet and phone; settings, session options, add case, panels) are
pixel-identical before and after, apart from clock times.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-13 14:18:13 +01:00
Codeman maintainer 91d157cae4 fix: name the pane directory field paneCwd and apply the review items for #740
`cwd` beside the fixed `workingDir` reads ambiguously on a public API surface, and
`paneCwd` says where the value comes from, like `paneExit`. Also pin the field on
the payload the browser really receives (getSessionStateWithRespawn), and give the
pane-directory text its own architecture-invariants paragraph.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 16:59:16 +01:00
Codeman maintainer 4f50af6a9f Merge pull request #740: feat(terminal): resolve relative path links against the pane's current directory 2026-11-04 16:57:30 +01:00
Codeman maintainerandDDT dc6f8d27e8 fix: keep a Bash( row out of the parenthesised path detector (#735)
A Bash( row holds a command, so its parentheses do not bound one spaced path:
`Bash(/tmp/a.out input.txt)` became a single link to a file that cannot exist and
lost master's `input.txt` link. The path tools keep linking a spaced name whole,
and a hard-wrapped Bash( row gets no spaced lookup either. Test rows from #734.

Co-Authored-By: DDT <141296842+Hugo-DDT@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 16:57:10 +01:00
Codeman maintainer 76e9f364f9 Merge pull request #735: fix(links): preserve wrapped configuration filenames 2026-11-04 16:55:44 +01:00
timkjrandClaude Opus 5.5 6a23fecb1c fix: keep the L-shaped tab strip whichever tab is selected
The wrapped header strip fell back to the plain wrap whenever its first
tab was wider than the room beside the toolbar. A selected tab is wider
(its gear and close show), so selecting the first tab flipped the whole
strip back: the toolbar took its own column again and, with long names,
every tab took a line of its own. Selecting any other tab brought the
full-width rows back.

The first tab is now trimmed to the room instead (tab-corner-fit, a
max-width from --tab-corner-first-max, its name ellipsized). Only a
toolbar that leaves less than TAB_CORNER_MIN_FIRST_TAB (120px) beside
it keeps the plain wrap, which does not depend on the selection.

Two measuring details make the trim hold:
- the room comes from the rounded-up float width, less the tab's 2px
  margin and a pixel for subpixel layout (a tab at the exact width still
  wrapped under the float);
- the first tab is measured with its trim cleared and its gear/close
  width transitions finished, or a grown room reads the last trim as the
  width and a selection change is judged by the width the tab is leaving.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 16:41:26 -05:00
Devvyn 3847e377d8 fix(links): preserve missing-path and labelled-wrap behaviour 2026-10-29 01:32:17 +08:00
Devvyn 168c15ae08 fix(links): scope comma handling to parenthesised paths 2026-10-29 00:18:20 +08:00
Devvyn fd0e52496d fix(links): preserve workspace openers after lookup refusals 2026-10-28 23:03:45 +08:00
Devvyn 97d53f6621 fix(links): resolve ambiguous hard wraps through guarded lookup 2026-10-28 21:45:49 +08:00
Aamer Akhter 1607b1e488 feat(terminal): resolve relative path links against the pane's current directory
A relative path printed in the terminal was resolved against the session's
workingDir, which is fixed at create time and never follows a cd. In a shell
that moved into a subdirectory or a sibling repo, the link opened the wrong
file or reported "File not found".

The pane-exit watcher's batched list-panes read already carries
#{pane_current_path}. derivePaneCurrentPaths() turns those rows into one
directory per single-pane session, TmuxManager.getPaneCurrentPath() serves it
from memory, and the server applies it on the existing paneExitsUpdated tick
as SessionState.cwd, broadcasting only on a change. No new tmux call, no new
SSE event. A failed read keeps the previous answers.

Session.setCwd() uses the paneExit scoping rule: remote and Docker sessions
never get a cwd (their local pane runs the ssh or docker exec client), nor do
direct-PTY or rediscovered sessions. The value is runtime-only.

In the browser, relativeResolutionBase() picks the live cwd when known and
workingDir otherwise. It only changes the base a relative path joins: the
result is routed as before, so a target outside the workspace still goes
through the attachment routes, and file-content/file-raw are unchanged.
2026-10-28 09:38:06 -04:00
Devvyn 5ce8213726 fix(links): preserve wrapped configuration filenames 2026-10-28 21:17:50 +08:00
Codeman maintainer cc08016a76 fix: keep links in the columns right of a wrapped URL's first row (release review)
terminalLinkLine() cut the URL's first row at its cell to stitch the URL, so a
path in a later column of that row was never scanned. The cut-off columns now
follow the stitched rows as a segment of their own, mapped back to their cells.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 03:08:33 +01:00
Codeman maintainer 3fdba31e36 fix: keep a shell prompt whose spaced working directory ends in $ out of a spaced path link (release review)
spacedFilePathOnLine() refused only a FIRST word ending in `$`, so `/mnt/c/My Docs$ ls notes.md`
and `/mnt/c/Users/me/OneDrive - Contoso/proj$ code notes.md` linked the whole line. Any earlier
word ending in `$` now refuses the candidate, and both lines link what master links.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 03:00:44 +01:00
Codeman maintainer 98526527c8 Merge pull request #711: fix(mobile): hold the PTY rows while the on-screen keyboard is up 2026-10-27 02:24:17 +01:00
Codeman maintainer 9c27342c7f Merge pull request #687: feat(terminal): link file paths that contain spaces as one path
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 02:11:29 +01:00
Codeman maintainer 3d0abbf21c Merge pull request #686: feat(terminal): link URLs and paths that wrap across rows or sit in table cells
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 02:11:29 +01:00
Codeman maintainer e52cc60c7c fix: apply the review items for #687 at merge
- constants.js:2296,2301 spacedFilePathOnLine: refuse a first word ending in `$`
  and an earlier word with no letter or digit (other than a bare `-`), so a
  shell prompt showing an absolute working directory (`/mnt/c/proj ❯ code
  notes.md`, `/home/me/proj$ cat notes.md`) keeps master's links; the six rows
  from the review are pinned, plus OneDrive, `Program Files (x86)` and
  `Docs - Copy` still linking whole.
- constants.js:2312: the leading strip is now `/^(?:~?\/|(?:\.\.?\/)+)/`, so
  `Saved to: ../My Docs/plan.md` links whole; an interior `..` still links
  nothing. Tests for the detector and the terminal provider.
- app.js:3032 _linkifyFilePaths: carry any leading bold label and let
  spacedFilePathOnLine() judge it, and give a text node that follows another
  inline element a placeholder word, so `**Note:** /home/me/proj/out
  report.pdf` and ``Use `--config` /home/me/proj/cfg data.json`` split as in
  the terminal; a spaced path inside inline code still links whole. The
  now-unused label-pattern guard is gone.
- JSDoc (findFilePathLinks, SPACED_FILE_PATH_LABEL_PATTERN,
  spacedFilePathOnLine) and the spaced-path paragraph in
  docs/architecture-invariants.md updated to match.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 01:34:15 +01:00
Codeman maintainer e93382f2f1 fix: apply the review items for #686 at merge
- constants.js:2618 terminalUrlContinuationExtent: trailingColumns now needs a
  one-row first line (first.startRow === first.endRow), so a URL row that
  soft-wraps with a column on its wrapped part no longer glues the next row
  onto that column (`/tmp/report.txtccc/x.html` at 41 columns); master's links
  come back. Negative tests for the extent and the link provider.
- constants.js:2847 terminalCellWrappedPaths: comment reworded to match the
  JSDoc (the exception is a left-column PATH), plus the wrapped-prose case.
- docs/architecture-invariants.md, CLAUDE.md: one clause that a left column of
  wrapped prose (a Description | Path table) also fills the continuation row,
  so the path beside it keeps its per-row fragment links, the safe direction.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 01:23:56 +01:00
Codeman maintainer e8f66c6446 fix: apply the review items for #711 at merge
- terminal-ui.js _applyKeyboardClip(): read container.clientHeight only while rows are held, so the release path and the setting-off path skip the forced layout on every render and cursor move
- constants.js keyboardClipPx JSDoc: drop the stray "ponytail:" marker, the note reads as plain text
- src/web/CLAUDE.md "Per-device vs synced settings": list keyboardHoldsRows among the per-device keys SettingsUpdateSchema does not declare

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 01:21:51 +01:00
Aamer Akhter ae3d31407b fix(terminal): keep a labelled table record out of a cell-wrapped path
A wrapper wraps every column of one table row together, so the row that
carries a cell's continuation is blank to the left of that cell unless a
path in a left column continued on it too. A label there (`Entry`,
`│ Helper │`) opens the next record, so a directory ending in `/` or `-`
(`Workspace  /home/u/proj/app/` over `Entry  main.py`) no longer takes
the next record's file name.

What still joins is a trailing-slash directory over a relative path in the
same column with nothing to its left, which reads exactly like a wrap at
`/`; that trade-off stays pinned.
2026-10-25 09:12:04 -04:00
Aamer Akhter 8ad60facf3 fix(terminal): keep a directory cell from taking the next row's file name
A table cell holding a whole directory (`/home/u/proj` over `config.yaml`)
was joined onto the file name in the same column on the next row, which is
the next record, and the joined link then replaced the correct links on both
cells. A wrapper breaks a path after `/` or `-` (codex wraps cells on
Unicode line-break opportunities plus a hyphen splitter, which never break at
`_`), so a cell fragment must end on one of those before the next row can
continue it.

A directory written with its trailing slash over a relative path in the same
column still joins, since it reads exactly like a wrap at `/`; pinned as a
known trade-off.
2026-10-25 09:03:29 -04:00
Aamer Akhter 1bdc341784 fix(terminal): keep code, fields and measurements out of wrapped URLs
A URL ending in `/` still took the next indented row when that row was
code, a label, a log or config field, a measurement or the end of a
sentence. A side-by-side sweep against master turned up shapes such as
`ready.` under a codex `Listening on` line, `expect(x).toBe(y)` under a
jest title, `Failure/Error:` under an rspec title, `API_KEY=...` under
`BASE_URL=...`, `level=info` log fields, `45%|####` tqdm bars,
`12.3MB/45.0MB`, `12.3ms` and `10:00:00.123`.

A continuation is now refused when it ends with a colon, when it is a
measurement or clock time, or when it is a `name=value` token under a
URL ending in `/` (a query needs `?` first). A token with only `-`, `_`
or `.` is also refused when it carries a call paren or ends a sentence.
Wiki titles such as `Mercury_(planet)`, a file name before its period,
query parameters after `?`, `&` or `_`, and percent-encoding that starts
with digits still join.

`self-hosted.` moves from the pinned trade-offs to the negatives; the
trade-off is now a lone word without sentence punctuation.
2026-10-24 22:50:23 -04:00
Aamer Akhter df9f93d3ee fix(terminal): keep sentences and script commands out of spaced paths
A side-by-side sweep of master and this branch over about 590 realistic lines
found more shapes where a spaced candidate swallowed prose or a command:

- An earlier word that is a lowercase connective or verb (and, or, to, in,
  the, is, has, contains, then, as and a few more) now makes the candidate a
  sentence, for own-line, list-marker and labelled forms alike:
  `/home/me/proj contains a.md`, `- /tmp/out has the logs in run.log`,
  `Path: /home/me/proj/out contains a.md`, `Files: src/components and
  utils/x.ts`, `Wrote: 3 files to docs/design notes/overview.md`. `at` and
  `of` stay out so screenshot names and `Copy of report.docx` still link
  whole; a name such as `Pros and Cons.md` splits as on master.
- A bare `=` word is refused like the arrows.
- An executable under `scripts/` or `tools/` is refused like one under
  `bin/` (`/home/me/proj/scripts/deploy prod.yml`).
- An earlier word holding an extension anywhere (`a.ts#L12`) ends the path.

The two residuals pinned earlier (`/home/me/proj contains a.md` and
`Files: src/components and utils/x.ts`) now return master's links.
2026-10-24 22:47:44 -04:00
Aamer Akhter 5e1daaf7c7 fix(terminal): keep label prose out of labelled spaced paths
A path label says a path follows, not that the words before it belong
to the path. Restore the guard that defers a relative candidate whose
only slash word is its last, so `Wrote: 120 lines to src/foo.ts` links
src/foo.ts as on master. `Saved to: My Docs/plan.md` and
`Saved to: 03 Attachments/plan.pptx` split again as a documented
trade-off; `Files: src/components and utils/x.ts` is pinned as the
residual the guard does not catch.

Refuse command lines whose executable sits under a bin/ or .bin/
directory, or that carry a flag word, so they keep master's links.

Update the JSDoc example, the CLAUDE.md pointer and the
architecture-invariants paragraph.
2026-10-24 22:28:57 -04:00
Aamer Akhter 2a90fb8118 fix(terminal): keep the next table record out of a last-column URL
A URL in the last column of a space-aligned table ended on a slash, so
the first cell of the next record (billing-api, CHANGELOG.md, ...)
passed every continuation test and was joined onto it. The alignment
check that refused this only ran when other columns followed the URL.

A continuation row must now start inside the URL's own cell: a gap of
two or more spaces between its indent and where the URL starts on the
first row means it opens an earlier column. An indented token that is
a whole email address is also refused.

Adds the six table shapes and the email case as negative tests at
detector and provider level, and documents the rule.
2026-10-24 22:28:29 -04:00
JD 3e334357e8 fix(mobile): hold the PTY rows while the on-screen keyboard is up
Every keyboard open and close resized the PTY, and Claude Code's inline renderer answers each height change by clearing the screen and reprinting only the transcript tail that fits, so rows above that tail were lost from tmux for good. Measured on Claude Code 2.1.306 with tmux 3.2a: five keyboard cycles (62x38 to 62x15 and back) left 75 of 100 numbered lines in the pane's history; with the rows held the PTY stays 62x38, no SIGWINCH reaches the pane and all 100 remain (#672).

While the keyboard is up the rows captured at the show transition are held through getTerminalDimensions, columns still follow the box, and the screen, now taller than its box, is translated up by --kb-clip so the last content row sits at the bottom of the visible area. This is the one documented exception to the columns-only geometry rule: there the extra rows came from another device and nothing kept the composer in view, here they are this device's own rows held on purpose and the clip keeps the composer visible. The local-echo overlay's setRowLimit narrows the renderer's existing totalRows bound, so one place decides where a long draft stops growing downward.

Only inline renderers hold, through a new registry capability keyboardHold: 'inline' (Claude alone) published to the page as window.__codemanKeyboardHoldModes. Full-height layouts such as codex, Claude's fullscreen mode (mouse tracking on, unmeasured), and remote or docker panes resize as before. The hold is released on keyboard close, a shape change, a tab switch, a session update, a reconnect, or the setting turned off. Per-device setting Keep Size Under Keyboard, default on.
2026-10-24 20:17:45 -04:00
Aamer Akhter 405c9928d9 fix(terminal): end spaced paths at line suffixes and generic labels
A spaced path is no longer taken across a `path:line` suffix (grep -n, rg -n
and compiler output) and an absolute path after a generic label such as
`Error:` or `Note:` goes back to the ordinary detectors: any label must now
be a path label. `Saved the report to: /mnt/c/My Docs/a.docx` splits as on
master, pinned as a documented trade-off.

Also:
- a rooted candidate needs a directory before its first space, and an
  unlinkable one reserves its span only while its last word holds a `/`, so
  `/usr/bin/env node build.js` and `/review the changes in src/foo.ts` keep
  their links while `/etc/secret files/passwords.txt` still links nothing
- a run of spaces is a column gap, not part of a name
- a leading `./` is not read as a segment
- the response viewer carries a bold path label that starts a later line of
  a paragraph (after a `<br>`), and degrades if the label pattern is missing
- response viewer tests render with the options chat uses
2026-10-24 19:55:11 -04:00
Aamer Akhter 3a4e2c996e fix(terminal): keep shell prompts and status lines out of wrapped URLs
A URL ending a row on `/` took the next row's first word whenever it
carried URL punctuation, so a shell prompt, a home path or an HTTP
status line could be appended to it. Refuse those tokens, and refuse a
column-0 row as a hard continuation (real continuations are indented),
which also keeps `status=200`, `24/7` and `really?` out.

A divider row no longer continues the row above it, so a URL reaching
the right edge does not absorb a full-width rule below it.

Pin the new negatives at detector and provider level, re-indent the
positive continuation fixtures, and pin the `self-hosted.` and `and/or`
trade-offs.
2026-10-24 19:54:40 -04:00
Codeman maintainer 2fdbe786b3 Merge pull request #680: feat(cases): rename a case and its folder, with a lock and live-session handling 2026-10-24 15:27:46 +02:00
Codeman maintainer 886bf9fa0b Merge remote-tracking branch 'origin/master' into relprep/pr-678 2026-10-24 13:55:07 +02:00
Aamer Akhter 27c93b20fb fix(terminal): stop URL continuations from swallowing prose 2026-10-23 10:00:38 -04:00
Aamer Akhter 74a107cf4f fix(terminal): require path labels and preserve markdown context 2026-10-23 09:01:44 -04: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
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
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
Aamer Akhter 536eca8f3d fix(sse): retry an EventSource connect attempt that never opens
An EventSource whose request never gets response headers stays in
CONNECTING without firing open or error, so the backoff never runs and
the staleness watchdog (which only judges a 'connected' stream) cannot
see it. Bound each attempt with a 15s connect timeout: if open has not
fired, close the attempt and retry through the same path onerror uses.

The timer is cleared on open, on failure and at the top of every
connectSSE(), and both the timeout and the failure handler ignore an
EventSource that has since been replaced.
2026-10-22 13:28:43 -04:00
JD 83d80578e4 fix(sessions): read the composer from a styled capture so a prompt suggestion is not a draft
Claude Code draws its prompt suggestion in an empty composer, the first letter in the reverse-video cursor cell and the rest faint, so a plain capture read it as typed text and keep-warm never nudged those sessions. The keep-warm pane read now uses capture-pane -e and drops the suggestion before checking the composer.

Also from review: an off status carries no window, automation and an unreadable composer retry instead of stopping, a failed evaluation re-arms a retry, a trigger during an evaluation queues one follow-up that remove() cancels, and the cap's clock is seeded from lastSubmitAt for pre-upgrade state.
2026-10-19 19:58:14 -04:00
JD 5db6c633d3 fix(sessions): leave a pending idle alert to the user through a keep-alive turn
Only a human spends an idle alert, but the keep-alive turn resolved an unseen one three ways: its working transition, its stop hook, and its own idle prompt replacing the item with a pre-acknowledged one. While the turn runs, a pending idle item is now left exactly as found, the page's stop handler returns on the keep-alive reason before clearing hooks or raising Response Complete, and session:working/session:idle carry keepAlive so the stuck-detection timer neither restarts nor re-arms.

The window that marks the turn closes on its own idle prompt or any other submit instead of a fixed three minutes, which a slow turn's idle prompt (sent a minute after the turn ends) could outlast. The service keeps the default and cap in memory, read through a strict reader so a mid-write read is retried rather than kept, and the cap is clamped before the strict settings save.
2026-10-19 01:42:00 -04:00
JD 71a51df2e2 feat(sessions): keep an idle session's prompt cache warm with a guarded keep-alive turn
Claude Code lets a subscription's one-hour prompt cache expire after an hour idle, and the first turn back re-writes the whole conversation at the cache-write rate; nearly every gap over an hour goes cold, while the saving is concentrated in gaps of one to four hours. Codeman now sends one short keep-alive turn five minutes before the expiry Claude Code reports on its statusline, so the next real prompt still reads the conversation from cache, and stops two hours (configurable) after the last real prompt.

Codeman owns the idle policy for these sessions, as agreed in Discussion #554: a launch with keep-warm on also carries idleCompaction: false in its one --settings JSON, so Claude Code's own idle compaction cannot replace the conversation with a summary a minute before the nudge. It is decided once per launch (keep-warm on, the statusLine exporter injected, Claude Code 2.1.290 or newer, an unknown version fails closed like --name), recorded on the mux record, and picked up by any relaunch; a launch with keep-warm off is byte-identical. A session switched on after launch is kept warm best-effort and the readout says so.

Every guard lives in one pure rulebook: the opt-in (an agent-spawned session does not inherit the app default), a launch that reports cache state, a warm 1h cache with enough tokens at stake and no compaction this gap, a live pane, no usage-limit pause, no respawn, Ralph, task or waiting agent, idle, no open dialog, an empty composer and no recent keystroke. The nudge is a submit and moves lastSubmitAt, which the response viewer's conversation correlation needs; the cap counts from its own persisted lastRealPromptAt instead. Read My Mind drops the exact nudge text, and the turn's stop hook and idle prompt open pre-acknowledged so nothing alerts. The last cache report and the clock survive a restart, so the timer re-arms.
2026-10-18 15:19:08 -04:00
Codeman maintainer f744c503c5 Merge branch 'relnext/security-notices': security notices, starting with the OpenCode Console breach
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-18 10:03:02 +02:00
Codeman maintainer 82c34fba99 fix(ui): notice buttons act once, links send no referrer, solo windows defer
- A closing notice card stays in the DOM for its 200 ms fade, and its
  link and "See what's new" buttons still worked there: a double click
  opened the notice twice. close() now makes the card inert and every
  button handler does nothing once it is closing.
- Notice links (and the combined card's "See what's new") open with
  noopener,noreferrer. noopener alone still sent this instance's origin,
  which can be a tailnet name or a tunnel URL, as the Referer. The plain
  update toast is unchanged.
- The https rule is checked again at the button
  (CodemanSecurityNotices.urlOk): a caller handing showSecurityNotice or
  showUpdateNotice an unchecked URL gets no link button.
- A detached session window (/session/:id) is rendered without
  __codemanCliAvailable, so it could never select the OpenCode notice,
  yet it took the update toast and recorded the version, and the
  dashboard then showed the notice as a separate card. While any notice
  is pending, a solo window now shows and records nothing and leaves
  both to the dashboard; with none pending it shows the plain toast as
  before.
- The wiring tests pin the page's own clock (JSDOM gives the window its
  own Date). On the real clock 18 of them would have failed on every push
  from 2027-01-01, when the fixture notice expires. New tests pin the
  once-only close directly (the old double-click comment claimed more
  than it checked), the single open, the sink check, the solo windows,
  the scroll cap and the light-skin ink.
- CLAUDE.md now says the public .json files are served no-cache, the
  exception to the year-long immutable cache.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-18 09:30:22 +02:00
Codeman maintainer 50bae2d371 feat(ui): security notices, shown only to users of the affected CLI
A hand-maintained src/web/public/security-notices.json carries warnings
about things outside Codeman that its users should act on. The first:
OpenCode's report of unauthorized access to its legacy Console, which
may have exposed provider API keys saved there.

- Pure core in constants.js (CodemanSecurityNotices): select() keeps the
  notices that apply now (inclusive local-day dates, not yet seen, https
  links, malformed entries skipped), and one naming a CLI only when
  window.__codemanCliAvailable[cli] === true. Unknown means not
  installed: a warning about software you do not have is noise. The CLI
  id is data in the JSON; no code branches on it. plan() decides the
  surface, so a load shows each notice once.
- An existing install on a new version gets the notice INSIDE the
  release's update toast: the highlights as before, a warning section,
  and the notice's link next to "See what's new". With no notice the
  update toast is the plain one it always was.
- Everyone else (a fresh install, a same-version reload) gets its own
  card in the toast stack: shield, "Security notice" eyebrow, title,
  message, the link, Dismiss and the ×; amber-to-red accent bar, theme
  tokens only (dark and light skins), full width on phones, an
  opacity/transform entrance and none under prefers-reduced-motion,
  role=alert.
- A notice is recorded as seen only when the user closes it, dismisses it
  or follows its link, never when it appears. Storage blocked shows
  nothing. The notice's own text is textContent and carries its own
  zh-CN, so i18n.js leaves it alone; the card's own copy translates.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-18 08:59:39 +02:00
Codeman maintainer 14a9e66547 Merge origin/master into #644: keep diff|patch beside #621's tsv and zip
Master's #621 added tsv and zip to both file-link extension groups in
constants.js while this branch added diff|patch to the same lines. The
resolution keeps both, in master's order with diff|patch after sql where the
PR put it; the rooted and relative groups stay identical, and every upload
document extension plus .diff/.patch still links in both patterns.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-18 08:12:35 +02:00
Michael GrundbergandClaude Opus 5.5 fe762f8dce feat(links): make .diff and .patch paths clickable
An agent that writes a diff for review prints a path such as
/tmp/.../scratchpad/resolution.diff, and that path rendered as plain text
because the link patterns had no diff or patch extension. The attachment
allowlist already serves both as inert text/plain, so only the two link
patterns in constants.js change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 08:29:44 +02:00
JD 244d9a49f1 feat(uploads): prompt uploads take documents
The Attach button, the Compose and paste-dialog pickers, terminal drop and the
clipboard now take documents beside images and videos: the office formats
pdf/docx/pptx/xlsx/zip and the text family txt/md/csv/tsv/json/log/yaml/yml/xml.

A document is admitted by its name's extension (the MIME a phone attaches to a
.md is anyone's guess), buffered under the same 50MB image cap, and judged on
its bytes by the same fail-closed sniffer: a magic-number signature for the
binary formats, UTF-8 with no NUL byte for text. It is saved under a sanitized
stem of its name, so Q3 report.pdf is findable as paste-<ts>-<hex>-Q3-report.pdf.

sh/py/js/html are refused by name with a clear 400. That is a scope choice, not
a security property: file-raw already serves non-media text inert, so this keeps
the server from storing what it would serve or an agent could run, and html and
svg are the ones deliberately kept out. One client classifier (_promptAttachKind)
now returns image, video or document, and the server's dispatch mirrors it: an
image MIME wins whatever the name says, a document extension takes the document
branch, and a foreign extension with no image MIME is refused only after the
image sniff fails, so nothing the image path accepted by its bytes is lost.
2026-10-13 20:31:39 -04:00
Codeman maintainer 0996f99fbb Merge branch 'rel1411/html-md': render HTML files as pages (#547) and open .md from the terminal in the rendered viewer
Conflicts resolved against #604, #606 and #601:
- constants.js: the rooted link pattern takes html-md's `markdown` and
  `html?` with #606's m4v/ogv; #604's relative pattern gets the same list
  (link-provider-regex pins the two equal); #604's findFilePathLinks /
  folder helpers and html-md's rendersAsPage() are both kept, and
  window.CodemanFilePaths exports all of them.
- terminal-ui.js: #604's resolved `target` (relative, ~/ and folder links)
  now also goes through rendersAsPage(), so every link kind routes .html to
  the page preview; .md routes there through FILE_PREVIEW_EXTENSIONS.
- CLAUDE.md, api-reference, architecture-invariants: both sides' text kept.
- test/folder-grid-ui.test.ts (#604) stubs rendersAsPage() like the other
  panels-ui harnesses.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 18:55:02 +02:00
Codeman maintainer 70e03b8ae7 fix(preview): relative path links take m4v and ogv like rooted ones (#604/#606 integration)
#606 added m4v and ogv to the rooted file-path link pattern for the new video
uploads; #604's relative path pattern copies that extension list, and its
test pins the two equal. The relative pattern now carries them too, so a
relative `clip.m4v` links like `/home/u/clip.m4v`.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 18:26:46 +02:00