A terminal interface for managing Markdown-based tasks. Built in Rust on top of mdbase-rs and compatible with TaskNotes collections.
Tasks live as markdown files with YAML frontmatter in your vault. The TUI reads and writes them directly — no database, no sync service.
From source:
cargo install --git <repo-url> --bin tasknotes-tuiPrebuilt binaries for Linux x86_64, macOS (Intel and Apple Silicon), and Windows are attached to GitHub Releases.
Your vault needs an mdbase.yaml and a task type definition. The TUI runs
against mdbase-rs v0.3 and continues to read v0.2 collections, so existing
TaskNotes vaults do not need an in-place rewrite just to use it:
tasknotes-tui --root /path/to/vaultThe TUI reads two config files from the vault root:
tasknotes.yaml— TaskNotes spec config: field mapping, defaults, archive behavior, status values.tasknotes-tui.yaml(optional) — TUI-specific config: keybinds and view slots.
To see the default TUI config:
tasknotes-tui print-default-configNumber keys 1–9 switch between configured view slots. The defaults:
| Key | View | Shows |
|---|---|---|
1 |
Open | Open tasks |
2 |
Date | Tasks for the focused date |
3 |
Overdue | Past-due tasks |
4 |
All | All non-archived tasks |
5 |
Tracked | Tasks with an active timer |
6 |
Archived | Archived tasks |
7 |
Project | Tasks linked to the active project |
A mini calendar sits in the top pane. The highlighted day is the focused date, and days with tasks are marked.
h/lor arrow keys — move by dayPgUp/PgDn— move by weekg— jump to today
When the date view (2) is active, the task list shows tasks for the focused date.
| Key | Action |
|---|---|
x or Space |
Toggle completion |
z |
Archive / restore |
T |
Start / stop time tracking |
S |
Skip / unskip a recurring instance |
P |
Toggle active project on selected task |
n |
Create task (multi-step: title, details, project, dates, priority, status, recurrence) |
c |
Quick create (title only, no dates, linked to active project if set) |
e |
Edit title |
i |
Open in $EDITOR |
d |
Edit due date |
s |
Edit scheduled date |
p |
Edit priority |
t |
Edit status |
R |
Edit recurrence rule |
A |
Edit recurrence anchor |
When editing due or scheduled dates, a date picker opens. If the field doesn't already
have a value, it opens cleared — pressing Enter immediately leaves it unset rather
than silently filling in today's date. The calendar cursor still starts on today, so
picking it is one keystroke away (t, or any arrow key):
- Arrow keys or
h/l— move by day (also picks the highlighted day) j/k— move by weekH/L— move by montht— jump to todayc— clear the value/— switch to manualYYYY-MM-DDentryEnter— save whatever is currently selected (or leave unset if nothing was picked)
Ctrl-P opens a fuzzy-filterable command palette with all available actions.
/ opens live search across tasks.
Shift-P treats the selected task as the active project context. Running it again on the same task clears the active project.
The active project is shown in the State pane. When an active project is set:
- quick create links new tasks to that project via the
projectsfield - the default Project view (
7) shows tasks whoseprojectslinks resolve to the active project - switching views does not clear the active project
An opt-in view kind (kind: "projects", see Configuration below) that lists every distinct
project referenced by a task's projects: field, instead of filtering individual tasks. Each row
shows open/total task counts, an earliest-due/scheduled date, and (if configured) a next-action
indicator. Projects don't need their own note to show up here — a wikilink with no matching file
still gets a row, dimmed and marked (no note), since not every GTD project needs a dedicated
page.
Enter— drill into the selected project's tasks (the normal task list, fully interactive)Esc— back out to the project summary listShift-N— add a next action: a title-only quick-create for a task linked to the highlighted project, with status pre-set to the first configurednext_action_statusesvalue (see Configuration below). Requiresnext_action_statusesto be configured./search filters project rows by title while the list is showing
This is unrelated to the Shift-P active-project mechanism above — it doesn't use or change the
active project, and works the same way for projects with a backing note and projects that only
exist as a wikilink.
Views are configured in tasknotes-tui.yaml under the views key. Built-in view kinds:
all,open,date,overdue,tracked,archivedstatus— filter by a status valueexpression— filter using mdbase expression syntaxprojects— lists distinct projects instead of tasks; see "Projects view" above
views:
8:
label: "Projects"
kind: "projects"Expression views have access to task fields (status, priority, due, scheduled, etc.) and special variables (focusDate, today, isCompleted, isTracked, isArchived, path).
Project-aware expression helpers are also available:
hasActiveProjectactiveProjectPathactiveProjectTitleisActiveProjectprojectPaths— resolved project targets for the current taskisProject— true if this task's own path is itself referenced as a project by some other task'sprojects:field (the project-as-task pattern). Useful for keeping project notes out of actionable-task views, e.g.where: "status == \"next_action\" && !isProject"
views:
6:
label: "Doing Today"
kind: "expression"
expression: 'status == "doing" && (scheduled == focusDate || due == focusDate)'
7:
label: "Project"
kind: "expression"
where: "hasActiveProject && projectPaths.contains(activeProjectPath) && path != activeProjectPath"Every view sorts by scheduled ?? due date by default (sort: date, the default). A view can
opt into Taskwarrior-style urgency sorting instead:
views:
1:
label: "Open"
kind: "open"
sort: "urgency"Urgency is a weighted score computed from due-date proximity, scheduled date, priority, age
(dateCreated), active time-tracking, tags, and project membership — higher means more urgent,
and the score is shown next to the selected task and in the detail pane. Coefficients are
configured under a top-level urgency key (all optional; shown here at their defaults):
urgency:
due: 12.0 # coefficient for the due-date term
due_ramp_days: 14.0 # days out at which the due term starts rising from 0 (overdue = full weight)
scheduled: 5.0 # flat bonus once the scheduled date has arrived
active: 4.0 # bonus while actively time-tracked
age: 2.0 # coefficient for the age term
age_max_days: 365.0 # age (days since dateCreated) at which the age term saturates
project: 1.0 # flat bonus for belonging to any project
priority:
urgent: 9.0
high: 6.0
normal: 1.0
low: -3.0
tags:
next: 15.0 # per-tag bonus; add your own tags hereThe computed score is also available as urgency in expression views, so you can build fully
custom sorted/filtered views without touching sort:, e.g. where: "urgency > 8 && !isCompleted".
Taskwarrior's dependency-based urgency (blocked/blocking/waiting) isn't supported — this
codebase has no dependency/blocking data model yet.
sort: has no effect on kind: "projects" views — project rows are always sorted by title.
The Projects view's next-action indicator is opt-in and vault-specific: it lights up for a
project when one of its open tasks has a status listed in next_action_statuses (top-level
config key, empty by default so the indicator is off until configured):
next_action_statuses:
- next_actionKeybinds map keys to commands. Multiple keys can map to the same command.
keybinds:
ctrl-p: command_palette
n: create_task
c: quick_create_task
shift-p: set_active_project
i: open_in_editor
shift-t: toggle_time_tracking
h: focus_prev_day
l: focus_next_dayedit_status (default t) and edit_priority (default p) open a picker listing the
configured status/priority values (from customStatuses/customPriorities in the
TaskNotes plugin config, or the built-in defaults). Use up/down or j/k to move,
enter to save, or / to fall back to free-text entry for a value not in the list.
The "New Task" flow uses the same pickers for its priority and status steps, so
creating a task no longer requires typing status/priority by hand.
The "New Task" flow's project step works the same way, listing every existing project
(see "Projects view" above) plus a (none) option. Pressing / on that step lets you type
a brand-new project name instead — since projects don't need a backing note, this creates a
phantom project the same way typing an unrecognized wikilink into projects: by hand would.
Leaving the project step on (none) falls back to linking the active project, if one is set
(Shift-P), matching quick create's existing behavior.
To jump straight to a fixed status value without opening any prompt, bind a key to
set_status:<value>:
keybinds:
b: "set_status:next_action"
u: "set_status:inbox"Note: digits 1-9 are reserved for view slot switching and can't be rebound.
Archive behavior is configured in tasknotes.yaml:
archive:
move_on_archive: false
folder: "TaskNotes/Archive"
tag: "archived"
field: "archived"T starts or stops a timer on the selected task. Active timers show in the task list and detail pane. View 5 (Tracked) shows all tasks with a running timer.
Tasks can have recurrence rules (RRULE syntax) with an anchor of either scheduled or completion. S skips the current instance without completing it.
cargo run --bin tasknotes-tui -- --root /path/to/vault
cargo run --bin tasknotes-tui -- --root /path/to/vault --focus-date 2026-03-29# Seed sample data
tasknotes-tui --root docs/demo-vault seed-demo-vault
# Render a snapshot to stdout
tasknotes-tui --root docs/demo-vault --focus-date 2026-03-29 \
render-snapshot --width 120 --height 32The repo includes adapters for running the TaskNotes spec test suite:
npm run conformance:test
npm run conformance:test:rust
npm run conformance:test:reference # against the mdbase-tasknotes reference impl