chronicle/annotate/
mod.rs1pub mod filter;
2pub mod gather;
3pub mod squash;
4
5use crate::error::{chronicle_error, Result};
6use crate::git::GitOps;
7use crate::provider::LlmProvider;
8use crate::schema::{Annotation, ContextLevel};
9use snafu::ResultExt;
10
11pub fn run(
14 git_ops: &dyn GitOps,
15 provider: &dyn LlmProvider,
16 commit: &str,
17 _sync: bool,
18) -> Result<Annotation> {
19 let context = gather::build_context(git_ops, commit)?;
21
22 let decision = filter::pre_llm_filter(&context);
24
25 let annotation = match decision {
26 filter::FilterDecision::Skip(reason) => {
27 tracing::info!("Skipping annotation: {}", reason);
28 Annotation::new_initial(
30 context.commit_sha.clone(),
31 format!("Skipped: {}", reason),
32 ContextLevel::Inferred,
33 )
34 }
35 filter::FilterDecision::Trivial(reason) => {
36 tracing::info!("Trivial commit: {}", reason);
37 let mut ann = Annotation::new_initial(
39 context.commit_sha.clone(),
40 context.commit_message.clone(),
41 ContextLevel::Inferred,
42 );
43 if let Some(ref author_ctx) = context.author_context {
45 ann.task = author_ctx.task.clone();
46 }
47 ann
48 }
49 filter::FilterDecision::Annotate => {
50 let (regions, cross_cutting, summary) =
52 crate::agent::run_agent_loop(provider, git_ops, &context)
53 .context(chronicle_error::AgentSnafu)?;
54
55 let context_level = if context.author_context.is_some() {
56 ContextLevel::Enhanced
57 } else {
58 ContextLevel::Inferred
59 };
60
61 let mut ann =
62 Annotation::new_initial(context.commit_sha.clone(), summary, context_level);
63 ann.regions = regions;
64 ann.cross_cutting = cross_cutting;
65 if let Some(ref author_ctx) = context.author_context {
67 ann.task = author_ctx.task.clone();
68 }
69 ann
70 }
71 };
72
73 let json = serde_json::to_string_pretty(&annotation).context(chronicle_error::JsonSnafu)?;
75 git_ops
76 .note_write(commit, &json)
77 .context(chronicle_error::GitSnafu)?;
78
79 Ok(annotation)
80}