@umgbhalla/pi-gigaplan 0.1.1 → 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.
@@ -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: any) {
495
+ function updateWidget(ctx?: any) {
496
+ if (!ctx?.ui) return;
496
497
  if (!activePlan) {
497
498
  ctx.ui.setStatus("gigaplan", "");
498
499
  return;
@@ -636,7 +637,7 @@ Start now with the **clarify** step.`;
636
637
  durationMs: Type.Optional(Type.Number({ description: "How long the step took in ms" })),
637
638
  }),
638
639
 
639
- async execute(_id, params) {
640
+ async execute(_id, params, _signal, _onUpdate, ctx) {
640
641
  try {
641
642
  const state = readJson(path.join(params.planDir, "state.json")) as PlanState;
642
643
  let result: StepResult;
@@ -662,7 +663,7 @@ Start now with the **clarify** step.`;
662
663
  if (activePlan) {
663
664
  activePlan.state = result.step;
664
665
  activePlan.step = result.nextSteps[0] ?? "done";
665
- updateWidget(null as any); // TODO: need ctx
666
+ updateWidget(ctx);
666
667
  }
667
668
 
668
669
  const nextAction = result.nextSteps.length > 0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umgbhalla/pi-gigaplan",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Structured AI planning with cross-model critique — gigaplan as a pi extension",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -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 → plan (loop)
43
+ CONTINUE → integrate → critique (loop)
44
44
  SKIP → gate → execute → review → done
45
45
  ESCALATE → ask user → override
46
46
  ABORT → done
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)}