既存のFlutterプロジェクトにAIエージェントサポートを入れてみる
Tactic Strikers はFlutterで作ったOmega Strikers用の戦略共有ツールである。
オメスト人気が下火になった今(2025/03現在)でも大会等があり、海外勢を中心に少し需要があるっぽい。
これはもともと自分が温かみのある手打ちのコードで作ったサービスで、AIの手が介在することはあまり想定していなかった。
ゲーム自体もメンテナンスモードなのでもう大きく手を加える気もあんまりない(マップ追加とかキャラ追加とかは画像用意してちょっとenumいじるだけだし、それももう無いと明言されてる)が、せっかく使ってくれてる人がいるなら多少のメンテナンスはしようと思った。
それとは別軸に、今AIエージェントを既存のプロジェクトで使っていくためのノウハウが欲しくて、ちょうどいい題材がそこにあるではないかということでやってみることに。
ちなみにTactic Strikersのソースは公開してない(公開するメリットを特に感じなかったので)
今回想定するAIエージェント:GitHub Copilot Edits (agent mode)
Kurogoma4D.icon Copilotもともと使ってたし、CursorとかClineとか新しく導入するよりもお手軽かなって
AIエージェントサポートとは:
AIエージェントに作業を依頼できる十分な環境が揃っていること、と定義する。
個人的には、Custom Instructionとプロジェクトの仕様がmarkdownとかで書かれてる文章がある、というのが最低限の環境かな、と思ってる
あとはディレクトリ構造とか自然言語でのコーディング規約とかがCustom Instructionに書かれていると想定して、それにプロジェクトの構造が一致していることも前提になるかな
Custom Instructionを用意したとき、
既存のプロジェクト構造を決まったCustom Instruction(に含まれるプロジェクト構造)に寄せる方向性がいいのか
既存のプロジェクト構造を遵守する形でCustom Instructionを用意するのがいいのか
というのが気になってるので特にそのへんを見ていく
というわけでCustom Instructionを用意する
→ また別にそのためのリポジトリを用意した
https://github.com/Kurogoma4D/agents-lab
某Mizchi氏がGitHubに置いているCline用のCustom Instructionを参考に、個別トピックのInstructionをマージして1ファイルにするような感じのツール
まずはプロジェクト構造をCustom Instructionの内容に合わせるためのタスクを書き出してもらった
code:md
# Tactic Strikers 改善タスク
このドキュメントは、Flutterフロントエンドプロジェクトとしてのベストプラクティスに基づいた改善タスクをまとめたものです。
## アーキテクチャの改善
### 1. クリーンアーキテクチャの適用
- **レイヤー構造の整理**
- プレゼンテーション層:UIコンポーネントとイベントハンドラ
- アプリケーション層:ユースケースとビジネスロジック
- ドメイン層:ビジネスモデルと中核ロジック
- データ層:APIクライアントやストレージアクセス
- 各レイヤーはFlutterのベストプラクティスに従い整理
- **依存方向の明確化**
- 外側のレイヤーが内側のレイヤーに依存する一方向の依存関係を確立
- プレゼンテーション層はビジネスロジックを知らない設計に変更
- 依存性逆転の原則に基づく適切な抽象化の導入
- **状態管理の改善**
- Riverpodを活用した明確な状態管理の設計
- プレゼンテーション層とビジネスロジックの分離
- イミュータブルな状態管理の徹底
### 2. テスト駆動開発 (TDD) の適用
- **単体テストの追加**
- ドメインモデル、ユースケース、リポジトリの単体テスト
- テストディレクトリ構造の整理
- テストヘルパーの作成
- **ウィジェットテストの追加**
- キーとなるウィジェットのテスト(Stage, StrikerIcon, AbilityIcon など)
## コード品質の改善
### 1. Widgetの設計改善
- **Widgetの責任範囲の明確化**
- 単一責任の原則に基づく適切な粒度でのWidget分割
- constコンストラクタの活用徹底
- 状態変更の範囲を限定するWidget設計
- **再利用可能性の向上**
- 共通パターンのコンポーネント化
- プロパティの適切な抽象化
- ビルド範囲の最適化
### 2. 状態管理とデータフローの最適化
- **Riverpodベースの状態管理**
- Provider設計の見直し
- 適切なスコープでの状態管理
- Watch/Selectによる効率的な購読の実装
- **イミュータブルな状態管理**
- copyWithパターンの活用
- 状態更新の一方向データフロー化
- 必要最小限のリビルド
### 3. パフォーマンスの最適化
- **不要な再構築の削減**
- メモ化の活用
- constコンストラクタのさらなる活用
- リビルド範囲の最小化
## ユーザビリティの向上
- **エラー処理とフィードバックの改善**
- ユーザーエラーのフィードバック
- 適切なエラーメッセージとリカバリー
- エラー状態のハンドリング改善
- **アクセシビリティの向上**
- セマンティックラベルの追加
- キーボード操作のサポート
- ローカライゼーションの強化
## ドキュメントと保守性
- **コード内ドキュメントの強化**
- 各クラスや重要な関数へのドキュメントコメント追加
- 責任範囲と使用方法の明確化
- パブリックAPIのドキュメント化
- **アーキテクチャドキュメントの作成**
- 全体構造の図解
- データフロー図
- 責任範囲の明確化
## 次のステップ
1. 最初に取り組むべき優先タスク:
- UIコンポーネントの整理と責任範囲の明確化
- Riverpodを活用した状態管理の最適化
- ユニットテストとWidgetテストの追加
2. 中期的なタスク:
- コンポーネントライブラリの整備
- パフォーマンス最適化
- エラー処理とフィードバックの改善
3. 長期的な目標:
- 完全なテストカバレッジ
- デザインシステムの確立
- CI/CDパイプラインの最適化
そんで仕様一覧も書き出してもらった
code:md
# Tactic Strikers 仕様書
## アプリケーション概要
Tactic Strikersは、ゲーム(Omega Strikers関連)のタクティカルボードアプリケーションです。ユーザーはゲームキャラクター(Striker)とその能力(Ability)をドラッグ&ドロップで配置し、戦術を視覚的に構築・共有できます。
## 主要機能
### 1. Strikerの配置機能
- 21種類のStrikerをドラッグ&ドロップで配置可能
- 各Strikerは味方/敵の2種類の状態を切り替え可能
- 配置したStrikerは再度ドラッグして位置を調整可能
### 2. Abilityの配置機能
- 各Strikerは固有のAbility(通常3種類)を持つ
- 共有Ability(Strike, Evade, Coreflip)が全Strikerで使用可能
- Abilityはドラッグ&ドロップでステージ上に配置可能
### 3. マップ選択機能
- 14種類のマップから選択可能
- マップ背景画像はWebから取得
### 4. コア配置機能
- ゲームのコア(中央オブジェクト)を自由に配置可能
- デフォルトではステージ中央に配置される
### 5. 描画機能
- ステージ上に線や図形を描画可能(StagePaintコンポーネント)
- タクティクスを視覚的に表現するための補助機能
### 6. 多言語対応
- 現在は日本語(ja)と英語(en)をサポート
- Flutterのローカリゼーションシステムにより実現
## データモデル
### Striker(キャラクター)
`dart
class Striker {
final StrikerId id; // 各キャラクターの識別子
final bool isEnemy; // 味方/敵の識別フラグ
}
`
- 全21種類のキャラクターが定義されている
- 各Strikerは固有のアビリティと画像を持つ
### Ability(能力)
`dart
class Abillity {
final String imagePath; // 能力アイコンの画像パス
final String? alt; // 代替テキスト(オプション)
}
`
- 各Strikerは基本的に3種類の固有Abilityを持つ
- 一部のStrikerは特殊な追加Abilityを持つ(例:kazanは6種類)
### StrikerData / AbilityData(配置データ)
`dart
class StrikerData {
final Striker striker;
final Offset offset; // ステージ上の位置
}
class AbillityData {
final Abillity abillity;
final Offset offset; // ステージ上の位置
}
`
- ステージ上に配置されたStrikerとAbilityの情報
- Offsetはステージサイズに対する相対位置
### MapId(マップ)
- 全14種類のマップが定義されている
- 各マップは対応する画像と多言語の名前を持つ
## UI構成
### メインレイアウト
- AppBar: タイトルとメニュー(ライセンス、寄付リンク)
- メインコンテンツ
- ステージエリア: マップ背景、配置したStriker/Ability/Core、描画内容
- コントロールエリア: Striker/Ability選択パネル、マップ選択、味方/敵切替
### ステージ
- AspectRatioで固定比率を維持
- レスポンシブデザイン(スケールに応じて自動調整)
- ドラッグ&ドロップによる要素配置
### ツールバー
- ドローイングツール(StagePaint関連)のコントロール
- 画面サイズ変更時のスケール調整機能
## 技術仕様
### 状態管理
- Flutter Riverpodを使用
- 主要な状態:
- 配置されたStriker (strikerDatum)
- 配置されたAbility (abillityDatum)
- 選択中のマップ (currentMap)
- Coreの位置 (currentCoreOffset)
- 敵味方の切替状態 (_isEnemy)
- 現在のステージスケール (currentScale)
### UI/UXの特徴
- ドラッグ&ドロップによる直感的な操作
- マウスホバーでAbilityのポップアップ表示
- レスポンシブ設計(異なる画面サイズに対応)
### アセット管理
- リモートアセット: 画像はリモートURL (assetsOrigin) から取得
- アイコン: Striker/Abilityのアイコンは対応するパスから取得
### その他
- サードパーティライブラリ: Riverpod, Flutter Portal, Gap, Google Fontsなど
- ダークテーマベースのUI
- Web対応アプリケーション
- モバイル等他のプラットフォームについてはサポートしない
## 将来的な拡張可能性
- 保存/共有機能
- リアルタイムコラボレーション
- より多くの言語サポート
- アニメーション機能
まぁ大体あってるか…?
あとはこれをもとに改善タスクを進めていってどうか、という話
https://gyazo.com/f63e8f4846dcb774d5c2a4358ccd56ba
でかいタスクを任せると大体無限ループしたりする
ので適宜修正が必要
Editsだとこんな感じのプロンプトが出てきて一時停止できるので、修正用の指示を加える
https://gyazo.com/72a2fb199ee5d7136a3c0b8234f7abee
もしかしたらリファクタリング系のタスクはどちらかというとやらせないほうがいいか…?
ファイル単位の編集ならともかく
プロジェクト構造を全体的に変えるとなるといろいろ副作用的に変えられる確率が高い気がする
Kurogoma4D.icon AIおせっかい
結論↑の指示だとめちゃくちゃ色んなところ壊されて修復するのも面倒なレベルになったので、やっぱりプロジェクト構造を変える方向性はやめたほうが良さそう
なので、既存のプロジェクト構造をAIに理解させたうえで、必要なタスクをやっていくというのが良さそう
https://gyazo.com/c58d69f15b7d23b0bbb0e04c82e99880
めっちゃ自信満々だけどね、これ壊れてるんだ……
というわけでagents-labの方でもコーディングプラクティスのうちプロジェクト構造はプラグインできるように作らないとな〜