poe-code 3.0.197 → 3.0.198

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 (97) 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/index.js +221 -262
  7. package/dist/index.js.map +4 -4
  8. package/dist/providers/claude-code.js +6 -20
  9. package/dist/providers/claude-code.js.map +3 -3
  10. package/dist/providers/codex.js +6 -20
  11. package/dist/providers/codex.js.map +3 -3
  12. package/dist/providers/create-provider.js +0 -2
  13. package/dist/providers/create-provider.js.map +1 -1
  14. package/dist/providers/goose.js +14 -25
  15. package/dist/providers/goose.js.map +3 -3
  16. package/dist/providers/kimi.js +6 -20
  17. package/dist/providers/kimi.js.map +3 -3
  18. package/dist/providers/opencode.js +6 -20
  19. package/dist/providers/opencode.js.map +3 -3
  20. package/dist/providers/poe-agent.js +66 -84
  21. package/dist/providers/poe-agent.js.map +4 -4
  22. package/dist/utils/command-checks.d.ts +2 -1
  23. package/dist/utils/command-checks.js +3 -1
  24. package/dist/utils/command-checks.js.map +1 -1
  25. package/package.json +4 -1
  26. package/packages/memory/dist/cache.js +1 -1
  27. package/packages/memory/dist/explain.js +1 -1
  28. package/packages/memory/dist/index.js +9 -7
  29. package/packages/memory/dist/index.js.map +3 -3
  30. package/packages/memory/dist/query.js +1 -1
  31. package/packages/memory/dist/tokens.js +1 -1
  32. package/packages/superintendent/dist/cli.d.ts +2 -0
  33. package/packages/superintendent/dist/cli.js +41 -0
  34. package/packages/superintendent/dist/commands/builder-group.d.ts +52 -0
  35. package/packages/superintendent/dist/commands/builder-group.js +73 -0
  36. package/packages/superintendent/dist/commands/complete.d.ts +19 -0
  37. package/packages/superintendent/dist/commands/complete.js +54 -0
  38. package/packages/superintendent/dist/commands/index.d.ts +4 -0
  39. package/packages/superintendent/dist/commands/index.js +4 -0
  40. package/packages/superintendent/dist/commands/inspector-group.d.ts +115 -0
  41. package/packages/superintendent/dist/commands/inspector-group.js +133 -0
  42. package/packages/superintendent/dist/commands/install.d.ts +31 -0
  43. package/packages/superintendent/dist/commands/install.js +148 -0
  44. package/packages/superintendent/dist/commands/plan-path.d.ts +9 -0
  45. package/packages/superintendent/dist/commands/plan-path.js +40 -0
  46. package/packages/superintendent/dist/commands/poe-agent-runner.d.ts +5 -0
  47. package/packages/superintendent/dist/commands/poe-agent-runner.js +27 -0
  48. package/packages/superintendent/dist/commands/run.d.ts +86 -0
  49. package/packages/superintendent/dist/commands/run.js +945 -0
  50. package/packages/superintendent/dist/commands/superintendent-group.d.ts +325 -0
  51. package/packages/superintendent/dist/commands/superintendent-group.js +238 -0
  52. package/packages/superintendent/dist/config-scope.d.ts +8 -0
  53. package/packages/superintendent/dist/config-scope.js +9 -0
  54. package/packages/superintendent/dist/direct-execution.d.ts +1 -0
  55. package/packages/superintendent/dist/direct-execution.js +20 -0
  56. package/packages/superintendent/dist/document/parse.d.ts +59 -0
  57. package/packages/superintendent/dist/document/parse.js +409 -0
  58. package/packages/superintendent/dist/document/tasks.d.ts +12 -0
  59. package/packages/superintendent/dist/document/tasks.js +96 -0
  60. package/packages/superintendent/dist/document/write.d.ts +6 -0
  61. package/packages/superintendent/dist/document/write.js +156 -0
  62. package/packages/superintendent/dist/index.d.ts +12 -0
  63. package/packages/superintendent/dist/index.js +15 -0
  64. package/packages/superintendent/dist/mcp.d.ts +24 -0
  65. package/packages/superintendent/dist/mcp.js +202 -0
  66. package/packages/superintendent/dist/runtime/agentic-tools.d.ts +33 -0
  67. package/packages/superintendent/dist/runtime/agentic-tools.js +74 -0
  68. package/packages/superintendent/dist/runtime/loop.d.ts +88 -0
  69. package/packages/superintendent/dist/runtime/loop.js +446 -0
  70. package/packages/superintendent/dist/runtime/resolve-cwd.d.ts +2 -0
  71. package/packages/superintendent/dist/runtime/resolve-cwd.js +10 -0
  72. package/packages/superintendent/dist/runtime/run-builder.d.ts +13 -0
  73. package/packages/superintendent/dist/runtime/run-builder.js +102 -0
  74. package/packages/superintendent/dist/runtime/run-inspector.d.ts +16 -0
  75. package/packages/superintendent/dist/runtime/run-inspector.js +119 -0
  76. package/packages/superintendent/dist/runtime/run-owner-review.d.ts +18 -0
  77. package/packages/superintendent/dist/runtime/run-owner-review.js +208 -0
  78. package/packages/superintendent/dist/runtime/run-superintendent.d.ts +13 -0
  79. package/packages/superintendent/dist/runtime/run-superintendent.js +208 -0
  80. package/packages/superintendent/dist/runtime/system-prompt.d.ts +17 -0
  81. package/packages/superintendent/dist/runtime/system-prompt.js +54 -0
  82. package/packages/superintendent/dist/runtime/templates.d.ts +22 -0
  83. package/packages/superintendent/dist/runtime/templates.js +23 -0
  84. package/packages/superintendent/dist/runtime/types.d.ts +4 -0
  85. package/packages/superintendent/dist/runtime/types.js +1 -0
  86. package/packages/superintendent/dist/runtime/workflow-tool.d.ts +29 -0
  87. package/packages/superintendent/dist/runtime/workflow-tool.js +83 -0
  88. package/packages/superintendent/dist/state/machine.d.ts +14 -0
  89. package/packages/superintendent/dist/state/machine.js +53 -0
  90. package/packages/superintendent/dist/templates/SKILL_superintendent.md +193 -0
  91. package/packages/superintendent/dist/testing/index.d.ts +2 -0
  92. package/packages/superintendent/dist/testing/index.js +1 -0
  93. package/packages/superintendent/dist/testing/simulation.d.ts +57 -0
  94. package/packages/superintendent/dist/testing/simulation.js +346 -0
  95. package/dist/providers/tiny-http-mcp-server.d.ts +0 -22
  96. package/dist/providers/tiny-http-mcp-server.js +0 -1471
  97. package/dist/providers/tiny-http-mcp-server.js.map +0 -7
@@ -0,0 +1,409 @@
1
+ import path from "node:path";
2
+ import { parseDocument } from "yaml";
3
+ export const superintendentDocumentSchemaId = "https://poe-platform.github.io/poe-code/schemas/plans/superintendent.schema.json";
4
+ export const superintendentBaseDocumentSchemaId = "https://poe-platform.github.io/poe-code/schemas/plans/superintendent-base.schema.json";
5
+ const mcpConfigSchema = {
6
+ type: "object",
7
+ properties: {
8
+ command: {
9
+ type: "string",
10
+ minLength: 1
11
+ },
12
+ args: {
13
+ type: "array",
14
+ items: {
15
+ type: "string"
16
+ }
17
+ },
18
+ timeout: {
19
+ type: "number",
20
+ exclusiveMinimum: 0
21
+ }
22
+ },
23
+ required: ["command"],
24
+ additionalProperties: false
25
+ };
26
+ const agentRoleSchema = {
27
+ type: "object",
28
+ properties: {
29
+ agent: {
30
+ type: "string",
31
+ minLength: 1,
32
+ default: "claude-code"
33
+ },
34
+ mode: {
35
+ type: "string",
36
+ minLength: 1
37
+ },
38
+ cwd: {
39
+ type: "string",
40
+ minLength: 1
41
+ },
42
+ mcp: {
43
+ type: "object",
44
+ additionalProperties: mcpConfigSchema
45
+ },
46
+ prompt: {
47
+ type: "string",
48
+ minLength: 1
49
+ }
50
+ },
51
+ required: ["prompt"],
52
+ additionalProperties: false
53
+ };
54
+ const partialAgentRoleSchema = {
55
+ type: "object",
56
+ properties: {
57
+ agent: {
58
+ type: "string",
59
+ minLength: 1,
60
+ default: "claude-code"
61
+ },
62
+ mode: {
63
+ type: "string",
64
+ minLength: 1
65
+ },
66
+ cwd: {
67
+ type: "string",
68
+ minLength: 1
69
+ },
70
+ mcp: {
71
+ type: "object",
72
+ additionalProperties: mcpConfigSchema
73
+ },
74
+ prompt: {
75
+ type: "string",
76
+ minLength: 1
77
+ }
78
+ },
79
+ additionalProperties: false
80
+ };
81
+ const statusSchema = {
82
+ type: "object",
83
+ properties: {
84
+ state: {
85
+ type: "string",
86
+ enum: ["in_progress", "review", "completed"]
87
+ },
88
+ round: {
89
+ type: "integer",
90
+ minimum: 0
91
+ },
92
+ review_turn: {
93
+ type: "integer",
94
+ minimum: 0
95
+ },
96
+ reason: {
97
+ type: "string",
98
+ minLength: 1
99
+ }
100
+ },
101
+ required: ["state", "round", "review_turn"],
102
+ additionalProperties: false
103
+ };
104
+ const partialStatusSchema = {
105
+ type: "object",
106
+ properties: {
107
+ state: {
108
+ type: "string",
109
+ enum: ["in_progress", "review", "completed"]
110
+ },
111
+ round: {
112
+ type: "integer",
113
+ minimum: 0
114
+ },
115
+ review_turn: {
116
+ type: "integer",
117
+ minimum: 0
118
+ },
119
+ reason: {
120
+ type: "string",
121
+ minLength: 1
122
+ }
123
+ },
124
+ additionalProperties: false
125
+ };
126
+ export const superintendentDocumentSchema = {
127
+ $schema: "https://json-schema.org/draft/2020-12/schema",
128
+ $id: superintendentDocumentSchemaId,
129
+ title: "Superintendent plan document",
130
+ type: "object",
131
+ properties: {
132
+ $schema: {
133
+ type: "string",
134
+ const: superintendentDocumentSchemaId
135
+ },
136
+ kind: {
137
+ type: "string",
138
+ const: "superintendent"
139
+ },
140
+ version: {
141
+ type: "integer",
142
+ minimum: 1
143
+ },
144
+ mcp: {
145
+ type: "object",
146
+ additionalProperties: mcpConfigSchema
147
+ },
148
+ builder: agentRoleSchema,
149
+ inspectors: {
150
+ type: "object",
151
+ additionalProperties: agentRoleSchema
152
+ },
153
+ superintendent: agentRoleSchema,
154
+ owner: agentRoleSchema,
155
+ max_rounds: {
156
+ type: "integer",
157
+ minimum: 1,
158
+ default: 100
159
+ },
160
+ status: statusSchema
161
+ },
162
+ required: ["kind", "version", "builder", "superintendent", "owner", "status"],
163
+ additionalProperties: false
164
+ };
165
+ export const superintendentBaseDocumentSchema = {
166
+ $schema: "https://json-schema.org/draft/2020-12/schema",
167
+ $id: superintendentBaseDocumentSchemaId,
168
+ title: "Superintendent base document",
169
+ type: "object",
170
+ properties: {
171
+ $schema: {
172
+ type: "string",
173
+ const: superintendentBaseDocumentSchemaId
174
+ },
175
+ kind: {
176
+ type: "string",
177
+ const: "superintendent-base"
178
+ },
179
+ version: {
180
+ type: "integer",
181
+ minimum: 1
182
+ },
183
+ mcp: {
184
+ type: "object",
185
+ additionalProperties: mcpConfigSchema
186
+ },
187
+ builder: partialAgentRoleSchema,
188
+ inspectors: {
189
+ type: "object",
190
+ additionalProperties: partialAgentRoleSchema
191
+ },
192
+ superintendent: partialAgentRoleSchema,
193
+ owner: partialAgentRoleSchema,
194
+ max_rounds: {
195
+ type: "integer",
196
+ minimum: 1
197
+ },
198
+ status: partialStatusSchema
199
+ },
200
+ required: ["kind"],
201
+ additionalProperties: false
202
+ };
203
+ const validStatusStates = new Set(["in_progress", "review", "completed"]);
204
+ export function parseSuperintendentDoc(filePath, content) {
205
+ const resolvedFilePath = path.resolve(filePath);
206
+ const { frontmatterText, body } = splitFrontmatter(resolvedFilePath, content);
207
+ const parsedFrontmatter = parseYamlFrontmatter(resolvedFilePath, frontmatterText);
208
+ return {
209
+ filePath: resolvedFilePath,
210
+ body,
211
+ frontmatter: parseFrontmatter(resolvedFilePath, parsedFrontmatter)
212
+ };
213
+ }
214
+ export function readExplicitBuilderAgent(filePath, content) {
215
+ const resolvedFilePath = path.resolve(filePath);
216
+ const { frontmatterText } = splitFrontmatter(resolvedFilePath, content);
217
+ const parsedFrontmatter = parseYamlFrontmatter(resolvedFilePath, frontmatterText);
218
+ const frontmatter = expectRecord(parsedFrontmatter, "frontmatter", resolvedFilePath);
219
+ if (frontmatter.builder === undefined) {
220
+ return undefined;
221
+ }
222
+ const builder = expectRecord(frontmatter.builder, "builder", resolvedFilePath);
223
+ return builder.agent === undefined
224
+ ? undefined
225
+ : expectString(builder.agent, "builder.agent", resolvedFilePath);
226
+ }
227
+ function splitFrontmatter(filePath, content) {
228
+ const normalizedContent = content.startsWith("\uFEFF") ? content.slice(1) : content;
229
+ const openingLineBreak = readOpeningLineBreak(normalizedContent);
230
+ if (openingLineBreak === undefined) {
231
+ throw new Error(`${filePath}: expected YAML frontmatter delimited by ---`);
232
+ }
233
+ const frontmatterStart = 3 + openingLineBreak.length;
234
+ const closingFenceIndex = findClosingFence(normalizedContent, frontmatterStart, filePath);
235
+ const frontmatterEnd = readFrontmatterEnd(normalizedContent, closingFenceIndex);
236
+ return {
237
+ frontmatterText: normalizedContent.slice(frontmatterStart, frontmatterEnd),
238
+ body: readBody(normalizedContent, closingFenceIndex + 4)
239
+ };
240
+ }
241
+ function readOpeningLineBreak(content) {
242
+ if (!content.startsWith("---")) {
243
+ return undefined;
244
+ }
245
+ const nextCharacter = content[3];
246
+ if (nextCharacter === "\n") {
247
+ return "\n";
248
+ }
249
+ if (nextCharacter === "\r" && content[4] === "\n") {
250
+ return "\r\n";
251
+ }
252
+ return nextCharacter === undefined ? "\n" : undefined;
253
+ }
254
+ function findClosingFence(content, searchFrom, filePath) {
255
+ let currentIndex = searchFrom - 1;
256
+ while (currentIndex < content.length) {
257
+ const candidateIndex = content.indexOf("\n---", currentIndex);
258
+ if (candidateIndex === -1) {
259
+ throw new Error(`${filePath}: missing YAML frontmatter end delimiter (---)`);
260
+ }
261
+ const fenceEnd = candidateIndex + 4;
262
+ const nextCharacter = content[fenceEnd];
263
+ if (nextCharacter === "\n" || nextCharacter === undefined) {
264
+ return candidateIndex;
265
+ }
266
+ if (nextCharacter === "\r" && content[fenceEnd + 1] === "\n") {
267
+ return candidateIndex;
268
+ }
269
+ currentIndex = fenceEnd;
270
+ }
271
+ throw new Error(`${filePath}: missing YAML frontmatter end delimiter (---)`);
272
+ }
273
+ function readBody(content, bodyStart) {
274
+ const nextCharacter = content[bodyStart];
275
+ if (nextCharacter === "\n") {
276
+ return content.slice(bodyStart + 1);
277
+ }
278
+ if (nextCharacter === "\r" && content[bodyStart + 1] === "\n") {
279
+ return content.slice(bodyStart + 2);
280
+ }
281
+ return content.slice(bodyStart);
282
+ }
283
+ function readFrontmatterEnd(content, closingFenceIndex) {
284
+ return content[closingFenceIndex - 1] === "\r" ? closingFenceIndex - 1 : closingFenceIndex;
285
+ }
286
+ function parseYamlFrontmatter(filePath, frontmatterText) {
287
+ const document = parseDocument(frontmatterText);
288
+ if (document.errors.length > 0) {
289
+ throw new Error(`${filePath}: invalid YAML frontmatter: ${document.errors[0].message}`);
290
+ }
291
+ return document.toJSON();
292
+ }
293
+ function parseFrontmatter(filePath, value) {
294
+ const frontmatter = expectRecord(value, "frontmatter", filePath);
295
+ const kind = frontmatter.kind;
296
+ if (kind === undefined) {
297
+ throw new Error(`${filePath}: missing \`kind\` field in frontmatter`);
298
+ }
299
+ if (kind !== "superintendent") {
300
+ throw new Error(`${filePath}: frontmatter kind must be "superintendent"`);
301
+ }
302
+ return {
303
+ kind,
304
+ version: expectNumber(frontmatter.version, "version", filePath),
305
+ mcp: parseMcpMap(frontmatter.mcp, filePath),
306
+ builder: parseRequiredRole(frontmatter.builder, "builder", filePath),
307
+ inspectors: parseInspectorMap(frontmatter.inspectors, filePath),
308
+ superintendent: parseRequiredRole(frontmatter.superintendent, "superintendent", filePath),
309
+ owner: parseRequiredRole(frontmatter.owner, "owner", filePath),
310
+ max_rounds: frontmatter.max_rounds === undefined
311
+ ? 100
312
+ : expectNumber(frontmatter.max_rounds, "max_rounds", filePath),
313
+ status: parseStatusBlock(frontmatter.status, filePath)
314
+ };
315
+ }
316
+ function parseRequiredRole(value, roleName, filePath) {
317
+ if (value === undefined) {
318
+ throw new Error(`${filePath}: missing required role \`${roleName}\``);
319
+ }
320
+ const role = expectRecord(value, roleName, filePath);
321
+ const mcp = role.mcp === undefined ? undefined : parseMcpMap(role.mcp, filePath, `${roleName}.mcp`);
322
+ return {
323
+ agent: role.agent === undefined
324
+ ? "claude-code"
325
+ : expectString(role.agent, `${roleName}.agent`, filePath),
326
+ mode: role.mode === undefined ? undefined : expectString(role.mode, `${roleName}.mode`, filePath),
327
+ cwd: role.cwd === undefined ? undefined : expectString(role.cwd, `${roleName}.cwd`, filePath),
328
+ mcp,
329
+ prompt: expectString(role.prompt, `${roleName}.prompt`, filePath)
330
+ };
331
+ }
332
+ function parseInspectorMap(value, filePath) {
333
+ if (value === undefined) {
334
+ return undefined;
335
+ }
336
+ const inspectors = expectRecord(value, "inspectors", filePath);
337
+ return Object.fromEntries(Object.entries(inspectors).map(([name, config]) => [
338
+ name,
339
+ parseRequiredRole(config, `inspectors.${name}`, filePath)
340
+ ]));
341
+ }
342
+ function parseMcpMap(value, filePath, fieldName = "mcp") {
343
+ if (value === undefined) {
344
+ return undefined;
345
+ }
346
+ const mcp = expectRecord(value, fieldName, filePath);
347
+ return Object.fromEntries(Object.entries(mcp).map(([name, config]) => [
348
+ name,
349
+ parseMcpConfig(config, `${fieldName}.${name}`, filePath)
350
+ ]));
351
+ }
352
+ function parseMcpConfig(value, fieldName, filePath) {
353
+ const config = expectRecord(value, fieldName, filePath);
354
+ return {
355
+ command: expectString(config.command, `${fieldName}.command`, filePath),
356
+ args: config.args === undefined
357
+ ? undefined
358
+ : expectStringArray(config.args, `${fieldName}.args`, filePath),
359
+ timeout: config.timeout === undefined
360
+ ? undefined
361
+ : expectPositiveNumber(config.timeout, `${fieldName}.timeout`, filePath)
362
+ };
363
+ }
364
+ function parseStatusBlock(value, filePath) {
365
+ const status = expectRecord(value, "status", filePath);
366
+ const state = expectString(status.state, "status.state", filePath);
367
+ if (!validStatusStates.has(state)) {
368
+ throw new Error(`${filePath}: status.state must be one of in_progress, review, completed`);
369
+ }
370
+ const parsedState = state;
371
+ return {
372
+ state: parsedState,
373
+ round: expectNumber(status.round, "status.round", filePath),
374
+ review_turn: expectNumber(status.review_turn, "status.review_turn", filePath)
375
+ };
376
+ }
377
+ function expectRecord(value, fieldName, filePath) {
378
+ if (!isRecord(value)) {
379
+ throw new Error(`${filePath}: ${fieldName} must be a mapping`);
380
+ }
381
+ return value;
382
+ }
383
+ function expectString(value, fieldName, filePath) {
384
+ if (typeof value !== "string") {
385
+ throw new Error(`${filePath}: ${fieldName} must be a string`);
386
+ }
387
+ return value;
388
+ }
389
+ function expectNumber(value, fieldName, filePath) {
390
+ if (typeof value !== "number" || !Number.isFinite(value)) {
391
+ throw new Error(`${filePath}: ${fieldName} must be a number`);
392
+ }
393
+ return value;
394
+ }
395
+ function expectPositiveNumber(value, fieldName, filePath) {
396
+ if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
397
+ throw new Error(`${filePath}: ${fieldName} must be a positive number`);
398
+ }
399
+ return value;
400
+ }
401
+ function expectStringArray(value, fieldName, filePath) {
402
+ if (!Array.isArray(value) || value.some((item) => typeof item !== "string")) {
403
+ throw new Error(`${filePath}: ${fieldName} must be an array of strings`);
404
+ }
405
+ return value;
406
+ }
407
+ function isRecord(value) {
408
+ return typeof value === "object" && value !== null && !Array.isArray(value);
409
+ }
@@ -0,0 +1,12 @@
1
+ export type TaskItem = {
2
+ text: string;
3
+ done: boolean;
4
+ };
5
+ export type TaskBoard = {
6
+ tasks: TaskItem[];
7
+ allDone: boolean;
8
+ openCount: number;
9
+ doneCount: number;
10
+ };
11
+ export declare function parseTaskBoard(body: string): TaskBoard;
12
+ export declare function hasTaskBoard(body: string): boolean;
@@ -0,0 +1,96 @@
1
+ import { parse } from "@poe-code/design-system";
2
+ export function parseTaskBoard(body) {
3
+ const sectionNodes = readTaskBoardSection(body);
4
+ const tasks = [];
5
+ for (const node of sectionNodes) {
6
+ collectTasks(node, tasks);
7
+ }
8
+ const doneCount = tasks.filter((task) => task.done).length;
9
+ const openCount = tasks.length - doneCount;
10
+ return {
11
+ tasks,
12
+ allDone: openCount === 0,
13
+ openCount,
14
+ doneCount
15
+ };
16
+ }
17
+ export function hasTaskBoard(body) {
18
+ return findTaskBoardHeadingIndex(parseRoot(body).children) !== -1;
19
+ }
20
+ function readTaskBoardSection(body) {
21
+ const root = parseRoot(body);
22
+ const headingIndex = findTaskBoardHeadingIndex(root.children);
23
+ if (headingIndex === -1) {
24
+ throw new Error('Missing "## Task Board" section');
25
+ }
26
+ const sectionNodes = [];
27
+ for (let index = headingIndex + 1; index < root.children.length; index += 1) {
28
+ const node = root.children[index];
29
+ if (node.type === "heading" && node.depth <= 2) {
30
+ break;
31
+ }
32
+ sectionNodes.push(node);
33
+ }
34
+ return sectionNodes;
35
+ }
36
+ function parseRoot(body) {
37
+ const ast = parse(body).ast;
38
+ if (ast.type !== "root") {
39
+ throw new Error("Expected markdown root node");
40
+ }
41
+ return ast;
42
+ }
43
+ function findTaskBoardHeadingIndex(children) {
44
+ return children.findIndex((node) => node.type === "heading" && node.depth === 2 && extractText(node).trim() === "Task Board");
45
+ }
46
+ function collectTasks(node, tasks) {
47
+ if (node.type === "listItem" && typeof node.checked === "boolean") {
48
+ tasks.push({
49
+ text: readTaskText(node),
50
+ done: node.checked
51
+ });
52
+ }
53
+ if (!("children" in node)) {
54
+ return;
55
+ }
56
+ for (const child of node.children) {
57
+ collectTasks(child, tasks);
58
+ }
59
+ }
60
+ function readTaskText(node) {
61
+ let text = "";
62
+ for (const child of node.children) {
63
+ if (child.type === "list") {
64
+ continue;
65
+ }
66
+ const childText = extractText(child).trim();
67
+ if (childText.length === 0) {
68
+ continue;
69
+ }
70
+ text = text.length === 0 ? childText : `${text}\n${childText}`;
71
+ }
72
+ return text;
73
+ }
74
+ function extractText(node) {
75
+ switch (node.type) {
76
+ case "text":
77
+ case "inlineCode":
78
+ case "code":
79
+ case "html":
80
+ return node.value;
81
+ case "image":
82
+ return node.alt;
83
+ case "break":
84
+ return "\n";
85
+ case "footnoteReference":
86
+ return node.label;
87
+ case "thematicBreak":
88
+ case "frontmatter":
89
+ return "";
90
+ default:
91
+ if (!("children" in node)) {
92
+ return "";
93
+ }
94
+ return node.children.map(extractText).join("");
95
+ }
96
+ }
@@ -0,0 +1,6 @@
1
+ import { type StatusBlock } from "./parse.js";
2
+ export declare function updateStatus(filePath: string, content: string, status: StatusBlock): string;
3
+ export declare function setStatusReason(filePath: string, content: string, reason: string | undefined): string;
4
+ export declare function incrementRound(filePath: string, content: string): string;
5
+ export declare function setReviewTurn(filePath: string, content: string, turn: number): string;
6
+ export declare function transitionState(filePath: string, content: string, newState: StatusBlock["state"]): string;
@@ -0,0 +1,156 @@
1
+ import path from "node:path";
2
+ import { isMap, parseDocument } from "yaml";
3
+ import { parseSuperintendentDoc, superintendentDocumentSchemaId } from "./parse.js";
4
+ export function updateStatus(filePath, content, status) {
5
+ return updateFrontmatter(filePath, content, (frontmatterDocument) => {
6
+ frontmatterDocument.set("status", {
7
+ state: status.state,
8
+ round: status.round,
9
+ review_turn: status.review_turn
10
+ });
11
+ });
12
+ }
13
+ export function setStatusReason(filePath, content, reason) {
14
+ return updateFrontmatter(filePath, content, (frontmatterDocument) => {
15
+ if (reason === undefined) {
16
+ frontmatterDocument.deleteIn(["status", "reason"]);
17
+ return;
18
+ }
19
+ frontmatterDocument.setIn(["status", "reason"], reason);
20
+ });
21
+ }
22
+ function updateFrontmatter(filePath, content, mutate) {
23
+ const resolvedFilePath = path.resolve(filePath);
24
+ const parts = splitDocument(resolvedFilePath, content);
25
+ const frontmatterDocument = parseDocument(parts.frontmatterText);
26
+ if (frontmatterDocument.errors.length > 0) {
27
+ throw new Error(`${resolvedFilePath}: invalid YAML frontmatter: ${frontmatterDocument.errors[0].message}`);
28
+ }
29
+ mutate(frontmatterDocument);
30
+ canonicalizeFrontmatter(frontmatterDocument);
31
+ return [
32
+ parts.bom,
33
+ "---",
34
+ parts.lineBreak,
35
+ formatFrontmatter(frontmatterDocument.toString(), parts.lineBreak),
36
+ parts.frontmatterSuffix,
37
+ parts.body
38
+ ].join("");
39
+ }
40
+ function getTopLevelMap(frontmatterDocument) {
41
+ if (!frontmatterDocument.contents || !isMap(frontmatterDocument.contents)) {
42
+ throw new Error("Expected superintendent frontmatter to be a top-level object.");
43
+ }
44
+ return frontmatterDocument.contents;
45
+ }
46
+ function reorderTopLevelKeys(map, keys) {
47
+ const remaining = [...map.items];
48
+ const ordered = keys.flatMap((key) => {
49
+ const index = remaining.findIndex((item) => item.key?.toString() === key);
50
+ return index === -1 ? [] : remaining.splice(index, 1);
51
+ });
52
+ map.items = [...ordered, ...remaining];
53
+ }
54
+ function canonicalizeFrontmatter(frontmatterDocument) {
55
+ const map = getTopLevelMap(frontmatterDocument);
56
+ map.delete("maxExperiments");
57
+ map.delete("metricTimeout");
58
+ map.delete("planPath");
59
+ map.set("$schema", superintendentDocumentSchemaId);
60
+ map.set("kind", "superintendent");
61
+ map.set("version", 1);
62
+ reorderTopLevelKeys(map, ["$schema", "kind", "version"]);
63
+ }
64
+ export function incrementRound(filePath, content) {
65
+ const document = parseSuperintendentDoc(filePath, content);
66
+ return updateStatus(filePath, content, {
67
+ ...document.frontmatter.status,
68
+ round: document.frontmatter.status.round + 1
69
+ });
70
+ }
71
+ export function setReviewTurn(filePath, content, turn) {
72
+ const document = parseSuperintendentDoc(filePath, content);
73
+ return updateStatus(filePath, content, {
74
+ ...document.frontmatter.status,
75
+ review_turn: turn
76
+ });
77
+ }
78
+ export function transitionState(filePath, content, newState) {
79
+ const document = parseSuperintendentDoc(filePath, content);
80
+ return updateStatus(filePath, content, {
81
+ ...document.frontmatter.status,
82
+ state: newState,
83
+ review_turn: newState === "in_progress" ? 0 : document.frontmatter.status.review_turn
84
+ });
85
+ }
86
+ function splitDocument(filePath, content) {
87
+ const bom = content.startsWith("\uFEFF") ? "\uFEFF" : "";
88
+ const normalizedContent = bom ? content.slice(1) : content;
89
+ const lineBreak = readOpeningLineBreak(normalizedContent);
90
+ if (lineBreak === undefined) {
91
+ throw new Error(`${filePath}: expected YAML frontmatter delimited by ---`);
92
+ }
93
+ const frontmatterStart = 3 + lineBreak.length;
94
+ const closingFenceIndex = findClosingFence(normalizedContent, frontmatterStart, filePath);
95
+ const frontmatterEnd = readFrontmatterEnd(normalizedContent, closingFenceIndex);
96
+ const bodyStart = readBodyStart(normalizedContent, closingFenceIndex + 4);
97
+ return {
98
+ bom,
99
+ lineBreak,
100
+ frontmatterText: normalizedContent.slice(frontmatterStart, frontmatterEnd),
101
+ frontmatterSuffix: normalizedContent.slice(frontmatterEnd, bodyStart),
102
+ body: normalizedContent.slice(bodyStart)
103
+ };
104
+ }
105
+ function readOpeningLineBreak(content) {
106
+ if (!content.startsWith("---")) {
107
+ return undefined;
108
+ }
109
+ const nextCharacter = content[3];
110
+ if (nextCharacter === "\n") {
111
+ return "\n";
112
+ }
113
+ if (nextCharacter === "\r" && content[4] === "\n") {
114
+ return "\r\n";
115
+ }
116
+ return nextCharacter === undefined ? "\n" : undefined;
117
+ }
118
+ function findClosingFence(content, searchFrom, filePath) {
119
+ let currentIndex = searchFrom - 1;
120
+ while (currentIndex < content.length) {
121
+ const candidateIndex = content.indexOf("\n---", currentIndex);
122
+ if (candidateIndex === -1) {
123
+ throw new Error(`${filePath}: missing YAML frontmatter end delimiter (---)`);
124
+ }
125
+ const fenceEnd = candidateIndex + 4;
126
+ const nextCharacter = content[fenceEnd];
127
+ if (nextCharacter === "\n" || nextCharacter === undefined) {
128
+ return candidateIndex;
129
+ }
130
+ if (nextCharacter === "\r" && content[fenceEnd + 1] === "\n") {
131
+ return candidateIndex;
132
+ }
133
+ currentIndex = fenceEnd;
134
+ }
135
+ throw new Error(`${filePath}: missing YAML frontmatter end delimiter (---)`);
136
+ }
137
+ function readBodyStart(content, bodyStart) {
138
+ const nextCharacter = content[bodyStart];
139
+ if (nextCharacter === "\n") {
140
+ return bodyStart + 1;
141
+ }
142
+ if (nextCharacter === "\r" && content[bodyStart + 1] === "\n") {
143
+ return bodyStart + 2;
144
+ }
145
+ return bodyStart;
146
+ }
147
+ function readFrontmatterEnd(content, closingFenceIndex) {
148
+ return content[closingFenceIndex - 1] === "\r" ? closingFenceIndex - 1 : closingFenceIndex;
149
+ }
150
+ function formatFrontmatter(serialized, lineBreak) {
151
+ const normalized = serialized.endsWith("\n") ? serialized.slice(0, -1) : serialized;
152
+ if (lineBreak === "\n") {
153
+ return normalized;
154
+ }
155
+ return normalized.replaceAll("\n", lineBreak);
156
+ }
@@ -0,0 +1,12 @@
1
+ export { parseSuperintendentDoc, superintendentBaseDocumentSchema, superintendentBaseDocumentSchemaId, superintendentDocumentSchema, superintendentDocumentSchemaId } from "./document/parse.js";
2
+ export { updateStatus, transitionState, incrementRound } from "./document/write.js";
3
+ export { parseTaskBoard, hasTaskBoard } from "./document/tasks.js";
4
+ export type { SuperintendentDoc, SuperintendentFrontmatter, StatusBlock, TaskBoard, TaskItem } from "./document/parse.js";
5
+ export { runLoop } from "./runtime/loop.js";
6
+ export { runBuilder } from "./runtime/run-builder.js";
7
+ export { runInspector, runAllInspectors } from "./runtime/run-inspector.js";
8
+ export { resolveTemplate } from "./runtime/templates.js";
9
+ export type { LoopCallbacks, BuilderResult, InspectorResult, TemplateContext } from "./runtime/types.js";
10
+ export { createLoopState, applyTransition, isComplete } from "./state/machine.js";
11
+ export { superintendentGroup } from "./commands/index.js";
12
+ export { superintendentConfigScope } from "./config-scope.js";