clipmem 0.5.5

macOS clipboard memory backed by SQLite and searchable from agent runtimes
Documentation
use std::path::{Path, PathBuf};

#[derive(Debug, Clone)]
pub(in crate::cli) struct OpenClawDoctorReport {
    pub(in crate::cli) target_dir: PathBuf,
    pub(in crate::cli) checks: Vec<AgentDoctorCheck>,
}

#[derive(Debug, Clone)]
pub(in crate::cli) struct AgentDoctorCheck {
    pub(in crate::cli) status: AgentDoctorStatus,
    pub(in crate::cli) label: String,
    pub(in crate::cli) detail: String,
    pub(in crate::cli) next_steps: Vec<String>,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(in crate::cli) enum AgentDoctorStatus {
    Ok,
    Warn,
    Fail,
}

#[derive(Debug, Clone)]
pub(in crate::cli) struct HermesDoctorReport {
    pub(in crate::cli) target_dir: PathBuf,
    pub(in crate::cli) checks: Vec<AgentDoctorCheck>,
}

pub(in crate::cli) fn render_agent_doctor_report(
    target_label: &str,
    target_dir: &Path,
    checks: &[AgentDoctorCheck],
) -> String {
    let mut out = String::new();
    let _ = std::fmt::Write::write_fmt(
        &mut out,
        format_args!("{target_label}: {}\n\n", target_dir.display()),
    );

    for check in checks {
        let status = doctor_status_label(check.status);
        let _ = std::fmt::Write::write_fmt(
            &mut out,
            format_args!("[{status}] {}\n{}\n", check.label, check.detail),
        );
        for step in &check.next_steps {
            let _ = std::fmt::Write::write_fmt(&mut out, format_args!("  - {step}\n"));
        }
        out.push('\n');
    }

    out
}

fn doctor_status_label(status: AgentDoctorStatus) -> &'static str {
    match status {
        AgentDoctorStatus::Ok => "OK",
        AgentDoctorStatus::Warn => "WARN",
        AgentDoctorStatus::Fail => "FAIL",
    }
}

#[cfg(test)]
mod tests {
    use super::{render_agent_doctor_report, AgentDoctorCheck, AgentDoctorStatus};

    #[test]
    fn render_agent_doctor_report_lists_statuses_details_and_next_steps() {
        let checks = vec![
            AgentDoctorCheck {
                label: "Binary available".to_string(),
                status: AgentDoctorStatus::Ok,
                detail: "clipmem is on PATH".to_string(),
                next_steps: Vec::new(),
            },
            AgentDoctorCheck {
                label: "Skill installed".to_string(),
                status: AgentDoctorStatus::Fail,
                detail: "SKILL.md missing".to_string(),
                next_steps: vec!["Run clipmem agents openclaw install-skill".to_string()],
            },
        ];

        let report = render_agent_doctor_report(
            "OpenClaw skill",
            std::path::Path::new("/tmp/skill"),
            &checks,
        );

        assert!(report.contains("OpenClaw skill: /tmp/skill"));
        assert!(report.contains("[OK] Binary available"));
        assert!(report.contains("clipmem is on PATH"));
        assert!(report.contains("[FAIL] Skill installed"));
        assert!(report.contains("  - Run clipmem agents openclaw install-skill"));
    }
}