@girardmedia/bootspring 2.0.21 → 2.0.23

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 (159) hide show
  1. package/bin/bootspring.js +5 -0
  2. package/cli/org.js +474 -0
  3. package/cli/preseed/index.js +16 -0
  4. package/cli/preseed/interactive.js +143 -0
  5. package/cli/preseed/templates.js +227 -0
  6. package/cli/preseed.js +9 -301
  7. package/cli/seed/builders/ai-context-builder.js +85 -0
  8. package/cli/seed/builders/index.js +13 -0
  9. package/cli/seed/builders/seed-builder.js +272 -0
  10. package/cli/seed/extractors/content-extractors.js +383 -0
  11. package/cli/seed/extractors/index.js +47 -0
  12. package/cli/seed/extractors/metadata-extractors.js +167 -0
  13. package/cli/seed/extractors/section-extractor.js +54 -0
  14. package/cli/seed/extractors/stack-extractors.js +228 -0
  15. package/cli/seed/index.js +18 -0
  16. package/cli/seed/utils/folder-structure.js +84 -0
  17. package/cli/seed/utils/index.js +11 -0
  18. package/cli/seed.js +23 -1074
  19. package/core/api-client.js +77 -0
  20. package/core/entitlements.js +36 -0
  21. package/core/organizations.js +223 -0
  22. package/core/policies.js +51 -6
  23. package/core/policy-matrix.js +303 -0
  24. package/core/project-context.js +1 -0
  25. package/dist/cli/index.d.ts +3 -0
  26. package/dist/cli/index.js +3220 -0
  27. package/dist/cli/index.js.map +1 -0
  28. package/dist/context-McpJQa_2.d.ts +5710 -0
  29. package/dist/core/index.d.ts +635 -0
  30. package/dist/core/index.js +2593 -0
  31. package/dist/core/index.js.map +1 -0
  32. package/dist/index-QqbeEiDm.d.ts +857 -0
  33. package/dist/index-UiYCgwiH.d.ts +174 -0
  34. package/dist/index.d.ts +453 -0
  35. package/dist/index.js +44228 -0
  36. package/dist/index.js.map +1 -0
  37. package/dist/mcp/index.d.ts +1 -0
  38. package/dist/mcp/index.js +41173 -0
  39. package/dist/mcp/index.js.map +1 -0
  40. package/generators/index.ts +82 -0
  41. package/intelligence/orchestrator/config/failure-signatures.js +48 -0
  42. package/intelligence/orchestrator/config/index.js +23 -0
  43. package/intelligence/orchestrator/config/pack-lifecycle.js +262 -0
  44. package/intelligence/orchestrator/config/phases.js +111 -0
  45. package/intelligence/orchestrator/config/remediation.js +150 -0
  46. package/intelligence/orchestrator/config/workflows.js +168 -0
  47. package/intelligence/orchestrator/core/index.js +16 -0
  48. package/intelligence/orchestrator/core/state-manager.js +88 -0
  49. package/intelligence/orchestrator/core/telemetry.js +24 -0
  50. package/intelligence/orchestrator/index.js +17 -0
  51. package/intelligence/orchestrator.js +17 -512
  52. package/mcp/contracts/mcp-contract.v1.json +1 -1
  53. package/package.json +16 -3
  54. package/src/cli/agent.ts +703 -0
  55. package/src/cli/analyze.ts +640 -0
  56. package/src/cli/audit.ts +707 -0
  57. package/src/cli/auth.ts +930 -0
  58. package/src/cli/billing.ts +364 -0
  59. package/src/cli/build.ts +1089 -0
  60. package/src/cli/business.ts +508 -0
  61. package/src/cli/checkpoint-utils.ts +236 -0
  62. package/src/cli/checkpoint.ts +757 -0
  63. package/src/cli/cloud-sync.ts +534 -0
  64. package/src/cli/content.ts +273 -0
  65. package/src/cli/context.ts +667 -0
  66. package/src/cli/dashboard.ts +133 -0
  67. package/src/cli/deploy.ts +704 -0
  68. package/src/cli/doctor.ts +480 -0
  69. package/src/cli/fundraise.ts +494 -0
  70. package/src/cli/generate.ts +346 -0
  71. package/src/cli/github-cmd.ts +566 -0
  72. package/src/cli/health.ts +599 -0
  73. package/src/cli/index.ts +113 -0
  74. package/src/cli/init.ts +838 -0
  75. package/src/cli/legal.ts +495 -0
  76. package/src/cli/log.ts +316 -0
  77. package/src/cli/loop.ts +1660 -0
  78. package/src/cli/manager.ts +878 -0
  79. package/src/cli/mcp.ts +275 -0
  80. package/src/cli/memory.ts +346 -0
  81. package/src/cli/metrics.ts +590 -0
  82. package/src/cli/monitor.ts +960 -0
  83. package/src/cli/mvp.ts +662 -0
  84. package/src/cli/onboard.ts +663 -0
  85. package/src/cli/orchestrator.ts +622 -0
  86. package/src/cli/plugin.ts +483 -0
  87. package/src/cli/prd.ts +671 -0
  88. package/src/cli/preseed-start.ts +1633 -0
  89. package/src/cli/preseed.ts +2434 -0
  90. package/src/cli/project.ts +526 -0
  91. package/src/cli/quality.ts +885 -0
  92. package/src/cli/security.ts +1079 -0
  93. package/src/cli/seed.ts +1224 -0
  94. package/src/cli/skill.ts +537 -0
  95. package/src/cli/suggest.ts +1225 -0
  96. package/src/cli/switch.ts +518 -0
  97. package/src/cli/task.ts +780 -0
  98. package/src/cli/telemetry.ts +172 -0
  99. package/src/cli/todo.ts +627 -0
  100. package/src/cli/types.ts +15 -0
  101. package/src/cli/update.ts +334 -0
  102. package/src/cli/visualize.ts +609 -0
  103. package/src/cli/watch.ts +895 -0
  104. package/src/cli/workspace.ts +709 -0
  105. package/src/core/action-recorder.ts +673 -0
  106. package/src/core/analyze-workflow.ts +1453 -0
  107. package/src/core/api-client.ts +1120 -0
  108. package/src/core/audit-workflow.ts +1681 -0
  109. package/src/core/auth.ts +471 -0
  110. package/src/core/build-orchestrator.ts +509 -0
  111. package/src/core/build-state.ts +621 -0
  112. package/src/core/checkpoint-engine.ts +482 -0
  113. package/src/core/config.ts +1285 -0
  114. package/src/core/context-loader.ts +694 -0
  115. package/src/core/context.ts +410 -0
  116. package/src/core/deploy-workflow.ts +1085 -0
  117. package/src/core/entitlements.ts +322 -0
  118. package/src/core/github-sync.ts +720 -0
  119. package/src/core/index.ts +981 -0
  120. package/src/core/ingest.ts +1186 -0
  121. package/src/core/metrics-engine.ts +886 -0
  122. package/src/core/mvp.ts +847 -0
  123. package/src/core/onboard-workflow.ts +1293 -0
  124. package/src/core/policies.ts +81 -0
  125. package/src/core/preseed-workflow.ts +1163 -0
  126. package/src/core/preseed.ts +1826 -0
  127. package/src/core/project-context.ts +380 -0
  128. package/src/core/project-state.ts +699 -0
  129. package/src/core/r2-sync.ts +691 -0
  130. package/src/core/scaffold.ts +1715 -0
  131. package/src/core/session.ts +286 -0
  132. package/src/core/task-extractor.ts +799 -0
  133. package/src/core/telemetry.ts +371 -0
  134. package/src/core/tier-enforcement.ts +737 -0
  135. package/src/core/utils.ts +437 -0
  136. package/src/index.ts +29 -0
  137. package/src/intelligence/agent-collab.ts +2376 -0
  138. package/src/intelligence/auto-suggest.ts +713 -0
  139. package/src/intelligence/content-gen.ts +1351 -0
  140. package/src/intelligence/cross-project.ts +1692 -0
  141. package/src/intelligence/git-memory.ts +529 -0
  142. package/src/intelligence/index.ts +318 -0
  143. package/src/intelligence/orchestrator.ts +534 -0
  144. package/src/intelligence/prd.ts +466 -0
  145. package/src/intelligence/recommendations.ts +982 -0
  146. package/src/intelligence/workflow-composer.ts +1472 -0
  147. package/src/mcp/capabilities.ts +233 -0
  148. package/src/mcp/index.ts +37 -0
  149. package/src/mcp/registry.ts +1268 -0
  150. package/src/mcp/response-formatter.ts +797 -0
  151. package/src/mcp/server.ts +240 -0
  152. package/src/types/agent.ts +69 -0
  153. package/src/types/config.ts +86 -0
  154. package/src/types/context.ts +77 -0
  155. package/src/types/index.ts +53 -0
  156. package/src/types/mcp.ts +91 -0
  157. package/src/types/skills.ts +47 -0
  158. package/src/types/workflow.ts +155 -0
  159. package/generators/index.js +0 -18
@@ -0,0 +1,757 @@
1
+ /**
2
+ * Bootspring Checkpoint Command
3
+ * Manage project checkpoints and milestones
4
+ *
5
+ * Commands:
6
+ * status Show all checkpoints with status
7
+ * complete <name> Mark a checkpoint as complete manually
8
+ * history View checkpoint completion timeline
9
+ * sync Re-scan files to detect completed items
10
+ * init Initialize checkpoint tracking
11
+ *
12
+ * @package bootspring
13
+ * @command checkpoint
14
+ */
15
+
16
+ import * as fs from 'fs';
17
+ import * as path from 'path';
18
+ import { scanFilesForCheckpoints, pushCheckpointsToServer } from './checkpoint-utils';
19
+
20
+ // Type interfaces for JS modules
21
+ interface Config {
22
+ _projectRoot: string;
23
+ apiBase?: string | undefined;
24
+ }
25
+
26
+ interface ConfigModule {
27
+ load(): Config;
28
+ }
29
+
30
+ interface UtilsModule {
31
+ COLORS: Colors;
32
+ print: PrintModule;
33
+ createSpinner(text: string): Spinner;
34
+ parseArgs(args: string[]): ParsedArgs;
35
+ }
36
+
37
+ interface Colors {
38
+ reset: string;
39
+ bold: string;
40
+ dim: string;
41
+ cyan: string;
42
+ green: string;
43
+ yellow: string;
44
+ red: string;
45
+ magenta: string;
46
+ }
47
+
48
+ interface PrintModule {
49
+ success(msg: string): void;
50
+ error(msg: string): void;
51
+ warning(msg: string): void;
52
+ info(msg: string): void;
53
+ dim(msg: string): void;
54
+ debug(msg: string): void;
55
+ }
56
+
57
+ interface ParsedArgs {
58
+ _: string[];
59
+ json?: boolean | undefined;
60
+ force?: boolean | undefined;
61
+ 'dry-run'?: boolean | undefined;
62
+ dryRun?: boolean | undefined;
63
+ type?: string | undefined;
64
+ notes?: string | undefined;
65
+ }
66
+
67
+ interface Spinner {
68
+ start(): Spinner;
69
+ stop(): void;
70
+ succeed(text: string): void;
71
+ fail(text: string): void;
72
+ info(text: string): void;
73
+ }
74
+
75
+ interface CheckpointDef {
76
+ id: string;
77
+ label: string;
78
+ }
79
+
80
+ interface CheckpointData {
81
+ completed?: boolean | undefined;
82
+ reason?: string | undefined;
83
+ }
84
+
85
+ interface ProjectState {
86
+ projectType: string;
87
+ checkpoints: Record<string, CheckpointData>;
88
+ autoTagged?: boolean | undefined;
89
+ taggedBy?: string | undefined;
90
+ }
91
+
92
+ interface CheckpointStatus {
93
+ exists: boolean;
94
+ projectType: string;
95
+ completed: number;
96
+ total: number;
97
+ percentage: number;
98
+ checkpoints: Array<{
99
+ id: string;
100
+ label: string;
101
+ completed: boolean;
102
+ reason?: string | undefined;
103
+ }>;
104
+ autoTagged?: boolean | undefined;
105
+ taggedBy?: string | undefined;
106
+ }
107
+
108
+ interface SyncResult {
109
+ changes: Array<{
110
+ type: string;
111
+ label: string;
112
+ reason: string;
113
+ }>;
114
+ completed: number;
115
+ total: number;
116
+ }
117
+
118
+ interface HistoryRecord {
119
+ checkpointId: string;
120
+ timestamp: string;
121
+ notes?: string | undefined;
122
+ }
123
+
124
+ interface NextStep {
125
+ action: string;
126
+ }
127
+
128
+ interface CheckpointProgress {
129
+ completed: number;
130
+ total: number;
131
+ percentage: number;
132
+ }
133
+
134
+ interface ProjectStateModule {
135
+ PROJECT_TYPES: Record<string, string>;
136
+ loadState(projectRoot: string): ProjectState | null;
137
+ initState(projectRoot: string, options: { projectType: string; autoTagged: boolean }): ProjectState;
138
+ getCheckpointDefinitions(projectType: string): CheckpointDef[];
139
+ completeCheckpoint(projectRoot: string, checkpointId: string, options: { completedBy: string; notes: string }): ProjectState | null;
140
+ getCheckpointProgress(projectRoot: string): CheckpointProgress;
141
+ getCheckpointHistory(projectRoot: string): HistoryRecord[];
142
+ setProjectType(projectRoot: string, type: string): ProjectState | null;
143
+ detectProjectType(projectRoot: string): string;
144
+ }
145
+
146
+ interface CheckpointEngineModule {
147
+ syncCheckpoints(projectRoot: string, options?: { verbose?: boolean | undefined; dryRun?: boolean | undefined }): SyncResult;
148
+ getCheckpointStatus(projectRoot: string): CheckpointStatus;
149
+ getColoredProgressBar(percentage: number, width: number): string;
150
+ getNextSteps(projectRoot: string): NextStep[];
151
+ }
152
+
153
+ interface TelemetryModule {
154
+ emitEvent(event: string, data: Record<string, unknown>, options?: { projectRoot?: string }): void;
155
+ }
156
+
157
+ const config = require('../core/config') as ConfigModule;
158
+ const utils = require('../core/utils') as UtilsModule;
159
+ const projectState = require('../core/project-state') as ProjectStateModule;
160
+ const checkpointEngine = require('../core/checkpoint-engine') as CheckpointEngineModule;
161
+ const telemetry = require('../core/telemetry') as TelemetryModule;
162
+
163
+ // ============================================================================
164
+ // Command Handlers
165
+ // ============================================================================
166
+
167
+ /**
168
+ * Show checkpoint status
169
+ */
170
+ async function handleStatus(projectRoot: string, options: { json?: boolean } = {}): Promise<void> {
171
+ // Auto-sync checkpoints before showing status
172
+ checkpointEngine.syncCheckpoints(projectRoot);
173
+
174
+ // Auto-push to server if authenticated (non-blocking)
175
+ pushCheckpointsToServer(projectRoot, { quiet: true, autoSync: true, skipMetrics: true }).catch(() => {});
176
+
177
+ const status = checkpointEngine.getCheckpointStatus(projectRoot);
178
+
179
+ if (!status.exists) {
180
+ console.log(`
181
+ ${utils.COLORS.cyan}${utils.COLORS.bold}⚡ Project Checkpoints${utils.COLORS.reset}
182
+
183
+ ${utils.COLORS.yellow}No checkpoint tracking found.${utils.COLORS.reset}
184
+
185
+ ${utils.COLORS.bold}Get started:${utils.COLORS.reset}
186
+ bootspring checkpoint init Initialize checkpoint tracking
187
+ bootspring preseed init Start with document generation
188
+ `);
189
+ return;
190
+ }
191
+
192
+ // Output as JSON if requested
193
+ if (options.json) {
194
+ console.log(JSON.stringify(status, null, 2));
195
+ return;
196
+ }
197
+
198
+ // Type badge
199
+ const typeBadge = getTypeBadge(status.projectType);
200
+ const autoTagNote = status.autoTagged ? ` ${utils.COLORS.dim}(auto-tagged by ${status.taggedBy})${utils.COLORS.reset}` : '';
201
+
202
+ console.log(`
203
+ ${utils.COLORS.cyan}${utils.COLORS.bold}⚡ Project Checkpoints${utils.COLORS.reset}
204
+
205
+ ${utils.COLORS.bold}Project Type:${utils.COLORS.reset} ${typeBadge}${autoTagNote}
206
+
207
+ ${utils.COLORS.bold}Progress:${utils.COLORS.reset} ${status.completed}/${status.total} (${status.percentage}%)
208
+ ${checkpointEngine.getColoredProgressBar(status.percentage, 30)}
209
+ `);
210
+
211
+ // Show all checkpoints
212
+ console.log(`${utils.COLORS.bold}Checkpoints:${utils.COLORS.reset}`);
213
+
214
+ for (const checkpoint of status.checkpoints) {
215
+ const icon = checkpoint.completed
216
+ ? `${utils.COLORS.green}✓${utils.COLORS.reset}`
217
+ : `${utils.COLORS.dim}○${utils.COLORS.reset}`;
218
+
219
+ const label = checkpoint.completed
220
+ ? checkpoint.label
221
+ : `${utils.COLORS.dim}${checkpoint.label}${utils.COLORS.reset}`;
222
+
223
+ const completedInfo = checkpoint.completed && checkpoint.reason
224
+ ? ` ${utils.COLORS.dim}(${checkpoint.reason})${utils.COLORS.reset}`
225
+ : '';
226
+
227
+ console.log(` ${icon} ${label}${completedInfo}`);
228
+ }
229
+
230
+ // Show next steps
231
+ const nextSteps = checkpointEngine.getNextSteps(projectRoot);
232
+ if (nextSteps.length > 0) {
233
+ console.log(`\n${utils.COLORS.bold}Next Steps:${utils.COLORS.reset}`);
234
+ for (const step of nextSteps) {
235
+ console.log(` ${utils.COLORS.cyan}→${utils.COLORS.reset} ${step.action}`);
236
+ }
237
+ }
238
+
239
+ console.log();
240
+
241
+ // Emit telemetry
242
+ telemetry.emitEvent('checkpoint:status', {
243
+ projectType: status.projectType,
244
+ completed: status.completed,
245
+ total: status.total,
246
+ percentage: status.percentage
247
+ }, { projectRoot });
248
+ }
249
+
250
+ /**
251
+ * Get type badge
252
+ */
253
+ function getTypeBadge(projectType: string): string {
254
+ const badges: Record<string, string> = {
255
+ development: `${utils.COLORS.cyan}[DEV]${utils.COLORS.reset}`,
256
+ content: `${utils.COLORS.magenta}[CONTENT]${utils.COLORS.reset}`,
257
+ business: `${utils.COLORS.yellow}[BUSINESS]${utils.COLORS.reset}`
258
+ };
259
+ const devBadge = badges.development as string;
260
+ return badges[projectType] || devBadge;
261
+ }
262
+
263
+ /**
264
+ * Initialize checkpoint tracking
265
+ */
266
+ function handleInit(projectRoot: string, options: { force?: boolean; type?: string | null } = {}): void {
267
+ const existingState = projectState.loadState(projectRoot);
268
+
269
+ if (existingState && !options.force) {
270
+ utils.print.warning('Checkpoint tracking already initialized.');
271
+ utils.print.dim('Use --force to reinitialize.');
272
+ console.log();
273
+ handleStatus(projectRoot);
274
+ return;
275
+ }
276
+
277
+ // Determine project type
278
+ let projectType = options.type;
279
+ if (!projectType) {
280
+ projectType = projectState.detectProjectType(projectRoot);
281
+ utils.print.info(`Auto-detected project type: ${projectType}`);
282
+ }
283
+
284
+ // Initialize state
285
+ projectState.initState(projectRoot, {
286
+ projectType,
287
+ autoTagged: false
288
+ });
289
+
290
+ // Run initial sync
291
+ const syncResult = checkpointEngine.syncCheckpoints(projectRoot, { verbose: true });
292
+
293
+ utils.print.success('Checkpoint tracking initialized!');
294
+ console.log();
295
+
296
+ // Show status
297
+ handleStatus(projectRoot);
298
+
299
+ // Emit telemetry
300
+ telemetry.emitEvent('checkpoint:init', {
301
+ projectType,
302
+ initialCompleted: syncResult.completed
303
+ }, { projectRoot });
304
+ }
305
+
306
+ /**
307
+ * Complete a checkpoint manually
308
+ */
309
+ function handleComplete(projectRoot: string, checkpointId: string | undefined, options: { notes?: string | null } = {}): void {
310
+ if (!checkpointId) {
311
+ utils.print.error('Please specify a checkpoint to complete.');
312
+ console.log(`\n${utils.COLORS.bold}Usage:${utils.COLORS.reset} bootspring checkpoint complete <checkpoint-id>`);
313
+ console.log(`\n${utils.COLORS.bold}Available checkpoints:${utils.COLORS.reset}`);
314
+
315
+ const state = projectState.loadState(projectRoot);
316
+ if (state) {
317
+ const defs = projectState.getCheckpointDefinitions(state.projectType);
318
+ for (const def of defs) {
319
+ const completed = state.checkpoints[def.id]?.completed;
320
+ const icon = completed ? `${utils.COLORS.green}✓${utils.COLORS.reset}` : `${utils.COLORS.dim}○${utils.COLORS.reset}`;
321
+ console.log(` ${icon} ${def.id} - ${def.label}`);
322
+ }
323
+ }
324
+ console.log();
325
+ return;
326
+ }
327
+
328
+ const state = projectState.loadState(projectRoot);
329
+ if (!state) {
330
+ utils.print.error('Checkpoint tracking not initialized.');
331
+ utils.print.dim('Run: bootspring checkpoint init');
332
+ return;
333
+ }
334
+
335
+ // Check if checkpoint exists
336
+ const defs = projectState.getCheckpointDefinitions(state.projectType);
337
+ const checkpoint = defs.find(c => c.id === checkpointId);
338
+
339
+ if (!checkpoint) {
340
+ utils.print.error(`Unknown checkpoint: ${checkpointId}`);
341
+ console.log(`\n${utils.COLORS.bold}Available checkpoints:${utils.COLORS.reset}`);
342
+ for (const def of defs) {
343
+ console.log(` - ${def.id}`);
344
+ }
345
+ console.log();
346
+ return;
347
+ }
348
+
349
+ // Check if already completed
350
+ if (state.checkpoints[checkpointId]?.completed) {
351
+ utils.print.warning(`Checkpoint "${checkpoint.label}" is already completed.`);
352
+ return;
353
+ }
354
+
355
+ // Complete the checkpoint
356
+ const updatedState = projectState.completeCheckpoint(projectRoot, checkpointId, {
357
+ completedBy: 'manual',
358
+ notes: options.notes || 'Manually marked as complete'
359
+ });
360
+
361
+ if (updatedState) {
362
+ utils.print.success(`Completed: ${checkpoint.label}`);
363
+
364
+ // Show progress
365
+ const progress = projectState.getCheckpointProgress(projectRoot);
366
+ console.log(`\nProgress: ${progress.completed}/${progress.total} (${progress.percentage}%)`);
367
+ console.log(checkpointEngine.getColoredProgressBar(progress.percentage, 30));
368
+ console.log();
369
+
370
+ // Emit telemetry
371
+ telemetry.emitEvent('checkpoint:complete', {
372
+ checkpointId,
373
+ label: checkpoint.label,
374
+ method: 'manual'
375
+ }, { projectRoot });
376
+ } else {
377
+ utils.print.error('Failed to complete checkpoint.');
378
+ }
379
+ }
380
+
381
+ /**
382
+ * Show checkpoint history
383
+ */
384
+ function handleHistory(projectRoot: string, options: { json?: boolean } = {}): void {
385
+ const history = projectState.getCheckpointHistory(projectRoot);
386
+
387
+ if (history.length === 0) {
388
+ console.log(`
389
+ ${utils.COLORS.cyan}${utils.COLORS.bold}⚡ Checkpoint History${utils.COLORS.reset}
390
+
391
+ ${utils.COLORS.dim}No checkpoint completions recorded yet.${utils.COLORS.reset}
392
+ `);
393
+ return;
394
+ }
395
+
396
+ // Output as JSON if requested
397
+ if (options.json) {
398
+ console.log(JSON.stringify(history, null, 2));
399
+ return;
400
+ }
401
+
402
+ const state = projectState.loadState(projectRoot);
403
+ const defs = state ? projectState.getCheckpointDefinitions(state.projectType) : [];
404
+
405
+ console.log(`
406
+ ${utils.COLORS.cyan}${utils.COLORS.bold}⚡ Checkpoint History${utils.COLORS.reset}
407
+ `);
408
+
409
+ // Group by date
410
+ const byDate: Record<string, HistoryRecord[]> = {};
411
+ for (const record of history) {
412
+ const date = new Date(record.timestamp).toLocaleDateString();
413
+ if (!byDate[date]) {
414
+ byDate[date] = [];
415
+ }
416
+ byDate[date].push(record);
417
+ }
418
+
419
+ // Display grouped
420
+ for (const [date, records] of Object.entries(byDate)) {
421
+ console.log(`${utils.COLORS.bold}${date}${utils.COLORS.reset}`);
422
+
423
+ for (const record of records) {
424
+ const checkpoint = defs.find(d => d.id === record.checkpointId);
425
+ const label = checkpoint?.label || record.checkpointId;
426
+ const time = new Date(record.timestamp).toLocaleTimeString();
427
+
428
+ console.log(` ${utils.COLORS.green}✓${utils.COLORS.reset} ${time} - ${label}`);
429
+ if (record.notes) {
430
+ console.log(` ${utils.COLORS.dim}${record.notes}${utils.COLORS.reset}`);
431
+ }
432
+ }
433
+ console.log();
434
+ }
435
+ }
436
+
437
+ /**
438
+ * Sync checkpoints from files
439
+ */
440
+ async function handleSync(projectRoot: string, options: { dryRun?: boolean } = {}): Promise<void> {
441
+ const spinner = utils.createSpinner('Scanning for completed checkpoints...');
442
+ spinner.start();
443
+
444
+ const result = checkpointEngine.syncCheckpoints(projectRoot, {
445
+ dryRun: options.dryRun,
446
+ verbose: false
447
+ });
448
+
449
+ if (result.changes.length === 0) {
450
+ spinner.info('No new checkpoints detected.');
451
+ } else {
452
+ spinner.succeed(`Found ${result.changes.length} new checkpoint(s)!`);
453
+ console.log();
454
+
455
+ for (const change of result.changes) {
456
+ if (change.type === 'completed') {
457
+ console.log(` ${utils.COLORS.green}✓${utils.COLORS.reset} ${change.label}`);
458
+ console.log(` ${utils.COLORS.dim}${change.reason}${utils.COLORS.reset}`);
459
+ }
460
+ }
461
+ console.log();
462
+ }
463
+
464
+ // Show progress
465
+ console.log(`${utils.COLORS.bold}Progress:${utils.COLORS.reset} ${result.completed}/${result.total} (${Math.round((result.completed / result.total) * 100)}%)`);
466
+ console.log(checkpointEngine.getColoredProgressBar(Math.round((result.completed / result.total) * 100), 30));
467
+ console.log();
468
+
469
+ if (options.dryRun) {
470
+ utils.print.dim('Dry run - no changes were saved.');
471
+ } else {
472
+ // Auto-push to server if authenticated
473
+ try {
474
+ const pushResult = await pushCheckpointsToServer(projectRoot, { quiet: true, autoSync: true });
475
+ if (pushResult.success) {
476
+ utils.print.success('Synced to dashboard automatically');
477
+ }
478
+ } catch {
479
+ // Silent fail - push is best-effort
480
+ }
481
+ }
482
+
483
+ // Emit telemetry
484
+ telemetry.emitEvent('checkpoint:sync', {
485
+ changes: result.changes.length,
486
+ completed: result.completed,
487
+ total: result.total
488
+ }, { projectRoot });
489
+ }
490
+
491
+ /**
492
+ * Set project type
493
+ */
494
+ function handleType(projectRoot: string, type: string | undefined): void {
495
+ const validTypes = Object.values(projectState.PROJECT_TYPES);
496
+
497
+ if (!type) {
498
+ // Show current type
499
+ const state = projectState.loadState(projectRoot);
500
+ if (!state) {
501
+ utils.print.error('Checkpoint tracking not initialized.');
502
+ return;
503
+ }
504
+
505
+ console.log(`
506
+ ${utils.COLORS.cyan}${utils.COLORS.bold}⚡ Project Type${utils.COLORS.reset}
507
+
508
+ Current type: ${getTypeBadge(state.projectType)}
509
+ `);
510
+
511
+ console.log(`${utils.COLORS.bold}Available types:${utils.COLORS.reset}`);
512
+ for (const t of validTypes) {
513
+ const badge = getTypeBadge(t);
514
+ const current = t === state.projectType ? ' (current)' : '';
515
+ console.log(` ${badge} ${t}${current}`);
516
+ }
517
+ console.log();
518
+
519
+ console.log(`${utils.COLORS.bold}Change type:${utils.COLORS.reset}`);
520
+ console.log(' bootspring checkpoint type <type>');
521
+ console.log();
522
+ return;
523
+ }
524
+
525
+ if (!validTypes.includes(type)) {
526
+ utils.print.error(`Invalid project type: ${type}`);
527
+ console.log(`\n${utils.COLORS.bold}Valid types:${utils.COLORS.reset} ${validTypes.join(', ')}`);
528
+ return;
529
+ }
530
+
531
+ const updatedState = projectState.setProjectType(projectRoot, type);
532
+
533
+ if (updatedState) {
534
+ utils.print.success(`Project type changed to: ${type}`);
535
+
536
+ // Sync checkpoints for new type
537
+ const syncResult = checkpointEngine.syncCheckpoints(projectRoot, { verbose: true });
538
+ console.log(`\nCheckpoints: ${syncResult.completed}/${syncResult.total} complete`);
539
+
540
+ telemetry.emitEvent('checkpoint:type-change', {
541
+ newType: type
542
+ }, { projectRoot });
543
+ }
544
+ }
545
+
546
+ /**
547
+ * Push checkpoints to dashboard
548
+ * Sends raw file scan data - server evaluates checkpoints and calculates scores
549
+ */
550
+ async function handlePush(projectRoot: string, _options: Record<string, unknown> = {}): Promise<void> {
551
+ const spinner = utils.createSpinner('Scanning and syncing to dashboard...');
552
+ spinner.start();
553
+
554
+ try {
555
+ const result = await pushCheckpointsToServer(projectRoot, { quiet: false });
556
+
557
+ if (!result.success) {
558
+ if (result.reason === 'not_authenticated') {
559
+ spinner.fail('Not authenticated. Run: bootspring auth login');
560
+ } else if (result.reason === 'no_project') {
561
+ spinner.fail('No project selected. Run: bootspring switch <project>');
562
+ } else {
563
+ spinner.fail('Failed to sync checkpoints');
564
+ }
565
+ return;
566
+ }
567
+
568
+ spinner.succeed('Checkpoints synced to dashboard!');
569
+
570
+ // Display server-evaluated results
571
+ console.log(`
572
+ ${utils.COLORS.bold}Sync Summary:${utils.COLORS.reset}
573
+ Project Type: ${result.projectType}
574
+ Checkpoints: ${result.progress?.completed}/${result.progress?.total} (${result.progress?.percentage}%)
575
+ Health Score: ${result.healthScore}%
576
+ Last Sync: ${result.lastSyncAt ? new Date(result.lastSyncAt).toLocaleString() : 'Now'}
577
+ `);
578
+
579
+ // Show health breakdown
580
+ if (result.healthBreakdown) {
581
+ console.log(`${utils.COLORS.bold}Health Breakdown:${utils.COLORS.reset}`);
582
+ for (const [key, value] of Object.entries(result.healthBreakdown)) {
583
+ const label = key.charAt(0).toUpperCase() + key.slice(1).replace(/([A-Z])/g, ' $1');
584
+ console.log(` ${label}: ${value}%`);
585
+ }
586
+ console.log();
587
+ }
588
+
589
+ // Emit telemetry
590
+ telemetry.emitEvent('checkpoint:push', {
591
+ healthScore: result.healthScore,
592
+ checkpointsCompleted: result.progress?.completed
593
+ }, { projectRoot });
594
+
595
+ } catch (error) {
596
+ spinner.fail(`Failed to sync: ${(error as Error).message}`);
597
+ }
598
+ }
599
+
600
+ /**
601
+ * Detect project type automatically
602
+ */
603
+ function handleDetect(projectRoot: string): void {
604
+ const detectedType = projectState.detectProjectType(projectRoot);
605
+ const state = projectState.loadState(projectRoot);
606
+ const currentType = state?.projectType;
607
+
608
+ console.log(`
609
+ ${utils.COLORS.cyan}${utils.COLORS.bold}⚡ Project Type Detection${utils.COLORS.reset}
610
+
611
+ Detected type: ${getTypeBadge(detectedType)}
612
+ `);
613
+
614
+ if (currentType && currentType !== detectedType) {
615
+ console.log(`Current type: ${getTypeBadge(currentType)}`);
616
+ console.log(`\n${utils.COLORS.bold}To change:${utils.COLORS.reset} bootspring checkpoint type ${detectedType}`);
617
+ } else if (!currentType) {
618
+ console.log(`${utils.COLORS.bold}To initialize:${utils.COLORS.reset} bootspring checkpoint init --type=${detectedType}`);
619
+ } else {
620
+ console.log(`${utils.COLORS.dim}Current type matches detected type.${utils.COLORS.reset}`);
621
+ }
622
+ console.log();
623
+ }
624
+
625
+ /**
626
+ * Show help
627
+ */
628
+ function showHelp(): void {
629
+ console.log(`
630
+ ${utils.COLORS.cyan}${utils.COLORS.bold}⚡ Bootspring Checkpoints${utils.COLORS.reset}
631
+ ${utils.COLORS.dim}Track project milestones and progress${utils.COLORS.reset}
632
+
633
+ ${utils.COLORS.bold}Usage:${utils.COLORS.reset}
634
+ bootspring checkpoint [command] [options]
635
+
636
+ ${utils.COLORS.bold}Commands:${utils.COLORS.reset}
637
+ status Show all checkpoints with status (default)
638
+ init Initialize checkpoint tracking
639
+ complete <id> Mark a checkpoint as complete
640
+ history View checkpoint completion timeline
641
+ sync Re-scan files to detect completed items
642
+ type [type] Show or set project type
643
+ detect Auto-detect project type from files
644
+ push Sync checkpoints to dashboard
645
+
646
+ ${utils.COLORS.bold}Options:${utils.COLORS.reset}
647
+ --json Output as JSON
648
+ --force Force reinitialize
649
+ --dry-run Don't save changes (for sync)
650
+ --type=<type> Set project type (dev, content, business)
651
+ --notes="..." Add notes when completing
652
+
653
+ ${utils.COLORS.bold}Project Types:${utils.COLORS.reset}
654
+ development Software development projects
655
+ content Content/editorial projects
656
+ business Business planning projects
657
+
658
+ ${utils.COLORS.bold}Examples:${utils.COLORS.reset}
659
+ bootspring checkpoint # Show status
660
+ bootspring checkpoint init # Initialize tracking
661
+ bootspring checkpoint complete prd # Complete the PRD checkpoint
662
+ bootspring checkpoint sync # Detect completed items
663
+ bootspring checkpoint type business # Change to business type
664
+ `);
665
+ }
666
+
667
+ // ============================================================================
668
+ // Main Runner
669
+ // ============================================================================
670
+
671
+ /**
672
+ * Run checkpoint command
673
+ */
674
+ export async function run(args: string[]): Promise<void> {
675
+ const cfg = config.load();
676
+ const projectRoot = cfg._projectRoot;
677
+
678
+ // Parse arguments
679
+ const parsed = utils.parseArgs(args);
680
+ const subcommand = parsed._[0] || 'status';
681
+ const options = {
682
+ json: parsed.json || false,
683
+ force: parsed.force || false,
684
+ dryRun: parsed['dry-run'] || parsed.dryRun || false,
685
+ type: parsed.type || null,
686
+ notes: parsed.notes || null
687
+ };
688
+
689
+ switch (subcommand) {
690
+ case 'status':
691
+ case 'show':
692
+ case 's':
693
+ await handleStatus(projectRoot, options);
694
+ break;
695
+
696
+ case 'init':
697
+ case 'initialize':
698
+ handleInit(projectRoot, options);
699
+ break;
700
+
701
+ case 'complete':
702
+ case 'done':
703
+ case 'c':
704
+ handleComplete(projectRoot, parsed._[1], options);
705
+ break;
706
+
707
+ case 'history':
708
+ case 'log':
709
+ case 'h':
710
+ handleHistory(projectRoot, options);
711
+ break;
712
+
713
+ case 'sync':
714
+ case 'scan':
715
+ await handleSync(projectRoot, options);
716
+ break;
717
+
718
+ case 'type':
719
+ case 't':
720
+ handleType(projectRoot, parsed._[1] || options.type || undefined);
721
+ break;
722
+
723
+ case 'detect':
724
+ case 'd':
725
+ handleDetect(projectRoot);
726
+ break;
727
+
728
+ case 'push':
729
+ case 'upload':
730
+ await handlePush(projectRoot, options);
731
+ break;
732
+
733
+ case 'help':
734
+ case '-h':
735
+ case '--help':
736
+ showHelp();
737
+ break;
738
+
739
+ default:
740
+ utils.print.error(`Unknown subcommand: ${subcommand}`);
741
+ showHelp();
742
+ }
743
+ }
744
+
745
+ // ============================================================================
746
+ // Exports
747
+ // ============================================================================
748
+
749
+ export {
750
+ handleStatus,
751
+ handleInit,
752
+ handleComplete,
753
+ handleHistory,
754
+ handleSync,
755
+ handleType,
756
+ handleDetect
757
+ };