@girardmedia/bootspring 1.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 (88) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +255 -0
  3. package/agents/README.md +93 -0
  4. package/agents/api-expert/context.md +416 -0
  5. package/agents/architecture-expert/context.md +454 -0
  6. package/agents/backend-expert/context.md +483 -0
  7. package/agents/code-review-expert/context.md +365 -0
  8. package/agents/database-expert/context.md +250 -0
  9. package/agents/devops-expert/context.md +446 -0
  10. package/agents/frontend-expert/context.md +364 -0
  11. package/agents/index.js +140 -0
  12. package/agents/performance-expert/context.md +377 -0
  13. package/agents/security-expert/context.md +343 -0
  14. package/agents/testing-expert/context.md +414 -0
  15. package/agents/ui-ux-expert/context.md +448 -0
  16. package/agents/vercel-expert/context.md +426 -0
  17. package/bin/bootspring.js +310 -0
  18. package/cli/agent.js +337 -0
  19. package/cli/context.js +194 -0
  20. package/cli/dashboard.js +150 -0
  21. package/cli/generate.js +294 -0
  22. package/cli/init.js +410 -0
  23. package/cli/loop.js +421 -0
  24. package/cli/mcp.js +241 -0
  25. package/cli/memory.js +303 -0
  26. package/cli/orchestrator.js +400 -0
  27. package/cli/plugin.js +451 -0
  28. package/cli/quality.js +332 -0
  29. package/cli/skill.js +369 -0
  30. package/cli/task.js +628 -0
  31. package/cli/telemetry.js +114 -0
  32. package/cli/todo.js +614 -0
  33. package/cli/update.js +312 -0
  34. package/core/config.js +245 -0
  35. package/core/context.js +329 -0
  36. package/core/entitlements.js +209 -0
  37. package/core/index.js +43 -0
  38. package/core/policies.js +68 -0
  39. package/core/telemetry.js +247 -0
  40. package/core/utils.js +380 -0
  41. package/dashboard/server.js +818 -0
  42. package/docs/integrations/claude-code.md +42 -0
  43. package/docs/integrations/codex.md +42 -0
  44. package/docs/mcp-api-platform.md +102 -0
  45. package/generators/generate.js +598 -0
  46. package/generators/index.js +18 -0
  47. package/hooks/context-detector.js +177 -0
  48. package/hooks/index.js +35 -0
  49. package/hooks/prompt-enhancer.js +289 -0
  50. package/intelligence/git-memory.js +551 -0
  51. package/intelligence/index.js +59 -0
  52. package/intelligence/orchestrator.js +964 -0
  53. package/intelligence/prd.js +447 -0
  54. package/intelligence/recommendation-weights.json +18 -0
  55. package/intelligence/recommendations.js +234 -0
  56. package/mcp/capabilities.js +71 -0
  57. package/mcp/contracts/mcp-contract.v1.json +497 -0
  58. package/mcp/registry.js +213 -0
  59. package/mcp/response-formatter.js +462 -0
  60. package/mcp/server.js +99 -0
  61. package/mcp/tools/agent-tool.js +137 -0
  62. package/mcp/tools/capabilities-tool.js +54 -0
  63. package/mcp/tools/context-tool.js +49 -0
  64. package/mcp/tools/dashboard-tool.js +58 -0
  65. package/mcp/tools/generate-tool.js +46 -0
  66. package/mcp/tools/loop-tool.js +134 -0
  67. package/mcp/tools/memory-tool.js +180 -0
  68. package/mcp/tools/orchestrator-tool.js +232 -0
  69. package/mcp/tools/plugin-tool.js +76 -0
  70. package/mcp/tools/quality-tool.js +47 -0
  71. package/mcp/tools/skill-tool.js +233 -0
  72. package/mcp/tools/telemetry-tool.js +95 -0
  73. package/mcp/tools/todo-tool.js +133 -0
  74. package/package.json +98 -0
  75. package/plugins/index.js +141 -0
  76. package/quality/index.js +380 -0
  77. package/quality/lint-budgets.json +19 -0
  78. package/skills/index.js +787 -0
  79. package/skills/patterns/README.md +163 -0
  80. package/skills/patterns/api/route-handler.md +217 -0
  81. package/skills/patterns/api/server-action.md +249 -0
  82. package/skills/patterns/auth/clerk.md +132 -0
  83. package/skills/patterns/database/prisma.md +180 -0
  84. package/skills/patterns/payments/stripe.md +272 -0
  85. package/skills/patterns/security/validation.md +268 -0
  86. package/skills/patterns/testing/vitest.md +307 -0
  87. package/templates/bootspring.config.js +83 -0
  88. package/templates/mcp.json +9 -0
@@ -0,0 +1,448 @@
1
+ # UI/UX Expert Agent
2
+
3
+ ## Role
4
+ Specialized in user interface design, user experience patterns, accessibility (a11y), design systems, and creating intuitive, beautiful, and inclusive interfaces.
5
+
6
+ ## Core Expertise
7
+
8
+ ### Design System Components
9
+
10
+ ```tsx
11
+ // Design tokens
12
+ // tailwind.config.ts
13
+ export default {
14
+ theme: {
15
+ extend: {
16
+ colors: {
17
+ primary: {
18
+ 50: '#eff6ff',
19
+ 100: '#dbeafe',
20
+ 500: '#3b82f6',
21
+ 600: '#2563eb',
22
+ 700: '#1d4ed8',
23
+ },
24
+ success: '#22c55e',
25
+ warning: '#f59e0b',
26
+ error: '#ef4444',
27
+ },
28
+ spacing: {
29
+ // 4px grid system
30
+ '4.5': '1.125rem', // 18px
31
+ },
32
+ borderRadius: {
33
+ DEFAULT: '0.5rem',
34
+ lg: '0.75rem',
35
+ },
36
+ },
37
+ },
38
+ };
39
+
40
+ // Component with consistent styling
41
+ export function Card({ children, className }: { children: React.ReactNode; className?: string }) {
42
+ return (
43
+ <div className={cn(
44
+ 'rounded-lg border bg-card p-6 shadow-sm',
45
+ 'transition-shadow hover:shadow-md',
46
+ className
47
+ )}>
48
+ {children}
49
+ </div>
50
+ );
51
+ }
52
+ ```
53
+
54
+ ### Accessibility (a11y)
55
+
56
+ ```tsx
57
+ // Accessible button
58
+ export function Button({
59
+ children,
60
+ isLoading,
61
+ disabled,
62
+ 'aria-label': ariaLabel,
63
+ ...props
64
+ }: ButtonProps) {
65
+ return (
66
+ <button
67
+ disabled={disabled || isLoading}
68
+ aria-disabled={disabled || isLoading}
69
+ aria-busy={isLoading}
70
+ aria-label={ariaLabel}
71
+ className={cn(
72
+ 'inline-flex items-center justify-center',
73
+ 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2',
74
+ 'disabled:pointer-events-none disabled:opacity-50'
75
+ )}
76
+ {...props}
77
+ >
78
+ {isLoading && <Spinner className="mr-2" aria-hidden="true" />}
79
+ {children}
80
+ </button>
81
+ );
82
+ }
83
+
84
+ // Accessible form
85
+ export function ContactForm() {
86
+ const [errors, setErrors] = useState<Record<string, string>>({});
87
+
88
+ return (
89
+ <form aria-label="Contact form">
90
+ <div className="space-y-4">
91
+ <div>
92
+ <label htmlFor="email" className="block text-sm font-medium">
93
+ Email <span aria-hidden="true" className="text-error">*</span>
94
+ <span className="sr-only">(required)</span>
95
+ </label>
96
+ <input
97
+ id="email"
98
+ type="email"
99
+ required
100
+ aria-required="true"
101
+ aria-invalid={!!errors.email}
102
+ aria-describedby={errors.email ? 'email-error' : undefined}
103
+ className={cn(
104
+ 'mt-1 block w-full rounded-md border px-3 py-2',
105
+ errors.email && 'border-error'
106
+ )}
107
+ />
108
+ {errors.email && (
109
+ <p id="email-error" role="alert" className="mt-1 text-sm text-error">
110
+ {errors.email}
111
+ </p>
112
+ )}
113
+ </div>
114
+
115
+ <button type="submit" className="btn-primary">
116
+ Send Message
117
+ </button>
118
+ </div>
119
+ </form>
120
+ );
121
+ }
122
+
123
+ // Skip to content link
124
+ export function SkipLink() {
125
+ return (
126
+ <a
127
+ href="#main-content"
128
+ className={cn(
129
+ 'sr-only focus:not-sr-only',
130
+ 'focus:absolute focus:top-4 focus:left-4 focus:z-50',
131
+ 'focus:bg-primary focus:text-white focus:px-4 focus:py-2 focus:rounded'
132
+ )}
133
+ >
134
+ Skip to main content
135
+ </a>
136
+ );
137
+ }
138
+ ```
139
+
140
+ ### Keyboard Navigation
141
+
142
+ ```tsx
143
+ 'use client';
144
+
145
+ import { useRef, useState, useEffect } from 'react';
146
+
147
+ // Accessible dropdown menu
148
+ export function DropdownMenu({ trigger, items }: DropdownMenuProps) {
149
+ const [isOpen, setIsOpen] = useState(false);
150
+ const [activeIndex, setActiveIndex] = useState(-1);
151
+ const menuRef = useRef<HTMLUListElement>(null);
152
+ const triggerRef = useRef<HTMLButtonElement>(null);
153
+
154
+ const handleKeyDown = (e: React.KeyboardEvent) => {
155
+ switch (e.key) {
156
+ case 'ArrowDown':
157
+ e.preventDefault();
158
+ setActiveIndex(i => Math.min(i + 1, items.length - 1));
159
+ break;
160
+ case 'ArrowUp':
161
+ e.preventDefault();
162
+ setActiveIndex(i => Math.max(i - 1, 0));
163
+ break;
164
+ case 'Home':
165
+ e.preventDefault();
166
+ setActiveIndex(0);
167
+ break;
168
+ case 'End':
169
+ e.preventDefault();
170
+ setActiveIndex(items.length - 1);
171
+ break;
172
+ case 'Escape':
173
+ setIsOpen(false);
174
+ triggerRef.current?.focus();
175
+ break;
176
+ case 'Enter':
177
+ case ' ':
178
+ if (activeIndex >= 0) {
179
+ items[activeIndex].onSelect?.();
180
+ setIsOpen(false);
181
+ }
182
+ break;
183
+ }
184
+ };
185
+
186
+ return (
187
+ <div className="relative">
188
+ <button
189
+ ref={triggerRef}
190
+ aria-haspopup="menu"
191
+ aria-expanded={isOpen}
192
+ onClick={() => setIsOpen(!isOpen)}
193
+ >
194
+ {trigger}
195
+ </button>
196
+
197
+ {isOpen && (
198
+ <ul
199
+ ref={menuRef}
200
+ role="menu"
201
+ aria-orientation="vertical"
202
+ onKeyDown={handleKeyDown}
203
+ className="absolute mt-1 py-1 bg-white rounded-lg shadow-lg border"
204
+ >
205
+ {items.map((item, index) => (
206
+ <li
207
+ key={item.id}
208
+ role="menuitem"
209
+ tabIndex={activeIndex === index ? 0 : -1}
210
+ className={cn(
211
+ 'px-4 py-2 cursor-pointer',
212
+ activeIndex === index && 'bg-primary-50'
213
+ )}
214
+ >
215
+ {item.label}
216
+ </li>
217
+ ))}
218
+ </ul>
219
+ )}
220
+ </div>
221
+ );
222
+ }
223
+ ```
224
+
225
+ ### Loading States & Feedback
226
+
227
+ ```tsx
228
+ // Skeleton loading
229
+ export function CardSkeleton() {
230
+ return (
231
+ <div className="rounded-lg border p-6 animate-pulse">
232
+ <div className="h-4 bg-gray-200 rounded w-3/4 mb-4" />
233
+ <div className="h-4 bg-gray-200 rounded w-1/2 mb-2" />
234
+ <div className="h-4 bg-gray-200 rounded w-5/6" />
235
+ </div>
236
+ );
237
+ }
238
+
239
+ // Toast notifications
240
+ import { toast } from 'sonner';
241
+
242
+ // Success feedback
243
+ toast.success('Profile updated successfully');
244
+
245
+ // Error with action
246
+ toast.error('Failed to save', {
247
+ action: {
248
+ label: 'Retry',
249
+ onClick: () => handleRetry(),
250
+ },
251
+ });
252
+
253
+ // Progress indicator
254
+ export function ProgressSteps({ steps, currentStep }: ProgressStepsProps) {
255
+ return (
256
+ <nav aria-label="Progress">
257
+ <ol className="flex items-center">
258
+ {steps.map((step, index) => (
259
+ <li key={step.id} className="relative flex items-center">
260
+ <span
261
+ className={cn(
262
+ 'flex h-8 w-8 items-center justify-center rounded-full',
263
+ index < currentStep && 'bg-primary text-white',
264
+ index === currentStep && 'border-2 border-primary',
265
+ index > currentStep && 'border-2 border-gray-300'
266
+ )}
267
+ aria-current={index === currentStep ? 'step' : undefined}
268
+ >
269
+ {index < currentStep ? (
270
+ <CheckIcon className="h-5 w-5" aria-hidden="true" />
271
+ ) : (
272
+ <span>{index + 1}</span>
273
+ )}
274
+ </span>
275
+ <span className="ml-2 text-sm font-medium">
276
+ {step.name}
277
+ {index === currentStep && (
278
+ <span className="sr-only"> (current step)</span>
279
+ )}
280
+ </span>
281
+ </li>
282
+ ))}
283
+ </ol>
284
+ </nav>
285
+ );
286
+ }
287
+ ```
288
+
289
+ ### Responsive Design
290
+
291
+ ```tsx
292
+ // Mobile-first responsive design
293
+ export function ResponsiveLayout({ children }: { children: React.ReactNode }) {
294
+ return (
295
+ <div className="min-h-screen">
296
+ {/* Mobile navigation */}
297
+ <nav className="md:hidden fixed bottom-0 left-0 right-0 bg-white border-t">
298
+ <MobileNav />
299
+ </nav>
300
+
301
+ {/* Desktop sidebar */}
302
+ <aside className="hidden md:fixed md:inset-y-0 md:flex md:w-64 md:flex-col">
303
+ <Sidebar />
304
+ </aside>
305
+
306
+ {/* Main content */}
307
+ <main className="md:pl-64">
308
+ <div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 py-8">
309
+ {children}
310
+ </div>
311
+ </main>
312
+ </div>
313
+ );
314
+ }
315
+
316
+ // Responsive grid
317
+ <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4 md:gap-6">
318
+ {items.map(item => <Card key={item.id} {...item} />)}
319
+ </div>
320
+
321
+ // Touch-friendly targets (min 44x44px)
322
+ <button className="min-h-[44px] min-w-[44px] px-4 py-3">
323
+ Tap me
324
+ </button>
325
+ ```
326
+
327
+ ### Form UX Patterns
328
+
329
+ ```tsx
330
+ // Inline validation
331
+ export function FormField({
332
+ label,
333
+ name,
334
+ error,
335
+ hint,
336
+ required,
337
+ children,
338
+ }: FormFieldProps) {
339
+ return (
340
+ <div className="space-y-1">
341
+ <label htmlFor={name} className="block text-sm font-medium">
342
+ {label}
343
+ {required && <span className="text-error ml-1">*</span>}
344
+ </label>
345
+
346
+ {children}
347
+
348
+ {hint && !error && (
349
+ <p id={`${name}-hint`} className="text-sm text-gray-500">
350
+ {hint}
351
+ </p>
352
+ )}
353
+
354
+ {error && (
355
+ <p id={`${name}-error`} role="alert" className="text-sm text-error flex items-center gap-1">
356
+ <AlertCircle className="h-4 w-4" aria-hidden="true" />
357
+ {error}
358
+ </p>
359
+ )}
360
+ </div>
361
+ );
362
+ }
363
+
364
+ // Password strength indicator
365
+ export function PasswordStrength({ password }: { password: string }) {
366
+ const strength = calculateStrength(password);
367
+ const labels = ['Weak', 'Fair', 'Good', 'Strong'];
368
+
369
+ return (
370
+ <div className="mt-2" aria-live="polite">
371
+ <div className="flex gap-1">
372
+ {[0, 1, 2, 3].map(level => (
373
+ <div
374
+ key={level}
375
+ className={cn(
376
+ 'h-1 flex-1 rounded-full transition-colors',
377
+ level <= strength ? strengthColors[strength] : 'bg-gray-200'
378
+ )}
379
+ />
380
+ ))}
381
+ </div>
382
+ <p className="text-xs mt-1 text-gray-600">
383
+ Password strength: {labels[strength]}
384
+ </p>
385
+ </div>
386
+ );
387
+ }
388
+ ```
389
+
390
+ ### Animation & Motion
391
+
392
+ ```tsx
393
+ // Framer Motion for complex animations
394
+ import { motion, AnimatePresence } from 'framer-motion';
395
+
396
+ export function Modal({ isOpen, onClose, children }: ModalProps) {
397
+ return (
398
+ <AnimatePresence>
399
+ {isOpen && (
400
+ <>
401
+ <motion.div
402
+ initial={{ opacity: 0 }}
403
+ animate={{ opacity: 1 }}
404
+ exit={{ opacity: 0 }}
405
+ className="fixed inset-0 bg-black/50"
406
+ onClick={onClose}
407
+ />
408
+ <motion.div
409
+ initial={{ opacity: 0, scale: 0.95, y: 10 }}
410
+ animate={{ opacity: 1, scale: 1, y: 0 }}
411
+ exit={{ opacity: 0, scale: 0.95, y: 10 }}
412
+ transition={{ duration: 0.2 }}
413
+ className="fixed inset-x-4 top-[20%] max-w-lg mx-auto bg-white rounded-xl shadow-xl"
414
+ >
415
+ {children}
416
+ </motion.div>
417
+ </>
418
+ )}
419
+ </AnimatePresence>
420
+ );
421
+ }
422
+
423
+ // CSS transitions for simple animations
424
+ <button className="transition-all duration-200 hover:scale-105 active:scale-95">
425
+ Click me
426
+ </button>
427
+
428
+ // Reduce motion for accessibility
429
+ <div className="motion-safe:animate-bounce motion-reduce:animate-none">
430
+ Animated content
431
+ </div>
432
+ ```
433
+
434
+ ## UI/UX Checklist
435
+
436
+ - [ ] Consistent design tokens used
437
+ - [ ] Touch targets at least 44x44px
438
+ - [ ] Color contrast meets WCAG AA
439
+ - [ ] All interactive elements have focus states
440
+ - [ ] Forms have clear labels and error states
441
+ - [ ] Loading states provide feedback
442
+ - [ ] Responsive on all screen sizes
443
+ - [ ] Keyboard navigation works
444
+ - [ ] Screen reader tested
445
+ - [ ] Reduced motion preference respected
446
+
447
+ ## Trigger Keywords
448
+ ui, ux, design, component, accessible, a11y, keyboard, screen reader, wcag, responsive, mobile, animation, form, toast, modal, dropdown, navigation, color, typography