## 経緯
[[Claude Code]]の練習の一環 かつ 地味に面倒な作業でもあるので。
> [!attention]
> 最終的に自動投稿は諦めて、クリップボードに文言コピーしてもらうようにした。
## 環境
| 対象 | バージョン |
| --------------- | ---------- |
| [[macOS]] | 15.5 |
| [[Ghostty]] | 1.1.3 |
| [[Claude Code]] | 1.0.34 |
## Claude Codeにヒアリング
> [!right-bubble] ![[minerva-face-right.webp]]
> BlueskyにGitHubでリリースした内容を共有するタスクをあなたにお願いしたいです。そのための準備に必要なことを教えて下さい。
> [!left-bubble] ![[claude-san-face.webp]]
> BlueuskyにGitHubリリースを共有するための準備として、以下が必要です:
> [!left-bubble] ![[claude-san-face.webp]]
> ## 必要な準備
>
> 1. **Bluesky APIアクセス**
> - Bluesky アカウント作成
> - App Password の生成(Settings > Privacy and Security > App Passwords)
> - AT Protocol APIの認証情報
>
> 2. **GitHub API認証**
> - Personal Access Token(リリース情報読み取り権限)
> - リポジトリアクセス権限
>
> 3. **技術的実装方針**
> - GitHub Actions での自動化 vs ローカルスクリプト
> - 投稿フォーマットの定義
> - エラーハンドリング戦略
>
> どの部分から始めたいですか?また、手動実行かGitHub Actionsでの自動化のどちらを希望しますか?
> [!right-bubble] ![[minerva-face-right.webp]]
> ★でコメントします。
>
> 1. Bluesky APIアクセス
> - Bluesky アカウント作成
> - ★アカウントは作成済です
> - App Password の生成(Settings > Privacy and Security > App Passwords)
> - AT Protocol APIの認証情報
> 2. GitHub API認証
> - Personal Access Token(リリース情報読み取り権限)
> - ★リリース情報は公開情報であり、GitHub CLIも使える状態になっているので必要ないかなと
> - リポジトリアクセス権限
> 3. 技術的実装方針
> - GitHub Actions での自動化 vs ローカルスクリプト
> - ★スクリプトで自動化するのではなくClaude Codeの方で考えてやってほしいです
> - 投稿フォーマットの定義
> - ★基本的なフォーマットや要件はもちろん用意します
> - エラーハンドリング戦略
> - ★これは一旦保留で
>
> [!left-bubble] ![[claude-san-face.webp]]
> 了解しました。Claude Codeで手動実行する方針ですね。
>
> ## 次に必要な準備
>
> 1. **Bluesky App Password生成**
> - Bluesky > Settings > Privacy and Security > App Passwords
> - 新しいApp Passwordを作成(名前は"GitHub Release Bot"など)
>
> 2. **投稿フォーマット定義**
> - どのような内容を投稿したいか(リリース名、変更点、URL等)
> - 文字数制限(Blueskyは300文字)
> - ハッシュタグの有無
>
> App Passwordを生成後、投稿フォーマットの要件を教えていただければ、Claude CodeでGitHub CLI + Bluesky APIを使った投稿スクリプトを作成できます。
>
> 投稿したい内容の例があれば教えてください。
## [[Bluesky]]の[[アプリパスワードを作成 (Bluesky)|アプリパスワードを作成]]
![[2025-06-25-17-44-31.avif]]
[[アプリパスワード (Bluesky)|アプリパスワード]]が表示されるので、それを利用する。
## `/release` コマンドの作成
> [!right-bubble] ![[minerva-face-right.webp]]
> 1のApp Passwordを作成しました。Carnelianの設定にApp Passwordの指定項目を追加すればOKかなと思っています。
> [!left-bubble] ![[claude-san-face.webp]]
> App Password作成完了ですね。CarnelianでBlueskyのApp Passwordを設定できるようにするのは良いアイデアです。
>
> 次に**投稿フォーマット**を決めましょう。GitHubリリースをBlueskyに投稿する際の内容として、どのような情報を含めたいですか?
>
> 例:
> - リリース名とバージョン
> - 主要な変更点(箇条書き)
> - GitHub リリースページのURL
> - ハッシュタグ(#ObsidianPlugin など)
>
> 投稿例のイメージがあれば教えてください。それに基づいてClaude Codeで投稿処理を実装します。
> [!right-bubble] ![[minerva-face-right.webp]]
> 以下のフォーマットを想定しています。
>
> ```
> 📦 ${プロダクト名} ${バージョン} 🚀
>
> ・箇条書きで新機能やメインの変更点を列挙
>
> ${GitHubリリースページのURL}
> ```
>
> また、この機能は `.claude/commands/**.md` でのコマンド登録を想定しており、そのコマンドではBluesky通知前に行うリリースジョブ実行なども定義するつもりです。
>
> 1. 私がClaude Codeで `/release` を実行
> 2. 現在のリポジトリのリリース作業を実行
> - `CLAUDE.md` に記載なければ `README.md` を参照
> 3. リリースジョブ実行後はリリースが完了しているかを定期的に確認し、完了していたら該当バージョンのリリースノートURLにアクセスし、リリースしていることを確認
> 4. リリースURLとリリースノートの内容から、先程提示したフォーマットに従いBlueskyへの投稿を行う
>
> [!right-bubble] ![[minerva-face-right.webp]]
> **作業中の割り込み指示:**
> ちょっと待ってください。一度中断してください。
> [!right-bubble] ![[minerva-face-right.webp]]
> すいません。このリリースフローは本リポジトリだけでなく、私が開発する場合に共通するものです。そのため.claudeはホームディレクトリの方を使います。
> [!left-bubble] ![[claude-san-face.webp]]
> 理解しました。ホームディレクトリの `~/.claude/commands/` を使って汎用的な `/release` コマンドを作成しますね。
>
> 現在のリポジトリのリリースプロセスを確認したところ:
> - GitHub Actions の Release workflow(手動トリガー)でsemantic-releaseを使用
> - package.json のバージョンは現在 13.6.0
>
> 次にホームディレクトリに `/release` コマンドを作成して、以下の流れを実装します:
> 1. Release Action手動実行
> 2. リリース完了の定期確認
> 3. 完了後にBluesky投稿
>
> ホームディレクトリの `.claude/commands/` ディレクトリ作成から始めましょうか?
> [!right-bubble] ![[minerva-face-right.webp]]
> お願いします。
> [!right-bubble] ![[minerva-face-right.webp]]
> **作業中の割り込み指示:**
> ちょっと待ってください。一度中断してください。
> [!right-bubble] ![[minerva-face-right.webp]]
> すいません。Carnelianは関係ありませんでした。。app passwordは環境変数に設定されている前提で大丈夫です。
> [!left-bubble] ![[claude-san-face.webp]]
> 了解しました。App Passwordは環境変数設定前提で進めます。
>
> `~/.claude/commands/release.md` を作成しますね。以下の内容で作成します:
>
> 1. 現在のリポジトリのリリース作業実行
> 2. リリース完了確認
> 3. 環境変数のApp Passwordを使ってBluesky投稿
>
> 作成してよろしいですか?
> [!right-bubble] ![[minerva-face-right.webp]]
> ok
> [!left-bubble] ![[claude-san-face.webp]]
> `~/.claude/commands/release.md` を作成しました。
>
> これで `/release` コマンドが使用できるようになりました。必要な環境変数は:
> - `BLUESKY_APP_PASSWORD` - 先ほど作成したApp Password
> - `BLUESKY_HANDLE` - あなたのBlueskyハンドル
>
> 環境変数を設定後、現在のリポジトリで `/release` を実行すると、リリース作業からBluesky投稿まで自動実行されます。
## `/release` コマンドの実行 (リリース完了から)
リリース完了してて[[Bluesky]]への通知だけしたかったのでイレギュラーな指令を出す。
> [!right-bubble] ![[minerva-face-right.webp]]
> **`/release`**
>
> <command-args>GitHubにはリリース済の状態からスタートして</command-args>
> [!left-bubble] ![[claude-san-face.webp]]
> GitHubにリリース済みの状態からスタートしてBlueskyに投稿するということですね。まず現在のリポジトリ状況を確認して、最新のリリース情報を取得します。
> [!left-bubble] ![[claude-san-face.webp]]
> リリース情報を確認できました。v13.6.0が最新リリースですね。今からBluesky投稿用のフォーマットを作成します。
> [!left-bubble] ![[claude-san-face.webp]]
> 次にBlueskyに投稿します。環境変数を確認してから投稿を実行します。
> [!left-bubble] ![[claude-san-face.webp]]
> JSONエスケープエラーが発生しました。投稿テキストをJSONエスケープしてもう一度試します。
> [!left-bubble] ![[claude-san-face.webp]]
> 日時フォーマットに問題があるようです。正しいフォーマットで再試行します。
> [!right-bubble] ![[minerva-face-right.webp]]
> **作業中の割り込み指示:**
> ちょっと待ってください。一度中断してください。
## 動作確認... しかし...
> [!right-bubble] ![[minerva-face-right.webp]]
> 投稿はできていたのですが、GitHubのリリースURLがOGPとして登録されないまま(リンクではないただの文字列として)投稿されてしまいました。該当の投稿は削除済なので、OGPリンクとなるように調整して投稿し直してください。
> [!left-bubble] ![[claude-san-face.webp]]
> OGPリンクとして認識されるように、URLを独立した行にして再投稿します。
> [!left-bubble] ![[claude-san-face.webp]]
> 投稿完了しました!URLを独立した行に配置することで、BlueskyでOGPリンクとして認識されるはずです。
> [!right-bubble] ![[minerva-face-right.webp]]
> [Image #1] だめそうです。。投稿は削除しました。
> [!left-bubble] ![[claude-san-face.webp]]
> URLがOGPとして認識されていませんね。Blueskyでは特定の条件でないとリンクカードが表示されないようです。別のアプローチを試してみましょう。
> [!right-bubble] ![[minerva-face-right.webp]]
> **作業中の割り込み指示:**
> ちょっと待ってください。一度中断してください。
> [!right-bubble] ![[minerva-face-right.webp]]
> 投稿フォーマットは変えないでください。恐らくfeedのPOST APIの使い方が間違っているだけだと思います。DeepwikiでBluesky APIの仕様を確認してみてください。
> [!left-bubble] ![[claude-san-face.webp]]
> わかりました!Blueskyでリンクカードを表示するには、単純にURLを投稿するだけでは不十分で、OGPメタデータを自分で取得して`embed`として明示的に指定する必要があるようです。
> [!left-bubble] ![[claude-san-face.webp]]
> HTMLパースがうまくいかないようです。別のアプローチを試してみます。
## 代替案の提供
> [!right-bubble] ![[minerva-face-right.webp]]
> **作業中の割り込み指示:**
> ちょっと待ってください。一度中断してください。
> [!right-bubble] ![[minerva-face-right.webp]]
> OGP対応難しそうなら、Blueskyに投稿する文言を作ってもらうところまでで大丈夫です。pbcopyでクリップボードに入れてもらえれば自分の方で投稿します。その方が環境変数も不要でセキュアですし、動画を貼ったりもできますしね。
> [!left-bubble] ![[claude-san-face.webp]]
> 投稿内容をクリップボードにコピーしました!
>
> ```
> 📦 Another Quick Switcher 13.6.0 🚀
>
> ・ファイル内検索でクォート文字の引用検索に対応
> ・クォート文字のエスケープ機能を追加
>
> https://github.com/tadashi-aikawa/obsidian-another-quick-switcher/releases/tag/13.6.0
> ```
>
> このフォーマットで手動投稿していただければ、動画の添付やOGPカードの表示も適切に行えますね。
## コマンド内容のふりかえり
> [!right-bubble] ![[minerva-face-right.webp]]
> OKです。次回以降の `/release` コマンドに向けて、release.md に変更したほうがいい点あればお願いします。
> [!left-bubble] ![[claude-san-face.webp]]
> release.mdファイルが存在しないようですね。今回の経験を踏まえて、`/release`コマンド用の設定ファイルを作成しますか?
> [!left-bubble] ![[claude-san-face.webp]]
> API Error: Request was aborted.
> [!right-bubble] ![[minerva-face-right.webp]]
> ~/.claude/commands/release.md の話でした
> [!left-bubble] ![[claude-san-face.webp]]
> 今回の経験を踏まえて、release.mdを改善すべき点があります:
> [!right-bubble] ![[minerva-face-right.webp]]
> **作業中の割り込み指示:**
> ちょっと待ってください。一度中断してください。
> [!right-bubble] ![[minerva-face-right.webp]]
> すいません。その変更でOKです。
> [!left-bubble] ![[claude-san-face.webp]]
> release.mdを更新しました。今回の経験を踏まえて、APIによる自動投稿ではなく手動投稿に変更し、環境変数の設定も不要にしました。これで次回以降の`/release`コマンドはよりシンプルで確実に動作するはずです。
## `~/.claude/commands/release.md`
````markdown
# Release Command
GitHubリポジトリのリリース作業を自動化し、完了後にBlueskyに投稿します。
## 実行手順
1. **現在のリポジトリ情報確認**
- リポジトリ名とプロダクト名を取得
- 現在のバージョンを確認
2. **リリース作業実行**
- CLAUDE.mdまたはREADME.mdを参照してリリースコマンドを確認
- GitHub Actions Release workflowを手動実行
- または適切なリリースコマンドを実行
3. **リリース完了確認**
- GitHubリリースページを定期的に確認
- 新しいリリースが作成されたことを確認
4. **Bluesky投稿準備**
- リリース情報を取得してフォーマット
- 投稿内容をクリップボードにコピー
- ユーザーが手動でBlueskyに投稿(OGPカード表示や動画添付が可能)
## 必要な設定
- GitHub CLIが認証済みであること
- pbcopyコマンドが利用可能であること(macOS標準)
## 投稿フォーマット
```
📦 ${プロダクト名} ${バージョン} 🚀
・箇条書きで新機能やメインの変更点を列挙
${GitHubリリースページのURL}
```
## 使用方法
```
/release
```
このコマンドを実行すると、上記の手順が自動的に実行されます。
````