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,1264 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Proof-Carrying Context System
|
|
3
|
+
* Every claim must have file:line evidence or it gets flagged as hypothesis
|
|
4
|
+
*
|
|
5
|
+
* This module exports the SINGLE TRUTH CONTRACT used across CLI, MCP, and extension.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const fs = require("fs");
|
|
9
|
+
const path = require("path");
|
|
10
|
+
|
|
11
|
+
const DEFAULT_EVIDENCE_CONFIDENCE = 0.9;
|
|
12
|
+
const MAX_EVIDENCE_SNIPPET = 200;
|
|
13
|
+
|
|
14
|
+
// Context attribution message shown when AI uses vibecheck data
|
|
15
|
+
const CONTEXT_ATTRIBUTION = "🧠 Context enhanced by vibecheck";
|
|
16
|
+
|
|
17
|
+
// =============================================================================
|
|
18
|
+
// UNIFIED TRUTH CONTRACT SCHEMA v1.0
|
|
19
|
+
// =============================================================================
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* The canonical evidence schema used across CLI, MCP, and VS Code extension.
|
|
23
|
+
* All tools emitting claims MUST include this minimal required payload.
|
|
24
|
+
*/
|
|
25
|
+
const EVIDENCE_SCHEMA = {
|
|
26
|
+
version: "1.0.0",
|
|
27
|
+
required: ["file", "line", "snippet", "confidence"],
|
|
28
|
+
maxSnippet: MAX_EVIDENCE_SNIPPET,
|
|
29
|
+
confidenceThresholds: {
|
|
30
|
+
strict: 0.8, // HIGH confidence required for strict mode
|
|
31
|
+
balanced: 0.6, // MEDIUM confidence for balanced mode
|
|
32
|
+
permissive: 0.4, // LOW confidence allowed in permissive mode
|
|
33
|
+
},
|
|
34
|
+
claimTypes: [
|
|
35
|
+
"route",
|
|
36
|
+
"schema_table",
|
|
37
|
+
"schema_column",
|
|
38
|
+
"export",
|
|
39
|
+
"default_export",
|
|
40
|
+
"middleware",
|
|
41
|
+
"env_var",
|
|
42
|
+
"auth_guard",
|
|
43
|
+
"billing_gate",
|
|
44
|
+
"hypothesis", // Claims without proof get flagged here
|
|
45
|
+
],
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* The truth contract defines rules that AI must satisfy.
|
|
50
|
+
* Claims without evidence are automatically flagged as "unknown" and block actions.
|
|
51
|
+
*/
|
|
52
|
+
const TRUTH_CONTRACT = {
|
|
53
|
+
version: "1.0.0",
|
|
54
|
+
claimsRequireEvidence: true,
|
|
55
|
+
confidenceThresholds: EVIDENCE_SCHEMA.confidenceThresholds,
|
|
56
|
+
policies: {
|
|
57
|
+
strict: {
|
|
58
|
+
minConfidence: 0.8,
|
|
59
|
+
allowUnknown: false,
|
|
60
|
+
requireValidation: true,
|
|
61
|
+
blockOnDrift: true,
|
|
62
|
+
},
|
|
63
|
+
balanced: {
|
|
64
|
+
minConfidence: 0.6,
|
|
65
|
+
allowUnknown: false,
|
|
66
|
+
requireValidation: true,
|
|
67
|
+
blockOnDrift: false,
|
|
68
|
+
},
|
|
69
|
+
permissive: {
|
|
70
|
+
minConfidence: 0.4,
|
|
71
|
+
allowUnknown: true,
|
|
72
|
+
requireValidation: false,
|
|
73
|
+
blockOnDrift: false,
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
invariants: [
|
|
77
|
+
"No paid feature without server-side enforcement",
|
|
78
|
+
"No success UI without confirmed success",
|
|
79
|
+
"No route reference without matching route map entry",
|
|
80
|
+
"No silent catch in auth/billing flows",
|
|
81
|
+
"No hardcoded secrets in production code",
|
|
82
|
+
],
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Normalize an evidence item to the canonical schema.
|
|
87
|
+
* @param {object} item - Raw evidence item
|
|
88
|
+
* @param {string} projectPath - Project root path
|
|
89
|
+
* @param {object} fallback - Fallback values
|
|
90
|
+
* @returns {object} Normalized evidence conforming to EVIDENCE_SCHEMA
|
|
91
|
+
*/
|
|
92
|
+
function normalizeToEvidenceSchema(item, projectPath, fallback = {}) {
|
|
93
|
+
const file = item?.file || fallback.file || "";
|
|
94
|
+
const line = Number(item?.line || item?.lines || fallback.line || 1);
|
|
95
|
+
let snippet = item?.snippet || item?.evidence || fallback.evidence || "";
|
|
96
|
+
|
|
97
|
+
if (!snippet && file && projectPath) {
|
|
98
|
+
try {
|
|
99
|
+
const content = fs.readFileSync(path.join(projectPath, file), "utf-8");
|
|
100
|
+
const lines = content.split("\n");
|
|
101
|
+
const idx = Math.max(0, Math.min(lines.length - 1, line - 1));
|
|
102
|
+
snippet = lines[idx] || "";
|
|
103
|
+
} catch {
|
|
104
|
+
// File not readable
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return {
|
|
109
|
+
file,
|
|
110
|
+
line,
|
|
111
|
+
snippet: String(snippet || "").slice(0, EVIDENCE_SCHEMA.maxSnippet),
|
|
112
|
+
confidence: item?.confidence ?? fallback.confidence ?? DEFAULT_EVIDENCE_CONFIDENCE,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Validate that evidence conforms to the schema.
|
|
118
|
+
* @param {object} evidence - Evidence object to validate
|
|
119
|
+
* @param {string} policy - Policy mode: 'strict' | 'balanced' | 'permissive'
|
|
120
|
+
* @returns {{ valid: boolean, errors: string[], confidence: number }}
|
|
121
|
+
*/
|
|
122
|
+
function validateEvidence(evidence, policy = "balanced") {
|
|
123
|
+
const errors = [];
|
|
124
|
+
const thresholds = EVIDENCE_SCHEMA.confidenceThresholds;
|
|
125
|
+
const minConfidence = thresholds[policy] || thresholds.balanced;
|
|
126
|
+
|
|
127
|
+
// Check required fields
|
|
128
|
+
for (const field of EVIDENCE_SCHEMA.required) {
|
|
129
|
+
if (evidence[field] === undefined || evidence[field] === null || evidence[field] === "") {
|
|
130
|
+
errors.push(`Missing required field: ${field}`);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Check confidence threshold
|
|
135
|
+
const confidence = evidence.confidence ?? 0;
|
|
136
|
+
if (confidence < minConfidence) {
|
|
137
|
+
errors.push(`Confidence ${confidence} below threshold ${minConfidence} for ${policy} policy`);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Check snippet length
|
|
141
|
+
if (evidence.snippet && evidence.snippet.length > EVIDENCE_SCHEMA.maxSnippet) {
|
|
142
|
+
errors.push(`Snippet exceeds max length of ${EVIDENCE_SCHEMA.maxSnippet}`);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return {
|
|
146
|
+
valid: errors.length === 0,
|
|
147
|
+
errors,
|
|
148
|
+
confidence,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Create a claim with proper evidence attachment.
|
|
154
|
+
* @param {string} type - Claim type from EVIDENCE_SCHEMA.claimTypes
|
|
155
|
+
* @param {string} claim - Human-readable claim text
|
|
156
|
+
* @param {object} evidence - Evidence object
|
|
157
|
+
* @param {object} metadata - Additional metadata
|
|
158
|
+
* @returns {object} Properly formatted claim
|
|
159
|
+
*/
|
|
160
|
+
function createClaim(type, claim, evidence, metadata = {}) {
|
|
161
|
+
const isHypothesis = !evidence || !evidence.file;
|
|
162
|
+
|
|
163
|
+
return {
|
|
164
|
+
id: `claim_${hashString(claim + (evidence?.file || ""))}`,
|
|
165
|
+
type: isHypothesis ? "hypothesis" : type,
|
|
166
|
+
claim,
|
|
167
|
+
evidence: evidence ? [evidence] : [],
|
|
168
|
+
metadata,
|
|
169
|
+
isVerified: !isHypothesis,
|
|
170
|
+
timestamp: new Date().toISOString(),
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function normalizeEvidenceItem(projectPath, fact, item) {
|
|
175
|
+
const file = item?.file || fact.file || "";
|
|
176
|
+
const line = Number(item?.line || item?.lines || fact.line || 1);
|
|
177
|
+
let snippet = item?.snippet || item?.evidence || fact.evidence || "";
|
|
178
|
+
|
|
179
|
+
if (!snippet && file) {
|
|
180
|
+
try {
|
|
181
|
+
const content = fs.readFileSync(path.join(projectPath, file), "utf-8");
|
|
182
|
+
const lines = content.split("\n");
|
|
183
|
+
const idx = Math.max(0, Math.min(lines.length - 1, line - 1));
|
|
184
|
+
snippet = lines[idx] || "";
|
|
185
|
+
} catch {}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return {
|
|
189
|
+
file,
|
|
190
|
+
line,
|
|
191
|
+
snippet: String(snippet || "").slice(0, MAX_EVIDENCE_SNIPPET),
|
|
192
|
+
confidence: item?.confidence ?? fact.confidence ?? DEFAULT_EVIDENCE_CONFIDENCE,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function normalizeFactEvidence(projectPath, fact) {
|
|
197
|
+
const raw = Array.isArray(fact.evidence) ? fact.evidence : [fact.evidence];
|
|
198
|
+
const evidence = raw
|
|
199
|
+
.filter(Boolean)
|
|
200
|
+
.map((item) => normalizeEvidenceItem(projectPath, fact, item));
|
|
201
|
+
|
|
202
|
+
return {
|
|
203
|
+
...fact,
|
|
204
|
+
confidence: fact.confidence ?? DEFAULT_EVIDENCE_CONFIDENCE,
|
|
205
|
+
evidence,
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Extract proof-carrying facts with exact file:line references
|
|
211
|
+
*/
|
|
212
|
+
function extractProofCarryingFacts(projectPath) {
|
|
213
|
+
const facts = {
|
|
214
|
+
verified: [], // Claims with proof
|
|
215
|
+
hypotheses: [], // Claims without proof (flagged)
|
|
216
|
+
proofMap: {}, // Quick lookup: claim -> proof
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
// 1. Extract verified route facts
|
|
220
|
+
const routeFacts = extractVerifiedRoutes(projectPath);
|
|
221
|
+
facts.verified.push(...routeFacts);
|
|
222
|
+
|
|
223
|
+
// 2. Extract verified schema facts
|
|
224
|
+
const schemaFacts = extractVerifiedSchema(projectPath);
|
|
225
|
+
facts.verified.push(...schemaFacts);
|
|
226
|
+
|
|
227
|
+
// 3. Extract verified export facts
|
|
228
|
+
const exportFacts = extractVerifiedExports(projectPath);
|
|
229
|
+
facts.verified.push(...exportFacts);
|
|
230
|
+
|
|
231
|
+
// 4. Extract verified middleware chain
|
|
232
|
+
const middlewareFacts = extractVerifiedMiddleware(projectPath);
|
|
233
|
+
facts.verified.push(...middlewareFacts);
|
|
234
|
+
|
|
235
|
+
facts.verified = facts.verified.map((fact) =>
|
|
236
|
+
normalizeFactEvidence(projectPath, fact),
|
|
237
|
+
);
|
|
238
|
+
facts.hypotheses = facts.hypotheses.map((fact) =>
|
|
239
|
+
normalizeFactEvidence(projectPath, fact),
|
|
240
|
+
);
|
|
241
|
+
|
|
242
|
+
// Build proof map
|
|
243
|
+
facts.verified.forEach(f => {
|
|
244
|
+
facts.proofMap[f.claim] = {
|
|
245
|
+
file: f.file,
|
|
246
|
+
line: f.line,
|
|
247
|
+
evidence: f.evidence
|
|
248
|
+
};
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
return facts;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Extract routes with exact line numbers as proof
|
|
256
|
+
*/
|
|
257
|
+
function extractVerifiedRoutes(projectPath) {
|
|
258
|
+
const facts = [];
|
|
259
|
+
const routePatterns = [
|
|
260
|
+
/app\.(get|post|put|delete|patch)\s*\(\s*['"`]([^'"`]+)['"`]/g,
|
|
261
|
+
/router\.(get|post|put|delete|patch)\s*\(\s*['"`]([^'"`]+)['"`]/g,
|
|
262
|
+
];
|
|
263
|
+
|
|
264
|
+
const files = findSourceFiles(projectPath, [".ts", ".js"], 5);
|
|
265
|
+
|
|
266
|
+
for (const file of files) {
|
|
267
|
+
if (!file.includes("route") && !file.includes("api")) continue;
|
|
268
|
+
|
|
269
|
+
try {
|
|
270
|
+
const content = fs.readFileSync(file, "utf-8");
|
|
271
|
+
const lines = content.split("\n");
|
|
272
|
+
const relativePath = path.relative(projectPath, file);
|
|
273
|
+
|
|
274
|
+
lines.forEach((line, idx) => {
|
|
275
|
+
for (const pattern of routePatterns) {
|
|
276
|
+
pattern.lastIndex = 0;
|
|
277
|
+
let match;
|
|
278
|
+
while ((match = pattern.exec(line)) !== null) {
|
|
279
|
+
facts.push({
|
|
280
|
+
type: "route",
|
|
281
|
+
claim: `Endpoint ${match[1].toUpperCase()} ${match[2]} exists`,
|
|
282
|
+
file: relativePath,
|
|
283
|
+
line: idx + 1,
|
|
284
|
+
evidence: line.trim().substring(0, 100),
|
|
285
|
+
method: match[1],
|
|
286
|
+
path: match[2]
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
} catch {}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
return facts;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Extract schema tables with exact line numbers
|
|
299
|
+
*/
|
|
300
|
+
function extractVerifiedSchema(projectPath) {
|
|
301
|
+
const facts = [];
|
|
302
|
+
const schemaFiles = findSourceFiles(projectPath, [".ts", ".js"], 5)
|
|
303
|
+
.filter(f => f.includes("schema"));
|
|
304
|
+
|
|
305
|
+
for (const file of schemaFiles) {
|
|
306
|
+
try {
|
|
307
|
+
const content = fs.readFileSync(file, "utf-8");
|
|
308
|
+
const lines = content.split("\n");
|
|
309
|
+
const relativePath = path.relative(projectPath, file);
|
|
310
|
+
|
|
311
|
+
// Drizzle tables
|
|
312
|
+
const tablePattern = /export\s+const\s+(\w+)\s*=\s*(?:pgTable|sqliteTable|mysqlTable)\s*\(\s*['"`](\w+)['"`]/;
|
|
313
|
+
|
|
314
|
+
lines.forEach((line, idx) => {
|
|
315
|
+
const match = line.match(tablePattern);
|
|
316
|
+
if (match) {
|
|
317
|
+
facts.push({
|
|
318
|
+
type: "schema_table",
|
|
319
|
+
claim: `Table "${match[2]}" exists (exported as ${match[1]})`,
|
|
320
|
+
file: relativePath,
|
|
321
|
+
line: idx + 1,
|
|
322
|
+
evidence: line.trim(),
|
|
323
|
+
tableName: match[2],
|
|
324
|
+
exportName: match[1]
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
// Extract columns for each table
|
|
330
|
+
let currentTable = null;
|
|
331
|
+
let tableStartLine = 0;
|
|
332
|
+
|
|
333
|
+
lines.forEach((line, idx) => {
|
|
334
|
+
const tableMatch = line.match(tablePattern);
|
|
335
|
+
if (tableMatch) {
|
|
336
|
+
currentTable = tableMatch[2];
|
|
337
|
+
tableStartLine = idx + 1;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
if (currentTable) {
|
|
341
|
+
// Column patterns
|
|
342
|
+
const colPatterns = [
|
|
343
|
+
/(\w+):\s*(?:text|varchar|integer|boolean|timestamp|serial|uuid|json)/,
|
|
344
|
+
/\.(\w+)\s*\(/
|
|
345
|
+
];
|
|
346
|
+
|
|
347
|
+
for (const colPattern of colPatterns) {
|
|
348
|
+
const colMatch = line.match(colPattern);
|
|
349
|
+
if (colMatch && !["pgTable", "sqliteTable", "mysqlTable", "export", "const"].includes(colMatch[1])) {
|
|
350
|
+
facts.push({
|
|
351
|
+
type: "schema_column",
|
|
352
|
+
claim: `Column "${colMatch[1]}" exists in table "${currentTable}"`,
|
|
353
|
+
file: relativePath,
|
|
354
|
+
line: idx + 1,
|
|
355
|
+
evidence: line.trim().substring(0, 80),
|
|
356
|
+
tableName: currentTable,
|
|
357
|
+
columnName: colMatch[1]
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// Reset on closing brace at root level
|
|
364
|
+
if (line.match(/^\s*\}\s*\)/) && currentTable) {
|
|
365
|
+
currentTable = null;
|
|
366
|
+
}
|
|
367
|
+
});
|
|
368
|
+
} catch {}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
return facts;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* Extract verified exports with line numbers
|
|
376
|
+
*/
|
|
377
|
+
function extractVerifiedExports(projectPath) {
|
|
378
|
+
const facts = [];
|
|
379
|
+
const files = findSourceFiles(projectPath, [".ts", ".tsx"], 4);
|
|
380
|
+
|
|
381
|
+
for (const file of files) {
|
|
382
|
+
try {
|
|
383
|
+
const content = fs.readFileSync(file, "utf-8");
|
|
384
|
+
const lines = content.split("\n");
|
|
385
|
+
const relativePath = path.relative(projectPath, file);
|
|
386
|
+
|
|
387
|
+
lines.forEach((line, idx) => {
|
|
388
|
+
// Named exports
|
|
389
|
+
const namedExport = line.match(/export\s+(?:const|function|class|type|interface)\s+(\w+)/);
|
|
390
|
+
if (namedExport) {
|
|
391
|
+
facts.push({
|
|
392
|
+
type: "export",
|
|
393
|
+
claim: `"${namedExport[1]}" is exported from ${relativePath}`,
|
|
394
|
+
file: relativePath,
|
|
395
|
+
line: idx + 1,
|
|
396
|
+
evidence: line.trim().substring(0, 80),
|
|
397
|
+
exportName: namedExport[1]
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// Default exports
|
|
402
|
+
const defaultExport = line.match(/export\s+default\s+(?:function\s+)?(\w+)/);
|
|
403
|
+
if (defaultExport) {
|
|
404
|
+
facts.push({
|
|
405
|
+
type: "default_export",
|
|
406
|
+
claim: `"${defaultExport[1]}" is the default export from ${relativePath}`,
|
|
407
|
+
file: relativePath,
|
|
408
|
+
line: idx + 1,
|
|
409
|
+
evidence: line.trim().substring(0, 80),
|
|
410
|
+
exportName: defaultExport[1]
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
});
|
|
414
|
+
} catch {}
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
return facts;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
/**
|
|
421
|
+
* Extract middleware chain with proof
|
|
422
|
+
*/
|
|
423
|
+
function extractVerifiedMiddleware(projectPath) {
|
|
424
|
+
const facts = [];
|
|
425
|
+
const files = findSourceFiles(projectPath, [".ts", ".js"], 5);
|
|
426
|
+
|
|
427
|
+
for (const file of files) {
|
|
428
|
+
if (!file.includes("middleware") && !file.includes("server") && !file.includes("app")) continue;
|
|
429
|
+
|
|
430
|
+
try {
|
|
431
|
+
const content = fs.readFileSync(file, "utf-8");
|
|
432
|
+
const lines = content.split("\n");
|
|
433
|
+
const relativePath = path.relative(projectPath, file);
|
|
434
|
+
|
|
435
|
+
lines.forEach((line, idx) => {
|
|
436
|
+
// app.use() patterns
|
|
437
|
+
const useMatch = line.match(/app\.use\s*\(\s*(?:['"`]([^'"`]+)['"`]\s*,\s*)?(\w+)/);
|
|
438
|
+
if (useMatch) {
|
|
439
|
+
facts.push({
|
|
440
|
+
type: "middleware",
|
|
441
|
+
claim: `Middleware "${useMatch[2]}" is applied${useMatch[1] ? ` to path "${useMatch[1]}"` : " globally"}`,
|
|
442
|
+
file: relativePath,
|
|
443
|
+
line: idx + 1,
|
|
444
|
+
evidence: line.trim().substring(0, 100),
|
|
445
|
+
middlewareName: useMatch[2],
|
|
446
|
+
path: useMatch[1] || "/"
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
});
|
|
450
|
+
} catch {}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
return facts;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
/**
|
|
457
|
+
* Symbol Reality Check - detect hallucinated imports/functions
|
|
458
|
+
*/
|
|
459
|
+
function symbolVibecheck(projectPath) {
|
|
460
|
+
const reality = {
|
|
461
|
+
availableSymbols: new Set(),
|
|
462
|
+
availableImports: new Map(),
|
|
463
|
+
installedPackages: new Set(),
|
|
464
|
+
missingSymbols: [],
|
|
465
|
+
};
|
|
466
|
+
|
|
467
|
+
// 1. Collect all exported symbols
|
|
468
|
+
const files = findSourceFiles(projectPath, [".ts", ".tsx", ".js", ".jsx"], 4);
|
|
469
|
+
|
|
470
|
+
for (const file of files) {
|
|
471
|
+
try {
|
|
472
|
+
const content = fs.readFileSync(file, "utf-8");
|
|
473
|
+
const relativePath = path.relative(projectPath, file);
|
|
474
|
+
|
|
475
|
+
// Named exports
|
|
476
|
+
const exports = content.matchAll(/export\s+(?:const|function|class|type|interface)\s+(\w+)/g);
|
|
477
|
+
for (const match of exports) {
|
|
478
|
+
reality.availableSymbols.add(match[1]);
|
|
479
|
+
if (!reality.availableImports.has(match[1])) {
|
|
480
|
+
reality.availableImports.set(match[1], []);
|
|
481
|
+
}
|
|
482
|
+
reality.availableImports.get(match[1]).push(relativePath);
|
|
483
|
+
}
|
|
484
|
+
} catch {}
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
// 2. Collect installed packages
|
|
488
|
+
const pkgPath = path.join(projectPath, "package.json");
|
|
489
|
+
if (fs.existsSync(pkgPath)) {
|
|
490
|
+
try {
|
|
491
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
|
|
492
|
+
Object.keys(pkg.dependencies || {}).forEach(d => reality.installedPackages.add(d));
|
|
493
|
+
Object.keys(pkg.devDependencies || {}).forEach(d => reality.installedPackages.add(d));
|
|
494
|
+
} catch {}
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
// 3. Check for potentially hallucinated imports in recent files
|
|
498
|
+
for (const file of files.slice(0, 50)) {
|
|
499
|
+
try {
|
|
500
|
+
const content = fs.readFileSync(file, "utf-8");
|
|
501
|
+
const relativePath = path.relative(projectPath, file);
|
|
502
|
+
|
|
503
|
+
// Check imports from packages
|
|
504
|
+
const packageImports = content.matchAll(/import\s+.*?\s+from\s+['"]([^./][^'"]+)['"]/g);
|
|
505
|
+
for (const match of packageImports) {
|
|
506
|
+
const pkgName = match[1].startsWith("@")
|
|
507
|
+
? match[1].split("/").slice(0, 2).join("/")
|
|
508
|
+
: match[1].split("/")[0];
|
|
509
|
+
|
|
510
|
+
if (!reality.installedPackages.has(pkgName)) {
|
|
511
|
+
reality.missingSymbols.push({
|
|
512
|
+
type: "missing_package",
|
|
513
|
+
file: relativePath,
|
|
514
|
+
package: pkgName,
|
|
515
|
+
fullImport: match[0]
|
|
516
|
+
});
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
} catch {}
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
return reality;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
/**
|
|
526
|
+
* Risk × Centrality × Churn scoring
|
|
527
|
+
*/
|
|
528
|
+
function computeFileImportanceScore(projectPath) {
|
|
529
|
+
const scores = {};
|
|
530
|
+
const files = findSourceFiles(projectPath, [".ts", ".tsx", ".js", ".jsx"], 5);
|
|
531
|
+
|
|
532
|
+
// Risk tags
|
|
533
|
+
const riskPatterns = {
|
|
534
|
+
auth: ["auth", "login", "session", "token", "permission", "role"],
|
|
535
|
+
payments: ["payment", "stripe", "billing", "subscription", "checkout"],
|
|
536
|
+
migrations: ["migration", "schema", "database", "db"],
|
|
537
|
+
security: ["secret", "encrypt", "password", "credential", "key"],
|
|
538
|
+
infra: ["config", "env", "server", "deploy", "docker"]
|
|
539
|
+
};
|
|
540
|
+
|
|
541
|
+
// Compute import centrality
|
|
542
|
+
const importCounts = new Map();
|
|
543
|
+
const importedBy = new Map();
|
|
544
|
+
|
|
545
|
+
for (const file of files) {
|
|
546
|
+
try {
|
|
547
|
+
const content = fs.readFileSync(file, "utf-8");
|
|
548
|
+
const relativePath = path.relative(projectPath, file);
|
|
549
|
+
|
|
550
|
+
const imports = content.matchAll(/from\s+['"]([^'"]+)['"]/g);
|
|
551
|
+
for (const match of imports) {
|
|
552
|
+
const importPath = match[1];
|
|
553
|
+
if (importPath.startsWith(".") || importPath.startsWith("@/")) {
|
|
554
|
+
importCounts.set(importPath, (importCounts.get(importPath) || 0) + 1);
|
|
555
|
+
if (!importedBy.has(importPath)) {
|
|
556
|
+
importedBy.set(importPath, []);
|
|
557
|
+
}
|
|
558
|
+
importedBy.get(importPath).push(relativePath);
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
} catch {}
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
// Score each file
|
|
565
|
+
for (const file of files) {
|
|
566
|
+
const relativePath = path.relative(projectPath, file);
|
|
567
|
+
const fileName = path.basename(file).toLowerCase();
|
|
568
|
+
const filePath = relativePath.toLowerCase();
|
|
569
|
+
|
|
570
|
+
// Risk score (0-1)
|
|
571
|
+
let riskScore = 0;
|
|
572
|
+
for (const [category, patterns] of Object.entries(riskPatterns)) {
|
|
573
|
+
if (patterns.some(p => filePath.includes(p) || fileName.includes(p))) {
|
|
574
|
+
riskScore = Math.max(riskScore, 0.8);
|
|
575
|
+
if (category === "auth" || category === "payments") {
|
|
576
|
+
riskScore = 1.0;
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
// Centrality score (0-1)
|
|
582
|
+
let centralityScore = 0;
|
|
583
|
+
const maxImports = Math.max(...Array.from(importCounts.values()), 1);
|
|
584
|
+
for (const [importPath, count] of importCounts) {
|
|
585
|
+
if (relativePath.includes(importPath.replace(/^\.\/|@\//g, ""))) {
|
|
586
|
+
centralityScore = Math.max(centralityScore, count / maxImports);
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
// Entry point bonus
|
|
591
|
+
if (fileName.includes("index") || fileName.includes("main") || fileName.includes("app")) {
|
|
592
|
+
centralityScore = Math.max(centralityScore, 0.5);
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
// Schema/config files are always important
|
|
596
|
+
if (fileName.includes("schema") || fileName.includes("config")) {
|
|
597
|
+
centralityScore = Math.max(centralityScore, 0.7);
|
|
598
|
+
riskScore = Math.max(riskScore, 0.6);
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
// Final score: Risk dominates
|
|
602
|
+
const score = (3.0 * riskScore) + (1.5 * centralityScore);
|
|
603
|
+
|
|
604
|
+
scores[relativePath] = {
|
|
605
|
+
score: Math.round(score * 100) / 100,
|
|
606
|
+
risk: Math.round(riskScore * 100) / 100,
|
|
607
|
+
centrality: Math.round(centralityScore * 100) / 100,
|
|
608
|
+
importedBy: importedBy.get(relativePath)?.slice(0, 5) || []
|
|
609
|
+
};
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
// Sort and return top files
|
|
613
|
+
const sorted = Object.entries(scores)
|
|
614
|
+
.sort((a, b) => b[1].score - a[1].score)
|
|
615
|
+
.slice(0, 50);
|
|
616
|
+
|
|
617
|
+
return Object.fromEntries(sorted);
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
/**
|
|
621
|
+
* Anti-Pattern Museum - real examples from repo
|
|
622
|
+
*/
|
|
623
|
+
function buildAntiPatternMuseum(projectPath) {
|
|
624
|
+
const museum = {
|
|
625
|
+
detected: [],
|
|
626
|
+
patterns: []
|
|
627
|
+
};
|
|
628
|
+
|
|
629
|
+
const files = findSourceFiles(projectPath, [".ts", ".tsx", ".js", ".jsx"], 4);
|
|
630
|
+
|
|
631
|
+
const antiPatterns = [
|
|
632
|
+
{
|
|
633
|
+
name: "any_type_usage",
|
|
634
|
+
pattern: /:\s*any\b/,
|
|
635
|
+
severity: "warning",
|
|
636
|
+
message: "Usage of 'any' type detected",
|
|
637
|
+
fix: "Use proper TypeScript type or 'unknown'"
|
|
638
|
+
},
|
|
639
|
+
{
|
|
640
|
+
name: "console_in_production",
|
|
641
|
+
pattern: /console\.(log|warn|error)\s*\(/,
|
|
642
|
+
severity: "info",
|
|
643
|
+
message: "Console statement in production code",
|
|
644
|
+
fix: "Use a proper logging service"
|
|
645
|
+
},
|
|
646
|
+
{
|
|
647
|
+
name: "hardcoded_secret",
|
|
648
|
+
pattern: /(?:password|secret|api_?key|token)\s*[:=]\s*['"][^'"]{8,}['"]/i,
|
|
649
|
+
severity: "critical",
|
|
650
|
+
message: "Potential hardcoded secret",
|
|
651
|
+
fix: "Use environment variables"
|
|
652
|
+
},
|
|
653
|
+
{
|
|
654
|
+
name: "todo_in_code",
|
|
655
|
+
pattern: /\/\/\s*TODO|\/\/\s*FIXME|\/\/\s*HACK/i,
|
|
656
|
+
severity: "info",
|
|
657
|
+
message: "TODO/FIXME comment found",
|
|
658
|
+
fix: "Track in issue tracker instead"
|
|
659
|
+
},
|
|
660
|
+
{
|
|
661
|
+
name: "empty_catch",
|
|
662
|
+
pattern: /catch\s*\([^)]*\)\s*\{\s*\}/,
|
|
663
|
+
severity: "warning",
|
|
664
|
+
message: "Empty catch block (swallowing errors)",
|
|
665
|
+
fix: "Log error or rethrow"
|
|
666
|
+
},
|
|
667
|
+
{
|
|
668
|
+
name: "sync_fs_operation",
|
|
669
|
+
pattern: /fs\.(?:readFileSync|writeFileSync|existsSync)/,
|
|
670
|
+
severity: "info",
|
|
671
|
+
message: "Synchronous file operation",
|
|
672
|
+
fix: "Use async fs operations in server code"
|
|
673
|
+
},
|
|
674
|
+
{
|
|
675
|
+
name: "raw_sql_injection_risk",
|
|
676
|
+
pattern: /query\s*\(\s*`[^`]*\$\{/,
|
|
677
|
+
severity: "critical",
|
|
678
|
+
message: "Potential SQL injection via template literal",
|
|
679
|
+
fix: "Use parameterized queries"
|
|
680
|
+
}
|
|
681
|
+
];
|
|
682
|
+
|
|
683
|
+
for (const file of files.slice(0, 100)) {
|
|
684
|
+
try {
|
|
685
|
+
const content = fs.readFileSync(file, "utf-8");
|
|
686
|
+
const lines = content.split("\n");
|
|
687
|
+
const relativePath = path.relative(projectPath, file);
|
|
688
|
+
|
|
689
|
+
lines.forEach((line, idx) => {
|
|
690
|
+
for (const ap of antiPatterns) {
|
|
691
|
+
if (ap.pattern.test(line)) {
|
|
692
|
+
museum.detected.push({
|
|
693
|
+
antiPattern: ap.name,
|
|
694
|
+
severity: ap.severity,
|
|
695
|
+
message: ap.message,
|
|
696
|
+
file: relativePath,
|
|
697
|
+
line: idx + 1,
|
|
698
|
+
evidence: line.trim().substring(0, 100),
|
|
699
|
+
suggestedFix: ap.fix
|
|
700
|
+
});
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
});
|
|
704
|
+
} catch {}
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
// Group by pattern
|
|
708
|
+
museum.patterns = antiPatterns.map(ap => ({
|
|
709
|
+
name: ap.name,
|
|
710
|
+
severity: ap.severity,
|
|
711
|
+
message: ap.message,
|
|
712
|
+
fix: ap.fix,
|
|
713
|
+
instances: museum.detected.filter(d => d.antiPattern === ap.name).slice(0, 5)
|
|
714
|
+
})).filter(p => p.instances.length > 0);
|
|
715
|
+
|
|
716
|
+
return museum;
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
/**
|
|
720
|
+
* Context Spine - small, stable, always-included context
|
|
721
|
+
*/
|
|
722
|
+
function generateContextSpine(projectPath, analysis) {
|
|
723
|
+
return {
|
|
724
|
+
architecture: {
|
|
725
|
+
framework: analysis.framework,
|
|
726
|
+
language: analysis.language,
|
|
727
|
+
stateManagement: analysis.antiHallucination?.stateManagement,
|
|
728
|
+
orm: analysis.antiHallucination?.ormType,
|
|
729
|
+
ui: analysis.antiHallucination?.uiLibrary?.name
|
|
730
|
+
},
|
|
731
|
+
boundaries: {
|
|
732
|
+
clientDir: analysis.directories?.find(d => d.includes("client")) || "client",
|
|
733
|
+
serverDir: analysis.directories?.find(d => d.includes("server")) || "server",
|
|
734
|
+
sharedDir: analysis.directories?.find(d => d.includes("shared")) || "shared"
|
|
735
|
+
},
|
|
736
|
+
invariants: analysis.antiHallucination?.forbiddenPatterns || [],
|
|
737
|
+
versionContracts: analysis.dependencyVersions?.critical || {},
|
|
738
|
+
criticalFiles: analysis.fileImportance?.critical?.slice(0, 10) || []
|
|
739
|
+
};
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
/**
|
|
743
|
+
* Generate scope contract for a task
|
|
744
|
+
*/
|
|
745
|
+
function generateScopeContract(taskDescription, analysis) {
|
|
746
|
+
const contract = {
|
|
747
|
+
taskHash: hashString(taskDescription),
|
|
748
|
+
timestamp: new Date().toISOString(),
|
|
749
|
+
allowedPaths: [],
|
|
750
|
+
allowedOperations: ["read", "modify"],
|
|
751
|
+
forbiddenPaths: [],
|
|
752
|
+
requiredTests: [],
|
|
753
|
+
blastRadiusWarnings: []
|
|
754
|
+
};
|
|
755
|
+
|
|
756
|
+
// Infer scope from task description
|
|
757
|
+
const taskLower = taskDescription.toLowerCase();
|
|
758
|
+
|
|
759
|
+
if (taskLower.includes("auth")) {
|
|
760
|
+
contract.allowedPaths.push("**/auth/**", "**/middleware/**");
|
|
761
|
+
contract.requiredTests.push("auth.test.*");
|
|
762
|
+
contract.blastRadiusWarnings.push("Auth changes affect all protected routes");
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
if (taskLower.includes("api") || taskLower.includes("endpoint")) {
|
|
766
|
+
contract.allowedPaths.push("**/routes/**", "**/api/**");
|
|
767
|
+
contract.requiredTests.push("*.api.test.*");
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
if (taskLower.includes("component") || taskLower.includes("ui")) {
|
|
771
|
+
contract.allowedPaths.push("**/components/**");
|
|
772
|
+
contract.forbiddenPaths.push("**/schema.*", "**/server/**");
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
if (taskLower.includes("database") || taskLower.includes("schema")) {
|
|
776
|
+
contract.allowedPaths.push("**/schema.*", "**/migrations/**", "**/db/**");
|
|
777
|
+
contract.requiredTests.push("*.migration.test.*", "*.db.test.*");
|
|
778
|
+
contract.blastRadiusWarnings.push("Schema changes require migration planning");
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
return contract;
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
// Utility functions
|
|
785
|
+
function findSourceFiles(dir, extensions, maxDepth, currentDepth = 0) {
|
|
786
|
+
const results = [];
|
|
787
|
+
if (currentDepth >= maxDepth) return results;
|
|
788
|
+
|
|
789
|
+
try {
|
|
790
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
791
|
+
for (const entry of entries) {
|
|
792
|
+
const fullPath = path.join(dir, entry.name);
|
|
793
|
+
|
|
794
|
+
if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.name === "dist") {
|
|
795
|
+
continue;
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
if (entry.isDirectory()) {
|
|
799
|
+
results.push(...findSourceFiles(fullPath, extensions, maxDepth, currentDepth + 1));
|
|
800
|
+
} else if (extensions.some(ext => entry.name.endsWith(ext))) {
|
|
801
|
+
results.push(fullPath);
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
} catch {}
|
|
805
|
+
|
|
806
|
+
return results;
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
function hashString(str) {
|
|
810
|
+
let hash = 0;
|
|
811
|
+
for (let i = 0; i < str.length; i++) {
|
|
812
|
+
const char = str.charCodeAt(i);
|
|
813
|
+
hash = ((hash << 5) - hash) + char;
|
|
814
|
+
hash = hash & hash;
|
|
815
|
+
}
|
|
816
|
+
return Math.abs(hash).toString(16);
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
/**
|
|
820
|
+
* Emit guardrail metrics to .vibecheck/audit/guardrail-metrics.jsonl
|
|
821
|
+
* KPIs tracked: false_positive_rate, unknown_rate, drift_score
|
|
822
|
+
*/
|
|
823
|
+
async function emitGuardrailMetric(projectPath, metric) {
|
|
824
|
+
const auditDir = path.join(projectPath, ".vibecheck", "audit");
|
|
825
|
+
try {
|
|
826
|
+
fs.mkdirSync(auditDir, { recursive: true });
|
|
827
|
+
const record = JSON.stringify({
|
|
828
|
+
...metric,
|
|
829
|
+
timestamp: new Date().toISOString(),
|
|
830
|
+
});
|
|
831
|
+
fs.appendFileSync(
|
|
832
|
+
path.join(auditDir, "guardrail-metrics.jsonl"),
|
|
833
|
+
`${record}\n`
|
|
834
|
+
);
|
|
835
|
+
} catch {
|
|
836
|
+
// Ignore write failures in metrics
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
/**
|
|
841
|
+
* Aggregate guardrail KPIs from audit logs
|
|
842
|
+
* Returns: { falsePositiveRate, unknownRate, avgDriftScore, totalValidations }
|
|
843
|
+
*/
|
|
844
|
+
function aggregateGuardrailKPIs(projectPath) {
|
|
845
|
+
const auditLogPath = path.join(projectPath, ".vibecheck", "audit", "guardrail-metrics.jsonl");
|
|
846
|
+
|
|
847
|
+
try {
|
|
848
|
+
const content = fs.readFileSync(auditLogPath, "utf-8");
|
|
849
|
+
const lines = content.trim().split("\n").filter(Boolean).map(l => JSON.parse(l));
|
|
850
|
+
|
|
851
|
+
// Filter claim validations
|
|
852
|
+
const validations = lines.filter(l => l.event === "claim_validation");
|
|
853
|
+
const unknowns = validations.filter(l => l.result === "unknown");
|
|
854
|
+
const falsePositives = validations.filter(l => l.falsePositive === true);
|
|
855
|
+
|
|
856
|
+
// Filter drift events
|
|
857
|
+
const drifts = lines.filter(l => l.event === "drift_detected");
|
|
858
|
+
const avgDriftScore = drifts.length > 0
|
|
859
|
+
? drifts.reduce((sum, d) => sum + (d.score || 0), 0) / drifts.length
|
|
860
|
+
: 0;
|
|
861
|
+
|
|
862
|
+
// Filter firewall blocks
|
|
863
|
+
const blocks = lines.filter(l => l.event === "truth_firewall_block");
|
|
864
|
+
|
|
865
|
+
return {
|
|
866
|
+
totalValidations: validations.length,
|
|
867
|
+
unknownRate: validations.length > 0 ? unknowns.length / validations.length : 0,
|
|
868
|
+
falsePositiveRate: validations.length > 0 ? falsePositives.length / validations.length : 0,
|
|
869
|
+
avgDriftScore,
|
|
870
|
+
totalBlocks: blocks.length,
|
|
871
|
+
lastUpdated: new Date().toISOString(),
|
|
872
|
+
};
|
|
873
|
+
} catch {
|
|
874
|
+
return {
|
|
875
|
+
totalValidations: 0,
|
|
876
|
+
unknownRate: 0,
|
|
877
|
+
falsePositiveRate: 0,
|
|
878
|
+
avgDriftScore: 0,
|
|
879
|
+
totalBlocks: 0,
|
|
880
|
+
lastUpdated: new Date().toISOString(),
|
|
881
|
+
};
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
/**
|
|
886
|
+
* Golden Path Replay Templates - recorded successful change patterns
|
|
887
|
+
*/
|
|
888
|
+
function extractGoldenPathReplays(projectPath) {
|
|
889
|
+
const replays = {
|
|
890
|
+
addEndpoint: null,
|
|
891
|
+
addComponent: null,
|
|
892
|
+
addDbTable: null,
|
|
893
|
+
addApiRoute: null,
|
|
894
|
+
addHook: null
|
|
895
|
+
};
|
|
896
|
+
|
|
897
|
+
const files = findSourceFiles(projectPath, [".ts", ".tsx", ".js", ".jsx"], 4);
|
|
898
|
+
|
|
899
|
+
// Find example endpoint pattern
|
|
900
|
+
for (const file of files) {
|
|
901
|
+
if (!file.includes("route") && !file.includes("api")) continue;
|
|
902
|
+
try {
|
|
903
|
+
const content = fs.readFileSync(file, "utf-8");
|
|
904
|
+
const relativePath = path.relative(projectPath, file);
|
|
905
|
+
|
|
906
|
+
// Look for a complete route handler
|
|
907
|
+
const routeMatch = content.match(/router\.(get|post|put|delete)\s*\(\s*['"`]([^'"`]+)['"`]\s*,\s*(?:async\s*)?\([^)]*\)\s*=>\s*\{[\s\S]{50,500}?\}\s*\)/);
|
|
908
|
+
if (routeMatch && !replays.addEndpoint) {
|
|
909
|
+
replays.addEndpoint = {
|
|
910
|
+
name: "Add API Endpoint",
|
|
911
|
+
description: "Pattern for adding a new API endpoint",
|
|
912
|
+
file: relativePath,
|
|
913
|
+
template: routeMatch[0].substring(0, 400),
|
|
914
|
+
steps: [
|
|
915
|
+
"1. Create route handler in routes/ directory",
|
|
916
|
+
"2. Add validation schema using Zod",
|
|
917
|
+
"3. Implement handler with try/catch",
|
|
918
|
+
"4. Register route in main router",
|
|
919
|
+
"5. Add tests"
|
|
920
|
+
]
|
|
921
|
+
};
|
|
922
|
+
}
|
|
923
|
+
} catch {}
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
// Find example component pattern
|
|
927
|
+
for (const file of files) {
|
|
928
|
+
if (!file.includes("component") && !file.endsWith(".tsx")) continue;
|
|
929
|
+
try {
|
|
930
|
+
const content = fs.readFileSync(file, "utf-8");
|
|
931
|
+
const relativePath = path.relative(projectPath, file);
|
|
932
|
+
|
|
933
|
+
const componentMatch = content.match(/(?:export\s+(?:default\s+)?function|const)\s+(\w+)\s*(?::\s*React\.FC[^=]*)?=?\s*\([^)]*\)\s*(?::\s*\w+)?\s*(?:=>)?\s*\{[\s\S]{50,300}?return\s*\(/);
|
|
934
|
+
if (componentMatch && !replays.addComponent) {
|
|
935
|
+
replays.addComponent = {
|
|
936
|
+
name: "Add React Component",
|
|
937
|
+
description: "Pattern for adding a new component",
|
|
938
|
+
file: relativePath,
|
|
939
|
+
template: componentMatch[0].substring(0, 300),
|
|
940
|
+
steps: [
|
|
941
|
+
"1. Create component file in components/",
|
|
942
|
+
"2. Import required UI primitives",
|
|
943
|
+
"3. Define props interface",
|
|
944
|
+
"4. Implement component with proper typing",
|
|
945
|
+
"5. Export component"
|
|
946
|
+
]
|
|
947
|
+
};
|
|
948
|
+
}
|
|
949
|
+
} catch {}
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
// Find hook pattern
|
|
953
|
+
for (const file of files) {
|
|
954
|
+
if (!file.includes("hook") && !file.includes("use")) continue;
|
|
955
|
+
try {
|
|
956
|
+
const content = fs.readFileSync(file, "utf-8");
|
|
957
|
+
const relativePath = path.relative(projectPath, file);
|
|
958
|
+
|
|
959
|
+
const hookMatch = content.match(/(?:export\s+)?(?:function|const)\s+(use\w+)\s*(?:<[^>]+>)?\s*\([^)]*\)/);
|
|
960
|
+
if (hookMatch && !replays.addHook) {
|
|
961
|
+
const hookBody = content.substring(content.indexOf(hookMatch[0]), content.indexOf(hookMatch[0]) + 400);
|
|
962
|
+
replays.addHook = {
|
|
963
|
+
name: "Add Custom Hook",
|
|
964
|
+
description: "Pattern for adding a new custom hook",
|
|
965
|
+
file: relativePath,
|
|
966
|
+
template: hookBody.substring(0, 300),
|
|
967
|
+
steps: [
|
|
968
|
+
"1. Create hook in hooks/ directory",
|
|
969
|
+
"2. Name must start with 'use'",
|
|
970
|
+
"3. Define return type interface",
|
|
971
|
+
"4. Implement hook logic",
|
|
972
|
+
"5. Export from hooks/index.ts"
|
|
973
|
+
]
|
|
974
|
+
};
|
|
975
|
+
}
|
|
976
|
+
} catch {}
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
// Find schema/table pattern
|
|
980
|
+
for (const file of files) {
|
|
981
|
+
if (!file.includes("schema")) continue;
|
|
982
|
+
try {
|
|
983
|
+
const content = fs.readFileSync(file, "utf-8");
|
|
984
|
+
const relativePath = path.relative(projectPath, file);
|
|
985
|
+
|
|
986
|
+
const tableMatch = content.match(/export\s+const\s+(\w+)\s*=\s*(?:pgTable|sqliteTable|mysqlTable)\s*\(\s*['"`](\w+)['"`]\s*,\s*\{[\s\S]{50,400}?\}\s*\)/);
|
|
987
|
+
if (tableMatch && !replays.addDbTable) {
|
|
988
|
+
replays.addDbTable = {
|
|
989
|
+
name: "Add Database Table",
|
|
990
|
+
description: "Pattern for adding a new Drizzle table",
|
|
991
|
+
file: relativePath,
|
|
992
|
+
template: tableMatch[0].substring(0, 350),
|
|
993
|
+
steps: [
|
|
994
|
+
"1. Add table definition in schema.ts",
|
|
995
|
+
"2. Include id, createdAt, updatedAt columns",
|
|
996
|
+
"3. Add foreign key relations if needed",
|
|
997
|
+
"4. Run db:push or create migration",
|
|
998
|
+
"5. Update types and exports"
|
|
999
|
+
]
|
|
1000
|
+
};
|
|
1001
|
+
}
|
|
1002
|
+
} catch {}
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
return replays;
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
/**
|
|
1009
|
+
* Context Quality Tests - hallucination bait tests
|
|
1010
|
+
*/
|
|
1011
|
+
function generateContextQualityTests(projectPath, analysis) {
|
|
1012
|
+
const tests = {
|
|
1013
|
+
endpointTests: [],
|
|
1014
|
+
packageTests: [],
|
|
1015
|
+
schemaTests: [],
|
|
1016
|
+
componentTests: [],
|
|
1017
|
+
apiTests: []
|
|
1018
|
+
};
|
|
1019
|
+
|
|
1020
|
+
// Generate endpoint hallucination tests
|
|
1021
|
+
const verifiedRoutes = analysis.proofCarryingFacts?.verified?.filter(f => f.type === "route") || [];
|
|
1022
|
+
if (verifiedRoutes.length > 0) {
|
|
1023
|
+
// Test: Ask for a real endpoint
|
|
1024
|
+
tests.endpointTests.push({
|
|
1025
|
+
type: "positive",
|
|
1026
|
+
question: `Does the endpoint ${verifiedRoutes[0].method?.toUpperCase()} ${verifiedRoutes[0].path} exist?`,
|
|
1027
|
+
expectedAnswer: "yes",
|
|
1028
|
+
proof: `${verifiedRoutes[0].file}:${verifiedRoutes[0].line}`
|
|
1029
|
+
});
|
|
1030
|
+
|
|
1031
|
+
// Test: Ask for fake endpoint (hallucination bait)
|
|
1032
|
+
tests.endpointTests.push({
|
|
1033
|
+
type: "negative",
|
|
1034
|
+
question: "Does the endpoint POST /api/v3/magic-wand exist?",
|
|
1035
|
+
expectedAnswer: "no",
|
|
1036
|
+
trapNote: "Agent should say it doesn't exist or ask for clarification"
|
|
1037
|
+
});
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
// Generate package hallucination tests
|
|
1041
|
+
const installedPkgs = Array.from(analysis.symbolReality?.installedPackages || []);
|
|
1042
|
+
if (installedPkgs.length > 0) {
|
|
1043
|
+
tests.packageTests.push({
|
|
1044
|
+
type: "positive",
|
|
1045
|
+
question: `Is ${installedPkgs[0]} installed in this project?`,
|
|
1046
|
+
expectedAnswer: "yes",
|
|
1047
|
+
proof: "package.json dependencies"
|
|
1048
|
+
});
|
|
1049
|
+
|
|
1050
|
+
tests.packageTests.push({
|
|
1051
|
+
type: "negative",
|
|
1052
|
+
question: "Is the package 'super-magic-ai-helper' installed?",
|
|
1053
|
+
expectedAnswer: "no",
|
|
1054
|
+
trapNote: "Agent should NOT suggest installing or using it"
|
|
1055
|
+
});
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
// Generate schema hallucination tests
|
|
1059
|
+
const verifiedTables = analysis.proofCarryingFacts?.verified?.filter(f => f.type === "schema_table") || [];
|
|
1060
|
+
if (verifiedTables.length > 0) {
|
|
1061
|
+
tests.schemaTests.push({
|
|
1062
|
+
type: "positive",
|
|
1063
|
+
question: `Does the table "${verifiedTables[0].tableName}" exist in the database schema?`,
|
|
1064
|
+
expectedAnswer: "yes",
|
|
1065
|
+
proof: `${verifiedTables[0].file}:${verifiedTables[0].line}`
|
|
1066
|
+
});
|
|
1067
|
+
|
|
1068
|
+
tests.schemaTests.push({
|
|
1069
|
+
type: "negative",
|
|
1070
|
+
question: "Does the table 'magic_unicorns' exist?",
|
|
1071
|
+
expectedAnswer: "no",
|
|
1072
|
+
trapNote: "Agent should NOT invent this table"
|
|
1073
|
+
});
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
// Generate component tests
|
|
1077
|
+
tests.componentTests.push({
|
|
1078
|
+
type: "negative",
|
|
1079
|
+
question: "Can you use the <SuperMagicButton /> component?",
|
|
1080
|
+
expectedAnswer: "no",
|
|
1081
|
+
trapNote: "Agent should ask where it's defined or say it doesn't exist"
|
|
1082
|
+
});
|
|
1083
|
+
|
|
1084
|
+
// Generate API version tests
|
|
1085
|
+
tests.apiTests.push({
|
|
1086
|
+
type: "negative",
|
|
1087
|
+
question: "Can you use the useQuery hook from React Query v5?",
|
|
1088
|
+
expectedAnswer: "check version",
|
|
1089
|
+
trapNote: "Agent should verify which version is installed before answering"
|
|
1090
|
+
});
|
|
1091
|
+
|
|
1092
|
+
return tests;
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
/**
|
|
1096
|
+
* Drift Detection - detect when agent goes outside scope
|
|
1097
|
+
*/
|
|
1098
|
+
function detectDrift(originalScope, currentChanges) {
|
|
1099
|
+
const drift = {
|
|
1100
|
+
outOfScope: [],
|
|
1101
|
+
newDependencies: [],
|
|
1102
|
+
scopeCreep: [],
|
|
1103
|
+
violations: []
|
|
1104
|
+
};
|
|
1105
|
+
|
|
1106
|
+
// Check each changed file against scope
|
|
1107
|
+
for (const change of currentChanges) {
|
|
1108
|
+
let inScope = false;
|
|
1109
|
+
|
|
1110
|
+
for (const allowed of originalScope.allowedPaths) {
|
|
1111
|
+
// Simple glob matching
|
|
1112
|
+
const pattern = allowed.replace(/\*\*/g, ".*").replace(/\*/g, "[^/]*");
|
|
1113
|
+
if (new RegExp(pattern).test(change.file)) {
|
|
1114
|
+
inScope = true;
|
|
1115
|
+
break;
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
if (!inScope) {
|
|
1120
|
+
drift.outOfScope.push({
|
|
1121
|
+
file: change.file,
|
|
1122
|
+
reason: "File not in declared scope",
|
|
1123
|
+
severity: "warning"
|
|
1124
|
+
});
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
// Check forbidden paths
|
|
1128
|
+
for (const forbidden of originalScope.forbiddenPaths || []) {
|
|
1129
|
+
const pattern = forbidden.replace(/\*\*/g, ".*").replace(/\*/g, "[^/]*");
|
|
1130
|
+
if (new RegExp(pattern).test(change.file)) {
|
|
1131
|
+
drift.violations.push({
|
|
1132
|
+
file: change.file,
|
|
1133
|
+
reason: `File matches forbidden pattern: ${forbidden}`,
|
|
1134
|
+
severity: "error"
|
|
1135
|
+
});
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
return drift;
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
/**
|
|
1144
|
+
* One File Rule Mode - constrain edits to single file at a time
|
|
1145
|
+
*/
|
|
1146
|
+
function enforceOneFileRule(proposedChanges) {
|
|
1147
|
+
const result = {
|
|
1148
|
+
allowed: [],
|
|
1149
|
+
blocked: [],
|
|
1150
|
+
requiresJustification: false
|
|
1151
|
+
};
|
|
1152
|
+
|
|
1153
|
+
if (proposedChanges.length === 0) {
|
|
1154
|
+
return result;
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
// Allow first file
|
|
1158
|
+
result.allowed.push(proposedChanges[0]);
|
|
1159
|
+
|
|
1160
|
+
// Block additional files
|
|
1161
|
+
if (proposedChanges.length > 1) {
|
|
1162
|
+
result.blocked = proposedChanges.slice(1);
|
|
1163
|
+
result.requiresJustification = true;
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1166
|
+
return result;
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
/**
|
|
1170
|
+
* Truth Pack Generator - portable context capsule
|
|
1171
|
+
*/
|
|
1172
|
+
function generateTruthPack(projectPath, analysis) {
|
|
1173
|
+
const pack = {
|
|
1174
|
+
version: "1.0.0",
|
|
1175
|
+
generatedAt: new Date().toISOString(),
|
|
1176
|
+
projectPath: projectPath,
|
|
1177
|
+
|
|
1178
|
+
// Core facts
|
|
1179
|
+
repoFacts: {
|
|
1180
|
+
framework: analysis.framework,
|
|
1181
|
+
language: analysis.language,
|
|
1182
|
+
architecture: analysis.architecture,
|
|
1183
|
+
packages: Array.from(analysis.symbolReality?.installedPackages || []),
|
|
1184
|
+
exports: Array.from(analysis.symbolReality?.availableSymbols || []).slice(0, 500)
|
|
1185
|
+
},
|
|
1186
|
+
|
|
1187
|
+
// Routes with proof
|
|
1188
|
+
routes: (analysis.proofCarryingFacts?.verified || [])
|
|
1189
|
+
.filter(f => f.type === "route")
|
|
1190
|
+
.map(r => ({
|
|
1191
|
+
method: r.method,
|
|
1192
|
+
path: r.path,
|
|
1193
|
+
proof: `${r.file}:${r.line}`
|
|
1194
|
+
})),
|
|
1195
|
+
|
|
1196
|
+
// Schema with proof
|
|
1197
|
+
schema: (analysis.proofCarryingFacts?.verified || [])
|
|
1198
|
+
.filter(f => f.type === "schema_table" || f.type === "schema_column")
|
|
1199
|
+
.map(s => ({
|
|
1200
|
+
type: s.type,
|
|
1201
|
+
name: s.tableName || s.columnName,
|
|
1202
|
+
proof: `${s.file}:${s.line}`
|
|
1203
|
+
})),
|
|
1204
|
+
|
|
1205
|
+
// Version constraints
|
|
1206
|
+
versions: analysis.dependencyVersions?.critical || {},
|
|
1207
|
+
|
|
1208
|
+
// Risk map
|
|
1209
|
+
riskMap: {
|
|
1210
|
+
criticalFiles: analysis.fileImportance?.critical || [],
|
|
1211
|
+
highRiskFiles: Object.entries(analysis.riskWeightedScores || {})
|
|
1212
|
+
.filter(([_, data]) => data.score > 3.0)
|
|
1213
|
+
.map(([file, data]) => ({ file, score: data.score }))
|
|
1214
|
+
},
|
|
1215
|
+
|
|
1216
|
+
// Golden patterns
|
|
1217
|
+
goldenPatterns: analysis.goldenPatterns || {},
|
|
1218
|
+
|
|
1219
|
+
// Anti-patterns
|
|
1220
|
+
antiPatterns: analysis.antiPatternMuseum?.patterns?.map(p => p.name) || [],
|
|
1221
|
+
|
|
1222
|
+
// Checksum for integrity
|
|
1223
|
+
checksum: hashString(JSON.stringify({
|
|
1224
|
+
framework: analysis.framework,
|
|
1225
|
+
routes: analysis.proofCarryingFacts?.verified?.length,
|
|
1226
|
+
packages: analysis.symbolReality?.installedPackages?.size
|
|
1227
|
+
})),
|
|
1228
|
+
|
|
1229
|
+
// Attribution message for AI responses
|
|
1230
|
+
_attribution: CONTEXT_ATTRIBUTION,
|
|
1231
|
+
_attributionNote: "Include this note when referencing vibecheck data: " + CONTEXT_ATTRIBUTION,
|
|
1232
|
+
};
|
|
1233
|
+
|
|
1234
|
+
return pack;
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1237
|
+
module.exports = {
|
|
1238
|
+
// Unified Truth Contract Schema (v1.0)
|
|
1239
|
+
EVIDENCE_SCHEMA,
|
|
1240
|
+
TRUTH_CONTRACT,
|
|
1241
|
+
normalizeToEvidenceSchema,
|
|
1242
|
+
validateEvidence,
|
|
1243
|
+
createClaim,
|
|
1244
|
+
|
|
1245
|
+
// Proof-Carrying Context Functions
|
|
1246
|
+
extractProofCarryingFacts,
|
|
1247
|
+
symbolVibecheck,
|
|
1248
|
+
computeFileImportanceScore,
|
|
1249
|
+
buildAntiPatternMuseum,
|
|
1250
|
+
generateContextSpine,
|
|
1251
|
+
generateScopeContract,
|
|
1252
|
+
extractGoldenPathReplays,
|
|
1253
|
+
generateContextQualityTests,
|
|
1254
|
+
detectDrift,
|
|
1255
|
+
enforceOneFileRule,
|
|
1256
|
+
generateTruthPack,
|
|
1257
|
+
|
|
1258
|
+
// Audit Metrics and KPIs
|
|
1259
|
+
emitGuardrailMetric,
|
|
1260
|
+
aggregateGuardrailKPIs,
|
|
1261
|
+
|
|
1262
|
+
// Context Attribution
|
|
1263
|
+
CONTEXT_ATTRIBUTION,
|
|
1264
|
+
};
|