@harlley/todomd
todo.md — agentic task manager
Package details
Install @harlley/todomd from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:@harlley/todomd- Package
@harlley/todomd- Version
0.5.7- Published
- May 5, 2026
- Downloads
- 1,029/mo · 1,029/wk
- Author
- harlley
- License
- MIT
- Types
- extension
- Size
- 533.5 KB
- Dependencies
- 4 dependencies · 1 peer
Pi manifest JSON
{
"extensions": [
"./extensions"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
todo.md
Agentic task manager — turns TODO.md into a visual task board. Click a task to dispatch an AI agent in an isolated git worktree. Review the diff, approve to merge, or discard.
Works with Claude Code, Open Code, and Pi inside cmux.
Quick Start
1. Install
Claude Code:
# Add the marketplace (one-time)
claude plugin marketplace add harlley/todomd
# Install the plugin
claude plugin install todomd
Then restart Claude Code or run /reload-plugins. The first time you run /todomd-start, the skill will install the @harlley/todomd npm package globally if it isn't already on your PATH (~10–30s, one-time).
To pin or update the CLI manually: npm install -g @harlley/todomd@latest.
Open Code:
opencode plugin @harlley/todomd -g
That's it. opencode plugin fetches the package, adds it to your global opencode.json, and the plugin auto-installs its slash commands and skills into Open Code's discovery paths on first load.
Pi Agent:
pi install git:github.com/harlley/todomd
2. Create a TODO.md
Add a TODO.md to your project root (or let the plugin create one for you on first run):
# My Project — Tasks
## Status Legend
- [ ] Todo
- [~] Doing
- [x] Done
## Tasks
### Backend
- [ ] !!! Implement user authentication
> JWT-based auth with refresh tokens
- [ ] !! Add rate limiting to API
- [ ] ! Update API docs
3. Open the board
/todomd-start
A visual board opens in the cmux browser panel showing your tasks organized by section.
4. Dispatch an agent
Click a task on the board and choose an agent. The agent launches in a new cmux workspace with its own git worktree — your main branch stays clean.
5. Review and merge
When the agent finishes, a diff panel opens automatically. Then:
/todomd-approve— merge the changes into main and mark the task done/todomd-discard— throw away the changes and reset the task to todo
Commands
| Command | Description |
|---|---|
/todomd-start |
Open the board and start watching |
/todomd-review |
Open/refresh the diff panel for an active task |
/todomd-approve |
Commit, merge to main, mark done, close workspace |
/todomd-discard |
Delete worktree/branch, reset task to todo, close workspace |
/todomd-logs |
Show debug logs |
/todomd-bug |
File a GitHub issue with environment context and log excerpt |
Approve flow & feature branches
/todomd-approve squash-merges the task branch into whatever branch you currently have checked out in the main repo — it is not hard-coded to main. The target is resolved at approve time via git rev-parse --abbrev-ref HEAD (see getCurrentBranch in extensions/todomd/git-utils.ts and approveTask in extensions/todomd/core.ts).
This means you can drive a multi-task epic on a feature branch without ever touching main until you're ready to open a PR:
- In the main repo, check out your feature branch:
git checkout -b feat/foo(orgit checkout feat/foo). - Dispatch tasks from the board as usual. Each agent runs in its own worktree off that branch.
- Run
/todomd-approvefor each finished task — every approve squash-merges ontofeat/foo, because that's what's checked out. - When the epic is done, push
feat/fooand open a single PR.
Switch branches between approves if you want different tasks to land on different branches — the target is re-read every time.
[x] row metadata
After approve, the task row is rewritten with a metadata comment capturing the squash commit, the agent that did the work, and the branch it landed on:
- [x] Implement user authentication <!-- id:a1b2c3 commit:abc1234 agent:claude branch:feat/foo -->
| Field | Source |
|---|---|
commit:<sha> |
Short SHA of the squash commit on the target branch |
agent:<id> |
id from the agent entry in agents: frontmatter |
branch:<name> |
Target branch at approve time (git rev-parse --abbrev-ref HEAD) |
Requirements
- cmux terminal
- Claude Code, Open Code, or Pi coding agent
TODO.md Format
# My Project — Tasks
## Status Legend
- [ ] Todo
- [~] Doing
- [x] Done
<!-- agents: [{"id":"claude","label":"Claude","command":"claude --dangerously-skip-permissions {promptFile}"}] -->
## Tasks
### Backend
- [ ] !!! Implement user authentication <!-- id:a1b2c3 -->
> JWT-based auth with refresh tokens and role-based access control
- [ ] !! Add rate limiting <!-- id:d4e5f6 -->
- [ ] ! Update API docs <!-- id:g7h8i9 -->
### Frontend <!-- instructions: Focus on accessibility -->
- [~] !! Build settings page <!-- id:j0k1l2 -->
- [x] Create landing page <!-- id:m3n4o5 commit:abc1234 agent:claude branch:main -->
Task attributes
| Attribute | Format | Description |
|---|---|---|
| Title | Text after priority marker | The task description |
| Description | > blockquote below task |
Context for the agent |
| Priority | ! / !! / !!! |
Low / Medium / High |
| Status | [ ] / [~] / [x] |
Todo / Doing / Done |
| ID | <!-- id:xxx --> |
Auto-generated persistent ID |
| Commit | commit:xxx in metadata |
Short SHA of the squash commit (set by approve) |
| Agent | agent:xxx in metadata |
Which agent completed it |
| Branch | branch:xxx in metadata |
Branch the task was squash-merged onto (see Approve flow & feature branches) |
Agents configuration
Configure agents via an HTML comment in your TODO.md:
<!-- agents: [{"id":"claude","label":"Claude","command":"claude --dangerously-skip-permissions {promptFile}"},{"id":"pi-opus","label":"Pi Opus","command":"pi --model anthropic/claude-opus-4-6 < {promptFile}"}] -->
Each agent has id, label, and command with a {promptFile} placeholder that gets replaced with the path to the generated prompt file.
Runners
The runner: frontmatter field selects the backend that launches agent processes and opens the board. It is required — there is no auto-detection or silent fallback.
---
runner: tmux
agents:
- id: claude
label: Claude
command: claude -p < {promptFile}
---
Valid values: cmux, tmux, spawn.
| Runner | What it does | Pick when |
|---|---|---|
cmux |
Spawns each agent in its own cmux workspace. The board opens as a cmux browser surface, and stale tabs are closed automatically. | You're developing inside cmux. Only runner that gets workspace-scoped surface ops (tree, rename-tab, browser eval). |
tmux |
Spawns each agent as a detached tmux session (todomd-<slug>). The board opens via the host OS opener (open / xdg-open / start). |
Any Unix box with tmux installed. Full TTY so interactive agents work (Claude Code REPL, pi, etc.), and sessions survive this process exiting — you can tmux attach to watch. |
spawn |
Spawns each agent as a detached child via node:child_process.spawn with detached: true, stdio: 'ignore'. No multiplexer required. |
Bulletproof fallback — works anywhere Node runs. Only non-interactive agents (claude -p, opencode --prompt-style flags) because stdio is ignored. |
spawn limitations
spawn discards stdin/stdout/stderr, so any agent command that expects a TTY or reads user input will hang or exit immediately. Use flags that take the prompt from a file or argument:
---
runner: spawn
agents:
- id: claude
label: Claude (headless)
command: claude -p < {promptFile}
- id: opencode
label: Open Code (headless)
command: opencode run --prompt-file {promptFile}
---
PIDs live only in the runner's in-memory map, so a fresh CLI subprocess (approve / discard from Claude Code) can't kill agents an earlier process started — closeAgent treats the PID as lost and no-ops. The [?] review handoff still works because it goes through TODO.md, not through the runner.
Section instructions
Add per-section instructions for agents:
### Spikes <!-- instructions:Do research only. Do not change code without explicit approval. -->
Priority markers
!— Low (gray border)!!— Medium (orange border)!!!— High (red border)
Development
Building
npm install
npm run build # compile to dist/ (Claude Code plugin)
npm test # run tests
npm run typecheck # type-check without emitting
The Pi extension uses .ts source directly. The Claude Code plugin uses compiled dist/ output.
Architecture
TODO.md (source of truth)
│
├── file watcher detects changes
│
▼
Board HTML (cmux browser)
│
├── click task → dispatch agent
│
▼
Agent workspace (git worktree)
│
├── agent works in isolation
├── notification on completion
├── diff panel opens automatically
│
▼
approve → squash merge to main
discard → delete worktree, reset task
See CLAUDE.md for detailed architecture documentation.
License
MIT