torudo 0.19.0

A terminal-based todo.txt viewer and manager with TUI interface
torudo-0.19.0 is not a library.

Torudo

Crates.io

A terminal-based todo.txt viewer and manager written in Rust with TUI interface.

Features

  • Project-based column view with priority sorting
  • Pick layout (Todo mode, v to toggle): groups todos into (A) / High / Low / No Energy columns for GTD next-action pickup; composes with the n energy/time filter
  • GTD modes (Inbox, Todo, Waiting, Ref, Someday) switchable with Tab / Shift+Tab
  • Vimium-like f jump: press f to overlay short labels on every visible card and jump selection with one keystroke
  • External capture via torudo inbox add "..." — add items to the inbox from scripts, launchers, or editor bindings without the TUI running
  • Vim integration and real-time file watching
  • URL detection (🔗) and browser open (o)
  • Self-update via GitHub Releases (torudo update)
  • crmux / Claude Code integration

Demo

gif

Installation

Quick install

curl -sSL https://raw.githubusercontent.com/maedana/torudo/main/install.sh | sh

Set TORUDO_VERSION to install a specific release instead of the latest one:

curl -sSL https://raw.githubusercontent.com/maedana/torudo/main/install.sh | TORUDO_VERSION=v0.17.0 sh

From crates.io

cargo install torudo --locked

Build from source

git clone https://github.com/maedana/torudo.git
cd torudo
cargo build --release

Configuration

Command Line Options

  • --todotxt-dir <PATH>: Directory containing your todo.txt file (default: ~/todotxt, fallback: TODOTXT_DIR env var)
  • --nvim-listen <PATH>: Neovim socket path set by nvim --listen (default: /tmp/nvim.sock, fallback: NVIM_LISTEN_ADDRESS env var)

Usage

First Time Setup

When you run Torudo for the first time, it will check for the required directory and files:

  1. If ~/todotxt directory doesn't exist, it will ask permission to create it
  2. If todo.txt doesn't exist, it will ask permission to create an empty file
  3. If you decline either creation, the application will exit

This ensures you have control over where your todo files are stored.

Basic Usage

# Run torudo (looks for todo.txt in $TODOTXT_DIR or ~/todotxt)
torudo

# Specify todotxt directory
torudo --todotxt-dir ~/my-todos

# Run with debug mode for detailed logging
torudo -d

# Specify Neovim socket path
torudo --nvim-listen /tmp/my-nvim.sock

Working with Items from Outside the TUI

Everything torudo holds is reachable from the CLI without the TUI running, which makes it usable from launchers, shell scripts, editor keybindings, and agents.

The CLI splits along one line: a mode is a place, so add and list name one (<mode> is inbox, todo, waiting, ref, or someday). An item is a thing, and its id: is unique across the files, so set, link, complete, promote and show take just the id and find the mode themselves. reopen is the exception that takes --to, because there the mode is the destination: the item is in done.txt, and nothing records which file it left.

# Capture a new task into the inbox
torudo inbox add "(A) Buy milk +grocery @home"

# Pipe the output through jq to grab the generated id
torudo inbox add "Draft blog post +writing" | jq -r .id

# Preserve an explicit id (otherwise a UUID is generated)
torudo inbox add "Fix #123 id:my-custom-id"

# List a mode — plain todo.txt lines, or JSON for scripting
torudo inbox list
torudo todo list --json

# Fold each item's detail md (todos/{id}.md) into the JSON as an "md" field (--json required)
torudo todo list --json --with-md

# Set GTD tags on an item (--time short|medium|long, --energy low|high)
torudo set <id> --time short --energy low

# Make an item recur on completion (see "Recurring Items" below)
torudo set <id> --rec 1w

# Stop an item recurring
torudo set <id> --rec none

# Set the priority (a-e, uppercase also works)
torudo set <id> --priority a

# Clear the priority
torudo set <id> --priority none

# Push an item out to a date (t:) — until then it sorts last and renders dimmed
torudo set <id> --threshold 2026-08-24

# Give it a deadline (due:)
torudo set <id> --due 2026-08-31

# Drop the threshold, or the deadline — none is case-insensitive here and above
torudo set <id> --threshold none
torudo set <id> --due none

# Attach a URL to an item that already exists — the TUI opens line URLs with `o`
torudo link <id> https://example.com/spec

# Attach several at once, drop one, or swap one for another in a single write
torudo link <id> https://a.example https://b.example
torudo link <id> --remove https://a.example
torudo link <id> https://new.example --remove https://b.example

# Move an item to another mode
torudo promote <id> --to todo

# Complete an item — moves it to done.txt with today's date (Todo / Waiting only)
# A recurring item also gets a "recurrence" field with the next occurrence's id/dates
torudo complete <id>

# Look at the archive, and undo a completion (back into todo unless --to says otherwise)
torudo done list --json
torudo reopen <id>
torudo reopen <id> --to someday

# Search titles, +project / @context tags, and detail-md content across every mode
# (done.txt is never included)
torudo search "release" --json
torudo search "release" --mode todo

# Take the detail md of every hit along with it
torudo search "release" --json --with-md

# Print one item by id as JSON with its detail md, from whatever mode holds it (done.txt included)
torudo show <id>

# Move the running TUI's cursor onto an item — the TUI must already be running
torudo focus <id>

# Chain them: jump straight to the first search hit
torudo search "release" --json | jq -r '.[0].id' | xargs torudo focus

add, set, and promote print the resulting item as JSON in the same format as torudo current; set and promote exit non-zero on an unknown id. When a TUI session is running, the file watcher picks up the change and the affected tab updates automatically.

search matches case-insensitively against the item's title, its +project and @context tags, and the body of its detail md; key:value tags such as id: and time: are not searched. The matched field reports where the query landed — title (title or tags), md, or both.

--with-md on <mode> list, done list, and search adds each item's raw detail md as an md field. It requires --json and is rejected at the argument boundary without it, since a raw md body has no place in the one-line output. An item whose todos/{id}.md is missing — or that has no id: — carries no md key at all rather than a null one, so check for the key. It is opt-in because a whole mode's worth of md bodies is a lot of output when all you wanted was an id; add, set, promote, complete, and reopen include md unconditionally, as they always have. done list --with-md works too: completing an item leaves its detail md in place.

torudo show <id> is the single-item read. It looks the id up across every GTD mode and then done.txt, and prints that one item as JSON with its detail md and a mode field naming where it was found (done for something already completed); an id that is in both a mode file and the archive is reported under the mode it can still be acted on from. The md is always included, so there is no --with-md to pass, and an unknown id exits non-zero. Unlike focus, show only reads files: it needs no running TUI and it honours --todotxt-dir.

search is a stateless CLI command; focus is not — it talks to a running TUI over its RPC socket and moves the cursor there, switching modes and clearing the Now filter automatically if the item is hidden. With no TUI running it prints torudo is not running and exits non-zero. Because the target is whichever session holds the socket, focus ignores --todotxt-dir and acts on the directory that TUI was started with.

Driving torudo from Claude Code

This repository ships an Agent Skill that lets Claude Code run the above for you in plain language — "add buy milk to my inbox", "make it short and low energy, then move it to todo", or a bare URL, in which case Claude resolves a readable title (using a host-specific CLI when one exists, otherwise fetching the page) and confirms the line before adding it. The skill is written to answer to Japanese phrasings as well. It also writes the - [ ] checklist in todos/{id}.md for an item, and can start work on a task — creating the git worktree, opening a terminal tab there, and launching a Claude session for it. That last part additionally needs git-wt and herdr; without them the capture and triage parts still work.

# Install globally
npx skills add maedana/torudo --skill torudo -g

Or copy skills/torudo/SKILL.md to ~/.claude/skills/torudo/SKILL.md by hand.

Updating

# Check for updates
torudo update --check

# Update to latest version
torudo update

# Force re-download
torudo update --force

Keyboard Controls

Press ? in the TUI or run torudo -h to see all keyboard shortcuts.

Todo.txt Format

Torudo supports the standard todo.txt format:

(A) Call Mom +family @phone
x 2024-01-15 2024-01-10 (B) Review quarterly report +work @office
(C) Buy groceries +personal @errands
Learn Rust programming +learning @coding id:abc123

Features supported:

  • Priority levels: (A) through (E)
  • Completion status: x prefix with completion date
  • Creation date: YYYY-MM-DD format
  • Projects: +project_name
  • Contexts: @context_name
  • Unique IDs: id:unique_identifier (automatically added if missing)
  • Key/value tags: key:value pairs (e.g. t:2026-05-30, due:2026-06-01) are parsed into a dedicated field; URLs in the description are not misdetected as tags
  • Recurring completion: rec:1w / rec:+1m — completing the item spawns its next occurrence automatically (see "Recurring Items" below)

Todo Sorting

Todos are automatically sorted within each project column using the following priority:

  1. Priority level: (A) items first, then (B), and so on down to (E)
  2. File line number: Within the same priority level, todos maintain their original file order

This ensures high-priority items are always visible at the top while preserving your intended ordering for items of the same priority.

Display Features

  • Threshold dates (t:YYYY-MM-DD): future items sort to bottom and render dimmed; set or clear one with torudo set <id> --threshold
  • Overdue highlighting (due:YYYY-MM-DD): items past due render with a red border
  • Detail md preview (Todo / Waiting tabs): top 3 unchecked - [ ] from todos/{id}.md shown inline on each card
  • Detail md badge (Todo / Waiting tabs): right-aligned {done}/{total} {elapsed} (e.g. 2/7 5m) on each card; updates live
  • Recurrence chip: ↻1w / ↻+1m next to the GTD chips when rec: is set and its pattern parses
  • Dynamic text wrap with per-item height calculation

Recurring Items

Add rec:<pattern> to an item to spawn its next occurrence automatically when you complete it. A pattern is <count><unit>, where unit is d (day), w (week), m (month), or y (year) — e.g. rec:1w, rec:2m. Prefixing the pattern with + switches what it counts from:

  • rec:1w (no +) — the next occurrence is due one week after the day you actually complete it. Good for "water the plants": however late you are, the next round starts fresh from today.
  • rec:+1m (with +) — the next occurrence is due one month after the item's original due: date, ignoring how late you completed it. Good for "pay rent": the schedule stays fixed even if you complete it a few days late.

A strict (+) pattern that has fallen a long way behind catches up in one go: the pattern is applied repeatedly until the next date lands after the day you completed it, so completing a due:2025-01-01 rec:+1m item on 2026-08-15 gives you 2026-09-01 rather than 2025-02-01. You never have to complete an item nineteen times to work off a backlog, and the schedule still lines up with the original day of the month. (This is a deliberate departure from topydo, which only ever advances one period.)

To stop an item repeating, clear the tag with torudo set <id> --rec none.

Completing a recurring item still moves the original line to done.txt as usual, and also appends a new line to the same mode file: a fresh id:, today as the creation date, t:/due: shifted by the pattern (the gap between them is preserved), and everything else — priority, +project, @context, rec:, time:, energy: — carried over unchanged. If the item has a todos/{id}.md detail file, it is copied to the new id with every - [x] checkbox reset to - [ ] (so a written-out procedure survives, just unchecked) and the frontmatter's branch: line dropped (cwd: is kept). An invalid rec: value never blocks completion — it just skips creating a next occurrence and reports why, both on the TUI status line and (for torudo complete) as a warning on stderr.

Vim Integration

If you have Neovim running with a socket, Torudo can automatically open todo detail files when navigating. Each todo item can have an associated markdown file in $TODOTXT_DIR/todos/{id}.md.

Todo Detail Frontmatter

Todo detail files (todos/{id}.md) support YAML frontmatter with a cwd field to specify the working directory for clp/cli claude launch:

---
cwd: /home/user/src/my-project
---
# Task details here

The cwd field is required for clp/cli — an error is shown if it is not set.

File Structure

Torudo keeps one todo.txt-format file per GTD mode plus a done.txt archive. Each file holds plain todo.txt lines; todos/{id}.md holds optional long-form detail for individual items.

~/todotxt/
├── inbox.txt         # Inbox — capture target (also `torudo inbox add`)
├── todo.txt          # Todo / Next actions (`x` completes here)
├── waiting.txt       # Waiting for (`x` also completes here)
├── ref.txt           # Reference material
├── someday.txt       # Someday / maybe
├── done.txt          # Archive of items completed from todo.txt
└── todos/            # Individual todo detail files
    ├── abc123.md
    └── def456.md

Only todo.txt is created at first launch; the other mode files are created lazily the first time something lands in them (e.g. via the s send-to prefix or torudo inbox add). Completing an item with x works in Todo and Waiting modes (completed items are moved to done.txt); to complete an item from Inbox / Ref / Someday, send it to Todo first with st.

If you prefer the classic todo.txt / done.txt workflow, just stay in Todo mode and ignore the other tabs — none of the GTD mode files are created until you write to them, and every existing key (x, hjkl, o, …) behaves exactly as before. GTD is opt-in, not required.

Development

Running in Development

cargo run

# With debug mode
cargo run -- -d

Running Tests

cargo test

Code Quality

cargo clippy
cargo fmt

Roadmap

Ideas that are on the table but not yet implemented. Order does not imply priority.

  • Show PR status for todos linked to a git working tree (new frontmatter field pointing at the working tree path)
  • torudo w sync subcommand: when invoked from inside a git working tree, automatically fill the currently selected todo's frontmatter with that path (no more hand-editing)

License

This project is licensed under the MIT License - see the LICENSE file for details.

Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Run tests and linting
  5. Submit a pull request

Acknowledgments

  • Inspired by the todo.txt format by Gina Trapani
  • Built with Ratatui for the terminal UI
  • Uses crossterm for cross-platform terminal handling