Turn messy “fix it later” notes — and in-code TODO/FIXME/XXX comments — into paste-ready briefs for Cursor, Claude, Codex, or any coding agent.
Todo CLI is a local, scriptable task queue with agent handoff built in — not another chat window. Capture work once, sync it from your codebase, reuse it forever, switch models freely.
Try it in the browser: amulyavarshney.github.io/todo-cli · Playground
If this saves you time, please ⭐ star the repo — it helps others discover it. Want to improve it? Fork and open a PR — see Contributing.
flowchart LR
You[You] --> Capture[Capture task]
Capture --> Enrich[Add context + acceptance]
Enrich --> Go["todo go"]
Go --> Clipboard[Clipboard prompt]
Clipboard --> Agent[Cursor / Claude / Codex]
Agent --> Done[todo done]
Done --> Recipe[Save as recipe]
Recipe --> Capture
Code[Code comments] --> Sync["todo sync / watch"]
Sync --> Capture
| Pain with chat-only agents | What todo-cli gives you |
|---|---|
| Work disappears when the tab closes | Durable queue on disk |
| Vague “fix auth” prompts | Structured briefs with acceptance criteria |
| Locked into one product | Same packet → any model |
| Repeating the same setup | Recipes + .todo/ project packs |
In-code TODO/FIXME/XXX rot |
todo sync / todo watch → queue + auto-close |
Follow these steps in order. Copy each block as you go.
Requirements: Python 3.9+
git clone https://github.com/amulyavarshney/todo-cli.git
cd todo-cli
python3 -m pip install -e .
todo --version # → todo-cli 1.6.2Optional but recommended — pin a stable data folder:
todo init
todo wheretodo add "water the plants"
todo add "change light bulb"
todo ls
todo done 1
todo reportYou should see numbered items like [2] t_… change light bulb. Numbers are for quick picks; ids stay stable.
todo add "Implement Google OAuth" \
--priority high \
--tag auth \
--for agent \
--template feature \
--accept "Users can sign in with Google" \
--accept "Existing auth tests pass"
todo context 1 "Reuse src/auth.py session store. Do not commit secrets."
todo ls --agent-onlytodo go --quietThat copies a full agent prompt to your clipboard. Paste it into Cursor, Claude, ChatGPT, or Codex.
Prefer files instead of clipboard?
todo go --handoff
# → handoffs/<task-id>/NEXT_PROMPT.mdPreview without touching the clipboard:
todo go --dry-runtodo project init # seeds code-todo / code-fixme / code-xxx recipes
todo scan --include .py # list hits
todo sync --include .py # import as linked tasks
todo ls --tag code
todo go --quietWhen you delete a TODO comment and run sync again, the linked task auto-completes. If the comment returns, the task reopens.
todo done 1
todo recipe save oauth 1 # save before done if you still need the task
# or polish a task first, then:
todo recipe save oauth <task-id>
todo recipe use oauth "Add GitHub OAuth provider"
todo go --quiet🎉 You’re in the loop: capture → sync → go → agent → done → recipe.
sequenceDiagram
participant U as You
participant T as todo-cli
participant A as Cursor / Claude / Codex
U->>T: todo add "... --for agent" / todo sync
U->>T: todo go
T-->>U: Prompt on clipboard (+ optional handoff files)
U->>A: Paste NEXT_PROMPT / clipboard
A-->>U: Code changes + summary
U->>T: todo done <id>
U->>T: todo recipe save name <id>
Optional live dispatch (Cursor SDK — not required for daily use):
python3 -m pip install 'todo-cli[agent]'
export CURSOR_API_KEY=...
todo go --run --cwd .
# Preview SDK payload without calling the API:
todo go --run --dry-run --cwd .Rule of thumb: commit recipes and defaults; keep each person’s queue local.
flowchart TB
subgraph repo [Git repo — shared]
Pack[".todo/project.json"]
Recipes[".todo/recipes/*.json"]
end
subgraph local [Per developer — private]
DirA["TODO_DIR / --dir A"]
DirB["TODO_DIR / --dir B"]
end
DevA[Developer A] --> Pack
DevB[Developer B] --> Pack
DevA --> DirA
DevB --> DirB
DirA --> GoA["todo go"]
DirB --> GoB["todo go"]
GoA --> AgentA[Any agent]
GoB --> AgentB[Any agent]
# In your application repo (once):
todo project init --name my-app
git add .todo/project.json .todo/recipes .todo/.gitignore
git commit -m "Add todo-cli agent project pack"
# On each machine / clone (private queue — do NOT commit tasks.json):
export TODO_DIR="$HOME/.todo-cli/queues/my-app"
# or: todo --dir ~/.todo-cli/queues/my-app sync
todo sync --include .py
todo go --quiet.todo/.gitignore (created by project init) already ignores tasks.json, undo.json, handoffs/, and the lock file so clones never fight over the same queue.
More detail: todo help team.
Scan the repo for single-line TODO / FIXME / XXX comments, import them as tasks (with recipes), and keep the queue in sync as comments appear or disappear:
todo scan # list hits (path:line + summary)
todo sync # create/update tasks; close when comment is gone
todo watch --interval 2 # re-sync when file mtimes change (Ctrl+C to stop)
todo go --quiet # hand the next imported task to an agent| Event | Result |
|---|---|
| New comment found | Creates a linked task (source.kind=code_comment) |
| Comment moves (same text) | Updates source.path / source.line |
| Comment removed | Auto-completes the linked task (one undoable mutation) |
| Comment returns | Reopens the task (clears claim + auto-complete notes) |
You edited todo context |
Your context is kept on later syncs |
| Scan finds 0 comments (files still match) | Auto-completes linked tasks that disappeared |
| Scan matches no source files | Auto-close is skipped (use --force-close to override) |
Identical comments in the same file get distinct keys (code:abc, code:abc#2, …).
| Flag | Meaning |
|---|---|
--root PATH |
Directory to scan (default: cwd) |
--include .py |
Limit to suffixes (repeatable) |
--marker FIXME |
Limit markers (repeatable; default TODO,FIXME,XXX) |
--no-close |
Do not auto-complete when comments disappear |
--no-reopen |
Do not reopen when comments return |
--no-agent |
Do not assign imported tasks to an agent |
--force-close |
Allow auto-close even when no source files match |
--no-ignore |
Do not respect .gitignore / .todoignore |
--json |
Machine-readable report (scan / sync) |
--once |
watch: run one sync and exit |
--interval N |
watch: poll every N seconds (default 2) |
Tip: always pass a sensible
--root/--includein large monorepos. If the filter matches no files, auto-close is skipped so a bad flag cannot wipe the queue — override with--force-closeonly when you mean it.
| I want to… | Run |
|---|---|
| Add a quick task | todo add "buy milk" |
| Add a bug for an agent | todo add "Login 500" --template bug --for agent |
| List / import code TODOs | todo scan / todo sync |
| Watch comments live | todo watch |
| See what’s next | todo next or todo go --quiet |
| Preview a go prompt | todo go --dry-run |
| Filter the queue | todo ls --tag auth --unblocked |
| Block task 3 on task 2 | todo block 3 --on 2 |
| Undo a mistake | todo undo |
| Check install health | todo doctor |
| Backup my queue | todo backup |
| Find my data files | todo where |
| Export prompts | todo export -f prompt --agent-only |
| Command | Description |
|---|---|
todo / todo help |
Show usage |
todo help COMMAND |
Per-command help (also team) |
todo add "text" |
Add a todo |
todo add "text" --template bug|feature|refactor|chore |
Add from built-in template |
todo add "text" --recipe NAME |
Add from a saved recipe |
todo ls / todo ls --json |
List pending todos |
todo edit REF ["text"] |
Edit text / metadata |
todo del REF |
Delete |
todo done REF |
Complete |
todo undo |
Revert last change |
todo go [REF] |
Next task → clipboard (+ --run / --handoff / --dry-run) |
todo next |
Show best unblocked task |
todo prompt [REF] |
Print agent prompt |
todo handoff [REF] |
Write Markdown + JSON package |
todo run [REF] |
Optional Cursor SDK dispatch |
todo recipe list|save|use|show|delete |
Manage recipes |
todo scan |
List TODO/FIXME/XXX comments |
todo sync |
Import/sync comments → tasks (auto-close) |
todo watch |
Poll file mtimes and re-sync |
todo doctor |
Health checks for data dir / config |
todo backup |
Zip the data directory |
todo completion bash|zsh|fish |
Print shell completion script |
todo project init |
Create .todo/ pack (+ comment recipes) |
todo context REF "..." |
Attach context |
todo accept REF "..." |
Add acceptance criterion |
todo block REF --on DEP |
Add dependency |
todo unblock REF |
Clear dependency |
todo claim REF --as cursor |
Mark claimed |
todo export -f json|md|prompt |
Export |
todo report / todo report --all |
Counts |
todo clear |
Clear completed history |
todo where / todo init |
Data dir + config |
todo --version |
Version |
REF can be a display number (1) or a stable id (t_abc12345).
| Flag | Where | Meaning |
|---|---|---|
-p / --priority |
add, edit |
low, medium, high, critical |
-t / --tag |
add, edit |
Repeatable tags |
--for AGENT |
add |
Assignee (agent, cursor, …) |
--accept |
add |
Acceptance criterion (repeatable) |
--agent-only / --all |
go, ls, next, … |
Prefer agent-assigned work / include all |
--quiet |
go |
Copy without reprinting the full prompt |
--handoff |
go |
Also write handoff files |
--run |
go |
Dispatch via Cursor SDK |
--dry-run |
go |
Preview prompt (no clipboard); with --run, preview SDK call |
--dry-run / --print-only |
run |
Preview without API calls |
--no-copy |
go |
Print prompt without clipboard |
flowchart TD
Dir["--dir flag"] --> Store
Env["TODO_DIR env"] --> Store
Cfg["config data_dir"] --> Store
Cwd["current directory"] --> Store
Store[(tasks.json + mirrors)]
Store --> Handoffs[handoffs/]
Store --> Undo[undo.json]
| File | Purpose |
|---|---|
tasks.json |
Source of truth |
todo.txt / done.txt |
Plain-text mirrors |
handoffs/ |
Agent packages + run results |
undo.json |
Last mutation for todo undo |
.todo/project.json |
Repo-shared defaults + comment_recipes |
.todo/recipes/*.json |
Shared / comment recipes |
todo where # show active data directory
todo init # default data_dir → ~/.todo-cliExit codes: 0 success, 1 error (messages on stderr).
todo-cli is local-first:
- No telemetry, no analytics, no phone-home.
- Task data stays in your data directory (
todo where). - The only optional network path is the Cursor SDK when you explicitly install
todo-cli[agent], setCURSOR_API_KEY, and runtodo run/todo go --run.
todo init
todo doctor # verify data dir + tasks.json
todo backup -o ~/Backups # zip before risky experiments
todo help sync # per-command help
eval "$(todo completion zsh)" # bash | zsh | fish
# fish: todo completion fish > ~/.config/fish/completions/todo.fish- Scan ignores
.gitignoreand.todoignoreby default (--no-ignoreto override). - Team layout: shared
.todo/recipes+ privateTODO_DIR— see Share the workflow /todo help team. - Security: SECURITY.md · Conduct: CODE_OF_CONDUCT.md
- Install: from source today; PyPI publish is wired via GitHub Releases. After release:
pipx install todo-cli. Packaging notes: docs/packaging.md. - Website: GitHub Actions deploys
web/→ amulyavarshney.github.io/todo-cli on every push tomain(workflow:.github/workflows/pages.yml). One-time setup: Settings → Pages → Source: GitHub Actions.
Todo CLI is free and open source (MIT). A little visibility goes a long way:
- ⭐ Star on GitHub — signals the project is useful
- 🍴 Fork the repo — experiment safely, then contribute back
- 💬 Open an issue — bugs, ideas, and questions welcome
- 🔀 Send a pull request — even small docs/tests PRs help
Star → Fork → Branch → Commit → PR → Review → Merge
We love contributions of all sizes — docs, tests, UX polish, and features.
# 1) Fork on GitHub, then clone YOUR fork
git clone https://github.com/<your-username>/todo-cli.git
cd todo-cli
# 2) Add upstream
git remote add upstream https://github.com/amulyavarshney/todo-cli.git
# 3) Create a branch
git checkout -b feat/my-improvement
# 4) Install + test
python3 -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
python3 -m pip install -e ".[dev]"
pytest
# 5) Push and open a PR against amulyavarshney/todo-cli
git push -u origin HEADFull guidelines: CONTRIBUTING.md.
MIT © Amulya Varshney
Ready? todo sync && todo go — then paste into your favorite agent.
If it helped, leave a star so more people can find it. Thank you!