Communityライティング&編集github.com

mariagorskikh/product-changelog

商品の変更点をユーザーに伝える定期的なチェンジログメールを作成・送信し、それに対応するXスレッド、LinkedIn投稿、コミュニティ(DiscordまたはSlack)通知を作成・投稿し、オプションでアニメーションチェンジログ動画の概要を作成します。

product-changelog とは?

このスキルは、商品の変更点をユーザーに伝えるための定期的なチェンジログメールの作成から送信、そして関連するソーシャルメディア投稿やコミュニティ通知の作成までを自動化します。ユーザーが「changelog email」や「write the changelog」などのキーフレーズを言うと、このスキルが起動します。Claude Code、Cursor、CodexなどのAIエージェントプラットフォームで動作し、Resendを使用したメール送信、Playwrightを使用したスクリーンショット撮影、そしてX、LinkedIn、Discord/Slackへの投稿をサポートします。設定は最初に一度だけ行い、その後は自律的に動作します。費用が発生するアクション(例えば、プロバイダープランの月間割当を超える送信)はユーザーの明示的な承認を待ちます。このスキルは、チェンジログメールを真実の源とし、そこから他のすべてのチャンネル(ソーシャルメディア投稿など)を派生させます。メールの作成、レビュー、テスト送信、配信、そしてソーシャルメディアへの展開をエンドツーエンドで処理します。

対応~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に質問する

このエージェントスキルを事前に読み込んだ状態で新しいチャットを開きます。

ドキュメント

プロダクトチェンジログ

プロダクトの変更点をユーザーに伝える定期的なメールです。人間らしい言葉で書き、安全に送信し、ソーシャル投稿に再利用します。メールが各号の真実の源であり、他のすべてのチャンネルはそれから派生します。

このスキルの実行方法

  • 最初に一度だけ質問します。 初回実行時(または設定が不足している場合や必要な値が空白の場合)は、設定質問を一度に簡潔に行い、config.yaml に書き込み、その後は質問なしで続行します。
  • その後はすべてを実行します。 autonomy.mode: autonomous(デフォルト)では、項目の選択、下書き、リント、自己レビュー、スクリーンショット、テスト送信、テストの読み上げ、オーディエンスの構築、ドライラン、全員への送信、確認、オプトアウトの取得、ソーシャル投稿の作成と投稿、アーカイブまでを行います。承認ステップはありません。各対外アクションは最初に自己レビューゲートを通過します。
  • お金だけが承認を必要とします。 費用がかかるものはすべて、すべてのモードでユーザーからの明示的な「はい」をチャットで待ちます。プロバイダープランの含まれる割当を超える送信(provider.monthly_quota)、プロバイダープランのアップグレード、ドメインの購入、有料ツールやライセンス(例:有料のRemotion会社ライセンス)などです。これら以外は「はい」を必要としません。
  • ハードストップは承認ではありません。 そのアクションを停止します。安全な他の処理をすべて完了し、報告します。ハードストップを参照してください。
  • 最後に報告します。 1つの要約:送信された件名と全文、カウント(キュー、送信、失敗、除外、配信停止)、配信サンプル、テスト読み上げ結果、各投稿とそのリンク、スキップされたものとその理由。
  • review モード は、従来のアクションごとの承認ゲートを維持します。レビューモードを参照してください。

すべてのスクリプトはデフォルトでドライランです。実際の送信には明示的なフラグ(--send、--yes-send-to-everyone、--yes-send-broadcast)が必要です。自律モードでは、自分でこれらのフラグを渡しますが、対応する自己レビューゲートを通過した後に限ります。スクリプト以外でアドホックにメールを送信しないでください。スクリプトがオプトアウトを強制するからです。

ステップ 0: 設定を読み込む

  1. 現在のプロジェクトルート、次にこのスキルのフォルダ(または $CHANGELOG_CONFIG)で config.yaml を探します。
  2. ない場合は、config.example.yaml をプロジェクトルートに config.yaml としてコピーし、1つのメッセージで以下を質問します。
    • プロダクト名、ウェブサイト、ダッシュボードとドキュメントの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 を読み取ります。Voice設定はリファレンスのデフォルトを上書きします。
  5. シークレットは設定やチャットに決して入れません。APIキーと配信停止署名シークレットは環境変数に保存します。

以降の号では何も質問しません。唯一のオプションの質問は「今回git以外で出荷されたものはありますか(価格設定、パートナーシップ、ドキュメントなど)?」です。ユーザーが不在の場合は、この質問をスキップし、gitとリリースノートから作業します。

ステップ 1: 含める項目を選ぶ

最初に references/voice-and-format.md を読んでください。

  • paths.issues_dir の最後の号(NNN-YYYY-MM-DD-slug という名前のフォルダ)を見つけます。最新のものが次の号のテンプレートです。
  • その日付以降の候補を収集します:git log --since=<日付> --no-merges --oneline、マージされたプルリクエスト、リリースノート、さらにユーザーが言及したもの。
  • ユーザーに見える変更のみを保持します。3~5項目で、読者にとっての有用性でランク付けします。最も有用な変更を先頭に、それはほとんどの読者が今日使えるものです。
  • すべての主張を実際のプロダクトとウェブサイトで確認します。機能、数値、日付を決して捏造しないでください。主張が検証できない場合は、その項目を削除します。
  • リストと順序を号の NOTES.md に書き込み、次に進みます。

ステップ 2: メールの下書きを作成する

paths.issues_dir/NNN-YYYY-MM-DD-slug/ を作成し、以下を書き込みます。

  • subject.txt:ベネフィット主導でプロダクトファースト(「シートにレポートをエクスポートできるようになりました」)。「Changelog #N」は絶対に使わないでください。
  • email.html:templates/email.html(または前回の号)から、設定と内容に基づいてすべての [[...]] マーカーを埋めます。
  • email.txt:同じ内容をプレーンテキストで、ベアURLを記載します。

{{NAME}} と {{UNSUB_URL}} はリテラルプレースホルダーとして保持します。送信スクリプトが受信者ごとに埋めます。

コアルール(詳細はリファレンスを参照):

  • Hi {{NAME}}, で始め、設定からの1行のオープナー、そしてすぐに最初の項目に入ります。
  • 各項目:何が変わったか、次になぜそれが重要なのかを、流れるような文章で記述します。ラベル付きの箇条書きは使いません。
  • 短い文、平易な言葉、「we built」ではなく「you can now」を使います。
  • 段落ごとに最大1つの太字アクセント。
  • 項目ごとに最大1つのスクリーンショット。ヒーロー項目には常に1つ付け、その機能にリンクします。
  • スタイルを最小限に抑えたHTML:600pxのラッパー、システムフォント、インラインスタイル、ボタンやバナーは使いません。
  • 「excited to announce」などの禁止リストにある表現は使いません。voice.no_dashes がオンの場合、emダッシュやenダッシュも使いません。
  • オプションのカジュアルな追伸(「BTW、...」)。
  • 設定からの署名。配信停止行は最後の行で、小さく目立たないようにします。

ステップ 3: スクリーンショット

references/screenshots.md に従います。デモアカウントで実際の状態をステージングし(顧客データは使わない)、Playwrightで2倍の解像度でキャプチャし、パネルに切り抜き、ファイルを <issue>-<slug>.png と名付け、paths.image_base_url でホストし、ブロードキャストの前に各URLが200を返すことを確認します。

ステップ 4: リントと自己レビュー(ゲート 1: コピー)

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

すべての失敗を修正し、以下のチェックリストを実行します。すべてのボックスが合格するまで修正と再実行を繰り返します。

  • lint_email.py --check-links が終了コード0で終了する。
  • 忙しい顧客として上から下まで読む:5秒以内に最も有用な変更が明確にわかる。
  • すべての項目がユーザーが今日見たり使ったりできるものである。
  • すべての主張、数値、リンクが実際のプロダクトとサイトと一致する。
  • プレスリリースのようには読めない。送信者が話しているように聞こえる。
  • 件名が60文字未満で、ベネフィット主導であり、「Changelog #N」を含まない。
  • すべての画像にaltテキストがあり、ホストされたURLが200を返す(またはステップ7の前にデプロイされる)。

ステップ 5: テスト送信と読み上げ(ゲート 2: 配信されたコピー)

python3 scripts/send_test.py ISSUE_DIR --send      # test_recipients のみ、[TEST] プレフィックス
python3 scripts/send_test.py ISSUE_DIR --readback  # 1分後

テストコピーは test_recipients にのみ送信され、まだデプロイされていない画像はインラインで埋め込まれます。--readback は、プロバイダーAPIから配信された各メッセージを取得し、最後のイベント、件名、レンダリングされた挨拶文と配信停止行をチェックし、プレースホルダーが残っていないことを確認します。また、返されたHTMLを自分で一度読みます。何か問題があれば、修正し、再リントし、再テストします。

own_endpoint モードでは、テストコピーの配信停止リンクを開き、オプトアウトが記録されたことを確認し、テストアドレスを再購読します。

ステップ 6: オーディエンスを構築する

詳細は references/sending.md を参照してください。

  • ユーザーのデータベースから ISSUE_DIR/audience.jsonl を新たに構築します(1行につき {"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    # 連絡先のupsert
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 を渡します。

1件ずつのブロードキャストは再開可能です。停止した場合、同じコマンドを再実行すると、既に送信済みのアドレスはスキップされます。

ステップ 8: 確認と同期

  • 1件ずつ:python3 scripts/verify_delivery.py ISSUE_DIR --sample 40。ブロードキャスト:プロバイダーAPIまたはダッシュボード経由で GET /broadcasts/{id} を実行します。
  • 1日後にオプトアウトを取得します(broadcast_unsubs.py)。ユーザーがオプトアウトを独自のデータベースで管理しており、書き込みアクセス権がある場合はインポートします。そうでない場合は、レポートでその旨を伝えます。

ステップ 9: ソーシャル投稿

最終版のメールから、references/social.md に従って作成します。

  • Xスレッド:social.hook_prefix で始まるフックツイート。すべての項目に名前を付け、リンクはなく、最も強力なスクリーンショットを添えます。次に、項目ごとに番号付きのツイートを1つ、そのリンクとスクリーンショットを添えます。さらに、創業者による1行の個人的な引用投稿を追加します。
  • LinkedIn:1つの投稿、最初の行にフック、項目ごとに1つの短い段落、プレーンテキスト、イメージギャラリー。
  • コミュニティ(DiscordまたはSlack):オープナー行、項目ごとに1行(ベアリンク付き)、スレッドリンクは公開されたら1つ。

最大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、および NOTES.md(項目リスト、送信日、使用されたパス、オーディエンスサイズ、送信数と失敗数、ブロードキャストID、投稿URL、1日後の配信停止を含む)。プロダクトに公開ウェブチェンジログページ(product.changelog_page)があり、そのソースがこのリポジトリにある場合は、そこにも号を追加します。ユーザーが文体やフォーマットを修正した場合、その教訓を設定の voice.notes に追加し、次の号からそれを反映させます。その後、冒頭で説明した1つの要約を送信します。

ハードストップ

そのアクションを停止し、安全な他のすべての処理を完了し、何が停止したかとその理由を報告します。ストップを回避しようとしないでください。

  • 修正できないリントエラー。
  • 修正できない壊れたリンクや画像(デプロイまたは修正後も200にならない)。
  • テストの読み上げに失敗し、原因が見つからない。
  • オプトアウトフィルタリングの失敗:オプトアウトリストを取得または構築できない、オーディエンスクエリがエラーになる、またはスクリプトが unsubs.txt を拒否する。なしでは絶対に送信しないでください。
  • 送信中にしきい値を超えるプロバイダーエラー(blast.py が provider.max_failures または provider.max_failure_rate で終了コード2を返す)。エラーを報告します。ブロードキャストは修正されれば再開可能です。
  • クォータを超える送信、プランアップグレード、またはその他の出費:ユーザーに尋ねます(お金のルール)。
  • ログイン、2FA、再認証のプロンプト、CAPTCHA、投稿ツールでのプラットフォーム警告やレート制限通知、支払いや請求フォーム。
  • オプトアウトやスパム対策法に違反するもの(配信停止行の欠落、オプトアウトした人の再購読)。
  • 検証できない主張。推測する代わりに削除します。

レビューモード

autonomy.mode: review では、3つのゲートもユーザーの承認を必要とします。

  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 付き)、broadcast_sync.py、broadcast_create.py、blast.py、verify_delivery.py、broadcast_unsubs.py。
  • references/:voice-and-format.md、screenshots.md、sending.md、social.md、video.md。

スクリプトの完全なコマンドラインインターフェースについては、各スクリプトのヘルプテキストを参照してください。

関連スキル