Help us improve
Share bugs, ideas, or general feedback.
From ark-skills
Interactive setup wizard — greenfield, vault migration, partial repair. Absorbs /wiki-setup.
npx claudepluginhub helloworldsungin/ark-skills --plugin ark-skillsHow this skill is triggered — by the user, by Claude, or both
Slash command
/ark-skills:ark-onboardThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Interactive setup wizard for new Ark projects. Detects project state (greenfield, migration, repair, healthy) and walks through setup at 3 tiers (Quick, Standard, Full). Absorbs all `/wiki-setup` functionality — single entry point for new project onboarding.
Guides technical evaluation of code review feedback: read fully, restate for understanding, verify against codebase, respond with reasoning or pushback before implementing.
Share bugs, ideas, or general feedback.
Interactive setup wizard for new Ark projects. Detects project state (greenfield, migration, repair, healthy) and walks through setup at 3 tiers (Quick, Standard, Full). Absorbs all /wiki-setup functionality — single entry point for new project onboarding.
Reference material (loaded on-demand):
references/templates.md — all file/config templates (shell, JSON, Python, markdown). Used by every path that writes a file.references/externalize-vault-plan.md — the Externalization plan-file template (199 LOC). Used by the Externalization path only.references/state-detection.md — project state detection bash + flag derivation. Used at wizard entry.references/plugin-install.md — Obsidian plugin download + fallback bash. Used by Greenfield Step 11 / Repair Check 12.references/centralized-vault-repair.md — centralized-vault repair scenarios (broken symlink, drift, missing script, missing hook). Used by Repair path.Exempt from normal context-discovery — must work when CLAUDE.md is missing, broken, or incomplete. When CLAUDE.md is absent, detect project state from the filesystem and route to the appropriate path (greenfield if no vault, partial/migration if a vault directory is found). Never abort because CLAUDE.md is missing — that's one of the states this skill is designed to handle.
| Term | Meaning | Example |
|---|---|---|
| Vault root | Top-level directory containing all vault content | vault/ |
| Project docs path | Subdirectory for project-specific knowledge (may equal vault root for standalone) | vault/Trading-Signal-AI/ |
| TaskNotes path | Sibling of project docs under vault root, never nested | vault/TaskNotes/ |
Path layout:
vault/ # {vault_root}
├── Trading-Signal-AI/ # {project_docs_path}
│ ├── Session-Logs/
│ └── ...
└── TaskNotes/ # {tasknotes_path} — sibling, NOT nested
├── Tasks/
├── Archive/
└── meta/ArkSignal-counter
New projects default to a centralized vault: the vault lives in its own git repo at ~/.superset/vaults/<project>/ (or ~/Vaults/<project>/ for non-superset users), and the project repo contains a vault symlink. Mirrors ArkNode-Poly's production pattern. Benefits:
obsidian-cli agrees with the agent.| Term | Meaning | Example |
|---|---|---|
<vault_repo_path> | Absolute path to the centralized vault repo | ~/.superset/vaults/ArkNode-Poly |
<project_repo> | The project repo that symlinks into the vault | ~/.superset/projects/ArkNode-Poly |
<common_git_dir> | Output of git rev-parse --git-common-dir (shared across worktrees) | ~/.superset/projects/ArkNode-Poly/.git |
if [ -d "$HOME/.superset" ]; then
DEFAULT_VAULT_REPO_PATH="$HOME/.superset/vaults/<project>"
else
DEFAULT_VAULT_REPO_PATH="$HOME/Vaults/<project>"
fi
$HOME-portableThe chosen vault path must start with $HOME/ (or ~/, normalized to $HOME/). Reject absolute paths outside $HOME (e.g., /Volumes/..., /mnt/..., another user's home). Users wanting an external drive should ln -s /Volumes/ExternalDrive/vaults $HOME/Vaults and point the wizard at the $HOME path. Keeps tracked metadata portable across machines by construction.
When writing VAULT_TARGET into the tracked setup script, always store the $HOME/-prefixed literal form (e.g., VAULT_TARGET="$HOME/.superset/vaults/ArkNode-Poly"), not the expanded path. $HOME expands at runtime on whichever machine runs the script.
<vault_repo_path>/ (centralized vault — its own git repo)
├── .git/
├── .obsidian/
├── .notebooklm/
│ ├── config.json (vault_root: ".", tracked)
│ └── sync-state.json (empty init, tracked)
├── _meta/, _Templates/, _Attachments/
├── TaskNotes/
├── 00-Home.md
└── <ProjectDocs>/ (monorepo layout) OR flat (standalone)
<project_repo>/
├── vault → <vault_repo_path>/ (symlink, git-ignored)
├── .notebooklm/config.json (monorepo only: vault_root: "vault", tracked.
│ Standalone vaults skip this file — the
│ vault-side config is authoritative.)
├── .gitignore (contains `vault`)
├── <common_git_dir>/hooks/post-checkout (installed, not tracked)
├── .superset/config.json (optional — if already present)
└── scripts/setup-vault-symlink.sh (tracked; canonical source — contains VAULT_TARGET with $HOME)
Users who explicitly want the vault committed to the project repo can skip the centralized layout. When they do, the wizard writes this row into CLAUDE.md's Project Configuration table:
| **Vault layout** | embedded (not symlinked) |
Check #20 (vault-externalized) reads this row: presence of embedded (case-insensitive) in the Vault layout row means opt-in to embedded — check #20 returns pass.
All file templates for this section live in references/templates.md:
scripts/setup-vault-symlink.sh — the canonical script (has VAULT_TARGET="$HOME/..." grep contract: ^VAULT_TARGET="[^"]*"\s*$, value must begin with $HOME/).git/hooks/post-checkout — the hook (install at $(git rev-parse --git-common-dir)/hooks/post-checkout, not .git/hooks/ — worktrees share the main repo's hooks via commondir).superset/config.json merge — append-only merge via Python (preserves existing entries)Vault repo .gitignoreFour user-provided fields. Everything else derives:
| Field | Format | Example |
|---|---|---|
| Project name | Any string | trading-signal-ai |
| Vault root | Path ending with / | vault/ |
| Task prefix | Ends with - | ArkSignal- |
| TaskNotes path | Path ending with / | vault/TaskNotes/ |
Derived:
{tasknotes_path}/meta/{task_prefix}counter{vault_root} for standalone layoutsSync note:
/ark-healthis the authoritative source for all 22 check definitions. This summary exists so/ark-onboardcan run diagnostics without invoking a separate skill. If this drifts from/ark-health, that skill is correct.
Summary of the 22 checks (pass conditions; full semantics + bash in /ark-health):
| # | Check | Tier | Pass |
|---|---|---|---|
| 1 | superpowers plugin | Critical | Any superpowers:* skill in session |
| 2 | gstack plugin | Standard | Any gstack skill (browse, qa, ship, review) in session |
| 3 | obsidian plugin | Standard | obsidian:obsidian-cli in session |
| 4 | CLAUDE.md exists | Critical | File exists in project root |
| 5 | CLAUDE.md required fields | Critical | All 4 fields present/non-empty |
| 6 | Task prefix format | Critical | Prefix ends with -, counter file exists |
| 7 | Vault directory exists | Critical | Vault root path resolves |
| 8 | Vault structure | Critical | _meta/, _Templates/, TaskNotes/ exist; plus 00-Home.md (standalone) or project docs subdir (monorepo) |
| 9 | Python 3.10+ | Critical | python3 --version ≥ 3.10 |
| 10 | Index status | Standard | index.md exists (staleness = warn, not fail) |
| 11 | Task counter | Standard | Counter file exists, valid integer |
| 12 | Obsidian vault plugins | Standard | tasknotes/main.js + obsidian-git/main.js in .obsidian/plugins/ |
| 13 | TaskNotes MCP | Standard | mcpServers.tasknotes in .mcp.json (config-only — Obsidian must be running for endpoint to respond) |
| 14 | MemPalace installed | Full | mempalace CLI on PATH |
| 14a | MemPalace plugin installed | Full (warn-only) | mempalace@mempalace plugin present at user scope per claude plugin list |
| 14b | MemPalace MCP reachable | Full (warn-only) | Plugin-declared MCP server discoverable via claude mcp list OR mempalace-mcp shim answers initialize handshake |
| 14c | MemPalace hook state | Full (informational) | Always passes — reports state A (neutralized) or state B (active) |
| 14d | MemPalace palace read sanity | Full (warn-only) | mempalace_search via shim returns valid response (no HNSW segfault) |
| 15 | MemPalace wing indexed | Full | mempalace status shows wing for project |
| 16 | History auto-index hook | Full | ~/.claude/hooks/ark-history-hook.sh exists AND registered in .claude/settings.json |
| 16b | History hook content drift | Full (warn-only) | Installed ~/.claude/hooks/ark-history-hook.sh is byte-identical to plugin's current copy |
| 17 | NotebookLM CLI installed | Full | notebooklm CLI on PATH |
| 18 | NotebookLM config | Full | .notebooklm/config.json with non-empty notebook ID |
| 19 | NotebookLM authenticated | Full | notebooklm auth check --test exits 0 |
| 20 | Vault externalized | Standard (warn-only) | Symlink matches script VAULT_TARGET, OR Vault layout: embedded opt-out |
| 21 | OMC plugin | Standard (tier-agnostic) | omc on PATH or ~/.claude/plugins/cache/omc/ exists; ARK_SKIP_OMC=true forces skip |
| 22 | ark-skills version current | Standard (warn-only) | .ark/plugin-version matches $ARK_SKILLS_ROOT/VERSION; .ark/ not gitignored |
Running diagnostics: run all 22 checks in sequence, never abort on failure. For records:
skip — "cannot check — CLAUDE.md missing". Checks 21, 22 are exempt.warn (not fail)warn (never fails)skip — "requires MemPalace plugin (check 14a)"skip — "requires check 16"skip — "requires MemPalace (check 14)"skip — "requires NotebookLM CLI (check 17)"upgradeupgrade if not installed; skip if ARK_SKIP_OMC=true; never fail. Tier-agnostic.Detection logic (full bash + flag derivation in references/state-detection.md):
vault/, docs/vault/, .vault/ — set VAULT_DIR if found._meta/vault-schema.md, _meta/taxonomy.md, index.md, TaskNotes/meta/ (max 4).IS_SYMLINK, SYMLINK_BROKEN, SYMLINK_DRIFT, SCRIPT_EXISTS, EMBEDDED_OPTOUT.| State | Condition | Path |
|---|---|---|
| No Vault | CLAUDE.md missing AND no vault, OR CLAUDE.md present but vault root missing/unresolved | Greenfield |
| Non-Ark Vault | Vault exists but < 3 Ark artifacts present | Migration |
| Partial Ark | ≥ 3 Ark artifacts (or vault exists but CLAUDE.md missing); OR broken symlink; OR symlink drift; OR missing script with live symlink | Repair |
| Healthy | All Critical + Standard checks pass | Report |
Key rules:
SYMLINK_BROKEN OR SYMLINK_DRIFT OR missing script with live symlink → Partial Ark with REPAIR_REASON=centralized-vault-drift (or centralized-vault-script-missing). Routes Partial Ark through the centralized-vault repair subsection FIRST.EMBEDDED_OPTOUT=true → respect opt-out, classify by artifact count only.| Tier | What Gets Set Up | Time |
|---|---|---|
| Quick | CLAUDE.md + vault structure + Python check + index generation | ~5 min |
| Standard | Quick + TaskNotes MCP + Obsidian plugins | ~10 min |
| Full | Standard + MemPalace + history hook + NotebookLM CLI + vault mining | ~25 min |
Present after state detection. Recommend Standard for most users. For Partial Ark (Repair) and Healthy, also offer tier upgrade based on current tier.
Which setup tier would you like?
[Q] Quick — CLAUDE.md, vault structure, index (~5 min)
[S] Standard — Quick + TaskNotes MCP, Obsidian plugins (~10 min) [recommended]
[F] Full — Standard + MemPalace, history hook, NotebookLM (~25 min)
Choose [Q/S/F]:
User runs /ark-onboard
|
v
[1] Check plugins (superpowers, gstack, obsidian)
|
v
[2] Missing critical plugin? --> Show install commands, PAUSE for user to install
Missing standard plugin? --> Note for later, continue
|
v
[3] Run state detection (references/state-detection.md)
|
v
[4] Route by state:
No Vault --> Ask tier --> Greenfield path
Non-Ark Vault --> Ask tier --> Migration path
Partial Ark --> Show failures --> Repair path (+ tier upgrade offer)
Healthy --> Show scorecard --> surface Full-tier upgrades
|
v
[5] Run full 22-check diagnostic
|
v
[6] Show before/after scorecard
|
v
[7] List follow-up reminders
Absorbs all /wiki-setup functionality. Creates a complete Ark project from scratch. 18 steps.
# Is this a git repo?
git rev-parse --git-dir 2>/dev/null && echo "GIT_REPO=yes" || echo "GIT_REPO=no"
# If git repo: is working tree clean?
git diff --quiet 2>/dev/null && git diff --cached --quiet 2>/dev/null && echo "CLEAN=yes" || echo "CLEAN=no — warn user to stash"
# Git user configured?
git config user.name 2>/dev/null && echo "GIT_USER=configured" || echo "GIT_USER=missing — warn user"
If not a git repo, offer git init. If working tree dirty, warn and ask to stash or commit first.
Ask the user 5 prompts with the centralized-vault defaulting behavior:
Prompt 1 — Project name:
Project name? (e.g., my-new-project)
Prompt 2 — Task prefix:
Task prefix? (e.g., ArkNew-) — must end with `-`
Prompt 3 — Vault layout:
Vault layout?
[S] Standalone — flat, project docs at vault root
[M] Monorepo — project docs in a subdirectory under vault root
Choose [S/M]:
Prompt 4 — Centralized vault location:
Compute the default by detecting whether $HOME/.superset exists:
if [ -d "$HOME/.superset" ]; then
DEFAULT_PATH="\$HOME/.superset/vaults/<project>"
else
DEFAULT_PATH="\$HOME/Vaults/<project>"
fi
Substitute <project> with the project name from Prompt 1. Show the literal $HOME/ form (not expanded) as the default:
Where should the centralized vault live?
Default: $DEFAULT_PATH
[press Enter to accept, or type a $HOME-prefixed path]
Prompt 5 — Embedded escape hatch (rare):
Use embedded vault inside the project repo instead? [y/N]
The centralized layout lets multiple worktrees and the Obsidian app share
one source of truth. Pick embedded only if you explicitly want the vault
committed to the project repo.
Validate each answer before proceeding:
-. Reject ArkNew (no dash) or ArkNew-- (double dash).$HOME/ or ~/ (normalize ~/ to $HOME/). Reject any other absolute path:
case "$USER_PATH" in
'$HOME/'*) ;; # OK
'~/'*) USER_PATH="\$HOME/${USER_PATH#'~/'}" ;; # normalize
*)
echo "ERROR: Vault path must be under \$HOME so tracked metadata stays portable."
echo "To use an external drive, symlink it: ln -s /Volumes/Drive/vaults \$HOME/Vaults"
echo "Then point the wizard at the \$HOME path."
# Re-prompt
;;
esac
eval echo "$USER_PATH") must not already exist, OR must exist and be empty. If it exists with content from a different project, refuse (see Step 2a edge case)../vault/ inside the project repo. Still reject if the path already exists.If user picked embedded: branch to the embedded sub-flow — skip centralized-vault Steps 2a–2d and proceed with the legacy ./vault/ setup.
Otherwise: Continue to Step 2 (Python check), then Steps 2a–2d (centralized setup).
PYTHON_VERSION=$(python3 --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+' | head -1)
MAJOR=$(echo "$PYTHON_VERSION" | cut -d. -f1)
MINOR=$(echo "$PYTHON_VERSION" | cut -d. -f2)
if [ -z "$PYTHON_VERSION" ]; then
echo "FAIL: python3 not found — install Python 3.10+ before continuing"
echo "macOS: brew install python@3.12"
echo "PAUSE — cannot continue without Python"
elif [ "$MAJOR" -gt 3 ] || ([ "$MAJOR" -eq 3 ] && [ "$MINOR" -ge 10 ]); then
echo "OK: Python $PYTHON_VERSION"
else
echo "FAIL: Python $PYTHON_VERSION too old — need >= 3.10"
echo "PAUSE"
fi
If missing or too old, PAUSE and tell user to install. Do not proceed.
Skip if user picked embedded in Prompt 5.
VAULT_REPO_PATH_EXPANDED=$(eval "echo $USER_PATH")
# Edge case: target exists with foreign content
if [ -d "$VAULT_REPO_PATH_EXPANDED" ] && [ -n "$(ls -A "$VAULT_REPO_PATH_EXPANDED" 2>/dev/null)" ]; then
echo "ERROR: $VAULT_REPO_PATH_EXPANDED already exists with content."
echo "If orphan from a failed run, delete it and retry."
echo "If it belongs to another project, choose a different path."
exit 1
fi
mkdir -p "$VAULT_REPO_PATH_EXPANDED"
cd "$VAULT_REPO_PATH_EXPANDED" && git init
Write .gitignore (template: references/templates.md § Vault repo .gitignore). Create initial NotebookLM sync-state (template: references/templates.md § Vault repo initial NotebookLM sync-state). Return to <project_repo>.
ln -s "$VAULT_REPO_PATH_EXPANDED" vault
grep -qxF 'vault' .gitignore 2>/dev/null || echo 'vault' >> .gitignore
test -L vault && test -e vault && echo "vault symlink OK -> $(readlink vault)"
Write scripts/setup-vault-symlink.sh using template from references/templates.md § scripts/setup-vault-symlink.sh. Substitute {VAULT_TARGET} with the $HOME/-prefixed form from Step 1 Prompt 4 (NOT the expanded path).
Verify the grep contract:
MATCH_COUNT=$(grep -cE '^VAULT_TARGET="[^"]*"\s*$' scripts/setup-vault-symlink.sh)
[ "$MATCH_COUNT" -eq 1 ] || { echo "ERROR: script must contain exactly one VAULT_TARGET= line"; exit 1; }
grep -qE '^VAULT_TARGET="\$HOME/' scripts/setup-vault-symlink.sh || { echo "ERROR: VAULT_TARGET must start with \$HOME/"; exit 1; }
Install post-checkout hook from references/templates.md § .git/hooks/post-checkout:
HOOK_PATH="$(git rev-parse --git-common-dir)/hooks/post-checkout"
# Write the template content to $HOOK_PATH
chmod +x "$HOOK_PATH"
If .superset/config.json exists, merge setup/teardown entries via references/templates.md § .superset/config.json merge.
Final post-install verification:
test -L vault && test -e vault \
&& test -x "$(git rev-parse --git-common-dir)/hooks/post-checkout" \
&& test -f scripts/setup-vault-symlink.sh \
&& grep -qE '^VAULT_TARGET="\$HOME/' scripts/setup-vault-symlink.sh \
|| { echo "ERROR: post-install verification failed"; exit 1; }
echo "Automation installed."
if command -v gh >/dev/null 2>&1 && gh auth status >/dev/null 2>&1; then
read -rp "Create a GitHub repo for this vault now? [y/N] " ANS
case "$ANS" in
y|Y)
cd "$VAULT_REPO_PATH_EXPANDED"
gh repo create --private "<project>-vault" --source=. --push
cd "<project_repo>"
;;
*)
echo "Skipped. Later: cd $VAULT_REPO_PATH_EXPANDED && gh repo create --private <project>-vault --source=. --push"
;;
esac
else
echo "gh not installed or not authenticated — skipping. Create later: cd $VAULT_REPO_PATH_EXPANDED && gh repo create --private <project>-vault --source=. --push"
fi
After Step 2d: Steps 3–18 run with {vault_path} set to the centralized <vault_repo_path> (expanded absolute path), NOT the project-repo vault symlink. Directory creation, templates, index generation, and the final git add . && git commit happen inside the centralized vault repo.
mkdir -p {vault_path}/{_Templates,_Attachments,_meta,.obsidian/plugins/{tasknotes,obsidian-git},TaskNotes/{Tasks/{Epic,Story,Bug,Task},Archive/{Epic,Story,Bug,Enhancement},Templates,Views,meta}}
For monorepo layout, also: mkdir -p {vault_path}/{project_docs_path}/Session-Logs
Write {vault_path}/00-Home.md (standalone) or {vault_path}/{project_docs_path}/00-Home.md (monorepo) from references/templates.md § 00-Home.md. Substitute {Project Name} and {today}.
Write from references/templates.md:
{vault_path}/_meta/vault-schema.md — § _meta/vault-schema.md{vault_path}/_meta/taxonomy.md — § _meta/taxonomy.md{vault_path}/_meta/generate-index.py — § _meta/generate-index.py (chmod +x after)Write 6 files in {vault_path}/_Templates/ from references/templates.md § Page templates:
Session-Template.md, Compiled-Insight-Template.md, Bug-Template.md, Task-Template.md, Research-Template.md, Service-Template.mdecho "1" > {vault_path}/TaskNotes/meta/{task_prefix}counter
{task_prefix} includes the trailing dash (counter filename: {task_prefix}counter → ArkNew-counter; no double dash).
Write {vault_path}/TaskNotes/00-Project-Management-Guide.md from references/templates.md § TaskNotes/00-Project-Management-Guide.md.
Write from references/templates.md § Obsidian config files:
{vault_path}/.gitignore — vault-level gitignore (workspace/graph/themes/plugin data.json){vault_path}/.obsidian/app.json, appearance.json, community-plugins.json, core-plugins.jsonIf CLAUDE.md missing, create from references/templates.md § CLAUDE.md template. If present but missing fields, update with vault/prefix/TaskNotes rows.
For centralized layout (default): no extra row needed — check #20 defaults to pass when the vault symlink resolves.
For embedded (escape hatch): append the | **Vault layout** | embedded (not symlinked) | row. Check #20's grep contract: ^\|\s*\*\*Vault layout\*\*\s*\|[^|]*embedded (case-insensitive) — do not deviate.
For monorepo layout, point Obsidian Vault row at the project docs subdirectory and add the vault root separately.
Skip to Step 16 if Quick tier. Three-tier install fallback — see references/plugin-install.md:
main.js, manifest.json, styles.css from GitHub releases (resolves repos via Obsidian's community-plugins.json registry).data.json — project-specific).Skip to Step 16 if Quick tier.
Write plugin data.json configs (gitignored, project-specific) from references/templates.md:
{vault_path}/.obsidian/plugins/tasknotes/data.json — § TaskNotes plugin data.json{vault_path}/.obsidian/plugins/obsidian-git/data.json — § Obsidian Git plugin data.jsonAdjust apiPort if user runs multiple Obsidian instances (8080/8081/8082).
Configure TaskNotes MCP in .mcp.json — first validate JSON, then merge from references/templates.md § .mcp.json TaskNotes MCP entry:
if [ -f .mcp.json ]; then
python3 -c "import json; json.load(open('.mcp.json'))" 2>/dev/null \
|| { echo "WARNING: .mcp.json is malformed JSON. Back up and recreate."; cp .mcp.json .mcp.json.bak; }
fi
Skip to Step 15 if Quick/Standard.
Known pin constraints (as of 2026-04-23, revisit when upstream lands fixes):
cp39-abi3 wheel. Tracked at MemPalace #1109.mempalace status CLI crashes on palaces past ~32k drawers (#802) — unavoidable CLI bug. Use the MCP mempalace_status tool instead.# Preflight 1: pipx must exist (we rely on it for isolated venvs)
if ! command -v pipx >/dev/null 2>&1; then
echo "WARNING: pipx not installed. Install via: brew install pipx (macOS) or python3 -m pip install --user pipx (Linux)."
MEMPALACE_OK=false
fi
# Preflight 2: Python 3.13 available
if [ -z "$MEMPALACE_OK" ]; then
PY313="$(command -v python3.13 2>/dev/null)"
if [ -z "$PY313" ]; then
if command -v brew >/dev/null 2>&1; then
echo "Installing Python 3.13 via Homebrew..."
brew install python@3.13 && PY313="$(command -v python3.13)"
fi
fi
if [ -z "$PY313" ]; then
echo "WARNING: Python 3.13 not found. On Linux: install via apt/pyenv. On macOS: install Homebrew first."
MEMPALACE_OK=false
fi
fi
# Check existing install's interpreter version by asking the venv's python directly
# (DO NOT parse `pipx list --short` — that shows the package version, not the interpreter).
if [ -z "$MEMPALACE_OK" ] && command -v mempalace >/dev/null 2>&1; then
VENV_PY="$(pipx environment --value PIPX_LOCAL_VENVS 2>/dev/null)/mempalace/bin/python"
if [ -x "$VENV_PY" ]; then
INSTALLED_PY="$("$VENV_PY" -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")' 2>/dev/null)"
if [ "$INSTALLED_PY" = "3.13" ]; then
echo "MemPalace already installed on Python 3.13."
MEMPALACE_OK=true
else
echo "MemPalace installed on Python $INSTALLED_PY — reinstalling on 3.13..."
if pipx reinstall --python "$PY313" mempalace; then
MEMPALACE_OK=true
else
echo "ERROR: pipx reinstall failed. Leaving the Python $INSTALLED_PY install in place (still works for CLI mine)."
MEMPALACE_OK=false
fi
fi
fi
fi
# Fresh install on Python 3.13
if [ -z "$MEMPALACE_OK" ]; then
echo "Installing mempalace on Python 3.13 via pipx..."
if pipx install --python "$PY313" "mempalace>=3.3.5,<4.0.0"; then
MEMPALACE_OK=true
else
MEMPALACE_OK=false
fi
fi
# Pin chromadb to 1.5.7 (critical — 1.5.8 crashes on vector query).
if [ "$MEMPALACE_OK" = "true" ]; then
INSTALLED_CHROMA="$(pipx runpip mempalace show chromadb 2>/dev/null | awk '/^Version:/ {print $2}')"
if [ "$INSTALLED_CHROMA" != "1.5.7" ]; then
echo "Pinning chromadb to 1.5.7 (was $INSTALLED_CHROMA)..."
pipx runpip mempalace install "chromadb==1.5.7" || echo "WARNING: chromadb pin failed — MCP may segfault on vector query."
fi
fi
If install fails, warn and skip. Non-blocking.
Step 13b: Install MemPalace Claude Code plugin (optional but recommended).
The plugin auto-starts the MemPalace MCP server on every Claude Code session at user scope — giving LLM-native access to the 19 MCP tools for T2 reads without a CLI round-trip. Only meaningful if Step 13 succeeded.
# Skip if CLI install failed
[ "$MEMPALACE_OK" = "false" ] && echo "Skipping plugin — CLI not installed." && SKIP_PLUGIN=true
# Detect if plugin is already installed at user scope.
# Block-aware parse: each plugin in `claude plugin list` is a block starting
# with " ❯ <name>@<marketplace>". Scanning the whole stream false-PASSes when
# a different plugin is the user-scope/enabled one.
if [ -z "$SKIP_PLUGIN" ]; then
if claude plugin list 2>/dev/null | awk '
/^ ❯ / {in_target = ($0 ~ /^ ❯ mempalace@/); next}
in_target && /Scope: user/ {scope=1}
in_target && /Status: ✔ enabled/ {enabled=1}
END {exit !(scope && enabled)}
'; then
echo "MemPalace plugin already installed at user scope."
PLUGIN_OK=true
fi
fi
If not installed, prompt the user:
MemPalace Claude Code plugin not detected. Install now? [Y/n]
[Y] Install plugin + MCP shim (recommended for macOS + Python 3.13 + chromadb 1.5.7)
[N] Skip the plugin — CLI install from Step 13 stays in place, but Claude Code
sessions won't have the mempalace_* MCP tools natively. You can still use
the CLI for mining and later install the plugin by re-running /ark-onboard.
The plugin auto-starts an MCP server on every Claude Code session, adding
19 memory tools and /mempalace:* slash commands.
On yes:
# The plugin's bundled .mcp.json declares command: mempalace-mcp, but the pip
# package ships the server as `python -m mempalace.mcp_server`. Create a shim
# so the plugin's declared MCP config works unchanged.
#
# HARD REQUIREMENT: the pipx venv python must exist. Falling back to system
# python3 is unsafe — it may lack the mempalace module or hit a broken 3.14
# install. If the venv is missing, abort the plugin install; the CLI-only
# path still works.
MEMPALACE_VENV_PYTHON="$(pipx environment --value PIPX_LOCAL_VENVS 2>/dev/null)/mempalace/bin/python"
if [ ! -x "$MEMPALACE_VENV_PYTHON" ]; then
echo "ERROR: mempalace pipx venv not found at $MEMPALACE_VENV_PYTHON."
echo "Run Step 13 first, or reinstall: pipx reinstall --python python3.13 mempalace"
SKIP_PLUGIN=true
fi
if [ -z "$SKIP_PLUGIN" ]; then
mkdir -p "$HOME/.local/bin"
SHIM_PATH="$HOME/.local/bin/mempalace-mcp"
# Refuse to clobber a pre-existing symlink — `cat > ...` follows it and
# overwrites the target, which could be anywhere. User-writable target,
# same-user threat, but still trivially avoidable.
if [ -L "$SHIM_PATH" ]; then
echo "ERROR: $SHIM_PATH is a symlink. Remove it before re-running Step 13b."
SKIP_PLUGIN=true
fi
if [ -z "$SKIP_PLUGIN" ]; then
# Write to tempfile + mv for atomic swap. Quote the interpreter path in the
# generated shim (handles pipx env paths with spaces on macOS).
SHIM_TMP=$(mktemp)
cat > "$SHIM_TMP" <<EOF
#!/bin/bash
# Shim for the MemPalace Claude Code plugin.
exec "$MEMPALACE_VENV_PYTHON" -m mempalace.mcp_server "\$@"
EOF
chmod +x "$SHIM_TMP"
mv "$SHIM_TMP" "$SHIM_PATH"
fi
# Verify ~/.local/bin is in PATH — Claude Code inherits the user's PATH when
# launching the plugin's MCP server. If it's missing, the plugin's
# "command: mempalace-mcp" will fail at session start and the install will
# look successful but do nothing.
case ":$PATH:" in
*":$HOME/.local/bin:"*)
echo "✓ ~/.local/bin is in PATH"
;;
*)
echo "WARNING: ~/.local/bin is NOT in PATH. Claude Code will not find the mempalace-mcp shim."
echo "Add this line to your shell rc (~/.zshrc or ~/.bashrc):"
echo " export PATH=\"\$HOME/.local/bin:\$PATH\""
echo "Then restart your shell and re-run this step."
;;
esac
claude plugin marketplace add milla-jovovich/mempalace
claude plugin install --scope user mempalace@mempalace
fi
Supply-chain note: claude plugin marketplace add does not expose commit-SHA pinning. The marketplace refreshes on claude plugin update and picks up whatever is on the remote's default branch. Audit marketplace contents at ~/.claude/plugins/marketplaces/mempalace/ if you need tighter supply-chain guarantees.
After install, tell the user to restart Claude Code so the new MCP server is picked up. If install fails, warn and continue — Step 14 still works via the CLI alone.
Skip to Step 16 if Quick/Standard.
Mine the vault (creates vault content wing):
bash skills/shared/mine-vault.sh
If mine-vault.sh not found, warn and skip.
Install history hook (conversation history wing, separate from vault content wing). Pre-validate .claude/settings.json:
if [ -f .claude/settings.json ]; then
python3 -c "import json; json.load(open('.claude/settings.json'))" 2>/dev/null \
|| { echo "ERROR: .claude/settings.json is malformed JSON. Skipping hook."; SKIP_HOOK=true; }
fi
if [ -z "$SKIP_HOOK" ]; then
bash skills/claude-history-ingest/hooks/install-hook.sh
fi
Both non-blocking.
Skip to Step 16 if Quick/Standard.
command -v notebooklm 2>/dev/null && echo "NotebookLM CLI found" || echo "NotebookLM CLI not found"
If not installed:
NotebookLM CLI not installed. To install:
pipx install notebooklm-cli
Then authenticate: notebooklm auth login
Skipping — configure later with /notebooklm-vault.
If installed, check auth:
notebooklm auth check --test 2>/dev/null
if [ $? -ne 0 ]; then
echo "NotebookLM not authenticated. Run: notebooklm auth login"
echo "PAUSE — authenticate, then continue."
fi
Create {vault_path}/.notebooklm/config.json from references/templates.md § NotebookLM config. Tell user to fill in notebooks.main.id and run /notebooklm-vault setup.
If CLI missing or auth fails, warn and continue. Non-blocking.
cd {vault_path} && python3 _meta/generate-index.py
Verify output: "index.md generated with N entries."
Re-check git state:
if ! git rev-parse --git-dir 2>/dev/null; then
echo "Initializing git repo..."
git init
fi
git config user.name 2>/dev/null || echo "WARNING: git user.name not set. Run: git config user.name 'Your Name'"
git config user.email 2>/dev/null || echo "WARNING: git user.email not set. Run: git config user.email 'you@example.com'"
Centralized vault (default): the initial vault-content commit lives in the vault repo. The project repo only commits metadata + the tracked script.
# 1. Initial commit inside centralized vault repo
cd "$VAULT_REPO_PATH_EXPANDED"
git add .
git commit -m "feat: initialize {project_name} vault with Ark structure"
# 2. Project-repo metadata
cd "<project_repo>"
git add scripts/setup-vault-symlink.sh .gitignore CLAUDE.md
git add .mcp.json .claude/settings.json .notebooklm/config.json 2>/dev/null
if [ -f .superset/config.json ]; then git add .superset/config.json; fi
git commit -m "feat: wire {project_name} project to centralized vault"
Embedded vault (escape hatch): legacy single-commit flow.
git add {vault_path}/ CLAUDE.md .mcp.json .claude/settings.json .notebooklm/ 2>/dev/null
git commit -m "feat: initialize {project_name} vault with Ark structure (embedded)"
The post-checkout hook is NOT tracked — it's installed per-clone by /ark-onboard.
Run the full 22-check diagnostic. Show the scorecard (§ Scorecard Output Format below).
Then show follow-up reminders. Adjust based on what was actually set up:
Setup complete! Follow-up reminders:
1. Open the vault in Obsidian — plugins are pre-configured (if downloaded/copied)
OR: Install TaskNotes + Obsidian Git via Community Plugins (if manual fallback was needed)
2. Fill in NotebookLM notebook ID in .notebooklm/config.json (if Full tier)
3. Optional: install OMC for /ark-workflow Path B — https://github.com/anthropics/oh-my-claudecode
4. Run /ark-health anytime to check ecosystem health
5. Run /ark-onboard again to upgrade tiers
HAS_OMC=true)Two distinct operations:
_meta/, _Templates/, TaskNotes/, etc. to an existing vault. 15-step flow below. Non-destructive: never deletes/overwrites. Frontmatter changes are explicit and reversible (separate commits)./ark-onboard to generate an externalization plan (the "Externalization Plan Generation" path below).Key principle: additive only. Never delete or overwrite existing content. Destructive steps (externalization) live in a separate plan file, never inline.
find {vault_path} -name "*.md" | wc -l
ls -d {vault_path}*/ 2>/dev/null
head -20 {vault_path}/*.md 2>/dev/null | head -60
grep -rh "^tags:" {vault_path} --include="*.md" 2>/dev/null | head -10
grep -roh "#[a-zA-Z][a-zA-Z0-9_-]*" {vault_path} --include="*.md" 2>/dev/null | sort | uniq -c | sort -rn | head -20
Report to user: "Found N pages, M existing tags, folder structure..."
Ask for project name + task prefix (must end with -). Vault path already known; confirm as vault root.
Run git safety checks (same as Greenfield Prerequisites). If not a git repo, offer git init. Commit a rollback point:
git add -A && git commit -m "checkpoint: pre-Ark migration"
If nothing to commit, continue.
Create only directories/files that don't already exist. Never overwrite.
[ -d "{vault_path}_meta" ] || mkdir -p "{vault_path}_meta"
[ -d "{vault_path}_Templates" ] || mkdir -p "{vault_path}_Templates"
[ -d "{vault_path}_Attachments" ] || mkdir -p "{vault_path}_Attachments"
[ -d "{vault_path}.obsidian" ] || mkdir -p "{vault_path}.obsidian"
[ -d "{vault_path}TaskNotes/Tasks/Epic" ] || mkdir -p "{vault_path}TaskNotes/Tasks/"{Epic,Story,Bug,Task}
[ -d "{vault_path}TaskNotes/Archive/Epic" ] || mkdir -p "{vault_path}TaskNotes/Archive/"{Epic,Story,Bug,Enhancement}
[ -d "{vault_path}TaskNotes/meta" ] || mkdir -p "{vault_path}TaskNotes/"{Templates,Views,meta}
Check before writing files:
[ -f "{vault_path}00-Home.md" ] && echo "00-Home.md exists — skipping" || echo "Creating 00-Home.md"
Create missing files from templates in references/templates.md, only if not already present.
Write {vault_path}/_meta/vault-schema.md from references/templates.md § _meta/vault-schema.md. If file exists, ask user before overwriting.
grep -roh "^ - [a-zA-Z][a-zA-Z0-9_-]*" {vault_path} --include="*.md" 2>/dev/null | sed 's/^ - //' | sort | uniq -c | sort -rn
grep -roh "#[a-zA-Z][a-zA-Z0-9_-]*" {vault_path} --include="*.md" 2>/dev/null | sed 's/^#//' | sort | uniq -c | sort -rn
Show the proposed mapping (existing tags kept as-is, mapped to Ark structural tags, new Ark tags added). Ask the user to accept/edit. Write {vault_path}/_meta/taxonomy.md.
Show 3 sample pages with current vs proposed Ark frontmatter. Ask: Apply frontmatter backfill to all N pages? [y/n/select].
Skip non-standard pages during bulk backfill:
---) ) at top of file.mdLog skipped: Skipped: {filename} — {reason}.
If accepted, apply backfill (Claude reads/edits each file, skipping non-standard). Separate commit:
git add -A && git commit -m "chore: backfill Ark frontmatter on N pages (M skipped)"
Individually revertable. If declined, skip — add later with /wiki-lint --fix.
Same as Greenfield Steps 7–8. Create counter + project management guide.
Same as Greenfield Step 9, but check before writing:
[ -f "{vault_path}.obsidian/app.json" ] || echo '{ "alwaysUpdateLinks": true }' > "{vault_path}.obsidian/app.json"
[ -f "{vault_path}.obsidian/appearance.json" ] || echo '{}' > "{vault_path}.obsidian/appearance.json"
For community-plugins.json: merge existing plugin list with Ark plugins (don't remove existing). For .gitignore: append Ark patterns if not already present.
Same as Greenfield Step 10. Update existing or create new.
cd {vault_path} && python3 _meta/generate-index.py
Run all 22 checks. Show scorecard.
git add -A && git commit -m "feat: add Ark scaffolding to {project_name} vault"
Show follow-up reminders (same format as Greenfield Step 18, adjusted for migration).
if [ -d vault ] && [ ! -L vault ]; then
echo "Ark scaffolding complete. The vault is still embedded inside the project repo."
echo "To externalize it (recommended for worktree/Obsidian-app consistency), run:"
echo " /ark-onboard"
echo "The wizard will detect the embedded-Ark state and generate an externalization plan."
fi
Pointer only — does NOT generate the plan inline. The externalization offer triggers when the user re-runs /ark-onboard and state detection classifies the project as Partial Ark with a real vault/ + Ark artifacts + no opt-out.
Triggered when: state detection finds vault/ is a real directory with Ark artifacts AND EMBEDDED_OPTOUT=false.
Behavior: no filesystem changes except writing the plan file. User reviews and executes via /executing-plans.
Compute default path per Default path detection.
if [ -d "$HOME/.superset" ]; then
DEFAULT_PATH="\$HOME/.superset/vaults/<project>"
else
DEFAULT_PATH="\$HOME/Vaults/<project>"
fi
echo "Detected: vault/ is committed to this repo as a real directory."
echo "The Ark convention is to externalize it. I'll generate a plan file (no destructive actions)."
echo "Review and run via /executing-plans."
echo ""
read -rp "Centralized location for the extracted vault [default: $DEFAULT_PATH]: " USER_PATH
USER_PATH="${USER_PATH:-$DEFAULT_PATH}"
# Path constraint (same as Greenfield)
case "$USER_PATH" in
'$HOME/'*) ;;
'~/'*) USER_PATH="\$HOME/${USER_PATH#'~/'}" ;;
*) echo "ERROR: path must start with \$HOME/ or ~/"; exit 1 ;;
esac
read -rp "Create a GitHub repo for the vault now? [y/N] " WANT_GH
case "$WANT_GH" in y|Y) WANT_GH=true ;; *) WANT_GH=false ;; esac
Discover sibling worktrees:
SIBLINGS=$(git worktree list --porcelain | awk '/^worktree /{print $2}' | grep -v "^$(git rev-parse --show-toplevel)$")
Write docs/superpowers/plans/$(date +%Y-%m-%d)-externalize-vault.md using the template in references/externalize-vault-plan.md. Substitute at generation time:
<PROJECT> → project name from CLAUDE.md<VAULT_REPO_PATH_PORTABLE> → user's chosen path (e.g., $HOME/.superset/vaults/my-project)<VAULT_REPO_PATH_EXPANDED> → eval "echo $USER_PATH"<SIBLINGS> → newline-separated list; inject one Phase 2 sub-step per sibling<WANT_GH> → true/false; only include Phase 1 Step 1.7 if true<MAIN> → main worktree pathAfter writing:
Plan file written to: docs/superpowers/plans/YYYY-MM-DD-externalize-vault.md
Sibling worktrees that will be touched:
<SIBLINGS>
Next step: review the plan, then run /executing-plans <plan-file>
Exit the wizard. No filesystem changes beyond the plan file.
For vaults that have Ark structure but some checks are failing.
If REPAIR_REASON=centralized-vault-drift or centralized-vault-script-missing, run this FIRST — before the generic 5-step flow below. Full scenarios + bash in references/centralized-vault-repair.md:
readlink != script's VAULT_TARGET) — prompt: trust symlink (update script) OR trust script (recreate symlink) OR do nothingscripts/setup-vault-symlink.sh using the portable form from readlinkAfter centralized-vault repairs, fall through to the generic 5-step repair flow (which re-runs the diagnostic and catches any remaining failures).
Run all 22 checks. Record failures.
Scorecard with failures highlighted, grouped by severity:
Ark Health — Repair Mode
Critical failures (must fix):
!! Check 5: CLAUDE.md missing task prefix
!! Check 8: Vault structure missing _Templates/
Standard failures (recommended):
!! Check 11: Task counter file not found
!! Check 13: TaskNotes MCP not configured
!! Check 16: History auto-index hook not registered
Warnings (non-blocking, review interactively):
~~ Check 16: Wing-mismatch — expected -my-project, found -my-project-subdir
~~ Check 16: Threshold-lock — baseline stuck at 4319 drawers
Available upgrades:
-- Check 14: MemPalace not installed
-- Check 17: NotebookLM not installed
Fix critical + standard issues now? [y/n]
Check 16 classification (context-dependent):
| Condition | Classification |
|---|---|
| MemPalace installed (Check 14 pass) AND vault wing has drawers (Check 15 pass) AND hook NOT in project-local settings | Standard failure — defensive coverage. Global hook usually covers this, so auto-fix is defense-in-depth rather than strictly functional. |
| MemPalace NOT installed | Available upgrade — hook depends on mempalace; present as Full-tier upgrade. |
| Hook registered but sub-warnings (wing-mismatch / threshold-staleness / threshold-lock) | Warning — surface in Step 3b. Do NOT auto-fix. threshold-lock is high-signal: it looks like "hook not running" but is a stuck baseline. |
Fix in order (Critical first, then Standard). For each fix:
Critical fixes (checks 4–9):
references/templates.md § CLAUDE.md templateStandard fixes (checks 10–13, 16):
python3 _meta/generate-index.pyecho "1" > {path}references/plugin-install.md (download → reference vault → manual fallback).mcp.json (see references/templates.md § .mcp.json TaskNotes MCP entry)bash skills/claude-history-ingest/hooks/install-hook.shCheck 16's sub-warnings need human judgment. Present each individually: fix now, skip, or explain.
Wing-mismatch (e.g., -ArkNode-AI expected, -ArkNode-AI-projects-trading-signal-ai found):
bash skills/shared/mine-vault.sh from current CWD to index the current project as its own wingThreshold-staleness (new_drawers >= 200):
/claude-history-ingest compile to clear the backlog and reset the baselineThreshold-lock (current == baseline, baseline > 500):
jq '."<wing>".drawers_at_last_compile = 0' ~/.mempalace/hook_state/compile_threshold.json \
> /tmp/t && mv /tmp/t ~/.mempalace/hook_state/compile_threshold.json
After fixes, determine current tier and offer upgrade. If user accepts, execute Greenfield Steps 13–15 (MemPalace + hook + NotebookLM).
Run all 22 checks again. Show before/after comparison (§ Scorecard Output Format § before/after below).
For version drift (plugin updated but project conventions out of date), run /ark-update — it replays additive conventions from the current target profile. /ark-update refuses to run on malformed CLAUDE.md / .mcp.json / .ark/migrations-applied.jsonl and points back here; coexistence is intentional. Note: if .ark/ is gitignored, remove the pattern and commit before running /ark-update.
For projects where all Critical + Standard checks pass.
Run all 22 checks. All Critical + Standard should pass.
Full scorecard per § Scorecard Output Format. Highlight the current tier.
If not at Full tier, show what's available:
Current tier: Standard. Available Full-tier upgrades:
MemPalace — deep vault search + experiential synthesis
Install: pipx install "mempalace>=3.0.0,<4.0.0"
Then: bash skills/shared/mine-vault.sh
History hook — auto-index Claude sessions on exit
Install: bash skills/claude-history-ingest/hooks/install-hook.sh
NotebookLM — fastest pre-synthesized vault queries
Install: pipx install notebooklm-cli
Then: /notebooklm-vault setup
Upgrade to Full tier now? [y/n]
Optional capability extensions (do NOT promote tier):
OMC plugin — autonomous execution for /ark-workflow Path B
Install: see https://github.com/anthropics/oh-my-claudecode
If accepted, execute Greenfield Steps 13–15. Re-run diagnostic + show updated scorecard.
If at Full tier:
Checks 1-20 all pass. Full tier active. (Checks 21 and 22 are tier-agnostic.)
No upgrades available. Run /ark-health anytime to verify.
+--------------------------------------+
| Ark Setup -- Scorecard |
+--------------------------------------+
| CLAUDE.md OK configured |
| Vault structure OK healthy |
| Python OK 3.12 |
| Index OK fresh |
| Task counter OK ready |
| Superpowers plugin OK v5.0.7 |
| Obsidian plugin OK v1.0.1 |
| Gstack plugin OK detected |
| TaskNotes MCP OK connected |
| MemPalace -- not installed |
| NotebookLM -- not installed |
| OMC plugin -- not installed |
| Plugin version OK v1.14.0 |
+--------------------------------------+
| Tier: Standard |
| 0 fixes, 0 warnings, 3 upgrades |
| Run /ark-health anytime to check |
+--------------------------------------+
Symbols: OK = pass, !! = fail (has fix), ~~ = warning, -- = available upgrade
Rules:
!! rows: short failure description (missing, malformed, not found)-- rows: not installed or not configured~~ rows: short warning (stale (5 pages changed)){N} fixes, {N} warnings, {N} upgrades (singular when count is 1)Tier: {Quick|Standard|Full}; below Quick → Tier: --Run /ark-health anytime to checkTier rules:
| Tier | Condition |
|---|---|
| Quick | No Critical/Standard fail in checks 1–11 (warn is OK) |
| Standard | No Critical/Standard fail in checks 1–13 (warn is OK) |
| Full | No Critical/Standard fail in checks 1–20 (warn is OK); Check 21 is tier-agnostic |
| Below Quick | Any critical check (1, 4–9) failing |
Warn and upgrade checks don't block tier classification. Checks 10/20/22 (warn) and 14/17/18/21 (upgrade) count as "no fail". /ark-health defines a "Minimal" tier (checks 1–9 pass, 10–11 skip); the wizard never uses Minimal because it always creates the vault.
Before/after comparison (Repair path):
--- BEFORE ---
{scorecard}
--- AFTER ---
{scorecard}
Changes: {N} fixes applied, {N} upgrades added
/wiki-setup is in the Greenfield path. Users should run /ark-onboard instead of /wiki-setup for new projects.install-hook.sh, verify .claude/settings.json is valid JSON (script uses Python and fails on malformed JSON).data.json for both plugins with Ark-specific defaults. User only enables plugins in Obsidian — no manual configuration. Falls back to reference vault copy, then manual install.mcpServers.tasknotes presence in .mcp.json, not endpoint reachability.### Step 1 of 18 — …) so progress is trackable at a glance./ark-health is authoritative for all 22 check definitions. If this convenience summary drifts, that skill wins.references/*.md. The agent loads what it needs when it needs it, cutting the at-invocation footprint without losing any operational detail.