starter-kit-upgrade について
Laravel Starter Kit Upgrade は、Laravel スターターキットを使うプロジェクト向けのアップグレードスキルで、上流の機能変更を個別に選んで取り込めます。専用ブランチ、機能ごとのコミット、動作確認でカスタマイズを保護し、競合の自動解決やマニフェスト・ロックファイルの上書きは行いません。
主な機能
- 01Vue、React、Svelte、Livewire の kit を判別
- 02上流の機能一覧から選択
- 03ファイルを比較しカスタム変更を提示
- 04専用ブランチで機能ごとにコミット
- 05適用後にテストとビルドを再実行
活用シーン
- 01カスタマイズ済みプロジェクトに上流の通知機能を導入
- 02上流変更が独自ファイルに与える影響を確認
- 03スターターキットの機能を個別に採用
- 04更新後もテストとビルドが通ることを検証
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ laravel/ agent-skills --skill starter-kit-upgradeインストール後、Claude Code で/starter-kit-upgradeを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
scripts/apply_new_files.shclassify_feature.shfetch_kit.shlater_edits.shpreflight.shreconcile_manifests.shrun_tests.shscan_transitive_imports.sh詳細
Laravel Starter Kit のアップグレード
- ユーザーは
laravel/vue-starter-kit、react-starter-kit、svelte-starter-kit、またはlivewire-starter-kitからプロジェクトを作成し、その後カスタマイズします。コードの所有者はユーザーです。 - 上流から取り込むのは「トースト通知」「2FA のオートフォーカス修正」などの特定の機能であり、「バージョンのアップグレード」ではありません。
- ユーザーの Git 履歴とキットの Git 履歴に関連性はありません。共通の祖先もありません。ユーザーの現在の状態と上流の現在の状態を、バイト単位で比較します。
- カスタマイズされたファイルを自動マージすることはありません。カスタマイズ箇所を提示し、判断はユーザーに委ねます。
- 動作の維持が契約です。現在パスしているユーザーのテスト、型チェック、ビルドは、適用後もすべてパスする必要があります。
安全上の契約:妥協不可
副作用が発生する操作の前に、以下をユーザーに伝え、その後も常に遵守してください。
- 作業ツリーはクリーンでなければなりません。
git status --porcelainの出力が空でない場合は拒否し、コミットまたはスタッシュするようユーザーに伝えてください。「ユーザーに代わってスタッシュ」してはいけません。 - すべての作業は専用ブランチ(
starter-kit-upgrade/<short-id>)で行います。ユーザーの現在のブランチは変更しません。 - 適用する機能ごとに、個別のコミットを作成します。これにより、変更を取り消せます。
- コンフリクトを自動解決してはいけません。カスタマイズされたコードに触れる変更は提示し、デフォルトではそのファイルをスキップします。
- マニフェストやロックファイル(
composer.json、package.json、*-lock.*)をユーザーに知らせず上書きしてはいけません。差分を表示し、判断はユーザーに委ねます。 - 動作が維持されていることを検証します。適用後にユーザーのテスト、型チェック、ビルドを再実行してください。以前パスしていたチェックが失敗した場合はリグレッションです。停止して状況を提示し、変更の取り消しを推奨してください。
- 曖昧さのない根拠から判定し、曖昧な場合は確認してください。(
config/fortify.phpが存在するなどの)具体的な根拠は問題ありません。シグナルが混在している、または存在しない場合に、可能性の高そうな答えを選んではいけません。
これらのいずれかに違反した場合は、問題の内容と復旧方法を明確に伝えて中止してください。
必須ツール
git(ユーザーのプロジェクト内)gh(認証済みであること。gh auth statusが OK を返す)jq(run_tests.shで使用)bash(同梱スクリプト用)
不足しているツールがある場合は、フェーズ 4 で停止し、インストール方法をユーザーに伝えてください。
注意点
事前に伝えられていない場合、エージェントが誤った判断をする環境固有の挙動です。ワークフローを始める前に読み、全体を通して適用してください。
-
並行実装。 機能に
newファイルがあり、呼び出し箇所がdiffersとなっている場合、ユーザーがすでに社内で同等の実装(独自のトーストヘルパー、バリデーションルールなど)を用意している可能性があります。機能全体を提示してください。「安全」な変更としてnewファイルだけを個別に適用してはいけません。デフォルトでは機能全体をスキップします。ユーザーは後からアップストリーム版を採用し、独自の実装を削除できます。 -
名前が変更されたパス。
newパスのベース名またはクラス名がユーザーのリポジトリ内の別の場所にすでに存在する場合、ユーザーが名前を変更したか、移動した可能性があります。自動適用せず、該当箇所を提示してください。そうしないと重複が作られてしまいます。アップストリームの変更内容を示し、ユーザーが名前を変更したファイルに手動で適用するか、ユーザーの確認を待ってください。 -
その後のアップストリーム編集。 アップストリームの HEAD をコピーすると、機能が導入されたコミットだけでなく、その後のすべてのコミットも取り込まれます。適用前に、必ず Phase 5 の手順 2 の確認を実行してください。その後の編集がある場合は、
HEAD:<path>ではなく<sha>:<path>の範囲に絞ってください。 -
間接的なインポート。 新規ファイルは、その機能のコミットに含まれていないヘルパーを
importしていることがよくあります(Vue/React/Svelte:@/lib/...、@/components/...、Livewire:@include、<x-...>、<livewire:...>)。Phase 5 の手順 4 でこの確認を行います。確認せずに機能の適用完了を宣言してはいけません。見落としたインポートは、実行時またはコンパイル時のエラーにつながります。 -
ロックファイルの差分。 マニフェストはユーザーが管理しています。上書きしてはいけません。アップストリームとの差分をユーザーに説明し、マージしてもらってから、パッケージマネージャーでロックファイルを再生成してください(Phase 6)。
-
メジャーアップデート後の古い node_modules。 Vite v7 から v8、React 18 から 19 などの更新後は、
npm installがERESOLVEで失敗することがよくあります。クリーンアップして再インストールしてください(Phase 6)。 -
新しいマイグレーション。 アップストリームでマイグレーションが追加された場合(例:「Skeleton に合わせてマイグレーションを追いつかせる」)、別途提示してください。まず
php artisan migrate:statusの実行を推奨します。データが入った DB に新しいマイグレーションを適用すると、明確なエラーが発生する可能性があります。 -
機能として扱うフレームワークのメジャーアップデート。 Laravel 12 から 13、Livewire 3 から 4、Inertia v2 から v3 などの変更は規模が大きく、機能ごとに進めるフローでは対応できません。このスキルを使って実行しようとしないでください。代わりに、ユーザーに対応する Laravel Boost MCP スラッシュコマンドを先に実行するよう案内し、アップグレード後の(クリーンな作業ツリーの)リポジトリでこのスキルを再実行してもらいます。Boost がまだインストールされていない場合:
composer require laravel/boost --dev && php artisan boost:install(Boost^2.0が必要です)。スラッシュコマンド:- Laravel 12 → 13:
/upgrade-laravel-v13 - Livewire 3 → 4:
/upgrade-livewire-v4 - Inertia v2 → v3:
/upgrade-inertia-v3
- Laravel 12 → 13:
-
すでに存在する機能。 Phase 2 の事前フィルターで見落とされ、Phase 5 の分類で全ファイルが
already-presentと報告された場合は、「すべてのファイルがアップストリームの現行版と一致しています。次に進みます。」と記して、その機能をスキップしてください。空のコミットを作成してはいけません。 -
differsが約 50 件を超える場合。 ファイルごとに確認する進め方では、その規模に対応するのは手間がかかりすぎて有益ではありません。中断し、その機能は手動でアップグレードするよう推奨してください。
ワークフロー
順番に進める 8 つのフェーズです。各フェーズで、次のフェーズが前提とする条件を確立します。
フェーズ 1:キットとブランチの種類を特定する
ユーザーのプロジェクトを調べます。
| vue | react | svelte | livewire | |
|---|---|---|---|---|
| 手掛かり | resources/js/components/ui/ 内の .vue ファイル | resources/js/components/ui/ 内の .tsx ファイル | resources/js/components/ui/ 内の .svelte ファイル | resources/js/components/ui/ ディレクトリがない |
package.json にあるもの | "vue" + "@inertiajs/vue3" | "react" + "@inertiajs/react" | "svelte" + "@inertiajs/svelte" | 該当なし |
composer.json にあるもの | 該当なし | 該当なし | 該当なし | "livewire/livewire" + "livewire/flux" |
検出したキットを明示します。一致する列が 1 つだけなら、そのまま進めます。2 つの列が部分的に一致する場合(例:.vue と .tsx の両方がある、または package.json に vue と react の両方が記載されている場合)は、停止して質問します。
次に、ブランチの種類を判定します。キットごとに、独立した 2 つの軸で決まる 4 つのブランチがあります。
- 認証軸(
composer.jsonを読み取ります):composer.jsonにlaravel/fortifyがある、またはconfig/fortify.phpが存在する、またはapp/Actions/Fortify/が存在する、またはapp/Providers/FortifyServiceProvider.phpが存在する場合は Fortify です。composer.jsonにlaravel/workosがあり、Fortify の目印がどれも存在しない場合は WorkOS です。
- チーム軸(チーム用のスキャフォールディングがあるか確認します):
app/Models/Team.phpが存在する場合は Teams です(通常はMembership.php、TeamInvitation.php、および..._create_teams_table.phpというマイグレーションもあります)。- それ以外は Non-teams です。
2 つの軸を組み合わせてブランチ名を決定します。
| Auth | Teams | Branch |
|---|---|---|
| Fortify | いいえ | main |
| Fortify | はい | teams |
| WorkOS | いいえ | workos |
| WorkOS | はい | workos-teams |
検出したブランチを明示します。シグナルが矛盾している場合(例:Fortify の目印があり、かつ composer に laravel/workos が記載されている、または Team.php モデルがあるのにチーム用マイグレーションがない場合)に限り、質問します。これはユーザーによるカスタマイズを示しており、安全に推測できないためです。
フェーズ 2:利用可能なアップストリーム機能を列挙する
ユーザーは「使用しているバージョン」を確実に伝えられないため(また、こちらもその確認はしません)、現時点のアップストリームを調べて、機能カタログを提示します。
生データを取得します。デフォルトの対象範囲は直近 100 件のコミット/マージ済み PRです。これより古い機能はカタログに表示されないことをユーザーが把握できるよう、最初にその旨を伝えます。対象範囲よりかなり前にブートストラップしていた場合は、&page=2、&page=3 などで遡るか、--limit を増やします。
gh api "repos/laravel/<kit>/commits?sha=<branch>&per_page=100" \
-q '.[] | {sha: .sha[0:7], date: .commit.author.date[0:10], msg: .commit.message | split("\n")[0]}'
gh pr list --repo "laravel/<kit>" --state merged --base "<branch>" --limit 100 \
--json number,title,mergeCommit,mergedAt
コミット/PR をユーザー向けの機能にまとめます。ユーザーが認識しやすい例を示します。
- 「すべてのキットにトースト通知を追加」(1 コミット、複数ファイル)
- 「認証フォームにパスワード表示切り替えを追加」(1 コミット、3 ファイル)
- 「2FA のオートフォーカスを修正」(1 コミット、1 ファイル)
- 「チームのサポート」(1 PR、多数のファイル。大規模と表示)
- 「Inertia 3 へのアップグレード」(ロックファイル中心。レビューが必要と表示)
- 「メンテナンス:フォーマット/Lint 設定」(小さなコミットをまとめたもの)
内部処理やリファクタリングのコミットは、1 件の「メンテナンス」にまとめます。通常、ユーザーはこれをスキップします。
事前フィルタリング:機能候補ごとに、そのコミットに対して scripts/classify_feature.sh を実行します。すべてのファイルが already-present の場合は、[!] すでに存在します と表示し、デフォルトではスキップします。
フェーズ 3:カタログを提示し、明示的な選択を得る
Available upstream features (vue-starter-kit, branch: main):
[ ] Toast notifications · PR #142, 4 files, 1 lockfile
[ ] Password visibility toggle · PR #131, 3 files
[ ] 2FA autofocus fix · commit 78fda0c, 1 file
[ ] Teams support · PR #98, 23 files (LARGE)
[~] Inertia 3 upgrade · PR #110, lockfile-heavy (review carefully)
[!] Already present: Vite font plugin
Which would you like to pull in?
選択を待ちます。選択内容と、影響を受けるファイル数をまとめて確認します。副作用が発生する前に、最後にもう一度確認します。
フェーズ 4:事前確認、ベースライン、ワークスペースのセットアップ
事前確認を実行します。
scripts/preflight.sh <user_repo>
リポジトリが Git リポジトリであること、作業ツリーがクリーンであること、認証済みの gh と jq が利用できることを確認します。ゼロ以外の終了コードが返された場合は、メッセージをそのまま提示して処理を停止します。
フェーズ 7 で回帰と既存の失敗を区別できるように、検証ベースラインを記録します。並行実行によって互いの結果が上書きされないよう、mktemp を使用します。
baseline=$(mktemp -t skup-baseline.XXXXXX.json)
scripts/run_tests.sh <user_repo> --baseline "$baseline"
$baseline を保持してください。フェーズ 7 で必要になります。
上流キットを取得し、そのパスを保存します。
kit_dir=$(scripts/fetch_kit.sh <kit> <branch>)
$kit_dir を保持してください。フェーズ 5 で必要になります。このスクリプトは冪等です。同じ引数で再実行すると、再クローンではなく最新のブランチ先端を取得します。
アップグレードブランチを作成します。
git -C <user_repo> checkout -b "starter-kit-upgrade/$(date +%Y%m%d-%H%M)-<first-slug>"
ユーザーがすでに starter-kit-upgrade/... ブランチにいる場合(前回の実行後にクリーンアップされていない場合)、新しい名前が既存の名前と重なると checkout -b は拒否されます。自動で解決しないでください。そのブランチで再開する(checkout -b をスキップし、現在の状態から続行する)、新しく開始する(新しいタイムスタンプ付きの名前は分単位で異なるため、そのまま再試行する。同じ分の場合は +%Y%m%d-%H%M%S に変更する)、または手動でクリーンアップできるよう中止するのいずれにするか、ユーザーに確認してください。ユーザーに代わって既存のブランチを削除してはいけません。
以降の書き込みはすべて、このブランチに対して行います。
フェーズ 5: 選択した各機能を適用する
選択した各機能について、順に:
- 分類します。
scripts/classify_feature.sh <kit_dir> <sha> <user_repo>を実行します。ステータス:
new: ファイルがユーザーリポジトリに存在せず、upstream HEAD には存在します。追加しても安全です。already-present: ユーザーのファイルが upstream HEAD とバイト単位で同一です。スキップします。differs: ユーザーがファイルを持っており、バイトが upstream HEAD と異なります。表面化します。deleted-upstream: upstream HEAD にファイルがありませんが、ユーザーは持っています。表面化します。デフォルトはユーザーのものを保持します。lockfile: マニフェストまたはロックファイルです。表面化します。自動マージは決して行いません。
分類器は upstream HEAD とのみ比較します。ユーザーの git 履歴はキットの履歴まで遡れないため、マージの基準となる「ビフォアイメージ」はありません。そこは試みません。機能コミットは、どのパスを確認すべきかを列挙するだけです。
- later-edits チェック。どの機能パスが 後の upstream コミットでも変更されたかを見つけます:
scripts/later_edits.sh <kit_dir> <sha> <user_repo>
スクリプトが出力する各パスは、upstream HEAD の内容をコピーすると 後の 変更も一緒に取り込まれてしまうパスです。<sha>:<path> を HEAD:<path> と diff します。空白以外のハンクが異なる場合は、機能コミットにスコープを絞り(git -C <kit_dir> show <sha>:<path>)、レポートに記録します。
3. new ファイルを適用します。 スクリプトは各 new パスについて upstream HEAD の内容を書き込み、ステージします。それ以外はすべてステップ 4〜5 に残します:
scripts/apply_new_files.sh <kit_dir> <sha> <user_repo>
書き込んだ各ファイルについて applied <path> を出力するので、機能のコミットメッセージとレポート用にリストを収集できます。
スクリプトを実行させる前に、リネームの落とし穴を確認します(落とし穴 →「リネームされたパス」を参照)。new パスのベース名がユーザーリポジトリの別の場所にすでに存在する場合は、適用する前にユーザーに表面化します。
4. 推移的インポートチェック。 新しいファイルは、同じ機能コミットに含まれないヘルパーをインポートすることがよくあります。スクリプトはキットに適した正規表現を選びます(Vue/React/Svelte は TS/JS インポートを処理し、Livewire は Blade インクルード / x- コンポーネント / livewire: タグを処理します):
scripts/scan_transitive_imports.sh <kit> <new_files...>
出力はインポートごとに <file>:<line>:<match> です。各マッチについて、対応するヘルパーファイルがユーザーリポジトリに存在するか確認します。存在しない場合、新しいファイルはコンパイル/レンダリングされません。不足しているターゲットを、ユーザーが取得する必要があるフォローアップ依存関係としてフラグを立てます(differs と同じウォークスルー)。
5. differs、deleted-upstream、lockfile についてユーザーをガイドします。 一度に 1 ファイルずつ:
- upstream が持っているものを表示します:
git -C <kit_dir> show HEAD:<path>(later_edits.shがこのパスにフラグを立てた場合は<sha>:<path>)。 - ユーザーの現在のファイルを表示します。
- 両者の diff を表示します。
- ユーザーに選択を求めます: upstream を丸ごと採用する(非可逆。先に確認する)、ユーザーのものを保持する、または手動でマージする(参考用に unified diff を生成し、ユーザーが結果を書きます)。
- 迷っている場合は、目の前に diff を置いてもう一度尋ねます。それでも迷っているなら → ユーザーのものを保持して先に進みます。黙って選ばないでください。
- ユーザーが選んだものをステージします:
git -C <user_repo> add <path>。
lockfile の場合: マニフェストを決して上書きしないでください。composer.json / package.json の upstream diff を表示し、関連する変更を案内し、ユーザーにマニフェストを編集させます。ロックファイルの再生成はフェーズ 6 で行います。
6. 機能を 1 つのリバート可能な単位としてコミットします:
git -C <user_repo> commit -m "starter-kit-upgrade: <feature name>
Upstream: laravel/<kit>@<sha>
Files added: <list>
Files updated (took upstream): <list>
Files updated (manual merge): <list>
Files kept as-is: <list>"
ユーザーがいつでも中止したい場合は、ブランチをそのままにしておきます。git branch -D で削除できます。
フェーズ 6: 必要に応じてマニフェストを調整する
いずれかの機能がマニフェストに触れた場合、ロックファイルは同期が取れていません。ユーザーが同意した後、次を実行します:
scripts/reconcile_manifests.sh <user_repo>
スクリプトは composer install を実行し(composer.json と composer.lock が両方存在する場合)、既存のロックファイルから JS パッケージマネージャーを自動検出して <pm> install を実行し、失敗した場合(通常は Vite v7 → v8 や React 18 → 19 のようなメジャーアップグレード後の ERESOLVE)は node_modules とロックファイルを消去して一度だけ再試行します。
ロックファイルの更新は、独立してリバートできるよう、別の starter-kit-upgrade: dependency lockfiles コミットとしてコミットします。
フェーズ 7:動作が維持されていることを確認する
ベースラインと比較します。
scripts/run_tests.sh <user_repo> --compare "$baseline"
比較モードでは、存在する PHP テスト、JS の型チェック、JS のビルドを実行し、ベースラインでは成功していたものの、現在失敗するようになったチェックのみを報告します。既存の失敗はアップグレードが原因ではないため、処理を妨げません。
リグレッションが報告された場合:
- スクリプトが示すチェックごとのログファイルから、失敗時の出力を提示します。
- まず
git revert HEADを推奨します。それで解決しない場合は、もう一度リバートします。 - 複数の機能が関係して原因が不明な場合は、
git bisect start <upgrade-branch> <previous-branch>を提案します。 - 失敗したチェックに合格させるためにコードを編集してはいけません。これは動作に関する契約に反します。
プロジェクトで検出可能な検証コマンドが見つからない場合は、レポートにその旨を明記します。検証を実施したかのように装ってはいけません。
フェーズ 8:レポートを作成する
まず /tmp/starter-kit-upgrade-report-<id>.md に書き込みます(<id> はアップグレードブランチの starter-kit-upgrade/<id> と一致します)。ユーザーのリポジトリに黙って書き込んではいけません。ID を付けることで、同時実行や再実行による上書きを防ぎます。パスを提示し、STARTER_KIT_UPGRADE.md としてリポジトリに追加するか、リポジトリの外に置いておくかを尋ねます。
# Starter Kit Upgrade Report
- Date: <date>
- Kit: laravel/<kit>
- Branch tracked: <branch>
- Upgrade branch: starter-kit-upgrade/<id>
## Features applied
- <feature name> · laravel/<kit>@<sha> · <N files>
- Applied: <list>
- Skipped: <list with reasons>
- Manual decisions: <if any, with reasoning>
- Later-edit drift avoided: <if any, with paths scoped manually>
## Lockfile updates
<which lock files were regenerated and how>
## Verification
- Baseline: <path or summary>
- Result: <PASS / REGRESSED:<list> / NO-CHECKS>
- Output: <relevant snippet>
## How to revert
- Drop a single feature: `git revert <commit-sha>`
- Discard everything: `git checkout <previous-branch> && git branch -D starter-kit-upgrade/<id>`
対象外
- ユーザーがどの「バージョン」のキットから始めたかの検出。確実な方法はないため、検出できるとは装いません。
- 依存関係のバージョン制約の自動調整。内容を提示し、判断はユーザーに委ねます。
- スターターキットのフォーク。リポジトリの構成が公式キット 3 種のいずれとも認識できない場合は、拒否して理由を説明します。
- キット間の移行(例:Vue → React)。
- 適用したファイルに対するリンター/フォーマッターの実行。ユーザー自身のツールで実行してもらいます。