@nredd/pi-todo
Pi todo extension with a persistent overlay and plan-aware task tracking.
Package details
Install @nredd/pi-todo from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:@nredd/pi-todo- Package
@nredd/pi-todo- Version
2.14.0- Published
- Nov 10, 2026
- Downloads
- 9,521/mo · 2,694/wk
- Author
- nredd
- License
- MIT
- Types
- extension
- Size
- 234.1 KB
- Dependencies
- 2 dependencies · 4 peers
Pi manifest JSON
{
"image": "https://raw.githubusercontent.com/nredd/pi-harness/main/packages/todo/docs/cover.png",
"extensions": [
"./index.ts"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
@nredd/pi-todo
Give the model a task list you can see. pi-todo adds a todo tool, a
/todos command, and a live panel above the editor to
Pi Agent, so you always know what the
agent is doing now, what it finished, and what is queued. The list is rebuilt
from the conversation itself, so it survives /reload and compaction — useful
on long research → design → implement sessions.
Fork changes
Imported from juicesharp/rpiv-mono
packages/rpiv-todo at fork commit 9a4e2e8 (nredd/rpiv-todo). Source, tests and docs are
based on that snapshot, with the workspace's injected-home seams and standalone test
support described below. Retained packaging deltas:
- Renamed to
@nredd/pi-todo;repository,homepage,bugsand the doc/license links point atnredd/pi-harness/packages/todo. Links that credit upstream stay onjuicesharp/rpiv-mono authordropped frompackage.json; the retained upstream MITLICENSEcarries attribution@juicesharp/rpiv-i18nis a harddependency(^2.13.9), not an optional peer, andpeerDependenciesMetais removed. The English-only fallback instate/i18n-bridge.tsis kept but unreachable in a normal install; only its comments changed to say sopi.imagepoints atdocs/cover.pngin this repository. The file is not packed (!docs/*.png); the URL resolves once the package is onmain- The persisted config path stays
~/.config/rpiv-todo/and the i18n namespace stays@juicesharp/rpiv-todo, for compatibility with existing configs and sessions - There is no nested
package-lock.json: the root workspace lock governs (npm ci --ignore-scripts). The packed contractnpm installs the tarball into an empty consumer, resolving from the registry, and audits that fresh install
In-process home injection
The default extension entry accepts registerTodo(pi, importOverlay?, options?: { homeDir?: string }).
An embedder can call registerTodo(pi, undefined, { homeDir: "/temporary/home" }) to bind config,
guidance, shortcut, resume and overlay reads to that home without changing HOME.
Omitting homeDir keeps the call-time os.homedir() default. An absolute XDG_CONFIG_HOME
still overrides the config directory; if its file is missing, the legacy fallback uses
<homeDir>/.config/rpiv-todo/config.json. This is an in-process dependency, not a new
user setting, and it does not write or migrate config files. importOverlay retains its
existing optional lazy-loader seam.
Verification
The workspace gate (from the repository root, or standalone in this directory):
npm ci --ignore-scripts
# Put Bun 1.4.4 (see .bun-version) on PATH.
npm run check
check runs Biome, strict TypeScript, and all 408 Vitest tests (including every
upstream test) on both Node and Bun. The two real interactive settled-row regressions
run inside that same suite on both engines. The test host
pins core v1.3.0-nredd.10 release tarballs. Unpublished upstream mock helpers
are vendored under .test-support/; provenance and test counts are documented
here. Tests inject temporary homes and use faux providers, never
a pi process or credentials. The measured line/function coverage and mutation receipts
are documented here; coverage policy remains separate.
Settled update/delete rows with a valid result snapshot retain that historical
subject. Legacy/error rows without one still read the live store and now call
context.invalidate() on foreground state changes, including replay and eviction.
Weak subscriptions avoid retaining discarded rows; render probes have no effects.
Install
pi install npm:@nredd/pi-todo
Restart your Pi session.
Quick start
Run /todos after the restart to confirm the extension is loaded. On a fresh
session it prints:
No todos yet. Ask the agent to add some!
Then ask for something with several steps — "add a repository layer with tests,
and track it as todos". The model calls todo and the panel appears above your
input box, updating as work moves:

Click the heading's ▾/▸ marker to collapse or expand the panel. The
ctrl+shift+t shortcut does the same thing. Resuming a session starts the panel collapsed (collapseOnResume). Run /todos at any time to print
the full list grouped by status.
What you get
- The plan stays on screen. A panel above the editor shows every task with a
status glyph, the label of whatever is in progress, and a
Todos (done/total)heading — you never have to ask the agent where it is. - Tasks survive
/reloadand compaction. Every change records the full post-mutation snapshot on the session branch, including changes made from a codemode script, and the list is replayed from there. No disk writes, nothing to lose. - Finished work gets out of the way. Completed rows stay visible for the rest of the turn, then drop at the start of the next one; the panel disappears entirely when the list empties.
- The overlay never eats your terminal. Past the row budget it becomes a
window over the whole list: scroll it with the mouse wheel, and
↑ N above/↓ N belowhints say what is out of view. Nothing is ever hidden. - Task status reflects actual work. Mark every started task
in_progress, including parallel work; assignownerto identify each worker. The owner shows on the row as@auth-audit; when it names a top-level subagent of this session (frompi-subagentslifecycle events, per the owner match rule) the row also shows that agent's live state,queued,running,done,failedorstopped. Anin_progresstodo whose agent failed or stopped is marked✗ failedor✗ stopped; the todo staysin_progressuntil the parent completes it with evidence. Complete each verified task immediately. Atomic batches may record independently verified results together, but must not delay individual completions. There is no global single-active-task limit. Plan text, completion evidence, and deferral reasons remain protected. - Requested changes cannot silently disappear. Action-inapplicable fields
reject the call, including an entire
opsbatch.createalways startspending; use an explicitupdatewith its id to change status. Dependency updates useaddBlockedBy/removeBlockedBy, neverblockedBy. - The agent can sequence work, not just list it.
blockedBydependencies are validated before anything is written — dangling ids, deleted dependencies, self-blocks, and cycles are all rejected. - An approved plan becomes the list, verbatim (nredd fork). With
pi-plan-mode, approving a plan seeds one todo per actionable bullet, copied word for word and tagged[plan]; context sections (Summary,Not adopted,Assumptions, ...) are skipped. Approved text cannot be edited and plan items cannot be deleted; activity labels, ownership, dependencies, and metadata remain editable. A revised approved plan replaces them. Status and evidence carry forward only for an unambiguous unchanged item, identified by its full heading path and work text. Legacy items without that identity restart pending on their next reapproval. See reconciliation and migration. The heading showsN/M plan items open. - Done needs proof (nredd fork).
completedrequires anevidencestring (command + result, commit, test name);deferredrequires areason, and the user is notified when a plan item is deferred. Batches notify only final deferrals, while each operation reports its own outcome and reason. See batch outcomes. If a run tries to finish with plan items open, the model gets one reminder listing them before it can wrap up. Outside Plan mode, a run that settles within_progresstodos also reports them (agent_settledinplan-hooks.ts), grouped by why each is still open:owner agent failed or stopped(all named),owner agent not found after resume,agent running for another session,held by the parent,agent running without a completion notice,blockedandagent running, three per group then a count.agent runningincludes an owner agent stillqueued.blockedmeans a liveblockedBydependency: onein_progresswhose owner agent has not failed or stopped, or onependingwhose owner agent is running or queued with a verified completion notice. A todo isheld by the parentwhen it has no owner, a free-text owner, an agent that finished without the todo being completed, or open blockers that are all dead (deferred, unownedpending, or abandoned), shown as e.g.(by #3 deferred). Each displayed todo names its owner even when no agent state is known, alongside any blocker details, e.g.(@release-team; by #3 deferred). AnAgent-tool background spawn or resume is heard atsubagents:created, so it counts as running while still queued; spawns that skipcreated(cross-extension RPC, the scheduler,@handle) are first heard atsubagents:startedand readhelduntil then (or atsubagents:failed, asstopped, if stopped while still queued). Its armed clause,armed: no todo-owned wake (monitor and agent notices list session wakes), says only that todos arm nothing themselves; thesubagentsandsession-kitmonitor notices say what does wake the parent. Only a lifecycle event explicitly confirming an agent completion notice makes that owner a verified wake; foreground, silent RPC and older unknown agents warn on every settle. Abandoned, lost or silent work, and parent-held work while nothing is armed (below), is a warning repeated on every settle, so a stop with owned work pending and nothing to wake the parent is never silent. When every todo waits on a running agent or a live dependency it is info, shown once: a settle that changes no todo's group, owner, owner agent state, wake eligibility or blocker identity, status, owner or owner agent state repeats nothing until the next prompt from a person. Todo status never changes on its own, e.g.Run ended with 2 in_progress todos, armed: no todo-owned wake (monitor and agent notices list session wakes); owner agent failed or stopped: #3 Audit auth (@auth-audit failed); held by the parent: #6 Deploy. After a process restart (session_startreasonstartup), anin_progresstodo whoseownerwas already set when the session started and that nosubagentsevent has named since isowner agent not found after resume(warning, every settle, all named): children die with the process, so it is not parent-held./new,/resume,/forkand/reloadare not that boundary: pi-subagents detaches still-running background agents. On everysession_start, and onsubagents:announce_survivors(which this extension sends once the session is bound and again at every settle), pi-subagents announces ALL running detached survivors in the process. Those registered under this session's file (after/reload, or a/resumeback into the session that spawned them) arrive assubagents:startedand arerunningand armed, withsubagents:completed/:failedreplayed when their results are delivered. Every other one arrives assubagents:other_session_survivorwith thesessionFileit wakes: its result spools there, not here. Only one registered under a file this session was forked from (its header'sparentSession, then that file's own, so a/fork, a fork of a fork, or a/reloadin either) makes a todo it owns readagent running for another session, which it wakes instead of this one(info, once); it is never armed here and never triggers the held-work reminder. Only ancestors count, compared by the exact paths pi wrote (no normalization; an unreadable or moved ancestor ends the chain): a survivor of any other session (/new,/resumeof another session, a sibling fork, or a descendant of this session) is not matched, because handles are per manager and a same-named dead owner must not hide behind it. That todo isheld(or lost) and reminds as usual, the safe direction. A repeatedstartedfor an agent already running with the same identity changes nothing, so re-announcing a survivor never outranks a newer same-named agent. The set is rebuilt from each announcement, so a/reloadloses nothing and a survivor that finished (even while this session was inactive) drops out at the next settle. An owner nobody names isheld, never lost. A free-text owner (a person or team) is indistinguishable from a dead agent handle, so it lands there too until it is reassigned. Parent-held work warns only when nothing is armed: a running monitor (session-kit:monitor:started/:stoppedonpi.events, payload{ sessionId, id, name }) or a background agent with a verified completion notice makes it an info report, shown once like the other waiting groups. Abandoned, lost and silent work always warn. When todos are held or lost and nothing is armed, nothing can wake the parent, soagent_before_settleappends one hiddenrpiv-todo-held-reminderand requests one more request, once per prompt from a person (a plan reminder is independent). Thecontexthook swaps the live list for the marker on that request only, like the plan reminder; it fires only if the todos are still held and unarmed then. Reminders pause while Plan mode is active; old approved todos remain until a replacement is approved. Exiting planning without approval resumes the old obligations. Details in the tool reference. - Parallel sessions stay separate. Task state is keyed by session, so a
detached or child session can neither read nor overwrite the foreground list.
Stale shutdowns use captured session identity and never assume ownership of
the foreground. Replay skips malformed snapshots rather than replacing valid
tasks, while accepting valid historical tasks without newer evidence fields.
Owner agent state is display-only: a subagent never reads or writes the
parent's list, and only the parent completes its todo. Without
pi-subagentsowners render without a state. An evicted agent keeps its last known state for the rest of the session, until 256 more recently updated agents push it out; agents are tracked per session, so after/newor/resumea todo never shows another session's agent. - Historical rows keep their own subjects. Settled calls render from their
validated result snapshots, not the current task list, including after reload
and resize. Live owner agent state appears only on the overlay and
/todos, never in a tool result or a settled row. Partial results and renderer probes do not change cached history. - Localized UI, no setup required. Nine locales ship with the package and are
served by
@juicesharp/rpiv-i18n, a regular dependency.
Configuration
Optional. Create ~/.config/rpiv-todo/config.json (or
$XDG_CONFIG_HOME/rpiv-todo/config.json if you set that variable):
{
"maxWidgetLines": 8,
"collapseKey": "alt+t"
}
| Setting | What it does | Default |
|---|---|---|
maxWidgetLines |
Content rows the overlay may use, heading included. Minimum 3. Applies on the next repaint. Pi's tool-output expansion mode shows all tasks. |
12 |
collapseKey |
Key that collapses and expands the panel, in Pi keybinding form (alt+o, ctrl+shift+t). Set "off" to register no shortcut. Needs /reload to rebind. |
"ctrl+shift+t" |
collapseOnResume |
Start the panel collapsed when a session is resumed. Set false to keep it expanded. Read at each session start. |
true |
guidance |
Replaces the built-in instructions the extension gives the model about when and how to use the todo list. Needs /reload. |
(built-ins) |
A missing or malformed file falls back to these defaults. pi-todo only reads
this file — it never writes one. Full semantics:
Configuration.
Reference
todotool reference — everytodoparameter, the status machine, the response envelope, and the exact error strings.- Configuration — config file resolution, option validation rules, and the accepted keybinding grammar.
- Overlay and
/todos— overlay lifecycle, glyphs, overflow behavior,/todosoutput, and localization.
Requirements
- A Pi Agent host. No API key, no model selection, no native dependencies.
- An interactive session for the panel and
/todos. Headless runs still get thetodotool; nothing is rendered. @juicesharp/rpiv-i18nand@juicesharp/rpiv-configinstall automatically as dependencies.
Related
@juicesharp/rpiv-i18n(source) — localizes this extension's UI chrome and adds a/languagespicker.@juicesharp/rpiv-pi(source) — the umbrella package that installs this extension alongside its siblings.
License
MIT, retained from upstream -- see LICENSE.
