Skip to main content

spec_driven_docs/commands/
assess.rs

1//! `assess` subcommand: runtime-shape.
2//!
3//! Resolves the target, asks the assess service, and renders text or JSON.
4//! Assess is a report, not a gate: every classification exits 0.
5
6use crate::cli::assess::AssessArgs;
7use crate::context::AppContext;
8use crate::error::AppError;
9use crate::output;
10use crate::services::assess::assess;
11
12/// Classify a target repository.
13///
14/// # Errors
15///
16/// [`AppError::ManifestInvalid`] when an instance manifest exists but
17/// cannot be trusted, and I/O errors from the walk.
18pub fn run(ctx: &AppContext, args: AssessArgs) -> Result<(), AppError> {
19    let target = if args.target.is_absolute() {
20        args.target
21    } else if args.target == "." {
22        ctx.cwd.clone()
23    } else {
24        return Err(AppError::Usage("target must be absolute or .".to_string()));
25    };
26    let report = assess(&target)?;
27    if args.json {
28        return output::json(&report);
29    }
30    output::line(format!(
31        "classification: {}",
32        report.classification.as_str()
33    ));
34    output::line(format!("instance: {}", report.instance.instance));
35    output::line(format!("doc roots: {}", join_or_none(&report.doc_roots)));
36    output::line(format!(
37        "populated doc roots: {}",
38        join_or_none(&report.populated_doc_roots)
39    ));
40    output::line(format!("document files: {}", report.documents.count));
41    output::line(format!(
42        "methodology markers: {}",
43        join_or_none(&report.methodology_markers)
44    ));
45    for (profile, existing) in &report.collisions {
46        output::line(format!("collisions ({profile}): {}", existing.len()));
47    }
48    Ok(())
49}
50
51fn join_or_none(items: &[String]) -> String {
52    if items.is_empty() {
53        "none".to_string()
54    } else {
55        items.join(", ")
56    }
57}