终端能跑的 CLI,CC Switch 却检测不到
在 Windows 上用 CC Switch 管理 AI 编程 CLI,有一类问题特别磨人:你在终端里敲 claude --version 或 codex --version,版本号打得清清楚楚;切回桌面应用,这个工具的状态却被判成「未安装」。或者更别扭的一种——应用认出来了,但显示的版本比终端里那个旧一截,怎么升级都不动窝。
这两种表现看上去像是检测逻辑写错了,实际上不是。CC Switch 在 v3.19.2 到 v3.20.1 之间有一条专治这件事的修复,提交是 de9af49a,标题 fix(detect): resolve Windows CLI detection from registry PATH and standalone installer dirs (#6284),只改了 src-tauri/src/commands/misc.rs 一个文件,+445/−36。它的 commit body 写着 Closes #6278, #6061, #6047、Refs #4366, #4701——一个 PR 结掉五个 issue,这本身就说明「检测不到」不是一个 bug,而是三种成因撞在同一个症状上。
先把口径讲清楚:本文依据的是 cc-switch 仓库 v3.20.1 的快照(commit 3217f725),核对日 2026-08-31。我们只是静态地读源码和仓库里的文档,没有编译、没有运行、也没有安装过这个桌面应用,所以下面不会出现任何关于窗口长什么样、点哪里、反应快不快的描述。凡是提到行号的地方,你都可以自己去仓库里翻到那一行对着看。
三种成因,长同一张脸
src-tauri/src/commands/misc.rs:1560-1579 那一大段文档注释,把三种成因原样写进了源码。这是我在这个仓库里见过的最省事的一处「源码即事实」——不用去翻 issue,注释里就有。
第一种:应用内自更新之后拉起的进程,环境本身就是残缺的。 对应 #6061。MSI/WiX 在安装完成后自动把新版本拉起来,这个进程只继承了机器级 PATH,丢掉了用户级 PATH。于是所有装在用户 PATH 位置上的 CLI 全部读作「未安装」——直到你彻底退出应用、从开始菜单重新启动一次,一切又正常了。这个「重启就好」的特征非常关键,后面判定要用。
第二种:安装位置从来就没被扫描过。 对应 #6278 / #6047 / #4366。不是所有人都用 npm 装 CLI:winget 装的 Claude Code、独立安装器装的 Codex、以及自定义了 npm prefix 的目录,都落在检测代码原先的硬编码清单之外。这一类的特征是:不管你怎么重启,它就是找不到。
第三种:探测顺序反了。 对应 #4701。旧代码把硬编码的 fallback 目录排在 PATH 默认项前面探测,于是 %APPDATA%\npm 里一个早就没人用的过期 shim,会把你终端里实际在跑的那个新版本整个遮住。表现出来就是「我明明升级过了,它还显示旧版本」。
三种成因分别对应三种排查动作,混在一起猜是浪费时间。
怎么确认是哪一种
判定的核心是一句话:桌面应用看不到你终端里能跑的命令,多半不是它眼瞎,而是它继承到的环境和你登录 shell 的环境不是同一份。 顺着这句话,有三个动作可以逐个做。
动作一,先排掉自更新残留。 如果这次「检测不到」发生在应用内升级之后,先把应用完全退出(不是关窗口,是进程真的没了),再从开始菜单启动一次。如果状态恢复正常,那就是第一种,跟你的安装位置无关。
动作二,看 PATH 默认项落在哪。 在终端里跑:
where claude
where codex
where 会按 PATH 顺序把所有命中列出来,第一条就是你敲命令时实际执行的那个。把这一条的目录记下来,跟下面这两组位置对一下:
| 位置 | 谁会装到这里 | v3.20.1 是否显式纳入 |
|---|---|---|
%LOCALAPPDATA%\Programs\OpenAI\Codex\bin | Codex 独立安装器 | 是(misc.rs:1764) |
%LOCALAPPDATA%\Programs\claude | winget / 官方原生安装器装的 Claude Code | 是(misc.rs:1775) |
%APPDATA%\npm | npm 全局安装 | 是(原先就有) |
| 自定义 npm prefix 目录 | 改过 prefix 的人 | 不在硬编码清单里,靠 PATH 覆盖 |
如果 where 的第一条落在最后一行那种自定义目录里,而你用的还是旧版本,那基本就是第二种成因。
动作三,比对两处 PATH 的差异。 系统上真正的 PATH 有两个来源:HKEY_CURRENT_USER\Environment 下的 Path(用户级)和 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment 下的 Path(机器级)。把你那个工具所在的目录,跟这两处的值比一比——如果它只出现在用户级里,那它就正好是自更新场景下最先被丢掉的那一类。顺带一提,这两个注册表键不是新引入的,misc.rs:1568 的注释显式点名「与 env_checker::check_system_env 读同一组键,这里只读 Path 值」,说明项目里本来就有先例。
以上命令与位置对照是按仓库中的字段语义组合的示例,未经实测,请以官方文档与 --help 的实际输出为准。
v3.20.1 是怎么处理的
v3.19.2 的检测直接吃进程继承的 PATH;v3.20.1 已经改成绕过继承、自己重建一份「有效 PATH」。落点是四个函数,都能实读:
effective_path_string()(misc.rs:1581)读进程 PATH,再分别读上面那两个注册表值,两个值都先过一遍 expand_env_chars(misc.rs:1633)把 REG_EXPAND_SZ 里的 %VAR% 展开——展不开的变量原样保留,对应单测 expand_env_chars_preserves_unknown_vars_and_plain_text(misc.rs:6626)。最后三份交给 merge_path_segments_win(misc.rs:1677)合并。
合并的顺序是有讲究的:进程条目排在最前面,注册表条目只填补进程缺的那部分,再做大小写不敏感去重。这个次序保证了运行时覆盖仍然优先——你临时在某个 shell 里改过 PATH 再启动应用,那份改动不会被注册表值顶掉。单测 merge_path_segments_win_preserves_order_and_dedupes_case_insensitively(misc.rs:6601)就锁着这条语义。
build_tool_search_paths(misc.rs:1695)负责第二种成因,把两个独立安装器目录显式加进候选,单测是 build_tool_search_paths_includes_standalone_installer_dirs(misc.rs:6643)。
第三种成因由新增的 probe_path_default_version(tool)(misc.rs:1972)解决:Windows 上先探 PATH 默认项,只有确实不存在时才回退到目录扫描——把顺序掰回和非 Windows 平台的 try_get_version → scan_cli_version 一致。真正去查 PATH 默认项的是 windows_path_lookup_command(misc.rs:2244)与 resolve_path_default(misc.rs:2272),对合并后的有效 PATH 跑 where。
这里还藏着一个容易忽略的细节。misc.rs:2290-2299 的注释说明,where 命中里凡是落在 Microsoft\WindowsApps 下的条目会被跳过——那些是 App Execution Alias(reparse point),作用是拉起应用商店或协议处理器,根本不是能用 --version 探测的 CLI,不能当成 PATH 默认项。另有一条单测 windows_path_lookup_ignores_same_named_file_in_current_directory(misc.rs:6672 附近)锚定「绝不搜索当前目录」。
需要说清的是:以上都是 v3.20.1 快照里的默认行为,是代码写死的检测策略,可能随版本变。它们也只是让检测「看到」和登录 shell 一样的安装位置,不构成「你一定能检测到」的保证。
处置之后怎么验证
判据只有一条,而且不依赖任何界面观察:应用显示的版本,应该和你在终端里 where 出来的第一条命中所对应的那个可执行文件的版本一致。 不一致就说明两边看到的还不是同一个二进制。
具体验证顺序建议这样走:先在终端 where <工具名> 拿到第一条命中;再对这一条直接跑 --version(而不是敲工具名,避免又被 PATH 顺序绕进去);最后看应用里报的版本号是不是同一个。三者对齐,才算真修好了。如果你是通过「把目录加进用户级 PATH」来解决的,记得改完之后要让应用重新读一次环境——按第一种成因的规律,这意味着完全退出、重新启动。
什么情况说明不是这个原因
这一节比上面几节更重要,因为它决定你要不要继续在这条路上耗。
你不在 Windows 上。 上面这一整套注册表 PATH 重建是 Windows 专属分支,非 Windows 平台走的是 try_get_version → scan_cli_version 那条路,成因和处置完全不同,别把这套判定套过去。
where 的第一条落在 Microsoft\WindowsApps 里。 那是 App Execution Alias,v3.20.1 里是被显式跳过的。这种情况下你要处理的是「这个别名指向的东西到底是不是一个可探测的 CLI」,而不是 PATH 可见性。
检测环节压根没走完,而是整个卡住不动。 那更像是环境预检的子进程挂死,跟 PATH 能不能看见没有关系——这一类的成因是进程组、控制终端、作业控制与超时,我们另有一篇专门讲。
版本号显示正常,但供应商配置改不动、切不了。 那是配置写入路径的问题,跟检测不在一条链上,而且这一条同样要挂版本口径。配置目录落在 WSL 的 \\wsl.localhost / \\wsl$ 这类 UNC 路径下写不动,是 v3.19.2 上原子写的回退条件卡得太窄,发布说明写明「仅 v3.19.2 受影响,该版本内无任何绕过手段」,修复随 v3.20.0 一起出;供应商编辑只更新了数据库、真正的配置文件纹丝不动是另一条独立成因,也在 v3.20.1 之前修掉了。所以你要是已经在 v3.20.1 上,这两条都不该再成立,得往别处找。
你用的还是 v3.19.2 或更早。 那么上面所有函数在你手上的版本里都不存在,比对源码行号只会白费力气。v3.19.2 时检测直接依赖进程继承的 PATH,也不扫那两个独立安装器目录;v3.20.1 已改成重建有效 PATH + 显式纳入独立安装器目录 + 先探 PATH 默认项。仓库的发布说明把这条修复记在 v3.20.0 一节(docs/release-notes/v3.20.0-zh.md:161),并按上面三段式做了归因。
最后说句实话:这条修复真正的价值不在于多扫了两个目录,而在于它把「检测不到」这个含糊的症状,拆成了三个各自可判定、各自可验证的具体问题。你下次再碰到任何桌面工具「看不见我终端里的命令」,第一个该问的都不是「它的检测代码怎么写的」,而是「它这个进程的环境是从哪继承来的」。
本文依据 CC Switch 官方仓库(github.com/farion1231/cc-switch)的 README、docs/ 下的用户手册、路由指南与发布说明,以及 src/、src-tauri/、tests/ 的源码整理,核对日 2026-08-31,对应仓库快照 3217f725(仓库内版本号 3.20.1)。本文内容为仓库源码与文档口径,我们没有安装或运行过这个桌面应用,因此不涉及界面外观、操作手感与切换速度的任何描述。文中出现的阈值与默认值均为源码中的默认配置,不构成对实际运行结果的保证。该项目仍在快速迭代,版本与默认值随时可能变动,请以仓库最新内容为准。
留言讨论
评论发布后会被人工复核,违规内容将被删除。
如果发表没有反应,可以前往联系我们告诉我们。