@danmoisan/drm-copilot-mcp 1.0.0 → 1.0.7
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 +814 -194
- package/package.json +1 -1
- package/resources/claude-customizations/.claude/agents/commit-message.md +42 -0
- package/resources/claude-customizations/.claude/agents/epic-orchestrator.md +127 -0
- package/resources/claude-customizations/.claude/agents/human-exception-runbook.md +48 -0
- package/resources/claude-customizations/.claude/agents/orchestrator.md +67 -2
- package/resources/claude-customizations/.claude/agents/pr-author.md +37 -21
- package/resources/claude-customizations/.claude/hooks/check-powershell-test-purity.ps1 +103 -65
- package/resources/claude-customizations/.claude/hooks/check-python-test-purity.ps1 +12 -9
- package/resources/claude-customizations/.claude/hooks/enforce-checkpoint-monotonic.ps1 +24 -16
- package/resources/claude-customizations/.claude/hooks/enforce-completion-consistency.ps1 +19 -13
- package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate.ps1 +304 -0
- package/resources/claude-customizations/.claude/hooks/enforce-epic-wave-barrier.ps1 +304 -0
- package/resources/claude-customizations/.claude/hooks/enforce-epic-worktree-removal-gate.ps1 +237 -0
- package/resources/claude-customizations/.claude/hooks/enforce-evidence-locations.ps1 +16 -12
- package/resources/claude-customizations/.claude/hooks/enforce-feature-folder-order.ps1 +14 -10
- package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate.ps1 +35 -8
- package/resources/claude-customizations/.claude/hooks/enforce-powershell-batch-budget.ps1 +17 -14
- package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.epic-base-branch.ps1 +104 -0
- package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.ps1 +268 -93
- package/resources/claude-customizations/.claude/hooks/enforce-prd-feature-before-planner.ps1 +19 -11
- package/resources/claude-customizations/.claude/hooks/enforce-promotion-mcp-only.ps1 +28 -6
- package/resources/claude-customizations/.claude/hooks/enforce-python-batch-budget.ps1 +17 -14
- package/resources/claude-customizations/.claude/hooks/validate-bash.ps1 +146 -36
- package/resources/claude-customizations/.claude/hooks/validate-orchestrator-output.ps1 +19 -7
- package/resources/claude-customizations/.claude/rules/orchestrator-state.md +36 -0
- package/resources/claude-customizations/.claude/settings.json +34 -1
- package/resources/claude-customizations/.claude/skills/epic-orchestrate/SKILL.md +252 -0
- package/resources/claude-customizations/.claude/skills/orchestrate/SKILL.md +42 -13
- package/resources/claude-customizations/.claude/skills/pr-author/SKILL.md +28 -0
- package/resources/claude-customizations/.claude/skills/remediation-handoff-atomic-planner/SKILL.md +22 -18
- package/resources/claude-customizations/pack-manifests/core.json +8 -0
- package/resources/codex-and-agents-customizations/.agents/skills/orchestrate/SKILL.md +6 -3
- package/resources/codex-and-agents-customizations/.agents/skills/remediation-handoff-atomic-planner/SKILL.md +1 -1
- package/resources/codex-and-agents-customizations/.agents-variants/csharp-legacy/skills/csharp/SKILL.md +65 -0
- package/resources/codex-and-agents-customizations/.agents-variants/csharp-legacy/skills/csharp-qa-gate/SKILL.md +79 -0
- package/resources/codex-and-agents-customizations/.agents-variants/csharp-legacy/skills/invoke-csharp-engineer/SKILL.md +66 -0
- package/resources/codex-and-agents-customizations/.codex/config.toml +1 -25
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-pr-author-skill.ps1 +257 -93
- package/resources/codex-and-agents-customizations/.codex-variants/csharp-legacy/agents/csharp-typed-engineer.toml +98 -0
- package/resources/codex-and-agents-customizations/pack-manifests/core.json +24 -0
- package/resources/codex-and-agents-customizations/pack-manifests/csharp-legacy.json +11 -0
- package/resources/codex-and-agents-customizations/pack-manifests/csharp-modern.json +10 -0
- package/resources/codex-and-agents-customizations/pack-manifests/powershell.json +15 -0
- package/resources/codex-and-agents-customizations/pack-manifests/python.json +15 -0
- package/resources/codex-and-agents-customizations/pack-manifests/typescript.json +9 -0
- package/resources/config/orchestration-routing.json +92 -0
- package/resources/customizations/.github/agents/pr-author.agent.md +24 -18
- package/resources/icon.png +0 -0
- package/resources/powershell/PoshQC/settings/pester.runsettings.psd1 +11 -0
- package/resources/codex-and-agents-customizations/.github/workflows/_validate-orchestrator-state.yml +0 -68
- package/resources/codex-and-agents-customizations/.github/workflows/validate-orchestrator-state.yml +0 -15
|
@@ -130,6 +130,36 @@ function Get-CheckpointContent {
|
|
|
130
130
|
return Get-Content -Raw -LiteralPath $script:CheckpointPath
|
|
131
131
|
}
|
|
132
132
|
|
|
133
|
+
function Get-OrchestrationPreimplementationGateAllowDecision {
|
|
134
|
+
[CmdletBinding()]
|
|
135
|
+
[OutputType([System.Collections.Specialized.OrderedDictionary])]
|
|
136
|
+
param()
|
|
137
|
+
|
|
138
|
+
return [ordered]@{
|
|
139
|
+
hookSpecificOutput = [ordered]@{
|
|
140
|
+
hookEventName = 'PreToolUse'
|
|
141
|
+
permissionDecision = 'allow'
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function Get-OrchestrationPreimplementationGateBlockDecision {
|
|
147
|
+
[CmdletBinding()]
|
|
148
|
+
[OutputType([System.Collections.Specialized.OrderedDictionary])]
|
|
149
|
+
param(
|
|
150
|
+
[Parameter(Mandatory)]
|
|
151
|
+
[string] $Reason
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
return [ordered]@{
|
|
155
|
+
hookSpecificOutput = [ordered]@{
|
|
156
|
+
hookEventName = 'PreToolUse'
|
|
157
|
+
permissionDecision = 'deny'
|
|
158
|
+
permissionDecisionReason = $Reason
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
133
163
|
function Invoke-OrchestrationPreimplementationGateDecision {
|
|
134
164
|
[CmdletBinding()]
|
|
135
165
|
[OutputType([System.Collections.Specialized.OrderedDictionary])]
|
|
@@ -139,7 +169,7 @@ function Invoke-OrchestrationPreimplementationGateDecision {
|
|
|
139
169
|
)
|
|
140
170
|
|
|
141
171
|
if (-not $ToolInputRaw) {
|
|
142
|
-
return
|
|
172
|
+
return Get-OrchestrationPreimplementationGateAllowDecision
|
|
143
173
|
}
|
|
144
174
|
try {
|
|
145
175
|
$toolInput = $ToolInputRaw | ConvertFrom-Json -ErrorAction Stop
|
|
@@ -162,7 +192,7 @@ function Invoke-OrchestrationPreimplementationGateDecision {
|
|
|
162
192
|
}
|
|
163
193
|
|
|
164
194
|
if (-not $requiresReadyCheckpoint) {
|
|
165
|
-
return
|
|
195
|
+
return Get-OrchestrationPreimplementationGateAllowDecision
|
|
166
196
|
}
|
|
167
197
|
|
|
168
198
|
if (-not $CheckpointRaw) {
|
|
@@ -175,12 +205,9 @@ function Invoke-OrchestrationPreimplementationGateDecision {
|
|
|
175
205
|
}
|
|
176
206
|
|
|
177
207
|
if (Test-OrchestrationReady -Payload $checkpoint) {
|
|
178
|
-
return
|
|
179
|
-
}
|
|
180
|
-
return [ordered]@{
|
|
181
|
-
decision = 'block'
|
|
182
|
-
reason = 'PREIMPLEMENTATION_GATE_BLOCKED: Implementation operations require artifacts/orchestration/orchestrator-state.json to contain issue number, feature folder, route metadata, lifecycle readiness, and checkpoint state before implementation begins.'
|
|
208
|
+
return Get-OrchestrationPreimplementationGateAllowDecision
|
|
183
209
|
}
|
|
210
|
+
return Get-OrchestrationPreimplementationGateBlockDecision -Reason 'PREIMPLEMENTATION_GATE_BLOCKED: Implementation operations require artifacts/orchestration/orchestrator-state.json to contain issue number, feature folder, route metadata, lifecycle readiness, and checkpoint state before implementation begins.'
|
|
184
211
|
}
|
|
185
212
|
|
|
186
213
|
if ($MyInvocation.InvocationName -eq '.') {
|
|
@@ -194,5 +221,5 @@ try {
|
|
|
194
221
|
exit 1
|
|
195
222
|
}
|
|
196
223
|
|
|
197
|
-
$decision | ConvertTo-Json -Compress | Write-Output
|
|
224
|
+
$decision | ConvertTo-Json -Compress -Depth 5 | Write-Output
|
|
198
225
|
exit 0
|
|
@@ -26,10 +26,10 @@
|
|
|
26
26
|
before the session starts, or by writing {"prodCap": N, "testCap": M} into the
|
|
27
27
|
state file.
|
|
28
28
|
|
|
29
|
-
When the cap would be exceeded by a new file, the script emits a JSON
|
|
30
|
-
|
|
31
|
-
deleting the state file before starting a new
|
|
32
|
-
always allowed through.
|
|
29
|
+
When the cap would be exceeded by a new file, the script emits a PreToolUse JSON
|
|
30
|
+
response with hookSpecificOutput.permissionDecision = 'deny' and exits 0. The session
|
|
31
|
+
must explicitly reset the counter by deleting the state file before starting a new
|
|
32
|
+
batch. Files already counted are always allowed through.
|
|
33
33
|
|
|
34
34
|
.NOTES
|
|
35
35
|
Compatible with PowerShell 7+.
|
|
@@ -90,8 +90,11 @@ function Get-PowerShellBatchBudgetBlockDecision {
|
|
|
90
90
|
)
|
|
91
91
|
|
|
92
92
|
$decision = [ordered]@{
|
|
93
|
-
|
|
94
|
-
|
|
93
|
+
hookSpecificOutput = [ordered]@{
|
|
94
|
+
hookEventName = 'PreToolUse'
|
|
95
|
+
permissionDecision = 'deny'
|
|
96
|
+
permissionDecisionReason = $Reason
|
|
97
|
+
}
|
|
95
98
|
}
|
|
96
99
|
if ($State) {
|
|
97
100
|
$decision.state = $State
|
|
@@ -116,7 +119,7 @@ function Invoke-PowerShellBatchBudgetDecision {
|
|
|
116
119
|
|
|
117
120
|
$normalized = $FilePath -replace '\\', '/'
|
|
118
121
|
if ($normalized -notmatch '\.(ps1|psm1|psd1)$') {
|
|
119
|
-
return [ordered]@{
|
|
122
|
+
return [ordered]@{ hookSpecificOutput = [ordered]@{ hookEventName = 'PreToolUse'; permissionDecision = 'allow' }; state = $State; shouldWriteState = $false }
|
|
120
123
|
}
|
|
121
124
|
|
|
122
125
|
$isTestFile = ($normalized -match '(^|/)tests/.*\.ps1$') -or ($normalized -match '\.Tests\.ps1$')
|
|
@@ -125,7 +128,7 @@ function Invoke-PowerShellBatchBudgetDecision {
|
|
|
125
128
|
$kind = if ($isTestFile) { 'test' } else { 'production' }
|
|
126
129
|
|
|
127
130
|
if ($targetList -contains $normalized) {
|
|
128
|
-
return [ordered]@{
|
|
131
|
+
return [ordered]@{ hookSpecificOutput = [ordered]@{ hookEventName = 'PreToolUse'; permissionDecision = 'allow' }; state = $State; shouldWriteState = $false }
|
|
129
132
|
}
|
|
130
133
|
|
|
131
134
|
if ($targetList.Count -ge $cap) {
|
|
@@ -141,7 +144,7 @@ function Invoke-PowerShellBatchBudgetDecision {
|
|
|
141
144
|
$State.prodFiles = @($State.prodFiles) + @($normalized)
|
|
142
145
|
}
|
|
143
146
|
|
|
144
|
-
return [ordered]@{
|
|
147
|
+
return [ordered]@{ hookSpecificOutput = [ordered]@{ hookEventName = 'PreToolUse'; permissionDecision = 'allow' }; state = $State; shouldWriteState = $true }
|
|
145
148
|
}
|
|
146
149
|
|
|
147
150
|
function Invoke-PowerShellBatchBudgetHook {
|
|
@@ -163,7 +166,7 @@ function Invoke-PowerShellBatchBudgetHook {
|
|
|
163
166
|
)
|
|
164
167
|
|
|
165
168
|
if (-not $ToolInputRaw) {
|
|
166
|
-
return [ordered]@{
|
|
169
|
+
return [ordered]@{ hookSpecificOutput = [ordered]@{ hookEventName = 'PreToolUse'; permissionDecision = 'allow' } }
|
|
167
170
|
}
|
|
168
171
|
|
|
169
172
|
try {
|
|
@@ -174,12 +177,12 @@ function Invoke-PowerShellBatchBudgetHook {
|
|
|
174
177
|
|
|
175
178
|
$filePath = $toolInput.file_path
|
|
176
179
|
if (-not $filePath) {
|
|
177
|
-
return [ordered]@{
|
|
180
|
+
return [ordered]@{ hookSpecificOutput = [ordered]@{ hookEventName = 'PreToolUse'; permissionDecision = 'allow' } }
|
|
178
181
|
}
|
|
179
182
|
|
|
180
183
|
$normalized = $filePath -replace '\\', '/'
|
|
181
184
|
if ($normalized -notmatch '\.(ps1|psm1|psd1)$') {
|
|
182
|
-
return [ordered]@{
|
|
185
|
+
return [ordered]@{ hookSpecificOutput = [ordered]@{ hookEventName = 'PreToolUse'; permissionDecision = 'allow' } }
|
|
183
186
|
}
|
|
184
187
|
|
|
185
188
|
$stateDir = Join-Path -Path $Root -ChildPath '.claude/state'
|
|
@@ -230,9 +233,9 @@ if ($env:CLAUDE_POWERSHELL_BUDGET_TEST -match '^\d+$') {
|
|
|
230
233
|
}
|
|
231
234
|
|
|
232
235
|
$decision = Invoke-PowerShellBatchBudgetHook -ToolInputRaw $env:CLAUDE_TOOL_INPUT -SessionId $sessionId -ProdCap $prodCap -TestCap $testCap
|
|
233
|
-
if ($decision.
|
|
236
|
+
if ($decision.hookSpecificOutput.permissionDecision -eq 'deny') {
|
|
234
237
|
$decision.Remove('state')
|
|
235
|
-
$decision | ConvertTo-Json -Compress | Write-Output
|
|
238
|
+
$decision | ConvertTo-Json -Compress -Depth 5 | Write-Output
|
|
236
239
|
}
|
|
237
240
|
|
|
238
241
|
exit 0
|
package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.epic-base-branch.ps1
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
<#
|
|
2
|
+
.SYNOPSIS
|
|
3
|
+
Dot-sourced by enforce-pr-author-skill.ps1 to keep that file under the repository's
|
|
4
|
+
500-line hard cap.
|
|
5
|
+
|
|
6
|
+
.DESCRIPTION
|
|
7
|
+
Contains the epic-mode base-branch override check (Get-PrAuthorCheckpointContent and
|
|
8
|
+
Test-EpicBaseBranchOverride) extracted verbatim from enforce-pr-author-skill.ps1. This is a
|
|
9
|
+
structural extraction only: no logic, decision behavior, or reason-string wording changed.
|
|
10
|
+
#>
|
|
11
|
+
|
|
12
|
+
function Get-PrAuthorCheckpointContent {
|
|
13
|
+
<#
|
|
14
|
+
.SYNOPSIS
|
|
15
|
+
Read the raw JSON text of the per-feature orchestrator checkpoint. Tests mock this
|
|
16
|
+
function (read seam) for the epic-mode base-branch override check.
|
|
17
|
+
.DESCRIPTION
|
|
18
|
+
Returns the raw text content of artifacts/orchestration/orchestrator-state.json, or
|
|
19
|
+
$null when the file is absent. This is the injectable boundary for checkpoint content
|
|
20
|
+
used by Test-EpicBaseBranchOverride; no test writes the checkpoint file to disk.
|
|
21
|
+
.PARAMETER CheckpointPath
|
|
22
|
+
The relative path to the per-feature orchestrator checkpoint.
|
|
23
|
+
.OUTPUTS
|
|
24
|
+
System.String or $null
|
|
25
|
+
#>
|
|
26
|
+
[CmdletBinding()]
|
|
27
|
+
[OutputType([string])]
|
|
28
|
+
param(
|
|
29
|
+
[Parameter(Mandatory = $false)]
|
|
30
|
+
[string] $CheckpointPath = 'artifacts/orchestration/orchestrator-state.json'
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
if (-not (Test-Path -LiteralPath $CheckpointPath)) {
|
|
34
|
+
return $null
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return (Get-Content -LiteralPath $CheckpointPath -Raw)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function Test-EpicBaseBranchOverride {
|
|
41
|
+
<#
|
|
42
|
+
.SYNOPSIS
|
|
43
|
+
Sixth ordered check: enforce the epic-mode --base override for gh pr create.
|
|
44
|
+
.DESCRIPTION
|
|
45
|
+
Reads the per-feature checkpoint via the injectable Get-PrAuthorCheckpointContent
|
|
46
|
+
seam. When the checkpoint has epic_mode == true, a gh pr create command text MUST
|
|
47
|
+
contain --base <epic_context.integration_branch> with the exact branch value recorded
|
|
48
|
+
in the checkpoint; a missing --base, or a --base value that does not match, is denied
|
|
49
|
+
with reason EPIC_BASE_BRANCH_MISMATCH. When epic_mode is absent or false, when the
|
|
50
|
+
checkpoint is unreadable, or when the command is not gh pr create (the base branch is
|
|
51
|
+
set at create time, not edit time), this check is a no-op and standalone behavior is
|
|
52
|
+
unchanged.
|
|
53
|
+
.PARAMETER CommandText
|
|
54
|
+
The Bash command text under evaluation.
|
|
55
|
+
.OUTPUTS
|
|
56
|
+
System.String or $null
|
|
57
|
+
#>
|
|
58
|
+
[CmdletBinding()]
|
|
59
|
+
[OutputType([string])]
|
|
60
|
+
param(
|
|
61
|
+
[Parameter(Mandatory)]
|
|
62
|
+
[string] $CommandText
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
# The epic-mode base-branch override only constrains gh pr create; gh pr edit does not
|
|
66
|
+
# re-target the base branch, so non-create commands are out of scope for this check.
|
|
67
|
+
if ($CommandText -notmatch '(?i)\bgh\s+pr\s+create\b') {
|
|
68
|
+
return $null
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
$checkpointRaw = Get-PrAuthorCheckpointContent
|
|
72
|
+
if ([string]::IsNullOrWhiteSpace($checkpointRaw)) {
|
|
73
|
+
return $null
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
try {
|
|
77
|
+
$checkpoint = $checkpointRaw | ConvertFrom-Json -ErrorAction Stop
|
|
78
|
+
} catch {
|
|
79
|
+
return $null
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
$checkpointProps = @($checkpoint.PSObject.Properties.Name)
|
|
83
|
+
if ($checkpointProps -notcontains 'epic_mode' -or -not [bool]$checkpoint.epic_mode) {
|
|
84
|
+
return $null
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
$integrationBranch = $null
|
|
88
|
+
if ($checkpointProps -contains 'epic_context' -and $null -ne $checkpoint.epic_context) {
|
|
89
|
+
$epicContextProps = @($checkpoint.epic_context.PSObject.Properties.Name)
|
|
90
|
+
if ($epicContextProps -contains 'integration_branch') {
|
|
91
|
+
$integrationBranch = [string]$checkpoint.epic_context.integration_branch
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if ([string]::IsNullOrWhiteSpace($integrationBranch)) {
|
|
96
|
+
return "EPIC_BASE_BRANCH_MISMATCH: checkpoint has epic_mode == true but no ``epic_context.integration_branch`` is recorded; ``gh pr create`` cannot be verified against the required ``--base`` value."
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if ($CommandText -cnotmatch [regex]::Escape("--base $integrationBranch")) {
|
|
100
|
+
return "EPIC_BASE_BRANCH_MISMATCH: ``gh pr create`` must pass ``--base $integrationBranch`` (``epic_context.integration_branch``) under ``epic_mode``; the command does not carry a matching ``--base`` argument."
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return $null
|
|
104
|
+
}
|