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,681 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""csharp_stryker_net_wrapper.py — reference wrapper for Stryker.NET.
|
|
3
|
+
|
|
4
|
+
Cross-platform authoritative: macOS, Linux, Windows Git Bash, and native
|
|
5
|
+
Windows all execute the same code path through Python 3's ``subprocess``,
|
|
6
|
+
``signal``, and ``pathlib`` — no shell-tooling divergence.
|
|
7
|
+
|
|
8
|
+
Copy this file AND ``csharp_stryker_net_status_loop.py`` together into your
|
|
9
|
+
repo's ``scripts/`` directory, and run in place of a bare ``dotnet stryker``
|
|
10
|
+
invocation (override defaults via CLI flags or the corresponding env vars).
|
|
11
|
+
|
|
12
|
+
Contract preserved from the bash version:
|
|
13
|
+
|
|
14
|
+
- ``DOTNET_ROOT`` probe across macOS Homebrew (Apple Silicon + Intel),
|
|
15
|
+
Debian/Ubuntu, Fedora/RHEL, user-scope, and Windows install paths
|
|
16
|
+
- Pre-building ``${SLN}`` and ``${SHIM_PROJECT}`` BEFORE hiding ``.sln``
|
|
17
|
+
(Stryker's own build step can't rebuild whole-solution after the hide)
|
|
18
|
+
- Hiding ``.sln`` during the run + restoring it on any exit path — normal
|
|
19
|
+
exit, non-zero exit, SIGINT (Ctrl-C), SIGTERM, KeyboardInterrupt
|
|
20
|
+
- Refusing (exit 2) when a stale ``.sln.stryker-hidden`` coexists with a
|
|
21
|
+
fresh ``.sln`` (idempotent — never silently clobbers either)
|
|
22
|
+
- Running Stryker as a subprocess so we can kill it on any signal — no
|
|
23
|
+
orphaned Stryker processes in CI or backgrounded contexts
|
|
24
|
+
- Direct log redirect (never a bare ``| tee`` — see #550)
|
|
25
|
+
|
|
26
|
+
Refs: #554, #557, #558, #559, #564, #567 (Windows verification), #572
|
|
27
|
+
(bash → Python migration epic).
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
from __future__ import annotations
|
|
31
|
+
|
|
32
|
+
import argparse
|
|
33
|
+
import os
|
|
34
|
+
import shutil
|
|
35
|
+
import signal
|
|
36
|
+
import subprocess
|
|
37
|
+
import sys
|
|
38
|
+
import threading
|
|
39
|
+
from collections.abc import Callable, Sequence
|
|
40
|
+
from pathlib import Path, PureWindowsPath
|
|
41
|
+
|
|
42
|
+
# =============================================================================
|
|
43
|
+
# Exit codes — same as the bash version, byte-compatible. EXIT_RESTORE_SLN_FAILED
|
|
44
|
+
# is new (#1955) — no bash-version precedent, since restore_sln() previously
|
|
45
|
+
# reported no outcome for main() to surface.
|
|
46
|
+
# =============================================================================
|
|
47
|
+
EXIT_OK = 0
|
|
48
|
+
EXIT_STALE_HIDDEN_SLN = 2
|
|
49
|
+
EXIT_NO_DOTNET_SDK = 3
|
|
50
|
+
EXIT_RESTORE_SLN_FAILED = 4
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
# =============================================================================
|
|
54
|
+
# DOTNET_ROOT probe — the 7-position chain documented in the spec.
|
|
55
|
+
# =============================================================================
|
|
56
|
+
def default_probe_candidates() -> list[str]:
|
|
57
|
+
"""Return the ordered default probe list. Named function so callers (and
|
|
58
|
+
tests) can point at a single authoritative source instead of copying the
|
|
59
|
+
literal path list.
|
|
60
|
+
"""
|
|
61
|
+
home = str(Path.home())
|
|
62
|
+
return [
|
|
63
|
+
"/opt/homebrew/opt/dotnet/libexec", # 1: macOS Apple Silicon Homebrew
|
|
64
|
+
"/usr/local/opt/dotnet/libexec", # 2: macOS Intel Homebrew
|
|
65
|
+
"/usr/share/dotnet", # 3: Debian/Ubuntu package
|
|
66
|
+
"/usr/lib/dotnet", # 4: Fedora/RHEL package
|
|
67
|
+
f"{home}/.dotnet", # 5: user-scope (dotnet-install.sh)
|
|
68
|
+
"/c/Program Files/dotnet", # 6: Windows Git Bash Program Files
|
|
69
|
+
"/c/program files/dotnet", # 7: Windows lowercase drive mount
|
|
70
|
+
# Native-Windows path (drive letter, no MSYS mount). Sensible extension
|
|
71
|
+
# of the bash contract on the native-Windows target.
|
|
72
|
+
r"C:\Program Files\dotnet",
|
|
73
|
+
]
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def probe_dotnet_root(candidates: Sequence[str]) -> str | None:
|
|
77
|
+
"""Return the first candidate whose SDK layout is present, or None.
|
|
78
|
+
|
|
79
|
+
A candidate hits when any of these are true:
|
|
80
|
+
- ``<c>/dotnet`` is an executable file
|
|
81
|
+
- ``<c>/dotnet.exe`` is an executable file
|
|
82
|
+
- ``<c>/shared`` is a directory
|
|
83
|
+
|
|
84
|
+
Empty candidate strings are skipped so callers can concatenate lists
|
|
85
|
+
without worrying about stray separators.
|
|
86
|
+
"""
|
|
87
|
+
for candidate in candidates:
|
|
88
|
+
if not candidate:
|
|
89
|
+
continue
|
|
90
|
+
c = Path(candidate)
|
|
91
|
+
dotnet_bin = c / "dotnet"
|
|
92
|
+
dotnet_exe = c / "dotnet.exe"
|
|
93
|
+
shared_dir = c / "shared"
|
|
94
|
+
if (
|
|
95
|
+
(dotnet_bin.is_file() and os.access(dotnet_bin, os.X_OK))
|
|
96
|
+
or (dotnet_exe.is_file() and os.access(dotnet_exe, os.X_OK))
|
|
97
|
+
or shared_dir.is_dir()
|
|
98
|
+
):
|
|
99
|
+
return str(c)
|
|
100
|
+
return None
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def resolve_dotnet_root(
|
|
104
|
+
preset: str | None,
|
|
105
|
+
candidates: Sequence[str],
|
|
106
|
+
) -> tuple[str | None, str | None]:
|
|
107
|
+
"""Return ``(dotnet_root, error_message)``.
|
|
108
|
+
|
|
109
|
+
- Preset value wins if truthy — matches the bash ``${DOTNET_ROOT:-}`` guard.
|
|
110
|
+
- Probe the candidate list next.
|
|
111
|
+
- Fall back to the directory containing ``dotnet`` on PATH.
|
|
112
|
+
- Return (None, error_message) when nothing resolves.
|
|
113
|
+
"""
|
|
114
|
+
if preset:
|
|
115
|
+
return preset, None
|
|
116
|
+
hit = probe_dotnet_root(candidates)
|
|
117
|
+
if hit is not None:
|
|
118
|
+
return hit, None
|
|
119
|
+
on_path = shutil.which("dotnet")
|
|
120
|
+
if on_path:
|
|
121
|
+
return str(Path(on_path).resolve().parent), None
|
|
122
|
+
msg = _no_sdk_message(candidates)
|
|
123
|
+
return None, msg
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def _no_sdk_message(candidates: Sequence[str]) -> str:
|
|
127
|
+
lines = [
|
|
128
|
+
"error: no .NET SDK found; DOTNET_ROOT is unset and no candidate resolved",
|
|
129
|
+
" probed:",
|
|
130
|
+
]
|
|
131
|
+
for c in candidates:
|
|
132
|
+
if c:
|
|
133
|
+
lines.append(f" {c}")
|
|
134
|
+
lines.append(" also tried: dirname of `dotnet` on PATH — dotnet not on PATH")
|
|
135
|
+
lines.append(
|
|
136
|
+
"set DOTNET_ROOT explicitly, or install .NET: https://dotnet.microsoft.com/download"
|
|
137
|
+
)
|
|
138
|
+
return "\n".join(lines) + "\n"
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
# =============================================================================
|
|
142
|
+
# Stale-hidden-.sln refusal
|
|
143
|
+
# =============================================================================
|
|
144
|
+
def check_stale_hidden_sln(sln: Path, sln_hidden: Path) -> str | None:
|
|
145
|
+
"""Return an error message if a stale hidden .sln coexists with a fresh
|
|
146
|
+
one; otherwise None. Refusal is deterministic — the wrapper produces no
|
|
147
|
+
side effects when it refuses.
|
|
148
|
+
"""
|
|
149
|
+
if sln_hidden.exists() and sln.exists():
|
|
150
|
+
return (
|
|
151
|
+
f"error: stale {sln_hidden} present alongside fresh {sln}\n"
|
|
152
|
+
f"resolve manually before rerunning "
|
|
153
|
+
f"(delete the stale hidden file if the fresh {sln} is correct)\n"
|
|
154
|
+
)
|
|
155
|
+
return None
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
# =============================================================================
|
|
159
|
+
# .sln hide/restore — used inside a try/finally so restore runs on any exit.
|
|
160
|
+
# =============================================================================
|
|
161
|
+
def hide_sln(sln: Path, sln_hidden: Path) -> None:
|
|
162
|
+
"""Move .sln to .sln.stryker-hidden. Idempotent — a no-op if already hidden."""
|
|
163
|
+
if sln.exists() and not sln_hidden.exists():
|
|
164
|
+
sln.rename(sln_hidden)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def restore_sln(sln: Path, sln_hidden: Path) -> bool:
|
|
168
|
+
"""Restore .sln from .sln.stryker-hidden. Guarded so we never clobber a
|
|
169
|
+
fresh .sln written mid-run (rare but real — some IDEs rewrite the file).
|
|
170
|
+
|
|
171
|
+
Returns True only when the hidden file existed and was renamed back to
|
|
172
|
+
``sln`` successfully. Returns False (#1955) — never raises — in every
|
|
173
|
+
case where restoration didn't happen: the guard's precondition wasn't
|
|
174
|
+
met (``sln_hidden`` doesn't exist at all — the hide never happened, or
|
|
175
|
+
something else already removed it — or a fresh ``sln`` already exists
|
|
176
|
+
alongside the hidden one, the same stale-coexistence condition
|
|
177
|
+
``check_stale_hidden_sln`` refuses at the START of a run, observed here
|
|
178
|
+
at the END instead), or ``.rename()`` itself raises (``OSError`` /
|
|
179
|
+
``PermissionError``, e.g. a locked file on Windows).
|
|
180
|
+
|
|
181
|
+
Mirrors ``mutation_kill_shared.git_revert``'s bool-return contract so
|
|
182
|
+
callers can react uniformly. This module stays dependency-free by
|
|
183
|
+
design (the module docstring: "copy this file... into your repo's
|
|
184
|
+
scripts/ directory") — a caller that needs a hard failure signal rather
|
|
185
|
+
than a bool it must check should raise its own exception on a False
|
|
186
|
+
return; see ``mutation_kill_loop.py``'s ``run_scoped_stryker``, which
|
|
187
|
+
raises ``mutation_kill_shared.RevertFailed``, mirroring #1939's
|
|
188
|
+
``git_revert`` handling.
|
|
189
|
+
"""
|
|
190
|
+
if not sln_hidden.exists() or sln.exists():
|
|
191
|
+
return False
|
|
192
|
+
try:
|
|
193
|
+
sln_hidden.rename(sln)
|
|
194
|
+
except OSError:
|
|
195
|
+
return False
|
|
196
|
+
return True
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
# =============================================================================
|
|
200
|
+
# Main
|
|
201
|
+
# =============================================================================
|
|
202
|
+
def parse_args(argv: Sequence[str]) -> argparse.Namespace:
|
|
203
|
+
"""Parse header-var overrides and pass-through Stryker args.
|
|
204
|
+
|
|
205
|
+
Everything after ``--`` (or unknown flags) is forwarded verbatim to
|
|
206
|
+
``dotnet stryker``. Header vars can be set via env or CLI flag; CLI wins.
|
|
207
|
+
"""
|
|
208
|
+
p = argparse.ArgumentParser(
|
|
209
|
+
prog="csharp_stryker_net_wrapper.py",
|
|
210
|
+
description="Reference wrapper for Stryker.NET — cross-platform via Python.",
|
|
211
|
+
add_help=True,
|
|
212
|
+
)
|
|
213
|
+
p.add_argument(
|
|
214
|
+
"--sln",
|
|
215
|
+
default=os.environ.get("SLN", "Foo.sln"),
|
|
216
|
+
help="Solution file to hide during run (default: %(default)s)",
|
|
217
|
+
)
|
|
218
|
+
p.add_argument(
|
|
219
|
+
"--shim-project",
|
|
220
|
+
default=os.environ.get("SHIM_PROJECT", ""),
|
|
221
|
+
help="Optional shim test project to pre-build. Empty = none.",
|
|
222
|
+
)
|
|
223
|
+
p.add_argument(
|
|
224
|
+
"--stryker-bin",
|
|
225
|
+
default=os.environ.get("STRYKER_BIN", "dotnet"),
|
|
226
|
+
help="Stryker executable name, or 'dotnet' to invoke a local-tool-"
|
|
227
|
+
"manifest install via 'dotnet stryker' (default: %(default)s)",
|
|
228
|
+
)
|
|
229
|
+
p.add_argument(
|
|
230
|
+
"--logfile",
|
|
231
|
+
default=os.environ.get("LOGFILE", "StrykerOutput/wrapper.log"),
|
|
232
|
+
help="Log redirect target (default: %(default)s)",
|
|
233
|
+
)
|
|
234
|
+
p.add_argument(
|
|
235
|
+
"--stryker-concurrency",
|
|
236
|
+
default=os.environ.get("STRYKER_MUTANT_CONCURRENCY"),
|
|
237
|
+
help=(
|
|
238
|
+
"Stryker's own mutant-testing-process concurrency (its -c/"
|
|
239
|
+
"--concurrency flag). Deliberately NOT -c/--concurrency here — "
|
|
240
|
+
"see csharp-stryker-net.md. Env equivalent: "
|
|
241
|
+
"STRYKER_MUTANT_CONCURRENCY. Default: max(1, cpu_count - 2)."
|
|
242
|
+
),
|
|
243
|
+
)
|
|
244
|
+
return p.parse_known_args(list(argv))[0], p.parse_known_args(list(argv))[1]
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
# =============================================================================
|
|
248
|
+
# Concurrency default — Stryker's own `-c`/`--concurrency` mutant-testing-
|
|
249
|
+
# process count. Defaults to cores-2 instead of Stryker's flat default of 5.
|
|
250
|
+
# =============================================================================
|
|
251
|
+
def default_concurrency(cpu_count: int | None) -> int:
|
|
252
|
+
"""Return ``max(1, (cpu_count or 2) - 2)``.
|
|
253
|
+
|
|
254
|
+
``cpu_count`` is normally ``os.cpu_count()``, which returns ``None`` when
|
|
255
|
+
the machine's core count can't be determined — falls back to 2 so the
|
|
256
|
+
computed default is still 1 (never 0 or negative).
|
|
257
|
+
"""
|
|
258
|
+
return max(1, (cpu_count or 2) - 2)
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
def _pass_through_concurrency_flag(stryker_args: Sequence[str]) -> str | None:
|
|
262
|
+
"""Return the pass-through concurrency flag (``-c`` or ``--concurrency``)
|
|
263
|
+
present in ``stryker_args``, or ``None``. Consolidates the "does the
|
|
264
|
+
caller already specify concurrency" check used by both the explicit-
|
|
265
|
+
value and computed-default injection paths.
|
|
266
|
+
"""
|
|
267
|
+
for flag in ("-c", "--concurrency"):
|
|
268
|
+
if flag in stryker_args:
|
|
269
|
+
return flag
|
|
270
|
+
return None
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
def build_stryker_argv(stryker_bin: str, stryker_args: Sequence[str]) -> list[str]:
|
|
274
|
+
r"""Return the full argv for launching Stryker.NET.
|
|
275
|
+
|
|
276
|
+
``dotnet stryker ...`` is the invocation shape for a **local** tool-
|
|
277
|
+
manifest install — the skill's preferred path (see csharp-stryker-net.md
|
|
278
|
+
"Prefer a local install"). A local tool's own command name
|
|
279
|
+
(``dotnet-stryker``) is never placed on ``PATH``; it is only reachable
|
|
280
|
+
through dotnet's own verb-resolution convention (``dotnet <verb>`` finds
|
|
281
|
+
a local ``dotnet-<verb>`` tool). A **global** install, by contrast, does
|
|
282
|
+
put a bare ``dotnet-stryker`` executable on ``PATH``, invoked directly
|
|
283
|
+
with no subcommand.
|
|
284
|
+
|
|
285
|
+
Auto-detected from ``stryker_bin`` alone — no separate flag: when its
|
|
286
|
+
filename stem (case-insensitive, extension and directory stripped) is
|
|
287
|
+
``"dotnet"``, insert the ``stryker`` verb. Matching on the stem rather
|
|
288
|
+
than the raw string (#2145) means an absolute path (``/usr/bin/dotnet``)
|
|
289
|
+
or a Windows-style name (``dotnet.exe``) is recognized identically to
|
|
290
|
+
the bare ``"dotnet"`` — a functionally-equivalent value that previously
|
|
291
|
+
fell through to the bare-executable branch below with no ``stryker``
|
|
292
|
+
verb inserted, silently reproducing the failure this module exists to
|
|
293
|
+
fix. Checked with both ``Path`` (the host-native flavor — ``WindowsPath``
|
|
294
|
+
on Windows, ``PosixPath`` elsewhere) and ``PureWindowsPath`` explicitly,
|
|
295
|
+
so a backslash-separated Windows path (e.g. ``C:\Program
|
|
296
|
+
Files\dotnet\dotnet.exe``) is recognized even when this module runs
|
|
297
|
+
under a POSIX-flavored Python (Cygwin/MSYS2), where plain ``Path`` alone
|
|
298
|
+
would treat the whole string as one opaque filename component and never
|
|
299
|
+
find the ``"dotnet"`` stem. Any other value (e.g. an explicit
|
|
300
|
+
``dotnet-stryker`` override for a global install) is used as the bare
|
|
301
|
+
executable, unchanged.
|
|
302
|
+
"""
|
|
303
|
+
if (
|
|
304
|
+
Path(stryker_bin).stem.lower() == "dotnet"
|
|
305
|
+
or PureWindowsPath(stryker_bin).stem.lower() == "dotnet"
|
|
306
|
+
):
|
|
307
|
+
return [stryker_bin, "stryker", *stryker_args]
|
|
308
|
+
return [stryker_bin, *stryker_args]
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
def build_project(project: str, cwd: Path | None = None) -> int:
|
|
312
|
+
"""Run ``dotnet build <project> -c Debug --nologo`` in the given cwd.
|
|
313
|
+
Returns the subprocess exit code.
|
|
314
|
+
"""
|
|
315
|
+
return subprocess.call(
|
|
316
|
+
["dotnet", "build", project, "-c", "Debug", "--nologo"],
|
|
317
|
+
cwd=cwd,
|
|
318
|
+
)
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
def run_stryker(
|
|
322
|
+
stryker_bin: str,
|
|
323
|
+
stryker_args: Sequence[str],
|
|
324
|
+
logfile: Path,
|
|
325
|
+
line_callback: Callable[[str], bool] | None = None,
|
|
326
|
+
cwd: Path | None = None,
|
|
327
|
+
) -> int:
|
|
328
|
+
"""Run Stryker with stdout+stderr captured to logfile. Returns exit code.
|
|
329
|
+
|
|
330
|
+
Signal handling: Python's default SIGINT delivery raises KeyboardInterrupt
|
|
331
|
+
in the parent, which our main() catches to run the finally block. The
|
|
332
|
+
Popen child inherits the process group by default — a tty Ctrl-C reaches
|
|
333
|
+
both. On explicit `signal` from a parent process manager, main()'s signal
|
|
334
|
+
handlers intercept and terminate the child before re-raising.
|
|
335
|
+
|
|
336
|
+
Thread-safety: csharp_stryker_net_slice_runner.py's slice-level
|
|
337
|
+
parallelism (#561) calls this function concurrently from multiple
|
|
338
|
+
ThreadPoolExecutor worker threads — one live Stryker subprocess per
|
|
339
|
+
in-flight slice at once. Every live Popen is tracked in the shared,
|
|
340
|
+
lock-guarded ``_RUNNING_STRYKER_PROCS`` set (not a single-slot global) so
|
|
341
|
+
the signal handler can terminate all of them, not just whichever slice
|
|
342
|
+
last registered (#732).
|
|
343
|
+
|
|
344
|
+
Line-callback (additive, backward-compatible — #1136 Slice 5): when
|
|
345
|
+
``line_callback`` is ``None`` the behavior is *exactly* as before —
|
|
346
|
+
Stryker's stdout+stderr are redirected straight into ``logfile`` and we
|
|
347
|
+
block on ``proc.wait()``. When a callback is supplied, output is instead
|
|
348
|
+
streamed line by line: every line is TEE'd to ``logfile`` (so shard
|
|
349
|
+
post-mortem logs stay intact) *and* handed to the callback. A truthy
|
|
350
|
+
return — or a raised exception — requests an abort, and we ``terminate()``
|
|
351
|
+
the Stryker process. That is the same kill the SIGINT/SIGTERM handler runs
|
|
352
|
+
via ``_terminate_all_tracked_processes``; the proc stays tracked in
|
|
353
|
+
``_RUNNING_STRYKER_PROCS`` the whole time, so a concurrent signal and a
|
|
354
|
+
callback-abort are idempotent (``terminate()`` is guarded by ``poll()``,
|
|
355
|
+
a no-op on an already-dead process). ``cwd`` runs Stryker from another
|
|
356
|
+
directory (the shard pipeline runs each shard in its own git worktree).
|
|
357
|
+
"""
|
|
358
|
+
logfile.parent.mkdir(parents=True, exist_ok=True)
|
|
359
|
+
popen_cwd = str(cwd) if cwd is not None else None
|
|
360
|
+
if line_callback is None:
|
|
361
|
+
with logfile.open("wb") as log:
|
|
362
|
+
proc = subprocess.Popen(
|
|
363
|
+
build_stryker_argv(stryker_bin, stryker_args),
|
|
364
|
+
stdout=log,
|
|
365
|
+
stderr=subprocess.STDOUT,
|
|
366
|
+
cwd=popen_cwd,
|
|
367
|
+
)
|
|
368
|
+
# Track for signal handlers.
|
|
369
|
+
with _RUNNING_STRYKER_LOCK:
|
|
370
|
+
_RUNNING_STRYKER_PROCS.add(proc)
|
|
371
|
+
try:
|
|
372
|
+
return proc.wait()
|
|
373
|
+
finally:
|
|
374
|
+
with _RUNNING_STRYKER_LOCK:
|
|
375
|
+
_RUNNING_STRYKER_PROCS.discard(proc)
|
|
376
|
+
return _run_stryker_streaming(
|
|
377
|
+
stryker_bin, stryker_args, logfile, line_callback, popen_cwd
|
|
378
|
+
)
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
# Bounds for the abort/raise reap. Stryker.NET spawns grandchild test-hosts
|
|
382
|
+
# that inherit the stdout PIPE; on a timeout-abort they can hold the pipe open,
|
|
383
|
+
# so we must never depend on stdout reaching EOF for either the drain or the
|
|
384
|
+
# wait. These bounds only bite when a grandchild is genuinely stuck — a healthy
|
|
385
|
+
# child exits in milliseconds. Portable (no os.killpg — absent on Windows).
|
|
386
|
+
_ABORT_DRAIN_TIMEOUT_S = 10.0
|
|
387
|
+
_ABORT_WAIT_TIMEOUT_S = 30.0
|
|
388
|
+
|
|
389
|
+
|
|
390
|
+
def _terminate(proc: subprocess.Popen) -> None:
|
|
391
|
+
"""Signal the child to stop if it is still running (poll-guarded no-op
|
|
392
|
+
otherwise — idempotent with the signal handler's terminate())."""
|
|
393
|
+
if proc.poll() is None:
|
|
394
|
+
proc.terminate()
|
|
395
|
+
|
|
396
|
+
|
|
397
|
+
def _close_stdout(proc: subprocess.Popen) -> None:
|
|
398
|
+
"""Close the child's stdout to unblock any reader blocked on an EOF a
|
|
399
|
+
surviving grandchild would otherwise withhold. Best-effort — fakes without
|
|
400
|
+
a real closable stream, or an already-closed pipe, are tolerated."""
|
|
401
|
+
try:
|
|
402
|
+
if proc.stdout is not None:
|
|
403
|
+
proc.stdout.close()
|
|
404
|
+
except (AttributeError, OSError, ValueError):
|
|
405
|
+
pass
|
|
406
|
+
|
|
407
|
+
|
|
408
|
+
def _bounded_reap(proc: subprocess.Popen) -> int:
|
|
409
|
+
"""Reap ``proc`` with a bounded, portable escalation:
|
|
410
|
+
``wait(timeout)`` → ``kill()`` → ``wait(timeout)``. Guarantees the child is
|
|
411
|
+
reaped even when a grandchild holds the stdout pipe, without ever blocking
|
|
412
|
+
indefinitely. Returns the child's exit code (or -1 if it never resolved)."""
|
|
413
|
+
try:
|
|
414
|
+
return proc.wait(timeout=_ABORT_WAIT_TIMEOUT_S)
|
|
415
|
+
except subprocess.TimeoutExpired:
|
|
416
|
+
pass
|
|
417
|
+
if proc.poll() is None:
|
|
418
|
+
proc.kill()
|
|
419
|
+
try:
|
|
420
|
+
return proc.wait(timeout=_ABORT_WAIT_TIMEOUT_S)
|
|
421
|
+
except subprocess.TimeoutExpired:
|
|
422
|
+
rc = proc.poll()
|
|
423
|
+
return rc if rc is not None else -1
|
|
424
|
+
|
|
425
|
+
|
|
426
|
+
def _drain_bounded(proc: subprocess.Popen, log) -> None:
|
|
427
|
+
"""Tee any output Stryker already buffered into ``log`` after an abort, so
|
|
428
|
+
the post-mortem log stays complete — but bounded: the drain runs on a
|
|
429
|
+
daemon thread joined with a timeout, and the caller closes stdout after, so
|
|
430
|
+
a grandchild holding the pipe cannot hang the drain."""
|
|
431
|
+
|
|
432
|
+
def _pump() -> None:
|
|
433
|
+
try:
|
|
434
|
+
for raw in proc.stdout: # type: ignore[union-attr]
|
|
435
|
+
log.write(raw)
|
|
436
|
+
except (AttributeError, OSError, ValueError):
|
|
437
|
+
pass
|
|
438
|
+
|
|
439
|
+
drain = threading.Thread(target=_pump, daemon=True)
|
|
440
|
+
drain.start()
|
|
441
|
+
drain.join(_ABORT_DRAIN_TIMEOUT_S)
|
|
442
|
+
log.flush()
|
|
443
|
+
|
|
444
|
+
|
|
445
|
+
def _run_stryker_streaming(
|
|
446
|
+
stryker_bin: str,
|
|
447
|
+
stryker_args: Sequence[str],
|
|
448
|
+
logfile: Path,
|
|
449
|
+
line_callback: Callable[[str], bool],
|
|
450
|
+
popen_cwd: str | None,
|
|
451
|
+
) -> int:
|
|
452
|
+
"""Streaming variant of :func:`run_stryker` used when a line-callback is
|
|
453
|
+
supplied. Tees every line to ``logfile`` while feeding it to the callback;
|
|
454
|
+
aborts the run (terminating AND reaping the child) when the callback
|
|
455
|
+
returns truthy or raises. Split out so the callback-less path above stays
|
|
456
|
+
byte-for-byte the original behavior.
|
|
457
|
+
|
|
458
|
+
Abort/raise are bounded (#1136 review): after ``terminate()`` we drain the
|
|
459
|
+
buffered tail on a joined daemon thread, close stdout to unblock any reader,
|
|
460
|
+
and reap with a ``wait(timeout)`` → ``kill()`` → ``wait(timeout)``
|
|
461
|
+
escalation. A grandchild test-host that inherits and holds the stdout pipe
|
|
462
|
+
can therefore never hang the drain or the wait, and the child is always
|
|
463
|
+
reaped before we return or re-raise.
|
|
464
|
+
"""
|
|
465
|
+
with logfile.open("wb") as log:
|
|
466
|
+
proc = subprocess.Popen(
|
|
467
|
+
build_stryker_argv(stryker_bin, stryker_args),
|
|
468
|
+
stdout=subprocess.PIPE,
|
|
469
|
+
stderr=subprocess.STDOUT,
|
|
470
|
+
cwd=popen_cwd,
|
|
471
|
+
)
|
|
472
|
+
with _RUNNING_STRYKER_LOCK:
|
|
473
|
+
_RUNNING_STRYKER_PROCS.add(proc)
|
|
474
|
+
try:
|
|
475
|
+
aborted = False
|
|
476
|
+
assert proc.stdout is not None
|
|
477
|
+
try:
|
|
478
|
+
for raw in proc.stdout:
|
|
479
|
+
log.write(raw) # tee: the full log is always preserved
|
|
480
|
+
log.flush()
|
|
481
|
+
if line_callback(raw.decode("utf-8", errors="replace")):
|
|
482
|
+
aborted = True
|
|
483
|
+
break
|
|
484
|
+
except BaseException:
|
|
485
|
+
# Callback raised — terminate, close, and reap (bounded) before
|
|
486
|
+
# propagating so we never orphan the Stryker process or hang on
|
|
487
|
+
# a grandchild holding the pipe (same kill path as the signal
|
|
488
|
+
# handler, plus a guaranteed reap).
|
|
489
|
+
_terminate(proc)
|
|
490
|
+
_close_stdout(proc)
|
|
491
|
+
_bounded_reap(proc)
|
|
492
|
+
raise
|
|
493
|
+
if aborted:
|
|
494
|
+
_terminate(proc)
|
|
495
|
+
# Capture the buffered tail (tee), then close stdout so the
|
|
496
|
+
# bounded reap can never wait on an EOF a grandchild withholds.
|
|
497
|
+
_drain_bounded(proc, log)
|
|
498
|
+
_close_stdout(proc)
|
|
499
|
+
return _bounded_reap(proc)
|
|
500
|
+
return proc.wait()
|
|
501
|
+
finally:
|
|
502
|
+
with _RUNNING_STRYKER_LOCK:
|
|
503
|
+
_RUNNING_STRYKER_PROCS.discard(proc)
|
|
504
|
+
|
|
505
|
+
|
|
506
|
+
# Guards _RUNNING_STRYKER_PROCS, which holds every currently-running Stryker
|
|
507
|
+
# Popen across all worker threads (one per in-flight slice under #561's
|
|
508
|
+
# slice-level parallelism). A plain set is not thread-safe on its own; the
|
|
509
|
+
# lock makes add/discard/snapshot atomic with respect to the signal handler.
|
|
510
|
+
_RUNNING_STRYKER_LOCK = threading.Lock()
|
|
511
|
+
_RUNNING_STRYKER_PROCS: set[subprocess.Popen] = set()
|
|
512
|
+
|
|
513
|
+
|
|
514
|
+
def _terminate_all_tracked_processes() -> None:
|
|
515
|
+
"""Terminate every Stryker subprocess currently tracked in
|
|
516
|
+
``_RUNNING_STRYKER_PROCS`` — there may be several concurrently, one per
|
|
517
|
+
in-flight slice under #561's slice-level parallelism. Split out from
|
|
518
|
+
``_install_signal_handlers`` so it's directly unit-testable without
|
|
519
|
+
needing to actually deliver an OS signal (real signal delivery is
|
|
520
|
+
inherently platform-fragile — see ``TestSignalHandlingPOSIX``).
|
|
521
|
+
"""
|
|
522
|
+
with _RUNNING_STRYKER_LOCK:
|
|
523
|
+
procs = list(_RUNNING_STRYKER_PROCS)
|
|
524
|
+
for proc in procs:
|
|
525
|
+
if proc.poll() is None:
|
|
526
|
+
try:
|
|
527
|
+
proc.terminate()
|
|
528
|
+
except OSError:
|
|
529
|
+
# Best-effort cleanup during shutdown — a process that exited
|
|
530
|
+
# between poll() and terminate() must not block terminating
|
|
531
|
+
# the rest of the tracked processes.
|
|
532
|
+
pass
|
|
533
|
+
|
|
534
|
+
|
|
535
|
+
def _install_signal_handlers() -> list[tuple[int, object]]:
|
|
536
|
+
"""Install SIGINT + SIGTERM handlers that terminate every currently-
|
|
537
|
+
running Stryker subprocess (there may be several concurrently, one per
|
|
538
|
+
in-flight slice under #561's slice-level parallelism) before re-raising
|
|
539
|
+
KeyboardInterrupt / exiting. On Windows, SIGTERM is delivered as
|
|
540
|
+
SIGBREAK — signal.signal accepts SIGTERM but doesn't actually receive it;
|
|
541
|
+
we register SIGBREAK too where available.
|
|
542
|
+
|
|
543
|
+
Returns a list of (signum, previous_handler) tuples so ``main()`` can
|
|
544
|
+
restore the process's prior handlers on exit. This matters when
|
|
545
|
+
``main()`` is called from another Python program (e.g. pytest) that
|
|
546
|
+
itself installs signal handlers — leaving ours in place across the
|
|
547
|
+
caller's remaining lifetime causes KeyboardInterrupt storms on
|
|
548
|
+
unrelated later code.
|
|
549
|
+
"""
|
|
550
|
+
|
|
551
|
+
def _handler(signum: int, _frame) -> None:
|
|
552
|
+
_terminate_all_tracked_processes()
|
|
553
|
+
# Re-raise as KeyboardInterrupt for main()'s finally to fire cleanly.
|
|
554
|
+
raise KeyboardInterrupt(f"signal {signum}")
|
|
555
|
+
|
|
556
|
+
previous: list[tuple[int, object]] = []
|
|
557
|
+
signals_to_install = [signal.SIGINT]
|
|
558
|
+
if hasattr(signal, "SIGTERM"):
|
|
559
|
+
signals_to_install.append(signal.SIGTERM)
|
|
560
|
+
if hasattr(signal, "SIGBREAK"):
|
|
561
|
+
# Windows console Ctrl-Break.
|
|
562
|
+
signals_to_install.append(signal.SIGBREAK)
|
|
563
|
+
for sig in signals_to_install:
|
|
564
|
+
previous.append((sig, signal.getsignal(sig)))
|
|
565
|
+
signal.signal(sig, _handler)
|
|
566
|
+
return previous
|
|
567
|
+
|
|
568
|
+
|
|
569
|
+
def _restore_signal_handlers(previous: list[tuple[int, object]]) -> None:
|
|
570
|
+
"""Restore the signal handlers captured by _install_signal_handlers."""
|
|
571
|
+
for sig, handler in previous:
|
|
572
|
+
try:
|
|
573
|
+
signal.signal(sig, handler)
|
|
574
|
+
except (ValueError, OSError, TypeError):
|
|
575
|
+
# Non-main-thread invocation or platform quirk — leave whatever
|
|
576
|
+
# is now in place; better than crashing at cleanup time.
|
|
577
|
+
pass
|
|
578
|
+
|
|
579
|
+
|
|
580
|
+
def main(argv: Sequence[str] | None = None) -> int:
|
|
581
|
+
"""Main entry point. Returns the process exit code."""
|
|
582
|
+
argv = list(sys.argv[1:] if argv is None else argv)
|
|
583
|
+
args, stryker_args = parse_args(argv)
|
|
584
|
+
|
|
585
|
+
# ---- DOTNET_ROOT resolution ---------------------------------------------
|
|
586
|
+
dotnet_root, err = resolve_dotnet_root(
|
|
587
|
+
preset=os.environ.get("DOTNET_ROOT"),
|
|
588
|
+
candidates=default_probe_candidates(),
|
|
589
|
+
)
|
|
590
|
+
if err is not None:
|
|
591
|
+
sys.stderr.write(err)
|
|
592
|
+
return EXIT_NO_DOTNET_SDK
|
|
593
|
+
assert dotnet_root is not None
|
|
594
|
+
os.environ["DOTNET_ROOT"] = dotnet_root
|
|
595
|
+
|
|
596
|
+
# ---- Stale-hidden refusal (BEFORE any state mutation) -------------------
|
|
597
|
+
sln = Path(args.sln)
|
|
598
|
+
sln_hidden = Path(f"{args.sln}.stryker-hidden")
|
|
599
|
+
stale_err = check_stale_hidden_sln(sln, sln_hidden)
|
|
600
|
+
if stale_err is not None:
|
|
601
|
+
sys.stderr.write(stale_err)
|
|
602
|
+
return EXIT_STALE_HIDDEN_SLN
|
|
603
|
+
|
|
604
|
+
# ---- Install signal handlers now that we're about to spawn children ----
|
|
605
|
+
# Save previous handlers so we restore them on exit. Critical when main()
|
|
606
|
+
# is called from another Python program (pytest) — leaving our handlers
|
|
607
|
+
# in place across the caller's lifetime causes KeyboardInterrupt storms
|
|
608
|
+
# on unrelated later code.
|
|
609
|
+
previous_signal_handlers = _install_signal_handlers()
|
|
610
|
+
|
|
611
|
+
exit_code: int = EXIT_OK
|
|
612
|
+
try:
|
|
613
|
+
# Pre-build BEFORE hiding — build against a hidden .sln fails.
|
|
614
|
+
rc = build_project(args.sln)
|
|
615
|
+
if rc != 0:
|
|
616
|
+
return rc
|
|
617
|
+
if args.shim_project:
|
|
618
|
+
rc = build_project(args.shim_project)
|
|
619
|
+
if rc != 0:
|
|
620
|
+
return rc
|
|
621
|
+
|
|
622
|
+
# Hide .sln during the run.
|
|
623
|
+
hide_sln(sln, sln_hidden)
|
|
624
|
+
|
|
625
|
+
# Inject Stryker's own mutant-testing concurrency (`-c`) unless the
|
|
626
|
+
# caller's pass-through args already specify one — pass-through
|
|
627
|
+
# always wins over both an explicit --stryker-concurrency/env value
|
|
628
|
+
# and the computed default; never silently override an explicit
|
|
629
|
+
# caller-supplied value.
|
|
630
|
+
pass_through_flag = _pass_through_concurrency_flag(stryker_args)
|
|
631
|
+
if pass_through_flag is None:
|
|
632
|
+
if args.stryker_concurrency is not None:
|
|
633
|
+
value = str(args.stryker_concurrency)
|
|
634
|
+
else:
|
|
635
|
+
value = str(default_concurrency(os.cpu_count()))
|
|
636
|
+
stryker_args = [*stryker_args, "-c", value]
|
|
637
|
+
elif args.stryker_concurrency is not None:
|
|
638
|
+
idx = stryker_args.index(pass_through_flag)
|
|
639
|
+
pass_through_value = (
|
|
640
|
+
stryker_args[idx + 1] if idx + 1 < len(stryker_args) else ""
|
|
641
|
+
)
|
|
642
|
+
sys.stderr.write(
|
|
643
|
+
f'note: pass-through "{pass_through_flag} '
|
|
644
|
+
f'{pass_through_value}" overrode explicit '
|
|
645
|
+
f'"--stryker-concurrency {args.stryker_concurrency}"\n'
|
|
646
|
+
)
|
|
647
|
+
|
|
648
|
+
# Run Stryker; capture its exit code.
|
|
649
|
+
exit_code = run_stryker(
|
|
650
|
+
stryker_bin=args.stryker_bin,
|
|
651
|
+
stryker_args=stryker_args,
|
|
652
|
+
logfile=Path(args.logfile),
|
|
653
|
+
)
|
|
654
|
+
except KeyboardInterrupt:
|
|
655
|
+
# SIGINT / SIGTERM propagated through the signal handlers. The Popen
|
|
656
|
+
# child was terminate()'d already; we now fall through to the finally
|
|
657
|
+
# block for .sln restoration.
|
|
658
|
+
exit_code = 130 # conventional SIGINT exit code
|
|
659
|
+
finally:
|
|
660
|
+
# ALWAYS restore .sln — no matter which exit path.
|
|
661
|
+
restored = restore_sln(sln, sln_hidden)
|
|
662
|
+
if not restored:
|
|
663
|
+
sys.stderr.write(
|
|
664
|
+
f"error: failed to restore {sln} from {sln_hidden} — the "
|
|
665
|
+
"solution file is left hidden/renamed, which will break the "
|
|
666
|
+
"next build (#1955)\n"
|
|
667
|
+
)
|
|
668
|
+
# Don't clobber a real Stryker failure — a restore failure on
|
|
669
|
+
# top of an already-non-zero exit adds no new information the
|
|
670
|
+
# operator needs; surface it only when Stryker itself looked
|
|
671
|
+
# clean.
|
|
672
|
+
if exit_code == EXIT_OK:
|
|
673
|
+
exit_code = EXIT_RESTORE_SLN_FAILED
|
|
674
|
+
# Restore previous signal handlers — critical for library-style use.
|
|
675
|
+
_restore_signal_handlers(previous_signal_handlers)
|
|
676
|
+
|
|
677
|
+
return exit_code
|
|
678
|
+
|
|
679
|
+
|
|
680
|
+
if __name__ == "__main__":
|
|
681
|
+
sys.exit(main())
|