1use clap::{ArgAction, ValueEnum};
4
5use crate::build::{LaunchBundleRequest, build_launch_bundle};
6use crate::cli::MarsContext;
7use crate::error::MarsError;
8
9#[derive(Debug, clap::Args)]
10pub struct BuildArgs {
11 #[command(subcommand)]
12 pub command: BuildCommand,
13}
14
15#[derive(Debug, clap::Subcommand)]
16pub enum BuildCommand {
17 LaunchBundle(LaunchBundleArgs),
19}
20
21#[derive(Debug, Clone, ValueEnum)]
22enum HarnessArg {
23 Claude,
24 Codex,
25 Opencode,
26 Cursor,
27 Pi,
28}
29
30impl HarnessArg {
31 fn as_str(&self) -> &'static str {
32 match self {
33 Self::Claude => "claude",
34 Self::Codex => "codex",
35 Self::Opencode => "opencode",
36 Self::Cursor => "cursor",
37 Self::Pi => "pi",
38 }
39 }
40}
41
42#[derive(Debug, Clone, ValueEnum)]
43enum EffortArg {
44 Low,
45 Medium,
46 High,
47 Xhigh,
48}
49
50impl EffortArg {
51 fn as_str(&self) -> &'static str {
52 match self {
53 Self::Low => "low",
54 Self::Medium => "medium",
55 Self::High => "high",
56 Self::Xhigh => "xhigh",
57 }
58 }
59}
60
61#[derive(Debug, Clone, ValueEnum)]
62enum ApprovalArg {
63 Default,
64 Auto,
65 Confirm,
66 Yolo,
67}
68
69impl ApprovalArg {
70 fn as_str(&self) -> &'static str {
71 match self {
72 Self::Default => "default",
73 Self::Auto => "auto",
74 Self::Confirm => "confirm",
75 Self::Yolo => "yolo",
76 }
77 }
78}
79
80#[derive(Debug, Clone, ValueEnum)]
81enum SandboxArg {
82 Default,
83 ReadOnly,
84 WorkspaceWrite,
85 DangerFullAccess,
86}
87
88impl SandboxArg {
89 fn as_str(&self) -> &'static str {
90 match self {
91 Self::Default => "default",
92 Self::ReadOnly => "read-only",
93 Self::WorkspaceWrite => "workspace-write",
94 Self::DangerFullAccess => "danger-full-access",
95 }
96 }
97}
98
99#[derive(Debug, clap::Args)]
100pub struct LaunchBundleArgs {
101 #[arg(long)]
103 pub agent: String,
104
105 #[arg(long)]
107 pub model: Option<String>,
108
109 #[arg(long, value_enum)]
111 harness: Option<HarnessArg>,
112
113 #[arg(long, value_enum)]
115 effort: Option<EffortArg>,
116
117 #[arg(long, value_enum)]
119 approval: Option<ApprovalArg>,
120
121 #[arg(long, value_enum)]
123 sandbox: Option<SandboxArg>,
124
125 #[arg(long = "skill", value_delimiter = ',', action = ArgAction::Append)]
127 pub extra_skills: Vec<String>,
128}
129
130pub fn run(args: &BuildArgs, ctx: &MarsContext, _json: bool) -> Result<i32, MarsError> {
131 match &args.command {
132 BuildCommand::LaunchBundle(subargs) => run_launch_bundle(subargs, ctx),
133 }
134}
135
136fn run_launch_bundle(args: &LaunchBundleArgs, ctx: &MarsContext) -> Result<i32, MarsError> {
137 let bundle = build_launch_bundle(
138 ctx,
139 LaunchBundleRequest {
140 agent: args.agent.clone(),
141 model: args.model.clone(),
142 harness: args.harness.as_ref().map(|h| h.as_str().to_string()),
143 effort: args.effort.as_ref().map(|e| e.as_str().to_string()),
144 approval: args.approval.as_ref().map(|a| a.as_str().to_string()),
145 sandbox: args.sandbox.as_ref().map(|s| s.as_str().to_string()),
146 extra_skills: args.extra_skills.clone(),
147 },
148 )?;
149
150 println!(
151 "{}",
152 serde_json::to_string_pretty(&bundle).map_err(|err| MarsError::Internal(format!(
153 "failed to serialize launch bundle: {err}"
154 )))?
155 );
156
157 Ok(0)
158}