@blackbelt-technology/pi-agent-dashboard 0.4.0 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/AGENTS.md +30 -8
  2. package/README.md +386 -494
  3. package/docs/architecture.md +63 -9
  4. package/package.json +8 -5
  5. package/packages/extension/package.json +6 -4
  6. package/packages/extension/src/__tests__/ask-user-tool.test.ts +40 -8
  7. package/packages/extension/src/__tests__/bridge-entry-id-pi-070.test.ts +174 -0
  8. package/packages/extension/src/__tests__/event-forwarder.test.ts +30 -0
  9. package/packages/extension/src/__tests__/fork-entryid-timing.test.ts +64 -76
  10. package/packages/extension/src/__tests__/multiselect-list.test.ts +137 -0
  11. package/packages/extension/src/__tests__/no-session-replacement-calls.test.ts +99 -0
  12. package/packages/extension/src/ask-user-tool.ts +5 -4
  13. package/packages/extension/src/bridge.ts +102 -15
  14. package/packages/extension/src/multiselect-list.ts +146 -0
  15. package/packages/extension/src/multiselect-polyfill.ts +43 -0
  16. package/packages/extension/src/server-launcher.ts +15 -3
  17. package/packages/server/package.json +5 -5
  18. package/packages/server/src/__tests__/fixtures/fork-jsonl-roundtrip.jsonl +8 -0
  19. package/packages/server/src/__tests__/fork-jsonl-roundtrip.test.ts +49 -0
  20. package/packages/server/src/__tests__/pi-version-skew.test.ts +72 -0
  21. package/packages/server/src/__tests__/restart-helper.test.ts +34 -6
  22. package/packages/server/src/cli.ts +56 -9
  23. package/packages/server/src/pi-version-skew.ts +12 -1
  24. package/packages/server/src/restart-helper.ts +13 -2
  25. package/packages/shared/package.json +1 -1
  26. package/packages/shared/src/__tests__/no-hardcoded-node-modules-paths.test.ts +176 -0
  27. package/packages/shared/src/__tests__/no-raw-node-import.test.ts +146 -0
  28. package/packages/shared/src/__tests__/node-spawn.test.ts +210 -0
  29. package/packages/shared/src/__tests__/resolve-tool-cli.test.ts +105 -0
  30. package/packages/shared/src/__tests__/state-replay-entry-id.test.ts +69 -0
  31. package/packages/shared/src/platform/index.ts +1 -0
  32. package/packages/shared/src/platform/node-spawn.ts +154 -0
  33. package/packages/shared/src/protocol.ts +23 -0
  34. package/packages/shared/src/state-replay.ts +9 -0
  35. package/packages/shared/src/tool-registry/definitions.ts +92 -0
package/README.md CHANGED
@@ -6,191 +6,163 @@
6
6
 
7
7
  A web-based dashboard for monitoring and interacting with [pi](https://github.com/badlogic/pi-mono) agent sessions from any browser, including mobile.
8
8
 
9
- **Website:** [blackbelttechnology.github.io/pi-agent-dashboard](https://blackbelttechnology.github.io/pi-agent-dashboard) — animated tour, screenshots, and install guide.
9
+ 🌐 **Website & demo:** [blackbelttechnology.github.io/pi-agent-dashboard](https://blackbelttechnology.github.io/pi-agent-dashboard) — animated tour, screenshots, and install guide.
10
+ 📝 **Changelog:** [`CHANGELOG.md`](CHANGELOG.md)
10
11
 
11
- **Changelog:** see [`CHANGELOG.md`](CHANGELOG.md) for release notes.
12
+ > **Note:** This dashboard only works with [pi](https://github.com/badlogic/pi-mono). Oh My Pi is **not** supported.
12
13
 
13
- **Releasing:** see [`docs/release-process.md`](docs/release-process.md) for the cut-a-release workflow.
14
+ ---
14
15
 
15
- ## Features
16
-
17
- - **Real-time session mirroring** — See all active pi sessions with live streaming messages
18
- - **Bidirectional interaction** — Send prompts and commands from the browser
19
- - **Workspace management** — Organize sessions by project folder with pinned directories and drag-to-reorder
20
- - **Command autocomplete** — `/` prefix triggers command dropdown with filtering
21
- - **Session statistics** — Token counts, costs, model info, thinking level, context usage bar
22
- - **Elapsed time tracking** — Live ticking counters on running operations, final duration on completed tool calls and reasoning blocks
23
- - **Mobile-friendly** — Responsive layout with swipe drawer, touch targets, and mobile action menus
24
- - **Session spawning** — Launch new pi sessions from the dashboard (headless by default, or via tmux)
25
- - **PromptBus architecture** — Unified prompt routing with adapters (TUI, dashboard, custom). Interactive dialogs (confirm/select/input/editor/multiselect) survive page refresh and server restart. First-response-wins semantics with cross-adapter dismissal.
26
- - **On-demand session loading** — Browse historical sessions with lazy-loaded content from pi session files
27
- - **Integrated terminal** — Full browser-based terminal emulator (xterm.js + node-pty) with ANSI color support, scrollback, and keep-alive
28
- - **pi-flows integration** — Live flow execution dashboard with agent cards, detail views, flow graph visualization, summary, abort/auto controls. Launch flows and design new ones with the Flow Architect — all from the browser. Fork decisions and subagent dialogs forwarded via PromptBus.
29
- - **Force kill escalation** — Two-click Stop button (in command bar and on running tool cards): first click sends soft abort, second click force-kills the process (SIGTERM → SIGKILL). Session preserved as "ended" for resume/fork. Repeated tool calls (e.g. health check loops) are auto-collapsed with a count badge.
30
- - **Searchable select dialogs** — Keyboard-navigable picker with real-time filtering for OpenSpec changes and flow commands
31
- - **Browser-based provider auth** — Sign in to Anthropic, OpenAI Codex, GitHub Copilot, Gemini CLI, and Antigravity directly from Settings. Enter API keys for other providers. Credentials saved to `~/.pi/agent/auth.json` and live-synced to running sessions.
32
- - **Custom LLM providers** — Add OpenAI-compatible, Anthropic-compatible, or Google Generative AI endpoints (Settings → Providers → LLM Providers). Each card has a **Test** button that pings the provider's `/models` endpoint to verify your base URL + API key combination before saving. Adding, editing, or removing a provider takes effect live in every running pi session — no restart required.
33
- - **Package management** — Browse, install, update, and remove pi packages from the dashboard. Search the npm registry for pi-package extensions/skills/themes, install from npm or git URL, manage global packages in Settings and local packages per workspace. All active sessions auto-reload after changes.
34
- - **OpenSpec integration** — Browse specs, view archive history, manage changes, and create new changes from the session sidebar
35
- - **Diff viewer** — Side-by-side and unified diff views with file tree navigation for reviewing agent changes
36
- - **Editor integration** — Open files in your preferred editor (VS Code, Cursor, etc.) directly from tool call cards
37
- - **Markdown preview** — Rendered markdown views with search, mermaid diagrams, and syntax highlighting
38
- - **Network discovery** — mDNS-based auto-discovery of other dashboard servers on the local network; connect to known remote servers
39
-
40
- ## Architecture
41
-
42
- ```mermaid
43
- graph LR
44
- subgraph "Per pi session"
45
- B[Bridge Extension]
46
- end
47
-
48
- subgraph "Dashboard Server (Node.js)"
49
- PG[Pi Gateway :9999]
50
- BG[Browser Gateway :8000]
51
- HTTP[HTTP / Static Files]
52
- MEM[(In-Memory Store)]
53
- JSON[(JSON Files)]
54
- end
55
-
56
- subgraph "Browser"
57
- UI[React Web Client]
58
- end
59
-
60
- B <-->|WebSocket| PG
61
- UI <-->|WebSocket| BG
62
- UI -->|HTTP| HTTP
63
- PG --- MEM
64
- PG --- JSON
65
- BG --- MEM
66
- ```
16
+ ## Table of contents
67
17
 
68
- The system has three components:
18
+ - [Quickstart](#quickstart)
19
+ - [Features](#features)
20
+ - [Prerequisites](#prerequisites)
21
+ - [Configuration](#configuration)
22
+ - [Usage](#usage)
23
+ - [Recommended extensions](#recommended-extensions)
24
+ - [Troubleshooting](#troubleshooting)
25
+ - [Architecture](#architecture)
26
+ - [Monitoring](#monitoring)
27
+ - [Development](#development)
28
+ - [Building the Electron app](#building-the-electron-app)
29
+ - [CI/CD & releasing](#cicd--releasing)
30
+ - [License](#license)
69
31
 
70
- | Component | Location | Role |
71
- |-----------|----------|------|
72
- | **Bridge Extension** | `packages/extension/` | Runs in every pi session. Forwards events, relays commands, auto-starts server, hosts PromptBus. |
73
- | **Dashboard Server** | `packages/server/` | Aggregates events in-memory, persists metadata to JSON, serves the web client, manages terminals. |
74
- | **Web Client** | `packages/client/` | React + Tailwind UI with real-time WebSocket updates. |
75
- | **Shared** | `packages/shared/` | TypeScript types, protocols, and utilities shared across all packages. |
76
-
77
- See [docs/architecture.md](docs/architecture.md) for detailed data flows, reconnection logic, and persistence model.
32
+ ---
78
33
 
79
- ## Getting Started
34
+ ## Quickstart
80
35
 
81
- There are three ways to use the dashboard, from simplest to most flexible:
36
+ Three install paths, pick one:
82
37
 
83
- ### Option A: Electron Desktop App (standalone — no prerequisites)
38
+ ### A Electron desktop app (no prerequisites)
84
39
 
85
- Download a pre-built installer from [GitHub Releases](https://github.com/BlackBeltTechnology/pi-agent-dashboard/releases) for your platform:
40
+ Download a pre-built installer from [GitHub Releases](https://github.com/BlackBeltTechnology/pi-agent-dashboard/releases):
86
41
 
87
42
  | Platform | Download |
88
43
  |----------|----------|
89
- | **macOS** (Apple Silicon) | `.dmg` (arm64) |
90
- | **macOS** (Intel) | `.dmg` (x64) |
91
- | **Linux** (x64) | `.deb` or `.AppImage` |
92
- | **Linux** (ARM64) | `.deb` |
93
- | **Windows** (x64) | `.exe` (NSIS installer), `.zip`, or portable `.exe` |
94
- | **Windows** (ARM64) | `.zip` or portable `.exe` |
95
-
96
- On first launch, a setup wizard guides you through:
97
-
98
- 1. **Choose a mode:**
99
- - **Standalone** — Bundles Node.js and auto-installs pi + dashboard + openspec into `~/.pi-dashboard/`. No Node.js, npm, or build tools needed.
100
- - **Power User** — Uses your existing system-installed pi and dashboard.
101
- 2. **Configure an API key** — Enter your Anthropic/OpenAI key or sign in via browser-based OAuth.
102
- 3. **Recommended extensions** — Install the curated set of pi extensions the dashboard is built to work with (see [Recommended extensions](#recommended-extensions) below). You can skip and manage them later from the Packages tab.
103
- 4. **Done** — The app discovers or spawns a dashboard server automatically.
44
+ | **macOS** (Apple Silicon / Intel) | `.dmg` (arm64 / x64) |
45
+ | **Linux** (x64 / ARM64) | `.deb` or `.AppImage` |
46
+ | **Windows** (x64 / ARM64) | `.exe` (NSIS), `.zip`, or portable `.exe` |
104
47
 
105
- > **No terminal, no npm, no Node.js required.** The Electron app is fully self-contained in standalone mode. It bundles a Node.js runtime, spawns the dashboard server internally, and manages all dependencies. System tray integration keeps it running in the background.
48
+ On first launch a setup wizard walks you through mode selection (standalone vs. power-user), API key / OAuth sign-in, and [recommended extensions](#recommended-extensions). The standalone mode bundles Node.js and auto-installs pi + dashboard + openspec into `~/.pi-dashboard/` **no terminal, npm, or Node.js required**.
106
49
 
107
- ### Option B: pi Package (recommended for CLI users)
108
-
109
- Requires [pi](https://github.com/badlogic/pi-mono) (or [Oh My Pi](https://www.npmjs.com/package/@oh-my-pi/pi-coding-agent)) and Node.js **≥ 22.18.0** (or ≥ 24.3.0). Older Node 22.x / 24.x builds are affected by [nodejs/node#58515](https://github.com/nodejs/node/issues/58515) which crashes Fastify at startup.
50
+ ### B pi package (recommended for CLI users)
110
51
 
111
52
  ```bash
112
53
  pi install npm:@blackbelt-technology/pi-agent-dashboard
113
54
  pi
114
55
  ```
115
56
 
116
- The bridge extension auto-starts the dashboard server on first launch. You'll see:
57
+ The bridge extension auto-starts the dashboard server on first launch:
117
58
 
118
59
  ```
119
60
  🌐 Dashboard started at http://localhost:8000
120
61
  ```
121
62
 
122
- Open **http://localhost:8000** in any browser. All active pi sessions appear automatically.
63
+ Open **http://localhost:8000** in any browser. All active pi sessions appear automatically. See [Prerequisites](#prerequisites) for Node.js / build-tool requirements.
123
64
 
124
- ### Option C: Local development install
65
+ ### C From source (contributors)
125
66
 
126
67
  ```bash
127
68
  git clone https://github.com/BlackBeltTechnology/pi-agent-dashboard.git
128
69
  cd pi-agent-dashboard
129
70
  npm install
130
- pi install /path/to/pi-agent-dashboard
71
+ pi install /path/to/pi-agent-dashboard # global
72
+ # or: pi install -l /path/to/pi-agent-dashboard # project-local only
131
73
  ```
132
74
 
133
- ## Recommended extensions
134
-
135
- The dashboard integrates tightly with a small, curated set of pi extensions
136
- — for custom tool rendering, the Flow dashboard, and anthropic-messages
137
- protocol compatibility. The wizard's Recommended-extensions step installs
138
- them in one go; the **Packages** tab and a top-of-page **banner** keep
139
- them discoverable afterwards.
140
-
141
- | Extension | Source | Status | Unlocks |
142
- |---|---|---|---|
143
- | `pi-anthropic-messages` | `git@github.com:BlackBeltTechnology/pi-anthropic-messages.git` | **required** | Tool calls on Claude-model Anthropic OAuth / 9Router `cc/*` / pi-model-proxy providers. Without it, tool calls fall back to Claude Code's built-in `bash_ide` sandbox and fail. |
144
- | `@tintinweb/pi-subagents` | `npm:@tintinweb/pi-subagents` | strongly suggested | `Agent` tool card UI, subagent activity badge, `get_subagent_result` / `steer_subagent` renderers. |
145
- | `pi-flows` | `git@github.com:BlackBeltTechnology/pi-flows.git` | strongly suggested | Flow dashboard, role aliases (`@planning`, `@coding`, …), subagent / flow_write / flow_results / agent_write / ask_user / skill_read / finish tools. |
146
- | `pi-web-access` | `npm:pi-web-access` | strongly suggested | `web_search`, `code_search`, `fetch_content`, `get_search_content`. |
147
- | `pi-agent-browser` | `npm:pi-agent-browser` | optional | `browser` tool (open, snapshot, click, screenshot). |
148
-
149
- Authoritative source of truth: `packages/shared/src/recommended-extensions.ts`.
150
- Descriptions, versions, and installed-state are enriched live at runtime via
151
- `GET /api/packages/recommended` (falling back to offline descriptions on
152
- network failure).
153
-
154
- ### GitHub SSH notes
155
-
156
- The `pi-flows` and `pi-anthropic-messages` entries install via `pi install
157
- git@github.com:…` (SSH). If your system doesn't have a GitHub SSH key
158
- configured the clone will fail with a "Permission denied (publickey)"
159
- error. Set up a key by following
160
- [GitHub's SSH docs](https://docs.github.com/en/authentication/connecting-to-github-with-ssh),
161
- or substitute the equivalent HTTPS URL in the manifest if your fork is
162
- public.
163
-
164
- ### Quick test (without installing)
165
-
166
75
  To try the extension in a single pi session without registering it:
167
76
 
168
77
  ```bash
169
78
  pi -e /path/to/pi-agent-dashboard/packages/extension/src/bridge.ts
170
79
  ```
171
80
 
81
+ Remove with `pi remove /path/to/pi-agent-dashboard`. Alternatively, add the package path directly to `~/.pi/agent/settings.json` (global) or `.pi/settings.json` (project) under `"packages": [...]`.
82
+
83
+ ---
84
+
85
+ ## Features
86
+
87
+ **Sessions & chat**
88
+ - **Real-time session mirroring** — all active pi sessions with live streaming messages
89
+ - **Bidirectional interaction** — send prompts and commands from the browser
90
+ - **Session statistics** — token counts, costs, model info, thinking level, context usage bar
91
+ - **Elapsed time tracking** — live ticking counters on running operations, final duration on completed tool calls and reasoning blocks
92
+ - **Session spawning** — launch new pi sessions from the dashboard (headless by default, or via tmux)
93
+ - **On-demand session loading** — browse historical sessions with lazy-loaded content from pi session files
94
+ - **Force kill escalation** — two-click Stop button; first click sends soft abort, second force-kills (SIGTERM → SIGKILL). Session preserved as "ended" for resume/fork.
95
+
96
+ **Workspace & UI**
97
+ - **Workspace management** — organize sessions by project folder with pinned directories and drag-to-reorder
98
+ - **Command autocomplete** — `/` prefix triggers a filtering dropdown
99
+ - **Mobile-friendly** — responsive layout with swipe drawer, touch targets, and mobile action menus
100
+ - **Markdown preview** — rendered markdown views with search, mermaid diagrams, and syntax highlighting
101
+ - **Searchable select dialogs** — keyboard-navigable picker with real-time filtering (OpenSpec changes, flow commands)
102
+
103
+ **Integrations**
104
+ - **PromptBus architecture** — unified prompt routing with adapters (TUI, dashboard, custom). Interactive dialogs (confirm/select/input/editor/multiselect) survive page refresh and server restart. First-response-wins semantics with cross-adapter dismissal.
105
+ - **pi-flows integration** — live flow execution dashboard with agent cards, detail views, flow graph, summary, abort/auto controls. Launch flows and design new ones with Flow Architect, all from the browser. Fork decisions and subagent dialogs forwarded via PromptBus.
106
+ - **OpenSpec integration** — browse specs, view archive history, manage changes, create new changes from the sidebar
107
+ - **Browser-based provider auth** — sign in to Anthropic, OpenAI Codex, GitHub Copilot, Gemini CLI, and Antigravity from Settings. Enter API keys for other providers. Credentials saved to `~/.pi/agent/auth.json` and live-synced to running sessions.
108
+ - **Custom LLM providers** — add OpenAI-compatible, Anthropic-compatible, or Google Generative AI endpoints (Settings → Providers → LLM Providers). **Test** button verifies the base URL + API key before saving. Adding / editing / removing takes effect live in every running session — no restart.
109
+ - **Package management** — browse, install, update, and remove pi packages. Search the npm registry for pi-package extensions/skills/themes; install from npm or git URL. Active sessions auto-reload after changes.
110
+
111
+ **Dev tools**
112
+ - **Integrated terminal** — full browser-based terminal emulator (xterm.js + node-pty) with ANSI colors, scrollback, and keep-alive
113
+ - **Diff viewer** — side-by-side and unified diff views with file tree navigation
114
+ - **Editor integration** — open files in VS Code, Cursor, etc. directly from tool call cards
115
+
116
+ **Networking & distribution**
117
+ - **Network discovery** — mDNS-based auto-discovery of other dashboard servers on the local network
118
+ - **Zrok tunnel** — optional persistent public URL via reserved shares (see [Configuration → Tunnel](#tunnel-zrok))
119
+
120
+ ---
121
+
172
122
  ## Prerequisites
173
123
 
174
- Only needed for Option B/C (the Electron app handles everything automatically).
124
+ **Only needed for Quickstart paths B and C.** The Electron app (path A) bundles everything in standalone mode.
175
125
 
176
126
  | Requirement | Why | Install |
177
127
  |-------------|-----|---------|
178
- | **[pi](https://github.com/badlogic/pi-mono)** or **[Oh My Pi](https://www.npmjs.com/package/@oh-my-pi/pi-coding-agent)** | The AI coding agent that the dashboard monitors | `npm i -g @mariozechner/pi-coding-agent` |
179
- | **Node.js ≥ 22.18.0** | Runtime for the dashboard server (older 22.x / 24.x affected by [nodejs/node#58515](https://github.com/nodejs/node/issues/58515)) | [nodejs.org](https://nodejs.org/) |
180
- | **C++ build tools** | Required by `node-pty` native addon for terminal emulation | Xcode CLI Tools (macOS) / `build-essential` (Linux) |
128
+ | **[pi](https://github.com/badlogic/pi-mono)** | The AI coding agent the dashboard monitors | `npm i -g @mariozechner/pi-coding-agent` |
129
+ | **Node.js ≥ 22.18.0** | Server runtime. Older 22.x / 24.x < 24.3.0 are affected by [nodejs/node#58515](https://github.com/nodejs/node/issues/58515) which crashes Fastify at startup. | [nodejs.org](https://nodejs.org/) |
130
+ | **C++ build tools** | Required by `node-pty` native addon for the integrated terminal | Xcode CLI Tools (macOS) / `build-essential` (Linux) |
181
131
 
182
- ### Optional tools
132
+ Optional:
183
133
 
184
134
  | Tool | Purpose | When needed |
185
135
  |------|---------|-------------|
186
- | **tmux** | Spawn new pi sessions from the browser in a tmux window | When `spawnStrategy` is `"tmux"` |
187
- | **[zrok](https://zrok.io/)** | Expose dashboard over the internet via tunnel (auto-connects on server start). Install with `brew install zrok` (macOS) and run `zrok enable <token>` to enroll — the dashboard reads zrok's own config (`~/.zrok2/environment.json`), no keys are stored in the dashboard. Uses reserved shares for persistent URLs across restarts. | When `tunnel.enabled` is `true` (default) |
136
+ | **tmux** | Spawn new pi sessions in a tmux window | When `spawnStrategy` is `"tmux"` |
137
+ | **[zrok](https://zrok.io/)** | Public tunnel with persistent URLs | When `tunnel.enabled` is `true` (default) |
138
+
139
+ ---
188
140
 
189
141
  ## Configuration
190
142
 
191
- Config file: **`~/.pi/dashboard/config.json`** (auto-created with defaults on first run)
143
+ - **Config file:** `~/.pi/dashboard/config.json` (auto-created with defaults on first run)
144
+ - **Tool overrides (machine-local):** `~/.pi/dashboard/tool-overrides.json` — see [Tool overrides](#tool-overrides)
145
+ - **Settings UI:** click the ⚙ gear icon in the sidebar header to edit all fields from the browser
146
+
147
+ ### Precedence & keys
148
+
149
+ CLI flags → environment variables → config file → built-in defaults.
192
150
 
193
- Tool path overrides (optional, machine-local): **`~/.pi/dashboard/tool-overrides.json`** see [Tool resolution & overrides](#tool-resolution--overrides) below.
151
+ | CLI flag | Env var | Config key | Default | Description |
152
+ |----------|---------|------------|---------|-------------|
153
+ | `--port` | `PI_DASHBOARD_PORT` | `port` | `8000` | HTTP + browser WebSocket port |
154
+ | `--pi-port` | `PI_DASHBOARD_PI_PORT` | `piPort` | `9999` | Pi extension WebSocket port |
155
+ | `--dev` | — | — | `false` | Development mode (proxy to Vite) |
156
+ | `--no-tunnel` | — | `tunnel.enabled` | `true` | Disable zrok tunnel |
157
+ | — | — | `autoStart` | `true` | Bridge auto-starts server if not running |
158
+ | — | — | `autoShutdown` | `false` | Server shuts down when idle |
159
+ | — | — | `shutdownIdleSeconds` | `300` | Seconds idle before auto-shutdown |
160
+ | — | — | `spawnStrategy` | `"headless"` | Session spawn mode: `"headless"` or `"tmux"` |
161
+ | — | — | `devBuildOnReload` | `false` | Rebuild client + restart server on `/reload` |
162
+
163
+ The bridge also honours `PI_DASHBOARD_URL=ws://host:port` to point at a remote server instead of localhost.
164
+
165
+ ### Minimal `config.json`
194
166
 
195
167
  ```json
196
168
  {
@@ -211,22 +183,56 @@ Tool path overrides (optional, machine-local): **`~/.pi/dashboard/tool-overrides
211
183
  }
212
184
  ```
213
185
 
214
- **OpenSpec background polling** (`openspec` block):
186
+ ### Authentication (optional)
187
+
188
+ OAuth2 authentication guards external (tunnel) access. Localhost is always unguarded.
189
+
190
+ ```json
191
+ {
192
+ "auth": {
193
+ "secret": "auto-generated-if-omitted",
194
+ "providers": {
195
+ "github": { "clientId": "...", "clientSecret": "..." },
196
+ "google": { "clientId": "...", "clientSecret": "..." },
197
+ "keycloak": { "clientId": "...", "clientSecret": "...", "issuerUrl": "https://keycloak.example.com/realms/myrealm" }
198
+ },
199
+ "allowedUsers": ["octocat", "user@example.com", "*@company.com"]
200
+ }
201
+ }
202
+ ```
203
+
204
+ | Key | Required | Description |
205
+ |-----|----------|-------------|
206
+ | `auth.secret` | No | JWT signing secret (auto-generated if omitted) |
207
+ | `auth.providers` | Yes | Map of provider → `{ clientId, clientSecret, issuerUrl? }` |
208
+ | `auth.allowedUsers` | No | Allowlist: usernames, emails, or `*@domain` wildcards. Empty = allow all |
209
+
210
+ **Supported providers:** `github`, `google`, `keycloak`, `oidc` (generic OIDC with `issuerUrl`).
211
+
212
+ **Callback URL:** register `https://<tunnel-url>/auth/callback/<provider>` in your OAuth provider settings. The tunnel URL is stable across restarts (reserved shares are auto-created).
213
+
214
+ ### Tunnel (zrok)
215
+
216
+ The dashboard auto-connects a [zrok](https://zrok.io/) tunnel on start when `tunnel.enabled` is `true`. Install with `brew install zrok` (macOS) and run `zrok enable <token>` to enrol — the dashboard reads zrok's own config (`~/.zrok2/environment.json`), no keys are stored in the dashboard. Reserved shares provide persistent URLs across restarts.
217
+
218
+ ### OpenSpec background polling
219
+
220
+ Tune how often the server polls known directories for OpenSpec updates (`openspec` block):
215
221
 
216
222
  | Key | Default | Range | Description |
217
223
  |-----|---------|-------|-------------|
218
- | `pollIntervalSeconds` | `30` | `5–3600` | How often each known directory is polled for OpenSpec updates |
219
- | `maxConcurrentSpawns` | `3` | `1–16` | Cap on concurrent `openspec` CLI invocations across all directories |
220
- | `changeDetection` | `"mtime"` | `"mtime" \| "always"` | `mtime` skips re-polling unchanged proposals (near-zero steady-state cost); `always` polls unconditionally |
221
- | `jitterSeconds` | `5` | `0–60` | Per-directory phase offset so polls don't all align on the same tick |
224
+ | `pollIntervalSeconds` | `30` | `5–3600` | How often each known directory is polled |
225
+ | `maxConcurrentSpawns` | `3` | `1–16` | Cap on concurrent `openspec` CLI invocations |
226
+ | `changeDetection` | `"mtime"` | `"mtime" \| "always"` | `mtime` skips unchanged proposals; `always` polls unconditionally |
227
+ | `jitterSeconds` | `5` | `0–60` | Per-directory phase offset so polls don't align on the same tick |
222
228
 
223
229
  Live-reconfigurable via Settings → Advanced → "Background polling (OpenSpec)" or `PUT /api/config` — no server restart needed. See [docs/architecture.md](docs/architecture.md) for the cost model.
224
230
 
225
- ### Tool resolution & overrides
231
+ ### Tool overrides
226
232
 
227
- The dashboard resolves every external tool it calls (`pi`, `pi-coding-agent`, `openspec`, `npm`, `node`, `tsx`, `git`, `zrok`, `pi-dashboard`) through a single `ToolRegistry`. Each tool has an ordered strategy chain (override → managed install → bare-import / npm-global → PATH search), and every resolution records a diagnostic trail of which strategies were tried and why each succeeded or failed.
233
+ The dashboard resolves every external tool it calls (`pi`, `pi-coding-agent`, `openspec`, `npm`, `node`, `tsx`, `git`, `zrok`, `pi-dashboard`) through a single `ToolRegistry`. Each tool has an ordered strategy chain (override → managed install → bare-import / npm-global → PATH search), and every resolution records a diagnostic trail.
228
234
 
229
- **Inspecting and overriding** — Settings → General → **Tools** shows every resolved tool, its source, and the trail. You can set a per-tool override path, rescan a single tool or all of them, and export the full diagnostic report for bug reports.
235
+ **Inspecting and overriding** — Settings → General → **Tools** shows every resolved tool, its source, and the trail. You can set a per-tool override path, rescan individually or all at once, and export the full diagnostic report.
230
236
 
231
237
  **Overrides file** — `~/.pi/dashboard/tool-overrides.json`:
232
238
 
@@ -240,241 +246,213 @@ The dashboard resolves every external tool it calls (`pi`, `pi-coding-agent`, `o
240
246
  }
241
247
  ```
242
248
 
243
- The file is deliberately separate from `config.json` so machine-specific paths don't follow a dotfiles sync to another host. Invalid overrides (path doesn't exist) are recorded in the diagnostic trail and the registry falls through to the next strategy automatically.
244
-
245
- **Troubleshooting** — if the dashboard says it can't find `pi`, `openspec`, `npm`, or any other tool, open Settings → General → Tools, click the chevron next to the failing tool to see the full `tried[]` trail, then either (a) install the missing tool on PATH / in the managed location shown in the trail, or (b) set an explicit override via the row's path input. Hit **Rescan** to pick up the change without a server restart.
249
+ The file is deliberately separate from `config.json` so machine-specific paths don't follow a dotfiles sync. Invalid overrides (path doesn't exist) are recorded in the trail and the registry falls through to the next strategy automatically.
246
250
 
247
- **Troubleshooting: sessions don't group under my pinned folder** — since v0.3+, session grouping uses OS-aware path equality (`platform/paths.ts`). Sessions group correctly under a pinned folder even when the path stored on pin and the path reported by the session differ in trailing separator, separator style, or case (on Windows and macOS). If you still see two entries for what should be one folder, the paths are likely on different Windows drives (`A:\Foo` and `B:\Foo` are different filesystems and never merge) — that's correct behavior, not a bug. If the paths really are the same filesystem, file an issue with both the pinned path (visible in Settings → Tools → Export diagnostics) and the session `cwd` as reported in `/api/sessions`.
251
+ ---
248
252
 
249
- ### Windows session durability
253
+ ## Usage
250
254
 
251
- **Behavior change**: Since the `consolidate-windows-spawn-and-platform-handlers` release, pi sessions on Windows now **survive dashboard server restart**, matching macOS/Linux behavior. Previously, killing or restarting the dashboard process (Task Manager, Ctrl+C, `/api/restart`, crash) would terminate every running pi session because the children were in the server's libuv kill-on-close Job Object. The fix uses `detached: true` so children are excluded from the parent's job — the Windows equivalent of Unix PGID detachment.
255
+ ### Auto-start (default)
252
256
 
253
- If you previously relied on "closing the dashboard cleans everything up," use the per-session **Force Kill** action instead (or `POST /api/session/:id/force-kill` via the REST API).
257
+ The bridge extension **automatically starts the dashboard server** when pi launches if it's not already running. Disable with `"autoStart": false` in `~/.pi/dashboard/config.json`.
254
258
 
255
- **Recommended: install Windows Terminal** (`wt.exe`) for tabbed interactive sessions on Windows 10/11. The dashboard prefers `wt` when available (new tab in an existing WT window, respects your default profile — cmd / PowerShell / WSL / whatever), and falls back to WSL tmux and then headless mode when absent. Windows Terminal is bundled with Windows 11; on Windows 10 install it from the Microsoft Store (search for "Windows Terminal").
259
+ ### Daemon mode
256
260
 
257
- **Troubleshooting: Windows Terminal tab doesn't appear** — if `wt.exe` is on PATH but launching does nothing, check Settings → Apps → Advanced app settings → App execution aliases. If the "wt" alias is disabled, `wt.exe` is found but can't be executed. Enable the alias or uninstall/reinstall Windows Terminal.
261
+ ```bash
262
+ pi-dashboard start # background daemon (production)
263
+ pi-dashboard start --dev # dev mode (proxy to Vite, fallback to production build)
264
+ pi-dashboard stop # stop daemon (also kills stale port holders)
265
+ pi-dashboard restart # restart (production)
266
+ pi-dashboard restart --dev # restart in dev mode
267
+ pi-dashboard status # daemon status
268
+ ```
258
269
 
259
- ### Authentication (Optional)
270
+ Daemon stdout/stderr is logged to `~/.pi/dashboard/server.log` (append mode with timestamped headers per start).
260
271
 
261
- Add an `auth` section to enable OAuth2 authentication for external (tunnel) access. Localhost is always unguarded.
272
+ ### Manual server start
262
273
 
263
- ```json
264
- {
265
- "auth": {
266
- "secret": "auto-generated-if-omitted",
267
- "providers": {
268
- "github": {
269
- "clientId": "your-github-client-id",
270
- "clientSecret": "your-github-client-secret"
271
- },
272
- "google": {
273
- "clientId": "your-google-client-id",
274
- "clientSecret": "your-google-client-secret"
275
- },
276
- "keycloak": {
277
- "clientId": "your-keycloak-client-id",
278
- "clientSecret": "your-keycloak-client-secret",
279
- "issuerUrl": "https://keycloak.example.com/realms/myrealm"
280
- }
281
- },
282
- "allowedUsers": ["octocat", "user@example.com", "*@company.com"]
283
- }
284
- }
274
+ ```bash
275
+ npx tsx packages/server/src/cli.ts
276
+ npx tsx packages/server/src/cli.ts --port 8000 --pi-port 9999
277
+ npx tsx packages/server/src/cli.ts --dev # proxy to Vite dev server
285
278
  ```
286
279
 
287
- | Key | Required | Description |
288
- |-----|----------|-------------|
289
- | `auth.secret` | No | JWT signing secret (auto-generated if omitted) |
290
- | `auth.providers` | Yes | Map of provider name → `{ clientId, clientSecret, issuerUrl? }` |
291
- | `auth.allowedUsers` | No | User allowlist: usernames, emails, or `*@domain` wildcards. Empty = allow all |
280
+ ### Graceful restart via API
292
281
 
293
- **Supported providers:** `github`, `google`, `keycloak`, `oidc` (generic OIDC with `issuerUrl`).
282
+ ```bash
283
+ # Restart in the same mode
284
+ curl -X POST http://localhost:8000/api/restart
294
285
 
295
- **Callback URL:** Register `https://<tunnel-url>/auth/callback/<provider>` in your OAuth provider settings. The tunnel URL is stable across restarts (reserved shares are auto-created).
286
+ # Switch to dev / production
287
+ curl -X POST http://localhost:8000/api/restart -H 'Content-Type: application/json' -d '{"dev":true}'
288
+ curl -X POST http://localhost:8000/api/restart -H 'Content-Type: application/json' -d '{"dev":false}'
296
289
 
297
- **Settings UI:** Click the ⚙ gear icon in the sidebar header to open the Settings panel, where all config fields (including auth) can be edited from the browser.
290
+ # Check current mode
291
+ curl -s http://localhost:8000/api/health | jq .mode
292
+ ```
298
293
 
299
- **Precedence:** CLI flags environment variables config file built-in defaults.
294
+ The restart endpoint waits for the old server to exit, starts the new one, and verifies health. It works identically on Windows, macOS, and Linux (no `sh` / `lsof` / `curl` dependency).
300
295
 
301
- | CLI Flag | Env Var | Config Key | Default | Description |
302
- |----------|---------|------------|---------|-------------|
303
- | `--port` | `PI_DASHBOARD_PORT` | `port` | `8000` | HTTP + Browser WebSocket port |
304
- | `--pi-port` | `PI_DASHBOARD_PI_PORT` | `piPort` | `9999` | Pi extension WebSocket port |
305
- | `--dev` | — | — | `false` | Development mode (proxy to Vite) |
306
- | `--no-tunnel` | — | `tunnel.enabled` | `true` | Disable zrok tunnel |
307
- | — | — | `autoStart` | `true` | Bridge auto-starts server if not running |
308
- | — | — | `autoShutdown` | `false` | Server shuts down when idle |
309
- | — | — | `shutdownIdleSeconds` | `300` | Seconds idle before auto-shutdown |
310
- | — | — | `spawnStrategy` | `"headless"` | Session spawn mode: `"headless"` or `"tmux"` |
311
- | — | — | `devBuildOnReload` | `false` | Rebuild client + restart server on `/reload` |
296
+ ### Dev mode with production fallback
312
297
 
313
- ### Override the server URL
298
+ When started with `--dev`, the server proxies client requests to the Vite dev server for HMR. If Vite is **not running**, it automatically falls back to serving the production build from `dist/client/`:
314
299
 
315
- By default the bridge connects to `ws://localhost:{piPort}`. To point at a remote server:
300
+ - `pi-dashboard start --dev` **always works** no 502 errors
301
+ - Start/stop Vite independently without restarting the dashboard
302
+ - Start Vite later and refresh the browser to get HMR
316
303
 
317
- ```bash
318
- PI_DASHBOARD_URL=ws://192.168.1.100:9999 pi
304
+ ### Dev build on reload
305
+
306
+ Set `"devBuildOnReload": true` in `config.json` for a one-command full-stack refresh:
307
+
308
+ ```
309
+ /reload → build client → stop server → reload extension → auto-start fresh server
319
310
  ```
320
311
 
321
- ## Installation Methods
312
+ > Blocks pi for ~2–5s during the build. The server shutdown affects all connected sessions — they auto-reconnect when one restarts the server.
322
313
 
323
- ### Electron Desktop App (standalone)
314
+ ### Session spawning
324
315
 
325
- See [Getting Started Option A](#option-a-electron-desktop-app-standalone--no-prerequisites) above. Download from [GitHub Releases](https://github.com/BlackBeltTechnology/pi-agent-dashboard/releases).
316
+ **Headless** (default)runs pi as a background process with no terminal attached. Interaction through the web UI.
326
317
 
327
- ### From npm (recommended for CLI users)
318
+ **tmux** runs pi inside a tmux session named `pi-dashboard`, each spawned session as a new window:
328
319
 
329
320
  ```bash
330
- # pi
331
- pi install npm:@blackbelt-technology/pi-agent-dashboard
332
-
333
- # Oh My Pi
334
- omp install npm:@blackbelt-technology/pi-agent-dashboard
321
+ tmux attach -t pi-dashboard # attach
322
+ tmux list-windows -t pi-dashboard # list windows
323
+ # inside tmux: Ctrl-b n / p / w # next / prev / picker
335
324
  ```
336
325
 
337
- > The package is compatible with both [pi](https://github.com/badlogic/pi-mono) and [Oh My Pi](https://www.npmjs.com/package/@oh-my-pi/pi-coding-agent) — no configuration needed.
326
+ Switch with `"spawnStrategy": "tmux"` in `~/.pi/dashboard/config.json`.
338
327
 
339
- ### Local development install
328
+ ### Keyboard shortcuts in chat input
340
329
 
341
- ```bash
342
- cd /path/to/pi-agent-dashboard
343
- npm install
330
+ Bash-style history recall and per-session draft persistence:
344
331
 
345
- # Global install
346
- pi install /path/to/pi-agent-dashboard
332
+ | Key | Action |
333
+ |-----|--------|
334
+ | `Enter` | Send the prompt |
335
+ | `Shift+Enter` | Insert a newline |
336
+ | `ArrowUp` | Recall previous user prompt (caret on first line, no dropdown open). Repeat to walk back |
337
+ | `ArrowDown` | Walk forward through history (caret on last line). Past the newest entry, restores the in-progress draft |
338
+ | `Escape` | Restore in-progress draft and exit history mode; also cancels pending prompt / dismisses dropdown |
339
+ | `Tab` / `Enter` in dropdown | Accept the highlighted `/command` or `@file` suggestion |
347
340
 
348
- # Or project-local only
349
- pi install -l /path/to/pi-agent-dashboard
350
- ```
341
+ Drafts (typed-but-unsent text) are persisted per session in `localStorage` under `chat-draft:<sessionId>` and survive navigation (Settings, OpenSpec preview, diffs, …) and full page reloads. Drafts never leak between sessions.
351
342
 
352
- Pi reads the `pi.extensions` field from `package.json` and loads the bridge extension automatically.
343
+ ---
353
344
 
354
- ### Manual settings entry
345
+ ## Recommended extensions
355
346
 
356
- Add the package path directly to your settings file:
347
+ The dashboard integrates tightly with a small, curated set of pi extensions — for custom tool rendering, the Flow dashboard, and anthropic-messages protocol compatibility. The Electron wizard installs them in one go; the **Packages** tab and a top-of-page banner keep them discoverable afterwards.
357
348
 
358
- **Global** (`~/.pi/agent/settings.json`):
359
- ```json
360
- {
361
- "packages": ["/path/to/pi-agent-dashboard"]
362
- }
363
- ```
349
+ | Extension | Source | Status | Unlocks |
350
+ |---|---|---|---|
351
+ | `pi-anthropic-messages` | `git@github.com:BlackBeltTechnology/pi-anthropic-messages.git` | **required** | Tool calls on Claude-model Anthropic OAuth / 9Router `cc/*` / pi-model-proxy providers. Without it, tool calls fall back to Claude Code's built-in `bash_ide` sandbox and fail. |
352
+ | `@tintinweb/pi-subagents` | `npm:@tintinweb/pi-subagents` | strongly suggested | `Agent` tool card UI, subagent activity badge, `get_subagent_result` / `steer_subagent` renderers |
353
+ | `pi-flows` | `git@github.com:BlackBeltTechnology/pi-flows.git` | strongly suggested | Flow dashboard, role aliases (`@planning`, `@coding`, …), subagent / flow_write / flow_results / agent_write / ask_user / skill_read / finish tools |
354
+ | `pi-web-access` | `npm:pi-web-access` | strongly suggested | `web_search`, `code_search`, `fetch_content`, `get_search_content` |
355
+ | `pi-agent-browser` | `npm:pi-agent-browser` | optional | `browser` tool (open, snapshot, click, screenshot) |
364
356
 
365
- **Project-local** (`.pi/settings.json`):
366
- ```json
367
- {
368
- "packages": ["/path/to/pi-agent-dashboard"]
369
- }
370
- ```
357
+ Authoritative source: `packages/shared/src/recommended-extensions.ts`. Descriptions, versions, and installed-state are enriched live via `GET /api/packages/recommended` (offline fallback).
371
358
 
372
- ### Removing
359
+ ---
373
360
 
374
- ```bash
375
- pi remove /path/to/pi-agent-dashboard
376
- ```
361
+ ## Troubleshooting
377
362
 
378
- ## Usage
363
+ ### Dashboard server doesn't start
379
364
 
380
- ### Auto-start (default)
365
+ If `pi` launches but the dashboard never becomes reachable, inspect the launch log:
381
366
 
382
- The bridge extension **automatically starts the dashboard server** when pi launches if it's not already running. No separate terminal needed.
367
+ ```bash
368
+ cat ~/.pi/dashboard/server.log # Linux / macOS
369
+ type %USERPROFILE%\.pi\dashboard\server.log # Windows
370
+ ```
383
371
 
384
- To disable: set `"autoStart": false` in `~/.pi/dashboard/config.json`.
372
+ The log is append-mode with timestamped headers per start attempt, so previous crashes are preserved. Common issues:
385
373
 
386
- ### Manual server start
374
+ - **`ERR_UNSUPPORTED_ESM_URL_SCHEME` on Windows** — fully fixed in 0.4.0+. The 0.2.10 release wrapped the `--import` loader position as a `file://` URL, but the entry-script position stayed a raw Windows path — which crashed Node on non-`C:` drives (`A:\`, `B:\`, …) because the drive-letter heuristic has gaps there. 0.4.0 routes all four server-spawn call sites through a single `spawnNodeScript` / `toFileUrl` helper that wraps both positions unconditionally, and a repo-level lint test prevents regression. Upgrade the package.
375
+ - **`Cannot find pi's TypeScript loader`** — pi is not installed globally. Run `npm install -g @mariozechner/pi-coding-agent`.
376
+ - **Fastify crash at startup** — you're on Node 22.0.0–22.17.x or 24.1.0–24.2.x which are affected by [nodejs/node#58515](https://github.com/nodejs/node/issues/58515). Upgrade to 22.18+ or 24.3+.
387
377
 
388
- ```bash
389
- npx tsx packages/server/src/cli.ts
390
- npx tsx packages/server/src/cli.ts --port 8000 --pi-port 9999
391
- npx tsx packages/server/src/cli.ts --dev # proxy to Vite dev server
392
- ```
378
+ ### Port already in use
393
379
 
394
- ### Daemon mode
380
+ - **Windows:** `netstat -ano | findstr :8000` then `taskkill /F /PID <pid>`
381
+ - **Unix:** `lsof -t -i :8000 | xargs kill`
382
+ - Or change `port` in `~/.pi/dashboard/config.json`.
395
383
 
396
- ```bash
397
- pi-dashboard start # Start as background daemon (production)
398
- pi-dashboard start --dev # Start in dev mode (proxy to Vite, fallback to production build)
399
- pi-dashboard stop # Stop running daemon (also kills stale port holders)
400
- pi-dashboard restart # Restart daemon (production)
401
- pi-dashboard restart --dev # Restart in dev mode
402
- pi-dashboard status # Show daemon status
403
- ```
384
+ ### UI is empty or stuck after switching servers
404
385
 
405
- Daemon stdout/stderr is logged to `~/.pi/dashboard/server.log` for crash diagnosis.
386
+ Since the `safe-server-switch` release, switching servers via the header dropdown is **transactional**: the UI verifies the target through a short-lived staging WebSocket (5 s timeout) **before** clearing state or writing `localStorage`. If the target is unreachable, nothing changes — a toast appears and you stay on the previous server.
406
387
 
407
- ### Keyboard shortcuts in chat input
388
+ If the currently-active server drops for more than 3 s, a yellow **“Disconnected from \<host\>. Retrying…”** banner appears at the top with a **Switch server** button — use it to pick a reachable server.
408
389
 
409
- The chat input supports bash-style history recall and per-session draft persistence:
390
+ You should no longer need to manually `localStorage.removeItem("pi-dashboard-last-server")` to recover from a bad switch. If you still get stuck, please file an issue.
410
391
 
411
- | Key | Action |
412
- |-----|--------|
413
- | `Enter` | Send the prompt. |
414
- | `Shift+Enter` | Insert a newline. |
415
- | `ArrowUp` | Recall the previous user prompt (only when the caret is on the first line and no autocomplete dropdown is open). Repeat to walk further back. |
416
- | `ArrowDown` | Walk forward through history (only when the caret is on the last line). Past the newest entry, restores the in-progress draft. |
417
- | `Escape` | While navigating history, restore the in-progress draft and exit history mode. Also cancels a pending prompt or dismisses the autocomplete dropdown. |
418
- | `Tab` / `Enter` in dropdown | Accept the highlighted `/command` or `@file` suggestion. |
392
+ ### Windows: sessions die when the dashboard restarts
419
393
 
420
- Drafts (typed-but-unsent text) are persisted per session in `localStorage` under `chat-draft:<sessionId>` and survive navigation (Settings, OpenSpec preview, file diffs, ...) as well as full page reloads. Drafts never leak between sessions.
394
+ Since the `consolidate-windows-spawn-and-platform-handlers` release, pi sessions on Windows **survive dashboard restart**, matching macOS/Linux behaviour. Previously, killing the dashboard process (Task Manager, Ctrl+C, `/api/restart`, crash) terminated every running pi session because the children were in the server's libuv kill-on-close Job Object. The fix uses `detached: true` so children are excluded from the parent's job.
421
395
 
422
- ### Graceful restart via API
396
+ If you previously relied on "closing the dashboard cleans everything up," use the per-session **Force Kill** action instead (or `POST /api/session/:id/force-kill`).
423
397
 
424
- Restart without CLI useful from scripts, other sessions, or the dashboard skill:
398
+ ### Windows Terminal tab doesn't appear
425
399
 
426
- ```bash
427
- # Restart in same mode (preserves current dev/prod)
428
- curl -X POST http://localhost:8000/api/restart
400
+ Install Windows Terminal (`wt.exe`) for tabbed interactive sessions — the dashboard prefers it over WSL tmux / headless when available. Windows 11 ships with it; on Windows 10 install from the Microsoft Store.
429
401
 
430
- # Switch to dev mode
431
- curl -X POST http://localhost:8000/api/restart -H 'Content-Type: application/json' -d '{"dev":true}'
402
+ If `wt.exe` is on PATH but launching does nothing, check **Settings → Apps → Advanced app settings → App execution aliases**. If the "wt" alias is disabled, `wt.exe` is found but can't be executed. Enable the alias or uninstall/reinstall Windows Terminal.
432
403
 
433
- # Switch to production mode
434
- curl -X POST http://localhost:8000/api/restart -H 'Content-Type: application/json' -d '{"dev":false}'
404
+ ### Sessions don't group under my pinned folder
435
405
 
436
- # Check current mode
437
- curl -s http://localhost:8000/api/health | jq .mode
438
- ```
406
+ Since v0.3+, session grouping uses OS-aware path equality (`platform/paths.ts`). Sessions group correctly under a pinned folder even across trailing-separator, separator-style, or case differences (on Windows and macOS).
439
407
 
440
- The restart endpoint waits for the old server to exit, starts the new one, and verifies health. If the new server fails to start, the error is logged to `server.log`.
408
+ If you still see two entries for what should be one folder, the paths are likely on different Windows drives (`A:\Foo` and `B:\Foo` are different filesystems and never merge) — that's correct behaviour. If the paths really are the same filesystem, file an issue with both the pinned path (Settings Tools → Export diagnostics) and the session `cwd` from `/api/sessions`.
441
409
 
442
- ### Dev mode with production fallback
410
+ ### Tool not found (pi / openspec / npm / …)
443
411
 
444
- When started with `--dev`, the server proxies client requests to the Vite dev server for HMR. If Vite is **not running**, the server automatically falls back to serving the production build from `dist/client/`. This means:
412
+ Open **Settings General → Tools**, click the chevron next to the failing tool to see the full `tried[]` trail, then either (a) install the missing tool on PATH / in the managed location shown in the trail, or (b) set an explicit override via the row's path input. Hit **Rescan** to pick up the change without a server restart.
445
413
 
446
- - `pi-dashboard start --dev` **always works** — no 502 errors
447
- - Start/stop Vite independently without restarting the dashboard
448
- - Seamless transition: start Vite later and refresh the browser to get HMR
414
+ ### Recommended extensions: "Permission denied (publickey)"
449
415
 
450
- ### Session spawning
416
+ `pi-flows` and `pi-anthropic-messages` install via SSH (`git@github.com:…`). If your system has no GitHub SSH key, set one up following [GitHub's SSH docs](https://docs.github.com/en/authentication/connecting-to-github-with-ssh), or substitute the equivalent HTTPS URL in the manifest if your fork is public.
451
417
 
452
- The dashboard can spawn new pi sessions from the browser. Two strategies are available:
418
+ ---
453
419
 
454
- **Headless** (default) — Runs pi as a background process with no terminal attached. Interaction happens entirely through the dashboard web UI.
420
+ ## Architecture
455
421
 
456
- **tmux** — Runs pi inside a tmux session named `pi-dashboard`. Each spawned session opens as a new tmux window. This lets you attach to the terminal when needed:
422
+ ```mermaid
423
+ graph LR
424
+ subgraph "Per pi session"
425
+ B[Bridge Extension]
426
+ end
457
427
 
458
- ```bash
459
- # Attach to the pi-dashboard tmux session
460
- tmux attach -t pi-dashboard
428
+ subgraph "Dashboard Server (Node.js)"
429
+ PG[Pi Gateway :9999]
430
+ BG[Browser Gateway :8000]
431
+ HTTP[HTTP / Static Files]
432
+ MEM[(In-Memory Store)]
433
+ JSON[(JSON Files)]
434
+ end
461
435
 
462
- # List all windows (each is a spawned pi session)
463
- tmux list-windows -t pi-dashboard
436
+ subgraph "Browser"
437
+ UI[React Web Client]
438
+ end
464
439
 
465
- # Switch between windows inside tmux
466
- Ctrl-b n # next window
467
- Ctrl-b p # previous window
468
- Ctrl-b w # interactive window picker
440
+ B <-->|WebSocket| PG
441
+ UI <-->|WebSocket| BG
442
+ UI -->|HTTP| HTTP
443
+ PG --- MEM
444
+ PG --- JSON
445
+ BG --- MEM
469
446
  ```
470
447
 
471
- To switch strategy, set `spawnStrategy` in `~/.pi/dashboard/config.json`:
448
+ | Component | Location | Role |
449
+ |-----------|----------|------|
450
+ | **Bridge Extension** | `packages/extension/` | Runs in every pi session. Forwards events, relays commands, auto-starts server, hosts PromptBus. |
451
+ | **Dashboard Server** | `packages/server/` | Aggregates events in-memory, persists metadata to JSON, serves the web client, manages terminals. |
452
+ | **Web Client** | `packages/client/` | React + Tailwind UI with real-time WebSocket updates. |
453
+ | **Shared** | `packages/shared/` | TypeScript types, protocols, and utilities shared across all packages. |
472
454
 
473
- ```json
474
- {
475
- "spawnStrategy": "tmux"
476
- }
477
- ```
455
+ See [docs/architecture.md](docs/architecture.md) for detailed data flows, reconnection logic, and persistence model.
478
456
 
479
457
  ### Auto-start flow
480
458
 
@@ -493,15 +471,25 @@ flowchart TD
493
471
 
494
472
  The server is spawned detached (`child_process.spawn` with `detached: true`, `unref()`), so it outlives the pi session. Duplicate spawn attempts from concurrent pi sessions fail harmlessly with `EADDRINUSE`.
495
473
 
496
- ### Dev build on reload
474
+ ---
497
475
 
498
- Set `"devBuildOnReload": true` in `config.json` for a one-command full-stack refresh:
476
+ ## Monitoring
499
477
 
478
+ The health endpoint provides server and agent process metrics:
479
+
480
+ ```bash
481
+ curl -s http://localhost:8000/api/health | jq
500
482
  ```
501
- /reload → build client → stop server → reload extension → auto-start fresh server
502
- ```
503
483
 
504
- > **Note:** Blocks pi for ~2–5s during the build. The server shutdown affects all connected sessions — they auto-reconnect when one restarts the server.
484
+ Returns:
485
+ - `mode` — `"dev"` or `"production"`
486
+ - `server.rss`, `server.heapUsed`, `server.heapTotal` — server memory
487
+ - `server.activeSessions`, `server.totalSessions` — session counts
488
+ - `agents[]` — per-agent metrics (CPU%, RSS, heap, event loop max delay, system load)
489
+
490
+ Agent metrics are collected every 15s via heartbeats and include `eventLoopMaxMs` — useful for diagnosing connection drops during long-running operations.
491
+
492
+ ---
505
493
 
506
494
  ## Development
507
495
 
@@ -536,8 +524,6 @@ pi -e packages/extension/src/bridge.ts # or just `pi` if installed
536
524
 
537
525
  ### Deploy after changes
538
526
 
539
- The `pi-dashboard` command is available globally when the package is installed. After making changes, restart the appropriate components:
540
-
541
527
  ```bash
542
528
  # After client changes (production mode)
543
529
  npm run build
@@ -547,142 +533,15 @@ curl -X POST http://localhost:8000/api/restart
547
533
  curl -X POST http://localhost:8000/api/restart
548
534
 
549
535
  # After bridge extension changes
550
- npm run reload # Reload all connected pi sessions
536
+ npm run reload
551
537
 
552
538
  # Full rebuild (e.g., after pulling updates)
553
539
  npm run build
554
540
  curl -X POST http://localhost:8000/api/restart
555
541
  npm run reload
556
-
557
- # Switch between dev and production mode
558
- curl -X POST http://localhost:8000/api/restart -H 'Content-Type: application/json' -d '{"dev":true}'
559
- curl -X POST http://localhost:8000/api/restart -H 'Content-Type: application/json' -d '{"dev":false}'
560
- ```
561
-
562
- ### Project Structure
563
-
564
- The project is a monorepo with npm workspaces:
565
-
566
- ```
567
- packages/
568
- ├── shared/ # Shared TypeScript types & utilities
569
- │ └── src/
570
- │ ├── protocol.ts # Extension ↔ Server messages
571
- │ ├── browser-protocol.ts # Server ↔ Browser messages (incl. PromptBus types)
572
- │ ├── types.ts # Data models
573
- │ ├── config.ts # Shared config loader
574
- │ ├── rest-api.ts # REST API types
575
- │ ├── session-meta.ts # Session metadata sidecar (.meta.json) read/write
576
- │ ├── state-replay.ts # Event synthesis on reconnect
577
- │ ├── stats-extractor.ts # Token/cost stats extraction
578
- │ ├── server-identity.ts # Server detection & identity
579
- │ ├── mdns-discovery.ts # mDNS network auto-discovery
580
- │ └── openspec-poller.ts # OpenSpec change data polling
581
- ├── extension/ # Bridge extension (runs in pi)
582
- │ └── src/
583
- │ ├── bridge.ts # Main extension entry
584
- │ ├── connection.ts # WebSocket with reconnection
585
- │ ├── event-forwarder.ts # Event mapping
586
- │ ├── flow-event-wiring.ts # pi-flows event forwarding
587
- │ ├── prompt-bus.ts # PromptBus — unified prompt routing with adapters
588
- │ ├── dashboard-default-adapter.ts # Default PromptBus adapter for dashboard UI
589
- │ ├── prompt-expander.ts # Prompt template expansion from disk
590
- │ ├── provider-register.ts # Provider auth registration & sync
591
- │ ├── model-tracker.ts # Model selection tracking
592
- │ ├── source-detector.ts # Session source detection (via .meta.json sidecar)
593
- │ ├── command-handler.ts # Command relay
594
- │ ├── server-probe.ts # TCP probe for server detection
595
- │ ├── server-auto-start.ts # Auto-start logic on session launch
596
- │ ├── server-launcher.ts # Spawn server as detached process
597
- │ ├── session-sync.ts # Session history sync
598
- │ ├── process-metrics.ts # Agent process CPU/memory metrics
599
- │ ├── process-scanner.ts # Running process detection
600
- │ ├── git-info.ts # Git branch/remote/PR detection
601
- │ ├── git-link-builder.ts # GitHub/GitLab permalink generation
602
- │ └── dev-build.ts # Dev build-on-reload helper
603
- ├── server/ # Dashboard server
604
- │ └── src/
605
- │ ├── cli.ts # CLI entry (start/stop/restart/status)
606
- │ ├── server.ts # HTTP + WebSocket server
607
- │ ├── pi-gateway.ts # Extension WebSocket gateway
608
- │ ├── browser-gateway.ts # Browser WebSocket gateway
609
- │ ├── memory-event-store.ts # In-memory event buffer (LRU, per-session cap, truncation)
610
- │ ├── memory-session-manager.ts # In-memory session registry
611
- │ ├── preferences-store.ts # User prefs: hidden sessions, pinned dirs
612
- │ ├── meta-persistence.ts # Session metadata persistence
613
- │ ├── session-order-manager.ts # Per-cwd session ordering
614
- │ ├── session-discovery.ts # Session file scanning & loading
615
- │ ├── process-manager.ts # tmux/headless session spawning
616
- │ ├── headless-pid-registry.ts # Track headless process PIDs
617
- │ ├── editor-registry.ts # Available editor detection
618
- │ ├── editor-manager.ts # Editor launch & file opening
619
- │ ├── provider-auth-storage.ts # Provider credential persistence
620
- │ ├── provider-auth-handlers.ts # OAuth flow handlers
621
- │ ├── npm-search-proxy.ts # npm registry search proxy
622
- │ ├── package-manager-wrapper.ts # pi package install/remove
623
- │ ├── pending-fork-registry.ts # Flow fork decision persistence
624
- │ ├── tunnel.ts # Zrok tunnel with reserved shares
625
- │ ├── terminal-manager.ts # Browser terminal sessions (xterm.js + node-pty)
626
- │ ├── server-pid.ts # PID file for daemon management
627
- │ ├── auth.ts # OAuth2 authentication
628
- │ └── json-store.ts # Atomic JSON file helpers
629
- ├── client/ # React web client
630
- │ └── src/
631
- │ ├── App.tsx
632
- │ ├── hooks/ # WebSocket hooks, mobile detection
633
- │ ├── lib/ # Event reducer, command filter
634
- │ └── components/ # UI components
635
- │ ├── FlowDashboard.tsx # Live flow execution view
636
- │ ├── FlowAgentCard.tsx # Per-agent status cards
637
- │ ├── FlowGraph.tsx # DAG visualization
638
- │ ├── FlowArchitect.tsx # Flow designer UI
639
- │ ├── FlowSummary.tsx # Post-flow result summary
640
- │ ├── DiffView.tsx # Side-by-side diff viewer
641
- │ ├── TerminalView.tsx # Browser terminal emulator
642
- │ ├── PackageBrowser.tsx # Package search & install
643
- │ ├── ProviderAuthSection.tsx # Provider sign-in UI
644
- │ ├── SettingsPanel.tsx # Config editor
645
- │ └── ... # 80+ components
646
- └── electron/ # Electron desktop app wrapper
647
- ├── src/main.ts
648
- ├── scripts/
649
- └── resources/
650
- ```
651
-
652
- ## Monitoring
653
-
654
- The health endpoint provides server and agent process metrics:
655
-
656
- ```bash
657
- curl -s http://localhost:8000/api/health | jq
658
542
  ```
659
543
 
660
- Returns:
661
- - `mode` — `"dev"` or `"production"`
662
- - `server.rss`, `server.heapUsed`, `server.heapTotal` — server memory
663
- - `server.activeSessions`, `server.totalSessions` — session counts
664
- - `agents[]` — per-agent metrics (CPU%, RSS, heap, event loop max delay, system load)
665
-
666
- Agent metrics are collected every 15s via heartbeats and include `eventLoopMaxMs` — useful for diagnosing connection drops during long-running operations.
667
-
668
- ### Troubleshooting: dashboard server doesn't start
669
-
670
- If `pi` launches but the dashboard never becomes reachable (no `http://localhost:8000`, no `🌐 Dashboard started` notification), inspect the launch log:
671
-
672
- ```bash
673
- cat ~/.pi/dashboard/server.log # Linux / macOS
674
- type %USERPROFILE%\.pi\dashboard\server.log # Windows
675
- ```
676
-
677
- The log is opened in append mode and prefixed with a timestamped header on every start attempt, so previous crashes are preserved. Common issues:
678
-
679
- - **`ERR_UNSUPPORTED_ESM_URL_SCHEME` on Windows** — fixed in pi-agent-dashboard 0.2.10+; upgrade the package.
680
- - **`Port 8000 is occupied by another service`** — another process is bound to the port. On Windows: `netstat -ano | findstr :8000` then `taskkill /F /PID <pid>`. On Unix: `lsof -t -i :8000 | xargs kill`. Or change `port` in `~/.pi/dashboard/config.json`.
681
- - **`Cannot find pi's TypeScript loader`** — pi is not installed globally. Run `npm install -g @mariozechner/pi-coding-agent`.
682
-
683
- The `POST /api/restart` endpoint and `pi-dashboard restart` command work identically on Windows, macOS, and Linux (no `sh`/`lsof`/`curl` dependency).
684
-
685
- ## Extension UI Events
544
+ ### Extension UI events
686
545
 
687
546
  Your own extensions can broadcast UI events to the dashboard:
688
547
 
@@ -696,40 +555,40 @@ pi.events.emit("dashboard:ui", {
696
555
 
697
556
  Supported methods: `confirm`, `select`, `input`, `notify`.
698
557
 
699
- ## Electron Desktop App
700
-
701
- The project includes an Electron wrapper at `packages/electron/` that bundles the dashboard as a **fully standalone native desktop app**. Pre-built installers for all platforms are available on [GitHub Releases](https://github.com/BlackBeltTechnology/pi-agent-dashboard/releases) — see [Getting Started](#getting-started) above.
702
-
703
- The Electron app supports two modes:
558
+ ### Project structure
704
559
 
705
- | Mode | Description |
706
- |------|-------------|
707
- | **Standalone** | Bundles Node.js, auto-installs pi + dashboard into `~/.pi-dashboard/`. Zero prerequisites. |
708
- | **Power User** | Detects and uses your existing system pi + dashboard install. |
560
+ Monorepo with npm workspaces — top-level layout only. See [AGENTS.md](AGENTS.md) for the full file-by-file index.
709
561
 
710
- Features: first-run setup wizard, auto-update checker, system tray, splash screen, VM detection (disables GPU acceleration), mDNS server discovery, and version compatibility checks.
562
+ ```
563
+ packages/
564
+ ├── shared/ # Shared TypeScript types, protocols, config, session-meta helpers
565
+ ├── extension/ # Bridge extension (runs inside pi) — WS client, PromptBus, event forwarding, auto-start
566
+ ├── server/ # Dashboard server — HTTP + dual WebSocket gateways, in-memory store, terminals, auth, tunnel
567
+ ├── client/ # React + Tailwind web client — 80+ components, hooks, event reducer
568
+ └── electron/ # Electron desktop wrapper — wizard, system tray, auto-update, bundled Node.js
569
+ ```
711
570
 
712
- ### Building from Source
571
+ ---
713
572
 
714
- > **Prerequisites for building:** Node.js 22.12+, platform-specific tools handled by Electron Forge automatically.
573
+ ## Building the Electron app
715
574
 
716
- ### Building for Your Platform
575
+ > **Prerequisites:** Node.js 22.12+; platform-specific tools handled by Electron Forge automatically.
717
576
 
718
- The easiest way — one command that handles everything (client build, Node.js bundling, installer creation):
577
+ ### Native build (current platform)
719
578
 
720
579
  ```bash
721
- npm run electron:build # Build for current platform & arch
722
- npm run electron:build -- --arch x64 # Override architecture
723
- npm run electron:build -- --skip-client # Skip client rebuild
580
+ npm run electron:build # Build for current platform & arch
581
+ npm run electron:build -- --arch x64 # Override architecture
582
+ npm run electron:build -- --skip-client # Skip client rebuild
724
583
  ```
725
584
 
726
585
  Or step by step:
727
586
 
728
587
  ```bash
729
- npm run build # Build web client
588
+ npm run build # Build web client
730
589
  cd packages/electron
731
- bash scripts/download-node.sh # Download Node.js for bundling
732
- npm run make # Build installer
590
+ bash scripts/download-node.sh # Download Node.js for bundling
591
+ npm run make # Build installer
733
592
  ```
734
593
 
735
594
  Output by platform:
@@ -738,25 +597,22 @@ Output by platform:
738
597
  |----------|--------|----------|
739
598
  | macOS | `.dmg` | `packages/electron/out/make/` |
740
599
  | Linux | `.deb` + `.AppImage` | `packages/electron/out/make/` |
741
- | Windows | `.exe` (NSIS installer) + `.zip` + portable `.exe` | `packages/electron/out/make/` |
600
+ | Windows | `.exe` (NSIS) + `.zip` + portable `.exe` | `packages/electron/out/make/` |
742
601
 
743
- ### Cross-Platform Builds (via Docker)
602
+ ### Cross-platform builds (Docker)
744
603
 
745
- From macOS or Linux, you can build installers for **all platforms** using Docker:
604
+ From macOS or Linux, build installers for all platforms:
746
605
 
747
606
  ```bash
748
- npm run electron:build -- --all # macOS (native) + Linux + Windows (Docker)
749
- npm run electron:build -- --linux # Linux .deb + .AppImage only
750
- npm run electron:build -- --windows # Windows .exe (NSIS) only
607
+ npm run electron:build -- --all # macOS (native) + Linux + Windows (Docker)
608
+ npm run electron:build -- --linux # Linux .deb + .AppImage only
609
+ npm run electron:build -- --windows # Windows .exe (NSIS) only
751
610
  npm run electron:build -- --linux --windows # Both, skip native
752
611
  ```
753
612
 
754
- Docker builds use a Node 22 Debian container with NSIS installed for Windows cross-compilation.
755
- All output goes to `packages/electron/out/make/`.
756
-
757
- > **Note:** Native builds (no flags) build for the current platform only. Docker is required for `--linux`, `--windows`, and `--all`.
613
+ Docker builds use a Node 22 Debian container with NSIS installed for Windows cross-compilation. Output goes to `packages/electron/out/make/`.
758
614
 
759
- ### Development Mode
615
+ ### Electron dev mode
760
616
 
761
617
  ```bash
762
618
  # Start the dashboard server and Vite dev server first
@@ -768,7 +624,7 @@ cd packages/electron
768
624
  npm run start:dev
769
625
  ```
770
626
 
771
- ### Regenerating Icons
627
+ ### Regenerating icons
772
628
 
773
629
  All platform icon variants are generated from the master icon at `packages/electron/resources/icon.png`:
774
630
 
@@ -777,9 +633,31 @@ cd packages/electron
777
633
  npm run icons # Generates .icns (macOS), .ico (Windows), and resized PNGs
778
634
  ```
779
635
 
780
- ### CI Builds & GitHub Releases
636
+ ---
637
+
638
+ ## CI/CD & releasing
639
+
640
+ See [`docs/release-process.md`](docs/release-process.md) for the full cut-a-release workflow.
641
+
642
+ ### Continuous integration
781
643
 
782
- The release workflow (`.github/workflows/publish.yml`) builds Electron installers for **all platforms** on every version tag (`v*`):
644
+ Every push to `develop` and every pull request against `develop` triggers [`ci.yml`](.github/workflows/ci.yml):
645
+
646
+ 1. `npm ci` — install dependencies
647
+ 2. `npm run lint` — type check
648
+ 3. `npm test` — run tests
649
+ 4. `npm run build` — build web client
650
+
651
+ ### Releasing
652
+
653
+ The publish workflow ([`publish.yml`](.github/workflows/publish.yml)) triggers on `v*` tags:
654
+
655
+ ```bash
656
+ npm version patch # or minor / major
657
+ git push --follow-tags
658
+ ```
659
+
660
+ This runs CI, publishes to npm with `--provenance` for supply-chain transparency, and builds Electron installers for all platforms on native runners:
783
661
 
784
662
  | Runner | Platform | Outputs |
785
663
  |--------|----------|---------|
@@ -789,37 +667,51 @@ The release workflow (`.github/workflows/publish.yml`) builds Electron installer
789
667
  | `windows-latest` | Windows x64 | `.exe` (NSIS) + `.zip` + portable |
790
668
  | `windows-latest` | Windows arm64 | `.zip` + portable (x64 Node.js via WoW64) |
791
669
 
792
- All artifacts are uploaded to a **draft GitHub Release** for review and publishing. The same workflow also publishes the npm package. Release notes for the draft are extracted automatically from the matching `## [<version>]` section of [`CHANGELOG.md`](CHANGELOG.md) — see [`docs/release-process.md`](docs/release-process.md) for the full cut-a-release workflow.
670
+ All artifacts are uploaded to a **draft GitHub Release**. Release notes are extracted automatically from the matching `## [<version>]` section of [`CHANGELOG.md`](CHANGELOG.md).
793
671
 
794
- ## CI/CD
672
+ ### Trusted Publisher (OIDC) setup
795
673
 
796
- ### Continuous Integration
674
+ The publish workflow uses **[npm Trusted Publishers](https://docs.npmjs.com/trusted-publishers)** over OIDC — **no `NPM_TOKEN` secret required**. Short-lived, workflow-scoped credentials are exchanged between GitHub and npm at publish time, and every release carries automatic [npm provenance](https://docs.npmjs.com/generating-provenance-statements) tying the published artifact to the exact workflow run.
797
675
 
798
- Every push to `develop` and every pull request against `develop` triggers the CI workflow (`.github/workflows/ci.yml`):
676
+ **Requirements** (already configured in [`publish.yml`](.github/workflows/publish.yml)):
799
677
 
800
- 1. `npm ci` — install dependencies
801
- 2. `npm run lint` — type check
802
- 3. `npm test` run tests
803
- 4. `npm run build` build web client
678
+ ```yaml
679
+ permissions:
680
+ contents: write # draft GitHub Release + tag push
681
+ id-token: write # OIDC token exchange with the npm registry
682
+ environment: npm-publish
683
+ ```
804
684
 
805
- ### Releasing to npm
685
+ Trusted Publishing requires **npm CLI ≥ 11.5.1**. The workflow upgrades npm automatically (`npm install -g npm@latest`) before publishing.
806
686
 
807
- The publish workflow (`.github/workflows/publish.yml`) triggers on `v*` tags:
687
+ **One-time npm-side setup** — repeat once per published package (five scoped workspaces; `@blackbelt-technology/pi-dashboard-electron` is private and skipped):
808
688
 
809
- ```bash
810
- npm version patch # or minor / major
811
- git push --follow-tags
812
- ```
689
+ | Package |
690
+ |---|
691
+ | `@blackbelt-technology/pi-agent-dashboard` (root) |
692
+ | `@blackbelt-technology/pi-dashboard-shared` |
693
+ | `@blackbelt-technology/pi-dashboard-extension` |
694
+ | `@blackbelt-technology/pi-dashboard-server` |
695
+ | `@blackbelt-technology/pi-dashboard-web` |
696
+
697
+ For each:
698
+
699
+ 1. Go to [npmjs.com](https://www.npmjs.com/) → the package → **Settings** → **Trusted Publisher** → **GitHub Actions**
700
+ 2. Fill in:
701
+ - **Organization or user:** `BlackBeltTechnology`
702
+ - **Repository:** `pi-agent-dashboard`
703
+ - **Workflow filename:** `publish.yml` *(filename only, not the full path)*
704
+ - **Environment name:** `npm-publish` *(must match the `environment:` field in the workflow)*
705
+ 3. Save
813
706
 
814
- This runs CI checks, then publishes to npm with `--provenance` for supply chain transparency.
707
+ **GitHub Environment (recommended)** configures an optional human-approval gate on every release:
815
708
 
816
- ### npm Token Setup
709
+ 1. GitHub repo → **Settings** → **Environments** → **New environment** → name `npm-publish`
710
+ 2. Optionally add **required reviewers** and/or **deployment branch/tag protection rules** (e.g. restrict to `v*` tags)
817
711
 
818
- The publish workflow requires an `NPM_TOKEN` secret in the GitHub repository:
712
+ No secrets to rotate, no tokens to leak.
819
713
 
820
- 1. Generate a token at [npmjs.com](https://www.npmjs.com/) → Access Tokens → Generate New Token (Granular Access Token)
821
- 2. Grant publish access to `@blackbelt-technology` packages
822
- 3. Add it as a repository secret: GitHub repo → Settings → Secrets and variables → Actions → New repository secret → Name: `NPM_TOKEN`
714
+ ---
823
715
 
824
716
  ## License
825
717