@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.
- package/out/mcp-server.js +1149 -661
- package/package.json +3 -2
- package/resources/claude-customizations/.claude/agents/atomic-executor.md +28 -6
- package/resources/claude-customizations/.claude/agents/atomic-planner.md +20 -2
- package/resources/claude-customizations/.claude/agents/epic-orchestrator.md +17 -4
- package/resources/claude-customizations/.claude/agents/epic-planner.md +5 -4
- package/resources/claude-customizations/.claude/agents/feature-review.md +6 -4
- package/resources/claude-customizations/.claude/agents/orchestrator.md +9 -5
- package/resources/claude-customizations/.claude/agents/parallel-orchestrator.md +35 -13
- package/resources/claude-customizations/.claude/agents/parallel-planner.md +9 -6
- package/resources/claude-customizations/.claude/agents/pr-author.md +15 -2
- package/resources/claude-customizations/.claude/agents/prd-feature.md +1 -1
- package/resources/claude-customizations/.claude/agents/task-researcher.md +8 -0
- package/resources/claude-customizations/.claude/hooks/check-powershell-test-purity.ps1 +7 -1
- package/resources/claude-customizations/.claude/hooks/check-python-test-purity.ps1 +7 -1
- package/resources/claude-customizations/.claude/hooks/enforce-checkpoint-monotonic.ps1 +7 -1
- package/resources/claude-customizations/.claude/hooks/enforce-completion-consistency.ps1 +111 -46
- package/resources/claude-customizations/.claude/hooks/enforce-completion-helpers.ps1 +105 -0
- package/resources/claude-customizations/.claude/hooks/enforce-discovery-artifact-gate.ps1 +8 -1
- package/resources/claude-customizations/.claude/hooks/enforce-epic-invocation-origin.ps1 +7 -1
- package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate-command.ps1 +190 -0
- package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate-resolution.ps1 +116 -59
- package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate.ps1 +78 -77
- package/resources/claude-customizations/.claude/hooks/enforce-epic-wave-barrier.ps1 +74 -75
- package/resources/claude-customizations/.claude/hooks/enforce-epic-worktree-removal-gate-resolution.ps1 +127 -35
- package/resources/claude-customizations/.claude/hooks/enforce-epic-worktree-removal-gate.ps1 +61 -57
- package/resources/claude-customizations/.claude/hooks/enforce-evidence-locations.ps1 +7 -1
- package/resources/claude-customizations/.claude/hooks/enforce-feature-folder-order.ps1 +129 -39
- package/resources/claude-customizations/.claude/hooks/enforce-mermaid-validation.ps1 +7 -1
- package/resources/claude-customizations/.claude/hooks/enforce-model-routing-receipt.ps1 +9 -3
- package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-epic-scope.ps1 +39 -51
- package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-helpers.ps1 +31 -58
- package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-modes.ps1 +63 -47
- package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-targets.ps1 +433 -0
- package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate.ps1 +17 -12
- package/resources/claude-customizations/.claude/hooks/enforce-parallel-abandon-gate.ps1 +9 -3
- package/resources/claude-customizations/.claude/hooks/enforce-parallel-cohort-barrier.ps1 +65 -50
- package/resources/claude-customizations/.claude/hooks/enforce-parallel-drift-gate.ps1 +49 -42
- package/resources/claude-customizations/.claude/hooks/enforce-parallel-worktree-removal-gate.ps1 +54 -72
- package/resources/claude-customizations/.claude/hooks/enforce-powershell-batch-budget.ps1 +8 -2
- package/resources/claude-customizations/.claude/hooks/enforce-pr-author-command-allowlist.ps1 +279 -0
- package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill-helpers.ps1 +129 -75
- package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.artifact-root.ps1 +233 -0
- package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.docs-only.ps1 +224 -0
- package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.ps1 +69 -24
- package/resources/claude-customizations/.claude/hooks/enforce-prd-feature-before-planner-helpers.ps1 +29 -18
- package/resources/claude-customizations/.claude/hooks/enforce-prd-feature-before-planner.ps1 +10 -4
- package/resources/claude-customizations/.claude/hooks/enforce-promotion-mcp-only.ps1 +14 -5
- package/resources/claude-customizations/.claude/hooks/enforce-python-batch-budget.ps1 +8 -2
- package/resources/claude-customizations/.claude/hooks/feature-folder-resolution.ps1 +373 -0
- package/resources/claude-customizations/.claude/hooks/feature-review-coverage-thresholds.ps1 +85 -0
- package/resources/claude-customizations/.claude/hooks/hook-command-heredoc.ps1 +119 -0
- package/resources/claude-customizations/.claude/hooks/hook-command-invocation-operands.ps1 +198 -0
- package/resources/claude-customizations/.claude/hooks/hook-command-invocation.ps1 +254 -255
- package/resources/claude-customizations/.claude/hooks/hook-command-payload-powershell.ps1 +201 -0
- package/resources/claude-customizations/.claude/hooks/hook-command-payload.ps1 +484 -0
- package/resources/claude-customizations/.claude/hooks/hook-command-scanner.ps1 +27 -110
- package/resources/claude-customizations/.claude/hooks/hook-dependency-guard.ps1 +114 -0
- package/resources/claude-customizations/.claude/hooks/hook-docs-only-diff.ps1 +497 -0
- package/resources/claude-customizations/.claude/hooks/premortem-section.ps1 +149 -0
- package/resources/claude-customizations/.claude/hooks/subagentstop-envelope.ps1 +192 -0
- package/resources/claude-customizations/.claude/hooks/validate-bash.ps1 +9 -3
- package/resources/claude-customizations/.claude/hooks/validate-discovery-artifact-gate.ps1 +40 -42
- package/resources/claude-customizations/.claude/hooks/validate-executor-output.ps1 +267 -113
- package/resources/claude-customizations/.claude/hooks/validate-feature-review-coverage-metrics.ps1 +180 -0
- package/resources/claude-customizations/.claude/hooks/validate-feature-review-coverage-rules.ps1 +195 -0
- package/resources/claude-customizations/.claude/hooks/validate-feature-review-coverage.ps1 +141 -395
- package/resources/claude-customizations/.claude/hooks/validate-orchestrator-output-resolution.ps1 +311 -0
- package/resources/claude-customizations/.claude/hooks/validate-orchestrator-output-routing.ps1 +170 -0
- package/resources/claude-customizations/.claude/hooks/validate-orchestrator-output.ps1 +115 -190
- package/resources/claude-customizations/.claude/hooks/validate-planner-output.ps1 +77 -33
- package/resources/claude-customizations/.claude/hooks/validate-pr-author-output.ps1 +39 -44
- package/resources/claude-customizations/.claude/hooks/validate-prd-feature-output.ps1 +43 -14
- package/resources/claude-customizations/.claude/hooks/validate-required-artifact-output.ps1 +66 -36
- package/resources/claude-customizations/.claude/hooks/validate-task-researcher-output.ps1 +52 -36
- package/resources/claude-customizations/.claude/lib/bash/mutate-parallel-run.sh +191 -0
- package/resources/claude-customizations/.claude/lib/bash/parallel-cohorts.sh +13 -3
- package/resources/claude-customizations/.claude/lib/bash/parallel-mutation-cli.sh +152 -0
- package/resources/claude-customizations/.claude/lib/bash/parallel-mutation-ops.sh +193 -0
- package/resources/claude-customizations/.claude/lib/bash/parallel-mutation.sh +322 -0
- package/resources/claude-customizations/.claude/lib/bash/remove-parallel-item.sh +146 -0
- package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusExtraction.psm1 +23 -33
- package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusTokenShape.psm1 +86 -6
- package/resources/claude-customizations/.claude/lib/ci-gate/Invoke-CiGateParser.ps1 +91 -19
- package/resources/claude-customizations/.claude/lib/ci-gate/Invoke-MergeConfirmationParser.ps1 +203 -0
- package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateEpicWaveBarrier.psm1 +346 -0
- package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateIssueAdoption.psm1 +24 -3
- package/resources/claude-customizations/.claude/lib/worktree-resolution/WorktreeItemResolution.psm1 +79 -2
- package/resources/claude-customizations/.claude/lib/worktree-resolution/WorktreeRunResolution.psm1 +3 -3
- package/resources/claude-customizations/.claude/rules/architecture-boundaries.md +5 -5
- package/resources/claude-customizations/.claude/rules/csharp.md +2 -2
- package/resources/claude-customizations/.claude/rules/general-code-change.md +1 -1
- package/resources/claude-customizations/.claude/rules/general-unit-test.md +3 -2
- package/resources/claude-customizations/.claude/rules/orchestrator-state.md +37 -4
- package/resources/claude-customizations/.claude/rules/parallel-orchestration.md +50 -5
- package/resources/claude-customizations/.claude/rules/plan-acceptance-gates.md +85 -10
- package/resources/claude-customizations/.claude/rules/quality-tiers.md +11 -7
- package/resources/claude-customizations/.claude/rules/shell.md +10 -8
- package/resources/claude-customizations/.claude/rules/typescript.md +1 -1
- package/resources/claude-customizations/.claude/settings.json +3 -4
- package/resources/claude-customizations/.claude/skills/acceptance-criteria-tracking/SKILL.md +1 -1
- package/resources/claude-customizations/.claude/skills/atomic-plan-contract/SKILL.md +52 -16
- package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/SKILL.md +7 -3
- package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_detached_lib.sh +1 -1
- package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_enumerate_lib.sh +2 -0
- package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_scan_helper.sh +2 -0
- package/resources/claude-customizations/.claude/skills/csharp-change-budget-router/SKILL.md +2 -2
- package/resources/claude-customizations/.claude/skills/epic-orchestrate/SKILL.md +45 -9
- package/resources/claude-customizations/.claude/skills/epic-plan/SKILL.md +5 -3
- package/resources/claude-customizations/.claude/skills/evidence-and-timestamp-conventions/SKILL.md +10 -1
- package/resources/claude-customizations/.claude/skills/execution-environment/SKILL.md +134 -0
- package/resources/claude-customizations/.claude/skills/feature-promotion-lifecycle/SKILL.md +1 -1
- package/resources/claude-customizations/.claude/skills/feature-review-workflow/SKILL.md +4 -3
- package/resources/claude-customizations/.claude/skills/orchestrate/SKILL.md +36 -15
- package/resources/claude-customizations/.claude/skills/parallel-add/SKILL.md +52 -14
- package/resources/claude-customizations/.claude/skills/parallel-close/SKILL.md +22 -9
- package/resources/claude-customizations/.claude/skills/parallel-orchestrate/SKILL.md +108 -21
- package/resources/claude-customizations/.claude/skills/parallel-plan/SKILL.md +63 -7
- package/resources/claude-customizations/.claude/skills/parallel-remove/SKILL.md +35 -12
- package/resources/claude-customizations/.claude/skills/powershell-change-budget-router/SKILL.md +1 -1
- package/resources/claude-customizations/.claude/skills/pr-author/SKILL.md +3 -1
- package/resources/claude-customizations/.claude/skills/python-change-budget-router/SKILL.md +1 -1
- package/resources/claude-customizations/.claude/skills/quota-throttling/SKILL.md +1 -1
- package/resources/claude-customizations/.claude/skills/remediation-handoff-atomic-planner/SKILL.md +16 -8
- package/resources/claude-customizations/.claude/skills/research-issue/SKILL.md +6 -0
- package/resources/claude-customizations/pack-manifests/core.json +27 -0
- package/resources/codex-and-agents-customizations/.agents/skills/README.md +1 -0
- package/resources/codex-and-agents-customizations/.agents/skills/acceptance-criteria-tracking/SKILL.md +1 -1
- package/resources/codex-and-agents-customizations/.agents/skills/architecture-boundaries/SKILL.md +5 -5
- package/resources/codex-and-agents-customizations/.agents/skills/atomic-executor/SKILL.md +19 -7
- package/resources/codex-and-agents-customizations/.agents/skills/atomic-plan-contract/SKILL.md +43 -4
- package/resources/codex-and-agents-customizations/.agents/skills/atomic-planner/SKILL.md +18 -6
- package/resources/codex-and-agents-customizations/.agents/skills/csharp/SKILL.md +2 -2
- package/resources/codex-and-agents-customizations/.agents/skills/csharp-change-budget-router/SKILL.md +2 -2
- package/resources/codex-and-agents-customizations/.agents/skills/csharp-qa-gate/SKILL.md +2 -2
- package/resources/codex-and-agents-customizations/.agents/skills/epic-orchestrate/SKILL.md +16 -1
- package/resources/codex-and-agents-customizations/.agents/skills/epic-plan/SKILL.md +17 -1
- package/resources/codex-and-agents-customizations/.agents/skills/epic-run/SKILL.md +2 -1
- package/resources/codex-and-agents-customizations/.agents/skills/evidence-and-timestamp-conventions/SKILL.md +10 -1
- package/resources/codex-and-agents-customizations/.agents/skills/execution-environment/SKILL.md +134 -0
- package/resources/codex-and-agents-customizations/.agents/skills/feature-review-workflow/SKILL.md +13 -5
- package/resources/codex-and-agents-customizations/.agents/skills/general-code-change/SKILL.md +1 -1
- package/resources/codex-and-agents-customizations/.agents/skills/general-unit-test/SKILL.md +3 -2
- package/resources/codex-and-agents-customizations/.agents/skills/orchestrate/SKILL.md +27 -8
- package/resources/codex-and-agents-customizations/.agents/skills/orchestrator-workflow/SKILL.md +1 -1
- package/resources/codex-and-agents-customizations/.agents/skills/powershell-change-budget-router/SKILL.md +1 -1
- package/resources/codex-and-agents-customizations/.agents/skills/python-change-budget-router/SKILL.md +1 -1
- package/resources/codex-and-agents-customizations/.agents/skills/quality-tiers/SKILL.md +11 -7
- package/resources/codex-and-agents-customizations/.agents/skills/remediation-handoff-atomic-planner/SKILL.md +7 -3
- package/resources/codex-and-agents-customizations/.agents/skills/research-issue/SKILL.md +6 -0
- package/resources/codex-and-agents-customizations/.agents/skills/translate-claude-to-codex/SKILL.md +1 -1
- package/resources/codex-and-agents-customizations/.agents-variants/csharp-legacy/skills/csharp/SKILL.md +2 -2
- package/resources/codex-and-agents-customizations/.agents-variants/csharp-legacy/skills/csharp-qa-gate/SKILL.md +2 -2
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c1.toml +21 -6
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c2.toml +21 -6
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c3-elevated.toml +21 -6
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c3.toml +21 -6
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c4.toml +21 -6
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor.toml +21 -6
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c1.toml +15 -2
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c2.toml +15 -2
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c3-elevated.toml +15 -2
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c3.toml +15 -2
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c4.toml +15 -2
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner.toml +15 -2
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-planning.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/csharp-atomic-planning.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/epic-orchestrator.toml +4 -1
- package/resources/codex-and-agents-customizations/.codex/agents/feature-review.toml +6 -6
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c1.toml +7 -4
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c2.toml +7 -4
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c3-elevated.toml +7 -4
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c3.toml +7 -4
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c4.toml +7 -4
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator.toml +7 -4
- package/resources/codex-and-agents-customizations/.codex/agents/powershell-atomic-planning.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/python-atomic-planning.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/task-researcher-c1.toml +6 -0
- package/resources/codex-and-agents-customizations/.codex/agents/task-researcher-c2.toml +6 -0
- package/resources/codex-and-agents-customizations/.codex/agents/task-researcher-c3-elevated.toml +6 -0
- package/resources/codex-and-agents-customizations/.codex/agents/task-researcher-c3.toml +6 -0
- package/resources/codex-and-agents-customizations/.codex/agents/task-researcher-c4.toml +6 -0
- package/resources/codex-and-agents-customizations/.codex/agents/task-researcher.toml +6 -0
- package/resources/codex-and-agents-customizations/.codex/codex-web-setup.sh +318 -273
- package/resources/codex-and-agents-customizations/.codex/config.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/hooks/check-powershell-test-purity.ps1 +7 -1
- package/resources/codex-and-agents-customizations/.codex/hooks/check-python-test-purity.ps1 +7 -1
- package/resources/codex-and-agents-customizations/.codex/hooks/codex-epic-child-launch-attestation.ps1 +1 -1
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-checkpoint-monotonic.ps1 +7 -1
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-codex-model-routing.ps1 +8 -2
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-completion-consistency.ps1 +97 -44
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-completion-helpers.ps1 +105 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-child-worktree-binding.ps1 +7 -1
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-merge-gate-command.ps1 +190 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-merge-gate.ps1 +54 -43
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-planning-only.ps1 +6 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-root-invocation.ps1 +7 -1
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-wave-barrier.ps1 +7 -1
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-worktree-removal-gate.ps1 +54 -41
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-evidence-locations.ps1 +7 -1
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-epic-scope.ps1 +35 -14
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-helpers.ps1 +31 -58
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-modes.ps1 +63 -47
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-targets.ps1 +433 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate.ps1 +24 -11
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-powershell-batch-budget.ps1 +8 -2
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-promotion-mcp-only.ps1 +13 -4
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-python-batch-budget.ps1 +8 -2
- package/resources/codex-and-agents-customizations/.codex/hooks/feature-folder-resolution.ps1 +373 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/feature-review-coverage-thresholds.ps1 +85 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-heredoc.ps1 +119 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-invocation-operands.ps1 +198 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-invocation.ps1 +254 -255
- package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-payload-powershell.ps1 +201 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-payload.ps1 +484 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-scanner.ps1 +27 -110
- package/resources/codex-and-agents-customizations/.codex/hooks/hook-dependency-guard.ps1 +114 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/hook-docs-only-diff.ps1 +497 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/validate-bash.ps1 +8 -2
- package/resources/codex-and-agents-customizations/.codex/hooks/validate-codex-subagent-routing.ps1 +7 -1
- package/resources/codex-and-agents-customizations/.codex/hooks/validate-feature-review-coverage.ps1 +24 -8
- package/resources/codex-and-agents-customizations/.codex/prompts/feature-review-remediate.md +6 -4
- package/resources/codex-and-agents-customizations/pack-manifests/core.json +11 -0
- package/resources/customizations/.github/agents/atomic_executor.agent.md +21 -11
- package/resources/customizations/.github/agents/atomic_planning.agent.md +8 -3
- package/resources/customizations/.github/agents/csharp-atomic-executor.agent.md +13 -9
- package/resources/customizations/.github/agents/csharp-atomic-planning.agent.md +2 -2
- package/resources/customizations/.github/agents/csharp-orchestrator.agent.md +2 -2
- package/resources/customizations/.github/agents/csharp-typed-engineer.agent.md +5 -5
- package/resources/customizations/.github/agents/orchestrator.agent.md +8 -5
- package/resources/customizations/.github/agents/powershell-atomic-executor.agent.md +13 -9
- package/resources/customizations/.github/agents/powershell-atomic-planning.agent.md +2 -2
- package/resources/customizations/.github/agents/powershell-orchestrator.agent.md +2 -2
- package/resources/customizations/.github/agents/powershell-typed-engineer.agent.md +3 -3
- package/resources/customizations/.github/agents/python-atomic-executor.agent.md +13 -9
- package/resources/customizations/.github/agents/python-atomic-planning.agent.md +2 -2
- package/resources/customizations/.github/agents/python-orchestrator.agent.md +2 -2
- package/resources/customizations/.github/agents/task-researcher.agent.md +6 -0
- package/resources/customizations/.github/instructions/csharp-code-change.instructions.md +4 -4
- package/resources/customizations/.github/instructions/csharp-unit-test.instructions.md +2 -2
- package/resources/customizations/.github/prompts/generate-atomic-plan.prompt.md +16 -10
- package/resources/customizations/.github/prompts/orchestrate-csharp-work.prompt.md +2 -2
- package/resources/customizations/.github/prompts/orchestrate-powershell-work.prompt.md +1 -1
- package/resources/customizations/.github/prompts/orchestrate-python-work.prompt.md +1 -1
- package/resources/customizations/.github/prompts/orchestrate-work.prompt.md +2 -2
- package/resources/customizations/.github/skills/acceptance-criteria-tracking/SKILL.md +1 -1
- package/resources/customizations/.github/skills/atomic-plan-contract/SKILL.md +43 -4
- package/resources/customizations/.github/skills/csharp-change-budget-router/SKILL.md +1 -1
- package/resources/customizations/.github/skills/evidence-and-timestamp-conventions/SKILL.md +10 -1
- package/resources/customizations/.github/skills/execution-environment/SKILL.md +134 -0
- package/resources/customizations/.github/skills/feature-review-workflow/SKILL.md +1 -1
- package/resources/customizations/.github/skills/powershell-change-budget-router/SKILL.md +1 -1
- package/resources/customizations/.github/skills/remediation-handoff-atomic-planner/SKILL.md +7 -3
- package/resources/feature-templates/bug/plan.yyyy-MM-ddTHH-mm.md +10 -0
- package/resources/feature-templates/feature/plan.yyyy-MM-ddTHH-mm.md +11 -0
- package/resources/feature-templates/refactor/plan.yyyy-MM-ddTHH-mm.md +11 -0
- package/resources/powershell/PoshQC/PoshQC.Coverage.psm1 +314 -0
- package/resources/powershell/PoshQC/PoshQC.CoverageMerge.psm1 +163 -0
- package/resources/powershell/PoshQC/PoshQC.Shard.psm1 +296 -0
- package/resources/powershell/PoshQC/PoshQC.Testing.psm1 +48 -32
- package/resources/powershell/PoshQC/PoshQC.psd1 +7 -1
- package/resources/powershell/PoshQC/PoshQC.psm1 +18 -5
- package/resources/powershell/PoshQC/README.md +21 -3
- package/resources/powershell/PoshQC/settings/pester.runsettings.psd1 +5 -319
|
@@ -30,27 +30,29 @@
|
|
|
30
30
|
hookSpecificOutput.permissionDecision='allow' (backward compatibility).
|
|
31
31
|
|
|
32
32
|
Edit tool calls supply only old_string/new_string (a partial patch). An Edit
|
|
33
|
-
call is validated by reading the checkpoint at the Edit's
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
Write
|
|
40
|
-
|
|
33
|
+
call is validated by reading the checkpoint at the Edit's targeted file_path
|
|
34
|
+
through the injectable CheckpointReader seam and applying the old_string ->
|
|
35
|
+
new_string replacement in memory; the resulting content is then evaluated
|
|
36
|
+
like a Write. One occurrence of old_string is replaced unless replace_all is
|
|
37
|
+
true. The hook fails closed: a missing, empty, or unreadable targeted
|
|
38
|
+
checkpoint, an absent or empty old_string, zero or ambiguous occurrences of
|
|
39
|
+
old_string, and a Write with empty content all deny, and the deny reason
|
|
40
|
+
names the cause. Write content (or patched content) that is not valid JSON
|
|
41
|
+
is still allowed, and the hook defers to downstream tools to surface the
|
|
42
|
+
error.
|
|
41
43
|
|
|
42
44
|
.NOTES
|
|
43
45
|
Compatible with PowerShell 7+. Read-only validation gate.
|
|
44
46
|
#>
|
|
45
47
|
[CmdletBinding()]
|
|
46
48
|
param()
|
|
49
|
+
$script:HookDependencyGuardLoadFailed = $false; try { . (Join-Path $PSScriptRoot 'hook-dependency-guard.ps1') } catch { $script:HookDependencyGuardLoadFailed = $true }
|
|
47
50
|
|
|
48
51
|
|
|
49
|
-
Import-Module (Join-Path $PSScriptRoot '../lib/hook-payload/HookPayload.psm1') -Force
|
|
52
|
+
try { Import-Module (Join-Path $PSScriptRoot '../lib/hook-payload/HookPayload.psm1') -Force -ErrorAction Stop } catch { Add-HookDependencyFailure -Name 'HookPayload.psm1' -ErrorRecord $_ }
|
|
50
53
|
# Dot-source the shared validation helpers. Guarded so a missing file produces a
|
|
51
54
|
# clear error and so dot-sourcing this hook in tests loads the helpers too.
|
|
52
|
-
|
|
53
|
-
. $script:CompletionHelpersPath
|
|
55
|
+
try { . (Join-Path $PSScriptRoot 'enforce-completion-helpers.ps1') } catch { Add-HookDependencyFailure -Name 'enforce-completion-helpers.ps1' -ErrorRecord $_ }
|
|
54
56
|
|
|
55
57
|
function ConvertFrom-CheckpointJson {
|
|
56
58
|
<#
|
|
@@ -72,9 +74,10 @@ function Get-CheckpointFileContent {
|
|
|
72
74
|
Reads the on-disk checkpoint content for the read-then-validate Edit path.
|
|
73
75
|
.DESCRIPTION
|
|
74
76
|
Returns the full file text when the path resolves to a file on disk, or
|
|
75
|
-
$null when the file does not exist.
|
|
76
|
-
|
|
77
|
-
|
|
77
|
+
$null when the file does not exist. An existing empty file yields an
|
|
78
|
+
empty string, never $null, so a missing checkpoint and an empty
|
|
79
|
+
checkpoint stay distinct. Tests inject a CheckpointReader scriptblock
|
|
80
|
+
instead of mocking this function so no temporary files are required.
|
|
78
81
|
#>
|
|
79
82
|
[CmdletBinding()]
|
|
80
83
|
[OutputType([string])]
|
|
@@ -86,7 +89,9 @@ function Get-CheckpointFileContent {
|
|
|
86
89
|
if (-not (Test-Path -LiteralPath $Path -PathType Leaf)) {
|
|
87
90
|
return $null
|
|
88
91
|
}
|
|
89
|
-
|
|
92
|
+
$text = Get-Content -LiteralPath $Path -Raw -ErrorAction Stop
|
|
93
|
+
if ($null -eq $text) { return '' }
|
|
94
|
+
return [string]$text
|
|
90
95
|
}
|
|
91
96
|
|
|
92
97
|
function Test-IsCheckpointPath {
|
|
@@ -266,20 +271,24 @@ function Get-MissingCompletionEvidence {
|
|
|
266
271
|
function Resolve-EditedCheckpointContent {
|
|
267
272
|
<#
|
|
268
273
|
.SYNOPSIS
|
|
269
|
-
Returns
|
|
270
|
-
|
|
274
|
+
Returns an object with Content and Failure for an Edit-tool call: the
|
|
275
|
+
patched checkpoint content on success, or a failure cause when the patch
|
|
276
|
+
cannot be applied against the targeted checkpoint.
|
|
271
277
|
.DESCRIPTION
|
|
272
|
-
Implements the read-then-validate Edit path.
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
old_string
|
|
279
|
-
|
|
278
|
+
Implements the read-then-validate Edit path. The checkpoint at the
|
|
279
|
+
caller-supplied CheckpointPath (the Edit's targeted file_path, passed to
|
|
280
|
+
the reader unchanged) is read through the injectable CheckpointReader seam
|
|
281
|
+
and the old_string -> new_string replacement is applied in memory (no
|
|
282
|
+
on-disk mutation). One occurrence of old_string is replaced unless
|
|
283
|
+
replace_all is true. Failure is one of no-old_string (absent or empty
|
|
284
|
+
old_string), checkpoint-unreadable (the reader threw), checkpoint-missing
|
|
285
|
+
(the reader returned $null), checkpoint-empty (the reader returned an
|
|
286
|
+
empty string), old_string-not-found, or old_string-ambiguous. Content is
|
|
287
|
+
$null on failure and Failure is $null on success. The caller denies on
|
|
288
|
+
any Failure.
|
|
280
289
|
#>
|
|
281
290
|
[CmdletBinding()]
|
|
282
|
-
[OutputType([
|
|
291
|
+
[OutputType([pscustomobject])]
|
|
283
292
|
param(
|
|
284
293
|
[Parameter(Mandatory)]
|
|
285
294
|
[AllowNull()]
|
|
@@ -297,7 +306,7 @@ function Resolve-EditedCheckpointContent {
|
|
|
297
306
|
$oldString = [string]$ToolInput.old_string
|
|
298
307
|
}
|
|
299
308
|
if ([string]::IsNullOrEmpty($oldString)) {
|
|
300
|
-
return $null
|
|
309
|
+
return [pscustomobject]@{ Content = $null; Failure = 'no-old_string' }
|
|
301
310
|
}
|
|
302
311
|
|
|
303
312
|
$newString = ''
|
|
@@ -305,20 +314,46 @@ function Resolve-EditedCheckpointContent {
|
|
|
305
314
|
$newString = [string]$ToolInput.new_string
|
|
306
315
|
}
|
|
307
316
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
317
|
+
try {
|
|
318
|
+
$onDisk = & $CheckpointReader $CheckpointPath
|
|
319
|
+
}
|
|
320
|
+
catch {
|
|
321
|
+
# A reader exception must not escape the gate; report it as a failure cause.
|
|
322
|
+
return [pscustomobject]@{ Content = $null; Failure = 'checkpoint-unreadable' }
|
|
323
|
+
}
|
|
324
|
+
if ($null -eq $onDisk) {
|
|
325
|
+
return [pscustomobject]@{ Content = $null; Failure = 'checkpoint-missing' }
|
|
312
326
|
}
|
|
313
327
|
|
|
314
328
|
$onDiskText = [string]$onDisk
|
|
315
|
-
if (
|
|
316
|
-
|
|
317
|
-
return $null
|
|
329
|
+
if ($onDiskText.Length -eq 0) {
|
|
330
|
+
return [pscustomobject]@{ Content = $null; Failure = 'checkpoint-empty' }
|
|
318
331
|
}
|
|
319
332
|
|
|
320
|
-
# Apply the patch in memory
|
|
321
|
-
|
|
333
|
+
# Apply the patch in memory; one occurrence unless replace_all is true.
|
|
334
|
+
$replaceAll = Get-EditReplaceAllFlag -ToolInput $ToolInput
|
|
335
|
+
return Invoke-SingleOccurrenceEdit -Text $onDiskText -OldString $oldString -NewString $newString -ReplaceAll $replaceAll
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
function ConvertTo-CompletionDenyDecision {
|
|
339
|
+
<#
|
|
340
|
+
.SYNOPSIS
|
|
341
|
+
Builds the PreToolUse deny decision for the given reason.
|
|
342
|
+
#>
|
|
343
|
+
[CmdletBinding()]
|
|
344
|
+
[OutputType([System.Collections.Specialized.OrderedDictionary])]
|
|
345
|
+
param(
|
|
346
|
+
[Parameter(Mandatory)]
|
|
347
|
+
[string] $Reason
|
|
348
|
+
)
|
|
349
|
+
|
|
350
|
+
return [ordered]@{
|
|
351
|
+
hookSpecificOutput = [ordered]@{
|
|
352
|
+
hookEventName = 'PreToolUse'
|
|
353
|
+
permissionDecision = 'deny'
|
|
354
|
+
permissionDecisionReason = $Reason
|
|
355
|
+
}
|
|
356
|
+
}
|
|
322
357
|
}
|
|
323
358
|
|
|
324
359
|
function Invoke-CompletionConsistencyDecision {
|
|
@@ -341,6 +376,8 @@ function Invoke-CompletionConsistencyDecision {
|
|
|
341
376
|
[Parameter(Mandatory = $false)]
|
|
342
377
|
[scriptblock] $RoutingMatrixReader
|
|
343
378
|
)
|
|
379
|
+
$dependencyDecision = Get-HookDependencyFailureDecision -HookEvent PreToolUse -ReasonPrefix 'COMPLETION_CONSISTENCY_BLOCKED:'
|
|
380
|
+
if ($null -ne $dependencyDecision) { return $dependencyDecision }
|
|
344
381
|
|
|
345
382
|
$envelope = Resolve-ClaudeHookToolInput -Raw $ToolInputRaw
|
|
346
383
|
if (-not $envelope.IsValid) {
|
|
@@ -366,18 +403,25 @@ function Invoke-CompletionConsistencyDecision {
|
|
|
366
403
|
return [ordered]@{ hookSpecificOutput = [ordered]@{ hookEventName = 'PreToolUse'; permissionDecision = 'allow' } }
|
|
367
404
|
}
|
|
368
405
|
|
|
369
|
-
# Write tool:
|
|
370
|
-
#
|
|
371
|
-
#
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
406
|
+
# Write tool: a present content property is validated directly, and empty
|
|
407
|
+
# content is denied. Edit tool: no content property is supplied, so read the
|
|
408
|
+
# targeted checkpoint through the injectable seam and apply the old_string ->
|
|
409
|
+
# new_string patch in memory (read-then-validate). Any failure cause denies.
|
|
410
|
+
$cause = $null
|
|
411
|
+
if ($toolInput.PSObject.Properties.Name -contains 'content') {
|
|
412
|
+
$content = Get-ClaudeHookToolInputString -ToolInput $toolInput -Name 'content'
|
|
375
413
|
if (-not $content) {
|
|
376
|
-
|
|
377
|
-
# state (missing file or non-matching patch): defer and allow.
|
|
378
|
-
return [ordered]@{ hookSpecificOutput = [ordered]@{ hookEventName = 'PreToolUse'; permissionDecision = 'allow' } }
|
|
414
|
+
$cause = 'write-content-empty'
|
|
379
415
|
}
|
|
380
416
|
}
|
|
417
|
+
else {
|
|
418
|
+
$edit = Resolve-EditedCheckpointContent -ToolInput $toolInput -CheckpointReader $CheckpointReader -CheckpointPath $filePath
|
|
419
|
+
$content = $edit.Content
|
|
420
|
+
$cause = $edit.Failure
|
|
421
|
+
}
|
|
422
|
+
if ($cause) {
|
|
423
|
+
return ConvertTo-CompletionDenyDecision -Reason "COMPLETION_CONSISTENCY_BLOCKED: the canonical checkpoint cannot be deleted, emptied, or replaced through an unresolved patch ($cause)."
|
|
424
|
+
}
|
|
381
425
|
|
|
382
426
|
try {
|
|
383
427
|
$payload = ConvertFrom-CheckpointJson -Json $content
|
|
@@ -411,12 +455,33 @@ function Invoke-CompletionConsistencyDecision {
|
|
|
411
455
|
}
|
|
412
456
|
}
|
|
413
457
|
|
|
458
|
+
function Invoke-CompletionConsistencyEntryPoint {
|
|
459
|
+
<#
|
|
460
|
+
.SYNOPSIS
|
|
461
|
+
Reads the payload and returns the gate decision, failing closed when the read or evaluation throws
|
|
462
|
+
(issue #903, #736 O-4).
|
|
463
|
+
#>
|
|
464
|
+
[CmdletBinding()]
|
|
465
|
+
[OutputType([System.Collections.Specialized.OrderedDictionary])]
|
|
466
|
+
param([scriptblock] $ReadPayload = { Read-ClaudeHookRawPayload })
|
|
467
|
+
|
|
468
|
+
try {
|
|
469
|
+
return Invoke-CompletionConsistencyDecision -ToolInputRaw ([string](& $ReadPayload))
|
|
470
|
+
}
|
|
471
|
+
catch {
|
|
472
|
+
return ConvertTo-CompletionDenyDecision -Reason "COMPLETION_CONSISTENCY_BLOCKED: the gate could not evaluate the payload ($($_.Exception.Message)) and fails closed."
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
|
|
414
476
|
# Guard allows dot-sourcing in tests without executing the entrypoint.
|
|
415
477
|
if ($MyInvocation.InvocationName -eq '.') {
|
|
416
478
|
return
|
|
417
479
|
}
|
|
480
|
+
if ($script:HookDependencyGuardLoadFailed) { [Console]::Error.WriteLine('COMPLETION_CONSISTENCY_BLOCKED: hook-dependency-guard.ps1 failed to load; the gate fails closed.'); exit 2 }
|
|
481
|
+
$dependencyDecision = Get-HookDependencyFailureDecision -HookEvent PreToolUse -ReasonPrefix 'COMPLETION_CONSISTENCY_BLOCKED:'
|
|
482
|
+
if ($null -ne $dependencyDecision) { $dependencyDecision | ConvertTo-Json -Compress -Depth 5 | Write-Output; exit 0 }
|
|
418
483
|
|
|
419
|
-
$decision = Invoke-
|
|
484
|
+
$decision = Invoke-CompletionConsistencyEntryPoint
|
|
420
485
|
|
|
421
486
|
$decision | ConvertTo-Json -Compress -Depth 5 | Write-Output
|
|
422
487
|
|
|
@@ -11,6 +11,12 @@
|
|
|
11
11
|
- Test-IsValidFeatureFolder: rejects sentinel/placeholder feature folders
|
|
12
12
|
and folders not anchored under docs/features/active/<segment>; optionally
|
|
13
13
|
verifies on-disk existence through an injectable scriptblock seam.
|
|
14
|
+
- Get-EditReplaceAllFlag: returns the boolean value of the replace_all
|
|
15
|
+
property of an Edit tool input; the string 'true' is honoured and the
|
|
16
|
+
string 'false' is not.
|
|
17
|
+
- Invoke-SingleOccurrenceEdit: pure single-occurrence patch of a text;
|
|
18
|
+
reports a failure cause instead of replacing when old_string is empty,
|
|
19
|
+
absent, or ambiguous.
|
|
14
20
|
|
|
15
21
|
This script is dot-sourced by enforce-completion-consistency.ps1. It contains
|
|
16
22
|
no entrypoint logic, so dot-sourcing it in tests has no side effects.
|
|
@@ -161,3 +167,102 @@ function Test-RouteRequiresPrGate {
|
|
|
161
167
|
}
|
|
162
168
|
return ([bool]$route.requires_pr_gate -eq $true)
|
|
163
169
|
}
|
|
170
|
+
|
|
171
|
+
function Get-EditReplaceAllFlag {
|
|
172
|
+
<#
|
|
173
|
+
.SYNOPSIS
|
|
174
|
+
Returns the boolean value of the replace_all property of an Edit tool input.
|
|
175
|
+
.DESCRIPTION
|
|
176
|
+
Returns $false when the tool input is null or has no replace_all property.
|
|
177
|
+
Returns the value when it is already a boolean. Returns $true for a string
|
|
178
|
+
whose trimmed, case-insensitive value is 'true'. Returns $false for every
|
|
179
|
+
other value. No [bool] cast is used because [bool]'false' is $true in
|
|
180
|
+
PowerShell.
|
|
181
|
+
#>
|
|
182
|
+
[CmdletBinding()]
|
|
183
|
+
[OutputType([bool])]
|
|
184
|
+
param(
|
|
185
|
+
[Parameter(Mandatory)]
|
|
186
|
+
[AllowNull()]
|
|
187
|
+
$ToolInput
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
if ($null -eq $ToolInput -or -not ($ToolInput.PSObject.Properties.Name -contains 'replace_all')) {
|
|
191
|
+
return $false
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
$value = $ToolInput.replace_all
|
|
195
|
+
if ($value -is [bool]) {
|
|
196
|
+
return $value
|
|
197
|
+
}
|
|
198
|
+
if ($value -is [string]) {
|
|
199
|
+
return $value.Trim() -ieq 'true'
|
|
200
|
+
}
|
|
201
|
+
return $false
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function Invoke-SingleOccurrenceEdit {
|
|
205
|
+
<#
|
|
206
|
+
.SYNOPSIS
|
|
207
|
+
Applies an old_string to new_string patch to a text in memory and reports
|
|
208
|
+
a failure cause when the patch cannot be applied unambiguously.
|
|
209
|
+
.DESCRIPTION
|
|
210
|
+
Pure function with no I/O. Normalizes CRLF to LF in all three strings. An
|
|
211
|
+
empty OldString yields Failure 'no-old_string'. Zero ordinal occurrences
|
|
212
|
+
yield Failure 'old_string-not-found'. When ReplaceAll is true every
|
|
213
|
+
occurrence is replaced by ordinal String.Replace. Otherwise a second
|
|
214
|
+
ordinal search that starts after the end of the first match yields Failure
|
|
215
|
+
'old_string-ambiguous' when it finds a match (overlapping candidates are
|
|
216
|
+
counted once), and no match yields a single splice by Substring. No regular
|
|
217
|
+
expression is used, so a '$' in NewString stays literal. Returns an object
|
|
218
|
+
with Content and Failure; Content is $null on failure and Failure is $null
|
|
219
|
+
on success.
|
|
220
|
+
#>
|
|
221
|
+
[CmdletBinding()]
|
|
222
|
+
[OutputType([pscustomobject])]
|
|
223
|
+
param(
|
|
224
|
+
[Parameter(Mandatory)]
|
|
225
|
+
[AllowNull()]
|
|
226
|
+
[AllowEmptyString()]
|
|
227
|
+
[string] $Text,
|
|
228
|
+
|
|
229
|
+
[Parameter(Mandatory)]
|
|
230
|
+
[AllowNull()]
|
|
231
|
+
[AllowEmptyString()]
|
|
232
|
+
[string] $OldString,
|
|
233
|
+
|
|
234
|
+
[Parameter(Mandatory)]
|
|
235
|
+
[AllowNull()]
|
|
236
|
+
[AllowEmptyString()]
|
|
237
|
+
[string] $NewString,
|
|
238
|
+
|
|
239
|
+
[Parameter(Mandatory = $false)]
|
|
240
|
+
[bool] $ReplaceAll = $false
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
$normalizedText = $Text.Replace("`r`n", "`n")
|
|
244
|
+
$normalizedOld = $OldString.Replace("`r`n", "`n")
|
|
245
|
+
$normalizedNew = $NewString.Replace("`r`n", "`n")
|
|
246
|
+
|
|
247
|
+
if ($normalizedOld.Length -eq 0) {
|
|
248
|
+
return [pscustomobject]@{ Content = $null; Failure = 'no-old_string' }
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
$comparison = [System.StringComparison]::Ordinal
|
|
252
|
+
$firstIndex = $normalizedText.IndexOf($normalizedOld, $comparison)
|
|
253
|
+
if ($firstIndex -lt 0) {
|
|
254
|
+
return [pscustomobject]@{ Content = $null; Failure = 'old_string-not-found' }
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
if ($ReplaceAll) {
|
|
258
|
+
return [pscustomobject]@{ Content = $normalizedText.Replace($normalizedOld, $normalizedNew); Failure = $null }
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
$afterFirst = $firstIndex + $normalizedOld.Length
|
|
262
|
+
if ($normalizedText.IndexOf($normalizedOld, $afterFirst, $comparison) -ge 0) {
|
|
263
|
+
return [pscustomobject]@{ Content = $null; Failure = 'old_string-ambiguous' }
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
$spliced = $normalizedText.Substring(0, $firstIndex) + $normalizedNew + $normalizedText.Substring($afterFirst)
|
|
267
|
+
return [pscustomobject]@{ Content = $spliced; Failure = $null }
|
|
268
|
+
}
|
|
@@ -32,9 +32,11 @@
|
|
|
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 $_ }
|
|
39
|
+
try { Import-Module (Join-Path $PSScriptRoot '../lib/discovery-validation/DiscoveryValidation.psm1') -Force -Global -ErrorAction Stop } catch { Add-HookDependencyFailure -Name 'DiscoveryValidation.psm1' -ErrorRecord $_ }
|
|
38
40
|
function Invoke-DiscoveryValidatorExe {
|
|
39
41
|
<#
|
|
40
42
|
.SYNOPSIS
|
|
@@ -164,6 +166,8 @@ function Invoke-DiscoveryArtifactGateDecision {
|
|
|
164
166
|
[Parameter(Mandatory = $false)]
|
|
165
167
|
[scriptblock] $RequiredArtifactReader = { Get-RequiredDiscoveryArtifactDeclaration }
|
|
166
168
|
)
|
|
169
|
+
$dependencyDecision = Get-HookDependencyFailureDecision -HookEvent PreToolUse -ReasonPrefix 'DISCOVERY_ARTIFACT_GATE_BLOCKED:'
|
|
170
|
+
if ($null -ne $dependencyDecision) { return $dependencyDecision }
|
|
167
171
|
|
|
168
172
|
$envelope = Resolve-ClaudeHookToolInput -Raw $ToolInputRaw
|
|
169
173
|
if (-not $envelope.IsValid) {
|
|
@@ -224,6 +228,9 @@ function Invoke-DiscoveryArtifactGateDecision {
|
|
|
224
228
|
if ($MyInvocation.InvocationName -eq '.') {
|
|
225
229
|
return
|
|
226
230
|
}
|
|
231
|
+
if ($script:HookDependencyGuardLoadFailed) { [Console]::Error.WriteLine('DISCOVERY_ARTIFACT_GATE_BLOCKED: hook-dependency-guard.ps1 failed to load; the gate fails closed.'); exit 2 }
|
|
232
|
+
$dependencyDecision = Get-HookDependencyFailureDecision -HookEvent PreToolUse -ReasonPrefix 'DISCOVERY_ARTIFACT_GATE_BLOCKED:'
|
|
233
|
+
if ($null -ne $dependencyDecision) { $dependencyDecision | ConvertTo-Json -Compress -Depth 5 | Write-Output; exit 0 }
|
|
227
234
|
|
|
228
235
|
$decision = Invoke-DiscoveryArtifactGateDecision -ToolInputRaw (Read-ClaudeHookRawPayload)
|
|
229
236
|
|
|
@@ -42,9 +42,10 @@
|
|
|
42
42
|
#>
|
|
43
43
|
[CmdletBinding()]
|
|
44
44
|
param()
|
|
45
|
+
$script:HookDependencyGuardLoadFailed = $false; try { . (Join-Path $PSScriptRoot 'hook-dependency-guard.ps1') } catch { $script:HookDependencyGuardLoadFailed = $true }
|
|
45
46
|
|
|
46
47
|
|
|
47
|
-
Import-Module (Join-Path $PSScriptRoot '../lib/hook-payload/HookPayload.psm1') -Force
|
|
48
|
+
try { Import-Module (Join-Path $PSScriptRoot '../lib/hook-payload/HookPayload.psm1') -Force -ErrorAction Stop } catch { Add-HookDependencyFailure -Name 'HookPayload.psm1' -ErrorRecord $_ }
|
|
48
49
|
$script:GatedSubagentTypes = @('epic-planner', 'epic-orchestrator', 'parallel-planner', 'parallel-orchestrator')
|
|
49
50
|
$script:ParallelSubagentTypes = @('parallel-planner', 'parallel-orchestrator')
|
|
50
51
|
$script:ProhibitedCallerAgentType = 'orchestrator'
|
|
@@ -214,6 +215,8 @@ function Invoke-EpicInvocationOriginDecision {
|
|
|
214
215
|
[AllowEmptyString()]
|
|
215
216
|
[string] $ToolInputRaw
|
|
216
217
|
)
|
|
218
|
+
$dependencyDecision = Get-HookDependencyFailureDecision -HookEvent PreToolUse -ReasonPrefix 'EPIC_INVOCATION_ORIGIN_BLOCKED:'
|
|
219
|
+
if ($null -ne $dependencyDecision) { return $dependencyDecision }
|
|
217
220
|
|
|
218
221
|
# The envelope carries both halves: the caller's agent_type at the root and the
|
|
219
222
|
# delegation target's subagent_type inside tool_input.
|
|
@@ -272,6 +275,9 @@ function Invoke-EpicInvocationOriginDecision {
|
|
|
272
275
|
if ($MyInvocation.InvocationName -eq '.') {
|
|
273
276
|
return
|
|
274
277
|
}
|
|
278
|
+
if ($script:HookDependencyGuardLoadFailed) { [Console]::Error.WriteLine('EPIC_INVOCATION_ORIGIN_BLOCKED: hook-dependency-guard.ps1 failed to load; the gate fails closed.'); exit 2 }
|
|
279
|
+
$dependencyDecision = Get-HookDependencyFailureDecision -HookEvent PreToolUse -ReasonPrefix 'EPIC_INVOCATION_ORIGIN_BLOCKED:'
|
|
280
|
+
if ($null -ne $dependencyDecision) { $dependencyDecision | ConvertTo-Json -Compress -Depth 5 | Write-Output; exit 0 }
|
|
275
281
|
|
|
276
282
|
$decision = Invoke-EpicInvocationOriginDecision -HookInputRaw (Read-ClaudeHookRawPayload)
|
|
277
283
|
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
<#
|
|
2
|
+
.SYNOPSIS
|
|
3
|
+
Scope predicate and pull-request selector reader for the epic merge gate (issue #952).
|
|
4
|
+
.DESCRIPTION
|
|
5
|
+
Dot-sourced by enforce-epic-merge-gate.ps1 on both runtimes; this file is byte-identical in
|
|
6
|
+
.claude/hooks and .codex/hooks. Under a merge queue every gh pr merge spelling (--merge, -m,
|
|
7
|
+
--squash, -s, --rebase, -r, --auto, --admin, or no method flag) adds the pull request to the
|
|
8
|
+
queue, and the gh api mutations enqueuePullRequest, enablePullRequestAutoMerge, and
|
|
9
|
+
mergePullRequest and the REST pulls/<n>/merge endpoint also reach the base branch. All of
|
|
10
|
+
them are in scope. A gh pr merge whose only action flag is --disable-auto is out of scope.
|
|
11
|
+
|
|
12
|
+
Pure string logic only: no disk, process, network, clock, or environment access. The file
|
|
13
|
+
dot-sources and imports nothing; it consumes Get-CommandLineInvocation and
|
|
14
|
+
Get-CommandLineStructuralMatch, which the gate loads from hook-command-invocation.ps1 first.
|
|
15
|
+
|
|
16
|
+
Known limitation (issue #952 code review CR-5): the gh api scope reads the command text
|
|
17
|
+
only. A GraphQL mutation supplied from a file (gh api graphql -F query=@mutation.graphql,
|
|
18
|
+
or gh api --input payload.json) is not visible to this text-based scope and is not gated.
|
|
19
|
+
#>
|
|
20
|
+
|
|
21
|
+
# gh pr merge flags that perform or schedule a merge. Any of them keeps a command in scope even
|
|
22
|
+
# when it also carries --disable-auto.
|
|
23
|
+
$script:EpicMergeGateActionFlags = @('--merge', '-m', '--squash', '-s', '--rebase', '-r', '--auto', '--admin')
|
|
24
|
+
|
|
25
|
+
# gh pr merge flags, including the inherited --repo, that consume the following token as a value.
|
|
26
|
+
$script:EpicMergeGateValueFlags = @('-b', '--body', '-F', '--body-file', '-t', '--subject', '-A', '--author-email', '--match-head-commit', '-R', '--repo')
|
|
27
|
+
|
|
28
|
+
# GraphQL mutations that merge, enqueue, or schedule the merge of a pull request.
|
|
29
|
+
$script:EpicMergeGateMutationNames = @('enqueuePullRequest', 'enablePullRequestAutoMerge', 'mergePullRequest')
|
|
30
|
+
|
|
31
|
+
# The REST merge endpoint path; group 1 is the pull-request number.
|
|
32
|
+
$script:EpicMergeGateRestMergePattern = '(?:^|/)pulls/(\d+)/merge(?:$|[^\w-])'
|
|
33
|
+
|
|
34
|
+
function Get-EpicMergeGateFlagName {
|
|
35
|
+
<#
|
|
36
|
+
.SYNOPSIS
|
|
37
|
+
Return the flag name of a dash-led token without any '=value' suffix.
|
|
38
|
+
.OUTPUTS
|
|
39
|
+
System.String
|
|
40
|
+
#>
|
|
41
|
+
[CmdletBinding()]
|
|
42
|
+
[OutputType([string])]
|
|
43
|
+
param([Parameter(Mandatory)][AllowEmptyString()][string] $Token)
|
|
44
|
+
|
|
45
|
+
$split = $Token.IndexOf('=')
|
|
46
|
+
if ($split -gt 0) { return $Token.Substring(0, $split) }
|
|
47
|
+
return $Token
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function ConvertFrom-EpicMergeGateSelector {
|
|
51
|
+
<#
|
|
52
|
+
.SYNOPSIS
|
|
53
|
+
Convert a gh pr merge selector to a pull-request number, or $null.
|
|
54
|
+
.DESCRIPTION
|
|
55
|
+
Accepts an all-digit selector or a URL whose path ends in /pull/<n>. A branch-name
|
|
56
|
+
selector carries no number and yields $null.
|
|
57
|
+
.OUTPUTS
|
|
58
|
+
System.Nullable[int]
|
|
59
|
+
#>
|
|
60
|
+
[CmdletBinding()]
|
|
61
|
+
[OutputType([int])]
|
|
62
|
+
param([Parameter(Mandatory)][AllowEmptyString()][string] $Selector)
|
|
63
|
+
|
|
64
|
+
$digits = $null
|
|
65
|
+
if ($Selector -match '^\d+$') {
|
|
66
|
+
$digits = $Selector
|
|
67
|
+
} elseif ($Selector -match '/pull/(\d+)/?(?:[?#].*)?$') {
|
|
68
|
+
$digits = $Matches[1]
|
|
69
|
+
}
|
|
70
|
+
$number = 0
|
|
71
|
+
if ($null -ne $digits -and [int]::TryParse($digits, [ref] $number)) { return $number }
|
|
72
|
+
return $null
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function Test-EpicMergeGateDisableAutoOnly {
|
|
76
|
+
<#
|
|
77
|
+
.SYNOPSIS
|
|
78
|
+
Report whether a Structural gh pr merge match carries --disable-auto and no action flag.
|
|
79
|
+
.OUTPUTS
|
|
80
|
+
System.Boolean
|
|
81
|
+
#>
|
|
82
|
+
[CmdletBinding()]
|
|
83
|
+
[OutputType([bool])]
|
|
84
|
+
param([Parameter(Mandatory)][pscustomobject] $Match)
|
|
85
|
+
|
|
86
|
+
$tokens = [string[]]@($Match.Segment.Tokens)
|
|
87
|
+
$hasDisableAuto = $false
|
|
88
|
+
for ($index = $Match.OperandIndex; $index -lt $tokens.Count; $index++) {
|
|
89
|
+
$name = Get-EpicMergeGateFlagName -Token $tokens[$index]
|
|
90
|
+
if ($script:EpicMergeGateActionFlags -ccontains $name) { return $false }
|
|
91
|
+
if ($name -ceq '--disable-auto') { $hasDisableAuto = $true }
|
|
92
|
+
}
|
|
93
|
+
return $hasDisableAuto
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function Test-EpicMergeGateApiMutationText {
|
|
97
|
+
<#
|
|
98
|
+
.SYNOPSIS
|
|
99
|
+
Report whether gh api text names a merge or enqueue mutation or the REST merge endpoint.
|
|
100
|
+
.OUTPUTS
|
|
101
|
+
System.Boolean
|
|
102
|
+
#>
|
|
103
|
+
[CmdletBinding()]
|
|
104
|
+
[OutputType([bool])]
|
|
105
|
+
param([Parameter(Mandatory)][AllowEmptyString()][AllowNull()][string] $Text)
|
|
106
|
+
|
|
107
|
+
if ([string]::IsNullOrEmpty($Text)) { return $false }
|
|
108
|
+
foreach ($name in $script:EpicMergeGateMutationNames) {
|
|
109
|
+
if ($Text.IndexOf($name, [System.StringComparison]::OrdinalIgnoreCase) -ge 0) { return $true }
|
|
110
|
+
}
|
|
111
|
+
return [regex]::IsMatch($Text, $script:EpicMergeGateRestMergePattern)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function Test-EpicMergeGateCommandInScope {
|
|
115
|
+
<#
|
|
116
|
+
.SYNOPSIS
|
|
117
|
+
Report whether a command line is a gated merge or enqueue action (issue #952).
|
|
118
|
+
.DESCRIPTION
|
|
119
|
+
True for any gh pr merge match, Structural or Indeterminate, unless the match is Structural
|
|
120
|
+
and its only action flag is --disable-auto; true for any gh api match whose tokens name a
|
|
121
|
+
merge or enqueue mutation or a pulls/<n>/merge path. Text that only mentions these words
|
|
122
|
+
produces no match and stays out of scope. An Indeterminate gh pr merge match stays in scope
|
|
123
|
+
(fail closed).
|
|
124
|
+
.OUTPUTS
|
|
125
|
+
System.Boolean
|
|
126
|
+
#>
|
|
127
|
+
[CmdletBinding()]
|
|
128
|
+
[OutputType([bool])]
|
|
129
|
+
param([Parameter(Mandatory)][AllowEmptyString()][AllowNull()][string] $CommandText)
|
|
130
|
+
|
|
131
|
+
foreach ($match in @(Get-CommandLineInvocation -CommandText $CommandText -CommandWord 'gh' -SubcommandPath @('pr', 'merge'))) {
|
|
132
|
+
if ($match.Status -ne 'Structural' -or -not (Test-EpicMergeGateDisableAutoOnly -Match $match)) { return $true }
|
|
133
|
+
}
|
|
134
|
+
foreach ($match in @(Get-CommandLineInvocation -CommandText $CommandText -CommandWord 'gh' -SubcommandPath @('api'))) {
|
|
135
|
+
if (Test-EpicMergeGateApiMutationText -Text (@($match.Segment.Tokens) -join ' ')) { return $true }
|
|
136
|
+
}
|
|
137
|
+
return $false
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function Get-EpicMergeGateSelectorPrNumber {
|
|
141
|
+
<#
|
|
142
|
+
.SYNOPSIS
|
|
143
|
+
Resolve the pull-request number of a gated merge or enqueue command, or $null.
|
|
144
|
+
.DESCRIPTION
|
|
145
|
+
For gh pr merge, only the first Structural match is read. Value flags are skipped with
|
|
146
|
+
their values, other flags are skipped, an equals-joined --merge=<digits> yields its
|
|
147
|
+
digits (an equals-joined value that is not a number is skipped like any other flag),
|
|
148
|
+
and the first non-flag token (or the token after a bare '--') is the selector.
|
|
149
|
+
For gh api, the number comes from a pulls/<n>/merge token. Any other command yields
|
|
150
|
+
$null, which the gate handles through its no-number branches.
|
|
151
|
+
.OUTPUTS
|
|
152
|
+
System.Nullable[int]
|
|
153
|
+
#>
|
|
154
|
+
[CmdletBinding()]
|
|
155
|
+
[OutputType([int])]
|
|
156
|
+
param([Parameter(Mandatory)][AllowEmptyString()][AllowNull()][string] $CommandText)
|
|
157
|
+
|
|
158
|
+
$match = Get-CommandLineStructuralMatch -CommandText $CommandText -CommandWord 'gh' -SubcommandPath @('pr', 'merge')
|
|
159
|
+
if ($null -ne $match) {
|
|
160
|
+
$tokens = [string[]]@($match.Segment.Tokens)
|
|
161
|
+
$index = $match.OperandIndex
|
|
162
|
+
while ($index -lt $tokens.Count) {
|
|
163
|
+
$token = $tokens[$index]
|
|
164
|
+
if ($token -ceq '--') {
|
|
165
|
+
if ($index + 1 -lt $tokens.Count) { return ConvertFrom-EpicMergeGateSelector -Selector $tokens[$index + 1] }
|
|
166
|
+
return $null
|
|
167
|
+
}
|
|
168
|
+
if ($token.StartsWith('-') -and $token.Length -gt 1) {
|
|
169
|
+
$name = Get-EpicMergeGateFlagName -Token $token
|
|
170
|
+
if ($name -ceq '--merge' -and $token.Length -gt $name.Length) {
|
|
171
|
+
$equalsNumber = ConvertFrom-EpicMergeGateSelector -Selector $token.Substring($name.Length + 1)
|
|
172
|
+
if ($null -ne $equalsNumber) { return $equalsNumber }
|
|
173
|
+
}
|
|
174
|
+
$index += if ($script:EpicMergeGateValueFlags -ccontains $token) { 2 } else { 1 }
|
|
175
|
+
continue
|
|
176
|
+
}
|
|
177
|
+
return ConvertFrom-EpicMergeGateSelector -Selector $token
|
|
178
|
+
}
|
|
179
|
+
return $null
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
foreach ($apiMatch in @(Get-CommandLineInvocation -CommandText $CommandText -CommandWord 'gh' -SubcommandPath @('api'))) {
|
|
183
|
+
foreach ($token in @($apiMatch.Segment.Tokens)) {
|
|
184
|
+
$rest = [regex]::Match([string]$token, $script:EpicMergeGateRestMergePattern)
|
|
185
|
+
$number = 0
|
|
186
|
+
if ($rest.Success -and [int]::TryParse($rest.Groups[1].Value, [ref] $number)) { return $number }
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return $null
|
|
190
|
+
}
|