Skip to main content

codewhale_execpolicy/
approval_mode.rs

1//! The user-facing approval posture (`Ask` / `Auto-Review` / `Full Access` /
2//! `Never`). Lives beside `AskForApproval` so policy code and the TUI share
3//! one definition; the TUI adds only presentation on top.
4
5/// Determines when tool executions require user approval
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
7pub enum ApprovalMode {
8    /// Automatically review risky tool calls before deciding whether to ask.
9    Auto,
10    /// Bypass approvals entirely (YOLO mode / --yolo flag).
11    Bypass,
12    /// Suggest approval for non-safe tools (non-YOLO modes)
13    #[default]
14    Suggest,
15    /// Never execute tools requiring approval
16    Never,
17}
18
19impl ApprovalMode {
20    /// Shift+Tab permission cycle order (#0.8.68 M2).
21    pub const PERMISSION_CYCLE: [Self; 3] = [Self::Suggest, Self::Auto, Self::Bypass];
22
23    pub fn label(self) -> &'static str {
24        match self {
25            ApprovalMode::Auto => "AUTO",
26            ApprovalMode::Bypass => "BYPASS",
27            ApprovalMode::Suggest => "SUGGEST",
28            ApprovalMode::Never => "NEVER",
29        }
30    }
31
32    pub fn from_config_value(value: &str) -> Option<Self> {
33        match value.trim().to_ascii_lowercase().as_str() {
34            "auto" | "auto-review" | "auto_review" => Some(ApprovalMode::Auto),
35            "bypass" | "yolo" | "dontask" | "dont_ask" | "bypass-permissions"
36            | "bypasspermissions" | "full-access" | "full_access" | "full" => {
37                Some(ApprovalMode::Bypass)
38            }
39            "suggest" | "suggested" | "on-request" | "untrusted" | "ask" => {
40                Some(ApprovalMode::Suggest)
41            }
42            "never" | "deny" | "denied" => Some(ApprovalMode::Never),
43            _ => None,
44        }
45    }
46
47    #[must_use]
48    pub fn cycle_permission_next(self) -> Self {
49        let Some(index) = Self::PERMISSION_CYCLE.iter().position(|mode| *mode == self) else {
50            return Self::Suggest;
51        };
52        Self::PERMISSION_CYCLE[(index + 1) % Self::PERMISSION_CYCLE.len()]
53    }
54
55    #[must_use]
56    pub fn permission_chip_label(self) -> &'static str {
57        match self {
58            Self::Suggest => "Ask",
59            Self::Auto => "Auto-Review",
60            Self::Bypass => "Full Access",
61            Self::Never => "Never",
62        }
63    }
64}