Claude CodeでCapacitorアプリを作る — cap syncの許可範囲の決め方
Claude CodeでCapacitorアプリを作る手順。npx cap syncまでを自動で回し、実機実行と署名ビルドは確認を挟むための権限ルールとCLAUDE.mdの書き方をまとめます。
Claude CodeでCapacitorアプリを作るときの要所は、npx cap sync までを自動で回し、実機実行と署名ビルドには確認を挟むという線引きです。CapacitorはビルドしたWeb資産をiOSとAndroidのネイティブシェルに載せる方式で、Web側の変更は「ビルド → sync」の2手順でネイティブ側に届きます。この2手順を安全に任せられれば、Claude Codeは画面の実装から動作確認の準備までを一続きで進められます。
Capacitorは何をするツールか
Capacitorは、既存のWebアプリをiOS・Androidのアプリとして動かすためのランタイムとCLIです。iOSではWKWebViewの中で、AndroidではWebView(Android 10以降はAndroid System WebView)の中でWebアプリが動きます。ネイティブUIを組み立てる方式ではなく、WebViewにWeb資産を表示する方式です。
既存のWebアプリにCapacitorを足すには、次の3つが揃っている必要があります。
package.jsonがあるdistやwwwのような、ビルド成果物だけを入れる別ディレクトリがある- そのディレクトリの直下に
index.htmlがある
index.html には <head> タグが必須です。<head> が無いとCapacitorのプラグインが動きません。AIにHTMLを書かせた直後は、この点だけ先に確認します。
動作環境は、iOSがiOS 15以上でXcode 26.0以上、AndroidがAPI 24(Android 7)以上です。iOSのビルドにはXcodeが要るため、macOS以外では実機ビルドまで進められません。
導入を非対話のコマンドに分ける
導入手順は次の流れです。ゼロから始める場合は npm init @capacitor/app@latest で雛形を作れます。既存のWebアプリに足す場合は、コアとCLIを入れてから設定ファイルを作り、プラットフォームを追加します。
npm i @capacitor/core
npm i -D @capacitor/cli
npx cap init "My App" com.example.myapp --web-dir dist
npm i @capacitor/android @capacitor/ios
npx cap add android
npx cap add ios素の npx cap init はアプリ名やアプリIDを対話形式で聞いてきます。Claude Codeに任せるなら、引数で渡す形が向いています。npx cap init <appName> <appID> に --web-dir を付けると、質問なしでWebアプリの出力先まで指定できます。
webDir はCapacitor設定ファイル(capacitor.config.ts か capacitor.config.json)に書かれます。フレームワークごとにビルド出力の場所が違い、Angularなら www、Reactなら build、Vueなら public が例に挙がっています。公式は、初回のsyncで問題が出たら webDir を見直すよう案内しています。
import { CapacitorConfig } from '@capacitor/cli';
const config: CapacitorConfig = {
appId: 'com.example.myapp',
appName: 'My App',
webDir: 'dist',
};
export default config;cap syncは何をして、何を巻き込むか
Webの変更をネイティブに反映するコマンドは npx cap sync です。syncは copy を実行してから update を実行します。コマンドを分けて見ると、許可の線引きがしやすくなります。
| コマンド | やること | 影響の範囲 |
|---|---|---|
npx cap copy | やることビルド済みWeb資産と設定ファイルをネイティブプロジェクトにコピー | 影響の範囲ネイティブ側のWeb資産と設定 |
npx cap update | やることpackage.json が参照するネイティブプラグインと依存関係を更新 | 影響の範囲ネイティブ側の依存関係 |
npx cap sync | やることcopy のあとに update | 影響の範囲上の両方 |
npx cap add <platform> | やることandroid か ios のネイティブプロジェクトを追加 | 影響の範囲プロジェクト構造 |
npx cap run <platform> | やることsync のあと、ビルドして端末へ配備 | 影響の範囲端末・エミュレーター |
npx cap build <platform> | やること署名済みのAAB・APK・IPAを生成 | 影響の範囲配布物 |
cap run は内部で sync を先に走らせます。--no-sync を付けたときだけ省略されます。つまり cap run を許可すると、syncの許可も一緒に出すことになります。
syncにはいくつか巻き込みがあります。
--deploymentオプションは「Podfile.lockを削除せず、pod installを--deploymentで実行する」という説明です。裏を返すと、オプション無しではPodfile.lockが削除される扱いです。iOSをCocoaPodsで管理しているプロジェクトでは、lockファイルの差分が出る前提で見ます- iOSでSwift Package Managerを選んだプロジェクトでは、プラグインの追加時に
CapApp-SPMパッケージをCLIが書き換えます。中身は手で触らないよう案内されています package.jsonのscriptsにcapacitor:sync:beforeやcapacitor:sync:afterを書くと、syncの前後でその内容が実行されます。copyとupdateにも同様のフックがあります
最後のフックは見落としやすい点です。npx cap sync を許可すると、package.json に書かれたフックのコマンドも、syncの一部として実行されます。Claude Codeに package.json を編集させる場面では、capacitor: で始まるスクリプトが増えていないかを差分で確かめます。
どこまで許可するか — settings.jsonの設計
Claude Codeの許可ルールは、deny、ask、allowの順に評価され、最初に一致したものが結果を決めます。askとallowの両方に一致する場合は、askが優先されて確認が出ます。この性質を使うと、「普段は通すが、特定のサブコマンドだけ確認する」構成を作れます。
Bashルールの * は空白を含む任意の文字列に一致します。末尾が * なら、引数なしの素のコマンドにも一致します。npx はClaude Codeのラッパー除去リストに入っていないため、ルールには npx cap ... まで含めて書きます。Bash(npx *) のように広く書くと、どのパッケージでも通ってしまいます。
次は、Capacitor向けの設定例です。
{
"permissions": {
"allow": [
"Bash(npm run build)",
"Bash(npx cap sync *)",
"Bash(npx cap copy *)",
"Bash(npx cap ls *)"
],
"ask": [
"Bash(npx cap add *)",
"Bash(npx cap update *)",
"Bash(npx cap run *)",
"Bash(npx cap open *)",
"Bash(npx cap build *)"
],
"deny": [
"Bash(git push *)"
]
}
}この分け方の根拠は次のとおりです。
- allowに置くもの: Webのビルドと、
sync/copy/ls。Web資産をコピーするだけで、端末にもストアにも触れません - askに置くもの:
addはネイティブプロジェクトの構造を作り、updateは依存関係を変え、runは端末へ配備します。openはXcodeやAndroid Studioを起動します buildを確認付きにする理由: 署名の鍵情報を扱うコマンドです。--keystorepassや--keystorealiaspassのように、パスワードをコマンドライン引数で渡すオプションがあります。パスワード入りのコマンドになりうるため、実行前に人間が目で見る位置に置きます
npm run build && npx cap sync のような複合コマンドは、&& で分けた各部分が個別に評価されます。両方に許可ルールが要ります。複合コマンドの扱いはClaude CodeのBash権限ルールは複合コマンドをどう評価するかで詳しく見られます。
毎回ビルドとsyncを並べて書くのが煩わしければ、package.json にまとめた1本のスクリプトを用意します。
{
"scripts": {
"cap:sync": "npm run build && cap sync"
}
}こうしておけば、許可ルールは Bash(npm run cap:sync) の1行で済みます。Claude Codeには「変更したらcap:syncを回す」とだけ伝えられます。
CLAUDE.mdに書く規約の断片
許可ルールは「できること」を決めるだけで、「やるべき順序」は伝えません。順序と禁止事項は CLAUDE.md に書きます。次はCapacitorプロジェクト向けの断片です。
## ビルドと同期
- Webコードを変更したら `npm run cap:sync` を実行する(ビルドとsyncを兼ねる)
- sync が通るまで完了扱いにしない。エラーが出たら出力を読んで直す
- `webDir`(dist)の中身は生成物。直接編集せず、元のソースを直す
## 触らない場所
- iOS を SPM で管理している場合、`CapApp-SPM` の中身は編集しない
- `capacitor.config.ts` の `server.url` と `server.cleartext` を残さない
- `package.json` に `capacitor:*` スクリプトを追加するときは理由を書いて確認を取る
## 実機・署名
- `cap run` は `--list` で端末を確認し、`--target` で指定する
- `cap build` と署名情報は人間が実行するポイントは3つあります。
1つ目は、完了の判定を「syncが通ること」に置いている点です。「Web資産のディレクトリが見つからない」というエラーが出たら webDir を直す、とされています。つまり、ビルドとsyncの成功は、ネイティブに渡せる状態になったことを機械的に確かめる材料になります。
2つ目は、server.url と server.cleartext の扱いです。この2つはライブリロード用の設定で、「本番での使用は意図していない」と明記されています。AndroidではAPI 28以降、平文通信が既定で禁止されており、cleartext はそれを開けるための設定です。Claude Codeが開発中に書いたまま残すと、本番ビルドにも入ります。CLAUDE.mdで残さないよう縛り、PRの差分でも確かめます。
3つ目は、cap run にターゲットを指定させている点です。npx cap run ios を引数なしで実行すると、ターゲットの選択を促されます。自動で進める作業には向かないため、--list で一覧を取ってから --target か --target-name を渡す形にします。
ライブリロードは設定ファイルを触らずに使う
実機でWebの変更を即座に確かめたいときは、ライブリロードを使います。server.url に開発サーバーのURLを書く方法もありますが、cap run には --live-reload(短縮形は -l)があります。--host、--port、--https を組み合わせて、URLをコマンドラインだけで組み立てられます。このオプションは server.url の設定を上書きします。
npx cap run android --live-reload --host 192.168.0.10 --port 5173設定ファイルを書き換えずに済むため、先ほどの「server.url を残さない」規約と相性が良い使い方です。Androidでは --forwardPorts で adb reverse を自動実行できます。
ネイティブ設定の変更は人間の目を通す
Capacitorの権限周りは、WebではなくネイティブのファイルにAIが触れる場面です。
- iOS: カメラや位置情報などのAPIを使うプラグインを入れたら、
Info.plistに利用目的の説明(UsageDescriptionを含むキー)が要ります。CapabilitiesはInfo.plistではなく、XcodeのSigning & Capabilitiesで設定します - Android: 使う権限は
android/app/src/main/AndroidManifest.xmlの<manifest>タグ内に<uses-permission>として書きます
どちらも、プラグインの導入に伴って必要になるファイルです。Claude Codeにプラグイン追加まで任せる場合は、git diff でこの2ファイルの変更を確認する手順をCLAUDE.mdに入れておくと、権限の追加を見落としにくくなります。
iPadOS 26向けの注意もあります。新しいウィンドウ操作ボタンがアプリのUIに重なることがあり、暫定対応として Info.plist に UIDesignRequiresCompatibility = YES を足す方法が案内されています。恒久的な対応ではないとされている点も押さえておきます。
他のモバイル開発方式との使い分け
Capacitorの強みは、既存のWebアプリをそのままネイティブアプリの形にできる点です。Web側の資産が多い、あるいは画面をWebで作り込む前提のチームに向いています。
ネイティブコンポーネントで画面を組む方式は、別の記事で手順を取り上げています。Claude CodeでReact Nativeアプリを実機ビルドまで作る手順、Claude CodeでExpoアプリ開発を進める手順、Claude CodeでFlutterアプリを開発する手順が該当します。Capacitorでは、画面の見た目と挙動はWebのコードで決まります。ネイティブのコードを書くのは、プラグインや権限周りが中心です。
CLAUDE.mdに書く内容も、この違いに沿って変わります。Capacitorの場合は「Web側のビルドと同期」が中心で、ネイティブ側は触らない場所を決める形になります。
よくあるつまずき
- web assets directoryが見つからない:
webDirがビルド出力と一致していません。Capacitor設定ファイルのwebDirを、実際の出力先に合わせます - プラグインが動かない:
index.htmlに<head>タグがありません。Capacitorが注入できず、プラグインが動作しなくなります - バージョンが食い違う: コア、Android、iOSのパッケージは同じバージョンに揃えます。更新は
npm i @capacitor/core @capacitor/ios @capacitor/androidとnpm i -D @capacitor/cliを一度に実行します - Androidのエミュレーターでうまく動かない: API 24以上のシステムイメージが必要です。System WebViewはエミュレーターで自動更新されません
- CocoaPodsからSPMへ移したい: 手作業で移す前に、
npx cap spm-migration-assistantが使えます。ただし手動の手順が2つ残り、SPM版が無いプラグインは警告が出ます。実行前にgitの状態を確認します - Webの変更が反映されない: ビルドを飛ばしてsyncだけ実行している可能性があります。syncはビルド済みのバンドルを渡すだけで、Webのビルドは行いません
まとめ
Capacitorでは、Webのビルドと cap sync がネイティブへの入り口です。この2手順をallowに置き、add update run open build はaskで確認を挟み、順序と触らない場所はCLAUDE.mdに書く、という三層で分けると、Claude Codeに任せる範囲が明確になります。syncが通ることを完了の条件にしておけば、Claude Codeが渡したものがネイティブ側に届く状態かを、毎回機械的に確かめられます。