diff options
| -rw-r--r-- | makima/src/daemon/task/manager.rs | 116 |
1 files changed, 116 insertions, 0 deletions
diff --git a/makima/src/daemon/task/manager.rs b/makima/src/daemon/task/manager.rs index 1138cdf..1f1e2c2 100644 --- a/makima/src/daemon/task/manager.rs +++ b/makima/src/daemon/task/manager.rs @@ -4567,6 +4567,17 @@ impl TaskManagerInner { } } + // Log task completion to progress.log (for non-supervisor tasks) + if !is_supervisor { + self.log_task_completion( + task_id, + &task_name, + &working_dir, + success, + &accumulated_output, + ).await; + } + // Execute completion action if task succeeded let completion_result = if success { if let Some(ref action) = completion_action { @@ -4811,6 +4822,111 @@ impl TaskManagerInner { )) } + /// Log task completion to progress.log for cross-task learning. + /// + /// This creates a `ProgressLogEntry` from the completed task, extracts any learnings + /// from the task's last output, and appends the entry to `progress.log` in the task's worktree. + async fn log_task_completion( + &self, + task_id: Uuid, + task_name: &str, + worktree_path: &std::path::Path, + success: bool, + accumulated_output: &str, + ) { + // Determine completion status + let status = if success { + TaskCompletionStatus::Done + } else { + TaskCompletionStatus::Failed + }; + + // Create the progress log entry + let mut entry = ProgressLogEntry::new(task_id.to_string(), task_name, status); + + // Extract learnings from the accumulated output + let learnings = extract_learnings_from_output(accumulated_output); + if !learnings.is_empty() { + tracing::info!( + task_id = %task_id, + learning_count = learnings.len(), + "Extracted learnings from task output" + ); + entry = entry.with_learnings(learnings); + } + + // Get list of changed files from git diff + let changed_files = self.get_changed_files(worktree_path).await; + if !changed_files.is_empty() { + entry = entry.with_files_changed(changed_files); + } + + // Append the entry to progress.log + if let Err(e) = progress_log::append_entry(worktree_path, &entry) { + tracing::warn!( + task_id = %task_id, + error = %e, + "Failed to append progress log entry" + ); + } else { + tracing::info!( + task_id = %task_id, + status = %status, + "Logged task completion to progress.log" + ); + } + } + + /// Get list of changed files in the worktree using git diff. + async fn get_changed_files(&self, worktree_path: &std::path::Path) -> Vec<String> { + use tokio::process::Command; + + // Get both staged and unstaged changes + let output = Command::new("git") + .args(["diff", "--name-only", "HEAD"]) + .current_dir(worktree_path) + .output() + .await; + + match output { + Ok(output) if output.status.success() => { + let files: Vec<String> = String::from_utf8_lossy(&output.stdout) + .lines() + .filter(|l| !l.is_empty()) + .map(|s| s.to_string()) + .collect(); + + // Also get untracked files + if let Ok(untracked) = Command::new("git") + .args(["ls-files", "--others", "--exclude-standard"]) + .current_dir(worktree_path) + .output() + .await + { + if untracked.status.success() { + let mut all_files = files; + all_files.extend( + String::from_utf8_lossy(&untracked.stdout) + .lines() + .filter(|l| !l.is_empty()) + .map(|s| s.to_string()) + ); + return all_files; + } + } + + files + } + _ => { + tracing::debug!( + worktree = %worktree_path.display(), + "Failed to get changed files from git" + ); + Vec::new() + } + } + } + async fn update_state(&self, task_id: Uuid, state: TaskState) { let mut tasks = self.tasks.write().await; if let Some(task) = tasks.get_mut(&task_id) { |
