Getting Started

The Todo.txt Format

Todo.txt is a plain-text format for managing tasks. Each line represents one task with an optional structure:

x (A) 2024-01-16 2024-01-15 Description @context +project key:value
│  │       │          │          │           │        │        │
│  │       │          │          │           │        │        └─ metadata
│  │       │          │          │           │        └─ project tag
│  │       │          │          │           └─ context tag
│  │       │          │          └─ description
│  │       │          └─ creation date
│  │       └─ completion date
│  └─ priority (A-Z)
└─ completion marker

All parts are optional except the description.

Parsing Tasks

Use parse_todo to parse a single line and parse_todos for multiple lines:

t = parse_todo("(A) 2024-01-15 Call Mom @phone +Family due:2024-01-20")
Todo: (A) 2024-01-15 Call Mom @phone +Family due:2024-01-20

The parser extracts each component into a dedicated field:

t.completed
false
t.priority
'A': ASCII/Unicode U+0041 (category Lu: Letter, uppercase)
t.creation_date
2024-01-15
t.description
"Call Mom"

Note that description contains only the plain text – contexts, projects, and metadata are stored separately:

t.contexts
1-element Vector{String}:
 "phone"
t.projects
1-element Vector{String}:
 "Family"
t.metadata
Dict{String, String} with 1 entry:
  "due" => "2024-01-20"

Completed Tasks

Completed tasks start with x and may include a completion date:

t = parse_todo("x 2024-01-16 2024-01-15 Pay bills")
t.completed, t.completion_date, t.creation_date
(true, Date("2024-01-16"), Date("2024-01-15"))

Constructing Tasks

The Todo constructor accepts a description string and keyword arguments. Tags embedded in the description are automatically extracted:

t = Todo("Buy groceries @store +Errands"; priority='B', creation_date=Date(2024, 1, 15))
Todo: (B) 2024-01-15 Buy groceries @store +Errands
t.description  # tags have been stripped
"Buy groceries"
t.contexts
1-element Vector{String}:
 "store"

If you pass explicit contexts, projects, or metadata keyword arguments, they override extraction from the description:

t = Todo("Call Mom @phone"; contexts=["work"])
t.contexts  # uses the explicit value, not "phone"
1-element Vector{String}:
 "work"

Writing Tasks

write_todo serializes a Todo back to a single Todo.txt line. Tags are written in canonical order: description, contexts (@), projects (+), then metadata (key:value sorted by key).

t = Todo("Call Mom @phone +Family"; priority='A', creation_date=Date(2024, 1, 15))
write_todo(t)
"(A) 2024-01-15 Call Mom @phone +Family"

write_todos joins multiple tasks with newlines:

todos = [
    Todo("Call Mom"; priority='A'),
    Todo("Buy groceries @store +Errands"; priority='B'),
]
print(write_todos(todos))
(A) Call Mom
(B) Buy groceries @store +Errands

File I/O

Read and write Todo.txt files directly:

# Write to a file
todos = [
    Todo("Call Mom @phone +Family"; priority='A', creation_date=Date(2024, 1, 15)),
    Todo("Pay bills"; completed=true, completion_date=Date(2024, 1, 16)),
    Todo("Buy groceries @store +Errands"),
]

path = tempname()
write_todos(path, todos)

# Read it back
loaded = read_todos(path)
loaded == todos
true

Roundtrip Fidelity

Parsing and writing are inverse operations – a roundtrip preserves all data:

line = "(B) 2024-01-15 Buy groceries @store +Errands"
write_todo(parse_todo(line)) == line
true
Note

Tags in the original line may be reordered to canonical order (contexts, then projects, then sorted metadata). If the original line already follows this order, the roundtrip produces an identical string.