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)]
104 pub agent: Option<String>,
105
106 #[arg(long)]
108 pub model: Option<String>,
109
110 #[arg(long, value_enum)]
112 harness: Option<HarnessArg>,
113
114 #[arg(long, value_enum)]
116 effort: Option<EffortArg>,
117
118 #[arg(long, value_enum)]
120 approval: Option<ApprovalArg>,
121
122 #[arg(long, value_enum)]
124 sandbox: Option<SandboxArg>,
125
126 #[arg(long = "skill", value_delimiter = ',', action = ArgAction::Append)]
128 pub extra_skills: Vec<String>,
129}
130
131pub fn run(args: &BuildArgs, ctx: &MarsContext, _json: bool) -> Result<i32, MarsError> {
132 match &args.command {
133 BuildCommand::LaunchBundle(subargs) => run_launch_bundle(subargs, ctx),
134 }
135}
136
137fn run_launch_bundle(args: &LaunchBundleArgs, ctx: &MarsContext) -> Result<i32, MarsError> {
138 let bundle = build_launch_bundle(
139 ctx,
140 LaunchBundleRequest {
141 agent: args.agent.clone(),
142 model: args.model.clone(),
143 harness: args.harness.as_ref().map(|h| h.as_str().to_string()),
144 effort: args.effort.as_ref().map(|e| e.as_str().to_string()),
145 approval: args.approval.as_ref().map(|a| a.as_str().to_string()),
146 sandbox: args.sandbox.as_ref().map(|s| s.as_str().to_string()),
147 extra_skills: args.extra_skills.clone(),
148 },
149 )?;
150
151 println!(
152 "{}",
153 serde_json::to_string_pretty(&bundle).map_err(|err| MarsError::Internal(format!(
154 "failed to serialize launch bundle: {err}"
155 )))?
156 );
157
158 Ok(0)
159}