Claude Codeの /loop と /goal 入門:「時間で繰り返す」と「終わるまで走らせる」を実機で使い分ける【2026年9月版】

AI入門

はじめに:「まだ終わらない?」「続けて」を、Claude Code に任せる

Claude Code(ターミナルで動く Anthropic 公式の AI コーディングツール)を使っていると、次のような「待ち」と「催促」が意外と多いことに気づきます。

  • ビルドやデプロイが終わるまで、数分おきに画面を見に行く
  • 「テストを直して」と頼み、1回で終わらなければ「まだ落ちてるよ、続けて」と何度も送る
  • PR(プルリクエスト)のレビューを、毎回同じ手順で頼む

Claude Code には、この2種類の繰り返しをそれぞれ任せるコマンドがあります。決めた時間ごとに同じ指示を実行する /loop と、条件を満たすまでターンを続けさせる /goal です。名前は似ていますが、「次の回を何が始めるか」がまったく違います。

/loop は時間で、/goal は評価モデルの判定で次の回へ進むことを左右に並べて比較する図

図1:/loop は「時間」、/goal は「条件の判定」で次の回が始まる

この記事では、公式ドキュメント(日本語版を含む)の記載を確認したうえで、このMacで実際に両方を動かしました。スクリーンショットと画面の録画はすべて今回の実行結果です。

この記事で分かることは次の4つです。

  • /loop の3つの書き方(固定間隔・おまかせ間隔・引数なし)と、実際の動き方
  • /goal が「終わった」と判断する仕組みと、うまく止まる条件の書き方
  • ヘッドレスモード(対話せずに1回実行するモード)と組み合わせた、手元でのレビュー自動化
  • クラウドの Routines やデスクトップアプリの予定タスクとの使い分け

想定読者は、Claude Code を一度はインストールして動かしたことがある方です。シェルの基本操作(cd やパイプ |)が分かれば読み進められます。

背景:2つのコマンドの位置づけ

/loop は「セッションの中の定期実行」

/loop は Claude Code に同梱されたスキル(スラッシュコマンドとして呼べる機能)です。2026年3月の v2.1.71 で追加され、その後、間隔を Claude が選ぶモードや loop.md による既定の指示が加わりました。別名の /proactive でも呼べます。

大事なのは、今開いている会話(セッション)の中だけで動くことです。ターミナルを閉じれば止まります。PCを閉じても動き続けてほしいなら、後述する Routines(クラウド)やデスクトップアプリの予定タスクを使います。

/goal は「ゴールまでの自走」

/goal は 2026年5月の v2.1.139 で追加されたコマンドです。条件を1つ渡すと、Claude が作業し、ターンが終わるたびに別の小さなモデルが「条件を満たしたか」を判定します。まだなら、あなたの承認を待たずに次のターンが始まります。

公式ドキュメントでは、/goal の実体は「セッション限定の、プロンプト型の Stop フック」と説明されています。Stop フックとは、Claude がターンを終えるたびに実行される仕組みのことです。自分で設定ファイルにフックを書かなくても、/goal の1行で同じことができる、と考えると分かりやすいでしょう。

項目/loop/goal
次の回が始まるとき時間がたったとき前のターンが終わり、評価モデルが「まだ」と判定したとき
止まるとき自分で止める/Claude が完了と判断/7日で失効条件を満たした/不可能と判定//goal clear
1セッションで持てる数最大50件(予定タスク全体)1つ
追加されたバージョンv2.1.71v2.1.139

準備:練習用のプロジェクトを作る

検証した環境

今回の検証環境は次のとおりです。画面の数値や表示は、この環境での結果です。

項目内容
OSmacOS(Apple Silicon)
Claude Codev2.1.280(ネイティブ版)
モデルOpus 5.5(effort は --effort medium で medium に指定)
権限モードauto モード(ツールの実行を分類器が自動で承認するモード)
Node.jsv22.23.2
検証日2026年9月25日

/loop は v2.1.71 以降、/goal は v2.1.139 以降で使えます。バージョンは次のコマンドで確認できます。

# インストール済みの Claude Code のバージョンを確認する
claude --version

練習用フォルダを作る

わざとバグを入れた小さなレシート計算ライブラリと、約3分かかる「ビルドのふり」をするスクリプトを用意します。ターミナルで次のブロックをそのまま貼り付けてください。ホームフォルダに loop-goal-lab が作られます。

# 練習用フォルダを作って移動する
mkdir -p ~/loop-goal-lab/{src,test,scripts,inbox,done,notes,.claude}
cd ~/loop-goal-lab

# テストは Node.js 標準のテストランナー(node --test)で動かす
cat > package.json <<'EOF'
{ "name": "receipt-lab", "version": "1.0.0", "type": "module", "scripts": { "test": "node --test" } }
EOF

# わざとバグを3つ入れたレシート計算
cat > src/receipt.js <<'EOF'
// レシート計算の小さなライブラリ(検証用にわざとバグを入れてある)

// 小計: items は [{ name, price, qty }]
export function subtotal(items) {
  return items.reduce((sum, it) => sum + it.price, 0);
}

// クーポン: { type: "percent", value: 10 } は10%引き、{ type: "yen", value: 300 } は300円引き
export function applyCoupon(amount, coupon) {
  if (!coupon) return amount;
  if (coupon.type === "percent") return amount - amount * coupon.value;
  if (coupon.type === "yen") return amount - coupon.value;
  return amount;
}

// 税込み金額: 1円未満は切り捨て
export function withTax(amount, rate = 0.1) {
  return Math.round(amount * (1 + rate));
}
EOF

# 仕様を表すテスト(6件のうち4件が落ちる状態から始める)
cat > test/receipt.test.js <<'EOF'
import { test } from "node:test";
import assert from "node:assert/strict";
import { subtotal, applyCoupon, withTax } from "../src/receipt.js";

test("小計は 単価×個数 の合計", () => {
  assert.equal(subtotal([{ name: "パン", price: 100, qty: 3 }, { name: "牛乳", price: 250, qty: 2 }]), 800);
});
test("商品がないときの小計は 0", () => assert.equal(subtotal([]), 0));
test("10%引きクーポン", () => assert.equal(applyCoupon(1000, { type: "percent", value: 10 }), 900));
test("値引きで 0 円未満にはならない", () => assert.equal(applyCoupon(200, { type: "yen", value: 300 }), 0));
test("税込み(10%)", () => assert.equal(withTax(1000), 1100));
test("税込みの 1 円未満は切り捨て", () => assert.equal(withTax(999), 1098));
EOF

# 約3分かけて build.log に進み具合を書き、最後に BUILD SUCCESS と書く「ビルドのふり」
cat > scripts/fake-build.sh <<'EOF'
#!/bin/bash
LOG=build.log
: > "$LOG"
for step in "依存関係を解決中" "TypeScript をコンパイル中" "画像を最適化中" "バンドルを作成中" "テストを実行中"; do
  echo "$(date +%H:%M:%S) [build] $step..." >> "$LOG"
  sleep 36
done
echo "$(date +%H:%M:%S) [build] BUILD SUCCESS (5/5 steps)" >> "$LOG"
EOF
chmod +x scripts/fake-build.sh

# git の管理下に置く(後半のレビュー演習で使う)
printf 'build.log\nnode_modules/\n' > .gitignore
git init -q -b main && git add -A && git commit -qm "初期状態(バグ入り)"

# テストを実行して、4件落ちることを確認する
npm test 2>&1 | tail -8

最後の出力に # pass 2 と # fail 4 が出ていれば準備完了です。このフォルダで claude を起動すると、初回は「このフォルダを信頼するか」を聞かれるので、Yes, I trust this folder を選びます。/goal はフックと同じ信頼ルールで動くため、信頼していないフォルダでは使えません。

第1部 /loop:時間で繰り返す

書き方は3パターン

入力欄で /lo まで打つと、候補に /loop が出てきます。説明文に「間隔を省略するとモデルが自分でペースを決める」と書かれているとおり、渡すもので動き方が変わります。

入力欄に /lo と打ったときの候補一覧。/loop の説明が表示されている

画面1:/loop の候補表示(v2.1.280)

3つのパターンを図にまとめます。上から順に強調されるので、入力例と動き方を対応させて見てください。

/loop の3つの書き方(間隔+プロンプト、プロンプトだけ、なし)と、それぞれの動き方を並べた図

図2:/loop [間隔] [プロンプト] の3パターン

プロンプトの代わりにスキルを渡すこともできます(例:/loop 20m /review-pr 1234)。ただし /model や /clear のような組み込みコマンドや、Claude が自分で呼び出せない設定のスキルは、実行されずにただの文字として渡されます。

実験1:1分ごとにビルドを見張る(固定間隔)

まずは一番基本の「間隔+プロンプト」です。別のターミナルタブで「ビルドのふり」を始めておきます。

# 別のタブで実行する(約3分で終わる)
cd ~/loop-goal-lab && ./scripts/fake-build.sh

次に Claude Code の入力欄で、1分ごとの確認を頼みます。最後に「終わったらループを止めて」と書いておくのがポイントです。

/loop 1m build.log の最後の行を確認して、進み具合を1行で報告して。BUILD SUCCESS か FAILED が出ていたら結果をまとめて、このループを止めて

Enter を押すと、Claude は CronCreate というツールで予定を登録しました。*/1 * * * * は cron(Unix 系の定期実行の書き方)で「毎分」の意味です。登録直後に1回目の確認もすぐ行われました。

CronCreate で毎分のジョブ 4a05ea05 が登録され、7日で自動停止すること、最初の確認結果が表示されている画面

画面2:登録された予定には8文字の ID(ここでは 4a05ea05)が付く

画面下に「このセッションを閉じるまで動く。クラウドで続けたいなら /schedule」という案内が出ている点にも注目してください。/loop がセッション単位の機能であることが、画面からも分かります。

ここから先は、何もせずに待つだけです。約5分間の画面を、変化のあった場面だけつないで録画にしました。

1分ごとに「Running scheduled task」が走り、進み具合を1行ずつ報告し、最後に自分でループを止めるまでの録画

録画1:/loop 1m の実行(同じ画面が続く待ち時間は省略)

時刻の流れを1本の線にすると、次のようになります。ビルドの各段階と、ループの各回がどう重なったかを見てください。

12:40 から 12:44 までのビルドの進み具合と、毎分の確認、最後の CronDelete の位置を並べたタイムライン

図3:実測のタイムライン。5回目の確認で成功を見つけて自分で止まった

5回目の確認でビルドの成功を見つけると、Claude は CronDelete で自分のジョブを削除し、PushNotification でターミナルに完了通知も送りました。続けて「いま予定されているタスクを一覧して」と頼むと、CronList の結果は No scheduled jobs でした。

CronDelete でジョブを取り消し、通知を送り、ビルド結果をまとめた後、CronList が No scheduled jobs を返した画面

画面3:ループの終了と、予定が残っていないことの確認

実際に動かして分かったことを3つ挙げます。

  • 待っている間も入力欄は空いている。 1回の確認は4〜12秒で終わり、その間以外は別の依頼を普通に打てる
  • 止め方を最初に書いておくと、自分で片付けてくれる。 固定間隔のループは、何もしなければ7日間続く
  • Claude が作業中のときは後回しになる。 公式ドキュメントによると、予定の時刻が来てもターンの途中では割り込まず、終わってから実行される。取りこぼした回をまとめて実行することはない

間隔の書き方と、時刻がずれる仕組み

間隔の書き方には細かいルールがあります。公式ドキュメントの記載をまとめると次のとおりです。

項目内容
単位s(秒)・m(分)・h(時間)・d(日)
書く位置先頭に 30m、または末尾に every 2 hours のような文
秒の扱いcron の最小単位が1分なので、分に切り上げ
割り切れない間隔7m や 90m は近いきれいな間隔に丸め、どれにしたかを Claude が伝える
時刻のずれ(ジッター)繰り返しの予定は最大30分(1時間より短い間隔なら最大で間隔の半分)遅れて実行される。全員が同じ時刻に API を呼ばないための仕組み
タイムゾーンPC のローカル時刻で解釈
寿命繰り返しの予定は作成から7日で最後に1回実行して消える

「9時ちょうどに」のように時刻が大事な処理では、ジッターがあることを覚えておきましょう。公式ドキュメントでは、:00 や :30 ではない分(例:9時3分)を指定する方法が紹介されています。

実験2:間隔を Claude に任せる(セルフペース)

次は間隔を書かずに頼みます。この場合 Claude は、回が終わるたびに1分〜1時間の範囲で次の待ち時間を決めます。

最初の試行では、Claude はポーリング(一定間隔で見に行く方式)ではなく Monitor ツールでファイルを見張る方法を選びました。公式ドキュメントにも「Monitor が使える環境では、ポーリングの代わりに Monitor を使うことがある」と書かれています。ただしこの試行では、ビルド完了の行を Monitor が出力した後も、6分ほど待つ間にセッションが自動で再開しませんでした。操作を疑似端末(プログラムから動かす端末)で行ったことが影響している可能性もあり、原因は特定できていません。

そこで、待ち時間を決める動きを観察するため、Monitor を使わないよう指示して試し直しました。

/loop build.log を見て、ビルドが終わるまで見守って。Monitor ツールは使わず、毎回待ち時間を決めて確認して。終わったら結果を1行で教えて
セルフペースの /loop の2回目で「75秒後に再確認」と決め、自動で再開した3回目でビルド成功を報告して止まった画面

画面4:2回目と3回目の様子。「Claude resuming /loop wakeup」の行が、自動で再開した印

セッションの記録を見ると、Claude は ScheduleWakeup というツールに待ち秒数と理由を渡していました。1回目はビルドが始まったばかりなので90秒、2回目は「各段階が約36秒で進んでいる」ことに気づいて75秒、3回目で成功を見つけると次の予約をせず stop: true で終了しています。

3回の確認と、その間に Claude が選んだ待ち時間(90秒、75秒、stop)と理由を並べた図

図4:セルフペースでは、観察した内容から次の待ち時間を決める

セルフペースには次のような特徴があります。

  • 待ち時間と理由が毎回の終わりに表示されるので、なぜその間隔なのかを追える
  • 次の予約も停止もしないまま回が終わると、約20分後に予備の起動が1回だけ入る。そこでも予約がなければ終わる
  • 固定間隔と違ってジッターはかからないが、7日の寿命は同じ

実験3:loop.md で「いつもの見回り」を決めておく

引数なしの /loop(または間隔だけの /loop 15m)を実行すると、組み込みの見回り指示が動きます。公式ドキュメントによると内容は、「会話に残っている未完了の作業を続ける」「今のブランチの PR のレビューコメント・失敗した CI・競合に対応する」「何もなければバグ探しや整理をする」の順です。push や削除のような取り消せない操作は、会話ですでに許可された作業の続きのときだけ行います。

この組み込み指示は、loop.md というファイルで自分用に置き換えられます。置き場所は2つで、両方あればプロジェクト側が優先されます。

パス範囲
.claude/loop.mdそのプロジェクトだけ(優先)
~/.claude/loop.md自分のすべてのプロジェクト

今回は「受信フォルダの新着メモを1行に要約して、処理済みフォルダへ移す」見回りを書きました。

# プロジェクト用の loop.md を作る
cat > ~/loop-goal-lab/.claude/loop.md <<'EOF'
inbox/ フォルダを確認してください。

- 新しい .txt があれば、1ファイルにつき1行の要約を notes/summary.md の末尾に追記し、そのファイルを done/ へ移す。
- 何もなければ「新着なし」と1行だけ答える。
- inbox/・done/・notes/ 以外のファイルは変更しない。
EOF

入力欄で /loop 1m とだけ打つと、登録された予定の中身が <<loop.md>> と表示され、ファイルの指示で動くことが分かります。その後、inbox/ に会議メモと問い合わせメモを1つずつ置きました。

/loop 1m を実行し、inbox に置いたメモが1分以内に要約されて done に移される様子の録画

録画2:loop.md の見回り。置いたファイルが次の回で処理される

2件目の問い合わせメモにはバグ報告が書かれていましたが、Claude は「ループのルールで inbox/・done/・notes/ 以外は変更しないため、修正は行っていません」と答えました。見回りの指示に「やらないこと」を書いておくと、定期実行が勝手に作業を広げないことが確認できます。

loop.md を書き換えると、次の回から新しい内容が使われます。長すぎる内容(25,000バイトを超えた部分)は切り捨てられるので、短く保ちましょう。

ループの止め方と寿命

固定間隔のループは、止めたいときに言葉で頼めば CronDelete で消してくれます。

「このループを止めて」と頼むと CronDelete でジョブ 8d9f1cd9 が取り消され、処理した件数が報告された画面

画面5:固定間隔のループは「止めて」と頼んで止める

止め方と寿命を表に整理します。「Esc で止まる」のはセルフペースの待機中だけ、という点がつまずきやすいところです。

やりたいこと方法
セルフペースのループを止める待機中に Esc(保留中の次回が消える)
固定間隔のループを止める「このループを止めて」と頼む(CronDelete)
予定の一覧を見る「予定されているタスクを一覧して」(CronList)
ターミナルを閉じた後に再開するclaude --resume か --continue。固定間隔の予定は戻るが、セルフペースは戻らないので /loop を打ち直す
スケジューラ自体を無効にする環境変数 CLAUDE_CODE_DISABLE_CRON=1

もう1つ、コストの注意点があります。公式ドキュメントのコストのページには、予定タスクはセッションが待機中でも間隔ごとに実行され、そのたびに会話全体のコンテキストを送ると書かれています。長い会話で短い間隔のループを回すと、1回ごとの消費が大きくなります。Pro/Max などのプランでは /usage の Loops 欄で、ループごとの実行回数とトークン数を確認できます(v2.1.242 以降)。

第2部 /goal:条件を満たすまで走らせる

仕組み:作業するモデルと、判定するモデルが別

/goal を使う前に、中で何が起きているかを押さえておきましょう。図の強調が移っていく順に、1ターン分の流れを追ってください。

/goal 入力、ターンNの作業、会話に出た内容、評価モデルの判定、「まだ」なら次のターンへ戻る、達成なら解除、というループの図

図5:/goal の評価ループ

ポイントは、評価モデルは会話に出た文字しか読まないことです。評価モデル(Claude API の既定は Haiku)はファイルを開いたりコマンドを実行したりしません。そのため、テストが通ったかどうかは、Claude が npm test を実行して結果を会話に表示して初めて判定できます。

判定は3種類で、それぞれ短い理由が付きます。

判定何が起きるか
まだ(Not yet met)理由を手がかりに、承認なしで次のターンへ
達成(Met)ゴールを自動で解除し、達成を会話に記録
不可能(Impossible)ゴールを解除し、理由とともに失敗を記録

/goal の基本コマンドは3つです。

コマンド役割
/goal <条件>ゴールを設定し、すぐに1ターン目を始める(条件は最大4,000文字)
/goal今のゴールの状態(経過時間・ターン数・トークン・直近の判定理由)を見る
/goal clearゴールを途中で解除する。stop・off・reset・none・cancel も同じ意味

実験4:テストが全部通るまで直してもらう

練習用フォルダの4件落ちているテストを、/goal で直してもらいます。条件には「何をもって完了とするか」「どう確かめるか」「何を変えてはいけないか」「いつ諦めるか」を入れました。

/goal npm test が exit 0 で終わり、その結果(pass と fail の件数)を会話に表示した状態にする。test/ 配下のファイルは変更しない。20ターンを超えたら止める

Enter を押すと、画面右下に ◎ /goal active (9s) のような表示が出て、経過時間が数えられます。今回は1ターン目で3つのバグをすべて直し、26秒・1ターンで達成と判定されました。

3つのバグを直して npm test が pass 6 / fail 0 になり、Goal achieved (26s · 1 turn · 1.7k tokens) と表示された画面

画面6:1ターンで達成した例。「Goal achieved」の行に時間・ターン数・トークン数が出る

このように、Claude が1ターンで終えられる規模の作業なら、/goal を使っても使わなくても結果は同じです。/goal が役に立つのは、1ターンでは終わらず「続けて」と言いたくなる作業です。

実験5:評価ループを目で見る

評価の様子を観察するため、条件に「1ターンで直すバグは1つだけ」という制約をわざと加えて、もう一度試しました(直した後のコードは元に戻しています)。

/goal npm test が exit 0 で終わり、その pass/fail 件数を会話に表示した状態にする。ただし動きを観察するため、1ターンで直すバグは1つだけにして、ターンの終わりに毎回 npm test の件数を表示する。test/ 配下は変更しない。10ターンを超えたら止める

ターンが終わるたびに Goal not yet met… continuing と表示され、誰も入力していないのに次のターンが始まります。pass の件数が 3 → 4 → 5 → 6 と増えていき、4ターン目で達成しました。

1ターンごとに1つずつバグを直し、Goal not yet met と表示されながら4ターン目で Goal achieved になるまでの録画

録画3:4ターン・約1分で達成(同じ画面が続く部分は省略)

実行中に /goal とだけ打つと、状態が表示されます。Last check: の行が、評価モデルが直前に出した判定の理由です。

Goal active の状態表示。running 23s・1 turn・965 tokens と、Last check として fail 3 件のため未達という理由が表示されている

画面7:/goal の状態表示。直近の判定理由が読める

達成後に Ctrl+O(詳細表示の切り替え)を押すと、最終判定の理由も英語で読めます。条件の各部分(exit 0、件数の表示、1ターン1修正、test/ を変更していないこと、10ターン以内)を1つずつ会話の内容と照らし合わせていることが分かります。

Ctrl+O の詳細表示で、Goal achieved の Reason として条件の5つの部分がそれぞれ満たされた根拠が列挙されている画面

画面8:達成と判定した理由。会話に出た「exit=0」や「Changed files」を根拠にしている

一方で、気になる点も見つかりました。画面7の途中判定の理由には、「『10ターンを超えたら止める』まで続ける必要がある」と書かれています。条件の上限を「10ターンまでは続けるべき」と読んだようです。最終的には4ターンで正しく達成と判定されましたが、上限の書き方は、誤読されない形にしておくほうが安全です。

条件の書き方:4つの要素

公式ドキュメントは、長いターンでも崩れない条件の要素として「1つの測定できる終わりの状態」「確かめ方の明記」「守るべき制約」の3つを挙げています。これに、同じページで勧められている「ターン数か時間の上限」を加えると、次の4要素になります。

あいまいな条件と判定できる条件を比べ、判定できる条件の中の4つの部分を順番に強調する図

図6:判定できる条件は4つの部分からできている

上の実験を踏まえると、上限は次のように「止まったときに何をするか」まで書くと誤読されにくくなります。

/goal npm test が exit 0 で終わり、pass/fail 件数を会話に表示した状態にする。test/ 配下は変更しない。10ターン以内に達成できなければ、作業をやめて原因を報告する

「使うかどうか」の判断には、次の3つの問いが役に立ちます。

  1. 終わりを会話の中で証明できるか。 テスト結果、終了コード、件数、grep の結果のように、Claude が表示できる証拠があるか。「きれいにする」「良くする」のような主観的な条件は避ける
  2. 次の一手が明らかか。 「落ちているテストを直す」なら毎ターンやることがはっきりしている。「品質を上げる」は手探りのループになりやすい
  3. 1ターンで終わりそうではないか。 画面6のように1ターンで終わる作業なら、普通に頼むのと変わらない

状態確認と途中での解除

/goal は、途中でやめることもできます。次の画面では、README に節を追加するゴールを設定した直後に Esc で作業を中断し、/goal clear で解除しました。

ゴール設定後に Esc で中断し、/goal clear を実行すると Goal cleared: と条件が表示された画面

画面9:/goal clear で解除すると「Goal cleared:」と条件が表示される

Esc で中断しただけでは、ゴールは残ります(中断直後に /goal を打つと ◎ Goal active のままでした)。完全にやめたいときは /goal clear まで実行しましょう。/clear で新しい会話を始めた場合もゴールは消えます。

ほかにも、公式ドキュメントに次のような仕様が書かれています。

  • 権限モードは変わらない。 /goal はターンごとの催促をなくすだけで、ツールの承認は今の権限モードのまま。無人で回すなら auto モードで使う(公式の表現では「auto モードはツールごとの確認を、/goal はターンごとの確認をなくす」)
  • 再開すると条件は戻る。 --resume や --continue で再開すると有効だったゴールが復元される。ただしターン数・時間・トークンの数え直しになる
  • 進展がないと止まる。 ツールを使わない返答が何ターンも続くと、警告を出してあなたに操作を返す
  • エラーの種類で扱いが違う。 認証切れやクレジット不足のように自分で直す必要があるエラーではゴールが解除され、通信断のような一時的なエラーでは自動で再試行される(対話モード、v2.1.269 以降)

第3部 ハンズオン:手元でレビューを自動化する

ここからは、ヘッドレスモード(claude -p、対話せずに結果だけを出力するモード)と /goal を組み合わせて、差分のレビューを自動化します。全体の流れは次の図のとおりです。

git diff を claude -p にパイプで渡し、結果を review.md に保存し、人が確認してから gh pr comment で投稿する流れと、/goal を使った発展形の図

図7:差分 → Claude → ファイル → 人の確認 → 投稿

今回は GitHub には投稿せず、ローカルのブランチ同士の差分で試しました。GitHub の PR で行う場合は、git diff の部分を gh pr diff <PR番号> に置き換えます(GitHub CLI のインストールと gh auth login が必要です)。

STEP1:レビュー対象のブランチを作る

会員ポイントを計算する新機能を、わざと問題を入れて feature/points ブランチに追加します。

cd ~/loop-goal-lab
git switch -c feature/points

# わざと問題を入れた新機能(デバッグ用ログ、会員でないと落ちる、端数、無駄な二重ループ、HTML への埋め込み)
cat > src/points.js <<'EOF'
// 会員ポイント(新機能)

// 支払額に応じてポイントを付ける。ゴールド会員は5%、それ以外は1%
export function earnPoints(total, member) {
  console.log("earnPoints", total, member);
  const rate = member.rank == "gold" ? 0.05 : 0.01;
  return total * rate;
}

// 会員一覧から ID で探す
export function findMember(members, id) {
  for (const m of members) {
    for (const n of members) {
      if (m.id === id) return m;
    }
  }
}

// 会員バッジの HTML
export function renderBadge(member) {
  return `<span class="badge">${member.name} 様</span>`;
}
EOF
# 新しいファイルだけをコミットする(loop.md や inbox などは含めない)
git add src/points.js && git commit -qm "会員ポイントを追加"
git switch main

# main との差分を確認する
git diff main...feature/points --stat

STEP2:差分を Claude に流してレビューしてもらう

レビューの依頼文を変数 ASK に入れておき、差分をパイプ(|)で claude -p に渡します。パイプで渡した内容は、プロンプトと一緒に Claude へ届きます。

ASK="この差分をレビューして、指摘を重要度つきの箇条書きで挙げて。日本語で簡潔に"
git diff main...feature/points | claude -p "$ASK"

約20秒で、次のようなレビューが返ってきました。わざと入れた問題(XSS、端数、会員でないときのエラー、二重ループ、ログの消し忘れ)がすべて指摘されています。

git diff を claude -p に渡した結果。XSS、ポイントの端数、非会員で落ちる、二重ループ、console.log の消し忘れなどが重要度つきで指摘されている

画面10:ヘッドレスモードでのレビュー結果

XSS(クロスサイトスクリプティング)とは、利用者が入力した文字列に仕込まれたスクリプトが、そのままページで実行されてしまう脆弱性のことです。

STEP3:ファイルに保存する・JSON で受け取る

結果をファイルに保存すれば、後で読み直したり、ほかのコマンドに渡したりできます。

# 結果を review.md に保存する
git diff main...feature/points | claude -p "$ASK" > review.md && wc -l review.md
      15 review.md

プログラムで扱いたい場合は、--output-format json を付けて JSON で受け取ります。jq(JSON を整形・抽出するコマンド)で必要な項目だけを取り出してみます。

git diff main...feature/points | claude -p "$ASK" --output-format json \
  | jq '{type, subtype, is_error, num_turns, duration_ms, total_cost_usd, result: (.result[0:60] + "…")}'
--output-format json の結果を jq で抜き出した画面。subtype が success、num_turns が 1、duration_ms が 12958、total_cost_usd が約 0.14

画面11:JSON で受け取ると、成否・所要時間・費用の目安も取れる

本文は result に入っているので、本文だけが欲しいときは jq -r '.result' で取り出せます。total_cost_usd は API 料金で換算した目安です(今回は1回約0.14ドル。Max プランでの実行なので、実際の請求ではなくプランの使用量として数えられます)。

STEP4:PR にコメントとして投稿する(実行はしていません)

GitHub の PR に投稿する場合は、保存したファイルを gh pr comment の本文に指定します。

# PR 番号 42 の差分をレビューして保存する
gh pr diff 42 | claude -p "$ASK" > review.md

# 中身を自分の目で確認してから投稿する
cat review.md
gh pr comment 42 --body-file review.md

このブロックは、公開リポジトリに書き込むため今回は実行していません。自動で投稿する前に、まず手元で結果を読む運用にしておくと、誤った指摘がそのまま流れる事故を防げます。

STEP5:/goal と組み合わせて「3観点がそろうまで」走らせる

最後に、ヘッドレスモードの中で /goal を使います。条件を「バグ・セキュリティ・パフォーマンスの3つの見出しと指摘がファイルに書かれ、その見出しを会話に表示した状態」にしておけば、観点が欠けていても次のターンで補ってくれます。

claude -p "/goal git diff main...feature/points を読み、バグ・セキュリティ・パフォーマンスの3観点それぞれの見出しと指摘が review-goal.md に書かれ、その見出しを会話に表示した状態にする。src/ は変更しない" \
  --allowedTools "Bash(git diff *)" "Write" \
  --output-format json | jq '{subtype, num_turns, duration_ms, total_cost_usd, result: (.result[0:120] + "…")}'

--allowedTools は、確認なしで使ってよいツールの指定です。ここでは git diff の実行とファイルの書き込みだけを許可しました。

claude -p で /goal を実行した結果。subtype success、num_turns 9、duration_ms 34447、total_cost_usd 約0.29

画面12:ヘッドレスの /goal は1回の実行でゴールまで回り切る

34秒で終わり、作られたファイルの見出しを確認すると、3観点がそろっていました(JSON の num_turns はツール呼び出しを含む往復の回数で、/goal の評価回数とは別の数です)。

grep -n '^#' review-goal.md; git status --short src/
1:# レビュー結果(`git diff main...feature/points`)
5:## 1. バグ
12:## 2. セキュリティ
17:## 3. パフォーマンス
22:## 補足

git status --short src/ は何も表示しなかったので、条件どおり src/ は変更されていません。

ヘッドレスで /goal を使うときの注意点を公式ドキュメントから補足します。

  • 既定のテキスト出力では、終わるまで何も表示されない。長く回るゴールは止まっているように見えるので、途中経過を見たいときは --output-format stream-json --verbose を付ける
  • 止めるときは Ctrl+C
  • /goal 自体には既定のターン上限がないので、条件に上限を書いておく

claude -p "/goal …" は1回のコマンドで完結するので、シェルスクリプトや CI(GitHub Actions など)にそのまま組み込めます。

使い分け:/loop・/goal・Desktop・Routines

ここまでの内容を、「何が次の回を始めるか」で選ぶ判断フローにまとめます。

終わりが条件で決まるなら /goal、時間で繰り返しセッションを開いたままなら /loop、そうでなければ Desktop の予定タスク、PC を閉じるなら Routines を選ぶ判断フロー

図8:どれを使うかの判断フロー

時間で繰り返す3つの方法は、公式ドキュメントで次のように比較されています。

項目Routines(クラウド)Desktop の予定タスク/loop
実行場所クラウド(既定は Anthropic 管理)自分の PC自分の PC
PC の起動が必要不要必要必要
セッションを開いておく必要不要不要必要
再起動後も残るか残る残る--resume で復元(例外あり)
ローカルのファイル使えない(毎回新しく取得)使える使える
権限の確認なし(自律実行)タスクごとに設定セッションを引き継ぐ
最短の間隔1時間1分1分

公式ドキュメントの勧めは、「自分の PC なしで確実に動かしたいならクラウド、ローカルのファイルやツールが必要なら Desktop、セッション中の手早い見張りなら /loop」です。1回だけのリマインダーなら、/loop を使わずに「15時にリリースブランチを push するよう知らせて」のように普通の文で頼むと、実行後に自分で消える予定が作られます。

トラブルシューティング

うまく動かないときは、次の順で確認してください。左が /loop、右が /goal です。

/loop が動かない・止まらないときの確認4項目と、/goal が終わらない・使えないときの確認4項目を順に強調する図

図9:確認する順番

よくある症状と対処を表にまとめます。

症状原因対処
ループがいつの間にか止まったターミナルを閉じた、新しい会話を始めたclaude --resume で戻す。セルフペースは /loop を打ち直す
予定の時刻より遅れて実行されるClaude が作業中だった、またはジッター作業が終わると1回だけ実行される。時刻が重要なら :00・:30 を避ける
Esc を押してもループが続く固定間隔のループは Esc では消えない「このループを止めて」と頼む
/loop が使えないCLAUDE_CODE_DISABLE_CRON=1 が設定されている環境変数を外す
/goal がいつまでも終わらない評価モデルが確認できる証拠が会話に出ていない「結果を会話に表示する」を条件に入れる。上限を書く
/goal 中に確認ダイアログで止まる権限モードが手動のまま無人で回すなら auto モード、ヘッドレスなら --allowedTools
/goal が使えないと表示されるフォルダを信頼していない、disableAllHooks が true などフォルダを信頼する、フックの無効化設定を見直す

後片付け

練習が終わったら、作ったものを消しておきましょう。予定タスクはセッションを終了すれば消えますが、念のため一覧で確認してから終了すると安心です。

# 練習用フォルダを削除する(中身を確認してから)
ls ~/loop-goal-lab
rm -rf ~/loop-goal-lab

まとめ

この記事では、Claude Code の2つの「繰り返し」を実機で試しました。

  • /loop は時間で繰り返す。 /loop 1m … のような固定間隔では cron の予定が登録され、今回は5回目の確認でビルド成功を見つけて自分でループを止めた。間隔を書かないと、Claude が観察した内容から待ち時間(今回は90秒→75秒)を選ぶ
  • loop.md で「いつもの見回り」を決められる。 やらないことを書いておくと、定期実行が勝手に作業を広げない
  • /goal は条件を満たすまでターンを続ける。 判定するのは会話しか読まない別のモデルなので、結果を画面に出させる条件にする。今回はテスト修正が26秒・1ターン、観察用の設定で1分・4ターンで達成した
  • 上限は誤読されない書き方にする。 「10ターン以内に達成できなければ中止して報告」のように、止まったときの動きまで書く
  • ヘッドレスモードと組み合わせると、シェルや CI に組み込める。 claude -p "/goal …" は1回の実行でゴールまで回り切る

次のステップとしては、/goal の仕組みの元になっている Stop フックを自分で書いてみること、PC を閉じても動く Routines(/schedule)を試すことがおすすめです。

参考リソース

タイトルとURLをコピーしました