bob query¶
bob query runs Dataview source expressions, Dataview DQL, and Obsidian Tasks
queries from the shell. Use it from scripts, smoke tests, and dashboards that
need query results without opening an Obsidian query pane.
The default engine is native, which evaluates queries against the local
Markdown vault without a running desktop Obsidian app. Use --engine obsidian
only when you need exact behavior from the live Dataview plugin; that mode
requires desktop Obsidian to already have the target vault open, and it does
not accept Tasks inputs. An env-gated test harness provides the live Tasks
renderer oracle without making DOM scraping a public query engine.
Stdout is reserved for query results only. Paths, JSON, and rendered Markdown
can be piped into scripts without sync logs, engine warnings, or diagnostics
mixed in. Date-sensitive Tasks filters follow BOB_NOW (then DATE, then the
system clock), matching other Bob commands.
Contents¶
- Examples
- Options
- JSON output
- Manual smoke test
- Native Tasks queries
- Native Dataview queries
- Live Obsidian engine
- Environment
Examples¶
Run a source query and print one vault-relative Markdown path per matching note:
bob query --source '#project and -"archive"'
Run DQL and print source note paths. paths is the default format, including
for TABLE queries:
bob query --query 'LIST FROM #waiting'
bob query --strict-paths --query 'TABLE file.link, status FROM #project'
Use JSON when a script needs metadata and the structured Dataview result:
bob query --format json --query 'TABLE status, due FROM #project'
bob query --format json --query-file ~/queries/projects.dql | jq '.paths'
Render a visible Dataview table. Markdown output requires DQL:
bob query --format markdown --origin Home.md --query 'TABLE file.link FROM #project'
Read a query from a file, or from stdin with -:
bob query --query-file ~/queries/waiting.dql
printf 'LIST FROM #waiting\n' | bob query --query-file -
Run Tasks v8 queries using the vault's plugin settings, custom statuses, global
filter/query, presets, task format, dependencies, and JavaScript by function
instructions. An empty query returns every task allowed by the global filter:
bob query --tasks ''
bob query --tasks 'status.type is TODO' --origin dash.md
bob query --format json --tasks-file queries/all.tasks
printf '# all globally-filtered tasks\n' | bob query --tasks-file -
Run every fenced tasks block in a note with that note's TQ_* Query File
Defaults and query.file.* context:
bob query --tasks-note dash.md
bob query --format json --tasks-note dash.md
bob query --format markdown --tasks-note dash.md
Options¶
-b, --bob-dir <PATH> sets the Bob vault root. It defaults to BOB_DIR or ~/bob.
The path is validated when it is supplied explicitly or when the native engine
is used.
-e, --engine <native|obsidian> selects the query engine. The default is
native, the local headless implementation of Bob's supported Dataview and
Tasks surface. obsidian evaluates Dataview through the live plugin and is
useful as an oracle or fallback when installed-plugin behavior matters. Tasks
inputs reject the Obsidian engine because its DOM-rendering mechanism has a
smaller, less stable output contract; use the live parity harness below as the
Tasks oracle.
-f, --format <paths|json|markdown> selects the output format. paths is the
default and prints matching source note paths for Dataview results or unique
note paths containing matched Tasks results. Use json for structured output
and markdown for rendered Dataview DQL or static Tasks output. For
--tasks-note, paths output labels each block before its unique paths, JSON
contains a blocks array, and Markdown gives each block its own heading.
-o, --origin <VAULT_RELATIVE_PATH> sets the origin note for Dataview this
and relative links. For --tasks and --tasks-file, it supplies
query.file.*, placeholder values, and the note's TQ_* Query File Defaults.
It must be vault-relative; absolute paths and .. traversal are rejected. It
cannot be combined with --tasks-note, whose note supplies its own origin.
-q, --query <DQL> runs an inline Dataview DQL query.
-Q, --query-file <PATH> reads Dataview DQL from a file. Use - to read the
query from stdin.
-s, --source <SOURCE> runs a Dataview source expression with pagePaths().
-S, --strict-paths makes paths output fail if note paths cannot be derived
cleanly from every DQL row. Without it, best-effort path extraction warnings go
to stderr and the command prints the paths it can derive.
-t, --tasks <QUERY> runs an inline Obsidian Tasks query. Newline-separated
instructions are accepted. Empty or comment-only input returns every task
allowed by the configured global filter. User-supplied Tasks queries reject
the native-only status.symbol filter, as Tasks 8.4.0 does.
-T, --tasks-file <PATH> reads an Obsidian Tasks query from a file. Use - to
read the query from stdin.
-n, --tasks-note <VAULT_RELATIVE_PATH> extracts and runs every fenced tasks
block in the note. Each block uses the note as its origin and is identified by
its nearest preceding Markdown heading, one-based block index, and zero-based
fence lineNumber in JSON (human output displays one-based line numbers).
-v, --vault <NAME_OR_ID> forwards an Obsidian vault name or ID to the
Obsidian CLI. It can only be used with --engine obsidian. If omitted in Obsidian mode,
BOB_DATAVIEW_VAULT is used when set.
Exactly one of -s|--source, -q|--query, -Q|--query-file, -t|--tasks,
-T|--tasks-file, and -n|--tasks-note is required.
bob query does not reconcile the vault. Vault freshness is owned by
bob vault-sync and the configured background or cron path.
JSON Output¶
Dataview JSON output is a stable object for scripts. It includes:
engine:nativeorobsidianquery_kind:sourceordqlformat:jsonpaths: extracted vault-relative note pathsresult: structured DQL data for DQL querieswarnings: path extraction or compatibility warnings
Single-query Tasks JSON uses the same wrapper with query_kind: "tasks". Its
result contains the matched task count and a full record for each task:
parsed status, descriptions, dates (including invalid-date state), priority,
recurrence, dependency and block-link metadata, tags, file and heading context,
list hierarchy, blocked/blocking state, and urgency. Line numbers are
zero-based, matching the Tasks plugin scripting API. settings contains the
Tasks settings that governed the scan. Metadata is parsed using the configured
Tasks format (dataview or tasksPluginEmoji).
Dependency equality follows Obsidian Tasks exactly and is vault-wide. Bob uses
<note-path-with-slashes-as-__>__<block-id> for [id::] and
[dependsOn::], while the navigation block remains file-local. For example,
projects/Shared.md#^review is represented as
projects__Shared__review. This lets separate notes reuse ^review without
colliding in blocked/blocking calculations.
Whole-note JSON uses query_kind: "tasks_note", includes note, the union of
matched paths, and one entry per code block in blocks. Each block has
index, zero-based lineNumber, heading, raw query, parsedQuery, paths,
and the same structured result object as a single Tasks query.
Manual Smoke Test¶
For local smoke tests, adjust tags or folders to values that exist in the vault.
bob query --source '#project'
bob query --query 'LIST FROM #project'
bob query --format json --query 'TABLE file.path FROM #project'
bob query --format markdown --origin Home.md --query 'TABLE file.link FROM #project'
printf 'LIST FROM #project\n' >/tmp/bob-dataview-smoke.dql
bob query --query-file /tmp/bob-dataview-smoke.dql
bob query --tasks ''
bob query --format json --tasks '' | jq '.result.count'
bob query --tasks 'status.type is TODO' --origin dash.md
bob query --format markdown --tasks-note dash.md
If the smoke test needs recently synced state, let the external background or
cron sync path finish first. bob query only reads the current vault state.
The fixture parity harness compares supported native fixture queries against the live Obsidian engine. It is intentionally gated because it requires desktop Obsidian to be running with the fixture vault open:
BOB_DATAVIEW_PARITY_LIVE=1 \
BOB_DATAVIEW_PARITY_VAULT=<opened-fixture-vault-name-or-id> \
cargo test --test dataview_parity dataview_live_obsidian_parity_harness_compares_supported_native_cases -- --nocapture
The Tasks parity fixture and filterless goldens run without Obsidian:
cargo test --test tasks_parity
The Tasks live oracle renders a fenced block through Obsidian's
MarkdownRenderer, waits for the Tasks plugin's asynchronous DOM output, and
scrapes descriptions, status symbols, backlink targets, group headings, and
render errors. It compares a dashboard-style block with the native result. The
test skips cleanly when the desktop command is unavailable; when enabled, the
fixture vault must be open in Obsidian with Tasks enabled:
BOB_TASKS_PARITY_LIVE=1 \
BOB_TASKS_PARITY_VAULT=<opened-fixture-vault-name-or-id> \
cargo test --test tasks_parity tasks_live_obsidian_parity_harness_renders_and_scrapes_tasks_blocks -- --nocapture
Real-vault Tasks acceptance¶
The gated real-vault acceptance test first snapshots the vault's Markdown and
installed Tasks settings so background sync cannot change the inputs between
queries. It then independently scans raw task lines without calling the native
Tasks parser or filter engine. It derives the expected WIP, NEXT, and READY
sets using the dashboard's global-filter, status, scheduled-date, #hide,
_templates, dash.md self-exclusion, and dependency-blocking rules. It
compares those sets by path, zero-based line number, and status symbol with both
--tasks-note dash.md and the three individual --tasks ... --origin dash.md
executions. It also executes every other fenced Tasks block in the snapshot and
verifies that none are skipped.
Pin BOB_NOW so scheduled-date behavior remains reproducible:
BOB_TASKS_REAL_VAULT_PARITY=1 \
BOB_DIR="$HOME/bob" \
BOB_NOW=2026-07-10T12:00:00 \
cargo test --test tasks_real_vault_parity -- --nocapture
The 2026-07-10 acceptance run matched all three independently derived sets: 6 WIP, 8 NEXT, and 40 READY tasks. All 13 other Tasks blocks present in the vault also parsed and executed successfully. The phase design's earlier inventory of 14 non-dashboard blocks had changed by acceptance time.
Desktop Obsidian was unavailable for that run, so live DOM-renderer
confirmation of the real dash.md remains a manual acceptance check. Native
Tasks queries intentionally remain read-only and do not render or mutate
interactive toolbar, edit, postpone, recurrence-generation, or completion
actions; Tasks inputs also remain unsupported by the public --engine
obsidian surface. These are explicit non-goals, not silent native-query
fallbacks.
For real-vault native smoke tests, use read-only queries against ~/bob. These
cover the supported local surface without requiring Obsidian:
bob query --strict-paths --query '
LIST
FROM "ref"
WHERE source_pdf
AND (
parent = [[ai_ref]]
OR parent.parent = [[ai_ref]]
OR parent.parent.parent = [[ai_ref]]
OR parent.parent.parent.parent = [[ai_ref]]
OR parent.parent.parent.parent.parent = [[ai_ref]]
)
'
bob query --source '#project'
bob query --source '"prj"'
bob query --strict-paths --query 'LIST FROM "prj" LIMIT 5'
bob query --strict-paths --query 'TABLE file.mday, parent FROM "prj" LIMIT 5'
bob query --strict-paths --query 'TASK LIMIT 3'
bob query --strict-paths --query 'CALENDAR file.day WHERE file.day LIMIT 5'
bob query --format markdown --query 'LIST FROM "prj" LIMIT 3'
bob query --format markdown --query 'TABLE file.mday, parent FROM "prj" LIMIT 3'
bob query --format markdown --query 'TASK LIMIT 3'
Native indexing may warn about ambiguous bare wikilinks when multiple notes share the same stem or alias. Those warnings are diagnostics about vault links; they do not make otherwise successful read-only smoke queries fail.
Native Tasks queries¶
Tasks queries use the vault's Tasks settings and local Markdown index; they do not require Obsidian or a separate JavaScript runtime. Ordinary filters, sorts, groups, and dependency queries run without building a JavaScript sandbox.
filter by function, sort by function, and group by function run in an
embedded QuickJS sandbox with the vendored Moment library. A Boolean
expression that contains one of those instructions also enables the sandbox.
The same check applies to a --tasks-note block, a query-file default, and
the configured global query. Those instructions are merged into the query,
so a global query of filter by function โฆ builds the sandbox even when
the typed query is an ordinary filter. Each expression has a 2-second budget.
Initialization has its own budget: 10 seconds plus 5 milliseconds per
indexed task, capped at 120 seconds. The sandbox does not load desktop
plugins and is not DataviewJS. Prefer standard filters for simple queries,
and narrow expensive JavaScript expressions when a query times out.
For example, the ordinary status filter and a custom expression use the same Tasks registry:
bob query --tasks 'status.type is IN_PROGRESS'
bob query --tasks 'filter by function task.status.type === "IN_PROGRESS"'
Queries that depend on desktop-only plugin helpers can omit tasks or fail
headlessly. Use bob freshness and bob plan for Bob's supported review
and ledger reports.
Native Dataview queries¶
Native mode runs against the local Markdown index. It does not call Obsidian or the live Dataview plugin.
bob query --strict-paths --query '
LIST
FROM "ref"
WHERE source_pdf
AND (
parent = [[ai_ref]]
OR parent.parent = [[ai_ref]]
OR parent.parent.parent = [[ai_ref]]
OR parent.parent.parent.parent = [[ai_ref]]
OR parent.parent.parent.parent.parent = [[ai_ref]]
)
'
The native engine supports source expressions, LIST, TABLE, TASK, and
CALENDAR JSON results, common Dataview expressions/functions, and ordered data
commands such as FROM, WHERE, SORT, GROUP BY, FLATTEN, and LIMIT.
Native evaluation shares container values instead of deep-copying them. FLATTEN
still materializes one output row per expanded value, so command order matters:
a later WHERE or LIMIT cannot shrink work that an earlier FLATTEN already
expanded. Narrow FROM or a page-level WHERE before FLATTEN. Each native
FLATTEN stage stops with a query error (exit code 1, no partial stdout) if it
would exceed 100,000 rows. That cap is an expansion guard, not a process memory
ceiling: indexing the vault and serializing a huge result can still use
substantial memory. Run large native queries serially on memory-limited hosts.
For task results, prefer a DQL TASK query over flattening file.tasks:
TASK
FROM "Projects"
WHERE !completed
--query 'TASK ...' is Dataview DQL. --tasks uses a different Tasks-plugin
grammar and is not a DQL query.
For a compact status census of tasks on matching pages:
TABLE WITHOUT ID Status, length(rows) AS Count
FROM "Projects"
WHERE file.tasks
FLATTEN file.tasks AS t
GROUP BY t.status AS Status
SORT Status ASC
Native paths output prints matching source note paths where a DQL result
retains source identity. Native json output emits the stable Bob wrapper.
Native markdown output renders DQL LIST, TABLE, and TASK results and
fails cleanly for CALENDAR, matching Dataview's Markdown export behavior.
Native mode remains scoped to the shell contract exposed by bob query. It
does not implement DataviewJS, inline DQL modes, Obsidian DOM rendering,
interactive task checking, or every plugin setting. Quoted native sources
resolve an exact note path first and otherwise act as folder sources; use a
more specific folder path when a vault contains both Name.md and Name/....
Live Obsidian engine¶
Use --engine obsidian when a query needs the exact installed Dataview plugin.
The target vault must already be open in desktop Obsidian, and the Dataview
community plugin must be enabled.
bob query --engine obsidian --source '#project'
bob query --engine obsidian --format markdown --origin Home.md --query 'TABLE file.link FROM #project'
Set BOB_DATAVIEW_OBSIDIAN_COMMAND to use a specific Obsidian CLI executable.
Set BOB_DATAVIEW_VAULT or pass --vault to choose the vault name or ID
forwarded to obsidian eval.
Environment¶
| Variable | Role |
|---|---|
BOB_DIR |
Vault root; defaults to ~/bob. Validated when --bob-dir is passed or the native engine is used. |
BOB_NOW |
Date/time override for native Tasks date calculations and other Bob date logic. |
DATE |
Legacy date override used when BOB_NOW is unset. |
BOB_DATAVIEW_OBSIDIAN_COMMAND |
Executable for --engine obsidian. |
BOB_DATAVIEW_VAULT |
Default vault name or ID forwarded to obsidian eval. |
Parity harness variables (BOB_DATAVIEW_PARITY_LIVE, BOB_DATAVIEW_PARITY_VAULT,
BOB_TASKS_PARITY_LIVE, BOB_TASKS_PARITY_VAULT, BOB_TASKS_REAL_VAULT_PARITY)
are test-only and are documented in Manual smoke test.