Skip to main content

codex_utils_cli/
shared_options.rs

1//! Shared command-line flags used by both interactive and non-interactive Codex entry points.
2
3use crate::SandboxModeCliArg;
4use clap::Args;
5use codex_protocol::config_types::ProfileV2Name;
6use std::path::PathBuf;
7
8#[derive(Args, Clone, Debug, Default)]
9pub struct SharedCliOptions {
10    /// Optional image(s) to attach to the initial prompt.
11    #[arg(
12        long = "image",
13        short = 'i',
14        value_name = "FILE",
15        value_delimiter = ',',
16        num_args = 1..
17    )]
18    pub images: Vec<PathBuf>,
19
20    /// Model the agent should use.
21    #[arg(long, short = 'm')]
22    pub model: Option<String>,
23
24    /// Use open-source provider.
25    #[arg(long = "oss", default_value_t = false)]
26    pub oss: bool,
27
28    /// Specify which local provider to use (lmstudio or ollama).
29    /// If not specified with --oss, will use config default or show selection.
30    #[arg(long = "local-provider")]
31    pub oss_provider: Option<String>,
32
33    /// Layer $CODEX_HOME/<name>.config.toml on top of the base user config.
34    #[arg(long = "profile", short = 'p')]
35    pub config_profile_v2: Option<ProfileV2Name>,
36
37    /// Select the sandbox policy to use when executing model-generated shell
38    /// commands.
39    #[arg(long = "sandbox", short = 's')]
40    pub sandbox_mode: Option<SandboxModeCliArg>,
41
42    /// Skip all confirmation prompts and execute commands without sandboxing.
43    /// EXTREMELY DANGEROUS. Intended solely for running in environments that are externally sandboxed.
44    #[arg(
45        long = "dangerously-bypass-approvals-and-sandbox",
46        alias = "yolo",
47        default_value_t = false
48    )]
49    pub dangerously_bypass_approvals_and_sandbox: bool,
50
51    /// Run enabled hooks without requiring persisted hook trust for this invocation.
52    /// DANGEROUS. Intended only for automation that already vets hook sources.
53    #[arg(long = "dangerously-bypass-hook-trust", default_value_t = false)]
54    pub bypass_hook_trust: bool,
55
56    /// Tell the agent to use the specified directory as its working root.
57    #[clap(long = "cd", short = 'C', value_name = "DIR")]
58    pub cwd: Option<PathBuf>,
59
60    /// Additional directories that should be writable alongside the primary workspace.
61    #[arg(long = "add-dir", value_name = "DIR", value_hint = clap::ValueHint::DirPath)]
62    pub add_dir: Vec<PathBuf>,
63}
64
65impl SharedCliOptions {
66    pub fn inherit_exec_root_options(&mut self, root: &Self) {
67        let self_selected_sandbox_mode =
68            self.sandbox_mode.is_some() || self.dangerously_bypass_approvals_and_sandbox;
69        let Self {
70            images,
71            model,
72            oss,
73            oss_provider,
74            config_profile_v2,
75            sandbox_mode,
76            dangerously_bypass_approvals_and_sandbox,
77            bypass_hook_trust,
78            cwd,
79            add_dir,
80        } = self;
81        let Self {
82            images: root_images,
83            model: root_model,
84            oss: root_oss,
85            oss_provider: root_oss_provider,
86            config_profile_v2: root_config_profile_v2,
87            sandbox_mode: root_sandbox_mode,
88            dangerously_bypass_approvals_and_sandbox: root_dangerously_bypass_approvals_and_sandbox,
89            bypass_hook_trust: root_bypass_hook_trust,
90            cwd: root_cwd,
91            add_dir: root_add_dir,
92        } = root;
93
94        if model.is_none() {
95            model.clone_from(root_model);
96        }
97        if *root_oss {
98            *oss = true;
99        }
100        if oss_provider.is_none() {
101            oss_provider.clone_from(root_oss_provider);
102        }
103        if config_profile_v2.is_none() {
104            config_profile_v2.clone_from(root_config_profile_v2);
105        }
106        if sandbox_mode.is_none() {
107            *sandbox_mode = *root_sandbox_mode;
108        }
109        if !self_selected_sandbox_mode {
110            *dangerously_bypass_approvals_and_sandbox =
111                *root_dangerously_bypass_approvals_and_sandbox;
112        }
113        if !*bypass_hook_trust {
114            *bypass_hook_trust = *root_bypass_hook_trust;
115        }
116        if cwd.is_none() {
117            cwd.clone_from(root_cwd);
118        }
119        if !root_images.is_empty() {
120            let mut merged_images = root_images.clone();
121            merged_images.append(images);
122            *images = merged_images;
123        }
124        if !root_add_dir.is_empty() {
125            let mut merged_add_dir = root_add_dir.clone();
126            merged_add_dir.append(add_dir);
127            *add_dir = merged_add_dir;
128        }
129    }
130
131    pub fn apply_subcommand_overrides(&mut self, subcommand: Self) {
132        let subcommand_selected_sandbox_mode = subcommand.sandbox_mode.is_some()
133            || subcommand.dangerously_bypass_approvals_and_sandbox;
134        let Self {
135            images,
136            model,
137            oss,
138            oss_provider,
139            config_profile_v2,
140            sandbox_mode,
141            dangerously_bypass_approvals_and_sandbox,
142            bypass_hook_trust,
143            cwd,
144            add_dir,
145        } = subcommand;
146
147        if let Some(model) = model {
148            self.model = Some(model);
149        }
150        if oss {
151            self.oss = true;
152        }
153        if let Some(oss_provider) = oss_provider {
154            self.oss_provider = Some(oss_provider);
155        }
156        if let Some(config_profile_v2) = config_profile_v2 {
157            self.config_profile_v2 = Some(config_profile_v2);
158        }
159        if subcommand_selected_sandbox_mode {
160            self.sandbox_mode = sandbox_mode;
161            self.dangerously_bypass_approvals_and_sandbox =
162                dangerously_bypass_approvals_and_sandbox;
163        }
164        if bypass_hook_trust {
165            self.bypass_hook_trust = true;
166        }
167        if let Some(cwd) = cwd {
168            self.cwd = Some(cwd);
169        }
170        if !images.is_empty() {
171            self.images = images;
172        }
173        if !add_dir.is_empty() {
174            self.add_dir.extend(add_dir);
175        }
176    }
177}