cc-switch 0.1.8

A CLI tool for managing multiple Claude API configurations and automatically switching between them
Documentation
use anyhow::{Context, Result};
use std::io::{self, Write};
use std::path::PathBuf;
use std::process::{Command, Stdio};

/// Get the path to the configuration storage file
///
/// Returns `~/.claude/cc_auto_switch_setting.json`
///
/// # Errors
/// Returns error if home directory cannot be found
pub fn get_config_storage_path() -> Result<PathBuf> {
    let home_dir = dirs::home_dir().context("Could not find home directory")?;
    Ok(home_dir.join(".claude").join("cc_auto_switch_setting.json"))
}

/// Get the path to the Claude settings file
///
/// Returns the path to settings.json, using custom directory if configured
/// Defaults to `~/.claude/settings.json`
///
/// # Errors
/// Returns error if home directory cannot be found or path is invalid
pub fn get_claude_settings_path(custom_dir: Option<&str>) -> Result<PathBuf> {
    if let Some(dir) = custom_dir {
        let custom_path = PathBuf::from(dir);
        if custom_path.is_absolute() {
            Ok(custom_path.join("settings.json"))
        } else {
            // If relative path, resolve from home directory
            let home_dir = dirs::home_dir().context("Could not find home directory")?;
            Ok(home_dir.join(custom_path).join("settings.json"))
        }
    } else {
        // Default path
        let home_dir = dirs::home_dir().context("Could not find home directory")?;
        Ok(home_dir.join(".claude").join("settings.json"))
    }
}

/// Read input from stdin with a prompt
///
/// # Arguments
/// * `prompt` - The prompt to display to the user
///
/// # Returns
/// The user's input as a String
pub fn read_input(prompt: &str) -> Result<String> {
    print!("{prompt}");
    io::stdout().flush().context("Failed to flush stdout")?;
    let mut input = String::new();
    io::stdin()
        .read_line(&mut input)
        .context("Failed to read input")?;
    Ok(input.trim().to_string())
}

/// Read sensitive input (token) with a prompt (without echoing)
///
/// # Arguments
/// * `prompt` - The prompt to display to the user
///
/// # Returns
/// The user's input as a String
pub fn read_sensitive_input(prompt: &str) -> Result<String> {
    print!("{prompt}");
    io::stdout().flush().context("Failed to flush stdout")?;
    let mut input = String::new();
    io::stdin()
        .read_line(&mut input)
        .context("Failed to read input")?;
    Ok(input.trim().to_string())
}

/// Execute claude command with or without --dangerously-skip-permissions
///
/// # Arguments
/// * `skip_permissions` - Whether to add --dangerously-skip-permissions flag
pub fn execute_claude_command(skip_permissions: bool) -> Result<()> {
    let mut command = Command::new("claude");
    if skip_permissions {
        command.arg("--dangerously-skip-permissions");
    }

    command
        .stdin(Stdio::inherit())
        .stdout(Stdio::inherit())
        .stderr(Stdio::inherit());

    let mut child = command.spawn().with_context(
        || "Failed to launch Claude CLI. Make sure 'claude' command is available in PATH",
    )?;

    let status = child
        .wait()
        .with_context(|| "Failed to wait for Claude CLI process")?;

    if !status.success() {
        anyhow::bail!("Claude CLI exited with error status: {}", status);
    }

    Ok(())
}

/// Launch Claude CLI with proper delay
pub fn launch_claude() -> Result<()> {
    println!("\nLaunching Claude CLI...");
    let mut child = Command::new("claude")
        .arg("--dangerously-skip-permissions")
        .stdin(Stdio::inherit())
        .stdout(Stdio::inherit())
        .stderr(Stdio::inherit())
        .spawn()
        .with_context(
            || "Failed to launch Claude CLI. Make sure 'claude' command is available in PATH",
        )?;

    let status = child.wait()?;

    if !status.success() {
        anyhow::bail!("Claude CLI exited with error status: {}", status);
    }

    Ok(())
}