pi-dev-team 0.2.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/LICENSE +21 -0
- package/PORTING.md +134 -0
- package/README.md +207 -0
- package/UPSTREAM.json +64 -0
- package/agents/Explore.md +15 -0
- package/agents/a11y-review.md +118 -0
- package/agents/adr-author.md +70 -0
- package/agents/ai-provenance-review.md +120 -0
- package/agents/angular-reactivity-review.md +95 -0
- package/agents/arch-review.md +135 -0
- package/agents/architect.md +78 -0
- package/agents/autoship-batch-proposer.md +69 -0
- package/agents/claude-setup-review.md +136 -0
- package/agents/codebase-recon.md +184 -0
- package/agents/component-architecture-review.md +119 -0
- package/agents/concurrency-review.md +109 -0
- package/agents/correctness-review.md +290 -0
- package/agents/data-flow-tracer.md +120 -0
- package/agents/doc-review.md +165 -0
- package/agents/domain-review.md +136 -0
- package/agents/general-purpose.md +10 -0
- package/agents/gherkin-quality-critic.md +113 -0
- package/agents/js-fp-review.md +114 -0
- package/agents/mutation-kill.md +684 -0
- package/agents/naming-review.md +142 -0
- package/agents/orchestrator.md +339 -0
- package/agents/performance-review.md +105 -0
- package/agents/plan-review-acceptance.md +115 -0
- package/agents/plan-review-design.md +90 -0
- package/agents/plan-review-parallelization.md +84 -0
- package/agents/plan-review-strategic.md +96 -0
- package/agents/plan-review-ux.md +110 -0
- package/agents/platform-engineer.md +64 -0
- package/agents/product-manager.md +68 -0
- package/agents/progress-guardian.md +79 -0
- package/agents/qa-engineer.md +289 -0
- package/agents/quality-reviewer.md +132 -0
- package/agents/react-reactivity-review.md +102 -0
- package/agents/refactor-opportunity-review.md +128 -0
- package/agents/security-engineer.md +60 -0
- package/agents/security-review.md +218 -0
- package/agents/session-analysis.md +95 -0
- package/agents/software-engineer.md +105 -0
- package/agents/spec-compliance-review.md +100 -0
- package/agents/spec-reviewer.md +114 -0
- package/agents/structure-review.md +146 -0
- package/agents/tech-writer.md +84 -0
- package/agents/test-review.md +246 -0
- package/agents/test-smell-review.md +188 -0
- package/agents/token-efficiency-review.md +139 -0
- package/agents/ui-ux-designer.md +54 -0
- package/agents/vue-reactivity-review.md +95 -0
- package/bin/__pycache__/claudecpython-314.pyc +0 -0
- package/bin/claude +258 -0
- package/docs/upstream/.pages +1 -0
- package/docs/upstream/CHANGELOG.md +2586 -0
- package/docs/upstream/README.md +155 -0
- package/docs/upstream/agent-architecture.md +214 -0
- package/docs/upstream/agent_info.md +187 -0
- package/docs/upstream/artifact-migration.md +124 -0
- package/docs/upstream/code-intelligence-nudge.md +149 -0
- package/docs/upstream/code-review-process.md +294 -0
- package/docs/upstream/concurrent-use.md +73 -0
- package/docs/upstream/context-management.md +111 -0
- package/docs/upstream/developer-notes.md +280 -0
- package/docs/upstream/diagrams/architecture-overview.svg +101 -0
- package/docs/upstream/diagrams/review-dispatch.svg +139 -0
- package/docs/upstream/diagrams/team-agents.svg +128 -0
- package/docs/upstream/diagrams/test-improve-flow.svg +166 -0
- package/docs/upstream/diagrams/workflow-linear.svg +66 -0
- package/docs/upstream/diagrams/workflow-three-phase.svg +200 -0
- package/docs/upstream/eval-maintenance.md +95 -0
- package/docs/upstream/eval-running-guide.md +147 -0
- package/docs/upstream/eval-system.md +291 -0
- package/docs/upstream/session-review-oss-complements.md +75 -0
- package/docs/upstream/session-review.md +212 -0
- package/docs/upstream/skills.md +188 -0
- package/docs/upstream/team-structure.md +21 -0
- package/docs/upstream/telemetry-ci-access.md +129 -0
- package/docs/upstream/telemetry-repo-security.md +120 -0
- package/docs/upstream/test-evaluation.md +277 -0
- package/docs/upstream/test-improve.md +154 -0
- package/docs/upstream/triage-workflow.md +282 -0
- package/docs/upstream/workflows.md +289 -0
- package/extensions/dev-team/index.ts +539 -0
- package/extensions/dev-team/lib/agents.ts +272 -0
- package/extensions/dev-team/lib/ai-credits.ts +92 -0
- package/extensions/dev-team/lib/autocompact.ts +81 -0
- package/extensions/dev-team/lib/child-run.ts +102 -0
- package/extensions/dev-team/lib/config.ts +236 -0
- package/extensions/dev-team/lib/gh-command.ts +103 -0
- package/extensions/dev-team/lib/github-style.ts +307 -0
- package/extensions/dev-team/lib/hooks.ts +350 -0
- package/extensions/dev-team/lib/metrics.ts +115 -0
- package/extensions/dev-team/lib/safe-read.ts +49 -0
- package/extensions/dev-team/lib/session-files.ts +57 -0
- package/extensions/dev-team/lib/session-spend.ts +123 -0
- package/extensions/dev-team/lib/shell-scan.ts +205 -0
- package/extensions/dev-team/lib/skills.ts +213 -0
- package/extensions/dev-team/lib/subagent-render.ts +245 -0
- package/extensions/dev-team/lib/subagent-types.ts +164 -0
- package/extensions/dev-team/lib/subagent.ts +596 -0
- package/extensions/dev-team/lib/terminal-text.ts +54 -0
- package/extensions/dev-team/lib/tools-misc.ts +152 -0
- package/extensions/dev-team/lib/transcript.ts +110 -0
- package/extensions/dev-team/lib/trust.ts +52 -0
- package/extensions/dev-team/lib/usage-breakdown.ts +176 -0
- package/extensions/dev-team/lib/usage-chart.ts +153 -0
- package/extensions/dev-team/lib/usage-command.ts +107 -0
- package/extensions/dev-team/lib/usage-history.ts +203 -0
- package/extensions/dev-team/lib/usage-render.ts +225 -0
- package/extensions/dev-team/lib/usage-split-bar.ts +127 -0
- package/extensions/dev-team/lib/usage-state.ts +116 -0
- package/extensions/dev-team/lib/usage-text.ts +159 -0
- package/extensions/dev-team/lib/usage-view.ts +109 -0
- package/hooks/__pycache__/refactor_test_freeze_guard.cpython-314.pyc +0 -0
- package/hooks/agent_dispatch_ledger.py +190 -0
- package/hooks/autocompact_setup_nudge.py +99 -0
- package/hooks/bash_retry_guard.py +228 -0
- package/hooks/boundary_events_write_guard.py +352 -0
- package/hooks/code_intelligence_nudge.py +293 -0
- package/hooks/code_intelligence_turn_mark.py +317 -0
- package/hooks/codegraph_bootstrap.py +139 -0
- package/hooks/contract_version_guard.py +362 -0
- package/hooks/cost_meter.py +106 -0
- package/hooks/destructive-commands.json +62 -0
- package/hooks/destructive_guard.py +477 -0
- package/hooks/eval_compliance_check.py +440 -0
- package/hooks/guards.json +17 -0
- package/hooks/hooks.json +323 -0
- package/hooks/internal_double_gate.py +296 -0
- package/hooks/js_fp_review.py +212 -0
- package/hooks/knowledge_index.py +119 -0
- package/hooks/lib/__pycache__/artifact_paths.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/atomic_state.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/autocompact_config.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/boundary_events.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/doc_classification.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/gh_pr_create_detect.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/git_safe_diff.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/instrument_log.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/metrics_query.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/plugin_version.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/pre_commit_doc_classifier.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/review_agent_registry.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/review_gate_corroboration.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/review_gate_hash.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/review_verdicts.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/stdin_json.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/stryker_invocation.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/telemetry_consent.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/test_file_classify.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/token_efficiency_limits.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/verify_guard_state.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/xunit_v3_operator_gate.cpython-314.pyc +0 -0
- package/hooks/lib/agent_skill_hints.py +74 -0
- package/hooks/lib/artifact_paths.py +263 -0
- package/hooks/lib/atomic_state.py +557 -0
- package/hooks/lib/autocompact_config.py +103 -0
- package/hooks/lib/autoship_log.py +106 -0
- package/hooks/lib/banned_scripts_policy.py +51 -0
- package/hooks/lib/boundary_events.py +436 -0
- package/hooks/lib/build_knowledge_index.py +504 -0
- package/hooks/lib/build_skills_index.py +361 -0
- package/hooks/lib/build_state.py +116 -0
- package/hooks/lib/classify_ship_outcome.py +126 -0
- package/hooks/lib/config_changelog_schema.py +115 -0
- package/hooks/lib/cost_meter.py +955 -0
- package/hooks/lib/doc_classification.py +116 -0
- package/hooks/lib/gh_pr_create_detect.py +136 -0
- package/hooks/lib/git_safe_diff.py +123 -0
- package/hooks/lib/instrument_log.py +66 -0
- package/hooks/lib/iteration_journal_gate.py +197 -0
- package/hooks/lib/knowledge_index_paths.py +88 -0
- package/hooks/lib/mcp_json_repowise.py +177 -0
- package/hooks/lib/metrics_query.py +202 -0
- package/hooks/lib/minimal_yaml.py +434 -0
- package/hooks/lib/plugin_version.py +142 -0
- package/hooks/lib/pre_commit_detect.py +537 -0
- package/hooks/lib/pre_commit_doc_classifier.py +126 -0
- package/hooks/lib/pricing.py +118 -0
- package/hooks/lib/report_pdf.py +371 -0
- package/hooks/lib/review_agent_registry.py +142 -0
- package/hooks/lib/review_dispatch_ledger.py +101 -0
- package/hooks/lib/review_gate_corroboration.py +521 -0
- package/hooks/lib/review_gate_hash.py +252 -0
- package/hooks/lib/review_gate_normalized_hash.py +1115 -0
- package/hooks/lib/review_verdicts.py +301 -0
- package/hooks/lib/run_report.py +160 -0
- package/hooks/lib/skill_categories.yaml +125 -0
- package/hooks/lib/stdin_json.py +57 -0
- package/hooks/lib/stryker_invocation.py +102 -0
- package/hooks/lib/telemetry_consent.py +41 -0
- package/hooks/lib/telemetry_report.py +108 -0
- package/hooks/lib/test_file_classify.py +160 -0
- package/hooks/lib/token_efficiency_limits.py +51 -0
- package/hooks/lib/turn_identity.py +77 -0
- package/hooks/lib/verify_guard_state.py +110 -0
- package/hooks/lib/workflow_state.py +206 -0
- package/hooks/lib/xunit_v3_operator_gate.py +596 -0
- package/hooks/mcp_json_repowise_nudge.py +74 -0
- package/hooks/mutation_adapters/__init__.py +7 -0
- package/hooks/mutation_adapters/__pycache__/__init__.cpython-314.pyc +0 -0
- package/hooks/mutation_adapters/__pycache__/lib.cpython-314.pyc +0 -0
- package/hooks/mutation_adapters/__pycache__/mutmut.cpython-314.pyc +0 -0
- package/hooks/mutation_adapters/__pycache__/pitest.cpython-314.pyc +0 -0
- package/hooks/mutation_adapters/__pycache__/stryker.cpython-314.pyc +0 -0
- package/hooks/mutation_adapters/__pycache__/stryker_net.cpython-314.pyc +0 -0
- package/hooks/mutation_adapters/lib.py +478 -0
- package/hooks/mutation_adapters/mutmut.py +188 -0
- package/hooks/mutation_adapters/pitest.py +266 -0
- package/hooks/mutation_adapters/stryker.py +157 -0
- package/hooks/mutation_adapters/stryker_net.py +264 -0
- package/hooks/mutation_gate.py +193 -0
- package/hooks/mutation_testing_smoke_gate.py +371 -0
- package/hooks/pending_review_notify.py +121 -0
- package/hooks/phase_marker.py +138 -0
- package/hooks/post_compact_state_reinject.py +180 -0
- package/hooks/post_format.py +115 -0
- package/hooks/pre_commit_knowledge_index.py +128 -0
- package/hooks/pre_commit_review.py +66 -0
- package/hooks/pre_pr_review.py +694 -0
- package/hooks/pre_tool_guard.py +405 -0
- package/hooks/py.sh +73 -0
- package/hooks/refactor-bash-write-patterns.json +29 -0
- package/hooks/refactor_test_bash_guard.py +253 -0
- package/hooks/refactor_test_freeze_guard.py +139 -0
- package/hooks/refactor_test_revert_guard.py +186 -0
- package/hooks/repo_review_nudge.py +287 -0
- package/hooks/review_verdict_recorder.py +464 -0
- package/hooks/scan_bash_command_for_banned_scripts.py +428 -0
- package/hooks/scan_worktree_for_banned_scripts.py +238 -0
- package/hooks/session_learning_trigger.py +248 -0
- package/hooks/skills_index.py +126 -0
- package/hooks/stryker_xunit_shim_guard.py +571 -0
- package/hooks/subagent_completion_guard.py +309 -0
- package/hooks/subagent_skill_context.py +139 -0
- package/hooks/task_completion_metrics.py +216 -0
- package/hooks/tdd_guard.py +229 -0
- package/hooks/telemetry.py +341 -0
- package/hooks/token_efficiency_review.py +194 -0
- package/hooks/verify_guard.py +183 -0
- package/hooks/verify_guard_edit_marker.py +73 -0
- package/hooks/version_check.py +173 -0
- package/knowledge/accepted-risks-schema.md +98 -0
- package/knowledge/adr-decision-criteria.md +64 -0
- package/knowledge/adversarial-review-protocol.md +139 -0
- package/knowledge/agent-registry.md +228 -0
- package/knowledge/agent-review-methodology.md +80 -0
- package/knowledge/ai-friendly-repo-guidelines.md +67 -0
- package/knowledge/architecture-assessment.md +96 -0
- package/knowledge/artifact-lifecycle.md +57 -0
- package/knowledge/cd-maturity-model.md +82 -0
- package/knowledge/cd-test-architecture.md +190 -0
- package/knowledge/ci-cd-file-scope.md +24 -0
- package/knowledge/codegraph-vs-graphify.md +192 -0
- package/knowledge/component-test-patterns.md +139 -0
- package/knowledge/database-change-management.md +80 -0
- package/knowledge/database-test-patterns.md +79 -0
- package/knowledge/decision-defaults.md +88 -0
- package/knowledge/dependency-breaking-techniques.md +116 -0
- package/knowledge/deployment-pipeline.md +86 -0
- package/knowledge/design-smells.md +122 -0
- package/knowledge/directory-enumeration.md +38 -0
- package/knowledge/domain-modeling.md +123 -0
- package/knowledge/evidence-bundle.md +90 -0
- package/knowledge/exploratory-testing-field-guide.md +122 -0
- package/knowledge/failure-routing.md +28 -0
- package/knowledge/fixture-construction.md +56 -0
- package/knowledge/frontend-component-architecture.md +139 -0
- package/knowledge/gherkin-quality-review-dispatch.md +135 -0
- package/knowledge/index.json +6766 -0
- package/knowledge/internal-collaborator-doubling.md +101 -0
- package/knowledge/legacy-test-strategy.md +71 -0
- package/knowledge/long-run-waiting.md +66 -0
- package/knowledge/microservice-testing.md +71 -0
- package/knowledge/model-pricing.json +23 -0
- package/knowledge/mutation-score-formulas.md +60 -0
- package/knowledge/object-calisthenics.md +147 -0
- package/knowledge/oracle-provenance.md +94 -0
- package/knowledge/orchestrator-script-implementation.md +185 -0
- package/knowledge/owasp-detection.md +148 -0
- package/knowledge/plan-review-rubric.md +56 -0
- package/knowledge/proxy-connectivity.md +62 -0
- package/knowledge/reactive-effect-patterns.md +73 -0
- package/knowledge/recon-inventory-excludes.txt +32 -0
- package/knowledge/references/bdd-value-guide.md +61 -0
- package/knowledge/references/csharp-http-client-testing.md +264 -0
- package/knowledge/release-strategies.md +74 -0
- package/knowledge/report-output-location.md +117 -0
- package/knowledge/report-pdf-integration.md +63 -0
- package/knowledge/report-print.css +129 -0
- package/knowledge/report-template.md +114 -0
- package/knowledge/report-to-pdf.md +69 -0
- package/knowledge/request-processing-flow.md +63 -0
- package/knowledge/result-verification.md +52 -0
- package/knowledge/review-agent-output-contract.md +121 -0
- package/knowledge/review-lens-classification.md +113 -0
- package/knowledge/review-rubric.md +62 -0
- package/knowledge/review-template.md +104 -0
- package/knowledge/rule-fixtures/A02.insecure-random-js/negative.js +1 -0
- package/knowledge/rule-fixtures/A02.insecure-random-js/positive.js +1 -0
- package/knowledge/rule-fixtures/A02.weak-hashing-md5/negative.py +1 -0
- package/knowledge/rule-fixtures/A02.weak-hashing-md5/positive.py +1 -0
- package/knowledge/rule-fixtures/A03.command-injection/negative.js +1 -0
- package/knowledge/rule-fixtures/A03.command-injection/positive.js +1 -0
- package/knowledge/rule-fixtures/A03.sql-injection/negative.js +1 -0
- package/knowledge/rule-fixtures/A03.sql-injection/positive.js +1 -0
- package/knowledge/rule-fixtures/A03.xss-innerhtml/negative.js +1 -0
- package/knowledge/rule-fixtures/A03.xss-innerhtml/positive.js +1 -0
- package/knowledge/rule-fixtures/A05.cors-wildcard/negative.js +1 -0
- package/knowledge/rule-fixtures/A05.cors-wildcard/positive.js +1 -0
- package/knowledge/rule-fixtures/A05.default-credentials/negative.js +1 -0
- package/knowledge/rule-fixtures/A05.default-credentials/positive.js +1 -0
- package/knowledge/rule-fixtures/A07.jwt-alg-none/negative.js +1 -0
- package/knowledge/rule-fixtures/A07.jwt-alg-none/positive.js +1 -0
- package/knowledge/rule-fixtures/A08.binary-formatter/negative.cs +1 -0
- package/knowledge/rule-fixtures/A08.binary-formatter/positive.cs +1 -0
- package/knowledge/rule-fixtures/A08.js-eval/negative.js +1 -0
- package/knowledge/rule-fixtures/A08.js-eval/positive.js +1 -0
- package/knowledge/rule-fixtures/A08.object-input-stream/negative.java +1 -0
- package/knowledge/rule-fixtures/A08.object-input-stream/positive.java +1 -0
- package/knowledge/schemas/disposition-register-v1.json +65 -0
- package/knowledge/schemas/recon-envelope-v1.json +198 -0
- package/knowledge/schemas/unified-finding-v1.json +72 -0
- package/knowledge/security-primitives-contract.md +301 -0
- package/knowledge/security-review-rule-map.yaml +107 -0
- package/knowledge/skills-registry.md +72 -0
- package/knowledge/task-size-classifier.md +103 -0
- package/knowledge/telemetry-schema.md +881 -0
- package/knowledge/test-automation-maturity.md +56 -0
- package/knowledge/test-automation-principles.md +71 -0
- package/knowledge/test-cadence-tradeoffs.md +68 -0
- package/knowledge/test-doubles.md +105 -0
- package/knowledge/test-file-indicators.md +22 -0
- package/knowledge/test-layer-gates.md +35 -0
- package/knowledge/test-matrix-examples/django-batch.md +24 -0
- package/knowledge/test-matrix-examples/dotnet-grpc-fronting-api.md +90 -0
- package/knowledge/test-matrix-examples/dotnet-http-consumer.md +131 -0
- package/knowledge/test-matrix-examples/react-node-spa.md +24 -0
- package/knowledge/test-matrix-examples/spring-boot-service.md +25 -0
- package/knowledge/test-matrix-examples/ssr-htmx.md +24 -0
- package/knowledge/test-organization.md +70 -0
- package/knowledge/test-pyramid.md +84 -0
- package/knowledge/test-refactoring.md +67 -0
- package/knowledge/test-review-division-of-labor.md +85 -0
- package/knowledge/test-smells.md +80 -0
- package/knowledge/test-stack-profiles/bdd-frameworks.md +235 -0
- package/knowledge/test-stack-profiles/django.md +13 -0
- package/knowledge/test-stack-profiles/dotnet.md +18 -0
- package/knowledge/test-stack-profiles/go.md +16 -0
- package/knowledge/test-stack-profiles/node.md +16 -0
- package/knowledge/test-stack-profiles/react.md +12 -0
- package/knowledge/test-stack-profiles/spring-boot.md +16 -0
- package/knowledge/test-stack-profiles/ssr-htmx.md +14 -0
- package/knowledge/test-stack-profiles/vue.md +12 -0
- package/knowledge/test-strategy.md +70 -0
- package/knowledge/testability-patterns.md +240 -0
- package/knowledge/testing-quadrants.md +44 -0
- package/knowledge/testing-techniques/approval.md +15 -0
- package/knowledge/testing-techniques/chaos.md +17 -0
- package/knowledge/testing-techniques/fuzz.md +15 -0
- package/knowledge/testing-techniques/property-based.md +15 -0
- package/knowledge/testing-techniques/schema-validation.md +15 -0
- package/knowledge/testing-techniques/screenshot.md +15 -0
- package/knowledge/three-phase-workflow.md +198 -0
- package/knowledge/value-patterns.md +55 -0
- package/knowledge/verification-mode.md +116 -0
- package/knowledge/virtual-service-libraries.md +75 -0
- package/knowledge/wave-consolidation-guidance.md +21 -0
- package/overrides/agents/Explore.md +15 -0
- package/overrides/agents/general-purpose.md +10 -0
- package/overrides/notes/autoship.md +6 -0
- package/overrides/notes/issues-from-assessment.md +3 -0
- package/overrides/notes/issues-from-plan.md +3 -0
- package/overrides/notes/mutation-night-watch.md +3 -0
- package/overrides/notes/mutation-testing.md +3 -0
- package/overrides/notes/pr.md +7 -0
- package/overrides/notes/project-init.md +6 -0
- package/overrides/notes/setup.md +13 -0
- package/overrides/notes/specs.md +3 -0
- package/overrides/skills/headless-run/SKILL.md +45 -0
- package/overrides/skills/upgrade/SKILL.md +30 -0
- package/overrides/skills/version/SKILL.md +25 -0
- package/package.json +36 -0
- package/scripts/authoring_digest.py +93 -0
- package/scripts/autoship_discover.py +121 -0
- package/scripts/autoship_group.py +409 -0
- package/scripts/autoship_proposals.py +494 -0
- package/scripts/autoship_queue.py +291 -0
- package/scripts/autoship_reclaim.py +495 -0
- package/scripts/build_jobs.py +108 -0
- package/scripts/build_rollback_point.py +240 -0
- package/scripts/build_slice_scope.py +157 -0
- package/scripts/build_wave.py +109 -0
- package/scripts/build_wave_reconcile.py +252 -0
- package/scripts/build_worktree_baseref.py +113 -0
- package/scripts/check_agent_scope.py +117 -0
- package/scripts/check_agent_tool_mapping.py +213 -0
- package/scripts/check_review_agent_mcp_tools.py +317 -0
- package/scripts/check_security_assessment_mcp_tools.py +165 -0
- package/scripts/checkpoint_abort.py +502 -0
- package/scripts/claude_setup_review.py +438 -0
- package/scripts/codebase_recon.py +556 -0
- package/scripts/coverage_config.py +623 -0
- package/scripts/coverage_delta_steering.py +330 -0
- package/scripts/coverage_discovery_dotnet.py +315 -0
- package/scripts/coverage_discovery_java.py +742 -0
- package/scripts/coverage_discovery_js.py +546 -0
- package/scripts/coverage_gap_ranking.py +556 -0
- package/scripts/coverage_readiness.py +455 -0
- package/scripts/coverage_report_parse.py +521 -0
- package/scripts/detect_bdd_convention.py +252 -0
- package/scripts/eval_ablation.py +376 -0
- package/scripts/gherkin_analysis_coverage_gate.py +306 -0
- package/scripts/gherkin_cross_feature_duplicate_titles_gate.py +173 -0
- package/scripts/gherkin_effectiveness_rollup.py +238 -0
- package/scripts/gherkin_failure_path_gate.py +206 -0
- package/scripts/gherkin_feature_merge.py +720 -0
- package/scripts/gherkin_stub_gate.py +163 -0
- package/scripts/gherkin_stub_merge.py +479 -0
- package/scripts/git_origin_host.py +88 -0
- package/scripts/install-java-static-analysis.py +110 -0
- package/scripts/issue_deps.py +74 -0
- package/scripts/lib/_bdd_markers.py +28 -0
- package/scripts/lib/_gherkin_text.py +93 -0
- package/scripts/lib/_vendored_tree.py +70 -0
- package/scripts/lib/autoship_state.py +397 -0
- package/scripts/lib/claude_md_guard.py +226 -0
- package/scripts/lib/deterministic_recon.py +446 -0
- package/scripts/lib/mcp_tool_grants.py +211 -0
- package/scripts/lib/plan_parse.py +386 -0
- package/scripts/lib/review_result.py +84 -0
- package/scripts/lib/review_roster.py +86 -0
- package/scripts/lib/session_log/__init__.py +34 -0
- package/scripts/lib/session_log/__pycache__/__init__.cpython-314.pyc +0 -0
- package/scripts/lib/session_log/__pycache__/records.cpython-314.pyc +0 -0
- package/scripts/lib/session_log/classify.py +231 -0
- package/scripts/lib/session_log/corrections.py +194 -0
- package/scripts/lib/session_log/discovery.py +108 -0
- package/scripts/lib/session_log/records.py +218 -0
- package/scripts/lib/session_log/redact.py +76 -0
- package/scripts/lib/session_log/signals.py +373 -0
- package/scripts/lib/session_report_downstream.py +614 -0
- package/scripts/lib/session_report_maintainer.py +1273 -0
- package/scripts/lib/session_report_shared.py +262 -0
- package/scripts/lib/settings_hook_guard.py +157 -0
- package/scripts/lib/slug.py +33 -0
- package/scripts/lib/stub_extractors/__init__.py +82 -0
- package/scripts/lib/stub_extractors/_common.py +328 -0
- package/scripts/lib/stub_extractors/csharp.py +19 -0
- package/scripts/lib/stub_extractors/go.py +173 -0
- package/scripts/lib/stub_extractors/java.py +18 -0
- package/scripts/lib/stub_extractors/jsts.py +126 -0
- package/scripts/mutation_stack_sections.py +149 -0
- package/scripts/mutation_yield_steering.py +345 -0
- package/scripts/orchestrator.py +895 -0
- package/scripts/plan_gherkin_export.py +227 -0
- package/scripts/plan_waves.py +208 -0
- package/scripts/pr_close_keyword_lint.py +108 -0
- package/scripts/progress_guardian.py +888 -0
- package/scripts/recon_inventory.py +273 -0
- package/scripts/review_findings_log.py +93 -0
- package/scripts/run_invariants.py +124 -0
- package/scripts/select_lenses.py +640 -0
- package/scripts/session_report.py +486 -0
- package/scripts/set_autocompact_env.py +221 -0
- package/scripts/ship_resume_guard.py +135 -0
- package/scripts/ship_review_gate.py +63 -0
- package/scripts/specs_convention_marker.py +103 -0
- package/scripts/test_improve_resume.py +277 -0
- package/scripts/test_review_mechanics.py +958 -0
- package/scripts/token_efficiency_review.py +322 -0
- package/scripts/verdict_scope.py +285 -0
- package/scripts/verify_gherkin_quality_critic_isolation.py +296 -0
- package/scripts/verify_tier.py +157 -0
- package/skills/adr-tools/SKILL.md +118 -0
- package/skills/agent-readiness/SKILL.md +105 -0
- package/skills/agent-readiness/ai_friendly_analyzers.py +326 -0
- package/skills/agent-readiness/scanner.py +441 -0
- package/skills/agent-readiness/scorecard.yaml +88 -0
- package/skills/api-design/SKILL.md +115 -0
- package/skills/apply-fixes/SKILL.md +171 -0
- package/skills/apply-test-doubles/SKILL.md +321 -0
- package/skills/artifact-lifecycle/SKILL.md +127 -0
- package/skills/autoship/SKILL.md +1124 -0
- package/skills/benchmark/SKILL.md +105 -0
- package/skills/branch-workflow/SKILL.md +89 -0
- package/skills/browse/SKILL.md +184 -0
- package/skills/browser-testing/SKILL.md +62 -0
- package/skills/browser-testing/references/playwright-patterns.md +216 -0
- package/skills/build/SKILL.md +422 -0
- package/skills/build/references/static-self-heal.md +245 -0
- package/skills/careful/SKILL.md +72 -0
- package/skills/cd-test-architecture/SKILL.md +371 -0
- package/skills/ci-debugging/SKILL.md +105 -0
- package/skills/co-evolution-audit/SKILL.md +269 -0
- package/skills/code-review/SKILL.md +1015 -0
- package/skills/code-review/examples/aggregated-sample.json +56 -0
- package/skills/code-review/examples/sample-report.md +41 -0
- package/skills/code-review/output-format.md +478 -0
- package/skills/code-review/scripts/activation.py +86 -0
- package/skills/code-review/scripts/change_impact.py +357 -0
- package/skills/code-review/scripts/change_shape.py +372 -0
- package/skills/code-review/scripts/change_size.py +212 -0
- package/skills/code-review/scripts/changed_file_list.py +141 -0
- package/skills/code-review/scripts/closing_pass.py +187 -0
- package/skills/code-review/scripts/consolidate.py +277 -0
- package/skills/code-review/scripts/contract_failure_report.py +185 -0
- package/skills/code-review/scripts/dispatch_reconcile.py +66 -0
- package/skills/code-review/scripts/dispatch_waves.py +164 -0
- package/skills/code-review/scripts/finding_signature.py +446 -0
- package/skills/code-review/scripts/ledger.py +283 -0
- package/skills/code-review/scripts/partition.py +169 -0
- package/skills/code-review/scripts/render_tiered_findings.py +274 -0
- package/skills/code-review/scripts/repo_invariants.py +1066 -0
- package/skills/code-review/scripts/review_context_pack.py +306 -0
- package/skills/code-review/scripts/review_round_log.py +345 -0
- package/skills/code-review/scripts/review_value_coverage.py +297 -0
- package/skills/code-review/scripts/validate_review_output.py +467 -0
- package/skills/code-review/sliced-mode.md +205 -0
- package/skills/competitive-analysis/SKILL.md +191 -0
- package/skills/context-loading-protocol/SKILL.md +157 -0
- package/skills/continue/SKILL.md +90 -0
- package/skills/cost-report/SKILL.md +178 -0
- package/skills/coverage-baseline/SKILL.md +335 -0
- package/skills/coverage-baseline/references/multi-project-discovery.md +202 -0
- package/skills/coverage-delta/SKILL.md +181 -0
- package/skills/coverage-delta/references/mutation-gate.md +70 -0
- package/skills/design-doc/SKILL.md +95 -0
- package/skills/design-interrogation/SKILL.md +89 -0
- package/skills/design-it-twice/SKILL.md +91 -0
- package/skills/docker-image-audit/SKILL.md +108 -0
- package/skills/docker-image-audit/references/install-guide.md +64 -0
- package/skills/docker-image-audit/references/report-template.md +73 -0
- package/skills/docker-image-create/SKILL.md +185 -0
- package/skills/domain-analysis/SKILL.md +183 -0
- package/skills/domain-driven-design/SKILL.md +194 -0
- package/skills/exploratory-testing/SKILL.md +108 -0
- package/skills/explore/SKILL.md +51 -0
- package/skills/farley-score/SKILL.md +165 -0
- package/skills/feature-file-validation/SKILL.md +78 -0
- package/skills/feature-file-validation/references/validation-rules.md +115 -0
- package/skills/feedback-learning/SKILL.md +414 -0
- package/skills/fix/SKILL.md +450 -0
- package/skills/freeze/SKILL.md +68 -0
- package/skills/frontend-architecture/SKILL.md +113 -0
- package/skills/gherkin-derive/SKILL.md +630 -0
- package/skills/gherkin-public/SKILL.md +266 -0
- package/skills/governance-compliance/SKILL.md +150 -0
- package/skills/guard/SKILL.md +75 -0
- package/skills/handoff/SKILL.md +139 -0
- package/skills/handoff/references/summary-templates.md +242 -0
- package/skills/harness-audit/SKILL.md +751 -0
- package/skills/harness-audit/scripts/lesson_validate.py +386 -0
- package/skills/harness-audit/scripts/redundancy_criterion.py +188 -0
- package/skills/headless-run/SKILL.md +45 -0
- package/skills/headless-run/scripts/isolated_dispatch.py +381 -0
- package/skills/help/SKILL.md +72 -0
- package/skills/hexagonal-architecture/SKILL.md +85 -0
- package/skills/human-oversight-protocol/SKILL.md +224 -0
- package/skills/issues-from-assessment/SKILL.md +223 -0
- package/skills/issues-from-plan/SKILL.md +133 -0
- package/skills/legacy-code/SKILL.md +132 -0
- package/skills/mermaid-diagramming/SKILL.md +120 -0
- package/skills/mutation-night-watch/SKILL.md +154 -0
- package/skills/mutation-night-watch/references/scheduling.md +135 -0
- package/skills/mutation-testing/SKILL.md +396 -0
- package/skills/mutation-testing/references/languages/csharp-stryker-net.md +676 -0
- package/skills/mutation-testing/references/languages/go-go-mutesting.md +95 -0
- package/skills/mutation-testing/references/languages/java-pitest.md +77 -0
- package/skills/mutation-testing/references/languages/javascript-stryker.md +188 -0
- package/skills/mutation-testing/references/languages/python-mutmut.md +97 -0
- package/skills/mutation-testing/references/time-estimation.md +34 -0
- package/skills/mutation-testing/references/tool-detection.md +15 -0
- package/skills/mutation-testing/references/workflow-callers.md +23 -0
- package/skills/mutation-testing/scripts/__pycache__/xunit_v3_feature_detector.cpython-314.pyc +0 -0
- package/skills/mutation-testing/scripts/csharp_stryker_net_slice_runner.py +635 -0
- package/skills/mutation-testing/scripts/csharp_stryker_net_status_loop.py +525 -0
- package/skills/mutation-testing/scripts/csharp_stryker_net_wrapper.py +681 -0
- package/skills/mutation-testing/scripts/mutation_baseline_reuse.py +292 -0
- package/skills/mutation-testing/scripts/mutation_exclude_policy.py +268 -0
- package/skills/mutation-testing/scripts/mutation_feasibility_gate.py +463 -0
- package/skills/mutation-testing/scripts/mutation_kill_headless.py +331 -0
- package/skills/mutation-testing/scripts/mutation_kill_insert.py +199 -0
- package/skills/mutation-testing/scripts/mutation_kill_insert_python.py +150 -0
- package/skills/mutation-testing/scripts/mutation_kill_loop.py +869 -0
- package/skills/mutation-testing/scripts/mutation_kill_loop_python.py +949 -0
- package/skills/mutation-testing/scripts/mutation_kill_retry.py +592 -0
- package/skills/mutation-testing/scripts/mutation_kill_shared.py +620 -0
- package/skills/mutation-testing/scripts/mutation_nightwatch.py +462 -0
- package/skills/mutation-testing/scripts/mutation_nightwatch_stacks.py +425 -0
- package/skills/mutation-testing/scripts/mutation_report.py +743 -0
- package/skills/mutation-testing/scripts/mutation_report_cli.py +175 -0
- package/skills/mutation-testing/scripts/mutation_safety_gate.py +69 -0
- package/skills/mutation-testing/scripts/stryker_shard_pipeline.py +847 -0
- package/skills/mutation-testing/scripts/stryker_shard_setup.py +440 -0
- package/skills/mutation-testing/scripts/stryker_timeout_retry.py +142 -0
- package/skills/mutation-testing/scripts/xunit_v3_feature_detector.py +341 -0
- package/skills/performance-benchmark/SKILL.md +174 -0
- package/skills/performance-benchmark/examples/report-format.md +43 -0
- package/skills/performance-benchmark/references/benchmark-script.md +169 -0
- package/skills/performance-metrics/SKILL.md +265 -0
- package/skills/plan/SKILL.md +199 -0
- package/skills/plan/references/gherkin-persistence.md +43 -0
- package/skills/plan/references/plan-template.md +182 -0
- package/skills/pr/SKILL.md +289 -0
- package/skills/pr/scripts/gate_retry_state.py +368 -0
- package/skills/project-init/README.md +141 -0
- package/skills/project-init/SKILL.md +1197 -0
- package/skills/project-init/evals/evals.json +200 -0
- package/skills/project-init/references/capability-tools.md +55 -0
- package/skills/project-init/references/configs.md +221 -0
- package/skills/property-based-testing/SKILL.md +121 -0
- package/skills/property-based-testing/fixtures/invariant_fixture.py +15 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/README.md +42 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/fast-check/LICENSE +21 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/fast-check/README.md +263 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/fast-check/lib/cjs/fast-check.d.ts +5165 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/fast-check/lib/cjs/fast-check.js +12147 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/fast-check/lib/cjs/package.json +3 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/fast-check/lib/cjs/types57/fast-check.d.ts +5165 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/fast-check/lib/fast-check.d.ts +5165 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/fast-check/lib/fast-check.js +12011 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/fast-check/lib/rolldown-runtime-D7D4PA-g.js +13 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/fast-check/lib/types57/fast-check.d.ts +5165 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/fast-check/package.json +94 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/LICENSE +21 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/README.md +168 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/RandomGenerator-DcXj09Ch.d.ts +14 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/distribution/uniformBigInt.d.ts +15 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/distribution/uniformBigInt.js +38 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/distribution/uniformFloat32.d.ts +15 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/distribution/uniformFloat32.js +18 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/distribution/uniformFloat64.d.ts +15 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/distribution/uniformFloat64.js +22 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/distribution/uniformInt.d.ts +15 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/distribution/uniformInt.js +134 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/RandomGenerator-DcXj09Ch.d.ts +14 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/distribution/uniformBigInt.d.ts +15 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/distribution/uniformBigInt.js +37 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/distribution/uniformFloat32.d.ts +15 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/distribution/uniformFloat32.js +17 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/distribution/uniformFloat64.d.ts +15 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/distribution/uniformFloat64.js +21 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/distribution/uniformInt.d.ts +15 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/distribution/uniformInt.js +133 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/generator/congruential32.d.ts +7 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/generator/congruential32.js +44 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/generator/mersenne.d.ts +7 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/generator/mersenne.js +90 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/generator/xoroshiro128plus.d.ts +7 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/generator/xoroshiro128plus.js +80 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/generator/xorshift128plus.d.ts +7 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/generator/xorshift128plus.js +78 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/package.json +3 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/types/JumpableRandomGenerator.d.ts +16 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/types/JumpableRandomGenerator.js +0 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/types/RandomGenerator.d.ts +2 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/types/RandomGenerator.js +0 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/utils/generateN.d.ts +6 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/utils/generateN.js +8 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/utils/purify.d.ts +12 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/utils/purify.js +9 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/utils/skipN.d.ts +6 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/utils/skipN.js +6 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/generator/congruential32.d.ts +7 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/generator/congruential32.js +46 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/generator/mersenne.d.ts +7 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/generator/mersenne.js +92 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/generator/xoroshiro128plus.d.ts +7 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/generator/xoroshiro128plus.js +82 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/generator/xorshift128plus.d.ts +7 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/generator/xorshift128plus.js +80 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/types/JumpableRandomGenerator.d.ts +16 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/types/JumpableRandomGenerator.js +0 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/types/RandomGenerator.d.ts +2 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/types/RandomGenerator.js +0 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/utils/generateN.d.ts +6 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/utils/generateN.js +9 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/utils/purify.d.ts +12 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/utils/purify.js +10 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/utils/skipN.d.ts +6 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/utils/skipN.js +7 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/package.json +133 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/package-lock.json +1179 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/package.json +14 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/roundtrip.js +29 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/roundtrip.properties.test.js +16 -0
- package/skills/property-based-testing/fixtures/no_property_fixture.py +10 -0
- package/skills/property-based-testing/fixtures/roundtrip_fixture.py +16 -0
- package/skills/property-based-testing/references/languages/javascript.md +54 -0
- package/skills/property-based-testing/scripts/detect_and_dispatch.py +80 -0
- package/skills/property-based-testing/scripts/hypothesis_scaffold.py +276 -0
- package/skills/proxy-resilience/SKILL.md +84 -0
- package/skills/quality-gate-pipeline/SKILL.md +184 -0
- package/skills/quality-targets-converge/SKILL.md +254 -0
- package/skills/repo-review/SKILL.md +159 -0
- package/skills/report-pdf/SKILL.md +66 -0
- package/skills/review/SKILL.md +47 -0
- package/skills/review-agent/SKILL.md +152 -0
- package/skills/review-summary/SKILL.md +73 -0
- package/skills/run-report/SKILL.md +70 -0
- package/skills/semantic-duplication-scan/SKILL.md +337 -0
- package/skills/semantic-scan/SKILL.md +53 -0
- package/skills/semgrep-analyze/SKILL.md +139 -0
- package/skills/setup/SKILL.md +1122 -0
- package/skills/ship/SKILL.md +240 -0
- package/skills/source-verification/SKILL.md +210 -0
- package/skills/source-verification/scripts/claim_extractor.py +155 -0
- package/skills/specs/.size-baseline.json +4 -0
- package/skills/specs/SKILL.md +243 -0
- package/skills/specs/references/completeness-checklist.md +83 -0
- package/skills/specs/references/extraction.md +58 -0
- package/skills/specs/references/glossary.md +59 -0
- package/skills/specs/references/persistence.md +115 -0
- package/skills/specs/references/predictability-check.md +77 -0
- package/skills/static-analysis-integration/SKILL.md +235 -0
- package/skills/static-analysis-integration/adapters/_envelope.py +26 -0
- package/skills/static-analysis-integration/adapters/jscpd-adapter.py +66 -0
- package/skills/static-analysis-integration/adapters/lizard-adapter.py +81 -0
- package/skills/static-analysis-integration/adapters/mypy-adapter.py +50 -0
- package/skills/static-analysis-integration/adapters/mypy-src-layout.py +93 -0
- package/skills/static-analysis-integration/adapters/security-review-adapter.py +212 -0
- package/skills/static-analysis-integration/maintenance.md +23 -0
- package/skills/static-analysis-integration/references/language-setup.md +228 -0
- package/skills/static-analysis-integration/references/sarif-parser.md +124 -0
- package/skills/static-analysis-integration/references/security-review-adapter.md +118 -0
- package/skills/static-analysis-integration/references/tool-configs.md +617 -0
- package/skills/static-analysis-integration/rulesets/pmd-quickstart.xml +24 -0
- package/skills/stryker-xunit-v2-shim/SKILL.md +274 -0
- package/skills/stryker-xunit-v2-shim/references/shim-howto.md +256 -0
- package/skills/stryker-xunit-v2-shim/scripts/generate_shim.py +143 -0
- package/skills/systematic-debugging/SKILL.md +130 -0
- package/skills/telemetry/SKILL.md +75 -0
- package/skills/test-audit-disable/SKILL.md +129 -0
- package/skills/test-design/SKILL.md +177 -0
- package/skills/test-design/scripts/__pycache__/internal_double_detector.cpython-314.pyc +0 -0
- package/skills/test-design/scripts/internal_double_detector.py +631 -0
- package/skills/test-design-advisor/SKILL.md +166 -0
- package/skills/test-driven-development/SKILL.md +169 -0
- package/skills/test-health/SKILL.md +262 -0
- package/skills/test-improve/SKILL.md +239 -0
- package/skills/test-improve/references/phase-0-approach-contract.md +228 -0
- package/skills/test-improve/references/phase-1-analyze.md +131 -0
- package/skills/test-improve/references/phase-2-baseline.md +121 -0
- package/skills/test-improve/references/phase-3-derive-gherkin.md +53 -0
- package/skills/test-improve/references/phase-4-plan-fixes.md +34 -0
- package/skills/test-improve/references/phase-5-improve.md +215 -0
- package/skills/test-improve/references/phase-6-refactor-decision.md +45 -0
- package/skills/test-improve/references/phase-7-refactor.md +44 -0
- package/skills/test-improve/references/phase-8-validate.md +66 -0
- package/skills/test-improve/references/phase-9-close-out-prompt.md +11 -0
- package/skills/test-improve/references/phase-9-report.md +62 -0
- package/skills/test-improve/references/review-loop.md +92 -0
- package/skills/test-improve/templates/executive-summary.md +123 -0
- package/skills/threat-modeling/SKILL.md +108 -0
- package/skills/triage/SKILL.md +211 -0
- package/skills/ubiquitous-language/SKILL.md +192 -0
- package/skills/ubiquitous-language/scripts/collect_domain_signals.py +300 -0
- package/skills/unfreeze/SKILL.md +37 -0
- package/skills/upgrade/SKILL.md +31 -0
- package/skills/upgrade/scripts/check_version_drift.py +113 -0
- package/skills/upgrade/scripts/enable_autoupdate.py +149 -0
- package/skills/version/SKILL.md +25 -0
- package/sync/__pycache__/sync_upstream.cpython-314.pyc +0 -0
- package/sync/sync_upstream.py +293 -0
- package/templates/ACCEPTED-RISKS.md.tmpl +46 -0
- package/templates/agents/agent-template.md +151 -0
- package/templates/agents/angular-testing.md +66 -0
- package/templates/agents/csharp-quality.md +63 -0
- package/templates/agents/esm-enforcer.md +52 -0
- package/templates/agents/front-end-testing.md +65 -0
- package/templates/agents/go-quality.md +65 -0
- package/templates/agents/python-quality.md +62 -0
- package/templates/agents/react-testing.md +61 -0
- package/templates/agents/ts-enforcer.md +60 -0
- package/templates/agents/twelve-factor-audit.md +49 -0
- package/tools/entropy-check.py +250 -0
- package/tools/model-hash-verify.py +213 -0
|
@@ -0,0 +1,694 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""pre_pr_review — Claude Code PreToolUse:Bash hook (#1886).
|
|
3
|
+
|
|
4
|
+
Successor to `hooks/pre_commit_review.py`'s review-corroboration gate.
|
|
5
|
+
Design decision (issue #1886, resolved): the gate moves from `git commit` to
|
|
6
|
+
`gh pr create` — a branch is now free to accumulate any number of local
|
|
7
|
+
commits without paying the review-panel cost at each one; the hard block
|
|
8
|
+
fires once, against the branch's FULL diff vs. its base, at PR-creation
|
|
9
|
+
time.
|
|
10
|
+
|
|
11
|
+
WHY A NEW MODULE RATHER THAN REPURPOSING `pre_commit_review.py` IN PLACE
|
|
12
|
+
(documenting the choice the issue explicitly left open): `pre_commit_review.py`
|
|
13
|
+
grew, across #1461/#1476/#1627/#1763/#1801 and further re-reviews, an enormous
|
|
14
|
+
amount of machinery specific to `git commit`'s command shape — per-target
|
|
15
|
+
`-C`/`--git-dir`/`--work-tree` resolution, `-a`/pathspec-form-commit detection,
|
|
16
|
+
cosmetic-delta carry-forward across re-stages, and a `--no-verify`/`-n`
|
|
17
|
+
flag-based bypass. None of that transfers to `gh pr create`, which has no `-C`
|
|
18
|
+
equivalent, no pathspec-commit-shaped ambiguity, and no native
|
|
19
|
+
`--no-verify`-shaped flag to key a bypass off. Reusing that file's own
|
|
20
|
+
git-commit-specific detector/hash pair in place would have meant either
|
|
21
|
+
carrying dead complexity forward or performing a much larger, riskier
|
|
22
|
+
in-place rewrite than standing up a new, deliberately leaner module that
|
|
23
|
+
reuses the SHARED library pieces (`review_gate_hash.py`,
|
|
24
|
+
`review_gate_corroboration.py`, `boundary_events.py`, `atomic_state.py`,
|
|
25
|
+
`artifact_paths.py`) both hooks depend on. `pre_commit_review.py` itself is
|
|
26
|
+
now a documented no-op (see its own module docstring) rather than deleted,
|
|
27
|
+
since deleting it would strand the many docs/tests/skills that still name it
|
|
28
|
+
in prose describing this exact migration.
|
|
29
|
+
|
|
30
|
+
WHAT CHANGED, MECHANICALLY, FROM THE COMMIT-TIME GATE:
|
|
31
|
+
|
|
32
|
+
- **Trigger**: `gh pr create` (`hooks/lib/gh_pr_create_detect.py`) instead of
|
|
33
|
+
`git commit`. Any other Bash command passes through immediately (exit 0).
|
|
34
|
+
- **Hash**: `hooks/lib/review_gate_hash.py`'s `branch_diff_gate_hash(base_ref,
|
|
35
|
+
cwd)` — `git diff <base>...HEAD`, the WHOLE branch diff against its
|
|
36
|
+
merge-base with the resolved default branch (`default_base_ref()`) —
|
|
37
|
+
instead of `git diff --cached` (one commit's staged patch).
|
|
38
|
+
- **Recency window anchor**: `before_ts` is the CURRENT time at
|
|
39
|
+
`gh pr create` invocation, not a gate file's own mtime (design decision,
|
|
40
|
+
#1886: "anchor to before PR creation"). A multi-commit branch may have been
|
|
41
|
+
reviewed well before its final commit; anchoring on "now" rather than on
|
|
42
|
+
when some earlier `/code-review` run happened to write the gate file means
|
|
43
|
+
the window's only real question is "did genuine, distinct review-agent
|
|
44
|
+
dispatches occur recently, for THIS exact branch-diff content" — not "did
|
|
45
|
+
they happen within `WINDOW_SECONDS` of an arbitrary earlier file write".
|
|
46
|
+
- **Gate file**: `.claude/memory/.pr-review-passed` (new; distinct from
|
|
47
|
+
`.claude/memory/.review-passed`, whose only remaining writer/reader is now
|
|
48
|
+
historical prose — see `pre_commit_review.py`'s docstring).
|
|
49
|
+
- **Bypass**: `PR_GATE_BYPASS_REASON` (an env var, not a `--no-verify`/`-n`
|
|
50
|
+
command-line flag — `gh pr create` has no such native flag to key off).
|
|
51
|
+
Mirrors #709's pattern for the ORIGINAL commit-time gate: when set to a
|
|
52
|
+
non-empty value, the PR is allowed and the bypass is durably logged to
|
|
53
|
+
`.claude/metrics/gate-bypass-audit.jsonl` (the SAME stream, distinguished
|
|
54
|
+
by `triggeredBy: "PR_GATE_BYPASS_REASON"`); when unset, the ordinary gate
|
|
55
|
+
evaluation applies — there is no separate "you tried to bypass but forgot
|
|
56
|
+
the reason" message, because there is no separate flag to detect trying
|
|
57
|
+
without one.
|
|
58
|
+
|
|
59
|
+
WHAT DID NOT CHANGE: the underlying corroboration architecture is reused
|
|
60
|
+
as-is from `hooks/lib/review_gate_corroboration.py` — >= 1 distinct,
|
|
61
|
+
registered review-agent dispatch (or a sanctioned doc-only/single-agent
|
|
62
|
+
exemption) recorded in the recency window, live-registry re-validated, with
|
|
63
|
+
the same #1763 dispatch-failure veto taking priority over every exemption.
|
|
64
|
+
See that module's own docstring for the full account of why this raises the
|
|
65
|
+
bar without cryptographically closing #1461's self-certification gap.
|
|
66
|
+
|
|
67
|
+
`_MIN_DISTINCT_DISPATCHES` was lowered from 2 to 1 (#2147): a single
|
|
68
|
+
`/code-review` invocation already dispatches a full multi-agent panel
|
|
69
|
+
internally, so requiring a SECOND, separate top-level dispatch on top of
|
|
70
|
+
that bought no additional corroboration — it only produced false blocks on
|
|
71
|
+
a legitimately-reviewed branch (e.g. a single `--agent <name>` review, or a
|
|
72
|
+
session whose panel dispatch shape records fewer ledger rows than a prior
|
|
73
|
+
plugin version did). The single-agent exemption event
|
|
74
|
+
(`single-agent-review-exempt`) is consequently now redundant with the
|
|
75
|
+
ordinary count check for the case it was built for — kept anyway, since it
|
|
76
|
+
is a harmless, already-audited alternate path, not because anything still
|
|
77
|
+
requires it.
|
|
78
|
+
|
|
79
|
+
DELIBERATELY DROPPED, not carried forward from `pre_commit_review.py`
|
|
80
|
+
(explicit scope decisions, not oversights):
|
|
81
|
+
|
|
82
|
+
1. **Cosmetic-delta carry-forward** (#1627): existed to stop a whitespace-only
|
|
83
|
+
re-stage from forcing a fresh review-agent dispatch before the NEXT commit.
|
|
84
|
+
That friction was a direct consequence of gating every commit; since this
|
|
85
|
+
gate fires once, at PR-creation time, against the branch's cumulative
|
|
86
|
+
diff, the scenario it existed to relieve no longer arises here.
|
|
87
|
+
2. **Multi-target `-C`/`--git-dir`/`--work-tree` resolution** (#1801): a
|
|
88
|
+
`git commit` can retarget which repository it commits to; `gh pr create`
|
|
89
|
+
has no equivalent multi-repository ambiguity — it always targets the
|
|
90
|
+
current repository's configured remote.
|
|
91
|
+
3. **`-a`/pathspec-form-commit detection** (#1476): specific to `git commit`
|
|
92
|
+
committing tracked-file changes that were never `git add`-ed; has no
|
|
93
|
+
`gh pr create` analogue (a PR is always opened against whatever is
|
|
94
|
+
already committed to the branch).
|
|
95
|
+
|
|
96
|
+
CLOSED (#1886 follow-up, extended by #1904 Bug 2b): `skills/code-review/SKILL.md`
|
|
97
|
+
step 9 WRITES `.claude/memory/.pr-review-passed` with a hash bound to the
|
|
98
|
+
branch diff (`branch_diff_gate_hash(default_base_ref(cwd), cwd)`, via this
|
|
99
|
+
module's own `review_gate_hash.py --branch-diff` CLI mode), for an
|
|
100
|
+
auto-scoped uncommitted-changes review OR a `--since <base>`-scoped one
|
|
101
|
+
that passed — the latter is the shape `/pr`'s own `/code-review --since
|
|
102
|
+
"$BASE" --json` call actually takes, the only real path to `gh pr create`.
|
|
103
|
+
`review_gate_hash.py`'s sibling `review_gate_hash()` (the staged-diff hash)
|
|
104
|
+
is no longer written to any gate file at all — `.review-passed` is retired.
|
|
105
|
+
`hooks/agent_dispatch_ledger.py` now stamps a dispatch's `subject_hash` with
|
|
106
|
+
this same branch-diff hash whenever nothing is staged (the routine state
|
|
107
|
+
during a `--since <base>`-scoped review, since `/pr`'s step 1 requires a
|
|
108
|
+
clean working tree first) — closing the mismatch for that shape completely,
|
|
109
|
+
since every dispatch during it and step 9's own write agree on one content
|
|
110
|
+
domain.
|
|
111
|
+
|
|
112
|
+
KNOWN RESIDUAL GAP, disclosed rather than silently accepted (narrower than
|
|
113
|
+
before #1904, still out of this fix's own scope): for an **auto-scoped
|
|
114
|
+
uncommitted-changes** review, `agent_dispatch_ledger.py` still stamps the
|
|
115
|
+
staged-diff hash (something IS staged in that mode), which is mathematically
|
|
116
|
+
identical to step 9's branch-diff write only in the common
|
|
117
|
+
single-commit-then-PR shape (a branch cut from its base, reviewed once while
|
|
118
|
+
staged, committed, then a PR opened immediately). On a branch with multiple
|
|
119
|
+
separate review-and-commit cycles in THAT mode, the branch-diff hash written
|
|
120
|
+
at step 9 won't match an earlier cycle's dispatch `subject_hash`, and this
|
|
121
|
+
gate correctly falls through to the "no dispatch found" block on a real
|
|
122
|
+
`gh pr create`, requiring a fresh `/code-review` run — a `--since <base>`
|
|
123
|
+
re-review now closes cleanly per the paragraph above, or the audited
|
|
124
|
+
`PR_GATE_BYPASS_REASON` escape hatch remains available — before it opens.
|
|
125
|
+
|
|
126
|
+
`gh_pr_create_detect.is_gh_pr_create_command`'s own docstring discloses its
|
|
127
|
+
detection gap (no `<shell> -c` unwrapping, no command-substitution
|
|
128
|
+
unwrapping, ...) — a much lighter heuristic than
|
|
129
|
+
`pre_commit_detect.is_git_commit_command`'s five-round-hardened one; see
|
|
130
|
+
that module's own docstring for why that difference is a deliberate,
|
|
131
|
+
disclosed scope decision, not an oversight.
|
|
132
|
+
|
|
133
|
+
Contract (docs/python-hook-contract.md):
|
|
134
|
+
Input : JSON on stdin (Claude Code PreToolUse:Bash payload)
|
|
135
|
+
Exit 0: allow the tool call
|
|
136
|
+
Exit 2: block the tool call (feedback returned to Claude on stdout,
|
|
137
|
+
mirrored to stderr — see `_emit_block`)
|
|
138
|
+
|
|
139
|
+
Stdlib-only.
|
|
140
|
+
"""
|
|
141
|
+
|
|
142
|
+
from __future__ import annotations
|
|
143
|
+
|
|
144
|
+
import hashlib
|
|
145
|
+
import json
|
|
146
|
+
import os
|
|
147
|
+
import subprocess
|
|
148
|
+
import sys
|
|
149
|
+
from datetime import datetime, timezone
|
|
150
|
+
from pathlib import Path
|
|
151
|
+
|
|
152
|
+
# The digest `sha256(b"")` produces — never treated as a valid subject-hash
|
|
153
|
+
# match here (issue #1904 Bug 1). Computed locally via hashlib rather than
|
|
154
|
+
# imported from `review_gate_hash.EMPTY_DIGEST` so this constant is available
|
|
155
|
+
# even on the degraded-import fallback path below.
|
|
156
|
+
_EMPTY_DIGEST = hashlib.sha256(b"").hexdigest()
|
|
157
|
+
|
|
158
|
+
_HOOK_DIR = Path(__file__).resolve().parent
|
|
159
|
+
_LIB_DIR = _HOOK_DIR / "lib"
|
|
160
|
+
|
|
161
|
+
sys.path.insert(0, str(_LIB_DIR))
|
|
162
|
+
try:
|
|
163
|
+
from artifact_paths import (
|
|
164
|
+
resolve_file as _resolve_file, # type: ignore[import-not-found]
|
|
165
|
+
)
|
|
166
|
+
from atomic_state import ( # type: ignore[import-not-found]
|
|
167
|
+
append_line_locked as _append_line_locked,
|
|
168
|
+
)
|
|
169
|
+
from boundary_events import ( # type: ignore[import-not-found]
|
|
170
|
+
emit_boundary_event as _emit_boundary_event,
|
|
171
|
+
)
|
|
172
|
+
from gh_pr_create_detect import ( # type: ignore[import-not-found]
|
|
173
|
+
is_gh_pr_create_command,
|
|
174
|
+
)
|
|
175
|
+
from git_safe_diff import run_safe_git_diff # type: ignore[import-not-found]
|
|
176
|
+
from pre_commit_doc_classifier import ( # type: ignore[import-not-found]
|
|
177
|
+
is_doc_only_changeset as _is_doc_only_changeset,
|
|
178
|
+
)
|
|
179
|
+
from review_gate_corroboration import ( # type: ignore[import-not-found]
|
|
180
|
+
evaluate as _evaluate_ledger,
|
|
181
|
+
)
|
|
182
|
+
from review_gate_corroboration import ( # type: ignore[import-not-found]
|
|
183
|
+
has_doc_only_exemption as _has_doc_only_exemption,
|
|
184
|
+
)
|
|
185
|
+
from review_gate_corroboration import ( # type: ignore[import-not-found]
|
|
186
|
+
has_single_agent_exemption as _has_single_agent_exemption,
|
|
187
|
+
)
|
|
188
|
+
from review_gate_hash import ( # type: ignore[import-not-found]
|
|
189
|
+
branch_diff_gate_hash,
|
|
190
|
+
default_base_ref,
|
|
191
|
+
)
|
|
192
|
+
from stdin_json import read_stdin_json # type: ignore[import-not-found]
|
|
193
|
+
except ImportError: # pragma: no cover - degraded-import fallback
|
|
194
|
+
|
|
195
|
+
def read_stdin_json() -> dict | None: # type: ignore[misc]
|
|
196
|
+
return None
|
|
197
|
+
|
|
198
|
+
def is_gh_pr_create_command(_command: str) -> bool: # type: ignore[misc]
|
|
199
|
+
return False
|
|
200
|
+
|
|
201
|
+
def _resolve_file( # type: ignore[misc]
|
|
202
|
+
category: str, filename: str, root=None, migrate: bool = True
|
|
203
|
+
) -> Path:
|
|
204
|
+
repo_root = Path(__file__).resolve().parents[3]
|
|
205
|
+
return repo_root / ".claude" / category / filename
|
|
206
|
+
|
|
207
|
+
def _emit_boundary_event(*_args, **_kwargs) -> None: # type: ignore[misc]
|
|
208
|
+
return None
|
|
209
|
+
|
|
210
|
+
def _append_line_locked( # type: ignore[misc]
|
|
211
|
+
path: Path, line: str, *, delay_env_var=None, fail_open: bool = True
|
|
212
|
+
) -> None:
|
|
213
|
+
# Degraded-import fallback: atomic_state itself couldn't be
|
|
214
|
+
# imported, so no hardened append is available. Unlike the OLD
|
|
215
|
+
# pre_commit_review.py fallback this replaces, this does NOT fall
|
|
216
|
+
# back to a plain unlocked `open(path, "a")` (#1904 item 5's own
|
|
217
|
+
# disclosed gap for that pattern) — a failed audit write is already
|
|
218
|
+
# fail-open by design (a bypass proceeds either way), so degrading
|
|
219
|
+
# further to "don't write at all" costs nothing beyond the audit
|
|
220
|
+
# trail entry itself, and avoids reintroducing the O_NOFOLLOW gap
|
|
221
|
+
# the shared helper exists to close.
|
|
222
|
+
if not fail_open:
|
|
223
|
+
raise OSError("atomic_state unavailable (degraded import)")
|
|
224
|
+
|
|
225
|
+
def run_safe_git_diff(extra_flags, cwd=None, text=False, target=None): # type: ignore[misc]
|
|
226
|
+
raise OSError("git_safe_diff unavailable (degraded import)")
|
|
227
|
+
|
|
228
|
+
def _is_doc_only_changeset(files: list[str]) -> bool: # type: ignore[misc]
|
|
229
|
+
return False
|
|
230
|
+
|
|
231
|
+
def branch_diff_gate_hash(base_ref: str, cwd=None) -> str: # type: ignore[misc]
|
|
232
|
+
return ""
|
|
233
|
+
|
|
234
|
+
def default_base_ref(cwd=None) -> str: # type: ignore[misc]
|
|
235
|
+
return "HEAD"
|
|
236
|
+
|
|
237
|
+
class _DegradedLedgerEvidence: # type: ignore[misc]
|
|
238
|
+
agents_in_window: frozenset = frozenset()
|
|
239
|
+
any_dispatch_ever = False
|
|
240
|
+
same_subject_dispatch_ever = False
|
|
241
|
+
read_failure_reason = "unreadable"
|
|
242
|
+
dispatch_failure_agents: frozenset | None = None
|
|
243
|
+
|
|
244
|
+
def _evaluate_ledger(cwd, before_ts, window_seconds, subject_hash): # type: ignore[misc]
|
|
245
|
+
return _DegradedLedgerEvidence()
|
|
246
|
+
|
|
247
|
+
def _has_doc_only_exemption( # type: ignore[misc]
|
|
248
|
+
cwd, before_ts, window_seconds, subject_hash
|
|
249
|
+
) -> bool:
|
|
250
|
+
return False
|
|
251
|
+
|
|
252
|
+
def _has_single_agent_exemption( # type: ignore[misc]
|
|
253
|
+
cwd, before_ts, window_seconds, subject_hash
|
|
254
|
+
) -> bool:
|
|
255
|
+
return False
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
def emit_boundary_event(*args, **kwargs) -> None:
|
|
259
|
+
"""Local safety net (#859): even a misbehaving helper must never affect
|
|
260
|
+
this hook's exit code, stdout, or stderr."""
|
|
261
|
+
try:
|
|
262
|
+
_emit_boundary_event(*args, **kwargs)
|
|
263
|
+
except Exception: # noqa: BLE001, S110 - fail-open by design
|
|
264
|
+
pass
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
_GATE_FILE_NAME = ".pr-review-passed"
|
|
268
|
+
|
|
269
|
+
# The env var must be set in a process environment this hook's own process
|
|
270
|
+
# actually inherits — e.g. `export PR_GATE_BYPASS_REASON=<reason>` in your
|
|
271
|
+
# shell profile, or a session-level persistent env config. An inline
|
|
272
|
+
# `PR_GATE_BYPASS_REASON=<reason> gh pr create ...` prefix on the SAME
|
|
273
|
+
# command line does NOT work (#2147): that assignment is scoped to the
|
|
274
|
+
# subshell that runs `gh`, which this PreToolUse hook — a separate process
|
|
275
|
+
# that runs BEFORE that subshell exists — never sees. Confirmed directly
|
|
276
|
+
# against this hook's own architecture (a PreToolUse hook reads
|
|
277
|
+
# `tool_input.command` as text; it does not execute it, and inline shell
|
|
278
|
+
# assignments are never reflected into a sibling process's `os.environ`).
|
|
279
|
+
_BYPASS_HINT = (
|
|
280
|
+
"To bypass: set PR_GATE_BYPASS_REASON as a real, inherited environment "
|
|
281
|
+
"variable before this command runs (e.g. `export "
|
|
282
|
+
"PR_GATE_BYPASS_REASON=<reason>`) — NOT as an inline "
|
|
283
|
+
"`PR_GATE_BYPASS_REASON=<reason> gh pr create ...` prefix on the same "
|
|
284
|
+
"line, which this hook cannot see and will not honor.\n"
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
_BLOCK_MESSAGE = (
|
|
288
|
+
"BLOCKED: Code review required before opening a PR.\n"
|
|
289
|
+
"\n"
|
|
290
|
+
"Run /code-review scoped to this branch's diff vs. its base, then\n"
|
|
291
|
+
"retry `gh pr create` — the PR will be allowed once review passes.\n"
|
|
292
|
+
"\n" + _BYPASS_HINT
|
|
293
|
+
)
|
|
294
|
+
|
|
295
|
+
# Recency window (#1886, mirrors #1461's WINDOW_SECONDS): how far back before
|
|
296
|
+
# `gh pr create`'s own invocation time a genuine review-agent dispatch still
|
|
297
|
+
# counts as corroborating evidence. Anchored on NOW (see module docstring's
|
|
298
|
+
# "Recency window anchor" bullet), not a gate file's mtime.
|
|
299
|
+
WINDOW_SECONDS = 1800
|
|
300
|
+
|
|
301
|
+
# #2147: lowered from 2 to 1 — a single `/code-review` invocation already
|
|
302
|
+
# dispatches a full multi-agent panel internally, so a second, separate
|
|
303
|
+
# top-level dispatch was never additional corroboration. See the module
|
|
304
|
+
# docstring's "`_MIN_DISTINCT_DISPATCHES` was lowered..." paragraph.
|
|
305
|
+
_MIN_DISTINCT_DISPATCHES = 1
|
|
306
|
+
|
|
307
|
+
_NO_DISPATCH_MESSAGE = (
|
|
308
|
+
f"BLOCKED: No genuine review-agent dispatch found in the last "
|
|
309
|
+
f"{WINDOW_SECONDS}s — run /code-review (scoped to this branch's diff "
|
|
310
|
+
f"vs. its base) before opening a PR.\n"
|
|
311
|
+
"\n" + _BYPASS_HINT
|
|
312
|
+
)
|
|
313
|
+
|
|
314
|
+
_STALE_MESSAGE = (
|
|
315
|
+
f"BLOCKED: Review-agent dispatch evidence found but outside the "
|
|
316
|
+
f"{WINDOW_SECONDS}s window — run /code-review again before opening a "
|
|
317
|
+
"PR.\n"
|
|
318
|
+
"\n" + _BYPASS_HINT
|
|
319
|
+
)
|
|
320
|
+
|
|
321
|
+
_DIFFERENT_CONTENT_MESSAGE = (
|
|
322
|
+
"BLOCKED: Review-agent dispatch evidence found, but for different "
|
|
323
|
+
"branch-diff content (the branch changed since that review) — run "
|
|
324
|
+
"/code-review again before opening a PR.\n"
|
|
325
|
+
"\n" + _BYPASS_HINT
|
|
326
|
+
)
|
|
327
|
+
|
|
328
|
+
_READ_FAILURE_MESSAGE = (
|
|
329
|
+
"BLOCKED: Could not read the dispatch ledger "
|
|
330
|
+
"(.claude/metrics/boundary-events.jsonl) — check hook registration; "
|
|
331
|
+
"this is an infra problem, not evidence that no review happened.\n"
|
|
332
|
+
"\n" + _BYPASS_HINT
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
_GATE_SETUP_FAILURE_MESSAGE = (
|
|
336
|
+
"BLOCKED: Could not determine the review-gate's own state (the branch "
|
|
337
|
+
"diff or the .claude/memory/.pr-review-passed path) — check that "
|
|
338
|
+
".claude/memory is a writable directory and the git repository is "
|
|
339
|
+
"healthy; this is an infra problem, not evidence that no review "
|
|
340
|
+
"happened.\n"
|
|
341
|
+
"\n" + _BYPASS_HINT
|
|
342
|
+
)
|
|
343
|
+
|
|
344
|
+
_REGISTRY_READ_FAILURE_MESSAGE = (
|
|
345
|
+
"BLOCKED: Could not read the registered review-agent set — cannot "
|
|
346
|
+
"prove no dispatch failure exists for this content; this is an infra "
|
|
347
|
+
"problem, not evidence that no review happened.\n"
|
|
348
|
+
"\n" + _BYPASS_HINT
|
|
349
|
+
)
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
def _insufficient_message(n: int) -> str:
|
|
353
|
+
return (
|
|
354
|
+
f"BLOCKED: Only {n} distinct review agent(s) dispatched (need >= "
|
|
355
|
+
f"{_MIN_DISTINCT_DISPATCHES}) — run /code-review before opening a "
|
|
356
|
+
"PR.\n"
|
|
357
|
+
"\n" + _BYPASS_HINT
|
|
358
|
+
)
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
def _dispatch_failure_message(agents: frozenset) -> str:
|
|
362
|
+
names = ", ".join(sorted(agents)) or "an unnamed review agent"
|
|
363
|
+
return (
|
|
364
|
+
f"BLOCKED: Dispatch failure recorded for {names} — this is a "
|
|
365
|
+
"dispatch/infra failure, not a code-review finding.\n"
|
|
366
|
+
"\n"
|
|
367
|
+
"Run /code-review again against this same, unchanged branch diff — "
|
|
368
|
+
"a clean rerun clears this block.\n"
|
|
369
|
+
"\n" + _BYPASS_HINT
|
|
370
|
+
)
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
def _emit_block(message: str) -> None:
|
|
374
|
+
sys.stdout.write(message)
|
|
375
|
+
sys.stderr.write(message)
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
def _branch_diff_names(base_ref: str, cwd: str | None) -> list[str] | None:
|
|
379
|
+
"""Return the branch-diff file list (`git diff <base>...HEAD
|
|
380
|
+
--name-only`), or `None` when git could not be asked at all — same
|
|
381
|
+
`None`-means-"couldn't determine" contract `pre_commit_review.py`'s own
|
|
382
|
+
`_staged_names()` established."""
|
|
383
|
+
try:
|
|
384
|
+
completed = run_safe_git_diff(
|
|
385
|
+
["--name-only"], cwd=(cwd or None), text=True, target=f"{base_ref}...HEAD"
|
|
386
|
+
)
|
|
387
|
+
except Exception: # noqa: BLE001 - "couldn't determine", not "nothing changed"
|
|
388
|
+
return None
|
|
389
|
+
if completed.returncode != 0:
|
|
390
|
+
return None
|
|
391
|
+
return [line for line in completed.stdout.splitlines() if line]
|
|
392
|
+
|
|
393
|
+
|
|
394
|
+
def _current_branch(cwd: str | None = None) -> str:
|
|
395
|
+
try:
|
|
396
|
+
completed = subprocess.run(
|
|
397
|
+
["git", "branch", "--show-current"],
|
|
398
|
+
cwd=cwd or None,
|
|
399
|
+
capture_output=True,
|
|
400
|
+
check=False,
|
|
401
|
+
text=True,
|
|
402
|
+
)
|
|
403
|
+
except (FileNotFoundError, OSError):
|
|
404
|
+
return ""
|
|
405
|
+
if completed.returncode != 0:
|
|
406
|
+
return ""
|
|
407
|
+
return completed.stdout.strip()
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
def _plugin_version() -> str:
|
|
411
|
+
manifest = _HOOK_DIR / ".." / ".claude-plugin" / "plugin.json"
|
|
412
|
+
try:
|
|
413
|
+
data = json.loads(manifest.read_text())
|
|
414
|
+
version = data.get("version")
|
|
415
|
+
if isinstance(version, str) and version:
|
|
416
|
+
return version
|
|
417
|
+
except (OSError, ValueError):
|
|
418
|
+
pass
|
|
419
|
+
return "unknown"
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
def _record_bypass_audit(reason: str, file_count: int, cwd: str) -> None:
|
|
423
|
+
"""Append one accountability line to
|
|
424
|
+
<project-root>/.claude/metrics/gate-bypass-audit.jsonl — the SAME
|
|
425
|
+
stream `pre_commit_review.py`'s `--no-verify` bypass used, distinguished
|
|
426
|
+
by `triggeredBy`. Fail-open: any failure to resolve the path, create the
|
|
427
|
+
directory, or write the line logs a diagnostic to stderr and never
|
|
428
|
+
blocks the PR."""
|
|
429
|
+
entry = {
|
|
430
|
+
"timestamp": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
|
|
431
|
+
"branch": _current_branch(cwd),
|
|
432
|
+
"triggeredBy": "PR_GATE_BYPASS_REASON",
|
|
433
|
+
"reason": reason,
|
|
434
|
+
"stagedFileCount": file_count,
|
|
435
|
+
"pluginVersion": _plugin_version(),
|
|
436
|
+
}
|
|
437
|
+
try:
|
|
438
|
+
audit_log_path = _resolve_file("metrics", "gate-bypass-audit.jsonl", cwd)
|
|
439
|
+
audit_log_path.parent.mkdir(parents=True, exist_ok=True)
|
|
440
|
+
_append_line_locked(
|
|
441
|
+
audit_log_path,
|
|
442
|
+
json.dumps(entry, separators=(",", ":")) + "\n",
|
|
443
|
+
fail_open=False,
|
|
444
|
+
)
|
|
445
|
+
except OSError as exc:
|
|
446
|
+
sys.stderr.write(f"[pre_pr_review] failed to record bypass audit: {exc}\n")
|
|
447
|
+
|
|
448
|
+
|
|
449
|
+
class GateVerdict:
|
|
450
|
+
__slots__ = ("matched_rule", "message", "passed")
|
|
451
|
+
|
|
452
|
+
def __init__(self, passed: bool, message: str = "", matched_rule: str = "") -> None:
|
|
453
|
+
self.passed = passed
|
|
454
|
+
self.message = message
|
|
455
|
+
self.matched_rule = matched_rule
|
|
456
|
+
|
|
457
|
+
|
|
458
|
+
def _stored_gate_hash(gate_file: Path) -> str:
|
|
459
|
+
"""Read `.pr-review-passed`'s single line: the raw branch-diff hash.
|
|
460
|
+
|
|
461
|
+
Fails CLOSED on ANY read/decode error (mirrors `pre_commit_review.py`'s
|
|
462
|
+
own `_stored_gate_hashes()` — an unreadable gate file is not a pass).
|
|
463
|
+
"""
|
|
464
|
+
try:
|
|
465
|
+
lines = gate_file.read_text(errors="replace").strip().splitlines()
|
|
466
|
+
except Exception: # noqa: BLE001 - fail CLOSED
|
|
467
|
+
return ""
|
|
468
|
+
return lines[0].strip() if lines else ""
|
|
469
|
+
|
|
470
|
+
|
|
471
|
+
def _hash_verdict(gate_file: Path, current_hash: str) -> GateVerdict | None:
|
|
472
|
+
"""Lens: the hash-match check, evaluated first — a mismatch (or a
|
|
473
|
+
missing/unreadable gate file) always rejects with `_BLOCK_MESSAGE`,
|
|
474
|
+
regardless of how much genuine dispatch evidence exists.
|
|
475
|
+
|
|
476
|
+
`current_hash == _EMPTY_DIGEST` is rejected with `_GATE_SETUP_FAILURE_MESSAGE`
|
|
477
|
+
rather than treated as an ordinary mismatch (issue #1904 Bug 1) — defense
|
|
478
|
+
in depth alongside `_prepare_gate()`'s own guard, which already refuses to
|
|
479
|
+
hand out an empty-digest `current_hash` in the first place, so this branch
|
|
480
|
+
is not expected to fire via `main()`'s normal call path; it exists so a
|
|
481
|
+
future caller of this function directly can't reintroduce the constant-hash
|
|
482
|
+
collapse by skipping `_prepare_gate()`.
|
|
483
|
+
|
|
484
|
+
Returns a rejecting `GateVerdict` when the hash doesn't match; `None`
|
|
485
|
+
when it matches, meaning "continue to the dispatch-ledger corroboration
|
|
486
|
+
lenses".
|
|
487
|
+
"""
|
|
488
|
+
if current_hash == _EMPTY_DIGEST:
|
|
489
|
+
return GateVerdict(False, _GATE_SETUP_FAILURE_MESSAGE, "gate-setup-failure-empty-digest")
|
|
490
|
+
if not gate_file.is_file():
|
|
491
|
+
return GateVerdict(False, _BLOCK_MESSAGE, "pre-pr-review")
|
|
492
|
+
stored = _stored_gate_hash(gate_file)
|
|
493
|
+
if not stored or stored != current_hash:
|
|
494
|
+
return GateVerdict(False, _BLOCK_MESSAGE, "pre-pr-review")
|
|
495
|
+
return None
|
|
496
|
+
|
|
497
|
+
|
|
498
|
+
def _dispatch_failure_verdict(evidence) -> GateVerdict | None:
|
|
499
|
+
"""Lens (#1763, reused as-is): the mechanical dispatch-failure veto.
|
|
500
|
+
Takes priority over every exemption AND the terminal distinct-dispatch
|
|
501
|
+
count."""
|
|
502
|
+
if evidence.dispatch_failure_agents is None:
|
|
503
|
+
return GateVerdict(False, _REGISTRY_READ_FAILURE_MESSAGE, "registry-read-failure")
|
|
504
|
+
if evidence.dispatch_failure_agents:
|
|
505
|
+
return GateVerdict(
|
|
506
|
+
False,
|
|
507
|
+
_dispatch_failure_message(evidence.dispatch_failure_agents),
|
|
508
|
+
"dispatch-failure-veto",
|
|
509
|
+
)
|
|
510
|
+
return None
|
|
511
|
+
|
|
512
|
+
|
|
513
|
+
def _doc_only_exemption_verdict(
|
|
514
|
+
cwd: str, before_ts: str, subject_hash: str, files: list[str]
|
|
515
|
+
) -> GateVerdict | None:
|
|
516
|
+
if _has_doc_only_exemption(
|
|
517
|
+
cwd, before_ts, WINDOW_SECONDS, subject_hash
|
|
518
|
+
) and _is_doc_only_changeset(files):
|
|
519
|
+
return GateVerdict(True)
|
|
520
|
+
return None
|
|
521
|
+
|
|
522
|
+
|
|
523
|
+
def _single_agent_exemption_verdict(
|
|
524
|
+
cwd: str, before_ts: str, subject_hash: str, n: int
|
|
525
|
+
) -> GateVerdict | None:
|
|
526
|
+
if n >= 1 and _has_single_agent_exemption(cwd, before_ts, WINDOW_SECONDS, subject_hash):
|
|
527
|
+
return GateVerdict(True)
|
|
528
|
+
return None
|
|
529
|
+
|
|
530
|
+
|
|
531
|
+
def _dispatch_count_verdict(evidence, n: int) -> GateVerdict:
|
|
532
|
+
if n >= _MIN_DISTINCT_DISPATCHES:
|
|
533
|
+
return GateVerdict(True)
|
|
534
|
+
if n >= 1:
|
|
535
|
+
return GateVerdict(False, _insufficient_message(n), "dispatch-evidence-insufficient")
|
|
536
|
+
if evidence.same_subject_dispatch_ever:
|
|
537
|
+
return GateVerdict(False, _STALE_MESSAGE, "dispatch-evidence-stale")
|
|
538
|
+
if evidence.any_dispatch_ever:
|
|
539
|
+
return GateVerdict(
|
|
540
|
+
False, _DIFFERENT_CONTENT_MESSAGE, "dispatch-evidence-different-content"
|
|
541
|
+
)
|
|
542
|
+
return GateVerdict(False, _NO_DISPATCH_MESSAGE, "dispatch-evidence-missing")
|
|
543
|
+
|
|
544
|
+
|
|
545
|
+
def _evaluate_gate(
|
|
546
|
+
gate_file: Path, current_hash: str, cwd: str, files: list[str]
|
|
547
|
+
) -> GateVerdict:
|
|
548
|
+
"""Decide whether `.pr-review-passed` corroborates a genuine, recent,
|
|
549
|
+
multi-agent review of the branch's full diff vs. its base (#1886).
|
|
550
|
+
|
|
551
|
+
A short pipeline over named decision lenses, mirroring
|
|
552
|
+
`pre_commit_review.py`'s own (now-retired) pipeline shape: `_hash_verdict`
|
|
553
|
+
first (always decisive on mismatch), then — only once the hash matches
|
|
554
|
+
and the ledger read succeeds — `_dispatch_failure_verdict`,
|
|
555
|
+
`_doc_only_exemption_verdict`, `_single_agent_exemption_verdict`, and
|
|
556
|
+
finally `_dispatch_count_verdict` as the terminal fallback.
|
|
557
|
+
"""
|
|
558
|
+
verdict = _hash_verdict(gate_file, current_hash)
|
|
559
|
+
if verdict is not None:
|
|
560
|
+
return verdict
|
|
561
|
+
|
|
562
|
+
try:
|
|
563
|
+
before_ts = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
564
|
+
|
|
565
|
+
evidence = _evaluate_ledger(cwd, before_ts, WINDOW_SECONDS, current_hash)
|
|
566
|
+
if evidence.read_failure_reason is not None:
|
|
567
|
+
return GateVerdict(False, _READ_FAILURE_MESSAGE, "dispatch-ledger-read-failure")
|
|
568
|
+
|
|
569
|
+
verdict = _dispatch_failure_verdict(evidence)
|
|
570
|
+
if verdict is not None:
|
|
571
|
+
return verdict
|
|
572
|
+
|
|
573
|
+
n = len(evidence.agents_in_window)
|
|
574
|
+
|
|
575
|
+
verdict = _doc_only_exemption_verdict(cwd, before_ts, current_hash, files)
|
|
576
|
+
if verdict is not None:
|
|
577
|
+
return verdict
|
|
578
|
+
|
|
579
|
+
verdict = _single_agent_exemption_verdict(cwd, before_ts, current_hash, n)
|
|
580
|
+
if verdict is not None:
|
|
581
|
+
return verdict
|
|
582
|
+
|
|
583
|
+
return _dispatch_count_verdict(evidence, n)
|
|
584
|
+
except Exception: # noqa: BLE001 - fail CLOSED: an unexpected error is not a pass
|
|
585
|
+
return GateVerdict(False, _READ_FAILURE_MESSAGE, "dispatch-ledger-read-failure")
|
|
586
|
+
|
|
587
|
+
|
|
588
|
+
def _handle_bypass(cwd: str, file_count: int, session_id) -> int | None:
|
|
589
|
+
"""`main()` lens: the `PR_GATE_BYPASS_REASON` bypass. Returns 0 when a
|
|
590
|
+
non-empty reason is set (allowing and auditing the bypass); `None`
|
|
591
|
+
otherwise (not applicable — `main()` continues to the normal gate
|
|
592
|
+
evaluation). There is no separate flag to detect "tried to bypass
|
|
593
|
+
without a reason" (see module docstring) — an unset/empty env var simply
|
|
594
|
+
means the ordinary gate applies."""
|
|
595
|
+
reason = os.environ.get("PR_GATE_BYPASS_REASON", "").strip()
|
|
596
|
+
if not reason:
|
|
597
|
+
return None
|
|
598
|
+
_record_bypass_audit(reason, file_count, cwd)
|
|
599
|
+
emit_boundary_event(cwd, "pre_pr_review", "Bash", "bypass", "PR_GATE_BYPASS_REASON", session_id)
|
|
600
|
+
return 0
|
|
601
|
+
|
|
602
|
+
|
|
603
|
+
def _prepare_gate(cwd: str) -> tuple[str, list[str], Path] | None:
|
|
604
|
+
"""`main()` lens: resolve the branch's base ref, the current branch-diff
|
|
605
|
+
hash, its file list, and the `.pr-review-passed` gate-file path,
|
|
606
|
+
creating its parent directory. Returns `(current_hash, files, gate_file)`
|
|
607
|
+
on success; `None` on failure (message/event already emitted).
|
|
608
|
+
|
|
609
|
+
Two additional failure modes, both issue #1904 Bug 1:
|
|
610
|
+
|
|
611
|
+
- `default_base_ref()` returning `None` (the base ref could not be
|
|
612
|
+
resolved at all — a routine state in a shallow/single-branch clone) is
|
|
613
|
+
a hard setup failure, not a reason to fall back to `"HEAD"` and
|
|
614
|
+
evaluate `git diff HEAD...HEAD`, which would succeed with an
|
|
615
|
+
empty-input digest and silently pass the gate.
|
|
616
|
+
- `current_hash == _EMPTY_DIGEST` — whether from the `None`-base-ref
|
|
617
|
+
case above, a genuinely empty branch diff, or any other git failure
|
|
618
|
+
`branch_diff_gate_hash()` fails closed to — is refused outright rather
|
|
619
|
+
than handed to `_hash_verdict()` as an ordinary hash to compare: the
|
|
620
|
+
empty digest is a CONSTANT, and treating it as comparable evidence
|
|
621
|
+
would let one dispatch recorded while nothing was staged corroborate
|
|
622
|
+
any later PR whose gate hash also happens to be empty.
|
|
623
|
+
"""
|
|
624
|
+
try:
|
|
625
|
+
base_ref = default_base_ref(cwd)
|
|
626
|
+
if base_ref is None:
|
|
627
|
+
raise OSError("could not resolve the branch's base ref")
|
|
628
|
+
current_hash = branch_diff_gate_hash(base_ref, cwd)
|
|
629
|
+
if current_hash == _EMPTY_DIGEST:
|
|
630
|
+
raise OSError("branch diff hashed to the empty-input digest")
|
|
631
|
+
files = _branch_diff_names(base_ref, cwd)
|
|
632
|
+
if files is None:
|
|
633
|
+
raise OSError("could not determine the branch diff's file list")
|
|
634
|
+
gate_file = _resolve_file("memory", _GATE_FILE_NAME, cwd)
|
|
635
|
+
gate_file.parent.mkdir(parents=True, exist_ok=True)
|
|
636
|
+
return current_hash, files, gate_file
|
|
637
|
+
except Exception: # noqa: BLE001 - fail CLOSED: setup failure is not a pass
|
|
638
|
+
return None
|
|
639
|
+
|
|
640
|
+
|
|
641
|
+
def main() -> int:
|
|
642
|
+
payload = read_stdin_json()
|
|
643
|
+
if payload is None:
|
|
644
|
+
return 0
|
|
645
|
+
|
|
646
|
+
tool_input = payload.get("tool_input") or {}
|
|
647
|
+
if not isinstance(tool_input, dict):
|
|
648
|
+
return 0
|
|
649
|
+
command = str(tool_input.get("command") or "")
|
|
650
|
+
|
|
651
|
+
if not is_gh_pr_create_command(command):
|
|
652
|
+
return 0
|
|
653
|
+
|
|
654
|
+
cwd = payload.get("cwd") or "."
|
|
655
|
+
session_id = payload.get("session_id")
|
|
656
|
+
|
|
657
|
+
prepared = _prepare_gate(cwd)
|
|
658
|
+
|
|
659
|
+
# Bug 2a (#1904): check the bypass BEFORE a gate-setup failure returns —
|
|
660
|
+
# `_GATE_SETUP_FAILURE_MESSAGE` itself documents `PR_GATE_BYPASS_REASON`
|
|
661
|
+
# as the way out, but that was previously unreachable on this path since
|
|
662
|
+
# `_prepare_gate()`'s failure short-circuited before `_handle_bypass()`
|
|
663
|
+
# ever ran. `-1` (module docstring: "branch diff unavailable") is the
|
|
664
|
+
# sentinel file count for the audit record when setup itself failed —
|
|
665
|
+
# there is no real file list to report a count for.
|
|
666
|
+
file_count = len(prepared[1]) if prepared is not None else -1
|
|
667
|
+
bypass_exit_code = _handle_bypass(cwd, file_count, session_id)
|
|
668
|
+
if bypass_exit_code is not None:
|
|
669
|
+
return bypass_exit_code
|
|
670
|
+
|
|
671
|
+
if prepared is None:
|
|
672
|
+
_emit_block(_GATE_SETUP_FAILURE_MESSAGE)
|
|
673
|
+
emit_boundary_event(cwd, "pre_pr_review", "Bash", "block", "gate-setup-failure", session_id)
|
|
674
|
+
return 2
|
|
675
|
+
current_hash, files, gate_file = prepared
|
|
676
|
+
|
|
677
|
+
verdict = _evaluate_gate(gate_file, current_hash, cwd, files)
|
|
678
|
+
if verdict.passed:
|
|
679
|
+
try:
|
|
680
|
+
gate_file.unlink()
|
|
681
|
+
except OSError:
|
|
682
|
+
pass
|
|
683
|
+
return 0
|
|
684
|
+
|
|
685
|
+
_emit_block(verdict.message)
|
|
686
|
+
emit_boundary_event(cwd, "pre_pr_review", "Bash", "block", verdict.matched_rule, session_id)
|
|
687
|
+
return 2
|
|
688
|
+
|
|
689
|
+
|
|
690
|
+
if __name__ == "__main__":
|
|
691
|
+
try:
|
|
692
|
+
sys.exit(main())
|
|
693
|
+
except Exception: # noqa: BLE001 - fail-open: a hook bug must never block a PR
|
|
694
|
+
sys.exit(0)
|