@arhen/pi-core-todo
Flat or arbitrarily nested Pi todos with explicit statuses, direct-child progress, dependencies, a compact widget, and a scrollable tree browser.
Package details
Install @arhen/pi-core-todo from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:@arhen/pi-core-todo- Package
@arhen/pi-core-todo- Version
1.0.14- Published
- Oct 13, 2026
- Downloads
- 1,105/mo · 390/wk
- Author
- arhen
- License
- MIT
- Types
- extension
- Size
- 76.9 KB
- Dependencies
- 0 dependencies · 4 peers
Pi manifest JSON
{
"extensions": [
"./src/index.ts"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
@arhen/pi-core-todo
Flat or arbitrarily nested todos for Pi, with explicit statuses, dependency links, atomic batch mutations, a compact widget, and a scrollable tree browser.
Install
pi install npm:@arhen/pi-core-todo
Registers todo and /todos; use only one todo extension at a time.
Nested todos
Nesting is optional. Existing flat tasks and saved sessions continue to work.
{ "action": "create", "subject": "Build feature" }
{ "action": "create", "subject": "Add validation", "parentId": 1 }
{ "action": "create", "subject": "Check inputs", "parentId": 2 }
Tool headings identify those tasks as #1, #1.a, and #1.a.a. Todo rows show clean titles without ID prefixes. Each nesting level uses sibling letters, continuing through z, aa, ab, etc. Deleted siblings reserve their letters so surviving siblings do not get renamed. Reparenting intentionally changes display paths.
Tool arguments still use stable numeric IDs. Model-facing results include numeric references for children, while tool headings use hierarchical display paths and todo rows contain no IDs.
- Set
parentIdon create/update to attach a task at any depth. - Set
parentId: nullto move a task and its subtree to the root. - Omit
parentIdto leave existing behavior unchanged. - Missing/deleted parents, self-parenting, and hierarchy cycles are rejected.
- Nesting and
blockedBydependencies are separate relationships.
Status and progress
Statuses stay explicit: pending ⇄ in_progress → completed, plus deleted tombstones (terminal and immutable). Completing a child does not change its parent's status. A parent cannot be completed while any live descendant is unfinished, and unfinished subtrees cannot be attached under completed parents.
Each parent displays [completed direct children / total live direct children]. Grandchildren and deeper descendants are not counted in that parent's counter; leaves have no counter.
Deleting a parent, including update with status: "deleted", promotes its live direct children to the nearest live ancestor, or the root. It never deletes their subtrees. Existing dependency cleanup still removes links to the deleted task.
Bounded UI
todo → #7.b.b
● Todos (2/12)
├─ ○ Build nested todos [1/2]
│ ├─ ✓ Parent links
│ └─ ○ Safe reparenting [1/2]
│ ├─ ✓ Validate parents
│ └─ ◐ Reject cycles
└─ +7 hidden · /todos
The above-editor widget uses at most 8 total lines or one quarter of the terminal height, whichever is smaller. Short panes shrink it to a summary/active row; panes below four rows hide it. Active tasks and their ancestor context take priority over unrelated rows. Completed rows hide after the next agent turn without changing progress totals.
Deep indentation compresses with an explicit depth marker; long display paths compress in tool headings. Subjects are sanitized and truncated by visible terminal columns, including ANSI styling, wide characters, and emoji.
Tool calls are single-line previews. Results remain bounded even when expanded: at most 5 collapsed or 8 expanded lines, reduced further for short panes. Mutations show the affected task rather than repeating the whole list. Model-facing list output shows up to 100 tasks; get provides individual details.
/todos browser
Open the full tree in a bounded overlay: at most 24 lines or 70% of terminal height. All tasks remain reachable by scrolling and folding; no long notification consumes the screen.
| Key | Action |
|---|---|
| ↑ / ↓ | Move selection |
| ← | Collapse branch, or select its parent |
| → | Expand branch, or select its first child |
| Enter / Space | Toggle branch |
| PgUp / PgDn | Scroll one page |
| Home / End | First / last visible task |
| Esc / Ctrl+C | Close and return to the editor |
Selection stays visible when the terminal resizes. The browser is read-only and TUI-only: outside a terminal /todos notifies instead of opening. The agent manages task state through todo.
Tool contract
Actions: create, update, batch, list, get, delete, clear.
Fields: subject, description, activeForm, status, parentId, id, blockedBy, addBlockedBy, removeBlockedBy, owner, metadata, includeDeleted, plus ops for batch.
Create defaults to pending and accepts an optional initial status: pending, in_progress, or completed, including in batch create ops. Use activeForm when creating an in-progress task. deleted is not an initial status; use delete or update to tombstone an existing task. Update sets the task's explicit status. blockedBy is the create-time seed — on update use addBlockedBy / removeBlockedBy. Dependency updates are additive, with self-block/cycle rejection. List hides deleted tasks unless includeDeleted: true and supports a status filter. Tombstoned tasks reject mutation (task #N is deleted; tombstones are immutable). clear wipes the list and restarts IDs at 1.
Keep the list live while you work: the same tool adds the new step, splits a step into child subtasks via parentId, rewords or re-scopes an item, and cancels dropped work by deleting it. An update must change something — a status or another mutable field; an update without one is rejected.
Batch changes
batch applies an ordered ops array of create / update / delete in one call, so a whole list, subtree, or round of progress updates is one tool call instead of many.
{
"action": "batch",
"ops": [
{ "action": "create", "ref": "parent", "subject": "Build feature" },
{ "action": "create", "subject": "Add validation", "parentId": "parent" },
{ "action": "create", "subject": "Wire UI", "parentId": "parent" },
{ "action": "update", "id": "parent", "status": "in_progress", "activeForm": "building feature" },
{ "action": "delete", "id": 7 }
]
}
- Each op accepts the same fields and validation as the matching single action, including initial
statuson create; the top level carries onlyops. - Ops run in order on the accumulated list. The batch is atomic: the first failing op aborts it — nothing is applied and the error names the op, e.g.
batch op 2 (update): #99 not found; no changes applied. - A create op may declare
ref(letter-first alias). Later ops target it inid,parentId,blockedBy,addBlockedBy, orremoveBlockedBy. Refs are unique per batch and only resolve backwards; numeric strings like"7"resolve as task ids. deleteaccepts onlyid; tombstone immutability, parent completion, dependency cycles, and dedup rules are identical to single actions.- Results report one line per op (
created #1: Build feature,created #2: Investigate (in_progress),updated #1 (pending → in_progress)), and the UI preview lists the affected tasks, bounded likelist. - Validation errors identify the rejected fields and are flagged as failed tool results, so the agent can correct its call.
Persistence
Every successful mutation saves a state snapshot in Pi's session JSONL log, including mutations called through codemode. Snapshots are outside model context and survive compaction, reloads, and restarts. Separate Pi sessions cannot overwrite one another's todos. --no-session runs remain ephemeral.
State follows the active branch: /tree restores the list at that point, and forks/clones inherit the copied snapshots. /new starts a separate list. Parent links, statuses, dependencies, metadata, tombstones, and the next numeric ID are preserved. After a context reset, use todo with action: "list" rather than recreating tasks.
Existing sessions recover from successful todo mutation results in their active branch, or migrate their legacy ~/.pi/agent/pi-todo-state.json entry when no logged snapshot exists. The legacy file is read-only; new state is never written to the shared sidecar. Error and read-only results do not override saved mutation state.
Development
bun test packages/core/pi-core-todo/test
bunx tsc -p packages/core/pi-core-todo/tsconfig.json --noEmit
Tests cover persistence and legacy recovery, reducers, batch atomicity and refs, deep hierarchy operations, deletion, display paths, direct-child counters, narrow/short rendering, keyboard folding/scrolling, resize, and hostile terminal text.
License
MIT. Forked from @juicesharp/rpiv-todo.