@vpxa/aikit 0.1.185 → 0.1.187
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/package.json +1 -1
- package/packages/browser/dist/index.js +1 -1
- package/scaffold/dist/definitions/skills/adr-skill.mjs +73 -155
- package/scaffold/dist/definitions/skills/aikit.mjs +291 -318
- package/scaffold/dist/definitions/skills/brainstorming.mjs +107 -242
- package/scaffold/dist/definitions/skills/browser-use.mjs +213 -1
- package/scaffold/dist/definitions/skills/c4-architecture.mjs +212 -2256
- package/scaffold/dist/definitions/skills/docs.mjs +395 -755
- package/scaffold/dist/definitions/skills/lesson-learned.mjs +61 -21
- package/scaffold/dist/definitions/skills/react.mjs +104 -137
- package/scaffold/dist/definitions/skills/requirements-clarity.mjs +55 -2
- package/scaffold/dist/definitions/skills/session-handoff.mjs +127 -177
- package/scaffold/dist/definitions/skills/typescript.mjs +16 -0
|
@@ -164,7 +164,7 @@ Code signals: A switch/if-else chain replaced with a strategy pattern or subclas
|
|
|
164
164
|
Code signals: Multiple related parameters grouped into a single options/config object. Function signatures simplified.
|
|
165
165
|
`},{file:`SKILL.md`,content:`---
|
|
166
166
|
name: lesson-learned
|
|
167
|
-
description: "Analyze recent code changes via git history and extract software engineering lessons. Use when the user asks 'what is the lesson here?', 'what can I learn from this?', 'engineering takeaway', 'what did I just learn?', 'reflect on this code', or wants to extract
|
|
167
|
+
description: "Analyze recent code changes via git history and extract software engineering lessons. Use when the user asks 'what is the lesson here?', 'what can I learn from this?', 'engineering takeaway', 'what did I just learn?', 'reflect on this code', 'what principles does this show?', or wants to extract actionable insights from recent work. Also use after completing any implementation task to capture learning."
|
|
168
168
|
metadata:
|
|
169
169
|
category: cross-cutting
|
|
170
170
|
domain: general
|
|
@@ -177,17 +177,22 @@ metadata:
|
|
|
177
177
|
|
|
178
178
|
# Lesson Learned
|
|
179
179
|
|
|
180
|
-
Extract specific, grounded software engineering lessons from actual code changes. Not a lecture -- a mirror. Show the user what their code already demonstrates.
|
|
180
|
+
Extract specific, grounded software engineering lessons from actual code changes. Not a lecture -- a mirror. Show the user what their code already demonstrates, and only elevate lessons that are earned by the diff.
|
|
181
181
|
|
|
182
182
|
## Before You Begin
|
|
183
183
|
|
|
184
|
-
|
|
184
|
+
- Determine the analysis scope first.
|
|
185
|
+
- Use the inline principles below for the common case.
|
|
186
|
+
- Read 'references/se-principles.md' only when the dominant pattern is unclear or you need a deeper principle match.
|
|
187
|
+
- Read 'references/anti-patterns.md' only when the diff suggests a gentle improvement opportunity.
|
|
185
188
|
|
|
186
|
-
|
|
187
|
-
2. Optionally read \`references/anti-patterns.md\` if you suspect the changes include areas for improvement
|
|
188
|
-
3. Determine the scope of analysis (see Phase 1)
|
|
189
|
+
## Core Principles
|
|
189
190
|
|
|
190
|
-
**
|
|
191
|
+
- **SRP** -- one module, one reason to change. Diff signal: a file/function was split because it was carrying mixed responsibilities.
|
|
192
|
+
- **Separation of Concerns** -- keep UI, domain, data, config, and orchestration in their own lanes. Diff signal: logic moved across a boundary into a more appropriate layer.
|
|
193
|
+
- **YAGNI** -- build only what current requirements justify. Diff signal: speculative abstractions, options, or branches were removed or avoided.
|
|
194
|
+
- **Fail Fast** -- detect bad input or invalid state at the boundary. Diff signal: guards, validation, assertions, or early returns were added near the entry point.
|
|
195
|
+
- **DRY** -- shared knowledge should live in one place. Diff signal: duplicated literals, branches, or helper logic were consolidated into one source.
|
|
191
196
|
|
|
192
197
|
## Phase 1: Determine Scope
|
|
193
198
|
|
|
@@ -207,23 +212,46 @@ Ask the user or infer from context what to analyze.
|
|
|
207
212
|
|
|
208
213
|
## Phase 2: Gather Changes
|
|
209
214
|
|
|
210
|
-
1. Run \`git log\` with the determined scope to get
|
|
211
|
-
2. Run \`git diff\` for the full diff of
|
|
212
|
-
3. If the diff is large (>500 lines),
|
|
213
|
-
4.
|
|
214
|
-
5. Only read changed files. Do not
|
|
215
|
+
1. Run \`git log\` with the determined scope to get commits and messages.
|
|
216
|
+
2. Run \`git diff\` for the full diff of that scope.
|
|
217
|
+
3. If the diff is large (>500 lines), start with \`git diff --stat\`, then inspect the 3-5 most-changed files.
|
|
218
|
+
4. Read commit messages carefully -- they often reveal intent that raw diffs do not.
|
|
219
|
+
5. Only read changed files. Do not widen scope beyond the diff.
|
|
220
|
+
6. Capture concrete evidence as you go: commit SHA, file path, and line references.
|
|
215
221
|
|
|
216
222
|
## Phase 3: Analyze
|
|
217
223
|
|
|
218
|
-
|
|
224
|
+
Apply this decision tree to find the dominant lesson:
|
|
219
225
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
- **Trade-offs made** -- What was gained vs. sacrificed? (readability vs. performance, DRY vs. clarity, speed vs. correctness)
|
|
223
|
-
- **Problems solved** -- What was the before/after? What made the "after" better?
|
|
224
|
-
- **Missed opportunities** -- Where could the code improve? (present gently as "next time, consider...")
|
|
226
|
+
1. **Is there a structural change?** (files split/merged, new module boundary, layer introduced)
|
|
227
|
+
-> Lesson is about architecture principles (SRP, Separation of Concerns, Cohesion)
|
|
225
228
|
|
|
226
|
-
|
|
229
|
+
2. **Is there a removed or simplified path?** (deleted code, simplified conditionals, fewer dependencies)
|
|
230
|
+
-> Lesson is about simplicity principles (YAGNI, KISS, Premature Abstraction avoidance)
|
|
231
|
+
|
|
232
|
+
3. **Is there error handling or edge case work?** (try/catch added, validation, fallbacks)
|
|
233
|
+
-> Lesson is about robustness principles (Fail Fast, Defensive Programming)
|
|
234
|
+
|
|
235
|
+
4. **Is there duplication resolved?** (extracted shared logic, parameterized, DRY applied)
|
|
236
|
+
-> Lesson is about DRY and Rule of Three
|
|
237
|
+
|
|
238
|
+
5. **Is there a naming or API change?** (renames, interface changes, contract adjustments)
|
|
239
|
+
-> Lesson is about Principle of Least Surprise and Encapsulation
|
|
240
|
+
|
|
241
|
+
6. **None of the above clearly dominate?**
|
|
242
|
+
-> The changes may be routine. Check commit messages for unstated intent. If still nothing emerges, be honest that no deep lesson exists.
|
|
243
|
+
|
|
244
|
+
For the dominant pattern, map to the specific principle from \`references/se-principles.md\` when needed and cite the concrete code change that demonstrates it.
|
|
245
|
+
|
|
246
|
+
## Quality Gate
|
|
247
|
+
|
|
248
|
+
A lesson is only worth presenting if ALL of these are true:
|
|
249
|
+
- **Non-obvious** -- someone reading the diff would not immediately see it without prompting
|
|
250
|
+
- **Actionable** -- it changes future behavior, not just "interesting observation"
|
|
251
|
+
- **Specific** -- cites actual file:line, not generic advice
|
|
252
|
+
- **Earned** -- emerges naturally from the code, not force-fit to a principle
|
|
253
|
+
|
|
254
|
+
If no lesson meets all four criteria, say so honestly: "These changes are solid execution -- no deep lesson here beyond good engineering practice."
|
|
227
255
|
|
|
228
256
|
## Phase 4: Present the Lesson
|
|
229
257
|
|
|
@@ -245,7 +273,7 @@ Use this template:
|
|
|
245
273
|
[One concrete, actionable sentence the user can apply to future work]
|
|
246
274
|
\`\`\`
|
|
247
275
|
|
|
248
|
-
If there is a second lesson worth noting
|
|
276
|
+
If there is a second lesson worth noting, include at most one more:
|
|
249
277
|
|
|
250
278
|
\`\`\`markdown
|
|
251
279
|
---
|
|
@@ -257,6 +285,8 @@ If there is a second lesson worth noting (maximum 2 additional):
|
|
|
257
285
|
**Takeaway:** [1 sentence]
|
|
258
286
|
\`\`\`
|
|
259
287
|
|
|
288
|
+
Do not present more than 2 lessons total. If the diff is mostly clean execution with no deeper pattern, say that directly.
|
|
289
|
+
|
|
260
290
|
## What NOT to Do
|
|
261
291
|
|
|
262
292
|
| Avoid | Why | Instead |
|
|
@@ -266,7 +296,17 @@ If there is a second lesson worth noting (maximum 2 additional):
|
|
|
266
296
|
| Ignoring commit messages | They contain intent that diffs miss | Read them as primary context |
|
|
267
297
|
| Abstract advice disconnected from the code | Not actionable | Always reference specific files/lines |
|
|
268
298
|
| Negative-only feedback | Demoralizing | Lead with what works, then suggest improvements |
|
|
269
|
-
| More than
|
|
299
|
+
| More than 2 lessons | Dilutes the insight | One well-grounded lesson beats seven vague ones |
|
|
300
|
+
|
|
301
|
+
## NEVER
|
|
302
|
+
|
|
303
|
+
- **NEVER present obvious refactoring as a lesson** ("the code got cleaner") -- this is table stakes, not insight
|
|
304
|
+
- **NEVER force-fit a principle** that does not naturally emerge from the diff -- if you have to stretch to connect code to principle, the connection is not there
|
|
305
|
+
- **NEVER produce more than 2 lessons** per analysis -- one well-grounded insight beats seven vague observations
|
|
306
|
+
- **NEVER use "best practice" without citing WHICH practice and WHY it is best HERE** -- "best practice" is the refuge of those who cannot explain the reasoning
|
|
307
|
+
- **NEVER analyze files that were not changed** -- scope creep dilutes focus
|
|
308
|
+
- **NEVER start with criticism** -- lead with what the code demonstrates well, then optionally note opportunities
|
|
309
|
+
- **NEVER produce a lesson without a concrete code reference** -- if you cannot point to a specific line or commit, the lesson is too abstract
|
|
270
310
|
|
|
271
311
|
## Conversation Style
|
|
272
312
|
|
|
@@ -22,18 +22,84 @@ metadata:
|
|
|
22
22
|
- Optimizing React performance (re-renders, bundle size, data fetching)
|
|
23
23
|
- Reviewing React code for correctness and best practices
|
|
24
24
|
|
|
25
|
+
## Thinking Model
|
|
26
|
+
|
|
27
|
+
React is a **description language for UI**, not an imperative framework. Think in snapshots, not steps:
|
|
28
|
+
- "Given this state, what should the screen look like?" (declarative)
|
|
29
|
+
- NOT "When this happens, do these 5 things in order" (imperative)
|
|
30
|
+
|
|
31
|
+
The render function is a PURE function of (props, state) → JSX. Side effects live in clearly-marked boundaries (effects, event handlers, server actions).
|
|
32
|
+
|
|
33
|
+
**Server vs Client boundary** — the most common architectural mistake in modern React:
|
|
34
|
+
- Default to Server Components (zero JS shipped, direct data access)
|
|
35
|
+
- Add 'use client' only when you need: event handlers, useState/useEffect, browser APIs
|
|
36
|
+
- The boundary is a CONTRACT — server can render client, client CANNOT import server
|
|
37
|
+
|
|
38
|
+
Before coding, decide in this order:
|
|
39
|
+
1. **What is pure UI derivation?** Keep it in render.
|
|
40
|
+
2. **What is interaction state?** Keep it in the smallest client boundary.
|
|
41
|
+
3. **What is data loading or mutation?** Prefer server.
|
|
42
|
+
4. **What is side effect vs event response?** Effects synchronize with outside systems; event handlers respond to user intent.
|
|
43
|
+
|
|
44
|
+
## Decision Flow
|
|
45
|
+
|
|
46
|
+
When implementing a React feature, follow this order:
|
|
47
|
+
|
|
48
|
+
1. **Server or Client?**
|
|
49
|
+
- If it needs browser APIs, event handlers, or local interaction state → Client Component.
|
|
50
|
+
- Everything else starts as a Server Component.
|
|
51
|
+
- Keep the client boundary as small as possible: interactive leaf, server parent.
|
|
52
|
+
|
|
53
|
+
2. **Derived state or stored state?**
|
|
54
|
+
- If a value can be computed from props/state during render, do NOT store it.
|
|
55
|
+
- Store only user input, async status, or state you cannot recompute cheaply or correctly.
|
|
56
|
+
|
|
57
|
+
3. **Data loading location?**
|
|
58
|
+
- Initial fetch on server.
|
|
59
|
+
- Client refetch/cache only when data must stay live after hydration or respond to client events.
|
|
60
|
+
|
|
61
|
+
4. **Mutation path?**
|
|
62
|
+
- Forms and mutations: Server Action + validation.
|
|
63
|
+
- Use \`useActionState\` for submission state/errors.
|
|
64
|
+
- Add \`useOptimistic\` only when instant feedback materially improves UX.
|
|
65
|
+
|
|
66
|
+
5. **Effect or not?**
|
|
67
|
+
- If synchronizing with DOM, network subscription, timer, or external API → effect.
|
|
68
|
+
- If reacting to user input → event handler.
|
|
69
|
+
- If deriving view state → render logic, not effect.
|
|
70
|
+
|
|
71
|
+
6. **Error and loading boundaries?**
|
|
72
|
+
- Rendering latency → \`<Suspense>\` with a real skeleton.
|
|
73
|
+
- Render failures → Error Boundary.
|
|
74
|
+
- Mutation failures → return structured errors from the Server Action.
|
|
75
|
+
|
|
76
|
+
7. **Performance pass?**
|
|
77
|
+
- Start with correct boundaries and minimal client JS.
|
|
78
|
+
- Then rely on React Compiler if enabled.
|
|
79
|
+
- Only then add manual memoization, virtualization, or code splitting where profiling justifies it.
|
|
80
|
+
|
|
81
|
+
8. **Extraction pass?**
|
|
82
|
+
- Business rules → hooks, utilities, or server functions.
|
|
83
|
+
- Components stay focused on describing UI.
|
|
84
|
+
|
|
85
|
+
## NEVER
|
|
86
|
+
|
|
87
|
+
- **NEVER use \`useEffect\` for derived state** — if you can compute it from props/state, compute it inline or with \`useMemo\`. Effects for derivation cause extra renders and race conditions.
|
|
88
|
+
- **NEVER put business logic in components** — components are UI description, not business rules. Extract logic to hooks, utilities, or server functions.
|
|
89
|
+
- **NEVER use \`any\` to silence TypeScript in component props** — use \`unknown\` and narrow, or fix the actual type. \`any\` in props infects the entire component tree.
|
|
90
|
+
- **NEVER create a context for state that only one component reads** — prop drilling 2-3 levels is fine. Context adds re-render cost to EVERY consumer.
|
|
91
|
+
- **NEVER \`await\` inside \`useEffect\` directly** — use an inner async function or a data-fetching library. Direct await creates unhandled promise rejections and races.
|
|
92
|
+
- **NEVER conditionally call hooks** — hooks must be called in the same order every render. If you need conditional logic, move it INSIDE the hook.
|
|
93
|
+
- **NEVER spread props onto DOM elements without filtering** — \`{...props}\` passes unknown attributes to the DOM, causing console warnings and potential XSS vectors.
|
|
94
|
+
- **NEVER render a list without a stable key** — index-as-key causes state corruption on reorder. Use a domain ID or generate a stable key from content.
|
|
95
|
+
|
|
25
96
|
## React 19 Quick Reference
|
|
26
97
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
| \`useOptimistic\` | Instant UI feedback | Optimistic updates before server confirms |
|
|
33
|
-
| \`useTransition\` | Non-urgent state updates | \`isPending\` for loading states |
|
|
34
|
-
| \`<form action>\` | Server Actions in forms | Direct server function binding |
|
|
35
|
-
| Server Components | Zero-bundle components | Default in App Router, no \`"use client"\` |
|
|
36
|
-
| Server Actions | Server mutations | \`"use server"\` functions, form actions |
|
|
98
|
+
- \`ref\` as prop — no \`forwardRef\` wrapper for common cases
|
|
99
|
+
- \`use()\` — read promises/context during render
|
|
100
|
+
- \`useActionState\` + \`<form action>\` — form mutations with server actions
|
|
101
|
+
- \`useOptimistic\` + \`useTransition\` — optimistic and non-urgent updates
|
|
102
|
+
- Server Components / Server Actions — default to server, opt into client only when needed
|
|
37
103
|
|
|
38
104
|
## Component Patterns
|
|
39
105
|
|
|
@@ -77,56 +143,36 @@ function Table<T extends Record<string, unknown>>({ data, columns, onRowClick }:
|
|
|
77
143
|
\`\`\`
|
|
78
144
|
|
|
79
145
|
### Children Typing
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
// Render function children
|
|
85
|
-
type Props = { children: (data: T) => React.ReactNode };
|
|
86
|
-
|
|
87
|
-
// String only
|
|
88
|
-
type Props = { children: string };
|
|
89
|
-
\`\`\`
|
|
146
|
+
- Specific children: \`React.ReactElement<TabProps>[]\`
|
|
147
|
+
- Render prop: \`(data: T) => React.ReactNode\`
|
|
148
|
+
- Text-only API: \`string\`
|
|
90
149
|
|
|
91
150
|
## Ref as Prop (React 19)
|
|
92
151
|
|
|
93
152
|
\`\`\`tsx
|
|
94
|
-
// React 19: pass ref directly, no forwardRef needed
|
|
95
153
|
function Input({ ref, ...props }: React.ComponentProps<"input">) {
|
|
96
154
|
return <input ref={ref} {...props} />;
|
|
97
155
|
}
|
|
98
|
-
|
|
99
|
-
// Cleanup function (new in React 19)
|
|
100
|
-
function MeasuredDiv({ ref, ...props }: React.ComponentProps<"div">) {
|
|
101
|
-
return (
|
|
102
|
-
<div
|
|
103
|
-
ref={(node) => {
|
|
104
|
-
if (node) measureElement(node);
|
|
105
|
-
return () => cleanupMeasurement(); // cleanup on unmount
|
|
106
|
-
}}
|
|
107
|
-
{...props}
|
|
108
|
-
/>
|
|
109
|
-
);
|
|
110
|
-
}
|
|
111
156
|
\`\`\`
|
|
112
157
|
|
|
158
|
+
Callback refs can return cleanup functions in React 19. Use that for measurement/subscription teardown instead of scattering cleanup elsewhere.
|
|
159
|
+
|
|
113
160
|
## Server Components & Actions
|
|
114
161
|
|
|
115
162
|
### Server Component (default — no directive needed)
|
|
116
163
|
\`\`\`tsx
|
|
117
|
-
// app/users/page.tsx — Server Component (default)
|
|
118
164
|
import { db } from "@/lib/db";
|
|
119
165
|
|
|
120
166
|
export default async function UsersPage() {
|
|
121
|
-
const users = await db.query.users.findMany();
|
|
122
|
-
return <UserList users={users} />;
|
|
167
|
+
const users = await db.query.users.findMany();
|
|
168
|
+
return <UserList users={users} />;
|
|
123
169
|
}
|
|
124
170
|
\`\`\`
|
|
125
171
|
|
|
126
172
|
### Client Component (explicit opt-in)
|
|
127
173
|
\`\`\`tsx
|
|
128
174
|
"use client";
|
|
129
|
-
import { useState
|
|
175
|
+
import { useState } from "react";
|
|
130
176
|
|
|
131
177
|
export function Counter({ initialCount }: { initialCount: number }) {
|
|
132
178
|
const [count, setCount] = useState(initialCount);
|
|
@@ -136,7 +182,6 @@ export function Counter({ initialCount }: { initialCount: number }) {
|
|
|
136
182
|
|
|
137
183
|
### Server Action with Form
|
|
138
184
|
\`\`\`tsx
|
|
139
|
-
// actions.ts
|
|
140
185
|
"use server";
|
|
141
186
|
import { z } from "zod";
|
|
142
187
|
|
|
@@ -145,7 +190,7 @@ const schema = z.object({ name: z.string().min(1), email: z.string().email() });
|
|
|
145
190
|
export async function createUser(_prev: unknown, formData: FormData) {
|
|
146
191
|
const result = schema.safeParse(Object.fromEntries(formData));
|
|
147
192
|
if (!result.success) return { error: result.error.flatten().fieldErrors };
|
|
148
|
-
await
|
|
193
|
+
await saveUser(result.data);
|
|
149
194
|
return { success: true };
|
|
150
195
|
}
|
|
151
196
|
\`\`\`
|
|
@@ -160,9 +205,7 @@ export function CreateUserForm() {
|
|
|
160
205
|
return (
|
|
161
206
|
<form action={action}>
|
|
162
207
|
<input name="name" required />
|
|
163
|
-
{state?.error?.name && <p className="text-red-500">{state.error.name}</p>}
|
|
164
208
|
<input name="email" type="email" required />
|
|
165
|
-
{state?.error?.email && <p className="text-red-500">{state.error.email}</p>}
|
|
166
209
|
<button disabled={isPending}>{isPending ? "Creating..." : "Create"}</button>
|
|
167
210
|
</form>
|
|
168
211
|
);
|
|
@@ -175,10 +218,10 @@ export function CreateUserForm() {
|
|
|
175
218
|
import { useOptimistic } from "react";
|
|
176
219
|
|
|
177
220
|
function TodoList({ todos, addTodo }: { todos: Todo[]; addTodo: (text: string) => Promise<void> }) {
|
|
178
|
-
const [optimisticTodos, addOptimistic] = useOptimistic(
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
);
|
|
221
|
+
const [optimisticTodos, addOptimistic] = useOptimistic(todos, (state, text: string) => [
|
|
222
|
+
...state,
|
|
223
|
+
{ id: "temp", text, pending: true },
|
|
224
|
+
]);
|
|
182
225
|
|
|
183
226
|
async function handleAdd(formData: FormData) {
|
|
184
227
|
const text = formData.get("text") as string;
|
|
@@ -202,109 +245,33 @@ function TodoList({ todos, addTodo }: { todos: Todo[]; addTodo: (text: string) =
|
|
|
202
245
|
|
|
203
246
|
## Event Handler Types
|
|
204
247
|
|
|
205
|
-
|
|
206
|
-
// Always use specific event types
|
|
207
|
-
onClick: (e: React.MouseEvent<HTMLButtonElement>) => void
|
|
208
|
-
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void
|
|
209
|
-
onSubmit: (e: React.FormEvent<HTMLFormElement>) => void
|
|
210
|
-
onKeyDown: (e: React.KeyboardEvent<HTMLInputElement>) => void
|
|
211
|
-
onFocus: (e: React.FocusEvent<HTMLInputElement>) => void
|
|
212
|
-
onDrag: (e: React.DragEvent<HTMLDivElement>) => void
|
|
213
|
-
\`\`\`
|
|
248
|
+
Prefer exact DOM event types at the boundary: \`MouseEvent<HTMLButtonElement>\`, \`ChangeEvent<HTMLInputElement>\`, \`FormEvent<HTMLFormElement>\`. If you start with a broad \`SyntheticEvent\`, you are probably typing too late.
|
|
214
249
|
|
|
215
250
|
## Hooks Typing
|
|
216
251
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
const inputRef = useRef<HTMLInputElement>(null);
|
|
223
|
-
|
|
224
|
-
// useRef for mutable value — no null, get MutableRefObject
|
|
225
|
-
const intervalRef = useRef<number>(0);
|
|
226
|
-
|
|
227
|
-
// useReducer with discriminated actions
|
|
228
|
-
type Action = { type: "increment" } | { type: "set"; payload: number };
|
|
229
|
-
const [count, dispatch] = useReducer((state: number, action: Action) => {
|
|
230
|
-
switch (action.type) {
|
|
231
|
-
case "increment": return state + 1;
|
|
232
|
-
case "set": return action.payload;
|
|
233
|
-
}
|
|
234
|
-
}, 0);
|
|
235
|
-
|
|
236
|
-
// Custom hook: return as const for tuple types
|
|
237
|
-
function useToggle(initial = false) {
|
|
238
|
-
const [value, setValue] = useState(initial);
|
|
239
|
-
const toggle = useCallback(() => setValue(v => !v), []);
|
|
240
|
-
return [value, toggle] as const;
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
// useContext with null guard
|
|
244
|
-
const ctx = useContext(ThemeContext);
|
|
245
|
-
if (!ctx) throw new Error("useTheme must be used within ThemeProvider");
|
|
246
|
-
\`\`\`
|
|
252
|
+
- \`useState\` — model UI state with explicit unions, not booleans that drift out of sync
|
|
253
|
+
- \`useRef<HTMLInputElement>(null)\` for DOM refs; mutable refs only for non-render state
|
|
254
|
+
- \`useReducer\` — discriminated union actions when state transitions are non-trivial
|
|
255
|
+
- Custom hooks returning tuples should use \`as const\`
|
|
256
|
+
- Context consumers should null-guard and fail fast outside their provider
|
|
247
257
|
|
|
248
258
|
## Performance Rules
|
|
249
259
|
|
|
250
260
|
### Prevent Async Waterfalls
|
|
251
261
|
\`\`\`tsx
|
|
252
|
-
// BAD — sequential fetching
|
|
253
262
|
const user = await getUser(id);
|
|
254
|
-
const posts = await getPosts(user.id);
|
|
255
|
-
const comments = await getComments(posts[0].id); // Waits for posts
|
|
263
|
+
const posts = await getPosts(user.id);
|
|
256
264
|
|
|
257
|
-
// GOOD — parallel where possible
|
|
258
265
|
const [user, posts] = await Promise.all([getUser(id), getPosts(id)]);
|
|
259
266
|
\`\`\`
|
|
260
267
|
|
|
261
|
-
###
|
|
262
|
-
-
|
|
263
|
-
-
|
|
264
|
-
-
|
|
265
|
-
-
|
|
266
|
-
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
- Default to Server Components — zero JS shipped to client
|
|
270
|
-
- Stream with Suspense: wrap slow data fetchers in \`<Suspense fallback={<Skeleton />}>\`
|
|
271
|
-
- Cache expensive computations with \`"use cache"\` directive
|
|
272
|
-
- Avoid serializing large objects across server/client boundary
|
|
273
|
-
|
|
274
|
-
### Client-Side Data Fetching
|
|
275
|
-
- Use SWR or React Query — automatic caching, revalidation, error retry
|
|
276
|
-
- Stale-while-revalidate: show cached data instantly, refresh in background
|
|
277
|
-
- Error boundaries: \`<ErrorBoundary>\` wrapping data-dependent trees
|
|
278
|
-
- Prefetch on hover/viewport for likely next navigations
|
|
279
|
-
|
|
280
|
-
### Re-render Optimization
|
|
281
|
-
- React Compiler (React 19): auto-memoizes — try this first before manual \`useMemo\`/\`useCallback\`
|
|
282
|
-
- If no compiler: \`useMemo\` for expensive computations, \`useCallback\` for stable function references
|
|
283
|
-
- Extract static JSX outside components — constants don't re-create
|
|
284
|
-
- Split context: separate frequently-changing state from rarely-changing state
|
|
285
|
-
- \`React.memo()\` on components receiving only primitive props
|
|
286
|
-
|
|
287
|
-
### Rendering Performance
|
|
288
|
-
- **Virtualize** lists > 100 items: \`@tanstack/react-virtual\`, \`react-window\`
|
|
289
|
-
- Defer heavy computations with \`useDeferredValue\`
|
|
290
|
-
- Batch state updates (React 18+ does this automatically in event handlers)
|
|
291
|
-
- Avoid reading DOM layout during renders (force sync layout = jank)
|
|
292
|
-
|
|
293
|
-
### Advanced Patterns
|
|
294
|
-
- Progressive hydration: load and hydrate components as they enter viewport
|
|
295
|
-
- Concurrent features: \`useTransition\` for non-blocking state updates
|
|
296
|
-
- Suspense caching: combine \`<Suspense>\` with data cache for instant page transitions
|
|
297
|
-
- React Compiler adoption: enable gradually, check for unsafe patterns first
|
|
298
|
-
|
|
299
|
-
## Decision Flow
|
|
300
|
-
|
|
301
|
-
When implementing a React feature:
|
|
268
|
+
### Performance Checklist
|
|
269
|
+
- Server Components first; ship less JS before optimizing JS
|
|
270
|
+
- \`<Suspense>\` + streaming for slow server work
|
|
271
|
+
- SWR/React Query when client data must stay fresh after hydration
|
|
272
|
+
- React Compiler first, then manual \`useMemo\`/\`useCallback\` only when profiling proves need
|
|
273
|
+
- Virtualize lists >100 items; defer heavy client work with \`useDeferredValue\`
|
|
274
|
+
- Lazy load below-fold components; prefer named imports for tree-shaking
|
|
275
|
+
- Avoid passing large objects across the server/client boundary
|
|
302
276
|
|
|
303
|
-
1. **Server or Client?** → If it uses browser APIs, event handlers, or useState → Client. Everything else → Server.
|
|
304
|
-
2. **Data fetching?** → Server Component for initial data. SWR/React Query for client-side mutations.
|
|
305
|
-
3. **Form?** → Server Action + \`useActionState\` + Zod validation.
|
|
306
|
-
4. **Optimistic?** → \`useOptimistic\` for instant feedback.
|
|
307
|
-
5. **Loading?** → \`<Suspense>\` with skeleton fallback.
|
|
308
|
-
6. **Performance?** → Try React Compiler first. Then manual memo. Then virtualization. Then code splitting.
|
|
309
|
-
7. **Error?** → Error boundaries for rendering errors. try/catch for Server Actions.
|
|
310
277
|
`}];export{e as default};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
var e=[{file:`SKILL.md`,content:`---
|
|
2
2
|
name: requirements-clarity
|
|
3
|
-
description: Clarify ambiguous requirements through focused dialogue before implementation. Use when requirements are unclear, features are complex (>2 days),
|
|
3
|
+
description: Clarify ambiguous requirements through focused dialogue before implementation. Use when requirements are unclear, features are complex (>2 days), involve cross-team coordination, or when the user says 'I want to build...', 'add a feature for...', 'implement...', 'create a system that...'. Ask two core questions first - Why? (YAGNI check) and Simpler? (KISS check) - then score 0-100 until clarity reaches 90+. Critical for preventing rework on underspecified features.
|
|
4
4
|
metadata:
|
|
5
5
|
category: cross-cutting
|
|
6
6
|
domain: general
|
|
@@ -20,6 +20,17 @@ Do NOT activate when:
|
|
|
20
20
|
- Bug fixes with clear reproduction steps
|
|
21
21
|
- Task is a single-file change with obvious scope
|
|
22
22
|
|
|
23
|
+
## Mindset
|
|
24
|
+
|
|
25
|
+
Requirements engineering is detective work, not stenography. Your job is NOT to write down what the user says - it's to uncover what they haven't said yet.
|
|
26
|
+
|
|
27
|
+
The user knows their PROBLEM but often conflates it with a specific SOLUTION. Separate these. The problem is the requirement; the solution is a design decision.
|
|
28
|
+
|
|
29
|
+
Before asking questions, mentally model:
|
|
30
|
+
- **Stakeholders** - Who else cares? Who gets paged at 2am? Who pays?
|
|
31
|
+
- **Failure modes** - What breaks? What's the blast radius? What's unrecoverable?
|
|
32
|
+
- **Boundaries** - What's explicitly OUT of scope? What's "phase 2"?
|
|
33
|
+
|
|
23
34
|
## Protocol
|
|
24
35
|
|
|
25
36
|
### 1. YAGNI/KISS Gate
|
|
@@ -41,7 +52,39 @@ Assess the requirement against this rubric:
|
|
|
41
52
|
| Implementation Completeness | /25 | Edge cases (8), error handling (9), data validation (8) |
|
|
42
53
|
| Business Context | /20 | Problem statement (7), target users (7), success metrics (6) |
|
|
43
54
|
|
|
44
|
-
|
|
55
|
+
### Calibration Examples
|
|
56
|
+
|
|
57
|
+
**Functional Clarity 10/10:**
|
|
58
|
+
"User clicks Submit -> system validates email format (RFC 5322), checks uniqueness against users table, returns 201 with serialized user object (id, email, createdAt) or 422 with field-level errors array [{field, code, message}]"
|
|
59
|
+
|
|
60
|
+
**Functional Clarity 3/10:**
|
|
61
|
+
"User can sign up"
|
|
62
|
+
|
|
63
|
+
**Technical Specificity 9/9 (constraints):**
|
|
64
|
+
"Response time < 200ms p95 for all CRUD endpoints, PostgreSQL 16+, horizontal scaling via stateless workers behind ALB, no server-side sessions"
|
|
65
|
+
|
|
66
|
+
**Technical Specificity 2/9:**
|
|
67
|
+
"Should be fast and scalable"
|
|
68
|
+
|
|
69
|
+
**Edge Cases 8/8:**
|
|
70
|
+
"Empty input -> show inline validation before submit. Concurrent duplicate emails -> second request gets 409 Conflict. Network timeout -> client retries 3x with exponential backoff, then shows offline banner"
|
|
71
|
+
|
|
72
|
+
**Edge Cases 2/8:**
|
|
73
|
+
"Handle errors appropriately"
|
|
74
|
+
|
|
75
|
+
### Expert Probing Techniques
|
|
76
|
+
|
|
77
|
+
For each gap category, ask focused questions that reveal hidden requirements:
|
|
78
|
+
|
|
79
|
+
**For edge cases:** Ask about empty states, concurrent access, partial failures, permission boundaries, rate limits, timezone/locale behavior, offline/degraded mode, data migration from existing state.
|
|
80
|
+
|
|
81
|
+
**For error handling:** Ask "What happens when [external dependency] is down?", "What does the user see during a 30-second timeout?", "How does the system recover from partial writes?"
|
|
82
|
+
|
|
83
|
+
**For integration points:** Ask "Who calls this?", "What calls does this make?", "What happens if the downstream service changes its contract?", "Is there a circuit breaker?"
|
|
84
|
+
|
|
85
|
+
**For data validation:** Ask "What's the maximum size?", "What characters are allowed?", "What about Unicode/emoji?", "What happens with 0, null, negative numbers, MAX_INT?"
|
|
86
|
+
|
|
87
|
+
**For business context:** Ask "Who loses money if this breaks?", "What's the rollback plan?", "How will you know this is working in production?"
|
|
45
88
|
|
|
46
89
|
### 3. Iterate Until ≥ 90
|
|
47
90
|
|
|
@@ -57,6 +100,16 @@ Prefer short, targeted questions over long questionnaires. Build on previous ans
|
|
|
57
100
|
|
|
58
101
|
**HARD RULE:** Do not generate the requirements document with a score below 90.
|
|
59
102
|
|
|
103
|
+
## NEVER
|
|
104
|
+
|
|
105
|
+
- **NEVER accept vague acceptance criteria** like "it should work" or "user-friendly" - demand measurable outcomes (response time, error rate, specific user action -> specific system response)
|
|
106
|
+
- **NEVER skip the YAGNI check** - 40% of requirements are premature. Ask "what happens if we ship without this?" first
|
|
107
|
+
- **NEVER ask more than 3 questions per round** - cognitive overload causes shallow answers. Depth > breadth.
|
|
108
|
+
- **NEVER accept "edge cases: handle errors appropriately"** - this means "I haven't thought about it." Push for specific scenarios.
|
|
109
|
+
- **NEVER generate the document below score 90** - incomplete requirements cause more rework than taking time to clarify
|
|
110
|
+
- **NEVER repeat questions the user already answered** - build on previous responses, reference what they said
|
|
111
|
+
- **NEVER score above 80 without concrete acceptance criteria** - if you can't write a test for it, it's not a requirement
|
|
112
|
+
|
|
60
113
|
### 4. Generate Requirements Document
|
|
61
114
|
|
|
62
115
|
**Output path (flow-aware):**
|