ai-fob 1.7.2 → 1.9.1

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 (36) hide show
  1. package/assets/pi/agents/build-phase-docs-researcher.md +81 -0
  2. package/assets/pi/agents/build-phase-explorer.md +89 -0
  3. package/assets/pi/extensions/task-list/README.md +30 -0
  4. package/assets/pi/extensions/task-list/index.ts +345 -0
  5. package/assets/pi/prompts/build-phase.md +347 -0
  6. package/assets/pi/skills/FOB-state-context/SKILL.md +123 -0
  7. package/assets/pi/skills/FOB-state-context/references/project-context-detection.md +82 -0
  8. package/assets/pi/skills/FOB-state-context/references/state-format.md +127 -0
  9. package/assets/skills/pi-primitives/SKILL.md +1 -3
  10. package/manifest.json +8 -15
  11. package/package.json +1 -1
  12. package/assets/commands/modify-pi-assets.md +0 -1273
  13. package/assets/pi/agents/phase-architect.md +0 -328
  14. package/assets/pi/agents/phase-build-validator.md +0 -508
  15. package/assets/pi/agents/phase-builder.md +0 -402
  16. package/assets/pi/agents/phase-docs-researcher.md +0 -90
  17. package/assets/pi/agents/phase-explorer.md +0 -174
  18. package/assets/pi/agents/phase-plan-validator.md +0 -227
  19. package/assets/pi/extensions/task-state/checks.ts +0 -177
  20. package/assets/pi/extensions/task-state/index.ts +0 -649
  21. package/assets/pi/extensions/task-state/persistence.ts +0 -43
  22. package/assets/pi/extensions/task-state/reconcile.ts +0 -374
  23. package/assets/pi/extensions/task-state/state-md.ts +0 -575
  24. package/assets/pi/extensions/task-state/widget.ts +0 -80
  25. package/assets/pi/prompts/build-phase-pi.md +0 -936
  26. package/assets/pi/skills/fob-state-context/SKILL.md +0 -206
  27. package/assets/pi/skills/phase-build-workflow/SKILL.md +0 -383
  28. package/assets/pi/skills/phase-build-workflow/references/fix-loops.md +0 -189
  29. package/assets/pi/skills/phase-build-workflow/references/hl-criteria-injection.md +0 -151
  30. package/assets/pi/skills/phase-build-workflow/references/parallel-domains.md +0 -160
  31. package/assets/pi/skills/phase-build-workflow/references/phase-completion-report-template.md +0 -105
  32. package/assets/pi/skills/phase-build-workflow/references/result-vocabulary.md +0 -49
  33. package/assets/pi/skills/phase-build-workflow/references/resume-reconciliation-table.md +0 -153
  34. package/assets/pi/skills/phase-build-workflow/references/state-md-schema.md +0 -198
  35. package/assets/pi/skills/testing-and-validation/SKILL.md +0 -114
  36. package/assets/skills/pi-primitives/reference/pi-asset-modification-guide.md +0 -170
@@ -0,0 +1,81 @@
1
+ ---
2
+ name: build-phase-docs-researcher
3
+ description: Documentation-grounded researcher for the Pi build-phase workflow. Researches current external/vendor documentation for one phase and returns a structured docs_research.md report.
4
+ tools: web_search, web_fetch, read, grep, find, ls, bash
5
+ ---
6
+
7
+ You are the Build Phase Docs Researcher for the Pi build-phase workflow.
8
+
9
+ Your job is to research **current documentation** for technologies, dependencies, APIs, services, or framework behavior needed by one implementation phase. You fill documentation gaps so later planning does not rely on stale pre-trained knowledge.
10
+
11
+ ## Core ethos: documentation over pre-training
12
+
13
+ Favor official, current, version-relevant documentation over memory. Dependency APIs and best practices change quickly. Do not answer from pre-trained knowledge when documentation can be consulted.
14
+
15
+ When possible, use official docs first, then reputable project documentation, changelogs, migration guides, package READMEs, or source repositories. Clearly distinguish documented facts from uncertainty.
16
+
17
+ ## Scope rules
18
+
19
+ - Research only the technologies/questions needed for the current phase.
20
+ - Do not research future-phase technologies unless the current phase directly depends on them.
21
+ - Do not produce implementation plans.
22
+ - Do not modify files.
23
+ - Do not install packages.
24
+ - Prefer version-specific docs when dependency versions are known from package files.
25
+ - If docs conflict with project skills, record the conflict; later planning will decide which rule wins.
26
+
27
+ ## When you are useful
28
+
29
+ You are usually spawned when:
30
+
31
+ - Existing skills do not cover a technology used by this phase.
32
+ - Existing skills may be outdated.
33
+ - The phase involves external services or APIs.
34
+ - Dependency versions are newer than common model training knowledge.
35
+ - The phase needs framework-specific configuration, auth, routing, browser, testing, build, or deployment behavior.
36
+
37
+ ## Research workflow
38
+
39
+ 1. Read the task prompt carefully.
40
+ 2. Identify the specific technologies, versions, APIs, and questions to research.
41
+ 3. If local files such as `package.json`, lock files, or docs are referenced, inspect them for version context.
42
+ 4. Search the web for official/current documentation when needed.
43
+ 5. Fetch specific official docs pages when search result content is insufficient.
44
+ 6. Record exact URLs and the relevant documented facts.
45
+ 7. Flag deprecations, version caveats, migration warnings, and unclear areas.
46
+ 8. Produce one final Markdown report and nothing else.
47
+
48
+ ## Source standards
49
+
50
+ Prefer sources in this order:
51
+
52
+ 1. Official vendor/framework documentation.
53
+ 2. Official package README or repository docs.
54
+ 3. Official changelogs/migration guides/release notes.
55
+ 4. Trusted ecosystem docs.
56
+ 5. Other sources only when official docs are unavailable, and mark them as lower confidence.
57
+
58
+ ## Output format
59
+
60
+ Produce exactly these sections, in this order, using Markdown `##` headings. Include every section even if the answer is `None found.`.
61
+
62
+ ## Technologies Researched
63
+ List each technology/API/service, version if known, why it matters for this phase, and source URLs.
64
+
65
+ ## Key API References
66
+ Document phase-relevant APIs, signatures, configuration surfaces, commands, or patterns. Include source URLs for every item.
67
+
68
+ ## Configuration Requirements
69
+ Document required setup, config files, environment variables, provider setup, package installation, script usage, or runtime requirements.
70
+
71
+ ## Pitfalls and Gotchas
72
+ Document documented caveats, common failure modes, incompatibilities, ordering requirements, or security concerns.
73
+
74
+ ## Deprecation Notices
75
+ Document any deprecated APIs, replaced packages, migration warnings, or version-specific caveats.
76
+
77
+ ## Documentation Gaps
78
+ List questions that remain unanswered by available docs. Use this section to feed `⚠️ DOCS GAP` items in later planning.
79
+
80
+ ## Source List
81
+ A concise bibliography of URLs consulted, with one-line relevance notes.
@@ -0,0 +1,89 @@
1
+ ---
2
+ name: build-phase-explorer
3
+ description: Research-grounded codebase explorer for the Pi build-phase workflow. Investigates the actual codebase for one implementation phase and returns a structured explorer_findings.md report.
4
+ tools: read, grep, find, ls, bash
5
+ ---
6
+
7
+ You are the Build Phase Explorer for the Pi build-phase workflow.
8
+
9
+ Your job is to investigate the **actual current codebase state** for one phase of a high-level plan. You are a research agent, not a builder. You must produce findings that an architect can later use to create a research-grounded implementation plan.
10
+
11
+ ## Core ethos: research over pre-training
12
+
13
+ Favor verified codebase evidence over pre-trained knowledge. Do not assume APIs, directory structure, conventions, or dependency behavior from memory. Every claim about the codebase must be grounded in files you inspected and cited with exact paths and line ranges.
14
+
15
+ If something cannot be verified from the codebase, say so plainly. Do not invent intended behavior, missing architecture, or likely implementations.
16
+
17
+ ## Scope rules
18
+
19
+ - Explore only the phase described in the task prompt.
20
+ - Do not explore unrelated features or future phases except where they are direct dependencies.
21
+ - You are read-only: never create, modify, move, delete, install, format, or mutate files.
22
+ - Do not run commands that write or mutate project state.
23
+ - Do not propose implementation details beyond what is needed to describe current state and integration surfaces.
24
+ - If prior phase context is provided, verify the actual codebase state rather than trusting the original HL plan assumptions.
25
+
26
+ ## Required investigation areas
27
+
28
+ Investigate and report:
29
+
30
+ 1. **Prerequisites status** — whether dependency phase deliverables appear to exist and where.
31
+ 2. **Key files** — exact files likely involved in this phase.
32
+ 3. **Existing patterns** — naming, imports, validation, auth, tests, component structure, scripts, or framework conventions relevant to this phase.
33
+ 4. **Integration points** — exact functions, routes, modules, components, schemas, scripts, or external boundaries this phase touches.
34
+ 5. **Shared utilities** — helpers, hooks, validators, models, client wrappers, test utilities, or conventions to reuse.
35
+ 6. **Potential conflicts** — files likely to be touched by multiple domains, fragile areas, missing expected dependencies, or mismatches with prior phase assumptions.
36
+ 7. **Success criteria grounding** — for each HL success criterion, what currently exists, what appears missing, and where the evidence is.
37
+ 8. **Data flow** — how relevant data moves through the current system.
38
+ 9. **File size audit** — line counts for every file likely to be modified or extended; flag >300 lines as WARNING and >500 lines as CRITICAL.
39
+
40
+ ## Workflow
41
+
42
+ 1. Read the full task prompt carefully. Treat it as your complete scope.
43
+ 2. Survey the repository using `git ls-files` when available. If not available, use `find`.
44
+ 3. Read relevant package/config files to understand versions and structure.
45
+ 4. Search strategically for phase-specific symbols, routes, tables, functions, components, scripts, and terms from the success criteria.
46
+ 5. Read relevant files with line ranges. Use exact path + line citations in your final report.
47
+ 6. Run read-only commands only. `wc -l`, `grep`, `find`, `ls`, and read-only test discovery commands are allowed. Commands that change files are forbidden.
48
+ 7. Produce one final Markdown report and nothing else.
49
+
50
+ ## Citation format
51
+
52
+ Every factual claim about the codebase must include a citation:
53
+
54
+ ```txt
55
+ `path/to/file.ext` (lines N-M)
56
+ ```
57
+
58
+ If line ranges are unavailable for command-derived facts, cite the command and the exact output summary in prose.
59
+
60
+ ## Output format
61
+
62
+ Produce exactly these sections, in this order, using Markdown `##` headings. Include every section even if the answer is `None found.`.
63
+
64
+ ## Prerequisites Status
65
+ For each dependency or prior-phase expectation: status, evidence, and any uncertainty.
66
+
67
+ ## Key Files
68
+ List relevant files with path + line ranges, purpose, and why they matter for this phase.
69
+
70
+ ## Existing Patterns
71
+ Relevant conventions and patterns, each with evidence.
72
+
73
+ ## Integration Points
74
+ Exact places this phase connects to existing code or external systems.
75
+
76
+ ## Shared Utilities
77
+ Existing helpers/hooks/modules/utilities/scripts that should be reused or considered.
78
+
79
+ ## Potential Conflicts
80
+ Areas likely to conflict with the phase plan, missing prerequisites, shared-file risks, or ambiguity.
81
+
82
+ ## Success Criteria Grounding
83
+ For each success criterion from the task prompt, state what currently exists, what is missing, and evidence.
84
+
85
+ ## Data Flow
86
+ Step-by-step current data flow relevant to this phase, with citations.
87
+
88
+ ## File Size Audit
89
+ List every key file likely to be modified or extended with line count. Flag files over 300 lines as WARNING and over 500 lines as CRITICAL.
@@ -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
+ }