@danmoisan/drm-copilot-mcp 1.1.11 → 1.1.12

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 (52) hide show
  1. package/out/mcp-server.js +366 -159
  2. package/package.json +1 -1
  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/pr-author.md +15 -0
  6. package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate-authorization.ps1 +443 -0
  7. package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate.ps1 +33 -37
  8. package/resources/claude-customizations/.claude/hooks/enforce-model-routing-receipt.ps1 +120 -5
  9. package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-epic-scope.ps1 +127 -0
  10. package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-helpers.ps1 +163 -15
  11. package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate.ps1 +14 -27
  12. package/resources/claude-customizations/.claude/hooks/enforce-parallel-worktree-removal-gate.ps1 +66 -5
  13. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill-helpers.ps1 +134 -10
  14. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.epic-base-branch.ps1 +37 -8
  15. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.ps1 +3 -1
  16. package/resources/claude-customizations/.claude/hooks/enforce-prd-feature-before-planner-helpers.ps1 +332 -0
  17. package/resources/claude-customizations/.claude/hooks/enforce-prd-feature-before-planner.ps1 +211 -182
  18. package/resources/claude-customizations/.claude/lib/codex-routing/CodexDeployment.psm1 +1 -0
  19. package/resources/claude-customizations/.claude/lib/worktree-resolution/EpicScopeReadiness.psm1 +176 -0
  20. package/resources/claude-customizations/.claude/lib/worktree-resolution/EpicScopeResolution.psm1 +369 -0
  21. package/resources/claude-customizations/.claude/lib/worktree-resolution/WorktreeItemResolution.psm1 +392 -0
  22. package/resources/claude-customizations/.claude/lib/worktree-resolution/WorktreeResolution.psm1 +500 -0
  23. package/resources/claude-customizations/.claude/lib/worktree-resolution/WorktreeTargetResolution.psm1 +347 -0
  24. package/resources/claude-customizations/.claude/rules/orchestrator-state.md +27 -0
  25. package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/SKILL.md +12 -0
  26. package/resources/claude-customizations/.claude/skills/epic-orchestrate/SKILL.md +14 -1
  27. package/resources/claude-customizations/.claude/skills/epic-plan/SKILL.md +4 -0
  28. package/resources/claude-customizations/.claude/skills/orchestrate/SKILL.md +4 -1
  29. package/resources/claude-customizations/.claude/skills/parallel-orchestrate/SKILL.md +39 -6
  30. package/resources/claude-customizations/.claude/skills/pr-author/SKILL.md +15 -0
  31. package/resources/claude-customizations/.claude/skills/quota-throttling/SKILL.md +221 -0
  32. package/resources/claude-customizations/pack-manifests/core.json +9 -0
  33. package/resources/codex-and-agents-customizations/.agents/skills/codex-model-routing/SKILL.md +13 -14
  34. package/resources/codex-and-agents-customizations/.codex/config.toml +1 -1
  35. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-merge-gate.ps1 +193 -0
  36. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-planning-only.ps1 +15 -5
  37. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-helpers.ps1 +163 -15
  38. package/resources/codex-and-agents-customizations/.codex/scripts/Resolve-CodexDeployment.ps1 +87 -0
  39. package/resources/codex-and-agents-customizations/.codex/scripts/Resolve-CodexTopology.ps1 +96 -0
  40. package/resources/codex-and-agents-customizations/.codex/scripts/codex-routing-cli-common.ps1 +370 -0
  41. package/resources/codex-and-agents-customizations/.codex/scripts/epic-child-launch-contract.ps1 +475 -0
  42. package/resources/codex-and-agents-customizations/.codex/scripts/epic-child-launch-runtime.ps1 +445 -0
  43. package/resources/codex-and-agents-customizations/.codex/scripts/epic-child-persistence-runtime.ps1 +118 -0
  44. package/resources/codex-and-agents-customizations/.codex/scripts/epic-child-sandbox-preflight.ps1 +69 -0
  45. package/resources/codex-and-agents-customizations/.codex/scripts/launch-epic-child-wave.ps1 +471 -0
  46. package/resources/codex-and-agents-customizations/.codex/scripts/post-codex-worktree-session.ps1 +280 -0
  47. package/resources/codex-and-agents-customizations/.codex/scripts/resume-epic-child.ps1 +263 -0
  48. package/resources/codex-and-agents-customizations/.codex-variants/csharp-legacy/agents/csharp-typed-engineer.toml +2 -1
  49. package/resources/codex-and-agents-customizations/pack-manifests/core.json +18 -1
  50. package/resources/lib/codex-routing/CodexDeployment.psm1 +315 -0
  51. package/resources/lib/codex-routing/CodexTopology.psm1 +394 -0
  52. package/resources/powershell/PoshQC/settings/pester.runsettings.psd1 +26 -0
@@ -0,0 +1,370 @@
1
+ #Requires -Version 7.0
2
+ <#
3
+ .SYNOPSIS
4
+ Shared helpers for the Codex routing CLI wrappers (issue #697).
5
+
6
+ .DESCRIPTION
7
+ Dot-sourced by Resolve-CodexTopology.ps1 and Resolve-CodexDeployment.ps1 so
8
+ the two wrappers share one implementation of module-candidate resolution,
9
+ GNU-style token parsing, and Python-compatible JSON serialization. The
10
+ wrappers replace `poetry run python -m scripts.dev_tools.resolve_codex_*` in
11
+ destinations that have no Python toolchain, and must print byte-identical
12
+ receipts, so the serializer reproduces Python
13
+ `json.dumps(receipt, indent=2, sort_keys=True)` with `ensure_ascii=True`.
14
+
15
+ This file defines functions only and has no top-level side effects.
16
+ #>
17
+
18
+ function Get-CodexRoutingModuleCandidate {
19
+ <#
20
+ .SYNOPSIS
21
+ Return the ordered module candidate paths for one codex-routing module.
22
+ .DESCRIPTION
23
+ The first candidate is the published destination location
24
+ (.codex/lib/codex-routing beside .codex/scripts). The second is the
25
+ self-hosting location in this repository (.claude/lib/codex-routing),
26
+ used where no .codex/lib directory exists.
27
+ .PARAMETER ScriptRoot
28
+ Directory of the calling wrapper (its $PSScriptRoot).
29
+ .PARAMETER ModuleName
30
+ Module file name, for example CodexTopology.psm1.
31
+ .OUTPUTS
32
+ System.String[] - the two candidate paths in resolution order.
33
+ #>
34
+ [CmdletBinding()]
35
+ [OutputType([string[]])]
36
+ param(
37
+ [Parameter(Mandatory = $true)][string] $ScriptRoot,
38
+ [Parameter(Mandatory = $true)][string] $ModuleName
39
+ )
40
+
41
+ return [string[]]@(
42
+ (Join-Path $ScriptRoot "../lib/codex-routing/$ModuleName"),
43
+ (Join-Path $ScriptRoot "../../.claude/lib/codex-routing/$ModuleName")
44
+ )
45
+ }
46
+
47
+ function Resolve-CodexRoutingModulePath {
48
+ <#
49
+ .SYNOPSIS
50
+ Return the first existing module candidate, or $null when none exists.
51
+ .PARAMETER Candidate
52
+ Candidate module paths in resolution order.
53
+ .OUTPUTS
54
+ System.String - the first candidate that is an existing file, or $null.
55
+ #>
56
+ [CmdletBinding()]
57
+ [OutputType([string])]
58
+ param(
59
+ [Parameter(Mandatory = $true)][AllowEmptyCollection()][string[]] $Candidate
60
+ )
61
+
62
+ # The first existing candidate wins, so a published destination module
63
+ # always takes precedence over the self-hosting fallback.
64
+ foreach ($path in $Candidate) {
65
+ if (Test-Path -LiteralPath $path -PathType Leaf) {
66
+ return $path
67
+ }
68
+ }
69
+ return $null
70
+ }
71
+
72
+ function Test-CodexRoutingOptionValueToken {
73
+ <#
74
+ .SYNOPSIS
75
+ Decide whether a token can be consumed as an option value.
76
+ .DESCRIPTION
77
+ Mirrors argparse: a token that looks like an option (a leading '-' that
78
+ is not a negative number) is not a value, so the preceding option
79
+ reports that it expected one argument.
80
+ .PARAMETER Token
81
+ The candidate value token.
82
+ .OUTPUTS
83
+ System.Boolean
84
+ #>
85
+ [CmdletBinding()]
86
+ [OutputType([bool])]
87
+ param([Parameter(Mandatory = $true)][AllowEmptyString()][string] $Token)
88
+
89
+ if (-not $Token.StartsWith('-')) {
90
+ return $true
91
+ }
92
+ return ($Token -match '^-\d+$|^-\d*\.\d+$')
93
+ }
94
+
95
+ function ConvertFrom-CodexRoutingArgument {
96
+ <#
97
+ .SYNOPSIS
98
+ Parse GNU-style CLI tokens against a wrapper specification.
99
+ .DESCRIPTION
100
+ Supports `--flag value` and `--flag=value`. An `append` option collects
101
+ every occurrence; a `switch` option takes no value; for other options
102
+ the last occurrence wins. Error messages follow argparse wording so the
103
+ wrappers report the same failures as the Python CLIs.
104
+ .PARAMETER Arguments
105
+ The CLI tokens.
106
+ .PARAMETER Specification
107
+ Ordered dictionary from option name (for example '--language') to a
108
+ hashtable with Key, Kind ('value', 'append', 'switch', or 'int'),
109
+ Required, optional Choices, and Default.
110
+ .OUTPUTS
111
+ PSCustomObject with Values (hashtable keyed by each option's Key) and
112
+ Error ($null, or an argparse-style 'error: ...' message).
113
+ #>
114
+ [CmdletBinding()]
115
+ [OutputType([pscustomobject])]
116
+ param(
117
+ [Parameter(Mandatory = $true)][AllowEmptyCollection()][AllowEmptyString()][string[]] $Arguments,
118
+ [Parameter(Mandatory = $true)][System.Collections.IDictionary] $Specification
119
+ )
120
+
121
+ $values = @{}
122
+ $appended = @{}
123
+ $seen = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::Ordinal)
124
+ $unrecognized = [System.Collections.Generic.List[string]]::new()
125
+
126
+ # Seed defaults so every key is present even when its option is absent.
127
+ foreach ($name in $Specification.Keys) {
128
+ $entry = $Specification[$name]
129
+ if ($entry.Kind -eq 'append') {
130
+ $appended[$entry.Key] = [System.Collections.Generic.List[string]]::new()
131
+ } else {
132
+ $values[$entry.Key] = $entry.Default
133
+ }
134
+ }
135
+
136
+ # Consume tokens left to right. Value errors stop immediately, as argparse
137
+ # does; unknown options and positional tokens are reported after the
138
+ # required-option check, matching argparse's reporting order.
139
+ $index = 0
140
+ while ($index -lt $Arguments.Count) {
141
+ $token = $Arguments[$index]
142
+ $index++
143
+ if ($token -eq '--') {
144
+ # Every token after the separator is positional, and neither
145
+ # wrapper accepts positional tokens.
146
+ for ($rest = $index; $rest -lt $Arguments.Count; $rest++) {
147
+ $unrecognized.Add($Arguments[$rest])
148
+ }
149
+ break
150
+ }
151
+ if (-not $token.StartsWith('--') -or $token.Length -le 2) {
152
+ $unrecognized.Add($token)
153
+ continue
154
+ }
155
+
156
+ $separator = $token.IndexOf('=')
157
+ $name = if ($separator -ge 0) { $token.Substring(0, $separator) } else { $token }
158
+ $inlineValue = if ($separator -ge 0) { $token.Substring($separator + 1) } else { $null }
159
+ if (-not $Specification.Contains($name)) {
160
+ $unrecognized.Add($token)
161
+ continue
162
+ }
163
+ $entry = $Specification[$name]
164
+ [void]$seen.Add($name)
165
+
166
+ # A switch takes no value; every other kind requires exactly one.
167
+ if ($entry.Kind -eq 'switch') {
168
+ if ($null -ne $inlineValue) {
169
+ return [pscustomobject]@{ Values = $values; Error = "error: argument ${name}: ignored explicit argument '$inlineValue'" }
170
+ }
171
+ $values[$entry.Key] = $true
172
+ continue
173
+ }
174
+ if ($null -ne $inlineValue) {
175
+ $value = $inlineValue
176
+ } elseif ($index -lt $Arguments.Count -and (Test-CodexRoutingOptionValueToken -Token $Arguments[$index])) {
177
+ $value = $Arguments[$index]
178
+ $index++
179
+ } else {
180
+ return [pscustomobject]@{ Values = $values; Error = "error: argument ${name}: expected one argument" }
181
+ }
182
+
183
+ if ($null -ne $entry.Choices -and @($entry.Choices) -cnotcontains $value) {
184
+ $choices = (@($entry.Choices) | ForEach-Object { "'$_'" }) -join ', '
185
+ return [pscustomobject]@{ Values = $values; Error = "error: argument ${name}: invalid choice: '$value' (choose from $choices)" }
186
+ }
187
+ if ($entry.Kind -eq 'int') {
188
+ $number = 0
189
+ $parsedNumber = [int]::TryParse(
190
+ $value,
191
+ [System.Globalization.NumberStyles]::Integer,
192
+ [System.Globalization.CultureInfo]::InvariantCulture,
193
+ [ref] $number)
194
+ if (-not $parsedNumber) {
195
+ return [pscustomobject]@{ Values = $values; Error = "error: argument ${name}: invalid int value: '$value'" }
196
+ }
197
+ $values[$entry.Key] = $number
198
+ } elseif ($entry.Kind -eq 'append') {
199
+ $appended[$entry.Key].Add($value)
200
+ } else {
201
+ $values[$entry.Key] = $value
202
+ }
203
+ }
204
+
205
+ $missing = @($Specification.Keys | Where-Object { $Specification[$_].Required -and -not $seen.Contains($_) })
206
+ if ($missing.Count -gt 0) {
207
+ return [pscustomobject]@{ Values = $values; Error = "error: the following arguments are required: $($missing -join ', ')" }
208
+ }
209
+ if ($unrecognized.Count -gt 0) {
210
+ return [pscustomobject]@{ Values = $values; Error = "error: unrecognized arguments: $($unrecognized -join ' ')" }
211
+ }
212
+
213
+ # Append options surface as string arrays, empty when never supplied.
214
+ foreach ($key in $appended.Keys) {
215
+ $values[$key] = [string[]]$appended[$key].ToArray()
216
+ }
217
+ return [pscustomobject]@{ Values = $values; Error = $null }
218
+ }
219
+
220
+ function ConvertTo-CodexRoutingJsonString {
221
+ <#
222
+ .SYNOPSIS
223
+ Encode one string as a JSON literal the way Python json.dumps does.
224
+ .DESCRIPTION
225
+ Escapes quote, backslash, and the short control escapes; every other
226
+ character below 0x20 or above 0x7E becomes a lowercase \uXXXX escape per
227
+ UTF-16 code unit (ensure_ascii=True), so astral characters produce two.
228
+ .PARAMETER Value
229
+ The string to encode.
230
+ .OUTPUTS
231
+ System.String - the quoted JSON literal.
232
+ #>
233
+ [CmdletBinding()]
234
+ [OutputType([string])]
235
+ param([Parameter(Mandatory = $true)][AllowEmptyString()][string] $Value)
236
+
237
+ $builder = [System.Text.StringBuilder]::new()
238
+ [void]$builder.Append('"')
239
+ foreach ($character in $Value.ToCharArray()) {
240
+ $code = [int]$character
241
+ switch ($code) {
242
+ 0x22 { [void]$builder.Append('\"'); break }
243
+ 0x5C { [void]$builder.Append('\\'); break }
244
+ 0x08 { [void]$builder.Append('\b'); break }
245
+ 0x0C { [void]$builder.Append('\f'); break }
246
+ 0x0A { [void]$builder.Append('\n'); break }
247
+ 0x0D { [void]$builder.Append('\r'); break }
248
+ 0x09 { [void]$builder.Append('\t'); break }
249
+ default {
250
+ if ($code -lt 0x20 -or $code -gt 0x7E) {
251
+ [void]$builder.Append('\u').Append($code.ToString('x4', [System.Globalization.CultureInfo]::InvariantCulture))
252
+ } else {
253
+ [void]$builder.Append($character)
254
+ }
255
+ }
256
+ }
257
+ }
258
+ [void]$builder.Append('"')
259
+ return $builder.ToString()
260
+ }
261
+
262
+ function ConvertTo-CodexRoutingJsonText {
263
+ <#
264
+ .SYNOPSIS
265
+ Recursively render one value at an indentation level.
266
+ .PARAMETER Value
267
+ Null, boolean, string, integer, dictionary, or enumerable.
268
+ .PARAMETER Level
269
+ Current nesting depth; each level indents by two spaces.
270
+ .OUTPUTS
271
+ System.String
272
+ #>
273
+ [CmdletBinding()]
274
+ [OutputType([string])]
275
+ param(
276
+ [Parameter(Mandatory = $true)][AllowNull()] $Value,
277
+ [Parameter(Mandatory = $true)][int] $Level
278
+ )
279
+
280
+ $inner = ' ' * (2 * ($Level + 1))
281
+ $outer = ' ' * (2 * $Level)
282
+ # Routing table: scalars first, then strings before enumerables (a string
283
+ # is enumerable), then objects and arrays; anything else is rejected.
284
+ if ($null -eq $Value) {
285
+ return 'null'
286
+ }
287
+ if ($Value -is [bool]) {
288
+ return $(if ($Value) { 'true' } else { 'false' })
289
+ }
290
+ if ($Value -is [string]) {
291
+ return ConvertTo-CodexRoutingJsonString -Value $Value
292
+ }
293
+ if ($Value -is [int] -or $Value -is [long] -or $Value -is [int16] -or $Value -is [byte]) {
294
+ return $Value.ToString([System.Globalization.CultureInfo]::InvariantCulture)
295
+ }
296
+ if ($Value -is [System.Collections.IDictionary]) {
297
+ $keys = [string[]]@($Value.Keys)
298
+ if ($keys.Count -eq 0) {
299
+ return '{}'
300
+ }
301
+ [Array]::Sort($keys, [System.StringComparer]::Ordinal)
302
+ $members = foreach ($key in $keys) {
303
+ $inner + (ConvertTo-CodexRoutingJsonString -Value $key) + ': ' + (ConvertTo-CodexRoutingJsonText -Value $Value[$key] -Level ($Level + 1))
304
+ }
305
+ return "{`n" + ($members -join ",`n") + "`n$outer}"
306
+ }
307
+ if ($Value -is [System.Collections.IEnumerable]) {
308
+ $items = @(foreach ($item in $Value) {
309
+ $inner + (ConvertTo-CodexRoutingJsonText -Value $item -Level ($Level + 1))
310
+ })
311
+ if ($items.Count -eq 0) {
312
+ return '[]'
313
+ }
314
+ return "[`n" + ($items -join ",`n") + "`n$outer]"
315
+ }
316
+ throw [System.ArgumentException]::new("unsupported JSON value type: $($Value.GetType().FullName)")
317
+ }
318
+
319
+ function ConvertTo-CodexRoutingJson {
320
+ <#
321
+ .SYNOPSIS
322
+ Serialize a receipt like Python json.dumps(indent=2, sort_keys=True).
323
+ .DESCRIPTION
324
+ Keys are sorted with the ordinal comparer, members are separated by a
325
+ comma and newline, and key and value are separated by ': '. The result
326
+ carries no trailing newline; the wrapper appends one as print() does.
327
+ .PARAMETER InputObject
328
+ The value to serialize.
329
+ .OUTPUTS
330
+ System.String
331
+ #>
332
+ [CmdletBinding()]
333
+ [OutputType([string])]
334
+ param([Parameter(Mandatory = $true)][AllowNull()] $InputObject)
335
+
336
+ return ConvertTo-CodexRoutingJsonText -Value $InputObject -Level 0
337
+ }
338
+
339
+ function New-CodexRoutingCliResult {
340
+ <#
341
+ .SYNOPSIS
342
+ Build the result object a wrapper returns: exit code and both streams.
343
+ .DESCRIPTION
344
+ Creates an in-memory object only; it changes no system state. It
345
+ declares ShouldProcess support to satisfy the repository analyzer
346
+ settings for the New verb and returns the object under -WhatIf too.
347
+ .PARAMETER ExitCode
348
+ Process exit code: 0 success, 1 resolver error, 2 usage error.
349
+ .PARAMETER Stdout
350
+ Standard output text.
351
+ .PARAMETER Stderr
352
+ Standard error text.
353
+ .OUTPUTS
354
+ PSCustomObject with ExitCode, Stdout, and Stderr.
355
+ #>
356
+ [CmdletBinding(SupportsShouldProcess = $true)]
357
+ [OutputType([pscustomobject])]
358
+ param(
359
+ [Parameter(Mandatory = $true)][int] $ExitCode,
360
+ [AllowEmptyString()][string] $Stdout = '',
361
+ [AllowEmptyString()][string] $Stderr = ''
362
+ )
363
+
364
+ [void]$PSCmdlet.ShouldProcess('codex routing CLI result', 'Create')
365
+ return [pscustomobject]@{
366
+ ExitCode = $ExitCode
367
+ Stdout = $Stdout
368
+ Stderr = $Stderr
369
+ }
370
+ }