TODO: cli that manages task, session, and worktrees for agentic coding.
📑 Table of Contents
- ✨ Features
- 🍿 Demo
- 🚀 Quick Start
- 📦 Installation
- 💻 Commands
- 🌳 Worktrees
- 🤖 Claude Integration
- 📅 Weekly Summaries
- 🗓️ Calendar
- ⚙️ Configuration
- 📂 Data
✨ Features
- Session management — links Claude sessions and working directories to tasks so you can resume exactly where you left off and reduce context overload
- Subtasks — break todos into smaller pieces that inherit their parent's branch, worktree, and links
- Plan management — each task and subtask have their own
plan.mdthat gets injected into Claude's system prompt, so context carries across sessions automatically. Subtasks automatically get their parent plans injected too. Works great with Obsidian! - Worktree management — spin up dedicated worktrees and leverage
td tryandtd takefor easy worktree management td do— create a todo and drop into a Claude session directly (run with no name to get a random NYC-inspired name)/tdslash command — manage todos from inside any Claude Code session using the non-interactive cli commands- Linear & GitHub linking — attach tickets, PRs, and branches to todos; open them from the picker
- Smart rename —
td renameuses Claude Haiku to suggest a clean, descriptive title based on your plan notes, so tasks stay organized without manual cleanup - Session summaries — auto-generates a
summary.mdwhen marking a todo as done, or on-demand from the picker. Uses Claude Haiku for fast, concise summaries of what was accomplished - Weekly summaries —
td weekgenerates an HTML report of tasks and PRs organized by day, with time-by-task stats showing how long you spent on each todo across sessions - Calendar view —
td calendarshows a month grid with per-day stats and clickable links into each day's weekly summary - Pre-compact hook — automatically snapshots conversation context into
plan.mdbefore Claude compacts, so notes are never lost - Local first — all storage is done in markdown and json reducing dependencies and letting agents modify directly.
🍿 Demo
This demo goes through the main td features: the todo list with subtasks, resuming sessions, using /td through Claude, worktree management, and plan injection using Obsidian.
td.mp4
🚀 Quick Start
td do "Fix the login bug" # Create a todo and start Claude immediately td do # Same thing — suggests a random NYC name td # Open the picker — select to resume the session td done # Mark it as done when finished
Inside a Claude session, use the /td slash command to manage todos without leaving the conversation.
📦 Installation
Requires Python 3.10+. Check your version:
Most macOS users already have Python 3 via Xcode command line tools or Homebrew. If not:
brew install python # macOS # or visit https://python.org/downloads
Quick install
curl -fsSL https://raw.githubusercontent.com/rosgoo/td/main/install-remote.sh | bashThis downloads the latest release, installs the Python package (via pipx, pip, or a managed venv), and sets up the Claude Code hook and /td slash command. fzf is bundled — no separate install needed. Make sure ~/.local/bin is in your PATH:
export PATH="$HOME/.local/bin:$PATH"
After installing, run td init to configure your data directory, editor, and other settings:
To update:
Other install methods
pip / pipx:
pipx install td # isolated install (recommended) pip install td # or pip
From source (for development):
git clone https://github.com/rosgoo/td.git cd td ./dev-install.sh # creates .venv, installs editable, links td to ~/.local/bin
This gives you td pointing to the Python dev version (editable — changes to src/ take effect immediately).
Hook configuration
The installer automatically injects the PreCompact hook into ~/.claude/settings.json. To skip this, pass --no-hooks:
curl -fsSL https://raw.githubusercontent.com/rosgoo/td/main/install-remote.sh | bash -s -- --no-hooksTo configure the hook manually instead, add this to ~/.claude/settings.json:
{
"hooks": {
"PreCompact": [
{
"hooks": [
{
"type": "command",
"command": "td-pre-compact",
"timeout": 10
}
]
}
]
}
}Editor setup
Obsidian:
Set editor to "obsidian" in your settings to open notes directly in Obsidian using the URI scheme. Your data_dir must be inside (or be) an Obsidian vault:
{
"data_dir": "~/td",
"editor": "obsidian"
}td edit <id> will open the note via obsidian://open?vault=td&file=..., navigating directly to the file. You can also create folders directly in Obsidian and run td sync to import them as todos.
Cursor:
Set editor to "cursor" to open notes in Cursor:
This uses the cursor CLI command, which Cursor installs via Cursor > Install 'cursor' command in the command palette.
Dependencies
| Tool | Purpose |
|---|---|
| Python 3.10+ | Runtime |
| Claude Code | AI coding sessions |
Python packages (installed automatically): typer, rich, iterfzf (bundles fzf).
💻 Commands
Most non-interactive commands accept an optional [id] (or ID prefix). This makes them usable by AI agents non-interactively.
Interactive
| Command | Description |
|---|---|
td |
Open the fzf picker (create or select a todo) |
td do ["title"] |
Create a todo and immediately open a Claude session (random name if omitted) |
td open |
Open notes directory in your editor |
td edit <id> |
Open plan.md in your editor |
td find [query] |
Search Claude sessions, create a todo, and resume |
td do and td new support -c <parent> to create as a subtask under a parent todo.
Non-interactive (AI-friendly)
| Command | Description |
|---|---|
td new ["title"] |
Create a new todo (-b for backlog, -c for subtask) |
td split [id] ["title"] |
Create a subtask under a parent todo |
td done [id] |
Mark a todo as done (optionally cleans up worktree/branch) |
td list |
List active todos |
td archive |
Show completed todos |
td get <id> |
Print todo as JSON |
td plan <id> |
Print the plan contents |
td plan <id> "text" |
Append text to a todo's plan |
td plan <id> -r <file> |
Replace plan.md with an existing file |
td plan <id> -o |
Open plan.md in your editor |
td show [id] |
Print the absolute path to plan.md |
td bump [id] |
Toggle a todo between TODO and backlog |
td rename [id] "title" |
Rename a todo |
td delete [id] |
Delete a todo and all related data (notes, worktree, branch) |
td link [id] [url/path] |
Link a Linear ticket, branch, PR, or plan file |
td try [id] |
Apply worktree diff to a try branch on main repo |
td take [id] |
Cherry-pick try branch changes back into the worktree |
td sync |
Two-way sync: create/remove todos and dirs (-n for dry run) |
td week |
Generate HTML weekly summary (--weeks N for multiple weeks) |
td calendar |
Open calendar view linking to weekly summaries (--months N) |
Admin
| Command | Description |
|---|---|
td init |
Configure settings interactively |
td settings |
Print the current settings file |
td update |
Update to latest version |
td version |
Print version |
td help |
Show help |
🔗 Linking
td link auto-detects the type from the input:
td link abc123 https://linear.app/team/CORE-456 # Linear ticket td link abc123 https://github.com/org/repo/pull/789 # GitHub PR td link abc123 https://github.com/org/repo/tree/feat # Git branch td link abc123 ~/vault/my-notes.md # External notes file
🧩 Subtasks
Subtasks inherit their parent's branch, worktree, and Linear ticket. Metadata that matches the parent is deduplicated in the picker view.
td split abc123 "Write tests" # Add a subtask under abc123 td new "Write tests" -c abc123 # Same thing td do "Write tests" -c abc123 # Create subtask and start Claude
🔄 Sync
td sync # Two-way sync: create todos for orphaned dirs, remove todos for missing dirs td sync -n # Dry run — show what would happen without making changes
If you create a folder in ~/td/todo/ manually (e.g. from Obsidian), td sync picks it up and creates a todo for it. If you delete a folder, td sync removes the orphaned todo. Nested subdirectories become subtasks automatically.
Todos can optionally use git worktrees for branch isolation. When you choose "Start Claude (new worktree)" from the picker, a worktree is created at .claude/worktrees/<slug> with a branch named todo/<slug>.
This keeps each task's changes on a separate branch in a separate directory, so you can work on multiple things without stashing or switching branches in your main repo.
td try — push changes out for testing
Use td try to test worktree changes on your main repo without leaving the worktree. It diffs all changes from the worktree branch against main and applies them as a single commit on a try-<slug> branch in the main repo.
This avoids the pain of switching back to main just to test — no reinstalling dependencies, no rebuilding, no restarting dev servers. Your worktree stays untouched while you can run the full test suite or start the app from the main repo on the try branch.
td try # pick a todo from the picker td try abc123 # or pass an ID directly
td take — pull changes back from try
After testing on the try branch, you may have made fixes or adjustments (manually or via Claude). Use td take to bring those changes back into the worktree.
It finds commits made on the try-<slug> branch after the initial td try commit and cherry-picks them into the worktree branch. Only the new work comes back — the original changes that were already in the worktree are skipped.
td take # pick a todo from the picker td take abc123 # or pass an ID directly
After a successful take, you're prompted to delete the try branch to keep things clean.
Typical workflow
worktree (todo/my-feature) main repo
| |
|--- td try ---> try-my-feature (changes applied)
| |
| fix tests, adjust code
| |
|<-- td take --- cherry-pick fixes back
|
continue working
Worktree setup script
Use the worktree_script setting to run a shell command automatically after a new worktree is created. The command runs with its working directory set to the new worktree, so you can install dependencies, copy environment files, or do any other setup.
Configure it in ~/.config/claude-todo/settings.json:
{
"worktree_script": "cp ../.env .env && npm install"
}Or set via the environment variable:
export TODO_WORKTREE_SCRIPT="cp ../.env .env && npm install"
This is useful when each worktree needs its own node_modules, virtual environment, or config files that aren't tracked by git.
Lifecycle
- Done —
td donegenerates a session summary (if linked to a session) and optionally cleans up the worktree and branch - Delete —
td deleteremoves the worktree, branch, notes, and todo record
🤖 Claude Integration
When you start or resume a Claude session from a todo, the tool:
- Injects the todo's
plan.mdinto Claude's system prompt via--append-system-prompt - For subtasks, also includes the parent's notes for context
- Tracks the session ID and working directory so you can resume later
Claude can also use td commands directly to manage work:
td new "Refactor auth middleware" td plan abc123 "Decided to use JWT instead of sessions" td link abc123 https://github.com/org/repo/pull/456 td done abc123 td delete abc123 --force
PreCompact hook
Before Claude Code compacts your conversation context (auto or manual), the pre-compact hook snapshots the conversation into your todo's plan.md under a ## Session Notes section. Each compact appends a timestamped block with user/assistant messages, so context is never fully lost.
The hook only activates for sessions linked to a todo (matched by session_id). Sessions without a todo are unaffected.
⚙️ Configuration
Run td init to configure settings interactively. Run td settings to view the current settings file.
This walks you through each setting:
data_dir — Where todos and notes are stored
Current: ~/td
editor — Editor for opening plan.md files
Examples: "code", "nvim", "obsidian"
Current: (auto-detect)
...
For example, to use Obsidian as your notes editor, set editor to "obsidian". This opens notes directly in Obsidian via the URI scheme — your data_dir should be inside (or be) an Obsidian vault. See Editor setup for more options.
Settings are saved to ~/.config/claude-todo/settings.json:
{
"data_dir": "~/td",
"repo": "",
"editor": "obsidian",
"linear_org": "",
"worktree_dir": ".claude/worktrees",
"branch_prefix": "todo",
"worktree_script": "",
"claude_command": ""
}| Field | Description | Default |
|---|---|---|
data_dir |
Where todos and notes are stored | ~/td |
repo |
Git repo root override — leave empty to auto-detect via git rev-parse. Useful if you work from a subdirectory or worktree and want to pin the repo root explicitly. |
(auto-detect) |
editor |
Editor for notes | open (macOS) / vi |
linear_org |
Linear organization slug (for ticket URLs) | (disabled) |
worktree_dir |
Worktree directory relative to repo root | .claude/worktrees |
branch_prefix |
Prefix for auto-created branches | todo |
worktree_script |
Shell command to run after creating a worktree (runs with cwd set to the new worktree) | (disabled) |
claude_command |
Command used to launch Claude (e.g. "claude --enable-auto-mode") |
claude |
Environment variables override settings: TODO_DATA_DIR, TODO_REPO, TODO_EDITOR, TODO_LINEAR_ORG, TODO_WORKTREE_DIR, TODO_BRANCH_PREFIX, TODO_WORKTREE_SCRIPT, TODO_CLAUDE_COMMAND.
📂 Data
~/td/
todos.json # All todo records
todo/
<title>/
plan.md # Notes for each todo (+ session notes appended by hooks)
summary.md # Session summary (generated on done or on-demand)
done/ # Completed todos (moved here by td done)
summary/
weekly-summary-YYYY-MM-DD.html # Weekly summaries (named by Monday's date)
calendar.html # Calendar view
📅 Weekly Summaries
td week generates a self-contained HTML report covering the past 7 days of work. Open it in your browser to see:
- Tasks by day — every todo that was active or completed that day, with its title, status, and any linked Linear ticket or GitHub PR
- Time-by-task stats — how many minutes each todo accumulated across all Claude sessions during the week, so you can see at a glance where your time actually went
- Session-based filtering — moves and cleanup that didn't involve a real Claude session are excluded, keeping the report focused on actual work
td week # current week td week --weeks 2 # last two weeks
The report is saved to ~/td/summary/weekly-summary-YYYY-MM-DD.html (named by Monday's date) and opened automatically in your default browser.
🗓️ Calendar
td calendar generates a month-grid HTML view and opens it in your browser. Each day shows:
- Total time worked (summed across all tasks with sessions that day)
- Number of todos touched
- A clickable link into that day's weekly summary
td calendar # current month td calendar --months 3 # last three months
The calendar is saved to ~/td/summary/calendar.html and re-generated each time you run the command.
📂 Data
Each todo record contains:
{
"id": "1773202250-266f62",
"title": "Fix the login bug",
"created_at": "2026-03-11T15:30:00Z",
"status": "active",
"branch": "todo/fix-the-login-bug",
"worktree_path": "/path/to/repo/.claude/worktrees/fix-the-login-bug",
"notes_path": "~/td/todo/Fix the login bug/plan.md",
"linear_ticket": "CORE-456",
"github_pr": "https://github.com/org/repo/pull/789",
"session_id": "a1b2c3d4-...",
"session_cwd": "/path/to/repo",
"parent_id": "",
"last_opened_at": "2026-03-11T16:00:00Z"
}