@benlooper/agent-board-tui 1.0.0

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.
package/AGENT_API.md ADDED
@@ -0,0 +1,301 @@
1
+ # Agent Board API Reference
2
+
3
+ Base URL: `http://localhost:31377/api`
4
+
5
+ This board is the shared workspace between you and the user. Use it to track your work, signal progress, and — critically — ask the user for input when you need it.
6
+
7
+ ---
8
+
9
+ ## Workflow Basics
10
+
11
+ 1. **Claim a card** when you pick up a task — this sets you as the owner and moves it to In Progress.
12
+ 2. **Post comments** on the card as you work. This is how the user follows along.
13
+ 3. **Ask for input** using the long-poll endpoint whenever you need a decision, approval, or information. The request surfaces in the UI with an audio alert. **Do not proceed past a blocking question — wait for the answer.**
14
+ 4. **Check your messages** at the start of each turn — the user may have left you a message via the chat system.
15
+ 5. **Update the status** when you finish or hit a wall.
16
+
17
+ ---
18
+
19
+ ## Cards
20
+
21
+ ### List all cards
22
+ ```
23
+ GET /cards
24
+ GET /cards?status=<statusId>
25
+ ```
26
+ Returns all cards, optionally filtered by status ID. Use `GET /statuses` first to resolve status names to IDs.
27
+
28
+ ### Get a single card (with comments)
29
+ ```
30
+ GET /cards/:id
31
+ ```
32
+ Returns the card plus its full comment thread.
33
+
34
+ ### Claim a card
35
+ ```
36
+ POST /cards/:id/claim
37
+ {
38
+ "agentId": "implementer-1",
39
+ "autoAdvance": true
40
+ }
41
+ ```
42
+ Sets you as the owner. If `autoAdvance` is true (default) and the card is in "To Do", it automatically moves to "In Progress". **Do this before starting work.**
43
+
44
+ ### What statuses can I move this card to?
45
+ ```
46
+ GET /cards/:id/allowed-statuses?agentId=implementer-1
47
+ ```
48
+ Returns the list of status objects you are permitted to move this card to from its current status, based on the configured transition rules. If no rules are configured, returns all statuses. **Check this before patching status** to avoid a rejected move.
49
+
50
+ ### Update a card
51
+ ```
52
+ PATCH /cards/:id
53
+ {
54
+ "statusId": "<id>",
55
+ "agentId": "implementer-1",
56
+ "title": "...",
57
+ "description": "...",
58
+ "type": "task|story|bug",
59
+ "epicId": "<id or null>",
60
+ "featureId": "<id or null>"
61
+ }
62
+ ```
63
+ All fields are optional. When changing status, include your `agentId` so transition rules are enforced. If a move is not permitted, the server returns an error.
64
+
65
+ ### Create a card
66
+ ```
67
+ POST /cards
68
+ {
69
+ "title": "Fix login bug",
70
+ "statusId": "<To Do status ID>",
71
+ "type": "bug",
72
+ "description": "...",
73
+ "agentId": "implementer-1",
74
+ "epicId": "<id>",
75
+ "featureId": "<id>"
76
+ }
77
+ ```
78
+
79
+ ### Delete a card
80
+ ```
81
+ DELETE /cards/:id
82
+ ```
83
+
84
+ ### Post a comment
85
+ ```
86
+ POST /cards/:id/comments
87
+ {
88
+ "body": "Finished the database migration. Moving to review.",
89
+ "author": "agent"
90
+ }
91
+ ```
92
+ Use comments to narrate your progress. The user reads these.
93
+
94
+ ---
95
+
96
+ ## Requesting User Input ⚠️
97
+
98
+ **This is the most important endpoint. Use it any time you need a decision, approval, clarification, or information you cannot determine yourself. Do not guess — ask.**
99
+
100
+ ```
101
+ POST /input
102
+ {
103
+ "cardId": "<your card id>",
104
+ "questions": [
105
+ {
106
+ "id": "q1",
107
+ "type": "yesno",
108
+ "prompt": "Should I overwrite the existing config file?"
109
+ },
110
+ {
111
+ "id": "q2",
112
+ "type": "choice",
113
+ "prompt": "Which environment should this deploy to?",
114
+ "options": ["staging", "production"]
115
+ },
116
+ {
117
+ "id": "q3",
118
+ "type": "text",
119
+ "prompt": "What should the new API endpoint be named?",
120
+ "default": "/api/v2/users"
121
+ }
122
+ ],
123
+ "timeoutSecs": 900
124
+ }
125
+ ```
126
+
127
+ **This call blocks.** The HTTP request stays open until the user answers in the UI (or the timeout expires). Your card is automatically moved to "Blocked" while waiting. When the user responds, the call returns:
128
+
129
+ ```json
130
+ {
131
+ "requestId": "...",
132
+ "status": "answered",
133
+ "answers": {
134
+ "q1": "yes",
135
+ "q2": "production",
136
+ "q3": "/api/v2/users"
137
+ }
138
+ }
139
+ ```
140
+
141
+ On timeout (HTTP 408):
142
+ ```json
143
+ { "requestId": "...", "status": "timed_out", "answers": null }
144
+ ```
145
+
146
+ **Question types:**
147
+ - `text` — free-form string. Supply `default` if there's a sensible fallback.
148
+ - `yesno` — answer will be `"yes"` or `"no"`.
149
+ - `choice` — single-select from `options`. Answer will be one of the option strings.
150
+
151
+ **When to use it:** Whenever you are blocked on a human decision, need approval before a destructive action, are uncertain about scope or intent, or need credentials/values you don't have. The user gets an audio alert and a prompt in the UI — they expect to be asked.
152
+
153
+ ---
154
+
155
+ ## Agent Chat (Message Queue)
156
+
157
+ The board has a bidirectional chat system. The user can send you messages between turns and expects you to read them before doing any work. You can also send replies that appear in the user's chat window.
158
+
159
+ ### Fuzzy agent matching
160
+
161
+ Messages use a fuzzy pattern match — the message's `agentId` is matched as a substring of your agent id. This means a message addressed to `"implementer"` will be picked up by `"implementer-1"`, `"implementer-frontend"`, or `"implementer"` itself.
162
+
163
+ Always poll using your **full** agent id:
164
+ ```
165
+ GET /queue?agentId=implementer-1&status=pending
166
+ ```
167
+
168
+ ### Check your messages (do this at the start of each turn)
169
+ ```
170
+ GET /queue?agentId=<your-agent-id>&status=pending
171
+ ```
172
+ Returns pending messages addressed to you (via fuzzy match), ordered oldest first. Filter for `status=pending` to only get unread messages.
173
+
174
+ ### Reply to a message / send a message
175
+ ```
176
+ POST /queue
177
+ {
178
+ "agentId": "implementer-1",
179
+ "body": "Done — the migration is complete. No data loss.",
180
+ "author": "implementer-1"
181
+ }
182
+ ```
183
+ Set `author` to your own agent id so the user's chat window shows it as a reply from you. The `agentId` field identifies which conversation thread this belongs to.
184
+
185
+ ### Mark a message as read
186
+ ```
187
+ POST /queue/:id/read
188
+ ```
189
+ Call this after you have processed a message.
190
+
191
+ ### List all conversations
192
+ ```
193
+ GET /queue/conversations
194
+ ```
195
+ Returns a summary per agent: `{ agentId, total, unread, lastAt }`.
196
+
197
+ ### QueueMessage shape
198
+ ```json
199
+ {
200
+ "id": "uuid",
201
+ "agentId": "implementer",
202
+ "body": "Please prioritize the login bug fix next.",
203
+ "status": "pending",
204
+ "author": "user",
205
+ "createdAt": "2026-03-24T10:00:00.000Z",
206
+ "readAt": null
207
+ }
208
+ ```
209
+
210
+ **`author` field:** `"user"` means the human sent it. Any other value is the agent id of the sender.
211
+
212
+ **Recommended per-turn workflow:**
213
+ 1. At the start of each turn, call `GET /queue?agentId=<your-id>&status=pending`.
214
+ 2. Read the messages and adjust your plan accordingly.
215
+ 3. Reply with `POST /queue` (set `author` to your agent id) if a response is warranted.
216
+ 4. Call `POST /queue/:id/read` for each message you act on.
217
+ 5. Note any changes to your plan in a comment on the relevant card.
218
+
219
+ ---
220
+
221
+ ## Statuses
222
+
223
+ ### List all statuses (ordered by position)
224
+ ```
225
+ GET /statuses
226
+ ```
227
+ Returns `[{ id, name, color, position }]`. Resolve status names to IDs before using them in card operations.
228
+
229
+ ---
230
+
231
+ ## Epics
232
+
233
+ ### List all epics
234
+ ```
235
+ GET /epics
236
+ ```
237
+
238
+ ### Create an epic
239
+ ```
240
+ POST /epics
241
+ {
242
+ "title": "Authentication Overhaul",
243
+ "description": "...",
244
+ "statusId": "<id>"
245
+ }
246
+ ```
247
+
248
+ ### Update an epic
249
+ ```
250
+ PATCH /epics/:id
251
+ { "title": "...", "description": "...", "statusId": "<id>" }
252
+ ```
253
+
254
+ ---
255
+
256
+ ## Features
257
+
258
+ ### List all features
259
+ ```
260
+ GET /features
261
+ ```
262
+
263
+ ### Create a feature
264
+ ```
265
+ POST /features
266
+ {
267
+ "epicId": "<id>",
268
+ "title": "JWT Token Issuance",
269
+ "description": "...",
270
+ "statusId": "<id>"
271
+ }
272
+ ```
273
+
274
+ ### Update a feature
275
+ ```
276
+ PATCH /features/:id
277
+ { "title": "...", "description": "...", "statusId": "<id>" }
278
+ ```
279
+
280
+ ---
281
+
282
+ ## Transition Rules
283
+
284
+ Rules control which agents can move cards to which statuses. If no rules exist, all moves are allowed.
285
+
286
+ ### List all rules
287
+ ```
288
+ GET /transition-rules
289
+ ```
290
+ Returns `[{ id, agentPattern, fromStatusId, toStatusId }]`. `null` means "any".
291
+
292
+ ---
293
+
294
+ ## Tips
295
+
296
+ - Always claim a card before working on it.
297
+ - Post a comment when you start, when you hit a decision point, and when you finish.
298
+ - Use `GET /cards/:id/allowed-statuses?agentId=<you>` before changing status — it tells you exactly what moves are available to you.
299
+ - When in doubt about anything, use `POST /input`. The user prefers being asked over having you guess.
300
+ - `timeoutSecs` defaults to 900 (15 min). For non-urgent questions you can increase it; for quick confirmations leave it at default.
301
+ - Check `GET /queue?agentId=<you>&status=pending` at the start of each turn before doing any other work.
package/README.md ADDED
@@ -0,0 +1,107 @@
1
+ # agent-board-tui
2
+
3
+ A terminal UI fork of [agent-board](https://github.com/benlooper/agent-board). Replaces the React/Vite web frontend with a keyboard-driven terminal interface built with [Ink](https://github.com/vadimdemedes/ink).
4
+
5
+ Everything else is unchanged — same Elysia API, same SQLite database, same agent integration.
6
+
7
+ ---
8
+
9
+ ## Setup
10
+
11
+ ```bash
12
+ bun install
13
+ bun run dev # starts server + TUI together
14
+ ```
15
+
16
+ Or run them independently:
17
+
18
+ ```bash
19
+ bun run server # API only — keeps running between TUI sessions
20
+ bun run tui # TUI only — connects to a running server
21
+ ```
22
+
23
+ The database is created automatically at `server/data/agent-board.db` on first run.
24
+
25
+ - **API** — `http://localhost:31377`
26
+ - **Swagger** — `http://localhost:31377/docs`
27
+
28
+ ---
29
+
30
+ ## Navigation
31
+
32
+ | Key | Action |
33
+ |-----|--------|
34
+ | `b` / `c` / `a` | Switch to Board / Chat / Admin view |
35
+ | `Ctrl+A` | Jump to Admin from anywhere |
36
+ | `h` / `l` | Move between columns (board) or tabs (admin) |
37
+ | `j` / `k` | Move up / down |
38
+ | `Enter` | Open card / confirm |
39
+ | `Esc` | Go back |
40
+ | `i` | Answer a pending agent input request |
41
+ | `?` | Help overlay |
42
+ | `Ctrl+R` | Restart TUI (server keeps running) |
43
+ | `Ctrl+C` | Quit |
44
+
45
+ ---
46
+
47
+ ## Themes
48
+
49
+ Admin → Theme tab. Four presets: **Default**, **Dracula**, **Nord**, **Gruvbox**. Persists to `~/.config/agent-board/settings.json`.
50
+
51
+ ---
52
+
53
+ ## Integrating agents
54
+
55
+ Same as the original. Include `AGENT_API.md` in any agent's system prompt:
56
+
57
+ ```
58
+ You have access to a task board at http://localhost:31377.
59
+ See the API reference below for how to use it.
60
+
61
+ <agent_api>
62
+ [contents of AGENT_API.md]
63
+ </agent_api>
64
+ ```
65
+
66
+ For Claude Code agents, `.claude/commands/` has slash command skills:
67
+
68
+ ```
69
+ /board-create-card /board-update-card /board-complete-card
70
+ /board-block-card /board-request-input /board-add-comment
71
+ /board-list-cards /board-get-card /board-create-epic
72
+ /board-create-feature /board-check-messages /board-send-message
73
+ ```
74
+
75
+ ---
76
+
77
+ ## Stack
78
+
79
+ | Layer | Technology |
80
+ |-------|-----------|
81
+ | Runtime | Bun |
82
+ | API | Elysia + Eden Treaty |
83
+ | Database | SQLite (Drizzle ORM) |
84
+ | Terminal UI | React 18 + [Ink](https://github.com/vadimdemedes/ink) |
85
+ | Server state | TanStack Query v5 |
86
+ | UI state | Zustand |
87
+ | Real-time | Native WebSocket |
88
+
89
+ ---
90
+
91
+ ## Project structure
92
+
93
+ ```
94
+ agent-board-tui/
95
+ ├── server/ # Elysia API (unchanged from original)
96
+ ├── tui/ # Ink terminal UI (replaces client/)
97
+ │ └── src/
98
+ │ ├── views/ # Board, CardDetail, Chat, Admin
99
+ │ ├── components/
100
+ │ ├── hooks/ # useWebSocket, useDimensions, useTheme
101
+ │ ├── store/ # Zustand
102
+ │ └── api/ # Eden Treaty client
103
+ ├── scripts/
104
+ │ ├── dev.ts # Starts server + TUI (restart-aware)
105
+ │ └── tui.ts # TUI-only launcher
106
+ └── AGENT_API.md # HTTP reference for agents
107
+ ```
@@ -0,0 +1,48 @@
1
+ Enter orchestrator mode for the agent-board kanban system.
2
+
3
+ $ARGUMENTS is an optional initial directive describing the work to accomplish.
4
+ If not provided, ask the user what they want to accomplish before proceeding.
5
+
6
+ ## Step 1: Ensure the server is running
7
+
8
+ Check if the board server is up:
9
+
10
+ curl -s --max-time 2 http://localhost:31377/api/cards
11
+
12
+ If that fails, start it:
13
+
14
+ agent-board-tui
15
+
16
+ Then poll every 2s (up to 15s) until the health check passes.
17
+
18
+ ## Step 2: API Reference
19
+
20
+ You now have the full agent-board API reference. Internalize it — all board
21
+ operations go through these endpoints.
22
+
23
+ {{AGENT_API}}
24
+
25
+ ## Step 3: Assume orchestrator role
26
+
27
+ Your agent ID for this session is `orchestrator`.
28
+
29
+ As orchestrator:
30
+ - Assign stable IDs to sub-agents before spawning (e.g. `implementer-1`, `reviewer-1`)
31
+ - Create an epic for the overall goal, features for major workstreams, cards for agent tasks
32
+ - Assign cards to agents by setting `agentId` — do not claim cards yourself
33
+ - Spawn sub-agents via the Agent tool, passing their card ID and agent ID in the prompt
34
+ - Monitor progress by reading card comments (`GET /cards/:id`)
35
+ - Check `GET /queue/conversations` for unread agent messages; route or forward as needed
36
+ - Use `POST /input` (with your own cardId) for decisions that require the user
37
+
38
+ ## Step 4: Accept the directive
39
+
40
+ Use $ARGUMENTS as the directive, or ask the user now if none was provided.
41
+
42
+ ## Step 5: Structure the work and begin orchestrating
43
+
44
+ 1. `POST /epics` — create an epic for the overall goal
45
+ 2. `POST /features` — one per major workstream
46
+ 3. `POST /cards` — one per sub-agent task; set `agentId` and `epicId`/`featureId`
47
+ 4. Spawn sub-agents via the Agent tool; each gets their card ID, agent ID, and enough context
48
+ 5. Monitor the board and handle input requests as they arrive
package/package.json ADDED
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "@benlooper/agent-board-tui",
3
+ "version": "1.0.0",
4
+ "bin": {
5
+ "agent-board-tui": "scripts/cli.ts"
6
+ },
7
+ "files": [
8
+ "scripts/",
9
+ "commands/",
10
+ "AGENT_API.md"
11
+ ],
12
+ "scripts": {
13
+ "dev": "bun scripts/dev.ts",
14
+ "server": "bun run --filter server dev",
15
+ "tui": "bun scripts/tui.ts",
16
+ "build": "bun run --filter '*' build",
17
+ "install:all": "bun install",
18
+ "setup": "bun scripts/setup.ts"
19
+ },
20
+ "devDependencies": {
21
+ "concurrently": "^8.2.2"
22
+ }
23
+ }
package/scripts/cli.ts ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env bun
2
+ /// <reference types="bun-types" />
3
+
4
+ const [subcommand] = process.argv.slice(2);
5
+
6
+ switch (subcommand) {
7
+ case "setup":
8
+ await import("./setup.ts");
9
+ break;
10
+ default:
11
+ console.error(
12
+ `Usage: agent-board-tui <command>\n\nCommands:\n setup Install the /kanban slash command to ~/.claude/commands/`
13
+ );
14
+ process.exit(subcommand ? 1 : 0);
15
+ }
package/scripts/dev.ts ADDED
@@ -0,0 +1,49 @@
1
+ import { spawn } from "child_process";
2
+ import { openSync } from "fs";
3
+ import { join } from "path";
4
+
5
+ const isWindows = process.platform === "win32";
6
+ const bunCmd = isWindows ? "bun.exe" : "bun";
7
+ const RESTART_CODE = 75;
8
+
9
+ // Redirect server output to a log file so it doesn't pollute the TUI terminal
10
+ const logPath = join(import.meta.dir, "../server.log");
11
+ const logFd = openSync(logPath, "w");
12
+
13
+ const server = spawn(bunCmd, ["run", "--filter", "server", "dev"], {
14
+ stdio: ["ignore", logFd, logFd],
15
+ });
16
+
17
+ server.on("error", (err) => {
18
+ console.error("[dev] failed to start server:", err.message);
19
+ process.exit(1);
20
+ });
21
+
22
+ process.on("SIGINT", () => {
23
+ server.kill();
24
+ });
25
+
26
+ function launchTui() {
27
+ // TUI runs in foreground — must inherit stdin so Ink gets a real TTY
28
+ const tui = spawn(bunCmd, ["run", "dev"], {
29
+ cwd: new URL("../tui", import.meta.url).pathname,
30
+ stdio: "inherit",
31
+ });
32
+
33
+ tui.on("error", (err) => {
34
+ console.error("[dev] failed to start tui:", err.message);
35
+ server.kill();
36
+ process.exit(1);
37
+ });
38
+
39
+ tui.on("exit", (code) => {
40
+ if (code === RESTART_CODE) {
41
+ launchTui(); // restart TUI, keep server running
42
+ } else {
43
+ server.kill();
44
+ process.exit(code ?? 0);
45
+ }
46
+ });
47
+ }
48
+
49
+ launchTui();
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env bun
2
+ /// <reference types="bun-types" />
3
+ /**
4
+ * setup.ts
5
+ *
6
+ * Installs the /kanban global slash command to ~/.claude/commands/kanban.md.
7
+ * Replaces {{AGENT_API}} in commands/kanban.md with the contents of AGENT_API.md.
8
+ *
9
+ * Usage:
10
+ * bun scripts/setup.ts
11
+ *
12
+ * Idempotent — re-running overwrites with the latest content.
13
+ */
14
+
15
+ import { readFileSync, writeFileSync, mkdirSync } from "fs";
16
+ import { join, dirname } from "path";
17
+ import { homedir } from "os";
18
+
19
+ const root = join(dirname(import.meta.path), "..");
20
+
21
+ const templatePath = join(root, "commands", "kanban.md");
22
+ const apiMdPath = join(root, "AGENT_API.md");
23
+
24
+ const template = readFileSync(templatePath, "utf8");
25
+ const apiContent = readFileSync(apiMdPath, "utf8").trimEnd();
26
+
27
+ const output = template.replace("{{AGENT_API}}", apiContent);
28
+
29
+ const destDir = join(homedir(), ".claude", "commands");
30
+ mkdirSync(destDir, { recursive: true });
31
+
32
+ const destPath = join(destDir, "kanban.md");
33
+ writeFileSync(destPath, output, "utf8");
34
+
35
+ console.log(`✓ Installed /kanban to ${destPath}`);
package/scripts/tui.ts ADDED
@@ -0,0 +1,29 @@
1
+ import { spawn } from "child_process";
2
+
3
+ const isWindows = process.platform === "win32";
4
+ const bunCmd = isWindows ? "bun.exe" : "bun";
5
+ const RESTART_CODE = 75;
6
+
7
+ function launchTui() {
8
+ const tui = spawn(bunCmd, ["run", "dev"], {
9
+ cwd: new URL("../tui", import.meta.url).pathname,
10
+ stdio: "inherit",
11
+ });
12
+
13
+ tui.on("error", (err) => {
14
+ console.error("[tui] failed to start:", err.message);
15
+ process.exit(1);
16
+ });
17
+
18
+ tui.on("exit", (code) => {
19
+ if (code === RESTART_CODE) {
20
+ launchTui();
21
+ } else {
22
+ process.exit(code ?? 0);
23
+ }
24
+ });
25
+
26
+ process.on("SIGINT", () => tui.kill());
27
+ }
28
+
29
+ launchTui();