@danmoisan/drm-copilot-mcp 1.0.21 → 1.0.23
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 +1730 -139
- package/package.json +1 -1
- package/resources/claude-customizations/.claude/agent-memory/epic-orchestrator/MEMORY.md +5 -1
- package/resources/claude-customizations/.claude/agent-memory/epic-orchestrator/feedback_commit_push_memory_before_pr.md +48 -2
- package/resources/claude-customizations/.claude/agent-memory/epic-orchestrator/feedback_no_sendmessage_tool.md +35 -0
- package/resources/claude-customizations/.claude/agent-memory/epic-orchestrator/feedback_worktree_isolation_branches_from_main.md +45 -0
- package/resources/claude-customizations/.claude/agents/parallel-orchestrator.md +257 -0
- package/resources/claude-customizations/.claude/agents/parallel-planner.md +183 -0
- package/resources/claude-customizations/.claude/hooks/enforce-epic-invocation-origin.ps1 +23 -11
- package/resources/claude-customizations/.claude/hooks/enforce-parallel-abandon-gate.ps1 +259 -0
- package/resources/claude-customizations/.claude/hooks/enforce-parallel-cohort-barrier.ps1 +499 -0
- package/resources/claude-customizations/.claude/hooks/enforce-parallel-drift-gate-helpers.ps1 +302 -0
- package/resources/claude-customizations/.claude/hooks/enforce-parallel-drift-gate.ps1 +359 -0
- package/resources/claude-customizations/.claude/hooks/enforce-parallel-worktree-removal-gate.ps1 +244 -0
- package/resources/claude-customizations/.claude/lib/bash/compute-cohorts.sh +143 -0
- package/resources/claude-customizations/.claude/lib/bash/compute-concurrency-batches.sh +122 -0
- package/resources/claude-customizations/.claude/lib/bash/parallel-cohorts.sh +330 -0
- package/resources/claude-customizations/.claude/lib/bash/parallel-common.sh +238 -0
- package/resources/claude-customizations/.claude/lib/bash/parallel-items-validate.sh +244 -0
- package/resources/claude-customizations/.claude/lib/bash/parallel-manifest-validate.sh +187 -0
- package/resources/claude-customizations/.claude/lib/bash/parallel-yaml-emit.sh +340 -0
- package/resources/claude-customizations/.claude/lib/bash/parallel-yaml-scan.sh +335 -0
- package/resources/claude-customizations/.claude/lib/bash/validate-parallel-manifest.sh +134 -0
- package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadius.psm1 +379 -0
- package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusConfig.psm1 +491 -0
- package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusExtraction.psm1 +490 -0
- package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusGlob.psm1 +429 -0
- package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusValidation.psm1 +366 -0
- package/resources/claude-customizations/.claude/rules/parallel-orchestration.md +184 -0
- package/resources/claude-customizations/.claude/rules/shell.md +7 -2
- package/resources/claude-customizations/.claude/settings.json +28 -0
- package/resources/claude-customizations/.claude/skills/parallel-add/SKILL.md +152 -0
- package/resources/claude-customizations/.claude/skills/parallel-close/SKILL.md +93 -0
- package/resources/claude-customizations/.claude/skills/parallel-orchestrate/SKILL.md +971 -0
- package/resources/claude-customizations/.claude/skills/parallel-plan/SKILL.md +461 -0
- package/resources/claude-customizations/.claude/skills/parallel-remove/SKILL.md +176 -0
- package/resources/claude-customizations/.claude/skills/parallel-run/SKILL.md +56 -0
- package/resources/claude-customizations/config/blast-radius.json +16 -0
- package/resources/claude-customizations/config/orchestration-routing.json +355 -0
- package/resources/claude-customizations/pack-manifests/core.json +32 -1
- package/resources/codex-and-agents-customizations/.codex/config.toml +1 -1
- package/resources/config/orchestration-routing.json +22 -0
- package/resources/powershell/PoshQC/settings/pester.runsettings.psd1 +29 -0
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
<#
|
|
2
2
|
.SYNOPSIS
|
|
3
|
-
Pre-tool-use hook that blocks epic-planner
|
|
4
|
-
originating from an
|
|
3
|
+
Pre-tool-use hook that blocks epic-planner, epic-orchestrator,
|
|
4
|
+
parallel-planner, and parallel-orchestrator delegations originating from an
|
|
5
|
+
orchestrator agent.
|
|
5
6
|
|
|
6
7
|
.DESCRIPTION
|
|
7
8
|
Invoked by the Claude Code PreToolUse hook on the "Agent" matcher before any
|
|
8
9
|
Agent (Task) call runs. Activates only when the delegation target
|
|
9
|
-
subagent_type is 'epic-planner'
|
|
10
|
+
subagent_type is 'epic-planner', 'epic-orchestrator', 'parallel-planner',
|
|
11
|
+
or 'parallel-orchestrator'.
|
|
10
12
|
|
|
11
13
|
Caller identity resolution:
|
|
12
14
|
- The full PreToolUse payload (CLAUDE_HOOK_INPUT) carries a top-level
|
|
@@ -17,13 +19,15 @@
|
|
|
17
19
|
|
|
18
20
|
Decision procedure:
|
|
19
21
|
1. Resolve the target subagent_type from CLAUDE_TOOL_INPUT, falling back
|
|
20
|
-
to the payload's tool_input object. A non-
|
|
22
|
+
to the payload's tool_input object. A non-gated target allows.
|
|
21
23
|
2. Resolve the calling agent_type from the payload. An absent or empty
|
|
22
24
|
agent_type indicates a main-thread invocation, which allows.
|
|
23
|
-
3. Deny
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
3. Deny when the calling agent_type is exactly 'orchestrator', with the
|
|
26
|
+
reason variant selected by target: EPIC_INVOCATION_ORIGIN_BLOCKED for
|
|
27
|
+
an epic target, PARALLEL_INVOCATION_ORIGIN_BLOCKED for a parallel
|
|
28
|
+
target. All four gated agents delegate to Agent(orchestrator); an
|
|
29
|
+
orchestrator-originated invocation would nest orchestrator inside its
|
|
30
|
+
own delegation chain.
|
|
27
31
|
|
|
28
32
|
.NOTES
|
|
29
33
|
Compatible with PowerShell 7+. No external module dependencies. Read-only
|
|
@@ -33,7 +37,8 @@
|
|
|
33
37
|
[CmdletBinding()]
|
|
34
38
|
param()
|
|
35
39
|
|
|
36
|
-
$script:GatedSubagentTypes = @('epic-planner', 'epic-orchestrator')
|
|
40
|
+
$script:GatedSubagentTypes = @('epic-planner', 'epic-orchestrator', 'parallel-planner', 'parallel-orchestrator')
|
|
41
|
+
$script:ParallelSubagentTypes = @('parallel-planner', 'parallel-orchestrator')
|
|
37
42
|
$script:ProhibitedCallerAgentType = 'orchestrator'
|
|
38
43
|
|
|
39
44
|
function Get-EpicInvocationOriginAllowDecision {
|
|
@@ -194,7 +199,7 @@ function Invoke-EpicInvocationOriginDecision {
|
|
|
194
199
|
[string] $ToolInputRaw
|
|
195
200
|
)
|
|
196
201
|
|
|
197
|
-
# The tool input identifies the delegation target; a non-
|
|
202
|
+
# The tool input identifies the delegation target; a non-gated target is
|
|
198
203
|
# outside this hook's scope, so the hook input is not parsed for it.
|
|
199
204
|
$toolInput = ConvertFrom-EpicInvocationOriginPayload -RawPayload $ToolInputRaw -PayloadName 'CLAUDE_TOOL_INPUT'
|
|
200
205
|
$hookInputParsed = $false
|
|
@@ -218,13 +223,20 @@ function Invoke-EpicInvocationOriginDecision {
|
|
|
218
223
|
}
|
|
219
224
|
|
|
220
225
|
# An absent agent_type marks a main-thread invocation, which is the
|
|
221
|
-
# intended entry point for
|
|
226
|
+
# intended entry point for every gated agent; only an orchestrator-context
|
|
222
227
|
# invocation is prohibited.
|
|
223
228
|
$caller = Get-EpicInvocationOriginCallerAgentType -HookInput $hookInput
|
|
224
229
|
if ($caller -ne $script:ProhibitedCallerAgentType) {
|
|
225
230
|
return Get-EpicInvocationOriginAllowDecision
|
|
226
231
|
}
|
|
227
232
|
|
|
233
|
+
# The parallel family carries its own reason variant because the epic reason
|
|
234
|
+
# names the two epic agents literally; the deny prose is selected by target.
|
|
235
|
+
if ($script:ParallelSubagentTypes -contains $target) {
|
|
236
|
+
$parallelReason = "PARALLEL_INVOCATION_ORIGIN_BLOCKED: Agent($target) must not be invoked from an orchestrator agent. Both parallel-planner and parallel-orchestrator delegate to Agent(orchestrator), so an orchestrator-originated invocation would nest orchestrator inside its own delegation chain. Invoke $target from the main session instead."
|
|
237
|
+
return Get-EpicInvocationOriginBlockDecision -Reason $parallelReason
|
|
238
|
+
}
|
|
239
|
+
|
|
228
240
|
$reason = "EPIC_INVOCATION_ORIGIN_BLOCKED: Agent($target) must not be invoked from an orchestrator agent. Both epic-planner and epic-orchestrator delegate to Agent(orchestrator), so an orchestrator-originated invocation would nest orchestrator inside its own delegation chain. Invoke $target from the main session instead."
|
|
229
241
|
return Get-EpicInvocationOriginBlockDecision -Reason $reason
|
|
230
242
|
}
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
<#
|
|
2
|
+
.SYNOPSIS
|
|
3
|
+
Pre-tool-use hook that gates the destructive parallel abandon disposition behind an
|
|
4
|
+
explicit confirmation marker.
|
|
5
|
+
|
|
6
|
+
.DESCRIPTION
|
|
7
|
+
Invoked by the Claude Code PreToolUse hook on the "Bash" matcher before any Bash
|
|
8
|
+
command runs. Reads CLAUDE_TOOL_INPUT, extracts the command text, and matches it
|
|
9
|
+
against the two tokens declared at the top of this file: the abandon disposition
|
|
10
|
+
token and the confirmation marker. A command carrying the abandon disposition token
|
|
11
|
+
without the confirmation marker in the SAME command is denied with reason prefix
|
|
12
|
+
PARALLEL_ABANDON_BLOCKED. A command carrying both is allowed. A command carrying no
|
|
13
|
+
abandon disposition token is out of scope and is allowed unchanged.
|
|
14
|
+
|
|
15
|
+
The gate exists because the abandon disposition of /parallel-remove closes a pull
|
|
16
|
+
request and removes a worktree. Both side effects are irreversible from the run's
|
|
17
|
+
point of view, so the destructive path must be confirmed deliberately rather than
|
|
18
|
+
reached by a command that merely looked like a removal.
|
|
19
|
+
|
|
20
|
+
.NOTES
|
|
21
|
+
Compatible with PowerShell 7+. No external module dependencies. Tool-input retrieval
|
|
22
|
+
goes through an injectable wrapper function so tests can mock the boundary with
|
|
23
|
+
literal JSON without writing temporary files, following the read-seam pattern of
|
|
24
|
+
.claude/hooks/enforce-epic-worktree-removal-gate.ps1.
|
|
25
|
+
|
|
26
|
+
The two token values are declared ONCE EACH below and every match in this file reads
|
|
27
|
+
those variables rather than repeating a literal. The producer side of the same pair is
|
|
28
|
+
scripts/dev_tools/parallel_mutation_abandon_cli.py, and
|
|
29
|
+
tests/scripts/dev_tools/test_parallel_abandon_token_seam.py parses both sides at run
|
|
30
|
+
time so a rename on one side without the other fails.
|
|
31
|
+
#>
|
|
32
|
+
[CmdletBinding()]
|
|
33
|
+
param()
|
|
34
|
+
|
|
35
|
+
# The two tokens this gate matches on. These are the ONLY places either token literal
|
|
36
|
+
# appears in this file; the seam test extracts the consumer-side values from exactly
|
|
37
|
+
# these two named assignments.
|
|
38
|
+
$script:AbandonDispositionToken = '--disposition abandon'
|
|
39
|
+
$script:AbandonConfirmToken = '--confirm-abandon'
|
|
40
|
+
|
|
41
|
+
# Literal prefix on the deny reason, so the reason code is greppable in transcripts.
|
|
42
|
+
$script:AbandonBlockedReasonCode = 'PARALLEL_ABANDON_BLOCKED'
|
|
43
|
+
|
|
44
|
+
function Get-ParallelAbandonGateToolInput {
|
|
45
|
+
<#
|
|
46
|
+
.SYNOPSIS
|
|
47
|
+
Read the raw JSON tool payload supplied by Claude Code. Tests mock this
|
|
48
|
+
function (read seam).
|
|
49
|
+
.OUTPUTS
|
|
50
|
+
System.String or $null
|
|
51
|
+
#>
|
|
52
|
+
[CmdletBinding()]
|
|
53
|
+
[OutputType([string])]
|
|
54
|
+
param()
|
|
55
|
+
|
|
56
|
+
return $env:CLAUDE_TOOL_INPUT
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function Get-ParallelAbandonNormalizedCommand {
|
|
60
|
+
<#
|
|
61
|
+
.SYNOPSIS
|
|
62
|
+
Collapse whitespace runs in a command so token matching is not defeated by
|
|
63
|
+
extra spacing or a line continuation.
|
|
64
|
+
.PARAMETER CommandText
|
|
65
|
+
The Bash command text under evaluation.
|
|
66
|
+
.OUTPUTS
|
|
67
|
+
System.String
|
|
68
|
+
#>
|
|
69
|
+
[CmdletBinding()]
|
|
70
|
+
[OutputType([string])]
|
|
71
|
+
param(
|
|
72
|
+
[AllowNull()]
|
|
73
|
+
[AllowEmptyString()]
|
|
74
|
+
[string] $CommandText
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
if ([string]::IsNullOrWhiteSpace($CommandText)) {
|
|
78
|
+
return ''
|
|
79
|
+
}
|
|
80
|
+
# A single space between tokens is the shape the declared tokens are written in, so
|
|
81
|
+
# normalizing here lets the tokens stay exactly as the producer declares them.
|
|
82
|
+
return ($CommandText -replace '\s+', ' ').Trim()
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function Test-ParallelAbandonCommandInScope {
|
|
86
|
+
<#
|
|
87
|
+
.SYNOPSIS
|
|
88
|
+
Report whether a command carries the abandon disposition token at all.
|
|
89
|
+
.PARAMETER NormalizedCommand
|
|
90
|
+
The whitespace-normalized command text.
|
|
91
|
+
.OUTPUTS
|
|
92
|
+
System.Boolean
|
|
93
|
+
#>
|
|
94
|
+
[CmdletBinding()]
|
|
95
|
+
[OutputType([bool])]
|
|
96
|
+
param(
|
|
97
|
+
[AllowNull()]
|
|
98
|
+
[AllowEmptyString()]
|
|
99
|
+
[string] $NormalizedCommand
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
if ([string]::IsNullOrWhiteSpace($NormalizedCommand)) {
|
|
103
|
+
return $false
|
|
104
|
+
}
|
|
105
|
+
return $NormalizedCommand.Contains(
|
|
106
|
+
$script:AbandonDispositionToken,
|
|
107
|
+
[System.StringComparison]::OrdinalIgnoreCase)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function Test-ParallelAbandonCommandConfirmed {
|
|
111
|
+
<#
|
|
112
|
+
.SYNOPSIS
|
|
113
|
+
Report whether a command carries the confirmation marker.
|
|
114
|
+
.PARAMETER NormalizedCommand
|
|
115
|
+
The whitespace-normalized command text.
|
|
116
|
+
.OUTPUTS
|
|
117
|
+
System.Boolean
|
|
118
|
+
#>
|
|
119
|
+
[CmdletBinding()]
|
|
120
|
+
[OutputType([bool])]
|
|
121
|
+
param(
|
|
122
|
+
[AllowNull()]
|
|
123
|
+
[AllowEmptyString()]
|
|
124
|
+
[string] $NormalizedCommand
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
if ([string]::IsNullOrWhiteSpace($NormalizedCommand)) {
|
|
128
|
+
return $false
|
|
129
|
+
}
|
|
130
|
+
return $NormalizedCommand.Contains(
|
|
131
|
+
$script:AbandonConfirmToken,
|
|
132
|
+
[System.StringComparison]::OrdinalIgnoreCase)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function Get-ParallelAbandonGateAllowDecision {
|
|
136
|
+
<#
|
|
137
|
+
.SYNOPSIS
|
|
138
|
+
Build the allow decision payload.
|
|
139
|
+
.OUTPUTS
|
|
140
|
+
System.Collections.Specialized.OrderedDictionary
|
|
141
|
+
#>
|
|
142
|
+
[CmdletBinding()]
|
|
143
|
+
[OutputType([System.Collections.Specialized.OrderedDictionary])]
|
|
144
|
+
param()
|
|
145
|
+
|
|
146
|
+
return [ordered]@{
|
|
147
|
+
hookSpecificOutput = [ordered]@{
|
|
148
|
+
hookEventName = 'PreToolUse'
|
|
149
|
+
permissionDecision = 'allow'
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function Get-ParallelAbandonGateBlockDecision {
|
|
155
|
+
<#
|
|
156
|
+
.SYNOPSIS
|
|
157
|
+
Build the deny decision payload carrying the supplied reason.
|
|
158
|
+
.PARAMETER Reason
|
|
159
|
+
The deny reason surfaced to the caller.
|
|
160
|
+
.OUTPUTS
|
|
161
|
+
System.Collections.Specialized.OrderedDictionary
|
|
162
|
+
#>
|
|
163
|
+
[CmdletBinding()]
|
|
164
|
+
[OutputType([System.Collections.Specialized.OrderedDictionary])]
|
|
165
|
+
param(
|
|
166
|
+
[Parameter(Mandatory)]
|
|
167
|
+
[string] $Reason
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
return [ordered]@{
|
|
171
|
+
hookSpecificOutput = [ordered]@{
|
|
172
|
+
hookEventName = 'PreToolUse'
|
|
173
|
+
permissionDecision = 'deny'
|
|
174
|
+
permissionDecisionReason = $Reason
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function Get-ParallelAbandonGateBlockReason {
|
|
180
|
+
<#
|
|
181
|
+
.SYNOPSIS
|
|
182
|
+
Build the deny reason text from the two declared tokens.
|
|
183
|
+
.OUTPUTS
|
|
184
|
+
System.String
|
|
185
|
+
#>
|
|
186
|
+
[CmdletBinding()]
|
|
187
|
+
[OutputType([string])]
|
|
188
|
+
param()
|
|
189
|
+
|
|
190
|
+
return @(
|
|
191
|
+
"$($script:AbandonBlockedReasonCode): this command carries"
|
|
192
|
+
"'$($script:AbandonDispositionToken)' without the explicit"
|
|
193
|
+
"'$($script:AbandonConfirmToken)' confirmation marker in the same command."
|
|
194
|
+
'The abandon disposition closes the item pull request and removes its worktree,'
|
|
195
|
+
'so it must be confirmed deliberately. Add the confirmation marker to the same'
|
|
196
|
+
'command, or use the detach disposition instead. Do not reformulate the command'
|
|
197
|
+
'to evade this gate.'
|
|
198
|
+
) -join ' '
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function Invoke-ParallelAbandonGateDecision {
|
|
202
|
+
<#
|
|
203
|
+
.SYNOPSIS
|
|
204
|
+
Parses CLAUDE_TOOL_INPUT and returns an allow-or-block decision.
|
|
205
|
+
.PARAMETER ToolInputRaw
|
|
206
|
+
The raw JSON tool payload supplied by Claude Code.
|
|
207
|
+
.OUTPUTS
|
|
208
|
+
System.Collections.Specialized.OrderedDictionary
|
|
209
|
+
#>
|
|
210
|
+
[CmdletBinding()]
|
|
211
|
+
[OutputType([System.Collections.Specialized.OrderedDictionary])]
|
|
212
|
+
param(
|
|
213
|
+
[AllowNull()]
|
|
214
|
+
[AllowEmptyString()]
|
|
215
|
+
[string] $ToolInputRaw
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
# An absent payload carries no command to gate, so there is nothing in scope. This is
|
|
219
|
+
# allow rather than deny because the gate constrains one specific destructive command
|
|
220
|
+
# shape and must not become a blanket Bash block.
|
|
221
|
+
if (-not $ToolInputRaw) {
|
|
222
|
+
return Get-ParallelAbandonGateAllowDecision
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
try {
|
|
226
|
+
$toolInput = $ToolInputRaw | ConvertFrom-Json -ErrorAction Stop
|
|
227
|
+
} catch {
|
|
228
|
+
throw "enforce-parallel-abandon-gate hook received malformed JSON in CLAUDE_TOOL_INPUT: $_"
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
$normalizedCommand = Get-ParallelAbandonNormalizedCommand -CommandText ([string]$toolInput.command)
|
|
232
|
+
if (-not (Test-ParallelAbandonCommandInScope -NormalizedCommand $normalizedCommand)) {
|
|
233
|
+
return Get-ParallelAbandonGateAllowDecision
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
# In scope: the command asks for the destructive disposition, so the confirmation
|
|
237
|
+
# marker decides. Its presence is the caller's explicit acknowledgement.
|
|
238
|
+
if (Test-ParallelAbandonCommandConfirmed -NormalizedCommand $normalizedCommand) {
|
|
239
|
+
return Get-ParallelAbandonGateAllowDecision
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
return Get-ParallelAbandonGateBlockDecision -Reason (Get-ParallelAbandonGateBlockReason)
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
# Guard allows dot-sourcing in tests without executing the entrypoint.
|
|
246
|
+
if ($MyInvocation.InvocationName -eq '.') {
|
|
247
|
+
return
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
try {
|
|
251
|
+
$decision = Invoke-ParallelAbandonGateDecision -ToolInputRaw (Get-ParallelAbandonGateToolInput)
|
|
252
|
+
} catch {
|
|
253
|
+
Write-Error $_
|
|
254
|
+
exit 1
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
$decision | ConvertTo-Json -Compress -Depth 5 | Write-Output
|
|
258
|
+
|
|
259
|
+
exit 0
|