@hustle-together/api-dev-tools 3.6.5 → 3.10.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 +5599 -258
- package/bin/cli.js +395 -20
- package/commands/README.md +459 -71
- package/commands/hustle-api-continue.md +158 -0
- package/commands/{api-create.md → hustle-api-create.md} +35 -15
- package/commands/{api-env.md → hustle-api-env.md} +4 -4
- package/commands/{api-interview.md → hustle-api-interview.md} +1 -1
- package/commands/{api-research.md → hustle-api-research.md} +3 -3
- package/commands/hustle-api-sessions.md +149 -0
- package/commands/{api-status.md → hustle-api-status.md} +16 -16
- package/commands/{api-verify.md → hustle-api-verify.md} +2 -2
- package/commands/hustle-combine.md +763 -0
- package/commands/hustle-ui-create-page.md +933 -0
- package/commands/hustle-ui-create.md +825 -0
- package/hooks/api-workflow-check.py +545 -21
- package/hooks/cache-research.py +337 -0
- package/hooks/check-api-routes.py +168 -0
- package/hooks/check-playwright-setup.py +103 -0
- package/hooks/check-storybook-setup.py +81 -0
- package/hooks/detect-interruption.py +165 -0
- package/hooks/enforce-a11y-audit.py +202 -0
- package/hooks/enforce-brand-guide.py +241 -0
- package/hooks/enforce-documentation.py +60 -8
- package/hooks/enforce-freshness.py +184 -0
- package/hooks/enforce-page-components.py +186 -0
- package/hooks/enforce-page-data-schema.py +155 -0
- package/hooks/enforce-questions-sourced.py +146 -0
- package/hooks/enforce-schema-from-interview.py +248 -0
- package/hooks/enforce-ui-disambiguation.py +108 -0
- package/hooks/enforce-ui-interview.py +130 -0
- package/hooks/generate-manifest-entry.py +1161 -0
- package/hooks/session-logger.py +297 -0
- package/hooks/session-startup.py +160 -15
- package/hooks/track-scope-coverage.py +220 -0
- package/hooks/track-tool-use.py +81 -1
- package/hooks/update-api-showcase.py +149 -0
- package/hooks/update-registry.py +352 -0
- package/hooks/update-ui-showcase.py +212 -0
- package/package.json +8 -3
- package/templates/BRAND_GUIDE.md +299 -0
- package/templates/CLAUDE-SECTION.md +56 -24
- package/templates/SPEC.json +640 -0
- package/templates/api-dev-state.json +217 -161
- package/templates/api-showcase/_components/APICard.tsx +153 -0
- package/templates/api-showcase/_components/APIModal.tsx +375 -0
- package/templates/api-showcase/_components/APIShowcase.tsx +231 -0
- package/templates/api-showcase/_components/APITester.tsx +522 -0
- package/templates/api-showcase/page.tsx +41 -0
- package/templates/component/Component.stories.tsx +172 -0
- package/templates/component/Component.test.tsx +237 -0
- package/templates/component/Component.tsx +86 -0
- package/templates/component/Component.types.ts +55 -0
- package/templates/component/index.ts +15 -0
- package/templates/dev-tools/_components/DevToolsLanding.tsx +320 -0
- package/templates/dev-tools/page.tsx +10 -0
- package/templates/page/page.e2e.test.ts +218 -0
- package/templates/page/page.tsx +42 -0
- package/templates/performance-budgets.json +58 -0
- package/templates/registry.json +13 -0
- package/templates/settings.json +90 -0
- package/templates/shared/HeroHeader.tsx +261 -0
- package/templates/shared/index.ts +1 -0
- package/templates/ui-showcase/_components/PreviewCard.tsx +315 -0
- package/templates/ui-showcase/_components/PreviewModal.tsx +676 -0
- package/templates/ui-showcase/_components/UIShowcase.tsx +262 -0
- package/templates/ui-showcase/page.tsx +26 -0
- package/demo/hustle-together/blog/gemini-vs-claude-widgets.html +0 -959
- package/demo/hustle-together/blog/interview-driven-api-development.html +0 -1146
- package/demo/hustle-together/blog/tdd-for-ai.html +0 -982
- package/demo/hustle-together/index.html +0 -1312
- package/demo/workflow-demo-v3.5-backup.html +0 -5008
- package/demo/workflow-demo.html +0 -6202
|
@@ -0,0 +1,933 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Create Next.js pages with 13-phase interview-driven workflow
|
|
3
|
+
argument-hint: [page-name]
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Hustle UI Create - Page Mode
|
|
7
|
+
|
|
8
|
+
**Version:** 3.10.0
|
|
9
|
+
**13-phase workflow for creating Next.js App Router pages**
|
|
10
|
+
|
|
11
|
+
You are creating a page using the Hustle Together interview-driven workflow.
|
|
12
|
+
|
|
13
|
+
## Pre-Flight Check
|
|
14
|
+
|
|
15
|
+
Before starting, verify state file exists:
|
|
16
|
+
```bash
|
|
17
|
+
cat .claude/api-dev-state.json 2>/dev/null || echo "Creating new state file"
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Initialize state for page creation:
|
|
21
|
+
```json
|
|
22
|
+
{
|
|
23
|
+
"workflow": "ui-create-page",
|
|
24
|
+
"element_name": "[page-name]",
|
|
25
|
+
"element_type": "page"
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
# Phase 1: DISAMBIGUATION
|
|
32
|
+
|
|
33
|
+
**Goal:** Clarify page type and purpose
|
|
34
|
+
|
|
35
|
+
Ask the user:
|
|
36
|
+
```
|
|
37
|
+
Phase 1: DISAMBIGUATION
|
|
38
|
+
|
|
39
|
+
What type of page is this?
|
|
40
|
+
|
|
41
|
+
A) Landing Page - Marketing/showcase page with hero, features, CTA
|
|
42
|
+
B) Dashboard - Data display with metrics, charts, tables
|
|
43
|
+
C) Form Page - User input form (create, edit, settings)
|
|
44
|
+
D) List/Grid - Display collection of items (search, filter, paginate)
|
|
45
|
+
E) Detail Page - Single item view (profile, product, article)
|
|
46
|
+
F) Auth Page - Login, register, password reset
|
|
47
|
+
G) Other - [describe]
|
|
48
|
+
|
|
49
|
+
Please select A-G:
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
**Wait for user response.**
|
|
53
|
+
|
|
54
|
+
Update state:
|
|
55
|
+
```json
|
|
56
|
+
{
|
|
57
|
+
"phases": {
|
|
58
|
+
"disambiguation": {
|
|
59
|
+
"status": "complete",
|
|
60
|
+
"page_type": "[landing|dashboard|form|list|detail|auth|other]"
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
# Phase 2: SCOPE
|
|
69
|
+
|
|
70
|
+
**Goal:** Confirm understanding of page purpose and route structure
|
|
71
|
+
|
|
72
|
+
Based on the page name and type, present your understanding:
|
|
73
|
+
|
|
74
|
+
```
|
|
75
|
+
Phase 2: SCOPE CONFIRMATION
|
|
76
|
+
|
|
77
|
+
Based on your input, here's my understanding:
|
|
78
|
+
|
|
79
|
+
Page: [Name]
|
|
80
|
+
Type: [Landing/Dashboard/Form/List/Detail/Auth]
|
|
81
|
+
Route: /[name] (or /[name]/[id] for detail pages)
|
|
82
|
+
|
|
83
|
+
Purpose: [Your understanding of what this page does]
|
|
84
|
+
|
|
85
|
+
Expected Sections:
|
|
86
|
+
- [Section 1]
|
|
87
|
+
- [Section 2]
|
|
88
|
+
- [Section 3]
|
|
89
|
+
|
|
90
|
+
Data Requirements:
|
|
91
|
+
- [Data source 1]
|
|
92
|
+
- [Data source 2]
|
|
93
|
+
|
|
94
|
+
Does this match your expectations?
|
|
95
|
+
A) Yes, proceed
|
|
96
|
+
B) No, let me clarify
|
|
97
|
+
|
|
98
|
+
Please select A or B:
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
**Wait for user response. Loop back if B selected.**
|
|
102
|
+
|
|
103
|
+
Update state with `phases.scope.status = "complete"`
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
# Phase 3: DESIGN RESEARCH
|
|
108
|
+
|
|
109
|
+
**Goal:** Research Next.js App Router patterns and check brand guide
|
|
110
|
+
|
|
111
|
+
### Step 3a: Brand Guide Check
|
|
112
|
+
|
|
113
|
+
```
|
|
114
|
+
Phase 3: DESIGN RESEARCH
|
|
115
|
+
|
|
116
|
+
I found your brand guide at .claude/BRAND_GUIDE.md
|
|
117
|
+
|
|
118
|
+
If you need to update your brand guide, NOW is the time!
|
|
119
|
+
1. Open .claude/BRAND_GUIDE.md
|
|
120
|
+
2. Make your changes
|
|
121
|
+
3. Save the file
|
|
122
|
+
4. Then select option A below
|
|
123
|
+
|
|
124
|
+
Should I use the project brand guide?
|
|
125
|
+
A) Yes, use brand guide (default)
|
|
126
|
+
B) No, use different reference [provide URL or description]
|
|
127
|
+
|
|
128
|
+
Please select A or B:
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
**Wait for user response.**
|
|
132
|
+
|
|
133
|
+
### Step 3b: Next.js Pattern Research
|
|
134
|
+
|
|
135
|
+
Perform 2-3 targeted searches based on page type:
|
|
136
|
+
|
|
137
|
+
**For Landing Pages:**
|
|
138
|
+
1. `Next.js 15 landing page best practices`
|
|
139
|
+
2. `shadcn landing page components hero section`
|
|
140
|
+
3. `responsive landing page layout App Router`
|
|
141
|
+
|
|
142
|
+
**For Dashboards:**
|
|
143
|
+
1. `Next.js 15 dashboard layout App Router`
|
|
144
|
+
2. `shadcn dashboard components data table`
|
|
145
|
+
3. `Recharts React charts dashboard`
|
|
146
|
+
|
|
147
|
+
**For Form Pages:**
|
|
148
|
+
1. `Next.js 15 server actions form handling`
|
|
149
|
+
2. `react-hook-form zod validation`
|
|
150
|
+
3. `shadcn form components`
|
|
151
|
+
|
|
152
|
+
**For List Pages:**
|
|
153
|
+
1. `Next.js 15 pagination App Router`
|
|
154
|
+
2. `shadcn data table filtering sorting`
|
|
155
|
+
3. `server-side pagination searchParams`
|
|
156
|
+
|
|
157
|
+
**For Detail Pages:**
|
|
158
|
+
1. `Next.js 15 dynamic routes [id] App Router`
|
|
159
|
+
2. `generateStaticParams ISR`
|
|
160
|
+
3. `optimistic updates detail page`
|
|
161
|
+
|
|
162
|
+
Use Context7 for Next.js and ShadCN documentation.
|
|
163
|
+
|
|
164
|
+
Document findings in state:
|
|
165
|
+
```json
|
|
166
|
+
{
|
|
167
|
+
"phases": {
|
|
168
|
+
"design_research": {
|
|
169
|
+
"status": "complete",
|
|
170
|
+
"brand_guide_applied": true,
|
|
171
|
+
"sources": ["source1", "source2"],
|
|
172
|
+
"patterns_found": ["pattern1", "pattern2"]
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
# Phase 4: INTERVIEW
|
|
181
|
+
|
|
182
|
+
**Goal:** Deep dive into page requirements
|
|
183
|
+
|
|
184
|
+
Ask 5-10 questions based on research findings. Questions should be GENERATED from research, not templates.
|
|
185
|
+
|
|
186
|
+
**Example questions for Dashboard page:**
|
|
187
|
+
|
|
188
|
+
```
|
|
189
|
+
Phase 4: INTERVIEW
|
|
190
|
+
|
|
191
|
+
Based on my research, I have some questions about your [Page]:
|
|
192
|
+
|
|
193
|
+
Q1: Data Fetching Strategy
|
|
194
|
+
How should data be fetched?
|
|
195
|
+
A) Server Components - Fetch on server (recommended for static data)
|
|
196
|
+
B) Client Components - Fetch with SWR/TanStack Query (real-time updates)
|
|
197
|
+
C) Hybrid - Server for initial, client for updates
|
|
198
|
+
|
|
199
|
+
Q2: Layout Structure
|
|
200
|
+
What layout should this page use?
|
|
201
|
+
A) Full width - No sidebar, content spans full width
|
|
202
|
+
B) Sidebar layout - Fixed sidebar with scrollable content
|
|
203
|
+
C) Nested layout - Inherit from parent layout
|
|
204
|
+
|
|
205
|
+
Q3: Authentication
|
|
206
|
+
What authentication is required?
|
|
207
|
+
A) Public - No auth required
|
|
208
|
+
B) Protected - Must be logged in
|
|
209
|
+
C) Role-based - Specific roles only
|
|
210
|
+
|
|
211
|
+
Q4: SEO Requirements
|
|
212
|
+
What SEO metadata is needed?
|
|
213
|
+
A) Basic - Title and description
|
|
214
|
+
B) Full - Open Graph, Twitter cards, JSON-LD
|
|
215
|
+
C) None - Dashboard/internal page
|
|
216
|
+
|
|
217
|
+
Q5: [Research-derived question about specific feature]
|
|
218
|
+
|
|
219
|
+
Q6: [Research-derived question about specific feature]
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
**Wait for all answers before proceeding.**
|
|
223
|
+
|
|
224
|
+
Store all decisions in state:
|
|
225
|
+
```json
|
|
226
|
+
{
|
|
227
|
+
"phases": {
|
|
228
|
+
"interview": {
|
|
229
|
+
"status": "complete",
|
|
230
|
+
"decisions": {
|
|
231
|
+
"data_fetching": "server",
|
|
232
|
+
"layout": "sidebar",
|
|
233
|
+
"auth": "protected",
|
|
234
|
+
"seo": "basic"
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
|
|
243
|
+
# Phase 5: PAGE ANALYSIS
|
|
244
|
+
|
|
245
|
+
**Goal:** Check registry for reusable components
|
|
246
|
+
|
|
247
|
+
Check existing components in registry:
|
|
248
|
+
|
|
249
|
+
```bash
|
|
250
|
+
cat .claude/registry.json | jq '.components'
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
Also check for existing pages with similar patterns:
|
|
254
|
+
|
|
255
|
+
```bash
|
|
256
|
+
cat .claude/registry.json | jq '.pages'
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
Present findings:
|
|
260
|
+
```
|
|
261
|
+
Phase 5: PAGE ANALYSIS
|
|
262
|
+
|
|
263
|
+
Existing Components Available:
|
|
264
|
+
|
|
265
|
+
From Registry:
|
|
266
|
+
- Button (primary, secondary, destructive)
|
|
267
|
+
- Card (header, content, footer)
|
|
268
|
+
- DataTable (sorting, filtering, pagination)
|
|
269
|
+
- Modal (confirm, form, custom)
|
|
270
|
+
|
|
271
|
+
Your [Page] could use:
|
|
272
|
+
- [Component 1] - for [purpose]
|
|
273
|
+
- [Component 2] - for [purpose]
|
|
274
|
+
- [Component 3] - for [purpose]
|
|
275
|
+
|
|
276
|
+
Would you like to use these existing components?
|
|
277
|
+
A) Yes, use all recommended
|
|
278
|
+
B) Some - let me specify which
|
|
279
|
+
C) None - create new components
|
|
280
|
+
|
|
281
|
+
Please select:
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
**Wait for user response.**
|
|
285
|
+
|
|
286
|
+
Update state with selected components list.
|
|
287
|
+
|
|
288
|
+
---
|
|
289
|
+
|
|
290
|
+
# Phase 6: DATA SCHEMA
|
|
291
|
+
|
|
292
|
+
**Goal:** Define TypeScript interfaces for page data
|
|
293
|
+
|
|
294
|
+
Based on interview decisions, create data schemas:
|
|
295
|
+
|
|
296
|
+
```typescript
|
|
297
|
+
// src/app/[name]/_types/index.ts
|
|
298
|
+
|
|
299
|
+
// API Response Types
|
|
300
|
+
export interface [Name]PageData {
|
|
301
|
+
/** Page title */
|
|
302
|
+
title: string;
|
|
303
|
+
|
|
304
|
+
/** Main content items */
|
|
305
|
+
items: [Name]Item[];
|
|
306
|
+
|
|
307
|
+
/** Pagination info */
|
|
308
|
+
pagination: {
|
|
309
|
+
page: number;
|
|
310
|
+
totalPages: number;
|
|
311
|
+
totalItems: number;
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
export interface [Name]Item {
|
|
316
|
+
id: string;
|
|
317
|
+
// ... other fields based on interview
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Form Data Types (if applicable)
|
|
321
|
+
export interface [Name]FormData {
|
|
322
|
+
// ... form fields based on interview
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// Search/Filter Types (if applicable)
|
|
326
|
+
export interface [Name]Filters {
|
|
327
|
+
search?: string;
|
|
328
|
+
sortBy?: string;
|
|
329
|
+
sortOrder?: 'asc' | 'desc';
|
|
330
|
+
// ... other filters
|
|
331
|
+
}
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
Present to user:
|
|
335
|
+
```
|
|
336
|
+
Phase 6: DATA SCHEMA
|
|
337
|
+
|
|
338
|
+
Here are the TypeScript interfaces I've created:
|
|
339
|
+
|
|
340
|
+
[Show interfaces]
|
|
341
|
+
|
|
342
|
+
Do these schemas look correct?
|
|
343
|
+
A) Yes, proceed
|
|
344
|
+
B) No, needs changes [specify]
|
|
345
|
+
|
|
346
|
+
Please select:
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
**Wait for approval before proceeding.**
|
|
350
|
+
|
|
351
|
+
---
|
|
352
|
+
|
|
353
|
+
# Phase 7: ENVIRONMENT
|
|
354
|
+
|
|
355
|
+
**Goal:** Verify API routes and required packages
|
|
356
|
+
|
|
357
|
+
### Check API Routes
|
|
358
|
+
|
|
359
|
+
```bash
|
|
360
|
+
ls -la src/app/api/v2/ 2>/dev/null | head -20
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
If page requires API routes that don't exist:
|
|
364
|
+
```
|
|
365
|
+
Phase 7: ENVIRONMENT CHECK
|
|
366
|
+
|
|
367
|
+
API Routes Required:
|
|
368
|
+
GET /api/v2/[name] - [Exists/Missing]
|
|
369
|
+
POST /api/v2/[name] - [Exists/Missing]
|
|
370
|
+
PUT /api/v2/[name]/:id - [Exists/Missing]
|
|
371
|
+
|
|
372
|
+
Missing routes found! Options:
|
|
373
|
+
A) Create them now using /api-create
|
|
374
|
+
B) Skip - I'll create them later
|
|
375
|
+
C) Use different API - [specify]
|
|
376
|
+
|
|
377
|
+
Please select:
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
### Check Auth Configuration
|
|
381
|
+
|
|
382
|
+
```bash
|
|
383
|
+
grep -r "middleware" src/ 2>/dev/null | head -5
|
|
384
|
+
cat middleware.ts 2>/dev/null | head -20
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
### Check Required Packages
|
|
388
|
+
|
|
389
|
+
```bash
|
|
390
|
+
cat package.json | jq '.dependencies, .devDependencies' | grep -E "next-auth|@tanstack|swr|recharts|@playwright"
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
Report status:
|
|
394
|
+
```
|
|
395
|
+
Environment Check:
|
|
396
|
+
API Routes: [X/Y available]
|
|
397
|
+
Auth Config: [Configured/Not configured]
|
|
398
|
+
Playwright: [Installed/Not installed]
|
|
399
|
+
|
|
400
|
+
Ready to proceed with TDD?
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
---
|
|
404
|
+
|
|
405
|
+
# Phase 8: TDD RED
|
|
406
|
+
|
|
407
|
+
**Goal:** Write failing Playwright E2E tests
|
|
408
|
+
|
|
409
|
+
### Create Test File
|
|
410
|
+
|
|
411
|
+
```typescript
|
|
412
|
+
// src/app/[name]/__tests__/[name].e2e.test.ts
|
|
413
|
+
|
|
414
|
+
import { test, expect } from '@playwright/test';
|
|
415
|
+
|
|
416
|
+
test.describe('[Name] Page', () => {
|
|
417
|
+
test.beforeEach(async ({ page }) => {
|
|
418
|
+
await page.goto('/[name]');
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
// Basic Rendering Tests
|
|
422
|
+
test('page loads successfully', async ({ page }) => {
|
|
423
|
+
await expect(page).toHaveTitle(/[Name]/);
|
|
424
|
+
await expect(page.locator('h1')).toContainText('[Expected Title]');
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
test('displays main content section', async ({ page }) => {
|
|
428
|
+
await expect(page.getByRole('main')).toBeVisible();
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
// Data Display Tests (for List/Dashboard)
|
|
432
|
+
test('displays data items', async ({ page }) => {
|
|
433
|
+
await expect(page.locator('[data-testid="item-card"]')).toHaveCount.greaterThan(0);
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
// Form Tests (for Form pages)
|
|
437
|
+
test('form validation works', async ({ page }) => {
|
|
438
|
+
await page.click('button[type="submit"]');
|
|
439
|
+
await expect(page.getByText('Required field')).toBeVisible();
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
test('form submission works', async ({ page }) => {
|
|
443
|
+
await page.fill('input[name="email"]', 'test@example.com');
|
|
444
|
+
await page.click('button[type="submit"]');
|
|
445
|
+
await expect(page.getByText('Success')).toBeVisible();
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
// Navigation Tests
|
|
449
|
+
test('navigation works correctly', async ({ page }) => {
|
|
450
|
+
await page.click('a[href="/[name]/details"]');
|
|
451
|
+
await expect(page).toHaveURL(/\/[name]\/details/);
|
|
452
|
+
});
|
|
453
|
+
|
|
454
|
+
// Responsive Tests
|
|
455
|
+
test('mobile layout works', async ({ page }) => {
|
|
456
|
+
await page.setViewportSize({ width: 375, height: 667 });
|
|
457
|
+
await expect(page.getByRole('navigation')).toBeVisible();
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
// Auth Tests (if protected)
|
|
461
|
+
test('redirects to login when unauthenticated', async ({ page }) => {
|
|
462
|
+
// Clear auth state
|
|
463
|
+
await page.context().clearCookies();
|
|
464
|
+
await page.goto('/[name]');
|
|
465
|
+
await expect(page).toHaveURL(/\/login/);
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
// Performance Tests
|
|
469
|
+
test('page loads within performance budget', async ({ page }) => {
|
|
470
|
+
const startTime = Date.now();
|
|
471
|
+
await page.goto('/[name]');
|
|
472
|
+
await page.waitForLoadState('networkidle');
|
|
473
|
+
const loadTime = Date.now() - startTime;
|
|
474
|
+
expect(loadTime).toBeLessThan(3000); // 3 second budget
|
|
475
|
+
});
|
|
476
|
+
});
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
### Run Tests (Expect Failure)
|
|
480
|
+
|
|
481
|
+
```bash
|
|
482
|
+
pnpm playwright test src/app/[name]
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
**Tests MUST fail at this point (page doesn't exist yet).**
|
|
486
|
+
|
|
487
|
+
Update state: `phases.tdd_red.status = "complete"`
|
|
488
|
+
|
|
489
|
+
---
|
|
490
|
+
|
|
491
|
+
# Phase 9: TDD GREEN
|
|
492
|
+
|
|
493
|
+
**Goal:** Implement page to pass tests
|
|
494
|
+
|
|
495
|
+
### Create Page Structure
|
|
496
|
+
|
|
497
|
+
```
|
|
498
|
+
src/app/[name]/
|
|
499
|
+
├── page.tsx # Main page component
|
|
500
|
+
├── layout.tsx # Optional layout
|
|
501
|
+
├── loading.tsx # Loading state
|
|
502
|
+
├── error.tsx # Error boundary
|
|
503
|
+
├── _components/ # Page-specific components
|
|
504
|
+
│ ├── [Name]Header.tsx
|
|
505
|
+
│ ├── [Name]Content.tsx
|
|
506
|
+
│ └── [Name]Sidebar.tsx
|
|
507
|
+
├── _types/
|
|
508
|
+
│ └── index.ts # TypeScript interfaces
|
|
509
|
+
├── _lib/
|
|
510
|
+
│ └── actions.ts # Server actions (if needed)
|
|
511
|
+
└── __tests__/
|
|
512
|
+
└── [name].e2e.test.ts
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
### Create Main Page
|
|
516
|
+
|
|
517
|
+
```typescript
|
|
518
|
+
// src/app/[name]/page.tsx
|
|
519
|
+
|
|
520
|
+
import { Metadata } from 'next';
|
|
521
|
+
import { [Name]Header } from './_components/[Name]Header';
|
|
522
|
+
import { [Name]Content } from './_components/[Name]Content';
|
|
523
|
+
|
|
524
|
+
export const metadata: Metadata = {
|
|
525
|
+
title: '[Name] | Your App',
|
|
526
|
+
description: '[Page description from interview]',
|
|
527
|
+
};
|
|
528
|
+
|
|
529
|
+
// Server Component - data fetching
|
|
530
|
+
async function get[Name]Data() {
|
|
531
|
+
const res = await fetch(`${process.env.API_URL}/api/v2/[name]`, {
|
|
532
|
+
cache: 'no-store', // or 'force-cache' based on interview
|
|
533
|
+
});
|
|
534
|
+
return res.json();
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
export default async function [Name]Page() {
|
|
538
|
+
const data = await get[Name]Data();
|
|
539
|
+
|
|
540
|
+
return (
|
|
541
|
+
<main className="container mx-auto py-8">
|
|
542
|
+
<[Name]Header />
|
|
543
|
+
<[Name]Content data={data} />
|
|
544
|
+
</main>
|
|
545
|
+
);
|
|
546
|
+
}
|
|
547
|
+
```
|
|
548
|
+
|
|
549
|
+
### Create Loading State
|
|
550
|
+
|
|
551
|
+
```typescript
|
|
552
|
+
// src/app/[name]/loading.tsx
|
|
553
|
+
|
|
554
|
+
import { Skeleton } from '@/components/ui/skeleton';
|
|
555
|
+
|
|
556
|
+
export default function [Name]Loading() {
|
|
557
|
+
return (
|
|
558
|
+
<main className="container mx-auto py-8">
|
|
559
|
+
<Skeleton className="h-12 w-64 mb-8" />
|
|
560
|
+
<div className="grid gap-4">
|
|
561
|
+
<Skeleton className="h-32 w-full" />
|
|
562
|
+
<Skeleton className="h-32 w-full" />
|
|
563
|
+
<Skeleton className="h-32 w-full" />
|
|
564
|
+
</div>
|
|
565
|
+
</main>
|
|
566
|
+
);
|
|
567
|
+
}
|
|
568
|
+
```
|
|
569
|
+
|
|
570
|
+
### Create Error Boundary
|
|
571
|
+
|
|
572
|
+
```typescript
|
|
573
|
+
// src/app/[name]/error.tsx
|
|
574
|
+
|
|
575
|
+
'use client';
|
|
576
|
+
|
|
577
|
+
import { useEffect } from 'react';
|
|
578
|
+
import { Button } from '@/components/ui/button';
|
|
579
|
+
|
|
580
|
+
export default function [Name]Error({
|
|
581
|
+
error,
|
|
582
|
+
reset,
|
|
583
|
+
}: {
|
|
584
|
+
error: Error & { digest?: string };
|
|
585
|
+
reset: () => void;
|
|
586
|
+
}) {
|
|
587
|
+
useEffect(() => {
|
|
588
|
+
console.error(error);
|
|
589
|
+
}, [error]);
|
|
590
|
+
|
|
591
|
+
return (
|
|
592
|
+
<main className="container mx-auto py-8 text-center">
|
|
593
|
+
<h2 className="text-2xl font-bold mb-4">Something went wrong!</h2>
|
|
594
|
+
<p className="text-muted-foreground mb-4">{error.message}</p>
|
|
595
|
+
<Button onClick={() => reset()}>Try again</Button>
|
|
596
|
+
</main>
|
|
597
|
+
);
|
|
598
|
+
}
|
|
599
|
+
```
|
|
600
|
+
|
|
601
|
+
### Run Tests (Expect Pass)
|
|
602
|
+
|
|
603
|
+
```bash
|
|
604
|
+
pnpm playwright test src/app/[name]
|
|
605
|
+
```
|
|
606
|
+
|
|
607
|
+
**All tests MUST pass before proceeding.**
|
|
608
|
+
|
|
609
|
+
Update state: `phases.tdd_green.status = "complete"`
|
|
610
|
+
|
|
611
|
+
---
|
|
612
|
+
|
|
613
|
+
# Phase 10: VERIFY (4-STEP MANDATORY)
|
|
614
|
+
|
|
615
|
+
**Goal:** Comprehensive verification across 4 dimensions
|
|
616
|
+
|
|
617
|
+
### Step 1: Responsive Check
|
|
618
|
+
|
|
619
|
+
Run Playwright at different viewports:
|
|
620
|
+
|
|
621
|
+
```bash
|
|
622
|
+
pnpm playwright test src/app/[name] --project=mobile --project=tablet --project=desktop
|
|
623
|
+
```
|
|
624
|
+
|
|
625
|
+
```
|
|
626
|
+
Step 1: Responsive Check
|
|
627
|
+
|
|
628
|
+
Desktop (1920px): [Checking...]
|
|
629
|
+
Tablet (768px): [Checking...]
|
|
630
|
+
Mobile (375px): [Checking...]
|
|
631
|
+
```
|
|
632
|
+
|
|
633
|
+
### Step 2: Data Flow Check
|
|
634
|
+
|
|
635
|
+
Verify data flows correctly from API to UI:
|
|
636
|
+
|
|
637
|
+
```
|
|
638
|
+
Step 2: Data Flow Check
|
|
639
|
+
|
|
640
|
+
API Response: [Valid JSON]
|
|
641
|
+
Server Component: [Data received]
|
|
642
|
+
Client Render: [Data displayed]
|
|
643
|
+
Error Handling: [Error boundary works]
|
|
644
|
+
Loading State: [Skeleton shows]
|
|
645
|
+
```
|
|
646
|
+
|
|
647
|
+
### Step 3: All Tests Passed
|
|
648
|
+
|
|
649
|
+
```
|
|
650
|
+
Step 3: Test Results
|
|
651
|
+
|
|
652
|
+
E2E tests: [X/X passed]
|
|
653
|
+
Navigation tests: [X/X passed]
|
|
654
|
+
Form tests: [X/X passed] (if applicable)
|
|
655
|
+
Auth tests: [X/X passed] (if applicable)
|
|
656
|
+
```
|
|
657
|
+
|
|
658
|
+
### Step 4: Performance Metrics
|
|
659
|
+
|
|
660
|
+
Check page performance:
|
|
661
|
+
|
|
662
|
+
```bash
|
|
663
|
+
pnpm lighthouse http://localhost:3000/[name] --output json
|
|
664
|
+
```
|
|
665
|
+
|
|
666
|
+
```
|
|
667
|
+
Step 4: Performance Metrics
|
|
668
|
+
|
|
669
|
+
First Contentful Paint: [< 1.8s]
|
|
670
|
+
Largest Contentful Paint: [< 2.5s]
|
|
671
|
+
Time to Interactive: [< 3.8s]
|
|
672
|
+
Total Blocking Time: [< 200ms]
|
|
673
|
+
Cumulative Layout Shift: [< 0.1]
|
|
674
|
+
```
|
|
675
|
+
|
|
676
|
+
**Present 4-step results:**
|
|
677
|
+
```
|
|
678
|
+
Phase 10: VERIFICATION (4-Step)
|
|
679
|
+
|
|
680
|
+
Step 1: Responsive Check
|
|
681
|
+
Desktop (1920px) - Renders correctly
|
|
682
|
+
Tablet (768px) - Renders correctly
|
|
683
|
+
Mobile (375px) - Renders correctly
|
|
684
|
+
|
|
685
|
+
Step 2: Data Flow Check
|
|
686
|
+
API → Server → Client flow verified
|
|
687
|
+
Error boundary tested
|
|
688
|
+
Loading state works
|
|
689
|
+
|
|
690
|
+
Step 3: All Tests Passed
|
|
691
|
+
E2E tests: 15/15 passed
|
|
692
|
+
Coverage: Core user flows covered
|
|
693
|
+
|
|
694
|
+
Step 4: Performance Metrics
|
|
695
|
+
LCP: 1.2s (Good)
|
|
696
|
+
FCP: 0.8s (Good)
|
|
697
|
+
CLS: 0.02 (Good)
|
|
698
|
+
|
|
699
|
+
All 4 checks passed!
|
|
700
|
+
|
|
701
|
+
Any issues to fix?
|
|
702
|
+
A) No, all good - proceed
|
|
703
|
+
B) Yes, need to fix [specify]
|
|
704
|
+
```
|
|
705
|
+
|
|
706
|
+
**Wait for user response. Loop back if issues found.**
|
|
707
|
+
|
|
708
|
+
---
|
|
709
|
+
|
|
710
|
+
# Phase 11: TDD REFACTOR
|
|
711
|
+
|
|
712
|
+
**Goal:** Clean up code while tests pass
|
|
713
|
+
|
|
714
|
+
Refactoring checklist:
|
|
715
|
+
- [ ] Extract repeated components to `_components/`
|
|
716
|
+
- [ ] Move data fetching to dedicated functions
|
|
717
|
+
- [ ] Extract server actions to `_lib/actions.ts`
|
|
718
|
+
- [ ] Optimize images with next/image
|
|
719
|
+
- [ ] Add proper error boundaries
|
|
720
|
+
- [ ] Implement proper loading states
|
|
721
|
+
- [ ] Extract types to `_types/`
|
|
722
|
+
|
|
723
|
+
Run tests after each refactor:
|
|
724
|
+
```bash
|
|
725
|
+
pnpm playwright test src/app/[name]
|
|
726
|
+
```
|
|
727
|
+
|
|
728
|
+
---
|
|
729
|
+
|
|
730
|
+
# Phase 12: DOCUMENTATION
|
|
731
|
+
|
|
732
|
+
**Goal:** Complete all documentation
|
|
733
|
+
|
|
734
|
+
### Page Documentation
|
|
735
|
+
|
|
736
|
+
Create or update:
|
|
737
|
+
```typescript
|
|
738
|
+
// src/app/[name]/README.md (optional)
|
|
739
|
+
|
|
740
|
+
# [Name] Page
|
|
741
|
+
|
|
742
|
+
## Route
|
|
743
|
+
`/[name]`
|
|
744
|
+
|
|
745
|
+
## Purpose
|
|
746
|
+
[From interview]
|
|
747
|
+
|
|
748
|
+
## Data Requirements
|
|
749
|
+
- API: `/api/v2/[name]`
|
|
750
|
+
- Auth: [Protected/Public]
|
|
751
|
+
|
|
752
|
+
## Components Used
|
|
753
|
+
- [Component 1]
|
|
754
|
+
- [Component 2]
|
|
755
|
+
|
|
756
|
+
## Testing
|
|
757
|
+
```bash
|
|
758
|
+
pnpm playwright test src/app/[name]
|
|
759
|
+
```
|
|
760
|
+
```
|
|
761
|
+
|
|
762
|
+
### Registry Entry
|
|
763
|
+
|
|
764
|
+
Update `.claude/registry.json`:
|
|
765
|
+
```json
|
|
766
|
+
{
|
|
767
|
+
"pages": {
|
|
768
|
+
"[name]": {
|
|
769
|
+
"name": "[Name]",
|
|
770
|
+
"description": "[From interview]",
|
|
771
|
+
"route": "/[name]",
|
|
772
|
+
"file": "src/app/[name]/page.tsx",
|
|
773
|
+
"type": "[landing|dashboard|form|list|detail|auth]",
|
|
774
|
+
"tests": "src/app/[name]/__tests__/",
|
|
775
|
+
"components_used": ["Button", "Card", "DataTable"],
|
|
776
|
+
"data_sources": ["/api/v2/[name]"],
|
|
777
|
+
"auth_required": true,
|
|
778
|
+
"seo": {
|
|
779
|
+
"title": "[Name] | App",
|
|
780
|
+
"description": "[Description]"
|
|
781
|
+
},
|
|
782
|
+
"status": "complete",
|
|
783
|
+
"created_at": "[date]"
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
```
|
|
788
|
+
|
|
789
|
+
Present checklist:
|
|
790
|
+
```
|
|
791
|
+
Phase 12: DOCUMENTATION
|
|
792
|
+
|
|
793
|
+
Page README: [Complete]
|
|
794
|
+
Route documented: [Complete]
|
|
795
|
+
Registry updated: [Complete]
|
|
796
|
+
SEO metadata: [Complete]
|
|
797
|
+
|
|
798
|
+
Documentation complete?
|
|
799
|
+
A) Yes, proceed to completion
|
|
800
|
+
B) No, need changes
|
|
801
|
+
```
|
|
802
|
+
|
|
803
|
+
---
|
|
804
|
+
|
|
805
|
+
# Phase 13: COMPLETION
|
|
806
|
+
|
|
807
|
+
**Goal:** Final output and showcase integration
|
|
808
|
+
|
|
809
|
+
### Update UI Showcase
|
|
810
|
+
|
|
811
|
+
Page is auto-added to UI Showcase via registry update hook.
|
|
812
|
+
|
|
813
|
+
### Final Output
|
|
814
|
+
|
|
815
|
+
```
|
|
816
|
+
[Name] page complete!
|
|
817
|
+
|
|
818
|
+
Created Files:
|
|
819
|
+
- src/app/[name]/page.tsx
|
|
820
|
+
- src/app/[name]/layout.tsx (if needed)
|
|
821
|
+
- src/app/[name]/loading.tsx
|
|
822
|
+
- src/app/[name]/error.tsx
|
|
823
|
+
- src/app/[name]/_components/[Name]Header.tsx
|
|
824
|
+
- src/app/[name]/_components/[Name]Content.tsx
|
|
825
|
+
- src/app/[name]/_types/index.ts
|
|
826
|
+
- src/app/[name]/__tests__/[name].e2e.test.ts
|
|
827
|
+
|
|
828
|
+
Route: /[name]
|
|
829
|
+
Auth: [Protected/Public]
|
|
830
|
+
|
|
831
|
+
Tests: All 15 E2E tests passed
|
|
832
|
+
Performance: LCP 1.2s, FCP 0.8s (within budget)
|
|
833
|
+
|
|
834
|
+
Registry: Added to .claude/registry.json
|
|
835
|
+
|
|
836
|
+
Would you like to create another page or component?
|
|
837
|
+
```
|
|
838
|
+
|
|
839
|
+
### Showcase Redirect
|
|
840
|
+
|
|
841
|
+
```
|
|
842
|
+
Your page has been added to the showcase!
|
|
843
|
+
|
|
844
|
+
View it at: http://localhost:3000/ui-showcase
|
|
845
|
+
|
|
846
|
+
The page preview card shows:
|
|
847
|
+
- Screenshot
|
|
848
|
+
- Route link
|
|
849
|
+
- Data sources
|
|
850
|
+
- Components used
|
|
851
|
+
|
|
852
|
+
Run `pnpm dev` and navigate to /ui-showcase to see it.
|
|
853
|
+
```
|
|
854
|
+
|
|
855
|
+
Update state: `phases.completion.status = "complete"`
|
|
856
|
+
|
|
857
|
+
---
|
|
858
|
+
|
|
859
|
+
# State File Structure
|
|
860
|
+
|
|
861
|
+
```json
|
|
862
|
+
{
|
|
863
|
+
"version": "3.10.0",
|
|
864
|
+
"workflow": "ui-create-page",
|
|
865
|
+
"active_element": "[name]",
|
|
866
|
+
"elements": {
|
|
867
|
+
"[name]": {
|
|
868
|
+
"type": "page",
|
|
869
|
+
"status": "in_progress",
|
|
870
|
+
"started_at": "2025-12-12T15:30:00Z",
|
|
871
|
+
"ui_config": {
|
|
872
|
+
"mode": "page",
|
|
873
|
+
"page_type": "dashboard",
|
|
874
|
+
"route": "/[name]",
|
|
875
|
+
"auth_required": true,
|
|
876
|
+
"data_fetching": "server",
|
|
877
|
+
"layout": "sidebar",
|
|
878
|
+
"seo_level": "full"
|
|
879
|
+
},
|
|
880
|
+
"phases": {
|
|
881
|
+
"disambiguation": { "status": "complete", "page_type": "dashboard" },
|
|
882
|
+
"scope": { "status": "complete" },
|
|
883
|
+
"design_research": { "status": "complete", "brand_guide_applied": true },
|
|
884
|
+
"interview": { "status": "complete", "decisions": {} },
|
|
885
|
+
"page_analysis": { "status": "complete", "components_selected": [] },
|
|
886
|
+
"data_schema": { "status": "complete", "schema_file": "..." },
|
|
887
|
+
"environment_check": { "status": "complete", "api_routes_verified": true },
|
|
888
|
+
"tdd_red": { "status": "complete", "tests_written": 15 },
|
|
889
|
+
"tdd_green": { "status": "complete", "tests_passed": 15 },
|
|
890
|
+
"verify": { "status": "complete", "four_step_passed": true },
|
|
891
|
+
"tdd_refactor": { "status": "complete" },
|
|
892
|
+
"documentation": { "status": "complete" },
|
|
893
|
+
"completion": { "status": "complete" }
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
```
|
|
899
|
+
|
|
900
|
+
---
|
|
901
|
+
|
|
902
|
+
# Hooks That Enforce This Workflow
|
|
903
|
+
|
|
904
|
+
| Phase | Hook | Purpose |
|
|
905
|
+
|-------|------|---------|
|
|
906
|
+
| 1 | `enforce-ui-disambiguation.py` | Validates page type selection |
|
|
907
|
+
| 3 | `enforce-brand-guide.py` | Ensures brand guide is checked |
|
|
908
|
+
| 4 | `enforce-ui-interview.py` | Injects interview decisions |
|
|
909
|
+
| 5 | `enforce-page-components.py` | Checks registry for components |
|
|
910
|
+
| 6 | `enforce-page-data-schema.py` | Validates data types defined |
|
|
911
|
+
| 7 | `check-api-routes.py` | Verifies required API routes |
|
|
912
|
+
| 8 | `check-playwright-setup.py` | Ensures Playwright is configured |
|
|
913
|
+
| 10 | `verify-after-green.py` | Triggers 4-step verification |
|
|
914
|
+
| 12 | `update-registry.py` | Adds page to registry |
|
|
915
|
+
| 12 | `update-ui-showcase.py` | Updates showcase |
|
|
916
|
+
| 13 | `api-workflow-check.py` | Blocks if incomplete |
|
|
917
|
+
|
|
918
|
+
---
|
|
919
|
+
|
|
920
|
+
# Key Principles
|
|
921
|
+
|
|
922
|
+
1. **ALWAYS ask user** - Never proceed without explicit response
|
|
923
|
+
2. **Brand guide first** - Check and apply before implementation
|
|
924
|
+
3. **Component reuse** - Check registry before creating new components
|
|
925
|
+
4. **4-Step verification** - All 4 checks MUST pass
|
|
926
|
+
5. **Playwright E2E** - Full page tests, not unit tests
|
|
927
|
+
6. **Performance budget** - LCP < 2.5s, FCP < 1.8s
|
|
928
|
+
7. **Showcase link guaranteed** - Always output at completion
|
|
929
|
+
|
|
930
|
+
---
|
|
931
|
+
|
|
932
|
+
**Version:** 3.10.0
|
|
933
|
+
**Last Updated:** 2025-12-12
|