开源 Agent 套件 ECC:281 个技能的放置规则与检索姿势
本文基于 ECC 仓库 commit 591ab5c(2026-07-29)梳理,该项目仍在高频迭代,具体行为以仓库 https://github.com/affaan-m/ECC 最新代码与文档为准。
在 ECC 里检索技能,最容易翻车的不是关键词没想对,而是你默认「所有技能都在同一个目录下」——这个前提从一开始就是错的。 仓库里的 skills/ 只装精选技能,另外还有三类技能压根不进仓库、也不会被 CI 看到,它们躺在你自己的用户目录里。搞不清这个分层,你会反复遇到两种尴尬:别人机器上跑得好好的技能,你在仓库里怎么翻都没有;或者你确认「ECC 没这个能力」,结果它一直在,只是名字被改过。
ECC(Everything Claude Code)是 MIT 许可证的开源项目,仓库地址 https://github.com/affaan-m/ECC 。它不是一个 Agent,而是一层装在编码 Agent 之上的工程增强件:agents/ 下 67 个 agent 定义、skills/ 下 281 个技能、commands/ 下 94 个命令,外加 rules、hooks、安装清单和一堆校验脚本。281 这个量级意味着,检索本身就成了一项需要方法的操作。
站内此前写过 Claude Code 技能机制 和 MCP 扩展框架的选型,那两篇讲的是通用方法论——技能这种东西该怎么设计、什么能力该放进协议层;这一篇不谈方法论,只拆一个真实项目:ECC 把这套东西落地成了什么样的目录规则、校验脚本和检索路径,以及它为此付出了什么代价。
一、技能不在一个地方:四类放置与它们的可见性
ECC 有一份专门的文档 docs/SKILL-PLACEMENT-POLICY.md 来定义这件事。它把技能分成四类,每类有各自的根路径,是否随发布分发、是否需要出处元数据都不一样。
精选技能(Curated)住在仓库的 skills/<skill-name>/,根目录下必须有 SKILL.md。这类是唯一会被安装清单引用、会被复制到你机器上的。它们不需要出处文件,靠 SKILL.md frontmatter 里的 origin 字段标注来源(值如 ECC、community)。
学习产出的技能(Learned)住在 ~/.claude/skills/learned/<skill-name>/,由 continuous-learning 机制产出——evaluate-session 钩子和 /learn 命令会往这里写。这个默认路径不是写死在代码里的,而是 skills/continuous-learning/config.json 里的 learned_skills_path 键;同一份配置里还管着「提炼出的技能要不要自动批准」这类开关。也就是说,别人机器上的 learned 技能到底躺在哪,取决于他改没改过这个键,你不能假设一定是默认位置。
导入的技能(Imported)住在 ~/.claude/skills/imported/<skill-name>/,是用户从外部来源装进来的。文档里写得很直白:目前没有自动导入器,放置靠约定。
演化技能(Evolved)走的是另一套系统,位置在 ~/.claude/homunculus/evolved/skills/(全局)或 ~/.claude/homunculus/projects/<hash>/evolved/skills/(按项目),由 instinct-cli evolve 从聚类后的 instinct 生成,出处从来源 instinct 继承。
后三类的共同点:不进仓库、不随发布分发。learned 和 imported 还额外强制要求在技能目录里放一个 .provenance.json,与 SKILL.md 同级,必填 source、created_at、confidence、author 四个字段,schema 在 schemas/provenance.schema.json,校验函数在 scripts/lib/skill-evolution/provenance.js 的 validateProvenance。
| 组成部分 | 它负责什么 | 仓库/磁盘位置 | 你什么时候会碰到它 |
|---|---|---|---|
| 精选技能 | 唯一被分发的技能本体 | skills/<name>/SKILL.md | 检索、安装、提 PR 时 |
| 学习产出技能 | 会话里提炼出的本地技能 | ~/.claude/skills/learned/<name>/ | 用过 /learn 之后 |
| 导入技能 | 外部来源手动放进来的 | ~/.claude/skills/imported/<name>/ | 抄别人配置时 |
| 演化技能 | 从 instinct 聚类生成 | ~/.claude/homunculus/evolved/skills/ | 开了持续学习 v2 才有 |
| 出处元数据 | 标注来源、时间、置信度、作者 | schemas/provenance.schema.json | 排查某个技能哪来的 |
| 精选技能校验 | 只查仓库里的精选技能 | scripts/ci/validate-skills.js | CI 报警或提 PR 时 |
| 清单核对 | 数 agents/skills/commands 并核对文档 | scripts/ci/catalog.js | 想拿准确清单时 |
| 安装清单校验 | 确认 modules 里的 paths 都存在 | scripts/ci/validate-install-manifests.js | 改安装模块时 |
| 健康探测 | 探 learned/imported 根目录的状态 | scripts/skills-health.js | 想知道本机装了什么 |
| 导航技能 | 教 Agent 怎么现读仓库回答问题 | skills/ecc-guide/SKILL.md | 问「ECC 有什么」时 |
| 搜索前置技能 | 建新技能前先搜本地和远端 | skills/skill-scout/SKILL.md | 准备造新技能时 |
| 工作流编组 | 把命令按前缀归族并给运行顺序 | skills/ecc-recipes/SKILL.md | 问「跑哪串命令」时 |
这张表最该记住的是第一列和第三列的对应关系。你搜不到某个技能时,第一个要问的不是「关键词对不对」,而是「我搜的这个根路径,是不是它该在的那个根路径」。
二、命名有规律,但别把命名当索引
翻一遍 skills/ 目录,命名规律不难看出来:全小写、连字符分词。docs/SKILL-DEVELOPMENT-GUIDE.md 的 frontmatter 字段表里对 name 的要求写得很明确——小写、带连字符的标识符,例子给的是 react-patterns。
粒度上,这份指南给了一组对照:react-hook-patterns 好于 react,postgresql-indexing 好于 databases,pytest-fixtures 好于 python-testing,nextjs-app-router 好于 nextjs。一个技能对应一个域或一个概念,宽到一个词就能概括的名字,通常意味着这个技能没写好。
目录里能看到几组成建制的命名族。框架类是最整齐的,四个后缀成套复用:django-patterns、django-security、django-tdd、django-verification 是一组,laravel-*、springboot-*、quarkus-* 各自也齐着这同样的四件。但成套不等于只有这四件——django 那组还多一个 django-celery,laravel 那组还多一个 laravel-plugin-discovery。这正是靠前缀猜名字的甜头和陷阱同时出现的地方:猜得中骨架,猜不中挂在骨架外面的那些。编排类以 orch- 打头:orch-add-feature、orch-build-mvp、orch-change-feature、orch-fix-defect、orch-pipeline、orch-refine-code。家庭实验室网络的一组以 homelab- 打头。另有一组以 ito- 打头,围绕 Itô 这个主题展开——预测市场篮子对比、行情研究、算力询价、交易规划工作表都在里面,看名字前缀完全猜不出它们各管一段。技能治理自己也占了三个:skill-scout、skill-stocktake、skill-comply。
按前缀猜名字确实能命中一部分。但这里有个坑:ECC 有一份 docs/skill-adaptation-policy.md,专门规定从外部仓库、prompt 包、插件、个人配置搬过来的东西要「变成 ECC 原生的界面」。什么时候保留原名、什么时候改名,它列了条件——ECC 实质性扩展、收窄或重新打包了原作时就要改名;原名偏厂商或社区品牌导向、而不是工作流导向时也要改名。文档里给的例子是:可复用的图排序原语保留为 social-graph-ranker,而更宽的工作流层则叫 lead-intelligence 或 connections-optimizer。
对检索者的含义很直接:你脑子里那个上游项目的名字,很可能在 ECC 里已经不叫那个名字了。 靠目录名做全部检索,漏的就是这一类。
真正承担「被找到」这个职责的是 frontmatter 里的 description。开发指南的字段表里写它是「用于技能列表和自动激活的一行描述」——也就是说,它既是给人看的检索文本,也是 Agent 判断该不该加载这个技能的依据。写得好不好,直接决定这个技能是不是「事实上不存在」。
看两个真实的写法。skills/skill-scout/SKILL.md 是紧凑型:
---
name: skill-scout
description: Search existing local, marketplace, GitHub, and web skill sources before creating a new skill. Use when the user wants to create, build, fork, or find a skill for a workflow.
metadata:
origin: community
---
skills/ecc-recipes/SKILL.md 则把触发条件和反触发条件都塞进了 description,明确写出 TRIGGER 和 DO NOT TRIGGER 两段——什么时候该用它、什么时候该改用 ecc-guide 或 prompt-optimizer。skills/prompt-optimizer/SKILL.md 更进一步,连中文触发词和容易混淆的中文反例都列进去了。
顺带一个检索时会绊人的细节:origin 这个字段在仓库里有两种写法。skills/ecc-recipes/SKILL.md 写在顶层(origin: community),skills/skill-scout/SKILL.md 和 skills/ecc-guide/SKILL.md 写在 metadata 下面(缩进的 origin: community)。你要按来源做筛选,正则得同时覆盖这两种缩进。
三、检索的顺序:先仓库、再清单、再本机
skills/ecc-guide/SKILL.md 把它的核心原则写在了显眼位置:Answer from current files, not memory——从当前文件回答,不要凭记忆。它给的理由是 ECC 变化很快,写死的目录数量、功能列表和安装说明都会过期。这条原则同样适用于你这个人类读者。
这份技能列出的现读命令是:
node scripts/ci/catalog.js --json
find skills -maxdepth 2 -name SKILL.md | sort
find commands -maxdepth 1 -name '*.md' | sort
find agents -maxdepth 1 -name '*.md' | sort
node scripts/install-plan.js --list-profiles
node scripts/install-plan.js --list-components --json
后面紧跟一句限制:用满足问题所需的最小读取集。这不是客气话——281 个技能的 SKILL.md 全读进上下文,代价你能算得出来。关于这类预算问题,可以另看 Agent 上下文预算怎么定。
按内容检索,ecc-guide 给的是三个目录一起搜:
rg -n "<query>" skills commands agents docs
三个目录一起搜的必要性在于,ECC 的能力不只落在技能上。docs/capability-surface-selection.md 是一份路由指南,专门规定一个能力该做成 rule、skill、MCP 服务器还是普通 CLI/API。它的判断顺序是:只要路径或事件匹配就该无条件发生、不需要模型判断的,做成 rule;主要是剧本、工作流、只在需要时才该加载的顾问层,做成 skill;需要结构化交互接口、多个 harness 反复调用的,做 MCP;能当脚本跑完、不需要常驻服务的,做本地 CLI 再用技能包一层。
也就是说,你要找的那个能力,有可能根本就没被设计成技能。只搜 skills/ 是会漏的。
本机这一层的搜法在 skills/skill-scout/SKILL.md 里。这个技能的定位是「造新技能之前先搜一遍」,它给的顺序是先搜本地、再搜远端,理由是本地来源已经在用户环境里了。它先要求提取意图——技能要干什么、触发条件、涉及的域和工具、三到五个搜索关键词加同义词——然后才动手搜:
grep -RilE "keyword|synonym" ~/.claude/skills ~/.claude/plugins/marketplaces 2>/dev/null
注意它搜的是 frontmatter 描述文本,不是目录名。这和上一节的结论是一致的。
最后一层是安装清单。manifests/ 下有 install-modules.json、install-profiles.json、install-components.json 三份。modules 是安装单元,每个带 id、kind、paths、targets、dependencies、defaultInstall 等字段;profiles 是模块组合,比如 minimal 由 rules-core、agents-core、commands-core、platform-configs、workflow-quality 组成;components 是面向用户的可选项,id 形如 baseline:rules、lang:typescript,各自映射到一组 modules。
这一层决定的是「仓库里有」到「你机器上有」之间的落差。README 里明确写了可以按名字装指定技能:
./install.sh --target claude --skills tdd-workflow,security-review
你选了什么 profile、装了哪些 components,直接决定你本机 ~/.claude/skills/ 下有什么。仓库有、本机没有,是完全正常的状态,不是 bug。
四、找不到的时候,怎么判断是真没有
把上面三节合起来,得到一个可执行的判断链。
第一步,确认你搜的是哪个根。 仓库的 skills/ 只覆盖精选技能。别人给你演示的技能,如果目录里有 .provenance.json,那它是 learned 或 imported,不在仓库里,你翻到天亮也找不到。
第二步,别把「没报错」当成「不存在」。 放置策略文档里明确写了:scripts/skills-health.js、scripts/lib/skill-evolution/health.js 和会话钩子会去探 ~/.claude/skills/learned 和 ~/.claude/skills/imported,目录不存在就当作空,不报错。安静地返回空集,和确认没有,是两件事。
第三步,换搜法而不是换关键词。 目录名搜不到,就搜 description;skills/ 搜不到,就把 commands/、agents/、docs/ 一起带上;英文词搜不到,试试上游项目的原名——因为改名政策的存在,反过来也成立。
第四步,确认它是不是被设计成了别的东西。 按 docs/capability-surface-selection.md 的路由逻辑,一个「每次匹配都要发生、不需要模型判断」的能力会落在 rules/;一个「跑一次就完」的动作会落在 scripts/ 里。你按技能去找一个规则,注定找不到。
第五步,才是下结论。 ecc-guide 在「Avoid」清单里专门列了一条:不要在没检查文件系统的情况下声称某个组件存在。这条反过来同样成立——没把上面四步走完,也别声称它不存在。
五、边界与代价:这套设计放弃了什么
校验只保结构,不保质量。 读一遍 scripts/ci/validate-skills.js 就清楚它到底查什么:每个子目录里有没有 SKILL.md、文件是不是非空、frontmatter 里有没有 name、description 是不是用了会保留内部换行的字面量块标量(| / |- / |+,会把按 description 取值的平铺表渲染器搞坏)。缺失或空文件永远是错误,frontmatter 相关的发现默认只是 WARN,加 --strict 或设 CI_STRICT_SKILLS=1 才升级成错误退出。
这意味着:CI 绿不代表这 281 个技能的描述都写得能被搜到。 描述是否名副其实、内容是否过期、示例是否还能跑,校验脚本一概不管。检索质量最终仍然压在写技能的人身上。
分发范围换来了可控性,代价是能见度。 learned、imported、evolved 三类都不进仓库、不进安装清单、不被 CI 看到。好处是别人的会话产物不会污染你的仓库;代价是这三类技能没有任何集中的目录可查,出处元数据里的 confidence 是个 0 到 1 的数,谁写的谁定,它不构成外部背书。
它会往你机器里写东西。 这是一整套安装系统:技能、agent、命令、hooks 运行时、平台配置、安装状态文件,都会落到你的用户目录或项目目录。README 里有一条相当重的警告——不要叠装。同一个 harness 装两遍 ECC,可能重复出技能、命令、hooks 或配置;装到多个不同 harness 则不会。另一条同样重要:Claude 是从 ~/.claude/skills/ 的直接子目录发现技能的,手动安装不要嵌套到 ~/.claude/skills/ecc/ 下面。
它明确不管的事。 它不保证技能里写的技术内容是对的或最新的——ecc-guide 的整个设计前提就是「内容会过期,所以现读文件」。它也不保证 Agent 真的会照技能说的做:这是另一个技能 skills/skill-comply/SKILL.md 在管的事,它通过跑 claude -p 抓取工具调用轨迹来测量合规率——注意这条路要真实调用模型,它自己的文档里就带了 --dry-run 选项说明「不产生费用,只出规格和场景」。各家模型服务商的计费与限制规则不同且会调整,以官方最新说明为准。
大部分技能它自己也没法替你判断该不该用。 skills/ecc-recipes/SKILL.md 在 Non-Goals 里写了四条,其中一条是「advisory only」——只给建议,不执行。它的输出模板里甚至专门带了一段自主循环的警告:无边界的循环会烧订阅或额度,让你在完成信号之外再加一个最大迭代或最大成本的兜底。这种坦白值得留意,它没把自动化说成没有成本的东西。
六、上手与避坑清单
别按记忆背清单。 会踩是因为 281 这个数字本身在变,你上次记住的目录下周就不准了。避法是把 node scripts/ci/catalog.js --json 当成唯一权威——这个脚本干的事就是数 agents/*.md、commands/*.md、skills/*/SKILL.md 并和文档里写的数字核对。
别只搜目录名。 会踩是因为改名政策的存在,上游名字在 ECC 里可能已经变了。避法是 description 和目录名两条路都走,rg -n "<query>" skills commands agents docs 一次覆盖四个目录。
别把技能装进 ~/.claude/skills/ecc/。 会踩是因为「归归类更整齐」这个直觉。避法是记住 Claude 只认 ~/.claude/skills/ 的直接子目录,嵌套一层就等于这个技能在磁盘上存在但永远不会被发现——这类问题最难查,因为文件明明就在那儿。
别同时用插件安装和手动/profile 安装。 会踩是因为两条路各自都能跑通,看不出冲突。避法是选一条,README 里对重复面的警告是明写的。
先跑 dry-run 再 apply。 会踩是因为安装会动你现有的配置文件。避法是用 node scripts/install-apply.js --profile minimal --target claude --dry-run 这类带 --dry-run 的路径先看清楚会改哪些文件,ecc-guide 的安装计划输出模板里专门有一行「Would change:
写新技能之前先搜一遍。 会踩是因为 281 个技能里重名或重功能的概率不低,而你搜的关键词往往只是自己的说法。避法是照 skill-scout 的顺序来:先提取意图和三到五个关键词加同义词,先搜本地和 marketplace,再搜远端。
description 别写成块标量。 会踩是因为描述长了就想换行,YAML 里顺手打个 |。避法是用行内字符串或折叠式的 >——validate-skills 会 WARN 这件事,但默认不会让 CI 失败,容易被忽略过去。
要按来源筛选时,正则覆盖两种缩进。 会踩是因为 origin 在仓库里既出现在 frontmatter 顶层,也出现在 metadata 下面,只匹配一种会漏掉一半。
别拿 skill-comply 当检索工具。 会踩是因为它名字里带 skill,看着像个查询命令。它实际是跑真实模型来测合规率的,不 dry-run 就会产生实际调用。想找技能,用 ecc-guide 或 skill-scout。
收束:三个文件的阅读顺序
如果你只打算读三个文件再动手,顺序建议是这样:
先读 docs/SKILL-PLACEMENT-POLICY.md,它决定你该去哪个根路径找东西,也决定你该不该把某个技能提交进仓库。再读 skills/ecc-guide/SKILL.md,把里面那组现读命令抄进你自己的备忘,它是这套系统里唯一一份「不要凭记忆」的操作规程。最后读 docs/SKILL-DEVELOPMENT-GUIDE.md 的 frontmatter 字段表和「Step 1: Choose a Focus」那一节——理解了 description 是怎么承担自动激活职责的,你对「为什么这个技能搜不到」的判断会准很多。
一份可以直接用的自检清单:搜之前先确认根路径;搜的时候目录名和 description 两条路都走;skills/、commands/、agents/、docs/ 一起带上;确认这个能力有没有可能被设计成 rule 或脚本;确认本机装了哪个 profile;以上都做完,才有资格说「它没有」。
工具选型上的通用判断可以参考 AI 工具选型流程——ECC 这类套件的引入决策,本质上和引入任何一个会往你机器里写文件的开发工具是同一类问题:先看它改动了什么,再看它承诺了什么。
本文属于 ECC 开源 Agent 套件专题(共 40 篇,从选择性安装一直拆到内部机制)。同一批还写了另一种取向的项目——不给你资产、只给你纪律的 superpowers 方法论专题。