@maxiaochao/pi-toolkit 0.9.1 → 0.9.2

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 CHANGED
@@ -1,3 +1,7 @@
1
+ ## 2026-09-30 - v0.9.2
2
+
3
+ - show contiguous queue positions in the todo widget while retaining stable internal task IDs
4
+
1
5
  ## 2026-09-30 - v0.9.1
2
6
 
3
7
  - redesign the todo lifecycle with plan replacement, cancellation, automatic advancement, active-only widget rendering, concise model guidance, and deterministic state tests
package/README.md CHANGED
@@ -38,7 +38,7 @@ To synchronize the file directly from a checkout instead:
38
38
  bash scripts/install.sh
39
39
  ```
40
40
 
41
- The `todo` tool maintains a session checklist for explicit plans and multi-step work. It automatically keeps one task active and advances the next queued task; `replace` starts a clean plan after a change of direction, while `cancelled` records obsolete work honestly. The persistent widget shows active work only, and `/todos` shows active and closed tasks in the current plan.
41
+ The `todo` tool maintains a session checklist for explicit plans and multi-step work. It automatically keeps one task active and advances the next queued task; `replace` starts a clean plan after a change of direction, while `cancelled` records obsolete work honestly. The persistent widget numbers active work by queue position; `/todos` shows active and closed tasks with their stable internal IDs.
42
42
 
43
43
  The `/btw` side chat is a separate agent session. It can inspect the main conversation and repository, and its thread stays out of the main context until you close it and choose to inject a summary. Use `/btw` to open it or `/btw <question>` to ask directly. While the BTW overlay has focus, use `Up` / `Down` to scroll its transcript one line at a time and `PageUp` / `PageDown` to move by one transcript page.
44
44
 
@@ -1,5 +1,6 @@
1
1
  import assert from "node:assert/strict";
2
2
  import {
3
+ activeTodoRows,
3
4
  activeTodos,
4
5
  applyTodoAction,
5
6
  restoreTodoState,
@@ -7,6 +8,19 @@ import {
7
8
 
8
9
  const empty = { todos: [], nextId: 1 };
9
10
 
11
+ assert.deepEqual(
12
+ activeTodoRows([
13
+ { id: 8, text: "closed", status: "done" },
14
+ { id: 9, text: "current", status: "doing" },
15
+ { id: 11, text: "next", status: "todo" },
16
+ ]).map(({ position, todo }) => ({ position, id: todo.id })),
17
+ [
18
+ { position: 1, id: 9 },
19
+ { position: 2, id: 11 },
20
+ ],
21
+ "active rows expose contiguous queue positions independently of internal IDs",
22
+ );
23
+
10
24
  const replaced = applyTodoAction(empty, {
11
25
  action: "replace",
12
26
  items: ["Inspect the failure", "Implement the fix", "Run verification"],
@@ -19,6 +19,11 @@ export interface TodoState {
19
19
  nextId: number;
20
20
  }
21
21
 
22
+ export interface ActiveTodoRow {
23
+ position: number;
24
+ todo: Todo;
25
+ }
26
+
22
27
  export interface TodoActionInput {
23
28
  action: TodoAction;
24
29
  text?: string;
@@ -73,6 +78,10 @@ export function activeTodos(todos: Todo[]): Todo[] {
73
78
  return todos.filter((todo) => todo.status === "doing" || todo.status === "todo");
74
79
  }
75
80
 
81
+ export function activeTodoRows(todos: Todo[]): ActiveTodoRow[] {
82
+ return activeTodos(todos).map((todo, index) => ({ position: index + 1, todo }));
83
+ }
84
+
76
85
  function cloneState(state: TodoState): TodoState {
77
86
  return { todos: state.todos.map((todo) => ({ ...todo })), nextId: state.nextId };
78
87
  }
@@ -9,7 +9,7 @@ import type { ExtensionAPI, ExtensionContext, Theme } from "@earendil-works/pi-c
9
9
  import { matchesKey, Text, truncateToWidth } from "@earendil-works/pi-tui";
10
10
  import { Type } from "typebox";
11
11
  import {
12
- activeTodos,
12
+ activeTodoRows,
13
13
  applyTodoAction,
14
14
  countTodoStatuses,
15
15
  normalizeTodo,
@@ -80,6 +80,11 @@ function renderTodoLine(theme: Theme, todo: Todo): string {
80
80
  return `${icon} ${theme.fg("accent", `#${todo.id}`)} ${theme.fg("text", todo.text)}`;
81
81
  }
82
82
 
83
+ function renderActiveTodoLine(theme: Theme, todo: Todo, position: number): string {
84
+ const icon = themedStatus(theme, todo.status, statusMarker(todo.status));
85
+ return `${icon} ${theme.fg("accent", `${position}.`)} ${theme.fg("text", todo.text)}`;
86
+ }
87
+
83
88
  class TodoListComponent {
84
89
  private cachedWidth?: number;
85
90
  private cachedLines?: string[];
@@ -134,7 +139,7 @@ export default function (pi: ExtensionAPI) {
134
139
 
135
140
  const refreshUI = (ctx: ExtensionContext) => {
136
141
  if (!ctx.hasUI) return;
137
- const visible = activeTodos(state.todos);
142
+ const visible = activeTodoRows(state.todos);
138
143
  if (visible.length === 0) {
139
144
  ctx.ui.setWidget(WIDGET_KEY, undefined);
140
145
  return;
@@ -146,7 +151,7 @@ export default function (pi: ExtensionAPI) {
146
151
  render(width: number): string[] {
147
152
  return [
148
153
  truncateToWidth(`${theme.fg("accent", "●")} ${theme.fg("muted", taskSummary(state.todos))}`, width),
149
- ...sortedTodos(visible).map((todo) => truncateToWidth(` ${renderTodoLine(theme, todo)}`, width)),
154
+ ...visible.map(({ position, todo }) => truncateToWidth(` ${renderActiveTodoLine(theme, todo, position)}`, width)),
150
155
  ];
151
156
  },
152
157
  invalidate() {},
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maxiaochao/pi-toolkit",
3
- "version": "0.9.1",
3
+ "version": "0.9.2",
4
4
  "description": "Personal pi coding agent toolkit: /todos, /btw, /cache_export and apply_patch extensions, browser automation, and bundled theme/skills.",
5
5
  "type": "module",
6
6
  "keywords": [