rust-doctor 0.2.0

A unified code health tool for Rust — scan, score, and fix your codebase
Documentation
# AGENTS.md

> rust-doctor — Rust code health scanner. CLI binary, library crate, MCP server, npm package, GitHub Action.
> Rust 2024, MSRV 1.85, single crate (not a workspace).

## Commands

```bash
cargo build                        # Debug build (with MCP server)
cargo build --no-default-features  # Build without MCP server
cargo test                         # All tests (unit + integration + snapshots)
cargo test test_name               # Single test
cargo test --test integration      # Integration tests only
cargo test --test snapshots        # Snapshot tests only
cargo insta review                 # Review snapshot changes
cargo clippy --all-targets -- -W clippy::all -W clippy::pedantic -W clippy::nursery -D warnings
cargo fmt --check
```

## Project Structure

```
src/
├── main.rs            # CLI entry point, --mcp flag dispatches to mcp::run_mcp_server()
├── lib.rs             # Module declarations + crate-root re-exports for passes
├── scan.rs            # Scan orchestrator: resolve roots → run passes → dedup → score
├── scanner.rs         # AnalysisPass trait + ScanOrchestrator
├── diagnostics.rs     # Diagnostic, ScanResult, Severity, Category — central types
├── config.rs          # Config resolution (CLI > TOML > Cargo.toml metadata > defaults)
├── output/            # Score calculation + terminal/JSON rendering
│   ├── mod.rs         # render_score(), render_json(), re-exports, tests
│   ├── score.rs       # calculate_score(), dimension weights, score_label()
│   └── terminal.rs    # render_terminal(), print_score_box(), print_diagnostics()
├── mcp/               # MCP server (rmcp v1.2.0, stdio, feature-gated)
│   ├── mod.rs         # Server struct, entry point, ServerHandler impl
│   ├── types.rs       # Input/output schemas (ScanInput, ScoreInput, etc.)
│   ├── tools.rs       # Tool + prompt handler implementations
│   ├── helpers.rs     # discover_and_resolve(), format_scan_report(), group_diagnostics()
│   └── rules.rs       # Rule documentation (explain_rule, list_rules)
├── passes/            # Analysis passes grouped by domain
│   ├── security/      # Security-focused passes
│   │   ├── audit.rs   # cargo-audit pass
│   │   ├── deny.rs    # cargo-deny pass
│   │   └── geiger.rs  # cargo-geiger pass
│   ├── static_analysis/  # Code analysis passes
│   │   ├── clippy/    # Clippy integration (55+ lint registry)
│   │   │   ├── mod.rs
│   │   │   └── lint_registry.rs
│   │   └── rules/     # Custom AST rules (syn::visit::Visit)
│   │       ├── mod.rs         # RulesPass + CustomRule trait + all_custom_rules()
│   │       ├── error_handling.rs
│   │       ├── performance.rs
│   │       ├── complexity.rs  # Cyclomatic complexity rule
│   │       ├── security.rs
│   │       ├── async_rules.rs
│   │       └── framework.rs
│   └── quality/       # Quality & dependency passes
│       ├── coverage.rs
│       ├── msrv.rs
│       ├── machete.rs
│       └── semver_checks.rs
├── discovery.rs       # Project detection (frameworks, dependencies, workspace)
├── diff.rs            # Git diff filtering
├── cache.rs           # Incremental cache (.rust-doctor-cache.json)
├── suppression.rs     # Inline suppression (// rust-doctor-disable-next-line)
├── process.rs         # Subprocess runner with timeout
├── fixer.rs           # Auto-fix suggestions
├── plan.rs            # Remediation plan generation
├── sarif.rs           # SARIF output format
├── deps.rs            # Dependency analysis
├── workspace.rs       # Cargo workspace resolution
├── error.rs           # 7 thiserror error types
└── cli.rs             # clap CLI definition
tests/
├── integration.rs     # Temp Rust projects with known violations
└── snapshots.rs       # insta JSON snapshot tests
```

Note: `lib.rs` re-exports pass modules at the crate root (`pub(crate) use passes::security::audit`, etc.) so that `use crate::audit` paths work throughout the codebase.

## Code Style

- Clippy pedantic enabled (`must_use_candidate`, `module_name_repetitions`, `missing_errors_doc`, `missing_panics_doc` allowed)
- Custom errors with `thiserror::Error` — no `anyhow`, no `Box<dyn Error>` in library code
- `Result<T, E>` + `?` operator everywhere — `unwrap()` only in tests
- Two parallelism levels: rayon for scan roots, `std::thread::scope` for passes within a root
- `PassError::Skipped` for missing external tools — graceful degradation, not failure

## Testing

- Unit tests: inline `#[cfg(test)]` modules in each source file
- Integration tests: temp Rust projects via `tempfile::TempDir`, `fast_config()` skips external passes
- Snapshot tests: `insta` JSON snapshots for serialization stability
- After changing snapshots: `cargo insta review`
- Self-scan tests: several modules scan rust-doctor itself as sanity check

## Architecture Rules

### Always
- New analysis passes implement `AnalysisPass` trait (`name()` + `run()``Vec<Diagnostic>`)
- New custom rules implement `CustomRule` trait in the appropriate `passes/static_analysis/rules/` submodule
- Diagnostics go to stderr, score to stdout (intentional for piping)
- MCP tools are read-only — no filesystem writes, directory under `$HOME` only
- Run `catch_unwind` around custom rules — a panicking rule must not crash the scan

### Ask First
- Changing score weights (Security ×2.0, Reliability ×1.5, Maintainability ×1.0, Performance ×1.0, Dependencies ×1.0)
- Adding new MCP tools or modifying security hardening
- Changing module visibility (pub vs pub(crate))

### Never
- Use `anyhow` — this project uses typed errors with `thiserror`
- Add `unsafe` blocks in production code
- Skip `catch_unwind` on custom rules
- Break the stderr/stdout output routing convention

## Anti-Friction Rules (claude-doctor)

Règles pour éviter les patterns de friction détectés par `claude-doctor` sur ce projet : edit-thrashing, restart-cluster, repeated-instructions, negative-drift, error-loop, excessive-exploration.

### Editing discipline (anti edit-thrashing)

- Read the full file before editing. Plan all changes, then make ONE complete edit.
- If you've edited the same file 3+ times, STOP. Re-read the user's original requirements and re-plan from scratch.
- Prefer one large coherent edit over multiple small incremental ones.

### Stay aligned with the user (anti repeated-instructions, rapid-corrections)

- Re-read the user's last message before responding. Follow through on every instruction completely — don't partially address requests.
- Every few turns on a long task, re-read the original request to verify you haven't drifted from the goal.
- When the user corrects you: stop, re-read their message, quote back what they actually asked for, and confirm understanding before proceeding.

### Act, don't explore (anti excessive-exploration)

- Don't read more than 3-5 files before making a change. Get a basic understanding, make the change, then iterate.
- Prefer acting early and correcting via feedback over prolonged reading and planning.

### Break loops (anti error-loop, restart-cluster)

- After 2 consecutive tool failures or the same error twice, STOP. Change your approach entirely — don't retry the same strategy. Explain what failed and try something genuinely different.
- When truly stuck, summarize what you've tried and ask the user for guidance rather than retrying.

### Verify output (anti negative-drift)

- Before presenting your result, double-check it actually addresses what the user asked for.
- If the diff doesn't map cleanly to the user's request, don't ship it — re-plan.