@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,364 @@
1
+ # Frontend Expert Agent
2
+
3
+ ## Role
4
+ Specialized in React, Next.js App Router, modern component patterns, state management, and UI development with Tailwind CSS.
5
+
6
+ ## Core Expertise
7
+
8
+ ### React Server Components (Default)
9
+
10
+ ```tsx
11
+ // Server Components are the default in Next.js App Router
12
+ // They run on the server and can directly access data
13
+
14
+ // app/users/page.tsx - Server Component
15
+ import { prisma } from '@/lib/prisma';
16
+
17
+ export default async function UsersPage() {
18
+ // Direct database access - no API needed!
19
+ const users = await prisma.user.findMany({
20
+ take: 10,
21
+ orderBy: { createdAt: 'desc' }
22
+ });
23
+
24
+ return (
25
+ <main className="container mx-auto py-8">
26
+ <h1 className="text-2xl font-bold mb-4">Users</h1>
27
+ <ul className="space-y-2">
28
+ {users.map(user => (
29
+ <UserCard key={user.id} user={user} />
30
+ ))}
31
+ </ul>
32
+ </main>
33
+ );
34
+ }
35
+
36
+ // This is also a Server Component
37
+ function UserCard({ user }: { user: User }) {
38
+ return (
39
+ <div className="p-4 border rounded-lg">
40
+ <h2 className="font-medium">{user.name}</h2>
41
+ <p className="text-gray-500">{user.email}</p>
42
+ </div>
43
+ );
44
+ }
45
+ ```
46
+
47
+ ### Client Components (When Needed)
48
+
49
+ ```tsx
50
+ 'use client';
51
+
52
+ // Use 'use client' ONLY when you need:
53
+ // - useState, useEffect, useRef
54
+ // - Event handlers (onClick, onChange, etc.)
55
+ // - Browser APIs (window, document)
56
+ // - Third-party libraries that use hooks
57
+
58
+ import { useState, useTransition } from 'react';
59
+ import { updateUser } from '@/app/actions';
60
+
61
+ export function EditUserForm({ user }: { user: User }) {
62
+ const [name, setName] = useState(user.name);
63
+ const [isPending, startTransition] = useTransition();
64
+
65
+ const handleSubmit = (e: React.FormEvent) => {
66
+ e.preventDefault();
67
+ startTransition(async () => {
68
+ await updateUser(user.id, { name });
69
+ });
70
+ };
71
+
72
+ return (
73
+ <form onSubmit={handleSubmit} className="space-y-4">
74
+ <input
75
+ type="text"
76
+ value={name}
77
+ onChange={(e) => setName(e.target.value)}
78
+ className="border rounded px-3 py-2 w-full"
79
+ disabled={isPending}
80
+ />
81
+ <button
82
+ type="submit"
83
+ disabled={isPending}
84
+ className="bg-blue-500 text-white px-4 py-2 rounded disabled:opacity-50"
85
+ >
86
+ {isPending ? 'Saving...' : 'Save'}
87
+ </button>
88
+ </form>
89
+ );
90
+ }
91
+ ```
92
+
93
+ ### Component Composition Pattern
94
+
95
+ ```tsx
96
+ // Compose Server and Client Components effectively
97
+
98
+ // ServerWrapper.tsx (Server Component)
99
+ import { prisma } from '@/lib/prisma';
100
+ import { InteractiveList } from './InteractiveList';
101
+
102
+ export async function ServerWrapper() {
103
+ const items = await prisma.item.findMany();
104
+
105
+ // Pass data to client component
106
+ return <InteractiveList initialItems={items} />;
107
+ }
108
+
109
+ // InteractiveList.tsx (Client Component)
110
+ 'use client';
111
+
112
+ import { useState } from 'react';
113
+
114
+ export function InteractiveList({ initialItems }: { initialItems: Item[] }) {
115
+ const [items, setItems] = useState(initialItems);
116
+ const [filter, setFilter] = useState('');
117
+
118
+ const filteredItems = items.filter(item =>
119
+ item.name.toLowerCase().includes(filter.toLowerCase())
120
+ );
121
+
122
+ return (
123
+ <div>
124
+ <input
125
+ type="search"
126
+ placeholder="Filter..."
127
+ value={filter}
128
+ onChange={(e) => setFilter(e.target.value)}
129
+ className="border rounded px-3 py-2 mb-4 w-full"
130
+ />
131
+ <ul className="space-y-2">
132
+ {filteredItems.map(item => (
133
+ <li key={item.id} className="p-2 border rounded">
134
+ {item.name}
135
+ </li>
136
+ ))}
137
+ </ul>
138
+ </div>
139
+ );
140
+ }
141
+ ```
142
+
143
+ ### State Management
144
+
145
+ #### Local State (useState)
146
+ ```tsx
147
+ // For component-specific state
148
+ const [isOpen, setIsOpen] = useState(false);
149
+ ```
150
+
151
+ #### URL State (useSearchParams)
152
+ ```tsx
153
+ 'use client';
154
+
155
+ import { useSearchParams, useRouter, usePathname } from 'next/navigation';
156
+
157
+ export function Filters() {
158
+ const searchParams = useSearchParams();
159
+ const router = useRouter();
160
+ const pathname = usePathname();
161
+
162
+ const updateFilter = (key: string, value: string) => {
163
+ const params = new URLSearchParams(searchParams);
164
+ if (value) {
165
+ params.set(key, value);
166
+ } else {
167
+ params.delete(key);
168
+ }
169
+ router.push(`${pathname}?${params.toString()}`);
170
+ };
171
+
172
+ return (
173
+ <select
174
+ value={searchParams.get('status') ?? ''}
175
+ onChange={(e) => updateFilter('status', e.target.value)}
176
+ >
177
+ <option value="">All</option>
178
+ <option value="active">Active</option>
179
+ <option value="archived">Archived</option>
180
+ </select>
181
+ );
182
+ }
183
+ ```
184
+
185
+ #### Global State (Zustand)
186
+ ```tsx
187
+ // store/user-store.ts
188
+ import { create } from 'zustand';
189
+ import { persist } from 'zustand/middleware';
190
+
191
+ interface UserStore {
192
+ user: User | null;
193
+ setUser: (user: User | null) => void;
194
+ preferences: { theme: 'light' | 'dark' };
195
+ setTheme: (theme: 'light' | 'dark') => void;
196
+ }
197
+
198
+ export const useUserStore = create<UserStore>()(
199
+ persist(
200
+ (set) => ({
201
+ user: null,
202
+ setUser: (user) => set({ user }),
203
+ preferences: { theme: 'light' },
204
+ setTheme: (theme) => set((state) => ({
205
+ preferences: { ...state.preferences, theme }
206
+ })),
207
+ }),
208
+ { name: 'user-storage' }
209
+ )
210
+ );
211
+
212
+ // Usage in component
213
+ 'use client';
214
+
215
+ export function ThemeToggle() {
216
+ const { preferences, setTheme } = useUserStore();
217
+
218
+ return (
219
+ <button onClick={() => setTheme(preferences.theme === 'light' ? 'dark' : 'light')}>
220
+ Toggle Theme
221
+ </button>
222
+ );
223
+ }
224
+ ```
225
+
226
+ ### Tailwind CSS Patterns
227
+
228
+ #### Responsive Design
229
+ ```tsx
230
+ <div className="
231
+ grid
232
+ grid-cols-1 /* Mobile: 1 column */
233
+ sm:grid-cols-2 /* Small: 2 columns */
234
+ lg:grid-cols-3 /* Large: 3 columns */
235
+ xl:grid-cols-4 /* XL: 4 columns */
236
+ gap-4
237
+ ">
238
+ {items.map(item => <Card key={item.id} {...item} />)}
239
+ </div>
240
+ ```
241
+
242
+ #### Component Variants with cva
243
+ ```tsx
244
+ import { cva, type VariantProps } from 'class-variance-authority';
245
+ import { cn } from '@/lib/utils';
246
+
247
+ const buttonVariants = cva(
248
+ 'inline-flex items-center justify-center rounded-md font-medium transition-colors focus-visible:outline-none focus-visible:ring-2',
249
+ {
250
+ variants: {
251
+ variant: {
252
+ default: 'bg-primary text-primary-foreground hover:bg-primary/90',
253
+ destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90',
254
+ outline: 'border border-input bg-background hover:bg-accent',
255
+ ghost: 'hover:bg-accent hover:text-accent-foreground',
256
+ },
257
+ size: {
258
+ default: 'h-10 px-4 py-2',
259
+ sm: 'h-9 rounded-md px-3',
260
+ lg: 'h-11 rounded-md px-8',
261
+ icon: 'h-10 w-10',
262
+ },
263
+ },
264
+ defaultVariants: {
265
+ variant: 'default',
266
+ size: 'default',
267
+ },
268
+ }
269
+ );
270
+
271
+ interface ButtonProps
272
+ extends React.ButtonHTMLAttributes<HTMLButtonElement>,
273
+ VariantProps<typeof buttonVariants> {}
274
+
275
+ export function Button({ className, variant, size, ...props }: ButtonProps) {
276
+ return (
277
+ <button
278
+ className={cn(buttonVariants({ variant, size, className }))}
279
+ {...props}
280
+ />
281
+ );
282
+ }
283
+ ```
284
+
285
+ ### Loading & Error States
286
+
287
+ ```tsx
288
+ // loading.tsx - Automatic loading UI
289
+ export default function Loading() {
290
+ return (
291
+ <div className="flex items-center justify-center min-h-[400px]">
292
+ <div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-500" />
293
+ </div>
294
+ );
295
+ }
296
+
297
+ // error.tsx - Automatic error UI
298
+ 'use client';
299
+
300
+ export default function Error({
301
+ error,
302
+ reset,
303
+ }: {
304
+ error: Error & { digest?: string };
305
+ reset: () => void;
306
+ }) {
307
+ return (
308
+ <div className="flex flex-col items-center justify-center min-h-[400px] gap-4">
309
+ <h2 className="text-xl font-semibold">Something went wrong!</h2>
310
+ <p className="text-gray-500">{error.message}</p>
311
+ <button
312
+ onClick={reset}
313
+ className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
314
+ >
315
+ Try again
316
+ </button>
317
+ </div>
318
+ );
319
+ }
320
+ ```
321
+
322
+ ### Suspense Boundaries
323
+
324
+ ```tsx
325
+ import { Suspense } from 'react';
326
+
327
+ export default function DashboardPage() {
328
+ return (
329
+ <div className="grid grid-cols-2 gap-4">
330
+ <Suspense fallback={<CardSkeleton />}>
331
+ <RevenueCard />
332
+ </Suspense>
333
+ <Suspense fallback={<CardSkeleton />}>
334
+ <UsersCard />
335
+ </Suspense>
336
+ <Suspense fallback={<TableSkeleton />}>
337
+ <RecentOrders />
338
+ </Suspense>
339
+ </div>
340
+ );
341
+ }
342
+
343
+ function CardSkeleton() {
344
+ return (
345
+ <div className="h-32 bg-gray-100 rounded-lg animate-pulse" />
346
+ );
347
+ }
348
+ ```
349
+
350
+ ## Component Checklist
351
+
352
+ - [ ] Server Component by default
353
+ - [ ] 'use client' only when necessary
354
+ - [ ] Props typed with TypeScript
355
+ - [ ] Loading state handled
356
+ - [ ] Error boundary in place
357
+ - [ ] Accessible (keyboard nav, ARIA labels)
358
+ - [ ] Responsive (mobile-first)
359
+ - [ ] No inline styles (use Tailwind)
360
+ - [ ] Component is focused and small
361
+ - [ ] Reusable where appropriate
362
+
363
+ ## Trigger Keywords
364
+ component, react, ui, tailwind, state, client, server component, hook, useState, useEffect, form, button, input, modal, dropdown, responsive, mobile, layout, grid, flex, styling, css
@@ -0,0 +1,140 @@
1
+ /**
2
+ * Bootspring Agents Module
3
+ * Programmatic access to agent data and invocation
4
+ */
5
+
6
+ const fs = require('fs');
7
+ const path = require('path');
8
+
9
+ // Agent registry from cli/agent.js
10
+ const AGENTS = {
11
+ 'database-expert': {
12
+ name: 'Database Expert',
13
+ expertise: ['SQL', 'Prisma', 'PostgreSQL', 'migrations', 'query optimization'],
14
+ description: 'Expert in database design, queries, and optimization'
15
+ },
16
+ 'security-expert': {
17
+ name: 'Security Expert',
18
+ expertise: ['OWASP', 'authentication', 'authorization', 'encryption', 'vulnerabilities'],
19
+ description: 'Expert in application security and best practices'
20
+ },
21
+ 'frontend-expert': {
22
+ name: 'Frontend Expert',
23
+ expertise: ['React', 'Next.js', 'Tailwind', 'components', 'state management'],
24
+ description: 'Expert in frontend development and UI'
25
+ },
26
+ 'backend-expert': {
27
+ name: 'Backend Expert',
28
+ expertise: ['Node.js', 'APIs', 'server actions', 'middleware', 'serverless'],
29
+ description: 'Expert in backend development and server-side logic'
30
+ },
31
+ 'api-expert': {
32
+ name: 'API Expert',
33
+ expertise: ['REST', 'GraphQL', 'tRPC', 'webhooks', 'API design'],
34
+ description: 'Expert in API design and integration'
35
+ },
36
+ 'testing-expert': {
37
+ name: 'Testing Expert',
38
+ expertise: ['Vitest', 'Jest', 'Playwright', 'coverage', 'TDD'],
39
+ description: 'Expert in testing strategies and implementation'
40
+ },
41
+ 'performance-expert': {
42
+ name: 'Performance Expert',
43
+ expertise: ['optimization', 'caching', 'profiling', 'Core Web Vitals', 'lazy loading'],
44
+ description: 'Expert in application performance optimization'
45
+ },
46
+ 'devops-expert': {
47
+ name: 'DevOps Expert',
48
+ expertise: ['CI/CD', 'Docker', 'deployment', 'monitoring', 'infrastructure'],
49
+ description: 'Expert in DevOps practices and deployment'
50
+ },
51
+ 'ui-ux-expert': {
52
+ name: 'UI/UX Expert',
53
+ expertise: ['design systems', 'accessibility', 'user experience', 'responsive design'],
54
+ description: 'Expert in UI/UX design and implementation'
55
+ },
56
+ 'architecture-expert': {
57
+ name: 'Architecture Expert',
58
+ expertise: ['system design', 'patterns', 'scalability', 'microservices'],
59
+ description: 'Expert in software architecture and system design'
60
+ },
61
+ 'code-review-expert': {
62
+ name: 'Code Review Expert',
63
+ expertise: ['code quality', 'best practices', 'refactoring', 'maintainability'],
64
+ description: 'Expert in code review and quality assessment'
65
+ },
66
+ 'vercel-expert': {
67
+ name: 'Vercel Expert',
68
+ expertise: ['Vercel', 'serverless', 'edge functions', 'deployment', 'analytics'],
69
+ description: 'Expert in Vercel platform and deployment'
70
+ }
71
+ };
72
+
73
+ /**
74
+ * Get all available agents
75
+ */
76
+ function getAgents() {
77
+ return AGENTS;
78
+ }
79
+
80
+ /**
81
+ * Get a specific agent by name
82
+ */
83
+ function getAgent(name) {
84
+ return AGENTS[name] || null;
85
+ }
86
+
87
+ /**
88
+ * List agent names
89
+ */
90
+ function listAgents() {
91
+ return Object.keys(AGENTS);
92
+ }
93
+
94
+ /**
95
+ * Get agent context file path
96
+ */
97
+ function getAgentContextPath(name) {
98
+ const agentsDir = path.dirname(__dirname);
99
+ return path.join(agentsDir, 'agents', name, 'context.md');
100
+ }
101
+
102
+ /**
103
+ * Load agent context file
104
+ */
105
+ function loadAgentContext(name) {
106
+ const contextPath = getAgentContextPath(name);
107
+ if (fs.existsSync(contextPath)) {
108
+ return fs.readFileSync(contextPath, 'utf-8');
109
+ }
110
+ return null;
111
+ }
112
+
113
+ /**
114
+ * Find agents by expertise keyword
115
+ */
116
+ function findAgentsByExpertise(keyword) {
117
+ const matches = [];
118
+ const lowerKeyword = keyword.toLowerCase();
119
+
120
+ for (const [id, agent] of Object.entries(AGENTS)) {
121
+ const hasMatch = agent.expertise.some(exp =>
122
+ exp.toLowerCase().includes(lowerKeyword)
123
+ );
124
+ if (hasMatch) {
125
+ matches.push({ id, ...agent });
126
+ }
127
+ }
128
+
129
+ return matches;
130
+ }
131
+
132
+ module.exports = {
133
+ AGENTS,
134
+ getAgents,
135
+ getAgent,
136
+ listAgents,
137
+ getAgentContextPath,
138
+ loadAgentContext,
139
+ findAgentsByExpertise
140
+ };