@girardmedia/bootspring 2.0.21 → 2.0.22

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 (147) hide show
  1. package/cli/preseed/index.js +16 -0
  2. package/cli/preseed/interactive.js +143 -0
  3. package/cli/preseed/templates.js +227 -0
  4. package/cli/seed/builders/ai-context-builder.js +85 -0
  5. package/cli/seed/builders/index.js +13 -0
  6. package/cli/seed/builders/seed-builder.js +272 -0
  7. package/cli/seed/extractors/content-extractors.js +383 -0
  8. package/cli/seed/extractors/index.js +47 -0
  9. package/cli/seed/extractors/metadata-extractors.js +167 -0
  10. package/cli/seed/extractors/section-extractor.js +54 -0
  11. package/cli/seed/extractors/stack-extractors.js +228 -0
  12. package/cli/seed/index.js +18 -0
  13. package/cli/seed/utils/folder-structure.js +84 -0
  14. package/cli/seed/utils/index.js +11 -0
  15. package/dist/cli/index.d.ts +3 -0
  16. package/dist/cli/index.js +3220 -0
  17. package/dist/cli/index.js.map +1 -0
  18. package/dist/context-McpJQa_2.d.ts +5710 -0
  19. package/dist/core/index.d.ts +635 -0
  20. package/dist/core/index.js +2593 -0
  21. package/dist/core/index.js.map +1 -0
  22. package/dist/index-QqbeEiDm.d.ts +857 -0
  23. package/dist/index-UiYCgwiH.d.ts +174 -0
  24. package/dist/index.d.ts +453 -0
  25. package/dist/index.js +44228 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/mcp/index.d.ts +1 -0
  28. package/dist/mcp/index.js +41173 -0
  29. package/dist/mcp/index.js.map +1 -0
  30. package/generators/index.ts +82 -0
  31. package/intelligence/orchestrator/config/failure-signatures.js +48 -0
  32. package/intelligence/orchestrator/config/index.js +20 -0
  33. package/intelligence/orchestrator/config/phases.js +111 -0
  34. package/intelligence/orchestrator/config/remediation.js +150 -0
  35. package/intelligence/orchestrator/config/workflows.js +168 -0
  36. package/intelligence/orchestrator/core/index.js +16 -0
  37. package/intelligence/orchestrator/core/state-manager.js +88 -0
  38. package/intelligence/orchestrator/core/telemetry.js +24 -0
  39. package/intelligence/orchestrator/index.js +17 -0
  40. package/mcp/contracts/mcp-contract.v1.json +1 -1
  41. package/package.json +16 -3
  42. package/src/cli/agent.ts +703 -0
  43. package/src/cli/analyze.ts +640 -0
  44. package/src/cli/audit.ts +707 -0
  45. package/src/cli/auth.ts +930 -0
  46. package/src/cli/billing.ts +364 -0
  47. package/src/cli/build.ts +1089 -0
  48. package/src/cli/business.ts +508 -0
  49. package/src/cli/checkpoint-utils.ts +236 -0
  50. package/src/cli/checkpoint.ts +757 -0
  51. package/src/cli/cloud-sync.ts +534 -0
  52. package/src/cli/content.ts +273 -0
  53. package/src/cli/context.ts +667 -0
  54. package/src/cli/dashboard.ts +133 -0
  55. package/src/cli/deploy.ts +704 -0
  56. package/src/cli/doctor.ts +480 -0
  57. package/src/cli/fundraise.ts +494 -0
  58. package/src/cli/generate.ts +346 -0
  59. package/src/cli/github-cmd.ts +566 -0
  60. package/src/cli/health.ts +599 -0
  61. package/src/cli/index.ts +113 -0
  62. package/src/cli/init.ts +838 -0
  63. package/src/cli/legal.ts +495 -0
  64. package/src/cli/log.ts +316 -0
  65. package/src/cli/loop.ts +1660 -0
  66. package/src/cli/manager.ts +878 -0
  67. package/src/cli/mcp.ts +275 -0
  68. package/src/cli/memory.ts +346 -0
  69. package/src/cli/metrics.ts +590 -0
  70. package/src/cli/monitor.ts +960 -0
  71. package/src/cli/mvp.ts +662 -0
  72. package/src/cli/onboard.ts +663 -0
  73. package/src/cli/orchestrator.ts +622 -0
  74. package/src/cli/plugin.ts +483 -0
  75. package/src/cli/prd.ts +671 -0
  76. package/src/cli/preseed-start.ts +1633 -0
  77. package/src/cli/preseed.ts +2434 -0
  78. package/src/cli/project.ts +526 -0
  79. package/src/cli/quality.ts +885 -0
  80. package/src/cli/security.ts +1079 -0
  81. package/src/cli/seed.ts +1224 -0
  82. package/src/cli/skill.ts +537 -0
  83. package/src/cli/suggest.ts +1225 -0
  84. package/src/cli/switch.ts +518 -0
  85. package/src/cli/task.ts +780 -0
  86. package/src/cli/telemetry.ts +172 -0
  87. package/src/cli/todo.ts +627 -0
  88. package/src/cli/types.ts +15 -0
  89. package/src/cli/update.ts +334 -0
  90. package/src/cli/visualize.ts +609 -0
  91. package/src/cli/watch.ts +895 -0
  92. package/src/cli/workspace.ts +709 -0
  93. package/src/core/action-recorder.ts +673 -0
  94. package/src/core/analyze-workflow.ts +1453 -0
  95. package/src/core/api-client.ts +1120 -0
  96. package/src/core/audit-workflow.ts +1681 -0
  97. package/src/core/auth.ts +471 -0
  98. package/src/core/build-orchestrator.ts +509 -0
  99. package/src/core/build-state.ts +621 -0
  100. package/src/core/checkpoint-engine.ts +482 -0
  101. package/src/core/config.ts +1285 -0
  102. package/src/core/context-loader.ts +694 -0
  103. package/src/core/context.ts +410 -0
  104. package/src/core/deploy-workflow.ts +1085 -0
  105. package/src/core/entitlements.ts +322 -0
  106. package/src/core/github-sync.ts +720 -0
  107. package/src/core/index.ts +981 -0
  108. package/src/core/ingest.ts +1186 -0
  109. package/src/core/metrics-engine.ts +886 -0
  110. package/src/core/mvp.ts +847 -0
  111. package/src/core/onboard-workflow.ts +1293 -0
  112. package/src/core/policies.ts +81 -0
  113. package/src/core/preseed-workflow.ts +1163 -0
  114. package/src/core/preseed.ts +1826 -0
  115. package/src/core/project-context.ts +380 -0
  116. package/src/core/project-state.ts +699 -0
  117. package/src/core/r2-sync.ts +691 -0
  118. package/src/core/scaffold.ts +1715 -0
  119. package/src/core/session.ts +286 -0
  120. package/src/core/task-extractor.ts +799 -0
  121. package/src/core/telemetry.ts +371 -0
  122. package/src/core/tier-enforcement.ts +737 -0
  123. package/src/core/utils.ts +437 -0
  124. package/src/index.ts +29 -0
  125. package/src/intelligence/agent-collab.ts +2376 -0
  126. package/src/intelligence/auto-suggest.ts +713 -0
  127. package/src/intelligence/content-gen.ts +1351 -0
  128. package/src/intelligence/cross-project.ts +1692 -0
  129. package/src/intelligence/git-memory.ts +529 -0
  130. package/src/intelligence/index.ts +318 -0
  131. package/src/intelligence/orchestrator.ts +534 -0
  132. package/src/intelligence/prd.ts +466 -0
  133. package/src/intelligence/recommendations.ts +982 -0
  134. package/src/intelligence/workflow-composer.ts +1472 -0
  135. package/src/mcp/capabilities.ts +233 -0
  136. package/src/mcp/index.ts +37 -0
  137. package/src/mcp/registry.ts +1268 -0
  138. package/src/mcp/response-formatter.ts +797 -0
  139. package/src/mcp/server.ts +240 -0
  140. package/src/types/agent.ts +69 -0
  141. package/src/types/config.ts +86 -0
  142. package/src/types/context.ts +77 -0
  143. package/src/types/index.ts +53 -0
  144. package/src/types/mcp.ts +91 -0
  145. package/src/types/skills.ts +47 -0
  146. package/src/types/workflow.ts +155 -0
  147. package/generators/index.js +0 -18
@@ -0,0 +1,482 @@
1
+ /**
2
+ * Bootspring Checkpoint Engine
3
+ * Detects and syncs project checkpoints based on file existence and criteria
4
+ *
5
+ * @package bootspring
6
+ * @module core/checkpoint-engine
7
+ */
8
+
9
+ import * as fs from 'fs';
10
+ import * as path from 'path';
11
+
12
+ // Module interfaces
13
+ interface Utils {
14
+ print: {
15
+ success: (msg: string) => void;
16
+ };
17
+ COLORS: {
18
+ green: string;
19
+ yellow: string;
20
+ cyan: string;
21
+ dim: string;
22
+ reset: string;
23
+ };
24
+ }
25
+
26
+ interface CheckpointDefinition {
27
+ id: string;
28
+ label: string;
29
+ sourceFile: string;
30
+ criteria: string;
31
+ }
32
+
33
+ interface CheckpointState {
34
+ completed: boolean;
35
+ completedAt?: string | undefined;
36
+ completedBy?: string | undefined;
37
+ }
38
+
39
+ interface ProjectStateData {
40
+ projectType?: string | undefined;
41
+ checkpoints: Record<string, CheckpointState>;
42
+ github?: { connected?: boolean } | undefined;
43
+ content?: { published?: boolean } | undefined;
44
+ autoTagged?: boolean | undefined;
45
+ taggedBy?: string | undefined;
46
+ createdAt?: string | undefined;
47
+ updatedAt?: string | undefined;
48
+ }
49
+
50
+ interface ProjectState {
51
+ PROJECT_TYPES: { DEVELOPMENT: string };
52
+ getStateFilePath: (root: string) => string;
53
+ getPlanningDir: (root: string) => string;
54
+ loadState: (root: string) => ProjectStateData | null;
55
+ getOrCreateState: (root: string) => ProjectStateData;
56
+ saveState: (root: string, state: ProjectStateData) => void;
57
+ getCheckpointDefinitions: (projectType: string) => CheckpointDefinition[];
58
+ recordCheckpointHistory: (root: string, record: CheckpointHistoryRecord) => void;
59
+ }
60
+
61
+ interface CheckpointHistoryRecord {
62
+ checkpointId: string;
63
+ completedAt: string;
64
+ completedBy: string;
65
+ notes: string;
66
+ }
67
+
68
+ export interface CheckpointEvaluation {
69
+ id: string;
70
+ label: string;
71
+ sourceFile: string;
72
+ criteria: string;
73
+ completed: boolean;
74
+ reason: string | null;
75
+ }
76
+
77
+ export interface SyncChange {
78
+ id: string;
79
+ label: string;
80
+ type: 'completed' | 'uncompleted';
81
+ reason: string | null;
82
+ }
83
+
84
+ export interface SyncResult {
85
+ changes: SyncChange[];
86
+ unchanged: CheckpointEvaluation[];
87
+ total: number;
88
+ completed: number;
89
+ state: ProjectStateData;
90
+ }
91
+
92
+ export interface SyncOptions {
93
+ dryRun?: boolean | undefined;
94
+ verbose?: boolean | undefined;
95
+ }
96
+
97
+ export interface CheckpointStatus {
98
+ exists: boolean;
99
+ message?: string | undefined;
100
+ projectType?: string | undefined;
101
+ autoTagged?: boolean | undefined;
102
+ taggedBy?: string | undefined;
103
+ total?: number | undefined;
104
+ completed?: number | undefined;
105
+ pending?: number | undefined;
106
+ percentage?: number | undefined;
107
+ checkpoints?: CheckpointEvaluation[] | undefined;
108
+ createdAt?: string | undefined;
109
+ updatedAt?: string | undefined;
110
+ }
111
+
112
+ export interface NextStep {
113
+ id: string;
114
+ label: string;
115
+ sourceFile: string;
116
+ action: string;
117
+ }
118
+
119
+ // Lazy-loaded modules
120
+ let _utils: Utils | null = null;
121
+ let _projectState: ProjectState | null = null;
122
+
123
+ function getUtils(): Utils {
124
+ if (!_utils) {
125
+ _utils = require('./utils') as Utils;
126
+ }
127
+ return _utils;
128
+ }
129
+
130
+ function getProjectState(): ProjectState {
131
+ if (!_projectState) {
132
+ _projectState = require('./project-state') as ProjectState;
133
+ }
134
+ return _projectState;
135
+ }
136
+
137
+ // ============================================================================
138
+ // Criteria Evaluators
139
+ // ============================================================================
140
+
141
+ /**
142
+ * Check if a file exists
143
+ */
144
+ export function criteriaFileExists(filePath: string): boolean {
145
+ return fs.existsSync(filePath);
146
+ }
147
+
148
+ /**
149
+ * Check if a file exists and has minimum content length
150
+ */
151
+ export function criteriaFileExistsMinLength(filePath: string, minLength: number = 500): boolean {
152
+ if (!fs.existsSync(filePath)) {
153
+ return false;
154
+ }
155
+
156
+ try {
157
+ const content = fs.readFileSync(filePath, 'utf-8');
158
+ return content.length >= minLength;
159
+ } catch {
160
+ return false;
161
+ }
162
+ }
163
+
164
+ /**
165
+ * Check if a file contains a URL
166
+ */
167
+ export function criteriaContainsUrl(filePath: string): boolean {
168
+ if (!fs.existsSync(filePath)) {
169
+ return false;
170
+ }
171
+
172
+ try {
173
+ const content = fs.readFileSync(filePath, 'utf-8');
174
+ // Match common URL patterns
175
+ const urlPattern = /https?:\/\/[^\s)\]"'`]+/i;
176
+ return urlPattern.test(content);
177
+ } catch {
178
+ return false;
179
+ }
180
+ }
181
+
182
+ /**
183
+ * Check if PROJECT_STATE.json exists (config exists)
184
+ */
185
+ export function criteriaConfigExists(projectRoot: string): boolean {
186
+ const projectState = getProjectState();
187
+ const stateFile = projectState.getStateFilePath(projectRoot);
188
+ return fs.existsSync(stateFile);
189
+ }
190
+
191
+ /**
192
+ * Check if GitHub is connected in state
193
+ */
194
+ export function criteriaGitHubConnected(projectRoot: string): boolean {
195
+ const projectState = getProjectState();
196
+ const state = projectState.loadState(projectRoot);
197
+ return state?.github?.connected === true;
198
+ }
199
+
200
+ /**
201
+ * Check if content is published in state
202
+ */
203
+ export function criteriaContentPublished(projectRoot: string): boolean {
204
+ const projectState = getProjectState();
205
+ const state = projectState.loadState(projectRoot);
206
+ return state?.content?.published === true;
207
+ }
208
+
209
+ // ============================================================================
210
+ // Checkpoint Evaluation
211
+ // ============================================================================
212
+
213
+ /**
214
+ * Evaluate a single checkpoint
215
+ */
216
+ export function evaluateCheckpoint(projectRoot: string, checkpoint: CheckpointDefinition): CheckpointEvaluation {
217
+ const projectState = getProjectState();
218
+ const planningDir = projectState.getPlanningDir(projectRoot);
219
+ const filePath = path.join(planningDir, checkpoint.sourceFile);
220
+
221
+ let completed = false;
222
+ let reason: string | null = null;
223
+
224
+ switch (checkpoint.criteria) {
225
+ case 'file_exists':
226
+ completed = criteriaFileExists(filePath);
227
+ reason = completed ? 'File exists' : 'File not found';
228
+ break;
229
+
230
+ case 'file_exists_min_500':
231
+ completed = criteriaFileExistsMinLength(filePath, 500);
232
+ reason = completed ? 'File exists with sufficient content' : 'File missing or too short (<500 chars)';
233
+ break;
234
+
235
+ case 'contains_url':
236
+ completed = criteriaContainsUrl(filePath);
237
+ reason = completed ? 'Deployment URL found' : 'No deployment URL found';
238
+ break;
239
+
240
+ case 'config_exists':
241
+ completed = criteriaConfigExists(projectRoot);
242
+ reason = completed ? 'Project initialized' : 'PROJECT_STATE.json not found';
243
+ break;
244
+
245
+ case 'github_connected':
246
+ completed = criteriaGitHubConnected(projectRoot);
247
+ reason = completed ? 'GitHub repository connected' : 'GitHub not connected';
248
+ break;
249
+
250
+ case 'content_published':
251
+ completed = criteriaContentPublished(projectRoot);
252
+ reason = completed ? 'Content published' : 'Content not yet published';
253
+ break;
254
+
255
+ default:
256
+ reason = `Unknown criteria: ${checkpoint.criteria}`;
257
+ }
258
+
259
+ return {
260
+ id: checkpoint.id,
261
+ label: checkpoint.label,
262
+ sourceFile: checkpoint.sourceFile,
263
+ criteria: checkpoint.criteria,
264
+ completed,
265
+ reason
266
+ };
267
+ }
268
+
269
+ /**
270
+ * Evaluate all checkpoints for a project
271
+ */
272
+ export function evaluateAllCheckpoints(projectRoot: string, projectType: string | null = null): CheckpointEvaluation[] {
273
+ const projectState = getProjectState();
274
+
275
+ // Get project type from state if not provided
276
+ if (!projectType) {
277
+ const state = projectState.loadState(projectRoot);
278
+ projectType = state?.projectType || projectState.PROJECT_TYPES.DEVELOPMENT;
279
+ }
280
+
281
+ const checkpointDefs = projectState.getCheckpointDefinitions(projectType);
282
+ return checkpointDefs.map(checkpoint => evaluateCheckpoint(projectRoot, checkpoint));
283
+ }
284
+
285
+ /**
286
+ * Sync checkpoints - detect completed checkpoints from files
287
+ */
288
+ export function syncCheckpoints(projectRoot: string, options: SyncOptions = {}): SyncResult {
289
+ const utils = getUtils();
290
+ const projectState = getProjectState();
291
+
292
+ const state = projectState.getOrCreateState(projectRoot);
293
+ const evaluations = evaluateAllCheckpoints(projectRoot, state.projectType || null);
294
+
295
+ const changes: SyncChange[] = [];
296
+ const unchanged: CheckpointEvaluation[] = [];
297
+
298
+ for (const evaluation of evaluations) {
299
+ const currentState = state.checkpoints[evaluation.id];
300
+ const wasCompleted = currentState?.completed || false;
301
+
302
+ if (evaluation.completed && !wasCompleted) {
303
+ // Newly completed
304
+ changes.push({
305
+ id: evaluation.id,
306
+ label: evaluation.label,
307
+ type: 'completed',
308
+ reason: evaluation.reason
309
+ });
310
+
311
+ if (!options.dryRun) {
312
+ state.checkpoints[evaluation.id] = {
313
+ completed: true,
314
+ completedAt: new Date().toISOString(),
315
+ completedBy: 'sync'
316
+ };
317
+
318
+ const checkpointState = state.checkpoints[evaluation.id];
319
+ if (checkpointState?.completedAt) {
320
+ projectState.recordCheckpointHistory(projectRoot, {
321
+ checkpointId: evaluation.id,
322
+ completedAt: checkpointState.completedAt,
323
+ completedBy: 'sync',
324
+ notes: evaluation.reason || ''
325
+ });
326
+ }
327
+ }
328
+ } else if (!evaluation.completed && wasCompleted) {
329
+ // Was completed but no longer meets criteria (rare case)
330
+ changes.push({
331
+ id: evaluation.id,
332
+ label: evaluation.label,
333
+ type: 'uncompleted',
334
+ reason: evaluation.reason
335
+ });
336
+
337
+ // Note: We don't uncomplete checkpoints automatically - they stay completed once done
338
+ unchanged.push(evaluation);
339
+ } else {
340
+ unchanged.push(evaluation);
341
+ }
342
+ }
343
+
344
+ // Save state if not dry run
345
+ if (!options.dryRun && changes.length > 0) {
346
+ projectState.saveState(projectRoot, state);
347
+ }
348
+
349
+ // Log if verbose
350
+ if (options.verbose) {
351
+ for (const change of changes) {
352
+ if (change.type === 'completed') {
353
+ utils.print.success(`Checkpoint completed: ${change.label}`);
354
+ }
355
+ }
356
+ }
357
+
358
+ return {
359
+ changes,
360
+ unchanged,
361
+ total: evaluations.length,
362
+ completed: evaluations.filter(e => e.completed).length,
363
+ state
364
+ };
365
+ }
366
+
367
+ // ============================================================================
368
+ // Progress Reporting
369
+ // ============================================================================
370
+
371
+ /**
372
+ * Get checkpoint status with visual indicators
373
+ */
374
+ export function getCheckpointStatus(projectRoot: string): CheckpointStatus {
375
+ const projectState = getProjectState();
376
+ const state = projectState.loadState(projectRoot);
377
+
378
+ if (!state) {
379
+ return {
380
+ exists: false,
381
+ message: 'Project state not initialized. Run `bootspring checkpoint init` to start.'
382
+ };
383
+ }
384
+
385
+ const evaluations = evaluateAllCheckpoints(projectRoot, state.projectType || null);
386
+ const completed = evaluations.filter(e => e.completed);
387
+ const pending = evaluations.filter(e => !e.completed);
388
+
389
+ return {
390
+ exists: true,
391
+ projectType: state.projectType,
392
+ autoTagged: state.autoTagged,
393
+ taggedBy: state.taggedBy,
394
+ total: evaluations.length,
395
+ completed: completed.length,
396
+ pending: pending.length,
397
+ percentage: Math.round((completed.length / evaluations.length) * 100),
398
+ checkpoints: evaluations,
399
+ createdAt: state.createdAt,
400
+ updatedAt: state.updatedAt
401
+ };
402
+ }
403
+
404
+ /**
405
+ * Get next steps - what checkpoints should be completed next
406
+ */
407
+ export function getNextSteps(projectRoot: string): NextStep[] {
408
+ const projectState = getProjectState();
409
+ const state = projectState.loadState(projectRoot);
410
+
411
+ if (!state) {
412
+ return [];
413
+ }
414
+
415
+ const evaluations = evaluateAllCheckpoints(projectRoot, state.projectType || null);
416
+ const pending = evaluations.filter(e => !e.completed);
417
+
418
+ // Return first 3 incomplete checkpoints as suggestions
419
+ return pending.slice(0, 3).map(checkpoint => ({
420
+ id: checkpoint.id,
421
+ label: checkpoint.label,
422
+ sourceFile: checkpoint.sourceFile,
423
+ action: getActionSuggestion(checkpoint)
424
+ }));
425
+ }
426
+
427
+ /**
428
+ * Get action suggestion for a checkpoint
429
+ */
430
+ export function getActionSuggestion(checkpoint: CheckpointEvaluation): string {
431
+ switch (checkpoint.criteria) {
432
+ case 'file_exists':
433
+ case 'file_exists_min_500':
434
+ return `Create planning/${checkpoint.sourceFile}`;
435
+ case 'contains_url':
436
+ return `Add deployment URL to planning/${checkpoint.sourceFile}`;
437
+ case 'github_connected':
438
+ return 'Run `bootspring github connect`';
439
+ case 'content_published':
440
+ return 'Mark content as published';
441
+ default:
442
+ return 'Complete this checkpoint';
443
+ }
444
+ }
445
+
446
+ // ============================================================================
447
+ // Progress Bar Generation
448
+ // ============================================================================
449
+
450
+ /**
451
+ * Generate a progress bar string
452
+ */
453
+ export function generateProgressBar(percentage: number, width: number = 20): string {
454
+ const filled = Math.round((percentage / 100) * width);
455
+ const empty = width - filled;
456
+
457
+ const filledChar = '█';
458
+ const emptyChar = '░';
459
+
460
+ return filledChar.repeat(filled) + emptyChar.repeat(empty);
461
+ }
462
+
463
+ /**
464
+ * Get colored progress bar
465
+ */
466
+ export function getColoredProgressBar(percentage: number, width: number = 20): string {
467
+ const utils = getUtils();
468
+ const bar = generateProgressBar(percentage, width);
469
+ let color: string;
470
+
471
+ if (percentage >= 80) {
472
+ color = utils.COLORS.green;
473
+ } else if (percentage >= 50) {
474
+ color = utils.COLORS.yellow;
475
+ } else if (percentage >= 25) {
476
+ color = utils.COLORS.cyan;
477
+ } else {
478
+ color = utils.COLORS.dim;
479
+ }
480
+
481
+ return `${color}${bar}${utils.COLORS.reset}`;
482
+ }