@girardmedia/bootspring 1.2.0 → 2.0.3
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/README.md +107 -14
- package/bin/bootspring.js +166 -27
- package/cli/agent.js +189 -17
- package/cli/analyze.js +499 -0
- package/cli/audit.js +557 -0
- package/cli/auth.js +495 -38
- package/cli/billing.js +302 -0
- package/cli/build.js +695 -0
- package/cli/business.js +109 -26
- package/cli/checkpoint-utils.js +168 -0
- package/cli/checkpoint.js +639 -0
- package/cli/cloud-sync.js +447 -0
- package/cli/content.js +198 -0
- package/cli/context.js +1 -1
- package/cli/deploy.js +543 -0
- package/cli/fundraise.js +112 -50
- package/cli/github-cmd.js +435 -0
- package/cli/health.js +477 -0
- package/cli/init.js +84 -13
- package/cli/legal.js +107 -95
- package/cli/log.js +2 -2
- package/cli/loop.js +976 -73
- package/cli/manager.js +711 -0
- package/cli/metrics.js +480 -0
- package/cli/monitor.js +812 -0
- package/cli/onboard.js +521 -0
- package/cli/orchestrator.js +12 -24
- package/cli/prd.js +594 -0
- package/cli/preseed-start.js +1483 -0
- package/cli/preseed.js +2302 -0
- package/cli/project.js +436 -0
- package/cli/quality.js +233 -0
- package/cli/security.js +913 -0
- package/cli/seed.js +1441 -5
- package/cli/skill.js +273 -211
- package/cli/suggest.js +989 -0
- package/cli/switch.js +453 -0
- package/cli/visualize.js +527 -0
- package/cli/watch.js +769 -0
- package/cli/workspace.js +607 -0
- package/core/analyze-workflow.js +1134 -0
- package/core/api-client.js +535 -22
- package/core/audit-workflow.js +1350 -0
- package/core/build-orchestrator.js +480 -0
- package/core/build-state.js +577 -0
- package/core/checkpoint-engine.js +408 -0
- package/core/config.js +1109 -26
- package/core/context-loader.js +21 -1
- package/core/deploy-workflow.js +836 -0
- package/core/entitlements.js +93 -22
- package/core/github-sync.js +610 -0
- package/core/index.js +8 -1
- package/core/ingest.js +1111 -0
- package/core/metrics-engine.js +768 -0
- package/core/onboard-workflow.js +1007 -0
- package/core/preseed-workflow.js +934 -0
- package/core/preseed.js +1617 -0
- package/core/project-context.js +325 -0
- package/core/project-state.js +694 -0
- package/core/r2-sync.js +583 -0
- package/core/scaffold.js +525 -7
- package/core/session.js +258 -0
- package/core/task-extractor.js +758 -0
- package/core/telemetry.js +28 -6
- package/core/tier-enforcement.js +737 -0
- package/core/utils.js +38 -14
- package/generators/questionnaire.js +15 -12
- package/generators/sections/ai.js +7 -7
- package/generators/sections/content.js +300 -0
- package/generators/sections/index.js +3 -0
- package/generators/sections/plugins.js +7 -6
- package/generators/templates/build-planning.template.js +596 -0
- package/generators/templates/content.template.js +819 -0
- package/generators/templates/index.js +2 -1
- package/hooks/git-autopilot.js +1250 -0
- package/hooks/index.js +9 -0
- package/intelligence/agent-collab.js +2057 -0
- package/intelligence/auto-suggest.js +634 -0
- package/intelligence/content-gen.js +1589 -0
- package/intelligence/cross-project.js +1647 -0
- package/intelligence/index.js +184 -0
- package/intelligence/learning/insights.json +517 -7
- package/intelligence/learning/pattern-learner.js +1008 -14
- package/intelligence/memory/decision-tracker.js +1431 -31
- package/intelligence/memory/decisions.jsonl +0 -0
- package/intelligence/orchestrator.js +2896 -1
- package/intelligence/prd.js +92 -1
- package/intelligence/recommendation-weights.json +14 -2
- package/intelligence/recommendations.js +463 -9
- package/intelligence/workflow-composer.js +1451 -0
- package/marketplace/index.d.ts +324 -0
- package/marketplace/index.js +1921 -0
- package/mcp/contracts/mcp-contract.v1.json +342 -4
- package/mcp/registry.js +680 -3
- package/mcp/response-formatter.js +23 -0
- package/mcp/tools/assist-tool.js +78 -4
- package/mcp/tools/autopilot-tool.js +408 -0
- package/mcp/tools/content-tool.js +571 -0
- package/mcp/tools/dashboard-tool.js +251 -5
- package/mcp/tools/mvp-tool.js +344 -0
- package/mcp/tools/plugin-tool.js +23 -1
- package/mcp/tools/prd-tool.js +579 -0
- package/mcp/tools/seed-tool.js +447 -0
- package/mcp/tools/skill-tool.js +43 -14
- package/mcp/tools/suggest-tool.js +147 -0
- package/package.json +15 -6
- package/agents/README.md +0 -93
- package/agents/ai-integration-expert/context.md +0 -386
- package/agents/api-expert/context.md +0 -416
- package/agents/architecture-expert/context.md +0 -454
- package/agents/auth-expert/context.md +0 -399
- package/agents/backend-expert/context.md +0 -483
- package/agents/business-strategy-expert/context.md +0 -180
- package/agents/code-review-expert/context.md +0 -365
- package/agents/competitive-analysis-expert/context.md +0 -239
- package/agents/data-modeling-expert/context.md +0 -352
- package/agents/database-expert/context.md +0 -250
- package/agents/devops-expert/context.md +0 -446
- package/agents/email-expert/context.md +0 -379
- package/agents/financial-expert/context.md +0 -213
- package/agents/frontend-expert/context.md +0 -364
- package/agents/fundraising-expert/context.md +0 -257
- package/agents/growth-expert/context.md +0 -249
- package/agents/index.js +0 -140
- package/agents/investor-relations-expert/context.md +0 -266
- package/agents/legal-expert/context.md +0 -284
- package/agents/marketing-expert/context.md +0 -236
- package/agents/monitoring-expert/context.md +0 -362
- package/agents/operations-expert/context.md +0 -279
- package/agents/partnerships-expert/context.md +0 -286
- package/agents/payment-expert/context.md +0 -340
- package/agents/performance-expert/context.md +0 -377
- package/agents/private-equity-expert/context.md +0 -246
- package/agents/railway-expert/context.md +0 -284
- package/agents/research-expert/context.md +0 -245
- package/agents/sales-expert/context.md +0 -241
- package/agents/security-expert/context.md +0 -343
- package/agents/testing-expert/context.md +0 -414
- package/agents/ui-ux-expert/context.md +0 -448
- package/agents/vercel-expert/context.md +0 -426
- package/skills/index.js +0 -787
- package/skills/patterns/README.md +0 -163
- package/skills/patterns/ai/agents.md +0 -281
- package/skills/patterns/ai/claude.md +0 -138
- package/skills/patterns/ai/embeddings.md +0 -150
- package/skills/patterns/ai/rag.md +0 -266
- package/skills/patterns/ai/streaming.md +0 -170
- package/skills/patterns/ai/structured-output.md +0 -162
- package/skills/patterns/ai/tools.md +0 -154
- package/skills/patterns/analytics/tracking.md +0 -220
- package/skills/patterns/api/errors.md +0 -296
- package/skills/patterns/api/graphql.md +0 -440
- package/skills/patterns/api/middleware.md +0 -279
- package/skills/patterns/api/openapi.md +0 -285
- package/skills/patterns/api/rate-limiting.md +0 -231
- package/skills/patterns/api/route-handler.md +0 -217
- package/skills/patterns/api/server-action.md +0 -249
- package/skills/patterns/api/versioning.md +0 -443
- package/skills/patterns/api/webhooks.md +0 -247
- package/skills/patterns/auth/clerk.md +0 -132
- package/skills/patterns/auth/mfa.md +0 -313
- package/skills/patterns/auth/nextauth.md +0 -140
- package/skills/patterns/auth/oauth.md +0 -237
- package/skills/patterns/auth/rbac.md +0 -152
- package/skills/patterns/auth/session-management.md +0 -367
- package/skills/patterns/auth/session.md +0 -120
- package/skills/patterns/database/audit.md +0 -177
- package/skills/patterns/database/migrations.md +0 -177
- package/skills/patterns/database/pagination.md +0 -230
- package/skills/patterns/database/pooling.md +0 -357
- package/skills/patterns/database/prisma.md +0 -180
- package/skills/patterns/database/relations.md +0 -187
- package/skills/patterns/database/seeding.md +0 -246
- package/skills/patterns/database/soft-delete.md +0 -153
- package/skills/patterns/database/transactions.md +0 -162
- package/skills/patterns/deployment/ci-cd.md +0 -231
- package/skills/patterns/deployment/docker.md +0 -188
- package/skills/patterns/deployment/monitoring.md +0 -387
- package/skills/patterns/deployment/vercel.md +0 -160
- package/skills/patterns/email/resend.md +0 -143
- package/skills/patterns/email/templates.md +0 -245
- package/skills/patterns/email/transactional.md +0 -503
- package/skills/patterns/email/verification.md +0 -176
- package/skills/patterns/files/download.md +0 -243
- package/skills/patterns/files/upload.md +0 -239
- package/skills/patterns/i18n/nextintl.md +0 -188
- package/skills/patterns/logging/structured.md +0 -292
- package/skills/patterns/notifications/email-queue.md +0 -248
- package/skills/patterns/notifications/push.md +0 -279
- package/skills/patterns/payments/checkout.md +0 -303
- package/skills/patterns/payments/invoices.md +0 -287
- package/skills/patterns/payments/portal.md +0 -245
- package/skills/patterns/payments/stripe.md +0 -272
- package/skills/patterns/payments/subscriptions.md +0 -300
- package/skills/patterns/payments/usage.md +0 -279
- package/skills/patterns/performance/caching.md +0 -276
- package/skills/patterns/performance/code-splitting.md +0 -233
- package/skills/patterns/performance/edge.md +0 -254
- package/skills/patterns/performance/isr.md +0 -266
- package/skills/patterns/performance/lazy-loading.md +0 -281
- package/skills/patterns/realtime/sse.md +0 -327
- package/skills/patterns/realtime/websockets.md +0 -336
- package/skills/patterns/search/filtering.md +0 -329
- package/skills/patterns/search/fulltext.md +0 -260
- package/skills/patterns/security/audit-logging.md +0 -444
- package/skills/patterns/security/csrf.md +0 -234
- package/skills/patterns/security/headers.md +0 -252
- package/skills/patterns/security/sanitization.md +0 -258
- package/skills/patterns/security/secrets.md +0 -261
- package/skills/patterns/security/validation.md +0 -268
- package/skills/patterns/security/xss.md +0 -229
- package/skills/patterns/seo/metadata.md +0 -252
- package/skills/patterns/state/context.md +0 -349
- package/skills/patterns/state/react-query.md +0 -313
- package/skills/patterns/state/url-state.md +0 -482
- package/skills/patterns/state/zustand.md +0 -262
- package/skills/patterns/testing/api.md +0 -259
- package/skills/patterns/testing/component.md +0 -233
- package/skills/patterns/testing/coverage.md +0 -207
- package/skills/patterns/testing/fixtures.md +0 -225
- package/skills/patterns/testing/integration.md +0 -436
- package/skills/patterns/testing/mocking.md +0 -177
- package/skills/patterns/testing/playwright.md +0 -162
- package/skills/patterns/testing/snapshot.md +0 -175
- package/skills/patterns/testing/vitest.md +0 -307
- package/skills/patterns/ui/accordions.md +0 -395
- package/skills/patterns/ui/cards.md +0 -299
- package/skills/patterns/ui/dropdowns.md +0 -476
- package/skills/patterns/ui/empty-states.md +0 -320
- package/skills/patterns/ui/forms.md +0 -405
- package/skills/patterns/ui/inputs.md +0 -319
- package/skills/patterns/ui/layouts.md +0 -282
- package/skills/patterns/ui/loading.md +0 -291
- package/skills/patterns/ui/modals.md +0 -338
- package/skills/patterns/ui/navigation.md +0 -374
- package/skills/patterns/ui/tables.md +0 -407
- package/skills/patterns/ui/toasts.md +0 -300
- package/skills/patterns/ui/tooltips.md +0 -396
- package/skills/patterns/utils/dates.md +0 -435
- package/skills/patterns/utils/errors.md +0 -451
- package/skills/patterns/utils/formatting.md +0 -345
- package/skills/patterns/utils/validation.md +0 -434
- package/templates/bootspring.config.js +0 -83
- package/templates/business/business-model-canvas.md +0 -246
- package/templates/business/business-plan.md +0 -266
- package/templates/business/competitive-analysis.md +0 -312
- package/templates/fundraising/data-room-checklist.md +0 -300
- package/templates/fundraising/investor-research.md +0 -243
- package/templates/fundraising/pitch-deck-outline.md +0 -253
- package/templates/legal/gdpr-checklist.md +0 -339
- package/templates/legal/privacy-policy.md +0 -285
- package/templates/legal/terms-of-service.md +0 -222
- package/templates/mcp.json +0 -9
|
@@ -0,0 +1,1007 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bootspring Onboard Workflow Engine
|
|
3
|
+
*
|
|
4
|
+
* Layers Bootspring onto existing codebases through detection,
|
|
5
|
+
* pattern scanning, and configuration generation.
|
|
6
|
+
*
|
|
7
|
+
* @package bootspring
|
|
8
|
+
* @module core/onboard-workflow
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const fs = require('fs');
|
|
12
|
+
const path = require('path');
|
|
13
|
+
const { StructureAnalyzer } = require('../analyzers/structure-analyzer');
|
|
14
|
+
const { ArchitectureAnalyzer } = require('../analyzers/architecture-analyzer');
|
|
15
|
+
const { DependencyAnalyzer } = require('../analyzers/dependency-analyzer');
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Workflow phase status
|
|
19
|
+
*/
|
|
20
|
+
const PHASE_STATUS = {
|
|
21
|
+
PENDING: 'pending',
|
|
22
|
+
IN_PROGRESS: 'in_progress',
|
|
23
|
+
COMPLETED: 'completed',
|
|
24
|
+
SKIPPED: 'skipped',
|
|
25
|
+
FAILED: 'failed'
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Onboarding phases
|
|
30
|
+
*/
|
|
31
|
+
const ONBOARD_PHASES = {
|
|
32
|
+
detection: {
|
|
33
|
+
name: 'Stack Detection',
|
|
34
|
+
description: 'Detect framework, language, database, and hosting',
|
|
35
|
+
order: 1,
|
|
36
|
+
required: true
|
|
37
|
+
},
|
|
38
|
+
patterns: {
|
|
39
|
+
name: 'Pattern Scan',
|
|
40
|
+
description: 'Find auth, payments, components, and code patterns',
|
|
41
|
+
order: 2,
|
|
42
|
+
required: true,
|
|
43
|
+
dependencies: ['detection']
|
|
44
|
+
},
|
|
45
|
+
docs: {
|
|
46
|
+
name: 'Documentation Discovery',
|
|
47
|
+
description: 'Inventory existing docs and find gaps',
|
|
48
|
+
order: 3,
|
|
49
|
+
required: false,
|
|
50
|
+
dependencies: ['detection']
|
|
51
|
+
},
|
|
52
|
+
config: {
|
|
53
|
+
name: 'Config Generation',
|
|
54
|
+
description: 'Generate bootspring.config.js and CLAUDE.md',
|
|
55
|
+
order: 4,
|
|
56
|
+
required: true,
|
|
57
|
+
dependencies: ['detection', 'patterns']
|
|
58
|
+
},
|
|
59
|
+
baseline: {
|
|
60
|
+
name: 'Baseline Capture',
|
|
61
|
+
description: 'Record metrics for future comparison',
|
|
62
|
+
order: 5,
|
|
63
|
+
required: false,
|
|
64
|
+
dependencies: ['detection', 'patterns']
|
|
65
|
+
},
|
|
66
|
+
reverse: {
|
|
67
|
+
name: 'Reverse Engineer',
|
|
68
|
+
description: 'Generate PRD from code structure (optional)',
|
|
69
|
+
order: 6,
|
|
70
|
+
required: false,
|
|
71
|
+
dependencies: ['detection', 'patterns', 'docs']
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Stack detection patterns
|
|
77
|
+
*/
|
|
78
|
+
const STACK_DETECTION = {
|
|
79
|
+
frameworks: [
|
|
80
|
+
{ id: 'nextjs', name: 'Next.js', files: ['next.config.js', 'next.config.mjs', 'next.config.ts'], packages: ['next'] },
|
|
81
|
+
{ id: 'remix', name: 'Remix', files: ['remix.config.js'], packages: ['@remix-run/react'] },
|
|
82
|
+
{ id: 'nuxt', name: 'Nuxt', files: ['nuxt.config.js', 'nuxt.config.ts'], packages: ['nuxt'] },
|
|
83
|
+
{ id: 'svelte', name: 'SvelteKit', files: ['svelte.config.js'], packages: ['@sveltejs/kit'] },
|
|
84
|
+
{ id: 'astro', name: 'Astro', files: ['astro.config.mjs'], packages: ['astro'] },
|
|
85
|
+
{ id: 'vite', name: 'Vite', files: ['vite.config.js', 'vite.config.ts'], packages: ['vite'] },
|
|
86
|
+
{ id: 'express', name: 'Express', packages: ['express'] },
|
|
87
|
+
{ id: 'fastify', name: 'Fastify', packages: ['fastify'] },
|
|
88
|
+
{ id: 'nestjs', name: 'NestJS', packages: ['@nestjs/core'] },
|
|
89
|
+
{ id: 'react', name: 'React', packages: ['react'] },
|
|
90
|
+
{ id: 'vue', name: 'Vue', packages: ['vue'] },
|
|
91
|
+
{ id: 'angular', name: 'Angular', files: ['angular.json'], packages: ['@angular/core'] }
|
|
92
|
+
],
|
|
93
|
+
languages: [
|
|
94
|
+
{ id: 'typescript', name: 'TypeScript', files: ['tsconfig.json'], packages: ['typescript'] },
|
|
95
|
+
{ id: 'javascript', name: 'JavaScript', files: ['package.json'] }
|
|
96
|
+
],
|
|
97
|
+
databases: [
|
|
98
|
+
{ id: 'prisma', name: 'Prisma', files: ['prisma/schema.prisma'], packages: ['prisma', '@prisma/client'] },
|
|
99
|
+
{ id: 'drizzle', name: 'Drizzle', packages: ['drizzle-orm'] },
|
|
100
|
+
{ id: 'mongoose', name: 'MongoDB (Mongoose)', packages: ['mongoose'] },
|
|
101
|
+
{ id: 'typeorm', name: 'TypeORM', packages: ['typeorm'] },
|
|
102
|
+
{ id: 'sequelize', name: 'Sequelize', packages: ['sequelize'] },
|
|
103
|
+
{ id: 'knex', name: 'Knex', packages: ['knex'] },
|
|
104
|
+
{ id: 'supabase', name: 'Supabase', packages: ['@supabase/supabase-js'] },
|
|
105
|
+
{ id: 'firebase', name: 'Firebase', packages: ['firebase', 'firebase-admin'] }
|
|
106
|
+
],
|
|
107
|
+
hosting: [
|
|
108
|
+
{ id: 'vercel', name: 'Vercel', files: ['vercel.json', '.vercel'] },
|
|
109
|
+
{ id: 'netlify', name: 'Netlify', files: ['netlify.toml', '.netlify'] },
|
|
110
|
+
{ id: 'railway', name: 'Railway', files: ['railway.json'] },
|
|
111
|
+
{ id: 'docker', name: 'Docker', files: ['Dockerfile', 'docker-compose.yml'] },
|
|
112
|
+
{ id: 'fly', name: 'Fly.io', files: ['fly.toml'] },
|
|
113
|
+
{ id: 'render', name: 'Render', files: ['render.yaml'] },
|
|
114
|
+
{ id: 'aws', name: 'AWS', files: ['serverless.yml', 'amplify.yml', 'samconfig.toml'] },
|
|
115
|
+
{ id: 'gcp', name: 'Google Cloud', files: ['app.yaml', 'cloudbuild.yaml'] }
|
|
116
|
+
],
|
|
117
|
+
testing: [
|
|
118
|
+
{ id: 'vitest', name: 'Vitest', files: ['vitest.config.ts', 'vitest.config.js'], packages: ['vitest'] },
|
|
119
|
+
{ id: 'jest', name: 'Jest', files: ['jest.config.js', 'jest.config.ts'], packages: ['jest'] },
|
|
120
|
+
{ id: 'playwright', name: 'Playwright', files: ['playwright.config.ts'], packages: ['@playwright/test'] },
|
|
121
|
+
{ id: 'cypress', name: 'Cypress', files: ['cypress.config.js', 'cypress.config.ts'], packages: ['cypress'] },
|
|
122
|
+
{ id: 'mocha', name: 'Mocha', packages: ['mocha'] }
|
|
123
|
+
],
|
|
124
|
+
auth: [
|
|
125
|
+
{ id: 'nextauth', name: 'NextAuth.js', packages: ['next-auth'] },
|
|
126
|
+
{ id: 'authjs', name: 'Auth.js', packages: ['@auth/core'] },
|
|
127
|
+
{ id: 'clerk', name: 'Clerk', packages: ['@clerk/nextjs', '@clerk/clerk-react'] },
|
|
128
|
+
{ id: 'auth0', name: 'Auth0', packages: ['@auth0/nextjs-auth0', 'auth0'] },
|
|
129
|
+
{ id: 'supabase-auth', name: 'Supabase Auth', packages: ['@supabase/auth-helpers-nextjs'] },
|
|
130
|
+
{ id: 'firebase-auth', name: 'Firebase Auth', packages: ['firebase/auth'] },
|
|
131
|
+
{ id: 'passport', name: 'Passport.js', packages: ['passport'] }
|
|
132
|
+
],
|
|
133
|
+
payments: [
|
|
134
|
+
{ id: 'stripe', name: 'Stripe', packages: ['stripe', '@stripe/stripe-js'] },
|
|
135
|
+
{ id: 'lemon-squeezy', name: 'Lemon Squeezy', packages: ['@lemonsqueezy/lemonsqueezy.js'] },
|
|
136
|
+
{ id: 'paddle', name: 'Paddle', packages: ['@paddle/paddle-js'] },
|
|
137
|
+
{ id: 'paypal', name: 'PayPal', packages: ['@paypal/react-paypal-js', 'paypal-rest-sdk'] }
|
|
138
|
+
]
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Default workflow state
|
|
143
|
+
*/
|
|
144
|
+
const DEFAULT_STATE = {
|
|
145
|
+
version: '1.0.0',
|
|
146
|
+
startedAt: null,
|
|
147
|
+
lastUpdated: null,
|
|
148
|
+
currentPhase: null,
|
|
149
|
+
phases: {},
|
|
150
|
+
detection: {
|
|
151
|
+
stack: null,
|
|
152
|
+
patterns: null,
|
|
153
|
+
docs: null
|
|
154
|
+
},
|
|
155
|
+
generated: {
|
|
156
|
+
config: null,
|
|
157
|
+
claudeMd: null
|
|
158
|
+
},
|
|
159
|
+
baseline: null
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* OnboardWorkflowEngine - Manages onboarding workflow
|
|
164
|
+
*/
|
|
165
|
+
class OnboardWorkflowEngine {
|
|
166
|
+
constructor(projectRoot, options = {}) {
|
|
167
|
+
this.projectRoot = projectRoot;
|
|
168
|
+
this.workflowDir = path.join(projectRoot, '.bootspring', 'onboard');
|
|
169
|
+
this.stateFile = path.join(this.workflowDir, 'workflow-state.json');
|
|
170
|
+
this.detectionDir = path.join(this.workflowDir, 'detection');
|
|
171
|
+
this.generatedDir = path.join(this.workflowDir, 'generated');
|
|
172
|
+
this.baselineDir = path.join(this.workflowDir, 'baseline');
|
|
173
|
+
this.options = options;
|
|
174
|
+
this.state = null;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Setup directories
|
|
179
|
+
*/
|
|
180
|
+
setupDirectories() {
|
|
181
|
+
const dirs = [
|
|
182
|
+
this.workflowDir,
|
|
183
|
+
this.detectionDir,
|
|
184
|
+
this.generatedDir,
|
|
185
|
+
this.baselineDir
|
|
186
|
+
];
|
|
187
|
+
|
|
188
|
+
for (const dir of dirs) {
|
|
189
|
+
if (!fs.existsSync(dir)) {
|
|
190
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Load workflow state
|
|
197
|
+
*/
|
|
198
|
+
loadState() {
|
|
199
|
+
if (fs.existsSync(this.stateFile)) {
|
|
200
|
+
try {
|
|
201
|
+
this.state = JSON.parse(fs.readFileSync(this.stateFile, 'utf-8'));
|
|
202
|
+
return true;
|
|
203
|
+
} catch (_e) {
|
|
204
|
+
this.state = { ...DEFAULT_STATE };
|
|
205
|
+
return false;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
this.state = { ...DEFAULT_STATE };
|
|
209
|
+
return false;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Save workflow state
|
|
214
|
+
*/
|
|
215
|
+
saveState() {
|
|
216
|
+
this.setupDirectories();
|
|
217
|
+
this.state.lastUpdated = new Date().toISOString();
|
|
218
|
+
fs.writeFileSync(this.stateFile, JSON.stringify(this.state, null, 2));
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Initialize workflow
|
|
223
|
+
*/
|
|
224
|
+
initializeWorkflow() {
|
|
225
|
+
this.setupDirectories();
|
|
226
|
+
this.state = {
|
|
227
|
+
...DEFAULT_STATE,
|
|
228
|
+
startedAt: new Date().toISOString(),
|
|
229
|
+
lastUpdated: new Date().toISOString()
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
// Initialize phase states
|
|
233
|
+
for (const phaseId of Object.keys(ONBOARD_PHASES)) {
|
|
234
|
+
this.state.phases[phaseId] = {
|
|
235
|
+
status: PHASE_STATUS.PENDING,
|
|
236
|
+
startedAt: null,
|
|
237
|
+
completedAt: null,
|
|
238
|
+
result: null,
|
|
239
|
+
error: null
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
this.state.currentPhase = 'detection';
|
|
244
|
+
this.saveState();
|
|
245
|
+
return this.state;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Check if workflow exists
|
|
250
|
+
*/
|
|
251
|
+
hasWorkflow() {
|
|
252
|
+
return fs.existsSync(this.stateFile);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Reset workflow
|
|
257
|
+
*/
|
|
258
|
+
resetWorkflow() {
|
|
259
|
+
if (fs.existsSync(this.stateFile)) {
|
|
260
|
+
fs.unlinkSync(this.stateFile);
|
|
261
|
+
}
|
|
262
|
+
this.state = null;
|
|
263
|
+
return true;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Check if phase dependencies are met
|
|
268
|
+
*/
|
|
269
|
+
arePhaseDependenciesMet(phaseId) {
|
|
270
|
+
const phase = ONBOARD_PHASES[phaseId];
|
|
271
|
+
if (!phase || !phase.dependencies || phase.dependencies.length === 0) {
|
|
272
|
+
return true;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
for (const depPhaseId of phase.dependencies) {
|
|
276
|
+
const depPhase = this.state.phases[depPhaseId];
|
|
277
|
+
if (!depPhase || depPhase.status !== PHASE_STATUS.COMPLETED) {
|
|
278
|
+
return false;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
return true;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Get next phase
|
|
287
|
+
*/
|
|
288
|
+
getNextPhase() {
|
|
289
|
+
const phaseOrder = Object.keys(ONBOARD_PHASES).sort(
|
|
290
|
+
(a, b) => ONBOARD_PHASES[a].order - ONBOARD_PHASES[b].order
|
|
291
|
+
);
|
|
292
|
+
|
|
293
|
+
for (const phaseId of phaseOrder) {
|
|
294
|
+
const phase = this.state.phases[phaseId];
|
|
295
|
+
if (phase.status === PHASE_STATUS.PENDING && this.arePhaseDependenciesMet(phaseId)) {
|
|
296
|
+
return phaseId;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
return null;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Start a phase
|
|
305
|
+
*/
|
|
306
|
+
startPhase(phaseId) {
|
|
307
|
+
if (!this.state.phases[phaseId]) {
|
|
308
|
+
throw new Error(`Unknown phase: ${phaseId}`);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
this.state.phases[phaseId].status = PHASE_STATUS.IN_PROGRESS;
|
|
312
|
+
this.state.phases[phaseId].startedAt = new Date().toISOString();
|
|
313
|
+
this.state.currentPhase = phaseId;
|
|
314
|
+
this.saveState();
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Complete a phase
|
|
319
|
+
*/
|
|
320
|
+
completePhase(phaseId, result = null) {
|
|
321
|
+
if (!this.state.phases[phaseId]) {
|
|
322
|
+
throw new Error(`Unknown phase: ${phaseId}`);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
this.state.phases[phaseId].status = PHASE_STATUS.COMPLETED;
|
|
326
|
+
this.state.phases[phaseId].completedAt = new Date().toISOString();
|
|
327
|
+
this.state.phases[phaseId].result = result;
|
|
328
|
+
this.saveState();
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Fail a phase
|
|
333
|
+
*/
|
|
334
|
+
failPhase(phaseId, error) {
|
|
335
|
+
if (!this.state.phases[phaseId]) {
|
|
336
|
+
throw new Error(`Unknown phase: ${phaseId}`);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
this.state.phases[phaseId].status = PHASE_STATUS.FAILED;
|
|
340
|
+
this.state.phases[phaseId].completedAt = new Date().toISOString();
|
|
341
|
+
this.state.phases[phaseId].error = error;
|
|
342
|
+
this.saveState();
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Skip a phase
|
|
347
|
+
*/
|
|
348
|
+
skipPhase(phaseId) {
|
|
349
|
+
if (!this.state.phases[phaseId]) {
|
|
350
|
+
throw new Error(`Unknown phase: ${phaseId}`);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
this.state.phases[phaseId].status = PHASE_STATUS.SKIPPED;
|
|
354
|
+
this.state.phases[phaseId].completedAt = new Date().toISOString();
|
|
355
|
+
this.saveState();
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Run detection phase
|
|
360
|
+
*/
|
|
361
|
+
async runDetection() {
|
|
362
|
+
const stack = {
|
|
363
|
+
framework: null,
|
|
364
|
+
language: null,
|
|
365
|
+
database: null,
|
|
366
|
+
hosting: null,
|
|
367
|
+
testing: null,
|
|
368
|
+
auth: null,
|
|
369
|
+
payments: null,
|
|
370
|
+
detected: []
|
|
371
|
+
};
|
|
372
|
+
|
|
373
|
+
// Read package.json if exists
|
|
374
|
+
const pkgPath = path.join(this.projectRoot, 'package.json');
|
|
375
|
+
let pkg = null;
|
|
376
|
+
if (fs.existsSync(pkgPath)) {
|
|
377
|
+
try {
|
|
378
|
+
pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
379
|
+
} catch (_e) {
|
|
380
|
+
// Skip
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
const allDeps = pkg ? {
|
|
385
|
+
...pkg.dependencies,
|
|
386
|
+
...pkg.devDependencies
|
|
387
|
+
} : {};
|
|
388
|
+
|
|
389
|
+
// Check for files and packages
|
|
390
|
+
const checkDetection = (category, detections) => {
|
|
391
|
+
for (const detection of detections) {
|
|
392
|
+
// Check files
|
|
393
|
+
if (detection.files) {
|
|
394
|
+
for (const file of detection.files) {
|
|
395
|
+
const filePath = path.join(this.projectRoot, file);
|
|
396
|
+
if (fs.existsSync(filePath)) {
|
|
397
|
+
return detection;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// Check packages
|
|
403
|
+
if (detection.packages) {
|
|
404
|
+
for (const pkgName of detection.packages) {
|
|
405
|
+
if (allDeps[pkgName]) {
|
|
406
|
+
return detection;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
return null;
|
|
412
|
+
};
|
|
413
|
+
|
|
414
|
+
// Map plural category names to singular for stack object
|
|
415
|
+
const categoryToStackKey = {
|
|
416
|
+
frameworks: 'framework',
|
|
417
|
+
languages: 'language',
|
|
418
|
+
databases: 'database',
|
|
419
|
+
hosting: 'hosting',
|
|
420
|
+
testing: 'testing',
|
|
421
|
+
auth: 'auth',
|
|
422
|
+
payments: 'payments'
|
|
423
|
+
};
|
|
424
|
+
|
|
425
|
+
// Detect each category
|
|
426
|
+
for (const [category, detections] of Object.entries(STACK_DETECTION)) {
|
|
427
|
+
const detected = checkDetection(category, detections);
|
|
428
|
+
if (detected) {
|
|
429
|
+
const stackKey = categoryToStackKey[category] || category;
|
|
430
|
+
stack[stackKey] = {
|
|
431
|
+
id: detected.id,
|
|
432
|
+
name: detected.name
|
|
433
|
+
};
|
|
434
|
+
stack.detected.push({
|
|
435
|
+
category,
|
|
436
|
+
...detected
|
|
437
|
+
});
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
// Run structure analysis
|
|
442
|
+
const structureAnalyzer = new StructureAnalyzer(this.projectRoot);
|
|
443
|
+
const structure = structureAnalyzer.analyze();
|
|
444
|
+
|
|
445
|
+
// Save detection results
|
|
446
|
+
const detectionResult = {
|
|
447
|
+
stack,
|
|
448
|
+
structure,
|
|
449
|
+
packageJson: pkg ? {
|
|
450
|
+
name: pkg.name,
|
|
451
|
+
version: pkg.version,
|
|
452
|
+
description: pkg.description
|
|
453
|
+
} : null,
|
|
454
|
+
timestamp: new Date().toISOString()
|
|
455
|
+
};
|
|
456
|
+
|
|
457
|
+
// Write to detection directory
|
|
458
|
+
fs.writeFileSync(
|
|
459
|
+
path.join(this.detectionDir, 'stack.json'),
|
|
460
|
+
JSON.stringify(stack, null, 2)
|
|
461
|
+
);
|
|
462
|
+
fs.writeFileSync(
|
|
463
|
+
path.join(this.detectionDir, 'structure.json'),
|
|
464
|
+
JSON.stringify(structure, null, 2)
|
|
465
|
+
);
|
|
466
|
+
|
|
467
|
+
this.state.detection.stack = stack;
|
|
468
|
+
return detectionResult;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
/**
|
|
472
|
+
* Run pattern scan phase
|
|
473
|
+
*/
|
|
474
|
+
async runPatternScan() {
|
|
475
|
+
// Auto-detect large codebase and use quick mode
|
|
476
|
+
const structurePath = path.join(this.detectionDir, 'structure.json');
|
|
477
|
+
let fileCount = 0;
|
|
478
|
+
if (fs.existsSync(structurePath)) {
|
|
479
|
+
try {
|
|
480
|
+
const structure = JSON.parse(fs.readFileSync(structurePath, 'utf-8'));
|
|
481
|
+
fileCount = structure.stats?.totalFiles || 0;
|
|
482
|
+
} catch (_e) {
|
|
483
|
+
// Ignore
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
// For large codebases (500+ files), use quick mode automatically
|
|
488
|
+
const LARGE_CODEBASE_THRESHOLD = 500;
|
|
489
|
+
if (fileCount >= LARGE_CODEBASE_THRESHOLD) {
|
|
490
|
+
return this.runQuickPatternScan();
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
// Run architecture analysis
|
|
494
|
+
const architectureAnalyzer = new ArchitectureAnalyzer(this.projectRoot);
|
|
495
|
+
const architecture = architectureAnalyzer.analyze();
|
|
496
|
+
|
|
497
|
+
// Run dependency analysis
|
|
498
|
+
const dependencyAnalyzer = new DependencyAnalyzer(this.projectRoot);
|
|
499
|
+
const dependencies = dependencyAnalyzer.analyze();
|
|
500
|
+
|
|
501
|
+
const patterns = {
|
|
502
|
+
architecture,
|
|
503
|
+
dependencies,
|
|
504
|
+
features: this.detectFeatures(),
|
|
505
|
+
timestamp: new Date().toISOString()
|
|
506
|
+
};
|
|
507
|
+
|
|
508
|
+
// Save pattern results
|
|
509
|
+
fs.writeFileSync(
|
|
510
|
+
path.join(this.detectionDir, 'patterns.json'),
|
|
511
|
+
JSON.stringify(patterns, null, 2)
|
|
512
|
+
);
|
|
513
|
+
|
|
514
|
+
this.state.detection.patterns = {
|
|
515
|
+
architecturePatterns: architecture.patterns.slice(0, 3),
|
|
516
|
+
moduleCount: architecture.modules.length,
|
|
517
|
+
circularDeps: dependencies.circularDependencies.length
|
|
518
|
+
};
|
|
519
|
+
|
|
520
|
+
return patterns;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
/**
|
|
524
|
+
* Run quick pattern scan (skips heavy dependency analysis)
|
|
525
|
+
*/
|
|
526
|
+
async runQuickPatternScan() {
|
|
527
|
+
// Run architecture analysis only (fast)
|
|
528
|
+
const architectureAnalyzer = new ArchitectureAnalyzer(this.projectRoot);
|
|
529
|
+
const architecture = architectureAnalyzer.analyze();
|
|
530
|
+
|
|
531
|
+
// Skip dependency analysis - it's too slow for large codebases
|
|
532
|
+
const patterns = {
|
|
533
|
+
architecture,
|
|
534
|
+
dependencies: {
|
|
535
|
+
fileCount: 0,
|
|
536
|
+
circularDependencies: [],
|
|
537
|
+
unusedDependencies: [],
|
|
538
|
+
summary: { totalFiles: 0, circularCount: 0, unusedCount: 0, skipped: true }
|
|
539
|
+
},
|
|
540
|
+
features: this.detectFeatures(),
|
|
541
|
+
timestamp: new Date().toISOString(),
|
|
542
|
+
quickMode: true
|
|
543
|
+
};
|
|
544
|
+
|
|
545
|
+
// Save pattern results
|
|
546
|
+
fs.writeFileSync(
|
|
547
|
+
path.join(this.detectionDir, 'patterns.json'),
|
|
548
|
+
JSON.stringify(patterns, null, 2)
|
|
549
|
+
);
|
|
550
|
+
|
|
551
|
+
this.state.detection.patterns = {
|
|
552
|
+
architecturePatterns: architecture.patterns.slice(0, 3),
|
|
553
|
+
moduleCount: architecture.modules.length,
|
|
554
|
+
circularDeps: 0,
|
|
555
|
+
quickMode: true
|
|
556
|
+
};
|
|
557
|
+
|
|
558
|
+
return patterns;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
/**
|
|
562
|
+
* Detect features in the codebase
|
|
563
|
+
*/
|
|
564
|
+
detectFeatures() {
|
|
565
|
+
const features = {
|
|
566
|
+
auth: false,
|
|
567
|
+
payments: false,
|
|
568
|
+
api: false,
|
|
569
|
+
database: false,
|
|
570
|
+
email: false,
|
|
571
|
+
uploads: false,
|
|
572
|
+
search: false,
|
|
573
|
+
realtime: false,
|
|
574
|
+
analytics: false,
|
|
575
|
+
i18n: false
|
|
576
|
+
};
|
|
577
|
+
|
|
578
|
+
// Check for feature directories
|
|
579
|
+
const featureDirs = {
|
|
580
|
+
auth: ['auth', 'authentication', 'login'],
|
|
581
|
+
payments: ['payments', 'billing', 'subscriptions', 'checkout'],
|
|
582
|
+
api: ['api', 'routes', 'endpoints'],
|
|
583
|
+
database: ['db', 'database', 'prisma', 'models'],
|
|
584
|
+
email: ['email', 'mail', 'notifications'],
|
|
585
|
+
uploads: ['uploads', 'files', 'storage'],
|
|
586
|
+
search: ['search'],
|
|
587
|
+
realtime: ['websocket', 'realtime', 'socket'],
|
|
588
|
+
analytics: ['analytics', 'tracking'],
|
|
589
|
+
i18n: ['i18n', 'locales', 'translations']
|
|
590
|
+
};
|
|
591
|
+
|
|
592
|
+
const searchPaths = [
|
|
593
|
+
this.projectRoot,
|
|
594
|
+
path.join(this.projectRoot, 'src'),
|
|
595
|
+
path.join(this.projectRoot, 'app'),
|
|
596
|
+
path.join(this.projectRoot, 'lib')
|
|
597
|
+
];
|
|
598
|
+
|
|
599
|
+
for (const searchPath of searchPaths) {
|
|
600
|
+
if (!fs.existsSync(searchPath)) continue;
|
|
601
|
+
|
|
602
|
+
try {
|
|
603
|
+
const items = fs.readdirSync(searchPath);
|
|
604
|
+
for (const item of items) {
|
|
605
|
+
const lowerItem = item.toLowerCase();
|
|
606
|
+
for (const [feature, dirs] of Object.entries(featureDirs)) {
|
|
607
|
+
if (dirs.includes(lowerItem)) {
|
|
608
|
+
features[feature] = true;
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
} catch (_e) {
|
|
613
|
+
// Skip
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
return features;
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
/**
|
|
621
|
+
* Run documentation discovery phase
|
|
622
|
+
*/
|
|
623
|
+
async runDocDiscovery() {
|
|
624
|
+
const docs = {
|
|
625
|
+
found: [],
|
|
626
|
+
missing: [],
|
|
627
|
+
recommended: []
|
|
628
|
+
};
|
|
629
|
+
|
|
630
|
+
// Check for common documentation files
|
|
631
|
+
const commonDocs = [
|
|
632
|
+
{ file: 'README.md', type: 'readme', required: true },
|
|
633
|
+
{ file: 'CONTRIBUTING.md', type: 'contributing', required: false },
|
|
634
|
+
{ file: 'CHANGELOG.md', type: 'changelog', required: false },
|
|
635
|
+
{ file: 'LICENSE', type: 'license', required: true },
|
|
636
|
+
{ file: 'CODE_OF_CONDUCT.md', type: 'code-of-conduct', required: false },
|
|
637
|
+
{ file: 'SECURITY.md', type: 'security', required: false },
|
|
638
|
+
{ file: 'docs/README.md', type: 'docs-readme', required: false },
|
|
639
|
+
{ file: 'API.md', type: 'api-docs', required: false },
|
|
640
|
+
{ file: '.env.example', type: 'env-example', required: true }
|
|
641
|
+
];
|
|
642
|
+
|
|
643
|
+
for (const doc of commonDocs) {
|
|
644
|
+
const docPath = path.join(this.projectRoot, doc.file);
|
|
645
|
+
if (fs.existsSync(docPath)) {
|
|
646
|
+
docs.found.push({
|
|
647
|
+
...doc,
|
|
648
|
+
path: doc.file,
|
|
649
|
+
size: fs.statSync(docPath).size
|
|
650
|
+
});
|
|
651
|
+
} else if (doc.required) {
|
|
652
|
+
docs.missing.push(doc);
|
|
653
|
+
} else {
|
|
654
|
+
docs.recommended.push(doc);
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
// Check for docs directory
|
|
659
|
+
const docsDir = path.join(this.projectRoot, 'docs');
|
|
660
|
+
if (fs.existsSync(docsDir)) {
|
|
661
|
+
docs.hasDocsDir = true;
|
|
662
|
+
try {
|
|
663
|
+
const docFiles = fs.readdirSync(docsDir);
|
|
664
|
+
docs.docsCount = docFiles.length;
|
|
665
|
+
} catch (_e) {
|
|
666
|
+
docs.docsCount = 0;
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
this.state.detection.docs = docs;
|
|
671
|
+
return docs;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
/**
|
|
675
|
+
* Run config generation phase
|
|
676
|
+
*/
|
|
677
|
+
async runConfigGeneration() {
|
|
678
|
+
const stack = this.state.detection.stack || {};
|
|
679
|
+
const patterns = this.state.detection.patterns || {};
|
|
680
|
+
|
|
681
|
+
// Generate bootspring.config.js
|
|
682
|
+
const configContent = this.generateConfigFile(stack, patterns);
|
|
683
|
+
const configPath = path.join(this.projectRoot, 'bootspring.config.js');
|
|
684
|
+
|
|
685
|
+
// Generate CLAUDE.md
|
|
686
|
+
const claudeMdContent = this.generateClaudeMd(stack, patterns);
|
|
687
|
+
const claudeMdPath = path.join(this.projectRoot, 'CLAUDE.md');
|
|
688
|
+
|
|
689
|
+
// Save to generated directory first
|
|
690
|
+
fs.writeFileSync(
|
|
691
|
+
path.join(this.generatedDir, 'bootspring.config.js'),
|
|
692
|
+
configContent
|
|
693
|
+
);
|
|
694
|
+
fs.writeFileSync(
|
|
695
|
+
path.join(this.generatedDir, 'CLAUDE.md'),
|
|
696
|
+
claudeMdContent
|
|
697
|
+
);
|
|
698
|
+
|
|
699
|
+
this.state.generated = {
|
|
700
|
+
config: configPath,
|
|
701
|
+
claudeMd: claudeMdPath,
|
|
702
|
+
timestamp: new Date().toISOString()
|
|
703
|
+
};
|
|
704
|
+
|
|
705
|
+
return {
|
|
706
|
+
configContent,
|
|
707
|
+
claudeMdContent,
|
|
708
|
+
configPath,
|
|
709
|
+
claudeMdPath
|
|
710
|
+
};
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
/**
|
|
714
|
+
* Apply generated config files
|
|
715
|
+
*/
|
|
716
|
+
applyGeneratedFiles() {
|
|
717
|
+
const generatedConfig = path.join(this.generatedDir, 'bootspring.config.js');
|
|
718
|
+
const generatedClaudeMd = path.join(this.generatedDir, 'CLAUDE.md');
|
|
719
|
+
|
|
720
|
+
if (fs.existsSync(generatedConfig)) {
|
|
721
|
+
fs.copyFileSync(generatedConfig, path.join(this.projectRoot, 'bootspring.config.js'));
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
if (fs.existsSync(generatedClaudeMd)) {
|
|
725
|
+
fs.copyFileSync(generatedClaudeMd, path.join(this.projectRoot, 'CLAUDE.md'));
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
return true;
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
/**
|
|
732
|
+
* Generate bootspring.config.js content
|
|
733
|
+
*/
|
|
734
|
+
generateConfigFile(stack, patterns) {
|
|
735
|
+
const framework = stack.framework?.id || 'unknown';
|
|
736
|
+
const language = stack.language?.id || 'javascript';
|
|
737
|
+
const database = stack.database?.id || null;
|
|
738
|
+
const hosting = stack.hosting?.id || null;
|
|
739
|
+
|
|
740
|
+
const plugins = [];
|
|
741
|
+
if (database) {
|
|
742
|
+
plugins.push(` database: {\n provider: '${database}',\n features: ['default']\n }`);
|
|
743
|
+
}
|
|
744
|
+
if (stack.testing) {
|
|
745
|
+
plugins.push(` testing: {\n provider: '${stack.testing.id}',\n features: ['default']\n }`);
|
|
746
|
+
}
|
|
747
|
+
plugins.push(' security: {\n provider: \'default\',\n features: [\'default\']\n }');
|
|
748
|
+
|
|
749
|
+
return `/**
|
|
750
|
+
* Bootspring Configuration
|
|
751
|
+
* Generated by: bootspring onboard
|
|
752
|
+
* Generated at: ${new Date().toISOString()}
|
|
753
|
+
*/
|
|
754
|
+
|
|
755
|
+
module.exports = {
|
|
756
|
+
// Project information
|
|
757
|
+
project: {
|
|
758
|
+
name: '${stack.packageJson?.name || 'my-project'}',
|
|
759
|
+
version: '${stack.packageJson?.version || '1.0.0'}',
|
|
760
|
+
description: '${stack.packageJson?.description || 'A modern web application'}'
|
|
761
|
+
},
|
|
762
|
+
|
|
763
|
+
// Technology stack
|
|
764
|
+
stack: {
|
|
765
|
+
framework: '${framework}',
|
|
766
|
+
language: '${language}',
|
|
767
|
+
database: ${database ? `'${database}'` : 'null'},
|
|
768
|
+
hosting: ${hosting ? `'${hosting}'` : 'null'}
|
|
769
|
+
},
|
|
770
|
+
|
|
771
|
+
// Enabled plugins
|
|
772
|
+
plugins: {
|
|
773
|
+
${plugins.join(',\n')}
|
|
774
|
+
},
|
|
775
|
+
|
|
776
|
+
// AI context settings
|
|
777
|
+
context: {
|
|
778
|
+
// Files to include in AI context
|
|
779
|
+
include: [
|
|
780
|
+
'src/**/*.{ts,tsx,js,jsx}',
|
|
781
|
+
'app/**/*.{ts,tsx,js,jsx}',
|
|
782
|
+
'lib/**/*.{ts,tsx,js,jsx}',
|
|
783
|
+
'components/**/*.{ts,tsx,js,jsx}'
|
|
784
|
+
],
|
|
785
|
+
// Files to exclude from AI context
|
|
786
|
+
exclude: [
|
|
787
|
+
'node_modules/**',
|
|
788
|
+
'dist/**',
|
|
789
|
+
'build/**',
|
|
790
|
+
'.next/**',
|
|
791
|
+
'coverage/**'
|
|
792
|
+
]
|
|
793
|
+
},
|
|
794
|
+
|
|
795
|
+
// Development settings
|
|
796
|
+
dev: {
|
|
797
|
+
// Quality gates
|
|
798
|
+
quality: {
|
|
799
|
+
preCommit: ['typecheck', 'lint'],
|
|
800
|
+
prePush: ['typecheck', 'lint', 'test'],
|
|
801
|
+
preDeploy: ['typecheck', 'lint', 'test', 'build']
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
};
|
|
805
|
+
`;
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
/**
|
|
809
|
+
* Generate CLAUDE.md content
|
|
810
|
+
*/
|
|
811
|
+
generateClaudeMd(stack, patterns) {
|
|
812
|
+
const framework = stack.framework?.name || 'Unknown Framework';
|
|
813
|
+
const language = stack.language?.name || 'JavaScript';
|
|
814
|
+
const database = stack.database?.name || 'None';
|
|
815
|
+
const hosting = stack.hosting?.name || 'Unknown';
|
|
816
|
+
|
|
817
|
+
const features = patterns.features || {};
|
|
818
|
+
const enabledFeatures = Object.entries(features)
|
|
819
|
+
.filter(([, v]) => v)
|
|
820
|
+
.map(([k]) => k);
|
|
821
|
+
|
|
822
|
+
return `# Project - AI Context
|
|
823
|
+
|
|
824
|
+
**Generated by**: Bootspring Onboard
|
|
825
|
+
**Last Updated**: ${new Date().toISOString().split('T')[0]}
|
|
826
|
+
**Status**: initialized
|
|
827
|
+
**Health**: good
|
|
828
|
+
|
|
829
|
+
---
|
|
830
|
+
|
|
831
|
+
## Project Overview
|
|
832
|
+
|
|
833
|
+
A modern web application built with ${framework}.
|
|
834
|
+
|
|
835
|
+
---
|
|
836
|
+
|
|
837
|
+
## Tech Stack
|
|
838
|
+
|
|
839
|
+
| Component | Technology |
|
|
840
|
+
|-----------|------------|
|
|
841
|
+
| Framework | ${framework} |
|
|
842
|
+
| Language | ${language} |
|
|
843
|
+
| Database | ${database} |
|
|
844
|
+
| Hosting | ${hosting} |
|
|
845
|
+
|
|
846
|
+
---
|
|
847
|
+
|
|
848
|
+
## Detected Features
|
|
849
|
+
|
|
850
|
+
${enabledFeatures.length > 0 ? enabledFeatures.map(f => `- **${f}**: Detected`).join('\n') : '- No specific features detected'}
|
|
851
|
+
|
|
852
|
+
---
|
|
853
|
+
|
|
854
|
+
## Architecture
|
|
855
|
+
|
|
856
|
+
${patterns.architecturePatterns?.length > 0
|
|
857
|
+
? patterns.architecturePatterns.map(p => `- **${p.name}**: ${p.description || 'Detected pattern'}`).join('\n')
|
|
858
|
+
: '- Standard project structure'}
|
|
859
|
+
|
|
860
|
+
---
|
|
861
|
+
|
|
862
|
+
## Development Guidelines
|
|
863
|
+
|
|
864
|
+
### Code Style
|
|
865
|
+
- Use ${language === 'TypeScript' ? 'TypeScript' : 'JavaScript'} for all new code
|
|
866
|
+
- Follow existing naming conventions in the codebase
|
|
867
|
+
- Keep files focused and under 300 lines when possible
|
|
868
|
+
|
|
869
|
+
### Best Practices
|
|
870
|
+
${framework.includes('Next') ? '- Use Server Components by default\n- Prefer Server Actions over API routes for mutations' : '- Follow framework best practices'}
|
|
871
|
+
- Use Zod for all input validation
|
|
872
|
+
- Never expose API keys to client-side code
|
|
873
|
+
- Write tests for new features
|
|
874
|
+
|
|
875
|
+
### Git Commits
|
|
876
|
+
- Use conventional commit format: \`feat:\`, \`fix:\`, \`docs:\`, \`refactor:\`
|
|
877
|
+
- Keep commits focused and atomic
|
|
878
|
+
- Never commit sensitive data or API keys
|
|
879
|
+
|
|
880
|
+
---
|
|
881
|
+
|
|
882
|
+
## Current State
|
|
883
|
+
|
|
884
|
+
- **Phase**: initialized
|
|
885
|
+
- **Health**: good
|
|
886
|
+
- **Open Todos**: 0
|
|
887
|
+
- **Issues**: none
|
|
888
|
+
|
|
889
|
+
---
|
|
890
|
+
|
|
891
|
+
*Generated by [Bootspring](https://bootspring.com) - Development scaffolding with intelligence*
|
|
892
|
+
`;
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
/**
|
|
896
|
+
* Run baseline capture phase
|
|
897
|
+
*/
|
|
898
|
+
async runBaselineCapture() {
|
|
899
|
+
const baseline = {
|
|
900
|
+
timestamp: new Date().toISOString(),
|
|
901
|
+
metrics: {
|
|
902
|
+
files: 0,
|
|
903
|
+
lines: 0,
|
|
904
|
+
dependencies: 0
|
|
905
|
+
}
|
|
906
|
+
};
|
|
907
|
+
|
|
908
|
+
// Count files
|
|
909
|
+
const structurePath = path.join(this.detectionDir, 'structure.json');
|
|
910
|
+
if (fs.existsSync(structurePath)) {
|
|
911
|
+
const structure = JSON.parse(fs.readFileSync(structurePath, 'utf-8'));
|
|
912
|
+
baseline.metrics.files = structure.stats?.totalFiles || 0;
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
// Count dependencies
|
|
916
|
+
const pkgPath = path.join(this.projectRoot, 'package.json');
|
|
917
|
+
if (fs.existsSync(pkgPath)) {
|
|
918
|
+
try {
|
|
919
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
920
|
+
baseline.metrics.dependencies =
|
|
921
|
+
Object.keys(pkg.dependencies || {}).length +
|
|
922
|
+
Object.keys(pkg.devDependencies || {}).length;
|
|
923
|
+
} catch (_e) {
|
|
924
|
+
// Skip
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
// Save baseline
|
|
929
|
+
fs.writeFileSync(
|
|
930
|
+
path.join(this.baselineDir, 'metrics.json'),
|
|
931
|
+
JSON.stringify(baseline, null, 2)
|
|
932
|
+
);
|
|
933
|
+
|
|
934
|
+
this.state.baseline = baseline;
|
|
935
|
+
return baseline;
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
/**
|
|
939
|
+
* Get workflow progress
|
|
940
|
+
*/
|
|
941
|
+
getProgress() {
|
|
942
|
+
if (!this.state) return null;
|
|
943
|
+
|
|
944
|
+
const phases = Object.entries(ONBOARD_PHASES).map(([phaseId, phase]) => {
|
|
945
|
+
const phaseState = this.state.phases[phaseId] || { status: PHASE_STATUS.PENDING };
|
|
946
|
+
|
|
947
|
+
return {
|
|
948
|
+
id: phaseId,
|
|
949
|
+
name: phase.name,
|
|
950
|
+
description: phase.description,
|
|
951
|
+
order: phase.order,
|
|
952
|
+
required: phase.required,
|
|
953
|
+
status: phaseState.status,
|
|
954
|
+
dependenciesMet: this.arePhaseDependenciesMet(phaseId)
|
|
955
|
+
};
|
|
956
|
+
});
|
|
957
|
+
|
|
958
|
+
const completedCount = phases.filter(p => p.status === PHASE_STATUS.COMPLETED).length;
|
|
959
|
+
const requiredCount = phases.filter(p => p.required).length;
|
|
960
|
+
const requiredCompleted = phases.filter(p => p.required && p.status === PHASE_STATUS.COMPLETED).length;
|
|
961
|
+
|
|
962
|
+
return {
|
|
963
|
+
currentPhase: this.state.currentPhase,
|
|
964
|
+
startedAt: this.state.startedAt,
|
|
965
|
+
lastUpdated: this.state.lastUpdated,
|
|
966
|
+
phases,
|
|
967
|
+
overall: {
|
|
968
|
+
completed: completedCount,
|
|
969
|
+
total: phases.length,
|
|
970
|
+
percentage: Math.round((completedCount / phases.length) * 100)
|
|
971
|
+
},
|
|
972
|
+
required: {
|
|
973
|
+
completed: requiredCompleted,
|
|
974
|
+
total: requiredCount,
|
|
975
|
+
percentage: Math.round((requiredCompleted / requiredCount) * 100)
|
|
976
|
+
},
|
|
977
|
+
isComplete: requiredCompleted === requiredCount,
|
|
978
|
+
detection: this.state.detection
|
|
979
|
+
};
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
/**
|
|
983
|
+
* Get resume point
|
|
984
|
+
*/
|
|
985
|
+
getResumePoint() {
|
|
986
|
+
if (!this.state || !this.state.currentPhase) {
|
|
987
|
+
return null;
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
const phase = ONBOARD_PHASES[this.state.currentPhase];
|
|
991
|
+
const phaseState = this.state.phases[this.state.currentPhase];
|
|
992
|
+
|
|
993
|
+
return {
|
|
994
|
+
phase: this.state.currentPhase,
|
|
995
|
+
phaseName: phase?.name,
|
|
996
|
+
phaseStatus: phaseState?.status,
|
|
997
|
+
lastUpdated: this.state.lastUpdated
|
|
998
|
+
};
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
module.exports = {
|
|
1003
|
+
OnboardWorkflowEngine,
|
|
1004
|
+
ONBOARD_PHASES,
|
|
1005
|
+
PHASE_STATUS,
|
|
1006
|
+
STACK_DETECTION
|
|
1007
|
+
};
|