mjolnir-qa 1.0.2 → 1.0.4
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/CHANGELOG.md +42 -0
- package/README.ar.md +4 -4
- package/README.bn.md +4 -4
- package/README.br.md +4 -4
- package/README.bs.md +4 -4
- package/README.da.md +4 -4
- package/README.de.md +4 -4
- package/README.es.md +4 -4
- package/README.fr.md +4 -4
- package/README.gr.md +4 -4
- package/README.he.md +4 -4
- package/README.it.md +4 -4
- package/README.ja.md +4 -4
- package/README.ko.md +4 -4
- package/README.md +5 -5
- package/README.no.md +4 -4
- package/README.pl.md +4 -4
- package/README.ru.md +4 -4
- package/README.th.md +4 -4
- package/README.tr.md +4 -4
- package/README.uk.md +4 -4
- package/README.vi.md +4 -4
- package/README.zh.md +4 -4
- package/README.zht.md +4 -4
- package/dist/cli.d.mts +1 -1
- package/dist/cli.mjs +353 -76
- package/dist/mcp/stdio.mjs +353 -76
- package/package.json +8 -2
package/dist/cli.mjs
CHANGED
|
@@ -10077,6 +10077,237 @@ function createScanCache(root) {
|
|
|
10077
10077
|
};
|
|
10078
10078
|
}
|
|
10079
10079
|
//#endregion
|
|
10080
|
+
//#region src/brand/tokens.ts
|
|
10081
|
+
/**
|
|
10082
|
+
* The single source of brand truth.
|
|
10083
|
+
*
|
|
10084
|
+
* Every colour, typeface and motion constant Mjölnir shows a human —
|
|
10085
|
+
* terminal, README SVGs, demo video, website, docs, badges — resolves to
|
|
10086
|
+
* a value in this file. Nothing else may define one.
|
|
10087
|
+
*
|
|
10088
|
+
* WHY THIS EXISTS. Before it, the palette existed in six independent
|
|
10089
|
+
* copies: `site/.vitepress/theme/styles/vars.css`, `NORSE` in
|
|
10090
|
+
* `src/reporter/theme.ts`, `scripts/readme-svg.ts`,
|
|
10091
|
+
* `scripts/video/terminal-page.ts`, `scripts/generate-readme-architecture.ts`
|
|
10092
|
+
* and the table in `assets/brand/README.md`. Exactly one pair of those
|
|
10093
|
+
* was guarded (site-doctor Check 8, doc ↔ vars.css). The unguarded edges
|
|
10094
|
+
* are where the shipped surfaces drifted apart: the terminal and the site
|
|
10095
|
+
* disagreed on six semantic roles, the architecture diagram invented its
|
|
10096
|
+
* own neutral ramp, and the README badges still carried a palette retired
|
|
10097
|
+
* two releases earlier. `scripts/brand-doctor.mjs` now checks every edge
|
|
10098
|
+
* against this file.
|
|
10099
|
+
*
|
|
10100
|
+
* PURITY. Pure data. No I/O, no rendering, no environment access, no
|
|
10101
|
+
* imports, no logic. Consumers convert (hex → ANSI triplet, hex → CSS)
|
|
10102
|
+
* themselves. Same reason `score-state.ts` is pure: it makes the whole
|
|
10103
|
+
* thing golden-testable and safe to ship inside the npm package, where
|
|
10104
|
+
* it costs a few hundred bytes and replaces values the package already
|
|
10105
|
+
* carried anyway.
|
|
10106
|
+
*
|
|
10107
|
+
* DERIVATION. The palette is the one derived from the logo in PR #20
|
|
10108
|
+
* (brushed steel and forge gold under an aurora, over midnight iron).
|
|
10109
|
+
* Where the terminal disagreed with it, the terminal converges — see
|
|
10110
|
+
* `PENDING_TERMINAL` below. Full rationale: `assets/brand/README.md`.
|
|
10111
|
+
*
|
|
10112
|
+
* ACCESSIBILITY. Every foreground token in `BRAND` meets WCAG AA
|
|
10113
|
+
* (≥ 4.5:1) against every surface token it is allowed to sit on. That is
|
|
10114
|
+
* not a claim, it is `brand-doctor` rule 8, which computes the ratios.
|
|
10115
|
+
* The weakest legal pairing is `steelDim` on `ink800` at 5.00:1.
|
|
10116
|
+
*/
|
|
10117
|
+
/**
|
|
10118
|
+
* The two brand hues plus the neutral they sit on.
|
|
10119
|
+
*
|
|
10120
|
+
* GOLD IS SCARCE. It means forged / certified / earned / decisive — the
|
|
10121
|
+
* primary mark, the FORGED state, one call to action. It is not a paint
|
|
10122
|
+
* bucket: gold as default text, default border or default heading is a
|
|
10123
|
+
* brand-doctor finding, not a style choice.
|
|
10124
|
+
*
|
|
10125
|
+
* AURORA is verification energy — the secondary, and the hue that marks
|
|
10126
|
+
* the runtime half of the trust ladder.
|
|
10127
|
+
*/
|
|
10128
|
+
const BRAND = {
|
|
10129
|
+
gold: "#C19A34",
|
|
10130
|
+
goldBright: "#E6BD57",
|
|
10131
|
+
goldHot: "#F4DC9C",
|
|
10132
|
+
/** Pressed / deepest gold — the only step dark enough to carry white. */
|
|
10133
|
+
goldDeep: "#A5811C",
|
|
10134
|
+
aurora: "#37ABBD",
|
|
10135
|
+
auroraBright: "#45C1D4",
|
|
10136
|
+
auroraCyan: "#5CBDE0",
|
|
10137
|
+
steel: "#C8CBCF",
|
|
10138
|
+
steelDim: "#8B939D"
|
|
10139
|
+
};
|
|
10140
|
+
/**
|
|
10141
|
+
* Midnight iron. One ramp, four steps, darkest first.
|
|
10142
|
+
*
|
|
10143
|
+
* `terminal` and `terminalBar` share one tone deliberately: the window's
|
|
10144
|
+
* only seam is a hairline ring and an inset shadow, never a second fill.
|
|
10145
|
+
* `chromeDot` is the three window dots — see the note on
|
|
10146
|
+
* `PENDING_TERMINAL.chromeDots` for why they are no longer red/amber/green.
|
|
10147
|
+
*/
|
|
10148
|
+
const SURFACE = {
|
|
10149
|
+
ink950: "#0A1119",
|
|
10150
|
+
ink900: "#0C1420",
|
|
10151
|
+
ink850: "#111A29",
|
|
10152
|
+
ink800: "#18243A",
|
|
10153
|
+
/** Raised panel (cards, elevated surfaces). */
|
|
10154
|
+
panel: "#141F33",
|
|
10155
|
+
/** Soft fill (inline code, quiet chips). */
|
|
10156
|
+
soft: "#1A2740",
|
|
10157
|
+
/** Terminal body — the deepest tone, so a terminal reads as recessed. */
|
|
10158
|
+
terminal: "#0A1119",
|
|
10159
|
+
/** Terminal title bar — the same tone; the seam is shadow, not colour. */
|
|
10160
|
+
terminalBar: "#0A1119",
|
|
10161
|
+
/** The three window dots. One neutral, not a traffic light. */
|
|
10162
|
+
chromeDot: "#18243A"
|
|
10163
|
+
};
|
|
10164
|
+
const TEXT = {
|
|
10165
|
+
primary: "#EAEEF5",
|
|
10166
|
+
secondary: "#ABB6C6",
|
|
10167
|
+
muted: "#8B939D",
|
|
10168
|
+
/** Ink for text set ON gold (buttons, the FORGED chip). 7.17:1 on `gold`. */
|
|
10169
|
+
onGold: "#0A1119"
|
|
10170
|
+
};
|
|
10171
|
+
/**
|
|
10172
|
+
* Non-score status. `ok` is the one green in the system and it is NOT a
|
|
10173
|
+
* score colour — it survives only for contexts with no worthiness
|
|
10174
|
+
* meaning ("autofix applied", "analysis complete"). A green score would
|
|
10175
|
+
* say "your software is fine", which is the exact claim this product
|
|
10176
|
+
* refuses to make.
|
|
10177
|
+
*/
|
|
10178
|
+
const STATUS = {
|
|
10179
|
+
ok: "#4FB477",
|
|
10180
|
+
info: "#5CC4E0",
|
|
10181
|
+
warning: "#E6BD57",
|
|
10182
|
+
error: "#EC6B66"
|
|
10183
|
+
};
|
|
10184
|
+
/**
|
|
10185
|
+
* The four ScoreState bands plus the unmeasured state. Band thresholds
|
|
10186
|
+
* and runes live in `src/reporter/score-state.ts`, which stays free of
|
|
10187
|
+
* colour — it emits a palette KEY and each surface resolves it here.
|
|
10188
|
+
*
|
|
10189
|
+
* `unmeasured` is steel-dim on purpose. UNKNOWN is a legitimate answer,
|
|
10190
|
+
* not a failure: colouring it red would make "we did not measure this"
|
|
10191
|
+
* look like "this is broken", which is precisely the dishonesty the
|
|
10192
|
+
* north-star law exists to prevent.
|
|
10193
|
+
*/
|
|
10194
|
+
const SCORE = {
|
|
10195
|
+
critical: "#EC6B66",
|
|
10196
|
+
warning: "#E6BD57",
|
|
10197
|
+
trusted: "#5CC4E0",
|
|
10198
|
+
forged: "#F4DC9C",
|
|
10199
|
+
unmeasured: "#8B939D"
|
|
10200
|
+
};
|
|
10201
|
+
/**
|
|
10202
|
+
* E0 → E1 → E2 is a certainty ramp, and it is deliberately HUE-FREE.
|
|
10203
|
+
*
|
|
10204
|
+
* Evidence level says how sure we are, not whether the news is good. A
|
|
10205
|
+
* deterministic proof (E2) is a defect we are certain about — painting
|
|
10206
|
+
* it gold or green would read as an achievement. So certainty is carried
|
|
10207
|
+
* by brightness alone, and the *shape* does the real work:
|
|
10208
|
+
*
|
|
10209
|
+
* E0 open ring observation, no weight
|
|
10210
|
+
* E1 half-filled pattern evidence, half weight
|
|
10211
|
+
* E2 sealed deterministic proof, full weight
|
|
10212
|
+
*
|
|
10213
|
+
* Colour never carries this alone (R11): the geometry is the signal and
|
|
10214
|
+
* survives `--ascii`, `NO_COLOR` and monochrome print.
|
|
10215
|
+
*/
|
|
10216
|
+
const EVIDENCE = {
|
|
10217
|
+
e0: "#8B939D",
|
|
10218
|
+
e1: "#ABB6C6",
|
|
10219
|
+
e2: "#EAEEF5"
|
|
10220
|
+
};
|
|
10221
|
+
/**
|
|
10222
|
+
* L0–L5, and the most important boundary in the product.
|
|
10223
|
+
*
|
|
10224
|
+
* L0–L2 are STATIC: the neutral steel ramp, brightening to the static
|
|
10225
|
+
* ceiling at L2. L3–L5 require a real run, and the hue changes to aurora
|
|
10226
|
+
* exactly there. The boundary is a hue break, not a gradient step,
|
|
10227
|
+
* because it is a change of kind and not of degree — a static-only
|
|
10228
|
+
* finding can never climb past L2, however confident it is.
|
|
10229
|
+
*
|
|
10230
|
+
* Every surface that draws the ladder must draw that break.
|
|
10231
|
+
*/
|
|
10232
|
+
const TRUST = {
|
|
10233
|
+
l0: "#8B939D",
|
|
10234
|
+
l1: "#ABB6C6",
|
|
10235
|
+
l2: "#C8CBCF",
|
|
10236
|
+
l3: "#37ABBD",
|
|
10237
|
+
l4: "#45C1D4",
|
|
10238
|
+
l5: "#5CC4E0"
|
|
10239
|
+
};
|
|
10240
|
+
const TINT = {
|
|
10241
|
+
gold: {
|
|
10242
|
+
fill: "#F6EBCC",
|
|
10243
|
+
stroke: "#7A5F16",
|
|
10244
|
+
text: "#4A3A0E"
|
|
10245
|
+
},
|
|
10246
|
+
aurora: {
|
|
10247
|
+
fill: "#D9F0F4",
|
|
10248
|
+
stroke: "#1F6F7C",
|
|
10249
|
+
text: "#10353C"
|
|
10250
|
+
},
|
|
10251
|
+
error: {
|
|
10252
|
+
fill: "#FADEDD",
|
|
10253
|
+
stroke: "#A83A35",
|
|
10254
|
+
text: "#4E1B19"
|
|
10255
|
+
},
|
|
10256
|
+
/** The unmeasured / unknown state. Neutral, never the error tint. */
|
|
10257
|
+
neutral: {
|
|
10258
|
+
fill: "#E4E7EB",
|
|
10259
|
+
stroke: "#5C646E",
|
|
10260
|
+
text: "#262B31"
|
|
10261
|
+
},
|
|
10262
|
+
ok: {
|
|
10263
|
+
fill: "#DCF0E4",
|
|
10264
|
+
stroke: "#276B45",
|
|
10265
|
+
text: "#163A26"
|
|
10266
|
+
}
|
|
10267
|
+
};
|
|
10268
|
+
/**
|
|
10269
|
+
* The score bands as the badge Mjölnir itself generates renders them.
|
|
10270
|
+
*
|
|
10271
|
+
* These are DEEPER than the score tokens on purpose, and it is not a
|
|
10272
|
+
* style preference: shields.io sets the message text in white and gives
|
|
10273
|
+
* you no say in it. `score.forged` (#F4DC9C) under white text measures
|
|
10274
|
+
* 1.35:1 — an unreadable badge, shipped to look on-brand. The brand's
|
|
10275
|
+
* own deep steps put every band between 4.9 and 6.3:1.
|
|
10276
|
+
*
|
|
10277
|
+
* What they replace was worse than off-brand, it was wrong:
|
|
10278
|
+
*
|
|
10279
|
+
* band was rendered as white-on now white-on
|
|
10280
|
+
* ────────────────────────────────────────────────────────────────────────────
|
|
10281
|
+
* 0-49 `red` #dd4343 4.24 #A83A35 6.32
|
|
10282
|
+
* 50-79 `yellow` #d8b800 1.95 #7A5F16 6.04
|
|
10283
|
+
* 80-99 `important` #ea7233 ORANGE 3.02 #1F6F7C 5.80
|
|
10284
|
+
* 100 `success` #44bb00 GREEN 2.51 #8A6D1E 4.90
|
|
10285
|
+
* unmeasured `lightgrey` #939393 3.07 #5C646E 5.99
|
|
10286
|
+
*
|
|
10287
|
+
* Two of those were defects, not preferences. `success` is green, and
|
|
10288
|
+
* green is not a score colour here — a 100 badge said "your software is
|
|
10289
|
+
* fine", which is the one claim this product refuses to make. And
|
|
10290
|
+
* `important` is ORANGE, not the blue-family colour the code's own
|
|
10291
|
+
* comment claimed for eight releases: every WORTHY badge ever rendered
|
|
10292
|
+
* showed the trusted band in a warning colour. Nobody had resolved a
|
|
10293
|
+
* shields name to a value and looked.
|
|
10294
|
+
*
|
|
10295
|
+
* Values are hex without `#`, the form shields.io's endpoint takes.
|
|
10296
|
+
* `A83A35`, `7A5F16`, `1F6F7C` and `5C646E` are the `TINT` strokes —
|
|
10297
|
+
* the same deep steps the mermaid diagrams use, for the same reason.
|
|
10298
|
+
* `8A6D1E` is the gold the brand document already named as the light
|
|
10299
|
+
* FORGED gradient's start.
|
|
10300
|
+
*/
|
|
10301
|
+
const BADGE_BAND = {
|
|
10302
|
+
critical: "A83A35",
|
|
10303
|
+
warning: "7A5F16",
|
|
10304
|
+
trusted: "1F6F7C",
|
|
10305
|
+
forged: "8A6D1E",
|
|
10306
|
+
unmeasured: "5C646E"
|
|
10307
|
+
};
|
|
10308
|
+
SURFACE.ink950, SURFACE.ink900, SURFACE.ink850, SURFACE.ink800, BRAND.steel, BRAND.steelDim, BRAND.gold, BRAND.goldBright, BRAND.goldHot, BRAND.aurora, BRAND.auroraBright, BRAND.auroraCyan;
|
|
10309
|
+
SCORE.trusted, SCORE.forged, SCORE.warning, SCORE.critical, STATUS.info, TEXT.onGold, STATUS.ok, EVIDENCE.e0, EVIDENCE.e1, EVIDENCE.e2, TRUST.l0, TRUST.l1, TRUST.l2, TRUST.l3, TRUST.l4, TRUST.l5;
|
|
10310
|
+
//#endregion
|
|
10080
10311
|
//#region src/reporter/score-state.ts
|
|
10081
10312
|
const HEADLINES = {
|
|
10082
10313
|
critical: "The hammer is cracked — {n} findings break its edge.",
|
|
@@ -10153,11 +10384,11 @@ function headlineFor(state, findings) {
|
|
|
10153
10384
|
* Respects NO_COLOR and non-TTY via `palette(isTTY)` — every renderer
|
|
10154
10385
|
* receives a palette and never touches process.env directly.
|
|
10155
10386
|
*
|
|
10156
|
-
* Palette:
|
|
10157
|
-
*
|
|
10158
|
-
*
|
|
10159
|
-
*
|
|
10160
|
-
*
|
|
10387
|
+
* Palette: resolved from `src/brand/tokens.ts`, the single source of
|
|
10388
|
+
* brand truth — this file defines no colour of its own. Emitted as
|
|
10389
|
+
* 24-bit truecolor SGR (`38;2;r;g;b`), which every modern terminal
|
|
10390
|
+
* renders and which `shouldColorize` already gates behind
|
|
10391
|
+
* TTY + !NO_COLOR.
|
|
10161
10392
|
*
|
|
10162
10393
|
* Symbols always accompany color (color-blind safe, R11).
|
|
10163
10394
|
*
|
|
@@ -10170,53 +10401,42 @@ function headlineFor(state, findings) {
|
|
|
10170
10401
|
* flag so output degrades to plain characters on cmd.exe/legacy
|
|
10171
10402
|
* consoles that mangle box-drawing glyphs and emoji.
|
|
10172
10403
|
*/
|
|
10173
|
-
/**
|
|
10404
|
+
/**
|
|
10405
|
+
* `"#RRGGBB"` → the `[r, g, b]` triplet the SGR truecolor emitter needs.
|
|
10406
|
+
* Lives here rather than in `src/brand/tokens.ts`, which is pure data:
|
|
10407
|
+
* each surface converts the canonical hex into its own colour space.
|
|
10408
|
+
*/
|
|
10409
|
+
function fromHex(hex) {
|
|
10410
|
+
const n = Number.parseInt(hex.slice(1), 16);
|
|
10411
|
+
return [
|
|
10412
|
+
n >> 16 & 255,
|
|
10413
|
+
n >> 8 & 255,
|
|
10414
|
+
n & 255
|
|
10415
|
+
];
|
|
10416
|
+
}
|
|
10417
|
+
/**
|
|
10418
|
+
* The terminal palette, 24-bit truecolor, resolved from
|
|
10419
|
+
* `src/brand/tokens.ts` — the single source of brand truth. Nothing in
|
|
10420
|
+
* this file may name a hex value of its own, and `brand-doctor` rule 2
|
|
10421
|
+
* fails if it tries.
|
|
10422
|
+
*
|
|
10423
|
+
* Every role is now the canonical token. Six of them used to be the
|
|
10424
|
+
* terminal's own: a frost-steel blue for headers, a teal for info, an
|
|
10425
|
+
* amber for warnings, a rune-red for errors, a bone white for bold and a
|
|
10426
|
+
* weathered stone for dim — a second palette for one product. The
|
|
10427
|
+
* rune-red also failed WCAG AA at 4.36:1 on this terminal's own
|
|
10428
|
+
* background; `STATUS.error` on the canonical ground is 6.20:1.
|
|
10429
|
+
*/
|
|
10174
10430
|
const NORSE = {
|
|
10175
|
-
ok:
|
|
10176
|
-
|
|
10177
|
-
|
|
10178
|
-
|
|
10179
|
-
|
|
10180
|
-
|
|
10181
|
-
|
|
10182
|
-
|
|
10183
|
-
|
|
10184
|
-
],
|
|
10185
|
-
accent: [
|
|
10186
|
-
138,
|
|
10187
|
-
180,
|
|
10188
|
-
216
|
|
10189
|
-
],
|
|
10190
|
-
warning: [
|
|
10191
|
-
224,
|
|
10192
|
-
165,
|
|
10193
|
-
38
|
|
10194
|
-
],
|
|
10195
|
-
error: [
|
|
10196
|
-
208,
|
|
10197
|
-
69,
|
|
10198
|
-
59
|
|
10199
|
-
],
|
|
10200
|
-
trusted: [
|
|
10201
|
-
92,
|
|
10202
|
-
196,
|
|
10203
|
-
224
|
|
10204
|
-
],
|
|
10205
|
-
forged: [
|
|
10206
|
-
244,
|
|
10207
|
-
220,
|
|
10208
|
-
156
|
|
10209
|
-
],
|
|
10210
|
-
bold: [
|
|
10211
|
-
237,
|
|
10212
|
-
230,
|
|
10213
|
-
214
|
|
10214
|
-
],
|
|
10215
|
-
dim: [
|
|
10216
|
-
124,
|
|
10217
|
-
133,
|
|
10218
|
-
144
|
|
10219
|
-
]
|
|
10431
|
+
ok: fromHex(STATUS.ok),
|
|
10432
|
+
info: fromHex(BRAND.aurora),
|
|
10433
|
+
accent: fromHex(BRAND.steel),
|
|
10434
|
+
warning: fromHex(STATUS.warning),
|
|
10435
|
+
error: fromHex(STATUS.error),
|
|
10436
|
+
trusted: fromHex(SCORE.trusted),
|
|
10437
|
+
forged: fromHex(SCORE.forged),
|
|
10438
|
+
bold: fromHex(TEXT.primary),
|
|
10439
|
+
dim: fromHex(TEXT.muted)
|
|
10220
10440
|
};
|
|
10221
10441
|
const on = {
|
|
10222
10442
|
ok: rgb(NORSE.ok),
|
|
@@ -12858,16 +13078,47 @@ function countBySeverity(result) {
|
|
|
12858
13078
|
}
|
|
12859
13079
|
return counts;
|
|
12860
13080
|
}
|
|
13081
|
+
EVIDENCE.e0, EVIDENCE.e1, EVIDENCE.e2;
|
|
13082
|
+
const RUNG_MEANINGS = [
|
|
13083
|
+
"observation only",
|
|
13084
|
+
"heuristic static",
|
|
13085
|
+
"deterministic static",
|
|
13086
|
+
"the finding's file executed",
|
|
13087
|
+
"the finding's test executed",
|
|
13088
|
+
"the run verdict corroborates"
|
|
13089
|
+
];
|
|
13090
|
+
const RUNG_COLORS = [
|
|
13091
|
+
TRUST.l0,
|
|
13092
|
+
TRUST.l1,
|
|
13093
|
+
TRUST.l2,
|
|
13094
|
+
TRUST.l3,
|
|
13095
|
+
TRUST.l4,
|
|
13096
|
+
TRUST.l5
|
|
13097
|
+
];
|
|
13098
|
+
const TRUST_RUNGS = RUNG_MEANINGS.map((meaning, i) => ({
|
|
13099
|
+
level: `L${i}`,
|
|
13100
|
+
meaning,
|
|
13101
|
+
runtime: i >= 3,
|
|
13102
|
+
color: RUNG_COLORS[i]
|
|
13103
|
+
}));
|
|
12861
13104
|
//#endregion
|
|
12862
13105
|
//#region src/reporter/trust-report.ts
|
|
12863
|
-
|
|
12864
|
-
|
|
12865
|
-
|
|
12866
|
-
|
|
12867
|
-
|
|
12868
|
-
|
|
12869
|
-
|
|
12870
|
-
|
|
13106
|
+
/**
|
|
13107
|
+
* The rung labels, built from `src/brand/symbols.ts` rather than typed
|
|
13108
|
+
* again here.
|
|
13109
|
+
*
|
|
13110
|
+
* They had drifted the moment there were two copies: this file said
|
|
13111
|
+
* "file executed" and "run corroborates defect" where the symbol module,
|
|
13112
|
+
* the architecture diagram and the website's ladder all said "the
|
|
13113
|
+
* finding's file executed" and "the run verdict corroborates". Small
|
|
13114
|
+
* enough that nobody would notice, and exactly the kind of divergence
|
|
13115
|
+
* that makes a reader wonder whether two surfaces mean the same thing.
|
|
13116
|
+
*
|
|
13117
|
+
* The runtime marker is not decoration either: L3 and above cannot be
|
|
13118
|
+
* reached without a real run report, and the label says so wherever the
|
|
13119
|
+
* ladder is not drawn to show it.
|
|
13120
|
+
*/
|
|
13121
|
+
const TRUST_LABELS = Object.fromEntries(TRUST_RUNGS.map((r) => [r.level, `${r.level} · ${r.meaning}${r.runtime ? " · runtime" : ""}`]));
|
|
12871
13122
|
function pct$1(v) {
|
|
12872
13123
|
return `${Math.round(v * 100)}%`;
|
|
12873
13124
|
}
|
|
@@ -13003,7 +13254,7 @@ function renderSarif(result, repoRootUri) {
|
|
|
13003
13254
|
tool: { driver: {
|
|
13004
13255
|
name: "Mjölnir",
|
|
13005
13256
|
informationUri: "https://github.com/Sergey-Bar/Mjolnir",
|
|
13006
|
-
version: "1.0.
|
|
13257
|
+
version: "1.0.4",
|
|
13007
13258
|
rules: [...rules.values()].map((r) => {
|
|
13008
13259
|
const meta = RULES.find((x) => x.id === r.id);
|
|
13009
13260
|
return {
|
|
@@ -13132,6 +13383,22 @@ function renderCodeQuality(result) {
|
|
|
13132
13383
|
}
|
|
13133
13384
|
//#endregion
|
|
13134
13385
|
//#region src/reporter/mermaid.ts
|
|
13386
|
+
/**
|
|
13387
|
+
* `--format mermaid` — test-architecture diagram (Sprint 9 Task 38,
|
|
13388
|
+
* Master-Stabilization-Plan.md). Ranked first among the delight
|
|
13389
|
+
* features because it is genuinely useful to QA leads presenting scan
|
|
13390
|
+
* results to stakeholders, not merely decorative — a flowchart of
|
|
13391
|
+
* detected frameworks → rule categories → severity buckets, so gaps
|
|
13392
|
+
* are visible at a glance in a format that pastes directly into a
|
|
13393
|
+
* GitHub/GitLab markdown comment or a slide (Mermaid renders natively
|
|
13394
|
+
* in both).
|
|
13395
|
+
*
|
|
13396
|
+
* Score-neutral (Sprint 9's own DoD line): this is a pure alternate
|
|
13397
|
+
* rendering of the exact same ScanResult every other format uses — it
|
|
13398
|
+
* changes no scoring, no exit code, no JSON contract field. Output is
|
|
13399
|
+
* fully deterministic: every collection is sorted before rendering, so
|
|
13400
|
+
* the same ScanResult always produces byte-identical Mermaid source.
|
|
13401
|
+
*/
|
|
13135
13402
|
function sanitizeId(raw) {
|
|
13136
13403
|
return raw.replace(/[^a-z0-9]/gi, "_");
|
|
13137
13404
|
}
|
|
@@ -13139,6 +13406,14 @@ function sanitizeId(raw) {
|
|
|
13139
13406
|
function escapeLabel(text) {
|
|
13140
13407
|
return text.replaceAll("\"", """).replaceAll("\n", " ");
|
|
13141
13408
|
}
|
|
13409
|
+
/**
|
|
13410
|
+
* One Mermaid `classDef` line from one brand tint. Every colour this
|
|
13411
|
+
* renderer emits comes from `src/brand/tokens.ts`; it names none of its
|
|
13412
|
+
* own, and `brand-doctor` rule 6 fails if it starts to.
|
|
13413
|
+
*/
|
|
13414
|
+
function classDef(name, tint) {
|
|
13415
|
+
return ` classDef ${name} fill:${tint.fill},stroke:${tint.stroke},color:${tint.text};`;
|
|
13416
|
+
}
|
|
13142
13417
|
function dimensionStyleClass(dim) {
|
|
13143
13418
|
if (dim.score >= 80) return "healthy";
|
|
13144
13419
|
if (dim.score >= 50) return "warn";
|
|
@@ -13157,8 +13432,8 @@ function renderMermaid(result) {
|
|
|
13157
13432
|
if (result.score === null) {
|
|
13158
13433
|
lines.push(` ${rootId} --> NOTESTS["No test files detected"]`);
|
|
13159
13434
|
lines.push("");
|
|
13160
|
-
lines.push("
|
|
13161
|
-
lines.push(" class NOTESTS
|
|
13435
|
+
lines.push(classDef("unknown", TINT.neutral));
|
|
13436
|
+
lines.push(" class NOTESTS unknown;");
|
|
13162
13437
|
return lines.join("\n");
|
|
13163
13438
|
}
|
|
13164
13439
|
const frameworks = [...result.frameworks].sort((a, b) => a.localeCompare(b));
|
|
@@ -13194,10 +13469,11 @@ function renderMermaid(result) {
|
|
|
13194
13469
|
}
|
|
13195
13470
|
}
|
|
13196
13471
|
lines.push("");
|
|
13197
|
-
lines.push("
|
|
13198
|
-
lines.push("
|
|
13199
|
-
lines.push("
|
|
13200
|
-
lines.push("
|
|
13472
|
+
lines.push(classDef("healthy", TINT.ok));
|
|
13473
|
+
lines.push(classDef("warn", TINT.gold));
|
|
13474
|
+
lines.push(classDef("critical", TINT.error));
|
|
13475
|
+
lines.push(classDef("info", TINT.aurora));
|
|
13476
|
+
lines.push(classDef("unknown", TINT.neutral));
|
|
13201
13477
|
for (const { id, cls } of styleAssignments) lines.push(` class ${id} ${cls};`);
|
|
13202
13478
|
return lines.join("\n");
|
|
13203
13479
|
}
|
|
@@ -16469,17 +16745,19 @@ function renderMutationSummary(report) {
|
|
|
16469
16745
|
* retarget fixes the historical threshold drift (the badge used
|
|
16470
16746
|
* ≥90/≥75/≥50 with four bands while the reporter used ≥80/≥50).
|
|
16471
16747
|
*
|
|
16472
|
-
*
|
|
16473
|
-
*
|
|
16474
|
-
*
|
|
16475
|
-
*
|
|
16748
|
+
* They are brand values now, from `BADGE_BAND`, not shields.io's named
|
|
16749
|
+
* colors. That mapping was documented as "trusted → `important`
|
|
16750
|
+
* (blue-family, closest to aurora-cyan)" and was simply untrue:
|
|
16751
|
+
* `important` resolves to #ea7233, which is orange. Every WORTHY badge
|
|
16752
|
+
* rendered the trusted band in a warning colour, and `success` — the
|
|
16753
|
+
* 100 state — rendered green, which is not a score colour here.
|
|
16754
|
+
*
|
|
16755
|
+
* ScoreState remains the truth; the badge is still a peripheral
|
|
16756
|
+
* surface. But peripheral is not the same as unchecked.
|
|
16476
16757
|
*/
|
|
16477
16758
|
function colorFor(score) {
|
|
16478
16759
|
const band = deriveScoreState(score).band;
|
|
16479
|
-
|
|
16480
|
-
if (band === "forged") return "success";
|
|
16481
|
-
if (band === "trusted") return "important";
|
|
16482
|
-
return band === "warning" ? "yellow" : "red";
|
|
16760
|
+
return BADGE_BAND[band];
|
|
16483
16761
|
}
|
|
16484
16762
|
/** Build the shields.io endpoint payload from a scan result. */
|
|
16485
16763
|
function buildBadge(result, commit) {
|
|
@@ -16490,7 +16768,6 @@ function buildBadge(result, commit) {
|
|
|
16490
16768
|
label: "MJÖLNIR",
|
|
16491
16769
|
message: score === null ? "no tests found" : score === 100 && errors === 0 ? "100/100 · forged" : `${score}/100 · ${errors} error${errors === 1 ? "" : "s"}`,
|
|
16492
16770
|
color: colorFor(score),
|
|
16493
|
-
namedLogo: "vitest",
|
|
16494
16771
|
...commit !== void 0 ? { commit } : {}
|
|
16495
16772
|
};
|
|
16496
16773
|
}
|
|
@@ -19434,7 +19711,7 @@ const { runScan, buildUniversalRules, fallbackWorkspace, pathMatchesGlob, isVali
|
|
|
19434
19711
|
* `scripts/sync-sarif-version.cjs` on release and guarded by
|
|
19435
19712
|
* `tests/version-consistency.spec.ts` locally.
|
|
19436
19713
|
*/
|
|
19437
|
-
const CLI_VERSION = "1.0.
|
|
19714
|
+
const CLI_VERSION = "1.0.4";
|
|
19438
19715
|
function parseArgs(argv, onError) {
|
|
19439
19716
|
const args = {
|
|
19440
19717
|
target: ".",
|