Community写作与编辑github.com

mariagorskikh/product-changelog

编写并发送定期产品更新邮件给用户,然后创建并发布对应的X线程、LinkedIn帖子和社区(Discord或Slack)公告,并可选择制作动画更新视频。

product-changelog 是什么?

该技能是一个端到端的产品更新自动化工作流,专为需要定期向用户发送产品变更通知的团队设计。它支持Claude Code、Cursor等AI代理平台,通过一个`config.yaml`文件配置所有设置。技能的核心是从Git提交、Pull Request和用户输入中收集产品变更,自动生成邮件内容(HTML和纯文本),通过Playwright截图,进行lint检查和自我审查,向测试收件人发送测试邮件并验证送达情况,然后通过Resend(支持广播或逐一发送)向所有用户发送最终版本,同时自动处理退订。发完后,它还会自动生成并发布X线程、LinkedIn帖子和社区公告,并归档所有历史版本。整个流程除了涉及付费操作(如超出配额、升级计划、购买工具)需要用户明确确认外,其余步骤完全自主运行,显著减少了人工操作时间。

兼容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/mariagorskikh/product-changelog/tree/HEAD

Installed? Explore more 写作与编辑 skills: steipete/notion, langchain-ai/langchain, bytedance/podcast-generation · View all 6 →

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

产品更新日志

一封定期邮件,像真人一样告诉用户产品发生了什么变化,安全发送,然后用于社交媒体帖子。邮件是每一期的唯一信源;所有其他渠道都基于它精简而成。

该技能如何运行

  • 一次性询问,提前完成。 首次运行时(或配置缺失、某必需值为空时),一次性询问所有设置问题,写入config.yaml,然后继续执行,不再提问。
  • 然后自动执行所有操作。 在autonomy.mode: autonomous(默认模式)下,你会选择内容、起草、lint、自我审查、截图、发送测试、读取测试结果、构建受众、执行测试性发送、向所有人发送、验证、拉取退订、编写并发布社交媒体帖子,以及归档。无需审批步骤。每个向外操作都先通过自我审查关卡。
  • 只有涉及金钱才需审批。 任何需要花钱的操作都会等待用户在聊天中明确同意,在任何模式下都如此:发送量超过提供商计划包含的配额(provider.monthly_quota)、升级提供商计划、购买域名、付费工具或许可(例如付费的Remotion公司许可)。其他操作无需同意。
  • 硬停不等于审批。 硬停仅中止该操作。完成其他安全可执行的操作,然后报告。参见硬停。
  • 最后生成报告。 一个摘要:已发出的主题和完整文本、计数(已排队、已发送、失败、已排除、已退订)、送达样本、测试回读结果、每个帖子及其链接,以及任何被跳过的内容及原因。
  • review模式 保留旧有的逐操作审批关卡。参见审查模式。

每个脚本默认都是测试性运行。真正的发送需要显式标志(--send、--yes-send-to-everyone、--yes-send-broadcast)。在自主模式下,你自行传递这些标志,且仅在匹配的自我审查关卡通过之后。切勿在脚本以外发送广告邮件,因为脚本是强制执行退订的保证。

步骤0:加载配置

  1. 在当前项目根目录查找config.yaml,然后在本技能文件夹(或$CHANGELOG_CONFIG)中查找。
  2. 如果没有,将config.example.yaml复制到项目根目录的config.yaml,并在一条消息中询问:
    • 产品名称、网站、仪表板和文档URL
    • 发件人姓名和头衔、发件地址、回复地址
    • 测试收件人(姓名和地址)
    • 提供商:Resend受众ID、保存API密钥的环境变量、以及每月包含邮件数(如知道,provider.monthly_quota,可选)
    • 退订模式(broadcast或own_endpoint)
    • 图片托管:截图存储位置及其公共基础URL
    • 可选:发送频率、品牌链接颜色、以及每个社交渠道的发布方式(social.post_via) 不要猜测任何值。将答案写入config.yaml,然后继续。
  3. 任何仍设置为大写占位符或yourproduct.example地址的值都视为缺失。
  4. 读取autonomy.mode(默认autonomous)、voice.notes和voice.banned_phrases。语音设置覆盖参考中的默认值。
  5. 密钥从不出现在配置或聊天中。API密钥和退订签名密钥存在于环境变量中。

后续版本无需询问。唯一可选的问题是:“本次是否有Git之外发布的(定价、合作、文档)?” 如果用户不在,则跳过,从Git和发布说明中工作。

步骤1:选择内容

首先阅读references/voice-and-format.md。

  • 在paths.issues_dir中找到最后一期(文件夹命名为NNN-YYYY-MM-DD-slug)。最新一期作为下一期的模板。
  • 收集该日期后的候选内容:git log --since=<date> --no-merges --oneline、已合并的Pull Request、发布说明,以及用户提及的任何内容。
  • 只保留用户可见的变更。3到5项,按对读者的有用性排序。以最有用的变更开头,即大多数读者今天能立即使用的。
  • 对照线上产品和网站检查每项声明。切勿编造功能、数字或日期。如果某项声明无法验证,则删除该项。
  • 将列表和顺序写入该版本的NOTES.md,然后继续。

步骤2:起草邮件

创建paths.issues_dir/NNN-YYYY-MM-DD-slug/并写入:

  • subject.txt:以利益为导向、产品优先(如“您现在可以将报告导出到Sheets”)。切勿使用“Changelog #N”。
  • email.html:基于templates/email.html(或上一期),填充每个来自配置和内容的[[...]]标记。
  • email.txt:与HTML相同的内容,纯文本,带有裸URL。

保留{{NAME}}和{{UNSUB_URL}}作为文字占位符;发送脚本会在收件人级别填充它们。

核心规则(详细信息见参考文档):

  • Hi {{NAME}},,然后是从配置中获取的一行开头语,直接进入主导项。
  • 每一项:变化了什么,为什么重要,用流畅的句子表达。无标记列表。
  • 短句,简单词,使用“您现在可以”而非“我们构建了”。
  • 每段最多一个粗体强调。
  • 每项最多一张截图;主导项始终配有一张,并链接到该功能。
  • 极简HTML样式:600px宽包裹器,系统字体,内联样式,无按钮或横幅。
  • 不使用“激动地宣布”及其他禁用短语。如果启用了voice.no_dashes,则不使用em破折号或en破折号。
  • 可选的非正式附注(“顺便说一句,...”)。
  • 来自配置的签名。退订行是最后一行,小字号且颜色柔和。

步骤3:截图

遵循references/screenshots.md:在演示账户上搭建真实状态(无客户数据),使用Playwright以2倍分辨率截取,裁剪到面板范围,命名文件为<issue>-<slug>.png,托管到paths.image_base_url,并在任何发送前确认每个URL返回200。

步骤4:Lint和自我审查(关卡1:文案)

python3 scripts/lint_email.py ISSUE_DIR --check-links

修复所有失败项,然后逐项检查清单。修复并重新运行,直到每个检查点通过。

  • lint_email.py --check-links退出码为0。
  • 以忙碌客户的身份从上到下阅读:最有用的变更在5秒内清晰可见。
  • 每一项都是用户今天可以看到或使用的。
  • 每项声明、数字和链接都与线上产品和网站一致。
  • 内容不像新闻稿;听起来像是发件人在说话。
  • 主题不超过60个字符,以利益为导向,不使用“Changelog #N”。
  • 每张图片都有替代文本,且托管URL返回200(或在步骤7之前部署)。

步骤5:测试发送和回读(关卡2:已送达副本)

python3 scripts/send_test.py ISSUE_DIR --send      # 仅发送给测试收件人,加[TEST]前缀
python3 scripts/send_test.py ISSUE_DIR --readback  # 一分钟后

测试副本仅发送给test_recipients,如果图片尚未部署,则内联嵌入。--readback从提供商API拉取每封已送达邮件,检查最新事件、主题、渲染的问候语和退订行,并确保没有存活占位符。同时,亲自阅读返回的HTML一次。如有任何问题,修复后重新lint、重新测试。

在own_endpoint模式下,打开测试副本中的退订链接,确认退订已记录,然后重新订阅测试地址。

步骤6:构建受众

详细信息见references/sending.md。

  • 从用户数据库新鲜构建ISSUE_DIR/audience.jsonl(每行{"email", "name"})。切勿重用旧列表。
  • 从退订表构建ISSUE_DIR/unsubs.txt。在广播模式下,首先运行python3 scripts/broadcast_unsubs.py --out <issues_dir>/unsubs.txt并将其复制进来。
  • 创始人、测试和演示账户在配置exclude中;它们改为接收测试副本。

步骤7:向所有人发送(关卡3:受众)

根据unsubscribe.mode选择路径。先运行测试性发送并检查计数。

广播模式(提供商托管退订):

python3 scripts/broadcast_sync.py ISSUE_DIR/audience.jsonl            # 计划
python3 scripts/broadcast_sync.py ISSUE_DIR/audience.jsonl --apply    # 更新联系人
python3 scripts/broadcast_create.py ISSUE_DIR --create                # 仅草稿
python3 scripts/broadcast_create.py --send BROADCAST_ID               # 显示收件人数量,拒绝
python3 scripts/broadcast_create.py --send BROADCAST_ID --yes-send-broadcast

自有端点模式(签名链接到自己的页面):

python3 scripts/blast.py ISSUE_DIR                                    # 测试运行并显示计数
python3 scripts/blast.py ISSUE_DIR --send --yes-send-to-everyone

受众检查清单,在发送标志前必须全部通过:

  • 关卡1和2在此确切副本上通过(自回读以来无任何更改)。
  • 退订列表在此次运行中已拉取或构建,且如果产品曾有退订,则非空。
  • 测试运行计数合理:队列数量接近用户数减去退订数减去排除数;与上一期的NOTES.md相比无意外跳跃。
  • 广播模式:broadcast_sync.py报告无意外过期联系人。
  • 每个托管图片URL返回200。
  • 配额:如果设置了provider.monthly_quota,则传递--sent-this-month N(累计本月各期NOTES.md中的已发送数量,以及同一计划上的其他邮件)。如果脚本显示发送将超过配额,停止并询问用户;仅在用户明确同意后传递--yes-over-quota。

逐一发送是可恢复的:如果停止,重新运行同一命令,它会跳过已发送的地址。

步骤8:验证和同步

  • 逐一发送:python3 scripts/verify_delivery.py ISSUE_DIR --sample 40。广播:通过提供商API或仪表板执行GET /broadcasts/{id}。
  • 一天后拉取退订(broadcast_unsubs.py)。如果用户在自己的数据库中维护退订,并且你有写权限,则导入它们;否则在报告中说明。

步骤9:社交媒体帖子

根据最终邮件编写,遵循references/social.md:

  • X线程:钩子推文以social.hook_prefix开头,列出所有项目,无链接,最强截图。然后每个项目一个编号推文,包含其链接和截图。加上一条创始人的单行个人引用推文。
  • LinkedIn:一个帖子,钩子第一行,每个项目一段短文,纯文本,图片库。
  • 社区(Discord或Slack):开头行,每个项目一行带裸链接,上线后的线程链接。

最多4到5项,无政策通知,声明与线上网站一致。在social.md上运行相同的禁用短语和破折号检查(lint_email.py会读取它)。然后,对于social.post_via中配置了工具的每个渠道,自行发布并记录帖子URL。对于没有工具的渠道,向用户提供最终文本和图片。创始人的引用推文来自个人账户,因此除非为该账户配置了工具,否则将其交给用户。

步骤10:视频(可选,仅按请求)

如果用户要求,遵循references/video.md:15到25秒的动态片段,使用版本截图,无标题幻灯片,每个项目大标题,截图主导,结尾带产品URL。对于超过一定团队规模,Remotion需要付费公司许可;这是一个关于钱的询问。

步骤11:归档和报告

在版本文件夹中保留:subject.txt、email.html、email.txt、截图、social.md、test_results.jsonl,以及包含项目列表、发送日期、使用的路径、受众规模、已发送和失败计数、广播ID、帖子URL和一天后的退订数量的NOTES.md。如果产品有公开的网络更新日志页面(product.changelog_page)且其源代码在此仓库中,则也将该版本添加到那里。当用户更正语音或格式时,将经验添加到配置的voice.notes中,以便下一期从中开始。然后发送开头描述的那个摘要。

硬停

停止该操作,完成所有其他安全操作,并报告停止的内容和原因。切勿绕过停止。

  • 无法修复的Lint失败。
  • 无法修复的损坏链接或图片(部署或更正后仍不是200)。
  • 测试回读失败且无法找到原因。
  • 退出过滤失败:无法拉取或构建退订列表、受众查询出错、或脚本因unsubs.txt而拒绝。切勿在没有退订的情况下发送。
  • 发送过程中提供商错误超过阈值(blast.py在达到provider.max_failures或provider.max_failure_rate时退出码为2)。报告错误;一旦修复即可恢复发送。
  • 任何会超过配额的发送、计划升级或其他支出:询问用户(金钱规则)。
  • 登录、2FA或重新认证提示、CAPTCHA、平台警告或发布工具上的速率限制通知、支付或账单表单。
  • 任何可能违反退订或反垃圾邮件法律的行为(缺少退订行、重新订阅已退订的人)。
  • 无法验证的声明。放弃而非猜测。

审查模式

当autonomy.mode: review时,三个关卡还需要用户的确认:

  1. 草稿关卡。 在聊天中显示主题、邮件和项目列表。在用户批准文案之前,不发送任何内容。
  2. 测试关卡。 仅发送给test_recipients。用户在真实收件箱中阅读,并说“继续”或给出修复。
  3. 发送关卡。 显示测试运行计数(已排队、已排除、已退订)。仅当用户在聊天中对此特定发送说“是”时,才向所有人发送。对先前步骤的同意并不代表对此步骤的同意。

社交媒体帖子在发布前显示,项目列表在编写前提出。在匹配的关卡之前,切勿传递发送标志。

回答“有多少订阅者?”

订阅者 = 用户数减去退订数。从用户的退订表或unsubs.txt(在广播模式下,先运行broadcast_unsubs.py)中计算退订数。同时给出最新版本的退订率。

文件

  • config.example.yaml:每个公司特定的值,加上autonomy、provider.monthly_quota和social.post_via。
  • templates/email.html、templates/email.txt:起始布局。
  • scripts/:lint_email.py、send_test.py(带--readback)、`...

相关技能