Server (git-diff.js, git-ops.js, worktree.js, routes/lanes.js):
- Every lane git call runs with GIT_LITERAL_PATHSPECS=1 and -z porcelain, so a
file name is a file name, never a glob, and odd names parse correctly.
- A lane that is a subdirectory of its repo sees and acts on ONLY its own
paths (reads, stage/unstage/discard/commit, and gitFacts.files).
- Any file named settings.local.json (the lane's router API key) is refused as
403 ESECRET on disk (symlinks resolved) and at every committed rev.
- commit refuses with 400 ESTAGEDOUTSIDE while anything outside the lane is
staged (checked with --no-renames, so a move into the lane is caught).
- push resolves its remote (upstream → only remote → origin), else 400
ENOREMOTE; unstaging a rename unstages both halves.
- Git writes serialize on the repo's git dir, not the lane id, so two lanes on
one index cannot collide on index.lock.
- git refusing an action (a numeric exit status) answers 409 EGIT with git's
stderr instead of a 500 carrying a numeric code; one shared parseNumstat.
Client:
- Changes panel: no stale answer from a previous lane or an older quick-open
query repaints the panel; discard disarms on lane switch; Enter on a file
path opens it instead of folding; bounded tree expand-all.
- Large diffs (> 500 rows) render only the visible rows, with stable width
and in-sync split columns; mirrored split scroll is clamped.
- Git bar: no "in sync" without counts, a clear no-remote message, branch list
re-read after a switch, and no previous-lane facts after a lane switch.
- Tooltips no longer flicker or close on unrelated scrolls.
- Changes card beside lane detail with five modes: Files tree, overview,
Review (whole diff, per-file collapse), single file, commit history.
- LaneDiffView: unified / side-by-side with independent scroll on both axes
and mirrored scrollLeft, expand/collapse context, intra-line emphasis.
- Staged-only commit box; git actions (fetch/pull/push/stage/unstage/
discard/commit/switch/branch-create/branch-delete) guarded in git-ops.js.
- Server: git-diff.js + git-ops.js behind GET /api/lanes/:id/git/* and
POST /api/lanes/:id/git/:action; gitFacts gains upstream/ahead/behind.
- Add lane: new/existing branch, current branch, worktree list, prefill
from the git bar's "New lane here"; /api/run/cwds gains a repo kind.
- GlobalTooltip upgrades every native title into a themed anchored bubble;
chart/info popovers use theme tokens.
The lane strip's tiles are now drag-reorderable (Alt+←/→ is the keyboard
path), and a lane's name can be set in place from the detail header. The
"Lane 23 ·" prefix is gone from both — the bare `#23` stays on the tile,
because that number is how the CLI addresses a lane.
Card order is a per-viewer preference, not a lane fact: it lives in
localStorage (`agenthub.workspace.laneOrder`) beside the split-view layout
and writes nothing server-side, so `GET /api/lanes` and `agenthub lanes`
keep answering in id order. Renaming is the opposite — it is PATCH
/api/lanes/:id with {title}, the one lane column a user now types into
directly.
That made a patch value reachable by hand for the first time, so
`bindable` now rejects any non-scalar for every PATCHABLE column with
EBADFIELD, and the route answers 400 for that and for the
SQLITE_CONSTRAINT family, rather than handing better-sqlite3 a datatype
throw and reporting 500 on plain bad input.
The uncommitted-files tooltip became a real panel. `GET /api/lanes/:id/git`
now returns `files` as {path, status, added, deleted} records — deltas from
one `git diff --numstat HEAD`, null where git has none to report (untracked,
binary, renamed) — and the card renders them portalled to the body, so the
Workspace's own scroll box cannot clip the list.
GET /api/lanes/:id/git now returns `files`, the raw `git status --porcelain`
lines the dirty/untracked counts were already parsed from, capped at 20 so a
lane mid-`npm install` cannot ship thousands of paths through a polled
endpoint. The lane card hovers them as the tooltip on its
"N modified · N untracked" text, appending "… +N" when the cap bit.
No new subprocess and no new endpoint — statusCounts was counting those lines
and throwing them away.
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).
- Repo mode adopts a directory as-is via /lanes/ensure (no worktree, no
branch fields) - the right choice for a main repo you want stage
detection on. Worktree mode (default) keeps the existing provisioning
flow but now requires a manually-typed branch name instead of deriving
one from the title.
- POST /lanes/worktree accepts an optional `branch`, validated via
`git check-ref-format --branch`; omitting it preserves the CLI's
existing auto-derived-branch behavior.
- New GET /lanes/browse lists a directory's immediate subdirectories,
backing a small folder-browse modal on both path fields - browsers
cannot expose an absolute path from a native picker, so this is
server-backed instead, consistent with the tool's local-first model.
Internal SmartGift build of a Claude Code monitoring dashboard.
Lanes: a durable unit of parallel agent work, one per working directory,
tracked across session restarts. Managed lanes are git worktrees the
dashboard provisions and can reset or remove behind a three-check destroy
guard and a counted preflight; adopted lanes are directories you already
own and are never destroyable.
Pipelines: a lane moves through pipeline stages. A stage the agent declares
with evidence renders green; a stage inferred from the tool-event stream
renders dashed amber and never counts as done. Detection is forward-only
within a 30-minute window, and never writes the declared stage.
Workspace: one page at /run with a lane grid, the selected lane's pipeline,
and a full Claude console behind a disclosure.