|
6 | 6 | use std::borrow::Cow; |
7 | 7 | use std::collections::HashMap; |
8 | 8 | use std::path::Path; |
9 | | -use std::process::Command; |
10 | 9 | use std::sync::{Arc, LazyLock, RwLock}; |
11 | 10 |
|
12 | 11 | use ai::skills::SkillProvider; |
@@ -600,29 +599,39 @@ impl CLIAgent { |
600 | 599 |
|
601 | 600 | /// 同步检测系统是否安装了此 agent。后台线程专用。 |
602 | 601 | fn is_installed_blocking(&self) -> bool { |
603 | | - let check_cmd = |cmd: &str| -> bool { |
604 | | - #[cfg(unix)] |
605 | | - let (shell, arg) = ("which", cmd); |
606 | | - #[cfg(windows)] |
607 | | - let (shell, arg) = ("where", cmd); |
608 | | - Command::new(shell) |
609 | | - .arg(arg) |
610 | | - .stdout(std::process::Stdio::null()) |
611 | | - .stderr(std::process::Stdio::null()) |
612 | | - .output() |
613 | | - .is_ok_and(|o| o.status.success()) |
614 | | - }; |
615 | 602 | match self { |
616 | 603 | CLIAgent::Unknown => false, |
617 | 604 | // `agent` 太泛化,Cursor CLI 用 cursor-agent 检测 |
618 | | - CLIAgent::CursorCli => check_cmd("cursor-agent"), |
| 605 | + CLIAgent::CursorCli => is_on_path("cursor-agent"), |
619 | 606 | // DeepSeek 同时检查主命令和别名 |
620 | | - CLIAgent::DeepSeek => check_cmd("deepseek") || check_cmd("deepseek-tui"), |
621 | | - other => check_cmd(other.command_prefix()), |
| 607 | + CLIAgent::DeepSeek => is_on_path("deepseek") || is_on_path("deepseek-tui"), |
| 608 | + other => is_on_path(other.command_prefix()), |
622 | 609 | } |
623 | 610 | } |
624 | 611 | } |
625 | 612 |
|
| 613 | +/// 内联 PATH 搜索,零进程、零闪窗。 |
| 614 | +#[cfg(unix)] |
| 615 | +fn is_on_path(cmd: &str) -> bool { |
| 616 | + let Ok(path_var) = std::env::var("PATH") else { |
| 617 | + return false; |
| 618 | + }; |
| 619 | + std::env::split_paths(&path_var).any(|dir| dir.join(cmd).is_file()) |
| 620 | +} |
| 621 | + |
| 622 | +#[cfg(windows)] |
| 623 | +fn is_on_path(cmd: &str) -> bool { |
| 624 | + let pathext = std::env::var("PATHEXT").unwrap_or_else(|_| ".EXE;.CMD;.BAT".into()); |
| 625 | + let Ok(path_var) = std::env::var("PATH") else { |
| 626 | + return false; |
| 627 | + }; |
| 628 | + let exts: Vec<&str> = pathext.split(';').collect(); |
| 629 | + std::env::split_paths(&path_var).any(|dir| { |
| 630 | + exts.iter() |
| 631 | + .any(|ext| dir.join(format!("{}{}", cmd, ext)).is_file()) |
| 632 | + }) |
| 633 | +} |
| 634 | + |
626 | 635 | #[cfg(test)] |
627 | 636 | #[path = "cli_agent_tests.rs"] |
628 | 637 | mod tests; |
0 commit comments