@umgbhalla/pi-gigaplan 0.1.0 → 0.1.2
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/extensions/index.ts +34 -30
- package/package.json +1 -1
- package/skills/gigaplan/SKILL.md +1 -1
- package/src/evaluation.ts +1 -1
- package/src/prompts.ts +10 -9
- package/src/workers.ts +119 -0
package/extensions/index.ts
CHANGED
|
@@ -43,7 +43,7 @@ import {
|
|
|
43
43
|
activePlanDirs,
|
|
44
44
|
resolvePlanDir,
|
|
45
45
|
loadPlan,
|
|
46
|
-
|
|
46
|
+
savePlanState,
|
|
47
47
|
latestPlanRecord,
|
|
48
48
|
latestPlanPath,
|
|
49
49
|
latestPlanMetaPath,
|
|
@@ -173,7 +173,7 @@ function initPlan(
|
|
|
173
173
|
last_evaluation: {},
|
|
174
174
|
};
|
|
175
175
|
|
|
176
|
-
|
|
176
|
+
savePlanState(planDir, state);
|
|
177
177
|
saveFlagRegistry(planDir, { flags: [] });
|
|
178
178
|
|
|
179
179
|
return { planDir, state };
|
|
@@ -202,7 +202,7 @@ function processStepOutput(
|
|
|
202
202
|
duration_ms: durationMs,
|
|
203
203
|
result: "success",
|
|
204
204
|
});
|
|
205
|
-
|
|
205
|
+
savePlanState(planDir, state);
|
|
206
206
|
return {
|
|
207
207
|
success: true,
|
|
208
208
|
step: "clarify",
|
|
@@ -264,7 +264,7 @@ function processStepOutput(
|
|
|
264
264
|
result: "success",
|
|
265
265
|
output_file: planFile,
|
|
266
266
|
});
|
|
267
|
-
|
|
267
|
+
savePlanState(planDir, state);
|
|
268
268
|
|
|
269
269
|
return {
|
|
270
270
|
success: true,
|
|
@@ -324,7 +324,7 @@ function processStepOutput(
|
|
|
324
324
|
output_file: critiqueFile,
|
|
325
325
|
flags_count: newFlags.length,
|
|
326
326
|
});
|
|
327
|
-
|
|
327
|
+
savePlanState(planDir, state);
|
|
328
328
|
|
|
329
329
|
return {
|
|
330
330
|
success: true,
|
|
@@ -345,7 +345,7 @@ function processStepOutput(
|
|
|
345
345
|
result: "success",
|
|
346
346
|
output_file: "execution.json",
|
|
347
347
|
});
|
|
348
|
-
|
|
348
|
+
savePlanState(planDir, state);
|
|
349
349
|
|
|
350
350
|
return {
|
|
351
351
|
success: true,
|
|
@@ -366,7 +366,7 @@ function processStepOutput(
|
|
|
366
366
|
result: "success",
|
|
367
367
|
output_file: "review.json",
|
|
368
368
|
});
|
|
369
|
-
|
|
369
|
+
savePlanState(planDir, state);
|
|
370
370
|
|
|
371
371
|
const criteria = (payload.criteria as Array<{ name: string; pass: boolean }>) ?? [];
|
|
372
372
|
const passed = criteria.filter((c) => c.pass).length;
|
|
@@ -405,7 +405,7 @@ function runEvaluate(planDir: string, state: PlanState): StepResult {
|
|
|
405
405
|
(evaluation.signals as Record<string, unknown>).weighted_score as number,
|
|
406
406
|
];
|
|
407
407
|
|
|
408
|
-
state.last_evaluation = evaluation
|
|
408
|
+
state.last_evaluation = evaluation as unknown as Record<string, unknown>;
|
|
409
409
|
state.current_state = STATE_EVALUATED;
|
|
410
410
|
state.history.push({
|
|
411
411
|
step: "evaluate",
|
|
@@ -414,13 +414,13 @@ function runEvaluate(planDir: string, state: PlanState): StepResult {
|
|
|
414
414
|
recommendation: evaluation.recommendation,
|
|
415
415
|
output_file: evalFile,
|
|
416
416
|
});
|
|
417
|
-
|
|
417
|
+
savePlanState(planDir, state);
|
|
418
418
|
|
|
419
419
|
return {
|
|
420
420
|
success: true,
|
|
421
421
|
step: "evaluate",
|
|
422
422
|
summary: `Evaluation: ${evaluation.recommendation} (${evaluation.confidence} confidence). ${evaluation.rationale}`,
|
|
423
|
-
nextSteps: evaluation.valid_next_steps,
|
|
423
|
+
nextSteps: evaluation.valid_next_steps ?? [],
|
|
424
424
|
artifacts: [evalFile],
|
|
425
425
|
};
|
|
426
426
|
}
|
|
@@ -470,7 +470,7 @@ function runGate(planDir: string, state: PlanState): StepResult {
|
|
|
470
470
|
timestamp: nowUtc(),
|
|
471
471
|
result: passed ? "success" : "failed",
|
|
472
472
|
});
|
|
473
|
-
|
|
473
|
+
savePlanState(planDir, state);
|
|
474
474
|
|
|
475
475
|
return {
|
|
476
476
|
success: passed,
|
|
@@ -492,7 +492,8 @@ export default function gigaplanExtension(pi: ExtensionAPI) {
|
|
|
492
492
|
// Widget state
|
|
493
493
|
let activePlan: { name: string; state: string; step: string } | null = null;
|
|
494
494
|
|
|
495
|
-
function updateWidget(ctx
|
|
495
|
+
function updateWidget(ctx?: any) {
|
|
496
|
+
if (!ctx?.ui) return;
|
|
496
497
|
if (!activePlan) {
|
|
497
498
|
ctx.ui.setStatus("gigaplan", "");
|
|
498
499
|
return;
|
|
@@ -514,11 +515,14 @@ export default function gigaplanExtension(pi: ExtensionAPI) {
|
|
|
514
515
|
}
|
|
515
516
|
|
|
516
517
|
// Ask for configuration
|
|
517
|
-
const
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
518
|
+
const ROBUSTNESS_OPTIONS = ["Light — pragmatic, fast", "Standard — balanced (default)", "Thorough — exhaustive review"];
|
|
519
|
+
const ROBUSTNESS_MAP: Record<string, string> = {
|
|
520
|
+
[ROBUSTNESS_OPTIONS[0]]: "light",
|
|
521
|
+
[ROBUSTNESS_OPTIONS[1]]: "standard",
|
|
522
|
+
[ROBUSTNESS_OPTIONS[2]]: "thorough",
|
|
523
|
+
};
|
|
524
|
+
const robustnessChoice = await ctx.ui.select("Robustness level", ROBUSTNESS_OPTIONS);
|
|
525
|
+
const robustness = ROBUSTNESS_MAP[robustnessChoice ?? ""] ?? "standard";
|
|
522
526
|
|
|
523
527
|
const autoApprove = await ctx.ui.confirm(
|
|
524
528
|
"Auto-approve?",
|
|
@@ -528,14 +532,14 @@ export default function gigaplanExtension(pi: ExtensionAPI) {
|
|
|
528
532
|
// Initialize
|
|
529
533
|
const root = ctx.cwd;
|
|
530
534
|
const { planDir, state } = initPlan(root, idea, {
|
|
531
|
-
robustness
|
|
535
|
+
robustness,
|
|
532
536
|
autoApprove,
|
|
533
537
|
});
|
|
534
538
|
|
|
535
539
|
activePlan = { name: state.name, state: state.current_state, step: "clarify" };
|
|
536
540
|
updateWidget(ctx);
|
|
537
541
|
|
|
538
|
-
ctx.ui.notify(`Plan "${state.name}" initialized. Starting orchestration...`, "
|
|
542
|
+
ctx.ui.notify(`Plan "${state.name}" initialized. Starting orchestration...`, "info");
|
|
539
543
|
|
|
540
544
|
// Send orchestration prompt to the LLM
|
|
541
545
|
const orchestrationPrompt = `You are now in **gigaplan mode**. A structured plan has been initialized.
|
|
@@ -613,7 +617,7 @@ Start now with the **clarify** step.`;
|
|
|
613
617
|
} catch (e) {
|
|
614
618
|
return {
|
|
615
619
|
content: [{ type: "text", text: `Error: ${e instanceof Error ? e.message : String(e)}` }],
|
|
616
|
-
details: { error: true },
|
|
620
|
+
details: { error: true } as any,
|
|
617
621
|
};
|
|
618
622
|
}
|
|
619
623
|
},
|
|
@@ -633,7 +637,7 @@ Start now with the **clarify** step.`;
|
|
|
633
637
|
durationMs: Type.Optional(Type.Number({ description: "How long the step took in ms" })),
|
|
634
638
|
}),
|
|
635
639
|
|
|
636
|
-
async execute(_id, params) {
|
|
640
|
+
async execute(_id, params, _signal, _onUpdate, ctx) {
|
|
637
641
|
try {
|
|
638
642
|
const state = readJson(path.join(params.planDir, "state.json")) as PlanState;
|
|
639
643
|
let result: StepResult;
|
|
@@ -659,7 +663,7 @@ Start now with the **clarify** step.`;
|
|
|
659
663
|
if (activePlan) {
|
|
660
664
|
activePlan.state = result.step;
|
|
661
665
|
activePlan.step = result.nextSteps[0] ?? "done";
|
|
662
|
-
updateWidget(
|
|
666
|
+
updateWidget(ctx);
|
|
663
667
|
}
|
|
664
668
|
|
|
665
669
|
const nextAction = result.nextSteps.length > 0
|
|
@@ -682,7 +686,7 @@ Start now with the **clarify** step.`;
|
|
|
682
686
|
} catch (e) {
|
|
683
687
|
return {
|
|
684
688
|
content: [{ type: "text", text: `Error advancing: ${e instanceof Error ? e.message : String(e)}` }],
|
|
685
|
-
details: { error: true },
|
|
689
|
+
details: { error: true } as any,
|
|
686
690
|
};
|
|
687
691
|
}
|
|
688
692
|
},
|
|
@@ -789,14 +793,14 @@ Start now with the **clarify** step.`;
|
|
|
789
793
|
timestamp: nowUtc(),
|
|
790
794
|
message: `add-note: ${params.note}`,
|
|
791
795
|
});
|
|
792
|
-
|
|
796
|
+
savePlanState(params.planDir, state);
|
|
793
797
|
return { content: [{ type: "text", text: `Note added. Continue with the current step.` }], details: {} };
|
|
794
798
|
}
|
|
795
799
|
|
|
796
800
|
case "abort": {
|
|
797
801
|
state.current_state = STATE_ABORTED;
|
|
798
802
|
state.history.push({ step: "override", timestamp: nowUtc(), message: "aborted" });
|
|
799
|
-
|
|
803
|
+
savePlanState(params.planDir, state);
|
|
800
804
|
return { content: [{ type: "text", text: `Plan "${state.name}" aborted.` }], details: {} };
|
|
801
805
|
}
|
|
802
806
|
|
|
@@ -808,10 +812,10 @@ Start now with the **clarify** step.`;
|
|
|
808
812
|
timestamp: nowUtc(),
|
|
809
813
|
message: "force-proceed (bypassed gate)",
|
|
810
814
|
});
|
|
811
|
-
|
|
815
|
+
savePlanState(params.planDir, state);
|
|
812
816
|
return {
|
|
813
817
|
content: [{ type: "text", text: "Force-proceeded to gate. Next step: execute." }],
|
|
814
|
-
details: { nextSteps: ["execute"] },
|
|
818
|
+
details: { nextSteps: ["execute"] } as any,
|
|
815
819
|
};
|
|
816
820
|
}
|
|
817
821
|
|
|
@@ -823,10 +827,10 @@ Start now with the **clarify** step.`;
|
|
|
823
827
|
timestamp: nowUtc(),
|
|
824
828
|
message: "skip (user override to SKIP)",
|
|
825
829
|
});
|
|
826
|
-
|
|
830
|
+
savePlanState(params.planDir, state);
|
|
827
831
|
return {
|
|
828
832
|
content: [{ type: "text", text: "Skipped to gate. Next step: gate." }],
|
|
829
|
-
details: { nextSteps: ["gate"] },
|
|
833
|
+
details: { nextSteps: ["gate"] } as any,
|
|
830
834
|
};
|
|
831
835
|
}
|
|
832
836
|
|
|
@@ -839,7 +843,7 @@ Start now with the **clarify** step.`;
|
|
|
839
843
|
} catch (e) {
|
|
840
844
|
return {
|
|
841
845
|
content: [{ type: "text", text: `Error: ${e instanceof Error ? e.message : String(e)}` }],
|
|
842
|
-
details: { error: true },
|
|
846
|
+
details: { error: true } as any,
|
|
843
847
|
};
|
|
844
848
|
}
|
|
845
849
|
},
|
package/package.json
CHANGED
package/skills/gigaplan/SKILL.md
CHANGED
|
@@ -40,7 +40,7 @@ For **evaluate** and **gate** steps: skip the subagent, just call `gigaplan_adva
|
|
|
40
40
|
```
|
|
41
41
|
clarify → plan → critique → evaluate
|
|
42
42
|
↓
|
|
43
|
-
CONTINUE → integrate →
|
|
43
|
+
CONTINUE → integrate → critique (loop)
|
|
44
44
|
SKIP → gate → execute → review → done
|
|
45
45
|
ESCALATE → ask user → override
|
|
46
46
|
ABORT → done
|
package/src/evaluation.ts
CHANGED
|
@@ -270,7 +270,7 @@ export function buildEvaluation(planDir: string, state: PlanState): EvaluationRe
|
|
|
270
270
|
const robustness = configuredRobustness(state);
|
|
271
271
|
const skipThreshold = ROBUSTNESS_SKIP_THRESHOLDS[robustness] ?? 2.0;
|
|
272
272
|
const stagnationFactor = ROBUSTNESS_STAGNATION_FACTORS[robustness] ?? 0.9;
|
|
273
|
-
const openScopeCreep = scopeCreepFlags(flagRegistry, FLAG_BLOCKING_STATUSES);
|
|
273
|
+
const openScopeCreep = scopeCreepFlags(flagRegistry, { statuses: FLAG_BLOCKING_STATUSES });
|
|
274
274
|
const significantCount = flagRegistry.flags.filter(
|
|
275
275
|
(f) => f.severity === "significant" && f.status !== "verified",
|
|
276
276
|
).length;
|
package/src/prompts.ts
CHANGED
|
@@ -4,7 +4,8 @@ import * as fs from "fs";
|
|
|
4
4
|
import * as path from "path";
|
|
5
5
|
import {
|
|
6
6
|
PlanState,
|
|
7
|
-
|
|
7
|
+
FlagRecord,
|
|
8
|
+
GigaplanError,
|
|
8
9
|
latestPlanPath,
|
|
9
10
|
readJson,
|
|
10
11
|
latestPlanMetaPath,
|
|
@@ -23,7 +24,7 @@ function clarifyPrompt(state: PlanState, planDir: string): string {
|
|
|
23
24
|
const notes = state.meta?.notes ?? [];
|
|
24
25
|
const notesBlock =
|
|
25
26
|
notes.length > 0
|
|
26
|
-
? notes.map((n:
|
|
27
|
+
? notes.map((n: Record<string, unknown>) => `- ${n.note}`).join("\n")
|
|
27
28
|
: "- None";
|
|
28
29
|
return `
|
|
29
30
|
You are a planning assistant. The user has proposed the following idea:
|
|
@@ -52,7 +53,7 @@ function planPrompt(state: PlanState, planDir: string): string {
|
|
|
52
53
|
const notes = state.meta?.notes ?? [];
|
|
53
54
|
const notesBlock =
|
|
54
55
|
notes.length > 0
|
|
55
|
-
? notes.map((n:
|
|
56
|
+
? notes.map((n: Record<string, unknown>) => `- ${n.note}`).join("\n")
|
|
56
57
|
: "- None";
|
|
57
58
|
const clarification = state.clarification ?? {};
|
|
58
59
|
const refined = clarification.refined_idea ?? "";
|
|
@@ -103,7 +104,7 @@ function integratePrompt(state: PlanState, planDir: string): string {
|
|
|
103
104
|
);
|
|
104
105
|
const evaluation = readJson(evaluatePath);
|
|
105
106
|
const unresolved = unresolvedSignificantFlags(flagRegistry);
|
|
106
|
-
const openFlags = unresolved.map((flag
|
|
107
|
+
const openFlags = unresolved.map((flag) => ({
|
|
107
108
|
id: flag.id,
|
|
108
109
|
severity: flag.severity,
|
|
109
110
|
status: flag.status,
|
|
@@ -151,10 +152,10 @@ function critiquePrompt(state: PlanState, planDir: string): string {
|
|
|
151
152
|
const flagRegistry = loadFlagRegistry(planDir);
|
|
152
153
|
const robustness = configuredRobustness(state);
|
|
153
154
|
const unresolved = flagRegistry.flags
|
|
154
|
-
.filter((flag
|
|
155
|
+
.filter((flag) =>
|
|
155
156
|
["addressed", "open", "disputed"].includes(flag.status as string)
|
|
156
157
|
)
|
|
157
|
-
.map((flag
|
|
158
|
+
.map((flag) => ({
|
|
158
159
|
id: flag.id,
|
|
159
160
|
concern: flag.concern,
|
|
160
161
|
status: flag.status,
|
|
@@ -255,7 +256,7 @@ function reviewClaudePrompt(state: PlanState, planDir: string): string {
|
|
|
255
256
|
const latestMeta = readJson(latestPlanMetaPath(planDir, state));
|
|
256
257
|
const execution = readJson(path.join(planDir, "execution.json"));
|
|
257
258
|
const gate = readJson(path.join(planDir, "gate.json"));
|
|
258
|
-
const diffSummary = collectGitDiffSummary(projectDir);
|
|
259
|
+
const diffSummary = collectGitDiffSummary(projectDir ?? process.cwd());
|
|
259
260
|
return `
|
|
260
261
|
Review the execution critically against user intent and observable success criteria.
|
|
261
262
|
|
|
@@ -294,7 +295,7 @@ function reviewCodexPrompt(state: PlanState, planDir: string): string {
|
|
|
294
295
|
);
|
|
295
296
|
const latestMeta = readJson(latestPlanMetaPath(planDir, state));
|
|
296
297
|
const execution = readJson(path.join(planDir, "execution.json"));
|
|
297
|
-
const diffSummary = collectGitDiffSummary(projectDir);
|
|
298
|
+
const diffSummary = collectGitDiffSummary(projectDir ?? process.cwd());
|
|
298
299
|
return `
|
|
299
300
|
Review the implementation against the success criteria.
|
|
300
301
|
|
|
@@ -358,7 +359,7 @@ export function createPrompt(
|
|
|
358
359
|
agent === "codex" ? CODEX_PROMPT_BUILDERS : CLAUDE_PROMPT_BUILDERS;
|
|
359
360
|
const builder = builders[step];
|
|
360
361
|
if (!builder) {
|
|
361
|
-
throw new
|
|
362
|
+
throw new GigaplanError(
|
|
362
363
|
"unsupported_step",
|
|
363
364
|
`Unsupported ${agent} step '${step}'`
|
|
364
365
|
);
|
package/src/workers.ts
CHANGED
|
@@ -60,6 +60,82 @@ function getRequiredKeys(step: string): string[] {
|
|
|
60
60
|
return (schema?.required as string[] | undefined) ?? [];
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
+
function getStepSchema(step: string): Record<string, unknown> | undefined {
|
|
64
|
+
const filename = STEP_SCHEMA_FILENAMES[step];
|
|
65
|
+
if (!filename) return undefined;
|
|
66
|
+
return SCHEMAS[filename] as Record<string, unknown> | undefined;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function describeType(value: unknown): string {
|
|
70
|
+
if (Array.isArray(value)) return "array";
|
|
71
|
+
if (value === null) return "null";
|
|
72
|
+
return typeof value;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function validateValueAgainstSchema(
|
|
76
|
+
value: unknown,
|
|
77
|
+
schema: Record<string, unknown> | undefined,
|
|
78
|
+
keyPath: string,
|
|
79
|
+
): void {
|
|
80
|
+
if (!schema) return;
|
|
81
|
+
|
|
82
|
+
const expectedType = schema.type as string | undefined;
|
|
83
|
+
if (expectedType === "string" && typeof value !== "string") {
|
|
84
|
+
throw new GigaplanError(
|
|
85
|
+
"parse_error",
|
|
86
|
+
`${keyPath} must be a string, got ${describeType(value)}`,
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
if (expectedType === "boolean" && typeof value !== "boolean") {
|
|
90
|
+
throw new GigaplanError(
|
|
91
|
+
"parse_error",
|
|
92
|
+
`${keyPath} must be a boolean, got ${describeType(value)}`,
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
if (expectedType === "array") {
|
|
96
|
+
if (!Array.isArray(value)) {
|
|
97
|
+
throw new GigaplanError(
|
|
98
|
+
"parse_error",
|
|
99
|
+
`${keyPath} must be an array, got ${describeType(value)}`,
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
const itemSchema = schema.items as Record<string, unknown> | undefined;
|
|
103
|
+
value.forEach((item, index) => {
|
|
104
|
+
validateValueAgainstSchema(item, itemSchema, `${keyPath}[${index}]`);
|
|
105
|
+
});
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
if (expectedType === "object") {
|
|
109
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
110
|
+
throw new GigaplanError(
|
|
111
|
+
"parse_error",
|
|
112
|
+
`${keyPath} must be an object, got ${describeType(value)}`,
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const obj = value as Record<string, unknown>;
|
|
117
|
+
const required = (schema.required as string[] | undefined) ?? [];
|
|
118
|
+
const missing = required.filter((k) => !(k in obj));
|
|
119
|
+
if (missing.length > 0) {
|
|
120
|
+
throw new GigaplanError(
|
|
121
|
+
"parse_error",
|
|
122
|
+
`${keyPath} missing required keys: ${missing.join(", ")}`,
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const properties = (schema.properties as Record<string, unknown> | undefined) ?? {};
|
|
127
|
+
for (const [prop, propSchema] of Object.entries(properties)) {
|
|
128
|
+
if (prop in obj) {
|
|
129
|
+
validateValueAgainstSchema(
|
|
130
|
+
obj[prop],
|
|
131
|
+
propSchema as Record<string, unknown>,
|
|
132
|
+
keyPath === "payload" ? prop : `${keyPath}.${prop}`,
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
63
139
|
// ---------------------------------------------------------------------------
|
|
64
140
|
// Validation
|
|
65
141
|
// ---------------------------------------------------------------------------
|
|
@@ -73,6 +149,8 @@ export function validatePayload(step: string, payload: Record<string, unknown>):
|
|
|
73
149
|
`${step} output missing required keys: ${missing.join(", ")}`,
|
|
74
150
|
);
|
|
75
151
|
}
|
|
152
|
+
|
|
153
|
+
validateValueAgainstSchema(payload, getStepSchema(step), "payload");
|
|
76
154
|
}
|
|
77
155
|
|
|
78
156
|
// ---------------------------------------------------------------------------
|
|
@@ -104,6 +182,44 @@ export function resolveAgent(step: string, state: PlanState): AgentRouting {
|
|
|
104
182
|
// Build subagent task
|
|
105
183
|
// ---------------------------------------------------------------------------
|
|
106
184
|
|
|
185
|
+
function stepOutputChecklist(step: string): string {
|
|
186
|
+
switch (step) {
|
|
187
|
+
case "clarify":
|
|
188
|
+
return [
|
|
189
|
+
'- Top-level keys: `questions`, `refined_idea`, `intent_summary`.',
|
|
190
|
+
'- `questions` must be an array of objects with `question` and `context`.',
|
|
191
|
+
].join("\n");
|
|
192
|
+
case "plan":
|
|
193
|
+
return [
|
|
194
|
+
'- Top-level keys: `plan`, `questions`, `success_criteria`, `assumptions`.',
|
|
195
|
+
'- `plan` must be a markdown string, not an array or object.',
|
|
196
|
+
].join("\n");
|
|
197
|
+
case "integrate":
|
|
198
|
+
return [
|
|
199
|
+
'- Top-level keys: `plan`, `changes_summary`, `flags_addressed`, `assumptions`, `success_criteria`, `questions`.',
|
|
200
|
+
'- `plan` must be a markdown string.',
|
|
201
|
+
'- `flags_addressed` must contain exact flag IDs from critique.',
|
|
202
|
+
].join("\n");
|
|
203
|
+
case "critique":
|
|
204
|
+
return [
|
|
205
|
+
'- Top-level keys: `flags`, `verified_flag_ids`, `disputed_flag_ids`.',
|
|
206
|
+
'- Use `flags`, not `significant_issues` or any alternate field name.',
|
|
207
|
+
'- Each `flags[]` item must include exactly: `id`, `concern`, `category`, `severity_hint`, `evidence`.',
|
|
208
|
+
].join("\n");
|
|
209
|
+
case "execute":
|
|
210
|
+
return [
|
|
211
|
+
'- Top-level keys: `output`, `files_changed`, `commands_run`, `deviations`.',
|
|
212
|
+
].join("\n");
|
|
213
|
+
case "review":
|
|
214
|
+
return [
|
|
215
|
+
'- Top-level keys: `criteria`, `issues`, `summary`.',
|
|
216
|
+
'- Each `criteria[]` item must include `name`, `pass`, `evidence`.',
|
|
217
|
+
].join("\n");
|
|
218
|
+
default:
|
|
219
|
+
return "- Follow the schema exactly.";
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
107
223
|
/**
|
|
108
224
|
* Build the full task prompt for a subagent.
|
|
109
225
|
* Includes the step prompt + instructions to write structured output.
|
|
@@ -131,6 +247,9 @@ ${prompt}
|
|
|
131
247
|
You MUST write your response as a valid JSON object to this file:
|
|
132
248
|
\`${outputPath}\`
|
|
133
249
|
|
|
250
|
+
Checklist:
|
|
251
|
+
${stepOutputChecklist(step)}
|
|
252
|
+
|
|
134
253
|
The JSON must conform to this schema:
|
|
135
254
|
\`\`\`json
|
|
136
255
|
${JSON.stringify(strict, null, 2)}
|