はじめに:「まだ終わらない?」「続けて」を、Claude Code に任せる
Claude Code(ターミナルで動く Anthropic 公式の AI コーディングツール)を使っていると、次のような「待ち」と「催促」が意外と多いことに気づきます。
- ビルドやデプロイが終わるまで、数分おきに画面を見に行く
- 「テストを直して」と頼み、1回で終わらなければ「まだ落ちてるよ、続けて」と何度も送る
- PR(プルリクエスト)のレビューを、毎回同じ手順で頼む
Claude Code には、この2種類の繰り返しをそれぞれ任せるコマンドがあります。決めた時間ごとに同じ指示を実行する /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.71 | v2.1.139 |
準備:練習用のプロジェクトを作る
検証した環境
今回の検証環境は次のとおりです。画面の数値や表示は、この環境での結果です。
| 項目 | 内容 |
|---|---|
| OS | macOS(Apple Silicon) |
| Claude Code | v2.1.280(ネイティブ版) |
| モデル | Opus 5.5(effort は --effort medium で medium に指定) |
| 権限モード | auto モード(ツールの実行を分類器が自動で承認するモード) |
| Node.js | v22.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 が出てきます。説明文に「間隔を省略するとモデルが自分でペースを決める」と書かれているとおり、渡すもので動き方が変わります。
画面1:/loop の候補表示(v2.1.280)
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回目の確認もすぐ行われました。
画面2:登録された予定には8文字の ID(ここでは 4a05ea05)が付く
画面下に「このセッションを閉じるまで動く。クラウドで続けたいなら /schedule」という案内が出ている点にも注目してください。/loop がセッション単位の機能であることが、画面からも分かります。
ここから先は、何もせずに待つだけです。約5分間の画面を、変化のあった場面だけつないで録画にしました。
録画1:/loop 1m の実行(同じ画面が続く待ち時間は省略)
時刻の流れを1本の線にすると、次のようになります。ビルドの各段階と、ループの各回がどう重なったかを見てください。
図3:実測のタイムライン。5回目の確認で成功を見つけて自分で止まった
5回目の確認でビルドの成功を見つけると、Claude は CronDelete で自分のジョブを削除し、PushNotification でターミナルに完了通知も送りました。続けて「いま予定されているタスクを一覧して」と頼むと、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行で教えて
画面4:2回目と3回目の様子。「Claude resuming /loop wakeup」の行が、自動で再開した印
セッションの記録を見ると、Claude は ScheduleWakeup というツールに待ち秒数と理由を渡していました。1回目はビルドが始まったばかりなので90秒、2回目は「各段階が約36秒で進んでいる」ことに気づいて75秒、3回目で成功を見つけると次の予約をせず stop: true で終了しています。
図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つずつ置きました。
録画2:loop.md の見回り。置いたファイルが次の回で処理される
2件目の問い合わせメモにはバグ報告が書かれていましたが、Claude は「ループのルールで inbox/・done/・notes/ 以外は変更しないため、修正は行っていません」と答えました。見回りの指示に「やらないこと」を書いておくと、定期実行が勝手に作業を広げないことが確認できます。
loop.md を書き換えると、次の回から新しい内容が使われます。長すぎる内容(25,000バイトを超えた部分)は切り捨てられるので、短く保ちましょう。
ループの止め方と寿命
固定間隔のループは、止めたいときに言葉で頼めば CronDelete で消してくれます。
画面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ターン分の流れを追ってください。
図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ターンで達成と判定されました。
画面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ターン目で達成しました。
録画3:4ターン・約1分で達成(同じ画面が続く部分は省略)
実行中に /goal とだけ打つと、状態が表示されます。Last check: の行が、評価モデルが直前に出した判定の理由です。
画面7:/goal の状態表示。直近の判定理由が読める
達成後に Ctrl+O(詳細表示の切り替え)を押すと、最終判定の理由も英語で読めます。条件の各部分(exit 0、件数の表示、1ターン1修正、test/ を変更していないこと、10ターン以内)を1つずつ会話の内容と照らし合わせていることが分かります。
画面8:達成と判定した理由。会話に出た「exit=0」や「Changed files」を根拠にしている
一方で、気になる点も見つかりました。画面7の途中判定の理由には、「『10ターンを超えたら止める』まで続ける必要がある」と書かれています。条件の上限を「10ターンまでは続けるべき」と読んだようです。最終的には4ターンで正しく達成と判定されましたが、上限の書き方は、誤読されない形にしておくほうが安全です。
条件の書き方:4つの要素
公式ドキュメントは、長いターンでも崩れない条件の要素として「1つの測定できる終わりの状態」「確かめ方の明記」「守るべき制約」の3つを挙げています。これに、同じページで勧められている「ターン数か時間の上限」を加えると、次の4要素になります。
図6:判定できる条件は4つの部分からできている
上の実験を踏まえると、上限は次のように「止まったときに何をするか」まで書くと誤読されにくくなります。
/goal npm test が exit 0 で終わり、pass/fail 件数を会話に表示した状態にする。test/ 配下は変更しない。10ターン以内に達成できなければ、作業をやめて原因を報告する
「使うかどうか」の判断には、次の3つの問いが役に立ちます。
-
終わりを会話の中で証明できるか。 テスト結果、終了コード、件数、
grepの結果のように、Claude が表示できる証拠があるか。「きれいにする」「良くする」のような主観的な条件は避ける - 次の一手が明らかか。 「落ちているテストを直す」なら毎ターンやることがはっきりしている。「品質を上げる」は手探りのループになりやすい
- 1ターンで終わりそうではないか。 画面6のように1ターンで終わる作業なら、普通に頼むのと変わらない
状態確認と途中での解除
/goal は、途中でやめることもできます。次の画面では、README に節を追加するゴールを設定した直後に Esc で作業を中断し、/goal clear で解除しました。
画面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 を組み合わせて、差分のレビューを自動化します。全体の流れは次の図のとおりです。
図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、端数、会員でないときのエラー、二重ループ、ログの消し忘れ)がすべて指摘されています。
画面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] + "…")}'
画面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 の実行とファイルの書き込みだけを許可しました。
画面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
ここまでの内容を、「何が次の回を始めるか」で選ぶ判断フローにまとめます。
図8:どれを使うかの判断フロー
時間で繰り返す3つの方法は、公式ドキュメントで次のように比較されています。
| 項目 | Routines(クラウド) | Desktop の予定タスク | /loop |
|---|---|---|---|
| 実行場所 | クラウド(既定は Anthropic 管理) | 自分の PC | 自分の PC |
| PC の起動が必要 | 不要 | 必要 | 必要 |
| セッションを開いておく必要 | 不要 | 不要 | 必要 |
| 再起動後も残るか | 残る | 残る |
--resume で復元(例外あり) |
| ローカルのファイル | 使えない(毎回新しく取得) | 使える | 使える |
| 権限の確認 | なし(自律実行) | タスクごとに設定 | セッションを引き継ぐ |
| 最短の間隔 | 1時間 | 1分 | 1分 |
公式ドキュメントの勧めは、「自分の PC なしで確実に動かしたいならクラウド、ローカルのファイルやツールが必要なら Desktop、セッション中の手早い見張りなら /loop」です。1回だけのリマインダーなら、/loop を使わずに「15時にリリースブランチを push するよう知らせて」のように普通の文で頼むと、実行後に自分で消える予定が作られます。
トラブルシューティング
うまく動かないときは、次の順で確認してください。左が /loop、右が /goal です。
図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)を試すことがおすすめです。
参考リソース
- スケジュールに従ってプロンプトを実行する(/loop・予定タスク)|Claude Code Docs
- Claude をゴールに向かって動作させ続ける(/goal)|Claude Code Docs
- Commands(コマンド一覧)|Claude Code Docs
- Run Claude Code programmatically(ヘッドレスモード)|Claude Code Docs
- Routines|Claude Code Docs
- Desktop scheduled tasks|Claude Code Docs
- Manage costs effectively|Claude Code Docs
- Claude Code CHANGELOG|GitHub
- Loop engineering: Getting started with loops|Claude Blog
























