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.
package/dist/index.js CHANGED
@@ -6,6 +6,8 @@ import { randomUUID as randomUUID3 } from "crypto";
6
6
  import { EventEmitter } from "events";
7
7
  import { execSync as execSync2 } from "child_process";
8
8
  import { randomUUID as randomUUID2 } from "crypto";
9
+ import { mkdirSync, existsSync as existsSync4, writeFileSync } from "fs";
10
+ import { join as join2 } from "path";
9
11
 
10
12
  // src/team-manager.ts
11
13
  import { readFile, writeFile, mkdir, rm } from "fs/promises";
@@ -338,6 +340,8 @@ if pid == 0:
338
340
  else:
339
341
  signal.signal(signal.SIGTERM, lambda *a: (os.kill(pid, signal.SIGTERM), sys.exit(0)))
340
342
  signal.signal(signal.SIGINT, lambda *a: (os.kill(pid, signal.SIGTERM), sys.exit(0)))
343
+ buf = b""
344
+ trust_sent = False
341
345
  try:
342
346
  while True:
343
347
  r, _, _ = select.select([fd, 0], [], [], 1.0)
@@ -347,6 +351,12 @@ else:
347
351
  if not data:
348
352
  break
349
353
  os.write(1, data)
354
+ if not trust_sent:
355
+ buf += data
356
+ if b"Yes" in buf and b"trust" in buf:
357
+ os.write(fd, b"\\r")
358
+ trust_sent = True
359
+ buf = b""
350
360
  except OSError:
351
361
  break
352
362
  if 0 in r:
@@ -372,6 +382,7 @@ else:
372
382
  stdio: ["pipe", "pipe", "pipe"],
373
383
  env: {
374
384
  ...process.env,
385
+ CLAUDECODE: "1",
375
386
  CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS: "1",
376
387
  ...opts.env
377
388
  }
@@ -705,7 +716,10 @@ var silentLogger = {
705
716
  var PROTOCOL_ONLY_TYPES = /* @__PURE__ */ new Set([
706
717
  "shutdown_approved",
707
718
  "plan_approval_response",
708
- "permission_response"
719
+ "permission_response",
720
+ "task_completed",
721
+ "sandbox_permission_request",
722
+ "sandbox_permission_response"
709
723
  ]);
710
724
  var AGENT_COLORS = [
711
725
  "#00FF00",
@@ -813,12 +827,17 @@ var ClaudeCodeController = class extends EventEmitter {
813
827
  name: opts.name,
814
828
  agentType: opts.type || "general-purpose",
815
829
  model: opts.model,
830
+ prompt: opts.prompt,
831
+ color,
832
+ planModeRequired: false,
833
+ backendType: "in-process",
816
834
  joinedAt: Date.now(),
817
835
  tmuxPaneId: "",
818
836
  cwd,
819
837
  subscriptions: []
820
838
  };
821
839
  await this.team.addMember(member);
840
+ this.ensureWorkspaceTrusted(cwd);
822
841
  const env = Object.keys(this.defaultEnv).length > 0 || opts.env ? { ...this.defaultEnv, ...opts.env } : void 0;
823
842
  const proc = this.processes.spawn({
824
843
  teamName: this.teamName,
@@ -1055,7 +1074,7 @@ var ClaudeCodeController = class extends EventEmitter {
1055
1074
  const { raw, parsed } = event;
1056
1075
  switch (parsed.type) {
1057
1076
  case "idle_notification":
1058
- this.emit("idle", raw.from);
1077
+ this.emit("idle", raw.from, parsed);
1059
1078
  break;
1060
1079
  case "shutdown_approved":
1061
1080
  this.log.info(
@@ -1075,6 +1094,9 @@ var ClaudeCodeController = class extends EventEmitter {
1075
1094
  );
1076
1095
  this.emit("permission:request", raw.from, parsed);
1077
1096
  break;
1097
+ case "task_completed":
1098
+ this.emit("message", raw.from, raw);
1099
+ break;
1078
1100
  case "plain_text":
1079
1101
  this.emit("message", raw.from, raw);
1080
1102
  break;
@@ -1083,6 +1105,19 @@ var ClaudeCodeController = class extends EventEmitter {
1083
1105
  }
1084
1106
  }
1085
1107
  }
1108
+ /**
1109
+ * Ensure the agent's cwd has a .claude/settings.local.json so the
1110
+ * CLI skips the interactive workspace trust prompt.
1111
+ */
1112
+ ensureWorkspaceTrusted(cwd) {
1113
+ const claudeDir = join2(cwd, ".claude");
1114
+ const settingsPath = join2(claudeDir, "settings.local.json");
1115
+ if (!existsSync4(settingsPath)) {
1116
+ mkdirSync(claudeDir, { recursive: true });
1117
+ writeFileSync(settingsPath, "{}\n");
1118
+ this.log.debug(`Created ${settingsPath} for workspace trust`);
1119
+ }
1120
+ }
1086
1121
  ensureInitialized() {
1087
1122
  if (!this.initialized) {
1088
1123
  throw new Error(
@@ -1129,7 +1164,7 @@ function waitForReady(controller, agentName, timeoutMs = 15e3) {
1129
1164
  )
1130
1165
  );
1131
1166
  }, timeoutMs);
1132
- const onReady = (name) => {
1167
+ const onReady = (name, ..._rest) => {
1133
1168
  if (name === agentName && !settled) {
1134
1169
  settled = true;
1135
1170
  cleanup();
@@ -1260,14 +1295,22 @@ var Agent = class _Agent extends EventEmitter2 {
1260
1295
  this.ensureNotDisposed();
1261
1296
  const timeout = opts?.timeout ?? 12e4;
1262
1297
  const responsePromise = new Promise((resolve, reject) => {
1298
+ let gotMessage = false;
1263
1299
  const timer = setTimeout(() => {
1264
1300
  cleanup();
1265
1301
  reject(new Error(`Timeout (${timeout}ms) waiting for response`));
1266
1302
  }, timeout);
1267
1303
  const onMsg = (text) => {
1304
+ gotMessage = true;
1268
1305
  cleanup();
1269
1306
  resolve(text);
1270
1307
  };
1308
+ const onIdle = () => {
1309
+ if (!gotMessage) {
1310
+ cleanup();
1311
+ resolve("");
1312
+ }
1313
+ };
1271
1314
  const onExit = (code) => {
1272
1315
  cleanup();
1273
1316
  reject(new Error(`Agent exited (code=${code}) before responding`));
@@ -1275,9 +1318,11 @@ var Agent = class _Agent extends EventEmitter2 {
1275
1318
  const cleanup = () => {
1276
1319
  clearTimeout(timer);
1277
1320
  this.removeListener("message", onMsg);
1321
+ this.removeListener("idle", onIdle);
1278
1322
  this.removeListener("exit", onExit);
1279
1323
  };
1280
1324
  this.on("message", onMsg);
1325
+ this.on("idle", onIdle);
1281
1326
  this.on("exit", onExit);
1282
1327
  });
1283
1328
  const wrapped = `${question}
@@ -1296,14 +1341,22 @@ IMPORTANT: You MUST send your complete answer back using the SendMessage tool. D
1296
1341
  this.ensureNotDisposed();
1297
1342
  const timeout = opts?.timeout ?? 12e4;
1298
1343
  return new Promise((resolve, reject) => {
1344
+ let gotMessage = false;
1299
1345
  const timer = setTimeout(() => {
1300
1346
  cleanup();
1301
1347
  reject(new Error(`Timeout (${timeout}ms) waiting for response`));
1302
1348
  }, timeout);
1303
1349
  const onMsg = (text) => {
1350
+ gotMessage = true;
1304
1351
  cleanup();
1305
1352
  resolve(text);
1306
1353
  };
1354
+ const onIdle = () => {
1355
+ if (!gotMessage) {
1356
+ cleanup();
1357
+ resolve("");
1358
+ }
1359
+ };
1307
1360
  const onExit = (code) => {
1308
1361
  cleanup();
1309
1362
  reject(new Error(`Agent exited (code=${code}) before responding`));
@@ -1311,9 +1364,11 @@ IMPORTANT: You MUST send your complete answer back using the SendMessage tool. D
1311
1364
  const cleanup = () => {
1312
1365
  clearTimeout(timer);
1313
1366
  this.removeListener("message", onMsg);
1367
+ this.removeListener("idle", onIdle);
1314
1368
  this.removeListener("exit", onExit);
1315
1369
  };
1316
1370
  this.on("message", onMsg);
1371
+ this.on("idle", onIdle);
1317
1372
  this.on("exit", onExit);
1318
1373
  });
1319
1374
  }
@@ -1344,7 +1399,7 @@ IMPORTANT: You MUST send your complete answer back using the SendMessage tool. D
1344
1399
  const onMessage = (name, msg) => {
1345
1400
  if (name === agentName) this.emit("message", msg.text);
1346
1401
  };
1347
- const onIdle = (name) => {
1402
+ const onIdle = (name, _details) => {
1348
1403
  if (name === agentName) this.emit("idle");
1349
1404
  };
1350
1405
  const onPermission = (name, parsed) => {