@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.
- package/bin/bootspring.js +5 -0
- package/cli/org.js +474 -0
- package/cli/preseed/index.js +16 -0
- package/cli/preseed/interactive.js +143 -0
- package/cli/preseed/templates.js +227 -0
- package/cli/preseed.js +9 -301
- package/cli/seed/builders/ai-context-builder.js +85 -0
- package/cli/seed/builders/index.js +13 -0
- package/cli/seed/builders/seed-builder.js +272 -0
- package/cli/seed/extractors/content-extractors.js +383 -0
- package/cli/seed/extractors/index.js +47 -0
- package/cli/seed/extractors/metadata-extractors.js +167 -0
- package/cli/seed/extractors/section-extractor.js +54 -0
- package/cli/seed/extractors/stack-extractors.js +228 -0
- package/cli/seed/index.js +18 -0
- package/cli/seed/utils/folder-structure.js +84 -0
- package/cli/seed/utils/index.js +11 -0
- package/cli/seed.js +23 -1074
- package/core/api-client.js +77 -0
- package/core/entitlements.js +36 -0
- package/core/organizations.js +223 -0
- package/core/policies.js +51 -6
- package/core/policy-matrix.js +303 -0
- package/core/project-context.js +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.js +3220 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/context-McpJQa_2.d.ts +5710 -0
- package/dist/core/index.d.ts +635 -0
- package/dist/core/index.js +2593 -0
- package/dist/core/index.js.map +1 -0
- package/dist/index-QqbeEiDm.d.ts +857 -0
- package/dist/index-UiYCgwiH.d.ts +174 -0
- package/dist/index.d.ts +453 -0
- package/dist/index.js +44228 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/index.d.ts +1 -0
- package/dist/mcp/index.js +41173 -0
- package/dist/mcp/index.js.map +1 -0
- package/generators/index.ts +82 -0
- package/intelligence/orchestrator/config/failure-signatures.js +48 -0
- package/intelligence/orchestrator/config/index.js +23 -0
- package/intelligence/orchestrator/config/pack-lifecycle.js +262 -0
- package/intelligence/orchestrator/config/phases.js +111 -0
- package/intelligence/orchestrator/config/remediation.js +150 -0
- package/intelligence/orchestrator/config/workflows.js +168 -0
- package/intelligence/orchestrator/core/index.js +16 -0
- package/intelligence/orchestrator/core/state-manager.js +88 -0
- package/intelligence/orchestrator/core/telemetry.js +24 -0
- package/intelligence/orchestrator/index.js +17 -0
- package/intelligence/orchestrator.js +17 -512
- package/mcp/contracts/mcp-contract.v1.json +1 -1
- package/package.json +16 -3
- package/src/cli/agent.ts +703 -0
- package/src/cli/analyze.ts +640 -0
- package/src/cli/audit.ts +707 -0
- package/src/cli/auth.ts +930 -0
- package/src/cli/billing.ts +364 -0
- package/src/cli/build.ts +1089 -0
- package/src/cli/business.ts +508 -0
- package/src/cli/checkpoint-utils.ts +236 -0
- package/src/cli/checkpoint.ts +757 -0
- package/src/cli/cloud-sync.ts +534 -0
- package/src/cli/content.ts +273 -0
- package/src/cli/context.ts +667 -0
- package/src/cli/dashboard.ts +133 -0
- package/src/cli/deploy.ts +704 -0
- package/src/cli/doctor.ts +480 -0
- package/src/cli/fundraise.ts +494 -0
- package/src/cli/generate.ts +346 -0
- package/src/cli/github-cmd.ts +566 -0
- package/src/cli/health.ts +599 -0
- package/src/cli/index.ts +113 -0
- package/src/cli/init.ts +838 -0
- package/src/cli/legal.ts +495 -0
- package/src/cli/log.ts +316 -0
- package/src/cli/loop.ts +1660 -0
- package/src/cli/manager.ts +878 -0
- package/src/cli/mcp.ts +275 -0
- package/src/cli/memory.ts +346 -0
- package/src/cli/metrics.ts +590 -0
- package/src/cli/monitor.ts +960 -0
- package/src/cli/mvp.ts +662 -0
- package/src/cli/onboard.ts +663 -0
- package/src/cli/orchestrator.ts +622 -0
- package/src/cli/plugin.ts +483 -0
- package/src/cli/prd.ts +671 -0
- package/src/cli/preseed-start.ts +1633 -0
- package/src/cli/preseed.ts +2434 -0
- package/src/cli/project.ts +526 -0
- package/src/cli/quality.ts +885 -0
- package/src/cli/security.ts +1079 -0
- package/src/cli/seed.ts +1224 -0
- package/src/cli/skill.ts +537 -0
- package/src/cli/suggest.ts +1225 -0
- package/src/cli/switch.ts +518 -0
- package/src/cli/task.ts +780 -0
- package/src/cli/telemetry.ts +172 -0
- package/src/cli/todo.ts +627 -0
- package/src/cli/types.ts +15 -0
- package/src/cli/update.ts +334 -0
- package/src/cli/visualize.ts +609 -0
- package/src/cli/watch.ts +895 -0
- package/src/cli/workspace.ts +709 -0
- package/src/core/action-recorder.ts +673 -0
- package/src/core/analyze-workflow.ts +1453 -0
- package/src/core/api-client.ts +1120 -0
- package/src/core/audit-workflow.ts +1681 -0
- package/src/core/auth.ts +471 -0
- package/src/core/build-orchestrator.ts +509 -0
- package/src/core/build-state.ts +621 -0
- package/src/core/checkpoint-engine.ts +482 -0
- package/src/core/config.ts +1285 -0
- package/src/core/context-loader.ts +694 -0
- package/src/core/context.ts +410 -0
- package/src/core/deploy-workflow.ts +1085 -0
- package/src/core/entitlements.ts +322 -0
- package/src/core/github-sync.ts +720 -0
- package/src/core/index.ts +981 -0
- package/src/core/ingest.ts +1186 -0
- package/src/core/metrics-engine.ts +886 -0
- package/src/core/mvp.ts +847 -0
- package/src/core/onboard-workflow.ts +1293 -0
- package/src/core/policies.ts +81 -0
- package/src/core/preseed-workflow.ts +1163 -0
- package/src/core/preseed.ts +1826 -0
- package/src/core/project-context.ts +380 -0
- package/src/core/project-state.ts +699 -0
- package/src/core/r2-sync.ts +691 -0
- package/src/core/scaffold.ts +1715 -0
- package/src/core/session.ts +286 -0
- package/src/core/task-extractor.ts +799 -0
- package/src/core/telemetry.ts +371 -0
- package/src/core/tier-enforcement.ts +737 -0
- package/src/core/utils.ts +437 -0
- package/src/index.ts +29 -0
- package/src/intelligence/agent-collab.ts +2376 -0
- package/src/intelligence/auto-suggest.ts +713 -0
- package/src/intelligence/content-gen.ts +1351 -0
- package/src/intelligence/cross-project.ts +1692 -0
- package/src/intelligence/git-memory.ts +529 -0
- package/src/intelligence/index.ts +318 -0
- package/src/intelligence/orchestrator.ts +534 -0
- package/src/intelligence/prd.ts +466 -0
- package/src/intelligence/recommendations.ts +982 -0
- package/src/intelligence/workflow-composer.ts +1472 -0
- package/src/mcp/capabilities.ts +233 -0
- package/src/mcp/index.ts +37 -0
- package/src/mcp/registry.ts +1268 -0
- package/src/mcp/response-formatter.ts +797 -0
- package/src/mcp/server.ts +240 -0
- package/src/types/agent.ts +69 -0
- package/src/types/config.ts +86 -0
- package/src/types/context.ts +77 -0
- package/src/types/index.ts +53 -0
- package/src/types/mcp.ts +91 -0
- package/src/types/skills.ts +47 -0
- package/src/types/workflow.ts +155 -0
- package/generators/index.js +0 -18
package/src/cli/audit.ts
ADDED
|
@@ -0,0 +1,707 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bootspring Audit CLI
|
|
3
|
+
*
|
|
4
|
+
* Quality, security, and best practices audit for codebases.
|
|
5
|
+
* Generates prioritized recommendations and remediation tasks.
|
|
6
|
+
*
|
|
7
|
+
* @package bootspring
|
|
8
|
+
* @module cli/audit
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
// Import JS modules with type interfaces
|
|
12
|
+
interface ParsedArgs {
|
|
13
|
+
_: string[];
|
|
14
|
+
phase?: string | undefined;
|
|
15
|
+
severity?: string | undefined;
|
|
16
|
+
fix?: boolean | undefined;
|
|
17
|
+
ci?: boolean | undefined;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface PhaseConfig {
|
|
21
|
+
name: string;
|
|
22
|
+
description: string;
|
|
23
|
+
required: boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface QualityResult {
|
|
27
|
+
score: number;
|
|
28
|
+
breakdown: {
|
|
29
|
+
complexity: number;
|
|
30
|
+
maintainability: number;
|
|
31
|
+
documentation: number;
|
|
32
|
+
codeSmells: number;
|
|
33
|
+
};
|
|
34
|
+
totalIssues: number;
|
|
35
|
+
totalFiles: number;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
interface SecurityResult {
|
|
39
|
+
passed: boolean;
|
|
40
|
+
total: number;
|
|
41
|
+
critical: number;
|
|
42
|
+
high: number;
|
|
43
|
+
medium: number;
|
|
44
|
+
low: number;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
interface PerformanceResult {
|
|
48
|
+
score: number;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
interface PracticesResult {
|
|
52
|
+
passed: number;
|
|
53
|
+
total: number;
|
|
54
|
+
checks: Record<string, {
|
|
55
|
+
passed: boolean;
|
|
56
|
+
issues: Array<{ title: string }>;
|
|
57
|
+
}>;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
interface TechDebtResult {
|
|
61
|
+
todos: number;
|
|
62
|
+
unusedDeps: number;
|
|
63
|
+
testCoverage: number;
|
|
64
|
+
depAnalysisSkipped?: boolean | undefined;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
interface RecommendationsResult {
|
|
68
|
+
reportPath: string;
|
|
69
|
+
passed: boolean;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
interface FindingsSummary {
|
|
73
|
+
total: number;
|
|
74
|
+
critical: number;
|
|
75
|
+
high: number;
|
|
76
|
+
medium: number;
|
|
77
|
+
low: number;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
interface OverallProgress {
|
|
81
|
+
completed: number;
|
|
82
|
+
total: number;
|
|
83
|
+
percentage: number;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
interface PhaseProgress {
|
|
87
|
+
name: string;
|
|
88
|
+
status: string;
|
|
89
|
+
required: boolean;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
interface WorkflowProgress {
|
|
93
|
+
overall: OverallProgress;
|
|
94
|
+
isComplete: boolean;
|
|
95
|
+
findings: FindingsSummary;
|
|
96
|
+
phases: PhaseProgress[];
|
|
97
|
+
startedAt?: string | undefined;
|
|
98
|
+
lastUpdated?: string | undefined;
|
|
99
|
+
summary?: {
|
|
100
|
+
reportPath?: string | undefined;
|
|
101
|
+
} | undefined;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
interface ResumePoint {
|
|
105
|
+
phaseName: string;
|
|
106
|
+
findingsCount: number;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
interface AuditWorkflowEngineClass {
|
|
110
|
+
new (projectRoot: string, options?: AuditOptions): AuditWorkflowEngine;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
interface AuditOptions {
|
|
114
|
+
severityFilter?: string | null | undefined;
|
|
115
|
+
ciMode?: boolean | undefined;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
interface AuditWorkflowEngine {
|
|
119
|
+
hasWorkflow(): boolean;
|
|
120
|
+
loadState(): void;
|
|
121
|
+
initializeWorkflow(): void;
|
|
122
|
+
getProgress(): WorkflowProgress;
|
|
123
|
+
getResumePoint(): ResumePoint | null;
|
|
124
|
+
getNextPhase(): string | null;
|
|
125
|
+
startPhase(phaseId: string): void;
|
|
126
|
+
completePhase(phaseId: string, result: unknown): void;
|
|
127
|
+
failPhase(phaseId: string, error: string): void;
|
|
128
|
+
skipPhase(phaseId: string): void;
|
|
129
|
+
resetWorkflow(): void;
|
|
130
|
+
getExitCode(): number;
|
|
131
|
+
runQualityMetrics(): Promise<QualityResult>;
|
|
132
|
+
runSecurityScan(): Promise<SecurityResult>;
|
|
133
|
+
runPerformanceAnalysis(): Promise<PerformanceResult>;
|
|
134
|
+
runBestPractices(): Promise<PracticesResult>;
|
|
135
|
+
runTechDebtInventory(): Promise<TechDebtResult>;
|
|
136
|
+
runRecommendations(): Promise<RecommendationsResult>;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
interface AuditWorkflowModule {
|
|
140
|
+
AuditWorkflowEngine: AuditWorkflowEngineClass;
|
|
141
|
+
AUDIT_PHASES: Record<string, PhaseConfig>;
|
|
142
|
+
SEVERITY_LEVELS: Record<string, unknown>;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
146
|
+
const utils = require('../../core/utils') as typeof import('../core/utils');
|
|
147
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
148
|
+
const auditWorkflowModule = require('../../core/audit-workflow') as AuditWorkflowModule;
|
|
149
|
+
|
|
150
|
+
const { AuditWorkflowEngine, AUDIT_PHASES } = auditWorkflowModule;
|
|
151
|
+
|
|
152
|
+
// Get project root
|
|
153
|
+
const projectRoot = process.cwd();
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Show audit help
|
|
157
|
+
*/
|
|
158
|
+
function showHelp(): void {
|
|
159
|
+
console.log(`
|
|
160
|
+
${utils.COLORS.bold}Bootspring Audit${utils.COLORS.reset}
|
|
161
|
+
Quality, security, and best practices audit
|
|
162
|
+
|
|
163
|
+
${utils.COLORS.bold}Usage:${utils.COLORS.reset}
|
|
164
|
+
bootspring audit Full audit
|
|
165
|
+
bootspring audit status Show progress
|
|
166
|
+
bootspring audit resume Continue from checkpoint
|
|
167
|
+
bootspring audit reset Reset workflow
|
|
168
|
+
|
|
169
|
+
${utils.COLORS.bold}Options:${utils.COLORS.reset}
|
|
170
|
+
--phase=<phase> Run specific phase only
|
|
171
|
+
--severity=<level> Filter by severity (critical, high, medium, low)
|
|
172
|
+
--fix Auto-fix safe issues (where possible)
|
|
173
|
+
--ci CI mode with exit codes
|
|
174
|
+
|
|
175
|
+
${utils.COLORS.bold}Phases:${utils.COLORS.reset}
|
|
176
|
+
quality Code quality metrics (complexity, duplication)
|
|
177
|
+
security Security scan (secrets, vulnerabilities)
|
|
178
|
+
performance Performance analysis
|
|
179
|
+
practices Best practices checks
|
|
180
|
+
techDebt Tech debt inventory
|
|
181
|
+
recommendations Generate prioritized recommendations
|
|
182
|
+
|
|
183
|
+
${utils.COLORS.bold}Exit Codes (CI Mode):${utils.COLORS.reset}
|
|
184
|
+
0 All checks passed
|
|
185
|
+
1 High severity issues found
|
|
186
|
+
2 Critical issues found
|
|
187
|
+
|
|
188
|
+
${utils.COLORS.bold}Notes:${utils.COLORS.reset}
|
|
189
|
+
Large codebases (500+ files) automatically skip heavy dependency
|
|
190
|
+
analysis in the tech debt phase for performance.
|
|
191
|
+
|
|
192
|
+
${utils.COLORS.bold}Examples:${utils.COLORS.reset}
|
|
193
|
+
bootspring audit Auto-adjusts for codebase size
|
|
194
|
+
bootspring audit --phase=security
|
|
195
|
+
bootspring audit --severity=high
|
|
196
|
+
bootspring audit --ci
|
|
197
|
+
`);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Start audit workflow
|
|
202
|
+
*/
|
|
203
|
+
async function auditStart(args: ParsedArgs): Promise<void> {
|
|
204
|
+
const workflow = new AuditWorkflowEngine(projectRoot, {
|
|
205
|
+
severityFilter: args.severity || null,
|
|
206
|
+
ciMode: args.ci || false
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
// Check for existing workflow
|
|
210
|
+
if (workflow.hasWorkflow()) {
|
|
211
|
+
workflow.loadState();
|
|
212
|
+
const progress = workflow.getProgress();
|
|
213
|
+
|
|
214
|
+
if (!progress.isComplete) {
|
|
215
|
+
utils.print.info('Existing audit workflow found.');
|
|
216
|
+
console.log(` Progress: ${progress.overall.percentage}% complete`);
|
|
217
|
+
console.log(` Findings: ${progress.findings.total}`);
|
|
218
|
+
console.log('');
|
|
219
|
+
utils.print.info('Use "bootspring audit resume" to continue');
|
|
220
|
+
utils.print.info('Use "bootspring audit reset" to start fresh');
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Initialize new workflow
|
|
226
|
+
utils.print.header('Codebase Audit');
|
|
227
|
+
console.log('Running quality, security, and best practices checks...');
|
|
228
|
+
console.log('');
|
|
229
|
+
|
|
230
|
+
workflow.initializeWorkflow();
|
|
231
|
+
|
|
232
|
+
// Run workflow
|
|
233
|
+
await runWorkflowLoop(workflow, args);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Resume audit workflow
|
|
238
|
+
*/
|
|
239
|
+
async function auditResume(args: ParsedArgs): Promise<void> {
|
|
240
|
+
const workflow = new AuditWorkflowEngine(projectRoot);
|
|
241
|
+
|
|
242
|
+
if (!workflow.hasWorkflow()) {
|
|
243
|
+
utils.print.error('No existing workflow found.');
|
|
244
|
+
utils.print.info('Run "bootspring audit" to start.');
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
workflow.loadState();
|
|
249
|
+
const resumePoint = workflow.getResumePoint();
|
|
250
|
+
|
|
251
|
+
if (!resumePoint) {
|
|
252
|
+
utils.print.success('Audit workflow already complete!');
|
|
253
|
+
displayCompletionSummary(workflow, args);
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
utils.print.header('Resuming Audit');
|
|
258
|
+
console.log(`Continuing from: ${resumePoint.phaseName}`);
|
|
259
|
+
console.log(`Current findings: ${resumePoint.findingsCount}`);
|
|
260
|
+
console.log('');
|
|
261
|
+
|
|
262
|
+
await runWorkflowLoop(workflow, args);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Run the workflow loop
|
|
267
|
+
*/
|
|
268
|
+
async function runWorkflowLoop(workflow: AuditWorkflowEngine, args: ParsedArgs): Promise<void> {
|
|
269
|
+
while (true) {
|
|
270
|
+
const nextPhaseId = workflow.getNextPhase();
|
|
271
|
+
|
|
272
|
+
if (!nextPhaseId) {
|
|
273
|
+
// Workflow complete
|
|
274
|
+
break;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const phase = AUDIT_PHASES[nextPhaseId];
|
|
278
|
+
if (!phase) continue;
|
|
279
|
+
|
|
280
|
+
// Skip optional phases
|
|
281
|
+
if (!phase.required) {
|
|
282
|
+
// For now, skip optional phases in basic flow
|
|
283
|
+
workflow.skipPhase(nextPhaseId);
|
|
284
|
+
continue;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// Run the phase
|
|
288
|
+
workflow.startPhase(nextPhaseId);
|
|
289
|
+
const spinner = utils.createSpinner(`Running: ${phase.name}`).start();
|
|
290
|
+
|
|
291
|
+
try {
|
|
292
|
+
let result: unknown;
|
|
293
|
+
|
|
294
|
+
switch (nextPhaseId) {
|
|
295
|
+
case 'quality': {
|
|
296
|
+
const qualityResult = await workflow.runQualityMetrics();
|
|
297
|
+
spinner.succeed(`Quality metrics: Score ${qualityResult.score}/100`);
|
|
298
|
+
displayQualityResults(qualityResult);
|
|
299
|
+
result = qualityResult;
|
|
300
|
+
break;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
case 'security': {
|
|
304
|
+
const securityResult = await workflow.runSecurityScan();
|
|
305
|
+
if (securityResult.critical > 0) {
|
|
306
|
+
spinner.fail(`Security scan: ${securityResult.critical} CRITICAL issues found`);
|
|
307
|
+
} else if (securityResult.high > 0) {
|
|
308
|
+
spinner.warn(`Security scan: ${securityResult.high} high severity issues found`);
|
|
309
|
+
} else if (securityResult.passed) {
|
|
310
|
+
spinner.succeed('Security scan: PASSED');
|
|
311
|
+
} else {
|
|
312
|
+
spinner.succeed(`Security scan: ${securityResult.total} findings`);
|
|
313
|
+
}
|
|
314
|
+
displaySecurityResults(securityResult);
|
|
315
|
+
result = securityResult;
|
|
316
|
+
break;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
case 'performance': {
|
|
320
|
+
const perfResult = await workflow.runPerformanceAnalysis();
|
|
321
|
+
spinner.succeed('Performance analysis complete');
|
|
322
|
+
result = perfResult;
|
|
323
|
+
break;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
case 'practices': {
|
|
327
|
+
const practicesResult = await workflow.runBestPractices();
|
|
328
|
+
spinner.succeed(`Best practices: ${practicesResult.passed}/${practicesResult.total} checks passed`);
|
|
329
|
+
displayPracticesResults(practicesResult);
|
|
330
|
+
result = practicesResult;
|
|
331
|
+
break;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
case 'techDebt': {
|
|
335
|
+
const techDebtResult = await workflow.runTechDebtInventory();
|
|
336
|
+
if (techDebtResult.depAnalysisSkipped) {
|
|
337
|
+
spinner.succeed(`Tech debt: ${techDebtResult.todos} TODOs (dep analysis skipped - large codebase)`);
|
|
338
|
+
} else {
|
|
339
|
+
spinner.succeed(`Tech debt: ${techDebtResult.todos} TODOs, ${techDebtResult.unusedDeps} unused deps`);
|
|
340
|
+
}
|
|
341
|
+
displayTechDebtResults(techDebtResult);
|
|
342
|
+
result = techDebtResult;
|
|
343
|
+
break;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
case 'recommendations': {
|
|
347
|
+
const recsResult = await workflow.runRecommendations();
|
|
348
|
+
spinner.succeed('Recommendations generated');
|
|
349
|
+
displayRecommendationsResults(recsResult);
|
|
350
|
+
result = recsResult;
|
|
351
|
+
break;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
default:
|
|
355
|
+
spinner.warn(`Unknown phase: ${nextPhaseId}`);
|
|
356
|
+
continue;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
workflow.completePhase(nextPhaseId, result);
|
|
360
|
+
|
|
361
|
+
} catch (error) {
|
|
362
|
+
spinner.fail(`Failed: ${(error as Error).message}`);
|
|
363
|
+
workflow.failPhase(nextPhaseId, (error as Error).message);
|
|
364
|
+
|
|
365
|
+
if (phase.required) {
|
|
366
|
+
utils.print.error('Required phase failed. Cannot continue.');
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
console.log('');
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// Workflow complete
|
|
375
|
+
displayCompletionSummary(workflow, args);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* Display quality results
|
|
380
|
+
*/
|
|
381
|
+
function displayQualityResults(result: QualityResult): void {
|
|
382
|
+
console.log('');
|
|
383
|
+
console.log(` ${utils.COLORS.cyan}Complexity:${utils.COLORS.reset} ${result.breakdown.complexity}/100`);
|
|
384
|
+
console.log(` ${utils.COLORS.cyan}Maintainability:${utils.COLORS.reset} ${result.breakdown.maintainability}/100`);
|
|
385
|
+
console.log(` ${utils.COLORS.cyan}Documentation:${utils.COLORS.reset} ${result.breakdown.documentation}/100`);
|
|
386
|
+
console.log(` ${utils.COLORS.cyan}Code Smells:${utils.COLORS.reset} ${result.breakdown.codeSmells}/100`);
|
|
387
|
+
|
|
388
|
+
if (result.totalIssues > 0) {
|
|
389
|
+
console.log('');
|
|
390
|
+
console.log(` ${utils.COLORS.yellow}○${utils.COLORS.reset} ${result.totalIssues} issues found across ${result.totalFiles} files`);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* Display security results
|
|
396
|
+
*/
|
|
397
|
+
function displaySecurityResults(result: SecurityResult): void {
|
|
398
|
+
if (result.total > 0) {
|
|
399
|
+
console.log('');
|
|
400
|
+
console.log(' Findings:');
|
|
401
|
+
|
|
402
|
+
if (result.critical > 0) {
|
|
403
|
+
console.log(` ${utils.COLORS.red}●${utils.COLORS.reset} Critical: ${result.critical}`);
|
|
404
|
+
}
|
|
405
|
+
if (result.high > 0) {
|
|
406
|
+
console.log(` ${utils.COLORS.yellow}●${utils.COLORS.reset} High: ${result.high}`);
|
|
407
|
+
}
|
|
408
|
+
if (result.medium > 0) {
|
|
409
|
+
console.log(` ${utils.COLORS.yellow}○${utils.COLORS.reset} Medium: ${result.medium}`);
|
|
410
|
+
}
|
|
411
|
+
if (result.low > 0) {
|
|
412
|
+
console.log(` ${utils.COLORS.dim}○${utils.COLORS.reset} Low: ${result.low}`);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* Display best practices results
|
|
419
|
+
*/
|
|
420
|
+
function displayPracticesResults(result: PracticesResult): void {
|
|
421
|
+
if (result.passed < result.total) {
|
|
422
|
+
console.log('');
|
|
423
|
+
for (const [, check] of Object.entries(result.checks)) {
|
|
424
|
+
if (!check.passed) {
|
|
425
|
+
for (const issue of check.issues.slice(0, 2)) {
|
|
426
|
+
console.log(` ${utils.COLORS.yellow}○${utils.COLORS.reset} ${issue.title}`);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* Display tech debt results
|
|
435
|
+
*/
|
|
436
|
+
function displayTechDebtResults(result: TechDebtResult): void {
|
|
437
|
+
console.log('');
|
|
438
|
+
console.log(` ${utils.COLORS.cyan}Test coverage:${utils.COLORS.reset} ${result.testCoverage}%`);
|
|
439
|
+
|
|
440
|
+
if (result.todos > 0) {
|
|
441
|
+
console.log(` ${utils.COLORS.yellow}○${utils.COLORS.reset} ${result.todos} TODO/FIXME comments`);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
if (result.unusedDeps > 0) {
|
|
445
|
+
console.log(` ${utils.COLORS.yellow}○${utils.COLORS.reset} ${result.unusedDeps} potentially unused dependencies`);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
/**
|
|
450
|
+
* Display recommendations results
|
|
451
|
+
*/
|
|
452
|
+
function displayRecommendationsResults(result: RecommendationsResult): void {
|
|
453
|
+
console.log('');
|
|
454
|
+
utils.print.success(`Report saved to: ${result.reportPath}`);
|
|
455
|
+
|
|
456
|
+
if (!result.passed) {
|
|
457
|
+
console.log('');
|
|
458
|
+
utils.print.warning('Audit found issues that need attention.');
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
/**
|
|
463
|
+
* Display completion summary
|
|
464
|
+
*/
|
|
465
|
+
function displayCompletionSummary(workflow: AuditWorkflowEngine, args: ParsedArgs): void {
|
|
466
|
+
const progress = workflow.getProgress();
|
|
467
|
+
|
|
468
|
+
console.log('');
|
|
469
|
+
|
|
470
|
+
// Show audit result
|
|
471
|
+
if (progress.findings.critical > 0) {
|
|
472
|
+
utils.print.error('Audit FAILED: Critical issues found');
|
|
473
|
+
} else if (progress.findings.high > 0) {
|
|
474
|
+
utils.print.warning('Audit completed with high severity issues');
|
|
475
|
+
} else {
|
|
476
|
+
utils.print.success('Audit completed successfully!');
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
console.log('');
|
|
480
|
+
|
|
481
|
+
// Show findings summary
|
|
482
|
+
utils.print.header('Findings Summary');
|
|
483
|
+
|
|
484
|
+
console.log('');
|
|
485
|
+
console.log(` Total findings: ${progress.findings.total}`);
|
|
486
|
+
console.log('');
|
|
487
|
+
|
|
488
|
+
if (progress.findings.critical > 0) {
|
|
489
|
+
console.log(` ${utils.COLORS.red}● Critical:${utils.COLORS.reset} ${progress.findings.critical} ${utils.COLORS.dim}(immediate action required)${utils.COLORS.reset}`);
|
|
490
|
+
}
|
|
491
|
+
if (progress.findings.high > 0) {
|
|
492
|
+
console.log(` ${utils.COLORS.yellow}● High:${utils.COLORS.reset} ${progress.findings.high} ${utils.COLORS.dim}(fix within 1-2 days)${utils.COLORS.reset}`);
|
|
493
|
+
}
|
|
494
|
+
if (progress.findings.medium > 0) {
|
|
495
|
+
console.log(` ${utils.COLORS.yellow}○ Medium:${utils.COLORS.reset} ${progress.findings.medium} ${utils.COLORS.dim}(plan within 1-2 weeks)${utils.COLORS.reset}`);
|
|
496
|
+
}
|
|
497
|
+
if (progress.findings.low > 0) {
|
|
498
|
+
console.log(` ${utils.COLORS.dim}○ Low:${utils.COLORS.reset} ${progress.findings.low} ${utils.COLORS.dim}(add to backlog)${utils.COLORS.reset}`);
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
if (progress.summary?.reportPath) {
|
|
502
|
+
console.log('');
|
|
503
|
+
console.log(' Reports generated:');
|
|
504
|
+
console.log(` ${utils.COLORS.green}✓${utils.COLORS.reset} ${progress.summary.reportPath}`);
|
|
505
|
+
console.log(` ${utils.COLORS.green}✓${utils.COLORS.reset} .bootspring/audit/reports/quality.md`);
|
|
506
|
+
console.log(` ${utils.COLORS.green}✓${utils.COLORS.reset} .bootspring/audit/reports/security.md`);
|
|
507
|
+
console.log(` ${utils.COLORS.green}✓${utils.COLORS.reset} .bootspring/audit/reports/tech-debt.md`);
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
console.log('');
|
|
511
|
+
utils.print.info('Review the detailed report for remediation steps.');
|
|
512
|
+
|
|
513
|
+
// CI mode exit code
|
|
514
|
+
if (args?.ci) {
|
|
515
|
+
const exitCode = workflow.getExitCode();
|
|
516
|
+
process.exitCode = exitCode;
|
|
517
|
+
console.log('');
|
|
518
|
+
console.log(`CI exit code: ${exitCode}`);
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
/**
|
|
523
|
+
* Show audit status
|
|
524
|
+
*/
|
|
525
|
+
async function auditStatus(): Promise<void> {
|
|
526
|
+
const workflow = new AuditWorkflowEngine(projectRoot);
|
|
527
|
+
|
|
528
|
+
if (!workflow.hasWorkflow()) {
|
|
529
|
+
utils.print.info('No audit workflow started.');
|
|
530
|
+
utils.print.info('Run "bootspring audit" to begin.');
|
|
531
|
+
return;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
workflow.loadState();
|
|
535
|
+
const progress = workflow.getProgress();
|
|
536
|
+
|
|
537
|
+
utils.print.header('Audit Status');
|
|
538
|
+
|
|
539
|
+
// Overall progress
|
|
540
|
+
console.log(`Progress: ${progress.overall.percentage}% complete`);
|
|
541
|
+
console.log(`Started: ${progress.startedAt ? new Date(progress.startedAt).toLocaleString() : 'N/A'}`);
|
|
542
|
+
console.log(`Last updated: ${progress.lastUpdated ? utils.formatRelativeTime(new Date(progress.lastUpdated)) : 'N/A'}`);
|
|
543
|
+
console.log('');
|
|
544
|
+
|
|
545
|
+
// Phase status
|
|
546
|
+
console.log('Phases:');
|
|
547
|
+
for (const phase of progress.phases) {
|
|
548
|
+
const statusIcon = getStatusIcon(phase.status);
|
|
549
|
+
const required = phase.required ? '' : ' (optional)';
|
|
550
|
+
console.log(` ${statusIcon} ${phase.name}${required}`);
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
// Findings summary
|
|
554
|
+
if (progress.findings.total > 0) {
|
|
555
|
+
console.log('');
|
|
556
|
+
console.log('Findings so far:');
|
|
557
|
+
console.log(` Critical: ${progress.findings.critical}`);
|
|
558
|
+
console.log(` High: ${progress.findings.high}`);
|
|
559
|
+
console.log(` Medium: ${progress.findings.medium}`);
|
|
560
|
+
console.log(` Low: ${progress.findings.low}`);
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
if (progress.summary?.reportPath) {
|
|
564
|
+
console.log('');
|
|
565
|
+
console.log(`Report: ${progress.summary.reportPath}`);
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
if (!progress.isComplete) {
|
|
569
|
+
console.log('');
|
|
570
|
+
utils.print.info('Run "bootspring audit resume" to continue');
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
/**
|
|
575
|
+
* Run specific phase
|
|
576
|
+
*/
|
|
577
|
+
async function auditPhase(args: ParsedArgs): Promise<void> {
|
|
578
|
+
const phaseId = args.phase;
|
|
579
|
+
|
|
580
|
+
if (!phaseId || !AUDIT_PHASES[phaseId]) {
|
|
581
|
+
utils.print.error(`Unknown phase: ${phaseId}`);
|
|
582
|
+
console.log('');
|
|
583
|
+
console.log('Available phases:');
|
|
584
|
+
for (const [id, phase] of Object.entries(AUDIT_PHASES)) {
|
|
585
|
+
console.log(` ${id}: ${phase.description}`);
|
|
586
|
+
}
|
|
587
|
+
return;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
const workflow = new AuditWorkflowEngine(projectRoot);
|
|
591
|
+
|
|
592
|
+
if (!workflow.hasWorkflow()) {
|
|
593
|
+
workflow.initializeWorkflow();
|
|
594
|
+
} else {
|
|
595
|
+
workflow.loadState();
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
const phase = AUDIT_PHASES[phaseId];
|
|
599
|
+
if (!phase) return;
|
|
600
|
+
|
|
601
|
+
const spinner = utils.createSpinner(`Running: ${phase.name}`).start();
|
|
602
|
+
|
|
603
|
+
try {
|
|
604
|
+
let result: unknown;
|
|
605
|
+
|
|
606
|
+
switch (phaseId) {
|
|
607
|
+
case 'quality':
|
|
608
|
+
result = await workflow.runQualityMetrics();
|
|
609
|
+
break;
|
|
610
|
+
case 'security':
|
|
611
|
+
result = await workflow.runSecurityScan();
|
|
612
|
+
break;
|
|
613
|
+
case 'performance':
|
|
614
|
+
result = await workflow.runPerformanceAnalysis();
|
|
615
|
+
break;
|
|
616
|
+
case 'practices':
|
|
617
|
+
result = await workflow.runBestPractices();
|
|
618
|
+
break;
|
|
619
|
+
case 'techDebt':
|
|
620
|
+
result = await workflow.runTechDebtInventory();
|
|
621
|
+
break;
|
|
622
|
+
case 'recommendations':
|
|
623
|
+
result = await workflow.runRecommendations();
|
|
624
|
+
break;
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
spinner.succeed(`${phase.name} complete`);
|
|
628
|
+
console.log('');
|
|
629
|
+
console.log('Result:', JSON.stringify(result, null, 2));
|
|
630
|
+
|
|
631
|
+
// Show findings for this phase
|
|
632
|
+
const progress = workflow.getProgress();
|
|
633
|
+
if (progress.findings.total > 0) {
|
|
634
|
+
console.log('');
|
|
635
|
+
console.log(`Findings: ${progress.findings.total}`);
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
} catch (error) {
|
|
639
|
+
spinner.fail(`Failed: ${(error as Error).message}`);
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
/**
|
|
644
|
+
* Reset audit workflow
|
|
645
|
+
*/
|
|
646
|
+
async function auditReset(): Promise<void> {
|
|
647
|
+
const workflow = new AuditWorkflowEngine(projectRoot);
|
|
648
|
+
|
|
649
|
+
if (!workflow.hasWorkflow()) {
|
|
650
|
+
utils.print.info('No workflow to reset.');
|
|
651
|
+
return;
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
workflow.resetWorkflow();
|
|
655
|
+
utils.print.success('Audit workflow reset.');
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
/**
|
|
659
|
+
* Get status icon
|
|
660
|
+
*/
|
|
661
|
+
function getStatusIcon(status: string): string {
|
|
662
|
+
switch (status) {
|
|
663
|
+
case 'completed':
|
|
664
|
+
return `${utils.COLORS.green}✓${utils.COLORS.reset}`;
|
|
665
|
+
case 'in_progress':
|
|
666
|
+
return `${utils.COLORS.cyan}●${utils.COLORS.reset}`;
|
|
667
|
+
case 'failed':
|
|
668
|
+
return `${utils.COLORS.red}✗${utils.COLORS.reset}`;
|
|
669
|
+
case 'skipped':
|
|
670
|
+
return `${utils.COLORS.dim}○${utils.COLORS.reset}`;
|
|
671
|
+
default:
|
|
672
|
+
return `${utils.COLORS.dim}○${utils.COLORS.reset}`;
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
/**
|
|
677
|
+
* Main entry point
|
|
678
|
+
*/
|
|
679
|
+
export async function run(args: string[]): Promise<void> {
|
|
680
|
+
const parsedArgs = utils.parseArgs(args) as ParsedArgs;
|
|
681
|
+
const subcommand = parsedArgs._[0];
|
|
682
|
+
|
|
683
|
+
// Handle --phase flag
|
|
684
|
+
if (parsedArgs.phase) {
|
|
685
|
+
return auditPhase(parsedArgs);
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
switch (subcommand) {
|
|
689
|
+
case 'status':
|
|
690
|
+
return auditStatus();
|
|
691
|
+
|
|
692
|
+
case 'resume':
|
|
693
|
+
return auditResume(parsedArgs);
|
|
694
|
+
|
|
695
|
+
case 'reset':
|
|
696
|
+
return auditReset();
|
|
697
|
+
|
|
698
|
+
case 'help':
|
|
699
|
+
case '--help':
|
|
700
|
+
case '-h':
|
|
701
|
+
return showHelp();
|
|
702
|
+
|
|
703
|
+
default:
|
|
704
|
+
// Start audit
|
|
705
|
+
return auditStart(parsedArgs);
|
|
706
|
+
}
|
|
707
|
+
}
|