@oisin-ee/pi-todo 0.9.3 → 0.10.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/README.md CHANGED
@@ -25,6 +25,8 @@ The model calls `todo` with one `op`. Tasks are addressed by their exact content
25
25
  Creation and append leave tasks pending. Nothing starts automatically. This replaces the previous single-active, automatic-start behavior.
26
26
  Use `start` to activate or reopen work explicitly. Starting a child also opens its parent. Starting a parent does not start its children.
27
27
 
28
+ Keep `content` a short headline: aim under 60 characters, imperative, no trailing detail. Put context, approach, or acceptance detail in the optional `description` (one or two sentences). Outcomes and subtask drafts accept `description`; a plain string item has none. A blank description counts as none. `update` with `description` sets it on an open task or subtask, and a blank value clears it. It never changes `content` or how a task is selected. `view` prints a description on the line under its headline; the Pi widget shows headlines only.
29
+
28
30
  `items` accepts strings or outcomes with string subtasks:
29
31
 
30
32
  ```json
@@ -89,8 +91,10 @@ Alt+J/K scrolls the hosted rows. Alt+I toggles their expansion. Ordinary editor
89
91
  `/todos` and Alt+T request the shared Todos view. `/todos list` still prints the full list.
90
92
  When shared presentation ends, the standalone widget returns without changing task state.
91
93
 
92
- The [version 2 display contract](https://github.com/oisin-ee/dogsled/blob/main/docs/pi-work.md)
94
+ The [version 3 display contract](https://github.com/oisin-ee/dogsled/blob/main/docs/pi-work.md)
93
95
  defines the snapshot request, acknowledgement, release, and read-only view events.
96
+ Version 3 adds `display.sections` to the snapshot: four typed status groups (`in_progress`, `pending`, `completed`, `abandoned`), always present and in that order, each with a count and its items. Each item carries its text, optional description, review flag, contract summary, and all subtasks (each with its own optional description). The core owns this grouping; a presenter only draws it.
97
+ Pi-todo does not answer version 2 requests.
94
98
  Incompatible versions leave the standalone widget available. Rendering failures restore it without changing saved tasks.
95
99
 
96
100
  ## Plannotator plans
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oisin-ee/pi-todo",
3
- "version": "0.9.3",
3
+ "version": "0.10.0",
4
4
  "description": "A todo tool for pi with a Rust core: one active task, state on the session branch.",
5
5
  "keywords": [
6
6
  "pi-extension",
package/src/display.ts CHANGED
@@ -1,7 +1,7 @@
1
- import type { Subtask, TodoList } from "./wasm.ts";
1
+ import type { Subtask, TodoList, TodoSection } from "./wasm.ts";
2
+ import { sections, view } from "./wasm.ts";
2
3
  import type { Theme } from "@earendil-works/pi-coding-agent";
3
4
  import { TodoView } from "./embedded.ts";
4
- import { view } from "./wasm.ts";
5
5
 
6
6
  /** Host-owned layout and rendering services for an inline todo view. */
7
7
  interface ViewHost {
@@ -25,6 +25,8 @@ interface TodoDisplay {
25
25
  readonly summary: string;
26
26
  readonly current: string;
27
27
  readonly text: string;
28
+ /** Status groups owned by the core: in_progress, pending, completed, abandoned. */
29
+ readonly sections: readonly TodoSection[];
28
30
  readonly state: unknown;
29
31
  readonly createView: (host: ViewHost) => HostedView;
30
32
  }
@@ -58,6 +60,7 @@ const emptyCount = 0,
58
60
  createView: (host) => new TodoView(list, host),
59
61
  current,
60
62
  empty: list.tasks.length === emptyCount,
63
+ sections: sections(list),
61
64
  state: list,
62
65
  summary: text.split("\n").at(headerIndex) ?? "",
63
66
  text,
@@ -5,6 +5,7 @@
5
5
  */
6
6
  export interface DraftItem {
7
7
  content: string;
8
+ description?: string;
8
9
  key?: string | undefined;
9
10
  contract?: ContractDraft;
10
11
  }
@@ -14,6 +15,10 @@ export interface DraftItem {
14
15
  */
15
16
  export interface Subtask {
16
17
  content: string;
18
+ /**
19
+ * Detail behind the short `content` headline.
20
+ */
21
+ description?: string;
17
22
  status: Status;
18
23
  unit?: Unit;
19
24
  }
@@ -31,6 +36,10 @@ export interface Change {
31
36
  */
32
37
  export interface Task {
33
38
  content: string;
39
+ /**
40
+ * Detail behind the short `content` headline.
41
+ */
42
+ description?: string;
34
43
  status: Status;
35
44
  subtasks?: Subtask[];
36
45
  unit?: Unit;
@@ -199,6 +208,10 @@ export interface Request {
199
208
  contract?: ContractDraft | undefined;
200
209
  start?: string | undefined;
201
210
  evidence?: string | undefined;
211
+ /**
212
+ * For `update`: sets the description; blank clears it; absent leaves it.
213
+ */
214
+ description?: string | undefined;
202
215
  }
203
216
 
204
217
  /**
@@ -217,6 +230,28 @@ export interface WidgetRow {
217
230
  text: string;
218
231
  }
219
232
 
233
+ /**
234
+ * One subtask of a section item, with its contract summary.
235
+ */
236
+ export interface SectionChild {
237
+ status: Status;
238
+ text: string;
239
+ description: string | undefined;
240
+ unit: string | undefined;
241
+ }
242
+
243
+ /**
244
+ * One top-level task of a section, with every subtask.
245
+ */
246
+ export interface SectionItem {
247
+ status: Status;
248
+ text: string;
249
+ review: boolean;
250
+ description: string | undefined;
251
+ unit: string | undefined;
252
+ children: SectionChild[];
253
+ }
254
+
220
255
  /**
221
256
  * Opaque executor identity; no process interpretation belongs here.
222
257
  */
@@ -280,7 +315,7 @@ export interface Scope {
280
315
  /**
281
316
  * Root creation accepts standalone outcomes or outcomes with leaf work units.
282
317
  */
283
- export type Item = string | { content: string; subtasks: LeafItem[] } | DraftItem;
318
+ export type Item = string | { content: string; description?: string; subtasks: LeafItem[] } | DraftItem;
284
319
 
285
320
  /**
286
321
  * Semantic styling for a terminal row.
@@ -316,6 +351,15 @@ export type Operation = "init" | "start" | "done" | "drop" | "append" | "view" |
316
351
  */
317
352
  export type Status = "pending" | "in_progress" | "completed" | "abandoned";
318
353
 
354
+ /**
355
+ * The tasks of one status, in list order.
356
+ */
357
+ export interface TodoSection {
358
+ status: Status;
359
+ count: number;
360
+ items: SectionItem[];
361
+ }
362
+
319
363
  /**
320
364
  * The whole todo list of a session branch.
321
365
  */
@@ -442,6 +486,15 @@ export function parse(snapshot: unknown): TodoList;
442
486
  */
443
487
  export function protect(before: TodoList, after: TodoList, leases: UnitRef[]): void;
444
488
 
489
+ /**
490
+ * Typed status groups of `list`: always four sections, in a fixed order.
491
+ *
492
+ * # Errors
493
+ *
494
+ * Throws when `list` is not a valid todo list.
495
+ */
496
+ export function sections(list: TodoList): TodoSection[];
497
+
445
498
  /**
446
499
  * Records an executor's exact release evidence, never acceptance.
447
500
  *
@@ -482,6 +535,7 @@ export interface InitOutput {
482
535
  readonly inspect: (a: number, b: number, c: number, d: number) => void;
483
536
  readonly parse: (a: number, b: number) => void;
484
537
  readonly protect: (a: number, b: number, c: number, d: number, e: number) => void;
538
+ readonly sections: (a: number, b: number) => void;
485
539
  readonly settle: (a: number, b: number, c: number, d: number, e: number, f: number) => void;
486
540
  readonly view: (a: number, b: number) => void;
487
541
  readonly widget: (a: number, b: number, c: number) => void;
@@ -290,6 +290,35 @@ export function protect(before, after, leases) {
290
290
  }
291
291
  }
292
292
 
293
+ /**
294
+ * Typed status groups of `list`: always four sections, in a fixed order.
295
+ *
296
+ * # Errors
297
+ *
298
+ * Throws when `list` is not a valid todo list.
299
+ * @param {TodoList} list
300
+ * @returns {TodoSection[]}
301
+ */
302
+ export function sections(list) {
303
+ try {
304
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
305
+ wasm.sections(retptr, addBorrowedObject(list));
306
+ var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
307
+ var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
308
+ var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
309
+ var r3 = getDataViewMemory0().getInt32(retptr + 4 * 3, true);
310
+ if (r3) {
311
+ throw takeObject(r2);
312
+ }
313
+ var v1 = getArrayJsValueFromWasm0(r0, r1);
314
+ wasm.__wbindgen_export4(r0, r1 * 4, 4);
315
+ return v1;
316
+ } finally {
317
+ wasm.__wbindgen_add_to_stack_pointer(16);
318
+ heap[stack_pointer++] = undefined;
319
+ }
320
+ }
321
+
293
322
  /**
294
323
  * Records an executor's exact release evidence, never acceptance.
295
324
  *
Binary file
@@ -11,6 +11,7 @@ export const init: (a: number, b: number, c: number) => void;
11
11
  export const inspect: (a: number, b: number, c: number, d: number) => void;
12
12
  export const parse: (a: number, b: number) => void;
13
13
  export const protect: (a: number, b: number, c: number, d: number, e: number) => void;
14
+ export const sections: (a: number, b: number) => void;
14
15
  export const settle: (a: number, b: number, c: number, d: number, e: number, f: number) => void;
15
16
  export const view: (a: number, b: number) => void;
16
17
  export const widget: (a: number, b: number, c: number) => void;
package/src/guidance.ts CHANGED
@@ -7,6 +7,8 @@ const workUnitGuidance =
7
7
  "Supply deliverable, repository-relative scope, acceptance, prerequisite IDs, and self or worker/profile assignment. " +
8
8
  "Use local keys for new prerequisites and the optional start selector for atomic creation and activation. " +
9
9
  "Use returned IDs after creation. Update drafts before start; only one complete self leaf executes locally. " +
10
- "Done requires concrete acceptance evidence; worker exit alone is not acceptance.";
10
+ "Done requires concrete acceptance evidence; worker exit alone is not acceptance. " +
11
+ "Keep content a short headline (aim under 60 characters, imperative, no trailing detail); " +
12
+ "put context, approach, or acceptance detail in the optional description (one or two sentences).";
11
13
 
12
14
  export { workUnitGuidance };
package/src/index.ts CHANGED
@@ -39,7 +39,7 @@ export default function piTodo(pi: TodoAPI): void {
39
39
  provider.register();
40
40
  registerGate(pi, provider);
41
41
  pi.registerTool({
42
- description: `Keep branch-local outcomes and executable contracts. Use returned IDs, not labels. Draft creation is pending. Start one complete self-assigned leaf; workers require their assigned profile through the executor. Update replaces pending contract fields. Done requires acceptance evidence. Parents require explicit integration review after children close. ${workUnitGuidance}`,
42
+ description: `Keep branch-local outcomes and executable contracts. Use returned IDs, not labels. Draft creation is pending. Start one complete self-assigned leaf; workers require their assigned profile through the executor. Update replaces pending contract fields and may set or clear an open item's description. Done requires acceptance evidence. Parents require explicit integration review after children close. ${workUnitGuidance}`,
43
43
  async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
44
44
  const result = provider.run(params, ctx);
45
45
  work.publish(ctx);
package/src/schema.ts CHANGED
@@ -1,7 +1,11 @@
1
1
  import { StringEnum } from "@earendil-works/pi-ai";
2
2
  import { Type } from "typebox";
3
3
 
4
- const strings = Type.Immutable(Type.Array(Type.String())),
4
+ const description = Type.String({
5
+ description:
6
+ "Optional detail behind the short content headline: context, approach, or acceptance in one or two sentences. Blank is none.",
7
+ }),
8
+ strings = Type.Immutable(Type.Array(Type.String())),
5
9
  scope = Type.Immutable(
6
10
  Type.Object(
7
11
  {
@@ -43,6 +47,7 @@ const strings = Type.Immutable(Type.Array(Type.String())),
43
47
  {
44
48
  content: Type.String(),
45
49
  contract: Type.Optional(contract),
50
+ description: Type.Optional(description),
46
51
  key: Type.Optional(Type.String()),
47
52
  },
48
53
  { additionalProperties: false },
@@ -51,7 +56,10 @@ const strings = Type.Immutable(Type.Array(Type.String())),
51
56
  leaf = Type.Union([Type.String(), draft]),
52
57
  subtasks = Type.Immutable(Type.Array(leaf)),
53
58
  outcome = Type.Immutable(
54
- Type.Object({ content: Type.String(), subtasks }, { additionalProperties: false }),
59
+ Type.Object(
60
+ { content: Type.String(), description: Type.Optional(description), subtasks },
61
+ { additionalProperties: false },
62
+ ),
55
63
  ),
56
64
  item = Type.Union([leaf, outcome]),
57
65
  items = Type.Immutable(Type.Array(item)),
@@ -68,10 +76,15 @@ const strings = Type.Immutable(Type.Array(Type.String())),
68
76
  description:
69
77
  "Unit ID, or exact legacy content before migration. Children can use their ID without parent.",
70
78
  }),
79
+ change = Type.String({
80
+ description:
81
+ "update only: set the open task or subtask description; blank clears it; omit to keep it. Never changes content.",
82
+ }),
71
83
  parameters = Type.Immutable(
72
84
  Type.Object(
73
85
  {
74
86
  contract: Type.Optional(contract),
87
+ description: Type.Optional(change),
75
88
  evidence: Type.Optional(evidence),
76
89
  items: Type.Optional(items),
77
90
  op: StringEnum(["init", "start", "done", "drop", "append", "view", "update"] as const),
package/src/wasm.ts CHANGED
@@ -25,6 +25,7 @@ export {
25
25
  completion_reminder as completionReminder,
26
26
  init,
27
27
  parse,
28
+ sections,
28
29
  view,
29
30
  widget,
30
31
  } from "./generated/todo_core.js";
@@ -51,10 +52,13 @@ export type {
51
52
  Operation,
52
53
  ReminderFact,
53
54
  Request,
55
+ SectionChild,
56
+ SectionItem,
54
57
  Status,
55
58
  Subtask,
56
59
  Task,
57
60
  TodoList,
61
+ TodoSection,
58
62
  WidgetKind,
59
63
  WidgetRow,
60
64
  } from "./generated/todo_core.js";
package/src/work.ts CHANGED
@@ -12,7 +12,7 @@ interface WorkHost extends TodoHost {
12
12
  }
13
13
 
14
14
  type WorkCommands = Readonly<Pick<ExtensionAPI, "registerCommand" | "registerShortcut">>;
15
- const protocolVersion = 2,
15
+ const protocolVersion = 3,
16
16
  initialRevision = 0;
17
17
 
18
18
  interface WorkContext extends TodoContext {