Use when checking or upgrading one luna-second-brain vault to the current himmel template.
Install
npx skillscat add yotamleo-org/himmel/luna-upgrade Install via the SkillsCat registry.
luna-upgrade — content-preserving vault/template upgrade (HIMMEL-389)
You bring an EXISTING luna-second-brain vault up to the current himmel template
without clobbering the user's own content. Vaults are scaffolded once and never
re-read the template; this is the upgrade path. The engine that does all the
work is templates/luna-second-brain/scripts/upgrade.sh — this skill is the
ergonomic dry-run → confirm → apply surface around it. ALL classification,
3-way merge, conflict handling, and fail-closed recovery logic lives inupgrade.sh; this runbook only orchestrates the preview/confirm/apply sequence
and surfaces the engine's output. Do NOT reimplement any merge logic here.
Invocation surfaces
- User prompt:
/luna-upgrade [--vault <path>] [--template-dir <path>]—
handled by the thin wrapper at.claude/commands/luna-upgrade.mdwhich
delegates here. - Programmatic Skill-tool dispatch (HIMMEL-128 compliant — no headless
claude):Skill { skill: "obsidian-triage:luna-upgrade", args: "<flags...>" }.
Wherever this document references $ARGUMENTS, treat it as the literal arg
string supplied via either path.
Inputs
$ARGUMENTS is [--check] [--vault <path>] [--template-dir <path>] (all optional).
--check— report whether an upgrade is available and stop (no plan, no
confirm, no changes). See "Check-only mode" below.--vault <path>— the vault to upgrade. Default: resolve from the current
session (see Step 1).--template-dir <path>— explicit himmel template root override. Default:
resolve the himmel checkout (see Step 0). The engine's own resolver order is--template-dir>$HIMMEL_DIR> generic$HOME-relative candidate paths >
sibling scan, so on a normal layout you pass neither flag.
Check-only mode (--check)
When --check is in $ARGUMENTS, do Steps 0 and 1 (locate himmel + the vault),
then run the engine in check mode and surface its single-line result verbatim —
do NOT run the dry-run/confirm/apply sequence:
bash "$UPGRADE" --template-dir "$TEMPLATE" --vault-dir "$VAULT" --checkIt prints either luna-second-brain: template vX.Y.Z available … (an upgrade
exists — tell the operator they can run /luna-upgrade to apply it) orluna-second-brain: vault is current …. It changes nothing and exits 0. STOP
after surfacing the line.
Step 0 — Locate himmel's upgrade.sh
A vault scaffolded BEFORE this feature has no scripts/upgrade.sh of its own
yet, so always invoke himmel's copy (the first upgrade then installsupgrade.sh into the vault — it is overwrite-class). Resolve the himmel
checkout the same way the engine resolves the template (explicit config wins,
then generic home-relative candidates):
HIMMEL=""
for d in "${HIMMEL_DIR:-}" "$HOME/github/himmel" "$HOME/github/Himmel" \
"$HOME/Documents/github/himmel" "$HOME/Documents/github/Himmel"; do
[ -n "$d" ] && [ -f "$d/templates/luna-second-brain/scripts/upgrade.sh" ] \
&& { HIMMEL="$d"; break; }
doneIf --template-dir <path> was passed, set HIMMEL so that$HIMMEL/templates/luna-second-brain equals that path (or pass the flag
straight through). If HIMMEL stays empty, stop and tell the operator to setHIMMEL_DIR or pass --template-dir — do NOT guess a path. Define:
TEMPLATE="$HIMMEL/templates/luna-second-brain"
UPGRADE="$TEMPLATE/scripts/upgrade.sh"This probe is best-effort and only locates upgrade.sh; the engine re-resolves
and re-validates the template itself (and warns if multiple checkouts match), so
the engine's resolution is authoritative — do not treat this snippet's result as
final.
Step 1 — Resolve the vault root
If --vault <path> was passed, use it. Otherwise resolve the current session's
vault: walk up from $PWD to the nearest ancestor that contains a .obsidian/
directory or a .vault-template.json stamp; if none is found, use $PWD.
Confirm the resolved $VAULT looks like a vault (it has .obsidian/ or.vault-template.json); if it clearly is not one, stop and ask the operator to
pass --vault.
Step 2 — Dry-run (preview)
Run the engine in preview mode — it touches nothing:
bash "$UPGRADE" --template-dir "$TEMPLATE" --vault-dir "$VAULT" --dry-runStep 3 — Surface the plan
Show the operator the engine's plan output verbatim (which files WRITE /
WRITE-NEW / MERGE-JSON / MERGE-3WAY / REPORT, and the template-vs-vault
versions). Call out explicitly if the plan includes:
- a
MERGE-3WAY _CLAUDE.mdline (their operating manual may merge or conflict), or - a changed
.obsidian/PLUGINS-SETUP.md(the manual-install list reprints on apply).
If the dry-run reports the vault is already current (exit 0, "already current"),
say so and STOP — nothing to do.
Step 4 — Confirm
Ask the operator to confirm they want to apply this plan to $VAULT. Wait for
an explicit yes. If they decline, STOP — make no changes.
Step 5 — Apply
On confirmation, re-run the engine with --yes (same resolution, no prompt):
bash "$UPGRADE" --template-dir "$TEMPLATE" --vault-dir "$VAULT" --yesStep 6 — Surface the result
Report the engine's outcome, preserving its loud signals:
- Success (exit 0): "Upgrade complete — vault is now vX.Y.Z."
_CLAUDE.mdconflict / write failure (non-zero exit): the engine did NOT
write the version stamp (fail-closed) and printed the reason. Surface that
loud alert verbatim — on a_CLAUDE.mdconflict the original is kept and the
conflicted 3-way merge is in_CLAUDE.md.template-merge; the operator
resolves it by hand, deletes the sidecar, and re-runs (writes are idempotent).- PLUGINS-SETUP reprint: if the engine reprinted the manual-install table,
pass it through so the operator can install/update the AGPL/proprietary
plugins (Charts, Templater, …) that cannot be bundled.
Do NOT attempt to resolve a conflict or re-run automatically — the
content-preserving contract is the engine's; your job is to surface its result.
Step 7 — Offer to commit (single-writer vaults only)
upgrade.sh writes template files but never commits — and the vault'svault-autosync.sh is opt-in (default OFF) and wired to no trigger, so a fresh
upgrade leaves a dirty working tree that silently never lands (the "N uncommitted
changes after /luna-upgrade" surprise). On a successful apply (exit 0), close
that gap:
# only if the vault commits straight to main by design AND has pending changes
[ -f "$VAULT/.single-writer" ] && [ -n "$(git -C "$VAULT" status --porcelain)" ] && echo offerIf both hold, OFFER to commit (operator confirms — do not auto-commit):
git -C "$VAULT" add -A && git -C "$VAULT" commit -m "chore: luna vault upgrade → template vX.Y.Z"If the vault is not single-writer (no .single-writer), do NOT offer — those
vaults gate vault writes through a PR lane; just tell the operator the upgrade
left uncommitted changes to review. (/himmel-doctor C3 is the standing backstop
that flags a dirty single-writer vault later.)
Exit codes (from the engine)
0— applied (or dry-run completed, or already current).1— a partial upgrade: write failure or_CLAUDE.mdconflict — stamp NOT
written, re-run after resolving.3— needs reconcile: every other file was applied, but local edits were
withheld (no write failure) — stamp NOT written. Stdout carries one stableupgrade: NEEDS-RECONCILE — …line. Reconcile each withheld file: take the
template copy, or re-run with--keep <rel>(repeatable, HIMMEL-3406) to
explicitly keep the vault's content — this records which template version
the decision was made against, so a later run that finds the template
unchanged for that file does not re-prompt, but a later run where the
template changes it again does. Then re-run. A run that also has a write
failure or_CLAUDE.mdconflict is still1.2— env/usage error (e.g. template not located, vault dir missing, unknown
flag, unreadable marketplace.json, missing python3/git/sha256sum). On "could
not locate the himmel template", setHIMMEL_DIRor pass--template-dir.
Notes
- The build of this skill was autonomous; its USE is interactive (the confirm
in Step 4 is the load-bearing safety gate). - Never edit user content. The engine only ever enumerates template-owned files;
this skill never writes vault files directly.