@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.
Files changed (66) hide show
  1. package/out/mcp-server.js +368 -209
  2. package/package.json +2 -2
  3. package/resources/claude-customizations/.claude/agents/epic-orchestrator.md +1 -1
  4. package/resources/claude-customizations/.claude/agents/epic-planner.md +4 -1
  5. package/resources/claude-customizations/.claude/agents/parallel-planner.md +18 -1
  6. package/resources/claude-customizations/.claude/agents/pr-author.md +15 -0
  7. package/resources/claude-customizations/.claude/hooks/enforce-completion-consistency.ps1 +16 -8
  8. package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate-authorization.ps1 +443 -0
  9. package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate.ps1 +33 -37
  10. package/resources/claude-customizations/.claude/hooks/enforce-model-routing-receipt.ps1 +120 -5
  11. package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-epic-scope.ps1 +127 -0
  12. package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-helpers.ps1 +172 -24
  13. package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate.ps1 +14 -27
  14. package/resources/claude-customizations/.claude/hooks/enforce-parallel-worktree-removal-gate.ps1 +66 -5
  15. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill-helpers.ps1 +134 -10
  16. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.epic-base-branch.ps1 +37 -8
  17. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.ps1 +3 -1
  18. package/resources/claude-customizations/.claude/hooks/enforce-prd-feature-before-planner-helpers.ps1 +332 -0
  19. package/resources/claude-customizations/.claude/hooks/enforce-prd-feature-before-planner.ps1 +211 -182
  20. package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadius.psm1 +43 -6
  21. package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusScheduling.psm1 +486 -0
  22. package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusValidation.psm1 +5 -2
  23. package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusWriteIntent.psm1 +446 -0
  24. package/resources/claude-customizations/.claude/lib/codex-routing/CodexDeployment.psm1 +1 -0
  25. package/resources/claude-customizations/.claude/lib/worktree-resolution/EpicScopeReadiness.psm1 +176 -0
  26. package/resources/claude-customizations/.claude/lib/worktree-resolution/EpicScopeResolution.psm1 +369 -0
  27. package/resources/claude-customizations/.claude/lib/worktree-resolution/WorktreeItemResolution.psm1 +392 -0
  28. package/resources/claude-customizations/.claude/lib/worktree-resolution/WorktreeResolution.psm1 +500 -0
  29. package/resources/claude-customizations/.claude/lib/worktree-resolution/WorktreeTargetResolution.psm1 +347 -0
  30. package/resources/claude-customizations/.claude/rules/orchestrator-state.md +27 -0
  31. package/resources/claude-customizations/.claude/rules/parallel-orchestration.md +150 -2
  32. package/resources/claude-customizations/.claude/rules/plan-acceptance-gates.md +5 -5
  33. package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/SKILL.md +12 -0
  34. package/resources/claude-customizations/.claude/skills/epic-orchestrate/SKILL.md +14 -1
  35. package/resources/claude-customizations/.claude/skills/epic-plan/SKILL.md +15 -0
  36. package/resources/claude-customizations/.claude/skills/orchestrate/SKILL.md +4 -1
  37. package/resources/claude-customizations/.claude/skills/parallel-add/SKILL.md +32 -17
  38. package/resources/claude-customizations/.claude/skills/parallel-orchestrate/SKILL.md +39 -6
  39. package/resources/claude-customizations/.claude/skills/parallel-plan/SKILL.md +39 -10
  40. package/resources/claude-customizations/.claude/skills/pr-author/SKILL.md +15 -0
  41. package/resources/claude-customizations/.claude/skills/quota-throttling/SKILL.md +221 -0
  42. package/resources/claude-customizations/config/blast-radius.json +14 -1
  43. package/resources/claude-customizations/pack-manifests/core.json +11 -0
  44. package/resources/codex-and-agents-customizations/.agents/skills/codex-model-routing/SKILL.md +13 -14
  45. package/resources/codex-and-agents-customizations/.codex/config.toml +1 -1
  46. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-merge-gate.ps1 +193 -0
  47. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-planning-only.ps1 +15 -5
  48. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-epic-resolution.ps1 +493 -0
  49. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-epic-scope.ps1 +189 -0
  50. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-helpers.ps1 +172 -24
  51. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate.ps1 +14 -27
  52. package/resources/codex-and-agents-customizations/.codex/scripts/Resolve-CodexDeployment.ps1 +87 -0
  53. package/resources/codex-and-agents-customizations/.codex/scripts/Resolve-CodexTopology.ps1 +96 -0
  54. package/resources/codex-and-agents-customizations/.codex/scripts/codex-routing-cli-common.ps1 +370 -0
  55. package/resources/codex-and-agents-customizations/.codex/scripts/epic-child-launch-contract.ps1 +475 -0
  56. package/resources/codex-and-agents-customizations/.codex/scripts/epic-child-launch-runtime.ps1 +445 -0
  57. package/resources/codex-and-agents-customizations/.codex/scripts/epic-child-persistence-runtime.ps1 +118 -0
  58. package/resources/codex-and-agents-customizations/.codex/scripts/epic-child-sandbox-preflight.ps1 +69 -0
  59. package/resources/codex-and-agents-customizations/.codex/scripts/launch-epic-child-wave.ps1 +471 -0
  60. package/resources/codex-and-agents-customizations/.codex/scripts/post-codex-worktree-session.ps1 +280 -0
  61. package/resources/codex-and-agents-customizations/.codex/scripts/resume-epic-child.ps1 +263 -0
  62. package/resources/codex-and-agents-customizations/.codex-variants/csharp-legacy/agents/csharp-typed-engineer.toml +2 -1
  63. package/resources/codex-and-agents-customizations/pack-manifests/core.json +20 -1
  64. package/resources/lib/codex-routing/CodexDeployment.psm1 +315 -0
  65. package/resources/lib/codex-routing/CodexTopology.psm1 +394 -0
  66. package/resources/powershell/PoshQC/settings/pester.runsettings.psd1 +37 -0
@@ -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
- poetry run python -m scripts.dev_tools.resolve_codex_topology `
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
- poetry run python -m scripts.dev_tools.resolve_codex_deployment `
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
- ```powershell
102
- poetry run python -m scripts.dev_tools.validate_orchestration_artifacts `
103
- orchestrator-state artifacts/orchestration/orchestrator-state.json `
104
- --require-codex-topology `
105
- --require-codex-model-routing
106
- ```
107
-
108
- For epic execution use `epic-orchestrator-state` with the same flag. The MCP
109
- `validate_orchestration_artifacts` surface is the authoritative completion gate
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.
@@ -2,7 +2,7 @@ default_permissions = ":danger-full-access"
2
2
 
3
3
  [mcp_servers.drm-copilot]
4
4
  command = "npx"
5
- args = ["-y", "@danmoisan/drm-copilot-mcp@1.1.11"]
5
+ args = ["-y", "@danmoisan/drm-copilot-mcp@1.1.14"]
6
6
  required = true
7
7
  enabled_tools = [
8
8
  "collect_commit_context",
@@ -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
  }
@@ -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 = $script:AllowedPreparationSemanticMcpTools -contains $toolName
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