repopilot 0.11.0

Local-first CLI for repository audit, architecture risk detection, baseline tracking, and CI-friendly code review.
Documentation
use crate::doctor::model::DoctorNextStep;
use std::path::Path;

const REPORT_FILE_PATH: &str = "repopilot-report.md";
const RECEIPT_FILE_PATH: &str = ".repopilot/receipt.json";

pub fn build_next_steps(
    path: &Path,
    has_config: bool,
    has_baseline: bool,
    has_repopilot_ci_gate: bool,
    files_analyzed: usize,
) -> Vec<DoctorNextStep> {
    let path = command_path(path);
    let mut steps = Vec::new();

    if !has_config {
        steps.push(DoctorNextStep {
            command: "repopilot init".to_string(),
            reason: "Create an explicit repopilot.toml before tuning thresholds or CI gates."
                .to_string(),
        });
    }

    if files_analyzed == 0 {
        steps.push(DoctorNextStep {
            command: format!("repopilot doctor {path} --include-low-signal"),
            reason: "Re-run readiness diagnostics with low-signal paths included because the current scan scope is empty."
                .to_string(),
        });
    }

    steps.push(DoctorNextStep {
        command: format!(
            "repopilot scan {path} --format markdown --output {REPORT_FILE_PATH} --receipt {RECEIPT_FILE_PATH}"
        ),
        reason: "Generate a human-readable audit report plus a reproducible JSON receipt."
            .to_string(),
    });

    if has_baseline {
        steps.push(DoctorNextStep {
            command: format!(
                "repopilot review {path} --base origin/main --baseline .repopilot/baseline.json --fail-on new-high"
            ),
            reason: "Review only changed-code findings against the accepted baseline.".to_string(),
        });
    } else {
        steps.push(DoctorNextStep {
            command: format!("repopilot baseline create {path}"),
            reason:
                "Accept existing findings as technical debt before enabling new-finding CI gates."
                    .to_string(),
        });
    }

    if !has_repopilot_ci_gate {
        steps.push(DoctorNextStep {
            command: format!(
                "repopilot scan {path} --baseline .repopilot/baseline.json --fail-on new-high"
            ),
            reason: "Use this as the minimum CI gate after committing a baseline.".to_string(),
        });
    }

    steps.push(DoctorNextStep {
        command: format!("repopilot ai context {path} --budget 4k --output repopilot-context.md"),
        reason: "Create AI-ready remediation context without uploading source code.".to_string(),
    });

    steps
}

pub fn build_next_command(path: &Path, has_baseline: bool) -> String {
    let path = command_path(path);

    if has_baseline {
        format!(
            "repopilot review {path} --base origin/main --baseline .repopilot/baseline.json --fail-on new-high"
        )
    } else {
        format!(
            "repopilot scan {path} --format markdown --output {REPORT_FILE_PATH} --receipt {RECEIPT_FILE_PATH}"
        )
    }
}

pub fn command_path(path: &Path) -> String {
    let value = path.display().to_string();

    if value.is_empty() {
        ".".to_string()
    } else {
        value
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn next_steps_start_with_init_when_config_is_missing() {
        let steps = build_next_steps(Path::new("."), false, false, false, 1);

        assert_eq!(
            steps.first().map(|step| step.command.as_str()),
            Some("repopilot init")
        );
        assert!(
            steps
                .iter()
                .any(|step| step.command.contains("--receipt .repopilot/receipt.json"))
        );
        assert!(
            steps
                .iter()
                .any(|step| step.command == "repopilot baseline create .")
        );
    }

    #[test]
    fn next_steps_use_review_when_baseline_exists() {
        let steps = build_next_steps(Path::new("."), true, true, true, 1);

        assert!(steps.iter().any(|step| {
            step.command
                == "repopilot review . --base origin/main --baseline .repopilot/baseline.json --fail-on new-high"
        }));
    }
}