An interactive TUI todo list manager built with Bun and TypeScript.
# Install Bun if you haven't already
curl -fsSL https://bun.sh/install | bash
# Clone and run
git clone https://github.com/dqian/todo-cli.git
cd todo-cli
bun startThe repo ships with example todos so you can try it immediately.
To reset with fresh example data:
bun run src/seed.tsbun start # or: todo (if linked)Navigate with arrow keys, use the hotkey bar at the bottom for available actions.
todo list # List all todos (grouped by category)
todo list --active # Active only
todo list --done # Completed only
todo list --category "work" # Filter by category
todo add "fix the bug" # Add a todo
todo add "deploy" --priority today # Add with priority
todo add "specs" --category "work" # Add with category
todo done 3 # Mark #3 done
todo undone 3 # Mark #3 active
todo rm 3 # Delete #3
todo edit 3 "updated text" # Edit #3
todo clear-done # Remove all completed
todo json # Raw JSON exportTo make todo available as a command, add an alias to your shell config (~/.bashrc, ~/.zshrc, etc.):
alias todo="bun run /path/to/todo-cli/src/index.ts"Then restart your shell or source the config. Now you can run todo from anywhere — TUI mode with no args, or CLI commands like todo list, todo add "...", etc.
Todos are organized into named categories. When adding a new todo in the TUI, it inherits the category of the currently selected item. Press Tab to cycle through existing categories, or use g to reassign a todo's category.
Three priority levels: today, week, and none. Press p to cycle through them. Filter the view with number keys:
1— show all2— today only3— today + week
Items with a prefix: description pattern (e.g. spec: dashboard layout, spec: API rate limiting) are automatically collapsed into expandable groups when they share a prefix and have no priority set. Press Tab to expand/collapse a group.
Press / to search across todo text, tags, and categories. Results filter in real time.
Press s to sort todos within each category by priority rank: today > week > done > none.
Press x to clear all completed todos (with confirmation).
Press o to open the options menu where you can rebind any hotkey. Bindings are saved to data/config.json.
URLs in todo text are extracted and rendered as clickable [Link] labels (terminal support required). Click to open in your default browser.
Press c to chat with Claude in natural language. You can say things like:
- "mark all errands as done"
- "add a todo to buy milk in the errands category"
- "move everything in learning to priority week"
- "delete all completed todos"
Claude uses tool calls to manipulate your todo list directly.
Set your Anthropic API key via either:
- Environment variable:
export ANTHROPIC_API_KEY=sk-ant-... - Options menu: Press
oin the TUI, select API Key, and paste your key
Get an API key at console.anthropic.com.
You can override the model with ANTHROPIC_MODEL (defaults to claude-sonnet-4-20250514).
todo-cli automatically commits and pushes data/todos.json to your git remote on every change, with a 10-second debounce. This gives you version history and cross-machine sync for free.
For sync to work, the repo needs push access to a git remote. Two options:
SSH (recommended)
If you cloned via SSH (git@github.com:...), sync works automatically as long as your SSH key is set up:
# Verify your remote
git remote -v
# Should show: git@github.com:your-user/your-repo.gitHTTPS with Personal Access Token
If you cloned via HTTPS, you'll need a GitHub Personal Access Token (PAT) for push access:
- Go to github.com/settings/tokens
- Generate a Fine-grained token with
Contents: Read and writepermission scoped to your repo - Configure git to use it:
# Option A: credential helper (stores token securely)
git config --global credential.helper osxkeychain # macOS
git config --global credential.helper store # Linux (plaintext ~/.git-credentials)
# Then push once and enter your token as the password
git push
# Option B: embed in remote URL (less secure)
git remote set-url origin https://<TOKEN>@github.com/your-user/your-repo.gitSince todo state lives in the repo itself (data/todos.json) and git sync pushes changes automatically, you'll want your own private copy. The recommended approach:
- Fork this repo and set the fork to private (Settings > Danger Zone > Change visibility)
- Clone your private fork
- Set up your alias to point at the fork
Your todos sync to your private fork, and you can pull upstream code changes whenever you want:
git remote add upstream https://github.com/dqian/todo-cli.git
git pull upstream mainIf you don't want git sync, the push will silently fail if there's no remote configured. No setup needed — it just works locally.
- Todos:
data/todos.json— your todo list (JSON, version 2 format) - Config:
data/config.json— API key and hotkey overrides (gitignored)
A progressive web app lives in docs/. It provides a browser-based interface to the same todo data. To use it, serve the docs/ directory or enable GitHub Pages on the repo.
MIT