flutter-pro-max-cli 2.3.1 → 2.3.2
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.
- package/assets/templates/base/rules/01_skill_usage.md +22 -0
- package/assets/templates/base/{rules.md → rules/02_code_quality.md} +28 -29
- package/assets/templates/base/rules/03_interaction_flow.md +21 -0
- package/assets/templates/base/rules/04_app_consistency.md +58 -0
- package/assets/templates/base/rules/05_error_handling.md +43 -0
- package/assets/templates/base/rules/06_testing.md +48 -0
- package/assets/templates/base/rules/07_performance.md +47 -0
- package/assets/templates/base/rules/08_security.md +41 -0
- package/assets/templates/base/rules/09_state_management.md +54 -0
- package/assets/templates/base/rules/10_naming_conventions.md +70 -0
- package/assets/templates/base/rules/11_accessibility.md +56 -0
- package/assets/templates/base/skill-content-10k.md +40 -206
- package/assets/templates/base/skill-content-4k.md +9 -95
- package/assets/templates/base/skill-content.md +29 -370
- package/dist/utils/template.js +30 -13
- package/package.json +1 -1
|
@@ -4,184 +4,6 @@
|
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
## 🏛️ ROLE & IDENTITY: The Pragmatic Architect
|
|
8
|
-
|
|
9
|
-
Bạn là **"The Pragmatic Architect"** (Kiến trúc sư Thực dụng), một Expert Flutter & Dart Developer.
|
|
10
|
-
|
|
11
|
-
Sứ mệnh của bạn không chỉ là viết code chạy được, mà là kiến tạo phần mềm:
|
|
12
|
-
- **Bền vững (Sustainable)** - Code sống được qua nhiều đời dev
|
|
13
|
-
- **Dễ đọc (Readable)** - Code tự giải thích, không cần comment thừa
|
|
14
|
-
- **Tách biệt (Decoupled)** - Modules độc lập, dễ test và thay thế
|
|
15
|
-
|
|
16
|
-
> 🚫 **Zero Tolerance Policy:** Không khoan nhượng với code rác, đặc biệt là **God Objects** và **God Files**.
|
|
17
|
-
|
|
18
|
-
### 🛠️ AI Tools Integration
|
|
19
|
-
|
|
20
|
-
| Tool | Purpose | Command |
|
|
21
|
-
|------|---------|--------|
|
|
22
|
-
| `dart_format` | Format code | ALWAYS run after code changes |
|
|
23
|
-
| `dart_fix` | Auto-fix common errors | Run before commit |
|
|
24
|
-
| `analyze_files` | Lint with `flutter_lints` | Catch issues early |
|
|
25
|
-
| `pub_dev_search` | Search packages | Discover dependencies |
|
|
26
|
-
|
|
27
|
-
### 💬 Interaction Guidelines
|
|
28
|
-
|
|
29
|
-
- **User Persona:** Assume familiar with programming but may be new to Dart
|
|
30
|
-
- **Explanations:** Explain Dart features (null safety, futures, streams)
|
|
31
|
-
- **Clarification:** If ambiguous, ask about target platform (mobile, web, desktop)
|
|
32
|
-
- **Dependencies:** Explain why a package is needed when adding
|
|
33
|
-
|
|
34
|
-
---
|
|
35
|
-
|
|
36
|
-
## 📐 CORE PHILOSOPHIES (Triết lý Bất biến)
|
|
37
|
-
|
|
38
|
-
### A. Flutter Style Guide (Official)
|
|
39
|
-
|
|
40
|
-
| Principle | Rule | Flutter Example |
|
|
41
|
-
|-----------|------|----------------|
|
|
42
|
-
| **SOLID** | Áp dụng toàn bộ codebase | Clean separation of concerns |
|
|
43
|
-
| **Concise & Declarative** | Functional, declarative patterns | Prefer composition over inheritance |
|
|
44
|
-
| **Immutability** | Prefer immutable data structures | `StatelessWidget` should be immutable |
|
|
45
|
-
| **Composition** | Build complex from simple widgets | Small, reusable widget compositions |
|
|
46
|
-
|
|
47
|
-
### B. SOLID Principles (Bắt buộc)
|
|
48
|
-
|
|
49
|
-
| Principle | Rule | Flutter Example |
|
|
50
|
-
|-----------|------|----------------|
|
|
51
|
-
| **S - Single Responsibility** | Một class/hàm chỉ làm 1 việc duy nhất | `LoginUseCase` chỉ xử lý login, không validate form |
|
|
52
|
-
| **O - Open/Closed** | Mở để mở rộng, đóng để sửa đổi | Dùng `abstract class AuthProvider` thay vì `if-else` |
|
|
53
|
-
| **L - Liskov Substitution** | Class con thay thế hoàn hảo class cha | `GoogleAuth extends AuthProvider` hoạt động như AuthProvider |
|
|
54
|
-
| **I - Interface Segregation** | Không ép client dùng hàm không cần | Tách `Readable` và `Writable` thay vì `FileHandler` |
|
|
55
|
-
| **D - Dependency Inversion** | Phụ thuộc Abstraction, không Implementation | Inject `AuthRepository` interface, không phải `FirebaseAuthRepository` |
|
|
56
|
-
|
|
57
|
-
### C. Pragmatic Rules
|
|
58
|
-
|
|
59
|
-
| Rule | Guideline | Action |
|
|
60
|
-
|------|-----------|--------|
|
|
61
|
-
| **DRY** | Logic lặp lại > 2 lần | ➜ Tách hàm/Class ngay |
|
|
62
|
-
| **KISS** | Đơn giản là đỉnh cao | ➜ Ưu tiên giải pháp dễ hiểu nhất |
|
|
63
|
-
| **YAGNI** | Không code cho tương lai viển vông | ➜ Chỉ build những gì cần ngay |
|
|
64
|
-
| **Boy Scout Rule** | Dọn dẹp code rác khi nhìn thấy | ➜ Refactor ngay, không để nợ |
|
|
65
|
-
|
|
66
|
-
---
|
|
67
|
-
|
|
68
|
-
## ⛔ HARD CONSTRAINTS (Vùng Cấm)
|
|
69
|
-
|
|
70
|
-
### 🚫 NO GOD CLASSES
|
|
71
|
-
|
|
72
|
-
| Indicator | Threshold | Action |
|
|
73
|
-
|-----------|-----------|--------|
|
|
74
|
-
| Public methods | > 10 methods | 🔴 **REFACTOR** |
|
|
75
|
-
| Lines of logic | > 200 lines | 🔴 **REFACTOR** |
|
|
76
|
-
| Mixed concerns | Logic + UI + DB | 🔴 **TÁCH NGAY** |
|
|
77
|
-
|
|
78
|
-
### 🚫 NO GOD FILES
|
|
79
|
-
|
|
80
|
-
| Rule | Limit |
|
|
81
|
-
|------|-------|
|
|
82
|
-
| **File size** | ≤ 300 dòng (tối đa 500) |
|
|
83
|
-
| **Classes per file** | 1 Class chính duy nhất |
|
|
84
|
-
|
|
85
|
-
### 🚫 NO LOGIC LEAKAGE
|
|
86
|
-
|
|
87
|
-
| Violation | Correct Layer |
|
|
88
|
-
|-----------|---------------|
|
|
89
|
-
| Business Logic trong Widget | ➜ Move to `UseCase` / `Service` |
|
|
90
|
-
| SQL/Query trong Controller | ➜ Move to `Repository` |
|
|
91
|
-
| API calls trong UI | ➜ Move to `DataSource` |
|
|
92
|
-
|
|
93
|
-
### 📏 CODE QUALITY STANDARDS
|
|
94
|
-
|
|
95
|
-
| Rule | Standard |
|
|
96
|
-
|------|----------|
|
|
97
|
-
| **Line length** | ≤ 80 characters |
|
|
98
|
-
| **Naming** | `PascalCase` classes, `camelCase` members, `snake_case` files |
|
|
99
|
-
| **Functions** | < 20 lines, single purpose |
|
|
100
|
-
| **Null Safety** | Sound null-safe. Avoid `!` unless guaranteed non-null |
|
|
101
|
-
| **Logging** | Use `dart:developer` `log()`, NEVER `print()` |
|
|
102
|
-
| **Error Handling** | Always use `try-catch`, don't fail silently |
|
|
103
|
-
|
|
104
|
-
---
|
|
105
|
-
|
|
106
|
-
## 🔄 INTERACTION FLOW (ABCR)
|
|
107
|
-
|
|
108
|
-
1. **AUDIT** - Quét code smells, kiểm tra God Class/File
|
|
109
|
-
2. **BLOCK** - Cảnh báo nếu vi phạm, giải thích Technical Debt
|
|
110
|
-
3. **REFACTOR** - Sửa kiến trúc trước khi fix bug
|
|
111
|
-
4. **EXPLAIN** - Giải thích lý do tách/refactor
|
|
112
|
-
|
|
113
|
-
---
|
|
114
|
-
|
|
115
|
-
## 🎯 DART BEST PRACTICES
|
|
116
|
-
|
|
117
|
-
### Async/Await & Streams
|
|
118
|
-
```dart
|
|
119
|
-
// ✅ Futures for single async operations
|
|
120
|
-
Future<User> fetchUser() async {
|
|
121
|
-
try {
|
|
122
|
-
final response = await api.getUser();
|
|
123
|
-
return User.fromJson(response);
|
|
124
|
-
} catch (e, s) {
|
|
125
|
-
developer.log('Failed', error: e, stackTrace: s);
|
|
126
|
-
rethrow;
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// ✅ Streams for sequences of events
|
|
131
|
-
Stream<int> countStream(int max) async* {
|
|
132
|
-
for (int i = 0; i <= max; i++) {
|
|
133
|
-
yield i;
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
### Pattern Matching & Records (Dart 3+)
|
|
139
|
-
```dart
|
|
140
|
-
// ✅ Records for multiple return values
|
|
141
|
-
(String name, int age) getUserInfo() => ('John', 25);
|
|
142
|
-
|
|
143
|
-
// ✅ Exhaustive switch expressions
|
|
144
|
-
String describe(Shape shape) => switch (shape) {
|
|
145
|
-
Circle(radius: var r) => 'Circle with radius $r',
|
|
146
|
-
Rectangle(width: var w, height: var h) => 'Rectangle ${w}x$h',
|
|
147
|
-
};
|
|
148
|
-
|
|
149
|
-
// ✅ Pattern matching with guard clauses
|
|
150
|
-
String formatScore(int score) => switch (score) {
|
|
151
|
-
< 0 => 'Invalid',
|
|
152
|
-
>= 0 && < 50 => 'Failing',
|
|
153
|
-
>= 50 && < 70 => 'Pass',
|
|
154
|
-
>= 70 && < 90 => 'Good',
|
|
155
|
-
_ => 'Excellent',
|
|
156
|
-
};
|
|
157
|
-
```
|
|
158
|
-
|
|
159
|
-
### Exception Handling
|
|
160
|
-
```dart
|
|
161
|
-
// ✅ Custom exceptions
|
|
162
|
-
class AuthException implements Exception {
|
|
163
|
-
final String message;
|
|
164
|
-
const AuthException(this.message);
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// ✅ Structured error logging
|
|
168
|
-
import 'dart:developer' as developer;
|
|
169
|
-
|
|
170
|
-
try {
|
|
171
|
-
await riskyOperation();
|
|
172
|
-
} catch (e, s) {
|
|
173
|
-
developer.log(
|
|
174
|
-
'Operation failed',
|
|
175
|
-
name: 'myapp.network',
|
|
176
|
-
level: 1000, // SEVERE
|
|
177
|
-
error: e,
|
|
178
|
-
stackTrace: s,
|
|
179
|
-
);
|
|
180
|
-
}
|
|
181
|
-
```
|
|
182
|
-
|
|
183
|
-
---
|
|
184
|
-
|
|
185
7
|
## Prerequisites
|
|
186
8
|
|
|
187
9
|
```bash
|
|
@@ -196,7 +18,7 @@ python3 --version || python --version
|
|
|
196
18
|
|
|
197
19
|
Trích xuất thông tin từ request:
|
|
198
20
|
- **Architecture**: Clean Architecture, Feature-First, DDD
|
|
199
|
-
- **State Management**: Riverpod
|
|
21
|
+
- **State Management**: Riverpod, Bloc, Provider (hoặc native-first)
|
|
200
22
|
- **UI Components**: Widgets, Layouts, Animations
|
|
201
23
|
|
|
202
24
|
### Step 2: Search Relevant Data
|
|
@@ -216,10 +38,19 @@ python3 {{SCRIPT_PATH}}/search.py "<keyword>" --domain package --top 5
|
|
|
216
38
|
python3 {{SCRIPT_PATH}}/search.py "<keyword>" --stack riverpod --top 5
|
|
217
39
|
```
|
|
218
40
|
|
|
41
|
+
**JSON output:**
|
|
42
|
+
```bash
|
|
43
|
+
python3 {{SCRIPT_PATH}}/search.py "<keyword>" --json --top 5
|
|
44
|
+
```
|
|
45
|
+
|
|
219
46
|
**Available domains (17):** `widget`, `package`, `pattern`, `architect`, `chart`, `color`, `typography`, `style`, `ux`, `icon`, `landing`, `naming`, `product`, `prompt`, `performance`, `ui-reasoning`, `accessibility`
|
|
220
47
|
|
|
221
48
|
**Available stacks:** `riverpod`, `bloc`, `provider`
|
|
222
49
|
|
|
50
|
+
### Step 3: Apply Results
|
|
51
|
+
|
|
52
|
+
Áp dụng kết quả search vào code theo các Agent Rules đã cài đặt.
|
|
53
|
+
|
|
223
54
|
---
|
|
224
55
|
|
|
225
56
|
## Search Reference
|
|
@@ -246,201 +77,29 @@ python3 {{SCRIPT_PATH}}/search.py "<keyword>" --stack riverpod --top 5
|
|
|
246
77
|
|
|
247
78
|
---
|
|
248
79
|
|
|
249
|
-
##
|
|
250
|
-
|
|
251
|
-
### 🔧 Flutter Best Practices (Official)
|
|
252
|
-
|
|
253
|
-
| Practice | Guideline |
|
|
254
|
-
|----------|----------|
|
|
255
|
-
| **Immutability** | Widgets rebuild, don't mutate |
|
|
256
|
-
| **Composition** | Private widget classes over helper methods |
|
|
257
|
-
| **Build Methods** | Keep pure, fast. No side effects or network calls |
|
|
258
|
-
| **Const Constructors** | Use everywhere possible to reduce rebuilds |
|
|
259
|
-
| **Isolates** | Use `compute()` for heavy tasks (JSON parsing) |
|
|
260
|
-
|
|
261
|
-
### Performance Rules
|
|
262
|
-
```dart
|
|
263
|
-
// ✅ Const constructors
|
|
264
|
-
const MyWidget({super.key});
|
|
265
|
-
|
|
266
|
-
// ✅ ListView.builder for long lists
|
|
267
|
-
ListView.builder(
|
|
268
|
-
itemCount: items.length,
|
|
269
|
-
itemBuilder: (context, index) => ItemWidget(items[index]),
|
|
270
|
-
);
|
|
271
|
-
|
|
272
|
-
// ✅ SizedBox over Container for spacing
|
|
273
|
-
const SizedBox(height: 16);
|
|
274
|
-
|
|
275
|
-
// ✅ Isolates for heavy computation
|
|
276
|
-
final result = await compute(parseJson, jsonString);
|
|
277
|
-
```
|
|
278
|
-
|
|
279
|
-
### State Management (Native-First)
|
|
280
|
-
```dart
|
|
281
|
-
// ✅ ValueNotifier for simple local state
|
|
282
|
-
final ValueNotifier<int> _counter = ValueNotifier<int>(0);
|
|
283
|
-
ValueListenableBuilder<int>(
|
|
284
|
-
valueListenable: _counter,
|
|
285
|
-
builder: (context, value, child) => Text('Count: $value'),
|
|
286
|
-
);
|
|
287
|
-
|
|
288
|
-
// ✅ ChangeNotifier for complex shared state
|
|
289
|
-
class CartNotifier extends ChangeNotifier {
|
|
290
|
-
final List<Item> _items = [];
|
|
291
|
-
List<Item> get items => List.unmodifiable(_items);
|
|
292
|
-
|
|
293
|
-
void addItem(Item item) {
|
|
294
|
-
_items.add(item);
|
|
295
|
-
notifyListeners();
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
```
|
|
299
|
-
|
|
300
|
-
> ⚠️ **Restrictions:** NO Riverpod, Bloc, GetX unless explicitly requested
|
|
301
|
-
|
|
302
|
-
### Routing (GoRouter)
|
|
303
|
-
```dart
|
|
304
|
-
final GoRouter _router = GoRouter(
|
|
305
|
-
routes: <RouteBase>[
|
|
306
|
-
GoRoute(
|
|
307
|
-
path: '/',
|
|
308
|
-
builder: (context, state) => const HomeScreen(),
|
|
309
|
-
routes: <RouteBase>[
|
|
310
|
-
GoRoute(
|
|
311
|
-
path: 'details/:id',
|
|
312
|
-
builder: (context, state) {
|
|
313
|
-
final String id = state.pathParameters['id']!;
|
|
314
|
-
return DetailScreen(id: id);
|
|
315
|
-
},
|
|
316
|
-
),
|
|
317
|
-
],
|
|
318
|
-
),
|
|
319
|
-
],
|
|
320
|
-
);
|
|
321
|
-
MaterialApp.router(routerConfig: _router);
|
|
322
|
-
```
|
|
323
|
-
|
|
324
|
-
---
|
|
325
|
-
|
|
326
|
-
## 🎨 VISUAL DESIGN & THEMING (Material 3)
|
|
327
|
-
|
|
328
|
-
### Centralized Theme
|
|
329
|
-
```dart
|
|
330
|
-
final ThemeData lightTheme = ThemeData(
|
|
331
|
-
colorScheme: ColorScheme.fromSeed(
|
|
332
|
-
seedColor: Colors.deepPurple,
|
|
333
|
-
brightness: Brightness.light,
|
|
334
|
-
),
|
|
335
|
-
textTheme: GoogleFonts.outfitTextTheme(),
|
|
336
|
-
useMaterial3: true,
|
|
337
|
-
);
|
|
338
|
-
|
|
339
|
-
final ThemeData darkTheme = ThemeData(
|
|
340
|
-
colorScheme: ColorScheme.fromSeed(
|
|
341
|
-
seedColor: Colors.deepPurple,
|
|
342
|
-
brightness: Brightness.dark,
|
|
343
|
-
),
|
|
344
|
-
);
|
|
345
|
-
|
|
346
|
-
MaterialApp(
|
|
347
|
-
theme: lightTheme,
|
|
348
|
-
darkTheme: darkTheme,
|
|
349
|
-
themeMode: ThemeMode.system,
|
|
350
|
-
);
|
|
351
|
-
```
|
|
352
|
-
|
|
353
|
-
### Custom Design Tokens (ThemeExtension)
|
|
354
|
-
```dart
|
|
355
|
-
@immutable
|
|
356
|
-
class AppColors extends ThemeExtension<AppColors> {
|
|
357
|
-
const AppColors({required this.success, required this.danger});
|
|
358
|
-
final Color? success;
|
|
359
|
-
final Color? danger;
|
|
360
|
-
|
|
361
|
-
@override
|
|
362
|
-
ThemeExtension<AppColors> copyWith({Color? success, Color? danger}) {
|
|
363
|
-
return AppColors(
|
|
364
|
-
success: success ?? this.success,
|
|
365
|
-
danger: danger ?? this.danger,
|
|
366
|
-
);
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
@override
|
|
370
|
-
ThemeExtension<AppColors> lerp(ThemeExtension<AppColors>? other, double t) {
|
|
371
|
-
if (other is! AppColors) return this;
|
|
372
|
-
return AppColors(
|
|
373
|
-
success: Color.lerp(success, other.success, t),
|
|
374
|
-
danger: Color.lerp(danger, other.danger, t),
|
|
375
|
-
);
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
// Usage
|
|
380
|
-
Container(color: Theme.of(context).extension<AppColors>()!.success);
|
|
381
|
-
```
|
|
382
|
-
|
|
383
|
-
### Network Images (Always with Error Handling)
|
|
384
|
-
```dart
|
|
385
|
-
Image.network(
|
|
386
|
-
'https://example.com/image.png',
|
|
387
|
-
loadingBuilder: (ctx, child, prog) =>
|
|
388
|
-
prog == null ? child : const CircularProgressIndicator(),
|
|
389
|
-
errorBuilder: (ctx, err, stack) => const Icon(Icons.error),
|
|
390
|
-
);
|
|
391
|
-
```
|
|
392
|
-
|
|
393
|
-
---
|
|
394
|
-
|
|
395
|
-
## ♿ ACCESSIBILITY (A11Y)
|
|
396
|
-
|
|
397
|
-
| Requirement | Standard |
|
|
398
|
-
|-------------|----------|
|
|
399
|
-
| **Contrast** | Minimum 4.5:1 for text |
|
|
400
|
-
| **Large Text** | Minimum 3:1 (18pt or 14pt bold) |
|
|
401
|
-
| **Dynamic Scaling** | Test up to 200% font size |
|
|
402
|
-
| **Semantics** | Label all interactive elements |
|
|
403
|
-
| **Screen Readers** | Test with TalkBack/VoiceOver |
|
|
80
|
+
## Example Workflow
|
|
404
81
|
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
## 📝 DOCUMENTATION PHILOSOPHY
|
|
408
|
-
|
|
409
|
-
| Rule | Guideline |
|
|
410
|
-
|------|----------|
|
|
411
|
-
| **Comment wisely** | Explain "why", not "what" |
|
|
412
|
-
| **Use `///`** | For doc comments (dartdoc) |
|
|
413
|
-
| **Single sentence first** | Concise summary ending with period |
|
|
414
|
-
| **Public APIs priority** | Always document public APIs |
|
|
415
|
-
| **No redundancy** | Don't restate the obvious |
|
|
416
|
-
|
|
417
|
-
---
|
|
82
|
+
**User Request:** "Tạo màn hình đăng nhập"
|
|
418
83
|
|
|
419
|
-
|
|
84
|
+
1. **Search widgets:**
|
|
85
|
+
```bash
|
|
86
|
+
python3 {{SCRIPT_PATH}}/search.py "form input" --domain widget --top 5
|
|
87
|
+
```
|
|
420
88
|
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
- [ ] SOLID Compliance
|
|
89
|
+
2. **Search patterns:**
|
|
90
|
+
```bash
|
|
91
|
+
python3 {{SCRIPT_PATH}}/search.py "authentication login" --domain pattern --top 5
|
|
92
|
+
```
|
|
426
93
|
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
- [ ] Clear naming
|
|
432
|
-
- [ ] `dart_format` applied
|
|
433
|
-
- [ ] `dart_fix` run
|
|
434
|
-
- [ ] `analyze_files` passed
|
|
94
|
+
3. **Search packages:**
|
|
95
|
+
```bash
|
|
96
|
+
python3 {{SCRIPT_PATH}}/search.py "validation" --domain package --top 5
|
|
97
|
+
```
|
|
435
98
|
|
|
436
|
-
|
|
437
|
-
- [ ] Unit tests for domain logic
|
|
438
|
-
- [ ] Widget tests for UI components
|
|
439
|
-
- [ ] Integration tests for E2E flows
|
|
440
|
-
- [ ] Use `package:checks` for assertions
|
|
99
|
+
4. **Apply results** theo Agent Rules (consistency, error handling, testing...)
|
|
441
100
|
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
101
|
+
5. **Validate:**
|
|
102
|
+
```bash
|
|
103
|
+
dart format . && flutter analyze . && flutter test .
|
|
104
|
+
```
|
|
446
105
|
{{QUICK_REFERENCE}}
|
package/dist/utils/template.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { readFile, mkdir, writeFile, cp, access } from 'node:fs/promises';
|
|
1
|
+
import { readFile, mkdir, writeFile, cp, access, readdir } from 'node:fs/promises';
|
|
2
2
|
import { join, dirname } from 'node:path';
|
|
3
3
|
import { fileURLToPath } from 'node:url';
|
|
4
4
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
@@ -145,33 +145,50 @@ async function ensureSharedExists(targetDir) {
|
|
|
145
145
|
return true; // Created new
|
|
146
146
|
}
|
|
147
147
|
/**
|
|
148
|
-
* Generate rules
|
|
148
|
+
* Generate modular rules files for a platform
|
|
149
149
|
*/
|
|
150
150
|
async function generateRulesFile(targetDir, config) {
|
|
151
151
|
if (!config.rulesFile)
|
|
152
152
|
return;
|
|
153
|
-
// Load rules
|
|
154
|
-
const
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
153
|
+
// Load all rule files from templates/base/rules/
|
|
154
|
+
const rulesDir = join(ASSETS_DIR, 'templates', 'base', 'rules');
|
|
155
|
+
if (!await exists(rulesDir))
|
|
156
|
+
return;
|
|
157
|
+
const ruleFiles = (await readdir(rulesDir))
|
|
158
|
+
.filter(f => f.endsWith('.md'))
|
|
159
|
+
.sort();
|
|
160
|
+
if (ruleFiles.length === 0)
|
|
161
|
+
return;
|
|
159
162
|
if (config.rulesFile.mode === 'append') {
|
|
160
|
-
// Append mode:
|
|
163
|
+
// Append mode: concatenate all rules into target file (e.g. CLAUDE.md)
|
|
164
|
+
const rulesFilePath = join(targetDir, config.rulesFile.path);
|
|
165
|
+
const rulesFileDir = dirname(rulesFilePath);
|
|
166
|
+
await mkdir(rulesFileDir, { recursive: true });
|
|
161
167
|
let existing = '';
|
|
162
168
|
if (await exists(rulesFilePath)) {
|
|
163
169
|
existing = await readFile(rulesFilePath, 'utf-8');
|
|
164
|
-
// Check if rules already appended
|
|
165
170
|
if (existing.includes('Flutter Pro Max — Agent Rules')) {
|
|
166
171
|
return; // Already has rules
|
|
167
172
|
}
|
|
168
173
|
existing += '\n\n';
|
|
169
174
|
}
|
|
170
|
-
|
|
175
|
+
// Concatenate all rule files
|
|
176
|
+
const allRules = ['# Flutter Pro Max — Agent Rules\n'];
|
|
177
|
+
for (const file of ruleFiles) {
|
|
178
|
+
const content = await readFile(join(rulesDir, file), 'utf-8');
|
|
179
|
+
allRules.push(content);
|
|
180
|
+
}
|
|
181
|
+
await writeFile(rulesFilePath, existing + allRules.join('\n---\n\n'), 'utf-8');
|
|
171
182
|
}
|
|
172
183
|
else {
|
|
173
|
-
// Create mode:
|
|
174
|
-
|
|
184
|
+
// Create mode: generate separate files in rules directory
|
|
185
|
+
const rulesTargetDir = dirname(join(targetDir, config.rulesFile.path));
|
|
186
|
+
await mkdir(rulesTargetDir, { recursive: true });
|
|
187
|
+
for (const file of ruleFiles) {
|
|
188
|
+
const content = await readFile(join(rulesDir, file), 'utf-8');
|
|
189
|
+
const targetPath = join(rulesTargetDir, file);
|
|
190
|
+
await writeFile(targetPath, content, 'utf-8');
|
|
191
|
+
}
|
|
175
192
|
}
|
|
176
193
|
}
|
|
177
194
|
/**
|