@bumpyclock/pi-tasque 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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Aditya Sharma
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/NOTICE.md ADDED
@@ -0,0 +1,7 @@
1
+ # Notices
2
+
3
+ `@bumpyclock/pi-tasque` is distributed under the MIT License.
4
+
5
+ The session todo layer adapts behavior and structure from the MIT-licensed `@juicesharp/rpiv-todo` package.
6
+
7
+ `pi-tasque` owns the runtime `todo` tool, `/todos` command, and todo overlay. `@juicesharp/rpiv-todo` should not be enabled at the same time; the compatibility is for replay/data behavior, not a runtime dependency.
package/README.md ADDED
@@ -0,0 +1,315 @@
1
+ # pi-tasque
2
+
3
+ Pi extension package for Tasque durable tasks plus branch-replayed in-session todos.
4
+
5
+ ## What this package provides
6
+
7
+ `pi-tasque` gives agents two task layers:
8
+
9
+ - **Session todos**: current-session execution checklist for inspect/edit/verify/handoff steps.
10
+ - **Durable Tasque tasks**: repo-local backlog, specs, dependencies, notes, and ownership through `tsq --format json`.
11
+
12
+ The bridge between the layers is explicit. `pi-tasque` can link, promote, or import tasks, but it does **not** automatically sync lifecycle state between todos and Tasque.
13
+
14
+ ## Install
15
+
16
+ Install with Pi:
17
+
18
+ ```bash
19
+ pi install npm:@bumpyclock/pi-tasque
20
+ ```
21
+
22
+ For local development, install the local package path once:
23
+
24
+ ```bash
25
+ pi install /Users/adityasharma/Projects/pi-tasque
26
+ ```
27
+
28
+ Then use this edit loop:
29
+
30
+ ```text
31
+ edit TypeScript files -> /reload -> test behavior
32
+ ```
33
+
34
+ Pi loads the extension entrypoint from the installed package path, so TypeScript source edits do not require reinstalling the local package. Use `/reload` in the Pi session after changing files under `src/`.
35
+
36
+ ### Remove `@juicesharp/rpiv-todo`
37
+
38
+ Remove or disable `@juicesharp/rpiv-todo` before enabling `pi-tasque`:
39
+
40
+ ```bash
41
+ pi remove npm:@juicesharp/rpiv-todo
42
+ ```
43
+
44
+ `pi-tasque` owns the same user-facing session todo surface:
45
+
46
+ - `todo` tool
47
+ - `/todos` command
48
+ - above-editor todo overlay
49
+
50
+ Running both packages together can register duplicate tools, commands, or widgets.
51
+
52
+ ## Agent guidance
53
+
54
+ Use the smallest task layer that matches the work:
55
+
56
+ - Use `todo` for tactical steps inside the current session: inspect, edit, verify, and handoff.
57
+ - Use `tsq_query` for fresh read-only Tasque state.
58
+ - Use `tsq_change` for explicit durable Tasque mutations, including lifecycle changes and block/order edges.
59
+ - Use `block`/`unblock` for hard dependency edges and `order`/`unorder` for sequencing edges via `tsq_change`.
60
+ - Use `tsq_claim` when taking ownership of a named durable Tasque task.
61
+ - Pass an explicit `assignee` when the agent has a role/name, such as `developer`, `oracle`, or a worker name.
62
+ - If no `assignee` is provided, `tsq_claim` defaults to `pi`.
63
+ - `tsq_claim` defaults `start` to true. Use `requireSpec` when the durable task must have an attached spec before work begins.
64
+ - Use `tsq_claim` with `createTodo: true` when claiming durable work should also create one linked session todo.
65
+ - Use `task_bridge promote_todo` when a session todo should become durable Tasque work.
66
+ - Use `task_bridge import_tsq` when a durable Tasque task should become current-session todo work.
67
+ - Do not create durable Tasque tasks for every session todo.
68
+ - Do not mark a Tasque task done just because linked todos are completed; durable completion requires explicit verification and an explicit Tasque mutation.
69
+
70
+ ## Lifecycle boundaries
71
+
72
+ There is no automatic lifecycle sync between session todos and Tasque:
73
+
74
+ - Completing a `todo` does not mark a Tasque task done.
75
+ - Marking a Tasque task done does not complete linked todos.
76
+ - `task_bridge link` records a relationship only.
77
+ - `task_bridge promote_todo` creates a Tasque task, links it, and completes the source todo as part of the explicit promotion flow.
78
+ - `task_bridge import_tsq` creates or reuses session todos for Tasque tasks, but later status changes remain explicit.
79
+
80
+ ## Example workflow
81
+
82
+ ```json
83
+ {
84
+ "action": "create",
85
+ "subject": "Investigate failing release check",
86
+ "owner": "developer"
87
+ }
88
+ ```
89
+
90
+ ```json
91
+ { "id": "tsq-349aqgsj.12", "assignee": "developer", "createTodo": true }
92
+ ```
93
+
94
+ ```json
95
+ { "action": "list_links" }
96
+ ```
97
+
98
+ ```json
99
+ {
100
+ "action": "done",
101
+ "id": "tsq-349aqgsj.12",
102
+ "note": "Verified docs and package checks."
103
+ }
104
+ ```
105
+
106
+ ## v1 tool and command reference
107
+
108
+ | Surface | Purpose | Notes |
109
+ | -------------------- | --------------------------------------------- | --------------------------------------------------------------- |
110
+ | `todo` | Manage current-session tactical todos. | Session-local; branch-replayed from successful tool results. |
111
+ | `/todos` | Show grouped current-session todos. | Interactive UI only. |
112
+ | `tsq_query` | Read fresh Tasque state. | Read-only; does not mutate Tasque. |
113
+ | `tsq_change` | Run explicit durable Tasque mutations. | Mutations are queued per cwd. |
114
+ | `tsq_claim` | Claim a named durable Tasque task. | `id` required; `assignee` defaults `pi`; `start` defaults true. |
115
+ | `task_bridge` | Link/promote/import between todos and Tasque. | Explicit bridge only; no automatic lifecycle sync. |
116
+ | Todo overlay | Show active session todos above the editor. | Completed todos hide on next turn. |
117
+ | Tasque status footer | Show cached durable-task status. | Display-only; agents should call `tsq_query` for fresh data. |
118
+
119
+ ### `todo`
120
+
121
+ Manage current-session tactical todos. Todos are branch-replayed from previous successful `todo` results in the current session branch.
122
+
123
+ Actions:
124
+
125
+ - `create`: add a todo.
126
+ - `update`: change status, text, owner, metadata, or dependencies.
127
+ - `list`: list visible todos; deleted tombstones are hidden unless `includeDeleted` is true.
128
+ - `get`: fetch one todo by id.
129
+ - `delete`: tombstone one todo.
130
+ - `clear`: clear current todo state.
131
+
132
+ Examples:
133
+
134
+ ```json
135
+ {
136
+ "action": "create",
137
+ "subject": "Run README verification",
138
+ "owner": "developer"
139
+ }
140
+ ```
141
+
142
+ ```json
143
+ {
144
+ "action": "update",
145
+ "id": 1,
146
+ "status": "in_progress",
147
+ "activeForm": "verifying docs"
148
+ }
149
+ ```
150
+
151
+ ### `/todos`
152
+
153
+ Show current-session todos grouped by status in interactive Pi UI. It reports pending, in-progress, and completed groups and requires interactive mode.
154
+
155
+ Example:
156
+
157
+ ```text
158
+ /todos
159
+ ```
160
+
161
+ ### Above-editor todo overlay
162
+
163
+ Shows active session todos above the editor. The overlay rebuilds from branch replay on session lifecycle events and updates after successful `todo`, `task_bridge`, and `tsq_claim` executions that affect todo state.
164
+
165
+ Completed todos remain visible until the next turn, then hide from the overlay.
166
+
167
+ ### `tsq_query`
168
+
169
+ Run read-only Tasque queries through `tsq --format json`. Use this for fresh durable task state without mutating Tasque.
170
+
171
+ Actions:
172
+
173
+ - `doctor`
174
+ - `find_ready`
175
+ - `find_open`
176
+ - `show`
177
+ - `show_with_spec`
178
+ - `deps`
179
+ - `notes`
180
+ - `find_tree`
181
+ - `similar`
182
+
183
+ Examples:
184
+
185
+ ```json
186
+ { "action": "find_ready", "lane": "coding", "assignee": "developer" }
187
+ ```
188
+
189
+ ```json
190
+ { "action": "show_with_spec", "id": "tsq-349aqgsj.12" }
191
+ ```
192
+
193
+ ### `tsq_change`
194
+
195
+ Run approved durable Tasque mutations. Mutations are queued per working directory so concurrent agents do not race `tsq` writes.
196
+
197
+ Actions:
198
+
199
+ - `create`
200
+ - `note`
201
+ - `done`
202
+ - `reopen`
203
+ - `defer`
204
+ - `start`
205
+ - `claim_assign_only`
206
+ - `block`: make `child` blocked by `blocker`.
207
+ - `unblock`: remove a block edge between `child` and `blocker`.
208
+ - `order`: make `later` start after `earlier`.
209
+ - `unorder`: remove an order edge between `later` and `earlier`.
210
+
211
+ Use `block` for hard blockers and `order` for sequencing. Use `tsq_query` with `deps` or `show` to inspect durable graph state. Edge actions cannot create self-edges.
212
+
213
+ Examples:
214
+
215
+ ```json
216
+ {
217
+ "action": "note",
218
+ "id": "tsq-349aqgsj.12",
219
+ "note": "README docs updated; running verification."
220
+ }
221
+ ```
222
+
223
+ ```json
224
+ {
225
+ "action": "done",
226
+ "id": "tsq-349aqgsj.12",
227
+ "note": "Docs and typecheck/test verification passed."
228
+ }
229
+ ```
230
+
231
+ ```json
232
+ { "action": "block", "child": "tsq-abc123.2", "blocker": "tsq-abc123.1" }
233
+ ```
234
+
235
+ ```json
236
+ { "action": "order", "later": "tsq-abc123.3", "earlier": "tsq-abc123.2" }
237
+ ```
238
+
239
+ ### `tsq_claim`
240
+
241
+ Claim a named durable Tasque task. This never auto-selects work; callers must provide the task id. `start` defaults to true, `assignee` defaults to `pi`, and `createTodo` optionally creates one linked session todo for the claimed task.
242
+
243
+ Example:
244
+
245
+ ```json
246
+ {
247
+ "id": "tsq-349aqgsj.12",
248
+ "assignee": "developer",
249
+ "requireSpec": true,
250
+ "createTodo": true
251
+ }
252
+ ```
253
+
254
+ ### `task_bridge`
255
+
256
+ Explicitly connect session todos and durable Tasque tasks. Bridge actions do not create implicit lifecycle sync.
257
+
258
+ Actions:
259
+
260
+ - `link`: associate an existing todo with an existing Tasque task via todo metadata.
261
+ - `list_links`: inspect current session todo ↔ Tasque associations.
262
+ - `promote_todo`: create a Tasque task from an existing todo, add a promotion note, link metadata, and complete the source todo.
263
+ - `import_tsq`: import a Tasque task, or an open-tree task plus children when available, into session todos with `tsqId` metadata links.
264
+
265
+ Link example:
266
+
267
+ ```json
268
+ { "action": "link", "todoId": 3, "tsqId": "tsq-349aqgsj.12" }
269
+ ```
270
+
271
+ Promote example:
272
+
273
+ ```json
274
+ {
275
+ "action": "promote_todo",
276
+ "todoId": 4,
277
+ "kind": "task",
278
+ "priority": 2,
279
+ "assignee": "developer",
280
+ "parent": "tsq-349aqgsj"
281
+ }
282
+ ```
283
+
284
+ Import example:
285
+
286
+ ```json
287
+ { "action": "import_tsq", "tsqId": "tsq-349aqgsj.12", "owner": "developer" }
288
+ ```
289
+
290
+ List links example:
291
+
292
+ ```json
293
+ { "action": "list_links" }
294
+ ```
295
+
296
+ ### Tasque status footer
297
+
298
+ The status footer shows cached Tasque status in interactive UI:
299
+
300
+ - ready coding count
301
+ - ready planning count
302
+ - `mine` count for tasks assigned to `pi`
303
+ - loading, stale, and error states
304
+
305
+ It refreshes on session start, periodically, and after successful durable mutations from `tsq_change`, `tsq_claim`, or `task_bridge`.
306
+
307
+ The footer is display-only. Refreshed status is not injected into the model context; agents should use `tsq_query` for fresh durable task data. If agents claim with a non-`pi` assignee, those tasks may not appear in the footer's `mine` count.
308
+
309
+ ## Verification
310
+
311
+ Run full local verification before handoff:
312
+
313
+ ```bash
314
+ npm run typecheck && npm test
315
+ ```
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@bumpyclock/pi-tasque",
3
+ "version": "0.1.0",
4
+ "description": "Pi extension package for Tasque durable tasks plus in-session todos.",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/BumpyClock/pi-tasque.git"
10
+ },
11
+ "bugs": {
12
+ "url": "https://github.com/BumpyClock/pi-tasque/issues"
13
+ },
14
+ "homepage": "https://github.com/BumpyClock/pi-tasque#readme",
15
+ "keywords": ["pi-package", "pi-extension", "tasque", "tsq", "todo"],
16
+ "scripts": {
17
+ "typecheck": "tsc --noEmit",
18
+ "test": "vitest run"
19
+ },
20
+ "files": ["src/", "README.md", "LICENSE", "NOTICE.md"],
21
+ "publishConfig": {
22
+ "access": "public",
23
+ "provenance": true
24
+ },
25
+ "pi": {
26
+ "extensions": ["./src/index.ts"]
27
+ },
28
+ "peerDependencies": {
29
+ "@earendil-works/pi-ai": "*",
30
+ "@earendil-works/pi-coding-agent": "*",
31
+ "@earendil-works/pi-tui": "*",
32
+ "typebox": "*"
33
+ },
34
+ "devDependencies": {
35
+ "@types/node": "latest",
36
+ "typescript": "latest",
37
+ "vitest": "latest"
38
+ }
39
+ }
@@ -0,0 +1,185 @@
1
+ import {
2
+ defineTool,
3
+ type AgentToolResult,
4
+ type ExtensionAPI,
5
+ type ExtensionContext,
6
+ } from "@earendil-works/pi-coding-agent";
7
+ import {
8
+ errorToolDetails,
9
+ okToolDetails,
10
+ textToolResult,
11
+ } from "../shared/tool-result.js";
12
+ import { deriveTaskLinks, linkTodoToTsq } from "./link-store.js";
13
+ import {
14
+ TASK_BRIDGE_TOOL_NAME,
15
+ TaskBridgeParamsSchema,
16
+ type ImportTsqBridgeParams,
17
+ type LinkBridgeParams,
18
+ type ListLinksBridgeParams,
19
+ type PromoteTodoBridgeParams,
20
+ type TaskBridgeAction,
21
+ type TaskBridgeDetails,
22
+ type TaskBridgeHandlerContext,
23
+ type TaskBridgeHandlers,
24
+ type TaskBridgeLink,
25
+ type TaskBridgeParams,
26
+ } from "./types.js";
27
+
28
+ export function registerTaskBridgeTool(
29
+ pi: ExtensionAPI,
30
+ handlers: TaskBridgeHandlers = {},
31
+ ): void {
32
+ pi.registerTool(
33
+ defineTool({
34
+ name: TASK_BRIDGE_TOOL_NAME,
35
+ label: "Task Bridge",
36
+ description:
37
+ "Explicitly link, list, promote, or import between session todos and durable Tasque tasks. No automatic lifecycle sync.",
38
+ promptSnippet:
39
+ "task_bridge links, lists, promotes, and imports between session todos and durable Tasque tasks; it never auto-completes one layer from the other.",
40
+ promptGuidelines: [
41
+ "Use link to associate an existing todo with an existing Tasque task via todo metadata tsqId.",
42
+ "Use list_links to inspect current session todo ↔ Tasque associations.",
43
+ "Use promote_todo to create a Tasque task from a todo and link the promoted todo explicitly.",
44
+ "Use import_tsq to create or reuse session todos from Tasque task state and link them explicitly.",
45
+ "Todo completion does not mark Tasque done; durable completion stays explicit.",
46
+ ],
47
+ parameters: TaskBridgeParamsSchema,
48
+ executionMode: "sequential",
49
+
50
+ async execute(_toolCallId, params, signal, _onUpdate, ctx) {
51
+ return executeTaskBridge(pi, params, signal, ctx, handlers);
52
+ },
53
+ }),
54
+ );
55
+ }
56
+
57
+ export async function executeTaskBridge(
58
+ pi: ExtensionAPI,
59
+ params: TaskBridgeParams,
60
+ signal: AbortSignal | undefined,
61
+ ctx: ExtensionContext,
62
+ handlers: TaskBridgeHandlers = {},
63
+ ): Promise<AgentToolResult<TaskBridgeDetails>> {
64
+ switch (params.action) {
65
+ case "link":
66
+ return executeLink(params as LinkBridgeParams);
67
+ case "list_links":
68
+ return executeListLinks(params as ListLinksBridgeParams);
69
+ case "promote_todo":
70
+ return executeInjectedHandler(
71
+ "promote_todo",
72
+ handlers.promote_todo,
73
+ params as PromoteTodoBridgeParams,
74
+ buildHandlerContext(pi, ctx, signal),
75
+ );
76
+ case "import_tsq":
77
+ return executeInjectedHandler(
78
+ "import_tsq",
79
+ handlers.import_tsq,
80
+ params as ImportTsqBridgeParams,
81
+ buildHandlerContext(pi, ctx, signal),
82
+ );
83
+ default:
84
+ return errorResult(
85
+ "validation_error",
86
+ "action must be a supported task_bridge action",
87
+ );
88
+ }
89
+ }
90
+
91
+ function executeLink(
92
+ params: LinkBridgeParams,
93
+ ): AgentToolResult<TaskBridgeDetails> {
94
+ if (typeof params.todoId !== "number" || !Number.isInteger(params.todoId)) {
95
+ return errorResult("validation_error", "todoId is required");
96
+ }
97
+ if (typeof params.tsqId !== "string") {
98
+ return errorResult("validation_error", "tsqId is required");
99
+ }
100
+
101
+ const result = linkTodoToTsq(params.todoId, params.tsqId);
102
+ if (!result.ok) {
103
+ return errorResult("validation_error", result.message);
104
+ }
105
+
106
+ return textToolResult(
107
+ `Linked todo #${result.link.todoId} to ${result.link.tsqId}`,
108
+ okToolDetails({
109
+ action: "link",
110
+ link: result.link,
111
+ todo: result.todo,
112
+ }),
113
+ );
114
+ }
115
+
116
+ function executeListLinks(
117
+ _params: ListLinksBridgeParams,
118
+ ): AgentToolResult<TaskBridgeDetails> {
119
+ const links = deriveTaskLinks();
120
+ return textToolResult(
121
+ formatLinks(links),
122
+ okToolDetails({ action: "list_links", links }),
123
+ );
124
+ }
125
+
126
+ async function executeInjectedHandler<TParams extends TaskBridgeParams>(
127
+ action: TaskBridgeAction,
128
+ handler:
129
+ | ((
130
+ params: TParams,
131
+ ctx: TaskBridgeHandlerContext,
132
+ ) =>
133
+ | AgentToolResult<TaskBridgeDetails>
134
+ | Promise<AgentToolResult<TaskBridgeDetails>>)
135
+ | undefined,
136
+ params: TParams,
137
+ ctx: TaskBridgeHandlerContext,
138
+ ): Promise<AgentToolResult<TaskBridgeDetails>> {
139
+ if (handler === undefined) {
140
+ return notImplementedResult(action);
141
+ }
142
+ return handler(params, ctx);
143
+ }
144
+
145
+ function buildHandlerContext(
146
+ pi: ExtensionAPI,
147
+ ctx: ExtensionContext,
148
+ signal: AbortSignal | undefined,
149
+ ): TaskBridgeHandlerContext {
150
+ return {
151
+ pi,
152
+ cwd: ctx.cwd,
153
+ extensionContext: ctx,
154
+ ...(signal === undefined ? {} : { signal }),
155
+ };
156
+ }
157
+
158
+ function notImplementedResult(
159
+ action: "promote_todo" | "import_tsq" | TaskBridgeAction,
160
+ ): AgentToolResult<TaskBridgeDetails> {
161
+ return errorResult(
162
+ "not_implemented",
163
+ `task_bridge action ${action} handler is not configured`,
164
+ );
165
+ }
166
+
167
+ function errorResult(
168
+ code: "validation_error" | "not_implemented",
169
+ message: string,
170
+ ): AgentToolResult<TaskBridgeDetails> {
171
+ return textToolResult(
172
+ `Error: ${message}`,
173
+ errorToolDetails({ code, message }),
174
+ );
175
+ }
176
+
177
+ function formatLinks(links: readonly TaskBridgeLink[]): string {
178
+ if (links.length === 0) return "No linked todos";
179
+ return [
180
+ `${links.length} linked ${links.length === 1 ? "todo" : "todos"}`,
181
+ ...links.map(
182
+ (link) => `#${link.todoId} ${link.todoSubject} ↔ ${link.tsqId}`,
183
+ ),
184
+ ].join("\n");
185
+ }