Documentation
¶
Index ¶
- Constants
- Variables
- func ApplyOptions() []config.ConfigOption
- func ArcCandidatesOptions() []config.ConfigOption
- func ArcReciteOptions() []config.ConfigOption
- func ArcReviewOptions() []config.ConfigOption
- func AskOptions() []config.ConfigOption
- func CheckOptions() []config.ConfigOption
- func DataviewOptions() []config.ConfigOption
- func DocsCommandsOptions() []config.ConfigOption
- func DoctorHealOptions() []config.ConfigOption
- func DoctorHealWikilinksOptions() []config.ConfigOption
- func DoctorOptions() []config.ConfigOption
- func EvalOptions() []config.ConfigOption
- func ExperimentCompareOptions() []config.ConfigOption
- func ExperimentReportOptions() []config.ConfigOption
- func ExperimentSummaryOptions() []config.ConfigOption
- func ExperimentTraceOptions() []config.ConfigOption
- func ExperimentsOptions() []config.ConfigOption
- func ExportOptions() []config.ConfigOption
- func FrontmatterFixOptions() []config.ConfigOption
- func FrontmatterMutationOptions() []config.ConfigOption
- func FrontmatterOptions() []config.ConfigOption
- func GitStatusOptions() []config.ConfigOption
- func HooksInstallOptions() []config.ConfigOption
- func HooksRecordOptions() []config.ConfigOption
- func HooksStatusOptions() []config.ConfigOption
- func HooksUninstallOptions() []config.ConfigOption
- func IdentityEnrollAddOptions() []config.ConfigOption
- func IdentityEnrollOptions() []config.ConfigOption
- func IdentityFetchRegistryOptions() []config.ConfigOption
- func IdentityInitOptions() []config.ConfigOption
- func IdentityInviteOptions() []config.ConfigOption
- func IdentityPathsOptions() []config.ConfigOption
- func IdentitySignEnrollmentOptions() []config.ConfigOption
- func IdentitySignEnvelopeOptions() []config.ConfigOption
- func IdentitySignOptions() []config.ConfigOption
- func IdentitySignRegistryOptions() []config.ConfigOption
- func IdentitySignerInstallOptions() []config.ConfigOption
- func IdentitySignerOptions() []config.ConfigOption
- func ImportOptions() []config.ConfigOption
- func IndexOptions() []config.ConfigOption
- func InitOptions() []config.ConfigOption
- func LinksNeighborsOptions() []config.ConfigOption
- func LinksOptions() []config.ConfigOption
- func LintFixLinksOptions() []config.ConfigOption
- func McpOptions() []config.ConfigOption
- func MemoryContextPackOptions() []config.ConfigOption
- func MemoryLinksOptions() []config.ConfigOption
- func MemoryNeighborsOptions() []config.ConfigOption
- func MemoryPackOptions() []config.ConfigOption
- func MemoryRecallOptions() []config.ConfigOption
- func MemoryRelatedOptions() []config.ConfigOption
- func MemorySummarizeOptions() []config.ConfigOption
- func NoteAcceptOptions() []config.ConfigOption
- func NoteCreateOptions() []config.ConfigOption
- func NoteGapOptions() []config.ConfigOption
- func NoteOptions() []config.ConfigOption
- func NotePinnedOptions() []config.ConfigOption
- func NoteProposeOptions() []config.ConfigOption
- func PingOptions() []config.ConfigOption
- func ResolveOptions() []config.ConfigOption
- func RetrievalOptions() []config.ConfigOption
- func SchemaOptions() []config.ConfigOption
- func SearchOptions() []config.ConfigOption
- func SelfOptions() []config.ConfigOption
- func ServeOptions() []config.ConfigOption
- func TreeOptions() []config.ConfigOption
- func VaultStatusOptions() []config.ConfigOption
- func WriteOptions() []config.ConfigOption
Constants ¶
const IncludeDraftsDescription = "" /* 144-byte string literal not displayed */
IncludeDraftsDescription is the help text for the one retrieval switch. Ask and search register the flag by hand: the key is not under either command prefix, and a second key would split the default.
Variables ¶
var ApplyMetadata = config.CommandMetadata{ Use: "apply <plan-file | ->", Short: "Execute an AI-generated plan to mutate vault notes", Long: `Parse a JSON plan file and execute its operations against the vault. Plans are typically produced by AI agents to make structured changes to your notes: setting frontmatter keys, creating new notes, or populating generated regions. Use preview modes to inspect what will change before writing anything to disk. THE PLAN FILE The argument is a path to a JSON file, or "-" to read from stdin. Required shape: {"version": 1, "description": "...", "operations": [...]} Supported operation types: frontmatter_set Set a frontmatter key in one note frontmatter_unset Remove a frontmatter key from one note frontmatter_merge Merge a map of keys into a note's frontmatter generated_region_render Write content into a delimited generated region note_create Create a new note with frontmatter and body PREVIEW AND EXECUTION MODES --dry-run Parse and simulate without writing. Shows what would change. --diff With --dry-run, display unified diffs of each note mutation. --commit Auto-stage and commit all changes in one git commit after success. Without --commit, writes are applied but not committed. FLAGS --vault <path> Vault root directory (default: ".") --json Machine-readable output: status envelope, per-operation results, and commit SHA when --commit succeeds. EXAMPLES vaultmind apply ./my-plan.json Apply the plan. Changes written to disk but not committed. vaultmind apply - < ./my-plan.json Read plan from stdin (pipe from an AI agent or another command). vaultmind apply ./my-plan.json --dry-run Simulate the plan without writing anything. vaultmind apply ./my-plan.json --dry-run --diff Simulate and show unified diffs so you see exactly what each mutation looks like. vaultmind apply ./my-plan.json --commit Apply, then auto-stage and commit in one step using the plan description. vaultmind apply ./my-plan.json --json Apply with machine-readable output including per-operation status.`, ConfigPrefix: "app.apply", FlagOverrides: map[string]string{ "app.apply.vault": "vault", "app.apply.json": "json", "app.apply.dry_run": "dry-run", "app.apply.diff": "diff", "app.apply.commit": "commit", }, }
ApplyMetadata defines metadata for the apply command.
var ArcCandidatesMetadata = config.CommandMetadata{ Use: "candidates", Short: "Surface arc-distillation candidate moments from episodes and the desk (propose-only)", Long: "Scan the vault and surface candidate transformation moments for arc distillation. " + "PROPOSE-ONLY: it never writes arcs — the moments are pointers for you to judge, draft, " + "and approve. See principle-how-to-write-arcs for the bar a real arc must clear.\n\n" + "Two sources are scanned:\n" + " <vault>/episodes session captures, phrase-matched for candidate moments\n" + " (authority-grants, manifesto-lens invocations) — guesses to check.\n" + " the desk any note in the vault whose frontmatter says `type: journal`: raw\n" + " entries written mid-session, already judged worth keeping. An entry\n" + " carrying `distilled_to: <arc-id>` is finished, and is not surfaced\n" + " again.\n\n" + "Each proposal is listed with the existing arcs it most resembles, so a new shape is " + "distinguishable from one already written. That comparison needs embeddings: run " + "`vaultmind index --embed` to enable it. Without them the proposals still appear — only " + "the neighbours are missing, and the report says so.\n\n" + "To find turning points in sessions, `vaultmind arc review` is the stronger tool: " + "measured on real sessions, the phrase rules here found 0 of 5 moments that became " + "arcs; an agent reading the review list found 4 of 5.", ConfigPrefix: "app.arc.candidates", FlagOverrides: map[string]string{ "app.arc.candidates.vault": "vault", "app.arc.candidates.json": "json", "app.arc.candidates.arcs_vault": "arcs-vault", }, }
ArcCandidatesMetadata defines the `arc candidates` command — propose-only arc-distillation candidate surfacing (plasticity step 2).
var ArcReciteMetadata = config.CommandMetadata{ Use: "recite", Short: "Load every arc as a body — the whole identity layer, unranked and ungated", Long: "Emit EVERY accepted arc in the vault as a token-bounded pack. Not top-K, not " + "relevance-gated, not a search. A status: draft arc is a proposal and is not " + "recited unless --include-drafts is set.\n\n" + "WHY THIS IS NOT `ask`\n\n" + " `ask` is retrieval: it ranks notes against a query and returns the best few. That is " + "the right shape for \"what do I know about X?\" and the wrong shape for \"who am I?\". " + "An identity layer is not a ranking problem — you do not want the arcs most similar to a " + "query, you want all of them.\n\n" + " Measured on a 27-arc identity vault, `ask \"who am I\"` delivered 3 arcs, the SAME 3 on " + "every run: selection was semantic similarity to that literal string, which is " + "uncorrelated with whatever the session is about. The other 24 were not occasionally " + "missed, they were permanently absent.\n\n" + "ORDER AND TRUNCATION\n\n" + " Arcs come back sorted by id, so two sessions get the same layer in the same order and " + "runs are comparable.\n\n" + " If --budget cannot fit them all, the ones left out are REPORTED by count and by id. A " + "bulk loader that silently drops its tail is the same defect this command exists to " + "fix — you could not tell \"here are your 27 arcs\" from \"here are 9 of them\".\n\n" + " --excerpt caps each arc's contribution, preferring its Principle section (the rule) " + "over its opening lines (the story setup). Excerpting reduces DEPTH per arc, never " + "COVERAGE of the layer.\n\n" + "EXAMPLES\n\n" + " vaultmind arc recite --vault ~/.vaultmind/persona\n" + " Every arc, whole bodies. Use when you want the full layer and have the room.\n\n" + " vaultmind arc recite --budget 3000 --excerpt 120\n" + " Session-start shape: every arc's rule, bounded. 27 arcs fit comfortably.\n\n" + " vaultmind arc recite --type principle\n" + " The same treatment for another always-load layer.", ConfigPrefix: "app.arc.recite", FlagOverrides: map[string]string{ "app.arc.recite.vault": "vault", "app.arc.recite.json": "json", "app.arc.recite.budget": "budget", "app.arc.recite.excerpt": "excerpt", "app.arc.recite.type": "type", }, }
ArcReciteMetadata defines `arc recite` — load the WHOLE arc layer, as bodies.
The counterpart to `ask`, not a variant of it. `ask` ranks; this enumerates. The identity vault's promise is that an agent reconstructs itself from its arcs at session start, and the shipped hook implemented that with `ask "who am I"`. Measured on a real 27-arc vault: three arcs arrived, the same three on every run, selected by similarity to a literal string that has nothing to do with the session. 89% of the layer was not occasionally missing — it was permanently dark (issue #47).
var ArcReviewMetadata = config.CommandMetadata{ Use: "review", Short: "Review captured sessions for turning points, oldest first", Long: "List the person's messages from a captured session, each with what the agent had " + "just said, for you to judge which ones changed how you understand or approach " + "something. Measured on real sessions: phrase rules (`arc candidates`) found 0 of 5 " + "moments that became arcs; an agent reading this list found 4 of 5. Machine messages, " + "compaction summaries and one-word replies are left out.\n\n" + "It works as a queue. With no episode named it opens the oldest session nobody has " + "judged. Write each turning point as a desk entry (arcs are still written by hand), " + "then record the judgement with --mark-reviewed: it sets `reviewed: <date>` on the " + "episode, and the queue moves on. `vaultmind self` — run at session start — says how " + "many sessions await review.\n\n" + " vaultmind arc review --vault ./vaultmind-identity\n" + " vaultmind arc review --vault ./vaultmind-identity --mark-reviewed episode-2026-09-24-abcd1234\n" + " vaultmind arc review --vault ./vaultmind-identity --episode episode-2026-09-24-abcd1234", ConfigPrefix: "app.arc.review", FlagOverrides: map[string]string{ "app.arc.review.vault": "vault", "app.arc.review.json": "json", "app.arc.review.episode": "episode", "app.arc.review.mark_reviewed": "mark-reviewed", }, }
ArcReviewMetadata defines the `arc review` command — the review queue for finding turning points in captured sessions (plasticity step 2).
var AskMetadata = config.CommandMetadata{ Use: "ask <query>", Short: "Compound search + context-pack: answer 'what do I know about X?'", Long: `Search the vault, pick the top hit, and pack token-budgeted context around it. One command replaces the manual search → recall → summarize chain. With --excerpt, the target keeps its first prose block unless a passage matching the question scores at least twice as highly; a positive match wins when the first block scores zero. Principle sections and context-note excerpts keep their existing behavior, within the same token cap. CHOOSE A RENDERING MODE BY INTENT vaultmind ask "spreading activation" --pointers-only Menu of relevant ids + titles. Cheapest. Use when you want to see what's relevant without reading bodies — then 'note get <id>' the one you want. vaultmind ask "spreading activation" --preview Menu + one-line snippet under each hit. Use when titles aren't enough to know what each note is about. Bridges --pointers-only and default. vaultmind ask "spreading activation" Default. Full token-budgeted context-pack around the top hit. Use when you want bodies in working context (not just to identify the right id). vaultmind ask "spreading activation" --explain Each hit shows per-lane RRF math (dense / sparse / colbert / fts). For investigating ranking decisions, not for answering content questions. SEARCH SEVERAL VAULTS AT ONCE vaultmind ask "X" --vaults ~/identity,~/desk,~/research One query, every vault, one ranked answer. Each vault judges relevance against ITS OWN noise floor, so a 12-note desk and a 400-note research vault are compared fairly; results merge by rank (cross-vault RRF), and every hit is tagged with the vault it came from. Use when your memory is not one vault — an identity vault plus a working desk plus a reference corpus is the normal shape, and the note you need is often not in the one you happened to point at. Note: --vault takes ONE path. Repeating it (--vault A --vault B) is an error, because it used to silently keep B and answer from it alone. SCOPE THE SEARCH vaultmind ask "X" --type decision vaultmind ask "X" --tag engineering vaultmind ask "X" --path decisions/ Rank only notes of that type, carrying that tag, or under that vault folder (a path prefix, the same match as tree --path). Applies to every vault under --vaults. The context pack around the top hit still follows its links, so a neighbour of another type or folder can appear there. DRAFTS A note with status: draft is excluded from ranking and from the context pack around a hit. --include-drafts puts it back. Naming the draft with note get still reads it. Accepting a draft is editing that status (one file, no copy). ANTI-PATTERN — AVOID vaultmind ask "X" --budget 3000 | tail -20 Don't double-clip. The budget asks the system to compute 3000 tokens of context-pack; the tail then throws most of it away. Pick one shape that fits the intent: --pointers-only if you want a menu, no bodies --preview if you want a menu with body snippets --budget=N (no tail) if you really want N tokens of context LONG QUERIES One forward pass embeds the first N tokens (--query-forward-tokens, default 1152). The cut is a token boundary, so the embedded text is a prefix of the query. Dense and sparse rank on that text, so a prompt's later content stays in the ranking. ColBERT and FTS use the K most salient terms of that pass (--query-salient-terms, default 64). A long word with no spaces contributes those tokens' own text, not the whole word. 0 turns each bound off. A query already within both bounds is ranked unchanged. The echoed query stays the full text, and the Search header reports when a bound applied. OUTPUT INCLUDES Search: [top-hit confidence: strong|moderate|weak] Per-hit: score, id, title, optional snippet (--preview), optional lanes (--explain) Context: target frontmatter + body, then ranked context items (default mode only)`, ConfigPrefix: "app.ask", FlagOverrides: map[string]string{ "app.ask.vault": "vault", "app.ask.vaults": "vaults", "app.ask.json": "json", "app.ask.budget": "budget", "app.ask.max_items": "max-items", "app.ask.search_limit": "search-limit", "app.ask.explain": "explain", "app.ask.pointers_only": "pointers-only", "app.ask.preview": "preview", "app.ask.read": "read", "app.ask.quiet_on_nomatch": "quiet-on-no-match", "app.ask.excerpt": "excerpt", "app.ask.type": "type", "app.ask.tag": "tag", "app.ask.path": "path", "app.ask.dedup_window": "dedup-window", "app.ask.query_forward_tokens": "query-forward-tokens", "app.ask.query_salient_terms": "query-salient-terms", }, }
AskMetadata defines metadata for the ask command.
var CheckMetadata = config.CommandMetadata{ Use: "check", Short: "Run quality checks", Long: `Run comprehensive code quality checks using checkmate. Includes 23 checks across 6 categories: Development Environment (2): go-version, tools Code Quality (2): format, lint Architecture Validation (10): defaults, commands, constants, task-naming, architecture, layering, package-org, config-consumption, output-patterns, security-patterns Security Scanning (2): secrets, sast Dependencies (6): deps, vuln, outdated, license-source, license-binary, sbom-vulns Tests (1): test Use --parallel to run checks within each category concurrently. Use --fail-fast to stop on the first failure.`, ConfigPrefix: "app.check", FlagOverrides: map[string]string{ "app.check.fail_fast": "fail-fast", "app.check.verbose": "verbose", "app.check.parallel": "parallel", "app.check.category": "category", "app.check.timing": "timing", }, Examples: []string{ "check", "check --category=security", "check -c security,tests", "check --fail-fast", }, SeeAlso: []string{"docs"}, }
CheckMetadata defines all metadata for the check command
var DataviewLintMetadata = config.CommandMetadata{ Use: "lint", Short: "Scan the vault for broken or duplicate VAULTMIND:GENERATED markers", Long: `Validate VAULTMIND:GENERATED markers across every note in the vault. The command reads all notes and checks each one for two classes of problems: malformed_markers A START marker has no matching END, or vice versa, for the same section key. Typically caused by a partial paste or a hand-edit that removed one half of the pair. duplicate_markers The same section key appears more than once in a single note. Rendering would be ambiguous; the second occurrence is flagged. read_error The file could not be read (permissions, encoding). Run this before "dataview render" to catch problems early. Run it after manual edits to confirm the markers are still well-formed. FLAGS --json Machine-readable output. Emits a JSON envelope whose "data" field contains files_checked (int), valid (int), and issues (array). Each issue has: path, section_key, rule, message, line. --vault Path to the vault root (default: current directory). OUTPUT INCLUDES Text: "Checked N files: N valid, N issues" followed by one line per issue in the form "[rule] path: message". JSON: { "status": "ok"|"warning", "data": { "files_checked": N, "valid": N, "issues": [ { "path": "...", "section_key": "...", "rule": "...", "message": "...", "line": N } ] } } EXAMPLES vaultmind dataview lint Scan the vault in the current directory; print summary and any issues. vaultmind dataview lint --vault /path/to/vault Scan a vault at an explicit path. vaultmind dataview lint --json Emit full JSON envelope; useful for piping into other tools. vaultmind dataview lint --json | jq '.data.issues' Extract just the issues array for scripted checks.`, ConfigPrefix: "app.dataviewlint", FlagOverrides: map[string]string{ "app.dataviewlint.vault": "vault", "app.dataviewlint.json": "json", }, }
DataviewLintMetadata defines metadata for the dataview lint command.
var DataviewRenderMetadata = config.CommandMetadata{ Use: "render <note-id-or-path>", Short: "Render a generated region", Long: `Replace content between VAULTMIND:GENERATED markers with section template content. Arguments: note-id-or-path Note ID, title, or file path (resolved via entity resolution) Markers have the format: <!-- VAULTMIND:GENERATED:{key}:START/END --> Section templates are loaded from .vaultmind/sections/{type}/{key}.md. Use --section-key to render a specific section (default: all sections). Use --force to override checksum mismatch (hand-edited content).`, ConfigPrefix: "app.dataviewrender", FlagOverrides: map[string]string{ "app.dataviewrender.vault": "vault", "app.dataviewrender.json": "json", "app.dataviewrender.dry_run": "dry-run", "app.dataviewrender.diff": "diff", "app.dataviewrender.commit": "commit", "app.dataviewrender.force": "force", "app.dataviewrender.section_key": "section-key", }, }
DataviewRenderMetadata defines metadata for the dataview render command.
var DocsCommandsMetadata = config.CommandMetadata{ Use: "commands", Short: "Generate the grouped command reference (COMMANDS.md)", Long: `Generate a markdown reference of every user-facing command, grouped by intent, each with its when-to-use trigger. This is the same catalog that backs 'vaultmind help' and the embedded onboarding doc — one generator, rendered to markdown here. Use --output to write COMMANDS.md; with no --output it prints to stdout.`, ConfigPrefix: "app.docs_commands", FlagOverrides: map[string]string{ "app.docs_commands.output": "output", }, Examples: []string{ "docs commands", "docs commands --output internal/onboard/COMMANDS.md", }, }
DocsCommandsMetadata defines the metadata for `docs commands` — emits the grouped command catalog (every user-facing command, with its when-to-use) as markdown. This is the COMMANDS.md generator: the same catalog that backs `vaultmind help` and the embedded onboarding doc, rendered to markdown.
var DoctorHealMetadata = config.CommandMetadata{ Use: "heal", Short: "heal (fix): apply all auto-fixable repairs doctor found", Long: `heal (fix): apply every auto-fixable repair that doctor's diagnosis can resolve. heal is the repair half of the doctor health hub. Today the auto-fixable set is exactly the Obsidian-incompatible wikilink rewriter, so 'doctor heal' rewrites [[Title]] links that won't resolve in Obsidian to the [[filename|Title]] form. As doctor learns to auto-fix more issues, they join this command. heal APPLIES changes by default. Pass --dry-run to preview the planned repairs without touching disk. (This is the inverse of the old 'lint fix-links', which was dry-run by default.) The 'fix' alias makes 'doctor fix' an exact synonym. SUBCOMMANDS wikilinks Surgically rewrite only Obsidian-incompatible wikilinks. EXAMPLES vaultmind doctor heal --vault ./my-vault # apply all auto-fixes vaultmind doctor heal --vault ./my-vault --dry-run # preview only vaultmind doctor heal wikilinks --vault ./my-vault # just the wikilink repair vaultmind doctor fix --vault ./my-vault # 'fix' alias`, ConfigPrefix: "app.doctorheal", FlagOverrides: map[string]string{ "app.doctorheal.vault": "vault", "app.doctorheal.json": "json", "app.doctorheal.dry_run": "dry-run", }, }
DoctorHealMetadata defines the metadata for the `doctor heal` command. heal APPLIES every auto-fixable repair doctor found (today that set is the wikilink rewriter); --dry-run previews instead. The `fix` alias is wired in cmd/ so `doctor fix` resolves here too.
var DoctorHealWikilinksMetadata = config.CommandMetadata{ Use: "wikilinks", Short: "Rewrite Obsidian-incompatible wikilinks to [[filename|Title]]", Long: `Detect [[Title]] wikilinks that won't resolve in Obsidian and rewrite them to the [[filename|Title]] form Obsidian requires. Scans every markdown file in the vault, reports each rewrite as "old -> new", and prints a summary of files scanned, files changed, and links fixed. Applies changes by default; pass --dry-run to preview without writing. The 'fix' alias makes 'doctor fix wikilinks' an exact synonym. EXAMPLES vaultmind doctor heal wikilinks --vault ./my-vault # apply the rewrites vaultmind doctor heal wikilinks --vault ./my-vault --dry-run # preview only vaultmind doctor heal wikilinks --json # machine-readable`, ConfigPrefix: "app.doctorhealwikilinks", FlagOverrides: map[string]string{ "app.doctorhealwikilinks.vault": "vault", "app.doctorhealwikilinks.json": "json", "app.doctorhealwikilinks.dry_run": "dry-run", }, }
DoctorHealWikilinksMetadata defines the metadata for the `doctor heal wikilinks` subcommand — the surgical wikilink repair that the old `lint fix-links` performed. Applies by default; --dry-run previews.
var DoctorMetadata = config.CommandMetadata{ Use: "doctor", Short: "Vault health hub: diagnose the vault and report issues", Long: `Run read-only diagnostics on the vault and report its health. doctor is the single vault-health command. It reports note counts, the per-type breakdown (per-type counts, required fields, valid statuses), embedding readiness, hook drift, unresolved/incompatible/dead links, stale-index drift, and an errors/warnings rollup. By default doctor also prints per-link details, which can run to hundreds of lines on a noisy vault. Use --summary for the cold-start view: counts, per-type breakdown, and the errors/warnings rollup, with the verbose per-link detail lines suppressed. (--summary replaces the former 'vault status'.) To repair what doctor finds, run 'doctor heal' (all auto-fixable) or 'doctor heal wikilinks'. Use --all to diagnose EVERY vault discovered under a root (directories containing a .vaultmind/ subdir), with a combined rollup plus a per-vault report. --root sets where discovery starts (default: the current directory). --all composes with --summary and --json (one combined envelope). When a Contract-B mesh signal exists (an identity key, a pinned network anchor, a --mesh-* flag, or a reachable local daemon) doctor also reports a mesh-identity section: key custody, whether your binding authenticates against a PINNED root, and chat reachability. Green is reserved for a cryptographically authenticated binding; everything else is a warning (doctor stays exit-0). --mesh-root-pubkey pins the root explicitly (otherwise the enroll-persisted anchor is used), --mesh-registry verifies an offline registry file, --mesh-slug overrides the agents.yaml slug, and --mesh-heartbeat overrides the wake-watcher heartbeat path. Doctor also lists notes whose status is draft (awaiting review: id, path, age) and .md files git reports as untracked or modified for more than --uncommitted-days (default 7). Neither list is an error. Accepting a draft is editing its status off "draft" — the same file, no copy. The uncommitted check is skipped, with a reason, when the vault is not a git repository. It lists open gaps (id, question, and age), how many gaps were opened and closed, the median days to close, and weak asks that did not match a note.`, ConfigPrefix: "app.doctor", FlagOverrides: map[string]string{ "app.doctor.vault": "vault", "app.doctor.json": "json", "app.doctor.summary": "summary", "app.doctor.all": "all", "app.doctor.root": "root", "app.doctor.mesh_root_pubkey": "mesh-root-pubkey", "app.doctor.mesh_registry": "mesh-registry", "app.doctor.mesh_slug": "mesh-slug", "app.doctor.mesh_heartbeat": "mesh-heartbeat", "app.doctor.uncommitted_days": "uncommitted-days", }, }
DoctorMetadata defines the metadata for the doctor command.
var EvalMetadata = config.CommandMetadata{ Use: "eval <queries.yaml>", Short: "Score a vault's retrieval against labelled queries: Hit@1, Hit@K, MRR", Long: "Run each labelled query through the vault's retriever and report how often a " + "relevant note came first (Hit@1), made the top K (Hit@K), and the mean reciprocal " + "rank of the first relevant note (MRR). Queries that did not put a relevant note " + "first are listed with the rank they got.\n\n" + "The query file is a YAML list, one entry per query:\n\n" + " - name: rrf\n" + " text: how does reciprocal rank fusion combine lanes\n" + " expected: [concept-reciprocal-rank-fusion, concept-hybrid-search]\n\n" + "`expected` names the notes a good answer surfaces; order is not significant. Use " + "real questions and label them without looking at the current ranking, so a " + "change to retrieval can be judged better or worse, not just different.\n\n" + " vaultmind eval queries.yaml --vault ./vaultmind-vault\n" + " vaultmind eval queries.yaml --vault ./vaultmind-vault --k 10 --json", ConfigPrefix: "app.eval", FlagOverrides: map[string]string{ "app.eval.vault": "vault", "app.eval.k": "k", "app.eval.json": "json", }, }
EvalMetadata defines the `eval` command — retrieval scored against labels.
var ExperimentCompareMetadata = config.CommandMetadata{ Use: "compare", Short: "Surface where retrieval variants disagree, without needing labeled ground truth", Long: `Read recent ask/search/context-pack events that recorded shadow variants and compute pairwise rank-agreement metrics (Jaccard@K and Kendall's tau) for each variant pair. Reports per-pair means and event counts, with an optional per-event breakdown. This command measures disagreement, not correctness. It answers: "do these two variants tend to return different results?" without requiring any labeled target notes. Contrast with "experiment report", which measures absolute quality and requires labeled targets. METRICS Jaccard@K: Intersection over union of the top-K result sets from two variants (0 = no overlap, 1 = identical top-K). Use K to control how deep in the ranking you care about. Kendall tau: Rank correlation on items shared by both variants (-1 = fully reversed, 0 = uncorrelated, 1 = identical order). Undefined when fewer than 2 results are shared. WHEN TO USE Use "experiment compare" when you have two or more retrieval variants (e.g., a new ranking model shadowing the primary) and want to measure how often they diverge, before investing in labeling ground truth for a full quality evaluation via "experiment report". FLAGS --k: top-K depth for Jaccard and the cap on list length for Kendall's tau (int; default 10). --per-event: emit one row per event in addition to aggregates (bool). --since: only include events at or after this RFC3339 timestamp (string). --session: restrict to a single session ID (string). --caller: restrict to a single caller label (string). --event-type: restrict to one event type: ask, search, or context_pack (string; default: all three). --json: emit JSON instead of a table (bool). EXAMPLES vaultmind experiment compare # Compare all variants across all recent events, K=10. vaultmind experiment compare --k 5 # Stricter: only count a result as "in top K" if it is in the top 5. vaultmind experiment compare --since 2024-06-01T00:00:00Z # Only events on or after a cutoff date. vaultmind experiment compare --session abc123 --per-event # Restrict to one agent session and emit one row per event. vaultmind experiment compare --event-type ask --json # Only ask events, machine-readable output for scripting.`, ConfigPrefix: "app.experimentcompare", FlagOverrides: map[string]string{ "app.experimentcompare.session": "session", "app.experimentcompare.caller": "caller", "app.experimentcompare.since": "since", "app.experimentcompare.event_type": "event-type", "app.experimentcompare.k": "k", "app.experimentcompare.per_event": "per-event", "app.experimentcompare.json": "json", }, }
ExperimentCompareMetadata defines metadata for the experiment compare command.
var ExperimentReportMetadata = config.CommandMetadata{ Use: "report", Short: "Measure retrieval quality: Hit@K and MRR per variant", Long: `Read the local experiment database and compute Hit@K and MRR for each variant. These are absolute quality metrics that tell you how well a retrieval variant performs when a labeled target exists. METRICS Hit@K: Fraction of lookups where the target note appeared in the top K results (0.0 = never found, 1.0 = always in top K). MRR: Mean Reciprocal Rank — average of 1/rank for each lookup where the target was found (rank 1 = 1.0, rank 2 = 0.5, rank 3 = 0.33). WHEN TO USE Use "experiment report" when you want to know whether a variant is actually correct — not just whether two variants disagree. It requires events where a target note was labeled at retrieval time (e.g., spreading-activation lookups where the source note is known). Contrast with "experiment compare", which measures variant disagreement and needs no labeled ground truth. FLAGS --experiment: restrict to a named experiment (string; default: all experiments). --k: K value for Hit@K (int; default 10). --json: emit JSON instead of a table (bool). EXAMPLES vaultmind experiment report # Hit@K and MRR across all variants and experiments, K=10. vaultmind experiment report --k 5 # Stricter threshold: target must appear in the top 5 to count. vaultmind experiment report --experiment spreading-activation-v2 # Metrics only for the named experiment. vaultmind experiment report --experiment spreading-activation-v2 --json # Machine-readable output for scripting or logging.`, ConfigPrefix: "app.experimentreport", FlagOverrides: map[string]string{ "app.experimentreport.experiment": "experiment", "app.experimentreport.json": "json", "app.experimentreport.k": "k", }, }
ExperimentReportMetadata defines metadata for the experiment report command.
var ExperimentSummaryMetadata = config.CommandMetadata{ Use: "summary", Short: "Memory usage overview: top recalled notes, session gap stats", Long: `Read the local experiment database and report which notes are recalled most often, how many unique notes have been retrieved, and the distribution of time gaps between sessions. Use this as the weekly readout on whether recall is staying focused or drifting. A small number of notes dominating the top list may signal retrieval fixation; many unique notes with long gaps between sessions may signal low vault engagement. OUTPUT INCLUDES Top-N most-recalled notes (note id, title, recall count). Total unique notes retrieved across all sessions. Session gap distribution (min, median, max time between sessions). FLAGS --top: number of top-recalled notes to show (int; default 10). --json: emit JSON instead of a table (bool). EXAMPLES vaultmind experiment summary # Overview with the top 10 recalled notes and session gap stats. vaultmind experiment summary --top 20 # Widen to the top 20 notes to see if recall coverage is broad. vaultmind experiment summary --json # Machine-readable output for logging or scripting.`, ConfigPrefix: "app.experimentsummary", FlagOverrides: map[string]string{ "app.experimentsummary.json": "json", "app.experimentsummary.top": "top", }, }
ExperimentSummaryMetadata defines metadata for the experiment summary command.
var ExperimentTraceMetadata = config.CommandMetadata{ Use: "trace", Short: "Drill into a specific session or note's retrieval history", Long: "Given --session <id>, reports that session's caller (agent + operator) and every retrieval it performed in order. Given --note <id>, reports every session that retrieved that note with caller attribution. Exactly one of --session or --note must be provided.", ConfigPrefix: "app.experimenttrace", FlagOverrides: map[string]string{ "app.experimenttrace.session": "session", "app.experimenttrace.note": "note", "app.experimenttrace.json": "json", }, }
ExperimentTraceMetadata defines metadata for the experiment trace command.
var ExportMetadata = config.CommandMetadata{ Use: "export", Short: "Export experiment data as a sanitized JSONL snapshot", Long: `Export the experiment DB (sessions, events, outcomes) as a JSONL stream that you can share with the VaultMind team. The output is sanitized according to your configured telemetry tier: anonymous Strips vault paths, query text, and result note_ids/paths. Aggregate shape (ranks, scores, counts, timestamps, variant names) is preserved. full Preserves everything. Use only if you've explicitly opted into Full data sharing on first run. off Refused — the user opted out, so producing a file would imply we collected data we shouldn't have. OUTPUT SHAPE A JSONL stream — one JSON object per line: {"kind":"manifest","tier":"anonymous","exported_at":"...",...} {"kind":"session","session_id":"...","started_at":"...","ended_at":"..."} {"kind":"event","event_id":"...","event_type":"ask","data":{...}} {"kind":"outcome","outcome_id":"...","rank":1,"variant":"primary",...} EXAMPLES vaultmind export Stream JSONL to stdout. Pipe to gzip or redirect: vaultmind export | gzip > vm-export-$(date +%Y%m%d).jsonl.gz vaultmind export --output ./vm-export.jsonl Write to a file directly. vaultmind export --tier full Override the configured tier (the tier only selects what is stripped; export never required a consent record). PRIVACY The Anonymous tier's stripping contract is tested in internal/experiment/export_test.go. If the tests change, the contract documented in telemetry.go must change too.`, ConfigPrefix: "app.export", FlagOverrides: map[string]string{ "app.export.output": "output", "app.export.tier": "tier", "app.export.rollup": "rollup", "app.export.vault": "vault", "app.export.preview": "preview", }, }
ExportMetadata defines the metadata for the export command — produces a sanitized JSONL snapshot of the experiment DB for sharing with the VaultMind team. Tier-aware: Anonymous strips note_ids, paths, and queries per the contract documented in internal/experiment/telemetry.go; Full preserves everything.
var FrontmatterFixMetadata = config.CommandMetadata{ Use: "fix", Short: "Backfill missing `created` frontmatter on domain notes", Long: `Walk the vault and identify domain notes missing the ` + "`created`" + ` field. Surface what's missing and the proposed value; with --apply, write the addition atomically. Provenance for ` + "`created`" + `: git first-commit (when the file is tracked) → file mtime → today's date. The first successful resolution wins. User-owned fields (title, status, tags, related_ids, etc.) are NEVER touched by this command — only the missing ` + "`created`" + ` field gets backfilled. Default is dry-run. Pass --apply to write changes. Output as JSON with --json. Examples: vaultmind frontmatter fix --vault . # dry-run audit vaultmind frontmatter fix --vault . --apply # backfill missing created vaultmind frontmatter fix --vault . --json # machine-readable audit`, ConfigPrefix: "app.frontmatterfix", FlagOverrides: map[string]string{ "app.frontmatterfix.vault": "vault", "app.frontmatterfix.json": "json", "app.frontmatterfix.apply": "apply", }, }
FrontmatterFixMetadata defines metadata for the `frontmatter fix` command.
Surfaces domain notes that are missing the `created` field and (with --apply) writes it. Migration scenarios — existing-vault notes that predate any auto-stamp era — are the primary use case.
Default mode is dry-run: the command prints what would change but does not write. Per the extend-don't-overwrite principle, vaultmind never silently rewrites user files; --apply is the explicit gate.
Provenance for the `created` value: git log first-commit (most accurate signal — actual creation date in version control), file mtime, today's date — tried in order, the first successful one wins.
Scope was originally `created` + `vm_updated` (the four-tier vaultmind-owned tier). The 2026-05-04 dogfood pass retired vm_updated entirely (no read-side consumer survived the false-positive collapse of mtime-based drift), so this command now covers only `created`.
var FrontmatterMergeMetadata = config.CommandMetadata{ Use: "merge <note-id-or-path> --file <yaml-file>", Short: "Merge multiple frontmatter fields", Long: `Merge key-value pairs from a YAML file into the note's frontmatter. Arguments: note-id-or-path Note ID, title, or file path (resolved via entity resolution) Required flags: --file Path to a YAML file containing key-value pairs to merge The YAML file should contain top-level keys matching frontmatter fields. Existing keys are overwritten; new keys are added. Use --allow-extra to merge keys not defined in the type schema. Use --dry-run --diff to preview.`, ConfigPrefix: "app.frontmattermerge", FlagOverrides: map[string]string{ "app.frontmattermerge.vault": "vault", "app.frontmattermerge.json": "json", "app.frontmattermerge.dry_run": "dry-run", "app.frontmattermerge.diff": "diff", "app.frontmattermerge.commit": "commit", "app.frontmattermerge.allow_extra": "allow-extra", "app.frontmattermerge.file": "file", }, }
FrontmatterMergeMetadata defines metadata for the frontmatter merge command.
var FrontmatterNormalizeMetadata = config.CommandMetadata{ Use: "normalize [<file-or-path>] [flags]", Short: "Normalize frontmatter on a file (sort keys, fix aliases/tags, dates, snake_case)", Long: `Normalize frontmatter formatting on a single vault file. Applies these transformations to the target file's frontmatter: - Sort keys into canonical order - Convert scalar aliases and tags fields to lists - Normalize all date fields (use --strip-time to truncate to date-only) - Convert all keys to snake_case The optional argument is the path to a note file. When omitted, "." is used as the target (current directory). FLAGS --vault string Path to vault root (default ".") --dry-run Preview changes without writing to disk --diff Show a unified diff of the proposed changes --commit Stage and commit the file after applying changes --strip-time Convert all datetime fields to date-only (drop time component) --json Output result in JSON format EXAMPLES vaultmind frontmatter normalize notes/my-note.md --dry-run --diff Preview what normalizations would be applied, showing a unified diff. vaultmind frontmatter normalize notes/my-note.md Apply all normalizations and write the file. vaultmind frontmatter normalize notes/my-note.md --strip-time --commit Normalize and strip time from dates, then stage and commit the change. vaultmind frontmatter normalize notes/my-note.md --json Apply normalizations and return structured JSON output. WHEN TO USE Use "frontmatter normalize" to clean up a single file before review or commit. For setting or removing individual fields, use "frontmatter set" or "frontmatter unset". For bulk key-value updates from a YAML file, use "frontmatter merge".`, ConfigPrefix: "app.frontmatternormalize", FlagOverrides: map[string]string{ "app.frontmatternormalize.vault": "vault", "app.frontmatternormalize.json": "json", "app.frontmatternormalize.dry_run": "dry-run", "app.frontmatternormalize.diff": "diff", "app.frontmatternormalize.commit": "commit", "app.frontmatternormalize.strip_time": "strip-time", }, }
FrontmatterNormalizeMetadata defines metadata for the frontmatter normalize command.
var FrontmatterSetMetadata = config.CommandMetadata{ Use: "set <note-id-or-path> <key> <value>", Short: "Set a frontmatter field", Long: `Set a single frontmatter key to a value. Validates against the type schema. Arguments: note-id-or-path Note ID, title, or file path (resolved via entity resolution) key Frontmatter key to set (e.g., "status", "tags") value Value to assign (strings, numbers, JSON arrays like '["a","b"]') A value that parses as a JSON array is written as a YAML list. To store such text as text, write it as a JSON string: '"[a, b]"'. The key is validated against the note's type schema. Use --allow-extra to set keys not defined in the schema. Use --dry-run --diff to preview the change.`, ConfigPrefix: "app.frontmatterset", FlagOverrides: map[string]string{ "app.frontmatterset.vault": "vault", "app.frontmatterset.json": "json", "app.frontmatterset.dry_run": "dry-run", "app.frontmatterset.diff": "diff", "app.frontmatterset.commit": "commit", "app.frontmatterset.allow_extra": "allow-extra", }, }
FrontmatterSetMetadata defines metadata for the frontmatter set command.
var FrontmatterUnsetMetadata = config.CommandMetadata{ Use: "unset <note-id-or-path> <key>", Short: "Remove a frontmatter field", Long: `Remove a frontmatter key from a note's frontmatter. Arguments: note-id-or-path Note ID, title, or file path (resolved via entity resolution) key Frontmatter key to remove Returns an error if the key is required by the note's type schema. Use --dry-run --diff to preview the change.`, ConfigPrefix: "app.frontmatterunset", FlagOverrides: map[string]string{ "app.frontmatterunset.vault": "vault", "app.frontmatterunset.json": "json", "app.frontmatterunset.dry_run": "dry-run", "app.frontmatterunset.diff": "diff", "app.frontmatterunset.commit": "commit", }, }
FrontmatterUnsetMetadata defines metadata for the frontmatter unset command.
var FrontmatterValidateMetadata = config.CommandMetadata{ Use: "validate", Short: "Check vault notes for frontmatter rule violations", Long: `Scan all domain notes and report frontmatter violations against the type registry. Catches four rule classes: missing required fields, invalid status values, unknown types, and broken frontmatter references (explicit_relation edges pointing to non-existent notes). MODES Default (indexed): Reads from the vault's SQLite index built by "vaultmind index". Fast. Use for routine health checks and CI pipelines. Reports the index hash in JSON output so results are traceable to a specific index snapshot. --live: Reads raw .md files on disk instead of the indexed database. Use when the index may be stale or you want to validate before indexing. No index hash is reported in this mode. A .md symlink is never read: it is reported as a skipped_symlink warning. FLAGS --vault PATH Path to vault root (default: ".") --live Validate raw .md files on disk instead of the indexed database --json Output structured JSON instead of human-readable text OUTPUT INCLUDES (human-readable) "Checked N files: M valid, K issues" Per issue: [severity] path: message (rule) Severity values: error (missing required field), warning (unknown type, invalid status, broken reference, skipped symlink) Rule values: missing_required_field, unknown_type, invalid_status, broken_reference, skipped_symlink (--live only) OUTPUT INCLUDES (--json) status: "ok" or "warning" (warning when any issues found) result: files_checked total notes scanned valid notes with no violations issues[] array of {path, id, severity, rule, message, field, value} meta: vault_path vault root used index_hash index snapshot identifier (empty in --live mode) EXAMPLES vaultmind frontmatter validate # check indexed database in current vault vaultmind frontmatter validate --vault ./my-vault # check a specific vault (indexed) vaultmind frontmatter validate --live # check raw .md files before indexing vaultmind frontmatter validate --json # machine-readable output for CI`, ConfigPrefix: "app.frontmatter", FlagOverrides: map[string]string{ "app.frontmatter.vault": "vault", "app.frontmatter.json": "json", "app.frontmatter.live": "live", }, }
FrontmatterValidateMetadata defines the metadata for the frontmatter validate command.
var GitStatusMetadata = config.CommandMetadata{ Use: "status", Short: "Git repository state for VaultMind policies", Long: "Report git repo state relevant to VaultMind mutation policies: branch, dirty files, merge/rebase status.", ConfigPrefix: "app.gitstatus", FlagOverrides: map[string]string{ "app.gitstatus.vault": "vault", "app.gitstatus.json": "json", }, }
GitStatusMetadata defines the metadata for the git status command.
var HooksInstallMetadata = config.CommandMetadata{ Use: "install [project-dir]", Short: "Install hook scripts into a project or user scope", Long: `Write VaultMind's Claude Code hook scripts to <project-dir>/.claude/scripts/. The scripts are embedded in the vaultmind binary; this command copies them out for use by Claude Code's hook system. SCOPE (--scope project|user) project (default): the existing project install, unchanged. user: install scripts under ~/.vaultmind/scripts and wire them by absolute path, so they run in every project. --merge targets ~/.claude/settings.json. The optional project-dir argument (default: current directory) selects the project to compare for overlaps; installation still targets the user files. --vault must be absolute at user scope: the hook cwd varies per project. Without --vault, hooks use vaultmind-identity in each active project. Existing differing scripts are protected; --force overwrites them. --merge --dry-run previews scripts and merged settings without writing. Existing hooks and unrelated settings are preserved; reruns are idempotent. Generated legacy user commands are migrated to absolute script paths. Custom commands are reported as conflicts and never rewritten, even with --force. --dry-run shows the resulting commands without writing. When user and project scopes wire the same script on the same event, install warns that it can fire twice. Remove one scope's wiring, or install only non-overlapping scripts with --only; neither scope is removed automatically. --local is project-only. Profiles are recorded in ~/.vaultmind/vaultmind-profile. User scope also supports --agent codex ($CODEX_HOME/hooks.json, default ~/.codex/hooks.json) and --agent cursor (~/.cursor/hooks.json). Both use absolute script paths under ~/.vaultmind/scripts, and determine the active project at hook runtime. Codex user hooks still need approval in /hooks, including after upgrades; project trust is required for project hooks only. Verified user hook locations: https://learn.chatgpt.com/docs/hooks and https://cursor.com/docs/hooks. hooks uninstall currently handles projects only. CODEX (--agent codex) Wires the same scripts into Codex CLI through <project-dir>/.codex/hooks.json, with the scripts under <project-dir>/.vaultmind/scripts/ (a Codex project gets no .claude/ folder): identity at session start, the health check, recall on every prompt, vault context before consequential commands, and episode capture when the session ends. Each command exports VAULTMIND_PROJECT_DIR and runs its script from it; Codex sets no project variable of its own. When the project is the root of a git work tree, the command asks git for it at run time ($(git rev-parse --show-toplevel)), so the committed .codex/hooks.json holds no absolute path and works from any subdirectory and any checkout. Otherwise the project path is written in (a vault outside the project stays absolute either way). Re-running install upgrades an older absolute command in place. Two steps Codex requires, and skips SILENTLY without: 1. trust the project (Codex asks when you first open it), and 2. approve the hooks: run /hooks inside Codex and trust them. Until both are done Codex starts with no memory and says nothing about it. Step 2 is not one-time: after an upgrade, a new or changed hook is skipped until you approve it again. ` + "`vaultmind hooks status <project-dir>`" + ` checks each VaultMind hook the way Codex does (never approved, or changed since), and fails while any would be skipped. Not wired for Codex yet: read-tracking (Codex has no Read tool), the code map, and the pre-compaction prompt (Codex cannot add context there). CURSOR (--agent cursor) Wires the same scripts into Cursor through <project-dir>/.cursor/hooks.json, each run through cursor-adapter.sh, with the scripts under <project-dir>/.vaultmind/scripts/. Cursor adds context only at session start and after a tool has run, so it gets: the vault map (and the persona, under the persona profile) at session start; the notes about a file after the agent reads or edits it; related notes after a commit. Per-prompt recall is not possible — Cursor's hooks cannot add context to a prompt. PROJECT-DIR Directory of the project to install hooks into. Defaults to the current working directory if omitted. The script writes to <project-dir>/.claude/scripts/. Creates the directory if missing. WHAT GETS INSTALLED The profile decides which scripts are written to .claude/scripts/ and wired by --merge (see --profile). The hooks, by event: vaultmind-health.sh SessionStart — vault health, the vault map, stale-hook warnings load-persona.sh SessionStart — identity context (persona and full only) load-arcs.sh SessionStart — the arc layer, every arc, in a hook of its own: each hook's output is cut to a preview past ~10,000 characters (persona and full only) vault-recall.sh UserPromptSubmit — the notes that match the prompt, as excerpts vault-track-read.sh PreToolUse(Read) — records which notes are read vault-reach.sh PreToolUse(Bash|Edit|Write|MultiEdit) — related notes before a commit, push, merge or vault write vault-code-map.sh PreToolUse(Read|Edit|Write|MultiEdit) — the notes whose paths: cover the file being opened precompact-preserve.sh PreCompact — asks what to write down before context is lost capture-episode.sh SessionEnd — episode transcript capture (persona and full only) The knowledge profile (a fresh project's default) wires health, recall, read-tracking, reach, the code map and pre-compaction; persona adds the identity loader and episode capture; full writes every script. Helpers (written by the full profile, not wired): vault-block-read.sh parked variant — block-and-redirect on Read auto-rag-guard.sh PreToolUse drift gate (opt-in — see auto-RAG) auto-rag-evaluate.sh scores auto-RAG guard decisions shell-strip.sh shared helper sourced by the auto-RAG scripts mesh-watch.sh the agent-mesh watcher, run by hand After writing the scripts, this command prints the .claude/settings.json "hooks" stanza that wires them. Pass --merge to apply it for you instead of copy-pasting (see WIRING below). --vault: point the hooks at a specific vault. The stanza bakes VAULTMIND_VAULT=<path> into every command so recall, read-tracking, episode capture, and persona loading all target that vault instead of the built-in default ($CLAUDE_PROJECT_DIR/vaultmind-identity). Omit it to keep the default convention. WIRING (--merge) --merge additively merges the profile's hook entries straight into the project's hook config — no hand-editing. The merge NEVER clobbers: a project's own hooks are preserved, our entries are de-duplicated (so re-running is a no-op), all other settings and key order are kept, and malformed settings error out before any write. Pair with "hooks uninstall" to cleanly remove only our entries later. --local: merge into .claude/settings.local.json (gitignored, personal) instead of .claude/settings.json (committed, team-shared). Choose local for a personal persona vault; the default for a capability the whole team should get. The scripts and .claude/vaultmind-profile are added to the checkout's git exclude in a marked block; uninstall --local removes it. Outside a git work tree, installation reports why exclusion was skipped. --dry-run: with --merge, print the merged result without writing it — use it to preview/diff before applying. OVERWRITE PROTECTION Default: refuses to write any script that already exists. Lists the conflicts and exits non-zero so the agent can surface them. --force: overwrites existing scripts unconditionally. Use when refreshing after a vaultmind binary upgrade (the canonical source updates; existing copies become stale). --only: comma-separated subset of canonical scripts to install and wire (e.g. --only auto-rag-guard.sh,shell-strip.sh,auto-rag-evaluate.sh). Consumers who've customized some hooks but want a clean canonical install of others — pass --only to scope the install. Unknown names rejected at lint time so typos surface explicitly. Existing wiring for scripts outside --only is kept; remove unwanted wiring explicitly before using --only to avoid an existing overlap. Doctor's hook-drift check (vaultmind doctor) flags when installed copies differ from the embedded canonical, so refresh-need is surfaced explicitly rather than left to guessing. EXAMPLES vaultmind hooks install # a fresh project gets the knowledge set vaultmind hooks install ~/dev/myproject # the same, into a specific project vaultmind hooks install --profile persona # an agent identity vault: persona loader + episodes vaultmind hooks install --scope user --vault /absolute/knowledge --merge # hooks in every project vaultmind hooks install --scope user --merge --force --dry-run # preview a user install upgrade vaultmind hooks install --force # refresh after binary upgrade vaultmind hooks install --json # machine-readable output vaultmind hooks install --only auto-rag-guard.sh,shell-strip.sh # only the auto-RAG slice (companion-project pattern) vaultmind hooks install --vault ./my-knowledge # print a stanza wired to a specific vault vaultmind hooks install --vault ./my-knowledge --merge --dry-run # preview the merge without writing vaultmind hooks install --vault ./my-knowledge --merge # write scripts AND wire settings.json vaultmind hooks install --vault ./my-knowledge --merge --local # wire personal settings.local.json instead vaultmind hooks install --agent codex --merge # wire Codex CLI (.codex/hooks.json) instead of Claude Code vaultmind hooks install --profile persona --vaults ./identity,./desk --merge # recall searches both; persona loads ./identity`, ConfigPrefix: "app.hooksinstall", FlagOverrides: map[string]string{ "app.hooksinstall.scope": "scope", "app.hooksinstall.force": "force", "app.hooksinstall.json": "json", "app.hooksinstall.only": "only", "app.hooksinstall.profile": "profile", "app.hooksinstall.vault": "vault", "app.hooksinstall.merge": "merge", "app.hooksinstall.local": "local", "app.hooksinstall.dryrun": "dry-run", "app.hooksinstall.agent": "agent", "app.hooksinstall.vaults": "vaults", }, }
HooksInstallMetadata defines the metadata for the `vaultmind hooks install` subcommand. Writes the embedded Claude Code hook scripts to <project>/.claude/scripts/, where the user's hook config (.claude/settings.json) can invoke them.
Default mode refuses to overwrite existing scripts; --force overwrites. Refresh after a binary upgrade is the same command with --force; doctor's hook-drift check (separate work) flags when stale copies need refreshing.
Per the extend-don't-overwrite principle, vaultmind never silently rewrites user files. Refuse-by-default + explicit --force is the same gate `vaultmind init` uses.
var HooksRecordMetadata = config.CommandMetadata{ Use: "record <event>", Short: "Record that a hook fired, so a zero can be told apart from a no-show", Long: `Record that a VaultMind hook fired. Some hooks PROMPT rather than QUERY. A prompt leaves no trace in the usage log, so "no notes were banked" and "the prompt was never shown" produce identical evidence. This writes the missing denominator. It is a denominator, not a score. Firings are not success — the outcome metric stays "were notes actually banked". This exists only so that a zero is interpretable. EVENTS Only names on the allowlist are accepted. Adding one is a reviewed diff, not a runtime string, because everything written here is later read as evidence. write_prompt the PreCompact write-path prompt was shown to the agent WHAT IS WRITTEN The event name, the vault path, and nothing else. No content, no query text. EXAMPLES vaultmind hooks record write_prompt --vault ~/vault vaultmind hooks record write_prompt --vault ~/vault --json`, ConfigPrefix: "app.hooksrecord", FlagOverrides: map[string]string{ "app.hooksrecord.json": "json", "app.hooksrecord.vault": "vault", }, }
HooksRecordMetadata defines `vaultmind hooks record` — the did-it-run gate for hooks that prompt rather than query.
The read-path hooks leave a trail: every recall and reach writes an ask or note_access row. The PreCompact write-path prompt left nothing at all, so a measurement window that ends with zero notes banked could not distinguish "the prompt fired and the agent ignored it" from "the prompt never fired" — a discipline problem and a wiring problem look identical, and only one of them is fixable by trying harder.
The event name is an ALLOWLIST (internal/hooks.RecordableEvents), not free text. The usage log is the evidence base for every retrieval measurement, and a general "record any event" CLI would let any script or typo write rows that later read as agent behaviour — the hole #121 closed, reopened from the CLI.
var HooksStatusMetadata = config.CommandMetadata{ Use: "status [project-dir]", Short: "Check installed hook scripts and wiring at project or user scope", Long: `Compare the hook scripts a project has installed against the canonical copies embedded in this binary, and report what differs. PROJECT-DIR Directory of the project to check. Defaults to the current working directory if omitted. SCOPE (--scope project|user) project (default): check the selected project. Also compare its wiring with user-scope wiring for Claude Code, Codex and Cursor. Overlapping scripts on the same event are reported with both settings paths and fail status. user: check ~/.vaultmind/scripts against canonical scripts and the wiring in ~/.claude/settings.json. Also check Codex user hooks ($CODEX_HOME/hooks.json, default ~/.codex/hooks.json), including approvals, and ~/.cursor/hooks.json when present. The project-dir argument (default: current directory) selects the project to compare for overlaps; user scripts/settings still live in HOME. Every wired script path must exist; project-variable paths are broken at user scope. Broken entries name the exact command and a fix. User profiles are recorded in ~/.vaultmind/vaultmind-profile; undeclared older installs are judged as full. Refresh differing scripts with hooks install --scope user --force --merge; preview first with --dry-run. STATES in sync the installed copy behaves identically to the canonical one. Comments and blank lines are ignored — a hook's behaviour is its code, and flagging annotations would make the whole report noise. drifted the installed copy differs behaviourally. Either this project changed it — in which case the change should go upstream, or an update will overwrite it — or the binary moved ahead and the project should update. missing this binary ships the hook and the project does not have it. Reported separately because an absence that renders as nothing looks exactly like health. EXIT CODE 0 when all expected scripts are in sync and events are wired, and Codex hooks are approved. Non-zero for drifted, missing, unwired, broken or overlapping hooks, so this is usable in a check: a status command that always exits 0 cannot gate anything. EXAMPLES vaultmind hooks status # check the current project vaultmind hooks status ~/dev/myproject # check another project vaultmind hooks status --scope user # inspect hooks across all projects vaultmind hooks status --json # machine-readable vaultmind hooks install --force . # the fix for drift`, ConfigPrefix: "app.hooksstatus", FlagOverrides: map[string]string{ "app.hooksstatus.scope": "scope", "app.hooksstatus.json": "json", }, }
HooksStatusMetadata defines `vaultmind hooks status` — the read-only comparison between the hook scripts this binary ships and the ones a project has installed.
The `hooks` help has advertised drift-checking and status since the command existed, while only install and uninstall were implemented. More to the point: doctor reported DRIFT but nothing reported ABSENCE, so a hook the binary ships and the project never installed looked exactly like a hook that matched. Two hooks stayed missing from every adopter for months that way.
var HooksUninstallMetadata = config.CommandMetadata{ Use: "uninstall [project-dir]", Short: "Remove VaultMind's hook entries from a project (Claude Code or Codex)", Long: `Strip VaultMind's hook entries from a project's Claude Code hook config, or with --agent codex from .codex/hooks.json. The inverse of "hooks install --merge": it removes only the entries that reference VaultMind's canonical scripts, leaving a project's own hooks and all unrelated settings (permissions, etc.) intact. PROJECT-DIR Directory of the project to clean. Defaults to the current working directory if omitted. WHAT GETS REMOVED Any hook entry whose command invokes one of VaultMind's scripts (load-persona.sh, load-arcs.sh, vault-recall.sh, vault-track-read.sh, capture-episode.sh, and the auto-RAG helpers). An event array left empty is dropped, and the "hooks" object is dropped if it becomes empty. Entries that merely have a similar name are NOT matched — removal is anchored to the exact script path, so a project's own hooks are never deleted. --local: clean .claude/settings.local.json instead of the default .claude/settings.json. Also remove the marked block for the scripts and profile file added to the checkout's git exclude by install --local. Run once per settings file if you wired into both. --remove-scripts: also delete the installed scripts under .claude/scripts/ (Codex: .vaultmind/scripts/). Off by default — uninstall leaves scripts in place unless you ask for them too. --agent codex: clean .codex/hooks.json instead, and with --remove-scripts .vaultmind/scripts/. The .vaultmind folder itself is never removed (it can also hold a vault's data), and Codex's own approval records are not touched. EXAMPLES vaultmind hooks uninstall # strip our entries from settings.json vaultmind hooks uninstall --local # strip from settings.local.json vaultmind hooks uninstall --remove-scripts # also delete .claude/scripts/*.sh vaultmind hooks uninstall --agent codex # strip our entries from .codex/hooks.json vaultmind hooks uninstall ~/dev/myproject --json # machine-readable output`, ConfigPrefix: "app.hooksuninstall", FlagOverrides: map[string]string{ "app.hooksuninstall.json": "json", "app.hooksuninstall.local": "local", "app.hooksuninstall.removescripts": "remove-scripts", "app.hooksuninstall.agent": "agent", }, }
HooksUninstallMetadata defines the metadata for the `vaultmind hooks uninstall` subcommand — the inverse of `hooks install --merge`. It strips VaultMind's hook entries from a project's hook config, removing ONLY entries that reference our canonical scripts. A project's own hooks and any unrelated settings are left untouched.
Per the extend-don't-overwrite principle, vaultmind never silently mutates user files beyond its own footprint: uninstall is surgical, and --remove-scripts (off by default) is the explicit opt-in to also delete the installed script files.
var IdentityEnrollAddMetadata = config.CommandMetadata{ Use: "enroll-add", Short: "Admin-add a member's enrollment request to the trust-root registry (Contract-B)", Long: `Run the Contract-B ADMIN side of onboarding. Read a member's signed enrollment-request JSON (the output of ` + "`identity enroll`" + `), VERIFY proof-of-possession, confirm the request is for YOUR network, add the member's binding to the trust-root REGISTRY, and print the UPDATED UNSIGNED registry to stdout for ` + "`identity sign-registry`" + ` to root-sign. This verb does NOT sign — the established two-verb split (build-then-sign): enroll > request.json -> enroll-add --request request.json --registry current.json > new-unsigned.json -> sign-registry --file new-unsigned.json > new-signed.json (ROOT signer) --registry accepts either an UNSIGNED wireRegistry or a SIGNED distribution envelope (auto-detected); absent => a fresh registry (first emit = epoch 1). A signed-envelope --registry is INTEGRITY-verified against --root-pubkey before any mutation (the root signature must hold — freshness/rollback are NOT checked, so an admin can mutate a stale registry) and is REFUSED without --root-pubkey. At least one of --root-pubkey or --network-id is required to resolve the admin network; when both are set they must AGREE. A request whose network_id is not the admin network is REFUSED (cross-network). Uniqueness is checked FAIL-CLOSED before the append: a duplicate {slug,key_epoch} or a slug that already has a live binding is refused (a bad append would poison the whole registry at the consumer). NOTE: transport_pubkey/transport_endpoint are NOT yet carried into the binding (WireGuard wiring is a later slice). This CLI is KEYLESS: it never signs.`, ConfigPrefix: "app.identityenrolladd", FlagOverrides: map[string]string{ "app.identityenrolladd.request": "request", "app.identityenrolladd.registry": "registry", "app.identityenrolladd.root_pubkey": "root-pubkey", "app.identityenrolladd.network_id": "network-id", "app.identityenrolladd.validity_seconds": "validity-seconds", "app.identityenrolladd.origin_daemon": "origin-daemon", }, }
IdentityEnrollAddMetadata defines metadata for the `identity enroll-add` command — the ADMIN counterpart to `identity enroll`.
var IdentityEnrollMetadata = config.CommandMetadata{ Use: "enroll", Short: "Enroll into a Contract-B network: consume an invite, confirm the fingerprint, self-sign a request", Long: `Run the Contract-B MEMBER-onboarding journey. Consume a network INVITE (a ` + "`vmenroll1:`" + ` token or an enroll URL), fetch the relay's /.well-known/vaultmind-root, CROSS-CHECK it against the invite's trust anchor, confirm the FINGERPRINT out of band, then assemble + self-sign an enrollment request via the KEYLESS signer and print the signed request JSON to stdout. The cross-checks are the security spine: the relay's advertised root MUST decode to a valid key, be self-consistent (network_id == NetworkID(root)), and match the invite's anchor (raw bytes AND network id). Any mismatch is a HARD error (wrong relay / MITM / misconfig) and the flow refuses to proceed. After signing, the request is SELF-VERIFIED (proof-of-possession). A self-verify failure means the running signer holds a DIFFERENT key than --pubkey — the flow fails loudly and emits NOTHING. --pubkey is your base64-std ed25519 IDENTITY pubkey from ` + "`identity init`" + `. --transport-pubkey is your base64-std WireGuard pubkey (` + "`wg pubkey`" + `). Pipe stdout to a file and hand it to your admin out of band; they run ` + "`vaultmind identity enroll-add`" + `. This CLI is KEYLESS: it never opens the key file.`, ConfigPrefix: "app.identityenroll", FlagOverrides: map[string]string{ "app.identityenroll.invite": "invite", "app.identityenroll.display_name": "display-name", "app.identityenroll.slug": "slug", "app.identityenroll.pubkey": "pubkey", "app.identityenroll.transport_pubkey": "transport-pubkey", "app.identityenroll.transport_endpoint": "transport-endpoint", "app.identityenroll.signer_socket": "signer-socket", "app.identityenroll.yes": "yes", }, }
IdentityEnrollMetadata defines metadata for the `identity enroll` command.
var IdentityFetchRegistryMetadata = config.CommandMetadata{ Use: "fetch-registry", Short: "Fetch the signed trust registry from the hub and keep the local copy current (Contract-B)", Long: `Download the signed registry the hub serves at /.well-known/vaultmind-directory, VERIFY it against the pinned root key, and store it at this machine's declared registry_path — so doctor and the mesh watcher can check it and count down to its lapse without anyone copying files by hand. Trust comes only from the root signature, never from the connection: - it refuses to run without a pinned root (--root-pubkey or its config key app.identityfetchregistry.root_pubkey, else the anchor ` + "`identity enroll`" + ` pinned); - it refuses anything that does not verify, or is past its valid_until; - it refuses an OLDER epoch than the local copy (a rollback); - the same epoch writes nothing; a newer one replaces the file atomically. The hub address and registry path come from --hub / --registry-file, else from agents.yaml (daemon_url, registry_path). Nothing is guessed: an undeclared address or path is an error.`, ConfigPrefix: "app.identityfetchregistry", FlagOverrides: map[string]string{ "app.identityfetchregistry.hub": "hub", "app.identityfetchregistry.registry_file": "registry-file", "app.identityfetchregistry.root_pubkey": "root-pubkey", }, }
IdentityFetchRegistryMetadata defines metadata for the `identity fetch-registry` command.
var IdentityInitMetadata = config.CommandMetadata{ Use: "init", Short: "Mint an agent keypair and seal the private key to the signer", Long: `Mint a per-agent ed25519 keypair, SEAL the private key to the signer's 0600 key file, and print the PUBLIC key. The private key is NEVER printed or logged. Re-running refuses to overwrite an existing sealed key. DEV-INTERIM: the sealed key is a raw 0600 file. Secure-Enclave wrapping and a dedicated service uid are deferred (see internal/identity/signer).`, ConfigPrefix: "app.identityinit", FlagOverrides: map[string]string{ "app.identityinit.signer_key": "signer-key", }, }
IdentityInitMetadata defines metadata for the `identity init` command.
var IdentityInviteMetadata = config.CommandMetadata{ Use: "invite", Short: "Emit an UNSIGNED network invite carrying the trust anchor (Contract-B)", Long: `Build a Contract-B agent-network INVITE from the network's ROOT public key and relay base URL, and print three blocks: the bootstrap TOKEN, the enroll URL (the token in its fragment), and the FINGERPRINT to share out of band. The invite is UNSIGNED by design: it CARRIES the network's root anchor (the ed25519 root public key) itself, so a signature would prove nothing. Authenticity comes OUT OF BAND — the admin reads the fingerprint (the vmnet1:… network id) to the member over a TRUSTED channel, and the member confirms it before ` + "`identity enroll`" + ` trusts the anchor. --root-pubkey is the base64-std value ` + "`identity init`" + ` printed for the root key. --relay is the relay base URL (e.g. https://chat.acme.com). Both are REQUIRED; an invalid root pubkey or empty relay FAILS CLOSED with an error and no output.`, ConfigPrefix: "app.identityinvite", FlagOverrides: map[string]string{ "app.identityinvite.root_pubkey": "root-pubkey", "app.identityinvite.relay": "relay", }, }
IdentityInviteMetadata defines metadata for the `identity invite` command.
var IdentityPathsMetadata = config.CommandMetadata{ Use: "paths", Short: "Emit this agent's resolved mesh identity and state paths", Long: `Resolve this agent's mesh identity — slug, wire principal, daemon URL, and every wake-watcher state path — and print it. The slug comes from the agents.yaml registry (AGENT_CHAT_REGISTRY) matched against the project path (AGENT_CHAT_PROJECT_PATH, else the working directory), exactly as chat-mcp itself resolves identity. The state paths come from the same derivation "vaultmind doctor" checks, so the watcher and its checker cannot disagree about where the heartbeat lives. FAILS LOUDLY when no slug is resolvable: a watcher armed with an empty identity would write degenerate state files a checker then reports on. Exit nonzero means "do not arm". OUTPUT default shell-quoted VM_MESH_* assignments, safe to eval: eval "$(vaultmind identity paths)" --json the same facts in the standard envelope EXAMPLES vaultmind identity paths vaultmind identity paths --json`, ConfigPrefix: "app.identitypaths", FlagOverrides: map[string]string{ "app.identitypaths.json": "json", }, }
IdentityPathsMetadata defines `vaultmind identity paths` — the one derivation of an agent's mesh identity and state paths, emitted for shells and machines.
The canonical wake-watcher's first statement is
eval "$(vaultmind identity paths)"
and the script contains zero identity literals of its own. That is the point: before this command, three hand-maintained watcher copies each carried the slug, the daemon URL, and the state paths inline (one heredoc cannot interpolate, so the slug alone was spelled four times in one script), and the copies drifted — one watcher died for seven days partly because the checker and the writer disagreed about a path nobody could see disagreeing.
A binary call cannot be forgotten the way an env var can, fails loudly when it cannot answer, and names WHERE each value came from. It is also a live preflight of the binary itself: if this works, `vaultmind` works.
var IdentitySignEnrollmentMetadata = config.CommandMetadata{ Use: "sign-enrollment", Short: "Self-sign an agent ENROLLMENT request via the keyless signer (Contract-B)", Long: `Read an agent-enrollment request (stdin or --file), enforce the Contract-B enrollment gates, CANONICALIZE the signed subset (RFC 8785 JCS), then sign the canonical bytes via the signer over its 0600 socket and print {sig, pubkey} as JSON. The signed subset is { alg_version, created, display_name, key_epoch, network_id, nonce, pubkey, slug, transport_endpoint? (optional), transport_pubkey }. The pubkey field IS the enrolling agent's ed25519 identity key AND the verification key: the self-signature is PROOF-OF-POSSESSION of that key. It does NOT prove the slug/identity is authorized — an admin separately decides to add the binding to the trust-root registry (the out-of-band step). This CLI is KEYLESS: it NEVER opens the private-key file. If the signer is unreachable it FAILS CLOSED with an error (never a silent unsigned result).`, ConfigPrefix: "app.identitysignenrollment", FlagOverrides: map[string]string{ "app.identitysignenrollment.file": "file", "app.identitysignenrollment.signer_socket": "signer-socket", }, }
IdentitySignEnrollmentMetadata defines metadata for the `identity sign-enrollment` command.
var IdentitySignEnvelopeMetadata = config.CommandMetadata{ Use: "sign-envelope", Short: "Sign a chat MESSAGE envelope via the keyless signer (Contract-B slice 5)", Long: `Read a chat-message envelope (stdin or --file), enforce the Contract-B signing gates, CANONICALIZE the signed subset (RFC 8785 JCS), then sign the canonical bytes via the signer over its 0600 socket and print {sig, from_pubkey, key_epoch} as JSON. The signed subset is { alg_version, body, from_agent, key_epoch, nonce, room?, seq, to_agent? (exactly one), ts }. from_pubkey is DERIVED, not signed. The transport fields (id, sig, from_pubkey, receive_ts, ioguard_verdict, origin_daemon) are excluded from the signed bytes. This CLI is KEYLESS: it NEVER opens the private-key file. If the signer is unreachable it FAILS CLOSED with an error (never a silent unsigned result). Anti-replay (seq high-water + nonce-unseen) is the receiving daemon's job.`, ConfigPrefix: "app.identitysignenvelope", FlagOverrides: map[string]string{ "app.identitysignenvelope.file": "file", "app.identitysignenvelope.signer_socket": "signer-socket", "app.identitysignenvelope.from_pubkey": "from-pubkey", }, }
IdentitySignEnvelopeMetadata defines metadata for the `identity sign-envelope` command.
var IdentitySignMetadata = config.CommandMetadata{ Use: "sign", Short: "Validate, canonicalize, and sign an entry via the keyless signer", Long: `Read a Contract-B entry (stdin or --file), VALIDATE and CANONICALIZE it (slice-1 schema gate + RFC 8785 JCS), then send the canonical bytes to the signer over its 0600 socket and print the signature. This CLI is KEYLESS: it NEVER opens the private-key file. If the signer is unreachable it FAILS CLOSED with an error (never a silent unsigned result).`, ConfigPrefix: "app.identitysign", FlagOverrides: map[string]string{ "app.identitysign.file": "file", "app.identitysign.signer_socket": "signer-socket", }, }
IdentitySignMetadata defines metadata for the `identity sign` command.
var IdentitySignRegistryMetadata = config.CommandMetadata{ Use: "sign-registry", Short: "Sign a trust-root REGISTRY via the keyless signer (Contract-B)", Long: `Read an UNSIGNED registry (stdin or --file), enforce the Contract-B epoch gate, CANONICALIZE it (RFC 8785 JCS), then sign the canonical bytes via the signer over its 0600 socket and print the distribution envelope {registry, root_sig, root_key_epoch} as JSON. The input is a registry.Registry: { agents[], epoch, valid_from, valid_until }, where each binding is { authorized_origin_daemons[], display_name, key_epoch, pubkey (base64-std), revoked_at? (absent == live), slug, valid_from, valid_until }. Every pubkey is re-validated (wrong-length / small-order keys are rejected) so a hostile key cannot enter a binding. This CLI is KEYLESS: it NEVER opens the root private-key file. If the signer is unreachable it FAILS CLOSED with an error (never a silent unsigned result). The consumer trust gate (root-sig verify, anti-rollback, freshness) runs at load via VerifyAndLoad, not here.`, ConfigPrefix: "app.identitysignregistry", FlagOverrides: map[string]string{ "app.identitysignregistry.file": "file", "app.identitysignregistry.signer_socket": "signer-socket", }, }
IdentitySignRegistryMetadata defines metadata for the `identity sign-registry` command.
var IdentitySignerInstallMetadata = config.CommandMetadata{ Use: "install", Short: "Run the signer under launchd: start at login, restart if it dies (macOS)", Long: "Write a LaunchAgent for the custody signer and load it, so the signer starts at " + "login and is restarted whenever it exits. Without this the signer is a foreground " + "process nothing restarts: when it dies, every signed send fails \"signer unreachable\" " + "until someone notices.\n\n" + "The job launches exactly the signer you would start by hand. The binary and the " + "key/socket paths are resolved NOW, with your flags and --config-path-mode, and written " + "into the job as absolute paths — launchd runs with its own environment, and a default " + "resolved there is not the one your shell resolves.\n\n" + "Refuses while another signer is already serving the socket: the launchd copy would fail " + "to bind and KeepAlive would turn that into a restart loop that looks like a running " + "service. Stop the hand-started signer first.\n\n" + "Rerunning replaces the job. One job per signer name (default: the key file's name), so " + "two signers on one machine get two jobs.\n\n" + "EXAMPLES\n\n" + " vaultmind identity signer install --print\n" + " Show the job without installing anything — for review before it touches a machine.\n\n" + " vaultmind identity signer install --config-path-mode native\n" + " Install for a signer whose key lives under the native (Application Support) paths.\n\n" + " vaultmind identity signer install --signer-key ~/.config/vaultmind/contractb/mira.key\n" + " Install for an explicit key; the job is named after it (com.vaultmind.signer.mira).", ConfigPrefix: "app.identitysignerinstall", FlagOverrides: map[string]string{ "app.identitysignerinstall.signer_key": "signer-key", "app.identitysignerinstall.signer_socket": "signer-socket", "app.identitysignerinstall.name": "name", "app.identitysignerinstall.print": "print", }, }
IdentitySignerInstallMetadata defines `identity signer install` — run the custody signer under launchd so it survives logouts, reboots and crashes.
Without it the signer is a foreground process that nothing restarts. On 2026-09-22 a member's signer died with a session reset and every signed send failed "signer unreachable" until someone restarted it by hand.
var IdentitySignerMetadata = config.CommandMetadata{ Use: "signer", Short: "Run the keyless custody signer daemon (Contract-B)", Long: `Run the keyless custody signer in the FOREGROUND. The signer holds the agent's ed25519 private key in memory and serves signatures over a 0600 Unix-domain socket, gated by LOCAL_PEERCRED to the current user's uid (the documented DEV-INTERIM posture). The private key NEVER leaves this process; the sign-* CLIs are KEYLESS and reach it only over the socket. This is a long-running process: it serves until it receives SIGINT/SIGTERM, then removes the socket and exits. Nothing restarts it if it dies: run ` + "`vaultmind identity signer install`" + ` to put it under launchd (starts at login, restarts on exit). It FAILS CLOSED: a missing/unreadable key, a bind failure, or an empty uid allowlist refuses to start with a non-zero exit and prints nothing sensitive. DEV-INTERIM: the signer runs as the SAME uid as the CLI today. This is the custody ARCHITECTURE, not real isolation — that needs a dedicated service uid + launchd + sandbox + Secure-Enclave key wrap (deferred).`, ConfigPrefix: "app.identitysigner", FlagOverrides: map[string]string{ "app.identitysigner.signer_key": "signer-key", "app.identitysigner.signer_socket": "signer-socket", }, }
IdentitySignerMetadata defines metadata for the `identity signer` command — the keyless custody-signer DAEMON that the sign-* commands connect to.
var ImportMetadata = config.CommandMetadata{ Use: "import <src>...", Short: "Import a folder or archive of docs (markdown, PDF, Office, HTML, CSV, EPUB, images), one file, one web page or PDF, or a whole site, as notes and keep them in step", Long: "Copy every *.md under <dir> into the vault as a note under imported/<repo>/, so " + "a project's existing docs are found by search, ask and the code hooks like any " + "other note. Each *.pdf in the folder becomes a note of its text, <name>-pdf.md, and " + "each Word, PowerPoint or Excel file (.docx, .pptx, .xlsx) a markdown note of its " + "headings, slides or sheets, <name>-docx.md and so on. A saved web page or exported " + "HTML (.html, .htm) becomes the article it holds, read as a fetched page is, " + "<name>-html.md; a CSV or TSV becomes a table like an Excel sheet (the first 200 rows " + "and 50 columns, its delimiter and encoding detected), <name>-csv.md. An EPUB book " + "becomes a folder <name>-epub/ with a note per chapter, in reading order and titled " + "from its table of contents (a file holding several long chapters is split at them), " + "and book.md listing them; a book whose chapters are DRM-encrypted is skipped. A zip or " + "tar archive (.zip, .tar, .tar.gz, .tgz) imports like the folder it holds, into " + "<name>-zip/ or <name>-tar/: its members are unpacked to a temporary folder outside the " + "vault (unsafe paths, links and encrypted members are skipped, archives inside it are " + "not opened, and an archive over 512 MiB unpacked or 64 MiB in one member is refused) " + "and imported as files on disk are. An image (.png, .jpg, .jpeg, .webp, .gif, .heic, " + ".heif) becomes <name>-png.md and so on, holding the text tesseract reads in it (when " + "tesseract is installed; results are cached, icons under 100 pixels are not read), the alt " + "text the import's markdown gives it, and its metadata, GPS included; an SVG becomes " + "the words it shows. An image with nothing but its pixels is counted, not written. With " + "--vision-endpoint and --vision-model (an OpenAI-compatible endpoint: Ollama's is " + "http://localhost:11434/v1), each image is also described by that model, the description " + "cached by the image's content so a re-import asks again for nothing; the key, when the " + "endpoint needs one, is read from the variable --vision-api-key-env names. Without an " + "endpoint no image leaves the machine, and a non-local one is named when it is used. With " + "--watch the import runs once, then keeps the vault in step with its source until Ctrl-C: a " + "folder or file is polled every --watch-interval (default 2s) and re-imported once a change " + "holds still for one interval, so a burst of edits is one re-sync; a URL is re-imported every " + "interval (default 6h, at least 1m). Each re-sync that changes something prints a time-stamped " + "line, and a failed one says why and the watch goes on. A file that " + "cannot be read (a PDF scan, a broken file, a PDF over 20 MiB, an HTML file over 5 MiB " + "or an Office or CSV file over 512 MiB) is skipped " + "with the reason and the rest still imports. A doc, page, PDF, chapter or member past " + "32,768 characters becomes an index note at its usual path plus <name>/NN-<heading>.md " + "section notes, split at headings, so each part is embedded and found on its own; tables " + "stay whole. Inside a git work tree, what git ignores " + "is left out (naming an ignored folder imports it). Pass one such file instead and " + "only that " + "file is imported, named as importing its folder would name it; the folder's other " + "notes are left alone. Pass several sources and each is imported in that order. A source " + "that fails is named; the others still import; the command exits non-zero if any failed. " + "--watch keeps one source in step and takes a single source. Pass an http or https URL " + "instead of a folder and that one page " + "becomes a note under imported/web/<host>/. A URL that serves a PDF becomes a note " + "holding the PDF's text, titled from its metadata or its first line.\n\n" + "Each folder note records its doc in `paths:` — reading or editing the doc brings the " + "note — and in `source:` and `source_hash:`, which is how a re-run knows what " + "changed. A page note records the URL you gave in `url:` and `source:` and has no " + "`paths:`. The doc stays the source of truth: edit the doc, then re-run. The page " + "is the source of a URL note: re-run the import to refresh it.\n\n" + "A re-run adds new docs, rewrites changed ones and leaves the rest alone. Nothing " + "is deleted without asking: a note whose doc is gone is reported as orphaned and " + "removed only with --prune. A note edited by hand whose doc or page also changed is " + "reported as a conflict and kept; --force overwrites it. Frontmatter you add to " + "an imported note (tags, links) survives a re-run.\n\n" + "A URL import fetches exactly the page you name and nothing else, unless --crawl is " + "given. Only http and https are accepted. The body must be HTML or text of at most " + "5 MiB, or a PDF of at most 20 MiB; the request times out after 30 seconds, and at " + "most five redirects are followed, only when they stay on http or https. Scripts on " + "the page are not run. A PDF's text is read by pdfium running in a WebAssembly " + "sandbox, for at most 60 seconds; a PDF with no text layer (a scan) is refused. " + "The first PDF import compiles pdfium once (a few seconds) and caches it. A URL that " + "names a public host never reaches a private address (localhost, 10.x, 192.168.x, " + "the cloud metadata address), not through a redirect, its DNS answer or a proxy; a URL " + "that names a private host is your choice, and is fetched.\n\n" + "--crawl imports the site the URL leads to, one note per page, each the note a " + "single-page import of that page would write. Pages come from the site's llms.txt, " + "its sitemaps (from robots.txt, else /sitemap.xml), then the links on each page, " + "breadth first. The crawl stays on the URL's host, in the URL's folder (/docs/intro " + "and /docs/ both mean /docs/), narrowed by --include and --exclude globs on the URL " + "path. Links to images, scripts, archives and other files that are not pages are not " + "followed. One request is made at a time, --delay apart (default 1s); --max-pages " + "(default 100) and --depth (link hops, default 5) bound it. robots.txt is read for " + "its sitemaps but not obeyed unless --respect-robots is given, which also honours " + "its Crawl-delay (up to 30s). A page that fails is skipped with the reason and the " + "crawl goes on. Pages the crawl no longer finds are reported as orphaned only when " + "it saw the whole site: no page failed and no cap stopped it.\n\n" + "Left out of a folder import: hidden folders, dependency folders (node_modules, vendor), symlinks, " + "and anything the vault's exclude list would hide from the index. A README is " + "imported as readme.md, since vaults exclude README.md as their own meta file. " + "A repository is known by its name (its origin remote's, else its folder's; " + "outside git, the folder's), so two repositories or folders of the same name " + "share imported/<name>/. Names that differ only in case are one note.\n\n" + " vaultmind import docs --vault ./knowledge --dry-run\n" + " vaultmind import docs --vault ./knowledge\n" + " vaultmind import docs *.md --vault ./knowledge\n" + " vaultmind import docs --vault ./knowledge --prune --json\n" + " vaultmind import papers/attention.pdf --vault ./knowledge\n" + " vaultmind import https://sqlite.org/fts5.html --vault ./kb\n" + " vaultmind import https://arxiv.org/pdf/2402.03216 --vault ./kb\n" + " vaultmind import https://docusaurus.io/docs --crawl --vault ./kb\n" + " vaultmind import https://example.com/docs/ --crawl --exclude 'docs/v1/**' --max-pages 300 --vault ./kb", ConfigPrefix: "app.import", FlagOverrides: map[string]string{ "app.import.vault": "vault", "app.import.dry_run": "dry-run", "app.import.prune": "prune", "app.import.force": "force", "app.import.json": "json", "app.import.crawl": "crawl", "app.import.max_pages": "max-pages", "app.import.depth": "depth", "app.import.include": "include", "app.import.exclude": "exclude", "app.import.respect_robots": "respect-robots", "app.import.public_only": "public-only", "app.import.delay": "delay", "app.import.vision_endpoint": "vision-endpoint", "app.import.vision_model": "vision-model", "app.import.vision_api_key_env": "vision-api-key-env", "app.import.watch": "watch", "app.import.watch_interval": "watch-interval", }, }
ImportMetadata defines the `import` command — a folder of docs as notes.
var IndexMetadata = config.CommandMetadata{ Use: "index", Short: "Scan and index vault notes into SQLite, with optional embedding", Long: `Scan the vault directory and update the SQLite index so search and recall commands see current note contents. By default only re-parses notes whose content has changed since the last run (incremental). Use --full to purge and rebuild the entire index from scratch. A plain index does not re-embed and does not load an embedding model. If stored sparse vectors are an older format, it warns and leaves every vector unchanged. vaultmind index --embed rewrites those sparse vectors once and keeps the dense and ColBERT bytes. FLAGS --vault path Vault root directory to index (default: current dir). --full Force a complete rebuild; purges the index first and re-parses every note. Shows deleted count explicitly. --embed Compute and store embeddings for all note bodies after indexing. Required for hybrid (semantic+keyword) search. --model name Embedding model to use with --embed. "minilm" — 384-dimensional, fast, pure-Go build. "bge-m3" — 1024-dimensional, dense+sparse+ColBERT (3-in-1), ORT-tagged build only. Empty (default): auto-selects bge-m3 on ORT builds, minilm on pure-Go builds. --allow-slow-backend Allow BGE-M3 on the pure-Go hugot backend. Omit this flag and the command refuses with a warning — BGE-M3 on pure-Go takes hours for a medium vault. --json Machine-readable JSON output (envelope format). EMBEDDING MODELS minilm: Default on pure-Go builds. Fast. Produces 384-dimensional dense vectors only. Sufficient for basic semantic search. bge-m3: Default on ORT-tagged builds ("task build:ort"). Produces dense (1024d) + sparse + ColBERT vectors in one pass, enabling the full 4-way RRF hybrid used by "ask" and "search". Recommended for recall quality. Requires an ORT binary; pure-Go falls back to minilm silently unless --allow-slow-backend is passed. OUTPUT INCLUDES Human-readable (default): "Indexed N notes (M skipped, A added, U updated, D deleted)" "Embedded N notes (M skipped, E errors) [model: <name>]" Warning line if any notes produced empty sparse/ColBERT output. JSON (--json): Envelope with index stats (indexed, added, updated, deleted, errors, full_rebuild) and, when --embed, embed stats (embedded, skipped, errors, model). EXAMPLES vaultmind index # incremental update vaultmind index --full # full rebuild from scratch vaultmind index --embed # incremental + compute embeddings vaultmind index --embed --model minilm # force minilm model explicitly vaultmind index --embed --model bge-m3 # bge-m3 (ORT build required) vaultmind index --vault ./my-vault --embed --json # JSON output for scripting`, ConfigPrefix: "app.index", FlagOverrides: map[string]string{ "app.index.vault": "vault", "app.index.json": "json", "app.index.full": "full", "app.index.embed": "embed", "app.index.model": "model", "app.index.allow_slow_backend": "allow-slow-backend", }, }
IndexMetadata defines the metadata for the index command.
var InitMetadata = config.CommandMetadata{ Use: "init [path]", Short: "Scaffold a fresh vault — a project knowledge base by default, or an agent's identity vault", Long: `Create a new VaultMind vault at <path>: a type registry, a vault-level README, and starter notes that demonstrate the schema. After init, you index, embed, and you're ready. By default it is a project knowledge base. --profile persona (or full) scaffolds an agent's identity vault instead. WHAT YOU GET (default: knowledge) <path>/ .vaultmind/config.yaml Type registry — decision, concept, source, reference, and the identity types README.md How to use the vault decisions/decision-example.md Template — a decision and why concepts/concept-example.md Template — a concept, with an optional paths: list tying it to code WITH --profile persona (or full) <path>/ .vaultmind/config.yaml The same type registry README.md The persona model + workflow identity/who-am-i.md Foundational identity note (placeholder) references/current-context.md Live-edge priority note (placeholder) principles/principle-example.md Template — replace or delete arcs/arc-example.md Template — replace or delete The starter notes have today's date in their frontmatter so the index reads them cleanly without hand-editing. Replace or delete them as real content arrives. EXAMPLES vaultmind init ./knowledge A project knowledge base at a relative path. vaultmind init ./knowledge --wire-hooks Scaffold AND wire Claude Code in one step (see ONE-COMMAND SETUP). vaultmind init "$HOME/.vaultmind/persona" --profile persona An agent's identity vault, outside the project tree — common when the agent's memory should persist across repos. vaultmind init --print-instructions Print the concise agent-onboarding quick-start and exit. No vault created. The quick-start is the skimmable 20% — install, init, hooks, env-var routing, index/embed, first ask. Add --full to print the whole guide (preflight, project read, greenfield + migration paths, Claude Code wiring with diff-preview) instead. Use this when an AI agent is helping a new user wire vaultmind into their environment — paste the output to the agent. vaultmind init --print-instructions --full Print the full agent-onboarding guide instead of the quick-start. ONE-COMMAND SETUP (--wire-hooks) --wire-hooks does the Claude Code wiring for you right after the scaffold: it installs the hook scripts into the current project's .claude/scripts/ and merges the hook entries into .claude/settings.json, baked to the new vault via VAULTMIND_VAULT. The merge is additive and NEVER clobbers — a project's own hooks are preserved, re-runs are no-ops, malformed settings error out before any write. (This is the same engine as "vaultmind hooks install --merge".) Which hooks: the --profile you name; else the profile the project already declared; else the profile of the vault init just made. So a fresh project gets knowledge hooks for a knowledge vault, and a project that declared its hooks keeps them. --local wire .claude/settings.local.json (gitignored, personal) instead of the committed settings.json; keep scripts and the profile out of git via git info/exclude, reporting changes or skips --dry-run preview the settings merge without writing it The vault is created at <path>; the hooks are wired into the CURRENT directory's .claude/ (your project root). After wiring, index + embed so recall has something to retrieve, then restart Claude Code. To undo: vaultmind hooks uninstall. NEXT STEPS cd <path> vaultmind index --vault . # build the SQLite index vaultmind index --embed --vault . # compute embeddings (one-time) vaultmind import ../docs --vault . # bring in a project's existing docs vaultmind ask "what did we decide about X" --vault . For agent-led setup (interview, project read, migration, hooks), run: vaultmind init --print-instructions THE PERSONA MODEL With --profile persona, VaultMind treats arcs — transformation notes — as the atomic unit of an agent's identity. Identity is carried by the journey, not by the rules. The persona scaffold gives you placeholder identity + current-context notes and templates for principles and arcs; let your real collaboration produce the rest.`, ConfigPrefix: "app.init", FlagOverrides: map[string]string{ "app.init.print_instructions": "print-instructions", "app.init.full": "full", "app.init.wire_hooks": "wire-hooks", "app.init.profile": "profile", "app.init.local": "local", "app.init.dry_run": "dry-run", "app.init.project_dir": "project-dir", }, }
InitMetadata defines the metadata for the init command — scaffolds a fresh vault at a user-provided path.
The knowledge vault is the product: a curated knowledge base for a project or across repos, found by search and by the code hooks. That is the default scaffold. The agent-identity (persona) vault is an add-on, one flag away.
var LinksInMetadata = config.CommandMetadata{ Use: "in <id-or-path>", Short: "List notes linking to a note (inbound edges)", Long: `Find all notes that reference the given note (inbound edges). Useful for understanding a note's context and impact: which notes depend on it, cite it as rationale, or point to it as a concept to explore further. Each result shows the source note's ID, the edge type, and a confidence label (high, medium, low) reflecting how strongly the reference was detected. FLAGS --edge-type Filter to a specific edge type (e.g., "cites"). Omit to show all types. --json Output in machine-readable JSON. Includes target_id and a "links" array with source_id, edge_type, confidence, and resolution status per edge. --vault Path to vault root (default: current directory). EXAMPLES vaultmind links in concept-spreading-activation Show all notes that reference spreading-activation, one per line: source_id edge_type confidence. vaultmind links in decision-expand-cache --edge-type cites Show only notes that cite this decision as rationale. vaultmind links in concept-spreading-activation --json Machine-readable output; pipe to jq or an agent tool.`, ConfigPrefix: "app.links", FlagOverrides: map[string]string{ "app.links.vault": "vault", "app.links.json": "json", "app.links.edge_type": "edge-type", }, }
LinksInMetadata defines the metadata for the links in command.
var LinksNeighborsMetadata = config.CommandMetadata{ Use: "neighbors <id-or-path>", Short: "Explore a note's graph neighborhood via breadth-first traversal", Long: `Explore the notes reachable from the given note within N hops (breadth-first traversal). Useful for finding related material at increasing degrees of separation: direct references (depth 1), second-order clusters (depth 2), or broader context. Results are ordered by distance from the starting note and show the connecting edge type and confidence for each hop. CONFIDENCE LABELS Edges carry a confidence label (high, medium, low) reflecting how strongly a reference was detected. Use --min-confidence to prune weak references and focus on the most reliable connections. FLAGS --depth Maximum hops from the starting note (default: 1). Increase to explore further context. --min-confidence Minimum edge confidence to include: high, medium, or low (default: low, includes all edges). Useful for reducing noise. --max-nodes Cap the number of nodes returned (default: 200). If the cap is reached, output notes "(max reached)" so you know the graph was truncated. --json Output in machine-readable JSON. Includes a "nodes" array with id, distance, and edge_from (edge_type, confidence) per node, plus a "max_nodes_reached" boolean. --vault Path to vault root (default: current directory). EXAMPLES vaultmind links neighbors concept-spreading-activation Show all notes within 1 hop (direct references), one per line. vaultmind links neighbors concept-spreading-activation --depth 2 Show second-degree connections — notes that cite notes that cite this one. vaultmind links neighbors concept-spreading-activation --depth 2 --min-confidence medium Second-degree neighborhood, pruned to medium+ confidence edges (reduces noise). vaultmind links neighbors concept-spreading-activation --json Machine-readable output; pipe to jq or an agent tool.`, ConfigPrefix: "app.linksneighbors", FlagOverrides: map[string]string{ "app.linksneighbors.vault": "vault", "app.linksneighbors.json": "json", "app.linksneighbors.depth": "depth", "app.linksneighbors.min_confidence": "min-confidence", "app.linksneighbors.max_nodes": "max-nodes", }, }
LinksNeighborsMetadata defines the metadata for the links neighbors command.
var LinksOutMetadata = config.CommandMetadata{ Use: "out <id-or-path>", Short: "List notes referenced by a note (outbound edges)", Long: `Find all notes referenced by the given note (outbound edges). Shows what a note depends on, cites, or extends — useful for tracing a note's sources, following its reasoning chain, or finding related material. Each edge includes its type (e.g., "cites", "extends", "references") and a confidence label (high, medium, low) reflecting how strongly the reference was detected. FLAGS --edge-type Filter to a specific edge type (e.g., "cites"). Omit to show all types. --json Output in machine-readable JSON. Includes source_id and a "links" array with edge_type, confidence, target_id, and resolution status per edge. --vault Path to vault root (default: current directory). EXAMPLES vaultmind links out concept-spreading-activation Show all outbound edges from the note, one per line: target edge_type confidence. vaultmind links out decision-expand-cache --edge-type cites Show only edges of type "cites" — the sources this decision depends on. vaultmind links out concept-spreading-activation --json Machine-readable output; pipe to jq or an agent tool.`, ConfigPrefix: "app.links", FlagOverrides: map[string]string{ "app.links.vault": "vault", "app.links.json": "json", "app.links.edge_type": "edge-type", }, }
LinksOutMetadata defines the metadata for the links out command.
var LintFixLinksMetadata = config.CommandMetadata{ Use: "fix-links", Short: "Rewrite wikilinks to Obsidian-compatible format", Long: "Detect [[Title]] wikilinks that won't resolve in Obsidian and rewrite to [[filename|Title]].\nDefault is dry-run (preview). Use --fix to apply changes.", ConfigPrefix: "app.lintfixlinks", FlagOverrides: map[string]string{ "app.lintfixlinks.vault": "vault", "app.lintfixlinks.json": "json", "app.lintfixlinks.fix": "fix", }, }
LintFixLinksMetadata defines metadata for the lint fix-links subcommand.
var McpMetadata = config.CommandMetadata{ Use: "mcp", Short: "Serve the vault to MCP clients (Claude Desktop, Cursor, any agent) over stdio", Long: "Run an MCP server on stdin/stdout, so an agent without a shell (Claude Desktop, or any " + "MCP client) can use the vault. Its tools are ask, search, note_get, tree, links, " + "note_create and import; each runs the vaultmind command of the same name with --json and " + "returns its output, so a tool answers exactly what the CLI answers. A value a client sends " + "is never read as a flag. import takes only http(s) URLs and reaches only public " + "addresses (import --public-only): a page the agent read could prompt it.\n\n" + "The vaults are fixed when the server starts: --vault, or --vaults a,b,c to read across " + "several (writes, imports and links go to the first). A client cannot point a tool at " + "another directory.\n\n" + "Claude Code:\n" + " claude mcp add vaultmind -- vaultmind mcp --vault /path/to/vault\n\n" + "Claude Desktop, Cursor (.cursor/mcp.json) and most clients take the same entry:\n" + " {\"mcpServers\": {\"vaultmind\": {\"command\": \"vaultmind\",\n" + " \"args\": [\"mcp\", \"--vault\", \"/path/to/vault\"]}}}\n\n" + "Each call starts the command afresh, as the CLI does, so ask takes about as long as " + "running it in a shell.", ConfigPrefix: "app.mcp", FlagOverrides: map[string]string{ "app.mcp.vault": "vault", "app.mcp.vaults": "vaults", }, SeeAlso: []string{"ask", "tree", "hooks install"}, }
McpMetadata defines the mcp command.
var MemoryContextPackMetadata = config.CommandMetadata{ Use: "context-pack <id-or-path>", Short: "Pack the target note and ranked context items within a token budget", Long: `Load a target note, then fill a token budget with its ranked neighbors, stopping when the budget is exhausted. Designed to produce an agent-ready payload: the target is always included, followed by context items in priority order (explicit relations first, then inferred edges). A neighbor whose status is draft is left out unless you pass --include-drafts. TOKEN BUDGET The budget is a soft limit on tokens. Context items are added one by one in ranked order; as soon as adding the next item would exceed the budget, it is skipped and the "truncated" field in the output is set to true. The target note itself is always included regardless of its size. Use --slim to reduce the token footprint of context item frontmatter when the budget is tight. FLAGS --budget <tokens> Token budget for context assembly. Default: 4096. --depth <n> BFS traversal depth for finding candidate context items. Default: 1 (direct neighbors only). --max-items <n> Maximum number of context items to include. 0 means no limit beyond the token budget. Default: 0. --slim Reduce each context item's frontmatter to {type, title, status} only, saving tokens for more items. --vault <path> Path to vault root. Default: ".". --json Output as a JSON envelope instead of text. OUTPUT INCLUDES target: the target note's frontmatter and body context: ranked context items, each with frontmatter (full or slim) used_tokens: tokens consumed budget_tokens: the requested budget truncated: true if one or more items were dropped due to budget EXAMPLES vaultmind memory context-pack concept-spreading-activation # default 4096-token pack around the target vaultmind memory context-pack concept-spreading-activation --budget 2000 --slim # tight budget, slim frontmatter to fit more items vaultmind memory context-pack concept-spreading-activation --max-items 8 --slim --json # cap at 8 items, slim, machine-readable (preferred for agent hooks) WHEN TO USE Use context-pack when an agent needs a self-contained, token-bounded working context around a topic. For a full neighborhood traversal without a budget, use "memory recall". For a flat list of related notes, use "memory related".`, ConfigPrefix: "app.memorycontextpack", FlagOverrides: map[string]string{ "app.memorycontextpack.vault": "vault", "app.memorycontextpack.json": "json", "app.memorycontextpack.budget": "budget", "app.memorycontextpack.depth": "depth", "app.memorycontextpack.max_items": "max-items", "app.memorycontextpack.slim": "slim", }, }
MemoryContextPackMetadata defines metadata for the memory context-pack command.
var MemoryLinksMetadata = config.CommandMetadata{ Use: "links <id-or-path>", Short: "List directed wikilink edges for a note (outbound, inbound, or both)", Long: `List the directed link edges of a note. By default shows both directions; use --out for outbound edges only or --in for inbound edges (backlinks) only. This listing includes a draft's id and title. It does not deliver the body. memory neighbors and memory pack omit a draft unless --include-drafts is set. Outbound edges are the notes this note references, cites, or extends. Inbound edges (backlinks) are the notes that reference this one — useful for finding callers, dependents, or consumers of a concept. Each edge includes its type (e.g., "cites", "extends", "references") and a confidence label (high, medium, low) reflecting how strongly it was detected. FLAGS --out Show only outbound edges (notes this note references). --in Show only inbound edges (backlinks: notes that reference this one). --both Show both directions (default). Mutually exclusive with --out/--in. --edge-type Filter to a specific edge type (e.g., "cites"). Omit to show all types. --json Output in machine-readable JSON. --vault Path to vault root (default: current directory). EXAMPLES vaultmind memory links concept-spreading-activation Show both inbound and outbound edges for the note. vaultmind memory links concept-spreading-activation --out Show only outbound edges (what the note references). vaultmind memory links concept-spreading-activation --in Show only backlinks (what references the note). vaultmind memory links concept-spreading-activation --out --json Machine-readable output; pipe to jq or an agent tool.`, ConfigPrefix: "app.memorylinks", FlagOverrides: map[string]string{ "app.memorylinks.vault": "vault", "app.memorylinks.json": "json", "app.memorylinks.edge_type": "edge-type", "app.memorylinks.out": "out", "app.memorylinks.in": "in", "app.memorylinks.both": "both", }, }
MemoryLinksMetadata defines metadata for the memory links command. It unifies directed wikilink-edge queries: outbound (--out), inbound (--in, backlinks), or both (--both, default). Absorbs the old top-level `links out` and `links in` commands into one direction-flagged command.
var MemoryNeighborsMetadata = config.CommandMetadata{ Use: "neighbors <id-or-path>", Short: "Traverse the graph from a note (BFS) and return neighbors with full frontmatter", Long: `Perform a breadth-first traversal starting from a target note and return all neighbors within the specified depth. Every node carries its full frontmatter, so callers get a rich, typed neighborhood without follow-up note-get calls. A note whose status is draft is not a neighbor unless you pass --include-drafts. The note you named is still returned. BFS (breadth-first search) means depth=1 returns only direct neighbors, depth=2 includes their neighbors as well, and so on. CONFIDENCE LEVELS Edges carry a confidence level (low, medium, high). --min-confidence sets the floor: only edges at or above that level are traversed. The default is "high" so traversal stays precise; lower it to include weaker, LLM-inferred relationships. FLAGS --depth <n> Maximum traversal depth. Default: 1. --max-nodes <n> Safety limit on result size. Default: 50. --min-confidence <l> Minimum edge confidence (low, medium, high). Default: high. --include-drafts Include notes whose status is draft. Default: false. --vault <path> Path to vault root. Default: ".". --json Output as a JSON envelope instead of text. EXAMPLES vaultmind memory neighbors concept-spreading-activation # direct neighbors (depth 1) at high confidence, with full frontmatter vaultmind memory neighbors concept-spreading-activation --depth 2 --max-nodes 20 # up to depth 2, capped at 20 nodes vaultmind memory neighbors concept-spreading-activation --min-confidence medium --json # include medium-confidence edges, machine-readable output WHEN TO USE Use neighbors when you want the full enriched neighborhood around a known note. For a flat list of edge-typed relations, use "memory related". For a token-budgeted payload ready for agent consumption, use "memory pack".`, ConfigPrefix: "app.memoryneighbors", FlagOverrides: map[string]string{ "app.memoryneighbors.vault": "vault", "app.memoryneighbors.json": "json", "app.memoryneighbors.depth": "depth", "app.memoryneighbors.min_confidence": "min-confidence", "app.memoryneighbors.max_nodes": "max-nodes", }, }
MemoryNeighborsMetadata defines metadata for the memory neighbors command. It merges the old `links neighbors` (BFS traversal) and `memory recall` (full-frontmatter enrichment) into one: BFS to a depth limit, returning every reachable node with its full frontmatter.
var MemoryPackMetadata = config.CommandMetadata{ Use: "pack <id-or-path>", Short: "Pack the target note and ranked context items within a token budget", Long: `Load a target note, then fill a token budget with its ranked neighbors, stopping when the budget is exhausted. Designed to produce an agent-ready payload: the target is always included, followed by context items in priority order (explicit relations first, then inferred edges). A neighbor whose status is draft is left out unless you pass --include-drafts. TOKEN BUDGET The budget is a soft limit on tokens. Context items are added one by one in ranked order; as soon as adding the next item would exceed the budget, it is skipped and the "truncated" field in the output is set to true. The target note itself is always included regardless of its size. Use --slim to reduce the token footprint of context item frontmatter when the budget is tight. FLAGS --budget <tokens> Token budget for context assembly. Default: 4096. --depth <n> BFS traversal depth for candidate context items. Default: 1. --max-items <n> Max context items. 0 means no limit beyond the budget. Default: 0. --slim Reduce each context item's frontmatter to {type, title, status}. --vault <path> Path to vault root. Default: ".". --json Output as a JSON envelope instead of text. EXAMPLES vaultmind memory pack concept-spreading-activation # default 4096-token pack around the target vaultmind memory pack concept-spreading-activation --budget 2000 --slim # tight budget, slim frontmatter to fit more items vaultmind memory pack concept-spreading-activation --max-items 8 --slim --json # cap at 8 items, slim, machine-readable (preferred for agent hooks) WHEN TO USE Use pack when an agent needs a self-contained, token-bounded working context around a topic. For a full neighborhood traversal without a budget, use "memory neighbors". For a flat list of related notes, use "memory related".`, ConfigPrefix: "app.memorypack", FlagOverrides: map[string]string{ "app.memorypack.vault": "vault", "app.memorypack.json": "json", "app.memorypack.budget": "budget", "app.memorypack.depth": "depth", "app.memorypack.max_items": "max-items", "app.memorypack.slim": "slim", "app.memorypack.excerpt": "excerpt", }, }
MemoryPackMetadata defines metadata for the memory pack command. It is a rename of the old `memory context-pack` (identical behavior).
var MemoryRecallMetadata = config.CommandMetadata{ Use: "recall <id-or-path>", Short: "Traverse the graph from a note and return neighbors with full frontmatter", Long: `Perform a breadth-first traversal starting from a target note and return all neighbors within the specified depth. Every node in the result carries its full frontmatter, so callers get a rich, typed neighborhood without follow-up note-get calls. A note whose status is draft is not a neighbor unless you pass --include-drafts. The note you named is still returned. BFS (breadth-first search) means depth=1 returns only direct neighbors, depth=2 includes their neighbors as well, and so on. CONFIDENCE LEVELS Edges in the vault carry a confidence level (low, medium, high). --min-confidence sets the floor: only edges at or above that level are traversed. The default is "high" so recall stays precise; lower it to "medium" or "low" to include weaker, LLM-inferred relationships. FLAGS --depth <n> Maximum traversal depth. Default: 1. --max-nodes <n> Safety limit on result size. Traversal stops when N nodes have been collected. The JSON output includes a "max_nodes_reached" field when the limit was hit. Default: 50. --min-confidence <low|medium|high> Minimum edge confidence required to follow an edge. Default: high. --vault <path> Path to vault root. Default: ".". --json Output as a JSON envelope instead of text. EXAMPLES vaultmind memory recall concept-spreading-activation # direct neighbors (depth 1) at high confidence vaultmind memory recall concept-spreading-activation --depth 2 --max-nodes 20 # up to depth 2, capped at 20 nodes vaultmind memory recall concept-spreading-activation --min-confidence medium --json # include medium-confidence edges, machine-readable output WHEN TO USE Use recall when you want the full neighborhood around a known note — for populating a reasoning window or checking what is explicitly linked. For a flat list of edge-typed relations, use "memory related" instead. For a token-budgeted payload ready for agent consumption, use "memory context-pack" instead.`, ConfigPrefix: "app.memoryrecall", FlagOverrides: map[string]string{ "app.memoryrecall.vault": "vault", "app.memoryrecall.json": "json", "app.memoryrecall.depth": "depth", "app.memoryrecall.min_confidence": "min-confidence", "app.memoryrecall.max_nodes": "max-nodes", }, }
MemoryRecallMetadata defines metadata for the memory recall command.
var MemoryRelatedMetadata = config.CommandMetadata{ Use: "related <id-or-path>", Short: "List notes related to the target, filtered by edge type", Long: `Return all notes directly related to the target, with each result showing the edge type and confidence level. Use --mode to filter by how the relationship was established. A note whose status is draft is left out unless you pass --include-drafts. The note you named is still the target. MODES explicit Only edges recorded in the note's frontmatter (related_ids, parent_id, source_ids), wikilinks [[...]], and markdown links [...]. These are user-curated and carry high confidence. inferred Only LLM-inferred edges. These represent relationships the model detected during indexing but that the user has not explicitly linked. They carry medium or low confidence and are useful for discovery. mixed Both explicit and inferred edges, ranked by confidence and edge type. This is the default. FLAGS --mode <explicit|inferred|mixed> Which edge types to include. Default: mixed. --vault <path> Path to vault root. Default: ".". --json Output as a JSON envelope instead of text. EXAMPLES vaultmind memory related concept-spreading-activation # all related notes (explicit + inferred) vaultmind memory related concept-spreading-activation --mode explicit # only notes with explicit frontmatter or wikilink edges vaultmind memory related concept-spreading-activation --mode inferred --json # LLM-inferred suggestions only, machine-readable WHEN TO USE Use related for a flat list of directly linked notes when you already know the target. For a depth-traversal that follows edges transitively, use "memory neighbors". For a token-budgeted agent payload, use "memory pack".`, ConfigPrefix: "app.memoryrelated", FlagOverrides: map[string]string{ "app.memoryrelated.vault": "vault", "app.memoryrelated.json": "json", "app.memoryrelated.mode": "mode", }, }
MemoryRelatedMetadata defines metadata for the memory related command.
var MemorySummarizeMetadata = config.CommandMetadata{ Use: "summarize [id1 id2 ...]", Short: "Assemble note material for agent synthesis", Long: "Load frontmatter and body excerpts from a set of notes. Agents use this output to create reflection notes. Not an LLM call — data assembly only.", ConfigPrefix: "app.memorysummarize", FlagOverrides: map[string]string{ "app.memorysummarize.vault": "vault", "app.memorysummarize.json": "json", "app.memorysummarize.ids": "ids", "app.memorysummarize.include_body": "include-body", "app.memorysummarize.max_body_len": "max-body-len", }, }
MemorySummarizeMetadata defines metadata for the memory summarize command.
var NoteAcceptMetadata = config.CommandMetadata{ Use: "accept <id>", Short: "Promote a draft into recall", Long: `Promote a draft written by note propose. Accepting is how a person says the draft belongs in recall. - A new draft flips status to the schema's published value (accepted when the type lists it or lists nothing, active when that is the published word). Same file, same id. - A patch draft appends its body under a heading of its question on the target note, keeps the target's frontmatter, adds sources when the target's type allows that field, and deletes the proposal. One copy remains. - A note that is not a draft is refused. A patch whose target is gone is refused. A proposal that was already accepted, and is gone from the index and from disk, exits 0 and does not change the note that holds its marker. The notes that changed are re-indexed, and embedded when embed-on-write is on. FLAGS --vault <dir> Path to the vault root. --json Output a v1 envelope.`, ConfigPrefix: "app.noteaccept", FlagOverrides: map[string]string{ "app.noteaccept.vault": "vault", "app.noteaccept.json": "json", }, }
NoteAcceptMetadata defines metadata for note accept.
var NoteCreateMetadata = config.CommandMetadata{ Use: "create <path> --type <type>", Short: "Create a note from a template with field overrides", Long: `Create a new note at <path> by expanding the template registered for --type, then override individual frontmatter fields and body text before writing. --type is required. The type must be registered in the vault schema; unknown types are rejected with a list of registered alternatives. TEMPLATE WORKFLOW 1. The vault schema maps each type to a template file. 2. Placeholders in the template (ID, TITLE, PATH, CREATED_DATE) are auto-filled from the note path and creation time. 3. --field key=value patches a frontmatter field after auto-fill (repeatable). 4. --body or --body-stdin replaces the template body. 5. Required fields declared by the type must be present and non-empty after all substitutions; missing required fields fail with an actionable error naming the field and suggesting --field. 6. The note is written to <vault-root>/<path>. The path must stay inside the vault directory (path traversal is rejected). FLAGS --type <type> Note type (required). Must match a type in the vault schema. --vault <dir> Path to vault root (default: "."). --field key=value Patch a frontmatter field after template expansion (repeatable). --body <text> Replace the template body with literal text. --body-stdin Read the body replacement from stdin (overrides --body). --commit Stage and git-commit the new file after writing. --json Output a structured envelope instead of the default one-liner. OUTPUT INCLUDES (--json) path vault-relative path of the created note id auto-assigned note ID type the --type value used created true on success write_hash sha256 of the file as written (integrity check) commit_sha git commit SHA when --commit is used (omitted otherwise) warnings list of non-fatal issues (e.g. re-index failure) EXAMPLES vaultmind note create concepts/memory.md --type concept --vault my-vault # create a concept note; auto-fill ID, TITLE, CREATED_DATE from the template vaultmind note create principles/focus.md --type principle --field author=alice # create and set the "author" frontmatter field in one step vaultmind note create episodes/ep-001.md --type episode --body-stdin --vault vaultmind-identity <<EOF Session opened. Explored spreading-activation scoring. EOF # pipe a multi-line body from stdin vaultmind note create arcs/growth.md --type arc --commit --vault vaultmind-identity # create and immediately git-commit the new file`, ConfigPrefix: "app.notecreate", FlagOverrides: map[string]string{ "app.notecreate.vault": "vault", "app.notecreate.json": "json", "app.notecreate.type": "type", "app.notecreate.body": "body", "app.notecreate.commit": "commit", }, }
NoteCreateMetadata defines metadata for the note create command.
var NoteGapMetadata = config.CommandMetadata{ Use: "gap <question>", Short: "Record a question the vault did not answer", Long: `Record an open gap: a note of type gap for a question this vault did not answer. A gap is a note under gaps/, so it stays in git. The index is a cache and is not the record. The note stores the question, when it was opened, the session when VAULTMIND_USER_SESSION_ID is set, and the note ids ask returns for that question right now. Drafts and gaps are not among those ids. An open gap with the same question, spacing and case aside, is reported (mode existing) instead of writing another note. A closed gap does not count. Gaps are never returned as knowledge. --include-drafts does not reveal them. note get still reads one. The tree marks it [gap]. doctor lists the open ones. FLAGS --vault <dir> Path to the vault root. --json Output a v1 envelope.`, ConfigPrefix: "app.notegap", FlagOverrides: map[string]string{ "app.notegap.vault": "vault", "app.notegap.json": "json", }, }
NoteGapMetadata defines metadata for note gap.
var NoteGetMetadata = config.CommandMetadata{ Use: "get <id-or-path>", Short: "Get a note's full content and metadata", Long: `Return full note content including frontmatter, body, headings, and blocks. Use --frontmatter-only to omit body text. This is the canonical READ-ONE-NOTE-BY-ID path. It fires access tracking (reinforcement signal for the activation-based ranking layer), so prefer it over the Read tool when you want to read a vault note's body. The cleanest read path is also the tracked one. EXAMPLES vaultmind note get reference-current-context --vault vaultmind-identity Print the note's id+type+title header followed by the body inline. vaultmind note get reference-current-context --vault vaultmind-identity --json Same content, structured envelope. Use from scripts. vaultmind note get concept-act-r --vault vaultmind-vault --frontmatter-only vaultmind note get <id> --vaults ~/identity,~/desk,~/research # an id from a federated ask Header + frontmatter only, no body. Use to inspect tags/related_ids. PAIRS WELL WITH vaultmind ask "X" --pointers-only # find the right id, then note get it vaultmind self # see what you've been touching`, ConfigPrefix: "app.note", FlagOverrides: map[string]string{ "app.note.vault": "vault", "app.note.vaults": "vaults", "app.note.json": "json", "app.note.frontmatter_only": "frontmatter-only", }, }
NoteGetMetadata defines the metadata for the note get command.
var NotePinnedMetadata = config.CommandMetadata{ Use: "pinned", Short: "Load notes marked pinned: true", Long: `Load every note whose frontmatter says pinned: true. Notes are ordered by id within a vault, and vaults are read in the order given to --vaults. One --budget covers every vault (default 1500). A note that does not fit is named by id, title, and vault. It is never dropped without a name. --excerpt N caps each note the same way arc recite does. 0 means the whole body. The session-start hook passes 400, so a long note arrives as its opening. With no pinned notes, the command prints nothing and exits 0, so a session-start hook can call it every time. The mark lives in the note, so it survives hooks install --force. Run hooks install --force once to pick up the hook that loads them. FLAGS --vault <dir> Path to the vault root. --vaults a,b,c Several vaults, comma-separated. Overrides --vault. One budget across all of them. --budget N Token ceiling for the whole run. 0 means unbounded. --excerpt N Cap each note at N tokens. 0 means the whole body. --json Output a v1 envelope.`, ConfigPrefix: "app.notepinned", FlagOverrides: map[string]string{ "app.notepinned.vault": "vault", "app.notepinned.vaults": "vaults", "app.notepinned.json": "json", "app.notepinned.budget": "budget", "app.notepinned.excerpt": "excerpt", }, }
NotePinnedMetadata defines metadata for note pinned.
var NoteProposeMetadata = config.CommandMetadata{ Use: "propose", Short: "Turn a finding into a reviewable draft", Long: `Write a draft from a question and a finding, without choosing an id or a path. propose retrieves on the question and the finding the same way ask does, drafts excluded, and reads the relevance band ask already computed for the top hit. It does not invent a second score. - patch, when that band is at least the close band (default: strong). A stronger band is also a patch: a strong hit with --close-band moderate still patches. A draft is written next to the closest note, or in --drafts-dir when that note is under the import folder. status is draft, patches names the target, and the body is the finding plus a Sources section. An accepted note cannot hold the addition yet: recall would return it before a person had reviewed it. - new, when the band is weaker than the close band, empty, or missing (moderate against a strong close band, weak, no_match, or no band). The type is the top hit's type when that type is registered, except the type the import command writes. Otherwise the vault's default knowledge type (concept when the schema registers it, otherwise the first type that can hold a draft). The id is the note-create id of a filename slugged from the question. The file is in the top hit's folder, or the vault root when there is no hit. A hit under the import folder is filed in --drafts-dir instead (default notes). The file is expanded through that type's template, so required fields hold. - existing, when a draft already has the same question (spacing and case aside). That draft is reported and nothing new is written. A person reviews the draft and promotes it with: vaultmind note accept <id> --finding is required. A source that contains :// must be a URL. Any other source must be a file inside the vault or the repository that contains it. --close-band names the weakest ask band that is close enough to patch. A stronger band is also close. The default is the strong band, so a weaker match becomes a new note instead of a patch on the wrong one. A band the vault does not know is not close. After the draft is written, propose re-runs the original question through the same ranking ask uses, with the same retrieval options and the default hit window (app.ask.search_limit). The line's before is the band that chose patch or new, measured on the question and the finding. It then reports, labeled as the question alone, where the closest existing note ranked with drafts excluded, and where this draft ranks with drafts included. rank_before is that question-only rank. A patch is measured on the proposal note, which holds the finding. The draft's frontmatter records rank_before, rank_after, and rank_checked_at. 0 means the note was not in the window. The check does not record an access. Keyword mode, when no embedder is available, still runs and is labeled. --json adds rank_check to the v1 envelope. FLAGS --question <text> The question the finding answers. --finding <text> The finding. Required. --source <url|path> A source URL or a file inside the vault or its repository. Repeatable. --close-band <band> Weakest ask relevance band that is close enough to patch. A stronger band is also close. --drafts-dir <dir> Folder for a draft that must not sit in the import folder. Default notes. "." is the vault root. --gap <id> An open gap this finding answers. The draft stores closes_gap. When --question is omitted, the gap's question is used. Accepting the draft closes that gap. --vault <dir> Path to the vault root. --json Output a v1 envelope.`, ConfigPrefix: "app.notepropose", FlagOverrides: map[string]string{ "app.notepropose.vault": "vault", "app.notepropose.json": "json", "app.notepropose.question": "question", "app.notepropose.finding": "finding", "app.notepropose.close_band": "close-band", "app.notepropose.drafts_dir": "drafts-dir", "app.notepropose.gap": "gap", }, }
NoteProposeMetadata defines metadata for note propose.
var PingMetadata = config.CommandMetadata{ Use: "ping", Short: "Responds with a pong", Long: `The ping command demonstrates configuration, logging, and optional Bubble Tea UI. - Without arguments, prints "Pong". - Supports overriding its output and an optional interactive UI.`, ConfigPrefix: "app.ping", FlagOverrides: map[string]string{ "app.ping.output_message": "message", "app.ping.output_color": "color", "app.ping.ui": "ui", }, Examples: []string{ "ping", "ping --message 'Hello World!'", "ping --color green", "ping --ui", }, SeeAlso: []string{"docs"}, }
PingMetadata defines all metadata for the ping command
var ResolveMetadata = config.CommandMetadata{ Use: "resolve <id-or-title-or-alias>", Short: "Resolve ambiguous note references to canonical IDs", Long: `Turn a fragment — a partial ID, a title, an alias, or a file path — into one or more matched notes with a resolution tier that explains how the match was found. Use resolve when you have a string that might refer to a note but you are not certain of its exact ID, or when you want to check whether a reference is ambiguous before acting on it. RESOLUTION TIERS Resolve walks the input through five tiers, returning at the first hit: path Input contains "/" or ends in ".md"; matched against stored paths. id Exact match against the note's canonical ID. title Exact match against the note's title field. alias Exact match against any alias stored for the note. normalized Case-insensitive match on title or alias, including treating hyphens and underscores as spaces. If no tier produces a hit, resolve reports "No match". If a tier produces multiple hits, the result is marked ambiguous and all candidates are returned — resolve does not silently pick one. FLAGS --vault: path to the vault root directory (string, default ".") --json: output the full resolution envelope as JSON instead of plain text (bool, default false) OUTPUT INCLUDES Plain text (one line per match): <id> <type> <title> (<path>) JSON envelope: resolved whether any match was found ambiguous true when multiple notes matched at the same tier input the original query string resolution_tier which tier produced the match ("id", "title", etc.) matches[] array of { id, type, title, path, status } WHEN TO USE resolve You have a fragment or alias and need the canonical ID. Handles ambiguity explicitly — tells you when a string is not unique. ask You have a semantic question and want ranked context around the best-matching note; works across whole vault topics, not single IDs. search You want a ranked list of notes matching a free-text query without needing exact disambiguation. EXAMPLES vaultmind resolve concept-act-r --vault ./my-vault # exact ID lookup vaultmind resolve "ACT-R" --vault ./my-vault # case-insensitive title match vaultmind resolve act-r --vault ./my-vault # normalized (hyphen → space) vaultmind resolve notes/decision-foo.md --vault . # path shortcut vaultmind resolve spreading-activation --json --vault . # machine-readable output`, ConfigPrefix: "app.resolve", FlagOverrides: map[string]string{ "app.resolve.vault": "vault", "app.resolve.json": "json", }, }
ResolveMetadata defines the metadata for the resolve command.
var SchemaListTypesMetadata = config.CommandMetadata{ Use: "list-types", Short: "Show the vault's type registry — available note types, required fields, and valid statuses", Long: `Return every registered note type together with its required-fields flag and allowed status values. Agents run this during startup or before creating notes to discover what types the vault supports and what constraints apply to each. Humans rarely need it directly; its primary use is feeding agent initialization logic and type-aware tooling. OUTPUT INCLUDES Text (default): one line per type — name (12-char padded), required=true|false, statuses=[...] JSON (--json): envelope with status "ok", command "schema list-types", and a "data" object keyed by type name; each value carries required and statuses fields. FLAGS --json Output the registry as a JSON envelope (default: human-readable text). --vault Path to vault root (default: current directory). EXAMPLES vaultmind schema list-types Print the type registry as a text table — use to see at a glance what types exist. vaultmind schema list-types --vault ./my-vault Same, but targeting a vault at a specific path. vaultmind schema list-types --json Return the registry as a JSON envelope; use in agent init scripts or tooling that needs to validate a type before calling "note create". WHEN TO USE Run list-types before creating a note when you are unsure whether a type name is valid or which status values are accepted. It is cheaper than attempting a write and handling a validation error.`, ConfigPrefix: "app.schema", FlagOverrides: map[string]string{ "app.schema.vault": "vault", "app.schema.json": "json", }, }
SchemaListTypesMetadata defines the metadata for the schema list-types command.
var SearchMetadata = config.CommandMetadata{ Use: "search <query>", Short: "Search vault notes by keyword, semantic similarity, or both", Long: `Search note titles and body text and return ranked hits. Keyword mode is the default and needs no index; semantic and hybrid modes require embeddings built with "vaultmind index --embed". SEARCH MODES keyword (default) Pure full-text search using SQLite FTS5. Fast, no embeddings needed. Best for exact-term queries. semantic Embedding-based similarity search. Needs: vaultmind index --embed. Best for concept queries where exact wording varies. hybrid Combines keyword and semantic via reciprocal rank fusion (RRF). Best all-around mode when embeddings are available. Needs: vaultmind index --embed. FLAGS --vault string Path to vault root (default ".") --mode string Search mode: keyword, semantic, or hybrid (default "keyword") --type string Filter results to notes of this type (e.g. concept, source) --tag string Filter results to notes with this tag --include-drafts Include notes whose status is draft (default false) --limit int Maximum number of results to return (default 20) --offset int Skip the first N results for pagination (default 0) --vaults string Search several vaults, comma-separated — one ranked section per vault --json Output results as a JSON envelope instead of plain text OUTPUT INCLUDES Default: one line per hit — id followed by title --json: envelope with query, offset, limit, total, and hits array (each hit: id, title, score, type, path) WHEN TO USE search: You want to browse ranked hits and choose what to read next. ask: You want the top hit plus token-budgeted context already packed — one command replaces the search-then-recall chain. EXAMPLES vaultmind search "spreading activation" --vault my-vault vaultmind search "memory consolidation" --mode hybrid --vault my-vault # semantic + keyword vaultmind search "attention" --type concept --tag learning --vault my-vault # filtered vaultmind search "retrieval" --limit 5 --json --vault my-vault # machine-readable top 5 vaultmind search "recall" --limit 10 --offset 10 --vault my-vault # page 2`, ConfigPrefix: "app.search", FlagOverrides: map[string]string{ "app.search.vault": "vault", "app.search.json": "json", "app.search.limit": "limit", "app.search.offset": "offset", "app.search.type": "type", "app.search.tag": "tag", "app.search.mode": "mode", "app.search.vaults": "vaults", }, }
SearchMetadata defines the metadata for the search command.
var SelfMetadata = config.CommandMetadata{ Use: "self", Short: "Show your memory state — recent, hot, stale notes", Long: `Render the activation state of your vault — first-person AX for the agent using vaultmind as long-term memory. Reads access_count + last_accessed_at, which RecordNoteAccess populates on every successful Ask + note get. THREE SECTIONS Recent (newest first): The last N notes you touched. Anchors you in "where am I right now" in conceptual space. Hot (top activation): Ranked by ln(1+count) - d*ln(elapsed_hours). Captures both frequency and recency. Stale (drifting away): Accessed but past the threshold. What your memory used to hold but isn't returning to. The hot column shows activation as +0.00 for the leader, then negative numbers indicating how-much-below the leader. Order-preserving — the sign is just relative-distance, not "broken". EXAMPLES vaultmind self --vault vaultmind-vault Default — limit 10 per section, 7-day stale threshold. vaultmind self --vault vaultmind-identity --limit 5 Tighter view — 5 rows per section. AUTO-INJECTED UNDER THE PERSONA PROFILE With hooks installed under the persona (or full) profile, the SessionStart hook runs 'vaultmind self' for the identity and research vaults, so you see your memory state before any work begins. The knowledge profile — a fresh project's default — does not; run this command when you want the check.`, ConfigPrefix: "app.self", FlagOverrides: map[string]string{ "app.self.vault": "vault", "app.self.limit": "limit", }, }
SelfMetadata defines the metadata for the self command — the agent's own memory-state introspection view (recent / hot / stale).
var ServeMetadata = config.CommandMetadata{ Use: "serve", Short: "Keep vaultmind warm so ask answers faster (started for you)", Long: "Keep one vaultmind process running with the embedding model loaded, and answer " + "`vaultmind ask` from it: about 0.6 s faster per ask, the time a new process spends " + "loading the model.\n\n" + "You don't need to run this. The first ask starts it in the background, and later asks " + "use it. Each ask still runs the full command, in its own directory and environment and " + "against the index as it is now, so it answers exactly what it would without the server. " + "If the server is gone or slow, ask runs on its own.\n\n" + "One server runs per user per installed binary, shared by every vault and project; it " + "listens on a private socket in the state directory and exits after --idle without a " + "request, releasing the model's memory. VAULTMIND_NO_SERVE=1 turns it off.", ConfigPrefix: "app.serve", FlagOverrides: map[string]string{ "app.serve.idle": "idle", "app.serve.max_rss_mb": "max-rss-mb", }, SeeAlso: []string{"ask"}, }
ServeMetadata defines the serve command.
var TreeMetadata = config.CommandMetadata{ Use: "tree", Short: "Show what a vault holds — folders, counts, one line per note", Long: "Print the map of a vault: every folder with how many notes it holds, and every " + "note as `Title (id) — what it is about`. The line is the first sentence of the " + "note's Principle section, or of its opening prose. Open a note with " + "`vaultmind note get <id>`.\n\n" + "A folder line says what the folder covers: the first sentence of its overview " + "note (a note of type overview inside it, written by whoever curates the vault), " + "else its most used tags. `vaultmind doctor` names big folders that have none.\n\n" + "Search needs a question already formed; the map shows what is there to ask about. " + "Start a large vault with --depth 1 (folders and counts), then narrow with --path " + "or --type.\n\n" + "A note names the code it is about with `paths:` in its frontmatter — globs relative " + "to the repository root (`internal/hooks/**`, `cmd/*.go`), with `repo:` in front for " + "code in another repository. --for <file> lists the notes that cover that file. " + "A draft is left out of that list unless --include-drafts is set; the full map " + "still shows it and marks the line [draft].\n\n" + " vaultmind tree --vault ./vaultmind-vault --depth 1\n" + " vaultmind tree --vault ./vaultmind-vault --path decisions/\n" + " vaultmind tree --vault ./vaultmind-vault --type concept --brief\n" + " vaultmind tree --vaults ./vaultmind-vault,./docs-vault --depth 1\n" + " vaultmind tree --vault ./vaultmind-vault --for internal/hooks/merge.go\n" + " vaultmind tree --vault ./vaultmind-vault --json", ConfigPrefix: "app.tree", FlagOverrides: map[string]string{ "app.tree.vault": "vault", "app.tree.vaults": "vaults", "app.tree.json": "json", "app.tree.path": "path", "app.tree.type": "type", "app.tree.depth": "depth", "app.tree.brief": "brief", "app.tree.for": "for", }, }
TreeMetadata defines the `tree` command — the map of a vault.
var VaultStatusMetadata = config.CommandMetadata{ Use: "status", Short: "Vault status summary (cold-start)", Long: "Single-call summary combining note counts, type registry, index freshness, and validation issues.", ConfigPrefix: "app.vaultstatus", FlagOverrides: map[string]string{ "app.vaultstatus.vault": "vault", "app.vaultstatus.json": "json", }, }
VaultStatusMetadata defines the metadata for the vault status command.
Functions ¶
func ApplyOptions ¶
func ApplyOptions() []config.ConfigOption
ApplyOptions returns config options for the apply command.
func ArcCandidatesOptions ¶
func ArcCandidatesOptions() []config.ConfigOption
ArcCandidatesOptions returns configuration options for `arc candidates`.
func ArcReciteOptions ¶ added in v0.8.0
func ArcReciteOptions() []config.ConfigOption
ArcReciteOptions returns configuration options for `arc recite`.
func ArcReviewOptions ¶ added in v0.9.3
func ArcReviewOptions() []config.ConfigOption
ArcReviewOptions returns configuration options for `arc review`.
func AskOptions ¶
func AskOptions() []config.ConfigOption
AskOptions returns config options for the ask command.
func CheckOptions ¶
func CheckOptions() []config.ConfigOption
CheckOptions returns configuration options for the check command
func DataviewOptions ¶
func DataviewOptions() []config.ConfigOption
DataviewOptions returns config options for dataview commands.
func DocsCommandsOptions ¶ added in v0.1.11
func DocsCommandsOptions() []config.ConfigOption
DocsCommandsOptions returns configuration options for `docs commands`.
func DoctorHealOptions ¶ added in v0.1.11
func DoctorHealOptions() []config.ConfigOption
DoctorHealOptions returns configuration options for `doctor heal`.
func DoctorHealWikilinksOptions ¶ added in v0.1.11
func DoctorHealWikilinksOptions() []config.ConfigOption
DoctorHealWikilinksOptions returns configuration options for `doctor heal wikilinks`.
func DoctorOptions ¶
func DoctorOptions() []config.ConfigOption
DoctorOptions returns configuration options for the doctor command.
func EvalOptions ¶ added in v0.9.16
func EvalOptions() []config.ConfigOption
EvalOptions returns configuration options for `eval`.
func ExperimentCompareOptions ¶
func ExperimentCompareOptions() []config.ConfigOption
ExperimentCompareOptions returns config options for the experiment compare command.
func ExperimentReportOptions ¶
func ExperimentReportOptions() []config.ConfigOption
ExperimentReportOptions returns config options for the experiment report command.
func ExperimentSummaryOptions ¶
func ExperimentSummaryOptions() []config.ConfigOption
ExperimentSummaryOptions returns config options for the experiment summary command.
func ExperimentTraceOptions ¶
func ExperimentTraceOptions() []config.ConfigOption
ExperimentTraceOptions returns config options for the experiment trace command.
func ExperimentsOptions ¶
func ExperimentsOptions() []config.ConfigOption
ExperimentsOptions returns config options for the experiments subsystem.
func ExportOptions ¶
func ExportOptions() []config.ConfigOption
ExportOptions returns configuration options for the export command.
func FrontmatterFixOptions ¶
func FrontmatterFixOptions() []config.ConfigOption
FrontmatterFixOptions returns configuration options for the fix command.
func FrontmatterMutationOptions ¶
func FrontmatterMutationOptions() []config.ConfigOption
FrontmatterMutationOptions returns config options for mutation commands.
func FrontmatterOptions ¶
func FrontmatterOptions() []config.ConfigOption
FrontmatterOptions returns configuration options for frontmatter commands.
func GitStatusOptions ¶
func GitStatusOptions() []config.ConfigOption
GitStatusOptions returns configuration options for git status.
func HooksInstallOptions ¶
func HooksInstallOptions() []config.ConfigOption
HooksInstallOptions returns configuration options for the hooks install subcommand. The project-dir argument is positional; --force and --json are the flags.
func HooksRecordOptions ¶ added in v0.7.1
func HooksRecordOptions() []config.ConfigOption
HooksRecordOptions returns configuration options for `hooks record`.
func HooksStatusOptions ¶ added in v0.6.0
func HooksStatusOptions() []config.ConfigOption
HooksStatusOptions returns configuration options for `hooks status`.
func HooksUninstallOptions ¶
func HooksUninstallOptions() []config.ConfigOption
HooksUninstallOptions returns configuration options for the hooks uninstall subcommand. The project-dir argument is positional.
func IdentityEnrollAddOptions ¶ added in v0.2.0
func IdentityEnrollAddOptions() []config.ConfigOption
IdentityEnrollAddOptions returns config options for `identity enroll-add`. Every flag description lives here (SSOT) — never inline in cmd/.
func IdentityEnrollOptions ¶ added in v0.2.0
func IdentityEnrollOptions() []config.ConfigOption
IdentityEnrollOptions returns config options for `identity enroll`. Every flag description lives here (SSOT) — never inline in cmd/.
func IdentityFetchRegistryOptions ¶ added in v0.9.0
func IdentityFetchRegistryOptions() []config.ConfigOption
IdentityFetchRegistryOptions returns config options for `identity fetch-registry`.
func IdentityInitOptions ¶ added in v0.2.0
func IdentityInitOptions() []config.ConfigOption
IdentityInitOptions returns config options for `identity init`.
func IdentityInviteOptions ¶ added in v0.2.0
func IdentityInviteOptions() []config.ConfigOption
IdentityInviteOptions returns config options for `identity invite`.
func IdentityPathsOptions ¶ added in v0.8.0
func IdentityPathsOptions() []config.ConfigOption
IdentityPathsOptions returns configuration options for `identity paths`.
func IdentitySignEnrollmentOptions ¶ added in v0.2.0
func IdentitySignEnrollmentOptions() []config.ConfigOption
IdentitySignEnrollmentOptions returns config options for `identity sign-enrollment`.
func IdentitySignEnvelopeOptions ¶ added in v0.2.0
func IdentitySignEnvelopeOptions() []config.ConfigOption
IdentitySignEnvelopeOptions returns config options for `identity sign-envelope`.
func IdentitySignOptions ¶ added in v0.2.0
func IdentitySignOptions() []config.ConfigOption
IdentitySignOptions returns config options for `identity sign`.
func IdentitySignRegistryOptions ¶ added in v0.2.0
func IdentitySignRegistryOptions() []config.ConfigOption
IdentitySignRegistryOptions returns config options for `identity sign-registry`.
func IdentitySignerInstallOptions ¶ added in v0.8.0
func IdentitySignerInstallOptions() []config.ConfigOption
IdentitySignerInstallOptions returns config options for `identity signer install`.
func IdentitySignerOptions ¶ added in v0.2.0
func IdentitySignerOptions() []config.ConfigOption
IdentitySignerOptions returns config options for `identity signer`.
func ImportOptions ¶ added in v0.9.17
func ImportOptions() []config.ConfigOption
ImportOptions returns configuration options for `import`.
func IndexOptions ¶
func IndexOptions() []config.ConfigOption
IndexOptions returns configuration options for the index command.
func InitOptions ¶
func InitOptions() []config.ConfigOption
InitOptions returns configuration options for the init command. The path argument is positional; --print-instructions is the one flag, which prints the embedded onboarding doc and exits without creating a vault. The flag is the answer to AX-design Q2 ("where does the doc live?") — embedded in the binary, accessible wherever vaultmind is installed.
func LinksNeighborsOptions ¶
func LinksNeighborsOptions() []config.ConfigOption
LinksNeighborsOptions returns configuration options for the links neighbors command.
func LinksOptions ¶
func LinksOptions() []config.ConfigOption
LinksOptions returns configuration options for links commands.
func LintFixLinksOptions ¶
func LintFixLinksOptions() []config.ConfigOption
LintFixLinksOptions returns configuration options for the lint fix-links command.
func McpOptions ¶ added in v0.10.3
func McpOptions() []config.ConfigOption
McpOptions returns the mcp command's options.
func MemoryContextPackOptions ¶
func MemoryContextPackOptions() []config.ConfigOption
MemoryContextPackOptions returns config options for memory context-pack.
func MemoryLinksOptions ¶ added in v0.1.11
func MemoryLinksOptions() []config.ConfigOption
MemoryLinksOptions returns config options for memory links.
func MemoryNeighborsOptions ¶ added in v0.1.11
func MemoryNeighborsOptions() []config.ConfigOption
MemoryNeighborsOptions returns config options for memory neighbors.
func MemoryPackOptions ¶ added in v0.1.11
func MemoryPackOptions() []config.ConfigOption
MemoryPackOptions returns config options for memory pack.
func MemoryRecallOptions ¶
func MemoryRecallOptions() []config.ConfigOption
MemoryRecallOptions returns config options for memory recall.
func MemoryRelatedOptions ¶
func MemoryRelatedOptions() []config.ConfigOption
MemoryRelatedOptions returns config options for memory related.
func MemorySummarizeOptions ¶
func MemorySummarizeOptions() []config.ConfigOption
MemorySummarizeOptions returns config options for memory summarize.
func NoteAcceptOptions ¶ added in v0.10.13
func NoteAcceptOptions() []config.ConfigOption
NoteAcceptOptions returns config options for note accept.
func NoteCreateOptions ¶
func NoteCreateOptions() []config.ConfigOption
NoteCreateOptions returns config options for note create.
func NoteGapOptions ¶ added in v0.10.13
func NoteGapOptions() []config.ConfigOption
NoteGapOptions returns config options for note gap.
func NoteOptions ¶
func NoteOptions() []config.ConfigOption
NoteOptions returns configuration options for note commands.
func NotePinnedOptions ¶ added in v0.10.16
func NotePinnedOptions() []config.ConfigOption
NotePinnedOptions returns config options for note pinned.
func NoteProposeOptions ¶ added in v0.10.13
func NoteProposeOptions() []config.ConfigOption
NoteProposeOptions returns config options for note propose.
func PingOptions ¶
func PingOptions() []config.ConfigOption
PingOptions returns configuration options for the ping command
func ResolveOptions ¶
func ResolveOptions() []config.ConfigOption
ResolveOptions returns configuration options for the resolve command.
func RetrievalOptions ¶ added in v0.10.13
func RetrievalOptions() []config.ConfigOption
RetrievalOptions is the shared retrieval policy. One key, read by every command that ranks notes.
func SchemaOptions ¶
func SchemaOptions() []config.ConfigOption
SchemaOptions returns configuration options for schema commands.
func SearchOptions ¶
func SearchOptions() []config.ConfigOption
SearchOptions returns configuration options for the search command.
func SelfOptions ¶
func SelfOptions() []config.ConfigOption
SelfOptions returns configuration options for the self command.
func ServeOptions ¶ added in v0.10.6
func ServeOptions() []config.ConfigOption
ServeOptions returns the serve command's options.
func TreeOptions ¶ added in v0.9.9
func TreeOptions() []config.ConfigOption
TreeOptions returns configuration options for `tree`.
func VaultStatusOptions ¶
func VaultStatusOptions() []config.ConfigOption
VaultStatusOptions returns configuration options for vault status.
func WriteOptions ¶ added in v0.9.10
func WriteOptions() []config.ConfigOption
WriteOptions are the settings every command that writes a note shares.
Types ¶
This section is empty.
Source Files
¶
- apply_config.go
- arc_candidates_config.go
- arc_recite_config.go
- arc_review_config.go
- ask_config.go
- check_config.go
- dataview_config.go
- docs_commands_config.go
- doctor_config.go
- doctor_heal_config.go
- eval_config.go
- experiment_config.go
- experiments_config.go
- export_config.go
- frontmatter_config.go
- frontmatter_fix_config.go
- frontmatter_mutation_config.go
- git_status_config.go
- hooks_install_config.go
- hooks_record_config.go
- hooks_status_config.go
- hooks_uninstall_config.go
- identity_enroll_add_config.go
- identity_enroll_config.go
- identity_fetch_registry_config.go
- identity_init_config.go
- identity_invite_config.go
- identity_paths_config.go
- identity_sign_config.go
- identity_sign_enrollment_config.go
- identity_sign_envelope_config.go
- identity_sign_registry_config.go
- identity_signer_config.go
- identity_signer_install_config.go
- import_config.go
- index_config.go
- init_config.go
- links_config.go
- lint_config.go
- mcp_config.go
- memory_config.go
- note_accept_config.go
- note_config.go
- note_create_config.go
- note_gap_config.go
- note_pinned_config.go
- note_propose_config.go
- ping_config.go
- resolve_config.go
- retrieval_config.go
- schema_config.go
- search_config.go
- self_config.go
- serve_config.go
- tree_config.go
- vault_config.go
- write_config.go