1use anyhow::{Result, anyhow};
16
17use crate::{agent, commands::run as run_cmd, config, debuglog};
18
19use super::args::{ParallelSubcommand, RunCommand, RunLoopArgs, RunOneArgs};
20
21pub fn handle_run(cmd: RunCommand, force: bool) -> Result<()> {
22 let profile = selected_profile(&cmd);
23 let resolved = config::resolve_from_cwd_with_profile(profile)?;
24
25 match cmd {
26 RunCommand::Resume(args) => {
27 maybe_enable_debug(args.debug, &resolved)?;
28 let overrides = agent::resolve_run_agent_overrides(&args.agent)?;
29 run_cmd::run_loop(
30 &resolved,
31 run_cmd::RunLoopOptions {
32 max_tasks: 0,
33 agent_overrides: overrides,
34 force: args.force || force,
35 auto_resume: true,
36 starting_completed: 0,
37 non_interactive: args.non_interactive,
38 parallel_workers: None,
39 wait_when_blocked: false,
40 wait_poll_ms: 1000,
41 wait_timeout_seconds: 0,
42 notify_when_unblocked: false,
43 wait_when_empty: false,
44 empty_poll_ms: 30_000,
45 run_event_handler: None,
46 },
47 )
48 }
49 RunCommand::One(args) => handle_run_one(args, force, &resolved),
50 RunCommand::Loop(args) => handle_run_loop(args, force, &resolved),
51 RunCommand::Parallel(args) => match args.command {
52 ParallelSubcommand::Status(status_args) => {
53 run_cmd::parallel_status(&resolved, status_args.json)
54 }
55 ParallelSubcommand::Retry(retry_args) => {
56 run_cmd::parallel_retry(&resolved, &retry_args.task)
57 }
58 },
59 }
60}
61
62fn selected_profile(cmd: &RunCommand) -> Option<&str> {
63 match cmd {
64 RunCommand::Resume(args) => args.agent.profile.as_deref(),
65 RunCommand::One(args) => args.agent.profile.as_deref(),
66 RunCommand::Loop(args) => args.agent.profile.as_deref(),
67 RunCommand::Parallel(_) => None,
68 }
69}
70
71fn maybe_enable_debug(debug: bool, resolved: &config::Resolved) -> Result<()> {
72 if debug {
73 debuglog::enable(&resolved.repo_root)?;
74 }
75 Ok(())
76}
77
78fn handle_run_one(args: RunOneArgs, force: bool, resolved: &config::Resolved) -> Result<()> {
79 maybe_enable_debug(args.debug, resolved)?;
80 let overrides = agent::resolve_run_agent_overrides(&args.agent)?;
81
82 if args.dry_run {
83 if args.parallel_worker {
84 return Err(anyhow!("--dry-run cannot be used with --parallel-worker"));
85 }
86 return run_cmd::dry_run_one(resolved, &overrides, args.id.as_deref());
87 }
88
89 if args.parallel_worker {
90 return handle_parallel_worker_run_one(args, force, resolved, overrides);
91 }
92
93 let resume_options = run_cmd::RunOneResumeOptions::detect(args.resume, args.non_interactive);
94 if let Some(task_id) = args.id.as_deref() {
95 run_cmd::run_one_with_id(
96 resolved,
97 &overrides,
98 force,
99 task_id,
100 resume_options,
101 None,
102 None,
103 None,
104 )?;
105 } else {
106 run_cmd::run_one(resolved, &overrides, force, resume_options)?;
107 }
108
109 Ok(())
110}
111
112fn handle_parallel_worker_run_one(
113 args: RunOneArgs,
114 force: bool,
115 resolved: &config::Resolved,
116 overrides: crate::agent::AgentOverrides,
117) -> Result<()> {
118 let task_id = args
119 .id
120 .as_deref()
121 .ok_or_else(|| anyhow!("--parallel-worker requires --id <TASK_ID>"))?;
122 let target_branch = args
123 .parallel_target_branch
124 .as_deref()
125 .ok_or_else(|| anyhow!("--parallel-worker requires --parallel-target-branch"))?;
126
127 let mut worker_resolved = resolved.clone();
128 worker_resolved.queue_path = args
129 .coordinator_queue_path
130 .clone()
131 .ok_or_else(|| anyhow!("--parallel-worker requires --coordinator-queue-path"))?;
132 worker_resolved.done_path = args
133 .coordinator_done_path
134 .clone()
135 .ok_or_else(|| anyhow!("--parallel-worker requires --coordinator-done-path"))?;
136
137 log::debug!(
138 "parallel worker using queue/done paths: queue={}, done={}, target_branch={}",
139 worker_resolved.queue_path.display(),
140 worker_resolved.done_path.display(),
141 target_branch
142 );
143
144 run_cmd::run_one_parallel_worker(&worker_resolved, &overrides, force, task_id, target_branch)?;
145 Ok(())
146}
147
148fn handle_run_loop(args: RunLoopArgs, force: bool, resolved: &config::Resolved) -> Result<()> {
149 maybe_enable_debug(args.debug, resolved)?;
150 let overrides = agent::resolve_run_agent_overrides(&args.agent)?;
151
152 if args.dry_run {
153 return run_cmd::dry_run_loop(resolved, &overrides);
154 }
155
156 run_cmd::run_loop(
157 resolved,
158 run_cmd::RunLoopOptions {
159 max_tasks: args.max_tasks,
160 agent_overrides: overrides,
161 force,
162 auto_resume: args.resume,
163 starting_completed: 0,
164 non_interactive: args.non_interactive,
165 parallel_workers: args.parallel,
166 wait_when_blocked: args.wait_when_blocked,
167 wait_poll_ms: args.wait_poll_ms,
168 wait_timeout_seconds: args.wait_timeout_seconds,
169 notify_when_unblocked: args.notify_when_unblocked,
170 wait_when_empty: args.wait_when_empty,
171 empty_poll_ms: args.empty_poll_ms,
172 run_event_handler: None,
173 },
174 )
175}