diff options
Diffstat (limited to 'makima/src/server/handlers/mesh_daemon.rs')
| -rw-r--r-- | makima/src/server/handlers/mesh_daemon.rs | 200 |
1 files changed, 199 insertions, 1 deletions
diff --git a/makima/src/server/handlers/mesh_daemon.rs b/makima/src/server/handlers/mesh_daemon.rs index 84c7a93..1caf805 100644 --- a/makima/src/server/handlers/mesh_daemon.rs +++ b/makima/src/server/handlers/mesh_daemon.rs @@ -519,7 +519,7 @@ pub enum DaemonMessage { } /// Information about a changed file in a worktree. -#[derive(Debug, Clone, Deserialize)] +#[derive(Debug, Clone, serde::Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct ChangedFileInfo { /// File path relative to worktree root @@ -1845,6 +1845,204 @@ async fn handle_daemon_connection(socket: WebSocket, state: SharedState, auth_re ); } } + Ok(DaemonMessage::ExportPatchCreated { + task_id, + contract_id, + success, + patch_content, + files_count, + lines_added, + lines_removed, + base_commit_sha, + error, + }) => { + tracing::info!( + task_id = %task_id, + contract_id = %contract_id, + success = success, + files_count = ?files_count, + "Export patch result received" + ); + + if success { + if let (Some(pool), Some(patch)) = (&state.db_pool, patch_content) { + // Get task details for naming + let task_name = match repository::get_task(pool, task_id).await { + Ok(Some(task)) => task.name, + _ => format!("Task {}", task_id), + }; + + // Create contract file with patch content + let file_name = format!("{} - Patch", task_name); + + // Build patch file content with instructions + let body_content = format!( + "# Patch: {}\n\n\ + ## Metadata\n\ + - **Task ID:** `{}`\n\ + - **Base Commit:** `{}`\n\ + - **Files Changed:** {}\n\ + - **Lines Added:** {}\n\ + - **Lines Removed:** {}\n\n\ + ## How to Apply\n\n\ + ```bash\n\ + # Save this patch to a file\n\ + # Then apply with:\n\ + git apply --check patch.diff # Dry run first\n\ + git apply patch.diff # Apply for real\n\ + ```\n\n\ + ## Patch Content\n\n\ + ```diff\n\ + {}\n\ + ```", + task_name, + task_id, + base_commit_sha.as_deref().unwrap_or("unknown"), + files_count.unwrap_or(0), + lines_added.unwrap_or(0), + lines_removed.unwrap_or(0), + patch + ); + + // Convert markdown to body elements + let body = crate::llm::markdown_to_body(&body_content); + + // Create the file request + let create_req = crate::db::models::CreateFileRequest { + name: file_name.clone(), + description: Some(format!( + "Git patch from task '{}' ({} files, +{} -{} lines)", + task_name, + files_count.unwrap_or(0), + lines_added.unwrap_or(0), + lines_removed.unwrap_or(0) + )), + transcript: None, + summary: None, + body: Some(body), + contract_id: Some(contract_id), + duration: None, + repo_file_path: None, + }; + + match repository::create_file_for_owner(pool, owner_id, create_req).await { + Ok(file) => { + tracing::info!( + file_id = %file.id, + task_id = %task_id, + contract_id = %contract_id, + "Created patch file in contract" + ); + + // Broadcast file creation notification + state.broadcast_file_update(crate::server::state::FileUpdateNotification { + file_id: file.id, + version: 1, + updated_fields: vec!["created".to_string()], + updated_by: "daemon".to_string(), + }); + + // Record history event + let _ = repository::record_history_event( + pool, + owner_id, + Some(contract_id), + Some(task_id), + "file", + Some("patch_exported"), + None, + serde_json::json!({ + "fileId": file.id, + "fileName": file_name, + "filesCount": files_count, + "linesAdded": lines_added, + "linesRemoved": lines_removed, + }), + ).await; + + // Notify supervisor if applicable + if let Ok(Some(supervisor)) = repository::get_contract_supervisor_task(pool, contract_id).await { + let msg = format!( + "[INFO] Patch exported successfully!\n\ + File: {}\n\ + Changes: {} files, +{} -{} lines\n\n\ + The patch has been saved to the contract files.", + file_name, + files_count.unwrap_or(0), + lines_added.unwrap_or(0), + lines_removed.unwrap_or(0) + ); + let _ = state.notify_supervisor( + supervisor.id, + supervisor.daemon_id, + &msg, + ).await; + } + } + Err(e) => { + tracing::error!( + task_id = %task_id, + contract_id = %contract_id, + error = %e, + "Failed to create patch file" + ); + } + } + } + } else { + tracing::warn!( + task_id = %task_id, + error = ?error, + "Failed to export patch" + ); + } + } + Ok(DaemonMessage::WorktreeInfo { + task_id, + success, + path, + branch, + base_commit, + files_changed, + has_uncommitted_changes, + error, + }) => { + tracing::info!( + task_id = %task_id, + success = success, + path = ?path, + branch = ?branch, + files_count = ?files_changed.as_ref().map(|f| f.len()), + "Worktree info received" + ); + + // Broadcast as task output for the UI to display + if success { + let info = serde_json::json!({ + "type": "worktreeInfo", + "path": path, + "branch": branch, + "baseCommit": base_commit, + "filesChanged": files_changed, + "hasUncommittedChanges": has_uncommitted_changes, + }); + + state.broadcast_task_output(crate::server::state::TaskOutputNotification { + task_id, + owner_id: Some(owner_id), + message_type: "worktree_info".to_string(), + content: serde_json::to_string_pretty(&info).unwrap_or_default(), + tool_name: None, + tool_input: Some(info), + }); + } else { + tracing::warn!( + task_id = %task_id, + error = ?error, + "Failed to get worktree info" + ); + } + } Err(e) => { tracing::warn!("Failed to parse daemon message: {}", e); } |
