@danmoisan/drm-copilot-mcp 1.1.11 → 1.1.14
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 +368 -209
- package/package.json +2 -2
- package/resources/claude-customizations/.claude/agents/epic-orchestrator.md +1 -1
- package/resources/claude-customizations/.claude/agents/epic-planner.md +4 -1
- package/resources/claude-customizations/.claude/agents/parallel-planner.md +18 -1
- package/resources/claude-customizations/.claude/agents/pr-author.md +15 -0
- package/resources/claude-customizations/.claude/hooks/enforce-completion-consistency.ps1 +16 -8
- package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate-authorization.ps1 +443 -0
- package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate.ps1 +33 -37
- package/resources/claude-customizations/.claude/hooks/enforce-model-routing-receipt.ps1 +120 -5
- package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-epic-scope.ps1 +127 -0
- package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-helpers.ps1 +172 -24
- package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate.ps1 +14 -27
- package/resources/claude-customizations/.claude/hooks/enforce-parallel-worktree-removal-gate.ps1 +66 -5
- package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill-helpers.ps1 +134 -10
- package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.epic-base-branch.ps1 +37 -8
- package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.ps1 +3 -1
- package/resources/claude-customizations/.claude/hooks/enforce-prd-feature-before-planner-helpers.ps1 +332 -0
- package/resources/claude-customizations/.claude/hooks/enforce-prd-feature-before-planner.ps1 +211 -182
- package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadius.psm1 +43 -6
- package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusScheduling.psm1 +486 -0
- package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusValidation.psm1 +5 -2
- package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusWriteIntent.psm1 +446 -0
- package/resources/claude-customizations/.claude/lib/codex-routing/CodexDeployment.psm1 +1 -0
- package/resources/claude-customizations/.claude/lib/worktree-resolution/EpicScopeReadiness.psm1 +176 -0
- package/resources/claude-customizations/.claude/lib/worktree-resolution/EpicScopeResolution.psm1 +369 -0
- package/resources/claude-customizations/.claude/lib/worktree-resolution/WorktreeItemResolution.psm1 +392 -0
- package/resources/claude-customizations/.claude/lib/worktree-resolution/WorktreeResolution.psm1 +500 -0
- package/resources/claude-customizations/.claude/lib/worktree-resolution/WorktreeTargetResolution.psm1 +347 -0
- package/resources/claude-customizations/.claude/rules/orchestrator-state.md +27 -0
- package/resources/claude-customizations/.claude/rules/parallel-orchestration.md +150 -2
- package/resources/claude-customizations/.claude/rules/plan-acceptance-gates.md +5 -5
- package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/SKILL.md +12 -0
- package/resources/claude-customizations/.claude/skills/epic-orchestrate/SKILL.md +14 -1
- package/resources/claude-customizations/.claude/skills/epic-plan/SKILL.md +15 -0
- package/resources/claude-customizations/.claude/skills/orchestrate/SKILL.md +4 -1
- package/resources/claude-customizations/.claude/skills/parallel-add/SKILL.md +32 -17
- package/resources/claude-customizations/.claude/skills/parallel-orchestrate/SKILL.md +39 -6
- package/resources/claude-customizations/.claude/skills/parallel-plan/SKILL.md +39 -10
- package/resources/claude-customizations/.claude/skills/pr-author/SKILL.md +15 -0
- package/resources/claude-customizations/.claude/skills/quota-throttling/SKILL.md +221 -0
- package/resources/claude-customizations/config/blast-radius.json +14 -1
- package/resources/claude-customizations/pack-manifests/core.json +11 -0
- package/resources/codex-and-agents-customizations/.agents/skills/codex-model-routing/SKILL.md +13 -14
- package/resources/codex-and-agents-customizations/.codex/config.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-merge-gate.ps1 +193 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-planning-only.ps1 +15 -5
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-epic-resolution.ps1 +493 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-epic-scope.ps1 +189 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-helpers.ps1 +172 -24
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate.ps1 +14 -27
- package/resources/codex-and-agents-customizations/.codex/scripts/Resolve-CodexDeployment.ps1 +87 -0
- package/resources/codex-and-agents-customizations/.codex/scripts/Resolve-CodexTopology.ps1 +96 -0
- package/resources/codex-and-agents-customizations/.codex/scripts/codex-routing-cli-common.ps1 +370 -0
- package/resources/codex-and-agents-customizations/.codex/scripts/epic-child-launch-contract.ps1 +475 -0
- package/resources/codex-and-agents-customizations/.codex/scripts/epic-child-launch-runtime.ps1 +445 -0
- package/resources/codex-and-agents-customizations/.codex/scripts/epic-child-persistence-runtime.ps1 +118 -0
- package/resources/codex-and-agents-customizations/.codex/scripts/epic-child-sandbox-preflight.ps1 +69 -0
- package/resources/codex-and-agents-customizations/.codex/scripts/launch-epic-child-wave.ps1 +471 -0
- package/resources/codex-and-agents-customizations/.codex/scripts/post-codex-worktree-session.ps1 +280 -0
- package/resources/codex-and-agents-customizations/.codex/scripts/resume-epic-child.ps1 +263 -0
- package/resources/codex-and-agents-customizations/.codex-variants/csharp-legacy/agents/csharp-typed-engineer.toml +2 -1
- package/resources/codex-and-agents-customizations/pack-manifests/core.json +20 -1
- package/resources/lib/codex-routing/CodexDeployment.psm1 +315 -0
- package/resources/lib/codex-routing/CodexTopology.psm1 +394 -0
- package/resources/powershell/PoshQC/settings/pester.runsettings.psd1 +37 -0
package/resources/codex-and-agents-customizations/.agents/skills/codex-model-routing/SKILL.md
CHANGED
|
@@ -16,7 +16,7 @@ agent profile.
|
|
|
16
16
|
2. Run the canonical topology resolver before choosing a logical agent:
|
|
17
17
|
|
|
18
18
|
```powershell
|
|
19
|
-
|
|
19
|
+
pwsh -NoProfile -File .codex/scripts/Resolve-CodexTopology.ps1 `
|
|
20
20
|
--language <python|powershell|csharp|typescript> `
|
|
21
21
|
--production-file-count <count> `
|
|
22
22
|
--test-file-count <count> `
|
|
@@ -53,7 +53,7 @@ epic root personas are forced independently of file count.
|
|
|
53
53
|
5. Run the canonical resolver:
|
|
54
54
|
|
|
55
55
|
```powershell
|
|
56
|
-
|
|
56
|
+
pwsh -NoProfile -File .codex/scripts/Resolve-CodexDeployment.ps1 `
|
|
57
57
|
--logical-agent <agent> `
|
|
58
58
|
--complexity-band <C1|C2|C3|C4> `
|
|
59
59
|
--execution-context <context> `
|
|
@@ -96,15 +96,14 @@ incomplete, and request a policy change. Do not silently fall back.
|
|
|
96
96
|
## Validation
|
|
97
97
|
|
|
98
98
|
Before accepting delegated results or reporting completion, validate the
|
|
99
|
-
checkpoint with the Codex routing gate
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
when available.
|
|
99
|
+
checkpoint with the Codex routing gate by calling the MCP tool
|
|
100
|
+
`validate_orchestration_artifacts` with these arguments:
|
|
101
|
+
|
|
102
|
+
- `artifact_type`: `orchestrator-state` (use `epic-orchestrator-state` for epic
|
|
103
|
+
execution)
|
|
104
|
+
- `artifact_path`: `artifacts/orchestration/orchestrator-state.json`
|
|
105
|
+
- `workspace_root`: the repository root
|
|
106
|
+
- `require_codex_topology`: `true`
|
|
107
|
+
- `require_codex_model_routing`: `true`
|
|
108
|
+
|
|
109
|
+
The MCP tool is the authoritative completion gate and needs no Python toolchain.
|
|
@@ -11,6 +11,14 @@ param()
|
|
|
11
11
|
. (Join-Path $PSScriptRoot 'hook-command-scanner.ps1')
|
|
12
12
|
. (Join-Path $PSScriptRoot 'hook-command-invocation.ps1')
|
|
13
13
|
|
|
14
|
+
# Reason codes for the standalone-merge authorization branch (issue #670), spelled
|
|
15
|
+
# byte-identically to the Claude helpers file. Each of the four assignments below is the only
|
|
16
|
+
# place its code literal appears in this file.
|
|
17
|
+
$script:CodexStandaloneAbsentReasonCode = 'STANDALONE_MERGE_AUTHORIZATION_ABSENT'
|
|
18
|
+
$script:CodexStandalonePrMismatchReasonCode = 'STANDALONE_MERGE_AUTHORIZATION_PR_MISMATCH'
|
|
19
|
+
$script:CodexStandaloneMalformedReasonCode = 'STANDALONE_MERGE_AUTHORIZATION_MALFORMED'
|
|
20
|
+
$script:CodexStandaloneNotPrSpecificReasonCode = 'STANDALONE_MERGE_AUTHORIZATION_NOT_PR_SPECIFIC'
|
|
21
|
+
|
|
14
22
|
function ConvertFrom-CodexMergeJson {
|
|
15
23
|
[CmdletBinding()]
|
|
16
24
|
param([AllowNull()][AllowEmptyString()][string] $Raw, [Parameter(Mandatory)][string] $Name, [switch] $Optional)
|
|
@@ -155,6 +163,24 @@ function Invoke-CodexEpicMergeDecision {
|
|
|
155
163
|
return $null
|
|
156
164
|
}
|
|
157
165
|
|
|
166
|
+
# Standalone branch (issue #670), evaluated last and only for an explicit PR number: a
|
|
167
|
+
# PR-specific authorization record bound to the payload session. Its deny keeps the gate
|
|
168
|
+
# token first.
|
|
169
|
+
if ($null -ne $prNumber) {
|
|
170
|
+
$sessionId = [string]$payload.session_id
|
|
171
|
+
$verdict = Test-CodexStandaloneMergeAuthorization -Checkpoints @($child, $epic) -CommandPrNumber $prNumber -SessionId $sessionId
|
|
172
|
+
if ($verdict.Allowed) {
|
|
173
|
+
return $null
|
|
174
|
+
}
|
|
175
|
+
return [ordered]@{
|
|
176
|
+
hookSpecificOutput = [ordered]@{
|
|
177
|
+
hookEventName = 'PreToolUse'
|
|
178
|
+
permissionDecision = 'deny'
|
|
179
|
+
permissionDecisionReason = 'EPIC_MERGE_GATE_BLOCKED: ' + $verdict.ReasonCode + ': ' + $verdict.Message
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
158
184
|
return [ordered]@{
|
|
159
185
|
hookSpecificOutput = [ordered]@{
|
|
160
186
|
hookEventName = 'PreToolUse'
|
|
@@ -164,6 +190,173 @@ function Invoke-CodexEpicMergeDecision {
|
|
|
164
190
|
}
|
|
165
191
|
}
|
|
166
192
|
|
|
193
|
+
function Test-CodexStandalonePositiveInteger {
|
|
194
|
+
<#
|
|
195
|
+
.SYNOPSIS
|
|
196
|
+
Report whether a parsed JSON value is an integer greater than zero; a digit string fails.
|
|
197
|
+
#>
|
|
198
|
+
[CmdletBinding()]
|
|
199
|
+
[OutputType([bool])]
|
|
200
|
+
param([AllowNull()] $Value)
|
|
201
|
+
|
|
202
|
+
return (($Value -is [int] -or $Value -is [long]) -and $Value -gt 0)
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function Test-CodexStandaloneText {
|
|
206
|
+
<#
|
|
207
|
+
.SYNOPSIS
|
|
208
|
+
Report whether a value is a string whose trimmed length reaches a minimum.
|
|
209
|
+
#>
|
|
210
|
+
[CmdletBinding()]
|
|
211
|
+
[OutputType([bool])]
|
|
212
|
+
param([AllowNull()] $Value, [int] $MinimumLength = 1)
|
|
213
|
+
|
|
214
|
+
return ($Value -is [string] -and $Value.Trim().Length -ge $MinimumLength)
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function Get-CodexStandaloneField {
|
|
218
|
+
<#
|
|
219
|
+
.SYNOPSIS
|
|
220
|
+
Read a named field off a parsed record, or $null; the unary comma keeps a
|
|
221
|
+
one-element array from unrolling into its scalar.
|
|
222
|
+
#>
|
|
223
|
+
[CmdletBinding()]
|
|
224
|
+
[OutputType([object], [object[]])]
|
|
225
|
+
param([Parameter(Mandatory)] $Record, [Parameter(Mandatory)][string] $Name)
|
|
226
|
+
|
|
227
|
+
$property = $Record.PSObject.Properties[$Name]
|
|
228
|
+
if ($null -eq $property) {
|
|
229
|
+
return $null
|
|
230
|
+
}
|
|
231
|
+
return , $property.Value
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function Get-CodexStandaloneRecordFailure {
|
|
235
|
+
<#
|
|
236
|
+
.SYNOPSIS
|
|
237
|
+
Return the first failing field of a matched record, or $null when every check passes.
|
|
238
|
+
.DESCRIPTION
|
|
239
|
+
Uses the same fixed order as the Claude helpers file: pr_url, issue_num, branch_name,
|
|
240
|
+
authorized_by, authorized_at, basis, run_slug, session_id. branch_name is checked for
|
|
241
|
+
presence only. A blank payload session fails closed.
|
|
242
|
+
#>
|
|
243
|
+
[CmdletBinding()]
|
|
244
|
+
[OutputType([string])]
|
|
245
|
+
param([Parameter(Mandatory)] $Record, [AllowNull()][AllowEmptyString()][string] $SessionId)
|
|
246
|
+
|
|
247
|
+
$prUrl = Get-CodexStandaloneField -Record $Record -Name 'pr_url'
|
|
248
|
+
if (-not (Test-CodexStandaloneText -Value $prUrl) -or
|
|
249
|
+
-not $prUrl.EndsWith('/pull/' + [string]$Record.pr_number, [System.StringComparison]::Ordinal)) {
|
|
250
|
+
return 'pr_url'
|
|
251
|
+
}
|
|
252
|
+
if (-not (Test-CodexStandalonePositiveInteger -Value (Get-CodexStandaloneField -Record $Record -Name 'issue_num'))) {
|
|
253
|
+
return 'issue_num'
|
|
254
|
+
}
|
|
255
|
+
foreach ($name in @('branch_name', 'authorized_by')) {
|
|
256
|
+
if (-not (Test-CodexStandaloneText -Value (Get-CodexStandaloneField -Record $Record -Name $name))) {
|
|
257
|
+
return $name
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
$authorizedAt = Get-CodexStandaloneField -Record $Record -Name 'authorized_at'
|
|
261
|
+
$parsedAt = [DateTime]::MinValue
|
|
262
|
+
$timestampValid = $authorizedAt -is [DateTime] -or $authorizedAt -is [DateTimeOffset]
|
|
263
|
+
if (-not $timestampValid -and (Test-CodexStandaloneText -Value $authorizedAt)) {
|
|
264
|
+
$timestampValid = [DateTime]::TryParse(
|
|
265
|
+
$authorizedAt,
|
|
266
|
+
[System.Globalization.CultureInfo]::InvariantCulture,
|
|
267
|
+
[System.Globalization.DateTimeStyles]::AdjustToUniversal -bor [System.Globalization.DateTimeStyles]::AssumeUniversal,
|
|
268
|
+
[ref] $parsedAt)
|
|
269
|
+
}
|
|
270
|
+
if (-not $timestampValid) {
|
|
271
|
+
return 'authorized_at'
|
|
272
|
+
}
|
|
273
|
+
if (-not (Test-CodexStandaloneText -Value (Get-CodexStandaloneField -Record $Record -Name 'basis') -MinimumLength 20)) {
|
|
274
|
+
return 'basis'
|
|
275
|
+
}
|
|
276
|
+
if ($null -ne $Record.PSObject.Properties['run_slug'] -and
|
|
277
|
+
-not (Test-CodexStandaloneText -Value (Get-CodexStandaloneField -Record $Record -Name 'run_slug'))) {
|
|
278
|
+
return 'run_slug'
|
|
279
|
+
}
|
|
280
|
+
$recordSession = Get-CodexStandaloneField -Record $Record -Name 'session_id'
|
|
281
|
+
if (-not (Test-CodexStandaloneText -Value $recordSession) -or
|
|
282
|
+
[string]::IsNullOrWhiteSpace($SessionId) -or
|
|
283
|
+
-not [string]::Equals($recordSession, $SessionId, [System.StringComparison]::Ordinal)) {
|
|
284
|
+
return 'session_id'
|
|
285
|
+
}
|
|
286
|
+
return $null
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
function Test-CodexStandaloneMergeAuthorization {
|
|
290
|
+
<#
|
|
291
|
+
.SYNOPSIS
|
|
292
|
+
Codex counterpart of the Claude standalone-merge authorization predicate.
|
|
293
|
+
.DESCRIPTION
|
|
294
|
+
Evaluates the parsed child and epic checkpoints with the same activation condition
|
|
295
|
+
as the Claude side: a PR-specific record naming CommandPrNumber, with well-formed
|
|
296
|
+
fields and a session_id equal to the payload session. Returns an object whose
|
|
297
|
+
Allowed member is $true on success, or $false with ReasonCode and Message.
|
|
298
|
+
#>
|
|
299
|
+
[CmdletBinding()]
|
|
300
|
+
[OutputType([pscustomobject])]
|
|
301
|
+
param(
|
|
302
|
+
[AllowNull()][AllowEmptyCollection()][object[]] $Checkpoints,
|
|
303
|
+
[Parameter(Mandatory)][int] $CommandPrNumber,
|
|
304
|
+
[AllowNull()][AllowEmptyString()][string] $SessionId
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
$key = 'standalone_merge_authorizations'
|
|
308
|
+
$present = $false
|
|
309
|
+
$specific = $true
|
|
310
|
+
$match = $null
|
|
311
|
+
foreach ($checkpoint in @($Checkpoints)) {
|
|
312
|
+
if ($checkpoint -isnot [System.Management.Automation.PSCustomObject] -or
|
|
313
|
+
$null -eq $checkpoint.PSObject.Properties[$key]) {
|
|
314
|
+
continue
|
|
315
|
+
}
|
|
316
|
+
$present = $true
|
|
317
|
+
$block = $checkpoint.PSObject.Properties[$key].Value
|
|
318
|
+
if ($block -isnot [System.Array] -or $block.Count -eq 0) {
|
|
319
|
+
$specific = $false
|
|
320
|
+
continue
|
|
321
|
+
}
|
|
322
|
+
foreach ($entry in $block) {
|
|
323
|
+
$number = $null
|
|
324
|
+
if ($entry -is [System.Management.Automation.PSCustomObject]) {
|
|
325
|
+
$number = Get-CodexStandaloneField -Record $entry -Name 'pr_number'
|
|
326
|
+
}
|
|
327
|
+
if (-not (Test-CodexStandalonePositiveInteger -Value $number)) {
|
|
328
|
+
$specific = $false
|
|
329
|
+
} elseif ($null -eq $match -and $number -eq $CommandPrNumber) {
|
|
330
|
+
$match = $entry
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
if (-not $present) {
|
|
336
|
+
$code = $script:CodexStandaloneAbsentReasonCode
|
|
337
|
+
$message = 'no orchestrator checkpoint carries an authorization record, but an authorized standalone merge path exists.'
|
|
338
|
+
} elseif (-not $specific) {
|
|
339
|
+
$code = $script:CodexStandaloneNotPrSpecificReasonCode
|
|
340
|
+
$message = 'an orchestrator checkpoint carries the authorization key but names no specific pull request; a blanket flag is not an authorization record.'
|
|
341
|
+
} elseif ($null -eq $match) {
|
|
342
|
+
$code = $script:CodexStandalonePrMismatchReasonCode
|
|
343
|
+
$message = "authorization records exist, but none names pull request $CommandPrNumber."
|
|
344
|
+
} else {
|
|
345
|
+
$field = Get-CodexStandaloneRecordFailure -Record $match -SessionId $SessionId
|
|
346
|
+
if ($null -eq $field) {
|
|
347
|
+
return [pscustomobject]@{ Allowed = $true; ReasonCode = $null; Message = $null }
|
|
348
|
+
}
|
|
349
|
+
$code = $script:CodexStandaloneMalformedReasonCode
|
|
350
|
+
$message = "the matched authorization record has an invalid '$field'."
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
return [pscustomobject]@{
|
|
354
|
+
Allowed = $false
|
|
355
|
+
ReasonCode = $code
|
|
356
|
+
Message = "$message To authorize a standalone merge, write a $key record naming this pull request and the current session_id into an orchestrator checkpoint, as described in .claude/rules/orchestrator-state.md."
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
167
360
|
if ($MyInvocation.InvocationName -eq '.') {
|
|
168
361
|
return
|
|
169
362
|
}
|
package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-planning-only.ps1
CHANGED
|
@@ -83,9 +83,6 @@ function Get-EpicPlanningRegisteredMcpTool {
|
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
$script:EpicPlanningRepositoryRoot = Split-Path (Split-Path $PSScriptRoot -Parent) -Parent
|
|
86
|
-
$script:AllowedPreparationSemanticMcpTools = @(Get-EpicPlanningRegisteredMcpTool `
|
|
87
|
-
-RegistryPath (Join-Path $script:EpicPlanningRepositoryRoot 'config/orchestration-handoff-registry.json') `
|
|
88
|
-
-SemanticIds $script:PreparationSemanticMcpIds)
|
|
89
86
|
|
|
90
87
|
function Get-EpicPlanningDenyDecision {
|
|
91
88
|
[CmdletBinding()]
|
|
@@ -209,7 +206,8 @@ function Invoke-EpicPlanningOnlyDecision {
|
|
|
209
206
|
[AllowNull()][AllowEmptyString()][string] $CheckpointRaw,
|
|
210
207
|
[AllowNull()][AllowEmptyString()][string] $EpicExecutionContext,
|
|
211
208
|
[AllowNull()][string[]] $StagedPaths,
|
|
212
|
-
[AllowNull()][AllowEmptyString()][string] $CurrentBranch
|
|
209
|
+
[AllowNull()][AllowEmptyString()][string] $CurrentBranch,
|
|
210
|
+
[string] $RegistryPath = (Join-Path $script:EpicPlanningRepositoryRoot 'config/orchestration-handoff-registry.json')
|
|
213
211
|
)
|
|
214
212
|
|
|
215
213
|
$payload = ConvertFrom-EpicPlanningJson -Raw $PayloadRaw -Name 'PreToolUse input'
|
|
@@ -269,8 +267,20 @@ function Invoke-EpicPlanningOnlyDecision {
|
|
|
269
267
|
return Get-EpicPlanningDenyDecision -Reason 'the Bash command is outside the preparation read, branch, commit, push, or validator allowlist.'
|
|
270
268
|
}
|
|
271
269
|
|
|
270
|
+
# Lifecycle tools are decided first and never need the registry. Only a
|
|
271
|
+
# registry-dependent mcp__ call reads it, so a destination without the
|
|
272
|
+
# registry keeps every other decision and fails closed for semantic MCP.
|
|
272
273
|
$allowedRepositoryMcp = $script:AllowedPreparationMcpTools -contains $toolName
|
|
273
|
-
$allowedSemanticMcp = $
|
|
274
|
+
$allowedSemanticMcp = $false
|
|
275
|
+
if (-not $allowedRepositoryMcp -and $toolName -like 'mcp__*') {
|
|
276
|
+
if (-not (Test-Path -LiteralPath $RegistryPath -PathType Leaf)) {
|
|
277
|
+
return Get-EpicPlanningDenyDecision -Reason "semantic MCP registry '$RegistryPath' does not exist; registry-dependent MCP tool '$toolName' is denied in preparation mode."
|
|
278
|
+
}
|
|
279
|
+
$registeredSemanticMcp = @(Get-EpicPlanningRegisteredMcpTool `
|
|
280
|
+
-RegistryPath $RegistryPath `
|
|
281
|
+
-SemanticIds $script:PreparationSemanticMcpIds)
|
|
282
|
+
$allowedSemanticMcp = $registeredSemanticMcp -contains $toolName
|
|
283
|
+
}
|
|
274
284
|
if ($allowedRepositoryMcp -or $allowedSemanticMcp) {
|
|
275
285
|
if ($attestedPreparation) {
|
|
276
286
|
$workspaceRoot = [string]$payload.tool_input.workspace_root
|