Skip to main content

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

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).

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:

CheckWhat it verifiesResolution when ok: false
Home directory resolvesAGENTS_HOME_DIR, HOME, USERPROFILE, or os.homedir() returns a non-empty pathSet AGENTS_HOME_DIR or ensure HOME is set
node on PATHnode is findable; required for MCP servers that spawn Node processesInstall 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 referenceproject.servers["<name>"] toggles a server that is not in the registry or the user MCP library.
  • Unknown skill referenceskills.include or skills.exclude names a skill that no built-in scope, curated layer, package-backed bundle, or locked card provides.
  • Unknown extension referenceextensions["<name>"] references an extension the registry does not know about.
  • Stale target overridetargets["<name>"].enabled matches what the machine config already says; the override is a no-op.
  • Card references unavailable skills — a locked card's skills.include lists a skill name the project's available inventory cannot satisfy.
  • Dangling defaults referencesdefaults.skills or defaults.mcpServers on 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