@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,491 @@
|
|
|
1
|
+
<#
|
|
2
|
+
.SYNOPSIS
|
|
3
|
+
Mermaid grammar reference data for the dependency-free structural validator.
|
|
4
|
+
|
|
5
|
+
.DESCRIPTION
|
|
6
|
+
Data-only module holding the diagram-type keyword allowlist, the per-type
|
|
7
|
+
arrow/edge token sets, the deep-checked type set, and the statement-keyword
|
|
8
|
+
exemption list used by MermaidLineScanner.psm1 and MermaidValidation.psm1.
|
|
9
|
+
Every export is a pure accessor: no filesystem, subprocess, network, or
|
|
10
|
+
wall-clock access, and no input is mutated.
|
|
11
|
+
|
|
12
|
+
Pinned documentation version: Mermaid 11.17.0.
|
|
13
|
+
Source: https://mermaid.js.org/intro/syntax-reference.html
|
|
14
|
+
(per-type pages under https://mermaid.js.org/syntax/ ; fetched 2026-08-19).
|
|
15
|
+
|
|
16
|
+
Staleness is auditable from this header. Mermaid adds diagram types several
|
|
17
|
+
times per year, so the allowlist is a snapshot, not a closed set: an
|
|
18
|
+
out-of-date allowlist costs a drift warning, never a false rejection. The one
|
|
19
|
+
exception is a near miss, documented at Resolve-MermaidMisspelledKeyword.
|
|
20
|
+
|
|
21
|
+
`Verified = $true` means the keyword form was read from the pinned
|
|
22
|
+
documentation. `Verified = $false` means the type appears in the 11.x
|
|
23
|
+
documentation sidebar but its exact first-line keyword form was not verified,
|
|
24
|
+
so it resolves with a drift warning and its body is not judged.
|
|
25
|
+
|
|
26
|
+
Only the five deep-checked types (flowchart, sequence, class, state, ER) carry
|
|
27
|
+
structural judgement; every other type is keyword-checked only, the fail-open
|
|
28
|
+
policy for free-text and plugin-backed grammars. Brackets are structural only
|
|
29
|
+
where they delimit node shapes or attribute blocks: a gantt task named
|
|
30
|
+
`Deploy (phase 1` must never be blocked as unbalanced.
|
|
31
|
+
#>
|
|
32
|
+
|
|
33
|
+
Set-StrictMode -Version Latest
|
|
34
|
+
|
|
35
|
+
$script:MermaidGrammarVersion = '11.17.0'
|
|
36
|
+
$script:MermaidGrammarSourceUrl = 'https://mermaid.js.org/intro/syntax-reference.html'
|
|
37
|
+
|
|
38
|
+
# Statement-keyword lines carry URLs, CSS declarations, and free text. They are
|
|
39
|
+
# exempt from BOTH the arrow rules and the bracket-balance rules; only the
|
|
40
|
+
# quote-termination rule may still apply. Narrowing or extending this list changes
|
|
41
|
+
# false-positive behavior, so it is held in one place.
|
|
42
|
+
$script:MermaidStatementKeyword = @(
|
|
43
|
+
'click', 'style', 'classDef', 'linkStyle', 'class', 'accTitle', 'accDescr', 'title'
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
# Block and declaration keywords that are never edge statements. They are exempt
|
|
47
|
+
# from the arrow rules only (their bracket characters still participate in the
|
|
48
|
+
# balance count, which is what keeps ER and state composite blocks correct).
|
|
49
|
+
# A longer list here can only make the validator more permissive, never stricter.
|
|
50
|
+
$script:MermaidNonEdgeKeyword = @(
|
|
51
|
+
'subgraph', 'end', 'direction', 'state', 'note', 'Note', 'participant',
|
|
52
|
+
'actor', 'loop', 'alt', 'else', 'opt', 'par', 'and', 'critical', 'break',
|
|
53
|
+
'rect', 'activate', 'deactivate', 'autonumber', 'box', 'create', 'destroy',
|
|
54
|
+
'namespace', 'cssClass', 'callback', 'link', 'links', 'properties',
|
|
55
|
+
'details', 'section', 'requirement', 'element'
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
# Types whose statement lines put free-text labels after the first colon. Arrow
|
|
59
|
+
# and quote checks apply to the pre-colon segment only for these types.
|
|
60
|
+
$script:MermaidPostColonLabelType = @('sequence', 'class', 'state', 'er')
|
|
61
|
+
|
|
62
|
+
<#
|
|
63
|
+
Arrow patterns are anchored regexes matched against a single candidate arrow
|
|
64
|
+
token produced by Get-MermaidArrowCandidate. They are deliberately more
|
|
65
|
+
permissive than the documented token list so that length variants (`---->`,
|
|
66
|
+
`====>`, `-...->`) and the tilde runs used by class-diagram generics never
|
|
67
|
+
produce a finding. The five deep entries below carry the full shape; the
|
|
68
|
+
keyword-accept entries are expanded from a compact map because they share one
|
|
69
|
+
fixed shape: keyword-checked only, brackets never structural, no arrow grammar.
|
|
70
|
+
#>
|
|
71
|
+
$script:MermaidDiagramType = [ordered]@{
|
|
72
|
+
flowchart = [ordered]@{
|
|
73
|
+
Keywords = @('flowchart', 'graph', 'flowchart-elk'); Verified = $true; Deep = $true; BracketStructural = $true
|
|
74
|
+
ArrowTokens = @('-->', '---', '-.->', '-.-', '==>', '===', '~~~', '--o', '--x', 'o--o', 'x--x', '<-->')
|
|
75
|
+
ArrowPattern = '^(?:~+|[<ox]?-{2,}[>ox]?|<?={2,}>?|-\.+-?>?|\.+-?>?)$'
|
|
76
|
+
}
|
|
77
|
+
sequence = [ordered]@{
|
|
78
|
+
Keywords = @('sequenceDiagram'); Verified = $true; Deep = $true; BracketStructural = $false
|
|
79
|
+
ArrowTokens = @('->', '-->', '->>', '-->>', '<<->>', '<<-->>', '-x', '--x', '-)', '--)')
|
|
80
|
+
ArrowPattern = '^(?:~+|(?:<<)?-{1,2}(?:>{1,2}|x|\)|\\|/)?)$'
|
|
81
|
+
}
|
|
82
|
+
class = [ordered]@{
|
|
83
|
+
Keywords = @('classDiagram', 'classDiagram-v2'); Verified = $true; Deep = $true; BracketStructural = $true
|
|
84
|
+
ArrowTokens = @('<|--', '--|>', '*--', '--*', 'o--', '--o', '-->', '<--', '--', '..>', '<..', '..|>', '<|..', '..')
|
|
85
|
+
ArrowPattern = '^(?:~+|(?:<\|?|\*|o)?(?:-{2,}|\.{2,})(?:\|>|>|\*|o)?)$'
|
|
86
|
+
}
|
|
87
|
+
state = [ordered]@{
|
|
88
|
+
Keywords = @('stateDiagram-v2', 'stateDiagram'); Verified = $true; Deep = $true; BracketStructural = $true
|
|
89
|
+
ArrowTokens = @('-->')
|
|
90
|
+
ArrowPattern = '^(?:~+|-{2,}>)$'
|
|
91
|
+
}
|
|
92
|
+
er = [ordered]@{
|
|
93
|
+
Keywords = @('erDiagram'); Verified = $true; Deep = $true; BracketStructural = $true
|
|
94
|
+
ArrowTokens = @('|o--o|', '||--||', '}o--o{', '}|--|{', '|o..o|', '}|..|{')
|
|
95
|
+
ArrowPattern = '^(?:~+|(?:\|o|\|\||\}o|\}\|)(?:-{2}|\.{2})(?:o\||\|\||o\{|\|\{))$'
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
# Keyword-accept types, verified against the pinned documentation. The map value is
|
|
100
|
+
# the documented first-line keyword form or forms. `packet` is the 11.17 keyword;
|
|
101
|
+
# `packet-beta` was the earlier form, retained as an alias so neither spelling
|
|
102
|
+
# costs a false rejection. The three types whose documentation lists edge tokens
|
|
103
|
+
# carry them for reference only (see the reference-token block after the expansion
|
|
104
|
+
# loop); no arrow judgement is ever performed on a non-deep type.
|
|
105
|
+
$script:MermaidVerifiedKeywordOnlyType = [ordered]@{
|
|
106
|
+
journey = @('journey')
|
|
107
|
+
gantt = @('gantt')
|
|
108
|
+
pie = @('pie')
|
|
109
|
+
quadrant = @('quadrantChart')
|
|
110
|
+
requirement = @('requirementDiagram')
|
|
111
|
+
gitgraph = @('gitGraph')
|
|
112
|
+
mindmap = @('mindmap')
|
|
113
|
+
timeline = @('timeline')
|
|
114
|
+
zenuml = @('zenuml')
|
|
115
|
+
sankey = @('sankey-beta')
|
|
116
|
+
xychart = @('xychart-beta')
|
|
117
|
+
block = @('block-beta')
|
|
118
|
+
packet = @('packet', 'packet-beta')
|
|
119
|
+
kanban = @('kanban')
|
|
120
|
+
architecture = @('architecture-beta')
|
|
121
|
+
radar = @('radar-beta')
|
|
122
|
+
treemap = @('treemap-beta')
|
|
123
|
+
c4 = @('C4Context', 'C4Container', 'C4Component', 'C4Dynamic', 'C4Deployment')
|
|
124
|
+
info = @('info')
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
# 11.x documentation-sidebar additions. The diagram types are documented but their
|
|
128
|
+
# exact first-line keyword forms were not individually verified, so they are
|
|
129
|
+
# keyword-accept only and always carry a drift warning.
|
|
130
|
+
$script:MermaidUnverifiedKeywordOnlyType = [ordered]@{
|
|
131
|
+
swimlanes = @('swimlanes')
|
|
132
|
+
eventmodeling = @('eventmodeling')
|
|
133
|
+
venn = @('venn')
|
|
134
|
+
ishikawa = @('ishikawa')
|
|
135
|
+
wardley = @('wardley')
|
|
136
|
+
cynefin = @('cynefin')
|
|
137
|
+
treeview = @('treeView')
|
|
138
|
+
railroad = @('railroad', 'railroad-beta')
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
foreach ($tier in @(
|
|
142
|
+
@{ Map = $script:MermaidVerifiedKeywordOnlyType; Verified = $true },
|
|
143
|
+
@{ Map = $script:MermaidUnverifiedKeywordOnlyType; Verified = $false })) {
|
|
144
|
+
foreach ($name in $tier.Map.Keys) {
|
|
145
|
+
$script:MermaidDiagramType[$name] = [ordered]@{
|
|
146
|
+
Keywords = @($tier.Map[$name]); Verified = $tier.Verified; Deep = $false; BracketStructural = $false
|
|
147
|
+
ArrowTokens = @()
|
|
148
|
+
ArrowPattern = $null
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
# Documented edge tokens for the keyword-accept types whose pages list them. These
|
|
154
|
+
# are reference data only: ArrowPattern stays $null, so no arrow is ever judged.
|
|
155
|
+
$script:MermaidDiagramType['requirement'].ArrowTokens = @('->', '<-')
|
|
156
|
+
$script:MermaidDiagramType['block'].ArrowTokens = @('-->', '--')
|
|
157
|
+
$script:MermaidDiagramType['architecture'].ArrowTokens = @('--', '-->')
|
|
158
|
+
|
|
159
|
+
function Get-MermaidGrammarVersion {
|
|
160
|
+
<#
|
|
161
|
+
.SYNOPSIS
|
|
162
|
+
Returns the pinned Mermaid documentation version this table snapshots.
|
|
163
|
+
#>
|
|
164
|
+
[CmdletBinding()]
|
|
165
|
+
[OutputType([string])]
|
|
166
|
+
param()
|
|
167
|
+
return $script:MermaidGrammarVersion
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function Get-MermaidGrammarSourceUrl {
|
|
171
|
+
<#
|
|
172
|
+
.SYNOPSIS
|
|
173
|
+
Returns the documentation URL the pinned grammar table was read from.
|
|
174
|
+
#>
|
|
175
|
+
[CmdletBinding()]
|
|
176
|
+
[OutputType([string])]
|
|
177
|
+
param()
|
|
178
|
+
return $script:MermaidGrammarSourceUrl
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function Get-MermaidDiagramTypeName {
|
|
182
|
+
<#
|
|
183
|
+
.SYNOPSIS
|
|
184
|
+
Returns the canonical diagram-type names in table order.
|
|
185
|
+
#>
|
|
186
|
+
[CmdletBinding()]
|
|
187
|
+
[OutputType([string[]])]
|
|
188
|
+
param()
|
|
189
|
+
return [string[]]@($script:MermaidDiagramType.Keys)
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function Get-MermaidDiagramTypeEntry {
|
|
193
|
+
<#
|
|
194
|
+
.SYNOPSIS
|
|
195
|
+
Returns the grammar entry for a canonical diagram-type name, or $null.
|
|
196
|
+
#>
|
|
197
|
+
[CmdletBinding()]
|
|
198
|
+
[OutputType([System.Collections.Specialized.OrderedDictionary])]
|
|
199
|
+
param([Parameter(Mandatory)][AllowEmptyString()][string] $DiagramType)
|
|
200
|
+
|
|
201
|
+
if (-not $script:MermaidDiagramType.Contains($DiagramType)) { return $null }
|
|
202
|
+
|
|
203
|
+
return $script:MermaidDiagramType[$DiagramType]
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function Get-MermaidDeepCheckedType {
|
|
207
|
+
<#
|
|
208
|
+
.SYNOPSIS
|
|
209
|
+
Returns the diagram types that receive structural judgement beyond the
|
|
210
|
+
first-line keyword check.
|
|
211
|
+
#>
|
|
212
|
+
[CmdletBinding()]
|
|
213
|
+
[OutputType([string[]])]
|
|
214
|
+
param()
|
|
215
|
+
|
|
216
|
+
$names = [System.Collections.Generic.List[string]]::new()
|
|
217
|
+
foreach ($name in $script:MermaidDiagramType.Keys) {
|
|
218
|
+
if ($script:MermaidDiagramType[$name].Deep) {
|
|
219
|
+
$names.Add($name)
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return [string[]]@($names.ToArray())
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function Test-MermaidDeepCheckedType {
|
|
227
|
+
<#
|
|
228
|
+
.SYNOPSIS
|
|
229
|
+
Returns $true when the supplied canonical diagram type is deep-checked.
|
|
230
|
+
#>
|
|
231
|
+
[CmdletBinding()]
|
|
232
|
+
[OutputType([bool])]
|
|
233
|
+
param([Parameter(Mandatory)][AllowEmptyString()][string] $DiagramType)
|
|
234
|
+
|
|
235
|
+
$entry = Get-MermaidDiagramTypeEntry -DiagramType $DiagramType
|
|
236
|
+
if ($null -eq $entry) { return $false }
|
|
237
|
+
|
|
238
|
+
return [bool]$entry.Deep
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function Test-MermaidBracketStructuralType {
|
|
242
|
+
<#
|
|
243
|
+
.SYNOPSIS
|
|
244
|
+
Returns $true when brackets are structural for the supplied diagram type.
|
|
245
|
+
#>
|
|
246
|
+
[CmdletBinding()]
|
|
247
|
+
[OutputType([bool])]
|
|
248
|
+
param([Parameter(Mandatory)][AllowEmptyString()][string] $DiagramType)
|
|
249
|
+
|
|
250
|
+
$entry = Get-MermaidDiagramTypeEntry -DiagramType $DiagramType
|
|
251
|
+
if ($null -eq $entry) { return $false }
|
|
252
|
+
|
|
253
|
+
return [bool]$entry.BracketStructural
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function Test-MermaidPostColonLabelType {
|
|
257
|
+
<#
|
|
258
|
+
.SYNOPSIS
|
|
259
|
+
Returns $true when the diagram type puts free text after the first colon.
|
|
260
|
+
#>
|
|
261
|
+
[CmdletBinding()]
|
|
262
|
+
[OutputType([bool])]
|
|
263
|
+
param([Parameter(Mandatory)][AllowEmptyString()][string] $DiagramType)
|
|
264
|
+
|
|
265
|
+
return $script:MermaidPostColonLabelType -contains $DiagramType
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function Get-MermaidArrowToken {
|
|
269
|
+
<#
|
|
270
|
+
.SYNOPSIS
|
|
271
|
+
Returns the documented arrow/edge token set for a diagram type.
|
|
272
|
+
.DESCRIPTION
|
|
273
|
+
The documentation reference set. Validation uses Get-MermaidArrowPattern,
|
|
274
|
+
which additionally admits the documented length variants.
|
|
275
|
+
#>
|
|
276
|
+
[CmdletBinding()]
|
|
277
|
+
[OutputType([string[]])]
|
|
278
|
+
param([Parameter(Mandatory)][AllowEmptyString()][string] $DiagramType)
|
|
279
|
+
|
|
280
|
+
$entry = Get-MermaidDiagramTypeEntry -DiagramType $DiagramType
|
|
281
|
+
if ($null -eq $entry) { return [string[]]@() }
|
|
282
|
+
|
|
283
|
+
return [string[]]@($entry.ArrowTokens)
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function Get-MermaidArrowPattern {
|
|
287
|
+
<#
|
|
288
|
+
.SYNOPSIS
|
|
289
|
+
Returns the anchored regex accepting a valid arrow token for a type.
|
|
290
|
+
.DESCRIPTION
|
|
291
|
+
Returns $null when the type carries no arrow grammar, which the caller must
|
|
292
|
+
treat as "do not judge arrows" rather than "reject all arrows".
|
|
293
|
+
#>
|
|
294
|
+
[CmdletBinding()]
|
|
295
|
+
[OutputType([string])]
|
|
296
|
+
param([Parameter(Mandatory)][AllowEmptyString()][string] $DiagramType)
|
|
297
|
+
|
|
298
|
+
$entry = Get-MermaidDiagramTypeEntry -DiagramType $DiagramType
|
|
299
|
+
if ($null -eq $entry) { return $null }
|
|
300
|
+
|
|
301
|
+
return $entry.ArrowPattern
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
function Get-MermaidStatementKeyword {
|
|
305
|
+
<#
|
|
306
|
+
.SYNOPSIS
|
|
307
|
+
Returns the statement keywords exempt from arrow and bracket rules.
|
|
308
|
+
#>
|
|
309
|
+
[CmdletBinding()]
|
|
310
|
+
[OutputType([string[]])]
|
|
311
|
+
param()
|
|
312
|
+
return [string[]]@($script:MermaidStatementKeyword)
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
function Test-MermaidStatementKeyword {
|
|
316
|
+
<#
|
|
317
|
+
.SYNOPSIS
|
|
318
|
+
Returns $true when a first token is an exempt statement keyword.
|
|
319
|
+
#>
|
|
320
|
+
[CmdletBinding()]
|
|
321
|
+
[OutputType([bool])]
|
|
322
|
+
param([AllowEmptyString()][string] $Token)
|
|
323
|
+
|
|
324
|
+
if ([string]::IsNullOrEmpty($Token)) { return $false }
|
|
325
|
+
|
|
326
|
+
return $script:MermaidStatementKeyword -contains $Token
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
function Test-MermaidNonEdgeKeyword {
|
|
330
|
+
<#
|
|
331
|
+
.SYNOPSIS
|
|
332
|
+
Returns $true when a first token opens a block or declaration.
|
|
333
|
+
#>
|
|
334
|
+
[CmdletBinding()]
|
|
335
|
+
[OutputType([bool])]
|
|
336
|
+
param([AllowEmptyString()][string] $Token)
|
|
337
|
+
|
|
338
|
+
if ([string]::IsNullOrEmpty($Token)) { return $false }
|
|
339
|
+
|
|
340
|
+
return $script:MermaidNonEdgeKeyword -contains $Token
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
function Test-MermaidPlausibleKeyword {
|
|
344
|
+
<#
|
|
345
|
+
.SYNOPSIS
|
|
346
|
+
Returns $true when a token is shaped like a Mermaid diagram keyword.
|
|
347
|
+
.DESCRIPTION
|
|
348
|
+
The shape rule is the version-drift safety valve: a token of letters,
|
|
349
|
+
digits, and hyphens beginning with a letter is treated as a plausible
|
|
350
|
+
keyword newer than the pinned allowlist. Only a missing or clearly
|
|
351
|
+
non-keyword first line is rejected outright.
|
|
352
|
+
#>
|
|
353
|
+
[CmdletBinding()]
|
|
354
|
+
[OutputType([bool])]
|
|
355
|
+
param([AllowEmptyString()][string] $Token)
|
|
356
|
+
|
|
357
|
+
if ([string]::IsNullOrEmpty($Token)) { return $false }
|
|
358
|
+
|
|
359
|
+
return [bool]($Token -cmatch '^[A-Za-z][A-Za-z0-9-]*$')
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
function Test-MermaidSingleEditDistance {
|
|
363
|
+
<#
|
|
364
|
+
.SYNOPSIS
|
|
365
|
+
Returns $true when two tokens differ by exactly one character edit.
|
|
366
|
+
.DESCRIPTION
|
|
367
|
+
One edit means one substitution (equal lengths, exactly one differing
|
|
368
|
+
position) or one insertion or deletion (lengths differing by one, where
|
|
369
|
+
removing one character from the longer yields the shorter). Comparison is
|
|
370
|
+
ordinal and case-sensitive, matching Mermaid keyword resolution.
|
|
371
|
+
#>
|
|
372
|
+
[CmdletBinding()]
|
|
373
|
+
[OutputType([bool])]
|
|
374
|
+
param(
|
|
375
|
+
[Parameter(Mandatory)][AllowEmptyString()][string] $First,
|
|
376
|
+
[Parameter(Mandatory)][AllowEmptyString()][string] $Second
|
|
377
|
+
)
|
|
378
|
+
|
|
379
|
+
if ([Math]::Abs($First.Length - $Second.Length) -gt 1) { return $false }
|
|
380
|
+
|
|
381
|
+
if ($First.Length -eq $Second.Length) {
|
|
382
|
+
$mismatch = 0
|
|
383
|
+
for ($index = 0; $index -lt $First.Length; $index++) {
|
|
384
|
+
if ($First[$index] -cne $Second[$index]) {
|
|
385
|
+
$mismatch++
|
|
386
|
+
}
|
|
387
|
+
if ($mismatch -gt 1) { return $false }
|
|
388
|
+
}
|
|
389
|
+
return ($mismatch -eq 1)
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
$longer = if ($First.Length -gt $Second.Length) { $First } else { $Second }
|
|
393
|
+
$shorter = if ($First.Length -gt $Second.Length) { $Second } else { $First }
|
|
394
|
+
for ($index = 0; $index -lt $longer.Length; $index++) {
|
|
395
|
+
if ([string]::Equals($longer.Remove($index, 1), $shorter, [System.StringComparison]::Ordinal)) { return $true }
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
return $false
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
function Resolve-MermaidMisspelledKeyword {
|
|
402
|
+
<#
|
|
403
|
+
.SYNOPSIS
|
|
404
|
+
Returns the known keyword a token appears to misspell, or $null.
|
|
405
|
+
.DESCRIPTION
|
|
406
|
+
Only tokens of five or more characters are considered, and only a
|
|
407
|
+
single-edit difference counts. The narrow radius is deliberate: a wider one
|
|
408
|
+
would begin catching genuinely new diagram types and convert the
|
|
409
|
+
version-drift warn-and-allow valve into a false rejection. A typo, by
|
|
410
|
+
contrast, is a defect the gate is required to name (`flowchar` for
|
|
411
|
+
`flowchart`).
|
|
412
|
+
#>
|
|
413
|
+
[CmdletBinding()]
|
|
414
|
+
[OutputType([string])]
|
|
415
|
+
param([AllowEmptyString()][string] $Token)
|
|
416
|
+
|
|
417
|
+
if ([string]::IsNullOrEmpty($Token) -or $Token.Length -lt 5) { return $null }
|
|
418
|
+
|
|
419
|
+
foreach ($name in $script:MermaidDiagramType.Keys) {
|
|
420
|
+
foreach ($keyword in $script:MermaidDiagramType[$name].Keywords) {
|
|
421
|
+
if ($keyword.Length -lt 5) {
|
|
422
|
+
continue
|
|
423
|
+
}
|
|
424
|
+
if (Test-MermaidSingleEditDistance -First $Token -Second $keyword) { return $keyword }
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
return $null
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
function Resolve-MermaidDiagramType {
|
|
432
|
+
<#
|
|
433
|
+
.SYNOPSIS
|
|
434
|
+
Resolves the declared diagram type from a candidate first line.
|
|
435
|
+
.DESCRIPTION
|
|
436
|
+
Takes the first whitespace-delimited token of the trimmed line, strips a
|
|
437
|
+
trailing colon or semicolon (the `gitGraph LR:` and `graph TD;` forms), and
|
|
438
|
+
looks it up with ordinal, case-sensitive comparison because Mermaid
|
|
439
|
+
keywords are case-sensitive (`C4Context`, `stateDiagram-v2`).
|
|
440
|
+
|
|
441
|
+
Returns Token (the normalized first token), Type (canonical name or $null),
|
|
442
|
+
IsKnown, IsVerified, IsPlausibleKeyword, and MisspelledOf (the keyword an
|
|
443
|
+
unresolved token appears to misspell, or $null).
|
|
444
|
+
#>
|
|
445
|
+
[CmdletBinding()]
|
|
446
|
+
[OutputType([System.Collections.Specialized.OrderedDictionary])]
|
|
447
|
+
param([AllowEmptyString()][string] $FirstLine)
|
|
448
|
+
|
|
449
|
+
$token = ''
|
|
450
|
+
if (-not [string]::IsNullOrWhiteSpace($FirstLine)) {
|
|
451
|
+
$token = (($FirstLine.Trim() -split '\s+', 2)[0]) -replace '[:;]+$', ''
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
$result = [ordered]@{
|
|
455
|
+
Token = $token
|
|
456
|
+
Type = $null
|
|
457
|
+
IsKnown = $false
|
|
458
|
+
IsVerified = $false
|
|
459
|
+
IsPlausibleKeyword = (Test-MermaidPlausibleKeyword -Token $token)
|
|
460
|
+
MisspelledOf = $null
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
if ([string]::IsNullOrEmpty($token)) { return $result }
|
|
464
|
+
|
|
465
|
+
foreach ($name in $script:MermaidDiagramType.Keys) {
|
|
466
|
+
$entry = $script:MermaidDiagramType[$name]
|
|
467
|
+
foreach ($keyword in $entry.Keywords) {
|
|
468
|
+
if ([string]::Equals($keyword, $token, [System.StringComparison]::Ordinal)) {
|
|
469
|
+
$result.Type = $name
|
|
470
|
+
$result.IsKnown = $true
|
|
471
|
+
$result.IsVerified = [bool]$entry.Verified
|
|
472
|
+
return $result
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
if ($result.IsPlausibleKeyword) {
|
|
478
|
+
$result.MisspelledOf = Resolve-MermaidMisspelledKeyword -Token $token
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
return $result
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
Export-ModuleMember -Function @(
|
|
485
|
+
'Get-MermaidGrammarVersion', 'Get-MermaidGrammarSourceUrl', 'Get-MermaidDiagramTypeName',
|
|
486
|
+
'Get-MermaidDiagramTypeEntry', 'Get-MermaidDeepCheckedType', 'Test-MermaidDeepCheckedType',
|
|
487
|
+
'Test-MermaidBracketStructuralType', 'Test-MermaidPostColonLabelType', 'Get-MermaidArrowToken',
|
|
488
|
+
'Get-MermaidArrowPattern', 'Get-MermaidStatementKeyword', 'Test-MermaidStatementKeyword',
|
|
489
|
+
'Test-MermaidNonEdgeKeyword', 'Test-MermaidPlausibleKeyword', 'Test-MermaidSingleEditDistance',
|
|
490
|
+
'Resolve-MermaidMisspelledKeyword', 'Resolve-MermaidDiagramType'
|
|
491
|
+
)
|