vibecheck-ai 2.0.1 → 5.0.0
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 +451 -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
|
@@ -0,0 +1,928 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vibecheck classify - Inventory Authority Command
|
|
3
|
+
*
|
|
4
|
+
* Produces duplication maps, legacy code maps, and risk classifications.
|
|
5
|
+
* Available on FREE tier - read-only analysis with no enforcement.
|
|
6
|
+
*
|
|
7
|
+
* Output:
|
|
8
|
+
* - Duplication map (exact, near, semantic duplicates)
|
|
9
|
+
* - Legacy map (deprecated, backup, obsolete, dead code)
|
|
10
|
+
* - Risk classifications (LOW, MEDIUM, HIGH, CRITICAL)
|
|
11
|
+
*
|
|
12
|
+
* Part of the Authority System - "The AI That Says No"
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const path = require("path");
|
|
16
|
+
const fs = require("fs");
|
|
17
|
+
const crypto = require("crypto");
|
|
18
|
+
const { withErrorHandling, createUserError } = require("./lib/error-handler");
|
|
19
|
+
const { parseGlobalFlags, shouldShowBanner } = require("./lib/global-flags");
|
|
20
|
+
const { EXIT } = require("./lib/exit-codes");
|
|
21
|
+
|
|
22
|
+
// Engine V2 support (when VIBECHECK_ENGINE_V2=1)
|
|
23
|
+
let createIndex = null;
|
|
24
|
+
try {
|
|
25
|
+
createIndex = require("./lib/engine").createIndex;
|
|
26
|
+
} catch {
|
|
27
|
+
// Engine V2 not available, use legacy file walking
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
31
|
+
// TERMINAL UI
|
|
32
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
33
|
+
|
|
34
|
+
const {
|
|
35
|
+
ansi,
|
|
36
|
+
colors,
|
|
37
|
+
Spinner,
|
|
38
|
+
} = require("./lib/terminal-ui");
|
|
39
|
+
|
|
40
|
+
const BANNER = `
|
|
41
|
+
${ansi.rgb(0, 200, 255)} ██╗ ██╗██╗██████╗ ███████╗ ██████╗██╗ ██╗███████╗ ██████╗██╗ ██╗${ansi.reset}
|
|
42
|
+
${ansi.rgb(30, 180, 255)} ██║ ██║██║██╔══██╗██╔════╝██╔════╝██║ ██║██╔════╝██╔════╝██║ ██╔╝${ansi.reset}
|
|
43
|
+
${ansi.rgb(60, 160, 255)} ██║ ██║██║██████╔╝█████╗ ██║ ███████║█████╗ ██║ █████╔╝ ${ansi.reset}
|
|
44
|
+
${ansi.rgb(90, 140, 255)} ╚██╗ ██╔╝██║██╔══██╗██╔══╝ ██║ ██╔══██║██╔══╝ ██║ ██╔═██╗ ${ansi.reset}
|
|
45
|
+
${ansi.rgb(120, 120, 255)} ╚████╔╝ ██║██████╔╝███████╗╚██████╗██║ ██║███████╗╚██████╗██║ ██╗${ansi.reset}
|
|
46
|
+
${ansi.rgb(150, 100, 255)} ╚═══╝ ╚═╝╚═════╝ ╚══════╝ ╚═════╝╚═╝ ╚═╝╚══════╝ ╚═════╝╚═╝ ╚═╝${ansi.reset}
|
|
47
|
+
|
|
48
|
+
${ansi.dim} ┌─────────────────────────────────────────────────────────────────────┐${ansi.reset}
|
|
49
|
+
${ansi.dim} │${ansi.reset} ${ansi.rgb(255, 255, 255)}${ansi.bold}Authority System${ansi.reset} ${ansi.dim}•${ansi.reset} ${ansi.rgb(200, 200, 200)}Inventory${ansi.reset} ${ansi.dim}•${ansi.reset} ${ansi.rgb(150, 150, 150)}Read-Only Classification${ansi.reset} ${ansi.dim}│${ansi.reset}
|
|
50
|
+
${ansi.dim} └─────────────────────────────────────────────────────────────────────┘${ansi.reset}
|
|
51
|
+
`;
|
|
52
|
+
|
|
53
|
+
function printBanner() {
|
|
54
|
+
console.log(BANNER);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
58
|
+
// ARGS PARSER
|
|
59
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
60
|
+
|
|
61
|
+
function parseArgs(args) {
|
|
62
|
+
const { flags: globalFlags, cleanArgs } = parseGlobalFlags(args);
|
|
63
|
+
|
|
64
|
+
const opts = {
|
|
65
|
+
path: globalFlags.path || process.cwd(),
|
|
66
|
+
json: globalFlags.json || false,
|
|
67
|
+
verbose: globalFlags.verbose || false,
|
|
68
|
+
help: globalFlags.help || false,
|
|
69
|
+
noBanner: globalFlags.noBanner || false,
|
|
70
|
+
ci: globalFlags.ci || false,
|
|
71
|
+
quiet: globalFlags.quiet || false,
|
|
72
|
+
// Classification options
|
|
73
|
+
includeNear: true, // Include near-duplicates
|
|
74
|
+
includeSemantic: false, // Include semantic duplicates (slower)
|
|
75
|
+
threshold: 0.8, // Similarity threshold (0-1)
|
|
76
|
+
maxFiles: 5000, // Max files to analyze
|
|
77
|
+
// Output options
|
|
78
|
+
output: null, // Output file path
|
|
79
|
+
format: 'table', // table, json, markdown
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
for (let i = 0; i < cleanArgs.length; i++) {
|
|
83
|
+
const arg = cleanArgs[i];
|
|
84
|
+
|
|
85
|
+
if (arg === '--include-semantic' || arg === '-s') opts.includeSemantic = true;
|
|
86
|
+
else if (arg === '--no-near') opts.includeNear = false;
|
|
87
|
+
else if (arg === '--threshold' || arg === '-t') opts.threshold = parseFloat(cleanArgs[++i]) || 0.8;
|
|
88
|
+
else if (arg === '--max-files') opts.maxFiles = parseInt(cleanArgs[++i], 10) || 5000;
|
|
89
|
+
else if (arg === '--output' || arg === '-o') opts.output = cleanArgs[++i];
|
|
90
|
+
else if (arg === '--format' || arg === '-f') opts.format = cleanArgs[++i] || 'table';
|
|
91
|
+
else if (arg === '--path' || arg === '-p') opts.path = cleanArgs[++i] || process.cwd();
|
|
92
|
+
else if (arg.startsWith('--path=')) opts.path = arg.split('=')[1];
|
|
93
|
+
else if (!arg.startsWith('-')) opts.path = path.resolve(arg);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return opts;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function printHelp(showBanner = true) {
|
|
100
|
+
if (showBanner && shouldShowBanner({})) {
|
|
101
|
+
printBanner();
|
|
102
|
+
}
|
|
103
|
+
console.log(`
|
|
104
|
+
${ansi.bold}USAGE${ansi.reset}
|
|
105
|
+
${colors.accent}vibecheck classify${ansi.reset} [path] [options]
|
|
106
|
+
|
|
107
|
+
${ansi.dim}Authority: inventory (FREE tier)${ansi.reset}
|
|
108
|
+
|
|
109
|
+
Produces a read-only inventory of your codebase:
|
|
110
|
+
- Duplication map (exact, near, semantic duplicates)
|
|
111
|
+
- Legacy code map (deprecated, backup, obsolete, dead)
|
|
112
|
+
- Risk classifications (LOW, MEDIUM, HIGH, CRITICAL)
|
|
113
|
+
|
|
114
|
+
${ansi.bold}OPTIONS${ansi.reset}
|
|
115
|
+
${colors.accent}--include-semantic, -s${ansi.reset} Include semantic duplicates ${ansi.dim}(slower)${ansi.reset}
|
|
116
|
+
${colors.accent}--no-near${ansi.reset} Skip near-duplicate detection
|
|
117
|
+
${colors.accent}--threshold, -t <n>${ansi.reset} Similarity threshold 0-1 ${ansi.dim}(default: 0.8)${ansi.reset}
|
|
118
|
+
${colors.accent}--max-files <n>${ansi.reset} Max files to analyze ${ansi.dim}(default: 5000)${ansi.reset}
|
|
119
|
+
|
|
120
|
+
${ansi.bold}OUTPUT OPTIONS${ansi.reset}
|
|
121
|
+
${colors.accent}--json${ansi.reset} Output as JSON
|
|
122
|
+
${colors.accent}--output, -o <file>${ansi.reset} Save output to file
|
|
123
|
+
${colors.accent}--format, -f <fmt>${ansi.reset} Format: table, json, markdown
|
|
124
|
+
|
|
125
|
+
${ansi.bold}GLOBAL OPTIONS${ansi.reset}
|
|
126
|
+
${colors.accent}--path, -p <dir>${ansi.reset} Run in specified directory
|
|
127
|
+
${colors.accent}--verbose, -v${ansi.reset} Show detailed progress
|
|
128
|
+
${colors.accent}--quiet, -q${ansi.reset} Suppress non-essential output
|
|
129
|
+
${colors.accent}--ci${ansi.reset} CI mode
|
|
130
|
+
${colors.accent}--help, -h${ansi.reset} Show this help
|
|
131
|
+
|
|
132
|
+
${ansi.bold}💡 EXAMPLES${ansi.reset}
|
|
133
|
+
|
|
134
|
+
${ansi.dim}# Quick inventory of current directory${ansi.reset}
|
|
135
|
+
vibecheck classify
|
|
136
|
+
|
|
137
|
+
${ansi.dim}# JSON output for processing${ansi.reset}
|
|
138
|
+
vibecheck classify --json > inventory.json
|
|
139
|
+
|
|
140
|
+
${ansi.dim}# Include semantic duplicates (deeper analysis)${ansi.reset}
|
|
141
|
+
vibecheck classify --include-semantic
|
|
142
|
+
|
|
143
|
+
${ansi.dim}# Classify specific directory${ansi.reset}
|
|
144
|
+
vibecheck classify ./packages/core
|
|
145
|
+
|
|
146
|
+
${ansi.bold}📊 OUTPUT${ansi.reset}
|
|
147
|
+
Results include:
|
|
148
|
+
- duplicationMap: Files with duplicated code
|
|
149
|
+
- legacyMap: Deprecated/obsolete code locations
|
|
150
|
+
- riskClassifications: Per-file risk levels
|
|
151
|
+
- summary: Statistics overview
|
|
152
|
+
|
|
153
|
+
${ansi.bold}🔗 RELATED COMMANDS${ansi.reset}
|
|
154
|
+
${colors.accent}vibecheck approve${ansi.reset} Get authority verdicts ${ansi.cyan}[STARTER]${ansi.reset}
|
|
155
|
+
${colors.accent}vibecheck scan${ansi.reset} Full code analysis
|
|
156
|
+
|
|
157
|
+
${ansi.dim}─────────────────────────────────────────────────────────────${ansi.reset}
|
|
158
|
+
${ansi.dim}Documentation: https://docs.vibecheckai.dev/cli/classify${ansi.reset}
|
|
159
|
+
`);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
163
|
+
// INVENTORY ANALYSIS ENGINE
|
|
164
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Legacy code indicators
|
|
168
|
+
*/
|
|
169
|
+
const LEGACY_INDICATORS = [
|
|
170
|
+
{ pattern: /\.old\.(js|ts|tsx|jsx)$/i, type: 'backup', confidence: 0.9 },
|
|
171
|
+
{ pattern: /\.bak\.(js|ts|tsx|jsx)$/i, type: 'backup', confidence: 0.95 },
|
|
172
|
+
{ pattern: /\.backup\.(js|ts|tsx|jsx)$/i, type: 'backup', confidence: 0.95 },
|
|
173
|
+
{ pattern: /\.deprecated\.(js|ts|tsx|jsx)$/i, type: 'deprecated', confidence: 0.9 },
|
|
174
|
+
{ pattern: /\.legacy\.(js|ts|tsx|jsx)$/i, type: 'obsolete', confidence: 0.85 },
|
|
175
|
+
{ pattern: /_old\.(js|ts|tsx|jsx)$/i, type: 'backup', confidence: 0.8 },
|
|
176
|
+
{ pattern: /-old\.(js|ts|tsx|jsx)$/i, type: 'backup', confidence: 0.8 },
|
|
177
|
+
{ pattern: /\/deprecated\//i, type: 'deprecated', confidence: 0.85 },
|
|
178
|
+
{ pattern: /\/legacy\//i, type: 'obsolete', confidence: 0.8 },
|
|
179
|
+
{ pattern: /\/old\//i, type: 'backup', confidence: 0.7 },
|
|
180
|
+
];
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Content patterns indicating deprecated code
|
|
184
|
+
*/
|
|
185
|
+
const DEPRECATED_CONTENT_PATTERNS = [
|
|
186
|
+
{ regex: /@deprecated/gi, type: 'deprecated', confidence: 0.95 },
|
|
187
|
+
{ regex: /TODO:\s*remove/gi, type: 'obsolete', confidence: 0.7 },
|
|
188
|
+
{ regex: /FIXME:\s*delete/gi, type: 'obsolete', confidence: 0.7 },
|
|
189
|
+
{ regex: /\/\/\s*LEGACY/gi, type: 'obsolete', confidence: 0.75 },
|
|
190
|
+
{ regex: /\/\/\s*DEPRECATED/gi, type: 'deprecated', confidence: 0.9 },
|
|
191
|
+
{ regex: /\/\*\*?\s*@deprecated/gi, type: 'deprecated', confidence: 0.95 },
|
|
192
|
+
];
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* High-risk path patterns
|
|
196
|
+
*/
|
|
197
|
+
const HIGH_RISK_PATHS = [
|
|
198
|
+
{ pattern: /\/auth\//i, reason: 'Authentication code', level: 'HIGH' },
|
|
199
|
+
{ pattern: /\/security\//i, reason: 'Security-related code', level: 'HIGH' },
|
|
200
|
+
{ pattern: /\/migrations?\//i, reason: 'Database migrations', level: 'CRITICAL' },
|
|
201
|
+
{ pattern: /\/billing\//i, reason: 'Billing/payment code', level: 'CRITICAL' },
|
|
202
|
+
{ pattern: /\/stripe\//i, reason: 'Payment processing', level: 'CRITICAL' },
|
|
203
|
+
{ pattern: /\.env/i, reason: 'Environment configuration', level: 'CRITICAL' },
|
|
204
|
+
{ pattern: /secrets?\.(js|ts|json)/i, reason: 'Secrets configuration', level: 'CRITICAL' },
|
|
205
|
+
{ pattern: /\/prisma\//i, reason: 'Database schema', level: 'HIGH' },
|
|
206
|
+
{ pattern: /\/middleware\//i, reason: 'Request middleware', level: 'MEDIUM' },
|
|
207
|
+
];
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Excluded directories
|
|
211
|
+
*/
|
|
212
|
+
const EXCLUDED_DIRS = new Set([
|
|
213
|
+
'node_modules',
|
|
214
|
+
'.git',
|
|
215
|
+
'dist',
|
|
216
|
+
'build',
|
|
217
|
+
'.next',
|
|
218
|
+
'coverage',
|
|
219
|
+
'.vibecheck',
|
|
220
|
+
'__pycache__',
|
|
221
|
+
'.cache',
|
|
222
|
+
'vendor',
|
|
223
|
+
]);
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Find all source files using RepoIndex (V2) or legacy walk
|
|
227
|
+
*/
|
|
228
|
+
async function findSourceFiles(rootPath, maxFiles) {
|
|
229
|
+
const extensions = new Set(['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs']);
|
|
230
|
+
|
|
231
|
+
// Use RepoIndex if V2 is enabled and available
|
|
232
|
+
if (process.env.VIBECHECK_ENGINE_V2 === "1" && createIndex) {
|
|
233
|
+
const index = await createIndex(rootPath);
|
|
234
|
+
const jsFiles = index.getJsFiles();
|
|
235
|
+
|
|
236
|
+
const files = jsFiles
|
|
237
|
+
.slice(0, maxFiles)
|
|
238
|
+
.map(f => ({
|
|
239
|
+
path: f.abs,
|
|
240
|
+
relativePath: f.rel,
|
|
241
|
+
name: path.basename(f.abs),
|
|
242
|
+
ext: f.ext,
|
|
243
|
+
_indexContent: index.getContent(f.abs), // Cache content from index
|
|
244
|
+
}));
|
|
245
|
+
|
|
246
|
+
// Store index reference for cleanup
|
|
247
|
+
files._index = index;
|
|
248
|
+
return files;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Legacy file walking
|
|
252
|
+
const files = [];
|
|
253
|
+
|
|
254
|
+
async function walk(dir, depth = 0) {
|
|
255
|
+
if (depth > 20 || files.length >= maxFiles) return;
|
|
256
|
+
|
|
257
|
+
try {
|
|
258
|
+
const entries = await fs.promises.readdir(dir, { withFileTypes: true });
|
|
259
|
+
|
|
260
|
+
for (const entry of entries) {
|
|
261
|
+
if (files.length >= maxFiles) break;
|
|
262
|
+
|
|
263
|
+
const fullPath = path.join(dir, entry.name);
|
|
264
|
+
const relativePath = path.relative(rootPath, fullPath);
|
|
265
|
+
|
|
266
|
+
if (entry.isDirectory()) {
|
|
267
|
+
if (!EXCLUDED_DIRS.has(entry.name) && !entry.name.startsWith('.')) {
|
|
268
|
+
await walk(fullPath, depth + 1);
|
|
269
|
+
}
|
|
270
|
+
} else if (entry.isFile()) {
|
|
271
|
+
const ext = path.extname(entry.name).toLowerCase();
|
|
272
|
+
if (extensions.has(ext)) {
|
|
273
|
+
files.push({
|
|
274
|
+
path: fullPath,
|
|
275
|
+
relativePath,
|
|
276
|
+
name: entry.name,
|
|
277
|
+
ext,
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
} catch (err) {
|
|
283
|
+
// Skip inaccessible directories
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
await walk(rootPath);
|
|
288
|
+
return files;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Calculate file hash for duplicate detection
|
|
293
|
+
*/
|
|
294
|
+
function hashContent(content) {
|
|
295
|
+
return crypto.createHash('sha256').update(content).digest('hex').slice(0, 16);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Normalize content for near-duplicate detection
|
|
300
|
+
*/
|
|
301
|
+
function normalizeContent(content) {
|
|
302
|
+
return content
|
|
303
|
+
.replace(/\/\/.*$/gm, '') // Remove single-line comments
|
|
304
|
+
.replace(/\/\*[\s\S]*?\*\//g, '') // Remove multi-line comments
|
|
305
|
+
.replace(/\s+/g, ' ') // Normalize whitespace
|
|
306
|
+
.replace(/['"`]/g, '"') // Normalize quotes
|
|
307
|
+
.trim();
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Calculate Jaccard similarity between two sets
|
|
312
|
+
*/
|
|
313
|
+
function jaccardSimilarity(set1, set2) {
|
|
314
|
+
const intersection = new Set([...set1].filter(x => set2.has(x)));
|
|
315
|
+
const union = new Set([...set1, ...set2]);
|
|
316
|
+
return union.size === 0 ? 0 : intersection.size / union.size;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Tokenize content for similarity comparison
|
|
321
|
+
*/
|
|
322
|
+
function tokenize(content) {
|
|
323
|
+
return new Set(
|
|
324
|
+
content
|
|
325
|
+
.split(/[\s\(\)\{\}\[\];,.:=<>!&|?]+/)
|
|
326
|
+
.filter(t => t.length > 2)
|
|
327
|
+
.map(t => t.toLowerCase())
|
|
328
|
+
);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Detect legacy code indicators
|
|
333
|
+
*/
|
|
334
|
+
function detectLegacyIndicators(filePath, content) {
|
|
335
|
+
const indicators = [];
|
|
336
|
+
|
|
337
|
+
// Check file path patterns
|
|
338
|
+
for (const { pattern, type, confidence } of LEGACY_INDICATORS) {
|
|
339
|
+
if (pattern.test(filePath)) {
|
|
340
|
+
indicators.push({ type, confidence, evidence: `File path matches: ${pattern}` });
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// Check content patterns
|
|
345
|
+
for (const { regex, type, confidence } of DEPRECATED_CONTENT_PATTERNS) {
|
|
346
|
+
const matches = content.match(regex);
|
|
347
|
+
if (matches) {
|
|
348
|
+
indicators.push({
|
|
349
|
+
type,
|
|
350
|
+
confidence,
|
|
351
|
+
evidence: `Found ${matches.length}x: ${regex.source}`,
|
|
352
|
+
count: matches.length,
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
return indicators;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Classify file risk level
|
|
362
|
+
*/
|
|
363
|
+
function classifyRisk(filePath, content) {
|
|
364
|
+
// Check path patterns
|
|
365
|
+
for (const { pattern, reason, level } of HIGH_RISK_PATHS) {
|
|
366
|
+
if (pattern.test(filePath)) {
|
|
367
|
+
return { level, reason, tags: [reason.toLowerCase().replace(/\s+/g, '-')] };
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// Check content indicators
|
|
372
|
+
const indicators = [];
|
|
373
|
+
|
|
374
|
+
if (/process\.env\./i.test(content)) {
|
|
375
|
+
indicators.push('uses-env-vars');
|
|
376
|
+
}
|
|
377
|
+
if (/import.*['"](crypto|bcrypt|argon2|jose)['"]/i.test(content)) {
|
|
378
|
+
indicators.push('cryptography');
|
|
379
|
+
}
|
|
380
|
+
if (/sql|query|execute/i.test(content) && /where|select|insert|update|delete/i.test(content)) {
|
|
381
|
+
indicators.push('database-operations');
|
|
382
|
+
}
|
|
383
|
+
if (/(Bearer|Authorization|api[_-]?key|secret|token)/i.test(content)) {
|
|
384
|
+
indicators.push('auth-related');
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
if (indicators.includes('cryptography') || indicators.includes('auth-related')) {
|
|
388
|
+
return { level: 'HIGH', reason: 'Security-sensitive operations', tags: indicators };
|
|
389
|
+
}
|
|
390
|
+
if (indicators.includes('database-operations')) {
|
|
391
|
+
return { level: 'MEDIUM', reason: 'Database operations', tags: indicators };
|
|
392
|
+
}
|
|
393
|
+
if (indicators.includes('uses-env-vars')) {
|
|
394
|
+
return { level: 'MEDIUM', reason: 'Environment-dependent', tags: indicators };
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
return { level: 'LOW', reason: 'Standard code', tags: [] };
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/**
|
|
401
|
+
* Run inventory analysis
|
|
402
|
+
*/
|
|
403
|
+
async function runInventoryAnalysis(projectPath, opts, spinner) {
|
|
404
|
+
const startTime = Date.now();
|
|
405
|
+
|
|
406
|
+
// Find source files
|
|
407
|
+
if (spinner) spinner.update('Discovering source files...');
|
|
408
|
+
const files = await findSourceFiles(projectPath, opts.maxFiles);
|
|
409
|
+
|
|
410
|
+
if (files.length === 0) {
|
|
411
|
+
return {
|
|
412
|
+
duplicationMap: [],
|
|
413
|
+
legacyMap: [],
|
|
414
|
+
riskClassifications: [],
|
|
415
|
+
summary: {
|
|
416
|
+
totalFiles: 0,
|
|
417
|
+
duplicatedFiles: 0,
|
|
418
|
+
legacyFiles: 0,
|
|
419
|
+
highRiskFiles: 0,
|
|
420
|
+
totalDuplicateLines: 0,
|
|
421
|
+
},
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
if (spinner) spinner.update(`Analyzing ${files.length} files...`);
|
|
426
|
+
|
|
427
|
+
// Read all file contents (use cached content from RepoIndex when available)
|
|
428
|
+
const fileContents = new Map();
|
|
429
|
+
const fileHashes = new Map();
|
|
430
|
+
const normalizedHashes = new Map();
|
|
431
|
+
|
|
432
|
+
for (const file of files) {
|
|
433
|
+
try {
|
|
434
|
+
// Use cached content from RepoIndex if available (V2 optimization)
|
|
435
|
+
const content = file._indexContent || await fs.promises.readFile(file.path, 'utf-8');
|
|
436
|
+
const lines = content.split('\n').length;
|
|
437
|
+
fileContents.set(file.relativePath, { content, lines });
|
|
438
|
+
|
|
439
|
+
// Calculate hashes
|
|
440
|
+
const hash = hashContent(content);
|
|
441
|
+
const normalizedHash = hashContent(normalizeContent(content));
|
|
442
|
+
|
|
443
|
+
fileHashes.set(file.relativePath, hash);
|
|
444
|
+
normalizedHashes.set(file.relativePath, normalizedHash);
|
|
445
|
+
} catch (err) {
|
|
446
|
+
// Skip unreadable files
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// Cleanup RepoIndex cache if used
|
|
451
|
+
if (files._index) {
|
|
452
|
+
files._index.clearContentCache();
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
456
|
+
// DUPLICATE DETECTION
|
|
457
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
458
|
+
|
|
459
|
+
if (spinner) spinner.update('Detecting duplicates...');
|
|
460
|
+
|
|
461
|
+
const duplicationMap = [];
|
|
462
|
+
const exactDuplicates = new Map(); // hash -> [files]
|
|
463
|
+
const nearDuplicates = new Map(); // normalizedHash -> [files]
|
|
464
|
+
|
|
465
|
+
// Group by exact hash
|
|
466
|
+
for (const [filePath, hash] of fileHashes.entries()) {
|
|
467
|
+
if (!exactDuplicates.has(hash)) {
|
|
468
|
+
exactDuplicates.set(hash, []);
|
|
469
|
+
}
|
|
470
|
+
exactDuplicates.get(hash).push(filePath);
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
// Find exact duplicates
|
|
474
|
+
for (const [hash, files] of exactDuplicates.entries()) {
|
|
475
|
+
if (files.length > 1) {
|
|
476
|
+
const primary = files[0];
|
|
477
|
+
const duplicates = files.slice(1);
|
|
478
|
+
const { lines } = fileContents.get(primary) || { lines: 0 };
|
|
479
|
+
|
|
480
|
+
duplicationMap.push({
|
|
481
|
+
primary,
|
|
482
|
+
duplicates,
|
|
483
|
+
similarity: 1.0,
|
|
484
|
+
type: 'exact',
|
|
485
|
+
lineCount: lines,
|
|
486
|
+
});
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
// Group by normalized hash for near-duplicates
|
|
491
|
+
if (opts.includeNear) {
|
|
492
|
+
for (const [filePath, hash] of normalizedHashes.entries()) {
|
|
493
|
+
if (!nearDuplicates.has(hash)) {
|
|
494
|
+
nearDuplicates.set(hash, []);
|
|
495
|
+
}
|
|
496
|
+
nearDuplicates.get(hash).push(filePath);
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
// Find near-duplicates (not already exact)
|
|
500
|
+
const exactPairs = new Set();
|
|
501
|
+
for (const entry of duplicationMap) {
|
|
502
|
+
for (const dup of entry.duplicates) {
|
|
503
|
+
exactPairs.add(`${entry.primary}:${dup}`);
|
|
504
|
+
exactPairs.add(`${dup}:${entry.primary}`);
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
for (const [hash, files] of nearDuplicates.entries()) {
|
|
509
|
+
if (files.length > 1) {
|
|
510
|
+
const primary = files[0];
|
|
511
|
+
const potentialDups = files.slice(1).filter(f => !exactPairs.has(`${primary}:${f}`));
|
|
512
|
+
|
|
513
|
+
if (potentialDups.length > 0) {
|
|
514
|
+
const { lines } = fileContents.get(primary) || { lines: 0 };
|
|
515
|
+
duplicationMap.push({
|
|
516
|
+
primary,
|
|
517
|
+
duplicates: potentialDups,
|
|
518
|
+
similarity: 0.95, // High similarity due to normalized match
|
|
519
|
+
type: 'near',
|
|
520
|
+
lineCount: lines,
|
|
521
|
+
});
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
// Semantic duplicates (token-based similarity)
|
|
528
|
+
if (opts.includeSemantic) {
|
|
529
|
+
if (spinner) spinner.update('Computing semantic similarity...');
|
|
530
|
+
|
|
531
|
+
const tokenSets = new Map();
|
|
532
|
+
for (const [filePath, { content }] of fileContents.entries()) {
|
|
533
|
+
tokenSets.set(filePath, tokenize(content));
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
const processedPairs = new Set();
|
|
537
|
+
const filePaths = Array.from(fileContents.keys());
|
|
538
|
+
|
|
539
|
+
for (let i = 0; i < filePaths.length && i < 500; i++) {
|
|
540
|
+
for (let j = i + 1; j < filePaths.length && j < 500; j++) {
|
|
541
|
+
const file1 = filePaths[i];
|
|
542
|
+
const file2 = filePaths[j];
|
|
543
|
+
const pairKey = `${file1}:${file2}`;
|
|
544
|
+
|
|
545
|
+
if (processedPairs.has(pairKey)) continue;
|
|
546
|
+
processedPairs.add(pairKey);
|
|
547
|
+
|
|
548
|
+
const tokens1 = tokenSets.get(file1);
|
|
549
|
+
const tokens2 = tokenSets.get(file2);
|
|
550
|
+
|
|
551
|
+
const similarity = jaccardSimilarity(tokens1, tokens2);
|
|
552
|
+
|
|
553
|
+
if (similarity >= opts.threshold) {
|
|
554
|
+
// Check not already in duplicationMap
|
|
555
|
+
const existing = duplicationMap.find(d =>
|
|
556
|
+
(d.primary === file1 && d.duplicates.includes(file2)) ||
|
|
557
|
+
(d.primary === file2 && d.duplicates.includes(file1))
|
|
558
|
+
);
|
|
559
|
+
|
|
560
|
+
if (!existing) {
|
|
561
|
+
const { lines } = fileContents.get(file1) || { lines: 0 };
|
|
562
|
+
duplicationMap.push({
|
|
563
|
+
primary: file1,
|
|
564
|
+
duplicates: [file2],
|
|
565
|
+
similarity: Math.round(similarity * 100) / 100,
|
|
566
|
+
type: 'semantic',
|
|
567
|
+
lineCount: lines,
|
|
568
|
+
});
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
576
|
+
// LEGACY CODE DETECTION
|
|
577
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
578
|
+
|
|
579
|
+
if (spinner) spinner.update('Detecting legacy code...');
|
|
580
|
+
|
|
581
|
+
const legacyMap = [];
|
|
582
|
+
|
|
583
|
+
for (const [filePath, { content }] of fileContents.entries()) {
|
|
584
|
+
const indicators = detectLegacyIndicators(filePath, content);
|
|
585
|
+
|
|
586
|
+
if (indicators.length > 0) {
|
|
587
|
+
// Use highest confidence indicator
|
|
588
|
+
const bestIndicator = indicators.reduce((a, b) => a.confidence > b.confidence ? a : b);
|
|
589
|
+
|
|
590
|
+
legacyMap.push({
|
|
591
|
+
file: filePath,
|
|
592
|
+
type: bestIndicator.type,
|
|
593
|
+
evidence: indicators.map(i => i.evidence),
|
|
594
|
+
confidence: bestIndicator.confidence,
|
|
595
|
+
});
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
600
|
+
// RISK CLASSIFICATION
|
|
601
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
602
|
+
|
|
603
|
+
if (spinner) spinner.update('Classifying risk levels...');
|
|
604
|
+
|
|
605
|
+
const riskClassifications = [];
|
|
606
|
+
|
|
607
|
+
for (const [filePath, { content }] of fileContents.entries()) {
|
|
608
|
+
const risk = classifyRisk(filePath, content);
|
|
609
|
+
|
|
610
|
+
if (risk.level !== 'LOW' || opts.verbose) {
|
|
611
|
+
riskClassifications.push({
|
|
612
|
+
file: filePath,
|
|
613
|
+
level: risk.level,
|
|
614
|
+
reason: risk.reason,
|
|
615
|
+
tags: risk.tags,
|
|
616
|
+
});
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
621
|
+
// SUMMARY
|
|
622
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
623
|
+
|
|
624
|
+
const duplicatedFiles = new Set();
|
|
625
|
+
let totalDuplicateLines = 0;
|
|
626
|
+
|
|
627
|
+
for (const entry of duplicationMap) {
|
|
628
|
+
duplicatedFiles.add(entry.primary);
|
|
629
|
+
for (const dup of entry.duplicates) {
|
|
630
|
+
duplicatedFiles.add(dup);
|
|
631
|
+
}
|
|
632
|
+
totalDuplicateLines += entry.lineCount * entry.duplicates.length;
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
const summary = {
|
|
636
|
+
totalFiles: files.length,
|
|
637
|
+
duplicatedFiles: duplicatedFiles.size,
|
|
638
|
+
legacyFiles: legacyMap.length,
|
|
639
|
+
highRiskFiles: riskClassifications.filter(r => r.level === 'HIGH' || r.level === 'CRITICAL').length,
|
|
640
|
+
totalDuplicateLines,
|
|
641
|
+
analysisTimeMs: Date.now() - startTime,
|
|
642
|
+
};
|
|
643
|
+
|
|
644
|
+
return {
|
|
645
|
+
duplicationMap,
|
|
646
|
+
legacyMap,
|
|
647
|
+
riskClassifications,
|
|
648
|
+
summary,
|
|
649
|
+
};
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
653
|
+
// OUTPUT FORMATTERS
|
|
654
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
655
|
+
|
|
656
|
+
function formatTableOutput(result, projectPath) {
|
|
657
|
+
const lines = [];
|
|
658
|
+
const { duplicationMap, legacyMap, riskClassifications, summary } = result;
|
|
659
|
+
|
|
660
|
+
lines.push('');
|
|
661
|
+
lines.push('┌────────────────────────────────────────────────────────────────────┐');
|
|
662
|
+
lines.push('│ Authority Verdict: INVENTORY (Read-Only) │');
|
|
663
|
+
lines.push('├────────────────────────────────────────────────────────────────────┤');
|
|
664
|
+
lines.push(`│ Project: ${path.basename(projectPath).padEnd(55)}│`);
|
|
665
|
+
lines.push(`│ Files analyzed: ${String(summary.totalFiles).padEnd(48)}│`);
|
|
666
|
+
lines.push(`│ Analysis time: ${String(summary.analysisTimeMs + 'ms').padEnd(49)}│`);
|
|
667
|
+
lines.push('└────────────────────────────────────────────────────────────────────┘');
|
|
668
|
+
lines.push('');
|
|
669
|
+
|
|
670
|
+
// Summary stats
|
|
671
|
+
lines.push('┌─────────────────────────────────────────┐');
|
|
672
|
+
lines.push('│ Summary │');
|
|
673
|
+
lines.push('├─────────────────────────────────────────┤');
|
|
674
|
+
lines.push(`│ Duplicated files: ${String(summary.duplicatedFiles).padStart(5)} │`);
|
|
675
|
+
lines.push(`│ Legacy files: ${String(summary.legacyFiles).padStart(5)} │`);
|
|
676
|
+
lines.push(`│ High-risk files: ${String(summary.highRiskFiles).padStart(5)} │`);
|
|
677
|
+
lines.push(`│ Duplicate lines: ${String(summary.totalDuplicateLines).padStart(5)} │`);
|
|
678
|
+
lines.push('└─────────────────────────────────────────┘');
|
|
679
|
+
lines.push('');
|
|
680
|
+
|
|
681
|
+
// Duplications
|
|
682
|
+
if (duplicationMap.length > 0) {
|
|
683
|
+
lines.push(`${ansi.bold}Duplications (${duplicationMap.length})${ansi.reset}`);
|
|
684
|
+
lines.push('');
|
|
685
|
+
|
|
686
|
+
for (const entry of duplicationMap.slice(0, 15)) {
|
|
687
|
+
const typeIcon = entry.type === 'exact' ? '=' : entry.type === 'near' ? '≈' : '~';
|
|
688
|
+
const similarity = Math.round(entry.similarity * 100);
|
|
689
|
+
lines.push(` ${typeIcon} ${ansi.dim}[${similarity}%]${ansi.reset} ${colors.accent}${entry.primary}${ansi.reset}`);
|
|
690
|
+
for (const dup of entry.duplicates.slice(0, 3)) {
|
|
691
|
+
lines.push(` └─ ${dup}`);
|
|
692
|
+
}
|
|
693
|
+
if (entry.duplicates.length > 3) {
|
|
694
|
+
lines.push(` ${ansi.dim}... and ${entry.duplicates.length - 3} more${ansi.reset}`);
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
if (duplicationMap.length > 15) {
|
|
699
|
+
lines.push(` ${ansi.dim}... and ${duplicationMap.length - 15} more duplicate groups${ansi.reset}`);
|
|
700
|
+
}
|
|
701
|
+
lines.push('');
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
// Legacy code
|
|
705
|
+
if (legacyMap.length > 0) {
|
|
706
|
+
lines.push(`${ansi.bold}Legacy Code (${legacyMap.length})${ansi.reset}`);
|
|
707
|
+
lines.push('');
|
|
708
|
+
|
|
709
|
+
for (const entry of legacyMap.slice(0, 15)) {
|
|
710
|
+
const typeIcon = entry.type === 'deprecated' ? '⚠' : entry.type === 'backup' ? '📦' : '👻';
|
|
711
|
+
const conf = Math.round(entry.confidence * 100);
|
|
712
|
+
lines.push(` ${typeIcon} ${ansi.dim}[${conf}%]${ansi.reset} ${colors.warning}${entry.file}${ansi.reset}`);
|
|
713
|
+
lines.push(` ${ansi.dim}Type: ${entry.type}${ansi.reset}`);
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
if (legacyMap.length > 15) {
|
|
717
|
+
lines.push(` ${ansi.dim}... and ${legacyMap.length - 15} more legacy files${ansi.reset}`);
|
|
718
|
+
}
|
|
719
|
+
lines.push('');
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
// High-risk files
|
|
723
|
+
const highRisk = riskClassifications.filter(r => r.level === 'HIGH' || r.level === 'CRITICAL');
|
|
724
|
+
if (highRisk.length > 0) {
|
|
725
|
+
lines.push(`${ansi.bold}High-Risk Files (${highRisk.length})${ansi.reset}`);
|
|
726
|
+
lines.push('');
|
|
727
|
+
|
|
728
|
+
for (const entry of highRisk.slice(0, 15)) {
|
|
729
|
+
const levelIcon = entry.level === 'CRITICAL' ? '🔴' : '🟠';
|
|
730
|
+
lines.push(` ${levelIcon} ${colors.error}${entry.file}${ansi.reset}`);
|
|
731
|
+
lines.push(` ${ansi.dim}${entry.reason}${ansi.reset}`);
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
if (highRisk.length > 15) {
|
|
735
|
+
lines.push(` ${ansi.dim}... and ${highRisk.length - 15} more high-risk files${ansi.reset}`);
|
|
736
|
+
}
|
|
737
|
+
lines.push('');
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
// Footer
|
|
741
|
+
lines.push('─────────────────────────────────────────────────────────────────────');
|
|
742
|
+
lines.push(`${ansi.dim}This is a read-only inventory. Use 'vibecheck approve' to get verdicts.${ansi.reset}`);
|
|
743
|
+
lines.push('');
|
|
744
|
+
|
|
745
|
+
return lines.join('\n');
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
function formatMarkdownOutput(result, projectPath) {
|
|
749
|
+
const { duplicationMap, legacyMap, riskClassifications, summary } = result;
|
|
750
|
+
const lines = [];
|
|
751
|
+
|
|
752
|
+
lines.push(`# Inventory Report`);
|
|
753
|
+
lines.push('');
|
|
754
|
+
lines.push(`**Project:** ${path.basename(projectPath)}`);
|
|
755
|
+
lines.push(`**Authority:** inventory (read-only)`);
|
|
756
|
+
lines.push(`**Generated:** ${new Date().toISOString()}`);
|
|
757
|
+
lines.push('');
|
|
758
|
+
|
|
759
|
+
lines.push('## Summary');
|
|
760
|
+
lines.push('');
|
|
761
|
+
lines.push('| Metric | Count |');
|
|
762
|
+
lines.push('|--------|-------|');
|
|
763
|
+
lines.push(`| Total files analyzed | ${summary.totalFiles} |`);
|
|
764
|
+
lines.push(`| Duplicated files | ${summary.duplicatedFiles} |`);
|
|
765
|
+
lines.push(`| Legacy files | ${summary.legacyFiles} |`);
|
|
766
|
+
lines.push(`| High-risk files | ${summary.highRiskFiles} |`);
|
|
767
|
+
lines.push(`| Duplicate lines | ${summary.totalDuplicateLines} |`);
|
|
768
|
+
lines.push('');
|
|
769
|
+
|
|
770
|
+
if (duplicationMap.length > 0) {
|
|
771
|
+
lines.push('## Duplications');
|
|
772
|
+
lines.push('');
|
|
773
|
+
for (const entry of duplicationMap) {
|
|
774
|
+
lines.push(`### ${entry.primary}`);
|
|
775
|
+
lines.push(`- Type: ${entry.type}`);
|
|
776
|
+
lines.push(`- Similarity: ${Math.round(entry.similarity * 100)}%`);
|
|
777
|
+
lines.push(`- Lines: ${entry.lineCount}`);
|
|
778
|
+
lines.push(`- Duplicates:`);
|
|
779
|
+
for (const dup of entry.duplicates) {
|
|
780
|
+
lines.push(` - \`${dup}\``);
|
|
781
|
+
}
|
|
782
|
+
lines.push('');
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
if (legacyMap.length > 0) {
|
|
787
|
+
lines.push('## Legacy Code');
|
|
788
|
+
lines.push('');
|
|
789
|
+
lines.push('| File | Type | Confidence |');
|
|
790
|
+
lines.push('|------|------|------------|');
|
|
791
|
+
for (const entry of legacyMap) {
|
|
792
|
+
lines.push(`| \`${entry.file}\` | ${entry.type} | ${Math.round(entry.confidence * 100)}% |`);
|
|
793
|
+
}
|
|
794
|
+
lines.push('');
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
const highRisk = riskClassifications.filter(r => r.level === 'HIGH' || r.level === 'CRITICAL');
|
|
798
|
+
if (highRisk.length > 0) {
|
|
799
|
+
lines.push('## High-Risk Files');
|
|
800
|
+
lines.push('');
|
|
801
|
+
lines.push('| File | Level | Reason |');
|
|
802
|
+
lines.push('|------|-------|--------|');
|
|
803
|
+
for (const entry of highRisk) {
|
|
804
|
+
lines.push(`| \`${entry.file}\` | ${entry.level} | ${entry.reason} |`);
|
|
805
|
+
}
|
|
806
|
+
lines.push('');
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
return lines.join('\n');
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
813
|
+
// MAIN COMMAND
|
|
814
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
815
|
+
|
|
816
|
+
async function runClassify(args) {
|
|
817
|
+
const opts = parseArgs(args);
|
|
818
|
+
|
|
819
|
+
// Show help
|
|
820
|
+
if (opts.help) {
|
|
821
|
+
printHelp(shouldShowBanner(opts));
|
|
822
|
+
return 0;
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
// CRITICAL: Check for JSON output FIRST - skip all banners/output if this flag is set
|
|
826
|
+
if (opts.json) {
|
|
827
|
+
process.env.NO_COLOR = '1';
|
|
828
|
+
// Skip all banner/output until we output JSON
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
// Print banner (skip if JSON mode)
|
|
832
|
+
if (shouldShowBanner(opts) && !opts.json) {
|
|
833
|
+
printBanner();
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
const projectPath = path.resolve(opts.path);
|
|
837
|
+
|
|
838
|
+
// Validate project path
|
|
839
|
+
if (!fs.existsSync(projectPath)) {
|
|
840
|
+
throw createUserError(`Project path does not exist: ${projectPath}`, "ValidationError");
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
if (!opts.quiet && !opts.json) {
|
|
844
|
+
console.log(` ${ansi.dim}Project:${ansi.reset} ${ansi.bold}${path.basename(projectPath)}${ansi.reset}`);
|
|
845
|
+
console.log(` ${ansi.dim}Authority:${ansi.reset} ${colors.accent}inventory${ansi.reset} (FREE tier)`);
|
|
846
|
+
console.log();
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
// Run analysis (skip spinner if JSON mode)
|
|
850
|
+
const spinner = !opts.json ? new Spinner({ color: colors.primary }) : null;
|
|
851
|
+
if (spinner) spinner.start('Starting inventory analysis...');
|
|
852
|
+
|
|
853
|
+
try {
|
|
854
|
+
const result = await runInventoryAnalysis(projectPath, opts, spinner);
|
|
855
|
+
|
|
856
|
+
if (spinner) {
|
|
857
|
+
spinner.succeed(`Analysis complete (${result.summary.analysisTimeMs}ms)`);
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
// Construct full output
|
|
861
|
+
const output = {
|
|
862
|
+
authority: 'inventory',
|
|
863
|
+
version: '1.0.0',
|
|
864
|
+
timestamp: new Date().toISOString(),
|
|
865
|
+
action: 'PROCEED', // Inventory is read-only, always PROCEED
|
|
866
|
+
projectPath: projectPath,
|
|
867
|
+
...result,
|
|
868
|
+
};
|
|
869
|
+
|
|
870
|
+
// Output based on format
|
|
871
|
+
if (opts.json) {
|
|
872
|
+
// CRITICAL: Output ONLY JSON, no banners/colors
|
|
873
|
+
process.env.NO_COLOR = '1';
|
|
874
|
+
console.log(JSON.stringify(output, null, 2));
|
|
875
|
+
// Exit immediately after JSON output
|
|
876
|
+
return EXIT.SUCCESS;
|
|
877
|
+
} else if (opts.format === 'markdown') {
|
|
878
|
+
console.log(formatMarkdownOutput(result, projectPath));
|
|
879
|
+
} else {
|
|
880
|
+
// Use Mission Control format
|
|
881
|
+
const { formatClassifyOutput } = require('./lib/classify-output');
|
|
882
|
+
console.log(formatClassifyOutput(result, { projectPath, version: 'v3.5.5' }));
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
// Save to file if requested
|
|
886
|
+
if (opts.output) {
|
|
887
|
+
const outputPath = path.resolve(opts.output);
|
|
888
|
+
const content = opts.format === 'markdown'
|
|
889
|
+
? formatMarkdownOutput(result, projectPath)
|
|
890
|
+
: JSON.stringify(output, null, 2);
|
|
891
|
+
|
|
892
|
+
await fs.promises.writeFile(outputPath, content);
|
|
893
|
+
|
|
894
|
+
if (!opts.quiet && !opts.json) {
|
|
895
|
+
console.log(` ${colors.success}✓${ansi.reset} Output saved to: ${outputPath}`);
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
return EXIT.SUCCESS;
|
|
900
|
+
|
|
901
|
+
} catch (error) {
|
|
902
|
+
if (spinner && !opts.json) {
|
|
903
|
+
spinner.fail(`Analysis failed: ${error.message}`);
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
// JSON error output
|
|
907
|
+
if (opts.json) {
|
|
908
|
+
process.env.NO_COLOR = '1';
|
|
909
|
+
const errorOutput = {
|
|
910
|
+
success: false,
|
|
911
|
+
error: {
|
|
912
|
+
code: error.code || 'CLASSIFY_ERROR',
|
|
913
|
+
message: error.message,
|
|
914
|
+
},
|
|
915
|
+
exitCode: error.code === 'VALIDATION_ERROR' ? EXIT.USER_ERROR : EXIT.INTERNAL_ERROR
|
|
916
|
+
};
|
|
917
|
+
console.log(JSON.stringify(errorOutput, null, 2));
|
|
918
|
+
process.exit(errorOutput.exitCode);
|
|
919
|
+
return errorOutput.exitCode;
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
throw error;
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
module.exports = {
|
|
927
|
+
runClassify: withErrorHandling(runClassify, "Classify failed"),
|
|
928
|
+
};
|