avorelo 0.3.8 → 0.4.0-rc.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +16 -23
- package/README.md +62 -72
- package/bin/avorelo +9 -0
- package/package.json +147 -139
- package/scripts/README.md +40 -0
- package/scripts/avorelo-session-collision-guard.js +540 -0
- package/scripts/avorelo-workstream-preflight.js +918 -0
- package/scripts/cco-dashboard.js +346 -0
- package/scripts/cco-posttooluse.js +504 -0
- package/scripts/cco-pretooluse.js +381 -0
- package/scripts/cco-status.js +540 -0
- package/scripts/cco-stop.js +77 -0
- package/scripts/cco-userprompt-submit.js +83 -0
- package/scripts/lib/activation/account-state.js +37 -0
- package/scripts/lib/activation/activate-command.js +114 -0
- package/scripts/lib/activation/activation-runner.js +607 -0
- package/scripts/lib/activation/activation-self-healing.js +481 -0
- package/scripts/lib/activation/activation-state.js +96 -0
- package/scripts/lib/activation/activation-summary.js +241 -0
- package/scripts/lib/activation/adapters/claude-code.js +77 -0
- package/scripts/lib/activation/adapters/codex-cli.js +52 -0
- package/scripts/lib/activation/adapters/cursor.js +37 -0
- package/scripts/lib/activation/adapters/github-agent.js +39 -0
- package/scripts/lib/activation/adapters/terminal.js +42 -0
- package/scripts/lib/activation/adapters/vscode.js +39 -0
- package/scripts/lib/activation/adapters/windsurf.js +37 -0
- package/scripts/lib/activation/ai-surface-detector.js +151 -0
- package/scripts/lib/activation/connect-account.js +145 -0
- package/scripts/lib/activation/detect-environment.js +75 -0
- package/scripts/lib/activation/detect-hosts.js +62 -0
- package/scripts/lib/activation/format-activation-output.js +109 -0
- package/scripts/lib/activation/next-action.js +43 -0
- package/scripts/lib/activation/repair-engine.js +219 -0
- package/scripts/lib/activation/run-entry.js +346 -0
- package/scripts/lib/activation-distribution-readiness.js +507 -0
- package/scripts/lib/adapter-conformance.js +176 -0
- package/scripts/lib/adapter-readiness.js +428 -0
- package/scripts/lib/adapter-safety-boundaries.js +335 -0
- package/scripts/lib/adapter-technical-readiness-gate.js +205 -0
- package/scripts/lib/agent-access-governance.js +455 -0
- package/scripts/lib/agent-activation-readiness.js +878 -0
- package/scripts/lib/agent-enforcement.js +765 -0
- package/scripts/lib/agent-evolution-loop.js +479 -0
- package/scripts/lib/agent-policy-profile.js +210 -0
- package/scripts/lib/agent-security/action-evaluator.js +507 -0
- package/scripts/lib/agent-security/adapter-registry.js +98 -0
- package/scripts/lib/agent-security/auto-policy.js +139 -0
- package/scripts/lib/agent-security/bounded-scan.js +93 -0
- package/scripts/lib/agent-security/enforcement-adapter.js +174 -0
- package/scripts/lib/agent-security/enforcement-engine.js +1129 -0
- package/scripts/lib/agent-security/file-write-adapter.js +183 -0
- package/scripts/lib/agent-security/file-write-rules.js +178 -0
- package/scripts/lib/agent-security/index.js +3342 -0
- package/scripts/lib/agent-security/instruction-risk.js +181 -0
- package/scripts/lib/agent-security/mcp-action-adapter.js +185 -0
- package/scripts/lib/agent-security/mcp-action-rules.js +184 -0
- package/scripts/lib/agent-security/package-action-adapter.js +175 -0
- package/scripts/lib/agent-security/package-action-rules.js +233 -0
- package/scripts/lib/agent-security/performance.js +148 -0
- package/scripts/lib/agent-security/permission-minimizer.js +403 -0
- package/scripts/lib/agent-security/scan-cache.js +74 -0
- package/scripts/lib/agent-security/source-trust.js +146 -0
- package/scripts/lib/ai-efficiency-dogfood-pack.js +437 -0
- package/scripts/lib/ai-efficiency-eval.js +207 -0
- package/scripts/lib/ai-efficiency-guard-contract.js +209 -0
- package/scripts/lib/ai-efficiency-measured-value.js +106 -0
- package/scripts/lib/ai-install-prompt.js +293 -0
- package/scripts/lib/ai-task-inference.js +155 -0
- package/scripts/lib/ai-value-ledger.js +121 -0
- package/scripts/lib/ai-value-proof-table.js +151 -0
- package/scripts/lib/ai-work-efficiency-routing.js +623 -0
- package/scripts/lib/ai-workspace-hygiene.js +1499 -0
- package/scripts/lib/alpha-activation.js +520 -0
- package/scripts/lib/alpha-feedback.js +263 -0
- package/scripts/lib/alpha-readiness-gate.js +332 -0
- package/scripts/lib/anti-gaming.js +169 -0
- package/scripts/lib/artifact-health.js +431 -0
- package/scripts/lib/artifact-receipt-hygiene.js +619 -0
- package/scripts/lib/attribution.js +180 -0
- package/scripts/lib/audit.js +289 -0
- package/scripts/lib/avorelo-skill-registry.js +810 -0
- package/scripts/lib/batch-jobs.js +71 -0
- package/scripts/lib/brain-pack.js +578 -0
- package/scripts/lib/brand-boundary.js +424 -0
- package/scripts/lib/brand.js +74 -0
- package/scripts/lib/browser-capability.js +1048 -0
- package/scripts/lib/browser-proof-governed-workflow.js +1020 -0
- package/scripts/lib/browser-proof-preflight.js +321 -0
- package/scripts/lib/cache-readiness.js +296 -0
- package/scripts/lib/canonical-reentry.js +162 -0
- package/scripts/lib/capability-contract-validator.js +177 -0
- package/scripts/lib/capability-hygiene.js +504 -0
- package/scripts/lib/capability-intake-quarantine.js +789 -0
- package/scripts/lib/capability-kernel-registry.js +566 -0
- package/scripts/lib/capability-kernel.js +202 -0
- package/scripts/lib/capability-manifest.js +255 -0
- package/scripts/lib/capability-outcome-normalizer.js +104 -0
- package/scripts/lib/capability-packs.js +314 -0
- package/scripts/lib/capability-recommender.js +513 -0
- package/scripts/lib/capability-registry.js +1062 -0
- package/scripts/lib/carry-forward-surfacing.js +194 -0
- package/scripts/lib/ccusage-adapter.js +188 -0
- package/scripts/lib/claude-session-evidence/index.js +1027 -0
- package/scripts/lib/cli-output-sanitizer.js +118 -0
- package/scripts/lib/company-loop.js +1149 -0
- package/scripts/lib/config.js +641 -0
- package/scripts/lib/context-acquisition-plan.js +359 -0
- package/scripts/lib/context-authority-rules.js +138 -0
- package/scripts/lib/context-budget-guard.js +170 -0
- package/scripts/lib/context-budget-scanner.js +257 -0
- package/scripts/lib/context-budget.js +167 -0
- package/scripts/lib/context-claim-safety.js +116 -0
- package/scripts/lib/context-compiler.js +236 -0
- package/scripts/lib/context-optimizer.js +715 -0
- package/scripts/lib/context-packet.js +121 -0
- package/scripts/lib/context-receipt.js +57 -0
- package/scripts/lib/context-reduction-plan.js +179 -0
- package/scripts/lib/context-safety.js +88 -0
- package/scripts/lib/context-savings-engine.js +158 -0
- package/scripts/lib/context-source-registry.js +393 -0
- package/scripts/lib/context-task-profiles.js +147 -0
- package/scripts/lib/cost-abuse-guard.js +975 -0
- package/scripts/lib/cost-evidence.js +287 -0
- package/scripts/lib/cost-to-outcome-signals.js +639 -0
- package/scripts/lib/cross-host-install-plan.js +308 -0
- package/scripts/lib/cross-host-install-readiness.js +242 -0
- package/scripts/lib/cross-host-value-flow.js +268 -0
- package/scripts/lib/dashboard.js +1056 -0
- package/scripts/lib/data-permissions-guard.js +1187 -0
- package/scripts/lib/deployment-environment-guard.js +1236 -0
- package/scripts/lib/design-partner-feedback.js +346 -0
- package/scripts/lib/dogfood-evidence-freshness.js +567 -0
- package/scripts/lib/efficiency-receipt.js +354 -0
- package/scripts/lib/entitlements.js +100 -0
- package/scripts/lib/execution-packet.js +559 -0
- package/scripts/lib/execution-route-governor.js +179 -0
- package/scripts/lib/experimentation-events.js +547 -0
- package/scripts/lib/external-capability-compliance.js +107 -0
- package/scripts/lib/external-readiness/ai-visibility/ai-visibility-runner.js +322 -0
- package/scripts/lib/external-readiness/calibration/benchmark-site-catalog.js +121 -0
- package/scripts/lib/external-readiness/calibration/completion-gate.js +112 -0
- package/scripts/lib/external-readiness/calibration/real-site-benchmark-runner.js +284 -0
- package/scripts/lib/external-readiness/drift-detector.js +100 -0
- package/scripts/lib/external-readiness/endpoint-prober.js +332 -0
- package/scripts/lib/external-readiness/external-readiness-runner.js +112 -0
- package/scripts/lib/external-readiness/full-agent-readiness-scorer.js +220 -0
- package/scripts/lib/external-readiness/golden-path-runner.js +333 -0
- package/scripts/lib/external-readiness/inside-out-runner.js +118 -0
- package/scripts/lib/external-readiness/proof-receipt-writer.js +112 -0
- package/scripts/lib/external-readiness/safe-fix-executor.js +117 -0
- package/scripts/lib/external-readiness/skills/category-boundary-guard.js +30 -0
- package/scripts/lib/external-readiness/skills/pmm-positioning-guard.js +69 -0
- package/scripts/lib/external-readiness/skills/safe-fix-policy.js +35 -0
- package/scripts/lib/external-user-simulation.js +166 -0
- package/scripts/lib/failure-recovery-readiness.js +81 -0
- package/scripts/lib/failure-recovery.js +419 -0
- package/scripts/lib/feedback-intelligence.js +537 -0
- package/scripts/lib/feedback-signals.js +205 -0
- package/scripts/lib/file-integrity.js +68 -0
- package/scripts/lib/fsx.js +129 -0
- package/scripts/lib/full-readiness-gate.js +451 -0
- package/scripts/lib/governed-action-exposure-primitives.js +1514 -0
- package/scripts/lib/governed-exposure-planner.js +186 -0
- package/scripts/lib/governed-workspace-hygiene.js +946 -0
- package/scripts/lib/guard-suite-common.js +412 -0
- package/scripts/lib/guidance-builder.js +174 -0
- package/scripts/lib/hook-apply.js +1075 -0
- package/scripts/lib/hook-baseline.js +310 -0
- package/scripts/lib/hook-config-preview.js +275 -0
- package/scripts/lib/hook-contracts.js +290 -0
- package/scripts/lib/hook-safety-boundary-readiness.js +80 -0
- package/scripts/lib/host-capability-matrix.js +351 -0
- package/scripts/lib/host-support-context.js +254 -0
- package/scripts/lib/http-hook-action.js +538 -0
- package/scripts/lib/install-ai-readiness.js +84 -0
- package/scripts/lib/install-intake-risk.js +1051 -0
- package/scripts/lib/install-journey-intelligence.js +329 -0
- package/scripts/lib/intervention-guidance.js +57 -0
- package/scripts/lib/known-limitations.js +115 -0
- package/scripts/lib/l8-path-truth.js +146 -0
- package/scripts/lib/launch-hardening-gate.js +436 -0
- package/scripts/lib/launch-readiness.js +635 -0
- package/scripts/lib/learning-experimentation-foundation.js +1484 -0
- package/scripts/lib/learning-memory.js +686 -0
- package/scripts/lib/lifecycle-hooks.js +982 -0
- package/scripts/lib/local-package-smoke.js +436 -0
- package/scripts/lib/local-pricing.js +299 -0
- package/scripts/lib/mcp-enforcement.js +311 -0
- package/scripts/lib/mcp-least-privilege-policy.js +303 -0
- package/scripts/lib/mcp-tool-inventory.js +388 -0
- package/scripts/lib/mcp-tool-risk.js +0 -0
- package/scripts/lib/memory.js +335 -0
- package/scripts/lib/metrics.js +699 -0
- package/scripts/lib/micro-proof.js +133 -0
- package/scripts/lib/model-work-class-routing.js +79 -0
- package/scripts/lib/next-run-context.js +436 -0
- package/scripts/lib/next-run-continuity.js +1207 -0
- package/scripts/lib/observability-recovery-guard.js +1030 -0
- package/scripts/lib/operating-value.js +1662 -0
- package/scripts/lib/optimization-v3.js +122 -0
- package/scripts/lib/orchestration/adapters/_shared.js +49 -0
- package/scripts/lib/orchestration/adapters/aider.js +18 -0
- package/scripts/lib/orchestration/adapters/claude-code.js +35 -0
- package/scripts/lib/orchestration/adapters/codex.js +35 -0
- package/scripts/lib/orchestration/adapters/gemini-cli.js +18 -0
- package/scripts/lib/orchestration/adapters/git.js +25 -0
- package/scripts/lib/orchestration/adapters/index.js +31 -0
- package/scripts/lib/orchestration/adapters/lm-studio.js +18 -0
- package/scripts/lib/orchestration/adapters/ollama.js +18 -0
- package/scripts/lib/orchestration/adapters/opencode.js +18 -0
- package/scripts/lib/orchestration/adapters/openrouter.js +18 -0
- package/scripts/lib/orchestration/adapters/test-runner.js +25 -0
- package/scripts/lib/orchestration/cli.js +438 -0
- package/scripts/lib/orchestration/execution-manager.js +279 -0
- package/scripts/lib/orchestration/handoff.js +314 -0
- package/scripts/lib/orchestration/index.js +456 -0
- package/scripts/lib/orchestration/inventory.js +47 -0
- package/scripts/lib/orchestration/model-discovery.js +498 -0
- package/scripts/lib/orchestration/model-profiler.js +170 -0
- package/scripts/lib/orchestration/model-profiles.js +252 -0
- package/scripts/lib/orchestration/model-refresh-policy.js +72 -0
- package/scripts/lib/orchestration/proof-writer.js +349 -0
- package/scripts/lib/orchestration/provider-discovery/aider.js +49 -0
- package/scripts/lib/orchestration/provider-discovery/claude-code.js +56 -0
- package/scripts/lib/orchestration/provider-discovery/codex.js +49 -0
- package/scripts/lib/orchestration/provider-discovery/common.js +186 -0
- package/scripts/lib/orchestration/provider-discovery/gemini.js +106 -0
- package/scripts/lib/orchestration/provider-discovery/lm-studio.js +118 -0
- package/scripts/lib/orchestration/provider-discovery/models-dev.js +12 -0
- package/scripts/lib/orchestration/provider-discovery/ollama.js +100 -0
- package/scripts/lib/orchestration/provider-discovery/opencode.js +47 -0
- package/scripts/lib/orchestration/provider-discovery/openrouter.js +44 -0
- package/scripts/lib/orchestration/risk-classifier.js +130 -0
- package/scripts/lib/orchestration/routing-policy.js +486 -0
- package/scripts/lib/orchestration/settings.js +95 -0
- package/scripts/lib/orchestration/state.js +165 -0
- package/scripts/lib/orchestration/verification-manager.js +138 -0
- package/scripts/lib/outcome-evidence-stack.js +1083 -0
- package/scripts/lib/output-profiles.js +146 -0
- package/scripts/lib/package-content-audit.js +368 -0
- package/scripts/lib/package-runtime.js +278 -0
- package/scripts/lib/plan-surface.js +53 -0
- package/scripts/lib/plans.js +2327 -0
- package/scripts/lib/policy-provider.js +27 -0
- package/scripts/lib/prelaunch-activation-readiness.js +409 -0
- package/scripts/lib/prelaunch-evidence-store.js +816 -0
- package/scripts/lib/prelaunch-intelligence.js +869 -0
- package/scripts/lib/pricing-experiment.js +118 -0
- package/scripts/lib/pro-moment-events.js +77 -0
- package/scripts/lib/pro-moment-state.js +227 -0
- package/scripts/lib/pro-moments.js +1216 -0
- package/scripts/lib/product-home-receipt.js +1790 -0
- package/scripts/lib/product-learning-events.js +696 -0
- package/scripts/lib/production-confidence-control-tower.js +1209 -0
- package/scripts/lib/production-confidence.js +1858 -0
- package/scripts/lib/project-profile.js +555 -0
- package/scripts/lib/prompt-compiler-plan.js +94 -0
- package/scripts/lib/prompt-compiler.js +280 -0
- package/scripts/lib/prompt-lint.js +32 -0
- package/scripts/lib/prompt-suggestions.js +52 -0
- package/scripts/lib/proof-canonical.js +398 -0
- package/scripts/lib/proof-drilldown.js +383 -0
- package/scripts/lib/proof-e2e/promise-completion.js +141 -0
- package/scripts/lib/proof-e2e/promise-dashboard-claims.js +135 -0
- package/scripts/lib/proof-e2e/promise-local-proof.js +156 -0
- package/scripts/lib/proof-e2e/promise-provider-neutral.js +136 -0
- package/scripts/lib/proof-e2e/promise-registry.js +219 -0
- package/scripts/lib/proof-e2e/promise-security-trust.js +141 -0
- package/scripts/lib/proof-e2e/promise-skills-risk.js +163 -0
- package/scripts/lib/proof-e2e/promise-token-savings.js +112 -0
- package/scripts/lib/proof-e2e/promise-work-management.js +126 -0
- package/scripts/lib/proof-events.js +342 -0
- package/scripts/lib/proof-history.js +243 -0
- package/scripts/lib/proof-metrics.js +296 -0
- package/scripts/lib/proof-outcome-evidence.js +134 -0
- package/scripts/lib/proof-receipt.js +335 -0
- package/scripts/lib/proof-record.js +461 -0
- package/scripts/lib/proof-savings-report.js +1549 -0
- package/scripts/lib/public-activation-distribution-gate.js +266 -0
- package/scripts/lib/public-cli.js +5631 -0
- package/scripts/lib/public-distribution-truth.js +211 -0
- package/scripts/lib/public-distribution.js +73 -0
- package/scripts/lib/public-install-claim-checker.js +300 -0
- package/scripts/lib/publish-provenance-readiness.js +287 -0
- package/scripts/lib/readiness-delta.js +218 -0
- package/scripts/lib/readiness-evidence-closure.js +196 -0
- package/scripts/lib/reentry-memory-capture.js +241 -0
- package/scripts/lib/reentry-memory-retrieval.js +302 -0
- package/scripts/lib/reentry-memory-status.js +147 -0
- package/scripts/lib/reentry-memory-store.js +183 -0
- package/scripts/lib/reentry-state.js +66 -0
- package/scripts/lib/release-candidate-bundle.js +166 -0
- package/scripts/lib/remediation.js +81 -0
- package/scripts/lib/repo-map.js +391 -0
- package/scripts/lib/run-action-policy.js +104 -0
- package/scripts/lib/run-contract-receipt.js +48 -0
- package/scripts/lib/run-contract.js +288 -0
- package/scripts/lib/run-cost-to-verified-outcome.js +87 -0
- package/scripts/lib/run-improvements-lifecycle.js +330 -0
- package/scripts/lib/run-improvements.js +789 -0
- package/scripts/lib/run-proof-contract.js +116 -0
- package/scripts/lib/run-risk-classifier.js +131 -0
- package/scripts/lib/run-task-classifier.js +216 -0
- package/scripts/lib/runtime-decision-policy.js +387 -0
- package/scripts/lib/safe-path-engine.js +705 -0
- package/scripts/lib/safe-repair.js +877 -0
- package/scripts/lib/safe-run-controller.js +887 -0
- package/scripts/lib/scope-repair-before-approval.js +865 -0
- package/scripts/lib/score.js +262 -0
- package/scripts/lib/seamless-enforcement.js +329 -0
- package/scripts/lib/seamless-outcome.js +689 -0
- package/scripts/lib/seamless-reality-gate.js +5053 -0
- package/scripts/lib/security-confidence/activation-safety.js +78 -0
- package/scripts/lib/security-confidence/adapters/adapter-contract.js +125 -0
- package/scripts/lib/security-confidence/adapters/mock-claude-bughunter-adapter.js +36 -0
- package/scripts/lib/security-confidence/adapters/mock-decepticon-adapter.js +34 -0
- package/scripts/lib/security-confidence/adapters/mock-mcp-scan-adapter.js +35 -0
- package/scripts/lib/security-confidence/adapters/mock-snyk-agent-scan-adapter.js +35 -0
- package/scripts/lib/security-confidence/adapters/mock-tank-adapter.js +34 -0
- package/scripts/lib/security-confidence/adapters/reference-registry.js +63 -0
- package/scripts/lib/security-confidence/auth-rbac-plan.js +39 -0
- package/scripts/lib/security-confidence/component-inventory.js +151 -0
- package/scripts/lib/security-confidence/constants.js +191 -0
- package/scripts/lib/security-confidence/dependency-script-risk.js +95 -0
- package/scripts/lib/security-confidence/evidence-hygiene.js +127 -0
- package/scripts/lib/security-confidence/feature-impact-classifier.js +80 -0
- package/scripts/lib/security-confidence/file-utils.js +161 -0
- package/scripts/lib/security-confidence/finding-triage.js +90 -0
- package/scripts/lib/security-confidence/findings.js +115 -0
- package/scripts/lib/security-confidence/index.js +29 -0
- package/scripts/lib/security-confidence/permission-budget.js +93 -0
- package/scripts/lib/security-confidence/redaction.js +82 -0
- package/scripts/lib/security-confidence/redteam-fixtures.js +53 -0
- package/scripts/lib/security-confidence/reference-adoption.js +229 -0
- package/scripts/lib/security-confidence/reference-taxonomy.js +155 -0
- package/scripts/lib/security-confidence/report-quality.js +99 -0
- package/scripts/lib/security-confidence/report-writer.js +79 -0
- package/scripts/lib/security-confidence/risk-classifier.js +187 -0
- package/scripts/lib/security-confidence/runner.js +179 -0
- package/scripts/lib/security-confidence/schema.js +188 -0
- package/scripts/lib/security-confidence/secret-safety.js +74 -0
- package/scripts/lib/security-confidence/security-capability-router.js +98 -0
- package/scripts/lib/security-confidence/web-security-smoke-plan.js +46 -0
- package/scripts/lib/security-risk-classifier.js +511 -0
- package/scripts/lib/security-scan.js +396 -0
- package/scripts/lib/session-context-optimizer.js +1211 -0
- package/scripts/lib/session-timing.js +322 -0
- package/scripts/lib/skill-hygiene.js +805 -0
- package/scripts/lib/skill-packs.js +161 -0
- package/scripts/lib/skills-operating-layer-v1.js +1023 -0
- package/scripts/lib/skills-operating-layer.js +629 -0
- package/scripts/lib/smallest-safe-plan.js +214 -0
- package/scripts/lib/smart-routing-plan.js +115 -0
- package/scripts/lib/smart-work-routing.js +768 -0
- package/scripts/lib/source-catalog.js +1022 -0
- package/scripts/lib/status-value-summary.js +32 -0
- package/scripts/lib/supply-chain-dependency-guard.js +1333 -0
- package/scripts/lib/supply-chain-guard.js +219 -0
- package/scripts/lib/support-bundle.js +561 -0
- package/scripts/lib/support-redaction.js +66 -0
- package/scripts/lib/task-continuation.js +440 -0
- package/scripts/lib/test-helpers.js +15 -0
- package/scripts/lib/tier.js +38 -0
- package/scripts/lib/token-context-quality-gate.js +370 -0
- package/scripts/lib/token-cost-capture.js +187 -0
- package/scripts/lib/token-cost-evidence.js +862 -0
- package/scripts/lib/token-cost-intelligence.js +358 -0
- package/scripts/lib/token-efficiency-evidence.js +213 -0
- package/scripts/lib/token-efficiency-map.js +110 -0
- package/scripts/lib/token-evidence.js +703 -0
- package/scripts/lib/tokenish.js +17 -0
- package/scripts/lib/tool-output-sandbox.js +559 -0
- package/scripts/lib/trust-audit.js +136 -0
- package/scripts/lib/unified-events.js +396 -0
- package/scripts/lib/upgrade-interruption-recovery.js +407 -0
- package/scripts/lib/usage-ledger.js +201 -0
- package/scripts/lib/value-ledger.js +130 -0
- package/scripts/lib/value-proof-calibration.js +531 -0
- package/scripts/lib/visual-qa.js +231 -0
- package/scripts/lib/voice-alpha.js +29 -0
- package/scripts/lib/webhook-idempotency-guard.js +990 -0
- package/scripts/lib/work-aware-orchestration.js +976 -0
- package/scripts/lib/work-control-receipts.js +577 -0
- package/scripts/lib/work-ledger.js +1123 -0
- package/scripts/lib/work-panel-preview.js +352 -0
- package/scripts/lib/workflow-discipline.js +280 -0
- package/scripts/lib/workflow-intelligence-radar.js +804 -0
- package/scripts/lib/workflow-signals.js +419 -0
- package/scripts/lib/workspace-map.js +281 -0
- package/scripts/lib/workspace-registry.js +1367 -0
- package/scripts/lib/workspace-resolver.js +542 -0
- package/scripts/wuz-doctor.js +315 -0
- package/bin/avorelo.mjs +0 -7
- package/dist/avorelo.mjs +0 -24678
|
@@ -0,0 +1,540 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
// Avorelo Session Collision Guard v1
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
// Internal-only operational safety check. Before an AI coding session edits,
|
|
6
|
+
// commits, pushes, force-pushes, or opens a PR, this guard returns a
|
|
7
|
+
// deterministic decision about whether the session is safe to continue, should
|
|
8
|
+
// adopt an existing branch, should stop and report, or risks duplicate work.
|
|
9
|
+
//
|
|
10
|
+
// Motivation: a real coordination incident where two sessions reasoned about
|
|
11
|
+
// the same PR/branch state and one session had local changes not yet visible
|
|
12
|
+
// on GitHub. This guard makes that hazard observable and deterministic.
|
|
13
|
+
//
|
|
14
|
+
// Architecture boundary (hard):
|
|
15
|
+
// - deterministic-first, local-first
|
|
16
|
+
// - read-only git only (never changes branch, never mutates the repo)
|
|
17
|
+
// - NO model calls, NO network in core mode, NO GitHub API in core mode
|
|
18
|
+
// - optional, opt-in `--with-gh` PR awareness that fails open if gh is absent
|
|
19
|
+
// - NO runtime / hook / dashboard / redaction / routing / verify imports
|
|
20
|
+
// - compact text output by default; stable JSON with --json
|
|
21
|
+
//
|
|
22
|
+
// This is NOT Workstream Preflight. It does not decide product/roadmap
|
|
23
|
+
// priority, does not read or change runtime behavior, and does not enforce
|
|
24
|
+
// GitHub policy. It is a local session-safety check only.
|
|
25
|
+
//
|
|
26
|
+
// The ONLY runtime dependency beyond Node core is the `git` binary, invoked
|
|
27
|
+
// read-only through a subcommand allowlist (see READONLY_GIT / READONLY_GH).
|
|
28
|
+
|
|
29
|
+
const { execFileSync } = require("child_process");
|
|
30
|
+
|
|
31
|
+
const SCHEMA_VERSION = "avorelo.sessionCollisionGuard.v1";
|
|
32
|
+
|
|
33
|
+
const DECISIONS = Object.freeze({
|
|
34
|
+
SAFE_TO_CONTINUE: "safe_to_continue",
|
|
35
|
+
ADOPT_EXISTING_BRANCH: "adopt_existing_branch",
|
|
36
|
+
STOP_AND_REPORT: "stop_and_report",
|
|
37
|
+
DUPLICATE_PR_RISK: "duplicate_pr_risk",
|
|
38
|
+
DIRTY_TREE_BLOCKED: "dirty_tree_blocked",
|
|
39
|
+
BRANCH_DIVERGED: "branch_diverged",
|
|
40
|
+
NO_GIT_REPO: "no_git_repo",
|
|
41
|
+
UNKNOWN_NEEDS_USER_DECISION: "unknown_needs_user_decision",
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// Read-only command allowlists. Any attempt to run a git/gh subcommand outside
|
|
45
|
+
// these sets throws — the guard structurally cannot push/reset/clean/checkout/
|
|
46
|
+
// merge/rebase/commit or merge a PR.
|
|
47
|
+
const READONLY_GIT = Object.freeze(
|
|
48
|
+
new Set(["rev-parse", "status", "branch", "rev-list", "log", "for-each-ref", "symbolic-ref"])
|
|
49
|
+
);
|
|
50
|
+
const READONLY_GH = Object.freeze(new Set(["pr", "--version"]));
|
|
51
|
+
const READONLY_GH_PR_ACTIONS = Object.freeze(new Set(["list", "status", "view"]));
|
|
52
|
+
|
|
53
|
+
// ---------------------------------------------------------------------------
|
|
54
|
+
// Command runners (read-only, allowlist-guarded).
|
|
55
|
+
// ---------------------------------------------------------------------------
|
|
56
|
+
|
|
57
|
+
function defaultGitRunner(cwd, args) {
|
|
58
|
+
if (!Array.isArray(args) || args.length === 0 || !READONLY_GIT.has(args[0])) {
|
|
59
|
+
throw new Error(`blocked non-read-only git invocation: ${(args || []).join(" ")}`);
|
|
60
|
+
}
|
|
61
|
+
return execFileSync("git", args, {
|
|
62
|
+
cwd,
|
|
63
|
+
encoding: "utf8",
|
|
64
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function defaultGhRunner(cwd, args) {
|
|
69
|
+
if (!Array.isArray(args) || args.length === 0 || !READONLY_GH.has(args[0])) {
|
|
70
|
+
throw new Error(`blocked non-read-only gh invocation: ${(args || []).join(" ")}`);
|
|
71
|
+
}
|
|
72
|
+
if (args[0] === "pr" && !READONLY_GH_PR_ACTIONS.has(args[1])) {
|
|
73
|
+
throw new Error(`blocked non-read-only gh pr action: ${args[1]}`);
|
|
74
|
+
}
|
|
75
|
+
return execFileSync("gh", args, {
|
|
76
|
+
cwd,
|
|
77
|
+
encoding: "utf8",
|
|
78
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Safe wrapper: returns { ok, out } and never throws for expected git failures
|
|
83
|
+
// (e.g. "no upstream configured", "not a git repository").
|
|
84
|
+
function tryGit(gitRunner, cwd, args) {
|
|
85
|
+
try {
|
|
86
|
+
return { ok: true, out: String(gitRunner(cwd, args) || "").trim() };
|
|
87
|
+
} catch (_err) {
|
|
88
|
+
return { ok: false, out: "" };
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// ---------------------------------------------------------------------------
|
|
93
|
+
// Repo inspection (pure given a gitRunner).
|
|
94
|
+
// ---------------------------------------------------------------------------
|
|
95
|
+
|
|
96
|
+
function inspectRepo(gitRunner, cwd) {
|
|
97
|
+
const insideWorkTree = tryGit(gitRunner, cwd, ["rev-parse", "--is-inside-work-tree"]);
|
|
98
|
+
if (!insideWorkTree.ok || insideWorkTree.out !== "true") {
|
|
99
|
+
return { isGitRepo: false };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const rootRes = tryGit(gitRunner, cwd, ["rev-parse", "--show-toplevel"]);
|
|
103
|
+
const root = rootRes.ok ? rootRes.out : null;
|
|
104
|
+
|
|
105
|
+
// Current branch ("" when in detached HEAD).
|
|
106
|
+
const branchRes = tryGit(gitRunner, cwd, ["branch", "--show-current"]);
|
|
107
|
+
const currentBranch = branchRes.ok && branchRes.out ? branchRes.out : null;
|
|
108
|
+
|
|
109
|
+
// Dirty tree: any porcelain entry (tracked modification, staged, or untracked).
|
|
110
|
+
const statusRes = tryGit(gitRunner, cwd, ["status", "--porcelain"]);
|
|
111
|
+
const statusLines = statusRes.out ? statusRes.out.split(/\r?\n/).filter(Boolean) : [];
|
|
112
|
+
const dirty = statusLines.length > 0;
|
|
113
|
+
|
|
114
|
+
// Upstream (@{u}); absent => no tracking branch configured.
|
|
115
|
+
const upstreamRes = tryGit(gitRunner, cwd, [
|
|
116
|
+
"rev-parse",
|
|
117
|
+
"--abbrev-ref",
|
|
118
|
+
"--symbolic-full-name",
|
|
119
|
+
"@{u}",
|
|
120
|
+
]);
|
|
121
|
+
const upstream = upstreamRes.ok && upstreamRes.out ? upstreamRes.out : null;
|
|
122
|
+
|
|
123
|
+
let ahead = 0;
|
|
124
|
+
let behind = 0;
|
|
125
|
+
if (upstream) {
|
|
126
|
+
const counts = tryGit(gitRunner, cwd, ["rev-list", "--left-right", "--count", "HEAD...@{u}"]);
|
|
127
|
+
if (counts.ok) {
|
|
128
|
+
const parts = counts.out.split(/\s+/).filter(Boolean);
|
|
129
|
+
ahead = Number.parseInt(parts[0], 10) || 0;
|
|
130
|
+
behind = Number.parseInt(parts[1], 10) || 0;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Local-only detection of a same-named remote-tracking branch (no network).
|
|
135
|
+
let sameNamedRemoteBranchExists = null;
|
|
136
|
+
if (currentBranch) {
|
|
137
|
+
const remoteRefs = tryGit(gitRunner, cwd, [
|
|
138
|
+
"for-each-ref",
|
|
139
|
+
"--format=%(refname:short)",
|
|
140
|
+
`refs/remotes/*/${currentBranch}`,
|
|
141
|
+
]);
|
|
142
|
+
if (remoteRefs.ok) {
|
|
143
|
+
sameNamedRemoteBranchExists = remoteRefs.out.split(/\r?\n/).filter(Boolean).length > 0;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return {
|
|
148
|
+
isGitRepo: true,
|
|
149
|
+
root,
|
|
150
|
+
currentBranch,
|
|
151
|
+
upstream,
|
|
152
|
+
ahead,
|
|
153
|
+
behind,
|
|
154
|
+
dirty,
|
|
155
|
+
dirtyCount: statusLines.length,
|
|
156
|
+
sameNamedRemoteBranchExists,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// ---------------------------------------------------------------------------
|
|
161
|
+
// Optional, opt-in GitHub PR awareness (fails open).
|
|
162
|
+
// ---------------------------------------------------------------------------
|
|
163
|
+
|
|
164
|
+
function probeGh(ghRunner, cwd, branch) {
|
|
165
|
+
// Availability probe first; if gh is missing, fail open (no PR signal).
|
|
166
|
+
try {
|
|
167
|
+
ghRunner(cwd, ["--version"]);
|
|
168
|
+
} catch (_err) {
|
|
169
|
+
return { checked: true, available: false, openPr: null };
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (!branch) {
|
|
173
|
+
return { checked: true, available: true, openPr: null };
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
try {
|
|
177
|
+
const out = ghRunner(cwd, [
|
|
178
|
+
"pr",
|
|
179
|
+
"list",
|
|
180
|
+
"--state",
|
|
181
|
+
"open",
|
|
182
|
+
"--head",
|
|
183
|
+
branch,
|
|
184
|
+
"--json",
|
|
185
|
+
"number,title,url,headRefName",
|
|
186
|
+
]);
|
|
187
|
+
const arr = JSON.parse(String(out || "[]"));
|
|
188
|
+
if (Array.isArray(arr) && arr.length > 0) {
|
|
189
|
+
const pr = arr[0];
|
|
190
|
+
return {
|
|
191
|
+
checked: true,
|
|
192
|
+
available: true,
|
|
193
|
+
openPr: {
|
|
194
|
+
number: pr.number,
|
|
195
|
+
title: pr.title,
|
|
196
|
+
url: pr.url,
|
|
197
|
+
headRefName: pr.headRefName,
|
|
198
|
+
},
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
return { checked: true, available: true, openPr: null };
|
|
202
|
+
} catch (_err) {
|
|
203
|
+
// gh present but the call failed (no remote, not authed, offline): fail open.
|
|
204
|
+
return { checked: true, available: true, openPr: null };
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// ---------------------------------------------------------------------------
|
|
209
|
+
// Core decision (deterministic, precedence-ordered).
|
|
210
|
+
// ---------------------------------------------------------------------------
|
|
211
|
+
|
|
212
|
+
function baseResult() {
|
|
213
|
+
return {
|
|
214
|
+
schemaVersion: SCHEMA_VERSION,
|
|
215
|
+
decision: DECISIONS.UNKNOWN_NEEDS_USER_DECISION,
|
|
216
|
+
reason: "",
|
|
217
|
+
safeNextAction: "",
|
|
218
|
+
sessionOwnershipAcknowledged: false,
|
|
219
|
+
repo: {
|
|
220
|
+
isGitRepo: false,
|
|
221
|
+
root: null,
|
|
222
|
+
currentBranch: null,
|
|
223
|
+
upstream: null,
|
|
224
|
+
ahead: 0,
|
|
225
|
+
behind: 0,
|
|
226
|
+
dirty: false,
|
|
227
|
+
},
|
|
228
|
+
risk: {
|
|
229
|
+
dirtyTree: false,
|
|
230
|
+
noUpstream: false,
|
|
231
|
+
diverged: false,
|
|
232
|
+
aheadOnly: false,
|
|
233
|
+
behindOnly: false,
|
|
234
|
+
sameNamedRemoteBranchExists: null,
|
|
235
|
+
openPrForBranch: null,
|
|
236
|
+
forcePushRisk: "unknown",
|
|
237
|
+
},
|
|
238
|
+
gh: {
|
|
239
|
+
checked: false,
|
|
240
|
+
available: false,
|
|
241
|
+
openPr: null,
|
|
242
|
+
},
|
|
243
|
+
evidence: [],
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function finalize(result, decision, reason, safeNextAction) {
|
|
248
|
+
result.decision = decision;
|
|
249
|
+
result.reason = reason;
|
|
250
|
+
result.safeNextAction = safeNextAction;
|
|
251
|
+
return result;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function evaluate(options = {}) {
|
|
255
|
+
const cwd = options.cwd || process.cwd();
|
|
256
|
+
const targetBranch = options.branch || null;
|
|
257
|
+
const withGh = !!options.withGh;
|
|
258
|
+
const allowOwnedSession = !!options.allowOwnedSession;
|
|
259
|
+
const strict = !!options.strict;
|
|
260
|
+
const gitRunner = options.gitRunner || defaultGitRunner;
|
|
261
|
+
const ghRunner = options.ghRunner || defaultGhRunner;
|
|
262
|
+
// Optional injected gh probe (for tests / alternate transports).
|
|
263
|
+
const ghProbe = options.ghProbe || ((c, b) => probeGh(ghRunner, c, b));
|
|
264
|
+
|
|
265
|
+
const result = baseResult();
|
|
266
|
+
result.sessionOwnershipAcknowledged = allowOwnedSession;
|
|
267
|
+
|
|
268
|
+
const repo = inspectRepo(gitRunner, cwd);
|
|
269
|
+
|
|
270
|
+
// Rule 1: not a git repository.
|
|
271
|
+
if (!repo.isGitRepo) {
|
|
272
|
+
result.evidence.push("git rev-parse --is-inside-work-tree: not a work tree");
|
|
273
|
+
return finalize(
|
|
274
|
+
result,
|
|
275
|
+
DECISIONS.NO_GIT_REPO,
|
|
276
|
+
"current working directory is not inside a git work tree.",
|
|
277
|
+
"Run inside the intended repository before editing, committing, or pushing."
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// Populate repo + risk snapshot.
|
|
282
|
+
result.repo = {
|
|
283
|
+
isGitRepo: true,
|
|
284
|
+
root: repo.root,
|
|
285
|
+
currentBranch: repo.currentBranch,
|
|
286
|
+
upstream: repo.upstream,
|
|
287
|
+
ahead: repo.ahead,
|
|
288
|
+
behind: repo.behind,
|
|
289
|
+
dirty: repo.dirty,
|
|
290
|
+
};
|
|
291
|
+
const aheadOnly = repo.ahead > 0 && repo.behind === 0;
|
|
292
|
+
const behindOnly = repo.behind > 0 && repo.ahead === 0;
|
|
293
|
+
const diverged = repo.ahead > 0 && repo.behind > 0;
|
|
294
|
+
result.risk.dirtyTree = repo.dirty;
|
|
295
|
+
result.risk.noUpstream = !repo.upstream;
|
|
296
|
+
result.risk.diverged = diverged;
|
|
297
|
+
result.risk.aheadOnly = aheadOnly;
|
|
298
|
+
result.risk.behindOnly = behindOnly;
|
|
299
|
+
result.risk.sameNamedRemoteBranchExists = repo.sameNamedRemoteBranchExists;
|
|
300
|
+
result.risk.forcePushRisk = computeForcePushRisk(repo, diverged, aheadOnly, behindOnly);
|
|
301
|
+
|
|
302
|
+
result.evidence.push(`currentBranch: ${repo.currentBranch || "(detached)"}`);
|
|
303
|
+
result.evidence.push(`git status --porcelain: ${repo.dirty ? `dirty (${repo.dirtyCount})` : "clean"}`);
|
|
304
|
+
result.evidence.push(`upstream: ${repo.upstream || "(none)"}`);
|
|
305
|
+
result.evidence.push(`ahead/behind: ${repo.ahead}/${repo.behind}`);
|
|
306
|
+
|
|
307
|
+
// Optional PR awareness (opt-in, fail-open). Recorded before decisions so it
|
|
308
|
+
// is visible regardless of which branch state wins.
|
|
309
|
+
let ghOpenPr = null;
|
|
310
|
+
if (withGh) {
|
|
311
|
+
const gh = ghProbe(cwd, repo.currentBranch);
|
|
312
|
+
result.gh = { checked: !!gh.checked, available: !!gh.available, openPr: gh.openPr || null };
|
|
313
|
+
ghOpenPr = gh.openPr || null;
|
|
314
|
+
result.risk.openPrForBranch = ghOpenPr ? ghOpenPr.number : null;
|
|
315
|
+
result.evidence.push(
|
|
316
|
+
`gh: ${result.gh.available ? "available" : "unavailable"}${ghOpenPr ? `, openPR #${ghOpenPr.number}` : ", no open PR for branch"}`
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Rule 2: dirty working tree (highest local blocker).
|
|
321
|
+
if (repo.dirty) {
|
|
322
|
+
return finalize(
|
|
323
|
+
result,
|
|
324
|
+
DECISIONS.DIRTY_TREE_BLOCKED,
|
|
325
|
+
`uncommitted changes exist (${repo.dirtyCount} entr${repo.dirtyCount === 1 ? "y" : "ies"}).`,
|
|
326
|
+
"Stop and report before editing or pushing; commit, stash, or hand off the dirty tree first."
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// Rule 3: detached HEAD / no current branch.
|
|
331
|
+
if (!repo.currentBranch) {
|
|
332
|
+
return finalize(
|
|
333
|
+
result,
|
|
334
|
+
DECISIONS.STOP_AND_REPORT,
|
|
335
|
+
"HEAD is detached; there is no current branch to own.",
|
|
336
|
+
"Stop and report; check out an intended branch before doing branch-scoped work."
|
|
337
|
+
);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// Rules 9 & 10: explicit --branch target differs from the current branch.
|
|
341
|
+
if (targetBranch && targetBranch !== repo.currentBranch) {
|
|
342
|
+
result.evidence.push(`targetBranch: ${targetBranch} (current: ${repo.currentBranch})`);
|
|
343
|
+
return finalize(
|
|
344
|
+
result,
|
|
345
|
+
DECISIONS.STOP_AND_REPORT,
|
|
346
|
+
`expected branch "${targetBranch}" but the session is on "${repo.currentBranch}".`,
|
|
347
|
+
"Stop and report; do not act on a different branch than the one you intend to own."
|
|
348
|
+
);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// Rule 4: no upstream configured.
|
|
352
|
+
if (!repo.upstream) {
|
|
353
|
+
return finalize(
|
|
354
|
+
result,
|
|
355
|
+
DECISIONS.UNKNOWN_NEEDS_USER_DECISION,
|
|
356
|
+
"branch has no upstream; remote ownership state cannot be determined locally.",
|
|
357
|
+
"Decide explicitly: set an upstream and re-check, or confirm this is a brand-new local branch before pushing."
|
|
358
|
+
);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// Rule 5: diverged (ahead AND behind).
|
|
362
|
+
if (diverged) {
|
|
363
|
+
return finalize(
|
|
364
|
+
result,
|
|
365
|
+
DECISIONS.BRANCH_DIVERGED,
|
|
366
|
+
`branch diverged from ${repo.upstream} (ahead ${repo.ahead}, behind ${repo.behind}).`,
|
|
367
|
+
"Stop and report; do not force-push. Reconcile by integrating upstream first, then push without --force."
|
|
368
|
+
);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// Rules 8 & 12: an open PR already exists for this branch.
|
|
372
|
+
if (ghOpenPr) {
|
|
373
|
+
if (!allowOwnedSession) {
|
|
374
|
+
return finalize(
|
|
375
|
+
result,
|
|
376
|
+
DECISIONS.DUPLICATE_PR_RISK,
|
|
377
|
+
`an open PR (#${ghOpenPr.number}) already targets this branch and ownership is not acknowledged.`,
|
|
378
|
+
"Confirm whether this session owns that PR. If it does, re-run with --allow-owned-session; otherwise stop to avoid duplicate work."
|
|
379
|
+
);
|
|
380
|
+
}
|
|
381
|
+
result.evidence.push("sessionOwnershipAcknowledged: true (open PR adopted by this session)");
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// Rule 6: behind only.
|
|
385
|
+
if (behindOnly) {
|
|
386
|
+
if (strict) {
|
|
387
|
+
return finalize(
|
|
388
|
+
result,
|
|
389
|
+
DECISIONS.STOP_AND_REPORT,
|
|
390
|
+
`branch is behind ${repo.upstream} by ${repo.behind} (strict mode).`,
|
|
391
|
+
"Stop and report; another session advanced this branch. Adopt their work deliberately before continuing."
|
|
392
|
+
);
|
|
393
|
+
}
|
|
394
|
+
return finalize(
|
|
395
|
+
result,
|
|
396
|
+
DECISIONS.ADOPT_EXISTING_BRANCH,
|
|
397
|
+
`branch is behind ${repo.upstream} by ${repo.behind} with no local commits.`,
|
|
398
|
+
"Adopt the existing branch: integrate the upstream commits (no force needed) before adding new work."
|
|
399
|
+
);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// Rule 7: ahead only.
|
|
403
|
+
if (aheadOnly) {
|
|
404
|
+
return finalize(
|
|
405
|
+
result,
|
|
406
|
+
DECISIONS.SAFE_TO_CONTINUE,
|
|
407
|
+
`branch is ahead of ${repo.upstream} by ${repo.ahead} with no upstream divergence.`,
|
|
408
|
+
"Safe to continue. A normal push works; only use force-with-lease (never plain force) if you intentionally amended or rebased."
|
|
409
|
+
);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// In sync (ahead 0, behind 0).
|
|
413
|
+
return finalize(
|
|
414
|
+
result,
|
|
415
|
+
DECISIONS.SAFE_TO_CONTINUE,
|
|
416
|
+
`branch is in sync with ${repo.upstream}.`,
|
|
417
|
+
"Safe to continue. Push normally when ready; no force-push is warranted."
|
|
418
|
+
);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
function computeForcePushRisk(repo, diverged, aheadOnly, behindOnly) {
|
|
422
|
+
if (!repo.upstream) return "unknown";
|
|
423
|
+
if (diverged) return "high";
|
|
424
|
+
if (behindOnly) return "medium";
|
|
425
|
+
if (aheadOnly) return "low";
|
|
426
|
+
return "low";
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// ---------------------------------------------------------------------------
|
|
430
|
+
// Rendering.
|
|
431
|
+
// ---------------------------------------------------------------------------
|
|
432
|
+
|
|
433
|
+
function formatText(result) {
|
|
434
|
+
const lines = [];
|
|
435
|
+
lines.push(`Session Guard: ${result.decision}`);
|
|
436
|
+
lines.push(`Reason: ${result.reason}`);
|
|
437
|
+
lines.push(`Safe next action: ${result.safeNextAction}`);
|
|
438
|
+
const detail = [];
|
|
439
|
+
if (result.repo.currentBranch) detail.push(`branch=${result.repo.currentBranch}`);
|
|
440
|
+
if (result.repo.upstream) detail.push(`upstream=${result.repo.upstream}`);
|
|
441
|
+
detail.push(`ahead/behind=${result.repo.ahead}/${result.repo.behind}`);
|
|
442
|
+
detail.push(`dirty=${result.repo.dirty}`);
|
|
443
|
+
detail.push(`forcePushRisk=${result.risk.forcePushRisk}`);
|
|
444
|
+
if (result.gh.checked) detail.push(`gh=${result.gh.available ? (result.gh.openPr ? `PR#${result.gh.openPr.number}` : "noPR") : "unavailable"}`);
|
|
445
|
+
lines.push(detail.join(" "));
|
|
446
|
+
return lines.join("\n");
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
// ---------------------------------------------------------------------------
|
|
450
|
+
// CLI.
|
|
451
|
+
// ---------------------------------------------------------------------------
|
|
452
|
+
|
|
453
|
+
function parseArgs(argv) {
|
|
454
|
+
const opts = { json: false, branch: null, withGh: false, allowOwnedSession: false, strict: false };
|
|
455
|
+
for (let i = 0; i < argv.length; i++) {
|
|
456
|
+
const a = argv[i];
|
|
457
|
+
if (a === "--json") opts.json = true;
|
|
458
|
+
else if (a === "--with-gh") opts.withGh = true;
|
|
459
|
+
else if (a === "--allow-owned-session") opts.allowOwnedSession = true;
|
|
460
|
+
else if (a === "--strict") opts.strict = true;
|
|
461
|
+
else if (a === "--branch") opts.branch = argv[++i] || null;
|
|
462
|
+
else if (a.startsWith("--branch=")) opts.branch = a.slice("--branch=".length) || null;
|
|
463
|
+
else if (a === "--help" || a === "-h") opts.help = true;
|
|
464
|
+
}
|
|
465
|
+
return opts;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
function helpText() {
|
|
469
|
+
return [
|
|
470
|
+
"Avorelo Session Collision Guard v1 (internal)",
|
|
471
|
+
"",
|
|
472
|
+
"Before you edit, commit, push, force-push, or open a PR, ask whether this",
|
|
473
|
+
"session is safe to continue, should adopt an existing branch, or should stop.",
|
|
474
|
+
"",
|
|
475
|
+
"Usage:",
|
|
476
|
+
" node scripts/avorelo-session-collision-guard.js",
|
|
477
|
+
" node scripts/avorelo-session-collision-guard.js --json",
|
|
478
|
+
" node scripts/avorelo-session-collision-guard.js --branch <name>",
|
|
479
|
+
" node scripts/avorelo-session-collision-guard.js --json --with-gh",
|
|
480
|
+
" node scripts/avorelo-session-collision-guard.js --allow-owned-session",
|
|
481
|
+
"",
|
|
482
|
+
"Flags:",
|
|
483
|
+
" --json emit stable JSON instead of compact text",
|
|
484
|
+
" --branch <name> assert the session is on <name>; mismatch => stop_and_report",
|
|
485
|
+
" --with-gh opt-in read-only PR awareness (fails open if gh absent)",
|
|
486
|
+
" --allow-owned-session acknowledge this session owns the open PR for the branch",
|
|
487
|
+
" --strict treat behind-only as stop_and_report instead of adopt",
|
|
488
|
+
"",
|
|
489
|
+
"Decisions: safe_to_continue | adopt_existing_branch | stop_and_report |",
|
|
490
|
+
" duplicate_pr_risk | dirty_tree_blocked | branch_diverged |",
|
|
491
|
+
" no_git_repo | unknown_needs_user_decision",
|
|
492
|
+
"",
|
|
493
|
+
"Read-only git only. No network/model/GitHub API in core mode.",
|
|
494
|
+
].join("\n");
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
function main(argv) {
|
|
498
|
+
const opts = parseArgs(argv);
|
|
499
|
+
if (opts.help) {
|
|
500
|
+
process.stdout.write(helpText() + "\n");
|
|
501
|
+
return 0;
|
|
502
|
+
}
|
|
503
|
+
let result;
|
|
504
|
+
try {
|
|
505
|
+
result = evaluate(opts);
|
|
506
|
+
} catch (err) {
|
|
507
|
+
// Only genuine execution errors (e.g. git binary missing) reach here.
|
|
508
|
+
process.stderr.write(`session-collision-guard: execution error: ${err && err.message}\n`);
|
|
509
|
+
return 2;
|
|
510
|
+
}
|
|
511
|
+
if (opts.json) {
|
|
512
|
+
process.stdout.write(JSON.stringify(result, null, 2) + "\n");
|
|
513
|
+
} else {
|
|
514
|
+
process.stdout.write(formatText(result) + "\n");
|
|
515
|
+
}
|
|
516
|
+
// Normal decisions (including stop/blocked) exit 0; only real errors are non-zero.
|
|
517
|
+
return 0;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
if (require.main === module) {
|
|
521
|
+
process.exit(main(process.argv.slice(2)));
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
module.exports = {
|
|
525
|
+
SCHEMA_VERSION,
|
|
526
|
+
DECISIONS,
|
|
527
|
+
READONLY_GIT,
|
|
528
|
+
READONLY_GH,
|
|
529
|
+
READONLY_GH_PR_ACTIONS,
|
|
530
|
+
defaultGitRunner,
|
|
531
|
+
defaultGhRunner,
|
|
532
|
+
evaluate,
|
|
533
|
+
inspectRepo,
|
|
534
|
+
probeGh,
|
|
535
|
+
formatText,
|
|
536
|
+
parseArgs,
|
|
537
|
+
helpText,
|
|
538
|
+
main,
|
|
539
|
+
computeForcePushRisk,
|
|
540
|
+
};
|