この記事を読み終えると、claude -p を cron や launchd で無人で動かすときに、「やってよいこと」だけを渡す設定が書けます。
根拠は、手元の Mac で毎日 15:00 に動かしているスクリプトの、2026-11-04〜11-05 の3回分の実行です。
何に困ったか
定期作業の一覧表(やること・最後にやった日・次の期限の表)を Markdown で持っています。
その中に「ファイルを読んで数えて書くだけ」の行があり、期限が来ても誰も手を付けないまま残っていました。
そこで、期限の来た行を Claude Code に無人で片付けさせることにしました。
ただ、見ている人がいない実行で、ふだんの対話と同じ道具をそのまま渡すのは怖いです。
- ファイルを消す、ほかのセッションの書きかけをまとめてコミットする
- ブラウザで送信ボタンを押す
- できなかった作業の期限を、勝手に後ろへずらす
このあたりを、文章のお願いではなく、渡す道具の側で止めるようにしました。
環境
- Claude Code(CLI)の
claude -p(対話なしで1回だけ動かすモード) - macOS の launchd で毎日 15:00 に zsh のスクリプトを起動
- 作業場所は git のリポジトリ1つ(ほかのセッションも同時に使う)
設定の全体
起動の行はこれだけです(パスは短くしています)。
claude -p --model sonnet --max-turns 60 --output-format text \
--settings ~/workspace/scripts/run-claude-due-settings.json \
--allowedTools "$TOOLS" \
< prompt.txt > out.txt
使っているのは3つです。
-
--allowedTools: 承認なしで使ってよい道具の一覧。ここにない道具は、無人なので承認できずに止まります -
--settings: この実行のときだけ足す設定ファイル。ここでフックを付けます -
--max-turns: 往復の上限。迷い続けて止まらない事故を防ぎます
1. git はサブコマンドごとに渡す
Bash を丸ごと渡すと、何でも打てます。そこで Bash(コマンド:*) の形で、前から一致したものだけを許しました。
TOOLS="Read Write Edit Glob Grep \
Bash(git add:*) Bash(git commit:*) Bash(git pull:*) Bash(git push:*) \
Bash(git status:*) Bash(git diff:*) Bash(git log:*) \
Bash(python3 scripts/cadence-set.py:*) Bash(date:*) Bash(ls:*) Bash(wc:*)"
ポイントは2つです。
-
git reset・git stash・git checkoutは一覧に入れていません。無人の実行が手元の変更を巻き戻す道がなくなります - Python は「表の1行を直す専用のスクリプト」だけを許しています(
python3:*にしない)
表の行は Edit ではなく専用のスクリプトで直させる
表の1行は長く、Edit ツールの置き換え(前後の文字列の一致で探す)だと外しやすいです。
そこで、行番号と列を指定して3つの欄だけ直す小さなスクリプトを作り、それだけを使うように指示しました。
# cadence-set.py <表のファイル> <行番号> --last 2026-11-04 --due 済 --memo-head "根拠: 〜"
if not re.search(r'(^|/)docs/cadence\.md$', a.path):
sys.exit('cadence.md だけを直せます')
row = L[a.line - 1]
if not row.startswith('|') or row.startswith('|---'):
sys.exit(f'{a.line} 行目は表の行ではない')
c = row.split('|') # c[4] 最後にやった日、c[5] 次の期限、c[6] メモ
ファイル名で対象を絞っているので、ほかのファイルには使えません。
2. フックは --settings で「この実行だけ」に足す
ふだんの ~/.claude/settings.json にもフックはありますが、無人の実行には、それに加えてブラウザ用の見張りを付けたいです。
そこで、この実行専用の設定ファイルを作りました。
{"hooks":{"PreToolUse":[
{"matcher":"Bash","hooks":[
{"type":"command","command":"python3 ~/.claude/hooks/delete-guard.py"},
{"type":"command","command":"python3 ~/.claude/hooks/git-guard.py"}]},
{"matcher":"mcp__claude-in-chrome__.*","hooks":[
{"type":"command","command":"python3 ~/workspace/scripts/sf-only-guard.py"},
{"type":"command","command":"python3 ~/.claude/hooks/boss-button-guard.py"}]}
]}}
-
delete-guard.py: フォルダごと消すコマンドを止める -
git-guard.py:git add -Aなどを止める(--allowedToolsで通したgit addの中でも、-Aだけは止めたいので二重にしています) -
sf-only-guard.py: ブラウザで開いてよいドメインを絞る -
boss-button-guard.py: 送信・公開のボタンを止める
ブラウザを渡すのは、ブラウザの作業がある回だけです。
CH=(); T2=$TOOLS
if [[ $P == <ブラウザの作業がある回> ]]; then
CH=(--chrome --disallowedTools "mcp__claude-in-chrome__javascript_tool")
T2="$TOOLS mcp__claude-in-chrome__*"
fi
JavaScript を実行する道具は、ボタンを押す抜け道になるので --disallowedTools で外しました。
3. できなかったときの書き方まで、指示で決める
道具を絞ると、できない行が出ます。そのときに期限を後ろへずらして「片付いた」ように見せるのが一番困ります。
指示には、終わり方を3つに分けて書きました。
- やった行: 「最後にやった日」を今日にし、メモ欄の頭に「根拠: 〜(何を数えた・どのファイルを見た)」を書く。根拠が書けないなら済にしない。
- やらなかった行: 「次の期限」は変えない。メモ欄の頭に「止まる理由(日付 自動): 〜が要る」と1つだけ書く。
- 最後に1行だけ: RESULT done=<済にした数> stopped=<止まる理由を書いた数> commit=<短いハッシュかなし>
スクリプト側は、最後の RESULT の行だけを拾って、実行の記録に1行足します。
RESULT の行がなければ失敗として Mac に通知を出します。
R=$(grep '^RESULT' out.txt | tail -1)
[[ -z $R ]] && FAIL=1
echo "| $D | $P | ${R:-失敗} |" >> history.md
動かした結果
2026-11-04〜11-05 の3回は、どれもコミットまで進みました(コミットの差分で確認)。
- 11/4 10:35(手で試しに起動): 表の6行を書き換え
- 11/4 15:00: 2行を書き換え
- 11/5 15:00: 2行を書き換え。そのうち「曲を聞いて質を判断する」行は、期限を変えずにメモ欄の頭へ「止まる理由(2026-11-05 自動): 曲を聞いて質を判断するのに人の耳…が要る」と書いて止まった
人の判断が要る行を、無理に済にせず止まってくれたのが、一番ありがたかった結果です。
つまずき
1. ヒアドキュメントの中のバッククォートが実行された
指示文はスクリプトの中にヒアドキュメント(<<PROMPT)で書いていました。
指示の中に、使い方の例として `python3 scripts/cadence-set.py … <行番号> …` と書いたところ、シェルがバッククォートをコマンドとして実行し、その文が空になりました。<行番号> はリダイレクトとして読まれ、ログには「no such file」が出ていました。
変数($P など)は展開したいので、<<'PROMPT' にはできません。バッククォートだけを \` でエスケープして直しました。
cat > prompt.txt <<PROMPT
- 表の行を直すときは Edit を使わず、必ず \`python3 scripts/cadence-set.py projects/$P/docs/cadence.md <行番号> …\` を使う
PROMPT
初回の実行の前に、作られた prompt.txt を目で見ておけば、すぐ気づけました。
2. 「動いた」はコミットの有無ではなく RESULT の行で見る
指示が空になった回も、Claude は何かしら作業をしてコミットまで進むことがあります。
コミットがあるから成功、とは言えないので、RESULT の行と、表の行に「根拠:」が入ったかで確かめています。
向いていない使い方
- 人の判断が要る作業(公開・送信・お金の手続き)。この形は「止まって理由を書く」までしかしません
- 道具の一覧が長くなる作業。一覧が長いほど、何を許したか自分でも追えなくなります
まとめ
- 無人の
claude -pは、--allowedToolsで道具を名指しし、git はサブコマンドごとに渡した - フックは
--settingsの専用ファイルで、その実行だけに足した - できなかった行は「期限を変えずに止まる理由を書く」と、終わり方まで指示で決めた
- 初回はプロンプトのファイルを先に見る(ヒアドキュメントのバッククォートに注意)
今日の一歩(10分)
いま手で動かしている claude -p があれば、--allowedTools に今使っている道具を書き出してみてください。
書き出してみると、Bash を丸ごと渡していた所が見つかるはずです。
参考
- Claude Code の CLI リファレンス(
-p・--allowedTools・--settings・--max-turns): https://docs.claude.com/en/docs/claude-code/cli-reference - 権限の書き方(
Bash(git diff:*)の形): https://docs.claude.com/en/docs/claude-code/iam