0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Copilot CLIスキルでZenn記事14本をQiita/noteに一括変換&API自動投稿した話

0
Last updated at Posted at 2026-04-06

Zennに14本の技術記事がたまった。読まれてはいるけど、正直もったいない。Qiitaやnote.comにも出せば、もっと多くの人に届くはず——そう思いつつ、1本ずつ手作業で変換するのは現実的じゃない。

そこで GitHub Copilot CLI のカスタムスキル(cross-post-publisher)を作り、14本の記事を 28ファイル自動生成Qiita APIで14本一括投稿 まで一気にやった。この記事では、スキル設計から実行、途中で踏んだ地雷まで全部書く。

やりたかったこと

Zennに書いた記事を、Qiitanote.com にも展開したい。ただし、コピペではダメで、各プラットフォームに合わせた変換が必要になる。

項目 Zenn Qiita note.com
読者層 エンジニア エンジニア 非エンジニア含む
記法 独自(:::message等) 独自(:::note等) Markdown(制限あり)
コードブロック ✅ そのまま ✅ そのまま ❌ 非エンジニア向けに削除
テーブル ✅ パイプ ✅ パイプ ❌ 非対応 → 箇条書きに
投稿方法 Git push API or Web Web手動

ポイントは、Qiita向けはフロントマターとZenn独自記法の変換だけでいいが、note.com向けはコードブロックを削除して日本語の説明に置き換える必要があること。つまり、2種類の変換ロジックが必要になる。

cross-post-publisher スキルを作る

Copilot CLIには「スキル」というカスタム指示書の仕組みがある。.github/skills/にMarkdownファイルを置くと、AIがその指示に従って作業してくれる。

今回作ったcross-post-publisherスキルの中身を簡単にまとめると、こんな構成になっている。

Qiita変換ルール

フロントマターの変換が地味にややこしい。

## Zenn(変換前)
---
title: "記事タイトル"
emoji: "🔧"
type: "tech"
topics: ["csharp", "dotnet", "blazor"]
published: true
---
## Qiita(変換後)
---
title: "記事タイトル"
tags:
  - name: "csharp"
    versions: []
  - name: "dotnet"
    versions: []
  - name: "blazor"
    versions: []
private: false
canonical_url: "https://zenn.dev/jodycraft/articles/{slug}"
---

ここでハマりポイントが2つある。

1つ目: tagsversionsは空配列でも必須

Qiita APIに投稿するとき、versionsフィールドがないとバリデーションエラーになる。中身は空の[]でいいけど、キー自体が必要。スキルの指示書には CRITICAL マーカーをつけて明記した。

2つ目: canonical_urlを忘れると重複コンテンツ扱いされる

同じ内容をZennとQiitaの両方に公開するなら、canonical_urlでオリジナルの記事を指定しないとSEO的にまずい。これもスキルに組み込んだ。

note.com変換ルール

note.comは非エンジニアの読者も多い。だから技術記事をそのまま持っていっても読まれない。

スキルに書いた変換方針はこんな感じ:

  • コードブロック → 削除して「何をしているか」を日本語で説明
  • 技術用語 → 補足を追加(例: ClosedXML → 「Excelをプログラムから操作できる無料ツール」)
  • テーブル → 箇条書きに変換(noteはパイプテーブル非対応)
  • タイトル → 問いかけ型に変更(「C# ClosedXMLで〜」→「Excelの帳票作成、まだ手作業ですか?」)
  • ビジネス価値を前面に出す(時間削減・コスト削減の数字を強調)

実際に変換されたnote向けタイトルの例を1つ出すと:

Zennタイトル noteタイトル
Buffer GraphQL APIでSNS自動投稿パイプラインを構築した話 SNS投稿、まだ手作業でやっていますか? — 記事を書いたらInstagram・X・YouTubeに自動で投稿される仕組みを作った話

読者に刺さるのはどちらか、一目瞭然だと思う。

14本を一括変換する

スキルができたので、実行する。14本を1本ずつ変換していたら時間がかかるので、4つの並列エージェントに分割して処理した。

エージェント1: 4本(excel-vba, blazor, buffer×2)
エージェント2: 4本(closedxml, copilot-cli, ecc-vs, skill-creator)
エージェント3: 3本(remotion, winforms, cowork-01)
エージェント4: 3本(cowork-02, cowork-03, cowork-04)

4エージェントが並列で動いて、約10分で28ファイル(Qiita 14本 + note 14本)が生成完了。手作業なら1本あたり30分としても7時間はかかる作業が、スキル設計込みでも1時間程度で終わった。

Qiita APIで一括投稿

変換されたMarkdownをQiita APIで投稿する。エンドポイントはシンプル。

## 記事作成
POST https://qiita.com/api/v2/items

## 記事更新
PATCH https://qiita.com/api/v2/items/{id}

リクエストボディのJSONはこんな形式になる。

{
  "title": "記事タイトル",
  "body": "本文のMarkdown",
  "tags": [
    {"name": "csharp", "versions": []},
    {"name": "dotnet", "versions": []}
  ],
  "private": false,
  "canonical_url": "https://zenn.dev/jodycraft/articles/slug"
}

14本をまとめてPOSTした。レート制限(1,000リクエスト/時間)があるので、1秒のインターバルを入れて投稿。結果は以下の通り。

区分 本数 理由
公開(public) 6本 Zennで公開済みの記事
限定公開(private) 8本 Zennで未公開の記事(先にZennで公開してから切り替える想定)

14本すべてエラーなしで投稿完了。やったと思った。

画像URLの罠

ところが、投稿後にQiitaで記事を確認すると、画像が全部404エラーで表示されない記事があった。

原因は単純で、Zenn記事内の画像パスが /images/xxx.png という相対パスで書かれていたから。Zennではzenn-contentリポジトリのimages/フォルダに画像を置けるが、このリポジトリがプライベートだと、外部からアクセスできない。

最初は raw.githubusercontent.com のURLに置き換えてみたが、プライベートリポジトリなのでやっぱり404。

結局、Zennの公開済みページのHTMLソースから、Zenn CDNのURLを取得して置き換えた。

## Zenn CDNのURLはこういう形式
https://storage.googleapis.com/zenn-user-upload/deployed-images/{hash}.png?sha={sha}

これをQiita記事にPATCH APIで反映して、ようやく画像が表示された。

ここから得た教訓:画像を含む記事をクロスポストするなら、最初からZennの画像アップローダー経由でCDN URLを使うべき。リポジトリの/images/に置く方式は、クロスポスト時に面倒なことになる。

スキルの品質を評価する

作ったスキルがちゃんと動くか、22パターンのテストケースで評価した。

テストケースの例:

  • Zenn記事のフロントマターが正しくQiita形式に変換されるか
  • :::message:::note info に変換されるか
  • note.com向けにコードブロックが削除されて日本語説明になるか
  • canonical_urlが正しく設定されるか
  • tagsのversions配列が含まれるか

結果:22/22テスト合格(100%)

スキルに CRITICAL マーカーをつけた部分(versionsの必須、canonical_urlの追加)は、マーカーなしだと漏れることがあった。AIへの指示書は「強調の仕方」で結果が変わるというのは面白い発見だった。

最終的な成果

項目 数値
変換元の記事数 14本
生成ファイル数 28ファイル(Qiita 14 + note 14)
Qiita API投稿 14本(公開6 + 限定公開8)
並列エージェント数 4
変換にかかった時間 約10分
スキル評価スコア 100%(22/22)
手作業見積もり 7時間以上

この仕組みを使い回すには

同じことをやりたい人向けに、スキル設計のポイントをまとめておく。

1. プラットフォーム差分をテーブルで整理する

最初にZenn/Qiita/noteの記法差分をテーブルにまとめてからスキルを書くと、漏れが減る。私は途中で差分に気づいて何度かスキルを修正したので、最初からやっておけばよかった。

2. CRITICALマーカーを使う

AIが見落としがちな重要ルール(versionsの必須、canonical_url)には、スキル内で CRITICAL という単語をつけて強調する。これだけで遵守率が目に見えて上がった。

3. 並列実行で時間を短縮する

14本を1本ずつ処理すると30分以上かかるが、4並列にすると10分で終わる。Copilot CLIではtaskツールで複数のバックグラウンドエージェントを起動できる。

4. APIレート制限を意識する

Qiita APIは1,000リクエスト/時間の制限がある。14本程度なら問題ないが、大量投稿するなら1秒以上のインターバルを入れること。

note.comは手動投稿になる

ここまで自動化した話を書いてきたが、note.comにはAPIがない(2026年3月時点)。生成された14本のMarkdownを、Web画面から1本ずつ手動で貼り付ける必要がある。

とはいえ、コンテンツの変換自体は自動化できているので、手動作業は「貼り付けて公開ボタンを押す」だけ。これは許容範囲だと思っている。

まとめ

Copilot CLIのカスタムスキルを使えば、Zenn記事を複数プラットフォームに展開する作業を大幅に効率化できる

今回学んだこと:

  1. スキル設計が8割 — 変換ルールを正確にスキルに書けば、あとはAIが勝手にやってくれる
  2. CRITICALマーカーの効果 — AIへの指示書は「書き方」で結果が変わる
  3. 画像URLは最初から外部URLを使う — プライベートリポジトリの相対パスはクロスポスト時に詰む
  4. 並列処理で10倍速 — 14本を4並列で処理して約10分

コンテンツを1つのプラットフォームに閉じ込めておくのは機会損失。変換の手間をAIに任せて、自分は新しい記事を書くことに時間を使う——これが今の私のスタイルになった。


📝 この記事は Zenn で最初に公開されました。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?