@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.
- package/cli/plan.js +602 -2
- package/core/planning/adaptive-engine.js +958 -0
- package/core/planning/feature-decomposer.js +772 -0
- package/core/planning/index.js +49 -0
- package/core/planning/simulator.js +1328 -0
- package/core/planning/stage-planner.js +624 -0
- package/intelligence/agent-router.js +795 -0
- package/intelligence/index.js +10 -0
- package/mcp/contracts/mcp-contract.v1.json +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,624 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bootspring Stage Planner
|
|
3
|
+
*
|
|
4
|
+
* Stage-aware planning for different project phases:
|
|
5
|
+
* - Discovery: New projects, pivots, major features
|
|
6
|
+
* - Definition: Ready to build, defining scope
|
|
7
|
+
* - Execution: During build, sprint planning
|
|
8
|
+
* - Iteration: Post-MVP, feedback incorporation
|
|
9
|
+
* - Scale: Growth phase, technical debt
|
|
10
|
+
*
|
|
11
|
+
* @package bootspring
|
|
12
|
+
* @module core/planning/stage-planner
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const fs = require('fs');
|
|
16
|
+
const path = require('path');
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Planning stages with their characteristics
|
|
20
|
+
*/
|
|
21
|
+
const PLANNING_STAGES = {
|
|
22
|
+
discovery: {
|
|
23
|
+
name: 'Discovery',
|
|
24
|
+
description: 'Understanding the problem space and opportunity',
|
|
25
|
+
triggers: ['new project', 'pivot', 'major feature', 'exploration'],
|
|
26
|
+
|
|
27
|
+
documents: ['vision', 'audience', 'market', 'competitors'],
|
|
28
|
+
|
|
29
|
+
questions: [
|
|
30
|
+
'What problem does this solve?',
|
|
31
|
+
'Who experiences this problem?',
|
|
32
|
+
'Why hasn\'t this been solved before?',
|
|
33
|
+
'What makes now the right time?',
|
|
34
|
+
'What is our unique advantage?',
|
|
35
|
+
'How big is the opportunity?'
|
|
36
|
+
],
|
|
37
|
+
|
|
38
|
+
activities: [
|
|
39
|
+
'User research and interviews',
|
|
40
|
+
'Market analysis',
|
|
41
|
+
'Competitive landscape review',
|
|
42
|
+
'Technical feasibility assessment',
|
|
43
|
+
'Risk identification'
|
|
44
|
+
],
|
|
45
|
+
|
|
46
|
+
outputs: {
|
|
47
|
+
vision: 'VISION.md - Core problem, solution, and success metrics',
|
|
48
|
+
audience: 'AUDIENCE.md - User personas and needs',
|
|
49
|
+
market: 'MARKET.md - Market size and opportunity',
|
|
50
|
+
competitors: 'COMPETITORS.md - Competitive analysis'
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
analysisDepth: 'shallow',
|
|
54
|
+
aiPromptStyle: 'exploratory',
|
|
55
|
+
|
|
56
|
+
completionCriteria: [
|
|
57
|
+
'Problem clearly defined',
|
|
58
|
+
'Target audience identified',
|
|
59
|
+
'Market opportunity validated',
|
|
60
|
+
'Technical feasibility confirmed'
|
|
61
|
+
]
|
|
62
|
+
},
|
|
63
|
+
|
|
64
|
+
definition: {
|
|
65
|
+
name: 'Definition',
|
|
66
|
+
description: 'Defining what to build and how',
|
|
67
|
+
triggers: ['vision approved', 'ready to build', 'scope definition'],
|
|
68
|
+
|
|
69
|
+
documents: ['prd', 'technical-spec', 'business-model', 'roadmap'],
|
|
70
|
+
|
|
71
|
+
questions: [
|
|
72
|
+
'What is the minimum viable feature set?',
|
|
73
|
+
'What technology stack fits best?',
|
|
74
|
+
'How will this make money?',
|
|
75
|
+
'What are the key milestones?',
|
|
76
|
+
'What are the critical risks?',
|
|
77
|
+
'What resources are needed?'
|
|
78
|
+
],
|
|
79
|
+
|
|
80
|
+
activities: [
|
|
81
|
+
'Feature prioritization',
|
|
82
|
+
'Architecture design',
|
|
83
|
+
'Tech stack selection',
|
|
84
|
+
'Business model development',
|
|
85
|
+
'Roadmap creation',
|
|
86
|
+
'Resource planning'
|
|
87
|
+
],
|
|
88
|
+
|
|
89
|
+
outputs: {
|
|
90
|
+
prd: 'PRD.md - Product requirements and user stories',
|
|
91
|
+
technicalSpec: 'TECHNICAL_SPEC.md - Architecture and tech decisions',
|
|
92
|
+
businessModel: 'BUSINESS_MODEL.md - Revenue and growth strategy',
|
|
93
|
+
roadmap: 'ROADMAP.md - Milestones and timeline'
|
|
94
|
+
},
|
|
95
|
+
|
|
96
|
+
analysisDepth: 'deep',
|
|
97
|
+
aiPromptStyle: 'decisive',
|
|
98
|
+
|
|
99
|
+
completionCriteria: [
|
|
100
|
+
'MVP scope defined',
|
|
101
|
+
'Architecture documented',
|
|
102
|
+
'Tech stack chosen',
|
|
103
|
+
'Roadmap created'
|
|
104
|
+
]
|
|
105
|
+
},
|
|
106
|
+
|
|
107
|
+
execution: {
|
|
108
|
+
name: 'Execution',
|
|
109
|
+
description: 'Building the product',
|
|
110
|
+
triggers: ['development started', 'sprint planning', 'active build'],
|
|
111
|
+
|
|
112
|
+
documents: ['sprint', 'tasks', 'blockers', 'decisions'],
|
|
113
|
+
|
|
114
|
+
questions: [
|
|
115
|
+
'What should be built this sprint?',
|
|
116
|
+
'What are the blockers?',
|
|
117
|
+
'What is the critical path?',
|
|
118
|
+
'Are we on track for milestones?',
|
|
119
|
+
'What decisions need to be made?',
|
|
120
|
+
'What has changed since last sprint?'
|
|
121
|
+
],
|
|
122
|
+
|
|
123
|
+
activities: [
|
|
124
|
+
'Sprint planning',
|
|
125
|
+
'Daily standups',
|
|
126
|
+
'Code reviews',
|
|
127
|
+
'Technical decisions',
|
|
128
|
+
'Blocker resolution',
|
|
129
|
+
'Progress tracking'
|
|
130
|
+
],
|
|
131
|
+
|
|
132
|
+
outputs: {
|
|
133
|
+
sprint: 'SPRINT.md - Current sprint goals and tasks',
|
|
134
|
+
tasks: 'TASKS.md - Detailed task breakdown',
|
|
135
|
+
blockers: 'BLOCKERS.md - Current blockers and resolutions',
|
|
136
|
+
decisions: 'DECISIONS.md - Architectural decision records'
|
|
137
|
+
},
|
|
138
|
+
|
|
139
|
+
analysisDepth: 'focused',
|
|
140
|
+
aiPromptStyle: 'tactical',
|
|
141
|
+
|
|
142
|
+
completionCriteria: [
|
|
143
|
+
'Sprint goals defined',
|
|
144
|
+
'Tasks estimated',
|
|
145
|
+
'Dependencies mapped',
|
|
146
|
+
'Blockers identified'
|
|
147
|
+
]
|
|
148
|
+
},
|
|
149
|
+
|
|
150
|
+
iteration: {
|
|
151
|
+
name: 'Iteration',
|
|
152
|
+
description: 'Post-MVP refinement based on feedback',
|
|
153
|
+
triggers: ['mvp launched', 'feedback received', 'metrics review'],
|
|
154
|
+
|
|
155
|
+
documents: ['feedback', 'iteration', 'metrics', 'learnings'],
|
|
156
|
+
|
|
157
|
+
questions: [
|
|
158
|
+
'What feedback are we getting?',
|
|
159
|
+
'What metrics are we seeing?',
|
|
160
|
+
'What needs to change?',
|
|
161
|
+
'What\'s working well?',
|
|
162
|
+
'What should we double down on?',
|
|
163
|
+
'What should we cut?'
|
|
164
|
+
],
|
|
165
|
+
|
|
166
|
+
activities: [
|
|
167
|
+
'User feedback analysis',
|
|
168
|
+
'Metrics review',
|
|
169
|
+
'Feature prioritization update',
|
|
170
|
+
'Quick wins identification',
|
|
171
|
+
'Technical debt assessment',
|
|
172
|
+
'Growth experiments'
|
|
173
|
+
],
|
|
174
|
+
|
|
175
|
+
outputs: {
|
|
176
|
+
feedback: 'FEEDBACK.md - User feedback summary',
|
|
177
|
+
iteration: 'ITERATION.md - Planned changes',
|
|
178
|
+
metrics: 'METRICS.md - Key performance indicators',
|
|
179
|
+
learnings: 'LEARNINGS.md - What we\'ve learned'
|
|
180
|
+
},
|
|
181
|
+
|
|
182
|
+
analysisDepth: 'moderate',
|
|
183
|
+
aiPromptStyle: 'analytical',
|
|
184
|
+
|
|
185
|
+
completionCriteria: [
|
|
186
|
+
'Feedback analyzed',
|
|
187
|
+
'Metrics baseline established',
|
|
188
|
+
'Iteration priorities set',
|
|
189
|
+
'Success criteria updated'
|
|
190
|
+
]
|
|
191
|
+
},
|
|
192
|
+
|
|
193
|
+
scale: {
|
|
194
|
+
name: 'Scale',
|
|
195
|
+
description: 'Growth phase optimization',
|
|
196
|
+
triggers: ['growth phase', 'scaling issues', 'team growth'],
|
|
197
|
+
|
|
198
|
+
documents: ['scale', 'tech-debt', 'team', 'infrastructure'],
|
|
199
|
+
|
|
200
|
+
questions: [
|
|
201
|
+
'What\'s breaking under load?',
|
|
202
|
+
'What technical debt exists?',
|
|
203
|
+
'What team changes are needed?',
|
|
204
|
+
'What infrastructure updates are required?',
|
|
205
|
+
'What processes need improvement?',
|
|
206
|
+
'What are the next growth bottlenecks?'
|
|
207
|
+
],
|
|
208
|
+
|
|
209
|
+
activities: [
|
|
210
|
+
'Performance optimization',
|
|
211
|
+
'Technical debt cleanup',
|
|
212
|
+
'Team scaling',
|
|
213
|
+
'Infrastructure planning',
|
|
214
|
+
'Process improvement',
|
|
215
|
+
'Documentation update'
|
|
216
|
+
],
|
|
217
|
+
|
|
218
|
+
outputs: {
|
|
219
|
+
scale: 'SCALE.md - Scaling strategy',
|
|
220
|
+
techDebt: 'TECH_DEBT.md - Technical debt backlog',
|
|
221
|
+
team: 'TEAM.md - Team structure and needs',
|
|
222
|
+
infrastructure: 'INFRASTRUCTURE.md - Infrastructure plan'
|
|
223
|
+
},
|
|
224
|
+
|
|
225
|
+
analysisDepth: 'comprehensive',
|
|
226
|
+
aiPromptStyle: 'strategic',
|
|
227
|
+
|
|
228
|
+
completionCriteria: [
|
|
229
|
+
'Scaling bottlenecks identified',
|
|
230
|
+
'Technical debt cataloged',
|
|
231
|
+
'Team plan created',
|
|
232
|
+
'Infrastructure roadmap defined'
|
|
233
|
+
]
|
|
234
|
+
}
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Stage Planner class
|
|
239
|
+
*/
|
|
240
|
+
class StagePlanner {
|
|
241
|
+
constructor(projectRoot) {
|
|
242
|
+
this.projectRoot = projectRoot;
|
|
243
|
+
this.stateFile = path.join(projectRoot, '.bootspring', 'planning-state.json');
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Detect current planning stage
|
|
248
|
+
*/
|
|
249
|
+
async detectStage() {
|
|
250
|
+
const signals = await this.gatherStageSignals();
|
|
251
|
+
|
|
252
|
+
// Score each stage based on signals
|
|
253
|
+
const scores = {};
|
|
254
|
+
for (const [stageName, stage] of Object.entries(PLANNING_STAGES)) {
|
|
255
|
+
scores[stageName] = this.scoreStage(stageName, stage, signals);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Find highest scoring stage
|
|
259
|
+
let bestStage = 'discovery';
|
|
260
|
+
let bestScore = 0;
|
|
261
|
+
for (const [stage, score] of Object.entries(scores)) {
|
|
262
|
+
if (score > bestScore) {
|
|
263
|
+
bestScore = score;
|
|
264
|
+
bestStage = stage;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
return {
|
|
269
|
+
detectedStage: bestStage,
|
|
270
|
+
confidence: Math.min(bestScore / 10, 1),
|
|
271
|
+
signals,
|
|
272
|
+
scores,
|
|
273
|
+
recommendation: this.getStageRecommendation(bestStage, signals)
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Gather signals about project state
|
|
279
|
+
*/
|
|
280
|
+
async gatherStageSignals() {
|
|
281
|
+
const signals = {
|
|
282
|
+
hasVision: false,
|
|
283
|
+
hasPrd: false,
|
|
284
|
+
hasCode: false,
|
|
285
|
+
hasTests: false,
|
|
286
|
+
hasDeployment: false,
|
|
287
|
+
hasUsers: false,
|
|
288
|
+
hasMetrics: false,
|
|
289
|
+
codebaseSize: 'none',
|
|
290
|
+
gitHistory: 'none',
|
|
291
|
+
recentActivity: 'low'
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
try {
|
|
295
|
+
// Check for preseed documents
|
|
296
|
+
const preseedDir = path.join(this.projectRoot, '.bootspring', 'preseed');
|
|
297
|
+
if (fs.existsSync(preseedDir)) {
|
|
298
|
+
signals.hasVision = fs.existsSync(path.join(preseedDir, 'VISION.md'));
|
|
299
|
+
signals.hasPrd = fs.existsSync(path.join(preseedDir, 'PRD.md'));
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// Check for code
|
|
303
|
+
const srcPaths = ['src', 'app', 'pages', 'lib'];
|
|
304
|
+
for (const srcPath of srcPaths) {
|
|
305
|
+
if (fs.existsSync(path.join(this.projectRoot, srcPath))) {
|
|
306
|
+
signals.hasCode = true;
|
|
307
|
+
break;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// Check for tests
|
|
312
|
+
const testPaths = ['__tests__', 'tests', 'test', 'spec'];
|
|
313
|
+
for (const testPath of testPaths) {
|
|
314
|
+
if (fs.existsSync(path.join(this.projectRoot, testPath))) {
|
|
315
|
+
signals.hasTests = true;
|
|
316
|
+
break;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Check for deployment config
|
|
321
|
+
const deployPaths = ['vercel.json', 'fly.toml', 'railway.json', 'Dockerfile', '.github/workflows'];
|
|
322
|
+
for (const deployPath of deployPaths) {
|
|
323
|
+
if (fs.existsSync(path.join(this.projectRoot, deployPath))) {
|
|
324
|
+
signals.hasDeployment = true;
|
|
325
|
+
break;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// Estimate codebase size
|
|
330
|
+
const packageJson = path.join(this.projectRoot, 'package.json');
|
|
331
|
+
if (fs.existsSync(packageJson)) {
|
|
332
|
+
const pkg = JSON.parse(fs.readFileSync(packageJson, 'utf8'));
|
|
333
|
+
const depCount = Object.keys(pkg.dependencies || {}).length;
|
|
334
|
+
if (depCount > 20) signals.codebaseSize = 'large';
|
|
335
|
+
else if (depCount > 10) signals.codebaseSize = 'medium';
|
|
336
|
+
else if (depCount > 0) signals.codebaseSize = 'small';
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// Check git history
|
|
340
|
+
const gitDir = path.join(this.projectRoot, '.git');
|
|
341
|
+
if (fs.existsSync(gitDir)) {
|
|
342
|
+
signals.gitHistory = 'exists';
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
} catch (_err) {
|
|
346
|
+
// Signal gathering failed, use defaults
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
return signals;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Score a stage based on signals
|
|
354
|
+
*/
|
|
355
|
+
scoreStage(stageName, stage, signals) {
|
|
356
|
+
let score = 0;
|
|
357
|
+
|
|
358
|
+
switch (stageName) {
|
|
359
|
+
case 'discovery':
|
|
360
|
+
if (!signals.hasVision) score += 3;
|
|
361
|
+
if (!signals.hasPrd) score += 2;
|
|
362
|
+
if (!signals.hasCode || signals.codebaseSize === 'small') score += 2;
|
|
363
|
+
break;
|
|
364
|
+
|
|
365
|
+
case 'definition':
|
|
366
|
+
if (signals.hasVision && !signals.hasPrd) score += 4;
|
|
367
|
+
if (!signals.hasCode) score += 2;
|
|
368
|
+
if (signals.codebaseSize === 'small') score += 1;
|
|
369
|
+
break;
|
|
370
|
+
|
|
371
|
+
case 'execution':
|
|
372
|
+
if (signals.hasPrd && signals.hasCode) score += 4;
|
|
373
|
+
if (signals.codebaseSize === 'medium') score += 2;
|
|
374
|
+
if (signals.recentActivity === 'high') score += 2;
|
|
375
|
+
break;
|
|
376
|
+
|
|
377
|
+
case 'iteration':
|
|
378
|
+
if (signals.hasDeployment) score += 3;
|
|
379
|
+
if (signals.hasUsers) score += 3;
|
|
380
|
+
if (signals.codebaseSize === 'medium' || signals.codebaseSize === 'large') score += 2;
|
|
381
|
+
break;
|
|
382
|
+
|
|
383
|
+
case 'scale':
|
|
384
|
+
if (signals.hasUsers && signals.hasMetrics) score += 4;
|
|
385
|
+
if (signals.codebaseSize === 'large') score += 3;
|
|
386
|
+
if (signals.hasDeployment) score += 2;
|
|
387
|
+
break;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
return score;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* Get recommendation for a stage
|
|
395
|
+
*/
|
|
396
|
+
getStageRecommendation(stageName, signals) {
|
|
397
|
+
const stage = PLANNING_STAGES[stageName];
|
|
398
|
+
|
|
399
|
+
const missingDocs = [];
|
|
400
|
+
const preseedDir = path.join(this.projectRoot, '.bootspring', 'preseed');
|
|
401
|
+
|
|
402
|
+
for (const doc of stage.documents) {
|
|
403
|
+
const docPath = path.join(preseedDir, `${doc.toUpperCase()}.md`);
|
|
404
|
+
if (!fs.existsSync(docPath)) {
|
|
405
|
+
missingDocs.push(doc);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
return {
|
|
410
|
+
stage: stageName,
|
|
411
|
+
name: stage.name,
|
|
412
|
+
description: stage.description,
|
|
413
|
+
keyQuestions: stage.questions.slice(0, 3),
|
|
414
|
+
suggestedActivities: stage.activities.slice(0, 3),
|
|
415
|
+
missingDocuments: missingDocs,
|
|
416
|
+
nextSteps: this.getNextSteps(stageName, signals, missingDocs)
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
/**
|
|
421
|
+
* Get next steps for a stage
|
|
422
|
+
*/
|
|
423
|
+
getNextSteps(stageName, signals, missingDocs) {
|
|
424
|
+
const steps = [];
|
|
425
|
+
|
|
426
|
+
if (missingDocs.length > 0) {
|
|
427
|
+
steps.push(`Generate missing documents: ${missingDocs.join(', ')}`);
|
|
428
|
+
steps.push('Run: bootspring preseed from-codebase --deep');
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
switch (stageName) {
|
|
432
|
+
case 'discovery':
|
|
433
|
+
steps.push('Answer the discovery questions');
|
|
434
|
+
steps.push('Validate problem with potential users');
|
|
435
|
+
break;
|
|
436
|
+
|
|
437
|
+
case 'definition':
|
|
438
|
+
steps.push('Define MVP feature set');
|
|
439
|
+
steps.push('Choose technology stack');
|
|
440
|
+
steps.push('Create initial roadmap');
|
|
441
|
+
break;
|
|
442
|
+
|
|
443
|
+
case 'execution':
|
|
444
|
+
steps.push('Set up sprint board');
|
|
445
|
+
steps.push('Break down features into tasks');
|
|
446
|
+
steps.push('Identify blockers early');
|
|
447
|
+
break;
|
|
448
|
+
|
|
449
|
+
case 'iteration':
|
|
450
|
+
steps.push('Set up user feedback collection');
|
|
451
|
+
steps.push('Define key metrics');
|
|
452
|
+
steps.push('Plan A/B tests');
|
|
453
|
+
break;
|
|
454
|
+
|
|
455
|
+
case 'scale':
|
|
456
|
+
steps.push('Audit performance bottlenecks');
|
|
457
|
+
steps.push('Catalog technical debt');
|
|
458
|
+
steps.push('Plan infrastructure upgrades');
|
|
459
|
+
break;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
return steps;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* Get planning context for a stage
|
|
467
|
+
*/
|
|
468
|
+
async getStageContext(stageName) {
|
|
469
|
+
const stage = PLANNING_STAGES[stageName];
|
|
470
|
+
|
|
471
|
+
if (!stage) {
|
|
472
|
+
throw new Error(`Unknown stage: ${stageName}`);
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
return {
|
|
476
|
+
stage: stageName,
|
|
477
|
+
...stage,
|
|
478
|
+
currentState: await this.gatherStageSignals(),
|
|
479
|
+
existingDocuments: await this.findExistingDocuments(stage.documents)
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
/**
|
|
484
|
+
* Find existing documents for a stage
|
|
485
|
+
*/
|
|
486
|
+
async findExistingDocuments(documentTypes) {
|
|
487
|
+
const existing = {};
|
|
488
|
+
const preseedDir = path.join(this.projectRoot, '.bootspring', 'preseed');
|
|
489
|
+
|
|
490
|
+
for (const docType of documentTypes) {
|
|
491
|
+
const docPath = path.join(preseedDir, `${docType.toUpperCase()}.md`);
|
|
492
|
+
if (fs.existsSync(docPath)) {
|
|
493
|
+
existing[docType] = {
|
|
494
|
+
path: docPath,
|
|
495
|
+
exists: true,
|
|
496
|
+
lastModified: fs.statSync(docPath).mtime
|
|
497
|
+
};
|
|
498
|
+
} else {
|
|
499
|
+
existing[docType] = {
|
|
500
|
+
path: docPath,
|
|
501
|
+
exists: false
|
|
502
|
+
};
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
return existing;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
/**
|
|
510
|
+
* Generate stage-appropriate AI prompt
|
|
511
|
+
*/
|
|
512
|
+
generateAIPrompt(stageName, task) {
|
|
513
|
+
const stage = PLANNING_STAGES[stageName];
|
|
514
|
+
|
|
515
|
+
if (!stage) {
|
|
516
|
+
return this.generateGenericPrompt(task);
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
const promptStyles = {
|
|
520
|
+
exploratory: `You are helping with early-stage exploration. Be curious, ask questions, and help discover insights. Focus on understanding the problem space before jumping to solutions.`,
|
|
521
|
+
|
|
522
|
+
decisive: `You are helping make key decisions. Be structured, evaluate trade-offs clearly, and provide concrete recommendations. Help choose between options with clear rationale.`,
|
|
523
|
+
|
|
524
|
+
tactical: `You are helping with day-to-day execution. Be practical, focus on immediate next steps, and help remove blockers. Keep suggestions actionable and time-bound.`,
|
|
525
|
+
|
|
526
|
+
analytical: `You are helping analyze feedback and data. Be objective, look for patterns, and help prioritize based on evidence. Separate signal from noise.`,
|
|
527
|
+
|
|
528
|
+
strategic: `You are helping with long-term planning. Think about scalability, sustainability, and team growth. Balance quick wins with foundational investments.`
|
|
529
|
+
};
|
|
530
|
+
|
|
531
|
+
const stylePrompt = promptStyles[stage.aiPromptStyle] || promptStyles.tactical;
|
|
532
|
+
|
|
533
|
+
return `${stylePrompt}
|
|
534
|
+
|
|
535
|
+
Current project stage: ${stage.name}
|
|
536
|
+
Stage description: ${stage.description}
|
|
537
|
+
|
|
538
|
+
Key questions for this stage:
|
|
539
|
+
${stage.questions.map(q => `- ${q}`).join('\n')}
|
|
540
|
+
|
|
541
|
+
Task: ${task}
|
|
542
|
+
|
|
543
|
+
Consider the stage-appropriate depth (${stage.analysisDepth}) and focus on outputs relevant to this stage.`;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
/**
|
|
547
|
+
* Generate generic prompt
|
|
548
|
+
*/
|
|
549
|
+
generateGenericPrompt(task) {
|
|
550
|
+
return `Help with the following task: ${task}`;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
/**
|
|
554
|
+
* Save planning state
|
|
555
|
+
*/
|
|
556
|
+
async saveState(state) {
|
|
557
|
+
const dir = path.dirname(this.stateFile);
|
|
558
|
+
if (!fs.existsSync(dir)) {
|
|
559
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
fs.writeFileSync(this.stateFile, JSON.stringify(state, null, 2));
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
/**
|
|
566
|
+
* Load planning state
|
|
567
|
+
*/
|
|
568
|
+
loadState() {
|
|
569
|
+
if (fs.existsSync(this.stateFile)) {
|
|
570
|
+
return JSON.parse(fs.readFileSync(this.stateFile, 'utf8'));
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
return {
|
|
574
|
+
currentStage: null,
|
|
575
|
+
stageHistory: [],
|
|
576
|
+
lastUpdated: null
|
|
577
|
+
};
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
/**
|
|
581
|
+
* Transition to a new stage
|
|
582
|
+
*/
|
|
583
|
+
async transitionTo(newStage, reason) {
|
|
584
|
+
const state = this.loadState();
|
|
585
|
+
|
|
586
|
+
if (state.currentStage) {
|
|
587
|
+
state.stageHistory.push({
|
|
588
|
+
stage: state.currentStage,
|
|
589
|
+
exitedAt: new Date().toISOString(),
|
|
590
|
+
reason
|
|
591
|
+
});
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
state.currentStage = newStage;
|
|
595
|
+
state.lastUpdated = new Date().toISOString();
|
|
596
|
+
|
|
597
|
+
await this.saveState(state);
|
|
598
|
+
|
|
599
|
+
return {
|
|
600
|
+
previousStage: state.stageHistory[state.stageHistory.length - 1]?.stage,
|
|
601
|
+
currentStage: newStage,
|
|
602
|
+
stageInfo: PLANNING_STAGES[newStage],
|
|
603
|
+
nextSteps: this.getNextSteps(newStage, await this.gatherStageSignals(), [])
|
|
604
|
+
};
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
/**
|
|
608
|
+
* Get all stages info
|
|
609
|
+
*/
|
|
610
|
+
getAllStages() {
|
|
611
|
+
return Object.entries(PLANNING_STAGES).map(([key, stage]) => ({
|
|
612
|
+
id: key,
|
|
613
|
+
name: stage.name,
|
|
614
|
+
description: stage.description,
|
|
615
|
+
documents: stage.documents,
|
|
616
|
+
triggers: stage.triggers
|
|
617
|
+
}));
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
module.exports = {
|
|
622
|
+
StagePlanner,
|
|
623
|
+
PLANNING_STAGES
|
|
624
|
+
};
|