poe-code 3.0.197 → 3.0.199

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.
Files changed (100) hide show
  1. package/dist/cli/commands/configure.d.ts +0 -7
  2. package/dist/cli/commands/configure.js +11 -14
  3. package/dist/cli/commands/configure.js.map +1 -1
  4. package/dist/cli/commands/provider.js +8 -1
  5. package/dist/cli/commands/provider.js.map +1 -1
  6. package/dist/cli/constants.d.ts +1 -1
  7. package/dist/cli/constants.js +0 -1
  8. package/dist/cli/constants.js.map +1 -1
  9. package/dist/index.js +221 -263
  10. package/dist/index.js.map +4 -4
  11. package/dist/providers/claude-code.js +6 -21
  12. package/dist/providers/claude-code.js.map +3 -3
  13. package/dist/providers/codex.js +6 -21
  14. package/dist/providers/codex.js.map +3 -3
  15. package/dist/providers/create-provider.js +0 -2
  16. package/dist/providers/create-provider.js.map +1 -1
  17. package/dist/providers/goose.js +14 -26
  18. package/dist/providers/goose.js.map +3 -3
  19. package/dist/providers/kimi.js +6 -21
  20. package/dist/providers/kimi.js.map +3 -3
  21. package/dist/providers/opencode.js +6 -21
  22. package/dist/providers/opencode.js.map +3 -3
  23. package/dist/providers/poe-agent.js +66 -85
  24. package/dist/providers/poe-agent.js.map +4 -4
  25. package/dist/utils/command-checks.d.ts +2 -1
  26. package/dist/utils/command-checks.js +3 -1
  27. package/dist/utils/command-checks.js.map +1 -1
  28. package/package.json +4 -1
  29. package/packages/memory/dist/cache.js +1 -1
  30. package/packages/memory/dist/explain.js +1 -1
  31. package/packages/memory/dist/index.js +9 -7
  32. package/packages/memory/dist/index.js.map +3 -3
  33. package/packages/memory/dist/query.js +1 -1
  34. package/packages/memory/dist/tokens.js +1 -1
  35. package/packages/superintendent/dist/cli.d.ts +2 -0
  36. package/packages/superintendent/dist/cli.js +41 -0
  37. package/packages/superintendent/dist/commands/builder-group.d.ts +52 -0
  38. package/packages/superintendent/dist/commands/builder-group.js +73 -0
  39. package/packages/superintendent/dist/commands/complete.d.ts +19 -0
  40. package/packages/superintendent/dist/commands/complete.js +54 -0
  41. package/packages/superintendent/dist/commands/index.d.ts +4 -0
  42. package/packages/superintendent/dist/commands/index.js +4 -0
  43. package/packages/superintendent/dist/commands/inspector-group.d.ts +115 -0
  44. package/packages/superintendent/dist/commands/inspector-group.js +133 -0
  45. package/packages/superintendent/dist/commands/install.d.ts +31 -0
  46. package/packages/superintendent/dist/commands/install.js +148 -0
  47. package/packages/superintendent/dist/commands/plan-path.d.ts +9 -0
  48. package/packages/superintendent/dist/commands/plan-path.js +40 -0
  49. package/packages/superintendent/dist/commands/poe-agent-runner.d.ts +5 -0
  50. package/packages/superintendent/dist/commands/poe-agent-runner.js +27 -0
  51. package/packages/superintendent/dist/commands/run.d.ts +86 -0
  52. package/packages/superintendent/dist/commands/run.js +945 -0
  53. package/packages/superintendent/dist/commands/superintendent-group.d.ts +325 -0
  54. package/packages/superintendent/dist/commands/superintendent-group.js +238 -0
  55. package/packages/superintendent/dist/config-scope.d.ts +8 -0
  56. package/packages/superintendent/dist/config-scope.js +9 -0
  57. package/packages/superintendent/dist/direct-execution.d.ts +1 -0
  58. package/packages/superintendent/dist/direct-execution.js +20 -0
  59. package/packages/superintendent/dist/document/parse.d.ts +59 -0
  60. package/packages/superintendent/dist/document/parse.js +409 -0
  61. package/packages/superintendent/dist/document/tasks.d.ts +12 -0
  62. package/packages/superintendent/dist/document/tasks.js +96 -0
  63. package/packages/superintendent/dist/document/write.d.ts +6 -0
  64. package/packages/superintendent/dist/document/write.js +156 -0
  65. package/packages/superintendent/dist/index.d.ts +12 -0
  66. package/packages/superintendent/dist/index.js +15 -0
  67. package/packages/superintendent/dist/mcp.d.ts +24 -0
  68. package/packages/superintendent/dist/mcp.js +202 -0
  69. package/packages/superintendent/dist/runtime/agentic-tools.d.ts +33 -0
  70. package/packages/superintendent/dist/runtime/agentic-tools.js +74 -0
  71. package/packages/superintendent/dist/runtime/loop.d.ts +88 -0
  72. package/packages/superintendent/dist/runtime/loop.js +446 -0
  73. package/packages/superintendent/dist/runtime/resolve-cwd.d.ts +2 -0
  74. package/packages/superintendent/dist/runtime/resolve-cwd.js +10 -0
  75. package/packages/superintendent/dist/runtime/run-builder.d.ts +13 -0
  76. package/packages/superintendent/dist/runtime/run-builder.js +102 -0
  77. package/packages/superintendent/dist/runtime/run-inspector.d.ts +16 -0
  78. package/packages/superintendent/dist/runtime/run-inspector.js +119 -0
  79. package/packages/superintendent/dist/runtime/run-owner-review.d.ts +18 -0
  80. package/packages/superintendent/dist/runtime/run-owner-review.js +208 -0
  81. package/packages/superintendent/dist/runtime/run-superintendent.d.ts +13 -0
  82. package/packages/superintendent/dist/runtime/run-superintendent.js +208 -0
  83. package/packages/superintendent/dist/runtime/system-prompt.d.ts +17 -0
  84. package/packages/superintendent/dist/runtime/system-prompt.js +54 -0
  85. package/packages/superintendent/dist/runtime/templates.d.ts +22 -0
  86. package/packages/superintendent/dist/runtime/templates.js +23 -0
  87. package/packages/superintendent/dist/runtime/types.d.ts +4 -0
  88. package/packages/superintendent/dist/runtime/types.js +1 -0
  89. package/packages/superintendent/dist/runtime/workflow-tool.d.ts +29 -0
  90. package/packages/superintendent/dist/runtime/workflow-tool.js +83 -0
  91. package/packages/superintendent/dist/state/machine.d.ts +14 -0
  92. package/packages/superintendent/dist/state/machine.js +53 -0
  93. package/packages/superintendent/dist/templates/SKILL_superintendent.md +193 -0
  94. package/packages/superintendent/dist/testing/index.d.ts +2 -0
  95. package/packages/superintendent/dist/testing/index.js +1 -0
  96. package/packages/superintendent/dist/testing/simulation.d.ts +57 -0
  97. package/packages/superintendent/dist/testing/simulation.js +346 -0
  98. package/dist/providers/tiny-http-mcp-server.d.ts +0 -22
  99. package/dist/providers/tiny-http-mcp-server.js +0 -1471
  100. package/dist/providers/tiny-http-mcp-server.js.map +0 -7
@@ -0,0 +1,945 @@
1
+ import path from "node:path";
2
+ import * as fsPromises from "node:fs/promises";
3
+ import { spawn as nodeSpawn, spawnSync as nodeSpawnSync } from "node:child_process";
4
+ import { resolveLoopAgent, resolveRunLogDir, resolveWorkflowPath } from "@poe-code/agent-harness-tools";
5
+ import { applyMiddlewares, getSpawnConfig, renderAcpStream, sessionCapture, spawn, spawnLog, spawnStreaming, usageCapture } from "@poe-code/agent-spawn";
6
+ import { parseAgentSpecifier } from "@poe-code/agent-defs";
7
+ import { executePoeAgent } from "./poe-agent-runner.js";
8
+ import { S, UserError, defineCommand } from "toolcraft";
9
+ import { acp, cancel, createDashboard, isCancel, resolveOutputFormat, select, shouldUseInteractiveDashboard, text } from "@poe-code/design-system";
10
+ import { planConfigScope, readMergedDocument, resolveConfigPath, resolveProjectConfigPath, resolveScope } from "@poe-code/poe-code-config";
11
+ import { superintendentConfigScope } from "../config-scope.js";
12
+ import { parseSuperintendentDoc, readExplicitBuilderAgent } from "../document/parse.js";
13
+ import { runLoop } from "../runtime/loop.js";
14
+ import { createLoopState } from "../state/machine.js";
15
+ const coreDefaultAgentConfigSchema = {
16
+ defaultAgent: {
17
+ type: "string",
18
+ default: "",
19
+ env: "POE_DEFAULT_AGENT",
20
+ doc: "Default agent used when no explicit Superintendent builder agent is provided"
21
+ }
22
+ };
23
+ const runParams = S.Object({
24
+ doc: S.Optional(S.String({ description: "Path to the superintendent markdown document" })),
25
+ agent: S.Optional(S.String({
26
+ description: "Override the builder agent for this run. Precedence: --agent > plan frontmatter builder.agent."
27
+ })),
28
+ tui: S.Optional(S.Boolean({ description: "Show a live dashboard while Superintendent is running" }))
29
+ });
30
+ export const runCommand = defineCommand({
31
+ name: "run",
32
+ description: "Run the full superintendent loop.",
33
+ positional: ["doc"],
34
+ params: runParams,
35
+ scope: ["cli", "sdk"],
36
+ handler: async ({ params }) => {
37
+ const cwd = process.cwd();
38
+ const homeDir = process.env.HOME ?? process.env.USERPROFILE ?? cwd;
39
+ const commandConfig = await resolveSuperintendentCommandConfig(cwd, homeDir, process.env);
40
+ const tuiEnabled = params.tui ?? commandConfig.tui;
41
+ return runSuperintendentCommand({
42
+ cwd,
43
+ homeDir,
44
+ docPath: params.doc,
45
+ ...(params.agent ? { builderAgent: params.agent } : {}),
46
+ configuredDefaultAgent: commandConfig.configuredDefaultAgent,
47
+ assumeYes: process.argv.includes("--yes"),
48
+ interactive: Boolean(process.stdin.isTTY),
49
+ useDashboard: shouldUseInteractiveDashboard(tuiEnabled) && resolveOutputFormat() === "terminal",
50
+ env: process.env,
51
+ ...(commandConfig.planDirectory ? { planDirectory: commandConfig.planDirectory } : {})
52
+ });
53
+ },
54
+ render: {
55
+ rich: (result, { logger }) => {
56
+ logger.success(`Superintendent run finished: ${result.stopReason}.`);
57
+ logger.message(text.section("Run:"));
58
+ logger.message(`Plan: ${result.docPath}`);
59
+ logger.message(`Builder agent: ${result.builderAgent}`);
60
+ logger.message(`State: ${result.state}`);
61
+ logger.message(`Round: ${result.round}`);
62
+ if (result.state === "review") {
63
+ logger.message(`Review turn: ${result.reviewTurn}`);
64
+ }
65
+ },
66
+ markdown: (result) => {
67
+ const lines = [
68
+ "## Superintendent run",
69
+ "",
70
+ `- Plan: ${result.docPath}`,
71
+ `- Builder agent: ${result.builderAgent}`,
72
+ `- Stop reason: ${result.stopReason}`,
73
+ `- State: ${result.state}`,
74
+ `- Round: ${result.round}`,
75
+ `- Review turn: ${result.reviewTurn}`
76
+ ];
77
+ return lines.join("\n");
78
+ },
79
+ json: (result) => result
80
+ }
81
+ });
82
+ export function createRunMcpCommand(runners) {
83
+ return defineCommand({
84
+ name: "run",
85
+ description: "Run the full superintendent loop without the dashboard UI.",
86
+ positional: ["doc"],
87
+ params: runParams,
88
+ scope: ["mcp"],
89
+ handler: async ({ params }) => {
90
+ const cwd = process.cwd();
91
+ const homeDir = process.env.HOME ?? process.env.USERPROFILE ?? cwd;
92
+ const commandConfig = await resolveSuperintendentCommandConfig(cwd, homeDir, process.env);
93
+ return runSuperintendentCommand({
94
+ cwd,
95
+ homeDir,
96
+ docPath: params.doc,
97
+ ...(params.agent ? { builderAgent: params.agent } : {}),
98
+ configuredDefaultAgent: commandConfig.configuredDefaultAgent,
99
+ assumeYes: true,
100
+ interactive: false,
101
+ useDashboard: false,
102
+ env: process.env,
103
+ ...(commandConfig.planDirectory ? { planDirectory: commandConfig.planDirectory } : {}),
104
+ ...(runners?.runLoop ? { runLoop: runners.runLoop } : {})
105
+ });
106
+ },
107
+ render: runCommand.render
108
+ });
109
+ }
110
+ export const runMcpCommand = createRunMcpCommand();
111
+ async function resolveSuperintendentCommandConfig(cwd, homeDir, env, fs) {
112
+ const configPath = resolveConfigPath(homeDir);
113
+ const projectConfigPath = resolveProjectConfigPath(cwd);
114
+ try {
115
+ const document = await readMergedDocument(createConfigResolutionFs(fs), configPath, projectConfigPath);
116
+ const planDirectory = resolveScope(planConfigScope.schema, document.plan, env).plan_directory?.trim();
117
+ const superintendentResolved = resolveScope(superintendentConfigScope.schema, document[superintendentConfigScope.scope], env);
118
+ const coreResolved = resolveScope(coreDefaultAgentConfigSchema, document.core, env);
119
+ return {
120
+ configuredDefaultAgent: normalizeAgentSelection(coreResolved.defaultAgent) ?? null,
121
+ ...(planDirectory ? { planDirectory } : {}),
122
+ tui: superintendentResolved.tui === true
123
+ };
124
+ }
125
+ catch {
126
+ return { configuredDefaultAgent: null, tui: false };
127
+ }
128
+ }
129
+ async function resolveSuperintendentPlanDirectory(cwd, homeDir, env, fs) {
130
+ const configPath = resolveConfigPath(homeDir);
131
+ const projectConfigPath = resolveProjectConfigPath(cwd);
132
+ const document = await readMergedDocument(createConfigResolutionFs(fs), configPath, projectConfigPath);
133
+ return resolveScope(planConfigScope.schema, document.plan, env).plan_directory;
134
+ }
135
+ const configFs = {
136
+ readFile: (filePath, encoding) => fsPromises.readFile(filePath, encoding),
137
+ writeFile: async (filePath, content, options) => {
138
+ await fsPromises.writeFile(filePath, content, options ?? { encoding: "utf8" });
139
+ },
140
+ mkdir: async (filePath, options) => {
141
+ await fsPromises.mkdir(filePath, options);
142
+ },
143
+ unlink: (filePath) => fsPromises.unlink(filePath),
144
+ stat: async (filePath) => {
145
+ const stat = await fsPromises.stat(filePath);
146
+ return { mode: stat.mode };
147
+ },
148
+ readdir: (filePath) => fsPromises.readdir(filePath)
149
+ };
150
+ function createConfigResolutionFs(fs) {
151
+ if (!fs) {
152
+ return configFs;
153
+ }
154
+ return {
155
+ ...configFs,
156
+ readFile: (filePath, encoding) => fs.readFile(filePath, encoding),
157
+ writeFile: async (filePath, content, options) => {
158
+ await fs.writeFile(filePath, content, options);
159
+ },
160
+ mkdir: async (filePath, options) => {
161
+ await fs.mkdir(filePath, options);
162
+ }
163
+ };
164
+ }
165
+ export async function runSuperintendentCommand(options) {
166
+ const fs = options.fs ?? createDefaultFs();
167
+ const now = options.now ?? Date.now;
168
+ const selectPrompt = options.selectPrompt ?? select;
169
+ const dashboardFactory = options.createDashboard ?? createDashboard;
170
+ const runLoopImpl = options.runLoop ?? runLoop;
171
+ const setIntervalImpl = options.setInterval ?? global.setInterval;
172
+ const clearIntervalImpl = options.clearInterval ?? global.clearInterval;
173
+ const env = options.env ?? process.env;
174
+ const interactive = options.interactive ?? Boolean(process.stdin.isTTY);
175
+ const assumeYes = options.assumeYes ?? false;
176
+ const useDashboard = options.useDashboard ?? resolveOutputFormat() === "terminal";
177
+ const stderr = options.stderr ?? process.stderr;
178
+ const exitProcess = options.exit ?? ((code) => process.exit(code));
179
+ const selectedDocPath = await resolveDocPath({
180
+ cwd: options.cwd,
181
+ homeDir: options.homeDir,
182
+ docPath: options.docPath,
183
+ planDirectory: options.planDirectory,
184
+ assumeYes,
185
+ interactive,
186
+ env,
187
+ fs,
188
+ selectPrompt
189
+ });
190
+ const runLogDir = resolveRunLogDir({
191
+ planPath: selectedDocPath,
192
+ runner: "superintendent",
193
+ homeDir: options.homeDir
194
+ });
195
+ const documentContent = await fs.readFile(selectedDocPath, "utf8");
196
+ const document = parseSuperintendentDoc(selectedDocPath, documentContent);
197
+ const selectedBuilder = await resolveLoopAgent({
198
+ providedAgent: normalizeAgentSelection(options.builderAgent),
199
+ frontmatterAgent: normalizeAgentSelection(readExplicitBuilderAgent(selectedDocPath, documentContent)),
200
+ configuredDefaultAgent: normalizeAgentSelection(options.configuredDefaultAgent) ?? null,
201
+ assumeYes,
202
+ fallbackAgent: "claude-code",
203
+ message: "Select agent to run Superintendent builder with:",
204
+ select: selectPrompt,
205
+ isCancel
206
+ });
207
+ if ("cancelled" in selectedBuilder) {
208
+ cancel("Operation cancelled.");
209
+ throw new UserError("Operation cancelled.");
210
+ }
211
+ const selectedBuilderAgent = selectedBuilder.agent;
212
+ if (!useDashboard) {
213
+ let activeStage = undefined;
214
+ const headlessAbort = new AbortController();
215
+ const headlessSigint = () => {
216
+ headlessAbort.abort();
217
+ exitProcess(130);
218
+ };
219
+ process.on("SIGINT", headlessSigint);
220
+ try {
221
+ const result = await runLoopImpl({
222
+ docPath: selectedDocPath,
223
+ cwd: options.cwd,
224
+ homeDir: options.homeDir,
225
+ ...(options.fs ? { fs } : {}),
226
+ signal: headlessAbort.signal,
227
+ logDir: runLogDir,
228
+ callbacks: {
229
+ onBuilderStart: () => {
230
+ activeStage = "builder";
231
+ },
232
+ onBuilderComplete: () => {
233
+ activeStage = undefined;
234
+ },
235
+ onBuilderFailed: () => {
236
+ activeStage = undefined;
237
+ },
238
+ onInspectorStart: (name) => {
239
+ activeStage = { inspector: name };
240
+ },
241
+ onInspectorComplete: () => {
242
+ activeStage = undefined;
243
+ },
244
+ onInspectorFailed: () => {
245
+ activeStage = undefined;
246
+ },
247
+ onSuperintendentStart: () => {
248
+ activeStage = "superintendent";
249
+ },
250
+ onSuperintendentComplete: () => {
251
+ activeStage = undefined;
252
+ },
253
+ onOwnerStart: () => {
254
+ activeStage = "owner";
255
+ },
256
+ onOwnerComplete: () => {
257
+ activeStage = undefined;
258
+ }
259
+ },
260
+ runAgent: createAgentRunner({
261
+ session: undefined,
262
+ executeAgent: options.executeAgent,
263
+ selectedBuilderAgent,
264
+ activeStage: () => activeStage,
265
+ now,
266
+ stderr
267
+ })
268
+ });
269
+ return {
270
+ ...result,
271
+ docPath: selectedDocPath,
272
+ builderAgent: selectedBuilderAgent
273
+ };
274
+ }
275
+ finally {
276
+ process.off("SIGINT", headlessSigint);
277
+ }
278
+ }
279
+ const session = {
280
+ dashboard: dashboardFactory({
281
+ title: "Superintendent",
282
+ statsTitle: "Loop",
283
+ rightPaneWidth: 32,
284
+ hints: [
285
+ { key: "q", label: "Quit" },
286
+ { key: "e", label: "Edit" },
287
+ { key: "l", label: "Log" },
288
+ { key: "p", label: "Pause" },
289
+ { key: "↑↓", label: "Scroll" },
290
+ { key: "F", label: "Follow" }
291
+ ]
292
+ }),
293
+ startedAt: now(),
294
+ state: createLoopState(document),
295
+ currentAction: undefined,
296
+ stopRequested: false,
297
+ pauseRequested: false,
298
+ paused: false,
299
+ activeStage: undefined,
300
+ tokensIn: 0,
301
+ tokensOut: 0,
302
+ resumeWaiters: []
303
+ };
304
+ const syncStats = () => {
305
+ session.dashboard?.updateStats({
306
+ status: readDashboardStatus(session),
307
+ iterations: session.state.round,
308
+ tokensIn: session.tokensIn,
309
+ tokensOut: session.tokensOut,
310
+ elapsedMs: Math.max(0, now() - session.startedAt),
311
+ currentAction: formatCurrentAction(session)
312
+ });
313
+ };
314
+ const appendEvent = (kind, message) => {
315
+ session.dashboard?.appendOutput({
316
+ kind,
317
+ text: `${formatTimestamp(now())} ${message}`,
318
+ ts: now()
319
+ });
320
+ };
321
+ const callbacks = {
322
+ onBuilderStart: () => {
323
+ session.activeStage = "builder";
324
+ session.currentAction = "builder";
325
+ appendEvent("status", "Builder starting");
326
+ syncStats();
327
+ },
328
+ onBuilderComplete: (result) => {
329
+ session.activeStage = undefined;
330
+ session.currentAction = undefined;
331
+ if (result.log_path)
332
+ session.latestLogFile = result.log_path;
333
+ appendEvent("success", "Builder completed");
334
+ syncStats();
335
+ },
336
+ onBuilderFailed: (error) => {
337
+ session.activeStage = undefined;
338
+ session.currentAction = undefined;
339
+ appendEvent("error", `Builder failed: ${error.message}`);
340
+ syncStats();
341
+ },
342
+ onInspectorStart: (name) => {
343
+ session.activeStage = { inspector: name };
344
+ session.currentAction = `inspector: ${name}`;
345
+ appendEvent("status", `Inspector ${name} starting`);
346
+ syncStats();
347
+ },
348
+ onInspectorComplete: (result) => {
349
+ session.activeStage = undefined;
350
+ session.currentAction = undefined;
351
+ if (result.log_path)
352
+ session.latestLogFile = result.log_path;
353
+ appendEvent("info", `Inspector ${result.name} completed`);
354
+ syncStats();
355
+ },
356
+ onInspectorFailed: (name, error) => {
357
+ session.activeStage = undefined;
358
+ session.currentAction = undefined;
359
+ appendEvent("error", `Inspector ${name} failed: ${error.message}`);
360
+ syncStats();
361
+ },
362
+ onSuperintendentStart: () => {
363
+ session.activeStage = "superintendent";
364
+ session.currentAction = "superintendent";
365
+ appendEvent("status", "Superintendent reviewing");
366
+ syncStats();
367
+ },
368
+ onSuperintendentComplete: (result) => {
369
+ session.activeStage = undefined;
370
+ session.currentAction = undefined;
371
+ if (result.log_path)
372
+ session.latestLogFile = result.log_path;
373
+ appendEvent("info", result.transition?.action === "request_review"
374
+ ? "Superintendent requested owner review"
375
+ : "Superintendent reviewed round");
376
+ syncStats();
377
+ },
378
+ onOwnerStart: () => {
379
+ session.activeStage = "owner";
380
+ session.currentAction = "owner";
381
+ appendEvent("status", "Owner reviewing");
382
+ syncStats();
383
+ },
384
+ onOwnerComplete: (result) => {
385
+ session.activeStage = undefined;
386
+ session.currentAction = undefined;
387
+ if (result.log_path)
388
+ session.latestLogFile = result.log_path;
389
+ appendEvent(result.transition.action === "approve_completion" ? "success" : "info", result.transition.action === "approve_completion"
390
+ ? "Owner approved"
391
+ : "Owner requested changes");
392
+ syncStats();
393
+ },
394
+ onRoundComplete: (round) => {
395
+ appendEvent("success", `Round ${round} completed`);
396
+ syncStats();
397
+ },
398
+ onLoopComplete: (result) => {
399
+ session.state = stripStopReason(result);
400
+ if (result.stopReason === "completed") {
401
+ appendEvent("success", "Loop completed");
402
+ }
403
+ else if (result.stopReason === "stopped") {
404
+ appendEvent("info", "Loop stopped");
405
+ }
406
+ else if (result.stopReason === "max_rounds") {
407
+ appendEvent("info", "Loop stopped at max rounds");
408
+ }
409
+ else if (result.stopReason === "aborted") {
410
+ appendEvent("error", "Loop aborted");
411
+ }
412
+ syncStats();
413
+ },
414
+ onStateChange: (state) => {
415
+ session.state = { ...state };
416
+ syncStats();
417
+ },
418
+ shouldPause: () => session.pauseRequested,
419
+ shouldStop: () => session.stopRequested
420
+ };
421
+ const intervalId = setIntervalImpl(() => {
422
+ syncStats();
423
+ }, 1_000);
424
+ const abortController = new AbortController();
425
+ const forceQuit = () => {
426
+ abortController.abort();
427
+ session.dashboard.stop();
428
+ session.dashboard.destroy();
429
+ exitProcess(130);
430
+ };
431
+ const handleDashboardCommand = (command) => {
432
+ if (command === "forceQuit") {
433
+ forceQuit();
434
+ return;
435
+ }
436
+ if (command === "quit") {
437
+ if (!session.stopRequested) {
438
+ session.stopRequested = true;
439
+ session.pauseRequested = false;
440
+ session.paused = false;
441
+ appendEvent("status", "Graceful stop requested");
442
+ releaseWaiters(session);
443
+ syncStats();
444
+ }
445
+ return;
446
+ }
447
+ if (command === "pause") {
448
+ if (session.paused) {
449
+ session.paused = false;
450
+ session.pauseRequested = false;
451
+ appendEvent("status", "Resuming loop");
452
+ releaseWaiters(session);
453
+ syncStats();
454
+ return;
455
+ }
456
+ session.pauseRequested = !session.pauseRequested;
457
+ appendEvent("status", session.pauseRequested ? "Pause requested" : "Pause request cancelled");
458
+ syncStats();
459
+ return;
460
+ }
461
+ if (command === "edit") {
462
+ const editor = resolveEditor(env);
463
+ if (editor.mode === "tty") {
464
+ session.pauseRequested = true;
465
+ }
466
+ editPlan(session.dashboard, selectedDocPath, env, options.openInEditor);
467
+ appendEvent("info", "Plan reopened in $EDITOR");
468
+ syncStats();
469
+ }
470
+ if (command === "view-log") {
471
+ if (!session.latestLogFile) {
472
+ appendEvent("info", "No log file available yet");
473
+ syncStats();
474
+ return;
475
+ }
476
+ const editor = resolveEditor(env);
477
+ if (editor.mode === "tty") {
478
+ session.pauseRequested = true;
479
+ }
480
+ editPlan(session.dashboard, session.latestLogFile, env, options.openInEditor);
481
+ appendEvent("info", `Log opened: ${path.basename(session.latestLogFile)}`);
482
+ syncStats();
483
+ }
484
+ };
485
+ session.dashboard.onCommand(handleDashboardCommand);
486
+ session.dashboard.start();
487
+ syncStats();
488
+ const sigintHandler = () => {
489
+ forceQuit();
490
+ };
491
+ process.on("SIGINT", sigintHandler);
492
+ let caughtError;
493
+ try {
494
+ while (true) {
495
+ session.paused = false;
496
+ syncStats();
497
+ const result = await runLoopImpl({
498
+ docPath: selectedDocPath,
499
+ cwd: options.cwd,
500
+ homeDir: options.homeDir,
501
+ ...(options.fs ? { fs } : {}),
502
+ callbacks,
503
+ signal: abortController.signal,
504
+ logDir: runLogDir,
505
+ runAgent: createAgentRunner({
506
+ session,
507
+ executeAgent: options.executeAgent,
508
+ selectedBuilderAgent,
509
+ activeStage: () => session.activeStage,
510
+ now,
511
+ stderr
512
+ })
513
+ });
514
+ session.state = stripStopReason(result);
515
+ if (result.stopReason === "paused") {
516
+ session.paused = true;
517
+ session.pauseRequested = false;
518
+ syncStats();
519
+ if (session.stopRequested) {
520
+ return {
521
+ ...session.state,
522
+ stopReason: "stopped",
523
+ docPath: selectedDocPath,
524
+ builderAgent: selectedBuilderAgent
525
+ };
526
+ }
527
+ await waitForResume(session);
528
+ continue;
529
+ }
530
+ return {
531
+ ...result,
532
+ docPath: selectedDocPath,
533
+ builderAgent: selectedBuilderAgent
534
+ };
535
+ }
536
+ }
537
+ catch (error) {
538
+ caughtError = error;
539
+ session.currentAction = undefined;
540
+ session.dashboard.appendOutput({
541
+ kind: "error",
542
+ text: `${formatTimestamp(now())} ${toError(error).message}`,
543
+ ts: now()
544
+ });
545
+ session.dashboard.updateStats({
546
+ status: "error",
547
+ elapsedMs: Math.max(0, now() - session.startedAt)
548
+ });
549
+ }
550
+ finally {
551
+ clearIntervalImpl(intervalId);
552
+ process.off("SIGINT", sigintHandler);
553
+ session.dashboard.stop();
554
+ session.dashboard.destroy();
555
+ }
556
+ const error = toError(caughtError);
557
+ stderr.write(`Superintendent run failed: ${error.message}\n`);
558
+ if (error.stack) {
559
+ stderr.write(`${error.stack}\n`);
560
+ }
561
+ throw caughtError;
562
+ }
563
+ async function resolveDocPath(options) {
564
+ if (options.docPath) {
565
+ return resolveWorkflowPath(options.docPath, options.cwd, options.homeDir);
566
+ }
567
+ const planDirectory = options.planDirectory ??
568
+ (await resolveSuperintendentPlanDirectory(options.cwd, options.homeDir, options.env, options.fs));
569
+ const docs = await discoverSuperintendentDocs({
570
+ cwd: options.cwd,
571
+ homeDir: options.homeDir,
572
+ planDirectory,
573
+ fs: options.fs
574
+ });
575
+ if (docs.length === 0) {
576
+ throw new UserError("No superintendent documents found.");
577
+ }
578
+ if (options.assumeYes || !options.interactive) {
579
+ return docs[0];
580
+ }
581
+ const selected = await options.selectPrompt({
582
+ message: "Select superintendent document",
583
+ options: docs.map((docPath) => ({
584
+ label: displayPath(docPath, options.cwd, options.homeDir),
585
+ value: docPath
586
+ })),
587
+ initialValue: docs[0]
588
+ });
589
+ if (isCancel(selected)) {
590
+ cancel("Operation cancelled.");
591
+ throw new UserError("Operation cancelled.");
592
+ }
593
+ return selected;
594
+ }
595
+ async function discoverSuperintendentDocs(options) {
596
+ const docs = await listPlanDirectoryDocs(options.fs, options.planDirectory, options.cwd, options.homeDir);
597
+ const matches = [];
598
+ for (const docPath of docs) {
599
+ try {
600
+ const content = await options.fs.readFile(docPath, "utf8");
601
+ const document = parseSuperintendentDoc(docPath, content);
602
+ if (document.frontmatter.kind === "superintendent") {
603
+ matches.push(docPath);
604
+ }
605
+ }
606
+ catch {
607
+ // Ignore invalid docs during discovery.
608
+ }
609
+ }
610
+ return matches;
611
+ }
612
+ async function listPlanDirectoryDocs(fs, planDirectory, cwd, homeDir) {
613
+ const absoluteDir = resolveAbsolutePlanDirectory(planDirectory, cwd, homeDir);
614
+ let entries;
615
+ try {
616
+ entries = await fs.readdir(absoluteDir);
617
+ }
618
+ catch (error) {
619
+ if (isMissingDirectory(error)) {
620
+ return [];
621
+ }
622
+ throw error;
623
+ }
624
+ return entries
625
+ .filter((entry) => entry.toLowerCase().endsWith(".md"))
626
+ .map((entry) => path.join(absoluteDir, entry))
627
+ .sort((left, right) => left.localeCompare(right));
628
+ }
629
+ function resolveAbsolutePlanDirectory(dir, cwd, homeDir) {
630
+ if (dir.startsWith("~/")) {
631
+ return path.join(homeDir, dir.slice(2));
632
+ }
633
+ return path.isAbsolute(dir) ? dir : path.resolve(cwd, dir);
634
+ }
635
+ function normalizeAgentSelection(value) {
636
+ if (typeof value !== "string") {
637
+ return undefined;
638
+ }
639
+ const trimmed = value.trim();
640
+ return trimmed.length > 0 ? trimmed : undefined;
641
+ }
642
+ function isMissingDirectory(error) {
643
+ if (!error || typeof error !== "object" || !("code" in error)) {
644
+ return false;
645
+ }
646
+ const code = error.code;
647
+ return code === "ENOENT" || code === "ENOTDIR";
648
+ }
649
+ function createAgentRunner(options) {
650
+ return async (input) => {
651
+ const activeStage = options.activeStage();
652
+ const agent = activeStage === "builder" ? options.selectedBuilderAgent : input.agent;
653
+ const executeAgent = options.executeAgent ?? executeSpawnAgent;
654
+ const stageLabel = formatStageLabel(activeStage);
655
+ const emitLine = (kind, line) => {
656
+ if (line.length === 0) {
657
+ return;
658
+ }
659
+ if (options.session) {
660
+ options.session.dashboard.appendOutput({
661
+ kind,
662
+ text: `${formatTimestamp(options.now())} [${stageLabel}] ${line}`,
663
+ ts: options.now()
664
+ });
665
+ }
666
+ else {
667
+ options.stderr.write(`[${stageLabel}] ${line}\n`);
668
+ }
669
+ };
670
+ const stdoutBuffer = createLineBuffer((line) => emitLine("tool", line));
671
+ const stderrBuffer = createLineBuffer((line) => emitLine("error", line));
672
+ const onStdout = (chunk) => stdoutBuffer.push(chunk);
673
+ const onStderr = (chunk) => stderrBuffer.push(chunk);
674
+ try {
675
+ const result = await executeAgent(agent, { ...input, onStdout, onStderr });
676
+ if (options.session && result.usage) {
677
+ options.session.tokensIn += result.usage.inputTokens;
678
+ options.session.tokensOut += result.usage.outputTokens;
679
+ }
680
+ return result;
681
+ }
682
+ finally {
683
+ stdoutBuffer.flush();
684
+ stderrBuffer.flush();
685
+ }
686
+ };
687
+ }
688
+ function createLineBuffer(emit) {
689
+ let pending = "";
690
+ return {
691
+ push(chunk) {
692
+ pending += chunk;
693
+ let newlineIndex = pending.indexOf("\n");
694
+ while (newlineIndex !== -1) {
695
+ const raw = pending.slice(0, newlineIndex);
696
+ const line = raw.endsWith("\r") ? raw.slice(0, -1) : raw;
697
+ emit(line);
698
+ pending = pending.slice(newlineIndex + 1);
699
+ newlineIndex = pending.indexOf("\n");
700
+ }
701
+ },
702
+ flush() {
703
+ if (pending.length > 0) {
704
+ const line = pending.endsWith("\r") ? pending.slice(0, -1) : pending;
705
+ emit(line);
706
+ pending = "";
707
+ }
708
+ }
709
+ };
710
+ }
711
+ function formatStageLabel(stage) {
712
+ if (!stage) {
713
+ return "agent";
714
+ }
715
+ if (typeof stage === "string") {
716
+ return stage;
717
+ }
718
+ return `inspector:${stage.inspector}`;
719
+ }
720
+ async function executeSpawnAgent(agent, input) {
721
+ if (parseAgentSpecifier(agent).agent === "poe-agent") {
722
+ return executePoeAgent(agent, input);
723
+ }
724
+ if ((input.onStdout || input.onStderr) && supportsStreaming(agent)) {
725
+ return executeSpawnAgentStreaming(agent, input);
726
+ }
727
+ const tee = input.onStdout || input.onStderr
728
+ ? {
729
+ ...(input.onStdout ? { stdout: { write: input.onStdout } } : {}),
730
+ ...(input.onStderr ? { stderr: { write: input.onStderr } } : {})
731
+ }
732
+ : undefined;
733
+ const result = await spawn(agent, {
734
+ prompt: input.prompt,
735
+ cwd: input.cwd,
736
+ useStdin: true,
737
+ ...(input.mode ? { mode: input.mode } : {}),
738
+ ...(input.mcpServers ? { mcpServers: input.mcpServers } : {}),
739
+ ...(input.signal ? { signal: input.signal } : {}),
740
+ ...(input.logPath ? { logPath: input.logPath } : {}),
741
+ ...(tee ? { tee } : {})
742
+ });
743
+ return {
744
+ stdout: result.stdout,
745
+ stderr: result.stderr,
746
+ exitCode: result.exitCode,
747
+ ...(result.logFile ? { logFile: result.logFile } : {}),
748
+ ...(result.usage ? { usage: result.usage } : {})
749
+ };
750
+ }
751
+ function supportsStreaming(agent) {
752
+ const config = getSpawnConfig(agent);
753
+ return config?.kind === "cli";
754
+ }
755
+ async function executeSpawnAgentStreaming(agent, input) {
756
+ const writer = (line) => {
757
+ input.onStdout?.(`${line}\n`);
758
+ };
759
+ const { events: rawEvents, done } = spawnStreaming({
760
+ agentId: agent,
761
+ prompt: input.prompt,
762
+ cwd: input.cwd,
763
+ useStdin: true,
764
+ ...(input.mode ? { mode: input.mode } : {}),
765
+ ...(input.mcpServers ? { mcpServers: input.mcpServers } : {}),
766
+ ...(input.signal ? { signal: input.signal } : {}),
767
+ ...(input.onStderr ? { tee: { stderr: { write: input.onStderr } } } : {})
768
+ });
769
+ const middlewareContext = {
770
+ sessionId: "unknown",
771
+ agent,
772
+ events: [],
773
+ usage: { inputTokens: 0, outputTokens: 0 },
774
+ eventStream: rawEvents,
775
+ prompt: input.prompt,
776
+ cwd: input.cwd,
777
+ startedAt: new Date(),
778
+ ...(input.logPath ? { logPath: input.logPath } : {}),
779
+ ...(input.mode ? { mode: input.mode } : {})
780
+ };
781
+ await applyMiddlewares([spawnLog, usageCapture, sessionCapture], middlewareContext);
782
+ await acp.withAcpWriter(writer, () => renderAcpStream(middlewareContext.eventStream ?? rawEvents));
783
+ const final = await done;
784
+ const logFile = middlewareContext.logFile ?? final.logFile;
785
+ const sessionResult = middlewareContext.sessionResult;
786
+ return {
787
+ stdout: final.stdout,
788
+ stderr: final.stderr,
789
+ exitCode: final.exitCode,
790
+ ...(logFile ? { logFile } : {}),
791
+ ...(sessionResult?.output ? { summary: sessionResult.output } : {}),
792
+ ...(middlewareContext.usage.inputTokens > 0 || middlewareContext.usage.outputTokens > 0 || middlewareContext.usage.cachedTokens !== undefined
793
+ ? {
794
+ usage: {
795
+ inputTokens: middlewareContext.usage.inputTokens,
796
+ outputTokens: middlewareContext.usage.outputTokens,
797
+ ...(typeof middlewareContext.usage.cachedTokens === "number"
798
+ ? { cachedTokens: middlewareContext.usage.cachedTokens }
799
+ : {})
800
+ }
801
+ }
802
+ : {}),
803
+ ...(sessionResult?.toolCalls.length
804
+ ? {
805
+ toolCalls: sessionResult.toolCalls.flatMap((toolCall) => typeof toolCall.title === "string"
806
+ ? [{ title: toolCall.title, ...(toolCall.input !== undefined ? { input: toolCall.input } : {}) }]
807
+ : [])
808
+ }
809
+ : {})
810
+ };
811
+ }
812
+ function readDashboardStatus(session) {
813
+ if (session.paused) {
814
+ return "paused";
815
+ }
816
+ if (session.state.state === "completed") {
817
+ return "done";
818
+ }
819
+ return "running";
820
+ }
821
+ function formatCurrentAction(session) {
822
+ if (session.paused) {
823
+ return "paused";
824
+ }
825
+ const segments = [
826
+ `state=${session.state.state}`,
827
+ `round=${session.state.round}`,
828
+ ...(session.state.state === "review" ? [`review=${session.state.reviewTurn}`] : []),
829
+ ...(session.currentAction ? [session.currentAction] : [])
830
+ ];
831
+ return segments.join(" · ");
832
+ }
833
+ function formatTimestamp(timestamp) {
834
+ const date = new Date(timestamp);
835
+ const hours = String(date.getHours()).padStart(2, "0");
836
+ const minutes = String(date.getMinutes()).padStart(2, "0");
837
+ const seconds = String(date.getSeconds()).padStart(2, "0");
838
+ return `[${hours}:${minutes}:${seconds}]`;
839
+ }
840
+ function editPlan(dashboard, absolutePath, env, openInEditor) {
841
+ const editor = resolveEditor(env);
842
+ const open = openInEditor ?? openInEditorWithSystem;
843
+ if (editor.mode === "gui") {
844
+ open(absolutePath, env);
845
+ return;
846
+ }
847
+ dashboard.stop();
848
+ try {
849
+ open(absolutePath, env);
850
+ }
851
+ finally {
852
+ dashboard.start();
853
+ }
854
+ }
855
+ function openInEditorWithSystem(absolutePath, env) {
856
+ const editor = resolveEditor(env);
857
+ if (editor.mode === "gui") {
858
+ const child = nodeSpawn(editor.command, [...editor.args, absolutePath], {
859
+ stdio: "ignore",
860
+ detached: true
861
+ });
862
+ child.unref();
863
+ return;
864
+ }
865
+ nodeSpawnSync(editor.command, [...editor.args, absolutePath], { stdio: "inherit" });
866
+ }
867
+ const GUI_EDITOR_BINARIES = new Set(["code", "code-insiders", "cursor", "windsurf", "subl"]);
868
+ function resolveEditor(env) {
869
+ const raw = (env.EDITOR?.trim() || env.VISUAL?.trim() || "").trim();
870
+ if (raw.length === 0) {
871
+ if (env.TERM_PROGRAM === "vscode") {
872
+ return { command: "code", args: [], mode: "gui" };
873
+ }
874
+ return { command: "vi", args: [], mode: "tty" };
875
+ }
876
+ const parts = raw.split(/\s+/);
877
+ const command = parts[0] ?? "vi";
878
+ const args = parts.slice(1);
879
+ const binary = path.basename(command);
880
+ const mode = GUI_EDITOR_BINARIES.has(binary) ? "gui" : "tty";
881
+ return { command, args, mode };
882
+ }
883
+ function releaseWaiters(session) {
884
+ while (session.resumeWaiters.length > 0) {
885
+ session.resumeWaiters.shift()?.();
886
+ }
887
+ }
888
+ async function waitForResume(session) {
889
+ if (session.stopRequested || !session.paused) {
890
+ return;
891
+ }
892
+ await new Promise((resolve) => {
893
+ session.resumeWaiters.push(resolve);
894
+ });
895
+ }
896
+ function stripStopReason(result) {
897
+ return {
898
+ state: result.state,
899
+ round: result.round,
900
+ reviewTurn: result.reviewTurn,
901
+ maxRounds: result.maxRounds,
902
+ maxReviewTurns: result.maxReviewTurns
903
+ };
904
+ }
905
+ function displayPath(filePath, cwd, homeDir) {
906
+ if (filePath.startsWith(`${cwd}${path.sep}`)) {
907
+ return path.relative(cwd, filePath);
908
+ }
909
+ if (filePath.startsWith(`${homeDir}${path.sep}`)) {
910
+ return `~/${path.relative(homeDir, filePath)}`;
911
+ }
912
+ return filePath;
913
+ }
914
+ function createDefaultFs() {
915
+ const fs = {
916
+ readFile: fsPromises.readFile,
917
+ writeFile: fsPromises.writeFile,
918
+ readdir: fsPromises.readdir,
919
+ open: (filePath, flags) => fsPromises.open(filePath, flags),
920
+ stat: async (filePath) => {
921
+ const stat = await fsPromises.stat(filePath);
922
+ return {
923
+ isFile: () => stat.isFile(),
924
+ isDirectory: () => stat.isDirectory(),
925
+ mtimeMs: stat.mtimeMs
926
+ };
927
+ },
928
+ unlink: async (filePath) => {
929
+ await fsPromises.unlink(filePath);
930
+ },
931
+ mkdir: async (filePath, mkdirOptions) => {
932
+ await fsPromises.mkdir(filePath, mkdirOptions);
933
+ },
934
+ rmdir: async (filePath) => {
935
+ await fsPromises.rmdir(filePath);
936
+ },
937
+ rename: async (oldPath, newPath) => {
938
+ await fsPromises.rename(oldPath, newPath);
939
+ }
940
+ };
941
+ return fs;
942
+ }
943
+ function toError(error) {
944
+ return error instanceof Error ? error : new Error(String(error));
945
+ }