9a4ed54cf7
- Plugin bootstrap: a failed npm install, a Node older than 22.5 or a failed UI build is recorded in runtime/last-failure.json; the next SessionStart prints it once as a systemMessage naming the log and /agenthub-doctor (a different failure is reported again; a successful bootstrap clears it). The foreground pass checks the Node version itself and spawns no worker on an old Node. - ReDoc: the standalone bundle (redoc 2.5.4, MIT, licence files kept) is vendored under server/vendor/redoc/ and the `redoc` dependency dropped — a runtime install (npm ci --omit=dev --ignore-scripts) goes 126 MB -> 45 MB. vendor/ is excluded from the header audit and prettier. - git missing from PATH: lane git actions answer 503 EGITMISSING with the install command instead of a bare ENOENT. - package.json engines: node >=22.5.0 (node:sqlite), matching MIN_NODE. - Docs: INSTALL.md is the one install/setup guide (SETUP.md merged in and now a pointer); no VS Code extension claim, one client-install hint, no windows-build-tools, better-sqlite3 notes scoped to checkouts, new "Plugin bootstrap never comes up" troubleshooting entry.
747 lines
35 KiB
Markdown
747 lines
35 KiB
Markdown
# Installation
|
||
|
||
The one install-and-setup guide for AgentHub — the local control surface for Claude Code agents: the plugin install, a checkout install, configuration, the database, importing history, and troubleshooting. (`SETUP.md` is only a pointer here now.)
|
||
|
||
## Fastest path — install it as a Claude Code plugin
|
||
|
||
If all you want is a working dashboard, skip every step below. From Claude Code:
|
||
|
||
```
|
||
/plugin marketplace add https://git.smartgift.io.vn/Smartgift-AI/AgentHub.git
|
||
/plugin install agenthub@agenthub
|
||
```
|
||
|
||
The next session start installs the hooks, boots the server, puts the `agenthub` CLI
|
||
on PATH — no clone, no `npm run setup`, no
|
||
`npm run install-hooks`, no manual `npm start`. Run `/agenthub-doctor` to check the
|
||
result and `/agenthub-open` to build the UI and get the URL.
|
||
|
||
Requires Node **>= 22.5** (a plugin install has no native `better-sqlite3`, so
|
||
the server stores data through `node:sqlite`), plus `git` and `tmux` from
|
||
[Step 0](#step-0--system-packages). Full behavior, including the uninstall
|
||
cleanup (`agenthub uninstall`), is in [`docs/PLUGINS.md`](docs/PLUGINS.md). If
|
||
the dashboard never comes up, see
|
||
[Plugin bootstrap never comes up](#plugin-bootstrap-never-comes-up).
|
||
|
||
Follow the steps below instead when you want a checkout — to develop the
|
||
dashboard or run it from source.
|
||
|
||
## Requirements
|
||
|
||
| Requirement | Version | Notes |
|
||
|---|---|---|
|
||
| Node.js | **22.5+** | Required for server and client (`node:sqlite`). Node 20 is end-of-life and unsupported |
|
||
| npm | 9+ | Comes with Node.js |
|
||
| Claude Code | 2.x+ | Required for hook integration |
|
||
| Git | Any | Cloning the repository, and every lane operation (worktrees, branch/status reads) |
|
||
| **tmux** | 3.x (3.7+ for pane scrollbars) | **Required for the run console** — the browser terminal, `agenthub workspace shell`, and typing `/reload-plugins` into a live pane. See [Step 0](#step-0--system-packages) |
|
||
| Codex CLI | Any | Optional — only for lanes whose tool is `codex` |
|
||
| GitHub CLI (`gh`) | Any | Optional — only for `agenthub workspace ci status` |
|
||
| Python | 3.6+ | Optional — only for `scripts/expand-ts-module-docs.py` (doc generation) |
|
||
|
||
---
|
||
|
||
## Step 0 — System packages
|
||
|
||
Node and npm are not enough: `git` backs every lane operation, and **`tmux`
|
||
backs the run console**. The dashboard shells out to the `tmux` binary and has
|
||
no bundled fallback, so without it **Start Run** and `agenthub workspace shell`
|
||
fail while everything else keeps working.
|
||
|
||
```bash
|
||
sudo apt install git tmux # Debian / Ubuntu
|
||
sudo dnf install git tmux # Fedora / RHEL
|
||
brew install git tmux # macOS
|
||
sudo pacman -S git tmux # Arch
|
||
```
|
||
|
||
Windows has no native tmux. Run AgentHub under **WSL2** and install it inside
|
||
the WSL distro — a tmux from Git Bash or MSYS is not what the server executes.
|
||
On a native Windows install, workspace terminals are unavailable, but Settings →
|
||
Agents still detects each AI CLI through `PATHEXT` (`claude.exe`, `codex.cmd`)
|
||
and runs a `.cmd` updater or npm through `cmd.exe`.
|
||
|
||
A terminal pane gets a draggable scrollbar only on **tmux 3.7+** — older
|
||
versions work, just without it, and `/agenthub-doctor` warns. Homebrew ships the
|
||
latest (`brew upgrade tmux`); Ubuntu 24.04's apt has 3.4, so build the release:
|
||
|
||
```bash
|
||
sudo apt install build-essential pkg-config libevent-dev libncurses-dev bison
|
||
curl -fsSL https://github.com/tmux/tmux/releases/download/3.7c/tmux-3.7c.tar.gz | tar xz
|
||
cd tmux-3.7c && ./configure && make && sudo make install # into /usr/local/bin
|
||
```
|
||
|
||
A tmux server already running keeps its old version until every tmux session
|
||
has closed (or the machine reboots) — only then do the scrollbars appear.
|
||
|
||
Check it:
|
||
|
||
```bash
|
||
tmux -V # e.g. tmux 3.4
|
||
git --version
|
||
node -v # v22.5+ / v24.x
|
||
```
|
||
|
||
Once the server is up you can ask it directly — this is the same probe the
|
||
dashboard uses:
|
||
|
||
```bash
|
||
curl -s localhost:4820/api/run/tmux # {"available":true}
|
||
```
|
||
|
||
**No configuration is needed.** AgentHub creates its own session per lane,
|
||
named `agenthub-lane-<id>`, and never reads your `~/.tmux.conf` for anything it
|
||
depends on (mouse mode, the hidden status bar and the scrollbar are set on the
|
||
session explicitly). You can attach to the
|
||
exact same pane from a normal terminal:
|
||
|
||
```bash
|
||
agenthub workspace shell # from inside the lane's directory
|
||
tmux attach -t agenthub-lane-7 # or by name
|
||
tmux ls # every dashboard session is prefixed agenthub-lane-
|
||
```
|
||
|
||
Detaching is `Ctrl-b d` — it leaves the run alive, which is the whole point:
|
||
the browser terminal and your local terminal are two clients on one pane, and
|
||
restarting the dashboard never stops a run.
|
||
|
||
### macOS
|
||
|
||
Status: **code-audited, untested on a Mac.** The Mac-specific paths below are
|
||
covered by Linux-side unit tests only; the checklist at the end of this section
|
||
is what a Mac user runs to confirm them.
|
||
|
||
```bash
|
||
brew install node git tmux # Node >= 22.5 for the plugin install (nvm works too)
|
||
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.zshrc # where the plugin puts `agenthub`
|
||
```
|
||
|
||
- **Node:** **>= 22.5** (the plugin stores data through `node:sqlite`; a
|
||
checkout needs the same). The `agenthub` launcher
|
||
pins the node that installed it and falls back to `node` on `PATH` when a
|
||
`brew upgrade` or `nvm` switch deletes that binary.
|
||
- **Starts at login once you turn it on (LaunchAgent).** Off by default; with
|
||
Settings → About → **Start AgentHub automatically** on, the plugin bootstrap
|
||
installs `~/Library/LaunchAgents/io.smartgift.agenthub.plist` (a checkout:
|
||
the same switch, or `agenthub service install`), so the dashboard comes back after every login with no
|
||
Claude Code session, and launchd restarts it if it dies. Lane panes survive
|
||
a restart of the server. Its output goes to `~/Library/Logs/agenthub.log`.
|
||
A LaunchAgent needs a login at the Mac itself; over SSH only, or when
|
||
`launchctl` fails, the server is a detached process started by the next
|
||
Claude Code session (within about 10 s) or by `agenthub start`, as before. A
|
||
discovery entry left over from before the reboot is ignored, so a recycled
|
||
pid can never pass for a live dashboard.
|
||
- **Started from a Claude Code session.** The server drops the variables that
|
||
describe that session (`CLAUDECODE`, `CLAUDE_CODE_SESSION_ID`,
|
||
`CLAUDE_PLUGIN_ROOT`, …) before anything else runs, so lane panes do not
|
||
inherit them. Your own configuration (`CLAUDE_CONFIG_DIR`,
|
||
`CLAUDE_CODE_USE_BEDROCK`, `ANTHROPIC_*`) passes through. A tmux server that
|
||
an older build started keeps its dirty global env until it exits
|
||
(`tmux kill-server` once, with no run you care about open).
|
||
- **Paths.** A lane's directory is stored by its real path, so a typed `/tmp/x`
|
||
becomes `/private/tmp/x` and a differently-cased path on a case-insensitive
|
||
volume becomes the one on disk — that is what sessions report, so they bind.
|
||
Lanes created before this change keep the string they were created with.
|
||
- **Account switching** uses the login Keychain item `Claude Code-credentials`;
|
||
macOS may ask once for permission the first time.
|
||
- **Option key** in the browser terminal types special characters (it is not
|
||
sent as Meta).
|
||
|
||
**Mac checklist** — please run it and report the result on issue #22:
|
||
|
||
1. Fresh plugin install from a Terminal `claude` session: the dashboard answers on `http://localhost:4820`, `~/Library/LaunchAgents/io.smartgift.agenthub.plist` exists, and `/agenthub-doctor`'s launchd row says the LaunchAgent is current and running.
|
||
2. `tmux show-environment -g | grep -i claude` prints nothing.
|
||
3. Add a Repo lane by typing its path in a different case: sessions bind to it, and `agenthub stage plan` inside it moves the map.
|
||
4. **Start Run** in the browser: the terminal attaches, typing works, an image paste into `claude` works, and `exit` returns to the workspace home.
|
||
5. Changes panel: diff, stage and commit on a lane.
|
||
6. Switch profile and check `claude /status` shows the new account. Repeat with a non-default `CLAUDE_HOME`, which also checks the Keychain service name.
|
||
7. `brew upgrade node && brew cleanup`, then `agenthub status` still works.
|
||
8. Reboot and log in WITHOUT opening `claude`: the dashboard answers within about 10 s (`agenthub service status` shows `state = running`).
|
||
9. `kill <server pid>` (from `agenthub status`): it is back within a few seconds.
|
||
10. With a lane run open, `/agenthub-update` completes, the server restarts on the new version, the plist names the new cache directory, and the run's terminal is still alive.
|
||
11. `agenthub service uninstall`: the plist is gone and nothing restarts the server after a `kill`.
|
||
|
||
---
|
||
|
||
## Step 1 — Clone the repository
|
||
|
||
```bash
|
||
git clone https://git.smartgift.io.vn/Smartgift-AI/AgentHub.git
|
||
cd AgentHub
|
||
```
|
||
|
||
---
|
||
|
||
## Step 2 — Install dependencies
|
||
|
||
```bash
|
||
npm run setup # or: make setup
|
||
```
|
||
|
||
This runs `npm install` (whose `postinstall` hook installs the client's
|
||
dependencies in `client/`), installs `client/` once more explicitly, and links
|
||
the `agenthub` CLI (`npm link`; if that has no permission it prints how to link
|
||
it by hand). A plain `npm install` is enough to build and run the dashboard —
|
||
only the CLI link is missing.
|
||
|
||
With `--ignore-scripts` the `postinstall` hook is skipped; install the client
|
||
yourself with `npm --prefix client install`.
|
||
|
||
`better-sqlite3` gyp/compile warnings here are harmless — see
|
||
[Troubleshooting](#better-sqlite3-errors-during-npm-install--npm-run-setup).
|
||
|
||
---
|
||
|
||
## Step 3 — Start the dashboard
|
||
|
||
```bash
|
||
npm run dev
|
||
```
|
||
|
||
This starts two processes concurrently:
|
||
|
||
| Process | URL | Description |
|
||
|---|---|---|
|
||
| Express server | http://localhost:4820 | API, WebSocket, SQLite |
|
||
| Vite dev server | http://localhost:5173 | React frontend with HMR |
|
||
|
||
Open **http://localhost:5173** in your browser.
|
||
|
||
> [!TIP]
|
||
> When you run the dashboard directly on the host with `npm run dev` or `npm start`, the server automatically writes the Claude Code hook configuration to `~/.claude/settings.json` — unless the `agenthub` plugin is enabled there, whose own hooks already run; then it removes any copies a checkout wrote instead, since two sets post every event twice. If you run the dashboard inside a container, install hooks from the host with `npm run install-hooks` instead — a hook path written from inside a container points at the container filesystem.
|
||
|
||
---
|
||
|
||
## Step 4 — Start a Claude Code session
|
||
|
||
Start a new Claude Code session from any directory **after** the dashboard server is running. The hooks will fire automatically and your sessions, agents, and events will appear in real-time.
|
||
|
||
```bash
|
||
# In a separate terminal, from any project directory:
|
||
claude
|
||
```
|
||
|
||
---
|
||
|
||
## Verification
|
||
|
||
After starting a Claude Code session, you should see:
|
||
|
||
- **Sessions page** — your session listed with status `Waiting` (a fresh CLI sitting at the prompt) or `Active` (mid-turn)
|
||
- **Agent Board** — a `Main Agent` card in the `Waiting` column until you type your first message; it flips to `Working` on `UserPromptSubmit` / `PreToolUse` and back to `Waiting` after each `Stop`
|
||
- **Activity Feed** — events streaming in as Claude Code uses tools
|
||
- **Dashboard** — stats updating in real-time
|
||
- **Settings page** — data export and cleanup tools on the Dashboard tab; Claude Code's model pricing rules, hook configuration status and CLAUDE_HOME on **Settings → Claude Code → Overview**
|
||
|
||
If nothing appears after 30 seconds, see [Troubleshooting](#no-sessions-appearing-after-starting-claude-code).
|
||
|
||
### PWA install (optional)
|
||
|
||
The dashboard is a Progressive Web App. After opening it in a supported browser (Chrome, Edge, Firefox), you can install it to your dock / home screen:
|
||
|
||
1. Look for the **install icon** (⊕) in the browser address bar, or open the browser menu → "Install app"
|
||
2. Once installed, the dashboard launches in its own window with no browser chrome
|
||
3. Offline support: previously visited pages and assets are served from the Service Worker cache when the network is unavailable
|
||
|
||
---
|
||
|
||
## Step 5 — (Optional) Importing existing Claude Code history
|
||
|
||
The dashboard automatically imports sessions from `~/.claude/projects/` on
|
||
**every startup**, so if Claude Code has been used on this machine, you'll
|
||
see history immediately after the first launch. If you need to bring in
|
||
history from another machine, from a backup, or just force a rescan, use
|
||
**Settings → Import History** in the UI — it's a guided, drag-and-drop
|
||
experience with live progress.
|
||
|
||
### Pick the right mode
|
||
|
||
```mermaid
|
||
flowchart TD
|
||
Q["Where is the history?"] --> Q1{Is it on this machine<br/>under ~/.claude/projects?}
|
||
Q1 -->|yes, and I just want<br/>to re-scan| M1["Mode: Rescan default folder<br/>one click"]
|
||
Q1 -->|yes, but in another folder<br/>on this machine| M2["Mode: Scan a folder<br/>paste the absolute path"]
|
||
Q1 -->|no — it's on another machine<br/>or in an archive file| M3["Mode: Upload files<br/>drag-drop JSONL or archive"]
|
||
|
||
M3 --> PREP["Archive source:<br/>tar -czf claude-history.tar.gz<br/>-C ~/.claude projects"]
|
||
|
||
style M1 fill:#10b981,stroke:#34d399,color:#fff
|
||
style M2 fill:#f59e0b,stroke:#fbbf24,color:#000
|
||
style M3 fill:#a855f7,stroke:#c084fc,color:#fff
|
||
```
|
||
|
||
### Step-by-step: moving history from one machine to another
|
||
|
||
**On the source machine**, bundle the projects folder:
|
||
|
||
```bash
|
||
# macOS / Linux
|
||
tar -czf claude-history.tar.gz -C ~/.claude projects
|
||
|
||
# Windows (PowerShell, via built-in tar)
|
||
tar -czf claude-history.tar.gz -C "$env:USERPROFILE\.claude" projects
|
||
```
|
||
|
||
Transfer the resulting `claude-history.tar.gz` to the destination machine
|
||
however you like — AirDrop, `scp`, USB, cloud storage.
|
||
|
||
**On the destination machine**, in the dashboard:
|
||
|
||
1. Open **Settings → Import History**.
|
||
2. Pick **Upload files** (the third tab).
|
||
3. Drag the archive onto the drop zone.
|
||
4. Click **Upload & Import** and watch the progress.
|
||
5. When the green result card appears, open **Analytics → Cost** to confirm
|
||
per-model token totals and estimated cost.
|
||
|
||
### Supported inputs
|
||
|
||
Any of the following can be dropped onto the upload zone or found inside a
|
||
folder given to **Scan a folder**:
|
||
|
||
- `.jsonl` — session transcripts
|
||
- `.meta.json` — subagent metadata sidecars
|
||
- `.zip` — extracted with path-traversal protection
|
||
- `.tar`, `.tar.gz`, `.tgz` — extracted via the `tar` package
|
||
- `.gz` — single gzipped JSONL (streaming-decompressed)
|
||
|
||
### Accuracy guarantees
|
||
|
||
- **Idempotent** — re-importing never double-counts. Sessions are
|
||
deduplicated by UUID.
|
||
- **Cost-preserving** — the `token_usage` table uses `baseline_*` columns
|
||
to preserve pre-compaction token totals, so re-ingesting a compacted
|
||
transcript never erases historical cost.
|
||
- **Same parser as live** — `parseSessionFile` + `importSession` is the
|
||
single source of truth for both hook-driven ingestion and manual
|
||
import, so imported numbers match captured numbers exactly.
|
||
|
||
### Safety
|
||
|
||
Archive extraction is hardened against path traversal and archive bombs.
|
||
The defaults are generous for real-world transcripts but tight enough to
|
||
stop obvious attacks. If you regularly import very large archives, raise them
|
||
before `npm run dev` or `npm start` (also listed under
|
||
[Environment variables](#environment-variables)):
|
||
|
||
| Variable | Default | Purpose |
|
||
| ----------------------------------- | ------- | ---------------------------------------------------------- |
|
||
| `AGENTHUB_IMPORT_MAX_BYTES` | 1 GB | Maximum size per uploaded file |
|
||
| `AGENTHUB_IMPORT_MAX_FILES` | 2000 | Maximum files per upload request |
|
||
| `AGENTHUB_IMPORT_MAX_EXTRACT_BYTES` | 4 GB | Ceiling on uncompressed bytes per archive (zip-bomb guard) |
|
||
|
||
```bash
|
||
AGENTHUB_IMPORT_MAX_EXTRACT_BYTES=17179869184 npm start # allow 16 GB extraction
|
||
```
|
||
|
||
### CLI alternative
|
||
|
||
For scripts and automation, the same logic runs from the terminal:
|
||
|
||
```bash
|
||
# Import (or re-import) everything under ~/.claude/projects
|
||
npm run import-history
|
||
|
||
# Dry run — show what would be imported without writing
|
||
node scripts/import-history.js --dry-run
|
||
|
||
# Scope to a single project dir
|
||
node scripts/import-history.js --project my-project
|
||
```
|
||
|
||
---
|
||
|
||
## Production mode
|
||
|
||
To run as a single process serving the built client:
|
||
|
||
```bash
|
||
npm run build # Build the React client
|
||
npm start # Start Express serving client/dist on port 4820
|
||
```
|
||
|
||
Open **http://localhost:4820** in your browser.
|
||
|
||
`npm start` (and `agenthub start`, its backgrounded form) only run while that
|
||
process is alive — a reboot loses it. Run the server as a systemd `--user`
|
||
service (Linux) or a LaunchAgent (macOS) instead so it comes back on its own
|
||
after every reboot, with no manual step and no Claude Code (or any other tool)
|
||
needed to trigger it:
|
||
|
||
```bash
|
||
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
|
||
```
|
||
|
||
See [`docs/OVERVIEW.md` → Run as a service](./docs/OVERVIEW.md#run-as-a-service-auto-start-on-boot)
|
||
and, for plugin installs, [`docs/PLUGINS.md` → Auto-start on boot](docs/PLUGINS.md#auto-start-on-boot).
|
||
|
||
---
|
||
|
||
## How it works
|
||
|
||
AgentHub integrates with Claude Code through its native hook system. When Claude Code performs any action (session start, tool use, turn completion, subagent finish, session exit), it fires a hook that calls a small Node.js script bundled with this project. That script forwards the event over HTTP to the dashboard server, which stores it in SQLite and broadcasts it to the browser over WebSocket.
|
||
|
||
```
|
||
Claude Code → hook fires → hook-handler.js → POST /api/hooks/event
|
||
↓
|
||
Browser ← WebSocket broadcast ← Express server ← SQLite
|
||
```
|
||
|
||
No extra Claude Code configuration is required in the normal host-run path — when you start the dashboard with `npm run dev` or `npm start`, the server configures the hooks automatically on startup (or, with the `agenthub` plugin enabled in `~/.claude/settings.json`, removes the checkout's copies — the plugin's own hooks already run, and both would post every event twice). Container deployments are the exception: after the container is up, run `npm run install-hooks` on the host so Claude Code points at `http://localhost:4820`.
|
||
|
||
---
|
||
|
||
## Configuration
|
||
|
||
### Hook auto-installation
|
||
|
||
When the dashboard is running directly on the host, the server writes the following to `~/.claude/settings.json` every time it starts:
|
||
|
||
```json
|
||
{
|
||
"hooks": {
|
||
"SessionStart": [{ "hooks": [{ "type": "command", "command": "node \"/path/to/scripts/hook-handler.js\" SessionStart" }] }],
|
||
"PreToolUse": [{ "matcher": "*", "hooks": [{ "type": "command", "command": "node \"/path/to/scripts/hook-handler.js\" PreToolUse" }] }],
|
||
"PostToolUse": [{ "matcher": "*", "hooks": [{ "type": "command", "command": "node \"/path/to/scripts/hook-handler.js\" PostToolUse" }] }],
|
||
"Stop": [{ "matcher": "*", "hooks": [{ "type": "command", "command": "node \"/path/to/scripts/hook-handler.js\" Stop" }] }],
|
||
"SubagentStop": [{ "matcher": "*", "hooks": [{ "type": "command", "command": "node \"/path/to/scripts/hook-handler.js\" SubagentStop" }] }],
|
||
"Notification": [{ "matcher": "*", "hooks": [{ "type": "command", "command": "node \"/path/to/scripts/hook-handler.js\" Notification" }] }],
|
||
"SessionEnd": [{ "hooks": [{ "type": "command", "command": "node \"/path/to/scripts/hook-handler.js\" SessionEnd" }] }]
|
||
}
|
||
}
|
||
```
|
||
|
||
> [!NOTE]
|
||
> Note: `SessionStart` and `SessionEnd` hooks do not support the `matcher` field — they fire unconditionally on every session start and exit.
|
||
|
||
Existing hooks in that file are preserved. The dashboard only adds or updates entries that contain `hook-handler.js`.
|
||
|
||
To re-run hook installation manually:
|
||
|
||
```bash
|
||
npm run install-hooks
|
||
```
|
||
|
||
> [!TIP]
|
||
> Container note: if you run the dashboard inside a container (a Dev Container, Codespaces, or your own image), do not install hooks from in there — the hook path a container writes points at the container filesystem, not the host. Run `npm run install-hooks` on the host instead. As a safeguard (issue #193) the installer **detects container execution and refuses to run** (exiting non-zero) so it can never poison a bind-mounted host `~/.claude`. If Claude Code itself genuinely runs inside that container, override with `AGENTHUB_ALLOW_CONTAINER_HOOKS=1 npm run install-hooks`.
|
||
|
||
> [!NOTE]
|
||
> Prefer a ready-made dev environment? This repo ships an **optional** Dev Container (`.devcontainer/`) for VS Code / GitHub Codespaces — Node 22, native build tools for `better-sqlite3`, Python, and ports `4820`/`5173` preconfigured. It's purely opt-in and changes nothing for host-based development. See [`.devcontainer/README.md`](.devcontainer/README.md). (Hooks remain host-side there too.)
|
||
|
||
### Environment variables
|
||
|
||
| Variable | Default | Description |
|
||
|---|---|---|
|
||
| `DASHBOARD_PORT` | `4820` | Port the Express server listens on |
|
||
| `CLAUDE_DASHBOARD_PORT` | `4820` | Port the hook handler uses when posting events to the dashboard |
|
||
| `DASHBOARD_DB_PATH` | `data/dashboard.db` | Path to the SQLite database file |
|
||
| `NODE_ENV` | `development` | Set to `production` to serve built client |
|
||
| `DASHBOARD_CLIENT_DIST` | `client/dist` | Directory the built UI is served from in production. Set by the `agenthub` plugin bootstrap to its own runtime dir, because the plugin cache is replaced on every update |
|
||
| `AGENTHUB_IMPORT_MAX_BYTES` | `1073741824` (1 GB) | Maximum size per uploaded file on `/api/import/upload` |
|
||
| `AGENTHUB_IMPORT_MAX_FILES` | `2000` | Maximum number of files per upload request |
|
||
| `AGENTHUB_IMPORT_MAX_EXTRACT_BYTES` | `4294967296` (4 GB) | Maximum uncompressed bytes any single archive is allowed to expand to (zip-bomb defense) |
|
||
| `DASHBOARD_DATA_DIR` | `~/.claude/agenthub` | AgentHub's data dir; a plugin install keeps its runtime (deps, UI build, logs) in `runtime/` under it |
|
||
|
||
Example with a custom port:
|
||
|
||
```bash
|
||
DASHBOARD_PORT=9000 npm run dev
|
||
```
|
||
|
||
> [!NOTE]
|
||
> You usually do **not** need to set `DASHBOARD_PORT` manually. `npm run dev` is wrapped by `scripts/dev.js`, which probes both `127.0.0.1` and `::1` (so an SSH `LocalForward` bound to one loopback can't slip past) and picks the first free port in `4820–4859` automatically. The chosen port is propagated to the Vite dev proxy via `DASHBOARD_PORT`, and the Express server writes it to `~/.claude/.agenthub.json` so the Claude Code hook handler discovers it without any env var.
|
||
>
|
||
> Multiple dashboards can run side by side — for example two `npm run dev` checkouts, or `npm run dev` alongside `npm start`. Each one appends its `{port, pid, startedAt}` entry to the discovery file, and `scripts/hook-handler.js` fan-outs every hook event to every live entry, so both UIs keep their real-time stream.
|
||
>
|
||
> Setting `CLAUDE_DASHBOARD_PORT=N` overrides discovery entirely and forces the hook handler to a single port — useful for tests and container setups where the in-process discovery file isn't reachable from the host.
|
||
>
|
||
> If you bypass the picker (e.g. `npm run dev:raw`, container builds, or anything else that calls `node server/index.js` directly), make sure your client is built / proxied against the port the server actually bound.
|
||
|
||
### Agent extension setup (Claude Code + Codex)
|
||
|
||
This repository ships extension files for both agent ecosystems:
|
||
|
||
- Claude Code:
|
||
- `CLAUDE.md`
|
||
- `.claude/rules/*`
|
||
- `skills/*` (the `agenthub` plugin's own skills)
|
||
- Codex:
|
||
- `AGENTS.md`
|
||
- `.codex/config.toml`
|
||
- `.codex/rules/default.rules`
|
||
- `.codex/agents/*`
|
||
- `.codex/skills/*`
|
||
|
||
See [`.codex/README.md`](./.codex/README.md) for Codex extension details.
|
||
|
||
### PWA configuration (optional)
|
||
|
||
The dashboard ships as a Progressive Web App. No configuration is required — its manifest and service worker are included out of the box.
|
||
|
||
**Customising the manifest:** Edit `client/public/manifest.json`. Common fields to change:
|
||
|
||
- `name` / `short_name` — displayed on the home screen / dock
|
||
- `theme_color` — address bar / title bar tint (default: `#6366f1`)
|
||
- `background_color` — splash screen background
|
||
- `start_url` — entry point when launched from home screen
|
||
|
||
**Updating the service worker cache:** The SW has a `CACHE_NAME` constant (e.g. `dashboard-v2`). After deploying new assets, bump the version string to force browsers to re-fetch — though for the dashboard this is rarely needed: hashed `/assets/*` URLs are immutable per build, everything else is fetched network-first with cache fallback, and a `controllerchange` listener in the client reloads the page exactly once when a new SW takes over, so a rebuild propagates without a hard refresh.
|
||
|
||
**Browser support:** PWA install prompts appear in Chrome 107+, Edge 107+, and Firefox 110+ (desktop and Android). Safari supports `apple-mobile-web-app-capable` for iOS home-screen mode but does not show an install banner.
|
||
|
||
**Verifying PWA status:** Open DevTools → Application → Manifest to confirm the manifest loads. Check the Service Workers section to verify the SW is registered and active. The Lighthouse PWA audit should pass all core checks.
|
||
|
||
---
|
||
|
||
## Database
|
||
|
||
The SQLite database is created automatically on first run (`data/dashboard.db` in a checkout, unless `DASHBOARD_DB_PATH` says otherwise). The directory is created if it does not exist. The database uses WAL mode for concurrent reads and foreign keys for referential integrity.
|
||
|
||
### Clear all data
|
||
|
||
To remove all sessions, agents, events, and token usage (useful after running seed data or for a clean start):
|
||
|
||
```bash
|
||
npm run clear-data
|
||
```
|
||
|
||
### Data management via Settings page
|
||
|
||
The Settings page (`/settings`) provides a UI for the following. **Model Pricing**
|
||
and **Hook Configuration** are not on its Dashboard tab: they sit under the
|
||
Claude Code tab's **Overview** (Settings → Claude Code → Overview), beside
|
||
CLAUDE_HOME; Codex (its Overview) and Antigravity have their own pricing and
|
||
hooks in their own tabs.
|
||
|
||
- **Model Pricing** — view and edit per-model cost rates, reset to defaults, add custom models
|
||
- **Hook Configuration** — check which hooks are installed and reinstall them
|
||
- **Data Export** — download all sessions, agents, events, and pricing as a JSON file
|
||
- **Session Cleanup** — abandon stale active sessions after N hours, purge old completed sessions after N days
|
||
- **Clear All Data** — remove all sessions, agents, events, and token usage
|
||
- **Data Management** and **About** sections render with loading placeholders while server info is being fetched, so the page is always fully navigable
|
||
|
||
### Seed demo data
|
||
|
||
To populate the dashboard with sample sessions, agents, and events for UI exploration:
|
||
|
||
```bash
|
||
npm run seed
|
||
```
|
||
|
||
---
|
||
|
||
## Scripts reference
|
||
|
||
| Script | Command | Description |
|
||
|---|---|---|
|
||
| `setup` | `npm run setup` | Install all dependencies (server + client) |
|
||
| `dev` | `npm run dev` | Start server + client in development mode |
|
||
| `start` | `npm start` | Start server in production mode |
|
||
| `build` | `npm run build` | Build the React client to `client/dist/` |
|
||
| `install-hooks` | `npm run install-hooks` | Write Claude Code hooks to `~/.claude/settings.json` |
|
||
| `clear-data` | `npm run clear-data` | Delete all data from the database |
|
||
| `seed` | `npm run seed` | Insert demo sessions/agents/events |
|
||
| `import-history` | `npm run import-history` | Import legacy sessions from `~/.claude/` (also runs on startup) |
|
||
| `claude` | Claude CLI | Uses `CLAUDE.md`, `.claude/rules`, and `.claude/skills` automatically |
|
||
| `test` | `npm test` | Run all server and client tests |
|
||
| `test:server` | `npm run test:server` | Run server integration tests only |
|
||
| `test:client` | `npm run test:client` | Run client unit tests only |
|
||
| `format` | `npm run format` | Format all files with Prettier |
|
||
| `format:check` | `npm run format:check` | Check formatting without writing |
|
||
|
||
---
|
||
|
||
## Makefile targets
|
||
|
||
All npm scripts are mirrored as `make` targets for convenience. Run `make help` to list them:
|
||
|
||
```bash
|
||
make help
|
||
```
|
||
|
||
Commonly used targets:
|
||
|
||
| Make target | Equivalent npm command | Description |
|
||
|---|---|---|
|
||
| `make setup` | `npm run setup` | Install all dependencies (root + client) |
|
||
| `make dev` | `npm run dev` | Start server + client in watch mode |
|
||
| `make build` | `npm run build` | Build the React client for production |
|
||
| `make start` | `npm start` | Start the production server |
|
||
| `make prod` | `npm run build && npm start` | Build then start in one step |
|
||
| `make test` | `npm test` | Run all tests (server + client) |
|
||
| `make test-server` | `npm run test:server` | Run server tests only |
|
||
| `make test-client` | `npm run test:client` | Run client tests only |
|
||
| `make format` | `npm run format` | Format all files with Prettier |
|
||
| `make format-check` | `npm run format:check` | Check formatting without writing |
|
||
| `make seed` | `npm run seed` | Load demo data |
|
||
| `make clear-data` | `npm run clear-data` | Delete all data rows |
|
||
|
||
---
|
||
|
||
## Troubleshooting
|
||
|
||
### Plugin bootstrap never comes up
|
||
|
||
The plugin's `SessionStart` hook only checks a fast path and hands the real
|
||
work (npm install, UI build, server start) to a detached background worker, so
|
||
a failure there does not show in the session. When the worker's last run
|
||
failed, the next session start prints a one-time **AgentHub:** message naming
|
||
the log to read. Everything is logged under `~/.claude/agenthub/runtime/`:
|
||
|
||
| File | What it holds |
|
||
|---|---|
|
||
| `bootstrap.log` | Each bootstrap step and why it stopped (Node too old, install failed, superseded build, …) |
|
||
| `npm-install.log` | npm's output for the runtime dependency install |
|
||
| `client-build.log` | The dashboard UI build (the API still works when only this fails) |
|
||
| `server.log` | The server's own output, when it was started detached |
|
||
| `last-failure.json` | The last failure the session-start message reports (removed once a bootstrap succeeds) |
|
||
|
||
Then run `/agenthub-doctor` from Claude Code — it checks Node, the runtime
|
||
dependencies, the server and the service unit, and names the fix for each
|
||
failing row. `/agenthub-update` re-runs the bootstrap in the foreground with its
|
||
output visible. The most common causes: Node older than 22.5 (`node -v` in the
|
||
shell Claude Code was started from), no network or a proxy blocking the npm
|
||
registry (`npm-install.log`), and a port held by another program (`server.log`,
|
||
see [Port 4820 already in use](#port-4820-already-in-use)).
|
||
|
||
### `better-sqlite3` errors during `npm install` / `npm run setup`
|
||
|
||
Only a **checkout** install sees these — the plugin installs with
|
||
`--ignore-scripts` and never builds `better-sqlite3`. They are **harmless**:
|
||
`better-sqlite3` is an optional dependency, and when it cannot be built npm
|
||
skips it and the server uses Node's built-in `node:sqlite` (Node 22.5+). You do
|
||
**not** need Python, Visual Studio Build Tools or a C++ compiler.
|
||
|
||
To build it anyway (it is used when present), install a toolchain and run
|
||
`npm rebuild better-sqlite3`:
|
||
|
||
- **Windows:** [Visual Studio Build Tools](https://visualstudio.microsoft.com/visual-cpp-build-tools/) with the C++ workload
|
||
- **macOS:** `xcode-select --install`
|
||
- **Linux:** `sudo apt install python3 make g++` (Debian/Ubuntu) or equivalent
|
||
|
||
### "SQLite backend not available" error on startup
|
||
|
||
Neither `better-sqlite3` nor `node:sqlite` could be loaded — almost always a
|
||
Node older than 22.5. Upgrade Node.
|
||
|
||
### Database is locked / busy errors
|
||
|
||
The SQLite database uses WAL mode with a 5-second busy timeout. If you see lock errors:
|
||
|
||
- Ensure only one dashboard server instance is running
|
||
- Check for zombie `node server/index.js` processes: `ps aux | grep server/index`
|
||
- Delete `data/dashboard.db-wal` and `data/dashboard.db-shm` if the server was killed uncleanly, then restart
|
||
|
||
### `npm run dev` fails immediately
|
||
|
||
Ensure both server and client dependencies are installed:
|
||
|
||
```bash
|
||
npm run setup
|
||
```
|
||
|
||
If the error mentions a missing module like `express` or `react`, dependencies may be incomplete. Delete `node_modules` in both the root and `client/`, then re-run setup:
|
||
|
||
```bash
|
||
rm -rf node_modules client/node_modules
|
||
npm run setup
|
||
```
|
||
|
||
### Server starts but client shows a blank page
|
||
|
||
The Vite dev server and Express server run on different ports. Make sure both are running (`npm run dev` starts both). Open **http://localhost:5173**, not `http://localhost:4820`, during development.
|
||
|
||
### "Start Run" fails, or the terminal never attaches
|
||
|
||
The run console is tmux-backed. Check, in this order:
|
||
|
||
```bash
|
||
tmux -V # not installed → see Step 0
|
||
curl -s localhost:4820/api/run/tmux # {"available":false} → the SERVER cannot see tmux
|
||
curl -s "localhost:4820/api/run/binary?tool=claude" # {"found":true,"path":"…"}
|
||
```
|
||
|
||
`{"available":false}` while your own shell finds tmux means the server runs with
|
||
a different `PATH` — most often a systemd `--user` service, which does not read
|
||
your shell profile. Fix it by installing tmux system-wide (`/usr/bin/tmux`)
|
||
rather than into a shell-managed prefix, then `agenthub service uninstall &&
|
||
agenthub service install`.
|
||
|
||
The same `PATH` rule applies to `claude` and `codex`: a run starts the binary by
|
||
name, so a `claude` installed only through an interactive shell's profile is
|
||
invisible to a service-managed server. `{"found":false}` for the lane's tool is
|
||
that case.
|
||
|
||
### No sessions appearing after starting Claude Code
|
||
|
||
**Check 1 — Is the server running?**
|
||
|
||
```bash
|
||
curl http://localhost:4820/api/health
|
||
# Expected: {"status":"ok","timestamp":"..."}
|
||
```
|
||
|
||
**Check 2 — Are hooks installed?**
|
||
|
||
With the plugin, its hooks come with it — run `/agenthub-doctor` instead. For a
|
||
checkout, open `~/.claude/settings.json` and confirm it contains a `hooks`
|
||
section with entries referencing `hook-handler.js`. If not, run:
|
||
|
||
```bash
|
||
npm run install-hooks
|
||
```
|
||
|
||
**Check 3 — Did you start a new Claude Code session after the server started?**
|
||
|
||
Hooks only apply to sessions started after installation. Restart Claude Code.
|
||
|
||
**Check 4 — Is Node.js in PATH when Claude Code runs hooks?**
|
||
|
||
On some systems, the shell environment when Claude Code fires hooks may not include the full PATH. Test with:
|
||
|
||
```bash
|
||
node --version
|
||
```
|
||
|
||
If Node.js is not found, use the full path to `node` in the hook command. Edit `scripts/install-hooks.js`, replace `node` with the absolute path (e.g. `/usr/local/bin/node`), and re-run `npm run install-hooks`.
|
||
|
||
### Dashboard shows "Disconnected" in the sidebar
|
||
|
||
The WebSocket connection to the server failed. Ensure the server is running:
|
||
|
||
```bash
|
||
npm run dev
|
||
```
|
||
|
||
The client will automatically reconnect every 2 seconds once the server is available.
|
||
|
||
### Port 4820 already in use
|
||
|
||
When another program holds the port, the server stops with
|
||
`Port 4820 is already in use by another program…` (a running AgentHub there is
|
||
detected and simply reused). Pick another port:
|
||
|
||
```bash
|
||
DASHBOARD_PORT=4821 npm start # or: DASHBOARD_PORT=4821 npm run dev
|
||
```
|
||
|
||
The Vite dev proxy and the Claude Code hooks follow the port on their own
|
||
(`client/vite.config.ts` reads `DASHBOARD_PORT`; the server publishes the port it bound).
|
||
|
||
And make sure Claude Code posts hooks to the new port:
|
||
|
||
```bash
|
||
CLAUDE_DASHBOARD_PORT=4821 claude
|
||
# or edit scripts/hook-handler.js and change the default port
|
||
```
|
||
|
||
## Ports
|
||
|
||
| Service | Default | Override |
|
||
|---|---|---|
|
||
| Dashboard server | `4820` | `DASHBOARD_PORT=xxxx npm run dev` |
|
||
| Client dev server | `5173` | Edit `client/vite.config.ts` |
|