2
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?

無人で動かす claude -p に渡す道具を絞る(--allowedTools で git をサブコマンドごとに・専用の --settings でフックだけ足す)

2
Posted at

この記事を読み終えると、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 を丸ごと渡していた所が見つかるはずです。

参考

2
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
2
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?