Installs into .claude/skills of the current project.
Are you the author of Vault?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/burakolgun-vault)
---
name: vault
description: Generate an Obsidian LLM wiki vault from a project's source code and any static documents in raw/
argument-hint: "[project-dir] [--workspace <dir>] [--lang <code>]"
---
# /vault-anything:vault
Analyzes a project's source code and any static documents the user has placed under `raw/`, then generates a Karpathy-style Obsidian wiki vault where every page is self-contained and useful as either human documentation or LLM context.
## Arguments
| Argument | Default | Description |
|----------|---------|-------------|
| `project-dir` | current directory | Source code to analyze |
| `--workspace` | `<project>/vault-anything/` | Where `raw/` and `vault/` are written |
| `--lang` | `en` | Vault page output language (must match a file in `locales/`) |
---
## Phase 0 — Pre-flight
Run the resolvers to get plugin and workspace paths. Save the outputs into shell variables you'll use throughout:
```bash
set -e
PLUGIN_ROOT=$(node "$CLAUDE_SKILL_DIR/resolve-plugin-root.mjs")
WORKSPACE=$(node "$PLUGIN_ROOT/skills/vault/resolve-workspace.mjs" "$@")
```
Parse `$WORKSPACE` (it's a JSON object) into shell variables with `jq`:
```bash
PROJECT_DIR=$(echo "$WORKSPACE" | jq -r '.projectDir')
PROJECT_NAME=$(echo "$WORKSPACE" | jq -r '.projectName')
WORKSPACE_DIR=$(echo "$WORKSPACE" | jq -r '.workspaceDir')
RAW_DIR=$(echo "$WORKSPACE" | jq -r '.rawDir')
VAULT_DIR=$(echo "$WORKSPACE" | jq -r '.vaultDir')
EXISTS=$(echo "$WORKSPACE" | jq -r '.exists')
VAULT_LANG="en" # parse from --lang argument if user provided one
```
**Decision:**
- If `$EXISTS` is `"true"`, this project has been analyzed before. Ask the user which path to take:
- **(a)** Full re-analysis (all phases run again)
- **(b)** Just update what changed → tell the user to run `/vault-anything:vault-update` instead, then STOP.
- **(c)** Skip scan + analyze, regenerate vault from existing `raw/` (jump to Phase 3).
- If `$EXISTS` is `"false"`, proceed with full analysis.
### Set up progress tracking
Before running the phases, use the **TaskCreate** tool to create a top-level task list the user can watch. The tasks are:
1. Phase 1: Scan project files
2. Phase 2: Analyze files
3. Phase 3: Cluster concepts + write skeleton
4. Phase 4: Write pages
5. Phase 5: Quality review
If the user picked option (c) above (regenerate from existing raw/), mark tasks 1 and 2 as `completed` immediately (they're being skipped, but recorded as already done so the user sees what's happening).
At the start of every subsequent phase, call **TaskUpdate** to mark the task `in_progress`. At the end of each phase, mark it `completed`.
---
## Phase 1 — SCAN
Mark "Phase 1" as `in_progress`. Report to the user: `[Phase 1/5] Scanning project files...`
Run the analyzer script. It scans files, detects frameworks and entry points deterministically, builds an import map, and produces batch plans for the file-analyzer agents:
```bash
SCAN=$(node "$PLUGIN_ROOT/skills/vault/run-analyze.mjs" \
"$PROJECT_DIR" "$RAW_DIR" "$PROJECT_NAME")
```
Extract counts to report to the user:
```bash
FILE_COUNT=$(echo "$SCAN" | jq -r '.files')
LANGUAGES=$(echo "$SCAN" | jq -r '.languages | join(", ")')
BATCH_COUNT=$(echo "$SCAN" | jq -r '.batches | length')
```
Check whether a commit hash was captured:
```bash
COMMIT_HASH=$(echo "$SCAN" | jq -r '.commitHash')
SUBREPO_COUNT=$(echo "$SCAN" | jq -r '.subRepos | length')
```
If `$COMMIT_HASH` is the string `"null"`, the root isn't under git (or has no commits yet). This is fine — analysis proceeds — but mention it to the user, and distinguish the multi-project case:
- If `$SUBREPO_COUNT` is greater than 0, the root is a parent directory holding several git repos (a multi-project run). List them from `echo "$SCAN" | jq -r '.subRepos[].name'` and print:
```
Note: The workspace root isn't a git repo, but <SUBREPO_COUNT> sub-repositories
were detected (<names>). Their HEADs are recorded for reference, but per-project
incremental code-change detection isn't supported yet (planned for multi-project
support). For now, re-running regenerates from scratch; static-file changes in
raw/ are still tracked.
```
- Otherwise (no sub-repos either) print:
```
Note: This project is not under git. Code change detection won't be available
for incremental updates; only static-file changes in raw/ will be tracked.
```
Now print a short plan summary for the user:
```
Plan
Project: <PROJECT_NAME> (<FILE_COUNT> files, languages: <LANGUAGES>)
Workspace: <WORKSPACE_DIR>
Will dispatch <BATCH_COUNT> file-analyzer agent(s) and (after clustering) one page-writer agent per category.
```
If `$FILE_COUNT` is greater than 200, warn the user this may take a while and ask them to confirm before continuing.
Mark "Phase 1" as `completed`.
---
## Phase 2 — ANALYZE FILES
Mark "Phase 2" as `in_progress`. Report: `[Phase 2/5] Analyzing files...`
For each batch (`batch-1`, `batch-2`, ...) add a sub-task under "Phase 2" so the user can watch individual batches complete. Use TaskCreate with `parentId` set to the Phase 2 task id, or use a single TaskUpdate that lists batch progress in the description — whichever your runtime supports.
For each batch in `$SCAN.batches`, dispatch a `vault-anything:file-analyzer` subagent **via the Agent tool**. Run up to 5 agents concurrently — in a single message with multiple Agent tool calls.
For each batch, the Agent tool call uses:
- `subagent_type`: `vault-anything:file-analyzer`
- `description`: `Analyze batch <batchIndex>/<totalBatches>`
- `prompt`: described below
Iterate the batches with jq to get each batch's data:
```bash
echo "$SCAN" | jq -c '.batches[]'
```
For each batch, build the agent prompt. Substitute the batch-specific values into this template:
> **Inputs:**
> - projectRoot: `<PROJECT_DIR>`
> - projectName: `<PROJECT_NAME>`
> - batchIndex: `<batch.batchIndex>`
> - totalBatches: `<total>`
> - outputPath: `<RAW_DIR>/<PROJECT_NAME>/file-summaries/batch-<batchIndex>.json`
>
> **Files to analyze** (read each one and produce a `FileSummary`):
> ```json
> <batch.files>
> ```
>
> **Intra-batch imports** (project-internal imports between files in this batch):
> ```json
> <batch.importData>
> ```
>
> **Cross-batch imports** (files outside this batch that are referenced — for context, do not re-analyze):
> ```json
> <batch.crossBatchImports>
> ```
>
> Write a JSON array of `FileSummary` objects to `outputPath`. Follow the schema and rules in your agent definition.
As each batch's subagent completes, mark its sub-task `completed`. After all batches complete, mark "Phase 2" as `completed`. Report: `Phase 2 complete. <FILE_COUNT> files analyzed.`
---
## Phase 3 — CLUSTER & GENERATE SKELETON
Mark "Phase 3" as `in_progress`. Report: `[Phase 3/5] Clustering concepts and generating vault skeleton...`
```bash
GEN=$(node "$PLUGIN_ROOT/skills/vault/run-generate.mjs" \
"$RAW_DIR" "$VAULT_DIR" "$PROJECT_NAME")
```
Extract counts:
```bash
PAGE_COUNT=$(echo "$GEN" | jq -r '.pagesWritten')
CATEGORIES=$(echo "$GEN" | jq -r '.categories | join(", ")')
USER_FILES=$(echo "$GEN" | jq -r '.userFiles | length')
```
If `$USER_FILES` is greater than 0, report: `Note: <USER_FILES> static file(s) from raw/ will be included.`
Report: `<PAGE_COUNT> pages planned across categories: <CATEGORIES>`
Mark "Phase 3" as `completed`.
---
## Phase 4 — WRITE PAGES
Mark "Phase 4" as `in_progress`. Report: `[Phase 4/5] Writing vault pages...`
For each category in `$GEN.categories`, add a sub-task under "Phase 4" so the user can track progress per category (e.g. "Phase 4: write 02-Architecture (3 pages)").
Load the locale guide and the type templates. If the user asked for a language that isn't bundled, fall back to English and warn them:
```bash
LOCALE_FILE="$PLUGIN_ROOT/locales/$VAULT_LANG.md"
if [ ! -f "$LOCALE_FILE" ]; then
echo "Note: no locale guide for '$VAULT_LANG' at $LOCALE_FILE — falling back to English." >&2
VAULT_LANG="en"
LOCALE_FILE="$PLUGIN_ROOT/locales/en.md"
fi
LOCALE_GUIDE=$(cat "$LOCALE_FILE")
# Load all type templates (map page type → template content)
TPL_FLOW=$(cat "$PLUGIN_ROOT/templates/flow.md")
TPL_CONCEPT=$(cat "$PLUGIN_ROOT/templates/concept.md")
TPL_CROSS=$(cat "$PLUGIN_ROOT/templates/cross-cutting.md")
TPL_REFERENCE=$(cat "$PLUGIN_ROOT/templates/reference.md")
TPL_SERVICE=$(cat "$PLUGIN_ROOT/templates/service-overview.md")
TPL_API=$(cat "$PLUGIN_ROOT/templates/api.md") # used by type: api_index
TPL_TOPIC=$(cat "$PLUGIN_ROOT/templates/topic.md") # used by type: topic_index
TPL_CONFIG=$(cat "$PLUGIN_ROOT/templates/config.md") # used by type: config_index
TPL_DATA_MODEL=$(cat "$PLUGIN_ROOT/templates/data-model.md") # used by type: data_model_index
TPL_INTEGRATIONS=$(cat "$PLUGIN_ROOT/templates/integrations.md") # used by type: integration_index
TPL_JOBS=$(cat "$PLUGIN_ROOT/templates/jobs.md") # used by type: job_index
TPL_PERMISSIONS=$(cat "$PLUGIN_ROOT/templates/permissions.md") # used by type: permission_index
```
For each category in `$GEN.categories`, dispatch one `vault-anything:page-writer` subagent **via the Agent tool**. Run all categories concurrently — in a single message with multiple Agent tool calls.
For each category, get the pages that belong to it:
```bash
echo "$GEN" | jq -c --arg CAT "$CATEGORY" '.pages[] | select(.category == $CAT)'
```
Build the agent prompt with these substitutions. Include only the templates for page types that actually appear in this category's pages (most categories use exactly one type — flows use `flow`, architecture uses `concept`, etc.):
> **Inputs:**
> - category: `<CATEGORY>`
> - vaultDir: `<VAULT_DIR>/<CATEGORY>/`
> - outputLanguage: `<VAULT_LANG>`
>
> **Templates for the page types in this category:**
>
> For type `<type>`:
> ```
> <template content for this type, e.g. $TPL_CONCEPT>
> ```
>
> (Include one template block per unique page type present in this category's pages.)
>
> **Locale guide:**
> ```
> <LOCALE_GUIDE>
> ```
>
> **wikilinkMap** (use these page titles for [[wikilinks]]):
> ```json
> <GEN.wikilinkMap>
> ```
>
> **Pages to write** (skeleton files already exist at vaultDir — read them for context, then overwrite with finished prose):
> ```json
> <pages for this category>
> ```
>
> **User-provided static files** that contribute to this category (read these for additional context):
> ```json
> <userFiles relevant to this category, or empty array>
> ```
>
> Follow the writing rules in your agent definition and the section structure in the template for each page's type. Produce one finished markdown page per entry in `pages`.
As each category's subagent completes, mark its sub-task `completed`. After all category agents finish, mark "Phase 4" as `completed`. Report: `Phase 4 complete.`
---
## Phase 5 — REVIEW
Mark "Phase 5" as `in_progress`. Report: `[Phase 5/5] Running quality checks...`
```bash
REVIEW=$(node "$PLUGIN_ROOT/skills/vault/run-review.mjs" "$VAULT_DIR" "$RAW_DIR")
SCORE=$(echo "$REVIEW" | jq -r '.qualityScore')
ISSUES=$(echo "$REVIEW" | jq -r '.issues | length')
WARNINGS=$(echo "$REVIEW" | jq -r '.warnings | length')
```
Report `Quality score: <SCORE>/100`.
If `$ISSUES` is greater than 0:
1. List up to 5 of them by reading `$REVIEW | jq '.issues'`.
2. Attempt automatic fixes (strip backticks, add missing front matter fields).
3. Re-run the review once to confirm.
If `$WARNINGS` is greater than 0, list the top 5 (informational only — does not block).
Mark "Phase 5" as `completed`.
---
## Final Report
Output to the user:
```
✓ Vault generated
Project: <PROJECT_NAME>
Workspace: <WORKSPACE_DIR>
Pages: <PAGE_COUNT> across <CATEGORIES>
Quality: <SCORE>/100
Open in Obsidian → <VAULT_DIR>
Run updates → /vault-anything:vault-update <PROJECT_DIR>
```
### Versioning suggestion
If the workspace is inside the project directory (default `<project>/vault-anything/`), suggest the user version it. Print:
```
Versioning vault-anything:
The vault and its raw layer are designed to be committed alongside your code,
so your team shares the same documentation. Add this single line to your
project's .gitignore:
vault-anything/raw/vault-review.json
(vault-review.json changes on every run and is noise; everything else is
meant to be committed.)
```
Only print this if (a) the workspace lives inside the project directory and
(b) the project has a `.git` directory. Skip it for separate-workspace setups.
**Multi-project case:** if the root has no `.git` of its own but `$SUBREPO_COUNT`
(from Phase 1) is greater than 0, the combined vault can't be committed to the
parent because it isn't a repo. Print this instead:
```
Versioning this multi-project vault:
The workspace root isn't a git repo, so the combined vault has nowhere to be
committed. Two options:
- git init the parent directory and commit the vault there, or
- re-run with --workspace pointing at a dedicated docs/wiki git repo.
vault-update tracks each sub-repo's HEAD independently, so incremental
updates work either way.
```