pub mod discovery;
pub mod invocation;
pub mod result;
pub mod schema;
use anyhow::Result;
use self::invocation::InvocationInput;
pub(crate) use self::discovery::find_skill_file;
pub use self::result::VerifyReport;
#[derive(Debug, Clone)]
pub struct VerifyInput {
pub root: std::path::PathBuf,
pub spawn_root: std::path::PathBuf,
pub has_cli: bool,
pub cli_command: Option<Vec<String>>,
}
pub fn run(input: &VerifyInput) -> Result<VerifyReport> {
let root = &input.root;
let mut report = VerifyReport::default();
for check in discovery::run(root)? {
report.push(check);
}
let skill_path = find_skill_file(root);
let skill_md = skill_path
.as_ref()
.and_then(|p| std::fs::read_to_string(p).ok())
.unwrap_or_default();
let inv = InvocationInput::new(
root,
&input.spawn_root,
&skill_md,
input.has_cli,
input.cli_command.as_deref(),
);
invocation::run(&inv, &mut report)?;
Ok(report)
}
pub fn render(report: &VerifyReport) -> String {
use self::result::Severity;
let mut out = String::new();
let (pass, warn, fail, skip) = report.counts();
for r in &report.results {
let glyph = match r.severity {
Severity::Pass => "✓",
Severity::Warn => "!",
Severity::Error => "✗",
Severity::Skipped => "·",
};
out.push_str(&format!(
"{} {} — {}\n",
glyph,
r.severity.as_str(),
r.check_name
));
if r.severity != Severity::Pass && !r.message.is_empty() {
out.push_str(&format!(" {}\n", r.message));
}
if let Some(s) = &r.suggestion {
out.push_str(&format!(" {s}\n"));
}
}
let _ = &pass;
let _ = &skip;
let _ = &warn;
out.push_str(&format!(
"\n{pass} passed, {warn} warning(s), {fail} failed"
));
out.push_str(if fail > 0 {
" — verify FAILED\n"
} else {
" — verify OK\n"
});
out
}