@danmoisan/drm-copilot-mcp 1.0.21 → 1.0.22

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 (31) hide show
  1. package/out/mcp-server.js +1624 -190
  2. package/package.json +1 -1
  3. package/resources/claude-customizations/.claude/agent-memory/epic-orchestrator/MEMORY.md +5 -1
  4. package/resources/claude-customizations/.claude/agent-memory/epic-orchestrator/feedback_commit_push_memory_before_pr.md +48 -2
  5. package/resources/claude-customizations/.claude/agent-memory/epic-orchestrator/feedback_no_sendmessage_tool.md +35 -0
  6. package/resources/claude-customizations/.claude/agent-memory/epic-orchestrator/feedback_worktree_isolation_branches_from_main.md +45 -0
  7. package/resources/claude-customizations/.claude/agents/parallel-orchestrator.md +238 -0
  8. package/resources/claude-customizations/.claude/agents/parallel-planner.md +149 -0
  9. package/resources/claude-customizations/.claude/hooks/enforce-epic-invocation-origin.ps1 +23 -11
  10. package/resources/claude-customizations/.claude/hooks/enforce-parallel-abandon-gate.ps1 +259 -0
  11. package/resources/claude-customizations/.claude/hooks/enforce-parallel-cohort-barrier.ps1 +499 -0
  12. package/resources/claude-customizations/.claude/hooks/enforce-parallel-drift-gate-helpers.ps1 +302 -0
  13. package/resources/claude-customizations/.claude/hooks/enforce-parallel-drift-gate.ps1 +359 -0
  14. package/resources/claude-customizations/.claude/hooks/enforce-parallel-worktree-removal-gate.ps1 +244 -0
  15. package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadius.psm1 +379 -0
  16. package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusConfig.psm1 +491 -0
  17. package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusExtraction.psm1 +490 -0
  18. package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusGlob.psm1 +429 -0
  19. package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusValidation.psm1 +366 -0
  20. package/resources/claude-customizations/.claude/rules/parallel-orchestration.md +184 -0
  21. package/resources/claude-customizations/.claude/settings.json +25 -0
  22. package/resources/claude-customizations/.claude/skills/parallel-add/SKILL.md +148 -0
  23. package/resources/claude-customizations/.claude/skills/parallel-close/SKILL.md +93 -0
  24. package/resources/claude-customizations/.claude/skills/parallel-orchestrate/SKILL.md +960 -0
  25. package/resources/claude-customizations/.claude/skills/parallel-plan/SKILL.md +420 -0
  26. package/resources/claude-customizations/.claude/skills/parallel-remove/SKILL.md +176 -0
  27. package/resources/claude-customizations/.claude/skills/parallel-run/SKILL.md +56 -0
  28. package/resources/claude-customizations/pack-manifests/core.json +19 -1
  29. package/resources/codex-and-agents-customizations/.codex/config.toml +1 -1
  30. package/resources/config/orchestration-routing.json +22 -0
  31. package/resources/powershell/PoshQC/settings/pester.runsettings.psd1 +29 -0
@@ -0,0 +1,429 @@
1
+ <#
2
+ .SYNOPSIS
3
+ Blast-radius glob, subsumption, and overlap primitives.
4
+
5
+ .DESCRIPTION
6
+ Destination-runtime PowerShell port of the path-pattern primitives the Python
7
+ reference splits across scripts/dev_tools/_blast_radius_extraction.py
8
+ (_glob_to_regex_text, matches_glob, is_path_subsumed),
9
+ scripts/dev_tools/_blast_radius_validation.py (is_glob_entry,
10
+ concrete_entries) and scripts/dev_tools/_blast_radius_conflicts.py
11
+ (_literal_prefix, _entries_overlap). They are gathered here because they form
12
+ one cohesive concern, pattern comparison over repository paths, and because
13
+ every PowerShell file must stay within the 500-line limit.
14
+
15
+ The Python modules remain the authoritative reference implementation. This
16
+ module is one half of a two-language mirror; it never imports validator
17
+ logic. Every function is pure: no filesystem, subprocess, network, or
18
+ wall-clock access, and no input is mutated.
19
+
20
+ Parity notes for maintainers:
21
+ - The glob vocabulary is a deliberate fnmatch subset (**, *, ?). Character
22
+ classes are unsupported because PowerShell's -like operator does not
23
+ agree with fnmatch on their semantics, which is also why this module
24
+ translates patterns to regex explicitly rather than using -like.
25
+ - [regex]::Escape and Python's re.escape escape different punctuation sets,
26
+ but every character either escapes to a literal or is already literal
27
+ outside a character class, so the translated patterns match the same
28
+ strings.
29
+ - Full-match anchoring uses \A and \z, which reproduce Python's
30
+ re.fullmatch exactly; ^ and $ would additionally admit a trailing
31
+ newline in .NET.
32
+ - Comparisons and ordering use [StringComparer]::Ordinal and
33
+ [string]::CompareOrdinal so results do not vary with the current culture.
34
+ #>
35
+
36
+ Set-StrictMode -Version Latest
37
+
38
+ # Wildcards that make a path entry a pattern rather than a file. The question
39
+ # mark is included because the subsumption helper treats it as a pattern;
40
+ # admitting it here keeps every comparison in the fail-closed direction.
41
+ $script:GlobWildcard = @('*', '?')
42
+
43
+
44
+ function Test-GlobEntry {
45
+ <#
46
+ .SYNOPSIS
47
+ Report whether a path entry is a wildcard pattern rather than a file.
48
+
49
+ .DESCRIPTION
50
+ Port of is_glob_entry. Used both to classify radius path entries and, one
51
+ character at a time, by the literal-prefix helper.
52
+
53
+ .PARAMETER Entry
54
+ A paths entry from a radius or an extraction, or a single character.
55
+
56
+ .OUTPUTS
57
+ System.Boolean. True when the entry carries any wildcard character.
58
+ #>
59
+ [CmdletBinding()]
60
+ [OutputType([bool])]
61
+ param(
62
+ [Parameter(Mandatory = $true)]
63
+ [AllowEmptyString()]
64
+ [string] $Entry
65
+ )
66
+
67
+ foreach ($wildcard in $script:GlobWildcard) {
68
+ if ($Entry.IndexOf($wildcard, [System.StringComparison]::Ordinal) -ge 0) {
69
+ return $true
70
+ }
71
+ }
72
+
73
+ return $false
74
+ }
75
+
76
+ function Get-ConcreteEntry {
77
+ <#
78
+ .SYNOPSIS
79
+ Select the wildcard-free entries of a path collection.
80
+
81
+ .DESCRIPTION
82
+ Port of concrete_entries. Only concrete entries can be compared for
83
+ equality, so the rules that count files or enumerate surfaces use this
84
+ subset. Input order is preserved, which is already ordinal for any radius
85
+ or extraction result.
86
+
87
+ .PARAMETER Entry
88
+ Entries mixing concrete paths and globs. An empty collection is accepted.
89
+
90
+ .OUTPUTS
91
+ System.Object[]. The concrete entries in input order.
92
+ #>
93
+ [CmdletBinding()]
94
+ [OutputType([System.Object[]])]
95
+ param(
96
+ [Parameter(Mandatory = $true)]
97
+ [AllowEmptyCollection()]
98
+ [AllowEmptyString()]
99
+ [string[]] $Entry
100
+ )
101
+
102
+ $concrete = [System.Collections.Generic.List[string]]::new()
103
+ foreach ($single in $Entry) {
104
+ if (-not (Test-GlobEntry -Entry $single)) {
105
+ $concrete.Add($single)
106
+ }
107
+ }
108
+
109
+ return @($concrete.ToArray())
110
+ }
111
+
112
+ # Port of _glob_to_regex_text. Scans one character at a time so the two-character
113
+ # ** token is recognized before the single-character * rule applies; the order
114
+ # matters because only ** may cross directory separators. Every other character,
115
+ # including [ and ], is escaped to a literal.
116
+ function ConvertTo-GlobRegexText {
117
+ [CmdletBinding()]
118
+ [OutputType([string])]
119
+ param(
120
+ [Parameter(Mandatory = $true)]
121
+ [AllowEmptyString()]
122
+ [string] $Pattern
123
+ )
124
+
125
+ $part = [System.Text.StringBuilder]::new()
126
+ $index = 0
127
+ while ($index -lt $Pattern.Length) {
128
+ if ($index + 1 -lt $Pattern.Length -and $Pattern[$index] -ceq '*' -and $Pattern[$index + 1] -ceq '*') {
129
+ [void]$part.Append('.*')
130
+ $index += 2
131
+ continue
132
+ }
133
+
134
+ $character = $Pattern[$index]
135
+ if ($character -ceq '*') {
136
+ [void]$part.Append('[^/]*')
137
+ } elseif ($character -ceq '?') {
138
+ [void]$part.Append('[^/]')
139
+ } else {
140
+ [void]$part.Append([regex]::Escape([string]$character))
141
+ }
142
+ $index += 1
143
+ }
144
+
145
+ return $part.ToString()
146
+ }
147
+
148
+ function Test-GlobMatch {
149
+ <#
150
+ .SYNOPSIS
151
+ Report whether a candidate path matches a glob pattern.
152
+
153
+ .DESCRIPTION
154
+ Port of matches_glob. Translates the supported glob subset to regex and
155
+ applies it as a whole-string match, reproducing Python's re.fullmatch.
156
+
157
+ .PARAMETER Pattern
158
+ Glob using the supported **, *, ? vocabulary.
159
+
160
+ .PARAMETER Candidate
161
+ Concrete repository-relative path to test.
162
+
163
+ .OUTPUTS
164
+ System.Boolean. True when the whole candidate matches the whole pattern.
165
+ #>
166
+ [CmdletBinding()]
167
+ [OutputType([bool])]
168
+ param(
169
+ [Parameter(Mandatory = $true)]
170
+ [AllowEmptyString()]
171
+ [string] $Pattern,
172
+ [Parameter(Mandatory = $true)]
173
+ [AllowEmptyString()]
174
+ [string] $Candidate
175
+ )
176
+
177
+ $regexText = '\A(?:' + (ConvertTo-GlobRegexText -Pattern $Pattern) + ')\z'
178
+ return [regex]::IsMatch($Candidate, $regexText)
179
+ }
180
+
181
+ function Test-PathSubsumed {
182
+ <#
183
+ .SYNOPSIS
184
+ Report whether a concrete path is covered by a collection of entries.
185
+
186
+ .DESCRIPTION
187
+ Port of is_path_subsumed. Implements the coverage relation validation rule
188
+ V1 applies: exact match, listed-directory prefix, or glob match. The three
189
+ rules are independent, so traversal order affects speed only, never the
190
+ verdict. An empty collection covers nothing.
191
+
192
+ .PARAMETER Path
193
+ Concrete repository-relative path to test.
194
+
195
+ .PARAMETER CoveringPath
196
+ Declared path entries, which may mix concrete paths, directory names, and
197
+ glob patterns. An empty collection is accepted.
198
+
199
+ .OUTPUTS
200
+ System.Boolean. True when at least one entry covers the path.
201
+ #>
202
+ [CmdletBinding()]
203
+ [OutputType([bool])]
204
+ param(
205
+ [Parameter(Mandatory = $true)]
206
+ [AllowEmptyString()]
207
+ [string] $Path,
208
+ [Parameter(Mandatory = $true)]
209
+ [AllowEmptyCollection()]
210
+ [AllowEmptyString()]
211
+ [string[]] $CoveringPath
212
+ )
213
+
214
+ foreach ($entry in $CoveringPath) {
215
+ if ([string]::Equals($entry, $Path, [System.StringComparison]::Ordinal)) {
216
+ return $true
217
+ }
218
+
219
+ # A wildcard entry is a pattern matched with the shared glob subset. A
220
+ # wildcard-free entry cannot be a pattern, so it is treated as a listed
221
+ # directory covering everything beneath it.
222
+ if (Test-GlobEntry -Entry $entry) {
223
+ if (Test-GlobMatch -Pattern $entry -Candidate $Path) {
224
+ return $true
225
+ }
226
+ } elseif ($Path.StartsWith($entry.TrimEnd('/') + '/', [System.StringComparison]::Ordinal)) {
227
+ return $true
228
+ }
229
+ }
230
+
231
+ return $false
232
+ }
233
+
234
+ function Get-LiteralPrefix {
235
+ <#
236
+ .SYNOPSIS
237
+ Return the leading portion of a path entry before its first wildcard.
238
+
239
+ .DESCRIPTION
240
+ Port of _literal_prefix. Scanning for the earliest wildcard of any kind
241
+ keeps the prefix a true literal, which is what makes the glob-versus-glob
242
+ disjointness test sound. The trailing return is the defensive
243
+ wildcard-free fallback the Python reference carries; the overlap relation
244
+ only reaches this helper with glob entries, so that branch is exercised
245
+ by direct invocation.
246
+
247
+ .PARAMETER Entry
248
+ A path entry that may contain wildcards.
249
+
250
+ .OUTPUTS
251
+ System.String. The literal prefix; the whole entry when it has no
252
+ wildcard.
253
+ #>
254
+ [CmdletBinding()]
255
+ [OutputType([string])]
256
+ param(
257
+ [Parameter(Mandatory = $true)]
258
+ [AllowEmptyString()]
259
+ [string] $Entry
260
+ )
261
+
262
+ for ($index = 0; $index -lt $Entry.Length; $index++) {
263
+ if (Test-GlobEntry -Entry ([string]$Entry[$index])) {
264
+ return $Entry.Substring(0, $index)
265
+ }
266
+ }
267
+
268
+ return $Entry
269
+ }
270
+
271
+ function Test-EntryOverlap {
272
+ <#
273
+ .SYNOPSIS
274
+ Report whether two path entries can name a common file.
275
+
276
+ .DESCRIPTION
277
+ Port of _entries_overlap. The cases are decided by how many sides are
278
+ patterns: two concrete entries overlap when equal or when either names a
279
+ directory containing the other, a mixed pair overlaps on a pattern match
280
+ or on a two-way nest between the glob's literal prefix and the concrete
281
+ entry's directory, and a pattern pair falls back to a conservative
282
+ literal-prefix proof. The two directory rules were added by issue #452 to
283
+ align this relation with Test-PathSubsumed, which already honoured them.
284
+ Glob-versus-glob containment is undecidable in
285
+ general, so that pair overlaps unless the prefixes diverge, which no
286
+ single path could satisfy. Any pair the test cannot separate is reported
287
+ as overlapping, the fail-closed direction.
288
+
289
+ .PARAMETER EntryA
290
+ First path entry, concrete or glob.
291
+
292
+ .PARAMETER EntryB
293
+ Second path entry, concrete or glob.
294
+
295
+ .OUTPUTS
296
+ System.Boolean. True when the entries overlap; the relation is symmetric.
297
+ #>
298
+ [CmdletBinding()]
299
+ [OutputType([bool])]
300
+ param(
301
+ [Parameter(Mandatory = $true)]
302
+ [AllowEmptyString()]
303
+ [string] $EntryA,
304
+ [Parameter(Mandatory = $true)]
305
+ [AllowEmptyString()]
306
+ [string] $EntryB
307
+ )
308
+
309
+ $aIsGlob = Test-GlobEntry -Entry $EntryA
310
+ $bIsGlob = Test-GlobEntry -Entry $EntryB
311
+
312
+ if (-not $aIsGlob -and -not $bIsGlob) {
313
+ # Anchoring each entry with a trailing separator before the prefix test is
314
+ # what keeps the containment sound: without the anchor, scripts/dev_tools
315
+ # would appear to contain scripts/dev_toolsX/a.py. Trimming first makes a
316
+ # trailing separator on either entry immaterial.
317
+ $directoryA = $EntryA.TrimEnd('/') + '/'
318
+ $directoryB = $EntryB.TrimEnd('/') + '/'
319
+ return ([string]::Equals($EntryA, $EntryB, [System.StringComparison]::Ordinal) -or
320
+ $EntryA.StartsWith($directoryB, [System.StringComparison]::Ordinal) -or
321
+ $EntryB.StartsWith($directoryA, [System.StringComparison]::Ordinal))
322
+ }
323
+ # A mixed pair also overlaps when the glob's literal prefix and the concrete
324
+ # entry's directory prefix nest. The nest is tested in both directions because
325
+ # the glob may be rooted above the directory (scripts/ above
326
+ # scripts/dev_tools/) or below it, and either arrangement admits a common file.
327
+ if ($aIsGlob -and -not $bIsGlob) {
328
+ $prefixGlob = Get-LiteralPrefix -Entry $EntryA
329
+ $directoryConcrete = $EntryB.TrimEnd('/') + '/'
330
+ return ((Test-GlobMatch -Pattern $EntryA -Candidate $EntryB) -or
331
+ $prefixGlob.StartsWith($directoryConcrete, [System.StringComparison]::Ordinal) -or
332
+ $directoryConcrete.StartsWith($prefixGlob, [System.StringComparison]::Ordinal))
333
+ }
334
+ if ($bIsGlob -and -not $aIsGlob) {
335
+ $prefixGlob = Get-LiteralPrefix -Entry $EntryB
336
+ $directoryConcrete = $EntryA.TrimEnd('/') + '/'
337
+ return ((Test-GlobMatch -Pattern $EntryB -Candidate $EntryA) -or
338
+ $prefixGlob.StartsWith($directoryConcrete, [System.StringComparison]::Ordinal) -or
339
+ $directoryConcrete.StartsWith($prefixGlob, [System.StringComparison]::Ordinal))
340
+ }
341
+
342
+ $prefixA = Get-LiteralPrefix -Entry $EntryA
343
+ $prefixB = Get-LiteralPrefix -Entry $EntryB
344
+ return ($prefixA.StartsWith($prefixB, [System.StringComparison]::Ordinal) -or
345
+ $prefixB.StartsWith($prefixA, [System.StringComparison]::Ordinal))
346
+ }
347
+
348
+ function Get-OrdinalSortedEntry {
349
+ <#
350
+ .SYNOPSIS
351
+ Deduplicate and ordinally sort a string collection.
352
+
353
+ .DESCRIPTION
354
+ Port of the tuple(sorted(set(...))) idiom the Python reference applies to
355
+ every collection it returns. Ordinal comparison is mandatory: PowerShell's
356
+ default Sort-Object is culture sensitive and would order entries
357
+ differently from Python's code-point ordering on some hosts.
358
+
359
+ .PARAMETER Entry
360
+ The entries to normalize. An empty collection is accepted and yields an
361
+ empty array.
362
+
363
+ .OUTPUTS
364
+ System.Object[]. The distinct entries in ordinal order.
365
+ #>
366
+ [CmdletBinding()]
367
+ [OutputType([System.Object[]])]
368
+ param(
369
+ [Parameter(Mandatory = $true)]
370
+ [AllowEmptyCollection()]
371
+ [AllowEmptyString()]
372
+ [string[]] $Entry
373
+ )
374
+
375
+ $unique = [System.Collections.Generic.HashSet[string]]::new([StringComparer]::Ordinal)
376
+ foreach ($item in $Entry) {
377
+ [void]$unique.Add($item)
378
+ }
379
+
380
+ $sorted = [System.Collections.Generic.List[string]]::new($unique)
381
+ $sorted.Sort([StringComparer]::Ordinal)
382
+ return @($sorted.ToArray())
383
+ }
384
+
385
+ function Get-OrdinalSmallestEntry {
386
+ <#
387
+ .SYNOPSIS
388
+ Return the ordinally smallest entry of a collection.
389
+
390
+ .DESCRIPTION
391
+ Port of the Python min() calls in the contention relation. Ordinal
392
+ comparison is mandatory so the reported detail does not vary with the
393
+ current culture.
394
+
395
+ .PARAMETER Entry
396
+ Candidate entries. An empty collection is accepted and yields $null,
397
+ mirroring the Python guards that return None for an empty candidate set.
398
+
399
+ .OUTPUTS
400
+ System.String. The smallest entry, or $null when the collection is empty.
401
+ #>
402
+ [CmdletBinding()]
403
+ [OutputType([string])]
404
+ param(
405
+ [Parameter(Mandatory = $true)]
406
+ [AllowEmptyCollection()]
407
+ [AllowEmptyString()]
408
+ [string[]] $Entry
409
+ )
410
+
411
+ $smallest = $null
412
+ foreach ($candidate in $Entry) {
413
+ if ($null -eq $smallest -or [string]::CompareOrdinal($candidate, $smallest) -lt 0) {
414
+ $smallest = $candidate
415
+ }
416
+ }
417
+
418
+ return $smallest
419
+ }
420
+
421
+ Export-ModuleMember -Function `
422
+ Test-GlobEntry, `
423
+ Get-ConcreteEntry, `
424
+ Test-GlobMatch, `
425
+ Test-PathSubsumed, `
426
+ Get-LiteralPrefix, `
427
+ Test-EntryOverlap, `
428
+ Get-OrdinalSmallestEntry, `
429
+ Get-OrdinalSortedEntry