fn log_checkpoint_header_and_elapsed(checkpoint: &PipelineCheckpoint, logger: &Logger) -> bool {
use chrono::Local;
let phase_indicator = get_phase_indicator(checkpoint.phase);
logger.info(&format!("{} {}", phase_indicator, checkpoint.description()));
let Some(checkpoint_time) = parse_checkpoint_timestamp_as_local(&checkpoint.timestamp) else {
logger.info(&format!(
"Session was interrupted at: {}",
checkpoint.timestamp
));
return false;
};
let now = Local::now();
let duration = now.signed_duration_since(checkpoint_time);
logger.info(&format!(
"Session was interrupted: {}",
format_time_ago(duration)
));
true
}
fn log_rebase_conflict_summary(checkpoint: &PipelineCheckpoint, logger: &Logger) {
if !matches!(
checkpoint.rebase_state,
crate::checkpoint::RebaseState::HasConflicts { .. }
) {
return;
}
if let crate::checkpoint::RebaseState::HasConflicts { files } = &checkpoint.rebase_state {
logger.warn(&format!(
"Rebase conflicts detected in {} file(s)",
files.len()
));
let display_files: Vec<_> = files.iter().take(5).cloned().collect();
display_files.iter().for_each(|file| {
logger.info(&format!(" - {file}"));
});
if files.len() > 5 {
logger.info(&format!(" ... and {} more", files.len().saturating_sub(5)));
}
}
}
fn log_progress_summary(checkpoint: &PipelineCheckpoint, logger: &Logger) {
if checkpoint.total_iterations > 0 {
let progress_bar = create_progress_bar(
checkpoint.actual_developer_runs,
checkpoint.total_iterations,
);
logger.info(&format!(
"Development: {} {}/{} completed",
progress_bar, checkpoint.actual_developer_runs, checkpoint.total_iterations
));
}
if checkpoint.total_reviewer_passes > 0 {
let progress_bar = create_progress_bar(
checkpoint.actual_reviewer_runs,
checkpoint.total_reviewer_passes,
);
logger.info(&format!(
"Review: {} {}/{} completed",
progress_bar, checkpoint.actual_reviewer_runs, checkpoint.total_reviewer_passes
));
}
}
fn log_agent_and_command_summary(checkpoint: &PipelineCheckpoint, logger: &Logger) {
if checkpoint.resume_count > 0 {
logger.info(&format!(
"This session has been resumed {} time(s) before",
checkpoint.resume_count
));
}
if let Some(reconstructed_command) = reconstruct_command(checkpoint) {
logger.info(&format!("Original command: {reconstructed_command}"));
}
logger.info(&format!("Developer agent: {}", checkpoint.developer_agent));
logger.info(&format!("Reviewer agent: {}", checkpoint.reviewer_agent));
if let Some(ref model) = checkpoint.developer_agent_config.model_override {
logger.info(&format!("Developer model: {model}"));
}
if let Some(ref model) = checkpoint.reviewer_agent_config.model_override {
logger.info(&format!("Reviewer model: {model}"));
}
if let Some(ref provider) = checkpoint.developer_agent_config.provider_override {
logger.info(&format!("Developer provider: {provider}"));
}
if let Some(ref provider) = checkpoint.reviewer_agent_config.provider_override {
logger.info(&format!("Reviewer provider: {provider}"));
}
}
fn log_recent_activity_step(
step: &crate::checkpoint::execution_history::ExecutionStep,
logger: &Logger,
) {
let outcome_marker = outcome_marker_ascii(&step.outcome);
logger.info(&format!(
" {:<4} {} ({})",
outcome_marker, step.step_type, step.phase
));
if let Some(ref detail) = step.modified_files_detail {
let added_count = detail.added.as_ref().map_or(0, |v| v.len());
let modified_count = detail.modified.as_ref().map_or(0, |v| v.len());
let deleted_count = detail.deleted.as_ref().map_or(0, |v| v.len());
let total_files = added_count
.saturating_add(modified_count)
.saturating_add(deleted_count);
if total_files > 0 {
let parts: Vec<String> = [
(added_count > 0).then(|| format!("{added_count} added")),
(modified_count > 0).then(|| format!("{modified_count} modified")),
(deleted_count > 0).then(|| format!("{deleted_count} deleted")),
]
.into_iter()
.flatten()
.collect();
let file_summary = format!(" Files: {}", parts.join(", "));
logger.info(&file_summary);
}
}
if let Some(ref issues) = step.issues_summary {
if issues.found > 0 || issues.fixed > 0 {
logger.info(&format!(
" Issues: {} found, {} fixed",
issues.found, issues.fixed
));
}
}
if let Some(ref oid) = step.git_commit_oid {
let short_oid = if oid.len() > SHORT_OID_LENGTH {
&oid[..SHORT_OID_LENGTH]
} else {
oid
};
logger.info(&format!(" Commit: {short_oid}"));
}
}
fn log_recent_activity(
history: &crate::checkpoint::execution_history::ExecutionHistory,
logger: &Logger,
) {
if history.steps.is_empty() {
return;
}
logger.info(&format!(
"Execution history: {} step(s) recorded",
history.steps.len()
));
let recent_steps: Vec<_> = history
.steps
.iter()
.rev()
.take(5)
.collect::<Vec<_>>()
.into_iter()
.rev()
.collect();
logger.info("");
logger.info("Recent Activity:");
recent_steps.iter().for_each(|step| {
log_recent_activity_step(step, logger);
});
}
fn log_resume_next_steps(checkpoint: &PipelineCheckpoint, logger: &Logger) {
let next_step = suggest_next_step(checkpoint);
logger.info("");
logger.info(&format!("Next: {next_step}"));
logger.info("");
logger.info("To inspect the current state, you can run:");
logger.info(" git status - See current changes");
logger.info(" git log --oneline -5 - See recent commits");
}
fn display_user_friendly_checkpoint_summary(checkpoint: &PipelineCheckpoint, logger: &Logger) {
if !log_checkpoint_header_and_elapsed(checkpoint, logger) {
return;
}
log_rebase_conflict_summary(checkpoint, logger);
log_progress_summary(checkpoint, logger);
log_agent_and_command_summary(checkpoint, logger);
if let Some(ref history) = checkpoint.execution_history {
log_recent_activity(history, logger);
}
log_resume_next_steps(checkpoint, logger);
}
fn display_checkpoint_summary(checkpoint: &PipelineCheckpoint, logger: &Logger) {
logger.info(&format!("Resuming from: {}", checkpoint.description()));
logger.info(&format!("Checkpoint saved at: {}", checkpoint.timestamp));
logger.info(&format!("Checkpoint version: {}", checkpoint.version));
logger.info(&format!("Run ID: {}", checkpoint.run_id));
if checkpoint.resume_count > 0 {
logger.info(&format!(
"Resume count: {} (this is resume #{} of this session)",
checkpoint.resume_count,
checkpoint.resume_count.saturating_add(1)
));
}
if let Some(ref parent_id) = checkpoint.parent_run_id {
logger.info(&format!("Parent run ID: {parent_id}"));
}
logger.info(&format!(
"Development: {} iteration(s) configured, {} completed",
checkpoint.total_iterations, checkpoint.actual_developer_runs
));
logger.info(&format!(
"Review: {} pass(es) configured, {} completed",
checkpoint.total_reviewer_passes, checkpoint.actual_reviewer_runs
));
if checkpoint.total_iterations > 0 {
logger.info(&format!(
"Current position: iteration {}/{}",
checkpoint.iteration, checkpoint.total_iterations
));
}
if checkpoint.total_reviewer_passes > 0 {
logger.info(&format!(
"Current position: pass {}/{}",
checkpoint.reviewer_pass, checkpoint.total_reviewer_passes
));
}
let cli = &checkpoint.cli_args;
if cli.developer_iters > 0 || cli.reviewer_reviews > 0 {
logger.info(&format!(
"Original config: -D {} -R {}",
cli.developer_iters, cli.reviewer_reviews
));
}
logger.info(&format!("Developer agent: {}", checkpoint.developer_agent));
logger.info(&format!("Reviewer agent: {}", checkpoint.reviewer_agent));
if let Some(ref model) = checkpoint.developer_agent_config.model_override {
logger.info(&format!("Developer model override: {model}"));
}
if let Some(ref model) = checkpoint.reviewer_agent_config.model_override {
logger.info(&format!("Reviewer model override: {model}"));
}
if let Some(ref provider) = checkpoint.developer_agent_config.provider_override {
logger.info(&format!("Developer provider: {provider}"));
}
if let Some(ref provider) = checkpoint.reviewer_agent_config.provider_override {
logger.info(&format!("Reviewer provider: {provider}"));
}
match &checkpoint.rebase_state {
crate::checkpoint::RebaseState::PreRebaseInProgress { upstream_branch } => {
logger.warn(&format!("Pre-rebase in progress to: {upstream_branch}"));
}
crate::checkpoint::RebaseState::HasConflicts { files } => {
logger.warn(&format!("Rebase has conflicts in {} files", files.len()));
files.iter().take(3).for_each(|file| {
logger.warn(&format!(" - {file}"));
});
if files.len() > 3 {
logger.warn(&format!(" ... and {} more", files.len().saturating_sub(3)));
}
}
_ => {}
}
}