前回はWP-CLIを都度実行方式でラップし、Claude Codeからssh経由で下書き投稿できるようにするところまでをお届けしました。今回はその周辺、「ネタをどう受け渡すか」と「画像をどう埋め込むか」で実際に転んだ話です。技術的には地味な話が多いですが、同じ構成を検討している人には割と参考になるはずです。
ネタ出しの受け渡し:Google Driveを間に挟む
記事のネタは、普段使いのclaude.ai側(チャット・プロジェクト機能)で思いつくことが多いのですが、これをどうClaude Code側に連携するかが最初の課題でした。jagha上にネタ帳ファイルを置いてSSHクライアントアプリ経由で書き込む案や、claude.aiプロジェクトへ手動でコピペする案も検討しましたが、しっくりこず。最終的に落ち着いたのは、Google Driveのフォルダを間に挟む方式です。
- Google Drive上に
マイドライブ/claude BLOGフォルダを用意 - claude.aiプロジェクト側にこのフォルダへの書き込みを許可
- Google Drive for Desktopでこのフォルダを作業PCにローカル同期
- Claude Code(ローカルセッション)は同期されたローカルパスをそのままRead/Bashで参照できる
手動コピペを挟まずに、claude.ai側の書き込みをClaude Codeが直接読みにいける状態になりました。ネタファイルの命名規則はYYYYMMDD-短い見出し.md、1ネタ1ファイル。処理済み(記事化して下書き投稿まで済んだ)ネタはposted/サブフォルダに移動し、投稿IDと編集URLを追記する運用にしています。
実際に動かしてみると、claude.aiプロジェクト側の相棒がネタファイルを書き出し → Claude Code側が読み取って実機と事実確認(SSH設定やプラグイン名などの整合性チェック)→ 記事化 → wp-autopost.shで下書き投稿 → ネタファイルをposted/に移動、という一連の流れが問題なく機能することを確認できました。
画像を入れたい:featured_imageとinline_images
手持ちの写真や技術記事用のSVG図解を投稿に入れたい、という要望に応えるため、wp-autopost.shにJSON payloadで指定できるオプションを追加しました。
{
"title": "...",
"content": "本文...\n\n(プレースホルダ)\n\n続き...",
"status": "draft",
"inline_images": [{"placeholder": "(プレースホルダ)", "filename": "photo.jpg", "alt": "..."}]
}
画像ファイルは呼び出し側が事前にscpでjagha上のステージングディレクトリに置いておき、スクリプトが処理後にそのディレクトリを毎回空にする方式です。ここから、いくつかの壁にぶつかりました。
ハマりどころその1:SVGアップロードの権限エラー
SVGをメディアライブラリにアップロードしようとすると、「このファイルタイプをアップロードする権限がありません」というエラーで弾かれました。原因は、SVGアップロードを許可しているsvg-supportプラグイン側の権限チェック(管理者権限相当)を、WP-CLI実行時のユーザーコンテキストが満たしていなかったことです。wp media importに--user=1(WordPress側の管理者ユーザーとして実行する指定。dockerの--userオプションとは別物です)を付けることで解決しました。
ハマりどころその2:権限600問題
Google Driveから取得した画像ファイルは-rw-------(600)権限のことがあり、scpでステージングした後もコンテナ内のwww-data(uid33)から読めずPermission deniedになることがありました。chmod 644を挟むことで解決。地味に踏み抜きやすいポイントなので、ネタ受け渡しの運用ルールにも「元画像は他人も読めるパーミッションで置く」という注記を追加しています。
ハマりどころその3:アイキャッチが全幅バナーになる事件
featured_image(アイキャッチ)を設定した投稿で、画像が記事幅に対して明らかに大きすぎるという指摘を受けました。原因を追うと、このテーマ(catch-base)のアイキャッチ機構はそもそも記事上部に出る全幅バナー用の仕組みで、表示サイズはサイト全体のCustomizer設定(featured_image_size)で決まる作り。現在の設定値full(原寸大)がそのまま反映されていました。本文中のinline画像側にも、実は<img>タグにサイズ指定が一切なく、元写真の解像度(4032×3024など)そのままで埋め込まれていたことも判明。inline側はstyle="max-width:50%; height:auto; display:block; margin:0 auto;"を追加して解決しましたが、アイキャッチについては「サイト全体の表示設定を変える」か「この用途では使わない」かの二択になり、今回は後者を選択。当面featured_imageオプションは使わず、本文中のinline画像だけで運用する方針にしました。
ヒヤリハット:画像の元ファイルを控えなく削除
ネタ処理後、mdファイルはposted/にアーカイブする運用にしていたのですが、ある回で添付写真の元ファイルを控えを残さずrmで消してしまいました。WordPressのメディアライブラリにはアップロード済みで実害はなかったのですが、元ファイルの単体控えが手元から失われる事故です。以降は「添付画像がある場合は画像もposted/にコピーしてから元を削除する」という運用ルールに直し、再発防止を図りました。
「正本」をjagha側に一元化する
ここまでの運用ルール(投稿手順、画像埋め込みのお約束、ネタの受け取り方)が積み上がってくると、次に問題になったのがルールの置き場所です。CLAUDE.mdはClaude Codeの作業ディレクトリとその親を辿って自動読み込まれる仕組みなので、「jagha上に置くだけ」だと、jagha上で直接Claudeを起動した場合にしか自動適用されません。今回のように手元のPCからsshでjaghaを遠隔操作する形だと、jagha側のCLAUDE.mdは自動では読まれない上、複数PCを使い分けているとルールをどこか1箇所にベタ書きした場合、他のPCやセッションでは反映されない・古くなるリスクもあります。
そこで採用したのが、「正本はjagha側の1ファイルに集約し、手元PC側のCLAUDE.mdには『作業前に必ずこれを読みに行け』という指示だけを書く」という方式です。実はこの記事自体も、この仕組みに沿って作業前にjagha側の正本を読みに行った上で書いています。運用ルールを更新したくなったら、jagha側の1ファイルを直すだけでどのPC・どのセッションからでも最新のルールに追従できる、というわけです。
今のところの運用と、残っている宿題
今のところは、Claude Codeが本文と画像を用意してwp-autopost.shで下書き投稿し、管理画面でユーザーがレビュー・仕上げ・公開する、という手動レビュー付きのフローで落ち着いています。status値のバリデーションや、カテゴリ・タグへの対応はまだ手つかずですが、必要になったタイミングで追加していく予定です。この2回に渡ってお届けした自動投稿の仕組み自体、実はこの記事を投稿するのにも使われています。
claude codeによる鯖管理シリーズ、メモも兼ねて一気に書き上げた(書き上げて貰った)らもう既にネタ切れです!
また暫く休眠かな・・・w