安装策略与落点:cli-hub 会往你机器上写哪些目录

2026-08-10

装任何一个包管理器之前,值得先问一句:它往我机器上写什么。cli-hub 这个工具尤其值得问,因为它管的不是纯 Python 库,而是一批要去驱动本机第三方桌面软件的 harness。本文只谈一件事:执行 cli-hub install 之后,哪些路径被写、哪些没有被写。截至 2026-08-10,我们读的是 CLI-Anything 仓库快照 39634a6cli-hub 包自身版本写在 cli-hub/setup.py:53cli-hub/cli_hub/__init__.py:3 两处,都是 0.4.1,setup.py 的分类器里带着 "Development Status :: 4 - Beta"cli-hub/setup.py:88)——这个 Beta 标记是仓库自己打的,照实记在这里。

五种策略,写在同一个常量里

安装逻辑集中在 cli-hub/cli_hub/installer.py。支持的策略共 5 种,列在第 304-310 行:pip / npm / uv / command / bundled

一条 registry 条目大多数时候不显式声明策略,走的是第 106-119 行的默认推断:

  • _source == "harness"(即来自仓库根 registry.json 的条目)→ 走 pip
  • npm_package 字段,或 package_manager == npm → 走 npm
  • 显式写了 uv / bundled → 各自对应
  • 其余全部落到 command

这条推断链解释了一个容易看错的现象:registry.json 里 79 条 harness(我们采集时实读的条数)几乎不写 install_strategy 字段——整个数组里只有 siyuan 一条带这个字段,值是 "pip"。其余 78 条不是”没配置”,而是靠 _source 兜到了 pip 分支。

反直觉的那一处:账本和落点不是同一样东西

cli-hub 会把安装记录写进 ~/.cli-hub/installed.json,矩阵安装状态写进 ~/.cli-hub/matrix_state.jsoninstaller.py:15-16)。看到这两个文件,很容易以为 harness 被装进了 ~/.cli-hub/ 里的某个私有目录,卸载就是删这个目录。不是。

第 184-192 行写得很直白:harness 安装实际执行的是 sys.executable -m pip install …。也就是说,包被装进的是当前解释器所在的那个 Python 环境——你在哪个虚拟环境里跑 cli-hub,它就装进那个环境。installed.json 只是一本记在 home 目录下的账,它和真实落点是两套东西,可以各自独立地对不上。

这不是我们的推测,是源码里自己承认的:matrix doctor 的实现(installer.py:565-589)逐个成员检查两件事——是否已被记录为安装,以及 entry_point 是否在 PATH 里,任一不满足就给出 cli-hub install <name> 的修复命令。把这两个条件分开检查,本身就说明它们被当成两个可以分别失败的判据。

真实落点具体在哪,取决于策略:

策略实际写到哪源码位置
pip(harness 默认)当前 Python 环境(sys.executable -m pip installinstaller.py:184-192
npm全局 npm 目录(npm install -g <npm_package>installer.py:257-272
uv由 uv 决定;uv 缺失时只返回一段多行安装提示installer.py:57-64
command由 registry 条目自带的命令串决定installer.py:67-84
bundled不写任何文件installer.py:154-162

最后一行值得单独说。bundled 策略根本不执行安装:它只检测条目的 detect_cmd / entry_point 是否已经在 PATH 中,不在就提示你去上游 App 里启用。也就是说,同一句 cli-hub install,对某些条目意味着往磁盘写包,对另一些条目只意味着做一次探测。截至 2026-08-10 我们采集时,public_registry.jsonpackage_manager 的取值分布是 npm 10、pip 4、brew 2、bundled 2、uv 1、script 1,另有 2 条缺该字段——bundled 那两条就属于”装了等于没装”的类型。

顺带一句:registry.json 有 79 条不等于这 79 个软件装上就能用。harness 是否齐件、能不能真正驱动宿主,是另一层的问题;README 也明确提醒过,包装真实桌面软件的 CLI 需要用户自行安装上游应用(README.md:236)。这一点在本站另有专门一篇讲 harness 的缺件情况。

卸载和更新是按名字拼出来的

既然装是走 pip,卸载与更新自然也是。installer.py:196 卸载 harness 时把包名按 cli-anything-<name> 拼接;第 206-215 行更新走的是 pip install --upgrade --force-reinstall

拼接这件事有个直接后果:能不能卸干净,取决于 registry 里的 name 和 PyPI 上的实际包名是否严格对得上。harness 的 install_cmdregistry.json 里写成这种形态(以第 1 条 jumpserver 为例,registry.json:16):

pip install git+https://github.com/HKUDS/CLI-Anything.git#subdirectory=jumpserver/agent-harness

装是从 GitHub 子目录直接装的,而卸载走的是名字拼接这条独立路径——两边并不共用同一个字符串。

~/.cli-hub/ 下到底有什么

把散在几个模块里的落点合到一起,这个目录里会出现的东西是:

路径写它的模块说明
installed.jsoninstaller.py:15-16安装账本
matrix_state.jsoninstaller.py:15-16矩阵安装状态
registry_cache.jsonregistry.py:11-14主 registry 缓存,TTL 3600 秒
public_registry_cache.jsonregistry.py:11-14公共 registry 缓存,同 TTL
matrix_registry_cache.jsonmatrix.py:13-15矩阵 registry 缓存,同 TTL
matrix/<name>/SKILL.mdmatrix_skill.py:5-8:31:40渲染出来的矩阵技能,同目录并排放 references/scripts/
matrix/<name>.SKILL.mdmatrix_skill.py:68-79旧的扁平布局,仅在新路径不存在时回落
.analytics_idanalytics.py:23:194-210一个 uuid4 的匿名标识

最后一行是很多人不会预期的:跑一次 cli-hub 就会在这里留下一个匿名 id。埋点默认 provider 是 posthog(analytics.py:16),退出方式是把 CLI_HUB_NO_ANALYTICS 设为 1 / true / yesanalytics.py:84-85)。事件通过 daemon 线程异步发送、超时 5 秒、异常一律吞掉,源码注释写的是 analytics 绝不能打断用户的工作流(analytics.py:247-264:278-281)。另外它识别 agent 宿主的父进程探测走的是 /proc/<pid>/status/proc/<pid>/cmdlineanalytics.py:104-118),这是 Linux 路径。

矩阵那侧的渲染落点(~/.cli-hub/matrix/)另有一篇专讲,这里只强调它同样落在这个目录下,不要以为矩阵内容只存在于远端。

它会在你机器上执行外部命令

这一点必须说清楚,不能一笔带过。installer.py:67 与第 70-84 行的 _run_command(),在命令串里含 |&&||;$( 或反引号时会用 shell=True 执行,源码注释给出的理由是这些命令来自受信任的 registry、不是用户输入。这就是说:执行什么,由远端 registry 里的字符串决定。加上 harness 本身的用途就是驱动本机的第三方软件,整条链路上会有第三方程序与脚本在你的机器上跑起来。

这里不做任何”要不要放心用”的判断,只把机制摆出来:远端 JSON → 本地 shell。你自己的环境是否适合这样用,由你评估。

可复现的核查动作

不必安装就能自查的几步:

  1. git clone 仓库后打开 cli-hub/cli_hub/installer.py,跳到第 304-310 行看策略枚举,再跳到 106-119 行看默认推断——两处对着读,就知道你关心的那条 registry 条目会走哪一支。
  2. 想确认某条目是不是”装了等于没装”,看它有没有 package_manager: bundled,再对照 installer.py:154-162
  3. 已经装过的机器,直接列 ~/.cli-hub/ 的文件,和上面那张表逐行对。表里没有的文件,说明来自我们这次没有逐行读完的模块(preview.py 等),本文不做推测。
  4. 怀疑账本和环境脱节时,用矩阵的体检命令:
cli-hub matrix doctor <matrix-name>

它会逐个成员报”是否记录安装 / entry_point 是否在 PATH”。命令按仓库中的参数语义组合,未经实测,以官方文档与 --help 的实际输出为准。顺带记住退出码契约(cli-hub/cli_hub/cli.py:60-65):0 成功、1 失败或未找到、2 用法错误、3 部分失败或存在 gap——第 3 种最容易被脚本当成成功,因为它不是 1。

一处口径差

cli-hub-meta-skill/SKILL.md:85 把 cli-hub 描述为 “cli-hub is a lightweight wrapper around pip”,而 installer.py:304-310 实现的是 pip / npm / uv / command / bundled 五种策略;meta-skill 全文也没有提到 public registry 与 npm、uv 这两条安装路径。两处口径不一致,以我们实读的仓库状态为准。

说到这里就停——本篇只负责告诉你,按哪一份描述去预期落点会落空。

其它宿主的安装落点

如果你不是直接用 cli-hub,而是通过某个 agent 宿主的适配层接入,落点又是另一批路径:codex 侧是 ${CODEX_HOME:-$HOME/.codex}/skills/cli-anything,目录已存在会拒绝覆盖(codex-skill/scripts/install.sh:10-11:28-32);hermes 侧是 ${HERMES_HOME:-$HOME/.hermes}/skills/cli-anything-hermeshermes-skill/scripts/install.sh:7-18);Pi 扩展落在 $HOME/.pi/agent/extensions/cli-anything.pi-extension/cli-anything/install.sh:17);qoder 那一路谁的目录都不建,只往 ${QODER_HOME:-$HOME}/.qoder.jsonplugins.sources.local[] 里注册一个绝对路径,改写前先备份成 .qoder.json.bakqoder-plugin/setup-qodercli.sh:41:75-84:103-109)。

这几家各自对接谁、机制有什么差别,本站另有一篇专门讲,这里只列落点,方便你在清理机器时知道该去哪几个地方看。


本文依据 CLI-Anything 官方仓库(github.com/HKUDS/CLI-Anything)的 README、registry.jsondocs/ 与各 agent-harness/ 下的源码整理,核对日 2026-08-10,对应仓库快照 39634a6。 本文内容为仓库源码与文档口径,我们没有安装或运行过其中任何一个 harness, 也没有在本机驱动过任何一款宿主软件,因此不涉及实际操控效果的任何描述。 这类 harness 会在本机执行外部程序与脚本,是否使用请结合自身环境评估。 注册表与 harness 内容随上游更新而变动,请以仓库最新内容为准。 安全相关做法请结合自身环境评估,本文不构成安全方案建议。

想系统学会用 AI?报名体系课或加入会员,照着学、照着用。