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,478 @@
|
|
|
1
|
+
"""Shared library for mutation-gate hook and adapters — Python port of lib.sh.
|
|
2
|
+
|
|
3
|
+
Adapter contract (mirrors the bash lib.sh header):
|
|
4
|
+
|
|
5
|
+
Inputs (env vars set by mutation-gate orchestrator):
|
|
6
|
+
ADAPTER_COMMAND — the original test command (used for pitest class extraction)
|
|
7
|
+
ADAPTER_SOURCE_FILE — path to the source file under test (JS/TS adapters)
|
|
8
|
+
ADAPTER_TIMEOUT — seconds (from MUTATION_GATE_TIMEOUT, default 60)
|
|
9
|
+
ADAPTER_RUNNER_STDOUT — captured test runner stdout (for pitest test-list derivation)
|
|
10
|
+
|
|
11
|
+
Output: adapter writes normalized zero-kill list to $TMPDIR/mutation-gate/zero-kills.json
|
|
12
|
+
Format: [{"name":"TestName","file":"path.ts","line":12,"covered":4}]
|
|
13
|
+
"line" is null when unknown.
|
|
14
|
+
|
|
15
|
+
Exit codes: 0 = success or advisory (orchestrator reads zero-kills.json), 1 = internal error.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
import hashlib
|
|
21
|
+
import json
|
|
22
|
+
import os
|
|
23
|
+
import re
|
|
24
|
+
import shutil
|
|
25
|
+
import subprocess
|
|
26
|
+
import sys
|
|
27
|
+
import time
|
|
28
|
+
from collections.abc import Callable, Sequence
|
|
29
|
+
from pathlib import Path
|
|
30
|
+
from typing import Any
|
|
31
|
+
|
|
32
|
+
# ---------------------------------------------------------------------------
|
|
33
|
+
# _timeout — portable wrapper: `timeout` > `gtimeout` > unbounded fallback
|
|
34
|
+
# ---------------------------------------------------------------------------
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def run_with_timeout(
|
|
38
|
+
seconds: int,
|
|
39
|
+
argv: Sequence[str],
|
|
40
|
+
**kwargs: Any,
|
|
41
|
+
) -> subprocess.CompletedProcess[bytes]:
|
|
42
|
+
"""Run `argv` with the given `seconds` wall-clock cap, mirroring bash `_timeout`.
|
|
43
|
+
|
|
44
|
+
Prefers `timeout`/`gtimeout` on PATH (so the runner keeps its bash-native
|
|
45
|
+
kill semantics on hosts that have coreutils); falls back to
|
|
46
|
+
`subprocess.run(..., timeout=...)` when neither is available. On timeout,
|
|
47
|
+
return code is 124 to match `timeout(1)`'s convention.
|
|
48
|
+
|
|
49
|
+
All extra kwargs pass through to `subprocess.run`. Callers pass `check=False`
|
|
50
|
+
when they want to inspect the exit code themselves.
|
|
51
|
+
"""
|
|
52
|
+
kwargs.setdefault("check", False)
|
|
53
|
+
timeout_bin = shutil.which("timeout") or shutil.which("gtimeout")
|
|
54
|
+
if timeout_bin is not None:
|
|
55
|
+
# `check` is already guaranteed present via the `setdefault` above;
|
|
56
|
+
# ruff can't see through `**kwargs` to confirm it statically.
|
|
57
|
+
return subprocess.run([timeout_bin, str(seconds), *argv], **kwargs) # noqa: PLW1510
|
|
58
|
+
# No coreutils timeout available — emit the same advisory as bash lib
|
|
59
|
+
# to stderr so the JSON stdout channel stays clean.
|
|
60
|
+
sys.stderr.write(
|
|
61
|
+
"mutation-gate: timeout unavailable (install coreutils for gtimeout); "
|
|
62
|
+
"run is unbounded\n"
|
|
63
|
+
)
|
|
64
|
+
try:
|
|
65
|
+
# `check` is already guaranteed present via the `setdefault` above;
|
|
66
|
+
# ruff can't see through `**kwargs` to confirm it statically.
|
|
67
|
+
return subprocess.run(list(argv), timeout=seconds, **kwargs) # noqa: PLW1510
|
|
68
|
+
except subprocess.TimeoutExpired as exc:
|
|
69
|
+
# Synthesize a CompletedProcess with the timeout-native exit code so
|
|
70
|
+
# callers keep their `exit == 124` branch.
|
|
71
|
+
return subprocess.CompletedProcess(
|
|
72
|
+
args=list(argv),
|
|
73
|
+
returncode=124,
|
|
74
|
+
stdout=exc.stdout or b"",
|
|
75
|
+
stderr=exc.stderr or b"",
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
# ---------------------------------------------------------------------------
|
|
80
|
+
# JSON output helpers
|
|
81
|
+
#
|
|
82
|
+
# The bash lib pipes through `jq -n` to build objects; here we compose with
|
|
83
|
+
# `json.dumps` and preserve the same key order + no trailing newline so the
|
|
84
|
+
# hook's JSON decision channel stays byte-parity with the bash implementation.
|
|
85
|
+
# ---------------------------------------------------------------------------
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def emit_block(reason: str) -> str:
|
|
89
|
+
"""Return `{"decision":"block","reason":"..."}` as compact JSON (no trailing \\n)."""
|
|
90
|
+
return json.dumps({"decision": "block", "reason": reason})
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
# Canonical coverage-capture-failure signal (see #1156). Verbatim from
|
|
94
|
+
# Stryker.NET's own log: src/Stryker.Core/Stryker.Core/CoverageAnalysis/
|
|
95
|
+
# CoverageAnalyser.cs emits "It looks like the test coverage capture failed.
|
|
96
|
+
# Disable coverage based optimisation." when the initial per-test coverage pass
|
|
97
|
+
# yields zero covered mutations, then calls AssumeAllTestsAreNeeded() —
|
|
98
|
+
# silently falling back to whole-suite-per-mutant. The British "optimisation"
|
|
99
|
+
# spelling matches the source. Kept byte-identical with the copy in the status
|
|
100
|
+
# loop (csharp_stryker_net_status_loop.py); the two live in different import
|
|
101
|
+
# roots (hooks/ vs skills/) so they cannot share a constant — the equivalence
|
|
102
|
+
# is armored by test_coverage_capture_regex_matches_status_loop.
|
|
103
|
+
_COVERAGE_CAPTURE_FAILURE_RE = re.compile(
|
|
104
|
+
r"test coverage capture failed|disable coverage based optimisation",
|
|
105
|
+
re.IGNORECASE,
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def detect_coverage_capture_failure(text: str) -> bool:
|
|
110
|
+
"""True when `text` (Stryker's captured stdout/stderr) shows per-test
|
|
111
|
+
coverage capture failed and Stryker fell back to whole-suite-per-mutant.
|
|
112
|
+
|
|
113
|
+
A pure predicate later slices (#1158's feasibility gate) branch on to
|
|
114
|
+
decide loop-vs-degrade. Matches either half of 'It looks like the test
|
|
115
|
+
coverage capture failed. Disable coverage based optimisation.'
|
|
116
|
+
"""
|
|
117
|
+
if not text:
|
|
118
|
+
return False
|
|
119
|
+
return bool(_COVERAGE_CAPTURE_FAILURE_RE.search(text))
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def emit_advisory(message: str) -> str:
|
|
123
|
+
"""Return the PostToolUse additionalContext envelope for an advisory message."""
|
|
124
|
+
return json.dumps(
|
|
125
|
+
{
|
|
126
|
+
"hookSpecificOutput": {
|
|
127
|
+
"hookEventName": "PostToolUse",
|
|
128
|
+
"additionalContext": message,
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
# ---------------------------------------------------------------------------
|
|
135
|
+
# State file management — mirrors bash lib
|
|
136
|
+
#
|
|
137
|
+
# Location: $TMPDIR/mutation-gate/session-<12-char sha256 of cwd>
|
|
138
|
+
# TTL: 4 hours (14400 seconds)
|
|
139
|
+
# ---------------------------------------------------------------------------
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
_STATE_TTL_SECONDS = 14400
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def _state_dir() -> Path:
|
|
146
|
+
return Path(os.environ.get("TMPDIR", "/tmp")) / "mutation-gate"
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def state_file_path(cwd: Path | None = None) -> Path:
|
|
150
|
+
"""Return the per-project state file path (`session-<hash>`)."""
|
|
151
|
+
base = str(cwd if cwd is not None else Path.cwd())
|
|
152
|
+
digest = hashlib.sha256(base.encode("utf-8")).hexdigest()[:12]
|
|
153
|
+
return _state_dir() / f"session-{digest}"
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def _purge_stale_states(state_dir: Path) -> None:
|
|
157
|
+
"""Delete session-* files older than TTL. Best-effort; errors ignored."""
|
|
158
|
+
if not state_dir.is_dir():
|
|
159
|
+
return
|
|
160
|
+
now = time.time()
|
|
161
|
+
for path in state_dir.glob("session-*"):
|
|
162
|
+
try:
|
|
163
|
+
if now - path.stat().st_mtime > _STATE_TTL_SECONDS:
|
|
164
|
+
path.unlink()
|
|
165
|
+
except OSError:
|
|
166
|
+
pass
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def write_state(result: str, event_json: str, cwd: Path | None = None) -> None:
|
|
170
|
+
"""Write the state file from a PostToolUse event body.
|
|
171
|
+
|
|
172
|
+
`result` is "pass" or "fail"; `event_json` is the raw stdin JSON string —
|
|
173
|
+
we extract `tool_response.output` and truncate it to 64 KiB, matching the
|
|
174
|
+
bash `head -c 65536` behaviour.
|
|
175
|
+
"""
|
|
176
|
+
state_dir = _state_dir()
|
|
177
|
+
state_dir.mkdir(parents=True, exist_ok=True)
|
|
178
|
+
_purge_stale_states(state_dir)
|
|
179
|
+
try:
|
|
180
|
+
event = json.loads(event_json) if event_json else {}
|
|
181
|
+
except (TypeError, ValueError):
|
|
182
|
+
event = {}
|
|
183
|
+
tool_response = event.get("tool_response") or {}
|
|
184
|
+
runner_stdout = tool_response.get("output") or ""
|
|
185
|
+
if len(runner_stdout) > 65536:
|
|
186
|
+
runner_stdout = runner_stdout[:65536]
|
|
187
|
+
payload = {
|
|
188
|
+
"result": result,
|
|
189
|
+
"timestamp": int(time.time()),
|
|
190
|
+
"runner_stdout": runner_stdout,
|
|
191
|
+
}
|
|
192
|
+
target = state_file_path(cwd)
|
|
193
|
+
target.write_text(json.dumps(payload))
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def read_state(cwd: Path | None = None) -> str:
|
|
197
|
+
"""Return the state JSON as a string, or `""` if absent / stale.
|
|
198
|
+
|
|
199
|
+
Stale files (> TTL) are deleted lazily on read so subsequent calls don't
|
|
200
|
+
keep re-reading them.
|
|
201
|
+
"""
|
|
202
|
+
path = state_file_path(cwd)
|
|
203
|
+
if not path.is_file():
|
|
204
|
+
return ""
|
|
205
|
+
try:
|
|
206
|
+
data = json.loads(path.read_text())
|
|
207
|
+
except (OSError, ValueError):
|
|
208
|
+
return ""
|
|
209
|
+
ts = data.get("timestamp") or 0
|
|
210
|
+
if int(time.time()) - int(ts) > _STATE_TTL_SECONDS:
|
|
211
|
+
try:
|
|
212
|
+
path.unlink()
|
|
213
|
+
except OSError:
|
|
214
|
+
pass
|
|
215
|
+
return ""
|
|
216
|
+
return path.read_text()
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
# ---------------------------------------------------------------------------
|
|
220
|
+
# Result detection
|
|
221
|
+
# ---------------------------------------------------------------------------
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
_PASS_PATTERNS = re.compile(
|
|
225
|
+
r"passing|tests run.*failures: 0|all tests passed|✓|ok [0-9]",
|
|
226
|
+
re.IGNORECASE,
|
|
227
|
+
)
|
|
228
|
+
_FAIL_PATTERNS = re.compile(
|
|
229
|
+
r"failing|failures: [1-9]|error:|FAILED",
|
|
230
|
+
re.IGNORECASE,
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def detect_result(event_json: str) -> str:
|
|
235
|
+
"""Return "pass" or "fail" for a PostToolUse event.
|
|
236
|
+
|
|
237
|
+
Primary signal: `tool_response.exit_code`. Secondary: stdout patterns —
|
|
238
|
+
used for runners that always exit 0. Default: "fail" when uncertain (the
|
|
239
|
+
bash lib does the same).
|
|
240
|
+
"""
|
|
241
|
+
try:
|
|
242
|
+
event = json.loads(event_json) if event_json else {}
|
|
243
|
+
except (TypeError, ValueError):
|
|
244
|
+
event = {}
|
|
245
|
+
tool_response = event.get("tool_response") or {}
|
|
246
|
+
exit_code = tool_response.get("exit_code")
|
|
247
|
+
if exit_code == 0:
|
|
248
|
+
return "pass"
|
|
249
|
+
if exit_code is not None and exit_code != -1:
|
|
250
|
+
return "fail"
|
|
251
|
+
output = tool_response.get("output") or ""
|
|
252
|
+
if _PASS_PATTERNS.search(output):
|
|
253
|
+
return "pass"
|
|
254
|
+
if _FAIL_PATTERNS.search(output):
|
|
255
|
+
return "fail"
|
|
256
|
+
return "fail"
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
def is_red_to_green(prev_result: str, cur_result: str) -> bool:
|
|
260
|
+
"""Return True if `prev_result` = fail and `cur_result` = pass."""
|
|
261
|
+
return prev_result == "fail" and cur_result == "pass"
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
# ---------------------------------------------------------------------------
|
|
265
|
+
# Test command detection
|
|
266
|
+
# ---------------------------------------------------------------------------
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
def _tokens(command: str) -> list[str]:
|
|
270
|
+
return command.strip().split()
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
def is_test_command(command: str) -> bool:
|
|
274
|
+
"""Return True when `command` looks like a supported test runner invocation."""
|
|
275
|
+
tokens = _tokens(command)
|
|
276
|
+
if not tokens:
|
|
277
|
+
return False
|
|
278
|
+
head = tokens[0]
|
|
279
|
+
if head == "npm":
|
|
280
|
+
if len(tokens) >= 2 and tokens[1] == "test":
|
|
281
|
+
return True
|
|
282
|
+
return len(tokens) >= 3 and tokens[1] == "run" and tokens[2] == "test"
|
|
283
|
+
if head == "npx" and len(tokens) >= 2 and tokens[1] in {"vitest", "jest"}:
|
|
284
|
+
return True
|
|
285
|
+
if (
|
|
286
|
+
head in {"mvn", "./mvnw", "mvnw"}
|
|
287
|
+
and len(tokens) >= 2
|
|
288
|
+
and tokens[1]
|
|
289
|
+
in {
|
|
290
|
+
"test",
|
|
291
|
+
"verify",
|
|
292
|
+
}
|
|
293
|
+
):
|
|
294
|
+
return True
|
|
295
|
+
if (
|
|
296
|
+
head in {"gradle", "./gradlew", "gradlew"}
|
|
297
|
+
and len(tokens) >= 2
|
|
298
|
+
and tokens[1] == "test"
|
|
299
|
+
):
|
|
300
|
+
return True
|
|
301
|
+
if head == "dotnet" and len(tokens) >= 2 and tokens[1] == "test":
|
|
302
|
+
return True
|
|
303
|
+
if head == "pytest":
|
|
304
|
+
return True
|
|
305
|
+
if head in {"python", "python3"} and any("pytest" in t for t in tokens[1:]):
|
|
306
|
+
return True
|
|
307
|
+
if head == "go" and len(tokens) >= 2 and tokens[1] == "test":
|
|
308
|
+
return True
|
|
309
|
+
return head == "cargo" and len(tokens) >= 2 and tokens[1] == "test"
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
# ---------------------------------------------------------------------------
|
|
313
|
+
# Adapter dispatch
|
|
314
|
+
# ---------------------------------------------------------------------------
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
def detect_adapter(command: str) -> str:
|
|
318
|
+
"""Return one of stryker | pitest | stryker-net | mutmut | none."""
|
|
319
|
+
tokens = _tokens(command)
|
|
320
|
+
if not tokens:
|
|
321
|
+
return "none"
|
|
322
|
+
head = tokens[0]
|
|
323
|
+
if head == "npm" and (
|
|
324
|
+
(len(tokens) >= 2 and tokens[1] == "test")
|
|
325
|
+
or (len(tokens) >= 3 and tokens[1] == "run" and tokens[2] == "test")
|
|
326
|
+
):
|
|
327
|
+
return "stryker"
|
|
328
|
+
if head == "npx" and len(tokens) >= 2 and tokens[1] in {"vitest", "jest"}:
|
|
329
|
+
return "stryker"
|
|
330
|
+
if (
|
|
331
|
+
head in {"mvn", "./mvnw", "mvnw"}
|
|
332
|
+
and len(tokens) >= 2
|
|
333
|
+
and tokens[1]
|
|
334
|
+
in {
|
|
335
|
+
"test",
|
|
336
|
+
"verify",
|
|
337
|
+
}
|
|
338
|
+
):
|
|
339
|
+
return "pitest"
|
|
340
|
+
if (
|
|
341
|
+
head in {"gradle", "./gradlew", "gradlew"}
|
|
342
|
+
and len(tokens) >= 2
|
|
343
|
+
and tokens[1] == "test"
|
|
344
|
+
):
|
|
345
|
+
return "pitest"
|
|
346
|
+
if head == "dotnet" and len(tokens) >= 2 and tokens[1] == "test":
|
|
347
|
+
return "stryker-net"
|
|
348
|
+
if head == "pytest":
|
|
349
|
+
return "mutmut"
|
|
350
|
+
if head in {"python", "python3"}:
|
|
351
|
+
# python -m pytest, python3 -m pytest, python somefile-pytest, python -m unittest
|
|
352
|
+
for t in tokens[1:]:
|
|
353
|
+
if "pytest" in t or "unittest" in t:
|
|
354
|
+
return "mutmut"
|
|
355
|
+
return "none"
|
|
356
|
+
return "none"
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
# ---------------------------------------------------------------------------
|
|
360
|
+
# First changed source file — shared by pitest / stryker-net / mutmut adapters
|
|
361
|
+
#
|
|
362
|
+
# Each adapter needs "the first non-test source file touched by this change"
|
|
363
|
+
# to scope its mutation run (target class, --mutate pattern, --paths-to-mutate).
|
|
364
|
+
# The traversal (git diff --name-only HEAD, falling back to --cached) is
|
|
365
|
+
# identical across adapters; only the extension set and test-name exclusion
|
|
366
|
+
# differ, so those are supplied as a single predicate.
|
|
367
|
+
# ---------------------------------------------------------------------------
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
def first_changed_file(is_source_predicate: Callable[[str], bool]) -> str:
|
|
371
|
+
"""Return the first changed file for which `is_source_predicate` is True.
|
|
372
|
+
|
|
373
|
+
Checks `git diff --name-only HEAD` first, then falls back to
|
|
374
|
+
`git diff --cached --name-only` (mirrors the bash adapters' behaviour of
|
|
375
|
+
preferring unstaged/working-tree changes over already-staged ones).
|
|
376
|
+
Returns "" when git is unavailable or no line satisfies the predicate.
|
|
377
|
+
"""
|
|
378
|
+
for cmd in (
|
|
379
|
+
["git", "diff", "--name-only", "HEAD"],
|
|
380
|
+
["git", "diff", "--cached", "--name-only"],
|
|
381
|
+
):
|
|
382
|
+
try:
|
|
383
|
+
proc = subprocess.run(cmd, capture_output=True, text=True, check=False)
|
|
384
|
+
except (FileNotFoundError, OSError):
|
|
385
|
+
return ""
|
|
386
|
+
for line in proc.stdout.splitlines():
|
|
387
|
+
if is_source_predicate(line):
|
|
388
|
+
return line
|
|
389
|
+
return ""
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
# ---------------------------------------------------------------------------
|
|
393
|
+
# Zero-kill parsing (Stryker JSON schema, shared by stryker + stryker-net)
|
|
394
|
+
# ---------------------------------------------------------------------------
|
|
395
|
+
|
|
396
|
+
|
|
397
|
+
def parse_stryker_kills(report_file: Path, output_file: Path) -> str | None:
|
|
398
|
+
"""Parse Stryker JSON, write zero-kills to `output_file`.
|
|
399
|
+
|
|
400
|
+
Returns None on success; returns the advisory JSON string when the report
|
|
401
|
+
is missing (mirrors bash: bash emits the advisory to stdout, callers
|
|
402
|
+
forward it). Caller writes `[]` to `output_file` in that path already so
|
|
403
|
+
the return value is only consumed for the advisory.
|
|
404
|
+
"""
|
|
405
|
+
report_file = Path(report_file)
|
|
406
|
+
output_file = Path(output_file)
|
|
407
|
+
if not report_file.is_file():
|
|
408
|
+
advisory = emit_advisory(
|
|
409
|
+
f"MUTATION GATE ADVISORY: Stryker report not found at {report_file}. "
|
|
410
|
+
"Run completed without mutation gate."
|
|
411
|
+
)
|
|
412
|
+
try:
|
|
413
|
+
output_file.write_text("[]")
|
|
414
|
+
except OSError:
|
|
415
|
+
pass
|
|
416
|
+
return advisory
|
|
417
|
+
|
|
418
|
+
data = json.loads(report_file.read_text())
|
|
419
|
+
mutants = data.get("mutants", [])
|
|
420
|
+
|
|
421
|
+
killing_set = set()
|
|
422
|
+
coverage: dict[str, int] = {}
|
|
423
|
+
for mutant in mutants:
|
|
424
|
+
for t in mutant.get("killedBy", []):
|
|
425
|
+
killing_set.add(t)
|
|
426
|
+
for t in mutant.get("coveredBy", []):
|
|
427
|
+
coverage[t] = coverage.get(t, 0) + 1
|
|
428
|
+
|
|
429
|
+
zero_kills: list[dict[str, Any]] = []
|
|
430
|
+
for test in sorted(set(coverage.keys()) - killing_set):
|
|
431
|
+
zero_kills.append(
|
|
432
|
+
{
|
|
433
|
+
"name": test,
|
|
434
|
+
"file": None,
|
|
435
|
+
"line": None,
|
|
436
|
+
"covered": coverage[test],
|
|
437
|
+
}
|
|
438
|
+
)
|
|
439
|
+
|
|
440
|
+
output_file.write_text(json.dumps(zero_kills))
|
|
441
|
+
return None
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
# ---------------------------------------------------------------------------
|
|
445
|
+
# Blocking-reason formatter — same output as the bash inline Python heredoc.
|
|
446
|
+
# ---------------------------------------------------------------------------
|
|
447
|
+
|
|
448
|
+
|
|
449
|
+
def format_blocking_reason(zero_kills_file: Path, command: str) -> str:
|
|
450
|
+
"""Return the multi-line human-readable reason string for a blocked gate.
|
|
451
|
+
|
|
452
|
+
`command` is unused (kept for signature parity with bash) — the message
|
|
453
|
+
catalogs the zero-kill tests read from `zero_kills_file`.
|
|
454
|
+
"""
|
|
455
|
+
with open(zero_kills_file) as f:
|
|
456
|
+
zero_kills = json.load(f)
|
|
457
|
+
|
|
458
|
+
lines = [
|
|
459
|
+
"MUTATION GATE BLOCKED: zero-kill tests detected after RED-GREEN transition",
|
|
460
|
+
"",
|
|
461
|
+
"These tests covered mutants but killed none — they provide no regression safety.",
|
|
462
|
+
"A useful assertion changes the expected value when the implementation changes",
|
|
463
|
+
"(e.g., assert result === 5, not assert result !== undefined).",
|
|
464
|
+
"Rewrite each to assert the precise return value or side effect, or remove it:",
|
|
465
|
+
"",
|
|
466
|
+
]
|
|
467
|
+
for zk in zero_kills:
|
|
468
|
+
name = zk.get("name", "<unknown>")
|
|
469
|
+
file_ = zk.get("file") or "<unknown>"
|
|
470
|
+
line = zk.get("line")
|
|
471
|
+
loc = f"({file_}:{line})" if line else f"({file_}:<unknown>)"
|
|
472
|
+
covered = zk.get("covered", 0)
|
|
473
|
+
lines.append(f" - {name} {loc}")
|
|
474
|
+
lines.append(f" Covered {covered} mutants, killed 0")
|
|
475
|
+
lines.append("")
|
|
476
|
+
|
|
477
|
+
lines.append("Fix these tests before continuing.")
|
|
478
|
+
return "\n".join(lines)
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
"""mutmut (Python) mutation-testing adapter — Python port of mutmut.sh.
|
|
2
|
+
|
|
3
|
+
mutmut scopes to a single file via `--paths-to-mutate` and completes in
|
|
4
|
+
seconds-to-minutes for typical Python unit test suites. mutmut has no
|
|
5
|
+
`--json` results output; survivors are read from `mutmut junitxml`.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import json
|
|
11
|
+
import os
|
|
12
|
+
import shutil
|
|
13
|
+
import subprocess
|
|
14
|
+
import sys
|
|
15
|
+
import xml.etree.ElementTree as ET
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
|
|
18
|
+
from . import lib
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def mutmut_detect() -> bool:
|
|
22
|
+
"""Return True when mutmut is available in this project."""
|
|
23
|
+
if shutil.which("mutmut") is not None:
|
|
24
|
+
return True
|
|
25
|
+
try:
|
|
26
|
+
# `mutmut` is a `version` subcommand, not a `--version` flag — the
|
|
27
|
+
# flag form exits nonzero on the real CLI and always reads as absent.
|
|
28
|
+
proc = subprocess.run(
|
|
29
|
+
["python3", "-m", "mutmut", "version"],
|
|
30
|
+
stdout=subprocess.DEVNULL,
|
|
31
|
+
stderr=subprocess.DEVNULL,
|
|
32
|
+
check=False,
|
|
33
|
+
)
|
|
34
|
+
if proc.returncode == 0:
|
|
35
|
+
return True
|
|
36
|
+
except (FileNotFoundError, OSError):
|
|
37
|
+
pass
|
|
38
|
+
print(
|
|
39
|
+
lib.emit_advisory(
|
|
40
|
+
"MUTATION GATE ADVISORY: mutmut not installed. Run /setup to "
|
|
41
|
+
"install it, or: pip install mutmut"
|
|
42
|
+
)
|
|
43
|
+
)
|
|
44
|
+
return False
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _derive_python_source(test_file: str) -> str:
|
|
48
|
+
"""Strip test_ prefix / _test suffix from `test_file` to find the source."""
|
|
49
|
+
base = Path(test_file).stem
|
|
50
|
+
base = base.removeprefix("test_")
|
|
51
|
+
base = base.removesuffix("_test")
|
|
52
|
+
for dir_ in ("src", "", "lib"):
|
|
53
|
+
candidate = f"{dir_}/{base}.py" if dir_ else f"{base}.py"
|
|
54
|
+
if Path(candidate).is_file():
|
|
55
|
+
return candidate
|
|
56
|
+
return test_file
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _is_python_source(line: str) -> bool:
|
|
60
|
+
"""True for a non-test Python source path."""
|
|
61
|
+
if not line.endswith(".py"):
|
|
62
|
+
return False
|
|
63
|
+
return not (line.startswith("test_") or "_test.py" in line or "/test_" in line)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _changed_python_source() -> str:
|
|
67
|
+
return lib.first_changed_file(_is_python_source)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _mutmut_argv() -> list[str]:
|
|
71
|
+
"""Return the argv prefix for invoking mutmut — either `mutmut` or `python3 -m mutmut`."""
|
|
72
|
+
if shutil.which("mutmut") is not None:
|
|
73
|
+
return ["mutmut"]
|
|
74
|
+
return ["python3", "-m", "mutmut"]
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def mutmut_run(output_file: Path) -> int:
|
|
78
|
+
"""Run mutmut scoped to the changed Python file; write zero-kills."""
|
|
79
|
+
output_file = Path(output_file)
|
|
80
|
+
timeout_seconds = int(os.environ.get("ADAPTER_TIMEOUT", "120"))
|
|
81
|
+
|
|
82
|
+
src_file = _changed_python_source()
|
|
83
|
+
|
|
84
|
+
mutmut_prefix = _mutmut_argv()
|
|
85
|
+
mutmut_args = ["run"]
|
|
86
|
+
if src_file:
|
|
87
|
+
mutmut_args.append(f"--paths-to-mutate={src_file}")
|
|
88
|
+
|
|
89
|
+
argv = [*mutmut_prefix, *mutmut_args]
|
|
90
|
+
completed = lib.run_with_timeout(
|
|
91
|
+
timeout_seconds,
|
|
92
|
+
argv,
|
|
93
|
+
stdout=subprocess.DEVNULL,
|
|
94
|
+
stderr=subprocess.PIPE,
|
|
95
|
+
text=True,
|
|
96
|
+
)
|
|
97
|
+
exit_code = completed.returncode
|
|
98
|
+
|
|
99
|
+
if exit_code == 124:
|
|
100
|
+
hint = (
|
|
101
|
+
""
|
|
102
|
+
if src_file
|
|
103
|
+
else " No source file detected — scope with --paths-to-mutate."
|
|
104
|
+
)
|
|
105
|
+
print(
|
|
106
|
+
lib.emit_advisory(
|
|
107
|
+
f"MUTATION GATE SKIPPED: mutmut timed out after {timeout_seconds}s.{hint}"
|
|
108
|
+
" Or: MUTATION_GATE_TIMEOUT=<seconds> to extend the limit."
|
|
109
|
+
)
|
|
110
|
+
)
|
|
111
|
+
output_file.write_text("[]")
|
|
112
|
+
return 0
|
|
113
|
+
|
|
114
|
+
# mutmut's exit code is a bitmask: 1=fatal error, 2=survived, 4=timeout,
|
|
115
|
+
# 8=slow (any combination ORs together; 0 means every mutant was
|
|
116
|
+
# killed). Bit 1 (fatal error — e.g. the baseline test run itself
|
|
117
|
+
# failed) is the only outcome that invalidates the run; survivors
|
|
118
|
+
# (bit 2) are the expected, common case and must still be parsed.
|
|
119
|
+
if exit_code & 1:
|
|
120
|
+
stderr = completed.stderr or ""
|
|
121
|
+
if "cannot pickle" in stderr and "itertools.count" in stderr:
|
|
122
|
+
hint = (
|
|
123
|
+
" Known cause: mutmut<3 crashes on Python 3.13+ "
|
|
124
|
+
"('TypeError: cannot pickle itertools.count object', a "
|
|
125
|
+
"parso/pony-ORM deepcopy incompatibility) — install mutmut "
|
|
126
|
+
"into a venv running Python <=3.12; the --runner test "
|
|
127
|
+
"command can stay on the project's real interpreter."
|
|
128
|
+
)
|
|
129
|
+
else:
|
|
130
|
+
hint = ""
|
|
131
|
+
print(
|
|
132
|
+
lib.emit_advisory(
|
|
133
|
+
f"MUTATION GATE ADVISORY: mutmut exited with code {exit_code}. "
|
|
134
|
+
f"Skipping mutation gate.{hint}"
|
|
135
|
+
)
|
|
136
|
+
)
|
|
137
|
+
output_file.write_text("[]")
|
|
138
|
+
return 0
|
|
139
|
+
|
|
140
|
+
# Read survivors from `mutmut junitxml` — mutmut's `results` subcommand
|
|
141
|
+
# has no `--json` output; junitxml is the only structured report it
|
|
142
|
+
# ships. A <testcase> with a <failure> child is a survived mutant under
|
|
143
|
+
# the default suspicious/untested policies (both "ignore").
|
|
144
|
+
try:
|
|
145
|
+
junit = subprocess.run(
|
|
146
|
+
[*mutmut_prefix, "junitxml"],
|
|
147
|
+
capture_output=True,
|
|
148
|
+
text=True,
|
|
149
|
+
check=False,
|
|
150
|
+
)
|
|
151
|
+
raw = junit.stdout or ""
|
|
152
|
+
except (FileNotFoundError, OSError):
|
|
153
|
+
raw = ""
|
|
154
|
+
|
|
155
|
+
try:
|
|
156
|
+
root = ET.fromstring(raw) if raw.strip() else None
|
|
157
|
+
except ET.ParseError:
|
|
158
|
+
root = None
|
|
159
|
+
|
|
160
|
+
zero_kills = []
|
|
161
|
+
if root is not None:
|
|
162
|
+
for testcase in root.iter("testcase"):
|
|
163
|
+
if testcase.find("failure") is None:
|
|
164
|
+
continue
|
|
165
|
+
line = testcase.get("line")
|
|
166
|
+
zero_kills.append(
|
|
167
|
+
{
|
|
168
|
+
"name": f"mutant_{testcase.get('name', '?')}",
|
|
169
|
+
"file": testcase.get("file"),
|
|
170
|
+
"line": int(line) if line and line.isdigit() else None,
|
|
171
|
+
"covered": 0,
|
|
172
|
+
}
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
output_file.write_text(json.dumps(zero_kills))
|
|
176
|
+
return 0
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def main(argv: list[str] | None = None) -> int:
|
|
180
|
+
args = list(sys.argv[1:] if argv is None else argv)
|
|
181
|
+
if not args:
|
|
182
|
+
print("mutmut.py: OUTPUT_FILE argument required", file=sys.stderr)
|
|
183
|
+
return 2
|
|
184
|
+
return mutmut_run(Path(args[0]))
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
if __name__ == "__main__": # pragma: no cover
|
|
188
|
+
sys.exit(main())
|