@vibecheckai/cli 2.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +532 -0
- package/dist/autopatch/verified-autopatch.d.ts +111 -0
- package/dist/autopatch/verified-autopatch.d.ts.map +1 -0
- package/dist/autopatch/verified-autopatch.js +503 -0
- package/dist/autopatch/verified-autopatch.js.map +1 -0
- package/dist/bundles/guardrail-core.js +25799 -0
- package/dist/bundles/guardrail-security.js +208687 -0
- package/dist/bundles/guardrail-ship.js +2318 -0
- package/dist/bundles/index.js +8 -0
- package/dist/commands/autopilot-decision.d.ts +24 -0
- package/dist/commands/autopilot-decision.d.ts.map +1 -0
- package/dist/commands/autopilot-decision.js +304 -0
- package/dist/commands/autopilot-decision.js.map +1 -0
- package/dist/commands/autopilot.d.ts +33 -0
- package/dist/commands/autopilot.d.ts.map +1 -0
- package/dist/commands/autopilot.js +1539 -0
- package/dist/commands/autopilot.js.map +1 -0
- package/dist/commands/baseline.d.ts +7 -0
- package/dist/commands/baseline.d.ts.map +1 -0
- package/dist/commands/baseline.js +79 -0
- package/dist/commands/baseline.js.map +1 -0
- package/dist/commands/cache.d.ts +13 -0
- package/dist/commands/cache.d.ts.map +1 -0
- package/dist/commands/cache.js +165 -0
- package/dist/commands/cache.js.map +1 -0
- package/dist/commands/checkpoint.d.ts +8 -0
- package/dist/commands/checkpoint.d.ts.map +1 -0
- package/dist/commands/checkpoint.js +35 -0
- package/dist/commands/checkpoint.js.map +1 -0
- package/dist/commands/context.d.ts +8 -0
- package/dist/commands/context.d.ts.map +1 -0
- package/dist/commands/context.js +340 -0
- package/dist/commands/context.js.map +1 -0
- package/dist/commands/debug.d.ts +78 -0
- package/dist/commands/debug.d.ts.map +1 -0
- package/dist/commands/debug.js +381 -0
- package/dist/commands/debug.js.map +1 -0
- package/dist/commands/doctor.d.ts +17 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +226 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/evidence.d.ts +45 -0
- package/dist/commands/evidence.d.ts.map +1 -0
- package/dist/commands/evidence.js +197 -0
- package/dist/commands/evidence.js.map +1 -0
- package/dist/commands/explain.d.ts +8 -0
- package/dist/commands/explain.d.ts.map +1 -0
- package/dist/commands/explain.js +52 -0
- package/dist/commands/explain.js.map +1 -0
- package/dist/commands/fix-consolidated.d.ts +19 -0
- package/dist/commands/fix-consolidated.d.ts.map +1 -0
- package/dist/commands/fix-consolidated.js +165 -0
- package/dist/commands/fix-consolidated.js.map +1 -0
- package/dist/commands/index.d.ts +8 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/index.js +15 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/commands/init.d.ts +8 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +125 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/launcher.d.ts +10 -0
- package/dist/commands/launcher.d.ts.map +1 -0
- package/dist/commands/launcher.js +174 -0
- package/dist/commands/launcher.js.map +1 -0
- package/dist/commands/on.d.ts +8 -0
- package/dist/commands/on.d.ts.map +1 -0
- package/dist/commands/on.js +123 -0
- package/dist/commands/on.js.map +1 -0
- package/dist/commands/preview.d.ts +54 -0
- package/dist/commands/preview.d.ts.map +1 -0
- package/dist/commands/preview.js +352 -0
- package/dist/commands/preview.js.map +1 -0
- package/dist/commands/quality/check.d.ts +31 -0
- package/dist/commands/quality/check.d.ts.map +1 -0
- package/dist/commands/quality/check.js +242 -0
- package/dist/commands/quality/check.js.map +1 -0
- package/dist/commands/quality/index.d.ts +8 -0
- package/dist/commands/quality/index.d.ts.map +1 -0
- package/dist/commands/quality/index.js +14 -0
- package/dist/commands/quality/index.js.map +1 -0
- package/dist/commands/quality/setup-quality.d.ts +23 -0
- package/dist/commands/quality/setup-quality.d.ts.map +1 -0
- package/dist/commands/quality/setup-quality.js +452 -0
- package/dist/commands/quality/setup-quality.js.map +1 -0
- package/dist/commands/quality/tidy.d.ts +41 -0
- package/dist/commands/quality/tidy.d.ts.map +1 -0
- package/dist/commands/quality/tidy.js +466 -0
- package/dist/commands/quality/tidy.js.map +1 -0
- package/dist/commands/quality/utils.d.ts +73 -0
- package/dist/commands/quality/utils.d.ts.map +1 -0
- package/dist/commands/quality/utils.js +158 -0
- package/dist/commands/quality/utils.js.map +1 -0
- package/dist/commands/replay.d.ts +8 -0
- package/dist/commands/replay.d.ts.map +1 -0
- package/dist/commands/replay.js +52 -0
- package/dist/commands/replay.js.map +1 -0
- package/dist/commands/scan-consolidated.d.ts +61 -0
- package/dist/commands/scan-consolidated.d.ts.map +1 -0
- package/dist/commands/scan-consolidated.js +243 -0
- package/dist/commands/scan-consolidated.js.map +1 -0
- package/dist/commands/scan-secrets.d.ts +47 -0
- package/dist/commands/scan-secrets.d.ts.map +1 -0
- package/dist/commands/scan-secrets.js +225 -0
- package/dist/commands/scan-secrets.js.map +1 -0
- package/dist/commands/scan-vulnerabilities-enhanced.d.ts +41 -0
- package/dist/commands/scan-vulnerabilities-enhanced.d.ts.map +1 -0
- package/dist/commands/scan-vulnerabilities-enhanced.js +368 -0
- package/dist/commands/scan-vulnerabilities-enhanced.js.map +1 -0
- package/dist/commands/scan-vulnerabilities-osv.d.ts +58 -0
- package/dist/commands/scan-vulnerabilities-osv.d.ts.map +1 -0
- package/dist/commands/scan-vulnerabilities-osv.js +716 -0
- package/dist/commands/scan-vulnerabilities-osv.js.map +1 -0
- package/dist/commands/scan-vulnerabilities.d.ts +32 -0
- package/dist/commands/scan-vulnerabilities.d.ts.map +1 -0
- package/dist/commands/scan-vulnerabilities.js +283 -0
- package/dist/commands/scan-vulnerabilities.js.map +1 -0
- package/dist/commands/secrets-allowlist.d.ts +7 -0
- package/dist/commands/secrets-allowlist.d.ts.map +1 -0
- package/dist/commands/secrets-allowlist.js +85 -0
- package/dist/commands/secrets-allowlist.js.map +1 -0
- package/dist/commands/ship-consolidated.d.ts +58 -0
- package/dist/commands/ship-consolidated.d.ts.map +1 -0
- package/dist/commands/ship-consolidated.js +515 -0
- package/dist/commands/ship-consolidated.js.map +1 -0
- package/dist/commands/stats.d.ts +8 -0
- package/dist/commands/stats.d.ts.map +1 -0
- package/dist/commands/stats.js +134 -0
- package/dist/commands/stats.js.map +1 -0
- package/dist/commands/upgrade.d.ts +8 -0
- package/dist/commands/upgrade.d.ts.map +1 -0
- package/dist/commands/upgrade.js +30 -0
- package/dist/commands/upgrade.js.map +1 -0
- package/dist/fix/analytics.d.ts +121 -0
- package/dist/fix/analytics.d.ts.map +1 -0
- package/dist/fix/analytics.js +289 -0
- package/dist/fix/analytics.js.map +1 -0
- package/dist/fix/applicator.d.ts +44 -0
- package/dist/fix/applicator.d.ts.map +1 -0
- package/dist/fix/applicator.js +144 -0
- package/dist/fix/applicator.js.map +1 -0
- package/dist/fix/audit.d.ts +61 -0
- package/dist/fix/audit.d.ts.map +1 -0
- package/dist/fix/audit.js +149 -0
- package/dist/fix/audit.js.map +1 -0
- package/dist/fix/backup.d.ts +38 -0
- package/dist/fix/backup.d.ts.map +1 -0
- package/dist/fix/backup.js +154 -0
- package/dist/fix/backup.js.map +1 -0
- package/dist/fix/config.d.ts +78 -0
- package/dist/fix/config.d.ts.map +1 -0
- package/dist/fix/config.js +200 -0
- package/dist/fix/config.js.map +1 -0
- package/dist/fix/engine.d.ts +55 -0
- package/dist/fix/engine.d.ts.map +1 -0
- package/dist/fix/engine.js +285 -0
- package/dist/fix/engine.js.map +1 -0
- package/dist/fix/impact.d.ts +74 -0
- package/dist/fix/impact.d.ts.map +1 -0
- package/dist/fix/impact.js +281 -0
- package/dist/fix/impact.js.map +1 -0
- package/dist/fix/index.d.ts +5 -0
- package/dist/fix/index.d.ts.map +1 -0
- package/dist/fix/index.js +12 -0
- package/dist/fix/index.js.map +1 -0
- package/dist/fix/interactive.d.ts +22 -0
- package/dist/fix/interactive.d.ts.map +1 -0
- package/dist/fix/interactive.js +172 -0
- package/dist/fix/interactive.js.map +1 -0
- package/dist/fix/learning.d.ts +109 -0
- package/dist/fix/learning.d.ts.map +1 -0
- package/dist/fix/learning.js +296 -0
- package/dist/fix/learning.js.map +1 -0
- package/dist/fix/metrics.d.ts +106 -0
- package/dist/fix/metrics.d.ts.map +1 -0
- package/dist/fix/metrics.js +138 -0
- package/dist/fix/metrics.js.map +1 -0
- package/dist/fix/parallel.d.ts +69 -0
- package/dist/fix/parallel.d.ts.map +1 -0
- package/dist/fix/parallel.js +203 -0
- package/dist/fix/parallel.js.map +1 -0
- package/dist/fix/report.d.ts +40 -0
- package/dist/fix/report.d.ts.map +1 -0
- package/dist/fix/report.js +212 -0
- package/dist/fix/report.js.map +1 -0
- package/dist/fix/strategy.d.ts +53 -0
- package/dist/fix/strategy.d.ts.map +1 -0
- package/dist/fix/strategy.js +143 -0
- package/dist/fix/strategy.js.map +1 -0
- package/dist/fix/templates.d.ts +58 -0
- package/dist/fix/templates.d.ts.map +1 -0
- package/dist/fix/templates.js +259 -0
- package/dist/fix/templates.js.map +1 -0
- package/dist/fix/testing.d.ts +68 -0
- package/dist/fix/testing.d.ts.map +1 -0
- package/dist/fix/testing.js +245 -0
- package/dist/fix/testing.js.map +1 -0
- package/dist/fix/validation.d.ts +71 -0
- package/dist/fix/validation.d.ts.map +1 -0
- package/dist/fix/validation.js +267 -0
- package/dist/fix/validation.js.map +1 -0
- package/dist/fix/visualization.d.ts +73 -0
- package/dist/fix/visualization.d.ts.map +1 -0
- package/dist/fix/visualization.js +243 -0
- package/dist/fix/visualization.js.map +1 -0
- package/dist/formatters/index.d.ts +6 -0
- package/dist/formatters/index.d.ts.map +1 -0
- package/dist/formatters/index.js +11 -0
- package/dist/formatters/index.js.map +1 -0
- package/dist/formatters/sarif-enhanced.d.ts +78 -0
- package/dist/formatters/sarif-enhanced.d.ts.map +1 -0
- package/dist/formatters/sarif-enhanced.js +144 -0
- package/dist/formatters/sarif-enhanced.js.map +1 -0
- package/dist/formatters/sarif-v2.d.ts +121 -0
- package/dist/formatters/sarif-v2.d.ts.map +1 -0
- package/dist/formatters/sarif-v2.js +356 -0
- package/dist/formatters/sarif-v2.js.map +1 -0
- package/dist/formatters/sarif.d.ts +72 -0
- package/dist/formatters/sarif.d.ts.map +1 -0
- package/dist/formatters/sarif.js +146 -0
- package/dist/formatters/sarif.js.map +1 -0
- package/dist/index.d.ts +61 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4455 -0
- package/dist/index.js.map +1 -0
- package/dist/init/ci-generator.d.ts +18 -0
- package/dist/init/ci-generator.d.ts.map +1 -0
- package/dist/init/ci-generator.js +317 -0
- package/dist/init/ci-generator.js.map +1 -0
- package/dist/init/detect-framework.d.ts +15 -0
- package/dist/init/detect-framework.d.ts.map +1 -0
- package/dist/init/detect-framework.js +301 -0
- package/dist/init/detect-framework.js.map +1 -0
- package/dist/init/hooks-installer.d.ts +22 -0
- package/dist/init/hooks-installer.d.ts.map +1 -0
- package/dist/init/hooks-installer.js +310 -0
- package/dist/init/hooks-installer.js.map +1 -0
- package/dist/init/index.d.ts +8 -0
- package/dist/init/index.d.ts.map +1 -0
- package/dist/init/index.js +22 -0
- package/dist/init/index.js.map +1 -0
- package/dist/init/templates.d.ts +401 -0
- package/dist/init/templates.d.ts.map +1 -0
- package/dist/init/templates.js +240 -0
- package/dist/init/templates.js.map +1 -0
- package/dist/mcp/server.d.ts +12 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +42 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/telemetry.d.ts +40 -0
- package/dist/mcp/telemetry.d.ts.map +1 -0
- package/dist/mcp/telemetry.js +98 -0
- package/dist/mcp/telemetry.js.map +1 -0
- package/dist/reality/no-dead-buttons/button-sweep-generator.d.ts +32 -0
- package/dist/reality/no-dead-buttons/button-sweep-generator.d.ts.map +1 -0
- package/dist/reality/no-dead-buttons/button-sweep-generator.js +236 -0
- package/dist/reality/no-dead-buttons/button-sweep-generator.js.map +1 -0
- package/dist/reality/no-dead-buttons/index.d.ts +11 -0
- package/dist/reality/no-dead-buttons/index.d.ts.map +1 -0
- package/dist/reality/no-dead-buttons/index.js +18 -0
- package/dist/reality/no-dead-buttons/index.js.map +1 -0
- package/dist/reality/no-dead-buttons/static-scanner.d.ts +34 -0
- package/dist/reality/no-dead-buttons/static-scanner.d.ts.map +1 -0
- package/dist/reality/no-dead-buttons/static-scanner.js +230 -0
- package/dist/reality/no-dead-buttons/static-scanner.js.map +1 -0
- package/dist/reality/reality-graph.d.ts +192 -0
- package/dist/reality/reality-graph.d.ts.map +1 -0
- package/dist/reality/reality-graph.js +600 -0
- package/dist/reality/reality-graph.js.map +1 -0
- package/dist/reality/reality-runner.d.ts +89 -0
- package/dist/reality/reality-runner.d.ts.map +1 -0
- package/dist/reality/reality-runner.js +540 -0
- package/dist/reality/reality-runner.js.map +1 -0
- package/dist/reality/receipt-generator.d.ts +152 -0
- package/dist/reality/receipt-generator.d.ts.map +1 -0
- package/dist/reality/receipt-generator.js +495 -0
- package/dist/reality/receipt-generator.js.map +1 -0
- package/dist/reality/runtime-tracer.d.ts +75 -0
- package/dist/reality/runtime-tracer.d.ts.map +1 -0
- package/dist/reality/runtime-tracer.js +109 -0
- package/dist/reality/runtime-tracer.js.map +1 -0
- package/dist/runtime/auth-utils.d.ts +43 -0
- package/dist/runtime/auth-utils.d.ts.map +1 -0
- package/dist/runtime/auth-utils.js +130 -0
- package/dist/runtime/auth-utils.js.map +1 -0
- package/dist/runtime/cli-errors.d.ts +38 -0
- package/dist/runtime/cli-errors.d.ts.map +1 -0
- package/dist/runtime/cli-errors.js +354 -0
- package/dist/runtime/cli-errors.js.map +1 -0
- package/dist/runtime/client.d.ts +74 -0
- package/dist/runtime/client.d.ts.map +1 -0
- package/dist/runtime/client.js +222 -0
- package/dist/runtime/client.js.map +1 -0
- package/dist/runtime/creds.d.ts +48 -0
- package/dist/runtime/creds.d.ts.map +1 -0
- package/dist/runtime/creds.js +245 -0
- package/dist/runtime/creds.js.map +1 -0
- package/dist/runtime/exit-codes.d.ts +49 -0
- package/dist/runtime/exit-codes.d.ts.map +1 -0
- package/dist/runtime/exit-codes.js +93 -0
- package/dist/runtime/exit-codes.js.map +1 -0
- package/dist/runtime/index.d.ts +9 -0
- package/dist/runtime/index.d.ts.map +1 -0
- package/dist/runtime/index.js +25 -0
- package/dist/runtime/index.js.map +1 -0
- package/dist/runtime/json-output.d.ts +42 -0
- package/dist/runtime/json-output.d.ts.map +1 -0
- package/dist/runtime/json-output.js +59 -0
- package/dist/runtime/json-output.js.map +1 -0
- package/dist/runtime/owner-mode.d.ts +48 -0
- package/dist/runtime/owner-mode.d.ts.map +1 -0
- package/dist/runtime/owner-mode.js +284 -0
- package/dist/runtime/owner-mode.js.map +1 -0
- package/dist/runtime/semver.d.ts +37 -0
- package/dist/runtime/semver.d.ts.map +1 -0
- package/dist/runtime/semver.js +110 -0
- package/dist/runtime/semver.js.map +1 -0
- package/dist/scan/dead-ui-detector.d.ts +48 -0
- package/dist/scan/dead-ui-detector.d.ts.map +1 -0
- package/dist/scan/dead-ui-detector.js +170 -0
- package/dist/scan/dead-ui-detector.js.map +1 -0
- package/dist/scan/playwright-sweep.d.ts +40 -0
- package/dist/scan/playwright-sweep.d.ts.map +1 -0
- package/dist/scan/playwright-sweep.js +216 -0
- package/dist/scan/playwright-sweep.js.map +1 -0
- package/dist/scan/proof-bundle.d.ts +25 -0
- package/dist/scan/proof-bundle.d.ts.map +1 -0
- package/dist/scan/proof-bundle.js +203 -0
- package/dist/scan/proof-bundle.js.map +1 -0
- package/dist/scan/proof-graph.d.ts +59 -0
- package/dist/scan/proof-graph.d.ts.map +1 -0
- package/dist/scan/proof-graph.js +64 -0
- package/dist/scan/proof-graph.js.map +1 -0
- package/dist/scan/reality-sniff.d.ts +56 -0
- package/dist/scan/reality-sniff.d.ts.map +1 -0
- package/dist/scan/reality-sniff.js +200 -0
- package/dist/scan/reality-sniff.js.map +1 -0
- package/dist/scan/structural-verifier.d.ts +20 -0
- package/dist/scan/structural-verifier.d.ts.map +1 -0
- package/dist/scan/structural-verifier.js +112 -0
- package/dist/scan/structural-verifier.js.map +1 -0
- package/dist/scan/verification-engine.d.ts +47 -0
- package/dist/scan/verification-engine.d.ts.map +1 -0
- package/dist/scan/verification-engine.js +141 -0
- package/dist/scan/verification-engine.js.map +1 -0
- package/dist/scanner/baseline.d.ts +52 -0
- package/dist/scanner/baseline.d.ts.map +1 -0
- package/dist/scanner/baseline.js +85 -0
- package/dist/scanner/baseline.js.map +1 -0
- package/dist/scanner/incremental.d.ts +30 -0
- package/dist/scanner/incremental.d.ts.map +1 -0
- package/dist/scanner/incremental.js +82 -0
- package/dist/scanner/incremental.js.map +1 -0
- package/dist/scanner/index.d.ts +8 -0
- package/dist/scanner/index.d.ts.map +1 -0
- package/dist/scanner/index.js +15 -0
- package/dist/scanner/index.js.map +1 -0
- package/dist/scanner/parallel.d.ts +43 -0
- package/dist/scanner/parallel.d.ts.map +1 -0
- package/dist/scanner/parallel.js +99 -0
- package/dist/scanner/parallel.js.map +1 -0
- package/dist/scanner/placeholder-detector.d.ts +56 -0
- package/dist/scanner/placeholder-detector.d.ts.map +1 -0
- package/dist/scanner/placeholder-detector.js +220 -0
- package/dist/scanner/placeholder-detector.js.map +1 -0
- package/dist/scanner/route-detector.d.ts +100 -0
- package/dist/scanner/route-detector.d.ts.map +1 -0
- package/dist/scanner/route-detector.js +455 -0
- package/dist/scanner/route-detector.js.map +1 -0
- package/dist/scanner/scoring.d.ts +67 -0
- package/dist/scanner/scoring.d.ts.map +1 -0
- package/dist/scanner/scoring.js +284 -0
- package/dist/scanner/scoring.js.map +1 -0
- package/dist/ship-baseline.d.ts +56 -0
- package/dist/ship-baseline.d.ts.map +1 -0
- package/dist/ship-baseline.js +194 -0
- package/dist/ship-baseline.js.map +1 -0
- package/dist/ship-config.d.ts +91 -0
- package/dist/ship-config.d.ts.map +1 -0
- package/dist/ship-config.js +133 -0
- package/dist/ship-config.js.map +1 -0
- package/dist/ship-data-loader.d.ts +70 -0
- package/dist/ship-data-loader.d.ts.map +1 -0
- package/dist/ship-data-loader.js +301 -0
- package/dist/ship-data-loader.js.map +1 -0
- package/dist/standalone.d.ts +1 -0
- package/dist/standalone.d.ts.map +1 -0
- package/dist/standalone.js +1 -0
- package/dist/standalone.js.map +1 -0
- package/dist/truth-pack/index.d.ts +102 -0
- package/dist/truth-pack/index.d.ts.map +1 -0
- package/dist/truth-pack/index.js +694 -0
- package/dist/truth-pack/index.js.map +1 -0
- package/dist/ui/frame.d.ts +68 -0
- package/dist/ui/frame.d.ts.map +1 -0
- package/dist/ui/frame.js +165 -0
- package/dist/ui/frame.js.map +1 -0
- package/dist/ui/index.d.ts +5 -0
- package/dist/ui/index.d.ts.map +1 -0
- package/dist/ui/index.js +16 -0
- package/dist/ui/index.js.map +1 -0
- package/dist/ui.d.ts +36 -0
- package/dist/ui.d.ts.map +1 -0
- package/dist/ui.js +45 -0
- package/dist/ui.js.map +1 -0
- package/dist/utils/ai-helpers.d.ts +72 -0
- package/dist/utils/ai-helpers.d.ts.map +1 -0
- package/dist/utils/ai-helpers.js +339 -0
- package/dist/utils/ai-helpers.js.map +1 -0
- package/dist/utils/validation.d.ts +34 -0
- package/dist/utils/validation.d.ts.map +1 -0
- package/dist/utils/validation.js +160 -0
- package/dist/utils/validation.js.map +1 -0
- package/package.json +66 -0
|
@@ -0,0 +1,1539 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* autopilot command
|
|
4
|
+
*
|
|
5
|
+
* Purpose:
|
|
6
|
+
* Orchestrates the Autopilot batch remediation system that:
|
|
7
|
+
* 1. Scans the project for issues
|
|
8
|
+
* 2. Groups findings into fix packs by strategy
|
|
9
|
+
* 3. Applies fixes in batches with verification
|
|
10
|
+
* 4. Rolls back on verification failure
|
|
11
|
+
*
|
|
12
|
+
* Usage:
|
|
13
|
+
* guardrail autopilot [--dry-run] [--interactive] [--max-fixes=N] [--evidence]
|
|
14
|
+
*
|
|
15
|
+
* Failure modes:
|
|
16
|
+
* - Authentication/entitlement failures: exits with AUTH_FAILURE
|
|
17
|
+
* - Invalid project path: exits with USER_ERROR
|
|
18
|
+
* - Scan failures: exits with SYSTEM_ERROR
|
|
19
|
+
* - Verification failures in dry-run: shows plan but exits with POLICY_FAIL
|
|
20
|
+
* - Verification failures in apply mode: rolls back automatically, exits with POLICY_FAIL
|
|
21
|
+
* - Git operations failures: continues with backup-based rollback
|
|
22
|
+
*
|
|
23
|
+
* This is a PRO feature and requires autopilot entitlement.
|
|
24
|
+
*/
|
|
25
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
26
|
+
if (k2 === undefined) k2 = k;
|
|
27
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
28
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
29
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
30
|
+
}
|
|
31
|
+
Object.defineProperty(o, k2, desc);
|
|
32
|
+
}) : (function(o, m, k, k2) {
|
|
33
|
+
if (k2 === undefined) k2 = k;
|
|
34
|
+
o[k2] = m[k];
|
|
35
|
+
}));
|
|
36
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
37
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
38
|
+
}) : function(o, v) {
|
|
39
|
+
o["default"] = v;
|
|
40
|
+
});
|
|
41
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
42
|
+
var ownKeys = function(o) {
|
|
43
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
44
|
+
var ar = [];
|
|
45
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
46
|
+
return ar;
|
|
47
|
+
};
|
|
48
|
+
return ownKeys(o);
|
|
49
|
+
};
|
|
50
|
+
return function (mod) {
|
|
51
|
+
if (mod && mod.__esModule) return mod;
|
|
52
|
+
var result = {};
|
|
53
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
54
|
+
__setModuleDefault(result, mod);
|
|
55
|
+
return result;
|
|
56
|
+
};
|
|
57
|
+
})();
|
|
58
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
59
|
+
exports.runAutopilotCommand = runAutopilotCommand;
|
|
60
|
+
exports.registerAutopilotCommand = registerAutopilotCommand;
|
|
61
|
+
const path_1 = require("path");
|
|
62
|
+
const fs_1 = require("fs");
|
|
63
|
+
const readline = __importStar(require("readline"));
|
|
64
|
+
const crypto = __importStar(require("crypto"));
|
|
65
|
+
const core_1 = require('guardrail-core');
|
|
66
|
+
const exit_codes_1 = require("../runtime/exit-codes");
|
|
67
|
+
const evidence_1 = require("./evidence");
|
|
68
|
+
const c = {
|
|
69
|
+
bold: (s) => `\x1b[1m${s}\x1b[0m`,
|
|
70
|
+
dim: (s) => `\x1b[2m${s}\x1b[0m`,
|
|
71
|
+
critical: (s) => `\x1b[35m${s}\x1b[0m`,
|
|
72
|
+
high: (s) => `\x1b[31m${s}\x1b[0m`,
|
|
73
|
+
medium: (s) => `\x1b[33m${s}\x1b[0m`,
|
|
74
|
+
low: (s) => `\x1b[36m${s}\x1b[0m`,
|
|
75
|
+
success: (s) => `\x1b[32m${s}\x1b[0m`,
|
|
76
|
+
info: (s) => `\x1b[34m${s}\x1b[0m`,
|
|
77
|
+
error: (s) => `\x1b[31m${s}\x1b[0m`,
|
|
78
|
+
cyan: (s) => `\x1b[36m${s}\x1b[0m`,
|
|
79
|
+
yellow: (s) => `\x1b[33m${s}\x1b[0m`,
|
|
80
|
+
};
|
|
81
|
+
/**
|
|
82
|
+
* Simple spinner implementation for progress indication
|
|
83
|
+
*/
|
|
84
|
+
function createSpinner(text) {
|
|
85
|
+
const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
86
|
+
let i = 0;
|
|
87
|
+
const interval = setInterval(() => {
|
|
88
|
+
process.stdout.write(`\r${c.cyan(frames[i])} ${text}`);
|
|
89
|
+
i = (i + 1) % frames.length;
|
|
90
|
+
}, 80);
|
|
91
|
+
return {
|
|
92
|
+
stop: (success = true, message) => {
|
|
93
|
+
clearInterval(interval);
|
|
94
|
+
const icon = success ? c.success('✓') : c.error('✗');
|
|
95
|
+
process.stdout.write(`\r${icon} ${message || text}${' '.repeat(20)}\n`);
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Format duration in human-readable format
|
|
101
|
+
*/
|
|
102
|
+
function formatDuration(ms) {
|
|
103
|
+
if (ms < 1000)
|
|
104
|
+
return `${ms}ms`;
|
|
105
|
+
if (ms < 60000)
|
|
106
|
+
return `${(ms / 1000).toFixed(1)}s`;
|
|
107
|
+
const minutes = Math.floor(ms / 60000);
|
|
108
|
+
const seconds = Math.floor((ms % 60000) / 1000);
|
|
109
|
+
return `${minutes}m ${seconds}s`;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Format file count with appropriate suffix
|
|
113
|
+
*/
|
|
114
|
+
function formatCount(count, singular, plural) {
|
|
115
|
+
const suffix = count === 1 ? singular : plural || `${singular}s`;
|
|
116
|
+
return `${count} ${suffix}`;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Create a simple table row
|
|
120
|
+
*/
|
|
121
|
+
function tableRow(...cells) {
|
|
122
|
+
return ` ${cells.join(' ')}`;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Create a separator line
|
|
126
|
+
*/
|
|
127
|
+
function separator(length = 80) {
|
|
128
|
+
return c.dim('─'.repeat(length));
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Detect if running in CI/CD environment
|
|
132
|
+
*/
|
|
133
|
+
function isCIEnvironment() {
|
|
134
|
+
return !!(process.env.CI ||
|
|
135
|
+
process.env.CONTINUOUS_INTEGRATION ||
|
|
136
|
+
process.env.GITHUB_ACTIONS ||
|
|
137
|
+
process.env.GITLAB_CI ||
|
|
138
|
+
process.env.JENKINS_URL ||
|
|
139
|
+
process.env.BUILDKITE ||
|
|
140
|
+
process.env.CIRCLECI ||
|
|
141
|
+
process.env.TRAVIS ||
|
|
142
|
+
process.env.TEAMCITY_VERSION);
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Load autopilot configuration from .guardrail/autopilot.json if it exists
|
|
146
|
+
*/
|
|
147
|
+
function loadAutopilotConfig(projectPath) {
|
|
148
|
+
const configPath = (0, path_1.join)(projectPath, '.guardrail', 'autopilot.json');
|
|
149
|
+
if (!(0, fs_1.existsSync)(configPath)) {
|
|
150
|
+
return {};
|
|
151
|
+
}
|
|
152
|
+
try {
|
|
153
|
+
const configContent = (0, fs_1.readFileSync)(configPath, 'utf8');
|
|
154
|
+
const config = JSON.parse(configContent);
|
|
155
|
+
// Validate and return only known options
|
|
156
|
+
const validOptions = {};
|
|
157
|
+
if (typeof config.profile === 'string' &&
|
|
158
|
+
['quick', 'full', 'ship', 'ci'].includes(config.profile)) {
|
|
159
|
+
validOptions.profile = config.profile;
|
|
160
|
+
}
|
|
161
|
+
if (typeof config.maxFixes === 'number' && config.maxFixes > 0) {
|
|
162
|
+
validOptions.maxFixes = config.maxFixes;
|
|
163
|
+
}
|
|
164
|
+
if (typeof config.verify === 'boolean') {
|
|
165
|
+
validOptions.verify = config.verify;
|
|
166
|
+
}
|
|
167
|
+
if (typeof config.force === 'boolean') {
|
|
168
|
+
validOptions.force = config.force;
|
|
169
|
+
}
|
|
170
|
+
if (typeof config.branchStrategy === 'string' &&
|
|
171
|
+
['none', 'worktree', 'copy'].includes(config.branchStrategy)) {
|
|
172
|
+
validOptions.branchStrategy = config.branchStrategy;
|
|
173
|
+
}
|
|
174
|
+
if (typeof config.audit === 'boolean') {
|
|
175
|
+
validOptions.audit = config.audit;
|
|
176
|
+
}
|
|
177
|
+
if (typeof config.compliance === 'boolean') {
|
|
178
|
+
validOptions.compliance = config.compliance;
|
|
179
|
+
}
|
|
180
|
+
if (typeof config.evidence === 'boolean') {
|
|
181
|
+
validOptions.evidence = config.evidence;
|
|
182
|
+
}
|
|
183
|
+
if (config.security && typeof config.security === 'object') {
|
|
184
|
+
validOptions.security = config.security;
|
|
185
|
+
}
|
|
186
|
+
if (Array.isArray(config.complianceFrameworks)) {
|
|
187
|
+
validOptions.complianceFrameworks = config.complianceFrameworks;
|
|
188
|
+
}
|
|
189
|
+
return validOptions;
|
|
190
|
+
}
|
|
191
|
+
catch (err) {
|
|
192
|
+
// Invalid config file, ignore
|
|
193
|
+
return {};
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Validate enterprise security requirements
|
|
198
|
+
*/
|
|
199
|
+
function validateSecurityRequirements(config, options) {
|
|
200
|
+
if (config.security?.requireEvidence &&
|
|
201
|
+
!options.evidence &&
|
|
202
|
+
!options.dryRun) {
|
|
203
|
+
return {
|
|
204
|
+
valid: false,
|
|
205
|
+
error: 'Evidence generation required by enterprise security policy. Use --evidence flag.',
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
if (config.security?.requireVerification && options.verify === false) {
|
|
209
|
+
return {
|
|
210
|
+
valid: false,
|
|
211
|
+
error: 'Verification required by enterprise security policy. Cannot use --no-verify.',
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
if (!config.security?.allowHighRisk && !options.force) {
|
|
215
|
+
// This would be checked during pack application
|
|
216
|
+
// Just a placeholder for enterprise security validation
|
|
217
|
+
}
|
|
218
|
+
return { valid: true };
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Create a simple progress bar
|
|
222
|
+
*/
|
|
223
|
+
function createProgressBar(current, total, width = 30) {
|
|
224
|
+
if (total === 0)
|
|
225
|
+
return c.dim('[' + ' '.repeat(width) + ']');
|
|
226
|
+
const percentage = Math.min(100, Math.round((current / total) * 100));
|
|
227
|
+
const filled = Math.round((current / total) * width);
|
|
228
|
+
const empty = width - filled;
|
|
229
|
+
const filledBar = c.success('█'.repeat(filled));
|
|
230
|
+
const emptyBar = c.dim('░'.repeat(empty));
|
|
231
|
+
return `[${filledBar}${emptyBar}] ${percentage}%`;
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Display progress bar for pack processing
|
|
235
|
+
*/
|
|
236
|
+
function displayPackProgress(current, total, packName) {
|
|
237
|
+
const bar = createProgressBar(current, total);
|
|
238
|
+
process.stdout.write(`\r${c.cyan('⚙')} Processing packs: ${bar} ${c.dim(`(${current}/${total})`)} ${c.dim(packName)}`);
|
|
239
|
+
if (current === total) {
|
|
240
|
+
process.stdout.write('\n');
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Prompt user for confirmation (non-interactive defaults to false for safety)
|
|
245
|
+
*/
|
|
246
|
+
async function promptConfirm(message, defaultValue = false) {
|
|
247
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
248
|
+
return defaultValue;
|
|
249
|
+
}
|
|
250
|
+
const rl = readline.createInterface({
|
|
251
|
+
input: process.stdin,
|
|
252
|
+
output: process.stdout,
|
|
253
|
+
});
|
|
254
|
+
return new Promise((resolve) => {
|
|
255
|
+
const hint = defaultValue ? '[Y/n]' : '[y/N]';
|
|
256
|
+
rl.question(`${message} ${hint}: `, (answer) => {
|
|
257
|
+
rl.close();
|
|
258
|
+
const lower = answer.toLowerCase().trim();
|
|
259
|
+
if (lower === '') {
|
|
260
|
+
resolve(defaultValue);
|
|
261
|
+
}
|
|
262
|
+
else {
|
|
263
|
+
resolve(lower === 'y' || lower === 'yes');
|
|
264
|
+
}
|
|
265
|
+
});
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Enhanced interactive pack selection for --interactive mode
|
|
270
|
+
*/
|
|
271
|
+
async function selectPacksInteractively(packs) {
|
|
272
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY || packs.length === 0) {
|
|
273
|
+
return packs;
|
|
274
|
+
}
|
|
275
|
+
const selected = new Set();
|
|
276
|
+
let currentIndex = 0;
|
|
277
|
+
// Initially select all packs
|
|
278
|
+
packs.forEach((_, i) => selected.add(i));
|
|
279
|
+
return new Promise((resolve) => {
|
|
280
|
+
const rl = readline.createInterface({
|
|
281
|
+
input: process.stdin,
|
|
282
|
+
output: process.stdout,
|
|
283
|
+
});
|
|
284
|
+
const render = () => {
|
|
285
|
+
console.clear();
|
|
286
|
+
console.log(`\n${c.bold('🤖 AUTOPILOT FIX PACK SELECTION')}\n`);
|
|
287
|
+
console.log(`${c.dim('Select packs to apply (Space to toggle, Enter to confirm, Q to quit)')}\n`);
|
|
288
|
+
console.log(`${separator(70)}\n`);
|
|
289
|
+
packs.forEach((pack, i) => {
|
|
290
|
+
const isSelected = selected.has(i);
|
|
291
|
+
const isCurrent = i === currentIndex;
|
|
292
|
+
const checkbox = isSelected ? `${c.success('[✓]')}` : `${c.dim('[ ]')}`;
|
|
293
|
+
const cursor = isCurrent ? `${c.info('❯')}` : ' ';
|
|
294
|
+
const riskColor = pack.estimatedRisk === 'high'
|
|
295
|
+
? c.high
|
|
296
|
+
: pack.estimatedRisk === 'medium'
|
|
297
|
+
? c.medium
|
|
298
|
+
: c.low;
|
|
299
|
+
const priorityBadge = pack.priority <= 3
|
|
300
|
+
? c.high('⚡')
|
|
301
|
+
: pack.priority <= 5
|
|
302
|
+
? c.medium('●')
|
|
303
|
+
: c.dim('○');
|
|
304
|
+
console.log(` ${cursor} ${checkbox} ${priorityBadge} ${c.bold(pack.name)}`);
|
|
305
|
+
console.log(` ${c.dim(pack.description)}`);
|
|
306
|
+
console.log(` ${c.dim(`Findings: ${pack.findings.length} | Files: ${pack.impactedFiles.length} | Risk: ${riskColor(pack.estimatedRisk.toUpperCase())}`)}`);
|
|
307
|
+
console.log('');
|
|
308
|
+
});
|
|
309
|
+
console.log(`${separator(70)}\n`);
|
|
310
|
+
const selectedCount = selected.size;
|
|
311
|
+
console.log(`${c.info(`Selected: ${selectedCount}/${packs.length} pack${selectedCount !== 1 ? 's' : ''}`)}\n`);
|
|
312
|
+
};
|
|
313
|
+
render();
|
|
314
|
+
// Enable raw mode for key detection
|
|
315
|
+
if (process.stdin.setRawMode) {
|
|
316
|
+
process.stdin.setRawMode(true);
|
|
317
|
+
}
|
|
318
|
+
process.stdin.on('data', (key) => {
|
|
319
|
+
const char = key.toString();
|
|
320
|
+
if (char === '\u0003' || char.toLowerCase() === 'q') {
|
|
321
|
+
// Ctrl+C or Q - quit
|
|
322
|
+
if (process.stdin.setRawMode) {
|
|
323
|
+
process.stdin.setRawMode(false);
|
|
324
|
+
}
|
|
325
|
+
rl.close();
|
|
326
|
+
resolve([]);
|
|
327
|
+
}
|
|
328
|
+
else if (char === '\r' || char === '\n') {
|
|
329
|
+
// Enter - confirm
|
|
330
|
+
if (process.stdin.setRawMode) {
|
|
331
|
+
process.stdin.setRawMode(false);
|
|
332
|
+
}
|
|
333
|
+
rl.close();
|
|
334
|
+
const selectedPacks = packs.filter((_, i) => selected.has(i));
|
|
335
|
+
resolve(selectedPacks);
|
|
336
|
+
}
|
|
337
|
+
else if (char === ' ') {
|
|
338
|
+
// Space - toggle selection
|
|
339
|
+
if (selected.has(currentIndex)) {
|
|
340
|
+
selected.delete(currentIndex);
|
|
341
|
+
}
|
|
342
|
+
else {
|
|
343
|
+
selected.add(currentIndex);
|
|
344
|
+
}
|
|
345
|
+
render();
|
|
346
|
+
}
|
|
347
|
+
else if (char === '\u001b[A') {
|
|
348
|
+
// Up arrow
|
|
349
|
+
currentIndex = Math.max(0, currentIndex - 1);
|
|
350
|
+
render();
|
|
351
|
+
}
|
|
352
|
+
else if (char === '\u001b[B') {
|
|
353
|
+
// Down arrow
|
|
354
|
+
currentIndex = Math.min(packs.length - 1, currentIndex + 1);
|
|
355
|
+
render();
|
|
356
|
+
}
|
|
357
|
+
});
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* Convert autopilot results to SARIF format
|
|
362
|
+
*/
|
|
363
|
+
function toSarifAutopilot(result) {
|
|
364
|
+
let version = '1.0.0';
|
|
365
|
+
try {
|
|
366
|
+
const pkg = require('../../package.json');
|
|
367
|
+
version = pkg.version || '1.0.0';
|
|
368
|
+
}
|
|
369
|
+
catch { }
|
|
370
|
+
const runs = [];
|
|
371
|
+
const run = {
|
|
372
|
+
tool: {
|
|
373
|
+
driver: {
|
|
374
|
+
name: 'guardrail-autopilot',
|
|
375
|
+
version,
|
|
376
|
+
informationUri: 'https://guardrail.dev',
|
|
377
|
+
rules: [],
|
|
378
|
+
},
|
|
379
|
+
},
|
|
380
|
+
results: [],
|
|
381
|
+
invocations: [
|
|
382
|
+
{
|
|
383
|
+
executionSuccessful: result.mode === 'apply'
|
|
384
|
+
? result.verification?.passed !== false
|
|
385
|
+
: true,
|
|
386
|
+
startTimeUtc: result.timestamp,
|
|
387
|
+
endTimeUtc: result.mode === 'apply'
|
|
388
|
+
? result.endTime
|
|
389
|
+
: result.timestamp,
|
|
390
|
+
},
|
|
391
|
+
],
|
|
392
|
+
};
|
|
393
|
+
if (result.mode === 'plan') {
|
|
394
|
+
for (const pack of result.packs) {
|
|
395
|
+
for (const finding of pack.findings) {
|
|
396
|
+
run.results.push({
|
|
397
|
+
ruleId: `autopilot-${pack.category}`,
|
|
398
|
+
level: finding.severity === 'critical' || finding.severity === 'high'
|
|
399
|
+
? 'error'
|
|
400
|
+
: finding.severity === 'medium'
|
|
401
|
+
? 'warning'
|
|
402
|
+
: 'note',
|
|
403
|
+
message: { text: finding.message },
|
|
404
|
+
locations: [
|
|
405
|
+
{
|
|
406
|
+
physicalLocation: {
|
|
407
|
+
artifactLocation: { uri: finding.file },
|
|
408
|
+
region: { startLine: finding.line },
|
|
409
|
+
},
|
|
410
|
+
},
|
|
411
|
+
],
|
|
412
|
+
properties: {
|
|
413
|
+
category: pack.category,
|
|
414
|
+
packId: pack.id,
|
|
415
|
+
fixable: finding.fixable,
|
|
416
|
+
},
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
else {
|
|
422
|
+
const applyResult = result;
|
|
423
|
+
for (const fix of applyResult.appliedFixes) {
|
|
424
|
+
run.results.push({
|
|
425
|
+
ruleId: `autopilot-fix-${fix.packId}`,
|
|
426
|
+
level: fix.success ? 'note' : 'warning',
|
|
427
|
+
message: {
|
|
428
|
+
text: fix.success
|
|
429
|
+
? 'Fix applied successfully'
|
|
430
|
+
: `Fix failed: ${fix.error || 'Unknown error'}`,
|
|
431
|
+
},
|
|
432
|
+
locations: [
|
|
433
|
+
{
|
|
434
|
+
physicalLocation: {
|
|
435
|
+
artifactLocation: { uri: fix.file },
|
|
436
|
+
},
|
|
437
|
+
},
|
|
438
|
+
],
|
|
439
|
+
properties: {
|
|
440
|
+
packId: fix.packId,
|
|
441
|
+
findingId: fix.findingId,
|
|
442
|
+
success: fix.success,
|
|
443
|
+
},
|
|
444
|
+
});
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
runs.push(run);
|
|
448
|
+
return {
|
|
449
|
+
$schema: 'https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json',
|
|
450
|
+
version: '2.1.0',
|
|
451
|
+
runs,
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
/**
|
|
455
|
+
* Enhanced plan results output with better formatting
|
|
456
|
+
*/
|
|
457
|
+
function outputPlanResult(result, options) {
|
|
458
|
+
if (options.format === 'sarif' || options.sarif) {
|
|
459
|
+
const sarif = toSarifAutopilot(result);
|
|
460
|
+
console.log(JSON.stringify(sarif, null, 2));
|
|
461
|
+
return;
|
|
462
|
+
}
|
|
463
|
+
if (options.json) {
|
|
464
|
+
console.log(JSON.stringify(result, null, 2));
|
|
465
|
+
return;
|
|
466
|
+
}
|
|
467
|
+
console.log(`\n${separator(80)}`);
|
|
468
|
+
console.log(`${c.bold('📋 AUTOPILOT PLAN')}`);
|
|
469
|
+
console.log(`${separator(80)}\n`);
|
|
470
|
+
// Summary section
|
|
471
|
+
console.log(`${c.bold('Summary')}`);
|
|
472
|
+
console.log(`${c.dim('├─')} Project: ${c.info(result.projectPath)}`);
|
|
473
|
+
console.log(`${c.dim('├─')} Profile: ${c.info(result.profile)}`);
|
|
474
|
+
console.log(`${c.dim('├─')} Timestamp: ${c.dim(result.timestamp)}`);
|
|
475
|
+
console.log(`${c.dim('└─')} Estimated Duration: ${c.yellow(result.estimatedDuration)}\n`);
|
|
476
|
+
// Findings overview
|
|
477
|
+
console.log(`${c.bold('Findings Overview')}`);
|
|
478
|
+
console.log(`${c.dim('├─')} Total Findings: ${c.bold(result.totalFindings.toString())}`);
|
|
479
|
+
console.log(`${c.dim('├─')} Fixable: ${c.success(result.fixableFindings.toString())}`);
|
|
480
|
+
console.log(`${c.dim('└─')} Fix Packs: ${c.info(result.packs.length.toString())}\n`);
|
|
481
|
+
// Risk assessment
|
|
482
|
+
console.log(`${c.bold('Risk Assessment')}`);
|
|
483
|
+
console.log(`${c.dim('├─')} ${c.low('Low Risk:')} ${result.riskAssessment.low}`);
|
|
484
|
+
console.log(`${c.dim('├─')} ${c.medium('Medium Risk:')} ${result.riskAssessment.medium}`);
|
|
485
|
+
console.log(`${c.dim('└─')} ${c.high('High Risk:')} ${result.riskAssessment.high}\n`);
|
|
486
|
+
if (result.packs.length === 0) {
|
|
487
|
+
console.log(`${c.success('✓')} ${c.bold('No fixable issues found')}\n`);
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
490
|
+
// Fix packs table with filtering support
|
|
491
|
+
const filteredPacks = result.packs; // Could be filtered by options.filterCategory, etc.
|
|
492
|
+
console.log(`${c.bold('Fix Packs')} ${c.dim(`(${filteredPacks.length} pack${filteredPacks.length !== 1 ? 's' : ''})`)}`);
|
|
493
|
+
console.log(`${separator(80)}`);
|
|
494
|
+
console.log(tableRow(c.bold('Pack'), c.bold('Findings'), c.bold('Files'), c.bold('Risk'), c.bold('Priority')));
|
|
495
|
+
console.log(`${separator(80)}`);
|
|
496
|
+
for (const pack of filteredPacks) {
|
|
497
|
+
const riskLabel = pack.estimatedRisk === 'high'
|
|
498
|
+
? c.high('HIGH')
|
|
499
|
+
: pack.estimatedRisk === 'medium'
|
|
500
|
+
? c.medium('MEDIUM')
|
|
501
|
+
: c.low('LOW');
|
|
502
|
+
const priorityIcon = pack.priority <= 3
|
|
503
|
+
? c.high('⚡')
|
|
504
|
+
: pack.priority <= 5
|
|
505
|
+
? c.medium('●')
|
|
506
|
+
: c.dim('○');
|
|
507
|
+
console.log(tableRow(`${priorityIcon} ${pack.name}`, pack.findings.length.toString(), pack.impactedFiles.length.toString(), riskLabel, pack.priority.toString()));
|
|
508
|
+
console.log(`${c.dim(' └─')} ${c.dim(pack.description)}`);
|
|
509
|
+
if (pack.impactedFiles.length > 0 && pack.impactedFiles.length <= 5) {
|
|
510
|
+
console.log(`${c.dim(' └─')} Files: ${c.dim(pack.impactedFiles.join(', '))}`);
|
|
511
|
+
}
|
|
512
|
+
else if (pack.impactedFiles.length > 5) {
|
|
513
|
+
console.log(`${c.dim(' └─')} Files: ${c.dim(pack.impactedFiles.slice(0, 3).join(', ') + ` ... +${pack.impactedFiles.length - 3} more`)}`);
|
|
514
|
+
}
|
|
515
|
+
console.log('');
|
|
516
|
+
}
|
|
517
|
+
console.log(`${separator(80)}\n`);
|
|
518
|
+
// Batch operation summary
|
|
519
|
+
if (filteredPacks.length > 1) {
|
|
520
|
+
const totalFindings = filteredPacks.reduce((sum, p) => sum + p.findings.length, 0);
|
|
521
|
+
const totalFiles = new Set(filteredPacks.flatMap((p) => p.impactedFiles))
|
|
522
|
+
.size;
|
|
523
|
+
console.log(`${c.bold('Batch Summary')}`);
|
|
524
|
+
console.log(`${c.dim('├─')} Total Packs: ${c.info(filteredPacks.length.toString())}`);
|
|
525
|
+
console.log(`${c.dim('├─')} Total Findings: ${c.info(totalFindings.toString())}`);
|
|
526
|
+
console.log(`${c.dim('└─')} Unique Files: ${c.info(totalFiles.toString())}\n`);
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
/**
|
|
530
|
+
* Enhanced apply results output with detailed statistics
|
|
531
|
+
*/
|
|
532
|
+
function outputApplyResult(result, options) {
|
|
533
|
+
if (options.format === 'sarif' || options.sarif) {
|
|
534
|
+
const sarif = toSarifAutopilot(result);
|
|
535
|
+
console.log(JSON.stringify(sarif, null, 2));
|
|
536
|
+
return;
|
|
537
|
+
}
|
|
538
|
+
if (options.json) {
|
|
539
|
+
console.log(JSON.stringify(result, null, 2));
|
|
540
|
+
return;
|
|
541
|
+
}
|
|
542
|
+
// Comparison mode handled in scan results section
|
|
543
|
+
console.log(`\n${separator(80)}`);
|
|
544
|
+
console.log(`${c.bold('🤖 AUTOPILOT APPLY RESULTS')}`);
|
|
545
|
+
console.log(`${separator(80)}\n`);
|
|
546
|
+
// Execution summary
|
|
547
|
+
console.log(`${c.bold('Execution Summary')}`);
|
|
548
|
+
console.log(`${c.dim('├─')} Project: ${c.info(result.projectPath)}`);
|
|
549
|
+
console.log(`${c.dim('├─')} Profile: ${c.info(result.profile)}`);
|
|
550
|
+
console.log(`${c.dim('├─')} Run ID: ${c.dim(result.runId || 'N/A')}`);
|
|
551
|
+
console.log(`${c.dim('├─')} Duration: ${c.yellow(formatDuration(result.duration))}`);
|
|
552
|
+
// Performance metrics
|
|
553
|
+
if (result.appliedFixes.length > 0 && result.duration > 0) {
|
|
554
|
+
const fixesPerSecond = (result.appliedFixes.length /
|
|
555
|
+
(result.duration / 1000)).toFixed(1);
|
|
556
|
+
console.log(`${c.dim('└─')} Performance: ${c.info(`${fixesPerSecond} fixes/sec`)}`);
|
|
557
|
+
}
|
|
558
|
+
else {
|
|
559
|
+
console.log(`${c.dim('└─')} Performance: ${c.dim('N/A')}`);
|
|
560
|
+
}
|
|
561
|
+
console.log('');
|
|
562
|
+
// Pack statistics
|
|
563
|
+
console.log(`${c.bold('Pack Statistics')}`);
|
|
564
|
+
const packSuccessRate = result.packsAttempted > 0
|
|
565
|
+
? ((result.packsSucceeded / result.packsAttempted) * 100).toFixed(1)
|
|
566
|
+
: '0.0';
|
|
567
|
+
console.log(`${c.dim('├─')} Attempted: ${result.packsAttempted}`);
|
|
568
|
+
console.log(`${c.dim('├─')} ${c.success('Succeeded:')} ${result.packsSucceeded}`);
|
|
569
|
+
console.log(`${c.dim('├─')} ${c.error('Failed:')} ${result.packsFailed}`);
|
|
570
|
+
console.log(`${c.dim('└─')} Success Rate: ${c.yellow(`${packSuccessRate}%`)}\n`);
|
|
571
|
+
// Fix statistics
|
|
572
|
+
const successfulFixes = result.appliedFixes.filter((f) => f.success);
|
|
573
|
+
const failedFixes = result.appliedFixes.filter((f) => !f.success);
|
|
574
|
+
const fixSuccessRate = result.appliedFixes.length > 0
|
|
575
|
+
? ((successfulFixes.length / result.appliedFixes.length) * 100).toFixed(1)
|
|
576
|
+
: '0.0';
|
|
577
|
+
console.log(`${c.bold('Fix Statistics')}`);
|
|
578
|
+
console.log(`${c.dim('├─')} Total Applied: ${result.appliedFixes.length}`);
|
|
579
|
+
console.log(`${c.dim('├─')} ${c.success('Successful:')} ${successfulFixes.length}`);
|
|
580
|
+
console.log(`${c.dim('├─')} ${c.error('Failed:')} ${failedFixes.length}`);
|
|
581
|
+
console.log(`${c.dim('└─')} Success Rate: ${c.yellow(`${fixSuccessRate}%`)}\n`);
|
|
582
|
+
// Verification results
|
|
583
|
+
if (result.verification) {
|
|
584
|
+
const v = result.verification;
|
|
585
|
+
console.log(`${c.bold('Verification Results')}`);
|
|
586
|
+
console.log(`${c.dim('├─')} Typecheck: ${v.typecheck.passed ? c.success('✓ PASS') : c.error('✗ FAIL')}`);
|
|
587
|
+
if (v.typecheck.errors.length > 0) {
|
|
588
|
+
v.typecheck.errors.slice(0, 3).forEach((err) => {
|
|
589
|
+
console.log(`${c.dim('│ └─')} ${c.dim(err.substring(0, 70))}${err.length > 70 ? '...' : ''}`);
|
|
590
|
+
});
|
|
591
|
+
}
|
|
592
|
+
console.log(`${c.dim('├─')} Build: ${v.build.passed ? c.success('✓ PASS') : c.error('✗ FAIL')}`);
|
|
593
|
+
if (v.build.errors.length > 0) {
|
|
594
|
+
v.build.errors.slice(0, 3).forEach((err) => {
|
|
595
|
+
console.log(`${c.dim('│ └─')} ${c.dim(err.substring(0, 70))}${err.length > 70 ? '...' : ''}`);
|
|
596
|
+
});
|
|
597
|
+
}
|
|
598
|
+
console.log(`${c.dim('├─')} Tests: ${v.tests.passed ? c.success('✓ PASS') : c.error('✗ FAIL')}`);
|
|
599
|
+
if (v.tests.errors.length > 0) {
|
|
600
|
+
v.tests.errors.slice(0, 3).forEach((err) => {
|
|
601
|
+
console.log(`${c.dim('│ └─')} ${c.dim(err.substring(0, 70))}${err.length > 70 ? '...' : ''}`);
|
|
602
|
+
});
|
|
603
|
+
}
|
|
604
|
+
console.log(`${c.dim('└─')} Overall: ${v.passed ? c.success('✓ PASS') : c.error('✗ FAIL')} ${c.dim(`(${formatDuration(v.duration)})`)}\n`);
|
|
605
|
+
}
|
|
606
|
+
// Scan results comparison
|
|
607
|
+
console.log(`${c.bold('Scan Results Comparison')}`);
|
|
608
|
+
const successfulFixesCount = result.appliedFixes.filter((f) => f.success).length;
|
|
609
|
+
const improvement = successfulFixesCount > 0
|
|
610
|
+
? c.success(`-${successfulFixesCount}`)
|
|
611
|
+
: c.dim('0');
|
|
612
|
+
console.log(`${c.dim('├─')} Findings Before: ${c.yellow((result.remainingFindings + successfulFixesCount).toString())}`);
|
|
613
|
+
console.log(`${c.dim('├─')} Fixes Applied: ${improvement}`);
|
|
614
|
+
console.log(`${c.dim('├─')} Remaining Findings: ${c.yellow(result.remainingFindings.toString())}`);
|
|
615
|
+
const improvementPercent = result.remainingFindings + successfulFixesCount > 0
|
|
616
|
+
? ((successfulFixesCount /
|
|
617
|
+
(result.remainingFindings + successfulFixesCount)) *
|
|
618
|
+
100).toFixed(1)
|
|
619
|
+
: '0.0';
|
|
620
|
+
console.log(`${c.dim('├─')} Improvement: ${c.success(`${improvementPercent}%`)}`);
|
|
621
|
+
const verdictLabel = result.newScanVerdict === 'pass'
|
|
622
|
+
? c.success('PASS')
|
|
623
|
+
: result.newScanVerdict === 'fail'
|
|
624
|
+
? c.error('FAIL')
|
|
625
|
+
: c.dim('SKIPPED');
|
|
626
|
+
console.log(`${c.dim('└─')} New Scan Verdict: ${verdictLabel}\n`);
|
|
627
|
+
// Enterprise compliance summary
|
|
628
|
+
if (options.compliance) {
|
|
629
|
+
const complianceMetrics = generateComplianceMetrics(result);
|
|
630
|
+
console.log(`${c.bold('Compliance Summary')}`);
|
|
631
|
+
console.log(`${c.dim('├─')} Change Control: ${complianceMetrics.compliance.changeControl.verificationPassed ? c.success('VERIFIED') : c.error('NOT VERIFIED')}`);
|
|
632
|
+
console.log(`${c.dim('├─')} Audit Trail: ${result.runId ? c.success('AVAILABLE') : c.error('NOT AVAILABLE')}`);
|
|
633
|
+
console.log(`${c.dim('├─')} Quality Gates: ${complianceMetrics.compliance.qualityGates.overallPassed ? c.success('PASSED') : c.error('FAILED')}`);
|
|
634
|
+
console.log(`${c.dim('└─')} Remediation Rate: ${c.info(`${complianceMetrics.compliance.remediation.improvementPercent}%`)}\n`);
|
|
635
|
+
}
|
|
636
|
+
// Git information
|
|
637
|
+
if (result.gitBranch || result.gitCommit) {
|
|
638
|
+
console.log(`${c.bold('Git Integration')}`);
|
|
639
|
+
if (result.gitBranch) {
|
|
640
|
+
console.log(`${c.dim('├─')} Branch: ${c.info(result.gitBranch)}`);
|
|
641
|
+
}
|
|
642
|
+
if (result.gitCommit) {
|
|
643
|
+
console.log(`${c.dim('└─')} Commit: ${c.dim(result.gitCommit)}\n`);
|
|
644
|
+
}
|
|
645
|
+
else {
|
|
646
|
+
console.log('');
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
// File change statistics
|
|
650
|
+
const filesChanged = new Set(result.appliedFixes.map((f) => f.file));
|
|
651
|
+
if (filesChanged.size > 0) {
|
|
652
|
+
console.log(`${c.bold('Files Changed')}`);
|
|
653
|
+
const fileCount = filesChanged.size;
|
|
654
|
+
const filesList = Array.from(filesChanged).slice(0, 10);
|
|
655
|
+
console.log(`${c.dim('├─')} Total: ${c.info(fileCount.toString())} ${fileCount === 1 ? 'file' : 'files'}`);
|
|
656
|
+
if (filesList.length > 0) {
|
|
657
|
+
console.log(`${c.dim('├─')} Changed files:`);
|
|
658
|
+
filesList.forEach((file, i) => {
|
|
659
|
+
const isLast = i === filesList.length - 1 && fileCount <= 10;
|
|
660
|
+
const prefix = isLast ? '└─' : '│';
|
|
661
|
+
const fixCount = result.appliedFixes.filter((f) => f.file === file && f.success).length;
|
|
662
|
+
console.log(`${c.dim(` ${prefix}`)} ${c.dim(file)} ${c.yellow(`(${fixCount} fix${fixCount !== 1 ? 'es' : ''})`)}`);
|
|
663
|
+
});
|
|
664
|
+
if (fileCount > 10) {
|
|
665
|
+
console.log(`${c.dim(` └─`)} ... and ${fileCount - 10} more files`);
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
console.log('');
|
|
669
|
+
}
|
|
670
|
+
// Errors with recovery suggestions
|
|
671
|
+
if (result.errors.length > 0) {
|
|
672
|
+
console.log(`${c.error('Errors')}`);
|
|
673
|
+
result.errors.slice(0, 10).forEach((error, i) => {
|
|
674
|
+
const prefix = i === result.errors.length - 1 && result.errors.length <= 10
|
|
675
|
+
? '└─'
|
|
676
|
+
: '├─';
|
|
677
|
+
console.log(`${c.dim(` ${prefix}`)} ${c.error('✗')} ${error}`);
|
|
678
|
+
// Add recovery suggestions for common errors
|
|
679
|
+
const errorLower = error.toLowerCase();
|
|
680
|
+
if (errorLower.includes('git') && errorLower.includes('branch')) {
|
|
681
|
+
console.log(`${c.dim(' →')} ${c.info('Suggestion:')} Check git status and ensure you are on a clean branch`);
|
|
682
|
+
}
|
|
683
|
+
else if (errorLower.includes('verification') ||
|
|
684
|
+
errorLower.includes('build')) {
|
|
685
|
+
console.log(`${c.dim(' →')} ${c.info('Suggestion:')} Run with --no-verify to skip verification, or fix build errors manually`);
|
|
686
|
+
}
|
|
687
|
+
else if (errorLower.includes('permission') ||
|
|
688
|
+
errorLower.includes('access')) {
|
|
689
|
+
console.log(`${c.dim(' →')} ${c.info('Suggestion:')} Check file permissions and ensure you have write access`);
|
|
690
|
+
}
|
|
691
|
+
});
|
|
692
|
+
if (result.errors.length > 10) {
|
|
693
|
+
console.log(`${c.dim(` └─`)} ... and ${result.errors.length - 10} more errors`);
|
|
694
|
+
}
|
|
695
|
+
console.log('');
|
|
696
|
+
}
|
|
697
|
+
// Final verdict
|
|
698
|
+
const passed = result.verification?.passed && result.newScanVerdict === 'pass';
|
|
699
|
+
console.log(`${separator(80)}`);
|
|
700
|
+
console.log(`${c.bold('VERDICT:')} ${passed ? c.success('✓ PASS') : c.error('✗ FAIL')}`);
|
|
701
|
+
if (!passed && result.runId) {
|
|
702
|
+
console.log(`${c.dim('Run ID:')} ${c.info(result.runId)} ${c.dim('(use --rollback --run-id to revert)')}`);
|
|
703
|
+
}
|
|
704
|
+
console.log(`${separator(80)}\n`);
|
|
705
|
+
// Next steps
|
|
706
|
+
if (!options.json && options.format !== 'sarif') {
|
|
707
|
+
console.log(`${c.bold('Next Steps')}`);
|
|
708
|
+
if (passed) {
|
|
709
|
+
if (result.gitBranch) {
|
|
710
|
+
console.log(`${c.dim('→')} Review changes: ${c.info(`git diff ${result.gitBranch}`)}`);
|
|
711
|
+
console.log(`${c.dim('→')} Test your application to ensure everything works as expected`);
|
|
712
|
+
console.log(`${c.dim('→')} Commit changes: ${c.info(`git commit -m "Autopilot fixes (runId: ${result.runId})"`)}`);
|
|
713
|
+
// CI/CD integration hint
|
|
714
|
+
if (isCIEnvironment()) {
|
|
715
|
+
console.log(`${c.dim('→')} ${c.info('CI/CD:')} Changes are on branch ${c.info(result.gitBranch)}, ready for PR/merge`);
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
else {
|
|
719
|
+
console.log(`${c.dim('→')} Review the changes in your project files`);
|
|
720
|
+
console.log(`${c.dim('→')} Test your application to ensure everything works as expected`);
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
else {
|
|
724
|
+
console.log(`${c.dim('→')} Review errors above and fix issues manually if needed`);
|
|
725
|
+
if (result.runId) {
|
|
726
|
+
console.log(`${c.dim('→')} Rollback changes: ${c.info(`guardrail autopilot --rollback --run-id ${result.runId}`)}`);
|
|
727
|
+
}
|
|
728
|
+
console.log(`${c.dim('→')} Re-run autopilot after fixing issues: ${c.info('guardrail autopilot')}`);
|
|
729
|
+
// CI/CD integration hint for failures
|
|
730
|
+
if (isCIEnvironment()) {
|
|
731
|
+
console.log(`${c.dim('→')} ${c.info('CI/CD:')} Pipeline will fail - fix issues or use ${c.info('--no-verify')} to skip verification`);
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
console.log('');
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
/**
|
|
738
|
+
* Enhanced rollback results output
|
|
739
|
+
*/
|
|
740
|
+
function outputRollbackResult(result, options) {
|
|
741
|
+
if (options.json) {
|
|
742
|
+
console.log(JSON.stringify(result, null, 2));
|
|
743
|
+
return;
|
|
744
|
+
}
|
|
745
|
+
console.log(`\n${separator(80)}`);
|
|
746
|
+
console.log(`${c.bold('↩️ AUTOPILOT ROLLBACK')}`);
|
|
747
|
+
console.log(`${separator(80)}\n`);
|
|
748
|
+
console.log(`${c.bold('Rollback Details')}`);
|
|
749
|
+
console.log(`${c.dim('├─')} Run ID: ${c.info(result.runId)}`);
|
|
750
|
+
console.log(`${c.dim('├─')} Method: ${c.info(result.method)}`);
|
|
751
|
+
console.log(`${c.dim('└─')} Timestamp: ${c.dim(result.timestamp)}\n`);
|
|
752
|
+
if (result.success) {
|
|
753
|
+
console.log(`${c.success('✓')} ${c.bold(result.message)}\n`);
|
|
754
|
+
}
|
|
755
|
+
else {
|
|
756
|
+
console.log(`${c.error('✗')} ${c.bold(result.message)}\n`);
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
/**
|
|
760
|
+
* Enhanced progress callback with spinner integration
|
|
761
|
+
*/
|
|
762
|
+
function createProgressCallback(options) {
|
|
763
|
+
let currentSpinner = null;
|
|
764
|
+
let lastStage = '';
|
|
765
|
+
const stageTimings = {};
|
|
766
|
+
return (stage, message) => {
|
|
767
|
+
if (options.json)
|
|
768
|
+
return; // Skip progress in JSON mode
|
|
769
|
+
if (stage !== lastStage) {
|
|
770
|
+
if (currentSpinner) {
|
|
771
|
+
currentSpinner.stop(true);
|
|
772
|
+
if (lastStage) {
|
|
773
|
+
stageTimings[lastStage] = Date.now();
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
const stageLabels = {
|
|
777
|
+
scan: 'Scanning project',
|
|
778
|
+
group: 'Grouping findings',
|
|
779
|
+
workspace: 'Preparing workspace',
|
|
780
|
+
git: 'Git operations',
|
|
781
|
+
fix: 'Applying fixes',
|
|
782
|
+
verify: 'Verifying changes',
|
|
783
|
+
apply: 'Applying to project',
|
|
784
|
+
rescan: 'Running verification scan',
|
|
785
|
+
rollback: 'Rolling back',
|
|
786
|
+
};
|
|
787
|
+
const label = stageLabels[stage] || stage;
|
|
788
|
+
currentSpinner = createSpinner(`${label}... ${c.dim(message)}`);
|
|
789
|
+
lastStage = stage;
|
|
790
|
+
}
|
|
791
|
+
};
|
|
792
|
+
}
|
|
793
|
+
/**
|
|
794
|
+
* Generate enterprise compliance metrics
|
|
795
|
+
*/
|
|
796
|
+
function generateComplianceMetrics(result) {
|
|
797
|
+
const successfulFixes = result.appliedFixes.filter((f) => f.success);
|
|
798
|
+
const filesChanged = new Set(result.appliedFixes.map((f) => f.file));
|
|
799
|
+
return {
|
|
800
|
+
timestamp: result.timestamp,
|
|
801
|
+
runId: result.runId,
|
|
802
|
+
compliance: {
|
|
803
|
+
changeControl: {
|
|
804
|
+
changesApplied: successfulFixes.length,
|
|
805
|
+
filesModified: filesChanged.size,
|
|
806
|
+
verificationPassed: result.verification?.passed || false,
|
|
807
|
+
rollbackAvailable: !!result.runId,
|
|
808
|
+
},
|
|
809
|
+
auditTrail: {
|
|
810
|
+
evidenceGenerated: false, // Set when evidence is generated
|
|
811
|
+
gitBranch: result.gitBranch,
|
|
812
|
+
gitCommit: result.gitCommit,
|
|
813
|
+
runId: result.runId,
|
|
814
|
+
},
|
|
815
|
+
qualityGates: {
|
|
816
|
+
typecheckPassed: result.verification?.typecheck.passed || false,
|
|
817
|
+
buildPassed: result.verification?.build.passed || false,
|
|
818
|
+
testsPassed: result.verification?.tests.passed || false,
|
|
819
|
+
overallPassed: result.verification?.passed || false,
|
|
820
|
+
},
|
|
821
|
+
remediation: {
|
|
822
|
+
findingsBefore: result.remainingFindings + successfulFixes.length,
|
|
823
|
+
findingsAfter: result.remainingFindings,
|
|
824
|
+
improvementPercent: result.remainingFindings + successfulFixes.length > 0
|
|
825
|
+
? ((successfulFixes.length /
|
|
826
|
+
(result.remainingFindings + successfulFixes.length)) *
|
|
827
|
+
100).toFixed(1)
|
|
828
|
+
: '0.0',
|
|
829
|
+
},
|
|
830
|
+
},
|
|
831
|
+
};
|
|
832
|
+
}
|
|
833
|
+
/**
|
|
834
|
+
* Generate markdown report from autopilot results
|
|
835
|
+
*/
|
|
836
|
+
function generateMarkdownReport(result, projectPath) {
|
|
837
|
+
const filesChanged = new Set(result.appliedFixes.map((f) => f.file));
|
|
838
|
+
const successfulFixes = result.appliedFixes.filter((f) => f.success);
|
|
839
|
+
const failedFixes = result.appliedFixes.filter((f) => !f.success);
|
|
840
|
+
const complianceMetrics = generateComplianceMetrics(result);
|
|
841
|
+
const report = [
|
|
842
|
+
'# Autopilot Execution Report',
|
|
843
|
+
'',
|
|
844
|
+
`**Run ID:** ${result.runId || 'N/A'}`,
|
|
845
|
+
`**Timestamp:** ${result.timestamp}`,
|
|
846
|
+
`**Profile:** ${result.profile}`,
|
|
847
|
+
`**Duration:** ${formatDuration(result.duration)}`,
|
|
848
|
+
'',
|
|
849
|
+
'## Summary',
|
|
850
|
+
'',
|
|
851
|
+
`- **Packs Attempted:** ${result.packsAttempted}`,
|
|
852
|
+
`- **Packs Succeeded:** ${result.packsSucceeded}`,
|
|
853
|
+
`- **Packs Failed:** ${result.packsFailed}`,
|
|
854
|
+
`- **Total Fixes Applied:** ${result.appliedFixes.length}`,
|
|
855
|
+
`- **Successful Fixes:** ${successfulFixes.length}`,
|
|
856
|
+
`- **Failed Fixes:** ${failedFixes.length}`,
|
|
857
|
+
`- **Files Changed:** ${filesChanged.size}`,
|
|
858
|
+
`- **Remaining Findings:** ${result.remainingFindings}`,
|
|
859
|
+
'',
|
|
860
|
+
'## Compliance Metrics',
|
|
861
|
+
'',
|
|
862
|
+
`- **Change Control:** ${complianceMetrics.compliance.changeControl.changesApplied} changes applied to ${complianceMetrics.compliance.changeControl.filesModified} files`,
|
|
863
|
+
`- **Verification:** ${complianceMetrics.compliance.qualityGates.overallPassed ? 'PASSED' : 'FAILED'}`,
|
|
864
|
+
`- **Audit Trail:** ${complianceMetrics.compliance.auditTrail.runId ? 'Available' : 'Not available'}`,
|
|
865
|
+
`- **Improvement:** ${complianceMetrics.compliance.remediation.improvementPercent}% reduction in findings`,
|
|
866
|
+
'',
|
|
867
|
+
'## Verification Results',
|
|
868
|
+
'',
|
|
869
|
+
];
|
|
870
|
+
if (result.verification) {
|
|
871
|
+
const v = result.verification;
|
|
872
|
+
report.push(`- **Typecheck:** ${v.typecheck.passed ? '✅ PASS' : '❌ FAIL'}`, `- **Build:** ${v.build.passed ? '✅ PASS' : '❌ FAIL'}`, `- **Tests:** ${v.tests.passed ? '✅ PASS' : '❌ FAIL'}`, `- **Overall:** ${v.passed ? '✅ PASS' : '❌ FAIL'}`, '');
|
|
873
|
+
}
|
|
874
|
+
else {
|
|
875
|
+
report.push('- Verification skipped', '');
|
|
876
|
+
}
|
|
877
|
+
report.push('## Files Changed', '', ...Array.from(filesChanged)
|
|
878
|
+
.slice(0, 50)
|
|
879
|
+
.map((file) => {
|
|
880
|
+
const fixCount = successfulFixes.filter((f) => f.file === file).length;
|
|
881
|
+
return `- \`${file}\` (${fixCount} fix${fixCount !== 1 ? 'es' : ''})`;
|
|
882
|
+
}), filesChanged.size > 50
|
|
883
|
+
? `\n... and ${filesChanged.size - 50} more files`
|
|
884
|
+
: '', '');
|
|
885
|
+
if (result.gitBranch || result.gitCommit) {
|
|
886
|
+
report.push('## Git Integration', '');
|
|
887
|
+
if (result.gitBranch) {
|
|
888
|
+
report.push(`- **Branch:** ${result.gitBranch}`);
|
|
889
|
+
}
|
|
890
|
+
if (result.gitCommit) {
|
|
891
|
+
report.push(`- **Commit:** ${result.gitCommit}`);
|
|
892
|
+
}
|
|
893
|
+
report.push('');
|
|
894
|
+
}
|
|
895
|
+
if (result.errors.length > 0) {
|
|
896
|
+
report.push('## Errors', '');
|
|
897
|
+
result.errors.forEach((error) => {
|
|
898
|
+
report.push(`- ${error}`);
|
|
899
|
+
});
|
|
900
|
+
report.push('');
|
|
901
|
+
}
|
|
902
|
+
report.push('## Verdict', '');
|
|
903
|
+
const passed = result.verification?.passed && result.newScanVerdict === 'pass';
|
|
904
|
+
report.push(passed ? '✅ **PASS**' : '❌ **FAIL**');
|
|
905
|
+
return report.join('\n');
|
|
906
|
+
}
|
|
907
|
+
/**
|
|
908
|
+
* Save markdown report to file
|
|
909
|
+
*/
|
|
910
|
+
function saveMarkdownReport(result, projectPath, options) {
|
|
911
|
+
if (options.json || !options.report)
|
|
912
|
+
return;
|
|
913
|
+
try {
|
|
914
|
+
const reportDir = (0, path_1.join)(projectPath, '.guardrail', 'reports');
|
|
915
|
+
if (!(0, fs_1.existsSync)(reportDir)) {
|
|
916
|
+
(0, fs_1.mkdirSync)(reportDir, { recursive: true });
|
|
917
|
+
}
|
|
918
|
+
const timestamp = new Date()
|
|
919
|
+
.toISOString()
|
|
920
|
+
.replace(/[:.]/g, '-')
|
|
921
|
+
.split('T')[0];
|
|
922
|
+
const runId = result.runId || 'unknown';
|
|
923
|
+
const reportPath = (0, path_1.join)(reportDir, `autopilot-${timestamp}-${String(runId).slice(0, 8)}.md`);
|
|
924
|
+
const report = generateMarkdownReport(result, projectPath);
|
|
925
|
+
(0, fs_1.writeFileSync)(reportPath, report, 'utf8');
|
|
926
|
+
console.log(`${c.info('📄')} Report saved: ${c.dim(reportPath)}\n`);
|
|
927
|
+
}
|
|
928
|
+
catch (err) {
|
|
929
|
+
console.log(`${c.error('⚠')} ${c.dim('Failed to save report, continuing...')}\n`);
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
/**
|
|
933
|
+
* Write enterprise audit log
|
|
934
|
+
*/
|
|
935
|
+
function writeAuditLog(entry, projectPath) {
|
|
936
|
+
try {
|
|
937
|
+
const auditDir = (0, path_1.join)(projectPath, '.guardrail', 'audit');
|
|
938
|
+
if (!(0, fs_1.existsSync)(auditDir)) {
|
|
939
|
+
(0, fs_1.mkdirSync)(auditDir, { recursive: true });
|
|
940
|
+
}
|
|
941
|
+
const timestamp = new Date()
|
|
942
|
+
.toISOString()
|
|
943
|
+
.replace(/[:.]/g, '-')
|
|
944
|
+
.split('T')[0];
|
|
945
|
+
const auditFile = (0, path_1.join)(auditDir, `autopilot-audit-${timestamp}.jsonl`);
|
|
946
|
+
// Append to audit log (JSONL format for easy streaming)
|
|
947
|
+
const logLine = JSON.stringify(entry) + '\n';
|
|
948
|
+
(0, fs_1.writeFileSync)(auditFile, logLine, { flag: 'a' });
|
|
949
|
+
}
|
|
950
|
+
catch (err) {
|
|
951
|
+
// Audit logging should not fail the operation
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
/**
|
|
955
|
+
* Save autopilot state for resume capability
|
|
956
|
+
*/
|
|
957
|
+
function saveAutopilotState(runId, state, projectPath) {
|
|
958
|
+
try {
|
|
959
|
+
const stateDir = (0, path_1.join)(projectPath, '.guardrail', 'autopilot-state');
|
|
960
|
+
if (!(0, fs_1.existsSync)(stateDir)) {
|
|
961
|
+
(0, fs_1.mkdirSync)(stateDir, { recursive: true });
|
|
962
|
+
}
|
|
963
|
+
const statePath = (0, path_1.join)(stateDir, `${runId}.json`);
|
|
964
|
+
const stateData = {
|
|
965
|
+
runId,
|
|
966
|
+
timestamp: new Date().toISOString(),
|
|
967
|
+
...state,
|
|
968
|
+
};
|
|
969
|
+
(0, fs_1.writeFileSync)(statePath, JSON.stringify(stateData, null, 2), 'utf8');
|
|
970
|
+
}
|
|
971
|
+
catch (err) {
|
|
972
|
+
// Silently fail - state saving is optional
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
/**
|
|
976
|
+
* Load autopilot state for resume
|
|
977
|
+
*/
|
|
978
|
+
function loadAutopilotState(runId, projectPath) {
|
|
979
|
+
try {
|
|
980
|
+
const statePath = (0, path_1.join)(projectPath, '.guardrail', 'autopilot-state', `${runId}.json`);
|
|
981
|
+
if (!(0, fs_1.existsSync)(statePath)) {
|
|
982
|
+
return null;
|
|
983
|
+
}
|
|
984
|
+
const stateContent = (0, fs_1.readFileSync)(statePath, 'utf8');
|
|
985
|
+
return JSON.parse(stateContent);
|
|
986
|
+
}
|
|
987
|
+
catch (err) {
|
|
988
|
+
return null;
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
/**
|
|
992
|
+
* List previous autopilot runs
|
|
993
|
+
*/
|
|
994
|
+
function listAutopilotRuns(projectPath) {
|
|
995
|
+
const stateDir = (0, path_1.join)(projectPath, '.guardrail', 'autopilot-state');
|
|
996
|
+
if (!(0, fs_1.existsSync)(stateDir)) {
|
|
997
|
+
return [];
|
|
998
|
+
}
|
|
999
|
+
try {
|
|
1000
|
+
const files = (0, fs_1.readdirSync)(stateDir).filter((f) => f.endsWith('.json'));
|
|
1001
|
+
const runs = [];
|
|
1002
|
+
for (const file of files) {
|
|
1003
|
+
try {
|
|
1004
|
+
const filePath = (0, path_1.join)(stateDir, file);
|
|
1005
|
+
const content = (0, fs_1.readFileSync)(filePath, 'utf8');
|
|
1006
|
+
const state = JSON.parse(content);
|
|
1007
|
+
const stat = (0, fs_1.statSync)(filePath);
|
|
1008
|
+
runs.push({
|
|
1009
|
+
runId: state.runId || file.replace('.json', ''),
|
|
1010
|
+
timestamp: state.timestamp || stat.mtime.toISOString(),
|
|
1011
|
+
mode: state.mode || 'unknown',
|
|
1012
|
+
});
|
|
1013
|
+
}
|
|
1014
|
+
catch {
|
|
1015
|
+
// Skip invalid files
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
return runs.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());
|
|
1019
|
+
}
|
|
1020
|
+
catch {
|
|
1021
|
+
return [];
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
/**
|
|
1025
|
+
* Export plan to JSON file
|
|
1026
|
+
*/
|
|
1027
|
+
function exportPlan(plan, projectPath, outputPath) {
|
|
1028
|
+
const exportDir = (0, path_1.join)(projectPath, '.guardrail', 'exports');
|
|
1029
|
+
if (!(0, fs_1.existsSync)(exportDir)) {
|
|
1030
|
+
(0, fs_1.mkdirSync)(exportDir, { recursive: true });
|
|
1031
|
+
}
|
|
1032
|
+
const timestamp = new Date()
|
|
1033
|
+
.toISOString()
|
|
1034
|
+
.replace(/[:.]/g, '-')
|
|
1035
|
+
.split('T')[0];
|
|
1036
|
+
const filename = outputPath || `autopilot-plan-${timestamp}.json`;
|
|
1037
|
+
const fullPath = outputPath ? (0, path_1.resolve)(outputPath) : (0, path_1.join)(exportDir, filename);
|
|
1038
|
+
(0, fs_1.writeFileSync)(fullPath, JSON.stringify(plan, null, 2), 'utf8');
|
|
1039
|
+
return fullPath;
|
|
1040
|
+
}
|
|
1041
|
+
/**
|
|
1042
|
+
* Import plan from JSON file
|
|
1043
|
+
*/
|
|
1044
|
+
function importPlan(planPath) {
|
|
1045
|
+
try {
|
|
1046
|
+
const content = (0, fs_1.readFileSync)(planPath, 'utf8');
|
|
1047
|
+
const plan = JSON.parse(content);
|
|
1048
|
+
// Basic validation
|
|
1049
|
+
if (plan.mode === 'plan' && plan.packs && Array.isArray(plan.packs)) {
|
|
1050
|
+
return plan;
|
|
1051
|
+
}
|
|
1052
|
+
return null;
|
|
1053
|
+
}
|
|
1054
|
+
catch {
|
|
1055
|
+
return null;
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
/**
|
|
1059
|
+
* Main autopilot command handler with enhanced features
|
|
1060
|
+
*/
|
|
1061
|
+
async function runAutopilotCommand(projectPath, options) {
|
|
1062
|
+
const resolvedPath = (0, path_1.resolve)(projectPath);
|
|
1063
|
+
if (!(0, fs_1.existsSync)(resolvedPath)) {
|
|
1064
|
+
(0, exit_codes_1.exitWith)(exit_codes_1.ExitCode.USER_ERROR, `Project path does not exist: ${resolvedPath}`);
|
|
1065
|
+
}
|
|
1066
|
+
// Load configuration from file and merge with CLI options
|
|
1067
|
+
const fileConfig = loadAutopilotConfig(resolvedPath);
|
|
1068
|
+
const mergedOptions = {
|
|
1069
|
+
...fileConfig,
|
|
1070
|
+
...options,
|
|
1071
|
+
// CLI options take precedence
|
|
1072
|
+
profile: options.profile || fileConfig.profile || 'ship',
|
|
1073
|
+
maxFixes: options.maxFixes
|
|
1074
|
+
? parseInt(options.maxFixes, 10)
|
|
1075
|
+
: fileConfig.maxFixes,
|
|
1076
|
+
verify: options.verify !== undefined
|
|
1077
|
+
? options.verify !== false
|
|
1078
|
+
: fileConfig.verify !== false,
|
|
1079
|
+
force: options.force || fileConfig.force || false,
|
|
1080
|
+
};
|
|
1081
|
+
// Auto-detect CI mode
|
|
1082
|
+
const isCI = isCIEnvironment();
|
|
1083
|
+
if (isCI && !options.interactive && !options.json) {
|
|
1084
|
+
mergedOptions.interactive = false;
|
|
1085
|
+
mergedOptions.verify = mergedOptions.verify !== false;
|
|
1086
|
+
}
|
|
1087
|
+
const progressCallback = createProgressCallback(mergedOptions);
|
|
1088
|
+
let currentSpinner = null;
|
|
1089
|
+
try {
|
|
1090
|
+
// Handle list runs command
|
|
1091
|
+
if (mergedOptions.list) {
|
|
1092
|
+
const runs = listAutopilotRuns(resolvedPath);
|
|
1093
|
+
if (mergedOptions.json) {
|
|
1094
|
+
console.log(JSON.stringify(runs, null, 2));
|
|
1095
|
+
return;
|
|
1096
|
+
}
|
|
1097
|
+
console.log(`\n${separator(80)}`);
|
|
1098
|
+
console.log(`${c.bold('📋 PREVIOUS AUTOPILOT RUNS')}`);
|
|
1099
|
+
console.log(`${separator(80)}\n`);
|
|
1100
|
+
if (runs.length === 0) {
|
|
1101
|
+
console.log(` ${c.dim('No previous runs found')}\n`);
|
|
1102
|
+
return;
|
|
1103
|
+
}
|
|
1104
|
+
console.log(tableRow(c.bold('Run ID'), c.bold('Mode'), c.bold('Timestamp')));
|
|
1105
|
+
console.log(`${separator(80)}`);
|
|
1106
|
+
runs.slice(0, 20).forEach((run) => {
|
|
1107
|
+
const date = new Date(run.timestamp).toLocaleString();
|
|
1108
|
+
console.log(tableRow(c.info(run.runId.slice(0, 16) + '...'), c.dim(run.mode), c.dim(date)));
|
|
1109
|
+
});
|
|
1110
|
+
if (runs.length > 20) {
|
|
1111
|
+
console.log(`${c.dim(` ... and ${runs.length - 20} more runs`)}`);
|
|
1112
|
+
}
|
|
1113
|
+
console.log(`${separator(80)}\n`);
|
|
1114
|
+
return;
|
|
1115
|
+
}
|
|
1116
|
+
// Handle import plan
|
|
1117
|
+
if (mergedOptions.import) {
|
|
1118
|
+
const plan = importPlan(mergedOptions.import);
|
|
1119
|
+
if (!plan) {
|
|
1120
|
+
(0, exit_codes_1.exitWith)(exit_codes_1.ExitCode.USER_ERROR, `Failed to import plan from ${mergedOptions.import}`);
|
|
1121
|
+
}
|
|
1122
|
+
outputPlanResult(plan, mergedOptions);
|
|
1123
|
+
if (mergedOptions.apply) {
|
|
1124
|
+
const confirmed = await promptConfirm('Apply imported plan?', false);
|
|
1125
|
+
if (!confirmed) {
|
|
1126
|
+
console.log(`\n ${c.dim('Cancelled by user')}\n`);
|
|
1127
|
+
return;
|
|
1128
|
+
}
|
|
1129
|
+
// Continue to apply mode with imported plan
|
|
1130
|
+
// This would need to be integrated with the apply flow
|
|
1131
|
+
console.log(`${c.info('→')} ${c.dim('Applying imported plan...')}\n`);
|
|
1132
|
+
}
|
|
1133
|
+
else {
|
|
1134
|
+
return;
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
if (mergedOptions.rollback) {
|
|
1138
|
+
if (!mergedOptions.runId) {
|
|
1139
|
+
(0, exit_codes_1.exitWith)(exit_codes_1.ExitCode.USER_ERROR, '--run-id is required for rollback');
|
|
1140
|
+
}
|
|
1141
|
+
if (!mergedOptions.json) {
|
|
1142
|
+
currentSpinner = createSpinner('Rolling back changes...');
|
|
1143
|
+
}
|
|
1144
|
+
const rollbackOptions = {
|
|
1145
|
+
projectPath: resolvedPath,
|
|
1146
|
+
mode: 'rollback',
|
|
1147
|
+
runId: mergedOptions.runId,
|
|
1148
|
+
onProgress: progressCallback,
|
|
1149
|
+
};
|
|
1150
|
+
const result = (await (0, core_1.runAutopilot)(rollbackOptions));
|
|
1151
|
+
if (currentSpinner) {
|
|
1152
|
+
currentSpinner.stop(result.success, result.success ? 'Rollback complete' : 'Rollback failed');
|
|
1153
|
+
}
|
|
1154
|
+
outputRollbackResult(result, mergedOptions);
|
|
1155
|
+
if (!result.success) {
|
|
1156
|
+
(0, exit_codes_1.exitWith)(exit_codes_1.ExitCode.SYSTEM_ERROR, result.message);
|
|
1157
|
+
}
|
|
1158
|
+
return;
|
|
1159
|
+
}
|
|
1160
|
+
// Plan mode (dry-run by default, or explicit --plan)
|
|
1161
|
+
if (mergedOptions.plan || (mergedOptions.dryRun && !mergedOptions.apply)) {
|
|
1162
|
+
if (!mergedOptions.json) {
|
|
1163
|
+
currentSpinner = createSpinner('Generating plan...');
|
|
1164
|
+
}
|
|
1165
|
+
const planOptions = {
|
|
1166
|
+
projectPath: resolvedPath,
|
|
1167
|
+
mode: 'plan',
|
|
1168
|
+
profile: mergedOptions.profile || 'ship',
|
|
1169
|
+
maxFixes: mergedOptions.maxFixes,
|
|
1170
|
+
onProgress: progressCallback,
|
|
1171
|
+
};
|
|
1172
|
+
const result = (await (0, core_1.runAutopilot)(planOptions));
|
|
1173
|
+
if (currentSpinner) {
|
|
1174
|
+
currentSpinner.stop(true, 'Plan generated');
|
|
1175
|
+
}
|
|
1176
|
+
// Save state for potential resume
|
|
1177
|
+
if (result.packs.length > 0) {
|
|
1178
|
+
const runId = crypto.randomBytes(8).toString('hex');
|
|
1179
|
+
saveAutopilotState(runId, {
|
|
1180
|
+
mode: 'plan',
|
|
1181
|
+
options: planOptions,
|
|
1182
|
+
planResult: result,
|
|
1183
|
+
}, resolvedPath);
|
|
1184
|
+
}
|
|
1185
|
+
// Enterprise audit logging for plan
|
|
1186
|
+
if (mergedOptions.audit !== false) {
|
|
1187
|
+
const user = process.env.USER ||
|
|
1188
|
+
process.env.USERNAME ||
|
|
1189
|
+
process.env.GUARDRAIL_USER ||
|
|
1190
|
+
'unknown';
|
|
1191
|
+
writeAuditLog({
|
|
1192
|
+
timestamp: new Date().toISOString(),
|
|
1193
|
+
runId: crypto.randomBytes(8).toString('hex'),
|
|
1194
|
+
action: 'plan',
|
|
1195
|
+
user,
|
|
1196
|
+
projectPath: resolvedPath,
|
|
1197
|
+
outcome: 'success',
|
|
1198
|
+
details: {
|
|
1199
|
+
totalFindings: result.totalFindings,
|
|
1200
|
+
fixableFindings: result.fixableFindings,
|
|
1201
|
+
packsCount: result.packs.length,
|
|
1202
|
+
profile: result.profile,
|
|
1203
|
+
},
|
|
1204
|
+
compliance: {
|
|
1205
|
+
evidenceGenerated: false,
|
|
1206
|
+
fixesApplied: 0,
|
|
1207
|
+
filesChanged: 0,
|
|
1208
|
+
},
|
|
1209
|
+
}, resolvedPath);
|
|
1210
|
+
}
|
|
1211
|
+
outputPlanResult(result, mergedOptions);
|
|
1212
|
+
// Export plan if requested
|
|
1213
|
+
if (mergedOptions.export) {
|
|
1214
|
+
const exportPath = exportPlan(result, resolvedPath, mergedOptions.export);
|
|
1215
|
+
console.log(`${c.info('💾')} Plan exported: ${c.dim(exportPath)}\n`);
|
|
1216
|
+
}
|
|
1217
|
+
// Generate evidence if requested
|
|
1218
|
+
if (mergedOptions.evidence && !mergedOptions.json) {
|
|
1219
|
+
const evidenceSpinner = createSpinner('Generating evidence pack...');
|
|
1220
|
+
try {
|
|
1221
|
+
await (0, evidence_1.generateEvidence)('autopilot-plan', result, resolvedPath);
|
|
1222
|
+
evidenceSpinner.stop(true, 'Evidence pack generated');
|
|
1223
|
+
}
|
|
1224
|
+
catch (err) {
|
|
1225
|
+
evidenceSpinner.stop(false, 'Evidence generation failed');
|
|
1226
|
+
console.log(`${c.error('⚠')} ${c.dim('Evidence generation failed, continuing...')}\n`);
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
return;
|
|
1230
|
+
}
|
|
1231
|
+
// Apply mode
|
|
1232
|
+
const applyOptions = {
|
|
1233
|
+
projectPath: resolvedPath,
|
|
1234
|
+
mode: 'apply',
|
|
1235
|
+
profile: mergedOptions.profile || 'ship',
|
|
1236
|
+
maxFixes: mergedOptions.maxFixes,
|
|
1237
|
+
dryRun: mergedOptions.dryRun || false,
|
|
1238
|
+
interactive: mergedOptions.interactive || false,
|
|
1239
|
+
verify: mergedOptions.verify !== false,
|
|
1240
|
+
runId: mergedOptions.runId,
|
|
1241
|
+
force: mergedOptions.force || false,
|
|
1242
|
+
branchStrategy: mergedOptions.branchStrategy,
|
|
1243
|
+
onProgress: progressCallback,
|
|
1244
|
+
};
|
|
1245
|
+
// If interactive mode, run plan first and let user select packs
|
|
1246
|
+
if (mergedOptions.interactive && !isCI) {
|
|
1247
|
+
if (!mergedOptions.json) {
|
|
1248
|
+
currentSpinner = createSpinner('Generating plan...');
|
|
1249
|
+
}
|
|
1250
|
+
const planOptions = {
|
|
1251
|
+
projectPath: resolvedPath,
|
|
1252
|
+
mode: 'plan',
|
|
1253
|
+
profile: applyOptions.profile,
|
|
1254
|
+
maxFixes: applyOptions.maxFixes,
|
|
1255
|
+
onProgress: progressCallback,
|
|
1256
|
+
};
|
|
1257
|
+
const planResult = (await (0, core_1.runAutopilot)(planOptions));
|
|
1258
|
+
if (currentSpinner) {
|
|
1259
|
+
currentSpinner.stop(true, 'Plan generated');
|
|
1260
|
+
}
|
|
1261
|
+
if (planResult.packs.length === 0) {
|
|
1262
|
+
console.log(`\n ${c.success('✓')} ${c.bold('No fixable issues found')}\n`);
|
|
1263
|
+
return;
|
|
1264
|
+
}
|
|
1265
|
+
outputPlanResult(planResult, { json: false });
|
|
1266
|
+
const selectedPacks = await selectPacksInteractively(planResult.packs);
|
|
1267
|
+
if (selectedPacks.length === 0) {
|
|
1268
|
+
console.log(`\n ${c.dim('Cancelled by user')}\n`);
|
|
1269
|
+
return;
|
|
1270
|
+
}
|
|
1271
|
+
applyOptions.packIds = selectedPacks.map((p) => p.id);
|
|
1272
|
+
const confirmed = await promptConfirm(`Apply ${selectedPacks.length} selected pack(s)?`, false);
|
|
1273
|
+
if (!confirmed) {
|
|
1274
|
+
console.log(`\n ${c.dim('Cancelled by user')}\n`);
|
|
1275
|
+
return;
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
if (!mergedOptions.json && !mergedOptions.dryRun) {
|
|
1279
|
+
currentSpinner = createSpinner('Running autopilot...');
|
|
1280
|
+
}
|
|
1281
|
+
// Check if resuming from a previous run
|
|
1282
|
+
if (mergedOptions.runId && !mergedOptions.rollback) {
|
|
1283
|
+
const savedState = loadAutopilotState(mergedOptions.runId, resolvedPath);
|
|
1284
|
+
if (savedState && savedState.planResult) {
|
|
1285
|
+
if (!mergedOptions.json) {
|
|
1286
|
+
console.log(`${c.info('↻')} ${c.dim(`Resuming from run ${mergedOptions.runId}`)}\n`);
|
|
1287
|
+
}
|
|
1288
|
+
// Could use saved plan result here if needed
|
|
1289
|
+
}
|
|
1290
|
+
}
|
|
1291
|
+
const result = (await (0, core_1.runAutopilot)(applyOptions));
|
|
1292
|
+
if (currentSpinner) {
|
|
1293
|
+
const success = result.verification?.passed && result.newScanVerdict === 'pass';
|
|
1294
|
+
currentSpinner.stop(success, success ? 'Autopilot complete' : 'Autopilot completed with issues');
|
|
1295
|
+
}
|
|
1296
|
+
// Save state for potential resume
|
|
1297
|
+
if (result.runId) {
|
|
1298
|
+
saveAutopilotState(result.runId, {
|
|
1299
|
+
mode: 'apply',
|
|
1300
|
+
options: applyOptions,
|
|
1301
|
+
applyResult: result,
|
|
1302
|
+
}, resolvedPath);
|
|
1303
|
+
}
|
|
1304
|
+
// Enterprise audit logging
|
|
1305
|
+
if (mergedOptions.audit !== false) {
|
|
1306
|
+
const user = process.env.USER ||
|
|
1307
|
+
process.env.USERNAME ||
|
|
1308
|
+
process.env.GUARDRAIL_USER ||
|
|
1309
|
+
'unknown';
|
|
1310
|
+
const outcome = result.verification?.passed && result.newScanVerdict === 'pass'
|
|
1311
|
+
? 'success'
|
|
1312
|
+
: result.packsSucceeded > 0
|
|
1313
|
+
? 'partial'
|
|
1314
|
+
: 'failure';
|
|
1315
|
+
const filesChanged = new Set(result.appliedFixes.map((f) => f.file));
|
|
1316
|
+
writeAuditLog({
|
|
1317
|
+
timestamp: new Date().toISOString(),
|
|
1318
|
+
runId: result.runId || 'unknown',
|
|
1319
|
+
action: 'apply',
|
|
1320
|
+
user,
|
|
1321
|
+
projectPath: resolvedPath,
|
|
1322
|
+
outcome,
|
|
1323
|
+
details: {
|
|
1324
|
+
packsAttempted: result.packsAttempted,
|
|
1325
|
+
packsSucceeded: result.packsSucceeded,
|
|
1326
|
+
packsFailed: result.packsFailed,
|
|
1327
|
+
fixesApplied: result.appliedFixes.filter((f) => f.success).length,
|
|
1328
|
+
duration: result.duration,
|
|
1329
|
+
profile: result.profile,
|
|
1330
|
+
},
|
|
1331
|
+
compliance: {
|
|
1332
|
+
evidenceGenerated: !!mergedOptions.evidence,
|
|
1333
|
+
gitBranch: result.gitBranch,
|
|
1334
|
+
verificationPassed: result.verification?.passed,
|
|
1335
|
+
fixesApplied: result.appliedFixes.filter((f) => f.success).length,
|
|
1336
|
+
filesChanged: filesChanged.size,
|
|
1337
|
+
},
|
|
1338
|
+
}, resolvedPath);
|
|
1339
|
+
}
|
|
1340
|
+
outputApplyResult(result, mergedOptions);
|
|
1341
|
+
// Generate markdown report if requested
|
|
1342
|
+
if (mergedOptions.report && !mergedOptions.json && !mergedOptions.dryRun) {
|
|
1343
|
+
saveMarkdownReport(result, resolvedPath, mergedOptions);
|
|
1344
|
+
}
|
|
1345
|
+
// Generate evidence if requested
|
|
1346
|
+
if (mergedOptions.evidence &&
|
|
1347
|
+
!mergedOptions.json &&
|
|
1348
|
+
!mergedOptions.dryRun) {
|
|
1349
|
+
const evidenceSpinner = createSpinner('Generating evidence pack...');
|
|
1350
|
+
try {
|
|
1351
|
+
await (0, evidence_1.generateEvidence)('autopilot-apply', result, resolvedPath);
|
|
1352
|
+
evidenceSpinner.stop(true, 'Evidence pack generated');
|
|
1353
|
+
}
|
|
1354
|
+
catch (err) {
|
|
1355
|
+
evidenceSpinner.stop(false, 'Evidence generation failed');
|
|
1356
|
+
console.log(`${c.error('⚠')} ${c.dim('Evidence generation failed, continuing...')}\n`);
|
|
1357
|
+
}
|
|
1358
|
+
}
|
|
1359
|
+
// Determine exit code based on results
|
|
1360
|
+
if (mergedOptions.dryRun) {
|
|
1361
|
+
// Dry-run always exits successfully (just shows what would happen)
|
|
1362
|
+
return;
|
|
1363
|
+
}
|
|
1364
|
+
const passed = result.verification?.passed && result.newScanVerdict === 'pass';
|
|
1365
|
+
if (!passed) {
|
|
1366
|
+
(0, exit_codes_1.exitWith)(exit_codes_1.ExitCode.POLICY_FAIL, 'Autopilot verification failed or scan verdict is FAIL');
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1369
|
+
catch (error) {
|
|
1370
|
+
if (currentSpinner) {
|
|
1371
|
+
currentSpinner.stop(false, 'Failed');
|
|
1372
|
+
}
|
|
1373
|
+
const errorMessage = error?.message || String(error);
|
|
1374
|
+
console.error(`\n ${c.error('✗')} ${c.bold('Autopilot failed:')} ${errorMessage}\n`);
|
|
1375
|
+
// Provide helpful recovery suggestions
|
|
1376
|
+
const errorLower = errorMessage.toLowerCase();
|
|
1377
|
+
if (errorLower.includes('entitlement') || errorLower.includes('feature')) {
|
|
1378
|
+
console.log(`${c.info('💡')} ${c.bold('Recovery suggestions:')}`);
|
|
1379
|
+
console.log(` ${c.dim('→')} Verify your account has access to the autopilot feature`);
|
|
1380
|
+
console.log(` ${c.dim('→')} Check your subscription tier at https://guardrail.dev/pricing`);
|
|
1381
|
+
console.log(` ${c.dim('→')} Contact support if you believe this is an error\n`);
|
|
1382
|
+
(0, exit_codes_1.exitWith)(exit_codes_1.ExitCode.AUTH_FAILURE, errorMessage);
|
|
1383
|
+
}
|
|
1384
|
+
else if (errorLower.includes('git')) {
|
|
1385
|
+
console.log(`${c.info('💡')} ${c.bold('Recovery suggestions:')}`);
|
|
1386
|
+
console.log(` ${c.dim('→')} Ensure you are in a git repository or run with a clean working tree`);
|
|
1387
|
+
console.log(` ${c.dim('→')} Check git status: git status`);
|
|
1388
|
+
console.log(` ${c.dim('→')} Autopilot will use backup-based rollback if git is unavailable\n`);
|
|
1389
|
+
}
|
|
1390
|
+
else if (errorLower.includes('permission') ||
|
|
1391
|
+
errorLower.includes('access')) {
|
|
1392
|
+
console.log(`${c.info('💡')} ${c.bold('Recovery suggestions:')}`);
|
|
1393
|
+
console.log(` ${c.dim('→')} Check file permissions in the project directory`);
|
|
1394
|
+
console.log(` ${c.dim('→')} Ensure you have write access to the project files`);
|
|
1395
|
+
console.log(` ${c.dim('→')} Try running with elevated permissions if necessary\n`);
|
|
1396
|
+
}
|
|
1397
|
+
else if (errorLower.includes('network') ||
|
|
1398
|
+
errorLower.includes('timeout')) {
|
|
1399
|
+
console.log(`${c.info('💡')} ${c.bold('Recovery suggestions:')}`);
|
|
1400
|
+
console.log(` ${c.dim('→')} Check your internet connection`);
|
|
1401
|
+
console.log(` ${c.dim('→')} Verify the Guardrail API is accessible`);
|
|
1402
|
+
console.log(` ${c.dim('→')} Retry the operation after connectivity is restored\n`);
|
|
1403
|
+
}
|
|
1404
|
+
// Check for entitlement errors
|
|
1405
|
+
if (errorMessage.toLowerCase().includes('autopilot') &&
|
|
1406
|
+
(errorMessage.toLowerCase().includes('entitlement') ||
|
|
1407
|
+
errorMessage.toLowerCase().includes('feature'))) {
|
|
1408
|
+
(0, exit_codes_1.exitWith)(exit_codes_1.ExitCode.AUTH_FAILURE, errorMessage);
|
|
1409
|
+
}
|
|
1410
|
+
(0, exit_codes_1.exitWith)(exit_codes_1.ExitCode.SYSTEM_ERROR, errorMessage);
|
|
1411
|
+
}
|
|
1412
|
+
}
|
|
1413
|
+
/**
|
|
1414
|
+
* Register the autopilot command
|
|
1415
|
+
*/
|
|
1416
|
+
function registerAutopilotCommand(program, requireAuth, printLogo) {
|
|
1417
|
+
program
|
|
1418
|
+
.command('autopilot')
|
|
1419
|
+
.description('Autopilot batch remediation system (PRO feature)')
|
|
1420
|
+
.addHelpText('after', `
|
|
1421
|
+
Examples:
|
|
1422
|
+
$ guardrail autopilot --plan Generate and display fix plan
|
|
1423
|
+
$ guardrail autopilot --dry-run Preview changes without applying
|
|
1424
|
+
$ guardrail autopilot --interactive Interactive pack selection
|
|
1425
|
+
$ guardrail autopilot --max-fixes=10 Limit fixes per category
|
|
1426
|
+
$ guardrail autopilot --evidence Generate evidence pack
|
|
1427
|
+
$ guardrail autopilot --report Generate markdown report
|
|
1428
|
+
$ guardrail autopilot --export plan.json Export plan to file
|
|
1429
|
+
$ guardrail autopilot --import plan.json Import and apply plan
|
|
1430
|
+
$ guardrail autopilot --list List previous runs
|
|
1431
|
+
$ guardrail autopilot --format sarif Output as SARIF format
|
|
1432
|
+
$ guardrail autopilot --compare Show before/after comparison
|
|
1433
|
+
$ guardrail autopilot --rollback --run-id <id> Rollback previous run
|
|
1434
|
+
|
|
1435
|
+
Features:
|
|
1436
|
+
• Automated issue detection and grouping
|
|
1437
|
+
• Batch fix application with verification
|
|
1438
|
+
• Automatic rollback on failure
|
|
1439
|
+
• Git integration with branch creation
|
|
1440
|
+
• Evidence generation for compliance
|
|
1441
|
+
• Interactive pack selection
|
|
1442
|
+
• Detailed reporting and statistics
|
|
1443
|
+
• Configuration file support (.guardrail/autopilot.json)
|
|
1444
|
+
• CI/CD mode with auto-detection
|
|
1445
|
+
• State persistence and resume capability
|
|
1446
|
+
• Plan export/import functionality
|
|
1447
|
+
• Run history and management
|
|
1448
|
+
• SARIF output format for CI/CD integration
|
|
1449
|
+
• Before/after comparison statistics
|
|
1450
|
+
• Enterprise audit logging (JSONL format)
|
|
1451
|
+
• Compliance metrics and reporting
|
|
1452
|
+
• Change control tracking
|
|
1453
|
+
• Quality gate enforcement
|
|
1454
|
+
|
|
1455
|
+
Enterprise Features:
|
|
1456
|
+
• Audit trail with user attribution
|
|
1457
|
+
• Compliance framework support (SOC2, GDPR, etc.)
|
|
1458
|
+
• Change control verification
|
|
1459
|
+
• Evidence generation for audits
|
|
1460
|
+
• Quality gate enforcement
|
|
1461
|
+
• Rollback capability with audit trail
|
|
1462
|
+
|
|
1463
|
+
Configuration:
|
|
1464
|
+
Create .guardrail/autopilot.json to set default options:
|
|
1465
|
+
{
|
|
1466
|
+
"profile": "ship",
|
|
1467
|
+
"maxFixes": 50,
|
|
1468
|
+
"verify": true,
|
|
1469
|
+
"force": false,
|
|
1470
|
+
"branchStrategy": "worktree",
|
|
1471
|
+
"audit": true,
|
|
1472
|
+
"compliance": true,
|
|
1473
|
+
"evidence": false,
|
|
1474
|
+
"security": {
|
|
1475
|
+
"requireEvidence": false,
|
|
1476
|
+
"requireVerification": true,
|
|
1477
|
+
"allowHighRisk": false,
|
|
1478
|
+
"maxConcurrentFixes": 10
|
|
1479
|
+
},
|
|
1480
|
+
"complianceFrameworks": ["SOC2", "GDPR"]
|
|
1481
|
+
}
|
|
1482
|
+
|
|
1483
|
+
Enterprise Security:
|
|
1484
|
+
• Audit logging enabled by default (disable with --no-audit)
|
|
1485
|
+
• Evidence generation for compliance audits (--evidence)
|
|
1486
|
+
• Quality gate enforcement (--no-verify disabled by policy)
|
|
1487
|
+
• Change control tracking and verification
|
|
1488
|
+
• User attribution in audit logs
|
|
1489
|
+
• Rollback capability with full audit trail
|
|
1490
|
+
`)
|
|
1491
|
+
.option('-p, --path <path>', 'Project path to scan', '.')
|
|
1492
|
+
.option('--profile <profile>', 'Scan profile: quick, full, ship, ci', 'ship')
|
|
1493
|
+
.option('--dry-run', 'Show plan without applying changes', false)
|
|
1494
|
+
.option('--plan', 'Generate and display plan only', false)
|
|
1495
|
+
.option('--apply', 'Apply fixes (default if not --plan or --dry-run)', false)
|
|
1496
|
+
.option('--rollback', 'Rollback a previous autopilot run', false)
|
|
1497
|
+
.option('--run-id <id>', 'Run ID for rollback or resuming', '')
|
|
1498
|
+
.option('--interactive', 'Interactive pack selection and confirmation', false)
|
|
1499
|
+
.option('--max-fixes <number>', 'Maximum number of fixes per pack category', '')
|
|
1500
|
+
.option('--no-verify', 'Skip verification after applying fixes', false)
|
|
1501
|
+
.option('--force', 'Apply high-risk packs without confirmation', false)
|
|
1502
|
+
.option('--evidence', 'Generate signed evidence pack', false)
|
|
1503
|
+
.option('--report', 'Generate markdown report in .guardrail/reports', false)
|
|
1504
|
+
.option('--export <file>', 'Export plan to JSON file', '')
|
|
1505
|
+
.option('--import <file>', 'Import plan from JSON file', '')
|
|
1506
|
+
.option('--list', 'List previous autopilot runs', false)
|
|
1507
|
+
.option('--format <format>', 'Output format: table, json, sarif', 'table')
|
|
1508
|
+
.option('--json', 'Output results as JSON (alias for --format json)', false)
|
|
1509
|
+
.option('--sarif', 'Output results as SARIF format', false)
|
|
1510
|
+
.option('--compare', 'Show before/after comparison statistics', false)
|
|
1511
|
+
.option('--compliance', 'Show compliance metrics and summary', false)
|
|
1512
|
+
.option('--no-audit', 'Disable enterprise audit logging', false)
|
|
1513
|
+
.option('--config <file>', 'Path to autopilot config file (default: .guardrail/autopilot.json)', '')
|
|
1514
|
+
.action(async (opts) => {
|
|
1515
|
+
requireAuth();
|
|
1516
|
+
printLogo();
|
|
1517
|
+
// Normalize format options
|
|
1518
|
+
if (opts.json) {
|
|
1519
|
+
opts.format = 'json';
|
|
1520
|
+
}
|
|
1521
|
+
else if (opts.sarif) {
|
|
1522
|
+
opts.format = 'sarif';
|
|
1523
|
+
}
|
|
1524
|
+
else if (!opts.format) {
|
|
1525
|
+
opts.format = 'table';
|
|
1526
|
+
}
|
|
1527
|
+
// Show CI mode indicator
|
|
1528
|
+
if (isCIEnvironment() &&
|
|
1529
|
+
opts.format !== 'json' &&
|
|
1530
|
+
opts.format !== 'sarif') {
|
|
1531
|
+
console.log(`${c.dim('🔧 Running in CI/CD mode (non-interactive)')}\n`);
|
|
1532
|
+
}
|
|
1533
|
+
if (opts.format !== 'json' && opts.format !== 'sarif') {
|
|
1534
|
+
console.log(`\n${c.bold('🤖 AUTOPILOT')}\n`);
|
|
1535
|
+
}
|
|
1536
|
+
await runAutopilotCommand(opts.path, opts);
|
|
1537
|
+
});
|
|
1538
|
+
}
|
|
1539
|
+
//# sourceMappingURL=autopilot.js.map
|