@polymorphism-tech/morph-spec 4.10.7 → 4.10.8

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 (122) hide show
  1. package/CLAUDE.md +3 -3
  2. package/LICENSE +71 -71
  3. package/README.md +379 -379
  4. package/bin/morph-spec.js +1 -1
  5. package/claude-plugin.json +14 -14
  6. package/docs/CHEATSHEET.md +1 -1
  7. package/docs/QUICKSTART.md +206 -206
  8. package/framework/agents/README.md +14 -14
  9. package/framework/agents/architecture/standards-architect.md +159 -159
  10. package/framework/agents/frontend/blazor-builder.md +13 -0
  11. package/framework/agents/frontend/css-specialist.md +87 -0
  12. package/framework/agents/frontend/nextjs-expert.md +13 -0
  13. package/framework/agents/frontend/nextjs-ui-specialist.md +218 -0
  14. package/framework/agents/frontend/ui-ux-designer.md +80 -10
  15. package/framework/agents/infrastructure/azure-architect.md +149 -149
  16. package/framework/agents/infrastructure/infra-architect.md +48 -48
  17. package/framework/agents.json +2268 -2204
  18. package/framework/hooks/README.md +204 -204
  19. package/framework/hooks/claude-code/session-start/inject-morph-context.js +10 -24
  20. package/framework/hooks/shared/phase-utils.js +139 -139
  21. package/framework/hooks/shared/worktree-helpers.js +30 -8
  22. package/framework/rules/csharp-standards.md +13 -13
  23. package/framework/rules/frontend-standards.md +13 -13
  24. package/framework/rules/infrastructure-standards.md +16 -16
  25. package/framework/rules/testing-standards.md +14 -14
  26. package/framework/skills/level-0-meta/morph-brainstorming/SKILL.md +12 -0
  27. package/framework/skills/level-0-meta/morph-code-review-nextjs/references/review-example-nextjs.md +254 -254
  28. package/framework/skills/level-0-meta/morph-init/SKILL.md +88 -3
  29. package/framework/skills/level-1-workflows/morph-phase-clarify/SKILL.md +247 -247
  30. package/framework/skills/level-1-workflows/morph-phase-codebase-analysis/SKILL.md +270 -270
  31. package/framework/skills/level-1-workflows/morph-phase-design/SKILL.md +515 -499
  32. package/framework/skills/level-1-workflows/morph-phase-implement/SKILL.md +508 -500
  33. package/framework/skills/level-1-workflows/morph-phase-plan/SKILL.md +12 -1
  34. package/framework/skills/level-1-workflows/morph-phase-setup/SKILL.md +272 -262
  35. package/framework/skills/level-1-workflows/morph-phase-tasks/SKILL.md +317 -311
  36. package/framework/skills/level-1-workflows/morph-phase-uiux/SKILL.md +424 -323
  37. package/framework/skills/level-1-workflows/morph-scope-escalation/SKILL.md +1 -1
  38. package/framework/standards/STANDARDS.json +2040 -2040
  39. package/framework/standards/frontend/nextjs/motion-patterns.md +321 -0
  40. package/framework/standards/frontend/nextjs/nextjs-patterns.md +17 -17
  41. package/framework/templates/README.md +814 -814
  42. package/framework/templates/code/dotnet/contracts/Commands.cs +74 -74
  43. package/framework/templates/code/dotnet/contracts/Entities.cs +25 -25
  44. package/framework/templates/code/dotnet/contracts/Queries.cs +74 -74
  45. package/framework/templates/code/dotnet/contracts/README.md +74 -74
  46. package/framework/templates/code/dotnet/contracts/api-contracts.cs +173 -173
  47. package/framework/templates/code/dotnet/frontend/component.razor +239 -239
  48. package/framework/templates/code/dotnet/jobs/agent.cs +163 -163
  49. package/framework/templates/code/dotnet/jobs/job.cs +171 -171
  50. package/framework/templates/code/dotnet/test.cs +239 -239
  51. package/framework/templates/code/sql/rls-policy.sql +57 -57
  52. package/framework/templates/code/sql/supabase-migration.sql +100 -100
  53. package/framework/templates/code/sql/supabase-migration.template.sql +113 -113
  54. package/framework/templates/code/typescript/contracts.ts +168 -168
  55. package/framework/templates/context/CONTEXT.md +181 -181
  56. package/framework/templates/context/design-system.md +211 -0
  57. package/framework/templates/docs/proposal.md +182 -182
  58. package/framework/templates/docs/user-stories.md +34 -34
  59. package/framework/templates/examples/design-system-examples.md +357 -357
  60. package/framework/templates/examples/spec-examples.md +90 -90
  61. package/framework/templates/feature/decisions.md +187 -187
  62. package/framework/templates/feature/recap.md +146 -146
  63. package/framework/templates/feature/tasks.md +199 -199
  64. package/framework/templates/infrastructure/azure/README.md +286 -286
  65. package/framework/templates/infrastructure/azure/deploy-checklist.md +426 -426
  66. package/framework/templates/infrastructure/azure/parameters.dev.json +29 -29
  67. package/framework/templates/infrastructure/azure/parameters.prod.json +29 -29
  68. package/framework/templates/infrastructure/azure/parameters.staging.json +29 -29
  69. package/framework/templates/infrastructure/docker/docker-compose.template.yml +67 -67
  70. package/framework/templates/infrastructure/docker/dockerfile-api.dockerfile +38 -38
  71. package/framework/templates/infrastructure/docker/dockerfile-web.dockerfile +48 -48
  72. package/framework/templates/infrastructure/docker/easypanel.template.json +54 -54
  73. package/package.json +82 -82
  74. package/src/commands/agents/index.js +13 -13
  75. package/src/commands/project/install-plugin-cmd.js +28 -28
  76. package/src/commands/project/setup-infra-cmd.js +12 -12
  77. package/src/commands/project/worktree.js +58 -2
  78. package/src/commands/tasks/index.js +4 -4
  79. package/src/commands/templates/index.js +5 -5
  80. package/src/commands/utils/index.js +3 -3
  81. package/src/commands/validation/index.js +4 -4
  82. package/src/core/state/phase-state-machine.js +217 -217
  83. package/src/lib/generators/design-system-generator.js +298 -298
  84. package/src/lib/generators/index.js +10 -10
  85. package/src/lib/generators/metadata-extractor.js +387 -387
  86. package/src/lib/stack-filter.js +65 -65
  87. package/src/lib/standards/index.js +7 -7
  88. package/src/lib/standards/standards-context-injector.js +302 -302
  89. package/src/lib/validators/architecture/architecture-validator.js +387 -387
  90. package/src/lib/validators/architecture/index.js +7 -7
  91. package/src/lib/validators/blazor/blazor-concurrency-analyzer.js +316 -316
  92. package/src/lib/validators/blazor/blazor-state-validator.js +279 -279
  93. package/src/lib/validators/blazor/blazor-validator.js +369 -369
  94. package/src/lib/validators/blazor/index.js +9 -9
  95. package/src/lib/validators/content/content-validator.js +287 -287
  96. package/src/lib/validators/content/index.js +7 -7
  97. package/src/lib/validators/contracts/index.js +7 -7
  98. package/src/lib/validators/css/css-validator.js +352 -352
  99. package/src/lib/validators/css/index.js +7 -7
  100. package/src/lib/validators/design-system/design-system-validator.js +231 -231
  101. package/src/lib/validators/design-system/index.js +7 -7
  102. package/src/lib/validators/nextjs/index.js +6 -6
  103. package/src/lib/validators/nextjs/next-component-validator.js +183 -183
  104. package/src/lib/validators/packages/index.js +7 -7
  105. package/src/lib/validators/packages/package-validator.js +360 -360
  106. package/src/lib/validators/shared/index.js +12 -12
  107. package/src/lib/validators/shared/issue-counter.js +18 -18
  108. package/src/lib/validators/shared/result-formatter.js +124 -124
  109. package/src/lib/validators/ui/index.js +7 -7
  110. package/src/lib/validators/ui/ui-contrast-validator.js +422 -422
  111. package/src/scripts/global-install.js +58 -58
  112. package/src/scripts/install-plugin.js +126 -126
  113. package/src/utils/claude-md-injector.js +90 -90
  114. package/src/utils/file-copier.js +200 -196
  115. package/src/utils/hooks-installer.js +556 -494
  116. package/src/utils/logger.js +32 -32
  117. package/src/utils/skills-installer.js +178 -178
  118. package/src/utils/version-checker.js +175 -175
  119. package/framework/skills/level-1-workflows/morph-phase-implement/.morph/logs/activity.json +0 -38
  120. package/framework/skills/level-1-workflows/morph-phase-tasks/.morph/logs/activity.json +0 -14
  121. package/framework/templates/.idea/morph-templates.xml +0 -92
  122. package/framework/templates/.vscode/morph-templates.code-snippets +0 -186
@@ -1,254 +1,254 @@
1
- # Code Review Report — User Management Feature
2
-
3
- > Example of a well-structured Next.js code review output. Filled-in reference — not a template.
4
-
5
- **Scope:** `src/features/users/` + `src/app/(dashboard)/users/page.tsx`
6
- **Date:** 2026-02-23
7
- **Reviewer:** code-review-nextjs skill
8
-
9
- ---
10
-
11
- ## Automated Scan Results
12
-
13
- ```bash
14
- $ node scripts/scan-nextjs.mjs src/features/users/
15
-
16
- CRITICAL (1)
17
- features/users/components/UserList.tsx:1 — React hooks used (useState) without 'use client'
18
-
19
- HIGH/MEDIUM (2)
20
- features/users/components/UserList.tsx — PascalCase filename (should be user-list.tsx)
21
- features/users/components/user-form.tsx — 'use client' directive present but no interactivity detected
22
-
23
- Summary: 8 files scanned | 3 issues (1 critical, 2 high/medium)
24
- ```
25
-
26
- ---
27
-
28
- ## Findings by Severity
29
-
30
- | Severity | Count |
31
- |----------|-------|
32
- | CRITICAL | 2 |
33
- | HIGH | 3 |
34
- | MEDIUM | 2 |
35
- | LOW | 1 |
36
-
37
- ---
38
-
39
- ### [CRITICAL] Missing 'use client' on interactive component
40
-
41
- **File:** `src/features/users/components/UserList.tsx:1`
42
-
43
- **Current code:**
44
- ```tsx
45
- import { useState } from 'react';
46
-
47
- export function UserList() {
48
- const [selected, setSelected] = useState<string | null>(null);
49
- return <div onClick={() => setSelected('test')}>...</div>;
50
- }
51
- ```
52
-
53
- **Suggested fix:**
54
- ```tsx
55
- 'use client';
56
-
57
- import { useState } from 'react';
58
- // ... rest unchanged
59
- ```
60
-
61
- **Why:** React hooks (`useState`) require `'use client'`. Without it, Next.js treats this as a Server Component and the `useState` call fails at runtime.
62
-
63
- ---
64
-
65
- ### [CRITICAL] useEffect for data fetching
66
-
67
- **File:** `src/features/users/components/user-list.tsx:8`
68
-
69
- **Current code:**
70
- ```tsx
71
- 'use client';
72
- import { useEffect, useState } from 'react';
73
-
74
- export function UserList() {
75
- const [users, setUsers] = useState([]);
76
- useEffect(() => {
77
- fetch('/api/users').then(r => r.json()).then(setUsers);
78
- }, []);
79
- return <ul>{users.map(u => <li key={u.id}>{u.name}</li>)}</ul>;
80
- }
81
- ```
82
-
83
- **Suggested fix:**
84
- ```tsx
85
- // Option A — Server Component (no 'use client' needed)
86
- // app/(dashboard)/users/page.tsx
87
- async function getUsers() {
88
- const res = await fetch(`${process.env.API_URL}/api/users`, { next: { revalidate: 30 } });
89
- return res.json();
90
- }
91
- export default async function UsersPage() {
92
- const users = await getUsers();
93
- return <UserList initialData={users} />;
94
- }
95
-
96
- // Option B — TanStack Query (when client interactivity needed)
97
- 'use client';
98
- import { useUsers } from '@/features/users/hooks/use-users';
99
- export function UserList() {
100
- const { data: users = [], isLoading } = useUsers();
101
- if (isLoading) return <div>Loading...</div>;
102
- return <ul>{users.map(u => <li key={u.id}>{u.name}</li>)}</ul>;
103
- }
104
- ```
105
-
106
- **Why:** `useEffect` + `useState` for fetching: (1) doubles renders, (2) no caching, (3) no SSR, (4) no error/loading states, (5) can't be cancelled. TanStack Query or Server Components solve all five.
107
-
108
- ---
109
-
110
- ### [HIGH] PascalCase file name
111
-
112
- **File:** `src/features/users/components/UserList.tsx`
113
-
114
- **Suggested fix:** Rename to `user-list.tsx`
115
-
116
- **Why:** Linux servers are case-sensitive. `UserList.tsx` and `user-list.tsx` are different files on the server but the same on macOS/Windows, causing import failures in production.
117
-
118
- ---
119
-
120
- ### [HIGH] Default export on non-page component
121
-
122
- **File:** `src/features/users/components/user-card.tsx:3`
123
-
124
- **Current code:**
125
- ```tsx
126
- export default function UserCard({ user }: { user: User }) {
127
- ```
128
-
129
- **Suggested fix:**
130
- ```tsx
131
- export function UserCard({ user }: { user: User }) {
132
- ```
133
-
134
- **Why:** Named exports are refactor-safe — IDEs track renames automatically. Default exports are anonymous in imports (`import X from './user-card'` allows `X` to be anything).
135
-
136
- ---
137
-
138
- ### [HIGH] Type defined manually instead of deriving from Zod schema
139
-
140
- **File:** `src/features/users/types/user.types.ts:1`
141
-
142
- **Current code:**
143
- ```ts
144
- export type User = {
145
- id: string;
146
- name: string;
147
- email: string;
148
- role: 'admin' | 'user';
149
- };
150
- ```
151
-
152
- **Suggested fix:**
153
- ```ts
154
- // user.schemas.ts — single source of truth
155
- export const userSchema = z.object({
156
- id: z.string(),
157
- name: z.string(),
158
- email: z.string().email(),
159
- role: z.enum(['admin', 'user']),
160
- });
161
-
162
- // user.types.ts — derived, never written manually
163
- export type User = z.infer<typeof userSchema>;
164
- ```
165
-
166
- **Why:** Manually written types drift from API responses. `z.infer<>` ensures the TypeScript type is always in sync with the runtime validation.
167
-
168
- ---
169
-
170
- ### [MEDIUM] Deep path import instead of feature index
171
-
172
- **File:** `src/app/(dashboard)/users/page.tsx:2`
173
-
174
- **Current code:**
175
- ```tsx
176
- import { UserList } from '@/features/users/components/user-list';
177
- ```
178
-
179
- **Suggested fix:**
180
- ```tsx
181
- import { UserList } from '@/features/users';
182
- ```
183
-
184
- **Why:** Deep path imports expose internal structure. When `user-list.tsx` moves or is renamed, every consumer breaks. The feature index is the stable public API.
185
-
186
- ---
187
-
188
- ### [MEDIUM] Query key not using factory pattern
189
-
190
- **File:** `src/features/users/hooks/use-users.ts:8`
191
-
192
- **Current code:**
193
- ```ts
194
- return useQuery({
195
- queryKey: ['users'],
196
- ```
197
-
198
- **Suggested fix:**
199
- ```ts
200
- // query-keys.ts
201
- export const userKeys = {
202
- all: ['users'] as const,
203
- lists: () => [...userKeys.all, 'list'] as const,
204
- };
205
-
206
- // use-users.ts
207
- return useQuery({
208
- queryKey: userKeys.lists(),
209
- ```
210
-
211
- **Why:** Hardcoded `['users']` cannot be selectively invalidated. `queryClient.invalidateQueries({ queryKey: userKeys.lists() })` correctly invalidates only list queries, not detail queries with the same prefix.
212
-
213
- ---
214
-
215
- ### [LOW] Test uses fireEvent instead of userEvent
216
-
217
- **File:** `src/features/users/components/user-card.test.tsx:14`
218
-
219
- **Current code:**
220
- ```tsx
221
- fireEvent.click(screen.getByRole('button', { name: /edit/i }));
222
- ```
223
-
224
- **Suggested fix:**
225
- ```tsx
226
- await userEvent.click(screen.getByRole('button', { name: /edit/i }));
227
- ```
228
-
229
- **Why:** `fireEvent.click` dispatches a synthetic event. `userEvent.click` simulates the full browser interaction sequence (pointerdown, mousedown, focus, click, etc.) — closer to real user behaviour.
230
-
231
- ---
232
-
233
- ## Top Priorities
234
-
235
- 1. `UserList.tsx:1` — CRITICAL missing `'use client'` (2 min fix)
236
- 2. `user-list.tsx:8` — CRITICAL useEffect fetch → useQuery (15 min)
237
- 3. `UserList.tsx` — HIGH rename to `user-list.tsx` (2 min)
238
- 4. `user-card.tsx:3` — HIGH named export (2 min)
239
- 5. `user.types.ts:1` — HIGH derive from Zod (10 min)
240
-
241
- ---
242
-
243
- ## Passed Checks ✅
244
-
245
- - `components/ui/` files untouched
246
- - `zodResolver` correctly wired in `user-form.tsx`
247
- - `isPending` used (v5 correct — not `isLoading`)
248
- - `loading.tsx` and `error.tsx` present in `app/(dashboard)/users/`
249
- - Feature has `index.ts` re-exporting public API
250
- - Test file co-located with component
251
-
252
- ---
253
-
254
- *MORPH-SPEC by Polymorphism Tech*
1
+ # Code Review Report — User Management Feature
2
+
3
+ > Example of a well-structured Next.js code review output. Filled-in reference — not a template.
4
+
5
+ **Scope:** `src/features/users/` + `src/app/(dashboard)/users/page.tsx`
6
+ **Date:** 2026-02-23
7
+ **Reviewer:** code-review-nextjs skill
8
+
9
+ ---
10
+
11
+ ## Automated Scan Results
12
+
13
+ ```bash
14
+ $ node scripts/scan-nextjs.mjs src/features/users/
15
+
16
+ CRITICAL (1)
17
+ features/users/components/UserList.tsx:1 — React hooks used (useState) without 'use client'
18
+
19
+ HIGH/MEDIUM (2)
20
+ features/users/components/UserList.tsx — PascalCase filename (should be user-list.tsx)
21
+ features/users/components/user-form.tsx — 'use client' directive present but no interactivity detected
22
+
23
+ Summary: 8 files scanned | 3 issues (1 critical, 2 high/medium)
24
+ ```
25
+
26
+ ---
27
+
28
+ ## Findings by Severity
29
+
30
+ | Severity | Count |
31
+ |----------|-------|
32
+ | CRITICAL | 2 |
33
+ | HIGH | 3 |
34
+ | MEDIUM | 2 |
35
+ | LOW | 1 |
36
+
37
+ ---
38
+
39
+ ### [CRITICAL] Missing 'use client' on interactive component
40
+
41
+ **File:** `src/features/users/components/UserList.tsx:1`
42
+
43
+ **Current code:**
44
+ ```tsx
45
+ import { useState } from 'react';
46
+
47
+ export function UserList() {
48
+ const [selected, setSelected] = useState<string | null>(null);
49
+ return <div onClick={() => setSelected('test')}>...</div>;
50
+ }
51
+ ```
52
+
53
+ **Suggested fix:**
54
+ ```tsx
55
+ 'use client';
56
+
57
+ import { useState } from 'react';
58
+ // ... rest unchanged
59
+ ```
60
+
61
+ **Why:** React hooks (`useState`) require `'use client'`. Without it, Next.js treats this as a Server Component and the `useState` call fails at runtime.
62
+
63
+ ---
64
+
65
+ ### [CRITICAL] useEffect for data fetching
66
+
67
+ **File:** `src/features/users/components/user-list.tsx:8`
68
+
69
+ **Current code:**
70
+ ```tsx
71
+ 'use client';
72
+ import { useEffect, useState } from 'react';
73
+
74
+ export function UserList() {
75
+ const [users, setUsers] = useState([]);
76
+ useEffect(() => {
77
+ fetch('/api/users').then(r => r.json()).then(setUsers);
78
+ }, []);
79
+ return <ul>{users.map(u => <li key={u.id}>{u.name}</li>)}</ul>;
80
+ }
81
+ ```
82
+
83
+ **Suggested fix:**
84
+ ```tsx
85
+ // Option A — Server Component (no 'use client' needed)
86
+ // app/(dashboard)/users/page.tsx
87
+ async function getUsers() {
88
+ const res = await fetch(`${process.env.API_URL}/api/users`, { next: { revalidate: 30 } });
89
+ return res.json();
90
+ }
91
+ export default async function UsersPage() {
92
+ const users = await getUsers();
93
+ return <UserList initialData={users} />;
94
+ }
95
+
96
+ // Option B — TanStack Query (when client interactivity needed)
97
+ 'use client';
98
+ import { useUsers } from '@/features/users/hooks/use-users';
99
+ export function UserList() {
100
+ const { data: users = [], isLoading } = useUsers();
101
+ if (isLoading) return <div>Loading...</div>;
102
+ return <ul>{users.map(u => <li key={u.id}>{u.name}</li>)}</ul>;
103
+ }
104
+ ```
105
+
106
+ **Why:** `useEffect` + `useState` for fetching: (1) doubles renders, (2) no caching, (3) no SSR, (4) no error/loading states, (5) can't be cancelled. TanStack Query or Server Components solve all five.
107
+
108
+ ---
109
+
110
+ ### [HIGH] PascalCase file name
111
+
112
+ **File:** `src/features/users/components/UserList.tsx`
113
+
114
+ **Suggested fix:** Rename to `user-list.tsx`
115
+
116
+ **Why:** Linux servers are case-sensitive. `UserList.tsx` and `user-list.tsx` are different files on the server but the same on macOS/Windows, causing import failures in production.
117
+
118
+ ---
119
+
120
+ ### [HIGH] Default export on non-page component
121
+
122
+ **File:** `src/features/users/components/user-card.tsx:3`
123
+
124
+ **Current code:**
125
+ ```tsx
126
+ export default function UserCard({ user }: { user: User }) {
127
+ ```
128
+
129
+ **Suggested fix:**
130
+ ```tsx
131
+ export function UserCard({ user }: { user: User }) {
132
+ ```
133
+
134
+ **Why:** Named exports are refactor-safe — IDEs track renames automatically. Default exports are anonymous in imports (`import X from './user-card'` allows `X` to be anything).
135
+
136
+ ---
137
+
138
+ ### [HIGH] Type defined manually instead of deriving from Zod schema
139
+
140
+ **File:** `src/features/users/types/user.types.ts:1`
141
+
142
+ **Current code:**
143
+ ```ts
144
+ export type User = {
145
+ id: string;
146
+ name: string;
147
+ email: string;
148
+ role: 'admin' | 'user';
149
+ };
150
+ ```
151
+
152
+ **Suggested fix:**
153
+ ```ts
154
+ // user.schemas.ts — single source of truth
155
+ export const userSchema = z.object({
156
+ id: z.string(),
157
+ name: z.string(),
158
+ email: z.string().email(),
159
+ role: z.enum(['admin', 'user']),
160
+ });
161
+
162
+ // user.types.ts — derived, never written manually
163
+ export type User = z.infer<typeof userSchema>;
164
+ ```
165
+
166
+ **Why:** Manually written types drift from API responses. `z.infer<>` ensures the TypeScript type is always in sync with the runtime validation.
167
+
168
+ ---
169
+
170
+ ### [MEDIUM] Deep path import instead of feature index
171
+
172
+ **File:** `src/app/(dashboard)/users/page.tsx:2`
173
+
174
+ **Current code:**
175
+ ```tsx
176
+ import { UserList } from '@/features/users/components/user-list';
177
+ ```
178
+
179
+ **Suggested fix:**
180
+ ```tsx
181
+ import { UserList } from '@/features/users';
182
+ ```
183
+
184
+ **Why:** Deep path imports expose internal structure. When `user-list.tsx` moves or is renamed, every consumer breaks. The feature index is the stable public API.
185
+
186
+ ---
187
+
188
+ ### [MEDIUM] Query key not using factory pattern
189
+
190
+ **File:** `src/features/users/hooks/use-users.ts:8`
191
+
192
+ **Current code:**
193
+ ```ts
194
+ return useQuery({
195
+ queryKey: ['users'],
196
+ ```
197
+
198
+ **Suggested fix:**
199
+ ```ts
200
+ // query-keys.ts
201
+ export const userKeys = {
202
+ all: ['users'] as const,
203
+ lists: () => [...userKeys.all, 'list'] as const,
204
+ };
205
+
206
+ // use-users.ts
207
+ return useQuery({
208
+ queryKey: userKeys.lists(),
209
+ ```
210
+
211
+ **Why:** Hardcoded `['users']` cannot be selectively invalidated. `queryClient.invalidateQueries({ queryKey: userKeys.lists() })` correctly invalidates only list queries, not detail queries with the same prefix.
212
+
213
+ ---
214
+
215
+ ### [LOW] Test uses fireEvent instead of userEvent
216
+
217
+ **File:** `src/features/users/components/user-card.test.tsx:14`
218
+
219
+ **Current code:**
220
+ ```tsx
221
+ fireEvent.click(screen.getByRole('button', { name: /edit/i }));
222
+ ```
223
+
224
+ **Suggested fix:**
225
+ ```tsx
226
+ await userEvent.click(screen.getByRole('button', { name: /edit/i }));
227
+ ```
228
+
229
+ **Why:** `fireEvent.click` dispatches a synthetic event. `userEvent.click` simulates the full browser interaction sequence (pointerdown, mousedown, focus, click, etc.) — closer to real user behaviour.
230
+
231
+ ---
232
+
233
+ ## Top Priorities
234
+
235
+ 1. `UserList.tsx:1` — CRITICAL missing `'use client'` (2 min fix)
236
+ 2. `user-list.tsx:8` — CRITICAL useEffect fetch → useQuery (15 min)
237
+ 3. `UserList.tsx` — HIGH rename to `user-list.tsx` (2 min)
238
+ 4. `user-card.tsx:3` — HIGH named export (2 min)
239
+ 5. `user.types.ts:1` — HIGH derive from Zod (10 min)
240
+
241
+ ---
242
+
243
+ ## Passed Checks ✅
244
+
245
+ - `components/ui/` files untouched
246
+ - `zodResolver` correctly wired in `user-form.tsx`
247
+ - `isPending` used (v5 correct — not `isLoading`)
248
+ - `loading.tsx` and `error.tsx` present in `app/(dashboard)/users/`
249
+ - Feature has `index.ts` re-exporting public API
250
+ - Test file co-located with component
251
+
252
+ ---
253
+
254
+ *MORPH-SPEC by Polymorphism Tech*
@@ -42,17 +42,19 @@ Use the resolved CLI prefix (e.g. `npx morph-spec`) for ALL bash commands in the
42
42
 
43
43
  ## Step 1 — Required Plugins
44
44
 
45
- Check for required Claude Code plugins: **superpowers** and **context7**.
45
+ Check for required Claude Code plugins: **superpowers**, **context7**, and **playwright**.
46
46
 
47
47
  Read `~/.claude/plugins/installed_plugins.json` and check for:
48
48
  - `superpowers@claude-plugins-official`
49
49
  - `context7@claude-plugins-official`
50
+ - `playwright@claude-plugins-official`
50
51
 
51
52
  For each missing plugin, run:
52
53
 
53
54
  ```bash
54
55
  claude plugin install superpowers@claude-plugins-official
55
56
  claude plugin install context7@claude-plugins-official
57
+ claude plugin install playwright@claude-plugins-official
56
58
  ```
57
59
 
58
60
  **If the command succeeds:** `✓ {plugin} installed. Restart Claude Code after /morph-init completes.`
@@ -66,7 +68,7 @@ Run manually and restart Claude Code:
66
68
  Then re-run /morph-init.
67
69
  ```
68
70
 
69
- Both `superpowers` and `context7` are required. Do not continue if either is missing.
71
+ All three plugins are required. Do not continue if any is missing.
70
72
 
71
73
  ---
72
74
 
@@ -151,6 +153,89 @@ Do **not** ask about technology already confirmed by file evidence.
151
153
 
152
154
  ---
153
155
 
156
+ ## Step 4.5 — Scaffold Design System
157
+
158
+ Check if the project already has a design system file:
159
+
160
+ ```
161
+ Read: .morph/context/design-system.md
162
+ ```
163
+
164
+ **If the file EXISTS with non-TODO content in the Aesthetic Direction section:**
165
+ → Skip this step. Print: `✓ Design system found — .morph/context/design-system.md`
166
+
167
+ **If the file does NOT EXIST or is empty:**
168
+
169
+ 1. **Copy the template:**
170
+ ```bash
171
+ cp .morph/framework/templates/context/design-system.md .morph/context/design-system.md
172
+ ```
173
+ *(The template is at `framework/templates/context/design-system.md` in the installed framework)*
174
+
175
+ 2. **Ask 3 quick questions** using `AskUserQuestion`:
176
+
177
+ ```json
178
+ {
179
+ "questions": [
180
+ {
181
+ "header": "Design System Setup",
182
+ "question": "Let's set up your project's design system. What is the aesthetic direction for [ProjectName]?",
183
+ "multiSelect": false,
184
+ "options": [
185
+ { "label": "Minimal Refined", "description": "Luxurious and precise — elegant serif + light sans, monochromatic + 1 sharp accent" },
186
+ { "label": "Editorial", "description": "Magazine-grade, opinionated — strong display font + high contrast, dramatic reveals" },
187
+ { "label": "Soft Professional", "description": "Warm and trustworthy — rounded sans + warm pastels, gentle micro-interactions" },
188
+ { "label": "Industrial", "description": "Technical and direct — monospace/condensed + neutral colors, minimal motion" },
189
+ { "label": "Modern Luxury", "description": "Exclusive and refined — italic display + dark background + gold/copper accent" },
190
+ { "label": "Skip for now", "description": "Leave as TODO — fill in manually later" }
191
+ ]
192
+ }
193
+ ]
194
+ }
195
+ ```
196
+
197
+ 3. **If user chose a direction (not "Skip for now")**, ask a follow-up:
198
+
199
+ ```json
200
+ {
201
+ "questions": [
202
+ {
203
+ "header": "Brand Color",
204
+ "question": "What is your approximate dominant/primary color? (You can adjust exact values later)",
205
+ "multiSelect": false,
206
+ "options": [
207
+ { "label": "Deep blue/navy", "description": "Professional, trustworthy" },
208
+ { "label": "Rich green", "description": "Growth, sustainability" },
209
+ { "label": "Warm orange/red", "description": "Energy, urgency" },
210
+ { "label": "Cool purple/violet", "description": "Creative, premium" },
211
+ { "label": "True black/dark", "description": "Minimal, premium, editorial" },
212
+ { "label": "I'll specify manually", "description": "Leave as TODO" }
213
+ ]
214
+ }
215
+ ]
216
+ }
217
+ ```
218
+
219
+ 4. **Fill in the design-system.md** with user responses:
220
+
221
+ | User answered | What to update |
222
+ |--------------|----------------|
223
+ | Direction chosen | Replace `[Minimal Refined / Editorial / ...]` with chosen direction. Update Font Pair with the correct fonts from this mapping: Minimal Refined → Cormorant Garamond + DM Sans; Editorial → Playfair Display + Source Sans 3; Soft Professional → Nunito + Inter; Industrial → JetBrains Mono + IBM Plex Sans; Modern Luxury → Bodoni Moda + Sora |
224
+ | Color chosen | Replace `--color-primary: #TODO` with a reasonable hex for that color family (deep blue → #1e3a5f, rich green → #065f46, warm orange → #c2410c, purple → #6d28d9, black → #0a0a0a). Add a note: "Adjust exact values to match your brand" |
225
+ | "Skip for now" | Leave all TODOs in place |
226
+
227
+ 5. **Update the Changelog** in design-system.md:
228
+ Add entry: `- [today's date] [morph-init] Initial scaffold from morph-init`
229
+
230
+ 6. Print confirmation:
231
+ ```
232
+ ✓ Design system scaffolded — .morph/context/design-system.md
233
+ Direction: [chosen direction or "TODO — fill in manually"]
234
+ Edit to refine exact token values before the UI/UX phase.
235
+ ```
236
+
237
+ ---
238
+
154
239
  ## Step 5 — Generate `context/README.md`
155
240
 
156
241
  Write `.morph/context/README.md`:
@@ -358,7 +443,7 @@ For Node.js CLI projects, update config:
358
443
  Before printing the summary, check `~/.claude/settings.local.json` for `env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS`. If set to `"1"`, mark Agent Teams as enabled; otherwise show the warning.
359
444
 
360
445
  ```
361
- ✓ Plugins: superpowers ✓ context7 ✓
446
+ ✓ Plugins: superpowers ✓ context7 ✓ playwright ✓
362
447
  ✓ Infrastructure: .morph/ structure, hooks, agents, rules, skills
363
448
  ✓ Agents: [N] total ([T1] orchestrators, [T2] domain leaders, [S] specialists)
364
449
  ✓ context/README.md generated