codecruise 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (129) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +111 -0
  3. package/bin/codecruise.js +68 -0
  4. package/config/CLAUDE.md +107 -0
  5. package/config/agents/analyst.md +48 -0
  6. package/config/agents/architect-reviewer.md +161 -0
  7. package/config/agents/architect.md +119 -0
  8. package/config/agents/critic.md +63 -0
  9. package/config/agents/developer.md +96 -0
  10. package/config/agents/devops.md +81 -0
  11. package/config/agents/orchestrator.md +91 -0
  12. package/config/agents/planner.md +139 -0
  13. package/config/agents/retro.md +52 -0
  14. package/config/agents/reviewer.md +101 -0
  15. package/config/agents/security-reviewer.md +57 -0
  16. package/config/agents/stack/expo/AGENT.md +473 -0
  17. package/config/agents/stack/expo/rules/critical.md +427 -0
  18. package/config/agents/stack/expo/rules/native.md +455 -0
  19. package/config/agents/stack/expo/rules/navigation.md +445 -0
  20. package/config/agents/stack/expo/rules/performance.md +415 -0
  21. package/config/agents/stack/fastify/AGENT.md +397 -0
  22. package/config/agents/stack/fastify/rules/api-design.md +283 -0
  23. package/config/agents/stack/fastify/rules/critical.md +232 -0
  24. package/config/agents/stack/fastify/rules/queues.md +303 -0
  25. package/config/agents/stack/fastify/rules/security.md +384 -0
  26. package/config/agents/stack/index.yaml +48 -0
  27. package/config/agents/stack/nextjs/AGENT.md +421 -0
  28. package/config/agents/stack/nextjs/rules/components.md +413 -0
  29. package/config/agents/stack/nextjs/rules/critical.md +391 -0
  30. package/config/agents/stack/nextjs/rules/performance.md +403 -0
  31. package/config/agents/stack/nextjs/rules/styling.md +334 -0
  32. package/config/agents/stack/shared-ts/AGENT.md +384 -0
  33. package/config/agents/stack/shared-ts/rules/critical.md +315 -0
  34. package/config/agents/stack/shared-ts/rules/patterns.md +384 -0
  35. package/config/agents/stack/shared-ts/rules/zod.md +427 -0
  36. package/config/agents/tester.md +79 -0
  37. package/config/commands/architect-discuss.md +366 -0
  38. package/config/commands/architect-list.md +160 -0
  39. package/config/commands/architect-review.md +111 -0
  40. package/config/commands/architect.md +118 -0
  41. package/config/commands/compact.md +118 -0
  42. package/config/commands/companion.md +279 -0
  43. package/config/commands/dashboard.md +152 -0
  44. package/config/commands/doctor.md +227 -0
  45. package/config/commands/dogfood-report.md +101 -0
  46. package/config/commands/flags/run-autonomous.md +110 -0
  47. package/config/commands/flags/run-pause.md +80 -0
  48. package/config/commands/ingest.md +173 -0
  49. package/config/commands/init.md +128 -0
  50. package/config/commands/metrics.md +87 -0
  51. package/config/commands/parallel.md +320 -0
  52. package/config/commands/pause.md +55 -0
  53. package/config/commands/plan-review.md +130 -0
  54. package/config/commands/plan.md +216 -0
  55. package/config/commands/production-check.md +308 -0
  56. package/config/commands/refine.md +323 -0
  57. package/config/commands/resume.md +72 -0
  58. package/config/commands/retro.md +121 -0
  59. package/config/commands/retry.md +75 -0
  60. package/config/commands/role.md +310 -0
  61. package/config/commands/run.md +417 -0
  62. package/config/commands/scope.md +85 -0
  63. package/config/commands/setup-permissions.md +104 -0
  64. package/config/commands/skip.md +75 -0
  65. package/config/commands/spec-forge.md +213 -0
  66. package/config/commands/spec-help.md +194 -0
  67. package/config/commands/spec-patch.md +342 -0
  68. package/config/commands/spec-resolve.md +110 -0
  69. package/config/commands/spec-review.md +153 -0
  70. package/config/commands/status.md +114 -0
  71. package/config/commands/sync.md +131 -0
  72. package/config/commands/task.md +138 -0
  73. package/config/commands/verify.md +124 -0
  74. package/config/hooks/README.md +632 -0
  75. package/config/hooks/activity-log.sh +187 -0
  76. package/config/hooks/anti-rationalize.sh +52 -0
  77. package/config/hooks/capture-verification.sh +112 -0
  78. package/config/hooks/collect-metrics.sh +135 -0
  79. package/config/hooks/enforce-file-scope.sh +75 -0
  80. package/config/hooks/enforce-state-machine.sh +161 -0
  81. package/config/hooks/enforce-tdd.sh +180 -0
  82. package/config/hooks/format.sh +40 -0
  83. package/config/hooks/lib/activity-helpers.sh +162 -0
  84. package/config/hooks/lib/read-settings.sh +71 -0
  85. package/config/hooks/load-context-skills.sh +95 -0
  86. package/config/hooks/notify.sh +81 -0
  87. package/config/hooks/pre-commit.sample +35 -0
  88. package/config/hooks/protect-files.sh +63 -0
  89. package/config/hooks/track-agents.sh +41 -0
  90. package/config/hooks/track-commands.sh +37 -0
  91. package/config/hooks/track-enforcement.sh +44 -0
  92. package/config/hooks/track-ooda.sh +77 -0
  93. package/config/hooks/validate-commit-msg.sh +35 -0
  94. package/config/hooks/validate-plan.sh +213 -0
  95. package/config/hooks/verify-criteria.sh +46 -0
  96. package/config/hooks/verify-todo-completion.sh +140 -0
  97. package/config/rules/comments.md +25 -0
  98. package/config/rules/decision-rules.md +308 -0
  99. package/config/rules/hygiene.md +247 -0
  100. package/config/rules/pattern-detection.md +372 -0
  101. package/config/rules/profiles.md +193 -0
  102. package/config/rules/recovery.md +83 -0
  103. package/config/rules/scope-detection.md +213 -0
  104. package/config/rules/standards.md +127 -0
  105. package/config/rules/workflow.md +121 -0
  106. package/config/schemas.md +767 -0
  107. package/config/settings.json +195 -0
  108. package/config/skills/backend/SKILL.md +734 -0
  109. package/config/skills/database/SKILL.md +426 -0
  110. package/config/skills/frontend/SKILL.md +434 -0
  111. package/config/skills/git/SKILL.md +396 -0
  112. package/config/skills/index.yaml +36 -0
  113. package/config/skills/observability/SKILL.md +430 -0
  114. package/config/skills/package-dev/SKILL.md +498 -0
  115. package/config/skills/performance/SKILL.md +378 -0
  116. package/config/skills/resilience/SKILL.md +573 -0
  117. package/config/skills/testing/SKILL.md +398 -0
  118. package/config/skills/testing-patterns/SKILL.md +276 -0
  119. package/config/skills/typescript/SKILL.md +152 -0
  120. package/config/templates/CLAUDE.md +70 -0
  121. package/config/templates/README.md +117 -0
  122. package/config/templates/steering/adr-template.md +102 -0
  123. package/config/templates/steering/product.md +60 -0
  124. package/config/templates/steering/rfc-template.md +159 -0
  125. package/config/templates/steering/structure.md +146 -0
  126. package/config/templates/steering/tech.md +85 -0
  127. package/package.json +40 -0
  128. package/src/install.js +163 -0
  129. package/src/report.js +310 -0
@@ -0,0 +1,434 @@
1
+ ---
2
+ name: frontend
3
+ description: React, accessibility, and performance patterns
4
+ version: 1.0.0
5
+ triggers:
6
+ - react
7
+ - component
8
+ - "*.tsx"
9
+ - accessibility
10
+ - a11y
11
+ - UI
12
+ - tailwind
13
+ ---
14
+
15
+ # Frontend Skill
16
+
17
+ Comprehensive React, accessibility, and performance patterns.
18
+
19
+ ## Quick Reference
20
+
21
+ ### React Patterns
22
+
23
+ | ID | Rule | Priority |
24
+ |----|------|----------|
25
+ | react-1 | Use composition over prop drilling | CRITICAL |
26
+ | react-2 | Server Components by default, Client for interactivity | CRITICAL |
27
+ | react-3 | Colocate state with components that use it | HIGH |
28
+ | react-4 | Use discriminated unions for component states | HIGH |
29
+ | react-5 | Avoid useEffect for data fetching | HIGH |
30
+ | react-6 | Memoize only after measuring performance | MEDIUM |
31
+ | react-7 | Use React.lazy for code splitting | MEDIUM |
32
+ | react-8 | Prefer controlled inputs for forms | MEDIUM |
33
+ | react-9 | Use error boundaries for graceful failures | HIGH |
34
+ | react-10 | Keep components under 200 lines | MEDIUM |
35
+
36
+ ### Accessibility (WCAG 2.1 AA)
37
+
38
+ | ID | Rule | Priority |
39
+ |----|------|----------|
40
+ | a11y-1 | Use semantic HTML elements | CRITICAL |
41
+ | a11y-2 | All images must have alt text | CRITICAL |
42
+ | a11y-3 | Interactive elements must be keyboard accessible | CRITICAL |
43
+ | a11y-4 | Maintain visible focus indicators | CRITICAL |
44
+ | a11y-5 | Color contrast ratio minimum 4.5:1 for text | CRITICAL |
45
+ | a11y-6 | Form inputs must have associated labels | CRITICAL |
46
+ | a11y-7 | Announce dynamic content with aria-live | HIGH |
47
+ | a11y-8 | Use skip links for main content | HIGH |
48
+ | a11y-9 | Support reduced motion preferences | HIGH |
49
+ | a11y-10 | Touch targets minimum 44x44 pixels | HIGH |
50
+ | a11y-11 | Provide text alternatives for icons | MEDIUM |
51
+ | a11y-12 | Use aria-describedby for error messages | MEDIUM |
52
+ | a11y-13 | Modal dialogs must trap focus | HIGH |
53
+ | a11y-14 | Use proper heading hierarchy (h1-h6) | HIGH |
54
+ | a11y-15 | Tables must have headers and captions | MEDIUM |
55
+
56
+ ### Performance
57
+
58
+ | ID | Rule | Priority |
59
+ |----|------|----------|
60
+ | perf-1 | Lazy load below-the-fold content | HIGH |
61
+ | perf-2 | Use next/image for optimized images | HIGH |
62
+ | perf-3 | Avoid layout shift (CLS < 0.1) | HIGH |
63
+ | perf-4 | Minimize JavaScript bundle size | HIGH |
64
+ | perf-5 | Use CSS for animations, not JS | MEDIUM |
65
+ | perf-6 | Debounce expensive event handlers | MEDIUM |
66
+ | perf-7 | Virtualize long lists (>100 items) | MEDIUM |
67
+ | perf-8 | Preload critical assets | MEDIUM |
68
+ | perf-9 | Avoid blocking the main thread | HIGH |
69
+ | perf-10 | Use web workers for heavy computation | LOW |
70
+
71
+ ---
72
+
73
+ ## Critical Rules
74
+
75
+ ### react-1: Composition Over Props
76
+
77
+ ```tsx
78
+ // BAD: Prop drilling
79
+ <Card showHeader showFooter showBorder variant="primary" size="lg" />
80
+
81
+ // GOOD: Composition
82
+ <Card>
83
+ <Card.Header>Title</Card.Header>
84
+ <Card.Body>Content</Card.Body>
85
+ <Card.Footer>Actions</Card.Footer>
86
+ </Card>
87
+ ```
88
+
89
+ ### react-2: Server vs Client Components
90
+
91
+ ```tsx
92
+ // Server Component (default) - data fetching
93
+ async function UserList() {
94
+ const users = await db.users.findMany();
95
+ return <ul>{users.map(u => <li key={u.id}>{u.name}</li>)}</ul>;
96
+ }
97
+
98
+ // Client Component - interactivity
99
+ 'use client'
100
+ function Counter() {
101
+ const [count, setCount] = useState(0);
102
+ return <button onClick={() => setCount(c => c + 1)}>{count}</button>;
103
+ }
104
+ ```
105
+
106
+ ### react-4: Discriminated Unions for State
107
+
108
+ ```tsx
109
+ type State =
110
+ | { status: 'idle' }
111
+ | { status: 'loading' }
112
+ | { status: 'success'; data: User[] }
113
+ | { status: 'error'; error: Error };
114
+
115
+ function UserList() {
116
+ const [state, setState] = useState<State>({ status: 'idle' });
117
+
118
+ switch (state.status) {
119
+ case 'loading': return <Spinner />;
120
+ case 'error': return <Error message={state.error.message} />;
121
+ case 'success': return <List users={state.data} />;
122
+ default: return <Button onClick={load}>Load</Button>;
123
+ }
124
+ }
125
+ ```
126
+
127
+ ### a11y-1: Semantic HTML
128
+
129
+ ```tsx
130
+ // BAD
131
+ <div onClick={handleClick}>Click me</div>
132
+ <div class="nav">...</div>
133
+
134
+ // GOOD
135
+ <button onClick={handleClick}>Click me</button>
136
+ <nav aria-label="Main navigation">...</nav>
137
+ <main id="main-content">...</main>
138
+ <article>...</article>
139
+ <aside>...</aside>
140
+ ```
141
+
142
+ ### a11y-2: Image Alt Text
143
+
144
+ ```tsx
145
+ // Decorative image
146
+ <img src="decoration.svg" alt="" role="presentation" />
147
+
148
+ // Informative image
149
+ <img src="chart.png" alt="Sales increased 25% in Q4 2024" />
150
+
151
+ // Linked image
152
+ <a href="/profile">
153
+ <img src="avatar.jpg" alt="View John's profile" />
154
+ </a>
155
+ ```
156
+
157
+ ### a11y-3: Keyboard Navigation
158
+
159
+ ```tsx
160
+ // Ensure all interactive elements are focusable
161
+ <button>Focusable by default</button>
162
+
163
+ // Custom interactive element needs tabIndex
164
+ <div
165
+ role="button"
166
+ tabIndex={0}
167
+ onClick={handleClick}
168
+ onKeyDown={(e) => {
169
+ if (e.key === 'Enter' || e.key === ' ') {
170
+ handleClick();
171
+ }
172
+ }}
173
+ >
174
+ Custom button
175
+ </div>
176
+ ```
177
+
178
+ ### a11y-4: Focus Indicators
179
+
180
+ ```css
181
+ /* Never remove focus outline without replacement */
182
+ /* BAD */
183
+ button:focus { outline: none; }
184
+
185
+ /* GOOD */
186
+ button:focus-visible {
187
+ outline: 2px solid var(--focus-color);
188
+ outline-offset: 2px;
189
+ }
190
+ ```
191
+
192
+ ### a11y-6: Form Labels
193
+
194
+ ```tsx
195
+ // Explicit label
196
+ <label htmlFor="email">Email</label>
197
+ <input id="email" type="email" />
198
+
199
+ // Implicit label
200
+ <label>
201
+ Email
202
+ <input type="email" />
203
+ </label>
204
+
205
+ // With error
206
+ <label htmlFor="email">Email</label>
207
+ <input
208
+ id="email"
209
+ type="email"
210
+ aria-invalid={!!error}
211
+ aria-describedby={error ? "email-error" : undefined}
212
+ />
213
+ {error && <span id="email-error" role="alert">{error}</span>}
214
+ ```
215
+
216
+ ### a11y-9: Reduced Motion
217
+
218
+ ```tsx
219
+ // CSS
220
+ @media (prefers-reduced-motion: reduce) {
221
+ *, *::before, *::after {
222
+ animation-duration: 0.01ms !important;
223
+ transition-duration: 0.01ms !important;
224
+ }
225
+ }
226
+
227
+ // React
228
+ const prefersReducedMotion = window.matchMedia(
229
+ '(prefers-reduced-motion: reduce)'
230
+ ).matches;
231
+
232
+ <motion.div
233
+ animate={{ opacity: 1 }}
234
+ transition={{ duration: prefersReducedMotion ? 0 : 0.3 }}
235
+ />
236
+ ```
237
+
238
+ ### a11y-13: Modal Focus Trap
239
+
240
+ ```tsx
241
+ function Modal({ isOpen, onClose, children }) {
242
+ const modalRef = useRef<HTMLDivElement>(null);
243
+
244
+ useEffect(() => {
245
+ if (!isOpen) return;
246
+
247
+ const focusableElements = modalRef.current?.querySelectorAll(
248
+ 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
249
+ );
250
+ const firstElement = focusableElements?.[0] as HTMLElement;
251
+ const lastElement = focusableElements?.[focusableElements.length - 1] as HTMLElement;
252
+
253
+ firstElement?.focus();
254
+
255
+ const handleKeyDown = (e: KeyboardEvent) => {
256
+ if (e.key === 'Escape') onClose();
257
+ if (e.key === 'Tab') {
258
+ if (e.shiftKey && document.activeElement === firstElement) {
259
+ e.preventDefault();
260
+ lastElement?.focus();
261
+ } else if (!e.shiftKey && document.activeElement === lastElement) {
262
+ e.preventDefault();
263
+ firstElement?.focus();
264
+ }
265
+ }
266
+ };
267
+
268
+ document.addEventListener('keydown', handleKeyDown);
269
+ return () => document.removeEventListener('keydown', handleKeyDown);
270
+ }, [isOpen, onClose]);
271
+
272
+ if (!isOpen) return null;
273
+
274
+ return (
275
+ <div role="dialog" aria-modal="true" ref={modalRef}>
276
+ {children}
277
+ </div>
278
+ );
279
+ }
280
+ ```
281
+
282
+ ### perf-1: Lazy Loading
283
+
284
+ ```tsx
285
+ // Dynamic imports
286
+ const HeavyChart = dynamic(() => import('./HeavyChart'), {
287
+ loading: () => <Skeleton />,
288
+ ssr: false,
289
+ });
290
+
291
+ // Intersection Observer for images
292
+ function LazyImage({ src, alt }) {
293
+ const [isVisible, setIsVisible] = useState(false);
294
+ const ref = useRef<HTMLDivElement>(null);
295
+
296
+ useEffect(() => {
297
+ const observer = new IntersectionObserver(
298
+ ([entry]) => {
299
+ if (entry.isIntersecting) {
300
+ setIsVisible(true);
301
+ observer.disconnect();
302
+ }
303
+ },
304
+ { rootMargin: '100px' }
305
+ );
306
+ if (ref.current) observer.observe(ref.current);
307
+ return () => observer.disconnect();
308
+ }, []);
309
+
310
+ return (
311
+ <div ref={ref}>
312
+ {isVisible && <img src={src} alt={alt} />}
313
+ </div>
314
+ );
315
+ }
316
+ ```
317
+
318
+ ### perf-3: Avoid Layout Shift
319
+
320
+ ```tsx
321
+ // Reserve space for images
322
+ <div style={{ aspectRatio: '16/9' }}>
323
+ <Image src={src} alt={alt} fill />
324
+ </div>
325
+
326
+ // Reserve space for dynamic content
327
+ <div style={{ minHeight: '200px' }}>
328
+ {isLoading ? <Skeleton /> : <Content />}
329
+ </div>
330
+
331
+ // Use CSS containment
332
+ .card {
333
+ contain: layout style;
334
+ }
335
+ ```
336
+
337
+ ### perf-7: Virtualize Long Lists
338
+
339
+ ```tsx
340
+ import { useVirtualizer } from '@tanstack/react-virtual';
341
+
342
+ function VirtualList({ items }) {
343
+ const parentRef = useRef<HTMLDivElement>(null);
344
+
345
+ const virtualizer = useVirtualizer({
346
+ count: items.length,
347
+ getScrollElement: () => parentRef.current,
348
+ estimateSize: () => 50,
349
+ });
350
+
351
+ return (
352
+ <div ref={parentRef} style={{ height: '400px', overflow: 'auto' }}>
353
+ <div style={{ height: `${virtualizer.getTotalSize()}px`, position: 'relative' }}>
354
+ {virtualizer.getVirtualItems().map((virtualItem) => (
355
+ <div
356
+ key={virtualItem.key}
357
+ style={{
358
+ position: 'absolute',
359
+ top: 0,
360
+ transform: `translateY(${virtualItem.start}px)`,
361
+ height: `${virtualItem.size}px`,
362
+ }}
363
+ >
364
+ {items[virtualItem.index]}
365
+ </div>
366
+ ))}
367
+ </div>
368
+ </div>
369
+ );
370
+ }
371
+ ```
372
+
373
+ ---
374
+
375
+ ## Form Patterns
376
+
377
+ ```tsx
378
+ // react-hook-form + zod
379
+ const schema = z.object({
380
+ email: z.string().email('Invalid email'),
381
+ password: z.string().min(8, 'Min 8 characters'),
382
+ });
383
+
384
+ function LoginForm() {
385
+ const { register, handleSubmit, formState: { errors } } = useForm({
386
+ resolver: zodResolver(schema),
387
+ });
388
+
389
+ return (
390
+ <form onSubmit={handleSubmit(onSubmit)}>
391
+ <label htmlFor="email">Email</label>
392
+ <input
393
+ id="email"
394
+ type="email"
395
+ aria-invalid={!!errors.email}
396
+ aria-describedby={errors.email ? 'email-error' : undefined}
397
+ {...register('email')}
398
+ />
399
+ {errors.email && (
400
+ <span id="email-error" role="alert">{errors.email.message}</span>
401
+ )}
402
+
403
+ <button type="submit">Login</button>
404
+ </form>
405
+ );
406
+ }
407
+ ```
408
+
409
+ ---
410
+
411
+ ## Error Boundaries
412
+
413
+ ```tsx
414
+ 'use client'
415
+
416
+ export default function Error({
417
+ error,
418
+ reset,
419
+ }: {
420
+ error: Error & { digest?: string };
421
+ reset: () => void;
422
+ }) {
423
+ useEffect(() => {
424
+ console.error(error);
425
+ }, [error]);
426
+
427
+ return (
428
+ <div role="alert">
429
+ <h2>Something went wrong</h2>
430
+ <button onClick={reset}>Try again</button>
431
+ </div>
432
+ );
433
+ }
434
+ ```