flutter-pro-max-cli 1.0.2 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/README.md +109 -16
  2. package/assets/data/flutter-performance.csv +36 -0
  3. package/assets/data/mobile-accessibility.csv +36 -0
  4. package/assets/data/ui-reasoning.csv +36 -0
  5. package/assets/scripts/__pycache__/core.cpython-312.pyc +0 -0
  6. package/assets/scripts/__pycache__/design_system.cpython-312.pyc +0 -0
  7. package/assets/{.claude/skills/flutter-pro-max/scripts → scripts}/core.py +72 -48
  8. package/assets/scripts/design_system.py +1074 -0
  9. package/assets/{.codebuddy/commands/scripts → scripts}/search.py +73 -2
  10. package/assets/templates/base/quick-reference.md +41 -0
  11. package/assets/templates/base/skill-content.md +179 -0
  12. package/assets/templates/platforms/agent.json +21 -0
  13. package/assets/templates/platforms/claude.json +21 -0
  14. package/assets/templates/platforms/codebuddy.json +21 -0
  15. package/assets/templates/platforms/codex.json +21 -0
  16. package/assets/templates/platforms/continue.json +21 -0
  17. package/assets/templates/platforms/copilot.json +18 -0
  18. package/assets/templates/platforms/cursor.json +18 -0
  19. package/assets/templates/platforms/gemini.json +21 -0
  20. package/assets/templates/platforms/kiro.json +18 -0
  21. package/assets/templates/platforms/opencode.json +21 -0
  22. package/assets/templates/platforms/qoder.json +21 -0
  23. package/assets/templates/platforms/roocode.json +18 -0
  24. package/assets/templates/platforms/trae.json +21 -0
  25. package/assets/templates/platforms/windsurf.json +18 -0
  26. package/dist/commands/init.js +13 -13
  27. package/dist/commands/update.d.ts +6 -0
  28. package/dist/commands/update.js +27 -0
  29. package/dist/commands/versions.d.ts +1 -0
  30. package/dist/commands/versions.js +36 -0
  31. package/dist/index.js +27 -1
  32. package/dist/types/index.d.ts +20 -1
  33. package/dist/types/index.js +4 -1
  34. package/dist/utils/detect.js +11 -1
  35. package/dist/utils/extract.d.ts +5 -0
  36. package/dist/utils/github.d.ts +11 -0
  37. package/dist/utils/github.js +81 -0
  38. package/dist/utils/template.d.ts +25 -0
  39. package/dist/utils/template.js +194 -0
  40. package/package.json +8 -4
  41. package/assets/.agent/workflows/flutter-pro-max.md +0 -221
  42. package/assets/.agent/workflows/scripts/core.py +0 -345
  43. package/assets/.agent/workflows/scripts/search.py +0 -106
  44. package/assets/.claude/skills/flutter-pro-max/SKILL.md +0 -339
  45. package/assets/.claude/skills/flutter-pro-max/scripts/search.py +0 -106
  46. package/assets/.codebuddy/commands/flutter-pro-max.md +0 -221
  47. package/assets/.codebuddy/commands/scripts/core.py +0 -345
  48. package/assets/.codex/skills/flutter-pro-max/SKILL.md +0 -221
  49. package/assets/.codex/skills/flutter-pro-max/scripts/core.py +0 -345
  50. package/assets/.codex/skills/flutter-pro-max/scripts/search.py +0 -106
  51. package/assets/.cursor/commands/flutter-pro-max.md +0 -221
  52. package/assets/.cursor/commands/scripts/core.py +0 -345
  53. package/assets/.cursor/commands/scripts/search.py +0 -106
  54. package/assets/.gemini/skills/flutter-pro-max/SKILL.md +0 -221
  55. package/assets/.gemini/skills/flutter-pro-max/scripts/core.py +0 -345
  56. package/assets/.gemini/skills/flutter-pro-max/scripts/search.py +0 -106
  57. package/assets/.github/prompts/flutter-pro-max.prompt.md +0 -221
  58. package/assets/.github/prompts/scripts/core.py +0 -345
  59. package/assets/.github/prompts/scripts/search.py +0 -106
  60. package/assets/.kiro/steering/flutter-pro-max.md +0 -220
  61. package/assets/.kiro/steering/scripts/core.py +0 -345
  62. package/assets/.kiro/steering/scripts/search.py +0 -106
  63. package/assets/.qoder/rules/flutter-pro-max.md +0 -220
  64. package/assets/.qoder/rules/scripts/core.py +0 -345
  65. package/assets/.qoder/rules/scripts/search.py +0 -106
  66. package/assets/.roo/commands/flutter-pro-max.md +0 -220
  67. package/assets/.roo/commands/scripts/core.py +0 -345
  68. package/assets/.roo/commands/scripts/search.py +0 -106
  69. package/assets/.shared/flutter-pro-max/SKILL.md +0 -221
  70. package/assets/.shared/flutter-pro-max/scripts/core.py +0 -341
  71. package/assets/.shared/flutter-pro-max/scripts/search.py +0 -106
  72. package/assets/.trae/skills/flutter-pro-max/SKILL.md +0 -221
  73. package/assets/.trae/skills/flutter-pro-max/scripts/core.py +0 -345
  74. package/assets/.trae/skills/flutter-pro-max/scripts/search.py +0 -106
  75. package/assets/.windsurf/workflows/flutter-pro-max.md +0 -221
  76. package/assets/.windsurf/workflows/scripts/core.py +0 -345
  77. package/assets/.windsurf/workflows/scripts/search.py +0 -106
  78. package/dist/utils/extract.js +0 -83
  79. /package/assets/{.shared/data → data}/architect.csv +0 -0
  80. /package/assets/{.shared/data → data}/charts.csv +0 -0
  81. /package/assets/{.shared/data → data}/colors.csv +0 -0
  82. /package/assets/{.shared/data → data}/icons.csv +0 -0
  83. /package/assets/{.shared/data → data}/landing.csv +0 -0
  84. /package/assets/{.shared/data → data}/name_convention.csv +0 -0
  85. /package/assets/{.shared/data → data}/package.csv +0 -0
  86. /package/assets/{.shared/data → data}/patterns.csv +0 -0
  87. /package/assets/{.shared/data → data}/products.csv +0 -0
  88. /package/assets/{.shared/data → data}/prompts.csv +0 -0
  89. /package/assets/{.shared/data → data}/styles.csv +0 -0
  90. /package/assets/{.shared/data → data}/typography.csv +0 -0
  91. /package/assets/{.shared/data → data}/ux-guidelines.csv +0 -0
  92. /package/assets/{.shared/data → data}/widget.csv +0 -0
package/README.md CHANGED
@@ -47,31 +47,78 @@ For CI/CD or automated setups, you can specify the assistant type directly:
47
47
  # Install for a specific assistant
48
48
  flutter-pro-max init --ai claude
49
49
  flutter-pro-max init --ai cursor
50
+ flutter-pro-max init --ai antigravity
50
51
 
51
52
  # Install for all supported assistants
52
53
  flutter-pro-max init --ai all
53
54
  ```
54
55
 
56
+ ### Other Commands
57
+
58
+ ```bash
59
+ # List available versions from GitHub releases
60
+ flutter-pro-max versions
61
+
62
+ # Update to the latest version
63
+ flutter-pro-max update
64
+ flutter-pro-max update --ai claude
65
+ ```
66
+
55
67
  ---
56
68
 
57
69
  ## 🤖 Supported AI Assistants
58
70
 
59
71
  This CLI bridges the gap between the Flutter Pro Max knowledge base and your development tools:
60
72
 
61
- | Assistant | Type Flag | Installation Path |
62
- |-----------|-----------|-------------------|
63
- | **Claude Code** | `claude` | `.claude/skills/` |
64
- | **Cursor** | `cursor` | `.cursor/commands/` |
65
- | **Windsurf** | `windsurf` | `.windsurf/workflows/` |
66
- | **Antigravity** | `antigravity` | `.agent/workflows/` |
67
- | **Trae** | `trae` | `.trae/skills/` |
68
- | **Gemini CLI** | `gemini` | `.gemini/skills/` |
69
- | **GitHub Copilot** | `copilot` | `.github/prompts/` |
70
- | **RooCode** | `roocode` | `.roo/commands/` |
71
- | **Kiro** | `kiro` | `.kiro/steering/` |
72
- | **Qoder** | `qoder` | `.qoder/rules/` |
73
- | **CodeBuddy** | `codebuddy` | `.codebuddy/commands/` |
74
- | **Codex** | `codex` | `.codex/skills/` |
73
+ | Assistant | Type Flag | Install Type | Structure |
74
+ |-----------|-----------|--------------|-----------|
75
+ | **Claude Code** | `claude` | Full | `.claude/skills/flutter-pro-max/` |
76
+ | **Codex CLI** | `codex` | Full | `.codex/skills/flutter-pro-max/` |
77
+ | **Continue** | `continue` | Full | `.continue/skills/flutter-pro-max/` |
78
+ | **Antigravity** | `antigravity` | Full | `.agent/skills/flutter-pro-max/` |
79
+ | **Cursor** | `cursor` | Reference | `.cursor/commands/` + `.shared/` |
80
+ | **Windsurf** | `windsurf` | Reference | `.windsurf/workflows/` + `.shared/` |
81
+ | **GitHub Copilot** | `copilot` | Reference | `.github/prompts/` + `.shared/` |
82
+ | **Kiro** | `kiro` | Reference | `.kiro/skills/` + `.shared/` |
83
+ | **RooCode** | `roocode` | Reference | `.roo/commands/` + `.shared/` |
84
+ | **Qodo/Qoder** | `qoder` | Reference | `.qodo/skills/` + `.shared/` |
85
+ | **Gemini CLI** | `gemini` | Reference | `.gemini/skills/` + `.shared/` |
86
+ | **Trae** | `trae` | Reference | `.trae/skills/` + `.shared/` |
87
+ | **CodeBuddy** | `codebuddy` | Reference | `.codebuddy/skills/` + `.shared/` |
88
+ | **OpenCode** | `opencode` | Reference | `.opencode/skills/` + `.shared/` |
89
+
90
+ **Install Types:**
91
+ - **Full**: Data và scripts nằm trong skill folder (standalone, ~500KB)
92
+ - **Reference**: Skill file trỏ đến `.shared/` folder chung (tiết kiệm dung lượng khi dùng nhiều assistants)
93
+
94
+ ---
95
+
96
+ ## 📊 What Gets Installed
97
+
98
+ ### Data Files (17 domains)
99
+ | Domain | File | Description |
100
+ |--------|------|-------------|
101
+ | Widgets | `widget.csv` | 65+ Flutter widgets |
102
+ | Packages | `package.csv` | 100+ packages với alternatives |
103
+ | Patterns | `patterns.csv` | 110+ design patterns |
104
+ | Architecture | `architect.csv` | Architecture layers |
105
+ | Performance | `flutter-performance.csv` | 35 optimization patterns |
106
+ | Accessibility | `mobile-accessibility.csv` | 35 accessibility patterns |
107
+ | UI Reasoning | `ui-reasoning.csv` | 35 app category decisions |
108
+ | Colors | `colors.csv` | 50+ color palettes |
109
+ | Typography | `typography.csv` | 40+ font pairings |
110
+ | Styles | `styles.csv` | 60+ UI styles |
111
+ | UX | `ux-guidelines.csv` | 50+ UX rules |
112
+ | Icons | `icons.csv` | 100+ icon recommendations |
113
+ | Landing | `landing.csv` | 30+ landing patterns |
114
+ | Products | `products.csv` | 40+ product recommendations |
115
+ | Prompts | `prompts.csv` | 30+ AI prompts |
116
+ | Charts | `charts.csv` | 20+ chart types |
117
+ | Naming | `name_convention.csv` | Naming conventions |
118
+
119
+ ### Search Scripts
120
+ - `search.py` - BM25 search CLI
121
+ - `core.py` - Search engine core
75
122
 
76
123
  ---
77
124
 
@@ -88,8 +135,42 @@ npm install
88
135
  # Build the project
89
136
  npm run build
90
137
 
91
- # Run locally in development mode
92
- npm run dev
138
+ # Run locally
139
+ node dist/index.js init --ai claude
140
+
141
+ # Test search
142
+ node dist/index.js init --ai claude
143
+ cd .claude/skills/flutter-pro-max
144
+ python3 scripts/search.py "ListView" --domain widget --top 3
145
+ ```
146
+
147
+ ---
148
+
149
+ ## 📁 Project Structure
150
+
151
+ ```
152
+ cli/
153
+ ├── src/
154
+ │ ├── index.ts # CLI entry point
155
+ │ ├── commands/
156
+ │ │ ├── init.ts # Install command
157
+ │ │ ├── versions.ts # List versions
158
+ │ │ └── update.ts # Update command
159
+ │ ├── types/
160
+ │ │ └── index.ts # TypeScript types
161
+ │ └── utils/
162
+ │ ├── detect.ts # AI type detection
163
+ │ ├── github.ts # GitHub API client
164
+ │ ├── logger.ts # Console logger
165
+ │ └── template.ts # Template renderer
166
+ ├── assets/
167
+ │ ├── data/ # 17 CSV knowledge files
168
+ │ ├── scripts/ # Python search scripts
169
+ │ └── templates/
170
+ │ ├── base/ # Markdown templates
171
+ │ └── platforms/ # 14 platform JSON configs
172
+ ├── package.json
173
+ └── tsconfig.json
93
174
  ```
94
175
 
95
176
  ---
@@ -99,6 +180,18 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
99
180
 
100
181
  ---
101
182
 
183
+ ## 📝 Changelog
184
+
185
+ ### v2.1.0 (2026-01-27)
186
+ - **Type Safety**: Full Python type hints cho Pylance strict mode
187
+ - **Python 3.10+**: Minimum Python version updated
188
+ - **Code Quality**: Xóa unused imports, fix linter warnings
189
+
190
+ ### v2.0.0
191
+ - Phiên bản đầu tiên với 14 AI assistant support
192
+
193
+ ---
194
+
102
195
  <div align="center">
103
196
 
104
197
  **Streamline your Flutter development with AI-powered architectural intelligence.**
@@ -0,0 +1,36 @@
1
+ No,Category,Issue,Keywords,Description,Do,Don't,Code Example Good,Code Example Bad,Severity
2
+ 1,Build Optimization,Const Constructors,const widget rebuild optimization,Sử dụng const constructor để tránh rebuild widget không cần thiết,"Thêm const cho widget tĩnh","Tạo widget mới mỗi lần build","const SizedBox(height: 16)","SizedBox(height: 16)",High
3
+ 2,Build Optimization,Widget Extraction,extract widget method rebuild,Tách widget thành StatelessWidget riêng thay vì dùng method để tối ưu rebuild,"Tách thành widget class riêng","Dùng method trả về widget","class MyButton extends StatelessWidget { ... }","Widget _buildButton() { return Container(...); }",Critical
4
+ 3,Build Optimization,RepaintBoundary,repaint boundary canvas custom,Bọc widget có animation phức tạp trong RepaintBoundary để isolate vùng repaint,"Dùng RepaintBoundary cho animation phức tạp","Để toàn bộ subtree repaint cùng nhau","RepaintBoundary(child: AnimatedWidget())","AnimatedWidget() // không có boundary",High
5
+ 4,Build Optimization,Builder Pattern,builder lazy build,Sử dụng Builder widgets (ListView.builder) để lazy-load thay vì tạo tất cả widget cùng lúc,"Dùng .builder constructors","Truyền list children trực tiếp","ListView.builder(itemBuilder: (_, i) => Item(i))","ListView(children: items.map((i) => Item(i)).toList())",Critical
6
+ 5,State Management,Selective Listening,select watch notifier rebuild,Chỉ watch những phần state cần thiết thay vì toàn bộ object,"Dùng select() để lọc state","Watch toàn bộ object","ref.watch(userProvider.select((u) => u.name))","ref.watch(userProvider)",High
7
+ 6,State Management,Late Provider Init,lazy init provider notifier,Không khởi tạo provider/state nặng ngay từ đầu,"Dùng autoDispose và lazy init","Khởi tạo tất cả state khi app start","@riverpod class Heavy extends _$Heavy { late final _data; }","final heavy = StateProvider((_) => expensiveInit())",Medium
8
+ 7,State Management,Avoid Unnecessary Notify,notify state equal check,Kiểm tra giá trị mới khác giá trị cũ trước khi notify listeners,"So sánh trước khi update","Notify mỗi lần set value","if (state != newValue) state = newValue","state = newValue // luôn notify",Medium
9
+ 8,Image & Asset,Image Caching,cache image memory network,Sử dụng cached_network_image thay vì Image.network cho danh sách,"Dùng CachedNetworkImage","Dùng Image.network trong list","CachedNetworkImage(imageUrl: url)","Image.network(url)",Critical
10
+ 9,Image & Asset,Image Resolution,resolution precache asset,Precache và sử dụng đúng resolution cho asset images,"Dùng ResolutionAwareAssetBundle","Load ảnh resolution cao không cần thiết","precacheImage(AssetImage('img.png'), context)","Image.asset('high_res.png') // 4K cho thumbnail",Medium
11
+ 10,Image & Asset,Memory Image Dispose,dispose controller animation,Dispose ImageProvider và AnimationController đúng cách để tránh memory leak,"Dispose trong dispose()","Quên dispose controllers","@override void dispose() { _controller.dispose(); super.dispose(); }","// Không có dispose()",Critical
12
+ 11,List & Scroll,Virtualization,lazy load list viewport,Sử dụng ListView.builder để chỉ render items trong viewport,"Dùng builder cho list > 20 items","Render tất cả items cùng lúc","ListView.builder(itemCount: 1000, ...)","ListView(children: List.generate(1000, ...))",Critical
13
+ 12,List & Scroll,ShrinkWrap Avoid,shrinkwrap scroll nested,Tránh sử dụng shrinkWrap: true cho list dài vì tắt lazy loading,"Dùng Expanded hoặc Flexible","Dùng shrinkWrap: true","Expanded(child: ListView.builder(...))","ListView(shrinkWrap: true, children: longList)",Critical
14
+ 13,List & Scroll,Item Extent,itemextent cacheextent scroll,Cung cấp itemExtent khi items có cùng chiều cao để tối ưu scroll performance,"Set itemExtent cho uniform height","Để Flutter tính toán mỗi item","ListView.builder(itemExtent: 72, ...)","ListView.builder(...) // không có itemExtent",Medium
15
+ 14,List & Scroll,Keep Alive Careful,keepalive memory tab,Cân nhắc khi dùng AutomaticKeepAliveClientMixin vì giữ widget trong bộ nhớ,"Chỉ dùng cho tabs quan trọng","Keep alive tất cả tabs","// Chỉ keep alive tab có data quan trọng","// Keep alive tất cả tabs trong TabBarView",Medium
16
+ 15,Animation,Animation Controller Dispose,dispose animation leak,Luôn dispose AnimationController để tránh memory leak và ticker leak,"Dispose trong dispose() method","Quên dispose controller","@override void dispose() { _animController.dispose(); super.dispose(); }","// Không dispose AnimationController",Critical
17
+ 16,Animation,Implicit Over Explicit,implicit animated simple,Ưu tiên implicit animations (AnimatedContainer) cho hiệu ứng đơn giản,"Dùng AnimatedFoo widgets","Dùng AnimationController cho mọi thứ","AnimatedContainer(duration: Duration(milliseconds: 300), ...)","AnimationController + AnimatedBuilder cho color change",Medium
18
+ 17,Animation,TweenAnimationBuilder,tween animation builder stateless,Dùng TweenAnimationBuilder cho animation không cần control manual,"Dùng TweenAnimationBuilder","Tạo StatefulWidget chỉ cho animation đơn giản","TweenAnimationBuilder<double>(tween: Tween(begin: 0, end: 1), ...)","StatefulWidget + AnimationController cho fade in",Medium
19
+ 18,Animation,Animation Curve,curve ease animation timing,Sử dụng Curves phù hợp thay vì linear để animation tự nhiên hơn,"Dùng Curves.easeOutCubic","Dùng Curves.linear cho mọi thứ","curve: Curves.easeOutCubic","curve: Curves.linear",Low
20
+ 19,Async & Network,Isolate Heavy Work,isolate compute background,Chạy JSON parsing lớn và tính toán nặng trong Isolate/compute(),"Dùng compute() cho work > 16ms","Parse JSON lớn trên main thread","final data = await compute(parseJson, response)","final data = jsonDecode(response) // 100MB JSON",High
21
+ 20,Async & Network,Cancel Async Operations,cancel async dispose,Hủy Future/Stream subscriptions khi widget dispose,"Cancel trong dispose()","Để async operations chạy tiếp sau dispose","subscription?.cancel(); // trong dispose","// Không cancel, gây setState after dispose",High
22
+ 21,Async & Network,Debounce Search,debounce timer search input,Debounce search input để tránh gọi API quá nhiều,"Timer 300-500ms trước khi search","Gọi API mỗi keystroke","Timer(Duration(milliseconds: 500), () => search(query))","onChanged: (q) => search(q) // gọi mỗi keystroke",High
23
+ 22,Async & Network,Connection Check,connectivity network offline,Check kết nối mạng trước khi gọi API heavy,"Check connectivity trước","Luôn gọi API rồi xử lý lỗi sau","if (await Connectivity().checkConnectivity() != none) { ... }","try { await api.call() } catch (e) { // network error }",Medium
24
+ 23,Layout,Avoid Nested Layouts,nested row column layout,Tránh nested Row/Column quá sâu, sử dụng CustomMultiChildLayout nếu cần,"Flatten layout structure","Nested 5+ level Row/Column","Wrap, Table, CustomMultiChildLayout","Row(Column(Row(Column(Row(...))))))",Medium
25
+ 24,Layout,Intrinsic Dimension Avoid,intrinsic width height expensive,Tránh IntrinsicHeight/IntrinsicWidth vì gây double layout pass,"Dùng constraints hoặc fixed size","Dùng IntrinsicHeight cho list items","ConstrainedBox hoặc AspectRatio","IntrinsicHeight(child: Row(...))",High
26
+ 25,Layout,Clip Behavior,clip antiAlias expensive,Sử dụng Clip.hardEdge thay vì antiAlias nếu không cần smooth edges,"Clip.hardEdge hoặc Clip.none","Clip.antiAlias everywhere","clipBehavior: Clip.hardEdge","clipBehavior: Clip.antiAlias // mọi Container",Low
27
+ 26,Text & Font,Font Preload,font precache asset,Preload custom fonts khi app start để tránh flash of unstyled text,"Preload fonts trong main()","Để font load lazy","await FontLoader('MyFont').load()","// Font load khi widget đầu tiên dùng nó",Medium
28
+ 27,Text & Font,Text Scale Factor,textscale accessibility responsive,Hỗ trợ textScaleFactor cho accessibility thay vì hardcode font size,"Dùng Theme.of(context).textTheme","Hardcode fontSize everywhere","Text('Hi', style: Theme.of(context).textTheme.bodyLarge)","Text('Hi', style: TextStyle(fontSize: 14))",Medium
29
+ 28,Platform,Platform Channel Batch,platform channel native batch,Batch nhiều platform channel calls thành một để giảm overhead,"Batch calls thành single method","Gọi nhiều method channel riêng lẻ","channel.invokeMethod('batchOperation', [data1, data2])","channel.invokeMethod('op1'); channel.invokeMethod('op2');",Medium
30
+ 29,Platform,Profile vs Debug,profile mode release debug,Test performance trong Profile mode, không phải Debug mode,"Flutter run --profile","Đánh giá perf trong debug mode","flutter run --profile --target=lib/main.dart","flutter run // debug mode chậm hơn nhiều",Critical
31
+ 30,Platform,Skia vs Impeller,impeller ios android render,Sử dụng Impeller trên iOS (default) và test trên Android cho performance tốt hơn,"Enable Impeller nếu chưa","Disable Impeller không lý do","flutter run --enable-impeller","--no-enable-impeller // fallback Skia",Medium
32
+ 31,Startup,Deferred Loading,deferred loading split,Sử dụng deferred loading để giảm initial bundle size,"Deferred import cho features phụ","Load tất cả code lúc startup","import 'heavy.dart' deferred as heavy; await heavy.loadLibrary()","import 'heavy.dart' // load lúc app start",High
33
+ 32,Startup,Lazy Initialization,lazy init singleton service,Khởi tạo services lazy thay vì tất cả trong main(),"Lazy init với getter","Init tất cả trong main()","static final instance = Lazy(() => HeavyService())","void main() { HeavyService.init(); ... }",High
34
+ 33,DevTools,Timeline Profiling,devtools timeline frame,Sử dụng Flutter DevTools Timeline để phát hiện jank frames,"Profile với DevTools Timeline","Đoán nguyên nhân jank","flutter pub global run devtools","// Không dùng profiling tools",Medium
35
+ 34,DevTools,Widget Inspector,inspector rebuild debug,Dùng Widget Inspector để detect unnecessary rebuilds,"Check rebuild indicators","Đoán widget nào rebuild","DevTools > Flutter Inspector > Track widget rebuilds","// Không biết widget nào rebuild nhiều",Medium
36
+ 35,Memory,Dispose Pattern,dispose stream subscription,Implement dispose pattern đúng cách cho tất cả resources,"Dispose tất cả controllers, subscriptions","Quên dispose một số resources","streams.forEach((s) => s.cancel()); controllers.forEach((c) => c.dispose());","// Dispose một số, quên một số",Critical
@@ -0,0 +1,36 @@
1
+ No,Category,Issue,Keywords,Platform,Description,Do,Don't,Code Example Good,Code Example Bad,Severity
2
+ 1,Semantics,Screen Reader Labels,semantics label button accessibility,Flutter,Tất cả interactive widgets cần Semantics label cho screen reader,"Thêm Semantics hoặc semanticLabel","Button không có label","Semantics(label: 'Đóng', child: IconButton(...))","IconButton(icon: Icon(Icons.close), onPressed: ...)",Critical
3
+ 2,Semantics,Exclude Decorative,excludesemantics decorative icon,Flutter,Ẩn decorative icons khỏi screen reader,"Dùng excludeFromSemantics: true","Decorative icon được đọc lên","Icon(Icons.star, semanticLabel: null) // hoặc ExcludeSemantics","Icon(Icons.star) // đọc là 'star'",Medium
4
+ 3,Semantics,Image Descriptions,image semantic description alt,Flutter,Mô tả ảnh cho người dùng khiếm thị,"Thêm semanticLabel cho Image","Image không có description","Image.asset('logo.png', semanticLabel: 'Logo công ty ABC')","Image.asset('logo.png')",High
5
+ 4,Touch Target,Minimum Size,touch target 48 44 size,Flutter,Touch target tối thiểu 48x48 dp (Material) hoặc 44x44 pt (iOS),"Set minimum size 48x48","Nút quá nhỏ","SizedBox(width: 48, height: 48, child: IconButton(...))","IconButton(...) // 24x24 mặc định",Critical
6
+ 5,Touch Target,Hit Test Extension,gesture hitslop padding,Flutter,Mở rộng vùng tap không ảnh hưởng visual size,"Dùng padding hoặc MaterialTapTargetSize","Chỉ dựa vào icon size","GestureDetector(behavior: HitTestBehavior.opaque, child: Padding(...))","Icon(size: 16) // quá nhỏ để tap",High
7
+ 6,Touch Target,Spacing Between Targets,spacing touch targets,Flutter,Khoảng cách tối thiểu 8dp giữa các touch targets,"Thêm spacing >= 8dp","Các button sát nhau","Row(children: [btn1, SizedBox(width: 8), btn2])","Row(children: [btn1, btn2]) // sát nhau",Medium
8
+ 7,Color Contrast,Text Contrast Ratio,contrast ratio wcag text,Flutter,Tỉ lệ contrast tối thiểu 4.5:1 cho text thường và 3:1 cho large text,"Check contrast ratio trước khi chọn màu","Text nhạt trên nền sáng","Text(style: TextStyle(color: Color(0xFF1A1A1A))) // trên nền trắng","Text(style: TextStyle(color: Colors.grey[400])) // trên nền trắng",Critical
9
+ 8,Color Contrast,Non-Text Contrast,contrast icon button visual,Flutter,UI elements (icons buttons borders) cần contrast tối thiểu 3:1,"Đảm bảo icon/border đủ contrast","Icon quá nhạt","Icon(Icons.add, color: Color(0xFF333333))","Icon(Icons.add, color: Colors.grey[300])",High
10
+ 9,Color Contrast,Dark Mode Support,dark mode theme contrast,Flutter,Đảm bảo contrast đạt chuẩn cho cả light và dark mode,"Test cả 2 theme modes","Chỉ test light mode","Theme.of(context).colorScheme.onSurface","Color(0xFF333333) // hardcode không đổi theo theme",High
11
+ 10,Color,Not Only Color,color blind indicator shape,Flutter,Không dùng màu là chỉ báo duy nhất cho thông tin quan trọng,"Kết hợp icon/shape với color","Chỉ dùng màu để phân biệt","Row(children: [Icon(Icons.error), Text('Lỗi', style: TextStyle(color: Colors.red))])","Text('Lỗi', style: TextStyle(color: Colors.red))",High
12
+ 11,Text,Scalable Text,textscaler font size accessibility,Flutter,Hỗ trợ textScaler để người dùng có thể phóng to text,"Dùng Theme.textTheme","Hardcode fontSize","Text('Hi', style: Theme.of(context).textTheme.bodyLarge)","Text('Hi', style: TextStyle(fontSize: 14))",High
13
+ 12,Text,Text Truncation,overflow ellipsis truncate,Flutter,Xử lý text overflow đúng cách với ellipsis hoặc fade,"Set overflow và maxLines","Text tràn ra ngoài","Text('Long...', overflow: TextOverflow.ellipsis, maxLines: 2)","Text('Long text...') // tràn container",Medium
14
+ 13,Text,Dynamic Type,font accessibility large small,Flutter,Test với font size từ nhỏ nhất đến lớn nhất trong settings,"Test với tất cả font scales","Chỉ test với font mặc định","MediaQuery.textScalerOf(context).scale(14)","// Không test với accessibility settings",Medium
15
+ 14,Focus,Focus Management,focus traversal keyboard,Flutter,Quản lý focus order hợp lý cho keyboard navigation,"Set FocusTraversalOrder khi cần","Focus order không logic","FocusTraversalGroup(policy: OrderedTraversalPolicy(...))","// Để Flutter tự quyết định khi order phức tạp",Medium
16
+ 15,Focus,Focus Visibility,focus indicator ring border,Flutter,Hiển thị focus indicator rõ ràng khi navigate bằng keyboard,"Custom focusColor hoặc FocusNode","Focus không hiển thị","Focus(onFocusChange: (f) => setState(() => hasFocus = f))","// Không có visual feedback khi focus",High
17
+ 16,Focus,Restore Focus,autofocus restore modal,Flutter,Restore focus về element trước khi mở modal/dialog,"FocusScope.of(context).previousFocus()","Focus bị mất sau khi đóng modal","Navigator.pop(context); FocusScope.of(context).previousFocus()","Navigator.pop(context) // focus bị mất",Medium
18
+ 17,Navigation,Back Button Handle,popscope android back,Flutter,Xử lý nút Back Android đúng cách với PopScope,"Dùng PopScope","WillPopScope (deprecated)","PopScope(canPop: false, onPopInvokedWithResult: ...)","WillPopScope(onWillPop: ...) // deprecated",High
19
+ 18,Navigation,Screen Reader Announce,announce route semantics,Flutter,Thông báo cho screen reader khi chuyển màn hình,"SemanticsService.announce()","Chuyển màn hình im lặng","SemanticsService.announce('Đã mở trang Cài đặt', TextDirection.ltr)","Navigator.push(...) // không thông báo",Medium
20
+ 19,Navigation,Skip Navigation,skip link content main,Flutter,Cung cấp cách bỏ qua navigation để đến nội dung chính,"Focus node cho main content","Phải tab qua toàn bộ nav","ElevatedButton(onPressed: () => mainContentFocus.requestFocus(), child: Text('Bỏ qua'))","// Không có skip link",Medium
21
+ 20,Forms,Error Messages,form error inline accessible,Flutter,Hiển thị lỗi inline gần field và thông báo cho screen reader,"errorText trong InputDecoration","Lỗi ở xa field","TextFormField(decoration: InputDecoration(errorText: error))","// Hiển thị lỗi ở cuối form",High
22
+ 21,Forms,Form Labels,label hint form input,Flutter,Mỗi form field cần label rõ ràng (không chỉ hint),"labelText trong InputDecoration","Chỉ dùng hintText","TextFormField(decoration: InputDecoration(labelText: 'Email', hintText: 'example@mail.com'))","TextFormField(decoration: InputDecoration(hintText: 'Email'))",High
23
+ 22,Forms,Required Indicator,required asterisk field,Flutter,Đánh dấu rõ ràng các field bắt buộc,"Thêm * hoặc (required) vào label","Không đánh dấu required","InputDecoration(labelText: 'Email *')","InputDecoration(labelText: 'Email') // required nhưng không hiển thị",Medium
24
+ 23,Motion,Reduced Motion,reduce motion animation accessibility,Flutter,Tôn trọng cài đặt Reduce Motion của hệ thống,"Check MediaQuery.disableAnimations","Animation luôn chạy","if (!MediaQuery.disableAnimationsOf(context)) animate()","// Animation luôn chạy bất kể settings",High
25
+ 24,Motion,Auto-play Content,autoplay video audio control,Flutter,Không auto-play video/audio hoặc cho phép tắt,"Cung cấp play button","Auto-play media","VideoPlayer(autoPlay: false)","VideoPlayer(autoPlay: true) // làm phiền user",Medium
26
+ 25,Motion,Animation Duration,animation duration time,Flutter,Animation không nên quá nhanh (< 100ms) hoặc quá chậm (> 500ms),"Duration 150-300ms cho micro-interactions","Animation quá nhanh hoặc quá chậm","Duration(milliseconds: 200)","Duration(milliseconds: 50) // quá nhanh để nhận ra",Low
27
+ 26,Feedback,Loading States,loading indicator progress,Flutter,Hiển thị loading state rõ ràng cho async operations,"CircularProgressIndicator hoặc Skeleton","Không có loading feedback","if (isLoading) CircularProgressIndicator() else Content()","// Blank screen khi loading",High
28
+ 27,Feedback,Error States,error message retry action,Flutter,Hiển thị thông báo lỗi rõ ràng với action để retry,"Error message + Retry button","Im lặng khi lỗi","Column([Text('Có lỗi xảy ra'), ElevatedButton('Thử lại')])","// Blank screen hoặc chỉ có icon lỗi",High
29
+ 28,Feedback,Success Confirmation,success toast snackbar confirmation,Flutter,Xác nhận hành động thành công cho người dùng,"SnackBar hoặc Dialog","Không có feedback","ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('Đã lưu!')))","// Không thông báo gì sau khi save",Medium
30
+ 29,Feedback,Haptic Feedback,haptic vibration touch,Flutter,Sử dụng haptic feedback cho các action quan trọng,"HapticFeedback.lightImpact()","Không có haptic","HapticFeedback.mediumImpact(); // sau khi tap important button","// Không có tactile feedback",Low
31
+ 30,Platform,iOS Guidelines,cupertino ios human interface,Flutter,Tuân thủ Human Interface Guidelines trên iOS,"Dùng Cupertino widgets hoặc adaptive","Material widgets trên iOS","CupertinoPageRoute, CupertinoSwitch khi cần","MaterialPageRoute trên iOS với animation lạ",Medium
32
+ 31,Platform,Android Guidelines,material android guidelines,Flutter,Tuân thủ Material Design guidelines trên Android,"Dùng Material 3 widgets","UI không theo platform conventions","MaterialApp, NavigationBar, FloatingActionButton","Custom navigation không theo Material",Medium
33
+ 32,Platform,Gesture Conventions,gesture swipe back platform,Flutter,Tôn trọng gesture conventions của từng platform,"iOS: swipe right to back","Block platform gestures","Scaffold với Navigator tự động handle","PopScope block iOS swipe back gesture",Medium
34
+ 33,Orientation,Rotation Support,rotation orientation landscape,Flutter,Hỗ trợ rotation nếu phù hợp với app type,"Test cả portrait và landscape","Lock orientation không cần thiết","LayoutBuilder để responsive","SystemChrome.setPreferredOrientations([portrait]) // không cần",Low
35
+ 34,Orientation,Keyboard Overlap,keyboard resize insets,Flutter,Xử lý keyboard hiển thị không che form fields,"resizeToAvoidBottomInset hoặc scroll","Keyboard che input field","Scaffold(resizeToAvoidBottomInset: true)","Scaffold(resizeToAvoidBottomInset: false) // keyboard che field",High
36
+ 35,Testing,Accessibility Audit,accessibility audit testing,Flutter,Chạy accessibility audit trong development,"Dùng flutter accessibility tools","Không test accessibility","debugCheckIntrinsicSizes: true, semanticsDumpEnabled: true","// Không test với TalkBack/VoiceOver",High
@@ -0,0 +1,36 @@
1
+ No,App_Category,Recommended_Pattern,Style_Priority,Color_Mood,Typography_Mood,Key_Effects,Decision_Rules,Anti_Patterns,Severity
2
+ 1,E-commerce App,Hero + Grid + Cart,Material 3 + Vibrant,Brand primary + Success green + Trust,Clear + Engaging,Card elevation + Add to cart animation + Price highlight,"{""if_luxury"": ""use-premium-colors"", ""must_have"": ""quick-add-to-cart""}",Tiny tap targets + Hidden cart + Cluttered product grid,HIGH
3
+ 2,Social Media App,Feed + Stories + Profile,Vibrant + Motion-driven,Engagement colors + Notification red,Modern + Bold,Pull to refresh + Like animation + Story progress,"{""must_have"": ""infinite-scroll"", ""must_have"": ""engagement-feedback""}",No loading states + Static content + Poor image loading,HIGH
4
+ 3,Banking/Fintech App,Dashboard + Cards + Transactions,Material 3 + Trust,Navy + Trust Blue + Security green,Professional + Clear,Balance animation + Transaction list + Security indicators,"{""must_have"": ""biometric-auth"", ""must_have"": ""transaction-security""}",Insecure storage + No encryption + Hidden fees,CRITICAL
5
+ 4,Healthcare App,Appointments + Records + Chat,Accessible + Calm,Calm blue + Health green + White,Readable (16px+) + Clear,Appointment reminders + Health charts + Doctor availability,"{""must_have"": ""hipaa-compliance"", ""must_have"": ""accessibility""}",Small text + Complex navigation + No offline access,HIGH
6
+ 5,Fitness/Workout App,Progress + Workouts + Stats,Vibrant + Energetic,Energetic orange + Progress green,Bold + Motivational,Progress rings + Achievement unlocks + Workout timer,"{""must_have"": ""progress-tracking"", ""if_gamification"": ""add-achievements""}",No visual feedback + Static progress + Poor timer UX,HIGH
7
+ 6,Food Delivery App,Search + Menu + Tracking,Appetizing + Fast,Warm (Orange Red) + Success green,Clear + Appetizing,Real-time tracking + Order status + Cart animation,"{""must_have"": ""real-time-tracking"", ""must_have"": ""easy-reorder""}",No tracking + Hidden delivery time + Complex checkout,HIGH
8
+ 7,Travel/Booking App,Search + Listings + Booking,Inspiring + Trust,Sky blue + Destination colors + Trust,Inspirational + Clear,Search animation + Map integration + Price comparison,"{""must_have"": ""search-filters"", ""must_have"": ""booking-confirmation""}",Complex booking flow + No price transparency + Poor photos,HIGH
9
+ 8,Education/Learning App,Courses + Progress + Quiz,Friendly + Engaging,Learning blue + Progress green + Playful,Friendly + Readable,Progress bars + Quiz feedback + Certificate animation,"{""must_have"": ""progress-saving"", ""must_have"": ""offline-mode""}",No progress indication + Lost progress + Boring design,HIGH
10
+ 9,Productivity/Todo App,Lists + Calendar + Reminders,Minimal + Clean,Neutral + Priority colors (Red Yellow Green),Clean + Efficient,Task completion animation + Calendar sync + Quick actions,"{""must_have"": ""quick-add"", ""must_have"": ""reminders""}",Complex task creation + No sync + Hidden features,MEDIUM
11
+ 10,Messaging/Chat App,Conversations + Messages + Media,Clean + Personal,Brand + Read indicators + Online status,Modern + Readable,Typing indicator + Message status + Media preview,"{""must_have"": ""message-status"", ""must_have"": ""media-support""}",No read receipts + Poor media handling + Slow loading,HIGH
12
+ 11,Music/Audio App,Library + Player + Playlists,Immersive + Dark,Album art colors + Control accents,Modern + Bold,Waveform visualization + Now playing animation + Playlist shuffle,"{""must_have"": ""background-playback"", ""must_have"": ""audio-controls""}",Poor audio player UX + No background play + No offline,HIGH
13
+ 12,News/Media App,Feed + Articles + Categories,Clean + Readable,Neutral + Category accents,Editorial + Clear,Pull to refresh + Reading progress + Share animation,"{""must_have"": ""offline-reading"", ""must_have"": ""category-filtering""}",Cluttered layout + Slow loading + No offline cache,MEDIUM
14
+ 13,Dating App,Profiles + Matching + Chat,Personal + Warm,Warm (Pink Red) + Match success,Friendly + Personal,Swipe animation + Match celebration + Profile reveal,"{""must_have"": ""privacy-controls"", ""must_have"": ""report-system""}",No privacy + Poor profile display + Generic design,HIGH
15
+ 14,Real Estate App,Listings + Search + Favorites,Professional + Trust,Trust blue + Premium gold + Clean white,Professional + Clear,Property gallery + Map markers + Price comparison,"{""must_have"": ""map-view"", ""must_have"": ""save-favorites""}",Poor photos + Complex search + No saved properties,HIGH
16
+ 15,Ride-sharing App,Booking + Tracking + Payment,Fast + Trust,Brand + Live tracking + Safety indicators,Clear + Functional,Real-time map + Driver tracking + Fare estimation,"{""must_have"": ""real-time-tracking"", ""must_have"": ""safety-features""}",No live tracking + Hidden pricing + Poor driver info,CRITICAL
17
+ 16,Wallet/Payment App,Balance + Send + History,Secure + Trust,Trust blue + Money green + Security,Professional + Clear,Balance animation + Transaction confirmation + QR scanner,"{""must_have"": ""biometric-auth"", ""must_have"": ""transaction-receipts""}",Insecure + No confirmation + Hidden fees,CRITICAL
18
+ 17,Weather App,Current + Forecast + Alerts,Atmospheric + Dynamic,Sky gradients + Weather-based colors,Clear + Large,Weather animation + Location-based + Push alerts,"{""must_have"": ""location-accuracy"", ""must_have"": ""severe-alerts""}",Inaccurate data + No alerts + Static design,MEDIUM
19
+ 18,Gaming App,Game + Leaderboard + Store,Immersive + Engaging,Vibrant + Achievement gold,Bold + Impactful,Game animations + Achievement unlock + Score celebration,"{""must_have"": ""save-progress"", ""must_have"": ""leaderboards""}",Lost progress + Poor performance + No haptics,HIGH
20
+ 19,Event/Ticketing App,Events + Tickets + QR,Exciting + Trust,Event colors + Ticket accents,Engaging + Clear,Event discovery + QR ticket + Calendar add,"{""must_have"": ""qr-ticket"", ""must_have"": ""calendar-integration""}",No offline ticket + Complex purchase + Hidden fees,HIGH
21
+ 20,Grocery/Shopping App,Categories + Cart + Delivery,Fresh + Fast,Fresh green + Category colors + CTA,Clear + Appetizing,Product search + Quick add + Delivery slots,"{""must_have"": ""search-autocomplete"", ""must_have"": ""delivery-scheduling""}",Poor product images + Complex checkout + No reorder,HIGH
22
+ 21,Photography/Camera App,Camera + Gallery + Edit,Creative + Minimal,Neutral + Creative accents,Minimal + Clean,Camera controls + Filter preview + Edit tools,"{""must_have"": ""fast-capture"", ""must_have"": ""non-destructive-editing""}",Slow capture + Poor gallery + Complex editing,MEDIUM
23
+ 22,Podcast/Audio App,Shows + Episodes + Player,Content-focused + Dark,Dark + Audio waveform accents,Modern + Clear,Audio player + Episode progress + Download indicator,"{""must_have"": ""background-audio"", ""must_have"": ""download-offline""}",Poor player controls + No downloads + No playback speed,HIGH
24
+ 23,Job Search App,Search + Applications + Profile,Professional + Trust,Professional blue + Success green,Clear + Professional,Job match + Application tracker + Interview reminders,"{""must_have"": ""application-tracking"", ""must_have"": ""profile-completion""}",Complex application + No tracking + Hidden salary,HIGH
25
+ 24,Pet Care App,Pets + Health + Reminders,Friendly + Warm,Playful + Pet category colors,Friendly + Clear,Pet profiles + Health tracking + Vet reminders,"{""must_have"": ""vaccination-tracking"", ""must_have"": ""vet-locator""}",Generic design + No reminders + Poor pet profiles,MEDIUM
26
+ 25,Home Automation App,Devices + Scenes + Schedules,Smart + Dark,Dark + Device status colors + Accent,Clear + Functional,Device control + Scene activation + Real-time status,"{""must_have"": ""real-time-control"", ""must_have"": ""automation""}",Slow response + No offline control + Complex setup,HIGH
27
+ 26,Meditation/Wellness App,Sessions + Progress + Sounds,Calm + Minimal,Calm pastels + Nature colors,Calming + Readable,Breathing animation + Timer + Progress tracking,"{""must_have"": ""offline-audio"", ""must_have"": ""reminders""}",Harsh colors + Distracting UI + No offline,HIGH
28
+ 27,Parking App,Find + Pay + Navigate,Fast + Trust,Location blue + Payment green,Clear + Functional,Map markers + Payment flow + Timer notification,"{""must_have"": ""map-accuracy"", ""must_have"": ""payment-receipts""}",Inaccurate locations + Complex payment + No timer,HIGH
29
+ 28,Restaurant/Menu App,Menu + Order + Reservation,Appetizing + Brand,Brand + Food colors + CTA,Appetizing + Clear,Menu browsing + Table booking + Order tracking,"{""must_have"": ""menu-photos"", ""must_have"": ""reservation-system""}",Poor food photos + Complex ordering + No confirmation,HIGH
30
+ 29,Rental/Sharing App,Browse + Book + Return,Trust + Efficient,Trust blue + Status colors,Clear + Efficient,Availability calendar + Booking confirmation + Return reminder,"{""must_have"": ""availability-check"", ""must_have"": ""damage-reporting""}",No availability + Complex booking + Poor damage flow,HIGH
31
+ 30,Language Learning App,Lessons + Practice + Progress,Engaging + Playful,Learning colors + Progress indicators,Friendly + Clear,Lesson progress + Quiz feedback + Streak animation,"{""must_have"": ""progress-sync"", ""must_have"": ""gamification""}",Boring design + No motivation + Lost progress,HIGH
32
+ 31,Pharmacy/Medicine App,Prescriptions + Orders + Reminders,Trust + Healthcare,Medical blue + Trust + Alert colors,Clear + Accessible,Prescription scanner + Refill reminders + Drug info,"{""must_have"": ""reminder-system"", ""must_have"": ""drug-interactions""}",Complex refill + No reminders + Poor drug info,HIGH
33
+ 32,Insurance App,Policies + Claims + Support,Trust + Professional,Trust blue + Success green + Alert,Professional + Clear,Policy overview + Claim status + Document upload,"{""must_have"": ""claim-tracking"", ""must_have"": ""document-management""}",Hidden policy details + Complex claims + No tracking,HIGH
34
+ 33,Donation/Charity App,Causes + Donate + Impact,Heartfelt + Trust,Warm + Impact colors + Trust,Heartfelt + Clear,Impact visualization + Donation confirmation + Recurring setup,"{""must_have"": ""impact-transparency"", ""must_have"": ""easy-recurring""}",Hidden impact + Complex donation + No receipts,HIGH
35
+ 34,Survey/Form App,Questions + Progress + Submit,Clean + Efficient,Brand + Progress + Completion,Clear + Readable,Progress indicator + Skip logic + Submit confirmation,"{""must_have"": ""save-progress"", ""must_have"": ""clear-navigation""}",Lost progress + Complex questions + No feedback,MEDIUM
36
+ 35,Kids/Family App,Content + Controls + Profiles,Safe + Playful,Playful + Safe colors,Large + Friendly,Parental controls + Age-appropriate content + Usage stats,"{""must_have"": ""parental-controls"", ""must_have"": ""content-filtering""}",No controls + Inappropriate content + Small targets,CRITICAL
@@ -10,19 +10,22 @@ import re
10
10
  from pathlib import Path
11
11
  from math import log
12
12
  from collections import defaultdict
13
+ from typing import Any
13
14
 
14
15
  # ============ CONFIGURATION ============
15
- def _get_data_dir():
16
+ def _get_data_dir() -> Path:
16
17
  """Auto-detect data directory based on script location"""
17
18
  script_dir = Path(__file__).parent
18
19
  possible_paths = [
20
+ # Full install: .claude/skills/flutter-pro-max/scripts/ -> data/ (sibling)
21
+ script_dir.parent / "data",
22
+ # Reference install: .shared/flutter-pro-max/scripts/ -> ../data
23
+ script_dir.parent.parent / "data",
19
24
  # When running from root/scripts/
20
25
  script_dir.parent / ".shared" / "data",
21
- # When running from .shared/flutter-pro-max/scripts/
22
- script_dir.parent.parent / "data",
23
26
  # When running from .agent/workflows/scripts/ (nested 3 levels deep)
24
27
  script_dir.parent.parent.parent / ".shared" / "data",
25
- # When running from .claude/skills/flutter-pro-max/scripts/ (nested 4 levels deep)
28
+ # When running from .claude/skills/flutter-pro-max/scripts/ (reference mode)
26
29
  script_dir.parent.parent.parent.parent / ".shared" / "data",
27
30
  # Fallback: cwd
28
31
  Path.cwd() / ".shared" / "data",
@@ -36,7 +39,7 @@ DATA_DIR = _get_data_dir()
36
39
  MAX_RESULTS = 5
37
40
 
38
41
  # Domain configuration: file, search columns, output columns
39
- CSV_CONFIG = {
42
+ CSV_CONFIG: dict[str, dict[str, str | list[str]]] = {
40
43
  "widget": {
41
44
  "file": "widget.csv",
42
45
  "search_cols": ["Widget Name", "Category", "Description", "Key Properties", "Usage Context & Pro-Tips"],
@@ -106,6 +109,21 @@ CSV_CONFIG = {
106
109
  "file": "prompts.csv",
107
110
  "search_cols": ["Style Category", "AI Prompt Keywords (Copy-Paste Ready)", "CSS/Technical Keywords"],
108
111
  "output_cols": ["Style Category", "AI Prompt Keywords (Copy-Paste Ready)", "CSS/Technical Keywords", "Implementation Checklist"]
112
+ },
113
+ "performance": {
114
+ "file": "flutter-performance.csv",
115
+ "search_cols": ["Category", "Issue", "Keywords", "Description", "Do", "Don't"],
116
+ "output_cols": ["Category", "Issue", "Keywords", "Description", "Do", "Don't", "Code Example Good", "Code Example Bad", "Severity"]
117
+ },
118
+ "ui-reasoning": {
119
+ "file": "ui-reasoning.csv",
120
+ "search_cols": ["App_Category", "Recommended_Pattern", "Style_Priority", "Color_Mood", "Key_Effects", "Decision_Rules"],
121
+ "output_cols": ["App_Category", "Recommended_Pattern", "Style_Priority", "Color_Mood", "Typography_Mood", "Key_Effects", "Decision_Rules", "Anti_Patterns", "Severity"]
122
+ },
123
+ "accessibility": {
124
+ "file": "mobile-accessibility.csv",
125
+ "search_cols": ["Category", "Issue", "Keywords", "Platform", "Description", "Do", "Don't"],
126
+ "output_cols": ["Category", "Issue", "Keywords", "Platform", "Description", "Do", "Don't", "Code Example Good", "Code Example Bad", "Severity"]
109
127
  }
110
128
  }
111
129
 
@@ -124,49 +142,49 @@ AVAILABLE_STACKS = list(STACK_EXCLUSIONS.keys())
124
142
  class BM25:
125
143
  """BM25 ranking algorithm for text search - zero dependencies"""
126
144
 
127
- def __init__(self, k1=1.5, b=0.75):
145
+ def __init__(self, k1: float = 1.5, b: float = 0.75) -> None:
128
146
  self.k1 = k1
129
147
  self.b = b
130
- self.corpus = []
131
- self.doc_lengths = []
132
- self.avgdl = 0
133
- self.idf = {}
134
- self.doc_freqs = defaultdict(int)
135
- self.N = 0
136
-
137
- def tokenize(self, text):
148
+ self.corpus: list[list[str]] = []
149
+ self.doc_lengths: list[int] = []
150
+ self.avgdl: float = 0
151
+ self.idf: dict[str, float] = {}
152
+ self.doc_freqs: defaultdict[str, int] = defaultdict(int)
153
+ self.n: int = 0
154
+
155
+ def tokenize(self, text: str) -> list[str]:
138
156
  """Lowercase, split, remove punctuation, filter short words"""
139
157
  text = re.sub(r'[^\w\s]', ' ', str(text).lower())
140
158
  return [w for w in text.split() if len(w) > 1]
141
159
 
142
- def fit(self, documents):
160
+ def fit(self, documents: list[str]) -> None:
143
161
  """Build BM25 index from documents"""
144
162
  self.corpus = [self.tokenize(doc) for doc in documents]
145
- self.N = len(self.corpus)
146
- if self.N == 0:
163
+ self.n = len(self.corpus)
164
+ if self.n == 0:
147
165
  return
148
166
  self.doc_lengths = [len(doc) for doc in self.corpus]
149
- self.avgdl = sum(self.doc_lengths) / self.N
167
+ self.avgdl = sum(self.doc_lengths) / self.n
150
168
 
151
169
  for doc in self.corpus:
152
- seen = set()
170
+ seen: set[str] = set()
153
171
  for word in doc:
154
172
  if word not in seen:
155
173
  self.doc_freqs[word] += 1
156
174
  seen.add(word)
157
175
 
158
176
  for word, freq in self.doc_freqs.items():
159
- self.idf[word] = log((self.N - freq + 0.5) / (freq + 0.5) + 1)
177
+ self.idf[word] = log((self.n - freq + 0.5) / (freq + 0.5) + 1)
160
178
 
161
- def score(self, query):
179
+ def score(self, query: str) -> list[tuple[int, float]]:
162
180
  """Score all documents against query"""
163
181
  query_tokens = self.tokenize(query)
164
- scores = []
182
+ scores: list[tuple[int, float]] = []
165
183
 
166
184
  for idx, doc in enumerate(self.corpus):
167
- score = 0
185
+ score: float = 0.0
168
186
  doc_len = self.doc_lengths[idx]
169
- term_freqs = defaultdict(int)
187
+ term_freqs: defaultdict[str, int] = defaultdict(int)
170
188
  for word in doc:
171
189
  term_freqs[word] += 1
172
190
 
@@ -184,13 +202,13 @@ class BM25:
184
202
 
185
203
 
186
204
  # ============ HELPER FUNCTIONS ============
187
- def _load_csv(filepath):
205
+ def _load_csv(filepath: Path) -> list[dict[str, str]]:
188
206
  """Load CSV and return list of dicts"""
189
207
  with open(filepath, 'r', encoding='utf-8') as f:
190
208
  return list(csv.DictReader(f))
191
209
 
192
210
 
193
- def _search_csv(filepath, search_cols, output_cols, query, max_results, boost_col=None, boost_query=None):
211
+ def _search_csv(filepath: Path, search_cols: list[str], output_cols: list[str], query: str, max_results: int, boost_col: str | None = None, boost_query: str | None = None) -> list[dict[str, Any]]:
194
212
  """Core search function using BM25 with optional boosting"""
195
213
  if not filepath.exists():
196
214
  return []
@@ -208,28 +226,29 @@ def _search_csv(filepath, search_cols, output_cols, query, max_results, boost_co
208
226
  # Apply boosting if specified (widget name match, etc.)
209
227
  if boost_col and boost_query:
210
228
  boost_query_lower = boost_query.lower()
211
- boosted = []
229
+ boosted: list[tuple[int, float]] = []
212
230
  for idx, score in ranked:
231
+ boosted_score = score
213
232
  if score > 0:
214
233
  boost_value = str(data[idx].get(boost_col, "")).lower()
215
234
  if boost_value in boost_query_lower or boost_query_lower in boost_value:
216
- score *= 2.0 # Double score for exact/partial match
217
- boosted.append((idx, score))
235
+ boosted_score = score * 2.0 # Double score for exact/partial match
236
+ boosted.append((idx, boosted_score))
218
237
  ranked = sorted(boosted, key=lambda x: x[1], reverse=True)
219
238
 
220
239
  # Get top results with score > 0
221
- results = []
240
+ results: list[dict[str, Any]] = []
222
241
  for idx, score in ranked[:max_results]:
223
242
  if score > 0:
224
243
  row = data[idx]
225
- result = {col: row.get(col, "") for col in output_cols if col in row}
244
+ result: dict[str, Any] = {col: row.get(col, "") for col in output_cols if col in row}
226
245
  result["_score"] = round(score, 4)
227
246
  results.append(result)
228
247
 
229
248
  return results
230
249
 
231
250
 
232
- def detect_domain(query):
251
+ def detect_domain(query: str) -> str:
233
252
  """Auto-detect the most relevant domain from query keywords"""
234
253
  query_lower = query.lower()
235
254
 
@@ -250,13 +269,13 @@ def detect_domain(query):
250
269
  "prompt": ["prompt", "ai", "css", "tailwind", "implementation"],
251
270
  }
252
271
 
253
- scores = {domain: sum(1 for kw in keywords if kw in query_lower) for domain, keywords in domain_keywords.items()}
254
- best = max(scores, key=scores.get)
272
+ scores: dict[str, int] = {domain: sum(1 for kw in keywords if kw in query_lower) for domain, keywords in domain_keywords.items()}
273
+ best = max(scores, key=lambda k: scores[k])
255
274
  return best if scores[best] > 0 else "widget"
256
275
 
257
276
 
258
277
  # ============ MAIN SEARCH FUNCTIONS ============
259
- def search(query, domain=None, max_results=MAX_RESULTS):
278
+ def search(query: str, domain: str | None = None, max_results: int = MAX_RESULTS) -> dict[str, Any]:
260
279
  """
261
280
  Main search function with auto-domain detection
262
281
 
@@ -275,7 +294,7 @@ def search(query, domain=None, max_results=MAX_RESULTS):
275
294
  return {"error": f"Unknown domain: {domain}. Available: {', '.join(AVAILABLE_DOMAINS)}"}
276
295
 
277
296
  config = CSV_CONFIG[domain]
278
- filepath = DATA_DIR / config["file"]
297
+ filepath = DATA_DIR / str(config["file"])
279
298
 
280
299
  if not filepath.exists():
281
300
  return {"error": f"File not found: {filepath}", "domain": domain}
@@ -284,15 +303,20 @@ def search(query, domain=None, max_results=MAX_RESULTS):
284
303
  boost_col = "Widget Name" if domain == "widget" else None
285
304
  boost_query = query if domain == "widget" else None
286
305
 
287
- results = _search_csv(
288
- filepath,
289
- config["search_cols"],
290
- config["output_cols"],
291
- query,
292
- max_results,
293
- boost_col=boost_col,
294
- boost_query=boost_query
295
- )
306
+ search_cols = config["search_cols"]
307
+ output_cols = config["output_cols"]
308
+ if isinstance(search_cols, list) and isinstance(output_cols, list):
309
+ results = _search_csv(
310
+ filepath,
311
+ search_cols,
312
+ output_cols,
313
+ query,
314
+ max_results,
315
+ boost_col=boost_col,
316
+ boost_query=boost_query
317
+ )
318
+ else:
319
+ results = []
296
320
 
297
321
  return {
298
322
  "domain": domain,
@@ -303,7 +327,7 @@ def search(query, domain=None, max_results=MAX_RESULTS):
303
327
  }
304
328
 
305
329
 
306
- def search_with_stack(query, stack, domain=None, max_results=MAX_RESULTS):
330
+ def search_with_stack(query: str, stack: str, domain: str | None = None, max_results: int = MAX_RESULTS) -> dict[str, Any]:
307
331
  """
308
332
  Search with stack-specific filtering (excludes conflicting packages)
309
333
 
@@ -326,11 +350,11 @@ def search_with_stack(query, stack, domain=None, max_results=MAX_RESULTS):
326
350
 
327
351
  # Filter out conflicting packages
328
352
  excluded = STACK_EXCLUSIONS[stack]
329
- filtered_results = []
353
+ filtered_results: list[dict[str, Any]] = []
330
354
 
331
355
  for item in result["results"]:
332
356
  # Check package name field
333
- pkg_name = item.get("pkg_name", "").lower()
357
+ pkg_name = str(item.get("pkg_name", "")).lower()
334
358
  if pkg_name not in excluded:
335
359
  filtered_results.append(item)
336
360