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