@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,268 @@
1
+ # Input Validation & Security Patterns
2
+
3
+ Battle-tested patterns for secure input handling.
4
+
5
+ ## Zod Schema Validation
6
+
7
+ ```typescript
8
+ // lib/validations/user.ts
9
+ import { z } from 'zod'
10
+
11
+ export const CreateUserSchema = z.object({
12
+ email: z.string().email('Invalid email address'),
13
+ name: z.string()
14
+ .min(1, 'Name is required')
15
+ .max(100, 'Name too long')
16
+ .regex(/^[a-zA-Z\s'-]+$/, 'Invalid characters in name'),
17
+ password: z.string()
18
+ .min(8, 'Password must be at least 8 characters')
19
+ .regex(/[A-Z]/, 'Password must contain uppercase letter')
20
+ .regex(/[a-z]/, 'Password must contain lowercase letter')
21
+ .regex(/[0-9]/, 'Password must contain number')
22
+ .regex(/[^A-Za-z0-9]/, 'Password must contain special character'),
23
+ age: z.number()
24
+ .int('Age must be a whole number')
25
+ .min(13, 'Must be at least 13 years old')
26
+ .max(120, 'Invalid age')
27
+ .optional()
28
+ })
29
+
30
+ export const UpdateProfileSchema = z.object({
31
+ name: z.string().min(1).max(100).optional(),
32
+ bio: z.string().max(500).optional(),
33
+ website: z.string().url().optional().or(z.literal(''))
34
+ })
35
+
36
+ export type CreateUserInput = z.infer<typeof CreateUserSchema>
37
+ export type UpdateProfileInput = z.infer<typeof UpdateProfileSchema>
38
+ ```
39
+
40
+ ## Server Action with Validation
41
+
42
+ ```typescript
43
+ // actions/user.ts
44
+ 'use server'
45
+
46
+ import { CreateUserSchema } from '@/lib/validations/user'
47
+ import { prisma } from '@/lib/db'
48
+
49
+ type ActionResult =
50
+ | { success: true; data: { id: string } }
51
+ | { success: false; error: string; fieldErrors?: Record<string, string[]> }
52
+
53
+ export async function createUser(formData: FormData): Promise<ActionResult> {
54
+ // Parse and validate
55
+ const raw = {
56
+ email: formData.get('email'),
57
+ name: formData.get('name'),
58
+ password: formData.get('password'),
59
+ age: formData.get('age') ? Number(formData.get('age')) : undefined
60
+ }
61
+
62
+ const result = CreateUserSchema.safeParse(raw)
63
+
64
+ if (!result.success) {
65
+ return {
66
+ success: false,
67
+ error: 'Validation failed',
68
+ fieldErrors: result.error.flatten().fieldErrors
69
+ }
70
+ }
71
+
72
+ // Hash password before storing
73
+ const hashedPassword = await hashPassword(result.data.password)
74
+
75
+ const user = await prisma.user.create({
76
+ data: {
77
+ ...result.data,
78
+ password: hashedPassword
79
+ }
80
+ })
81
+
82
+ return { success: true, data: { id: user.id } }
83
+ }
84
+ ```
85
+
86
+ ## Sanitizing User Input
87
+
88
+ ```typescript
89
+ // lib/sanitize.ts
90
+ import DOMPurify from 'isomorphic-dompurify'
91
+
92
+ // Sanitize HTML content (for rich text)
93
+ export function sanitizeHtml(dirty: string): string {
94
+ return DOMPurify.sanitize(dirty, {
95
+ ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a', 'p', 'br', 'ul', 'ol', 'li'],
96
+ ALLOWED_ATTR: ['href', 'target', 'rel']
97
+ })
98
+ }
99
+
100
+ // Strip all HTML (for plain text fields)
101
+ export function stripHtml(input: string): string {
102
+ return DOMPurify.sanitize(input, { ALLOWED_TAGS: [] })
103
+ }
104
+
105
+ // Sanitize for SQL-safe strings (use with parameterized queries)
106
+ export function escapeString(input: string): string {
107
+ return input
108
+ .replace(/\\/g, '\\\\')
109
+ .replace(/'/g, "\\'")
110
+ .replace(/"/g, '\\"')
111
+ }
112
+
113
+ // URL validation and sanitization
114
+ export function sanitizeUrl(url: string): string | null {
115
+ try {
116
+ const parsed = new URL(url)
117
+ // Only allow http/https
118
+ if (!['http:', 'https:'].includes(parsed.protocol)) {
119
+ return null
120
+ }
121
+ return parsed.toString()
122
+ } catch {
123
+ return null
124
+ }
125
+ }
126
+ ```
127
+
128
+ ## Rate Limiting
129
+
130
+ ```typescript
131
+ // lib/rate-limit.ts
132
+ import { Ratelimit } from '@upstash/ratelimit'
133
+ import { Redis } from '@upstash/redis'
134
+
135
+ // Using Upstash Redis for serverless
136
+ const redis = new Redis({
137
+ url: process.env.UPSTASH_REDIS_REST_URL!,
138
+ token: process.env.UPSTASH_REDIS_REST_TOKEN!
139
+ })
140
+
141
+ // Different rate limiters for different purposes
142
+ export const authLimiter = new Ratelimit({
143
+ redis,
144
+ limiter: Ratelimit.slidingWindow(5, '1 m'), // 5 attempts per minute
145
+ prefix: 'ratelimit:auth'
146
+ })
147
+
148
+ export const apiLimiter = new Ratelimit({
149
+ redis,
150
+ limiter: Ratelimit.slidingWindow(100, '1 m'), // 100 requests per minute
151
+ prefix: 'ratelimit:api'
152
+ })
153
+
154
+ // Usage in API route
155
+ export async function POST(request: Request) {
156
+ const ip = request.headers.get('x-forwarded-for') ?? 'anonymous'
157
+
158
+ const { success, limit, remaining, reset } = await authLimiter.limit(ip)
159
+
160
+ if (!success) {
161
+ return new Response('Too many requests', {
162
+ status: 429,
163
+ headers: {
164
+ 'X-RateLimit-Limit': limit.toString(),
165
+ 'X-RateLimit-Remaining': remaining.toString(),
166
+ 'X-RateLimit-Reset': reset.toString()
167
+ }
168
+ })
169
+ }
170
+
171
+ // Continue with handler...
172
+ }
173
+ ```
174
+
175
+ ## CSRF Protection
176
+
177
+ ```typescript
178
+ // lib/csrf.ts
179
+ import { cookies } from 'next/headers'
180
+ import { randomBytes, createHmac } from 'crypto'
181
+
182
+ const SECRET = process.env.CSRF_SECRET!
183
+
184
+ export async function generateCsrfToken(): Promise<string> {
185
+ const token = randomBytes(32).toString('hex')
186
+ const signature = createHmac('sha256', SECRET).update(token).digest('hex')
187
+ const fullToken = `${token}.${signature}`
188
+
189
+ const cookieStore = await cookies()
190
+ cookieStore.set('csrf-token', fullToken, {
191
+ httpOnly: true,
192
+ secure: process.env.NODE_ENV === 'production',
193
+ sameSite: 'strict',
194
+ maxAge: 60 * 60 // 1 hour
195
+ })
196
+
197
+ return token // Only return the token part to the client
198
+ }
199
+
200
+ export async function validateCsrfToken(clientToken: string): Promise<boolean> {
201
+ const cookieStore = await cookies()
202
+ const cookieToken = cookieStore.get('csrf-token')?.value
203
+
204
+ if (!cookieToken) return false
205
+
206
+ const [storedToken, storedSignature] = cookieToken.split('.')
207
+ const expectedSignature = createHmac('sha256', SECRET)
208
+ .update(storedToken)
209
+ .digest('hex')
210
+
211
+ return (
212
+ storedToken === clientToken &&
213
+ storedSignature === expectedSignature
214
+ )
215
+ }
216
+ ```
217
+
218
+ ## Security Headers Middleware
219
+
220
+ ```typescript
221
+ // middleware.ts
222
+ import { NextResponse } from 'next/server'
223
+ import type { NextRequest } from 'next/server'
224
+
225
+ export function middleware(request: NextRequest) {
226
+ const response = NextResponse.next()
227
+
228
+ // Security headers
229
+ response.headers.set('X-Frame-Options', 'DENY')
230
+ response.headers.set('X-Content-Type-Options', 'nosniff')
231
+ response.headers.set('Referrer-Policy', 'strict-origin-when-cross-origin')
232
+ response.headers.set('X-XSS-Protection', '1; mode=block')
233
+
234
+ // Content Security Policy
235
+ response.headers.set(
236
+ 'Content-Security-Policy',
237
+ [
238
+ "default-src 'self'",
239
+ "script-src 'self' 'unsafe-inline' 'unsafe-eval'",
240
+ "style-src 'self' 'unsafe-inline'",
241
+ "img-src 'self' data: https:",
242
+ "font-src 'self'",
243
+ "connect-src 'self' https://api.stripe.com",
244
+ "frame-ancestors 'none'"
245
+ ].join('; ')
246
+ )
247
+
248
+ return response
249
+ }
250
+ ```
251
+
252
+ ## Environment Variable Validation
253
+
254
+ ```typescript
255
+ // lib/env.ts
256
+ import { z } from 'zod'
257
+
258
+ const envSchema = z.object({
259
+ DATABASE_URL: z.string().url(),
260
+ STRIPE_SECRET_KEY: z.string().startsWith('sk_'),
261
+ STRIPE_WEBHOOK_SECRET: z.string().startsWith('whsec_'),
262
+ NEXT_PUBLIC_URL: z.string().url(),
263
+ NODE_ENV: z.enum(['development', 'production', 'test'])
264
+ })
265
+
266
+ // Validate at build/startup time
267
+ export const env = envSchema.parse(process.env)
268
+ ```
@@ -0,0 +1,307 @@
1
+ # Vitest Testing Patterns
2
+
3
+ Battle-tested patterns for testing with Vitest in Next.js.
4
+
5
+ ## Setup Configuration
6
+
7
+ ```typescript
8
+ // vitest.config.ts
9
+ import { defineConfig } from 'vitest/config'
10
+ import react from '@vitejs/plugin-react'
11
+ import path from 'path'
12
+
13
+ export default defineConfig({
14
+ plugins: [react()],
15
+ test: {
16
+ environment: 'jsdom',
17
+ globals: true,
18
+ setupFiles: ['./vitest.setup.ts'],
19
+ include: ['**/*.test.{ts,tsx}'],
20
+ coverage: {
21
+ provider: 'v8',
22
+ reporter: ['text', 'json', 'html'],
23
+ exclude: ['node_modules/', '*.config.*']
24
+ }
25
+ },
26
+ resolve: {
27
+ alias: {
28
+ '@': path.resolve(__dirname, './src')
29
+ }
30
+ }
31
+ })
32
+ ```
33
+
34
+ ```typescript
35
+ // vitest.setup.ts
36
+ import '@testing-library/jest-dom/vitest'
37
+ import { cleanup } from '@testing-library/react'
38
+ import { afterEach, vi } from 'vitest'
39
+
40
+ afterEach(() => {
41
+ cleanup()
42
+ })
43
+
44
+ // Mock next/navigation
45
+ vi.mock('next/navigation', () => ({
46
+ useRouter: () => ({
47
+ push: vi.fn(),
48
+ replace: vi.fn(),
49
+ back: vi.fn()
50
+ }),
51
+ usePathname: () => '/',
52
+ useSearchParams: () => new URLSearchParams()
53
+ }))
54
+ ```
55
+
56
+ ## Unit Testing Functions
57
+
58
+ ```typescript
59
+ // lib/utils.test.ts
60
+ import { describe, it, expect } from 'vitest'
61
+ import { formatPrice, calculateDiscount, validateEmail } from './utils'
62
+
63
+ describe('formatPrice', () => {
64
+ it('formats cents to dollars', () => {
65
+ expect(formatPrice(1000)).toBe('$10.00')
66
+ expect(formatPrice(1599)).toBe('$15.99')
67
+ })
68
+
69
+ it('handles zero', () => {
70
+ expect(formatPrice(0)).toBe('$0.00')
71
+ })
72
+ })
73
+
74
+ describe('calculateDiscount', () => {
75
+ it('applies percentage discount', () => {
76
+ expect(calculateDiscount(100, 10)).toBe(90)
77
+ expect(calculateDiscount(50, 50)).toBe(25)
78
+ })
79
+
80
+ it('returns original price for zero discount', () => {
81
+ expect(calculateDiscount(100, 0)).toBe(100)
82
+ })
83
+ })
84
+
85
+ describe('validateEmail', () => {
86
+ it('accepts valid emails', () => {
87
+ expect(validateEmail('user@example.com')).toBe(true)
88
+ expect(validateEmail('user+tag@domain.co.uk')).toBe(true)
89
+ })
90
+
91
+ it('rejects invalid emails', () => {
92
+ expect(validateEmail('invalid')).toBe(false)
93
+ expect(validateEmail('no@domain')).toBe(false)
94
+ expect(validateEmail('')).toBe(false)
95
+ })
96
+ })
97
+ ```
98
+
99
+ ## Testing React Components
100
+
101
+ ```typescript
102
+ // components/button.test.tsx
103
+ import { describe, it, expect, vi } from 'vitest'
104
+ import { render, screen, fireEvent } from '@testing-library/react'
105
+ import { Button } from './button'
106
+
107
+ describe('Button', () => {
108
+ it('renders with children', () => {
109
+ render(<Button>Click me</Button>)
110
+ expect(screen.getByText('Click me')).toBeInTheDocument()
111
+ })
112
+
113
+ it('calls onClick when clicked', () => {
114
+ const handleClick = vi.fn()
115
+ render(<Button onClick={handleClick}>Click</Button>)
116
+
117
+ fireEvent.click(screen.getByText('Click'))
118
+ expect(handleClick).toHaveBeenCalledTimes(1)
119
+ })
120
+
121
+ it('is disabled when loading', () => {
122
+ render(<Button loading>Submit</Button>)
123
+ expect(screen.getByRole('button')).toBeDisabled()
124
+ })
125
+
126
+ it('applies variant classes', () => {
127
+ render(<Button variant="destructive">Delete</Button>)
128
+ expect(screen.getByRole('button')).toHaveClass('bg-red-500')
129
+ })
130
+ })
131
+ ```
132
+
133
+ ## Testing Async Components
134
+
135
+ ```typescript
136
+ // components/user-profile.test.tsx
137
+ import { describe, it, expect, vi } from 'vitest'
138
+ import { render, screen, waitFor } from '@testing-library/react'
139
+ import { UserProfile } from './user-profile'
140
+
141
+ // Mock the fetch function
142
+ vi.mock('@/lib/api', () => ({
143
+ fetchUser: vi.fn()
144
+ }))
145
+
146
+ import { fetchUser } from '@/lib/api'
147
+
148
+ describe('UserProfile', () => {
149
+ it('shows loading state initially', () => {
150
+ vi.mocked(fetchUser).mockImplementation(() => new Promise(() => {}))
151
+
152
+ render(<UserProfile userId="123" />)
153
+ expect(screen.getByText('Loading...')).toBeInTheDocument()
154
+ })
155
+
156
+ it('displays user data after loading', async () => {
157
+ vi.mocked(fetchUser).mockResolvedValue({
158
+ name: 'John Doe',
159
+ email: 'john@example.com'
160
+ })
161
+
162
+ render(<UserProfile userId="123" />)
163
+
164
+ await waitFor(() => {
165
+ expect(screen.getByText('John Doe')).toBeInTheDocument()
166
+ expect(screen.getByText('john@example.com')).toBeInTheDocument()
167
+ })
168
+ })
169
+
170
+ it('shows error state on failure', async () => {
171
+ vi.mocked(fetchUser).mockRejectedValue(new Error('Failed'))
172
+
173
+ render(<UserProfile userId="123" />)
174
+
175
+ await waitFor(() => {
176
+ expect(screen.getByText('Error loading profile')).toBeInTheDocument()
177
+ })
178
+ })
179
+ })
180
+ ```
181
+
182
+ ## Testing Server Actions
183
+
184
+ ```typescript
185
+ // actions/posts.test.ts
186
+ import { describe, it, expect, vi, beforeEach } from 'vitest'
187
+ import { createPost, deletePost } from './posts'
188
+ import { prisma } from '@/lib/db'
189
+ import { auth } from '@/lib/auth'
190
+
191
+ vi.mock('@/lib/db', () => ({
192
+ prisma: {
193
+ post: {
194
+ create: vi.fn(),
195
+ delete: vi.fn(),
196
+ findUnique: vi.fn()
197
+ }
198
+ }
199
+ }))
200
+
201
+ vi.mock('@/lib/auth', () => ({
202
+ auth: vi.fn()
203
+ }))
204
+
205
+ vi.mock('next/cache', () => ({
206
+ revalidatePath: vi.fn()
207
+ }))
208
+
209
+ describe('createPost', () => {
210
+ beforeEach(() => {
211
+ vi.clearAllMocks()
212
+ })
213
+
214
+ it('creates post for authenticated user', async () => {
215
+ vi.mocked(auth).mockResolvedValue({ userId: 'user-123' })
216
+ vi.mocked(prisma.post.create).mockResolvedValue({
217
+ id: 'post-1',
218
+ title: 'Test Post',
219
+ authorId: 'user-123'
220
+ })
221
+
222
+ const formData = new FormData()
223
+ formData.set('title', 'Test Post')
224
+
225
+ const result = await createPost(formData)
226
+
227
+ expect(result.success).toBe(true)
228
+ expect(prisma.post.create).toHaveBeenCalledWith({
229
+ data: expect.objectContaining({
230
+ title: 'Test Post',
231
+ authorId: 'user-123'
232
+ })
233
+ })
234
+ })
235
+
236
+ it('returns error for unauthenticated user', async () => {
237
+ vi.mocked(auth).mockResolvedValue({ userId: null })
238
+
239
+ const formData = new FormData()
240
+ formData.set('title', 'Test')
241
+
242
+ const result = await createPost(formData)
243
+
244
+ expect(result.success).toBe(false)
245
+ expect(result.error).toBe('Not authenticated')
246
+ })
247
+ })
248
+ ```
249
+
250
+ ## Testing Hooks
251
+
252
+ ```typescript
253
+ // hooks/use-debounce.test.ts
254
+ import { describe, it, expect, vi } from 'vitest'
255
+ import { renderHook, act } from '@testing-library/react'
256
+ import { useDebounce } from './use-debounce'
257
+
258
+ describe('useDebounce', () => {
259
+ beforeEach(() => {
260
+ vi.useFakeTimers()
261
+ })
262
+
263
+ afterEach(() => {
264
+ vi.useRealTimers()
265
+ })
266
+
267
+ it('returns initial value immediately', () => {
268
+ const { result } = renderHook(() => useDebounce('hello', 500))
269
+ expect(result.current).toBe('hello')
270
+ })
271
+
272
+ it('debounces value changes', () => {
273
+ const { result, rerender } = renderHook(
274
+ ({ value }) => useDebounce(value, 500),
275
+ { initialProps: { value: 'hello' } }
276
+ )
277
+
278
+ rerender({ value: 'world' })
279
+ expect(result.current).toBe('hello') // Still old value
280
+
281
+ act(() => {
282
+ vi.advanceTimersByTime(500)
283
+ })
284
+
285
+ expect(result.current).toBe('world') // Now updated
286
+ })
287
+ })
288
+ ```
289
+
290
+ ## Running Tests
291
+
292
+ ```bash
293
+ # Run all tests
294
+ npm test
295
+
296
+ # Watch mode
297
+ npm test -- --watch
298
+
299
+ # Coverage report
300
+ npm test -- --coverage
301
+
302
+ # Run specific file
303
+ npm test -- button.test.tsx
304
+
305
+ # Run tests matching pattern
306
+ npm test -- --grep "createPost"
307
+ ```
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Bootspring Configuration
3
+ * Copy this file to your project root and customize
4
+ */
5
+ module.exports = {
6
+ // Project information
7
+ project: {
8
+ name: 'My Project',
9
+ description: 'A modern web application',
10
+ version: '0.1.0'
11
+ },
12
+
13
+ // Technology stack
14
+ stack: {
15
+ framework: 'nextjs', // nextjs, remix, astro
16
+ language: 'typescript', // typescript, javascript
17
+ database: 'postgresql', // postgresql, mysql, mongodb
18
+ hosting: 'vercel' // vercel, aws, railway
19
+ },
20
+
21
+ // Enabled plugins
22
+ plugins: {
23
+ auth: {
24
+ enabled: true,
25
+ provider: 'clerk', // clerk, nextauth, auth0, supabase
26
+ features: ['social_login', 'email_password']
27
+ },
28
+ payments: {
29
+ enabled: false,
30
+ provider: 'stripe', // stripe, paddle, lemonsqueezy
31
+ features: ['checkout', 'subscriptions']
32
+ },
33
+ database: {
34
+ enabled: true,
35
+ provider: 'prisma', // prisma, drizzle, typeorm
36
+ features: ['migrations', 'transactions']
37
+ },
38
+ testing: {
39
+ enabled: true,
40
+ provider: 'vitest', // vitest, jest, playwright
41
+ features: ['unit', 'integration']
42
+ },
43
+ security: {
44
+ enabled: true,
45
+ features: ['input_validation', 'rate_limiting']
46
+ },
47
+ ai: {
48
+ enabled: false,
49
+ provider: 'anthropic', // anthropic, openai, google
50
+ features: ['streaming', 'tool_use']
51
+ }
52
+ },
53
+
54
+ // Dashboard settings
55
+ dashboard: {
56
+ port: 3456,
57
+ autoOpen: false
58
+ },
59
+
60
+ // Context generation options
61
+ context: {
62
+ includeEnvVars: true,
63
+ includeTechStack: true,
64
+ includePlugins: true,
65
+ customSections: []
66
+ },
67
+
68
+ // Quality gate settings
69
+ quality: {
70
+ preCommit: {
71
+ enabled: true,
72
+ checks: ['lint', 'typecheck']
73
+ },
74
+ prePush: {
75
+ enabled: true,
76
+ checks: ['test', 'build']
77
+ },
78
+ preDeploy: {
79
+ enabled: true,
80
+ checks: ['security', 'coverage']
81
+ }
82
+ }
83
+ };
@@ -0,0 +1,9 @@
1
+ {
2
+ "mcpServers": {
3
+ "bootspring": {
4
+ "command": "npx",
5
+ "args": ["bootspring", "mcp"],
6
+ "env": {}
7
+ }
8
+ }
9
+ }