An AI agent built to do Ralph loops - plan mode for planning and ralph mode for implementing.
1use std::io::{self, Write};
2
3pub trait PermissionHandler: Send + Sync {
4 fn request_permission(&self, request: &PermissionRequest) -> PermissionResult;
5}
6
7pub struct PermissionRequest {
8 pub resource_type: ResourceType,
9 pub action: String,
10 pub reason: String,
11}
12
13#[derive(Debug, Clone, Copy)]
14pub enum ResourceType {
15 ShellCommand,
16 FilePath,
17}
18
19#[derive(Debug)]
20pub enum PermissionResult {
21 Allow,
22 Deny,
23 AllowAlways(String),
24 Quit,
25}
26
27pub struct CliPermissionHandler;
28
29impl PermissionHandler for CliPermissionHandler {
30 fn request_permission(&self, request: &PermissionRequest) -> PermissionResult {
31 println!("\n⚠️ Permission Required");
32 println!("Type: {:?}", request.resource_type);
33 println!("Action: {}", request.action);
34 println!("Reason: {}", request.reason);
35 println!();
36 print!("Allow? [y]es / [n]o / [a]lways allow / [q]uit: ");
37 io::stdout().flush().unwrap();
38
39 let mut input = String::new();
40 io::stdin().read_line(&mut input).unwrap();
41
42 match input.trim().to_lowercase().as_str() {
43 "y" | "yes" => PermissionResult::Allow,
44 "a" | "always" => {
45 // Extract the resource to remember
46 let resource = match request.resource_type {
47 ResourceType::ShellCommand => {
48 // Extract base command
49 request.action.split_whitespace().next().unwrap_or("")
50 }
51 ResourceType::FilePath => &request.action,
52 };
53 PermissionResult::AllowAlways(resource.to_string())
54 }
55 "q" | "quit" => PermissionResult::Quit,
56 _ => PermissionResult::Deny,
57 }
58 }
59}
60
61/// Auto-approve handler for testing
62pub struct AutoApproveHandler;
63
64impl PermissionHandler for AutoApproveHandler {
65 fn request_permission(&self, _request: &PermissionRequest) -> PermissionResult {
66 PermissionResult::Allow
67 }
68}