@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,599 @@
1
+ /**
2
+ * Bootspring Health Command
3
+ * Quick project health overview and status check
4
+ *
5
+ * @package bootspring
6
+ * @module cli/health
7
+ */
8
+
9
+ import * as fs from 'fs';
10
+ import * as path from 'path';
11
+ import { execSync } from 'child_process';
12
+
13
+ // Import JS modules with type interfaces
14
+ interface Config {
15
+ _projectRoot: string;
16
+ project?: { name: string };
17
+ }
18
+
19
+ interface ConfigModule {
20
+ load(): Config;
21
+ }
22
+
23
+ interface Phase {
24
+ status: string;
25
+ }
26
+
27
+ interface WorkflowState {
28
+ phases?: Record<string, Phase>;
29
+ lastUpdated?: string;
30
+ }
31
+
32
+ interface WorkflowStatus {
33
+ exists: boolean;
34
+ progress?: number | undefined;
35
+ lastUpdated?: string | undefined;
36
+ isComplete?: boolean | undefined;
37
+ error?: boolean | undefined;
38
+ }
39
+
40
+ interface TodoSummary {
41
+ exists: boolean;
42
+ pending?: number;
43
+ completed?: number;
44
+ total?: number;
45
+ error?: boolean;
46
+ }
47
+
48
+ interface Vulnerabilities {
49
+ total?: number;
50
+ critical?: number;
51
+ high?: number;
52
+ }
53
+
54
+ interface DependencyHealth {
55
+ exists: boolean;
56
+ dependencies?: number;
57
+ devDependencies?: number;
58
+ total?: number;
59
+ vulnerabilities?: Vulnerabilities;
60
+ error?: boolean;
61
+ }
62
+
63
+ interface QualityMetrics {
64
+ hasTypeScript: boolean;
65
+ hasEslint: boolean;
66
+ hasPrettier: boolean;
67
+ hasTests: boolean;
68
+ testCoverage: number | null;
69
+ }
70
+
71
+ interface MonitorMetrics {
72
+ exists: boolean;
73
+ noData?: boolean | undefined;
74
+ bundleSize?: number | undefined;
75
+ dependencies?: number | undefined;
76
+ timestamp?: string | undefined;
77
+ error?: boolean | undefined;
78
+ }
79
+
80
+ interface CheckpointProgress {
81
+ completed: number;
82
+ total: number;
83
+ percentage: number;
84
+ }
85
+
86
+ interface ProjectStateModule {
87
+ getCheckpointProgress(projectRoot: string): CheckpointProgress | null;
88
+ }
89
+
90
+ interface CheckpointEngineModule {
91
+ getColoredProgressBar(percentage: number, width: number): string;
92
+ }
93
+
94
+ interface HealthData {
95
+ workflows: Record<string, WorkflowStatus>;
96
+ todos: TodoSummary;
97
+ quality: QualityMetrics;
98
+ deps: DependencyHealth;
99
+ monitor: MonitorMetrics;
100
+ checkpoints: CheckpointProgress | null;
101
+ score?: number;
102
+ grade?: string;
103
+ }
104
+
105
+ interface ParsedArgs {
106
+ _: string[];
107
+ json?: boolean;
108
+ }
109
+
110
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
111
+ const config = require('../../core/config') as ConfigModule;
112
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
113
+ const utils = require('../../core/utils') as typeof import('../core/utils');
114
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
115
+ const projectState = require('../../core/project-state') as ProjectStateModule;
116
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
117
+ const checkpointEngine = require('../../core/checkpoint-engine') as CheckpointEngineModule;
118
+
119
+ /**
120
+ * Get workflow status
121
+ */
122
+ export function getWorkflowStatus(projectRoot: string): Record<string, WorkflowStatus> {
123
+ const workflows = ['analyze', 'audit', 'onboard', 'deploy'];
124
+ const status: Record<string, WorkflowStatus> = {};
125
+
126
+ for (const workflow of workflows) {
127
+ const stateFile = path.join(projectRoot, '.bootspring', workflow, 'workflow-state.json');
128
+ if (fs.existsSync(stateFile)) {
129
+ try {
130
+ const state = JSON.parse(fs.readFileSync(stateFile, 'utf-8')) as WorkflowState;
131
+ const completedPhases = Object.values(state.phases || {})
132
+ .filter(p => p.status === 'completed').length;
133
+ const totalPhases = Object.keys(state.phases || {}).length;
134
+
135
+ status[workflow] = {
136
+ exists: true,
137
+ progress: totalPhases > 0 ? Math.round((completedPhases / totalPhases) * 100) : 0,
138
+ lastUpdated: state.lastUpdated,
139
+ isComplete: completedPhases === totalPhases && totalPhases > 0
140
+ };
141
+ } catch {
142
+ status[workflow] = { exists: true, error: true };
143
+ }
144
+ } else {
145
+ status[workflow] = { exists: false };
146
+ }
147
+ }
148
+
149
+ return status;
150
+ }
151
+
152
+ /**
153
+ * Get todo summary
154
+ */
155
+ export function getTodoSummary(projectRoot: string): TodoSummary {
156
+ const todoPath = path.join(projectRoot, 'todo.md');
157
+ if (!fs.existsSync(todoPath)) {
158
+ return { exists: false };
159
+ }
160
+
161
+ try {
162
+ const content = fs.readFileSync(todoPath, 'utf-8');
163
+ const pending = (content.match(/- \[ \]/g) || []).length;
164
+ const completed = (content.match(/- \[x\]/gi) || []).length;
165
+
166
+ return {
167
+ exists: true,
168
+ pending,
169
+ completed,
170
+ total: pending + completed
171
+ };
172
+ } catch {
173
+ return { exists: true, error: true };
174
+ }
175
+ }
176
+
177
+ interface PackageJson {
178
+ dependencies?: Record<string, string>;
179
+ devDependencies?: Record<string, string>;
180
+ }
181
+
182
+ interface AuditResult {
183
+ metadata?: {
184
+ vulnerabilities?: Vulnerabilities;
185
+ };
186
+ }
187
+
188
+ /**
189
+ * Get dependency health
190
+ */
191
+ export function getDependencyHealth(projectRoot: string): DependencyHealth {
192
+ const pkgPath = path.join(projectRoot, 'package.json');
193
+ if (!fs.existsSync(pkgPath)) {
194
+ return { exists: false };
195
+ }
196
+
197
+ try {
198
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8')) as PackageJson;
199
+ const deps = Object.keys(pkg.dependencies || {}).length;
200
+ const devDeps = Object.keys(pkg.devDependencies || {}).length;
201
+
202
+ let vulnerabilities: Vulnerabilities = { total: 0, critical: 0, high: 0 };
203
+
204
+ try {
205
+ const auditResult = execSync('npm audit --json 2>/dev/null', {
206
+ cwd: projectRoot,
207
+ encoding: 'utf-8',
208
+ timeout: 10000,
209
+ stdio: ['pipe', 'pipe', 'pipe']
210
+ });
211
+ const audit = JSON.parse(auditResult) as AuditResult;
212
+ if (audit.metadata && audit.metadata.vulnerabilities) {
213
+ vulnerabilities = audit.metadata.vulnerabilities;
214
+ }
215
+ } catch {
216
+ // Audit failed or timed out
217
+ }
218
+
219
+ return {
220
+ exists: true,
221
+ dependencies: deps,
222
+ devDependencies: devDeps,
223
+ total: deps + devDeps,
224
+ vulnerabilities
225
+ };
226
+ } catch {
227
+ return { exists: true, error: true };
228
+ }
229
+ }
230
+
231
+ interface CoverageSummary {
232
+ total?: {
233
+ lines?: {
234
+ pct?: number;
235
+ };
236
+ };
237
+ }
238
+
239
+ /**
240
+ * Get quality metrics
241
+ */
242
+ export function getQualityMetrics(projectRoot: string): QualityMetrics {
243
+ const metrics: QualityMetrics = {
244
+ hasTypeScript: false,
245
+ hasEslint: false,
246
+ hasPrettier: false,
247
+ hasTests: false,
248
+ testCoverage: null
249
+ };
250
+
251
+ // Check for TypeScript
252
+ if (fs.existsSync(path.join(projectRoot, 'tsconfig.json'))) {
253
+ metrics.hasTypeScript = true;
254
+ }
255
+
256
+ // Check for ESLint
257
+ const eslintConfigs = ['.eslintrc', '.eslintrc.json', '.eslintrc.js', 'eslint.config.js'];
258
+ metrics.hasEslint = eslintConfigs.some(f => fs.existsSync(path.join(projectRoot, f)));
259
+
260
+ // Check for Prettier
261
+ const prettierConfigs = ['.prettierrc', '.prettierrc.json', 'prettier.config.js'];
262
+ metrics.hasPrettier = prettierConfigs.some(f => fs.existsSync(path.join(projectRoot, f)));
263
+
264
+ // Check for tests
265
+ const testDirs = ['__tests__', 'tests', 'test', 'spec'];
266
+ metrics.hasTests = testDirs.some(d => fs.existsSync(path.join(projectRoot, d)));
267
+
268
+ // Check for coverage
269
+ const coveragePath = path.join(projectRoot, 'coverage', 'coverage-summary.json');
270
+ if (fs.existsSync(coveragePath)) {
271
+ try {
272
+ const coverage = JSON.parse(fs.readFileSync(coveragePath, 'utf-8')) as CoverageSummary;
273
+ metrics.testCoverage = coverage.total?.lines?.pct ?? null;
274
+ } catch {
275
+ // Coverage file invalid
276
+ }
277
+ }
278
+
279
+ return metrics;
280
+ }
281
+
282
+ interface MetricsData {
283
+ bundleSize?: number;
284
+ dependencies?: number;
285
+ timestamp?: string;
286
+ }
287
+
288
+ /**
289
+ * Get monitor metrics
290
+ */
291
+ function getMonitorMetrics(projectRoot: string): MonitorMetrics {
292
+ const metricsDir = path.join(projectRoot, '.bootspring', 'monitor', 'metrics');
293
+ if (!fs.existsSync(metricsDir)) {
294
+ return { exists: false };
295
+ }
296
+
297
+ try {
298
+ const files = fs.readdirSync(metricsDir).sort().slice(-1);
299
+ if (files.length === 0) {
300
+ return { exists: true, noData: true };
301
+ }
302
+
303
+ const fileName = files[0];
304
+ if (!fileName) {
305
+ return { exists: true, noData: true };
306
+ }
307
+
308
+ const latest = JSON.parse(fs.readFileSync(path.join(metricsDir, fileName), 'utf-8')) as MetricsData;
309
+ return {
310
+ exists: true,
311
+ bundleSize: latest.bundleSize,
312
+ dependencies: latest.dependencies,
313
+ timestamp: latest.timestamp
314
+ };
315
+ } catch {
316
+ return { exists: true, error: true };
317
+ }
318
+ }
319
+
320
+ /**
321
+ * Calculate overall health score
322
+ */
323
+ export function calculateHealthScore(data: HealthData): number {
324
+ let score = 0;
325
+ let maxScore = 0;
326
+
327
+ // Checkpoints (20 points max)
328
+ if (data.checkpoints && data.checkpoints.total > 0) {
329
+ maxScore += 20;
330
+ score += (data.checkpoints.percentage / 100) * 20;
331
+ }
332
+
333
+ // Workflows (15 points max)
334
+ maxScore += 15;
335
+ const workflowsDone = Object.values(data.workflows)
336
+ .filter(w => w.isComplete).length;
337
+ score += (workflowsDone / 4) * 15;
338
+
339
+ // Todos (10 points max)
340
+ if (data.todos.exists && data.todos.total && data.todos.total > 0 && data.todos.completed !== undefined) {
341
+ maxScore += 10;
342
+ const completionRate = data.todos.completed / data.todos.total;
343
+ score += completionRate * 10;
344
+ }
345
+
346
+ // Quality tooling (25 points max)
347
+ maxScore += 25;
348
+ if (data.quality.hasTypeScript) score += 8;
349
+ if (data.quality.hasEslint) score += 8;
350
+ if (data.quality.hasPrettier) score += 4;
351
+ if (data.quality.hasTests) score += 5;
352
+
353
+ // Test coverage (15 points max)
354
+ if (data.quality.testCoverage !== null) {
355
+ maxScore += 15;
356
+ score += (data.quality.testCoverage / 100) * 15;
357
+ }
358
+
359
+ // Dependencies (15 points max)
360
+ maxScore += 15;
361
+ if (data.deps.exists) {
362
+ const vulns = data.deps.vulnerabilities || {};
363
+ if ((vulns.critical || 0) === 0 && (vulns.high || 0) === 0) {
364
+ score += 15;
365
+ } else if ((vulns.critical || 0) === 0) {
366
+ score += 8;
367
+ }
368
+ }
369
+
370
+ return Math.round((score / maxScore) * 100);
371
+ }
372
+
373
+ /**
374
+ * Get health grade
375
+ */
376
+ function getHealthGrade(score: number): { grade: string; color: string } {
377
+ if (score >= 90) return { grade: 'A', color: utils.COLORS.green };
378
+ if (score >= 80) return { grade: 'B', color: utils.COLORS.green };
379
+ if (score >= 70) return { grade: 'C', color: utils.COLORS.yellow };
380
+ if (score >= 60) return { grade: 'D', color: utils.COLORS.yellow };
381
+ return { grade: 'F', color: utils.COLORS.red };
382
+ }
383
+
384
+ /**
385
+ * Show health report
386
+ */
387
+ function showHealth(projectRoot: string): { score: number; grade: string; data: HealthData } {
388
+ const cfg = config.load();
389
+
390
+ console.log(`
391
+ ${utils.COLORS.cyan}${utils.COLORS.bold}⚡ Project Health Report${utils.COLORS.reset}
392
+ ${utils.COLORS.dim}${cfg.project?.name || 'Unknown Project'}${utils.COLORS.reset}
393
+ `);
394
+
395
+ // Collect all data
396
+ const data: HealthData = {
397
+ workflows: getWorkflowStatus(projectRoot),
398
+ todos: getTodoSummary(projectRoot),
399
+ quality: getQualityMetrics(projectRoot),
400
+ deps: getDependencyHealth(projectRoot),
401
+ monitor: getMonitorMetrics(projectRoot),
402
+ checkpoints: projectState.getCheckpointProgress(projectRoot)
403
+ };
404
+
405
+ // Calculate score
406
+ const score = calculateHealthScore(data);
407
+ const { grade, color } = getHealthGrade(score);
408
+
409
+ // Health Score
410
+ console.log(`${utils.COLORS.bold}Overall Health${utils.COLORS.reset}`);
411
+ console.log(` ${color}${utils.COLORS.bold}${grade}${utils.COLORS.reset} - ${score}/100`);
412
+ console.log();
413
+
414
+ // Checkpoints
415
+ if (data.checkpoints && data.checkpoints.total > 0) {
416
+ console.log(`${utils.COLORS.bold}Checkpoints${utils.COLORS.reset}`);
417
+ const cpBar = checkpointEngine.getColoredProgressBar(data.checkpoints.percentage, 20);
418
+ console.log(` ${cpBar} ${data.checkpoints.completed}/${data.checkpoints.total} (${data.checkpoints.percentage}%)`);
419
+ console.log();
420
+ }
421
+
422
+ // Workflows
423
+ console.log(`${utils.COLORS.bold}Workflows${utils.COLORS.reset}`);
424
+ for (const [name, status] of Object.entries(data.workflows)) {
425
+ if (status.error) {
426
+ console.log(` ${utils.COLORS.yellow}⚠${utils.COLORS.reset} ${name}: Error reading state`);
427
+ } else if (!status.exists) {
428
+ console.log(` ${utils.COLORS.dim}○${utils.COLORS.reset} ${name}: Not started`);
429
+ } else if (status.isComplete) {
430
+ console.log(` ${utils.COLORS.green}✓${utils.COLORS.reset} ${name}: Complete`);
431
+ } else {
432
+ console.log(` ${utils.COLORS.yellow}◐${utils.COLORS.reset} ${name}: ${status.progress}% complete`);
433
+ }
434
+ }
435
+ console.log();
436
+
437
+ // Todos
438
+ console.log(`${utils.COLORS.bold}Tasks${utils.COLORS.reset}`);
439
+ if (!data.todos.exists) {
440
+ console.log(` ${utils.COLORS.dim}No todo.md found${utils.COLORS.reset}`);
441
+ } else {
442
+ const pendingIcon = data.todos.pending === 0 ?
443
+ `${utils.COLORS.green}✓${utils.COLORS.reset}` :
444
+ `${utils.COLORS.yellow}●${utils.COLORS.reset}`;
445
+ console.log(` ${pendingIcon} ${data.todos.pending} pending, ${data.todos.completed} completed`);
446
+ }
447
+ console.log();
448
+
449
+ // Quality
450
+ console.log(`${utils.COLORS.bold}Quality Tooling${utils.COLORS.reset}`);
451
+ const qIcon = (has: boolean): string => has ?
452
+ `${utils.COLORS.green}✓${utils.COLORS.reset}` :
453
+ `${utils.COLORS.dim}○${utils.COLORS.reset}`;
454
+ console.log(` ${qIcon(data.quality.hasTypeScript)} TypeScript`);
455
+ console.log(` ${qIcon(data.quality.hasEslint)} ESLint`);
456
+ console.log(` ${qIcon(data.quality.hasPrettier)} Prettier`);
457
+ console.log(` ${qIcon(data.quality.hasTests)} Tests`);
458
+ if (data.quality.testCoverage !== null) {
459
+ const covColor = data.quality.testCoverage >= 80 ? utils.COLORS.green :
460
+ data.quality.testCoverage >= 60 ? utils.COLORS.yellow : utils.COLORS.red;
461
+ console.log(` ${covColor}●${utils.COLORS.reset} Coverage: ${data.quality.testCoverage}%`);
462
+ }
463
+ console.log();
464
+
465
+ // Dependencies
466
+ console.log(`${utils.COLORS.bold}Dependencies${utils.COLORS.reset}`);
467
+ if (!data.deps.exists) {
468
+ console.log(` ${utils.COLORS.dim}No package.json found${utils.COLORS.reset}`);
469
+ } else {
470
+ console.log(` ${utils.COLORS.dim}●${utils.COLORS.reset} ${data.deps.dependencies} deps, ${data.deps.devDependencies} devDeps`);
471
+ const vulns = data.deps.vulnerabilities || {};
472
+ if ((vulns.critical || 0) > 0) {
473
+ console.log(` ${utils.COLORS.red}✗${utils.COLORS.reset} ${vulns.critical} critical vulnerabilities`);
474
+ } else if ((vulns.high || 0) > 0) {
475
+ console.log(` ${utils.COLORS.yellow}⚠${utils.COLORS.reset} ${vulns.high} high vulnerabilities`);
476
+ } else if ((vulns.total || 0) > 0) {
477
+ console.log(` ${utils.COLORS.yellow}○${utils.COLORS.reset} ${vulns.total} vulnerabilities (low/moderate)`);
478
+ } else {
479
+ console.log(` ${utils.COLORS.green}✓${utils.COLORS.reset} No vulnerabilities`);
480
+ }
481
+ }
482
+ console.log();
483
+
484
+ // Metrics
485
+ if (data.monitor.exists && !data.monitor.noData && !data.monitor.error) {
486
+ console.log(`${utils.COLORS.bold}Latest Metrics${utils.COLORS.reset}`);
487
+ if (data.monitor.bundleSize) {
488
+ console.log(` Bundle size: ${data.monitor.bundleSize} KB`);
489
+ }
490
+ if (data.monitor.timestamp) {
491
+ const date = new Date(data.monitor.timestamp).toLocaleDateString();
492
+ console.log(` ${utils.COLORS.dim}Collected: ${date}${utils.COLORS.reset}`);
493
+ }
494
+ console.log();
495
+ }
496
+
497
+ // Recommendations
498
+ const recommendations: string[] = [];
499
+
500
+ // Checkpoint recommendations
501
+ if (!data.checkpoints || data.checkpoints.total === 0) {
502
+ recommendations.push('Run `bootspring checkpoint init` to start tracking progress');
503
+ } else if (data.checkpoints.percentage < 50) {
504
+ recommendations.push('Complete more checkpoints with `bootspring checkpoint sync`');
505
+ }
506
+
507
+ if (!data.quality.hasTypeScript) {
508
+ recommendations.push('Add TypeScript for type safety');
509
+ }
510
+ if (!data.quality.hasEslint) {
511
+ recommendations.push('Add ESLint for code quality');
512
+ }
513
+ if (!data.quality.hasTests) {
514
+ recommendations.push('Add tests for reliability');
515
+ }
516
+ if (data.todos.pending && data.todos.pending > 5) {
517
+ recommendations.push(`Address ${data.todos.pending} pending tasks`);
518
+ }
519
+ if (!data.workflows.analyze?.isComplete) {
520
+ recommendations.push('Run `bootspring analyze` for codebase insights');
521
+ }
522
+ if (!data.workflows.audit?.isComplete) {
523
+ recommendations.push('Run `bootspring audit` for quality assessment');
524
+ }
525
+ const vulns = data.deps.vulnerabilities || {};
526
+ if ((vulns.critical || 0) > 0 || (vulns.high || 0) > 0) {
527
+ recommendations.push('Run `npm audit fix` to address vulnerabilities');
528
+ }
529
+
530
+ if (recommendations.length > 0) {
531
+ console.log(`${utils.COLORS.bold}Recommendations${utils.COLORS.reset}`);
532
+ recommendations.slice(0, 5).forEach(rec => {
533
+ console.log(` ${utils.COLORS.cyan}→${utils.COLORS.reset} ${rec}`);
534
+ });
535
+ console.log();
536
+ }
537
+
538
+ return { score, grade, data };
539
+ }
540
+
541
+ /**
542
+ * Show help
543
+ */
544
+ function showHelp(): void {
545
+ console.log(`
546
+ ${utils.COLORS.cyan}${utils.COLORS.bold}⚡ Bootspring Health${utils.COLORS.reset}
547
+ ${utils.COLORS.dim}Quick project health overview${utils.COLORS.reset}
548
+
549
+ ${utils.COLORS.bold}Usage:${utils.COLORS.reset}
550
+ bootspring health [options]
551
+
552
+ ${utils.COLORS.bold}Options:${utils.COLORS.reset}
553
+ --json Output as JSON
554
+
555
+ ${utils.COLORS.bold}What it checks:${utils.COLORS.reset}
556
+ - Workflow completion status
557
+ - Todo/task progress
558
+ - Quality tooling (TypeScript, ESLint, Prettier)
559
+ - Test coverage
560
+ - Dependency vulnerabilities
561
+ - Recent metrics
562
+
563
+ ${utils.COLORS.bold}Examples:${utils.COLORS.reset}
564
+ bootspring health
565
+ bootspring health --json
566
+ `);
567
+ }
568
+
569
+ /**
570
+ * Run health command
571
+ */
572
+ export async function run(args: string[]): Promise<void> {
573
+ const parsedArgs = utils.parseArgs(args) as ParsedArgs;
574
+ const subcommand = parsedArgs._[0];
575
+
576
+ if (subcommand === 'help' || subcommand === '-h' || subcommand === '--help') {
577
+ showHelp();
578
+ return;
579
+ }
580
+
581
+ const cfg = config.load();
582
+ const projectRoot = cfg._projectRoot;
583
+
584
+ if (parsedArgs.json) {
585
+ const data: HealthData = {
586
+ workflows: getWorkflowStatus(projectRoot),
587
+ todos: getTodoSummary(projectRoot),
588
+ quality: getQualityMetrics(projectRoot),
589
+ deps: getDependencyHealth(projectRoot),
590
+ monitor: getMonitorMetrics(projectRoot),
591
+ checkpoints: projectState.getCheckpointProgress(projectRoot)
592
+ };
593
+ data.score = calculateHealthScore(data);
594
+ data.grade = getHealthGrade(data.score).grade;
595
+ console.log(JSON.stringify(data, null, 2));
596
+ } else {
597
+ showHealth(projectRoot);
598
+ }
599
+ }