首页
/ ECC Auto Update 命令深度解析:基于 install-state 的仓库拉取与受管目标重装机制

ECC Auto Update 命令深度解析:基于 install-state 的仓库拉取与受管目标重装机制

2026-09-07 18:22:38作者:凤尚柏Louis

ECC(everything-claude-code)的 auto-update 命令用于"从上游仓库拉取最新改动,并按原始安装请求重新生成当前上下文下所有受管目标的安装"。它是 ECC 选择性安装体系(SELECTIVE-INSTALL-ARCHITECTURE.md)中负责版本演进同步的关键入口:与只做增量修补的 repair.js 不同,auto-update 采用"先拉仓库、再基于记录的 install-state 重建请求重跑 install-apply.js"的全量重装策略,从而能安全消化上游的重命名与删除。读完本文,你将掌握该命令的参数语义、底层执行管线(install-state 发现 → git 同步 → 参数重建 → 重装),以及它在多 harness、多项目、旧版布局迁移场景下的行为边界与安全防线。

关联文档:commands/auto-update.md(另有中文译本 docs/zh-CN/commands/auto-update.md);核心实现:scripts/auto-update.js

一、命令是什么:从文档定位到源码入口

原文档用一句话定义了该命令的职责:

Update ECC from its upstream repo and regenerate the current context's managed install using the original install-state request.

即两段式语义:

  1. 更新上游仓库——把 ECC 仓库本身 git fetch + git pull 到最新;
  2. 重建受管安装——读取每个目标此前落盘的 ecc-install-state.json,从中还原出"最初的安装请求"(profile、modules、components、hooks 开关等),再用这个请求重新执行 install-apply.js

在命令注册层面,scripts/ecc.jsauto-update 映射到 scripts/auto-update.js,说明这是 ECC 选择性安装 CLI 的一个一等公民子命令,因此既可以直接 node scripts/auto-update.js ...,也可以通过 ecc auto-update ... 间接触发(scripts/ecc.js 的帮助示例中即包含 ecc auto-update --dry-run)。文档 front-matter 中标有 disable-model-invocation: true,表示该命令面向用户在终端或 hook 流程中手动执行,而非由模型自行发起。

二、核心思想:为什么"重新安装"比"增量修复"更可靠

文档 Notes 部分给出两个关键论断,这也是理解整个设计的一把钥匙:

  • 命令使用记录的安装状态请求(recorded install-state request),在拉取最新仓库改动后重新运行 install-apply.js
  • 重装是有意为之(Reinstall is intentional):它能处理上游的重命名和删除,而 repair.js 无法仅凭过期的操作记录安全地重建这些变更。

2.1 与 repair 的分工

repair.js 的思路是基于 install-state 中逐条记录的 operations(如 copy-filemerge-jsonrender-template)做"对账式修补"。当一个文件在上游被重命名时,旧状态里记录的 sourceRelativePath 已指向不存在的源文件;当一个文件被删除时,旧状态中的 copy-file 记录需要反过来执行清理。单纯重放过期操作既无法得知新文件布局,也可能对已被用户修改的文件产生误判。而 auto-update 用"重建请求 → 全量重装"绕开了这一问题:安装器拿到的是语义化的原始请求(要什么 profile、哪些 modules/components),而非脆弱的逐文件 diff,于是上游任何结构性变化都能在重新解析清单时被自然吸收。

源码中,scripts/auto-update.js 的执行主函数 runAutoUpdatescripts/auto-update.js)完整呈现了这一管线:

discoverInstalledStates(homeDir, projectRoot, targets)
        │
        ▼
过滤存在且非 legacy 的记录(records),单独收集 legacy 记录用于告警
        │
        ▼
对每条记录推断/校验 repo root(--repo-root 或从 state.operations 反推)
        │
        ▼
非 dry-run 时:git fetch --all --prune && git pull --ff-only
        │
        ▼
buildInstallApplyArgs(record) 从 state.request 重建安装参数
        │
        ▼
在 determineInstallCwd 决定的 cwd 下调用 install-apply.js(附 --json,dry-run 再加 --dry-run)
        │
        ▼
汇总 checked / planned·updated / errors,--json 输出结构化结果

三、命令行用法与参数语义

原文档提供了三种典型调用方式,下面逐一展开并结合源码补充参数细节。

3.1 参数总览

scripts/auto-update.jsparseArgsscripts/auto-update.js)与帮助文本(scripts/auto-update.js)定义了全部参数:

参数 说明 取值 / 行为
--target <name> 限定要处理的受管目标(可重复出现) 支持目标列表来自 SUPPORTED_INSTALL_TARGETS(见下)。省略时默认扫描全部 adapter
--repo-root <path> 显式指定 ECC 仓库根目录,跳过推断 传入后会先经 validateRepoRoot 校验(见第六节),否则从 install-state 记录反推
--dry-run 预览模式:不执行 git 拉取、不真正落盘 只重建出重装计划并交给 install-apply.js --dry-run,状态标记为 planned
--json 以 JSON 输出结构化结果 便于脚本化消费 summary 与逐条 results
--help / -h 打印用法并退出 退出码 0
未知参数 抛出 Unknown argument: <arg>scripts/auto-update.js

SUPPORTED_INSTALL_TARGETS 定义于 scripts/lib/install-manifests.js,当前为:

claude, claude-project, cursor, antigravity, codex, gemini, opencode,
codebuddy, joycode, qwen, zed, hermes, openclaw, kimi, adal

可见 ECC 的"受管目标"横跨多种 Agent harness:既有人目录级的 claude,也有项目级的 claude-project / cursor / antigravity 等。

3.2 原文档三种调用方式

# 1) 预览本次更新,不做任何变更
ECC_ROOT="${CLAUDE_PLUGIN_ROOT:-$(node -e "…解析 ECC 仓库根目录的一行脚本…")}"
node "$ECC_ROOT/scripts/auto-update.js" --dry-run

# 2) 只更新当前项目中 Cursor 管理的文件
node "$ECC_ROOT/scripts/auto-update.js" --target cursor

# 3) 显式覆盖 ECC 仓库根目录
node "$ECC_ROOT/scripts/auto-update.js" --repo-root /path/to/everything-claude-code

关于用法 1 中那段较长的 node -e 脚本:它本质是一个仓库根目录解析器,优先级依次为 CLAUDE_PLUGIN_ROOT 环境变量 → ~/.claude 下的 plugins 候选目录(eccecc@eccmarketplaces/ecceverything-claude-code 等)→ ~/.claude/plugins/cache 中的缓存目录,最后通过 scripts/lib/resolve-ecc-root.jsresolveEccRoot({ probe: 'scripts/auto-update.js' }) 逐级探测。也就是说,只要仓库布局未变,就能定位到含 scripts/auto-update.js 的 ECC 根目录。日常若已通过 ecc CLI 使用,可直接简化为:

ecc auto-update --dry-run
ecc auto-update --target cursor

三种调用与源码参数一一对应:用法 2 对应 parsed.targets = ['cursor'],随后 discoverInstalledStates 只会为该 target 构造发现记录(scripts/lib/install-lifecycle.js 起,内部按 normalizeTargets 归一化后 flatMap 每个 adapter);用法 3 对应 requestedRepoRoot,一旦传入,所有记录共用该根目录且跳过推断(scripts/auto-update.js 中若未显式指定且推断出多个不同仓库根会直接抛错 Multiple ECC repo roots detected)。

四、核心管线逐步拆解

4.1 第 1 步:发现受管目标与 install-state

入口 discoverInstalledStates({ homeDir, projectRoot, targets })(实现在 scripts/lib/install-lifecycle.js)为每个 target 定位对应的 adapter,并构造"发现记录"。记录携带的关键字段有:

  • adapter{ id, target, kind },其中 kind 区分 home(安装到用户主目录,如 ~/.claude)与 project(安装到当前项目目录,如 .cursor);
  • installStatePath:install-state 文件位置,例如 home 布局下的 ~/.claude/ecc/install-state.json 或项目布局下的 .cursor/ecc-install-state.json(测试代码 tests/scripts/auto-update.test.jsmakeRecord 可见两种路径约定);
  • state:解析后的安装状态本体,包含 target.rootrequestresolutionoperationssource 等;
  • exists / error / legacy:用于后续过滤。

runAutoUpdate 会立刻把记录分为两组(scripts/auto-update.js):

const records = discoveredRecords.filter(record => record.exists && !record.legacy);
const legacyRecords = discoveredRecords.filter(record => record.exists && record.legacy);
  • 非 legacy 记录,进入正常更新流程;
  • 仅剩 legacy 记录的场景(例如只发现旧版 OpenCode ~/.opencode 或旧版 Antigravity .agent 的 install-state),不会执行任何 git 或重装动作,而是给出迁移提示警告:先运行一次对应 harness 的安装器完成布局迁移(.opencode → 配置目录、.agent.agents),再回来做 auto-update。相关告警文本生成在 legacyMigrationWarningscripts/auto-update.js),并在测试 tests/scripts/auto-update.test.js 中被验证。

如果当前 home/project 上下文下完全没有 install-state,则直接返回空结果(checkedCount = 0),人类可读输出为:

No ECC install-state files found for the current home/project context.

(或存在 legacy 时的 No active ECC install-state files found ... + 迁移警告。)

4.2 第 2 步:推断并校验 ECC 仓库根目录

有两种来源(scripts/auto-update.js):

  1. 显式 --repo-root:直接 validateRepoRoot
  2. 从 install-state 反推deriveRepoRootFromStatescripts/auto-update.js)遍历 state.operations,利用每条操作的 sourcePath(源文件绝对路径)与其 sourceRelativePath 的相对层级,逐级 path.dirname 向上收缩,得到仓库根。测试用例 tests/scripts/auto-update.test.js 验证了"基于 scripts/setup-package-manager.js 推导出仓库根 /tmp/ecc"以及"缺少源元数据时报 Unable to infer ECC repo root"两种情形。

若未显式指定、但各记录推断出不同的仓库根,为防混用多个版本仓库,直接抛错终止(详见 tests/scripts/auto-update.test.jsMultiple ECC repo roots detected 用例)。

4.3 第 3 步:同步仓库(非 dry-run)

在仓库根目录下依次执行两条 git 命令(scripts/auto-update.js):

execute('git', ['fetch', '--all', '--prune'], { cwd: repoRoot, env });
execute('git', ['pull', '--ff-only'], { cwd: repoRoot, env });
  • fetch --all --prune:抓取所有远端并清理已删除的远端分支引用;
  • pull --ff-only仅允许快进合并,若本地存在与上游分叉的提交会直接失败,避免产生 merge commit 污染受管仓库。

两命令在 --dry-run 下都会被跳过,且用于子进程的环境变量固定为 { ...process.env, HOME: homeDir, USERPROFILE: homeDir }scripts/auto-update.js),把 HOME 钉在发现 install-state 时所用的同一主目录,保证跨用户场景行为一致。外部命令统一走 runExternalCommandscripts/auto-update.js),基于 spawnSync 同步执行,maxBuffer 设为 10 MB,任何非零退出码都会把 stderr/stdout 并入错误信息抛出。

4.4 第 4 步:从记录请求重建安装参数

buildInstallApplyArgs(record)scripts/auto-update.js)把 install-state 中记录的"原始请求"翻译回 install-apply.js 的命令行参数:

install-state 字段 重建出的参数
state.target.target(回退 record.adapter.target --target <target>
request.profile --profile <profile>
request.modules --modules <m1,m2,…>(逗号连接)
request.includeComponents[] 每个组件追加一个 --with <id>
request.excludeComponents[] 每个组件追加一个 --without <id>
hook 同意状态(getRecordedHookConsent,见 scripts/lib/install/hook-consent.js enabled--enable-hooksdeclined--no-hooks
request.legacyLanguages[](旧式语言参数) 按顺序作为位置参数追加,如 typescript python

也就是说,auto-update 不是直接照搬上一轮的逐文件清单,而是把语义化请求原样交给当前最新清单重新求解,这正是它能消化上游重命名/删除的机理。测试对"legacy 重装"与"manifest 重装"两类重建均有断言(tests/scripts/auto-update.test.js),其中 manifest 示例的期望输出为:

--target cursor --profile developer --modules platform-configs
--with component:alpha --without component:beta --no-hooks

另外对"旧版 install-state 缺少 hookConsent 字段"的情况,getRecordedHookConsent 会结合 resolution.selectedModules / 操作记录中是否包含 hooks-runtime 模块做推断,测试用例(tests/scripts/auto-update.test.js)显示其会补出 --enable-hooks

4.5 第 5 步:在正确的工作目录执行重装

determineInstallCwd(record, repoRoot)scripts/auto-update.js)决定 install-apply 子进程的 cwd

  • kind === 'project' 的项目级适配器:以 state.target.root(形如 <项目>/.cursor)的父目录(即项目根)作为 cwd;
  • 其余(home 级):以 ECC 仓库根作为 cwd。

随后针对每条有效记录执行(scripts/auto-update.js):

const args = [path.join(repoRoot, 'scripts', 'install-apply.js'), ...installArgs, '--json'];
if (options.dryRun) args.push('--dry-run');
execute(process.execPath, args, { cwd, env });

即用当前 Node 可执行文件运行仓库内的 scripts/install-apply.js,固定追加 --json(dry-run 时再加 --dry-run),然后解析其 stdout JSON 作为 payload 记录进结果。端到端测试 tests/scripts/auto-update.test.js 断言了完整命令序列:

['git', 'fetch'], ['git', 'pull'],
[node, <repoRoot>/scripts/install-apply.js]

install-apply.js 收到 --target cursor --profile developer --with component:alpha --without component:beta --jsoncwd 为项目根目录。

五、运行结果与退出语义

人类可读输出由 printHuman 生成(scripts/auto-update.js),典型形态为:

Auto-update summary:

Repo root: /path/to/everything-claude-code

- cursor-project
  Status: UPDATED
  Install-state: /path/to/project/.cursor/ecc-install-state.json
  Reinstall args: --target cursor --profile developer --with component:alpha --without component:beta

Summary: checked=1, updated=1, errors=0

dry-run 时首行为 Auto-update dry run,状态为 PLANNED,且末尾汇总文案变为 planned=。每条结果的 status 取值包括 updated / planned / error,出错时会附带 installStatePathrepoRootinstallArgserror.message 便于排查。

--json 模式输出的顶层结构为:

{
  "dryRun": true,
  "repoRoot": "/path/to/everything-claude-code",
  "results": [
    {
      "adapter": { "id": "cursor-project", "target": "cursor", "kind": "project" },
      "installStatePath": "/path/to/project/.cursor/ecc-install-state.json",
      "repoRoot": "/path/to/everything-claude-code",
      "cwd": "/path/to/project",
      "installArgs": ["--target", "cursor", "--json"],
      "status": "planned"
    }
  ],
  "warnings": [],
  "summary": { "checkedCount": 1, "updatedCount": 1, "errorCount": 0 }
}

进程退出码同样明确(scripts/auto-update.js):summary.errorCount > 0 时置为 1,否则为 0;顶层异常则打印 Error: <message> 后以 1 退出——这套语义适合被 CI、pre-push hook 或日常脚本直接消费。

六、安全设计:非受信仓库根的防御

auto-update 会把仓库内的 install-apply.js 当作可执行代码来运行,因此仓库根目录的可信性是安全边界。validateRepoRootscripts/auto-update.js)强制三道校验:

  1. 根目录下必须存在 package.json,否则抛 missing package.json
  2. 根目录下必须存在 scripts/install-apply.js
  3. package.jsonname 必须在白名单中:
const ECC_PACKAGE_NAMES = new Set(['ecc-universal', 'everything-claude-code']);

源码注释(scripts/auto-update.js)说明了动机:若不加约束,某个克隆项目如果自带了形如 evil/{package.json, scripts/install-apply.js} 的嵌套文件,就可能诱导 auto-update 去执行攻击者代码(对应安全公告编号 GHSA-hfpv-w6mp-5g95)。因此任何经 --repo-root 传入或从 install-state 推断出的仓库根,只要其 package.json 名称不在白名单内,都会立即以 Refusing to run install from untrusted repo root ... 拒绝执行。测试端用 ensureFakeRepo 写入 { name: 'everything-claude-code' } 模拟合法仓库(tests/scripts/auto-update.test.js),验证了这一准入机制与后续全流程。

另外注意,即使仓库本身可信,真正的写入/删除动作仍落在 scripts/install-apply.js 一侧,而该安装器内部对 install-state 的每条操作都做"受信根内"路径校验(如 scripts/lib/install-lifecycle.jsgetManagedDestination / ensureContainedParentDir),杜绝越界写删。auto-update 因而形成"仓库可信 + 安装器路径受信"的双层防线。

七、自动化调用建议与最佳实践

综合原文档 Notes 与源码行为,落地使用时的建议如下:

  1. 先 dry-run 再实跑:升级涉及整仓 git 操作与全量重装,先执行 node "$ECC_ROOT/scripts/auto-update.js" --dry-run(或 ecc auto-update --dry-run),观察重建出的 Reinstall args 是否符合预期,再正式执行。
  2. 按需限定 target:多 harness 用户若只想同步某个目标(如项目级 Cursor),加 --target cursor 可缩小影响面;--target 支持重复传参(如 --target claude --target codex),实际只会保留 SUPPORTED_INSTALL_TARGETS 中注册过的目标。
  3. 多仓库并存时显式指定:若同一上下文里存在多份 ECC 仓库的 install-state 记录,省略 --repo-root 会触发 Multiple ECC repo roots detected 报错,此时应显式传入 --repo-root 指向当前要同步的仓库。
  4. 旧版布局先迁移:当输出提示"legacy OpenCode / Antigravity install-state"时,先按提示运行对应安装器完成目录迁移,再执行 auto-update;不要试图手工修改 install-state 绕过。
  5. 失败定位:单条记录失败不影响其他记录执行,错误会被汇总到 summary.errorCount 并以非零码结束;配合 --json 可从各 results[].error 拿到具体原因。

这套"记录请求 → 仓库同步 → 全量重装"的模式,也解释了为何升级比修补更彻底:install-state 是整个 ECC 受管安装的"单一事实来源",auto-update 只是把这份来源重新交给最新清单执行一遍。若想深入了解其对账细节、路径安全与 legacy 迁移实现,可继续阅读 scripts/auto-update.jsscripts/lib/install-lifecycle.js 与测试 tests/scripts/auto-update.test.js;命令行层面的兄弟命令(repairinstalldoctorlist-installed 等)可参见 COMMANDS-QUICK-REF.md 与命令注册表 scripts/ecc.js

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.76 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
860
1.35 K
docsdocs
暂无描述
Markdown
899
5.83 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
925
1.85 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.84 K
1.02 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
533
601
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.03 K
525
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.37 K
1.46 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
548
395