summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsoryu <soryu@soryu.co>2026-01-24 13:05:46 +0000
committersoryu <soryu@soryu.co>2026-01-24 13:05:46 +0000
commit854075a944f9f70bccb90cd2ef42c07a3a4bcc91 (patch)
tree75aead06a644517267134bbb2c623f39b5e27f50
parent1bd24cba69291432cc182bc2a718dcc4aaac45e2 (diff)
downloadsoryu-makima/add-learning.tar.gz
soryu-makima/add-learning.zip
feat: Integrate learning modules into task managermakima/add-learning
When tasks complete (success or failure), the task manager now: - Creates a ProgressLogEntry with task status and timestamp - Extracts learnings from Claude output using extract_learnings_from_output - Gets list of changed files from git diff - Appends the entry to progress.log in the task's worktree This enables cross-task learning where future tasks can benefit from patterns, gotchas, and tips discovered during previous task executions. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
-rw-r--r--makima/src/daemon/task/manager.rs116
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) {