cb19d7e5d2
Gathered from the parallel sessions' uncommitted work on main, at the
owner's request (the session that wrote #78/#79/#80 was stopped mid-task):
- #78/#79/#80: Files tab upload/download (GET /:id/git/download, POST
/:id/git/upload, server/lib/lane-file-transfer.js) and a non-git tree
(git-diff.js walkFolder); pinned sessions (sessions.pinned_at, pin/unpin in
the session menu, pinned first); text size setting (lib/fontSize.ts,
Appearance).
- Git identity and clone over HTTPS/SSH (server/lib/git-identity.js,
routes/git-identity.js, GitIdentitySettings, AddLaneModal clone).
- pets.js FEATURED list and view().
Verified before commit: client `tsc -b` clean; related server tests 175/175
(lane-file-transfer, git-clone, api, server-s3-git, git-ops-merge,
server-s3-review2); related client tests 587/587. Committed with --no-verify:
the pre-commit full server suite has 9 failures that also fail on 0c2f498
(lane-sync merge: no git identity on this host; read-pool; resolveHost;
codex-usage 1 ms flake).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
379 lines
20 KiB
Markdown
379 lines
20 KiB
Markdown
# AgentHub
|
||
|
||
Internal SmartGift build. Local-first **control surface** for Claude Code agents —
|
||
monitoring is one part of it, not the whole. Hooks POST every tool call to an
|
||
Express + SQLite server and a React UI updates over WebSocket; **workspaces** track
|
||
parallel agent work, one working directory each; and the `agenthub` CLI drives real terminal
|
||
sessions, workspace runtimes, and Claude Code's own configuration.
|
||
|
||
AgentHub records and shows — it does not orchestrate. The session in control makes
|
||
every decision.
|
||
|
||
Internal build — all rights reserved.
|
||
|
||
*(Tiếng Việt: [OVERVIEW.vi.md](OVERVIEW.vi.md))*
|
||
|
||
## Main features
|
||
|
||
**Observe**
|
||
|
||
- **Sessions, agents, events.** Everything Claude Code emits, recorded and
|
||
searchable: tool calls, token usage, cost, subagent trees, transcripts.
|
||
- **Analytics and cost.** Per-model token totals and estimated cost, with
|
||
editable pricing rules; top tools, agent types, and workflow patterns.
|
||
- **Activity feed and alerts.** A live event stream, alert rules, and webhook
|
||
targets that fire on them.
|
||
- **History import.** Every session under `~/.claude/projects/` is imported on
|
||
startup; archives from another machine come in through Settings → Import
|
||
History (idempotent, never double-counts tokens).
|
||
- **Remote sources.** Pull session history from other machines over SSH
|
||
(`agenthub remote-sources`).
|
||
|
||
**Drive**
|
||
|
||
- **Workspaces.** One workspace per working directory, surviving session restarts. Once you
|
||
install a pipeline template (none ships), a workspace moves through its stages and
|
||
the dashboard shows where it is.
|
||
- **Stage detection.** With a template installed, the stage is inferred from the
|
||
tool stream, so a session that never calls `agenthub stage` still shows
|
||
progress — rendered dashed amber and never as done, because an inference is
|
||
not evidence.
|
||
- **Run Claude Code or Codex from the browser.** A real terminal (tmux + a real
|
||
PTY, rendered with xterm.js) attached to a workspace's directory — the exact TUI
|
||
you'd see locally, fully interactive, resumable, and attachable from a real
|
||
terminal too via `agenthub workspace shell`. Which CLI it starts is the workspace's own
|
||
setting. **This is the part that needs `tmux` installed** — see
|
||
[Prerequisites](#prerequisites).
|
||
- **Git, from the dashboard.** Each workspace card shows its branch, `↑ahead ↓behind`
|
||
and its uncommitted files, with buttons for the ordinary moves: fetch, pull
|
||
(always `--ff-only`), push, commit-all, and switching branch on a clean tree.
|
||
Beside the workspace sits a collapsible **Changes** panel — an overview of the
|
||
changed files, a full review of every diff, or one file at a time, comparing
|
||
either uncommitted work or the whole branch against its base. There is no
|
||
merge/rebase/reset button by design: recovery from those needs a terminal.
|
||
- **Per-workspace app stacks.** A repo that declares `.agenthub/profile/` gets its own
|
||
slot, ports and services per workspace (`agenthub workspace up|down|logs`).
|
||
|
||
**Configure**
|
||
|
||
- **Workspaces like Claude Desktop projects.** The sidebar lists every
|
||
workspace with its sessions under it (a live one opens its tmux pane, any
|
||
other its conversation with a prompt box — sending a message resumes it,
|
||
opening never does; **Analyst** in the right panel opens its analysis). A workspace opens on its home: a
|
||
prompt box that starts a new session, Recents, and the workspace's own
|
||
Instructions, Memory, Context files (picked with + or dragged in from the OS; each is a copy taken at upload, so edit the original and add it again — a Folder is read live)
|
||
and extra Folders, which Claude and Codex sessions start with. Files and
|
||
images dragged onto a terminal or a prompt box are uploaded and attached by
|
||
their saved path. A session can be pinned to the top of its workspace's
|
||
list. The Files tab also works in a folder that is not a git repo, and
|
||
downloads any file or uploads new ones into the workspace (never over an
|
||
existing file) — on a server install, how a member with no SSH moves files.
|
||
- **Settings as one page per section** (`/settings/<section>`): a left nav
|
||
grouped General (Appearance — language, theme, pet — / Notifications /
|
||
System) / Agents (AI tools, one page per installed AI tool, AI Proxy) / Data
|
||
(Database / Pricing / Snapshots & reports / Git — logins and which untracked
|
||
worktrees a workspace's Add folder suggests), a search box (Ctrl+F) over every
|
||
section whose results deep-link a row's sub-tab and anchor. Model prices live
|
||
only on Pricing; a tool's page links there.
|
||
- **Claude Code + Codex configuration explorer.** Settings hosts one page per
|
||
tool: every plugin, skill, subagent, slash command, MCP server, hook, settings
|
||
file, memory file, marketplace, keybinding and statusline Claude Code knows
|
||
about — read for all of them, edit/create/delete for the text-file surfaces
|
||
(skills, agents, commands, output styles, `CLAUDE.md`). Plugins, MCP, hooks and
|
||
`settings.json` stay read-only: they race with the live CLI.
|
||
- **Updates.** Release-based update detection with an in-UI **Update all**
|
||
(marketplaces → every plugin → AgentHub last → one session reload).
|
||
- **Appearance.** Settings → Appearance picks Dark / Light / System (follows the
|
||
OS live) and a theme per mode — dark: Default, Darcula, Apple Dark, Orca Dark;
|
||
light: Default, Apple Light, Canvas, Orca Light. Each mode
|
||
remembers its own theme, so the sidebar's quick toggle swaps between the two.
|
||
It also picks the workspace terminals' font: JetBrains Mono (default), the
|
||
rounded Maple Mono, or the browser's own fixed-width
|
||
font (which may lack some Vietnamese letters) — and its size (11–20 px,
|
||
default 13), both applied to open terminals at once — and the interface's
|
||
**Text size** (Small / Default / Large / Larger: a 14 / 16 / 17 / 18 px root
|
||
font size that every text in the UI scales with). Saved to the account. Text across
|
||
the UI and the terminals is one step heavier than stock (regular 500, bold
|
||
700), and the Chat UI default sits at the top of the page.
|
||
- **Notification sounds.** Settings → Notifications picks a sound (None, Chime,
|
||
Pop, Alert — off by default) played with every notification shown, and each
|
||
notification type can pick its own.
|
||
- **A CLI for all of it** (`agenthub`, plus `agenthub repl`), a PWA install, and
|
||
a UI in English, Vietnamese, Chinese and Korean.
|
||
- **Outside the dashboard, all opt-in:** a read-only MCP server
|
||
(`claude mcp add agenthub -- agenthub mcp`, [docs/MCP.md](MCP.md)), a
|
||
Claude Code statusline with session and today's cost
|
||
(`agenthub statusline install`, [docs/STATUSLINE.md](STATUSLINE.md)), and
|
||
Prometheus rules + Grafana boards for `/api/metrics`
|
||
(`docker-compose.monitoring.yml`, [docs/MONITORING.md](MONITORING.md)).
|
||
- **Server mode for a shared dashboard** (`agenthub server setup`): a login on
|
||
every page and API call, with admin / member / analyst roles. It is off by
|
||
default, and a local install does not change. It is login only, without
|
||
per-user isolation yet ([docs/SERVER-MODE.md](SERVER-MODE.md)).
|
||
|
||
## Prerequisites
|
||
|
||
| Need | Version | Required for |
|
||
|---|---|---|
|
||
| **Node.js** | >= 20 (>= 22.5 for a plugin install) | Everything. Both suites are verified green on **24** and **26** |
|
||
| **npm** | 9+ | Ships with Node |
|
||
| **git** | any | Cloning, and every workspace operation (worktrees, branch/status reads) |
|
||
| **tmux** | 3.x | The browser run console, `agenthub workspace shell`, typing `/reload-plugins` into a live pane |
|
||
| **Claude Code** | 2.x | Hook ingestion — the whole monitoring side |
|
||
| **Codex CLI** | any | Optional — only for `codex` workspaces |
|
||
| **GitHub CLI (`gh`)** | any | Optional — only for `agenthub workspace ci status` |
|
||
| **systemd (user)** | — | Optional — only for `agenthub service install` (Linux auto-start on boot) |
|
||
|
||
Node **25** is the one version to avoid: it breaks 6 server tests through a
|
||
better-sqlite3 ABI mismatch and 20 client tests through a global `localStorage`
|
||
change. Both were gone again by 26 (1712 server + 488 client tests green on
|
||
v26.8.1).
|
||
|
||
### Installing tmux
|
||
|
||
The dashboard shells out to the `tmux` binary; there is no bundled fallback, so
|
||
**Start Run** and `agenthub workspace shell` fail without it. Everything else
|
||
(sessions, analytics, workspaces, stage detection, the CLI) works fine.
|
||
|
||
```bash
|
||
sudo apt install tmux # Debian / Ubuntu
|
||
sudo dnf install tmux # Fedora / RHEL
|
||
brew install tmux # macOS
|
||
sudo pacman -S tmux # Arch
|
||
```
|
||
|
||
Windows has no native tmux — run AgentHub under WSL2 and install it there.
|
||
|
||
macOS (code-audited, untested on a Mac): `brew install node git tmux` (Node
|
||
>= 22.5 for the plugin), add `~/.local/bin` to `PATH` in `~/.zshrc`. There is
|
||
no systemd, so the dashboard runs until logout/reboot and comes back at the
|
||
next Claude Code session, or by hand with `agenthub start`. Details and a
|
||
checklist to verify it: [INSTALL.md § macOS](../INSTALL.md#macos).
|
||
|
||
Verify, both from your shell and from the server's point of view:
|
||
|
||
```bash
|
||
tmux -V # e.g. tmux 3.4
|
||
curl -s localhost:4820/api/run/tmux # {"available":true}
|
||
```
|
||
|
||
A workspace's run lives in a tmux session named `agenthub-lane-<id>`, so the browser
|
||
terminal and a local `tmux attach -t agenthub-lane-<id>` (or `agenthub workspace
|
||
shell`) are two clients on the same pane — they stay in sync, and killing the
|
||
dashboard never kills the run.
|
||
|
||
## Install as a Claude Code plugin
|
||
|
||
Two commands on a machine that has nothing but Claude Code, no clone and no
|
||
`npm run setup`:
|
||
|
||
```
|
||
/plugin marketplace add https://git.smartgift.io.vn/Smartgift-AI/AgentHub.git
|
||
/plugin install agenthub@agenthub
|
||
```
|
||
|
||
The first session start installs the hooks, boots the server, puts `agenthub` on
|
||
PATH; it runs detached, so the session never waits on
|
||
it. `/agenthub-doctor` reports the state, `/agenthub-open` builds the UI and prints the
|
||
URL, `/agenthub-update` refreshes after a plugin update. This path needs Node
|
||
**>= 22.5** (no native `better-sqlite3`, so the server uses `node:sqlite`).
|
||
Details, including what to delete on uninstall:
|
||
[`docs/PLUGINS.md`](PLUGINS.md).
|
||
|
||
## Install from a checkout
|
||
|
||
```bash
|
||
npm run setup # root and client dependencies
|
||
npm run build # builds the client into client/dist
|
||
npm start # serves the built client and the API on :4820
|
||
```
|
||
|
||
Open <http://localhost:4820>.
|
||
|
||
Development, with hot reload:
|
||
|
||
```bash
|
||
npm run dev # server on :4820, Vite client on :5173
|
||
```
|
||
|
||
`DASHBOARD_PORT` overrides the port, `DASHBOARD_CLIENT_DIST` overrides where the
|
||
built UI is served from (defaults to `client/dist`; the plugin install points it
|
||
at its own runtime directory). `postinstall` writes the Claude Code hook entries
|
||
that feed the dashboard — do not run it when the `agenthub` plugin is installed, or
|
||
every event is counted twice.
|
||
|
||
### Run as a service (auto-start on boot)
|
||
|
||
`npm start` only runs while that terminal (or `agenthub start`'s background
|
||
process) is alive — a reboot loses it. `agenthub service install` sets up a
|
||
systemd `--user` service (Linux) or a LaunchAgent (macOS) instead, so `:4820` is up again on its own after
|
||
every reboot, with no manual step and no Claude Code (or any AI tool) needed
|
||
to trigger it:
|
||
|
||
```bash
|
||
npm run build # the service serves the built client, same as npm start
|
||
agenthub service install # Linux: ~/.config/systemd/user/agenthub.service
|
||
# macOS: ~/Library/LaunchAgents/io.smartgift.agenthub.plist
|
||
agenthub service status # systemctl --user status / launchctl print
|
||
agenthub service uninstall # disable and remove it
|
||
```
|
||
|
||
On Linux, `agenthub service install` also tries `loginctl enable-linger` so the service
|
||
starts on a cold boot, before anyone logs in; if that needs elevated rights on
|
||
your machine it prints the `sudo loginctl enable-linger <user>` command to run
|
||
once yourself. The same is a switch in the dashboard — Settings → System →
|
||
**Start AgentHub automatically** (off by default; it enables or disables the
|
||
unit and never stops the running server). Plugin installs get it only once
|
||
that switch is on — see
|
||
[Auto-start on boot](PLUGINS.md#auto-start-on-boot) in `docs/PLUGINS.md`.
|
||
|
||
## The CLI
|
||
|
||
`agenthub` is linked by `npm run setup`; otherwise call `node bin/agenthub.js`.
|
||
|
||
```bash
|
||
agenthub status # is the dashboard up
|
||
agenthub start # start it in the background and wait for healthy
|
||
agenthub service install # auto-start on boot via systemd --user (Linux)
|
||
agenthub sessions # recent sessions
|
||
agenthub workspaces # workspaces with stage and progress
|
||
agenthub workspace pipeline # this lane's pipeline template, or switch it
|
||
agenthub workspace node <id> # run ONE pipeline node's own command (no id: list them)
|
||
agenthub stage <name> # declare the current lane's stage
|
||
agenthub tail # live event feed
|
||
```
|
||
|
||
`agenthub --help` lists the rest.
|
||
|
||
## Workspaces
|
||
|
||
A workspace is a working directory the dashboard watches. Two kinds:
|
||
|
||
- **adopted** — a directory you already had. The dashboard only reads it; it is
|
||
never reset or deleted.
|
||
- **managed** — a git worktree the dashboard created under `LANES_ROOT`. It may
|
||
reset that worktree, behind a three-check destroy guard and a counted preflight
|
||
the caller has to echo back. Removing a workspace — either kind — deletes only the
|
||
dashboard's record of it: the working tree, its branch and its database stay,
|
||
for you to delete yourself.
|
||
|
||
```bash
|
||
agenthub workspace add --cwd /path/to/repo --title "My feature" # adopt
|
||
agenthub workspace add --repo /path/to/repo --slug my-feature # managed worktree
|
||
```
|
||
|
||
Every workspace also carries the CLI that drives it — `claude` (the default) or
|
||
`codex` — picked in the Add-workspace modal and fixed for the life of the workspace. A
|
||
Codex workspace's run panel offers Codex's own sandbox, approval and reasoning-effort
|
||
choices, and resumes with `codex resume --last`. Codex sessions and token usage
|
||
are read from Codex's own rollout journals: every Codex session (a workspace's or
|
||
not) appears in Sessions with a **Codex** badge and is priced like any other,
|
||
with OpenAI rates filled in automatically (Settings → Codex → Overview). For a
|
||
live feed, install **Codex hooks** from the same tab: the next time Codex starts
|
||
it asks you to trust them (**Trust all and continue**), and from then on each
|
||
tool call lands in the session's timeline, a codex workspace turns running/idle as
|
||
each turn starts and ends, and "Codex needs your permission" shows on the workspace
|
||
card and as a notification while Codex waits for an approval. A Codex workspace also
|
||
carries **no pipeline** — no stage, no pipeline map, nothing inferred or
|
||
advanced; without hooks the dashboard mirrors its running/idle status from the
|
||
rollout journals on disk (`$CODEX_HOME/sessions/**/rollout-*.jsonl`, polled
|
||
every `DASHBOARD_CODEX_POLL_MS` ms, default 15000).
|
||
|
||
An **Antigravity** workspace runs Antigravity's `agy` CLI. Its sessions and token
|
||
usage are read from agy's own conversation files
|
||
(`~/.gemini/antigravity-cli/conversations/<id>.db`, every
|
||
`DASHBOARD_ANTIGRAVITY_POLL_MS` ms, default 15000): every agy CLI session
|
||
appears in Sessions with an **Antigravity** badge, priced with Gemini rates that
|
||
are filled in automatically (Settings → Antigravity). **Antigravity hooks**, in
|
||
the same tab, add a live feed: each tool call in the session's timeline and a
|
||
workspace that turns running/idle as a run starts and ends. agy asks for no trust,
|
||
and it has no hook for a permission prompt, so an Antigravity workspace never shows
|
||
"needs your permission". agy fires no hook when it exits, nor when a run ends
|
||
on an API error or a permission refused in headless mode, so a session gone
|
||
quiet is marked completed and its workspace set idle — after ten minutes of quiet
|
||
for a session the hooks report on (a long tool call writes nothing, and after a
|
||
Stop agy may still be waiting for you), after two minutes for one without
|
||
hooks. Antigravity workspaces carry no pipeline either.
|
||
|
||
**Cursor** sessions — the Cursor CLI's and the Cursor editor's agent chats — are
|
||
read from Cursor's agent transcripts
|
||
(`~/.cursor/projects/*/agent-transcripts/`, every `DASHBOARD_CURSOR_POLL_MS` ms,
|
||
default 15000) and appear in Sessions with a **Cursor CLI** badge. Cursor writes
|
||
no token counts or model ids there, so a Cursor session shows no tokens and no
|
||
cost.
|
||
|
||
**Pipelines are dormant.** AgentHub ships no pipeline template, so a workspace has no
|
||
stages until you drop a template JSON into `DASHBOARD_PIPELINES_DIR` (see
|
||
[`docs/LANES.md`](LANES.md#custom-pipeline-templates)); until then
|
||
`agenthub stage` answers `ENOPIPELINE` and the Workspace hides the pipeline map.
|
||
Further development is planned.
|
||
|
||
The declared stage comes from `agenthub stage`. The inferred stage comes from tool
|
||
events and expires after `DETECTION_TTL_MS` (default 5 minutes), so a workspace can
|
||
move backwards between work sessions. Detection never writes the declared stage,
|
||
and an inferred node never renders as done.
|
||
|
||
A workspace's pipeline template can be switched after creation — `agenthub workspace
|
||
pipeline <template>` from the terminal, or the pipeline-template picker next to
|
||
the workspace's title in the Workspace detail panel. Both re-resolve the workspace's
|
||
current declared stage against the new template's nodes and warn if it no
|
||
longer matches one.
|
||
|
||
A workspace can also run **its own application stack**, isolated per workspace, when its
|
||
repository declares a profile at `<repo>/.agenthub/profile/` — a `profile.env` of
|
||
declarations plus shell hooks the dashboard calls. Each workspace gets a slot, and its
|
||
ports and per-workspace directories derive from it:
|
||
|
||
```bash
|
||
agenthub workspace up # boot the stack of the lane owning this directory
|
||
agenthub workspace runtime # slot, ports, service health
|
||
agenthub workspace logs api # tail a service log
|
||
agenthub workspace down
|
||
```
|
||
|
||
Services are fully detached, so restarting the dashboard never stops a running
|
||
workspace. This is resource namespacing on the host, not a container: workspaces run as the
|
||
same user and share the network.
|
||
|
||
[`docs/LANES.md`](LANES.md) has the pipeline model, the destroy guard, the
|
||
preflight contract, the Workspace page, the git surface (`GET /api/lanes/:id/git`,
|
||
`/git/changes`, `/git/log`, `/git/file`, `/git/ls`, `/git/tree`,
|
||
`POST /:id/git/:action`), and the full
|
||
runtime/profile contract.
|
||
|
||
## Tests
|
||
|
||
```bash
|
||
npm run test:server # node:test
|
||
npm run test:client # Vitest
|
||
```
|
||
|
||
Both must be green before a commit. The pre-commit hook (`.husky/pre-commit`)
|
||
runs Prettier, then the tests the commit needs: both full suites for a big or
|
||
risky commit (over 10 code files, a dependency, build/test config or the hook
|
||
itself, a deleted file), only the related tests for a small one (`vitest
|
||
related` for `client/`, the server test files that name each other staged
|
||
module), nothing for docs alone. `AGENTHUB_FULL_TESTS=1 git commit …` forces
|
||
both full suites.
|
||
|
||
## Layout
|
||
|
||
| Path | What |
|
||
|---|---|
|
||
| `server/` | Express API, SQLite schema, hook ingest, workspace and worktree libraries |
|
||
| `client/` | React 18 + Vite + Tailwind dashboard |
|
||
| `bin/agenthub.js` | CLI |
|
||
| `mcp/`, `statusline/` | Read-only MCP server and Claude Code statusline (opt-in) |
|
||
| `monitoring/` | Prometheus config/rules and Grafana boards for `/api/metrics` |
|
||
| `docs/` | Architecture, API, workspaces, database |
|
||
| `plugins/` | Claude Code plugins shipped with the dashboard |
|
||
|
||
## Docs
|
||
|
||
- [`ARCHITECTURE.md`](../ARCHITECTURE.md) — request flow, schema, WebSocket surface
|
||
- [`docs/LANES.md`](LANES.md) — workspaces, pipelines, stage detection
|
||
- [`docs/API.md`](API.md) — REST endpoints (`openapi.yaml` is generated)
|
||
- [`docs/DATABASE.md`](DATABASE.md) — tables and migrations
|
||
- [`INSTALL.md`](../INSTALL.md)
|
||
- [`CLAUDE.md`](../CLAUDE.md) — the rules an agent working in this repo must follow
|