Reading Doctor
drwn doctor runs every detector and reports what looks wrong without mutating anything. It is the inverse of drwn write: where write enforces invariants before touching disk, doctor enumerates problems and exits. Treat its output as a worklist, not a guarantee of safety.
Run it directly or with a machine-readable payload:
drwn doctor
drwn doctor --json
The Report-Only Contract
doctor never removes a stale symlink, never rewrites a drifted MCP file, never re-pulls a missing card. It reads the same configuration drwn write would resolve and runs the detectors against current disk state. If you want it fixed, you decide which command to run next.
This is deliberate. The doctor and the write pipeline share one diagnostics engine in cli/core/diagnostics.ts, but the write path raises typed errors and aborts before mutation while doctor surfaces the same conditions as a report and returns normally. See Diagnostics Model for the split.
JSON Output Shape
drwn doctor --json returns a DoctorReport:
{
"brokenSymlinks": [],
"staleSkillSymlinks": [],
"mcpDrift": [],
"missingGeneratedFiles": [],
"hookIssues": [],
"surfaceNotes": [],
"platformChecks": [],
"projectConfigIssues": [],
"cards": { "configuredRefs": [], "lockedVersions": [], "warnings": [] },
"store": { "path": "...", "initialized": true, "schemaVersion": 1, "cardCount": 0, "sourceCount": 0, "skillBundleCount": 0, "mcpServerCount": 0, "legacyLayoutDetected": false },
"writeRecord": { "path": "...", "present": true, "corrupt": false, "managedPathCount": 0, "lastWriteAt": "...", "lastWriteHarnessVersion": "..." }
}
Each of the top-level arrays maps to one detector category below.
Detector Categories
Broken symlinks
A symlink under ~/.claude/skills/ or ~/.codex/skills/ whose target file no longer exists. Usually means a skill bundle was uninstalled or a curated skill directory was renamed.
drwn doctor --json
drwn write --dry-run
Re-running drwn write re-points drwn-owned links to the correct source if the underlying skill is still resolved. If the skill itself is gone, remove the offending ref from defaults.skills (machine) or skills.include (project).
Stale skill symlinks
A symlink that drwn no longer wants because the skill is no longer in the resolved set, but is still on disk. drwn-owned stale links are cleaned up on the next drwn write via the write record. User-owned replacements are preserved and warned about.
drwn doctor --json
drwn write
See Stale Symlinks for the ownership distinction and the store migrate --cleanup-legacy-orphans escape hatch.
MCP drift
The managed mcpServers key in ~/.claude/settings.json or the [mcp_servers] block in ~/.codex/config.toml has been edited outside drwn. Cursor reports drift when the generated cursor-mcp.json no longer matches the rendered expectation.
drwn doctor --json
drwn status --why server:<name>
drwn write --dry-run
drwn write --force
--force only overwrites drwn-managed regions. See Ownership Conflicts for the decision tree.
Missing generated files
This category is retained for output-shape stability but is no longer triggered by Cursor. Cursor's mcp.json is written directly as managed-content rather than via a generated sidecar file, so there is no generated file that can go missing. If missingGeneratedFiles is non-empty in a report, it indicates a pre-migration write record; re-running drwn write clears it.
Hook issues
A locked card declares hook policies but no hook consent has been recorded. drwn write will not materialize hooks for this card until consent is granted.
drwn doctor --json
drwn card trust @your-handle/backend --hooks
drwn write
Use drwn card untrust @your-handle/backend to revoke consent.
Platform checks
platformChecks is an array of { name, ok, detail? } entries for environment prerequisites. A non-empty array with ok: false entries indicates a setup problem that will cause commands to fail.
Currently checked:
| Check | What it verifies | Resolution when ok: false |
|---|---|---|
| Home directory resolves | AGENTS_HOME_DIR, HOME, USERPROFILE, or os.homedir() returns a non-empty path | Set AGENTS_HOME_DIR or ensure HOME is set |
node on PATH | node is findable; required for MCP servers that spawn Node processes | Install Node.js and ensure it is on PATH |
platformChecks entries with ok: true are normal. Only ok: false entries require action.
Project config issues
A single category that aggregates problems with <project>/.agents/drwn/config.json and the resolved card lock:
- Unknown server reference —
project.servers["<name>"]toggles a server that is not in the registry or the user MCP library. - Unknown skill reference —
skills.includeorskills.excludenames a skill that no built-in scope, curated layer, package-backed bundle, or locked card provides. - Unknown extension reference —
extensions["<name>"]references an extension the registry does not know about. - Stale target override —
targets["<name>"].enabledmatches what the machine config already says; the override is a no-op. - Card references unavailable skills — a locked card's
skills.includelists a skill name the project's available inventory cannot satisfy. - Dangling defaults references —
defaults.skillsordefaults.mcpServerson the machine config points at something not in the inventory.
At write time these would each abort drwn write before mutation. At doctor time they collect into projectConfigIssues and the run continues so the rest of the report still renders.
drwn doctor --json
drwn status --why skill:<name>
drwn status --why server:<name>
Fix by editing the offending config file, removing the reference, or installing the missing inventory (drwn library add ...).
Cross-References
- reference/cli/doctor for the command surface
- Diagnostics Model for how doctor and the write pipeline share one engine
- Using
status --whyfor tracing where an active item came from - Stale Symlinks and Ownership Conflicts for the most common follow-ups