Incrementally update an existing vault — detect code changes via git diff and static file changes via SHA256, then re-analyze and rewrite only affected pages
Installs into .claude/skills of the current project.
Are you the author of Vault Update?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/burakolgun-vault-update)
---
name: vault-update
description: Incrementally update an existing vault — detect code changes via git diff and static file changes via SHA256, then re-analyze and rewrite only affected pages
argument-hint: "[project-dir] [--workspace <dir>]"
---
# /vault-anything:vault-update
Incrementally updates an existing vault. Detects code changes via `git diff` and static-file changes via SHA256 hash, then re-analyzes and rewrites only the pages that need to change.
## Arguments
| Argument | Default | Description |
|----------|---------|-------------|
| `project-dir` | current directory | Project being updated |
| `--workspace` | `<project>/vault-anything/` | Where existing `raw/` and `vault/` live |
---
## Phase 0 — Pre-flight
```bash
set -e
PLUGIN_ROOT=$(node "$CLAUDE_SKILL_DIR/../vault/resolve-plugin-root.mjs")
WORKSPACE=$(node "$PLUGIN_ROOT/skills/vault/resolve-workspace.mjs" "$@")
PROJECT_DIR=$(echo "$WORKSPACE" | jq -r '.projectDir')
PROJECT_NAME=$(echo "$WORKSPACE" | jq -r '.projectName')
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 if provided
```
If `$EXISTS` is `"false"`:
- Tell the user: `No vault found. Run /vault-anything:vault <project-dir> first.`
- STOP.
### Set up progress tracking
Before running the phases, use the **TaskCreate** tool to create the task list:
1. Phase 1: Detect changes
2. Phase 2: Re-analyze changed code files
3. Phase 3: Route new static files
4. Phase 4: Regenerate affected pages
5. Phase 5: Review and save
Note that phases 2-4 are **conditional**:
- Phase 2 is skipped if there are no code changes.
- Phase 3 is skipped if there are no new static files in `raw/`.
- Phase 4 is skipped if no vault pages are affected.
For each skipped phase, mark its task as `cancelled` (not completed) so the user can see what was skipped.
Mark each running phase `in_progress` when it starts, `completed` when it finishes.
---
## Phase 1 — DETECT CHANGES
Mark "Phase 1" as `in_progress`.
```bash
PLAN=$(node "$PLUGIN_ROOT/skills/vault/run-update.mjs" \
"$PROJECT_DIR" "$RAW_DIR" "$PROJECT_NAME")
CODE_CHANGED=$(echo "$PLAN" | jq -r '.codeChanges.hasChanges')
RAW_CHANGED=$(echo "$PLAN" | jq -r '.rawChanges.hasChanges')
FULL_REANALYZE=$(echo "$PLAN" | jq -r '.codeChanges.fullReanalyze')
FULL_GENERATE=$(echo "$PLAN" | jq -r '.requiresFullGenerate')
```
If both `$CODE_CHANGED` and `$RAW_CHANGED` are `"false"`:
- Mark "Phase 1" as `completed` and mark phases 2-5 as `cancelled`.
- Report `✓ Vault is up to date.` and STOP.
Otherwise, summarize the changes:
```bash
CHANGED_FILES=$(echo "$PLAN" | jq -r '.codeChanges.changedFiles | length')
ADDED=$(echo "$PLAN" | jq -r '.rawChanges.added | length')
CHANGED=$(echo "$PLAN" | jq -r '.rawChanges.changed | length')
REMOVED=$(echo "$PLAN" | jq -r '.rawChanges.removed | length')
AFFECTED=$(echo "$PLAN" | jq -r '.affectedVaultPages | length')
```
Report to the user:
```
Changes detected:
Code: <CHANGED_FILES> file(s) changed
Raw: <ADDED> new, <CHANGED> changed, <REMOVED> removed
Vault pages affected: <AFFECTED>
```
Ask the user to confirm before proceeding. Mark "Phase 1" as `completed`.
---
## Phase 2 — RE-ANALYZE CHANGED CODE FILES
If `$CODE_CHANGED` is `"false"`, mark "Phase 2" as `cancelled` and skip to Phase 3.
Otherwise mark "Phase 2" as `in_progress`.
If `$FULL_REANALYZE` is `"true"` (the saved git hash is invalid), restart by invoking `/vault-anything:vault` for a full rebuild, then STOP.
Otherwise, dispatch a single `vault-anything:file-analyzer` subagent via the Agent tool. The prompt should contain:
> **Task:** Re-analyze only the changed files. Match by `filePath` and overwrite any existing summaries for those files.
>
> **Inputs:**
> - projectRoot: `<PROJECT_DIR>`
> - projectName: `<PROJECT_NAME>`
> - outputPath: `<RAW_DIR>/<PROJECT_NAME>/file-summaries/batch-update.json`
>
> **Changed files to re-analyze:**
> ```json
> <changedFiles from PLAN>
> ```
>
> Follow the schema and rules in your agent definition.
---
## Phase 3 — ROUTE NEW STATIC FILES
If `.rawChanges.added | length` is `0`, mark "Phase 3" as `cancelled` and skip to Phase 4.
Otherwise mark "Phase 3" as `in_progress`.
For each new file in `$PLAN.rawChanges.added`, you need to decide which vault pages it contributes to. List the existing vault page titles for the agent:
```bash
PAGE_TITLES=$(find "$VAULT_DIR" -name "*.md" -not -name "index.md" \
-exec basename {} .md \; | jq -R . | jq -s .)
```
For each new file, dispatch a lightweight subagent (the general-purpose Agent works fine here) with this prompt:
> Read this static document and decide which vault pages it contributes to.
>
> **File path:** `<RAW_DIR>/<relPath>`
> **File content:**
> ```
> <read the file>
> ```
>
> **Available vault page titles:**
> ```json
> <PAGE_TITLES>
> ```
>
> Return JSON only: `{ "affectsPages": ["Page Title 1", "Page Title 2"] }`
> Choose at most 5 pages. If none are relevant, return `{ "affectsPages": [] }`.
After the agent responds with `affectsPages`, update the manifest. Run this Node snippet, substituting the file's `relPath`, `hash`, and the `affectsPages` array:
```bash
node -e "
import('$PLUGIN_ROOT/packages/core/src/write-raw.mjs').then(({ readMeta, updateRawManifest }) => {
const meta = readMeta('$RAW_DIR');
updateRawManifest('$RAW_DIR', meta, '<relPath>', '<hash>', <affectsPages JSON>);
});
"
```
When all new files have been routed, mark "Phase 3" as `completed`.
---
## Phase 4 — REGENERATE AFFECTED PAGES
Collect all pages to regenerate:
- `$PLAN.affectedVaultPages` (pages affected by changed code or removed static files)
- Pages from the `affectsPages` lists of any new/changed static files (Phase 3)
If the affected page set is empty, mark "Phase 4" as `cancelled` and skip to Phase 5.
Otherwise mark "Phase 4" as `in_progress`.
Compute the ratio of affected pages to total pages:
```bash
TOTAL=$(find "$VAULT_DIR" -name "*.md" -not -name "index.md" | wc -l)
AFFECTED_COUNT=<computed>
RATIO=$(( AFFECTED_COUNT * 100 / TOTAL ))
```
If `$RATIO` is greater than 80, or `$FULL_GENERATE` is `"true"`, restart by invoking `/vault-anything:vault` to do a full rebuild and STOP.
Otherwise, load the locale guide and type templates. Fall back to English if the requested locale guide is missing:
```bash
LOCALE_FILE="$PLUGIN_ROOT/locales/$VAULT_LANG.md"
if [ ! -f "$LOCALE_FILE" ]; then
echo "Note: no locale guide for '$VAULT_LANG' — falling back to English." >&2
VAULT_LANG="en"
LOCALE_FILE="$PLUGIN_ROOT/locales/en.md"
fi
LOCALE_GUIDE=$(cat "$LOCALE_FILE")
PAGE_PLAN=$(cat "$RAW_DIR/$PROJECT_NAME/page-plan.json")
WIKILINK_MAP=$(echo "$PAGE_PLAN" | jq -r '.wikilinkMap')
# Load templates for each page type
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 affected page, dispatch a `vault-anything:page-writer` subagent via the Agent tool. Run them concurrently (multiple Agent tool calls in a single message).
For each affected page, the agent prompt should contain:
> **Task:** Rewrite this vault page with updated content.
>
> **Page path:** `<VAULT_DIR>/<category>/<page>.md`
> **Page type:** `<type>` (look up the type from the page's existing front matter)
>
> **Template for this page type:**
> ```
> <template content for the page's type, e.g. $TPL_CONCEPT>
> ```
>
> **Current page content** (for continuity — preserve good content where possible):
> ```
> <read the existing page>
> ```
>
> **Updated source summaries** (from re-analyzed files relevant to this page):
> ```json
> <summaries for this page>
> ```
>
> **New static content contributing to this page** (if any):
> ```
> <content from changed/new raw files affecting this page>
> ```
>
> **Locale guide:**
> ```
> <LOCALE_GUIDE>
> ```
>
> **wikilinkMap:** `<WIKILINK_MAP>`
>
> Follow your agent definition's writing rules and the section structure in the template. Overwrite the page at `vaultDir`.
When all affected pages have been rewritten, mark "Phase 4" as `completed`.
---
## Phase 5 — REVIEW & SAVE
Mark "Phase 5" as `in_progress`.
```bash
node "$PLUGIN_ROOT/skills/vault/run-review.mjs" "$VAULT_DIR" "$RAW_DIR"
```
Update the baseline in `meta.json` so the next run diffs against this state. For a single git repo this records the current commit hash; for a multi-project parent (root not a git repo) it records each sub-repo's current HEAD:
```bash
node -e "
Promise.all([
import('$PLUGIN_ROOT/packages/core/src/write-raw.mjs'),
import('$PLUGIN_ROOT/packages/core/src/diff-update.mjs'),
]).then(([raw, diff]) => {
const hash = diff.getCurrentHash('$PROJECT_DIR');
const subRepos = hash ? [] : diff.detectSubRepos('$PROJECT_DIR');
if (hash || subRepos.length) raw.updateProjectMeta('$RAW_DIR', '$PROJECT_NAME', hash, subRepos);
});
"
```
Mark "Phase 5" as `completed`.
Report: `✓ Vault updated. <AFFECTED_COUNT> pages regenerated. Quality: <SCORE>/100`