@danmoisan/drm-copilot-mcp 1.1.18 → 1.1.20

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 (264) hide show
  1. package/out/mcp-server.js +1149 -661
  2. package/package.json +3 -2
  3. package/resources/claude-customizations/.claude/agents/atomic-executor.md +28 -6
  4. package/resources/claude-customizations/.claude/agents/atomic-planner.md +20 -2
  5. package/resources/claude-customizations/.claude/agents/epic-orchestrator.md +17 -4
  6. package/resources/claude-customizations/.claude/agents/epic-planner.md +5 -4
  7. package/resources/claude-customizations/.claude/agents/feature-review.md +6 -4
  8. package/resources/claude-customizations/.claude/agents/orchestrator.md +9 -5
  9. package/resources/claude-customizations/.claude/agents/parallel-orchestrator.md +35 -13
  10. package/resources/claude-customizations/.claude/agents/parallel-planner.md +9 -6
  11. package/resources/claude-customizations/.claude/agents/pr-author.md +15 -2
  12. package/resources/claude-customizations/.claude/agents/prd-feature.md +1 -1
  13. package/resources/claude-customizations/.claude/agents/task-researcher.md +8 -0
  14. package/resources/claude-customizations/.claude/hooks/check-powershell-test-purity.ps1 +7 -1
  15. package/resources/claude-customizations/.claude/hooks/check-python-test-purity.ps1 +7 -1
  16. package/resources/claude-customizations/.claude/hooks/enforce-checkpoint-monotonic.ps1 +7 -1
  17. package/resources/claude-customizations/.claude/hooks/enforce-completion-consistency.ps1 +111 -46
  18. package/resources/claude-customizations/.claude/hooks/enforce-completion-helpers.ps1 +105 -0
  19. package/resources/claude-customizations/.claude/hooks/enforce-discovery-artifact-gate.ps1 +8 -1
  20. package/resources/claude-customizations/.claude/hooks/enforce-epic-invocation-origin.ps1 +7 -1
  21. package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate-command.ps1 +190 -0
  22. package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate-resolution.ps1 +116 -59
  23. package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate.ps1 +78 -77
  24. package/resources/claude-customizations/.claude/hooks/enforce-epic-wave-barrier.ps1 +74 -75
  25. package/resources/claude-customizations/.claude/hooks/enforce-epic-worktree-removal-gate-resolution.ps1 +127 -35
  26. package/resources/claude-customizations/.claude/hooks/enforce-epic-worktree-removal-gate.ps1 +61 -57
  27. package/resources/claude-customizations/.claude/hooks/enforce-evidence-locations.ps1 +7 -1
  28. package/resources/claude-customizations/.claude/hooks/enforce-feature-folder-order.ps1 +129 -39
  29. package/resources/claude-customizations/.claude/hooks/enforce-mermaid-validation.ps1 +7 -1
  30. package/resources/claude-customizations/.claude/hooks/enforce-model-routing-receipt.ps1 +9 -3
  31. package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-epic-scope.ps1 +39 -51
  32. package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-helpers.ps1 +31 -58
  33. package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-modes.ps1 +63 -47
  34. package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-targets.ps1 +433 -0
  35. package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate.ps1 +17 -12
  36. package/resources/claude-customizations/.claude/hooks/enforce-parallel-abandon-gate.ps1 +9 -3
  37. package/resources/claude-customizations/.claude/hooks/enforce-parallel-cohort-barrier.ps1 +65 -50
  38. package/resources/claude-customizations/.claude/hooks/enforce-parallel-drift-gate.ps1 +49 -42
  39. package/resources/claude-customizations/.claude/hooks/enforce-parallel-worktree-removal-gate.ps1 +54 -72
  40. package/resources/claude-customizations/.claude/hooks/enforce-powershell-batch-budget.ps1 +8 -2
  41. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-command-allowlist.ps1 +279 -0
  42. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill-helpers.ps1 +129 -75
  43. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.artifact-root.ps1 +233 -0
  44. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.docs-only.ps1 +224 -0
  45. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.ps1 +69 -24
  46. package/resources/claude-customizations/.claude/hooks/enforce-prd-feature-before-planner-helpers.ps1 +29 -18
  47. package/resources/claude-customizations/.claude/hooks/enforce-prd-feature-before-planner.ps1 +10 -4
  48. package/resources/claude-customizations/.claude/hooks/enforce-promotion-mcp-only.ps1 +14 -5
  49. package/resources/claude-customizations/.claude/hooks/enforce-python-batch-budget.ps1 +8 -2
  50. package/resources/claude-customizations/.claude/hooks/feature-folder-resolution.ps1 +373 -0
  51. package/resources/claude-customizations/.claude/hooks/feature-review-coverage-thresholds.ps1 +85 -0
  52. package/resources/claude-customizations/.claude/hooks/hook-command-heredoc.ps1 +119 -0
  53. package/resources/claude-customizations/.claude/hooks/hook-command-invocation-operands.ps1 +198 -0
  54. package/resources/claude-customizations/.claude/hooks/hook-command-invocation.ps1 +254 -255
  55. package/resources/claude-customizations/.claude/hooks/hook-command-payload-powershell.ps1 +201 -0
  56. package/resources/claude-customizations/.claude/hooks/hook-command-payload.ps1 +484 -0
  57. package/resources/claude-customizations/.claude/hooks/hook-command-scanner.ps1 +27 -110
  58. package/resources/claude-customizations/.claude/hooks/hook-dependency-guard.ps1 +114 -0
  59. package/resources/claude-customizations/.claude/hooks/hook-docs-only-diff.ps1 +497 -0
  60. package/resources/claude-customizations/.claude/hooks/premortem-section.ps1 +149 -0
  61. package/resources/claude-customizations/.claude/hooks/subagentstop-envelope.ps1 +192 -0
  62. package/resources/claude-customizations/.claude/hooks/validate-bash.ps1 +9 -3
  63. package/resources/claude-customizations/.claude/hooks/validate-discovery-artifact-gate.ps1 +40 -42
  64. package/resources/claude-customizations/.claude/hooks/validate-executor-output.ps1 +267 -113
  65. package/resources/claude-customizations/.claude/hooks/validate-feature-review-coverage-metrics.ps1 +180 -0
  66. package/resources/claude-customizations/.claude/hooks/validate-feature-review-coverage-rules.ps1 +195 -0
  67. package/resources/claude-customizations/.claude/hooks/validate-feature-review-coverage.ps1 +141 -395
  68. package/resources/claude-customizations/.claude/hooks/validate-orchestrator-output-resolution.ps1 +311 -0
  69. package/resources/claude-customizations/.claude/hooks/validate-orchestrator-output-routing.ps1 +170 -0
  70. package/resources/claude-customizations/.claude/hooks/validate-orchestrator-output.ps1 +115 -190
  71. package/resources/claude-customizations/.claude/hooks/validate-planner-output.ps1 +77 -33
  72. package/resources/claude-customizations/.claude/hooks/validate-pr-author-output.ps1 +39 -44
  73. package/resources/claude-customizations/.claude/hooks/validate-prd-feature-output.ps1 +43 -14
  74. package/resources/claude-customizations/.claude/hooks/validate-required-artifact-output.ps1 +66 -36
  75. package/resources/claude-customizations/.claude/hooks/validate-task-researcher-output.ps1 +52 -36
  76. package/resources/claude-customizations/.claude/lib/bash/mutate-parallel-run.sh +191 -0
  77. package/resources/claude-customizations/.claude/lib/bash/parallel-cohorts.sh +13 -3
  78. package/resources/claude-customizations/.claude/lib/bash/parallel-mutation-cli.sh +152 -0
  79. package/resources/claude-customizations/.claude/lib/bash/parallel-mutation-ops.sh +193 -0
  80. package/resources/claude-customizations/.claude/lib/bash/parallel-mutation.sh +322 -0
  81. package/resources/claude-customizations/.claude/lib/bash/remove-parallel-item.sh +146 -0
  82. package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusExtraction.psm1 +23 -33
  83. package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusTokenShape.psm1 +86 -6
  84. package/resources/claude-customizations/.claude/lib/ci-gate/Invoke-CiGateParser.ps1 +91 -19
  85. package/resources/claude-customizations/.claude/lib/ci-gate/Invoke-MergeConfirmationParser.ps1 +203 -0
  86. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateEpicWaveBarrier.psm1 +346 -0
  87. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateIssueAdoption.psm1 +24 -3
  88. package/resources/claude-customizations/.claude/lib/worktree-resolution/WorktreeItemResolution.psm1 +79 -2
  89. package/resources/claude-customizations/.claude/lib/worktree-resolution/WorktreeRunResolution.psm1 +3 -3
  90. package/resources/claude-customizations/.claude/rules/architecture-boundaries.md +5 -5
  91. package/resources/claude-customizations/.claude/rules/csharp.md +2 -2
  92. package/resources/claude-customizations/.claude/rules/general-code-change.md +1 -1
  93. package/resources/claude-customizations/.claude/rules/general-unit-test.md +3 -2
  94. package/resources/claude-customizations/.claude/rules/orchestrator-state.md +37 -4
  95. package/resources/claude-customizations/.claude/rules/parallel-orchestration.md +50 -5
  96. package/resources/claude-customizations/.claude/rules/plan-acceptance-gates.md +85 -10
  97. package/resources/claude-customizations/.claude/rules/quality-tiers.md +11 -7
  98. package/resources/claude-customizations/.claude/rules/shell.md +10 -8
  99. package/resources/claude-customizations/.claude/rules/typescript.md +1 -1
  100. package/resources/claude-customizations/.claude/settings.json +3 -4
  101. package/resources/claude-customizations/.claude/skills/acceptance-criteria-tracking/SKILL.md +1 -1
  102. package/resources/claude-customizations/.claude/skills/atomic-plan-contract/SKILL.md +52 -16
  103. package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/SKILL.md +7 -3
  104. package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_detached_lib.sh +1 -1
  105. package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_enumerate_lib.sh +2 -0
  106. package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_scan_helper.sh +2 -0
  107. package/resources/claude-customizations/.claude/skills/csharp-change-budget-router/SKILL.md +2 -2
  108. package/resources/claude-customizations/.claude/skills/epic-orchestrate/SKILL.md +45 -9
  109. package/resources/claude-customizations/.claude/skills/epic-plan/SKILL.md +5 -3
  110. package/resources/claude-customizations/.claude/skills/evidence-and-timestamp-conventions/SKILL.md +10 -1
  111. package/resources/claude-customizations/.claude/skills/execution-environment/SKILL.md +134 -0
  112. package/resources/claude-customizations/.claude/skills/feature-promotion-lifecycle/SKILL.md +1 -1
  113. package/resources/claude-customizations/.claude/skills/feature-review-workflow/SKILL.md +4 -3
  114. package/resources/claude-customizations/.claude/skills/orchestrate/SKILL.md +36 -15
  115. package/resources/claude-customizations/.claude/skills/parallel-add/SKILL.md +52 -14
  116. package/resources/claude-customizations/.claude/skills/parallel-close/SKILL.md +22 -9
  117. package/resources/claude-customizations/.claude/skills/parallel-orchestrate/SKILL.md +108 -21
  118. package/resources/claude-customizations/.claude/skills/parallel-plan/SKILL.md +63 -7
  119. package/resources/claude-customizations/.claude/skills/parallel-remove/SKILL.md +35 -12
  120. package/resources/claude-customizations/.claude/skills/powershell-change-budget-router/SKILL.md +1 -1
  121. package/resources/claude-customizations/.claude/skills/pr-author/SKILL.md +3 -1
  122. package/resources/claude-customizations/.claude/skills/python-change-budget-router/SKILL.md +1 -1
  123. package/resources/claude-customizations/.claude/skills/quota-throttling/SKILL.md +1 -1
  124. package/resources/claude-customizations/.claude/skills/remediation-handoff-atomic-planner/SKILL.md +16 -8
  125. package/resources/claude-customizations/.claude/skills/research-issue/SKILL.md +6 -0
  126. package/resources/claude-customizations/pack-manifests/core.json +27 -0
  127. package/resources/codex-and-agents-customizations/.agents/skills/README.md +1 -0
  128. package/resources/codex-and-agents-customizations/.agents/skills/acceptance-criteria-tracking/SKILL.md +1 -1
  129. package/resources/codex-and-agents-customizations/.agents/skills/architecture-boundaries/SKILL.md +5 -5
  130. package/resources/codex-and-agents-customizations/.agents/skills/atomic-executor/SKILL.md +19 -7
  131. package/resources/codex-and-agents-customizations/.agents/skills/atomic-plan-contract/SKILL.md +43 -4
  132. package/resources/codex-and-agents-customizations/.agents/skills/atomic-planner/SKILL.md +18 -6
  133. package/resources/codex-and-agents-customizations/.agents/skills/csharp/SKILL.md +2 -2
  134. package/resources/codex-and-agents-customizations/.agents/skills/csharp-change-budget-router/SKILL.md +2 -2
  135. package/resources/codex-and-agents-customizations/.agents/skills/csharp-qa-gate/SKILL.md +2 -2
  136. package/resources/codex-and-agents-customizations/.agents/skills/epic-orchestrate/SKILL.md +16 -1
  137. package/resources/codex-and-agents-customizations/.agents/skills/epic-plan/SKILL.md +17 -1
  138. package/resources/codex-and-agents-customizations/.agents/skills/epic-run/SKILL.md +2 -1
  139. package/resources/codex-and-agents-customizations/.agents/skills/evidence-and-timestamp-conventions/SKILL.md +10 -1
  140. package/resources/codex-and-agents-customizations/.agents/skills/execution-environment/SKILL.md +134 -0
  141. package/resources/codex-and-agents-customizations/.agents/skills/feature-review-workflow/SKILL.md +13 -5
  142. package/resources/codex-and-agents-customizations/.agents/skills/general-code-change/SKILL.md +1 -1
  143. package/resources/codex-and-agents-customizations/.agents/skills/general-unit-test/SKILL.md +3 -2
  144. package/resources/codex-and-agents-customizations/.agents/skills/orchestrate/SKILL.md +27 -8
  145. package/resources/codex-and-agents-customizations/.agents/skills/orchestrator-workflow/SKILL.md +1 -1
  146. package/resources/codex-and-agents-customizations/.agents/skills/powershell-change-budget-router/SKILL.md +1 -1
  147. package/resources/codex-and-agents-customizations/.agents/skills/python-change-budget-router/SKILL.md +1 -1
  148. package/resources/codex-and-agents-customizations/.agents/skills/quality-tiers/SKILL.md +11 -7
  149. package/resources/codex-and-agents-customizations/.agents/skills/remediation-handoff-atomic-planner/SKILL.md +7 -3
  150. package/resources/codex-and-agents-customizations/.agents/skills/research-issue/SKILL.md +6 -0
  151. package/resources/codex-and-agents-customizations/.agents/skills/translate-claude-to-codex/SKILL.md +1 -1
  152. package/resources/codex-and-agents-customizations/.agents-variants/csharp-legacy/skills/csharp/SKILL.md +2 -2
  153. package/resources/codex-and-agents-customizations/.agents-variants/csharp-legacy/skills/csharp-qa-gate/SKILL.md +2 -2
  154. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c1.toml +21 -6
  155. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c2.toml +21 -6
  156. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c3-elevated.toml +21 -6
  157. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c3.toml +21 -6
  158. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c4.toml +21 -6
  159. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor.toml +21 -6
  160. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c1.toml +15 -2
  161. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c2.toml +15 -2
  162. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c3-elevated.toml +15 -2
  163. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c3.toml +15 -2
  164. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c4.toml +15 -2
  165. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner.toml +15 -2
  166. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planning.toml +1 -1
  167. package/resources/codex-and-agents-customizations/.codex/agents/csharp-atomic-planning.toml +1 -1
  168. package/resources/codex-and-agents-customizations/.codex/agents/epic-orchestrator.toml +4 -1
  169. package/resources/codex-and-agents-customizations/.codex/agents/feature-review.toml +6 -6
  170. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c1.toml +7 -4
  171. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c2.toml +7 -4
  172. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c3-elevated.toml +7 -4
  173. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c3.toml +7 -4
  174. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c4.toml +7 -4
  175. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator.toml +7 -4
  176. package/resources/codex-and-agents-customizations/.codex/agents/powershell-atomic-planning.toml +1 -1
  177. package/resources/codex-and-agents-customizations/.codex/agents/python-atomic-planning.toml +1 -1
  178. package/resources/codex-and-agents-customizations/.codex/agents/task-researcher-c1.toml +6 -0
  179. package/resources/codex-and-agents-customizations/.codex/agents/task-researcher-c2.toml +6 -0
  180. package/resources/codex-and-agents-customizations/.codex/agents/task-researcher-c3-elevated.toml +6 -0
  181. package/resources/codex-and-agents-customizations/.codex/agents/task-researcher-c3.toml +6 -0
  182. package/resources/codex-and-agents-customizations/.codex/agents/task-researcher-c4.toml +6 -0
  183. package/resources/codex-and-agents-customizations/.codex/agents/task-researcher.toml +6 -0
  184. package/resources/codex-and-agents-customizations/.codex/codex-web-setup.sh +318 -273
  185. package/resources/codex-and-agents-customizations/.codex/config.toml +1 -1
  186. package/resources/codex-and-agents-customizations/.codex/hooks/check-powershell-test-purity.ps1 +7 -1
  187. package/resources/codex-and-agents-customizations/.codex/hooks/check-python-test-purity.ps1 +7 -1
  188. package/resources/codex-and-agents-customizations/.codex/hooks/codex-epic-child-launch-attestation.ps1 +1 -1
  189. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-checkpoint-monotonic.ps1 +7 -1
  190. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-codex-model-routing.ps1 +8 -2
  191. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-completion-consistency.ps1 +97 -44
  192. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-completion-helpers.ps1 +105 -0
  193. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-child-worktree-binding.ps1 +7 -1
  194. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-merge-gate-command.ps1 +190 -0
  195. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-merge-gate.ps1 +54 -43
  196. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-planning-only.ps1 +6 -0
  197. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-root-invocation.ps1 +7 -1
  198. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-wave-barrier.ps1 +7 -1
  199. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-worktree-removal-gate.ps1 +54 -41
  200. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-evidence-locations.ps1 +7 -1
  201. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-epic-scope.ps1 +35 -14
  202. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-helpers.ps1 +31 -58
  203. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-modes.ps1 +63 -47
  204. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-targets.ps1 +433 -0
  205. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate.ps1 +24 -11
  206. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-powershell-batch-budget.ps1 +8 -2
  207. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-promotion-mcp-only.ps1 +13 -4
  208. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-python-batch-budget.ps1 +8 -2
  209. package/resources/codex-and-agents-customizations/.codex/hooks/feature-folder-resolution.ps1 +373 -0
  210. package/resources/codex-and-agents-customizations/.codex/hooks/feature-review-coverage-thresholds.ps1 +85 -0
  211. package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-heredoc.ps1 +119 -0
  212. package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-invocation-operands.ps1 +198 -0
  213. package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-invocation.ps1 +254 -255
  214. package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-payload-powershell.ps1 +201 -0
  215. package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-payload.ps1 +484 -0
  216. package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-scanner.ps1 +27 -110
  217. package/resources/codex-and-agents-customizations/.codex/hooks/hook-dependency-guard.ps1 +114 -0
  218. package/resources/codex-and-agents-customizations/.codex/hooks/hook-docs-only-diff.ps1 +497 -0
  219. package/resources/codex-and-agents-customizations/.codex/hooks/validate-bash.ps1 +8 -2
  220. package/resources/codex-and-agents-customizations/.codex/hooks/validate-codex-subagent-routing.ps1 +7 -1
  221. package/resources/codex-and-agents-customizations/.codex/hooks/validate-feature-review-coverage.ps1 +24 -8
  222. package/resources/codex-and-agents-customizations/.codex/prompts/feature-review-remediate.md +6 -4
  223. package/resources/codex-and-agents-customizations/pack-manifests/core.json +11 -0
  224. package/resources/customizations/.github/agents/atomic_executor.agent.md +21 -11
  225. package/resources/customizations/.github/agents/atomic_planning.agent.md +8 -3
  226. package/resources/customizations/.github/agents/csharp-atomic-executor.agent.md +13 -9
  227. package/resources/customizations/.github/agents/csharp-atomic-planning.agent.md +2 -2
  228. package/resources/customizations/.github/agents/csharp-orchestrator.agent.md +2 -2
  229. package/resources/customizations/.github/agents/csharp-typed-engineer.agent.md +5 -5
  230. package/resources/customizations/.github/agents/orchestrator.agent.md +8 -5
  231. package/resources/customizations/.github/agents/powershell-atomic-executor.agent.md +13 -9
  232. package/resources/customizations/.github/agents/powershell-atomic-planning.agent.md +2 -2
  233. package/resources/customizations/.github/agents/powershell-orchestrator.agent.md +2 -2
  234. package/resources/customizations/.github/agents/powershell-typed-engineer.agent.md +3 -3
  235. package/resources/customizations/.github/agents/python-atomic-executor.agent.md +13 -9
  236. package/resources/customizations/.github/agents/python-atomic-planning.agent.md +2 -2
  237. package/resources/customizations/.github/agents/python-orchestrator.agent.md +2 -2
  238. package/resources/customizations/.github/agents/task-researcher.agent.md +6 -0
  239. package/resources/customizations/.github/instructions/csharp-code-change.instructions.md +4 -4
  240. package/resources/customizations/.github/instructions/csharp-unit-test.instructions.md +2 -2
  241. package/resources/customizations/.github/prompts/generate-atomic-plan.prompt.md +16 -10
  242. package/resources/customizations/.github/prompts/orchestrate-csharp-work.prompt.md +2 -2
  243. package/resources/customizations/.github/prompts/orchestrate-powershell-work.prompt.md +1 -1
  244. package/resources/customizations/.github/prompts/orchestrate-python-work.prompt.md +1 -1
  245. package/resources/customizations/.github/prompts/orchestrate-work.prompt.md +2 -2
  246. package/resources/customizations/.github/skills/acceptance-criteria-tracking/SKILL.md +1 -1
  247. package/resources/customizations/.github/skills/atomic-plan-contract/SKILL.md +43 -4
  248. package/resources/customizations/.github/skills/csharp-change-budget-router/SKILL.md +1 -1
  249. package/resources/customizations/.github/skills/evidence-and-timestamp-conventions/SKILL.md +10 -1
  250. package/resources/customizations/.github/skills/execution-environment/SKILL.md +134 -0
  251. package/resources/customizations/.github/skills/feature-review-workflow/SKILL.md +1 -1
  252. package/resources/customizations/.github/skills/powershell-change-budget-router/SKILL.md +1 -1
  253. package/resources/customizations/.github/skills/remediation-handoff-atomic-planner/SKILL.md +7 -3
  254. package/resources/feature-templates/bug/plan.yyyy-MM-ddTHH-mm.md +10 -0
  255. package/resources/feature-templates/feature/plan.yyyy-MM-ddTHH-mm.md +11 -0
  256. package/resources/feature-templates/refactor/plan.yyyy-MM-ddTHH-mm.md +11 -0
  257. package/resources/powershell/PoshQC/PoshQC.Coverage.psm1 +314 -0
  258. package/resources/powershell/PoshQC/PoshQC.CoverageMerge.psm1 +163 -0
  259. package/resources/powershell/PoshQC/PoshQC.Shard.psm1 +296 -0
  260. package/resources/powershell/PoshQC/PoshQC.Testing.psm1 +48 -32
  261. package/resources/powershell/PoshQC/PoshQC.psd1 +7 -1
  262. package/resources/powershell/PoshQC/PoshQC.psm1 +18 -5
  263. package/resources/powershell/PoshQC/README.md +21 -3
  264. package/resources/powershell/PoshQC/settings/pester.runsettings.psd1 +5 -319
@@ -8,17 +8,29 @@
8
8
  under one of the two tracked research roots
9
9
  (docs/features/<feature>/research/ for feature-associated research, or
10
10
  docs/research/ for one-off research), matches the documented filename
11
- convention, and the file exists on disk.
11
+ convention, and the file exists on disk. Feature-associated research under
12
+ docs/features/ must also carry a complete `## Pre-Mortem` section
13
+ (Test-PreMortemSection in premortem-section.ps1, issue #964); one-off
14
+ research under docs/research/ is exempt.
12
15
 
13
16
  .NOTES
14
- Reads the hook payload from CLAUDE_HOOK_INPUT as JSON. Exits 0 to allow
15
- termination; exits 1 with an error message to block. Filesystem reads go
16
- through Test-ResearchFile so tests can mock the boundary without writing
17
- temporary files.
17
+ Reads the SubagentStop envelope JSON from stdin through the shared
18
+ envelope helper (subagentstop-envelope.ps1) and validates its
19
+ last_assistant_message field, the researcher's last assistant message.
20
+ Exits 0 to allow termination. A block writes the reason to stderr and
21
+ exits 2 through the shared envelope helper; an invalid envelope blocks
22
+ with a payload anomaly reason. A hook dependency that fails to load
23
+ blocks with exit 2 and the validate-task-researcher-output: prefix.
24
+ Filesystem reads go through Test-ResearchFile and Get-ResearchFileContent
25
+ so tests can mock the boundary without writing temporary files.
26
+ An unhandled error during validation blocks with exit 2 and a reason (issue #928).
18
27
  #>
19
28
 
20
29
  [CmdletBinding()]
21
30
  param()
31
+ $script:HookDependencyGuardLoadFailed = $false; try { . (Join-Path $PSScriptRoot 'hook-dependency-guard.ps1') } catch { $script:HookDependencyGuardLoadFailed = $true }
32
+ try { . (Join-Path $PSScriptRoot 'subagentstop-envelope.ps1') } catch { Add-HookDependencyFailure -Name 'subagentstop-envelope.ps1' -ErrorRecord $_ }
33
+ try { . (Join-Path $PSScriptRoot 'premortem-section.ps1') } catch { Add-HookDependencyFailure -Name 'premortem-section.ps1' -ErrorRecord $_ }
22
34
 
23
35
  Set-StrictMode -Version Latest
24
36
  $ErrorActionPreference = 'Stop'
@@ -34,6 +46,16 @@ function Test-ResearchFile {
34
46
  return [bool](Test-Path -LiteralPath $Path -PathType Leaf)
35
47
  }
36
48
 
49
+ function Get-ResearchFileContent {
50
+ # File seam: returns the research file text, or $null when no file exists at the path.
51
+ [CmdletBinding()]
52
+ [OutputType([string])]
53
+ param([Parameter(Mandatory = $true)] [string] $Path)
54
+
55
+ if (-not (Test-Path -LiteralPath $Path -PathType Leaf)) { return $null }
56
+ return Get-Content -LiteralPath $Path -Raw -ErrorAction Stop
57
+ }
58
+
37
59
  function Get-ResearchPathFromOutput {
38
60
  [CmdletBinding()]
39
61
  [OutputType([string])]
@@ -94,7 +116,7 @@ function Test-IsValidResearchFileName {
94
116
  $fileName = [System.IO.Path]::GetFileName($normalized)
95
117
  return [regex]::IsMatch(
96
118
  $fileName,
97
- '^\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-[A-Za-z0-9][A-Za-z0-9-]*-research\.md$'
119
+ '^(?:(?:\d{4}-\d{2}-\d{2}(?:T\d{2}-?\d{2})?|\d{8})-[A-Za-z0-9][A-Za-z0-9-]*[-.]research|research(?:-addendum)?\.\d{4}-\d{2}-\d{2}(?:T\d{2}-\d{2})?)\.md$'
98
120
  )
99
121
  }
100
122
 
@@ -192,7 +214,7 @@ function Test-NumericDerivationEvidence {
192
214
  if (-not $match.Success) { return @{ Ok = $false; Message = "task-researcher hook: numeric derivation evidence is missing $label." } }
193
215
  $values[$label] = $match.Groups['value'].Value.Trim()
194
216
  }
195
- if ($values['Exhaustive Search Scope'] -notmatch '(?i)\b(entire|all|complete)\b.*\b(repository|repo|source tree|tree)\b') {
217
+ if ($values['Exhaustive Search Scope'] -notmatch '(?i)\b(entire|whole|all|complete)\b.*\b(repository|repo|worktree|source tree|tree)\b') {
196
218
  return @{ Ok = $false; Message = 'task-researcher hook: numeric derivation evidence does not declare an exhaustive repository search scope.' }
197
219
  }
198
220
  $primaryStrategy = $values['Primary Search Strategy or Query Expression']
@@ -227,29 +249,18 @@ function Invoke-TaskResearcherOutputValidation {
227
249
  [CmdletBinding()]
228
250
  [OutputType([hashtable])]
229
251
  param(
230
- [string] $RawPayload
252
+ [AllowNull()] [AllowEmptyString()] [string] $InputText
231
253
  )
232
-
233
- if ([string]::IsNullOrWhiteSpace($RawPayload)) {
234
- return @{ Ok = $false; Message = 'task-researcher hook: CLAUDE_HOOK_INPUT is empty; cannot validate task-researcher output.' }
235
- }
236
-
237
- try {
238
- $payload = $RawPayload | ConvertFrom-Json -ErrorAction Stop
239
- }
240
- catch {
241
- return @{ Ok = $false; Message = "task-researcher hook: failed to parse CLAUDE_HOOK_INPUT as JSON: $($_.Exception.Message)" }
242
- }
243
-
244
- $agentOutput = $null
245
- if ($payload.PSObject.Properties.Name -contains 'output') {
246
- $agentOutput = $payload.output
247
- }
248
- if ([string]::IsNullOrWhiteSpace($agentOutput)) {
254
+ $dependencyDecision = Get-HookDependencyFailureDecision -HookEvent SubagentStop -ReasonPrefix 'validate-task-researcher-output:'
255
+ if ($null -ne $dependencyDecision) { return $dependencyDecision }
256
+ $envelope = if ($PSBoundParameters.ContainsKey('InputText')) { Read-SubagentStopEnvelope -InputText $InputText } else { Read-SubagentStopEnvelope }
257
+ if (-not $envelope.IsValid) { return @{ Ok = $false; Message = "task-researcher hook: payload anomaly - $(Get-SubagentStopEnvelopeAnomalyReason -Anomaly $envelope.Anomaly)" } }
258
+ $assistantMessage = [string]$envelope.AssistantText
259
+ if ([string]::IsNullOrWhiteSpace($assistantMessage)) {
249
260
  return @{ Ok = $false; Message = 'task-researcher hook: agent output is empty; researcher must return research-path before termination.' }
250
261
  }
251
262
 
252
- $researchPath = Get-ResearchPathFromOutput -AgentOutput $agentOutput
263
+ $researchPath = Get-ResearchPathFromOutput -AgentOutput $assistantMessage
253
264
  if ($null -eq $researchPath) {
254
265
  return @{ Ok = $false; Message = 'task-researcher hook: agent output does not advertise a research-path. Researcher must report `research-path: <path>` pointing to docs/features/<feature>/research/ (feature-associated) or docs/research/ (one-off).' }
255
266
  }
@@ -266,16 +277,22 @@ function Invoke-TaskResearcherOutputValidation {
266
277
  return @{ Ok = $false; Message = "task-researcher hook: researcher advertised research-path '$researchPath' but no file exists at that location." }
267
278
  }
268
279
 
269
- $feasibilityResult = Test-AutomationFeasibilitySection -ResearchFilePath $researchPath -AgentOutput $agentOutput
280
+ $feasibilityResult = Test-AutomationFeasibilitySection -ResearchFilePath $researchPath -AgentOutput $assistantMessage
270
281
  if (-not $feasibilityResult.Ok) {
271
282
  return @{ Ok = $false; Message = $feasibilityResult.Message }
272
283
  }
273
284
 
274
- if (Test-Path -LiteralPath $researchPath -PathType Leaf) {
275
- $numericEvidenceResult = Test-NumericDerivationEvidence -Content (Get-Content -LiteralPath $researchPath -Raw -ErrorAction Stop)
285
+ try { $researchContent = Get-ResearchFileContent -Path $researchPath } catch { return @{ Ok = $false; Message = "task-researcher hook: failed to read research-path '$researchPath': $($_.Exception.Message)" } }
286
+ if ($null -ne $researchContent) {
287
+ $numericEvidenceResult = Test-NumericDerivationEvidence -Content $researchContent
276
288
  if (-not $numericEvidenceResult.Ok) {
277
289
  return $numericEvidenceResult
278
290
  }
291
+ # Issue #964: feature research carries a Pre-Mortem; one-off research under docs/research/ is exempt.
292
+ if ((($researchPath -replace '\\', '/') -replace '^\./', '').StartsWith('docs/features/', [System.StringComparison]::OrdinalIgnoreCase)) {
293
+ $preMortem = Test-PreMortemSection -Content ([string]$researchContent)
294
+ if (-not $preMortem.Ok) { return @{ Ok = $false; Message = "task-researcher hook: feature research '$researchPath' $($preMortem.Message)" } }
295
+ }
279
296
  }
280
297
 
281
298
  return @{ Ok = $true; Message = $null }
@@ -284,11 +301,10 @@ function Invoke-TaskResearcherOutputValidation {
284
301
  if ($MyInvocation.InvocationName -eq '.') {
285
302
  return
286
303
  }
287
-
288
- $result = Invoke-TaskResearcherOutputValidation -RawPayload $env:CLAUDE_HOOK_INPUT
289
- if (-not $result.Ok) {
290
- Write-Error $result.Message
291
- exit 1
292
- }
293
-
304
+ if ($script:HookDependencyGuardLoadFailed) { [Console]::Error.WriteLine('validate-task-researcher-output: hook-dependency-guard.ps1 failed to load; the gate fails closed.'); exit 2 }
305
+ $dependencyDecision = Get-HookDependencyFailureDecision -HookEvent SubagentStop -ReasonPrefix 'validate-task-researcher-output:'
306
+ if ($null -ne $dependencyDecision) { [Console]::Error.WriteLine($dependencyDecision.Reason); exit $dependencyDecision.ExitCode }
307
+ try { $result = Invoke-TaskResearcherOutputValidation }
308
+ catch { Exit-SubagentStopBlock -Message ('validate-task-researcher-output: validator error: ' + ([string]$_.Exception.Message -split "`r?`n")[0] + '; the gate fails closed.' + "`n" + 'This condition is not repairable by the subagent. Report it to the operator and halt.') }
309
+ if (-not $result.Ok) { Exit-SubagentStopBlock -Message $result.Message }
294
310
  exit 0
@@ -0,0 +1,191 @@
1
+ #!/usr/bin/env bash
2
+ # mutate-parallel-run.sh: destination-portable command-line entry point for the
3
+ # decision and audit-record steps of the parallel-add, parallel-close, and
4
+ # parallel-orchestrate skills (issue #890). It exists so a workspace that
5
+ # received the Claude customization payload can run those steps with nothing
6
+ # but bash -- no interpreter, no repository checkout.
7
+ #
8
+ # Usage:
9
+ # bash .claude/lib/bash/mutate-parallel-run.sh admit --candidate <key> \
10
+ # [--edges "<a>:<b> ..."] --in-flight "<k> ..." \
11
+ # --current-cohort-members "<k> ..."
12
+ # bash .claude/lib/bash/mutate-parallel-run.sh recolor --unstarted "<k> ..." \
13
+ # [--edges "<a>:<b> ..."] --pinned "<k> ..." --generation <g> \
14
+ # --current-cohort <c> --highest-pinned-cohort <h>
15
+ # bash .claude/lib/bash/mutate-parallel-run.sh add-entry --item <key> \
16
+ # --deferred true|false --generation <g> [--at <timestamp>]
17
+ # bash .claude/lib/bash/mutate-parallel-run.sh close --items "<k>:<state> ..."
18
+ # bash .claude/lib/bash/mutate-parallel-run.sh close-entry --generation <g> \
19
+ # [--at <timestamp>]
20
+ # bash .claude/lib/bash/mutate-parallel-run.sh requeue-entry --item <key> \
21
+ # --generation <g> [--at <timestamp>]
22
+ #
23
+ # `--edges` is optional; `--in-flight`, `--current-cohort-members`,
24
+ # `--unstarted`, `--pinned`, and `--items` accept an empty string. Integer
25
+ # tokens match `-?(0|[1-9][0-9]*)`. An `--items` token is an integer, a colon,
26
+ # and a `[A-Za-z_]+` state; a duplicate key is a usage error. `--at` defaults to
27
+ # the current UTC time as yyyy-MM-ddTHH-mm; the clock is read only when `--at`
28
+ # is omitted.
29
+ #
30
+ # Exit-code contract (identical to remove-parallel-item.sh):
31
+ # exit 0 success; one compact JSON object on stdout
32
+ # exit 1 rule rejection; the reference implementation's exact message as
33
+ # one stderr line
34
+ # exit 2 usage error; one stderr line beginning
35
+ # "PARALLEL_MUTATION_ERROR: usage error:"
36
+ # Argument validation runs first, then rule rejection, then output.
37
+ #
38
+ # Parity reference: scripts/dev_tools/parallel_mutation_protocol.py
39
+ # (decide_admission, recolor_unstarted, decide_close) and
40
+ # scripts/dev_tools/_parallel_mutation_entries.py (build_add_entry,
41
+ # build_close_entry, build_requeue_entry).
42
+ #
43
+ # Declared divergences (also listed in both parity lanes):
44
+ # 1. A duplicate key in `close --items` is a usage error (exit 2); the Python
45
+ # mapping would silently keep the last record for that key.
46
+ # 2. `at` is a caller-supplied string instead of a `datetime`.
47
+ # 3. Option abbreviations are rejected.
48
+ # 4. `admit` has no rejection branch, in either implementation.
49
+ set -euo pipefail
50
+
51
+ # Resolve this script's own directory so the libraries source regardless of cwd.
52
+ MPR_SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)
53
+ # shellcheck source=.claude/lib/bash/parallel-mutation-ops.sh
54
+ # shellcheck disable=SC1091
55
+ source "$MPR_SCRIPT_DIR/parallel-mutation-ops.sh"
56
+ # shellcheck source=.claude/lib/bash/parallel-mutation-cli.sh
57
+ # shellcheck disable=SC1091
58
+ source "$MPR_SCRIPT_DIR/parallel-mutation-cli.sh"
59
+
60
+ pc_enforce_c_locale
61
+
62
+ mpr_admit() {
63
+ # Run the admit subcommand.
64
+ local candidate edges in_flight members rc=0
65
+ pmcli_parse_options "candidate edges in-flight current-cohort-members" "$@"
66
+ pmcli_require candidate in-flight current-cohort-members
67
+ pmcli_integer "${PMCLI_OPTS[candidate]}" "--candidate"
68
+ candidate="$PMCLI_VALUE"
69
+ pmcli_edge_list "${PMCLI_OPTS[edges]-}"
70
+ edges="$PMCLI_VALUE"
71
+ pmcli_integer_list "${PMCLI_OPTS["in-flight"]}" "--in-flight"
72
+ in_flight="$PMCLI_VALUE"
73
+ pmcli_integer_list "${PMCLI_OPTS["current-cohort-members"]}" "--current-cohort-members"
74
+ members="$PMCLI_VALUE"
75
+ pm_decide_admission "$candidate" "$edges" "$in_flight" "$members" || rc=$?
76
+ pmcli_emit "$rc"
77
+ }
78
+
79
+ mpr_recolor() {
80
+ # Run the recolor subcommand; same options as remove-parallel-item.sh.
81
+ local unstarted edges pinned generation current highest rc=0
82
+ pmcli_parse_options "unstarted edges pinned generation current-cohort highest-pinned-cohort" "$@"
83
+ pmcli_require unstarted pinned generation current-cohort highest-pinned-cohort
84
+ pmcli_integer_list "${PMCLI_OPTS[unstarted]}" "--unstarted"
85
+ unstarted="$PMCLI_VALUE"
86
+ pmcli_edge_list "${PMCLI_OPTS[edges]-}"
87
+ edges="$PMCLI_VALUE"
88
+ pmcli_integer_list "${PMCLI_OPTS[pinned]}" "--pinned"
89
+ pinned="$PMCLI_VALUE"
90
+ pmcli_integer "${PMCLI_OPTS[generation]}" "--generation"
91
+ generation="$PMCLI_VALUE"
92
+ pmcli_integer "${PMCLI_OPTS["current-cohort"]}" "--current-cohort"
93
+ current="$PMCLI_VALUE"
94
+ pmcli_integer "${PMCLI_OPTS["highest-pinned-cohort"]}" "--highest-pinned-cohort"
95
+ highest="$PMCLI_VALUE"
96
+ pm_recolor_unstarted "$unstarted" "$edges" "$pinned" "$generation" "$current" "$highest" || rc=$?
97
+ pmcli_emit "$rc"
98
+ }
99
+
100
+ mpr_add_entry() {
101
+ # Run the add-entry subcommand.
102
+ local item deferred generation at rc=0
103
+ pmcli_parse_options "item deferred generation at" "$@"
104
+ pmcli_require item deferred generation
105
+ pmcli_integer "${PMCLI_OPTS[item]}" "--item"
106
+ item="$PMCLI_VALUE"
107
+ deferred="${PMCLI_OPTS[deferred]}"
108
+ [[ $deferred == true || $deferred == false ]] ||
109
+ pmcli_usage_error "--deferred must be true or false; found: $deferred"
110
+ pmcli_integer "${PMCLI_OPTS[generation]}" "--generation"
111
+ generation="$PMCLI_VALUE"
112
+ pmcli_timestamp
113
+ at="$PMCLI_VALUE"
114
+ pm_build_add_entry "$item" "$deferred" "$generation" "$at" || rc=$?
115
+ pmcli_emit "$rc"
116
+ }
117
+
118
+ mpr_close() {
119
+ # Run the close subcommand over every `<key>:<state>` token.
120
+ local token key state canonical="" seen=" " rc=0
121
+ pmcli_parse_options "items" "$@"
122
+ pmcli_require items
123
+ pcoh_split_words "${PMCLI_OPTS[items]}"
124
+ local -a tokens=("${PCOH_WORDS[@]}")
125
+ for token in "${tokens[@]}"; do
126
+ [[ $token =~ ^(-?(0|[1-9][0-9]*)):([A-Za-z_]+)$ ]] ||
127
+ pmcli_usage_error "--items token must be <key>:<state>; found: $token"
128
+ state="${BASH_REMATCH[3]}"
129
+ key=$((BASH_REMATCH[1]))
130
+ # A duplicate key would let one record silently replace another, so it
131
+ # is rejected before the library runs (declared divergence 1).
132
+ [[ $seen != *" $key "* ]] || pmcli_usage_error "--items key given more than once: $key"
133
+ seen="$seen$key "
134
+ canonical="$canonical $key:$state"
135
+ done
136
+ pm_decide_close "${canonical# }" || rc=$?
137
+ pmcli_emit "$rc"
138
+ }
139
+
140
+ mpr_close_entry() {
141
+ # Run the close-entry subcommand.
142
+ local generation at rc=0
143
+ pmcli_parse_options "generation at" "$@"
144
+ pmcli_require generation
145
+ pmcli_integer "${PMCLI_OPTS[generation]}" "--generation"
146
+ generation="$PMCLI_VALUE"
147
+ pmcli_timestamp
148
+ at="$PMCLI_VALUE"
149
+ pm_build_close_entry "$generation" "$at" || rc=$?
150
+ pmcli_emit "$rc"
151
+ }
152
+
153
+ mpr_requeue_entry() {
154
+ # Run the requeue-entry subcommand.
155
+ local item generation at rc=0
156
+ pmcli_parse_options "item generation at" "$@"
157
+ pmcli_require item generation
158
+ pmcli_integer "${PMCLI_OPTS[item]}" "--item"
159
+ item="$PMCLI_VALUE"
160
+ pmcli_integer "${PMCLI_OPTS[generation]}" "--generation"
161
+ generation="$PMCLI_VALUE"
162
+ pmcli_timestamp
163
+ at="$PMCLI_VALUE"
164
+ pm_build_requeue_entry "$item" "$generation" "$at" || rc=$?
165
+ pmcli_emit "$rc"
166
+ }
167
+
168
+ mpr_main() {
169
+ # Dispatch on the subcommand.
170
+ (($# > 0)) ||
171
+ pmcli_usage_error "missing subcommand; expected one of admit, recolor, add-entry, close, close-entry, requeue-entry"
172
+ local subcommand="$1"
173
+ shift
174
+ case "$subcommand" in
175
+ admit) mpr_admit "$@" ;;
176
+ recolor) mpr_recolor "$@" ;;
177
+ add-entry) mpr_add_entry "$@" ;;
178
+ close) mpr_close "$@" ;;
179
+ close-entry) mpr_close_entry "$@" ;;
180
+ requeue-entry) mpr_requeue_entry "$@" ;;
181
+ *) pmcli_usage_error "unknown subcommand: $subcommand" ;;
182
+ esac
183
+ }
184
+
185
+ # Guard so the file can be sourced without executing main. main's return code
186
+ # is captured and re-exited explicitly as the final statement.
187
+ if [[ ${BASH_SOURCE[0]} == "${0}" ]]; then
188
+ mpr_rc=0
189
+ mpr_main "$@" || mpr_rc=$?
190
+ exit "$mpr_rc"
191
+ fi
@@ -52,14 +52,19 @@ PCOH_RESULT=""
52
52
  PCOH_WORDS=()
53
53
 
54
54
  pcoh_split_words() {
55
- # Populate PCOH_WORDS by splitting a space-separated string.
55
+ # Populate PCOH_WORDS by splitting a whitespace-separated string.
56
56
  #
57
57
  # Args: $1 = the string to split. An empty or whitespace-only string yields
58
58
  # an empty array, which is the empty-graph and empty-cohort case. `read -ra`
59
59
  # is used rather than an unquoted expansion so a token is never subjected to
60
- # pathname expansion.
60
+ # pathname expansion. Newline, CR, VT, and FF are converted to spaces first:
61
+ # `read` consumes one newline-terminated record and splits it on IFS only, so
62
+ # without the conversion every token after the first newline is dropped and
63
+ # CR, VT, and FF are not separators. With it, the separator set is space, tab,
64
+ # LF, CR, VT, and FF, which matches Python str.split() for ASCII.
65
+ local text="${1-}"
61
66
  PCOH_WORDS=()
62
- read -ra PCOH_WORDS <<<"${1-}"
67
+ read -ra PCOH_WORDS <<<"${text//[$'\n\r\v\f']/ }"
63
68
  }
64
69
 
65
70
  pcoh_fail() {
@@ -123,9 +128,14 @@ pcoh_build_adjacency() {
123
128
  PCOH_ADJACENT=()
124
129
  pcoh_split_words "$keys"
125
130
  local -a key_list=("${PCOH_WORDS[@]}")
131
+ # Rebuild the membership haystack from the split tokens. The endpoint checks
132
+ # below match a space-delimited word, so they would miss a key that the caller
133
+ # separated with a newline, tab, CR, VT, or FF instead of a single space.
134
+ keys=""
126
135
  # Seed every declared key so an isolated vertex survives into the coloring
127
136
  # step; inferring vertices from the edge list alone would drop it.
128
137
  for key in "${key_list[@]}"; do
138
+ keys="$keys $key"
129
139
  PCOH_DEGREE["$key"]=0
130
140
  PCOH_NEIGHBORS["$key"]=""
131
141
  done
@@ -0,0 +1,152 @@
1
+ #!/usr/bin/env bash
2
+ # parallel-mutation-cli.sh: sourceable command-line helpers shared by the two
3
+ # parallel mutation entry points, remove-parallel-item.sh (issue #791) and
4
+ # mutate-parallel-run.sh (issue #890). It holds option parsing, required-option
5
+ # checks, integer, key-list, and edge-list validation, the `--at` timestamp, and
6
+ # the result printer, so both entry points apply one grammar and one exit-code
7
+ # contract.
8
+ #
9
+ # Grammar. Every option takes exactly one value. An unknown name, an
10
+ # abbreviation, a `--name=value` form, a repeated option, or a missing value is
11
+ # a usage error; a value beginning with `--` counts as missing. Integer tokens
12
+ # match `-?(0|[1-9][0-9]*)`. `--at` matches `[0-9A-Za-z:.+-]+` and defaults to
13
+ # the current UTC minute as yyyy-MM-ddTHH-mm.
14
+ #
15
+ # Exit-code contract carried by these helpers:
16
+ # exit 2 usage error; one stderr line beginning
17
+ # "PARALLEL_MUTATION_ERROR: usage error:" (pmcli_usage_error)
18
+ # exit 1 rule rejection; PM_ERROR printed as one stderr line (pmcli_emit)
19
+ # exit 0 success; PM_RESULT printed as one stdout line (pmcli_emit)
20
+ #
21
+ # Dependencies. pmcli_emit reads PM_RESULT and PM_ERROR, which
22
+ # parallel-mutation.sh owns; an entry point sources that library before calling
23
+ # it. The only external command is `date`, run solely when `--at` is omitted.
24
+ #
25
+ # shellcheck disable=SC2034
26
+ # SC2034 is disabled file-wide because PMCLI_OPTS and PMCLI_VALUE are written
27
+ # here and read by the two entry points, which shellcheck analyses separately.
28
+
29
+ # Resolve this file's own directory so its dependencies source regardless of
30
+ # the caller's working directory.
31
+ PMCLI_LIB_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)
32
+ # shellcheck source=.claude/lib/bash/parallel-cohorts.sh
33
+ # shellcheck disable=SC1091
34
+ source "$PMCLI_LIB_DIR/parallel-cohorts.sh"
35
+
36
+ # Parsed option values, keyed by option name without the leading dashes.
37
+ declare -gA PMCLI_OPTS=()
38
+
39
+ # The canonical value stored by the most recent validator.
40
+ PMCLI_VALUE=""
41
+
42
+ pmcli_usage_error() {
43
+ # Print one usage-error line on stderr and exit 2.
44
+ #
45
+ # Args: $1 = the specific reason.
46
+ printf 'PARALLEL_MUTATION_ERROR: usage error: %s\n' "$1" >&2
47
+ exit 2
48
+ }
49
+
50
+ pmcli_parse_options() {
51
+ # Parse `--name value` pairs into PMCLI_OPTS against an allowed-name list.
52
+ #
53
+ # Args: $1 = space-separated allowed option names, then the arguments.
54
+ local allowed="$1" name
55
+ shift
56
+ PMCLI_OPTS=()
57
+ while (($# > 0)); do
58
+ name="${1#--}"
59
+ if [[ $1 != --* ]] || ! pc_contains_word "$allowed" "$name"; then
60
+ pmcli_usage_error "unknown option: $1"
61
+ fi
62
+ if [[ -n ${PMCLI_OPTS["$name"]+set} ]]; then
63
+ pmcli_usage_error "option given more than once: $1"
64
+ fi
65
+ if (($# < 2)) || [[ $2 == --* ]]; then
66
+ pmcli_usage_error "option $1 requires a value"
67
+ fi
68
+ PMCLI_OPTS["$name"]="$2"
69
+ shift 2
70
+ done
71
+ }
72
+
73
+ pmcli_require() {
74
+ # Exit with a usage error unless every named option was supplied.
75
+ #
76
+ # Args: the option names that are required.
77
+ local name
78
+ for name in "$@"; do
79
+ [[ -n ${PMCLI_OPTS["$name"]+set} ]] || pmcli_usage_error "missing required option: --$name"
80
+ done
81
+ }
82
+
83
+ pmcli_integer() {
84
+ # Validate one integer token and store it in canonical form in PMCLI_VALUE.
85
+ #
86
+ # Args: $1 = the token, $2 = a label naming where it came from. The
87
+ # validators store their result in a global instead of echoing it, because
88
+ # a usage error raised inside a command substitution would exit only the
89
+ # subshell.
90
+ if [[ ! $1 =~ ^-?(0|[1-9][0-9]*)$ ]]; then
91
+ pmcli_usage_error "$2 must be a decimal integer matching -?(0|[1-9][0-9]*); found: $1"
92
+ fi
93
+ PMCLI_VALUE=$(($1))
94
+ }
95
+
96
+ pmcli_integer_list() {
97
+ # Validate a whitespace-separated key list; store it space separated.
98
+ #
99
+ # Args: $1 = the list, $2 = a label naming the option.
100
+ local token canonical=""
101
+ pcoh_split_words "$1"
102
+ local -a tokens=("${PCOH_WORDS[@]}")
103
+ for token in "${tokens[@]}"; do
104
+ pmcli_integer "$token" "$2 key"
105
+ canonical="$canonical $PMCLI_VALUE"
106
+ done
107
+ PMCLI_VALUE="${canonical# }"
108
+ }
109
+
110
+ pmcli_edge_list() {
111
+ # Validate a whitespace-separated `a:b` edge list; store it canonical.
112
+ #
113
+ # Args: $1 = the edge list.
114
+ local token first canonical=""
115
+ pcoh_split_words "$1"
116
+ local -a tokens=("${PCOH_WORDS[@]}")
117
+ for token in "${tokens[@]}"; do
118
+ if [[ $token != *:* || $token == *:*:* ]]; then
119
+ pmcli_usage_error "edge must be <a>:<b>; found: $token"
120
+ fi
121
+ pmcli_integer "${token%%:*}" "edge endpoint"
122
+ first="$PMCLI_VALUE"
123
+ pmcli_integer "${token#*:}" "edge endpoint"
124
+ canonical="$canonical $first:$PMCLI_VALUE"
125
+ done
126
+ PMCLI_VALUE="${canonical# }"
127
+ }
128
+
129
+ pmcli_timestamp() {
130
+ # Store the validated `--at` value, or the current UTC minute when omitted.
131
+ if [[ -n ${PMCLI_OPTS[at]+set} ]]; then
132
+ PMCLI_VALUE="${PMCLI_OPTS[at]}"
133
+ [[ $PMCLI_VALUE =~ ^[0-9A-Za-z:.+-]+$ ]] ||
134
+ pmcli_usage_error "--at must match [0-9A-Za-z:.+-]+; found: $PMCLI_VALUE"
135
+ else
136
+ # The only clock read of either entry point, taken only when --at is
137
+ # omitted.
138
+ PMCLI_VALUE=$(date -u +%Y-%m-%dT%H-%M)
139
+ fi
140
+ }
141
+
142
+ pmcli_emit() {
143
+ # Print the library outcome and return the matching exit code.
144
+ #
145
+ # Args: $1 = the library function's return code.
146
+ if (($1 != 0)); then
147
+ printf '%s\n' "$PM_ERROR" >&2
148
+ return 1
149
+ fi
150
+ printf '%s\n' "$PM_RESULT"
151
+ return 0
152
+ }