@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,317 @@
1
+ # Hook Extraction Patterns
2
+
3
+ This document provides detailed guidance on extracting custom hooks from complex components.
4
+
5
+ ## When to Extract Hooks
6
+
7
+ Extract a custom hook when you identify:
8
+
9
+ 1. **Coupled state groups** - Multiple `useState` hooks that are always used together
10
+ 1. **Complex effects** - `useEffect` with multiple dependencies or cleanup logic
11
+ 1. **Business logic** - Data transformations, validations, or calculations
12
+ 1. **Reusable patterns** - Logic that appears in multiple components
13
+
14
+ ## Extraction Process
15
+
16
+ ### Step 1: Identify State Groups
17
+
18
+ Look for state variables that are logically related:
19
+
20
+ ```typescript
21
+ // ❌ These belong together - extract to hook
22
+ const [modelConfig, setModelConfig] = useState<ModelConfig>(...)
23
+ const [completionParams, setCompletionParams] = useState<FormValue>({})
24
+ const [modelModeType, setModelModeType] = useState<ModelModeType>(...)
25
+
26
+ // These are model-related state that should be in useModelConfig()
27
+ ```
28
+
29
+ ### Step 2: Identify Related Effects
30
+
31
+ Find effects that modify the grouped state:
32
+
33
+ ```typescript
34
+ // ❌ These effects belong with the state above
35
+ useEffect(() => {
36
+ if (hasFetchedDetail && !modelModeType) {
37
+ const mode = currModel?.model_properties.mode
38
+ if (mode) {
39
+ const newModelConfig = produce(modelConfig, (draft) => {
40
+ draft.mode = mode
41
+ })
42
+ setModelConfig(newModelConfig)
43
+ }
44
+ }
45
+ }, [textGenerationModelList, hasFetchedDetail, modelModeType, currModel])
46
+ ```
47
+
48
+ ### Step 3: Create the Hook
49
+
50
+ ```typescript
51
+ // hooks/use-model-config.ts
52
+ import type { FormValue } from '@/app/components/header/account-setting/model-provider-page/declarations'
53
+ import type { ModelConfig } from '@/models/debug'
54
+ import { produce } from 'immer'
55
+ import { useEffect, useState } from 'react'
56
+ import { ModelModeType } from '@/types/app'
57
+
58
+ interface UseModelConfigParams {
59
+ initialConfig?: Partial<ModelConfig>
60
+ currModel?: { model_properties?: { mode?: ModelModeType } }
61
+ hasFetchedDetail: boolean
62
+ }
63
+
64
+ interface UseModelConfigReturn {
65
+ modelConfig: ModelConfig
66
+ setModelConfig: (config: ModelConfig) => void
67
+ completionParams: FormValue
68
+ setCompletionParams: (params: FormValue) => void
69
+ modelModeType: ModelModeType
70
+ }
71
+
72
+ export const useModelConfig = ({
73
+ initialConfig,
74
+ currModel,
75
+ hasFetchedDetail,
76
+ }: UseModelConfigParams): UseModelConfigReturn => {
77
+ const [modelConfig, setModelConfig] = useState<ModelConfig>({
78
+ provider: 'langgenius/openai/openai',
79
+ model_id: 'gpt-3.5-turbo',
80
+ mode: ModelModeType.unset,
81
+ // ... default values
82
+ ...initialConfig,
83
+ })
84
+
85
+ const [completionParams, setCompletionParams] = useState<FormValue>({})
86
+
87
+ const modelModeType = modelConfig.mode
88
+
89
+ // Fill old app data missing model mode
90
+ useEffect(() => {
91
+ if (hasFetchedDetail && !modelModeType) {
92
+ const mode = currModel?.model_properties?.mode
93
+ if (mode) {
94
+ setModelConfig(produce(modelConfig, (draft) => {
95
+ draft.mode = mode
96
+ }))
97
+ }
98
+ }
99
+ }, [hasFetchedDetail, modelModeType, currModel])
100
+
101
+ return {
102
+ modelConfig,
103
+ setModelConfig,
104
+ completionParams,
105
+ setCompletionParams,
106
+ modelModeType,
107
+ }
108
+ }
109
+ ```
110
+
111
+ ### Step 4: Update Component
112
+
113
+ ```typescript
114
+ // Before: 50+ lines of state management
115
+ const Configuration: FC = () => {
116
+ const [modelConfig, setModelConfig] = useState<ModelConfig>(...)
117
+ // ... lots of related state and effects
118
+ }
119
+
120
+ // After: Clean component
121
+ const Configuration: FC = () => {
122
+ const {
123
+ modelConfig,
124
+ setModelConfig,
125
+ completionParams,
126
+ setCompletionParams,
127
+ modelModeType,
128
+ } = useModelConfig({
129
+ currModel,
130
+ hasFetchedDetail,
131
+ })
132
+
133
+ // Component now focuses on UI
134
+ }
135
+ ```
136
+
137
+ ## Naming Conventions
138
+
139
+ ### Hook Names
140
+
141
+ - Use `use` prefix: `useModelConfig`, `useDatasetConfig`
142
+ - Be specific: `useAdvancedPromptConfig` not `usePrompt`
143
+ - Include domain: `useWorkflowVariables`, `useMCPServer`
144
+
145
+ ### File Names
146
+
147
+ - Kebab-case: `use-model-config.ts`
148
+ - Place in `hooks/` subdirectory when multiple hooks exist
149
+ - Place alongside component for single-use hooks
150
+
151
+ ### Return Type Names
152
+
153
+ - Suffix with `Return`: `UseModelConfigReturn`
154
+ - Suffix params with `Params`: `UseModelConfigParams`
155
+
156
+ ## Common Hook Patterns
157
+
158
+ ### 1. Data Fetching Hook (React Query)
159
+
160
+ ```typescript
161
+ // Pattern: Use @tanstack/react-query for data fetching
162
+ import { useQuery, useQueryClient } from '@tanstack/react-query'
163
+ import { get } from '@/service/base'
164
+ import { useInvalid } from '@/service/use-base'
165
+
166
+ const NAME_SPACE = 'appConfig'
167
+
168
+ // Query keys for cache management
169
+ export const appConfigQueryKeys = {
170
+ detail: (appId: string) => [NAME_SPACE, 'detail', appId] as const,
171
+ }
172
+
173
+ // Main data hook
174
+ export const useAppConfig = (appId: string) => {
175
+ return useQuery({
176
+ enabled: !!appId,
177
+ queryKey: appConfigQueryKeys.detail(appId),
178
+ queryFn: () => get<AppDetailResponse>(`/apps/${appId}`),
179
+ select: data => data?.model_config || null,
180
+ })
181
+ }
182
+
183
+ // Invalidation hook for refreshing data
184
+ export const useInvalidAppConfig = () => {
185
+ return useInvalid([NAME_SPACE])
186
+ }
187
+
188
+ // Usage in component
189
+ const Component = () => {
190
+ const { data: config, isLoading, error, refetch } = useAppConfig(appId)
191
+ const invalidAppConfig = useInvalidAppConfig()
192
+
193
+ const handleRefresh = () => {
194
+ invalidAppConfig() // Invalidates cache and triggers refetch
195
+ }
196
+
197
+ return <div>...</div>
198
+ }
199
+ ```
200
+
201
+ ### 2. Form State Hook
202
+
203
+ ```typescript
204
+ // Pattern: Form state + validation + submission
205
+ export const useConfigForm = (initialValues: ConfigFormValues) => {
206
+ const [values, setValues] = useState(initialValues)
207
+ const [errors, setErrors] = useState<Record<string, string>>({})
208
+ const [isSubmitting, setIsSubmitting] = useState(false)
209
+
210
+ const validate = useCallback(() => {
211
+ const newErrors: Record<string, string> = {}
212
+ if (!values.name) newErrors.name = 'Name is required'
213
+ setErrors(newErrors)
214
+ return Object.keys(newErrors).length === 0
215
+ }, [values])
216
+
217
+ const handleChange = useCallback((field: string, value: any) => {
218
+ setValues(prev => ({ ...prev, [field]: value }))
219
+ }, [])
220
+
221
+ const handleSubmit = useCallback(async (onSubmit: (values: ConfigFormValues) => Promise<void>) => {
222
+ if (!validate()) return
223
+ setIsSubmitting(true)
224
+ try {
225
+ await onSubmit(values)
226
+ } finally {
227
+ setIsSubmitting(false)
228
+ }
229
+ }, [values, validate])
230
+
231
+ return { values, errors, isSubmitting, handleChange, handleSubmit }
232
+ }
233
+ ```
234
+
235
+ ### 3. Modal State Hook
236
+
237
+ ```typescript
238
+ // Pattern: Multiple modal management
239
+ type ModalType = 'edit' | 'delete' | 'duplicate' | null
240
+
241
+ export const useModalState = () => {
242
+ const [activeModal, setActiveModal] = useState<ModalType>(null)
243
+ const [modalData, setModalData] = useState<any>(null)
244
+
245
+ const openModal = useCallback((type: ModalType, data?: any) => {
246
+ setActiveModal(type)
247
+ setModalData(data)
248
+ }, [])
249
+
250
+ const closeModal = useCallback(() => {
251
+ setActiveModal(null)
252
+ setModalData(null)
253
+ }, [])
254
+
255
+ return {
256
+ activeModal,
257
+ modalData,
258
+ openModal,
259
+ closeModal,
260
+ isOpen: useCallback((type: ModalType) => activeModal === type, [activeModal]),
261
+ }
262
+ }
263
+ ```
264
+
265
+ ### 4. Toggle/Boolean Hook
266
+
267
+ ```typescript
268
+ // Pattern: Boolean state with convenience methods
269
+ export const useToggle = (initialValue = false) => {
270
+ const [value, setValue] = useState(initialValue)
271
+
272
+ const toggle = useCallback(() => setValue(v => !v), [])
273
+ const setTrue = useCallback(() => setValue(true), [])
274
+ const setFalse = useCallback(() => setValue(false), [])
275
+
276
+ return [value, { toggle, setTrue, setFalse, set: setValue }] as const
277
+ }
278
+
279
+ // Usage
280
+ const [isExpanded, { toggle, setTrue: expand, setFalse: collapse }] = useToggle()
281
+ ```
282
+
283
+ ## Testing Extracted Hooks
284
+
285
+ After extraction, test hooks in isolation:
286
+
287
+ ```typescript
288
+ // use-model-config.spec.ts
289
+ import { renderHook, act } from '@testing-library/react'
290
+ import { useModelConfig } from './use-model-config'
291
+
292
+ describe('useModelConfig', () => {
293
+ it('should initialize with default values', () => {
294
+ const { result } = renderHook(() => useModelConfig({
295
+ hasFetchedDetail: false,
296
+ }))
297
+
298
+ expect(result.current.modelConfig.provider).toBe('langgenius/openai/openai')
299
+ expect(result.current.modelModeType).toBe(ModelModeType.unset)
300
+ })
301
+
302
+ it('should update model config', () => {
303
+ const { result } = renderHook(() => useModelConfig({
304
+ hasFetchedDetail: true,
305
+ }))
306
+
307
+ act(() => {
308
+ result.current.setModelConfig({
309
+ ...result.current.modelConfig,
310
+ model_id: 'gpt-4',
311
+ })
312
+ })
313
+
314
+ expect(result.current.modelConfig.model_id).toBe('gpt-4')
315
+ })
316
+ })
317
+ ```