Commit Graph
1045 Commits
Author SHA1 Message Date
Devvyn dc22519adb fix(discord): discard a thread created for a replaced parent channel, test the notify timeout, document the bridge invariants
- a thread creation that finishes after the parent channel changed is dropped, and a finished creation only clears its own pending entry
- postJson takes an optional timeout, pinned by a test against a server that never answers
- drop the flat discord-bridge.json fallback
- add a Discord bridge section to architecture-invariants
2026-10-30 06:42:17 +08:00
Devvyn 1ab54b9e9b fix(discord): report a refused post as a failure, bound the file probe, drop threads of a previous channel
- codeman notify exits 1 when the route answers 200 with data.ok false
- notify request times out at 30 s instead of hanging
- fileProblem probes the file with the bounded probe before realpath
- the thread map records its parent channel; a new channel starts afresh
- threadFor returns its failure instead of sharing this.last
2026-10-30 04:58:17 +08:00
Devvyn 0db9b55961 fix(discord): rate limit discord-notify, bound the folder probe, stop sendTest swapping the live config
- discord-notify: per-session token bucket (6 a minute), 429 RATE_LIMITED, plain line from codeman notify
- fileProblem probes the working folder with the bounded probe before realpath
- sendTest uses its own DiscordApi and leaves this.cfg alone
- drop the resumed-tab check from the typed hint (only Claude's field was read)
- first sessionRenamed per session records the name without a PATCH
- boot goes through ensureLoaded, so one gateway starts
- a thread created without an id reports the failure
2026-10-30 03:47:25 +08:00
Devvyn 6d4357502b feat(discord): make the typed first-message hint a separate opt-in, off by default
Claude keeps the silent system-prompt hint (startupHint, on). Other CLIs are sent the typed message only with typedHint, which costs a model turn per tab.
2026-10-30 01:38:37 +08:00
Devvyn eb0553a645 fix(discord): address review on #741
Builds the alert tail only for a post that goes out; separates deduped from disabled and never dedupes a deliberate notify; types the startup hint with fromUser false and skips resumed, remote and Docker tabs; clears per-tab maps and closes the bridge on stop; acts only on a real delete, not a detach; serialises thread-map writes; reloads the bridge after a discord.json restore; probes and self-ignores .codeman-discord; validates the message id; reacts when a reply is rate limited; documents what leaves the machine.
2026-10-29 23:03:26 +08:00
Devvyn 902d4491e5 feat(discord): testable codeman notify, short-id route, wiki page, docs and glue tests
Moves the notify command into src/cli-notify.ts with tests, lets discord-notify take the 8-character id a Docker case exports, adds the wiki page and index entries, rewrites the stale changeset and security notes, and pins the WebServer wiring.
2026-10-29 21:46:06 +08:00
Devvyn e3ca07d59d docs(discord): restore api-reference.md formatting, keep only the Discord section
A formatter run re-padded every table in the file and broke the cron docs test, which matches the original spacing.
2026-10-29 21:39:49 +08:00
Devvyn 1c4338e8b5 feat(discord): codeman notify, startup hint for every CLI, and file send/receive
Adds the codeman notify command, a startup hint (Claude via --append-system-prompt, other CLIs once on first idle), files saved from thread attachments into the tab folder, and --file uploads limited to the session folder.
2026-10-29 21:27:23 +08:00
Devvyn 61bf26cfdf feat(discord): two-way Discord bridge with a thread per tab
Alerts for permission prompts, questions, idle and errors post into a per-tab thread; a reply from an allowlisted user is typed into that tab. Thread creation, thread cleanup on tab delete and pinging are configurable. POST /api/sessions/:id/discord-notify lets a CLI ask for the user. Setup is in docs/discord-bridge.md.
2026-10-29 21:04:18 +08:00
Aamer Akhter f23e18b1fc fix(terminal): report the pane directory in the workspace's own spelling
tmux reads a pane's directory from the process cwd, which is always the
resolved path, while workingDir keeps the spelling the case was created
with. For a workspace reached through a symlink every reading looked
like it was outside the workspace, so the browser sent each relative
link through the attachment route instead of the workspace routes.

Each session now resolves workingDir once in the background, bounded by
probePathKind() and a timed realpath(), and setCwd() writes a reading at
or under the resolved path back in workingDir's spelling. Until that
settles only a reading lexically inside workingDir is taken. An unknown
probe or a failed realpath leaves readings untranslated and retries
after WORKSPACE_REALPATH_RETRY_MS.

Also corrects two comments: cwd is written to state.json but never read
back, and a blank pane path is usually a pane whose program exited.
2026-10-28 19:29:05 -04:00
Aamer Akhter 54c1692350 fix(terminal): clear the pane directory when a read says the session has none
applyPaneExits() only applied the manager's directory when it returned a
value, so after a successful read a session that was split into several
panes, or was no longer on the socket, kept its stale cwd. The manager
already keeps its map across a failed or empty read, so the server now
applies its answer as given, undefined included, and setCwd() broadcasts
the clear once.

A blank #{pane_current_path} on a single-pane session (tmux could not read
the process cwd that tick) now carries the previous entry forward in
derivePaneCurrentPaths(), so only a split or unlisted session clears.

The server tests now drive the real TmuxManager with only the tmux read
faked. docs/api-reference.md documents SessionState.cwd.
2026-10-28 15:28:59 -04: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
Codeman maintainer 1ad9db7606 fix: keep a remote session's fix plan authoritative (release review, #722)
#722 stopped handing a remote session's workingDir to the Ralph tracker,
which also left isFileAuthoritative false for remote sessions only. That
flag is what keeps the output-based completion guessers off, and it is
true for every local session with a working directory, so a remote loop
could end, and mark every todo done, on a prose line such as "All tasks
completed" while a local one cannot.

setRemoteWorkingDir() records the path without reading or watching the
local disk, so the fix plan stays authoritative as for a local session;
the file still reaches the tracker only through fix-plan/read, over ssh.
The test pins both, and fails on the previous head.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 09:25:50 +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 6d640b08ce Merge pull request #728: fix: read a remote session's @fix_plan.md on its host, refuse remote Ralph writes 2026-10-28 09:10:06 +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 66528962ee Merge pull request #722: fix: don't point the Ralph @fix_plan.md watcher at a remote session's directory 2026-10-28 09:10:05 +01:00
Codeman maintainer cdff4d4d98 fix: apply the review items for #721 at merge
Add the L-shaped strip to the Tab layouts section of
docs/architecture-invariants.md, the mechanism file src/web/CLAUDE.md
links to.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 09:09:56 +01:00
Codeman maintainer 79a7f494b1 fix: apply the review items for #728 at merge
Export isPathWithinRoot from file-routes.ts and use it for the remote
@fix_plan.md confinement, so the two checks cannot drift; rewrap the
readRemoteFixPlan JSDoc and say NOT_FOUND; list fix-plan/read and the two
write refusals in docs/remote-sessions.md and architecture-invariants.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 09:09:12 +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
Codeman maintainer 74fc3ea6ae fix: apply the review items for #722 at merge
Note in docs/remote-sessions.md that the @fix_plan.md auto-load does not
run for remote sessions, and rewrap the test's header comment.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 09:07:28 +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
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 8a6e34bece docs: list both unauthenticated-exposure acknowledgements as stable
CODEMAN_ALLOW_UNAUTHENTICATED_NETWORK and the new
CODEMAN_ALLOW_UNAUTHENTICATED_TUNNEL (#718), with their CLI flags, join
the documented environment variables the versioning policy covers, from
1.46.0, the release that gave each its own meaning.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 09:36:15 +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
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 93691e6096 fix: bring the skill, plugin, README and API docs in line with the merged agent behaviour (release review)
The self-gate now says the skill reads saved Codex conversations offline outside a session; the staging
reference says only tagged input gets 409 during a guarded submit (the rest is held), and the API
reference notes a managed write past a full hold queue falls back to a direct write. Also: missing
reference rows, agent --help naming inspect/stage/submit, the tunnel opt-in in Remote Access, MCP wording.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 03:04:14 +01:00
Codeman maintainer 1752d7ee2c fix: refuse to git fetch while the Docker case list cannot be read (release review)
readDockerCases() reads an unreadable or half-written docker-cases.json as [], so a fetch (which runs
repository hooks, and runs unattended on the interval) could run inside a Docker workspace. The fetch
paths now read it through a strict reader: the Fetch button answers a failed run (state 'error') and the
scheduler skips the tick, undoes its picks and logs once. The status and diff routes are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 02:58:37 +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 b21b8485c3 Merge pull request #710: test(deepseek): stop pinning Node's one-shot zstd decoder to the first frame 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 01cd509c2c Merge pull request #708: feat(agent): COD-549 launch workers in existing project directories 2026-10-27 02:12:50 +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 cad8621324 Merge pull request #707: feat(history): COD-549 read saved Codex conversations offline
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 82ef8b1edd Merge pull request #719: fix(settings): serialize settings.json writes so a quick-start cannot tear the file
Conflict in src/web/routes/system-routes.ts (PUT /api/settings) resolved by
keeping both sides: #718's line that never stores an unauthorized
tunnelEnabled:true runs first, then #719's serialized mutateSettingsFile()
write replaces the inline read and plain writeFile.

test/routes/system-routes-tunnel-guard.test.ts: #718's new tests asserted
plain writeFile calls on settings.json. With #719 every write goes to a temp
file renamed over it, so the assertions read writes through a settingsWrites()
helper (writeFile contents keyed by their rename), and the concurrent-stop
harness applies a write and the stop at the rename.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 02:10:35 +01:00
Codeman maintainer 6368128f1e fix: apply the review items for #694 at merge
- src/web/routes/session-routes.ts:2102: the guarded-submit 409 now applies to tagged input (clientId + seq) only; untagged input is held like a WebSocket frame. The raw write (:2228) goes through holdForGuardedLease and answers 200 once held (a send-and-wait reads `delivered` from the held write; a full queue releases the waiter and answers 409); a mux write takes its own turn in writeViaMux.
- test/guarded-lease-held-input.test.ts: real-Session route tests for an untagged raw POST during the lease (written after the Enter) and a held send-and-wait (delivered: true); both fail without the fix.
- test/routes/session-routes.test.ts + test/mocks/mock-session.ts: the 409 test now covers tagged input only; new tests for untagged raw (held), untagged mux (own turn) and a full hold queue (409); MockSession gains holdForGuardedLease/releaseGuardedLease and a lease-aware writeViaMux.
- src/session.ts:4787 (guardedInputHeld JSDoc) + write() + docs/architecture-invariants.md: write() refuses during the lease only, so cron's paste mode can land ahead of input still draining.
- docs/api-reference.md: the input route's 409 is for tagged input; untagged input is held.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 02:07:39 +01:00
Codeman maintainer 4b3eebd0ed fix: apply the review items for #708 at merge
- test/cli-agent-project-spawn.test.ts:345: the doc-sync test no longer reads .changeset/project-worker-directories.md (changeset version deletes it at release); README, API reference and wiki stay.
- CLAUDE.md:132 + src/cli-agent.ts @fileoverview (invariant 5): the directory spawn (POST /api/v1/sessions plus /interactive or /shell, no Agent-Origin header, so no marker) and its refusals (empty --working-dir, bare spawn from a HOME cwd); the invariants now name test/cli-agent-project-spawn.test.ts too.
- test/cli-agent-project-spawn.test.ts:322: "allows a cwd below HOME" mocks the cwd to <temp HOME>/src/app, so it covers the case the guard must leave alone.
- docs/wiki/Driving-Codeman-From-An-Agent.md:59: split the sentence so exit 4 covers only the empty --working-dir; spawn "" reaches quick-start and exits 1.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 01:48:21 +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 8574e3c880 fix: apply the review items for #719 at merge
- src/web/settings-writer.ts:110: export assertSettingsWritable(), the writer's own read-for-write rule run on its own
- src/web/routes/case-routes.ts:1860: an admin's rename checks settings.json before it ends any session or moves the folder, and answers 409 CONFLICT with the writer's message (placed before the session-starting check, so nothing is awaited between it and the lock)
- test/routes/case-rename-routes.test.ts:314: the torn-file pin now expects 409 CONFLICT (no stoppedSessions, like the other pre-checks)
- test/routes/case-rename-routes.test.ts:331: new test, a live session with stopSessions and a torn file: 409 CONFLICT, cleanupSession not called
- docs/api-reference.md: the Settings paragraph says an admin's rename answers this refusal as 409 CONFLICT before ending sessions
- src/hooks-config.ts:1161, src/prompt-cache-keepwarm.ts:341: comments describe the atomic writer, not an in-place rewrite

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 01:24: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 e9f3aa20a5 fix: apply the review items for #710 at merge
- docs/architecture-invariants.md DeepSeek mechanism (1): the decoder stops at the first frame only before Node 24.22 and 26.10 (nodejs/node#65865)
- src/deepseek-transcript.ts:33: reflow the over-long comment line to the width of the lines around it
- docs/deepseek-integration.md:241: same reflow

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 01:21:56 +01:00