Skip to content

Latest commit

 

History

History
1402 lines (1098 loc) · 101 KB

cli.md

File metadata and controls

1402 lines (1098 loc) · 101 KB

CLI Guide

This guide documents the current user-facing Vulcan CLI. It matches the implemented command surface in this repository. For architecture and design decisions, see docs/design_document.md.

Build and first scan

Vulcan's minimum supported Rust version is 1.88. The repository pins Rust 1.88 through rust-toolchain.toml, so a rustup-managed toolchain is selected automatically. Build the release binary from the repo root:

cargo build --release -p vulcan-cli --bin vulcan

The binary is written to:

./target/release/vulcan

Typical first run against a vault:

./target/release/vulcan --vault ~/wikis/mimir index init
./target/release/vulcan --vault ~/wikis/mimir index scan
./target/release/vulcan --vault ~/wikis/mimir --limit 5 query --where 'status = open'
./target/release/vulcan --vault ~/wikis/mimir browse

For whole-vault device synchronization, including a detached Git directory for Android shared storage, see Git-backed device synchronization. Git-over-SSH authentication (the device key, enrollment on vault clone/add, device replace, devices revoke) is covered in Device keys and vulcan help device-keys. vulcan sync run is a finite direct workflow and does not require the daemon.

Daemon conflict automation

A designated daemon can automatically accept only high-confidence singleton Markdown or text conflict groups. This is opt-in at both levels: the daemon has an explicit wiki allowlist and each vault must enable sync.agent_auto_accept in device-local config. The principal commands are:

vulcan daemon config set-agent resolution --base-url <url> --model <model> --api-key-env <name>
vulcan --vault <path> config set sync.agent_auto_accept true --target local
vulcan daemon config set-conflict-worker --wiki <id> --dry-run
vulcan daemon config set-conflict-worker --wiki <id>
vulcan daemon conflict-status
vulcan --output json daemon conflict-status
vulcan daemon config clear-conflict-worker

The configuration is loaded at daemon startup, so restart the daemon after changing the worker. Provider secrets are read from the named environment variable, never stored in daemon config. The worker skips paused or actively syncing wikis, applies at most one conflict per wiki per pass, and retains binary, structural, incomplete, oversized, or otherwise uncertain conflicts for manual review. Pair it with set-semantic-worker when the accepted sync history should also become a human-readable semantic history on main. See the complete setup, status schema, and troubleshooting guide.

vulcan daemon stop is a syncing shutdown, not an immediate kill. It queues a final retained sync for each active Git-backed registration, waits up to 30 seconds, and then cooperatively cancels unfinished final jobs before completing shutdown. Ctrl-C and supported service-manager termination use the same path. After suspend, a detected clock gap queues an immediate resume sync. These paths retain normal pause, permission, conflict, lease, and recovery behavior and cannot promise remote publication after abrupt power loss, a forced kill, or unavailable networking.

For a manually installed portable binary, vulcan self-update check inspects the binary's update channel and vulcan self-update apply --dry-run downloads and verifies an update without replacing the executable. Portable installations can also project a trusted unattended update into the native user scheduler with vulcan self-update schedule install --dry-run; package-managed installations must update through their package manager. See the portable update guide, including its unattended setup, and the update-channel specification for channel selection, platform timing, daemon coordination, current signing status, and trust/downgrade controls.

Self-discovery

The CLI is designed to be self-describing at runtime.

  • vulcan --help shows the top-level command groups.
  • vulcan <command> --help shows command-specific syntax, caveats, and examples.
  • vulcan help shows the integrated topic index, and vulcan help <topic> expands one command or concept.
  • Topics include command docs (help note get), concepts (help filters, help sandbox), and JS/runtime reference entries (help js, help js.vault, help js.plugins).
  • vulcan describe prints a compact command inventory for humans.
  • vulcan --output json describe prints the runtime command schema in machine-oriented JSON.
  • vulcan --output json describe --format openai-tools exports OpenAI function-calling tool definitions.
  • vulcan --output json describe --format mcp exports MCP-style tool definitions.
  • The default MCP navigation surface exposes exact note reads, search, structured query, daily reads, status, and a compact capabilities tool. Graph analysis is available through --tool-pack graph.
  • Adaptive MCP sessions expose one tool_packs controller; startup-selected packs remain pinned, and clients must still refresh tools/list after a list-change notification.
  • MCP HTTP checks a browser Origin as a complete http(s)://host[:port] value: loopback listeners accept localhost, 127.0.0.1, and [::1]; non-loopback listeners require their exact bind IP. Origins with paths, credentials, queries, or fragments are rejected. This is a browser request check, not a substitute for a token or OAuth grant.
  • MCP HTTP accepts a single unambiguous Content-Length for request bodies; duplicate framing or authorization headers, malformed header lines, and chunked request transfer encoding are rejected before dispatch. Configure a reverse proxy to forward a normal length-delimited request body.
  • MCP query pages default to 50 compact rows, daily list/range pages default to 20 rows, and oversized structured results are returned through session resource links.
  • A named remote write that outlives its response deadline returns an indeterminate tool error with operation_id and status_path. Query GET <public-origin><status_path> with a current bearer token for the same connection grant; the response reports whether dispatch occurred, the retained state, and any known commit outcome. Known terminal status is retained for 30 days and unknown terminal status for 90 days; after expiry, inspect the vault. The timed-out MCP session is retired. Never replay an unknown write merely because its HTTP response timed out; proxy the /operations/<id> subpath alongside the MCP resource path.
  • vulcan help mcp-remotes documents named remote initialization, concurrent instances, multi-vault hosting, per-connection consent, credential migration, and recovery. Definitions are device-global; vault profiles remain in shared/local vault configuration. Local stdio and loopback HTTP still work without a daemon or named remote.
  • vulcan help chatgpt-mcp documents the private ChatGPT Developer Mode setup with named IndieAuth remotes, the daily-wiki-agent profile, and daily/task packs; advanced external OIDC remains available.
  • vulcan completions <shell> generates shell completions.

Useful starting points:

  • vulcan note --help
  • vulcan search --help
  • vulcan help filters
  • vulcan help query
  • vulcan periodic --help
  • vulcan vectors --help
  • vulcan saved --help
  • vulcan plugin --help
  • vulcan edit --help
  • vulcan browse --help
  • vulcan skill --help
  • vulcan help skill
  • vulcan help skill-command

Global options and output behavior

These global flags are available on all commands:

  • --vault <PATH>: vault root directory
  • --output <human|markdown|json>: output format
  • --refresh <off|blocking|background>: override automatic cache refresh for cache-backed commands
  • --route <direct|daemon|auto> (or VULCAN_ROUTE, else the device's [client] route from vulcan device config set-route, else direct): where routable reads run. direct (the default) reads the cache in this process. daemon asks the running daemon, which answers from its retained index after an index barrier confirms every change made before the request is indexed, and fails with daemon_unavailable when it cannot. auto asks the daemon when it hosts the vault (registered with vulcan vault add and indexed) and runs directly otherwise. Routed output is identical to direct output. Currently routable: query with a DSL, --json, or DQL query (not --where alone or --list-fields), keyword search (not --mode hybrid, which embeds the query with this process's provider), ls, tasks query (not while a TaskNotes status auto-archives, whose upkeep runs first and needs a fresh cache), and graph stats, graph hubs, and graph path (with both notes named), which the daemon answers from a graph it keeps until the cache changes.
  • --fields <a,b,c>: select columns on list-style output
  • --provider <NAME>: override the embedding provider for vector commands
  • --limit <N>: cap returned rows
  • --offset <N>: paginate list output
  • -v/--verbose: raise the diagnostic level; repeat for more (-v verbose, -vv debug, -vvv trace)
  • -q/--quiet: suppress progress, warnings, and other non-essential stderr output (wins over -v)
  • --no-header: suppress table headers for tabular human output
  • --color <auto|always|never>: control ANSI color output

Verbosity levels:

  • Levels are ordered quiet < normal < verbose < debug < trace; each level includes the output of the levels below it. Commands that only distinguish "extra detail" treat every level from verbose up the same way.
  • VULCAN_VERBOSITY=quiet|normal|verbose|debug|trace sets the default level when neither -v nor -q is given; VULCAN_QUIET=1 remains equivalent to -q.
  • Global flags may appear before or after any subcommand. Repeat -v in one position (-vv, or -v --verbose); a count given at a deeper subcommand replaces one given earlier rather than adding to it.
  • Vulcan processes launched by Vulcan itself (the detached daemon start --detach child and the daemon restart after self-update run) inherit the level and --color. They do not inherit --output, --vault, or scoping flags, because each child keeps its own output contract and scope. Installed service units are not rewritten with a level; set VULCAN_VERBOSITY=<level> in daemon.env in the Vulcan config directory instead, which daemon start reads on every platform when no -v/-q is given.

Output rules:

  • --output json is the stable scripting and agent surface.
  • Row-oriented commands emit line-delimited JSON.
  • Single-report commands emit one JSON object.
  • Command failures in JSON mode emit {"error":"...","code":"..."} on stdout with a non-zero exit code.
  • Many query commands also support --export <csv|jsonl> --export-path <FILE>.
  • In non-interactive mode, Vulcan does not open fuzzy pickers or TTY prompts.

Configuration layering and automatic refresh

Vulcan uses two vault-local config files:

  • .vulcan/config.toml: shared vault config that is intended to be synced with the vault
  • .vulcan/config.local.toml: optional device-local override loaded after config.toml

Precedence is:

  1. .vulcan/config.local.toml
  2. .vulcan/config.toml
  3. .obsidian/app.json
  4. Built-in defaults

vulcan index init creates .vulcan/config.toml, cache.db, and a default .vulcan/.gitignore that tracks config.toml, saved report definitions, and .vulcan/templates/ while ignoring config.local.toml, the cache, and other device-local state. An existing .vulcan/.gitignore is never rewritten; add !templates/ and !templates/** to it by hand if your vault keeps templates there. It also detects importable Obsidian settings and reports them; use vulcan index init --import to apply every detected importer immediately. Use vulcan index init --agent-files to write the bundled AGENTS.md template, default Agent Skills-compatible .agents/skills/<name>/SKILL.md reference files, and prompt examples into the vault. Add --example-tool to also scaffold a starter skill command under .agents/skills/<example>/scripts/ with the corresponding metadata.vulcan.commands entry in SKILL.md.

Automatic cache refresh is configured under [scan]:

[scan]
default_mode = "blocking"
browse_mode = "background"

Meaning:

  • default_mode controls one-shot cache-backed commands such as note backlinks, note links, note diff, query, search, graph, and refactor commands that depend on current cache state.
  • browse_mode controls vulcan browse.
  • off uses the current cache as-is.
  • blocking runs an incremental scan before the command continues.
  • background is intended for long-lived interactive surfaces; one-shot commands treat it the same as blocking.

Use --refresh to override the configured behavior for one invocation:

vulcan --refresh off note backlinks Projects/Alpha
vulcan --refresh background browse

Note resolution rules:

  • Commands that take a note reference accept a vault-relative path, filename, or alias.
  • Some single-note commands allow omitting the note in a TTY and will open the built-in picker instead.
  • When --output json is active, or when stdin/stdout is not interactive, Vulcan will not auto-prompt.

Command aliases

Vulcan expands command aliases before clap parses the final argv. Aliases live under [aliases] in .vulcan/config.toml and are merged with a small built-in default set.

Example:

[aliases]
q = "query"
t = "tasks list"
today = "daily today"
ship = "query --where 'status = shipped'"

Behavior:

  • Built-in defaults currently include q, t, and today.
  • Vault-local aliases override built-in aliases when they use the same name.
  • Use vulcan config show aliases to inspect the effective alias map.
  • Alias expansion happens before clap parsing, so aliases can target grouped commands such as tasks list or query --where ....

Command catalogue

Indexing, cache, and local service commands

  • vulcan index init [--import|--no-import] [--agent-files] [--example-tool]: create .vulcan/, cache.db, config.toml, and the local ignore rules; optionally import detected Obsidian settings and write bundled AGENTS/skills/prompt files. --example-tool scaffolds a starter Agent Skills-compatible skill command.
  • vulcan agent install [--reset <skill>] [--overwrite] [--example-tool]: install agent scaffolds and refresh bundled skills marked with metadata.vulcan.managed: true. Same-name unmarked skill packages are preserved wholesale. --reset <skill> deliberately replaces and enrolls one bundled skill; repeat it for several names. --overwrite is the broad compatibility escape hatch for all bundled skill names. AGENTS.md, prompts, and the optional example tool are create-only.
  • vulcan index scan [--full] [--no-commit]: perform an incremental or full scan and refresh the cache.
  • vulcan index rebuild [--dry-run]: rebuild the cache from disk.
  • vulcan index repair fts [--dry-run]: rebuild the full-text search index from cached chunks.
  • vulcan index watch [--debounce-ms <MS>] [--no-commit]: keep the cache fresh from filesystem events.
  • vulcan index serve [--bind <ADDR>] [--no-watch] [--debounce-ms <MS>] [--auth-token <TOKEN>]: start the local HTTP API server backed by the cache. Its authenticated root document lists the legacy endpoint paths plus route-derived query/response schemas and explicit feature availability.
  • vulcan cache inspect: show cache sizes and row counts.
  • vulcan cache verify [--fail-on-errors]: verify cache invariants.
  • vulcan cache vacuum [--dry-run]: run SQLite VACUUM on the cache.
  • vulcan checkpoint create <name>: capture the current cache state under a checkpoint name.
  • vulcan checkpoint list: list saved scan and manual checkpoints.
  • vulcan export markdown|json|csv|epub|zip|outline-zip|sqlite [<query>] ...: materialize notes as combined documents, datasets, books, or archives. Omitting <query>, --query-json, and --selection-json exports the full vault (from notes). --selection-json accepts the shared additive query/graph selection plan. outline-zip plans Outline's sibling file/directory hierarchy and accepts --profile <name> to reuse an Outline publication profile's selection and rendering settings without requiring its API fields; see Outline publishing.
  • export markdown|json|zip|sqlite --evaluate [--dataviewjs off|static|on] writes notes with Dataview, Tasks, .base embeds, and note embeds replaced by their results, after content transforms and under the selected --permissions profile. Export profiles take evaluate = true and dataview_js for the same formats. EPUB output is always evaluated. Links, tags, and backlinks are derived from the evaluated notes, so links that appear only in query results are exported, and ZIP exports copy the attachments they point to.
  • Query-capable [export.profiles.<name>] entries use the same full-vault default when neither query nor query_json is configured.
  • vulcan export search-index [--path <FILE>] [--pretty]: write the cached search corpus as a static JSON index.
  • vulcan publish outline <profile> [--dry-run] [--create-collection] [--adopt-pulled] [--interactive] [--overwrite-conflict <source-path>] [--overwrite-conflicts]: reconcile a configured publication into Outline with human-readable phase/item progress. When collection_id is absent, --create-collection (or profile auto_create_collection = true) creates the configured collection_title, persists its UUID in shared config, and continues publication. --adopt-pulled explicitly reuses selected durable pull bindings only when the remote content, title, parent, and collection still match the last successful pull; ambiguous ownership fails closed. Remote drift stops publication unless terminal-guided, selective, or all-conflict overwrite authorization is supplied; see Outline publishing.
  • vulcan outline collections list|show|create|update|archive|restore|bind ...: discover accessible Outline collections and UUIDs, create and automatically bind a collection to a profile, update collection metadata/access defaults, recoverably archive or restore a collection, or bind an existing UUID. Every mutating command supports --dry-run; archiving a still-bound collection requires --allow-bound, and Vulcan does not expose permanent collection deletion.
  • vulcan pull outline <profile> --into <vault-directory> [--dry-run] [--root-document <remote-id>]... [--max-depth <depth>] [--exclude-document <remote-id>]... [--max-documents <count>] [--max-content-bytes <bytes>] [--max-attachments <count>] [--max-attachment-bytes <bytes>] [--max-total-attachment-bytes <bytes>] [--interactive|--overwrite-conflicts|--conflict-markers] [--apply-remote-moves] [--archive-missing <vault-directory>|--delete-missing --confirm-delete-count <count>] [--archive-stale-attachments <vault-directory>|--delete-stale-attachments --confirm-stale-attachment-delete-count <count>] [--no-commit]: pull the profile's explicitly configured collection into a contained local namespace, reverse supported Outline Markdown, materialize referenced attachments under _attachments/, and reconcile through durable three-way state. Document count, cumulative Markdown, attachment count, individual attachment size, cumulative attachment downloads, and API response bodies are bounded. Changing, duplicate, or incomplete pagination snapshots fail closed. Repeated roots select the union of their subtrees; depth zero selects only each root, while exclusions remove a named document and all descendants. Local/remote drift fails closed unless the user chooses remote overwrite or Git-backed diff3 merging; non-overlapping edits are applied automatically and only overlapping hunks receive localized markers. Live mutation holds the shared vault write lock and journals an operation_id before every action; interrupted runs report resumed_operation and reuse that journal until the final scan succeeds. Human output reports pull phases and items on stderr; --quiet suppresses progress. Remote title and hierarchy changes keep stable local paths unless --apply-remote-moves is explicitly selected. Missing remote documents and stale managed attachments are retained unless their respective archive/delete policies are explicitly selected; active documents outside the current selector are reported as out_of_scope, never as missing.
  • vulcan integration list|show|validate|plan|run|status: inspect, validate, dry-run, execute, and diagnose named [integrations.routes.<name>] Outline routes. integration run <name> composes pull/push according to direction and authority; --interactive reviews review conflicts, --all runs every enabled route, and --scheduled runs only routes whose @hourly, @daily, @weekly, or every N[m|h|d] interval is due. Live runs are locked and checkpointed outside the cache.
  • vulcan integration bindings|bind|unbind <route> ...: list durable identities, adopt an existing local Markdown file as an existing immutable Outline document ID without rewriting either side, or remove only that identity state. Exact document_bindings in route config override generated pull paths. See Outline publishing.
  • vulcan changes [--checkpoint <name>]: report note, link, property, and embedding changes since the last scan or a named checkpoint.
  • vulcan note diff <note> [--since <checkpoint>]: show one note's changes since git HEAD, the last scan, or a named checkpoint.

Config and import commands

  • vulcan config show [section]: show the effective merged config or one section such as periodic or aliases.
  • vulcan config list [section]: list schema-backed config keys together with type, default, provenance, and preferred dedicated commands.
  • vulcan config get <key>: read one config value.
  • vulcan config edit [--no-commit]: open the interactive ratatui settings editor for .vulcan/config.toml.
  • vulcan config set <key> <value> [--target <shared|local>] [--dry-run] [--no-commit]: validate and write one config value.
  • vulcan config unset <key> [--target <shared|local>] [--dry-run] [--no-commit]: remove one config override and prune empty tables.
  • vulcan config alias list|set|delete ...: manage [aliases] entries without editing raw TOML.
  • vulcan config permissions profile list|show|create|set|delete ...: manage [permissions.profiles.<name>] entries without hand-writing tables.
  • vulcan config permissions profile pin <name> [--dry-run]: copy the shared config's definition of a profile into .vulcan/config.local.toml. Profiles this device selects as limits (--permissions, vault set --permissions-profile, MCP remote ceilings, the schedule ceiling) must be built-in or local, and a shared definition that widens one is refused; pinning is the review step that accepts what the vault defines.
  • vulcan plugin set|delete ...: manage full plugin registration metadata under [plugins.<name>], including path, events, sandbox, permission profile, and description.
  • vulcan config import core [--preview|--dry-run|--apply] [--target <shared|local>] [--no-commit]: import Obsidian core settings from .obsidian/app.json, .obsidian/templates.json, and .obsidian/types.json.
  • vulcan config import dataview [--preview|--dry-run|--apply] [--target <shared|local>] [--no-commit]: import Obsidian Dataview plugin settings.
  • vulcan config import folder-notes [--preview|--dry-run|--apply] [--target shared] [--no-commit]: import the Obsidian Folder Notes plugin's explicit filename and placement convention. Folder-note structure is shared repository state, so a local target is rejected.
  • vulcan config import kanban [--preview|--dry-run|--apply] [--target <shared|local>] [--no-commit]: import Obsidian Kanban plugin settings.
  • vulcan config import periodic-notes [--preview|--dry-run|--apply] [--target <shared|local>] [--no-commit]: import Obsidian Daily Notes core plugin settings plus the community Periodic Notes plugin settings.
  • vulcan config import tasks [--preview|--dry-run|--apply] [--target <shared|local>] [--no-commit]: import Obsidian Tasks plugin settings.
  • vulcan config import templater [--preview|--dry-run|--apply] [--target <shared|local>] [--no-commit]: import Obsidian Templater plugin settings.
  • vulcan config import --all [--preview|--dry-run|--apply] [--target <shared|local>] [--no-commit]: run every detected importer in registry order and aggregate the results.
  • vulcan config import --list: show every registered importer together with detection status and source paths.

Shared behavior:

  • config edit is backed by the same schema registry as config list and the non-interactive config mutation commands, exposes shared/local target switching, can create schema-defined dynamic entries such as aliases, permission profiles, and plugin registrations even when they are absent from both config files, and includes dedicated enum/list/map editors for common settings. Templater folder, regex, and ignored-folder creation rules use structured row editors; folder fields suggest existing vault directories while continuing to accept manual paths.
  • --preview and --dry-run are equivalent: they print the mapping plus a diff of the target config file without writing either .vulcan/config.toml or .vulcan/config.local.toml.
  • --apply is the explicit write path; omitting both --preview and --apply still applies the import for backwards compatibility.
  • --target local writes to .vulcan/config.local.toml; the default target is the shared .vulcan/config.toml.
  • --output json returns the full import report, including target file, whether the run was a dry run, the mappings applied, and any detected conflicts.
  • When vault auto-commit is enabled for mutations, config imports participate unless --no-commit is passed.
  • The generated reference for every supported key path, plus manual-edit examples for aliases, permission profiles, plugin registrations, and imported sections, lives in docs/reference/config.md and matches vulcan help config --output markdown.

Query, graph, and reporting commands

  • vulcan note links [note]: list outgoing links for a note.
  • vulcan note backlinks [note]: list inbound links for a note.
  • vulcan graph path [from] [to]: shortest resolved-link path between two notes.
  • vulcan graph hubs: notes with the highest degree.
  • vulcan graph moc: candidate map-of-content notes.
  • vulcan graph dead-ends: notes without outbound resolved note links.
  • vulcan graph components: weakly connected components.
  • vulcan graph stats: note-graph and vault analytics summary.
  • vulcan graph trends [--limit <N>]: trends across saved scan checkpoints.
  • vulcan search <query> [--regex <pattern>] ...: full-text search with optional typed property filters, explicit result sorting, and explicit regex mode.
  • vulcan query [QUERY] [--where <filter>] [--sort <field>] [--desc] [--language <auto|vulcan|dql>] ...: run the human DSL, Dataview DQL, or JSON query payload with --format table|paths|detail|count and optional --glob.
  • vulcan ls [--where <filter>] [--tag <tag>] [--glob <pattern>] [--format <paths|detail|count>]: thin alias for query 'from notes'.
  • vulcan refactor suggest mentions [note]: plain-text mentions that could become links.
  • vulcan refactor suggest duplicates: duplicate titles, alias collisions, and merge candidates.
  • vulcan refactor ...: grouped cross-vault mutation commands (rename-*, merge-tags, rewrite, move, link-mentions, suggest).
  • vulcan saved list: list saved query/report definitions from .vulcan/reports.
  • vulcan saved show <name>: show one saved report definition.
  • vulcan saved create search <name> ...: save a search definition.
  • vulcan saved create notes <name> ...: save a query shortcut built from query --where/--sort.
  • vulcan saved create bases <name> <file.base>: save a .base evaluation definition.
  • vulcan saved delete <name>: remove one saved report definition.
  • vulcan saved run <name>: execute one saved report.
  • vulcan automation list: list saved reports automation can run.
  • vulcan automation run [<name> ...] [--all] ...: run saved reports plus optional scan, doctor, verify, or FTS repair steps for CI and scripts.

Periodic note commands

  • vulcan daily open [date] [--no-edit] [--dry-run] [--no-commit]: open or create the daily note for any date (defaults to today). --dry-run reports the resolved date and path, and whether the note would be created, without writing.
  • vulcan daily today [--no-edit] [--no-commit]: shorthand for daily open today.
  • vulcan daily calendar [date|YYYY-MM] [--no-commit]: interactive month calendar. Days with notes are marked (+, or * with events), today is underlined, and the right pane previews the selected note. Enter or e creates the note from the daily template when missing and opens it in $EDITOR, then returns to the calendar. Keys: arrows/hjkl move, [/] or PageUp/PageDown change month, t jumps to today, typing YYYY-MM-DD jumps to a date, q/Esc quits. Bare vulcan daily opens the same picker. Without an interactive terminal, both list that month's daily notes instead.
  • vulcan daily latest: read the newest existing daily note from the configured daily-note folder. JSON includes date, path, exists, and content.
  • vulcan daily show [date]: print one daily note's contents. Defaults to today. Human output shows the note rendered, with Dataview, Tasks, .base embeds, and note embeds evaluated as in vulcan render; --output markdown prints the source.
  • vulcan daily list [--from <date>] [--to <date>] [--week] [--month]: list daily notes and extracted schedule events across a date window.
  • vulcan daily export-ics [--from <date>] [--to <date>] [--week] [--month] [--path <file.ics>] [--calendar-name <name>]: export extracted daily-note events as an ICS calendar. Without --path, the calendar is written to stdout.
  • vulcan daily append <text> [--heading <heading>] [--date <date>] [--no-commit]: append text to one daily note, creating it first when needed.
  • vulcan periodic weekly [date] [--no-edit] [--no-commit]: open or create the weekly note containing the given date.
  • vulcan periodic monthly [date] [--no-edit] [--no-commit]: open or create the monthly note containing the given date.
  • vulcan periodic <type> [date] [--no-edit] [--no-commit]: generic open-or-create command for any configured period type.
  • vulcan periodic list [--type <period>]: list indexed periodic notes from the cache.
  • vulcan periodic gaps [--type <period>] [--from <date>] [--to <date>]: show missing periodic notes by expected path.

Behavior:

  • Date arguments for daily and periodic commands accept YYYY-MM-DD, today, yesterday, tomorrow, signed offsets (-1, +3, -2w, +1m), and last <weekday> / next <weekday>. Relative dates and the default "today" use the local calendar day, not UTC.
  • Periodic note defaults come from [periodic.*] in .vulcan/config.toml.
  • Custom period types use the same config map: define [periodic.<name>] with unit = "days|weeks|months|quarters|years", interval = <n>, and an optional anchor_date = "YYYY-MM-DD" to align the cycle.
  • daily list uses the configured weekly start when --week is selected and includes parsed schedule events from the events cache table.
  • MCP exposes a stable high-level daily read tool with latest, today, show, list, and range operations. latest can include content in the same call; today returns exists: false when absent rather than substituting another date.
  • MCP initialization includes concise routing instructions. Common reads are on the default surface; adaptive packs remain opt-in for less-common capabilities because not every host refreshes callable schemas after notifications/tools/list_changed.
  • MCP query defaults to 50 compact results, supports path_prefix, filename_pattern, limit, offset, field projection, and page metadata, and requires explicit opt-in above 200 rows (hard maximum 1000).
  • daily export-ics uses the same cached events and emits a one-way RFC 5545 calendar export.
  • Periodic note creation uses the configured periodic template name when it resolves successfully; otherwise Vulcan creates a blank note and reports the template warning. Built-in template dates such as {{date}}, {{date:dddd}}, and {{DATE+1}} refer to the note's own period start, so a note created for yesterday is dated yesterday; Templater's tp.date.now() still returns the current time.
  • Use periodic weekly and periodic monthly directly; Vulcan does not ship compatibility aliases for the old top-level forms.
  • These commands participate in auto-commit when they mutate note files and vault git auto-commit is enabled.

Git commands

  • vulcan git status: show staged, unstaged, and untracked vault files while filtering out .vulcan/ internals.
  • vulcan git log [--limit <n>]: show recent commit history for the vault repository.
  • vulcan git diff [path]: show the current diff for one vault-relative path or for all eligible changed files.
  • vulcan git commit -m <message>: stage changed vault files and create a commit, skipping .vulcan/.
  • vulcan git blame <path>: show per-line authorship for one tracked vault-relative file.

Behavior:

  • All git subcommands support --output json.
  • git diff and git blame take vault-relative file paths, not note aliases.
  • git commit refuses to stage .vulcan/ state even when it is dirty.

Runtime commands

  • vulcan run <script.js>: execute a JavaScript file through the Vulcan runtime, stripping a leading shebang line when present.
  • vulcan run <script-name>: resolve .vulcan/scripts/<name> or .vulcan/scripts/<name>.js.
  • vulcan run --script <file>: shebang-friendly entrypoint for ad hoc scripts.
  • vulcan skill exec <script>: shebang-friendly entrypoint used by generated skill command scripts.
  • vulcan run: open the interactive JS REPL with persistent context.

Behavior:

  • console.log(...) emits paragraph output before the final return value.
  • --output json returns both outputs and the final value.
  • --timeout <duration> overrides the JS execution limit for one script run or REPL session.
  • --sandbox strict|fs|net|none selects the runtime capability tier for one script run or REPL session.
  • The REPL supports multiline input, tab completion, history in .vulcan/repl_history, pretty-printed objects, and preserved JS variables between prompts.
  • The runtime exposes vault.note(), vault.notes(), vault.query(), vault.search(), query/search builders, vault.graph.*, vault.daily.*, vault.events(), vault.set/create/append/patch/update/unset, vault.transaction(), vault.plan(), vault.refactor.*, vulcan.permissions(), tool.result(), tools.callChecked(), web.search(), web.fetch(), and help(obj).
  • vault.note(path).outline() returns the same semantic section/block outline as vulcan note outline, and vault.note(path).read(opts) reuses the same section/heading/block/line/match selectors as vulcan note get.
  • Use vulcan run for exploratory scripts. Promote reusable scripts into Agent Skills-compatible skill commands when they need discovery, schemas, permissions, MCP exposure, or cross-harness packaging.

Skill and skill-command commands

  • vulcan skill list: list discovered Agent Skills-compatible skill directories.
  • vulcan skill get <skill>: show parsed metadata and SKILL.md instructions for one skill.
  • vulcan skill show <skill>: show parsed metadata and SKILL.md instructions for one skill (alias for get).
  • vulcan skill commands <skill>: list Vulcan-declared commands exported by one skill.
  • vulcan skill run <skill> <command> --input-json <json>: run one skill command with validated JSON input.
  • vulcan skill run <skill> <command> --arg key=value --arg-json flag=true: build an input object from shell-friendly arguments.
  • vulcan skill run <skill> <command> --arg-file transcript=chat.md --arg-json-file messages=turns.json: read larger input fields from files, or use - to read one field from stdin.
  • vulcan skill exec <script> --arg key=value --arg-json flag=true: run an executable skill command script directly.
  • vulcan skill validate [<skill>]: validate skill frontmatter, command metadata, schemas, scripts, and permission-profile references.
  • vulcan skill init <name>: scaffold a new skill directory with SKILL.md and optional starter command.
  • vulcan tool init <alias> [--template minimal|reader|mutation|exporter|wrapper]: scaffold a skill-backed custom tool with schemas, CLI metadata, a Vulcan shebang script, and a smoke example.
  • vulcan tool lint [<alias>]: check exposed custom tools for schemas, CLI coverage, examples, shebangs, executable bits, mutation dry-run conventions, and packaging issues.
  • vulcan tool compat <alias>: report whether a tool is cleanly usable from CLI, MCP, OpenAI-tool, and JS registry surfaces.
  • vulcan tool types <alias>|--all: emit TypeScript declarations from tool input and output JSON schemas.
  • vulcan tool ci [--profile <name>]: run strict lint, declared examples, and surface compatibility checks in one authoring pass.

Behavior:

  • Skills are discovered from .agents/skills/<name>/SKILL.md and other configured skill roots.
  • Vulcan-specific command metadata lives under metadata.vulcan.commands in SKILL.md.
  • Commands usually point at scripts under the skill's scripts/ directory.
  • Command input is validated with JSON Schema before execution.
  • Field arguments merge into the same input object as --input-json, --input-file, or stdin, then schema validation runs.
  • Command output is validated when an output schema is declared.
  • Trusted skill commands may be projected into vulcan describe --format mcp|openai-tools|json-schema and into the live MCP server as first-class tools.
  • Exposed tools may declare custom CLI aliases so vulcan tool run <alias> --flag value builds the same validated input JSON as MCP.
  • vulcan tool help <alias> prints the shell-friendly usage generated from that metadata.
  • vulcan tool lint --strict treats authoring warnings as failures; --fix applies safe packaging repairs such as shebang normalization and executable-bit fixes.
  • vulcan tool test <alias> runs examples declared in command metadata, supports fixture files relative to the skill directory, reports JSON diffs when expected output does not match, accepts --profile <name> to test under the same permission profile used by MCP or another agent surface, supports --all for every exposed tool, and can refresh file-backed snapshots with --update-expected.
  • vulcan tool compat <alias> --surface cli,mcp checks surface-specific requirements such as CLI flag coverage, structured schemas, relative entrypoints, sandbox exposure, and callable status.
  • vulcan tool types --all emits one TypeScript declaration bundle for every visible custom tool.
  • vulcan tool ci is the shortest preflight for custom tools because it combines strict lint, all declared examples, and compatibility checks.
  • vulcan skill validate warns about exposed commands that are missing examples, output schemas, or CLI aliases.
  • Mutating commands should support dry-run/proposal output so Vulcan can preview diffs, require approval, and write audit records.
  • Skill-command permissions are the intersection of the active caller profile, the command's declared permission profile, its sandbox, and normal Vulcan path/network/execute checks.

Example:

vulcan skill list
vulcan skill get daily-review
vulcan skill commands daily-review
vulcan skill run daily-review prepare-day --input-json '{"date":"2026-05-05","dryRun":true}'
vulcan skill run daily-review prepare-day --arg date=2026-05-05 --arg-json dryRun=true
jq '.messages' chat.json | vulcan skill run conversation-export export --arg title=Chat --arg-json-file messages=-
vulcan tool init meeting-summary --description "Summarize meeting notes"
vulcan tool run conversation-export --title Chat --user Hello --assistant "Some message"
vulcan tool help conversation-export
vulcan tool lint conversation-export --strict
vulcan tool test conversation-export --profile daily-wiki-agent --example dry-run-cli
vulcan tool compat conversation-export --surface cli,mcp,openai-tools,js
vulcan tool ci --profile daily-wiki-agent

Plugin commands

  • vulcan plugin list: list registered plugin definitions plus discovered .vulcan/plugins/*.js files.
  • vulcan plugin enable <name>: create or update [plugins.<name>] in .vulcan/config.toml and mark it enabled.
  • vulcan plugin disable <name>: keep the plugin registration but disable hook execution.
  • vulcan plugin run <name>: execute one plugin's main(event, ctx) entrypoint manually.

Behavior:

  • Plugin files default to .vulcan/plugins/<name>.js unless [plugins.<name>].path overrides the location.
  • Plugin registrations live in .vulcan/config.toml and can declare events, sandbox, permission_profile, and description.
  • on_note_write and on_pre_commit are blocking hooks; other lifecycle hooks are post-events and only log failures.
  • Vault trust is required for plugin execution. Untrusted vaults skip hooks with a warning and reject manual plugin run.
  • vulcan help js.plugins documents the handler names, event payloads, and execution context.

Web commands

  • vulcan web search <query> [--backend <name>] [--limit <n>]: query the configured web search backend and return title/url/snippet results.
  • vulcan web fetch <url> [--mode markdown|html|raw] [--save <path>]: fetch one URL and render or save the response body.
  • vulcan help [<topic>] [--search <keyword>]: browse integrated command and concept docs, with --output json for structured help consumers.
  • Built-in help topics include getting-started, examples, filters, query-dsl, custom-tools, scripting, sandbox, js, js.vault, js.vault.graph, and js.vault.note.
  • vulcan describe [--format json-schema|openai-tools|mcp]: export the CLI surface for humans or tool integrations.

Behavior:

  • web search reads [web.search] from .vulcan/config.toml. The default backend is duckduckgo (no API key required). Setting backend = "auto" auto-selects the first available keyed backend (Kagi → Exa → Tavily → Brave → Ollama) and falls back to DuckDuckGo. Setting backend = "disabled" makes web search and web.search() fail intentionally while leaving web fetch available. Supported backends: disabled, duckduckgo, kagi (KAGI_API_KEY), exa (EXA_API_KEY), tavily (TAVILY_API_KEY), brave (BRAVE_API_KEY), ollama (OLLAMA_API_KEY).
  • web fetch uses the configured Vulcan user-agent and performs a best-effort robots.txt check before requesting the target URL.
  • web fetch --mode markdown converts HTML with rs-trafilatura main-content extraction.
  • If no readable main content can be extracted, retry with --mode html or --mode raw.
  • web fetch --save writes the fetched output to disk and still reports metadata in JSON mode.

Bases commands

vulcan bases now covers both read and write workflows:

  • vulcan bases eval <file.base>: evaluate a .base file against the indexed vault state.
  • vulcan bases create <file.base> [--title <title>] [--dry-run] [--no-commit]: create a note matching the first view context.
  • vulcan bases tui <file.base>: open the interactive Bases TUI.
  • vulcan bases view-add <file.base> <name> ...: add a view definition.
  • vulcan bases view-delete <file.base> <name> [--dry-run] [--no-commit]: delete a view.
  • vulcan bases view-rename <file.base> <old> <new> [--dry-run] [--no-commit]: rename a view.
  • vulcan bases view-edit <file.base> <name> ...: mutate filters, columns, sort, and grouping on an existing view.

Important behavior:

  • View mutations operate on the parsed .base model and write it back through the serializer.
  • Unsupported Bases constructs surface as diagnostics instead of being silently ignored.
  • bases create derives the note folder from file.folder = ... and file.inFolder(...) filters, and pre-populates equality filters like status = todo into frontmatter.
  • view-* commands support --dry-run where the result can be previewed, and --no-commit to suppress auto-commit for that invocation.

Semantic and vector commands

  • vulcan vectors index [--dry-run]: embed pending chunks.
  • vulcan vectors repair [--dry-run]: repair stale or missing vector rows.
  • vulcan vectors rebuild [--dry-run]: rebuild the vector index from scratch.
  • vulcan vectors queue status: inspect pending vector indexing work.
  • vulcan vectors queue run [--dry-run]: execute the explicit vector queue.
  • vulcan vectors neighbors [query] [--note <NOTE>]: nearest indexed chunks for ad hoc text or an existing note.
  • vulcan vectors related [note]: semantically related notes for one seed note.
  • vulcan vectors cluster [--clusters <N>] [--dry-run]: group indexed vectors into topical clusters.
  • vulcan vectors duplicates [--threshold <F32>]: highly similar chunk pairs.
  • vulcan vectors models: list stored embedding models.
  • vulcan vectors drop-model <cache-key>: drop one model and its vectors.

Under --permissions, commands that embed text (index and queue run, even with --dry-run; repair, rebuild, and neighbors) need network access to the configured provider's endpoint, and commands that write the vector index (index, queue run, repair, rebuild, drop-model, and cluster without --dry-run) need the index grant. Both are checked before any request.

Query and filter syntax

vulcan query --where, vulcan search --where, vulcan refactor rewrite, vulcan note update, vulcan note unset, and bases view-* filters all use the same typed filter grammar.

Each filter uses this form:

<field> <operator> <value>

Repeat --where to combine filters with logical AND.

Current limitation:

  • --where does not support OR, parentheses, or nested expressions.

Supported fields:

  • Any property key, for example status, due, reviewed, tags
  • file.path
  • file.name
  • file.ext
  • file.mtime

Supported operators:

  • =
  • >
  • >=
  • <
  • <=
  • starts_with
  • contains
  • matches
  • matches_i

Operator notes:

  • starts_with is for text fields.
  • contains is for list-valued properties such as tags.
  • matches applies a Rust regex to string-valued fields.
  • matches_i is the case-insensitive regex variant.

Supported value types:

  • Text: done, "In Progress", 'Rule Index'
  • Boolean: true, false
  • Null: null
  • Number: 42, 3.5
  • Date or datetime: 2026-03-01, 2026-03-01T09:30:00Z
  • file.mtime: integer milliseconds since the Unix epoch

Examples:

vulcan query --where 'status = done'
vulcan query --where 'tags contains sprint' --sort due
vulcan query --where 'file.path starts_with "Projects/"'
vulcan query --where 'file.name matches "^2026-"'
vulcan query --where 'owner matches_i "alice"'
vulcan note update --where 'status = draft' --key status --value done --dry-run
vulcan refactor rewrite --where 'file.path starts_with "Archive/"' --find TODO --replace DONE
vulcan bases view-add release.base Inbox --filter 'status = idea'

Folder-note conventions and conversion

Folder-note matching is configured once per repository and is never inferred from filenames during normal commands:

[folder_notes]
placement = "inside"       # inside | outside
name = "{{folder_name}}"   # exact stem/template; no .md suffix

The supported patterns include Folder/index.md, Folder/README.md, Folder/readme.md, Folder/Folder.md, and—with placement = "outside"—Folder.md beside Folder/. {{folder_name}} expands to the folder basename; literal names are matched case-sensitively. Outside placement requires the placeholder because a literal sibling filename cannot identify which adjacent folder it belongs to.

Preview or apply a vault-wide conversion with:

vulcan --output json refactor folder-notes \
  --to-placement inside --to-name index --dry-run
vulcan refactor folder-notes \
  --from-placement inside --from-name README \
  --to-placement outside --to-name '{{folder_name}}'

When --from-* is omitted, the source is the effective repository config. The command preflights every move, rejects overwrites and case-insensitive conflicts, uses the normal resolved-link move engine, and updates shared config only after every move succeeds. A dry run changes neither notes nor config. vulcan init --import or vulcan config import folder-notes can seed the convention from .obsidian/plugins/folder-notes/data.json; that setup import does not enable runtime auto-detection.

Splitting large documents into note trees

vulcan refactor split-note materializes a heading range from one large Markdown note as a folder-note-aware wiki tree. This is intended for sources such as PDF-derived rulebooks whose images and other assets already live in a companion vault directory.

vulcan --output json refactor split-note Rulebook.md \
  --from-level 2 --through-level 3 --dry-run
vulcan refactor split-note Rulebook.md \
  --destination Rules/Rulebook \
  --from-level 2 --through-level 3

The first selected heading level becomes children of the generated root folder note; headings through --through-level become nested notes. Deeper headings remain inside their nearest materialized note. The original frontmatter and preamble remain on the generated root note rather than being copied into every child. Generated filenames are portable and collision-safe, selected headings are promoted to page-level headings, and optional Contents lists link parents to immediate children.

The operation preflights the entire plan under the vault write lock. It rejects existing destinations, a stale cache, unresolved local links, ambiguous links to duplicate headings, unsafe asset references, and incomplete source-span ownership before writing. Inbound heading/block links, cross-section links, and relative Markdown links are retargeted while preserving authored wikilink, Markdown-link, label, and embed forms. Referenced asset files remain in place; only their destinations are rewritten. A successful apply refreshes the cache before returning.

PDF converters commonly emit explicit page anchors such as <span id="page-135-0"></span>. Vulcan preserves those anchors and retargets links to the generated note that owns them, while excluding the raw tag from generated note titles and paths. Rewritten Markdown destinations include a .md extension when a local fragment becomes cross-note and use CommonMark angle brackets when spaces or parentheses require them.

The source note is replaced by default. --keep-source retains it as an archival copy while creating the tree at a distinct folder-note path. Use --no-navigation to suppress generated parent/child lists. Missing source fragments fail closed by default. For noisy conversions with links to page IDs that never existed, --preserve-missing-fragments retains those already-broken links against the generated root (or the retained source) and emits one warning per fragment; it does not claim to repair them. Markdown footnotes and reference-style definitions currently fail closed because their definitions are file-local; convert them to inline links or keep the affected material within one output note before splitting.

Importing evidence-preserving Markdown artifacts

vulcan artifact consumes extractor- and source-format-neutral Markdown Artifact Format (MDAF) directories and .mdaf ZIP files. Sources may be PDFs, images, audiovisual media, web or office documents, structured data, text, compound media, or future formats; Markdown is the normalized output. The versioned format specification and schemas live in docs/specs/mdaf/v1/. Vulcan validates hashes, archive safety, composable source selectors, aligned outline evidence, and tool/model provenance without interpreting extractor-native renditions or extensions.

Fine-grained imports default to --min-section-bytes 2048: small descendant section subtrees stay inline with their nearest selected ancestor. The first selected level always materializes, so chapter-only imports are unchanged. Use --min-section-bytes 0 for exact depth-only splitting. This is a presentation policy, not a quality score or a change to the artifact's outline. Plain source references use safe occurrence correspondence, and duplicate fragment targets may resolve in a unique enclosing-note subtree; remaining ambiguity is reported.

vulcan --output json artifact inspect ./book.mdaf
vulcan artifact validate ./book.mdaf
vulcan --output json artifact import ./book.mdaf \
  --destination Rules/Book --from-level 2 --through-level 3 --dry-run
vulcan artifact import ./book.mdaf \
  --destination Rules/Book --hierarchy outline \
  --from-level 2 --through-level 3

Import always requires a new explicit vault-relative destination. Hierarchy authority defaults to auto: a validated aligned outline.json is preferred when it can represent the requested projection, with a diagnosed Markdown-heading fallback when no usable outline is available. Use --hierarchy markdown or --hierarchy outline to force either authority; forced outline requires a completely aligned outline.json and fails instead of falling back. Reports identify the authority actually used. The default level range starts at level 2 and includes level 3; when --from-level is changed without --through-level, the importer includes one descendant level, capped at level 6. The dry-run report lists every generated note, copied asset, source span, diagnostic, and rewritten link without creating the destination. A live import streams declared assets, adds vulcan.source artifact identity and source locators to each note, rewrites declared asset links and uniquely resolvable normalized source references, refreshes the cache, and removes the partial destination if writing or reindexing fails. The MDAF package remains external evidence rather than becoming canonical vault state.

Exchanging a single Markdown document

vulcan exchange textbundle reads and writes the TextBundle v1/v2 directory format and its compressed TextPack form. Use this boundary when one editable Markdown document and its referenced local assets should move between applications. Use MDAF instead when original-source selectors, extraction provenance, or alternative evidence must survive.

vulcan --output json exchange textbundle inspect ./note.textpack
vulcan exchange textbundle validate ./note.textpack
vulcan --output json exchange textbundle import ./note.textpack \
  --destination Imported/Note --dry-run
vulcan exchange textbundle export Notes/Note.md \
  --package ./note.textpack --dry-run

Import requires a new explicit vault-relative folder. It preserves the standard assets/ layout, refreshes the cache after applying, removes partial output on failure, and supports normal auto-commit controls. Unknown application-specific fields in info.json remain visible in inspection and import reports. Export rewrites references to included local assets into collision-safe package paths; it does not copy remote resources, other Markdown notes, or files outside the vault. A .textbundle output creates a directory package, while .textpack creates a ZIP package.

For a complete multi-document snapshot, use the versioned Markdown Wiki Package format:

vulcan --output json exchange wiki export \
  --package ./knowledge.wikipack --title "Knowledge" --dry-run
vulcan exchange wiki validate ./knowledge.wikipack
vulcan --output json exchange wiki import ./knowledge.wikipack \
  --destination Imported/Knowledge --dry-run

.wikibundle is the directory serialization and .wikipack is the ZIP serialization; both have the same BLAKE3 logical identity. Export writes Wiki Package v2: wiki.json, byte-preserved vault files below content/, and a provenance.json that records the export. It excludes Vulcan, Git, Obsidian, trash, synchronization, and common operating-system metadata state, and it rejects symbolic or special files.

inspect and validate accept version 1 and version 2 packages. For version 2 they report:

  • notes, assets, and declared sources;
  • provenance activities;
  • source-map mappings and references;
  • knowledge entities and claims, with their accepted counts.

Validation checks every digest, the provenance graph, source-map spans and locators, and the rules of any hosted Knowledge v1 snapshot.

Import requires a new destination, refreshes the cache, and rolls back partial output. Notes are copied byte for byte, except that a note with source-map mappings receives a vulcan.source frontmatter entry. By default the entry holds the package identity, member path, and mapping count. --source-locators full writes every byte span and source locator instead, in the same shape as MDAF import. Full locators can be larger than the notes themselves, so use them only when the vault needs them without the package. The report lists these notes as annotated_notes. An existing vulcan.source entry fails the import before anything is written. Provenance, knowledge, embedded sources, and extensions stay in the package, which remains external evidence; the materialized Markdown remains canonical.

The shared container rules are in docs/specs/container-core/v1/, and the format specifications, schemas, test vectors, and synthetic examples are in docs/specs/wiki-package/.

Searching with search

vulcan search searches indexed note content and can be combined with typed filters.

Examples:

vulcan search dashboard
vulcan search 'dashboard "release notes" -draft'
vulcan search dashboard --where 'reviewed = true'
vulcan search --regex 'release\\s+readiness'
vulcan search 'tag:index path:People/ has:status owned'
vulcan search Bob --match-case
vulcan search dashboard --sort path-desc
vulcan search dashboard --sort modified-newest

Default query syntax:

  • Plain terms are combined with logical AND.
  • Parentheses group boolean expressions: (dashboard or summary) release.
  • Quoted phrases stay together: "owned by".
  • Use or between positive terms: dashboard or summary.
  • Prefix a term, quoted phrase, or parenthesized group with - to exclude it.
  • Scoped operators can require terms to co-occur in one line, block, or heading section: line:(mix flour), block:(release notes), section:(dog cat).

Supported inline filters inside the query text:

  • tag:<tag>
  • path:<prefix>
  • has:<property>
  • property:<property>
  • [property]: require a frontmatter property to exist, for example [aliases]
  • [property:value]: require a property to equal a value, for example [status:done]
  • [property:value OR other]: require one of several values, for example [status:Draft OR Published]
  • /pattern/: match note content with a Rust regex pattern
  • path:/pattern/: match note paths with a Rust regex pattern
  • file:<filename-fragment>: match the filename only, not the full path
  • content:<term>: restrict the term to note body content, excluding title, aliases, and headings
  • match-case:<term>: require an exact-case match after the normal FTS candidate search
  • ignore-case:<term>: override a global case-sensitive search for one term
  • task:<term>: require the term to appear on a Markdown task line
  • task-todo:<term>: require the term to appear on an incomplete task (- [ ])
  • task-done:<term>: require the term to appear on a completed task (- [x])

Useful search flags:

  • --where <FILTER>: typed property filters using the grammar above
  • --tag <TAG>: require a tag
  • --path-prefix <PREFIX>: require a path prefix
  • --has-property <KEY>: require the property to exist
  • --mode <keyword|hybrid>: select keyword-only or keyword+vector search
  • --match-case: require case-sensitive matching by default for unscoped terms
  • --sort <relevance|path-asc|path-desc|modified-newest|modified-oldest|created-newest|created-oldest>: override result ordering
  • --context-size <N>: snippet context size
  • --raw-query: pass SQLite FTS5 syntax through unchanged
  • --fuzzy: retry empty searches with typo-tolerant expansion
  • --explain: include the parsed boolean tree, active filters, and scoring details

Additional explain behavior:

  • When --explain finds no hits, Vulcan includes query suggestions such as likely operator typos and task-specific hints.
  • JSON search output includes matched_line when Vulcan can identify the best line within the matched chunk.
  • JSON search hits also include section_id and absolute line_spans so agents can follow a hit with note get --section <id> or a narrow line-range read instead of reopening the whole note.

Raw FTS5 example:

vulcan search --raw-query '"release" NEAR/5 "notes"'

Planned enhancements (Roadmap 9.6): Browse-TUI search controls will continue to grow with inline explanation panes and more live search toggles. See docs/ROADMAP.md §9.6.

Query DSL and JSON payloads

vulcan query exposes the shared query layer directly.

DSL shape:

from notes
  [where <field> <op> <value> [and <field> <op> <value>...]]
  [select <field>[,<field>...]]
  [order by <field> [desc|asc]]
  [limit <n>]
  [offset <n>]

JSON payload example:

{"source":"notes","predicates":[{"field":"status","operator":"eq","value":"done"}],"sort":{"field":"file.mtime","descending":true},"limit":10}

Examples:

vulcan query
vulcan query --where 'status = done' --sort due
vulcan query 'from notes where status = done order by file.mtime desc limit 10'
vulcan query 'from notes where tags contains sprint and reviewed = true'
vulcan query --format paths 'from notes where file.name matches "^2026-"'
vulcan query --language dql 'TABLE status FROM #project'
vulcan query --glob 'Projects/**' 'from notes'
vulcan query --json '{"source":"notes","predicates":[{"field":"status","operator":"eq","value":"done"}]}'
vulcan query --explain 'from notes where status = backlog'

Behavior:

  • Bare vulcan query defaults to from notes.
  • query --where ... --sort ... builds a note query without writing the DSL explicitly.
  • --language auto detects Dataview DQL when the query starts with TABLE, LIST, TASK, or CALENDAR.
  • Use --language dql or --language vulcan to force the parser when auto-detection is not what you want.

Use vulcan describe when you need the runtime schema for the JSON form rather than prose.

Interactive workflows

Picker-backed note selection

These commands can resolve a note interactively when you omit the note in a TTY session:

  • note links
  • note backlinks
  • graph path
  • vectors related
  • note diff
  • edit
  • open

In non-interactive mode, or with JSON output redirected, Vulcan does not auto-prompt.

edit

vulcan edit opens a note in $VISUAL, then $EDITOR, then vi.

Examples:

vulcan edit Projects/Alpha
vulcan edit
vulcan edit --new Inbox/Idea

Behavior:

  • --new creates the path first, appending .md if needed.
  • After the editor exits, Vulcan runs an incremental scan of the edited path.
  • If auto-commit is enabled, the edit is committed unless --no-commit is passed.

browse

vulcan browse is the persistent note browser TUI.

Modes:

  • Default: fuzzy note picker with live preview
  • Ctrl-F: full-text search with snippet preview
  • Ctrl-T: tag filter mode
  • Ctrl-P: property filter mode
  • /: return to fuzzy mode

Primary keys:

  • Enter: edit the selected note
  • Ctrl-V: toggle the preview pane between the rendered note (Dataview, Tasks, and .base embeds evaluated, dataviewjs run in the configured sandbox) and its source; in full-text mode it toggles between the matching snippet and the rendered note
  • PageUp / PageDown: scroll the note preview, rendered or source (the source view shows the whole file with line numbers)
  • Tab / Shift-Tab: select the next or previous link or image in the rendered preview; Enter follows a link (a note opens in browse if the session's profile can read it, a URL opens in the default browser) or shows an image full screen, and Esc clears the selection. In the full-screen image view, Esc, Enter, or q returns to browse and o opens the file in the system viewer
  • Images the rendered preview embeds (![[pic.png]], ![alt](assets/pic.png)) are drawn in place of their [image: ...] line (below it when the line also holds text), as wide and tall as the pane allows, with small images enlarged up to three times their size: at full resolution in terminals with the Kitty, sixel, or iTerm2 graphics protocol (detected when browse starts), and with half-block characters in any other true-color terminal and inside tmux. The status bar shows the mode in use (Images: kitty|sixel|iterm2|half-blocks|off); set VULCAN_IMAGE_PROTOCOL to kitty, sixel, iterm2, halfblocks, or off to force one. An image that cannot be shown gets a line saying why (remote, missing, damaged, over 32 MiB, or not a PNG, JPEG, GIF, WebP, or BMP file); one the session's profile cannot read reads as missing. They are found as Obsidian finds them (next to the note, from the vault root, in the attachment folder, or by file name) and shown only when the session's profile can read them
  • Ctrl-N: create a new note
  • Ctrl-R: move or rename the selected note
  • Ctrl-B: backlinks view
  • Ctrl-O: outgoing-links view
  • Ctrl-D: note-scoped doctor view
  • Ctrl-G: git history for the selected file
  • o: with an empty fuzzy/tag/property query, open the selected Kanban board in a side-by-side board view
  • Esc: quit

Ctrl-F full-text extras:

  • Ctrl-S: cycle search result ordering (relevance, path-*, modified-*, created-*)
  • Alt-C: toggle global case-sensitive matching
  • Ctrl-E: toggle the parsed-query explain pane
  • PageUp / PageDown: scroll the explain pane

Additional browse notes:

  • By default, browse opens immediately on current cache contents and runs a background incremental refresh. Use [scan].browse_mode or --refresh to change that behavior.
  • Printable characters always extend the active query or prompt. Browse actions use Enter or Ctrl-* shortcuts.
  • In fuzzy/tag/property modes, o is reserved as a board-view shortcut only when the current query is empty and the selected note is an indexed Kanban board.
  • In full-text mode, the status line mirrors the parsed query/explain output when the explain pane is open.
  • After edits, creates, and moves, Vulcan rescans the affected files and refreshes the list.
  • In backlinks and outgoing-link views, o opens the selected .base file in the Bases TUI.
  • browse accepts --no-commit to suppress auto-commit for that session.

open

vulcan open [note] resolves a note and launches obsidian://open?vault=<vault>&file=<path> through the platform opener:

  • Linux: xdg-open
  • macOS: open
  • Windows: start

This is for quickly handing off from CLI inspection to the Obsidian desktop app.

lsp

vulcan lsp serves the Language Server Protocol over stdin/stdout so Neovim, Vim, Helix, Emacs, Zed, and other LSP clients get live vault semantics. Start it from the editor, not interactively. Neovim users can install the bundled vulcan.nvim plugin with vulcan editor nvim install, which writes the copy this binary carries into Neovim's package directory (--dir for elsewhere, --dry-run to preview). vulcan editor nvim status reports missing, current, outdated, modified, or unmanaged; path prints the default directory; uninstall removes a managed copy. Copies Vulcan did not install, or ones edited since, need --force, and self-update refreshes an unedited managed copy. Vim users install vulcan.vim (for Vim 9 with yegappan/lsp) the same way with vulcan editor vim install|status|path|uninstall, into ~/.vim/pack/vulcan/start/vulcan.vim by default; For Helix, which has no plugins, vulcan editor helix install merges the languages.toml entries and keybindings into Helix's configuration in place: it keeps comments and your settings, adds Vulcan to the Markdown server list instead of replacing it, keeps keys you bound yourself (--force takes them over), backs up each file before its first change, and records what it wrote in .vulcan-helix.json, so a rerun (also run by self-update) refreshes only that and uninstall removes only that. status reports each entry as missing, current, outdated, modified, removed, or foreign; --dry-run shows a diff. print-config [--only languages|keys] prints the entries for merging by hand (--output json returns both and Helix's config directory). The editors guide covers Vim, Helix, and Emacs. A minimal Neovim setup without the plugin:

vim.lsp.config("vulcan", {
  cmd = { "vulcan", "lsp" },
  filetypes = { "markdown" },
  root_markers = { ".vulcan", ".vulcan.toml", ".obsidian" },
})
vim.lsp.enable("vulcan")
  • The vault is --vault when given, otherwise discovered from the client's workspace folder exactly as other commands discover it from the current directory. Workspace folders in several vaults are each served by their own vault, and folders added or removed later start or stop that vault. The server never initializes a vault; without .vulcan/ it serves buffer-local features and warns once.
  • Supported today: go-to-definition on links, embeds, #heading and #^block subpaths; references (backlinks, or links to a heading when the cursor is on one); hover previews of link targets and tag counts; completion after [[ (notes, aliases, attachments, headings, block ids), #tags, and frontmatter keys and tags: values; diagnostics for open buffers (unresolved, ambiguous, and outside-vault links plus parse problems, using doctor categories); document symbols with note outline section ids; workspace symbols over paths and aliases.
  • Unsaved buffers are an in-memory overlay. A link to a note that exists only as an unsaved buffer resolves, and the cache is only refreshed from saved files (on didSave). The link being typed is not reported as unresolved.
  • Requests run on worker threads from snapshots of the open buffers, and every scan runs on one writer thread, so a move or sync holding the vault write lock never stalls completion or navigation. Diagnostics refresh 200 ms after the last edit.
  • Files changed outside the editor: with several editors on one vault, the Vulcan process holding .vulcan/index-owner.lock (an editor, the daemon, vulcan serve, or MCP over HTTP) watches the file system and indexes changes, and the others follow it through the scan journal and take over when it exits. initializationOptions.watcher selects "own" (default), "client" (index paths from workspace/didChangeWatchedFiles), or "off" (saved files only). Files renamed through the editor keep their identity via workspace/didRenameFiles. initializationOptions.showDocument: true makes the server open created notes in clients that handle window/showDocument without advertising it (Helix). Command arguments naming a document accept a file URI, a vault path, or a filesystem path, and positions may be 1-based lineNumber/columnNumber, so keybindings in editors without plugins can pass the cursor. After every index change the server re-diagnoses open buffers and sends vulcan/indexChanged with the changed paths and scan summary.
  • vulcan/status reports the custom protocol version, cache state (indexing, fresh, or missing), and watcher mode (lease, following, client, or off); clients check capabilities.experimental.vulcan.protocolVersion before using vulcan/ methods.
  • Renames rewrite every link: rename a link's note target to move the note (a plain name keeps its folder, a name with / is a vault path), or its #heading / #^block part (or the heading or block id itself) to rename that. workspace/executeCommand vulcan.note.move with { "from": <path or URI>, "to": <path>, "dryRun": <bool> } moves any note, including the current one. Closed files are rewritten on disk in one journaled batch; open buffers receive versioned edits through workspace/applyEdit (a moved open note is renamed by the editor so its buffer follows). If the editor rejects the edit, the disk changes are undone. Buffers with unsaved changes are rewritten from their own text, so links typed but not yet saved follow too; only moving a note that itself has unsaved changes is refused. Renames started from a file explorer (workspace/willRenameFiles) rewrite links too.
  • Code actions at the cursor: create the missing note for an unresolved link (vulcan.note.create, which opens it when the editor supports window/showDocument), link an ambiguous target to one candidate, convert a link between wikilink and Markdown style, and complete or reopen the task on the line (vulcan.task.toggle with { "path", "line", "dryRun"? }). On a heading, extract its section into a new note named after it (vulcan.section.extract with { "path", "position", "dryRun"? }); links to the heading, its subheadings, and its block ids follow the content. Toggling an open buffer is a versioned edit and never writes the file; a closed file is toggled on disk. Completing a recurring task inserts its next occurrence, as tasks complete does.
  • vulcan/render with { textDocument, range?, dataviewJs? } returns the evaluated inline expressions, Dataview and Tasks blocks, and .base embeds of the open buffer as regions with source ranges, for editors that show results as virtual text. dataviewjs blocks always run in the strict sandbox (no file access, network, or writes); vaults trusted with vulcan trust get full evaluation, which still never writes. A client may request off, static, or on, and on falls back to static for an untrusted vault.
  • workspace/diagnostic reports problems in closed notes (vulcan.nvim: :Vulcan problems); open notes keep pushed diagnostics.
  • Semantic tokens mark links, embeds, tags, inline-field keys, and block ids, and flag unresolved or ambiguous links.
  • Code lenses count the notes linking to each note, and inlay hints show how far off an open task's due date is. [editor] in .vulcan/config.toml (or config.local.toml) turns these off (code_lens, inlay_hints, semantic_tokens) and sets diagnostic_delay_ms (200) and diagnose_unsupported_syntax.
  • vulcan/outline and vulcan/backlinks ({ textDocument }) return the same JSON as note outline and note backlinks; the outline follows unsaved headings.
  • Completion also covers inline-field keys typed inside [ or ( ([sta completes to [status:: ).
  • More commands: vulcan.capture ({ "text" }, appending to the configured inbox note like vulcan inbox), vulcan.scan ({ "full": true } rebuilds), vulcan.periodic.open ({ "period": "daily", "date": "2026-10-13" }, creating the note from its template when missing and opening it), and vulcan.template.insert ({ "path", "template", "position" } or "mode": "append" | "prepend"; the template renders against the unsaved buffer with file-creating template calls disabled).
  • vulcan --permissions <profile> lsp checks every planned change against the profile, needs the index grant for vulcan.scan, and treats notes the profile cannot read as nonexistent in navigation, completion, diagnostics, and vulcan/render.
  • Planned next: see Roadmap 9.40 and the editor integration contract.

bases tui

vulcan bases tui <file.base> opens the interactive Bases TUI. It is the main interactive surface for inspecting a .base result set and editing view definitions through validated mutation paths instead of manual YAML editing.

Mutations, dry runs, and auto-commit

Most vault-mutating commands support --no-commit. Preview-capable mutations also expose --dry-run.

Common mutating commands:

  • note
  • refactor move
  • refactor split-note
  • refactor folder-notes
  • refactor link-mentions
  • refactor rewrite
  • refactor rename-property
  • refactor merge-tags
  • refactor rename-alias
  • refactor rename-heading
  • refactor rename-block-ref
  • edit
  • browse
  • daily
  • periodic
  • inbox
  • template
  • bases view-*

Auto-commit is opt-in and configured in vault config, typically .vulcan/config.toml and optionally overridden in .vulcan/config.local.toml:

[git]
auto_commit = true
trigger = "mutation"
message = "vulcan {action}: {files}"
scope = "vulcan-only"
exclude = [".obsidian/workspace.json", ".obsidian/workspace-mobile.json"]

Behavior:

  • auto_commit = false is the default.
  • trigger = "mutation" commits successful mutating commands.
  • trigger = "scan" also allows scan and watch to auto-commit external changes after they are indexed.
  • --no-commit suppresses auto-commit for one invocation even when the vault config enables it.
  • A vault registered on this device with the Git sync backend (vulcan vault add without --no-sync) is captured by sync's hidden-ref snapshots instead. Its per-write commits stand down with a warning, so automatic capture never lands twice. Manual vulcan git commit still works.

Batched auto-commits

strategy = "batched" stops individual commands from committing. The daemon commits whatever has accumulated instead:

[git]
auto_commit = true
strategy = "batched"          # default "per-write"
batch_interval_seconds = 300  # 30 to 86400; out-of-range values are clamped
batch_messages = "template"   # "template", "deterministic", or "agent"
  • A batch opens with the first change after the previous batch and is committed one interval later, once edits have paused. A vault that never pauses is committed one interval later regardless. Changes left from before the daemon started are committed one interval after it starts.
  • The daemon runs batches only for vaults registered with --no-sync; a vault that syncs with Git has no batch consumer, because sync owns its capture. Without a daemon, vulcan git batch-commit runs one batch with the same settings, ownership, and consent, for cron or a timer; --dry-run shows the planned commits (including any agent call) without committing.
  • Daemon shutdown cancels a batch that is waiting on the agent; a commit already under way finishes first.
  • template makes one commit with the message template ({action} is batch). deterministic makes one commit per top-level folder, with one more for each file at the vault root. agent asks the daemon's semantic agent to group the changes and write the messages.
  • The agent only groups and describes the changes; it never changes their bytes. Every changed path must appear exactly once in its plan. If the agent is unavailable, refuses, returns an invalid plan, or the batch is larger than 200 paths or 1 MiB of patches, the batch is committed with deterministic groups and the status records why.
  • Agent messages need both the vault's batch_messages = "agent" and this device's consent: vulcan daemon config set-auto-commit-agent --wiki <id> (clear-auto-commit-agent revokes it). The agent is the one configured with vulcan daemon config set-agent semantic, and the vault's permission profile must allow its endpoint. Without consent, batches fall back to deterministic.
  • Batches leave alone any path you staged yourself, and they wait while a merge, rebase, cherry-pick, revert, or bisect is in progress, while paths are unmerged, or while HEAD is detached. exclude applies, and .vulcan/ is never committed.
  • vulcan daemon auto-commit-status shows each registered vault's capture owner, its [git] batch settings, whether agent messages are allowed, and the latest batch outcome.

note

vulcan note groups selector-aware single-note CRUD commands for automation and agent workflows.

Examples:

vulcan note outline Dashboard
vulcan note get Dashboard --section dashboard/tasks@9
vulcan note get Dashboard --heading Tasks --match TODO --context 1
vulcan note get Dashboard --block-ref done-item --raw
vulcan note set Dashboard --no-frontmatter < body.md
vulcan note create Inbox/Idea --template brief --frontmatter status=idea
vulcan note append Dashboard "Shipped" --heading "## Done"
vulcan note patch Dashboard --find "/TODO \\d+/" --replace DONE --all --dry-run
vulcan note patch Dashboard --heading Nested --find TODO --replace DONE

Behavior:

  • note outline returns section ids, heading paths, block refs, and absolute line spans so agents can discover a note's structure before reading it.
  • note get reads one note from disk and can compose --section, --heading, --block-ref, --lines, --match, --context, and --no-frontmatter.
  • note get --raw prints only the selected content; without --raw, line-oriented selectors show line numbers in human output.
  • note get --output json returns the selected content, parsed frontmatter, and selection metadata, including continuation hints (total_lines, has_more_before, has_more_after) and section_id when applicable.
  • note get --evaluate replaces inline Dataview expressions, dataview and tasks blocks, and .base embeds with their results as Markdown and transcludes note embeds (![[Note]], ![[Note#Heading]]) as > [!embed] callouts, under the selected --permissions profile. --mode html always evaluates. --dataviewjs off|static|on controls dataviewjs blocks for one command. Without it they run in the strict sandbox (deterministic; no file access, network, or writes), or with full evaluation (the configured sandbox tier, still read-only) in a vault trusted with vulcan trust. The vault's [dataview] render_dataview_js can lower that, but its on only applies to a trusted vault, since vault content cannot grant itself trust. The same policy applies to render, daily show, browse previews, editors (vulcan lsp), MCP note_get, and evaluated exports (whose profile dataview_js is clamped the same way). JSON output reports metadata.evaluated, metadata.dataview_js, and evaluation diagnostics.
  • vulcan render <file> renders Markdown in the terminal, wrapped to the terminal width. Files inside the vault are evaluated the same way as note get --evaluate; --raw renders the source unchanged.
  • note set reads replacement content from stdin by default; --file <path> switches the input source.
  • note set --no-frontmatter preserves the leading YAML block byte-for-byte and replaces only the note body.
  • note create creates an empty note when stdin is a TTY, or merges piped stdin content with an optional template body when provided.
  • note create --frontmatter key=value adds or overrides top-level frontmatter keys after template rendering.
  • For an ordinary note create with an explicit template or creation trigger, direct template create/insert, and a creation trigger applied to an existing ordinary note, tp.file.create_new companion notes publish with the final note in one recoverable batch. Template moves/renames and plugin side effects are not part of that batch.
  • note append accepts literal text or - to read appended content from stdin.
  • note patch accepts literal strings or /regex/ patterns. It fails when the pattern matches more than once unless --all is passed.
  • note patch can also be narrowed with --section, --heading, --block-ref, and --lines, reusing the same semantic selectors as note get.
  • note patch --dry-run reports the planned replacements without modifying the note.
  • Mutating note commands rescan the vault incrementally after a successful write and participate in auto-commit unless --no-commit is passed.
  • --check on set, create, append, and patch runs non-blocking doctor-like diagnostics for the resulting note and includes those diagnostics in JSON output.

Inbox and templates

inbox

vulcan inbox appends a quick capture entry to the configured inbox note.

Examples:

vulcan inbox "Call Alice"
echo "idea" | vulcan inbox -
vulcan inbox --file draft.txt

Inbox configuration lives under [inbox] in vault config (.vulcan/config.toml with optional local overrides in .vulcan/config.local.toml):

[inbox]
path = "Inbox.md"
format = "- {text}"
timestamp = true
heading = "## Inbox"

Supported inbox template variables:

  • {text}
  • {date}
  • {time}
  • {datetime}

Behavior:

  • The inbox note is created automatically if it does not exist.
  • If heading is set, Vulcan appends under that heading and creates the heading if needed.
  • After writing, Vulcan runs an incremental scan and then auto-commits if enabled.

template

vulcan template creates notes from templates, vulcan template insert inserts rendered templates into existing notes, and vulcan template preview renders without writing a note. If .obsidian/templates.json or the Templater plugin configures a template folder, Vulcan also discovers templates there and reports the source in --list.

Examples:

vulcan template list
vulcan template daily --path Daily/2026-03-26 --engine auto
vulcan template meeting
vulcan template insert daily Projects/Alpha --engine templater --var project=Vulcan
vulcan template insert daily --prepend
vulcan template preview daily --path Journal/Today

Behavior:

  • NAME can be the full template filename or the filename stem.
  • .vulcan/templates takes precedence over the Obsidian template folder when the same template name exists in both places.
  • If --path is omitted, Vulcan creates <date>-<template-name>.md in the vault root.
  • In an interactive terminal, the rendered note is opened in the editor before the rescan.
  • Default {{date}} and {{time}} formats come from [templates] in .vulcan/config.toml.
  • --engine auto|native|templater selects the renderer. auto is the default and switches to the Templater-compatible engine when the template contains <% ... %> tags.
  • Repeat --var key=value to satisfy tp.system.prompt() and tp.system.suggester() in non-interactive runs.
  • template insert renders {{title}} and the other template variables against the target note.
  • template insert appends by default; --prepend inserts after the target note's frontmatter.
  • If the insert target note is omitted in an interactive terminal, Vulcan opens the note picker.
  • template preview disables mutating tp.file.* helpers and reports them as diagnostics instead of writing files.
  • Template frontmatter is merged on insert: existing scalar values are preserved, missing keys are added, and list properties are union-merged.
  • After either template creation or template insertion, Vulcan runs an incremental scan and then auto-commits if enabled.

Templater compatibility:

  • Templater tags <% ... %>, <%* ... %>, <%+ ... %>, <%_, _%>, <%-, and -%> are supported alongside the original {{date}} and {{title}} variables.
  • Native tp.date, tp.file, tp.frontmatter, tp.system, and tp.config helpers work without any extra setup.
  • JS-backed helpers such as tp.web.*, tp.user.*, tp.hooks.on_all_templates_executed(), and tp.obsidian.requestUrl() require the default js_runtime feature.
  • [templates].user_scripts_folder controls tp.user.<name>() script discovery, and [templates].web_allowlist gates outbound hosts for tp.web.request(), tp.web.daily_quote(), and tp.obsidian.requestUrl().

Supported template variables:

  • {{title}}
  • {{date}}
  • {{date:YYYY-MM-DD}}
  • {{date:dddd, MMMM Do YYYY}}
  • {{time}}
  • {{time:HH:mm}}
  • {{datetime}}
  • {{uuid}}

Supported Moment-style tokens for {{date:...}} and {{time:...}}:

  • YYYY, YY
  • MM, M
  • DD, D, Do
  • dd, ddd, dddd
  • HH, H
  • hh, h
  • mm, m
  • ss, s
  • A, a
  • MMMM, MMM

Saved reports, exports, and automation

Saved report definitions live under .vulcan/reports.

Relevant commands:

  • vulcan saved create search ...
  • vulcan saved create notes ...
  • vulcan saved create bases ...
  • vulcan saved run <name>
  • vulcan saved list
  • vulcan saved show <name>
  • vulcan saved delete <name>
  • vulcan automation list
  • vulcan automation run [<name> ...] [--all] ...

Examples:

vulcan saved create search release-dashboard 'dashboard "release notes"' --description "Release dashboard hits"
vulcan saved create notes due-soon --where 'due <= 2026-04-01' --sort due
vulcan saved run release-dashboard --output json
vulcan automation list
vulcan export epub 'from notes where file.path matches "^(People|Projects)/"' -o exports/team.epub --title "Team Notes" --backlinks
vulcan export epub 'from notes where file.path starts_with "Projects/"' -o exports/projects.epub --toc flat
vulcan export epub 'from notes where file.path = "Home.md"' -o exports/home.epub --frontmatter
vulcan export json 'from notes where file.path starts_with "Projects/"' --exclude-callout internal -o exports/public.json
vulcan export json 'from notes where file.path starts_with "People/"' --replace-rule regex '[A-Za-z0-9._%+-]+@example\.com' redacted -o exports/people.json
vulcan export profile list
vulcan export profile run team-book
vulcan export profile show team-book
vulcan export profile create team-book --format epub 'from notes where file.path matches "^(People|Projects)/"' -o exports/team-book.epub --title "Team Book"
vulcan export profile set team-book --backlinks --frontmatter
vulcan export profile rule add team-book --exclude-callout internal
vulcan export profile rule add team-book 'from notes where file.path starts_with "People/"' --replace-rule regex '[A-Za-z0-9._%+-]+@example\.com' redacted
vulcan export profile rule list team-book
vulcan export profile delete team-book
vulcan automation run release-dashboard due-soon --scan --doctor
vulcan automation run --all --verify-cache --fail-on-issues

Automation notes:

  • automation run --doctor-fix applies deterministic doctor fixes before reporting status.
  • automation run --fail-on-issues returns exit code 2 when checks complete but issues remain.
  • export profile list lists named recipes from [export.profiles.<name>].
  • export profile create|set|delete writes the shared .vulcan/config.toml.
  • export profile rule list|add|update|delete|move edits the ordered content_transforms rules stored in shared config.
  • export profile show prints the effective merged profile after shared/local config merge.
  • export profile run <name> runs a config-driven export and resolves relative profile paths from the vault root.
  • Direct exports and named profiles may use an additive selection plan instead of one query/query_json. The same plan is accepted by static-site and Outline publish profiles.
  • export epub preserves the selected note tree in the table of contents by default and can be flattened with --toc flat.
  • export epub --frontmatter includes each note's YAML metadata in a styled collapsible panel before the rendered note body.
  • markdown, json, epub, and zip support publication transforms that rewrite exported notes without mutating source files.
  • saved create search uses the same syntax as search.
  • saved create notes uses the same --where and --sort shortcut shape as query.
  • Use the public grouped forms directly: automation run ... for batch execution and saved create ... for persisted report definitions.

Publication transforms

markdown, json, epub, and zip can apply publication-oriented transforms to exported notes before Vulcan packages the output.

Supported transform families today:

  • exclude_callouts: drop matching callout blocks and their contents.
  • exclude_headings: drop heading sections and nested subsections.
  • exclude_frontmatter_keys: remove selected YAML frontmatter keys.
  • exclude_inline_fields: remove selected inline fields.
  • replace: apply ordered literal or regex replacements.

Selection and rule semantics:

  • A legacy export query defines the selected notes, or a structured selection plan unions any number of independent query and graph clauses.
  • Graph clauses accept multiple seeds with shared settings. Separate clauses let different seeds use different direction, depth, result filters, or traversal filters.
  • result_query filters which reached notes enter the result while traversal continues through nonmatching notes. traverse_query stops expansion through nonmatching notes. Global exclusions and permission boundaries always stop both inclusion and traversal.
  • Graph traversal is cycle-safe. depth = 0 selects only included seeds, an omitted depth is recursive, and max_nodes defaults to 10,000 per seed as a safety bound.
  • Transform rules never add notes. A rule query only narrows within the already-selected export set.
  • Direct CLI flags such as --exclude-callout or --replace-rule are sugar for one implicit rule that applies to all exported notes.
  • Profiles store the persisted form as ordered [[export.profiles.<name>.content_transforms]] tables.
  • export profile create and export profile set manage profile-wide fields such as format, query or --selection-json, path, and EPUB/JSON options.
  • export profile rule ... manages ordered persisted transform rules explicitly instead of flattening transform flags into profile creation.
  • If multiple rules match one note, Vulcan merges them before reparsing the note. Exclusions union together, and replacement rules keep declaration order.

Export pipeline behavior:

  • Transforms run on note content before packaging.
  • After rewriting the content, Vulcan reparses the transformed note and rebuilds exported frontmatter, properties, tags, links, backlinks, inline-expression evaluation, and copied attachment references from the transformed content.
  • This prevents removed callouts, stripped metadata, or rewritten links from leaking back into JSON, EPUB, or ZIP output through cached projections.

Direct export examples:

vulcan export json 'from notes where file.path starts_with "Projects/"' \
  --exclude-callout internal \
  --exclude-heading Scratch \
  -o exports/public.json

vulcan export json 'from notes where file.path starts_with "People/"' \
  --replace-rule literal '[[People/Bob]]' '[[People/Alice]]' \
  --replace-rule regex '[A-Za-z0-9._%+-]+@example\.com' redacted \
  --pretty \
  -o exports/people.json

Profile CLI examples:

vulcan export profile create public_json --format json \
  'from notes where file.path matches "^(People|Projects)/"' \
  -o exports/public.json

vulcan export profile set public_json --pretty

vulcan export profile rule add public_json \
  --exclude-callout internal \
  --exclude-heading Scratch

vulcan export profile rule add public_json \
  'from notes where file.path starts_with "People/"' \
  --exclude-frontmatter-key email \
  --exclude-inline-field owner \
  --replace-rule literal '[[People/Bob]]' '[[People/Alice]]'

vulcan export profile rule move public_json 2 --before 1

Persisted config example:

[export.profiles.public_json]
format = "json"
query = 'from notes where file.path matches "^(People|Projects)/"'
path = "exports/public.json"
pretty = true

[[export.profiles.public_json.content_transforms]]
exclude_callouts = ["internal", "secret gm"]
exclude_headings = ["Scratch"]

[[export.profiles.public_json.content_transforms.replace]]
pattern = "[A-Za-z0-9._%+-]+@example\\.com"
replacement = "redacted"
regex = true

[[export.profiles.public_json.content_transforms]]
query = 'from notes where file.path starts_with "People/"'
exclude_frontmatter_keys = ["email", "phone"]
exclude_inline_fields = ["owner"]

[[export.profiles.public_json.content_transforms.replace]]
pattern = "[[People/Bob]]"
replacement = "[[People/Alice]]"

Reading the content-transform example:

  • The first rule applies to every exported note selected by the profile query.
  • The second rule applies only to the exported notes under People/.
  • The nested replace tables belong to the most recent content_transforms rule table.
  • regex = true switches one replacement entry from literal matching to Rust regex matching.
  • The profile CLI edits this persisted rule list directly instead of inventing a second "simple profile transform" schema.

Additive graph-selection profile example:

[export.profiles.graph_book]
format = "epub"
path = "exports/graph-book.epub"
title = "Graph Book"

[export.profiles.graph_book.selection]
max_nodes = 5000

[[export.profiles.graph_book.selection.clauses]]
type = "graph"
seeds = ["Home", "Projects/Index"]
direction = "outgoing"
depth = 3
include_seeds = true
result_query = 'from notes where publish = true'

[[export.profiles.graph_book.selection.clauses]]
type = "graph"
seeds = ["Reference/Index"]
direction = "both"
include_seeds = true
traverse_query = 'from notes where visibility = "team"'

[[export.profiles.graph_book.selection.clauses]]
type = "query"
query = 'from notes where file.path starts_with "Appendices/"'

[export.profiles.graph_book.selection.exclusions]
query = 'from notes where visibility = "private"'
paths = ["Scratch"]

Selection clauses are additive and declaration order does not change membership. Notes are deduplicated, output ordering remains deterministic by path, and selection-aware JSON/ZIP/SQLite artifacts retain the plan and per-clause seed/depth provenance.

Query output modes

  • query --format table: the default structured note rows.
  • query --format paths: one document path per line.
  • query --format detail: document path, property summary, and a short preview.
  • query --format count: only the number of matched rows.
  • query --glob 'Projects/**': apply an extra path glob after query evaluation.
  • ls shares the same implementation but defaults to --format paths.

Shell completions

Generate completions:

vulcan completions bash
vulcan completions fish
vulcan completions zsh

Typical install examples:

vulcan completions bash > ~/.local/share/bash-completion/completions/vulcan
vulcan completions fish > ~/.config/fish/completions/vulcan.fish

Supported shells:

  • bash
  • elvish
  • fish
  • powershell
  • zsh

Bash, Fish, and Zsh completions include vault-aware dynamic candidates for notes, task views, scripts, templates, saved reports, skills, plugins, aliases, permission profiles, export profiles, Outline publication profiles, site profiles, and integration routes. This includes positional profile arguments such as vulcan outline collections list <TAB> and applicable option values such as --profile, --site-profile, --permissions, and --permission-profile. When a skill command declares metadata.vulcan.commands[].cli, vulcan tool run <TAB> completes its projected tool name and aliases, and vulcan tool run <alias> --<TAB> completes the declared custom flags. Flags with choices or completion metadata also complete values after the flag.

Platform status and later roadmap items

The Phase 9 command, automation, publication, MCP, and external-agent foundations below are implemented. The next platform gate is the multi-vault daemon in Phase 10. Phase 12 then adds device/file-tree synchronization, while Phase 15 separately adds explicit routes between the canonical local vault and external knowledge systems.

The planned knowledge-hub model is not a generic two-way sync command. A route selects local notes, declares pull or push direction and authority, and uses durable connector state outside the rebuildable cache. See Local information hub and external knowledge routes for the current/planned boundary.

Phase A: CLI for LLMs (Roadmap Wave 5)

The core Wave 5 CLI surface is implemented: grouped commands, note CRUD, structured help, describe --format ..., bundled AGENTS/skills files, web tools, and git tools are all available today.

Phase B: External Agent Runtime

The embedded vulcan assistant host was removed. Use MCP or an external runtime that shells out to Vulcan's JSON commands, loads AGENTS.md, and discovers bundled skills.

vulcan agent install                         # write AGENTS.md, default skills, and prompts
vulcan agent install --reset vault-query     # replace and re-enroll one bundled skill
vulcan agent print-config --runtime generic  # wrapper contract for external runtimes
vulcan skill list                            # discover installed skills
vulcan skill run conversation-export export  # archive a chat transcript as Markdown
vulcan mcp --transport http                  # expose the curated MCP tool surface

Runtime Status

vulcan run <script.js|script-name>       # current script execution surface
vulcan run --script <file>               # ad hoc script shebang entry point
vulcan skill exec <script>               # skill command script shebang entry point
vulcan run --sandbox fs runtime.js       # enable vault writes
vulcan run --sandbox net fetch.js        # enable web helpers
vulcan run                               # interactive REPL

Current runtime capabilities:

  • sandbox selection with strict, fs, net, and none
  • multiline REPL editing, completion, history, and persistent context
  • write-capable vault APIs such as vault.transaction() and vault.refactor.*
  • network-gated helpers through web.search() and web.fetch()

canvas (Roadmap Phase 18)

Canvas support will add CLI commands for inspecting and validating Obsidian JSON Canvas files (.canvas):

  • vulcan canvas [path]: summary view (node/edge counts, referenced files)
  • vulcan canvas list: list all canvas files in the vault
  • vulcan canvas nodes <path>: list all nodes with type, position, and content preview
  • vulcan canvas edges <path>: list all edges with from/to labels
  • vulcan canvas validate <path>: structural validation
  • vulcan canvas refs <path>: file references with resolution status

Canvas text nodes will be indexed in FTS5 and searchable via vulcan search. File node references will participate in the vault graph (backlinks, doctor). See docs/ROADMAP.md Phase 18.

Related docs

  • docs/design_document.md
  • docs/ROADMAP.md
  • docs/performance.md