@harms-haus/pi-tasks 0.1.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/CHANGELOG.md ADDED
@@ -0,0 +1,69 @@
1
+ # Changelog
2
+
3
+ ## [Unreleased]
4
+
5
+ ### Added
6
+
7
+ - New `advance_tasks` tool for advancing tasks through implementing → reviewing → done (extracted from `edit_tasks`)
8
+ - Double-usage warning: alerts when `advance_tasks` is called twice consecutively without intervening tool usage
9
+ - Conditional board rendering: `get_ready_tasks` and `advance_tasks` now show only the active phase
10
+ - Truncated claimed task output: shows 3 lines of prompt with `... (ctrl-o to expand)` hint
11
+ - Emoji status icons: ⚪🔵🟢▶️🔍✅❌ replace ASCII symbols
12
+
13
+ ### Bug Fixes
14
+
15
+ - Fixed double auto-continue firing in headless mode due to missing `clearTimeout` on existing timer
16
+ - Fixed `pendingPhasePrompt` not being cleared in session history, causing stale phase prompts on session restore
17
+
18
+ ### Changed
19
+
20
+ - **`write_tasks` schema restructured**: parameter schema changed from a flat `tasks` array (with per-task `phase` field) to a nested `phases` array where each phase has `title` and `tasks` properties; phase numbers are now auto-assigned from array position instead of specified per-task
21
+ - **`write_tasks` gains `mode` parameter**: `"replace"` clears the board before writing (rejects if any tasks are `implementing`/`reviewing`), `"append"` adds tasks to the existing board
22
+ - **Phase titles**: each phase now carries a short title stored in `PhaseRecord`, persisted in board snapshots, and displayed in the UI and status bar
23
+ - Validation error messages from `write_tasks` now include phase context for easier debugging
24
+ - Task IDs now use `t-{phase}.{index}` format (e.g., `t-1.1`, `t-2.3`) instead of `task-{N}`
25
+ - `edit_tasks` no longer supports `type: "advance"` — use the new `advance_tasks` tool instead
26
+ - Board rendering: phase headers use `─── Phase N ───` format, task lines use `{emoji} {id}: {title}` format
27
+ - `nextTaskId` field removed from `TaskBoardSnapshot` (backward compatible with old snapshots)
28
+ - Consolidated all `cloneBoard()` usage to a single helper, eliminating scattered deep-clone patterns
29
+ - Moved `getStatusCounts` from `engine.ts` to `validation.ts` to colocate with other status-related logic
30
+ - Replaced all magic status-string literals with the `TERMINAL_STATUSES` constant for consistency
31
+ - Renamed `detectPhaseCompletion` → `checkAndSetPhaseCompletion` to better reflect its side-effect nature
32
+
33
+ ### Removed
34
+
35
+ - Removed dead code: `isValidTaskRecord`, `getActivePhase`, `getReadyTasks` (unexported), `resetAutoContinue`, and several internal helpers that were never called externally
36
+ - Removed duplicate and low-value test cases
37
+
38
+ ### Refactored
39
+
40
+ - Extracted all TypeBox schemas from `engine.ts` into dedicated `schemas.ts` module
41
+ - Split monolithic engine.test.ts into 4 focused engine test files (engine-compile.test.ts, engine-edits.test.ts, engine-queries.test.ts, engine-write.test.ts). Added new test files for renderers.ts, index.ts, and config.ts coverage
42
+
43
+ ### CI
44
+
45
+ - Updated CI pipeline to enforce coverage thresholds (statement and branch)
46
+
47
+ ### Stats
48
+
49
+ - **Tests:** 278 (was 289 — net reduction from dead-code removal, offset by new coverage tests)
50
+ - **Statement coverage:** 95.69% (was 92.1%)
51
+ - **Branch coverage:** 93.14%
52
+
53
+ ## 0.1.0 (2025-05-20)
54
+
55
+ ### Added
56
+
57
+ - Initial release of pi-tasks extension
58
+ - 5 tools: `write_tasks`, `edit_tasks`, `compile_tasks`, `clear_tasks`, `get_ready_tasks`
59
+ - Strict task status gating with 7 statuses: `draft`, `configured`, `ready`, `implementing`, `reviewing`, `done`, `abandoned`
60
+ - Phase-based workflow with hard gating between phases
61
+ - Dependency tracking between tasks
62
+ - Auto-continue with circuit breaker (max 20 iterations)
63
+ - 3-second countdown with interrupt capability
64
+ - Session persistence via custom entries (event + snapshot)
65
+ - State reconstruction from snapshots on `session_start` and `session_tree`
66
+ - Hidden context injection via `before_agent_start`
67
+ - Deadlock detection with repair instructions
68
+ - Phase completion prompt template via `.pi/phased-tasks.json`
69
+ - 289 tests with 92% code coverage
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 harms-haus
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,402 @@
1
+ # pi-tasks
2
+
3
+ Phased task workflow extension for [pi coding agent](https://github.com/harms-haus/pi-coding-agent). Provides a structured task board with strict status gating, phase-based execution, dependency tracking, auto-continue, and session persistence.
4
+
5
+ ## Features
6
+
7
+ - **Strict status lifecycle** — tasks progress through an enforced pipeline: `draft` → `configured` → `ready` → `implementing` → `reviewing` → `done`
8
+ - **Phase gating** — tasks are grouped into numbered phases; later phases don't become ready until earlier ones complete
9
+ - **Dependency tracking** — tasks can depend on other tasks within the same phase; cycles and missing references are detected at compile time
10
+ - **Auto-continue** — after each agent turn, the board checks for actionable tasks and automatically prompts the agent to continue (with a configurable circuit breaker)
11
+ - **Session persistence** — board state is persisted as snapshot entries in the session tree, so the board survives restarts and session switches
12
+ - **Atomic batch edits** — all edits in a single `edit_tasks` call are validated before any are applied; a failure rolls back the entire batch
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ # npm
18
+ npm install pi-tasks
19
+
20
+ # pi package manager
21
+ pi install pi-tasks
22
+ ```
23
+
24
+ Or add as a local extension in your project's `.pi/extensions` directory.
25
+
26
+ ### Peer Dependencies
27
+
28
+ ```json
29
+ {
30
+ "@earendil-works/pi-ai": "*",
31
+ "@earendil-works/pi-coding-agent": "*",
32
+ "@earendil-works/pi-tui": "*",
33
+ "typebox": "*"
34
+ }
35
+ ```
36
+
37
+ ## Quick Start
38
+
39
+ The agent uses these tools in sequence. Here's a typical workflow:
40
+
41
+ ```
42
+ 1. write_tasks → define phases (each with a title and tasks) and a mode ('replace' or 'append')
43
+ 2. edit_tasks → set dependencies between tasks (type: "blockers")
44
+ 3. compile_tasks → validate the board and activate phase 1
45
+ 4. get_ready_tasks → claim tasks that are ready to work on
46
+ 5. advance_tasks → advance tasks: implementing → reviewing → done
47
+ (repeat 4–5 until all tasks are done)
48
+ ```
49
+
50
+ ### Example Session
51
+
52
+ ```
53
+ Agent: I'll break this feature into phased tasks.
54
+
55
+ → write_tasks: mode=replace, 3 phases, 6 tasks
56
+
57
+ Task Board:
58
+
59
+ ─── Phase 1: Setup ───
60
+ ⚪ t-1.1: Set up database schema
61
+ ⚪ t-1.2: Create API endpoints
62
+ ⚪ t-1.3: Write unit tests for API
63
+
64
+ ─── Phase 2: Implementation ───
65
+ ⚪ t-2.1: Build frontend components
66
+ ⚪ t-2.2: Integration tests
67
+
68
+ ─── Phase 3: QA ───
69
+ ⚪ t-3.1: End-to-end QA
70
+
71
+ Summary: 6 draft
72
+
73
+ → edit_tasks: t-2.2 blockers → [t-1.2, t-2.1]
74
+
75
+ → compile_tasks
76
+
77
+ Task Board:
78
+
79
+ ─── Phase 1: Setup ───
80
+ 🟢 t-1.1: Set up database schema
81
+ 🟢 t-1.2: Create API endpoints
82
+ 🟢 t-1.3: Write unit tests for API
83
+
84
+ ─── Phase 2: Implementation ───
85
+ 🔵 t-2.1: Build frontend components
86
+ 🔵 t-2.2: Integration tests → depends on t-1.2, t-2.1
87
+
88
+ ─── Phase 3: QA ───
89
+ 🔵 t-3.1: End-to-end QA
90
+
91
+ Summary: 3 ready, 3 configured
92
+
93
+ → get_ready_tasks: count 2
94
+
95
+ Claimed 2 task(s).
96
+
97
+ ▶️ t-1.1: Set up database schema (coder)
98
+ Create the database tables for ...
99
+
100
+ ▶️ t-1.2: Create API endpoints (coder)
101
+ Build REST endpoints for ...
102
+ ... (truncated)
103
+
104
+ Review each claimed task and advance through
105
+ implementing → reviewing → done using advance_tasks.
106
+
107
+ → advance_tasks: t-1.1
108
+ (implementing → reviewing)
109
+
110
+ → advance_tasks: t-1.1
111
+ (reviewing → done)
112
+
113
+ ... Phase 2 unlocks when all Phase 1 tasks are done ...
114
+ ```
115
+
116
+ ## Tool Reference
117
+
118
+ ### `write_tasks`
119
+
120
+ Add tasks to the board grouped by phase. Each task is created in `draft` status.
121
+
122
+ | Parameter | Type | Required | Description |
123
+ | --------- | -------- | -------- | --------------------------------------------------------------------------------------------- |
124
+ | `mode` | `string` | Yes | `"replace"` clears the board before writing; `"append"` adds phases to an existing board |
125
+ | `phases` | `array` | Yes | Array of phase objects (min 1). Phase numbers are auto-assigned from array position (1, 2, …) |
126
+
127
+ Each phase object:
128
+
129
+ | Field | Type | Required | Description |
130
+ | ------- | -------- | -------- | -------------------------------- |
131
+ | `title` | `string` | Yes | Short phase title (e.g. "Setup") |
132
+ | `tasks` | `array` | Yes | Tasks in this phase (at least 1) |
133
+
134
+ Each task object:
135
+
136
+ | Field | Type | Required | Description |
137
+ | --------- | -------- | -------- | ------------------------------------ |
138
+ | `title` | `string` | Yes | Short task title |
139
+ | `prompt` | `string` | Yes | Detailed implementation instructions |
140
+ | `profile` | `string` | Yes | Agent profile name for delegation |
141
+
142
+ **Constraints:**
143
+
144
+ - Maximum 100 tasks per board (`MAX_TASKS`)
145
+ - Phase title, task title, prompt, and profile must be non-empty strings
146
+ - Each phase must contain at least 1 task
147
+ - `mode: "replace"` cannot be used while tasks are `implementing` or `reviewing`
148
+ - `mode: "append"` auto-computes the starting phase number as `max(existing phases) + 1`
149
+
150
+ ### `edit_tasks`
151
+
152
+ Batch-edit tasks on the board. Supports three edit types. Edits are atomic — if any validation fails, none are applied.
153
+
154
+ | Parameter | Type | Required | Description |
155
+ | --------- | ------- | -------- | ------------------------------------------------- |
156
+ | `tasks` | `array` | Yes | Array of edit objects (mixed types, max 50 items) |
157
+
158
+ **Type: `data`** — modify task fields
159
+
160
+ | Field | Type | Description |
161
+ | -------------- | --------- | -------------------------------- |
162
+ | `id` | `string` | Task ID |
163
+ | `type` | `"data"` | Edit type |
164
+ | `data.title` | `string` | Optional. New title |
165
+ | `data.prompt` | `string` | Optional. New prompt |
166
+ | `data.profile` | `string` | Optional. New profile |
167
+ | `data.phase` | `integer` | Optional. New phase number (≥ 1) |
168
+
169
+ Structural edits (data/blockers) cannot be applied while any task is `implementing` or `reviewing`. Applying a structural edit resets all non-terminal, non-active tasks back to `draft`, requiring a recompile.
170
+
171
+ **Type: `blockers`** — set task dependencies
172
+
173
+ | Field | Type | Description |
174
+ | ------------------- | ------------ | -------------------------------------- |
175
+ | `id` | `string` | Task ID |
176
+ | `type` | `"blockers"` | Edit type |
177
+ | `data.dependencies` | `string[]` | Array of task IDs this task depends on |
178
+
179
+ Validates against self-dependencies, duplicate entries, and references to non-existent tasks.
180
+
181
+ **Type: `abandon`** — mark task as abandoned
182
+
183
+ | Field | Type | Description |
184
+ | ------ | ----------- | ----------- |
185
+ | `id` | `string` | Task ID |
186
+ | `type` | `"abandon"` | Edit type |
187
+
188
+ Cannot abandon tasks already in `done` or `abandoned` status.
189
+
190
+ ### `compile_tasks`
191
+
192
+ Validate the board and activate the first phase. Checks for:
193
+
194
+ - Duplicate task IDs
195
+ - Missing dependency references
196
+ - Dependency cycles (detected via DFS)
197
+
198
+ On success, all `draft` tasks move to `configured`. Tasks in the active phase with satisfied dependencies move to `ready`.
199
+
200
+ Cannot compile while any task is `implementing` or `reviewing`.
201
+
202
+ ### `get_ready_tasks`
203
+
204
+ Claim ready tasks for implementation. Moves claimed tasks to `implementing` status.
205
+
206
+ | Parameter | Type | Required | Description |
207
+ | --------- | --------- | -------- | ------------------------------ |
208
+ | `count` | `integer` | Yes | Number of tasks to claim (≥ 1) |
209
+
210
+ Tasks are ordered by phase ascending, then by creation order. Cannot claim while any task is `implementing` or `reviewing`.
211
+
212
+ Claimed task output shows the first 3 lines of each task's prompt, followed by `... (truncated)` if the prompt is longer. The board display after claiming shows only the active phase.
213
+
214
+ Error messages distinguish between:
215
+
216
+ - **All tasks resolved** — board is complete
217
+ - **Active tasks exist** — advance or complete them first
218
+ - **Deadlock** — tasks remain but none are actionable; suggests resolving blockers
219
+
220
+ ### `advance_tasks`
221
+
222
+ Advance tasks through their lifecycle: `implementing` → `reviewing` → `done`. Each call advances each task by one step.
223
+
224
+ | Parameter | Type | Required | Description |
225
+ | --------- | ---------- | -------- | ------------------------------------------- |
226
+ | `ids` | `string[]` | Yes | Array of task IDs to advance (max 50 items) |
227
+
228
+ Tasks must be in `implementing` or `reviewing` status. Duplicate IDs in the array are deduplicated.
229
+
230
+ The board display after advancing shows only the active phase (or the full board if all tasks are terminal).
231
+
232
+ **Double-advance warning:** If `advance_tasks` is called twice in a row without any other tool usage in between, a warning is injected reminding the agent to actually review the work before advancing to `done`:
233
+
234
+ > ⚠️ Review should not be skipped. Please actually review the work before advancing to done.
235
+
236
+ ### `clear_tasks`
237
+
238
+ Clear the entire board. Removes all tasks, phases, and resets state. Takes no parameters.
239
+
240
+ Cannot clear the board while any task is `implementing` or `reviewing`. Complete or advance active tasks first.
241
+
242
+ ## Task Lifecycle
243
+
244
+ ```
245
+ draft ──→ configured ──→ ready ──→ implementing ──→ reviewing ──→ done
246
+ │ ↑
247
+ └─── (any non-terminal) ──→ abandoned
248
+ ```
249
+
250
+ | Status | Icon | Description |
251
+ | -------------- | ---- | ------------------------------------------------------------------- |
252
+ | `draft` | `⚪` | Initial state after `write_tasks` |
253
+ | `configured` | `🔵` | Validated by `compile_tasks`; awaiting readiness |
254
+ | `ready` | `🟢` | Phase is active and all dependencies are done; available to claim |
255
+ | `implementing` | `▶️` | Claimed via `get_ready_tasks`; actively being worked on |
256
+ | `reviewing` | `🔍` | Advanced from `implementing`; awaiting final review |
257
+ | `done` | `✅` | Advanced from `reviewing`; terminal state |
258
+ | `abandoned` | `❌` | Explicitly skipped via `edit_tasks` (type: abandon); terminal state |
259
+
260
+ Terminal statuses (`done`, `abandoned`) are permanent — tasks in these states cannot be edited or advanced.
261
+
262
+ ## Phases
263
+
264
+ Tasks are grouped into numbered phases (≥ 1). Phases execute sequentially:
265
+
266
+ 1. **`compile_tasks`** determines the active phase (the lowest-numbered phase with non-terminal tasks).
267
+ 2. Only tasks in the active phase can transition to `ready` (after their dependencies are satisfied).
268
+ 3. When all tasks in a phase reach a terminal status, the phase is marked `completed` and the next phase becomes `active`.
269
+ 4. Pending phases remain locked until all preceding phases complete.
270
+
271
+ Phase status tracking:
272
+
273
+ | Phase Status | Meaning |
274
+ | ------------ | ---------------------------------------------- |
275
+ | `pending` | Not yet reached; tasks are locked |
276
+ | `active` | Current phase; tasks can become ready |
277
+ | `completed` | All tasks are terminal (`done` or `abandoned`) |
278
+
279
+ When a phase completes and actionable tasks remain, a visible "Phase N complete." notification is sent to the user. If a [prompt template](#configuration) is configured, the template message is also injected as hidden context for the next agent turn. When the final phase completes (all tasks terminal), the board summary in the tool output reflects completion without a separate notification. Phase completion can trigger auto-continue.
280
+
281
+ ## Dependencies
282
+
283
+ Each task can declare dependencies on other tasks. Dependencies are validated during `compile_tasks` and `edit_tasks` (type: `blockers`):
284
+
285
+ - **No self-references** — a task cannot depend on itself
286
+ - **No duplicates** — each dependency ID must be unique within a task
287
+ - **No missing references** — all dependency IDs must exist on the board
288
+ - **No cycles** — the dependency graph must be a DAG (validated via DFS cycle detection)
289
+
290
+ A task becomes `ready` only when:
291
+
292
+ 1. Its phase is active
293
+ 2. All of its dependencies have status `done`
294
+
295
+ Tasks with unsatisfied dependencies remain in `configured` even when their phase is active.
296
+
297
+ ## Configuration
298
+
299
+ Create `.pi/phased-tasks.json` in your project root:
300
+
301
+ ```json
302
+ {
303
+ "phaseCompletionPromptTemplate": "Phase {phase} is complete. Review the results and proceed to the next phase."
304
+ }
305
+ ```
306
+
307
+ | Field | Type | Description |
308
+ | ------------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------- |
309
+ | `phaseCompletionPromptTemplate` | `string` | Optional. Template for the prompt injected when a phase completes. Use `{phase}` as a placeholder for the phase number. |
310
+
311
+ The template is resolved by replacing `{phase}` with the completed phase number. If no template is configured, phase completion is detected but no additional prompt is injected.
312
+
313
+ Configuration is loaded once per session and cached. It resets on `session_start`, `session_tree`, and `session_shutdown` events (i.e., whenever the session initializes or the branch changes).
314
+
315
+ ## Auto-Continue
316
+
317
+ After each agent turn (`agent_end` event), the extension checks the board state:
318
+
319
+ 1. **No tasks on the board** → do nothing.
320
+ 2. **Agent turn was aborted** → do nothing (user interrupted).
321
+ 3. **Actionable tasks exist** (ready, implementing, or reviewing) → schedule an auto-continue prompt after a 3-second countdown.
322
+ 4. **Deadlock** (non-terminal tasks but none actionable) → schedule an auto-continue with a deadlock diagnostic.
323
+ 5. **All tasks terminal** → do nothing.
324
+
325
+ The auto-continue uses a 3-second countdown timer. In UI mode, a countdown widget is displayed ("⏳ Auto-continuing in Xs..."). Typing anything cancels the countdown. In headless mode, a simple 3-second timeout is used.
326
+
327
+ ### Circuit Breaker
328
+
329
+ Auto-continue stops after **20 iterations** (`MAX_AUTO_CONTINUE`). When the limit is reached, a visible notice is sent to the user:
330
+
331
+ > Auto-continue limit reached (20 iterations). N task(s) remain unresolved. Take over manually.
332
+
333
+ The counter resets whenever the board is mutated via `setBoard()`.
334
+
335
+ ### Hidden Context
336
+
337
+ Before each agent turn (`before_agent_start`), the extension injects a hidden context message summarizing the board state: active phase, status counts, claimed tasks, remaining tasks, and recently completed tasks. This keeps the agent informed without consuming visible context.
338
+
339
+ ## Event Persistence
340
+
341
+ The extension persists two types of custom entries to the session tree:
342
+
343
+ | Custom Type | Purpose |
344
+ | ----------------------- | --------------------------------------------------------------- |
345
+ | `phased-tasks:event` | Individual workflow events (write, edit, compile, claim, clear) |
346
+ | `phased-tasks:snapshot` | Full board snapshot after each mutation |
347
+
348
+ On `session_start` and `session_tree` events, the board is reconstructed by scanning the session branch in reverse for the latest valid snapshot.
349
+
350
+ On `session_shutdown`, all in-memory state is reset (board, auto-continue counter, advance-tracking flag) and any active countdown timers are cleared.
351
+
352
+ ## Status Bar Integration
353
+
354
+ pi-tasks integrates with [pi-powerline](https://github.com/harms-haus/pi-powerline) to display task progress in the status bar above the composer. No additional configuration is needed — the integration is automatic when both extensions are active.
355
+
356
+ ### What you'll see
357
+
358
+ The status bar shows two elements, updated after every board mutation:
359
+
360
+ - **Progress line** — `{done}/{total} - Phase {n}`, where `done` counts both completed and abandoned tasks. Displays `No active phase` when tasks exist but no phase is active. When the board is empty, both status slots are removed entirely.
361
+ - **Active tasks** — tasks in `implementing` or `reviewing` status are listed above the progress line, one per line: `[t-1.1] Set up database schema`
362
+
363
+ Example status bar with active tasks:
364
+
365
+ ```
366
+ [t-1.1] Set up database schema
367
+ [t-1.2] Create API endpoints
368
+ 2/6 - Phase 1
369
+ ```
370
+
371
+ When the board is cleared, both status slots are removed from the bar.
372
+
373
+ ### Coexistence with pi-til-done
374
+
375
+ pi-tasks publishes to the same `til-done` / `til-done-active` status slots used by [pi-til-done](https://github.com/harms-haus/pi-til-done). When both extensions are active, whichever extension last updates the slot wins the display — there is no merging or deduplication.
376
+
377
+ ## Architecture
378
+
379
+ ```
380
+ src/
381
+ ├── index.ts # Extension entry point; registers tools, event handlers, and renderers
382
+ ├── types.ts # Type definitions, status constants, event types, edit types
383
+ ├── engine.ts # Pure functions: board creation, write/compile/edit/claim logic, phase computation
384
+ ├── state.ts # Mutable board state, session reconstruction, persistence helpers, UI sync
385
+ ├── schemas.ts # TypeBox schemas for tool parameters (extracted from tools.ts)
386
+ ├── tools.ts # Tool definitions: execute, renderCall, renderResult
387
+ ├── events.ts # Event handlers: session_start, session_tree, session_shutdown, before_agent_start, agent_end, input, tool_result
388
+ ├── config.ts # Configuration loading from .pi/phased-tasks.json
389
+ ├── formatting.ts # Plain-text formatting for board display, summaries, and prompts
390
+ ├── validation.ts # Input validation, dependency cycle detection, snapshot type guards
391
+ └── renderers.ts # Message renderers for phased-tasks-context and phased-tasks-notice
392
+ ```
393
+
394
+ Key design decisions:
395
+
396
+ - **Engine is pure** — all board transformation functions are side-effect-free and return new snapshots. Mutability is confined to `state.ts`.
397
+ - **Validation before mutation** — `applyEdits` and `compileBoard` validate all changes before modifying any state, ensuring atomic batch operations.
398
+ - **Phase recomputation is recursive** — advancing the last task in a phase to `done` triggers recursive recomputation, automatically unlocking the next phase and cascading readiness.
399
+
400
+ ## License
401
+
402
+ MIT
package/package.json ADDED
@@ -0,0 +1,62 @@
1
+ {
2
+ "name": "@harms-haus/pi-tasks",
3
+ "version": "0.1.0",
4
+ "description": "pi-coding-agent extension: phased task workflow with dependency tracking and strict status gating",
5
+ "keywords": [
6
+ "pi-package"
7
+ ],
8
+ "type": "module",
9
+ "main": "src/index.ts",
10
+ "pi": {
11
+ "extensions": [
12
+ "./src/index.ts"
13
+ ]
14
+ },
15
+ "publishConfig": {
16
+ "access": "public"
17
+ },
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/harms-haus/pi-tasks.git"
21
+ },
22
+ "files": [
23
+ "src/**/*.ts",
24
+ "!src/__tests__/**",
25
+ "docs/",
26
+ "README.md",
27
+ "CHANGELOG.md",
28
+ "LICENSE"
29
+ ],
30
+ "scripts": {
31
+ "lint": "eslint src/",
32
+ "test": "vitest run",
33
+ "test:watch": "vitest",
34
+ "test:coverage": "vitest run --coverage",
35
+ "format": "prettier --write src/",
36
+ "format:check": "prettier --check src/",
37
+ "typecheck": "tsc --noEmit",
38
+ "lint:fix": "eslint --fix src/"
39
+ },
40
+ "peerDependencies": {
41
+ "@earendil-works/pi-ai": "*",
42
+ "@earendil-works/pi-coding-agent": "*",
43
+ "@earendil-works/pi-tui": "*",
44
+ "typebox": "*"
45
+ },
46
+ "devDependencies": {
47
+ "@eslint/js": "^10.0.1",
48
+ "@types/node": "^22.0.0",
49
+ "@vitest/coverage-v8": "^4.1.6",
50
+ "eslint": "^10.4.0",
51
+ "eslint-config-prettier": "^10.1.8",
52
+ "prettier": "^3.8.3",
53
+ "typescript": "^6.0.3",
54
+ "typescript-eslint": "^8.59.3",
55
+ "vitest": "^4.1.6"
56
+ },
57
+ "author": "harms-haus",
58
+ "engines": {
59
+ "node": ">=22.0.0"
60
+ },
61
+ "license": "MIT"
62
+ }
package/src/config.ts ADDED
@@ -0,0 +1,53 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+
4
+ export interface PhasedTasksConfig {
5
+ phaseCompletionPromptTemplate?: string;
6
+ }
7
+
8
+ const CONFIG_PATH = ".pi/phased-tasks.json";
9
+
10
+ /** Cached config, loaded once per session. */
11
+ let cachedConfig: PhasedTasksConfig | null = null;
12
+
13
+ /** Load config from the project-local JSON file. Returns empty config on missing/invalid file. */
14
+ export async function loadConfig(): Promise<PhasedTasksConfig> {
15
+ if (cachedConfig !== null) return cachedConfig;
16
+ try {
17
+ const raw = await readFile(join(process.cwd(), CONFIG_PATH), "utf-8");
18
+ const parsed: unknown = JSON.parse(raw);
19
+ cachedConfig =
20
+ typeof parsed === "object" &&
21
+ parsed !== null &&
22
+ "phaseCompletionPromptTemplate" in parsed &&
23
+ typeof (parsed as Record<string, unknown>).phaseCompletionPromptTemplate === "string"
24
+ ? {
25
+ phaseCompletionPromptTemplate: (parsed as Record<string, unknown>)
26
+ .phaseCompletionPromptTemplate as string,
27
+ }
28
+ : {};
29
+ } catch (err) {
30
+ if (err instanceof Error && "code" in err && (err as NodeJS.ErrnoException).code !== "ENOENT") {
31
+ console.warn(
32
+ "[pi-tasks] Failed to load config:",
33
+ err instanceof Error ? err.message : String(err),
34
+ );
35
+ }
36
+ cachedConfig = {};
37
+ }
38
+ return cachedConfig;
39
+ }
40
+
41
+ /** Resolve the phase completion prompt template for a given phase number. Returns undefined if no template configured. */
42
+ export function resolvePhasePrompt(
43
+ template: string | undefined,
44
+ phase: number,
45
+ ): string | undefined {
46
+ if (!template) return undefined;
47
+ return template.replace(/\{phase\}/g, String(phase));
48
+ }
49
+
50
+ /** Reset cached config. For testing only. */
51
+ export function resetConfig(): void {
52
+ cachedConfig = null;
53
+ }