Two modes: use init first-time to onboard, then check regularly to verify. Default (no args) → check.
check— fast diagnostic: findsscan-query, verifies index exists and fresh, runs smoke test, audits which skill files have injection block. Prints✓/✗/⚠per check with one-line remediation hints. Pure bash — no model reasoning needed for happy path.init— interactive onboarding: builds index if missing, discovers all installed skills and agents, scores by how much codemap would help, presents recommendation table, asks which to wire in, inserts correct injection block into each selected file.
NOT for: building or rebuilding index (use /codemap:scan-codebase); running structural query (use /codemap:query-code).
Arguments: check (no args) or init [--approve] — --approve auto-applies all ★ recommendations non-interactively.
- $ARGUMENTS: optional — one of:
- Omitted or
check— run diagnostic; print health status for all codemap integration points init— interactive onboarding: build index if missing, discover skills/agents, recommend injection sites, wire in selected filesinit --approve— non-interactive; auto-applies all High+Medium injection recommendations and installs post-commit hook without prompting. ⚠ Scope warning: injects into ALL High+Medium-scored files across all installed plugins (cache files — see I2/I5 warning). Recommended: runinit(interactive) first to review the candidate list before using--approvefor subsequent runs.
- Omitted or
Mode detection
Parse $ARGUMENTS (case-insensitive):
- Starts with
checkor empty → run check mode (Steps C1–C5) - Starts with
init→ run init mode (Steps I0–I6 (I5 has sub-steps I5a, I5b)) - Anything else → use
AskUserQuestion: "Unrecognized command$ARGUMENTS. Which operation did you want?" Options: (a)check— audit integration health, (b)init— onboard codemap interactively (add--approveto auto-apply all recommendations without prompting)
CHECK MODE (Steps C1–C5)
C1 — Locate scan-query
Three-tier fallback (PATH → CLAUDE_PLUGIN_ROOT → newest cache install) handled by bin/locate_scan_query.py.
SQ=$(python "${CLAUDE_PLUGIN_ROOT:-plugins/codemap}/bin/locate_scan_query.py" 2>/dev/null || true) # timeout: 5000
if [ -n "$SQ" ] && [ -x "$SQ" ]; then
printf "✓ scan-query: %s\n" "$SQ"
# Cross-block status persistence (fresh shell per Bash() call — vars don't survive)
echo "ok" > "${TMPDIR:-/tmp}/codemap-c1-status"
else
printf "✗ scan-query: not found\n"
printf " → Install: claude plugin install codemap@borda-ai-rig\n"
echo "failed" > "${TMPDIR:-/tmp}/codemap-c1-status"
exit 1
fi
C2 — PROJ and index existence
# timeout: 5000
# Skip if C1 failed — fresh shell loses C1's exit status, so check sentinel file
C1_STATUS=$(cat "${TMPDIR:-/tmp}/codemap-c1-status" 2>/dev/null || echo "ok")
if [ "$C1_STATUS" = "failed" ]; then
echo "C1 failed — skipping this step."
exit 0
fi
# PROJ/INDEX resolution — also used in Step I1 (init mode); keep in sync
# NOTE: uses single-strategy basename lookup; scan-query uses three-strategy walk-up
# If index not found here but scan-query works, run with explicit --index flag or re-run /codemap:scan-codebase from project root
# bash 3.2 compatible — mapfile is bash 4+ only; macOS ships bash 3.2
_idx=()
while IFS= read -r line; do
_idx+=("$line")
done < <(python "${CLAUDE_PLUGIN_ROOT:-plugins/codemap}/bin/resolve_proj_index.py")
PROJ="${_idx[0]:-}"
INDEX="${_idx[1]:-}"
# Persist for C3/C4 — fresh shell per Bash() call loses bash variables
echo "$INDEX" > "${TMPDIR:-/tmp}/codemap-index"
echo "$PROJ" > "${TMPDIR:-/tmp}/codemap-proj"
printf " project: %s\n index: %s\n" "$PROJ" "$INDEX"
if [ -f "$INDEX" ]; then
printf "✓ index: exists\n"
else
printf "✗ index: not found\n"
printf " → Run /codemap:scan-codebase to build the index\n"
echo "failed" > "${TMPDIR:-/tmp}/codemap-c1-status"
exit 1
fi
C3 — Index freshness (calendar age)
# timeout: 10000
C1_STATUS=$(cat "${TMPDIR:-/tmp}/codemap-c1-status" 2>/dev/null || echo "ok")
if [ "$C1_STATUS" = "failed" ]; then
echo "C1/C2 failed — skipping this step."
exit 0
fi
INDEX=$(cat "${TMPDIR:-/tmp}/codemap-index")
python "${CLAUDE_PLUGIN_ROOT:-plugins/codemap}/bin/check_index_freshness.py" "$INDEX"
C4 — Smoke test and mtime-staleness check
smoke_test_index.py validates that the index file is loadable JSON and reports mtime age vs --max-age-hours (default 24).
C1_STATUS=$(cat "${TMPDIR:-/tmp}/codemap-c1-status" 2>/dev/null || echo "ok")
if [ "$C1_STATUS" = "failed" ]; then
echo "C1/C2 failed — skipping this step."
exit 0
fi
INDEX=$(cat "${TMPDIR:-/tmp}/codemap-index")
SMOKE_RESULT=$(python "${CLAUDE_PLUGIN_ROOT:-plugins/codemap}/bin/smoke_test_index.py" --index-path "$INDEX") # timeout: 10000
OK=$(echo "$SMOKE_RESULT" | jq -r '.ok')
STALE=$(echo "$SMOKE_RESULT" | jq -r '.stale')
AGE=$(echo "$SMOKE_RESULT" | jq -r '.age_hours')
if [ "$OK" != "true" ]; then
ERR=$(echo "$SMOKE_RESULT" | jq -r '.error // "unknown"')
printf "✗ smoke test: %s\n" "$ERR"
printf " → Re-run /codemap:scan-codebase to rebuild index\n"
else
printf "✓ smoke test: index valid (mtime-age=%sh)\n" "$AGE"
if [ "$STALE" = "true" ]; then
printf " ⚠ Index older than freshness threshold — run /codemap:scan-codebase to update\n"
fi
fi
C5 — Skill injection audit
# timeout: 20000
python "${CLAUDE_PLUGIN_ROOT:-plugins/codemap}/bin/check_injection.py" "$CLAUDE_PLUGIN_ROOT"
INIT MODE (Steps I0–I6)
I0 — Detect --approve
--approve in $ARGUMENTS → skip all AskUserQuestion calls, auto-select ★ option for every prompt. Print [--approve] applying recommended options in place of each question. Reasoning instruction — no bash variable needed. All subsequent AskUserQuestion calls follow this automatically.
Unsupported flag check — after extracting supported flags, scan $ARGUMENTS for remaining --<token> tokens. If found: print ! Unknown flag(s): \--<token>`. Supported: `--approve`.then invokeAskUserQuestion` — (a) Abort (stop, re-invoke with correct flags) · (b) Continue ignoring (skip unknown flags, proceed). On Abort: stop.
I1 — Verify or build the index
# timeout: 5000
# PROJ/INDEX resolution (mirrors block in Step C2 — keep in sync)
# bash 3.2 compatible — mapfile is bash 4+ only; macOS ships bash 3.2
_idx=()
while IFS= read -r line; do
_idx+=("$line")
done < <(python "${CLAUDE_PLUGIN_ROOT:-plugins/codemap}/bin/resolve_proj_index.py")
PROJ="${_idx[0]:-}"
INDEX="${_idx[1]:-}"
Index exists: report and proceed. Index missing:
Use AskUserQuestion:
No codemap index found for project: $PROJ
a) Build now ★ — scans all .py files via ast.parse (Python only), <60s on most projects
b) Skip — I'll run /codemap:scan-codebase later (recommendations will be generic, no module-count weighting)
If a (or auto-approved): verify binary exists first, then run scanner:
# timeout: 5000
[ -x "${CLAUDE_PLUGIN_ROOT:-plugins/codemap}/bin/scan-index" ] || { printf "✗ scan-index not found at ${CLAUDE_PLUGIN_ROOT:-plugins/codemap}/bin/scan-index\nTry: /codemap:scan-codebase to install and rebuild.\n"; exit 1; }
# timeout: 360000
${CLAUDE_PLUGIN_ROOT:-plugins/codemap}/bin/scan-index
Report result (module count, degraded count). If b: note "Proceeding without index — recommendations based on skill purpose only, not module count."
I2 — Discover installed skills and agents
Read ~/.claude/plugins/installed_plugins.json (Claude Code internal plugin registry — format may change across versions; fallback: glob ~/.claude/plugins/cache/*/*/ if file absent or unreadable). For each plugin entry, check installPath key present before accessing; if absent, log `inst