claude-code-controller 0.5.0 → 0.6.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/dist/api/index.cjs +204 -4
- package/dist/api/index.cjs.map +1 -1
- package/dist/api/index.d.cts +1 -1
- package/dist/api/index.d.ts +1 -1
- package/dist/api/index.js +204 -4
- package/dist/api/index.js.map +1 -1
- package/dist/{claude-CSXlMCvP.d.cts → claude-B7-oBjuE.d.cts} +52 -1
- package/dist/{claude-CSXlMCvP.d.ts → claude-B7-oBjuE.d.ts} +52 -1
- package/dist/index.cjs +214 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +60 -4
- package/dist/index.d.ts +60 -4
- package/dist/index.js +209 -4
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -102,6 +102,48 @@ interface PlainTextMessage {
|
|
|
102
102
|
type: "plain_text";
|
|
103
103
|
text: string;
|
|
104
104
|
}
|
|
105
|
+
/** JSON data sent by Claude Code to the statusLine command via stdin. */
|
|
106
|
+
interface StatusLineData {
|
|
107
|
+
model?: {
|
|
108
|
+
id?: string;
|
|
109
|
+
display_name?: string;
|
|
110
|
+
};
|
|
111
|
+
session_id?: string;
|
|
112
|
+
context_window?: {
|
|
113
|
+
used_percentage?: number;
|
|
114
|
+
remaining_percentage?: number;
|
|
115
|
+
total_input_tokens?: number;
|
|
116
|
+
total_output_tokens?: number;
|
|
117
|
+
context_window_size?: number;
|
|
118
|
+
current_usage?: {
|
|
119
|
+
input_tokens?: number;
|
|
120
|
+
output_tokens?: number;
|
|
121
|
+
cache_creation_input_tokens?: number;
|
|
122
|
+
cache_read_input_tokens?: number;
|
|
123
|
+
};
|
|
124
|
+
};
|
|
125
|
+
cost?: {
|
|
126
|
+
total_cost_usd?: number;
|
|
127
|
+
total_duration_ms?: number;
|
|
128
|
+
total_api_duration_ms?: number;
|
|
129
|
+
total_lines_added?: number;
|
|
130
|
+
total_lines_removed?: number;
|
|
131
|
+
};
|
|
132
|
+
workspace?: {
|
|
133
|
+
current_dir?: string;
|
|
134
|
+
project_dir?: string;
|
|
135
|
+
};
|
|
136
|
+
cwd?: string;
|
|
137
|
+
agent?: {
|
|
138
|
+
name?: string;
|
|
139
|
+
};
|
|
140
|
+
vim?: {
|
|
141
|
+
mode?: string;
|
|
142
|
+
};
|
|
143
|
+
output_style?: {
|
|
144
|
+
name?: string;
|
|
145
|
+
};
|
|
146
|
+
}
|
|
105
147
|
interface TeamConfig {
|
|
106
148
|
name: string;
|
|
107
149
|
description?: string;
|
|
@@ -172,6 +214,7 @@ interface ControllerEvents {
|
|
|
172
214
|
"task:completed": [task: TaskFile];
|
|
173
215
|
"agent:spawned": [agentName: string, pid: number];
|
|
174
216
|
"agent:exited": [agentName: string, code: number | null];
|
|
217
|
+
"agent:statusline": [agentName: string, data: StatusLineData];
|
|
175
218
|
error: [error: Error];
|
|
176
219
|
}
|
|
177
220
|
type LogLevel = "debug" | "info" | "warn" | "error" | "silent";
|
|
@@ -326,6 +369,7 @@ declare class ClaudeCodeController extends EventEmitter<ControllerEvents> implem
|
|
|
326
369
|
readonly tasks: TaskManager;
|
|
327
370
|
private processes;
|
|
328
371
|
private poller;
|
|
372
|
+
private statusLineWatcher;
|
|
329
373
|
private log;
|
|
330
374
|
private cwd;
|
|
331
375
|
private claudeBinary;
|
|
@@ -424,6 +468,12 @@ declare class ClaudeCodeController extends EventEmitter<ControllerEvents> implem
|
|
|
424
468
|
version: string | null;
|
|
425
469
|
};
|
|
426
470
|
private handlePollEvents;
|
|
471
|
+
/**
|
|
472
|
+
* Ensure the agent's cwd has a .claude/settings.local.json so the
|
|
473
|
+
* CLI skips the interactive workspace trust prompt.
|
|
474
|
+
* Also injects statusLine capture configuration.
|
|
475
|
+
*/
|
|
476
|
+
private ensureWorkspaceTrusted;
|
|
427
477
|
private ensureInitialized;
|
|
428
478
|
}
|
|
429
479
|
|
|
@@ -557,6 +607,7 @@ interface PlanRequestInfo {
|
|
|
557
607
|
interface AgentEvents {
|
|
558
608
|
message: [text: string];
|
|
559
609
|
idle: [];
|
|
610
|
+
statusline: [data: StatusLineData];
|
|
560
611
|
permission: [request: PermissionRequestInfo];
|
|
561
612
|
plan: [request: PlanRequestInfo];
|
|
562
613
|
exit: [code: number | null];
|
|
@@ -663,4 +714,4 @@ declare const claude: typeof claudeCall & {
|
|
|
663
714
|
session: (opts?: SessionOptions) => Promise<Session>;
|
|
664
715
|
};
|
|
665
716
|
|
|
666
|
-
export { Agent as A, type
|
|
717
|
+
export { Agent as A, type TaskCompletedMessage as B, ClaudeCodeController as C, type TaskFile as D, TaskManager as E, type TaskStatus as F, type TeamConfig as G, TeamManager as H, type InboxMessage as I, type TeamMember as J, claude as K, type Logger as L, type PermissionMode as P, type ReceiveOptions as R, type StructuredMessage as S, type TaskAssignmentMessage as T, type StatusLineData as a, type LogLevel as b, type AgentEvents as c, AgentHandle as d, type AgentType as e, type AskOptions as f, type ClaudeOptions as g, type ControllerEvents as h, type ControllerOptions as i, type IdleNotificationMessage as j, type PermissionPreset as k, type PermissionRequestInfo as l, type PermissionRequestMessage as m, type PermissionResponseMessage as n, type PlainTextMessage as o, type PlanApprovalRequestMessage as p, type PlanApprovalResponseMessage as q, type PlanRequestInfo as r, type SandboxPermissionRequestMessage as s, type SandboxPermissionResponseMessage as t, Session as u, type SessionAgentOptions as v, type SessionOptions as w, type ShutdownApprovedMessage as x, type ShutdownRequestMessage as y, type SpawnAgentOptions as z };
|
|
@@ -102,6 +102,48 @@ interface PlainTextMessage {
|
|
|
102
102
|
type: "plain_text";
|
|
103
103
|
text: string;
|
|
104
104
|
}
|
|
105
|
+
/** JSON data sent by Claude Code to the statusLine command via stdin. */
|
|
106
|
+
interface StatusLineData {
|
|
107
|
+
model?: {
|
|
108
|
+
id?: string;
|
|
109
|
+
display_name?: string;
|
|
110
|
+
};
|
|
111
|
+
session_id?: string;
|
|
112
|
+
context_window?: {
|
|
113
|
+
used_percentage?: number;
|
|
114
|
+
remaining_percentage?: number;
|
|
115
|
+
total_input_tokens?: number;
|
|
116
|
+
total_output_tokens?: number;
|
|
117
|
+
context_window_size?: number;
|
|
118
|
+
current_usage?: {
|
|
119
|
+
input_tokens?: number;
|
|
120
|
+
output_tokens?: number;
|
|
121
|
+
cache_creation_input_tokens?: number;
|
|
122
|
+
cache_read_input_tokens?: number;
|
|
123
|
+
};
|
|
124
|
+
};
|
|
125
|
+
cost?: {
|
|
126
|
+
total_cost_usd?: number;
|
|
127
|
+
total_duration_ms?: number;
|
|
128
|
+
total_api_duration_ms?: number;
|
|
129
|
+
total_lines_added?: number;
|
|
130
|
+
total_lines_removed?: number;
|
|
131
|
+
};
|
|
132
|
+
workspace?: {
|
|
133
|
+
current_dir?: string;
|
|
134
|
+
project_dir?: string;
|
|
135
|
+
};
|
|
136
|
+
cwd?: string;
|
|
137
|
+
agent?: {
|
|
138
|
+
name?: string;
|
|
139
|
+
};
|
|
140
|
+
vim?: {
|
|
141
|
+
mode?: string;
|
|
142
|
+
};
|
|
143
|
+
output_style?: {
|
|
144
|
+
name?: string;
|
|
145
|
+
};
|
|
146
|
+
}
|
|
105
147
|
interface TeamConfig {
|
|
106
148
|
name: string;
|
|
107
149
|
description?: string;
|
|
@@ -172,6 +214,7 @@ interface ControllerEvents {
|
|
|
172
214
|
"task:completed": [task: TaskFile];
|
|
173
215
|
"agent:spawned": [agentName: string, pid: number];
|
|
174
216
|
"agent:exited": [agentName: string, code: number | null];
|
|
217
|
+
"agent:statusline": [agentName: string, data: StatusLineData];
|
|
175
218
|
error: [error: Error];
|
|
176
219
|
}
|
|
177
220
|
type LogLevel = "debug" | "info" | "warn" | "error" | "silent";
|
|
@@ -326,6 +369,7 @@ declare class ClaudeCodeController extends EventEmitter<ControllerEvents> implem
|
|
|
326
369
|
readonly tasks: TaskManager;
|
|
327
370
|
private processes;
|
|
328
371
|
private poller;
|
|
372
|
+
private statusLineWatcher;
|
|
329
373
|
private log;
|
|
330
374
|
private cwd;
|
|
331
375
|
private claudeBinary;
|
|
@@ -424,6 +468,12 @@ declare class ClaudeCodeController extends EventEmitter<ControllerEvents> implem
|
|
|
424
468
|
version: string | null;
|
|
425
469
|
};
|
|
426
470
|
private handlePollEvents;
|
|
471
|
+
/**
|
|
472
|
+
* Ensure the agent's cwd has a .claude/settings.local.json so the
|
|
473
|
+
* CLI skips the interactive workspace trust prompt.
|
|
474
|
+
* Also injects statusLine capture configuration.
|
|
475
|
+
*/
|
|
476
|
+
private ensureWorkspaceTrusted;
|
|
427
477
|
private ensureInitialized;
|
|
428
478
|
}
|
|
429
479
|
|
|
@@ -557,6 +607,7 @@ interface PlanRequestInfo {
|
|
|
557
607
|
interface AgentEvents {
|
|
558
608
|
message: [text: string];
|
|
559
609
|
idle: [];
|
|
610
|
+
statusline: [data: StatusLineData];
|
|
560
611
|
permission: [request: PermissionRequestInfo];
|
|
561
612
|
plan: [request: PlanRequestInfo];
|
|
562
613
|
exit: [code: number | null];
|
|
@@ -663,4 +714,4 @@ declare const claude: typeof claudeCall & {
|
|
|
663
714
|
session: (opts?: SessionOptions) => Promise<Session>;
|
|
664
715
|
};
|
|
665
716
|
|
|
666
|
-
export { Agent as A, type
|
|
717
|
+
export { Agent as A, type TaskCompletedMessage as B, ClaudeCodeController as C, type TaskFile as D, TaskManager as E, type TaskStatus as F, type TeamConfig as G, TeamManager as H, type InboxMessage as I, type TeamMember as J, claude as K, type Logger as L, type PermissionMode as P, type ReceiveOptions as R, type StructuredMessage as S, type TaskAssignmentMessage as T, type StatusLineData as a, type LogLevel as b, type AgentEvents as c, AgentHandle as d, type AgentType as e, type AskOptions as f, type ClaudeOptions as g, type ControllerEvents as h, type ControllerOptions as i, type IdleNotificationMessage as j, type PermissionPreset as k, type PermissionRequestInfo as l, type PermissionRequestMessage as m, type PermissionResponseMessage as n, type PlainTextMessage as o, type PlanApprovalRequestMessage as p, type PlanApprovalResponseMessage as q, type PlanRequestInfo as r, type SandboxPermissionRequestMessage as s, type SandboxPermissionResponseMessage as t, Session as u, type SessionAgentOptions as v, type SessionOptions as w, type ShutdownApprovedMessage as x, type ShutdownRequestMessage as y, type SpawnAgentOptions as z };
|
package/dist/index.cjs
CHANGED
|
@@ -26,8 +26,11 @@ __export(index_exports, {
|
|
|
26
26
|
InboxPoller: () => InboxPoller,
|
|
27
27
|
ProcessManager: () => ProcessManager,
|
|
28
28
|
Session: () => Session,
|
|
29
|
+
StatusLineWatcher: () => StatusLineWatcher,
|
|
29
30
|
TaskManager: () => TaskManager,
|
|
30
31
|
TeamManager: () => TeamManager,
|
|
32
|
+
buildStatusLineCommand: () => buildStatusLineCommand,
|
|
33
|
+
buildStatusLineSettings: () => buildStatusLineSettings,
|
|
31
34
|
claude: () => claude,
|
|
32
35
|
createLogger: () => createLogger,
|
|
33
36
|
inboxPath: () => inboxPath,
|
|
@@ -36,6 +39,8 @@ __export(index_exports, {
|
|
|
36
39
|
readInbox: () => readInbox,
|
|
37
40
|
readUnread: () => readUnread,
|
|
38
41
|
silentLogger: () => silentLogger,
|
|
42
|
+
statusLineDir: () => statusLineDir,
|
|
43
|
+
statusLineLogPath: () => statusLineLogPath,
|
|
39
44
|
taskPath: () => taskPath,
|
|
40
45
|
tasksBaseDir: () => tasksBaseDir,
|
|
41
46
|
tasksDir: () => tasksDir,
|
|
@@ -47,13 +52,15 @@ __export(index_exports, {
|
|
|
47
52
|
module.exports = __toCommonJS(index_exports);
|
|
48
53
|
|
|
49
54
|
// src/claude.ts
|
|
50
|
-
var
|
|
55
|
+
var import_node_events3 = require("events");
|
|
51
56
|
var import_node_crypto3 = require("crypto");
|
|
52
57
|
|
|
53
58
|
// src/controller.ts
|
|
54
|
-
var
|
|
59
|
+
var import_node_events2 = require("events");
|
|
55
60
|
var import_node_child_process3 = require("child_process");
|
|
56
61
|
var import_node_crypto2 = require("crypto");
|
|
62
|
+
var import_node_fs5 = require("fs");
|
|
63
|
+
var import_node_path4 = require("path");
|
|
57
64
|
|
|
58
65
|
// src/team-manager.ts
|
|
59
66
|
var import_promises = require("fs/promises");
|
|
@@ -386,6 +393,8 @@ if pid == 0:
|
|
|
386
393
|
else:
|
|
387
394
|
signal.signal(signal.SIGTERM, lambda *a: (os.kill(pid, signal.SIGTERM), sys.exit(0)))
|
|
388
395
|
signal.signal(signal.SIGINT, lambda *a: (os.kill(pid, signal.SIGTERM), sys.exit(0)))
|
|
396
|
+
buf = b""
|
|
397
|
+
trust_sent = False
|
|
389
398
|
try:
|
|
390
399
|
while True:
|
|
391
400
|
r, _, _ = select.select([fd, 0], [], [], 1.0)
|
|
@@ -395,6 +404,12 @@ else:
|
|
|
395
404
|
if not data:
|
|
396
405
|
break
|
|
397
406
|
os.write(1, data)
|
|
407
|
+
if not trust_sent:
|
|
408
|
+
buf += data
|
|
409
|
+
if b"Yes" in buf and b"trust" in buf:
|
|
410
|
+
os.write(fd, b"\\r")
|
|
411
|
+
trust_sent = True
|
|
412
|
+
buf = b""
|
|
398
413
|
except OSError:
|
|
399
414
|
break
|
|
400
415
|
if 0 in r:
|
|
@@ -750,6 +765,135 @@ var silentLogger = {
|
|
|
750
765
|
}
|
|
751
766
|
};
|
|
752
767
|
|
|
768
|
+
// src/statusline-capture.ts
|
|
769
|
+
var import_node_events = require("events");
|
|
770
|
+
var import_node_fs4 = require("fs");
|
|
771
|
+
var import_node_path3 = require("path");
|
|
772
|
+
function statusLineDir(teamName) {
|
|
773
|
+
return (0, import_node_path3.join)(teamDir(teamName), "statusline");
|
|
774
|
+
}
|
|
775
|
+
function statusLineLogPath(teamName, agentName) {
|
|
776
|
+
return (0, import_node_path3.join)(statusLineDir(teamName), `${agentName}.jsonl`);
|
|
777
|
+
}
|
|
778
|
+
function buildStatusLineCommand(logFilePath) {
|
|
779
|
+
const escapedPath = logFilePath.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
|
|
780
|
+
return [
|
|
781
|
+
"python3 -c",
|
|
782
|
+
`'import sys,json;`,
|
|
783
|
+
`d=json.load(sys.stdin);`,
|
|
784
|
+
`open("${escapedPath}","a").write(json.dumps(d)+"\\n");`,
|
|
785
|
+
`print(d.get("model",{}).get("display_name",""))'`
|
|
786
|
+
].join(" ");
|
|
787
|
+
}
|
|
788
|
+
function buildStatusLineSettings(logFilePath) {
|
|
789
|
+
return {
|
|
790
|
+
statusLine: {
|
|
791
|
+
type: "command",
|
|
792
|
+
command: buildStatusLineCommand(logFilePath)
|
|
793
|
+
}
|
|
794
|
+
};
|
|
795
|
+
}
|
|
796
|
+
var StatusLineWatcher = class extends import_node_events.EventEmitter {
|
|
797
|
+
teamName;
|
|
798
|
+
log;
|
|
799
|
+
watchers = /* @__PURE__ */ new Map();
|
|
800
|
+
fileOffsets = /* @__PURE__ */ new Map();
|
|
801
|
+
stopped = false;
|
|
802
|
+
constructor(teamName, logger) {
|
|
803
|
+
super();
|
|
804
|
+
this.teamName = teamName;
|
|
805
|
+
this.log = logger;
|
|
806
|
+
}
|
|
807
|
+
/**
|
|
808
|
+
* Ensure the statusline directory exists.
|
|
809
|
+
*/
|
|
810
|
+
ensureDir() {
|
|
811
|
+
const dir = statusLineDir(this.teamName);
|
|
812
|
+
if (!(0, import_node_fs4.existsSync)(dir)) {
|
|
813
|
+
(0, import_node_fs4.mkdirSync)(dir, { recursive: true });
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
/**
|
|
817
|
+
* Start watching a specific agent's statusLine log file.
|
|
818
|
+
*/
|
|
819
|
+
watchAgent(agentName) {
|
|
820
|
+
if (this.stopped) return;
|
|
821
|
+
const filePath = statusLineLogPath(this.teamName, agentName);
|
|
822
|
+
if (!(0, import_node_fs4.existsSync)(filePath)) {
|
|
823
|
+
(0, import_node_fs4.writeFileSync)(filePath, "");
|
|
824
|
+
}
|
|
825
|
+
try {
|
|
826
|
+
const stats = (0, import_node_fs4.readFileSync)(filePath);
|
|
827
|
+
this.fileOffsets.set(filePath, stats.length);
|
|
828
|
+
} catch {
|
|
829
|
+
this.fileOffsets.set(filePath, 0);
|
|
830
|
+
}
|
|
831
|
+
try {
|
|
832
|
+
const watcher = (0, import_node_fs4.watch)(filePath, (eventType) => {
|
|
833
|
+
if (eventType === "change") {
|
|
834
|
+
this.readNewLines(agentName, filePath);
|
|
835
|
+
}
|
|
836
|
+
});
|
|
837
|
+
this.watchers.set(agentName, watcher);
|
|
838
|
+
this.log.debug(`Watching statusLine for agent "${agentName}" at ${filePath}`);
|
|
839
|
+
} catch (err) {
|
|
840
|
+
this.log.error(`Failed to watch statusLine for "${agentName}": ${err}`);
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
/**
|
|
844
|
+
* Stop watching a specific agent.
|
|
845
|
+
*/
|
|
846
|
+
unwatchAgent(agentName) {
|
|
847
|
+
const watcher = this.watchers.get(agentName);
|
|
848
|
+
if (watcher) {
|
|
849
|
+
watcher.close();
|
|
850
|
+
this.watchers.delete(agentName);
|
|
851
|
+
}
|
|
852
|
+
const filePath = statusLineLogPath(this.teamName, agentName);
|
|
853
|
+
this.fileOffsets.delete(filePath);
|
|
854
|
+
}
|
|
855
|
+
/**
|
|
856
|
+
* Stop all watchers.
|
|
857
|
+
*/
|
|
858
|
+
stop() {
|
|
859
|
+
this.stopped = true;
|
|
860
|
+
for (const [, watcher] of this.watchers) {
|
|
861
|
+
watcher.close();
|
|
862
|
+
}
|
|
863
|
+
this.watchers.clear();
|
|
864
|
+
this.fileOffsets.clear();
|
|
865
|
+
}
|
|
866
|
+
/**
|
|
867
|
+
* Read new lines appended to the log file since last read.
|
|
868
|
+
*/
|
|
869
|
+
readNewLines(agentName, filePath) {
|
|
870
|
+
try {
|
|
871
|
+
const content = (0, import_node_fs4.readFileSync)(filePath, "utf-8");
|
|
872
|
+
const offset = this.fileOffsets.get(filePath) ?? 0;
|
|
873
|
+
const newContent = content.slice(offset);
|
|
874
|
+
this.fileOffsets.set(filePath, content.length);
|
|
875
|
+
if (!newContent.trim()) return;
|
|
876
|
+
const lines = newContent.trim().split("\n");
|
|
877
|
+
for (const line of lines) {
|
|
878
|
+
if (!line.trim()) continue;
|
|
879
|
+
try {
|
|
880
|
+
const data = JSON.parse(line);
|
|
881
|
+
const event = {
|
|
882
|
+
agentName,
|
|
883
|
+
data,
|
|
884
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
885
|
+
};
|
|
886
|
+
this.emit("update", event);
|
|
887
|
+
} catch (parseErr) {
|
|
888
|
+
this.log.debug(`Failed to parse statusLine JSON: ${line}`);
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
} catch (err) {
|
|
892
|
+
this.log.debug(`Error reading statusLine file: ${err}`);
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
};
|
|
896
|
+
|
|
753
897
|
// src/controller.ts
|
|
754
898
|
var PROTOCOL_ONLY_TYPES = /* @__PURE__ */ new Set([
|
|
755
899
|
"shutdown_approved",
|
|
@@ -769,12 +913,13 @@ var AGENT_COLORS = [
|
|
|
769
913
|
"#FF69B4",
|
|
770
914
|
"#7B68EE"
|
|
771
915
|
];
|
|
772
|
-
var ClaudeCodeController = class extends
|
|
916
|
+
var ClaudeCodeController = class extends import_node_events2.EventEmitter {
|
|
773
917
|
teamName;
|
|
774
918
|
team;
|
|
775
919
|
tasks;
|
|
776
920
|
processes;
|
|
777
921
|
poller;
|
|
922
|
+
statusLineWatcher;
|
|
778
923
|
log;
|
|
779
924
|
cwd;
|
|
780
925
|
claudeBinary;
|
|
@@ -796,7 +941,11 @@ var ClaudeCodeController = class extends import_node_events.EventEmitter {
|
|
|
796
941
|
"controller",
|
|
797
942
|
this.log
|
|
798
943
|
);
|
|
944
|
+
this.statusLineWatcher = new StatusLineWatcher(this.teamName, this.log);
|
|
799
945
|
this.poller.onMessages((events) => this.handlePollEvents(events));
|
|
946
|
+
this.statusLineWatcher.on("update", (event) => {
|
|
947
|
+
this.emit("agent:statusline", event.agentName, event.data);
|
|
948
|
+
});
|
|
800
949
|
}
|
|
801
950
|
// ─── Lifecycle ───────────────────────────────────────────────────────
|
|
802
951
|
/**
|
|
@@ -807,6 +956,7 @@ var ClaudeCodeController = class extends import_node_events.EventEmitter {
|
|
|
807
956
|
if (this.initialized) return this;
|
|
808
957
|
await this.team.create({ cwd: this.cwd });
|
|
809
958
|
await this.tasks.init();
|
|
959
|
+
this.statusLineWatcher.ensureDir();
|
|
810
960
|
this.poller.start();
|
|
811
961
|
this.initialized = true;
|
|
812
962
|
this.log.info(
|
|
@@ -847,6 +997,7 @@ var ClaudeCodeController = class extends import_node_events.EventEmitter {
|
|
|
847
997
|
}
|
|
848
998
|
await this.processes.killAll();
|
|
849
999
|
this.poller.stop();
|
|
1000
|
+
this.statusLineWatcher.stop();
|
|
850
1001
|
await this.team.destroy();
|
|
851
1002
|
this.initialized = false;
|
|
852
1003
|
this.log.info("Controller shut down");
|
|
@@ -875,6 +1026,8 @@ var ClaudeCodeController = class extends import_node_events.EventEmitter {
|
|
|
875
1026
|
subscriptions: []
|
|
876
1027
|
};
|
|
877
1028
|
await this.team.addMember(member);
|
|
1029
|
+
this.ensureWorkspaceTrusted(cwd, opts.name);
|
|
1030
|
+
this.statusLineWatcher.watchAgent(opts.name);
|
|
878
1031
|
const env = Object.keys(this.defaultEnv).length > 0 || opts.env ? { ...this.defaultEnv, ...opts.env } : void 0;
|
|
879
1032
|
const proc = this.processes.spawn({
|
|
880
1033
|
teamName: this.teamName,
|
|
@@ -1081,6 +1234,7 @@ var ClaudeCodeController = class extends import_node_events.EventEmitter {
|
|
|
1081
1234
|
* Kill a specific agent.
|
|
1082
1235
|
*/
|
|
1083
1236
|
async killAgent(name) {
|
|
1237
|
+
this.statusLineWatcher.unwatchAgent(name);
|
|
1084
1238
|
await this.processes.kill(name);
|
|
1085
1239
|
await this.team.removeMember(name);
|
|
1086
1240
|
}
|
|
@@ -1142,6 +1296,32 @@ var ClaudeCodeController = class extends import_node_events.EventEmitter {
|
|
|
1142
1296
|
}
|
|
1143
1297
|
}
|
|
1144
1298
|
}
|
|
1299
|
+
/**
|
|
1300
|
+
* Ensure the agent's cwd has a .claude/settings.local.json so the
|
|
1301
|
+
* CLI skips the interactive workspace trust prompt.
|
|
1302
|
+
* Also injects statusLine capture configuration.
|
|
1303
|
+
*/
|
|
1304
|
+
ensureWorkspaceTrusted(cwd, agentName) {
|
|
1305
|
+
const claudeDir = (0, import_node_path4.join)(cwd, ".claude");
|
|
1306
|
+
const settingsPath = (0, import_node_path4.join)(claudeDir, "settings.local.json");
|
|
1307
|
+
(0, import_node_fs5.mkdirSync)(claudeDir, { recursive: true });
|
|
1308
|
+
let settings = {};
|
|
1309
|
+
if ((0, import_node_fs5.existsSync)(settingsPath)) {
|
|
1310
|
+
try {
|
|
1311
|
+
settings = JSON.parse((0, import_node_fs5.readFileSync)(settingsPath, "utf-8"));
|
|
1312
|
+
} catch {
|
|
1313
|
+
settings = {};
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1316
|
+
if (agentName && !settings.statusLine) {
|
|
1317
|
+
const logPath = statusLineLogPath(this.teamName, agentName);
|
|
1318
|
+
const statusLineSettings = buildStatusLineSettings(logPath);
|
|
1319
|
+
settings = { ...settings, ...statusLineSettings };
|
|
1320
|
+
this.log.debug(`Injected statusLine capture for "${agentName}"`);
|
|
1321
|
+
}
|
|
1322
|
+
(0, import_node_fs5.writeFileSync)(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
1323
|
+
this.log.debug(`Updated ${settingsPath}`);
|
|
1324
|
+
}
|
|
1145
1325
|
ensureInitialized() {
|
|
1146
1326
|
if (!this.initialized) {
|
|
1147
1327
|
throw new Error(
|
|
@@ -1226,7 +1406,7 @@ function waitForReady(controller, agentName, timeoutMs = 15e3) {
|
|
|
1226
1406
|
controller.on("agent:exited", onExit);
|
|
1227
1407
|
});
|
|
1228
1408
|
}
|
|
1229
|
-
var Agent = class _Agent extends
|
|
1409
|
+
var Agent = class _Agent extends import_node_events3.EventEmitter {
|
|
1230
1410
|
controller;
|
|
1231
1411
|
handle;
|
|
1232
1412
|
ownsController;
|
|
@@ -1319,14 +1499,22 @@ var Agent = class _Agent extends import_node_events2.EventEmitter {
|
|
|
1319
1499
|
this.ensureNotDisposed();
|
|
1320
1500
|
const timeout = opts?.timeout ?? 12e4;
|
|
1321
1501
|
const responsePromise = new Promise((resolve, reject) => {
|
|
1502
|
+
let gotMessage = false;
|
|
1322
1503
|
const timer = setTimeout(() => {
|
|
1323
1504
|
cleanup();
|
|
1324
1505
|
reject(new Error(`Timeout (${timeout}ms) waiting for response`));
|
|
1325
1506
|
}, timeout);
|
|
1326
1507
|
const onMsg = (text) => {
|
|
1508
|
+
gotMessage = true;
|
|
1327
1509
|
cleanup();
|
|
1328
1510
|
resolve(text);
|
|
1329
1511
|
};
|
|
1512
|
+
const onIdle = () => {
|
|
1513
|
+
if (!gotMessage) {
|
|
1514
|
+
cleanup();
|
|
1515
|
+
resolve("");
|
|
1516
|
+
}
|
|
1517
|
+
};
|
|
1330
1518
|
const onExit = (code) => {
|
|
1331
1519
|
cleanup();
|
|
1332
1520
|
reject(new Error(`Agent exited (code=${code}) before responding`));
|
|
@@ -1334,9 +1522,11 @@ var Agent = class _Agent extends import_node_events2.EventEmitter {
|
|
|
1334
1522
|
const cleanup = () => {
|
|
1335
1523
|
clearTimeout(timer);
|
|
1336
1524
|
this.removeListener("message", onMsg);
|
|
1525
|
+
this.removeListener("idle", onIdle);
|
|
1337
1526
|
this.removeListener("exit", onExit);
|
|
1338
1527
|
};
|
|
1339
1528
|
this.on("message", onMsg);
|
|
1529
|
+
this.on("idle", onIdle);
|
|
1340
1530
|
this.on("exit", onExit);
|
|
1341
1531
|
});
|
|
1342
1532
|
const wrapped = `${question}
|
|
@@ -1355,14 +1545,22 @@ IMPORTANT: You MUST send your complete answer back using the SendMessage tool. D
|
|
|
1355
1545
|
this.ensureNotDisposed();
|
|
1356
1546
|
const timeout = opts?.timeout ?? 12e4;
|
|
1357
1547
|
return new Promise((resolve, reject) => {
|
|
1548
|
+
let gotMessage = false;
|
|
1358
1549
|
const timer = setTimeout(() => {
|
|
1359
1550
|
cleanup();
|
|
1360
1551
|
reject(new Error(`Timeout (${timeout}ms) waiting for response`));
|
|
1361
1552
|
}, timeout);
|
|
1362
1553
|
const onMsg = (text) => {
|
|
1554
|
+
gotMessage = true;
|
|
1363
1555
|
cleanup();
|
|
1364
1556
|
resolve(text);
|
|
1365
1557
|
};
|
|
1558
|
+
const onIdle = () => {
|
|
1559
|
+
if (!gotMessage) {
|
|
1560
|
+
cleanup();
|
|
1561
|
+
resolve("");
|
|
1562
|
+
}
|
|
1563
|
+
};
|
|
1366
1564
|
const onExit = (code) => {
|
|
1367
1565
|
cleanup();
|
|
1368
1566
|
reject(new Error(`Agent exited (code=${code}) before responding`));
|
|
@@ -1370,9 +1568,11 @@ IMPORTANT: You MUST send your complete answer back using the SendMessage tool. D
|
|
|
1370
1568
|
const cleanup = () => {
|
|
1371
1569
|
clearTimeout(timer);
|
|
1372
1570
|
this.removeListener("message", onMsg);
|
|
1571
|
+
this.removeListener("idle", onIdle);
|
|
1373
1572
|
this.removeListener("exit", onExit);
|
|
1374
1573
|
};
|
|
1375
1574
|
this.on("message", onMsg);
|
|
1575
|
+
this.on("idle", onIdle);
|
|
1376
1576
|
this.on("exit", onExit);
|
|
1377
1577
|
});
|
|
1378
1578
|
}
|
|
@@ -1406,6 +1606,9 @@ IMPORTANT: You MUST send your complete answer back using the SendMessage tool. D
|
|
|
1406
1606
|
const onIdle = (name, _details) => {
|
|
1407
1607
|
if (name === agentName) this.emit("idle");
|
|
1408
1608
|
};
|
|
1609
|
+
const onStatusLine = (name, data) => {
|
|
1610
|
+
if (name === agentName) this.emit("statusline", data);
|
|
1611
|
+
};
|
|
1409
1612
|
const onPermission = (name, parsed) => {
|
|
1410
1613
|
if (name !== agentName) return;
|
|
1411
1614
|
let handled = false;
|
|
@@ -1454,6 +1657,7 @@ IMPORTANT: You MUST send your complete answer back using the SendMessage tool. D
|
|
|
1454
1657
|
};
|
|
1455
1658
|
this.controller.on("message", onMessage);
|
|
1456
1659
|
this.controller.on("idle", onIdle);
|
|
1660
|
+
this.controller.on("agent:statusline", onStatusLine);
|
|
1457
1661
|
this.controller.on("permission:request", onPermission);
|
|
1458
1662
|
this.controller.on("plan:approval_request", onPlan);
|
|
1459
1663
|
this.controller.on("agent:exited", onExit);
|
|
@@ -1461,6 +1665,7 @@ IMPORTANT: You MUST send your complete answer back using the SendMessage tool. D
|
|
|
1461
1665
|
this.boundListeners = [
|
|
1462
1666
|
{ event: "message", fn: onMessage },
|
|
1463
1667
|
{ event: "idle", fn: onIdle },
|
|
1668
|
+
{ event: "agent:statusline", fn: onStatusLine },
|
|
1464
1669
|
{ event: "permission:request", fn: onPermission },
|
|
1465
1670
|
{ event: "plan:approval_request", fn: onPlan },
|
|
1466
1671
|
{ event: "agent:exited", fn: onExit },
|
|
@@ -1607,8 +1812,11 @@ var claude = Object.assign(claudeCall, {
|
|
|
1607
1812
|
InboxPoller,
|
|
1608
1813
|
ProcessManager,
|
|
1609
1814
|
Session,
|
|
1815
|
+
StatusLineWatcher,
|
|
1610
1816
|
TaskManager,
|
|
1611
1817
|
TeamManager,
|
|
1818
|
+
buildStatusLineCommand,
|
|
1819
|
+
buildStatusLineSettings,
|
|
1612
1820
|
claude,
|
|
1613
1821
|
createLogger,
|
|
1614
1822
|
inboxPath,
|
|
@@ -1617,6 +1825,8 @@ var claude = Object.assign(claudeCall, {
|
|
|
1617
1825
|
readInbox,
|
|
1618
1826
|
readUnread,
|
|
1619
1827
|
silentLogger,
|
|
1828
|
+
statusLineDir,
|
|
1829
|
+
statusLineLogPath,
|
|
1620
1830
|
taskPath,
|
|
1621
1831
|
tasksBaseDir,
|
|
1622
1832
|
tasksDir,
|