Print a project-level AI provenance summary. Reads each change's embedded provenance (independent of attestations) and classifies as AI / Human / Needs-attention / System. The headline metric is `ai_authored_pct = AI / (AI + Human)`. Default output is terse and product-facing: the headline %, the human count, and the AI source / tool breakdown. Implementation detail (system/bootstrap exclusions,
(&self, repo: &Repository)
| 145 | /// lines) is shown only under `--verbose`. Non-zero data-quality |
| 146 | /// conditions (needs-attention, unreadable) always surface as warnings. |
| 147 | fn show_summary(&self, repo: &Repository) -> CliResult<()> { |
| 148 | let view_name = self |
| 149 | .view |
| 150 | .clone() |
| 151 | .unwrap_or_else(|| repo.current_view().to_string()); |
| 152 | |
| 153 | let (summary, header_line) = if let Some(parent_view) = self.pending.as_deref() { |
| 154 | let s = repo |
| 155 | .provenance_summary_pending(&view_name, parent_view) |
| 156 | .map_err(CliError::Repository)?; |
| 157 | (s, format!("Pending work: {} → {}", view_name, parent_view)) |
| 158 | } else { |
| 159 | let s = repo |
| 160 | .provenance_summary(&view_name) |
| 161 | .map_err(CliError::Repository)?; |
| 162 | (s, format!("Project: {}", view_name)) |
| 163 | }; |
| 164 | |
| 165 | println!("{}", header_line); |
| 166 | println!(); |
| 167 | |
| 168 | // Headline. |
| 169 | match summary.ai_authored_pct() { |
| 170 | None => { |
| 171 | println!("No authored changes in this view yet."); |
| 172 | } |
| 173 | Some(pct) => { |
| 174 | let denom = summary.authored_denominator(); |
| 175 | println!( |
| 176 | "AI-authored: {:.0}% ({} of {} authored change{})", |
| 177 | pct, |
| 178 | summary.ai_changes, |
| 179 | denom, |
| 180 | if denom == 1 { "" } else { "s" }, |
| 181 | ); |
| 182 | if summary.human_changes > 0 { |
| 183 | println!( |
| 184 | "Human-authored: {} change{}", |
| 185 | summary.human_changes, |
| 186 | if summary.human_changes == 1 { "" } else { "s" }, |
| 187 | ); |
| 188 | } |
| 189 | if !summary.by_vendor.is_empty() { |
| 190 | let parts: Vec<String> = summary |
| 191 | .by_vendor |
| 192 | .iter() |
| 193 | .map(|(v, n)| format!("{} {}", pretty_vendor(v), n)) |
| 194 | .collect(); |
| 195 | println!("AI sources: {}", parts.join(" · ")); |
| 196 | } |
| 197 | if !summary.by_tool.is_empty() { |
| 198 | let parts: Vec<String> = summary |
| 199 | .by_tool |
| 200 | .iter() |
| 201 | .map(|(t, n)| format!("{} {}", pretty_tool(t), n)) |
| 202 | .collect(); |
| 203 | println!("Tools: {}", parts.join(" · ")); |
| 204 | } |
no test coverage detected