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:
- Look for a
.nyann/profile or equivalent profile hint in the repo.
- If the repo's CLAUDE.md declares an active profile, use that.
- 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:
- Run
bin/health-trend.sh --target <cwd> --last 10.
- Show the sparkline and summary: "Health trend: ▃▄▅▆▇ — 72→85 over
last 10 checks (↑ improving)."
- If any
category_deltas show worsening (delta < 0 in the breakdown),
call them out: "⚠ missing got worse (−3 over the window)."
- 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.
1---2name: doctor3description: 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).4---56# doctor78Read-only audit. Never mutates the filesystem. Wraps9`bin/doctor.sh`, which internally runs `bin/retrofit.sh --report-only`.1011## 1. Resolve the profile1213`doctor.sh` requires `--profile <name>`. The skill's job is to pick the14right one without pestering the user:15161. Look for a `.nyann/profile` or equivalent profile hint in the repo.172. If the repo's CLAUDE.md declares an active profile, use that.183. If neither exists, ask the user which profile to audit against19 (`default`, `nextjs-prototype`, `python-cli`, or any user/team profile20 they've installed). Don't silently pick `default` — the audit is21 only meaningful against an intended baseline.2223## 2. Invoke2425```26bin/doctor.sh --target <cwd> --profile <name> [--json] [--scope <csv>]27```2829`--profile` takes a **bare profile name** (e.g. `python-cli`, `nextjs-prototype`),30not a filesystem path. The script resolves the name to the profile JSON internally.3132Pass `--json` when the user says "machine-readable", "as JSON",33"pipe this", or similar — otherwise emit the human-readable report.3435Pass `--scope <csv>` when the user wants to inspect only a subset of36the audit — e.g. "just check my docs", "are my hooks healthy". Accepts37any of `docs`, `hooks`, `branching`, `gitignore`, `editorconfig`,38`github`, `history`, `all`, comma-separated. When narrower than `all`,39the rendered output gains a `Scope: <csv>` line and `--persist` is40auto-disabled (a partial-scope health score would corrupt the trend41series in `memory/health.json`).4243## 3. Interpret the exit code4445| Code | Meaning | What to tell the user |46|---|---|---|47| 0 | clean | "No drift. Hygiene and docs look healthy." |48| 4 | warnings only | "Not failing, but some things have drifted. Here's the list." Offer to remediate via `retrofit`. |49| 5 | critical | "Missing required files or broken internal links. Run `retrofit` to fix." |5051## 4. Sections in the report5253The output has several blocks. When the user asks "what does each section54mean?", explain in terms of the repo, not nyann internals:5556- **MISSING:** files the profile expects but the repo lacks (e.g. no57 `.husky/pre-commit` when the profile declares husky hooks).58- **MISCONFIGURED:** files present but content doesn't match (e.g. a59 `.husky/pre-commit` exists but doesn't actually run the expected60 linter).61- **NON-COMPLIANT HISTORY:** last N commit subjects that don't match62 Conventional Commits. Informational — nyann never rewrites history.63- **DOCUMENTATION:** CLAUDE.md size vs budget, internal link resolution,64 MCP link reachability (when MCP connectors are available), orphan files65 under `docs/` / `memory/`, and misplaced docs (files at non-canonical66 paths that could be reorganized via retrofit).67- **GITHUB PROTECTION:** branch protection per strategy, tag-protection68 rulesets (when `.github.tag_protection_pattern` is declared in the69 profile), CODEOWNERS-required gate (when a CODEOWNERS file exists70 or `.github.require_code_owner_reviews=true`), and repo-security71 settings (Dependabot, secret scanning, push protection, code72 scanning). Soft-skips when `gh` is missing or unauthenticated.73 Driven by `bin/gh-integration.sh --check` under the hood. Critical74 drift here bumps the exit code to 5; warn drift bumps it to 4.75- **DOC STALENESS:** doc files under `docs/` whose correlated source76 paths have changed substantially since the doc itself was last77 touched. Heuristic-based (`bin/docs-staleness.sh`); thresholds78 configurable via the profile (`documentation.staleness_threshold_*`).79 Informational — never affects exit code.80- **PUBLIC-DOC DRIFT:** README/CONTRIBUTING/SECURITY/docs/*.md scanned81 for version-ref drift (semver older than latest tag), broken82 markdown link targets, missing `npm run` / `make` script references,83 and count-claim drift (opt-in per profile). Driven by84 `bin/docs-drift-scan.sh`. Critical/high findings escalate the exit85 code (mirrors GitHub protection); medium/low are advisory.86- **IAC DRIFT:** IaC source (`*.tf`, `Chart.yaml`, `Pulumi*.yaml`,87 `*.tfvars`, Ansible vars) scanned for committed secrets (critical),88 unpinned module refs / providers / deps (high), missing lockfiles and89 Helm `appVersion` lag (medium). Filesystem + git only — no90 `terraform plan`, no cloud calls. Driven by `bin/iac-drift-scan.sh`,91 gated per profile via `iac.drift_check.{enabled, unpinned_refs,92 missing_lockfile, secrets_in_vars, version_lag}`. Critical/high93 findings escalate the exit code (mirrors public-doc drift); medium/low94 are advisory. Silent on non-infra repos. Like public-doc drift, this95 is score-isolated — it shapes the exit code but not the numeric health96 score.9798The protection block respects nyann's gh-best-effort invariant — it99never prompts for credentials and never blocks the audit. When `gh`100isn't reachable, the section reports `skipped` and contributes no101drift to the exit code.102103## 5. Surface health trend (when available)104105After showing the audit report, check whether `memory/health.json`106exists in the target repo. If it does:1071081. Run `bin/health-trend.sh --target <cwd> --last 10`.1092. Show the sparkline and summary: "Health trend: ▃▄▅▆▇ — 72→85 over110 last 10 checks (↑ improving)."1113. If any `category_deltas` show worsening (delta < 0 in the breakdown),112 call them out: "⚠ `missing` got worse (−3 over the window)."1134. If the trend direction is `down`, suggest: "Score is declining — run114 `/nyann:retrofit` to address the drift."115116If `memory/health.json` doesn't exist, skip silently — don't suggest117creating it. The persist step happens automatically via `doctor.sh`.118119## 6. What to do after120121- **User asks to fix drift** → hand off to `retrofit` ("fix this repo's122 drift"). Do not attempt to fix anything from inside the doctor skill.123- **User asks "why is this missing?"** → read back the profile's124 expectation for that item; don't guess.125- **User says "ignore warnings, they're false positives"** → there's126 no silencing mechanism inside doctor itself. If it's a recurring127 false positive, the profile or the repo's `.nyann-ignore`-equivalent128 is the right lever (future work, not today).129130## When to hand off131132- "Fix it" / "remediate" / "bring into compliance" → `retrofit`.133- "Why is this profile the active one?" → `inspect-profile` (if134 available) or read the profile JSON directly.135- "I want a different profile applied" → `retrofit` with the new136 profile name.