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,228 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""bash_retry_guard — Claude Code PreToolUse hook (Python port of bash-retry-guard.sh).
|
|
3
|
+
|
|
4
|
+
Detects repeated identical Bash commands within a session. After THRESHOLD
|
|
5
|
+
consecutive identical commands, nudges the agent to vary its approach
|
|
6
|
+
rather than re-running the same failing command.
|
|
7
|
+
|
|
8
|
+
Contract (docs/python-hook-contract.md):
|
|
9
|
+
Input : PreToolUse JSON on stdin
|
|
10
|
+
Output: advisory nudge on stdout when threshold reached
|
|
11
|
+
Exit : always 0 (advisory — never blocks)
|
|
12
|
+
Env : DEV_TEAM_BASH_RETRY_THRESHOLD (default 3; 0 disables)
|
|
13
|
+
|
|
14
|
+
State is persisted per-session under $TMPDIR/dev-team-bash-retry/<key>.json
|
|
15
|
+
where <key> is the session_id when present, else a cksum of $cwd. Files
|
|
16
|
+
are tiny and short-lived — no cleanup story needed beyond the OS's tmp
|
|
17
|
+
sweeping.
|
|
18
|
+
|
|
19
|
+
Stdlib-only. See #591 / #572 Phase 3.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from __future__ import annotations
|
|
23
|
+
|
|
24
|
+
import json
|
|
25
|
+
import os
|
|
26
|
+
import re
|
|
27
|
+
import sys
|
|
28
|
+
import tempfile
|
|
29
|
+
import time
|
|
30
|
+
import zlib
|
|
31
|
+
from pathlib import Path
|
|
32
|
+
from typing import Any
|
|
33
|
+
|
|
34
|
+
_HOOK_DIR = Path(__file__).resolve().parent
|
|
35
|
+
_LIB_DIR = _HOOK_DIR / "lib"
|
|
36
|
+
|
|
37
|
+
sys.path.insert(0, str(_LIB_DIR))
|
|
38
|
+
try:
|
|
39
|
+
from atomic_state import ( # type: ignore[import-not-found]
|
|
40
|
+
atomic_write,
|
|
41
|
+
locked_state,
|
|
42
|
+
race_window_delay,
|
|
43
|
+
)
|
|
44
|
+
from boundary_events import ( # type: ignore[import-not-found]
|
|
45
|
+
emit_boundary_event as _emit_boundary_event,
|
|
46
|
+
)
|
|
47
|
+
from stdin_json import read_stdin_json # type: ignore[import-not-found]
|
|
48
|
+
except ImportError: # pragma: no cover
|
|
49
|
+
import contextlib as _contextlib
|
|
50
|
+
|
|
51
|
+
def read_stdin_json() -> dict | None: # type: ignore[misc]
|
|
52
|
+
return None
|
|
53
|
+
|
|
54
|
+
def _emit_boundary_event(*_args, **_kwargs) -> None: # type: ignore[misc]
|
|
55
|
+
return None
|
|
56
|
+
|
|
57
|
+
def atomic_write(path: Path, text: str) -> None: # type: ignore[misc]
|
|
58
|
+
try:
|
|
59
|
+
path.write_text(text, encoding="utf-8")
|
|
60
|
+
except OSError:
|
|
61
|
+
pass
|
|
62
|
+
|
|
63
|
+
@_contextlib.contextmanager
|
|
64
|
+
def locked_state(_path: Path): # type: ignore[misc]
|
|
65
|
+
yield
|
|
66
|
+
|
|
67
|
+
def race_window_delay(_env_var: str) -> None: # type: ignore[misc]
|
|
68
|
+
return None
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def emit_boundary_event(*args, **kwargs) -> None:
|
|
72
|
+
"""Local safety net (#859): even a misbehaving helper must never affect
|
|
73
|
+
this hook's exit code, stdout, or stderr."""
|
|
74
|
+
try:
|
|
75
|
+
_emit_boundary_event(*args, **kwargs)
|
|
76
|
+
except Exception: # noqa: BLE001, S110 - fail-open by design
|
|
77
|
+
pass
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
# Verify-family commands — the verify-guard hook handles retry-nudging for
|
|
81
|
+
# these; this hook must not double-warn. The pattern MUST match the .sh's
|
|
82
|
+
# ERE byte-for-byte.
|
|
83
|
+
_VERIFY_RE = re.compile(
|
|
84
|
+
r"\b(npm (run )?(test|lint|build)|pytest|bats|eslint|tsc|"
|
|
85
|
+
r"go test|cargo (test|build)|mvn|gradle|make( |$)|vitest|"
|
|
86
|
+
r"jest|ruff|mypy|shellcheck)\b"
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
# Trivially short / read-only commands that retry harmlessly. Matches the
|
|
90
|
+
# `case` list in the .sh.
|
|
91
|
+
_READ_ONLY_FIRST_TOKENS = {"ls", "pwd", "echo", "cat", "head", "tail", "grep"}
|
|
92
|
+
|
|
93
|
+
_STATE_DIRNAME = "dev-team-bash-retry"
|
|
94
|
+
|
|
95
|
+
# Same TTL-purge-on-write pattern as tdd_guard.py / mutation_adapters/lib.py
|
|
96
|
+
# (#732) — one state file is written per session with no expiry otherwise,
|
|
97
|
+
# so long-lived hosts accumulate a file per session forever.
|
|
98
|
+
_STATE_TTL_SECONDS = 14400 # 4 hours
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def _cksum(text: str) -> str:
|
|
102
|
+
"""POSIX cksum-compatible hash for state-key/hash purposes.
|
|
103
|
+
|
|
104
|
+
The .sh uses `cksum` (POSIX CRC + length). We use zlib.crc32 as a
|
|
105
|
+
stable stdlib hash — byte-parity with the .sh is NOT required here
|
|
106
|
+
because the state file is a private per-session artifact never
|
|
107
|
+
surfaced to the user or committed to the repo. Determinism within
|
|
108
|
+
the Python port is sufficient.
|
|
109
|
+
"""
|
|
110
|
+
return str(zlib.crc32(text.encode("utf-8")) & 0xFFFFFFFF)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def _normalize(cmd: str) -> str:
|
|
114
|
+
"""Collapse runs of whitespace to a single space and trim ends."""
|
|
115
|
+
return re.sub(r"\s+", " ", cmd).strip()
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def _first_token(cmd: str) -> str:
|
|
119
|
+
trimmed = _normalize(cmd)
|
|
120
|
+
if not trimmed:
|
|
121
|
+
return ""
|
|
122
|
+
return trimmed.split(" ", 1)[0]
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def _should_skip(cmd: str) -> bool:
|
|
126
|
+
"""True when this command class is out of scope (verify family or read-only)."""
|
|
127
|
+
if _VERIFY_RE.search(cmd):
|
|
128
|
+
return True
|
|
129
|
+
return _first_token(cmd) in _READ_ONLY_FIRST_TOKENS
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def _state_key(session_id: str, cwd: str) -> str:
|
|
133
|
+
if session_id:
|
|
134
|
+
return session_id
|
|
135
|
+
return _cksum(cwd or os.getcwd())
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def _purge_stale(state_dir: Path) -> None:
|
|
139
|
+
if not state_dir.is_dir():
|
|
140
|
+
return
|
|
141
|
+
now = time.time()
|
|
142
|
+
for path in state_dir.glob("*.json"):
|
|
143
|
+
try:
|
|
144
|
+
if now - path.stat().st_mtime > _STATE_TTL_SECONDS:
|
|
145
|
+
path.unlink()
|
|
146
|
+
except OSError:
|
|
147
|
+
pass
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def _state_file(state_key: str) -> Path:
|
|
151
|
+
tmp = Path(os.environ.get("TMPDIR", tempfile.gettempdir()))
|
|
152
|
+
state_dir = tmp / _STATE_DIRNAME
|
|
153
|
+
state_dir.mkdir(parents=True, exist_ok=True)
|
|
154
|
+
_purge_stale(state_dir)
|
|
155
|
+
return state_dir / f"{state_key}.json"
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def _read_state(path: Path) -> dict[str, Any]:
|
|
159
|
+
if not path.is_file():
|
|
160
|
+
return {"hash": "", "count": 0}
|
|
161
|
+
try:
|
|
162
|
+
data = json.loads(path.read_text(encoding="utf-8"))
|
|
163
|
+
except (OSError, json.JSONDecodeError):
|
|
164
|
+
return {"hash": "", "count": 0}
|
|
165
|
+
return {
|
|
166
|
+
"hash": str(data.get("hash", "") or ""),
|
|
167
|
+
"count": int(data.get("count", 0) or 0),
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def _write_state(path: Path, state: dict[str, Any]) -> None:
|
|
172
|
+
# Atomic (tempfile + rename) so a concurrent reader never sees a torn
|
|
173
|
+
# file. The read-increment-write cycle is additionally serialized under
|
|
174
|
+
# locked_state() in main() (#1501). Best-effort — advisory hook.
|
|
175
|
+
atomic_write(path, json.dumps(state, separators=(",", ":")))
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def main() -> int:
|
|
179
|
+
payload = read_stdin_json()
|
|
180
|
+
if payload is None:
|
|
181
|
+
return 0
|
|
182
|
+
|
|
183
|
+
tool_input = payload.get("tool_input") or {}
|
|
184
|
+
command = str(tool_input.get("command") or "")
|
|
185
|
+
if not command:
|
|
186
|
+
return 0
|
|
187
|
+
|
|
188
|
+
if _should_skip(command):
|
|
189
|
+
return 0
|
|
190
|
+
|
|
191
|
+
threshold = int(os.environ.get("DEV_TEAM_BASH_RETRY_THRESHOLD", "3") or "3")
|
|
192
|
+
if threshold <= 0:
|
|
193
|
+
# 0 disables the nudge entirely. Skip state accounting so a later
|
|
194
|
+
# non-zero threshold doesn't fire on stale state.
|
|
195
|
+
return 0
|
|
196
|
+
|
|
197
|
+
session_id = str(payload.get("session_id") or "")
|
|
198
|
+
cwd = str(payload.get("cwd") or "")
|
|
199
|
+
state_key = _state_key(session_id, cwd)
|
|
200
|
+
state_path = _state_file(state_key)
|
|
201
|
+
|
|
202
|
+
normalized = _normalize(command)
|
|
203
|
+
cur_hash = _cksum(normalized)
|
|
204
|
+
|
|
205
|
+
# Serialize the read-increment-write so two concurrent invocations of the
|
|
206
|
+
# same command (e.g. a shared-cwd state key across two sessions) each add
|
|
207
|
+
# exactly one to the consecutive-run count instead of racing and losing an
|
|
208
|
+
# increment (#1501). Fail-open: an unlockable environment runs unlocked.
|
|
209
|
+
with locked_state(state_path):
|
|
210
|
+
prev = _read_state(state_path)
|
|
211
|
+
if cur_hash == prev["hash"]:
|
|
212
|
+
count = prev["count"] + 1
|
|
213
|
+
else:
|
|
214
|
+
count = 1
|
|
215
|
+
race_window_delay("DEV_TEAM_BASH_RETRY_TEST_DELAY_MS")
|
|
216
|
+
_write_state(state_path, {"hash": cur_hash, "count": count})
|
|
217
|
+
|
|
218
|
+
if count >= threshold:
|
|
219
|
+
print(f"bash-retry-guard: This command has run {count} consecutive times.")
|
|
220
|
+
print(" If it keeps failing, investigate the root cause rather than retrying.")
|
|
221
|
+
print(" Set DEV_TEAM_BASH_RETRY_THRESHOLD=0 to disable this warning.")
|
|
222
|
+
emit_boundary_event(cwd, "bash_retry_guard", "Bash", "warn", "bash-retry-threshold", session_id)
|
|
223
|
+
|
|
224
|
+
return 0
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
if __name__ == "__main__":
|
|
228
|
+
sys.exit(main())
|
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""boundary_events_write_guard.py — Claude Code PreToolUse hook (#2171).
|
|
3
|
+
|
|
4
|
+
`.claude/metrics/boundary-events.jsonl` is the plugin's boundary-level
|
|
5
|
+
accountability ledger (`hooks/lib/boundary_events.py`, #859) — every guard
|
|
6
|
+
hook's block/warn/bypass decision is recorded there, and that module's own
|
|
7
|
+
docstring states rows must never carry free text (command text, prompt
|
|
8
|
+
text, file paths, reasons), only rule IDs from closed vocabularies. A
|
|
9
|
+
direct `Write` or `Edit` tool call targeting that file bypasses
|
|
10
|
+
`emit_boundary_event()` entirely and could forge or corrupt that record
|
|
11
|
+
from inside the session — this hook raises the cost of that forgery the
|
|
12
|
+
same way `pre_tool_guard.py` raises it for other sensitive paths, by
|
|
13
|
+
blocking a Write/Edit whose `file_path`/`path` resolves to the ledger.
|
|
14
|
+
|
|
15
|
+
Step 1.1 of plans/2164-verdict-ledger-writer.md added the Write/Edit
|
|
16
|
+
path-match guard. Step 1.2 extends this same module to also inspect Bash
|
|
17
|
+
`tool_input.command` text for write-shaped commands (redirect, `tee`,
|
|
18
|
+
`sed -i`, `cp`/`mv`/`rm`/`truncate`/`dd`, or a write/append/exclusive-mode
|
|
19
|
+
Python `open()`) that target the ledger by filename, in any path form —
|
|
20
|
+
see `bash_command_writes_to_ledger()` and `_BASH_WRITE_SHAPE_PATTERNS`
|
|
21
|
+
below. The hook is registered in `settings.json`'s existing `Write|Edit`
|
|
22
|
+
and `Bash` `PreToolUse` matcher groups.
|
|
23
|
+
|
|
24
|
+
Contract (docs/python-hook-contract.md):
|
|
25
|
+
Input : PreToolUse JSON on stdin — for Write/Edit, `tool_input.file_path`
|
|
26
|
+
or `tool_input.path`, resolved relative to `cwd` when not
|
|
27
|
+
absolute; for Bash, `tool_input.command`
|
|
28
|
+
Output: block message on stdout. The Write/Edit-path message names
|
|
29
|
+
`emit_boundary_event()` (`hooks/lib/boundary_events.py`) as the
|
|
30
|
+
remedy — a Python-only function unreachable from a shell
|
|
31
|
+
command. The Bash-path message instead names the
|
|
32
|
+
`hooks/lib/boundary_events.py` CLI's actual invocable shape
|
|
33
|
+
(`python3 plugins/dev-team/hooks/lib/boundary_events.py --event
|
|
34
|
+
<event> --subject-hash <hash> ...`, #1461) — the message
|
|
35
|
+
interpolates the live `<event>` choice set from
|
|
36
|
+
`boundary_events.cli_event_names()` rather than restating it
|
|
37
|
+
here, so this docstring never drifts the way the message
|
|
38
|
+
itself used to (review finding, #2171). `--event` is a flag
|
|
39
|
+
drawn from a closed `choices` set, not a positional, and most
|
|
40
|
+
events require `--subject-hash`; the CLI cannot construct an
|
|
41
|
+
arbitrary row by design — a genuinely custom row still needs
|
|
42
|
+
`emit_boundary_event()` called from a hook (plan-review-ux
|
|
43
|
+
finding, Step 1.2, corrected by review).
|
|
44
|
+
Exit : 2 to block, 0 to allow. Fail-open on any exception (malformed
|
|
45
|
+
payload, unreadable cwd, unresolvable repo root) — never raises.
|
|
46
|
+
|
|
47
|
+
Path matching is lexical only (`os.path.abspath` — `.`/`..` collapsing, no
|
|
48
|
+
symlink following): a symlink-escape sandbox is explicitly out of scope
|
|
49
|
+
(plan Step 1.1 TEST note) — this guard raises the cost of forgery, it does
|
|
50
|
+
not sandbox the filesystem. One narrow exception: `targets_ledger()`
|
|
51
|
+
resolves the `cwd` anchor itself via `os.path.realpath` before joining a
|
|
52
|
+
relative `file_path` against it (review finding — see `targets_ledger()`'s
|
|
53
|
+
own docstring) so that anchor stays symmetric with the ledger side, which
|
|
54
|
+
already passes through `git rev-parse --show-toplevel`'s own symlink
|
|
55
|
+
resolution via `artifact_paths.resolve_file()`/`project_root()`. Everything
|
|
56
|
+
beyond that single anchor — the rest of the joined path, and all Bash
|
|
57
|
+
command-text matching below — stays purely lexical, no further symlink
|
|
58
|
+
following.
|
|
59
|
+
|
|
60
|
+
Stdlib-only. See ADR 0014.
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
from __future__ import annotations
|
|
64
|
+
|
|
65
|
+
import os
|
|
66
|
+
import re
|
|
67
|
+
import sys
|
|
68
|
+
from pathlib import Path
|
|
69
|
+
|
|
70
|
+
_HOOK_DIR = Path(__file__).resolve().parent
|
|
71
|
+
_LIB_DIR = _HOOK_DIR / "lib"
|
|
72
|
+
if str(_LIB_DIR) not in sys.path:
|
|
73
|
+
sys.path.insert(0, str(_LIB_DIR))
|
|
74
|
+
|
|
75
|
+
import artifact_paths
|
|
76
|
+
from boundary_events import LOG_NAME as _LEDGER_NAME
|
|
77
|
+
from boundary_events import cli_event_names as _cli_event_names
|
|
78
|
+
from boundary_events import emit_boundary_event as _emit_boundary_event
|
|
79
|
+
from stdin_json import read_stdin_json # type: ignore[import-not-found]
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def emit_boundary_event(*args, **kwargs) -> None:
|
|
83
|
+
"""Local safety net (#859): even a misbehaving helper must never affect
|
|
84
|
+
this hook's exit code, stdout, or stderr."""
|
|
85
|
+
try:
|
|
86
|
+
_emit_boundary_event(*args, **kwargs)
|
|
87
|
+
except Exception: # noqa: BLE001, S110 - fail-open by design
|
|
88
|
+
pass
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _extract_file_path(tool_input: object) -> str:
|
|
92
|
+
"""Return `tool_input.file_path`, `tool_input.path`, or empty string —
|
|
93
|
+
mirrors `pre_tool_guard._extract_file_path`'s field-preference order."""
|
|
94
|
+
if not isinstance(tool_input, dict):
|
|
95
|
+
return ""
|
|
96
|
+
file_path = tool_input.get("file_path")
|
|
97
|
+
if isinstance(file_path, str) and file_path:
|
|
98
|
+
return file_path
|
|
99
|
+
path = tool_input.get("path")
|
|
100
|
+
if isinstance(path, str) and path:
|
|
101
|
+
return path
|
|
102
|
+
return ""
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def targets_ledger(file_path: str, cwd: str) -> bool:
|
|
106
|
+
"""True when `file_path` (joined against `cwd` if not absolute) names
|
|
107
|
+
the same on-disk path `emit_boundary_event()` itself resolves and
|
|
108
|
+
writes to — `artifact_paths.resolve_file("metrics", ...)` under the
|
|
109
|
+
repo root, not a bare `.claude/metrics/` prefix match (so
|
|
110
|
+
`review-verdicts.jsonl`, Slice 2's own store, is unaffected) — OR the
|
|
111
|
+
pre-migration legacy path `<project-root>/metrics/boundary-events.jsonl`
|
|
112
|
+
(review finding, #2171): `resolve_file(..., migrate=True)`, the default
|
|
113
|
+
every `emit_boundary_event()` call uses, `shutil.move`s an untracked
|
|
114
|
+
file at that legacy path into the ledger the *next* time anything
|
|
115
|
+
emits, whenever the new-location file does not yet exist. Matching
|
|
116
|
+
only the new-location path would let a Write/Edit plant a forged file
|
|
117
|
+
at the legacy path — invisible to this guard — that a later, entirely
|
|
118
|
+
legitimate `emit_boundary_event()` call then silently promotes into
|
|
119
|
+
ledger history. Both candidates must be blocked for the guard's
|
|
120
|
+
forgery-cost claim to hold.
|
|
121
|
+
|
|
122
|
+
The `cwd` anchor is realpath'd before the join (review finding): the
|
|
123
|
+
ledger side already resolves symlinks transparently, because
|
|
124
|
+
`artifact_paths.resolve_file()` -> `project_root()` finds the repo root
|
|
125
|
+
via `git rev-parse --show-toplevel`, and git itself resolves a
|
|
126
|
+
symlinked `cwd` to its real location. Joining `file_path` against the
|
|
127
|
+
*unresolved* `cwd` string, then comparing both sides with only
|
|
128
|
+
`os.path.abspath` (no symlink resolution), left the two sides
|
|
129
|
+
comparing a symlinked-form candidate against a resolved-form ledger —
|
|
130
|
+
a real, if narrow, false negative on a symlinked `cwd`. Resolving only
|
|
131
|
+
the `cwd` anchor keeps this guard's `os.path.abspath` comparison the
|
|
132
|
+
same symmetric idiom `pre_tool_guard.py` uses elsewhere, without
|
|
133
|
+
resolving symlinks anywhere else in the path (module docstring)."""
|
|
134
|
+
if not file_path:
|
|
135
|
+
return False
|
|
136
|
+
base = Path(os.path.realpath(cwd)) if cwd else Path.cwd()
|
|
137
|
+
candidate = Path(file_path)
|
|
138
|
+
if not candidate.is_absolute():
|
|
139
|
+
candidate = base / candidate
|
|
140
|
+
candidate_norm = os.path.abspath(str(candidate))
|
|
141
|
+
|
|
142
|
+
# Cheap lexical pre-check (performance review finding, #2171): every
|
|
143
|
+
# candidate this function can match ends in `_LEDGER_NAME` — skip the
|
|
144
|
+
# `git rev-parse` subprocess `resolve_stream()`/`project_root()` incur
|
|
145
|
+
# for the overwhelming majority of Write/Edit calls that plainly don't.
|
|
146
|
+
if os.path.basename(candidate_norm) != _LEDGER_NAME:
|
|
147
|
+
return False
|
|
148
|
+
|
|
149
|
+
ledger = artifact_paths.resolve_file(
|
|
150
|
+
"metrics", _LEDGER_NAME, Path(cwd) if cwd else Path.cwd(), migrate=False
|
|
151
|
+
)
|
|
152
|
+
ledger_norm = os.path.abspath(str(ledger))
|
|
153
|
+
if candidate_norm == ledger_norm:
|
|
154
|
+
return True
|
|
155
|
+
|
|
156
|
+
legacy_ledger = artifact_paths.project_root(start=cwd) / "metrics" / _LEDGER_NAME
|
|
157
|
+
legacy_norm = os.path.abspath(str(legacy_ledger))
|
|
158
|
+
|
|
159
|
+
return candidate_norm == legacy_norm
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def _extract_command(tool_input: object) -> str:
|
|
163
|
+
"""Return `tool_input.command`, or empty string — mirrors
|
|
164
|
+
`destructive_guard._extract_command`'s field access."""
|
|
165
|
+
if not isinstance(tool_input, dict):
|
|
166
|
+
return ""
|
|
167
|
+
command = tool_input.get("command")
|
|
168
|
+
return command if isinstance(command, str) else ""
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
# Any prefix of path characters ending in the ledger's literal filename.
|
|
172
|
+
# Matches the filename in every ordinary path form the plan requires
|
|
173
|
+
# (relative, absolute, "./"-prefixed, bare after a `cd .claude/metrics`
|
|
174
|
+
# prefix, `~/`-prefixed, `$VAR`/`${VAR}`-expanded, or a quoted path
|
|
175
|
+
# containing a space), since all of those literally end in this substring.
|
|
176
|
+
# Widening this class is not an attempt at obfuscation-grade path matching
|
|
177
|
+
# (shell escapes, `$(...)` substitution, base64-encoded paths, etc. remain
|
|
178
|
+
# out of scope — see the module docstring's "heuristic, not a sandbox"
|
|
179
|
+
# note); it only covers path forms a real command would ordinarily write.
|
|
180
|
+
_LEDGER_PATH_SUFFIX = r"[\w./~${} -]*" + re.escape(_LEDGER_NAME)
|
|
181
|
+
|
|
182
|
+
# Bash write-shaped patterns targeting the ledger (Step 1.2, #2171). Each
|
|
183
|
+
# pattern embeds `_LEDGER_PATH_SUFFIX` directly, so a match always means the
|
|
184
|
+
# command's write operation targets the ledger specifically — not merely
|
|
185
|
+
# that the ledger's filename appears somewhere unrelated in the command
|
|
186
|
+
# (e.g. `cat boundary-events.jsonl | tee /tmp/copy.txt` reads the ledger and
|
|
187
|
+
# writes elsewhere; it does not match the `tee` pattern below because
|
|
188
|
+
# `tee`'s own argument is `/tmp/copy.txt`, not the ledger). Heuristic, not a
|
|
189
|
+
# sandbox — see the plan's Risks note ("raises the cost of forgery," per
|
|
190
|
+
# #2171's Out of Scope). Mirrors destructive_guard.py's own pattern-table
|
|
191
|
+
# idiom (a module-level constant, one comment per pattern).
|
|
192
|
+
_BASH_WRITE_SHAPE_PATTERNS: tuple[re.Pattern, ...] = (
|
|
193
|
+
# `>`/`>>` redirect whose target is the ledger. Also catches a
|
|
194
|
+
# heredoc's trailing redirect operator (`cat <<'EOF' >> ...`) — the
|
|
195
|
+
# heredoc `<<` marker itself is never parsed or matched (plan Decision
|
|
196
|
+
# note); detection is via this same trailing operator, like any other
|
|
197
|
+
# write-shaped command.
|
|
198
|
+
re.compile(rf">{{1,2}}\s*['\"]?{_LEDGER_PATH_SUFFIX}"),
|
|
199
|
+
# `tee` writing to the ledger, within the same shell statement (same
|
|
200
|
+
# same-statement scoping as the cp/mv/rm pattern below) — the ledger
|
|
201
|
+
# can be any of tee's operands (`tee /tmp/log boundary-events.jsonl`),
|
|
202
|
+
# not just its first.
|
|
203
|
+
re.compile(rf"\btee\b[^;|&\n]*['\"]?{_LEDGER_PATH_SUFFIX}"),
|
|
204
|
+
# `sed -i`/`sed --in-place` (in-place edit) targeting the ledger, within
|
|
205
|
+
# the same shell statement (stops at `;`/`|`/`&` so an unrelated later
|
|
206
|
+
# statement that happens to also mention the ledger doesn't
|
|
207
|
+
# false-positive).
|
|
208
|
+
re.compile(
|
|
209
|
+
rf"\bsed\b[^;|&\n]*(?:-i\b|--in-place\b)[^;|&\n]*['\"]?{_LEDGER_PATH_SUFFIX}"
|
|
210
|
+
),
|
|
211
|
+
# cp/mv/rm/truncate/dd targeting the ledger, within the same shell
|
|
212
|
+
# statement (same same-statement scoping as the sed pattern above).
|
|
213
|
+
re.compile(rf"\b(?:cp|mv|rm|truncate|dd)\b[^;|&\n]*{_LEDGER_PATH_SUFFIX}"),
|
|
214
|
+
# A Python `open(...)` call on the ledger using any mode that permits
|
|
215
|
+
# writing: "w"/"a"/"x" (optionally suffixed, e.g. "wb"/"a+"/"x+"), or a
|
|
216
|
+
# "+" read-write mode such as "r+"/"rb+"/"r+b" — any mode string
|
|
217
|
+
# containing w/a/x/+ can write. The mode may be positional or the
|
|
218
|
+
# keyword form (`mode='a'`). "Allowed" means read-ONLY: a mode-omitted
|
|
219
|
+
# (default "r") or explicit pure-"r" `open()` never matches this
|
|
220
|
+
# pattern — not "read-mode" generically, since "r+" is a read-*and*-
|
|
221
|
+
# write mode despite starting with "r".
|
|
222
|
+
re.compile(
|
|
223
|
+
rf"open\(\s*['\"]{_LEDGER_PATH_SUFFIX}['\"]\s*,\s*"
|
|
224
|
+
r"(?:mode\s*=\s*)?['\"][^'\"]*[wax+][^'\"]*['\"]"
|
|
225
|
+
),
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def bash_command_writes_to_ledger(command: str) -> bool:
|
|
230
|
+
"""True when `command` is write-shaped AND targets the ledger by
|
|
231
|
+
filename, in any path form — see `_BASH_WRITE_SHAPE_PATTERNS`. A
|
|
232
|
+
read-shaped command referencing the same filename (`cat`, `grep`,
|
|
233
|
+
`tail`, `head`, a read-mode `open()`) never matches any pattern here,
|
|
234
|
+
so it is allowed without a separate read-allowlist check.
|
|
235
|
+
|
|
236
|
+
O(n) fast path first (security review finding, #2171): every pattern in
|
|
237
|
+
`_BASH_WRITE_SHAPE_PATTERNS` requires the literal `_LEDGER_NAME`
|
|
238
|
+
substring, so a command that lacks it cannot match any of them — this
|
|
239
|
+
is semantically equivalent to running the patterns, not a heuristic
|
|
240
|
+
shortcut. Skipping straight to `False` on a long non-matching command
|
|
241
|
+
(e.g. a `rm` of padding data) avoids the patterns' overlapping
|
|
242
|
+
`[^;|&\\n]*`/path-suffix character classes backtracking quadratically
|
|
243
|
+
on input that was never going to match."""
|
|
244
|
+
if not command or _LEDGER_NAME not in command:
|
|
245
|
+
return False
|
|
246
|
+
return any(pattern.search(command) for pattern in _BASH_WRITE_SHAPE_PATTERNS)
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
def _block(
|
|
250
|
+
cwd: str,
|
|
251
|
+
tool: str,
|
|
252
|
+
session_id: str | None,
|
|
253
|
+
blocked_message: str,
|
|
254
|
+
remedy_message: str,
|
|
255
|
+
) -> int:
|
|
256
|
+
"""Shared block sequence for both `main()` branches: record the guard's
|
|
257
|
+
own decision (every sibling guard does — see the module docstring),
|
|
258
|
+
print the block explanation, and return the block exit code.
|
|
259
|
+
|
|
260
|
+
Mirrors every line to stderr in addition to stdout (docs/python-hook-
|
|
261
|
+
contract.md § stderr, "Exception — exit-2 (block) messages"): some
|
|
262
|
+
Claude Code hook-error wrappers surface only stderr on a nonzero hook
|
|
263
|
+
exit, so a stdout-only block message can go unseen there. Stdout stays
|
|
264
|
+
the canonical channel; stderr is additive duplication for this exit-2
|
|
265
|
+
path only — this is a new hook, so it converges to the documented
|
|
266
|
+
standard from the start rather than joining the stdout-only legacy list."""
|
|
267
|
+
emit_boundary_event(
|
|
268
|
+
cwd, "boundary_events_write_guard", tool, "block", "ledger-write-blocked", session_id
|
|
269
|
+
)
|
|
270
|
+
lines = (
|
|
271
|
+
blocked_message,
|
|
272
|
+
(
|
|
273
|
+
"This file is the boundary-events accountability ledger (#859) — "
|
|
274
|
+
"it is append-only from the session's perspective."
|
|
275
|
+
),
|
|
276
|
+
remedy_message,
|
|
277
|
+
)
|
|
278
|
+
for line in lines:
|
|
279
|
+
print(line)
|
|
280
|
+
for line in lines:
|
|
281
|
+
print(line, file=sys.stderr)
|
|
282
|
+
return 2
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
def _handle_bash_tool(payload: dict, cwd: str, session_id: str | None) -> int:
|
|
286
|
+
command = _extract_command(payload.get("tool_input"))
|
|
287
|
+
if not bash_command_writes_to_ledger(command):
|
|
288
|
+
return 0
|
|
289
|
+
|
|
290
|
+
events = "|".join(_cli_event_names())
|
|
291
|
+
return _block(
|
|
292
|
+
cwd,
|
|
293
|
+
"Bash",
|
|
294
|
+
session_id,
|
|
295
|
+
f"BLOCKED: This Bash command writes to '.claude/metrics/{_LEDGER_NAME}', "
|
|
296
|
+
"which is not allowed.",
|
|
297
|
+
"Use 'python3 plugins/dev-team/hooks/lib/boundary_events.py "
|
|
298
|
+
f"--event <{events}> "
|
|
299
|
+
"--subject-hash <hash> ...' instead of writing to it from Bash — "
|
|
300
|
+
"an arbitrary row isn't CLI-constructible by design (that CLI only "
|
|
301
|
+
"accepts a closed --event vocabulary); this exact row needs a "
|
|
302
|
+
"Python-side hook change instead.",
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
def _handle_write_edit_tool(
|
|
307
|
+
payload: dict, cwd: str, tool: str, session_id: str | None
|
|
308
|
+
) -> int:
|
|
309
|
+
file_path = _extract_file_path(payload.get("tool_input"))
|
|
310
|
+
if not file_path:
|
|
311
|
+
return 0
|
|
312
|
+
|
|
313
|
+
if not targets_ledger(file_path, cwd):
|
|
314
|
+
return 0
|
|
315
|
+
|
|
316
|
+
return _block(
|
|
317
|
+
cwd,
|
|
318
|
+
tool,
|
|
319
|
+
session_id,
|
|
320
|
+
f"BLOCKED: Direct write to '{file_path}' is not allowed.",
|
|
321
|
+
"Use emit_boundary_event() in plugins/dev-team/hooks/lib/boundary_events.py "
|
|
322
|
+
"instead of writing to it directly.",
|
|
323
|
+
)
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
def main() -> int:
|
|
327
|
+
try:
|
|
328
|
+
payload = read_stdin_json()
|
|
329
|
+
if payload is None:
|
|
330
|
+
return 0
|
|
331
|
+
|
|
332
|
+
raw_cwd = payload.get("cwd")
|
|
333
|
+
cwd = (
|
|
334
|
+
raw_cwd
|
|
335
|
+
if isinstance(raw_cwd, str) and raw_cwd and "\0" not in raw_cwd
|
|
336
|
+
else "."
|
|
337
|
+
)
|
|
338
|
+
raw_tool = payload.get("tool_name")
|
|
339
|
+
tool = raw_tool if isinstance(raw_tool, str) and raw_tool else "Write"
|
|
340
|
+
raw_session_id = payload.get("session_id")
|
|
341
|
+
session_id = raw_session_id if isinstance(raw_session_id, str) else None
|
|
342
|
+
|
|
343
|
+
if tool == "Bash":
|
|
344
|
+
return _handle_bash_tool(payload, cwd, session_id)
|
|
345
|
+
|
|
346
|
+
return _handle_write_edit_tool(payload, cwd, tool, session_id)
|
|
347
|
+
except Exception: # noqa: BLE001 - fail-open by design, see module docstring
|
|
348
|
+
return 0
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
if __name__ == "__main__": # pragma: no cover
|
|
352
|
+
sys.exit(main())
|