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.
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 vulcanThe 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 browseFor 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.
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-workerThe 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.
The CLI is designed to be self-describing at runtime.
vulcan --helpshows the top-level command groups.vulcan <command> --helpshows command-specific syntax, caveats, and examples.vulcan helpshows the integrated topic index, andvulcan 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 describeprints a compact command inventory for humans.vulcan --output json describeprints the runtime command schema in machine-oriented JSON.vulcan --output json describe --format openai-toolsexports OpenAI function-calling tool definitions.vulcan --output json describe --format mcpexports MCP-style tool definitions.- The default MCP navigation surface exposes exact note reads, search, structured query, daily reads, status, and a compact
capabilitiestool. Graph analysis is available through--tool-pack graph. - Adaptive MCP sessions expose one
tool_packscontroller; startup-selected packs remain pinned, and clients must still refreshtools/listafter a list-change notification. - MCP HTTP checks a browser
Originas a completehttp(s)://host[:port]value: loopback listeners acceptlocalhost,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-Lengthfor 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_idandstatus_path. QueryGET <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-remotesdocuments 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-mcpdocuments the private ChatGPT Developer Mode setup with named IndieAuth remotes, thedaily-wiki-agentprofile, and daily/task packs; advanced external OIDC remains available.vulcan completions <shell>generates shell completions.
Useful starting points:
vulcan note --helpvulcan search --helpvulcan help filtersvulcan help queryvulcan periodic --helpvulcan vectors --helpvulcan saved --helpvulcan plugin --helpvulcan edit --helpvulcan browse --helpvulcan skill --helpvulcan help skillvulcan help skill-command
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>(orVULCAN_ROUTE, else the device's[client] routefromvulcan device config set-route, elsedirect): where routable reads run.direct(the default) reads the cache in this process.daemonasks the running daemon, which answers from its retained index after an index barrier confirms every change made before the request is indexed, and fails withdaemon_unavailablewhen it cannot.autoasks the daemon when it hosts the vault (registered withvulcan vault addand indexed) and runs directly otherwise. Routed output is identical to direct output. Currently routable:querywith a DSL,--json, or DQL query (not--wherealone or--list-fields), keywordsearch(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), andgraph stats,graph hubs, andgraph 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 (-vverbose,-vvdebug,-vvvtrace)-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 fromverboseup the same way. VULCAN_VERBOSITY=quiet|normal|verbose|debug|tracesets the default level when neither-vnor-qis given;VULCAN_QUIET=1remains equivalent to-q.- Global flags may appear before or after any subcommand. Repeat
-vin 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 --detachchild and the daemon restart afterself-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; setVULCAN_VERBOSITY=<level>indaemon.envin the Vulcan config directory instead, whichdaemon startreads on every platform when no-v/-qis given.
Output rules:
--output jsonis 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.
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 afterconfig.toml
Precedence is:
.vulcan/config.local.toml.vulcan/config.toml.obsidian/app.json- 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_modecontrols one-shot cache-backed commands such asnote backlinks,note links,note diff,query,search,graph, and refactor commands that depend on current cache state.browse_modecontrolsvulcan browse.offuses the current cache as-is.blockingruns an incremental scan before the command continues.backgroundis intended for long-lived interactive surfaces; one-shot commands treat it the same asblocking.
Use --refresh to override the configured behavior for one invocation:
vulcan --refresh off note backlinks Projects/Alpha
vulcan --refresh background browseNote 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 jsonis active, or when stdin/stdout is not interactive, Vulcan will not auto-prompt.
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, andtoday. - Vault-local aliases override built-in aliases when they use the same name.
- Use
vulcan config show aliasesto inspect the effective alias map. - Alias expansion happens before clap parsing, so aliases can target grouped commands such as
tasks listorquery --where ....
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-toolscaffolds a starter Agent Skills-compatible skill command.vulcan agent install [--reset <skill>] [--overwrite] [--example-tool]: install agent scaffolds and refresh bundled skills marked withmetadata.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.--overwriteis 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 SQLiteVACUUMon 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-jsonexports the full vault (from notes).--selection-jsonaccepts the shared additive query/graph selection plan.outline-zipplans 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,.baseembeds, and note embeds replaced by their results, after content transforms and under the selected--permissionsprofile. Export profiles takeevaluate = trueanddataview_jsfor 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 neitherquerynorquery_jsonis 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. Whencollection_idis absent,--create-collection(or profileauto_create_collection = true) creates the configuredcollection_title, persists its UUID in shared config, and continues publication.--adopt-pulledexplicitly 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 anoperation_idbefore every action; interrupted runs reportresumed_operationand reuse that journal until the final scan succeeds. Human output reports pull phases and items on stderr;--quietsuppresses progress. Remote title and hierarchy changes keep stable local paths unless--apply-remote-movesis 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 asout_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 todirectionandauthority;--interactivereviewsreviewconflicts,--allruns every enabled route, and--scheduledruns only routes whose@hourly,@daily,@weekly, orevery 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. Exactdocument_bindingsin 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 gitHEAD, the last scan, or a named checkpoint.
vulcan config show [section]: show the effective merged config or one section such asperiodicoraliases.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 interactiveratatuisettings 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 editis backed by the same schema registry asconfig listand 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.--previewand--dry-runare equivalent: they print the mapping plus a diff of the target config file without writing either.vulcan/config.tomlor.vulcan/config.local.toml.--applyis the explicit write path; omitting both--previewand--applystill applies the import for backwards compatibility.--target localwrites to.vulcan/config.local.toml; the default target is the shared.vulcan/config.toml.--output jsonreturns 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-commitis 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.
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|countand optional--glob.vulcan ls [--where <filter>] [--tag <tag>] [--glob <pattern>] [--format <paths|detail|count>]: thin alias forquery '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 fromquery --where/--sort.vulcan saved create bases <name> <file.base>: save a.baseevaluation 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.
vulcan daily open [date] [--no-edit] [--dry-run] [--no-commit]: open or create the daily note for any date (defaults to today).--dry-runreports the resolved date and path, and whether the note would be created, without writing.vulcan daily today [--no-edit] [--no-commit]: shorthand fordaily 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 orecreates the note from the daily template when missing and opens it in$EDITOR, then returns to the calendar. Keys: arrows/hjklmove,[/]or PageUp/PageDown change month,tjumps to today, typingYYYY-MM-DDjumps to a date,q/Esc quits. Barevulcan dailyopens 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 includesdate,path,exists, andcontent.vulcan daily show [date]: print one daily note's contents. Defaults to today. Human output shows the note rendered, with Dataview, Tasks,.baseembeds, and note embeds evaluated as invulcan render;--output markdownprints 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
dailyandperiodiccommands acceptYYYY-MM-DD,today,yesterday,tomorrow, signed offsets (-1,+3,-2w,+1m), andlast <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>]withunit = "days|weeks|months|quarters|years",interval = <n>, and an optionalanchor_date = "YYYY-MM-DD"to align the cycle. daily listuses the configured weekly start when--weekis selected and includes parsed schedule events from theeventscache table.- MCP exposes a stable high-level
dailyread tool withlatest,today,show,list, andrangeoperations.latestcan include content in the same call;todayreturnsexists: falsewhen 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
querydefaults to 50 compact results, supportspath_prefix,filename_pattern,limit,offset, field projection, and page metadata, and requires explicit opt-in above 200 rows (hard maximum 1000). daily export-icsuses 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'stp.date.now()still returns the current time. - Use
periodic weeklyandperiodic monthlydirectly; 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.
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
gitsubcommands support--output json. git diffandgit blametake vault-relative file paths, not note aliases.git commitrefuses to stage.vulcan/state even when it is dirty.
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 jsonreturns bothoutputsand the finalvalue.--timeout <duration>overrides the JS execution limit for one script run or REPL session.--sandbox strict|fs|net|noneselects 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(), andhelp(obj). vault.note(path).outline()returns the same semantic section/block outline asvulcan note outline, andvault.note(path).read(opts)reuses the same section/heading/block/line/match selectors asvulcan note get.- Use
vulcan runfor exploratory scripts. Promote reusable scripts into Agent Skills-compatible skill commands when they need discovery, schemas, permissions, MCP exposure, or cross-harness packaging.
vulcan skill list: list discovered Agent Skills-compatible skill directories.vulcan skill get <skill>: show parsed metadata andSKILL.mdinstructions for one skill.vulcan skill show <skill>: show parsed metadata andSKILL.mdinstructions for one skill (alias forget).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 withSKILL.mdand 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.mdand other configured skill roots. - Vulcan-specific command metadata lives under
metadata.vulcan.commandsinSKILL.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-schemaand into the live MCP server as first-class tools. - Exposed tools may declare custom CLI aliases so
vulcan tool run <alias> --flag valuebuilds the same validated input JSON as MCP. vulcan tool help <alias>prints the shell-friendly usage generated from that metadata.vulcan tool lint --stricttreats authoring warnings as failures;--fixapplies 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--allfor every exposed tool, and can refresh file-backed snapshots with--update-expected.vulcan tool compat <alias> --surface cli,mcpchecks surface-specific requirements such as CLI flag coverage, structured schemas, relative entrypoints, sandbox exposure, and callable status.vulcan tool types --allemits one TypeScript declaration bundle for every visible custom tool.vulcan tool ciis the shortest preflight for custom tools because it combines strict lint, all declared examples, and compatibility checks.vulcan skill validatewarns 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-agentvulcan plugin list: list registered plugin definitions plus discovered.vulcan/plugins/*.jsfiles.vulcan plugin enable <name>: create or update[plugins.<name>]in.vulcan/config.tomland mark it enabled.vulcan plugin disable <name>: keep the plugin registration but disable hook execution.vulcan plugin run <name>: execute one plugin'smain(event, ctx)entrypoint manually.
Behavior:
- Plugin files default to
.vulcan/plugins/<name>.jsunless[plugins.<name>].pathoverrides the location. - Plugin registrations live in
.vulcan/config.tomland can declareevents,sandbox,permission_profile, anddescription. on_note_writeandon_pre_commitare 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.pluginsdocuments the handler names, event payloads, and execution context.
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 jsonfor structured help consumers.- Built-in help topics include
getting-started,examples,filters,query-dsl,custom-tools,scripting,sandbox,js,js.vault,js.vault.graph, andjs.vault.note. vulcan describe [--format json-schema|openai-tools|mcp]: export the CLI surface for humans or tool integrations.
Behavior:
web searchreads[web.search]from.vulcan/config.toml. The default backend isduckduckgo(no API key required). Settingbackend = "auto"auto-selects the first available keyed backend (Kagi → Exa → Tavily → Brave → Ollama) and falls back to DuckDuckGo. Settingbackend = "disabled"makesweb searchandweb.search()fail intentionally while leavingweb fetchavailable. 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 fetchuses the configured Vulcan user-agent and performs a best-effortrobots.txtcheck before requesting the target URL.web fetch --mode markdownconverts HTML withrs-trafilaturamain-content extraction.- If no readable main content can be extracted, retry with
--mode htmlor--mode raw. web fetch --savewrites the fetched output to disk and still reports metadata in JSON mode.
vulcan bases now covers both read and write workflows:
vulcan bases eval <file.base>: evaluate a.basefile 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
.basemodel and write it back through the serializer. - Unsupported Bases constructs surface as diagnostics instead of being silently ignored.
bases createderives the note folder fromfile.folder = ...andfile.inFolder(...)filters, and pre-populates equality filters likestatus = todointo frontmatter.view-*commands support--dry-runwhere the result can be previewed, and--no-committo suppress auto-commit for that invocation.
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.
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:
--wheredoes not supportOR, parentheses, or nested expressions.
Supported fields:
- Any property key, for example
status,due,reviewed,tags file.pathfile.namefile.extfile.mtime
Supported operators:
=>>=<<=starts_withcontainsmatchesmatches_i
Operator notes:
starts_withis for text fields.containsis for list-valued properties such astags.matchesapplies a Rustregexto string-valued fields.matches_iis the case-insensitiveregexvariant.
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 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 suffixThe 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.
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 3The 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.
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 3Import 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.
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-runImport 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/.
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-newestDefault query syntax:
- Plain terms are combined with logical
AND. - Parentheses group boolean expressions:
(dashboard or summary) release. - Quoted phrases stay together:
"owned by". - Use
orbetween 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 Rustregexpatternpath:/pattern/: match note paths with a Rustregexpatternfile:<filename-fragment>: match the filename only, not the full pathcontent:<term>: restrict the term to note body content, excluding title, aliases, and headingsmatch-case:<term>: require an exact-case match after the normal FTS candidate searchignore-case:<term>: override a global case-sensitive search for one termtask:<term>: require the term to appear on a Markdown task linetask-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
--explainfinds no hits, Vulcan includes query suggestions such as likely operator typos and task-specific hints. - JSON search output includes
matched_linewhen Vulcan can identify the best line within the matched chunk. - JSON search hits also include
section_idand absoluteline_spansso agents can follow a hit withnote 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.
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 querydefaults tofrom notes. query --where ... --sort ...builds a note query without writing the DSL explicitly.--language autodetects Dataview DQL when the query starts withTABLE,LIST,TASK, orCALENDAR.- Use
--language dqlor--language vulcanto 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.
These commands can resolve a note interactively when you omit the note in a TTY session:
note linksnote backlinksgraph pathvectors relatednote diffeditopen
In non-interactive mode, or with JSON output redirected, Vulcan does not auto-prompt.
vulcan edit opens a note in $VISUAL, then $EDITOR, then vi.
Examples:
vulcan edit Projects/Alpha
vulcan edit
vulcan edit --new Inbox/IdeaBehavior:
--newcreates the path first, appending.mdif 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-commitis passed.
vulcan browse is the persistent note browser TUI.
Modes:
- Default: fuzzy note picker with live preview
Ctrl-F: full-text search with snippet previewCtrl-T: tag filter modeCtrl-P: property filter mode/: return to fuzzy mode
Primary keys:
Enter: edit the selected noteCtrl-V: toggle the preview pane between the rendered note (Dataview, Tasks, and.baseembeds evaluated,dataviewjsrun in the configured sandbox) and its source; in full-text mode it toggles between the matching snippet and the rendered notePageUp/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;Enterfollows 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, andEscclears the selection. In the full-screen image view,Esc,Enter, orqreturns to browse andoopens the file in the system viewer- Images the rendered preview embeds (
![[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); setVULCAN_IMAGE_PROTOCOLtokitty,sixel,iterm2,halfblocks, oroffto 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 noteCtrl-R: move or rename the selected noteCtrl-B: backlinks viewCtrl-O: outgoing-links viewCtrl-D: note-scoped doctor viewCtrl-G: git history for the selected fileo: with an empty fuzzy/tag/property query, open the selected Kanban board in a side-by-side board viewEsc: quit
Ctrl-F full-text extras:
Ctrl-S: cycle search result ordering (relevance,path-*,modified-*,created-*)Alt-C: toggle global case-sensitive matchingCtrl-E: toggle the parsed-query explain panePageUp/PageDown: scroll the explain pane
Additional browse notes:
- By default,
browseopens immediately on current cache contents and runs a background incremental refresh. Use[scan].browse_modeor--refreshto change that behavior. - Printable characters always extend the active query or prompt. Browse actions use
EnterorCtrl-*shortcuts. - In fuzzy/tag/property modes,
ois 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,
oopens the selected.basefile in the Bases TUI. browseaccepts--no-committo suppress auto-commit for that session.
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.
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
--vaultwhen 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,
#headingand#^blocksubpaths; 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 andtags:values; diagnostics for open buffers (unresolved, ambiguous, and outside-vault links plus parse problems, usingdoctorcategories); document symbols withnote outlinesection 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
moveorsyncholding 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.watcherselects"own"(default),"client"(index paths fromworkspace/didChangeWatchedFiles), or"off"(saved files only). Files renamed through the editor keep their identity viaworkspace/didRenameFiles.initializationOptions.showDocument: truemakes the server open created notes in clients that handlewindow/showDocumentwithout advertising it (Helix). Command arguments naming a document accept a file URI, a vault path, or a filesystem path, and positions may be 1-basedlineNumber/columnNumber, so keybindings in editors without plugins can pass the cursor. After every index change the server re-diagnoses open buffers and sendsvulcan/indexChangedwith the changed paths and scan summary. vulcan/statusreports the custom protocol version, cache state (indexing,fresh, ormissing), and watcher mode (lease,following,client, oroff); clients checkcapabilities.experimental.vulcan.protocolVersionbefore usingvulcan/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/#^blockpart (or the heading or block id itself) to rename that.workspace/executeCommandvulcan.note.movewith{ "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 throughworkspace/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 supportswindow/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.togglewith{ "path", "line", "dryRun"? }). On a heading, extract its section into a new note named after it (vulcan.section.extractwith{ "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, astasks completedoes. vulcan/renderwith{ textDocument, range?, dataviewJs? }returns the evaluated inline expressions, Dataview and Tasks blocks, and.baseembeds of the open buffer as regions with source ranges, for editors that show results as virtual text.dataviewjsblocks always run in the strict sandbox (no file access, network, or writes); vaults trusted withvulcan trustget full evaluation, which still never writes. A client may requestoff,static, oron, andonfalls back tostaticfor an untrusted vault.workspace/diagnosticreports 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(orconfig.local.toml) turns these off (code_lens,inlay_hints,semantic_tokens) and setsdiagnostic_delay_ms(200) anddiagnose_unsupported_syntax. vulcan/outlineandvulcan/backlinks({ textDocument }) return the same JSON asnote outlineandnote backlinks; the outline follows unsaved headings.- Completion also covers inline-field keys typed inside
[or(([stacompletes to[status::). - More commands:
vulcan.capture({ "text" }, appending to the configured inbox note likevulcan 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), andvulcan.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> lspchecks every planned change against the profile, needs theindexgrant forvulcan.scan, and treats notes the profile cannot read as nonexistent in navigation, completion, diagnostics, andvulcan/render.- Planned next: see Roadmap 9.40 and the editor integration contract.
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.
Most vault-mutating commands support --no-commit. Preview-capable mutations also expose --dry-run.
Common mutating commands:
noterefactor moverefactor split-noterefactor folder-notesrefactor link-mentionsrefactor rewriterefactor rename-propertyrefactor merge-tagsrefactor rename-aliasrefactor rename-headingrefactor rename-block-refeditbrowsedailyperiodicinboxtemplatebases 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 = falseis the default.trigger = "mutation"commits successful mutating commands.trigger = "scan"also allowsscanandwatchto auto-commit external changes after they are indexed.--no-commitsuppresses 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 addwithout--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. Manualvulcan git commitstill works.
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-commitruns one batch with the same settings, ownership, and consent, for cron or a timer;--dry-runshows 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.
templatemakes one commit with themessagetemplate ({action}isbatch).deterministicmakes one commit per top-level folder, with one more for each file at the vault root.agentasks 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
deterministicgroups 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-agentrevokes it). The agent is the one configured withvulcan daemon config set-agent semantic, and the vault's permission profile must allow its endpoint. Without consent, batches fall back todeterministic. - 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
HEADis detached.excludeapplies, and.vulcan/is never committed. vulcan daemon auto-commit-statusshows each registered vault's capture owner, its[git]batch settings, whether agent messages are allowed, and the latest batch outcome.
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 DONEBehavior:
note outlinereturns section ids, heading paths, block refs, and absolute line spans so agents can discover a note's structure before reading it.note getreads one note from disk and can compose--section,--heading,--block-ref,--lines,--match,--context, and--no-frontmatter.note get --rawprints only the selected content; without--raw, line-oriented selectors show line numbers in human output.note get --output jsonreturns the selectedcontent, parsedfrontmatter, and selectionmetadata, including continuation hints (total_lines,has_more_before,has_more_after) andsection_idwhen applicable.note get --evaluatereplaces inline Dataview expressions,dataviewandtasksblocks, and.baseembeds with their results as Markdown and transcludes note embeds (![[Note]],![[Note#Heading]]) as> [!embed]callouts, under the selected--permissionsprofile.--mode htmlalways evaluates.--dataviewjs off|static|oncontrolsdataviewjsblocks 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 withvulcan trust. The vault's[dataview] render_dataview_jscan lower that, but itsononly applies to a trusted vault, since vault content cannot grant itself trust. The same policy applies torender,daily show, browse previews, editors (vulcan lsp), MCPnote_get, and evaluated exports (whose profiledataview_jsis clamped the same way). JSON output reportsmetadata.evaluated,metadata.dataview_js, and evaluationdiagnostics.vulcan render <file>renders Markdown in the terminal, wrapped to the terminal width. Files inside the vault are evaluated the same way asnote get --evaluate;--rawrenders the source unchanged.note setreads replacement content from stdin by default;--file <path>switches the input source.note set --no-frontmatterpreserves the leading YAML block byte-for-byte and replaces only the note body.note createcreates an empty note when stdin is a TTY, or merges piped stdin content with an optional template body when provided.note create --frontmatter key=valueadds or overrides top-level frontmatter keys after template rendering.- For an ordinary
note createwith an explicit template or creation trigger, directtemplate create/insert, and a creation trigger applied to an existing ordinary note,tp.file.create_newcompanion notes publish with the final note in one recoverable batch. Template moves/renames and plugin side effects are not part of that batch. note appendaccepts literal text or-to read appended content from stdin.note patchaccepts literal strings or/regex/patterns. It fails when the pattern matches more than once unless--allis passed.note patchcan also be narrowed with--section,--heading,--block-ref, and--lines, reusing the same semantic selectors asnote get.note patch --dry-runreports 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-commitis passed. --checkonset,create,append, andpatchruns non-blocking doctor-like diagnostics for the resulting note and includes those diagnostics in JSON output.
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.txtInbox 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
headingis 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.
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/TodayBehavior:
NAMEcan be the full template filename or the filename stem..vulcan/templatestakes precedence over the Obsidian template folder when the same template name exists in both places.- If
--pathis omitted, Vulcan creates<date>-<template-name>.mdin 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|templaterselects the renderer.autois the default and switches to the Templater-compatible engine when the template contains<% ... %>tags.- Repeat
--var key=valueto satisfytp.system.prompt()andtp.system.suggester()in non-interactive runs. template insertrenders{{title}}and the other template variables against the target note.template insertappends by default;--prependinserts after the target note's frontmatter.- If the insert target note is omitted in an interactive terminal, Vulcan opens the note picker.
template previewdisables mutatingtp.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, andtp.confighelpers work without any extra setup. - JS-backed helpers such as
tp.web.*,tp.user.*,tp.hooks.on_all_templates_executed(), andtp.obsidian.requestUrl()require the defaultjs_runtimefeature. [templates].user_scripts_foldercontrolstp.user.<name>()script discovery, and[templates].web_allowlistgates outbound hosts fortp.web.request(),tp.web.daily_quote(), andtp.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,YYMM,MDD,D,Dodd,ddd,ddddHH,Hhh,hmm,mss,sA,aMMMM,MMM
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 listvulcan saved show <name>vulcan saved delete <name>vulcan automation listvulcan 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-issuesAutomation notes:
automation run --doctor-fixapplies deterministic doctor fixes before reporting status.automation run --fail-on-issuesreturns exit code2when checks complete but issues remain.export profile listlists named recipes from[export.profiles.<name>].export profile create|set|deletewrites the shared.vulcan/config.toml.export profile rule list|add|update|delete|moveedits the orderedcontent_transformsrules stored in shared config.export profile showprints 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
selectionplan instead of onequery/query_json. The same plan is accepted by static-site and Outline publish profiles. export epubpreserves the selected note tree in the table of contents by default and can be flattened with--toc flat.export epub --frontmatterincludes each note's YAML metadata in a styled collapsible panel before the rendered note body.markdown,json,epub, andzipsupport publication transforms that rewrite exported notes without mutating source files.saved create searchuses the same syntax assearch.saved create notesuses the same--whereand--sortshortcut shape asquery.- Use the public grouped forms directly:
automation run ...for batch execution andsaved create ...for persisted report definitions.
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_queryfilters which reached notes enter the result while traversal continues through nonmatching notes.traverse_querystops expansion through nonmatching notes. Global exclusions and permission boundaries always stop both inclusion and traversal.- Graph traversal is cycle-safe.
depth = 0selects only included seeds, an omitted depth is recursive, andmax_nodesdefaults 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-calloutor--replace-ruleare 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 createandexport profile setmanage 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.jsonProfile 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 1Persisted 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
replacetables belong to the most recentcontent_transformsrule table. regex = trueswitches 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 --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.lsshares the same implementation but defaults to--format paths.
Generate completions:
vulcan completions bash
vulcan completions fish
vulcan completions zshTypical install examples:
vulcan completions bash > ~/.local/share/bash-completion/completions/vulcan
vulcan completions fish > ~/.config/fish/completions/vulcan.fishSupported shells:
bashelvishfishpowershellzsh
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.
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.
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.
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
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, andnone - multiline REPL editing, completion, history, and persistent context
- write-capable vault APIs such as
vault.transaction()andvault.refactor.* - network-gated helpers through
web.search()andweb.fetch()
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 vaultvulcan canvas nodes <path>: list all nodes with type, position, and content previewvulcan canvas edges <path>: list all edges with from/to labelsvulcan canvas validate <path>: structural validationvulcan 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.
docs/design_document.mddocs/ROADMAP.mddocs/performance.md