doctor

doctor is a command for coding agents from komluk/scaffolding. It costs 24 tokens per session (3,692 once invoked), scanned A, original, MIT.

A read-only health-check command for a scaffolding installation. Scaffolding is the starter setup that creates or configures a project and its add-ons.

In plain words
What is it for?
Run /doctor from any project to check the installation, group problems by severity, and receive suggested fixes.
Why use it?
It finds documented installation problems and prints the exact commands needed to fix them, without changing project files or restarting anything.

Command

Installs and runs on its own, but its text points at files inside its plugin — anything it tells you to read at a ${CLAUDE_PLUGIN_ROOT} path is only there once the plugin is installed. Installing the plugin gets both.

Part of the scaffolding plugin — 35 skills, 9 commands, 13 agents shipped together

Install

Getting it into your agent

One page per mod, every tool's command on it. A separate URL per tool would split the same page into five that compete with each other.

agentmods
npx agentmods add commands/komluk/scaffolding/doctor
Clone the repo
git clone --depth 1 https://github.com/komluk/scaffolding

Or install scaffolding, the plugin that ships this one along with the rest of its 35 skills, 9 commands, 13 agents.

Wrote this? Show the measurements

A badge with what this costs and how it scanned, read live from this page, so it follows the numbers instead of freezing them. Markdown for a README, HTML for a documentation site or a project page.

agentmods badge for doctor

README.md
[![agentmods](https://agentmods.dev/badge/commands/komluk/scaffolding/doctor.svg)](https://agentmods.dev/commands/komluk/scaffolding/doctor)
Your own site
<a href="https://agentmods.dev/commands/komluk/scaffolding/doctor"><img src="https://agentmods.dev/badge/commands/komluk/scaffolding/doctor.svg" alt="Measured on agentmods" height="20"></a>
Per session 24 Only the description is in the session, so the agent can decide to use it. The body loads when it is invoked.
When invoked 3,692 The whole file, excluding the scripts and references it only reads on demand.
Security scan A 0 findings. Scan, not verified.
Origin original No closer match found in the catalogue.
Token cost

What it costs to keep this loaded

Counted locally with the o200k_base tokenizer, which is exact for GPT models; Claude uses its own tokenizer and its counts differ. Treat this as one consistent yardstick across the catalogue rather than a bill. Prices are per million input tokens.

ModelPer sessionOnce invoked
Fable 5 $0.00024 $0.03692
Opus 5 $0.00012 $0.01846
Sonnet 5 $0.00005 $0.00738
Haiku 4.5 $0.00002 $0.00369

Measured 5d ago against content hash 4cb3101c1a5f, method: parsed. Prices are Anthropic first-party input rates as of 2026-08-30, from the pricing page.

Security

Grade A, and why

doctor scanned grade A with 0 findings against 26 rules in 11 categories — prompt injection, anti-refusal, data exfiltration, privilege escalation, supply chain, agent snooping, system-prompt leakage, SSRF and excessive agency — measured 5d ago.

A static scan of the body, not an audit. Every finding is printed with the line that produced it so you can judge whether it matters here. A mod is markdown that instructs an agent; that is exactly why what it instructs is worth reading.

Nothing flagged

None of the 26 patterns this scan looks for appear in this file: no shell pipes, no recursive deletes, no credential paths, no hidden text, no instruction-override or anti-refusal phrasing, no agent-config snooping. That is not a guarantee, it is the absence of the things that are checkable.

commands/doctor.md · 297 lines

How it starts

The opening of the file, as written. The whole thing — 297 lines — stays where its author put it; the contents beside it link to each section on GitHub.

/doctor Command

Run a health check of the scaffolding plugin install and report problems with the exact command to fix each one. Diagnoses the documented first-run gotchas (the #1 being Agent type 'scaffolding:...' not found after a fresh install) and groups findings by severity.

Usage

/doctor

Run from any project. No arguments. Safe to run repeatedly.

Guardrail — DIAGNOSE ONLY (never mutate)

This command only diagnoses and prints fixes. It MUST NEVER:

  • run /reload-plugins, /plugin install/update, or restart anything itself
  • edit settings.json, CLAUDE.md, .gitignore, or any user/project file
  • chmod, mkdir, cp, register an MCP server, or set an env var

It only reads files, runs read-only shell, and spawns one trivial probe agent (check #2). Print the exact command for the user to run — matches the propose-don't-mutate posture of /memory.

Steps

Follow these steps exactly, in order.

Step A — Run the inline health-check bash block

Run the block below verbatim. It performs the file/shell checks (#1, #3–#10) and prints a grouped report. It writes nothing.

# ---- scaffolding /doctor — read-only health check ----
BLOCKING=0; RECOMMENDED=0; OPTIONAL=0
pass() { printf '  [PASS] %s\n' "$1"; }
fail() { printf '  [FAIL] %s\n        fix: %s\n' "$1" "$2"; }

# Locate the installed plugin root (same multi-base discovery as /init-scaffolding)
PLUGIN_ROOT=""
find_plugin_root() {
  local base="$1"; [ -d "$base" ] || return
  local latest
  latest=$(find "$base" -name "CLAUDE.md" -path "*/scaffolding/*/CLAUDE.md" 2>/dev/null | sort -V | tail -1 | xargs dirname 2>/dev/null || true)
  if [ -n "$latest" ] && [ -f "$latest/CLAUDE.md" ]; then echo "$latest"; return; fi
  latest=$(find "$base" -name "CLAUDE.md" 2>/dev/null | sort -V | tail -1 | xargs dirname 2>/dev/null || true)
  if [ -n "$latest" ] && [ -f "$latest/CLAUDE.md" ]; then echo "$latest"; return; fi
}
# Prefer the env var Claude Code injects when the plugin is loaded
if [ -n "${CLAUDE_PLUGIN_ROOT:-}" ] && [ -f "${CLAUDE_PLUGIN_ROOT}/CLAUDE.md" ]; then
  PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT}"
fi
if [ -z "$PLUGIN_ROOT" ]; then
  for base in \
    "$HOME/.claude/plugins/cache/komluk-scaffolding" \
    "$HOME/.claude/plugins/marketplaces/komluk-scaffolding" \
    "${USERPROFILE:-}/.claude/plugins/cache/komluk-scaffolding" \
    "${LOCALAPPDATA:-}/claude/plugins/cache/komluk-scaffolding"; do
    [ -n "$base" ] || continue
    found=$(find_plugin_root "$base")
    if [ -n "$found" ]; then PLUGIN_ROOT="$found"; break; fi
  done
fi

echo "=== BLOCKING (install will not work until these pass) ==="

# Check 1 — plugin installed / loaded
if [ -n "$PLUGIN_ROOT" ] && [ -f "$PLUGIN_ROOT/CLAUDE.md" ]; then
  if [ -n "${CLAUDE_PLUGIN_ROOT:-}" ]; then
    pass "Plugin installed & loaded (CLAUDE_PLUGIN_ROOT set; root: $PLUGIN_ROOT)"
  else
    pass "Plugin installed (cache root: $PLUGIN_ROOT)"
    echo "        note: CLAUDE_PLUGIN_ROOT not set in this shell — fine for /doctor, but if hooks misbehave, restart Claude Code."
  fi
else
  BLOCKING=$((BLOCKING+1))
  fail "Plugin not installed / not found" "/plugin marketplace add komluk/scaffolding   then   /plugin install scaffolding@komluk-scaffolding"
fi

# Check 1b — agent files present (13) — needed for resolution
if [ -n "$PLUGIN_ROOT" ]; then
  AGENT_COUNT=$(find "$PLUGIN_ROOT/agents" -maxdepth 1 -name '*.md' 2>/dev/null | wc -l | tr -d ' ')
  if [ "$AGENT_COUNT" = "13" ]; then
    pass "All 13 agent definitions present in plugin root"
  else
    BLOCKING=$((BLOCKING+1))
    fail "Incomplete install — found ${AGENT_COUNT:-0}/13 agent files" "/plugin update scaffolding@komluk-scaffolding   then   /reload-plugins"
  fi
fi

# Check 4 — plugin.json valid + hooks registered
if [ -n "$PLUGIN_ROOT" ]; then
  PJ="$PLUGIN_ROOT/.claude-plugin/plugin.json"
  [ -f "$PJ" ] || PJ="$PLUGIN_ROOT/plugin.json"
  if [ -f "$PJ" ] && python3 -c "import json,sys; d=json.load(open(sys.argv[1])); assert d.get('hooks')" "$PJ" 2>/dev/null; then
    pass "plugin.json valid and hooks registered"
  else
    BLOCKING=$((BLOCKING+1))
    fail "plugin.json missing/invalid or no hooks registered" "/plugin update scaffolding@komluk-scaffolding   then   /reload-plugins"
  fi
fi

echo ""
echo "=== RECOMMENDED (protocol / hooks / memory tiers) ==="

# Check 3a — settings.json present in plugin root
if [ -n "$PLUGIN_ROOT" ] && [ -f "$PLUGIN_ROOT/settings.json" ] \
   && python3 -c "import json,sys; json.load(open(sys.argv[1]))" "$PLUGIN_ROOT/settings.json" 2>/dev/null; then
  pass "settings.json present and valid in plugin root"
else
  RECOMMENDED=$((RECOMMENDED+1))
  fail "settings.json missing or unparseable in plugin root" "/plugin update scaffolding@komluk-scaffolding (reinstall)"
fi

# Check 3b — hook .sh files present + executable (9 plugin-registered hooks; refresh-mcp-token.sh is opt-in)
if [ -n "$PLUGIN_ROOT" ] && [ -d "$PLUGIN_ROOT/hooks" ]; then
  NONEXEC=$(find "$PLUGIN_ROOT/hooks" -maxdepth 1 -name '*.sh' ! -perm -u+x 2>/dev/null | wc -l | tr -d ' ')
  SH_COUNT=$(find "$PLUGIN_ROOT/hooks" -maxdepth 1 -name '*.sh' 2>/dev/null | wc -l | tr -d ' ')
  if [ "${NONEXEC:-0}" = "0" ] && [ "${SH_COUNT:-0}" -gt 0 ]; then
    pass "All $SH_COUNT hook scripts present and executable"
  else
    RECOMMENDED=$((RECOMMENDED+1))
    fail "$NONEXEC hook script(s) not executable — hooks will silently no-op" "chmod +x \"$PLUGIN_ROOT/hooks/\"*.sh   (or reinstall the plugin)"
  fi
else
  RECOMMENDED=$((RECOMMENDED+1))
  fail "hooks/ directory not found in plugin root" "/plugin update scaffolding@komluk-scaffolding (reinstall)"
fi

# Check 5 — in-repo CLAUDE.md (per-project routing)
if [ -f "./CLAUDE.md" ]; then
  pass "In-repo CLAUDE.md present — routing protocol travels to this repo"
  # Check 5b — routing section intact (HARD INVARIANT: never relocate routing
  # into nested CLAUDE.md). Guards against Item 1 extraction gone wrong.
  if grep -q 'subagent_type="scaffolding:' "./CLAUDE.md" 2>/dev/null; then
    pass "Routing section present in ./CLAUDE.md (always-loaded protocol intact)"
  else
    RECOMMENDED=$((RECOMMENDED+1))
    fail "./CLAUDE.md is missing the routing protocol (Task subagent_type=\"scaffolding:...\")" "restore the Protocol + Decision Tree + Agents table to the project-root CLAUDE.md — routing must stay always-loaded, never only in a nested CLAUDE.md"
  fi
else
  RECOMMENDED=$((RECOMMENDED+1))
  fail "No ./CLAUDE.md — agent routing protocol won't apply in this project" "/init-scaffolding"
fi

# Check 5c — skills: references resolve + every skill has a description
# (orchestration contract, Item 4). Validates the plugin install, not the repo.
if [ -n "$PLUGIN_ROOT" ] && [ -d "$PLUGIN_ROOT/agents" ] && [ -d "$PLUGIN_ROOT/skills" ]; then
  SKILL_ISSUES=$(python3 - "$PLUGIN_ROOT" <<'PY' 2>/dev/null || echo "ERR"
import os, re, sys
root = sys.argv[1]
problems = []
skills_dir = os.path.join(root, "skills")
# Every skill must have a non-empty description in its frontmatter.
for name in sorted(os.listdir(skills_dir)):
    sk = os.path.join(skills_dir, name, "SKILL.md")
    if not os.path.isfile(sk):
        continue
    head = open(sk, encoding="utf-8", errors="ignore").read()[:4000]
    if not re.search(r'(?m)^description:\s*\S', head):
        problems.append(f"skill '{name}' has no description:")
# Every skills: reference in an agent must exist on disk.
agents_dir = os.path.join(root, "agents")
for fn in sorted(os.listdir(agents_dir)):
    if not fn.endswith(".md"):
        continue
    txt = open(os.path.join(agents_dir, fn), encoding="utf-8", errors="ignore").read()
    m = re.search(r'(?ms)^skills:\s*\n((?:\s*-\s*\S+\s*\n)+)', txt)
    if not m:
        continue
    for ref in re.findall(r'-\s*([A-Za-z0-9_-]+)', m.group(1)):
        if not os.path.isfile(os.path.join(skills_dir, ref, "SKILL.md")):
            problems.append(f"agent '{fn}' references missing skill '{ref}'")
print("\n".join(problems))
PY
)
  if [ -z "$SKILL_ISSUES" ]; then
    pass "All agent skills: references resolve and every skill has a description"
  elif [ "$SKILL_ISSUES" = "ERR" ]; then
    : # python failed — skip silently, this is a soft check
  else
    RECOMMENDED=$((RECOMMENDED+1))
    fail "Orchestration contract issues: $(echo "$SKILL_ISSUES" | tr '\n' ';')" "fix the named skill description(s) / agent skills: reference(s) in the plugin source"
  fi
fi

# Check 6 — .scaffolding/ present (file-based memory tiers)
if [ -d "./.scaffolding" ]; then
  pass ".scaffolding/ present — file-based memory tiers can persist"
else
  RECOMMENDED=$((RECOMMENDED+1))
  fail "No ./.scaffolding/ — file-based agent memory can't be written" "/init-scaffolding"
fi

echo ""
echo "=== OPTIONAL (opt-in cross-device semantic memory) ==="

# Check 7 — MCP memory server configured (opt-in — never a failure if absent)
if command -v claude >/dev/null 2>&1 && claude mcp list 2>/dev/null | grep -qi 'memory'; then
  pass "memory MCP wired"
  # Check 8 — token only matters once #7 is wired
  if [ -n "${MEMORY_MCP_TOKEN:-}" ]; then
    pass "MEMORY_MCP_TOKEN is set"
  else
    OPTIONAL=$((OPTIONAL+1))
    fail "memory MCP wired but MEMORY_MCP_TOKEN unset — calls will 401" "export MEMORY_MCP_TOKEN=...   then   /memory status"
  fi
else
  echo "  [SKIP] memory MCP not wired — OPTIONAL, off by default."
  echo "         enable cross-device memory with: /memory enable   (skip if not wanted)"
fi

echo ""
echo "=== SUMMARY (bash checks) ==="
echo "  Blocking: $BLOCKING   Recommended: $RECOMMENDED   Optional: $OPTIONAL"
echo "  Next: run Step B (live agent-resolution probe) — the #1 first-run check."
# ---- end health check ----

Read the full file on GitHub · 297 lines

Changes

What this file has done since we first saw it

Hashed on every crawl. A supply-chain change to an agent config is a question of when, not whether, so the history is kept rather than the latest state alone.

  1. 5d ago First seen · 297 lines · 24 tokens per session scan A 4cb3101c1a5f

Subscribe to this mod's changes

doctor is a command published in the GitHub repository komluk/scaffolding (15 stars, last pushed 1mo ago), licensed MIT. It adds 24 tokens to every session and 3,692 once invoked, about $0.0001 per session on Opus 5. A static security scan graded it A with 0 findings. No closer match exists in the catalogue, so it is treated as the original; first seen 2026-08-30.