https://github.com/webstonehq/tuxedo
A fast, keyboard-driven terminal UI for todo.txt.
https://github.com/webstonehq/tuxedo
todo todo-app todotxt tui
Last synced: about 1 month ago
JSON representation
A fast, keyboard-driven terminal UI for todo.txt.
- Host: GitHub
- URL: https://github.com/webstonehq/tuxedo
- Owner: webstonehq
- Created: 2026-05-07T22:16:21.000Z (about 2 months ago)
- Default Branch: main
- Last Pushed: 2026-05-09T07:09:53.000Z (about 1 month ago)
- Last Synced: 2026-05-09T08:43:04.492Z (about 1 month ago)
- Topics: todo, todo-app, todotxt, tui
- Language: Rust
- Homepage:
- Size: 297 KB
- Stars: 4
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
- awesome-github-repos - webstonehq/tuxedo - A fast, keyboard-driven terminal UI for todo.txt. (Rust)
- awesome-ratatui - tuxedo - A fast, keyboard-driven terminal UI for todo.txt. (π» Apps / π Productivity and Utilities)
README
# tuxedo
A fast, keyboard-driven terminal UI for [todo.txt](http://todotxt.org/).
Vim-style bindings, atomic writes, instant external-edit detection, and four
hand-tuned themes β all in a single static binary.
```sh
brew install webstonehq/tap/tuxedo
```
[](https://github.com/webstonehq/tuxedo/actions/workflows/ci.yml)
[](https://github.com/webstonehq/tuxedo/releases/latest)
[](#license)
[](https://www.rust-lang.org)

## Highlights
- **Pure todo.txt.** Reads and writes the [standard format](https://github.com/todotxt/todo.txt) β every line is plain text you can edit with anything else.
- **Natural-language add.** Type prose into the add prompt β `Pay rent monthly on the first, show 3 days before due, project home` β and tuxedo rewrites it to canonical todo.txt for you to review and save. Local, offline, no AI service.
- **Phone capture.** Press `s` for a QR pointing at a tiny PWA on your machine's LAN β type tasks from your phone and they appear in the list. Captures land in a sibling `inbox.txt` first, so any tool that can append a line (shell, iOS Shortcuts, cron) is also a capture source.
- **Vim keys, no surprises.** `j` / `k` to move, `dd` to delete, `gg` / `G` to jump, `u` to undo (50 levels), chord prompts (`gg`, `dd`, `fp`, `fc`) with a 600 ms window.
- **Command palette.** `:` or `Ctrl-P` opens a fuzzy palette over every action β type a few letters, hit Enter. Same matcher as `/` search, ranked so start-of-label hits beat word-boundary hits beat mid-word hits.
- **Atomic, sync-friendly writes.** Every change goes through write-temp-then-rename. If another process β Dropbox, an editor, a script β modifies the file, tuxedo reloads on the next keypress (or within ~250 ms while idle) and flashes a notice.
- **Sibling-file archive.** `A` moves completed tasks to `done.txt` next to your file, atomically.
- **Filter, sort, multi-select.** Cycle by `+project` or `@context`, sort by priority / due / file order, and bulk-complete or bulk-delete in visual mode.
- **Four themes, three densities.** Cycle with `T` and `D`. Choices persist across runs.
- **No daemon, no database, no cloud.** One file in, one file out.
## Screens
| | |
| --- | --- |
| **Empty state** β’ cell-bowtie mark and quick-start when the file has no tasks |  |
| **List** β’ list of todos, optionally grouped |  |
| **Archive** β’ completed tasks grouped by completion date |  |
| **Filter sidebar active** β’ `fp` cycles projects with j/k, `fc` cycles contexts |  |
| **Command palette** β’ `:` or `Ctrl-P` opens a fuzzy palette over every action |  |
| **Help** β’ `?` opens the full keybindings overlay |  |
How to generate the screenshots and demo
The screenshots in the table above are checked-in SVGs. Regenerate them with:
mise run screenshots
The hero GIF at the top is recorded with vhs from docs/demo.tape. Regenerate it with:
mise run demo
## Themes
`T` cycles through four built-in themes.
| Muted Slate (default) | Dawn |
| --- | --- |
|  |  |
| **Nord** | **Matrix** |
|  |  |
## Install
### Homebrew (macOS, Linux)
```sh
brew install webstonehq/tap/tuxedo
```
### Prebuilt binaries
Download the archive for your platform from the [latest release](https://github.com/webstonehq/tuxedo/releases/latest) and put `tuxedo` on your `PATH`.
Targets: `x86_64-unknown-linux-gnu`, `aarch64-unknown-linux-gnu`, `x86_64-apple-darwin`, `aarch64-apple-darwin`, `x86_64-pc-windows-msvc`. Each archive ships with a `.sha256` checksum.
### From source
```sh
cargo install --git https://github.com/webstonehq/tuxedo
```
Or clone and build:
```sh
git clone https://github.com/webstonehq/tuxedo
cd tuxedo
cargo build --release
./target/release/tuxedo [FILE]
```
Requires the Rust 2024 edition (recent stable toolchain).
## Usage
```sh
tuxedo [FILE] # open FILE (created if missing)
tuxedo # open ./todo.txt, or a sample file if none
tuxedo --sample # open the bundled sample file in the temp dir
tuxedo update # print upgrade instructions for your install
tuxedo --help
tuxedo --version
```
When a newer release is available, the status bar shows `β (tuxedo
update)` next to the version. The check runs in the background, is cached at
`$XDG_CACHE_HOME/tuxedo/latest_version.json` for 24 h, and fails silently
when offline. Set `TUXEDO_NO_UPDATE_CHECK=1` to disable.
If `FILE` is omitted, tuxedo opens `./todo.txt` from the current working
directory if it exists. Otherwise it falls back to a sample todo.txt in the
system temp directory so you can poke around without committing to a path.
Edits are persisted on every change via atomic write (write `.tmp`, rename).
If the file changes on disk (another editor, a sync client, a script),
tuxedo notices on the next keypress, or within ~250 ms while idle, and
reloads. The keystroke that triggered the reload is consumed β press it
again to act on the fresh state β and the status bar flashes a notice.
Pressing `A` appends every completed task to a sibling `done.txt` and
removes them from the working file (atomically: `done.txt` is written
before the originals are dropped). `a` toggles the archive view so you
can browse, un-archive, or permanently delete past tasks.
## Keybindings
### Navigation
| Key | Action |
| --- | --- |
| `j` / `β` | next task |
| `k` / `β` | previous task |
| `gg` | first task |
| `G` | last task |
| `Ctrl-d` / `Ctrl-u` | half-page down / up |
### Editing
| Key | Action |
| --- | --- |
| `n` | add task |
| `e` / `i` | edit current task |
| `x` | toggle complete |
| `dd` | delete task |
| `p` | cycle priority A β B β C β Β· |
| `c` | add or remove a context |
| `+` | add a project |
| `yy` | copy current line to clipboard |
| `yb` | copy current body only (no priority, dates, projects, contexts, `key:value`) |
| `u` | undo (50 levels) |
### Filtering, sort, view
| Key | Action |
| --- | --- |
| `/` | search |
| `fp` | filter by project (`j` / `k` cycles, `Esc` clears) |
| `fc` | filter by context (`j` / `k` cycles, `Esc` clears) |
| `S` | cycle sort: priority β due β file order |
| `v` | enter visual / multi-select; `space` toggles a row |
| `x` / `dd` (in visual) | bulk-complete / bulk-delete the selection |
| `l` | list (default) view |
| `a` | toggle archive view |
| `A` | archive completed tasks β `done.txt` |
| `H` | toggle showing done tasks in the main list |
### Layout & theme
| Key | Action |
| --- | --- |
| `[` | toggle filter sidebar |
| `]` | toggle detail sidebar |
| `T` | cycle theme |
| `D` | cycle density: compact β comfortable β cozy |
| `L` | toggle line numbers |
### System
| Key | Action |
| --- | --- |
| `:` / `Ctrl-P` | command palette |
| `s` | share capture QR (phone PWA) |
| `?` | help overlay |
| `,` | settings overlay |
| `q` | quit |
Two-key chord prompts (`gg`, `dd`, `yy`, `yb`, `fp`, `fc`) show a `gβ¦` /
`dβ¦` / `yβ¦` / `fβ¦` indicator in the status-bar mode chip while the
leader is armed; the window is 600 ms.
Copy uses the OSC 52 terminal escape, so it works locally and over SSH on
any terminal that supports it (kitty, alacritty, wezterm, iTerm2, foot,
modern xterm; tmux when `set -g set-clipboard on`). Older terminals will
silently ignore the keystroke.
## todo.txt format
Standard [todo.txt](https://github.com/todotxt/todo.txt) lines:
```
(A) 2026-04-28 Call dentist @phone +health due:2026-05-08
```
- `(A)` β priority, A through Z (omit for none)
- `2026-04-28` β creation date in ISO 8601
- `+project` β project tag
- `@context` β context tag
- `key:value` β extension; `due:YYYY-MM-DD` is recognized for sort and
due-bucket grouping in the list view
- `rec:[+]N{d,b,w,m,y}` β recurrence; on completion (`x`), tuxedo inserts
a fresh copy of the task with `due:` advanced by `N` days, business
days (MonβFri), weeks, months, or years. The `+` prefix means
*strict* recurrence anchored to the previous due date (e.g.
`rec:+1m` for monthly rent on the 15th); without it, the new due is
computed from the completion date (e.g. `rec:1w` for "water plants
one week after I last did").
Completed tasks are prefixed with `x ` and a completion date:
```
x 2026-05-05 2026-05-01 Submit expense report +work
```
Recurring example:
```
2026-05-09 Pay rent due:2026-05-15 rec:+1m
```
Pressing `x` on the line above marks the original complete *and* inserts
`2026-05-09 Pay rent due:2026-06-15 rec:+1m`. `u` undoes both at once.
## Natural-language add
Press `n` to open the add prompt. Type the task in plain English. When the
buffer contains recognized phrases (dates, weekdays, recurrence, project /
context names, priority), pressing Enter rewrites the draft into canonical
todo.txt β review or tweak it, then Enter again to save.
| What you type | What lands in the draft |
| --- | --- |
| `Pay rent monthly on the first of the month, show the todo 3 days before the due date. It's part of project home and context bank` | `Pay rent +home @bank due:2026-06-01 rec:+1m t:-3d` |
| `Buy milk tomorrow` | `Buy milk due:2026-05-12` |
| `Call mom every week starting Friday for project family` | `Call mom +family due:2026-05-15 rec:+1w` |
| `Submit timesheet every other friday show 1 day before` | `Submit timesheet due:2026-05-15 rec:+2w t:-1d` |
| `Daily standup high priority` | `(A) standup rec:+1d` |
| `Annual review April 15 +work @office` | `Annual review +work @office due:2027-04-15` |
Recognized vocabulary:
- **Dates** β `today`, `tonight`, `tomorrow`, `yesterday`, weekdays (`monday` / `mon` β¦), months (`april 15`, `15th of april`), `in 3 days`, `the first of the month`, ISO `2026-05-15`.
- **Recurrence** β `daily`, `weekly`, `biweekly`, `monthly`, `yearly`, `annually`, `every monday`, `every 2 weeks`, `every other friday`, `every business day`.
- **Threshold** β `show 3 days before due`, `2 weeks before due`.
- **Projects / contexts** β prose form `project home` and `context bank`, or the standard `+home` / `@bank` sigils.
- **Priority** β `high priority` β A, `medium priority` β B, `low priority` β C, or `priority A`.
Parsing is rule-based and runs locally β no network calls, no API key. If
the buffer already contains a `due:`, `rec:`, or `t:` token, tuxedo assumes
you've typed canonical form and saves it directly on the first Enter.
## Phone capture
Press `s` to start a tiny capture server on your machine's LAN address and
display a QR code for it. Scan it from your phone β any modern browser β to
get a minimal PWA you can install to your home screen. Type a task, tap
Add, and within a tick it shows up in your task list.
Captures never touch `todo.txt` directly. They land in a sibling
`inbox.txt`, which tuxedo drains on every external-change poll: each line
is run through the same natural-language pipeline as the `n` add prompt,
given a creation date if missing, and merged into `todo.txt` as a single
undoable batch (`u` rolls back the whole drain at once).
That makes `inbox.txt` a general capture endpoint, not just a PWA backend.
Anything that can append a line works as a producer:
```sh
echo "Refill prescription tomorrow" >> ~/notes/inbox.txt
echo "Call dentist due:2026-06-01" >> ~/notes/inbox.txt
```
Shell aliases, iOS Shortcuts writing to a synced folder, cron jobs,
email-to-file gateways β pick your producer. As long as it appends a line
to the sibling `inbox.txt`, tuxedo picks it up.
The server:
- Binds on first `s` press and stays up for the rest of the session.
Subsequent `s` presses just re-show the QR; any key dismisses the
overlay.
- Listens on `0.0.0.0:` so phones on the same WiFi can reach it.
The port is OS-assigned on first use and persisted to `config.toml` so
phone bookmarks survive across sessions.
- Gates every protected route on a 64-character hex token baked into the
URL path. The token is generated once, persisted to `config.toml`, and
compared in constant time.
- Speaks plain HTTP β **trusted networks only.** On a shared or public
WiFi anyone passive-sniffing can recover the token. To rotate, delete
`share_token` from `config.toml` and press `s` again.
Drains from tuxedo-managed producers are crash-safe: the capture server
holds the same advisory lock as the TUI's rename-and-merge, and any
staging file left over from an interrupted drain is replayed on the
next session. Plain shell appends are useful for lightweight capture,
but they do not take that lock; use the capture server or the same lock
if a producer must be serialized with the TUI drain.
## Configuration
Persisted to `${XDG_CONFIG_HOME:-$HOME/.config}/tuxedo/config.toml`. Cycling
theme, density, or sort, and toggling sidebars / line-numbers / done-visibility
all update the file. Unknown keys are ignored, so older binaries don't break
on newer files.
Two additional keys, `share_token` and `share_port`, are written by the
[phone capture](#phone-capture) server on first use. Treat `share_token`
as a secret β anyone who has the value and LAN reach can append to your
inbox. Delete the key from `config.toml` to rotate it on the next `s`
press.
## Development
```sh
mise run fmt # cargo fmt --all
mise run clippy # cargo clippy --all-targets --locked -- -D warnings
mise run test # cargo test --locked
```
CI runs all three on every push and pull request. Tasks are also runnable as
plain `cargo` commands if you don't use [mise](https://mise.jdx.dev/).
## Acknowledgments
- [todo.txt](http://todotxt.org/) by Gina Trapani β the format that makes a tool like this possible.
- [ratatui](https://ratatui.rs/) and [crossterm](https://github.com/crossterm-rs/crossterm) β the rendering and terminal-input crates tuxedo is built on.
## Roadmap
Planned and in-flight work lives in [`todo.txt`](./todo.txt) β eat your own dog food.
## Contributing
Issues and pull requests are welcome. For larger changes, please open an
issue first to discuss the approach. Run `mise run fmt clippy test` (or the
plain cargo equivalents) before submitting.
## License
Released under the [MIT License](https://opensource.org/licenses/MIT).