@relipa/ai-flow-kit 0.2.0 → 0.2.2-beta.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.
- package/README.md +4 -4
- package/custom/rules/java/spring-boot-rules.md +209 -0
- package/custom/rules/javascript/nestjs-examples.md +41 -0
- package/custom/rules/javascript/nestjs-rules.md +42 -0
- package/custom/rules/javascript/nodejs-express-examples.md +35 -0
- package/custom/rules/javascript/nodejs-express-rules.md +49 -0
- package/custom/rules/javascript/reactjs-examples.md +380 -0
- package/custom/rules/javascript/reactjs-rules.md +173 -0
- package/custom/rules/php/php-examples.md +161 -0
- package/custom/rules/php/php-rules.md +127 -0
- package/custom/rules/python/python-django-examples.md +34 -0
- package/custom/rules/python/python-django-rules.md +48 -0
- package/custom/rules/python/python-examples.md +32 -0
- package/custom/rules/python/python-fastapi-examples.md +30 -0
- package/custom/rules/python/python-fastapi-rules.md +35 -0
- package/custom/rules/python/python-ml-examples.md +187 -0
- package/custom/rules/python/python-ml-rules.md +121 -0
- package/custom/rules/python/python-rules.md +58 -0
- package/custom/skills/ba-skills/skill-ba-qna-template-v1.md +4 -4
- package/custom/skills/ba-skills/skill-ba-qna-v1.md +6 -0
- package/custom/skills/create-system-requirement/SKILL.md +99 -25
- package/custom/skills/create-system-requirement/system-requirement-template-v1.md +282 -0
- package/custom/skills/execute-flow/SKILL.md +142 -36
- package/custom/skills/execute-flow/templates/evidence-helper.ts +145 -0
- package/custom/skills/execute-flow/templates/playwright.config.ts +28 -8
- package/custom/skills/impact-analysis/SKILL.md +106 -106
- package/custom/skills/read-study-requirement/SKILL.md +1 -2
- package/custom/skills/report-customer/SKILL.md +99 -99
- package/custom/skills/script-sync/SKILL.md +54 -16
- package/custom/templates/nestjs.md +5 -72
- package/custom/templates/nodejs-express.md +5 -73
- package/custom/templates/php-plain.md +5 -261
- package/custom/templates/php.md +5 -261
- package/custom/templates/python-django.md +5 -71
- package/custom/templates/python-fastapi.md +5 -54
- package/custom/templates/python-ml.md +1 -269
- package/custom/templates/python.md +5 -79
- package/custom/templates/reactjs.md +5 -492
- package/custom/templates/shared/gate-workflow.md +18 -11
- package/custom/templates/shared/ml-gate-workflow.md +1 -0
- package/custom/templates/spring-boot.md +5 -224
- package/docs/common/CHANGELOG.md +24 -6
- package/docs/common/INDEX.md +1 -0
- package/docs/common/QUICK_START.md +1 -1
- package/docs/common/System-Requirement-Read-Guide.md +178 -0
- package/docs/common/Testing-Structure.md +31 -25
- package/docs/common/cli-reference.md +12 -10
- package/package.json +1 -1
- package/scripts/init.js +143 -40
- package/scripts/prompt.js +3 -3
- package/scripts/scaffold-playwright.js +2 -0
|
@@ -1,492 +1,5 @@
|
|
|
1
|
-
# React.js AI System Prompt
|
|
2
|
-
|
|
3
|
-
You are an expert React.js developer. Follow these specific rules when generating or modifying code in this project.
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
## Project Stack
|
|
8
|
-
|
|
9
|
-
- **React:** 18+
|
|
10
|
-
- **Language:** TypeScript
|
|
11
|
-
- **Build Tool:** Vite (prefer) or Create React App
|
|
12
|
-
- **State Management:** Zustand (local/global) or TanStack Query (server state)
|
|
13
|
-
- **Routing:** React Router v6
|
|
14
|
-
- **Styling:** Tailwind CSS + shadcn/ui (or CSS Modules if no Tailwind)
|
|
15
|
-
- **Form:** React Hook Form + Zod validation
|
|
16
|
-
- **HTTP Client:** Axios with interceptors
|
|
17
|
-
- **Testing:** Vitest + React Testing Library
|
|
18
|
-
|
|
19
|
-
---
|
|
20
|
-
|
|
21
|
-
## Project Structure
|
|
22
|
-
|
|
23
|
-
```
|
|
24
|
-
src/
|
|
25
|
-
├── components/
|
|
26
|
-
│ ├── ui/ # Generic, reusable UI (Button, Input, Modal...)
|
|
27
|
-
│ └── [feature]/ # Feature-specific components
|
|
28
|
-
├── pages/ # Route-level components (thin, delegate to features)
|
|
29
|
-
├── features/ # Self-contained feature modules
|
|
30
|
-
│ └── [feature]/
|
|
31
|
-
│ ├── components/ # UI specific to this feature
|
|
32
|
-
│ ├── hooks/ # Custom hooks for this feature
|
|
33
|
-
│ ├── api.ts # API calls for this feature
|
|
34
|
-
│ ├── store.ts # Zustand slice (if needed)
|
|
35
|
-
│ ├── types.ts # TypeScript types/interfaces
|
|
36
|
-
│ └── index.ts # Public exports
|
|
37
|
-
├── hooks/ # Shared custom hooks
|
|
38
|
-
├── services/
|
|
39
|
-
│ └── api.ts # Axios instance + interceptors
|
|
40
|
-
├── store/ # Global Zustand store
|
|
41
|
-
├── types/ # Shared TypeScript types
|
|
42
|
-
├── utils/ # Pure utility functions
|
|
43
|
-
├── constants/ # App-wide constants
|
|
44
|
-
└── App.tsx
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
---
|
|
48
|
-
|
|
49
|
-
## Component Rules
|
|
50
|
-
|
|
51
|
-
### Always use Functional Components
|
|
52
|
-
|
|
53
|
-
```tsx
|
|
54
|
-
// ✅ Good
|
|
55
|
-
const UserCard = ({ user }: UserCardProps) => {
|
|
56
|
-
return <div>{user.name}</div>;
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
// ❌ Bad — class component
|
|
60
|
-
class UserCard extends React.Component {}
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
### Define Props with TypeScript interface
|
|
64
|
-
|
|
65
|
-
```tsx
|
|
66
|
-
// ✅ Good
|
|
67
|
-
interface UserCardProps {
|
|
68
|
-
user: User;
|
|
69
|
-
onDelete?: (id: number) => void;
|
|
70
|
-
className?: string;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
const UserCard = ({ user, onDelete, className }: UserCardProps) => {
|
|
74
|
-
// ...
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
// ❌ Bad — any, or no type
|
|
78
|
-
const UserCard = ({ user }: any) => {};
|
|
79
|
-
const UserCard = (props) => {};
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
### Keep components focused and small
|
|
83
|
-
|
|
84
|
-
- One component = one responsibility
|
|
85
|
-
- If a component exceeds ~150 lines, split it
|
|
86
|
-
- Extract logic to custom hooks, keep JSX clean
|
|
87
|
-
|
|
88
|
-
```tsx
|
|
89
|
-
// ✅ Good — logic extracted to hook
|
|
90
|
-
const UserList = () => {
|
|
91
|
-
const { users, isLoading, error, deleteUser } = useUserList();
|
|
92
|
-
|
|
93
|
-
if (isLoading) return <Spinner />;
|
|
94
|
-
if (error) return <ErrorMessage message={error.message} />;
|
|
95
|
-
|
|
96
|
-
return (
|
|
97
|
-
<ul>
|
|
98
|
-
{users.map(user => (
|
|
99
|
-
<UserCard key={user.id} user={user} onDelete={deleteUser} />
|
|
100
|
-
))}
|
|
101
|
-
</ul>
|
|
102
|
-
);
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
// ❌ Bad — logic mixed into component
|
|
106
|
-
const UserList = () => {
|
|
107
|
-
const [users, setUsers] = useState([]);
|
|
108
|
-
const [loading, setLoading] = useState(true);
|
|
109
|
-
|
|
110
|
-
useEffect(() => {
|
|
111
|
-
fetch('/api/users')
|
|
112
|
-
.then(r => r.json())
|
|
113
|
-
.then(data => { setUsers(data); setLoading(false); });
|
|
114
|
-
}, []);
|
|
115
|
-
|
|
116
|
-
const handleDelete = async (id) => {
|
|
117
|
-
await fetch(`/api/users/${id}`, { method: 'DELETE' });
|
|
118
|
-
setUsers(prev => prev.filter(u => u.id !== id));
|
|
119
|
-
};
|
|
120
|
-
// ... long JSX
|
|
121
|
-
};
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
### Export convention
|
|
125
|
-
|
|
126
|
-
```tsx
|
|
127
|
-
// Named export for regular components
|
|
128
|
-
export const UserCard = ({ user }: UserCardProps) => { ... };
|
|
129
|
-
|
|
130
|
-
// Default export only for page-level components
|
|
131
|
-
export default function UserPage() { ... }
|
|
132
|
-
```
|
|
133
|
-
|
|
134
|
-
---
|
|
135
|
-
|
|
136
|
-
## Custom Hook Rules
|
|
137
|
-
|
|
138
|
-
- Prefix with `use`: `useUserList`, `useAuth`, `useModal`
|
|
139
|
-
- Extract all side effects, API calls, and complex state from components
|
|
140
|
-
- Return objects (not arrays) for multiple values — easier to destructure
|
|
141
|
-
|
|
142
|
-
```tsx
|
|
143
|
-
// ✅ Good
|
|
144
|
-
const useUserList = () => {
|
|
145
|
-
const { data: users = [], isLoading, error } = useQuery({
|
|
146
|
-
queryKey: ['users'],
|
|
147
|
-
queryFn: userApi.getAll,
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
const { mutate: deleteUser } = useMutation({
|
|
151
|
-
mutationFn: userApi.delete,
|
|
152
|
-
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['users'] }),
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
return { users, isLoading, error, deleteUser };
|
|
156
|
-
};
|
|
157
|
-
|
|
158
|
-
// Usage
|
|
159
|
-
const { users, isLoading, deleteUser } = useUserList();
|
|
160
|
-
```
|
|
161
|
-
|
|
162
|
-
---
|
|
163
|
-
|
|
164
|
-
## State Management Rules
|
|
165
|
-
|
|
166
|
-
### Server state → TanStack Query
|
|
167
|
-
|
|
168
|
-
Use for all API data (fetching, caching, mutations):
|
|
169
|
-
|
|
170
|
-
```tsx
|
|
171
|
-
// Fetching
|
|
172
|
-
const { data, isLoading, error } = useQuery({
|
|
173
|
-
queryKey: ['users', filters], // include deps in key
|
|
174
|
-
queryFn: () => userApi.getAll(filters),
|
|
175
|
-
staleTime: 5 * 60 * 1000, // 5 minutes
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
// Mutation
|
|
179
|
-
const { mutate, isPending } = useMutation({
|
|
180
|
-
mutationFn: userApi.create,
|
|
181
|
-
onSuccess: () => {
|
|
182
|
-
queryClient.invalidateQueries({ queryKey: ['users'] });
|
|
183
|
-
toast.success('User created');
|
|
184
|
-
},
|
|
185
|
-
onError: (error) => toast.error(error.message),
|
|
186
|
-
});
|
|
187
|
-
```
|
|
188
|
-
|
|
189
|
-
### Client/UI state → Zustand (or useState for local)
|
|
190
|
-
|
|
191
|
-
```tsx
|
|
192
|
-
// Local state — use useState
|
|
193
|
-
const [isOpen, setIsOpen] = useState(false);
|
|
194
|
-
|
|
195
|
-
// Shared UI state — use Zustand
|
|
196
|
-
const useAuthStore = create<AuthStore>((set) => ({
|
|
197
|
-
user: null,
|
|
198
|
-
token: null,
|
|
199
|
-
login: (user, token) => set({ user, token }),
|
|
200
|
-
logout: () => set({ user: null, token: null }),
|
|
201
|
-
}));
|
|
202
|
-
```
|
|
203
|
-
|
|
204
|
-
### Rules:
|
|
205
|
-
- **Never** use global state for server/API data — that's TanStack Query's job
|
|
206
|
-
- **Never** fetch in `useEffect` manually — use TanStack Query
|
|
207
|
-
- **Never** put loading/error state in Zustand — TanStack Query handles that
|
|
208
|
-
|
|
209
|
-
---
|
|
210
|
-
|
|
211
|
-
## Form Rules
|
|
212
|
-
|
|
213
|
-
Use **React Hook Form + Zod** for all forms:
|
|
214
|
-
|
|
215
|
-
```tsx
|
|
216
|
-
// 1. Define schema with Zod
|
|
217
|
-
const createUserSchema = z.object({
|
|
218
|
-
email: z.string().email('Invalid email'),
|
|
219
|
-
fullName: z.string().min(2, 'Min 2 characters').max(100),
|
|
220
|
-
password: z.string().min(8, 'Min 8 characters'),
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
type CreateUserForm = z.infer<typeof createUserSchema>;
|
|
224
|
-
|
|
225
|
-
// 2. Use in component
|
|
226
|
-
const CreateUserForm = () => {
|
|
227
|
-
const { register, handleSubmit, formState: { errors, isSubmitting } } = useForm<CreateUserForm>({
|
|
228
|
-
resolver: zodResolver(createUserSchema),
|
|
229
|
-
});
|
|
230
|
-
|
|
231
|
-
const onSubmit = async (data: CreateUserForm) => {
|
|
232
|
-
await createUser(data);
|
|
233
|
-
};
|
|
234
|
-
|
|
235
|
-
return (
|
|
236
|
-
<form onSubmit={handleSubmit(onSubmit)}>
|
|
237
|
-
<input {...register('email')} />
|
|
238
|
-
{errors.email && <span>{errors.email.message}</span>}
|
|
239
|
-
|
|
240
|
-
<button type="submit" disabled={isSubmitting}>
|
|
241
|
-
{isSubmitting ? 'Saving...' : 'Create'}
|
|
242
|
-
</button>
|
|
243
|
-
</form>
|
|
244
|
-
);
|
|
245
|
-
};
|
|
246
|
-
```
|
|
247
|
-
|
|
248
|
-
---
|
|
249
|
-
|
|
250
|
-
## API Layer Rules
|
|
251
|
-
|
|
252
|
-
One `axios` instance shared across the app with interceptors:
|
|
253
|
-
|
|
254
|
-
```tsx
|
|
255
|
-
// src/services/api.ts
|
|
256
|
-
const api = axios.create({
|
|
257
|
-
baseURL: import.meta.env.VITE_API_URL,
|
|
258
|
-
timeout: 10000,
|
|
259
|
-
headers: { 'Content-Type': 'application/json' },
|
|
260
|
-
});
|
|
261
|
-
|
|
262
|
-
// Attach token automatically
|
|
263
|
-
api.interceptors.request.use((config) => {
|
|
264
|
-
const token = useAuthStore.getState().token;
|
|
265
|
-
if (token) config.headers.Authorization = `Bearer ${token}`;
|
|
266
|
-
return config;
|
|
267
|
-
});
|
|
268
|
-
|
|
269
|
-
// Handle 401 globally
|
|
270
|
-
api.interceptors.response.use(
|
|
271
|
-
(response) => response,
|
|
272
|
-
(error) => {
|
|
273
|
-
if (error.response?.status === 401) {
|
|
274
|
-
useAuthStore.getState().logout();
|
|
275
|
-
window.location.href = '/login';
|
|
276
|
-
}
|
|
277
|
-
return Promise.reject(error);
|
|
278
|
-
}
|
|
279
|
-
);
|
|
280
|
-
```
|
|
281
|
-
|
|
282
|
-
Feature API module:
|
|
283
|
-
|
|
284
|
-
```tsx
|
|
285
|
-
// src/features/users/api.ts
|
|
286
|
-
export const userApi = {
|
|
287
|
-
getAll: (params?: UserListParams): Promise<User[]> =>
|
|
288
|
-
api.get('/users', { params }).then(r => r.data),
|
|
289
|
-
|
|
290
|
-
getById: (id: number): Promise<User> =>
|
|
291
|
-
api.get(`/users/${id}`).then(r => r.data),
|
|
292
|
-
|
|
293
|
-
create: (data: CreateUserRequest): Promise<User> =>
|
|
294
|
-
api.post('/users', data).then(r => r.data),
|
|
295
|
-
|
|
296
|
-
update: (id: number, data: UpdateUserRequest): Promise<User> =>
|
|
297
|
-
api.put(`/users/${id}`, data).then(r => r.data),
|
|
298
|
-
|
|
299
|
-
delete: (id: number): Promise<void> =>
|
|
300
|
-
api.delete(`/users/${id}`).then(r => r.data),
|
|
301
|
-
};
|
|
302
|
-
```
|
|
303
|
-
|
|
304
|
-
---
|
|
305
|
-
|
|
306
|
-
## TypeScript Rules
|
|
307
|
-
|
|
308
|
-
- **No `any`** — use `unknown`, proper types, or generics
|
|
309
|
-
- Define shared types in `types.ts` files
|
|
310
|
-
- Use `interface` for object shapes, `type` for unions/intersections
|
|
311
|
-
- Always type function return values for public hooks and API functions
|
|
312
|
-
|
|
313
|
-
```tsx
|
|
314
|
-
// ✅ Good
|
|
315
|
-
interface User {
|
|
316
|
-
id: number;
|
|
317
|
-
email: string;
|
|
318
|
-
fullName: string;
|
|
319
|
-
createdAt: string;
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
type UserStatus = 'active' | 'inactive' | 'banned';
|
|
323
|
-
|
|
324
|
-
const getUser = async (id: number): Promise<User> => {
|
|
325
|
-
return api.get(`/users/${id}`).then(r => r.data);
|
|
326
|
-
};
|
|
327
|
-
|
|
328
|
-
// ❌ Bad
|
|
329
|
-
const getUser = async (id: any) => {
|
|
330
|
-
return api.get(`/users/${id}`).then((r: any) => r.data);
|
|
331
|
-
};
|
|
332
|
-
```
|
|
333
|
-
|
|
334
|
-
---
|
|
335
|
-
|
|
336
|
-
## Styling Rules (Tailwind CSS)
|
|
337
|
-
|
|
338
|
-
- Use utility classes directly — avoid custom CSS unless necessary
|
|
339
|
-
- Extract repeated class combinations into components or a `cn()` helper
|
|
340
|
-
- Use `className` prop for external style overrides
|
|
341
|
-
|
|
342
|
-
```tsx
|
|
343
|
-
// ✅ Good — cn() for conditional classes
|
|
344
|
-
import { cn } from '\@/utils/cn';
|
|
345
|
-
|
|
346
|
-
const Button = ({ variant = 'primary', disabled, className, children }: ButtonProps) => (
|
|
347
|
-
<button
|
|
348
|
-
className={cn(
|
|
349
|
-
'px-4 py-2 rounded font-medium transition-colors',
|
|
350
|
-
variant === 'primary' && 'bg-blue-600 text-white hover:bg-blue-700',
|
|
351
|
-
variant === 'outline' && 'border border-gray-300 hover:bg-gray-50',
|
|
352
|
-
disabled && 'opacity-50 cursor-not-allowed',
|
|
353
|
-
className
|
|
354
|
-
)}
|
|
355
|
-
disabled={disabled}
|
|
356
|
-
>
|
|
357
|
-
{children}
|
|
358
|
-
</button>
|
|
359
|
-
);
|
|
360
|
-
|
|
361
|
-
// ❌ Bad — inline style
|
|
362
|
-
<button style={{ backgroundColor: 'blue', padding: '8px 16px' }}>
|
|
363
|
-
```
|
|
364
|
-
|
|
365
|
-
---
|
|
366
|
-
|
|
367
|
-
## Performance Rules
|
|
368
|
-
|
|
369
|
-
- Use `React.memo` only when profiling shows a real problem — do not optimize prematurely
|
|
370
|
-
- Use `useMemo` / `useCallback` for expensive computations or stable references passed to children
|
|
371
|
-
- Lazy-load routes with `React.lazy` + `Suspense`
|
|
372
|
-
- Avoid anonymous functions in JSX props when passing to memoized children
|
|
373
|
-
|
|
374
|
-
```tsx
|
|
375
|
-
// Lazy routes
|
|
376
|
-
const UserPage = React.lazy(() => import('./pages/UserPage'));
|
|
377
|
-
|
|
378
|
-
<Suspense fallback={<PageSkeleton />}>
|
|
379
|
-
<UserPage />
|
|
380
|
-
</Suspense>
|
|
381
|
-
|
|
382
|
-
// useMemo for expensive computation
|
|
383
|
-
const sortedUsers = useMemo(
|
|
384
|
-
() => [...users].sort((a, b) => a.fullName.localeCompare(b.fullName)),
|
|
385
|
-
[users]
|
|
386
|
-
);
|
|
387
|
-
```
|
|
388
|
-
|
|
389
|
-
---
|
|
390
|
-
|
|
391
|
-
## Error Handling Rules
|
|
392
|
-
|
|
393
|
-
- Use React Error Boundary at the route level
|
|
394
|
-
- Handle API errors in TanStack Query `onError` callbacks
|
|
395
|
-
- Show user-friendly messages — never raw error objects
|
|
396
|
-
- Use toast notifications for async operation feedback
|
|
397
|
-
|
|
398
|
-
```tsx
|
|
399
|
-
// Error boundary at route level
|
|
400
|
-
<ErrorBoundary fallback={<ErrorPage />}>
|
|
401
|
-
<Routes>
|
|
402
|
-
<Route path="/users" element={<UserPage />} />
|
|
403
|
-
</Routes>
|
|
404
|
-
</ErrorBoundary>
|
|
405
|
-
|
|
406
|
-
// Async error handling
|
|
407
|
-
const { mutate } = useMutation({
|
|
408
|
-
mutationFn: userApi.create,
|
|
409
|
-
onSuccess: () => toast.success('User created successfully'),
|
|
410
|
-
onError: (error: AxiosError<ApiError>) =>
|
|
411
|
-
toast.error(error.response?.data?.message ?? 'Something went wrong'),
|
|
412
|
-
});
|
|
413
|
-
```
|
|
414
|
-
|
|
415
|
-
---
|
|
416
|
-
|
|
417
|
-
## Testing Rules
|
|
418
|
-
|
|
419
|
-
### Unit/Component tests with React Testing Library
|
|
420
|
-
|
|
421
|
-
```tsx
|
|
422
|
-
// Test behavior, not implementation
|
|
423
|
-
describe('UserCard', () => {
|
|
424
|
-
it('should display user name and email', () => {
|
|
425
|
-
const user: User = { id: 1, email: 'test\@example.com', fullName: 'Test User' };
|
|
426
|
-
render(<UserCard user={user} />);
|
|
427
|
-
|
|
428
|
-
expect(screen.getByText('Test User')).toBeInTheDocument();
|
|
429
|
-
expect(screen.getByText('test\@example.com')).toBeInTheDocument();
|
|
430
|
-
});
|
|
431
|
-
|
|
432
|
-
it('should call onDelete with user id when delete button clicked', async () => {
|
|
433
|
-
const onDelete = vi.fn();
|
|
434
|
-
const user: User = { id: 1, email: 'test\@example.com', fullName: 'Test User' };
|
|
435
|
-
|
|
436
|
-
render(<UserCard user={user} onDelete={onDelete} />);
|
|
437
|
-
await userEvent.click(screen.getByRole('button', { name: /delete/i }));
|
|
438
|
-
|
|
439
|
-
expect(onDelete).toHaveBeenCalledWith(1);
|
|
440
|
-
});
|
|
441
|
-
});
|
|
442
|
-
```
|
|
443
|
-
|
|
444
|
-
### Hook tests
|
|
445
|
-
|
|
446
|
-
```tsx
|
|
447
|
-
import { renderHook, waitFor } from '\@testing-library/react';
|
|
448
|
-
|
|
449
|
-
it('should fetch users', async () => {
|
|
450
|
-
const { result } = renderHook(() => useUserList(), { wrapper: QueryWrapper });
|
|
451
|
-
|
|
452
|
-
await waitFor(() => expect(result.current.isLoading).toBe(false));
|
|
453
|
-
|
|
454
|
-
expect(result.current.users).toHaveLength(2);
|
|
455
|
-
});
|
|
456
|
-
```
|
|
457
|
-
|
|
458
|
-
---
|
|
459
|
-
|
|
460
|
-
## Naming Conventions
|
|
461
|
-
|
|
462
|
-
| Element | Convention | Example |
|
|
463
|
-
|---------|-----------|---------|
|
|
464
|
-
| Component file | PascalCase | `UserCard.tsx` |
|
|
465
|
-
| Hook file | camelCase | `useUserList.ts` |
|
|
466
|
-
| Utility file | camelCase | `formatDate.ts` |
|
|
467
|
-
| Type/Interface | PascalCase | `User`, `CreateUserRequest` |
|
|
468
|
-
| Component | PascalCase | `UserCard`, `OrderList` |
|
|
469
|
-
| Hook | `use` + PascalCase | `useUserList`, `useAuth` |
|
|
470
|
-
| Event handler | `handle` + Action | `handleDelete`, `handleSubmit` |
|
|
471
|
-
| Boolean variable | `is/has/can` prefix | `isLoading`, `hasError`, `canEdit` |
|
|
472
|
-
| Constant | UPPER_SNAKE_CASE | `MAX_RETRY`, `API_TIMEOUT` |
|
|
473
|
-
| CSS class (custom) | kebab-case | `user-card`, `nav-item` |
|
|
474
|
-
|
|
475
|
-
---
|
|
476
|
-
|
|
477
|
-
## Common Anti-Patterns to Avoid
|
|
478
|
-
|
|
479
|
-
- ❌ Fetching in `useEffect` → use TanStack Query
|
|
480
|
-
- ❌ Using `any` type → define proper TypeScript types
|
|
481
|
-
- ❌ Storing server data in Zustand → that's TanStack Query's responsibility
|
|
482
|
-
- ❌ Class components → use functional components
|
|
483
|
-
- ❌ `index.js` everywhere → use named files for traceability
|
|
484
|
-
- ❌ Direct DOM manipulation → use React state/refs
|
|
485
|
-
- ❌ Mutating state directly → always use setter functions
|
|
486
|
-
- ❌ Large components doing everything → split into smaller focused components
|
|
487
|
-
- ❌ Prop drilling more than 2 levels → use context or Zustand
|
|
488
|
-
- ❌ Hardcoding API URLs → use `import.meta.env.VITE_API_URL`
|
|
489
|
-
|
|
490
|
-
---
|
|
491
|
-
|
|
492
|
-
When explaining changes, refer to the [React Official Documentation](https://react.dev), [TanStack Query](https://tanstack.com/query), and [React Hook Form](https://react-hook-form.com) conventions.
|
|
1
|
+
# React.js AI System Prompt
|
|
2
|
+
|
|
3
|
+
You are an expert React.js developer. Follow these specific rules when generating or modifying code in this project.
|
|
4
|
+
|
|
5
|
+
> **Rules & code examples:** Read `.rules/javascript/reactjs-rules.md` (structure, per-area rules, naming, anti-patterns) and `.rules/javascript/reactjs-examples.md` (code samples per rule area) **in full** before writing or modifying any React code in this project.
|
|
@@ -162,6 +162,7 @@ Only runs after Gate 2 has been APPROVED.
|
|
|
162
162
|
|
|
163
163
|
**INVOKE:** `superpowers:test-driven-development`
|
|
164
164
|
- **Pre-flight (bắt buộc):** chạy [Pre-flight — Đồng bộ Source & Docs](#pre-flight-bắt-buộc--đồng-bộ-source--docs-đầu-mỗi-gate) ở đầu file. Lỗi → hiển thị ⚠️ cảnh báo, không dừng gate.
|
|
165
|
+
- **Framework rules (bắt buộc, đọc trước dòng code đầu tiên):** nếu chưa đọc trong session này, đọc đầy đủ file(s) được trỏ ở dòng "> **Rules & code examples:**" đầu CLAUDE.md/AGENTS.md (`.rules/<lang>/<framework>-rules.md` + `-examples.md`). Áp dụng cho MỌI lần sửa code trong Gate này, không chỉ lần đầu.
|
|
165
166
|
- Complex feature (3+ files): `superpowers:subagent-driven-development`
|
|
166
167
|
- Write tests FIRST — run to confirm FAIL -> implement -> PASS.
|
|
167
168
|
- Bug fix EXTRA: `superpowers:systematic-debugging` + `investigate-bug` skill first.
|
|
@@ -474,9 +475,10 @@ Pre-flight checks (stop and report if any fail):
|
|
|
474
475
|
- If Y: read and update `~/.claude/settings.json` by merging the playwright entry into `mcpServers`, then instruct TESTER to **restart Claude Code** before retrying. Stop here.
|
|
475
476
|
- If n: show manual install instructions (add `playwright` to `mcpServers` in `~/.claude/settings.json`). Stop here.
|
|
476
477
|
1. TC file is parseable — Section 3 has at least one TC row
|
|
477
|
-
2. `
|
|
478
|
+
2. `AK-Docs/03.Testing/` subfolders exist (`04.Evidence/`, `05.Scripts/`, `02.Reports/`, `06.Bugs/` — auto-create if missing)
|
|
478
479
|
3. `BASE_URL` env var is set
|
|
479
|
-
4. `
|
|
480
|
+
4. `AK-Docs/03.Testing/05.Scripts/{repo}/playwright.config.ts` exists (or auto-scaffold the Playwright project inside AK-Docs — package.json, node_modules gitignored — see `execute-flow` SKILL.md "Scaffold Playwright Project")
|
|
481
|
+
5. `.gitignore` (repo root containing AK-Docs) ignores `03.Testing/04.Evidence/` (binary evidence, never committed even though it lives inside AK-Docs) and `03.Testing/05.Scripts/**/{node_modules,test-results,playwright-report,blob-report}/` — auto-append if missing
|
|
480
482
|
|
|
481
483
|
Then display work plan:
|
|
482
484
|
|
|
@@ -515,7 +517,7 @@ Hash-based TC↔script sync — for each TC in the file:
|
|
|
515
517
|
- **Unchanged TC** (hash matches): skip — do not touch.
|
|
516
518
|
- **Manual/Blocked TC**: mark `⏭️ Untest` / `⏳ Pending` — no script generated.
|
|
517
519
|
|
|
518
|
-
Output: `
|
|
520
|
+
Output: `AK-Docs/03.Testing/05.Scripts/{repo}/{featureDir}/{ScreenID}.spec.ts` (1 file per screen, all TCs inside). Every TC Step that changes UI must call `captureStepEvidence()` from `Shared/evidence-helper.ts` (waits for loading to finish, opens popups/modals via click before capturing, scrolls to the confirm area, red-outline highlights the item to confirm) and every assertion must check the actual Expected Result content — not just element presence. See `script-sync` SKILL.md "Assertion Rules" / "Evidence Capture per Step".
|
|
519
521
|
|
|
520
522
|
- **INVOKE** `gate-review` skill (generate mode) — write `.aiflow/review/gate-2-[ticket-id].md`
|
|
521
523
|
- Display gate pause message from `gate-review` skill — wait for **APPROVED**
|
|
@@ -543,17 +545,22 @@ Chạy [Pre-flight — Đồng bộ Source & Docs](#pre-flight-bắt-buộc--đ
|
|
|
543
545
|
|
|
544
546
|
**Step 3.1 — Determine run number**
|
|
545
547
|
|
|
546
|
-
Check `
|
|
548
|
+
Check `AK-Docs/03.Testing/04.Evidence/{repo}/{featureDir}/` — next run is `run-{N+1}`.
|
|
547
549
|
|
|
548
|
-
**Step 3.2 — Execute Playwright**
|
|
550
|
+
**Step 3.2 — Execute Playwright** (script and evidence both live inside AK-Docs; evidence stays out of Git via `.gitignore`, not via a separate working dir):
|
|
549
551
|
|
|
550
552
|
```bash
|
|
551
|
-
|
|
553
|
+
cd AK-Docs/03.Testing/05.Scripts
|
|
554
|
+
EVIDENCE_DIR="$(pwd)/../04.Evidence/{repo}/{featureDir}/run-{N}" \
|
|
555
|
+
BASE_URL=<url> \
|
|
556
|
+
npx playwright test --config={repo}/playwright.config.ts {repo}/{featureDir}/{ScreenID}.spec.ts
|
|
552
557
|
```
|
|
553
558
|
|
|
554
|
-
**Step 3.3 — Organize evidence
|
|
555
|
-
-
|
|
556
|
-
-
|
|
559
|
+
**Step 3.3 — Organize evidence + report:**
|
|
560
|
+
- Evidence (already written directly to `AK-Docs/03.Testing/04.Evidence/{repo}/{featureDir}/run-{N}/{TC_ID}-{scenario}/`, gitignored): `step-NN-{desc}.png` per Step, `trace.zip`
|
|
561
|
+
- Report (`AK-Docs/03.Testing/02.Reports/{repo}/{featureDir}/run-{N}/{TC_ID}-{scenario}/result.md`) — text only, links back to the `04.Evidence/` path
|
|
562
|
+
- Before recording PASS/FAIL, re-check that the assertion actually matches the TC's Expected Result content (not just "did not throw") — see `execute-flow` SKILL.md "DOM Verification Rules"
|
|
563
|
+
- Auto-draft `AK-Docs/03.Testing/06.Bugs/{repo}/{featureDir}/run-{N}/BUG-NNN-{slug}.md` for failed TCs
|
|
557
564
|
|
|
558
565
|
**Step 3.4 — Update TC file** R1/R2 columns:
|
|
559
566
|
- `✅ Pass` · `❌ Fail` · `⏭️ Untest` · `⏳ Pending`
|
|
@@ -592,7 +599,7 @@ Only runs after Gate 3 APPROVED.
|
|
|
592
599
|
|
|
593
600
|
Chạy [Pre-flight — Đồng bộ Source & Docs](#pre-flight-bắt-buộc--đồng-bộ-source--docs-đầu-mỗi-gate) ở đầu file. Lỗi → hiển thị ⚠️ cảnh báo, không dừng gate.
|
|
594
601
|
|
|
595
|
-
**Step 4.1** — Generate `
|
|
602
|
+
**Step 4.1** — Generate `AK-Docs/03.Testing/02.Reports/{repo}/{featureDir}/run-{N}/testreport.md`:
|
|
596
603
|
- Per-module rows with Pass/Fail/Untest/Pending counts, R1/R2 columns, % completion
|
|
597
604
|
- Go/No-Go: 0 unresolved Critical = Go
|
|
598
605
|
|
|
@@ -608,7 +615,7 @@ Wait for reply before moving to next bug.
|
|
|
608
615
|
⏸️ GATE 4: REPORT READY
|
|
609
616
|
Decision: [GO / NO-GO]
|
|
610
617
|
Bugs logged: [N] | Skipped: [N]
|
|
611
|
-
→ Review: [
|
|
618
|
+
→ Review: [AK-Docs/03.Testing/02.Reports/{repo}/{featureDir}/run-{N}/testreport.md](AK-Docs/03.Testing/02.Reports/{repo}/{featureDir}/run-{N}/testreport.md)
|
|
612
619
|
→ Type APPROVED to sign off
|
|
613
620
|
```
|
|
614
621
|
|
|
@@ -59,6 +59,7 @@ DO NOT just check format — **understand the data and propose solutions**.
|
|
|
59
59
|
Only runs after Gate 2 has been APPROVED.
|
|
60
60
|
|
|
61
61
|
**INVOKE:** `train-model` skill
|
|
62
|
+
- **Framework rules (bắt buộc, đọc trước dòng code đầu tiên):** nếu chưa đọc trong session này, đọc đầy đủ file(s) được trỏ ở dòng "> **Rules & code examples:**" đầu CLAUDE.md/AGENTS.md (`.rules/<lang>/<framework>-rules.md` + `-examples.md`). Áp dụng cho MỌI lần sửa code trong Gate này, không chỉ lần đầu.
|
|
62
63
|
- For algorithm-optimization tickets EXTRA: `improve-algorithm` skill
|
|
63
64
|
|
|
64
65
|
**Eval-harness-first rule:** Write the metric/eval harness and establish a reproducible baseline BEFORE iterating on the model. Confirm the baseline runs and is logged — then proceed with tracked experiments.
|