Skip to content

Getting started

Bob is a CLI for an Obsidian vault that uses #task checkboxes and a daily Pomodoro ledger. Basic capture and native queries run without desktop Obsidian. Dashboard badges, review keymaps, and the Task Card are provided by the custom Bob plugins; installing the CLI alone does not install that interface. Ready, Next, and Pending name task statuses ("lanes"). Pending is another name for In Progress ([/]), not a waiting or Blocked state.

Install and select a vault

From a checkout, run just install. Run just install-all to also deploy the Bob plugins (and, on macOS, Bob Mac Capture) from sibling checkouts (offering to clone any that are missing), restarting a running Obsidian when the plugins change (on macOS that restart posts a notification first). With Cargo alone, run:

cargo install --path . --locked
bob completion install

See the installation guide for remote installation and completion for Bash/Zsh setup. Completion installation does not edit shell rc files.

Select an existing vault directory and inspect it:

export BOB_DIR=/path/to/your/bob-vault
bob --help
bob capture-targets
bob projects list

The default vault is ~/bob. Commands that accept --bob-dir let you override it for one invocation. bob does not bootstrap a vault, its daily notes, Obsidian settings, or Git remote.

Vault requirements

Bob writes Dataview-style task metadata such as [created::2026-10-04] and [scheduled::2026-10-05]. Configure Obsidian Tasks to use the Dataview task format and the #task global filter. Its status registry should include:

Checkbox Bob meaning Tasks type
[ ] Ready TODO
[*] Next ON_HOLD
[/] In Progress, also called Pending or WIP IN_PROGRESS
[?] Blocked ON_HOLD
[x] Done DONE
[-] Canceled CANCELLED

The CLI reads settings from .obsidian/plugins/obsidian-tasks-plugin/data.json. Without that file, native Tasks queries use the default emoji format; bob freshness and bob ready refuse a non-Dataview format. Blocked transitions also require the compatible ? definition described in task status hooks (bob task reconcile; formerly bob task-status-hooks, still accepted).

Capture routes select root-level notes: @work writes work.md. Route names are lowercased and accept ASCII letters, digits, _, and -; picker-discovered filenames must already have lowercase stems. A missing target note is created when needed, and an existing ## Tasks section is the preferred insertion point. Ordinary capture does not create that heading or give the note an area/project type. The : task-link and + parent-task pickers discover the inbox, area notes, and non-terminal project notes. Give area/project notes the appropriate frontmatter type ("[[area]]" or "[[project]]") to include them in those catalogs and in per-note Ready reports. Nested prerequisites use a separate path-aware & syntax; see dependency capture.

For the desktop interface, use the plugin deployment guide: select a local bob-plugins source checkout, deploy with bob plugins sync, then enable the installed plugins in Obsidian's Community plugins settings. Review requires Bob Ledger Tools and Bob Navigation Hotkeys. Sync copies plugin assets; it does not enable plugins or create dashboard notes.

Capture a first task

Preview the exact task, then write it:

bob capture --dry-run 'Write an outline @mac_inbox^outline'
bob capture 'Write an outline @mac_inbox^outline'
bob query --tasks '' --format markdown

The task lands in mac_inbox.md with a created date and ^outline block ID. New tasks have no freshness stamp: they await human confirmation. Use a unique ID for each task in a note. Reusing this creation command does not edit the existing task; a duplicate requested ID is an error.

For an ordinary task without an ID, bob capture 'Buy milk' uses the same inbox. An eligible bare public URL queues a reference job instead of creating a task; use bob capture --no-ref 'https://example.com/article' to keep it in the inbox. For several tasks, separate items with blank lines; later bullet lines within an item become children. The batch is planned before it writes, so a failed item aborts the capture. See Capture for routing, clipboard, scheduling, prerequisites, and JSON interfaces.

Plan and run a session

Use today's daily note at YYYY/YYYYMMDD.md inside the vault (for example, 2026/20261004.md on October 4), or set BOB_DAY_FILE to its full path. Create the note if needed. Add a Pomodoros section with an unindented open entry like the one below; linking needs an open entry, and the bare =3 start needs an untimed () placeholder with no other timed session open.

---
parent: "[[day]]"
---
# Daily note

## Pomodoros

- [ ] () โ€” WORK

Link the task created above to that session, then start it for 15 minutes:

bob capture '@mac_inbox:outline'
bob plan
bob capture '=3'
bob pomodoro

The marker-only @mac_inbox:outline links the existing task and raises Ready to Next. A Task Link is the dedicated [[mac_inbox#^outline]] child bullet in the daily note; the task itself stays in mac_inbox.md. =3 starts the queued session's timer and leaves the task Next; the close records its work outcome. Units are five minutes, and bare = means 25 minutes. Quote session tokens in Zsh. Linking and starting can also be one transaction: bob capture '@mac_inbox:outline=3'.

When finished, preview bob capture --dry-run 'x' to see the session's numbered Task Links and proposed outcomes, then submit one close command:

Command Outcome
bob capture 'x' Close; ordinary plain links count as worked, eligible tasks become Pending, and their links carry to the next session
bob capture 'x*' Close; park all numbered links, eligible tasks become Pending, and none of those links carry to the next session
bob capture 'x!' Close and complete every numbered Task Link
bob capture 'x1!2' Keep task 1 in progress, complete task 2, and defer the remaining links

For this one-task example, use x! if the outline is done, or x* if it still needs work but should leave today's queue. The numbered x1!2 example requires at least two links. "Deferred" means carry the link to the next session without changing the task's lane or scheduled date. Plain x also respects existing link markers: a trailing # defers a link, while an embed (![[note#^id]]) requests completion. Unresolved or ineligible targets can be skipped with warnings; check the capture report. See close outcomes for the full rules.

When the close completes a planned task, Bob recovers newly unblocked dependents immediately and offers them as successor offers (plan.link_unblocked: true by default). Nothing is auto-linked; pick which tasks to pull in through the Obsidian picker, the Bob Mac Capture choice step, or bob capture -p/--pull-in. There is no five-successor cap. If the project has no remaining work under the project surfacing rules and no open sub-projects, its completion tracker (^prj) is offered the same way after #hide is removed, raising Ready or newly unblocked trackers to Next only when picked, and preserving In Progress. You still decide whether the project is complete. Future dates and remaining prerequisites or inherited project blocking prevent that offer. See completion follow-ups and project pull-in for eligibility.

Inspect bob task reconcile --dry-run, then run bob task reconcile to reconcile statuses and clean up links. Next and Pending are sticky: unlinking a task keeps its lane. The explicit release gesture in Obsidian is Alt+N. Blocked ([?]) is derived from open prerequisites, a future scheduled date, or project blocking. A project's prerequisites and date live on its ^prj completion tracker. While those inputs block the project, its other open tasks become Blocked and carry #prj_is_blocked. Blocking also passes through sub-projects linked by their parent frontmatter, including their ^prj lines and open tasks. See task dependencies ยง13.

With the cursor on a Pomodoro Task Link, Alt+[ / Alt+] toggles the linked task between Next and In Progress (asking once for an optional Work Log summary when moving back to Next); In Progress targets show a rendered โ— mark in today's daily note.

Review the backlog

bob plan
bob freshness list --limit 10
bob ready

bob plan reports today's themes and Task Links plus Next/Pending pressure. bob freshness shows the due review queue in PRE โ†’ NEW โ†’ PROJECTS โ†’ PENDING โ†’ NEXT โ†’ RECURRING โ†’ TICKLER โ†’ ROTTEN โ†’ POST order. These are review groups, separate from task statuses. A visible recurring occurrence joins RECURRING when the earliest of its scheduled, due, and start dates arrives; complete it in Obsidian so Tasks writes the next occurrence. Recurring tasks never receive a freshness confirmation date ([fresh:: YYYY-MM-DD], a "stamp"). An ordinary Ready task with a stamp is a TICKLER when a later scheduled date arrives, or its recorded [unblocked::] date is on or after the stamp and no later than today. Without a stamp, it stays NEW. Due project completion trackers (^prj) use PROJECTS instead.

bob ready counts each area's or project's visible, non-recurring Ready tasks, including NEW and ROTTEN, and excludes its ^prj tracker. The dashboard READY backlog filters out those review buckets and Today-linked work. Today means linked under an open Pomodoro in the selected daily note; it is not a task status. Today-linked tasks and tasks in canonical daily notes (YYYY/YYYYMMDD.md) are excluded from ordinary review. Checklist rows tagged #gtd #pre or #gtd #post still walk in PRE or POST in those places, even when recurring; resolve them by completion. Hidden, blocked, and future-scheduled tasks stay out.

bob stats is a separate read-only glance at derived daily metrics. bob themes lists remembered Pomodoro names and their stable hues.

Use bob ready NOTE for one note's worklist, or bob ready --check to exit 3 when notes are crowded.

With the review plugins enabled in Obsidian, [S or ]s from outside the queue starts at PRE. Every gesture that answers the landed row advances the walk once to the next remaining item in the same keystroke: Ctrl+Enter completes (never crossing the PRE/POST boundary โ€” Ctrl+Alt+F does), Ctrl+Alt+F keeps an ordinary non-recurring task, Alt+N releases, Ctrl+Shift+Enter picks a Pomodoro (โ†ต takes the current/next one), and a resolving Task Card commit moves on. On an inbox task, every non-closing Task Card answer and Ctrl+Shift+Enter asks where the task goes as the last step before writing, then answers and moves there. Ctrl+Shift+M never advances: it follows the moved task to its destination note, and the next ]s resumes the walk. Alt+F is the one answer that stays, ]s skips, and <C-o> returns to the row just answered. Away from the current review task, ]s / [s first bring you back to it. Review the commitment groups from PRE through TICKLER until the footer says Commitments done. Use Ctrl+Alt+F to keep an ordinary task or complete a PRE/POST checklist row. On RECURRING it writes nothing and stays; complete the occurrence with Ctrl+Enter, reschedule it into the future, link it to Today, or cancel it. ]s skips a row without resolving it. ROTTEN follows for backlog upkeep. ]S jumps to POST; complete the Morning review checklist last with Ctrl+Enter or Alt+F (on a checklist, Alt+F completes the row). Ctrl+Alt+J/K also walks due tasks. Alt+F confirms an eligible non-recurring, non-checklist task under the cursor. ]s / [s are the equivalent configured Vim bindings, not shortcuts installed by the CLI. On those ordinary tasks, Ctrl+Alt+F confirms and advances (Control+Option+F on a Mac). Hand edits alone do not stamp freshness. bob freshness list only reports the queue; it does not confirm tasks. bob freshness seed is a one-time migration for an existing backlog; it is not the command to confirm today's new captures. See Freshness for review gestures and tracker cadence.

Optional config lives at ~/.config/bob/config.yml, selected by BOB_CONFIG_FILE or XDG_CONFIG_HOME. Plan documents caps; Freshness documents review intervals. BLOCKS lines documents dependencies.blocks_lines, and Task relations documents relations.linked_from_lines. $1โ€“$4 capture and bob task reroll also require the priority configuration.

Save and read references

Bob's reference pipeline files the PDF into lib/, writes its note under ref/, and creates a reading task. A bare link capture does this in a background job. The success line says queued โ€ฆ โ†’ reading queue as soon as the job is saved. If intake cannot run, the PDF remains in xlib/ and the host's local intake timer or bob ref scan -i finishes filing it.

bob capture --dry-run 'https://example.com/article'
bob capture 'https://example.com/article'
bob ref jobs
bob ref find 'https://example.com/article' --include-intake

The capture preview reads local state and writes nothing. The submit queues the job; the worker fetches the link independently of the capture process. bob ref jobs shows that job. While the PDF is queued in xlib/, find --include-intake can locate it. Once filed, plain bob ref find finds the note.

An eligible URL fills the capture item, optionally with a parent route and a listen marker: URL @route, URL %, or URL % @route. Put the standalone % immediately after the URL to request companion audio; URL @ is an alias. Extra prose, child bullets, or other capture markers keep it a task. Hosts without a dot, such as http://go/x and localhost, IP literals, and the default excluded hosts (google.com, googleplex.com, youtube.com, youtu.be, github.com, x.com, twitter.com, and their subdomains) stay tasks. Use --no-ref for a task, or configure the URL routing policy.

When the job's state is clipped, preview and run the scan, then open the reading queue:

bob ref scan --dry-run
bob ref scan
bob ref list

scan previews the whole configured library, so review that preview before writing. Pending jobs and intake PDFs stay out of ref list until the scan creates a reference note. The human and JSON job state for a finished clip is clipped; the done.jsonl record stores outcome: created. If background capture fails, the worker writes the link as an inbox task with a warning and retry command. See ref jobs for recovery.

Check dependencies with bob ref doctor. To import a local PDF, Markdown report, or URL immediately, run bob ref create <TARGET> -P <area-or-project>. This writes the intake PDF; then run the scan steps above. Local PDFs are stamped as-is; Markdown needs pandoc, XeLaTeX, DejaVu fonts, and LaTeX packages; web articles need uv and a browser. bob ref create --dry-run skips installing the final PDF, and a web article, PDF URL, or arXiv target still contacts the network. Use bob capture --dry-run for an offline link preview. See the target guide. Lookup commands require the configured ref/ directory to exist.

To read a note's annotations and your own comments, use its path or an exact identifier returned by find or list:

bob ref show ref/blogs/article.md
bob ref show ref/blogs/article.md --comments-only --format markdown

The path above is illustrative; use the actual note path Bob reports.

What commands change

Commands Effects
query, plan, ready, freshness list, stats, capture discovery, capture-complete, projects list Read local vault state
capture-parse Classify the draft without opening the vault. It reads the URL routing config unless --no-ref is set; it does not validate vault targets
capture-rewrite Rewrite the draft in memory. It does not open the vault, the clipboard, or the URL routing config
ref find, ref list, ref show, ref today --dry-run, ref jobs [list], ref migrate-tasks, ref migrate-zorg without --write Inspect local references, jobs, or a migration plan without vault writes
capture, capture-task-id, capture-pomodoro-name, projects sync, task reconcile, freshness seed, ref today Write vault notes; a bare public link queues a job that later writes an intake PDF or a fallback task; URL % asks that job for companion audio; capture --dry-run is offline
vault-sync, nightly Reconcile the vault with Git, including commits, merges, and pushes
task archive Archive task blocks and repair links; in a Git vault, commit touched files and push
task move Move open tasks and repair vault references; --dry-run previews without state changes; live Git runs sync before/after and commit touched notes; --offline commits locally
task reroll Re-roll schedules; in a Git worktree, sync before/after and commit rewritten notes; --offline skips sync but still commits locally
plugins list, plugins sync Pull the plugin source repo by default, including for sync --dry-run (--no-pull skips); sync deploys plugin assets with backups. -f json prints one object instead of the table
gkeep list, gkeep pull Contact Keep; pull writes inbox tasks or intake PDFs, commits the Keep inbox note when tasks were written unless --no-commit, then archives verified Keep notes unless --no-archive; a clips-only pull skips that commit; pull --dry-run still contacts Keep and does not clip. A valid note @route chooses a clip's parent ahead of -P
gkeep migrate-markers, gkeep migrate-tasks Rewrite Keep task Markdown and local import evidence offline, with no Keep credentials or network. --dry-run previews. In a Git vault, a live run commits changed notes and import evidence unless --no-commit
ref create, ref scan, ref sync Write PDFs and reference notes inline; create --dry-run still downloads or opens URL targets; create --listen also narrates companion audio; writing scans can run a configured pre-scan hook. scan -f json prints one report
ref jobs run Fetch queued links for their stored destination vaults; write intake PDFs or fallback inbox tasks
ref migrate-tasks --write, ref migrate-zorg --write Copy legacy reading records into ref/zorg/ or move open ref tasks into parent notes and commit them; sync before and after unless --offline
completion install, completion uninstall Change shell adapter files and the completion manifest

Native vault reading and basic capture need no Git setup. Git workflows expect the configured remote and credentials; vault-sync uses origin/master. Follow the Git sync runbook before enabling maintenance.

If a command refuses

Symptom What to check
Task absent from a picker Root-level route, inbox/area/project type, project status, task status, and Tasks global filter; & has a broader catalog than :/+
Freshness or Ready reports reject the task format Tasks must use taskFormat: "dataview"; missing settings fall back to emoji
Ready rejects invalid freshness config bob ready shares the freshness scan; fix freshness: as well as plan: config
Linking fails Daily note, Pomodoros heading, open entry, and a unique task block ID
Start says to finish the current Pomodoro Close the running timed entry first; bob capture 'x =3' closes and starts atomically
Hooks refuse a Blocked transition The Tasks registry's ? status must match the required definition
A native query using plugin-dependent dashboard filters omits tasks Native queries do not load desktop Bob plugins; use bob freshness for the headless review queue
A captured link is absent from ref list Inspect bob ref jobs; a clipped intake PDF needs bob ref scan before it has a note
A link stays a task Extra prose, unsupported markers, children, a parent-task destination, --no-ref, a host without a dot, an IP address, an excluded host, or routing disabled in config. A simple @route parent and a % listen marker immediately after the URL are supported
Markdown PDF creation fails Check the pandoc, xelatex, and latex_packages rows in bob ref doctor. Missing packages produce one tlmgr install line in the warnings section. Doctor does not check fonts; install DejaVu Serif, DejaVu Sans, and DejaVu Sans Mono when XeLaTeX cannot find them

Use bob <command> --help for accepted options and the contract index for detailed behavior, output formats, and recovery steps.

Tested rolling snapshot bob-cli 41afa14 bob-mac-capture 556bb67 bob-plugins af1e691 Built 2026-10-24T22:34:13Z (zensical) build.json