diff options
| author | soryu <soryu@soryu.co> | 2026-02-10 14:50:07 +0000 |
|---|---|---|
| committer | soryu <soryu@soryu.co> | 2026-02-10 14:50:07 +0000 |
| commit | 15b6e5fba161a194fe5427d7d29b0c4286423260 (patch) | |
| tree | fdd7bde229150cbb56d37714c23c2dc9db902f28 /makima/src/daemon | |
| parent | 526edf672aae73c3670ab6141253bf92f1fbfe8c (diff) | |
| download | soryu-15b6e5fba161a194fe5427d7d29b0c4286423260.tar.gz soryu-15b6e5fba161a194fe5427d7d29b0c4286423260.zip | |
Add auto-PR creation for remote repos in directives
Diffstat (limited to 'makima/src/daemon')
| -rw-r--r-- | makima/src/daemon/api/directive.rs | 20 | ||||
| -rw-r--r-- | makima/src/daemon/cli/directive.rs | 15 | ||||
| -rw-r--r-- | makima/src/daemon/cli/mod.rs | 3 | ||||
| -rw-r--r-- | makima/src/daemon/task/manager.rs | 45 | ||||
| -rw-r--r-- | makima/src/daemon/worktree/manager.rs | 92 |
5 files changed, 161 insertions, 14 deletions
diff --git a/makima/src/daemon/api/directive.rs b/makima/src/daemon/api/directive.rs index cd21692..5886766 100644 --- a/makima/src/daemon/api/directive.rs +++ b/makima/src/daemon/api/directive.rs @@ -134,4 +134,24 @@ impl ApiClient { let req = UpdateGoalRequest { goal: goal.to_string() }; self.put(&format!("/api/v1/directives/{}/goal", directive_id), &req).await } + + /// Update directive metadata (PR URL, PR branch, etc.) + pub async fn directive_update( + &self, + directive_id: Uuid, + pr_url: Option<String>, + pr_branch: Option<String>, + ) -> Result<JsonValue, ApiError> { + let req = UpdateDirectiveMetadataRequest { pr_url, pr_branch }; + self.put(&format!("/api/v1/directives/{}", directive_id), &req).await + } +} + +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +pub struct UpdateDirectiveMetadataRequest { + #[serde(skip_serializing_if = "Option::is_none")] + pub pr_url: Option<String>, + #[serde(skip_serializing_if = "Option::is_none")] + pub pr_branch: Option<String>, } diff --git a/makima/src/daemon/cli/directive.rs b/makima/src/daemon/cli/directive.rs index cd94a56..2e6ac1d 100644 --- a/makima/src/daemon/cli/directive.rs +++ b/makima/src/daemon/cli/directive.rs @@ -110,3 +110,18 @@ pub struct BatchAddStepsArgs { #[arg(long)] pub json: String, } + +/// Arguments for update command. +#[derive(Args, Debug)] +pub struct UpdateArgs { + #[command(flatten)] + pub common: DirectiveArgs, + + /// PR URL to store on the directive + #[arg(long)] + pub pr_url: Option<String>, + + /// PR branch name to store on the directive + #[arg(long)] + pub pr_branch: Option<String>, +} diff --git a/makima/src/daemon/cli/mod.rs b/makima/src/daemon/cli/mod.rs index 98923d9..bcaaa70 100644 --- a/makima/src/daemon/cli/mod.rs +++ b/makima/src/daemon/cli/mod.rs @@ -246,6 +246,9 @@ pub enum DirectiveCommand { /// Batch add multiple steps from JSON BatchAddSteps(directive::BatchAddStepsArgs), + + /// Update directive metadata (PR URL, etc.) + Update(directive::UpdateArgs), } impl Cli { diff --git a/makima/src/daemon/task/manager.rs b/makima/src/daemon/task/manager.rs index a24f527..22b41d9 100644 --- a/makima/src/daemon/task/manager.rs +++ b/makima/src/daemon/task/manager.rs @@ -5587,8 +5587,8 @@ impl TaskManagerInner { let target_repo = match target_repo_path { Some(path) => Some(crate::daemon::worktree::expand_tilde(path)), None => { - if action == "pr" { - // For PR action, check if worktree has an origin remote we can use directly + if action == "pr" || action == "branch" { + // For PR/branch action without target_repo, use origin directly None } else { tracing::warn!(task_id = %task_id, "No target_repo_path configured, skipping completion action"); @@ -5633,19 +5633,36 @@ impl TaskManagerInner { match action { "branch" => { - let target_repo = target_repo.ok_or_else(|| "No target_repo_path configured for branch action".to_string())?; - // Just push the branch to target repo - self.worktree_manager - .push_to_target_repo(worktree_path, &target_repo, &branch_name, task_name) - .await - .map_err(|e| e.to_string())?; + match target_repo { + Some(target_repo) => { + // Push branch to local target repo + self.worktree_manager + .push_to_target_repo(worktree_path, &target_repo, &branch_name, task_name) + .await + .map_err(|e| e.to_string())?; - let msg = DaemonMessage::task_output( - task_id, - format!("Branch '{}' pushed to {}\n", branch_name, target_repo.display()), - false, - ); - let _ = self.ws_tx.send(msg).await; + let msg = DaemonMessage::task_output( + task_id, + format!("Branch '{}' pushed to {}\n", branch_name, target_repo.display()), + false, + ); + let _ = self.ws_tx.send(msg).await; + } + None => { + // Push branch to origin (GitHub) + self.worktree_manager + .push_branch_to_origin(worktree_path, &branch_name, task_name) + .await + .map_err(|e| e.to_string())?; + + let msg = DaemonMessage::task_output( + task_id, + format!("Branch '{}' pushed to origin\n", branch_name), + false, + ); + let _ = self.ws_tx.send(msg).await; + } + } Ok(None) } "merge" => { diff --git a/makima/src/daemon/worktree/manager.rs b/makima/src/daemon/worktree/manager.rs index 310627c..20c93b1 100644 --- a/makima/src/daemon/worktree/manager.rs +++ b/makima/src/daemon/worktree/manager.rs @@ -1417,6 +1417,98 @@ impl WorktreeManager { Ok(()) } + /// Push a worktree branch to origin (the upstream GitHub remote). + /// Simpler than push_to_target_repo — just pushes to origin. + pub async fn push_branch_to_origin( + &self, + worktree_path: &Path, + branch_name: &str, + task_name: &str, + ) -> Result<(), WorktreeError> { + tracing::info!( + worktree = %worktree_path.display(), + branch = %branch_name, + "Pushing branch to origin" + ); + + // Stage all changes + let output = Command::new("git") + .args(["add", "-A"]) + .current_dir(worktree_path) + .output() + .await?; + + if !output.status.success() { + let stderr = String::from_utf8_lossy(&output.stderr); + return Err(WorktreeError::GitCommand(format!( + "Failed to stage changes: {}", + stderr + ))); + } + + // Check if there are staged changes to commit + let output = Command::new("git") + .args(["diff", "--cached", "--quiet"]) + .current_dir(worktree_path) + .output() + .await?; + + // Exit code 1 means there are staged changes + if !output.status.success() { + tracing::info!("Committing staged changes before push to origin"); + + let commit_message = format!("feat: {}", task_name); + let output = Command::new("git") + .args(["commit", "-m", &commit_message]) + .current_dir(worktree_path) + .output() + .await?; + + if !output.status.success() { + let stderr = String::from_utf8_lossy(&output.stderr); + return Err(WorktreeError::GitCommand(format!( + "Failed to commit changes: {}", + stderr + ))); + } + } + + // Ensure there are commits to push + let output = Command::new("git") + .args(["log", "--oneline", "-1"]) + .current_dir(worktree_path) + .output() + .await?; + + if !output.status.success() { + return Err(WorktreeError::GitCommand( + "No commits in worktree".to_string(), + )); + } + + // Push to origin + let output = Command::new("git") + .args(["push", "-u", "origin", &format!("HEAD:{}", branch_name)]) + .current_dir(worktree_path) + .output() + .await?; + + if !output.status.success() { + let stderr = String::from_utf8_lossy(&output.stderr); + return Err(WorktreeError::GitCommand(format!( + "Failed to push to origin: {}", + stderr + ))); + } + + tracing::info!( + branch = %branch_name, + "Branch pushed to origin successfully" + ); + + Ok(()) + } + /// Merge a branch into the target branch in the target repository. /// /// This pushes the branch first (if needed), then performs a merge in the target repo. |
