1use anyhow::{Context, Result};
2use std::io::{self, Write};
3use std::path::PathBuf;
4use std::process::{Command, Stdio};
5use std::thread;
6use std::time::Duration;
7
8pub fn get_config_storage_path() -> Result<PathBuf> {
15 let home_dir = dirs::home_dir().context("Could not find home directory")?;
16 Ok(home_dir.join(".claude").join("cc_auto_switch_setting.json"))
17}
18
19pub fn get_claude_settings_path(custom_dir: Option<&str>) -> Result<PathBuf> {
27 if let Some(dir) = custom_dir {
28 let custom_path = PathBuf::from(dir);
29 if custom_path.is_absolute() {
30 Ok(custom_path.join("settings.json"))
31 } else {
32 let home_dir = dirs::home_dir().context("Could not find home directory")?;
34 Ok(home_dir.join(custom_path).join("settings.json"))
35 }
36 } else {
37 let home_dir = dirs::home_dir().context("Could not find home directory")?;
39 Ok(home_dir.join(".claude").join("settings.json"))
40 }
41}
42
43pub fn read_input(prompt: &str) -> Result<String> {
51 print!("{prompt}");
52 io::stdout().flush().context("Failed to flush stdout")?;
53 let mut input = String::new();
54 io::stdin()
55 .read_line(&mut input)
56 .context("Failed to read input")?;
57 Ok(input.trim().to_string())
58}
59
60pub fn read_sensitive_input(prompt: &str) -> Result<String> {
68 print!("{prompt}");
69 io::stdout().flush().context("Failed to flush stdout")?;
70 let mut input = String::new();
71 io::stdin()
72 .read_line(&mut input)
73 .context("Failed to read input")?;
74 Ok(input.trim().to_string())
75}
76
77pub fn validate_alias_name(alias_name: &str) -> Result<()> {
85 if alias_name.is_empty() {
86 anyhow::bail!("Alias name cannot be empty");
87 }
88 if alias_name == "cc" {
89 anyhow::bail!("Alias name 'cc' is reserved and cannot be used");
90 }
91 if alias_name.chars().any(|c| c.is_whitespace()) {
92 anyhow::bail!("Alias name cannot contain whitespace");
93 }
94 Ok(())
95}
96
97pub fn execute_claude_command(skip_permissions: bool) -> Result<()> {
102 let mut command = Command::new("claude");
103 if skip_permissions {
104 command.arg("--dangerously-skip-permissions");
105 }
106
107 command
108 .stdin(Stdio::inherit())
109 .stdout(Stdio::inherit())
110 .stderr(Stdio::inherit());
111
112 let mut child = command.spawn().with_context(
113 || "Failed to launch Claude CLI. Make sure 'claude' command is available in PATH",
114 )?;
115
116 let status = child
117 .wait()
118 .with_context(|| "Failed to wait for Claude CLI process")?;
119
120 if !status.success() {
121 anyhow::bail!("Claude CLI exited with error status: {}", status);
122 }
123
124 Ok(())
125}
126
127pub fn launch_claude() -> Result<()> {
129 println!("\nWaiting 0.5 seconds before launching Claude...");
130 thread::sleep(Duration::from_millis(500));
131
132 println!("Launching Claude CLI...");
133 let mut child = Command::new("claude")
134 .arg("--dangerously-skip-permissions")
135 .stdin(Stdio::inherit())
136 .stdout(Stdio::inherit())
137 .stderr(Stdio::inherit())
138 .spawn()
139 .with_context(
140 || "Failed to launch Claude CLI. Make sure 'claude' command is available in PATH",
141 )?;
142
143 let status = child.wait()?;
144
145 if !status.success() {
146 anyhow::bail!("Claude CLI exited with error status: {}", status);
147 }
148
149 Ok(())
150}