pTask has two parsers: quick-add (composing a new task in one shot)
and the filter DSL (selecting rows from tasks for pt list).
Free text with inline tokens. The non-token remainder is the title;
trailing //description becomes the description.
| Token | Effect |
|---|---|
p1..p5 |
priority, native scale: p1=low, p2=normal, p3=high, p4=urgent, p5=critical. Identical to --priority and pt priority — no Todoist inversion. |
@label |
append to pt_extensions.labels JSON. Multiple allowed; surrounding prose punctuation is dropped (@bob, and @(bob) → bob). |
#project |
pt_extensions.project (last wins). An all-digit #42 is an issue reference and stays title text. |
~30m, ~2h, ~1d |
pt_extensions.duration_min (a positive whole number of units). |
!HH:MM |
reminder time of day (a valid HH:MM only). Echoed by pt add; not persisted. |
//description |
everything from // to end-of-text → description. |
every monday, every weekday, every! 5 days, etc. |
recurrence — see recurrence.md. |
YYYY-MM-DD |
deadline, when it is a standalone date after today (today's date is usually provenance, e.g. "discovered 2026-08-01"; use --deadline). Stored date-only: due all day, not overdue until the day has passed. |
due:date |
scheduled date (due_at): when you plan to do it, distinct from the deadline. One word: due:2026-10-02, due:tomorrow. |
Body-text deadline inference is deliberately narrow: only a standalone
future ISO date (2030-03-01) sets the deadline. Other date prose
(tomorrow, next friday, 4/5, a past date) stays title text instead of
silently setting a wrong deadline; pass --deadline for anything else.
Recurrence phrases (every monday at 9am) set the first occurrence, unless
the text also carries an explicit ISO date: that date is the first deadline
wherever it appears (at the rule's at <time> if it has one, and anchoring the
cadence: 2027-01-31 every month at 9:00 runs Jan 31, Feb 28, Mar 31 at
09:00), and is never folded into the rule.
Operator timezone: Europe/London (DST-correct via jiff).
pt add 'gym @health every! monday at 8am p2 ~45m'
pt add 'renew passport 2030-03-01 @home p2 ~1h //book the appointment early'
pt add 'investigate ceph mon quorum @ops p4 #fleet'
pt add 'review the sync PR @review #ptask //gh pr view 42'
Boolean expressions over field tokens. Status is not a token: use
pt list -s/--status (or status= on GET /list).
A "day" is a calendar day in the operator timezone (Europe/London). A
datetime deadline with a Z or ±HH:MM offset compares as an instant
against that day's local start and end (2026-10-20T23:30:00Z is 21 Oct in
BST). A date-only deadline (2026-10-02), and any older row stored without
such an offset (naive 2026-10-02T23:30, +0100, [Europe/London]),
compares by the date written at its start. A deadline with no readable date
matches no day.
| Token | Predicate |
|---|---|
today |
deadline on today, or scheduled (due_at, quick-add due:) for today |
tomorrow, yesterday |
deadline on that day |
overdue |
not done or dismissed, and a date-only deadline before today, a datetime deadline before now, or a deadline that can't be read as a date |
no date (alias no deadline) |
no deadline |
recurring |
row has a pt_recurrence entry |
p1..p5 |
exact priority match |
@label |
label in pt_extensions.labels; the name runs to the next space or operator (@domain:mgmt, @v1.2) |
#project |
exact project match; same name rule (#infra/core) |
due:phrase |
deadline on the day the phrase resolves to |
due before:phrase |
deadline before that day |
due after:phrase |
deadline after that day |
search:str |
str in the title or description (case-insensitive); an empty str is an error |
kind:scout|ship |
investigation vs implementation (see pt promote) |
| Op | Form |
|---|---|
| AND | a & b (binds tighter than OR; juxtaposition a b is an error) |
| OR | `a |
| NOT | !a — true whenever a is not (a task with no project matches !#fleet) |
| group | `(a |
Limits: at most 2048 bytes, 32 levels of ( / ! nesting and 256 terms.
Not supported; the parser rejects them: created:, status: (filter by
status with pt list -s STATE), and whitespace between terms as an
implicit AND (join terms with &).
pt list 'today & p1'
pt list '(today | overdue) & #fleet'
pt list '@waiting & no date'
pt list 'due before: next friday & !recurring'
pt list 'search: ceph & @ops'
pt list 'kind: scout & p4'
Use single quotes around the whole DSL string in shells — &, |,
! are interpreted by the shell otherwise.