Skip to main content

codei_tools/
approval.rs

1use std::io::{self, Write};
2
3use async_trait::async_trait;
4use serde_json::Value;
5
6#[derive(Debug, Clone)]
7pub struct ApprovalRequest {
8    pub tool_name: String,
9    pub arguments: Value,
10}
11
12#[derive(Debug, Clone)]
13pub struct ApprovalResponse {
14    pub approved: bool,
15}
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub enum ApprovalPolicy {
19    Always,
20    Never,
21    OnDestructive,
22}
23
24#[async_trait]
25pub trait ApprovalHandler: Send + Sync {
26    async fn approve(&self, request: ApprovalRequest) -> ApprovalResponse;
27}
28
29pub struct AutoApprove;
30
31#[async_trait]
32impl ApprovalHandler for AutoApprove {
33    async fn approve(&self, _request: ApprovalRequest) -> ApprovalResponse {
34        ApprovalResponse { approved: true }
35    }
36}
37
38pub struct PromptApprove;
39
40#[async_trait]
41impl ApprovalHandler for PromptApprove {
42    async fn approve(&self, request: ApprovalRequest) -> ApprovalResponse {
43        eprintln!(
44            "\n[approval] tool={} args={}",
45            request.tool_name, request.arguments
46        );
47        eprint!("Approve? [y/N]: ");
48        let _ = io::stderr().flush();
49        let mut line = String::new();
50        if io::stdin().read_line(&mut line).is_err() {
51            return ApprovalResponse { approved: false };
52        }
53        let approved = line.trim().eq_ignore_ascii_case("y");
54        ApprovalResponse { approved }
55    }
56}
57
58/// Auto-approve safe tools; prompt for destructive ones.
59pub struct OnDestructiveApprove {
60    inner: PromptApprove,
61}
62
63#[async_trait]
64impl ApprovalHandler for OnDestructiveApprove {
65    async fn approve(&self, request: ApprovalRequest) -> ApprovalResponse {
66        match request.tool_name.as_str() {
67            "write" | "edit" | "shell" => self.inner.approve(request).await,
68            _ => ApprovalResponse { approved: true },
69        }
70    }
71}
72
73pub fn handler_for_policy(policy: ApprovalPolicy) -> Box<dyn ApprovalHandler> {
74    match policy {
75        ApprovalPolicy::Never => Box::new(AutoApprove),
76        ApprovalPolicy::Always => Box::new(PromptApprove),
77        ApprovalPolicy::OnDestructive => Box::new(OnDestructiveApprove {
78            inner: PromptApprove,
79        }),
80    }
81}