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.
npx agentmods add commands/komluk/scaffolding/doctorgit clone --depth 1 https://github.com/komluk/scaffoldingWrote 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.
[](https://agentmods.dev/commands/komluk/scaffolding/doctor)<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>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.
| Model | Per session | Once 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 |
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.
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 ----
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.
- 5d ago First seen · 297 lines · 24 tokens per session scan A 4cb3101c1a5f
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.
Other commands, from other repositories
launch-meta-campaign
Launch a complete Meta Ads campaign — campaign, ad set, and ad(s) — built paused and ready to review. Use when the user wants a new Facebook/Instagram campaign.
negative-keyword-sweep
Find wasted spend in Google Ads search terms and add negatives at the right scope (account list, campaign, or ad group). Use when the user says "find junk keywords", "stop wasting on bad searches", or wants a search-terms cleanup.
duck-off
Turn off Rubber Duck mode and answer normally.
create-topic
Research and generate a complete topic plan with keyword mapping, audience, angle, and competitive positioning.
extensions
Show what the connected context expects to reach, and whether this machine provides it.
list
List the NeatContext contexts you can connect.