@pageai/ralph-loop 1.0.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 (120) hide show
  1. package/.agent/PROMPT.md +58 -0
  2. package/.agent/STEERING.md +3 -0
  3. package/.agent/logs/LOG.md +13 -0
  4. package/.agent/prd/.gitkeep +0 -0
  5. package/.agent/screenshots/.gitkeep +0 -0
  6. package/.agent/skills/component-refactoring/SKILL.md +247 -0
  7. package/.agent/skills/component-refactoring/references/complexity-patterns.md +485 -0
  8. package/.agent/skills/component-refactoring/references/component-splitting.md +419 -0
  9. package/.agent/skills/component-refactoring/references/hook-extraction.md +317 -0
  10. package/.agent/skills/e2e-tester/SKILL.md +595 -0
  11. package/.agent/skills/frontend-code-review/SKILL.md +73 -0
  12. package/.agent/skills/frontend-code-review/references/code-quality.md +28 -0
  13. package/.agent/skills/frontend-code-review/references/performance.md +36 -0
  14. package/.agent/skills/frontend-testing/SKILL.md +316 -0
  15. package/.agent/skills/frontend-testing/assets/component-test.template.tsx +293 -0
  16. package/.agent/skills/frontend-testing/assets/hook-test.template.ts +207 -0
  17. package/.agent/skills/frontend-testing/assets/utility-test.template.ts +154 -0
  18. package/.agent/skills/frontend-testing/references/async-testing.md +345 -0
  19. package/.agent/skills/frontend-testing/references/checklist.md +188 -0
  20. package/.agent/skills/frontend-testing/references/common-patterns.md +449 -0
  21. package/.agent/skills/frontend-testing/references/mocking.md +289 -0
  22. package/.agent/skills/frontend-testing/references/workflow.md +265 -0
  23. package/.agent/skills/prd-creator/JSON.md +613 -0
  24. package/.agent/skills/prd-creator/PRD.md +196 -0
  25. package/.agent/skills/prd-creator/SKILL.md +143 -0
  26. package/.agent/skills/skill-creator/SKILL.md +355 -0
  27. package/.agent/skills/skill-creator/references/output-patterns.md +86 -0
  28. package/.agent/skills/skill-creator/references/workflows.md +28 -0
  29. package/.agent/skills/skill-creator/scripts/init_skill.py +300 -0
  30. package/.agent/skills/skill-creator/scripts/package_skill.py +110 -0
  31. package/.agent/skills/vercel-react-best-practices/AGENTS.md +2249 -0
  32. package/.agent/skills/vercel-react-best-practices/SKILL.md +125 -0
  33. package/.agent/skills/vercel-react-best-practices/rules/advanced-event-handler-refs.md +55 -0
  34. package/.agent/skills/vercel-react-best-practices/rules/advanced-use-latest.md +49 -0
  35. package/.agent/skills/vercel-react-best-practices/rules/async-api-routes.md +38 -0
  36. package/.agent/skills/vercel-react-best-practices/rules/async-defer-await.md +80 -0
  37. package/.agent/skills/vercel-react-best-practices/rules/async-dependencies.md +36 -0
  38. package/.agent/skills/vercel-react-best-practices/rules/async-parallel.md +28 -0
  39. package/.agent/skills/vercel-react-best-practices/rules/async-suspense-boundaries.md +99 -0
  40. package/.agent/skills/vercel-react-best-practices/rules/bundle-barrel-imports.md +59 -0
  41. package/.agent/skills/vercel-react-best-practices/rules/bundle-conditional.md +31 -0
  42. package/.agent/skills/vercel-react-best-practices/rules/bundle-defer-third-party.md +49 -0
  43. package/.agent/skills/vercel-react-best-practices/rules/bundle-dynamic-imports.md +35 -0
  44. package/.agent/skills/vercel-react-best-practices/rules/bundle-preload.md +50 -0
  45. package/.agent/skills/vercel-react-best-practices/rules/client-event-listeners.md +74 -0
  46. package/.agent/skills/vercel-react-best-practices/rules/client-swr-dedup.md +56 -0
  47. package/.agent/skills/vercel-react-best-practices/rules/js-batch-dom-css.md +82 -0
  48. package/.agent/skills/vercel-react-best-practices/rules/js-cache-function-results.md +80 -0
  49. package/.agent/skills/vercel-react-best-practices/rules/js-cache-property-access.md +28 -0
  50. package/.agent/skills/vercel-react-best-practices/rules/js-cache-storage.md +70 -0
  51. package/.agent/skills/vercel-react-best-practices/rules/js-combine-iterations.md +32 -0
  52. package/.agent/skills/vercel-react-best-practices/rules/js-early-exit.md +50 -0
  53. package/.agent/skills/vercel-react-best-practices/rules/js-hoist-regexp.md +45 -0
  54. package/.agent/skills/vercel-react-best-practices/rules/js-index-maps.md +37 -0
  55. package/.agent/skills/vercel-react-best-practices/rules/js-length-check-first.md +49 -0
  56. package/.agent/skills/vercel-react-best-practices/rules/js-min-max-loop.md +82 -0
  57. package/.agent/skills/vercel-react-best-practices/rules/js-set-map-lookups.md +24 -0
  58. package/.agent/skills/vercel-react-best-practices/rules/js-tosorted-immutable.md +57 -0
  59. package/.agent/skills/vercel-react-best-practices/rules/rendering-activity.md +26 -0
  60. package/.agent/skills/vercel-react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
  61. package/.agent/skills/vercel-react-best-practices/rules/rendering-conditional-render.md +40 -0
  62. package/.agent/skills/vercel-react-best-practices/rules/rendering-content-visibility.md +38 -0
  63. package/.agent/skills/vercel-react-best-practices/rules/rendering-hoist-jsx.md +46 -0
  64. package/.agent/skills/vercel-react-best-practices/rules/rendering-hydration-no-flicker.md +82 -0
  65. package/.agent/skills/vercel-react-best-practices/rules/rendering-svg-precision.md +28 -0
  66. package/.agent/skills/vercel-react-best-practices/rules/rerender-defer-reads.md +39 -0
  67. package/.agent/skills/vercel-react-best-practices/rules/rerender-dependencies.md +45 -0
  68. package/.agent/skills/vercel-react-best-practices/rules/rerender-derived-state.md +29 -0
  69. package/.agent/skills/vercel-react-best-practices/rules/rerender-functional-setstate.md +74 -0
  70. package/.agent/skills/vercel-react-best-practices/rules/rerender-lazy-state-init.md +58 -0
  71. package/.agent/skills/vercel-react-best-practices/rules/rerender-memo.md +44 -0
  72. package/.agent/skills/vercel-react-best-practices/rules/rerender-transitions.md +40 -0
  73. package/.agent/skills/vercel-react-best-practices/rules/server-after-nonblocking.md +73 -0
  74. package/.agent/skills/vercel-react-best-practices/rules/server-cache-lru.md +41 -0
  75. package/.agent/skills/vercel-react-best-practices/rules/server-cache-react.md +26 -0
  76. package/.agent/skills/vercel-react-best-practices/rules/server-parallel-fetching.md +79 -0
  77. package/.agent/skills/vercel-react-best-practices/rules/server-serialization.md +38 -0
  78. package/.agent/skills/vitest-best-practices/AGENTS.md +84 -0
  79. package/.agent/skills/vitest-best-practices/SKILL.md +130 -0
  80. package/.agent/skills/vitest-best-practices/references/aaa-pattern.md +260 -0
  81. package/.agent/skills/vitest-best-practices/references/assertions.md +393 -0
  82. package/.agent/skills/vitest-best-practices/references/async-testing.md +454 -0
  83. package/.agent/skills/vitest-best-practices/references/error-handling.md +382 -0
  84. package/.agent/skills/vitest-best-practices/references/organization.md +212 -0
  85. package/.agent/skills/vitest-best-practices/references/parameterized-tests.md +297 -0
  86. package/.agent/skills/vitest-best-practices/references/performance.md +528 -0
  87. package/.agent/skills/vitest-best-practices/references/snapshot-testing.md +483 -0
  88. package/.agent/skills/vitest-best-practices/references/test-doubles.md +499 -0
  89. package/.agent/skills/vitest-best-practices/references/vitest-features.md +529 -0
  90. package/.agent/skills/web-design-guidelines/SKILL.md +39 -0
  91. package/.agent/tasks/.gitkeep +0 -0
  92. package/.agent/tasks.json +1 -0
  93. package/.claude/agents/code-reviewer.md +172 -0
  94. package/.claude/commands/aw.md +50 -0
  95. package/.claude/hooks/play-sound.js +87 -0
  96. package/.claude/hooks/pre-tool-use.js +40 -0
  97. package/.claude/settings.json +54 -0
  98. package/.claude/settings.local.json +13 -0
  99. package/.mcp.json +31 -0
  100. package/AGENTS.md +44 -0
  101. package/CLAUDE.md +1 -0
  102. package/README.md +236 -0
  103. package/bin/cli.js +156 -0
  104. package/bin/lib/copy.js +149 -0
  105. package/bin/lib/display.js +137 -0
  106. package/package.json +65 -0
  107. package/ralph.sh +333 -0
  108. package/scripts/lib/args.sh +44 -0
  109. package/scripts/lib/cleanup.sh +53 -0
  110. package/scripts/lib/constants.sh +25 -0
  111. package/scripts/lib/display.sh +196 -0
  112. package/scripts/lib/logging.sh +30 -0
  113. package/scripts/lib/notify.sh +41 -0
  114. package/scripts/lib/output.sh +147 -0
  115. package/scripts/lib/preflight.sh +57 -0
  116. package/scripts/lib/preview.sh +77 -0
  117. package/scripts/lib/promise.sh +76 -0
  118. package/scripts/lib/spinner.sh +85 -0
  119. package/scripts/lib/terminal.sh +57 -0
  120. package/scripts/lib/timing.sh +223 -0
@@ -0,0 +1,419 @@
1
+ # Component Splitting Patterns
2
+
3
+ This document provides detailed guidance on splitting large components into smaller, focused components.
4
+
5
+ ## When to Split Components
6
+
7
+ Split a component when you identify:
8
+
9
+ 1. **Multiple UI sections** - Distinct visual areas with minimal coupling that can be composed independently
10
+ 1. **Conditional rendering blocks** - Large `{condition && <JSX />}` blocks
11
+ 1. **Repeated patterns** - Similar UI structures used multiple times
12
+ 1. **300+ lines** - Component exceeds manageable size
13
+ 1. **Modal clusters** - Multiple modals rendered in one component
14
+
15
+ ## Splitting Strategies
16
+
17
+ ### Strategy 1: Section-Based Splitting
18
+
19
+ Identify visual sections and extract each as a component.
20
+
21
+ ```typescript
22
+ // ❌ Before: Monolithic component (500+ lines)
23
+ const ConfigurationPage = () => {
24
+ return (
25
+ <div>
26
+ {/* Header Section - 50 lines */}
27
+ <div className="header">
28
+ <h1>{t('configuration.title')}</h1>
29
+ <div className="actions">
30
+ {isAdvancedMode && <Badge>Advanced</Badge>}
31
+ <ModelParameterModal ... />
32
+ <AppPublisher ... />
33
+ </div>
34
+ </div>
35
+
36
+ {/* Config Section - 200 lines */}
37
+ <div className="config">
38
+ <Config />
39
+ </div>
40
+
41
+ {/* Debug Section - 150 lines */}
42
+ <div className="debug">
43
+ <Debug ... />
44
+ </div>
45
+
46
+ {/* Modals Section - 100 lines */}
47
+ {showSelectDataSet && <SelectDataSet ... />}
48
+ {showHistoryModal && <EditHistoryModal ... />}
49
+ {showUseGPT4Confirm && <Confirm ... />}
50
+ </div>
51
+ )
52
+ }
53
+
54
+ // ✅ After: Split into focused components
55
+ // configuration/
56
+ // ├── index.tsx (orchestration)
57
+ // ├── configuration-header.tsx
58
+ // ├── configuration-content.tsx
59
+ // ├── configuration-debug.tsx
60
+ // └── configuration-modals.tsx
61
+
62
+ // configuration-header.tsx
63
+ interface ConfigurationHeaderProps {
64
+ isAdvancedMode: boolean
65
+ onPublish: () => void
66
+ }
67
+
68
+ const ConfigurationHeader: FC<ConfigurationHeaderProps> = ({
69
+ isAdvancedMode,
70
+ onPublish,
71
+ }) => {
72
+ const { t } = useTranslation()
73
+
74
+ return (
75
+ <div className="header">
76
+ <h1>{t('configuration.title')}</h1>
77
+ <div className="actions">
78
+ {isAdvancedMode && <Badge>Advanced</Badge>}
79
+ <ModelParameterModal ... />
80
+ <AppPublisher onPublish={onPublish} />
81
+ </div>
82
+ </div>
83
+ )
84
+ }
85
+
86
+ // index.tsx (orchestration only)
87
+ const ConfigurationPage = () => {
88
+ const { modelConfig, setModelConfig } = useModelConfig()
89
+ const { activeModal, openModal, closeModal } = useModalState()
90
+
91
+ return (
92
+ <div>
93
+ <ConfigurationHeader
94
+ isAdvancedMode={isAdvancedMode}
95
+ onPublish={handlePublish}
96
+ />
97
+ <ConfigurationContent
98
+ modelConfig={modelConfig}
99
+ onConfigChange={setModelConfig}
100
+ />
101
+ {!isMobile && (
102
+ <ConfigurationDebug
103
+ inputs={inputs}
104
+ onSetting={handleSetting}
105
+ />
106
+ )}
107
+ <ConfigurationModals
108
+ activeModal={activeModal}
109
+ onClose={closeModal}
110
+ />
111
+ </div>
112
+ )
113
+ }
114
+ ```
115
+
116
+ ### Strategy 2: Conditional Block Extraction
117
+
118
+ Extract large conditional rendering blocks.
119
+
120
+ ```typescript
121
+ // ❌ Before: Large conditional blocks
122
+ const AppInfo = () => {
123
+ return (
124
+ <div>
125
+ {expand ? (
126
+ <div className="expanded">
127
+ {/* 100 lines of expanded view */}
128
+ </div>
129
+ ) : (
130
+ <div className="collapsed">
131
+ {/* 50 lines of collapsed view */}
132
+ </div>
133
+ )}
134
+ </div>
135
+ )
136
+ }
137
+
138
+ // ✅ After: Separate view components
139
+ const AppInfoExpanded: FC<AppInfoViewProps> = ({ appDetail, onAction }) => {
140
+ return (
141
+ <div className="expanded">
142
+ {/* Clean, focused expanded view */}
143
+ </div>
144
+ )
145
+ }
146
+
147
+ const AppInfoCollapsed: FC<AppInfoViewProps> = ({ appDetail, onAction }) => {
148
+ return (
149
+ <div className="collapsed">
150
+ {/* Clean, focused collapsed view */}
151
+ </div>
152
+ )
153
+ }
154
+
155
+ const AppInfo = () => {
156
+ return (
157
+ <div>
158
+ {expand
159
+ ? <AppInfoExpanded appDetail={appDetail} onAction={handleAction} />
160
+ : <AppInfoCollapsed appDetail={appDetail} onAction={handleAction} />
161
+ }
162
+ </div>
163
+ )
164
+ }
165
+ ```
166
+
167
+ ### Strategy 3: Modal Extraction
168
+
169
+ Extract modals with their trigger logic.
170
+
171
+ ```typescript
172
+ // ❌ Before: Multiple modals in one component
173
+ const AppInfo = () => {
174
+ const [showEdit, setShowEdit] = useState(false)
175
+ const [showDuplicate, setShowDuplicate] = useState(false)
176
+ const [showDelete, setShowDelete] = useState(false)
177
+ const [showSwitch, setShowSwitch] = useState(false)
178
+
179
+ const onEdit = async (data) => { /* 20 lines */ }
180
+ const onDuplicate = async (data) => { /* 20 lines */ }
181
+ const onDelete = async () => { /* 15 lines */ }
182
+
183
+ return (
184
+ <div>
185
+ {/* Main content */}
186
+
187
+ {showEdit && <EditModal onConfirm={onEdit} onClose={() => setShowEdit(false)} />}
188
+ {showDuplicate && <DuplicateModal onConfirm={onDuplicate} onClose={() => setShowDuplicate(false)} />}
189
+ {showDelete && <DeleteConfirm onConfirm={onDelete} onClose={() => setShowDelete(false)} />}
190
+ {showSwitch && <SwitchModal ... />}
191
+ </div>
192
+ )
193
+ }
194
+
195
+ // ✅ After: Modal manager component
196
+ // app-info-modals.tsx
197
+ type ModalType = 'edit' | 'duplicate' | 'delete' | 'switch' | null
198
+
199
+ interface AppInfoModalsProps {
200
+ appDetail: AppDetail
201
+ activeModal: ModalType
202
+ onClose: () => void
203
+ onSuccess: () => void
204
+ }
205
+
206
+ const AppInfoModals: FC<AppInfoModalsProps> = ({
207
+ appDetail,
208
+ activeModal,
209
+ onClose,
210
+ onSuccess,
211
+ }) => {
212
+ const handleEdit = async (data) => { /* logic */ }
213
+ const handleDuplicate = async (data) => { /* logic */ }
214
+ const handleDelete = async () => { /* logic */ }
215
+
216
+ return (
217
+ <>
218
+ {activeModal === 'edit' && (
219
+ <EditModal
220
+ appDetail={appDetail}
221
+ onConfirm={handleEdit}
222
+ onClose={onClose}
223
+ />
224
+ )}
225
+ {activeModal === 'duplicate' && (
226
+ <DuplicateModal
227
+ appDetail={appDetail}
228
+ onConfirm={handleDuplicate}
229
+ onClose={onClose}
230
+ />
231
+ )}
232
+ {activeModal === 'delete' && (
233
+ <DeleteConfirm
234
+ onConfirm={handleDelete}
235
+ onClose={onClose}
236
+ />
237
+ )}
238
+ {activeModal === 'switch' && (
239
+ <SwitchModal
240
+ appDetail={appDetail}
241
+ onClose={onClose}
242
+ />
243
+ )}
244
+ </>
245
+ )
246
+ }
247
+
248
+ // Parent component
249
+ const AppInfo = () => {
250
+ const { activeModal, openModal, closeModal } = useModalState()
251
+
252
+ return (
253
+ <div>
254
+ {/* Main content with openModal triggers */}
255
+ <Button onClick={() => openModal('edit')}>Edit</Button>
256
+
257
+ <AppInfoModals
258
+ appDetail={appDetail}
259
+ activeModal={activeModal}
260
+ onClose={closeModal}
261
+ onSuccess={handleSuccess}
262
+ />
263
+ </div>
264
+ )
265
+ }
266
+ ```
267
+
268
+ ### Strategy 4: List Item Extraction
269
+
270
+ Extract repeated item rendering.
271
+
272
+ ```typescript
273
+ // ❌ Before: Inline item rendering
274
+ const OperationsList = () => {
275
+ return (
276
+ <div>
277
+ {operations.map(op => (
278
+ <div key={op.id} className="operation-item">
279
+ <span className="icon">{op.icon}</span>
280
+ <span className="title">{op.title}</span>
281
+ <span className="description">{op.description}</span>
282
+ <button onClick={() => op.onClick()}>
283
+ {op.actionLabel}
284
+ </button>
285
+ {op.badge && <Badge>{op.badge}</Badge>}
286
+ {/* More complex rendering... */}
287
+ </div>
288
+ ))}
289
+ </div>
290
+ )
291
+ }
292
+
293
+ // ✅ After: Extracted item component
294
+ interface OperationItemProps {
295
+ operation: Operation
296
+ onAction: (id: string) => void
297
+ }
298
+
299
+ const OperationItem: FC<OperationItemProps> = ({ operation, onAction }) => {
300
+ return (
301
+ <div className="operation-item">
302
+ <span className="icon">{operation.icon}</span>
303
+ <span className="title">{operation.title}</span>
304
+ <span className="description">{operation.description}</span>
305
+ <button onClick={() => onAction(operation.id)}>
306
+ {operation.actionLabel}
307
+ </button>
308
+ {operation.badge && <Badge>{operation.badge}</Badge>}
309
+ </div>
310
+ )
311
+ }
312
+
313
+ const OperationsList = () => {
314
+ const handleAction = useCallback((id: string) => {
315
+ const op = operations.find(o => o.id === id)
316
+ op?.onClick()
317
+ }, [operations])
318
+
319
+ return (
320
+ <div>
321
+ {operations.map(op => (
322
+ <OperationItem
323
+ key={op.id}
324
+ operation={op}
325
+ onAction={handleAction}
326
+ />
327
+ ))}
328
+ </div>
329
+ )
330
+ }
331
+ ```
332
+
333
+ ## Directory Structure Patterns
334
+
335
+ Feature-Based Structure (Standard) is recommended. Group related components of a feature together and use a domain-driven approach for file naming and structure.
336
+
337
+ ## Props Design
338
+
339
+ ### Minimal Props Principle
340
+
341
+ Pass only what's needed:
342
+
343
+ ```typescript
344
+ // ❌ Bad: Passing entire objects when only some fields needed
345
+ <ConfigHeader appDetail={appDetail} modelConfig={modelConfig} />
346
+
347
+ // ✅ Good: Destructure to minimum required
348
+ <ConfigHeader
349
+ appName={appDetail.name}
350
+ isAdvancedMode={modelConfig.isAdvanced}
351
+ onPublish={handlePublish}
352
+ />
353
+ ```
354
+
355
+ ### Callback Props Pattern
356
+
357
+ Use callbacks for child-to-parent communication:
358
+
359
+ ```typescript
360
+ // Parent
361
+ const Parent = () => {
362
+ const [value, setValue] = useState('')
363
+
364
+ return (
365
+ <Child
366
+ value={value}
367
+ onChange={setValue}
368
+ onSubmit={handleSubmit}
369
+ />
370
+ )
371
+ }
372
+
373
+ // Child
374
+ interface ChildProps {
375
+ value: string
376
+ onChange: (value: string) => void
377
+ onSubmit: () => void
378
+ }
379
+
380
+ const Child: FC<ChildProps> = ({ value, onChange, onSubmit }) => {
381
+ return (
382
+ <div>
383
+ <input value={value} onChange={e => onChange(e.target.value)} />
384
+ <button onClick={onSubmit}>Submit</button>
385
+ </div>
386
+ )
387
+ }
388
+ ```
389
+
390
+ ### Render Props for Flexibility
391
+
392
+ When sub-components need parent context:
393
+
394
+ ```typescript
395
+ interface ListProps<T> {
396
+ items: T[]
397
+ renderItem: (item: T, index: number) => React.ReactNode
398
+ renderEmpty?: () => React.ReactNode
399
+ }
400
+
401
+ function List<T>({ items, renderItem, renderEmpty }: ListProps<T>) {
402
+ if (items.length === 0 && renderEmpty) {
403
+ return <>{renderEmpty()}</>
404
+ }
405
+
406
+ return (
407
+ <div>
408
+ {items.map((item, index) => renderItem(item, index))}
409
+ </div>
410
+ )
411
+ }
412
+
413
+ // Usage
414
+ <List
415
+ items={operations}
416
+ renderItem={(op, i) => <OperationItem key={i} operation={op} />}
417
+ renderEmpty={() => <EmptyState message="No operations" />}
418
+ />
419
+ ```