use crate::config::{EnvironmentConfig, ProviderPreset};
use anyhow::{anyhow, Result};
use portable_pty::{native_pty_system, Child, CommandBuilder, MasterPty, PtySize};
use std::path::Path;
pub struct ClaudeProcess {
pub master: Box<dyn MasterPty + Send>,
pub child: Box<dyn Child + Send + Sync>,
}
pub fn spawn_claude_code(
claude_config_dir: &Path,
env_config: &EnvironmentConfig,
size: PtySize,
working_dir: &Path,
) -> Result<ClaudeProcess> {
let claude_path = which::which("claude")
.map_err(|_| anyhow!("Claude Code not found in PATH. Install with: npm install -g @anthropic-ai/claude-code"))?;
let pty_system = native_pty_system();
let pair = pty_system.openpty(size)?;
let mut cmd = CommandBuilder::new(&claude_path);
cmd.cwd(working_dir);
cmd.env("CLAUDE_CONFIG_DIR", claude_config_dir.to_string_lossy().to_string());
if let Ok(path) = std::env::var("PATH") {
cmd.env("PATH", path);
}
if let Ok(term) = std::env::var("TERM") {
cmd.env("TERM", term);
} else {
cmd.env("TERM", "xterm-256color");
}
if let Ok(home) = std::env::var("HOME") {
cmd.env("HOME", home);
}
if let Ok(user) = std::env::var("USER") {
cmd.env("USER", user);
}
#[cfg(windows)]
{
if let Ok(userprofile) = std::env::var("USERPROFILE") {
cmd.env("USERPROFILE", userprofile);
}
if let Ok(appdata) = std::env::var("APPDATA") {
cmd.env("APPDATA", appdata);
}
if let Ok(localappdata) = std::env::var("LOCALAPPDATA") {
cmd.env("LOCALAPPDATA", localappdata);
}
if let Ok(systemroot) = std::env::var("SystemRoot") {
cmd.env("SystemRoot", systemroot);
}
}
if env_config.provider.preset != ProviderPreset::Anthropic {
if let Some(base_url) = env_config.provider.effective_base_url() {
cmd.env("ANTHROPIC_BASE_URL", base_url);
}
if let Some(ref api_key) = env_config.provider.api_key {
cmd.env("ANTHROPIC_AUTH_TOKEN", api_key);
}
cmd.env("API_TIMEOUT_MS", "3000000");
cmd.env("CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC", "1");
if let Some(models) = env_config.provider.effective_models() {
if !models.opus.is_empty() {
cmd.env("ANTHROPIC_MODEL", &models.opus);
cmd.env("ANTHROPIC_DEFAULT_OPUS_MODEL", &models.opus);
}
if !models.sonnet.is_empty() {
cmd.env("ANTHROPIC_DEFAULT_SONNET_MODEL", &models.sonnet);
}
if !models.haiku.is_empty() {
cmd.env("ANTHROPIC_SMALL_FAST_MODEL", &models.haiku);
cmd.env("ANTHROPIC_DEFAULT_HAIKU_MODEL", &models.haiku);
}
}
}
let child = pair.slave.spawn_command(cmd)?;
Ok(ClaudeProcess {
master: pair.master,
child,
})
}