bob-cli installs the bob command for the Bob Obsidian vault and Pomodoro
workflow. The preferred interface is bob <subcommand>; bob --help is the
authoritative command index. Command implementations are native Rust by
default.
Legacy command names still exist as installed binaries for existing tmux, shell, and automation callers. The Pomodoro and notification shell implementations remain embedded as a targeted rollback path; see Compatibility shims for the exact mappings and fallback behavior.
- Installation
- Terms
- Daily workflow
- Vault layout
- Commands
- Capture
- Query
- Task status hooks
- Projects
- Randomize
- Plugins
- Gkeep
- Highlights
- Nightly maintenance
- Vault sync
- Move done tasks
- Pomodoro status
- Compatibility shims
- Runtime dependencies
- Environment
- Migration notes
- Release checklist
- Detailed command contracts
Installation requires a current stable Rust toolchain with cargo. The default
vault location is ~/bob; set BOB_DIR when the vault lives elsewhere.
For local development from this checkout:
cargo install --path . --locked --forceFor installation from the Git remote:
cargo install --git git@github.com:bobs-org/bob-cli.git --locked --force bob-cliWith just installed, smoke-test an install without replacing an existing
user install:
just install-smokeAfter installation, verify the vault selection with read-only commands before running a command that writes or pushes changes:
export BOB_DIR=/path/to/bob-vault
bob --help
bob capture-targets
bob projects listPriority rolls (p:<N>), bob randomize, Highlights pre-scan hooks,
bob gkeep, and bob plan read ~/.config/bob/config.yml. Override
that path with BOB_CONFIG_FILE or XDG_CONFIG_HOME; see
Environment.
These words show up across every command. They are vault conventions, not
separate bob subcommands.
| Term | Meaning |
|---|---|
| Vault | The Obsidian folder Bob operates on (~/bob by default; BOB_DIR to override) |
| Route | The lowercase name of a top-level note; @groceries writes groceries.md |
| Pomodoro | A checkbox in the daily note's Pomodoros section for one work session |
| Block ID | The trailing ^id on a task line, used to link or nest under that task |
| Task link | A [[note#^id]] (or embed) pointing at a task. When that is the only content of a Pomodoro child bullet, it is that session's planned work |
| Schedule Log | A managed 🗓️ **SCHEDULE LOG** child that records each schedule change |
| Work Log | A managed 🛠️ **WORK LOG** child that records work summaries |
| Cancel Log | A managed ❌ CANCEL LOG child (placed first) that records why a task was cancelled |
Bob tracks a daily Pomodoro ledger inside an Obsidian vault and keeps that vault synced through Git. Capture, linking, status, and nightly maintenance are separate steps:
- Capture with
bob captureor Bob Mac Capture (the macOS panel that calls the same commands). Tasks land inmac_inbox.mdunless an@routetoken sends them to another note; scheduled checkbox-bearing captures start Blocked ([?]). - Link today's work onto a Pomodoro in the daily note. That happens when
you capture with
@route:id(which also marks the task Next), ensure an already-planned task with a bare@route+idcapture, explicitly toggle a task with@route+id!, or add a task link under a Pomodoro in Obsidian.bob pomodoro,bob tmux-pomodoro, andbob notifyonly read that ledger; they do not create links. - Reconcile statuses with
bob task-status-hooksso Next, In Progress, and Blocked markers follow the ledger and any schedules changed outside capture. After time away, preview the overdue backlog withbob randomize --dry-run, thenbob randomize --seed <seed>to re-roll it. - Nightly, run
bob nightlyto reconcile the vault through Git, archive done and canceled tasks, and reconcile the vault again.
Read-only inspection (bob query, bob projects list, bob plugins list,
bob highlights doctor) can run at any time.
Paths below are relative to BOB_DIR (~/bob by default):
| Path | Role |
|---|---|
mac_inbox.md |
Default capture target |
gkeep_inbox.md |
Default target for bob gkeep pull (gkeep.target can name another vault-relative note) |
<route>.md |
Area or project note selected by an @route token |
<route>_<id>.md |
Project note created by @route^id+; hyphens in the block ID become underscores |
YYYY/YYYYMMDD.md |
Daily note; the Pomodoros section is the session ledger |
done/ |
Archive notes written by bob move-done-tasks |
img/, file/ |
Images and saved clipboard snippets written by bob capture |
_conflicts/ |
Local copies of vault files bob vault-sync could not merge |
.obsidian/plugins/ |
Installed community plugins, including Bob's custom plugins |
xlib/ |
Highlights intake PDFs from bob highlights create and bob highlights clip |
lib/ |
Highlights library PDFs after bob highlights scan |
old_lib/ |
Archival predecessor of lib/; tracked in the vault Git repo, which is now the vault's only sync channel |
ref/ |
Generated Highlights reference notes |
Daily-note selection uses BOB_DAY_FILE when set, otherwise
<bob-dir>/YYYY/YYYYMMDD.md for the local date (or BOB_NOW).
Bob's workflow commands are:
| Command | Purpose |
|---|---|
capture |
Capture a task or section bullet, optionally with clipboard content |
capture-complete |
Complete capture marker or wikilink syntax at the cursor |
capture-parse |
Preview what in-progress capture text and wikilinks mean |
capture-pomodoro-name |
Assign a canonical name to an unnamed Pomodoro |
capture-pomodoros |
List today's Pomodoro ledger entries |
capture-rewrite |
Apply the capture grammar's automatic draft rewrites (bare @@ absorption) |
capture-sections |
List the non-Tasks headings in a routed note |
capture-targets |
List inbox, area, and non-terminal project capture routes |
capture-task-id |
Assign a user-authored block ID to an open capture task |
capture-task-sections |
List the ALL-CAPS child sections of a capture task |
capture-tasks |
List the open tasks in a routed note |
freshness |
List the tasks due for freshness review and seed the cutover |
gkeep |
Drain the Google Keep inbox into Obsidian tasks |
highlights |
Synchronize Highlights PDF annotations with reference notes |
move-done-tasks |
Archive done and canceled task blocks and repair their links |
nightly |
Run the Git sync and maintenance workflow |
notify |
Notify when the current Pomodoro finishes |
plan |
Show today's plan budget, Today's tasks, and the NEXT/PENDING lanes |
plugins |
List and deploy Bob's custom Obsidian plugins |
pomodoro |
Print the current Pomodoro status |
projects |
Inspect and synchronize project lifecycle tasks |
query |
Run headless Dataview or Tasks queries, or live Dataview queries |
randomize |
Re-roll due prioritized tasks within their priority windows |
task-status-hooks |
Reconcile Pomodoro links, task ranks, and derived Blocked state |
tmux-pomodoro |
Print Pomodoro status for a tmux status line |
vault-sync |
Reconcile the Bob vault through Git |
Use bob <command> --help for concise usage. The sections below summarize each
workflow and link to the detailed command contract where one exists.
The hidden task-status-setter and mark-next-tasks spellings remain
compatibility-only aliases for task-status-hooks and are not listed in
top-level help.
bob capture [OPTIONS] [--] [TEXT]...Captures one task, ordinary Markdown bullet, or task sub-bullet into the vault
without opening desktop Obsidian. The default destination is mac_inbox.md.
TEXT may be several physical lines: each item’s first nonblank line is the
parent, later authored bullets become children, and blank lines split a batch
of items. A @@route or @@route+id token anywhere in the draft is a
draft-wide destination declaration, not body text; item-local @... markers
still win, and bob capture warns when a local marker shadows the declaration
typed on that same item. The whole batch is planned before anything is written.
| Marker | Meaning |
|---|---|
@@route |
Shared task destination, anywhere in the draft, for otherwise-unrouted items |
@@route+id |
Shared parent-task destination, anywhere in the draft, for otherwise-unrouted items |
@route |
Task in <route>.md |
@route#Section |
Ordinary bullet under a matching non-Tasks heading |
@route# |
Ordinary bullet under any non-Tasks heading |
@route^id |
Ordinary task with a user-authored block ID |
@route:id |
Next-status task plus a Pomodoro task link; scheduled tasks start Blocked |
@route:id#pomodoro |
Same, linked under a matching named open Pomodoro or a new named future Pomodoro |
@route:id=<X> |
Same, and atomically start the session; <X> mirrors the se<X> snippet (empty is 25 minutes) |
@route:id#pomodoro=<X> |
Same under the named Pomodoro, starting that session |
@route:id[#pomodoro][=<X>] with no other text |
Link the existing task into today's ledger (no new task); =<X> starts the resolved session |
^route:id[#pomodoro][=<X>] with no other text |
Identical execution; ^ completes only In Progress and Next tasks |
:<query> with no other text |
Task-link picker query over every open task; accepting inserts @route:id (never captured) |
+[N] / -[N] |
Adjust today's current timed Pomodoro by N five-minute units (+5 extends 25m, - shortens 5m; the count defaults to 1); the item must contain only the signed count |
++[N] / --[N] |
Shift today's running timed Pomodoro N five-minute units later/earlier, keeping its duration (++3 moves 15m later, -- moves 5m earlier; the count defaults to 1); the item must contain only the operator |
= / =<X> |
Start today's next future Pomodoro now with se<X> timing (= is 25 minutes, =3 is 15 minutes); the item must contain only the start token (quote in zsh) |
=<X>#pomodoro |
Start the named Pomodoro now with se<X> timing (=#deep-work is 25 minutes, =3#bugs is 15 minutes); an open match (whole slug, else prefix) starts in place, a completed match starts a new session with that name, otherwise a new named session is created and started; the item must contain only the start token (quote in zsh) |
=[<X>][#<name>]~<K> |
Start without the queued Task Links in <K> (=~2, =3~2,4, =#bugs~2); ~ drops, the drop part always comes last (quote in zsh) |
=x[<N>][!<M>][~<K>] |
Close today's running timed Pomodoro (case-insensitive =X, !/~ in either order); <N> keeps only those numbered Task Links in progress, !<M> completes those links, ~<K> drops those links (removed, not carried, not started), a lone 0 means none; the item must contain only the token (quote in zsh, since ! history expansion also applies) |
+2 =x, =x =, =x =#bugs, =x =~2 |
Same-line session-operator chain: whitespace-separated session tokens on one line run left to right like blank-line items (=x =~2 closes then starts without link 2) |
@route:id=x… / ^route:id=x… with no other text |
Put that existing task into the running session, then close it; numbers refer to the post-link lineup |
<text> @route:id=x… |
Create the new task in the running session, then close it |
@route^id+ |
New project note <route>_<id>.md; the daily note and the parent note are left unchanged |
@route^id+#pomodoro |
Same, picking the Pomodoro that :<task-id> Task Links go under (named open Pomodoro or new named future Pomodoro) |
first-level bullet ending in :<task-id> |
Names the task, makes it Next, and links it into the current/next Pomodoro or the #pomodoro Pomodoro |
first-level bullet ending in ^<task-id> |
Names the task only (never linked) |
@route:id+ |
Retired project-note spelling; use @route^id+ instead |
@route+id |
Child bullet under an existing task |
@route+id#section |
Child bullet under an ALL-CAPS section of that task |
@route+id with no other text |
Ensure the task is Next and relocate its existing open-Pomodoro Task Link to today's current/next Pomodoro |
@route+id#pomodoro with no other text |
Ensure Next and move that existing Task Link to the named Pomodoro, creating the named future Pomodoro if needed |
@route+id! with no other text |
Toggle that task's Pomodoro Task Link: link it when unlinked (Ready/Blocked rise to Next) or unlink it when linked (lane unchanged) |
trailing # |
Plain-text note on a Pomodoro (no @route) |
s:<N> |
Schedule N days from today; checkbox-bearing captures start Blocked, including s:0 |
p:<N> |
Write priority level N and roll a scheduled date, so checkbox-bearing captures start Blocked |
%, %N, %header |
Attach clipboard content |
# is not one marker. A trailing bare # is a Pomodoro note; @route#… selects
a heading in that note; @route+id#… selects an ALL-CAPS child section of that
task once the item has body text, but selects a Pomodoro name when
@route+id#… is the whole item; @route:id#… selects a matching named open
Pomodoro or creates a named future Pomodoro; =<X>#… starts the named
Pomodoro now (open match in place, completed match as a new "again" session,
otherwise a new named session). A # in the middle of the body
stays ordinary text. The retired @route::id spelling is not accepted; use
@route^id for an ordinary task with a block ID.
+ has two positions. @route+id is a child bullet, or an Ensure Next
request, under an existing task. @route^id+ puts +
immediately after the block ID and creates <route>_<id>.md at the vault root,
replacing every - in the block ID with _. Capture leaves the parent note
unchanged; bob projects sync owns its Sub-projects line. The project's own
^prj task is never linked. End a first-level task bullet with :<task-id>
to link it into the current/next Pomodoro or the #pomodoro Pomodoro, or with
^<task-id> to name it only. The retired @route:id+ spelling is not
accepted; use @route^id+ instead. A + after a Pomodoro name, as in
@sase:deep-fix#bugs+,
stays part of the name. Route names may contain letters, digits, _, and -.
Block IDs may contain letters, digits, and -.
A bare @route+id marker-only capture is the default Ensure Next operation:
Ready [ ], Blocked [?], In Progress [/], and Next [*] all end as Next,
and Bob moves the task's single existing open-Pomodoro Task Link subtree to the
implicit current/next open Pomodoro. @route+id#pomodoro is the same Ensure
Next operation targeting a named open Pomodoro, or creating that named future
Pomodoro when no open match exists. Neither unsuffixed form toggles Next back
to Ready or creates a missing link. Repeating the request when the task is
already Next and the link is already at the destination is a no-op.
Use @route+id! to toggle the Pomodoro Task Link itself: an unlinked Ready
[ ] or Blocked [?] task becomes Next [*] and gets [[route#^id]] under
the implicit current/next open Pomodoro; an unlinked Next [*] or In Progress
[/] task links with its status unchanged; a linked task of any open status
unlinks every matching link under every open Pomodoro with its status
unchanged. ! cannot be combined with #pomodoro. Future schedules are retired
only when the link direction sets the task to Next and the task has exactly one
valid future [scheduled::YYYY-MM-DD], and
a Schedule Log entry is written only if the task already has that log. Done,
canceled, missing, duplicate, and non-task IDs are rejected
before any note is written. Users upgrading from the initial !
implementation should note this reversal: the unsuffixed forms are now the
idempotent default, and ! is the link/unlink escape hatch that never lowers
a lane.
Append =<X> to a Pomodoro-linked marker to start its session in the same
transaction: bob capture 'Write outline @sase:outline=3' starts a
15-minute session on the next open slot, and
@sase:outline#deep-work=-2 starts the default 25-minute session with a
10-minute offset. The started entry moves ahead of every open Pomodoro, right
after the last completed one. <X> is exactly the suffix of the se<X> snippet — empty,
digits, -, -digits, or digits- with optional digits — and the range is
rounded to 5 minutes like the snippet. A timed open Pomodoro stops the
capture with a "finish the current Pomodoro first" error, and =<X> cannot
combine with s:<N> or p:<N>.
Capture a whole item +N or -N to adjust today's current timed Pomodoro:
bob capture +5 extends by 25 minutes, bob capture -- -2 shortens by 10
minutes, and printf '+5\n\nCall bank @Cash+\n' | bob capture mixes an
adjustment with an ordinary item across blank lines. Session operators may
share one line (bob capture '+2 =x' extends then closes; bob capture '=x ='
closes then starts). The item must contain
only the signed count; Plan +5 stays prose, and forced route/task/clipboard
options are rejected on adjustments.
Capture a whole item =/=<X> to start today's next future Pomodoro
(bob capture '=' starts 25 minutes, bob capture '=3' starts 15 minutes;
quote in zsh, which expands a leading =word). A trailing ~<K> drop list
(bob capture '=~2', bob capture '=3#bugs~1') starts without those queued
Task Links: ~ drops, so =~2 drops task 2 from the session you start the
way =x~2 drops task 2 from the session you stop.
Capture a whole item =x[<N>][!<M>][~<K>] to close today's running timed
Pomodoro the way Obsidian's Ctrl+Enter completion does, plus an
auto-decrement that shortens an early-stopped session to the earliest
five-minute step at or after now: bob capture '=x' closes the session,
bob capture '=x2' keeps only task 2 in progress, bob capture '=x1!2'
keeps task 1 in progress and completes task 2, bob capture '=x0' defers everything,
bob capture '=x1~2' keeps task 1 in progress and drops task 2,
bob capture '^bob:capture-stop=x' puts that task into the running session
then closes it, and printf -- '-2\n\n=x\n' | bob capture adjusts then
closes atomically (or bob capture '-2 =x' on one line). Numbers count the session's Task Links in ledger order
from 1; on link forms they refer to the post-link lineup. See
docs/capture.md for the numbering, outcomes, classification, carry, Work
Log, link-form, diagnostic, JSON, and batch rules.
bob capture buy milk @groceries
bob capture '@dev^foobar' 'Some ordinary task.'
bob capture '@cash^goog-exit+' 'Finish the Google exit packet!'
bob capture '@dev:foobar' 'Some foobar task.'
bob capture '@dev:foobar#bugs' 'Some foobar task.'
bob capture 'Write outline @sase:outline=3'
bob capture +5
bob capture -- -2
bob capture '='
bob capture '=3'
bob capture '=x'
bob capture '^bob:capture-stop=x'
bob capture '@cash+goog-exit' 'Called Morgan Stanley today.'
bob capture '@cash+goog-exit'
bob capture '@cash+goog-exit!'
bob capture '@cash+goog-exit#coding'
bob capture remembered to bump the timeout #
printf '@@foo\nFirst task\n\nSecond task @bar\n' | bob captureEditor clients such as Bob Mac Capture call bob capture --format json,
bob capture-parse, bob capture-rewrite, and bob capture-complete.
bob capture-rewrite turns a bare @@ typed inside an item that already has
a @route (or @route+id) marker into @@route (or @@route+id),
deleting the marker it absorbed. Discovery helpers
(capture-targets, capture-sections, capture-tasks,
capture-task-sections, capture-pomodoros) feed those pickers.
capture-task-id assigns a user-authored block ID to an open task that still
lacks one. capture-pomodoro-name assigns a canonical ALL-CAPS name to an
unnamed Pomodoro in today's daily note.
Captures that change today's ledger also report the plan budget: a
top-level plan_budget with before/after theme and link meters, a
→ under GOALS (next up) destination line naming where the Task Link
landed (JSON adds a role of current, next_up, named, or
created), and a stderr warning only while the batch grows a meter
past its cap. With plan.strict: true, a batch that would create a new
named Pomodoro past the theme cap is refused atomically (exit 1,
code: plan_theme_cap_exceeded); session starts are never refused.
The full grammar, JSON contracts, and picker protocol live in
docs/capture.md.
bob query --source '#project'
bob query --query 'LIST FROM #waiting'
bob query --format json --query-file queries/projects.dql
bob query --tasks 'status.type is TODO' --origin dash.md
bob query --format json --tasks-file queries/all.tasks
bob query --format markdown --tasks-note dash.mdRuns Dataview source expressions, DQL queries, and Obsidian Tasks queries from
the shell. The default native engine evaluates queries against the local
Markdown vault, so scripts do not need a running desktop Obsidian app. paths
output prints vault-relative Markdown paths, json output is stable for
scripts, and markdown output prints Dataview-rendered Markdown for supported
DQL results. Native Tasks support includes filters, Boolean expressions,
JavaScript by function instructions with Moment, sorting, grouping, limits,
layout instructions, Query File Defaults, placeholders, and rendered Markdown.
--tasks-note runs every fenced Tasks block with its note context and identifies
each result by heading.
This command does not reconcile the vault; freshness is handled by
bob vault-sync and the configured background or cron sync path. Use
--engine obsidian when you want exact
behavior from the live Dataview plugin in an open Obsidian vault. Tasks inputs
remain native-only, with an env-gated live renderer harness for parity checks.
The full command contract and live smoke-test steps live in
docs/dataview.md.
bob task-status-hooks [-b|--bob-dir DIR] [-d|--dry-run] [-f|--format human|json] [-r|--retry-timeout SECONDS]Run this after capturing or closing Pomodoro-linked work, and after
bob projects sync writes schedules. It makes today's Pomodoro ledger the
source of truth for Next / In Progress promotions and structural cleanup, and
uses the latest existing earlier daily note as a read-only recent-activity
source.
Direct block links under open Pomodoros promote Ready tasks to Next ([*]) and
leave In Progress ([/]) alone. Sole transcluded dependencies inherit the
strongest parent rank. Lanes are sticky: removing a link never lowers Next or
In Progress — only a Next task inside a canonical daily note (or the current
ledger) still clears back to Ready once unlinked and stale. Independently,
open Dataview dependencies and future [scheduled:: YYYY-MM-DD] dates mark a
task Blocked ([?]). The command
also retires completed references, moves stray bullets onto the current
Pomodoro, repairs Pomodoro markers, de-duplicates the same task under later
open Pomodoros, removes list items that only point at canceled tasks, deletes
childless current-daily Pomodoro entries, and
groups area/project Tasks sections into Next & In Progress, Blocked, and
Done & Canceled child headings with a linked status-count badge row while
keeping Ready tasks in the intake.
bob task-status-hooks --dry-runThe command refuses to change files if the current daily note is missing, lacks
a Pomodoros section, or has multiple non-empty open timed Pomodoros. Live writes use
guarded snapshots, recovery copies, and a bounded quiet-period check for
structural regrouping; --dry-run computes the same plan without locking or
writing recovery state. Live runs also retry lock contention and other
transient failures with jittered backoff, bounded by --retry-timeout
(default 120s; 0 fails fast on the first attempt). The full sync, grouping,
link-resolution, exclusion, retry, output, and JSON contract lives in
docs/task-status-hooks.md.
bob plan [-b|--bob-dir DIR] [-f|--format human|json]Today is closed: GTD plus at most 3 themes. bob plan is read-only:
it reports the plan budget from today's daily-note ledger (distinct
open Pomodoro names besides the exempt ones, distinct Task Links
under them), Today's tasks with a dedicated Task Link under today's
open Pomodoros, and the NEXT/PENDING lane counts from the native
Tasks index, with lints and hints. Removing a Task Link never changes
a task's lane. The first open non-exempt entry is the highlight (★);
the timed entry is running (▶). A missing daily note or missing
Pomodoros section still reports TODAY 0 and the lanes, and exits 0.
The full definition, JSON contract, lint codes, and conformance
examples live in docs/plan.md.
Every visible, non-recurring Ready task carries a human-confirmed
[fresh:: YYYY-MM-DD]: the date a human last confirmed it still needs
doing as written. Tasks never confirmed, or confirmed longer ago than
their refresh interval (7 days by default, overridable per task, per
note, and in config), are due for review. Supported keymaps and
bob capture edits stamp the tasks they rewrite; creation and
automation never do. The definition, placement and state rules, and
conformance vectors live in docs/freshness.md.
The freshness mark display contract lives in §11 of that file.
bob freshness [-b|--bob-dir DIR] [-f|--format human|json] [-l|--limit N]
bob freshness list [-b|--bob-dir DIR] [-f|--format human|json] [-l|--limit N]
bob freshness seed [-b|--bob-dir DIR] [-d|--dry-run] [-F|--force] [-f|--format human|json]Running bob freshness with no subcommand runs list: the tasks due
for review (never confirmed, resurfaced, or stale), with whole-vault
counts. seed stamps the one-time cutover: Ready tasks staggered
across the last 7 days by note, every other open task today. The seed
refuses a second run, aborts on any parse change, and --dry-run
previews without writing.
bob projects list [-b|--bob-dir DIR]
bob projects sync [-b|--bob-dir DIR] [-d|--dry-run]Scans notes whose frontmatter declares type: "[[project]]". list prints
frontmatter status, open #task counts, and the ^prj lifecycle task. sync
reconciles status from that task (done, canceled, or reopen to wip),
manages #hide so ^prj surfaces on dash.md only when nothing else is open,
maintains the machine-owned Sub-projects ledger, and propagates optional
scheduled: YYYY-MM-DD frontmatter onto ordinary open tasks.
sync writes frontmatter, #hide, Sub-projects lines, and inline schedules;
it does not change checkboxes. Run bob task-status-hooks afterward to derive
or recover [?] Blocked markers. The property picker in Bob Navigation
Hotkeys can propagate schedules and reconcile Blocked in the same editor
transaction. The full project task contract lives in
docs/projects.md.
bob randomize [-d|--dry-run] [-f|--format human|json] [-l|--level LABEL]...
[-o|--offline] [-r|--retry-timeout SECONDS] [-s|--seed SEED]
[-u|--until DATE|+N]Re-rolls every due prioritized task to its own random date inside that
task's configured priority window (P1 2–7 days through P4 91–365 days).
Each re-roll replaces the scheduled date, flips a future-dated Ready task
to Blocked, writes a 🎲 Schedule Log entry, and regroups eligible project
notes in the same write. P0 tasks, Next and In Progress tasks, tasks linked
from today's open Pomodoros, ^prj lifecycle tasks, and due/repeat
tasks are always left alone. --level limits the roll to those labels.
--until treats tasks scheduled through that date as due and rolls windows
from it. An offset that cannot be represented on the calendar is a usage
error (exit 2): bob randomize: invalid --until "…": date out of range.
Always preview first with --dry-run, then apply those exact dates with
the printed --seed, repeating --level and --until when you used them.
Omit --seed to use BOB_PRIORITY_ROLL_SEED or a generated seed. A live
run holds the shared vault-maintenance lock while it syncs, plans, writes,
commits exactly the rewritten notes as one bob randomize commit, and
syncs again. --offline skips both sync cycles and still commits locally.
Undo with git -C ~/bob revert <sha> && bob vault-sync. The full contract
lives in docs/randomize.md.
bob plugins [-b|--bob-dir DIR] [-f|--format table|json] [-n|--no-pull] [-r|--repo DIR]
bob plugins list [-b|--bob-dir DIR] [-f|--format table|json] [-n|--no-pull] [-r|--repo DIR]
bob plugins sync [-B|--backup-dir DIR] [-b|--bob-dir DIR] [-d|--dry-run] [-F|--force] [-n|--no-pull] [-p|--plugin ID] [-r|--repo DIR]Lists Bryan's custom Bob Obsidian plugins from the
bobs-org/bob-plugins repo and
annotates each with live vault state. Running bob plugins with no subcommand
runs list. Managed files are manifest.json, main.js, and styles.css
when present; runtime files such as data.json are never touched.
The repo root resolves from -r, --repo, then BOB_PLUGINS_DIR, then
~/projects/github/bobs-org/bob-plugins. The vault root resolves from
-b, --bob-dir, then BOB_DIR, then ~/bob. By default, list and sync
run a non-interactive git pull first; pass -n, --no-pull to skip it.
sync copies managed files into
<bob-dir>/.obsidian/plugins/<id>/, skips vault files with uncommitted Git
changes unless -F, --force is set, and writes timestamped backups first.
The full command contract lives in docs/plugins.md.
bob gkeep [-a|--all] [-b|--bob-dir DIR] [-f|--format table|json] [-s|--source both|keep|vault]
bob gkeep list [-a|--all] [-b|--bob-dir DIR] [-f|--format table|json] [-s|--source both|keep|vault]
bob gkeep pull [-b|--bob-dir DIR] [-d|--dry-run] [-f|--format human|json] [-i|--id REF]... [-p|--include-pinned] [-S|--include-shared] [-l|--limit N] [-n|--no-archive] [-C|--no-commit] [-q|--quiet]
bob gkeep login [-e|--email EMAIL]
bob gkeep doctor [-b|--bob-dir DIR] [-f|--format human|json]Moves every Google Keep inbox note into gkeep_inbox.md as an Obsidian task
and archives each note in Keep only after its current content is provably in
the vault. Running bob gkeep with no subcommand runs list: Keep notes
and vault tasks side by side with per-note pull states. pull -d previews
the exact Markdown, pull -n writes without archiving, and pull writes,
verifies, commits, then archives. login is the one-time token setup and
doctor diagnoses the whole chain.
The full command contract lives in docs/gkeep.md.
bob highlights clip <URL> [-A|--author NAME] [-b|--bob-dir PATH] [-d|--dry-run] [-f|--force] [-H|--html FILE] [-l|--lib-dir PATH] [-N|--name STEM] [-o|--output PDF] [-P|--parent NOTE] [-p|--published DATE] [-r|--ref-dir PATH] [-s|--status STATUS] [-T|--title TITLE] [-t|--ref-type DIR] [-x|--xlib-dir PATH]
bob highlights create <md-file> [-b|--bob-dir PATH] [-d|--dry-run] [-f|--force] [-i|--include-id] [-l|--lib-dir PATH] [-o|--output PDF] [-P|--parent NOTE] [-r|--ref-dir PATH] [-s|--status STATUS] [-t|--ref-type DIR] [-x|--xlib-dir PATH]
bob highlights doctor [-b|--bob-dir PATH] [-l|--lib-dir PATH] [-n|--no-hooks] [-r|--ref-dir PATH] [-x|--xlib-dir PATH]
bob highlights marker <pdf> [-b|--bob-dir PATH] [-l|--lib-dir PATH] [-r|--ref-dir PATH] [-x|--xlib-dir PATH]
bob highlights scan [-b|--bob-dir PATH] [-d|--dry-run] [-j|--jobs N] [-l|--lib-dir PATH] [-n|--no-hooks] [-r|--ref-dir PATH] [-v|--verbose] [-w|--write-pdfs] [-x|--xlib-dir PATH]
bob highlights sync <pdf> [-b|--bob-dir PATH] [-d|--dry-run] [-l|--lib-dir PATH] [-p|--prefer marker|frontmatter] [-r|--ref-dir PATH] [-w|--write-pdf] [-x|--xlib-dir PATH]Turns Markdown and web articles into Highlights-ready PDFs and turns Highlights annotations into Obsidian reference notes.
clip <URL>captures a web article in reader mode through the pinned web-clip adapter intoxlib/blogs/<slug>.pdf(override the subdirectory with--ref-type), stamping the page-1 marker withsource_url,author,published, andcapturedprovenance. Already-captured URLs are refused, even with--force; bot-protected sites retry headed and otherwise fail closed with a hint.-H, --htmlreplays a page saved from a real browser. Needsuv, Google Chrome (orBOB_CHROME), and Xvfb for bot-protected sites on Linux.create <md-file>renders through pandoc and xelatex intoxlib/chat/<basename>.pdf(override the subdirectory with--ref-type) and embeds the page-1 markerscanneeds.-o, --outputwrites the complete PDF path instead, including the filename, and cannot be combined with--ref-type. Relative output paths are resolved from the current directory and a leading~is expanded.--include-idadds markeridfrom the Markdown filename stem. Intake targets still go throughscan; a PDF written directly into the library is also found byscan; a PDF outside both directories needsbob highlights sync <PDF>.scanruns the configuredhighlights.pre_scan_hookon writing runs, then moves pending PDFs fromxlib/<rel>tolib/<rel>and recursively syncs the library. Pass-n, --no-hooksonscanordoctor, or before the subcommand asbob highlights --no-hooks scan, to ignore the hook. By default it does not write PDF markers; usescan --dry-run --write-pdfs, review, thenscan --write-pdfs.-v, --verboseprints the detailed per-PDF plan instead of the concise report.sync <pdf>updates one reference note from the page-1 marker and sidecar.marker <pdf>inspects that marker without writing.doctorchecks vault paths, intake, sidecars, markers, Git, pandoc, the web-clip chain (uv, adapter, browser, headed fallback), and optionalobwithout writing. Pass-n, --no-hooksto skip the pre-scan hook check.
Generated notes live under ref/. Nested library PDFs such as
lib/books/foo.pdf write ref/books/foo.md with type: "[[ref]]" and
ref_type: books. The generated ^ref task is the visible lifecycle control.
Marker status values are ready, next, wip, read, abandoned, and
legacy.
The full contract and MacBook setup guide live in
docs/highlights-ref-sync.md; the web-capture
contract lives in docs/highlights-clip.md.
bob nightlyRuns the nightly Bob maintenance path. It acquires the shared vault-sync lock
(default $XDG_RUNTIME_DIR/bob_sync.lock when that variable names an existing
directory, otherwise /tmp/bob_sync.lock), then:
- Runs
bob vault-syncagainst the vault. - Runs
bob move-done-tasksagainst the vault. - Runs
bob vault-syncagainst the vault again.
A failed step is reported but does not prevent later steps from running. If
another Bob maintenance run already holds the lock, the command exits 0 after
printing that it is already active. bob nightly accepts no options other than
-h, --help.
bob vault-sync [run] [-n|--dry-run] [-m|--message MESSAGE] [-q|--quiet]
bob vault-sync status [-j|--json]Runs one Git reconcile cycle for the Bob vault. The default subcommand is
run, so bob vault-sync --dry-run is accepted. A cycle acquires the shared
maintenance lock, recovers interrupted merge/rebase/cherry-pick state, commits
local vault changes when present, fetches and merges origin/master, resolves
supported conflicts by writing local conflict copies under _conflicts/, and
pushes with bounded non-fast-forward retries.
The command refuses to stage any file at or above 95 MiB, warns for files at or
above 50 MiB, and writes a status record after each non-dry-run cycle. Use
bob vault-sync status --json for the machine-readable record containing the
last attempt/success timestamps, local and remote SHAs, committed-file count,
push retries, duration, conflict-copy paths, interrupted-merge recovery flag,
and last error.
If another maintenance command already holds the lock, bob vault-sync run
exits 0 silently.
The operational runbook for the two-machine Bob vault sync channel lives in
docs/vault-git-sync.md.
bob move-done-tasks [-t|--threshold N]Scans the Bob vault for completed ([x]) and canceled ([-]) Markdown task
blocks containing #task, then moves blocks from notes that meet the threshold
into matching archive notes under done/. The default threshold is 10; use a
smaller value for a targeted collection pass, such as -t 1 in a
fixture vault.
Archive paths mirror the source note path and add _done to the file stem. For
example, projects/foo.md archives to done/projects/foo_done.md. Archive
notes are created with parent pointing at the original source note plus
type: "[[done]]", such as parent: "[[projects/foo]]" and
type: "[[done]]". Existing archive notes have parent and type frontmatter
inserted or repaired before new blocks are appended. Source notes that have a
matching archive note are linked back to it with done_tasks, such as
done_tasks: "[[done/projects/foo_done]]". Existing archive notes under done/
are backfilled into source note frontmatter and archive metadata on future runs
even when no task blocks meet the threshold.
When task blocks with explicit Obsidian block ids are moved, links to those
blocks are repaired across vault Markdown notes. For example,
[[projects/foo#^abc123]], ![[projects/foo#^abc123]], and aliases such as
[[projects/foo#^abc123|follow-up]] are rewritten to
[[done/projects/foo_done#^abc123]]. Moved block ids are de-duplicated within
their destination archive note before link repair. If ^abc123 already exists
in done/projects/foo_done.md, the moved id becomes the smallest available
suffix such as ^abc123-1, and repaired links point at that final id. If
multiple moved blocks originally share the same id, their archived ids are still
made unique, but existing links to the original duplicate id are left unchanged
because the intended block is ambiguous. Only explicit ^block-id targets can
be rewritten; heading links and tasks without block ids do not have a stable
target to repair.
Task dependency metadata has a separate vault-wide identity from its Obsidian
block link. A task at projects/foo.md#^abc123 uses
[id:: projects__foo__abc123], and dependents use the same value in
[dependsOn:: projects__foo__abc123]; the trailing block token remains
^abc123. When a task moves, the command rewrites its [id::] to the archive
path/final block ID and repairs exact dependency tokens across all planned
files. Metadata and link repair share the same atomic preview/write plan.
The command itself does not reconcile the full vault; bob nightly runs
vault-sync before and after invoking it. In a Git worktree, the command stages
only the files it touches, commits with a bob move-done-tasks YYYY-MM-DD
message, and pushes. Existing uncommitted changes in touched source, archive,
or link-repair files are included in that scoped commit after the command
rewrites those files. Non-Git vaults are left uncommitted.
bob pomodoro [-d|--debug] [-s|--show-stale] [-v|--verbose]Prints the current Pomodoro ledger entry from today's Bob daily note, including
time remaining or recent overdue status. It defaults to
$BOB_DIR/YYYY/YYYYMMDD.md, or ~/bob/YYYY/YYYYMMDD.md when BOB_DIR is
unset, unless BOB_DAY_FILE is set.
Ledger entries may use bold Markdown ranges such as
(**0945-1015** [t:: 30m]); command output remains plain, for example
0945-1015 Review crate skeleton.
The command exits successfully with no output when the daily note is missing,
has no open Pomodoro, or the open Pomodoro is more than nine minutes overdue.
Pass -s / --show-stale when a consumer needs to distinguish an old open
entry from no open entry; stale open Pomodoros keep the same normalized
[OVERDUE by <minutes>m] HHMM-HHMM <task> output shape as recent overdue
Pomodoros. -d, --debug and -v, --verbose enable debug tracing on stderr.
bob notify [-v] PRE_CHECK_SLEEP POST_NOTIFY_SLEEPPolls Pomodoro status until the current entry is overdue, then sends a desktop
notification when notify-send is available and rings the terminal bell three
times. PRE_CHECK_SLEEP is the seconds to wait between status checks;
POST_NOTIFY_SLEEP is the seconds to wait after a notification before polling
again. Polling uses the same default status as bob pomodoro without
--show-stale: an entry more than nine minutes overdue looks like no open
Pomodoro, so start bob notify while the session is still running or only
recently overdue. Loop status messages always go to stderr. -v / --verbose
may be repeated; extra debug tracing is emitted at -vv. Help text still uses
the legacy binary name bob_notify.
bob tmux-pomodoroPrints Pomodoro status in tmux status-line format: the regular status followed
by |. Missing or stale Pomodoros produce no status. When today's daily
note has a Pomodoros section, the plan-budget meter plan T/Tc · L/Lc is
appended after the status (or alone when there is no status), wrapped in
#[reverse]...#[noreverse] when over the cap. With BOB_CLI_USE_SCRIPT=1
the script fallback stays budget-less.
The installed legacy binaries map to the preferred interface as follows:
| Compatibility binary | Preferred command |
|---|---|
bob_notify |
bob notify |
bob_pomodoro |
bob pomodoro |
tmux_bob_pomodoro |
bob tmux-pomodoro |
By default they call the same native Rust implementations as the preferred
commands. With BOB_CLI_USE_SCRIPT=1, the notification and Pomodoro commands
and their shims delegate to their embedded shell assets. Native-only commands
ignore the fallback setting. Extracted assets are cached in a version-and-content-specific subdirectory of
$XDG_CACHE_HOME/bob-cli/scripts/. If XDG_CACHE_HOME is unset or empty, the
base is $HOME/.cache; if neither variable is available, Bob uses the system
temporary directory.
Native command execution does not require Bash or Perl. Forced shell fallback
with BOB_CLI_USE_SCRIPT=1 requires Bash, and the Pomodoro-based fallback
scripts also require Perl.
The documented workflows use these external-tool integrations:
obsidianCLI plus a running desktop Obsidian vault with the Dataview plugin only when usingbob query --engine obsidiangitforbob vault-sync, Git-backedbob move-done-tasks, plugin dirty-file checks, and the defaultbob pluginsrepository refresh; remote operations also need the credentials required by the configured remotenotify-sendfor desktop notifications frombob notify; Bob also rings the terminal bell whether or notnotify-sendis available- platform clipboard tools for
bob captureclipboard input:pbpasteon macOS;wl-paste,xclip, orxselon Linux; ortmux show-bufferin a display-less tmux session (seeBOB_CLIPBOARD_CMDbelow for the exact fallback order) pandocandxelatexforbob highlights create; override pandoc withBOB_PANDOC_COMMANDuvplus Google Chrome or Chromium (orBOB_CHROME) forbob highlights clip:uvfetches Python ≥3.10 and the pinned Playwright, Pillow, and nh3 on first run (override the whole spawn withBOB_WEB_CLIP_ADAPTER); on Linux,Xvfblets it retry bot-protected sites headedbashfor the embedded shell fallback and for sourcing~/.ssh-agent-thing; the Pomodoro shell fallback additionally usesperluvforbob gkeep: it fetches Python ≥3.10 and the pinnedgkeepapion first run (override the whole spawn withBOB_GKEEP_ADAPTER)passfor the defaultbob gkeeptoken store (token_commandandtoken_store_commandin thegkeep:config section)
No old chezmoi script files are required after installation. Cargo installs the Rust binaries, and the binaries carry the script assets they need.
BOB_VAULT_SYNC_LOCK_FILE overrides the lock path used by bob vault-sync,
bob nightly, live bob task-status-hooks runs, and bob randomize.
The default path is the same shared bob_sync.lock path used by nightly
maintenance.
BOB_VAULT_SYNC_STATE_FILE overrides the JSON status record written and read
by bob vault-sync. The default is
$XDG_STATE_HOME/bob-cli/vault-sync.json, or
$HOME/.local/state/bob-cli/vault-sync.json when XDG_STATE_HOME is unset.
BOB_CHROME sets the Chrome or Chromium executable the bob highlights clip
adapter launches instead of auto-discovering one.
BOB_CLI_USE_SCRIPT=1 selects an embedded shell implementation where one is
available. See Compatibility shims for the exact command
coverage and cache location.
BOB_CLIPBOARD_CMD is whitespace-split into a command and arguments and takes
priority over platform clipboard tools for bob capture. Without it, capture
uses pbpaste on macOS; on Linux it uses wl-paste --no-newline --type text
under Wayland or xclip -selection clipboard -o under X11, falling back to
xsel --clipboard --output when xclip is unavailable. A tmux session without
a display uses tmux show-buffer. Setting BOB_CLIPBOARD_CMD is also the
recommended deterministic automation and test hook.
BOB_CLIPBOARD_HISTORY_CMD is the portable clipboard-history provider for
counted captures above one. It is whitespace-split like BOB_CLIPBOARD_CMD,
receives the requested total count as its final argument, and must print a UTF-8
JSON array of complete clipboard strings ordered newest first. JSON framing
allows an entry to contain newlines. Bob reads the live clipboard separately,
removes at most the first equal history candidate, and then requires enough
older candidates to fulfill the exact count. A failed command, malformed JSON,
invalid entry, or insufficient result aborts the capture without vault writes.
Without that override, macOS reads Clipy's production sqlite.db history
read-only, validates the required schema, and reconstructs stored UTF-8 text
and file/URL assets rather than using Clipy's truncated display title. Other
platforms have no automatic history provider and report how to configure
BOB_CLIPBOARD_HISTORY_CMD; % and %1 continue to use the portable live
clipboard source alone.
BOB_CONFIG_FILE sets the exact Bob config file. When unset, Bob uses
$XDG_CONFIG_HOME/bob/config.yml, then ~/.config/bob/config.yml. That
file holds the priority windows for p:<N> and bob randomize,
highlights.pre_scan_hook for bob highlights scan and
bob highlights doctor, the gkeep: section for bob gkeep, and the
optional plan: block for bob plan (caps default to 3 themes, 10
links, 15 NEXT tasks, and 10 PENDING tasks; bob plan exits 2 on
invalid values while every other surface falls back to the
defaults).
COLUMNS, when set to a positive integer, is the width bob plugins list
and bob gkeep use when they shorten human table text so each row fits.
Otherwise Bob uses 100 columns. bob randomize lays its task lines out for
a fixed 100 columns.
BOB_DATAVIEW_OBSIDIAN_COMMAND overrides the executable used by
bob query --engine obsidian.
BOB_DATAVIEW_VAULT sets the default Obsidian vault name or ID forwarded to
obsidian eval by bob query --engine obsidian.
BOB_DAY_FILE sets the exact daily note path used by bob pomodoro,
bob tmux-pomodoro, bob notify (via the same status reader), Pomodoro-linked
and Pomodoro-note bob capture requests, bob task-status-hooks, and
bob randomize.
BOB_DIR sets the Bob vault directory. It defaults to ~/bob.
BOB_GKEEP_ADAPTER is the path of an executable that speaks the Keep adapter
protocol and replaces uv run --script … for bob gkeep. It is the test
hook: tests point it at a fake adapter and configure everything else through
a temporary config file.
BOB_HIGHLIGHTS_LIB_DIR sets the Highlights PDF library directory used by
bob highlights. It defaults to lib under BOB_DIR. Relative values are
resolved under the Bob vault; absolute paths and ~/... paths are used as
configured.
BOB_HIGHLIGHTS_PRE_SCAN_HOOK overrides
highlights.pre_scan_hook from ~/.config/bob/config.yml for
bob highlights scan and bob highlights doctor. Non-empty values run with
sh -c from BOB_DIR before intake; an empty value disables a configured
hook. scan --dry-run reports the hook it would run without executing it.
Pass -n, --no-hooks to ignore the hook from every source. The legacy
BOB_HIGHLIGHTS_PRE_SCAN_COMMAND variable is now an error. Bob exports
BOB_HIGHLIGHTS_IN_PRE_SCAN_HOOK=1 to the hook child process.
BOB_HIGHLIGHTS_REF_DIR sets the generated reference note directory used by
bob highlights. It defaults to ref under BOB_DIR.
BOB_HIGHLIGHTS_XLIB_DIR sets the Highlights PDF intake directory used by
bob highlights. It defaults to xlib under BOB_DIR. lib and xlib must
be distinct, non-nested directories so intake cannot move PDFs inside the tree
being scanned.
BOB_NOW overrides the local date and time used for Pomodoro status and default
daily-note selection by bob pomodoro, Pomodoro-linked capture, and
bob task-status-hooks. It also controls capture created/scheduled dates and
clipboard-snippet names, native Tasks-query date calculations, the default
bob move-done-tasks YYYY-MM-DD commit-message date, scheduled-project
visibility, and the timestamped directory name for plugin backups. Supported
formats are YYYY-MM-DD, YYYY-MM-DD HH:MM, and YYYY-MM-DD HH:MM:SS; T
may replace the space. Timezone names and UTC-offset suffixes are not accepted.
An unsupported value is ignored, after which Bob tries DATE and then the
system clock.
BOB_PANDOC_COMMAND overrides the pandoc executable used by
bob highlights create.
BOB_PLUGINS_DIR sets the source repository used by bob plugins. It defaults
to ~/projects/github/bobs-org/bob-plugins.
BOB_PLUGIN_BACKUPS_DIR sets the base directory for backups created before
bob plugins sync overwrites a vault plugin file. It defaults to
~/.local/state/bob-cli/plugin-backups.
BOB_PRIORITY_ROLL_SEED pins the p:<N> scheduled-date roll to a decimal
integer seed so a --dry-run preview matches a real capture. Unset means each
capture rolls independently. bob randomize also reads it as the default base
seed when --seed is omitted.
BOB_WEB_CLIP_ADAPTER is the path of an executable that speaks the web-clip
adapter protocol and replaces uv run --script … for bob highlights clip
and the doctor web-clip rows. It is the test hook, like BOB_GKEEP_ADAPTER.
BOB_WEB_CLIP_KEEP_WORKDIR=1 keeps the bob highlights clip scratch
directory for debugging and prints its path.
BOB_WEB_CLIP_TIMEOUT_SECS sets the overall bob highlights clip adapter
timeout in seconds. It defaults to 300; the first run may download the pinned
Python dependencies.
DATE preserves the legacy date override behavior, including the date used by
bob capture when BOB_NOW is unset. It can be a date command prefix such as
date --utc, or a timestamp in the same formats accepted by BOB_NOW.
NO_COLOR disables ANSI color in native human-readable output that would
otherwise be styled when stdout is a terminal.
XDG_CACHE_HOME is the cache root for extracted shell-fallback assets. See
Compatibility shims.
XDG_CONFIG_HOME is the base directory for the default
$XDG_CONFIG_HOME/bob/config.yml path used when BOB_CONFIG_FILE is unset.
XDG_RUNTIME_DIR is the preferred directory for the default
bob_sync.lock maintenance lock when that path exists as a directory.
Use bob pomodoro, bob notify, bob vault-sync, and
bob tmux-pomodoro for new integrations, and run
bob move-done-tasks when done and canceled task blocks should be archived
from the vault.
The old top-level commands were renamed: bob collect-done is now
bob move-done-tasks, bob dataview is now bob query, bob highlights-ref
is now bob highlights. bob sync, bob bulk-git-commit, and the bob_sync
binary have been retired in favor of bob vault-sync. The old top-level names
are no longer registered.
The original script implementations remain embedded only as a rollback path. New integrations should rely on the native Rust command behavior.
The retired @<route>::<block-id> capture spelling is no longer accepted; use
@<route>^<block-id> for an ordinary task with a requested block ID, and
@<route>:<block-id> for a Pomodoro-linked next task, optionally with
#<pomodoro> to select or create a named Pomodoro. Sub-bullet capture uses
@<route>+<block-id>.
Run the package checks from a clean worktree:
just all
just check-scripts
just package-listRun a local install smoke test:
just install-smokeRun a tmux status smoke test after installing locally:
tmux display-message -p '#(bob tmux-pomodoro)'Before running bob vault-sync in a release smoke test, verify that BOB_DIR
points at the intended vault and that its Git remote can be pushed without
prompts. Before running bob move-done-tasks against the real vault,
verify that ~/bob is the intended vault, inspect git -C ~/bob status --short,
and review any local edits that may be included when touched candidate files are
rewritten.
The default bob query smoke tests are local and headless. Before running
live Obsidian smoke tests, start desktop Obsidian, open the target vault, enable
Dataview, and use the explicit --engine obsidian examples in
docs/dataview.md.
For an end-to-end collection smoke test, install the local binary, run
bob move-done-tasks against ~/bob, then verify that archive notes under
~/bob/done include parent: "[[source]]" for the original note and
type: "[[done]]", source notes include matching done_tasks links and no
longer contain the collected blocks, Obsidian links to moved ^block-id task
blocks point at done/..._done#^block-id, and the vault Git commit was pushed.
| Topic | Document |
|---|---|
| Capture grammar, JSON, and picker protocol | docs/capture.md |
bob query Dataview and Tasks |
docs/dataview.md |
| Task freshness review lease, placement, evaluation, and display | docs/freshness.md |
| Highlights PDF intake and reference notes | docs/highlights-ref-sync.md |
| Web article capture into Highlights intake PDFs | docs/highlights-clip.md |
| Obsidian Sync folder exclusion runbook (historical) | docs/obsidian-sync-exclusions.md |
| Bob vault Git sync runbook | docs/vault-git-sync.md |
| Custom plugin list and vault deploy | docs/plugins.md |
Project ^prj lifecycle and schedules |
docs/projects.md |
| Bulk re-roll of due prioritized tasks | docs/randomize.md |
| Google Keep inbox drain into Obsidian tasks | docs/gkeep.md |
| Pomodoro-driven task status sync | docs/task-status-hooks.md |