标签: Skill

  • 给 Codex 增加自动整理并发布博客的 Skill

    最近把博客的发布链路改成了一个很直接的 MCP:本地启动 astro-blog-ssh,再通过 SSH 直接把 Markdown 写到远端 Astro 博客的 content/blog 目录。

    有了这条链路之后,一个很自然的下一步,就是让 Codex 不只是“能调用工具”,而是“知道什么时候应该把当前对话整理成文章并直接发布”。这次做的,就是把这套行为沉淀成一个可安装的 skill。

    目标不是安装说明,而是行为约束

    一开始最需要收窄的问题,不是怎么写 SKILL.md,而是这个 skill 到底要教 Codex 做什么。

    这次明确的目标是:

    1. 当用户明确说“发布到我的博客”时,Codex 直接整理并发布。
    2. 当当前对话已经形成成体系的技术内容时,Codex 也默认自动发布。
    3. 如果上下文不够,Codex 不能乱编内容,只能缩小主题、说明假设,或者停下来补信息。

    也就是说,这个 skill 不是安装器,不是 MCP 教程,而是一个“从对话到公开文章”的行为规范。

    先写 spec,再落 skill 目录

    为了避免把 skill 写成随意提示词,先在仓库里补了一份设计文档:

    • docs/superpowers/specs/2026-05-01-astro-blog-auto-publish-design.md

    这份 spec 主要固定了几件事:

    • 触发条件分成显式发布和隐式发布两类。
    • 默认行为是自动发布,而不是先出草稿等确认。
    • 自动发布不等于可以编造内容,缺关键信息时必须收窄或中止。
    • skill 只负责教 Codex 调用现有 MCP 工具,不改 MCP 服务本身。

    接着又补了一份实现计划:

    • docs/superpowers/plans/2026-05-01-astro-blog-auto-publish.md

    这样后续生成的 skill 文件就有了清晰边界,既不容易超范围,也更方便以后继续维护。

    最终生成的 skill 结构

    这次只保留最小必要文件:

    skills/astro-blog-auto-publish/SKILL.md
    skills/astro-blog-auto-publish/agents/openai.yaml

    SKILL.md 负责定义核心行为:

    • 什么时候使用这个 skill
    • 什么时候不要使用
    • 如何把对话重写成公共技术文章
    • 如何处理信息不足
    • 何时调用 generate_slugcreate_post
    • 发布失败时如何保留草稿并说明失败原因

    agents/openai.yaml 则负责给 Codex 的技能列表和显式调用入口提供元数据,比如:

    • display_name
    • short_description
    • default_prompt

    这个分层很重要。真正的工作规范应该放在 SKILL.md,而不是都挤进 description 里,不然模型很容易只读简短描述、不读完整 skill。

    skill 里最重要的几条规则

    相比“写出一篇文章”,更重要的是“什么时候不该发”。

    所以这次 skill 里最关键的约束有几条:

    1. 默认自动发布,但前提是当前对话已经足够完整。
    2. 文章必须围绕一个清晰技术主题,而不是原样搬运聊天记录。
    3. 对于小缺口,可以用保守假设补齐,并在最终响应里说明。
    4. 对于关键缺口,不能编造事实,只能补问或缩小文章范围。
    5. 涉及私密、凭据、内部细节的内容,不能直接发。

    这几条基本决定了 skill 的可用性。如果没有这些边界,所谓“自动发布”很容易变成“自动制造垃圾文章”。

    一个实际踩到的坑:元数据生成脚本依赖 PyYAML

    为了避免手写 agents/openai.yaml,我直接复用了现成 skill 自带的生成脚本。

    第一次运行时,脚本报错了:缺少 yaml 模块。

    根因不是脚本逻辑错误,而是它默认会读取 SKILL.md frontmatter,并使用 PyYAML 解析。本地当前 Python 环境没有这个依赖。

    这时候最小改动不是去改环境,也不是去改脚本,而是直接使用它支持的 --name 参数,绕开 frontmatter 解析步骤。这样既不污染环境,也不偏离当前任务范围。

    这个处理方式的好处很明显:

    • 不增加仓库依赖
    • 不修改外部 skill 提供的脚本
    • 仍然能产出标准 openai.yaml

    另一个小坑:$skill-name 被 shell 吃掉了

    openai.yaml 里有个 default_prompt 字段,规范要求显式写出 $skill-name

    第一次生成后,结果从:

    Use $astro-blog-auto-publish ...

    变成了:

    Use -blog-auto-publish ...

    问题出在 shell 展开:$astro 被当成环境变量替换掉了,剩下后半截字符串。

    根因明确之后,修复也很简单:不要怀疑 skill 内容本身,直接把生成后的 openai.yaml 用字面量修正为正确的 $astro-blog-auto-publish

    这个问题很小,但挺典型:

    • 不是业务逻辑问题
    • 不是 MCP 问题
    • 是命令层的字符串展开问题

    如果没有在最后做一次完整验证,很容易带着这个细小错误交付出去。

    最后做了哪些验证

    这次没有去验证“Codex 运行时一定会自动触发 skill”,因为那取决于实际的 skill 加载环境和 MCP 可用性。但本地已经做了这些可证实的检查:

    1. skill 目录结构完整。
    2. SKILL.md frontmatter 合法,命名符合 hyphen-case。
    3. openai.yaml 已生成,字段和 skill 语义一致。
    4. default_prompt 中的 $astro-blog-auto-publish 已按字面量保留。
    5. 仓库 diff 范围只包含 spec、plan 和 skill 目录,没有误改 MCP 服务代码。

    这意味着“skill 包结构正确”这件事是可以确认的;至于“运行时是否自动发布成功”,则仍然依赖 Codex skill 加载能力和 astro-blog-ssh MCP 是否在线。

    这类 skill 的价值

    如果只是偶尔发一篇文章,直接手工整理也能做。

    但当你已经有了 MCP 工具链,下一步最有价值的就不是重复调用工具,而是把“什么时候该调用、如何整理、什么情况下不能发”固化下来。

    skill 的真正价值,不在于省掉几个命令,而在于让自动化行为有边界、有默认策略、也有失败回退路径。

    这次的 astro-blog-auto-publish 就属于这种类型:它不是新能力,而是把已有能力变成了可重复复用的工作流。