Local-first · plain text · terminal

Tasks that outlive the tracker.

Farora is a task manager for the terminal whose storage format is a folder of markdown files. Nest as deep as you like, keep FAR-42 meaningful forever, and read the whole thing with cat long after Farora is gone.

$ uvx farora
v0.5.0 on PyPI macOS · Python 3.14+
The Farora TUI: a collapsible task forest on the left, the selected task's rendered markdown body and activity log on the right, key hints along the bottom.

The whole application. Forest left, detail right, the keymap along the bottom edge.

Storage

The file is the task.

There is no database and no export step. tasks/FAR-42.md is what helix edits, what the detail pane renders, what git diffs. Frontmatter holds the metadata; everything under it is yours.

Hand-edit a file and Farora journals what changed. Break the YAML and it offers to rebuild the frontmatter from the log without touching a word of your prose. Every write is atomic and lands in exactly one small file, which is what keeps a sync conflict small enough to merge.

~/.farora/projects/FAR/tasks/FAR-4.md
---
schema: 1
uid: 01a082ae-ee57-74cc-9aa4-a67f18bebc7e
id: FAR-4
title: Journal & tombstones
status: ACTIVE
doby: 2026-09-11
parent: FAR-2
rank: "a0"
created_at: 2026-09-08T09:41:52.318Z
updated_at: 2026-09-08T10:02:11.007Z
---

Every operation on a task appends one line to
`journal/FAR-4.jsonl`.

> **note 2026-09-08T10:02Z** — retention defaults to 30d
the repository is a folder
~/.farora/
├── farora.toml
└── projects/
    └── FAR/
        ├── project.toml
        ├── counter
        ├── tasks/
        │   ├── FAR-2.md
        │   └── FAR-4.md
        └── journal/
            ├── _project.jsonl
            ├── FAR-2.jsonl
            └── FAR-4.jsonl

Interaction

One keystroke, no forms.

Vim keys, every one dual-bound to its arrow. Creating, renaming, re-statusing and re-ranking are a single press each, applied instantly — there is no save step anywhere in Farora. Everything else is one line at the foot of the pane, or one small dialog over a tree you can still read.

Press a key, or pick one: / b m ,

A modal over the Farora tree holding a YYYY-MM-DD field and a September 2026 month grid, with the 29th selected.

A deadline — a doby — is a typed date and a month grid over the same value. The tree grows a days-remaining column; x inside the dialog clears the date again.

A fuzzy picker titled 'New parent of FAR-4', listing ROOT and every task in the project, with the done ones struck through.

Reparent through a fuzzy picker that excludes the task's own descendants. Subtrees move intact, one file is rewritten, and the id never changes.

The Git tracking and sync screen: a remote URL field, a push-cadence selector, Save / Sync now / Disable buttons, and a table of recent git operations.

Git tracking, configured in place: a remote, a push cadence, and the last hundred operations with what each one actually did.

01

Ids that still mean something next year

FAR-42 is allocated once and never reused — not even after the task is deleted. y yanks it to the clipboard, which is where a commit message wants it.

02

Nothing changes quietly

Every operation appends one line to the task's own journal. u undoes the last one. A delete leaves a full snapshot behind — which is what undo restores from, and what stays readable in the folder until gc collects it.

03

Sync is a folder problem

Point the built-in git tracking at a remote, or let Dropbox, iCloud or Syncthing carry the directory. Writes are atomic and confined to one small file, and farora doctor reconciles whatever is left over.

04

No daemon, no background anything

Expired tombstones are swept during ordinary runs — nothing is scheduled and nothing runs while you are not. farora gc --older-than 7d if you would rather not wait.

The whole surface

Forty keys, and that is the product.

Capitals trend toward the bigger or more destructive variant. d is deliberately left unbound, so done and delete can never be confused. ? shows this map inside the app.

Navigate

j / k, ↓ / ↑move the cursor
gg / G, Home / Endfirst / last row
PgUp / PgDna screen at a time
h / l, ← / →collapse / expand (ranger-style)
ztoggle fold
Enterfocus the detail pane
Tabcycle focus tree ↔ detail

Create

anew sibling below
Anew subtask
Onew sibling above

Edit & view

eedit the body in hx, side-by-side
vlive rendered view, side-by-side
Vfull-screen rendered view
rrename the title
nquick note onto the body
bset the doby (deadline), or clear it

Status

Spacecycle NEW → ACTIVE → DONE
sstatus picker, custom statuses too

Delete

xdelete, recursively — asks first

Move

J / K, Shift-↓ / Shift-↑reorder within the parent
>demote — child of the previous sibling
<promote — sibling after the parent
mreparent, via a fuzzy picker
Mmove to another parent or project
ccopy the item alone
Ccopy with its subtree

Filter

/filter by id + title + body
Ctrl-Nsearch ids on / off
Ctrl-Tsearch titles on / off
Ctrl-Bsearch bodies on / off
fwhich statuses are shown
Escclear the filter · back out of a modal

Misc

yyank the id (FAR-42) to the clipboard
uundo the last operation
Rrepair broken metadata from the log
pprojects: show, create, rename
Pcreate a project
,git tracking & sync
?this help
qquit
Ctrl-Ccopy the selected text

Getting it

Two commands, one of them optional.

Farora 0.5 is developed and tested on macOS with Python 3.14 or newer; a recent uv provisions the interpreter for you. Linux and Windows are not supported in this release.

uvx farora              # run it without installing
uv tool install farora  # keep it

Optional, each unlocking one thing

helix e opens the task body in helix — otherwise $EDITOR, then vi
mdcat ≥ 2.11 v / V render the body live, with inline terminal images
WezTerm editor and viewer open in a split pane beside the tree, rather than suspending it
git ≥ 2.30 sync a task repo between machines from the , screen
nothing at all Farora degrades cleanly and says which key it had to fall back on

Commands

farora launch the TUI
farora doctor validate and reconcile a repo
farora gc purge expired tombstones now
--repo PATH accepted by all three; defaults to $FARORA_REPO, else ~/.farora