darwincode 1.9.89

The open source terminal AI coding agent
use crate::config::PermissionLevel;
use crate::app::core::{App, Screen, SubmitAction, PendingTask};
use crate::app::chat::MessageLine;
use crate::app::permission::PermissionPickerState;

pub fn run(app: &mut App, level: Option<PermissionLevel>) -> Option<SubmitAction> {
    if let Some(level) = level {
        app.chat.config.permission_level = level;
        let level_label = app.chat.config.permission_level.label();
        if app.chat.messages.last().is_some_and(|m| m.pending) {
            app.chat.messages.pop();
        }
        app.chat.messages.push(MessageLine::info(format!(
            "Permission level set to **{level_label}**"
        )));
        let _ = app.chat.config.save();

        if let Some(PendingTask::ConfirmFunction { name, args }) = app.proc.pending.clone() {
            let auto_allowed = level == PermissionLevel::Chaos
                || (level == PermissionLevel::Safe
                    && (name == "read"
                        || name == "grep"
                        || name == "glob"
                        || name == "websearch"
                        || name == "ask"
                        || name == "todo"
                        || name == "ps"
                        || name == "logs"));
            if auto_allowed {
                app.backup_before_execution(&name, &args);
                app.proc.pending = Some(PendingTask::ExecutingFunction { name: name.clone() });
                app.status = format!("Auto-executing {name}");
                app.start_function_execution(&name, &args);
                return Some(SubmitAction::ExecuteFunction {
                    name,
                    args,
                    config: app.chat.config.clone(),
                });
            } else if level == PermissionLevel::Safe
                && (name == "sh"
                    || name == "write"
                    || name == "edit"
                    || name == "patch"
                    || name == "kill")
                && let Some(crate::app::FunctionAction::ResumeGeneration(request)) = app
                    .complete_function_execution(
                        name,
                        serde_json::json!({"error": "Permission denied: restricted mode"}),
                    )
            {
                return Some(SubmitAction::Generate(request));
            }
        }
        None
    } else {
        app.ui.screen = Screen::Permissions;
        let current = app.chat.config.permission_level;
        app.ui.permissions.selected = PermissionPickerState::options()
            .iter()
            .position(|(_, _, l)| *l == current)
            .unwrap_or(0);
        app.status = "Select permission level. Enter to apply, Esc to cancel.".to_owned();
        None
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::config::StoredConfig;

    #[test]
    fn test_permissions_run_some() {
        let _lock = crate::config::TEST_LOCK.lock().unwrap();
        let temp_dir = std::env::temp_dir().join(format!("darwin_test_{}", std::time::Instant::now().elapsed().as_nanos()));
        std::fs::create_dir_all(&temp_dir).unwrap();
        *crate::config::TEST_CONFIG_DIR.lock().unwrap() = Some(temp_dir.clone());

        let mut app = App::new(Some(StoredConfig::default()));
        let result = run(&mut app, Some(PermissionLevel::Safe));
        assert!(result.is_none());
        assert_eq!(app.chat.config.permission_level, PermissionLevel::Safe);
        assert!(!app.chat.messages.is_empty());

        *crate::config::TEST_CONFIG_DIR.lock().unwrap() = None;
        let _ = std::fs::remove_dir_all(&temp_dir);
    }

    #[test]
    fn test_permissions_run_none() {
        let mut app = App::new(Some(StoredConfig::default()));
        app.ui.screen = Screen::Chat;
        let result = run(&mut app, None);
        assert!(result.is_none());
        assert_eq!(app.ui.screen, Screen::Permissions);
    }
}