Your todos, in markdown, done fast.
A fast, single-binary CLI todo manager focused on developer experience. Features vim-style navigation, an interactive TUI, and scriptable commandsβall stored in plain markdown you can version control.
- β‘ Fast - Single binary (4MB), instant startup, 30-40x faster than alternatives
- π Markdown-native - Todos live in
todo.md, version control friendly - β¨οΈ Vim-style navigation -
j/k, relative jumps (5j), number keys - π₯οΈ Interactive TUI - Toggle, create, edit, delete, undo, move, copy
- π― Command Palette - Helix-style
:commands with fuzzy search - π Read-Only Mode - Prevent auto-save, check/uncheck all, filter done
- π§ Scriptable -
list,add,toggle,edit,deletecommands - π Smart Conflict Detection - Auto-merge external changes, reactive file watching
- π Per-File Configuration - YAML frontmatter for file-specific settings
- π Recent Files - Jump to recently opened files with cursor position restoration
- π Cross-platform - macOS, Linux, Windows
brew install niklas-heer/tap/tdxcurl -fsSL https://niklas-heer.github.io/tdx/install.sh | bashDownload the latest binary for your platform from Releases:
tdx-darwin-arm64- macOS Apple Silicontdx-darwin-amd64- macOS Inteltdx-linux-amd64- Linux x64tdx-linux-arm64- Linux ARM64tdx-windows-amd64.exe- Windows x64
Requires Go 1.25+:
git clone https://github.com/niklas-heer/tdx.git
cd tdx
just build
just install# Try it without installing
nix run github:niklas-heer/tdx
# Install to profile
nix profile install github:niklas-heer/tdxLaunch the interactive todo manager:
tdxKeyboard Shortcuts:
| Key | Action |
|---|---|
j / k |
Move down / up |
gg |
Go to first item |
G |
Go to last item |
Space / Enter |
Toggle completion |
n |
New todo after cursor |
N |
New todo at end of file |
e |
Edit todo |
d |
Delete todo |
c |
Copy to clipboard |
m |
Move mode |
Tab |
Indent (nest under previous) |
Shift+Tab |
Outdent (move up one level) |
/ |
Fuzzy search |
t |
Tag filter |
p |
Priority filter |
D |
Due date filter |
r |
Recent files |
: |
Command palette |
u |
Undo |
? |
Help menu |
Esc |
Quit |
Cmd+V / Ctrl+Y |
Paste (in edit mode) |
Command Palette (:):
Press : to open the command palette with fuzzy search. Available commands:
| Command | Description |
|---|---|
check-all |
Mark all todos as complete |
uncheck-all |
Mark all todos as incomplete |
sort-done |
Sort todos by completion (incomplete first) |
sort-priority |
Sort todos by priority (p1 first, then p2, etc.) |
sort-due |
Sort todos by due date (earliest first) |
filter-done |
Toggle showing/hiding completed todos |
filter-due |
Toggle showing only todos with due dates |
filter-overdue |
Toggle showing only overdue todos |
filter-today |
Toggle showing only todos due today |
filter-week |
Toggle showing only todos due this week |
clear-done |
Delete all completed todos |
read-only |
Toggle read-only mode (changes not saved) |
save |
Save current state to file |
force-save |
Force save even if file was modified externally |
reload |
Reload file from disk (discards unsaved changes) |
wrap |
Toggle word wrap for long lines |
line-numbers |
Toggle relative line numbers |
set-max-visible |
Set max visible items for this session |
show-headings |
Toggle displaying markdown headings between tasks |
Read-Only Mode:
Start tdx with -r or --read-only flag for workflows where you don't want changes saved automatically:
tdx -r checklist.mdUse :save to manually save when ready, or :read-only to turn auto-save back on.
Vim-style navigation:
5j- Move down 5 lines3k- Move up 3 linesgg- Jump to first itemG- Jump to last item
Fuzzy Search:
Press / to enter search mode. Type to filter todos with live highlighting. Press Enter to select or Esc to cancel.
Nested Tasks:
Organize your todos hierarchically using Tab and Shift+Tab:
- [ ] Main project
- [ ] Subtask 1
- [ ] Subtask 2
- [ ] Sub-subtask
- [ ] Another task- Press
Tabto indent a task under its previous sibling - Press
Shift+Tabto outdent (move up one level) - Deleting a parent task promotes its children to the parent's level
- New tasks (
n) are created at the same nesting level as the cursor
Tags & Filtering:
Add hashtags to your todos for organization:
- [ ] Fix authentication #urgent #backend
- [ ] Update docs #docs
- [ ] Add dark mode #feature #frontendPress t to open tag filter mode:
- Navigate with
β/βorj/k - Toggle tags with
SpaceorEnter - Clear all filters with
c - Press
Escwhen done
Active tag filters are shown in the status bar. Todos are automatically filtered to show only matching items.
Priorities:
Add priority markers to your todos using !p1, !p2, !p3, etc.:
- [ ] Fix critical security bug !p1
- [ ] Update dependencies !p2
- [ ] Write documentation !p3
- [ ] Refactor code !p2
- [ ] Add nice-to-have featurePriority levels:
!p1- Critical/Urgent (displayed in red)!p2- High priority (displayed in orange)!p3- Medium priority (displayed in yellow)!p4+- Lower priorities (displayed dimmed)
Use the :sort-priority command to sort todos by priority (p1 first, then p2, etc.). Tasks without a priority marker are placed at the end. You can combine priorities with tags: Fix bug !p1 #backend #urgent
Priority Filtering:
Press p to open priority filter mode:
- Navigate with
β/βorj/k - Toggle priorities with
SpaceorEnter - Clear all filters with
c - Press
Escwhen done
Active priority filters are shown in the status bar (e.g., β‘ p1 p2). You can combine priority and tag filters to narrow down your view.
Due Dates:
Add due dates to your todos using @due(YYYY-MM-DD):
- [ ] Submit quarterly report @due(2025-12-01)
- [ ] Review pull request @due(2025-11-30) #code-review
- [ ] Fix critical bug !p1 @due(2025-11-29) #urgent
- [ ] Plan team meeting @due(2025-12-15)Due date display colors based on urgency:
- Overdue - Red (past the due date)
- Due today - Orange
- Due soon - Yellow (within 3 days)
- Future - Dimmed
Use the :sort-due command to sort todos by due date (earliest first). Tasks without a due date are placed at the end. You can combine due dates with priorities and tags.
Due Date Filtering:
Press D (capital D) to open due date filter mode:
- Overdue - Show only overdue tasks
- Today - Show tasks due today
- This Week - Show tasks due within 7 days
- Has Due Date - Show all tasks with any due date
Navigate with β/β or j/k, select with Space or Enter, clear with c, and press Esc when done.
Active due date filters are shown in the status bar (e.g., π
overdue). You can combine due date filters with priority and tag filters.
# List all todos
tdx list
# Add a new todo
tdx add "Buy milk"
# Toggle completion (1-based index)
tdx toggle 1
# Edit a todo
tdx edit 2 "Updated text"
# Delete a todo
tdx delete 3
# Open most recent file
tdx last
# Use custom file
tdx ~/notes/work.md list
tdx project.md add "Task"tdx automatically tracks recently opened files and restores your cursor position when you reopen them.
TUI Mode:
Press r in the TUI to open the recent files overlay:
- Type to filter files by path (fuzzy search)
- Navigate with
β/βorj/k - Press
Enterto open a file - Press
Escorrto close
CLI Commands:
# Open the most recently used file
tdx last
# List recently opened files (sorted by frequency and recency)
tdx recent
# Open a specific recent file by number
tdx recent 1
# Clear recent files history
tdx recent clearFeatures:
- Smart Sorting: Files are ranked by both frequency (how often you open them) and recency (when you last accessed them)
- Cursor Restoration: When you reopen a file, tdx automatically restores your cursor to the last position
- Change Detection: If the file content has changed since your last visit, the cursor resets to the first item for safety
- Configurable Limit: Set maximum recent files in your config (default: 20)
Configuration:
In ~/.config/tdx/config.toml:
[recent]
max_files = 20 # Maximum number of recent files to trackRecent files are stored in ~/.config/tdx/recent.json and include:
- File path
- Last access time
- Access count (frequency)
- Last cursor position
- Content hash (for change detection)
Todos are stored in todo.md using standard Markdown:
# Todos
- [x] Completed task
- [ ] Incomplete task
- [ ] Another task
Other markdown content is preserved.tdx supports three levels of configuration with the following priority:
Priority Order: CLI flags > Frontmatter > Global config > Defaults
Create ~/.config/tdx/config.toml (or $XDG_CONFIG_HOME/tdx/config.toml) to set defaults:
[theme]
name = "tokyo-night"
[display]
check_symbol = "β"
select_marker = "β"
[defaults]
file = "todo.md" # default file (use ~/path for central file)
max_visible = 0 # 0 = unlimited
word_wrap = true
show_headings = false
read_only = false
filter_done = false
[recent]
max_files = 20You only need to include the settings you want to change from the defaults.
Available options:
| Section | Option | Type | Default | Description |
|---|---|---|---|---|
[theme] |
name |
string | "tokyo-night" | Theme to use |
[display] |
check_symbol |
string | "β" | Symbol for completed items |
[display] |
select_marker |
string | "β" | Symbol for selected item |
[defaults] |
file |
string | "todo.md" | Default file path (use ~/path for central file) |
[defaults] |
max_visible |
number | 0 | Limit visible tasks (0 = unlimited) |
[defaults] |
word_wrap |
boolean | true | Enable word wrapping for long lines |
[defaults] |
show_headings |
boolean | false | Show markdown headings between tasks |
[defaults] |
read_only |
boolean | false | Prevent all edits (view-only mode) |
[defaults] |
filter_done |
boolean | false | Hide completed tasks by default |
[recent] |
max_files |
number | 20 | Maximum recent files to track |
Add YAML frontmatter to customize behavior for specific files:
---
read-only: false
max-visible: 10
show-headings: true
---
# Todos
- [ ] Task oneExamples:
Read-only checklist:
---
read-only: true
---
# Shopping List
- [ ] MilkProject tracker with headings:
---
show-headings: true
max-visible: 15
filter-done: true
---
# Project Tasks
## Backend
- [ ] API endpoints
## Frontend
- [ ] UI componentsSettings are applied in this order (highest to lowest priority):
- CLI flags -
tdx -r --show-headings todo.md - Frontmatter - YAML at top of individual todo files
- Global config -
~/.config/tdx/config.toml - Defaults - Built-in defaults (word_wrap: true, others: false/0)
Example:
# config.toml sets word_wrap = false
# Frontmatter sets read-only: true
# CLI flag: --show-headings
# Result: word_wrap=false, read_only=true, show_headings=true
tdx --show-headings todo.mdtdx uses Goldmark (Go's industry-standard Markdown parser) with a custom serializer to provide robust, format-preserving todo management:
Why AST over regex?
- β‘ Performance - Parse once, manipulate efficiently in memory
- π― Precision - Surgical updates to specific nodes without side effects
- π Format Preservation - Maintains your markdown structure, spacing, and formatting
- π Reliability - Correctly handles edge cases (nested lists, code blocks, links, etc.)
- π·οΈ Rich Features - Enables advanced features like tag extraction, heading tracking
How it works:
Read File β Goldmark Parser β AST (in-memory tree)
β
Manipulate nodes
(toggle, add, delete, swap)
β
Custom Serializer β Write File
Implementation Details:
-
Parser (
internal/markdown/ast.go:29)- Uses Goldmark with GitHub Flavored Markdown (GFM) extension
- Parses markdown into an Abstract Syntax Tree
- Each todo becomes a
TaskCheckBoxnode within aListItem - Preserves source bytes with segment pointers for text nodes
-
AST Operations (
internal/markdown/ast.go)ExtractTodos()- Walk AST and collect all task list itemsExtractHeadings()- Find headings and their positions relative to todosToggleTodo()- Flip checkbox state in the ASTUpdateTodoText()- Append new text to source, update segment pointersDeleteTodo()- Remove list item node from parentAddTodo()- Create new list item with checkbox and text nodesSwapTodos()- Reorder list items (handles adjacent and cross-section swaps)
-
Custom Serializer (
internal/markdown/serializer.go:12)- Recursively walks the modified AST
- Reconstructs markdown with proper formatting
- Built custom because Goldmark's renderer had formatting issues
- Handles: headings, lists, checkboxes, code blocks, links, emphasis, strikethrough, etc.
- Preserves spacing and blank lines
Key Benefits:
- β Non-destructive - Your markdown formatting, comments, and structure stay intact
- β Complex markdown - Handles nested lists, code blocks, links, emphasis seamlessly
- β Fast operations - No regex scanning, no full-file rewrites
- β Predictable - AST guarantees correct parsing and serialization
- β Tag support - HashtagExtraction built into AST traversal
- β Heading-aware - Knows which todos belong under which headings
tdx is built for speed with several key optimizations:
- Search debouncing - Search operations are debounced (50ms) to avoid scoring all todos on every keystroke
- Heading caching - Heading positions are cached and only recomputed when todos change
- Zero-allocation navigation - Finding next/previous visible items allocates no memory (~8ns)
- Unified input handling - TUI and piped input share the same handlers, reducing code and bugs
Benchmark results (Apple M4):
FuzzyScore (exact match): 5.6ns/op 0 allocs
FuzzyScore (fuzzy match): 33.4ns/op 0 allocs
Cached headings: 1.0ns/op 0 allocs
Search 100 todos: 9.8Β΅s/op 114 allocs
Navigation (visible todo): 8.0ns/op 0 allocs
tdx/
βββ cmd/tdx/ # Main application
β βββ main.go # Entry point, CLI routing
β βββ config.go # Build-time configuration
β βββ userconfig.go # User configuration (themes, settings)
β βββ *_test.go # Comprehensive test suite
βββ internal/
β βββ markdown/ # AST-based markdown engine
β β βββ parser.go # Markdown β AST
β β βββ ast.go # AST data structures
β β βββ serializer.go # AST β Markdown
β βββ tui/ # Terminal UI (Bubble Tea)
β β βββ model.go # Application state
β β βββ update.go # Event handling
β β βββ view.go # Rendering
β β βββ commands.go # Command palette
β β βββ render.go # Display logic
β β βββ *_test.go # Unit & benchmark tests
β βββ cmd/ # CLI command handlers
β β βββ cli.go # List, add, toggle, etc.
β βββ config/ # Configuration handling
β β βββ config.go # Legacy YAML config (deprecated)
β β βββ recent.go # Recent files tracking
β βββ util/ # Utilities
β βββ text.go # Text processing, fuzzy search
β βββ clipboard.go # Clipboard operations
β βββ text_test.go # Unit & benchmark tests
βββ scripts/ # Development & release tools
- Go 1.25+
- just (command runner)
# Build binary
just build
# Build for all platforms
just build-all
# Install to /usr/local/bin
just installjust build # Build binary
just install # Install to PATH
just tui # Run TUI
just list # List todos
just add "X" # Add todo
just toggle 1 # Toggle todo
just check # Run go vet
just fmt # Format code
just clean # Clean artifactsPress : to open the command palette and select theme to open the theme picker:
- Navigate with
β/βorj/kto preview themes in real-time - Press
Enterto apply and save the theme - Press
Escto cancel and restore the previous theme
The selected theme is automatically saved to your config file.
Set your theme in ~/.config/tdx/config.toml:
[theme]
name = "tokyo-night" # or any builtin/custom themeSee the Global Configuration section for all available settings.
tokyo-night(default)catppuccin-latte(light)catppuccin-frappecatppuccin-macchiatocatppuccin-mochadraculagithub-darkgruvbox-darkmonokainordone-darkrose-pinesolarized-dark
Create your own themes by adding .toml files to ~/.config/tdx/themes/:
# ~/.config/tdx/themes/my-theme.toml
[theme]
name = "my-theme"
author = "Your Name"
[colors]
# Core colors
Base = "#c0caf5" # default foreground
Dim = "#565f89" # muted text
Accent = "#7aa2f7" # highlights, selections
Success = "#9ece6a" # completed items, matches
Warning = "#e0af68" # move mode
Important = "#bb9af7" # checked items
AlertError = "#f7768e" # errors
# Tags (hashtags like #urgent)
Tag = "#e0af68"
# Priorities (!p1, !p2, !p3+)
PriorityHigh = "#f7768e" # !p1 - critical
PriorityMedium = "#bb9af7" # !p2 - high
PriorityLow = "#565f89" # !p3+ - medium/low
# Due dates (@due(YYYY-MM-DD))
DueUrgent = "#7dcfff" # overdue or due today
DueSoon = "#7aa2f7" # due within 3 days
DueFuture = "#565f89" # due laterCustom themes appear in the theme picker alongside builtin themes. You can also override builtin themes by creating a file with the same theme name.
Note: The Tag, Priority*, and Due* colors are optional. If omitted, sensible defaults are used based on the core colors.
tdx ~/notes/work.md # Use specific file
tdx project.md add "Task" # All commands workBuild metadata in tdx.toml:
version = "0.6.0"
description = "your todos, in markdown, done fast"MIT - see LICENSE
