vibecheck-ai 2.0.2 → 5.0.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/bin/.generated +25 -0
- package/bin/_deprecations.js +463 -0
- package/bin/_router.js +46 -0
- package/bin/cli-hygiene.js +241 -0
- package/bin/dev/run-v2-torture.js +30 -0
- package/bin/registry.js +656 -0
- package/bin/runners/CLI_REFACTOR_SUMMARY.md +229 -0
- package/bin/runners/ENHANCEMENT_GUIDE.md +121 -0
- package/bin/runners/REPORT_AUDIT.md +64 -0
- package/bin/runners/cli-utils.js +1070 -0
- package/bin/runners/context/ai-task-decomposer.js +337 -0
- package/bin/runners/context/analyzer.js +513 -0
- package/bin/runners/context/api-contracts.js +427 -0
- package/bin/runners/context/context-diff.js +342 -0
- package/bin/runners/context/context-pruner.js +291 -0
- package/bin/runners/context/dependency-graph.js +414 -0
- package/bin/runners/context/generators/claude.js +107 -0
- package/bin/runners/context/generators/codex.js +108 -0
- package/bin/runners/context/generators/copilot.js +119 -0
- package/bin/runners/context/generators/cursor-enhanced.js +2525 -0
- package/bin/runners/context/generators/cursor.js +514 -0
- package/bin/runners/context/generators/mcp.js +169 -0
- package/bin/runners/context/generators/windsurf.js +180 -0
- package/bin/runners/context/git-context.js +304 -0
- package/bin/runners/context/index.js +1110 -0
- package/bin/runners/context/insights.js +173 -0
- package/bin/runners/context/mcp-server/generate-rules.js +337 -0
- package/bin/runners/context/mcp-server/index.js +1176 -0
- package/bin/runners/context/mcp-server/package.json +24 -0
- package/bin/runners/context/memory.js +200 -0
- package/bin/runners/context/monorepo.js +215 -0
- package/bin/runners/context/multi-repo-federation.js +404 -0
- package/bin/runners/context/patterns.js +253 -0
- package/bin/runners/context/proof-context.js +1264 -0
- package/bin/runners/context/security-scanner.js +541 -0
- package/bin/runners/context/semantic-search.js +350 -0
- package/bin/runners/context/shared.js +264 -0
- package/bin/runners/context/team-conventions.js +336 -0
- package/bin/runners/lib/__tests__/entitlements-v2.test.js +295 -0
- package/bin/runners/lib/agent-firewall/ai/false-positive-analyzer.js +474 -0
- package/bin/runners/lib/agent-firewall/change-packet/builder.js +488 -0
- package/bin/runners/lib/agent-firewall/change-packet/schema.json +228 -0
- package/bin/runners/lib/agent-firewall/change-packet/store.js +200 -0
- package/bin/runners/lib/agent-firewall/claims/claim-types.js +21 -0
- package/bin/runners/lib/agent-firewall/claims/extractor.js +303 -0
- package/bin/runners/lib/agent-firewall/claims/patterns.js +24 -0
- package/bin/runners/lib/agent-firewall/critic/index.js +151 -0
- package/bin/runners/lib/agent-firewall/critic/judge.js +432 -0
- package/bin/runners/lib/agent-firewall/critic/prompts.js +305 -0
- package/bin/runners/lib/agent-firewall/enforcement/gateway.js +1059 -0
- package/bin/runners/lib/agent-firewall/enforcement/index.js +98 -0
- package/bin/runners/lib/agent-firewall/enforcement/mode.js +318 -0
- package/bin/runners/lib/agent-firewall/enforcement/orchestrator.js +484 -0
- package/bin/runners/lib/agent-firewall/enforcement/proof-artifact.js +418 -0
- package/bin/runners/lib/agent-firewall/enforcement/schemas/change-event.schema.json +173 -0
- package/bin/runners/lib/agent-firewall/enforcement/schemas/intent.schema.json +181 -0
- package/bin/runners/lib/agent-firewall/enforcement/schemas/verdict.schema.json +222 -0
- package/bin/runners/lib/agent-firewall/enforcement/verdict-v2.js +333 -0
- package/bin/runners/lib/agent-firewall/evidence/auth-evidence.js +88 -0
- package/bin/runners/lib/agent-firewall/evidence/contract-evidence.js +75 -0
- package/bin/runners/lib/agent-firewall/evidence/env-evidence.js +127 -0
- package/bin/runners/lib/agent-firewall/evidence/resolver.js +102 -0
- package/bin/runners/lib/agent-firewall/evidence/route-evidence.js +213 -0
- package/bin/runners/lib/agent-firewall/evidence/side-effect-evidence.js +145 -0
- package/bin/runners/lib/agent-firewall/fs-hook/daemon.js +19 -0
- package/bin/runners/lib/agent-firewall/fs-hook/installer.js +87 -0
- package/bin/runners/lib/agent-firewall/fs-hook/watcher.js +184 -0
- package/bin/runners/lib/agent-firewall/git-hook/pre-commit.js +163 -0
- package/bin/runners/lib/agent-firewall/ide-extension/cursor.js +107 -0
- package/bin/runners/lib/agent-firewall/ide-extension/vscode.js +68 -0
- package/bin/runners/lib/agent-firewall/ide-extension/windsurf.js +66 -0
- package/bin/runners/lib/agent-firewall/index.js +200 -0
- package/bin/runners/lib/agent-firewall/integration/index.js +20 -0
- package/bin/runners/lib/agent-firewall/integration/ship-gate.js +437 -0
- package/bin/runners/lib/agent-firewall/intent/alignment-engine.js +634 -0
- package/bin/runners/lib/agent-firewall/intent/auto-detect.js +426 -0
- package/bin/runners/lib/agent-firewall/intent/index.js +102 -0
- package/bin/runners/lib/agent-firewall/intent/schema.js +352 -0
- package/bin/runners/lib/agent-firewall/intent/store.js +283 -0
- package/bin/runners/lib/agent-firewall/interception/fs-interceptor.js +502 -0
- package/bin/runners/lib/agent-firewall/interception/index.js +23 -0
- package/bin/runners/lib/agent-firewall/interceptor/base.js +308 -0
- package/bin/runners/lib/agent-firewall/interceptor/cursor.js +35 -0
- package/bin/runners/lib/agent-firewall/interceptor/vscode.js +35 -0
- package/bin/runners/lib/agent-firewall/interceptor/windsurf.js +34 -0
- package/bin/runners/lib/agent-firewall/lawbook/distributor.js +465 -0
- package/bin/runners/lib/agent-firewall/lawbook/evaluator.js +604 -0
- package/bin/runners/lib/agent-firewall/lawbook/index.js +304 -0
- package/bin/runners/lib/agent-firewall/lawbook/registry.js +514 -0
- package/bin/runners/lib/agent-firewall/lawbook/schema.js +420 -0
- package/bin/runners/lib/agent-firewall/logger.js +141 -0
- package/bin/runners/lib/agent-firewall/policy/default-policy.json +90 -0
- package/bin/runners/lib/agent-firewall/policy/engine.js +103 -0
- package/bin/runners/lib/agent-firewall/policy/loader.js +451 -0
- package/bin/runners/lib/agent-firewall/policy/rules/auth-drift.js +50 -0
- package/bin/runners/lib/agent-firewall/policy/rules/contract-drift.js +50 -0
- package/bin/runners/lib/agent-firewall/policy/rules/fake-success.js +79 -0
- package/bin/runners/lib/agent-firewall/policy/rules/ghost-env.js +227 -0
- package/bin/runners/lib/agent-firewall/policy/rules/ghost-route.js +191 -0
- package/bin/runners/lib/agent-firewall/policy/rules/scope.js +93 -0
- package/bin/runners/lib/agent-firewall/policy/rules/unsafe-side-effect.js +57 -0
- package/bin/runners/lib/agent-firewall/policy/schema.json +183 -0
- package/bin/runners/lib/agent-firewall/policy/verdict.js +54 -0
- package/bin/runners/lib/agent-firewall/proposal/extractor.js +394 -0
- package/bin/runners/lib/agent-firewall/proposal/index.js +212 -0
- package/bin/runners/lib/agent-firewall/proposal/schema.js +251 -0
- package/bin/runners/lib/agent-firewall/proposal/validator.js +386 -0
- package/bin/runners/lib/agent-firewall/reality/index.js +332 -0
- package/bin/runners/lib/agent-firewall/reality/state.js +625 -0
- package/bin/runners/lib/agent-firewall/reality/watcher.js +322 -0
- package/bin/runners/lib/agent-firewall/risk/index.js +173 -0
- package/bin/runners/lib/agent-firewall/risk/scorer.js +328 -0
- package/bin/runners/lib/agent-firewall/risk/thresholds.js +322 -0
- package/bin/runners/lib/agent-firewall/risk/vectors.js +421 -0
- package/bin/runners/lib/agent-firewall/session/collector.js +451 -0
- package/bin/runners/lib/agent-firewall/session/index.js +26 -0
- package/bin/runners/lib/agent-firewall/simulator/diff-simulator.js +472 -0
- package/bin/runners/lib/agent-firewall/simulator/import-resolver.js +346 -0
- package/bin/runners/lib/agent-firewall/simulator/index.js +181 -0
- package/bin/runners/lib/agent-firewall/simulator/route-validator.js +380 -0
- package/bin/runners/lib/agent-firewall/time-machine/incident-correlator.js +661 -0
- package/bin/runners/lib/agent-firewall/time-machine/index.js +267 -0
- package/bin/runners/lib/agent-firewall/time-machine/replay-engine.js +436 -0
- package/bin/runners/lib/agent-firewall/time-machine/state-reconstructor.js +490 -0
- package/bin/runners/lib/agent-firewall/time-machine/timeline-builder.js +530 -0
- package/bin/runners/lib/agent-firewall/truthpack/index.js +67 -0
- package/bin/runners/lib/agent-firewall/truthpack/loader.js +137 -0
- package/bin/runners/lib/agent-firewall/unblock/planner.js +337 -0
- package/bin/runners/lib/agent-firewall/utils/ignore-checker.js +118 -0
- package/bin/runners/lib/ai-bridge.js +416 -0
- package/bin/runners/lib/analysis-core.js +309 -0
- package/bin/runners/lib/analyzers.js +2500 -0
- package/bin/runners/lib/api-client.js +269 -0
- package/bin/runners/lib/approve-output.js +235 -0
- package/bin/runners/lib/artifact-envelope.js +540 -0
- package/bin/runners/lib/assets/vibecheck-logo.png +0 -0
- package/bin/runners/lib/audit-bridge.js +391 -0
- package/bin/runners/lib/auth-shared.js +977 -0
- package/bin/runners/lib/auth-truth.js +193 -0
- package/bin/runners/lib/auth.js +215 -0
- package/bin/runners/lib/authority-badge.js +425 -0
- package/bin/runners/lib/backup.js +62 -0
- package/bin/runners/lib/billing.js +107 -0
- package/bin/runners/lib/checkpoint.js +941 -0
- package/bin/runners/lib/claims.js +118 -0
- package/bin/runners/lib/classify-output.js +204 -0
- package/bin/runners/lib/cleanup/engine.js +571 -0
- package/bin/runners/lib/cleanup/index.js +53 -0
- package/bin/runners/lib/cleanup/output.js +375 -0
- package/bin/runners/lib/cleanup/rules.js +1060 -0
- package/bin/runners/lib/cli-output.js +400 -0
- package/bin/runners/lib/cli-ui.js +540 -0
- package/bin/runners/lib/compliance-bridge-new.js +0 -0
- package/bin/runners/lib/compliance-bridge.js +165 -0
- package/bin/runners/lib/contracts/auth-contract.js +202 -0
- package/bin/runners/lib/contracts/env-contract.js +181 -0
- package/bin/runners/lib/contracts/external-contract.js +206 -0
- package/bin/runners/lib/contracts/guard.js +168 -0
- package/bin/runners/lib/contracts/index.js +89 -0
- package/bin/runners/lib/contracts/plan-validator.js +311 -0
- package/bin/runners/lib/contracts/route-contract.js +199 -0
- package/bin/runners/lib/contracts.js +804 -0
- package/bin/runners/lib/default-config.js +127 -0
- package/bin/runners/lib/detect.js +89 -0
- package/bin/runners/lib/detectors-v2.js +622 -0
- package/bin/runners/lib/doctor/autofix.js +254 -0
- package/bin/runners/lib/doctor/diagnosis-receipt.js +454 -0
- package/bin/runners/lib/doctor/failure-signatures.js +526 -0
- package/bin/runners/lib/doctor/fix-script.js +336 -0
- package/bin/runners/lib/doctor/index.js +37 -0
- package/bin/runners/lib/doctor/modules/build-tools.js +453 -0
- package/bin/runners/lib/doctor/modules/dependencies.js +325 -0
- package/bin/runners/lib/doctor/modules/index.js +105 -0
- package/bin/runners/lib/doctor/modules/network.js +250 -0
- package/bin/runners/lib/doctor/modules/os-quirks.js +706 -0
- package/bin/runners/lib/doctor/modules/project.js +312 -0
- package/bin/runners/lib/doctor/modules/repo-integrity.js +485 -0
- package/bin/runners/lib/doctor/modules/runtime.js +224 -0
- package/bin/runners/lib/doctor/modules/security.js +350 -0
- package/bin/runners/lib/doctor/modules/system.js +213 -0
- package/bin/runners/lib/doctor/modules/vibecheck.js +394 -0
- package/bin/runners/lib/doctor/reporter.js +262 -0
- package/bin/runners/lib/doctor/safe-repair.js +384 -0
- package/bin/runners/lib/doctor/service.js +262 -0
- package/bin/runners/lib/doctor/types.js +113 -0
- package/bin/runners/lib/doctor/ui.js +263 -0
- package/bin/runners/lib/doctor-enhanced.js +233 -0
- package/bin/runners/lib/doctor-output.js +226 -0
- package/bin/runners/lib/doctor-v2.js +608 -0
- package/bin/runners/lib/drift.js +425 -0
- package/bin/runners/lib/enforcement.js +72 -0
- package/bin/runners/lib/engine/ast-cache.js +210 -0
- package/bin/runners/lib/engine/auth-extractor.js +211 -0
- package/bin/runners/lib/engine/billing-extractor.js +112 -0
- package/bin/runners/lib/engine/enforcement-extractor.js +100 -0
- package/bin/runners/lib/engine/env-extractor.js +207 -0
- package/bin/runners/lib/engine/express-extractor.js +208 -0
- package/bin/runners/lib/engine/extractors.js +849 -0
- package/bin/runners/lib/engine/index.js +207 -0
- package/bin/runners/lib/engine/repo-index.js +514 -0
- package/bin/runners/lib/engine/types.js +124 -0
- package/bin/runners/lib/engines/accessibility-engine.js +190 -0
- package/bin/runners/lib/engines/api-consistency-engine.js +162 -0
- package/bin/runners/lib/engines/ast-cache.js +99 -0
- package/bin/runners/lib/engines/attack-detector.js +1192 -0
- package/bin/runners/lib/engines/code-quality-engine.js +255 -0
- package/bin/runners/lib/engines/console-logs-engine.js +115 -0
- package/bin/runners/lib/engines/cross-file-analysis-engine.js +268 -0
- package/bin/runners/lib/engines/dead-code-engine.js +198 -0
- package/bin/runners/lib/engines/deprecated-api-engine.js +226 -0
- package/bin/runners/lib/engines/empty-catch-engine.js +150 -0
- package/bin/runners/lib/engines/file-filter.js +131 -0
- package/bin/runners/lib/engines/hardcoded-secrets-engine.js +251 -0
- package/bin/runners/lib/engines/mock-data-engine.js +272 -0
- package/bin/runners/lib/engines/parallel-processor.js +71 -0
- package/bin/runners/lib/engines/performance-issues-engine.js +265 -0
- package/bin/runners/lib/engines/security-vulnerabilities-engine.js +243 -0
- package/bin/runners/lib/engines/todo-fixme-engine.js +115 -0
- package/bin/runners/lib/engines/type-aware-engine.js +152 -0
- package/bin/runners/lib/engines/unsafe-regex-engine.js +225 -0
- package/bin/runners/lib/engines/vibecheck-engines/README.md +53 -0
- package/bin/runners/lib/engines/vibecheck-engines/index.js +15 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/ast-cache.js +164 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/code-quality-engine.js +291 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/console-logs-engine.js +83 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/dead-code-engine.js +198 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/deprecated-api-engine.js +275 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/empty-catch-engine.js +167 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/file-filter.js +217 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/hardcoded-secrets-engine.js +139 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/mock-data-engine.js +140 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/parallel-processor.js +164 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/performance-issues-engine.js +234 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/type-aware-engine.js +217 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/unsafe-regex-engine.js +78 -0
- package/bin/runners/lib/engines/vibecheck-engines/package.json +13 -0
- package/bin/runners/lib/enterprise-detect.js +603 -0
- package/bin/runners/lib/enterprise-init.js +942 -0
- package/bin/runners/lib/entitlements-v2.js +265 -0
- package/bin/runners/lib/entitlements.generated.js +0 -0
- package/bin/runners/lib/entitlements.js +340 -0
- package/bin/runners/lib/env-resolver.js +417 -0
- package/bin/runners/lib/env-template.js +66 -0
- package/bin/runners/lib/env.js +189 -0
- package/bin/runners/lib/error-handler.js +368 -0
- package/bin/runners/lib/error-messages.js +289 -0
- package/bin/runners/lib/evidence-pack.js +684 -0
- package/bin/runners/lib/exit-codes.js +275 -0
- package/bin/runners/lib/extractors/client-calls.js +990 -0
- package/bin/runners/lib/extractors/fastify-route-dump.js +573 -0
- package/bin/runners/lib/extractors/fastify-routes.js +426 -0
- package/bin/runners/lib/extractors/index.js +363 -0
- package/bin/runners/lib/extractors/next-routes.js +524 -0
- package/bin/runners/lib/extractors/proof-graph.js +431 -0
- package/bin/runners/lib/extractors/route-matcher.js +451 -0
- package/bin/runners/lib/extractors/truthpack-v2.js +377 -0
- package/bin/runners/lib/extractors/ui-bindings.js +547 -0
- package/bin/runners/lib/finding-id.js +69 -0
- package/bin/runners/lib/finding-sorter.js +89 -0
- package/bin/runners/lib/findings-schema.js +281 -0
- package/bin/runners/lib/fingerprint.js +377 -0
- package/bin/runners/lib/firewall-prompt.js +50 -0
- package/bin/runners/lib/fix-output.js +228 -0
- package/bin/runners/lib/global-flags.js +250 -0
- package/bin/runners/lib/graph/graph-builder.js +265 -0
- package/bin/runners/lib/graph/html-renderer.js +413 -0
- package/bin/runners/lib/graph/index.js +32 -0
- package/bin/runners/lib/graph/runtime-collector.js +215 -0
- package/bin/runners/lib/graph/static-extractor.js +518 -0
- package/bin/runners/lib/help-formatter.js +413 -0
- package/bin/runners/lib/html-proof-report.js +913 -0
- package/bin/runners/lib/html-report.js +650 -0
- package/bin/runners/lib/init-wizard.js +601 -0
- package/bin/runners/lib/interactive-menu.js +1496 -0
- package/bin/runners/lib/json-output.js +76 -0
- package/bin/runners/lib/llm.js +75 -0
- package/bin/runners/lib/logger.js +38 -0
- package/bin/runners/lib/meter.js +61 -0
- package/bin/runners/lib/missions/briefing.js +427 -0
- package/bin/runners/lib/missions/checkpoint.js +753 -0
- package/bin/runners/lib/missions/evidence.js +126 -0
- package/bin/runners/lib/missions/hardening.js +851 -0
- package/bin/runners/lib/missions/plan.js +648 -0
- package/bin/runners/lib/missions/safety-gates.js +645 -0
- package/bin/runners/lib/missions/schema.js +478 -0
- package/bin/runners/lib/missions/templates.js +317 -0
- package/bin/runners/lib/next-action.js +560 -0
- package/bin/runners/lib/packs/bundle.js +675 -0
- package/bin/runners/lib/packs/evidence-pack.js +671 -0
- package/bin/runners/lib/packs/pack-factory.js +837 -0
- package/bin/runners/lib/packs/permissions-pack.js +686 -0
- package/bin/runners/lib/packs/proof-graph-pack.js +779 -0
- package/bin/runners/lib/patch.js +40 -0
- package/bin/runners/lib/permissions/auth-model.js +213 -0
- package/bin/runners/lib/permissions/idor-prover.js +205 -0
- package/bin/runners/lib/permissions/index.js +45 -0
- package/bin/runners/lib/permissions/matrix-builder.js +198 -0
- package/bin/runners/lib/pkgjson.js +28 -0
- package/bin/runners/lib/policy.js +295 -0
- package/bin/runners/lib/polish/accessibility.js +62 -0
- package/bin/runners/lib/polish/analyzer.js +93 -0
- package/bin/runners/lib/polish/backend.js +87 -0
- package/bin/runners/lib/polish/configuration.js +83 -0
- package/bin/runners/lib/polish/documentation.js +83 -0
- package/bin/runners/lib/polish/frontend.js +817 -0
- package/bin/runners/lib/polish/index.js +27 -0
- package/bin/runners/lib/polish/infrastructure.js +80 -0
- package/bin/runners/lib/polish/internationalization.js +85 -0
- package/bin/runners/lib/polish/libraries.js +180 -0
- package/bin/runners/lib/polish/observability.js +75 -0
- package/bin/runners/lib/polish/performance.js +64 -0
- package/bin/runners/lib/polish/privacy.js +110 -0
- package/bin/runners/lib/polish/resilience.js +92 -0
- package/bin/runners/lib/polish/security.js +78 -0
- package/bin/runners/lib/polish/seo.js +71 -0
- package/bin/runners/lib/polish/styles.js +62 -0
- package/bin/runners/lib/polish/utils.js +104 -0
- package/bin/runners/lib/preflight.js +142 -0
- package/bin/runners/lib/prerequisites.js +149 -0
- package/bin/runners/lib/prove-output.js +220 -0
- package/bin/runners/lib/reality/correlation-detectors.js +359 -0
- package/bin/runners/lib/reality/index.js +318 -0
- package/bin/runners/lib/reality/request-hashing.js +416 -0
- package/bin/runners/lib/reality/request-mapper.js +453 -0
- package/bin/runners/lib/reality/safety-rails.js +463 -0
- package/bin/runners/lib/reality/semantic-snapshot.js +408 -0
- package/bin/runners/lib/reality/toast-detector.js +393 -0
- package/bin/runners/lib/reality-findings.js +84 -0
- package/bin/runners/lib/reality-output.js +231 -0
- package/bin/runners/lib/receipts.js +179 -0
- package/bin/runners/lib/redact.js +29 -0
- package/bin/runners/lib/replay/capsule-manager.js +154 -0
- package/bin/runners/lib/replay/index.js +263 -0
- package/bin/runners/lib/replay/player.js +348 -0
- package/bin/runners/lib/replay/recorder.js +331 -0
- package/bin/runners/lib/report-engine.js +626 -0
- package/bin/runners/lib/report-html.js +1233 -0
- package/bin/runners/lib/report-output.js +366 -0
- package/bin/runners/lib/report-templates.js +967 -0
- package/bin/runners/lib/report.js +135 -0
- package/bin/runners/lib/route-detection.js +1209 -0
- package/bin/runners/lib/route-truth.js +1322 -0
- package/bin/runners/lib/safelist/index.js +96 -0
- package/bin/runners/lib/safelist/integration.js +334 -0
- package/bin/runners/lib/safelist/matcher.js +696 -0
- package/bin/runners/lib/safelist/schema.js +948 -0
- package/bin/runners/lib/safelist/store.js +438 -0
- package/bin/runners/lib/sandbox/index.js +59 -0
- package/bin/runners/lib/sandbox/proof-chain.js +399 -0
- package/bin/runners/lib/sandbox/sandbox-runner.js +205 -0
- package/bin/runners/lib/sandbox/worktree.js +174 -0
- package/bin/runners/lib/scan-cache.js +330 -0
- package/bin/runners/lib/scan-output-schema.js +344 -0
- package/bin/runners/lib/scan-output.js +631 -0
- package/bin/runners/lib/scan-runner.js +135 -0
- package/bin/runners/lib/schema-validator.js +350 -0
- package/bin/runners/lib/schemas/ajv-validator.js +464 -0
- package/bin/runners/lib/schemas/contracts.schema.json +160 -0
- package/bin/runners/lib/schemas/error-envelope.schema.json +105 -0
- package/bin/runners/lib/schemas/finding-v3.schema.json +151 -0
- package/bin/runners/lib/schemas/finding.schema.json +100 -0
- package/bin/runners/lib/schemas/mission-pack.schema.json +206 -0
- package/bin/runners/lib/schemas/proof-graph.schema.json +176 -0
- package/bin/runners/lib/schemas/reality-report.schema.json +162 -0
- package/bin/runners/lib/schemas/report-artifact.schema.json +120 -0
- package/bin/runners/lib/schemas/run-request.schema.json +108 -0
- package/bin/runners/lib/schemas/share-pack.schema.json +180 -0
- package/bin/runners/lib/schemas/ship-manifest.schema.json +251 -0
- package/bin/runners/lib/schemas/ship-report.schema.json +117 -0
- package/bin/runners/lib/schemas/truthpack-v2.schema.json +303 -0
- package/bin/runners/lib/schemas/validator.js +465 -0
- package/bin/runners/lib/schemas/verdict.schema.json +140 -0
- package/bin/runners/lib/score-history.js +282 -0
- package/bin/runners/lib/security-bridge.js +249 -0
- package/bin/runners/lib/server-usage.js +513 -0
- package/bin/runners/lib/share-pack.js +239 -0
- package/bin/runners/lib/ship-gate.js +832 -0
- package/bin/runners/lib/ship-manifest.js +1153 -0
- package/bin/runners/lib/ship-output-enterprise.js +239 -0
- package/bin/runners/lib/ship-output.js +1128 -0
- package/bin/runners/lib/snippets.js +67 -0
- package/bin/runners/lib/status-output.js +340 -0
- package/bin/runners/lib/terminal-ui.js +356 -0
- package/bin/runners/lib/truth.js +1691 -0
- package/bin/runners/lib/ui.js +562 -0
- package/bin/runners/lib/unified-cli-output.js +947 -0
- package/bin/runners/lib/unified-output.js +197 -0
- package/bin/runners/lib/upsell.js +410 -0
- package/bin/runners/lib/usage.js +153 -0
- package/bin/runners/lib/validate-patch.js +156 -0
- package/bin/runners/lib/verdict-engine.js +628 -0
- package/bin/runners/lib/verification.js +345 -0
- package/bin/runners/lib/why-tree.js +650 -0
- package/bin/runners/reality/engine.js +917 -0
- package/bin/runners/reality/flows.js +122 -0
- package/bin/runners/reality/report.js +378 -0
- package/bin/runners/reality/session.js +193 -0
- package/bin/runners/runAIAgent.js +229 -0
- package/bin/runners/runAgent.d.ts +5 -0
- package/bin/runners/runAgent.js +161 -0
- package/bin/runners/runAllowlist.js +418 -0
- package/bin/runners/runApprove.js +320 -0
- package/bin/runners/runAudit.js +692 -0
- package/bin/runners/runAuth.js +731 -0
- package/bin/runners/runCI.js +353 -0
- package/bin/runners/runCheckpoint.js +530 -0
- package/bin/runners/runClassify.js +928 -0
- package/bin/runners/runCleanup.js +343 -0
- package/bin/runners/runContext.d.ts +4 -0
- package/bin/runners/runContext.js +175 -0
- package/bin/runners/runDoctor.js +877 -0
- package/bin/runners/runEvidencePack.js +362 -0
- package/bin/runners/runFirewall.d.ts +5 -0
- package/bin/runners/runFirewall.js +134 -0
- package/bin/runners/runFirewallHook.d.ts +5 -0
- package/bin/runners/runFirewallHook.js +56 -0
- package/bin/runners/runFix.js +1355 -0
- package/bin/runners/runForge.js +380 -0
- package/bin/runners/runGuard.js +262 -0
- package/bin/runners/runInit.js +1927 -0
- package/bin/runners/runIntent.js +906 -0
- package/bin/runners/runKickoff.js +878 -0
- package/bin/runners/runLabs.js +424 -0
- package/bin/runners/runLaunch.js +2000 -0
- package/bin/runners/runLink.js +785 -0
- package/bin/runners/runMcp.js +1875 -0
- package/bin/runners/runPacks.js +2089 -0
- package/bin/runners/runPolish.d.ts +4 -0
- package/bin/runners/runPolish.js +390 -0
- package/bin/runners/runPromptFirewall.js +211 -0
- package/bin/runners/runProve.js +1411 -0
- package/bin/runners/runQuickstart.js +531 -0
- package/bin/runners/runReality.js +2260 -0
- package/bin/runners/runReport.js +726 -0
- package/bin/runners/runRuntime.js +110 -0
- package/bin/runners/runSafelist.js +1190 -0
- package/bin/runners/runScan.js +688 -0
- package/bin/runners/runShield.js +1282 -0
- package/bin/runners/runShip.js +1660 -0
- package/bin/runners/runTruth.d.ts +5 -0
- package/bin/runners/runTruth.js +101 -0
- package/bin/runners/runValidate.js +179 -0
- package/bin/runners/runWatch.js +478 -0
- package/bin/runners/utils.js +360 -0
- package/bin/scan.js +617 -0
- package/bin/vibecheck.js +1617 -0
- package/dist/guardrail/index.d.ts +2405 -0
- package/dist/guardrail/index.js +9747 -0
- package/dist/guardrail/index.js.map +1 -0
- package/dist/scanner/index.d.ts +282 -0
- package/dist/scanner/index.js +3395 -0
- package/dist/scanner/index.js.map +1 -0
- package/package.json +123 -104
- package/README.md +0 -491
- package/dist/index.js +0 -99711
- package/dist/index.js.map +0 -1
package/bin/vibecheck.js
ADDED
|
@@ -0,0 +1,1617 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// bin/vibecheck.js - World-Class CLI (Refactored)
|
|
3
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
4
|
+
// VibeCheck - Proves your app is real
|
|
5
|
+
// Ship with confidence. Catch fake features before your users do.
|
|
6
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
7
|
+
|
|
8
|
+
"use strict";
|
|
9
|
+
|
|
10
|
+
const { performance } = require("perf_hooks");
|
|
11
|
+
const STARTUP_TIME = performance.now();
|
|
12
|
+
|
|
13
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
14
|
+
// LAZY LOADING - Defer all requires until needed for fast startup
|
|
15
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
16
|
+
const lazyModules = {};
|
|
17
|
+
|
|
18
|
+
function lazy(name, loader) {
|
|
19
|
+
return () => {
|
|
20
|
+
if (!lazyModules[name]) {
|
|
21
|
+
lazyModules[name] = loader();
|
|
22
|
+
}
|
|
23
|
+
return lazyModules[name];
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const getFs = lazy("fs", () => require("fs"));
|
|
28
|
+
const getPath = lazy("path", () => require("path"));
|
|
29
|
+
const getOs = lazy("os", () => require("os"));
|
|
30
|
+
const getCrypto = lazy("crypto", () => require("crypto"));
|
|
31
|
+
const getHttps = lazy("https", () => require("https"));
|
|
32
|
+
const getReadline = lazy("readline", () => require("readline"));
|
|
33
|
+
|
|
34
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
35
|
+
// VERSION & METADATA (lazy loaded)
|
|
36
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
37
|
+
let _version = null;
|
|
38
|
+
function getVersion() {
|
|
39
|
+
if (_version) return _version;
|
|
40
|
+
try {
|
|
41
|
+
const fs = getFs();
|
|
42
|
+
const path = getPath();
|
|
43
|
+
const pkgPath = path.join(__dirname, "..", "package.json");
|
|
44
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
|
|
45
|
+
_version = pkg.version || "0.0.0";
|
|
46
|
+
} catch {
|
|
47
|
+
_version = "0.0.0";
|
|
48
|
+
}
|
|
49
|
+
return _version;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const CLI_NAME = "vibecheck";
|
|
53
|
+
const CONFIG_FILE = ".vibecheckrc";
|
|
54
|
+
const UPDATE_CHECK_INTERVAL = 24 * 60 * 60 * 1000; // 24 hours
|
|
55
|
+
|
|
56
|
+
// Cache/state paths (computed lazily)
|
|
57
|
+
let _cacheDir = null;
|
|
58
|
+
let _stateFile = null;
|
|
59
|
+
function getCacheDir() {
|
|
60
|
+
if (!_cacheDir) {
|
|
61
|
+
const os = getOs();
|
|
62
|
+
const path = getPath();
|
|
63
|
+
_cacheDir = path.join(os.homedir(), ".vibecheck");
|
|
64
|
+
}
|
|
65
|
+
return _cacheDir;
|
|
66
|
+
}
|
|
67
|
+
function getStateFile() {
|
|
68
|
+
if (!_stateFile) {
|
|
69
|
+
const path = getPath();
|
|
70
|
+
_stateFile = path.join(getCacheDir(), "state.json");
|
|
71
|
+
}
|
|
72
|
+
return _stateFile;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
76
|
+
// ANSI STYLES - Premium terminal styling with gradient support
|
|
77
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
78
|
+
const SUPPORTS_COLOR = process.stdout.isTTY && !process.env.NO_COLOR;
|
|
79
|
+
const SUPPORTS_TRUECOLOR = SUPPORTS_COLOR && (
|
|
80
|
+
process.env.COLORTERM === "truecolor" ||
|
|
81
|
+
process.env.TERM_PROGRAM === "iTerm.app" ||
|
|
82
|
+
process.env.TERM_PROGRAM === "Apple_Terminal" ||
|
|
83
|
+
process.env.WT_SESSION
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
const c = SUPPORTS_COLOR ? {
|
|
87
|
+
reset: "\x1b[0m",
|
|
88
|
+
bold: "\x1b[1m",
|
|
89
|
+
dim: "\x1b[2m",
|
|
90
|
+
italic: "\x1b[3m",
|
|
91
|
+
underline: "\x1b[4m",
|
|
92
|
+
blink: "\x1b[5m",
|
|
93
|
+
inverse: "\x1b[7m",
|
|
94
|
+
hidden: "\x1b[8m",
|
|
95
|
+
strikethrough: "\x1b[9m",
|
|
96
|
+
black: "\x1b[30m",
|
|
97
|
+
red: "\x1b[31m",
|
|
98
|
+
green: "\x1b[32m",
|
|
99
|
+
yellow: "\x1b[33m",
|
|
100
|
+
blue: "\x1b[34m",
|
|
101
|
+
magenta: "\x1b[35m",
|
|
102
|
+
cyan: "\x1b[36m",
|
|
103
|
+
white: "\x1b[37m",
|
|
104
|
+
gray: "\x1b[90m",
|
|
105
|
+
brightRed: "\x1b[91m",
|
|
106
|
+
brightGreen: "\x1b[92m",
|
|
107
|
+
brightYellow: "\x1b[93m",
|
|
108
|
+
brightBlue: "\x1b[94m",
|
|
109
|
+
brightMagenta: "\x1b[95m",
|
|
110
|
+
brightCyan: "\x1b[96m",
|
|
111
|
+
brightWhite: "\x1b[97m",
|
|
112
|
+
bgRed: "\x1b[41m",
|
|
113
|
+
bgGreen: "\x1b[42m",
|
|
114
|
+
bgYellow: "\x1b[43m",
|
|
115
|
+
bgBlue: "\x1b[44m",
|
|
116
|
+
bgMagenta: "\x1b[45m",
|
|
117
|
+
bgCyan: "\x1b[46m",
|
|
118
|
+
bgWhite: "\x1b[47m",
|
|
119
|
+
bgGray: "\x1b[100m",
|
|
120
|
+
rgb: (r, g, b) => SUPPORTS_TRUECOLOR ? `\x1b[38;2;${r};${g};${b}m` : "",
|
|
121
|
+
bgRgb: (r, g, b) => SUPPORTS_TRUECOLOR ? `\x1b[48;2;${r};${g};${b}m` : "",
|
|
122
|
+
} : Object.fromEntries([
|
|
123
|
+
"reset", "bold", "dim", "italic", "underline", "blink", "inverse", "hidden", "strikethrough",
|
|
124
|
+
"black", "red", "green", "yellow", "blue", "magenta", "cyan", "white", "gray",
|
|
125
|
+
"brightRed", "brightGreen", "brightYellow", "brightBlue", "brightMagenta", "brightCyan", "brightWhite",
|
|
126
|
+
"bgRed", "bgGreen", "bgYellow", "bgBlue", "bgMagenta", "bgCyan", "bgWhite", "bgGray"
|
|
127
|
+
].map(k => [k, ""]));
|
|
128
|
+
|
|
129
|
+
// Add no-op rgb functions for non-color mode
|
|
130
|
+
if (!SUPPORTS_COLOR) {
|
|
131
|
+
c.rgb = () => "";
|
|
132
|
+
c.bgRgb = () => "";
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function gradient(text, colors = [[0, 255, 255], [255, 0, 255], [255, 255, 0]]) {
|
|
136
|
+
if (!SUPPORTS_TRUECOLOR) return `${c.cyan}${text}${c.reset}`;
|
|
137
|
+
const chars = [...text];
|
|
138
|
+
const len = chars.length;
|
|
139
|
+
if (len === 0) return text;
|
|
140
|
+
return chars.map((char, i) => {
|
|
141
|
+
const t = i / Math.max(len - 1, 1);
|
|
142
|
+
const segmentLen = colors.length - 1;
|
|
143
|
+
const segment = Math.min(Math.floor(t * segmentLen), segmentLen - 1);
|
|
144
|
+
const localT = (t * segmentLen) - segment;
|
|
145
|
+
const c1 = colors[segment];
|
|
146
|
+
const c2 = colors[segment + 1] || c1;
|
|
147
|
+
const r = Math.round(c1[0] + (c2[0] - c1[0]) * localT);
|
|
148
|
+
const g = Math.round(c1[1] + (c2[1] - c1[1]) * localT);
|
|
149
|
+
const b = Math.round(c1[2] + (c2[2] - c1[2]) * localT);
|
|
150
|
+
return `${c.rgb(r, g, b)}${char}`;
|
|
151
|
+
}).join("") + c.reset;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
155
|
+
// UNICODE SYMBOLS
|
|
156
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
157
|
+
const SUPPORTS_UNICODE = process.platform !== "win32" || process.env.WT_SESSION || process.env.TERM_PROGRAM;
|
|
158
|
+
|
|
159
|
+
const sym = SUPPORTS_UNICODE ? {
|
|
160
|
+
success: "✓", error: "✗", warning: "⚠", info: "ℹ", pending: "○", active: "●",
|
|
161
|
+
spinner: ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"],
|
|
162
|
+
progress: ["░", "▒", "▓", "█"],
|
|
163
|
+
arrow: "→", arrowRight: "▸", arrowDown: "▾", bullet: "•",
|
|
164
|
+
star: "★", starEmpty: "☆", heart: "♥", shield: "🛡️", rocket: "🚀",
|
|
165
|
+
fire: "🔥", sparkles: "✨", check: "☑", box: "☐", lock: "🔒",
|
|
166
|
+
key: "🔑", lightning: "⚡", clock: "⏱", folder: "📁", file: "📄",
|
|
167
|
+
gear: "⚙", chart: "📊",
|
|
168
|
+
boxTopLeft: "╭", boxTopRight: "╮", boxBottomLeft: "╰", boxBottomRight: "╯",
|
|
169
|
+
boxHorizontal: "─", boxVertical: "│", boxCross: "┼",
|
|
170
|
+
boxHorizontalDown: "┬", boxHorizontalUp: "┴",
|
|
171
|
+
boxVerticalRight: "├", boxVerticalLeft: "┤",
|
|
172
|
+
dblBoxTopLeft: "╔", dblBoxTopRight: "╗", dblBoxBottomLeft: "╚", dblBoxBottomRight: "╝",
|
|
173
|
+
dblBoxHorizontal: "═", dblBoxVertical: "║",
|
|
174
|
+
} : {
|
|
175
|
+
success: "√", error: "×", warning: "!", info: "i", pending: "o", active: "*",
|
|
176
|
+
spinner: ["-", "\\", "|", "/"], progress: [".", ":", "#", "#"],
|
|
177
|
+
arrow: "->", arrowRight: ">", arrowDown: "v", bullet: "*",
|
|
178
|
+
star: "*", starEmpty: "o", heart: "<3", shield: "[#]", rocket: ">>",
|
|
179
|
+
fire: "(!)", sparkles: "*", check: "[x]", box: "[ ]", lock: "[L]",
|
|
180
|
+
key: "[K]", lightning: "!", clock: "[T]", folder: "[D]", file: "[F]",
|
|
181
|
+
gear: "[G]", chart: "[C]",
|
|
182
|
+
boxTopLeft: "+", boxTopRight: "+", boxBottomLeft: "+", boxBottomRight: "+",
|
|
183
|
+
boxHorizontal: "-", boxVertical: "|", boxCross: "+",
|
|
184
|
+
boxHorizontalDown: "+", boxHorizontalUp: "+",
|
|
185
|
+
boxVerticalRight: "+", boxVerticalLeft: "+",
|
|
186
|
+
dblBoxTopLeft: "+", dblBoxTopRight: "+", dblBoxBottomLeft: "+", dblBoxBottomRight: "+",
|
|
187
|
+
dblBoxHorizontal: "=", dblBoxVertical: "||",
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
191
|
+
// CI DETECTION
|
|
192
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
193
|
+
function isCI() {
|
|
194
|
+
return !!(
|
|
195
|
+
process.env.CI ||
|
|
196
|
+
process.env.CONTINUOUS_INTEGRATION ||
|
|
197
|
+
process.env.RAILWAY_ENVIRONMENT ||
|
|
198
|
+
process.env.VERCEL ||
|
|
199
|
+
process.env.NETLIFY ||
|
|
200
|
+
process.env.GITHUB_ACTIONS ||
|
|
201
|
+
process.env.GITLAB_CI ||
|
|
202
|
+
process.env.CIRCLECI ||
|
|
203
|
+
process.env.TRAVIS ||
|
|
204
|
+
process.env.BUILDKITE ||
|
|
205
|
+
process.env.RENDER ||
|
|
206
|
+
process.env.HEROKU ||
|
|
207
|
+
process.env.CODEBUILD_BUILD_ID ||
|
|
208
|
+
process.env.JENKINS_URL ||
|
|
209
|
+
process.env.TEAMCITY_VERSION ||
|
|
210
|
+
process.env.TF_BUILD ||
|
|
211
|
+
!process.stdin.isTTY
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
216
|
+
// SPINNER CLASS
|
|
217
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
218
|
+
class Spinner {
|
|
219
|
+
constructor(text = "") {
|
|
220
|
+
this.text = text;
|
|
221
|
+
this.frame = 0;
|
|
222
|
+
this.interval = null;
|
|
223
|
+
this.stream = process.stderr;
|
|
224
|
+
this.isCI = isCI();
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
start(text) {
|
|
228
|
+
if (text) this.text = text;
|
|
229
|
+
if (this.isCI) {
|
|
230
|
+
this.stream.write(`${c.dim}${sym.pending}${c.reset} ${this.text}\n`);
|
|
231
|
+
return this;
|
|
232
|
+
}
|
|
233
|
+
this.interval = setInterval(() => {
|
|
234
|
+
const spinner = sym.spinner[this.frame % sym.spinner.length];
|
|
235
|
+
this.stream.write(`\r${c.cyan}${spinner}${c.reset} ${this.text}`);
|
|
236
|
+
this.frame++;
|
|
237
|
+
}, 80);
|
|
238
|
+
return this;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
update(text) {
|
|
242
|
+
this.text = text;
|
|
243
|
+
if (this.isCI) this.stream.write(` ${c.dim}${sym.arrowRight}${c.reset} ${text}\n`);
|
|
244
|
+
return this;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
succeed(text) {
|
|
248
|
+
this.stop();
|
|
249
|
+
this.stream.write(`\r${c.green}${sym.success}${c.reset} ${text || this.text}\n`);
|
|
250
|
+
return this;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
fail(text) {
|
|
254
|
+
this.stop();
|
|
255
|
+
this.stream.write(`\r${c.red}${sym.error}${c.reset} ${text || this.text}\n`);
|
|
256
|
+
return this;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
warn(text) {
|
|
260
|
+
this.stop();
|
|
261
|
+
this.stream.write(`\r${c.yellow}${sym.warning}${c.reset} ${text || this.text}\n`);
|
|
262
|
+
return this;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
info(text) {
|
|
266
|
+
this.stop();
|
|
267
|
+
this.stream.write(`\r${c.blue}${sym.info}${c.reset} ${text || this.text}\n`);
|
|
268
|
+
return this;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
stop() {
|
|
272
|
+
if (this.interval) {
|
|
273
|
+
clearInterval(this.interval);
|
|
274
|
+
this.interval = null;
|
|
275
|
+
this.stream.write("\r\x1b[K");
|
|
276
|
+
}
|
|
277
|
+
return this;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
282
|
+
// STATE MANAGEMENT (lazy file I/O)
|
|
283
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
284
|
+
function ensureCacheDir() {
|
|
285
|
+
const fs = getFs();
|
|
286
|
+
const cacheDir = getCacheDir();
|
|
287
|
+
if (!fs.existsSync(cacheDir)) {
|
|
288
|
+
fs.mkdirSync(cacheDir, { recursive: true });
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function loadState() {
|
|
293
|
+
try {
|
|
294
|
+
const fs = getFs();
|
|
295
|
+
const stateFile = getStateFile();
|
|
296
|
+
if (fs.existsSync(stateFile)) {
|
|
297
|
+
return JSON.parse(fs.readFileSync(stateFile, "utf-8"));
|
|
298
|
+
}
|
|
299
|
+
} catch {}
|
|
300
|
+
return {
|
|
301
|
+
firstRun: Date.now(),
|
|
302
|
+
lastRun: null,
|
|
303
|
+
runCount: 0,
|
|
304
|
+
lastUpdateCheck: null,
|
|
305
|
+
latestVersion: null,
|
|
306
|
+
commandHistory: [],
|
|
307
|
+
favorites: [],
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
function saveState(state) {
|
|
312
|
+
try {
|
|
313
|
+
const fs = getFs();
|
|
314
|
+
ensureCacheDir();
|
|
315
|
+
fs.writeFileSync(getStateFile(), JSON.stringify(state, null, 2));
|
|
316
|
+
} catch {}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function loadConfig() {
|
|
320
|
+
const fs = getFs();
|
|
321
|
+
const path = getPath();
|
|
322
|
+
|
|
323
|
+
const config = {
|
|
324
|
+
debug: false,
|
|
325
|
+
verbose: false,
|
|
326
|
+
quiet: false,
|
|
327
|
+
color: true,
|
|
328
|
+
analytics: true,
|
|
329
|
+
updateCheck: true,
|
|
330
|
+
timeout: 30000,
|
|
331
|
+
maxRetries: 3,
|
|
332
|
+
noBanner: false,
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
const projectConfigPath = path.join(process.cwd(), CONFIG_FILE);
|
|
336
|
+
if (fs.existsSync(projectConfigPath)) {
|
|
337
|
+
try {
|
|
338
|
+
Object.assign(config, JSON.parse(fs.readFileSync(projectConfigPath, "utf-8")));
|
|
339
|
+
} catch {}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// Environment overrides
|
|
343
|
+
if (process.env.VIBECHECK_DEBUG === "true") config.debug = true;
|
|
344
|
+
if (process.env.VIBECHECK_VERBOSE === "true") config.verbose = true;
|
|
345
|
+
if (process.env.VIBECHECK_QUIET === "true") config.quiet = true;
|
|
346
|
+
if (process.env.VIBECHECK_NO_BANNER === "true") config.noBanner = true;
|
|
347
|
+
if (process.env.NO_COLOR || process.env.VIBECHECK_NO_COLOR) config.color = false;
|
|
348
|
+
|
|
349
|
+
return config;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
353
|
+
// UPDATE CHECKER - Actually implemented
|
|
354
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
355
|
+
async function checkForUpdates(state, config) {
|
|
356
|
+
if (!config.updateCheck) return null;
|
|
357
|
+
if (isCI()) return null;
|
|
358
|
+
|
|
359
|
+
const now = Date.now();
|
|
360
|
+
if (state.lastUpdateCheck && (now - state.lastUpdateCheck) < UPDATE_CHECK_INTERVAL) {
|
|
361
|
+
// Use cached version if recent
|
|
362
|
+
if (state.latestVersion && state.latestVersion !== getVersion()) {
|
|
363
|
+
return state.latestVersion;
|
|
364
|
+
}
|
|
365
|
+
return null;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// Async check - don't block CLI startup
|
|
369
|
+
return new Promise((resolve) => {
|
|
370
|
+
const https = getHttps();
|
|
371
|
+
const timeout = setTimeout(() => resolve(null), 3000); // 3s timeout
|
|
372
|
+
|
|
373
|
+
const req = https.get(
|
|
374
|
+
"https://registry.npmjs.org/@vibecheck/cli/latest",
|
|
375
|
+
{ headers: { "Accept": "application/json", "User-Agent": `vibecheck-cli/${getVersion()}` } },
|
|
376
|
+
(res) => {
|
|
377
|
+
let data = "";
|
|
378
|
+
res.on("data", (chunk) => { data += chunk; });
|
|
379
|
+
res.on("end", () => {
|
|
380
|
+
clearTimeout(timeout);
|
|
381
|
+
try {
|
|
382
|
+
const pkg = JSON.parse(data);
|
|
383
|
+
const latest = pkg.version;
|
|
384
|
+
state.lastUpdateCheck = now;
|
|
385
|
+
state.latestVersion = latest;
|
|
386
|
+
saveState(state);
|
|
387
|
+
|
|
388
|
+
if (latest && latest !== getVersion()) {
|
|
389
|
+
resolve(latest);
|
|
390
|
+
} else {
|
|
391
|
+
resolve(null);
|
|
392
|
+
}
|
|
393
|
+
} catch {
|
|
394
|
+
resolve(null);
|
|
395
|
+
}
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
);
|
|
399
|
+
|
|
400
|
+
req.on("error", () => {
|
|
401
|
+
clearTimeout(timeout);
|
|
402
|
+
resolve(null);
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
req.end();
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
function printUpdateNotice(latestVersion) {
|
|
410
|
+
const currentVersion = getVersion();
|
|
411
|
+
console.log();
|
|
412
|
+
console.log(`${c.yellow}${sym.sparkles}${c.reset} Update available: ${c.dim}${currentVersion}${c.reset} ${sym.arrow} ${c.green}${latestVersion}${c.reset}`);
|
|
413
|
+
console.log(` Run ${c.cyan}npm update -g @vibecheck/cli${c.reset} to update`);
|
|
414
|
+
console.log();
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
418
|
+
// FUZZY MATCHING
|
|
419
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
420
|
+
function levenshtein(a, b) {
|
|
421
|
+
const matrix = [];
|
|
422
|
+
for (let i = 0; i <= b.length; i++) matrix[i] = [i];
|
|
423
|
+
for (let j = 0; j <= a.length; j++) matrix[0][j] = j;
|
|
424
|
+
for (let i = 1; i <= b.length; i++) {
|
|
425
|
+
for (let j = 1; j <= a.length; j++) {
|
|
426
|
+
if (b.charAt(i - 1) === a.charAt(j - 1)) {
|
|
427
|
+
matrix[i][j] = matrix[i - 1][j - 1];
|
|
428
|
+
} else {
|
|
429
|
+
matrix[i][j] = Math.min(
|
|
430
|
+
matrix[i - 1][j - 1] + 1,
|
|
431
|
+
matrix[i][j - 1] + 1,
|
|
432
|
+
matrix[i - 1][j] + 1
|
|
433
|
+
);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
return matrix[b.length][a.length];
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
function findSimilarCommands(input, commands, maxDistance = 3) {
|
|
441
|
+
const matches = [];
|
|
442
|
+
for (const cmd of commands) {
|
|
443
|
+
const distance = levenshtein(input.toLowerCase(), cmd.toLowerCase());
|
|
444
|
+
if (distance <= maxDistance) {
|
|
445
|
+
matches.push({ cmd, distance });
|
|
446
|
+
}
|
|
447
|
+
if (cmd.toLowerCase().startsWith(input.toLowerCase()) && input.length >= 2) {
|
|
448
|
+
matches.push({ cmd, distance: 0.5 });
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
return [...new Set(matches.sort((a, b) => a.distance - b.distance).map(m => m.cmd))].slice(0, 3);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
455
|
+
// COMMAND REGISTRY (lazy loaded)
|
|
456
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
457
|
+
let _registry = null;
|
|
458
|
+
function getRegistry() {
|
|
459
|
+
if (!_registry) {
|
|
460
|
+
_registry = require("./registry");
|
|
461
|
+
}
|
|
462
|
+
return _registry;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
function getRunner(cmd) {
|
|
466
|
+
const registry = getRegistry();
|
|
467
|
+
return registry.getRunner(cmd, { red: c.red, reset: c.reset, errorSymbol: sym.error });
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
471
|
+
// ENTITLEMENTS & UPSELL (lazy loaded)
|
|
472
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
473
|
+
let _entitlements = null;
|
|
474
|
+
let _upsell = null;
|
|
475
|
+
|
|
476
|
+
function getEntitlements() {
|
|
477
|
+
if (!_entitlements) {
|
|
478
|
+
_entitlements = require("./runners/lib/entitlements-v2");
|
|
479
|
+
}
|
|
480
|
+
return _entitlements;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
function getUpsell() {
|
|
484
|
+
if (!_upsell) {
|
|
485
|
+
_upsell = require("./runners/lib/upsell");
|
|
486
|
+
}
|
|
487
|
+
return _upsell;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
491
|
+
// CLI OUTPUT UTILITIES (lazy loaded)
|
|
492
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
493
|
+
let _cliOutput = null;
|
|
494
|
+
function getCliOutput() {
|
|
495
|
+
if (!_cliOutput) {
|
|
496
|
+
_cliOutput = require("./runners/lib/cli-output");
|
|
497
|
+
}
|
|
498
|
+
return _cliOutput;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
502
|
+
// AUTH MODULE (lazy loaded)
|
|
503
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
504
|
+
let _authModule = null;
|
|
505
|
+
function getAuthModule() {
|
|
506
|
+
if (!_authModule) {
|
|
507
|
+
_authModule = require("./runners/lib/auth");
|
|
508
|
+
}
|
|
509
|
+
return _authModule;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
513
|
+
// ACCESS CONTROL
|
|
514
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
515
|
+
async function checkCommandAccess(cmd, args, authInfo) {
|
|
516
|
+
const registry = getRegistry();
|
|
517
|
+
const def = registry.COMMANDS[cmd];
|
|
518
|
+
if (!def) return { allowed: true };
|
|
519
|
+
|
|
520
|
+
const entitlements = getEntitlements();
|
|
521
|
+
const result = await entitlements.enforce(cmd, {
|
|
522
|
+
apiKey: authInfo?.key,
|
|
523
|
+
projectPath: process.cwd(),
|
|
524
|
+
silent: true,
|
|
525
|
+
});
|
|
526
|
+
|
|
527
|
+
if (result.allowed) {
|
|
528
|
+
return {
|
|
529
|
+
allowed: true,
|
|
530
|
+
tier: result.tier,
|
|
531
|
+
downgrade: result.downgrade,
|
|
532
|
+
limits: result.limits,
|
|
533
|
+
caps: result.caps,
|
|
534
|
+
};
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
const upsell = getUpsell();
|
|
538
|
+
return {
|
|
539
|
+
allowed: false,
|
|
540
|
+
tier: result.tier,
|
|
541
|
+
requiredTier: result.requiredTier,
|
|
542
|
+
exitCode: result.exitCode,
|
|
543
|
+
reason: upsell.formatDenied(cmd, {
|
|
544
|
+
currentTier: result.tier,
|
|
545
|
+
requiredTier: result.requiredTier,
|
|
546
|
+
}),
|
|
547
|
+
};
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
551
|
+
// SHELL COMPLETIONS
|
|
552
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
553
|
+
function generateBashCompletion() {
|
|
554
|
+
const registry = getRegistry();
|
|
555
|
+
const commands = Object.keys(registry.COMMANDS);
|
|
556
|
+
const aliases = Object.keys(registry.ALIAS_MAP);
|
|
557
|
+
const allCmds = [...commands, ...aliases].join(" ");
|
|
558
|
+
|
|
559
|
+
return `# vibecheck bash completion
|
|
560
|
+
# Add to ~/.bashrc or ~/.bash_profile:
|
|
561
|
+
# eval "$(vibecheck completion bash)"
|
|
562
|
+
|
|
563
|
+
_vibecheck_completions() {
|
|
564
|
+
local cur="\${COMP_WORDS[COMP_CWORD]}"
|
|
565
|
+
local prev="\${COMP_WORDS[COMP_CWORD-1]}"
|
|
566
|
+
|
|
567
|
+
# Main commands
|
|
568
|
+
local commands="${allCmds}"
|
|
569
|
+
|
|
570
|
+
# Global flags
|
|
571
|
+
local global_flags="--help --version --json --ci --quiet --verbose --debug --path --output --no-banner"
|
|
572
|
+
|
|
573
|
+
case "\${prev}" in
|
|
574
|
+
vibecheck|vc)
|
|
575
|
+
COMPREPLY=($(compgen -W "\${commands} \${global_flags}" -- "\${cur}"))
|
|
576
|
+
return 0
|
|
577
|
+
;;
|
|
578
|
+
--path|--output|-p|-o)
|
|
579
|
+
COMPREPLY=($(compgen -d -- "\${cur}"))
|
|
580
|
+
return 0
|
|
581
|
+
;;
|
|
582
|
+
init)
|
|
583
|
+
COMPREPLY=($(compgen -W "--local --connect --quick --enterprise --ci --soc2 --hipaa --gdpr --team --mcp" -- "\${cur}"))
|
|
584
|
+
return 0
|
|
585
|
+
;;
|
|
586
|
+
scan)
|
|
587
|
+
COMPREPLY=($(compgen -W "--fix --autofix --json --strict --path" -- "\${cur}"))
|
|
588
|
+
return 0
|
|
589
|
+
;;
|
|
590
|
+
report)
|
|
591
|
+
COMPREPLY=($(compgen -W "--format --output html md json sarif csv" -- "\${cur}"))
|
|
592
|
+
return 0
|
|
593
|
+
;;
|
|
594
|
+
completion)
|
|
595
|
+
COMPREPLY=($(compgen -W "bash zsh fish" -- "\${cur}"))
|
|
596
|
+
return 0
|
|
597
|
+
;;
|
|
598
|
+
esac
|
|
599
|
+
|
|
600
|
+
if [[ "\${cur}" == -* ]]; then
|
|
601
|
+
COMPREPLY=($(compgen -W "\${global_flags}" -- "\${cur}"))
|
|
602
|
+
fi
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
complete -F _vibecheck_completions vibecheck
|
|
606
|
+
complete -F _vibecheck_completions vc
|
|
607
|
+
`;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
function generateZshCompletion() {
|
|
611
|
+
const registry = getRegistry();
|
|
612
|
+
const commands = Object.entries(registry.COMMANDS);
|
|
613
|
+
|
|
614
|
+
let cmdList = commands.map(([cmd, def]) => {
|
|
615
|
+
const desc = (def.description || "").replace(/'/g, "\\'").replace(/"/g, '\\"');
|
|
616
|
+
return ` '${cmd}:${desc}'`;
|
|
617
|
+
}).join(" \\\n");
|
|
618
|
+
|
|
619
|
+
return `#compdef vibecheck vc
|
|
620
|
+
# vibecheck zsh completion
|
|
621
|
+
# Add to ~/.zshrc:
|
|
622
|
+
# eval "$(vibecheck completion zsh)"
|
|
623
|
+
|
|
624
|
+
_vibecheck() {
|
|
625
|
+
local -a commands
|
|
626
|
+
commands=(
|
|
627
|
+
${cmdList}
|
|
628
|
+
)
|
|
629
|
+
|
|
630
|
+
local -a global_opts
|
|
631
|
+
global_opts=(
|
|
632
|
+
'--help[Show help]'
|
|
633
|
+
'--version[Show version]'
|
|
634
|
+
'--json[Output as JSON]'
|
|
635
|
+
'--ci[CI mode]'
|
|
636
|
+
'--quiet[Suppress output]'
|
|
637
|
+
'--verbose[Verbose output]'
|
|
638
|
+
'--debug[Debug mode]'
|
|
639
|
+
'--path[Project path]:directory:_directories'
|
|
640
|
+
'--output[Output directory]:directory:_directories'
|
|
641
|
+
'--no-banner[Hide banner]'
|
|
642
|
+
)
|
|
643
|
+
|
|
644
|
+
_arguments -C \\
|
|
645
|
+
"1: :->command" \\
|
|
646
|
+
"*::arg:->args"
|
|
647
|
+
|
|
648
|
+
case "\$state" in
|
|
649
|
+
command)
|
|
650
|
+
_describe -t commands 'vibecheck commands' commands
|
|
651
|
+
_describe -t options 'options' global_opts
|
|
652
|
+
;;
|
|
653
|
+
args)
|
|
654
|
+
case "\$words[1]" in
|
|
655
|
+
init)
|
|
656
|
+
_arguments \\
|
|
657
|
+
'--local[Local setup only]' \\
|
|
658
|
+
'--connect[GitHub integration]' \\
|
|
659
|
+
'--quick[Quick setup]' \\
|
|
660
|
+
'--enterprise[Enterprise mode]' \\
|
|
661
|
+
'--ci[Setup CI/CD]' \\
|
|
662
|
+
'--soc2[SOC2 compliance]' \\
|
|
663
|
+
'--hipaa[HIPAA compliance]'
|
|
664
|
+
;;
|
|
665
|
+
scan)
|
|
666
|
+
_arguments \\
|
|
667
|
+
'--fix[Apply fixes]' \\
|
|
668
|
+
'--autofix[Auto-fix issues]' \\
|
|
669
|
+
'--json[JSON output]' \\
|
|
670
|
+
'--strict[Strict mode]'
|
|
671
|
+
;;
|
|
672
|
+
report)
|
|
673
|
+
_arguments \\
|
|
674
|
+
'--format[Output format]:format:(html md json sarif csv)' \\
|
|
675
|
+
'--output[Output file]:file:_files'
|
|
676
|
+
;;
|
|
677
|
+
completion)
|
|
678
|
+
_arguments '1:shell:(bash zsh fish)'
|
|
679
|
+
;;
|
|
680
|
+
esac
|
|
681
|
+
;;
|
|
682
|
+
esac
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
compdef _vibecheck vibecheck
|
|
686
|
+
compdef _vibecheck vc
|
|
687
|
+
`;
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
function generateFishCompletion() {
|
|
691
|
+
const registry = getRegistry();
|
|
692
|
+
const commands = Object.entries(registry.COMMANDS);
|
|
693
|
+
|
|
694
|
+
let completions = `# vibecheck fish completion
|
|
695
|
+
# Add to ~/.config/fish/completions/vibecheck.fish
|
|
696
|
+
# Or run: vibecheck completion fish > ~/.config/fish/completions/vibecheck.fish
|
|
697
|
+
|
|
698
|
+
# Disable file completion by default
|
|
699
|
+
complete -c vibecheck -f
|
|
700
|
+
complete -c vc -f
|
|
701
|
+
|
|
702
|
+
# Global flags
|
|
703
|
+
complete -c vibecheck -l help -d 'Show help'
|
|
704
|
+
complete -c vibecheck -l version -d 'Show version'
|
|
705
|
+
complete -c vibecheck -l json -d 'Output as JSON'
|
|
706
|
+
complete -c vibecheck -l ci -d 'CI mode'
|
|
707
|
+
complete -c vibecheck -l quiet -d 'Suppress output'
|
|
708
|
+
complete -c vibecheck -l verbose -d 'Verbose output'
|
|
709
|
+
complete -c vibecheck -l debug -d 'Debug mode'
|
|
710
|
+
complete -c vibecheck -l path -d 'Project path' -r -a '(__fish_complete_directories)'
|
|
711
|
+
complete -c vibecheck -l output -d 'Output directory' -r -a '(__fish_complete_directories)'
|
|
712
|
+
complete -c vibecheck -l no-banner -d 'Hide banner'
|
|
713
|
+
|
|
714
|
+
# Commands
|
|
715
|
+
`;
|
|
716
|
+
|
|
717
|
+
for (const [cmd, def] of commands) {
|
|
718
|
+
const desc = (def.description || "").replace(/'/g, "\\'");
|
|
719
|
+
completions += `complete -c vibecheck -n '__fish_use_subcommand' -a '${cmd}' -d '${desc}'\n`;
|
|
720
|
+
completions += `complete -c vc -n '__fish_use_subcommand' -a '${cmd}' -d '${desc}'\n`;
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
// Add aliases
|
|
724
|
+
for (const [alias, target] of Object.entries(registry.ALIAS_MAP)) {
|
|
725
|
+
const def = registry.COMMANDS[target];
|
|
726
|
+
if (def) {
|
|
727
|
+
const desc = `Alias for ${target}`;
|
|
728
|
+
completions += `complete -c vibecheck -n '__fish_use_subcommand' -a '${alias}' -d '${desc}'\n`;
|
|
729
|
+
completions += `complete -c vc -n '__fish_use_subcommand' -a '${alias}' -d '${desc}'\n`;
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
completions += `
|
|
734
|
+
# init subcommand options
|
|
735
|
+
complete -c vibecheck -n '__fish_seen_subcommand_from init' -l local -d 'Local setup only'
|
|
736
|
+
complete -c vibecheck -n '__fish_seen_subcommand_from init' -l connect -d 'GitHub integration'
|
|
737
|
+
complete -c vibecheck -n '__fish_seen_subcommand_from init' -l quick -d 'Quick setup'
|
|
738
|
+
complete -c vibecheck -n '__fish_seen_subcommand_from init' -l enterprise -d 'Enterprise mode'
|
|
739
|
+
|
|
740
|
+
# scan subcommand options
|
|
741
|
+
complete -c vibecheck -n '__fish_seen_subcommand_from scan' -l fix -d 'Apply fixes'
|
|
742
|
+
complete -c vibecheck -n '__fish_seen_subcommand_from scan' -l autofix -d 'Auto-fix issues'
|
|
743
|
+
complete -c vibecheck -n '__fish_seen_subcommand_from scan' -l strict -d 'Strict mode'
|
|
744
|
+
|
|
745
|
+
# report subcommand options
|
|
746
|
+
complete -c vibecheck -n '__fish_seen_subcommand_from report' -l format -d 'Output format' -r -a 'html md json sarif csv'
|
|
747
|
+
|
|
748
|
+
# completion subcommand
|
|
749
|
+
complete -c vibecheck -n '__fish_seen_subcommand_from completion' -a 'bash zsh fish' -d 'Shell type'
|
|
750
|
+
`;
|
|
751
|
+
|
|
752
|
+
return completions;
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
function runCompletion(args) {
|
|
756
|
+
const shell = args[0];
|
|
757
|
+
|
|
758
|
+
if (!shell || shell === "--help" || shell === "-h") {
|
|
759
|
+
console.log(`
|
|
760
|
+
${c.bold}Usage:${c.reset} vibecheck completion <shell>
|
|
761
|
+
|
|
762
|
+
${c.bold}Shells:${c.reset}
|
|
763
|
+
bash Generate bash completions
|
|
764
|
+
zsh Generate zsh completions
|
|
765
|
+
fish Generate fish completions
|
|
766
|
+
|
|
767
|
+
${c.bold}Installation:${c.reset}
|
|
768
|
+
${c.dim}# Bash (add to ~/.bashrc)${c.reset}
|
|
769
|
+
eval "$(vibecheck completion bash)"
|
|
770
|
+
|
|
771
|
+
${c.dim}# Zsh (add to ~/.zshrc)${c.reset}
|
|
772
|
+
eval "$(vibecheck completion zsh)"
|
|
773
|
+
|
|
774
|
+
${c.dim}# Fish${c.reset}
|
|
775
|
+
vibecheck completion fish > ~/.config/fish/completions/vibecheck.fish
|
|
776
|
+
`);
|
|
777
|
+
return 0;
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
switch (shell.toLowerCase()) {
|
|
781
|
+
case "bash":
|
|
782
|
+
console.log(generateBashCompletion());
|
|
783
|
+
return 0;
|
|
784
|
+
case "zsh":
|
|
785
|
+
console.log(generateZshCompletion());
|
|
786
|
+
return 0;
|
|
787
|
+
case "fish":
|
|
788
|
+
console.log(generateFishCompletion());
|
|
789
|
+
return 0;
|
|
790
|
+
default:
|
|
791
|
+
console.error(`${c.red}${sym.error}${c.reset} Unknown shell: ${shell}`);
|
|
792
|
+
console.error(`Supported shells: bash, zsh, fish`);
|
|
793
|
+
return 1;
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
798
|
+
// HELP SYSTEM - World-Class CLI Experience
|
|
799
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
800
|
+
function printBanner() {
|
|
801
|
+
const VERSION = getVersion();
|
|
802
|
+
console.log(`
|
|
803
|
+
${c.dim}${sym.boxTopLeft}${sym.boxHorizontal.repeat(64)}${sym.boxTopRight}${c.reset}
|
|
804
|
+
${c.dim}${sym.boxVertical}${c.reset} ${gradient("VIBECHECK", [[0, 255, 255], [138, 43, 226], [255, 20, 147]])} ${c.dim}v${VERSION}${c.reset}${" ".repeat(64 - 13 - VERSION.length - 4)}${c.dim}${sym.boxVertical}${c.reset}
|
|
805
|
+
${c.dim}${sym.boxVertical}${c.reset} ${c.dim}${sym.boxVertical}${c.reset}
|
|
806
|
+
${c.dim}${sym.boxVertical}${c.reset} ${c.bold}Catch AI hallucinations before your users do.${c.reset} ${c.dim}${sym.boxVertical}${c.reset}
|
|
807
|
+
${c.dim}${sym.boxVertical}${c.reset} ${c.dim}${sym.boxVertical}${c.reset}
|
|
808
|
+
${c.dim}${sym.boxVertical}${c.reset} ${c.cyan}${sym.check}${c.reset} Detects routes that look real but don't work ${c.dim}${sym.boxVertical}${c.reset}
|
|
809
|
+
${c.dim}${sym.boxVertical}${c.reset} ${c.cyan}${sym.check}${c.reset} Finds env vars used but never declared ${c.dim}${sym.boxVertical}${c.reset}
|
|
810
|
+
${c.dim}${sym.boxVertical}${c.reset} ${c.cyan}${sym.check}${c.reset} Flags auth endpoints with no protection ${c.dim}${sym.boxVertical}${c.reset}
|
|
811
|
+
${c.dim}${sym.boxVertical}${c.reset} ${c.dim}${sym.boxVertical}${c.reset}
|
|
812
|
+
${c.dim}${sym.boxVertical}${c.reset} ${c.dim}SHIP = proof your code works. Not a vibe check.${c.reset} ${c.dim}${sym.boxVertical}${c.reset}
|
|
813
|
+
${c.dim}${sym.boxBottomLeft}${sym.boxHorizontal.repeat(64)}${sym.boxBottomRight}${c.reset}
|
|
814
|
+
`);
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
/**
|
|
818
|
+
* Print command-specific help with rich examples
|
|
819
|
+
*/
|
|
820
|
+
function printCommandHelp(cmd) {
|
|
821
|
+
const registry = getRegistry();
|
|
822
|
+
const def = registry.COMMANDS[cmd];
|
|
823
|
+
if (!def) return false;
|
|
824
|
+
|
|
825
|
+
// Build reverse alias map
|
|
826
|
+
const reverseAliases = {};
|
|
827
|
+
for (const [alias, target] of Object.entries(registry.ALIAS_MAP)) {
|
|
828
|
+
if (!reverseAliases[target]) reverseAliases[target] = [];
|
|
829
|
+
reverseAliases[target].push(alias);
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
const aliases = reverseAliases[cmd] || [];
|
|
833
|
+
|
|
834
|
+
// Tier badge (2-tier: FREE / PRO)
|
|
835
|
+
const tierBadge = def.tier === "free" ? `${c.green}[FREE]${c.reset}` :
|
|
836
|
+
def.tier === "pro" ? `${c.magenta}[PRO]${c.reset}` : "";
|
|
837
|
+
|
|
838
|
+
console.log(`
|
|
839
|
+
${c.bold}${sym.arrowRight} vibecheck ${cmd}${c.reset} ${tierBadge}
|
|
840
|
+
${aliases.length > 0 ? `${c.dim}Aliases: ${aliases.join(", ")}${c.reset}` : ""}
|
|
841
|
+
|
|
842
|
+
${def.longDescription || def.description}
|
|
843
|
+
`);
|
|
844
|
+
|
|
845
|
+
// Examples
|
|
846
|
+
if (def.examples && def.examples.length > 0) {
|
|
847
|
+
console.log(` ${c.bold}${sym.star} EXAMPLES${c.reset}\n`);
|
|
848
|
+
for (const ex of def.examples) {
|
|
849
|
+
const exTier = ex.tier === "pro" ? `${c.magenta}[PRO]${c.reset} ` : "";
|
|
850
|
+
console.log(` ${c.dim}#${c.reset} ${ex.description} ${exTier}`);
|
|
851
|
+
console.log(` ${c.cyan}${ex.command}${c.reset}`);
|
|
852
|
+
console.log();
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
// Related commands
|
|
857
|
+
if (def.related && def.related.length > 0) {
|
|
858
|
+
console.log(` ${c.bold}${sym.arrowRight} RELATED COMMANDS${c.reset}\n`);
|
|
859
|
+
for (const relCmd of def.related) {
|
|
860
|
+
const relDef = registry.COMMANDS[relCmd];
|
|
861
|
+
if (relDef) {
|
|
862
|
+
const relTier = relDef.tier === "pro" ? `${c.magenta}[PRO]${c.reset} ` : "";
|
|
863
|
+
console.log(` ${c.cyan}vibecheck ${relCmd}${c.reset} ${relTier}${c.dim}${relDef.description}${c.reset}`);
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
console.log();
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
// Documentation link
|
|
870
|
+
if (def.docsUrl) {
|
|
871
|
+
console.log(` ${c.dim}${sym.boxHorizontal.repeat(56)}${c.reset}`);
|
|
872
|
+
console.log(` ${c.dim}Documentation: ${c.underline}${def.docsUrl}${c.reset}`);
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
console.log(` ${c.dim}Run 'vibecheck --help' for all commands.${c.reset}\n`);
|
|
876
|
+
|
|
877
|
+
return true;
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
function printHelp(showBanner = true) {
|
|
881
|
+
if (showBanner) printBanner();
|
|
882
|
+
|
|
883
|
+
const registry = getRegistry();
|
|
884
|
+
const { COMMANDS } = registry;
|
|
885
|
+
|
|
886
|
+
// Categories — clean story: Start → Core → Evidence → Automation
|
|
887
|
+
const categoryOrder = ["start", "core", "output", "verify", "enforce", "automation"];
|
|
888
|
+
const categories = {
|
|
889
|
+
start: { name: "GETTING STARTED", color: c.yellow, icon: sym.rocket },
|
|
890
|
+
core: { name: "CORE LOOP", color: c.green, icon: sym.shield },
|
|
891
|
+
output: { name: "EVIDENCE", color: c.blue, icon: sym.file },
|
|
892
|
+
verify: { name: "VERIFICATION", color: c.cyan, icon: sym.check },
|
|
893
|
+
enforce: { name: "ENFORCEMENT", color: c.magenta, icon: sym.lock },
|
|
894
|
+
automation: { name: "AUTOMATION", color: c.brightBlue, icon: sym.gear },
|
|
895
|
+
};
|
|
896
|
+
|
|
897
|
+
// Group commands by category
|
|
898
|
+
const grouped = {};
|
|
899
|
+
for (const [cmd, def] of Object.entries(COMMANDS)) {
|
|
900
|
+
const cat = def.category || "automation";
|
|
901
|
+
if (!grouped[cat]) grouped[cat] = [];
|
|
902
|
+
grouped[cat].push({ cmd, ...def });
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
// Print each category
|
|
906
|
+
for (const catKey of categoryOrder) {
|
|
907
|
+
const commands = grouped[catKey];
|
|
908
|
+
if (!commands || commands.length === 0) continue;
|
|
909
|
+
|
|
910
|
+
const cat = categories[catKey];
|
|
911
|
+
console.log(`\n${cat.color}${cat.icon} ${cat.name}${c.reset}\n`);
|
|
912
|
+
|
|
913
|
+
for (const { cmd, description, tier, subcommands } of commands) {
|
|
914
|
+
// Tier badge
|
|
915
|
+
const tierBadge = tier === "pro" ? `${c.magenta}[PRO]${c.reset} ` : `${c.green}[FREE]${c.reset} `;
|
|
916
|
+
|
|
917
|
+
// Subcommand hint
|
|
918
|
+
let subHint = "";
|
|
919
|
+
if (subcommands && subcommands.length > 0) {
|
|
920
|
+
const subNames = subcommands.map(s => s.name).join(", ");
|
|
921
|
+
subHint = ` ${c.dim}(${subNames})${c.reset}`;
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
console.log(` ${c.cyan}${cmd.padEnd(12)}${c.reset} ${tierBadge}${description}${subHint}`);
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
console.log(`
|
|
929
|
+
${c.dim}${sym.boxHorizontal.repeat(64)}${c.reset}
|
|
930
|
+
|
|
931
|
+
${c.green}${sym.star} PRODUCT STORY${c.reset}
|
|
932
|
+
|
|
933
|
+
${c.bold}Scan${c.reset} ${c.dim}→${c.reset} ${c.bold}Ship${c.reset} ${c.dim}→${c.reset} ${c.bold}Certify${c.reset} ${c.dim}That's it. Three steps to proven code.${c.reset}
|
|
934
|
+
|
|
935
|
+
${c.green}${sym.rocket} QUICK START${c.reset}
|
|
936
|
+
|
|
937
|
+
${c.bold}One command:${c.reset}
|
|
938
|
+
${c.cyan}vibecheck kickoff${c.reset} ${c.dim}60-second first run experience${c.reset}
|
|
939
|
+
|
|
940
|
+
${c.bold}Step by step:${c.reset}
|
|
941
|
+
${c.bold}1.${c.reset} ${c.cyan}vibecheck scan${c.reset} ${c.dim}Find what's broken${c.reset}
|
|
942
|
+
${c.bold}2.${c.reset} ${c.cyan}vibecheck fix --apply${c.reset} ${c.dim}Fix it with AI${c.reset}
|
|
943
|
+
${c.bold}3.${c.reset} ${c.cyan}vibecheck ship${c.reset} ${c.dim}Get verdict: SHIP / WARN / BLOCK${c.reset}
|
|
944
|
+
|
|
945
|
+
${c.green}${sym.lightning} COMMON WORKFLOWS${c.reset}
|
|
946
|
+
|
|
947
|
+
${c.cyan}vibecheck scan secrets${c.reset} ${c.dim}Scan for leaked secrets${c.reset}
|
|
948
|
+
${c.cyan}vibecheck firewall on${c.reset} ${c.dim}Enable agent firewall${c.reset}
|
|
949
|
+
${c.cyan}vibecheck certify --badge${c.reset} ${c.dim}Full proof + badge${c.reset}
|
|
950
|
+
${c.cyan}vibecheck report sarif${c.reset} ${c.dim}SARIF for GitHub CI${c.reset}
|
|
951
|
+
${c.cyan}vibecheck ship preflight${c.reset} ${c.dim}Pre-release checklist${c.reset}
|
|
952
|
+
|
|
953
|
+
${c.green}${sym.star} PRICING${c.reset}
|
|
954
|
+
|
|
955
|
+
${c.green}FREE${c.reset} ${c.dim}$0${c.reset} kickoff, scan, report, ci, config, doctor
|
|
956
|
+
${c.magenta}PRO${c.reset} ${c.dim}$49/mo${c.reset} + ship, fix, certify, reality, firewall, mcp
|
|
957
|
+
|
|
958
|
+
${c.bold}GLOBAL OPTIONS${c.reset}
|
|
959
|
+
|
|
960
|
+
${c.cyan}--help, -h${c.reset} Show help for any command
|
|
961
|
+
${c.cyan}--json${c.reset} Machine-readable JSON output
|
|
962
|
+
${c.cyan}--quiet, -q${c.reset} Suppress non-essential output
|
|
963
|
+
${c.cyan}--verbose${c.reset} Detailed output for debugging
|
|
964
|
+
${c.cyan}--ci${c.reset} CI mode (quiet + no-banner)
|
|
965
|
+
${c.cyan}--offline${c.reset} Run without API connection
|
|
966
|
+
${c.cyan}--path, -p <dir>${c.reset} Run in specified directory
|
|
967
|
+
|
|
968
|
+
${c.dim}${sym.boxHorizontal.repeat(64)}${c.reset}
|
|
969
|
+
${c.dim}Run 'vibecheck <command> --help' for subcommands and options.${c.reset}
|
|
970
|
+
${c.dim}Docs: https://docs.vibecheckai.dev${c.reset}
|
|
971
|
+
`);
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
975
|
+
// HELP VALIDATION
|
|
976
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
977
|
+
|
|
978
|
+
/**
|
|
979
|
+
* Self-test function to verify help output matches actual commands
|
|
980
|
+
* Run with: node bin/vibecheck.js --self-test
|
|
981
|
+
*/
|
|
982
|
+
function validateHelpOutput() {
|
|
983
|
+
const registry = getRegistry();
|
|
984
|
+
const { COMMANDS, ALIAS_MAP } = registry;
|
|
985
|
+
|
|
986
|
+
// Extract commands from help text (simplified - just check registry)
|
|
987
|
+
const actualCommands = Object.keys(COMMANDS);
|
|
988
|
+
const actualAliases = Object.keys(ALIAS_MAP);
|
|
989
|
+
const allActualCommands = [...actualCommands, ...actualAliases];
|
|
990
|
+
|
|
991
|
+
// Check for obvious issues
|
|
992
|
+
const issues = [];
|
|
993
|
+
|
|
994
|
+
// Check that all commands have runners
|
|
995
|
+
for (const [cmd, def] of Object.entries(COMMANDS)) {
|
|
996
|
+
if (!def.runner) {
|
|
997
|
+
issues.push(`Command '${cmd}' missing runner`);
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
// Check that aliases point to valid commands
|
|
1002
|
+
for (const [alias, target] of Object.entries(ALIAS_MAP)) {
|
|
1003
|
+
if (!COMMANDS[target]) {
|
|
1004
|
+
issues.push(`Alias '${alias}' points to non-existent command '${target}'`);
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
if (issues.length > 0) {
|
|
1009
|
+
console.error('Help output validation failed:');
|
|
1010
|
+
issues.forEach(issue => console.error(` - ${issue}`));
|
|
1011
|
+
return false;
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
return true;
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
1018
|
+
// HELPER FUNCTIONS
|
|
1019
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
1020
|
+
function getArgValue(args, flags) {
|
|
1021
|
+
for (const flag of flags) {
|
|
1022
|
+
const idx = args.indexOf(flag);
|
|
1023
|
+
if (idx !== -1 && idx < args.length - 1) return args[idx + 1];
|
|
1024
|
+
}
|
|
1025
|
+
return undefined;
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
function hasFlag(args, flags) {
|
|
1029
|
+
for (const flag of flags) {
|
|
1030
|
+
if (args.includes(flag)) return true;
|
|
1031
|
+
}
|
|
1032
|
+
return false;
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
function formatError(error, config) {
|
|
1036
|
+
const lines = [`${c.red}${sym.error} Error:${c.reset} ${error.message}`];
|
|
1037
|
+
if (config.debug && error.stack) {
|
|
1038
|
+
lines.push("", `${c.dim}Stack trace:${c.reset}`, c.dim + error.stack.split("\n").slice(1).join("\n") + c.reset);
|
|
1039
|
+
}
|
|
1040
|
+
return lines.join("\n");
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
/**
|
|
1044
|
+
* Map error to standardized exit code
|
|
1045
|
+
* @param {Error} error - Error object
|
|
1046
|
+
* @param {boolean} json - Whether JSON output is requested
|
|
1047
|
+
* @returns {number} Exit code
|
|
1048
|
+
*/
|
|
1049
|
+
function mapErrorToExitCode(error, json = false) {
|
|
1050
|
+
const EXIT_CODES = {
|
|
1051
|
+
'AUTH_REQUIRED': 5,
|
|
1052
|
+
'AUTH_FAILED': 6,
|
|
1053
|
+
'TIER_REQUIRED': 7,
|
|
1054
|
+
'RATE_LIMITED': 8,
|
|
1055
|
+
'NOT_FOUND': 4,
|
|
1056
|
+
'VALIDATION_ERROR': 3,
|
|
1057
|
+
'USER_ERROR': 3,
|
|
1058
|
+
'NETWORK_ERROR': 9,
|
|
1059
|
+
};
|
|
1060
|
+
|
|
1061
|
+
// Check error.code first
|
|
1062
|
+
if (error.code && EXIT_CODES[error.code]) {
|
|
1063
|
+
return EXIT_CODES[error.code];
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
// Check error.exitCode
|
|
1067
|
+
if (error.exitCode !== undefined) {
|
|
1068
|
+
return error.exitCode;
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
// Check error message for common patterns
|
|
1072
|
+
const message = error.message?.toLowerCase() || '';
|
|
1073
|
+
if (message.includes('not found') || message.includes('not exist')) {
|
|
1074
|
+
return 4; // NOT_FOUND
|
|
1075
|
+
}
|
|
1076
|
+
if (message.includes('auth') || message.includes('login')) {
|
|
1077
|
+
return 5; // AUTH_REQUIRED
|
|
1078
|
+
}
|
|
1079
|
+
if (message.includes('network') || message.includes('connection')) {
|
|
1080
|
+
return 9; // NETWORK_ERROR
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
// Default to internal error
|
|
1084
|
+
return 10; // INTERNAL_ERROR
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
1088
|
+
// FLAG PARSING
|
|
1089
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
1090
|
+
function parseGlobalFlags(rawArgs) {
|
|
1091
|
+
const flags = {
|
|
1092
|
+
help: false,
|
|
1093
|
+
version: false,
|
|
1094
|
+
json: false,
|
|
1095
|
+
ci: false,
|
|
1096
|
+
quiet: false,
|
|
1097
|
+
verbose: false,
|
|
1098
|
+
debug: false,
|
|
1099
|
+
strict: false,
|
|
1100
|
+
noBanner: false,
|
|
1101
|
+
offline: false,
|
|
1102
|
+
path: process.cwd(),
|
|
1103
|
+
output: null,
|
|
1104
|
+
};
|
|
1105
|
+
|
|
1106
|
+
const cleanArgs = [];
|
|
1107
|
+
let i = 0;
|
|
1108
|
+
|
|
1109
|
+
while (i < rawArgs.length) {
|
|
1110
|
+
const arg = rawArgs[i];
|
|
1111
|
+
|
|
1112
|
+
switch (arg) {
|
|
1113
|
+
case "--help":
|
|
1114
|
+
case "-h":
|
|
1115
|
+
flags.help = true;
|
|
1116
|
+
break;
|
|
1117
|
+
case "--version":
|
|
1118
|
+
case "-v":
|
|
1119
|
+
flags.version = true;
|
|
1120
|
+
break;
|
|
1121
|
+
case "--json":
|
|
1122
|
+
flags.json = true;
|
|
1123
|
+
break;
|
|
1124
|
+
case "--ci":
|
|
1125
|
+
flags.ci = true;
|
|
1126
|
+
break;
|
|
1127
|
+
case "--quiet":
|
|
1128
|
+
case "-q":
|
|
1129
|
+
flags.quiet = true;
|
|
1130
|
+
break;
|
|
1131
|
+
case "--verbose":
|
|
1132
|
+
flags.verbose = true;
|
|
1133
|
+
break;
|
|
1134
|
+
case "--debug":
|
|
1135
|
+
flags.debug = true;
|
|
1136
|
+
break;
|
|
1137
|
+
case "--strict":
|
|
1138
|
+
flags.strict = true;
|
|
1139
|
+
break;
|
|
1140
|
+
case "--no-banner":
|
|
1141
|
+
flags.noBanner = true;
|
|
1142
|
+
break;
|
|
1143
|
+
case "--offline":
|
|
1144
|
+
case "--local":
|
|
1145
|
+
flags.offline = true;
|
|
1146
|
+
break;
|
|
1147
|
+
case "--path":
|
|
1148
|
+
case "-p":
|
|
1149
|
+
flags.path = rawArgs[++i] || process.cwd();
|
|
1150
|
+
break;
|
|
1151
|
+
case "--output":
|
|
1152
|
+
case "-o":
|
|
1153
|
+
flags.output = rawArgs[++i] || null;
|
|
1154
|
+
break;
|
|
1155
|
+
default:
|
|
1156
|
+
if (arg.startsWith("--path=")) {
|
|
1157
|
+
flags.path = arg.split("=")[1];
|
|
1158
|
+
} else if (arg.startsWith("--output=")) {
|
|
1159
|
+
flags.output = arg.split("=")[1];
|
|
1160
|
+
} else {
|
|
1161
|
+
cleanArgs.push(arg);
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
i++;
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
return { flags, cleanArgs };
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
1171
|
+
// MAIN ENTRY POINT
|
|
1172
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
1173
|
+
async function main() {
|
|
1174
|
+
const startTime = performance.now();
|
|
1175
|
+
const rawArgs = process.argv.slice(2);
|
|
1176
|
+
|
|
1177
|
+
// Parse global flags
|
|
1178
|
+
const { flags: globalFlags, cleanArgs } = parseGlobalFlags(rawArgs);
|
|
1179
|
+
|
|
1180
|
+
// Handle version (fast path - no config loading)
|
|
1181
|
+
if (globalFlags.version) {
|
|
1182
|
+
console.log(`vibecheck v${getVersion()}`);
|
|
1183
|
+
process.exit(0);
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
// Handle self-test flag (for CI validation)
|
|
1187
|
+
if (cleanArgs.includes('--self-test')) {
|
|
1188
|
+
const registry = getRegistry();
|
|
1189
|
+
if (!validateHelpOutput()) {
|
|
1190
|
+
process.exit(1);
|
|
1191
|
+
}
|
|
1192
|
+
console.log('Self-test passed: Help output matches command registry');
|
|
1193
|
+
process.exit(0);
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1196
|
+
// Load config and state
|
|
1197
|
+
const config = loadConfig();
|
|
1198
|
+
const state = loadState();
|
|
1199
|
+
|
|
1200
|
+
// Apply flag overrides to config
|
|
1201
|
+
if (globalFlags.debug) config.debug = true;
|
|
1202
|
+
if (globalFlags.verbose) config.verbose = true;
|
|
1203
|
+
if (globalFlags.quiet) config.quiet = true;
|
|
1204
|
+
if (globalFlags.noBanner) config.noBanner = true;
|
|
1205
|
+
if (globalFlags.ci) {
|
|
1206
|
+
config.quiet = true;
|
|
1207
|
+
config.noBanner = true;
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1210
|
+
// Handle no command
|
|
1211
|
+
if (!cleanArgs[0]) {
|
|
1212
|
+
printHelp(!config.noBanner);
|
|
1213
|
+
process.exit(0);
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
// Handle global help
|
|
1217
|
+
if (globalFlags.help && !cleanArgs[0]) {
|
|
1218
|
+
printHelp(!config.noBanner);
|
|
1219
|
+
process.exit(0);
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1222
|
+
// Handle completion command (special - doesn't need registry loaded normally)
|
|
1223
|
+
if (cleanArgs[0] === "completion") {
|
|
1224
|
+
process.exit(runCompletion(cleanArgs.slice(1)));
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
// Load registry for command resolution
|
|
1228
|
+
const registry = getRegistry();
|
|
1229
|
+
const { COMMANDS, ALIAS_MAP, ALL_COMMANDS, HIDDEN_COMMANDS, SUBCOMMAND_MAP, DEPRECATION_MAP } = registry;
|
|
1230
|
+
|
|
1231
|
+
let cmd = cleanArgs[0];
|
|
1232
|
+
const originalCmd = cmd; // Save original for deprecation warning
|
|
1233
|
+
let subcommandRunner = null; // Will be set if a subcommand route matches
|
|
1234
|
+
|
|
1235
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
1236
|
+
// COMMAND RESOLUTION (3-pass)
|
|
1237
|
+
// 1. Exact match in 12 top-level COMMANDS
|
|
1238
|
+
// 2. Alias resolution (old names → new 12)
|
|
1239
|
+
// 3. Hidden command fallback (power-user / legacy)
|
|
1240
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
1241
|
+
let resolvedViaAlias = false;
|
|
1242
|
+
let resolvedViaHidden = false;
|
|
1243
|
+
|
|
1244
|
+
if (!COMMANDS[cmd]) {
|
|
1245
|
+
if (ALIAS_MAP[cmd]) {
|
|
1246
|
+
resolvedViaAlias = true;
|
|
1247
|
+
cmd = ALIAS_MAP[cmd];
|
|
1248
|
+
} else if (HIDDEN_COMMANDS && HIDDEN_COMMANDS[cmd]) {
|
|
1249
|
+
resolvedViaHidden = true;
|
|
1250
|
+
// Hidden commands run directly — skip subcommand logic
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
|
|
1254
|
+
let cmdArgs = cleanArgs.slice(1);
|
|
1255
|
+
|
|
1256
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
1257
|
+
// SUBCOMMAND ROUTING
|
|
1258
|
+
// If the first arg matches a known subcommand, route to its dedicated runner.
|
|
1259
|
+
// e.g. "vibecheck scan secrets" → SUBCOMMAND_MAP["scan:secrets"]
|
|
1260
|
+
// "vibecheck ship preflight" → SUBCOMMAND_MAP["ship:preflight"]
|
|
1261
|
+
// "vibecheck firewall on" → SUBCOMMAND_MAP["firewall:on"]
|
|
1262
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
1263
|
+
if (COMMANDS[cmd] && cmdArgs.length > 0 && !resolvedViaHidden) {
|
|
1264
|
+
const subKey = `${cmd}:${cmdArgs[0]}`;
|
|
1265
|
+
if (SUBCOMMAND_MAP[subKey]) {
|
|
1266
|
+
try {
|
|
1267
|
+
const subDef = SUBCOMMAND_MAP[subKey];
|
|
1268
|
+
subcommandRunner = subDef.runner();
|
|
1269
|
+
const prependArgs = subDef.prependArgs || [];
|
|
1270
|
+
// Drop the subcommand name from args, prepend any mapped args
|
|
1271
|
+
cmdArgs = [...prependArgs, ...cmdArgs.slice(1)];
|
|
1272
|
+
} catch (e) {
|
|
1273
|
+
// Fall through to default runner if subcommand runner fails to load
|
|
1274
|
+
subcommandRunner = null;
|
|
1275
|
+
}
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
|
|
1279
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
1280
|
+
// SPECIAL ALIAS ROUTING
|
|
1281
|
+
// When old top-level commands that are now subcommands are used via alias,
|
|
1282
|
+
// route them to the correct subcommand runner.
|
|
1283
|
+
// e.g. "vibecheck launch" → alias to "ship" → route to ship:preflight runner
|
|
1284
|
+
// "vibecheck login" → alias to "config" → route to config:auth runner
|
|
1285
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
1286
|
+
if (resolvedViaAlias && !subcommandRunner) {
|
|
1287
|
+
const aliasRoutes = {
|
|
1288
|
+
// Auth aliases → config:auth
|
|
1289
|
+
"login": "config:auth", "logout": "config:auth", "whoami": "config:auth",
|
|
1290
|
+
"me": "config:auth", "signin": "config:auth", "signout": "config:auth",
|
|
1291
|
+
// launch → ship:preflight
|
|
1292
|
+
"launch": "ship:preflight", "gate": "ship:preflight",
|
|
1293
|
+
"ci-gate": "ship:preflight", "preflight": "ship:preflight", "prelaunch": "ship:preflight",
|
|
1294
|
+
// shield/guard → firewall (default runner is fine)
|
|
1295
|
+
// prove/verify/seal/badge → certify subcommands
|
|
1296
|
+
"prove": "certify:prove", "verify": "certify:verify",
|
|
1297
|
+
"seal": "certify:seal", "badge": "certify:seal", "attest": "certify:seal",
|
|
1298
|
+
"truth": "certify:truth", "truthpack": "certify:truth",
|
|
1299
|
+
// safelist/allowlist → config:safelist
|
|
1300
|
+
"safelist": "config:safelist", "allowlist": "config:safelist",
|
|
1301
|
+
"al": "config:safelist", "suppress": "config:safelist",
|
|
1302
|
+
// context → config:context
|
|
1303
|
+
"context": "config:context", "ctx": "config:context",
|
|
1304
|
+
// forge → firewall:rules
|
|
1305
|
+
"rules": "firewall:rules", "ai-rules": "firewall:rules",
|
|
1306
|
+
"mdc": "firewall:rules", "brain": "firewall:rules",
|
|
1307
|
+
// intent/approve → firewall subcommands
|
|
1308
|
+
"intent": "firewall:intent", "approve": "firewall:approve",
|
|
1309
|
+
// checkpoint shortcuts → fix:checkpoint
|
|
1310
|
+
"checkpoint": "fix:checkpoint", "cp": "fix:checkpoint",
|
|
1311
|
+
"snap": "fix:checkpoint", "snapshot": "fix:checkpoint",
|
|
1312
|
+
"timemachine": "fix:checkpoint", "rollback": "fix:checkpoint",
|
|
1313
|
+
// polish → fix:polish
|
|
1314
|
+
"prod": "fix:polish", "final": "fix:polish",
|
|
1315
|
+
// missions → fix:missions
|
|
1316
|
+
"missions": "fix:missions",
|
|
1317
|
+
// packs → report (default runner)
|
|
1318
|
+
// evidence-pack → report:evidence
|
|
1319
|
+
"evidence-pack": "report:evidence",
|
|
1320
|
+
"permissions-pack": "report:permissions",
|
|
1321
|
+
"proof-graph": "report:graph",
|
|
1322
|
+
};
|
|
1323
|
+
|
|
1324
|
+
const subKey = aliasRoutes[originalCmd];
|
|
1325
|
+
if (subKey && SUBCOMMAND_MAP[subKey]) {
|
|
1326
|
+
try {
|
|
1327
|
+
const subDef = SUBCOMMAND_MAP[subKey];
|
|
1328
|
+
subcommandRunner = subDef.runner();
|
|
1329
|
+
const prependArgs = subDef.prependArgs || [];
|
|
1330
|
+
// For auth aliases, prepend the original command as subcommand
|
|
1331
|
+
const authAliases = ['login', 'logout', 'whoami', 'me', 'signin', 'signout'];
|
|
1332
|
+
if (authAliases.includes(originalCmd)) {
|
|
1333
|
+
const mapped = originalCmd === 'signin' ? 'login' : originalCmd === 'signout' ? 'logout' : originalCmd;
|
|
1334
|
+
cmdArgs = [mapped, ...cmdArgs];
|
|
1335
|
+
}
|
|
1336
|
+
cmdArgs = [...prependArgs, ...cmdArgs];
|
|
1337
|
+
} catch (e) {
|
|
1338
|
+
subcommandRunner = null;
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
|
|
1343
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
1344
|
+
// DEPRECATION WARNING
|
|
1345
|
+
// Show one-line hint when user uses an old command name
|
|
1346
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
1347
|
+
if (resolvedViaAlias && DEPRECATION_MAP && DEPRECATION_MAP[originalCmd]) {
|
|
1348
|
+
const dep = DEPRECATION_MAP[originalCmd];
|
|
1349
|
+
if (!globalFlags.json && !globalFlags.quiet) {
|
|
1350
|
+
console.log(`${c.yellow}${sym.warning}${c.reset} ${c.dim}${dep.message} — use '${c.cyan}vibecheck ${dep.target}${c.reset}${c.dim}' instead${c.reset}`);
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
|
|
1354
|
+
// Handle command-specific help (vibecheck <cmd> --help)
|
|
1355
|
+
if (globalFlags.help && cmd && (COMMANDS[cmd] || (HIDDEN_COMMANDS && HIDDEN_COMMANDS[cmd]))) {
|
|
1356
|
+
// Try our rich help first, then fall back to runner's --help
|
|
1357
|
+
if (COMMANDS[cmd] && printCommandHelp(cmd)) {
|
|
1358
|
+
process.exit(0);
|
|
1359
|
+
}
|
|
1360
|
+
}
|
|
1361
|
+
|
|
1362
|
+
// Pass --help to runner if specified with command
|
|
1363
|
+
if (globalFlags.help) cmdArgs = ["--help", ...cmdArgs];
|
|
1364
|
+
|
|
1365
|
+
// Pass standard flags to runners
|
|
1366
|
+
if (globalFlags.json) cmdArgs.push("--json");
|
|
1367
|
+
if (globalFlags.ci) cmdArgs.push("--ci");
|
|
1368
|
+
if (globalFlags.noBanner) cmdArgs.push("--no-banner");
|
|
1369
|
+
if (globalFlags.quiet) cmdArgs.push("--quiet");
|
|
1370
|
+
if (globalFlags.path && globalFlags.path !== process.cwd()) {
|
|
1371
|
+
cmdArgs.push("--path", globalFlags.path);
|
|
1372
|
+
}
|
|
1373
|
+
if (globalFlags.output) {
|
|
1374
|
+
cmdArgs.push("--output", globalFlags.output);
|
|
1375
|
+
}
|
|
1376
|
+
if (globalFlags.verbose) cmdArgs.push("--verbose");
|
|
1377
|
+
if (globalFlags.strict) cmdArgs.push("--strict");
|
|
1378
|
+
if (globalFlags.offline) cmdArgs.push("--offline");
|
|
1379
|
+
|
|
1380
|
+
// Unknown command
|
|
1381
|
+
if (!COMMANDS[cmd] && !resolvedViaHidden) {
|
|
1382
|
+
const suggestions = findSimilarCommands(cmd, [...Object.keys(COMMANDS)]);
|
|
1383
|
+
console.log(`\n${c.red}${sym.error}${c.reset} Unknown command: ${c.yellow}${originalCmd}${c.reset}`);
|
|
1384
|
+
|
|
1385
|
+
if (suggestions.length > 0) {
|
|
1386
|
+
console.log(`\n${c.dim}Did you mean:${c.reset}`);
|
|
1387
|
+
suggestions.forEach((s) => {
|
|
1388
|
+
const def = COMMANDS[s];
|
|
1389
|
+
console.log(` ${c.cyan}vibecheck ${s}${c.reset} ${c.dim}${def?.description || ""}${c.reset}`);
|
|
1390
|
+
});
|
|
1391
|
+
}
|
|
1392
|
+
console.log(`\n${c.dim}Run 'vibecheck --help' for available commands.${c.reset}\n`);
|
|
1393
|
+
process.exit(4);
|
|
1394
|
+
}
|
|
1395
|
+
|
|
1396
|
+
// Generate runId
|
|
1397
|
+
const cliOutput = getCliOutput();
|
|
1398
|
+
const runId = cliOutput.generateRunId();
|
|
1399
|
+
const runStart = new Date().toISOString();
|
|
1400
|
+
|
|
1401
|
+
const cmdDef = COMMANDS[cmd] || (HIDDEN_COMMANDS && HIDDEN_COMMANDS[cmd]) || {};
|
|
1402
|
+
let authInfo = { key: null };
|
|
1403
|
+
|
|
1404
|
+
// Check for offline/dev mode (via flag or env var)
|
|
1405
|
+
// SECURITY: VIBECHECK_DEV_PRO only works in non-production to prevent auth bypass
|
|
1406
|
+
const isDevProAllowed = process.env.NODE_ENV !== 'production' &&
|
|
1407
|
+
process.env.CI !== 'true' &&
|
|
1408
|
+
process.env.CI !== '1' &&
|
|
1409
|
+
process.env.VIBECHECK_DEV_PRO === '1';
|
|
1410
|
+
const isOffline = globalFlags.offline ||
|
|
1411
|
+
process.env.VIBECHECK_OFFLINE === '1' ||
|
|
1412
|
+
process.env.VIBECHECK_LOCAL === '1' ||
|
|
1413
|
+
isDevProAllowed;
|
|
1414
|
+
|
|
1415
|
+
// Auth check (unless skipAuth or offline mode)
|
|
1416
|
+
// Auth-exempt: config (has auth subcommand), doctor, kickoff, plus direct auth aliases
|
|
1417
|
+
const authExemptCommands = ['auth', 'config', 'login', 'logout', 'whoami', 'me', 'signin', 'signout', 'help', '--help', '-h', 'version', '--version', 'doctor', 'kickoff'];
|
|
1418
|
+
const needsAuth = !authExemptCommands.includes(cmd) && !cmdDef.skipAuth && !isOffline;
|
|
1419
|
+
|
|
1420
|
+
if (needsAuth) {
|
|
1421
|
+
const auth = getAuthModule();
|
|
1422
|
+
const { key } = auth.getApiKey();
|
|
1423
|
+
authInfo.key = key;
|
|
1424
|
+
|
|
1425
|
+
// If no API key, prompt for login
|
|
1426
|
+
if (!key) {
|
|
1427
|
+
console.log(`\n ${c.red}❌ Authentication Required${c.reset}`);
|
|
1428
|
+
console.log(` Please log in to use vibecheck.\n`);
|
|
1429
|
+
console.log(` ${c.cyan}Options:${c.reset}`);
|
|
1430
|
+
console.log(` 1. Press ${c.yellow}Enter${c.reset} to open browser and sign up`);
|
|
1431
|
+
console.log(` 2. Visit ${c.underline}https://vibecheckai.dev${c.reset} directly`);
|
|
1432
|
+
console.log(` 3. Run ${c.cyan}vibecheck login${c.reset} after getting your API key\n`);
|
|
1433
|
+
|
|
1434
|
+
// Wait for Enter key
|
|
1435
|
+
const readline = getReadline();
|
|
1436
|
+
readline.createInterface({
|
|
1437
|
+
input: process.stdin,
|
|
1438
|
+
output: process.stdout
|
|
1439
|
+
}).question('', async () => {
|
|
1440
|
+
try {
|
|
1441
|
+
const https = getHttps();
|
|
1442
|
+
const url = 'https://vibecheckai.dev';
|
|
1443
|
+
console.log(`\n Opening ${c.underline}${url}${c.reset} in your browser...\n`);
|
|
1444
|
+
|
|
1445
|
+
// Try to open browser
|
|
1446
|
+
const start = process.platform === 'darwin' ? 'open' :
|
|
1447
|
+
process.platform === 'win32' ? 'start' : 'xdg-open';
|
|
1448
|
+
require('child_process').exec(`${start} ${url}`);
|
|
1449
|
+
} catch (e) {
|
|
1450
|
+
console.log(` Could not open browser. Please visit: ${c.underline}https://vibecheckai.dev${c.reset}`);
|
|
1451
|
+
}
|
|
1452
|
+
process.exit(0);
|
|
1453
|
+
});
|
|
1454
|
+
|
|
1455
|
+
// Keep process alive
|
|
1456
|
+
return new Promise(() => {});
|
|
1457
|
+
}
|
|
1458
|
+
|
|
1459
|
+
const access = await checkCommandAccess(cmd, cmdArgs, authInfo);
|
|
1460
|
+
|
|
1461
|
+
if (!access.allowed) {
|
|
1462
|
+
console.log(access.reason);
|
|
1463
|
+
|
|
1464
|
+
// Show upgrade prompt if tier insufficient
|
|
1465
|
+
if (access.upgradeUrl) {
|
|
1466
|
+
console.log(`\n ${c.cyan}Upgrade:${c.reset} ${access.upgradeUrl}`);
|
|
1467
|
+
}
|
|
1468
|
+
|
|
1469
|
+
return 1;
|
|
1470
|
+
}
|
|
1471
|
+
|
|
1472
|
+
authInfo.access = access;
|
|
1473
|
+
} else if (isOffline) {
|
|
1474
|
+
// Offline mode - provide basic access info
|
|
1475
|
+
// Suppress message if JSON/SARIF output is requested (check both global flags and command args)
|
|
1476
|
+
const hasJsonOutput = globalFlags.json || cmdArgs.includes('--json') || cmdArgs.includes('--sarif');
|
|
1477
|
+
if (!config.quiet && !config.noBanner && !hasJsonOutput) {
|
|
1478
|
+
console.log(`${c.cyan}${sym.arrowRight} OFFLINE${c.reset} ${c.dim}mode - local scanning without API${c.reset}`);
|
|
1479
|
+
}
|
|
1480
|
+
authInfo.access = {
|
|
1481
|
+
allowed: true,
|
|
1482
|
+
tier: 'free',
|
|
1483
|
+
caps: { maxFiles: 100, maxDepth: 3 },
|
|
1484
|
+
offline: true
|
|
1485
|
+
};
|
|
1486
|
+
}
|
|
1487
|
+
|
|
1488
|
+
// Update state
|
|
1489
|
+
state.runCount++;
|
|
1490
|
+
state.lastRun = Date.now();
|
|
1491
|
+
state.commandHistory = [...(state.commandHistory || []).slice(-99), { cmd, timestamp: Date.now() }];
|
|
1492
|
+
saveState(state);
|
|
1493
|
+
|
|
1494
|
+
// Check for updates (async, non-blocking for startup)
|
|
1495
|
+
let updatePromise = null;
|
|
1496
|
+
if (!config.quiet && !config.noBanner && !isCI()) {
|
|
1497
|
+
updatePromise = checkForUpdates(state, config);
|
|
1498
|
+
}
|
|
1499
|
+
|
|
1500
|
+
let exitCode = 0;
|
|
1501
|
+
|
|
1502
|
+
try {
|
|
1503
|
+
// Use subcommand runner if resolved, otherwise default command runner
|
|
1504
|
+
const runner = subcommandRunner || getRunner(cmd);
|
|
1505
|
+
if (!runner) {
|
|
1506
|
+
console.error(`${c.red}${sym.error}${c.reset} Failed to load runner for: ${cmd}`);
|
|
1507
|
+
process.exit(4); // NOT_FOUND
|
|
1508
|
+
}
|
|
1509
|
+
|
|
1510
|
+
const context = {
|
|
1511
|
+
repoRoot: globalFlags.path,
|
|
1512
|
+
config,
|
|
1513
|
+
state,
|
|
1514
|
+
authInfo,
|
|
1515
|
+
version: getVersion(),
|
|
1516
|
+
isCI: isCI(),
|
|
1517
|
+
runId,
|
|
1518
|
+
runStart,
|
|
1519
|
+
};
|
|
1520
|
+
|
|
1521
|
+
// Execute command - all commands use consistent runner signature
|
|
1522
|
+
const result = await runner(cmdArgs, context);
|
|
1523
|
+
|
|
1524
|
+
// Ensure result has standardized exit code
|
|
1525
|
+
if (typeof result === 'object' && result.exitCode !== undefined) {
|
|
1526
|
+
exitCode = result.exitCode;
|
|
1527
|
+
} else if (typeof result === 'number') {
|
|
1528
|
+
exitCode = result;
|
|
1529
|
+
} else {
|
|
1530
|
+
exitCode = 0; // SUCCESS
|
|
1531
|
+
}
|
|
1532
|
+
} catch (error) {
|
|
1533
|
+
// Map errors to exit codes
|
|
1534
|
+
exitCode = mapErrorToExitCode(error, globalFlags.json);
|
|
1535
|
+
|
|
1536
|
+
if (globalFlags.json) {
|
|
1537
|
+
console.log(JSON.stringify({
|
|
1538
|
+
success: false,
|
|
1539
|
+
error: {
|
|
1540
|
+
code: error.code || 'INTERNAL_ERROR',
|
|
1541
|
+
message: error.message,
|
|
1542
|
+
},
|
|
1543
|
+
exitCode,
|
|
1544
|
+
}, null, 2));
|
|
1545
|
+
} else {
|
|
1546
|
+
console.error(formatError(error, config));
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1549
|
+
|
|
1550
|
+
// Create receipt for paid commands
|
|
1551
|
+
if (cmdDef.tier !== "free" && runId) {
|
|
1552
|
+
try {
|
|
1553
|
+
const { createManifestAndReceipt } = require("./runners/lib/receipts");
|
|
1554
|
+
await createManifestAndReceipt({
|
|
1555
|
+
runId,
|
|
1556
|
+
command: cmd,
|
|
1557
|
+
args: cmdArgs,
|
|
1558
|
+
tier: authInfo.access?.tier || "free",
|
|
1559
|
+
exitCode,
|
|
1560
|
+
startTime: runStart,
|
|
1561
|
+
endTime: new Date().toISOString(),
|
|
1562
|
+
projectPath: globalFlags.path,
|
|
1563
|
+
});
|
|
1564
|
+
} catch (e) {
|
|
1565
|
+
if (config.debug) console.error(`Failed to create receipt: ${e.message}`);
|
|
1566
|
+
}
|
|
1567
|
+
}
|
|
1568
|
+
|
|
1569
|
+
// Show update notice after command (if available)
|
|
1570
|
+
if (updatePromise) {
|
|
1571
|
+
const latestVersion = await updatePromise;
|
|
1572
|
+
if (latestVersion) {
|
|
1573
|
+
printUpdateNotice(latestVersion);
|
|
1574
|
+
}
|
|
1575
|
+
}
|
|
1576
|
+
|
|
1577
|
+
// Debug timing
|
|
1578
|
+
if (config.debug) {
|
|
1579
|
+
console.log(`\n${c.dim}${sym.clock} Total: ${(performance.now() - startTime).toFixed(0)}ms${c.reset}`);
|
|
1580
|
+
}
|
|
1581
|
+
|
|
1582
|
+
process.exit(exitCode);
|
|
1583
|
+
}
|
|
1584
|
+
|
|
1585
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
1586
|
+
// GRACEFUL SHUTDOWN
|
|
1587
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
1588
|
+
process.on("SIGINT", () => {
|
|
1589
|
+
console.log(`\n${c.yellow}${sym.warning}${c.reset} Interrupted`);
|
|
1590
|
+
process.exit(130);
|
|
1591
|
+
});
|
|
1592
|
+
|
|
1593
|
+
process.on("SIGTERM", () => {
|
|
1594
|
+
console.log(`\n${c.yellow}${sym.warning}${c.reset} Terminated`);
|
|
1595
|
+
process.exit(143);
|
|
1596
|
+
});
|
|
1597
|
+
|
|
1598
|
+
process.on("uncaughtException", (error) => {
|
|
1599
|
+
console.error(`\n${c.red}${sym.error} Uncaught Exception:${c.reset} ${error.message}`);
|
|
1600
|
+
if (process.env.VIBECHECK_DEBUG === "true") {
|
|
1601
|
+
console.error(c.dim + error.stack + c.reset);
|
|
1602
|
+
}
|
|
1603
|
+
process.exit(1);
|
|
1604
|
+
});
|
|
1605
|
+
|
|
1606
|
+
process.on("unhandledRejection", (reason) => {
|
|
1607
|
+
console.error(`\n${c.red}${sym.error} Unhandled Rejection:${c.reset} ${reason}`);
|
|
1608
|
+
process.exit(1);
|
|
1609
|
+
});
|
|
1610
|
+
|
|
1611
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
1612
|
+
// RUN
|
|
1613
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
1614
|
+
main().catch((error) => {
|
|
1615
|
+
console.error(`\n${c.red}${sym.error} Fatal:${c.reset} ${error.message}`);
|
|
1616
|
+
process.exit(1);
|
|
1617
|
+
});
|