claude-code-controller 0.1.0 → 0.2.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/README.md CHANGED
@@ -1,55 +1,119 @@
1
- # claude-code-controller
1
+ <p align="center">
2
+ <h1 align="center">claude-code-controller</h1>
3
+ <p align="center">
4
+ <strong>Spawn, orchestrate, and control Claude Code agents — programmatically.</strong>
5
+ </p>
6
+ <p align="center">
7
+ <a href="https://www.npmjs.com/package/claude-code-controller"><img src="https://img.shields.io/npm/v/claude-code-controller" alt="npm" /></a>
8
+ <a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-blue.svg" alt="MIT License" /></a>
9
+ <a href="https://nodejs.org"><img src="https://img.shields.io/badge/node-%3E%3D18-brightgreen" alt="Node >= 18" /></a>
10
+ </p>
11
+ </p>
2
12
 
3
- **Spawn, orchestrate, and control Claude Code agents from your own code.**
13
+ <br />
4
14
 
5
- [![npm](https://img.shields.io/npm/v/claude-code-controller)](https://www.npmjs.com/package/claude-code-controller)
6
- [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
7
- [![Node >= 18](https://img.shields.io/badge/node-%3E%3D18-brightgreen)](https://nodejs.org)
15
+ <p align="center">
16
+ <img src="screenshot.png" alt="Claude Code Controller — Web Dashboard" width="100%" />
17
+ </p>
8
18
 
19
+ <br />
20
+
21
+ > Control real Claude Code instances through a **REST API**, a **TypeScript SDK**, or a **Web Dashboard**.
22
+ > Spawn agents, send them messages, assign tasks, approve plans — from your code or your browser.
23
+
24
+ <br />
25
+
26
+ ---
27
+
28
+ <br />
29
+
30
+ ## Why this instead of the Agent SDK?
31
+
32
+ This runs **real Claude Code processes**. Not a wrapper around the API. Not a simplified `-p` mode. Actual Claude Code — the same one you use in your terminal every day.
33
+
34
+ That means:
35
+
36
+ - **Uses your Claude Code subscription** — No separate API key. No usage-based billing surprise. If you have a Max plan, your agents run on it.
37
+ - **Day 0 features** — When Anthropic ships a new Claude Code feature (new tools, new models, better context handling), you get it immediately. No library update needed. No waiting for SDK support.
38
+ - **Full tool access** — Bash, Read, Write, Edit, Glob, Grep, WebSearch, Task sub-agents... everything Claude Code can do, your agents can do.
39
+ - **Real terminal environment** — Agents run in a PTY. They can install packages, run tests, use git, call APIs. They work in your actual project directory.
40
+ - **Battle-tested agent loop** — Claude Code's agent loop is production-hardened. You get all of that for free: retries, error handling, tool orchestration, context management.
41
+
42
+ <br />
43
+
44
+ ---
45
+
46
+ <br />
47
+
48
+ ## What you can do
49
+
50
+ **Spawn multiple agents on the same codebase**, each with a different role. One reviews security, another writes tests, another refactors — all in parallel, all through a simple API.
51
+
52
+ ```bash
53
+ # Spawn an agent via the REST API
54
+ curl -X POST http://localhost:3000/agents \
55
+ -H "Content-Type: application/json" \
56
+ -d '{"name": "security-reviewer", "model": "opus"}'
57
+
58
+ # Give it work
59
+ curl -X POST http://localhost:3000/agents/security-reviewer/messages \
60
+ -H "Content-Type: application/json" \
61
+ -d '{"message": "Audit src/auth/ for vulnerabilities. Reply with SendMessage."}'
9
62
  ```
10
- Your Code
11
-
12
-
13
- ┌─────────────────────┐
14
- │ ClaudeCodeController│
15
- │ ┌───────────────┐ │
16
- │ │ TeamManager │ │ ┌─────────┐
17
- │ │ TaskManager │──┼────▶│ Agent 1 │ claude CLI (PTY)
18
- │ │ ProcessMgr │ │ └─────────┘
19
- │ InboxPoller │──┼────▶│ Agent 2 │ claude CLI (PTY)
20
- └───────────────┘ │ └─────────┘
21
- └─────────────────────┘ │ Agent N │ claude CLI (PTY)
22
- └─────────┘
63
+
64
+ **Build automation on top of Claude Code.** A webhook that triggers a code fix when CI fails. A Slack bot that assigns tasks to agents. A cron job that runs nightly code reviews. If you can make an HTTP call, you can control Claude Code.
65
+
66
+ **Monitor and control agents from a web dashboard.** See what each agent is doing, approve or reject their plans, grant tool permissions, kill runaway agents — all in real-time from your browser.
67
+
68
+ **Manage work with tasks.** Create tasks, assign them to agents, track progress, define dependencies between tasks. Agents pick up their assignments and report back when done.
69
+
70
+ ```typescript
71
+ const taskId = await ctrl.createTask({
72
+ subject: "Add input validation to all API routes",
73
+ description: "Use zod schemas for request body validation in src/routes/",
74
+ });
75
+ await ctrl.assignTask(taskId, "coder");
76
+ await ctrl.waitForTask(taskId); // blocks until done
23
77
  ```
24
78
 
25
- No Agent SDK. No `-p` mode. Full programmatic control over real Claude Code instances through the internal filesystem-based teams protocol.
79
+ <br />
26
80
 
27
81
  ---
28
82
 
29
- ## Why?
83
+ <br />
30
84
 
31
- Claude Code is powerful interactively, but there's no clean way to control it programmatically for automation. The Agent SDK is a separate thing. The `-p` flag is limited to single prompts.
85
+ ## Features
32
86
 
33
- This library reverse-engineers Claude Code's internal team communication protocol (filesystem-based inboxes, team configs, task files) and wraps it in a clean TypeScript API. You get:
87
+ - **REST API** Control everything over HTTP. Spawn agents, send messages, manage tasks. Works from any language, any platform.
88
+ - **TypeScript SDK** — Full programmatic control with type safety and an event-driven architecture.
89
+ - **Web Dashboard** — Real-time monitoring, agent management, and interactive approvals from your browser.
90
+ - **Multi-Agent** — Run multiple agents in parallel, each with their own role, model, and permissions.
91
+ - **Task Management** — Create tasks, assign them, track status, define blocking dependencies.
92
+ - **Plan & Permission Approval** — Agents ask before acting. You approve or reject — programmatically or from the UI.
93
+ - **Any Provider** — Point agents at any Anthropic-compatible endpoint. Per-agent environment and API key overrides.
94
+ - **Your Subscription** — Runs on your existing Claude Code plan. No separate API costs.
34
95
 
35
- - **Real Claude Code agents** with all their tools (Bash, Read, Write, Glob, Grep...)
36
- - **Multi-agent orchestration** with message passing and task assignment
37
- - **Full event system** for plan approvals, permission requests, shutdowns
38
- - **Custom environment routing** to use any API provider
96
+ <br />
39
97
 
40
98
  ---
41
99
 
100
+ <br />
101
+
42
102
  ## Install
43
103
 
44
104
  ```bash
45
105
  npm install claude-code-controller
46
106
  ```
47
107
 
48
- > **Prerequisites:** Claude Code CLI v2.1.34+ installed
108
+ > **Prerequisite:** [Claude Code CLI](https://docs.anthropic.com/en/docs/claude-code) v2.1.34+
109
+
110
+ <br />
49
111
 
50
112
  ---
51
113
 
52
- ## Quick Start
114
+ <br />
115
+
116
+ ## Quick Start — 30 seconds to your first agent
53
117
 
54
118
  ```typescript
55
119
  import { ClaudeCodeController } from "claude-code-controller";
@@ -59,14 +123,11 @@ await ctrl.init();
59
123
 
60
124
  const agent = await ctrl.spawnAgent({
61
125
  name: "coder",
62
- type: "general-purpose",
63
126
  model: "sonnet",
64
127
  });
65
128
 
66
- // Wait for the agent to boot up
67
129
  await new Promise((r) => setTimeout(r, 10_000));
68
130
 
69
- // Ask something and get the answer back
70
131
  const answer = await agent.ask(
71
132
  "Read package.json and tell me the project name. Reply using SendMessage.",
72
133
  { timeout: 60_000 }
@@ -76,26 +137,302 @@ console.log(answer);
76
137
  await ctrl.shutdown();
77
138
  ```
78
139
 
140
+ <br />
141
+
142
+ ---
143
+
144
+ <br />
145
+
146
+ ## REST API
147
+
148
+ The API lets you control Claude Code agents from **any language, any platform** — just HTTP.
149
+
150
+ Start a server in a few lines:
151
+
152
+ ```typescript
153
+ import { createApi } from "claude-code-controller/api";
154
+ import { serve } from "bun"; // or any Hono-compatible runtime
155
+
156
+ const app = createApi(); // lazy mode — init via POST /session/init
157
+ serve({ port: 3000, fetch: app.fetch.bind(app) });
158
+ ```
159
+
160
+ Or attach to an existing controller:
161
+
162
+ ```typescript
163
+ import { ClaudeCodeController } from "claude-code-controller";
164
+ import { createApi } from "claude-code-controller/api";
165
+
166
+ const ctrl = new ClaudeCodeController({ teamName: "my-team" });
167
+ await ctrl.init();
168
+
169
+ const app = createApi(ctrl); // pre-initialized mode
170
+ ```
171
+
172
+ <br />
173
+
174
+ ### Endpoints
175
+
176
+ #### Session
177
+
178
+ | Method | Endpoint | Description |
179
+ |--------|----------|-------------|
180
+ | `GET` | `/health` | Server health & uptime |
181
+ | `GET` | `/session` | Current session info |
182
+ | `POST` | `/session/init` | Initialize a new controller session |
183
+ | `POST` | `/session/shutdown` | Shut down the controller |
184
+
185
+ #### Agents
186
+
187
+ | Method | Endpoint | Description |
188
+ |--------|----------|-------------|
189
+ | `GET` | `/agents` | List all agents |
190
+ | `POST` | `/agents` | Spawn a new agent |
191
+ | `GET` | `/agents/:name` | Get agent details |
192
+ | `POST` | `/agents/:name/messages` | Send a message to an agent |
193
+ | `POST` | `/agents/:name/kill` | Force-kill an agent |
194
+ | `POST` | `/agents/:name/shutdown` | Request graceful shutdown |
195
+ | `POST` | `/agents/:name/approve-plan` | Approve or reject a plan |
196
+ | `POST` | `/agents/:name/approve-permission` | Approve or deny tool use |
197
+
198
+ #### Tasks
199
+
200
+ | Method | Endpoint | Description |
201
+ |--------|----------|-------------|
202
+ | `GET` | `/tasks` | List all tasks |
203
+ | `POST` | `/tasks` | Create a new task |
204
+ | `GET` | `/tasks/:id` | Get task details |
205
+ | `PATCH` | `/tasks/:id` | Update a task |
206
+ | `DELETE` | `/tasks/:id` | Delete a task |
207
+ | `POST` | `/tasks/:id/assign` | Assign task to an agent |
208
+
209
+ #### Actions (for dashboards & UIs)
210
+
211
+ | Method | Endpoint | Description |
212
+ |--------|----------|-------------|
213
+ | `GET` | `/actions` | All pending actions (approvals, idle agents, unassigned tasks) |
214
+ | `GET` | `/actions/approvals` | Pending approval requests |
215
+ | `GET` | `/actions/tasks` | Unassigned tasks |
216
+ | `GET` | `/actions/idle-agents` | Idle agents waiting for work |
217
+
218
+ #### Broadcasting
219
+
220
+ | Method | Endpoint | Description |
221
+ |--------|----------|-------------|
222
+ | `POST` | `/broadcast` | Send a message to all agents |
223
+
224
+ <br />
225
+
226
+ ### API Examples
227
+
228
+ **Initialize a session:**
229
+
230
+ ```bash
231
+ curl -X POST http://localhost:3000/session/init \
232
+ -H "Content-Type: application/json" \
233
+ -d '{"teamName": "my-team", "cwd": "/path/to/project"}'
234
+ ```
235
+
236
+ **Spawn an agent:**
237
+
238
+ ```bash
239
+ curl -X POST http://localhost:3000/agents \
240
+ -H "Content-Type: application/json" \
241
+ -d '{
242
+ "name": "reviewer",
243
+ "type": "general-purpose",
244
+ "model": "sonnet",
245
+ "permissions": ["Bash", "Read", "Write"]
246
+ }'
247
+ ```
248
+
249
+ **Send a message:**
250
+
251
+ ```bash
252
+ curl -X POST http://localhost:3000/agents/reviewer/messages \
253
+ -H "Content-Type: application/json" \
254
+ -d '{"message": "Review src/auth.ts for security vulnerabilities. Reply with SendMessage."}'
255
+ ```
256
+
257
+ **Create and assign a task:**
258
+
259
+ ```bash
260
+ # Create
261
+ curl -X POST http://localhost:3000/tasks \
262
+ -H "Content-Type: application/json" \
263
+ -d '{"subject": "Fix login bug", "description": "Users cannot login with SSO"}'
264
+
265
+ # Assign
266
+ curl -X POST http://localhost:3000/tasks/1/assign \
267
+ -H "Content-Type: application/json" \
268
+ -d '{"agent": "reviewer"}'
269
+ ```
270
+
271
+ **Check pending actions:**
272
+
273
+ ```bash
274
+ curl http://localhost:3000/actions
275
+ # → { "pending": 2, "approvals": [...], "unassignedTasks": [...], "idleAgents": [...] }
276
+ ```
277
+
278
+ <br />
279
+
280
+ ---
281
+
282
+ <br />
283
+
284
+ ## TypeScript SDK
285
+
286
+ The SDK gives you full programmatic control with type safety and an event-driven architecture.
287
+
288
+ ### Controller
289
+
290
+ ```typescript
291
+ import { ClaudeCodeController } from "claude-code-controller";
292
+
293
+ const ctrl = new ClaudeCodeController({
294
+ teamName: "my-team", // auto-generated if omitted
295
+ cwd: "/path/to/project", // working directory for agents
296
+ claudeBinary: "claude", // path to CLI binary
297
+ env: { // default env vars for all agents
298
+ ANTHROPIC_BASE_URL: "https://your-proxy.example.com",
299
+ },
300
+ logLevel: "info", // "debug" | "info" | "warn" | "error" | "silent"
301
+ });
302
+
303
+ await ctrl.init();
304
+ // ... use the controller ...
305
+ await ctrl.shutdown();
306
+ ```
307
+
308
+ ### Spawning Agents
309
+
310
+ ```typescript
311
+ const agent = await ctrl.spawnAgent({
312
+ name: "coder",
313
+ type: "general-purpose", // "general-purpose" | "Bash" | "Explore" | "Plan"
314
+ model: "sonnet", // "sonnet" | "opus" | "haiku" | full model ID
315
+ cwd: "/specific/directory",
316
+ permissions: ["Bash", "Read", "Write", "Glob", "Grep"],
317
+ env: { MY_VAR: "value" }, // per-agent env overrides
318
+ });
319
+ ```
320
+
321
+ ### AgentHandle
322
+
323
+ Every spawned agent returns an `AgentHandle` — a convenient wrapper for interacting with it.
324
+
325
+ ```typescript
326
+ // Send and receive
327
+ await agent.send("Analyze the codebase structure.");
328
+ const response = await agent.receive({ timeout: 30_000 });
329
+
330
+ // Or use ask() for send + receive in one call
331
+ const answer = await agent.ask("What framework is this project using?", {
332
+ timeout: 60_000,
333
+ });
334
+
335
+ // Stream events
336
+ for await (const msg of agent.events()) {
337
+ console.log(`[${agent.name}]`, msg.text);
338
+ }
339
+
340
+ // Lifecycle
341
+ agent.isRunning; // boolean
342
+ agent.pid; // process ID
343
+ await agent.shutdown(); // graceful
344
+ await agent.kill(); // force
345
+ ```
346
+
347
+ ### Messaging
348
+
349
+ ```typescript
350
+ // Direct messaging
351
+ await ctrl.send("agent-name", "Your instructions here", "optional summary");
352
+
353
+ // Broadcast to all agents
354
+ await ctrl.broadcast("Everyone stop and report status.");
355
+
356
+ // Wait for a response
357
+ const messages = await ctrl.receive("agent-name", {
358
+ timeout: 60_000,
359
+ pollInterval: 500,
360
+ all: true, // get all unread messages
361
+ });
362
+
363
+ // Wait for any agent to respond
364
+ const msg = await ctrl.receiveAny({ timeout: 30_000 });
365
+ ```
366
+
367
+ ### Task Management
368
+
369
+ ```typescript
370
+ // Create a task
371
+ const taskId = await ctrl.createTask({
372
+ subject: "Add input validation",
373
+ description: "Add zod schemas to all API endpoints in src/routes/",
374
+ owner: "coder", // optional — assign immediately
375
+ metadata: { priority: "high" },
376
+ });
377
+
378
+ // Assign later
379
+ await ctrl.assignTask(taskId, "coder");
380
+
381
+ // Wait for completion
382
+ const task = await ctrl.waitForTask(taskId, 120_000);
383
+ console.log(task.status); // "completed"
384
+ ```
385
+
386
+ ### Events
387
+
388
+ ```typescript
389
+ // Agent messages
390
+ ctrl.on("message", (agentName, message) => {
391
+ console.log(`[${agentName}] ${message.text}`);
392
+ });
393
+
394
+ // Plan approval — agent wants to execute a plan
395
+ ctrl.on("plan:approval_request", (agentName, msg) => {
396
+ console.log(`${agentName} wants to execute a plan:`, msg.planContent);
397
+ ctrl.sendPlanApproval(agentName, msg.requestId, true);
398
+ });
399
+
400
+ // Permission request — agent wants to use a tool
401
+ ctrl.on("permission:request", (agentName, msg) => {
402
+ const safe = ["Read", "Glob", "Grep"].includes(msg.toolName);
403
+ ctrl.sendPermissionResponse(agentName, msg.requestId, safe);
404
+ });
405
+
406
+ // Lifecycle events
407
+ ctrl.on("agent:spawned", (name, pid) => console.log(`${name} started (pid: ${pid})`));
408
+ ctrl.on("agent:exited", (name, code) => console.log(`${name} exited (code: ${code})`));
409
+ ctrl.on("idle", (name) => console.log(`${name} is idle`));
410
+ ctrl.on("task:completed", (task) => console.log(`Task done: ${task.subject}`));
411
+ ctrl.on("error", (err) => console.error("Controller error:", err));
412
+ ```
413
+
414
+ <br />
415
+
79
416
  ---
80
417
 
418
+ <br />
419
+
81
420
  ## Real-World Examples
82
421
 
83
- ### Multi-Agent Code Review
422
+ ### Parallel Code Review
84
423
 
85
424
  ```typescript
86
425
  const ctrl = new ClaudeCodeController({ teamName: "review" });
87
426
  await ctrl.init();
88
427
 
89
- // Spawn specialized reviewers in parallel
90
428
  const [security, perf, style] = await Promise.all([
91
- ctrl.spawnAgent({ name: "security", type: "general-purpose", model: "opus" }),
92
- ctrl.spawnAgent({ name: "perf", type: "general-purpose", model: "sonnet" }),
93
- ctrl.spawnAgent({ name: "style", type: "general-purpose", model: "haiku" }),
429
+ ctrl.spawnAgent({ name: "security", model: "opus" }),
430
+ ctrl.spawnAgent({ name: "perf", model: "sonnet" }),
431
+ ctrl.spawnAgent({ name: "style", model: "haiku" }),
94
432
  ]);
95
433
 
96
434
  await new Promise((r) => setTimeout(r, 12_000));
97
435
 
98
- // Give each reviewer a different focus
99
436
  const reviews = await Promise.all([
100
437
  security.ask("Review src/ for security vulnerabilities. Reply with SendMessage."),
101
438
  perf.ask("Review src/ for performance issues. Reply with SendMessage."),
@@ -118,14 +455,12 @@ await ctrl.init();
118
455
  const worker = await ctrl.spawnAgent({ name: "worker", model: "sonnet" });
119
456
  await new Promise((r) => setTimeout(r, 10_000));
120
457
 
121
- // Create and assign a task
122
458
  const taskId = await ctrl.createTask({
123
459
  subject: "Add input validation",
124
460
  description: "Add zod validation to all API route handlers in src/routes/",
125
461
  owner: "worker",
126
462
  });
127
463
 
128
- // Wait for the agent to complete it
129
464
  const result = await ctrl.waitForTask(taskId, 120_000);
130
465
  console.log(`Task ${result.status}: ${result.subject}`);
131
466
 
@@ -134,8 +469,6 @@ await ctrl.shutdown();
134
469
 
135
470
  ### Custom API Provider
136
471
 
137
- Route agents through any OpenAI-compatible endpoint:
138
-
139
472
  ```typescript
140
473
  const ctrl = new ClaudeCodeController({
141
474
  teamName: "custom",
@@ -144,210 +477,128 @@ const ctrl = new ClaudeCodeController({
144
477
  ANTHROPIC_AUTH_TOKEN: process.env.MY_API_KEY!,
145
478
  },
146
479
  });
147
- ```
148
-
149
- Per-agent overrides take precedence:
150
480
 
151
- ```typescript
481
+ // Per-agent overrides
152
482
  const agent = await ctrl.spawnAgent({
153
483
  name: "worker",
154
484
  env: { ANTHROPIC_AUTH_TOKEN: "different-key-for-this-agent" },
155
485
  });
156
486
  ```
157
487
 
158
- ### Event-Driven Control
488
+ ### Auto-Approve Everything (YOLO mode)
159
489
 
160
490
  ```typescript
161
- const ctrl = new ClaudeCodeController({ teamName: "events" });
162
- await ctrl.init();
163
-
164
- // Auto-approve all plan requests
165
491
  ctrl.on("plan:approval_request", (agent, msg) => {
166
492
  ctrl.sendPlanApproval(agent, msg.requestId, true);
167
493
  });
168
494
 
169
- // Auto-approve safe tool use, reject dangerous ones
170
495
  ctrl.on("permission:request", (agent, msg) => {
171
- const safe = ["Read", "Glob", "Grep"].includes(msg.toolName);
172
- ctrl.sendPermissionResponse(agent, msg.requestId, safe);
496
+ ctrl.sendPermissionResponse(agent, msg.requestId, true);
173
497
  });
174
-
175
- // React to agent messages
176
- ctrl.on("message", (agent, msg) => {
177
- console.log(`[${agent}] ${msg.text}`);
178
- });
179
-
180
- // Track lifecycle
181
- ctrl.on("agent:spawned", (name, pid) => console.log(`${name} started (${pid})`));
182
- ctrl.on("agent:exited", (name, code) => console.log(`${name} exited (${code})`));
183
498
  ```
184
499
 
185
- ---
186
-
187
- ## API Reference
188
-
189
- ### `ClaudeCodeController`
500
+ ### Selective Permission Control
190
501
 
191
502
  ```typescript
192
- const ctrl = new ClaudeCodeController({
193
- teamName?: string, // default: auto-generated
194
- cwd?: string, // working directory for agents
195
- claudeBinary?: string, // path to claude CLI (default: "claude")
196
- env?: Record<string, string>, // default env vars for all agents
197
- logLevel?: "debug" | "info" | "warn" | "error" | "silent",
503
+ const SAFE_TOOLS = ["Read", "Glob", "Grep", "Task"];
504
+ const NEEDS_REVIEW = ["Bash", "Write", "Edit"];
505
+
506
+ ctrl.on("permission:request", (agent, msg) => {
507
+ if (SAFE_TOOLS.includes(msg.toolName)) {
508
+ ctrl.sendPermissionResponse(agent, msg.requestId, true);
509
+ } else if (NEEDS_REVIEW.includes(msg.toolName)) {
510
+ console.log(`[REVIEW] ${agent} wants to use ${msg.toolName}: ${msg.description}`);
511
+ // Implement your own review logic here
512
+ ctrl.sendPermissionResponse(agent, msg.requestId, true);
513
+ } else {
514
+ ctrl.sendPermissionResponse(agent, msg.requestId, false);
515
+ }
198
516
  });
199
517
  ```
200
518
 
201
- #### Lifecycle
519
+ <br />
202
520
 
203
- | Method | Description |
204
- |---|---|
205
- | `ctrl.init()` | Initialize controller. Must call first. |
206
- | `ctrl.shutdown()` | Graceful shutdown: request stop, wait, kill stragglers, clean up files. |
521
+ ---
207
522
 
208
- #### Agents
523
+ <br />
209
524
 
210
- | Method | Returns | Description |
211
- |---|---|---|
212
- | `ctrl.spawnAgent(opts)` | `AgentHandle` | Spawn a Claude Code agent |
213
- | `ctrl.isAgentRunning(name)` | `boolean` | Check if agent process is alive |
214
- | `ctrl.killAgent(name)` | `void` | Force-kill an agent |
525
+ ## Web Dashboard
215
526
 
216
- `SpawnAgentOptions`:
527
+ A built-in web UI for real-time agent management — no code required.
217
528
 
218
- ```typescript
219
- {
220
- name: string,
221
- type?: "general-purpose" | "Bash" | "Explore" | "Plan" | string,
222
- model?: "sonnet" | "opus" | "haiku" | string,
223
- cwd?: string,
224
- permissions?: string[], // e.g. ["Bash", "Read", "Write"]
225
- env?: Record<string, string>,
226
- }
529
+ ```bash
530
+ cd web && bun install
227
531
  ```
228
532
 
229
- #### Messaging
230
-
231
- | Method | Returns | Description |
232
- |---|---|---|
233
- | `ctrl.send(agent, message, summary?)` | `void` | Send message to an agent |
234
- | `ctrl.broadcast(message, summary?)` | `void` | Send to all agents |
235
- | `ctrl.receive(agent, opts?)` | `InboxMessage[]` | Wait for messages from agent |
236
- | `ctrl.receiveAny(opts?)` | `InboxMessage` | Wait for message from any agent |
237
-
238
- #### Tasks
533
+ **Development:**
239
534
 
240
- | Method | Returns | Description |
241
- |---|---|---|
242
- | `ctrl.createTask({ subject, description, owner? })` | `string` (task ID) | Create a task, optionally assign it |
243
- | `ctrl.assignTask(taskId, agentName)` | `void` | Assign task to agent |
244
- | `ctrl.waitForTask(taskId, timeout?)` | `TaskFile` | Block until task completes |
245
-
246
- #### Protocol
247
-
248
- | Method | Description |
249
- |---|---|
250
- | `ctrl.sendPlanApproval(agent, requestId, approve, feedback?)` | Respond to plan approval request |
251
- | `ctrl.sendPermissionResponse(agent, requestId, approve)` | Respond to permission request |
252
- | `ctrl.sendShutdownRequest(agent)` | Request graceful shutdown |
253
-
254
- ### `AgentHandle`
255
-
256
- Convenience wrapper returned by `spawnAgent()`.
257
-
258
- ```typescript
259
- await agent.send(message, summary?) // Send a message
260
- await agent.receive(opts?) // Wait for response text
261
- await agent.ask(question, opts?) // Send + receive in one call
262
- await agent.shutdown() // Request graceful shutdown
263
- await agent.kill() // Force kill
264
- agent.isRunning // Check if alive (getter)
265
- agent.name // Agent name
266
- agent.pid // Process PID
267
-
268
- // Async iterator for streaming events
269
- for await (const msg of agent.events()) {
270
- console.log(msg.text);
271
- }
535
+ ```bash
536
+ bun run dev # backend on :3456
537
+ bun run dev:vite # frontend on :5174
272
538
  ```
273
539
 
274
- ### Events
540
+ **Production:**
275
541
 
276
- ```typescript
277
- ctrl.on("message", (agentName, message) => { ... });
278
- ctrl.on("idle", (agentName) => { ... });
279
- ctrl.on("agent:spawned", (agentName, pid) => { ... });
280
- ctrl.on("agent:exited", (agentName, code) => { ... });
281
- ctrl.on("task:completed", (task) => { ... });
282
- ctrl.on("shutdown:approved", (agentName, message) => { ... });
283
- ctrl.on("plan:approval_request",(agentName, message) => { ... });
284
- ctrl.on("permission:request", (agentName, message) => { ... });
285
- ctrl.on("error", (error) => { ... });
542
+ ```bash
543
+ bun run build && bun run start # everything on :3456
286
544
  ```
287
545
 
288
- ---
289
-
290
- ## How It Works
546
+ The dashboard gives you:
291
547
 
292
- Claude Code has an internal "teammate" system that communicates through the filesystem:
548
+ - **Session management** Initialize and shut down the controller
549
+ - **Agent spawning** — Configure name, type, model, and environment variables
550
+ - **Live message feed** — Real-time messages via WebSocket
551
+ - **Approval prompts** — Interactive plan and permission approval banners
552
+ - **Agent controls** — Shutdown or kill agents individually
293
553
 
294
- | Component | Path | Purpose |
295
- |---|---|---|
296
- | Teams | `~/.claude/teams/{name}/config.json` | Team membership & config |
297
- | Inboxes | `~/.claude/teams/{name}/inboxes/{agent}.json` | Message passing |
298
- | Tasks | `~/.claude/tasks/{name}/{id}.json` | Task tracking |
299
-
300
- This library creates those files, spawns real Claude Code CLI processes via PTY, and communicates with them through the inbox protocol. Agents think they're in a normal team and respond naturally.
301
-
302
- ```
303
- ClaudeCodeController
304
- ├── TeamManager — team config CRUD
305
- ├── TaskManager — task lifecycle management
306
- ├── ProcessManager — PTY-based process spawning
307
- ├── InboxPoller — polls controller inbox for agent messages
308
- └── AgentHandle — per-agent convenience wrapper
309
- ```
554
+ <br />
310
555
 
311
556
  ---
312
557
 
313
- ## Web UI
558
+ <br />
314
559
 
315
- A built-in web dashboard lets you spawn agents, see their messages in real-time, and handle plan/permission approvals — all from your browser.
560
+ ## How It Works
316
561
 
317
- ![Claude Code Controller UI](screenshot.png)
562
+ Claude Code has an internal "teammate" protocol that uses the filesystem for communication. This library creates the required files, spawns real Claude Code CLI processes via PTY, and communicates with them through inbox files. Agents think they're in a normal team and behave naturally.
318
563
 
319
- ```bash
320
- cd web
321
- bun install
322
564
  ```
323
-
324
- **Development** (two terminals):
325
-
326
- ```bash
327
- bun run dev # backend on :3456
328
- bun run dev:vite # frontend on :5174 (proxies API to :3456)
565
+ ~/.claude/
566
+ ├── teams/{teamName}/
567
+ │ ├── config.json # Team membership & config
568
+ │ └── inboxes/
569
+ │ ├── controller.json # Messages TO controller FROM agents
570
+ │ ├── agent-1.json # Messages TO agent-1 FROM controller
571
+ │ └── agent-2.json # Messages TO agent-2 FROM controller
572
+ └── tasks/{teamName}/
573
+ ├── 1.json # Task files
574
+ └── 2.json
329
575
  ```
330
576
 
331
- Open http://localhost:5174
332
-
333
- **Production:**
577
+ **Architecture:**
334
578
 
335
- ```bash
336
- bun run build # build frontend
337
- bun run start # serve everything on :3456
579
+ ```
580
+ ClaudeCodeController
581
+ ├── TeamManager Team config CRUD
582
+ ├── TaskManager → Task lifecycle management
583
+ ├── ProcessManager → PTY-based process spawning
584
+ ├── InboxPoller → Polls controller inbox for agent messages
585
+ └── AgentHandle[] → Per-agent convenience wrappers
338
586
  ```
339
587
 
340
- Open http://localhost:3456
588
+ **The flow:**
341
589
 
342
- The UI provides:
343
- - **Session management** — initialize/shutdown the controller
344
- - **Agent spawning** — configure name, type, model, and environment variables
345
- - **Live message feed** — real-time messages via WebSocket
346
- - **Approval prompts** — interactive plan and permission approval banners
347
- - **Agent controls** — shutdown or kill agents individually
590
+ 1. **Spawn** — Calls `claude --teammate-mode auto --agent-id name@team ...` via a PTY wrapper
591
+ 2. **Register** — Agent is registered in the team config with its role, model, and permissions
592
+ 3. **Communicate** — Controller writes to `inboxes/{agent}.json`, agent writes to `inboxes/controller.json`
593
+ 4. **Poll** — InboxPoller reads the controller inbox every 500ms and fires events
594
+ 5. **Lock** — All file operations use `proper-lockfile` to prevent corruption from concurrent access
595
+
596
+ <br />
348
597
 
349
598
  ---
350
599
 
600
+ <br />
601
+
351
602
  ## Development
352
603
 
353
604
  ```bash
@@ -357,17 +608,25 @@ bun run typecheck # type check
357
608
  bun run build # build for distribution
358
609
  ```
359
610
 
611
+ <br />
612
+
360
613
  ---
361
614
 
615
+ <br />
616
+
362
617
  ## Roadmap
363
618
 
364
- - **Tmux session per agent** — Spawn each agent in its own tmux pane so you can attach to it (`tmux attach -t <agent>`) and watch it work in real time: tool calls, file edits, reasoning — like watching someone use Claude Code interactively
365
- - **Task management in the UI** — Create, assign, and track tasks from the web dashboard
366
- - **Agent-to-agent messaging** — Let agents communicate directly with each other
367
- - **Persistent sessions** — Resume a team session after server restart
619
+ - **Tmux session per agent** — Spawn each agent in its own tmux pane. Attach with `tmux attach -t <agent>` and watch it work: tool calls, file edits, reasoning — like watching someone use Claude Code interactively.
620
+ - **Task management in the UI** — Create, assign, and track tasks from the web dashboard.
621
+ - **Agent-to-agent messaging** — Let agents communicate directly with each other.
622
+ - **Persistent sessions** — Resume a team session after server restart.
623
+
624
+ <br />
368
625
 
369
626
  ---
370
627
 
628
+ <br />
629
+
371
630
  ## License
372
631
 
373
632
  MIT