memento-mori-jester 0.1.74 → 0.1.75

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 CHANGED
@@ -4,6 +4,12 @@ All notable changes to Memento Mori Jester are tracked here.
4
4
 
5
5
  ## Unreleased
6
6
 
7
+ ## 0.1.75
8
+
9
+ - Added `npm run fixtures:report -- --markdown` for paste-ready fixture coverage snapshots.
10
+ - Added Markdown tables for totals, counts, rule-family slices, preset slices, gaps, quiet-pass coverage, curation-next guidance, and next commands.
11
+ - Updated maintainer docs, release docs, and production-readiness checks for the Markdown fixture report export.
12
+
7
13
  ## 0.1.74
8
14
 
9
15
  - Added six API quiet-pass fixtures, growing the corpus to 208 fixtures.
package/README.md CHANGED
@@ -501,7 +501,7 @@ Use the false-positive template for noisy cautions or blocks. Include `jester su
501
501
 
502
502
  Maintainers can use [docs/MAINTAINER_TRIAGE.md](docs/MAINTAINER_TRIAGE.md) to turn useful false-positive reports into redacted fixtures.
503
503
  Run `npm run fixtures:check` before merging fixture changes; it catches duplicate IDs, missing rule metadata, weak descriptions, unsafe-looking content, and duplicate content.
504
- Run `npm run fixtures:report` to see fixture coverage by rule, rule family, preset slice, kind, verdict, quiet-pass boundaries, feasible pass-case gaps, and curation-next guidance before choosing the next fixture.
504
+ Run `npm run fixtures:report` to see fixture coverage by rule, rule family, preset slice, kind, verdict, quiet-pass boundaries, feasible pass-case gaps, and curation-next guidance before choosing the next fixture. Use `npm run fixtures:report -- --markdown` when you want a paste-ready summary for release notes or GitHub issues.
505
505
 
506
506
  For vulnerabilities, private code exposure, or credential-handling concerns, follow [SECURITY.md](SECURITY.md) instead of opening a public issue with sensitive details.
507
507
 
package/ROADMAP.md CHANGED
@@ -6,6 +6,7 @@ Memento Mori Jester is usable today as a CLI, MCP server, GitHub Action, and git
6
6
 
7
7
  ## Recently Shipped
8
8
 
9
+ - Markdown fixture report export in v0.1.75 for paste-ready coverage snapshots in release notes, GitHub issues, and maintainer updates.
9
10
  - API fixture curation in v0.1.74, adding six quiet-pass examples for schema parsing, query-builder filters, enabled rate limiting, read-only Prisma migration diffs, signed-webhook docs, and OpenAPI schema docs.
10
11
  - Web/AI fixture curation in v0.1.73, adding six quiet-pass examples for safe text rendering, allowlisted target paths, public analytics IDs, model-check commands, tool allowlist checks, and public model-name config.
11
12
  - Python/security fixture curation in v0.1.72, adding six quiet-pass examples for Bandit, pip-audit, coverage/pytest, Trivy, npm audit, and TLS verification-enabled diffs.
@@ -65,7 +66,7 @@ Memento Mori Jester is usable today as a CLI, MCP server, GitHub Action, and git
65
66
 
66
67
  - Collect real-world reports for the next lowest-count preset slices: python, security, web, then AI.
67
68
  - Add more framework-specific false-positive examples from real reports so tuning guidance keeps getting sharper.
68
- - Add a Markdown export for fixture reports so maintainers can paste coverage snapshots into issues or release notes.
69
+ - Add a promo/share kit with X post copy, still images, and a simple sharing checklist.
69
70
 
70
71
  ## Quality And Safety
71
72
 
package/docs/DEMO.md CHANGED
@@ -355,7 +355,7 @@ Preset packs:
355
355
 
356
356
  The fixture suite in `examples/fixtures/preset-review-cases.json` captures small real-usage examples with expected `pass`, `caution`, or `block` verdicts. It also includes matched-pass examples for low-severity rules, quiet-pass `absentRuleIds` examples that prove noisy rules stay silent for safe near-misses, stack-specific coverage for every built-in preset, quiet-pass boundaries across built-in, structural, custom, and preset/config-derived rules, second firing examples for preset blocked-command rules, second examples for AI/API, framework custom, built-in, and configured sensitive-domain rules, AI tool-dispatch examples with safe allowlist/schema boundaries, and real-world low-count preset examples across node, python, web, infra, AI, and security slices. Recent quiet-pass examples cover typechecks, prebuild scripts, mypy, dataclass parsing, CodeQL, Dependabot limits, form validation, accessibility copy, read-only Kubernetes inspection, Docker disk usage, Terraform linting, public-IP hardening changes, npm audit/outdated/ci, development-mode Node commands, package export maps, workspace test scripts, Bandit, pip-audit, coverage/pytest, Trivy filesystem scans, npm audit, TLS verification-enabled diffs, safe text rendering, allowlisted target paths, public analytics IDs, model-check commands, tool allowlist checks, public model-name config, API schema parsing, query-builder filters, enabled rate limiting, read-only Prisma migration diffs, signed-webhook docs, and OpenAPI schema docs. These examples are run by `npm test`, so preset tuning changes stay visible.
357
357
 
358
- Maintainers can run `npm run fixtures:report` to see coverage by verdict, kind, preset, rule family, and preset slice. The report also includes a `Curation next` section that points at the next useful fixture batch, such as thin rules, feasible pass-case evidence, rule-family gaps, or lower-count presets.
358
+ Maintainers can run `npm run fixtures:report` to see coverage by verdict, kind, preset, rule family, and preset slice. The report also includes a `Curation next` section that points at the next useful fixture batch, such as thin rules, feasible pass-case evidence, rule-family gaps, or lower-count presets. Use `npm run fixtures:report -- --markdown` for a paste-ready version of the same snapshot.
359
359
 
360
360
  Maintainers can use `docs/MAINTAINER_TRIAGE.md` to turn useful false-positive reports into redacted fixture cases.
361
361
 
@@ -77,13 +77,14 @@ npm.cmd test
77
77
  npm.cmd run fixtures:check
78
78
  npm.cmd run fixtures:report
79
79
  npm.cmd run fixtures:report -- --json
80
+ npm.cmd run fixtures:report -- --markdown
80
81
  node .\dist\cli.js tune <rule-id>
81
82
  node .\dist\cli.js tune <rule-id> --json
82
83
  node .\dist\cli.js tune coverage
83
84
  ```
84
85
 
85
86
  5. Fix any duplicate IDs, missing expected rule metadata, weak descriptions, unsafe content, or duplicate content reported by `fixtures:check`.
86
- 6. Use `fixtures:report` to check whether the change improves feasible pass-case, quiet-pass, preset, kind, rule-family, or verdict coverage. Start with the report's `Curation next` section when deciding which fixture batch to add first.
87
+ 6. Use `fixtures:report` to check whether the change improves feasible pass-case, quiet-pass, preset, kind, rule-family, or verdict coverage. Start with the report's `Curation next` section when deciding which fixture batch to add first, and use `fixtures:report -- --markdown` when you need a paste-ready snapshot for an issue or release note.
87
88
  7. Check whether support/confidence changed in the expected direction.
88
89
  8. If the fixture changes verdict behavior, mention the exact rule impact in `CHANGELOG.md`.
89
90
 
@@ -53,7 +53,7 @@ This checklist defines what "production grade" means for Memento Mori Jester rig
53
53
  - `SECURITY.md` routes vulnerability reports away from public issues and asks for redacted diagnostics.
54
54
  - `docs/MAINTAINER_TRIAGE.md` explains how to turn useful false-positive reports into fixture coverage before changing rule logic.
55
55
  - `npm run fixtures:check` validates fixture IDs, metadata, unsafe-looking content, duplicate content, and explicit expected/absent rule intent.
56
- - `npm run fixtures:report` shows fixture coverage by rule, rule family, preset slice, kind, verdict, quiet-pass rule boundaries, and feasible pass-case gaps so maintainers can pick the next fixture target.
56
+ - `npm run fixtures:report` shows fixture coverage by rule, rule family, preset slice, kind, verdict, quiet-pass rule boundaries, and feasible pass-case gaps so maintainers can pick the next fixture target; `npm run fixtures:report -- --markdown` produces a paste-ready maintainer snapshot.
57
57
  - npm publish has a manual workflow fallback, but the normal release path is tag-driven trusted publishing.
58
58
 
59
59
  ## Static Guard
package/docs/RELEASE.md CHANGED
@@ -11,6 +11,7 @@ npm.cmd run production:check
11
11
  npm.cmd run fixtures:check
12
12
  npm.cmd run fixtures:report
13
13
  npm.cmd run fixtures:report -- --json
14
+ npm.cmd run fixtures:report -- --markdown
14
15
  npm.cmd run pack:dry
15
16
  git diff --check
16
17
  ```
@@ -0,0 +1,42 @@
1
+ # Memento Mori Jester v0.1.75
2
+
3
+ This release adds a Markdown export for the fixture coverage report so maintainers can paste stable quality snapshots into release notes, GitHub issues, and project updates. Review behavior is unchanged.
4
+
5
+ ## What Changed
6
+
7
+ - Added `npm run fixtures:report -- --markdown`.
8
+ - Added Markdown sections for:
9
+ - summary totals.
10
+ - verdict, kind, and preset counts.
11
+ - rule-family slices.
12
+ - preset slices.
13
+ - gap sections.
14
+ - quiet-pass rule coverage.
15
+ - quiet-pass fixture samples.
16
+ - curation-next guidance.
17
+ - next commands.
18
+ - Added a guard that rejects combining `--json` and `--markdown`.
19
+ - Updated maintainer docs, release docs, demo docs, README support notes, and production-readiness checks.
20
+
21
+ ## Public Interface
22
+
23
+ - New maintainer script mode: `npm run fixtures:report -- --markdown`.
24
+ - No CLI command changes.
25
+ - No config schema changes.
26
+ - No rule matching, scoring, or verdict behavior changes.
27
+ - No MCP, playground, GitHub Action, or npm publishing changes.
28
+
29
+ ## Release Validation
30
+
31
+ ```powershell
32
+ npm.cmd test
33
+ npm.cmd run demo:svg:check
34
+ npm.cmd run fixtures:report
35
+ npm.cmd run fixtures:report -- --json
36
+ npm.cmd run fixtures:report -- --markdown
37
+ npm.cmd run pack:dry
38
+ git diff --check
39
+ node .\dist\cli.js tune coverage --no-config
40
+ node .\dist\cli.js tune risky-domain --json --no-config
41
+ git diff | node .\dist\cli.js diff --fail-on block --subject "v0.1.75 markdown fixture report export"
42
+ ```
@@ -38,6 +38,7 @@ npm.cmd test
38
38
  npm.cmd run fixtures:check
39
39
  npm.cmd run fixtures:report
40
40
  npm.cmd run fixtures:report -- --json
41
+ npm.cmd run fixtures:report -- --markdown
41
42
  ```
42
43
 
43
44
  For one-off manual review, paste a fixture `content` value into:
@@ -63,4 +64,4 @@ Do not add secrets, private code, customer data, complete logs, or machine-speci
63
64
 
64
65
  `npm run fixtures:report` summarizes coverage by rule, rule family, preset slice, review kind, verdict, and quiet-pass rule boundaries. Use it to find rules without pass-case coverage, pass-eligible rules without pass-case coverage, rules without quiet-pass coverage, thin rule coverage, preset/kind gaps, quiet pass fixtures, and the next curation target.
65
66
 
66
- The `Curation next` section is a maintainer shortcut: start there when deciding whether the next fixture batch should focus on thin rules, feasible pass-case evidence, a specific rule family, or lower-count presets. The `--json` output includes the same `ruleFamilySlices`, `presetSlices`, `passEligibleRulesWithoutPassCases`, and `curationNext` fields for scripts.
67
+ The `Curation next` section is a maintainer shortcut: start there when deciding whether the next fixture batch should focus on thin rules, feasible pass-case evidence, a specific rule family, or lower-count presets. The `--json` output includes the same `ruleFamilySlices`, `presetSlices`, `passEligibleRulesWithoutPassCases`, and `curationNext` fields for scripts. The `--markdown` output renders the same snapshot as paste-ready Markdown tables for release notes, GitHub issues, or maintainer updates.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "memento-mori-jester",
3
- "version": "0.1.74",
3
+ "version": "0.1.75",
4
4
  "description": "A local court-jester sidecar for AI coding agents: review plans, commands, diffs, and final claims before they get too pleased with themselves.",
5
5
  "type": "module",
6
6
  "repository": {
@@ -94,6 +94,7 @@ requireText("README.md", /false-positive/i, "false-positive support guidance");
94
94
  requireText("README.md", /MAINTAINER_TRIAGE\.md/, "maintainer triage guide link");
95
95
  requireText("README.md", /fixtures:check/, "fixture authoring check guidance");
96
96
  requireText("README.md", /fixtures:report/, "fixture coverage report guidance");
97
+ requireText("README.md", /fixtures:report -- --markdown/, "Markdown fixture report guidance");
97
98
  requireText("README.md", /License: PolyForm Noncommercial/, "the noncommercial license badge");
98
99
  requireText("docs/PRODUCTION_READINESS.md", /npm package/i, "npm package readiness");
99
100
  requireText("docs/PRODUCTION_READINESS.md", /GitHub Action/i, "GitHub Action readiness");
@@ -106,6 +107,7 @@ requireText("docs/PRODUCTION_READINESS.md", /issue templates/i, "issue template
106
107
  requireText("docs/PRODUCTION_READINESS.md", /MAINTAINER_TRIAGE\.md/, "maintainer triage readiness");
107
108
  requireText("docs/PRODUCTION_READINESS.md", /fixtures:check/, "fixture authoring check readiness");
108
109
  requireText("docs/PRODUCTION_READINESS.md", /fixtures:report/, "fixture coverage report readiness");
110
+ requireText("docs/PRODUCTION_READINESS.md", /fixtures:report -- --markdown/, "Markdown fixture report readiness");
109
111
  requireText("docs/PRODUCTION_READINESS.md", /quiet-pass/, "quiet-pass fixture readiness");
110
112
  requireText("docs/CLI.md", /jester doctor --json/, "doctor JSON CLI docs");
111
113
  requireText("docs/CLI.md", /quiet-pass fixture/, "quiet-pass fixture CLI docs");
@@ -118,6 +120,7 @@ requireText("examples/fixtures/README.md", /MAINTAINER_TRIAGE\.md/, "maintainer
118
120
  requireText("examples/fixtures/README.md", /Adding A Fixture From A Report/, "fixture report conversion guidance");
119
121
  requireText("examples/fixtures/README.md", /fixtures:check/, "fixture authoring check guidance");
120
122
  requireText("examples/fixtures/README.md", /fixtures:report/, "fixture coverage report guidance");
123
+ requireText("examples/fixtures/README.md", /fixtures:report -- --markdown/, "Markdown fixture report guidance");
121
124
  requireText("scripts/check-fixtures.mjs", /duplicated/, "duplicate fixture id check");
122
125
  requireText("scripts/check-fixtures.mjs", /unsafeContentPatterns/, "unsafe fixture content checks");
123
126
  forbidText("scripts/check-fixtures.mjs", /src\/config\.ts|src\/types\.ts/, "source-only fixture validator dependencies");
@@ -125,6 +128,7 @@ requireText("scripts/report-fixtures.mjs", /rulesWithoutPassCases/, "rules witho
125
128
  requireText("scripts/report-fixtures.mjs", /rulesWithoutQuietPassCoverage/, "rules without quiet-pass coverage report");
126
129
  requireText("scripts/report-fixtures.mjs", /quietPassRuleCoverage/, "quiet-pass rule coverage report");
127
130
  requireText("scripts/report-fixtures.mjs", /presetKindGaps/, "preset and kind gap report");
131
+ requireText("scripts/report-fixtures.mjs", /--markdown/, "Markdown fixture report output");
128
132
  forbidText("scripts/report-fixtures.mjs", /src\/config\.ts|src\/types\.ts/, "source-only fixture report dependencies");
129
133
  requireText("package.json", /"fixtures:check": "node scripts\/check-fixtures\.mjs"/, "fixture authoring check script");
130
134
  requireText("package.json", /"fixtures:report": "node scripts\/report-fixtures\.mjs"/, "fixture coverage report script");
@@ -33,6 +33,12 @@ const ruleFamilyOrder = [
33
33
 
34
34
  const args = new Set(process.argv.slice(2));
35
35
  const json = args.has("--json");
36
+ const markdown = args.has("--markdown");
37
+
38
+ if (json && markdown) {
39
+ process.stderr.write("Use only one output format: --json or --markdown.\n");
40
+ process.exit(1);
41
+ }
36
42
 
37
43
  function read(path) {
38
44
  return readFileSync(join(root, path), "utf8");
@@ -55,6 +61,8 @@ const report = buildFixtureReport(fixtures);
55
61
 
56
62
  if (json) {
57
63
  process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
64
+ } else if (markdown) {
65
+ process.stdout.write(renderFixtureReportMarkdown(report));
58
66
  } else {
59
67
  process.stdout.write(renderFixtureReport(report));
60
68
  }
@@ -271,6 +279,91 @@ function renderFixtureReport(report) {
271
279
  return `${lines.join("\n")}\n`;
272
280
  }
273
281
 
282
+ function renderFixtureReportMarkdown(report) {
283
+ const lines = [
284
+ "# Fixture Coverage Report",
285
+ "",
286
+ "Generated from `examples/fixtures/preset-review-cases.json`.",
287
+ "",
288
+ "## Summary",
289
+ "",
290
+ "| Metric | Value |",
291
+ "| --- | ---: |",
292
+ `| Fixtures | ${report.totalFixtures} |`,
293
+ `| Weighted fixtures | ${report.totalWeight} |`,
294
+ `| Edge-case fixtures | ${report.edgeCaseFixtures} |`,
295
+ `| Rules covered by expectedRuleIds | ${report.rules.length} |`,
296
+ "",
297
+ "## Counts",
298
+ "",
299
+ "### By Verdict",
300
+ "",
301
+ ...formatMarkdownCountTable("Verdict", report.byVerdict),
302
+ "",
303
+ "### By Kind",
304
+ "",
305
+ ...formatMarkdownCountTable("Kind", report.byKind),
306
+ "",
307
+ "### By Preset",
308
+ "",
309
+ ...formatMarkdownCountTable("Preset", report.byPreset),
310
+ "",
311
+ "## Rule Family Slices",
312
+ "",
313
+ ...formatMarkdownRuleFamilyTable(report.ruleFamilySlices),
314
+ "",
315
+ "## Preset Slices",
316
+ "",
317
+ ...formatMarkdownPresetTable(report.presetSlices),
318
+ "",
319
+ "## Gaps",
320
+ "",
321
+ "### Rules Without Pass-Case Coverage",
322
+ "",
323
+ ...formatMarkdownRuleGapList(report.gaps.rulesWithoutPassCases),
324
+ "",
325
+ "### Pass-Eligible Rules Without Pass-Case Coverage",
326
+ "",
327
+ ...formatMarkdownRuleGapList(report.gaps.passEligibleRulesWithoutPassCases),
328
+ "",
329
+ "### Rules Without Quiet-Pass Coverage",
330
+ "",
331
+ ...formatMarkdownRuleGapList(report.gaps.rulesWithoutQuietPassCoverage),
332
+ "",
333
+ "### Thin Rule Coverage",
334
+ "",
335
+ ...formatMarkdownRuleGapList(report.gaps.thinRuleCoverage),
336
+ "",
337
+ "### Preset/Kind Gaps",
338
+ "",
339
+ ...formatMarkdownPresetKindGaps(report.gaps.presetKindGaps),
340
+ "",
341
+ "## Quiet-Pass Rule Coverage",
342
+ "",
343
+ ...formatMarkdownQuietPassTable(report.gaps.quietPassRuleCoverage),
344
+ "",
345
+ "## Quiet-Pass Fixture Samples",
346
+ "",
347
+ ...formatMarkdownFixtureSamples(report.gaps.quietPassFixtures),
348
+ "",
349
+ "## Curation Next",
350
+ "",
351
+ ...formatMarkdownCurationNext(report.curationNext),
352
+ "",
353
+ "## Next Commands",
354
+ "",
355
+ "```powershell",
356
+ "npm run fixtures:check",
357
+ "npm run fixtures:report",
358
+ "npm run fixtures:report -- --json",
359
+ "npm run fixtures:report -- --markdown",
360
+ "node .\\dist\\cli.js tune coverage",
361
+ "```"
362
+ ];
363
+
364
+ return `${lines.join("\n")}\n`;
365
+ }
366
+
274
367
  function createRuleEntry(ruleId) {
275
368
  return {
276
369
  ruleId,
@@ -575,6 +668,141 @@ function formatCounts(counts) {
575
668
  .join(", ");
576
669
  }
577
670
 
671
+ function formatMarkdownCountTable(label, counts) {
672
+ return [
673
+ `| ${label} | Count |`,
674
+ "| --- | ---: |",
675
+ ...Object.entries(counts).map(([key, value]) => `| ${markdownCell(key)} | ${value} |`)
676
+ ];
677
+ }
678
+
679
+ function formatMarkdownRuleFamilyTable(entries) {
680
+ if (entries.length === 0) {
681
+ return ["None."];
682
+ }
683
+
684
+ return [
685
+ "| Family | Rules | Fixture Refs | Pass | Caution | Block | Quiet Pass | Thin |",
686
+ "| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: |",
687
+ ...entries.map((entry) =>
688
+ [
689
+ markdownCell(entry.family),
690
+ entry.ruleCount,
691
+ entry.fixtureReferences,
692
+ entry.passCases,
693
+ entry.cautionCases,
694
+ entry.blockCases,
695
+ entry.quietPassCases,
696
+ entry.thinRules.length
697
+ ].join(" | ")
698
+ ).map((row) => `| ${row} |`)
699
+ ];
700
+ }
701
+
702
+ function formatMarkdownPresetTable(entries) {
703
+ if (entries.length === 0) {
704
+ return ["None."];
705
+ }
706
+
707
+ return [
708
+ "| Preset | Fixtures | Weight | Pass | Caution | Block | Quiet Pass | Rule Refs | Absent Refs |",
709
+ "| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: |",
710
+ ...entries.map((entry) =>
711
+ [
712
+ markdownCell(entry.preset),
713
+ entry.total,
714
+ entry.weight,
715
+ entry.byVerdict.pass ?? 0,
716
+ entry.byVerdict.caution ?? 0,
717
+ entry.byVerdict.block ?? 0,
718
+ entry.quietPassFixtures,
719
+ entry.expectedRuleReferences,
720
+ entry.quietPassRuleReferences
721
+ ].join(" | ")
722
+ ).map((row) => `| ${row} |`)
723
+ ];
724
+ }
725
+
726
+ function formatMarkdownRuleGapList(entries) {
727
+ if (entries.length === 0) {
728
+ return ["None."];
729
+ }
730
+
731
+ return entries
732
+ .slice(0, 12)
733
+ .map((entry) => `- \`${entry.ruleId}\`: ${entry.total} fixture(s), pass ${entry.passCases}, caution ${entry.cautionCases}, block ${entry.blockCases}, quiet-pass ${entry.quietPassCases}`);
734
+ }
735
+
736
+ function formatMarkdownPresetKindGaps(entries) {
737
+ if (entries.length === 0) {
738
+ return ["None."];
739
+ }
740
+
741
+ return entries.map((entry) => `- \`${entry.preset}\`: ${entry.missingKinds.map((kind) => `\`${kind}\``).join(", ")}`);
742
+ }
743
+
744
+ function formatMarkdownQuietPassTable(entries) {
745
+ if (entries.length === 0) {
746
+ return ["None."];
747
+ }
748
+
749
+ return [
750
+ "| Rule | Quiet-Pass Fixtures | Weight |",
751
+ "| --- | ---: | ---: |",
752
+ ...entries
753
+ .slice(0, 12)
754
+ .map((entry) => `| \`${markdownCell(entry.ruleId)}\` | ${entry.total} | ${entry.weight} |`)
755
+ ];
756
+ }
757
+
758
+ function formatMarkdownFixtureSamples(entries) {
759
+ if (entries.length === 0) {
760
+ return ["None."];
761
+ }
762
+
763
+ return entries
764
+ .slice(0, 8)
765
+ .map((entry) => `- \`${entry.id}\`: ${markdownCell(entry.description)}`);
766
+ }
767
+
768
+ function formatMarkdownCurationNext(entries) {
769
+ if (entries.length === 0) {
770
+ return ["None."];
771
+ }
772
+
773
+ return [
774
+ "| Priority | Area | Count | Details |",
775
+ "| --- | --- | ---: | --- |",
776
+ ...entries.map((entry) =>
777
+ `| ${markdownCell(entry.priority)} | ${markdownCell(entry.area)} | ${entry.count} | ${markdownCell(markdownCurationDetails(entry))} |`
778
+ )
779
+ ];
780
+ }
781
+
782
+ function markdownCurationDetails(entry) {
783
+ if (Array.isArray(entry.ruleIds) && entry.ruleIds.length > 0) {
784
+ return entry.ruleIds.join(", ");
785
+ }
786
+
787
+ if (Array.isArray(entry.details) && entry.details.length > 0) {
788
+ return entry.details.join("; ");
789
+ }
790
+
791
+ if (Array.isArray(entry.families) && entry.families.length > 0) {
792
+ return entry.families.map((family) => `${family.family} ${family.thinRules}`).join(", ");
793
+ }
794
+
795
+ if (Array.isArray(entry.presets) && entry.presets.length > 0) {
796
+ return entry.presets.map((preset) => `${preset.preset} ${preset.total}`).join(", ");
797
+ }
798
+
799
+ return "";
800
+ }
801
+
802
+ function markdownCell(value) {
803
+ return String(value).replace(/\|/g, "\\|").replace(/\r?\n/g, " ");
804
+ }
805
+
578
806
  function formatRuleGaps(entries) {
579
807
  if (entries.length === 0) {
580
808
  return ["- none"];