claude-code-controller 0.4.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -134,6 +134,8 @@ var import_hono = require("hono");
134
134
  var import_node_events = require("events");
135
135
  var import_node_child_process3 = require("child_process");
136
136
  var import_node_crypto2 = require("crypto");
137
+ var import_node_fs4 = require("fs");
138
+ var import_node_path3 = require("path");
137
139
 
138
140
  // src/team-manager.ts
139
141
  var import_promises = require("fs/promises");
@@ -466,6 +468,8 @@ if pid == 0:
466
468
  else:
467
469
  signal.signal(signal.SIGTERM, lambda *a: (os.kill(pid, signal.SIGTERM), sys.exit(0)))
468
470
  signal.signal(signal.SIGINT, lambda *a: (os.kill(pid, signal.SIGTERM), sys.exit(0)))
471
+ buf = b""
472
+ trust_sent = False
469
473
  try:
470
474
  while True:
471
475
  r, _, _ = select.select([fd, 0], [], [], 1.0)
@@ -475,6 +479,12 @@ else:
475
479
  if not data:
476
480
  break
477
481
  os.write(1, data)
482
+ if not trust_sent:
483
+ buf += data
484
+ if b"Yes" in buf and b"trust" in buf:
485
+ os.write(fd, b"\\r")
486
+ trust_sent = True
487
+ buf = b""
478
488
  except OSError:
479
489
  break
480
490
  if 0 in r:
@@ -500,6 +510,7 @@ else:
500
510
  stdio: ["pipe", "pipe", "pipe"],
501
511
  env: {
502
512
  ...process.env,
513
+ CLAUDECODE: "1",
503
514
  CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS: "1",
504
515
  ...opts.env
505
516
  }
@@ -817,7 +828,10 @@ function createLogger(level = "info") {
817
828
  var PROTOCOL_ONLY_TYPES = /* @__PURE__ */ new Set([
818
829
  "shutdown_approved",
819
830
  "plan_approval_response",
820
- "permission_response"
831
+ "permission_response",
832
+ "task_completed",
833
+ "sandbox_permission_request",
834
+ "sandbox_permission_response"
821
835
  ]);
822
836
  var AGENT_COLORS = [
823
837
  "#00FF00",
@@ -925,12 +939,17 @@ var ClaudeCodeController = class extends import_node_events.EventEmitter {
925
939
  name: opts.name,
926
940
  agentType: opts.type || "general-purpose",
927
941
  model: opts.model,
942
+ prompt: opts.prompt,
943
+ color,
944
+ planModeRequired: false,
945
+ backendType: "in-process",
928
946
  joinedAt: Date.now(),
929
947
  tmuxPaneId: "",
930
948
  cwd,
931
949
  subscriptions: []
932
950
  };
933
951
  await this.team.addMember(member);
952
+ this.ensureWorkspaceTrusted(cwd);
934
953
  const env = Object.keys(this.defaultEnv).length > 0 || opts.env ? { ...this.defaultEnv, ...opts.env } : void 0;
935
954
  const proc = this.processes.spawn({
936
955
  teamName: this.teamName,
@@ -1167,7 +1186,7 @@ var ClaudeCodeController = class extends import_node_events.EventEmitter {
1167
1186
  const { raw, parsed } = event;
1168
1187
  switch (parsed.type) {
1169
1188
  case "idle_notification":
1170
- this.emit("idle", raw.from);
1189
+ this.emit("idle", raw.from, parsed);
1171
1190
  break;
1172
1191
  case "shutdown_approved":
1173
1192
  this.log.info(
@@ -1187,6 +1206,9 @@ var ClaudeCodeController = class extends import_node_events.EventEmitter {
1187
1206
  );
1188
1207
  this.emit("permission:request", raw.from, parsed);
1189
1208
  break;
1209
+ case "task_completed":
1210
+ this.emit("message", raw.from, raw);
1211
+ break;
1190
1212
  case "plain_text":
1191
1213
  this.emit("message", raw.from, raw);
1192
1214
  break;
@@ -1195,6 +1217,19 @@ var ClaudeCodeController = class extends import_node_events.EventEmitter {
1195
1217
  }
1196
1218
  }
1197
1219
  }
1220
+ /**
1221
+ * Ensure the agent's cwd has a .claude/settings.local.json so the
1222
+ * CLI skips the interactive workspace trust prompt.
1223
+ */
1224
+ ensureWorkspaceTrusted(cwd) {
1225
+ const claudeDir = (0, import_node_path3.join)(cwd, ".claude");
1226
+ const settingsPath = (0, import_node_path3.join)(claudeDir, "settings.local.json");
1227
+ if (!(0, import_node_fs4.existsSync)(settingsPath)) {
1228
+ (0, import_node_fs4.mkdirSync)(claudeDir, { recursive: true });
1229
+ (0, import_node_fs4.writeFileSync)(settingsPath, "{}\n");
1230
+ this.log.debug(`Created ${settingsPath} for workspace trust`);
1231
+ }
1232
+ }
1198
1233
  ensureInitialized() {
1199
1234
  if (!this.initialized) {
1200
1235
  throw new Error(
@@ -1243,7 +1278,7 @@ function waitForReady(controller, agentName, timeoutMs = 15e3) {
1243
1278
  )
1244
1279
  );
1245
1280
  }, timeoutMs);
1246
- const onReady = (name) => {
1281
+ const onReady = (name, ..._rest) => {
1247
1282
  if (name === agentName && !settled) {
1248
1283
  settled = true;
1249
1284
  cleanup();
@@ -1374,14 +1409,22 @@ var Agent = class _Agent extends import_node_events2.EventEmitter {
1374
1409
  this.ensureNotDisposed();
1375
1410
  const timeout = opts?.timeout ?? 12e4;
1376
1411
  const responsePromise = new Promise((resolve, reject) => {
1412
+ let gotMessage = false;
1377
1413
  const timer = setTimeout(() => {
1378
1414
  cleanup();
1379
1415
  reject(new Error(`Timeout (${timeout}ms) waiting for response`));
1380
1416
  }, timeout);
1381
1417
  const onMsg = (text) => {
1418
+ gotMessage = true;
1382
1419
  cleanup();
1383
1420
  resolve(text);
1384
1421
  };
1422
+ const onIdle = () => {
1423
+ if (!gotMessage) {
1424
+ cleanup();
1425
+ resolve("");
1426
+ }
1427
+ };
1385
1428
  const onExit = (code) => {
1386
1429
  cleanup();
1387
1430
  reject(new Error(`Agent exited (code=${code}) before responding`));
@@ -1389,9 +1432,11 @@ var Agent = class _Agent extends import_node_events2.EventEmitter {
1389
1432
  const cleanup = () => {
1390
1433
  clearTimeout(timer);
1391
1434
  this.removeListener("message", onMsg);
1435
+ this.removeListener("idle", onIdle);
1392
1436
  this.removeListener("exit", onExit);
1393
1437
  };
1394
1438
  this.on("message", onMsg);
1439
+ this.on("idle", onIdle);
1395
1440
  this.on("exit", onExit);
1396
1441
  });
1397
1442
  const wrapped = `${question}
@@ -1410,14 +1455,22 @@ IMPORTANT: You MUST send your complete answer back using the SendMessage tool. D
1410
1455
  this.ensureNotDisposed();
1411
1456
  const timeout = opts?.timeout ?? 12e4;
1412
1457
  return new Promise((resolve, reject) => {
1458
+ let gotMessage = false;
1413
1459
  const timer = setTimeout(() => {
1414
1460
  cleanup();
1415
1461
  reject(new Error(`Timeout (${timeout}ms) waiting for response`));
1416
1462
  }, timeout);
1417
1463
  const onMsg = (text) => {
1464
+ gotMessage = true;
1418
1465
  cleanup();
1419
1466
  resolve(text);
1420
1467
  };
1468
+ const onIdle = () => {
1469
+ if (!gotMessage) {
1470
+ cleanup();
1471
+ resolve("");
1472
+ }
1473
+ };
1421
1474
  const onExit = (code) => {
1422
1475
  cleanup();
1423
1476
  reject(new Error(`Agent exited (code=${code}) before responding`));
@@ -1425,9 +1478,11 @@ IMPORTANT: You MUST send your complete answer back using the SendMessage tool. D
1425
1478
  const cleanup = () => {
1426
1479
  clearTimeout(timer);
1427
1480
  this.removeListener("message", onMsg);
1481
+ this.removeListener("idle", onIdle);
1428
1482
  this.removeListener("exit", onExit);
1429
1483
  };
1430
1484
  this.on("message", onMsg);
1485
+ this.on("idle", onIdle);
1431
1486
  this.on("exit", onExit);
1432
1487
  });
1433
1488
  }
@@ -1458,7 +1513,7 @@ IMPORTANT: You MUST send your complete answer back using the SendMessage tool. D
1458
1513
  const onMessage = (name, msg) => {
1459
1514
  if (name === agentName) this.emit("message", msg.text);
1460
1515
  };
1461
- const onIdle = (name) => {
1516
+ const onIdle = (name, _details) => {
1462
1517
  if (name === agentName) this.emit("idle");
1463
1518
  };
1464
1519
  const onPermission = (name, parsed) => {