Skip to content

Commit e3c538f

Browse files
author
Marlon Costa
committed
Merge PR winfunc#427: asdf Claude Code Detection
2 parents ab717ba + 2029cda commit e3c538f

1 file changed

Lines changed: 175 additions & 11 deletions

File tree

src-tauri/src/claude_binary.rs

Lines changed: 175 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -131,15 +131,16 @@ fn source_preference(installation: &ClaudeInstallation) -> u8 {
131131
"system" => 3,
132132
"nvm-active" => 4,
133133
source if source.starts_with("nvm") => 5,
134-
"local-bin" => 6,
135-
"claude-local" => 7,
136-
"npm-global" => 8,
137-
"yarn" | "yarn-global" => 9,
138-
"bun" => 10,
139-
"node-modules" => 11,
140-
"home-bin" => 12,
141-
"PATH" => 13,
142-
_ => 14,
134+
"asdf" => 6,
135+
"local-bin" => 7,
136+
"claude-local" => 8,
137+
"npm-global" => 9,
138+
"yarn" | "yarn-global" => 10,
139+
"bun" => 11,
140+
"node-modules" => 12,
141+
"home-bin" => 13,
142+
"PATH" => 14,
143+
_ => 15,
143144
}
144145
}
145146

@@ -152,10 +153,13 @@ fn discover_system_installations() -> Vec<ClaudeInstallation> {
152153
installations.push(installation);
153154
}
154155

155-
// 2. Check NVM paths (includes current active NVM)
156+
// 2. Check asdf shims first (before NVM)
157+
installations.extend(find_asdf_installations());
158+
159+
// 3. Check NVM paths (includes current active NVM)
156160
installations.extend(find_nvm_installations());
157161

158-
// 3. Check standard paths
162+
// 4. Check standard paths
159163
installations.extend(find_standard_installations());
160164

161165
// Remove duplicates by path
@@ -251,6 +255,120 @@ fn try_which_command() -> Option<ClaudeInstallation> {
251255
}
252256
}
253257

258+
/// Find Claude installations in asdf shims directories
259+
#[cfg(unix)]
260+
fn find_asdf_installations() -> Vec<ClaudeInstallation> {
261+
let mut installations = Vec::new();
262+
let mut checked_paths = std::collections::HashSet::new();
263+
264+
// Check ASDF_DIR environment variable first
265+
if let Ok(asdf_dir) = std::env::var("ASDF_DIR") {
266+
let claude_path = PathBuf::from(&asdf_dir).join("shims").join("claude");
267+
if claude_path.exists() && claude_path.is_file() {
268+
debug!("Found Claude via ASDF_DIR: {:?}", claude_path);
269+
let path_str = claude_path.to_string_lossy().to_string();
270+
checked_paths.insert(path_str.clone());
271+
272+
let version = get_claude_version(&path_str)
273+
.ok()
274+
.flatten();
275+
installations.push(ClaudeInstallation {
276+
path: path_str,
277+
version,
278+
source: "asdf".to_string(),
279+
installation_type: InstallationType::System,
280+
});
281+
}
282+
}
283+
284+
// Then check default ~/.asdf location (skip if already found via ASDF_DIR)
285+
if let Ok(home) = std::env::var("HOME") {
286+
let asdf_shims_path = PathBuf::from(&home)
287+
.join(".asdf")
288+
.join("shims")
289+
.join("claude");
290+
291+
let path_str = asdf_shims_path.to_string_lossy().to_string();
292+
293+
// Skip if we already found this path via ASDF_DIR
294+
if !checked_paths.contains(&path_str) {
295+
debug!("Checking asdf shims directory: {:?}", asdf_shims_path);
296+
297+
if asdf_shims_path.exists() && asdf_shims_path.is_file() {
298+
debug!("Found Claude in asdf shims: {}", path_str);
299+
300+
// Get Claude version
301+
let version = get_claude_version(&path_str).ok().flatten();
302+
303+
installations.push(ClaudeInstallation {
304+
path: path_str,
305+
version,
306+
source: "asdf".to_string(),
307+
installation_type: InstallationType::System,
308+
});
309+
}
310+
}
311+
}
312+
313+
installations
314+
}
315+
316+
#[cfg(windows)]
317+
fn find_asdf_installations() -> Vec<ClaudeInstallation> {
318+
let mut installations = Vec::new();
319+
let mut checked_paths = std::collections::HashSet::new();
320+
321+
// Check ASDF_DIR environment variable first
322+
if let Ok(asdf_dir) = std::env::var("ASDF_DIR") {
323+
let claude_path = PathBuf::from(&asdf_dir).join("shims").join("claude.exe");
324+
if claude_path.exists() && claude_path.is_file() {
325+
debug!("Found Claude via ASDF_DIR: {:?}", claude_path);
326+
let path_str = claude_path.to_string_lossy().to_string();
327+
checked_paths.insert(path_str.clone());
328+
329+
let version = get_claude_version(&path_str)
330+
.ok()
331+
.flatten();
332+
installations.push(ClaudeInstallation {
333+
path: path_str,
334+
version,
335+
source: "asdf".to_string(),
336+
installation_type: InstallationType::System,
337+
});
338+
}
339+
}
340+
341+
// Then check default location (skip if already found via ASDF_DIR)
342+
if let Ok(user_profile) = std::env::var("USERPROFILE") {
343+
let asdf_shims_path = PathBuf::from(&user_profile)
344+
.join(".asdf")
345+
.join("shims")
346+
.join("claude.exe");
347+
348+
let path_str = asdf_shims_path.to_string_lossy().to_string();
349+
350+
// Skip if we already found this path via ASDF_DIR
351+
if !checked_paths.contains(&path_str) {
352+
debug!("Checking asdf shims directory: {:?}", asdf_shims_path);
353+
354+
if asdf_shims_path.exists() && asdf_shims_path.is_file() {
355+
debug!("Found Claude in asdf shims: {}", path_str);
356+
357+
let version = get_claude_version(&path_str).ok().flatten();
358+
359+
installations.push(ClaudeInstallation {
360+
path: path_str,
361+
version,
362+
source: "asdf".to_string(),
363+
installation_type: InstallationType::System,
364+
});
365+
}
366+
}
367+
}
368+
369+
installations
370+
}
371+
254372
/// Find Claude installations in NVM directories
255373
#[cfg(unix)]
256374
fn find_nvm_installations() -> Vec<ClaudeInstallation> {
@@ -638,6 +756,10 @@ pub fn create_command_with_env(program: &str) -> Command {
638756
|| key == "NVM_BIN"
639757
|| key == "HOMEBREW_PREFIX"
640758
|| key == "HOMEBREW_CELLAR"
759+
// Add asdf environment variables
760+
|| key == "ASDF_DIR"
761+
|| key == "ASDF_DATA_DIR"
762+
|| key == "ASDF_CONFIG_FILE"
641763
// Add proxy environment variables (only uppercase)
642764
|| key == "HTTP_PROXY"
643765
|| key == "HTTPS_PROXY"
@@ -689,5 +811,47 @@ pub fn create_command_with_env(program: &str) -> Command {
689811
}
690812
}
691813

814+
// Add asdf support if the program is in an asdf shims directory
815+
// Also check if the program is a symlink pointing to asdf shims
816+
let is_asdf_program = program.contains("/.asdf/shims/")
817+
|| std::fs::read_link(program)
818+
.map(|target| target.to_string_lossy().contains("/.asdf/shims/"))
819+
.unwrap_or(false);
820+
821+
if is_asdf_program {
822+
if let Ok(home) = std::env::var("HOME") {
823+
let asdf_bin_dir = format!("{}/.asdf/bin", home);
824+
let asdf_shims_dir = format!("{}/.asdf/shims", home);
825+
let current_path = std::env::var("PATH").unwrap_or_default();
826+
827+
let mut new_path = current_path.clone();
828+
829+
// Add asdf bin directory if not already in PATH
830+
if !current_path.contains(&asdf_bin_dir) {
831+
new_path = format!("{}:{}", asdf_bin_dir, new_path);
832+
debug!("Adding asdf bin directory to PATH: {}", asdf_bin_dir);
833+
}
834+
835+
// Add asdf shims directory if not already in PATH
836+
if !current_path.contains(&asdf_shims_dir) {
837+
new_path = format!("{}:{}", asdf_shims_dir, new_path);
838+
debug!("Adding asdf shims directory to PATH: {}", asdf_shims_dir);
839+
}
840+
841+
if new_path != current_path {
842+
cmd.env("PATH", new_path);
843+
}
844+
845+
// Set ASDF_DIR if not already set
846+
if std::env::var("ASDF_DIR").is_err() {
847+
let asdf_dir = format!("{}/.asdf", home);
848+
if std::path::Path::new(&asdf_dir).exists() {
849+
debug!("Setting ASDF_DIR to: {}", asdf_dir);
850+
cmd.env("ASDF_DIR", asdf_dir);
851+
}
852+
}
853+
}
854+
}
855+
692856
cmd
693857
}

0 commit comments

Comments
 (0)