@girardmedia/bootspring 2.2.0 → 2.2.1
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/bin/bootspring.js +127 -73
- package/claude-commands/agent.md +34 -0
- package/claude-commands/bs.md +31 -0
- package/claude-commands/build.md +25 -0
- package/claude-commands/skill.md +31 -0
- package/claude-commands/todo.md +25 -0
- package/dist/core/index.d.ts +5814 -0
- package/dist/core.js +5779 -0
- package/dist/index.js +93883 -0
- package/dist/mcp/index.d.ts +1 -0
- package/dist/mcp-server.js +2298 -0
- package/package.json +22 -55
- package/core/api-client.d.ts +0 -69
- package/core/api-client.js +0 -1482
- package/core/auth.d.ts +0 -98
- package/core/auth.js +0 -737
- package/core/build-orchestrator.js +0 -508
- package/core/build-state.js +0 -612
- package/core/config.d.ts +0 -106
- package/core/config.js +0 -1328
- package/core/context-loader.js +0 -580
- package/core/context.d.ts +0 -61
- package/core/context.js +0 -327
- package/core/entitlements.d.ts +0 -70
- package/core/entitlements.js +0 -322
- package/core/index.d.ts +0 -53
- package/core/index.js +0 -62
- package/core/mcp-config.js +0 -115
- package/core/policies.d.ts +0 -43
- package/core/policies.js +0 -113
- package/core/policy-matrix.js +0 -303
- package/core/project-activity.js +0 -175
- package/core/redaction.d.ts +0 -5
- package/core/redaction.js +0 -63
- package/core/self-update.js +0 -259
- package/core/session.js +0 -353
- package/core/task-extractor.js +0 -1098
- package/core/telemetry.d.ts +0 -55
- package/core/telemetry.js +0 -617
- package/core/tier-enforcement.js +0 -928
- package/core/utils.d.ts +0 -90
- package/core/utils.js +0 -455
- package/core/validation.js +0 -572
- package/mcp/server.d.ts +0 -57
- package/mcp/server.js +0 -264
|
@@ -1,508 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Bootspring Build Orchestrator
|
|
3
|
-
*
|
|
4
|
-
* Main orchestration engine for the autonomous build loop.
|
|
5
|
-
* Coordinates between seed documents, task extraction, planning,
|
|
6
|
-
* and the continuous build loop.
|
|
7
|
-
*
|
|
8
|
-
* @package bootspring
|
|
9
|
-
* @module core/build-orchestrator
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
const fs = require('fs');
|
|
13
|
-
const path = require('path');
|
|
14
|
-
const buildState = require('./build-state');
|
|
15
|
-
const taskExtractor = require('./task-extractor');
|
|
16
|
-
const planningTemplate = require('../generators/templates/build-planning.template');
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Detect if the project root contains an existing codebase
|
|
20
|
-
*/
|
|
21
|
-
function isExistingCodebase(projectRoot) {
|
|
22
|
-
const indicators = [
|
|
23
|
-
'package.json', 'Cargo.toml', 'go.mod', 'requirements.txt',
|
|
24
|
-
'pyproject.toml', 'Gemfile', 'pom.xml', 'build.gradle', 'composer.json', '.git'
|
|
25
|
-
];
|
|
26
|
-
const srcDirs = ['src', 'lib', 'app', 'pages', 'components'];
|
|
27
|
-
|
|
28
|
-
for (const indicator of indicators) {
|
|
29
|
-
if (fs.existsSync(path.join(projectRoot, indicator))) return true;
|
|
30
|
-
}
|
|
31
|
-
for (const dir of srcDirs) {
|
|
32
|
-
const dirPath = path.join(projectRoot, dir);
|
|
33
|
-
if (fs.existsSync(dirPath) && fs.statSync(dirPath).isDirectory()) return true;
|
|
34
|
-
}
|
|
35
|
-
return false;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Get the appropriate preseed command suggestion
|
|
40
|
-
*/
|
|
41
|
-
function getPreseedSuggestion(projectRoot) {
|
|
42
|
-
return isExistingCodebase(projectRoot) ? 'bootspring preseed codebase' : 'bootspring preseed start';
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Build Orchestrator Class
|
|
47
|
-
*/
|
|
48
|
-
class BuildOrchestrator {
|
|
49
|
-
/**
|
|
50
|
-
* Create a new orchestrator instance
|
|
51
|
-
* @param {string} projectRoot - Project root path
|
|
52
|
-
* @param {object} options - Options
|
|
53
|
-
*/
|
|
54
|
-
constructor(projectRoot, options = {}) {
|
|
55
|
-
this.projectRoot = projectRoot;
|
|
56
|
-
this.options = options;
|
|
57
|
-
this.state = null;
|
|
58
|
-
this.docs = {};
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Initialize the build orchestrator
|
|
63
|
-
* Parses docs, extracts tasks, generates planning folder, initializes state
|
|
64
|
-
* @returns {object} Initialization result
|
|
65
|
-
*/
|
|
66
|
-
async initialize() {
|
|
67
|
-
const result = {
|
|
68
|
-
success: false,
|
|
69
|
-
state: null,
|
|
70
|
-
files: {},
|
|
71
|
-
taskCount: 0,
|
|
72
|
-
error: null
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
try {
|
|
76
|
-
// Step 1: Load seed documents
|
|
77
|
-
this.docs = this.loadSeedDocuments();
|
|
78
|
-
|
|
79
|
-
if (Object.keys(this.docs).length === 0) {
|
|
80
|
-
const suggestion = getPreseedSuggestion(this.projectRoot);
|
|
81
|
-
result.error = `No seed documents found. Run "${suggestion}" first.`;
|
|
82
|
-
return result;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// Step 2: Extract project name
|
|
86
|
-
const projectName = this.extractProjectName();
|
|
87
|
-
|
|
88
|
-
// Step 3: Extract tasks from documents
|
|
89
|
-
const extracted = taskExtractor.extractFromDocs(this.docs, {
|
|
90
|
-
projectName
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
if (extracted.tasks.length === 0) {
|
|
94
|
-
result.error = 'No tasks could be extracted from seed documents.';
|
|
95
|
-
return result;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// Step 4: Initialize build state
|
|
99
|
-
this.state = buildState.initialize(this.projectRoot, {
|
|
100
|
-
projectName,
|
|
101
|
-
preseedDocs: Object.keys(this.docs),
|
|
102
|
-
seedSource: this.getSeedSource()
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
// Step 5: Set tasks in state
|
|
106
|
-
buildState.setTasks(this.projectRoot, extracted.tasks);
|
|
107
|
-
|
|
108
|
-
// Step 6: Set MVP features/criteria
|
|
109
|
-
if (extracted.mvpCriteria.length > 0) {
|
|
110
|
-
buildState.setMvpFeatures(this.projectRoot, extracted.mvpCriteria);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// Step 7: Generate planning artifacts
|
|
114
|
-
result.files = planningTemplate.generateAll(
|
|
115
|
-
this.projectRoot,
|
|
116
|
-
this.docs,
|
|
117
|
-
extracted.tasks,
|
|
118
|
-
{
|
|
119
|
-
projectName,
|
|
120
|
-
currentPhase: 'foundation'
|
|
121
|
-
}
|
|
122
|
-
);
|
|
123
|
-
|
|
124
|
-
// Step 8: Update state with planning info
|
|
125
|
-
this.state = buildState.load(this.projectRoot);
|
|
126
|
-
this.state.status = 'pending';
|
|
127
|
-
this.state.currentPhase = 'foundation';
|
|
128
|
-
buildState.save(this.projectRoot, this.state);
|
|
129
|
-
|
|
130
|
-
result.success = true;
|
|
131
|
-
result.state = this.state;
|
|
132
|
-
result.taskCount = extracted.tasks.length;
|
|
133
|
-
|
|
134
|
-
return result;
|
|
135
|
-
|
|
136
|
-
} catch (error) {
|
|
137
|
-
result.error = error.message;
|
|
138
|
-
return result;
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* Load seed documents from preseed directory and SEED.md
|
|
144
|
-
* @returns {object} Document contents
|
|
145
|
-
*/
|
|
146
|
-
loadSeedDocuments() {
|
|
147
|
-
const docs = {};
|
|
148
|
-
|
|
149
|
-
// Load from preseed directory
|
|
150
|
-
const preseedDir = path.join(this.projectRoot, '.bootspring', 'preseed');
|
|
151
|
-
if (fs.existsSync(preseedDir)) {
|
|
152
|
-
const validDocs = [
|
|
153
|
-
'VISION.md', 'AUDIENCE.md', 'MARKET.md', 'COMPETITORS.md',
|
|
154
|
-
'BUSINESS_MODEL.md', 'PRD.md', 'TECHNICAL_SPEC.md', 'ROADMAP.md'
|
|
155
|
-
];
|
|
156
|
-
|
|
157
|
-
for (const file of validDocs) {
|
|
158
|
-
const filePath = path.join(preseedDir, file);
|
|
159
|
-
if (fs.existsSync(filePath)) {
|
|
160
|
-
const docName = file.replace('.md', '');
|
|
161
|
-
docs[docName] = fs.readFileSync(filePath, 'utf-8');
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
// Load SEED.md if exists
|
|
167
|
-
const seedPath = path.join(this.projectRoot, 'SEED.md');
|
|
168
|
-
if (fs.existsSync(seedPath)) {
|
|
169
|
-
docs.SEED = fs.readFileSync(seedPath, 'utf-8');
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
return docs;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
/**
|
|
176
|
-
* Get the seed source type
|
|
177
|
-
* @returns {string} Source type
|
|
178
|
-
*/
|
|
179
|
-
getSeedSource() {
|
|
180
|
-
const hasPreseed = fs.existsSync(path.join(this.projectRoot, '.bootspring', 'preseed'));
|
|
181
|
-
const hasSeed = fs.existsSync(path.join(this.projectRoot, 'SEED.md'));
|
|
182
|
-
|
|
183
|
-
if (hasPreseed && hasSeed) return 'preseed+seed';
|
|
184
|
-
if (hasPreseed) return 'preseed';
|
|
185
|
-
if (hasSeed) return 'seed';
|
|
186
|
-
return 'none';
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
/**
|
|
190
|
-
* Extract project name from documents
|
|
191
|
-
* @returns {string} Project name
|
|
192
|
-
*/
|
|
193
|
-
extractProjectName() {
|
|
194
|
-
// Try VISION first
|
|
195
|
-
const visionDoc = this.docs.VISION || this.docs.vision;
|
|
196
|
-
if (visionDoc) {
|
|
197
|
-
const nameMatch = visionDoc.match(/\*{0,2}Application\s+Name:?\*{0,2}\s*(.+)/i);
|
|
198
|
-
if (nameMatch) return nameMatch[1].trim();
|
|
199
|
-
|
|
200
|
-
const titleMatch = visionDoc.match(/^#\s+([^—\-\n]+)/m);
|
|
201
|
-
if (titleMatch) return titleMatch[1].trim();
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
// Try SEED
|
|
205
|
-
const seedDoc = this.docs.SEED || this.docs.seed;
|
|
206
|
-
if (seedDoc) {
|
|
207
|
-
const titleMatch = seedDoc.match(/^#\s+([^—\-\n]+)/m);
|
|
208
|
-
if (titleMatch) return titleMatch[1].trim();
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
// Try PRD
|
|
212
|
-
const prdDoc = this.docs.PRD || this.docs.prd;
|
|
213
|
-
if (prdDoc) {
|
|
214
|
-
const titleMatch = prdDoc.match(/^#\s+([^—\-\n]+)/m);
|
|
215
|
-
if (titleMatch) return titleMatch[1].trim();
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
return 'Project';
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
/**
|
|
222
|
-
* Get the next task to execute
|
|
223
|
-
* @returns {object|null} Next task or null
|
|
224
|
-
*/
|
|
225
|
-
getNextTask() {
|
|
226
|
-
return buildState.getNextTask(this.projectRoot);
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
/**
|
|
230
|
-
* Get all tasks for the loop
|
|
231
|
-
* @returns {array} Tasks formatted for loop execution
|
|
232
|
-
*/
|
|
233
|
-
getTasksForLoop() {
|
|
234
|
-
const state = buildState.load(this.projectRoot);
|
|
235
|
-
if (!state) return [];
|
|
236
|
-
|
|
237
|
-
return state.implementationQueue.map(task => ({
|
|
238
|
-
id: task.id,
|
|
239
|
-
title: task.title,
|
|
240
|
-
description: task.description || '',
|
|
241
|
-
status: task.status,
|
|
242
|
-
acceptance: task.acceptanceCriteria || [],
|
|
243
|
-
phase: task.phase,
|
|
244
|
-
source: task.source
|
|
245
|
-
}));
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
/**
|
|
249
|
-
* Complete a task
|
|
250
|
-
* @param {string} taskId - Task ID
|
|
251
|
-
* @param {object} details - Completion details
|
|
252
|
-
* @returns {object|null} Updated state
|
|
253
|
-
*/
|
|
254
|
-
completeTask(taskId, details = {}) {
|
|
255
|
-
return buildState.updateProgress(this.projectRoot, taskId, 'completed', {
|
|
256
|
-
completedAt: new Date().toISOString(),
|
|
257
|
-
...details
|
|
258
|
-
});
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
/**
|
|
262
|
-
* Mark task as blocked
|
|
263
|
-
* @param {string} taskId - Task ID
|
|
264
|
-
* @param {string} reason - Block reason
|
|
265
|
-
* @returns {object|null} Updated state
|
|
266
|
-
*/
|
|
267
|
-
blockTask(taskId, reason) {
|
|
268
|
-
return buildState.updateProgress(this.projectRoot, taskId, 'blocked', {
|
|
269
|
-
error: reason
|
|
270
|
-
});
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
/**
|
|
274
|
-
* Skip a task
|
|
275
|
-
* @param {string} taskId - Task ID
|
|
276
|
-
* @param {string} reason - Skip reason
|
|
277
|
-
* @returns {object|null} Updated state
|
|
278
|
-
*/
|
|
279
|
-
skipTask(taskId, reason) {
|
|
280
|
-
return buildState.updateProgress(this.projectRoot, taskId, 'skipped', {
|
|
281
|
-
error: reason
|
|
282
|
-
});
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
/**
|
|
286
|
-
* Start a task
|
|
287
|
-
* @param {string} taskId - Task ID
|
|
288
|
-
* @returns {object|null} Updated state
|
|
289
|
-
*/
|
|
290
|
-
startTask(taskId) {
|
|
291
|
-
return buildState.updateProgress(this.projectRoot, taskId, 'in_progress');
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
/**
|
|
295
|
-
* Check if MVP criteria are met
|
|
296
|
-
* @returns {boolean} Whether MVP is complete
|
|
297
|
-
*/
|
|
298
|
-
isMvpComplete() {
|
|
299
|
-
const state = buildState.load(this.projectRoot);
|
|
300
|
-
if (!state) return false;
|
|
301
|
-
|
|
302
|
-
return state.mvpCriteria.allCriteriaMet;
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
/**
|
|
306
|
-
* Check if should exit the loop
|
|
307
|
-
* @returns {object} Exit check result
|
|
308
|
-
*/
|
|
309
|
-
shouldExit() {
|
|
310
|
-
const state = buildState.load(this.projectRoot);
|
|
311
|
-
if (!state) return { shouldExit: true, reason: 'no_state' };
|
|
312
|
-
|
|
313
|
-
// Check MVP completion
|
|
314
|
-
if (this.isMvpComplete()) {
|
|
315
|
-
return { shouldExit: true, reason: 'mvp_complete' };
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
// Check max iterations
|
|
319
|
-
if (state.loopSession.currentIteration >= state.loopSession.maxIterations) {
|
|
320
|
-
return { shouldExit: true, reason: 'max_iterations' };
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
// Check if all tasks are done
|
|
324
|
-
const pendingTasks = state.implementationQueue.filter(
|
|
325
|
-
t => t.status === 'pending' || t.status === 'in_progress'
|
|
326
|
-
);
|
|
327
|
-
if (pendingTasks.length === 0) {
|
|
328
|
-
return { shouldExit: true, reason: 'all_tasks_complete' };
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
// Check if paused
|
|
332
|
-
if (state.status === 'paused') {
|
|
333
|
-
return { shouldExit: true, reason: 'paused' };
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
// Check if failed
|
|
337
|
-
if (state.status === 'failed') {
|
|
338
|
-
return { shouldExit: true, reason: 'failed' };
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
return { shouldExit: false };
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
/**
|
|
345
|
-
* Generate a prompt for a specific task
|
|
346
|
-
* @param {object} task - Task to generate prompt for
|
|
347
|
-
* @returns {string} Task prompt
|
|
348
|
-
*/
|
|
349
|
-
generateTaskPrompt(task) {
|
|
350
|
-
const state = buildState.load(this.projectRoot);
|
|
351
|
-
const projectName = state?.projectName || 'Project';
|
|
352
|
-
|
|
353
|
-
let prompt = `# Bootspring Build Task
|
|
354
|
-
|
|
355
|
-
You are executing a single task from the autonomous build loop.
|
|
356
|
-
|
|
357
|
-
## Project: ${projectName}
|
|
358
|
-
|
|
359
|
-
## Current Task
|
|
360
|
-
|
|
361
|
-
**ID:** ${task.id}
|
|
362
|
-
**Title:** ${task.title}
|
|
363
|
-
${task.description ? `**Description:** ${task.description}` : ''}
|
|
364
|
-
**Phase:** ${task.phase || 'MVP'}
|
|
365
|
-
**Source:** ${task.source || 'Manual'} ${task.sourceSection ? `(${task.sourceSection})` : ''}
|
|
366
|
-
**Complexity:** ${task.estimatedComplexity || 'medium'}
|
|
367
|
-
|
|
368
|
-
`;
|
|
369
|
-
|
|
370
|
-
if (task.acceptanceCriteria && task.acceptanceCriteria.length > 0) {
|
|
371
|
-
prompt += `## Acceptance Criteria
|
|
372
|
-
|
|
373
|
-
${task.acceptanceCriteria.map(c => `- [ ] ${c}`).join('\n')}
|
|
374
|
-
|
|
375
|
-
`;
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
prompt += `## Rules
|
|
379
|
-
|
|
380
|
-
1. **Focus on THIS task only** - Do not work on other tasks
|
|
381
|
-
2. **Read CLAUDE.md first** - Understand project patterns
|
|
382
|
-
3. **Run quality checks** - \`npm run lint && npm run test\`
|
|
383
|
-
4. **Commit when done** - Use conventional commits (feat:, fix:, etc.)
|
|
384
|
-
5. **Update BUILD_STATE.json** - Mark task as completed
|
|
385
|
-
|
|
386
|
-
## Exit Signals
|
|
387
|
-
|
|
388
|
-
When you complete the task successfully, output:
|
|
389
|
-
\`\`\`
|
|
390
|
-
<loop-status>TASK_COMPLETE</loop-status>
|
|
391
|
-
\`\`\`
|
|
392
|
-
|
|
393
|
-
If you cannot complete the task, output:
|
|
394
|
-
\`\`\`
|
|
395
|
-
<loop-status>TASK_BLOCKED</loop-status>
|
|
396
|
-
Reason: [explanation]
|
|
397
|
-
\`\`\`
|
|
398
|
-
|
|
399
|
-
## Session Info
|
|
400
|
-
|
|
401
|
-
- Session: ${state?.loopSession?.sessionId || 'N/A'}
|
|
402
|
-
- Iteration: ${(state?.loopSession?.currentIteration || 0) + 1} of ${state?.loopSession?.maxIterations || 50}
|
|
403
|
-
- Timestamp: ${new Date().toISOString()}
|
|
404
|
-
|
|
405
|
-
## Context Files
|
|
406
|
-
|
|
407
|
-
Read these files for context:
|
|
408
|
-
- \`CLAUDE.md\` - Project patterns and decisions
|
|
409
|
-
- \`planning/CONTEXT.md\` - Build context
|
|
410
|
-
- \`planning/BUILD_STATE.json\` - Current state
|
|
411
|
-
|
|
412
|
-
Begin working on the task now.
|
|
413
|
-
`;
|
|
414
|
-
|
|
415
|
-
return prompt;
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
/**
|
|
419
|
-
* Get build statistics
|
|
420
|
-
* @returns {object} Build statistics
|
|
421
|
-
*/
|
|
422
|
-
getStats() {
|
|
423
|
-
return buildState.getStats(this.projectRoot);
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
/**
|
|
427
|
-
* Pause the build loop
|
|
428
|
-
* @returns {object|null} Updated state
|
|
429
|
-
*/
|
|
430
|
-
pause() {
|
|
431
|
-
return buildState.pause(this.projectRoot);
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
/**
|
|
435
|
-
* Resume the build loop
|
|
436
|
-
* @returns {object|null} Updated state
|
|
437
|
-
*/
|
|
438
|
-
resume() {
|
|
439
|
-
return buildState.resume(this.projectRoot);
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
/**
|
|
443
|
-
* Mark build as complete
|
|
444
|
-
* @returns {object|null} Updated state
|
|
445
|
-
*/
|
|
446
|
-
complete() {
|
|
447
|
-
return buildState.complete(this.projectRoot);
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
/**
|
|
451
|
-
* Mark build as failed
|
|
452
|
-
* @param {string} reason - Failure reason
|
|
453
|
-
* @returns {object|null} Updated state
|
|
454
|
-
*/
|
|
455
|
-
fail(reason) {
|
|
456
|
-
return buildState.fail(this.projectRoot, reason);
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
/**
|
|
460
|
-
* Reset the build state
|
|
461
|
-
* @returns {object} Fresh state
|
|
462
|
-
*/
|
|
463
|
-
reset() {
|
|
464
|
-
return buildState.reset(this.projectRoot);
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
/**
|
|
468
|
-
* Update planning files from current state
|
|
469
|
-
*/
|
|
470
|
-
updatePlanningFiles() {
|
|
471
|
-
const state = buildState.load(this.projectRoot);
|
|
472
|
-
if (state) {
|
|
473
|
-
planningTemplate.updateFromState(this.projectRoot, state);
|
|
474
|
-
}
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
/**
|
|
478
|
-
* Check if build state exists
|
|
479
|
-
* @returns {boolean}
|
|
480
|
-
*/
|
|
481
|
-
hasState() {
|
|
482
|
-
return buildState.exists(this.projectRoot);
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
/**
|
|
486
|
-
* Load existing state
|
|
487
|
-
* @returns {object|null}
|
|
488
|
-
*/
|
|
489
|
-
loadState() {
|
|
490
|
-
this.state = buildState.load(this.projectRoot);
|
|
491
|
-
return this.state;
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
/**
|
|
496
|
-
* Create a new build orchestrator
|
|
497
|
-
* @param {string} projectRoot - Project root path
|
|
498
|
-
* @param {object} options - Options
|
|
499
|
-
* @returns {BuildOrchestrator}
|
|
500
|
-
*/
|
|
501
|
-
function create(projectRoot, options = {}) {
|
|
502
|
-
return new BuildOrchestrator(projectRoot, options);
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
module.exports = {
|
|
506
|
-
BuildOrchestrator,
|
|
507
|
-
create
|
|
508
|
-
};
|