ECC 2.0 选择性安装发现¶
目的¶
本文档将 3 月 11 日大型计划中的选择性安装要求转化为具体的 ECC 2.0 发现设计。
目标不仅仅是“安装期间复制更少的文件”。实际目标是构建一个安装系统,能够确定性地回答:
- 请求了什么
- 解析出了什么
- 复制或生成了什么
- 应用了哪些目标特定的转换
- ECC 拥有哪些内容,并且之后可以安全地移除或修复
这是 ECC 1.x 安装与 ECC 2.0 控制平面之间缺失的契约。
当前已实现的基础¶
第一个选择性安装基础层已经存在于仓库中:
manifests/install-modules.jsonmanifests/install-profiles.jsonschemas/install-modules.schema.jsonschemas/install-profiles.schema.jsonschemas/install-state.schema.jsonscripts/ci/validate-install-manifests.jsscripts/lib/install-manifests.jsscripts/lib/install/request.jsscripts/lib/install/runtime.jsscripts/lib/install/apply.jsscripts/lib/install-targets/scripts/lib/install-state.jsscripts/lib/install-executor.jsscripts/lib/install-lifecycle.jsscripts/ecc.jsscripts/install-apply.jsscripts/install-plan.jsscripts/list-installed.jsscripts/doctor.js
当前能力:
- 机器可读的模块和配置目录
- CI 校验清单条目指向真实仓库路径
- 依赖展开和目标过滤
- 适配器感知的操作规划
- 针对旧版和清单安装模式的规范化请求归一化
- 从规范化请求到计划创建的显式运行时分发
- 旧版安装和清单安装都会写入持久安装状态
- 在任何变更之前对安装计划进行只读检查
- 统一的
eccCLI 路由安装、规划和生命周期命令 - 通过
list-installed、doctor、repair和uninstall进行生命周期检查和变更
当前限制:
- 某些模块的目标特定合并/移除语义仍处于脚手架级别
- 旧版
ecc-install兼容性仍指向install.sh package.json中的发布范围仍然较宽
当前代码审查¶
当前安装器栈已经比最初的语言优先 shell 安装器健康得多,但仍在少数文件中集中了过多职责。
当前运行时路径¶
当前的运行时流程是:
install.sh解析真实包根目录的薄 shell 包装器scripts/install-apply.js面向用户的安装器 CLI,支持旧版和清单模式scripts/lib/install/request.jsCLI 解析以及规范化请求归一化scripts/lib/install/runtime.js从规范化请求到安装计划的运行时分发scripts/lib/install-executor.js参数转换、旧版兼容性、操作物化、文件系统变更和安装状态写入scripts/lib/install-manifests.js模块/配置目录加载以及依赖展开scripts/lib/install-targets/目标根目录和目的地路径脚手架scripts/lib/install-state.js基于架构的安装状态读写scripts/lib/install-lifecycle.js从已存储操作派生的 doctor/repair/uninstall 行为
这足以证明选择性安装基础层,但还不足以让安装器架构显得稳定。
当前优势¶
- 安装意图现在通过
--profile和--modules明确表达 - 请求解析和请求归一化现在已从 CLI shell 中拆分出来
- 目标根目录解析已经适配器化
- 生命周期命令现在使用持久安装状态,而不是猜测
- 仓库已经通过
ecc和install-apply.js拥有统一的 Node 入口点
当前仍存在的耦合¶
install-executor.js比以前更小,但仍在同时承载过多规划层和物化层。 请求边界现在已经提取出来,但旧版请求转换、清单计划展开和操作物化仍然混在一起。- 目标适配器仍然过于单薄。 目前它们主要解析根目录并搭建目的地路径。真正的安装语义仍然存在于执行器分支和路径启发式规则中。
- 规划器/执行器边界还不够清晰。
install-manifests.js解析模块,但最终安装操作集仍部分构建在执行器特定逻辑中。 - 生命周期行为更多依赖于底层记录的操作,而不是稳定的模块语义。 这对普通文件复制有效,但对合并/生成/移除行为会变得脆弱。
- 兼容模式直接混入主安装器运行时。 旧版语言安装应表现得像请求适配器,而不是一个并行的安装器架构。
提议的模块化架构变更¶
下一个架构步骤是将安装器拆分为显式层,每一层返回稳定数据,而不是立即变更文件。
目标状态¶
期望的安装流水线是:
- CLI 界面
- 请求归一化
- 模块解析
- 目标规划
- 操作规划
- 执行
- 安装状态持久化
- 基于同一操作契约构建的生命周期服务
主要思想很简单:
- 清单描述内容
- 适配器描述目标特定的落地语义
- 规划器描述应该发生什么
- 执行器应用这些计划
- 生命周期命令复用同一计划/状态模型,而不是重新发明它
提议的运行时层¶
1. CLI 界面¶
职责:
- 仅解析用户意图
- 路由到 install、plan、doctor、repair、uninstall
- 渲染人类可读或 JSON 输出
不应拥有:
- 旧版语言转换
- 目标特定安装规则
- 操作构建
建议文件:
scripts/ecc.js
scripts/install-apply.js
scripts/install-plan.js
scripts/doctor.js
scripts/repair.js
scripts/uninstall.js
这些将保持为入口点,但会成为库模块的薄封装。
2. 请求规范化器¶
职责:
- 将原始 CLI 标志转换为规范的安装请求
- 将旧版语言安装转换为兼容性请求结构
- 尽早拒绝混合或歧义输入
建议的规范请求:
{
"mode": "manifest",
"target": "cursor",
"profile": "developer",
"modules": [],
"legacyLanguages": [],
"dryRun": false
}
或者,在兼容模式下:
{
"mode": "legacy-compat",
"target": "claude",
"profile": null,
"modules": [],
"legacyLanguages": ["typescript", "python"],
"dryRun": false
}
这使得流水线其余部分可以忽略请求来自旧版还是新版 CLI 语法。
3. 模块解析器¶
职责:
- 加载 manifest 目录
- 展开依赖
- 拒绝冲突
- 按目标过滤不支持的模块
- 返回规范解析对象
该层应保持纯函数且只读。
它不应知道:
- 目标文件系统路径
- 合并语义
- 复制策略
当前最接近的文件:
scripts/lib/install-manifests.js
建议拆分:
scripts/lib/install/catalog.js
scripts/lib/install/resolve-request.js
scripts/lib/install/resolve-modules.js
4. 目标规划器¶
职责:
- 选择安装目标适配器
- 解析目标根目录
- 解析 install-state 路径
- 展开模块到目标的映射规则
- 发出目标感知的操作意图
目标特定语义应在此处实现。
示例:
- Claude 可能在
~/.claude下保留原生层级结构 - Cursor 可能以不同于规则的方式同步捆绑的
.cursor根子项 - 生成的配置可能根据目标需要合并或替换语义
当前最接近的文件:
scripts/lib/install-targets/helpers.jsscripts/lib/install-targets/registry.js
建议演进:
scripts/lib/install/targets/registry.js
scripts/lib/install/targets/claude-home.js
scripts/lib/install/targets/cursor-project.js
scripts/lib/install/targets/antigravity-project.js
每个适配器最终应暴露比 resolveRoot 更多的能力。
它应负责其目标家族的路径和策略映射。
5. 操作规划器¶
职责:
- 将模块解析加上适配器规则转换为类型化操作图
- 发出一等公民操作,例如:
copy-filecopy-treemerge-jsonrender-templateremove- 附加所有权和验证元数据
这是当前安装器中缺失的架构接缝。
目前,操作一部分在脚手架层面,一部分特定于执行器。 ECC 2.0 应将操作规划作为独立阶段,以便:
plan成为执行的真正预览doctor可以验证预期行为,而不仅仅是当前文件repair可以安全地重建确切缺失的工作uninstall可以仅回滚受管理的操作
6. 执行引擎¶
职责:
- 应用类型化操作图
- 强制覆盖和所有权规则
- 安全地暂存写入
- 收集最终已应用操作结果
该层不应决定做什么。 它只应决定如何安全地应用提供的操作类型。
当前最接近的文件:
scripts/lib/install-executor.js
建议重构:
scripts/lib/install/executor/apply-plan.js
scripts/lib/install/executor/apply-copy.js
scripts/lib/install/executor/apply-merge-json.js
scripts/lib/install/executor/apply-remove.js
这将把执行器逻辑从一个大型分支运行时转变为一组小型操作处理器。
7. 安装状态存储¶
职责:
- 验证并持久化 install-state
- 记录规范请求、解析结果和已应用操作
- 支持生命周期命令,而不迫使它们逆向工程安装
当前最接近的文件:
scripts/lib/install-state.js
该层已经接近正确形态。主要剩余更改是在合并/生成语义真正落地后,存储更丰富的操作元数据。
8. 生命周期服务¶
职责:
list-installed:仅检查状态doctor:将期望/install-state 视图与当前文件系统比较repair:从状态重新生成计划并重新应用安全操作uninstall:仅移除 ECC 拥有的输出
当前最接近的文件:
scripts/lib/install-lifecycle.js
该层最终应基于操作类型和所有权策略运行,而不仅仅是原始 copy-file 记录。
建议的文件布局¶
干净的模块化最终状态大致应如下所示:
scripts/lib/install/
catalog.js
request.js
resolve-modules.js
plan-operations.js
state-store.js
targets/
registry.js
claude-home.js
cursor-project.js
antigravity-project.js
codex-home.js
opencode-home.js
executor/
apply-plan.js
apply-copy.js
apply-merge-json.js
apply-render-template.js
apply-remove.js
lifecycle/
discover.js
doctor.js
repair.js
uninstall.js
这不是打包拆分。 它是当前仓库内的代码所有权拆分,使每一层只负责一项工作。
当前文件迁移映射¶
最低风险的迁移路径是演进式的,而不是重写。
保留¶
install.sh作为公共兼容垫片scripts/ecc.js作为统一 CLIscripts/lib/install-state.js作为状态存储的起点- 当前目标适配器 ID 和状态位置
提取¶
- 从
scripts/lib/install-executor.js中提取请求解析和兼容性转换 - 将目标感知的操作规划从执行器分支移到目标适配器以及规划器模块
- 将生命周期特定分析从共享生命周期单体拆分为更小的服务
逐步替换¶
- 用类型化操作替换宽泛的路径复制启发式
- 用适配器拥有的语义替换仅脚手架的适配器规划
- 用旧版请求转换替换旧版语言安装分支,使其进入同一规划器/执行器流水线
接下来要实施的立即架构更改¶
如果目标是 ECC 2.0,而不仅仅是“足够可用”,那么下一步模块化工作应该是:
- 将
install-executor.js拆分为请求规范化、操作规划和执行模块 - 将目标特定的策略决策移入由适配器拥有的规划方法
- 让
repair和uninstall基于类型化的操作处理器运行,而不是只基于普通的copy-file记录 - 让清单了解安装策略和所有权,使规划器不再依赖路径启发式
- 只有在内部模块边界稳定之后,再收窄 npm 发布面
当前模型为何不够¶
今天 ECC 仍然像一个宽泛的负载复制器:
install.sh以语言优先,并且目标分支很重- 目标部分隐含在目录布局中
- uninstall、repair 和 doctor 现在已经存在,但仍然是早期生命周期命令
- 仓库无法证明先前安装实际写入了什么
package.json中的发布面仍然很宽
这会带来 mega plan 中已经指出的问题:
- 用户会拉取比其 harness 或工作流所需更多的内容
- 由于安装未被记录,支持和升级更难
- 由于安装逻辑在 shell 分支中重复,目标行为会发生漂移
- 像 Codex 或 OpenCode 这样的未来目标需要更多特例逻辑,而不是 复用稳定的安装契约
ECC 2.0 设计主张¶
选择性安装应被建模为:
- 将请求意图解析为规范模块图
- 通过目标适配器转换该图
- 执行确定性的安装操作集
- 将 install-state 写入为持久的事实来源
这意味着 ECC 2.0 需要两个契约,而不是一个:
- 内容契约 哪些模块存在,以及它们如何相互依赖
- 目标契约 这些模块如何落入 Claude、Cursor、Antigravity、Codex 或 OpenCode 内部
当前仓库只有前半部分的早期形态。 当前仓库现在有了第一个完整垂直切片,但没有完整的 目标特定语义。
设计约束¶
- 保持
everything-claude-code作为规范源仓库。 - 在迁移期间保留现有
install.sh流程。 - 从同一规划器支持 home 作用域和项目作用域目标。
- 让 uninstall/repair/doctor 无需猜测即可实现。
- 避免每个目标的复制逻辑泄漏回模块定义。
- 让未来 Codex 和 OpenCode 支持是增量的,而不是重写。
规范产物¶
1. 模块目录¶
模块目录是规范内容图。
当前已实现的字段:
idkinddescriptionpathstargetsdependenciesdefaultInstallcoststability
ECC 2.0 仍需要的字段:
installStrategy例如copy、flatten-rules、generate、merge-configownershipECC 是否完全拥有目标路径,还是只拥有其下生成的文件pathMode例如preserve、flatten、target-templateconflicts不能在同一目标中共存的模块或路径族publish模块是否默认打包、可选,或在安装后生成
建议的未来形态:
{
"id": "hooks-runtime",
"kind": "hooks",
"paths": ["hooks", "scripts/hooks"],
"targets": ["claude", "cursor", "opencode"],
"dependencies": [],
"installStrategy": "copy",
"pathMode": "preserve",
"ownership": "managed",
"defaultInstall": true,
"cost": "medium",
"stability": "stable"
}
2. 配置目录¶
配置保持精简。
它们应表达用户意图,而不是重复目标逻辑。
当前已实现的示例:
coredevelopersecurityresearchfull
仍需要的字段:
defaultTargetsrecommendedForexcludesrequiresConfirmation
这让 ECC 2.0 可以表达类似以下内容:
developer是 Claude 和 Cursor 的推荐默认research对于狭窄的本地安装可能较重full被允许但不是默认
3. 目标适配器¶
这是主要缺失的层。
模块图不应知道:
- Claude home 位于哪里
- Cursor 如何扁平化或重映射内容
- 哪些配置文件需要合并语义,而不是盲目复制
这属于目标适配器。
建议的接口:
type InstallTargetAdapter = {
id: string;
kind: "home" | "project";
supports(target: string): boolean;
resolveRoot(input?: string): Promise<string>;
planOperations(input: InstallOperationInput): Promise<InstallOperation[]>;
validate?(input: InstallOperationInput): Promise<ValidationIssue[]>;
};
建议的首批适配器:
claude-home写入~/.claude/...cursor-project写入./.cursor/...antigravity-project写入./.agents/...codex-home后续opencode-home后续
这与 session-adapter 发现文档中已经提出的相同模式一致:先规范契约, 后 harness 特定适配器。
安装规划模型¶
当前 scripts/install-plan.js CLI 证明仓库可以将请求的模块解析为过滤后的模块集。
ECC 2.0 需要下一层:操作规划。
建议的阶段:
- 输入规范化
- 解析
--target - 解析
--profile - 解析
--modules - 可选地转换旧语言参数
- 模块解析
- 展开依赖
- 拒绝冲突
- 按支持的目标过滤
- 适配器规划
- 解析目标根
- 推导精确的复制或生成操作
- 识别配置合并和目标重映射
- dry-run 输出
- 显示已选模块
- 显示跳过模块
- 显示精确文件操作
- 变更
- 执行操作计划
- 状态写入
- 仅在成功完成后持久化 install-state
建议的操作形态:
{
"kind": "copy",
"moduleId": "rules-core",
"source": "rules/common/coding-style.md",
"destination": "/Users/example/.claude/rules/ecc/common/coding-style.md",
"ownership": "managed",
"overwritePolicy": "replace"
}
其他操作类型:
copycopy-treeflatten-copyrender-templatemerge-jsonmerge-jsoncmkdirremove
安装状态契约¶
安装状态是 ECC 1.x 缺失的持久化契约。
建议的路径约定:
- Claude 目标:
~/.claude/ecc/install-state.json - Cursor 目标:
./.cursor/ecc-install-state.json - Antigravity 目标:
./.agents/ecc-install-state.json - 未来的 Codex 目标:
~/.codex/ecc-install-state.json
建议的载荷:
{
"schemaVersion": "ecc.install.v1",
"installedAt": "2026-03-13T00:00:00Z",
"lastValidatedAt": "2026-03-13T00:00:00Z",
"target": {
"id": "claude-home",
"root": "/Users/example/.claude"
},
"request": {
"profile": "developer",
"modules": ["orchestration"],
"legacyLanguages": ["typescript", "python"]
},
"resolution": {
"selectedModules": [
"rules-core",
"agents-core",
"commands-core",
"hooks-runtime",
"platform-configs",
"workflow-quality",
"framework-language",
"database",
"orchestration"
],
"skippedModules": []
},
"source": {
"repoVersion": "2.2.2",
"repoCommit": "git-sha",
"manifestVersion": 1
},
"operations": [
{
"kind": "copy",
"moduleId": "rules-core",
"destination": "/Users/example/.claude/rules/ecc/common/coding-style.md",
"digest": "sha256:..."
}
]
}
状态要求:
- 足够详细,以便 uninstall 仅移除 ECC 管理的输出
- 足够详细,以便 repair 比较期望状态与实际安装文件
- 足够详细,以便 doctor 解释漂移而不是猜测
生命周期命令¶
以下命令是安装状态的生命周期接口:
ecc list-installedecc uninstallecc doctorecc repair
当前实现状态:
ecc list-installed路由到node scripts/list-installed.jsecc uninstall路由到node scripts/uninstall.jsecc doctor路由到node scripts/doctor.jsecc repair路由到node scripts/repair.js- 迁移期间,旧版脚本入口点仍可用
list-installed¶
职责:
- 显示目标 id 和根目录
- 显示请求的 profile/模块
- 显示已解析的模块
- 显示源版本和安装时间
uninstall¶
职责:
- 加载安装状态
- 仅移除状态中记录的 ECC 管理目标
- 保留用户创建的不相关文件
- 仅在成功清理后删除安装状态
doctor¶
职责:
- 检测缺失的管理文件
- 检测意外的配置漂移
- 检测不再存在的目标根目录
- 检测清单/版本不匹配
repair¶
职责:
- 从安装状态重建期望的操作计划
- 重新复制缺失或漂移的管理文件
- 如果请求的模块不再存在于当前清单中,则拒绝 repair, 除非存在兼容映射
旧版兼容层¶
当前 install.sh 接受:
--target <claude|cursor|antigravity>- 语言名称列表
该行为不能一次性移除,因为用户已经依赖它。
ECC 2.0 应将旧版语言参数转换为兼容性请求。
建议方法:
- 为旧版模式保留现有 CLI 形态
- 将语言名称映射到模块请求,例如:
rules-core- 与目标兼容的规则子集
- 即使对于旧版安装也写入安装状态
- 将请求标记为
legacyMode: true
示例:
这既保留了旧行为可用,又将所有安装迁移到同一状态契约。
发布边界¶
当前 npm 包仍通过 package.json 发布较宽的载荷。
ECC 2.0 应谨慎改进这一点。
推荐顺序:
- 先保留一个规范的 npm 包
- 在更改发布形态之前,先用清单驱动安装时选择
- 之后仅在安全时考虑缩减打包范围
原因:
- 选择性安装可以先于激进的包改造发布
- uninstall 和 repair 更依赖安装状态,而不是发布变更
- 如果包源保持统一,Codex/OpenCode 支持更容易
可能的后续方向:
- 按 profile 生成精简包
- 生成目标特定的 tarball
- 可选远程获取重型模块
这些属于阶段 3 或之后,不是支持 profile 的安装的前置条件。
文件布局建议¶
建议的后续文件:
scripts/lib/install-targets/
claude-home.js
cursor-project.js
antigravity-project.js
registry.js
scripts/lib/install-state.js
scripts/ecc.js
scripts/install-apply.js
scripts/list-installed.js
scripts/uninstall.js
scripts/doctor.js
scripts/repair.js
tests/lib/install-targets.test.js
tests/lib/install-state.test.js
tests/lib/install-lifecycle.test.js
install.sh 在迁移期间可以保留为面向用户的入口点,但它应成为围绕基于 Node 的规划器和执行器的薄壳,而不是继续按目标增加 shell 分支。
实施顺序¶
阶段 1:规划器到契约¶
- 保留当前清单 schema 和解析器
- 在已解析模块之上添加操作规划
- 定义
ecc.install.v1状态 schema - 在成功安装时写入安装状态
阶段 2:目标适配器¶
- 将 Claude 安装行为提取到
claude-home适配器 - 将 Cursor 安装行为提取到
cursor-project适配器 - 将 Antigravity 安装行为提取到
antigravity-project适配器 - 将
install.sh缩减为参数解析加适配器调用
阶段 3:生命周期¶
- 添加更强的目标特定合并/移除语义
- 扩展 repair/uninstall 对非 copy 操作的覆盖
- 将包发布范围缩减为模块图,而不是宽泛文件夹
- 决定何时将
ecc-install变为ecc install的薄别名
阶段 4:发布与未来目标¶
- 评估安全缩减
package.json发布范围 - 添加
codex-home - 添加
opencode-home - 如果打包压力仍然很高,考虑生成配置捆绑包
仓库本地即时后续步骤¶
本仓库中信号最强的下一步实现动作是:
- 为配置类模块添加针对目标的合并/移除语义
- 将 repair 和 uninstall 扩展到简单复制文件操作之外
- 将包发布范围缩减为模块图,而不是宽泛的文件夹
- 决定
ecc-install是否保持独立,还是变为ecc install - 添加用于锁定以下行为的测试:
- 针对目标的合并/移除行为
- 非复制操作的 repair 和 uninstall 安全性
- 统一的
eccCLI 路由和兼容性保证
开放问题¶
- 规则是否应在遗留模式下永远保持可按语言寻址,还是仅在迁移窗口期间保持?
platform-configs是否应始终随core一起安装,还是拆分为更小的针对目标的模块?- 我们是否希望配置合并语义记录在操作级别,还是仅记录在适配器逻辑中?
- 重量级技能家族最终是否应迁移到按需获取,而不是在打包时包含?
- Codex 和 OpenCode 目标适配器是否应仅在 Claude/Cursor 生命周期命令稳定后才发布?
建议¶
将当前 manifest 解析器视为安装的适配器 0:
- 保留当前安装面
- 将实际复制行为移到目标适配器之后
- 为每次成功安装写入安装状态
- 使 uninstall、doctor 和 repair 仅依赖安装状态
- 然后才缩减打包或添加更多目标
这是从 ECC 1.x 安装器蔓延到 ECC 2.0 安装/控制契约的最短路径,该契约是确定性的、可支持的,并且可扩展的。
本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 ev/ECC