red64-cli 0.1.0 → 0.3.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 (125) hide show
  1. package/README.md +1 -2
  2. package/dist/cli/parseArgs.d.ts.map +1 -1
  3. package/dist/cli/parseArgs.js +5 -0
  4. package/dist/cli/parseArgs.js.map +1 -1
  5. package/dist/components/init/CompleteStep.d.ts.map +1 -1
  6. package/dist/components/init/CompleteStep.js +2 -2
  7. package/dist/components/init/CompleteStep.js.map +1 -1
  8. package/dist/components/init/TestCheckStep.d.ts +16 -0
  9. package/dist/components/init/TestCheckStep.d.ts.map +1 -0
  10. package/dist/components/init/TestCheckStep.js +120 -0
  11. package/dist/components/init/TestCheckStep.js.map +1 -0
  12. package/dist/components/init/index.d.ts +1 -0
  13. package/dist/components/init/index.d.ts.map +1 -1
  14. package/dist/components/init/index.js +1 -0
  15. package/dist/components/init/index.js.map +1 -1
  16. package/dist/components/init/types.d.ts +9 -0
  17. package/dist/components/init/types.d.ts.map +1 -1
  18. package/dist/components/screens/InitScreen.d.ts.map +1 -1
  19. package/dist/components/screens/InitScreen.js +69 -6
  20. package/dist/components/screens/InitScreen.js.map +1 -1
  21. package/dist/components/screens/ListScreen.d.ts.map +1 -1
  22. package/dist/components/screens/ListScreen.js +28 -3
  23. package/dist/components/screens/ListScreen.js.map +1 -1
  24. package/dist/components/screens/StartScreen.d.ts.map +1 -1
  25. package/dist/components/screens/StartScreen.js +212 -13
  26. package/dist/components/screens/StartScreen.js.map +1 -1
  27. package/dist/components/ui/ArtifactsSidebar.d.ts +19 -0
  28. package/dist/components/ui/ArtifactsSidebar.d.ts.map +1 -0
  29. package/dist/components/ui/ArtifactsSidebar.js +51 -0
  30. package/dist/components/ui/ArtifactsSidebar.js.map +1 -0
  31. package/dist/components/ui/FeatureSidebar.d.ts.map +1 -1
  32. package/dist/components/ui/FeatureSidebar.js +1 -1
  33. package/dist/components/ui/FeatureSidebar.js.map +1 -1
  34. package/dist/components/ui/index.d.ts +1 -0
  35. package/dist/components/ui/index.d.ts.map +1 -1
  36. package/dist/components/ui/index.js +1 -0
  37. package/dist/components/ui/index.js.map +1 -1
  38. package/dist/services/ClaudeErrorDetector.js +3 -3
  39. package/dist/services/ClaudeErrorDetector.js.map +1 -1
  40. package/dist/services/ConfigService.d.ts +1 -0
  41. package/dist/services/ConfigService.d.ts.map +1 -1
  42. package/dist/services/ConfigService.js.map +1 -1
  43. package/dist/services/ProjectDetector.d.ts +28 -0
  44. package/dist/services/ProjectDetector.d.ts.map +1 -0
  45. package/dist/services/ProjectDetector.js +236 -0
  46. package/dist/services/ProjectDetector.js.map +1 -0
  47. package/dist/services/TestRunner.d.ts +46 -0
  48. package/dist/services/TestRunner.d.ts.map +1 -0
  49. package/dist/services/TestRunner.js +85 -0
  50. package/dist/services/TestRunner.js.map +1 -0
  51. package/dist/services/index.d.ts +2 -0
  52. package/dist/services/index.d.ts.map +1 -1
  53. package/dist/services/index.js +2 -0
  54. package/dist/services/index.js.map +1 -1
  55. package/dist/types/index.d.ts +13 -0
  56. package/dist/types/index.d.ts.map +1 -1
  57. package/dist/types/index.js.map +1 -1
  58. package/framework/.red64/settings/templates/specs/gap-analysis.md +163 -0
  59. package/framework/agents/claude/.claude/agents/red64/spec-impl.md +131 -2
  60. package/framework/agents/claude/.claude/agents/red64/validate-gap.md +13 -7
  61. package/framework/agents/claude/.claude/commands/red64/spec-impl.md +24 -0
  62. package/framework/agents/claude/.claude/commands/red64/validate-gap.md +4 -0
  63. package/framework/agents/codex/.codex/agents/red64/spec-impl.md +131 -2
  64. package/framework/agents/codex/.codex/agents/red64/validate-gap.md +13 -7
  65. package/framework/agents/codex/.codex/commands/red64/spec-impl.md +24 -0
  66. package/framework/agents/codex/.codex/commands/red64/validate-gap.md +4 -0
  67. package/framework/stacks/generic/feedback.md +80 -0
  68. package/framework/stacks/nextjs/accessibility.md +437 -0
  69. package/framework/stacks/nextjs/api.md +431 -0
  70. package/framework/stacks/nextjs/coding-style.md +282 -0
  71. package/framework/stacks/nextjs/commenting.md +226 -0
  72. package/framework/stacks/nextjs/components.md +411 -0
  73. package/framework/stacks/nextjs/conventions.md +333 -0
  74. package/framework/stacks/nextjs/css.md +310 -0
  75. package/framework/stacks/nextjs/error-handling.md +442 -0
  76. package/framework/stacks/nextjs/feedback.md +124 -0
  77. package/framework/stacks/nextjs/migrations.md +332 -0
  78. package/framework/stacks/nextjs/models.md +362 -0
  79. package/framework/stacks/nextjs/queries.md +410 -0
  80. package/framework/stacks/nextjs/responsive.md +338 -0
  81. package/framework/stacks/nextjs/tech-stack.md +177 -0
  82. package/framework/stacks/nextjs/test-writing.md +475 -0
  83. package/framework/stacks/nextjs/validation.md +467 -0
  84. package/framework/stacks/python/api.md +468 -0
  85. package/framework/stacks/python/authentication.md +342 -0
  86. package/framework/stacks/python/code-quality.md +283 -0
  87. package/framework/stacks/python/code-refactoring.md +315 -0
  88. package/framework/stacks/python/coding-style.md +462 -0
  89. package/framework/stacks/python/conventions.md +399 -0
  90. package/framework/stacks/python/error-handling.md +512 -0
  91. package/framework/stacks/python/feedback.md +92 -0
  92. package/framework/stacks/python/implement-ai-llm.md +468 -0
  93. package/framework/stacks/python/migrations.md +388 -0
  94. package/framework/stacks/python/models.md +399 -0
  95. package/framework/stacks/python/python.md +232 -0
  96. package/framework/stacks/python/queries.md +451 -0
  97. package/framework/stacks/python/structure.md +245 -58
  98. package/framework/stacks/python/tech.md +92 -35
  99. package/framework/stacks/python/testing.md +380 -0
  100. package/framework/stacks/python/validation.md +471 -0
  101. package/framework/stacks/rails/authentication.md +176 -0
  102. package/framework/stacks/rails/code-quality.md +287 -0
  103. package/framework/stacks/rails/code-refactoring.md +299 -0
  104. package/framework/stacks/rails/feedback.md +130 -0
  105. package/framework/stacks/rails/implement-ai-llm-with-rubyllm.md +342 -0
  106. package/framework/stacks/rails/rails.md +301 -0
  107. package/framework/stacks/rails/rails8-best-practices.md +498 -0
  108. package/framework/stacks/rails/rails8-css.md +573 -0
  109. package/framework/stacks/rails/structure.md +140 -0
  110. package/framework/stacks/rails/tech.md +108 -0
  111. package/framework/stacks/react/code-quality.md +521 -0
  112. package/framework/stacks/react/components.md +625 -0
  113. package/framework/stacks/react/data-fetching.md +586 -0
  114. package/framework/stacks/react/feedback.md +110 -0
  115. package/framework/stacks/react/forms.md +694 -0
  116. package/framework/stacks/react/performance.md +640 -0
  117. package/framework/stacks/react/product.md +22 -9
  118. package/framework/stacks/react/state-management.md +472 -0
  119. package/framework/stacks/react/structure.md +351 -44
  120. package/framework/stacks/react/tech.md +219 -30
  121. package/framework/stacks/react/testing.md +690 -0
  122. package/package.json +1 -1
  123. package/framework/stacks/node/product.md +0 -27
  124. package/framework/stacks/node/structure.md +0 -82
  125. package/framework/stacks/node/tech.md +0 -63
@@ -0,0 +1,586 @@
1
+ # Data Fetching Patterns
2
+
3
+ Server state management with TanStack Query for caching, synchronization, and background updates.
4
+
5
+ ---
6
+
7
+ ## Philosophy
8
+
9
+ - **Server state is different**: API data needs caching, deduplication, background refresh
10
+ - **Cache-first**: Serve stale data immediately, refresh in background
11
+ - **Optimistic updates**: Update UI before server confirms for better UX
12
+ - **Error boundaries**: Handle errors at the right level, not in every component
13
+
14
+ ---
15
+
16
+ ## TanStack Query Setup
17
+
18
+ ### Query Client Configuration
19
+
20
+ ```typescript
21
+ // lib/query-client.ts
22
+ import { QueryClient } from '@tanstack/react-query';
23
+
24
+ export const queryClient = new QueryClient({
25
+ defaultOptions: {
26
+ queries: {
27
+ staleTime: 1000 * 60 * 5, // 5 minutes
28
+ gcTime: 1000 * 60 * 30, // 30 minutes (formerly cacheTime)
29
+ retry: 3,
30
+ retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000),
31
+ refetchOnWindowFocus: true,
32
+ refetchOnReconnect: true,
33
+ },
34
+ mutations: {
35
+ retry: 1,
36
+ },
37
+ },
38
+ });
39
+ ```
40
+
41
+ ### Provider Setup
42
+
43
+ ```typescript
44
+ // app/App.tsx
45
+ import { QueryClientProvider } from '@tanstack/react-query';
46
+ import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
47
+ import { queryClient } from '@/lib/query-client';
48
+
49
+ export function App() {
50
+ return (
51
+ <QueryClientProvider client={queryClient}>
52
+ <RouterProvider router={router} />
53
+ <ReactQueryDevtools initialIsOpen={false} />
54
+ </QueryClientProvider>
55
+ );
56
+ }
57
+ ```
58
+
59
+ ---
60
+
61
+ ## Query Patterns
62
+
63
+ ### Basic Query
64
+
65
+ ```typescript
66
+ // features/users/hooks/useUsers.ts
67
+ import { useQuery } from '@tanstack/react-query';
68
+ import { api } from '@/services/api';
69
+ import type { User, PaginatedResponse } from '@/types';
70
+
71
+ // Query key factory
72
+ export const userKeys = {
73
+ all: ['users'] as const,
74
+ lists: () => [...userKeys.all, 'list'] as const,
75
+ list: (filters: UserFilters) => [...userKeys.lists(), filters] as const,
76
+ details: () => [...userKeys.all, 'detail'] as const,
77
+ detail: (id: number) => [...userKeys.details(), id] as const,
78
+ };
79
+
80
+ interface UserFilters {
81
+ page?: number;
82
+ search?: string;
83
+ role?: string;
84
+ }
85
+
86
+ async function fetchUsers(filters: UserFilters): Promise<PaginatedResponse<User>> {
87
+ const params = new URLSearchParams();
88
+ if (filters.page) params.set('page', String(filters.page));
89
+ if (filters.search) params.set('q', filters.search);
90
+ if (filters.role) params.set('role', filters.role);
91
+
92
+ return api.get(`users?${params}`).json();
93
+ }
94
+
95
+ export function useUsers(filters: UserFilters = {}) {
96
+ return useQuery({
97
+ queryKey: userKeys.list(filters),
98
+ queryFn: () => fetchUsers(filters),
99
+ });
100
+ }
101
+ ```
102
+
103
+ ### Single Resource Query
104
+
105
+ ```typescript
106
+ // features/users/hooks/useUser.ts
107
+ async function fetchUser(id: number): Promise<User> {
108
+ return api.get(`users/${id}`).json();
109
+ }
110
+
111
+ export function useUser(id: number) {
112
+ return useQuery({
113
+ queryKey: userKeys.detail(id),
114
+ queryFn: () => fetchUser(id),
115
+ enabled: id > 0, // Don't fetch if no ID
116
+ });
117
+ }
118
+ ```
119
+
120
+ ### Dependent Queries
121
+
122
+ ```typescript
123
+ // Fetch user, then fetch their posts
124
+ function useUserWithPosts(userId: number) {
125
+ const userQuery = useUser(userId);
126
+
127
+ const postsQuery = useQuery({
128
+ queryKey: ['users', userId, 'posts'],
129
+ queryFn: () => fetchUserPosts(userId),
130
+ enabled: !!userQuery.data, // Only fetch when user is loaded
131
+ });
132
+
133
+ return { user: userQuery, posts: postsQuery };
134
+ }
135
+ ```
136
+
137
+ ### Parallel Queries
138
+
139
+ ```typescript
140
+ import { useQueries } from '@tanstack/react-query';
141
+
142
+ function useUserDetails(userIds: number[]) {
143
+ return useQueries({
144
+ queries: userIds.map((id) => ({
145
+ queryKey: userKeys.detail(id),
146
+ queryFn: () => fetchUser(id),
147
+ })),
148
+ });
149
+ }
150
+ ```
151
+
152
+ ---
153
+
154
+ ## Mutation Patterns
155
+
156
+ ### Basic Mutation
157
+
158
+ ```typescript
159
+ // features/users/hooks/useCreateUser.ts
160
+ import { useMutation, useQueryClient } from '@tanstack/react-query';
161
+
162
+ interface CreateUserInput {
163
+ name: string;
164
+ email: string;
165
+ role: string;
166
+ }
167
+
168
+ async function createUser(input: CreateUserInput): Promise<User> {
169
+ return api.post('users', { json: input }).json();
170
+ }
171
+
172
+ export function useCreateUser() {
173
+ const queryClient = useQueryClient();
174
+
175
+ return useMutation({
176
+ mutationFn: createUser,
177
+ onSuccess: () => {
178
+ // Invalidate and refetch user list
179
+ queryClient.invalidateQueries({ queryKey: userKeys.lists() });
180
+ },
181
+ });
182
+ }
183
+ ```
184
+
185
+ ### Update Mutation with Optimistic Updates
186
+
187
+ ```typescript
188
+ // features/users/hooks/useUpdateUser.ts
189
+ interface UpdateUserInput {
190
+ id: number;
191
+ data: Partial<User>;
192
+ }
193
+
194
+ async function updateUser({ id, data }: UpdateUserInput): Promise<User> {
195
+ return api.patch(`users/${id}`, { json: data }).json();
196
+ }
197
+
198
+ export function useUpdateUser() {
199
+ const queryClient = useQueryClient();
200
+
201
+ return useMutation({
202
+ mutationFn: updateUser,
203
+ // Optimistic update
204
+ onMutate: async ({ id, data }) => {
205
+ // Cancel outgoing refetches
206
+ await queryClient.cancelQueries({ queryKey: userKeys.detail(id) });
207
+
208
+ // Snapshot previous value
209
+ const previousUser = queryClient.getQueryData<User>(userKeys.detail(id));
210
+
211
+ // Optimistically update
212
+ if (previousUser) {
213
+ queryClient.setQueryData<User>(userKeys.detail(id), {
214
+ ...previousUser,
215
+ ...data,
216
+ });
217
+ }
218
+
219
+ return { previousUser };
220
+ },
221
+ onError: (_err, { id }, context) => {
222
+ // Rollback on error
223
+ if (context?.previousUser) {
224
+ queryClient.setQueryData(userKeys.detail(id), context.previousUser);
225
+ }
226
+ },
227
+ onSettled: (_data, _error, { id }) => {
228
+ // Refetch to ensure server state
229
+ queryClient.invalidateQueries({ queryKey: userKeys.detail(id) });
230
+ queryClient.invalidateQueries({ queryKey: userKeys.lists() });
231
+ },
232
+ });
233
+ }
234
+ ```
235
+
236
+ ### Delete Mutation
237
+
238
+ ```typescript
239
+ export function useDeleteUser() {
240
+ const queryClient = useQueryClient();
241
+
242
+ return useMutation({
243
+ mutationFn: (id: number) => api.delete(`users/${id}`),
244
+ onSuccess: () => {
245
+ queryClient.invalidateQueries({ queryKey: userKeys.lists() });
246
+ },
247
+ });
248
+ }
249
+ ```
250
+
251
+ ---
252
+
253
+ ## Usage in Components
254
+
255
+ ### Query Component
256
+
257
+ ```typescript
258
+ // features/users/components/UserList.tsx
259
+ import { useUsers } from '../hooks/useUsers';
260
+
261
+ export function UserList({ filters }: { filters: UserFilters }) {
262
+ const { data, isLoading, isError, error, refetch } = useUsers(filters);
263
+
264
+ if (isLoading) {
265
+ return <LoadingSpinner />;
266
+ }
267
+
268
+ if (isError) {
269
+ return (
270
+ <ErrorMessage
271
+ message={error.message}
272
+ onRetry={() => refetch()}
273
+ />
274
+ );
275
+ }
276
+
277
+ if (!data?.items.length) {
278
+ return <EmptyState message="No users found" />;
279
+ }
280
+
281
+ return (
282
+ <ul>
283
+ {data.items.map((user) => (
284
+ <UserCard key={user.id} user={user} />
285
+ ))}
286
+ </ul>
287
+ );
288
+ }
289
+ ```
290
+
291
+ ### Mutation Component
292
+
293
+ ```typescript
294
+ // features/users/components/CreateUserForm.tsx
295
+ import { useCreateUser } from '../hooks/useCreateUser';
296
+
297
+ export function CreateUserForm({ onSuccess }: { onSuccess?: () => void }) {
298
+ const createUser = useCreateUser();
299
+
300
+ const handleSubmit = (data: CreateUserInput) => {
301
+ createUser.mutate(data, {
302
+ onSuccess: () => {
303
+ toast.success('User created');
304
+ onSuccess?.();
305
+ },
306
+ onError: (error) => {
307
+ toast.error(error.message);
308
+ },
309
+ });
310
+ };
311
+
312
+ return (
313
+ <form onSubmit={form.handleSubmit(handleSubmit)}>
314
+ {/* Form fields */}
315
+ <Button type="submit" disabled={createUser.isPending}>
316
+ {createUser.isPending ? 'Creating...' : 'Create User'}
317
+ </Button>
318
+ </form>
319
+ );
320
+ }
321
+ ```
322
+
323
+ ---
324
+
325
+ ## API Client Setup
326
+
327
+ ### ky Configuration
328
+
329
+ ```typescript
330
+ // services/api.ts
331
+ import ky from 'ky';
332
+ import { useAuthStore } from '@/stores/auth.store';
333
+
334
+ export const api = ky.create({
335
+ prefixUrl: import.meta.env.VITE_API_URL,
336
+ timeout: 30000,
337
+ hooks: {
338
+ beforeRequest: [
339
+ (request) => {
340
+ const token = useAuthStore.getState().token;
341
+ if (token) {
342
+ request.headers.set('Authorization', `Bearer ${token}`);
343
+ }
344
+ },
345
+ ],
346
+ afterResponse: [
347
+ async (_request, _options, response) => {
348
+ if (response.status === 401) {
349
+ useAuthStore.getState().clearAuth();
350
+ window.location.href = '/login';
351
+ }
352
+ return response;
353
+ },
354
+ ],
355
+ },
356
+ });
357
+ ```
358
+
359
+ ### Type-safe API with Zod
360
+
361
+ ```typescript
362
+ // features/users/api/users.api.ts
363
+ import { z } from 'zod';
364
+ import { api } from '@/services/api';
365
+
366
+ const UserSchema = z.object({
367
+ id: z.number(),
368
+ name: z.string(),
369
+ email: z.string().email(),
370
+ role: z.enum(['admin', 'user', 'guest']),
371
+ createdAt: z.string().datetime(),
372
+ });
373
+
374
+ const PaginatedUsersSchema = z.object({
375
+ items: z.array(UserSchema),
376
+ total: z.number(),
377
+ page: z.number(),
378
+ perPage: z.number(),
379
+ hasNext: z.boolean(),
380
+ });
381
+
382
+ export type User = z.infer<typeof UserSchema>;
383
+ export type PaginatedUsers = z.infer<typeof PaginatedUsersSchema>;
384
+
385
+ export async function fetchUsers(filters: UserFilters): Promise<PaginatedUsers> {
386
+ const response = await api.get('users', { searchParams: filters }).json();
387
+ return PaginatedUsersSchema.parse(response); // Runtime validation
388
+ }
389
+ ```
390
+
391
+ ---
392
+
393
+ ## Prefetching and Preloading
394
+
395
+ ### Prefetch on Hover
396
+
397
+ ```typescript
398
+ function UserListItem({ user }: { user: User }) {
399
+ const queryClient = useQueryClient();
400
+
401
+ const prefetchUser = () => {
402
+ queryClient.prefetchQuery({
403
+ queryKey: userKeys.detail(user.id),
404
+ queryFn: () => fetchUser(user.id),
405
+ staleTime: 1000 * 60 * 5, // 5 minutes
406
+ });
407
+ };
408
+
409
+ return (
410
+ <Link
411
+ to={`/users/${user.id}`}
412
+ onMouseEnter={prefetchUser}
413
+ onFocus={prefetchUser}
414
+ >
415
+ {user.name}
416
+ </Link>
417
+ );
418
+ }
419
+ ```
420
+
421
+ ### Prefetch on Route Load
422
+
423
+ ```typescript
424
+ // app/routes.tsx
425
+ import { queryClient } from '@/lib/query-client';
426
+
427
+ export const router = createBrowserRouter([
428
+ {
429
+ path: '/users/:id',
430
+ element: <UserDetail />,
431
+ loader: async ({ params }) => {
432
+ const id = Number(params.id);
433
+ // Prefetch data before rendering
434
+ await queryClient.ensureQueryData({
435
+ queryKey: userKeys.detail(id),
436
+ queryFn: () => fetchUser(id),
437
+ });
438
+ return null;
439
+ },
440
+ },
441
+ ]);
442
+ ```
443
+
444
+ ---
445
+
446
+ ## Infinite Queries (Pagination)
447
+
448
+ ```typescript
449
+ // features/posts/hooks/usePosts.ts
450
+ import { useInfiniteQuery } from '@tanstack/react-query';
451
+
452
+ export function usePosts() {
453
+ return useInfiniteQuery({
454
+ queryKey: ['posts'],
455
+ queryFn: ({ pageParam }) => fetchPosts({ cursor: pageParam }),
456
+ initialPageParam: undefined as string | undefined,
457
+ getNextPageParam: (lastPage) => lastPage.nextCursor,
458
+ getPreviousPageParam: (firstPage) => firstPage.prevCursor,
459
+ });
460
+ }
461
+
462
+ // Usage
463
+ function PostList() {
464
+ const {
465
+ data,
466
+ fetchNextPage,
467
+ hasNextPage,
468
+ isFetchingNextPage,
469
+ } = usePosts();
470
+
471
+ return (
472
+ <>
473
+ {data?.pages.map((page) =>
474
+ page.items.map((post) => <PostCard key={post.id} post={post} />)
475
+ )}
476
+ {hasNextPage && (
477
+ <Button
478
+ onClick={() => fetchNextPage()}
479
+ disabled={isFetchingNextPage}
480
+ >
481
+ {isFetchingNextPage ? 'Loading...' : 'Load More'}
482
+ </Button>
483
+ )}
484
+ </>
485
+ );
486
+ }
487
+ ```
488
+
489
+ ---
490
+
491
+ ## Error Handling
492
+
493
+ ### Query Error Boundary
494
+
495
+ ```typescript
496
+ // components/QueryErrorBoundary.tsx
497
+ import { QueryErrorResetBoundary } from '@tanstack/react-query';
498
+ import { ErrorBoundary } from 'react-error-boundary';
499
+
500
+ export function QueryErrorBoundary({ children }: { children: React.ReactNode }) {
501
+ return (
502
+ <QueryErrorResetBoundary>
503
+ {({ reset }) => (
504
+ <ErrorBoundary
505
+ onReset={reset}
506
+ fallbackRender={({ error, resetErrorBoundary }) => (
507
+ <div className="error-container">
508
+ <p>Something went wrong: {error.message}</p>
509
+ <Button onClick={resetErrorBoundary}>Try again</Button>
510
+ </div>
511
+ )}
512
+ >
513
+ {children}
514
+ </ErrorBoundary>
515
+ )}
516
+ </QueryErrorResetBoundary>
517
+ );
518
+ }
519
+ ```
520
+
521
+ ### Global Error Handling
522
+
523
+ ```typescript
524
+ // lib/query-client.ts
525
+ export const queryClient = new QueryClient({
526
+ defaultOptions: {
527
+ queries: {
528
+ throwOnError: (error) => {
529
+ // Only throw for specific errors to be caught by boundary
530
+ return error instanceof NetworkError;
531
+ },
532
+ },
533
+ mutations: {
534
+ onError: (error) => {
535
+ // Global mutation error handling
536
+ toast.error(error.message);
537
+ },
538
+ },
539
+ },
540
+ });
541
+ ```
542
+
543
+ ---
544
+
545
+ ## Suspense Mode
546
+
547
+ ```typescript
548
+ // Enable suspense for a query
549
+ export function useUser(id: number) {
550
+ return useSuspenseQuery({
551
+ queryKey: userKeys.detail(id),
552
+ queryFn: () => fetchUser(id),
553
+ });
554
+ }
555
+
556
+ // Usage with Suspense boundary
557
+ function UserPage({ userId }: { userId: number }) {
558
+ return (
559
+ <Suspense fallback={<LoadingSpinner />}>
560
+ <UserDetail userId={userId} />
561
+ </Suspense>
562
+ );
563
+ }
564
+
565
+ function UserDetail({ userId }: { userId: number }) {
566
+ const { data: user } = useUser(userId); // Never undefined with suspense
567
+ return <div>{user.name}</div>;
568
+ }
569
+ ```
570
+
571
+ ---
572
+
573
+ ## Anti-Patterns
574
+
575
+ | Anti-Pattern | Problem | Correct Approach |
576
+ |--------------|---------|------------------|
577
+ | Storing query data in Zustand | Double source of truth | Let TanStack Query manage server state |
578
+ | No query keys | Cache conflicts | Use query key factories |
579
+ | Fetching in useEffect | Missing caching, deduplication | Use useQuery |
580
+ | Manual refetch everywhere | Stale data | Configure staleTime, use invalidation |
581
+ | Not handling loading/error | Bad UX | Always handle all states |
582
+ | Inline query functions | No reuse, hard to test | Extract to separate files |
583
+
584
+ ---
585
+
586
+ _Server state is not your state. TanStack Query manages it better than you can._
@@ -0,0 +1,110 @@
1
+ # Feedback Configuration
2
+
3
+ Project-specific commands for automated feedback during React implementation.
4
+
5
+ ---
6
+
7
+ ## Test Commands
8
+
9
+ Commands to run tests during implementation. The agent will use these to verify code changes.
10
+
11
+ ```yaml
12
+ # Primary test command (REQUIRED)
13
+ test: pnpm test:run
14
+
15
+ # Test with coverage report
16
+ test_coverage: pnpm test:coverage
17
+
18
+ # Run specific test file (use {file} as placeholder)
19
+ test_file: pnpm test:run {file}
20
+
21
+ # Watch mode (for development)
22
+ test_watch: pnpm test
23
+ ```
24
+
25
+ ---
26
+
27
+ ## Linting Commands
28
+
29
+ Commands for code quality checks.
30
+
31
+ ```yaml
32
+ # Primary lint command
33
+ lint: pnpm lint
34
+
35
+ # Lint with auto-fix
36
+ lint_fix: pnpm lint --fix
37
+
38
+ # Type checking
39
+ type_check: pnpm tsc --noEmit
40
+
41
+ # Format check
42
+ format_check: pnpm prettier --check .
43
+
44
+ # Format fix
45
+ format_fix: pnpm prettier --write .
46
+ ```
47
+
48
+ ---
49
+
50
+ ## Development Server
51
+
52
+ Commands for starting the development server (required for UI verification).
53
+
54
+ ```yaml
55
+ # Start dev server (Vite)
56
+ dev_server: pnpm dev
57
+
58
+ # Dev server port
59
+ dev_port: 5173
60
+
61
+ # Dev server base URL
62
+ dev_url: http://localhost:5173
63
+ ```
64
+
65
+ ---
66
+
67
+ ## UI Verification
68
+
69
+ Settings for agent-browser UI verification.
70
+
71
+ ```yaml
72
+ # Enable UI verification for this project
73
+ ui_verification_enabled: true
74
+
75
+ # Default wait time after navigation (milliseconds)
76
+ navigation_wait: 3000
77
+
78
+ # Screenshot directory
79
+ screenshot_dir: /tmp/ui-captures
80
+
81
+ # Storybook URL (if using Storybook)
82
+ storybook_url: http://localhost:6006
83
+ ```
84
+
85
+ ---
86
+
87
+ ## E2E Testing
88
+
89
+ End-to-end testing with Playwright.
90
+
91
+ ```yaml
92
+ # Run E2E tests
93
+ e2e: pnpm test:e2e
94
+
95
+ # Run E2E with UI
96
+ e2e_ui: pnpm test:e2e --ui
97
+
98
+ # Run E2E for specific browser
99
+ e2e_chromium: pnpm test:e2e --project=chromium
100
+ ```
101
+
102
+ ---
103
+
104
+ ## Notes
105
+
106
+ - Uses Vitest for unit/integration tests (faster than Jest)
107
+ - Uses Playwright for E2E testing
108
+ - pnpm is the default package manager (faster, disk-efficient)
109
+ - Vite dev server on port 5173 by default
110
+ - UI verification is enabled by default for React projects