ECC Auto Update 命令深度解析:基于 install-state 的仓库拉取与受管目标重装机制
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.
即两段式语义:
- 更新上游仓库——把 ECC 仓库本身
git fetch+git pull到最新; - 重建受管安装——读取每个目标此前落盘的
ecc-install-state.json,从中还原出"最初的安装请求"(profile、modules、components、hooks 开关等),再用这个请求重新执行install-apply.js。
在命令注册层面,scripts/ecc.js 将 auto-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-file、merge-json、render-template)做"对账式修补"。当一个文件在上游被重命名时,旧状态里记录的 sourceRelativePath 已指向不存在的源文件;当一个文件被删除时,旧状态中的 copy-file 记录需要反过来执行清理。单纯重放过期操作既无法得知新文件布局,也可能对已被用户修改的文件产生误判。而 auto-update 用"重建请求 → 全量重装"绕开了这一问题:安装器拿到的是语义化的原始请求(要什么 profile、哪些 modules/components),而非脆弱的逐文件 diff,于是上游任何结构性变化都能在重新解析清单时被自然吸收。
源码中,scripts/auto-update.js 的执行主函数 runAutoUpdate(scripts/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.js 的 parseArgs(scripts/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 候选目录(ecc、ecc@ecc、marketplaces/ecc、everything-claude-code 等)→ ~/.claude/plugins/cache 中的缓存目录,最后通过 scripts/lib/resolve-ecc-root.js 的 resolveEccRoot({ 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.js 中makeRecord可见两种路径约定);state:解析后的安装状态本体,包含target.root、request、resolution、operations、source等;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。相关告警文本生成在legacyMigrationWarning(scripts/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):
- 显式
--repo-root:直接validateRepoRoot; - 从 install-state 反推:
deriveRepoRootFromState(scripts/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.js 的 Multiple 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 时所用的同一主目录,保证跨用户场景行为一致。外部命令统一走 runExternalCommand(scripts/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-hooks;declined → --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 --json,cwd 为项目根目录。
五、运行结果与退出语义
人类可读输出由 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,出错时会附带 installStatePath、repoRoot、installArgs 与 error.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 当作可执行代码来运行,因此仓库根目录的可信性是安全边界。validateRepoRoot(scripts/auto-update.js)强制三道校验:
- 根目录下必须存在
package.json,否则抛missing package.json; - 根目录下必须存在
scripts/install-apply.js; package.json的name必须在白名单中:
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.js 的 getManagedDestination / ensureContainedParentDir),杜绝越界写删。auto-update 因而形成"仓库可信 + 安装器路径受信"的双层防线。
七、自动化调用建议与最佳实践
综合原文档 Notes 与源码行为,落地使用时的建议如下:
- 先 dry-run 再实跑:升级涉及整仓 git 操作与全量重装,先执行
node "$ECC_ROOT/scripts/auto-update.js" --dry-run(或ecc auto-update --dry-run),观察重建出的 Reinstall args 是否符合预期,再正式执行。 - 按需限定 target:多 harness 用户若只想同步某个目标(如项目级 Cursor),加
--target cursor可缩小影响面;--target支持重复传参(如--target claude --target codex),实际只会保留SUPPORTED_INSTALL_TARGETS中注册过的目标。 - 多仓库并存时显式指定:若同一上下文里存在多份 ECC 仓库的 install-state 记录,省略
--repo-root会触发Multiple ECC repo roots detected报错,此时应显式传入--repo-root指向当前要同步的仓库。 - 旧版布局先迁移:当输出提示"legacy OpenCode / Antigravity install-state"时,先按提示运行对应安装器完成目录迁移,再执行 auto-update;不要试图手工修改 install-state 绕过。
- 失败定位:单条记录失败不影响其他记录执行,错误会被汇总到
summary.errorCount并以非零码结束;配合--json可从各results[].error拿到具体原因。
这套"记录请求 → 仓库同步 → 全量重装"的模式,也解释了为何升级比修补更彻底:install-state 是整个 ECC 受管安装的"单一事实来源",auto-update 只是把这份来源重新交给最新清单执行一遍。若想深入了解其对账细节、路径安全与 legacy 迁移实现,可继续阅读 scripts/auto-update.js、scripts/lib/install-lifecycle.js 与测试 tests/scripts/auto-update.test.js;命令行层面的兄弟命令(repair、install、doctor、list-installed 等)可参见 COMMANDS-QUICK-REF.md 与命令注册表 scripts/ecc.js。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0631
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
video-shotcraftAI宣传片skill,使用 Remotion 制作电影级产品视频:提供106 张镜头配方卡和可复用的视频魔板。适用于 Claude Code 与 Codex以及所有其他智能体Markdown00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python09
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00