Skip to main content

claude_wrapper/command/
auto_mode.rs

1//! `claude auto-mode` subcommands: inspect the auto-mode classifier
2//! configuration.
3//!
4//! The CLI exposes three children under `auto-mode`:
5//!
6//! - [`AutoModeConfigCommand`] -- `auto-mode config`, the effective
7//!   merged config as JSON.
8//! - [`AutoModeDefaultsCommand`] -- `auto-mode defaults`, the default
9//!   rules as JSON.
10//! - [`AutoModeCritiqueCommand`] -- `auto-mode critique`, AI feedback
11//!   on your custom auto-mode rules.
12
13#[cfg(feature = "async")]
14use crate::Claude;
15use crate::command::ClaudeCommand;
16#[cfg(feature = "async")]
17use crate::error::Result;
18#[cfg(feature = "async")]
19use crate::exec;
20use crate::exec::CommandOutput;
21
22/// Print the effective auto-mode config as JSON.
23///
24/// Emits your settings where set and defaults otherwise, merged.
25///
26/// # Example
27///
28/// ```no_run
29/// # #[cfg(feature = "async")] {
30/// use claude_wrapper::{AutoModeConfigCommand, Claude, ClaudeCommand};
31///
32/// # async fn example() -> claude_wrapper::Result<()> {
33/// let claude = Claude::builder().build()?;
34/// let output = AutoModeConfigCommand::new().execute(&claude).await?;
35/// println!("{}", output.stdout);
36/// # Ok(()) }
37/// # }
38/// ```
39#[derive(Debug, Clone, Default)]
40pub struct AutoModeConfigCommand;
41
42impl AutoModeConfigCommand {
43    #[must_use]
44    pub fn new() -> Self {
45        Self
46    }
47}
48
49impl ClaudeCommand for AutoModeConfigCommand {
50    type Output = CommandOutput;
51
52    fn args(&self) -> Vec<String> {
53        vec!["auto-mode".to_string(), "config".to_string()]
54    }
55
56    #[cfg(feature = "async")]
57    async fn execute(&self, claude: &Claude) -> Result<CommandOutput> {
58        exec::run_claude(claude, self.args()).await
59    }
60}
61
62/// Print the default auto-mode environment, allow, soft_deny, and
63/// hard_deny rules as JSON. Useful as a reference when writing
64/// custom rules. The soft/hard deny split distinguishes "warn but
65/// allow when explicitly overridden" from "always block."
66#[derive(Debug, Clone, Default)]
67pub struct AutoModeDefaultsCommand;
68
69impl AutoModeDefaultsCommand {
70    #[must_use]
71    pub fn new() -> Self {
72        Self
73    }
74}
75
76impl ClaudeCommand for AutoModeDefaultsCommand {
77    type Output = CommandOutput;
78
79    fn args(&self) -> Vec<String> {
80        vec!["auto-mode".to_string(), "defaults".to_string()]
81    }
82
83    #[cfg(feature = "async")]
84    async fn execute(&self, claude: &Claude) -> Result<CommandOutput> {
85        exec::run_claude(claude, self.args()).await
86    }
87}
88
89/// Get AI feedback on your custom auto-mode rules.
90///
91/// Takes an optional model override; without one the CLI picks its
92/// default. Output is free-form text.
93///
94/// # Example
95///
96/// ```no_run
97/// # #[cfg(feature = "async")] {
98/// use claude_wrapper::{AutoModeCritiqueCommand, Claude, ClaudeCommand};
99///
100/// # async fn example() -> claude_wrapper::Result<()> {
101/// let claude = Claude::builder().build()?;
102/// let output = AutoModeCritiqueCommand::new()
103///     .model("opus")
104///     .execute(&claude)
105///     .await?;
106/// println!("{}", output.stdout);
107/// # Ok(()) }
108/// # }
109/// ```
110#[derive(Debug, Clone, Default)]
111pub struct AutoModeCritiqueCommand {
112    model: Option<String>,
113}
114
115impl AutoModeCritiqueCommand {
116    #[must_use]
117    pub fn new() -> Self {
118        Self::default()
119    }
120
121    /// Override the model used for the critique.
122    #[must_use]
123    pub fn model(mut self, model: impl Into<String>) -> Self {
124        self.model = Some(model.into());
125        self
126    }
127}
128
129impl ClaudeCommand for AutoModeCritiqueCommand {
130    type Output = CommandOutput;
131
132    fn args(&self) -> Vec<String> {
133        let mut args = vec!["auto-mode".to_string(), "critique".to_string()];
134        if let Some(ref model) = self.model {
135            args.push("--model".to_string());
136            args.push(model.clone());
137        }
138        args
139    }
140
141    #[cfg(feature = "async")]
142    async fn execute(&self, claude: &Claude) -> Result<CommandOutput> {
143        exec::run_claude(claude, self.args()).await
144    }
145}
146
147#[cfg(test)]
148mod tests {
149    use super::*;
150
151    #[test]
152    fn config_command_args() {
153        let cmd = AutoModeConfigCommand::new();
154        assert_eq!(
155            ClaudeCommand::args(&cmd),
156            vec!["auto-mode".to_string(), "config".to_string()]
157        );
158    }
159
160    #[test]
161    fn defaults_command_args() {
162        let cmd = AutoModeDefaultsCommand::new();
163        assert_eq!(
164            ClaudeCommand::args(&cmd),
165            vec!["auto-mode".to_string(), "defaults".to_string()]
166        );
167    }
168
169    #[test]
170    fn critique_command_defaults_omit_model() {
171        let cmd = AutoModeCritiqueCommand::new();
172        let args = ClaudeCommand::args(&cmd);
173        assert_eq!(args, vec!["auto-mode".to_string(), "critique".to_string()]);
174    }
175
176    #[test]
177    fn critique_command_with_model() {
178        let cmd = AutoModeCritiqueCommand::new().model("opus");
179        let args = ClaudeCommand::args(&cmd);
180        assert_eq!(
181            args,
182            vec![
183                "auto-mode".to_string(),
184                "critique".to_string(),
185                "--model".to_string(),
186                "opus".to_string(),
187            ]
188        );
189    }
190}