claude-code-controller 0.1.1 → 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,41 +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
 
9
- ![Claude Code Controller UI](screenshot.png)
19
+ <br />
10
20
 
11
- No Agent SDK. No `-p` mode. Full programmatic control over real Claude Code instances through the internal filesystem-based teams protocol.
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 />
12
25
 
13
26
  ---
14
27
 
15
- ## Why?
28
+ <br />
29
+
30
+ ## Why this instead of the Agent SDK?
16
31
 
17
- 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.
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.
18
33
 
19
- 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:
34
+ That means:
20
35
 
21
- - **Real Claude Code agents** with all their tools (Bash, Read, Write, Glob, Grep...)
22
- - **Multi-agent orchestration** with message passing and task assignment
23
- - **Full event system** for plan approvals, permission requests, shutdowns
24
- - **Custom environment routing** to use any API provider
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 />
25
43
 
26
44
  ---
27
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."}'
62
+ ```
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
77
+ ```
78
+
79
+ <br />
80
+
81
+ ---
82
+
83
+ <br />
84
+
85
+ ## Features
86
+
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.
95
+
96
+ <br />
97
+
98
+ ---
99
+
100
+ <br />
101
+
28
102
  ## Install
29
103
 
30
104
  ```bash
31
105
  npm install claude-code-controller
32
106
  ```
33
107
 
34
- > **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 />
35
111
 
36
112
  ---
37
113
 
38
- ## Quick Start
114
+ <br />
115
+
116
+ ## Quick Start — 30 seconds to your first agent
39
117
 
40
118
  ```typescript
41
119
  import { ClaudeCodeController } from "claude-code-controller";
@@ -45,14 +123,11 @@ await ctrl.init();
45
123
 
46
124
  const agent = await ctrl.spawnAgent({
47
125
  name: "coder",
48
- type: "general-purpose",
49
126
  model: "sonnet",
50
127
  });
51
128
 
52
- // Wait for the agent to boot up
53
129
  await new Promise((r) => setTimeout(r, 10_000));
54
130
 
55
- // Ask something and get the answer back
56
131
  const answer = await agent.ask(
57
132
  "Read package.json and tell me the project name. Reply using SendMessage.",
58
133
  { timeout: 60_000 }
@@ -62,26 +137,302 @@ console.log(answer);
62
137
  await ctrl.shutdown();
63
138
  ```
64
139
 
140
+ <br />
141
+
65
142
  ---
66
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
+
416
+ ---
417
+
418
+ <br />
419
+
67
420
  ## Real-World Examples
68
421
 
69
- ### Multi-Agent Code Review
422
+ ### Parallel Code Review
70
423
 
71
424
  ```typescript
72
425
  const ctrl = new ClaudeCodeController({ teamName: "review" });
73
426
  await ctrl.init();
74
427
 
75
- // Spawn specialized reviewers in parallel
76
428
  const [security, perf, style] = await Promise.all([
77
- ctrl.spawnAgent({ name: "security", type: "general-purpose", model: "opus" }),
78
- ctrl.spawnAgent({ name: "perf", type: "general-purpose", model: "sonnet" }),
79
- 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" }),
80
432
  ]);
81
433
 
82
434
  await new Promise((r) => setTimeout(r, 12_000));
83
435
 
84
- // Give each reviewer a different focus
85
436
  const reviews = await Promise.all([
86
437
  security.ask("Review src/ for security vulnerabilities. Reply with SendMessage."),
87
438
  perf.ask("Review src/ for performance issues. Reply with SendMessage."),
@@ -104,14 +455,12 @@ await ctrl.init();
104
455
  const worker = await ctrl.spawnAgent({ name: "worker", model: "sonnet" });
105
456
  await new Promise((r) => setTimeout(r, 10_000));
106
457
 
107
- // Create and assign a task
108
458
  const taskId = await ctrl.createTask({
109
459
  subject: "Add input validation",
110
460
  description: "Add zod validation to all API route handlers in src/routes/",
111
461
  owner: "worker",
112
462
  });
113
463
 
114
- // Wait for the agent to complete it
115
464
  const result = await ctrl.waitForTask(taskId, 120_000);
116
465
  console.log(`Task ${result.status}: ${result.subject}`);
117
466
 
@@ -120,8 +469,6 @@ await ctrl.shutdown();
120
469
 
121
470
  ### Custom API Provider
122
471
 
123
- Route agents through any OpenAI-compatible endpoint:
124
-
125
472
  ```typescript
126
473
  const ctrl = new ClaudeCodeController({
127
474
  teamName: "custom",
@@ -130,208 +477,128 @@ const ctrl = new ClaudeCodeController({
130
477
  ANTHROPIC_AUTH_TOKEN: process.env.MY_API_KEY!,
131
478
  },
132
479
  });
133
- ```
134
-
135
- Per-agent overrides take precedence:
136
480
 
137
- ```typescript
481
+ // Per-agent overrides
138
482
  const agent = await ctrl.spawnAgent({
139
483
  name: "worker",
140
484
  env: { ANTHROPIC_AUTH_TOKEN: "different-key-for-this-agent" },
141
485
  });
142
486
  ```
143
487
 
144
- ### Event-Driven Control
488
+ ### Auto-Approve Everything (YOLO mode)
145
489
 
146
490
  ```typescript
147
- const ctrl = new ClaudeCodeController({ teamName: "events" });
148
- await ctrl.init();
149
-
150
- // Auto-approve all plan requests
151
491
  ctrl.on("plan:approval_request", (agent, msg) => {
152
492
  ctrl.sendPlanApproval(agent, msg.requestId, true);
153
493
  });
154
494
 
155
- // Auto-approve safe tool use, reject dangerous ones
156
495
  ctrl.on("permission:request", (agent, msg) => {
157
- const safe = ["Read", "Glob", "Grep"].includes(msg.toolName);
158
- ctrl.sendPermissionResponse(agent, msg.requestId, safe);
159
- });
160
-
161
- // React to agent messages
162
- ctrl.on("message", (agent, msg) => {
163
- console.log(`[${agent}] ${msg.text}`);
496
+ ctrl.sendPermissionResponse(agent, msg.requestId, true);
164
497
  });
165
-
166
- // Track lifecycle
167
- ctrl.on("agent:spawned", (name, pid) => console.log(`${name} started (${pid})`));
168
- ctrl.on("agent:exited", (name, code) => console.log(`${name} exited (${code})`));
169
498
  ```
170
499
 
171
- ---
172
-
173
- ## API Reference
174
-
175
- ### `ClaudeCodeController`
500
+ ### Selective Permission Control
176
501
 
177
502
  ```typescript
178
- const ctrl = new ClaudeCodeController({
179
- teamName?: string, // default: auto-generated
180
- cwd?: string, // working directory for agents
181
- claudeBinary?: string, // path to claude CLI (default: "claude")
182
- env?: Record<string, string>, // default env vars for all agents
183
- 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
+ }
184
516
  });
185
517
  ```
186
518
 
187
- #### Lifecycle
519
+ <br />
188
520
 
189
- | Method | Description |
190
- |---|---|
191
- | `ctrl.init()` | Initialize controller. Must call first. |
192
- | `ctrl.shutdown()` | Graceful shutdown: request stop, wait, kill stragglers, clean up files. |
521
+ ---
193
522
 
194
- #### Agents
523
+ <br />
195
524
 
196
- | Method | Returns | Description |
197
- |---|---|---|
198
- | `ctrl.spawnAgent(opts)` | `AgentHandle` | Spawn a Claude Code agent |
199
- | `ctrl.isAgentRunning(name)` | `boolean` | Check if agent process is alive |
200
- | `ctrl.killAgent(name)` | `void` | Force-kill an agent |
525
+ ## Web Dashboard
201
526
 
202
- `SpawnAgentOptions`:
527
+ A built-in web UI for real-time agent management — no code required.
203
528
 
204
- ```typescript
205
- {
206
- name: string,
207
- type?: "general-purpose" | "Bash" | "Explore" | "Plan" | string,
208
- model?: "sonnet" | "opus" | "haiku" | string,
209
- cwd?: string,
210
- permissions?: string[], // e.g. ["Bash", "Read", "Write"]
211
- env?: Record<string, string>,
212
- }
529
+ ```bash
530
+ cd web && bun install
213
531
  ```
214
532
 
215
- #### Messaging
216
-
217
- | Method | Returns | Description |
218
- |---|---|---|
219
- | `ctrl.send(agent, message, summary?)` | `void` | Send message to an agent |
220
- | `ctrl.broadcast(message, summary?)` | `void` | Send to all agents |
221
- | `ctrl.receive(agent, opts?)` | `InboxMessage[]` | Wait for messages from agent |
222
- | `ctrl.receiveAny(opts?)` | `InboxMessage` | Wait for message from any agent |
533
+ **Development:**
223
534
 
224
- #### Tasks
225
-
226
- | Method | Returns | Description |
227
- |---|---|---|
228
- | `ctrl.createTask({ subject, description, owner? })` | `string` (task ID) | Create a task, optionally assign it |
229
- | `ctrl.assignTask(taskId, agentName)` | `void` | Assign task to agent |
230
- | `ctrl.waitForTask(taskId, timeout?)` | `TaskFile` | Block until task completes |
231
-
232
- #### Protocol
233
-
234
- | Method | Description |
235
- |---|---|
236
- | `ctrl.sendPlanApproval(agent, requestId, approve, feedback?)` | Respond to plan approval request |
237
- | `ctrl.sendPermissionResponse(agent, requestId, approve)` | Respond to permission request |
238
- | `ctrl.sendShutdownRequest(agent)` | Request graceful shutdown |
239
-
240
- ### `AgentHandle`
535
+ ```bash
536
+ bun run dev # backend on :3456
537
+ bun run dev:vite # frontend on :5174
538
+ ```
241
539
 
242
- Convenience wrapper returned by `spawnAgent()`.
540
+ **Production:**
243
541
 
244
- ```typescript
245
- await agent.send(message, summary?) // Send a message
246
- await agent.receive(opts?) // Wait for response text
247
- await agent.ask(question, opts?) // Send + receive in one call
248
- await agent.shutdown() // Request graceful shutdown
249
- await agent.kill() // Force kill
250
- agent.isRunning // Check if alive (getter)
251
- agent.name // Agent name
252
- agent.pid // Process PID
253
-
254
- // Async iterator for streaming events
255
- for await (const msg of agent.events()) {
256
- console.log(msg.text);
257
- }
542
+ ```bash
543
+ bun run build && bun run start # everything on :3456
258
544
  ```
259
545
 
260
- ### Events
546
+ The dashboard gives you:
261
547
 
262
- ```typescript
263
- ctrl.on("message", (agentName, message) => { ... });
264
- ctrl.on("idle", (agentName) => { ... });
265
- ctrl.on("agent:spawned", (agentName, pid) => { ... });
266
- ctrl.on("agent:exited", (agentName, code) => { ... });
267
- ctrl.on("task:completed", (task) => { ... });
268
- ctrl.on("shutdown:approved", (agentName, message) => { ... });
269
- ctrl.on("plan:approval_request",(agentName, message) => { ... });
270
- ctrl.on("permission:request", (agentName, message) => { ... });
271
- ctrl.on("error", (error) => { ... });
272
- ```
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
273
553
 
274
- ---
554
+ <br />
275
555
 
276
- ## How It Works
556
+ ---
277
557
 
278
- Claude Code has an internal "teammate" system that communicates through the filesystem:
558
+ <br />
279
559
 
280
- | Component | Path | Purpose |
281
- |---|---|---|
282
- | Teams | `~/.claude/teams/{name}/config.json` | Team membership & config |
283
- | Inboxes | `~/.claude/teams/{name}/inboxes/{agent}.json` | Message passing |
284
- | Tasks | `~/.claude/tasks/{name}/{id}.json` | Task tracking |
560
+ ## How It Works
285
561
 
286
- 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.
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.
287
563
 
288
564
  ```
289
- ClaudeCodeController
290
- ├── TeamManager — team config CRUD
291
- ├── TaskManager — task lifecycle management
292
- ├── ProcessManager — PTY-based process spawning
293
- ├── InboxPoller — polls controller inbox for agent messages
294
- └── AgentHandle — per-agent convenience wrapper
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
295
575
  ```
296
576
 
297
- ---
298
-
299
- ## Web UI
300
-
301
- A built-in web dashboard lets you spawn agents, see their messages in real-time, and handle plan/permission approvals — all from your browser.
577
+ **Architecture:**
302
578
 
303
- ```bash
304
- cd web
305
- bun install
306
579
  ```
307
-
308
- **Development** (two terminals):
309
-
310
- ```bash
311
- bun run dev # backend on :3456
312
- bun run dev:vite # frontend on :5174 (proxies API to :3456)
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
313
586
  ```
314
587
 
315
- Open http://localhost:5174
588
+ **The flow:**
316
589
 
317
- **Production:**
318
-
319
- ```bash
320
- bun run build # build frontend
321
- bun run start # serve everything on :3456
322
- ```
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
323
595
 
324
- Open http://localhost:3456
325
-
326
- The UI provides:
327
- - **Session management** — initialize/shutdown the controller
328
- - **Agent spawning** — configure name, type, model, and environment variables
329
- - **Live message feed** — real-time messages via WebSocket
330
- - **Approval prompts** — interactive plan and permission approval banners
331
- - **Agent controls** — shutdown or kill agents individually
596
+ <br />
332
597
 
333
598
  ---
334
599
 
600
+ <br />
601
+
335
602
  ## Development
336
603
 
337
604
  ```bash
@@ -341,17 +608,25 @@ bun run typecheck # type check
341
608
  bun run build # build for distribution
342
609
  ```
343
610
 
611
+ <br />
612
+
344
613
  ---
345
614
 
615
+ <br />
616
+
346
617
  ## Roadmap
347
618
 
348
- - **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
349
- - **Task management in the UI** — Create, assign, and track tasks from the web dashboard
350
- - **Agent-to-agent messaging** — Let agents communicate directly with each other
351
- - **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 />
352
625
 
353
626
  ---
354
627
 
628
+ <br />
629
+
355
630
  ## License
356
631
 
357
632
  MIT