@danmoisan/drm-copilot-mcp 1.0.26 → 1.0.27
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 +542 -41
- package/package.json +1 -1
- package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate.ps1 +109 -5
- package/resources/claude-customizations/.claude/hooks/enforce-mermaid-validation.ps1 +390 -0
- package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadius.psm1 +105 -2
- package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusConfig.psm1 +32 -52
- package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusExtraction.psm1 +107 -99
- package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusNormalization.psm1 +295 -0
- package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusValidation.psm1 +9 -3
- package/resources/claude-customizations/.claude/lib/mermaid/MermaidGrammar.psm1 +491 -0
- package/resources/claude-customizations/.claude/lib/mermaid/MermaidLineScanner.psm1 +488 -0
- package/resources/claude-customizations/.claude/lib/mermaid/MermaidMarkdownFences.psm1 +298 -0
- package/resources/claude-customizations/.claude/lib/mermaid/MermaidValidation.psm1 +496 -0
- package/resources/claude-customizations/.claude/rules/mermaid.md +142 -0
- package/resources/claude-customizations/.claude/rules/parallel-orchestration.md +61 -1
- package/resources/claude-customizations/.claude/rules/plan-acceptance-gates.md +116 -0
- package/resources/claude-customizations/.claude/settings.json +5 -0
- package/resources/claude-customizations/.claude/skills/atomic-plan-contract/SKILL.md +15 -0
- package/resources/claude-customizations/.claude/skills/evidence-and-timestamp-conventions/SKILL.md +13 -0
- package/resources/claude-customizations/.claude/skills/feature-promotion-lifecycle/SKILL.md +6 -0
- package/resources/claude-customizations/.claude/skills/mermaid-diagram/SKILL.md +184 -0
- package/resources/claude-customizations/.claude/skills/mermaid-diagram/references/c4.md +50 -0
- package/resources/claude-customizations/.claude/skills/mermaid-diagram/references/class.md +63 -0
- package/resources/claude-customizations/.claude/skills/mermaid-diagram/references/er.md +56 -0
- package/resources/claude-customizations/.claude/skills/mermaid-diagram/references/flowchart.md +68 -0
- package/resources/claude-customizations/.claude/skills/mermaid-diagram/references/gantt.md +51 -0
- package/resources/claude-customizations/.claude/skills/mermaid-diagram/references/other-types.md +82 -0
- package/resources/claude-customizations/.claude/skills/mermaid-diagram/references/pie.md +32 -0
- package/resources/claude-customizations/.claude/skills/mermaid-diagram/references/sequence.md +63 -0
- package/resources/claude-customizations/.claude/skills/mermaid-diagram/references/state.md +49 -0
- package/resources/claude-customizations/.claude/skills/parallel-orchestrate/SKILL.md +8 -7
- package/resources/claude-customizations/.claude/skills/parallel-plan/SKILL.md +24 -4
- package/resources/claude-customizations/config/blast-radius.json +8 -0
- package/resources/claude-customizations/pack-manifests/core.json +19 -1
- package/resources/codex-and-agents-customizations/.agents/skills/evidence-and-timestamp-conventions/SKILL.md +13 -0
- package/resources/codex-and-agents-customizations/.codex/config.toml +1 -1
- package/resources/customizations/.github/skills/evidence-and-timestamp-conventions/SKILL.md +13 -0
- package/resources/powershell/PoshQC/settings/pester.runsettings.psd1 +16 -1
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
<#
|
|
2
|
+
.SYNOPSIS
|
|
3
|
+
Markdown fenced-block tracker and opt-out marker detector for the Mermaid gate.
|
|
4
|
+
|
|
5
|
+
.DESCRIPTION
|
|
6
|
+
Extracts ```` ```mermaid ```` blocks from Markdown text without parsing
|
|
7
|
+
CommonMark. The rule set is deliberately small and each rule exists for a
|
|
8
|
+
stated reason:
|
|
9
|
+
|
|
10
|
+
- An opening fence is a run of three or more backticks or three or more
|
|
11
|
+
tildes, indented up to three spaces, optionally prefixed by blockquote
|
|
12
|
+
markers, whose info string's first word is `mermaid` (case-insensitive).
|
|
13
|
+
- A closing fence uses the same fence character, is at least as long as the
|
|
14
|
+
opening run, carries no info string, and sits at the same blockquote
|
|
15
|
+
depth.
|
|
16
|
+
- A fence stack is maintained so a `mermaid` fence nested inside an outer
|
|
17
|
+
open fence is reported as nested. Documentation showing example Mermaid is
|
|
18
|
+
not a diagram, so the validator must skip it (fail-open item 6).
|
|
19
|
+
- An unclosed fence is tolerated: its collected body is still reported, so a
|
|
20
|
+
diagram at the end of a truncated document is not silently dropped.
|
|
21
|
+
- Body lines of a fence opened inside a blockquote are stripped of their
|
|
22
|
+
`> ` prefix, so a blockquoted diagram validates as its unquoted twin.
|
|
23
|
+
- Tilde fences may contain backtick runs and vice versa, which the
|
|
24
|
+
same-character close rule handles without special cases.
|
|
25
|
+
|
|
26
|
+
The opt-out marker is `<!-- mermaid-validator: ignore -->` on the line
|
|
27
|
+
immediately preceding the opening fence, with no intervening line. Its scope
|
|
28
|
+
is exactly that one block; a later block needs its own marker. The marker
|
|
29
|
+
exists so that documentation deliberately quoting invalid Mermaid is never
|
|
30
|
+
blocked, which would be worse than having no gate at all.
|
|
31
|
+
|
|
32
|
+
Pinned to Mermaid 11.17.0 through MermaidGrammar.psm1. Every function is pure:
|
|
33
|
+
no filesystem, subprocess, network, or wall-clock access, and no input is
|
|
34
|
+
mutated.
|
|
35
|
+
#>
|
|
36
|
+
|
|
37
|
+
Set-StrictMode -Version Latest
|
|
38
|
+
|
|
39
|
+
# The marker text must be exactly `mermaid-validator: ignore` and is
|
|
40
|
+
# case-sensitive. Whitespace is permitted around the line, around the comment
|
|
41
|
+
# delimiters, and a blockquote prefix is tolerated so the marker works inside a
|
|
42
|
+
# quoted passage.
|
|
43
|
+
$script:OptOutMarkerPattern = '^(?:\s{0,3}>\s?)*\s*<!--\s*mermaid-validator: ignore\s*-->\s*$'
|
|
44
|
+
|
|
45
|
+
# Fence line shape: optional blockquote prefixes, up to three spaces of
|
|
46
|
+
# indentation, then the fence run, then the info string.
|
|
47
|
+
$script:FenceLinePattern = '^(?<quote>(?:\s{0,3}>\s?)*)(?<indent>\s{0,3})(?<fence>`{3,}|~{3,})(?<info>.*)$'
|
|
48
|
+
|
|
49
|
+
function Split-MermaidTextLine {
|
|
50
|
+
<#
|
|
51
|
+
.SYNOPSIS
|
|
52
|
+
Splits text into lines, normalizing CRLF, CR, and LF endings.
|
|
53
|
+
.DESCRIPTION
|
|
54
|
+
Line-ending normalization happens once, here, so every downstream rule
|
|
55
|
+
sees the same line array and a CRLF document produces a byte-identical
|
|
56
|
+
verdict to its LF twin.
|
|
57
|
+
#>
|
|
58
|
+
[CmdletBinding()]
|
|
59
|
+
[OutputType([string[]])]
|
|
60
|
+
param(
|
|
61
|
+
[AllowEmptyString()]
|
|
62
|
+
[AllowNull()]
|
|
63
|
+
[string] $Text
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
if ([string]::IsNullOrEmpty($Text)) {
|
|
67
|
+
return [string[]]@()
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return [string[]]($Text -split '\r\n|\n|\r')
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function Test-MermaidOptOutMarker {
|
|
74
|
+
<#
|
|
75
|
+
.SYNOPSIS
|
|
76
|
+
Returns $true when a line is the documented Mermaid validator opt-out marker.
|
|
77
|
+
#>
|
|
78
|
+
[CmdletBinding()]
|
|
79
|
+
[OutputType([bool])]
|
|
80
|
+
param(
|
|
81
|
+
[AllowEmptyString()]
|
|
82
|
+
[AllowNull()]
|
|
83
|
+
[string] $Line
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
if ([string]::IsNullOrWhiteSpace($Line)) {
|
|
87
|
+
return $false
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return [bool]($Line -cmatch $script:OptOutMarkerPattern)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function Get-MermaidFenceLine {
|
|
94
|
+
<#
|
|
95
|
+
.SYNOPSIS
|
|
96
|
+
Parses a candidate Markdown fence line.
|
|
97
|
+
.DESCRIPTION
|
|
98
|
+
Returns $null when the line is not a fence line. Otherwise returns an
|
|
99
|
+
ordered dictionary with FenceCharacter, FenceLength, QuoteDepth, Info, and
|
|
100
|
+
IsMermaid (the info string's first word is `mermaid`, case-insensitive).
|
|
101
|
+
#>
|
|
102
|
+
[CmdletBinding()]
|
|
103
|
+
[OutputType([System.Collections.Specialized.OrderedDictionary])]
|
|
104
|
+
param(
|
|
105
|
+
[AllowEmptyString()]
|
|
106
|
+
[AllowNull()]
|
|
107
|
+
[string] $Line
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
if ([string]::IsNullOrEmpty($Line)) {
|
|
111
|
+
return $null
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
$match = [regex]::Match($Line, $script:FenceLinePattern)
|
|
115
|
+
if (-not $match.Success) {
|
|
116
|
+
return $null
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
$fence = $match.Groups['fence'].Value
|
|
120
|
+
$info = $match.Groups['info'].Value.Trim()
|
|
121
|
+
$quoteDepth = ([regex]::Matches($match.Groups['quote'].Value, '>')).Count
|
|
122
|
+
|
|
123
|
+
$firstWord = ''
|
|
124
|
+
if (-not [string]::IsNullOrWhiteSpace($info)) {
|
|
125
|
+
$firstWord = ($info -split '[\s{,]+', 2)[0]
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return [ordered]@{
|
|
129
|
+
FenceCharacter = [string]$fence[0]
|
|
130
|
+
FenceLength = $fence.Length
|
|
131
|
+
QuoteDepth = $quoteDepth
|
|
132
|
+
Info = $info
|
|
133
|
+
IsMermaid = [bool]($firstWord -imatch '^mermaid$')
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function Get-MermaidUnquotedLine {
|
|
138
|
+
<#
|
|
139
|
+
.SYNOPSIS
|
|
140
|
+
Strips up to QuoteDepth blockquote markers from the start of a body line.
|
|
141
|
+
.DESCRIPTION
|
|
142
|
+
A fence opened inside a blockquote carries a `> ` prefix on every body
|
|
143
|
+
line. Leaving the prefix in place would make the first body line start
|
|
144
|
+
with `>`, which the validator would read as a non-keyword first line and
|
|
145
|
+
reject. Stripping it is what makes a blockquoted diagram validate the same
|
|
146
|
+
as its unquoted twin.
|
|
147
|
+
#>
|
|
148
|
+
[CmdletBinding()]
|
|
149
|
+
[OutputType([string])]
|
|
150
|
+
param(
|
|
151
|
+
[AllowEmptyString()]
|
|
152
|
+
[AllowNull()]
|
|
153
|
+
[string] $Line,
|
|
154
|
+
|
|
155
|
+
[Parameter(Mandatory)]
|
|
156
|
+
[int] $QuoteDepth
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
if ($QuoteDepth -le 0 -or [string]::IsNullOrEmpty($Line)) {
|
|
160
|
+
return $Line
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
$result = $Line
|
|
164
|
+
for ($count = 0; $count -lt $QuoteDepth; $count++) {
|
|
165
|
+
$stripped = $result -replace '^\s{0,3}>\s?', ''
|
|
166
|
+
if ($stripped -eq $result) {
|
|
167
|
+
break
|
|
168
|
+
}
|
|
169
|
+
$result = $stripped
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return $result
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function Test-MermaidFenceClose {
|
|
176
|
+
<#
|
|
177
|
+
.SYNOPSIS
|
|
178
|
+
Returns $true when a parsed fence line closes the supplied open fence.
|
|
179
|
+
#>
|
|
180
|
+
[CmdletBinding()]
|
|
181
|
+
[OutputType([bool])]
|
|
182
|
+
param(
|
|
183
|
+
[Parameter(Mandatory)]
|
|
184
|
+
[System.Collections.Specialized.OrderedDictionary] $Fence,
|
|
185
|
+
|
|
186
|
+
[Parameter(Mandatory)]
|
|
187
|
+
[System.Collections.Specialized.OrderedDictionary] $OpenFence
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
if ($Fence.FenceCharacter -ne $OpenFence.FenceCharacter) {
|
|
191
|
+
return $false
|
|
192
|
+
}
|
|
193
|
+
if ($Fence.FenceLength -lt $OpenFence.FenceLength) {
|
|
194
|
+
return $false
|
|
195
|
+
}
|
|
196
|
+
if ($Fence.QuoteDepth -ne $OpenFence.QuoteDepth) {
|
|
197
|
+
return $false
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return [string]::IsNullOrEmpty($Fence.Info)
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function Get-MermaidFenceBlock {
|
|
204
|
+
<#
|
|
205
|
+
.SYNOPSIS
|
|
206
|
+
Extracts every Mermaid fenced block from Markdown text.
|
|
207
|
+
.DESCRIPTION
|
|
208
|
+
Returns an array of ordered dictionaries, one per ```` ```mermaid ````
|
|
209
|
+
block, each with:
|
|
210
|
+
Content the block body, joined with newlines
|
|
211
|
+
StartLine 1-based line number of the opening fence
|
|
212
|
+
BodyStartLine 1-based line number of the first body line
|
|
213
|
+
IsNested the fence opened while another fence was already open
|
|
214
|
+
IsOptedOut the immediately preceding line carried the opt-out marker
|
|
215
|
+
IsClosed a matching closing fence was found
|
|
216
|
+
A document with no Mermaid fence returns an empty array.
|
|
217
|
+
.PARAMETER Content
|
|
218
|
+
The full Markdown text.
|
|
219
|
+
#>
|
|
220
|
+
[CmdletBinding()]
|
|
221
|
+
[OutputType([object[]])]
|
|
222
|
+
param(
|
|
223
|
+
[AllowEmptyString()]
|
|
224
|
+
[AllowNull()]
|
|
225
|
+
[string] $Content
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
$blocks = [System.Collections.Generic.List[object]]::new()
|
|
229
|
+
$lines = @(Split-MermaidTextLine -Text $Content)
|
|
230
|
+
if ($lines.Count -eq 0) {
|
|
231
|
+
return [object[]]@()
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
$stack = [System.Collections.Generic.List[object]]::new()
|
|
235
|
+
|
|
236
|
+
for ($index = 0; $index -lt $lines.Count; $index++) {
|
|
237
|
+
$line = $lines[$index]
|
|
238
|
+
$fence = Get-MermaidFenceLine -Line $line
|
|
239
|
+
|
|
240
|
+
if ($null -ne $fence) {
|
|
241
|
+
if ($stack.Count -gt 0 -and (Test-MermaidFenceClose -Fence $fence -OpenFence $stack[$stack.Count - 1].Fence)) {
|
|
242
|
+
$entry = $stack[$stack.Count - 1]
|
|
243
|
+
$stack.RemoveAt($stack.Count - 1)
|
|
244
|
+
if ($entry.Fence.IsMermaid) {
|
|
245
|
+
$entry.Block.Content = ($entry.Body -join "`n")
|
|
246
|
+
$entry.Block.IsClosed = $true
|
|
247
|
+
$blocks.Add($entry.Block)
|
|
248
|
+
}
|
|
249
|
+
continue
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
$marker = $false
|
|
253
|
+
if ($index -gt 0) {
|
|
254
|
+
$marker = Test-MermaidOptOutMarker -Line $lines[$index - 1]
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
$stack.Add([ordered]@{
|
|
258
|
+
Fence = $fence
|
|
259
|
+
Body = [System.Collections.Generic.List[string]]::new()
|
|
260
|
+
Block = [ordered]@{
|
|
261
|
+
Content = ''
|
|
262
|
+
StartLine = $index + 1
|
|
263
|
+
BodyStartLine = $index + 2
|
|
264
|
+
IsNested = ($stack.Count -gt 0)
|
|
265
|
+
IsOptedOut = $marker
|
|
266
|
+
IsClosed = $false
|
|
267
|
+
}
|
|
268
|
+
})
|
|
269
|
+
continue
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if ($stack.Count -gt 0) {
|
|
273
|
+
$top = $stack[$stack.Count - 1]
|
|
274
|
+
$stack[$stack.Count - 1].Body.Add((Get-MermaidUnquotedLine -Line $line -QuoteDepth $top.Fence.QuoteDepth))
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
# Unclosed-fence tolerance: report whatever body was collected so a diagram at
|
|
279
|
+
# the end of a truncated document is not silently dropped.
|
|
280
|
+
foreach ($entry in $stack) {
|
|
281
|
+
if ($entry.Fence.IsMermaid) {
|
|
282
|
+
$entry.Block.Content = ($entry.Body -join "`n")
|
|
283
|
+
$blocks.Add($entry.Block)
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
# Unclosed blocks are appended after the closed ones, so sort back into
|
|
288
|
+
# document order to keep the caller's line-number reporting monotonic.
|
|
289
|
+
return [object[]]@($blocks.ToArray() | Sort-Object -Property { $_.StartLine })
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
Export-ModuleMember -Function `
|
|
293
|
+
Split-MermaidTextLine, `
|
|
294
|
+
Test-MermaidOptOutMarker, `
|
|
295
|
+
Get-MermaidFenceLine, `
|
|
296
|
+
Get-MermaidUnquotedLine, `
|
|
297
|
+
Test-MermaidFenceClose, `
|
|
298
|
+
Get-MermaidFenceBlock
|