# Doctor

> Run a read-only hygiene + documentation audit on the current repo. TRIGGER when the user says "is this repo healthy", "check hygiene", "audit this repo" (when they mean inspect, not remediate), "what's drifted", "run doctor", "run a health check", "audit docs", "check for doc drift", "what's broken in this project", "are the hooks still installed", "/nyann:doctor". Do NOT trigger on "fix this repo" / "remediate" / "bring it into compliance" — those are retrofit (audit + fix). doctor reports, never writes. Do NOT trigger on "what does this repo do" (that's a docs pointer, not a hygiene audit).

- Skill: `thettwe/doctor` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add thettwe/doctor`
- Raw SKILL.md: https://api.skillmd.com/api/skills/thettwe/doctor/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Security
- Author: thettwe (https://skillmd.com/u/thettwe)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/thettwe/doctor

---


# doctor

Read-only audit. Never mutates the filesystem. Wraps
`bin/doctor.sh`, which internally runs `bin/retrofit.sh --report-only`.

## 1. Resolve the profile

`doctor.sh` requires `--profile <name>`. The skill's job is to pick the
right one without pestering the user:

1. Look for a `.nyann/profile` or equivalent profile hint in the repo.
2. If the repo's CLAUDE.md declares an active profile, use that.
3. If neither exists, ask the user which profile to audit against
   (`default`, `nextjs-prototype`, `python-cli`, or any user/team profile
   they've installed). Don't silently pick `default` — the audit is
   only meaningful against an intended baseline.

## 2. Invoke

```
bin/doctor.sh --target <cwd> --profile <name> [--json] [--scope <csv>]
```

`--profile` takes a **bare profile name** (e.g. `python-cli`, `nextjs-prototype`),
not a filesystem path. The script resolves the name to the profile JSON internally.

Pass `--json` when the user says "machine-readable", "as JSON",
"pipe this", or similar — otherwise emit the human-readable report.

Pass `--scope <csv>` when the user wants to inspect only a subset of
the audit — e.g. "just check my docs", "are my hooks healthy". Accepts
any of `docs`, `hooks`, `branching`, `gitignore`, `editorconfig`,
`github`, `history`, `all`, comma-separated. When narrower than `all`,
the rendered output gains a `Scope: <csv>` line and `--persist` is
auto-disabled (a partial-scope health score would corrupt the trend
series in `memory/health.json`).

## 3. Interpret the exit code

| Code | Meaning | What to tell the user |
|---|---|---|
| 0 | clean | "No drift. Hygiene and docs look healthy." |
| 4 | warnings only | "Not failing, but some things have drifted. Here's the list." Offer to remediate via `retrofit`. |
| 5 | critical | "Missing required files or broken internal links. Run `retrofit` to fix." |

## 4. Sections in the report

The output has several blocks. When the user asks "what does each section
mean?", explain in terms of the repo, not nyann internals:

- **MISSING:** files the profile expects but the repo lacks (e.g. no
  `.husky/pre-commit` when the profile declares husky hooks).
- **MISCONFIGURED:** files present but content doesn't match (e.g. a
  `.husky/pre-commit` exists but doesn't actually run the expected
  linter).
- **NON-COMPLIANT HISTORY:** last N commit subjects that don't match
  Conventional Commits. Informational — nyann never rewrites history.
- **DOCUMENTATION:** CLAUDE.md size vs budget, internal link resolution,
  MCP link reachability (when MCP connectors are available), orphan files
  under `docs/` / `memory/`, and misplaced docs (files at non-canonical
  paths that could be reorganized via retrofit).
- **GITHUB PROTECTION:** branch protection per strategy, tag-protection
  rulesets (when `.github.tag_protection_pattern` is declared in the
  profile), CODEOWNERS-required gate (when a CODEOWNERS file exists
  or `.github.require_code_owner_reviews=true`), and repo-security
  settings (Dependabot, secret scanning, push protection, code
  scanning). Soft-skips when `gh` is missing or unauthenticated.
  Driven by `bin/gh-integration.sh --check` under the hood. Critical
  drift here bumps the exit code to 5; warn drift bumps it to 4.
- **DOC STALENESS:** doc files under `docs/` whose correlated source
  paths have changed substantially since the doc itself was last
  touched. Heuristic-based (`bin/docs-staleness.sh`); thresholds
  configurable via the profile (`documentation.staleness_threshold_*`).
  Informational — never affects exit code.
- **PUBLIC-DOC DRIFT:** README/CONTRIBUTING/SECURITY/docs/*.md scanned
  for version-ref drift (semver older than latest tag), broken
  markdown link targets, missing `npm run` / `make` script references,
  and count-claim drift (opt-in per profile). Driven by
  `bin/docs-drift-scan.sh`. Critical/high findings escalate the exit
  code (mirrors GitHub protection); medium/low are advisory.
- **IAC DRIFT:** IaC source (`*.tf`, `Chart.yaml`, `Pulumi*.yaml`,
  `*.tfvars`, Ansible vars) scanned for committed secrets (critical),
  unpinned module refs / providers / deps (high), missing lockfiles and
  Helm `appVersion` lag (medium). Filesystem + git only — no
  `terraform plan`, no cloud calls. Driven by `bin/iac-drift-scan.sh`,
  gated per profile via `iac.drift_check.{enabled, unpinned_refs,
  missing_lockfile, secrets_in_vars, version_lag}`. Critical/high
  findings escalate the exit code (mirrors public-doc drift); medium/low
  are advisory. Silent on non-infra repos. Like public-doc drift, this
  is score-isolated — it shapes the exit code but not the numeric health
  score.

The protection block respects nyann's gh-best-effort invariant — it
never prompts for credentials and never blocks the audit. When `gh`
isn't reachable, the section reports `skipped` and contributes no
drift to the exit code.

## 5. Surface health trend (when available)

After showing the audit report, check whether `memory/health.json`
exists in the target repo. If it does:

1. Run `bin/health-trend.sh --target <cwd> --last 10`.
2. Show the sparkline and summary: "Health trend: ▃▄▅▆▇ — 72→85 over
   last 10 checks (↑ improving)."
3. If any `category_deltas` show worsening (delta < 0 in the breakdown),
   call them out: "⚠ `missing` got worse (−3 over the window)."
4. If the trend direction is `down`, suggest: "Score is declining — run
   `/nyann:retrofit` to address the drift."

If `memory/health.json` doesn't exist, skip silently — don't suggest
creating it. The persist step happens automatically via `doctor.sh`.

## 6. What to do after

- **User asks to fix drift** → hand off to `retrofit` ("fix this repo's
  drift"). Do not attempt to fix anything from inside the doctor skill.
- **User asks "why is this missing?"** → read back the profile's
  expectation for that item; don't guess.
- **User says "ignore warnings, they're false positives"** → there's
  no silencing mechanism inside doctor itself. If it's a recurring
  false positive, the profile or the repo's `.nyann-ignore`-equivalent
  is the right lever (future work, not today).

## When to hand off

- "Fix it" / "remediate" / "bring into compliance" → `retrofit`.
- "Why is this profile the active one?" → `inspect-profile` (if
  available) or read the profile JSON directly.
- "I want a different profile applied" → `retrofit` with the new
  profile name.

