summaryrefslogtreecommitdiff
path: root/makima/src/server/handlers/mesh_daemon.rs
diff options
context:
space:
mode:
Diffstat (limited to 'makima/src/server/handlers/mesh_daemon.rs')
-rw-r--r--makima/src/server/handlers/mesh_daemon.rs200
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);
}