@girardmedia/bootspring 2.1.3 → 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.
Files changed (65) hide show
  1. package/bin/bootspring.js +157 -83
  2. package/claude-commands/agent.md +34 -0
  3. package/claude-commands/bs.md +31 -0
  4. package/claude-commands/build.md +25 -0
  5. package/claude-commands/skill.md +31 -0
  6. package/claude-commands/todo.md +25 -0
  7. package/dist/core/index.d.ts +5814 -0
  8. package/dist/core.js +5779 -0
  9. package/dist/index.js +93883 -0
  10. package/dist/mcp/index.d.ts +1 -0
  11. package/dist/mcp-server.js +2298 -0
  12. package/generators/api-docs.js +3 -3
  13. package/generators/decisions.js +14 -14
  14. package/generators/health.js +6 -6
  15. package/generators/sprint.js +4 -4
  16. package/generators/templates/build-planning.template.js +2 -2
  17. package/generators/visual-doc-generator.js +1 -1
  18. package/package.json +22 -68
  19. package/cli/agent.js +0 -799
  20. package/cli/auth.js +0 -896
  21. package/cli/billing.js +0 -320
  22. package/cli/build.js +0 -1442
  23. package/cli/dashboard.js +0 -123
  24. package/cli/init.js +0 -669
  25. package/cli/mcp.js +0 -240
  26. package/cli/orchestrator.js +0 -240
  27. package/cli/project.js +0 -825
  28. package/cli/quality.js +0 -281
  29. package/cli/skill.js +0 -503
  30. package/cli/switch.js +0 -453
  31. package/cli/todo.js +0 -629
  32. package/cli/update.js +0 -132
  33. package/core/api-client.d.ts +0 -69
  34. package/core/api-client.js +0 -1482
  35. package/core/auth.d.ts +0 -98
  36. package/core/auth.js +0 -737
  37. package/core/build-orchestrator.js +0 -508
  38. package/core/build-state.js +0 -612
  39. package/core/config.d.ts +0 -106
  40. package/core/config.js +0 -1328
  41. package/core/context-loader.js +0 -580
  42. package/core/context.d.ts +0 -61
  43. package/core/context.js +0 -327
  44. package/core/entitlements.d.ts +0 -70
  45. package/core/entitlements.js +0 -322
  46. package/core/index.d.ts +0 -53
  47. package/core/index.js +0 -62
  48. package/core/mcp-config.js +0 -115
  49. package/core/policies.d.ts +0 -43
  50. package/core/policies.js +0 -113
  51. package/core/policy-matrix.js +0 -303
  52. package/core/project-activity.js +0 -175
  53. package/core/redaction.d.ts +0 -5
  54. package/core/redaction.js +0 -63
  55. package/core/self-update.js +0 -259
  56. package/core/session.js +0 -353
  57. package/core/task-extractor.js +0 -1098
  58. package/core/telemetry.d.ts +0 -55
  59. package/core/telemetry.js +0 -617
  60. package/core/tier-enforcement.js +0 -928
  61. package/core/utils.d.ts +0 -90
  62. package/core/utils.js +0 -455
  63. package/core/validation.js +0 -572
  64. package/mcp/server.d.ts +0 -57
  65. package/mcp/server.js +0 -264
@@ -1,612 +0,0 @@
1
- /**
2
- * Bootspring Build State Management
3
- *
4
- * Manages BUILD_STATE.json for continuous build loop persistence.
5
- * Tracks phases, implementation queue, MVP criteria, and loop session.
6
- *
7
- * @package bootspring
8
- * @module core/build-state
9
- */
10
-
11
- const fs = require('fs');
12
- const path = require('path');
13
-
14
- const BUILD_STATE_FILE = 'BUILD_STATE.json';
15
- const PLANNING_DIR = 'planning';
16
-
17
- /**
18
- * Default build state schema
19
- */
20
- function getDefaultState(projectName = 'Project') {
21
- return {
22
- version: '1.0.0',
23
- projectName,
24
- status: 'pending', // pending, in_progress, paused, completed, failed
25
- currentPhase: null,
26
-
27
- phases: {
28
- foundation: { status: 'pending', tasks: [] },
29
- mvp: { status: 'pending', tasks: [] },
30
- launch: { status: 'pending', tasks: [] }
31
- },
32
-
33
- implementationQueue: [],
34
-
35
- mvpCriteria: {
36
- features: [],
37
- completionPercentage: 0,
38
- allCriteriaMet: false
39
- },
40
-
41
- loopSession: {
42
- sessionId: null,
43
- currentIteration: 0,
44
- maxIterations: 50,
45
- startedAt: null,
46
- lastUpdated: null,
47
- pausedAt: null
48
- },
49
-
50
- metadata: {
51
- createdAt: new Date().toISOString(),
52
- updatedAt: new Date().toISOString(),
53
- seedSource: null,
54
- preseedDocs: []
55
- }
56
- };
57
- }
58
-
59
- /**
60
- * Get the planning directory path
61
- * @param {string} projectRoot - Project root path
62
- * @returns {string} Planning directory path
63
- */
64
- function getPlanningDir(projectRoot) {
65
- return path.join(projectRoot, PLANNING_DIR);
66
- }
67
-
68
- /**
69
- * Get the build state file path
70
- * @param {string} projectRoot - Project root path
71
- * @returns {string} Build state file path
72
- */
73
- function getStatePath(projectRoot) {
74
- return path.join(getPlanningDir(projectRoot), BUILD_STATE_FILE);
75
- }
76
-
77
- /**
78
- * Check if build state exists
79
- * @param {string} projectRoot - Project root path
80
- * @returns {boolean} Whether build state exists
81
- */
82
- function exists(projectRoot) {
83
- return fs.existsSync(getStatePath(projectRoot));
84
- }
85
-
86
- /**
87
- * Initialize a new build state
88
- * @param {string} projectRoot - Project root path
89
- * @param {object} config - Initial configuration
90
- * @returns {object} Initialized build state
91
- */
92
- function initialize(projectRoot, config = {}) {
93
- const planningDir = getPlanningDir(projectRoot);
94
-
95
- // Create planning directory if it doesn't exist
96
- if (!fs.existsSync(planningDir)) {
97
- fs.mkdirSync(planningDir, { recursive: true });
98
- }
99
-
100
- const state = getDefaultState(config.projectName || 'Project');
101
-
102
- // Apply config overrides
103
- if (config.phases) {
104
- state.phases = { ...state.phases, ...config.phases };
105
- }
106
-
107
- if (config.implementationQueue) {
108
- state.implementationQueue = config.implementationQueue;
109
- }
110
-
111
- if (config.mvpCriteria) {
112
- state.mvpCriteria = { ...state.mvpCriteria, ...config.mvpCriteria };
113
- }
114
-
115
- if (config.maxIterations) {
116
- state.loopSession.maxIterations = config.maxIterations;
117
- }
118
-
119
- if (config.seedSource) {
120
- state.metadata.seedSource = config.seedSource;
121
- }
122
-
123
- if (config.preseedDocs) {
124
- state.metadata.preseedDocs = config.preseedDocs;
125
- }
126
-
127
- // Generate session ID
128
- state.loopSession.sessionId = generateSessionId();
129
-
130
- // Save initial state
131
- save(projectRoot, state);
132
-
133
- return state;
134
- }
135
-
136
- /**
137
- * Generate a unique session ID
138
- * @returns {string} Session ID
139
- */
140
- function generateSessionId() {
141
- return `build-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
142
- }
143
-
144
- /**
145
- * Load build state from disk
146
- * @param {string} projectRoot - Project root path
147
- * @returns {object|null} Build state or null if not found
148
- */
149
- function load(projectRoot) {
150
- const statePath = getStatePath(projectRoot);
151
-
152
- if (!fs.existsSync(statePath)) {
153
- return null;
154
- }
155
-
156
- try {
157
- const content = fs.readFileSync(statePath, 'utf-8');
158
- return JSON.parse(content);
159
- } catch (error) {
160
- console.error(`Failed to load build state: ${error.message}`);
161
- return null;
162
- }
163
- }
164
-
165
- /**
166
- * Save build state to disk
167
- * @param {string} projectRoot - Project root path
168
- * @param {object} state - Build state to save
169
- * @returns {boolean} Success
170
- */
171
- function save(projectRoot, state) {
172
- const statePath = getStatePath(projectRoot);
173
- const planningDir = getPlanningDir(projectRoot);
174
-
175
- // Ensure planning directory exists
176
- if (!fs.existsSync(planningDir)) {
177
- fs.mkdirSync(planningDir, { recursive: true });
178
- }
179
-
180
- // Update timestamp
181
- state.metadata.updatedAt = new Date().toISOString();
182
- state.loopSession.lastUpdated = new Date().toISOString();
183
-
184
- try {
185
- fs.writeFileSync(statePath, JSON.stringify(state, null, 2));
186
- return true;
187
- } catch (error) {
188
- console.error(`Failed to save build state: ${error.message}`);
189
- return false;
190
- }
191
- }
192
-
193
- /**
194
- * Update task progress
195
- * @param {string} projectRoot - Project root path
196
- * @param {string} taskId - Task ID
197
- * @param {string} status - New status (pending, in_progress, completed, blocked, skipped)
198
- * @param {object} details - Additional details
199
- * @returns {object|null} Updated state or null on failure
200
- */
201
- function updateProgress(projectRoot, taskId, status, details = {}) {
202
- const state = load(projectRoot);
203
- if (!state) return null;
204
-
205
- // Find task in queue
206
- const taskIndex = state.implementationQueue.findIndex(t => t.id === taskId);
207
- if (taskIndex === -1) return null;
208
-
209
- // Update task status
210
- state.implementationQueue[taskIndex].status = status;
211
- state.implementationQueue[taskIndex].updatedAt = new Date().toISOString();
212
-
213
- // Add any additional details
214
- if (details.completedAt) {
215
- state.implementationQueue[taskIndex].completedAt = details.completedAt;
216
- }
217
- if (details.error) {
218
- state.implementationQueue[taskIndex].error = details.error;
219
- }
220
- if (details.output) {
221
- state.implementationQueue[taskIndex].lastOutput = details.output;
222
- }
223
-
224
- // Update phase progress
225
- const task = state.implementationQueue[taskIndex];
226
- if (task.phase && state.phases[task.phase]) {
227
- updatePhaseProgress(state, task.phase);
228
- }
229
-
230
- // Update MVP criteria progress
231
- updateMvpProgress(state);
232
-
233
- // Update iteration count if completing
234
- if (status === 'completed' || status === 'skipped') {
235
- state.loopSession.currentIteration++;
236
- }
237
-
238
- // Save updated state
239
- const saved = save(projectRoot, state);
240
- if (!saved) return null;
241
-
242
- return state;
243
- }
244
-
245
- /**
246
- * Update phase progress based on task statuses
247
- * @param {object} state - Build state
248
- * @param {string} phaseName - Phase name
249
- */
250
- function updatePhaseProgress(state, phaseName) {
251
- const phaseTasks = state.implementationQueue.filter(t => t.phase === phaseName);
252
- const completedTasks = phaseTasks.filter(t => t.status === 'completed');
253
-
254
- if (phaseTasks.length === 0) return;
255
-
256
- const progress = completedTasks.length / phaseTasks.length;
257
-
258
- if (progress === 0) {
259
- state.phases[phaseName].status = 'pending';
260
- } else if (progress === 1) {
261
- state.phases[phaseName].status = 'completed';
262
- } else {
263
- state.phases[phaseName].status = 'in_progress';
264
- }
265
-
266
- state.phases[phaseName].tasks = phaseTasks.map(t => ({
267
- id: t.id,
268
- title: t.title,
269
- status: t.status
270
- }));
271
- }
272
-
273
- /**
274
- * Update MVP criteria progress
275
- * @param {object} state - Build state
276
- */
277
- function updateMvpProgress(state) {
278
- if (!state.mvpCriteria || typeof state.mvpCriteria !== 'object') {
279
- state.mvpCriteria = { features: [], completionPercentage: 0, allCriteriaMet: false };
280
- }
281
- if (!Array.isArray(state.mvpCriteria.features)) {
282
- state.mvpCriteria.features = [];
283
- }
284
-
285
- const mvpTasks = state.implementationQueue.filter(t => t.phase === 'mvp');
286
- const completedMvpTasks = mvpTasks.filter(t => t.status === 'completed');
287
-
288
- if (mvpTasks.length > 0) {
289
- state.mvpCriteria.completionPercentage = Math.round(
290
- (completedMvpTasks.length / mvpTasks.length) * 100
291
- );
292
- }
293
-
294
- // Update feature completion status
295
- const safeFeatures = (state.mvpCriteria.features || []).filter(feature =>
296
- feature &&
297
- typeof feature.name === 'string' &&
298
- feature.name.trim().length > 0
299
- );
300
-
301
- state.mvpCriteria.features = safeFeatures.map(feature => {
302
- const featureName = feature.name.trim();
303
- const relatedTasks = mvpTasks.filter(t =>
304
- t.title.toLowerCase().includes(featureName.toLowerCase()) ||
305
- t.sourceSection?.toLowerCase().includes(featureName.toLowerCase())
306
- );
307
-
308
- const allComplete = relatedTasks.length > 0 &&
309
- relatedTasks.every(t => t.status === 'completed');
310
-
311
- return {
312
- ...feature,
313
- name: featureName,
314
- status: allComplete ? 'completed' : 'pending'
315
- };
316
- });
317
-
318
- // Check if all MVP criteria are met
319
- const hasMvpTasks = mvpTasks.length > 0;
320
- const hasMvpFeatures = state.mvpCriteria.features.length > 0;
321
- const featuresComplete = hasMvpFeatures && state.mvpCriteria.features.every(f => f.status === 'completed');
322
-
323
- if (hasMvpTasks) {
324
- state.mvpCriteria.allCriteriaMet =
325
- state.mvpCriteria.completionPercentage === 100 &&
326
- (!hasMvpFeatures || featuresComplete);
327
- } else {
328
- state.mvpCriteria.allCriteriaMet = featuresComplete;
329
- }
330
- }
331
-
332
- /**
333
- * Get the next pending task
334
- * @param {string} projectRoot - Project root path
335
- * @returns {object|null} Next task or null if none
336
- */
337
- function getNextTask(projectRoot) {
338
- const state = load(projectRoot);
339
- if (!state) return null;
340
-
341
- // First, check for any in_progress tasks
342
- const inProgress = state.implementationQueue.find(t => t.status === 'in_progress');
343
- if (inProgress) return inProgress;
344
-
345
- // Then get next pending task (respecting dependencies)
346
- const pendingTasks = state.implementationQueue.filter(t => t.status === 'pending');
347
-
348
- for (const task of pendingTasks) {
349
- // Check if all dependencies are completed
350
- if (!task.dependencies || task.dependencies.length === 0) {
351
- return task;
352
- }
353
-
354
- const depsCompleted = task.dependencies.every(depId => {
355
- const depTask = state.implementationQueue.find(t => t.id === depId);
356
- return depTask && depTask.status === 'completed';
357
- });
358
-
359
- if (depsCompleted) {
360
- return task;
361
- }
362
- }
363
-
364
- return null;
365
- }
366
-
367
- /**
368
- * Add a task to the implementation queue
369
- * @param {string} projectRoot - Project root path
370
- * @param {object} task - Task to add
371
- * @returns {object|null} Updated state or null on failure
372
- */
373
- function addTask(projectRoot, task) {
374
- const state = load(projectRoot);
375
- if (!state) return null;
376
-
377
- // Generate task ID if not provided
378
- if (!task.id) {
379
- const count = state.implementationQueue.length + 1;
380
- task.id = `task-${count.toString().padStart(3, '0')}`;
381
- }
382
-
383
- // Set defaults
384
- task.status = task.status || 'pending';
385
- task.createdAt = new Date().toISOString();
386
-
387
- state.implementationQueue.push(task);
388
- save(projectRoot, state);
389
-
390
- return state;
391
- }
392
-
393
- /**
394
- * Set tasks in batch
395
- * @param {string} projectRoot - Project root path
396
- * @param {array} tasks - Tasks to set
397
- * @returns {object|null} Updated state or null on failure
398
- */
399
- function setTasks(projectRoot, tasks) {
400
- const state = load(projectRoot);
401
- if (!state) return null;
402
-
403
- // Process tasks with defaults
404
- state.implementationQueue = tasks.map((task, index) => ({
405
- id: task.id || `task-${(index + 1).toString().padStart(3, '0')}`,
406
- title: task.title,
407
- description: task.description || '',
408
- source: task.source || 'manual',
409
- sourceSection: task.sourceSection || null,
410
- phase: task.phase || 'mvp',
411
- status: task.status || 'pending',
412
- acceptanceCriteria: task.acceptanceCriteria || [],
413
- dependencies: task.dependencies || [],
414
- estimatedComplexity: task.estimatedComplexity || 'medium',
415
- createdAt: new Date().toISOString()
416
- }));
417
-
418
- save(projectRoot, state);
419
- return state;
420
- }
421
-
422
- /**
423
- * Set MVP features
424
- * @param {string} projectRoot - Project root path
425
- * @param {array} features - MVP features
426
- * @returns {object|null} Updated state or null on failure
427
- */
428
- function setMvpFeatures(projectRoot, features) {
429
- const state = load(projectRoot);
430
- if (!state) return null;
431
-
432
- state.mvpCriteria.features = features.map(f => ({
433
- name: typeof f === 'string' ? f : f.name,
434
- status: 'pending'
435
- }));
436
-
437
- save(projectRoot, state);
438
- return state;
439
- }
440
-
441
- /**
442
- * Update loop session
443
- * @param {string} projectRoot - Project root path
444
- * @param {object} updates - Session updates
445
- * @returns {object|null} Updated state or null on failure
446
- */
447
- function updateSession(projectRoot, updates) {
448
- const state = load(projectRoot);
449
- if (!state) return null;
450
-
451
- state.loopSession = {
452
- ...state.loopSession,
453
- ...updates
454
- };
455
-
456
- save(projectRoot, state);
457
- return state;
458
- }
459
-
460
- /**
461
- * Pause the build loop
462
- * @param {string} projectRoot - Project root path
463
- * @returns {object|null} Updated state or null on failure
464
- */
465
- function pause(projectRoot) {
466
- const state = load(projectRoot);
467
- if (!state) return null;
468
-
469
- state.status = 'paused';
470
- state.loopSession.pausedAt = new Date().toISOString();
471
-
472
- save(projectRoot, state);
473
- return state;
474
- }
475
-
476
- /**
477
- * Resume the build loop
478
- * @param {string} projectRoot - Project root path
479
- * @returns {object|null} Updated state or null on failure
480
- */
481
- function resume(projectRoot) {
482
- const state = load(projectRoot);
483
- if (!state) return null;
484
-
485
- state.status = 'in_progress';
486
- state.loopSession.pausedAt = null;
487
-
488
- // Generate new session ID if resuming after long pause
489
- const pausedAt = state.loopSession.pausedAt ? new Date(state.loopSession.pausedAt) : null;
490
- const now = new Date();
491
- if (pausedAt && (now - pausedAt) > 3600000) { // 1 hour
492
- state.loopSession.sessionId = generateSessionId();
493
- }
494
-
495
- save(projectRoot, state);
496
- return state;
497
- }
498
-
499
- /**
500
- * Mark build as complete
501
- * @param {string} projectRoot - Project root path
502
- * @returns {object|null} Updated state or null on failure
503
- */
504
- function complete(projectRoot) {
505
- const state = load(projectRoot);
506
- if (!state) return null;
507
-
508
- state.status = 'completed';
509
- state.loopSession.completedAt = new Date().toISOString();
510
-
511
- save(projectRoot, state);
512
- return state;
513
- }
514
-
515
- /**
516
- * Mark build as failed
517
- * @param {string} projectRoot - Project root path
518
- * @param {string} reason - Failure reason
519
- * @returns {object|null} Updated state or null on failure
520
- */
521
- function fail(projectRoot, reason) {
522
- const state = load(projectRoot);
523
- if (!state) return null;
524
-
525
- state.status = 'failed';
526
- state.loopSession.failedAt = new Date().toISOString();
527
- state.loopSession.failureReason = reason;
528
-
529
- save(projectRoot, state);
530
- return state;
531
- }
532
-
533
- /**
534
- * Get build statistics
535
- * @param {string} projectRoot - Project root path
536
- * @returns {object|null} Statistics or null if no state
537
- */
538
- function getStats(projectRoot) {
539
- const state = load(projectRoot);
540
- if (!state) return null;
541
- const mvpFeatures = Array.isArray(state.mvpCriteria?.features) ? state.mvpCriteria.features : [];
542
-
543
- const tasks = state.implementationQueue;
544
- const completed = tasks.filter(t => t.status === 'completed').length;
545
- const pending = tasks.filter(t => t.status === 'pending').length;
546
- const inProgress = tasks.filter(t => t.status === 'in_progress').length;
547
- const blocked = tasks.filter(t => t.status === 'blocked').length;
548
- const skipped = tasks.filter(t => t.status === 'skipped').length;
549
- const mvpTasks = tasks.filter(t => t.phase === 'mvp');
550
- const completedMvpTasks = mvpTasks.filter(t => t.status === 'completed').length;
551
- const computedMvpProgress = mvpTasks.length > 0
552
- ? Math.round((completedMvpTasks / mvpTasks.length) * 100)
553
- : state.mvpCriteria.completionPercentage;
554
- const computedMvpComplete = mvpTasks.length > 0
555
- ? computedMvpProgress === 100
556
- : (mvpFeatures.length > 0 && mvpFeatures.every(f => f.status === 'completed'));
557
- const completedPercent = tasks.length > 0 ? (completed / tasks.length) * 100 : 0;
558
-
559
- return {
560
- total: tasks.length,
561
- completed,
562
- pending,
563
- inProgress,
564
- blocked,
565
- skipped,
566
- progress: Math.round(completedPercent),
567
- completedPercent,
568
- currentPhase: state.currentPhase,
569
- mvpProgress: computedMvpProgress,
570
- mvpComplete: computedMvpComplete,
571
- iteration: state.loopSession.currentIteration,
572
- maxIterations: state.loopSession.maxIterations,
573
- status: state.status
574
- };
575
- }
576
-
577
- /**
578
- * Reset build state
579
- * @param {string} projectRoot - Project root path
580
- * @returns {object} Fresh state
581
- */
582
- function reset(projectRoot) {
583
- const state = load(projectRoot);
584
- const projectName = state?.projectName || 'Project';
585
-
586
- return initialize(projectRoot, { projectName });
587
- }
588
-
589
- module.exports = {
590
- BUILD_STATE_FILE,
591
- PLANNING_DIR,
592
- getDefaultState,
593
- getPlanningDir,
594
- getStatePath,
595
- exists,
596
- initialize,
597
- generateSessionId,
598
- load,
599
- save,
600
- updateProgress,
601
- getNextTask,
602
- addTask,
603
- setTasks,
604
- setMvpFeatures,
605
- updateSession,
606
- pause,
607
- resume,
608
- complete,
609
- fail,
610
- getStats,
611
- reset
612
- };
package/core/config.d.ts DELETED
@@ -1,106 +0,0 @@
1
- /**
2
- * Bootspring Configuration Types
3
- * @module core/config
4
- */
5
-
6
- export interface ProjectConfig {
7
- name: string;
8
- description: string;
9
- version: string;
10
- }
11
-
12
- export interface StackConfig {
13
- framework: string;
14
- language: string;
15
- database: string;
16
- hosting: string;
17
- }
18
-
19
- export interface PluginConfig {
20
- enabled: boolean;
21
- provider?: string;
22
- }
23
-
24
- export interface PluginsConfig {
25
- auth: PluginConfig;
26
- payments: PluginConfig;
27
- database: PluginConfig;
28
- testing: PluginConfig;
29
- security: PluginConfig;
30
- ai: PluginConfig;
31
- }
32
-
33
- export interface DashboardConfig {
34
- port: number;
35
- autoOpen: boolean;
36
- }
37
-
38
- export interface QualityConfig {
39
- preCommit: boolean;
40
- prePush: boolean;
41
- strictMode: boolean;
42
- }
43
-
44
- export interface PathsConfig {
45
- context: string;
46
- config: string;
47
- todo: string;
48
- }
49
-
50
- export interface BootspringConfig {
51
- project: ProjectConfig;
52
- stack: StackConfig;
53
- plugins: PluginsConfig;
54
- dashboard: DashboardConfig;
55
- quality: QualityConfig;
56
- paths: PathsConfig;
57
- /** Internal: project root path */
58
- _projectRoot?: string;
59
- }
60
-
61
- /** Default configuration values */
62
- export const DEFAULT_CONFIG: BootspringConfig;
63
-
64
- /** Config file names to search for */
65
- export const CONFIG_FILES: string[];
66
-
67
- /**
68
- * Load configuration from bootspring.config.js
69
- * @param projectRoot - Optional project root path
70
- * @returns Loaded configuration merged with defaults
71
- */
72
- export function load(projectRoot?: string): BootspringConfig;
73
-
74
- /**
75
- * Save configuration to bootspring.config.js
76
- * @param config - Configuration to save
77
- * @param targetPath - Path to save to
78
- * @returns True if saved successfully
79
- */
80
- export function save(config: Partial<BootspringConfig>, targetPath: string): boolean;
81
-
82
- /**
83
- * Validate configuration object
84
- * @param config - Configuration to validate
85
- * @returns Validation result with errors array
86
- */
87
- export function validate(config: unknown): { valid: boolean; errors: string[] };
88
-
89
- /**
90
- * Get default configuration
91
- * @returns Default configuration object
92
- */
93
- export function getDefaults(): BootspringConfig;
94
-
95
- /**
96
- * Find the project root directory
97
- * @returns Project root path or null
98
- */
99
- export function findProjectRoot(): string | null;
100
-
101
- /**
102
- * Find configuration file in directory
103
- * @param dir - Directory to search
104
- * @returns Config file path or null
105
- */
106
- export function findConfigFile(dir: string): string | null;