poe-code 3.0.196 → 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.
- package/dist/cli/commands/configure.d.ts +0 -7
- package/dist/cli/commands/configure.js +11 -14
- package/dist/cli/commands/configure.js.map +1 -1
- package/dist/cli/commands/provider.js +8 -1
- package/dist/cli/commands/provider.js.map +1 -1
- package/dist/index.js +263 -269
- package/dist/index.js.map +4 -4
- package/dist/providers/claude-code.js +6 -20
- package/dist/providers/claude-code.js.map +3 -3
- package/dist/providers/codex.js +6 -20
- package/dist/providers/codex.js.map +3 -3
- package/dist/providers/create-provider.js +0 -2
- package/dist/providers/create-provider.js.map +1 -1
- package/dist/providers/goose.js +14 -25
- package/dist/providers/goose.js.map +3 -3
- package/dist/providers/kimi.js +6 -20
- package/dist/providers/kimi.js.map +3 -3
- package/dist/providers/opencode.js +6 -20
- package/dist/providers/opencode.js.map +3 -3
- package/dist/providers/poe-agent.js +66 -84
- package/dist/providers/poe-agent.js.map +4 -4
- package/dist/utils/command-checks.d.ts +2 -1
- package/dist/utils/command-checks.js +3 -1
- package/dist/utils/command-checks.js.map +1 -1
- package/package.json +4 -1
- package/packages/memory/dist/cache.js +1 -1
- package/packages/memory/dist/explain.js +1 -1
- package/packages/memory/dist/index.js +18 -7
- package/packages/memory/dist/index.js.map +3 -3
- package/packages/memory/dist/query.js +1 -1
- package/packages/memory/dist/tokens.js +1 -1
- package/packages/superintendent/dist/cli.d.ts +2 -0
- package/packages/superintendent/dist/cli.js +41 -0
- package/packages/superintendent/dist/commands/builder-group.d.ts +52 -0
- package/packages/superintendent/dist/commands/builder-group.js +73 -0
- package/packages/superintendent/dist/commands/complete.d.ts +19 -0
- package/packages/superintendent/dist/commands/complete.js +54 -0
- package/packages/superintendent/dist/commands/index.d.ts +4 -0
- package/packages/superintendent/dist/commands/index.js +4 -0
- package/packages/superintendent/dist/commands/inspector-group.d.ts +115 -0
- package/packages/superintendent/dist/commands/inspector-group.js +133 -0
- package/packages/superintendent/dist/commands/install.d.ts +31 -0
- package/packages/superintendent/dist/commands/install.js +148 -0
- package/packages/superintendent/dist/commands/plan-path.d.ts +9 -0
- package/packages/superintendent/dist/commands/plan-path.js +40 -0
- package/packages/superintendent/dist/commands/poe-agent-runner.d.ts +5 -0
- package/packages/superintendent/dist/commands/poe-agent-runner.js +27 -0
- package/packages/superintendent/dist/commands/run.d.ts +86 -0
- package/packages/superintendent/dist/commands/run.js +945 -0
- package/packages/superintendent/dist/commands/superintendent-group.d.ts +325 -0
- package/packages/superintendent/dist/commands/superintendent-group.js +238 -0
- package/packages/superintendent/dist/config-scope.d.ts +8 -0
- package/packages/superintendent/dist/config-scope.js +9 -0
- package/packages/superintendent/dist/direct-execution.d.ts +1 -0
- package/packages/superintendent/dist/direct-execution.js +20 -0
- package/packages/superintendent/dist/document/parse.d.ts +59 -0
- package/packages/superintendent/dist/document/parse.js +409 -0
- package/packages/superintendent/dist/document/tasks.d.ts +12 -0
- package/packages/superintendent/dist/document/tasks.js +96 -0
- package/packages/superintendent/dist/document/write.d.ts +6 -0
- package/packages/superintendent/dist/document/write.js +156 -0
- package/packages/superintendent/dist/index.d.ts +12 -0
- package/packages/superintendent/dist/index.js +15 -0
- package/packages/superintendent/dist/mcp.d.ts +24 -0
- package/packages/superintendent/dist/mcp.js +202 -0
- package/packages/superintendent/dist/runtime/agentic-tools.d.ts +33 -0
- package/packages/superintendent/dist/runtime/agentic-tools.js +74 -0
- package/packages/superintendent/dist/runtime/loop.d.ts +88 -0
- package/packages/superintendent/dist/runtime/loop.js +446 -0
- package/packages/superintendent/dist/runtime/resolve-cwd.d.ts +2 -0
- package/packages/superintendent/dist/runtime/resolve-cwd.js +10 -0
- package/packages/superintendent/dist/runtime/run-builder.d.ts +13 -0
- package/packages/superintendent/dist/runtime/run-builder.js +102 -0
- package/packages/superintendent/dist/runtime/run-inspector.d.ts +16 -0
- package/packages/superintendent/dist/runtime/run-inspector.js +119 -0
- package/packages/superintendent/dist/runtime/run-owner-review.d.ts +18 -0
- package/packages/superintendent/dist/runtime/run-owner-review.js +208 -0
- package/packages/superintendent/dist/runtime/run-superintendent.d.ts +13 -0
- package/packages/superintendent/dist/runtime/run-superintendent.js +208 -0
- package/packages/superintendent/dist/runtime/system-prompt.d.ts +17 -0
- package/packages/superintendent/dist/runtime/system-prompt.js +54 -0
- package/packages/superintendent/dist/runtime/templates.d.ts +22 -0
- package/packages/superintendent/dist/runtime/templates.js +23 -0
- package/packages/superintendent/dist/runtime/types.d.ts +4 -0
- package/packages/superintendent/dist/runtime/types.js +1 -0
- package/packages/superintendent/dist/runtime/workflow-tool.d.ts +29 -0
- package/packages/superintendent/dist/runtime/workflow-tool.js +83 -0
- package/packages/superintendent/dist/state/machine.d.ts +14 -0
- package/packages/superintendent/dist/state/machine.js +53 -0
- package/packages/superintendent/dist/templates/SKILL_superintendent.md +193 -0
- package/packages/superintendent/dist/testing/index.d.ts +2 -0
- package/packages/superintendent/dist/testing/index.js +1 -0
- package/packages/superintendent/dist/testing/simulation.d.ts +57 -0
- package/packages/superintendent/dist/testing/simulation.js +346 -0
- package/dist/providers/tiny-http-mcp-server.d.ts +0 -22
- package/dist/providers/tiny-http-mcp-server.js +0 -1471
- 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";
|