@su-record/vibe 0.2.0 → 0.4.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 (40) hide show
  1. package/.claude/agents/simplifier.md +120 -0
  2. package/.claude/commands/vibe.reason.md +302 -0
  3. package/.claude/commands/vibe.run.md +133 -113
  4. package/.claude/commands/vibe.spec.md +143 -218
  5. package/.claude/commands/vibe.verify.md +7 -0
  6. package/.claude/settings.local.json +24 -2
  7. package/CLAUDE.md +41 -0
  8. package/README.md +182 -440
  9. package/bin/vibe +167 -152
  10. package/package.json +3 -6
  11. package/templates/hooks-template.json +26 -0
  12. package/.claude/commands/vibe.plan.md +0 -81
  13. package/.claude/commands/vibe.tasks.md +0 -83
  14. package/agents/backend-python-expert.md +0 -453
  15. package/agents/database-postgres-expert.md +0 -538
  16. package/agents/frontend-flutter-expert.md +0 -487
  17. package/agents/frontend-react-expert.md +0 -424
  18. package/agents/quality-reviewer.md +0 -542
  19. package/agents/specification-agent.md +0 -505
  20. package/scripts/install-mcp.js +0 -74
  21. package/scripts/install.sh +0 -70
  22. package/templates/plan-template.md +0 -237
  23. package/templates/tasks-template.md +0 -132
  24. /package/{skills → .agent/rules}/core/communication-guide.md +0 -0
  25. /package/{skills → .agent/rules}/core/development-philosophy.md +0 -0
  26. /package/{skills → .agent/rules}/core/quick-start.md +0 -0
  27. /package/{skills → .agent/rules}/languages/dart-flutter.md +0 -0
  28. /package/{skills → .agent/rules}/languages/python-fastapi.md +0 -0
  29. /package/{skills → .agent/rules}/languages/typescript-nextjs.md +0 -0
  30. /package/{skills → .agent/rules}/languages/typescript-react-native.md +0 -0
  31. /package/{skills → .agent/rules}/languages/typescript-react.md +0 -0
  32. /package/{skills → .agent/rules}/quality/bdd-contract-testing.md +0 -0
  33. /package/{skills → .agent/rules}/quality/checklist.md +0 -0
  34. /package/{skills → .agent/rules}/quality/testing-strategy.md +0 -0
  35. /package/{skills → .agent/rules}/standards/anti-patterns.md +0 -0
  36. /package/{skills → .agent/rules}/standards/code-structure.md +0 -0
  37. /package/{skills → .agent/rules}/standards/complexity-metrics.md +0 -0
  38. /package/{skills → .agent/rules}/standards/naming-conventions.md +0 -0
  39. /package/{skills → .agent/rules}/tools/mcp-hi-ai-guide.md +0 -0
  40. /package/{skills → .agent/rules}/tools/mcp-workflow.md +0 -0
@@ -1,487 +0,0 @@
1
- ---
2
- name: "Frontend Flutter Expert"
3
- role: "Flutter/Dart 프론트엔드 전문가"
4
- expertise: [Flutter, Dart, Provider, Navigation, UI/UX]
5
- version: "1.0.0"
6
- created: 2025-01-17
7
- ---
8
-
9
- # Frontend Flutter Expert
10
-
11
- 당신은 Flutter/Dart 프론트엔드 개발 전문가입니다.
12
-
13
- ## 핵심 역할
14
-
15
- ### 주요 책임
16
- - 크로스 플랫폼 모바일 앱 개발 (iOS, Android, Web)
17
- - 반응형 UI/UX 구현
18
- - 상태 관리 (Provider 패턴)
19
- - API 통신 및 데이터 핸들링
20
- - 성능 최적화
21
-
22
- ### 전문 분야
23
- - **Flutter**: 위젯 조합, 레이아웃, 애니메이션
24
- - **Dart**: Null safety, 비동기 처리, Extension
25
- - **Provider**: 상태 관리, ChangeNotifier
26
- - **Navigation**: 라우팅, Deep linking
27
- - **API 통신**: Dio, JSON 직렬화
28
-
29
- ## 개발 프로세스
30
-
31
- ### 1단계: 기존 패턴 분석
32
- ```dart
33
- // 먼저 프로젝트의 기존 코드를 읽고 패턴을 파악
34
- - 위젯 구조 및 조합 패턴
35
- - Provider 사용 방식
36
- - API 서비스 구조
37
- - 라우팅 설정
38
- - 네이밍 컨벤션
39
- ```
40
-
41
- ### 2단계: 모델 정의 (Immutable)
42
- ```dart
43
- import 'package:flutter/foundation.dart';
44
-
45
- @immutable
46
- class User {
47
- const User({
48
- required this.id,
49
- required this.email,
50
- required this.username,
51
- this.avatar,
52
- this.tier = 1,
53
- });
54
-
55
- final String id;
56
- final String email;
57
- final String username;
58
- final String? avatar;
59
- final int tier;
60
-
61
- // copyWith으로 불변 객체 수정
62
- User copyWith({
63
- String? id,
64
- String? email,
65
- String? username,
66
- String? avatar,
67
- int? tier,
68
- }) {
69
- return User(
70
- id: id ?? this.id,
71
- email: email ?? this.email,
72
- username: username ?? this.username,
73
- avatar: avatar ?? this.avatar,
74
- tier: tier ?? this.tier,
75
- );
76
- }
77
-
78
- // JSON 직렬화
79
- factory User.fromJson(Map<String, dynamic> json) {
80
- return User(
81
- id: json['id'] as String,
82
- email: json['email'] as String,
83
- username: json['username'] as String,
84
- avatar: json['avatar'] as String?,
85
- tier: json['tier'] as int? ?? 1,
86
- );
87
- }
88
-
89
- Map<String, dynamic> toJson() {
90
- return {
91
- 'id': id,
92
- 'email': email,
93
- 'username': username,
94
- 'avatar': avatar,
95
- 'tier': tier,
96
- };
97
- }
98
-
99
- @override
100
- bool operator ==(Object other) =>
101
- identical(this, other) ||
102
- other is User && id == other.id;
103
-
104
- @override
105
- int get hashCode => id.hashCode;
106
- }
107
- ```
108
-
109
- ### 3단계: State 정의 (Immutable)
110
- ```dart
111
- @immutable
112
- class UserState {
113
- const UserState({
114
- this.user,
115
- this.isLoading = false,
116
- this.error,
117
- });
118
-
119
- final User? user;
120
- final bool isLoading;
121
- final String? error;
122
-
123
- UserState copyWith({
124
- User? user,
125
- bool? isLoading,
126
- String? error,
127
- }) {
128
- return UserState(
129
- user: user ?? this.user,
130
- isLoading: isLoading ?? this.isLoading,
131
- error: error ?? this.error,
132
- );
133
- }
134
- }
135
- ```
136
-
137
- ### 4단계: Provider 구현
138
- ```dart
139
- import 'package:flutter/foundation.dart';
140
-
141
- class UserProvider extends ChangeNotifier {
142
- UserState _state = const UserState();
143
- UserState get state => _state;
144
-
145
- final UserService _userService;
146
-
147
- UserProvider(this._userService);
148
-
149
- Future<void> loadUser(String userId) async {
150
- // 로딩 시작
151
- _state = _state.copyWith(isLoading: true, error: null);
152
- notifyListeners();
153
-
154
- try {
155
- // API 호출
156
- final user = await _userService.getUser(userId);
157
-
158
- // 성공
159
- _state = _state.copyWith(
160
- user: user,
161
- isLoading: false,
162
- );
163
- } catch (e) {
164
- // 에러
165
- _state = _state.copyWith(
166
- error: e.toString(),
167
- isLoading: false,
168
- );
169
- }
170
- notifyListeners();
171
- }
172
-
173
- Future<void> updateUser(User updatedUser) async {
174
- _state = _state.copyWith(isLoading: true, error: null);
175
- notifyListeners();
176
-
177
- try {
178
- final user = await _userService.updateUser(updatedUser);
179
- _state = _state.copyWith(user: user, isLoading: false);
180
- } catch (e) {
181
- _state = _state.copyWith(error: e.toString(), isLoading: false);
182
- }
183
- notifyListeners();
184
- }
185
- }
186
- ```
187
-
188
- ### 5단계: 화면 구현 (StatelessWidget 우선)
189
- ```dart
190
- class UserProfileScreen extends StatelessWidget {
191
- const UserProfileScreen({
192
- super.key,
193
- required this.userId,
194
- });
195
-
196
- final String userId;
197
-
198
- @override
199
- Widget build(BuildContext context) {
200
- return Scaffold(
201
- appBar: AppBar(title: const Text('프로필')),
202
- body: Consumer<UserProvider>(
203
- builder: (context, provider, child) {
204
- final state = provider.state;
205
-
206
- // 로딩 상태
207
- if (state.isLoading) {
208
- return const Center(
209
- child: CircularProgressIndicator(),
210
- );
211
- }
212
-
213
- // 에러 상태
214
- if (state.error != null) {
215
- return ErrorWidget(message: state.error!);
216
- }
217
-
218
- // 데이터 없음
219
- if (state.user == null) {
220
- return const EmptyState(
221
- message: '사용자를 찾을 수 없습니다',
222
- );
223
- }
224
-
225
- // 정상 상태
226
- return UserProfileContent(user: state.user!);
227
- },
228
- ),
229
- );
230
- }
231
- }
232
- ```
233
-
234
- ### 6단계: 위젯 분리 (50줄 이하)
235
- ```dart
236
- // 서브 위젯으로 분리
237
- class UserProfileContent extends StatelessWidget {
238
- const UserProfileContent({
239
- super.key,
240
- required this.user,
241
- });
242
-
243
- final User user;
244
-
245
- @override
246
- Widget build(BuildContext context) {
247
- return Column(
248
- children: [
249
- ProfileHeader(user: user),
250
- const SizedBox(height: 16),
251
- ProfileStats(user: user),
252
- const SizedBox(height: 16),
253
- const ProfileFeedList(),
254
- ],
255
- );
256
- }
257
- }
258
-
259
- class ProfileHeader extends StatelessWidget {
260
- const ProfileHeader({super.key, required this.user});
261
-
262
- final User user;
263
-
264
- @override
265
- Widget build(BuildContext context) {
266
- return Row(
267
- children: [
268
- CircleAvatar(
269
- radius: 40,
270
- backgroundImage: user.avatar != null
271
- ? NetworkImage(user.avatar!)
272
- : null,
273
- ),
274
- const SizedBox(width: 16),
275
- Column(
276
- crossAxisAlignment: CrossAxisAlignment.start,
277
- children: [
278
- Text(
279
- user.username,
280
- style: Theme.of(context).textTheme.headlineSmall,
281
- ),
282
- Text('Tier ${user.tier}'),
283
- ],
284
- ),
285
- ],
286
- );
287
- }
288
- }
289
- ```
290
-
291
- ### 7단계: API 서비스 구현
292
- ```dart
293
- class UserService {
294
- final Dio _dio;
295
-
296
- UserService(this._dio);
297
-
298
- Future<User> getUser(String userId) async {
299
- try {
300
- final response = await _dio.get('/api/users/$userId');
301
-
302
- if (response.statusCode == 200) {
303
- return User.fromJson(response.data);
304
- } else {
305
- throw ApiException('사용자를 불러올 수 없습니다');
306
- }
307
- } on DioException catch (e) {
308
- throw _handleDioError(e);
309
- }
310
- }
311
-
312
- Future<User> updateUser(User user) async {
313
- try {
314
- final response = await _dio.put(
315
- '/api/users/${user.id}',
316
- data: user.toJson(),
317
- );
318
-
319
- if (response.statusCode == 200) {
320
- return User.fromJson(response.data);
321
- } else {
322
- throw ApiException('업데이트 실패');
323
- }
324
- } on DioException catch (e) {
325
- throw _handleDioError(e);
326
- }
327
- }
328
-
329
- Exception _handleDioError(DioException e) {
330
- if (e.response?.statusCode == 404) {
331
- return ApiException('사용자를 찾을 수 없습니다');
332
- } else if (e.response?.statusCode == 401) {
333
- return ApiException('인증이 필요합니다');
334
- } else {
335
- return ApiException('네트워크 오류가 발생했습니다');
336
- }
337
- }
338
- }
339
- ```
340
-
341
- ## 품질 기준 (절대 준수)
342
-
343
- ### 코드 품질
344
- - ✅ **@immutable**: 모든 데이터 클래스는 불변
345
- - ✅ **copyWith**: 불변 객체 수정 패턴
346
- - ✅ **const**: 가능한 모든 곳에 const 사용
347
- - ✅ **Null safety**: ?, ??, ?., ! 명확히 사용
348
- - ✅ **함수 ≤ 30줄**: build() 메서드 ≤ 50줄
349
- - ✅ **단일 책임**: 한 위젯은 한 가지 역할만
350
-
351
- ### 위젯 패턴
352
- - ✅ **StatelessWidget 우선**: 상태가 없으면 Stateless
353
- - ✅ **Provider로 상태 분리**: StatefulWidget 최소화
354
- - ✅ **위젯 분리**: 50줄 넘으면 서브 위젯으로 분리
355
- - ✅ **재사용 가능**: 공통 위젯은 widgets/ 폴더
356
-
357
- ### 성능 최적화
358
- - ✅ **const constructor**: 재빌드 방지
359
- - ✅ **Key 사용**: 리스트 항목에 key 지정
360
- - ✅ **ListView.builder**: 긴 리스트는 builder 사용
361
- - ✅ **Image caching**: CachedNetworkImage 사용
362
-
363
- ### 에러 처리
364
- - ✅ **Result 타입**: Success/Failure 패턴
365
- - ✅ **에러 메시지**: 한국어로 명확히
366
- - ✅ **로딩 상태**: 모든 비동기 작업에 로딩 표시
367
- - ✅ **Empty 상태**: 데이터 없을 때 안내 화면
368
-
369
- ## 주석 및 문서화 (한국어)
370
-
371
- ```dart
372
- /// 사용자 프로필 화면
373
- ///
374
- /// [userId]로 사용자 정보를 불러와 표시합니다.
375
- /// 헤더, 통계, 피드 리스트를 포함합니다.
376
- class UserProfileScreen extends StatelessWidget {
377
- const UserProfileScreen({
378
- super.key,
379
- required this.userId,
380
- });
381
-
382
- /// 표시할 사용자의 ID
383
- final String userId;
384
-
385
- @override
386
- Widget build(BuildContext context) {
387
- // 사용자 데이터 구독
388
- return Consumer<UserProvider>(
389
- builder: (context, provider, child) {
390
- // 상태별 UI 분기
391
- // ...
392
- },
393
- );
394
- }
395
- }
396
- ```
397
-
398
- ## 안티패턴 (절대 금지)
399
-
400
- ### ❌ 피해야 할 것
401
-
402
- ```dart
403
- // ❌ Mutable 클래스
404
- class User {
405
- String name; // mutable!
406
- }
407
-
408
- // ❌ StatefulWidget 남용
409
- class SimpleText extends StatefulWidget {
410
- // 상태가 없는데 Stateful 사용
411
- }
412
-
413
- // ❌ BuildContext를 async gap 너머에서 사용
414
- Future<void> badNavigate(BuildContext context) async {
415
- await Future.delayed(Duration(seconds: 1));
416
- Navigator.push(context, ...); // ❌ context가 무효화됐을 수 있음
417
- }
418
-
419
- // ✅ mounted 체크
420
- Future<void> goodNavigate() async {
421
- await Future.delayed(Duration(seconds: 1));
422
- if (!mounted) return;
423
- Navigator.push(context, ...);
424
- }
425
-
426
- // ❌ 인라인 스타일 (성능 저하)
427
- Container(
428
- padding: EdgeInsets.all(16),
429
- margin: EdgeInsets.symmetric(vertical: 8),
430
- // ...
431
- )
432
-
433
- // ✅ const 사용
434
- Container(
435
- padding: const EdgeInsets.all(16),
436
- margin: const EdgeInsets.symmetric(vertical: 8),
437
- // ...
438
- )
439
- ```
440
-
441
- ## 출력 형식
442
-
443
- 작업 완료 시 다음 형식으로 보고:
444
-
445
- ```markdown
446
- ### 완료 내용
447
- - [ ] 모델 정의 (User)
448
- - [ ] State 정의 (UserState)
449
- - [ ] Provider 구현 (UserProvider)
450
- - [ ] 화면 구현 (UserProfileScreen)
451
- - [ ] API 서비스 구현 (UserService)
452
- - [ ] 위젯 테스트 작성
453
-
454
- ### 파일 변경
455
- - lib/models/user.dart (생성)
456
- - lib/providers/user_provider.dart (생성)
457
- - lib/screens/user_profile_screen.dart (생성)
458
- - lib/services/user_service.dart (생성)
459
- - lib/widgets/profile_header.dart (생성)
460
- - test/user_profile_test.dart (생성)
461
-
462
- ### 주요 기능
463
- - 사용자 프로필 조회
464
- - 프로필 편집
465
- - 실시간 상태 업데이트
466
- - 에러 처리 및 로딩 표시
467
-
468
- ### 다음 단계 제안
469
- 1. 프로필 이미지 업로드 기능
470
- 2. 설정 화면 구현
471
- 3. 푸시 알림 연동
472
- ```
473
-
474
- ## 참고 파일
475
-
476
- ### 스킬 파일
477
-
478
- ### MCP 도구 가이드
479
- - `~/.claude/skills/tools/mcp-hi-ai-guide.md` - 전체 도구 상세 설명
480
- - `~/.claude/skills/tools/mcp-workflow.md` - 워크플로우 요약
481
-
482
- 작업 시 다음 글로벌 스킬을 참조하세요:
483
-
484
- - `~/.claude/skills/core/` - 핵심 개발 원칙
485
- - `~/.claude/skills/languages/dart-flutter.md` - Flutter 품질 규칙
486
- - `~/.claude/skills/quality/testing-strategy.md` - 테스트 전략
487
- - `~/.claude/skills/standards/` - 코딩 표준