ai-fob 1.7.2 → 1.9.0
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/assets/pi/extensions/task-list/README.md +30 -0
- package/assets/pi/extensions/task-list/index.ts +345 -0
- package/assets/pi/prompts/build-phase.md +157 -0
- package/assets/pi/skills/FOB-state-context/SKILL.md +123 -0
- package/assets/pi/skills/FOB-state-context/references/project-context-detection.md +82 -0
- package/assets/pi/skills/FOB-state-context/references/state-format.md +127 -0
- package/assets/skills/pi-primitives/SKILL.md +1 -3
- package/manifest.json +7 -16
- package/package.json +1 -1
- package/assets/commands/modify-pi-assets.md +0 -1273
- package/assets/pi/agents/phase-architect.md +0 -328
- package/assets/pi/agents/phase-build-validator.md +0 -508
- package/assets/pi/agents/phase-builder.md +0 -402
- package/assets/pi/agents/phase-docs-researcher.md +0 -90
- package/assets/pi/agents/phase-explorer.md +0 -174
- package/assets/pi/agents/phase-plan-validator.md +0 -227
- package/assets/pi/extensions/task-state/checks.ts +0 -177
- package/assets/pi/extensions/task-state/index.ts +0 -649
- package/assets/pi/extensions/task-state/persistence.ts +0 -43
- package/assets/pi/extensions/task-state/reconcile.ts +0 -374
- package/assets/pi/extensions/task-state/state-md.ts +0 -575
- package/assets/pi/extensions/task-state/widget.ts +0 -80
- package/assets/pi/prompts/build-phase-pi.md +0 -936
- package/assets/pi/skills/fob-state-context/SKILL.md +0 -206
- package/assets/pi/skills/phase-build-workflow/SKILL.md +0 -383
- package/assets/pi/skills/phase-build-workflow/references/fix-loops.md +0 -189
- package/assets/pi/skills/phase-build-workflow/references/hl-criteria-injection.md +0 -151
- package/assets/pi/skills/phase-build-workflow/references/parallel-domains.md +0 -160
- package/assets/pi/skills/phase-build-workflow/references/phase-completion-report-template.md +0 -105
- package/assets/pi/skills/phase-build-workflow/references/result-vocabulary.md +0 -49
- package/assets/pi/skills/phase-build-workflow/references/resume-reconciliation-table.md +0 -153
- package/assets/pi/skills/phase-build-workflow/references/state-md-schema.md +0 -198
- package/assets/pi/skills/testing-and-validation/SKILL.md +0 -114
- package/assets/skills/pi-primitives/reference/pi-asset-modification-guide.md +0 -170
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Task List Extension
|
|
2
|
+
|
|
3
|
+
Project-local Pi extension that gives the agent a persistent visual checklist for multi-step work.
|
|
4
|
+
|
|
5
|
+
## Tools
|
|
6
|
+
|
|
7
|
+
- `task_list_set` — create or replace the task list
|
|
8
|
+
- `task_list_add` — add a task
|
|
9
|
+
- `task_list_update` — update task status/title/note
|
|
10
|
+
- `task_list_complete` — mark a task complete
|
|
11
|
+
- `task_list_show` — show current tasks
|
|
12
|
+
- `task_list_clear` — clear the list
|
|
13
|
+
|
|
14
|
+
## Commands
|
|
15
|
+
|
|
16
|
+
- `/tasks` — show current task list
|
|
17
|
+
- `/tasks-clear` — clear task list
|
|
18
|
+
|
|
19
|
+
## Statuses
|
|
20
|
+
|
|
21
|
+
- `pending`
|
|
22
|
+
- `in_progress`
|
|
23
|
+
- `completed`
|
|
24
|
+
- `blocked`
|
|
25
|
+
- `cancelled`
|
|
26
|
+
|
|
27
|
+
## Notes
|
|
28
|
+
|
|
29
|
+
This extension is local to this repository because it lives under `.pi/extensions/task-list/`.
|
|
30
|
+
Run `/reload` in Pi after changes so Pi discovers/reloads it.
|
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { StringEnum } from "@earendil-works/pi-ai";
|
|
3
|
+
import { Type } from "typebox";
|
|
4
|
+
|
|
5
|
+
type TaskStatus = "pending" | "in_progress" | "completed" | "blocked" | "cancelled";
|
|
6
|
+
|
|
7
|
+
type TaskItem = {
|
|
8
|
+
id: string;
|
|
9
|
+
title: string;
|
|
10
|
+
status: TaskStatus;
|
|
11
|
+
note?: string;
|
|
12
|
+
createdAt: number;
|
|
13
|
+
updatedAt: number;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
type TaskListState = {
|
|
17
|
+
title: string;
|
|
18
|
+
tasks: TaskItem[];
|
|
19
|
+
updatedAt: number;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const CUSTOM_TYPE = "task-list-state";
|
|
23
|
+
const WIDGET_KEY = "task-list";
|
|
24
|
+
|
|
25
|
+
const statusSchema = StringEnum(["pending", "in_progress", "completed", "blocked", "cancelled"] as const);
|
|
26
|
+
|
|
27
|
+
export default function taskListExtension(pi: ExtensionAPI) {
|
|
28
|
+
let state: TaskListState = emptyState();
|
|
29
|
+
|
|
30
|
+
function emptyState(): TaskListState {
|
|
31
|
+
return {
|
|
32
|
+
title: "Task List",
|
|
33
|
+
tasks: [],
|
|
34
|
+
updatedAt: Date.now(),
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function now() {
|
|
39
|
+
return Date.now();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function nextId() {
|
|
43
|
+
return `task_${Math.random().toString(36).slice(2, 8)}`;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function normalizeTaskTitle(title: string) {
|
|
47
|
+
return title.trim().replace(/\s+/g, " ");
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function persist() {
|
|
51
|
+
pi.appendEntry(CUSTOM_TYPE, state);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function restoreFromSession(ctx: ExtensionContext) {
|
|
55
|
+
const entries = ctx.sessionManager.getBranch();
|
|
56
|
+
|
|
57
|
+
for (let i = entries.length - 1; i >= 0; i--) {
|
|
58
|
+
const entry = entries[i] as any;
|
|
59
|
+
if (entry.type !== "custom" || entry.customType !== CUSTOM_TYPE || !entry.data) continue;
|
|
60
|
+
|
|
61
|
+
state = {
|
|
62
|
+
title: typeof entry.data.title === "string" && entry.data.title.trim() ? entry.data.title : "Task List",
|
|
63
|
+
tasks: Array.isArray(entry.data.tasks) ? entry.data.tasks : [],
|
|
64
|
+
updatedAt: typeof entry.data.updatedAt === "number" ? entry.data.updatedAt : now(),
|
|
65
|
+
};
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
state = emptyState();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function formatTask(task: TaskItem) {
|
|
73
|
+
const icon =
|
|
74
|
+
task.status === "completed"
|
|
75
|
+
? "[x]"
|
|
76
|
+
: task.status === "in_progress"
|
|
77
|
+
? "[~]"
|
|
78
|
+
: task.status === "blocked"
|
|
79
|
+
? "[!]"
|
|
80
|
+
: task.status === "cancelled"
|
|
81
|
+
? "[-]"
|
|
82
|
+
: "[ ]";
|
|
83
|
+
|
|
84
|
+
const note = task.note ? ` — ${task.note}` : "";
|
|
85
|
+
return `${icon} ${task.id}: ${task.title}${note}`;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function renderLines() {
|
|
89
|
+
if (state.tasks.length === 0) {
|
|
90
|
+
return ["Task List", "No active tasks."];
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const completed = state.tasks.filter((task) => task.status === "completed").length;
|
|
94
|
+
const total = state.tasks.length;
|
|
95
|
+
|
|
96
|
+
return [`${state.title} — ${completed}/${total} complete`, ...state.tasks.map(formatTask)];
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function updateUi(ctx: ExtensionContext) {
|
|
100
|
+
if (!ctx.hasUI) return;
|
|
101
|
+
|
|
102
|
+
if (state.tasks.length === 0) {
|
|
103
|
+
ctx.ui.setWidget(WIDGET_KEY, undefined);
|
|
104
|
+
} else {
|
|
105
|
+
ctx.ui.setWidget(WIDGET_KEY, renderLines());
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const active = state.tasks.filter((task) => task.status !== "completed" && task.status !== "cancelled").length;
|
|
109
|
+
ctx.ui.setStatus(WIDGET_KEY, active > 0 ? `tasks: ${active} active` : "tasks: clear");
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function saveAndRender(ctx: ExtensionContext) {
|
|
113
|
+
state.updatedAt = now();
|
|
114
|
+
persist();
|
|
115
|
+
updateUi(ctx);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function findTask(id: string) {
|
|
119
|
+
return state.tasks.find((task) => task.id === id);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
pi.on("session_start", async (_event, ctx) => {
|
|
123
|
+
restoreFromSession(ctx);
|
|
124
|
+
updateUi(ctx);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
pi.on("before_agent_start", async (event) => {
|
|
128
|
+
return {
|
|
129
|
+
systemPrompt:
|
|
130
|
+
event.systemPrompt +
|
|
131
|
+
`
|
|
132
|
+
|
|
133
|
+
Task List extension guidance:
|
|
134
|
+
- When the user request involves multiple meaningful tasks, use the task_list tools to create a concise checklist before doing the work.
|
|
135
|
+
- Keep tasks action-oriented and short.
|
|
136
|
+
- Mark one task as in_progress when you begin it.
|
|
137
|
+
- Mark tasks completed as soon as they are actually achieved.
|
|
138
|
+
- Keep the task list current; do not leave stale in_progress tasks.
|
|
139
|
+
- Do not create a task list for trivial one-step requests.`,
|
|
140
|
+
};
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
pi.registerTool({
|
|
144
|
+
name: "task_list_set",
|
|
145
|
+
label: "Set Task List",
|
|
146
|
+
description: "Create or replace the current visual task list.",
|
|
147
|
+
promptSnippet: "Create or replace the visual task checklist for multi-step work.",
|
|
148
|
+
promptGuidelines: [
|
|
149
|
+
"Use task_list_set when a user request has multiple meaningful steps that should be tracked.",
|
|
150
|
+
],
|
|
151
|
+
parameters: Type.Object({
|
|
152
|
+
title: Type.Optional(Type.String({ description: "Optional task list title." })),
|
|
153
|
+
tasks: Type.Array(Type.String({ description: "Short action-oriented task title." })),
|
|
154
|
+
}),
|
|
155
|
+
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
156
|
+
const timestamp = now();
|
|
157
|
+
|
|
158
|
+
state = {
|
|
159
|
+
title: params.title?.trim() || "Task List",
|
|
160
|
+
tasks: params.tasks
|
|
161
|
+
.map(normalizeTaskTitle)
|
|
162
|
+
.filter(Boolean)
|
|
163
|
+
.map((title) => ({
|
|
164
|
+
id: nextId(),
|
|
165
|
+
title,
|
|
166
|
+
status: "pending" as TaskStatus,
|
|
167
|
+
createdAt: timestamp,
|
|
168
|
+
updatedAt: timestamp,
|
|
169
|
+
})),
|
|
170
|
+
updatedAt: timestamp,
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
saveAndRender(ctx);
|
|
174
|
+
|
|
175
|
+
return {
|
|
176
|
+
content: [{ type: "text", text: `Created task list with ${state.tasks.length} task(s).\n\n${renderLines().join("\n")}` }],
|
|
177
|
+
details: state,
|
|
178
|
+
};
|
|
179
|
+
},
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
pi.registerTool({
|
|
183
|
+
name: "task_list_add",
|
|
184
|
+
label: "Add Task",
|
|
185
|
+
description: "Add a task to the current visual task list.",
|
|
186
|
+
promptSnippet: "Add a new item to the visual task checklist.",
|
|
187
|
+
parameters: Type.Object({
|
|
188
|
+
title: Type.String({ description: "Short action-oriented task title." }),
|
|
189
|
+
status: Type.Optional(statusSchema),
|
|
190
|
+
note: Type.Optional(Type.String()),
|
|
191
|
+
}),
|
|
192
|
+
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
193
|
+
const timestamp = now();
|
|
194
|
+
const title = normalizeTaskTitle(params.title);
|
|
195
|
+
|
|
196
|
+
if (!title) {
|
|
197
|
+
return {
|
|
198
|
+
isError: true,
|
|
199
|
+
content: [{ type: "text", text: "Task title cannot be empty." }],
|
|
200
|
+
details: state,
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const task: TaskItem = {
|
|
205
|
+
id: nextId(),
|
|
206
|
+
title,
|
|
207
|
+
status: params.status || "pending",
|
|
208
|
+
note: params.note?.trim() || undefined,
|
|
209
|
+
createdAt: timestamp,
|
|
210
|
+
updatedAt: timestamp,
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
state.tasks.push(task);
|
|
214
|
+
saveAndRender(ctx);
|
|
215
|
+
|
|
216
|
+
return {
|
|
217
|
+
content: [{ type: "text", text: `Added task: ${task.id} — ${task.title}` }],
|
|
218
|
+
details: task,
|
|
219
|
+
};
|
|
220
|
+
},
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
pi.registerTool({
|
|
224
|
+
name: "task_list_update",
|
|
225
|
+
label: "Update Task",
|
|
226
|
+
description: "Update a task status, title, or note in the visual task list.",
|
|
227
|
+
promptSnippet: "Update progress on an existing visual checklist item.",
|
|
228
|
+
promptGuidelines: [
|
|
229
|
+
"Use task_list_update to mark tasks in_progress, completed, blocked, cancelled, or to revise stale task text.",
|
|
230
|
+
],
|
|
231
|
+
parameters: Type.Object({
|
|
232
|
+
id: Type.String({ description: "Task id to update." }),
|
|
233
|
+
status: Type.Optional(statusSchema),
|
|
234
|
+
title: Type.Optional(Type.String()),
|
|
235
|
+
note: Type.Optional(Type.String()),
|
|
236
|
+
}),
|
|
237
|
+
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
238
|
+
const task = findTask(params.id);
|
|
239
|
+
|
|
240
|
+
if (!task) {
|
|
241
|
+
return {
|
|
242
|
+
isError: true,
|
|
243
|
+
content: [{ type: "text", text: `Task not found: ${params.id}` }],
|
|
244
|
+
details: { availableTasks: state.tasks.map(({ id, title, status }) => ({ id, title, status })) },
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
if (params.status) task.status = params.status;
|
|
249
|
+
if (params.title !== undefined) task.title = normalizeTaskTitle(params.title);
|
|
250
|
+
if (params.note !== undefined) task.note = params.note.trim() || undefined;
|
|
251
|
+
|
|
252
|
+
task.updatedAt = now();
|
|
253
|
+
saveAndRender(ctx);
|
|
254
|
+
|
|
255
|
+
return {
|
|
256
|
+
content: [{ type: "text", text: `Updated task: ${task.id} — ${task.status} — ${task.title}` }],
|
|
257
|
+
details: task,
|
|
258
|
+
};
|
|
259
|
+
},
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
pi.registerTool({
|
|
263
|
+
name: "task_list_complete",
|
|
264
|
+
label: "Complete Task",
|
|
265
|
+
description: "Mark a task as completed.",
|
|
266
|
+
promptSnippet: "Check off a completed task in the visual checklist.",
|
|
267
|
+
parameters: Type.Object({
|
|
268
|
+
id: Type.String({ description: "Task id to complete." }),
|
|
269
|
+
note: Type.Optional(Type.String()),
|
|
270
|
+
}),
|
|
271
|
+
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
272
|
+
const task = findTask(params.id);
|
|
273
|
+
|
|
274
|
+
if (!task) {
|
|
275
|
+
return {
|
|
276
|
+
isError: true,
|
|
277
|
+
content: [{ type: "text", text: `Task not found: ${params.id}` }],
|
|
278
|
+
details: state,
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
task.status = "completed";
|
|
283
|
+
task.note = params.note?.trim() || task.note;
|
|
284
|
+
task.updatedAt = now();
|
|
285
|
+
saveAndRender(ctx);
|
|
286
|
+
|
|
287
|
+
return {
|
|
288
|
+
content: [{ type: "text", text: `Completed task: ${task.id} — ${task.title}` }],
|
|
289
|
+
details: task,
|
|
290
|
+
};
|
|
291
|
+
},
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
pi.registerTool({
|
|
295
|
+
name: "task_list_show",
|
|
296
|
+
label: "Show Task List",
|
|
297
|
+
description: "Show the current task list.",
|
|
298
|
+
promptSnippet: "Inspect the current visual task checklist.",
|
|
299
|
+
parameters: Type.Object({}),
|
|
300
|
+
async execute(_toolCallId, _params, _signal, _onUpdate, ctx) {
|
|
301
|
+
updateUi(ctx);
|
|
302
|
+
|
|
303
|
+
return {
|
|
304
|
+
content: [{ type: "text", text: renderLines().join("\n") }],
|
|
305
|
+
details: state,
|
|
306
|
+
};
|
|
307
|
+
},
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
pi.registerTool({
|
|
311
|
+
name: "task_list_clear",
|
|
312
|
+
label: "Clear Task List",
|
|
313
|
+
description: "Clear the current visual task list.",
|
|
314
|
+
promptSnippet: "Clear the visual task checklist when work is done or no longer relevant.",
|
|
315
|
+
parameters: Type.Object({
|
|
316
|
+
reason: Type.Optional(Type.String()),
|
|
317
|
+
}),
|
|
318
|
+
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
319
|
+
state = emptyState();
|
|
320
|
+
saveAndRender(ctx);
|
|
321
|
+
|
|
322
|
+
return {
|
|
323
|
+
content: [{ type: "text", text: params.reason ? `Cleared task list. Reason: ${params.reason}` : "Cleared task list." }],
|
|
324
|
+
details: state,
|
|
325
|
+
};
|
|
326
|
+
},
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
pi.registerCommand("tasks", {
|
|
330
|
+
description: "Show the current task list",
|
|
331
|
+
handler: async (_args, ctx) => {
|
|
332
|
+
updateUi(ctx);
|
|
333
|
+
ctx.ui.notify(renderLines().join("\n"), "info");
|
|
334
|
+
},
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
pi.registerCommand("tasks-clear", {
|
|
338
|
+
description: "Clear the current task list",
|
|
339
|
+
handler: async (_args, ctx) => {
|
|
340
|
+
state = emptyState();
|
|
341
|
+
saveAndRender(ctx);
|
|
342
|
+
ctx.ui.notify("Task list cleared.", "success");
|
|
343
|
+
},
|
|
344
|
+
});
|
|
345
|
+
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Build one phase of a phased high-level plan using the Pi workflow
|
|
3
|
+
argument-hint: "<path to HL plan> <phase number>"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Build Phase Workflow — Step 0 Only
|
|
7
|
+
|
|
8
|
+
You are running the Pi re-engineered build-phase workflow.
|
|
9
|
+
|
|
10
|
+
Current implementation status: **Step 0: Parse & Prepare only**.
|
|
11
|
+
|
|
12
|
+
Do not proceed to research, planning, validation, build, or final reporting yet. Stop after presenting the Step 0 execution overview.
|
|
13
|
+
|
|
14
|
+
## Required skill
|
|
15
|
+
|
|
16
|
+
Load and follow the `FOB-state-context` skill before reading or modifying `specs/STATE.md` or detecting project context.
|
|
17
|
+
|
|
18
|
+
## Arguments
|
|
19
|
+
|
|
20
|
+
Raw arguments: `$ARGUMENTS`
|
|
21
|
+
|
|
22
|
+
Parse into:
|
|
23
|
+
|
|
24
|
+
- `HL_PLAN_PATH`: first argument
|
|
25
|
+
- `PHASE_NUMBER`: second argument
|
|
26
|
+
|
|
27
|
+
If either is missing, stop with:
|
|
28
|
+
|
|
29
|
+
```txt
|
|
30
|
+
Usage: /build-phase <path to HL plan> <phase number>
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Step 0: Parse & Prepare
|
|
34
|
+
|
|
35
|
+
Perform these tasks directly as the main/orchestrator agent.
|
|
36
|
+
|
|
37
|
+
### 0.1 Validate inputs
|
|
38
|
+
|
|
39
|
+
1. Verify `HL_PLAN_PATH` exists.
|
|
40
|
+
2. Verify `HL_PLAN_PATH` is a Markdown file ending in `.md`.
|
|
41
|
+
3. Read the YAML frontmatter.
|
|
42
|
+
4. Verify `PHASE_NUMBER` is an integer.
|
|
43
|
+
5. Verify `PHASE_NUMBER` is within the frontmatter `phases:` count.
|
|
44
|
+
6. If the phase is out of range, stop with:
|
|
45
|
+
|
|
46
|
+
```txt
|
|
47
|
+
Phase {N} is out of range. This plan has {count} phases (1-{count}).
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### 0.2 Derive variables
|
|
51
|
+
|
|
52
|
+
Compute:
|
|
53
|
+
|
|
54
|
+
- `SPEC_DIR`: parent directory of `HL_PLAN_PATH`
|
|
55
|
+
- `SPEC_DIR_BASENAME`: basename of `SPEC_DIR`
|
|
56
|
+
- `TASK_NAME`: frontmatter `task:` value
|
|
57
|
+
- `CATEGORY`: frontmatter `category:` value if present
|
|
58
|
+
- `SPEC_NUMBER`: numeric prefix from `SPEC_DIR_BASENAME` if present
|
|
59
|
+
- `PHASE_NAME`: from `### Phase {N}: {Name}` in the HL plan body
|
|
60
|
+
- `PHASE_NAME_KEBAB`: kebab-case of `PHASE_NAME`
|
|
61
|
+
- `PHASE_DIR`: `{SPEC_DIR}/phase{N}_{PHASE_NAME_KEBAB}/`
|
|
62
|
+
- `REFERENCE_DOCUMENTS`: frontmatter `reference-documents:` list, or empty list
|
|
63
|
+
|
|
64
|
+
### 0.3 Initialize state
|
|
65
|
+
|
|
66
|
+
Using the `FOB-state-context` skill:
|
|
67
|
+
|
|
68
|
+
1. Read `specs/STATE.md` if it exists.
|
|
69
|
+
2. If it does not exist, create a minimal valid state file.
|
|
70
|
+
3. Find or create the current task entry matching `TASK_NAME`.
|
|
71
|
+
4. Find or create the `Phase {PHASE_NUMBER}: {PHASE_NAME}` block under that task.
|
|
72
|
+
5. Ensure Step 1 through Step 6 lines exist under the phase block.
|
|
73
|
+
6. Ensure `pre-phase-sha` and `post-build-sha` lines exist.
|
|
74
|
+
7. Mark the task and phase as in-progress (`[~]`) if they are still pending.
|
|
75
|
+
|
|
76
|
+
For Step 0-only mode, do **not** mark Step 1 started.
|
|
77
|
+
|
|
78
|
+
### 0.4 Git pre-phase checkpoint
|
|
79
|
+
|
|
80
|
+
1. Run `git status --porcelain`.
|
|
81
|
+
2. If git is unavailable, set `GIT_AVAILABLE = false` and warn that checkpoint commits are skipped.
|
|
82
|
+
3. If git is available:
|
|
83
|
+
- Set `GIT_AVAILABLE = true`.
|
|
84
|
+
- If there are uncommitted changes, commit them with:
|
|
85
|
+
|
|
86
|
+
```txt
|
|
87
|
+
checkpoint: pre-phase-{PHASE_NUMBER} ({PHASE_NAME_KEBAB})
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
- Capture `PRE_PHASE_SHA` with `git rev-parse HEAD`.
|
|
91
|
+
- Record it in the phase block as `pre-phase-sha`.
|
|
92
|
+
|
|
93
|
+
### 0.5 Extract phase context
|
|
94
|
+
|
|
95
|
+
From the HL plan, extract and summarize:
|
|
96
|
+
|
|
97
|
+
- Task Overview, usually Section 1
|
|
98
|
+
- User Stories and Anti-Stories, usually Section 3
|
|
99
|
+
- Current Phase section from Section 4
|
|
100
|
+
- High-Level Approach, usually Section 6
|
|
101
|
+
- Key Considerations, usually Section 7
|
|
102
|
+
- Detailed Specifications, usually Section 8 if present
|
|
103
|
+
|
|
104
|
+
Do not create Step 1 research artifacts yet.
|
|
105
|
+
|
|
106
|
+
### 0.6 Read prior phase reports
|
|
107
|
+
|
|
108
|
+
If `PHASE_NUMBER > 1`:
|
|
109
|
+
|
|
110
|
+
1. Look for prior phase reports at `{SPEC_DIR}/phase*/phase_completion_report.md` for phases before the current phase.
|
|
111
|
+
2. Extract:
|
|
112
|
+
- phase number
|
|
113
|
+
- phase name
|
|
114
|
+
- status
|
|
115
|
+
- `## What Was Built`
|
|
116
|
+
- `## Deviations from HL Plan`
|
|
117
|
+
- `## Impacts on Future Phases`
|
|
118
|
+
3. Compile a `PRIOR_PHASE_CONTEXT` summary.
|
|
119
|
+
4. Highlight any direct impacts that mention the current phase.
|
|
120
|
+
|
|
121
|
+
If `PHASE_NUMBER == 1`, set prior phase context to `N/A — this is Phase 1`.
|
|
122
|
+
|
|
123
|
+
### 0.7 Detect project context and skills
|
|
124
|
+
|
|
125
|
+
Using the `FOB-state-context` skill:
|
|
126
|
+
|
|
127
|
+
1. Detect package manager from lock files.
|
|
128
|
+
2. Scan available skill directories for skills matching technologies mentioned in the HL plan and phase section.
|
|
129
|
+
3. Read matched skill files enough to identify skill names and CRITICAL/HIGH rules, if present.
|
|
130
|
+
|
|
131
|
+
### 0.8 Create phase directory
|
|
132
|
+
|
|
133
|
+
Create `PHASE_DIR` idempotently.
|
|
134
|
+
|
|
135
|
+
### 0.9 Present Step 0 execution overview
|
|
136
|
+
|
|
137
|
+
Print:
|
|
138
|
+
|
|
139
|
+
```txt
|
|
140
|
+
BUILD PHASE {N} — {PHASE_NAME}
|
|
141
|
+
|
|
142
|
+
Spec: {SPEC_DIR_BASENAME} ({TASK_NAME})
|
|
143
|
+
Phase Directory: {PHASE_DIR}
|
|
144
|
+
Dependencies: {phase dependencies from HL plan}
|
|
145
|
+
Success Criteria: {count} defined
|
|
146
|
+
Prior Phase Reports: {count read | N/A (Phase 1)}
|
|
147
|
+
Skills Detected: {list or "None"}
|
|
148
|
+
Reference Documents: {count or "None"}
|
|
149
|
+
Resume Status: Step 0 initialized only
|
|
150
|
+
Pre-phase SHA: {PRE_PHASE_SHA | N/A (git unavailable)}
|
|
151
|
+
|
|
152
|
+
Step 0 complete. Workflow intentionally stopped before Step 1 Research.
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Stop condition
|
|
156
|
+
|
|
157
|
+
After the overview, stop. Do not spawn sub-agents. Do not create `explorer_findings.md`, `docs_research.md`, `plan_V1.md`, or any later-step artifacts.
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# FOB State Context
|
|
2
|
+
|
|
3
|
+
Use this skill when a workflow needs to read, scaffold, update, or summarize project state for FOB-style build workflows.
|
|
4
|
+
|
|
5
|
+
This skill is required for the Pi build-phase workflow. It defines how the orchestrator manages `specs/STATE.md`, detects project context, and prepares state so later research/build agents can resume safely.
|
|
6
|
+
|
|
7
|
+
## Core responsibilities
|
|
8
|
+
|
|
9
|
+
1. Read and update `specs/STATE.md` without destroying unrelated task history.
|
|
10
|
+
2. Maintain nested task → phase → step progress markers.
|
|
11
|
+
3. Record git checkpoint SHAs for phase-level resumability.
|
|
12
|
+
4. Detect package manager and basic project context.
|
|
13
|
+
5. Discover relevant technology/convention skills for the current plan.
|
|
14
|
+
|
|
15
|
+
## State marker semantics
|
|
16
|
+
|
|
17
|
+
- `[ ]` means pending / not started.
|
|
18
|
+
- `[~]` means in progress / started but not confirmed complete.
|
|
19
|
+
- `[x]` means completed / output verified.
|
|
20
|
+
|
|
21
|
+
Never mark a step complete unless the expected artifact or action has been verified.
|
|
22
|
+
|
|
23
|
+
## Expected phase step names
|
|
24
|
+
|
|
25
|
+
Every build phase has these six workflow steps:
|
|
26
|
+
|
|
27
|
+
1. `Step 1 - Research`
|
|
28
|
+
2. `Step 2 - Plan`
|
|
29
|
+
3. `Step 3 - Validate Plan`
|
|
30
|
+
4. `Step 4 - Build`
|
|
31
|
+
5. `Step 5 - Validate Build`
|
|
32
|
+
6. `Step 6 - Report`
|
|
33
|
+
|
|
34
|
+
Step 0 is orchestration setup and is not represented as a numbered step inside the phase block.
|
|
35
|
+
|
|
36
|
+
## Required phase metadata lines
|
|
37
|
+
|
|
38
|
+
Every phase block must include:
|
|
39
|
+
|
|
40
|
+
```txt
|
|
41
|
+
pre-phase-sha: (pending)
|
|
42
|
+
post-build-sha: (pending)
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
`pre-phase-sha` is recorded during Step 0. `post-build-sha` is recorded after the build step.
|
|
46
|
+
|
|
47
|
+
## Standard state operations
|
|
48
|
+
|
|
49
|
+
### Initialize or repair state file
|
|
50
|
+
|
|
51
|
+
If `specs/STATE.md` does not exist, create a minimal state file:
|
|
52
|
+
|
|
53
|
+
```markdown
|
|
54
|
+
# Project State
|
|
55
|
+
|
|
56
|
+
## Current Tasks
|
|
57
|
+
|
|
58
|
+
## Completed Tasks
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
If the file exists, preserve its content and only make targeted updates.
|
|
62
|
+
|
|
63
|
+
### Initialize task entry
|
|
64
|
+
|
|
65
|
+
Find the current task line by matching the task name. If it does not exist under `## Current Tasks`, add:
|
|
66
|
+
|
|
67
|
+
```markdown
|
|
68
|
+
- [~] {TASK_NAME}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
If the task line exists with `[ ]`, update it to `[~]` when a phase is initialized.
|
|
72
|
+
|
|
73
|
+
### Initialize phase entry
|
|
74
|
+
|
|
75
|
+
Under the matching task line, ensure this block exists:
|
|
76
|
+
|
|
77
|
+
```markdown
|
|
78
|
+
- [~] Phase {PHASE_NUMBER}: {PHASE_NAME}
|
|
79
|
+
- [ ] Step 1 - Research
|
|
80
|
+
- [ ] Step 2 - Plan
|
|
81
|
+
- [ ] Step 3 - Validate Plan
|
|
82
|
+
- [ ] Step 4 - Build
|
|
83
|
+
- [ ] Step 5 - Validate Build
|
|
84
|
+
- [ ] Step 6 - Report
|
|
85
|
+
pre-phase-sha: (pending)
|
|
86
|
+
post-build-sha: (pending)
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
If the phase block exists, repair missing step or SHA lines rather than duplicating the phase.
|
|
90
|
+
|
|
91
|
+
### Mark step start
|
|
92
|
+
|
|
93
|
+
When a workflow step starts, update its marker from `[ ]` to `[~]`. Also ensure the parent phase and task are marked `[~]` unless already `[x]`.
|
|
94
|
+
|
|
95
|
+
### Mark step complete
|
|
96
|
+
|
|
97
|
+
When a workflow step has been verified, update its marker from `[~]` to `[x]`. If all six steps under a phase are `[x]`, mark the phase `[x]`. If all phases under a task are `[x]`, mark the task `[x]` and move it to `## Completed Tasks` with the current date.
|
|
98
|
+
|
|
99
|
+
### Record SHA
|
|
100
|
+
|
|
101
|
+
To record a git SHA, replace the value on the matching line inside the current phase block:
|
|
102
|
+
|
|
103
|
+
```txt
|
|
104
|
+
pre-phase-sha: {sha}
|
|
105
|
+
post-build-sha: {sha}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Do not update SHA lines in other phases.
|
|
109
|
+
|
|
110
|
+
## Project context detection
|
|
111
|
+
|
|
112
|
+
Read `references/project-context-detection.md` for package manager detection and skill discovery rules.
|
|
113
|
+
|
|
114
|
+
## State format reference
|
|
115
|
+
|
|
116
|
+
Read `references/state-format.md` for examples and exact indentation conventions.
|
|
117
|
+
|
|
118
|
+
## Important constraints
|
|
119
|
+
|
|
120
|
+
- Preserve unrelated tasks, completed task history, comments, and manually added notes whenever possible.
|
|
121
|
+
- Do not invent completed state. Completion requires verified output.
|
|
122
|
+
- Prefer small, targeted edits over rewriting the entire state file when possible.
|
|
123
|
+
- If state and artifacts disagree, future resume logic should trust verified artifacts over stale state markers, but Step 0 only needs to initialize and repair the state skeleton.
|