Files
AgentHub/docs/superpowers/specs/2026-08-14-split-terminal-view-design.md
T
nntrivi2001 17d56d0e01 refactor!: rebrand CCAM to AgentHub
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).
2026-09-07 12:09:15 +07:00

3.3 KiB

Split terminal view for the Workspace console

Status: approved 2026-08-14.

Problem

Workspace.tsx renders exactly one lane's run console at a time: a single RunSetup/TerminalView switcher (client/src/pages/Workspace.tsx:789-824) driven by page-level state (selectedLaneId, prompt, cwd, model, permissionMode, effort, resumeSession, handle, busy, activeRuns, runHistory, cwdSuggestions). Lanes are independent working directories that can each have their own live tmux/PTY session running concurrently on the server (server/lib/pty-attach.js), but the dashboard can only show one at a time — comparing two lanes' output means switching back and forth.

The user wants to view multiple lanes' terminals side by side: 1 pane (today's behavior), 2 panes (left/right), or 4 panes (2x2 grid).

Approach

Extract a self-contained LaneConsolePane component. Move the existing RunSetup/TerminalView switcher and all its state out of Workspace.tsx into its own component that owns one lane's run lifecycle independently. Each pane gets its own laneId (chosen via a dropdown in the pane header, listing all lanes, not just ones with an active run) and manages its own prompt/cwd/model/permissionMode/effort/resumeSession/handle/busy/activeRuns/ runHistory state — nothing is shared across panes.

Workspace keeps a paneLaneIds: (number | null)[] array sized to the current layout (1, 2, or 4) and renders that many LaneConsolePane instances in a CSS grid. This is the only viable approach given the existing state model is single-lane; the alternative (keeping one shared state object indexed by lane) would require rewriting every handler in Workspace.tsx to be lane-aware and is a much larger, riskier diff for the same result.

Layout

A layout toggle (1 / 2 / 4 buttons) sits next to the existing console header. Grid via CSS:

  • 1: full width — identical to today.
  • 2: grid-cols-2 — left/right.
  • 4: grid-cols-2 grid-rows-2 — four corners.

Each pane has a small header with a lane-select dropdown. If the selected lane has no active run, the pane shows a compact RunSetup (reused component, same as today's pre-run form) so the user can start one directly from the pane. If it has an active run, the pane shows TerminalView as today.

Persistence

The chosen layout mode and each pane's selected laneId are saved to localStorage (e.g. key agenthub.workspace.splitView) and restored on next visit to Workspace. If a persisted lane no longer exists, that pane falls back to unselected (dropdown placeholder).

Non-goals

  • No server/API changes — this is purely a client-side rendering feature. Each lane's run already exists independently server-side; this just lets the UI display more than one at once.
  • No synchronized input across panes (typing in one pane's terminal does not affect others) — each TerminalView keeps its own independent WebSocket connection, unchanged from today's single-instance behavior.

Testing

  • client/src/pages/__tests__/Workspace.test.tsx currently mocks TerminalView and exercises the single-console flow; update it (or add a sibling test file) to cover: layout toggle, per-pane lane dropdown, starting a run from within a pane, and multiple panes rendering independent TerminalView/RunSetup instances.
  • Run npm run test:client before considering this done.