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.
Files changed (780) hide show
  1. package/LICENSE +21 -0
  2. package/PORTING.md +134 -0
  3. package/README.md +207 -0
  4. package/UPSTREAM.json +64 -0
  5. package/agents/Explore.md +15 -0
  6. package/agents/a11y-review.md +118 -0
  7. package/agents/adr-author.md +70 -0
  8. package/agents/ai-provenance-review.md +120 -0
  9. package/agents/angular-reactivity-review.md +95 -0
  10. package/agents/arch-review.md +135 -0
  11. package/agents/architect.md +78 -0
  12. package/agents/autoship-batch-proposer.md +69 -0
  13. package/agents/claude-setup-review.md +136 -0
  14. package/agents/codebase-recon.md +184 -0
  15. package/agents/component-architecture-review.md +119 -0
  16. package/agents/concurrency-review.md +109 -0
  17. package/agents/correctness-review.md +290 -0
  18. package/agents/data-flow-tracer.md +120 -0
  19. package/agents/doc-review.md +165 -0
  20. package/agents/domain-review.md +136 -0
  21. package/agents/general-purpose.md +10 -0
  22. package/agents/gherkin-quality-critic.md +113 -0
  23. package/agents/js-fp-review.md +114 -0
  24. package/agents/mutation-kill.md +684 -0
  25. package/agents/naming-review.md +142 -0
  26. package/agents/orchestrator.md +339 -0
  27. package/agents/performance-review.md +105 -0
  28. package/agents/plan-review-acceptance.md +115 -0
  29. package/agents/plan-review-design.md +90 -0
  30. package/agents/plan-review-parallelization.md +84 -0
  31. package/agents/plan-review-strategic.md +96 -0
  32. package/agents/plan-review-ux.md +110 -0
  33. package/agents/platform-engineer.md +64 -0
  34. package/agents/product-manager.md +68 -0
  35. package/agents/progress-guardian.md +79 -0
  36. package/agents/qa-engineer.md +289 -0
  37. package/agents/quality-reviewer.md +132 -0
  38. package/agents/react-reactivity-review.md +102 -0
  39. package/agents/refactor-opportunity-review.md +128 -0
  40. package/agents/security-engineer.md +60 -0
  41. package/agents/security-review.md +218 -0
  42. package/agents/session-analysis.md +95 -0
  43. package/agents/software-engineer.md +105 -0
  44. package/agents/spec-compliance-review.md +100 -0
  45. package/agents/spec-reviewer.md +114 -0
  46. package/agents/structure-review.md +146 -0
  47. package/agents/tech-writer.md +84 -0
  48. package/agents/test-review.md +246 -0
  49. package/agents/test-smell-review.md +188 -0
  50. package/agents/token-efficiency-review.md +139 -0
  51. package/agents/ui-ux-designer.md +54 -0
  52. package/agents/vue-reactivity-review.md +95 -0
  53. package/bin/__pycache__/claudecpython-314.pyc +0 -0
  54. package/bin/claude +258 -0
  55. package/docs/upstream/.pages +1 -0
  56. package/docs/upstream/CHANGELOG.md +2586 -0
  57. package/docs/upstream/README.md +155 -0
  58. package/docs/upstream/agent-architecture.md +214 -0
  59. package/docs/upstream/agent_info.md +187 -0
  60. package/docs/upstream/artifact-migration.md +124 -0
  61. package/docs/upstream/code-intelligence-nudge.md +149 -0
  62. package/docs/upstream/code-review-process.md +294 -0
  63. package/docs/upstream/concurrent-use.md +73 -0
  64. package/docs/upstream/context-management.md +111 -0
  65. package/docs/upstream/developer-notes.md +280 -0
  66. package/docs/upstream/diagrams/architecture-overview.svg +101 -0
  67. package/docs/upstream/diagrams/review-dispatch.svg +139 -0
  68. package/docs/upstream/diagrams/team-agents.svg +128 -0
  69. package/docs/upstream/diagrams/test-improve-flow.svg +166 -0
  70. package/docs/upstream/diagrams/workflow-linear.svg +66 -0
  71. package/docs/upstream/diagrams/workflow-three-phase.svg +200 -0
  72. package/docs/upstream/eval-maintenance.md +95 -0
  73. package/docs/upstream/eval-running-guide.md +147 -0
  74. package/docs/upstream/eval-system.md +291 -0
  75. package/docs/upstream/session-review-oss-complements.md +75 -0
  76. package/docs/upstream/session-review.md +212 -0
  77. package/docs/upstream/skills.md +188 -0
  78. package/docs/upstream/team-structure.md +21 -0
  79. package/docs/upstream/telemetry-ci-access.md +129 -0
  80. package/docs/upstream/telemetry-repo-security.md +120 -0
  81. package/docs/upstream/test-evaluation.md +277 -0
  82. package/docs/upstream/test-improve.md +154 -0
  83. package/docs/upstream/triage-workflow.md +282 -0
  84. package/docs/upstream/workflows.md +289 -0
  85. package/extensions/dev-team/index.ts +539 -0
  86. package/extensions/dev-team/lib/agents.ts +272 -0
  87. package/extensions/dev-team/lib/ai-credits.ts +92 -0
  88. package/extensions/dev-team/lib/autocompact.ts +81 -0
  89. package/extensions/dev-team/lib/child-run.ts +102 -0
  90. package/extensions/dev-team/lib/config.ts +236 -0
  91. package/extensions/dev-team/lib/gh-command.ts +103 -0
  92. package/extensions/dev-team/lib/github-style.ts +307 -0
  93. package/extensions/dev-team/lib/hooks.ts +350 -0
  94. package/extensions/dev-team/lib/metrics.ts +115 -0
  95. package/extensions/dev-team/lib/safe-read.ts +49 -0
  96. package/extensions/dev-team/lib/session-files.ts +57 -0
  97. package/extensions/dev-team/lib/session-spend.ts +123 -0
  98. package/extensions/dev-team/lib/shell-scan.ts +205 -0
  99. package/extensions/dev-team/lib/skills.ts +213 -0
  100. package/extensions/dev-team/lib/subagent-render.ts +245 -0
  101. package/extensions/dev-team/lib/subagent-types.ts +164 -0
  102. package/extensions/dev-team/lib/subagent.ts +596 -0
  103. package/extensions/dev-team/lib/terminal-text.ts +54 -0
  104. package/extensions/dev-team/lib/tools-misc.ts +152 -0
  105. package/extensions/dev-team/lib/transcript.ts +110 -0
  106. package/extensions/dev-team/lib/trust.ts +52 -0
  107. package/extensions/dev-team/lib/usage-breakdown.ts +176 -0
  108. package/extensions/dev-team/lib/usage-chart.ts +153 -0
  109. package/extensions/dev-team/lib/usage-command.ts +107 -0
  110. package/extensions/dev-team/lib/usage-history.ts +203 -0
  111. package/extensions/dev-team/lib/usage-render.ts +225 -0
  112. package/extensions/dev-team/lib/usage-split-bar.ts +127 -0
  113. package/extensions/dev-team/lib/usage-state.ts +116 -0
  114. package/extensions/dev-team/lib/usage-text.ts +159 -0
  115. package/extensions/dev-team/lib/usage-view.ts +109 -0
  116. package/hooks/__pycache__/refactor_test_freeze_guard.cpython-314.pyc +0 -0
  117. package/hooks/agent_dispatch_ledger.py +190 -0
  118. package/hooks/autocompact_setup_nudge.py +99 -0
  119. package/hooks/bash_retry_guard.py +228 -0
  120. package/hooks/boundary_events_write_guard.py +352 -0
  121. package/hooks/code_intelligence_nudge.py +293 -0
  122. package/hooks/code_intelligence_turn_mark.py +317 -0
  123. package/hooks/codegraph_bootstrap.py +139 -0
  124. package/hooks/contract_version_guard.py +362 -0
  125. package/hooks/cost_meter.py +106 -0
  126. package/hooks/destructive-commands.json +62 -0
  127. package/hooks/destructive_guard.py +477 -0
  128. package/hooks/eval_compliance_check.py +440 -0
  129. package/hooks/guards.json +17 -0
  130. package/hooks/hooks.json +323 -0
  131. package/hooks/internal_double_gate.py +296 -0
  132. package/hooks/js_fp_review.py +212 -0
  133. package/hooks/knowledge_index.py +119 -0
  134. package/hooks/lib/__pycache__/artifact_paths.cpython-314.pyc +0 -0
  135. package/hooks/lib/__pycache__/atomic_state.cpython-314.pyc +0 -0
  136. package/hooks/lib/__pycache__/autocompact_config.cpython-314.pyc +0 -0
  137. package/hooks/lib/__pycache__/boundary_events.cpython-314.pyc +0 -0
  138. package/hooks/lib/__pycache__/doc_classification.cpython-314.pyc +0 -0
  139. package/hooks/lib/__pycache__/gh_pr_create_detect.cpython-314.pyc +0 -0
  140. package/hooks/lib/__pycache__/git_safe_diff.cpython-314.pyc +0 -0
  141. package/hooks/lib/__pycache__/instrument_log.cpython-314.pyc +0 -0
  142. package/hooks/lib/__pycache__/metrics_query.cpython-314.pyc +0 -0
  143. package/hooks/lib/__pycache__/plugin_version.cpython-314.pyc +0 -0
  144. package/hooks/lib/__pycache__/pre_commit_doc_classifier.cpython-314.pyc +0 -0
  145. package/hooks/lib/__pycache__/review_agent_registry.cpython-314.pyc +0 -0
  146. package/hooks/lib/__pycache__/review_gate_corroboration.cpython-314.pyc +0 -0
  147. package/hooks/lib/__pycache__/review_gate_hash.cpython-314.pyc +0 -0
  148. package/hooks/lib/__pycache__/review_verdicts.cpython-314.pyc +0 -0
  149. package/hooks/lib/__pycache__/stdin_json.cpython-314.pyc +0 -0
  150. package/hooks/lib/__pycache__/stryker_invocation.cpython-314.pyc +0 -0
  151. package/hooks/lib/__pycache__/telemetry_consent.cpython-314.pyc +0 -0
  152. package/hooks/lib/__pycache__/test_file_classify.cpython-314.pyc +0 -0
  153. package/hooks/lib/__pycache__/token_efficiency_limits.cpython-314.pyc +0 -0
  154. package/hooks/lib/__pycache__/verify_guard_state.cpython-314.pyc +0 -0
  155. package/hooks/lib/__pycache__/xunit_v3_operator_gate.cpython-314.pyc +0 -0
  156. package/hooks/lib/agent_skill_hints.py +74 -0
  157. package/hooks/lib/artifact_paths.py +263 -0
  158. package/hooks/lib/atomic_state.py +557 -0
  159. package/hooks/lib/autocompact_config.py +103 -0
  160. package/hooks/lib/autoship_log.py +106 -0
  161. package/hooks/lib/banned_scripts_policy.py +51 -0
  162. package/hooks/lib/boundary_events.py +436 -0
  163. package/hooks/lib/build_knowledge_index.py +504 -0
  164. package/hooks/lib/build_skills_index.py +361 -0
  165. package/hooks/lib/build_state.py +116 -0
  166. package/hooks/lib/classify_ship_outcome.py +126 -0
  167. package/hooks/lib/config_changelog_schema.py +115 -0
  168. package/hooks/lib/cost_meter.py +955 -0
  169. package/hooks/lib/doc_classification.py +116 -0
  170. package/hooks/lib/gh_pr_create_detect.py +136 -0
  171. package/hooks/lib/git_safe_diff.py +123 -0
  172. package/hooks/lib/instrument_log.py +66 -0
  173. package/hooks/lib/iteration_journal_gate.py +197 -0
  174. package/hooks/lib/knowledge_index_paths.py +88 -0
  175. package/hooks/lib/mcp_json_repowise.py +177 -0
  176. package/hooks/lib/metrics_query.py +202 -0
  177. package/hooks/lib/minimal_yaml.py +434 -0
  178. package/hooks/lib/plugin_version.py +142 -0
  179. package/hooks/lib/pre_commit_detect.py +537 -0
  180. package/hooks/lib/pre_commit_doc_classifier.py +126 -0
  181. package/hooks/lib/pricing.py +118 -0
  182. package/hooks/lib/report_pdf.py +371 -0
  183. package/hooks/lib/review_agent_registry.py +142 -0
  184. package/hooks/lib/review_dispatch_ledger.py +101 -0
  185. package/hooks/lib/review_gate_corroboration.py +521 -0
  186. package/hooks/lib/review_gate_hash.py +252 -0
  187. package/hooks/lib/review_gate_normalized_hash.py +1115 -0
  188. package/hooks/lib/review_verdicts.py +301 -0
  189. package/hooks/lib/run_report.py +160 -0
  190. package/hooks/lib/skill_categories.yaml +125 -0
  191. package/hooks/lib/stdin_json.py +57 -0
  192. package/hooks/lib/stryker_invocation.py +102 -0
  193. package/hooks/lib/telemetry_consent.py +41 -0
  194. package/hooks/lib/telemetry_report.py +108 -0
  195. package/hooks/lib/test_file_classify.py +160 -0
  196. package/hooks/lib/token_efficiency_limits.py +51 -0
  197. package/hooks/lib/turn_identity.py +77 -0
  198. package/hooks/lib/verify_guard_state.py +110 -0
  199. package/hooks/lib/workflow_state.py +206 -0
  200. package/hooks/lib/xunit_v3_operator_gate.py +596 -0
  201. package/hooks/mcp_json_repowise_nudge.py +74 -0
  202. package/hooks/mutation_adapters/__init__.py +7 -0
  203. package/hooks/mutation_adapters/__pycache__/__init__.cpython-314.pyc +0 -0
  204. package/hooks/mutation_adapters/__pycache__/lib.cpython-314.pyc +0 -0
  205. package/hooks/mutation_adapters/__pycache__/mutmut.cpython-314.pyc +0 -0
  206. package/hooks/mutation_adapters/__pycache__/pitest.cpython-314.pyc +0 -0
  207. package/hooks/mutation_adapters/__pycache__/stryker.cpython-314.pyc +0 -0
  208. package/hooks/mutation_adapters/__pycache__/stryker_net.cpython-314.pyc +0 -0
  209. package/hooks/mutation_adapters/lib.py +478 -0
  210. package/hooks/mutation_adapters/mutmut.py +188 -0
  211. package/hooks/mutation_adapters/pitest.py +266 -0
  212. package/hooks/mutation_adapters/stryker.py +157 -0
  213. package/hooks/mutation_adapters/stryker_net.py +264 -0
  214. package/hooks/mutation_gate.py +193 -0
  215. package/hooks/mutation_testing_smoke_gate.py +371 -0
  216. package/hooks/pending_review_notify.py +121 -0
  217. package/hooks/phase_marker.py +138 -0
  218. package/hooks/post_compact_state_reinject.py +180 -0
  219. package/hooks/post_format.py +115 -0
  220. package/hooks/pre_commit_knowledge_index.py +128 -0
  221. package/hooks/pre_commit_review.py +66 -0
  222. package/hooks/pre_pr_review.py +694 -0
  223. package/hooks/pre_tool_guard.py +405 -0
  224. package/hooks/py.sh +73 -0
  225. package/hooks/refactor-bash-write-patterns.json +29 -0
  226. package/hooks/refactor_test_bash_guard.py +253 -0
  227. package/hooks/refactor_test_freeze_guard.py +139 -0
  228. package/hooks/refactor_test_revert_guard.py +186 -0
  229. package/hooks/repo_review_nudge.py +287 -0
  230. package/hooks/review_verdict_recorder.py +464 -0
  231. package/hooks/scan_bash_command_for_banned_scripts.py +428 -0
  232. package/hooks/scan_worktree_for_banned_scripts.py +238 -0
  233. package/hooks/session_learning_trigger.py +248 -0
  234. package/hooks/skills_index.py +126 -0
  235. package/hooks/stryker_xunit_shim_guard.py +571 -0
  236. package/hooks/subagent_completion_guard.py +309 -0
  237. package/hooks/subagent_skill_context.py +139 -0
  238. package/hooks/task_completion_metrics.py +216 -0
  239. package/hooks/tdd_guard.py +229 -0
  240. package/hooks/telemetry.py +341 -0
  241. package/hooks/token_efficiency_review.py +194 -0
  242. package/hooks/verify_guard.py +183 -0
  243. package/hooks/verify_guard_edit_marker.py +73 -0
  244. package/hooks/version_check.py +173 -0
  245. package/knowledge/accepted-risks-schema.md +98 -0
  246. package/knowledge/adr-decision-criteria.md +64 -0
  247. package/knowledge/adversarial-review-protocol.md +139 -0
  248. package/knowledge/agent-registry.md +228 -0
  249. package/knowledge/agent-review-methodology.md +80 -0
  250. package/knowledge/ai-friendly-repo-guidelines.md +67 -0
  251. package/knowledge/architecture-assessment.md +96 -0
  252. package/knowledge/artifact-lifecycle.md +57 -0
  253. package/knowledge/cd-maturity-model.md +82 -0
  254. package/knowledge/cd-test-architecture.md +190 -0
  255. package/knowledge/ci-cd-file-scope.md +24 -0
  256. package/knowledge/codegraph-vs-graphify.md +192 -0
  257. package/knowledge/component-test-patterns.md +139 -0
  258. package/knowledge/database-change-management.md +80 -0
  259. package/knowledge/database-test-patterns.md +79 -0
  260. package/knowledge/decision-defaults.md +88 -0
  261. package/knowledge/dependency-breaking-techniques.md +116 -0
  262. package/knowledge/deployment-pipeline.md +86 -0
  263. package/knowledge/design-smells.md +122 -0
  264. package/knowledge/directory-enumeration.md +38 -0
  265. package/knowledge/domain-modeling.md +123 -0
  266. package/knowledge/evidence-bundle.md +90 -0
  267. package/knowledge/exploratory-testing-field-guide.md +122 -0
  268. package/knowledge/failure-routing.md +28 -0
  269. package/knowledge/fixture-construction.md +56 -0
  270. package/knowledge/frontend-component-architecture.md +139 -0
  271. package/knowledge/gherkin-quality-review-dispatch.md +135 -0
  272. package/knowledge/index.json +6766 -0
  273. package/knowledge/internal-collaborator-doubling.md +101 -0
  274. package/knowledge/legacy-test-strategy.md +71 -0
  275. package/knowledge/long-run-waiting.md +66 -0
  276. package/knowledge/microservice-testing.md +71 -0
  277. package/knowledge/model-pricing.json +23 -0
  278. package/knowledge/mutation-score-formulas.md +60 -0
  279. package/knowledge/object-calisthenics.md +147 -0
  280. package/knowledge/oracle-provenance.md +94 -0
  281. package/knowledge/orchestrator-script-implementation.md +185 -0
  282. package/knowledge/owasp-detection.md +148 -0
  283. package/knowledge/plan-review-rubric.md +56 -0
  284. package/knowledge/proxy-connectivity.md +62 -0
  285. package/knowledge/reactive-effect-patterns.md +73 -0
  286. package/knowledge/recon-inventory-excludes.txt +32 -0
  287. package/knowledge/references/bdd-value-guide.md +61 -0
  288. package/knowledge/references/csharp-http-client-testing.md +264 -0
  289. package/knowledge/release-strategies.md +74 -0
  290. package/knowledge/report-output-location.md +117 -0
  291. package/knowledge/report-pdf-integration.md +63 -0
  292. package/knowledge/report-print.css +129 -0
  293. package/knowledge/report-template.md +114 -0
  294. package/knowledge/report-to-pdf.md +69 -0
  295. package/knowledge/request-processing-flow.md +63 -0
  296. package/knowledge/result-verification.md +52 -0
  297. package/knowledge/review-agent-output-contract.md +121 -0
  298. package/knowledge/review-lens-classification.md +113 -0
  299. package/knowledge/review-rubric.md +62 -0
  300. package/knowledge/review-template.md +104 -0
  301. package/knowledge/rule-fixtures/A02.insecure-random-js/negative.js +1 -0
  302. package/knowledge/rule-fixtures/A02.insecure-random-js/positive.js +1 -0
  303. package/knowledge/rule-fixtures/A02.weak-hashing-md5/negative.py +1 -0
  304. package/knowledge/rule-fixtures/A02.weak-hashing-md5/positive.py +1 -0
  305. package/knowledge/rule-fixtures/A03.command-injection/negative.js +1 -0
  306. package/knowledge/rule-fixtures/A03.command-injection/positive.js +1 -0
  307. package/knowledge/rule-fixtures/A03.sql-injection/negative.js +1 -0
  308. package/knowledge/rule-fixtures/A03.sql-injection/positive.js +1 -0
  309. package/knowledge/rule-fixtures/A03.xss-innerhtml/negative.js +1 -0
  310. package/knowledge/rule-fixtures/A03.xss-innerhtml/positive.js +1 -0
  311. package/knowledge/rule-fixtures/A05.cors-wildcard/negative.js +1 -0
  312. package/knowledge/rule-fixtures/A05.cors-wildcard/positive.js +1 -0
  313. package/knowledge/rule-fixtures/A05.default-credentials/negative.js +1 -0
  314. package/knowledge/rule-fixtures/A05.default-credentials/positive.js +1 -0
  315. package/knowledge/rule-fixtures/A07.jwt-alg-none/negative.js +1 -0
  316. package/knowledge/rule-fixtures/A07.jwt-alg-none/positive.js +1 -0
  317. package/knowledge/rule-fixtures/A08.binary-formatter/negative.cs +1 -0
  318. package/knowledge/rule-fixtures/A08.binary-formatter/positive.cs +1 -0
  319. package/knowledge/rule-fixtures/A08.js-eval/negative.js +1 -0
  320. package/knowledge/rule-fixtures/A08.js-eval/positive.js +1 -0
  321. package/knowledge/rule-fixtures/A08.object-input-stream/negative.java +1 -0
  322. package/knowledge/rule-fixtures/A08.object-input-stream/positive.java +1 -0
  323. package/knowledge/schemas/disposition-register-v1.json +65 -0
  324. package/knowledge/schemas/recon-envelope-v1.json +198 -0
  325. package/knowledge/schemas/unified-finding-v1.json +72 -0
  326. package/knowledge/security-primitives-contract.md +301 -0
  327. package/knowledge/security-review-rule-map.yaml +107 -0
  328. package/knowledge/skills-registry.md +72 -0
  329. package/knowledge/task-size-classifier.md +103 -0
  330. package/knowledge/telemetry-schema.md +881 -0
  331. package/knowledge/test-automation-maturity.md +56 -0
  332. package/knowledge/test-automation-principles.md +71 -0
  333. package/knowledge/test-cadence-tradeoffs.md +68 -0
  334. package/knowledge/test-doubles.md +105 -0
  335. package/knowledge/test-file-indicators.md +22 -0
  336. package/knowledge/test-layer-gates.md +35 -0
  337. package/knowledge/test-matrix-examples/django-batch.md +24 -0
  338. package/knowledge/test-matrix-examples/dotnet-grpc-fronting-api.md +90 -0
  339. package/knowledge/test-matrix-examples/dotnet-http-consumer.md +131 -0
  340. package/knowledge/test-matrix-examples/react-node-spa.md +24 -0
  341. package/knowledge/test-matrix-examples/spring-boot-service.md +25 -0
  342. package/knowledge/test-matrix-examples/ssr-htmx.md +24 -0
  343. package/knowledge/test-organization.md +70 -0
  344. package/knowledge/test-pyramid.md +84 -0
  345. package/knowledge/test-refactoring.md +67 -0
  346. package/knowledge/test-review-division-of-labor.md +85 -0
  347. package/knowledge/test-smells.md +80 -0
  348. package/knowledge/test-stack-profiles/bdd-frameworks.md +235 -0
  349. package/knowledge/test-stack-profiles/django.md +13 -0
  350. package/knowledge/test-stack-profiles/dotnet.md +18 -0
  351. package/knowledge/test-stack-profiles/go.md +16 -0
  352. package/knowledge/test-stack-profiles/node.md +16 -0
  353. package/knowledge/test-stack-profiles/react.md +12 -0
  354. package/knowledge/test-stack-profiles/spring-boot.md +16 -0
  355. package/knowledge/test-stack-profiles/ssr-htmx.md +14 -0
  356. package/knowledge/test-stack-profiles/vue.md +12 -0
  357. package/knowledge/test-strategy.md +70 -0
  358. package/knowledge/testability-patterns.md +240 -0
  359. package/knowledge/testing-quadrants.md +44 -0
  360. package/knowledge/testing-techniques/approval.md +15 -0
  361. package/knowledge/testing-techniques/chaos.md +17 -0
  362. package/knowledge/testing-techniques/fuzz.md +15 -0
  363. package/knowledge/testing-techniques/property-based.md +15 -0
  364. package/knowledge/testing-techniques/schema-validation.md +15 -0
  365. package/knowledge/testing-techniques/screenshot.md +15 -0
  366. package/knowledge/three-phase-workflow.md +198 -0
  367. package/knowledge/value-patterns.md +55 -0
  368. package/knowledge/verification-mode.md +116 -0
  369. package/knowledge/virtual-service-libraries.md +75 -0
  370. package/knowledge/wave-consolidation-guidance.md +21 -0
  371. package/overrides/agents/Explore.md +15 -0
  372. package/overrides/agents/general-purpose.md +10 -0
  373. package/overrides/notes/autoship.md +6 -0
  374. package/overrides/notes/issues-from-assessment.md +3 -0
  375. package/overrides/notes/issues-from-plan.md +3 -0
  376. package/overrides/notes/mutation-night-watch.md +3 -0
  377. package/overrides/notes/mutation-testing.md +3 -0
  378. package/overrides/notes/pr.md +7 -0
  379. package/overrides/notes/project-init.md +6 -0
  380. package/overrides/notes/setup.md +13 -0
  381. package/overrides/notes/specs.md +3 -0
  382. package/overrides/skills/headless-run/SKILL.md +45 -0
  383. package/overrides/skills/upgrade/SKILL.md +30 -0
  384. package/overrides/skills/version/SKILL.md +25 -0
  385. package/package.json +36 -0
  386. package/scripts/authoring_digest.py +93 -0
  387. package/scripts/autoship_discover.py +121 -0
  388. package/scripts/autoship_group.py +409 -0
  389. package/scripts/autoship_proposals.py +494 -0
  390. package/scripts/autoship_queue.py +291 -0
  391. package/scripts/autoship_reclaim.py +495 -0
  392. package/scripts/build_jobs.py +108 -0
  393. package/scripts/build_rollback_point.py +240 -0
  394. package/scripts/build_slice_scope.py +157 -0
  395. package/scripts/build_wave.py +109 -0
  396. package/scripts/build_wave_reconcile.py +252 -0
  397. package/scripts/build_worktree_baseref.py +113 -0
  398. package/scripts/check_agent_scope.py +117 -0
  399. package/scripts/check_agent_tool_mapping.py +213 -0
  400. package/scripts/check_review_agent_mcp_tools.py +317 -0
  401. package/scripts/check_security_assessment_mcp_tools.py +165 -0
  402. package/scripts/checkpoint_abort.py +502 -0
  403. package/scripts/claude_setup_review.py +438 -0
  404. package/scripts/codebase_recon.py +556 -0
  405. package/scripts/coverage_config.py +623 -0
  406. package/scripts/coverage_delta_steering.py +330 -0
  407. package/scripts/coverage_discovery_dotnet.py +315 -0
  408. package/scripts/coverage_discovery_java.py +742 -0
  409. package/scripts/coverage_discovery_js.py +546 -0
  410. package/scripts/coverage_gap_ranking.py +556 -0
  411. package/scripts/coverage_readiness.py +455 -0
  412. package/scripts/coverage_report_parse.py +521 -0
  413. package/scripts/detect_bdd_convention.py +252 -0
  414. package/scripts/eval_ablation.py +376 -0
  415. package/scripts/gherkin_analysis_coverage_gate.py +306 -0
  416. package/scripts/gherkin_cross_feature_duplicate_titles_gate.py +173 -0
  417. package/scripts/gherkin_effectiveness_rollup.py +238 -0
  418. package/scripts/gherkin_failure_path_gate.py +206 -0
  419. package/scripts/gherkin_feature_merge.py +720 -0
  420. package/scripts/gherkin_stub_gate.py +163 -0
  421. package/scripts/gherkin_stub_merge.py +479 -0
  422. package/scripts/git_origin_host.py +88 -0
  423. package/scripts/install-java-static-analysis.py +110 -0
  424. package/scripts/issue_deps.py +74 -0
  425. package/scripts/lib/_bdd_markers.py +28 -0
  426. package/scripts/lib/_gherkin_text.py +93 -0
  427. package/scripts/lib/_vendored_tree.py +70 -0
  428. package/scripts/lib/autoship_state.py +397 -0
  429. package/scripts/lib/claude_md_guard.py +226 -0
  430. package/scripts/lib/deterministic_recon.py +446 -0
  431. package/scripts/lib/mcp_tool_grants.py +211 -0
  432. package/scripts/lib/plan_parse.py +386 -0
  433. package/scripts/lib/review_result.py +84 -0
  434. package/scripts/lib/review_roster.py +86 -0
  435. package/scripts/lib/session_log/__init__.py +34 -0
  436. package/scripts/lib/session_log/__pycache__/__init__.cpython-314.pyc +0 -0
  437. package/scripts/lib/session_log/__pycache__/records.cpython-314.pyc +0 -0
  438. package/scripts/lib/session_log/classify.py +231 -0
  439. package/scripts/lib/session_log/corrections.py +194 -0
  440. package/scripts/lib/session_log/discovery.py +108 -0
  441. package/scripts/lib/session_log/records.py +218 -0
  442. package/scripts/lib/session_log/redact.py +76 -0
  443. package/scripts/lib/session_log/signals.py +373 -0
  444. package/scripts/lib/session_report_downstream.py +614 -0
  445. package/scripts/lib/session_report_maintainer.py +1273 -0
  446. package/scripts/lib/session_report_shared.py +262 -0
  447. package/scripts/lib/settings_hook_guard.py +157 -0
  448. package/scripts/lib/slug.py +33 -0
  449. package/scripts/lib/stub_extractors/__init__.py +82 -0
  450. package/scripts/lib/stub_extractors/_common.py +328 -0
  451. package/scripts/lib/stub_extractors/csharp.py +19 -0
  452. package/scripts/lib/stub_extractors/go.py +173 -0
  453. package/scripts/lib/stub_extractors/java.py +18 -0
  454. package/scripts/lib/stub_extractors/jsts.py +126 -0
  455. package/scripts/mutation_stack_sections.py +149 -0
  456. package/scripts/mutation_yield_steering.py +345 -0
  457. package/scripts/orchestrator.py +895 -0
  458. package/scripts/plan_gherkin_export.py +227 -0
  459. package/scripts/plan_waves.py +208 -0
  460. package/scripts/pr_close_keyword_lint.py +108 -0
  461. package/scripts/progress_guardian.py +888 -0
  462. package/scripts/recon_inventory.py +273 -0
  463. package/scripts/review_findings_log.py +93 -0
  464. package/scripts/run_invariants.py +124 -0
  465. package/scripts/select_lenses.py +640 -0
  466. package/scripts/session_report.py +486 -0
  467. package/scripts/set_autocompact_env.py +221 -0
  468. package/scripts/ship_resume_guard.py +135 -0
  469. package/scripts/ship_review_gate.py +63 -0
  470. package/scripts/specs_convention_marker.py +103 -0
  471. package/scripts/test_improve_resume.py +277 -0
  472. package/scripts/test_review_mechanics.py +958 -0
  473. package/scripts/token_efficiency_review.py +322 -0
  474. package/scripts/verdict_scope.py +285 -0
  475. package/scripts/verify_gherkin_quality_critic_isolation.py +296 -0
  476. package/scripts/verify_tier.py +157 -0
  477. package/skills/adr-tools/SKILL.md +118 -0
  478. package/skills/agent-readiness/SKILL.md +105 -0
  479. package/skills/agent-readiness/ai_friendly_analyzers.py +326 -0
  480. package/skills/agent-readiness/scanner.py +441 -0
  481. package/skills/agent-readiness/scorecard.yaml +88 -0
  482. package/skills/api-design/SKILL.md +115 -0
  483. package/skills/apply-fixes/SKILL.md +171 -0
  484. package/skills/apply-test-doubles/SKILL.md +321 -0
  485. package/skills/artifact-lifecycle/SKILL.md +127 -0
  486. package/skills/autoship/SKILL.md +1124 -0
  487. package/skills/benchmark/SKILL.md +105 -0
  488. package/skills/branch-workflow/SKILL.md +89 -0
  489. package/skills/browse/SKILL.md +184 -0
  490. package/skills/browser-testing/SKILL.md +62 -0
  491. package/skills/browser-testing/references/playwright-patterns.md +216 -0
  492. package/skills/build/SKILL.md +422 -0
  493. package/skills/build/references/static-self-heal.md +245 -0
  494. package/skills/careful/SKILL.md +72 -0
  495. package/skills/cd-test-architecture/SKILL.md +371 -0
  496. package/skills/ci-debugging/SKILL.md +105 -0
  497. package/skills/co-evolution-audit/SKILL.md +269 -0
  498. package/skills/code-review/SKILL.md +1015 -0
  499. package/skills/code-review/examples/aggregated-sample.json +56 -0
  500. package/skills/code-review/examples/sample-report.md +41 -0
  501. package/skills/code-review/output-format.md +478 -0
  502. package/skills/code-review/scripts/activation.py +86 -0
  503. package/skills/code-review/scripts/change_impact.py +357 -0
  504. package/skills/code-review/scripts/change_shape.py +372 -0
  505. package/skills/code-review/scripts/change_size.py +212 -0
  506. package/skills/code-review/scripts/changed_file_list.py +141 -0
  507. package/skills/code-review/scripts/closing_pass.py +187 -0
  508. package/skills/code-review/scripts/consolidate.py +277 -0
  509. package/skills/code-review/scripts/contract_failure_report.py +185 -0
  510. package/skills/code-review/scripts/dispatch_reconcile.py +66 -0
  511. package/skills/code-review/scripts/dispatch_waves.py +164 -0
  512. package/skills/code-review/scripts/finding_signature.py +446 -0
  513. package/skills/code-review/scripts/ledger.py +283 -0
  514. package/skills/code-review/scripts/partition.py +169 -0
  515. package/skills/code-review/scripts/render_tiered_findings.py +274 -0
  516. package/skills/code-review/scripts/repo_invariants.py +1066 -0
  517. package/skills/code-review/scripts/review_context_pack.py +306 -0
  518. package/skills/code-review/scripts/review_round_log.py +345 -0
  519. package/skills/code-review/scripts/review_value_coverage.py +297 -0
  520. package/skills/code-review/scripts/validate_review_output.py +467 -0
  521. package/skills/code-review/sliced-mode.md +205 -0
  522. package/skills/competitive-analysis/SKILL.md +191 -0
  523. package/skills/context-loading-protocol/SKILL.md +157 -0
  524. package/skills/continue/SKILL.md +90 -0
  525. package/skills/cost-report/SKILL.md +178 -0
  526. package/skills/coverage-baseline/SKILL.md +335 -0
  527. package/skills/coverage-baseline/references/multi-project-discovery.md +202 -0
  528. package/skills/coverage-delta/SKILL.md +181 -0
  529. package/skills/coverage-delta/references/mutation-gate.md +70 -0
  530. package/skills/design-doc/SKILL.md +95 -0
  531. package/skills/design-interrogation/SKILL.md +89 -0
  532. package/skills/design-it-twice/SKILL.md +91 -0
  533. package/skills/docker-image-audit/SKILL.md +108 -0
  534. package/skills/docker-image-audit/references/install-guide.md +64 -0
  535. package/skills/docker-image-audit/references/report-template.md +73 -0
  536. package/skills/docker-image-create/SKILL.md +185 -0
  537. package/skills/domain-analysis/SKILL.md +183 -0
  538. package/skills/domain-driven-design/SKILL.md +194 -0
  539. package/skills/exploratory-testing/SKILL.md +108 -0
  540. package/skills/explore/SKILL.md +51 -0
  541. package/skills/farley-score/SKILL.md +165 -0
  542. package/skills/feature-file-validation/SKILL.md +78 -0
  543. package/skills/feature-file-validation/references/validation-rules.md +115 -0
  544. package/skills/feedback-learning/SKILL.md +414 -0
  545. package/skills/fix/SKILL.md +450 -0
  546. package/skills/freeze/SKILL.md +68 -0
  547. package/skills/frontend-architecture/SKILL.md +113 -0
  548. package/skills/gherkin-derive/SKILL.md +630 -0
  549. package/skills/gherkin-public/SKILL.md +266 -0
  550. package/skills/governance-compliance/SKILL.md +150 -0
  551. package/skills/guard/SKILL.md +75 -0
  552. package/skills/handoff/SKILL.md +139 -0
  553. package/skills/handoff/references/summary-templates.md +242 -0
  554. package/skills/harness-audit/SKILL.md +751 -0
  555. package/skills/harness-audit/scripts/lesson_validate.py +386 -0
  556. package/skills/harness-audit/scripts/redundancy_criterion.py +188 -0
  557. package/skills/headless-run/SKILL.md +45 -0
  558. package/skills/headless-run/scripts/isolated_dispatch.py +381 -0
  559. package/skills/help/SKILL.md +72 -0
  560. package/skills/hexagonal-architecture/SKILL.md +85 -0
  561. package/skills/human-oversight-protocol/SKILL.md +224 -0
  562. package/skills/issues-from-assessment/SKILL.md +223 -0
  563. package/skills/issues-from-plan/SKILL.md +133 -0
  564. package/skills/legacy-code/SKILL.md +132 -0
  565. package/skills/mermaid-diagramming/SKILL.md +120 -0
  566. package/skills/mutation-night-watch/SKILL.md +154 -0
  567. package/skills/mutation-night-watch/references/scheduling.md +135 -0
  568. package/skills/mutation-testing/SKILL.md +396 -0
  569. package/skills/mutation-testing/references/languages/csharp-stryker-net.md +676 -0
  570. package/skills/mutation-testing/references/languages/go-go-mutesting.md +95 -0
  571. package/skills/mutation-testing/references/languages/java-pitest.md +77 -0
  572. package/skills/mutation-testing/references/languages/javascript-stryker.md +188 -0
  573. package/skills/mutation-testing/references/languages/python-mutmut.md +97 -0
  574. package/skills/mutation-testing/references/time-estimation.md +34 -0
  575. package/skills/mutation-testing/references/tool-detection.md +15 -0
  576. package/skills/mutation-testing/references/workflow-callers.md +23 -0
  577. package/skills/mutation-testing/scripts/__pycache__/xunit_v3_feature_detector.cpython-314.pyc +0 -0
  578. package/skills/mutation-testing/scripts/csharp_stryker_net_slice_runner.py +635 -0
  579. package/skills/mutation-testing/scripts/csharp_stryker_net_status_loop.py +525 -0
  580. package/skills/mutation-testing/scripts/csharp_stryker_net_wrapper.py +681 -0
  581. package/skills/mutation-testing/scripts/mutation_baseline_reuse.py +292 -0
  582. package/skills/mutation-testing/scripts/mutation_exclude_policy.py +268 -0
  583. package/skills/mutation-testing/scripts/mutation_feasibility_gate.py +463 -0
  584. package/skills/mutation-testing/scripts/mutation_kill_headless.py +331 -0
  585. package/skills/mutation-testing/scripts/mutation_kill_insert.py +199 -0
  586. package/skills/mutation-testing/scripts/mutation_kill_insert_python.py +150 -0
  587. package/skills/mutation-testing/scripts/mutation_kill_loop.py +869 -0
  588. package/skills/mutation-testing/scripts/mutation_kill_loop_python.py +949 -0
  589. package/skills/mutation-testing/scripts/mutation_kill_retry.py +592 -0
  590. package/skills/mutation-testing/scripts/mutation_kill_shared.py +620 -0
  591. package/skills/mutation-testing/scripts/mutation_nightwatch.py +462 -0
  592. package/skills/mutation-testing/scripts/mutation_nightwatch_stacks.py +425 -0
  593. package/skills/mutation-testing/scripts/mutation_report.py +743 -0
  594. package/skills/mutation-testing/scripts/mutation_report_cli.py +175 -0
  595. package/skills/mutation-testing/scripts/mutation_safety_gate.py +69 -0
  596. package/skills/mutation-testing/scripts/stryker_shard_pipeline.py +847 -0
  597. package/skills/mutation-testing/scripts/stryker_shard_setup.py +440 -0
  598. package/skills/mutation-testing/scripts/stryker_timeout_retry.py +142 -0
  599. package/skills/mutation-testing/scripts/xunit_v3_feature_detector.py +341 -0
  600. package/skills/performance-benchmark/SKILL.md +174 -0
  601. package/skills/performance-benchmark/examples/report-format.md +43 -0
  602. package/skills/performance-benchmark/references/benchmark-script.md +169 -0
  603. package/skills/performance-metrics/SKILL.md +265 -0
  604. package/skills/plan/SKILL.md +199 -0
  605. package/skills/plan/references/gherkin-persistence.md +43 -0
  606. package/skills/plan/references/plan-template.md +182 -0
  607. package/skills/pr/SKILL.md +289 -0
  608. package/skills/pr/scripts/gate_retry_state.py +368 -0
  609. package/skills/project-init/README.md +141 -0
  610. package/skills/project-init/SKILL.md +1197 -0
  611. package/skills/project-init/evals/evals.json +200 -0
  612. package/skills/project-init/references/capability-tools.md +55 -0
  613. package/skills/project-init/references/configs.md +221 -0
  614. package/skills/property-based-testing/SKILL.md +121 -0
  615. package/skills/property-based-testing/fixtures/invariant_fixture.py +15 -0
  616. package/skills/property-based-testing/fixtures/js-roundtrip/README.md +42 -0
  617. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/fast-check/LICENSE +21 -0
  618. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/fast-check/README.md +263 -0
  619. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/fast-check/lib/cjs/fast-check.d.ts +5165 -0
  620. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/fast-check/lib/cjs/fast-check.js +12147 -0
  621. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/fast-check/lib/cjs/package.json +3 -0
  622. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/fast-check/lib/cjs/types57/fast-check.d.ts +5165 -0
  623. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/fast-check/lib/fast-check.d.ts +5165 -0
  624. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/fast-check/lib/fast-check.js +12011 -0
  625. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/fast-check/lib/rolldown-runtime-D7D4PA-g.js +13 -0
  626. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/fast-check/lib/types57/fast-check.d.ts +5165 -0
  627. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/fast-check/package.json +94 -0
  628. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/LICENSE +21 -0
  629. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/README.md +168 -0
  630. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/RandomGenerator-DcXj09Ch.d.ts +14 -0
  631. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/distribution/uniformBigInt.d.ts +15 -0
  632. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/distribution/uniformBigInt.js +38 -0
  633. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/distribution/uniformFloat32.d.ts +15 -0
  634. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/distribution/uniformFloat32.js +18 -0
  635. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/distribution/uniformFloat64.d.ts +15 -0
  636. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/distribution/uniformFloat64.js +22 -0
  637. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/distribution/uniformInt.d.ts +15 -0
  638. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/distribution/uniformInt.js +134 -0
  639. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/RandomGenerator-DcXj09Ch.d.ts +14 -0
  640. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/distribution/uniformBigInt.d.ts +15 -0
  641. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/distribution/uniformBigInt.js +37 -0
  642. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/distribution/uniformFloat32.d.ts +15 -0
  643. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/distribution/uniformFloat32.js +17 -0
  644. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/distribution/uniformFloat64.d.ts +15 -0
  645. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/distribution/uniformFloat64.js +21 -0
  646. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/distribution/uniformInt.d.ts +15 -0
  647. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/distribution/uniformInt.js +133 -0
  648. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/generator/congruential32.d.ts +7 -0
  649. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/generator/congruential32.js +44 -0
  650. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/generator/mersenne.d.ts +7 -0
  651. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/generator/mersenne.js +90 -0
  652. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/generator/xoroshiro128plus.d.ts +7 -0
  653. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/generator/xoroshiro128plus.js +80 -0
  654. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/generator/xorshift128plus.d.ts +7 -0
  655. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/generator/xorshift128plus.js +78 -0
  656. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/package.json +3 -0
  657. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/types/JumpableRandomGenerator.d.ts +16 -0
  658. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/types/JumpableRandomGenerator.js +0 -0
  659. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/types/RandomGenerator.d.ts +2 -0
  660. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/types/RandomGenerator.js +0 -0
  661. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/utils/generateN.d.ts +6 -0
  662. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/utils/generateN.js +8 -0
  663. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/utils/purify.d.ts +12 -0
  664. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/utils/purify.js +9 -0
  665. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/utils/skipN.d.ts +6 -0
  666. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/utils/skipN.js +6 -0
  667. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/generator/congruential32.d.ts +7 -0
  668. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/generator/congruential32.js +46 -0
  669. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/generator/mersenne.d.ts +7 -0
  670. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/generator/mersenne.js +92 -0
  671. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/generator/xoroshiro128plus.d.ts +7 -0
  672. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/generator/xoroshiro128plus.js +82 -0
  673. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/generator/xorshift128plus.d.ts +7 -0
  674. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/generator/xorshift128plus.js +80 -0
  675. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/types/JumpableRandomGenerator.d.ts +16 -0
  676. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/types/JumpableRandomGenerator.js +0 -0
  677. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/types/RandomGenerator.d.ts +2 -0
  678. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/types/RandomGenerator.js +0 -0
  679. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/utils/generateN.d.ts +6 -0
  680. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/utils/generateN.js +9 -0
  681. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/utils/purify.d.ts +12 -0
  682. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/utils/purify.js +10 -0
  683. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/utils/skipN.d.ts +6 -0
  684. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/utils/skipN.js +7 -0
  685. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/package.json +133 -0
  686. package/skills/property-based-testing/fixtures/js-roundtrip/package-lock.json +1179 -0
  687. package/skills/property-based-testing/fixtures/js-roundtrip/package.json +14 -0
  688. package/skills/property-based-testing/fixtures/js-roundtrip/roundtrip.js +29 -0
  689. package/skills/property-based-testing/fixtures/js-roundtrip/roundtrip.properties.test.js +16 -0
  690. package/skills/property-based-testing/fixtures/no_property_fixture.py +10 -0
  691. package/skills/property-based-testing/fixtures/roundtrip_fixture.py +16 -0
  692. package/skills/property-based-testing/references/languages/javascript.md +54 -0
  693. package/skills/property-based-testing/scripts/detect_and_dispatch.py +80 -0
  694. package/skills/property-based-testing/scripts/hypothesis_scaffold.py +276 -0
  695. package/skills/proxy-resilience/SKILL.md +84 -0
  696. package/skills/quality-gate-pipeline/SKILL.md +184 -0
  697. package/skills/quality-targets-converge/SKILL.md +254 -0
  698. package/skills/repo-review/SKILL.md +159 -0
  699. package/skills/report-pdf/SKILL.md +66 -0
  700. package/skills/review/SKILL.md +47 -0
  701. package/skills/review-agent/SKILL.md +152 -0
  702. package/skills/review-summary/SKILL.md +73 -0
  703. package/skills/run-report/SKILL.md +70 -0
  704. package/skills/semantic-duplication-scan/SKILL.md +337 -0
  705. package/skills/semantic-scan/SKILL.md +53 -0
  706. package/skills/semgrep-analyze/SKILL.md +139 -0
  707. package/skills/setup/SKILL.md +1122 -0
  708. package/skills/ship/SKILL.md +240 -0
  709. package/skills/source-verification/SKILL.md +210 -0
  710. package/skills/source-verification/scripts/claim_extractor.py +155 -0
  711. package/skills/specs/.size-baseline.json +4 -0
  712. package/skills/specs/SKILL.md +243 -0
  713. package/skills/specs/references/completeness-checklist.md +83 -0
  714. package/skills/specs/references/extraction.md +58 -0
  715. package/skills/specs/references/glossary.md +59 -0
  716. package/skills/specs/references/persistence.md +115 -0
  717. package/skills/specs/references/predictability-check.md +77 -0
  718. package/skills/static-analysis-integration/SKILL.md +235 -0
  719. package/skills/static-analysis-integration/adapters/_envelope.py +26 -0
  720. package/skills/static-analysis-integration/adapters/jscpd-adapter.py +66 -0
  721. package/skills/static-analysis-integration/adapters/lizard-adapter.py +81 -0
  722. package/skills/static-analysis-integration/adapters/mypy-adapter.py +50 -0
  723. package/skills/static-analysis-integration/adapters/mypy-src-layout.py +93 -0
  724. package/skills/static-analysis-integration/adapters/security-review-adapter.py +212 -0
  725. package/skills/static-analysis-integration/maintenance.md +23 -0
  726. package/skills/static-analysis-integration/references/language-setup.md +228 -0
  727. package/skills/static-analysis-integration/references/sarif-parser.md +124 -0
  728. package/skills/static-analysis-integration/references/security-review-adapter.md +118 -0
  729. package/skills/static-analysis-integration/references/tool-configs.md +617 -0
  730. package/skills/static-analysis-integration/rulesets/pmd-quickstart.xml +24 -0
  731. package/skills/stryker-xunit-v2-shim/SKILL.md +274 -0
  732. package/skills/stryker-xunit-v2-shim/references/shim-howto.md +256 -0
  733. package/skills/stryker-xunit-v2-shim/scripts/generate_shim.py +143 -0
  734. package/skills/systematic-debugging/SKILL.md +130 -0
  735. package/skills/telemetry/SKILL.md +75 -0
  736. package/skills/test-audit-disable/SKILL.md +129 -0
  737. package/skills/test-design/SKILL.md +177 -0
  738. package/skills/test-design/scripts/__pycache__/internal_double_detector.cpython-314.pyc +0 -0
  739. package/skills/test-design/scripts/internal_double_detector.py +631 -0
  740. package/skills/test-design-advisor/SKILL.md +166 -0
  741. package/skills/test-driven-development/SKILL.md +169 -0
  742. package/skills/test-health/SKILL.md +262 -0
  743. package/skills/test-improve/SKILL.md +239 -0
  744. package/skills/test-improve/references/phase-0-approach-contract.md +228 -0
  745. package/skills/test-improve/references/phase-1-analyze.md +131 -0
  746. package/skills/test-improve/references/phase-2-baseline.md +121 -0
  747. package/skills/test-improve/references/phase-3-derive-gherkin.md +53 -0
  748. package/skills/test-improve/references/phase-4-plan-fixes.md +34 -0
  749. package/skills/test-improve/references/phase-5-improve.md +215 -0
  750. package/skills/test-improve/references/phase-6-refactor-decision.md +45 -0
  751. package/skills/test-improve/references/phase-7-refactor.md +44 -0
  752. package/skills/test-improve/references/phase-8-validate.md +66 -0
  753. package/skills/test-improve/references/phase-9-close-out-prompt.md +11 -0
  754. package/skills/test-improve/references/phase-9-report.md +62 -0
  755. package/skills/test-improve/references/review-loop.md +92 -0
  756. package/skills/test-improve/templates/executive-summary.md +123 -0
  757. package/skills/threat-modeling/SKILL.md +108 -0
  758. package/skills/triage/SKILL.md +211 -0
  759. package/skills/ubiquitous-language/SKILL.md +192 -0
  760. package/skills/ubiquitous-language/scripts/collect_domain_signals.py +300 -0
  761. package/skills/unfreeze/SKILL.md +37 -0
  762. package/skills/upgrade/SKILL.md +31 -0
  763. package/skills/upgrade/scripts/check_version_drift.py +113 -0
  764. package/skills/upgrade/scripts/enable_autoupdate.py +149 -0
  765. package/skills/version/SKILL.md +25 -0
  766. package/sync/__pycache__/sync_upstream.cpython-314.pyc +0 -0
  767. package/sync/sync_upstream.py +293 -0
  768. package/templates/ACCEPTED-RISKS.md.tmpl +46 -0
  769. package/templates/agents/agent-template.md +151 -0
  770. package/templates/agents/angular-testing.md +66 -0
  771. package/templates/agents/csharp-quality.md +63 -0
  772. package/templates/agents/esm-enforcer.md +52 -0
  773. package/templates/agents/front-end-testing.md +65 -0
  774. package/templates/agents/go-quality.md +65 -0
  775. package/templates/agents/python-quality.md +62 -0
  776. package/templates/agents/react-testing.md +61 -0
  777. package/templates/agents/ts-enforcer.md +60 -0
  778. package/templates/agents/twelve-factor-audit.md +49 -0
  779. package/tools/entropy-check.py +250 -0
  780. 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())