@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 +301 -0
- package/README.md +107 -0
- package/commands/kanban.md +48 -0
- package/package.json +23 -0
- package/scripts/cli.ts +15 -0
- package/scripts/dev.ts +49 -0
- package/scripts/setup.ts +35 -0
- package/scripts/tui.ts +29 -0
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();
|
package/scripts/setup.ts
ADDED
|
@@ -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();
|