psmux (the WinGet `tmux` on Windows) has no `=name` exact-match targets: it
answered "no server running on session '=agenthub-lane-1'" for a live pane,
so every Windows start reported "the session's command exited as soon as it
started" while the session ran on, and the browser could not attach.
- server/lib/tmux.js: `session`/`pane` targets probe the first live session
once — `=name` failing where the bare name answers means bare names from
then on; real tmux keeps `=name` / `=name:` (it matches by prefix otherwise)
- pty-attach.js and cc-update-all-helper.js use the same targets
- /api/workspaces and `agenthub workspace(s)` / `--workspace` as aliases of
/api/lanes and `agenthub lanes` / `--lane`; old names print one stderr note.
REPL `help lanes|workspace|workspaces` resolves through the aliases.
- A workspace holds up to ten sessions (slots 0-9); every console tab is a
group of 1-4 split panes (PaneGroups, agenthub.workspace.paneLayout);
parseGroups keeps the stored active tab when an earlier one is dropped.
- Browse button beside path fields: POST /api/lanes/pick-path opens the OS
picker on the server's desktop — loopback only and no forwarding header
(EREMOTE), a display failure is ENODIALOG not a Cancel, the dialog is
killed when the request closes, a folder field falls back to the in-app
browser on any failure.
- Changes rail counts chip, image viewer (/git/raw), status bar session
counts and per-day cost (`date=` on /api/pricing/cost).
- /agenthub-open and /agenthub-doctor run from the INSTALLED build
(supersededBy), doctor offers fixes behind two confirmations; plugin-open
treats a locked bootstrap as non-fatal.
- New-terminal shell waits for the first attach (issue #55).
- repo-onboarding / debug-live-issue skills removed.
- OpenAPI: pick-path, lane/date on /pricing/cost, tool on /run/history,
/api/workspaces mount.
- GET /api/workflows: one LEAD() window pass in a read worker (read-pool.js,
workflow-stats.js); idx_events_session restored, idx_events_tool added
- compat-sqlite gains function/pluck/readonly so a plugin install without a
better-sqlite3 build boots again; antigravity-usage uses db.js's Database
- install-hooks adds no checkout hooks while the agenthub plugin is enabled
(events were doubled)
- terminal attach runs the live tmux server's own binary; the pane scrollbar
gets a visible 2-column style, and claude panes start with
CLAUDE_CODE_DISABLE_ALTERNATE_SCREEN=1 so tmux keeps history to scroll
- Codex sessions' conversation in GET /api/sessions/:id/transcript
- Codex/Antigravity sessions claimed by their workspace without hooks
- UI-audit follow-ups, docs and OpenAPI
- tmux targets are exact (`=name`, `=name:` for panes/options): a bare
`-t agenthub-lane-1` resolves by prefix to agenthub-lane-12 once lane 1
is gone, so killing a finished lane could kill another.
- getRun/killRun refuse ids that are not `agenthub-lane-<n>`, so
DELETE /api/run/main can no longer kill a user's own tmux session.
- router/apply reports restart_required only for a running pane; getRun
returns a `gone` handle, so it was always true.
- needs_action is cleared only by the session that raised it (new nullable
lanes.needs_action_session); a second session in the same cwd used to
clear it on its second hook.
- Split view: every pane shows the selected lane, each in its own tmux
session (`slot` 0-3 on POST /api/lanes/:id/start; slot 0 stays the
lane's `run_id`, slots 1-3 are `agenthub-lane-<id>-<slot>`). A pane's
Active runs hides what the other panes show; stop/reset/remove kill
every slot. No per-pane lane picker any more.
- Whole-file editor: PUT /api/lanes/:id/git/file (lane-file-write.js)
with the read path's guards, existing text files only, and a baseSha
stale check (409 ESTALE). A file it cannot take says why.
- Per-hunk review bar under each hunk's last changed line: prev/next,
"n of N", Undo, Keep. Review diffs no longer nest their own scroll box
(unless windowed), so navigation moves the one real scroller.
- Files tree lists untracked-not-ignored files and colours entries by git
status (M/U/A/D/R), folders holding changes get a dot.
- Changes panel: Unstage all on the single commit/Stage-all row; sizes
Narrow (30%) / Medium / Full screen, no 70/30.
- Lane card: the needs-you banner is gone (the strip tile keeps it).
Clean break, no compatibility alias. Renames the product name, the CLI
binary (`ccam` -> `agenthub`), the env var prefix (`CCAM_` ->
`AGENTHUB_`), the Prometheus metric namespace (`ccam_*` ->
`agenthub_*`), the tmux session prefix (`ccam-lane-<id>` ->
`agenthub-lane-<id>`), the per-repo runtime profile directory
(`.ccam/profile` -> `.agenthub/profile`), the lanes state root
(`~/.claude/ccam-lanes` -> `~/.claude/agenthub-lanes`), the bundled
plugins (`plugins/ccam*` -> `plugins/agenthub*`) and their slash
commands (`/ccam-doctor` -> `/agenthub-doctor`, `/ccam:*` ->
`/agenthub:*`).
Because there is no alias, an existing install has to be updated by
hand: rename its `.ccam/profile` directory and any `CCAM_*` variables,
and reinstall the plugin under its new name.
Two other name families are deliberately left alone here, since they are
not just strings: the `Claude-Code-Monitor` repository/marketplace name,
and the `agent-dashboard` npm package plus its `~/.claude/agent-dashboard`
runtime directory, which holds the live database and needs a migration
rather than a rename.
Verified: server 1406/1406, client 459/459, `grep -i ccam` clean across
tracked files, `openapi.yaml` regenerated (95 paths, unchanged count).
Sessions created before the mouse-on fix landed (or before the server
restarted to pick it up) never got the set-option call, so their pane
still had no drag/wheel scroll. Moving it into pty-attach's attach()
makes it idempotent per-connection instead of once-at-birth.
xterm.js's onData hands the browser a plain string, and WebSocket.send(string)
always emits a TEXT frame — pty-attach.js only forwarded BINARY frames to
pty.write(), so every keystroke was silently dropped as an unparseable JSON
control message. Now any non-control text frame reaches the pty.
Deeper root cause of the terminal never accepting input at all: the /ws
WebSocketServer used the {server, path} shorthand, whose own internal
upgrade listener calls handleUpgrade() for every upgrade on the shared
http.Server and aborts with 400 on a path mismatch — killing /ws-pty/*
upgrades before the PTY server's own listener ever ran. Switched /ws to
noServer + a manual path-checked dispatch, matching /ws-pty's pattern.
node-pty ships prebuilt binaries for darwin/win32 only — on Linux it
needs a native build via its install script. The plugin's
--ignore-scripts install (scripts/plugin-bootstrap.js, deliberately
skipped to avoid requiring a build toolchain) silently left node-pty
unusable: server/lib/pty-attach.js's require() threw
"Cannot find module './prebuilds/linux-x64//pty.node'" the moment a
terminal was attached, leaving TerminalView permanently blank with no
visible error.
@lydell/node-pty is an API-compatible fork that ships each platform's
binary as a regular optionalDependency instead of a postinstall build
step, so a plain --ignore-scripts install resolves a working native
binding on Linux with no compiler needed. Verified by installing with
the exact `npm install --omit=dev --ignore-scripts` invocation the
plugin bootstrap uses and confirming require() succeeds.