@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 +69 -0
- package/LICENSE +21 -0
- package/README.md +402 -0
- package/package.json +62 -0
- package/src/config.ts +53 -0
- package/src/engine.ts +474 -0
- package/src/events.ts +223 -0
- package/src/formatting.ts +201 -0
- package/src/index.ts +23 -0
- package/src/renderers.ts +28 -0
- package/src/schemas.ts +65 -0
- package/src/state.ts +124 -0
- package/src/tools.ts +611 -0
- package/src/types.ts +137 -0
- package/src/validation.ts +185 -0
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
|
+
}
|