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,472 @@
1
+ # State Management Patterns
2
+
3
+ Modern state management for React applications using Zustand for client state and TanStack Query for server state.
4
+
5
+ ---
6
+
7
+ ## Philosophy
8
+
9
+ - **Separate server and client state**: Server state (API data) and client state (UI state) have different needs
10
+ - **Minimal global state**: Keep state local when possible; lift only when necessary
11
+ - **Single source of truth**: Each piece of state lives in exactly one place
12
+ - **Derived state over stored state**: Compute values from existing state rather than storing duplicates
13
+
14
+ ---
15
+
16
+ ## State Categories
17
+
18
+ | Category | Definition | Tool | Examples |
19
+ |----------|------------|------|----------|
20
+ | **Server State** | Data from external sources | TanStack Query | Users list, product data, API responses |
21
+ | **Client State** | UI and application state | Zustand | Sidebar open, theme, shopping cart |
22
+ | **Local State** | Component-specific state | useState | Form inputs, toggles, modals |
23
+ | **URL State** | Route and search params | React Router | Current page, filters, search query |
24
+
25
+ ---
26
+
27
+ ## Zustand for Client State
28
+
29
+ ### Basic Store
30
+
31
+ ```typescript
32
+ // stores/ui.store.ts
33
+ import { create } from 'zustand';
34
+
35
+ interface UIState {
36
+ sidebarOpen: boolean;
37
+ theme: 'light' | 'dark' | 'system';
38
+ toggleSidebar: () => void;
39
+ setTheme: (theme: 'light' | 'dark' | 'system') => void;
40
+ }
41
+
42
+ export const useUIStore = create<UIState>((set) => ({
43
+ sidebarOpen: true,
44
+ theme: 'system',
45
+ toggleSidebar: () => set((state) => ({ sidebarOpen: !state.sidebarOpen })),
46
+ setTheme: (theme) => set({ theme }),
47
+ }));
48
+ ```
49
+
50
+ ### Store with Persistence
51
+
52
+ ```typescript
53
+ // stores/auth.store.ts
54
+ import { create } from 'zustand';
55
+ import { persist, createJSONStorage } from 'zustand/middleware';
56
+
57
+ interface AuthState {
58
+ token: string | null;
59
+ user: User | null;
60
+ setAuth: (token: string, user: User) => void;
61
+ clearAuth: () => void;
62
+ isAuthenticated: () => boolean;
63
+ }
64
+
65
+ export const useAuthStore = create<AuthState>()(
66
+ persist(
67
+ (set, get) => ({
68
+ token: null,
69
+ user: null,
70
+ setAuth: (token, user) => set({ token, user }),
71
+ clearAuth: () => set({ token: null, user: null }),
72
+ isAuthenticated: () => get().token !== null,
73
+ }),
74
+ {
75
+ name: 'auth-storage',
76
+ storage: createJSONStorage(() => localStorage),
77
+ partialize: (state) => ({ token: state.token, user: state.user }), // Only persist these fields
78
+ }
79
+ )
80
+ );
81
+ ```
82
+
83
+ ### Store with Immer (Complex Updates)
84
+
85
+ ```typescript
86
+ // stores/cart.store.ts
87
+ import { create } from 'zustand';
88
+ import { immer } from 'zustand/middleware/immer';
89
+
90
+ interface CartItem {
91
+ id: string;
92
+ name: string;
93
+ price: number;
94
+ quantity: number;
95
+ }
96
+
97
+ interface CartState {
98
+ items: CartItem[];
99
+ addItem: (item: Omit<CartItem, 'quantity'>) => void;
100
+ removeItem: (id: string) => void;
101
+ updateQuantity: (id: string, quantity: number) => void;
102
+ clearCart: () => void;
103
+ total: () => number;
104
+ }
105
+
106
+ export const useCartStore = create<CartState>()(
107
+ immer((set, get) => ({
108
+ items: [],
109
+
110
+ addItem: (item) =>
111
+ set((state) => {
112
+ const existing = state.items.find((i) => i.id === item.id);
113
+ if (existing) {
114
+ existing.quantity += 1;
115
+ } else {
116
+ state.items.push({ ...item, quantity: 1 });
117
+ }
118
+ }),
119
+
120
+ removeItem: (id) =>
121
+ set((state) => {
122
+ state.items = state.items.filter((i) => i.id !== id);
123
+ }),
124
+
125
+ updateQuantity: (id, quantity) =>
126
+ set((state) => {
127
+ const item = state.items.find((i) => i.id === id);
128
+ if (item) {
129
+ item.quantity = Math.max(0, quantity);
130
+ }
131
+ }),
132
+
133
+ clearCart: () => set({ items: [] }),
134
+
135
+ total: () => get().items.reduce((sum, item) => sum + item.price * item.quantity, 0),
136
+ }))
137
+ );
138
+ ```
139
+
140
+ ### Slices Pattern (Large Stores)
141
+
142
+ ```typescript
143
+ // stores/slices/userSlice.ts
144
+ import type { StateCreator } from 'zustand';
145
+
146
+ export interface UserSlice {
147
+ user: User | null;
148
+ setUser: (user: User | null) => void;
149
+ }
150
+
151
+ export const createUserSlice: StateCreator<UserSlice> = (set) => ({
152
+ user: null,
153
+ setUser: (user) => set({ user }),
154
+ });
155
+
156
+ // stores/slices/preferencesSlice.ts
157
+ export interface PreferencesSlice {
158
+ theme: 'light' | 'dark';
159
+ language: string;
160
+ setTheme: (theme: 'light' | 'dark') => void;
161
+ setLanguage: (lang: string) => void;
162
+ }
163
+
164
+ export const createPreferencesSlice: StateCreator<PreferencesSlice> = (set) => ({
165
+ theme: 'light',
166
+ language: 'en',
167
+ setTheme: (theme) => set({ theme }),
168
+ setLanguage: (language) => set({ language }),
169
+ });
170
+
171
+ // stores/app.store.ts
172
+ import { create } from 'zustand';
173
+ import { createUserSlice, type UserSlice } from './slices/userSlice';
174
+ import { createPreferencesSlice, type PreferencesSlice } from './slices/preferencesSlice';
175
+
176
+ type AppStore = UserSlice & PreferencesSlice;
177
+
178
+ export const useAppStore = create<AppStore>()((...a) => ({
179
+ ...createUserSlice(...a),
180
+ ...createPreferencesSlice(...a),
181
+ }));
182
+ ```
183
+
184
+ ---
185
+
186
+ ## Zustand Best Practices
187
+
188
+ ### Selectors for Performance
189
+
190
+ ```typescript
191
+ // BAD - Subscribes to entire store, re-renders on any change
192
+ const { sidebarOpen, theme, user } = useUIStore();
193
+
194
+ // GOOD - Only subscribes to what's needed
195
+ const sidebarOpen = useUIStore((state) => state.sidebarOpen);
196
+ const theme = useUIStore((state) => state.theme);
197
+
198
+ // GOOD - Multiple values with shallow comparison
199
+ import { useShallow } from 'zustand/react/shallow';
200
+
201
+ const { sidebarOpen, theme } = useUIStore(
202
+ useShallow((state) => ({ sidebarOpen: state.sidebarOpen, theme: state.theme }))
203
+ );
204
+ ```
205
+
206
+ ### Actions Outside Components
207
+
208
+ ```typescript
209
+ // Access store outside React
210
+ const token = useAuthStore.getState().token;
211
+ useAuthStore.getState().setAuth(newToken, user);
212
+
213
+ // Useful in API interceptors, service functions
214
+ export const api = ky.create({
215
+ hooks: {
216
+ beforeRequest: [
217
+ (request) => {
218
+ const token = useAuthStore.getState().token;
219
+ if (token) {
220
+ request.headers.set('Authorization', `Bearer ${token}`);
221
+ }
222
+ },
223
+ ],
224
+ },
225
+ });
226
+ ```
227
+
228
+ ### Derived State
229
+
230
+ ```typescript
231
+ // BAD - Storing computed values
232
+ interface CartState {
233
+ items: CartItem[];
234
+ total: number; // Don't store this!
235
+ }
236
+
237
+ // GOOD - Compute from source
238
+ interface CartState {
239
+ items: CartItem[];
240
+ }
241
+
242
+ // Option 1: Method in store
243
+ export const useCartStore = create<CartState>((set, get) => ({
244
+ items: [],
245
+ // Computed value as method
246
+ getTotal: () => get().items.reduce((sum, item) => sum + item.price * item.quantity, 0),
247
+ }));
248
+
249
+ // Option 2: Selector
250
+ const total = useCartStore((state) =>
251
+ state.items.reduce((sum, item) => sum + item.price * item.quantity, 0)
252
+ );
253
+ ```
254
+
255
+ ---
256
+
257
+ ## Local State (useState)
258
+
259
+ Use local state when:
260
+ - State is only used by one component
261
+ - State doesn't need to persist
262
+ - State is UI-specific (hover, focus, animation)
263
+
264
+ ```typescript
265
+ // Modal open state - local to component
266
+ function UserActions({ user }: { user: User }) {
267
+ const [isDeleteModalOpen, setDeleteModalOpen] = useState(false);
268
+
269
+ return (
270
+ <>
271
+ <Button onClick={() => setDeleteModalOpen(true)}>Delete</Button>
272
+ <DeleteModal
273
+ open={isDeleteModalOpen}
274
+ onClose={() => setDeleteModalOpen(false)}
275
+ user={user}
276
+ />
277
+ </>
278
+ );
279
+ }
280
+ ```
281
+
282
+ ---
283
+
284
+ ## useReducer for Complex Local State
285
+
286
+ ```typescript
287
+ // Complex state with multiple related values
288
+ type FormState = {
289
+ values: Record<string, string>;
290
+ errors: Record<string, string>;
291
+ touched: Record<string, boolean>;
292
+ isSubmitting: boolean;
293
+ };
294
+
295
+ type FormAction =
296
+ | { type: 'SET_VALUE'; field: string; value: string }
297
+ | { type: 'SET_ERROR'; field: string; error: string }
298
+ | { type: 'SET_TOUCHED'; field: string }
299
+ | { type: 'START_SUBMIT' }
300
+ | { type: 'END_SUBMIT' }
301
+ | { type: 'RESET' };
302
+
303
+ function formReducer(state: FormState, action: FormAction): FormState {
304
+ switch (action.type) {
305
+ case 'SET_VALUE':
306
+ return {
307
+ ...state,
308
+ values: { ...state.values, [action.field]: action.value },
309
+ errors: { ...state.errors, [action.field]: '' },
310
+ };
311
+ case 'SET_ERROR':
312
+ return {
313
+ ...state,
314
+ errors: { ...state.errors, [action.field]: action.error },
315
+ };
316
+ case 'SET_TOUCHED':
317
+ return {
318
+ ...state,
319
+ touched: { ...state.touched, [action.field]: true },
320
+ };
321
+ case 'START_SUBMIT':
322
+ return { ...state, isSubmitting: true };
323
+ case 'END_SUBMIT':
324
+ return { ...state, isSubmitting: false };
325
+ case 'RESET':
326
+ return initialState;
327
+ }
328
+ }
329
+ ```
330
+
331
+ ---
332
+
333
+ ## Context for Dependency Injection
334
+
335
+ Use Context for:
336
+ - Dependency injection (providing services, clients)
337
+ - Theme/i18n that rarely changes
338
+ - Feature flags
339
+
340
+ **Don't use for frequently changing state** (causes unnecessary re-renders).
341
+
342
+ ```typescript
343
+ // contexts/ApiContext.tsx
344
+ import { createContext, useContext, type ReactNode } from 'react';
345
+ import type { ApiClient } from '@/services/api';
346
+
347
+ const ApiContext = createContext<ApiClient | null>(null);
348
+
349
+ export function ApiProvider({ client, children }: { client: ApiClient; children: ReactNode }) {
350
+ return <ApiContext.Provider value={client}>{children}</ApiContext.Provider>;
351
+ }
352
+
353
+ export function useApiClient(): ApiClient {
354
+ const client = useContext(ApiContext);
355
+ if (!client) {
356
+ throw new Error('useApiClient must be used within ApiProvider');
357
+ }
358
+ return client;
359
+ }
360
+ ```
361
+
362
+ ---
363
+
364
+ ## URL State (React Router)
365
+
366
+ Store state in URL when:
367
+ - State should be shareable via link
368
+ - State should survive page refresh
369
+ - State represents the current "view" (filters, pagination, search)
370
+
371
+ ```typescript
372
+ // Using search params for filters
373
+ import { useSearchParams } from 'react-router-dom';
374
+
375
+ function UserList() {
376
+ const [searchParams, setSearchParams] = useSearchParams();
377
+
378
+ const page = Number(searchParams.get('page')) || 1;
379
+ const sort = searchParams.get('sort') || 'name';
380
+ const search = searchParams.get('q') || '';
381
+
382
+ const setPage = (newPage: number) => {
383
+ setSearchParams((params) => {
384
+ params.set('page', String(newPage));
385
+ return params;
386
+ });
387
+ };
388
+
389
+ const setSearch = (query: string) => {
390
+ setSearchParams((params) => {
391
+ if (query) {
392
+ params.set('q', query);
393
+ } else {
394
+ params.delete('q');
395
+ }
396
+ params.set('page', '1'); // Reset to first page
397
+ return params;
398
+ });
399
+ };
400
+
401
+ // Use page, sort, search in your query
402
+ const { data } = useUsers({ page, sort, search });
403
+
404
+ return (/* ... */);
405
+ }
406
+ ```
407
+
408
+ ---
409
+
410
+ ## State Decision Flowchart
411
+
412
+ ```
413
+ Is this data from an API?
414
+ ├── Yes → TanStack Query (server state)
415
+ └── No → Is it needed by multiple components?
416
+ ├── No → useState or useReducer (local state)
417
+ └── Yes → Should it be in the URL?
418
+ ├── Yes → useSearchParams (URL state)
419
+ └── No → Does it persist across sessions?
420
+ ├── Yes → Zustand with persist
421
+ └── No → Zustand (client state)
422
+ ```
423
+
424
+ ---
425
+
426
+ ## Anti-Patterns
427
+
428
+ | Anti-Pattern | Problem | Correct Approach |
429
+ |--------------|---------|------------------|
430
+ | Storing API data in Zustand | Stale data, no caching | Use TanStack Query for server state |
431
+ | Global state for everything | Unnecessary re-renders | Keep state local when possible |
432
+ | Context for frequent updates | Performance issues | Use Zustand with selectors |
433
+ | Duplicate state | Sync issues | Single source of truth, derive when needed |
434
+ | Not using selectors | Unnecessary re-renders | Always select specific values |
435
+ | Storing derived values | Can become stale | Compute on read |
436
+
437
+ ---
438
+
439
+ ## Testing Stores
440
+
441
+ ```typescript
442
+ // Reset store between tests
443
+ import { useCartStore } from './cart.store';
444
+
445
+ beforeEach(() => {
446
+ useCartStore.setState({ items: [] });
447
+ });
448
+
449
+ test('adds item to cart', () => {
450
+ const { addItem } = useCartStore.getState();
451
+
452
+ addItem({ id: '1', name: 'Product', price: 100 });
453
+
454
+ expect(useCartStore.getState().items).toHaveLength(1);
455
+ expect(useCartStore.getState().items[0].quantity).toBe(1);
456
+ });
457
+
458
+ test('increments quantity for existing item', () => {
459
+ useCartStore.setState({
460
+ items: [{ id: '1', name: 'Product', price: 100, quantity: 1 }],
461
+ });
462
+
463
+ const { addItem } = useCartStore.getState();
464
+ addItem({ id: '1', name: 'Product', price: 100 });
465
+
466
+ expect(useCartStore.getState().items[0].quantity).toBe(2);
467
+ });
468
+ ```
469
+
470
+ ---
471
+
472
+ _State is data plus the rules for changing it. Keep it minimal, local, and predictable._