@polymorphism-tech/morph-spec 4.10.7 → 4.10.9

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 (123) 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 +136 -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 +457 -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/detectors/design-system-detector.js +73 -31
  84. package/src/lib/generators/design-system-generator.js +298 -298
  85. package/src/lib/generators/index.js +10 -10
  86. package/src/lib/generators/metadata-extractor.js +387 -387
  87. package/src/lib/stack-filter.js +65 -65
  88. package/src/lib/standards/index.js +7 -7
  89. package/src/lib/standards/standards-context-injector.js +302 -302
  90. package/src/lib/validators/architecture/architecture-validator.js +387 -387
  91. package/src/lib/validators/architecture/index.js +7 -7
  92. package/src/lib/validators/blazor/blazor-concurrency-analyzer.js +316 -316
  93. package/src/lib/validators/blazor/blazor-state-validator.js +279 -279
  94. package/src/lib/validators/blazor/blazor-validator.js +369 -369
  95. package/src/lib/validators/blazor/index.js +9 -9
  96. package/src/lib/validators/content/content-validator.js +287 -287
  97. package/src/lib/validators/content/index.js +7 -7
  98. package/src/lib/validators/contracts/index.js +7 -7
  99. package/src/lib/validators/css/css-validator.js +352 -352
  100. package/src/lib/validators/css/index.js +7 -7
  101. package/src/lib/validators/design-system/design-system-validator.js +231 -231
  102. package/src/lib/validators/design-system/index.js +7 -7
  103. package/src/lib/validators/nextjs/index.js +6 -6
  104. package/src/lib/validators/nextjs/next-component-validator.js +183 -183
  105. package/src/lib/validators/packages/index.js +7 -7
  106. package/src/lib/validators/packages/package-validator.js +360 -360
  107. package/src/lib/validators/shared/index.js +12 -12
  108. package/src/lib/validators/shared/issue-counter.js +18 -18
  109. package/src/lib/validators/shared/result-formatter.js +124 -124
  110. package/src/lib/validators/ui/index.js +7 -7
  111. package/src/lib/validators/ui/ui-contrast-validator.js +422 -422
  112. package/src/scripts/global-install.js +58 -58
  113. package/src/scripts/install-plugin.js +126 -126
  114. package/src/utils/claude-md-injector.js +90 -90
  115. package/src/utils/file-copier.js +200 -196
  116. package/src/utils/hooks-installer.js +556 -494
  117. package/src/utils/logger.js +32 -32
  118. package/src/utils/skills-installer.js +178 -178
  119. package/src/utils/version-checker.js +175 -175
  120. package/framework/skills/level-1-workflows/morph-phase-implement/.morph/logs/activity.json +0 -38
  121. package/framework/skills/level-1-workflows/morph-phase-tasks/.morph/logs/activity.json +0 -14
  122. package/framework/templates/.idea/morph-templates.xml +0 -92
  123. 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,137 @@ 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
+ ### 4.5.0 — Check if DS already exists
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 steps 4.5.1–4.5.2. Print: `✓ Design system found — .morph/context/design-system.md`
166
+
167
+ ### 4.5.1 — ALWAYS scan for existing CSS tokens
168
+
169
+ Search for existing CSS custom properties across the project's frontend directories.
170
+
171
+ Glob these patterns (exclude `node_modules/`, `.next/`, `dist/`, `build/`):
172
+ ```
173
+ wwwroot/**/*.css
174
+ wwwroot/**/*.scss
175
+ styles/**/*.css
176
+ src/styles/**/*.css
177
+ src/**/*.css
178
+ src/**/*.scss
179
+ src/**/*.module.css
180
+ public/**/*.css
181
+ ```
182
+
183
+ For each CSS/SCSS file found, Grep for `:root {` blocks containing `--` custom properties.
184
+
185
+ Track:
186
+ - `cssTokensFound` (bool)
187
+ - `tokenCount` (int — total number of `--variable` lines found)
188
+ - `sourceFiles` (array of file paths that had tokens)
189
+
190
+ ### 4.5.2 — Decision tree
191
+
192
+ **Case A: CSS tokens found (existing project)**
193
+
194
+ 1. Create `.morph/context/design-system.md` from scanned tokens:
195
+ - `## Color Tokens` → `:root { }` block with all found color variables (preserve exact variable names from CSS)
196
+ - `## Typography` → found font-family values (map to `--font-display` / `--font-body` by variable name heuristic)
197
+ - `## Spacing Tokens` → found spacing variables
198
+ - `## Aesthetic Direction` → `#TODO` (requires user judgment — fill in during `/morph-phase-uiux`)
199
+ - `## Changelog` → `- [today's date] [morph-init] Auto-generated from CSS scan ([N] tokens from [files])`
200
+
201
+ 2. Skip the `AskUserQuestion` for direction/brand color — the project has established colors
202
+
203
+ 3. Print:
204
+ ```
205
+ ✓ Design system scaffolded from existing CSS
206
+ Source: [file list]
207
+ Tokens: [N] colors, [M] font families, [K] spacing values
208
+ Note: Aesthetic Direction is TODO — fill in during /morph-phase-uiux
209
+ ```
210
+
211
+ 4. Continue to Step 5.
212
+
213
+ **Case B: No CSS tokens found (greenfield project)**
214
+
215
+ 1. Copy the template:
216
+ ```bash
217
+ cp .morph/framework/templates/context/design-system.md .morph/context/design-system.md
218
+ ```
219
+ *(The template is at `framework/templates/context/design-system.md` in the installed framework)*
220
+
221
+ 2. Ask 3 quick questions using `AskUserQuestion`:
222
+
223
+ ```json
224
+ {
225
+ "questions": [
226
+ {
227
+ "header": "Design System Setup",
228
+ "question": "Let's set up your project's design system. What is the aesthetic direction for [ProjectName]?",
229
+ "multiSelect": false,
230
+ "options": [
231
+ { "label": "Minimal Refined", "description": "Luxurious and precise — elegant serif + light sans, monochromatic + 1 sharp accent" },
232
+ { "label": "Editorial", "description": "Magazine-grade, opinionated — strong display font + high contrast, dramatic reveals" },
233
+ { "label": "Soft Professional", "description": "Warm and trustworthy — rounded sans + warm pastels, gentle micro-interactions" },
234
+ { "label": "Industrial", "description": "Technical and direct — monospace/condensed + neutral colors, minimal motion" },
235
+ { "label": "Modern Luxury", "description": "Exclusive and refined — italic display + dark background + gold/copper accent" },
236
+ { "label": "Skip for now", "description": "Leave as TODO — fill in manually later" }
237
+ ]
238
+ }
239
+ ]
240
+ }
241
+ ```
242
+
243
+ 3. **If user chose a direction (not "Skip for now")**, ask a follow-up:
244
+
245
+ ```json
246
+ {
247
+ "questions": [
248
+ {
249
+ "header": "Brand Color",
250
+ "question": "What is your approximate dominant/primary color? (You can adjust exact values later)",
251
+ "multiSelect": false,
252
+ "options": [
253
+ { "label": "Deep blue/navy", "description": "Professional, trustworthy" },
254
+ { "label": "Rich green", "description": "Growth, sustainability" },
255
+ { "label": "Warm orange/red", "description": "Energy, urgency" },
256
+ { "label": "Cool purple/violet", "description": "Creative, premium" },
257
+ { "label": "True black/dark", "description": "Minimal, premium, editorial" },
258
+ { "label": "I'll specify manually", "description": "Leave as TODO" }
259
+ ]
260
+ }
261
+ ]
262
+ }
263
+ ```
264
+
265
+ 4. Fill in the design-system.md with user responses:
266
+
267
+ | User answered | What to update |
268
+ |--------------|----------------|
269
+ | Direction chosen | Replace `[Minimal Refined / Editorial / ...]` with chosen direction. Update Font Pair with the correct fonts: 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 |
270
+ | 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 note: "Adjust exact values to match your brand" |
271
+ | "Skip for now" | Leave all TODOs in place |
272
+
273
+ 5. Update the Changelog in design-system.md:
274
+ Add entry: `- [today's date] [morph-init] Initial scaffold from morph-init`
275
+
276
+ 6. Print confirmation:
277
+ ```
278
+ ✓ Design system scaffolded — .morph/context/design-system.md
279
+ Direction: [chosen direction or "TODO — fill in manually"]
280
+ Edit to refine exact token values before the UI/UX phase.
281
+ ```
282
+
283
+ 7. Continue to Step 5.
284
+
285
+ ---
286
+
154
287
  ## Step 5 — Generate `context/README.md`
155
288
 
156
289
  Write `.morph/context/README.md`:
@@ -358,7 +491,7 @@ For Node.js CLI projects, update config:
358
491
  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
492
 
360
493
  ```
361
- ✓ Plugins: superpowers ✓ context7 ✓
494
+ ✓ Plugins: superpowers ✓ context7 ✓ playwright ✓
362
495
  ✓ Infrastructure: .morph/ structure, hooks, agents, rules, skills
363
496
  ✓ Agents: [N] total ([T1] orchestrators, [T2] domain leaders, [S] specialists)
364
497
  ✓ context/README.md generated