diff --git a/INSTALL.md b/INSTALL.md index 21e21f4a..aa472e87 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -30,12 +30,64 @@ dashboard, run it from source, or stay on Node 20. | Node.js | 20+ (22+ recommended) | Required for server and client | | 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 | **Required for the run console** — the browser terminal, `agenthub lanes shell`, typing `/reload-plugins` into a live pane, and the Codex nudge. 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 lanes ci status` and the PR stages of `/agenthub:ship-full` | | Python | 3.6+ | Optional — only for `scripts/expand-ts-module-docs.py` (doc generation) | -| Git | Any | For cloning the repository | | `superpowers` plugin | Any | Optional — required only by `/agenthub:ship-feature` and `/agenthub:ship-feature-lane`, which drive its skills step by step (and which the built-in pipeline templates name as their per-node commands). Nothing else in AgentHub needs it; `/agenthub-doctor` reports it as `WARN` when absent. **Installed automatically** by the plugin's `SessionStart` bootstrap (opt out with `AGENTHUB_AUTO_INSTALL_PLUGINS=0`); by hand: `claude plugin install superpowers@claude-plugins-official` | --- +## 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 lanes 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. + +Check it: + +```bash +tmux -V # e.g. tmux 3.4 +git --version +node -v # v20.x / v22.x / 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-`, and never reads your `~/.tmux.conf` for anything it +depends on (mouse mode is set on the session explicitly). You can attach to the +exact same pane from a normal terminal: + +```bash +agenthub lanes 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. + +--- + ## Step 1 — Clone the repository ```bash @@ -261,6 +313,27 @@ npm run setup 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 See [SETUP.md — Troubleshooting](./SETUP.md#troubleshooting) for detailed hook debugging steps. diff --git a/README.md b/README.md index b396f7e1..238dce90 100644 --- a/README.md +++ b/README.md @@ -13,10 +13,24 @@ Internal build — all rights reserved. *(Tiếng Việt: [README.vi.md](README.vi.md))* -## What it does +## 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** + - **Lanes.** One lane per working directory, surviving session restarts. A lane moves through pipeline stages and the dashboard shows where it is. - **Stage detection.** The stage is inferred from the tool stream, so a session @@ -26,15 +40,70 @@ Internal build — all rights reserved. PTY, rendered with xterm.js) attached to a lane's directory — the exact TUI you'd see locally, fully interactive, resumable, and attachable from a real terminal too via `agenthub lanes shell`. Which CLI it starts is the lane's own - setting. -- **Analytics (in-dashboard tab), alerts, and a workflow view**, plus a CLI. + setting. **This is the part that needs `tmux` installed** — see + [Prerequisites](#prerequisites). +- **Per-lane app stacks.** A repo that declares `.agenthub/profile/` gets its own + slot, ports and services per lane (`agenthub lanes up|down|logs`). +- **Ship pipelines.** `/agenthub:ship-full` and `/agenthub:ship-quick` drive a + feature from intake to PR, declaring every stage to the dashboard as it goes. -## Requirements +**Configure** -Node **>= 20** (`engines` in `package.json`). Node **24** is what the test suites -are verified on — node 25 currently breaks 6 server tests through a -better-sqlite3 ABI mismatch and 20 client tests through a global `localStorage` -change. +- **Claude Code + Codex configuration explorer.** Settings hosts one panel 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). +- **A CLI for all of it** (`agenthub`, plus `agenthub repl`), a PWA install, and + a UI in English, Vietnamese, Chinese and Korean. + +## Prerequisites + +| Need | Version | Required for | +|---|---|---| +| **Node.js** | >= 20 (>= 22.5 for a plugin install) | Everything. Node **24** is what the test suites are verified on | +| **npm** | 9+ | Ships with Node | +| **git** | any | Cloning, and every lane operation (worktrees, branch/status reads) | +| **tmux** | 3.x | The browser run console, `agenthub lanes shell`, typing `/reload-plugins` into a live pane, the Codex nudge | +| **Claude Code** | 2.x | Hook ingestion — the whole monitoring side | +| **Codex CLI** | any | Optional — only for `codex` lanes | +| **GitHub CLI (`gh`)** | any | Optional — only for `agenthub lanes ci status` and the PR stages of the ship pipelines | +| **systemd (user)** | — | Optional — only for `agenthub service install` (Linux auto-start on boot) | + +Node 25 currently breaks 6 server tests through a better-sqlite3 ABI mismatch +and 20 client tests through a global `localStorage` change — stay on 20/22/24. + +### Installing tmux + +The dashboard shells out to the `tmux` binary; there is no bundled fallback, so +**Start Run** and `agenthub lanes shell` fail without it. Everything else +(sessions, analytics, lanes, 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. + +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 lane's run lives in a tmux session named `agenthub-lane-`, so the browser +terminal and a local `tmux attach -t agenthub-lane-` (or `agenthub lanes +shell`) are two clients on the same pane — they stay in sync, and killing the +dashboard never kills the run. + +### The superpowers plugin The **`superpowers` plugin is required by the ship-feature pipelines only**, not by the dashboard. `/agenthub:ship-feature` and `/agenthub:ship-feature-lane` drive diff --git a/README.vi.md b/README.vi.md index 715fdb81..bcec0345 100644 --- a/README.vi.md +++ b/README.vi.md @@ -13,10 +13,24 @@ Internal build — all rights reserved. *(Bản dịch tiếng Việt của [README.md](README.md), tham khảo — README.md gốc là bản chính thức.)* -## Làm được gì +## Chức năng chính + +**Quan sát** - **Session, agent, event.** Mọi thứ Claude Code emit ra, ghi lại và tìm kiếm được: tool call, token usage, cost, cây subagent, transcript. +- **Analytics và chi phí.** Token và cost ước tính theo từng model, với bảng + pricing sửa được; top tool, agent type và workflow pattern. +- **Activity feed và alert.** Luồng event trực tiếp, alert rule, và webhook + target bắn khi rule khớp. +- **Import lịch sử.** Mọi session dưới `~/.claude/projects/` được import lúc + khởi động; lịch sử từ máy khác đưa vào qua Settings → Import History + (idempotent, không bao giờ đếm trùng token). +- **Remote source.** Kéo lịch sử session từ máy khác qua SSH + (`agenthub remote-sources`). + +**Điều khiển** + - **Lane.** Mỗi working directory một lane, sống sót qua session restart. Lane đi qua các pipeline stage và dashboard hiển thị nó đang ở đâu. - **Stage detection.** Stage được suy luận (infer) từ tool stream, nên một @@ -26,14 +40,85 @@ Internal build — all rights reserved. - **Chạy Claude Code hoặc Codex từ trình duyệt.** Một terminal thật (tmux + PTY thật, render bằng xterm.js) gắn vào thư mục của lane — đúng cái TUI bạn thấy ở máy, tương tác đầy đủ, resume được, và attach được từ terminal thật qua - `agenthub lanes shell`. Chạy CLI nào là thiết lập riêng của từng lane. -- **Analytics (tab trong dashboard), alert và workflow view**, cộng thêm CLI. + `agenthub lanes shell`. Chạy CLI nào là thiết lập riêng của từng lane. **Đây + chính là phần cần cài `tmux`** — xem [Yêu cầu hệ thống](#yêu-cầu-hệ-thống). +- **App stack riêng cho từng lane.** Repo khai báo `.agenthub/profile/` thì mỗi + lane được cấp slot, port và service riêng (`agenthub lanes up|down|logs`). +- **Ship pipeline.** `/agenthub:ship-full` và `/agenthub:ship-quick` đưa một + feature từ intake tới PR, khai báo từng stage về dashboard trên đường đi. -## Yêu cầu +**Cấu hình** -Node **>= 20** (`engines` trong `package.json`). Node **24** là bản test suite -được verify — node 25 hiện đang làm gãy 6 test server do lệch ABI -`better-sqlite3` và 20 test client do thay đổi global `localStorage`. +- **Trình khám phá cấu hình Claude Code + Codex.** Trang Settings có một panel + cho mỗi tool: mọi plugin, skill, subagent, slash command, MCP server, hook, + settings file, memory file, marketplace, keybinding và statusline mà Claude + Code biết — đọc được tất cả, còn sửa/tạo/xóa thì chỉ cho các bề mặt dạng file + text (skill, agent, command, output style, `CLAUDE.md`). Plugin, MCP, hook và + `settings.json` để read-only: chúng tranh ghi với CLI đang chạy. +- **Cập nhật.** Phát hiện bản mới dựa trên release, kèm nút **Update all** ngay + trong UI (marketplace → mọi plugin → AgentHub sau cùng → reload session). +- **CLI cho toàn bộ** (`agenthub`, và `agenthub repl`), cài được như PWA, UI có + tiếng Anh, Việt, Trung và Hàn. + +## Yêu cầu hệ thống + +| Cần | Phiên bản | Dùng cho | +|---|---|---| +| **Node.js** | >= 20 (>= 22.5 nếu cài qua plugin) | Tất cả. Test suite được verify trên Node **24** | +| **npm** | 9+ | Đi kèm Node | +| **git** | bất kỳ | Clone repo, và mọi thao tác lane (worktree, đọc branch/status) | +| **tmux** | 3.x | Run console trên trình duyệt, `agenthub lanes shell`, gõ `/reload-plugins` vào pane đang sống, nhắc nhở Codex | +| **Claude Code** | 2.x | Hook ingestion — toàn bộ phần monitoring | +| **Codex CLI** | bất kỳ | Tùy chọn — chỉ cho lane dùng `codex` | +| **GitHub CLI (`gh`)** | bất kỳ | Tùy chọn — chỉ cho `agenthub lanes ci status` và các stage PR của ship pipeline | +| **systemd (user)** | — | Tùy chọn — chỉ cho `agenthub service install` (tự khởi động khi boot, Linux) | + +Node 25 hiện đang làm gãy 6 test server do lệch ABI `better-sqlite3` và 20 test +client do thay đổi global `localStorage` — hãy ở lại 20/22/24. + +### Cài tmux + +Dashboard gọi thẳng binary `tmux`, không có bản dự phòng đi kèm, nên thiếu nó +thì **Start Run** và `agenthub lanes shell` sẽ lỗi. Phần còn lại (session, +analytics, lane, stage detection, CLI) vẫn chạy bình thường. + +```bash +sudo apt install tmux # Debian / Ubuntu +sudo dnf install tmux # Fedora / RHEL +brew install tmux # macOS +sudo pacman -S tmux # Arch +``` + +Windows không có tmux native — chạy AgentHub trong WSL2 và cài tmux ở trong đó. + +Kiểm tra, cả từ shell của bạn lẫn từ góc nhìn của server: + +```bash +tmux -V # ví dụ: tmux 3.4 +curl -s localhost:4820/api/run/tmux # {"available":true} +``` + +Mỗi lane chạy trong một tmux session tên `agenthub-lane-`, nên terminal +trên trình duyệt và `tmux attach -t agenthub-lane-` (hoặc `agenthub lanes +shell`) ở máy là hai client trên cùng một pane — chúng đồng bộ với nhau, và tắt +dashboard không bao giờ giết run đang chạy. Detach bằng `Ctrl-b d`. + +### Plugin superpowers + +Plugin **`superpowers` chỉ cần cho ship-feature pipeline**, không cần cho +dashboard. `/agenthub:ship-feature` và `/agenthub:ship-feature-lane` lái các skill +của nó theo từng phase. Thiếu nó thì dashboard, hook và CLI vẫn chạy bình thường +— chỉ hai skill pipeline là dừng giữa chừng; `/agenthub-doctor` báo dòng `WARN`. + +**Bình thường bạn không phải tự cài.** Bootstrap `SessionStart` của plugin cài +giúp một lần trong worker chạy nền, và session hiện tại cần `/reload-plugins` +thì skill mới resolve được (session sau tự nhận). Cài tay: + +```bash +claude plugin install superpowers@claude-plugins-official +``` + +Đặt `AGENTHUB_AUTO_INSTALL_PLUGINS=0` để tắt cài tự động. ## Cài như Claude Code plugin diff --git a/SETUP.md b/SETUP.md index f20a5a10..0d07f3c7 100644 --- a/SETUP.md +++ b/SETUP.md @@ -324,6 +324,26 @@ The SQLite database uses WAL mode with a 5-second busy timeout. If you see lock --- +### The run console won't start a run / the terminal never attaches + +Runs are tmux-backed: the server executes the `tmux` binary and has no fallback. + +```bash +tmux -V # missing → sudo apt install tmux +curl -s http://localhost:4820/api/run/tmux # {"available":true} +curl -s "http://localhost:4820/api/run/binary?tool=claude" # {"found":true,"path":"…"} +``` + +`{"available":false}` while your own shell finds tmux means the **server's** +`PATH` differs from yours — typically a systemd `--user` service, which never +reads your shell profile. Install tmux system-wide (`/usr/bin/tmux`) and +re-run `agenthub service uninstall && agenthub service install`. The same +applies to `claude` / `codex`: a run launches them by name. + +Install instructions per OS: [INSTALL.md → Step 0](./INSTALL.md#step-0--system-packages). + +--- + ### No sessions appearing after starting Claude Code **Check 1 — Is the server running?** diff --git a/bin/agenthub.js b/bin/agenthub.js index 1f2f524a..0c27b29a 100755 --- a/bin/agenthub.js +++ b/bin/agenthub.js @@ -1279,6 +1279,23 @@ async function cmdDoctor() { ); console.log(`${c.green("✔")} WS connections ${srv.ws_connections ?? 0}`); + // Asked of the SERVER, not of this shell: a run is a tmux session the server + // spawns, so the only PATH that decides whether Start Run works is its own — + // a systemd --user service never reads your shell profile. Missing tmux is a + // warning, not a failure: it costs the run console and `lanes shell`, nothing + // else. + try { + const { available } = await get("/api/run/tmux"); + console.log( + available + ? `${c.green("✔")} tmux available to the server` + : `${c.yellow("!")} tmux not on the server's PATH — Start Run and \`agenthub lanes shell\` will fail. ` + + `Install it (sudo apt install tmux / brew install tmux), then restart the server` + ); + } catch { + console.log(`${c.dim("·")} tmux not reported by this server`); + } + try { const { sources = [] } = await get("/api/remote-sources"); if (sources.length === 0) { @@ -2995,7 +3012,7 @@ const COMMAND_GROUPS = [ [ "Administration", [ - ["doctor", "", "Connectivity, hooks, database, and remote sources diagnosis"], + ["doctor", "", "Connectivity, hooks, database, tmux, and remote sources diagnosis"], ["info", "", "Raw system info JSON"], ["export", "[file.json]", "Export all data as JSON"], ["cleanup", "--hours N --days M", "Abandon stale / purge old sessions"], diff --git a/docs/CLI.md b/docs/CLI.md index a9d640ee..7adf7fc7 100644 --- a/docs/CLI.md +++ b/docs/CLI.md @@ -272,7 +272,7 @@ Because a lane's services are fully detached, restarting the dashboard (or `agen | Command | Description | | ------- | ----------- | -| `agenthub doctor` | Diagnosis: API reachability, hook installation status + path, database path/size/row counts, server uptime and Node version, WS connections | +| `agenthub doctor` | Diagnosis: API reachability, hook installation status + path, database path/size/row counts, server uptime and Node version, WS connections, whether `tmux` is on **the server's** PATH (asked via `GET /api/run/tmux` — a systemd `--user` service does not read your shell profile), remote-source status | | `agenthub info` | The raw `/api/settings/info` JSON (pipe it to `jq`) | | `agenthub export [file.json]` | Full JSON data export (sessions, agents, events, tokens, workflows, dashboard runs, alert rules, pricing) — defaults to a dated filename. Re-importable via `agenthub import-data` | | `agenthub cleanup --hours N --days M` | Abandon active sessions idle for `N` hours and/or purge completed sessions older than `M` days | diff --git a/docs/PLUGINS.md b/docs/PLUGINS.md index 5f33a161..f70cb318 100644 --- a/docs/PLUGINS.md +++ b/docs/PLUGINS.md @@ -176,7 +176,7 @@ reboot, same as always. | Command | Does | |---|---| -| `/agenthub-doctor` | Node version, bootstrap state, runtime deps, server liveness, duplicate hooks, CLI launcher + PATH, **skill plugins** (see below), MCP build freshness, UI bundle | +| `/agenthub-doctor` | Node version, bootstrap state, runtime deps, server liveness, duplicate hooks, CLI launcher + PATH, **tmux** (`WARN` when absent — only the run console, `agenthub lanes shell` and session reloads need it), **skill plugins** (see below), MCP build freshness, UI bundle | | `/agenthub-update` | Pull the latest `agenthub` release via `claude plugin marketplace update` + `claude plugin update agenthub@ -y`, then reinstall dependencies and restart the server (`plugin-bootstrap.js --force`). The plugin pull needs a manual `/reload-plugins` afterward to take effect in the current session — not scriptable from a command. | | `/agenthub-open` | Rebuild the UI bundle and print the dashboard URL. Re-runs the bootstrap first when the server is down or still executing a plugin root that a mid-session `/plugin` update retired, so a fresh bundle never talks to an older server's routes | | `/ship-full ` | Put the lane on the 16-node `ship-feature` template, then run `/agenthub:ship-feature-lane` **in this session**. The in-session twin of `agenthub ship-full` (which spawns its own `claude` from a terminal) | diff --git a/docs/README.md b/docs/README.md index 79bc710b..fae7a9e9 100644 --- a/docs/README.md +++ b/docs/README.md @@ -70,10 +70,12 @@ graph LR **Quick Start:** -1. Read [SETUP.md](../SETUP.md) -2. Run `npm run setup` -3. Run `npm run dev` -4. Open browser to `http://localhost:5173` +1. Install the system packages — `node` (20+), `git`, and `tmux` (the run + console has no fallback without it): [INSTALL.md → Step 0](../INSTALL.md#step-0--system-packages) +2. Read [SETUP.md](../SETUP.md) +3. Run `npm run setup` +4. Run `npm run dev` +5. Open browser to `http://localhost:5173` --- diff --git a/plugins/agenthub/commands/agenthub-doctor.md b/plugins/agenthub/commands/agenthub-doctor.md index f44b1976..bf6ff1df 100644 --- a/plugins/agenthub/commands/agenthub-doctor.md +++ b/plugins/agenthub/commands/agenthub-doctor.md @@ -1,5 +1,5 @@ --- -description: Diagnose a plugin-installed AgentHub — Node, npm, disk, bootstrap, server, hooks, CLI, UI build +description: Diagnose a plugin-installed AgentHub — Node, npm, disk, bootstrap, server, hooks, CLI, tmux, UI build --- Run the diagnostic and report what it found. Do not fix anything unless the user @@ -31,6 +31,11 @@ Print the output verbatim, then add at most three lines of guidance based on the means that attempt has not run or did not succeed — say `/agenthub-update` retries it, and print the `claude plugin install …` line from the output as the manual route. +- `WARN tmux` → only the run console, `agenthub lanes shell` and the + session-reload keystrokes are affected; monitoring, lanes and the CLI are not. + Print the install command from the output. If the row says `ok` but Start Run + still fails, the server has a different PATH than this check — ask it directly + with `curl -s localhost:4820/api/run/tmux`. - `WARN Dashboard UI` → run `/agenthub-open` to build the bundle. When the row quotes a build error, report that error — it is the actual problem. diff --git a/scripts/plugin-doctor.js b/scripts/plugin-doctor.js index 2330a78b..a45dd98a 100644 --- a/scripts/plugin-doctor.js +++ b/scripts/plugin-doctor.js @@ -5,7 +5,8 @@ * npm, free disk on the data dir, bootstrap state, runtime dependencies (every * declared package, not just that node_modules exists), server liveness, * duplicate hook entries (the one failure that silently doubles every token and - * cost figure), the `agenthub` CLI launcher and its PATH, whether the slash + * cost figure), the `agenthub` CLI launcher and its PATH, the `tmux` binary + * every browser-started run depends on, whether the slash * commands the pipeline templates name as node drivers are actually resolvable * (the `superpowers` plugin, today), and the dashboard bundle — quoting the last * build error when it is missing. @@ -82,6 +83,7 @@ function diagnose() { ); add(...cliStatus()); + add(...tmuxStatus()); add(...skillPluginStatus()); add(...dashboardUiStatus(rt)); @@ -218,6 +220,29 @@ function skillPluginStatus() { ]; } +/** + * WARN (never FAIL) when `tmux` is absent: every run the dashboard starts is a + * tmux session (`server/lib/tmux.js`), so without the binary the run console, + * `agenthub lanes shell` and the session-reload keystrokes all fail — while + * sessions, analytics, lanes and the CLI keep working. Same reasoning as the + * skill-plugins row: a partial feature loss must not fail a doctor run. + * + * This checks the doctor's own PATH. A server started as a systemd `--user` + * service can still see a different one — `GET /api/run/tmux` is the server's + * own answer, and `agenthub doctor` reports that. + */ +function tmuxStatus() { + if (require("../server/lib/tmux").isTmuxAvailable()) { + return ["ok", "tmux", "on PATH — the run console can start sessions"]; + } + return [ + "warn", + "tmux", + "not found — Start Run and `agenthub lanes shell` will fail (everything else is unaffected). " + + "Install it: sudo apt install tmux / brew install tmux (Windows: run AgentHub under WSL2)", + ]; +} + function countLegacyHookEntries() { try { const settings = JSON.parse(fs.readFileSync(getSettingsPath(), "utf8")); diff --git a/server/__tests__/plugin-doctor-tmux.test.js b/server/__tests__/plugin-doctor-tmux.test.js new file mode 100644 index 00000000..d4a6c99f --- /dev/null +++ b/server/__tests__/plugin-doctor-tmux.test.js @@ -0,0 +1,54 @@ +/** + * @file Tests the `tmux` row of scripts/plugin-doctor.js — the check that tells + * an install its browser run console cannot work, because every run the + * dashboard starts is a tmux session (server/lib/tmux.js). The row must WARN + * and never FAIL: sessions, analytics, lanes and the CLI all work without the + * binary, so failing a doctor run over it would cry wolf. tmux is faked through + * the module's own exec seam, so the test never depends on the host having it. + * @author Nguyễn Ngọc Trí Vĩ + */ + +const { describe, it, after } = require("node:test"); +const assert = require("node:assert/strict"); +const fs = require("node:fs"); +const os = require("node:os"); +const path = require("node:path"); + +const TMP_HOME = fs.mkdtempSync(path.join(os.tmpdir(), "agenthub-doctor-tmux-")); +process.env.CLAUDE_HOME = TMP_HOME; + +const tmux = require("../lib/tmux"); +const { diagnose } = require("../../scripts/plugin-doctor"); + +after(() => { + tmux.__reset(); + fs.rmSync(TMP_HOME, { recursive: true, force: true }); + delete process.env.CLAUDE_HOME; +}); + +/** Fake the tmux binary as present or absent, then read back the row. */ +function rowWithTmux(present) { + tmux.__setExecImpl(() => { + if (!present) throw new Error("spawnSync tmux ENOENT"); + return "tmux 3.4\n"; + }); + const row = diagnose().find((r) => r.label === "tmux"); + assert.ok(row, "the doctor must always emit a tmux row"); + return row; +} + +describe("plugin-doctor: tmux row", () => { + it("warns, never fails, when tmux is not on PATH", () => { + const row = rowWithTmux(false); + + assert.equal(row.level, "warn", "a missing tmux must not fail the doctor run"); + assert.match(row.detail, /lanes shell/, "the reader needs to know WHAT breaks"); + assert.match(row.detail, /install/i, "and how to fix it"); + }); + + it("passes when tmux is available", () => { + const row = rowWithTmux(true); + + assert.equal(row.level, "ok", row.detail); + }); +});