compose-agent について
compose-agentは、現代のAndroidXの指針に沿ってJetpack Composeコードをレビュー、作成、修正するスキルです。状態、ライフサイクル、性能、ナビゲーション、テスト、アニメーションなどを扱います。
主な機能
- 01非推奨APIとAndroidX指針の確認
- 02状態、副作用、Flow収集の検査
- 03性能とModifierの使い方の評価
- 04ナビゲーション、アニメーション、ページングの指針
- 05Composeコード作成時のルール適用
活用シーン
- 01画面のFlow収集がライフサイクルに沿うか確認
- 02状態とModifierを適切に使うComposableの作成
- 03ページングリストの安定キーと読み込み状態の確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ hamen/ compose_skill --skill compose-agentインストール後、Claude Code で/compose-agentを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
.claude-plugin/plugin.json.cursor-plugin/plugin.jsonevals/evals.jsonreferences/animation.mdapi.mdcomponent-api.mdconcurrency.mdeffects.mdflows.mdfocus.mdkmp.mdkotlin.mdmodifiers.mdnavigation.mdpaging.mdperformance.mdstate.mdtesting.md詳細
Compose Agent
Compose コードをレビュー、作成、または変更し、正確性、最新 API の使用、公式 AndroidX ガイドラインへの準拠を確認します。本当に問題がある場合のみ報告し、細かな点をあげつらったり、問題をでっち上げたりしないでください。
対象トラック(リポジトリに別の指定がない場合):
- Kotlin
2.0.20+、Compose Compiler1.5.4+(Strong Skipping Mode はデフォルトで有効)。 - 最新の Jetpack Compose BOM、Material 3、
collectAsStateWithLifecycle()を備えたandroidx.lifecycle。 - プロジェクトで採用できる場合は Navigation 3(
androidx.navigation3)、それ以外の場合は Navigation 2.8 以降。 - 非同期処理のプリミティブには Coroutines と Flow を使用します。RxJava やブロッキング I/O は選択しないでください。
リポジトリで古いバージョンが固定されている場合は、そのバージョンに合わせます。ただし、最新の方法がどのようなものかを 1 行の注記で説明してください。
レビュープロセス
references/api.mdを使用して、非推奨または段階的に非推奨となっている API を確認します。references/state.mdを使用して、状態とデータフローを検証します。references/effects.mdを使用して、副作用の選択(LaunchedEffect、DisposableEffect、produceState、snapshotFlow、rememberUpdatedState)を検証します。references/performance.mdを使用して、コンポーザブルのパフォーマンス(安定性、ラムダ修飾子、遅延リストのキー、遅延読み取り、フェーズをまたぐ書き戻し)をレビューします。references/modifiers.mdを使用して、修飾子の使い方(順序、ラムダ形式、composed { }よりModifier.Nodeを優先)をレビューします。references/navigation.mdを使用してナビゲーションをレビューします。呼び出し箇所に手を加える前に、判断表(Nav3、型安全な Nav2、プレーンな状態)から確認してください。references/concurrency.mdを使用して、コルーチンとライフサイクルに応じたコレクションをレビューします。references/flows.mdを使用して、Flow 演算子と StateFlow / SharedFlow の構成(stateIn、shareIn、flatMapの各バリアント、combine、エラー処理、バックプレッシャー、asStateFlow())をレビューします。references/component-api.mdを使用して、コンポーザブル API の構成(パラメーターの順序、スロット、命名、デフォルト値)をレビューします。- テスト、セマンティクス、スクリーンショット、プレビュー、または UI のフェイク依存関係が対象に含まれる場合は、
references/testing.mdを使用してUI テストのパターンをレビューします。 - UI でフォーカス API、キーボード入力、TV、デスクトップ、ChromeOS、またはアクセシビリティのフォーカス動作を使用している場合は、
references/focus.mdを使用してフォーカスとキーボード/D-pad ナビゲーションをレビューします。 - 共通コード、
expect/actual、プラットフォームサービス、ネイティブビュー、または共有 UI ターゲットが対象に含まれる場合は、references/kmp.mdを使用してCompose Multiplatform / KMP の境界をレビューします。 animate*、Animatable、Transition、AnimatedVisibility、AnimatedContent、または無限トランジション API が対象に含まれる場合は、references/animation.mdを使用してアニメーション API の選択とライフサイクル(宣言型と命令型の選択、rememberされたAnimatable、ターゲットに応じた起動、tweenよりspringを優先、遅延されたアニメーション読み取り、AnimatedContentのキー)をレビューします。LazyPagingItems、PagingData、またはcollectAsLazyPagingItemsが対象に含まれる場合は、references/paging.mdを使用してCompose での Paging 3(collectAsLazyPagingItems(paging differ。PagingDataには適用されないcollectAsStateWithLifecycleではありません)、安定したitemKey、LoadStateの分岐、ユーザー操作によるrefresh()/retry())をレビューします。references/kotlin.mdを使用して、最後にKotlin スタイルを確認します。
部分的なレビューを行う場合は、関連する参照ファイルのみを読み込んでください。各参照ファイルは単独で読めるように作られています。
基本指示
- コンポーザブルは、コンポジション中に副作用を発生させないでください。I/O、remember されたオブジェクト外部の状態変更、分析などは、
LaunchedEffect、DisposableEffect、produceState、ViewModel、またはイベント/ジェスチャー駆動の処理に限り、ハンドラー内のrememberCoroutineScope().launchで行います。ターゲット駆動のアニメーションは、コンポジション本体のscope.launchではなく、LaunchedEffectまたは宣言的なanimate*AsState/updateTransitionを使用します。references/animation.mdとreferences/effects.mdを参照してください。 Modifierを受け取るすべてのコンポーザブルでは、パラメーター名をmodifierとし、型をModifier = Modifierにして、最外層のレイアウトにのみ適用します。転送しないmodifierパラメーターを作成しないでください。- コンポーネントのパラメーター順は、必須データ →
modifier: Modifier = Modifier→ デフォルト値を持つその他の省略可能なパラメーター → 最後に末尾のcontent: @Composable () -> Unitスロットです。コンポーネントごとにmodifierパラメーターは1つだけにします。 - ステートフルな API では、ステートレスなオーバーロードと、
rememberをホイストするステートフルな便利版を公開します。references/component-api.mdを参照してください。 - UI コードでは、Flow を
collectAsStateWithLifecycle()で収集します。通常のcollectAsState()は画面が表示されていない間も収集を続け、バッテリーと帯域幅を消費します。例外:Flow<PagingData<T>>はcollectAsLazyPagingItems()で収集し、collectAsStateWithLifecycle()は使用しません。references/paging.mdを参照してください。 - 値をシリアル化できない、または導出可能な場合を除き、構成変更やプロセス終了後も保持する UI 状態には
rememberよりrememberSaveableを優先します。 - プリミティブ型の状態には、型付き状態ファクトリー(
mutableIntStateOf、mutableLongStateOf、mutableFloatStateOf、mutableDoubleStateOf)を使用します。生のmutableStateOf<Int>(...)はボックス化されます。 - あるフェーズですでに読み取られた状態に書き込まないでください。レイアウトコールバック(
onSizeChanged/onGloballyPositioned/onPlaced)から、コンポジション中に兄弟要素が読み取る状態へ書き込んではいけません(フェーズをまたぐ逆方向の書き込み、measure → recompose ループになります)。また、その状態を読み取るコンポーザブル本体内でmutableStateListOf/mutableStateMapOfを変更してはいけません(コンポジションフェーズで自身が無効化され、変更 → 再コンポーズのループになります)。測定値はレイアウト/描画で使用するか、イベント/LaunchedEffect/状態ホルダーからスナップショット状態を変更してください。references/performance.mdを参照してください。 - 効果のない「最適化」を持ち込まないでください。純粋関数に対する
remember(index)や、Strong Skipping(SSM)が不安定なキャプチャーを含むラムダも自動でメモ化するのに、すでに自動メモ化されているコールバックをrememberすることなどが該当します。パターンを使っているかどうかではなく、Layout Inspector のカウントやコンパイラーレポートで再コンポーズの改善を確認してください。references/performance.mdを参照してください。 CompositionLocalにラムダを入れないでください。明示的なパラメーターを使用します。- 事前に確認せず、サードパーティーのライブラリーを追加しないでください。ページャー、スワイプリフレッシュ、フローレイアウト、システム UI コントローラーを提供する Accompanist ライブラリーは非推奨です。これらの機能は現在 AndroidX にあります(
HorizontalPager、PullToRefreshBox、FlowRow/FlowColumn、enableEdgeToEdge())。 - 要素の背景が(直接、またはブレンドを介して)
MaterialTheme.colorScheme.*を参照する場合、テキストとアイコンの色も同じテーマの参照元から取得する必要があります。テーマ駆動の背景に、ハードコードされたColor.Black/Color.White/生の ARGB リテラルを使用すると、ダークモードで問題が発生します。references/component-api.mdを参照してください。 - まず宣言的なアニメーションを使用します。状態駆動の値が1つなら
animate*AsState、同期する値ならupdateTransitionを使い、命令型の制御(ジェスチャー、フリング、シーケンス)が必要な場合に限りAnimatableを使用します。Animatableは必ずrememberし、ターゲット駆動のアニメーションはLaunchedEffectから開始します(状態への反応にコンポジション中のscope.launchを使わないでください)。中断可能な動きにはtweenよりspring()を優先し、アニメーション値はレイアウト/描画フェーズ(ラムダ修飾子/graphicsLayer)で読み取って、毎フレームの再コンポーズを避けます。references/animation.mdを参照してください。 - ページ分割されたフィードでは、
collectAsLazyPagingItems()を使用し、安定したドメイン ID を指定したitems(count = …, key = …)で描画します。読み込み/空/エラー/追加の各状態はloadStateで分岐し、refresh()/retry()はユーザー操作に限定して、コンポジション本体では呼び出さないでください。references/paging.mdを参照してください。
出力形式(レビュー モード)
問題をファイルごとに整理します。問題ごとに、以下を示してください。
- ファイルと該当する行番号を示します。
- 違反しているルールの名前を示します(例:「
collectAsState()``ではなくcollectAsStateWithLifecycle()`を使用する」)。 - 修正前と修正後のコードを簡潔に示します。
- 公式の参照元(
developer.android.com/...または AndroidX コンポーネントのガイドライン)へのリンクを記載します。
問題のないファイルは省略します。最後に、優先度の高い順に最大3項目の要約を記載します。
出力例
feature/profile/ProfileScreen.kt
34 行目:collectAsStateWithLifecycle()を使用してください — UI Flow は画面が STARTED でなくなったら収集を停止する必要があります。
// Before
val user by viewModel.user.collectAsState()
// After
val user by viewModel.user.collectAsStateWithLifecycle()
https://developer.android.com/topic/architecture/ui-layer/state-production#continuous-vs-discrete
58 行目:modifierは内部コンテンツではなく、最も外側のレイアウトに渡してください。
// Before
@Composable
fun UserCard(user: User, modifier: Modifier = Modifier) {
Column {
Text(user.name, modifier = modifier) // modifier swallowed here
Text(user.email)
}
}
// After
@Composable
fun UserCard(user: User, modifier: Modifier = Modifier) {
Column(modifier = modifier) {
Text(user.name)
Text(user.email)
}
}
https://developer.android.com/develop/ui/compose/api-guidelines#naming-modifiers
72 行目:ラムダ形式を使って、アニメーションのoffsetの読み取りをレイアウトフェーズまで遅らせてください。
// Before
val dx by animateDpAsState(targetValue = if (expanded) 0.dp else (-200).dp, label = "drawerOffset")
Box(modifier = Modifier.offset(x = dx))
// After — layout-phase read, skips recomposition on every frame
val dx by animateDpAsState(targetValue = if (expanded) 0.dp else (-200).dp, label = "drawerOffset")
Box(modifier = Modifier.offset { IntOffset(dx.roundToPx(), 0) })
要約
- パフォーマンス(高):
ProfileScreen.kt:72、HomeScreen.kt:110、DetailScreen.kt:88では、アニメーション値を遅延せずに読み取るため、フレームごとに再コンポーズが発生します。修飾子をラムダ形式に切り替えてください。 - ライフサイクル(高): 3 つの画面で
collectAsState()が使用されています。バックグラウンドでの収集を避けるため、collectAsStateWithLifecycle()に置き換えてください。 - API の形状(中):
UserCardはmodifierパラメーターを使っていません。最も外側のレイアウトに渡してください。
例ここまで。
作成モード
エージェントがコードをレビューするのではなく新しいコードを作成する場合も、同じルールをガードレールとして適用します。コンポーザブルを生成する前に、以下を内部で確認してください。
modifier: Modifier = Modifierを受け取っていますか?- 状態はホイストされていますか? それとも、この場所で状態を所有する明確な理由がありますか?
- リストを表示する場合、安定した
key =を使っていますか? - 処理を開始する場合、その処理はコンポジション本体ではなく、
LaunchedEffect、produceState、または ViewModel 内にありますか? - Flow を収集する場合、
collectAsStateWithLifecycle()を使っていますか?(例外はFlow<PagingData<T>>で、この場合はcollectAsLazyPagingItems()を使います。) - パラメーターの順序は、データ → modifier → その他 → 最後にコンテンツスロットになっていますか?
- アニメーションを行う場合、まず宣言的な API を使い、必要な箇所で状態を記憶し、ライフサイクルを考慮し、適切なフェーズで処理していますか?
- レイアウトコールバックやスナップショット収集の変更によって書き込まれた状態が、コンポジションで再び読み取られることはありませんか(フェーズをまたぐ、または自己無効化するループ)?
- ページングを行う場合、キーは安定しており、
LoadStateが処理され、更新や再試行はユーザー操作によって行われますか? - フォーカス、テスト、またはプラットフォーム固有の動作が必要な場合、評価する前に該当するリファレンスを読み込みましたか?
意図的な理由がないのに、どれかの答えが「いいえ」であれば、コードを返す前に修正してください。
このスキルの対象外
v1 では、以下は対象外です。別のスキルに委任するか、明示的に範囲を絞ってください。
- Material 3 への準拠、テーマ設定、デザイントークン —
material-3スキルを使用してください。 - 既存のコードベースを数値評価すること — 同じリポジトリ内の関連スキル
jetpack-compose-auditを使用してください。 - Wear OS/TV/Auto/Glance の詳細なプラットフォームレビュー — このスキルではフォーカスとキーボード/D-pad の基本を扱うようになりましたが、プラットフォーム認定全体は対象外です。
- アクセシビリティの詳細なレビュー — 明らかな不足(
contentDescriptionの欠落、ラベルのないアイコンのみのボタン、48dp 未満のタッチターゲット)は指摘しますが、評価は行いません。
ユーザーが上記のいずれかを求める場合は、範囲を絞り、その旨を伝えてください。
参考資料
references/api.md— 非推奨および段階的に非推奨となっている Compose API と、最新の代替 API。references/state.md— 状態のホイスティング、rememberとrememberSaveableの違い、ViewModel の境界、derivedStateOf。references/effects.md—LaunchedEffect、DisposableEffect、SideEffect、produceState、snapshotFlow、rememberUpdatedState。references/performance.md— 安定性、Strong Skipping Mode、ラムダ修飾子、遅延キー、型付き状態ファクトリー、読み取りの遅延。references/modifiers.md— 修飾子の順序、ラムダ形式、composed { }よりModifier.Nodeを優先すること。references/navigation.md— 選択用の表(Nav3、型安全な Nav2、通常の状態の比較)、Navigation 3、および Navigation 2 から置き換えるもの。references/concurrency.md— コルーチン、Flow、collectAsStateWithLifecycle、repeatOnLifecycle、スコープの選択。references/flows.md— 演算子の選択:StateFlowとSharedFlowとコールドFlow、stateIn(WhileSubscribed)、shareIn、flatMapの各バリアント、combine/merge/zip、エラー処理、バックプレッシャー、asStateFlow()による公開。references/component-api.md— コンポーザブル API のガイドライン:パラメーターの順序、スロット、命名、デフォルト値、状態のホイスティングの形式。references/testing.md— Compose UI テスト、セマンティクスのアサーション、スクリーンショットテスト、画像/プラットフォームサービスのフェイク、プレビュー。references/focus.md—FocusRequester、フォーカスの復元、キーボード/D-pad 入力、キーイベントハンドラー、フォーカステスト。references/kmp.md— Kotlin Multiplatform と Compose Multiplatform の境界、expect/actual、インターフェース、プラットフォーム固有の末端コンポーザブル。references/animation.md— アニメーション API の選択(animate*AsStateとupdateTransitionとAnimatable)、rememberするアニメーション状態、ターゲット駆動の起動、ジェスチャー駆動のsnapTo/animateDecay、spring/tween/keyframes、モーション低減、アニメーション読み取りの遅延、AnimatedContent/AnimatedVisibility、animateItem()、画面外の無限遷移。references/paging.md— Compose の Paging 3:ページングと通常のリストの使い分け、collectAsLazyPagingItems、安定したitemKey、LoadStateの UI、引っ張って更新、ページングされた遅延リストにおけるアンチパターン。references/kotlin.md— Kotlin のコーディング規約と、LLM が見落としがちな Android Kotlin スタイル。
受け入れ評価
evals/evals.json には、Compose のコードを作成するよう求めるプロンプトと、生成コードが満たすべき期待事項からなる、書き込みモードの受け入れケースが含まれています。ケース 0~2 は jetpack-compose-audit の採点ルール(フェーズをまたぐ書き戻し、Strong Skipping の誤った手がかり、スナップショットによる自己無効化)と 1 対 1 で対応しています。bin/ci が両者の同期を保つため、作成パスが監査パスから乖離することはありません。ケース 3~4 では、基礎となるフェーズに即した読み取りと、ライフサイクルを考慮した Flow の収集が追加されています。このスキルを読み込んだモデルを各プロンプトに対して実行し、すべての期待事項を確認してください。
主要な情報源
このスキルのすべてのルールは、次のいずれかを根拠としています。
https://developer.android.com/develop/ui/composeとそのサブページhttps://developer.android.com/kotlin/style-guidehttps://kotlinlang.org/docs/coding-conventions.htmlhttps://www.jetbrains.com/help/kotlin-multiplatform-dev/https://android.googlesource.com/platform/frameworks/support/+/androidx-main/compose/docs/compose-api-guidelines.mdhttps://android.googlesource.com/platform/frameworks/support/+/androidx-main/compose/docs/compose-component-api-guidelines.md
補足情報源(ブログ記事、カンファレンストークなど)がこれらと異なる見解を示す場合は、主要な情報源を優先してください。