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 },
"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 previously projected source was removed.
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 it with drwn machine skill disable (machine) or from 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 safe manual inspection steps.
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 an earlier generated-sidecar 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 —
mcpServers["<name>"]toggles a server that is not in the registry, standalone MCP inventory, or selected Worker closure. - Unknown skill reference —
skills.includeorskills.excludenames a skill that no repo-native source, package-backed bundle, or selected Worker closure provides. - Unknown extension reference —
extensions["<name>"]references an extension the registry does not know about. - Stale target override —
targets["<name>"].enabledmatches packaged project policy; the override is a no-op. - Card references unavailable skills — a Card in the selected root closure lists a skill name the project's available inventory cannot satisfy.
- Unresolved machine capability — an explicit machine skill or MCP ID is unavailable in machine inventory.
- Invalid profile bytes — the pinned profile extraction is missing or no longer matches its recorded integrity.
- Machine projection conflict — a destination is foreign or prior-owned state has drifted. Doctor reports it without repair.
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 package/record with drwn machine skill install or drwn machine mcp 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