@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
@@ -32,27 +32,21 @@
32
32
  #>
33
33
  [CmdletBinding()]
34
34
  param()
35
+ $script:HookDependencyGuardLoadFailed = $false; try { . (Join-Path $PSScriptRoot 'hook-dependency-guard.ps1') } catch { $script:HookDependencyGuardLoadFailed = $true }
35
36
 
36
37
 
37
- Import-Module (Join-Path $PSScriptRoot '../lib/hook-payload/HookPayload.psm1') -Force
38
+ try { Import-Module (Join-Path $PSScriptRoot '../lib/hook-payload/HookPayload.psm1') -Force -ErrorAction Stop } catch { Add-HookDependencyFailure -Name 'HookPayload.psm1' -ErrorRecord $_ }
38
39
  # Sanctioned-removal manifest reader (issue #635), consumed by the manifest branch in
39
40
  # Invoke-ParallelWorktreeRemovalGateDecision. Same module the epic gate imports.
40
- Import-Module (Join-Path $PSScriptRoot '../lib/cleanup-manifest/CleanupWorktreeManifest.psm1') -Force
41
+ try { Import-Module (Join-Path $PSScriptRoot '../lib/cleanup-manifest/CleanupWorktreeManifest.psm1') -Force -ErrorAction Stop } catch { Add-HookDependencyFailure -Name 'CleanupWorktreeManifest.psm1' -ErrorRecord $_ }
41
42
  # Shared command-line parser (issue #545), consumed by the scope filter in
42
- # Invoke-ParallelWorktreeRemovalGateDecision and by Get-ParallelWorktreeRemovalCommandPath.
43
+ # Invoke-ParallelWorktreeRemovalGateDecision and by Resolve-CommandLineInvocationTarget.
43
44
  # Both call sites are byte-for-byte the calls the epic gate makes, so the duplicated
44
45
  # concern now has one implementation.
45
- . (Join-Path $PSScriptRoot 'hook-command-scanner.ps1')
46
- . (Join-Path $PSScriptRoot 'hook-command-invocation.ps1')
46
+ try { . (Join-Path $PSScriptRoot 'hook-command-scanner.ps1') } catch { Add-HookDependencyFailure -Name 'hook-command-scanner.ps1' -ErrorRecord $_ }
47
+ try { . (Join-Path $PSScriptRoot 'hook-command-invocation.ps1') } catch { Add-HookDependencyFailure -Name 'hook-command-invocation.ps1' -ErrorRecord $_ }
47
48
 
48
- # Import guard (issue #690): a failed import denies instead of failing open.
49
- $script:ParallelWorktreeGateResolutionImportFailure = $null
50
- try {
51
- Import-Module (Join-Path $PSScriptRoot '../lib/worktree-resolution/WorktreeRunResolution.psm1') -Force -ErrorAction Stop
52
- }
53
- catch {
54
- $script:ParallelWorktreeGateResolutionImportFailure = 'WorktreeRunResolution.psm1'
55
- }
49
+ try { Import-Module (Join-Path $PSScriptRoot '../lib/worktree-resolution/WorktreeRunResolution.psm1') -Force -ErrorAction Stop } catch { Add-HookDependencyFailure -Name 'WorktreeRunResolution.psm1' -ErrorRecord $_ }
56
50
 
57
51
  $script:AllowedMergeStatuses = @('merged', 'worktree_removed')
58
52
  # Sanctioned-removal manifest location. Recorded here so every hook-read document this
@@ -164,52 +158,6 @@ function Read-ParallelWorktreeGateRunCheckpoint {
164
158
  }
165
159
  }
166
160
 
167
- function Get-ParallelWorktreeRemovalCommandPath {
168
- <#
169
- .SYNOPSIS
170
- Extract the target worktree path argument from a git worktree remove command.
171
- .DESCRIPTION
172
- The operand comes from the segment that structurally invokes git worktree remove,
173
- so a 'cd <path> &&' segment chained before the removal contributes nothing and a
174
- quoted mention of the phrase resolves to no operand at all. Quotes around the path
175
- are already stripped by the tokenizer.
176
-
177
- '--force' is a zero-argument flag, so it never contributes an operand and may be
178
- written on either side of the target path. Its presence is read structurally through
179
- Test-CommandLineFlag rather than by searching the raw text, so a '--force' spelling
180
- that appears inside an unrelated quoted argument cannot change how the operand list
181
- is read. When no operand resolves, a present '--force' is reported in its place, so
182
- the checkpoint lookup fails closed on a value that matches no recorded worktree_path
183
- - the same value the previous raw-text pattern returned for that input.
184
-
185
- This body is identical to Get-EpicWorktreeRemovalCommandPath in
186
- enforce-epic-worktree-removal-gate.ps1. The two gates fire on the same command and
187
- now delegate the shared concern to one parser rather than to two patterns that had
188
- already diverged from the Codex copy.
189
- .PARAMETER CommandText
190
- The Bash command text under evaluation.
191
- .OUTPUTS
192
- System.String or $null
193
- #>
194
- [CmdletBinding()]
195
- [OutputType([string])]
196
- param(
197
- [Parameter(Mandatory)]
198
- [string] $CommandText
199
- )
200
-
201
- $hasForce = Test-CommandLineFlag -CommandText $CommandText -CommandWord 'git' -SubcommandPath @('worktree', 'remove') -FlagName '--force'
202
- $operands = @(Get-CommandLineOperand -CommandText $CommandText -CommandWord 'git' -SubcommandPath @('worktree', 'remove'))
203
-
204
- if ($operands.Count -gt 0) {
205
- return $operands[0]
206
- }
207
- if ($hasForce) {
208
- return '--force'
209
- }
210
- return $null
211
- }
212
-
213
161
  function Find-ParallelWorktreeItemRecord {
214
162
  <#
215
163
  .SYNOPSIS
@@ -334,13 +282,8 @@ function Invoke-ParallelWorktreeRemovalGateDecision {
334
282
  param(
335
283
  [string] $ToolInputRaw
336
284
  )
337
-
338
- # A failed worktree-resolution import denies before any other logic (issue #690).
339
- if ($script:ParallelWorktreeGateResolutionImportFailure) {
340
- return Get-ParallelWorktreeGateBlockDecision -Reason (
341
- "PARALLEL_WORKTREE_REMOVAL_BLOCKED: the worktree-resolution module '$($script:ParallelWorktreeGateResolutionImportFailure)' " +
342
- 'failed to import, so the run checkpoint that governs this removal cannot be located; the gate fails closed.')
343
- }
285
+ $dependencyDecision = Get-HookDependencyFailureDecision -HookEvent PreToolUse -ReasonPrefix 'PARALLEL_WORKTREE_REMOVAL_BLOCKED:'
286
+ if ($null -ne $dependencyDecision) { return $dependencyDecision }
344
287
 
345
288
  $payload = Resolve-ClaudeHookToolInput -Raw $ToolInputRaw
346
289
  if (-not $payload.IsValid) {
@@ -362,7 +305,43 @@ function Invoke-ParallelWorktreeRemovalGateDecision {
362
305
  return Get-ParallelWorktreeGateAllowDecision
363
306
  }
364
307
 
365
- $worktreePath = Get-ParallelWorktreeRemovalCommandPath -CommandText $commandText
308
+ # Issue #824: the removal targets are derived structurally from every invocation.
309
+ # A target that cannot be derived denies before any checkpoint is read, and every
310
+ # derived target must be authorized on its own.
311
+ $resolution = Resolve-CommandLineInvocationTarget -CommandText $commandText -CommandWord 'git' -SubcommandPath @('worktree', 'remove')
312
+ if ($resolution.Status -eq 'NoMatch') {
313
+ return Get-ParallelWorktreeGateAllowDecision
314
+ }
315
+ if ($resolution.Status -ne 'Targets') {
316
+ return Get-ParallelWorktreeGateBlockDecision -Reason 'PARALLEL_WORKTREE_REMOVAL_BLOCKED: TARGET_WORKTREE_NOT_DERIVABLE: the git worktree remove target cannot be derived from the command text. No checkpoint can authorize this removal.'
317
+ }
318
+ foreach ($target in @($resolution.Targets)) {
319
+ $denial = Get-ParallelWorktreeRemovalTargetDenial -WorktreePath $target
320
+ if ($null -ne $denial) {
321
+ return $denial
322
+ }
323
+ }
324
+ return Get-ParallelWorktreeGateAllowDecision
325
+ }
326
+
327
+ function Get-ParallelWorktreeRemovalTargetDenial {
328
+ <#
329
+ .SYNOPSIS
330
+ Evaluate one derived removal target and return its deny decision, or $null when authorized.
331
+ .DESCRIPTION
332
+ Holds the per-path authorization cascade (parallel checkpoint, epic checkpoint,
333
+ sanctioned-removal manifest) for a single worktree path (issue #824).
334
+ .PARAMETER WorktreePath
335
+ One removal target derived by Resolve-CommandLineInvocationTarget.
336
+ .OUTPUTS
337
+ System.Collections.Specialized.OrderedDictionary or $null
338
+ #>
339
+ [CmdletBinding()]
340
+ [OutputType([System.Collections.Specialized.OrderedDictionary])]
341
+ param(
342
+ [Parameter(Mandatory)]
343
+ [string] $WorktreePath
344
+ )
366
345
 
367
346
  # Each run checkpoint is read beneath the worktree that records this path (issue #690),
368
347
  # parallel kind first; an ambiguous target denies before any allow or manifest check.
@@ -377,7 +356,7 @@ function Invoke-ParallelWorktreeRemovalGateDecision {
377
356
 
378
357
  $itemRecord = Find-ParallelWorktreeItemRecord -Checkpoint $checkpoint -WorktreePath $worktreePath
379
358
  if (Test-ParallelWorktreeRemovalAllowed -ItemRecord $itemRecord) {
380
- return Get-ParallelWorktreeGateAllowDecision
359
+ return $null
381
360
  }
382
361
 
383
362
  # Epic-authorization branch (issue #688). It runs after the parallel allow above and
@@ -394,7 +373,7 @@ function Invoke-ParallelWorktreeRemovalGateDecision {
394
373
 
395
374
  $featureRecord = Find-ParallelWorktreeItemRecord -Checkpoint $epicCheckpoint -WorktreePath $worktreePath -RecordArrayName 'features'
396
375
  if (Test-ParallelWorktreeRemovalAllowed -ItemRecord $featureRecord) {
397
- return Get-ParallelWorktreeGateAllowDecision
376
+ return $null
398
377
  }
399
378
 
400
379
  # Sanctioned-removal manifest branch (issue #635). It runs last, so a
@@ -409,15 +388,15 @@ function Invoke-ParallelWorktreeRemovalGateDecision {
409
388
  # only; the epic gate covers the features array with its own exclusion.
410
389
  if (-not (Test-CleanupManifestCheckpointCoversPath -Checkpoint $checkpoint -RecordArrayName 'items' -WorktreePath $worktreePath) -and
411
390
  (Test-CleanupWorktreeManifestAuthorizesRemoval -WorktreePath $worktreePath)) {
412
- return Get-ParallelWorktreeGateAllowDecision
391
+ return $null
413
392
  }
414
393
 
415
394
  # When neither kind resolved, the deny names the resolution reason first (issue #690).
416
395
  $prefix = ''
417
396
  if ($parallelRead.Target.Status -eq 'NoTarget' -and $epicRead.Target.Status -eq 'NoTarget') {
418
- $prefix = "PARALLEL_WORKTREE_REMOVAL_BLOCKED: $($parallelRead.Target.ReasonCode): $($parallelRead.Target.Detail). "
397
+ $prefix = "$($parallelRead.Target.ReasonCode): $($parallelRead.Target.Detail). "
419
398
  }
420
- return Get-ParallelWorktreeGateBlockDecision -Reason ($prefix + "PARALLEL_WORKTREE_REMOVAL_BLOCKED: git worktree remove for '$worktreePath' requires a matching parallel checkpoint items[] record with merge_status in {merged, worktree_removed}. The checkpoint was unreadable, no matching record was found, or merge_status was not yet safe for removal.")
399
+ return Get-ParallelWorktreeGateBlockDecision -Reason ('PARALLEL_WORKTREE_REMOVAL_BLOCKED: ' + $prefix + "git worktree remove for '$worktreePath' requires a matching parallel checkpoint items[] record with merge_status in {merged, worktree_removed}. The checkpoint was unreadable, no matching record was found, or merge_status was not yet safe for removal.")
421
400
  }
422
401
 
423
402
  function Invoke-ParallelWorktreeRemovalGateEntryPoint {
@@ -462,6 +441,9 @@ function Invoke-ParallelWorktreeRemovalGateEntryPoint {
462
441
  if ($MyInvocation.InvocationName -eq '.') {
463
442
  return
464
443
  }
444
+ if ($script:HookDependencyGuardLoadFailed) { [Console]::Error.WriteLine('PARALLEL_WORKTREE_REMOVAL_BLOCKED: hook-dependency-guard.ps1 failed to load; the gate fails closed.'); exit 2 }
445
+ $dependencyDecision = Get-HookDependencyFailureDecision -HookEvent PreToolUse -ReasonPrefix 'PARALLEL_WORKTREE_REMOVAL_BLOCKED:'
446
+ if ($null -ne $dependencyDecision) { $dependencyDecision | ConvertTo-Json -Compress -Depth 5 | Write-Output; exit 0 }
465
447
 
466
448
  # The entry point returns its [int] exit code as the last pipeline element and the
467
449
  # decision JSON before it. `exit (<call>)` would capture BOTH into the exit
@@ -57,10 +57,11 @@
57
57
  #>
58
58
  [CmdletBinding()]
59
59
  param()
60
+ $script:HookDependencyGuardLoadFailed = $false; try { . (Join-Path $PSScriptRoot 'hook-dependency-guard.ps1') } catch { $script:HookDependencyGuardLoadFailed = $true }
60
61
 
61
62
 
62
- Import-Module (Join-Path $PSScriptRoot '../lib/hook-payload/HookPayload.psm1') -Force
63
- . (Join-Path $PSScriptRoot 'enforce-batch-budget-route.ps1')
63
+ try { Import-Module (Join-Path $PSScriptRoot '../lib/hook-payload/HookPayload.psm1') -Force -ErrorAction Stop } catch { Add-HookDependencyFailure -Name 'HookPayload.psm1' -ErrorRecord $_ }
64
+ try { . (Join-Path $PSScriptRoot 'enforce-batch-budget-route.ps1') } catch { Add-HookDependencyFailure -Name 'enforce-batch-budget-route.ps1' -ErrorRecord $_ }
64
65
 
65
66
  function Test-PowerShellBatchBudgetPathInRoot {
66
67
  <#
@@ -277,6 +278,8 @@ function Invoke-PowerShellBatchBudgetDecision {
277
278
  [AllowEmptyString()]
278
279
  [string] $ObservedRoute = ''
279
280
  )
281
+ $dependencyDecision = Get-HookDependencyFailureDecision -HookEvent PreToolUse -ReasonPrefix 'POWERSHELL_LARGE_PATH_REQUIRED:'
282
+ if ($null -ne $dependencyDecision) { return $dependencyDecision }
280
283
 
281
284
  $normalized = $FilePath -replace '\\', '/'
282
285
  if ($normalized -notmatch '\.(ps1|psm1|psd1)$') {
@@ -474,6 +477,9 @@ function Invoke-PowerShellBatchBudgetEntryPoint {
474
477
  if ($MyInvocation.InvocationName -eq '.') {
475
478
  return
476
479
  }
480
+ if ($script:HookDependencyGuardLoadFailed) { [Console]::Error.WriteLine('POWERSHELL_LARGE_PATH_REQUIRED: hook-dependency-guard.ps1 failed to load; the gate fails closed.'); exit 2 }
481
+ $dependencyDecision = Get-HookDependencyFailureDecision -HookEvent PreToolUse -ReasonPrefix 'POWERSHELL_LARGE_PATH_REQUIRED:'
482
+ if ($null -ne $dependencyDecision) { $dependencyDecision | ConvertTo-Json -Compress -Depth 5 | Write-Output; exit 0 }
477
483
 
478
484
  # The entry point returns its [int] exit code as the last pipeline element and the
479
485
  # decision JSON before it. `exit (<call>)` would capture BOTH into the exit
@@ -0,0 +1,279 @@
1
+ <#
2
+ .SYNOPSIS
3
+ PreToolUse hook that limits the pr-author subagent's Bash calls to an allowlist of forms.
4
+
5
+ .DESCRIPTION
6
+ Registered in the frontmatter of .claude/agents/pr-author.md on the "Bash" matcher
7
+ (issue #824, #733). Every command is read with Read-CommandLineInvocationSegment, so a
8
+ wrapper payload, a substitution body, or an xargs-injected command is a record of its own.
9
+ The command is allowed only when every record is a literal, top-level, unwrapped command
10
+ in one of these forms, matched by token position:
11
+
12
+ - git log ... and git rev-parse ... (no git global option)
13
+ - gh pr create ... and gh pr edit ... (no gh global option)
14
+ - sha256sum artifacts/pr_body_<N>.md (only as the whole command)
15
+ - date -u [+<format>] (only as the whole command)
16
+
17
+ Segments may be joined only by ';', '&&', '||', '|', or a newline. A record that is
18
+ nested, unbalanced, opaque, non-literal, wrapper-led, led by a VAR=value assignment, or
19
+ whose scanner segment carries a redirection is denied. Every deny reason begins
20
+ PR_AUTHOR_COMMAND_NOT_ALLOWED: and names the first rejected segment.
21
+
22
+ The hook is read-only: it inspects the attempted command and emits a JSON
23
+ allow-or-deny decision without mutating the command text.
24
+
25
+ .NOTES
26
+ Compatible with PowerShell 7+. Pure string logic over the shared command-line parser;
27
+ no disk, process, or network access beyond payload acquisition.
28
+ #>
29
+ [CmdletBinding()]
30
+ param()
31
+
32
+
33
+ Import-Module (Join-Path $PSScriptRoot '../lib/hook-payload/HookPayload.psm1') -Force
34
+ # Shared command-line parser (issues #545 and #824).
35
+ . (Join-Path $PSScriptRoot 'hook-command-scanner.ps1')
36
+ . (Join-Path $PSScriptRoot 'hook-command-invocation.ps1')
37
+
38
+ $script:PrAuthorAllowlistReasonPrefix = 'PR_AUTHOR_COMMAND_NOT_ALLOWED: '
39
+ $script:PrAuthorAllowlistDelimiters = @('', ';', '&&', '||', '|', "`n")
40
+ $script:PrAuthorAllowlistWrapperWords = @(
41
+ 'sh', 'bash', 'zsh', 'dash', 'ksh', 'pwsh', 'powershell', 'eval', 'xargs',
42
+ 'env', 'timeout', 'time', 'command', 'nohup'
43
+ )
44
+ $script:PrAuthorAllowlistFormText = 'git log, git rev-parse, gh pr create, gh pr edit, sha256sum artifacts/pr_body_<N>.md, date -u [+<format>]'
45
+
46
+ function Test-PrAuthorAllowlistForm {
47
+ <#
48
+ .SYNOPSIS
49
+ Report whether one record's tokens match an allowed command form by token position.
50
+ .PARAMETER Token
51
+ The record's tokens, quotes already stripped.
52
+ .PARAMETER IsWholeCommand
53
+ Whether the command has exactly one top-level record; sha256sum and date require it.
54
+ .OUTPUTS
55
+ System.Boolean
56
+ #>
57
+ [CmdletBinding()]
58
+ [OutputType([bool])]
59
+ param(
60
+ [Parameter(Mandatory)][AllowEmptyCollection()][string[]] $Token,
61
+ [Parameter(Mandatory)][bool] $IsWholeCommand
62
+ )
63
+
64
+ if ($Token.Count -eq 0) {
65
+ return $false
66
+ }
67
+ switch -CaseSensitive ($Token[0]) {
68
+ 'git' {
69
+ return ($Token.Count -ge 2 -and @('log', 'rev-parse') -ccontains $Token[1])
70
+ }
71
+ 'gh' {
72
+ return ($Token.Count -ge 3 -and $Token[1] -ceq 'pr' -and @('create', 'edit') -ccontains $Token[2])
73
+ }
74
+ 'sha256sum' {
75
+ return ($IsWholeCommand -and $Token.Count -eq 2 -and $Token[1] -cmatch '^artifacts/pr_body_\d+\.md$')
76
+ }
77
+ 'date' {
78
+ if (-not $IsWholeCommand -or $Token.Count -lt 2 -or $Token[1] -cne '-u') {
79
+ return $false
80
+ }
81
+ return ($Token.Count -eq 2 -or ($Token.Count -eq 3 -and $Token[2].StartsWith('+')))
82
+ }
83
+ }
84
+ return $false
85
+ }
86
+
87
+ function Get-PrAuthorAllowlistRecordProblem {
88
+ <#
89
+ .SYNOPSIS
90
+ Return the reason one record is rejected, or $null when the record is allowed.
91
+ .PARAMETER Record
92
+ One Read-CommandLineInvocationSegment record.
93
+ .PARAMETER MaskedText
94
+ The masked text of the record's depth-0 scanner segment, or '' for a nested record.
95
+ .PARAMETER IsWholeCommand
96
+ Whether the command has exactly one top-level record.
97
+ .OUTPUTS
98
+ System.String or $null
99
+ #>
100
+ [CmdletBinding()]
101
+ [OutputType([string])]
102
+ param(
103
+ [Parameter(Mandatory)][pscustomobject] $Record,
104
+ [Parameter(Mandatory)][AllowEmptyString()][string] $MaskedText,
105
+ [Parameter(Mandatory)][bool] $IsWholeCommand
106
+ )
107
+
108
+ $tokens = [string[]]@($Record.Tokens)
109
+ if ($Record.Depth -gt 0) { return "it is a nested $($Record.Origin) command" }
110
+ if ($Record.Unbalanced) { return 'its quoting or grouping is unbalanced' }
111
+ if ($Record.Opaque) { return "its payload cannot be read ($($Record.OpaqueReason))" }
112
+ if (-not $Record.Literal) { return 'it carries a variable, substitution, or other non-literal token' }
113
+ if ($script:PrAuthorAllowlistWrapperWords -contains $Record.CommandWord) { return "it is led by the wrapper '$($Record.CommandWord)'" }
114
+ if ($script:PrAuthorAllowlistDelimiters -notcontains $Record.Delimiter) { return "it is joined by the delimiter '$($Record.Delimiter)'" }
115
+ if ($MaskedText.Contains('<') -or $MaskedText.Contains('>')) { return 'it carries a redirection' }
116
+ if ($tokens.Count -gt 0 -and $tokens[0] -match '^[A-Za-z_][A-Za-z0-9_]*=') { return 'it is led by a VAR=value assignment' }
117
+ if (-not (Test-PrAuthorAllowlistForm -Token $tokens -IsWholeCommand $IsWholeCommand)) { return 'it is not an allowed command form' }
118
+ return $null
119
+ }
120
+
121
+ function Get-PrAuthorCommandAllowlistReason {
122
+ <#
123
+ .SYNOPSIS
124
+ Return the deny reason for a command outside the pr-author allowlist, or $null.
125
+ .PARAMETER CommandText
126
+ The Bash command text extracted from the envelope's tool_input.
127
+ .OUTPUTS
128
+ System.String or $null
129
+ #>
130
+ [CmdletBinding()]
131
+ [OutputType([string])]
132
+ param(
133
+ [Parameter(Mandatory)]
134
+ [AllowEmptyString()]
135
+ [string] $CommandText
136
+ )
137
+
138
+ if ([string]::IsNullOrWhiteSpace($CommandText)) {
139
+ return $null
140
+ }
141
+
142
+ $records = @(Read-CommandLineInvocationSegment -CommandText $CommandText)
143
+ $segments = @(Read-CommandLineSegment -CommandText (ConvertTo-CommandLineNormalizedText -CommandText $CommandText))
144
+ $topLevel = @($records | Where-Object { $_.Depth -eq 0 })
145
+ $isWholeCommand = $topLevel.Count -eq 1
146
+ $topLevelPosition = 0
147
+
148
+ foreach ($record in $records) {
149
+ $maskedText = ''
150
+ if ($record.Depth -eq 0) {
151
+ if ($topLevelPosition -lt $segments.Count) {
152
+ $maskedText = [string]$segments[$topLevelPosition].MaskedText
153
+ }
154
+ $topLevelPosition++
155
+ }
156
+ $problem = Get-PrAuthorAllowlistRecordProblem -Record $record -MaskedText $maskedText -IsWholeCommand $isWholeCommand
157
+ if ($null -ne $problem) {
158
+ return ($script:PrAuthorAllowlistReasonPrefix +
159
+ "the segment '$($record.RawText.Trim())' is rejected because $problem. The pr-author agent may run only: $($script:PrAuthorAllowlistFormText), each without wrappers, substitutions, or redirections.")
160
+ }
161
+ }
162
+
163
+ return $null
164
+ }
165
+
166
+ function Get-PrAuthorCommandAllowlistDecision {
167
+ <#
168
+ .SYNOPSIS
169
+ Build an ordered allow or deny decision.
170
+ .PARAMETER Reason
171
+ The deny reason, or $null for an allow.
172
+ .OUTPUTS
173
+ System.Collections.Specialized.OrderedDictionary
174
+ #>
175
+ [CmdletBinding()]
176
+ [OutputType([System.Collections.Specialized.OrderedDictionary])]
177
+ param(
178
+ [AllowNull()]
179
+ [string] $Reason
180
+ )
181
+
182
+ if ([string]::IsNullOrEmpty($Reason)) {
183
+ return [ordered]@{
184
+ hookSpecificOutput = [ordered]@{
185
+ hookEventName = 'PreToolUse'
186
+ permissionDecision = 'allow'
187
+ }
188
+ }
189
+ }
190
+ return [ordered]@{
191
+ hookSpecificOutput = [ordered]@{
192
+ hookEventName = 'PreToolUse'
193
+ permissionDecision = 'deny'
194
+ permissionDecisionReason = $Reason
195
+ }
196
+ }
197
+ }
198
+
199
+ function Invoke-PrAuthorCommandAllowlistDecision {
200
+ <#
201
+ .SYNOPSIS
202
+ Return the ordered allow-or-deny decision for one command text.
203
+ .PARAMETER CommandText
204
+ The Bash command text.
205
+ .OUTPUTS
206
+ System.Collections.Specialized.OrderedDictionary
207
+ #>
208
+ [CmdletBinding()]
209
+ [OutputType([System.Collections.Specialized.OrderedDictionary])]
210
+ param(
211
+ [Parameter(Mandatory)]
212
+ [AllowEmptyString()]
213
+ [string] $CommandText
214
+ )
215
+
216
+ return Get-PrAuthorCommandAllowlistDecision -Reason (Get-PrAuthorCommandAllowlistReason -CommandText $CommandText)
217
+ }
218
+
219
+ function Invoke-PrAuthorCommandAllowlistEntryPoint {
220
+ <#
221
+ .SYNOPSIS
222
+ Runs the hook decision and returns the process exit code.
223
+ .DESCRIPTION
224
+ Acquires the payload through the shared reader unless the caller supplies one, emits
225
+ the compact decision JSON, and returns 0. It never returns 1: exit 1 is non-blocking
226
+ for PreToolUse, so every anomaly is already a deny decision by the time control
227
+ reaches here. The function does not call exit; the thin tail converts the returned
228
+ code into a process exit.
229
+ .PARAMETER ToolInputRaw
230
+ Optional pre-acquired payload text. When omitted the ReadPayload seam runs.
231
+ .PARAMETER ReadPayload
232
+ Seam for payload acquisition, so tests can drive the empty-on-all-transports case
233
+ without touching a console.
234
+ .OUTPUTS
235
+ System.Int32
236
+ #>
237
+ [CmdletBinding()]
238
+ [OutputType([int])]
239
+ param(
240
+ [AllowNull()]
241
+ [AllowEmptyString()]
242
+ [string] $ToolInputRaw,
243
+
244
+ [scriptblock] $ReadPayload = { Read-ClaudeHookRawPayload }
245
+ )
246
+
247
+ if (-not $PSBoundParameters.ContainsKey('ToolInputRaw')) {
248
+ $ToolInputRaw = [string](& $ReadPayload)
249
+ }
250
+
251
+ $payload = Resolve-ClaudeHookToolInput -Raw $ToolInputRaw
252
+ if (-not $payload.IsValid) {
253
+ $decision = Get-PrAuthorCommandAllowlistDecision -Reason (
254
+ $script:PrAuthorAllowlistReasonPrefix + 'payload anomaly - ' +
255
+ (Get-ClaudeHookPayloadAnomalyReason -Anomaly $payload.Anomaly) +
256
+ '. The hook fails closed on an envelope it cannot read.')
257
+ } else {
258
+ $commandText = [string](Get-ClaudeHookToolInputString -ToolInput $payload.Value -Name 'command')
259
+ $decision = Invoke-PrAuthorCommandAllowlistDecision -CommandText $commandText
260
+ }
261
+ $decision | ConvertTo-Json -Compress -Depth 5 | Write-Output
262
+
263
+ return 0
264
+ }
265
+
266
+ # Allow dot-sourcing in tests without executing the entrypoint.
267
+ if ($MyInvocation.InvocationName -eq '.') {
268
+ return
269
+ }
270
+
271
+ # The entry point returns its [int] exit code as the last pipeline element and the
272
+ # decision JSON before it. `exit (<call>)` would capture BOTH into the exit
273
+ # expression and emit nothing, so the decision is written explicitly here first.
274
+ $entryPointResult = @(Invoke-PrAuthorCommandAllowlistEntryPoint)
275
+ if ($entryPointResult.Count -gt 1) {
276
+ $entryPointResult[0..($entryPointResult.Count - 2)] | Write-Output
277
+ }
278
+
279
+ exit ([int]$entryPointResult[-1])