@harlley/todomd

todo.md — agentic task manager

Package details

extension

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:

  1. In the main repo, check out your feature branch: git checkout -b feat/foo (or git checkout feat/foo).
  2. Dispatch tasks from the board as usual. Each agent runs in its own worktree off that branch.
  3. Run /todomd-approve for each finished task — every approve squash-merges onto feat/foo, because that's what's checked out.
  4. When the epic is done, push feat/foo and 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


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