@danmoisan/drm-copilot-mcp 0.0.1 → 0.0.5
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 +5 -1
- package/package.json +21 -5
- package/resources/claude-customizations/.claude/agent-memory/orchestrator/MEMORY.md +15 -3
- package/resources/claude-customizations/.claude/agent-memory/orchestrator/feedback_branch_base_check_unmerged_pr_deps.md +16 -0
- package/resources/claude-customizations/.claude/agent-memory/orchestrator/feedback_every_change_through_lifecycle.md +15 -0
- package/resources/claude-customizations/.claude/agent-memory/orchestrator/feedback_policy_compliance_not_optional.md +18 -0
- package/resources/claude-customizations/.claude/agent-memory/orchestrator/feedback_potential_to_issue_creates_github_issue.md +13 -0
- package/resources/claude-customizations/.claude/agent-memory/orchestrator/feedback_remediation_plan_em_dash_required.md +13 -0
- package/resources/claude-customizations/.claude/agent-memory/orchestrator/feedback_small_bug_uses_minor_audit.md +13 -0
- package/resources/claude-customizations/.claude/agent-memory/orchestrator/feedback_test_files_count_against_500_cap.md +13 -0
- package/resources/claude-customizations/.claude/agents/atomic-executor.md +7 -7
- package/resources/claude-customizations/.claude/agents/csharp-typed-engineer.md +4 -5
- package/resources/claude-customizations/.claude/agents/feature-review.md +7 -3
- package/resources/claude-customizations/.claude/agents/orchestrator.md +16 -1
- package/resources/claude-customizations/.claude/agents/powershell-typed-engineer.md +1 -1
- package/resources/claude-customizations/.claude/hooks/enforce-checkpoint-monotonic.ps1 +245 -0
- package/resources/claude-customizations/.claude/hooks/enforce-completion-consistency.ps1 +273 -0
- package/resources/claude-customizations/.claude/hooks/enforce-feature-folder-order.ps1 +148 -0
- package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.ps1 +190 -0
- package/resources/claude-customizations/.claude/hooks/enforce-prd-feature-before-planner.ps1 +216 -0
- package/resources/claude-customizations/.claude/hooks/enforce-promotion-mcp-only.ps1 +84 -15
- package/resources/claude-customizations/.claude/hooks/validate-executor-output.ps1 +1 -1
- package/resources/claude-customizations/.claude/hooks/validate-feature-review-coverage.ps1 +75 -5
- package/resources/claude-customizations/.claude/hooks/validate-orchestrator-output.ps1 +93 -0
- package/resources/claude-customizations/.claude/hooks/validate-task-researcher-output.ps1 +68 -0
- package/resources/claude-customizations/.claude/rules/architecture-boundaries.md +46 -0
- package/resources/claude-customizations/.claude/rules/benchmark-baselines.md +35 -0
- package/resources/claude-customizations/.claude/rules/ci-workflows.md +36 -0
- package/resources/claude-customizations/.claude/rules/csharp.md +62 -16
- package/resources/claude-customizations/.claude/rules/general-code-change.md +12 -3
- package/resources/claude-customizations/.claude/rules/general-unit-test.md +47 -2
- package/resources/claude-customizations/.claude/rules/orchestrator-state.md +39 -0
- package/resources/claude-customizations/.claude/rules/powershell.md +5 -5
- package/resources/claude-customizations/.claude/rules/python.md +4 -3
- package/resources/claude-customizations/.claude/rules/quality-tiers.md +51 -0
- package/resources/claude-customizations/.claude/rules/typescript.md +37 -8
- package/resources/claude-customizations/.claude/settings.json +37 -12
- package/resources/claude-customizations/.claude/skills/atomic-plan-contract/SKILL.md +2 -2
- package/resources/claude-customizations/.claude/skills/csharp-qa-gate/SKILL.md +25 -10
- package/resources/claude-customizations/.claude/skills/execute-hard-lock/SKILL.md +6 -6
- package/resources/claude-customizations/.claude/skills/feature-promotion-lifecycle/SKILL.md +8 -8
- package/resources/claude-customizations/.claude/skills/feature-review-workflow/SKILL.md +17 -6
- package/resources/claude-customizations/.claude/skills/human-exception-runbook/SKILL.md +52 -0
- package/resources/claude-customizations/.claude/skills/human-exception-runbook/example.runbook.md +36 -0
- package/resources/claude-customizations/.claude/skills/invoke-csharp-engineer/SKILL.md +4 -4
- package/resources/claude-customizations/.claude/skills/orchestrate/SKILL.md +96 -3
- package/resources/claude-customizations/.claude/skills/policy-audit-template-usage/SKILL.md +3 -3
- package/resources/claude-customizations/.claude/skills/powershell-qa-gate/SKILL.md +4 -4
- package/resources/claude-customizations/.claude/skills/pr-base-branch-merge-base/SKILL.md +3 -3
- package/resources/claude-customizations/.claude/skills/python-qa-gate/SKILL.md +1 -1
- package/resources/claude-customizations/.claude/skills/remediation-handoff-atomic-planner/SKILL.md +90 -17
- package/resources/claude-dir-customizations/.mcp.json +3 -3
- package/resources/codex-and-agents-customizations/.agents/README.md +1 -1
- package/resources/codex-and-agents-customizations/.agents/skills/acceptance-criteria-tracking/SKILL.md +0 -5
- package/resources/codex-and-agents-customizations/.agents/skills/architecture-boundaries/SKILL.md +52 -0
- package/resources/codex-and-agents-customizations/.agents/skills/atomic-plan-contract/SKILL.md +16 -8
- package/resources/codex-and-agents-customizations/.agents/skills/benchmark-baselines/SKILL.md +44 -0
- package/resources/codex-and-agents-customizations/.agents/skills/ci-workflows/SKILL.md +45 -0
- package/resources/codex-and-agents-customizations/.agents/skills/commit-message/SKILL.md +3 -11
- package/resources/codex-and-agents-customizations/.agents/skills/csharp/SKILL.md +1 -5
- package/resources/codex-and-agents-customizations/.agents/skills/csharp-change-budget-router/SKILL.md +1 -6
- package/resources/codex-and-agents-customizations/.agents/skills/csharp-orchestration-state-machine/SKILL.md +0 -5
- package/resources/codex-and-agents-customizations/.agents/skills/csharp-qa-gate/SKILL.md +0 -5
- package/resources/codex-and-agents-customizations/.agents/skills/evidence-and-timestamp-conventions/SKILL.md +0 -5
- package/resources/codex-and-agents-customizations/.agents/skills/execute-hard-lock/SKILL.md +8 -17
- package/resources/codex-and-agents-customizations/.agents/skills/feature-promotion-lifecycle/SKILL.md +13 -14
- package/resources/codex-and-agents-customizations/.agents/skills/feature-review-workflow/SKILL.md +1 -6
- package/resources/codex-and-agents-customizations/.agents/skills/fill-feature-docs/SKILL.md +0 -5
- package/resources/codex-and-agents-customizations/.agents/skills/general-code-change/SKILL.md +86 -0
- package/resources/codex-and-agents-customizations/.agents/skills/general-unit-test/SKILL.md +111 -0
- package/resources/codex-and-agents-customizations/.agents/skills/human-exception-runbook/SKILL.md +57 -0
- package/resources/codex-and-agents-customizations/.agents/skills/human-exception-runbook/example.runbook.md +36 -0
- package/resources/codex-and-agents-customizations/.agents/skills/invoke-csharp-engineer/SKILL.md +0 -9
- package/resources/codex-and-agents-customizations/.agents/skills/invoke-powershell-engineer/SKILL.md +0 -9
- package/resources/codex-and-agents-customizations/.agents/skills/invoke-python-engineer/SKILL.md +0 -9
- package/resources/codex-and-agents-customizations/.agents/skills/make-skill-template/SKILL.md +0 -5
- package/resources/codex-and-agents-customizations/.agents/skills/orchestrate/SKILL.md +93 -8
- package/resources/codex-and-agents-customizations/.agents/skills/orchestrator-state/SKILL.md +48 -0
- package/resources/codex-and-agents-customizations/.agents/skills/orchestrator-workflow/SKILL.md +61 -2
- package/resources/codex-and-agents-customizations/.agents/skills/policy-audit-template-usage/SKILL.md +3 -8
- package/resources/codex-and-agents-customizations/.agents/skills/policy-compliance-order/SKILL.md +0 -10
- package/resources/codex-and-agents-customizations/.agents/skills/powershell/SKILL.md +4 -8
- package/resources/codex-and-agents-customizations/.agents/skills/powershell-change-budget-router/SKILL.md +1 -6
- package/resources/codex-and-agents-customizations/.agents/skills/powershell-orchestration-state-machine/SKILL.md +0 -5
- package/resources/codex-and-agents-customizations/.agents/skills/powershell-qa-gate/SKILL.md +3 -9
- package/resources/codex-and-agents-customizations/.agents/skills/pr-author/SKILL.md +1 -9
- package/resources/codex-and-agents-customizations/.agents/skills/pr-base-branch-merge-base/SKILL.md +4 -9
- package/resources/codex-and-agents-customizations/.agents/skills/pr-context-artifacts/SKILL.md +0 -5
- package/resources/codex-and-agents-customizations/.agents/skills/python/SKILL.md +1 -5
- package/resources/codex-and-agents-customizations/.agents/skills/python-change-budget-router/SKILL.md +1 -6
- package/resources/codex-and-agents-customizations/.agents/skills/python-qa-gate/SKILL.md +0 -5
- package/resources/codex-and-agents-customizations/.agents/skills/python-suppressions/SKILL.md +2 -6
- package/resources/codex-and-agents-customizations/.agents/skills/quality-tiers/SKILL.md +57 -0
- package/resources/codex-and-agents-customizations/.agents/skills/remediation-handoff-atomic-planner/SKILL.md +0 -5
- package/resources/codex-and-agents-customizations/.agents/skills/repo-automation-adapter/SKILL.md +91 -72
- package/resources/codex-and-agents-customizations/.agents/skills/repo-automation-adapter/agents/openai.yaml +1 -1
- package/resources/codex-and-agents-customizations/.agents/skills/research-issue/SKILL.md +0 -10
- package/resources/codex-and-agents-customizations/.agents/skills/review-epic/SKILL.md +0 -5
- package/resources/codex-and-agents-customizations/.agents/skills/review-feature/SKILL.md +0 -5
- package/resources/codex-and-agents-customizations/.agents/skills/review-staged/SKILL.md +0 -5
- package/resources/codex-and-agents-customizations/.agents/skills/self-explanatory-code-commenting/SKILL.md +2 -6
- package/resources/codex-and-agents-customizations/.agents/skills/skill-canonical-location-audit/SKILL.md +0 -5
- package/resources/codex-and-agents-customizations/.agents/skills/tonality/SKILL.md +86 -0
- package/resources/codex-and-agents-customizations/.agents/skills/translate-claude-to-codex/SKILL.md +297 -0
- package/resources/codex-and-agents-customizations/.agents/skills/translate-copilot-to-claude/SKILL.md +0 -22
- package/resources/codex-and-agents-customizations/.agents/skills/typescript/SKILL.md +1 -5
- package/resources/codex-and-agents-customizations/.agents/skills/typescript-suppressions/SKILL.md +2 -6
- package/resources/codex-and-agents-customizations/.agents/skills/update-status/SKILL.md +0 -5
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor.toml +5 -5
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator.toml +91 -63
- package/resources/codex-and-agents-customizations/.codex/agents/powershell-atomic-executor.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/config.toml +51 -136
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-promotion-mcp-only.ps1 +1 -1
- package/resources/codex-and-agents-customizations/.codex/prompts/orchestrate-work.md +4 -3
- package/resources/codex-and-agents-customizations/.codex/scripts/post-codex-worktree-session.ps1 +5 -0
- package/resources/codex-and-agents-customizations/.github/workflows/_validate-orchestrator-state.yml +68 -0
- package/resources/codex-and-agents-customizations/.github/workflows/validate-orchestrator-state.yml +15 -0
- package/resources/config/orchestration-routing.json +84 -0
- package/resources/customizations/.github/agents/Powershell DI Unit Test Engineer.agent.md +1 -1
- package/resources/customizations/.github/agents/atomic_executor.agent.md +1 -1
- package/resources/customizations/.github/agents/atomic_planning.agent.md +10 -10
- package/resources/customizations/.github/agents/csharp-orchestrator.agent.md +6 -2
- package/resources/customizations/.github/agents/feature-review.agent.md +2 -2
- package/resources/customizations/.github/agents/orchestrator.agent.md +6 -2
- package/resources/customizations/.github/agents/powershell-atomic-executor.agent.md +4 -4
- package/resources/customizations/.github/agents/powershell-atomic-planning.agent.md +10 -10
- package/resources/customizations/.github/agents/powershell-orchestrator.agent.md +6 -2
- package/resources/customizations/.github/agents/powershell-typed-engineer.agent.md +2 -2
- package/resources/customizations/.github/agents/python-orchestrator.agent.md +6 -2
- package/resources/customizations/.github/agents/staged-review.agent.md +1 -1
- package/resources/customizations/.github/instructions/powershell-code-change.instructions.md +6 -6
- package/resources/customizations/.github/prompts/generate-commit-message-repo.prompt.md +1 -1
- package/resources/customizations/.github/prompts/orchestrate-csharp-work.prompt.md +5 -3
- package/resources/customizations/.github/prompts/orchestrate-work.prompt.md +5 -3
- package/resources/customizations/.github/skills/atomic-plan-contract/SKILL.md +14 -1
- package/resources/customizations/.github/skills/feature-promotion-lifecycle/SKILL.md +11 -7
- package/resources/customizations/.github/skills/feature-review-workflow/SKILL.md +10 -1
- package/resources/customizations/.github/skills/pr-base-branch-merge-base/SKILL.md +2 -2
- package/resources/customizations/.github/skills/remediation-handoff-atomic-planner/SKILL.md +5 -0
- package/resources/powershell/PoshQC/settings/pester.runsettings.psd1 +7 -0
- package/resources/scripts/dev_tools/_orchestrator_state_human_interaction.py +127 -0
- package/resources/scripts/dev_tools/_orchestrator_state_routing.py +216 -0
- package/resources/scripts/dev_tools/push_down_claude_customizations.py +191 -5
- package/resources/scripts/dev_tools/validate_orchestration_artifacts.py +103 -411
- package/resources/scripts/dev_tools/validate_orchestration_review_artifacts.py +107 -0
- package/resources/scripts/dev_tools/validate_orchestrator_state.py +428 -0
- package/resources/scripts/dev_tools/validate_policy_audit_artifact.py +448 -0
- package/resources/templates/push_down_claude_customizations.py +227 -6
- package/resources/claude-customizations/.claude/agent-memory/orchestrator/feedback_repo_root_is_source_of_truth.md +0 -11
- package/resources/claude-customizations/.claude/agent-memory/orchestrator/feedback_vsce_verify_package_location.md +0 -19
- package/resources/claude-customizations/.claude/agent-memory/orchestrator/project_extension_location.md +0 -11
- package/resources/claude-customizations/.claude/agent-memory/prd-feature/MEMORY.md +0 -1
- package/resources/claude-customizations/.claude/agent-memory/prd-feature/project_push_down_pattern.md +0 -13
- package/resources/claude-customizations/.claude/agent-memory/task-researcher/MEMORY.md +0 -3
- package/resources/claude-customizations/.claude/agent-memory/task-researcher/project_push_down_claude_dir.md +0 -11
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
<#
|
|
2
|
+
.SYNOPSIS
|
|
3
|
+
Pre-tool-use hook that blocks Write operations on the orchestrator checkpoint
|
|
4
|
+
file when the checkpoint asserts completion without verifiable completion
|
|
5
|
+
evidence.
|
|
6
|
+
|
|
7
|
+
.DESCRIPTION
|
|
8
|
+
Invoked by the Claude Code PreToolUse hook on Write or Edit operations. The
|
|
9
|
+
hook activates only when the target file_path normalizes to
|
|
10
|
+
artifacts/orchestration/orchestrator-state.json.
|
|
11
|
+
|
|
12
|
+
A written checkpoint asserts completion when any of the following hold:
|
|
13
|
+
|
|
14
|
+
next_step == "complete"
|
|
15
|
+
completed_steps contains "S12_complete"
|
|
16
|
+
step8_status, step9_status, or step10_status == "completed"
|
|
17
|
+
|
|
18
|
+
When completion is asserted, the write is blocked unless ALL of the following
|
|
19
|
+
completion evidence is present:
|
|
20
|
+
|
|
21
|
+
- a non-empty issue-num (top-level, falling back to variables.issue-num);
|
|
22
|
+
- a non-empty feature-folder (top-level, falling back to
|
|
23
|
+
variables.feature-folder);
|
|
24
|
+
- a ci_gate object with conclusion == "success" and a non-empty head_sha.
|
|
25
|
+
|
|
26
|
+
The block reason names the specific missing evidence so the caller can
|
|
27
|
+
remediate. When completion is not asserted, the write is allowed
|
|
28
|
+
(backward compatibility).
|
|
29
|
+
|
|
30
|
+
Edit tool calls supply only old_string/new_string (a partial patch) and
|
|
31
|
+
cannot be reliably validated without the full target file content, so they
|
|
32
|
+
are allowed by this hook, matching enforce-checkpoint-monotonic.ps1. For
|
|
33
|
+
Write calls whose content is not valid JSON, the hook allows the operation
|
|
34
|
+
and defers to downstream tools to surface the error.
|
|
35
|
+
|
|
36
|
+
.NOTES
|
|
37
|
+
Compatible with PowerShell 7+. Read-only validation gate.
|
|
38
|
+
#>
|
|
39
|
+
[CmdletBinding()]
|
|
40
|
+
param()
|
|
41
|
+
|
|
42
|
+
function ConvertFrom-CheckpointJson {
|
|
43
|
+
<#
|
|
44
|
+
.SYNOPSIS
|
|
45
|
+
Wrapper around ConvertFrom-Json for the checkpoint content. Mockable.
|
|
46
|
+
#>
|
|
47
|
+
[CmdletBinding()]
|
|
48
|
+
param(
|
|
49
|
+
[Parameter(Mandatory)]
|
|
50
|
+
[string] $Json
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
return $Json | ConvertFrom-Json -ErrorAction Stop
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function Test-IsCheckpointPath {
|
|
57
|
+
[CmdletBinding()]
|
|
58
|
+
[OutputType([bool])]
|
|
59
|
+
param(
|
|
60
|
+
[Parameter(Mandatory)]
|
|
61
|
+
[string] $NormalizedPath
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
return $NormalizedPath -match '(^|/)artifacts/orchestration/orchestrator-state\.json$'
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function Get-CheckpointStringValue {
|
|
68
|
+
<#
|
|
69
|
+
.SYNOPSIS
|
|
70
|
+
Returns a trimmed string for a payload property, or an empty string when
|
|
71
|
+
the property is absent or its value is not a non-empty string.
|
|
72
|
+
#>
|
|
73
|
+
[CmdletBinding()]
|
|
74
|
+
[OutputType([string])]
|
|
75
|
+
param(
|
|
76
|
+
[Parameter(Mandatory)]
|
|
77
|
+
[AllowNull()]
|
|
78
|
+
$Payload,
|
|
79
|
+
|
|
80
|
+
[Parameter(Mandatory)]
|
|
81
|
+
[string] $Name
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
if ($null -eq $Payload) {
|
|
85
|
+
return ''
|
|
86
|
+
}
|
|
87
|
+
if (-not ($Payload.PSObject.Properties.Name -contains $Name)) {
|
|
88
|
+
return ''
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
$value = $Payload.$Name
|
|
92
|
+
if ($null -eq $value) {
|
|
93
|
+
return ''
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return ([string]$value).Trim()
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function Test-CompletionAsserted {
|
|
100
|
+
<#
|
|
101
|
+
.SYNOPSIS
|
|
102
|
+
Returns $true when the checkpoint payload asserts completion via
|
|
103
|
+
next_step, completed_steps, or step8/9/10 status fields.
|
|
104
|
+
#>
|
|
105
|
+
[CmdletBinding()]
|
|
106
|
+
[OutputType([bool])]
|
|
107
|
+
param(
|
|
108
|
+
[Parameter(Mandatory)]
|
|
109
|
+
[AllowNull()]
|
|
110
|
+
$Payload
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
if ($null -eq $Payload) {
|
|
114
|
+
return $false
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
$nextStep = Get-CheckpointStringValue -Payload $Payload -Name 'next_step'
|
|
118
|
+
if ($nextStep -eq 'complete') {
|
|
119
|
+
return $true
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if ($Payload.PSObject.Properties.Name -contains 'completed_steps' -and $Payload.completed_steps) {
|
|
123
|
+
foreach ($step in $Payload.completed_steps) {
|
|
124
|
+
if (([string]$step) -eq 'S12_complete') {
|
|
125
|
+
return $true
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
foreach ($statusField in @('step8_status', 'step9_status', 'step10_status')) {
|
|
131
|
+
$status = Get-CheckpointStringValue -Payload $Payload -Name $statusField
|
|
132
|
+
if ($status -eq 'completed') {
|
|
133
|
+
return $true
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return $false
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function Get-MissingCompletionEvidence {
|
|
141
|
+
<#
|
|
142
|
+
.SYNOPSIS
|
|
143
|
+
Returns the list of missing completion-evidence descriptions for a
|
|
144
|
+
completion-asserting checkpoint. An empty list means all evidence is
|
|
145
|
+
present.
|
|
146
|
+
#>
|
|
147
|
+
[CmdletBinding()]
|
|
148
|
+
[OutputType([string[]])]
|
|
149
|
+
param(
|
|
150
|
+
[Parameter(Mandatory)]
|
|
151
|
+
[AllowNull()]
|
|
152
|
+
$Payload
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
$missing = @()
|
|
156
|
+
|
|
157
|
+
$issueNum = Get-CheckpointStringValue -Payload $Payload -Name 'issue-num'
|
|
158
|
+
if (-not $issueNum -and $null -ne $Payload -and ($Payload.PSObject.Properties.Name -contains 'variables')) {
|
|
159
|
+
$issueNum = Get-CheckpointStringValue -Payload $Payload.variables -Name 'issue-num'
|
|
160
|
+
}
|
|
161
|
+
if (-not $issueNum) {
|
|
162
|
+
$missing += 'issue-num'
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
$featureFolder = Get-CheckpointStringValue -Payload $Payload -Name 'feature-folder'
|
|
166
|
+
if (-not $featureFolder -and $null -ne $Payload -and ($Payload.PSObject.Properties.Name -contains 'variables')) {
|
|
167
|
+
$featureFolder = Get-CheckpointStringValue -Payload $Payload.variables -Name 'feature-folder'
|
|
168
|
+
}
|
|
169
|
+
if (-not $featureFolder) {
|
|
170
|
+
$missing += 'feature-folder'
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
$ciGate = $null
|
|
174
|
+
if ($null -ne $Payload -and ($Payload.PSObject.Properties.Name -contains 'ci_gate')) {
|
|
175
|
+
$ciGate = $Payload.ci_gate
|
|
176
|
+
}
|
|
177
|
+
if ($null -eq $ciGate -or $ciGate -isnot [System.Management.Automation.PSCustomObject]) {
|
|
178
|
+
$missing += 'ci_gate (object with conclusion == "success" and non-empty head_sha)'
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
$conclusion = Get-CheckpointStringValue -Payload $ciGate -Name 'conclusion'
|
|
182
|
+
if ($conclusion -ne 'success') {
|
|
183
|
+
$missing += 'ci_gate.conclusion == "success"'
|
|
184
|
+
}
|
|
185
|
+
$headSha = Get-CheckpointStringValue -Payload $ciGate -Name 'head_sha'
|
|
186
|
+
if (-not $headSha) {
|
|
187
|
+
$missing += 'ci_gate.head_sha'
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return [string[]]$missing
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function Invoke-CompletionConsistencyDecision {
|
|
195
|
+
<#
|
|
196
|
+
.SYNOPSIS
|
|
197
|
+
Parses CLAUDE_TOOL_INPUT and returns an allow-or-block decision based on
|
|
198
|
+
completion-evidence consistency.
|
|
199
|
+
#>
|
|
200
|
+
[CmdletBinding()]
|
|
201
|
+
[OutputType([System.Collections.Specialized.OrderedDictionary])]
|
|
202
|
+
param(
|
|
203
|
+
[string] $ToolInputRaw
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
if (-not $ToolInputRaw) {
|
|
207
|
+
return [ordered]@{ decision = 'allow' }
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
try {
|
|
211
|
+
$toolInput = $ToolInputRaw | ConvertFrom-Json -ErrorAction Stop
|
|
212
|
+
}
|
|
213
|
+
catch {
|
|
214
|
+
throw "enforce-completion-consistency hook received malformed JSON in CLAUDE_TOOL_INPUT: $_"
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
$filePath = $toolInput.file_path
|
|
218
|
+
if (-not $filePath) {
|
|
219
|
+
return [ordered]@{ decision = 'allow' }
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
$normalized = $filePath -replace '\\', '/'
|
|
223
|
+
if (-not (Test-IsCheckpointPath -NormalizedPath $normalized)) {
|
|
224
|
+
return [ordered]@{ decision = 'allow' }
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
# Write tool: validate the content payload. Edit tool: partial new_string is
|
|
228
|
+
# not reliable without the full target file content, so allow.
|
|
229
|
+
$content = $toolInput.content
|
|
230
|
+
if (-not $content) {
|
|
231
|
+
return [ordered]@{ decision = 'allow' }
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
try {
|
|
235
|
+
$payload = ConvertFrom-CheckpointJson -Json $content
|
|
236
|
+
}
|
|
237
|
+
catch {
|
|
238
|
+
# The content itself is not valid JSON. Let downstream tools surface the
|
|
239
|
+
# error rather than blocking with a misleading reason here.
|
|
240
|
+
return [ordered]@{ decision = 'allow' }
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (-not (Test-CompletionAsserted -Payload $payload)) {
|
|
244
|
+
return [ordered]@{ decision = 'allow' }
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
$missing = Get-MissingCompletionEvidence -Payload $payload
|
|
248
|
+
if ($missing.Count -eq 0) {
|
|
249
|
+
return [ordered]@{ decision = 'allow' }
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
return [ordered]@{
|
|
253
|
+
decision = 'block'
|
|
254
|
+
reason = "COMPLETION_CONSISTENCY_BLOCKED: the checkpoint asserts completion but is missing required completion evidence: $($missing -join ', '). A completion-asserting checkpoint must include a non-empty issue-num, a non-empty feature-folder, and a ci_gate object with conclusion == 'success' and a non-empty head_sha. Supply the missing evidence or remove the completion assertion."
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
# Guard allows dot-sourcing in tests without executing the entrypoint.
|
|
259
|
+
if ($MyInvocation.InvocationName -eq '.') {
|
|
260
|
+
return
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
try {
|
|
264
|
+
$decision = Invoke-CompletionConsistencyDecision -ToolInputRaw $env:CLAUDE_TOOL_INPUT
|
|
265
|
+
}
|
|
266
|
+
catch {
|
|
267
|
+
Write-Error $_
|
|
268
|
+
exit 1
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
$decision | ConvertTo-Json -Compress | Write-Output
|
|
272
|
+
|
|
273
|
+
exit 0
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
<#
|
|
2
|
+
.SYNOPSIS
|
|
3
|
+
Pre-tool-use hook that blocks writes to a feature folder's plan.md when issue.md,
|
|
4
|
+
spec.md, or user-story.md are not yet present in that same folder.
|
|
5
|
+
|
|
6
|
+
.DESCRIPTION
|
|
7
|
+
Invoked by the Claude Code PreToolUse hook on Write or Edit operations. Reads
|
|
8
|
+
tool input JSON from the CLAUDE_TOOL_INPUT environment variable. When the
|
|
9
|
+
target file_path matches a feature-folder plan.md path under
|
|
10
|
+
docs/features/(active|archive)/<folder>/plan.md, the script verifies that
|
|
11
|
+
each of issue.md, spec.md, and user-story.md exists in the same folder.
|
|
12
|
+
|
|
13
|
+
If any of the three sibling files is missing, the script emits a JSON
|
|
14
|
+
response with decision='block' and exits 0 so Claude Code surfaces the
|
|
15
|
+
reason. All other paths pass through with decision='allow'.
|
|
16
|
+
|
|
17
|
+
Filesystem reads go through Get-FeatureFolderFileExistence so tests can
|
|
18
|
+
inject a fake without touching disk.
|
|
19
|
+
|
|
20
|
+
.NOTES
|
|
21
|
+
Compatible with PowerShell 7+. Read-only validation gate; no state mutation.
|
|
22
|
+
#>
|
|
23
|
+
[CmdletBinding()]
|
|
24
|
+
param()
|
|
25
|
+
|
|
26
|
+
function Get-FeatureFolderFileExistence {
|
|
27
|
+
<#
|
|
28
|
+
.SYNOPSIS
|
|
29
|
+
Wrapper around Test-Path for sibling-file existence checks. Tests mock this.
|
|
30
|
+
.PARAMETER Path
|
|
31
|
+
Absolute or workspace-relative file path to check.
|
|
32
|
+
#>
|
|
33
|
+
[CmdletBinding()]
|
|
34
|
+
[OutputType([bool])]
|
|
35
|
+
param(
|
|
36
|
+
[Parameter(Mandatory)]
|
|
37
|
+
[string] $Path
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
return [bool](Test-Path -LiteralPath $Path -PathType Leaf)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function Get-FeatureFolderMissingFile {
|
|
44
|
+
<#
|
|
45
|
+
.SYNOPSIS
|
|
46
|
+
Returns the list of required sibling files missing alongside plan.md.
|
|
47
|
+
.PARAMETER PlanFilePath
|
|
48
|
+
Normalized (forward-slash) path to the plan.md target.
|
|
49
|
+
#>
|
|
50
|
+
[CmdletBinding()]
|
|
51
|
+
[OutputType([string[]])]
|
|
52
|
+
param(
|
|
53
|
+
[Parameter(Mandatory)]
|
|
54
|
+
[string] $PlanFilePath
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
$folder = $PlanFilePath -replace '/plan\.md$', ''
|
|
58
|
+
$required = @('issue.md', 'spec.md', 'user-story.md')
|
|
59
|
+
[System.Collections.Generic.List[string]] $missing = [System.Collections.Generic.List[string]]::new()
|
|
60
|
+
|
|
61
|
+
foreach ($name in $required) {
|
|
62
|
+
$siblingPath = "$folder/$name"
|
|
63
|
+
if (-not (Get-FeatureFolderFileExistence -Path $siblingPath)) {
|
|
64
|
+
$missing.Add($name)
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return [string[]] $missing.ToArray()
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function Test-IsFeaturePlanPath {
|
|
72
|
+
<#
|
|
73
|
+
.SYNOPSIS
|
|
74
|
+
Returns $true if the normalized path targets a feature-folder plan.md.
|
|
75
|
+
#>
|
|
76
|
+
[CmdletBinding()]
|
|
77
|
+
[OutputType([bool])]
|
|
78
|
+
param(
|
|
79
|
+
[Parameter(Mandatory)]
|
|
80
|
+
[string] $NormalizedPath
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
return $NormalizedPath -match '(^|/)docs/features/(active|archive)/[^/]+/plan\.md$'
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function Invoke-FeatureFolderOrderDecision {
|
|
87
|
+
<#
|
|
88
|
+
.SYNOPSIS
|
|
89
|
+
Parses CLAUDE_TOOL_INPUT and produces an allow-or-block decision.
|
|
90
|
+
.PARAMETER ToolInputRaw
|
|
91
|
+
Raw JSON string. Empty/null returns allow.
|
|
92
|
+
#>
|
|
93
|
+
[CmdletBinding()]
|
|
94
|
+
[OutputType([System.Collections.Specialized.OrderedDictionary])]
|
|
95
|
+
param(
|
|
96
|
+
[string] $ToolInputRaw
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
if (-not $ToolInputRaw) {
|
|
100
|
+
return [ordered]@{ decision = 'allow' }
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
try {
|
|
104
|
+
$toolInput = $ToolInputRaw | ConvertFrom-Json -ErrorAction Stop
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
throw "enforce-feature-folder-order hook received malformed JSON in CLAUDE_TOOL_INPUT: $_"
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
$filePath = $toolInput.file_path
|
|
111
|
+
if (-not $filePath) {
|
|
112
|
+
return [ordered]@{ decision = 'allow' }
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
$normalized = $filePath -replace '\\', '/'
|
|
116
|
+
|
|
117
|
+
if (-not (Test-IsFeaturePlanPath -NormalizedPath $normalized)) {
|
|
118
|
+
return [ordered]@{ decision = 'allow' }
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
$missing = Get-FeatureFolderMissingFile -PlanFilePath $normalized
|
|
122
|
+
if ($missing.Count -eq 0) {
|
|
123
|
+
return [ordered]@{ decision = 'allow' }
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
$list = ($missing -join ', ')
|
|
127
|
+
return [ordered]@{
|
|
128
|
+
decision = 'block'
|
|
129
|
+
reason = "FEATURE_FOLDER_ORDER_BLOCKED: cannot write plan.md before producing prerequisite documents. Missing in feature folder: $list. Invoke the prd-feature subagent to generate the missing file(s) before authoring plan.md."
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
# Guard allows dot-sourcing in tests without executing the entrypoint.
|
|
134
|
+
if ($MyInvocation.InvocationName -eq '.') {
|
|
135
|
+
return
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
try {
|
|
139
|
+
$decision = Invoke-FeatureFolderOrderDecision -ToolInputRaw $env:CLAUDE_TOOL_INPUT
|
|
140
|
+
}
|
|
141
|
+
catch {
|
|
142
|
+
Write-Error $_
|
|
143
|
+
exit 1
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
$decision | ConvertTo-Json -Compress | Write-Output
|
|
147
|
+
|
|
148
|
+
exit 0
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
<#
|
|
2
|
+
.SYNOPSIS
|
|
3
|
+
Pre-tool-use hook that enforces the pr-author skill is used before gh pr create or gh pr edit.
|
|
4
|
+
|
|
5
|
+
.DESCRIPTION
|
|
6
|
+
Invoked by the Claude Code PreToolUse hook before any Bash command runs. Reads
|
|
7
|
+
tool input JSON from the CLAUDE_TOOL_INPUT environment variable, inspects the
|
|
8
|
+
attempted command, and blocks gh pr create / gh pr edit commands that bypass the
|
|
9
|
+
pr-author skill workflow.
|
|
10
|
+
|
|
11
|
+
Required sequence:
|
|
12
|
+
1. mcp__drm-copilot__collect_pr_context writes artifacts/pr_context.summary.txt
|
|
13
|
+
2. pr-author skill reads that file and writes artifacts/pr_body_<N>.md
|
|
14
|
+
3. gh pr create --body-file artifacts/pr_body_<N>.md
|
|
15
|
+
(or gh pr edit --body-file ...)
|
|
16
|
+
|
|
17
|
+
Three block cases:
|
|
18
|
+
Case A - gh pr create with --body (inline, no --body-file): blocked.
|
|
19
|
+
Case B - gh pr create with neither --body nor --body-file: blocked.
|
|
20
|
+
Case C - gh pr create or gh pr edit with --body-file but context artifact absent: blocked.
|
|
21
|
+
|
|
22
|
+
.NOTES
|
|
23
|
+
Compatible with PowerShell 7+. No external module dependencies.
|
|
24
|
+
#>
|
|
25
|
+
[CmdletBinding()]
|
|
26
|
+
param()
|
|
27
|
+
|
|
28
|
+
$script:PrContextArtifactPath = 'artifacts/pr_context.summary.txt'
|
|
29
|
+
|
|
30
|
+
function Get-PrContextArtifactExistence {
|
|
31
|
+
<#
|
|
32
|
+
.SYNOPSIS
|
|
33
|
+
Wrapper around Test-Path for the PR context artifact. Tests mock this function.
|
|
34
|
+
.OUTPUTS
|
|
35
|
+
System.Boolean
|
|
36
|
+
#>
|
|
37
|
+
[CmdletBinding()]
|
|
38
|
+
[OutputType([bool])]
|
|
39
|
+
param()
|
|
40
|
+
|
|
41
|
+
return [bool](Test-Path -LiteralPath $script:PrContextArtifactPath)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function Get-PrAuthorBypassReason {
|
|
45
|
+
<#
|
|
46
|
+
.SYNOPSIS
|
|
47
|
+
Inspect the command text and return a block reason string, or $null when the command is allowed.
|
|
48
|
+
.DESCRIPTION
|
|
49
|
+
Returns PR_AUTHOR_SKILL_BLOCKED when gh pr create is run with --body (inline) or with no body
|
|
50
|
+
flag at all. Returns PR_CONTEXT_MISSING when --body-file is present but the context artifact
|
|
51
|
+
does not exist on disk. Returns $null for all allowed patterns.
|
|
52
|
+
.PARAMETER CommandText
|
|
53
|
+
The Bash command text extracted from CLAUDE_TOOL_INPUT.
|
|
54
|
+
.PARAMETER ContextExists
|
|
55
|
+
Whether artifacts/pr_context.summary.txt currently exists on disk.
|
|
56
|
+
.OUTPUTS
|
|
57
|
+
System.String or $null
|
|
58
|
+
#>
|
|
59
|
+
[CmdletBinding()]
|
|
60
|
+
[OutputType([string])]
|
|
61
|
+
param(
|
|
62
|
+
[Parameter(Mandatory)]
|
|
63
|
+
[string] $CommandText,
|
|
64
|
+
|
|
65
|
+
[Parameter(Mandatory)]
|
|
66
|
+
[bool] $ContextExists
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
# Only act on gh pr create or gh pr edit subcommands.
|
|
70
|
+
$isPrCreate = $CommandText -match '(?i)\bgh\s+pr\s+create\b'
|
|
71
|
+
$isPrEdit = $CommandText -match '(?i)\bgh\s+pr\s+edit\b'
|
|
72
|
+
|
|
73
|
+
if (-not $isPrCreate -and -not $isPrEdit) {
|
|
74
|
+
return $null
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
$hasBodyFile = $CommandText -match '(?i)--body-file\b'
|
|
78
|
+
$hasInlineBody = $CommandText -match '(?i)--body(?!-file)\b'
|
|
79
|
+
|
|
80
|
+
if ($isPrCreate) {
|
|
81
|
+
# Case A: gh pr create with inline --body (not --body-file).
|
|
82
|
+
if ($hasInlineBody -and -not $hasBodyFile) {
|
|
83
|
+
return "PR_AUTHOR_SKILL_BLOCKED: ``gh pr create`` must use ``--body-file`` with a file produced by the pr-author skill from ``$script:PrContextArtifactPath``. Run ``mcp__drm-copilot__collect_pr_context`` to generate the context file, apply the pr-author skill to produce ``artifacts/pr_body_<N>.md``, then pass that file via ``--body-file``."
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
# Case B: gh pr create with no body flag at all.
|
|
87
|
+
if (-not $hasInlineBody -and -not $hasBodyFile) {
|
|
88
|
+
return "PR_AUTHOR_SKILL_BLOCKED: New PRs require ``--body-file``. Run ``mcp__drm-copilot__collect_pr_context`` to generate ``$script:PrContextArtifactPath``, apply the pr-author skill to produce ``artifacts/pr_body_<N>.md``, then pass that file via ``--body-file``."
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if ($isPrEdit) {
|
|
93
|
+
# gh pr edit with no --body or --body-file (e.g., --title, --add-label, --reviewer) is allowed.
|
|
94
|
+
if (-not $hasInlineBody -and -not $hasBodyFile) {
|
|
95
|
+
return $null
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
# Case C: --body-file present but context artifact is absent.
|
|
100
|
+
if ($hasBodyFile -and -not $ContextExists) {
|
|
101
|
+
return "PR_CONTEXT_MISSING: ``$script:PrContextArtifactPath`` is absent. Run ``mcp__drm-copilot__collect_pr_context`` before creating or editing the PR body."
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return $null
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function Invoke-PrAuthorSkillDecision {
|
|
108
|
+
<#
|
|
109
|
+
.SYNOPSIS
|
|
110
|
+
Parse CLAUDE_TOOL_INPUT and return an allow-or-block decision.
|
|
111
|
+
.PARAMETER ToolInputRaw
|
|
112
|
+
The raw JSON tool payload supplied by Claude Code.
|
|
113
|
+
.OUTPUTS
|
|
114
|
+
System.Collections.Specialized.OrderedDictionary
|
|
115
|
+
.NOTES
|
|
116
|
+
Missing tool input or missing command text is treated as allow.
|
|
117
|
+
#>
|
|
118
|
+
[CmdletBinding()]
|
|
119
|
+
[OutputType([System.Collections.Specialized.OrderedDictionary])]
|
|
120
|
+
param(
|
|
121
|
+
[string] $ToolInputRaw
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
if (-not $ToolInputRaw) {
|
|
125
|
+
return [ordered]@{ decision = 'allow' }
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
try {
|
|
129
|
+
$toolInput = $ToolInputRaw | ConvertFrom-Json -ErrorAction Stop
|
|
130
|
+
} catch {
|
|
131
|
+
throw "enforce-pr-author-skill hook received malformed JSON in CLAUDE_TOOL_INPUT: $_"
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
$commandText = $toolInput.command
|
|
135
|
+
if (-not $commandText) {
|
|
136
|
+
return [ordered]@{ decision = 'allow' }
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
$contextExists = Get-PrContextArtifactExistence
|
|
140
|
+
$reason = Get-PrAuthorBypassReason -CommandText $commandText -ContextExists $contextExists
|
|
141
|
+
|
|
142
|
+
if ($reason) {
|
|
143
|
+
return [ordered]@{
|
|
144
|
+
decision = 'block'
|
|
145
|
+
reason = $reason
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return [ordered]@{ decision = 'allow' }
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function Test-PrAuthorBypassRequired {
|
|
153
|
+
<#
|
|
154
|
+
.SYNOPSIS
|
|
155
|
+
Return $true when a Bash command requires the pr-author skill to run first.
|
|
156
|
+
.PARAMETER CommandText
|
|
157
|
+
The Bash command text extracted from CLAUDE_TOOL_INPUT.
|
|
158
|
+
.PARAMETER ContextExists
|
|
159
|
+
Whether artifacts/pr_context.summary.txt currently exists on disk.
|
|
160
|
+
.OUTPUTS
|
|
161
|
+
System.Boolean
|
|
162
|
+
#>
|
|
163
|
+
[CmdletBinding()]
|
|
164
|
+
[OutputType([bool])]
|
|
165
|
+
param(
|
|
166
|
+
[Parameter(Mandatory)]
|
|
167
|
+
[string] $CommandText,
|
|
168
|
+
|
|
169
|
+
[Parameter(Mandatory)]
|
|
170
|
+
[bool] $ContextExists
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
return ($null -ne (Get-PrAuthorBypassReason -CommandText $CommandText -ContextExists $ContextExists))
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
# Allow dot-sourcing in tests without executing the entrypoint.
|
|
177
|
+
if ($MyInvocation.InvocationName -eq '.') {
|
|
178
|
+
return
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
try {
|
|
182
|
+
$decision = Invoke-PrAuthorSkillDecision -ToolInputRaw $env:CLAUDE_TOOL_INPUT
|
|
183
|
+
} catch {
|
|
184
|
+
Write-Error $_
|
|
185
|
+
exit 1
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
$decision | ConvertTo-Json -Compress | Write-Output
|
|
189
|
+
|
|
190
|
+
exit 0
|