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(
27        &target,
28        &crate::release::embedded::EmbeddedReleaseBundle::new(),
29    )?;
30    if args.json {
31        return output::json(&report);
32    }
33    output::line(format!(
34        "classification: {}",
35        report.classification.as_str()
36    ));
37    output::line(format!("instance: {}", report.instance.instance));
38    output::line(format!("doc roots: {}", join_or_none(&report.doc_roots)));
39    output::line(format!(
40        "populated doc roots: {}",
41        join_or_none(&report.populated_doc_roots)
42    ));
43    output::line(format!("document files: {}", report.documents.count));
44    output::line(format!(
45        "methodology markers: {}",
46        join_or_none(&report.methodology_markers)
47    ));
48    for (profile, existing) in &report.collisions {
49        output::line(format!("collisions ({profile}): {}", existing.len()));
50    }
51    Ok(())
52}
53
54fn join_or_none(items: &[String]) -> String {
55    if items.is_empty() {
56        "none".to_string()
57    } else {
58        items.join(", ")
59    }
60}