Capture syntax¶
bob capture reads one draft and writes tasks, bullets, reference jobs, or
Pomodoro session changes. This page is the token lookup. The full grammar and
refusal messages live in the Capture contract.
Examples below were captured from this checkout's bob in an isolated
HOME/XDG_* tree, a synthetic vault, BOB_NOW=2026-10-20 12:00:00,
TZ=UTC, and BOB_REF_JOBS_KICK=off. Copy the bundled demo with the
first Bob day tutorial when you need that
fixture. Session examples need today's daily note with an open
## Pomodoros entry. Dependency writes need Obsidian Tasks settings that
define Blocked as ?. Quote tokens in Zsh (*, !, ?, and # glob).
Zsh's EQUALS expansion rewrites a word that starts with =, so bob capture =focus fails unquoted: always single-quote ('=focus', 'x =focus').
Network, clipboard, sync, and hook execution are not used here.
Token index¶
Each row is a distinct operation. Equivalent spellings share a heading. Incomplete picker input is labeled picker and is refused if you submit it.
| Token | Meaning | Heading |
|---|---|---|
| Bare public URL | Queue a reference job under mac_inbox |
Bare public URL |
URL @route / @route URL |
Queue the reference under that route | URL @route |
URL % / URL % @route |
Queue with companion audio | URL % |
-r route |
Force the URL (or task) route from the CLI | -r |
@@route |
Shared task destination for unrouted items | @@route |
@@route+block-id |
Shared parent-task destination | @@route+block-id |
@@template: |
Expand one capture per template row | @@template: |
@route |
Write a task to <route>.md |
@route |
@route#Section |
Ordinary bullet under a matching heading | @route#Section |
@route# |
Ordinary bullet under any non-Tasks heading |
@route# |
@route^block-id |
New open task with a user-authored block ID | @route^block-id |
@route:block-id |
New Next task plus a Pomodoro Task Link | @route:block-id |
@route:block-id#pomodoro |
Same, under a named Pomodoro | @route:block-id#pomodoro |
@route:block-id=<X> / @route:block-id=name |
Same, and start that session (@work:outline=bugs links under BUGS and starts it) |
@route:block-id=<X> |
@route:block-id with no other text |
Link an existing task into today's ledger | Link an existing task |
^route:block-id |
Active-task spelling of a link | ^route:block-id |
:<query> |
Picker: choose any open task to link | :<query> |
/<query> |
Picker: choose any open task to open | /<query> |
+query |
Picker: choose a parent task | +query |
lone + |
Editor parent picker; Escape leaves +5m unchanged |
lone + |
¬e:block-id |
Captured task depends on that prerequisite | ¬e:block-id |
& / &query |
Picker: choose a prerequisite | & |
^route:block-id ¬e:id |
Add a prerequisite to an existing owner | Add a prerequisite |
*note:block-id |
Named open task depends on the captured task | *note:block-id |
*query |
Picker: choose a blocked task | *query |
^route:block-id *note:id |
Existing owner blocks an existing task | Block from an existing owner |
~note:block-id |
This task links to that task, with an optional label | ~note:block-id |
~note:block-id\|\|label |
Same link; the label hides from suggestions once the capture succeeds (a one-off) | ~note:block-id |
~~note:block-id |
That task links to this task, with an optional label | ~~note:block-id |
^route:block-id ~note:id |
Relate an existing owner | Relate an existing owner |
! / !query |
Picker: choose a task to complete | ! |
!note:block-id |
Complete that existing open task | !note:block-id |
+[N] |
Extend today's current timed Pomodoro | +[N] |
-[N] |
Shorten today's current timed Pomodoro | -[N] |
++[N] |
Shift the running session later | ++[N] |
--[N] |
Shift the running session earlier | --[N] |
= / =<X> |
Start the next future Pomodoro, or restart | = |
=name |
Start a named Pomodoro (=bugs, same token as =#bugs) |
=name |
~<K> |
Drop those queued Task Links from the start | ~<K> |
x |
Close today's running timed Pomodoro | x |
x!<M> |
Close, completing those Task Links | x!<M> |
x*<P> |
Close, parking those Task Links | x*<P> |
x~<K> |
Close, dropping those Task Links | x~<K> |
| Work Log | Log work onto the close's worked tasks | Work Log |
+2 x / x = |
Chain session operators on one line | Operator chain |
@route:block-id=x |
Link that task, then close | @route:block-id=x |
@route^block-id+ |
Create a project note | @route^block-id+ |
:id |
Project-note child: name, Next, and link | :id child |
^id |
Project-note child: name only | ^id child |
@route+block-id |
Child bullet under an existing task | @route+block-id |
@route+block-id#section |
Child under that task's ALL-CAPS section | @route+block-id#section |
@route+block-id with no other text |
Ensure Next and move its Task Link | Ensure Next |
@route+block-id! |
Toggle that task's Pomodoro Task Link | @route+block-id! |
trailing # |
Plain-text note on the current Pomodoro | trailing # |
s:<N> |
Schedule N days from today; checkbox tasks start Blocked | s:<N> |
$ / $<N> |
Write priority N and roll a scheduled date | $ |
% / %N / %header |
Capture clipboard content as child bullets | % |
Overloaded markers¶
These characters mean different things in different positions. Read them by what they attach to, not as one global operator.
| Marker | Context | Meaning |
|---|---|---|
+ |
+[N] alone |
Adjust the current Pomodoro |
+ |
++[N] alone |
Shift the running session later |
+ |
+query / lone + |
Parent-task picker |
+ |
@route+block-id |
Child under an existing task, or Ensure Next when the item has no other text |
+ |
@route^block-id+ |
Create a project note |
+ |
after #name |
Part of the Pomodoro name (BUGS+) |
# |
trailing bare # |
Pomodoro note |
# |
@route#Section |
Heading in that note |
# |
@route+id#section with body text |
ALL-CAPS task section |
# |
@route:id#pomodoro / =name |
Named Pomodoro (=bugs and =#bugs are one token) |
@ |
@route… |
Route / destination token |
@ |
@@route |
Draft-wide destination |
@ |
x@<N> / =name@<X> |
Timing or keep-list typed last (=bugs@3 means =3#bugs) |
@ |
URL @route |
Explicit reference parent |
* |
*note:block-id |
Blocked-task (star) link |
* |
x* |
Park Task Links on close |
* |
line-initial bare * |
Never a blocked-task marker |
~ |
~note:block-id[\|"label"], ~note:block-id\|\|"label" |
Relation link (~); || hides the label from suggestions once the capture succeeds |
~ |
x~ / =~ |
Drop Task Links on close / drop on start |
~ |
~/path, ~5m, mid-line ~x:y |
Never a relation marker |
~~ |
~~note:block-id[\|"label"] |
Reverse relation link (~~) |
~~ |
~~old~~ |
Strikethrough, never a relation marker |
! |
!note:block-id |
Complete an existing task |
! |
x! |
Complete Task Links on close |
% |
URL % |
Request companion audio |
% |
trailing % |
Clipboard capture |
Contract: Grammar at a glance.
Mnemonic: ~ on its own ties two tasks; ~ inside x / = drops.
= then a word starts that Pomodoro; # only after a number. =bugs and =#bugs are one token; write # only when a number comes first (=3#bugs) or the name starts with a digit or reads as a close (=#2fa, =#x2).
Incomplete input versus executable syntax¶
A picker token is a draft state, not a capture. bob capture and
bob capture-parse refuse it with a teaching error until the selection
becomes a concrete @route:id, !note:id, ¬e:id, *note:id,
~note:id, ~~note:id, or
@route+id. Session tokens that still need a number or name (x~, =~, x@,
=#) are the same kind of incomplete input. A bare =name start is complete (=bugs); only the empty =# needs pomodoro_name.
A signed count with extra prose (+2 more, =3 more) is an error, not a
task. A sign or = followed by prose (Plan +5, = foo) stays ordinary
task text. Capital X never closes; X *job:cleanup is how you capture a
task that must start with the word x.
Retired and rejected forms¶
These are not valid commands. Bob teaches the replacement.
| Typed | What happens |
|---|---|
==, ==<X>, ==#name |
Retired restart/swap; write one = (=, =3, =bugs) |
==x |
Error: close with x |
@route:id+ / @route:id+#pomodoro |
Retired project-note forms; write @route^id+ |
@route::id |
Retired ordinary block-ID form; write @route^id |
#now / a free #tag next to @route |
Retired; heading and Pomodoro # attach to the route token |
x#bugs |
Error: x always closes the running session; write x =bugs to close then start |
Contract: Retired close spellings.
Routes and destinations¶
@route¶
Write a #task into <route>.md at the vault root. Route names are
A–Z a–z 0–9 _ -, lower-cased. The default without a route is mac_inbox.
bob capture 'Review the queue input'
✓ captured mac_inbox.md
- [ ] #task Review the queue input [created::2026-10-20]
Contract: Routing and insertion.
@@route¶
A draft-wide destination for otherwise-unrouted items. It may sit anywhere.
A plain @@route also routes default (routeless) refs as source global.
@@route+block-id keeps refs as tasks.
bob capture 'Buy milk @@work'
Contract: Global destination declaration.
@@route+block-id¶
Same declaration, but unrouted items become direct children of that existing task.
bob capture 'Check the parser @@work+prep'
✓ captured work.md
under [ ] Prep the intake ^prep
- Check the parser
Contract: Global destination declaration.
@route#Section¶
Write an ordinary bullet (no checkbox) into a matching non-Tasks heading.
bob capture 'Remember the timeout @notes#Ideas'
✓ captured notes.md
- Remember the timeout [created::2026-10-20]
Contract: Section bullets.
@route#¶
Same, targeting any non-Tasks heading. Generated status-group headings
under Tasks are never bullet targets.
bob capture 'Remember the timeout @notes#'
✓ captured notes.md
- Remember the timeout [created::2026-10-20]
Contract: Section bullets.
-r / -r route¶
--route NAME forces NAME.md and keeps typed @tokens literal. On a bare
URL it queues the reference under that route with source explicit.
bob capture -r work -- 'https://example.com/article'
Contract: Command-line options and Saving links.
URL routing and audio¶
Bare public URL¶
When the URL routing policy admits it, a bare public URL queues a background
reference job under mac_inbox. The confirmation says reading queue; the
job files the note later. -R, --no-ref keeps it a task.
bob capture --dry-run 'https://example.com/article'
[dry-run] ok would queue example.com/article → mac_inbox
new to your library · reading task lands in mac_inbox.md · file it later
bob capture --dry-run --no-ref 'https://example.com/article'
[dry-run] ok would capture mac_inbox.md
- [ ] #task https://example.com/article [created::2026-10-20]
Contract: Saving links to your reading queue.
URL @route¶
Queue the reference under that route (aliases resolve; source explicit).
@route URL is the same.
bob capture --dry-run 'https://example.com/article @work'
[dry-run] ok would queue example.com/article → work
new to your library · reading task lands in work.md · file it later
Contract: Saving links to your reading queue.
URL %¶
Queue the reference with companion audio (ref.listen true). Parent
selection matches a bare URL: default, @@route global, or -r. Exact
standalone @ remains a compatible listen alias. URL % @route / @route URL %
sets an explicit parent.
bob capture --dry-run 'https://example.com/article % @work'
[dry-run] ok would queue example.com/article → work
new to your library · reading task lands in work.md · file it later · listen
Contract: Saving links to your reading queue.
Templates¶
@@template:¶
The first line is @@template:. The following {N} template captures
exactly like its expansion: one item per later nonblank row ({0}-only
templates can pack several values on one line). -E, --expand prints the
expansion and exits before vault access.
printf '%s\n' '@@template:' 'Call {0} @work' '' 'Alex' 'Sam' | bob capture -E
Call Alex @work
Call Sam @work
A successful real @@template: capture saves the template block (not the
rows) on the shelf. Manage it with bob capture-templates.
Editors that run bob capture-rewrite,
including the Mac capture panel, turn a typed @@@ at the very start of a
draft into the @@template: header and put the caret on the next line.
Contract: Bulk templates.
Block IDs and linked tasks¶
@route^block-id¶
Create an ordinary open task with that block ID.
bob capture 'Write an outline @work^outline'
✓ captured work.md
- [ ] #task Write an outline [created::2026-10-20] ^outline
Contract: Task with a requested block ID.
@route:block-id¶
Create a Next ([*]) task and a Pomodoro Task Link. A future s:<N>
(N ≥ 1) starts Blocked ([?]) and unlinked.
bob capture 'Write an outline @work:outline'
Contract: Pomodoro-linked tasks.
@route:block-id#pomodoro¶
Same, linked under a matching named open Pomodoro, or a new named future Pomodoro.
bob capture 'Write an outline @work:outline#focus'
✓ captured work.md
- [*] #task Write an outline [created::2026-10-20] ^outline
Linked under FOCUS
Contract: Pomodoro-linked tasks.
@route:block-id=<X> / @route:block-id=name¶
Same, and atomically start the selected session. <X> uses five-minute units:
=3 means 15 minutes; empty = means 25 minutes. A bare suffix name
(@work:outline=bugs) means @work:outline#bugs=.
bob capture '@work:outline=3'
bob capture '@work:outline=bugs'
Suffix on @work:outline or ^work:outline |
Session and timing |
|---|---|
=focus@3 |
FOCUS, 15 minutes; equivalent to =3#focus or #focus=3 |
=focus@-2 |
FOCUS, 25 minutes; start is now minus 10 minutes, rounded up to a five-minute boundary |
=#2fa |
A name starting with a digit, using the explicit # form |
=focus@ |
Incomplete: timing is still needed |
=3#focus@2 |
Rejected: timing appears twice |
A link-and-start fills an open () placeholder or creates a started session.
It refuses while any timed Pomodoro in today's daily note is open. The whole-item =name
start has separate restart and swap behavior.
Contract: Starting the session atomically.
@route:block-id with no other text¶
Link the existing ^block-id task in route.md into today's ledger. No new
task is created. A task Blocked only by its own prerequisites drops those
links first, then the gesture runs. =<X> starts the resolved session.
bob capture '@work:outline'
✓ link work.md
[ ] → [*] Write an outline ^outline
Linked under WORK
The Task Link is a dedicated child under the open Pomodoro:
- [ ] () — WORK
- [[work#^outline]]
Contract: Linking and starting existing tasks.
^route:block-id¶
Identical execution to the @ link forms. ^ is the active-task spelling
and completes In Progress and Next tasks when that is the gesture.
bob capture '^work:outline'
✓ link work.md
[ ] → [*] Write an outline ^outline
Linked under WORK
Contract: Linking and starting existing tasks.
Task pickers¶
:<query>¶
Incomplete: pick any open task to link. capture-parse needs task_link.
Accepting inserts @route:block-id. Execution never captures the query.
bob capture ':queue'
bob capture: capture item 1 starting on line 1: `:queue` opens the task picker; pick a task to insert its `@<route>:<block-id>` link, or write the link yourself (for example `@sase:deep-fix`)
Contract: Picking any open task with ':'.
/<query>¶
Incomplete: pick any open task to open in Obsidian. capture-parse needs
task_goto. Accepting opens the task; execution never captures it.
bob capture '/queue'
bob capture: capture item 1 starting on line 1: `/queue` opens the Go To picker; pick a task to open it in Obsidian — it never captures
Contract: Going to a task with '/'.
+query¶
Incomplete: choose a parent task. capture-parse needs task_parent.
Accepting inserts @route+block-id. Terminal prose + is the same picker.
bob capture-parse -f json -- '+prep'
{"ok":true,"schema_version":1,"mode":"incomplete","needs":["task_parent"],"body":""}
This is a draft state, not a successful capture.
Contract: Sub-bullets under existing tasks.
lone +¶
Opens the same parent picker in an editor. Escape leaves the valid +5m
Pomodoro adjustment unchanged. A submitted lone + is the five-minute
adjustment, not this picker.
bob capture --dry-run '+'
[dry-run] ok would adjust 2026/20261020.md
WORK 1200-1215 (15m) to 1200-1220 (20m), +5m at line 8
Contract: Grammar at a glance.
Prerequisites and blocked-task links¶
¬e:block-id¶
The captured (or explicitly selected) task depends on note.md's
^block-id task. Nested notes use &projects/foo:bar; notes with spaces use
&"Shopping List":bar.
bob capture 'Review the intake queue @work^queue &work:prep'
✓ captured work.md
- [?] #task Review the intake queue [created::2026-10-20] [id:: work__queue] [dependsOn:: work__prep] ^queue
⛓ depends on "Prep the intake" work.md ^prep [ ] Ready
The source of truth is the managed Depends-On child, not the field:
- [?] #task Review the intake queue [created::2026-10-20] [id:: work__queue] [dependsOn:: work__prep] ^queue
- ⛓️ **DEPENDS ON:** [[#^prep]]
A selected ¬e:block-id with no owner is incomplete
(dependency_target): type task text or + to choose an existing owner.
Contract: Adding prerequisites with '&'.
& / &query¶
Incomplete: the prerequisite picker is still open (task_dependency).
&query and ¬e: are the same incomplete family.
bob capture-parse -f json -- '&prep'
{"ok":true,"schema_version":1,"mode":"incomplete","needs":["task_dependency"]}
This is a draft state, not a successful capture.
Contract: Adding prerequisites with '&'.
^route:block-id ¬e:id¶
Add a prerequisite to an existing open owner. ^route:id is a bare owner
alias here and does not create a Pomodoro link. ¬e:id ^route:block-id
is the same.
bob capture '^work:queue &work:prep'
✓ related work.md
[ ] → [?] Review the intake queue ^queue
⛓ depends on "Prep the intake" work.md ^prep [ ] Ready
Contract: Relating existing tasks.
*note:block-id¶
The named open task depends on the captured (or explicitly selected) task.
Quote * on the shell. Nested notes and quoted names match &.
bob capture 'Ask for the dump @work^dump *work:queue'
✓ captured work.md
- [ ] #task Ask for the dump [created::2026-10-20] [id:: work__dump] ^dump
🧱 blocks "Review the intake queue" work.md ^queue [?] Blocked
A line-initial bare * is never this marker. A selected star with no owner
is incomplete (dependency_target).
Contract: Blocking existing tasks with '*'.
*query¶
Incomplete: the blocked-task picker is still open (blocked_task).
X * and X *note: are the same family after other text on the line.
bob capture-parse -f json -- 'Ask for the dump *queue'
{"ok":true,"schema_version":1,"mode":"incomplete","needs":["blocked_task"]}
This is a draft state, not a successful capture.
Contract: Blocking existing tasks with '*'.
^route:block-id *note:id¶
Make the named open task depend on the existing owner. ^route:id is a bare
owner alias and does not create a Pomodoro link.
bob capture '^work:dump *work:queue'
✓ related work.md
🧱 blocks "Review the intake queue" work.md ^queue [?] Blocked
Contract: Relating existing tasks.
Relation links¶
~note:block-id[|"label"]¶
The captured (or explicitly selected) task links to note.md's
^block-id task, with an optional source-side label. Nested notes use
~projects/foo:bar; notes with spaces use ~"Shopping List":bar;
labels use ~note:id|see or ~note:id|"follow-up of". Quote ~ on
the shell. The target may be open, closed, in any active note, a daily
note, or done/ (a mirror is skipped for archived and previous-daily
targets). Relations are inert: no status, lane, Pomodoro, or field
changes. A || spelling (~note:id||see,
~note:id||"follow-up of") stores the identical label and hides it
from suggestions once the capture succeeds; a third pipe is a teaching
error (use || to hide a label, | to name one).
The label pipe attaches directly to the target. Editors using
capture-rewrite with --cursor just
after a newly typed pipe can attach it across spaces or tabs
(~note:id | → ~note:id|, ~note:id || → ~note:id||); direct
bob capture input does not apply that rewrite.
bob capture 'Fix flaky capture retries ~sase:retry-design|see'
bob capture 'Fix trip ~work:foo||prompted-by'
✓ captured mac_inbox.md
🪢 links to "Design a retry policy" · see sase.md ^retry-design [ ] Ready
A selected ~note:block-id with no owner is incomplete
(dependency_target): type task text or + to choose an existing
owner. A bare ~ / ~query / ~note: needs relation_target; a
label position (~note:id|, ~note:id||) needs relation_label.
Contract: Linking tasks with '~' and '~~'.
~~note:block-id[|"label"]¶
The named task links to the captured (or explicitly selected) task.
The captured task is the target, so it is always minted; the named
task's LINKS line must be editable (the same refusal set as *: daily
notes, done/, _templates, _generated, _conflicts,
dot-directories, fenced code).
bob capture 'Write the outline ~~sase_blog:prj|"next step"'
Contract: Linking tasks with '~' and '~~'.
^route:block-id ~note:id¶
Relate an existing owner. ^route:id is a bare owner alias here and
does not create a Pomodoro link. ~note:id ^route:block-id and
^route:block-id ~~note:id are the same family.
bob capture '^bob:fix-retries ~sase:retry-design|see'
Contract: Linking tasks with '~' and '~~'.
Completion¶
! / !query¶
Incomplete: pick an open task to complete (task_complete). Accepting
inserts !<note>:<block-id>. !fix, !sase:, and !"Shopping List are
still typing the query.
bob capture '!'
bob capture: capture item 1 starting on line 1: `!` opens the task picker; pick a task to insert its `!<note>:<block-id>` token, or write one yourself (for example `!sase:fix-flaky`)
Contract: Completing tasks with '!'.
!note:block-id¶
Complete that existing open task. The token must be the whole capture item.
bob capture closes it exactly as x!N would, retires its Task Links, and
unblocks dependents. Extra text is invalid_task_complete.
bob capture '!work:prep'
✓ completed [ ] → [x] Prep the intake work.md ^prep
Contract: Completing tasks with '!'.
Adjustments, shifts, and session start¶
+[N]¶
Extend today's current timed Pomodoro by N five-minute units (default 1).
+5 is +25 minutes. The item must contain only the signed count.
bob capture '+2'
✓ adjusted 2026/20261020.md
WORK 1200-1215 (15m) to 1200-1225 (25m), +10m at line 8
Contract: Adjusting the current Pomodoro.
-[N]¶
Shorten by N units. Bare - is one unit (−5 minutes). Extra text after a
bare sign stays prose (- foo).
bob capture --dry-run '-2'
[dry-run] ok would adjust 2026/20261020.md
WORK 1200-1225 (25m) to 1200-1215 (15m), −10m at line 8
Contract: Adjusting the current Pomodoro.
++[N]¶
Shift the running timed Pomodoro N units later, keeping its duration.
++3 is +15 minutes. The item must contain only the operator. +++ stays
prose.
bob capture --dry-run '++3'
[dry-run] ok would shift 2026/20261020.md
WORK 1200-1225 (25m) to 1215-1240 (25m), +15m at line 8
Contract: Shifting the current Pomodoro.
--[N]¶
Shift N units earlier. Bare -- is one unit.
bob capture --dry-run '--'
[dry-run] ok would shift 2026/20261020.md
WORK 1200-1225 (25m) to 1155-1220 (25m), −5m at line 8
Contract: Shifting the current Pomodoro.
= / =<X>¶
Start today's next future Pomodoro now with se<X> timing (= is 25
minutes, =3 is 15, =-2 is 25 minutes with a 10-minute offset), or
restart the running session with that timing. A restart prints an override
warning. The item must contain only the start token.
bob capture '=3'
✓ started 2026/20261020.md
WORK 1200-1215 (15m) at line 8
- [ ] (**1200-1215** [t:: 15m]) — WORK
1 [*] Write an outline work.md ^outline
Contract: Starting the next Pomodoro.
=name / =<X>#pomodoro¶
Start the named Pomodoro now (=focus, same token as =#focus; equivalent spellings =<X>#pomodoro, =name@<X>). An open match (whole slug, else prefix)
starts in place; a completed match starts a new session with that name
("again"); otherwise a new named session is created and started. While
another session runs, a different named target swaps in. Empty <X> keeps
the running ledger. Write # only when a number comes first (=3#bugs) or the name starts with a digit or reads as a close (=#2fa, =#x2).
bob capture '=focus'
✓ started 2026/20261020.md
FOCUS 1200-1225 (25m) at line 8
- [ ] (**1200-1225** [t:: 25m]) — FOCUS
Contract: Starting a named Pomodoro.
~<K>¶
On a start token, drop the queued Task Links in <K> (=~2, =3~2,4,
=bugs~2). ~ always comes last. =~ and =~2, are incomplete
(pomodoro_start_task). A start cannot complete links; ! belongs on x.
bob capture --dry-run '=~2'
[dry-run] ok would start 2026/20261020.md
WORK 1200-1225 (25m) at line 10
dropped link 2
bob capture-parse -f json -- '=~'
{"ok":true,"schema_version":1,"mode":"incomplete","needs":["pomodoro_start_task"]}
Contract: Starting the next Pomodoro.
Closing, outcomes, and work logs¶
x¶
Close today's running timed Pomodoro. Lowercase x only. Selection pieces
*, !, ~, and @ may appear in any order. <N> keeps those numbered
Task Links in progress; a lone 0 means none. @<N> is the same keep-list
typed last (x!1@2 means x2!1).
bob capture --dry-run 'x'
[dry-run] ok would close WORK 1200-1225 → 1200-1200 (0m, −25m) · 2026/20261020.md line 8
1 [*] → [/] Write an outline work.md ^outline
next: WORK (created) at line 10 · carries 1 link
A mid-body x stays prose (Plan x). xx, x-ray, and capital X never
close.
Contract: Closing the running Pomodoro.
x!<M>¶
Complete those numbered Task Links. A present-but-empty ! completes every
remaining numbered link (x!). x!1 narrows to task 1.
bob capture --dry-run 'x!'
[dry-run] ok would close WORK 1200-1225 → 1200-1200 (0m, −25m) · 2026/20261020.md line 8
1 [*] → [x] Write an outline work.md ^outline
Contract: Choosing each Task Link's outcome.
x*<P>¶
Park those links (normal work without carrying forward). Empty * parks
every remaining numbered link (x*).
bob capture --dry-run 'x*'
[dry-run] ok would close WORK 1200-1225 → 1200-1200 (0m, −25m) · 2026/20261020.md line 8
1 [*] → [ ] Write an outline work.md ^outline
Contract: Choosing each Task Link's outcome.
x~<K>¶
Drop those links (removed, not carried, not started). x~ is incomplete
(pomodoro_close_task).
bob capture --dry-run 'x~1'
[dry-run] ok would close WORK 1200-1225 → 1200-1200 (0m, −25m) · 2026/20261020.md line 8
1 [*] dropped Write an outline work.md ^outline
bob capture-parse -f json -- 'x~'
{"ok":true,"schema_version":1,"mode":"incomplete","needs":["pomodoro_close_task"]}
Contract: Choosing each Task Link's outcome.
Work Log¶
One entry may sit on the close line; several use child bullets. With no
number, the line logs to the first task the close works. A leading number
names the task (x 1 3 bugs fixed logs 3 bugs fixed to task 1). x 1
without text is incomplete (pomodoro_close_log_text). Bullets are numbered
all or none. A two-space bullet nests an undated detail.
bob capture --dry-run 'x wired the lexer'
[dry-run] ok would close WORK 1200-1225 → 1200-1200 (0m, −25m) · 2026/20261020.md line 8
1 [*] → [/] Write an outline work.md ^outline
logged: wired the lexer
Contract: Logging work while closing.
+2 x / x =¶
Whitespace-separated session tokens on one line run left to right exactly
like blank-line items (+2 x, x =, x =bugs, =bugs +2). An inline
Work Log entry sits right after its close, with operators trailing
(x wired the lexer =). Child bullets attach to that line's x.
bob capture --dry-run '+2 x'
[dry-run] ok would adjust then close 2026/20261020.md
WORK 1200-1215 (15m) to 1200-1225 (25m), then close
Contract: Chaining session operators on one line.
@route:block-id=x¶
Put that existing task into the running session, then close it. Own-prerequisite
Blocked targets drop those links first. The = is the suffix separator;
numbers refer to the post-link lineup. ^route:block-id=x is the
active-task spelling. With extra text, create the new linked task and then
close.
bob capture --dry-run '@work:outline=x'
[dry-run] ok would link then close work.md ^outline
[ ] → [*] Write an outline, then close WORK
Contract: Closing the running Pomodoro.
Project captures¶
@route^block-id+¶
Create the project note <route>_<block_id>.md (hyphens in the block ID
become underscores). The ^prj task is never linked and the daily note is
untouched. #pomodoro on this form sends :id Task Links under that named
open Pomodoro.
bob capture '@work^intake-flow+' 'Ship the intake flow'
✓ captured work_intake_flow.md
parent [[work]]
- [ ] #task #prj Ship the intake flow #hide ^prj
1 task · sections —
Contract: Project notes.
:id child¶
On a project-note capture, - <task> :<task-id> names the task, makes it
Next ([*], or [?] when scheduled), and links [[<stem>#^<task-id>]]
into the current/next Pomodoro, or the #pomodoro one.
bob capture '@work^intake-flow+' 'Ship the intake flow' '- Name the stages :stages'
The child is Next and linked from today's ledger as
[[work_intake_flow#^stages]].
Contract: Project notes.
^id child¶
On a project-note capture, - <task> ^<task-id> names the task only:
Contract: Project notes.
Task children and link toggles¶
@route+block-id¶
Ordinary child bullet under an existing task.
bob capture 'Check the parser @work+prep'
✓ captured work.md
under [ ] Prep the intake ^prep
- Check the parser
Contract: Sub-bullets under existing tasks.
@route+block-id#section¶
Child bullet under an ALL-CAPS section of that task. With no other text, #
names a Pomodoro instead (Ensure Next / move).
bob capture 'Need a parser @work+prep#requirements'
✓ captured work.md
under [ ] Prep the intake ^prep REQUIREMENTS
- Need a parser
Contract: Sub-bullets under existing tasks.
@route+block-id with no other text¶
@route+block-id with no other text ensures the task is Next and relocates
its existing open-Pomodoro Task Link to today's implicit current/next
Pomodoro. #pomodoro moves the subtree to that named open Pomodoro,
creating a named future Pomodoro when missing. Own-prerequisite Blocked
targets drop those links first.
bob capture '@work+outline'
✓ ensure-next work.md
[ ] → [*] Write an outline ^outline
moved Task Link under WORK
Contract: Task Link toggle.
@route+block-id!¶
Toggle that task's Pomodoro Task Link: link it when unlinked (Ready and own-prerequisite Blocked rise to Next; other Blocked stay unlinked) or unlink it when linked (lane unchanged). Removing a link never lowers Next or Pending.
bob capture '@work+outline!'
✓ toggle work.md
[ ] → [*] Write an outline ^outline
linked under WORK
Contract: Task Link toggle.
Pomodoro notes¶
trailing #¶
A trailing bare # is a plain-text note on a Pomodoro, not a routed task.
bob capture 'remembered the timeout #'
✓ captured 2026/20261020.md
under (**1200-1230** [t:: 30m]) — WORK
- remembered the timeout
Contract: Pomodoro notes.
Scheduling, priority, and clipboard¶
s:<N>¶
[scheduled::] N days from today, recognized in the terminal token region.
Checkbox-bearing captures start Blocked ([?]), including s:0.
bob capture 'File taxes @work s:1'
✓ captured work.md
- [?] #task File taxes [created::2026-10-20] [scheduled::2026-10-21]
Contract: Scheduling and priority.
$ / $<N>¶
Write priority level N (bare $ = 1) and roll a scheduled date in that
level's window. A missing config file is an error; there is no built-in
default. An explicit s:<N> wins the date; $<N> still writes priority.
Mid-line amounts stay text (Pay $3 to Bob).
bob capture-parse -f json -- 'File taxes @work $1'
{"ok":true,"schema_version":1,"mode":"task","route":"work","body":"File taxes"}
A configured P1 window writes the priority field and rolls [scheduled::]
in that window. This illustration is the resulting task text, not a captured
envelope:
Contract: Scheduling and priority.
% / %N / %header¶
Capture clipboard content as child bullets. %, %N, and %header are
the spellings. -n, --no-clip keeps trailing % literal. -c, --clip
forces clipboard capture. Clipboard backends are stubbed in this isolated
fixture, so the example keeps % literal.
bob capture --no-clip 'Keep the percent @work %'
✓ captured work.md
- [ ] #task Keep the percent % [created::2026-10-20]
Contract: Clipboard.