claude-profiles 0.7.0

Launch claude with only the plugins, skills, and MCP servers a profile defines
const SINGLE_VALUE: [&str; 1] = ["--permission-mode"];
const TOOL_LISTS: [&str; 4] = [
    "--allowedTools",
    "--disallowedTools",
    "--allowed-tools",
    "--disallowed-tools",
];
const NO_VALUE: [&str; 1] = ["--dangerously-skip-permissions"];

fn is_flag(arg: &str) -> bool {
    arg.starts_with('-')
}

pub fn permission_flags(extra: &[String]) -> Vec<String> {
    let mut result = Vec::new();
    let mut i = 0;

    while i < extra.len() {
        let element = extra[i].as_str();
        i += 1;

        if let Some((flag, _)) = element.split_once('=') {
            if SINGLE_VALUE.contains(&flag) || TOOL_LISTS.contains(&flag) || NO_VALUE.contains(&flag) {
                result.push(element.to_string());
            }
        } else if NO_VALUE.contains(&element) {
            result.push(element.to_string());
        } else if SINGLE_VALUE.contains(&element) {
            if i < extra.len() {
                result.push(element.to_string());
                result.push(extra[i].clone());
                i += 1;
            }
        } else if TOOL_LISTS.contains(&element) {
            let start = i;
            while i < extra.len() && !is_flag(&extra[i]) {
                i += 1;
            }
            if i > start {
                result.push(element.to_string());
                result.extend(extra[start..i].iter().cloned());
            }
        }
    }

    result
}

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

    #[test]
    fn keeps_permission_mode_and_value() {
        let input = vec![
            "--model".to_string(),
            "opus".to_string(),
            "--permission-mode".to_string(),
            "acceptEdits".to_string(),
        ];
        let result = permission_flags(&input);
        assert_eq!(result, vec!["--permission-mode", "acceptEdits"]);
    }

    #[test]
    fn keeps_equals_form() {
        let input = vec!["--permission-mode=plan".to_string()];
        let result = permission_flags(&input);
        assert_eq!(result, vec!["--permission-mode=plan"]);
    }

    #[test]
    fn keeps_skip_permissions_flag() {
        let input = vec![
            "--dangerously-skip-permissions".to_string(),
            "-c".to_string(),
        ];
        let result = permission_flags(&input);
        assert_eq!(result, vec!["--dangerously-skip-permissions"]);
    }

    #[test]
    fn keeps_tool_lists_with_spaces() {
        let input = vec![
            "--allowedTools".to_string(),
            "Bash(git log:*) Edit".to_string(),
            "--disallowedTools".to_string(),
            "WebFetch".to_string(),
        ];
        let result = permission_flags(&input);
        assert_eq!(
            result,
            vec![
                "--allowedTools",
                "Bash(git log:*) Edit",
                "--disallowedTools",
                "WebFetch"
            ]
        );
    }

    #[test]
    fn keeps_kebab_case_tool_flags() {
        let input: Vec<String> = ["--allowed-tools", "Edit", "--disallowed-tools=WebFetch", "--model", "opus"]
            .iter()
            .map(|s| s.to_string())
            .collect();
        assert_eq!(
            permission_flags(&input),
            vec!["--allowed-tools", "Edit", "--disallowed-tools=WebFetch"]
        );
    }

    #[test]
    fn tool_flags_keep_every_following_value() {
        let input: Vec<String> = ["--allowedTools", "Edit", "Bash(git:*)", "--model", "opus", "--disallowedTools", "A", "B"]
            .iter()
            .map(|s| s.to_string())
            .collect();
        assert_eq!(
            permission_flags(&input),
            vec!["--allowedTools", "Edit", "Bash(git:*)", "--disallowedTools", "A", "B"]
        );
    }

    #[test]
    fn permission_mode_keeps_one_value() {
        let input: Vec<String> = ["--permission-mode", "plan", "do the thing"]
            .iter()
            .map(|s| s.to_string())
            .collect();
        assert_eq!(permission_flags(&input), vec!["--permission-mode", "plan"]);
    }

    #[test]
    fn missing_value_at_end_is_dropped() {
        let input = vec!["--permission-mode".to_string()];
        let result = permission_flags(&input);
        assert_eq!(result, Vec::<String>::new());
    }
}