🔄 Claude Codeのモデル終了をlaunchdで無人移行する ― settings.jsonを自動パッチする䜿い捚おゞョブ — リヌダヌ×
🔄

Claude Codeのモデル終了をlaunchdで無人移行する ― settings.jsonを自動パッチする䜿い捚おゞョブ

#claudecode#launchd#自動化#macos2026-07-18 · 箄8分

「Claude Code環境」シリヌズ。前回 launchdのマルチスロットリトラむ蚭蚈 で「朝昌倜3回冪等発火」パタヌンを曞いた。今回はその応甚で、**Fable 5の終了2026-07-07に備えお仕蟌んでいた「䜿い捚お移行ゞョブ」**の実装を公開する。

settings.jsonのmodelフィヌルドをjqで曞き換え、デスクトップ通知を飛ばし、最埌にlaunchctl unloadで自分の登録を倖しお消える。起動・実行・自滅を1スクリプトで完結させる蚭蚈だ。モデル移行に限らず「特定日に䞀床だけ蚭定を倉えお消えるゞョブ」ずしお汎甚で䜿えるパタヌンなので、実コヌドを党郚出す。

困りごずモデルEOLは静かに壊れる

Claude Codeの~/.claude/settings.jsonに"model": "claude-fable-5"を曞いた環境で、EOL圓日以降もその蚭定が残り続けるず、静かに意図しない挙動になる。゚ラヌは出ず、フォヌルバックや別モデルで動いおいるかもしれないが、自分がどのモデルを䜿っおいるか分からなくなる。

手動でsettings.jsonを開いお曞き換えるのが最もシンプルだが、「圓日に確実に」「忘れずに」「䞀床だけ」を人間が揃えるのは案倖難しい。launchdに委譲しお自動化した。

蚭蚈3ステップで完結させる

1. 日付ガヌド      → 7/7より前なら即 exit 0catch-up発火察策
2. settings.json パッチ → バックアップ → jq曞き換え → JSON怜蚌 → mv
3. 通知 + self-unload → osascript で通知 → launchctl unload で自分を倖す

「䜿い捚お」の栞は ステップ3でゞョブ自身を登録解陀するこず。これで次回以降の発火がない。ただし plist ファむルは消さず~/Library/LaunchAgents/に残す。再びlaunchctl loadすれば埩垰できるようにしおおくスクリプトのコメントにも「plistは残す再登録可胜」ず曞いおいる。

日付ガヌド早期発火を無害化する

launchdには、macが起動したずきに「本来発火するはずだった過去のスロットを遡っお実行する」catch-upがある。7/7以前に登録した堎合、mac再起動のタむミングで前倒し発火する可胜性がある。最初の䞀行でこれを止める。

# 7/7より前なら䜕もしないcatch-up発火察策
if [ "$(date +%Y%m%d)" -lt 20260707 ]; then
  log "skip: before 2026-07-07"; exit 0
fi

date +%Y%m%dは20260707圢匏の敎数を返すので、数倀比范で刀定できる。7/7圓日以降に初めお実際の凊理が走る。

この日付ガヌドがないず、7/7前に登録しおmacを再起動するたびにスクリプトが走る。catch-upはStartCalendarIntervalゞョブ特有の挙動なので、定時ゞョブを組む際は必ず意識する。

settings.jsonのパッチアトミックな4ステップ

モデル曞き換えは4ステップのアトミック操䜜にしおいる。

current=$(jq -r '.model // empty' "$SETTINGS")
if echo "$current" | grep -qi 'fable'; then
  cp "$SETTINGS" "$SETTINGS.bak-model-transition"
  jq '.model = "opus"' "$SETTINGS" > "$SETTINGS.tmp" \
    && jq . "$SETTINGS.tmp" > /dev/null \
    && mv "$SETTINGS.tmp" "$SETTINGS"
  log "switched model: $current -> opus"
else
  log "no-op: model is already '$current'"
fi

各ステップの意図

  • jq -r '.model // empty' ― .modelキヌが存圚しない堎合は空文字を返す。null文字列を埌段に流さない
  • grep -qi 'fable' ― 倧文字小文字を問わずfableを含む堎合だけ曞き換える。すでに別モデルに移行枈みならelseに萜ちおno-opログだけ曞いお終わる冪等
  • cp "$SETTINGS" "$SETTINGS.bak-model-transition" ― 曞き換え前のバックアップ。倱敗時の埩旧甚
  • jq . "$SETTINGS.tmp" > /dev/null ― 曞き換えた.tmpがJSON的に正しいか怜蚌しおからmvする。壊れたJSONを本番パスに眮かない

スクリプト冒頭でset -uo pipefailを宣蚀しおいるので、&&チェヌンのどこかが倱敗すればmvは走らない。

jq . file > /dev/nullによる怜蚌を挟む理由は、jqの出力が䜕らかの理由で壊れたJSONになった堎合に、.tmp→本番ぞのmvを止めるため。--argjsonを䜿わず文字列リテラルを埋め蟌む今回の構造では実際には起きにくいが、習慣ずしお入れおおくず他の曞き換えスクリプトぞの転甚時に安党だ。

通知ずセルフunload

曞き換えが完了したらosascriptでデスクトップ通知を送る。

/usr/bin/osascript -e 'display notification "Fable 5終了に䌎いデフォルトモデルをOpusぞ切替えたした" with title "Claude model transition"' \
  >/dev/null 2>&1 || true

|| trueを付けおいるのは、通知が倱敗しおもゞョブ党䜓を゚ラヌ扱いにしたくないため。通知はあくたで人間ぞの報告であり、凊理の本䜓ではない。

圹目を終えたら自分をunloadする。

# 圹目を終えたらゞョブを倖すplistは残す再登録可胜
launchctl unload "$PLIST" 2>/dev/null || true
log "done (job unloaded)"

PLIST倉数はスクリプト冒頭でPLIST="$HOME/Library/LaunchAgents/com.shun.model-transition-0707.plist"ず定矩しおある。launchctl unloadはplistファむルを削陀しないので、~/Library/LaunchAgents/にファむルは残ったたただ。次回launchctl load "$PLIST"を打おば即座に再登録できる。

plistの配線3スロット冪等発火

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"><dict>
  <key>Label</key><string>com.shun.model-transition-0707</string>
  <key>ProgramArguments</key><array>
    <string>/bin/bash</string>
    <string>~/.claude/scripts/model-transition-0707.sh</string>
  </array>
  <key>StartCalendarInterval</key><array>
    <dict><key>Hour</key><integer>6</integer><key>Minute</key><integer>50</integer></dict>
    <dict><key>Hour</key><integer>12</integer><key>Minute</key><integer>50</integer></dict>
    <dict><key>Hour</key><integer>20</integer><key>Minute</key><integer>50</integer></dict>
  </array>
  <key>EnvironmentVariables</key><dict>
    <key>PATH</key>
    <string>~/.nvm/versions/node/v24.13.0/bin:/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:~/.local/bin</string>
  </dict>
  <key>StandardErrorPath</key><string>~/.claude/logs/model-transition.err</string>
</dict></plist>

6:50・12:50・20:50の1日3スロット。前回の蚘事で曞いたマルチスロット蚭蚈の盎接応甚だ。1回目が完走しおself-unloadすれば2回目以降は発火しない。macがスリヌプで6:50を逃しおも、12:50・20:50で拟える。

StandardErrorPathをplistで蚭定しおいるので、スクリプト内でstderrのリダむレクトを曞かなくおよい。デバッグ時は~/.claude/logs/model-transition.errを芋る。

螏んだ萜ずし穎

  • jqがpathに無くおexit 127 → plistのEnvironmentVariables.PATHに/opt/homebrew/binが無いず、Homebrew入りのjqが芋぀からない。タヌミナルから手動実行は成功するのに、launchdだけ壊れる兞型パタヌン。Apple Siliconの定番眠
  • 日付ガヌドを入れる前のテストでcatch-up発火 → 7/7前に動䜜確認でロヌドしたずころ、mac再起動のcatch-upが走っお7/7より早く移行が完了しおしたった。日付ガヌドは埌付けではなく最初から必須
  • .modelキヌが存圚しないsettings.jsonで誀動䜜 → jq -r '.model'だずnull文字列が返り、grep -qi 'fable'が䞍䞀臎で枈むが、将来的なキヌ名倉曎や欠萜を考えお// emptyで空文字フォヌルバックを明瀺した
  • self-unload埌に次スロットで再発火するか心配した → launchctl unloadを実行した時点でそのゞョブのスケゞュヌルは消える。同じlaunchctl loadなしには再発火しない
  • stderrの行方が分からなくなる → StandardErrorPathを蚭定しおいないず、launchdはstderrを/dev/nullか予枬䞍胜な堎所に捚おる。デバッグ䞍胜になるので必ず蚭定する

汎甚パタヌンずしお

このゞョブはFable 5のEOL固有に芋えるが、以䞋の3芁件が揃えばそのたた転甚できる。

芁件本ゞョブでの実装
特定日以降に䞀床だけ実行date +%Y%m%d比范で日付ガヌド
冪等䜕床走っおも同じ結果珟圚倀チェックしおから曞き換え
完了埌に自動的に消えるlaunchctl unload "$PLIST"

応甚䟋

  • 特定日以降にconfigの機胜フラグをON/OFFする
  • 期限切れのAPI゚ンドポむントを新URLに曞き換える
  • バヌゞョンアップに䌎うJSON蚭定のスキヌマ移行

「䞀床だけ実行しおほしいcron」は、cronやlaunchdに氞続登録するず毎回発火しおしたい冪等性の確保が面倒になる。self-unloadで「走ったら消える」蚭蚈にする方が、氞続ゞョブのdedupe凊理を曞くより単玔だ。

たずめ

  • 日付ガヌドdate +%Y%m%d比范でcatch-up発火を無害化する ―― 最初から入れる
  • jqパッチはバックアップ→曞き換え→JSON怜蚌→mvのアトミック4ステップ
  • 珟圚倀チェックgrep -qi 'fable'で冪等にする。移行枈み環境でも安党に再実行できる
  • **launchctl unload "$PLIST"**でself-unload ―― plistは残しお再登録可胜にする
  • plistのマルチスロット6:50/12:50/20:50でスリヌプ時のskipに備える
  • Homebrew入りのjqを䜿うなら**plistのPATHに/opt/homebrew/bin**を忘れない

次回は、このような䜿い捚おゞョブも含めたlaunchd党䜓の死掻を䞀コマンドでチェックする automation-health.sh の話を曞く予定です。


Lily@bokuwalily― 個人開発者。Claude Code で自動化基盀を組みながら、iOSアプリやWebサヌビスを量産しおいたす

  • AIで「寝おおも回る仕組み」を䜜っお月120䞇にした話は noteの有料蚘事 に💰
  • OSS: github.com/bokuwalily 🐙
  • 最新情報・お問い合わせは X @bokuwalily ぞ🌍

皆さんの ❀ やシェアが励みになりたす