Bootstrap
Session startup skill — pre-flight checks, drift/update offers, and initial routing.From its SKILL.md
npx -y skills add carson-sweet/sweetclaude --skill bootstrapAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 6 stars6 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
24.2 KB, as published. Nobody here has run it
SweetClaude
Load state, then make a decision. Delegate.
cat .sweetclaude/state/sweetclaude.yaml 2>/dev/null || echo "SC_YAML_NOT_FOUND"
If the output is SC_YAML_NOT_FOUND, the state file does not exist. Otherwise parse the output as YAML.
Step 0: Pre-flight
Run the pre-flight helper to resolve runner path, plugin state, and version-dir repair.
eval "$(bash "${CLAUDE_PLUGIN_ROOT}/scripts/preflight.sh" 2>/dev/null)"
RUNNER is now set (empty if not found). VERSION_DIR_HEALED=true if the install directory was just repaired to a version-named path.
If VERSION_DIR_HEALED=true, print exactly this before continuing:
SweetClaude install repaired.
─────────────────────────────
Your plugin directory was misaligned with the installed version. It has been
corrected automatically. Please restart Claude Code — your full skill set will
be available in the next session.
Then stop. Do not continue past Step 0 in this session — the restart is required for the repair to take effect.
If SC_PLUGIN_CHANNEL_RETIRED=true, the installed plugin is on a retired
channel (ISSUE-241: the beta channel is retired; its branch is archived and
its marketplace no longer serves updates). Print this message with the
variables substituted, then stop before any project maintenance, migration,
doctor, setup, or recovery routing:
SweetClaude channel retired.
────────────────────────────
Installed plugin: {SC_PLUGIN_KEY}
Installed version: {SC_PLUGIN_VERSION}
Channel: {SC_PLUGIN_CHANNEL} (retired — no further updates)
One-time switch to the {SC_PLUGIN_RETIREMENT_TARGET_CHANNEL} channel — run
these in order so you are never double-installed (both channels' skills and
hooks load at once):
/plugin marketplace add carson-sweet/sweetclaude@main
/plugin install sweetclaude@sweetclaude-stable
/plugin marketplace remove sweetclaude-beta
(Removing the retired marketplace uninstalls its plugin, so add and install
the new channel first.) Then restart Claude Code and run /sweetclaude:update;
your project data migrates normally. No project files were changed.
Then stop. Do not invoke any project-mutating skill from this retired-channel stop path.
If SC_PLUGIN_STALE_BETA=true (and the retired-channel stop above did not
fire), print this message with the variables substituted, then stop before any project
maintenance, migration, doctor, setup, or recovery routing:
SweetClaude beta plugin update required.
──────────────────────────────────────
Installed plugin: {SC_PLUGIN_KEY}
Installed version: {SC_PLUGIN_VERSION}
Minimum safe beta: {SC_PLUGIN_MIN_SAFE_BETA_VERSION}
Run this Claude Code command:
{SC_PLUGIN_UPDATE_COMMAND}
Then restart Claude Code and run:
/sweetclaude:update
Stopping here because this installed beta is old enough to have unsafe
update/recovery behavior. No project files were changed.
Do not invoke /sweetclaude:update, /sweetclaude:doctor,
/sweetclaude:recover, /sweetclaude:migrate, _migrate, setup, purge, or
any project-mutating skill from this stale-beta stop path.
Step 1: Handle missing or unparseable file
If the pre-loaded content is SC_YAML_NOT_FOUND:
ls .sweetclaude/state/phase.yaml .sweetclaude/state/skills.yaml 2>/dev/null | wc -l
- Count > 0 → invoke
sweetclaude:_migratethen stop (migration tells user to re-run) - Count = 0 → invoke
sweetclaude:setupthen stop
If the pre-loaded content exists but fails YAML parsing (malformed — check by attempting to interpret it as a YAML structure):
"Something in my config got scrambled. Let me fix it." Invoke
sweetclaude:fix-sweetclaude. Stop.
Step 2: Schema version check
Read schema_version from pre-loaded state.
If schema_version is not in {1, 2}:
- Invoke
sweetclaude:_migrate --schema-upgrade - Stop (migration tells user to re-run)
If schema_version is 1, the registry-driven runner will pick up the v1→v2 migration during the Step 5c drift scan. Bootstrap continues — it does not need to short-circuit here.
Step 3: Check migration status
Read framework.migration_status.
in_progressorfailed→ invokesweetclaude:_migrate(retry). Stop.completeornull→ continue.
Step 4: Stale hook check
Read framework.hook_last_ran.
If null or more than 2 hours ago:
- Invoke
sweetclaude:_healthto run checks inline - Re-read updated values from file before continuing
To check if stale:
python3 -c "
import yaml
from datetime import datetime, timezone, timedelta
d = yaml.safe_load(open('.sweetclaude/state/sweetclaude.yaml')) or {}
ts = d.get('framework', {}).get('hook_last_ran')
if not ts:
print('STALE')
exit()
try:
t = datetime.fromisoformat(str(ts).replace('Z','+00:00'))
stale = (datetime.now(timezone.utc) - t) > timedelta(hours=2)
print('STALE' if stale else 'FRESH')
except:
print('STALE')
"
Step 5: Check setup complete
Read framework.setup_complete.
If false:
- Invoke
sweetclaude:setup. Stop.
Step 5b: Maintenance guard — v3 artifacts and compatibility states
After confirming setup is complete, decide whether the recovery guard needs to run, in one of two modes:
- hard-stop — a v4 plugin is running against a project that hasn't migrated
yet (project
installed_versionbelow 4.x, or v3BL-*.mdfiles present). Unsafe to continue; the session stops after routing. - advisory — the project is v4 but sits in a compatibility/recovery state
(
recovery.taxonomy.status: stabilized-without-migration), or its product tree still holds old-taxonomy files (STORY-/BUG-/DEBT-/CHORE-) despite the state claiming migration is complete. Graduation opportunities, blockers, and recoverable states are only visible through the guard, so it runs at session start, routes to an action, and the session continues.
Every status the guard can emit has a route below. There are no unrouted statuses: bootstrap either offers the resolving action or explains honestly why none exists.
# Resolve product_base from artifact-privacy.yaml; fall back to .sweetclaude/product
PRODUCT_BASE=$(python3 -c "
import yaml, pathlib
p = pathlib.Path('.sweetclaude/artifact-privacy.yaml')
if p.exists():
d = yaml.safe_load(p.read_text()) or {}
base = d.get('categories', {}).get('product', {}).get('base_path', '')
if base:
print(base.rstrip('/'))
exit()
print('.sweetclaude/product')
" 2>/dev/null || echo '.sweetclaude/product')
# Detect v4 plugin: plugin version ≥ 4.x, project installed_version < 4.x
PLUGIN_V=$(python3 -c "
import json, os, re
try:
d = json.load(open(os.path.expanduser('~/.claude/plugins/installed_plugins.json')))
entries = [e for versions in d.get('plugins', {}).values()
for e in versions if e.get('scope') == 'user']
for e in sorted(entries, key=lambda x: x.get('lastUpdated',''), reverse=True):
v = e.get('version','')
if re.match(r'^4\.', v) and 'sweetclaude' in str(e.get('installPath','')).lower():
print(v)
break
except Exception:
pass
" 2>/dev/null)
PROJECT_V=$(python3 -c "
import yaml
d = yaml.safe_load(open('.sweetclaude/state/sweetclaude.yaml')) or {}
print(d.get('framework', {}).get('installed_version', ''))
" 2>/dev/null)
V3_FILES=$(find "${PRODUCT_BASE}/backlog" -maxdepth 1 -name 'BL-*.md' 2>/dev/null | wc -l | tr -d ' ')
PLUGIN_IS_V4=false
case "$PLUGIN_V" in 4.*) PLUGIN_IS_V4=true ;; esac
PROJECT_NOT_V4=false
case "$PROJECT_V" in 4.*) ;; *) PROJECT_NOT_V4=true ;; esac
COMPAT_STATE=$(python3 -c "
import yaml
d = yaml.safe_load(open('.sweetclaude/state/sweetclaude.yaml')) or {}
taxonomy = (d.get('recovery') or {}).get('taxonomy') or {}
print('yes' if taxonomy.get('status') == 'stabilized-without-migration' else 'no')
" 2>/dev/null || echo no)
LEGACY_FILES=$(find "${PRODUCT_BASE}" \( -name 'STORY-*.md' -o -name 'BUG-*.md' -o -name 'DEBT-*.md' -o -name 'CHORE-*.md' \) 2>/dev/null | wc -l | tr -d ' ')
GUARD_MODE=none
if $PLUGIN_IS_V4 && ( $PROJECT_NOT_V4 || [ "$V3_FILES" -gt 0 ] ); then
GUARD_MODE=hard-stop
elif [ "$COMPAT_STATE" = "yes" ] || [ "$LEGACY_FILES" -gt 0 ]; then
GUARD_MODE=advisory
fi
GUARD_STATUS=""
if [ "$GUARD_MODE" != "none" ]; then
SCRIPT=${CLAUDE_PLUGIN_ROOT}/scripts/recovery/recover_project.py
if [ ! -f "$SCRIPT" ]; then
SCRIPT=$(find "$(dirname "${CLAUDE_PLUGIN_ROOT}")" -type f -path '*/scripts/recovery/recover_project.py' 2>/dev/null | sort -V | tail -1)
fi
if [ -n "$SCRIPT" ] && [ -f "$SCRIPT" ]; then
GUARD_JSON=$(python3 "$SCRIPT" guard --project-dir . 2>/dev/null)
GUARD_STATUS=$(printf '%s' "$GUARD_JSON" | python3 -c "
import json, sys
try:
print(json.load(sys.stdin).get('status', ''))
except Exception:
pass
" 2>/dev/null)
fi
[ -z "$GUARD_STATUS" ] && GUARD_STATUS=guard-unavailable
echo "GUARD_MODE=$GUARD_MODE"
echo "GUARD_STATUS=$GUARD_STATUS"
printf '%s\n' "$GUARD_JSON"
fi
If GUARD_MODE=none: continue to Step 5c.
Otherwise route on GUARD_STATUS. In hard-stop mode, stop after routing
(the project must not run normal skills against unmigrated v3 artifacts). In
advisory mode, continue to Step 5c after the route resolves or the user
declines.
graduation-available — the project is v4-compliant and one state-only
write away from leaving compatibility mode. Present via AskUserQuestion:
This project is v4-compliant and can graduate from compatibility mode now. Graduation is a state-only write — no work-item files change.
Options:
- Graduate now (Recommended) — invoke
sweetclaude:doctor; its maintenance route runs graduation-check and graduates with archive and rollback support. - Not now — continue in compatibility mode.
graduation-blocked — fixable validation blockers stand between this
project and graduation. List each entry from the guard's
graduation_blockers array as - {code}: {detail}, then present via
AskUserQuestion:
This project could graduate from compatibility mode, but {N} blocker(s) must be fixed first. Fixes run through Doctor's executor — archived and reversible.
Options:
- Fix blockers and graduate (Recommended) — invoke
sweetclaude:doctor; itsgraduation-blockedroute resolves each blocker, re-checks, and graduates. - Not now — continue in compatibility mode.
run-recover — a recoverable maintenance state. Present via
AskUserQuestion: Run recovery now (Recommended) (invoke
sweetclaude:recover) or Not now. In hard-stop mode, stop after the
choice resolves; do not run normal skills until recovery completes.
migration-may-be-needed — flat v3 BL-*.md items with no recovery
state. Tell the user: "Old-format work items were found. Run
/sweetclaude:migrate to review the migration — it runs its own preflight
and safety steps." Stop (hard-stop mode only fires here).
supported-migration-available — a typed legacy backlog layout that can
now be migrated. Tell the user: "This project has a typed legacy backlog
layout. Run /sweetclaude:migrate to migrate to the unified ISSUE-NNN
taxonomy — it handles typed backlog folders (stories/, bugs/, debt/, chores/)
and creates a rollback snapshot before writing anything." Stop in hard-stop
mode; in advisory mode offer migration or continue.
compatibility-mode — legacy taxonomy layout that can be migrated.
Print one line: "This project is in compatibility mode. Run
/sweetclaude:migrate to convert to v4 layout, or /sweetclaude:recover
to check graduation eligibility." In advisory mode continue the session.
manual-review — the guard cannot classify the project safely. Print the
guard message, then: "Run /sweetclaude:doctor for a full diagnostic scan —
it can characterize states the guard cannot." Stop in hard-stop mode.
missing-product-base — artifact-privacy.yaml points at a product
directory that does not exist. Print the configured path from the guard
payload and: "Fix the base_path in .sweetclaude/artifact-privacy.yaml or
restore the directory, then restart. /sweetclaude:doctor can diagnose
this." Stop in hard-stop mode.
guard-unavailable — the guard script is missing from the install. Print:
"Recovery guard unavailable — the install is incomplete. Run
/sweetclaude:update to repair it, then restart." Stop in hard-stop mode.
ok — nothing to do; continue to Step 5c.
Recommend /sweetclaude:migrate for statuses migration-may-be-needed,
supported-migration-available, and compatibility-mode.
Step 5c: Artifact-format drift check (hard demand)
Runs whenever Step 5b did not stop the session (guard mode none, or an advisory route that resolved or was declined). Catches steady-state schema drift on projects that are already at v4 but have schema versions behind the registry's current.
The drift-gate.sh SessionStart hook has already scanned for drift and written the marker if any was found. Read the marker first; if it exists, use it as the source of truth (drift-gate's scan is authoritative for the session). If the marker is absent (drift-gate didn't run, e.g. versionless path was just self-healed in Step 0), run the runner inline and parse its stdout directly — do NOT rely on the runner writing the marker (it only writes to stdout via --report-drift-for-skill).
DRIFT_MARKER=".sweetclaude/state/pending-drift-decision.yaml"
DRIFT_COUNT=0
CASE=A
if [ -f "$DRIFT_MARKER" ]; then
EXISTING=$(python3 -c "
import yaml, sys
try:
d = yaml.safe_load(open(sys.argv[1])) or {}
print(d.get('case', 'A'))
print(d.get('drift_count', 0))
except Exception:
print('A')
print(0)
" "$DRIFT_MARKER" 2>/dev/null)
CASE=$(printf '%s\n' "$EXISTING" | sed -n '1p')
DRIFT_COUNT=$(printf '%s\n' "$EXISTING" | sed -n '2p')
elif [ -n "$RUNNER" ] && [ -f "$RUNNER" ]; then
DRIFT_OUTPUT=$(python3 "$RUNNER" --project-dir . --report-drift-for-skill 2>/dev/null)
DRIFT_COUNT=$(printf '%s\n' "$DRIFT_OUTPUT" | grep '^DRIFT_COUNT=' | cut -d= -f2)
[ -z "$DRIFT_COUNT" ] && DRIFT_COUNT=0
if printf '%s\n' "$DRIFT_OUTPUT" | grep -q '|chain=broken'; then
CASE=B
fi
fi
echo "CASE=$CASE"
echo "DRIFT_COUNT=$DRIFT_COUNT"
If DRIFT_COUNT is 0: continue to Step 6.
If DRIFT_COUNT > 0: the binary prompt depends on CASE.
Case A (CASE=A — all chains ok): present via AskUserQuestion (single-select, no "Something else"):
"{DRIFT_COUNT} SweetClaude state file(s) in this project need migration before SweetClaude can continue. Would you like to do this now?"
Options:
- Migrate now — invoke
sweetclaude:_migrateto bring artifacts up to current.- Remove SweetClaude from this project (re-onboarding required to reactivate) — invoke
sweetclaude:purge.
Case B (CASE=B — at least one chain broken, out of 3-major support window): present via AskUserQuestion (single-select):
"This project's SweetClaude state files are too old for automatic migration (out of framework support window). How would you like to proceed?"
Options:
- Re-onboard from scratch — archive existing SweetClaude content and run
/sweetclaude:initagainst a fresh state. Your old files stay as reference; init does not auto-import them.- Remove SweetClaude from this project (re-onboarding required to reactivate) — invoke
sweetclaude:purge.
Re-onboarding flow (Case B → Re-onboard from scratch)
Archive .sweetclaude/ aside through the re-adopt script (reversible, no inline
mv), then mirror any relocated artifact bases into the same legacy tree:
RA=$(python3 ${CLAUDE_PLUGIN_ROOT}/scripts/recovery/re_adopt.py execute --project-dir .)
LEGACY=$(python3 -c "import json,sys;print(json.loads(sys.stdin.read())['legacy_path'])" <<<"$RA")
python3 ${CLAUDE_PLUGIN_ROOT}/scripts/maintenance/archive-sweetclaude-dir.py "$LEGACY"
echo "Archived existing SweetClaude content to $LEGACY/ — init will use it as reference, not auto-migrate."
re_adopt.py execute moves .sweetclaude/ into .sweetclaude.legacy/<timestamp>/
and reports legacy_path; the archive helper then relocates the artifact bases
(e.g. docs/product) under that same path.
Then invoke sweetclaude:init. Init runs against the now-empty project and
re-onboards the existing source. The legacy tree at
.sweetclaude.legacy/<timestamp>/ is visible to the user during onboarding so
they can manually port content as needed; init does not auto-import it.
There is no "Not now" option in either case. No third path. Stop after the user picks.
Locked in scratch/v3-upgrade-assessment-2026-05-11/DECISIONS.md Gaps #7 and #8.
Step 6: Drift and update offers
Read framework.consistency.status.
If drift_detected:
python3 -c "
import yaml
d = yaml.safe_load(open('.sweetclaude/state/sweetclaude.yaml')) or {}
drift = d.get('framework',{}).get('consistency',{}).get('drift',[])
print(', '.join(drift) if drift else 'configuration drift detected')
"
"I found some drift: [drift list]. Fix it now? (Yes / No)"
- Yes → invoke
sweetclaude:fix-sweetclaude. Stop. - No → continue.
Read framework.update.available, framework.update.declined, and framework.installed_version from pre-loaded state. Apply the version-aware decline rule (Gap #1 #8, locked in scratch/v3-upgrade-assessment-2026-05-11/DECISIONS.md):
availableis null → no offer.availableis a prerelease (-beta/-rc/-alpha) while the installed version is not → no offer, ever (ISSUE-248: stable installs are never prompted with prerelease versions, regardless of what an older hook wrote to state).availableis non-null:- Compute
is_major_bump = major(available) > major(installed_version). - If
is_major_bumpis true → always prompt, regardless ofdeclined. - Else (minor/patch within installed major):
declinedis null/missing/false → prompt.declinedis the booleantrue(legacy) → treat as if user declined the installed major (silence until next major).declinedis a version string → prompt only ifmajor(available) > major(declined). Otherwise silent.
- Compute
python3 - .sweetclaude/state/sweetclaude.yaml << 'PY'
import sys, yaml, re
path = sys.argv[1]
with open(path) as f: d = yaml.safe_load(f) or {}
fw = d.get("framework") or {}
installed = (fw.get("installed_version") or "").lstrip("v")
upd = fw.get("update") or {}
available = upd.get("available")
declined = upd.get("declined")
def major(v):
if not isinstance(v, str): return None
m = re.match(r"^(\d+)\.", v.lstrip("v"))
return int(m.group(1)) if m else None
def is_prerelease(v):
return bool(re.search(r"-(?:beta|rc|alpha)", str(v or ""), re.IGNORECASE))
if not available:
print("DECISION=silent"); sys.exit()
# ISSUE-248: never prompt a stable install with a prerelease version, no
# matter what an older health-check hook left in framework.update.available.
if is_prerelease(available) and not is_prerelease(installed):
print("DECISION=silent"); sys.exit()
inst_maj, avail_maj = major(installed), major(available)
if inst_maj is None or avail_maj is None:
print("DECISION=prompt"); sys.exit()
if avail_maj > inst_maj:
print("DECISION=prompt"); sys.exit()
if declined in (None, False):
print("DECISION=prompt"); sys.exit()
declined_maj = inst_maj if declined is True else major(str(declined))
if declined_maj is None or avail_maj > declined_maj:
print("DECISION=prompt"); sys.exit()
# Silenced by decline. Emit the installed major so the caller can compose
# the user-facing message ("all 3.x.x updates will be skipped").
print(f"DECISION=silent_declined|{inst_maj}")
PY
-
DECISION=silent → no update available; continue silently past this section.
-
DECISION=silent_declined|<major> → user previously declined a minor in this major. Surface a brief informational message so the user is not silently kept in the dark, then continue past this section. The message MUST use the major from the decision output (parameterized — it is not always
3):"Minor updates were previously declined by the user, so all <major>.x.x updates will be skipped. Run
/sweetclaude:updateto manually update at any time."Show this message ONCE per session, then continue to Step 7. Do not pause for input — the user is being informed, not prompted.
-
DECISION=prompt → present:
"SweetClaude [available] is out. Update now? (Yes / Not now)"
- Yes → invoke
sweetclaude:update. Stop. - Not now → write
declined: <available>(the specific version declined), continue:
- Yes → invoke
python3 ${CLAUDE_PLUGIN_ROOT}/scripts/maintenance/write-decline.py .
Step 6b: Doctor checkup prompt
Check if a doctor checkup prompt is pending:
DOCTOR_MARKER=".sweetclaude/state/doctor-prompt-pending.json"
if [ -f "$DOCTOR_MARKER" ]; then
python3 -c "
import json, sys
try:
d = json.load(open(sys.argv[1]))
print('TRIGGER=' + d.get('trigger', 'unknown'))
except Exception:
print('TRIGGER=unknown')
" "$DOCTOR_MARKER" 2>/dev/null
fi
If the marker exists, present a prompt based on the trigger type via AskUserQuestion:
- Time trigger (
TRIGGER=time): "It's been a while since your last checkup. Want me to run one?" - Update trigger (
TRIGGER=update): "You just updated SweetClaude — want to run a quick checkup?" - Migration trigger (
TRIGGER=migration): "Migration just completed — want to run a quick checkup?" - Unknown/other (
TRIGGER=unknownor any other value): "Want to run a quick checkup?"
Options:
- Yes — invoke
sweetclaude:doctor, then remove the marker file - Not now — remove the marker file (session-scoped suppression; the health-check hook rewrites it next session if the condition still holds)
Remove the marker regardless of choice:
rm -f "$DOCTOR_MARKER" 2>/dev/null || true
If the marker does not exist, continue silently.
Step 7: Route on args
If $ARGUMENTS is present and non-empty:
→ invoke sweetclaude:_route with $ARGUMENTS
Stop.
Step 8: All-clear status surface
Set the bootstrap-ran session flag so the master-preflight hook knows bootstrap has completed this session:
_SC_PROJ=$(git rev-parse --show-toplevel 2>/dev/null || echo "$PWD")
_SC_HASH=$(printf '%s' "$_SC_PROJ" | md5 2>/dev/null \
|| printf '%s' "$_SC_PROJ" | md5sum 2>/dev/null | cut -d' ' -f1)
touch "/tmp/.sweetclaude-bootstrap-ran-${_SC_HASH}"
Record a session_start event if metrics are enabled:
_SC_METRICS_CONFIG=".sweetclaude/metrics/config.yaml"
_SC_EVENTS_LOG=".sweetclaude/metrics/events.log"
if [ -f "$_SC_METRICS_CONFIG" ] && grep -q "enabled: true" "$_SC_METRICS_CONFIG" 2>/dev/null; then
_SC_PHASE=$(python3 -c "
import yaml
d = yaml.safe_load(open('.sweetclaude/state/sweetclaude.yaml')) or {}
print(d.get('work', {}).get('active', {}).get('phase') or 'none')
" 2>/dev/null || echo "none")
_SC_DEFERENCE=$(python3 -c "
import yaml
d = yaml.safe_load(open('.sweetclaude/state/sweetclaude.yaml')) or {}
print(d.get('session', {}).get('deference_level') or 'unknown')
" 2>/dev/null || echo "unknown")
_SC_TS=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
printf -- '---\ntimestamp: %s\nevent: session_start\nphase: %s\ndeference: %s\n' \
"$_SC_TS" "$_SC_PHASE" "$_SC_DEFERENCE" >> "$_SC_EVENTS_LOG"
fi
Read from pre-loaded state:
python3 - << 'PY'
import yaml, sys
sc_path = '.sweetclaude/state/sweetclaude.yaml'
with open(sc_path) as f:
d = yaml.safe_load(f) or {}
p = d.get('project', {})
w = d.get('work', {})
h = d.get('work_history', [])
name = p.get('name') or 'this project'
stage = p.get('version_stage', '')
sc_ver = d.get('framework', {}).get('installed_version', '')
active = w.get('active', {}) or {}
last3 = h[:3]
ver_str = f"SweetClaude {sc_ver} · " if sc_ver else ""
print(f"{ver_str}**{name}** · {stage}")
if active.get('title'):
print(f"Active: {active['title']} [{active.get('phase','')}]")
elif last3:
print(f"Last completed: {last3[0].get('title','')} ({last3[0].get('outcome','')})")
else:
print("No work history yet.")
PY
Show the output, then ask:
"Want to work on something, or review the current plan?"
- "Work on something" → prompt for description →
sweetclaude:_route - "Review the plan" → "Roadmap · Backlog · Open work · Bugs — which?"