mixtape_cli/repl/
approval.rs1use mixtape_core::permission::{AuthorizationResponse, Grant, Scope};
11use std::io::{stdout, BufRead, Write};
12
13#[derive(Debug, Clone)]
19pub struct PermissionRequest {
20 pub tool_name: String,
22 pub tool_use_id: String,
24 pub params_hash: String,
26 pub formatted_display: Option<String>,
28}
29
30pub trait ApprovalPrompter: Send + Sync {
34 fn prompt(&self, request: &PermissionRequest) -> AuthorizationResponse;
36
37 fn name(&self) -> &'static str;
39}
40
41pub struct SimplePrompter;
53
54impl ApprovalPrompter for SimplePrompter {
55 fn name(&self) -> &'static str {
56 "SimplePrompter"
57 }
58
59 fn prompt(&self, request: &PermissionRequest) -> AuthorizationResponse {
60 print_tool_header(request);
62
63 println!("\n\x1b[33mPermission required:\x1b[0m");
65 println!(" \x1b[1my\x1b[0m approve once");
66 println!(" \x1b[1me\x1b[0m trust this exact call (session)");
67 println!(" \x1b[1mt\x1b[0m trust entire tool (session)");
68 println!(" \x1b[1mn\x1b[0m deny");
69
70 loop {
71 print!("\nChoice: ");
72 let _ = stdout().flush();
73
74 let input = read_input();
75 let input = input.trim().to_lowercase();
76
77 match input.as_str() {
78 "y" | "yes" => {
79 print_confirmation("Approved once");
80 return AuthorizationResponse::Once;
81 }
82 "e" | "exact" => {
83 let grant = Grant::exact(&request.tool_name, &request.params_hash)
84 .with_scope(Scope::Session);
85 print_confirmation("Trusted exact call for session");
86 return AuthorizationResponse::Trust { grant };
87 }
88 "t" | "tool" | "trust" => {
89 let grant = Grant::tool(&request.tool_name).with_scope(Scope::Session);
90 print_confirmation("Trusted entire tool for session");
91 return AuthorizationResponse::Trust { grant };
92 }
93 "n" | "no" | "deny" => {
94 print_confirmation("Denied");
95 return AuthorizationResponse::Deny { reason: None };
96 }
97 "" => continue,
98 _ => {
99 println!("\x1b[31mInvalid choice. Use y/e/t/n\x1b[0m");
100 }
101 }
102 }
103 }
104}
105
106pub type DefaultPrompter = SimplePrompter;
108
109pub fn print_tool_header(request: &PermissionRequest) {
115 println!("\n🛠️ \x1b[1m{}\x1b[0m", request.tool_name);
116
117 if let Some(ref display) = request.formatted_display {
118 for line in display.lines() {
119 println!(" {}", line);
120 }
121 }
122}
123
124pub fn read_input() -> String {
126 let stdin = std::io::stdin();
127 let mut line = String::new();
128 let _ = stdin.lock().read_line(&mut line);
129 line
130}
131
132pub fn print_confirmation(message: &str) {
134 println!(" \x1b[32m✓\x1b[0m {}", message);
135}
136
137pub fn prompt_for_approval(request: &PermissionRequest) -> AuthorizationResponse {
139 SimplePrompter.prompt(request)
140}