agentleFS
AirMileAirMile/claude-config/skills/core-bootstrap/SKILL.md

core-bootstrap

Skill0 starsChanged 4 months agoMITView on GitHub

Bootstrap ~/.claude/ config, symlinks, and settings. Use with /core-bootstrap.

The file

Show its rules and text

We split this skill into the rules it gives an agent

16 rules. About 0 are common, 1 is distinctive, and 15 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈1

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 15

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Check jq availability (advisory only — does not stop execution):

    line 47
  2. Very specific:

    Check whether ~/.claude/CLAUDE.md already contains a Language: setting:

    line 55
  3. Very specific:

    If Language: is found → store LANGUAGE_CHOICE=skip (language step skipped — re-runs leave existing setting alone).

    line 61
  4. Very specific:

    If Language: is not found → ask (two sequential modals, 4-option cap per shared/SKILL-PATTERNS.md § Modal Option Cap.

    line 63
  5. Very specific:

    Check whether ~/.claude/CLAUDE.md already contains an Explanation Level: setting:

    line 95
  6. Very specific:

    If Explanation Level: is found → store EXPLANATION_CHOICE=skip (step skipped — re-runs leave existing setting alone).

    line 101
  7. Very specific:

    If Explanation Level: is not found → ask:

    line 103
  8. Very specific:

    If ~/.claude/settings.json is found → store PERM_CHOICE=skip (already configured — don't touch it).

    line 130
  9. Distinctive:

    If not found → ask:

    line 132
  10. Very specific:

    Check $CONFIG_REPO/.claude/paths.local.yaml for a persisted value first:

    line 149
  11. Very specific:

    If PLAN_TIER is non-empty → skip the question (use persisted value, note it in PHASE 3 report as <value> (saved)).

    line 169
  12. Very specific:

    If PLAN_TIER is empty → ask:

    line 171
  13. Very specific:

    Write per-machine path configuration to $CONFIG_REPO/.claude/paths.local.yaml.

    line 194
  14. Very specific:

    After copying CLAUDE.md (only if it was actually placed, and LANGUAGE_CHOICE is not skip):

    line 349
  15. Very specific:

    After copying settings.json (only if it was actually placed, and PERM_CHOICE=auto):

    line 397
  16. Very specific:

    skip: no plan-tier block shown.

    line 703

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Core Bootstrap Skill

Trigger: /core-bootstrap

Bootstrap of the user-global Claude Code configuration. Deploys 4 user-files to ~/.claude/ and creates 4 global symlinks/junctions. Fully idempotent — running again skips anything already in place, so it is always safe to re-run after a git pull or on a new machine. To replace a deployed file, delete it manually first, then re-run.


PHASE 0: Pre-flight

Resolve CONFIG_REPO — the root of the cloned claude-config repo:

# macOS/Linux — follow symlink of ~/.claude/skills if it already exists
if [ -L "$HOME/.claude/skills" ]; then
  CONFIG_REPO="$(realpath "$HOME/.claude/skills/..")"
else
  # Fallback: current working directory if it is the repo
  CONFIG_REPO="$(pwd)"
fi
# Windows
if (Test-Path "$env:USERPROFILE\.claude\skills" -PathType Container) {
  $CONFIG_REPO = Split-Path (Resolve-Path "$env:USERPROFILE\.claude\skills") -Parent
} else {
  $CONFIG_REPO = (Get-Location).Path
}

Validate that $CONFIG_REPO/local/ exists. If not:

Cannot find local/. Run /core-bootstrap from the claude-config repo directory, or ensure ~/.claude/skills already symlinks to the repo.

Stop.

Check jq availability (advisory only — does not stop execution):

command -v jq >/dev/null 2>&1 || echo "warn: jq not found — settings overlay merge will be skipped if personal/settings.overlay.json is present"

Language selection

Check whether ~/.claude/CLAUDE.md already contains a Language: setting:

grep -q "Language:" "$HOME/.claude/CLAUDE.md" 2>/dev/null && echo "found" || echo "not-found"

If Language: is found → store LANGUAGE_CHOICE=skip (language step skipped — re-runs leave existing setting alone).

If Language: is not found → ask (two sequential modals, 4-option cap per shared/SKILL-PATTERNS.md § Modal Option Cap; show modal 2 only if "More languages →" is picked):

header: "Language"
question: "Which language should Claude use for output?"
options:
  - label: "English (Recommended)"
    description: "Claude responds in English"
  - label: "Nederlands"
    description: "Claude responds in Dutch"
  - label: "Deutsch"
    description: "Claude antwortet auf Deutsch"
  - label: "More languages →"
    description: "Français, Español"
multiSelect: false
header: "Language"
question: "Which language should Claude use for output?"
options:
  - label: "Français"
    description: "Claude répond en français"
  - label: "Español"
    description: "Claude responde en español"
multiSelect: false

Store the chosen label in LANGUAGE_CHOICE (e.g. "Nederlands"). If the user selects "English (Recommended)" → LANGUAGE_CHOICE=skip (template default, no patch needed).

Explanation Level selection

Check whether ~/.claude/CLAUDE.md already contains an Explanation Level: setting:

grep -q "Explanation Level:" "$HOME/.claude/CLAUDE.md" 2>/dev/null && echo "found" || echo "not-found"

If Explanation Level: is found → store EXPLANATION_CHOICE=skip (step skipped — re-runs leave existing setting alone).

If Explanation Level: is not found → ask:

header: "Explanation Level"
question: "How should Claude calibrate jargon and explanation depth by default?"
options:
  - label: "Intermediate (Recommended)"
    description: "Standard depth. Jargon ok, no extra scaffolding. Good default for most stacks."
  - label: "Beginner"
    description: "Every non-trivial term explained. Analogies always used. Good for stacks you're learning from scratch."
  - label: "Novice"
    description: "Framework-specific jargon explained. Analogies when helpful. Between Beginner and standard."
  - label: "Expert"
    description: "Compact. Assumes full stack familiarity. No term introductions or analogies."
multiSelect: false

Store the chosen label in EXPLANATION_CHOICE (e.g. "Beginner"). If the user selects "Intermediate (Recommended)" → EXPLANATION_CHOICE=skip (template default, no patch needed).

Permission mode selection

settings.json is only copied when it does not already exist. Mirror that for this question — re-runs leave an existing permission posture alone:

[ -f "$HOME/.claude/settings.json" ] && echo "found" || echo "not-found"

If ~/.claude/settings.json is found → store PERM_CHOICE=skip (already configured — don't touch it).

If not found → ask:

header: "Permission mode"
question: "How should Claude handle tool permissions by default?"
options:
  - label: "Bypass permissions (Recommended)"
    description: "No permission prompts. Safe here because all skills run within guard rails (plan mode, security-reminder + format-on-save hooks, explicit handoff contracts)."
  - label: "Auto mode"
    description: "Claude's classifier auto-approves safe actions and prompts only on risky/irreversible ones."
multiSelect: false

Store the choice in PERM_CHOICE. If the user selects "Bypass permissions (Recommended)" → PERM_CHOICE=skip (template default: bypassPermissions + disableAutoMode + dangerous-mode dialog pre-accepted, no patch needed). If "Auto mode" → PERM_CHOICE=auto (patched in PHASE 1).

Claude plan tier

Check $CONFIG_REPO/.claude/paths.local.yaml for a persisted value first:

# macOS/Linux
PLAN_TIER=""
if [ -f "$CONFIG_REPO/.claude/paths.local.yaml" ]; then
  PLAN_TIER=$(grep "claude_plan:" "$CONFIG_REPO/.claude/paths.local.yaml" | sed 's/.*claude_plan:[[:space:]]*//' | tr -d '"')
fi
# Windows
$planTier = ""
$pathsLocal = "$CONFIG_REPO\.claude\paths.local.yaml"
if (Test-Path $pathsLocal) {
  $line = Select-String -Path $pathsLocal -Pattern "claude_plan:" | Select-Object -First 1
  if ($line) { $planTier = ($line.Line -replace '.*claude_plan:\s*"?([^"]+)"?.*', '$1').Trim() }
}

If PLAN_TIER is non-empty → skip the question (use persisted value, note it in PHASE 3 report as <value> (saved)).

If PLAN_TIER is empty → ask:

header: "Claude plan"
question: "Which Claude plan are you on? (used to tailor the post-bootstrap tip)"
options:
  - label: "Max 5x"
    description: 'Tip: /model opus + effortLevel:"high" — full Opus; plan mode is an approval gate, not a routing switch'
  - label: "Pro"
    description: 'Tip: /model sonnet + effortLevel:"medium" — Opus quota is tight on Pro'
  - label: "Max 10x+"
    description: 'Tip: /model opus + effortLevel:"high" — quota headroom for full-Opus runs'
  - label: "Skip"
    description: "No plan-specific tip shown"
multiSelect: false

Store the choice in PLAN_TIER (one of max-5x, pro, max-10x, skip). Write to paths.local.yaml in PHASE 0.5 (see below).


PHASE 0.5: Paths setup

Write per-machine path configuration to $CONFIG_REPO/.claude/paths.local.yaml. Idempotent — skip writing if the file already exists, but validate that its projects_root still points at an existing directory and offer to correct a stale path.

# macOS/Linux
PATHS_LOCAL="$CONFIG_REPO/.claude/paths.local.yaml"
if [ -f "$PATHS_LOCAL" ]; then
  PATHS_STATUS="already-exists"
  # Validate the configured projects_root still points at an existing directory.
  # A stale path (e.g. moved projects folder) leaves the backlog server scanning
  # nothing — correct it, but don't re-prompt when the path is fine (stays idempotent).
  cur=$(awk -F'"' '/^[[:space:]]*projects_root:/ {print $2; exit}' "$PATHS_LOCAL")
  exp="${cur/#\~/$HOME}"; exp="${exp/#\$HOME/$HOME}"
  if [ -n "$cur" ] && [ ! -d "$exp" ]; then
    # AskUserQuestion (open text input): projects_root "$cur" no longer exists —
    # ask for a new absolute path that must exist as a directory. Store in NEW_ROOT.
    sed -i '' 's|^\([[:space:]]*projects_root:\).*|\1 "'"$NEW_ROOT"'"|' "$PATHS_LOCAL"
    PATHS_STATUS="updated"
  fi
else
  # Ask the user via AskUserQuestion (open text input — no default, no fallback):
  #   header: "Projects root"
  #   question: "What is the absolute path to your projects root? This is where
  #              /project-add creates new projects.
  #              Example: /Users/you/projects or /Users/you/Documents/Projects"
  # Validate: the path must exist as a directory. If not, re-ask.
  # Store input in PROJECTS_ROOT (no fallback — the user must answer).

  mkdir -p "$CONFIG_REPO/.claude"
  cat > "$PATHS_LOCAL" <<EOF
paths:
  projects_root: "$PROJECTS_ROOT"
  config_repo: "$CONFIG_REPO"
preferences:
  claude_plan: "$PLAN_TIER"
EOF
  PATHS_STATUS="written"
fi

# Backfill preferences block if file existed but predates plan-tier
if [ "$PATHS_STATUS" = "already-exists" ] && ! grep -q "claude_plan:" "$PATHS_LOCAL" 2>/dev/null; then
  printf '\npreferences:\n  claude_plan: "%s"\n' "$PLAN_TIER" >> "$PATHS_LOCAL"
fi
# Windows
$pathsLocal = "$CONFIG_REPO\.claude\paths.local.yaml"
if (Test-Path $pathsLocal) {
  $pathsStatus = "already-exists"
  # Backfill preferences block if missing (old format)
  $content = Get-Content $pathsLocal -Raw
  if ($content -notmatch "claude_plan:") {
    Add-Content $pathsLocal "`npreferences:`n  claude_plan: `"$planTier`""
  }
  # Validate projects_root still exists; correct a stale path (don't re-prompt if fine).
  if ($content -match '(?m)^\s*projects_root:\s*"?([^"\r\n]+)"?') {
    $cur = $Matches[1].Trim().TrimEnd('"')
    if (-not (Test-Path $cur -PathType Container)) {
      # AskUserQuestion (open text input): projects_root "$cur" no longer exists —
      # ask for a new absolute path that must exist. Store in $newRoot.
      (Get-Content $pathsLocal -Raw) -replace '(?m)^(\s*projects_root:).*', "`$1 `"$newRoot`"" | Set-Content $pathsLocal
      $pathsStatus = "updated"
    }
  }
} else {
  # Ask the user via AskUserQuestion (open text input — no default, no fallback):
  #   header: "Projects root"
  #   question: "What is the absolute path to your projects root?
  #              Example: C:\Projects or C:\Users\you\Documents\Projects"
  # Validate: the path must exist as a directory. If not, re-ask.
  # Store as $projectsRoot (no fallback — the user must answer).

  # Ask the user (optional):
  #   header: "Godot executable"
  #   question: "Where is your Godot executable? (optional — press Enter / leave empty to skip)"
  # Store as $godotPath; if empty, omit from file

  New-Item -ItemType Directory -Force -Path "$CONFIG_REPO\.claude" | Out-Null
  $lines = @("paths:", "  projects_root: `"$projectsRoot`"", "  config_repo: `"$CONFIG_REPO`"")
  if ($godotPath) { $lines += "  godot_executable: `"$godotPath`"" }
  $lines += @("preferences:", "  claude_plan: `"$planTier`"")
  $lines -join "`n" | Set-Content $pathsLocal
  $pathsStatus = "written"
}

Note: paths.local.yaml is read by serve-backlog.js (and other shared tools) via lib/config.js fallback — so the backlog server finds your projects automatically after bootstrap without any env var setup. Setting env vars in your shell profile (export CLAUDE_PROJECTS_ROOT="...") remains an override option but is no longer required.


PHASE 1: Copy user-files

Copy 4 files to ~/.claude/. Skip if the destination already exists — never overwrites. Create ~/.claude/ if it is missing.

Report the outcome per file (for the PHASE 3 report):

FileSourceDestination
CLAUDE.md$CONFIG_REPO/local/CLAUDE.md.base~/.claude/CLAUDE.md
settings.json$CONFIG_REPO/local/settings.json.template~/.claude/settings.json
keybindings.json$CONFIG_REPO/local/keybindings.json~/.claude/keybindings.json
statusline-command.cjs$CONFIG_REPO/local/statusline-command.cjs~/.claude/statusline-command.cjs
# macOS/Linux
CLAUDE_DIR="$HOME/.claude"
mkdir -p "$CLAUDE_DIR"
LOCAL="$CONFIG_REPO/local"

declare -A FILES=(
  ["CLAUDE.md"]="$LOCAL/CLAUDE.md.base"
  ["settings.json"]="$LOCAL/settings.json.template"
  ["keybindings.json"]="$LOCAL/keybindings.json"
  ["statusline-command.cjs"]="$LOCAL/statusline-command.cjs"
)

for dest_name in "${!FILES[@]}"; do
  src="${FILES[$dest_name]}"
  dest="$CLAUDE_DIR/$dest_name"
  if [ ! -f "$dest" ]; then
    cp "$src" "$dest"
    # STATUS: placed
  else
    : # STATUS: already-exists
  fi
done
# Windows
$claudeDir = "$env:USERPROFILE\.claude"
New-Item -ItemType Directory -Force -Path $claudeDir | Out-Null
$local = "$CONFIG_REPO\local"

$files = @{
  "CLAUDE.md"               = "$local\CLAUDE.md.base"
  "settings.json"           = "$local\settings.json.template"
  "keybindings.json"        = "$local\keybindings.json"
  "statusline-command.cjs"  = "$local\statusline-command.cjs"
}

foreach ($destName in $files.Keys) {
  $src  = $files[$destName]
  $dest = "$claudeDir\$destName"
  if (-not (Test-Path $dest)) {
    Copy-Item $src $dest
    # STATUS: placed
  } else {
    # STATUS: already-exists
  }
}

Language patch

After copying CLAUDE.md (only if it was actually placed, and LANGUAGE_CHOICE is not skip):

Patch the Language: line in ~/.claude/CLAUDE.md and the "language" field in ~/.claude/settings.json to keep them in sync.

# macOS/Linux — CLAUDE.md (portable sed)
sed -i.bak "s/^Language:.*$/Language: $LANGUAGE_CHOICE/" "$HOME/.claude/CLAUDE.md" && rm -f "$HOME/.claude/CLAUDE.md.bak"

# settings.json — prefer jq; fall back to sed
SETTINGS="$HOME/.claude/settings.json"
if command -v jq >/dev/null 2>&1; then
  jq --arg lang "$LANGUAGE_CHOICE" '.language = $lang' "$SETTINGS" > "$SETTINGS.tmp" && mv "$SETTINGS.tmp" "$SETTINGS"
else
  sed -i.bak "s/\"language\":.*$/\"language\": \"$LANGUAGE_CHOICE\",/" "$SETTINGS" && rm -f "$SETTINGS.bak"
fi
# Windows — CLAUDE.md
(Get-Content "$env:USERPROFILE\.claude\CLAUDE.md") -replace '^Language:.*$', "Language: $LANGUAGE_CHOICE" |
  Set-Content "$env:USERPROFILE\.claude\CLAUDE.md"

# settings.json
$settings = "$env:USERPROFILE\.claude\settings.json"
$json = Get-Content $settings -Raw | ConvertFrom-Json
$json.language = $LANGUAGE_CHOICE
$json | ConvertTo-Json -Depth 10 | Set-Content $settings

Explanation Level patch

After copying CLAUDE.md (only if it was actually placed, and EXPLANATION_CHOICE is not skip):

Patch the Explanation Level: line in ~/.claude/CLAUDE.md.

# macOS/Linux — CLAUDE.md
sed -i.bak "s/^Explanation Level:.*$/Explanation Level: $EXPLANATION_CHOICE/" "$HOME/.claude/CLAUDE.md" && rm -f "$HOME/.claude/CLAUDE.md.bak"
# Windows — CLAUDE.md
(Get-Content "$env:USERPROFILE\.claude\CLAUDE.md") -replace '^Explanation Level:.*$', "Explanation Level: $EXPLANATION_CHOICE" |
  Set-Content "$env:USERPROFILE\.claude\CLAUDE.md"

Permission mode patch

After copying settings.json (only if it was actually placed, and PERM_CHOICE=auto):

The template ships the recommended bypass posture, so the recommended choice needs no patch. The auto-mode choice flips defaultMode to auto, drops the bypass-only flags, and pre-accepts the auto-mode opt-in dialog.

# macOS/Linux — settings.json (jq preferred, sed fallback)
SETTINGS="$HOME/.claude/settings.json"
if command -v jq >/dev/null 2>&1; then
  jq '.permissions.defaultMode = "auto"
      | del(.disableAutoMode)
      | del(.skipDangerousModePermissionPrompt)
      | .skipAutoPermissionPrompt = true' \
     "$SETTINGS" > "$SETTINGS.tmp" && mv "$SETTINGS.tmp" "$SETTINGS"
else
  # sed fallback flips defaultMode only — it cannot reliably remove the disableAutoMode /
  # skipDangerousModePermissionPrompt keys. Install jq for a clean auto-mode switch.
  sed -i.bak 's/"defaultMode":[[:space:]]*"bypassPermissions"/"defaultMode": "auto"/' "$SETTINGS" && rm -f "$SETTINGS.bak"
fi
# Windows — settings.json
$settings = "$env:USERPROFILE\.claude\settings.json"
$json = Get-Content $settings -Raw | ConvertFrom-Json
$json.permissions.defaultMode = "auto"
$json.PSObject.Properties.Remove("disableAutoMode")
$json.PSObject.Properties.Remove("skipDangerousModePermissionPrompt")
$json | Add-Member -NotePropertyName skipAutoPermissionPrompt -NotePropertyValue $true -Force
$json | ConvertTo-Json -Depth 10 | Set-Content $settings

After copying: show a brief reminder:

settings.json copied — verify that hook paths are correct for your platform (see local/README.md).

Show this reminder only if settings.json was actually placed or overwritten.


PHASE 1.5: Personal overlay

Apply user-specific customisations from $CONFIG_REPO/personal/ (gitignored, optional).

# macOS/Linux
PERSONAL_DIR="$CONFIG_REPO/personal"
if [ -d "$PERSONAL_DIR" ]; then
  OVERLAY_COUNT=0

  # 1. CLAUDE.md overlay — append to base
  if [ -f "$PERSONAL_DIR/CLAUDE.md.overlay" ]; then
    cat "$PERSONAL_DIR/CLAUDE.md.overlay" >> "$HOME/.claude/CLAUDE.md"
    OVERLAY_COUNT=$((OVERLAY_COUNT + 1))
  fi

  # 2. settings.overlay.json — deep-merge (right-wins) via jq
  if [ -f "$PERSONAL_DIR/settings.overlay.json" ]; then
    if command -v jq >/dev/null 2>&1; then
      jq -s '.[0] * .[1]' "$HOME/.claude/settings.json" "$PERSONAL_DIR/settings.overlay.json" \
        > "$HOME/.claude/settings.json.tmp" && mv "$HOME/.claude/settings.json.tmp" "$HOME/.claude/settings.json"
      OVERLAY_COUNT=$((OVERLAY_COUNT + 1))
    else
      echo "warn: jq not found — skipping settings overlay merge"
    fi
  fi

  # 3. styles/ — symlink to ~/.claude/styles/
  if [ -d "$PERSONAL_DIR/styles" ]; then
    ln -sfn "$PERSONAL_DIR/styles" "$HOME/.claude/styles"
    OVERLAY_COUNT=$((OVERLAY_COUNT + 1))
  fi

  # STATUS: applied ($OVERLAY_COUNT items) if OVERLAY_COUNT > 0, else found-nothing
else
  # STATUS: not found (optional)
fi
# Windows
$personalDir = "$CONFIG_REPO\personal"
$overlayCount = 0

if (Test-Path $personalDir) {
  # 1. CLAUDE.md overlay
  $overlay = "$personalDir\CLAUDE.md.overlay"
  if (Test-Path $overlay) {
    Get-Content $overlay | Add-Content "$env:USERPROFILE\.claude\CLAUDE.md"
    $overlayCount++
  }

  # 2. settings.overlay.json — deep-merge via jq
  $settingsOverlay = "$personalDir\settings.overlay.json"
  if (Test-Path $settingsOverlay) {
    if (Get-Command jq -ErrorAction SilentlyContinue) {
      $merged = jq -s '.[0] * .[1]' "$env:USERPROFILE\.claude\settings.json" $settingsOverlay
      $merged | Set-Content "$env:USERPROFILE\.claude\settings.json"
      $overlayCount++
    } else {
      Write-Warning "jq not found — skipping settings overlay merge"
    }
  }

  # 3. styles/ — junction to ~/.claude/styles/
  $stylesDir = "$personalDir\styles"
  $stylesTarget = "$env:USERPROFILE\.claude\styles"
  if ((Test-Path $stylesDir) -and (-not (Test-Path $stylesTarget))) {
    cmd /c "mklink /J `"$stylesTarget`" `"$stylesDir`""
    $overlayCount++
  }

  # STATUS: applied ($overlayCount items) if $overlayCount > 0, else found-nothing
} else {
  # STATUS: not found (optional)
}

PHASE 2: Global symlinks / junctions

Link 4 directories: agents, hooks, skills, scripts. Skip if the link already exists (-e check, no overwrite).

# macOS/Linux
for dir in agents hooks skills scripts; do
  target="$HOME/.claude/$dir"
  [ ! -e "$target" ] && ln -sfn "$CONFIG_REPO/$dir" "$target"
  # STATUS: linked (new) or already-exists
done
# Windows
foreach ($dir in @("agents","hooks","skills","scripts")) {
  $target = "$env:USERPROFILE\.claude\$dir"
  if (-not (Test-Path $target)) {
    cmd /c "mklink /J `"$target`" `"$CONFIG_REPO\$dir`""
    # STATUS: linked (new)
  } else {
    # STATUS: already-exists
  }
}

PHASE 2.5: Board background service

Best-effort, silent, non-blocking — sets up the /project-app board's background service (keeps serve-backlog.js warm so the board is one click, no manual server start; see project-app/SKILL.md). A failure here (e.g. Node.js not installed yet) must never stop the rest of bootstrap — this is a nice-to-have, not a core-config requirement.

Todo: Read .claude/skills/shared/APP-INSTALL-CHECK.md and follow it.

Runs after PHASE 2 so the ~/.claude/skills/... symlinks/junctions it depends on already exist. No AskUserQuestion here — installed silently, outcome only surfaces in the PHASE 3 report (matches this repo owner's explicit preference: no extra prompt for this one).

Store the outcome as BOARD_SERVICE_STATUS: installed-now (from INSTALLED_NOW), already-installed (from ALREADY_INSTALLED), or skipped: <reason> (from SKIPPED: <reason>) — for the PHASE 3 report row.


PHASE 2.6: Usage-window opener routine (optional, opt-in)

Best-effort, opt-in — asks once, defaults to Skip. Sets up a daily cloud routine (via the RemoteTrigger harness tool, same mechanism as the /schedule skill) that pings a minimal Haiku turn three times a day so the account's rolling 5-hour usage window opens at predictable anchors instead of drifting with whenever the user happens to first prompt. This is per-account, not per-machine — re-running bootstrap on a second machine must not create a duplicate routine (the reference below dedups by name before creating).

header: "Window opener"
question: "Set up a daily cloud routine that opens your 5h usage window at fixed times (08:00 / 18:00 / 23:00 Europe/Amsterdam, Haiku model)?"
options:
  - label: "Skip (Recommended)"
    description: "Don't create a routine. You can set this up later via /schedule or re-run bootstrap."
  - label: "Create"
    description: "Creates a cloud routine now (uses account quota for 3 minimal Haiku pings/day)."
multiSelect: false

Store the choice in WINDOW_OPENER. If "Skip (Recommended)" → WINDOW_OPENER_STATUS="skipped (declined)", no further action this phase.

If "Create":

Todo: Read .claude/skills/core-bootstrap/references/usage-window-opener.md and follow it.

That reference is fully self-contained (tool loading, account-level dedup, environment/repo resolution, routine creation) and sets WINDOW_OPENER_STATUS to one of: created · already-exists (account) · skipped (<reason>). A failure here must never block PHASE 3.


PHASE 3: Report

Show ASCII table with outcome per item:

Bootstrap complete
══════════════════════════════════════════════════════
 Item                      Status
──────────────────────────────────────────────────────
 ~/.claude/CLAUDE.md        placed
 ~/.claude/settings.json    already-exists
 ~/.claude/keybindings.json already-exists
 ~/.claude/statusline-…     placed
 ~/.claude/agents/          linked
 ~/.claude/hooks/           already-exists
 ~/.claude/skills/          already-exists
 ~/.claude/scripts/         already-exists
 Personal overlay           applied (2 items)
 Language                   English
 Explanation Level          Intermediate
 Permission mode            Bypass permissions
 Claude plan                Max 5x
 Paths (paths.local.yaml)   written
 Board background service   installed
 Window opener routine      created
══════════════════════════════════════════════════════

Statuses: placed · already-exists · linked · error: <reason>

For the Board background service row: installed (from BOARD_SERVICE_STATUS=installed-now), already installed (from already-installed), or skipped (<reason>) (from skipped: <reason> — e.g. skipped (Node.js not found)). This never blocks the rest of the report or a skipped overall bootstrap outcome — it's its own independent row.

For the Window opener routine row: show WINDOW_OPENER_STATUS verbatim (created · already-exists (account) · skipped (declined) · skipped (<reason>)). If created, also show the routine link in the closing tip:

Window opener routine created: https://claude.ai/code/routines/{id} Ticks daily at 08:00 / 18:00 / 23:00 Europe/Amsterdam (fixed UTC — shifts an hour earlier in winter). Adjust anytime via /schedule or https://claude.ai/code/routines.

Show this extra tip block only when WINDOW_OPENER_STATUS="created".

For the Language row: show the chosen language, or the current value with (already set) suffix if LANGUAGE_CHOICE=skip. Read the current value via:

CURRENT_LANG=$(grep "^Language:" "$HOME/.claude/CLAUDE.md" | sed 's/^Language:[[:space:]]*//')

For the Explanation Level row: show the chosen level, or the current value with (already set) suffix if EXPLANATION_CHOICE=skip. Read the current value via:

CURRENT_EXPL=$(grep "^Explanation Level:" "$HOME/.claude/CLAUDE.md" | sed 's/^Explanation Level:[[:space:]]*//')

For the Permission mode row: show Bypass permissions or Auto mode per PERM_CHOICE, or the current defaultMode value with (already set) suffix if PERM_CHOICE=skip. Read the current value via:

CURRENT_MODE=$(jq -r '.permissions.defaultMode' "$HOME/.claude/settings.json" 2>/dev/null)

For the Claude plan row: show the PLAN_TIER label (Max 5x, Pro, Max 10x+, or skipped).

For the Paths row: show written / already-exists.

For the Personal overlay row:

  • personal/ does not exist → not found (optional)
  • personal/ exists, OVERLAY_COUNT > 0 → applied (N items)
  • personal/ exists, OVERLAY_COUNT = 0 → found, nothing to apply

Closing tip (always show):

Next step: open a project and run /core-setup for project-internal setup. To stay current with claude-config updates later: run /core-update.

Plan-tier tip

Based on PLAN_TIER (from PHASE 0), show one of the following blocks. These settings are not auto-applied — they require a runtime /model choice and an explicit edit to ~/.claude/settings.json.

Skills don't depend on a specific model — they use plan mode (shared/PLAN-MODE.md) purely as an approval gate; the session model is opus throughout regardless of plan-mode state. For Max 5x users, /model opus spends Opus quota on the whole session, not just planning — watch usage; drop to /model sonnet if quota runs tight.

  • max-5x:

    Tip for Max 5x:
      • Run /model opus          (Opus for the whole session; watch quota since plan mode no longer switches to Sonnet for execution)
      • Set "effortLevel": "high" in ~/.claude/settings.json
  • pro:

    Tip for Pro — Opus quota is tight:
      • Run /model sonnet (or leave default)
      • Set "effortLevel": "medium" in ~/.claude/settings.json
      Plan mode still works under Sonnet — it's purely an approval gate, not a model-routing device, so there's no separate router to choose.
  • max-10x:

    Tip for Max 10x+ — quota headroom:
      • Run /model opus (full Opus for everything)
      • Set "effortLevel": "high" in ~/.claude/settings.json
  • skip: no plan-tier block shown.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.