Commit Graph
1739 Commits
Author SHA1 Message Date
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
Devvyn 5ce8213726 fix(links): preserve wrapped configuration filenames 2026-10-28 21:17:50 +08:00
Codeman maintainer f1f7eec4d6 chore: version packages
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 14:10:31 +01:00
Codeman maintainer 8074f67c6a fix: keep the soft-keyboard recovery on Android with a fine pointer (release review, #726)
#726 attaches the keyCode 229 recovery only when (pointer: coarse)
matches, which keeps a desktop dead key from arriving twice. Chromium
reports the PRIMARY pointer as fine once a mouse, a trackpad cover or a
stylus is attached, so on such an Android device the module was off
while the text still came from the soft keyboard, and the #441 dropped
last character and the #541 autocorrect duplication came back.

The gate is now a coarse pointer OR an Android user agent. Android ran
the recovery unconditionally before #726, so this only keeps that; a
desktop (fine pointer, not Android) and a touchscreen laptop stay off.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 09:35:20 +01:00
Codeman maintainer 9f7cba07ab fix: strip nested paste markers until none is left (release review, #726)
Ctrl+V's hand-built bracketed paste (#726) and the phone composer's
dropped CSI 200~/201~ markers from the text in ONE pass, so a clipboard
carrying ESC[20 ESC[201~ 1~ came out of it with an ESC[201~ that ended
the frame early: everything after it was typed as keys and each CR ran.
Both now strip until nothing matches. The composer's copy predates this
release; it is the same one-liner.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 09:30:15 +01:00
Codeman maintainer 593b2eb2ea Merge pull request #721: feat(tabs): L-shaped header strip, wrapped rows run under the toolbar 2026-10-28 09:10:09 +01:00
Codeman maintainer 5add56eaf6 Merge pull request #726: fix(terminal): don't double a desktop dead key, and bracket Ctrl+V after replay 2026-10-28 09:10:09 +01:00
Codeman maintainer 9ee6df027d Merge pull request #730: fix(file-preview): let rendered Markdown fill the resized window 2026-10-28 09:10:08 +01:00
Codeman maintainer f1df8f8c0a Merge pull request #731: fix(response-viewer): a middle click on a file path opens it instead of the dashboard root 2026-10-28 09:10:07 +01:00
Codeman maintainer 9fec26009f Merge pull request #727: fix: don't point the image watcher at a remote session's directory 2026-10-28 09:10:06 +01:00
Codeman maintainer c708312a3f fix: apply the review items for #730 at merge
Set --rv-content-max: none on the File Viewer's rendered document, so
ASCII-diagram blocks (capped by that variable with a 720px fallback the
Response Viewer sets) follow the window like the rest of the document,
and pin it with a third assertion.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 09:09:37 +01:00
Codeman maintainer dd56674a1d fix: apply the review items for #727 at merge
toggleSessionImageWatcher checks the response, so any refusal puts the
switch back and toasts an error instead of a success; architecture-
invariants says the image watcher never watches a remote workingDir.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 09:08:03 +01:00
DDTandDDT 6f820ca990 fix: deliver Ctrl+V paste through terminal.input
Hand the bracketed frame to the target terminal with input() so onData
scrolls to the bottom, clears the selection, and delivers the bytes.
The paste trap no longer writes the frame itself.

Fixes #716

Co-authored-by: DDT <1786035110@qq.com>
2026-10-28 07:09:35 +00:00
JD b63935fa66 fix(response-viewer): a middle click on a file path opens it instead of the dashboard root
Path links and in-document links carry a placeholder href (#, #fragment) that the click delegate resolves itself, but a middle click fires auxclick, never click, so the browser followed the placeholder and opened the dashboard root in a new tab. A middle click on those links now does what a click does; real URLs keep the browser's own middle click.
2026-10-28 00:34:50 -04:00
timkjr 591eebe6e5 fix(i18n): drop the duplicate ZH_CN "Idle" key
The Approvals Inbox strings added a second `Idle: '空闲'` to the ZH_CN
table, beside the original in the status words. Same value, so nothing
changed on screen, but every build warned:

  [WARNING] Duplicate key "Idle" in object literal [duplicate-object-key]
2026-10-27 09:52:07 -05:00
timkjr 2c2f69adba fix: don't point the image watcher at a remote session's directory
A remote-SSH session's workingDir is a path on the remote host, and the
image watcher watched it on the local disk: a folder that is not there,
or a same-named local folder whose images then popped up as the remote
session's. Same root cause as the Ralph @fix_plan.md watcher fix.

Three places start a watch, and all three now skip a remote session:
setupSessionListeners(), the app-wide toggle in PUT /api/settings (which
re-watches every enabled session), and POST /api/sessions/:id/image-watcher,
which refuses to turn it on (INVALID_INPUT, like the Git fetch settings
route) and still lets it be turned off. The Session Options row is hidden
for a remote session, the way the Git tab is, since the client's
_apiPost() does not throw on a refused request.
2026-10-27 09:26:17 -05:00
DDTandDDT dd09a383f1 fix: stop desktop dead-key doubling and bracket Ctrl+V paste
KeyCode 229 recovery forwarded a composed character that xterm had
already delivered, so a desktop dead-key backtick arrived twice.
create() now attaches only when the primary pointer is coarse.

Ctrl+V text paste went through terminal.paste(), which drops the
CSI 200~/201~ markers after a replay clears xterm's DECSET 2004
mirror. Plain text is now a hand-built bracketed frame on the
session input path.

Fixes #716

Co-authored-by: DDT <1786035110@qq.com>
2026-10-27 12:06:45 +00:00
Codeman maintainer ecf28fa3be chore: version packages
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 09:37:13 +01:00
Codeman maintainer c04fa97f78 chore: replace the em-dashes this release added with plain punctuation
The merged PRs (mostly #714's git fetch) added about 120 em-dashes in
comments, docs, test names and three settings strings. Each one is now a
comma, colon, semicolon or parentheses. The English settings strings in
index.html and their i18n.js keys change together, and the zh-CN value
for the fetch interval uses full-width parentheses instead of a double
dash. The one left is the regex in constants.js that matches a dash
typed as a word, which is behaviour, not punctuation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 09:11:19 +01:00
timkjr 285aa0b2b2 fix(tabs): keep the plain wrap when the strip scrolls, add a browser test
Review follow-up for the L-shaped header strip:

- _syncTabStripCorner() keeps the plain wrap when the plain strip is
  already past --tab-strip-max-height, checked before .tabs-corner goes
  on: the float scrolled with the tabs while the toolbar stayed put, so a
  scrolled strip put tabs under #headerRight, and a re-render of a
  scrolled two-rows strip sized the float from the scrolled frame. The
  "- scrollTop" in contentTop is gone (always 0 now).
- A strip just under its max-height that passes it once the float is in
  falls back to the plain wrap as well.
- test/tab-strip-corner.browser.test.ts (real Chromium, real header and
  styles.css), listed in BROWSER_TEST_GLOBS.
- src/web/CLAUDE.md Tab layouts entry describes the L-shaped classic strip.
2026-10-26 21:35:04 -05: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 0f1fca6ea0 Merge pull request #712: feat(tabs): start a session beside any tab from an opt-in + in its actions 2026-10-27 02:24:17 +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 5908cc51fd Merge pull request #694: feat(agent): COD-529 add bounded inspection and guarded submission
Conflicts resolved by keeping both sides:
- CLAUDE.md (Agent CLI): #694's opening (an HTTP client, the experimental
  agent-session-routes behind agentCoordinationEnabled) with #708's clause
  on the directory spawn and its two refusals.
- docs/api-reference.md (codeman agent): #708's spawn paragraph and --json
  shape, with #694's inspect/stage/submit in the verb list and the guarded
  endpoints named before "adds no route for the other verbs".
- src/cli-agent.ts (@fileoverview): #708's quick-start / directory spawn
  routes and #694's experimental routes in one endpoint list; the two
  node:fs/promises imports (#708's realpath+stat, #694's open+stat) merged
  into one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 02:14:44 +01:00
Codeman maintainer 6612a89ebc Merge pull request #714: feat(git): a Fetch button in the Git status header, and an interval for it app-wide and per case
Conflict in CLAUDE.md (Adding Features, App setting) resolved by keeping
both sides: #714's sentence that a setting the server acts on must be
synced, after the `.strict()` clause, and #719's mutateSettingsFile() rule
at the end of the bullet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 02:11:49 +01:00
Codeman maintainer 6d07a1059f Merge pull request #717: fix: cycle Command-K CLI picker both ways at query boundaries
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 02:11:29 +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 6539bc6c82 fix: apply the review items for #714 at merge
- Session update no longer resets "How often" while typing: updateGitFetchStatus
  (session-ui.js) leaves a control alone while it is document.activeElement, and
  autoSaveGitFetch writes the clamped interval back itself (Enter saves with the
  field still focused).
- Partial override shows the app values: enabled = session ?? app switch,
  interval = session ?? app interval ?? 300 (session-ui.js updateGitFetchStatus).
- runFetch (git-fetch.ts) arms the spawn with what the core.sshCommand read left
  of timeoutMs, and answers ETIMEDOUT without spawning when nothing is left, so a
  git that never answers costs one timeout, not two.
- fetchEnvFor keeps a GIT_SSH from the server's environment (repoSshCommand ||
  base.GIT_SSH), fileoverview and CLAUDE.md line updated to say so.
- i18n.js: the zh-CN "Git fetch on an interval" description scopes the only
  writing git command to the Git window.
- styles.css: the disabled/busy rules are scoped to .git-status-actions
  .btn-icon-sm (moved beside .git-status-actions), and the busy pulse stops
  under prefers-reduced-motion.
- Comments that called the override per case now say per session: session.ts
  (field, restore and getter docs, setGitFetchSettings), git-fetch-scheduler.ts:2,
  server-timing.ts:78, server.ts (cleanup and restore), app.js _onSessionUpdated,
  session-ui.js (openSessionOptions and updateGitFetchStatus).
- Tests: test/git-fetch-session-options-ui.test.ts (new, JSDOM: typing survives
  an SSE update, the Enter write-back, both partial cases); test/git-fetch.test.ts
  (GIT_SSH kept; one bound for the read and the fetch, stub git on PATH).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 01:58:14 +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 04aa44b69c fix: apply the review items for #712 at merge
- session-ui.js _reloadCasesAfterRun: reload onto the toolbar's own case after every launch, not only one that names its case; comment updated
- test/tab-new-session.test.ts: a worktree tab's "+" Claude launch, the toolbar Run and a group's "New session" each keep the toolbar's case after a "+" moved lastUsedCase (fail without the fix); the reload setup is shared with the existing lastUsedCase test
- docs/architecture-invariants.md:349: the _reloadCasesAfterRun sentence covers every launch
- app.js _openTabLayoutMenu focusout: focus landing on the trigger is not a departure, so pressing the "+" again closes its picker in a real browser
- tab-rail-resize.js openTabRailActionMenu: close the group menu too, so the rail's ... pressed again does not leave the picker beside the row menu
- tab-rail-resize.js:329-331: the "New session here…" comment no longer says Enter on the "+" does nothing

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 01:30:07 +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
timkjr 3dafc89095 feat(tabs): L-shaped header strip, wrapped rows run under the toolbar
A wrapped classic tab strip stopped at the toolbar's left edge on every row,
so the space under the header chips stayed empty and long tabs took a row
each. Once the strip wraps, the toolbar now sits over the strip's top-right
corner and the strip lays its tabs out inline around a float sized to the
toolbar's box (_syncTabStripCorner), so only the rows beside the toolbar stop
short of it and every later row runs the header's full width.

- Wrap is still measured with the toolbar in flow (the corner is dropped
  before the measure), so the single-row strip is unchanged.
- A ResizeObserver on the toolbar re-decides wrap + corner and redraws the
  lineage routes when a reading or the divider changes its size.
- Falls back to the plain strip when the first tab cannot fit beside the
  toolbar (it would otherwise leave an empty band).
- Classic arrangement, desktop, header host only; state/case/ledger, the
  sidebar and the tab rail keep their own layouts.
2026-10-26 19:19:06 -05:00
JD fc0ecc6051 fix(tunnel): give the unauthenticated tunnel its own acknowledgement
The network acknowledgement that install.sh writes for every passwordless LAN install also satisfied the tunnel guard, so one click on the welcome screen's tunnel link published full terminal control to a public URL with no login and no confirm. The tunnel now needs CODEMAN_ALLOW_UNAUTHENTICATED_TUNNEL or --allow-unauthenticated-tunnel (carried by web -d and service install), and with no login the browser asks before every enable, reading a new loginConfigured field from GET /api/tunnel/status.

The guard is now decided once per request and required again where the tunnel starts, so a save that found the tunnel running can no longer restart it unguarded after a concurrent stop.

Closes #706
2026-10-26 03:13:25 -04:00
JD 4f6481bf7c fix(tabs): open the tab + from the keyboard and probe a Docker tab's container
The + announces a menu but Enter and Space did nothing on it, and the header strip has no row menu to fall back on. A + on a Docker tab the toolbar has not selected read only the link-time mode list, since nothing probed its container.

Opening the picker from the keyboard focuses an entry, so a held Enter or Space repeated onto it and launched that CLI; the shared tab-layout menu now ignores a repeated Enter or Space.
2026-10-26 03:00:23 -04:00
Saqeb Akhter 0cc9332f3d fix: cycle Command-K CLI picker at query edges (johoja12/Codeman#21) 2026-10-26 01:14:48 +04:00
Devvyn 3411d7f35a fix(git): the fetch timeout reaches the whole tree, a tick covers every session on its folder, and the rest of the review (review)
`execFile` signals the `git` parent only, so a fetch that times out can leave its http helper
still running, and on the interval that repeats every run with nobody watching. `runFetch` now
spawns `detached`, so the fetch and the helper it spawns share one process group, and the kill
is SIGTERM to the negative pid then SIGKILL `FETCH_KILL_ESCALATION_MS` later; the escalation is
not cancelled when the promise settles, because the caller is answered at the timeout while the
group is still dying. The output cap is kept, and the clone's slot queue is not needed here.

The tick is two passes: every enabled local session is collected by folder, a folder is due when
ANY session open on it is due, the folder is probed once for the group, and the run is published
to every session on it. A single pass decided by the order the sessions were read, so [long,
short] published only the short one and the 3600 s session's row never updated again while its
folder was fetched every 30 s.

`gitNonInteractiveEnv` writes BatchMode ssh as a default, not an override: `core.sshCommand` is
read inside the same slot and the same bound as the fetch it serves, and `fetchEnvFor` keeps the
repository's answer when it has one, because that is usually the setup that works on a machine
with several ssh setups. The `accept-new` host key it can write is stated next to the BatchMode
note: a prompt is stopped, a write is not.

The write claim is scoped to the Git panel and to the timer, with cloning and worktree management
named as the writes that happen when the user asked for them, and the zh keys sit with the group
of the UI surface their English string belongs to.

Tests: the process-group test proves the tree by group membership, because the helper is a
grandchild (`git fetch` runs `git remote-http`, which spawns `git-remote-http`) and because the
group is what the negative pid reaches, and it remembers each process once found instead of
re-scanning a group it had already emptied. The non-zero-exit test changed premises: `git fetch
--quiet` in a repository with no remote exits 0 on git 2.47.3, so resolving empty stdout there was
correct, and the case that has to reject is a remote pointing at a path that is not a repository.
The join test asserts after the run and does not await the request before the held run is
released, which would wait on the run the test is holding.
2026-10-25 22:31:25 +08: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
Devvyn ea4318947b fix(git): the interval fetches one run per folder, and the rest of the review (review)
The scheduler is the part that runs with nobody watching, so it is decided per folder
rather than per session: sessions sharing a folder are the same clone and share one run
that both see, `inFlight`/`lastRunAt` are keyed by the resolved folder, the schedule is
stamped when a folder is picked instead of when its run starts, and a tick is skipped
while the previous one is still going. The click route joins a run already going for the
same folder rather than starting a second fetch in one clone.

The tick keeps ONE bound for the git processes of the whole run and hands it to every
folder, and bounds its folders with a bound of its own: the same semaphore cannot serve
both levels, because a folder holding a slot would then wait for a slot its own fetch
needs. Both bounds are `FETCH_CONCURRENCY`, so the ceiling is unchanged. The bound claim
in CLAUDE.md, the `Limiter` jsdoc and docs/api-reference.md now describes it as it is.

Before anything is fetched the folder is probed with the bounded path probe, so a case on
a mount that stopped answering is skipped and never touched.

The rest of the review: the hooks are named as they really are (`reference-transaction`,
`pre-auto-gc` through the maintenance a fetch triggers, config commands such as
`credential.helper`) and `FETCH_CONFIG_ARGS` turns maintenance and gc off for an
unattended run; the claim that an agent in the same repository is not disturbed is
replaced by the ref-lock collision it actually is; the click route publishes its own run
as `trigger: 'click'`; `autoSaveGitFetch` uses `_apiJson` so a 400 shows the error toast
and applies the returned settings; and the two wording fixes.

Tests: the scheduler pins one run per folder, the two bounds, the probe before anything
runs, and that closing the session holding the override keeps the folder while another
session is still open on it; the route test pins the click publish and the join, asserting
the join before the held run is released — releasing first lets the run finish and clears
the lane, so the joiner would be entitled to its own fetch and the test would pass for the
wrong reason.
2026-10-25 20:04:01 +08:00
Devvyn 9432d9f9e2 feat(git): a Fetch button in the Git status header, and an interval for it app-wide and per case
Fetch is the only git command Codeman runs that writes, and the only one that runs a
repository's own fetch-post hook, so it stays behind a click and off by default. The Git
status window's header row gains Fetch; App Settings -> Bottom bar gains the switch and
the interval, synced because the scheduler acts on them, and Session Options -> Git
overrides both per case. The scheduler reads settings fresh each tick, times the interval
from the start of a run, never overlaps runs, and re-checks the Docker and remote rules
every tick before any git runs. Nothing but remote-tracking refs moves: HEAD, the index
and the working tree are proven unchanged against real git while origin/main advances.

Tests: src/git-fetch.ts and the scheduler, the per-case settings route, the synced/display
setting split, the two new i18n rows, and the Fetch block of the Git status browser suite.
2026-10-25 17:23:55 +08:00
Aamer Akhter c78635a3d3 feat(agent): make coordination routes opt-in and address review
Gate inspect, stage and submit behind the synced agentCoordinationEnabled
setting (default OFF, App Settings > Cross-session features, zh-CN
strings). While off, all three routes answer 403 FORBIDDEN naming the
setting, checked before session lookup; the CLI verbs print it verbatim.

Remember the attributed Codex rollout per session and re-verify only its
head on later calls, rescanning only date directories not older than it
for a /new conflict. The first walk reads 1 KiB prefixes first, so a
store of 2,100+ unrelated rollouts still attributes.

Answer unexpected inspection errors with 422 OPERATION_FAILED and log
the session id and error code only.

Read the guarded composer glyph and footer from the CLI registry: codex
workDetect.promptGlyph and a new workDetect.composerFooterLine
capability, compiled through compileVersionRegex. Codex footer effort
words are one shared list with modelDetect.screenLine.

Fix the wiki API reference link, frame the versioning policy entry as a
case of the experimental rule, join the CLAUDE.md Agent CLI paragraph,
and update the changeset, skill reference and architecture notes.
2026-10-24 22:54:17 -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
DevvynandClaude Code 34f7766d43 fix(file-preview): let rendered markdown fill the resized window
The corner grip and Expand size the window correctly, but the document
kept a fixed 960px reading column from before the resize feature
existed, so text stopped reflowing once the window grew past it.
Pinned by new rule 13 in file-preview-window.test.ts.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-25 09:56:40 +08:00