【免费下载链接】cindy
Consider it done. The open-source AI agent that works out of the box · 想到,就能做到。开源、开箱即用的 AI Agent。
Cindy 桌面端把若干"官方 Skill"作为应用资源随安装包发布,并在启动准备阶段把它们物化(materialize)到用户数据目录,供本地 Claude Code、Codex、Pi 三种 Harness 共同发现与加载。本篇以 内置 Skill 说明文档 为主线,完整继承其中关于来源、许可证、vendored 依赖与写入边界的约定,并结合 built-in-skills.ts、built-in-skill-activation.ts 与配套测试,讲清"随包资源 → 内容寻址版本目录 →.active指针 → Agent 私有投影"这条链路,以及如何正确使用cindy-skill-creator的初始化、元数据生成与校验脚本。
一、这份 README 到底定义了什么
apps/desktop/resources/system-skills/README.md 全文只有十余行,却是这个目录的"权属声明"与"运行约定",其核心信息可以拆成五条:
cindy-skill-creator是改编自 OpenAI Codex 的上游资产,固定在上游 commit977193486dfe7a88c4dab24abeafe9b754f5b13f的codex-rs/skills/src/assets/samples/skill-creator路径下。- 许可证为 Apache-2.0,且许可证文本必须"就地保留"在
cindy-skill-creator/license.txt,与资源相邻。 - 校验与元数据生成脚本内嵌了 PyYAML 6.0.3 的纯 Python 模块,因此无需用户额外
pip install;上游 MIT 许可证与版本/来源记录放在cindy-skill-creator/scripts/_vendor/。 learn由 Cindy 自研自维护,它通过必需的cindy_helper桥启动 Cindy 托管的 Learn Host;其他客户端即使能发现这些 Skill 文件,缺少该宿主能力也无法跑通工作流。- 这些资源会在启动时被物化到用户数据目录,禁止把用户自建 Skill 放进这个目录。
最后一条是整篇文档里最容易踩坑的约束,后文会结合源码说明它为什么是一条硬性架构边界,而不是一句风格建议。
二、目录实况:不只是两个 Skill
README 直接点名了两个 Skill,但resources/system-skills/的实际内容更完整。当前仓库目录如下:
apps/desktop/resources/system-skills/ |-- README.md 目录权属与来源说明 |-- cindy-skill-creator/ 改编自 Codex,Apache-2.0 | |-- SKILL.md 入口:创建/编辑/搜索/发布 Skill | |-- license.txt 上游 Apache-2.0 文本 | |-- agents/openai.yaml UI 元数据 | |-- assets/ 图标等生成物素材 | |-- references/ openai.yaml 字段说明、SkillHub 发布流程 | `-- scripts/ init_skill.py / quick_validate.py / | generate_openai_yaml.py / _frontmatter.py | `-- _vendor/yaml/ PyYAML 6.0.3 纯 Python 运行时 |-- learn/ Cindy 自研 | |-- SKILL.md | `-- agents/openai.yaml `-- cindy-plugin-creator/ Cindy 自研(bundle 13 起随包) |-- SKILL.md |-- agents/openai.yaml `-- references/ 37 份插件作者契约分册cindy-plugin-creator的出现有明确的时间线证据:credentials-and-local-storage.md 中的"Cindy 托管的预装技能"一节写明它"从内置 bundle 13 起提供",并说明核验旧 bundle 时只对这一个尚未引入的技能容许缺席。源码中的常量把这条规则固化了:
// apps/desktop/src/main/maker-host/built-in-skills.ts const BUILT_IN_SKILL_MUTATION_WAIT_MS = 5_000; /** Increment whenever shipped built-in Skill bytes change between releases. */ export const BUILT_IN_SKILLS_BUNDLE_VERSION = 19; const PLUGIN_CREATOR_FIRST_BUNDLE_VERSION = 13;对应的白名单与名称常量集中在 cindyBuiltInSkills.ts:
export const CINDY_SKILL_CREATOR_NAME = 'cindy-skill-creator'; export const CINDY_LEARN_NAME = 'learn'; export const CINDY_PLUGIN_CREATOR_NAME = 'cindy-plugin-creator'; export const CINDY_BUILT_IN_SKILL_NAMES = [ CINDY_SKILL_CREATOR_NAME, CINDY_LEARN_NAME, CINDY_PLUGIN_CREATOR_NAME, ] as const;也就是说:README 描述的是"来源与许可"层面的事实,而"到底有哪些内置 Skill、它们叫什么"由这份共享常量与BUILT_IN_SKILLS_BUNDLE_VERSION共同决定。更新随包 Skill 的字节时必须递增 bundle 版本号,否则会在下文所述的"同版本不同字节"检查中被拒绝。
三、Skill 的标准解剖结构
cindy-skill-creator的入口文件 SKILL.md 给出了一个可直接照抄的目录模型:
skill-name/ |-- SKILL.md Required skill instructions | |-- YAML frontmatter Required name and description | `-- Markdown body Instructions loaded when the skill is used |-- agents/ Optional UI metadata and invocation policy | `-- openai.yaml |-- scripts/ Optional executable helpers |-- references/ Optional documentation loaded as needed `-- assets/ Optional files used in generated output四个可选目录各有清晰分工,文档要求"没有明确用途就不要创建目录、占位符或辅助文档":
| 目录 | 放什么 | 典型用法 | 反例 |
|---|---|---|---|
scripts/ | 可执行助手 | 重复的文件转换、可靠的 API 操作、数据处理 | 把一次性的临时命令固化成脚本 |
references/ | 仅在特定上下文需要加载的文档 | Schema、公司策略、API 文档、按交付物区分写作指南 | 抄一份上游手册或通用教程 |
assets/ | 进入生成结果、而不是进入模型指令的文件 | 模板、图片、字体、图标、脚手架工程 | 把素材当指令加载进上下文 |
agents/ | 面向机器/宿主读取的产品级配置 | display_name、short_description、default_prompt、调用策略 | 手改无关字段导致策略被覆盖 |
SKILL.md的 frontmatter 只强制两个字段:name与description。校验器额外允许license、allowed-tools、metadata三个键(见 quick_validate.py 的allowed_properties),出现其他键会直接判定失败。
三阶段渐进式披露
文档反复强调"便宜且精确的发现成本",并把信息披露切成三级:
- 名称与描述:在 Skill 选择阶段就可见,因此必须简短、有区分度,避免"万能 catchall"把无关请求吸引过来。
SKILL.md正文:仅在该 Skill 被采用时加载,只放目的、必要流程、真实约束与索引。- 支撑资源:
references/与scripts/只在当前任务真正需要时读取或执行。
一个多模式 Skill 的推荐组织方式(文档原文示例)是入口只保留模式选择,把各模式细节拆到并列参考文件:
cloud-deploy/ |-- SKILL.md `-- references/ |-- aws.md |-- gcp.md `-- azure.md用户选了 AWS,就只读references/aws.md,不要再把 GCP、Azure 一起塞进上下文。这条原则在仓库里有真实规模样本:cindy-plugin-creator的references/下有 37 份分册,而它的SKILL.md只做流程编排与"按需阅读索引",并在开头明确要求"不要一次加载整本手册"。
四、从安装包到用户目录:物化链路
4.1 打包:作为 extraResources 随包发布
forge.config.ts 把该目录列入 extraResources,并附了一句解释性注释:
// Cindy-owned Agent Skills are materialized under userData on startup. 'resources/system-skills',打包后资源位于process.resourcesPath/system-skills,开发态则位于appPath/resources/system-skills。这个分叉由 resolveBundledSystemSkillsRoot 统一处理:
export function resolveBundledSystemSkillsRoot(input: { isPackaged: boolean; appPath: string; resourcesPath: string; }): string { return input.isPackaged ? path.join(input.resourcesPath, 'system-skills') : path.join(input.appPath, 'resources', 'system-skills'); }调用点在 auth-adapters.ts 的runEnsureSharedGlobalSkills:它在"共享全局 Skill 投影变更"的持有者边界内先调用prepareBuiltInSkills,成功后再执行旧链接迁移与共享投影,任何 warning 都会以assetPrepLog.warn输出。
4.2 存储位置:跨 profile 的共享根
README 说"物化到用户数据目录",而当前实现会优先使用跨 profile 共享根:
// 旧路径:单个 profile 私有 export function builtInSkillsRoot(userDataDir: string): string { return path.join(userDataDir, 'system-skills'); } /** Profile-independent storage shared by Global, China, dev, and isolated profiles. */ export function sharedBuiltInSkillsRoot(appDataDir: string): string { return path.join(appDataDir, 'Cindy', 'shared-system-skills'); }传入appDataDir时用共享根,否则回退到userData/system-skills。这解释了 credentials-and-local-storage.md 中的登记:"app.getPath('appData')/Cindy/shared-system-skills,只保存随应用发布、可由 bundle 重建的官方 Skill;Global、China、dev 与 isolated profile 共用稳定物理路径"。
4.3 目录形态:内容寻址的版本目录 + 单一发布指针
物化后的布局(全部由源码行为推导,字段名可在实现中逐一对应):
<appData>/Cindy/shared-system-skills/ |-- .cindy-system-skills.json 物化清单(schemaVersion 3 / bundleVersion / fingerprints / activeBundle) |-- .versions/ | |-- v19-<16位摘要>-<uuid>/ 不可变的历史/当前 bundle | `-- ... `-- .active -> .versions/v19-...README 中"materialize"对应的正是这套结构,其关键设计点有三:
(1)按目录内容做 SHA-256 指纹。hashDirectory 递归遍历、按文件名排序,目录更新d\0<相对路径>\0、文件更新f\0<相对路径>\0加内容字节,最终取摘要。任何字节变化(包括新增空目录)都会改变指纹。
(2)bundle 名由"版本号 + 指纹摘要 + UUID"构成。materializedBundleName 对每个 Skill 名称与其指纹排序后共同摘要,取前 16 位,再拼接随机 UUID,因此同名同版本也不会错误复用旧目录,同时validActiveBundle用严格正则约束格式:
export function validActiveBundle(value: unknown): value is string { return typeof value === 'string' && /^v[1-9][0-9]*-[a-f0-9]{16}-[a-f0-9]{8}(?:-[a-f0-9]{4}){3}-[a-f0-9]{12}$/.test(value); }(3)所有 Agent 投影永远指向.active,发布靠一次指针替换完成。源码注释直接写明了意图:"Every managed projection permanently targets.active/<skill>. Preparing those links cannot split the live bundle because.activestill resolves to the previous version. The final pointer replacement publishes every Skill to every managed Agent together."(built-in-skills.ts)
4.4 提交顺序与失败回滚
prepareBuiltInSkills的发布事务(built-in-skills.ts)大致是:
- 在
.versions/.<bundle>.pending下逐个materializeSkill,先拷到.<name>.stage-<uuid>,校验指纹后再rename成正式目录,保留.backup-<uuid>以便回滚; - 整个 pending 目录
rename为已发布目录,并记录publishedDirectory = true; - 快照当前所有投影状态(symlink 指向、缺失、用户自有),再尝试把稳定描述符投影到
.active; - 写清单(
manifestAdvanced = true); switchActiveBundle以"先建.active.next-<uuid>,再替换.active"的方式切换指针;- 任一步失败:删除 pending;若清单已推进则写回旧清单并把
.active切回旧 bundle(旧清单是 schema 2 时删除指针);恢复投影快照;在恢复完整时才删除刚发布的目录。
最后返回的projectionSafe只在"清单非空 + 已提交 bundle 校验通过 + 每个描述符的现值指纹等于清单记录"时为true;为false时descriptors返回空数组,调用方拿不到任何"官方身份"。
4.5 拒绝降级与拒绝同版本换字节
两个容易被忽略但很关键的保护:
- 拒绝降级:若磁盘上的
manifest.bundleVersion大于当前构建携带的版本,直接保留旧 bundle 并给出 warningkept built-in Skill bundle <n>; this build only carries older bundle <m>。这意味着用户回滚到旧版 Cindy 不会把更新的内置 Skill 覆盖成旧字节。 - 拒绝同版本不同字节:当
manifest.bundleVersion === bundleVersion但记录指纹与随包指纹不一致时,warning 为kept built-in Skill <name> because bundle version <n> was reused for different bytes。这条规则让"改内容必须升 bundle 版本"从约定变成强制。
此外,committedSkillDescriptors专门处理历史差异:bundle 版本低于 13 且清单里没有该条目时,允许cindy-plugin-creator合法缺席,其余技能指纹与完整性要求不放宽。
五、三种 Harness 如何看到这些 Skill
README 说 learn 依赖宿主能力;实现层面则是"一个私有投影目录,多路加载"。
5.1 私有投影根
export function builtInSkillPluginRoot(userDataDir: string): string { return path.join(userDataDir, 'managed-agent-skills', 'cindy'); }每个描述符同时带两个路径:absolutePath(.active/<name>真实路径)和nativeClaudePath(managed-agent-skills/cindy/skills/<name>)。后者是投影,不是副本:ensureSkillEntry用symlink(Windows 用 junction)建立,并且只在目标确实是 Cindy 管理路径时才替换,否则返回built-in Skill <name> was not linked because <path> is already owned by the user——用户同名自建 Skill 不会被抢占。
5.2 加载方式
credentials-and-local-storage.md 的"Cindy 托管的预装技能"一节给出了三套 Harness 的对接方式:本地 Claude plugin 加载、Cindy 的CODEX_HOME/skills加载、Pi 通过显式--skill加载;并且明确"不再安装到~/.agents/skills或~/.claude/skills"。
历史遗留链接由migrateBuiltInGlobalSkillLinks清理,它只删除"经isCindyManagedSystemSkillTarget确认属于 Cindy 管理"的符号链接,覆盖~/.claude/skills/<name>、<userData>/claude-home/skills/<name>、~/.agents/skills/<name>三处,读到的链接在删除前还会二次比对,避免路径被替换后误删用户内容。Codex 侧的托管链接命名规则可在 codex-global-skills.ts 看到:cindy-${encodeURIComponent(command)},并且只识别shared-system-skills、ghost-install-state/skill-snapshots、managed-agent-skills/cindy这几类受管根。
5.3 信任来自路径,而不是名字
渲染进程拿到的 Skill 列表可能包含任意同名条目,因此主进程提供两级"认证":
export function markCindyBuiltInAgentSkills(skills, descriptors) { // 只有 skill.path 的 realpath 等于某个描述符的 <absolutePath>/SKILL.md 才打 builtIn = true } export function activeCindyBuiltInAgentSkills(skills, descriptors, isEnabled) { // 先认证,再只对 builtIn 条目应用启用/禁用覆盖 }注释写得非常直白:"names and descriptions are not trusted"。任何仅靠name === 'learn'判断官方身份的写法都会绕过这层校验。
六、启用状态与版本升级的稳定性
用户在 UI 里禁用某个内置 Skill,偏好要能在 bundle 从 v18 升到 v19 时继续生效。若直接记录绝对路径.versions/v19-xxx/learn,升级后键就变了。为此 built-in-skill-activation.ts 把"任意版本目录里的路径"归一化成稳定键:
export function canonicalBuiltInSkillActivationPath(source, userDataDir, appDataDir?) { // .active/<name> → <root>/<name> // .versions/<bundle>/<name> → <root>/<name> }只有名字在白名单内、且父目录确实是受管根时才会归一化;其他路径原样返回。这带来三个效果:
- 升级 bundle 后禁用状态继续有效;
- 同一份字节在多 profile 共享时只有一份偏好键;
- 外来目录下同名路径不会被误判成官方 Skill。
activationPreferences.ts 在此之上做了两件事:把禁用意图镜像到nativeClaudePath(仅在两者归一键相同、且投影未被用户接管时),并给 Learn 提供"列表读取用存储意图、执行读取用当前完整性"的两套判断,注释里明确"A retired profile copy must not veto an explicitly reenabled shared copy"。
七、为什么不能把用户 Skill 放进这个目录
README 最后一句"Do not put user-authored Skills here."在实现层面有四个互锁理由:
- 字节所有权:目录内容会被
hashDirectory计算指纹并与清单比对。用户新增或修改任何文件都会让指纹不匹配,builtInSkillDescriptors直接返回空数组——整个内置 Skill 集合"消失",而不是"多出一个"。 - 不可变性假设:版本目录被当作不可变历史(源码注释为 "immutable old bundle remains available throughout the transaction")。用户写入会被下一次发布事务的
.versions管理逻辑当作旧版本对待。 - 覆盖风险:物化时
materializeSkill会先rename旧目录为 backup、再把新目录 rename 到位,最后删除 backup。放在这里的自建内容不会被当作 Skill 保留。 - 升级即失效:目录名含 bundle 版本与 UUID,升级后新目录是全新的一份拷贝,原目录成为历史版本。
正确位置在cindy-skill-creator的SKILL.md中已经写明:需要项目级 Skill 时放<当前工作目录>/.agents/skills,否则放~/.agents/skills;这两个共享根让 Cindy 的本地 Claude Code、Codex、Pi 都能发现。新建或更新后的 Skill 由新的或被重启的 Agent 会话拾取。
八、实操:用 cindy-skill-creator 创建与校验 Skill
以下命令完整来自 SKILL.md,脚本本身位于同目录scripts/下,需以 Skill 目录为基准执行。Python 3 启动器的选择规则:macOS/Linux 用python3;Windows 用py -3,只有在确认python指向 Python 3 后才可用python。
8.1 初始化
# macOS/Linux python3 scripts/init_skill.py <skill-name> --path <output-directory> [--resources scripts,references,assets] [--examples] # Windows py -3 scripts/init_skill.py <skill-name> --path <output-directory> [--resources scripts,references,assets] [--examples]实际示例:
# macOS/Linux python3 scripts/init_skill.py my-skill --path "$HOME/.agents/skills" python3 scripts/init_skill.py my-skill --path "$HOME/.agents/skills" --resources references # Windows PowerShell py -3 scripts/init_skill.py my-skill --path "$HOME\.agents\skills" py -3 scripts/init_skill.py my-skill --path "$HOME\.agents\skills" --resources references行为说明(可在 init_skill.py 中核对):
- 创建 Skill 目录、一份简洁的
SKILL.md起始文件,以及agents/openai.yaml; - 只在显式请求时创建资源目录与示例文件;
--examples必须与--resources同时使用,否则报错; - 需要 UI 值时传
--interface key=value; - 名称会被规范化到 hyphen-case,长度上限 64;
- 不要对已有 Skill 重复初始化。
初始化生成的SKILL.md模板包含[TODO: ...]占位描述,必须替换后再校验。
8.2 生成或更新 agents/openai.yaml
# macOS/Linux python3 scripts/generate_openai_yaml.py <path/to/skill-folder> --interface key=value # Windows py -3 scripts/generate_openai_yaml.py <path/to/skill-folder> --interface key=value文档特别提醒:该生成器会整体替换文件。若已有文件含policy或dependencies,应就地只改目标字段,而不是重新生成。可选 interface 字段只在用户提供或要求时才填写。
字段全集与约束见 references/openai_yaml.md,其完整示例包含三类顶层键:
interface: display_name: "Optional user-facing name" short_description: "Optional user-facing description" icon_small: "./assets/small-400px.png" icon_large: "./assets/large-logo.svg" brand_color: "#3B82F6" default_prompt: "Optional surrounding prompt to use the skill with" dependencies: tools: - type: "mcp" value: "github" description: "GitHub MCP server" transport: "streamable_http" url: "https://api.githubcopilot.com/mcp/" policy: allow_implicit_invocation: true仓库里两个真实样例可以直接对照。cindy-skill-creator只声明 UI 元数据,因此保持默认的自动发现:
interface: display_name: "Cindy Skill Creator" short_description: "Create or update a Cindy Skill" icon_small: "./assets/skill-creator-small.svg" icon_large: "./assets/skill-creator.png"learn则显式关闭隐式调用:
interface: display_name: "Learn" short_description: "Distill a reusable Skill with Cindy" policy: allow_implicit_invocation: false文档对何时改这个默认值的口径很严格:只有用户明确要求"仅显式调用"时才改。保持可发现、把授权放在真正执行变更之前,不要因为"操作敏感"就偷偷关掉自动发现。关闭后 Skill 仍可在 Cindy 里用/skill-name显式触发,只是不再自动进入模型上下文。
8.3 校验
# macOS/Linux python3 scripts/quick_validate.py <path/to/skill-folder> # Windows py -3 scripts/quick_validate.py <path/to/skill-folder>校验器实际检查的内容(quick_validate.py):
| 检查项 | 规则 | 失败信息示例 |
|---|---|---|
SKILL.md存在 | 必须存在于传入目录 | SKILL.md not found |
| frontmatter | 文件以---开头,能被解析为 YAML 字典 | No YAML frontmatter found/Frontmatter must be a YAML dictionary |
| 键白名单 | 仅name、description、license、allowed-tools、metadata | Unexpected key(s) in SKILL.md frontmatter: ... |
| 必有字段 | name、description必须存在且为非空字符串 | Missing 'name' in frontmatter |
| 名称格式 | ^[a-z0-9-]+$,不得首尾连字符、不得连续连字符,长度 ≤ 64 | should be hyphen-case (...),Name is too long |
| 描述约束 | 非空、不以[TODO:开头、不含<与>、长度 ≤ 1024 | Description contains an unfinished TODO placeholder |
| 正文占位符 | 代码围栏之外不得残留整行[TODO: ...] | Skill instructions contain an unfinished TODO placeholder |
脚本还会设置sys.dont_write_bytecode = True,注释说明原因是"这些助手运行在 Cindy 内容寻址的内置 Skill bundle 中",不允许在不可变目录里生成__pycache__——这正是 README 所述"随包物化"约束在脚本层的体现。
需要划清的边界是:校验器只证明格式合规,不证明"这个 Skill 能做出正确决策"。文档要求同时自查描述是否有区分度、指令是否保住用户意图、引用是否可发现、脚本是否真的能跑。测试也应验证可观察行为或有意义的不变量,而不是匹配生成文案、标题或正则。
8.4 独立前向测试
当 Skill 足够复杂或风险足够高,且环境允许委派时,可以让独立子代理做一次现实性验证。文档给出的最小提示形式是:
Use $skill-name at /path/to/skill-name to complete this realistic request.要点是:给真实用户请求、Skill 本体和完成任务所必需的最少原始素材;不要提供预期答案、怀疑的缺陷、拟定修法或既有结论;生成物放隔离的临时工作区,避免污染工作树或影响后续评估;涉及额外授权、真实生产系统、显著时间或成本时需要先获批准。评估结束后只看实际产物与结果,据此做最小修正。
九、learn:一个"不能脱离宿主"的 Skill
learn/SKILL.md 只有二十余行,但把职责边界写得很死:
- 它启动的是 Cindy 托管的 Learn 流程:Cindy 负责收集证据、在独立任务中创建 Skill,并在写入用户 Skills 目录前展示 diff 供审阅;
- 不得绕过该流程直接创建或编辑 Skill 文件;
- 解析
/learn(Pi 中为/skill:learn)之后的文本,调用一次cindy_helper→call_tool,name: "start_skill_learning",参数分三种:
| 输入形态 | 传参 |
|---|---|
| 无文本 | source_kind: "session",input: "" |
hub:<slug> [instructions] | source_kind: "hub",hub_slug: <slug>,hub_catalog_scope: "market",剩余文本作input |
hub:<market\|team>:<slug> [instructions] | 同上,但hub_catalog_scope采用显式声明的 scope |
| 其他任意文本 | source_kind: "freetext",全部剩余文本作input |
- 不要传任务 ID、工作目录或输出路径:Cindy 自己绑定当前任务,并掌管暂存、审阅与安装;
- 调用成功后只简短告知用户"已启动、进度与审阅会出现在 Cindy 里",不要轮询、不要重复调用;
- 若 helper 或工具不可用,应说明该 Skill 需要 Cindy 的 Learn 宿主,而不是直接写文件绕开审阅。
这正好对应 README 第 12–14 行的判断:"另一个客户端可以发现这些 Skill 文件,但没有该宿主能力就无法运行工作流。"从源码结构看,Learn 的宿主逻辑位于 learn-host 目录,入口含controller.ts与registerIpc.ts;当宿主未就绪时 IPC 会抛learn host is not ready,账号切换时返回learn host disposed (account switch)。所以"发现得到"和"跑得起来"是两件事,README 的措辞不是营销话术而是能力边界的准确描述。
十、许可与合规:为什么要在资源旁留许可证
README 的两条许可证说明不是形式主义,它们被构建期脚本消费:
- generate-third-party-notices.mjs 把 "OpenAI Codex skill-creator (adapted)" 以
Apache-2.0记入第三方声明,版本字段就填上游 commit977193486dfe7a88c4dab24abeafe9b754f5b13f,许可证正文从cindy-skill-creator/license.txt读取; - 同一脚本把 "PyYAML (vendored pure-Python runtime)" 以
MIT、版本6.0.3记入,正文取自scripts/_vendor/PyYAML-LICENSE.txt; - docs/legal/README.md 也把这两个文件登记为"必须与资源相邻保留"的合规证据。
_vendor/PyYAML-VERSION.txt还记录了本地补丁:
PyYAML 6.0.3 Source: https://github.com/yaml/pyyaml/tree/6.0.3 Local patch: reader.py expresses the YAML printable-codepoint ranges as ordinal comparisons instead of one equivalent regular expression, avoiding an ambiguous range warning from CodeQL.因此"vendored"不是把包随手拷进来:版本、来源、补丁原因、许可证四者都留了可审计记录。这也解释了为什么脚本能在没有pip install pyyaml的机器上直接运行——_frontmatter.py 会把scripts/_vendor插到sys.path首位再import yaml。
十一、排障:常见 warning 与含义
prepareBuiltInSkills的warnings数组是排查内置 Skill 问题的第一手材料。下表按源码文本整理:
| warning 文本(节选) | 触发条件 | 含义与建议 |
|---|---|---|
kept existing built-in Skills because their manifest is unavailable: ... | 清单读不出,但已有物化内容 | 采取"宁可不动"策略;检查清单文件权限与完整性 |
built-in Skill manifest is missing while materialized Skills still exist | 目录有内容但清单缺失 | 拒绝自动重建,避免覆盖未知状态 |
kept built-in Skill bundle <n>; this build only carries older bundle <m> | 磁盘版本更新 | 正常降级保护,无需处理 |
kept built-in Skill <name> because bundle version <n> was reused for different bytes | 同版本改内容 | 发版时必须递增BUILT_IN_SKILLS_BUNDLE_VERSION |
the committed built-in Skill bundle does not match its recorded fingerprints | 版本目录被改写 | 该 bundle 不被信任,投影会被清理 |
built-in Skill <name> was not linked because <path> is already owned by the user | 目标位置是用户真实目录 | 有意为之,不会覆盖用户内容 |
bundled Skill is missing SKILL.md: <path> | 随包目录缺入口文件 | 打包不完整,需修资源 |
staged Skill fingerprint mismatch: <name> | 拷贝后指纹与源不一致 | 文件系统或并发写入异常 |
could not activate built-in Skill bundle: ... | 事务失败 | 已回滚清单、指针与投影,旧 bundle 继续可用 |
could not prepare built-in Skills because another Skill mutation is in progress | 5 秒内未拿到 Skill 变更锁 | 并发准备/安装,稍后重试即可 |
判断"内置 Skill 是否真的可用"的权威信号是projectionSafe与返回的descriptors:只有前者为true,后者才非空,且每个描述符的<absolutePath>/SKILL.md必须是真实文件、目录指纹必须与清单一致、.active必须是指向.versions/<bundle>的链接且realpath校验通过。任何一项失败都会整体退化为空集合,这正是"绝不从路径存在性推断信任"的设计取舍。
十二、小结
回到 README 的四句话,它们其实各自对应一条可验证的工程机制:
- "改编自 Codex" → 固定 commit、
license.txt就地保留、构建期写入第三方声明; - "vendored PyYAML 6.0.3" →
sys.path注入 + 版本/补丁/许可证记录,脚本零外部依赖; - "
learn由 Cindy 自研,需cindy_helper宿主" →start_skill_learning调用契约 + Learn Host 就绪检查,禁止绕过审阅流程; - "启动时物化,不要放用户 Skill" → 共享根 + 内容寻址版本目录 +
.active单指针 + 指纹清单 + 拒绝降级/同版本换字节 + 投影只认真实路径。
理解这条链路后,无论是新增一个随包 Skill、修改既有 Skill 的字节、排查"Skill 突然消失",还是解释"为什么在别的客户端里learn跑不起来",都能直接落到具体的文件与函数上,而不是猜测。
【免费下载链接】cindy
Consider it done. The open-source AI agent that works out of the box · 想到,就能做到。开源、开箱即用的 AI Agent。
相关推荐
oneTBB concurrent_vector 的容量与大小查询接口:size、empty、max_size、capacity 的语义与底层实现
oneTBB concurrent_vector 的容量与大小查询接口:size、empty、max_size、capacity 的语义与底层实现 本篇文章基于
Cindy engineer-bot 开场设置技能解析:工程交付伙伴的 onboarding 流程设计与宿主校验机制
Cindy engineer bot 开场设置技能解析:工程交付伙伴的 onboarding 流程设计与宿主校验机制 engineer bot getting
深入解析 Cindy 视频剪辑伙伴的时间线证据 Skill:让剪辑计划可校验、可追溯
深入解析 Cindy 视频剪辑伙伴的时间线证据 Skill:让剪辑计划可校验、可追溯 这份文档定义的是 Cindy 内置「视频剪辑伙伴」中的一个离线辅助技能 v
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考