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,428 @@
1
+ #!/usr/bin/env python3
2
+ """scan_bash_command_for_banned_scripts.py — real-time PreToolUse denial of new
3
+ shell-script writes under plugins/dev-team/ (#1755).
4
+
5
+ This repo's own CLAUDE.md states the rule ("every shipped
6
+ plugins/dev-team/ script is Python 3.10+ stdlib-only", ADR 0014/0015) and CI
7
+ has a `check-python-only` gate, but neither stops a `.sh`/`.bash`/`.bat`/
8
+ `.cmd`/`.ps1` file from being written *during* a session — the violation was
9
+ only caught later, in code review or CI. This hook closes that gap at the
10
+ point of the `Bash` tool call itself, before the write happens.
11
+
12
+ Heuristic, not exhaustive: it regex/token-scans the command string for
13
+ redirect (`>`, `>>`), `cp`, `mv`, and `tee` write targets. A sufficiently
14
+ obfuscated command (base64-decoded write, a wrapper script, `eval`) can
15
+ evade it — this hook's own scan says so, deliberately, rather than implying
16
+ a guarantee it can't back. `scan_worktree_for_banned_scripts.py` (PostToolUse,
17
+ `*` matcher) is the required backstop: it inspects the real working tree
18
+ after every tool call, catching anything this scan misses regardless of which
19
+ tool wrote it.
20
+
21
+ Contract (docs/python-hook-contract.md):
22
+ Input : PreToolUse JSON on stdin with tool_input.command (Bash matcher)
23
+ Output: exit 0 = allow; exit 2 = block (message on stdout AND stderr)
24
+
25
+ Stdlib-only. See ADR 0014/0015.
26
+ """
27
+
28
+ from __future__ import annotations
29
+
30
+ import re
31
+ import shlex
32
+ import sys
33
+ from pathlib import Path
34
+
35
+ _SCRIPT_DIR = Path(__file__).resolve().parent
36
+ _LIB_DIR = _SCRIPT_DIR / "lib"
37
+ if str(_LIB_DIR) not in sys.path:
38
+ sys.path.insert(0, str(_LIB_DIR))
39
+
40
+ from artifact_paths import project_root
41
+ from banned_scripts_policy import (
42
+ ALLOWED_RELATIVE_PATHS,
43
+ BANNED_EXTENSIONS,
44
+ SCOPED_PREFIX,
45
+ looks_like_monorepo_checkout,
46
+ )
47
+ from stdin_json import read_stdin_json
48
+
49
+ # `>|` is tolerated between the operator and its target (bash's
50
+ # force-overwrite/noclobber-override redirect) — without it, `|` sits
51
+ # outside the capture class and the whole match silently fails, letting a
52
+ # `>|`-redirected banned write escape `_redirect_targets` entirely.
53
+ _REDIRECT_RE = re.compile(r">>?\|?\s*([^\s;|&<>]+)")
54
+
55
+ # (#1904 item 12) Shell metacharacters always tokenized on their own,
56
+ # regardless of adjacent whitespace, by `_tokenize_command`'s
57
+ # `punctuation_chars` lexer. `\n` is included so a bare newline between two
58
+ # commands is a real segment boundary rather than being swallowed as
59
+ # ordinary whitespace (the default `shlex` behavior, which would silently
60
+ # merge two unrelated commands' tokens into one stream).
61
+ #
62
+ # `|&` (bash shorthand for `2>&1 |`) and `;&` (a case-statement fallthrough
63
+ # terminator) are real command separators too — both group into a single
64
+ # lexer token, same as `&&`/`||`, and omitting them left a command like
65
+ # `true |& cp evil.sh dest.sh` as one un-split segment whose `prog` resolved
66
+ # to `true`, never reaching the cp/mv destination check at all.
67
+ _PUNCTUATION_CHARS = "();<>|&\n"
68
+ _COMMAND_SEPARATORS = frozenset({"&&", "||", ";", "&", "|", "|&", ";&", "\n"})
69
+ _OPERATOR_CHARS = frozenset("();<>|&")
70
+
71
+ # (#1904 item 13) GNU's attached-value form for a short-option cluster
72
+ # ending in `t` (`-tDIR`, `-rtDIR`) — the value is glued directly onto the
73
+ # cluster with no space, distinct from the detached `-t DIR` / `-rt DIR`
74
+ # form handled separately in `_parse_destination_args`.
75
+ _ATTACHED_TARGET_DIR_RE = re.compile(r"^-[^-]*t(?P<value>.+)$")
76
+ _TARGET_DIRECTORY_LONG_OPTION = "--target-directory"
77
+
78
+
79
+ def _has_banned_extension(path_str: str) -> bool:
80
+ return path_str.lower().endswith(BANNED_EXTENSIONS)
81
+
82
+
83
+ def _strip_quotes(token: str) -> str:
84
+ """Strip one matching pair of leading/trailing quote characters.
85
+
86
+ The redirect regex's capture group has no notion of shell quoting, so
87
+ `> "evil.sh"` captures the target WITH its surrounding quotes — which
88
+ then fails `.endswith(".sh")` (#1864 sub-claim 3). This normalizes the
89
+ captured text before the extension check, nothing more.
90
+ """
91
+ if len(token) >= 2 and token[0] == token[-1] and token[0] in ("'", '"'):
92
+ return token[1:-1]
93
+ return token
94
+
95
+
96
+ def _redirect_targets(command: str) -> list[str]:
97
+ targets = []
98
+ for match in _REDIRECT_RE.finditer(command):
99
+ target = _strip_quotes(match.group(1))
100
+ if _has_banned_extension(target):
101
+ targets.append(target)
102
+ return targets
103
+
104
+
105
+ def _match_long_target_directory(args: list[str], i: int, n: int) -> tuple[int, str | None]:
106
+ """`--target-directory=DIR` or the detached `--target-directory DIR`.
107
+
108
+ Returns `(tokens_consumed, target_dir)` — `(0, None)` on no match, the
109
+ contract every matcher in `_TARGET_DIR_MATCHERS` shares.
110
+ """
111
+ arg = args[i]
112
+ if arg.startswith("--target-directory="):
113
+ return 1, arg.split("=", 1)[1]
114
+ if arg == "--target-directory" and i + 1 < n:
115
+ return 2, args[i + 1]
116
+ return 0, None
117
+
118
+
119
+ def _match_abbreviated_long_option(args: list[str], i: int, n: int) -> tuple[int, str | None]:
120
+ """An unambiguous abbreviation of `--target-directory=` (e.g.
121
+ `--target-dir=DIR`) — cp/mv define no other `--target-*` long option,
122
+ so any prefix is unambiguous (mirrors GNU getopt_long's own
123
+ abbreviation rule). Checked before the `=` split so a boundary must
124
+ actually exist (#1904 item 13)."""
125
+ del n
126
+ arg = args[i]
127
+ if arg.startswith("--") and "=" in arg:
128
+ flag, _, value = arg.partition("=")
129
+ if len(flag) > 2 and _TARGET_DIRECTORY_LONG_OPTION.startswith(flag):
130
+ return 1, value
131
+ return 0, None
132
+
133
+
134
+ def _match_attached_short_option(args: list[str], i: int, n: int) -> tuple[int, str | None]:
135
+ """GNU's attached-value form: the value is glued directly onto a
136
+ short-option cluster ending in `t`, with no space (`-tDIR`, `-rtDIR`) —
137
+ distinct from the detached `-t DIR` form (#1904 item 13)."""
138
+ del n
139
+ arg = args[i]
140
+ if arg.startswith("-") and not arg.startswith("--"):
141
+ attached = _ATTACHED_TARGET_DIR_RE.match(arg)
142
+ if attached:
143
+ return 1, attached.group("value")
144
+ return 0, None
145
+
146
+
147
+ def _match_clustered_short_option(args: list[str], i: int, n: int) -> tuple[int, str | None]:
148
+ """A single-dash cluster ending in `t` (`-t`, `-rt`, `-at`, `-vt`, ...)
149
+ is GNU coreutils' clustered-short-option form of `-t DIR` — the
150
+ unclustered `-t` alone was the only form recognized before, so
151
+ `cp -rt DIR src.sh` fell through to the (wrong) last-positional
152
+ heuristic and evaded detection."""
153
+ arg = args[i]
154
+ if (
155
+ arg.startswith("-")
156
+ and not arg.startswith("--")
157
+ and len(arg) > 1
158
+ and arg.endswith("t")
159
+ and i + 1 < n
160
+ ):
161
+ return 2, args[i + 1]
162
+ return 0, None
163
+
164
+
165
+ _TARGET_DIR_MATCHERS = (
166
+ _match_long_target_directory,
167
+ _match_abbreviated_long_option,
168
+ _match_attached_short_option,
169
+ _match_clustered_short_option,
170
+ )
171
+
172
+
173
+ def _parse_destination_args(args: list[str]) -> tuple[str | None, list[str]]:
174
+ """Tokenize `cp`/`mv` arguments (after the program name) into a GNU
175
+ target-directory value (`-t DIR` / `--target-directory=DIR` /
176
+ `--target-directory DIR`, plus the attached/abbreviated/clustered
177
+ variants in `_TARGET_DIR_MATCHERS`) and the remaining positional
178
+ arguments.
179
+
180
+ Returns `(target_dir, positionals)`. `target_dir` is `None` when no
181
+ target-directory form was used, and the caller falls back to treating
182
+ the last positional as the destination (#1875). A dispatch loop over
183
+ `_TARGET_DIR_MATCHERS`, in order — first match wins, mirroring the
184
+ original if/elif precedence.
185
+ """
186
+ target_dir: str | None = None
187
+ positionals: list[str] = []
188
+ i = 0
189
+ n = len(args)
190
+ while i < n:
191
+ arg = args[i]
192
+ matched = False
193
+ for matcher in _TARGET_DIR_MATCHERS:
194
+ consumed, value = matcher(args, i, n)
195
+ if consumed:
196
+ target_dir = value
197
+ i += consumed
198
+ matched = True
199
+ break
200
+ if matched:
201
+ continue
202
+ if arg.startswith("-") and arg != "-":
203
+ i += 1
204
+ continue
205
+ positionals.append(arg)
206
+ i += 1
207
+ return target_dir, positionals
208
+
209
+
210
+ def _tokenize_command(command: str) -> list[str]:
211
+ """Tokenize the whole command once, quote-aware, with shell
212
+ metacharacters always returned as their own tokens regardless of
213
+ adjacent whitespace (#1904 item 12b). This is what makes a quoted
214
+ separator or redirect character (`"a&b.sh"`, `"a>b.sh"`) survive as
215
+ literal content of the word it belongs to, instead of desyncing a plain
216
+ string split/regex over the raw command text — the previous approach's
217
+ `re.split` had no notion of quoting, so a separator character inside
218
+ quotes silently broke the word apart mid-token.
219
+
220
+ Raises `ValueError` on malformed quoting; callers must fail open (skip,
221
+ never crash) on that, matching the rest of this hook's contract.
222
+ """
223
+ lexer = shlex.shlex(command, posix=True, punctuation_chars=_PUNCTUATION_CHARS)
224
+ lexer.whitespace_split = True
225
+ lexer.whitespace = " \t\r"
226
+ return list(lexer)
227
+
228
+
229
+ def _segment_simple_commands(tokens: list[str]) -> list[list[str]]:
230
+ """Split a flat token stream into one list per simple command, breaking
231
+ at command-separator tokens (`&&`, `||`, `;`, `&`, `|`, or a bare
232
+ newline) — the tokenized replacement for the old raw-string
233
+ `re.split(r"&&|\\|\\||[;|&\\n\\r]", command)` (#1904 item 12b)."""
234
+ segments: list[list[str]] = []
235
+ current: list[str] = []
236
+ for tok in tokens:
237
+ if tok in _COMMAND_SEPARATORS:
238
+ if current:
239
+ segments.append(current)
240
+ current = []
241
+ continue
242
+ current.append(tok)
243
+ if current:
244
+ segments.append(current)
245
+ return segments
246
+
247
+
248
+ def _is_operator_token(tok: str) -> bool:
249
+ """True when every character in `tok` is a shell metacharacter — the
250
+ heuristic signature of an UNQUOTED occurrence.
251
+
252
+ KNOWN LIMITATION: this is imperfect in posix `shlex` mode. A wholly
253
+ quoted metacharacter sequence normalizes to the IDENTICAL token text as
254
+ the real unquoted operator — `shlex` strips quotes before this
255
+ function ever sees the token, so `tee ";" plugins/dev-team/hooks/
256
+ evil.sh` produces the same `";"` token as an actual unquoted `;`
257
+ separator, and `_segment_simple_commands` splits `tee` from its own
258
+ (quoted, literal) destination argument. This lets that destination
259
+ escape detection. A correct fix needs per-token quotedness tracked
260
+ from the lexer itself (subclassing `shlex.shlex.read_token` to record
261
+ whether a quote character was consumed) rather than inferred from the
262
+ token's content after the fact — a larger tokenizer rework than this
263
+ fix; see `test_quoted_semicolon_argument_known_limitation_evades_scan`
264
+ in the test suite for the exact repro, tracked as a follow-up rather
265
+ than silently left unaddressed."""
266
+ return bool(tok) and all(c in _OPERATOR_CHARS for c in tok)
267
+
268
+
269
+ def _strip_redirects(tokens: list[str]) -> list[str]:
270
+ """Drop every unquoted redirect/operator token, the operand
271
+ immediately following one, and a bare file-descriptor digit
272
+ immediately preceding one (`2>err.log`, `2>&1`), from a simple
273
+ command's token list (#1904 item 12a) — a trailing shell redirect
274
+ (`... 2>/dev/null`, `... > /dev/null`) must never be mistaken for a
275
+ `cp`/`mv` positional destination argument.
276
+
277
+ Any operator token CONTAINING `>` consumes its following operand, not
278
+ just the two literal forms `>`/`>>` — `shlex`'s `punctuation_chars`
279
+ lexer groups a run of adjacent metacharacters into one multi-character
280
+ token, so bash's `&>`, `>&`, `>|`, and fd-duplication forms like
281
+ `2>&1` all arrive as a single `>`-containing token distinct from a
282
+ bare `>`/`>>`. Treating only the two literal forms as redirects left
283
+ every one of those evading this heuristic entirely, since their
284
+ trailing operand (e.g. `/dev/null`, a bare fd number) would survive
285
+ into the positional-argument list and get mistaken for the real
286
+ destination.
287
+ """
288
+ cleaned: list[str] = []
289
+ i = 0
290
+ n = len(tokens)
291
+ while i < n:
292
+ tok = tokens[i]
293
+ if (
294
+ tok.isdigit()
295
+ and i + 1 < n
296
+ and _is_operator_token(tokens[i + 1])
297
+ and ">" in tokens[i + 1]
298
+ ):
299
+ i += 3 if i + 2 < n else 2
300
+ continue
301
+ if _is_operator_token(tok) and ">" in tok:
302
+ i += 2 if i + 1 < n else 1
303
+ continue
304
+ if _is_operator_token(tok):
305
+ i += 1
306
+ continue
307
+ cleaned.append(tok)
308
+ i += 1
309
+ return cleaned
310
+
311
+
312
+ def _target_dir_destinations(target_dir: str, positionals: list[str]) -> list[str]:
313
+ """Expand a GNU target-directory `cp`/`mv` invocation (#1878) into its
314
+ effective per-source write targets: `target_dir/basename(source)` for
315
+ every remaining positional, filtered to banned extensions."""
316
+ destinations: list[str] = []
317
+ for source in positionals:
318
+ dest = f"{target_dir.rstrip('/')}/{Path(source).name}"
319
+ if _has_banned_extension(dest):
320
+ destinations.append(dest)
321
+ return destinations
322
+
323
+
324
+ def _copy_move_tee_targets(command: str) -> list[str]:
325
+ """Best-effort tokenization to catch `cp`/`mv`/`tee` destinations.
326
+
327
+ Tokenizes the whole command once via `_tokenize_command` and segments
328
+ on the resulting command-separator tokens (#1904 item 12), rather than
329
+ regex-splitting the raw command string first. Each segment then has its
330
+ trailing shell redirect stripped via `_strip_redirects` before the
331
+ last-positional-is-the-destination heuristic runs. Malformed quoting is
332
+ skipped, never crashed on: a scan error must fail open, not block a
333
+ legitimate command it couldn't parse.
334
+ """
335
+ targets: list[str] = []
336
+ try:
337
+ tokens = _tokenize_command(command)
338
+ except ValueError:
339
+ return targets
340
+ for segment in _segment_simple_commands(tokens):
341
+ segment = _strip_redirects(segment)
342
+ if not segment:
343
+ continue
344
+ prog = Path(segment[0]).name
345
+ if prog == "tee":
346
+ # tee writes to every named file, not just the last.
347
+ args = [t for t in segment[1:] if not t.startswith("-")]
348
+ targets.extend(a for a in args if _has_banned_extension(a))
349
+ elif prog in ("cp", "mv"):
350
+ target_dir, positionals = _parse_destination_args(segment[1:])
351
+ if target_dir is not None:
352
+ # GNU target-directory form (#1878): every remaining
353
+ # positional is a SOURCE being copied/moved INTO
354
+ # target_dir.
355
+ targets.extend(_target_dir_destinations(target_dir, positionals))
356
+ elif positionals and _has_banned_extension(positionals[-1]):
357
+ # Fixed #1875 heuristic: the destination is the actual last
358
+ # positional argument — never an earlier source, even one
359
+ # with a banned extension (e.g. `mv evil.sh good.py`, the
360
+ # correct remediation, must not be flagged).
361
+ targets.append(positionals[-1])
362
+ return targets
363
+
364
+
365
+ def _scoped_relative_path(target: str, cwd: Path, root: Path) -> str | None:
366
+ """Resolve `target` (as written in the command) against `cwd`, then
367
+ return its path relative to `root` in POSIX form — or ``None`` when it
368
+ resolves outside `root` entirely."""
369
+ candidate = Path(target)
370
+ resolved = candidate if candidate.is_absolute() else (cwd / candidate)
371
+ try:
372
+ rel = resolved.resolve().relative_to(root.resolve())
373
+ except ValueError:
374
+ return None
375
+ return rel.as_posix()
376
+
377
+
378
+ def find_banned_write(command: str, cwd: Path, root: Path) -> str | None:
379
+ """Return the first plugins/dev-team/-scoped banned-extension write
380
+ target in `command` (relative to `root`), or ``None``. Skips the two
381
+ documented bootstrap-shim carve-outs."""
382
+ for target in [*_redirect_targets(command), *_copy_move_tee_targets(command)]:
383
+ rel = _scoped_relative_path(target, cwd, root)
384
+ if rel is None or not rel.startswith(SCOPED_PREFIX):
385
+ continue
386
+ if rel in ALLOWED_RELATIVE_PATHS:
387
+ continue
388
+ return rel
389
+ return None
390
+
391
+
392
+ def main() -> int:
393
+ payload = read_stdin_json() or {}
394
+
395
+ tool_input = payload.get("tool_input")
396
+ command = tool_input.get("command") if isinstance(tool_input, dict) else None
397
+ if not isinstance(command, str) or not command.strip():
398
+ return 0
399
+
400
+ cwd = Path(payload.get("cwd") or ".")
401
+
402
+ # (#1861) Cheap, non-git early exit for any checkout where this
403
+ # monorepo's own plugins/dev-team/ tree doesn't exist — e.g. every
404
+ # downstream install of the plugin. Checked via a plain filesystem walk
405
+ # BEFORE project_root() (which itself shells out to git), not after.
406
+ if not looks_like_monorepo_checkout(cwd):
407
+ return 0
408
+
409
+ root = project_root(cwd)
410
+ hit = find_banned_write(command, cwd, root)
411
+ if hit is None:
412
+ return 0
413
+
414
+ message = (
415
+ f"[BLOCK] {hit} is a shell-script write under plugins/dev-team/. "
416
+ "Every shipped script there must be Python 3.10+ stdlib-only "
417
+ "(ADR 0014/0015; repo CLAUDE.md § Script authoring). Write a "
418
+ ".py module instead, or confirm this is genuinely one of the two "
419
+ "documented bootstrap-shim exceptions (plugins/dev-team/install.sh, "
420
+ "plugins/dev-team/hooks/py.sh)."
421
+ )
422
+ sys.stdout.write(message + "\n")
423
+ sys.stderr.write(message + "\n")
424
+ return 2
425
+
426
+
427
+ if __name__ == "__main__":
428
+ sys.exit(main())
@@ -0,0 +1,238 @@
1
+ #!/usr/bin/env python3
2
+ """scan_worktree_for_banned_scripts.py — PostToolUse backstop for
3
+ scan_bash_command_for_banned_scripts.py (#1755).
4
+
5
+ The PreToolUse scan only intercepts a *Bash command string*; a banned
6
+ `.sh`/`.bash`/`.bat`/`.cmd`/`.ps1` file written any other way (the `Write` or
7
+ `Edit` tool, or a `Bash` command whose target the PreToolUse heuristic
8
+ missed) still lands under plugins/dev-team/. This hook is the deterministic
9
+ backstop: it runs after EVERY tool call (`*` matcher) and inspects the
10
+ actual working tree via `git status --porcelain`, so it catches a banned
11
+ file regardless of which tool created it.
12
+
13
+ The tool call that wrote the file has already completed by the time this
14
+ hook runs — exit 2 cannot undo that. What it does instead is what
15
+ `docs/python-hook-contract.md`'s block contract is for: it puts the offense
16
+ directly in front of the agent (stdout AND stderr) so the very next thing it
17
+ does is remove or convert the file, rather than the violation surviving
18
+ silently to code review or CI.
19
+
20
+ Contract (docs/python-hook-contract.md):
21
+ Input : PostToolUse JSON on stdin (matcher "*" — any tool_name)
22
+ Output: exit 0 = clean; exit 2 = block (message on stdout AND stderr)
23
+
24
+ Stdlib-only. See ADR 0014/0015.
25
+ """
26
+
27
+ from __future__ import annotations
28
+
29
+ import subprocess
30
+ import sys
31
+ from pathlib import Path
32
+
33
+ _SCRIPT_DIR = Path(__file__).resolve().parent
34
+ _LIB_DIR = _SCRIPT_DIR / "lib"
35
+ if str(_LIB_DIR) not in sys.path:
36
+ sys.path.insert(0, str(_LIB_DIR))
37
+
38
+ from artifact_paths import project_root
39
+ from banned_scripts_policy import (
40
+ ALLOWED_RELATIVE_PATHS,
41
+ BANNED_EXTENSIONS,
42
+ SCOPED_PREFIX,
43
+ looks_like_monorepo_checkout,
44
+ )
45
+ from stdin_json import read_stdin_json
46
+
47
+ _GIT_TIMEOUT_S = 10
48
+
49
+ # Cap on files walked inside a single untracked directory (#1879). A
50
+ # wholly-new directory collapses to one `?? some/new/dir/` porcelain line —
51
+ # it must be recursed into on disk to see individual files — but an
52
+ # unbounded walk could hang on a pathological huge directory. This mirrors
53
+ # _GIT_TIMEOUT_S's intent (never let this scan hang indefinitely) with a
54
+ # count bound instead of a wall-clock one, since `Path.rglob` has no native
55
+ # timeout.
56
+ _MAX_UNTRACKED_DIR_ENTRIES = 5000
57
+
58
+
59
+ def _porcelain_paths(root: Path) -> list[str]:
60
+ """Every path `git status --porcelain` reports under
61
+ `plugins/dev-team/`, tracked or untracked. Fails open (returns `[]`) on
62
+ any git failure — no repo, git missing, timeout — matching every other
63
+ advisory hook's contract: a scan that can't run must never block.
64
+
65
+ An entry whose path no longer exists on disk is skipped — checked by
66
+ actually stat-ing it, not by inferring "deleted" from a `D` in the
67
+ status code, which is wrong for an unresolved-merge conflict code
68
+ (`DU`/`UD`) where the path IS still present in the working tree. The
69
+ "old -> new" rename/copy arrow is only stripped when the status code
70
+ actually reports a rename or copy (`R`/`C`), not on every entry that
71
+ happens to contain the literal substring "-> ".
72
+ """
73
+ try:
74
+ result = subprocess.run(
75
+ [
76
+ "git",
77
+ "-c",
78
+ "core.quotePath=false",
79
+ "status",
80
+ "--porcelain",
81
+ "--",
82
+ SCOPED_PREFIX.rstrip("/"),
83
+ ],
84
+ cwd=str(root),
85
+ capture_output=True,
86
+ text=True,
87
+ check=False,
88
+ timeout=_GIT_TIMEOUT_S,
89
+ )
90
+ except (FileNotFoundError, OSError, subprocess.TimeoutExpired):
91
+ return []
92
+ if result.returncode != 0:
93
+ return []
94
+
95
+ paths = []
96
+ for line in result.stdout.splitlines():
97
+ if len(line) < 3:
98
+ continue
99
+ status = line[:2]
100
+ entry = line[3:]
101
+ if status[0] in ("R", "C") and "-> " in entry:
102
+ entry = entry.split("-> ", 1)[1]
103
+ entry = entry.strip().strip('"')
104
+ if not entry:
105
+ continue
106
+ # Test the actual runtime property ("is this path still present on
107
+ # disk") rather than inferring it from the status code: a plain `D`
108
+ # skip is wrong for an unresolved-merge conflict code (`DU`/`UD`),
109
+ # where the path IS present in the working tree despite carrying a
110
+ # `D` character.
111
+ if not (root / entry).exists():
112
+ continue
113
+ paths.append(entry)
114
+ return paths
115
+
116
+
117
+ def _files_in_untracked_dir(root: Path, rel_dir: str) -> tuple[list[str], bool]:
118
+ """Recurse into a wholly-new untracked directory (a porcelain entry
119
+ ending in `/`), returning banned-extension files found inside, relative
120
+ to `root` in POSIX form. Bounded by `_MAX_UNTRACKED_DIR_ENTRIES` and
121
+ fails open (returns whatever was found so far) on any OS error.
122
+
123
+ Returns `(hits, truncated)` — `truncated` is `True` when the walk hit
124
+ `_MAX_UNTRACKED_DIR_ENTRIES` before exhausting the directory (#1904 item
125
+ 8): a directory with more entries than the cap can hide a banned script
126
+ behind it, so the caller must be able to tell an exhaustive scan apart
127
+ from a capped one instead of treating both as equally complete.
128
+
129
+ Relativizes via the known `root`/`rel_dir` prefix, not `path.resolve()`
130
+ — resolving would follow a symlink named e.g. `evil.sh` to its target,
131
+ which can land outside `root` and raise `ValueError`, silently dropping
132
+ the entry. That's weaker than the sibling non-directory branch in
133
+ `_porcelain_paths`, which flags such a link (it never resolves at all);
134
+ this walk must not be the one gap in an otherwise-consistent scan.
135
+ """
136
+ hits: list[str] = []
137
+ truncated = False
138
+ try:
139
+ for count, path in enumerate((root / rel_dir).rglob("*")):
140
+ if count >= _MAX_UNTRACKED_DIR_ENTRIES:
141
+ truncated = True
142
+ break
143
+ if not (path.is_file() or path.is_symlink()):
144
+ continue
145
+ if not path.name.lower().endswith(BANNED_EXTENSIONS):
146
+ continue
147
+ try:
148
+ rel = path.relative_to(root).as_posix()
149
+ except ValueError:
150
+ continue
151
+ hits.append(rel)
152
+ except OSError:
153
+ return hits, truncated
154
+ return hits, truncated
155
+
156
+
157
+ def find_banned_files(root: Path) -> tuple[list[str], list[str]]:
158
+ """Returns `(hits, truncated_dirs)`. `truncated_dirs` lists every
159
+ untracked directory (relative to `root`, POSIX form) whose recursive
160
+ walk hit `_MAX_UNTRACKED_DIR_ENTRIES` before finishing — a non-empty
161
+ list means `hits` may be incomplete for reasons other than "no banned
162
+ scripts exist" (#1904 item 8)."""
163
+ hits = set()
164
+ truncated_dirs: list[str] = []
165
+ for rel in _porcelain_paths(root):
166
+ if rel.endswith("/"):
167
+ # A brand-new untracked directory — expand it on disk (#1879)
168
+ # rather than checking the directory path string itself.
169
+ if not rel.startswith(SCOPED_PREFIX):
170
+ continue
171
+ found, truncated = _files_in_untracked_dir(root, rel)
172
+ if truncated:
173
+ truncated_dirs.append(rel)
174
+ for f in found:
175
+ if f in ALLOWED_RELATIVE_PATHS:
176
+ continue
177
+ hits.add(f)
178
+ continue
179
+ if not rel.startswith(SCOPED_PREFIX) or rel in ALLOWED_RELATIVE_PATHS:
180
+ continue
181
+ if rel.lower().endswith(BANNED_EXTENSIONS):
182
+ hits.add(rel)
183
+ return sorted(hits), sorted(truncated_dirs)
184
+
185
+
186
+ def main() -> int:
187
+ payload = read_stdin_json() or {}
188
+ cwd = Path(payload.get("cwd") or ".")
189
+
190
+ # (#1861) Cheap, non-git early exit for any checkout where this
191
+ # monorepo's own plugins/dev-team/ tree doesn't exist — e.g. every
192
+ # downstream install of the plugin. This hook runs on the PostToolUse
193
+ # `*` matcher (every tool call, forever), so it must not shell out to
194
+ # `git rev-parse`/`git status` at all when there is nothing under
195
+ # plugins/dev-team/ to scan — checked via a plain filesystem walk
196
+ # BEFORE project_root() (which itself shells out to git), not after.
197
+ if not looks_like_monorepo_checkout(cwd):
198
+ return 0
199
+
200
+ root = project_root(cwd)
201
+ hits, truncated_dirs = find_banned_files(root)
202
+
203
+ truncation_note = None
204
+ if truncated_dirs:
205
+ truncation_note = (
206
+ "[NOTE] scan of "
207
+ + ", ".join(truncated_dirs)
208
+ + f" stopped after {_MAX_UNTRACKED_DIR_ENTRIES} entries; "
209
+ "result may be incomplete."
210
+ )
211
+
212
+ if not hits:
213
+ # (#1904 item 8) A truncated scan found nothing WITHIN the entries
214
+ # it walked, but that is not the same claim as "no banned scripts
215
+ # exist" — surface the note even though this path still fails open
216
+ # (exit 0), matching this hook's fail-open contract.
217
+ if truncation_note:
218
+ sys.stdout.write(truncation_note + "\n")
219
+ sys.stderr.write(truncation_note + "\n")
220
+ return 0
221
+
222
+ message = (
223
+ "[BLOCK] Shell script(s) present under plugins/dev-team/ — every "
224
+ "shipped script there must be Python 3.10+ stdlib-only (ADR "
225
+ "0014/0015; repo CLAUDE.md § Script authoring): " + ", ".join(hits) + ". "
226
+ "Remove or convert to .py, or confirm this is genuinely one of the "
227
+ "two documented bootstrap-shim exceptions "
228
+ "(plugins/dev-team/install.sh, plugins/dev-team/hooks/py.sh)."
229
+ )
230
+ if truncation_note:
231
+ message += " " + truncation_note
232
+ sys.stdout.write(message + "\n")
233
+ sys.stderr.write(message + "\n")
234
+ return 2
235
+
236
+
237
+ if __name__ == "__main__":
238
+ sys.exit(main())