Claude Code に過去の会話や複数ターンの指示を CI / スクリプトから流し込みたい人向け
CI や夜間バッチで Claude Code に過去の会話履歴を再注入し、継続性のあるレビューや判断をさせたい場面で、claude -p --input-format stream-json と書いて使う。1往復しか送らないなら text のままで済むので書く必要はない。対話セッションでは無視されるため -p / --print 必須
--input-formatはclaude -pのプリントモードで、Claude Codeに渡す入力の形を切り替えるための指定です。値はtextとstream-jsonの2つだけ。普段のecho "..." | claude -pは何も書かなくてtext扱いになるので、わざわざ--input-formatを書くのは「多ターンの会話履歴を1ストリームで流し込みたい」場面に限られます。
用途はかなり限定的です。CIや夜間バッチで「過去のやりとりを踏まえてClaudeに作業させる」運用に踏み込んだときに初めて出番が来ます。
噛み砕くと
家電のコンセントの差し込み口を、丸ピンか平ピンか切り替えるイメージです。Claudeが受け取る側の「形」を、人間が手で打つ1行の質問と、JSONLで区切られた会話履歴の2種類から選ぶ。前者がtext、後者がstream-json。「会話履歴を再注入したい」とき以外はtextのままで困りません。
裏返すと、--input-format stream-jsonを入れる動機は「会話を1往復じゃなくて、複数ターン分まとめて渡したい」一点だけということです。1往復しか送らないなら、JSONLに包む手間がムダになります。
大事な前提:-pを付けてないと、そもそも無視される
公式の説明文も「Specify input format for print mode」と書いてあり、対話セッションには効きません。普通にclaudeを叩いて開いた対話画面で--input-format stream-jsonと書いても、-pがなければ静かに無視されるだけです。
「対話画面で過去履歴を流し込みたい」なら、それは--resumeや--continueの仕事で、--input-formatの出番ではない。ここを混ぜると延々ハマります。
「料理ブログの過去レビュー履歴を再注入する」を例に、実際の手順を見る
料理ブログの新着レシピを毎晩CIでClaudeにレビューさせる運用を組んでいるとします。過去に「写真の解像度が低い」「材料の分量表記がバラバラ」と指摘した履歴があって、新着レシピも同じ観点でチェックしたい。この継続性をどう仕込むかを、--input-format抜き → 入りで順に見ていきます。
ステップ1: 何も指定しない素のプリントモード
textがデフォルトなので、まずは1ターンだけ素で叩きます。標準入力からレシピ本文を流し込んでレビューさせる形です。
$ cat new-recipe.md | claude -p "このレシピをレビューして"
--input-formatを書いていませんが、これはtext扱いです。普通のテキストが入って、普通のテキストが返ってくる。多くのCI運用はここで止めて困らない構成です。
ステップ2: 過去の指摘を踏まえさせたい、と気づく
このやり方の弱点は、毎晩のレビューが「初対面のClaude」になっていることです。先週「写真の解像度」を指摘したことを覚えていないので、同じ指摘を新着レシピごとに最初から組み立て直すハメになります。
ここで初心者がやりがちな勘違いは、「echoで過去履歴をテキストに詰めて流し込めばいいのでは」というやつ。やってもいいんですが、量が増えるとtext1本では構造がぼやけて、Claude側がどこからどこまでが誰のメッセージかを取り違えやすくなります。
ステップ3: 過去のuserメッセージをJSONLで保存しておく
そこで過去の指摘を、改行区切りのJSONで保存します。1行1メッセージで、過去の会話を時系列に並べる形です。
$ cat past-messages.jsonl
{"type":"user","message":{"role":"user","content":"写真の解像度が720px以下のレシピは弾いて"}}
{"type":"user","message":{"role":"user","content":"材料の分量は『大さじ1』ではなく『15ml』に統一して"}}
ここで注意したいのは、このJSONの中身の細かい構造は公式のAgent SDKドキュメントを必ず見て合わせること。Claude CodeのCLIリファレンスにはstream-jsonの名称と例示しか書かれておらず、各JSONLのtypeやmessageのスキーマはAgent SDK側のドキュメントで定義されています。スキーマがズレるとparse errorで起動失敗します。
ステップ4: --input-format stream-jsonで流し込む
JSONLが用意できたら、catでclaude -pに流し込みます。今回は出力もJSONで受け取ってログ集計に使いたいので、--output-format jsonも付けます。
$ cat past-messages.jsonl new-recipe.jsonl | \
claude -p --input-format stream-json --output-format json
これでClaudeは「過去にこの2点を指摘してきた相手」として新着レシピを読み始めます。--input-formatと--output-formatは入力側と出力側で完全に独立した指定なので、片側だけstream-jsonにして反対側をtextやjsonにする組み合わせもできます。
ステップ5: 受信確認したいなら--replay-user-messages
「ちゃんと過去履歴をClaudeが受け取れたか」をログで確認したい場合、--replay-user-messagesを足します。公式CLIリファレンスの説明はこうなっています。
Re-emit user messages from stdin back on stdout for acknowledgment. Requires
--input-format stream-jsonand--output-format stream-json
つまり--replay-user-messagesを使うときは、入力側だけでなく出力側もstream-jsonにする必要がある。これを忘れて--output-format jsonのまま叩くと起動失敗します。
$ cat past-messages.jsonl | claude -p \
--input-format stream-json \
--output-format stream-json \
--replay-user-messages
ステップ6: ストリーミング系の追加機能と組み合わせる
--include-partial-messagesと--include-hook-eventsは、どちらも--output-format stream-jsonが前提です。前者はトークンが生成されるたびに途中状態を吐く指定、後者はフックの発火イベントも一緒に吐く指定。入力側のstream-jsonと直接の依存関係はありませんが、「CIで会話履歴を再注入しつつ、出力側もイベント単位で受けたい」なら自然と両側stream-jsonになります。
逆に言うと、入力stream-json+出力textの組み合わせは合法です。出力は1本のテキストで欲しい、でも入力は会話履歴を多ターン渡したい、という運用は素直に動きます。
つまり--input-formatは何をしてくれるのか
- やってくれる: プリントモードでstdinから読む内容の形を、素のテキストかJSONLのどちらかに切り替える。
stream-jsonなら多ターンの会話を1ストリームで流し込める - やってくれない: 対話セッションでは無視される。出力側の形は別に
--output-formatで指定するので、自動連動はしない。JSONLのスキーマ検証もしてくれない - 意味が薄い場面: 1往復しか送らないCI、対話セッション、
--resumeで済む単純な継続セッション
使いどころ3シナリオ(具体題材で再現)
シナリオ1: 料理ブログのレシピレビューCIで、過去の指摘ガイドを毎晩注入する
レシピ管理のプロジェクト一式で、新着レシピが追加されたら夜間バッチでClaudeにレビューさせる運用。過去6ヶ月でClaudeが指摘してきた「写真解像度」「分量表記」「アレルゲン記載漏れ」をJSONLにまとめて保存しておき、cat reviewer-history.jsonl new-recipe.jsonl | claude -p --input-format stream-json --output-format jsonで流す。Claudeは「このブログをずっと見てきたレビュー担当」のスタンスで読むようになります。CIから呼ぶときは公式推奨の--bareも足しておくと再現性が上がります。
シナリオ2: 家計簿アプリのコード自動レビューで、社内ルールを毎回再注入する
家計簿アプリのソースコード一式に対して、PRが作られるたびにClaudeにレビューさせる構成。社内コーディングルールを20項目ほどJSONLで保存しておき、毎回PR diffの前にこれを流し込みます。ルールは「any禁止」「金額計算はDecimal型」「DBアクセスは必ずトランザクション内」のような具体項目です。--append-system-promptでルールを差すやり方もありますが、ルールをuser発言の履歴として残す形にすると、ClaudeがPR内のコードを「ルールに照らして検査する」モードで読みやすくなる、というのが実際にスクリプト化してみた感触です。
シナリオ3: OSSのIssueトリアージbotで、過去のラベル付け判断を学習させる
GitHubのOSSで毎日大量に来るIssueを、Claudeにラベル付け・優先度判定させるbot。過去にメンテナが「この種のIssueはduplicate」「これはhelp-wantedにすべき」と判断したログをJSONLに蓄積しておき、新着Issueの本文と一緒に毎回流し込む。--replay-user-messagesも足して、Claudeが過去履歴を正しく受け取れたかをstream-jsonの出力で確認しながら回す構成にできます。
初心者が踏みやすい落とし穴
- 対話セッションで叩いても何も起きない。公式が「for print mode」と明記している通り、
-pか--printとセットでしか効きません - JSONLのスキーマを自己流で書くと起動失敗。公式Agent SDKドキュメントの
stream-json形式を必ず確認すること。各行のtype、message.role、message.content等の構造が定義されています。スキーマが崩れているとClaude Code起動時にparse errorで落ちます --replay-user-messagesは入出力両方stream-jsonを要求する。出力側をjsonのままにしてハマるパターンが多い。公式説明文にRequires --input-format stream-json and --output-format stream-jsonと書いてある通り- piped stdinはv2.1.128以降10MB上限。公式headlessドキュメントにpiped stdin is capped at 10MBと明記。会話履歴を貯めすぎてこれを超えると非ゼロ終了する。回避策は履歴をファイルに書き、ファイルの場所を
userメッセージで参照させる形に切り替えること textがデフォルトなので、書かなくても動く。普段のecho "..." | claude -pに--input-format textを書き足しても挙動は変わりません。書くならstream-jsonのときだけで十分- 「1往復だけだけどstream-jsonにしたい」は基本オーバーキル。1ターンならtextで困りません。
stream-jsonの存在価値は多ターンを1本のストリームで送れる点だけ --input-formatと--output-formatは独立している。入力stream-json+出力textは普通に動きます。連動するのは--replay-user-messagesのような付随機能を使うときだけ- 対話継続が目的なら
--resumeか--continueを先に検討する。Claude Codeが保持しているセッションを引き継ぐだけなら、JSONLを自作するより-cや-r <session>のほうが速い。--input-format stream-jsonが刺さるのは「Claude Codeのセッション履歴ではなく、自分で組み立てた履歴を流したい」場面です
書き方
claude -p --input-format <text|stream-json>
やってみるとこうなる
入力
cat past-messages.jsonl new-recipe.jsonl | claude -p --input-format stream-json --output-format json
出力例
{
"type": "result",
"subtype": "success",
"result": "このレシピは過去の指摘どおり写真解像度が640pxで基準未満です...",
"session_id": "...",
"total_cost_usd": 0.0123
}
このページに出てきた言葉
- プリントモード
- <code>claude -p</code>(または<code>--print</code>)で起動するモード。対話画面を開かず、入力を1回処理して結果を出して終了する。CIや<code>cron</code>での自動実行向け
- JSONL
- 1行1JSONの形式(JSON Lines)。改行で区切って複数のJSONを並べる。<code>stream-json</code>もこの形で、多ターンの会話を1本のストリームに詰め込める
- 多ターン / マルチターン
- 「人が質問→AIが返答→人がさらに質問」と複数往復する会話。1往復だけがシングルターン。<code>stream-json</code>を使う動機の中心はこのマルチターンを丸ごと渡せる点
- stdin(標準入力)
- 別のコマンドの出力やファイルの中身を、<code>|</code>(パイプ)や<code><</code>(リダイレクト)で別のコマンドに渡す経路。<code>cat file.jsonl | claude -p</code>の<code>|</code>の右側がstdinから読む側
- Agent SDK
- Claude Codeと同じツール・推論ループ・コンテキスト管理を、CLIやPython・TypeScriptから呼び出せる公式の枠組み。<code>stream-json</code>のJSONLスキーマはこちらのドキュメントに定義がある
- parse error
- 受け取った文字列をJSON等として読み解こうとして失敗するエラー。JSONLのスキーマが崩れているとClaude Codeは起動前に落ちる
- CI
- Continuous Integrationの略。GitHub Actionsなどで、コードが更新されるたびに自動でテスト・レビュー等を回す仕組み