Skip to content

fix(cli-agent): use interactive login-shell PATH for install detection#254

Open
zerx-lab wants to merge 2 commits into
mainfrom
claude/sleepy-meitner-plpjnv
Open

fix(cli-agent): use interactive login-shell PATH for install detection#254
zerx-lab wants to merge 2 commits into
mainfrom
claude/sleepy-meitner-plpjnv

Conversation

@zerx-lab

Copy link
Copy Markdown
Owner

关联 Issue

Fixes #253

问题点(确定性描述)

根因app/src/terminal/cli_agent.rs:657is_on_path 函数调用 std::env::var("PATH") 读取进程环境 PATH

// 修复前(cli_agent.rs:656-661)
#[cfg(unix)]
fn is_on_path(cmd: &str) -> bool {
    let Ok(path_var) = std::env::var("PATH") else {
        return false;
    };
    std::env::split_paths(&path_var).any(|dir| dir.join(cmd).is_file())
}

macOS 通过 Dock / Finder 启动 GUI 应用时,进程仅继承最小化系统 PATH(/usr/bin:/bin:/usr/sbin:/sbin),不含用户级工具安装目录(Homebrew /opt/homebrew/bin、npm global ~/.npm-global/bin、cargo ~/.cargo/bin 等),导致 CLIAgentInstallModel 的后台扫描对所有这些目录中的 CLI 工具返回 false

调用链

  1. CLIAgentInstallModel::new (cli_agent.rs:592) → ctx.spawn(async)
  2. cli_agent_is_on_path(a) (cli_agent.rs:596 before fix)
  3. is_on_path(other.command_prefix()) (cli_agent.rs:650)
  4. std::env::var("PATH") → 返回最小化 PATH → is_file() 检测失败

复现证据

agent_input_footer/mod.rs:1021-1025 中已有注释明确记录了同一问题并为 plugin 自动安装路径提供了修复,但 CLIAgentInstallModel 扫描未同步:

// Await the interactive PATH so nvm-installed tools like `claude`
// are on PATH, matching how LSP operations capture the PATH.
let path_future = LocalShellState::handle(ctx).update(ctx, |shell_state, ctx| {
    shell_state.get_interactive_path_env_var(ctx)
});

修复方案

CLIAgentInstallModel::new 中,于 ctx.spawn 之前通过 LocalShellState::get_interactive_path_env_var 获取交互式 login-shell PATH 的 future,在异步扫描任务内 await 该 future,将真实 PATH 传递给检测函数。local_tty 特性未启用时回退到 None(最终仍读 std::env::var("PATH"))。

此模式与以下两处已有实现完全一致:

  • agent_input_footer/mod.rs:1021-1025
  • diff_state.rs:2891-2899

规模声明

  • 修改文件数:1app/src/terminal/cli_agent.rs
  • 修改行数:+32 / -10 = 42 行
  • 影响面:cli_agent_is_on_path 仅 1 处调用;is_on_path 仅 3 处调用,均在同一文件内,无跨文件影响
  • 无公共 API 变更、无依赖新增、无 schema/auth/concurrency 变更

修改文件清单

文件 改动说明
app/src/terminal/cli_agent.rs 新增 LocalShellState import;CLIAgentInstallModel::new 获取交互式 PATH future;cli_agent_is_on_path / is_on_path(unix & windows)增加 path_env: Option<&str> 参数

验证

cargo check -p warp --no-default-features --features local_tty
# 唯一错误:环境中缺 protoc(预先存在的环境问题,与本改动无关)
# 无 error[E*] 类型检查错误

现有测试 cli_agent_tests.rs 覆盖 CLIAgent::detect,不覆盖 is_on_path(私有函数),无需修改。

影响面

  • CLIAgentInstallModel::is_cli_agent_installed 的返回值会在 macOS GUI 启动场景下从 false 变为正确值
  • settings_view/ai_page.rs:6351-6353 中依赖此值渲染的"已安装 agent 列表"将正常显示
  • 新建标签页中同样依赖此值的 agent 入口将正常显示
  • 无其他调用方受影响(is_cli_agent_installed 仅被 UI 渲染代码读取)

Generated by Claude Code

Fixes #253)

Root cause: cli_agent.rs:657 — `is_on_path` read `std::env::var("PATH")`,
which on macOS GUI launch only contains the minimal system PATH
(/usr/bin:/bin:/usr/sbin:/sbin), missing Homebrew, nvm, cargo, etc.

Fix: in CLIAgentInstallModel::new, obtain a BoxFuture from
LocalShellState::get_interactive_path_env_var (interactive -i -l shell PATH)
before spawning the scan, then await it and pass the result to
cli_agent_is_on_path / is_on_path. Falls back to std::env::var("PATH")
when the future yields None (non-local_tty builds).

Identical pattern already used by agent_input_footer.rs and
diff_state.rs for the same macOS PATH problem.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014oDbXMhdYGC1RErbFLwcuZ

@zerx-lab zerx-lab left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

整体判断:倾向 Approve,只有一处值得调整的 Windows 边界情况,不阻塞合并。


Generated by Claude Code

Comment thread app/src/terminal/cli_agent.rs Outdated

Copy link
Copy Markdown
Owner Author

Review 总结

整体倾向:Comment(可直接合并,但有一处建议改进)


阻塞问题

无。


建议

Windows is_on_pathunwrap_or_default() 引入隐式边界情况(见 inline comment)

path_envNone 且进程环境中 PATH 也不存在时(极罕见,但 Windows 并非不可能),原代码直接 return false,现在退化为以空字符串调用 split_paths,最终会对当前工作目录执行 is_file() 检测,而非直接失败。与同文件的 Unix 实现(path_envNonePATH 读取失败时仍返回 false)也不对称。建议改用显式 match 保持一致语义(详见 inline 注释)。


看起来不错的地方

  • 根因诊断清晰std::env::var("PATH") 在 macOS Dock 启动时只拿到 /usr/bin:/bin:/usr/sbin:/sbin 是经典已知问题,PR body 的调用链分析到位。
  • 一致性好:完全复用 agent_input_footer/mod.rsdiff_state.rs 中已验证的 get_interactive_path_env_var 模式,无自造轮子。
  • #[cfg] 条件编译正确local_tty 和非 local_tty 两个分支类型均为 BoxFuture<'static, Option<String>>,被 async move 捕获后按顺序 .await 没有问题。
  • 影响面克制:改动只有 1 个文件、42 行,无公共 API 变更,无跨文件副作用。

— 由 Claude Routine 自动生成;如需复评请 @ 维护者人工触发。


Generated by Claude Code

unwrap_or_default() on a missing PATH degrades to an empty string,
causing split_paths("") to yield one empty component and is_file()
to probe the CWD — not the intended early-return false.
Align with the Unix implementation using an explicit match/return false.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014oDbXMhdYGC1RErbFLwcuZ
@zerx-lab

Copy link
Copy Markdown
Owner Author

本 PR 修复的 #253 已被先行合入 main 的 #252(commit 35d745f,修复 macOS GUI 启动时 CLI 智能体检测遗漏)以不同方案解决:#252 通过 cli_agent_search_dirs 直接把 /opt/homebrew/bin、/.cargo/bin、/.bun/bin、~/.local/bin、nvm node bin 等常见目录并入搜索集(纯同步、无子进程兜底)。

本 PR 改为 await 交互式 login-shell PATH 后再扫描,二者从同一 merge-base 改写了同一段检测代码,已产生真实内容冲突(非 import 块),无法机械合并;且按本 PR 侧解决会删除 #252cli_agent_search_dirs,导致 main 上引用该函数的 cli_agent_tests.rs 编译失败。

因此暂缓合并。建议二选一:

  1. 按重复关闭本 PR(CLI 智能体设置界面 Mac 显示未安装,新建标签页也没有显示 #253 已由 修复 macOS GUI 启动时 CLI 智能体检测遗漏 #252 修复);
  2. 若倾向交互式 PATH 方案(与 agent_input_footer / diff_state 对齐),请基于当前 main rebase,把交互式 PATH 注入 修复 macOS GUI 启动时 CLI 智能体检测遗漏 #252cli_agent_search_dirs,而非删除其目录增广兜底(shell 捕获失败/返回 None 时仍能命中常见目录,本 PR 此时会退回最小进程 PATH 丢失兜底)。

Copy link
Copy Markdown
Owner Author

已确认冲突分析属实。已阅读 #252 (35d745f0) 的 cli_agent_search_dirs 方案:进程 PATH + 硬编码兜底目录 + 去重,纯同步无子进程,并已在 cli_agent_tests.rs 增加测试。

两个方案的核心差异:

#252 已合入 本 PR #254
PATH 来源 进程 PATH + 硬编码常见目录 交互式 login-shell PATH (await)
shell 捕获失败时 仍有硬编码兜底 退回进程 PATH,无兜底
实现复杂度 纯同步 异步 await
遗漏风险 依赖枚举目录是否完整 依赖 shell 能正确运行

等待维护者决策:是作为 duplicate 关闭(#253 已由 #252 修复),还是 rebase 并将交互式 PATH 注入 cli_agent_search_dirs 作为优先来源(保留硬编码兜底)。维护者确认后立即执行。


Generated by Claude Code

Copy link
Copy Markdown
Owner Author

新 commit 追踪评审

第二个 commit(ecd1440)完整实现了上次 inline comment 里建议的 Windows 修复:

let path_var = match path_env {
    Some(p) => p.to_string(),
    None => match std::env::var("PATH") {
        Ok(v) => v,
        Err(_) => return false,
    },
};

这与我建议的写法完全一致:PATH 未设置时提前返回 false,不再退化为空字符串导致 split_paths("") 产生空路径分量并触碰 CWD 检测。之前标记的问题已解决,review 线程已 resolved。

无新问题引入。 上次整体判断维持不变:倾向 Approve

— 由 Claude Routine 自动生成;如需复评请 @ 维护者人工触发。


Generated by Claude Code

Copy link
Copy Markdown
Owner Author

上次 review(ff65445)指出了 Windows is_on_pathPATH 未设置时使用 unwrap_or_default() 会产生空字符串、进而误探测当前目录的边界情况。新提交 ecd1440 已将其替换为显式 match ... { Err(_) => return false },与 Unix 侧的行为严格对齐。

该问题已修复,无新增问题。

— 由 Claude Routine 自动生成;如需复评请 @ 维护者人工触发。


Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

CLI 智能体设置界面 Mac 显示未安装,新建标签页也没有显示

2 participants