@girardmedia/bootspring 2.0.36 → 2.0.37

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.
@@ -0,0 +1,772 @@
1
+ /**
2
+ * Bootspring Feature Decomposer
3
+ *
4
+ * Intelligently breaks down features into layered implementation tasks
5
+ * with effort estimates, dependencies, critical path analysis, and risks.
6
+ *
7
+ * @package bootspring
8
+ * @module core/planning/feature-decomposer
9
+ */
10
+
11
+ const fs = require('fs');
12
+ const path = require('path');
13
+
14
+ /**
15
+ * Feature patterns for intelligent decomposition
16
+ */
17
+ const FEATURE_PATTERNS = {
18
+ auth: {
19
+ keywords: ['auth', 'login', 'register', 'signup', 'signin', 'password', 'session', 'jwt', 'oauth'],
20
+ database: [
21
+ { task: 'Create User model', effort: 'small', deps: [] },
22
+ { task: 'Create Session/Account model', effort: 'small', deps: ['User model'] },
23
+ { task: 'Add authentication indexes', effort: 'small', deps: ['User model'] }
24
+ ],
25
+ backend: [
26
+ { task: 'Configure auth provider', effort: 'medium', deps: [] },
27
+ { task: 'Create auth service layer', effort: 'medium', deps: ['User model'] },
28
+ { task: 'Create auth middleware', effort: 'small', deps: ['Auth service'] },
29
+ { task: 'Create login endpoint', effort: 'medium', deps: ['Auth service'] },
30
+ { task: 'Create register endpoint', effort: 'medium', deps: ['Auth service'] },
31
+ { task: 'Create logout endpoint', effort: 'small', deps: ['Auth service'] },
32
+ { task: 'Implement session management', effort: 'medium', deps: ['Session model'] }
33
+ ],
34
+ frontend: [
35
+ { task: 'Create login form component', effort: 'medium', deps: [] },
36
+ { task: 'Create register form component', effort: 'medium', deps: [] },
37
+ { task: 'Create auth context/provider', effort: 'medium', deps: ['Auth endpoints'] },
38
+ { task: 'Create protected route wrapper', effort: 'small', deps: ['Auth context'] },
39
+ { task: 'Add auth state persistence', effort: 'small', deps: ['Auth context'] }
40
+ ],
41
+ testing: [
42
+ { task: 'Unit tests for auth service', effort: 'medium', deps: ['Auth service'] },
43
+ { task: 'Integration tests for auth endpoints', effort: 'medium', deps: ['Auth endpoints'] },
44
+ { task: 'E2E tests for login flow', effort: 'large', deps: ['Login form', 'Login endpoint'] }
45
+ ],
46
+ risks: [
47
+ { risk: 'Session security vulnerabilities', severity: 'high', mitigation: 'Use httpOnly cookies, implement token rotation' },
48
+ { risk: 'Password storage', severity: 'high', mitigation: 'Use bcrypt with proper cost factor (12+)' },
49
+ { risk: 'Brute force attacks', severity: 'medium', mitigation: 'Implement rate limiting and account lockout' }
50
+ ],
51
+ patterns: ['auth/nextauth', 'auth/jwt', 'security/password-hashing', 'security/rate-limiting']
52
+ },
53
+
54
+ payment: {
55
+ keywords: ['payment', 'billing', 'stripe', 'subscription', 'checkout', 'invoice', 'pricing'],
56
+ database: [
57
+ { task: 'Create Subscription model', effort: 'small', deps: ['User model'] },
58
+ { task: 'Create Payment/Transaction model', effort: 'small', deps: ['User model'] },
59
+ { task: 'Create Invoice model', effort: 'small', deps: ['User model'] },
60
+ { task: 'Create Plan/Pricing model', effort: 'small', deps: [] }
61
+ ],
62
+ backend: [
63
+ { task: 'Configure Stripe SDK', effort: 'small', deps: [] },
64
+ { task: 'Create payment service layer', effort: 'large', deps: ['Subscription model'] },
65
+ { task: 'Create checkout session endpoint', effort: 'medium', deps: ['Payment service'] },
66
+ { task: 'Create webhook handler', effort: 'large', deps: ['Payment service'] },
67
+ { task: 'Create subscription management endpoints', effort: 'medium', deps: ['Payment service'] },
68
+ { task: 'Create billing portal endpoint', effort: 'small', deps: ['Payment service'] }
69
+ ],
70
+ frontend: [
71
+ { task: 'Create pricing page', effort: 'medium', deps: [] },
72
+ { task: 'Create checkout component', effort: 'medium', deps: ['Checkout endpoint'] },
73
+ { task: 'Create subscription management UI', effort: 'medium', deps: ['Subscription endpoints'] },
74
+ { task: 'Create billing history component', effort: 'medium', deps: ['Invoice model'] }
75
+ ],
76
+ testing: [
77
+ { task: 'Unit tests for payment service', effort: 'medium', deps: ['Payment service'] },
78
+ { task: 'Webhook handler tests', effort: 'medium', deps: ['Webhook handler'] },
79
+ { task: 'E2E checkout flow tests', effort: 'large', deps: ['Checkout component'] }
80
+ ],
81
+ risks: [
82
+ { risk: 'PCI compliance', severity: 'high', mitigation: 'Use Stripe Elements, never handle raw card data' },
83
+ { risk: 'Webhook reliability', severity: 'medium', mitigation: 'Implement idempotency, verify signatures' },
84
+ { risk: 'Subscription state sync', severity: 'medium', mitigation: 'Rely on webhooks as source of truth' }
85
+ ],
86
+ patterns: ['payment/stripe', 'payment/webhooks', 'billing/subscriptions']
87
+ },
88
+
89
+ api: {
90
+ keywords: ['api', 'endpoint', 'rest', 'graphql', 'crud', 'resource'],
91
+ database: [
92
+ { task: 'Create resource model', effort: 'small', deps: [] },
93
+ { task: 'Add model relationships', effort: 'small', deps: ['Resource model'] },
94
+ { task: 'Add indexes for queries', effort: 'small', deps: ['Resource model'] }
95
+ ],
96
+ backend: [
97
+ { task: 'Create validation schemas', effort: 'small', deps: [] },
98
+ { task: 'Create service layer', effort: 'medium', deps: ['Resource model'] },
99
+ { task: 'Create GET endpoint', effort: 'small', deps: ['Service layer'] },
100
+ { task: 'Create POST endpoint', effort: 'small', deps: ['Service layer', 'Validation'] },
101
+ { task: 'Create PUT endpoint', effort: 'small', deps: ['Service layer', 'Validation'] },
102
+ { task: 'Create DELETE endpoint', effort: 'small', deps: ['Service layer'] },
103
+ { task: 'Add pagination support', effort: 'small', deps: ['GET endpoint'] },
104
+ { task: 'Add filtering support', effort: 'small', deps: ['GET endpoint'] }
105
+ ],
106
+ frontend: [
107
+ { task: 'Create API client functions', effort: 'small', deps: ['API endpoints'] },
108
+ { task: 'Create data display component', effort: 'medium', deps: ['API client'] },
109
+ { task: 'Create form component', effort: 'medium', deps: ['API client'] }
110
+ ],
111
+ testing: [
112
+ { task: 'Unit tests for service layer', effort: 'medium', deps: ['Service layer'] },
113
+ { task: 'Integration tests for endpoints', effort: 'medium', deps: ['API endpoints'] },
114
+ { task: 'Validation tests', effort: 'small', deps: ['Validation schemas'] }
115
+ ],
116
+ risks: [
117
+ { risk: 'Input validation', severity: 'high', mitigation: 'Use Zod/Yup for all inputs' },
118
+ { risk: 'Authorization bypass', severity: 'high', mitigation: 'Check ownership on all mutations' },
119
+ { risk: 'N+1 queries', severity: 'medium', mitigation: 'Use eager loading, add query limits' }
120
+ ],
121
+ patterns: ['api/rest-crud', 'api/validation', 'api/pagination']
122
+ },
123
+
124
+ dashboard: {
125
+ keywords: ['dashboard', 'admin', 'analytics', 'metrics', 'charts', 'overview'],
126
+ database: [
127
+ { task: 'Create analytics/metrics models', effort: 'small', deps: [] },
128
+ { task: 'Add aggregation queries', effort: 'medium', deps: ['Metrics models'] }
129
+ ],
130
+ backend: [
131
+ { task: 'Create analytics service', effort: 'medium', deps: ['Metrics models'] },
132
+ { task: 'Create dashboard data endpoint', effort: 'medium', deps: ['Analytics service'] },
133
+ { task: 'Add data aggregation logic', effort: 'medium', deps: ['Analytics service'] },
134
+ { task: 'Add caching for metrics', effort: 'small', deps: ['Analytics service'] }
135
+ ],
136
+ frontend: [
137
+ { task: 'Create dashboard layout', effort: 'medium', deps: [] },
138
+ { task: 'Create metric cards/widgets', effort: 'medium', deps: ['Dashboard layout'] },
139
+ { task: 'Create charts/visualizations', effort: 'large', deps: ['Dashboard data endpoint'] },
140
+ { task: 'Add date range filtering', effort: 'small', deps: ['Dashboard layout'] },
141
+ { task: 'Add real-time updates', effort: 'medium', deps: ['Dashboard data endpoint'] }
142
+ ],
143
+ testing: [
144
+ { task: 'Unit tests for analytics service', effort: 'medium', deps: ['Analytics service'] },
145
+ { task: 'Component tests for widgets', effort: 'medium', deps: ['Widgets'] },
146
+ { task: 'E2E tests for dashboard', effort: 'large', deps: ['Dashboard'] }
147
+ ],
148
+ risks: [
149
+ { risk: 'Performance with large datasets', severity: 'medium', mitigation: 'Pre-aggregate data, add caching' },
150
+ { risk: 'Real-time update complexity', severity: 'low', mitigation: 'Start with polling, upgrade to WebSocket later' }
151
+ ],
152
+ patterns: ['dashboard/metrics', 'frontend/charts', 'caching/redis']
153
+ },
154
+
155
+ upload: {
156
+ keywords: ['upload', 'file', 'image', 'media', 'storage', 's3', 'cloudinary'],
157
+ database: [
158
+ { task: 'Create File/Media model', effort: 'small', deps: [] },
159
+ { task: 'Add file metadata fields', effort: 'small', deps: ['File model'] }
160
+ ],
161
+ backend: [
162
+ { task: 'Configure storage provider', effort: 'small', deps: [] },
163
+ { task: 'Create upload service', effort: 'medium', deps: ['File model', 'Storage provider'] },
164
+ { task: 'Create upload endpoint', effort: 'medium', deps: ['Upload service'] },
165
+ { task: 'Create delete endpoint', effort: 'small', deps: ['Upload service'] },
166
+ { task: 'Add file validation', effort: 'small', deps: ['Upload endpoint'] },
167
+ { task: 'Add image processing', effort: 'medium', deps: ['Upload service'] }
168
+ ],
169
+ frontend: [
170
+ { task: 'Create file upload component', effort: 'medium', deps: [] },
171
+ { task: 'Add drag-and-drop support', effort: 'small', deps: ['Upload component'] },
172
+ { task: 'Add progress indicator', effort: 'small', deps: ['Upload component'] },
173
+ { task: 'Create file preview component', effort: 'small', deps: ['Upload component'] }
174
+ ],
175
+ testing: [
176
+ { task: 'Unit tests for upload service', effort: 'medium', deps: ['Upload service'] },
177
+ { task: 'Integration tests with mock storage', effort: 'medium', deps: ['Upload endpoint'] },
178
+ { task: 'File validation tests', effort: 'small', deps: ['File validation'] }
179
+ ],
180
+ risks: [
181
+ { risk: 'Malicious file uploads', severity: 'high', mitigation: 'Validate file types, scan for malware' },
182
+ { risk: 'Storage costs', severity: 'medium', mitigation: 'Set size limits, implement cleanup' },
183
+ { risk: 'Large file handling', severity: 'medium', mitigation: 'Use chunked uploads, streaming' }
184
+ ],
185
+ patterns: ['upload/s3', 'upload/cloudinary', 'security/file-validation']
186
+ },
187
+
188
+ notification: {
189
+ keywords: ['notification', 'email', 'push', 'alert', 'messaging', 'sms'],
190
+ database: [
191
+ { task: 'Create Notification model', effort: 'small', deps: ['User model'] },
192
+ { task: 'Create NotificationPreference model', effort: 'small', deps: ['User model'] }
193
+ ],
194
+ backend: [
195
+ { task: 'Configure email provider', effort: 'small', deps: [] },
196
+ { task: 'Create notification service', effort: 'medium', deps: ['Notification model'] },
197
+ { task: 'Create email templates', effort: 'medium', deps: ['Email provider'] },
198
+ { task: 'Create push notification service', effort: 'medium', deps: ['Notification service'] },
199
+ { task: 'Create notification preferences endpoint', effort: 'small', deps: ['Preference model'] },
200
+ { task: 'Add notification queue', effort: 'medium', deps: ['Notification service'] }
201
+ ],
202
+ frontend: [
203
+ { task: 'Create notification bell component', effort: 'small', deps: [] },
204
+ { task: 'Create notification dropdown/panel', effort: 'medium', deps: ['Notification endpoint'] },
205
+ { task: 'Create preferences settings UI', effort: 'medium', deps: ['Preferences endpoint'] },
206
+ { task: 'Add real-time notification updates', effort: 'medium', deps: ['Notification panel'] }
207
+ ],
208
+ testing: [
209
+ { task: 'Unit tests for notification service', effort: 'medium', deps: ['Notification service'] },
210
+ { task: 'Email template tests', effort: 'small', deps: ['Email templates'] },
211
+ { task: 'Integration tests', effort: 'medium', deps: ['Notification endpoints'] }
212
+ ],
213
+ risks: [
214
+ { risk: 'Email deliverability', severity: 'medium', mitigation: 'Use reputable provider, set up SPF/DKIM' },
215
+ { risk: 'Notification spam', severity: 'medium', mitigation: 'Respect preferences, add rate limiting' }
216
+ ],
217
+ patterns: ['notification/email', 'notification/push', 'messaging/queues']
218
+ },
219
+
220
+ search: {
221
+ keywords: ['search', 'filter', 'query', 'elasticsearch', 'algolia', 'fulltext'],
222
+ database: [
223
+ { task: 'Add search indexes', effort: 'small', deps: [] },
224
+ { task: 'Configure full-text search', effort: 'medium', deps: [] }
225
+ ],
226
+ backend: [
227
+ { task: 'Create search service', effort: 'medium', deps: ['Search indexes'] },
228
+ { task: 'Create search endpoint', effort: 'medium', deps: ['Search service'] },
229
+ { task: 'Add filtering capabilities', effort: 'medium', deps: ['Search endpoint'] },
230
+ { task: 'Add faceted search', effort: 'medium', deps: ['Search service'] },
231
+ { task: 'Implement search suggestions', effort: 'medium', deps: ['Search service'] }
232
+ ],
233
+ frontend: [
234
+ { task: 'Create search input component', effort: 'small', deps: [] },
235
+ { task: 'Create search results component', effort: 'medium', deps: ['Search endpoint'] },
236
+ { task: 'Add autocomplete/suggestions', effort: 'medium', deps: ['Suggestions endpoint'] },
237
+ { task: 'Create filter sidebar', effort: 'medium', deps: ['Filtering'] }
238
+ ],
239
+ testing: [
240
+ { task: 'Unit tests for search service', effort: 'medium', deps: ['Search service'] },
241
+ { task: 'Search accuracy tests', effort: 'medium', deps: ['Search endpoint'] },
242
+ { task: 'Performance tests', effort: 'medium', deps: ['Search endpoint'] }
243
+ ],
244
+ risks: [
245
+ { risk: 'Search performance', severity: 'medium', mitigation: 'Use dedicated search engine for scale' },
246
+ { risk: 'Index synchronization', severity: 'medium', mitigation: 'Use event-driven indexing' }
247
+ ],
248
+ patterns: ['search/fulltext', 'search/elasticsearch', 'search/algolia']
249
+ }
250
+ };
251
+
252
+ /**
253
+ * Effort mappings to time estimates
254
+ */
255
+ const EFFORT_ESTIMATES = {
256
+ small: { time: '30min-1h', points: 1 },
257
+ medium: { time: '1-3h', points: 3 },
258
+ large: { time: '3-6h', points: 5 },
259
+ xlarge: { time: '6-12h', points: 8 }
260
+ };
261
+
262
+ /**
263
+ * Feature Decomposer class
264
+ */
265
+ class FeatureDecomposer {
266
+ constructor(projectRoot) {
267
+ this.projectRoot = projectRoot;
268
+ }
269
+
270
+ /**
271
+ * Decompose a feature into implementation tasks
272
+ */
273
+ async decompose(feature, options = {}) {
274
+ const { depth = 'standard', includePatterns = true } = options;
275
+
276
+ // Classify the feature
277
+ const classification = this.classifyFeature(feature);
278
+
279
+ // Get base decomposition
280
+ let decomposition;
281
+ if (classification.pattern) {
282
+ decomposition = this.decomposeFromPattern(feature, classification);
283
+ } else {
284
+ decomposition = this.decomposeGeneric(feature);
285
+ }
286
+
287
+ // Analyze project context
288
+ const projectContext = await this.analyzeProjectContext();
289
+
290
+ // Adjust for project context
291
+ decomposition = this.adjustForContext(decomposition, projectContext);
292
+
293
+ // Calculate metrics
294
+ const metrics = this.calculateMetrics(decomposition);
295
+
296
+ // Generate critical path
297
+ const criticalPath = this.calculateCriticalPath(decomposition);
298
+
299
+ // Find recommended patterns
300
+ const patterns = includePatterns
301
+ ? await this.findRecommendedPatterns(feature, classification)
302
+ : [];
303
+
304
+ return {
305
+ feature,
306
+ classification: classification.type,
307
+ confidence: classification.confidence,
308
+ complexity: metrics.complexity,
309
+ estimatedEffort: metrics.totalEffort,
310
+ estimatedPoints: metrics.totalPoints,
311
+
312
+ layers: decomposition.layers,
313
+ risks: decomposition.risks,
314
+ criticalPath,
315
+
316
+ metrics: {
317
+ totalTasks: metrics.totalTasks,
318
+ byLayer: metrics.byLayer,
319
+ byEffort: metrics.byEffort
320
+ },
321
+
322
+ recommendedPatterns: patterns,
323
+ suggestedOrder: this.generateSuggestedOrder(decomposition),
324
+
325
+ // Deep analysis additions
326
+ ...(depth === 'deep' && {
327
+ dependencies: this.mapAllDependencies(decomposition),
328
+ parallelizableTasks: this.findParallelizable(decomposition),
329
+ blockers: this.identifyBlockers(decomposition, projectContext)
330
+ })
331
+ };
332
+ }
333
+
334
+ /**
335
+ * Classify the feature type
336
+ */
337
+ classifyFeature(feature) {
338
+ const featureLower = feature.toLowerCase();
339
+ let bestMatch = null;
340
+ let bestScore = 0;
341
+
342
+ for (const [patternName, pattern] of Object.entries(FEATURE_PATTERNS)) {
343
+ let score = 0;
344
+ for (const keyword of pattern.keywords) {
345
+ if (featureLower.includes(keyword)) {
346
+ score += keyword.length; // Longer matches = higher score
347
+ }
348
+ }
349
+
350
+ if (score > bestScore) {
351
+ bestScore = score;
352
+ bestMatch = patternName;
353
+ }
354
+ }
355
+
356
+ if (bestMatch && bestScore > 0) {
357
+ return {
358
+ type: bestMatch,
359
+ pattern: FEATURE_PATTERNS[bestMatch],
360
+ confidence: Math.min(bestScore / 10, 1)
361
+ };
362
+ }
363
+
364
+ return {
365
+ type: 'generic',
366
+ pattern: null,
367
+ confidence: 0
368
+ };
369
+ }
370
+
371
+ /**
372
+ * Decompose from a known pattern
373
+ */
374
+ decomposeFromPattern(feature, classification) {
375
+ const pattern = classification.pattern;
376
+
377
+ return {
378
+ layers: {
379
+ database: [...pattern.database],
380
+ backend: [...pattern.backend],
381
+ frontend: [...pattern.frontend],
382
+ testing: [...pattern.testing],
383
+ documentation: [
384
+ { task: 'Update API documentation', effort: 'small', deps: ['API endpoints'] },
385
+ { task: 'Update user guide', effort: 'small', deps: ['Frontend'] }
386
+ ]
387
+ },
388
+ risks: [...pattern.risks]
389
+ };
390
+ }
391
+
392
+ /**
393
+ * Generic decomposition for unknown features
394
+ */
395
+ decomposeGeneric(feature) {
396
+ // Extract likely entity name
397
+ const entityName = this.extractEntityName(feature);
398
+
399
+ return {
400
+ layers: {
401
+ database: [
402
+ { task: `Create ${entityName} model`, effort: 'small', deps: [] },
403
+ { task: 'Add model relationships', effort: 'small', deps: [`${entityName} model`] },
404
+ { task: 'Add database indexes', effort: 'small', deps: [`${entityName} model`] }
405
+ ],
406
+ backend: [
407
+ { task: 'Create validation schema', effort: 'small', deps: [] },
408
+ { task: `Create ${entityName} service`, effort: 'medium', deps: [`${entityName} model`] },
409
+ { task: 'Create API endpoints', effort: 'medium', deps: [`${entityName} service`] },
410
+ { task: 'Add business logic', effort: 'medium', deps: [`${entityName} service`] }
411
+ ],
412
+ frontend: [
413
+ { task: `Create ${entityName} list component`, effort: 'medium', deps: [] },
414
+ { task: `Create ${entityName} form component`, effort: 'medium', deps: [] },
415
+ { task: `Create ${entityName} detail component`, effort: 'medium', deps: [] },
416
+ { task: 'Connect to API', effort: 'small', deps: ['API endpoints'] }
417
+ ],
418
+ testing: [
419
+ { task: 'Unit tests for service', effort: 'medium', deps: [`${entityName} service`] },
420
+ { task: 'API integration tests', effort: 'medium', deps: ['API endpoints'] },
421
+ { task: 'Component tests', effort: 'medium', deps: ['Components'] }
422
+ ],
423
+ documentation: [
424
+ { task: 'Document API endpoints', effort: 'small', deps: ['API endpoints'] }
425
+ ]
426
+ },
427
+ risks: [
428
+ { risk: 'Scope creep', severity: 'medium', mitigation: 'Define clear acceptance criteria' },
429
+ { risk: 'Integration complexity', severity: 'low', mitigation: 'Build incrementally with tests' }
430
+ ]
431
+ };
432
+ }
433
+
434
+ /**
435
+ * Extract entity name from feature description
436
+ */
437
+ extractEntityName(feature) {
438
+ const words = feature.split(/\s+/);
439
+
440
+ // Look for capitalized words or nouns
441
+ for (const word of words) {
442
+ if (/^[A-Z][a-z]+$/.test(word)) {
443
+ return word;
444
+ }
445
+ }
446
+
447
+ // Fall back to first significant word
448
+ const stopWords = ['add', 'create', 'implement', 'build', 'make', 'the', 'a', 'an', 'for', 'to', 'with'];
449
+ for (const word of words) {
450
+ if (!stopWords.includes(word.toLowerCase()) && word.length > 2) {
451
+ return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
452
+ }
453
+ }
454
+
455
+ return 'Entity';
456
+ }
457
+
458
+ /**
459
+ * Analyze project context
460
+ */
461
+ async analyzeProjectContext() {
462
+ const context = {
463
+ hasDatabase: false,
464
+ databaseType: null,
465
+ hasAuth: false,
466
+ hasTests: false,
467
+ framework: null
468
+ };
469
+
470
+ try {
471
+ // Check for Prisma
472
+ if (fs.existsSync(path.join(this.projectRoot, 'prisma', 'schema.prisma'))) {
473
+ context.hasDatabase = true;
474
+ context.databaseType = 'prisma';
475
+ }
476
+
477
+ // Check for auth
478
+ const authPaths = ['app/api/auth', 'pages/api/auth', 'src/lib/auth'];
479
+ for (const authPath of authPaths) {
480
+ if (fs.existsSync(path.join(this.projectRoot, authPath))) {
481
+ context.hasAuth = true;
482
+ break;
483
+ }
484
+ }
485
+
486
+ // Check for tests
487
+ const testPaths = ['__tests__', 'tests', 'test', 'spec'];
488
+ for (const testPath of testPaths) {
489
+ if (fs.existsSync(path.join(this.projectRoot, testPath))) {
490
+ context.hasTests = true;
491
+ break;
492
+ }
493
+ }
494
+
495
+ // Check for framework
496
+ const packageJsonPath = path.join(this.projectRoot, 'package.json');
497
+ if (fs.existsSync(packageJsonPath)) {
498
+ const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
499
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
500
+
501
+ if (deps.next) context.framework = 'nextjs';
502
+ else if (deps.react) context.framework = 'react';
503
+ else if (deps.vue) context.framework = 'vue';
504
+ else if (deps.express) context.framework = 'express';
505
+ }
506
+ } catch (_err) {
507
+ // Context analysis failed, use defaults
508
+ }
509
+
510
+ return context;
511
+ }
512
+
513
+ /**
514
+ * Adjust decomposition based on project context
515
+ */
516
+ adjustForContext(decomposition, context) {
517
+ const adjusted = JSON.parse(JSON.stringify(decomposition));
518
+
519
+ // If no database, skip database layer or add setup task
520
+ if (!context.hasDatabase && adjusted.layers.database.length > 0) {
521
+ adjusted.layers.database.unshift({
522
+ task: 'Set up database (Prisma)',
523
+ effort: 'medium',
524
+ deps: []
525
+ });
526
+ }
527
+
528
+ // If no auth but feature needs it, add auth dependency
529
+ if (!context.hasAuth) {
530
+ const needsAuth = adjusted.layers.backend.some(t =>
531
+ t.task.toLowerCase().includes('auth') ||
532
+ t.deps?.some(d => d.toLowerCase().includes('user'))
533
+ );
534
+
535
+ if (needsAuth) {
536
+ adjusted.risks.push({
537
+ risk: 'Authentication not set up',
538
+ severity: 'high',
539
+ mitigation: 'Implement authentication first or use bootspring preseed auth'
540
+ });
541
+ }
542
+ }
543
+
544
+ // If no tests, add test setup task
545
+ if (!context.hasTests && adjusted.layers.testing.length > 0) {
546
+ adjusted.layers.testing.unshift({
547
+ task: 'Set up testing framework',
548
+ effort: 'medium',
549
+ deps: []
550
+ });
551
+ }
552
+
553
+ return adjusted;
554
+ }
555
+
556
+ /**
557
+ * Calculate metrics for decomposition
558
+ */
559
+ calculateMetrics(decomposition) {
560
+ let totalTasks = 0;
561
+ let totalPoints = 0;
562
+ const byLayer = {};
563
+ const byEffort = { small: 0, medium: 0, large: 0, xlarge: 0 };
564
+
565
+ for (const [layer, tasks] of Object.entries(decomposition.layers)) {
566
+ byLayer[layer] = tasks.length;
567
+ totalTasks += tasks.length;
568
+
569
+ for (const task of tasks) {
570
+ const effort = task.effort || 'medium';
571
+ byEffort[effort] = (byEffort[effort] || 0) + 1;
572
+ totalPoints += EFFORT_ESTIMATES[effort]?.points || 3;
573
+ }
574
+ }
575
+
576
+ // Determine complexity
577
+ let complexity = 'low';
578
+ if (totalTasks > 15 || totalPoints > 40) complexity = 'high';
579
+ else if (totalTasks > 8 || totalPoints > 20) complexity = 'medium';
580
+
581
+ // Calculate total effort
582
+ const totalHours = totalPoints * 1.5; // Rough estimate
583
+ let totalEffort;
584
+ if (totalHours <= 8) totalEffort = '1 day';
585
+ else if (totalHours <= 24) totalEffort = '2-3 days';
586
+ else if (totalHours <= 40) totalEffort = '1 week';
587
+ else totalEffort = '2+ weeks';
588
+
589
+ return {
590
+ totalTasks,
591
+ totalPoints,
592
+ totalEffort,
593
+ complexity,
594
+ byLayer,
595
+ byEffort
596
+ };
597
+ }
598
+
599
+ /**
600
+ * Calculate critical path
601
+ */
602
+ calculateCriticalPath(decomposition) {
603
+ const allTasks = [];
604
+ const tasksByName = {};
605
+
606
+ // Flatten all tasks
607
+ for (const [layer, tasks] of Object.entries(decomposition.layers)) {
608
+ for (const task of tasks) {
609
+ const taskEntry = { ...task, layer };
610
+ allTasks.push(taskEntry);
611
+ tasksByName[task.task] = taskEntry;
612
+ }
613
+ }
614
+
615
+ // Find tasks with most dependencies leading to them
616
+ const criticalPath = [];
617
+
618
+ // Start with database layer
619
+ const dbTasks = decomposition.layers.database || [];
620
+ if (dbTasks.length > 0) {
621
+ criticalPath.push(dbTasks[0].task);
622
+ }
623
+
624
+ // Add backend service
625
+ const backendTasks = decomposition.layers.backend || [];
626
+ const serviceTasks = backendTasks.filter(t =>
627
+ t.task.toLowerCase().includes('service')
628
+ );
629
+ if (serviceTasks.length > 0) {
630
+ criticalPath.push(serviceTasks[0].task);
631
+ }
632
+
633
+ // Add API endpoints
634
+ const endpointTasks = backendTasks.filter(t =>
635
+ t.task.toLowerCase().includes('endpoint')
636
+ );
637
+ if (endpointTasks.length > 0) {
638
+ criticalPath.push('API endpoints');
639
+ }
640
+
641
+ // Add frontend integration
642
+ const frontendTasks = decomposition.layers.frontend || [];
643
+ if (frontendTasks.length > 0) {
644
+ criticalPath.push('Frontend integration');
645
+ }
646
+
647
+ return criticalPath;
648
+ }
649
+
650
+ /**
651
+ * Find recommended patterns from skills library
652
+ */
653
+ async findRecommendedPatterns(feature, classification) {
654
+ const patterns = [];
655
+
656
+ if (classification.pattern?.patterns) {
657
+ patterns.push(...classification.pattern.patterns);
658
+ }
659
+
660
+ // Try to load patterns from skills library
661
+ try {
662
+ const skillsPath = path.join(this.projectRoot, 'node_modules', '@girardmedia', 'bootspring', 'skills', 'patterns');
663
+ if (fs.existsSync(skillsPath)) {
664
+ const catalogPath = path.join(skillsPath, 'catalog.json');
665
+ if (fs.existsSync(catalogPath)) {
666
+ const catalog = JSON.parse(fs.readFileSync(catalogPath, 'utf8'));
667
+ // Match patterns based on feature keywords
668
+ // This is a simplified version
669
+ }
670
+ }
671
+ } catch (_err) {
672
+ // Skills lookup failed, use defaults
673
+ }
674
+
675
+ return [...new Set(patterns)];
676
+ }
677
+
678
+ /**
679
+ * Generate suggested implementation order
680
+ */
681
+ generateSuggestedOrder(decomposition) {
682
+ return [
683
+ '1. Database models and migrations',
684
+ '2. Backend service layer',
685
+ '3. API endpoints with validation',
686
+ '4. Frontend components',
687
+ '5. Connect frontend to API',
688
+ '6. Testing',
689
+ '7. Documentation'
690
+ ];
691
+ }
692
+
693
+ /**
694
+ * Map all dependencies (for deep analysis)
695
+ */
696
+ mapAllDependencies(decomposition) {
697
+ const deps = {};
698
+
699
+ for (const [layer, tasks] of Object.entries(decomposition.layers)) {
700
+ for (const task of tasks) {
701
+ deps[task.task] = {
702
+ layer,
703
+ effort: task.effort,
704
+ dependsOn: task.deps || [],
705
+ blockedBy: []
706
+ };
707
+ }
708
+ }
709
+
710
+ // Calculate blockedBy (reverse dependencies)
711
+ for (const [taskName, taskDeps] of Object.entries(deps)) {
712
+ for (const dep of taskDeps.dependsOn) {
713
+ if (deps[dep]) {
714
+ deps[dep].blockedBy.push(taskName);
715
+ }
716
+ }
717
+ }
718
+
719
+ return deps;
720
+ }
721
+
722
+ /**
723
+ * Find parallelizable tasks
724
+ */
725
+ findParallelizable(decomposition) {
726
+ const parallelGroups = [];
727
+
728
+ // Tasks with no dependencies can run in parallel
729
+ for (const [layer, tasks] of Object.entries(decomposition.layers)) {
730
+ const noDeps = tasks.filter(t => !t.deps || t.deps.length === 0);
731
+ if (noDeps.length > 1) {
732
+ parallelGroups.push({
733
+ layer,
734
+ tasks: noDeps.map(t => t.task)
735
+ });
736
+ }
737
+ }
738
+
739
+ return parallelGroups;
740
+ }
741
+
742
+ /**
743
+ * Identify blockers
744
+ */
745
+ identifyBlockers(decomposition, context) {
746
+ const blockers = [];
747
+
748
+ if (!context.hasDatabase) {
749
+ blockers.push({
750
+ type: 'infrastructure',
751
+ blocker: 'Database not configured',
752
+ resolution: 'Run: npx prisma init'
753
+ });
754
+ }
755
+
756
+ if (!context.hasAuth && decomposition.risks.some(r => r.risk.toLowerCase().includes('auth'))) {
757
+ blockers.push({
758
+ type: 'feature',
759
+ blocker: 'Authentication required',
760
+ resolution: 'Implement auth or use bootspring preseed auth'
761
+ });
762
+ }
763
+
764
+ return blockers;
765
+ }
766
+ }
767
+
768
+ module.exports = {
769
+ FeatureDecomposer,
770
+ FEATURE_PATTERNS,
771
+ EFFORT_ESTIMATES
772
+ };