ma-agents 3.18.0-beta.2 → 3.18.0

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 (168) hide show
  1. package/README.md +2 -2
  2. package/bin/cli.js +29 -32
  3. package/docs/architecture.md +2 -2
  4. package/lib/bmad-cache/cache-manifest.json +0 -12
  5. package/lib/bmad-extension-plugin/.claude-plugin/marketplace.json +1 -1
  6. package/lib/bmad.js +33 -34
  7. package/package.json +1 -1
  8. package/scripts/build-bmad-cache.js +18 -9
  9. package/lib/bmad-cache/gds/.claude-plugin/marketplace.json +0 -56
  10. package/lib/bmad-cache/gds/.markdownlint-cli2.yaml +0 -35
  11. package/lib/bmad-cache/gds/.nvmrc +0 -1
  12. package/lib/bmad-cache/gds/.prettierignore +0 -9
  13. package/lib/bmad-cache/gds/CNAME +0 -1
  14. package/lib/bmad-cache/gds/LICENSE +0 -26
  15. package/lib/bmad-cache/gds/README.md +0 -134
  16. package/lib/bmad-cache/gds/_git_preserved/HEAD +0 -1
  17. package/lib/bmad-cache/gds/_git_preserved/config +0 -13
  18. package/lib/bmad-cache/gds/_git_preserved/description +0 -1
  19. package/lib/bmad-cache/gds/_git_preserved/hooks/applypatch-msg.sample +0 -15
  20. package/lib/bmad-cache/gds/_git_preserved/hooks/commit-msg.sample +0 -74
  21. package/lib/bmad-cache/gds/_git_preserved/hooks/fsmonitor-watchman.sample +0 -168
  22. package/lib/bmad-cache/gds/_git_preserved/hooks/post-update.sample +0 -8
  23. package/lib/bmad-cache/gds/_git_preserved/hooks/pre-applypatch.sample +0 -14
  24. package/lib/bmad-cache/gds/_git_preserved/hooks/pre-commit.sample +0 -49
  25. package/lib/bmad-cache/gds/_git_preserved/hooks/pre-merge-commit.sample +0 -13
  26. package/lib/bmad-cache/gds/_git_preserved/hooks/pre-push.sample +0 -53
  27. package/lib/bmad-cache/gds/_git_preserved/hooks/pre-rebase.sample +0 -169
  28. package/lib/bmad-cache/gds/_git_preserved/hooks/pre-receive.sample +0 -24
  29. package/lib/bmad-cache/gds/_git_preserved/hooks/prepare-commit-msg.sample +0 -42
  30. package/lib/bmad-cache/gds/_git_preserved/hooks/push-to-checkout.sample +0 -78
  31. package/lib/bmad-cache/gds/_git_preserved/hooks/sendemail-validate.sample +0 -77
  32. package/lib/bmad-cache/gds/_git_preserved/hooks/update.sample +0 -128
  33. package/lib/bmad-cache/gds/_git_preserved/index +0 -0
  34. package/lib/bmad-cache/gds/_git_preserved/info/exclude +0 -6
  35. package/lib/bmad-cache/gds/_git_preserved/objects/pack/pack-dcb7c556d9bb6b6b70d2301e094eaac6d7300552.idx +0 -0
  36. package/lib/bmad-cache/gds/_git_preserved/objects/pack/pack-dcb7c556d9bb6b6b70d2301e094eaac6d7300552.pack +0 -0
  37. package/lib/bmad-cache/gds/_git_preserved/objects/pack/pack-dcb7c556d9bb6b6b70d2301e094eaac6d7300552.rev +0 -0
  38. package/lib/bmad-cache/gds/_git_preserved/packed-refs +0 -2
  39. package/lib/bmad-cache/gds/_git_preserved/refs/heads/main +0 -1
  40. package/lib/bmad-cache/gds/_git_preserved/shallow +0 -1
  41. package/lib/bmad-cache/gds/eslint.config.mjs +0 -141
  42. package/lib/bmad-cache/gds/package.json +0 -91
  43. package/lib/bmad-cache/gds/prettier.config.mjs +0 -32
  44. package/lib/bmad-cache/gds/src/agents/gds-agent-game-architect/customize.toml +0 -57
  45. package/lib/bmad-cache/gds/src/agents/gds-agent-game-designer/customize.toml +0 -59
  46. package/lib/bmad-cache/gds/src/agents/gds-agent-game-dev/customize.toml +0 -129
  47. package/lib/bmad-cache/gds/src/agents/gds-agent-game-dev/gametest/qa-index.csv +0 -18
  48. package/lib/bmad-cache/gds/src/agents/gds-agent-game-solo-dev/customize.toml +0 -55
  49. package/lib/bmad-cache/gds/src/agents/gds-agent-tech-writer/customize.toml +0 -65
  50. package/lib/bmad-cache/gds/src/module-help.csv +0 -30
  51. package/lib/bmad-cache/gds/src/module.yaml +0 -113
  52. package/lib/bmad-cache/gds/src/workflows/1-preproduction/gds-brainstorm-game/customize.toml +0 -41
  53. package/lib/bmad-cache/gds/src/workflows/1-preproduction/gds-brainstorm-game/game-brain-methods.csv +0 -26
  54. package/lib/bmad-cache/gds/src/workflows/1-preproduction/gds-create-game-brief/customize.toml +0 -116
  55. package/lib/bmad-cache/gds/src/workflows/1-preproduction/research/gds-domain-research/customize.toml +0 -41
  56. package/lib/bmad-cache/gds/src/workflows/2-design/gds-create-narrative/customize.toml +0 -41
  57. package/lib/bmad-cache/gds/src/workflows/2-design/gds-gdd/assets/game-types.csv +0 -25
  58. package/lib/bmad-cache/gds/src/workflows/2-design/gds-gdd/assets/genre-complexity.csv +0 -26
  59. package/lib/bmad-cache/gds/src/workflows/2-design/gds-gdd/assets/validation-report-template.html +0 -190
  60. package/lib/bmad-cache/gds/src/workflows/2-design/gds-gdd/customize.toml +0 -99
  61. package/lib/bmad-cache/gds/src/workflows/2-design/gds-gdd/scripts/render-validation-html.py +0 -290
  62. package/lib/bmad-cache/gds/src/workflows/2-design/gds-prd/assets/validation-report-template.html +0 -190
  63. package/lib/bmad-cache/gds/src/workflows/2-design/gds-prd/customize.toml +0 -84
  64. package/lib/bmad-cache/gds/src/workflows/2-design/gds-ux/assets/validation-report-template.html +0 -319
  65. package/lib/bmad-cache/gds/src/workflows/2-design/gds-ux/customize.toml +0 -101
  66. package/lib/bmad-cache/gds/src/workflows/3-technical/gds-check-implementation-readiness/customize.toml +0 -41
  67. package/lib/bmad-cache/gds/src/workflows/3-technical/gds-create-epics-and-stories/customize.toml +0 -41
  68. package/lib/bmad-cache/gds/src/workflows/3-technical/gds-game-architecture/architecture-patterns.yaml +0 -508
  69. package/lib/bmad-cache/gds/src/workflows/3-technical/gds-game-architecture/customize.toml +0 -41
  70. package/lib/bmad-cache/gds/src/workflows/3-technical/gds-game-architecture/decision-catalog.yaml +0 -428
  71. package/lib/bmad-cache/gds/src/workflows/3-technical/gds-game-architecture/engine-mcps.yaml +0 -392
  72. package/lib/bmad-cache/gds/src/workflows/3-technical/gds-game-architecture/pattern-categories.csv +0 -13
  73. package/lib/bmad-cache/gds/src/workflows/3-technical/gds-generate-project-context/customize.toml +0 -41
  74. package/lib/bmad-cache/gds/src/workflows/4-production/gds-code-review/customize.toml +0 -41
  75. package/lib/bmad-cache/gds/src/workflows/4-production/gds-correct-course/customize.toml +0 -41
  76. package/lib/bmad-cache/gds/src/workflows/4-production/gds-create-story/customize.toml +0 -41
  77. package/lib/bmad-cache/gds/src/workflows/4-production/gds-dev-story/customize.toml +0 -41
  78. package/lib/bmad-cache/gds/src/workflows/4-production/gds-investigate/customize.toml +0 -62
  79. package/lib/bmad-cache/gds/src/workflows/4-production/gds-retrospective/customize.toml +0 -41
  80. package/lib/bmad-cache/gds/src/workflows/4-production/gds-sprint-planning/customize.toml +0 -41
  81. package/lib/bmad-cache/gds/src/workflows/4-production/gds-sprint-planning/sprint-status-template.yaml +0 -55
  82. package/lib/bmad-cache/gds/src/workflows/4-production/gds-sprint-status/customize.toml +0 -41
  83. package/lib/bmad-cache/gds/src/workflows/gametest/gds-e2e-scaffold/customize.toml +0 -41
  84. package/lib/bmad-cache/gds/src/workflows/gametest/gds-performance-test/customize.toml +0 -41
  85. package/lib/bmad-cache/gds/src/workflows/gametest/gds-playtest-plan/customize.toml +0 -41
  86. package/lib/bmad-cache/gds/src/workflows/gametest/gds-test-automate/customize.toml +0 -41
  87. package/lib/bmad-cache/gds/src/workflows/gametest/gds-test-design/customize.toml +0 -41
  88. package/lib/bmad-cache/gds/src/workflows/gametest/gds-test-framework/customize.toml +0 -41
  89. package/lib/bmad-cache/gds/src/workflows/gametest/gds-test-review/customize.toml +0 -41
  90. package/lib/bmad-cache/gds/src/workflows/gds-document-project/customize.toml +0 -41
  91. package/lib/bmad-cache/gds/src/workflows/gds-document-project/documentation-requirements.csv +0 -12
  92. package/lib/bmad-cache/gds/src/workflows/gds-document-project/templates/project-scan-report-schema.json +0 -160
  93. package/lib/bmad-cache/gds/src/workflows/gds-quick-flow/gds-quick-dev/customize.toml +0 -41
  94. package/lib/bmad-cache/gds/tools/build-docs.mjs +0 -450
  95. package/lib/bmad-cache/gds/website/astro.config.mjs +0 -142
  96. package/lib/bmad-cache/gds/website/public/favicon.ico +0 -0
  97. package/lib/bmad-cache/gds/website/public/img/bmad-dark.png +0 -0
  98. package/lib/bmad-cache/gds/website/public/img/bmad-light.png +0 -0
  99. package/lib/bmad-cache/gds/website/src/components/Banner.astro +0 -71
  100. package/lib/bmad-cache/gds/website/src/components/Header.astro +0 -94
  101. package/lib/bmad-cache/gds/website/src/components/MobileMenuFooter.astro +0 -33
  102. package/lib/bmad-cache/gds/website/src/content/config.ts +0 -6
  103. package/lib/bmad-cache/gds/website/src/content/docs +0 -1
  104. package/lib/bmad-cache/gds/website/src/lib/site-url.mjs +0 -25
  105. package/lib/bmad-cache/gds/website/src/rehype-base-paths.js +0 -88
  106. package/lib/bmad-cache/gds/website/src/rehype-markdown-links.js +0 -117
  107. package/lib/bmad-cache/gds/website/src/styles/custom.css +0 -503
  108. package/lib/bmad-cache/wds/.claude-plugin/marketplace.json +0 -35
  109. package/lib/bmad-cache/wds/.markdownlint-cli2.yaml +0 -38
  110. package/lib/bmad-cache/wds/.nvmrc +0 -1
  111. package/lib/bmad-cache/wds/.prettierignore +0 -9
  112. package/lib/bmad-cache/wds/LICENSE +0 -27
  113. package/lib/bmad-cache/wds/README.md +0 -139
  114. package/lib/bmad-cache/wds/_git_preserved/HEAD +0 -1
  115. package/lib/bmad-cache/wds/_git_preserved/config +0 -13
  116. package/lib/bmad-cache/wds/_git_preserved/description +0 -1
  117. package/lib/bmad-cache/wds/_git_preserved/hooks/applypatch-msg.sample +0 -15
  118. package/lib/bmad-cache/wds/_git_preserved/hooks/commit-msg.sample +0 -74
  119. package/lib/bmad-cache/wds/_git_preserved/hooks/fsmonitor-watchman.sample +0 -168
  120. package/lib/bmad-cache/wds/_git_preserved/hooks/post-update.sample +0 -8
  121. package/lib/bmad-cache/wds/_git_preserved/hooks/pre-applypatch.sample +0 -14
  122. package/lib/bmad-cache/wds/_git_preserved/hooks/pre-commit.sample +0 -49
  123. package/lib/bmad-cache/wds/_git_preserved/hooks/pre-merge-commit.sample +0 -13
  124. package/lib/bmad-cache/wds/_git_preserved/hooks/pre-push.sample +0 -53
  125. package/lib/bmad-cache/wds/_git_preserved/hooks/pre-rebase.sample +0 -169
  126. package/lib/bmad-cache/wds/_git_preserved/hooks/pre-receive.sample +0 -24
  127. package/lib/bmad-cache/wds/_git_preserved/hooks/prepare-commit-msg.sample +0 -42
  128. package/lib/bmad-cache/wds/_git_preserved/hooks/push-to-checkout.sample +0 -78
  129. package/lib/bmad-cache/wds/_git_preserved/hooks/sendemail-validate.sample +0 -77
  130. package/lib/bmad-cache/wds/_git_preserved/hooks/update.sample +0 -128
  131. package/lib/bmad-cache/wds/_git_preserved/index +0 -0
  132. package/lib/bmad-cache/wds/_git_preserved/info/exclude +0 -6
  133. package/lib/bmad-cache/wds/_git_preserved/objects/pack/pack-656c3d8d5426e73043b6a7f45eedaab74e3c419e.idx +0 -0
  134. package/lib/bmad-cache/wds/_git_preserved/objects/pack/pack-656c3d8d5426e73043b6a7f45eedaab74e3c419e.pack +0 -0
  135. package/lib/bmad-cache/wds/_git_preserved/objects/pack/pack-656c3d8d5426e73043b6a7f45eedaab74e3c419e.rev +0 -0
  136. package/lib/bmad-cache/wds/_git_preserved/packed-refs +0 -2
  137. package/lib/bmad-cache/wds/_git_preserved/refs/heads/main +0 -1
  138. package/lib/bmad-cache/wds/_git_preserved/refs/tags/v0.4.3 +0 -1
  139. package/lib/bmad-cache/wds/_git_preserved/shallow +0 -1
  140. package/lib/bmad-cache/wds/eslint.config.mjs +0 -152
  141. package/lib/bmad-cache/wds/package.json +0 -82
  142. package/lib/bmad-cache/wds/prettier.config.mjs +0 -32
  143. package/lib/bmad-cache/wds/src/agents/wds-agent-freya-ux/customize.toml +0 -80
  144. package/lib/bmad-cache/wds/src/agents/wds-agent-mimir-builder/customize.toml +0 -52
  145. package/lib/bmad-cache/wds/src/agents/wds-agent-saga-analyst/customize.toml +0 -70
  146. package/lib/bmad-cache/wds/src/module-help.csv +0 -19
  147. package/lib/bmad-cache/wds/src/module.yaml +0 -173
  148. package/lib/bmad-cache/wds/src/scripts/README.md +0 -155
  149. package/lib/bmad-cache/wds/src/scripts/wds-add-object.js +0 -202
  150. package/lib/bmad-cache/wds/src/scripts/wds-add-spacing.js +0 -158
  151. package/lib/bmad-cache/wds/src/scripts/wds-init-page.js +0 -229
  152. package/lib/bmad-cache/wds/src/scripts/wds-init-scenario.js +0 -120
  153. package/lib/bmad-cache/wds/src/scripts/wds-nav.js +0 -201
  154. package/lib/bmad-cache/wds/src/scripts/wds-validate.js +0 -301
  155. package/lib/bmad-cache/wds/src/workflows/wds-0-project-setup/resources/wds-1-project-brief/templates/platform-requirements.template.yaml +0 -69
  156. package/lib/bmad-cache/wds/src/workflows/wds-0-project-setup/resources/wds-7-design-system/templates/catalog.template.html +0 -363
  157. package/lib/bmad-cache/wds/src/workflows/wds-1-project-brief/templates/platform-requirements.template.yaml +0 -69
  158. package/lib/bmad-cache/wds/src/workflows/wds-3-scenarios/workflow.xml +0 -450
  159. package/lib/bmad-cache/wds/src/workflows/wds-4-ux-design/templates/design-delivery.template.yaml +0 -104
  160. package/lib/bmad-cache/wds/src/workflows/wds-4-ux-design/templates/test-scenario.template.yaml +0 -192
  161. package/lib/bmad-cache/wds/src/workflows/wds-4-ux-design/workflow-specify.xml +0 -387
  162. package/lib/bmad-cache/wds/src/workflows/wds-5-agentic-development/templates/components/dev-mode.css +0 -164
  163. package/lib/bmad-cache/wds/src/workflows/wds-5-agentic-development/templates/components/dev-mode.html +0 -18
  164. package/lib/bmad-cache/wds/src/workflows/wds-5-agentic-development/templates/components/dev-mode.js +0 -430
  165. package/lib/bmad-cache/wds/src/workflows/wds-5-agentic-development/templates/demo-data-template.json +0 -63
  166. package/lib/bmad-cache/wds/src/workflows/wds-5-agentic-development/templates/page-template.html +0 -465
  167. package/lib/bmad-cache/wds/src/workflows/wds-5-agentic-development/templates/work-file-template.yaml +0 -264
  168. package/lib/bmad-cache/wds/src/workflows/wds-7-design-system/templates/catalog.template.html +0 -363
@@ -1,290 +0,0 @@
1
- #!/usr/bin/env python3
2
- # /// script
3
- # requires-python = ">=3.10"
4
- # ///
5
- """Render a PRD validation findings JSON into HTML + markdown reports.
6
-
7
- Reads structured findings produced by the validator subagent, groups them by
8
- category (explicit `category` field, else derived from ID prefix), computes a
9
- pass/warn/fail summary and grade, substitutes into the configured HTML
10
- template, writes a markdown companion at the same path with `.md` extension,
11
- and optionally opens the HTML in the default browser.
12
- """
13
-
14
- import argparse
15
- import html
16
- import json
17
- import string
18
- import sys
19
- import webbrowser
20
- from datetime import datetime
21
- from pathlib import Path
22
-
23
- CATEGORY_FROM_PREFIX = {
24
- "Q": "Quality",
25
- "D": "Discipline",
26
- "S": "Structural integrity",
27
- "STK": "Stakes-gated",
28
- "M": "Mechanical",
29
- }
30
-
31
- CATEGORY_ORDER = ["Quality", "Discipline", "Structural integrity", "Stakes-gated", "Mechanical"]
32
-
33
-
34
- def category_for(finding: dict) -> str:
35
- explicit = finding.get("category")
36
- if explicit:
37
- return explicit
38
- fid = finding.get("id", "")
39
- prefix = fid.split("-", 1)[0] if "-" in fid else fid
40
- return CATEGORY_FROM_PREFIX.get(prefix, prefix or "Other")
41
-
42
-
43
- def compute_stats(findings: list[dict]) -> dict:
44
- total = len(findings)
45
- by_status = {"pass": 0, "warn": 0, "fail": 0, "n/a": 0}
46
- failed_critical = 0
47
- failed_high = 0
48
- for f in findings:
49
- status = (f.get("status") or "n/a").lower()
50
- if status in by_status:
51
- by_status[status] += 1
52
- if status == "fail":
53
- sev = (f.get("severity") or "low").lower()
54
- if sev == "critical":
55
- failed_critical += 1
56
- elif sev == "high":
57
- failed_high += 1
58
- return {
59
- "total": total,
60
- "passed": by_status["pass"],
61
- "warned": by_status["warn"],
62
- "failed": by_status["fail"],
63
- "na": by_status["n/a"],
64
- "failed_critical": failed_critical,
65
- "failed_high": failed_high,
66
- }
67
-
68
-
69
- def grade_from(stats: dict) -> tuple[str, str]:
70
- if stats["failed_critical"] > 0:
71
- return "Poor", "grade-poor"
72
- if stats["failed_high"] >= 1 or stats["failed"] >= 4:
73
- return "Fair", "grade-fair"
74
- if stats["failed"] > 0 or stats["warned"] > 2:
75
- return "Good", "grade-good"
76
- return "Excellent", "grade-excellent"
77
-
78
-
79
- def render_score_bar(stats: dict, width: int = 480, height: int = 22) -> str:
80
- total = max(stats["total"], 1)
81
- p = stats["passed"] / total * width
82
- w = stats["warned"] / total * width
83
- f = stats["failed"] / total * width
84
- n = stats["na"] / total * width
85
- return (
86
- f'<svg width="{width}" height="{height}" viewBox="0 0 {width} {height}" role="img" '
87
- f'aria-label="Pass / warn / fail / n-a breakdown">'
88
- f'<rect x="0" y="0" width="{p:.1f}" height="{height}" fill="#22c55e"/>'
89
- f'<rect x="{p:.1f}" y="0" width="{w:.1f}" height="{height}" fill="#eab308"/>'
90
- f'<rect x="{p + w:.1f}" y="0" width="{f:.1f}" height="{height}" fill="#ef4444"/>'
91
- f'<rect x="{p + w + f:.1f}" y="0" width="{n:.1f}" height="{height}" fill="#94a3b8"/>'
92
- f"</svg>"
93
- )
94
-
95
-
96
- def render_finding(f: dict) -> str:
97
- status = (f.get("status") or "n/a").lower()
98
- severity = (f.get("severity") or "low").lower()
99
- fid = html.escape(f.get("id") or "")
100
- title = html.escape(f.get("title") or fid)
101
- location = html.escape(f.get("location") or "")
102
- note = html.escape(f.get("note") or "")
103
- fix = html.escape(f.get("suggested_fix") or "")
104
-
105
- status_class = "na" if status == "n/a" else status
106
- parts = [
107
- f'<article class="finding finding-{status_class}">',
108
- '<header>',
109
- f'<span class="badge badge-status badge-{status_class}">{status.upper()}</span>',
110
- f'<span class="badge badge-severity badge-sev-{severity}">{severity}</span>',
111
- f'<span class="finding-id">{fid}</span>',
112
- f'<h3 class="finding-title">{title}</h3>',
113
- '</header>',
114
- ]
115
- if location:
116
- parts.append(f'<div class="finding-location"><strong>Location:</strong> {location}</div>')
117
- if note:
118
- parts.append(f'<div class="finding-note">{note}</div>')
119
- if fix:
120
- parts.append(f'<div class="finding-fix"><strong>Suggested fix:</strong> {fix}</div>')
121
- parts.append("</article>")
122
- return "\n".join(parts)
123
-
124
-
125
- def render_category(name: str, findings: list[dict]) -> str:
126
- items = "\n".join(render_finding(f) for f in findings)
127
- name_e = html.escape(name)
128
- return (
129
- f'<section class="category">'
130
- f"<details open>"
131
- f'<summary><h2>{name_e} <span class="count">({len(findings)})</span></h2></summary>'
132
- f"{items}"
133
- f"</details>"
134
- f"</section>"
135
- )
136
-
137
-
138
- SEVERITY_ORDER = ["critical", "high", "medium", "low"]
139
-
140
-
141
- def render_finding_md(f: dict) -> str:
142
- status = (f.get("status") or "n/a").upper()
143
- severity = (f.get("severity") or "low").lower()
144
- fid = f.get("id") or ""
145
- title = f.get("title") or fid
146
- location = f.get("location") or ""
147
- note = f.get("note") or ""
148
- fix = f.get("suggested_fix") or ""
149
-
150
- lines = [f"### [{status}] {fid} — {title} _(severity: {severity})_"]
151
- if location:
152
- lines.append(f"- **Location:** {location}")
153
- if note:
154
- lines.append(f"- **Finding:** {note}")
155
- if fix:
156
- lines.append(f"- **Suggested fix:** {fix}")
157
- return "\n".join(lines)
158
-
159
-
160
- def render_markdown_report(data: dict, findings: list[dict], stats: dict, grade: str) -> str:
161
- prd_name = data.get("prd_name") or "PRD"
162
- prd_path = data.get("prd_path") or ""
163
- checklist_path = data.get("checklist_path") or ""
164
- timestamp = data.get("timestamp") or datetime.now().isoformat(timespec="seconds")
165
- synthesis = data.get("overall_synthesis") or ""
166
-
167
- out = [
168
- f"# Validation Report — {prd_name}",
169
- "",
170
- f"- **PRD:** `{prd_path}`",
171
- f"- **Checklist:** `{checklist_path}`",
172
- f"- **Run at:** {timestamp}",
173
- f"- **Grade:** {grade}",
174
- "",
175
- f"**Summary:** {stats['passed']} pass · {stats['warned']} warn · {stats['failed']} fail · {stats['na']} n/a "
176
- f"(total {stats['total']}; critical fails: {stats['failed_critical']}, high fails: {stats['failed_high']})",
177
- ]
178
- if synthesis:
179
- out += ["", "## Overall synthesis", "", synthesis]
180
-
181
- # Group by severity then status: failed criticals first, then highs, etc.
182
- by_sev: dict[str, list[dict]] = {s: [] for s in SEVERITY_ORDER}
183
- other: list[dict] = []
184
- for f in findings:
185
- sev = (f.get("severity") or "low").lower()
186
- if sev in by_sev:
187
- by_sev[sev].append(f)
188
- else:
189
- other.append(f)
190
-
191
- out += ["", "## Findings by severity"]
192
- any_findings = False
193
- for sev in SEVERITY_ORDER:
194
- items = by_sev[sev]
195
- if not items:
196
- continue
197
- any_findings = True
198
- out += ["", f"### {sev.capitalize()} ({len(items)})", ""]
199
- out += [render_finding_md(f) for f in items]
200
- if other:
201
- any_findings = True
202
- out += ["", f"### Other ({len(other)})", ""]
203
- out += [render_finding_md(f) for f in other]
204
- if not any_findings:
205
- out += ["", "_No findings._"]
206
-
207
- return "\n".join(out) + "\n"
208
-
209
-
210
- def main(argv: list[str]) -> int:
211
- parser = argparse.ArgumentParser(description="Render PRD validation findings to HTML.")
212
- parser.add_argument("--findings", required=True, help="Path to validation-findings.json")
213
- parser.add_argument("--template", required=True, help="Path to HTML template")
214
- parser.add_argument("--output", required=True, help="Path to write the rendered HTML")
215
- parser.add_argument("--open", action="store_true", help="Open the rendered HTML in the default browser")
216
- args = parser.parse_args(argv)
217
-
218
- findings_path = Path(args.findings)
219
- template_path = Path(args.template)
220
- output_path = Path(args.output)
221
-
222
- try:
223
- data = json.loads(findings_path.read_text(encoding="utf-8"))
224
- except FileNotFoundError:
225
- print(f"error: findings file not found: {findings_path}", file=sys.stderr)
226
- return 1
227
- except json.JSONDecodeError as e:
228
- print(f"error: findings file is not valid JSON ({findings_path}): {e}", file=sys.stderr)
229
- return 1
230
- try:
231
- template = template_path.read_text(encoding="utf-8")
232
- except FileNotFoundError:
233
- print(f"error: template file not found: {template_path}", file=sys.stderr)
234
- return 1
235
-
236
- findings = data.get("findings", []) or []
237
-
238
- by_cat: dict[str, list[dict]] = {}
239
- for f in findings:
240
- by_cat.setdefault(category_for(f), []).append(f)
241
-
242
- sorted_cats = sorted(
243
- by_cat.keys(),
244
- key=lambda c: (CATEGORY_ORDER.index(c) if c in CATEGORY_ORDER else 99, c),
245
- )
246
- categories_html = "\n".join(render_category(c, by_cat[c]) for c in sorted_cats)
247
-
248
- stats = compute_stats(findings)
249
- grade, grade_class = grade_from(stats)
250
- score_svg = render_score_bar(stats)
251
-
252
- timestamp = data.get("timestamp") or datetime.now().isoformat(timespec="seconds")
253
- substitutions = {
254
- "prd_name": html.escape(str(data.get("prd_name") or "PRD")),
255
- "prd_path": html.escape(str(data.get("prd_path") or "")),
256
- "checklist_path": html.escape(str(data.get("checklist_path") or "")),
257
- "timestamp": html.escape(timestamp),
258
- "overall_synthesis": html.escape(str(data.get("overall_synthesis") or "")),
259
- "grade": grade,
260
- "grade_class": grade_class,
261
- "total": str(stats["total"]),
262
- "passed": str(stats["passed"]),
263
- "failed": str(stats["failed"]),
264
- "warned": str(stats["warned"]),
265
- "na": str(stats["na"]),
266
- "score_svg": score_svg,
267
- "categories_html": categories_html,
268
- }
269
-
270
- rendered = string.Template(template).safe_substitute(substitutions)
271
- output_path.write_text(rendered, encoding="utf-8")
272
-
273
- md_path = output_path.with_suffix(".md")
274
- md_path.write_text(render_markdown_report(data, findings, stats, grade), encoding="utf-8")
275
-
276
- print(json.dumps({
277
- "output": str(output_path),
278
- "markdown": str(md_path),
279
- "grade": grade,
280
- "stats": stats,
281
- }))
282
-
283
- if args.open:
284
- webbrowser.open(output_path.resolve().as_uri())
285
-
286
- return 0
287
-
288
-
289
- if __name__ == "__main__":
290
- sys.exit(main(sys.argv[1:]))
@@ -1,190 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="utf-8">
5
- <title>PRD Validation: $prd_name</title>
6
- <style>
7
- :root {
8
- --bg: #fafaf9;
9
- --surface: #ffffff;
10
- --border: #e7e5e4;
11
- --text: #1c1917;
12
- --muted: #78716c;
13
- --pass: #22c55e;
14
- --warn: #eab308;
15
- --fail: #ef4444;
16
- --na: #94a3b8;
17
- --sev-low: #64748b;
18
- --sev-medium: #ca8a04;
19
- --sev-high: #ea580c;
20
- --sev-critical: #dc2626;
21
- --grade-exc: #16a34a;
22
- --grade-good: #65a30d;
23
- --grade-fair: #d97706;
24
- --grade-poor: #dc2626;
25
- }
26
- * { box-sizing: border-box; }
27
- html, body { margin: 0; padding: 0; }
28
- body {
29
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Inter, system-ui, sans-serif;
30
- background: var(--bg);
31
- color: var(--text);
32
- line-height: 1.55;
33
- font-size: 15px;
34
- }
35
- .container { max-width: 960px; margin: 0 auto; padding: 32px 24px 64px; }
36
-
37
- header.report-header {
38
- display: flex;
39
- align-items: center;
40
- justify-content: space-between;
41
- gap: 24px;
42
- padding-bottom: 16px;
43
- border-bottom: 1px solid var(--border);
44
- margin-bottom: 24px;
45
- }
46
- .title h1 { margin: 0; font-size: 22px; font-weight: 600; letter-spacing: -0.01em; }
47
- .title .subtitle { color: var(--muted); font-size: 13px; margin-top: 4px; font-family: ui-monospace, "SF Mono", Menlo, monospace; }
48
- .grade {
49
- padding: 10px 18px;
50
- border-radius: 8px;
51
- font-weight: 600;
52
- color: white;
53
- font-size: 15px;
54
- white-space: nowrap;
55
- }
56
- .grade-excellent { background: var(--grade-exc); }
57
- .grade-good { background: var(--grade-good); }
58
- .grade-fair { background: var(--grade-fair); }
59
- .grade-poor { background: var(--grade-poor); }
60
-
61
- .synthesis {
62
- background: var(--surface);
63
- border: 1px solid var(--border);
64
- border-left: 3px solid var(--muted);
65
- border-radius: 8px;
66
- padding: 16px 20px;
67
- margin-bottom: 24px;
68
- color: var(--text);
69
- font-size: 15px;
70
- }
71
- .synthesis:empty { display: none; }
72
-
73
- .scoreboard {
74
- background: var(--surface);
75
- border: 1px solid var(--border);
76
- border-radius: 8px;
77
- padding: 18px 20px;
78
- margin-bottom: 24px;
79
- }
80
- .score-bar { margin: 0 0 14px; line-height: 0; }
81
- .score-stats { display: flex; gap: 22px; font-size: 14px; flex-wrap: wrap; }
82
- .score-stats span { display: inline-flex; align-items: center; gap: 6px; }
83
- .score-stats .dot { width: 10px; height: 10px; border-radius: 50%; display: inline-block; }
84
- .dot-pass { background: var(--pass); }
85
- .dot-warn { background: var(--warn); }
86
- .dot-fail { background: var(--fail); }
87
- .dot-na { background: var(--na); }
88
- .total-count { margin-left: auto; color: var(--muted); }
89
-
90
- section.category { margin-bottom: 16px; }
91
- section.category details {
92
- background: var(--surface);
93
- border: 1px solid var(--border);
94
- border-radius: 8px;
95
- overflow: hidden;
96
- }
97
- section.category summary {
98
- padding: 14px 20px;
99
- cursor: pointer;
100
- user-select: none;
101
- list-style: none;
102
- }
103
- section.category summary::-webkit-details-marker { display: none; }
104
- section.category summary::before {
105
- content: "▸";
106
- display: inline-block;
107
- margin-right: 10px;
108
- color: var(--muted);
109
- transition: transform 0.15s ease;
110
- }
111
- section.category details[open] summary::before { transform: rotate(90deg); }
112
- section.category summary h2 { display: inline; margin: 0; font-size: 16px; font-weight: 600; letter-spacing: -0.005em; }
113
- section.category .count { color: var(--muted); font-weight: 400; margin-left: 6px; font-size: 14px; }
114
-
115
- article.finding { padding: 16px 20px; border-top: 1px solid var(--border); }
116
- article.finding-fail { background: rgba(239, 68, 68, 0.025); }
117
- article.finding header {
118
- display: flex;
119
- align-items: center;
120
- gap: 10px;
121
- flex-wrap: wrap;
122
- margin-bottom: 8px;
123
- }
124
- .badge {
125
- font-size: 10.5px;
126
- padding: 3px 8px;
127
- border-radius: 4px;
128
- font-weight: 600;
129
- text-transform: uppercase;
130
- letter-spacing: 0.04em;
131
- line-height: 1.4;
132
- }
133
- .badge-pass { background: rgba(34, 197, 94, 0.12); color: #15803d; }
134
- .badge-warn { background: rgba(234, 179, 8, 0.14); color: #854d0e; }
135
- .badge-fail { background: rgba(239, 68, 68, 0.12); color: #b91c1c; }
136
- .badge-na { background: rgba(148, 163, 184, 0.16); color: #475569; }
137
- .badge-sev-low { background: rgba(100, 116, 139, 0.12); color: var(--sev-low); }
138
- .badge-sev-medium { background: rgba(202, 138, 4, 0.14); color: var(--sev-medium); }
139
- .badge-sev-high { background: rgba(234, 88, 12, 0.14); color: var(--sev-high); }
140
- .badge-sev-critical { background: rgba(220, 38, 38, 0.14); color: var(--sev-critical); }
141
- .finding-id { font-family: ui-monospace, "SF Mono", Menlo, monospace; font-size: 12px; color: var(--muted); }
142
- .finding-title { margin: 0; font-size: 15px; font-weight: 500; flex: 1; min-width: 200px; }
143
- .finding-location, .finding-note, .finding-fix { margin-top: 6px; font-size: 14px; color: var(--text); }
144
- .finding-location strong, .finding-fix strong { color: var(--muted); font-weight: 500; }
145
-
146
- footer.report-footer {
147
- margin-top: 40px;
148
- padding-top: 16px;
149
- border-top: 1px solid var(--border);
150
- font-size: 12px;
151
- color: var(--muted);
152
- font-family: ui-monospace, "SF Mono", Menlo, monospace;
153
- }
154
- footer .meta { display: flex; gap: 24px; flex-wrap: wrap; }
155
- </style>
156
- </head>
157
- <body>
158
- <div class="container">
159
- <header class="report-header">
160
- <div class="title">
161
- <h1>$prd_name — Validation Report</h1>
162
- <div class="subtitle">$prd_path</div>
163
- </div>
164
- <div class="grade $grade_class">$grade</div>
165
- </header>
166
-
167
- <div class="synthesis">$overall_synthesis</div>
168
-
169
- <div class="scoreboard">
170
- <div class="score-bar">$score_svg</div>
171
- <div class="score-stats">
172
- <span><span class="dot dot-pass"></span>$passed pass</span>
173
- <span><span class="dot dot-warn"></span>$warned warn</span>
174
- <span><span class="dot dot-fail"></span>$failed fail</span>
175
- <span><span class="dot dot-na"></span>$na n/a</span>
176
- <span class="total-count">$total items checked</span>
177
- </div>
178
- </div>
179
-
180
- $categories_html
181
-
182
- <footer class="report-footer">
183
- <div class="meta">
184
- <span>Checklist: $checklist_path</span>
185
- <span>Generated: $timestamp</span>
186
- </div>
187
- </footer>
188
- </div>
189
- </body>
190
- </html>
@@ -1,84 +0,0 @@
1
- # DO NOT EDIT -- overwritten on every update.
2
- #
3
- # Workflow customization surface for gds-prd.
4
- #
5
- # Override files (not edited here):
6
- # {project-root}/_bmad/custom/gds-prd.toml (team)
7
- # {project-root}/_bmad/custom/gds-prd.user.toml (personal)
8
-
9
- [workflow]
10
-
11
- # --- Configurable below. Overrides merge per BMad structural rules: ---
12
- # scalars: override wins • arrays: append
13
-
14
- # Steps to run before the standard activation (config load, greet).
15
- # Use for pre-flight loads, compliance checks, etc.
16
- activation_steps_prepend = []
17
-
18
- # Steps to run after greet but before the workflow begins.
19
- # Use for context-heavy setup that should happen once the user has been acknowledged.
20
- activation_steps_append = []
21
-
22
- # Persistent facts the workflow keeps in mind for the whole run
23
- # (standards, compliance constraints, stylistic guardrails).
24
- # Each entry is either a literal sentence, a skill prefixed with `skill:`, or a `file:`-prefixed
25
- # path/glob whose contents are loaded as facts.
26
- #
27
- # Default loads project-context.md if gds-generate-project-context has produced one — this gives
28
- # the facilitator persistent awareness of the project's tech, domain, and constraints without
29
- # re-asking. Common opt-ins (set in team/user override TOML):
30
- # "skill:acme-studio:terms-and-conditions" # a skill that contains relevant info
31
- # "Every PRD requirement must be testable and traceable to a GDD pillar." # generic agent instruction
32
- persistent_facts = [
33
- "file:{project-root}/**/project-context.md",
34
- ]
35
-
36
- # Executed when the workflow completes (after the user has been told the
37
- # PRD is ready). Accepts either a string scalar (single instruction)
38
- # or an array of instructions executed in order. Empty for none.
39
- on_complete = ""
40
-
41
- # Default PRD structure. Treated as a starting point — the LLM adapts it
42
- # to the product, project type, and domain. Override the path in team/user TOML
43
- # to enforce a different structure (e.g. regulated-industry, internal-tool).
44
- prd_template = "assets/prd-template.md"
45
-
46
- # Validation checklist used at the Validate intent and at Finalize step 3.
47
- # A subagent walks the checklist against prd.md and returns structured findings.
48
- # Override the path in team/user TOML to enforce a studio-specific checklist.
49
- validation_checklist = "assets/prd-validation-checklist.md"
50
-
51
- # HTML skeleton the synthesis pass fills directly when consolidating reviewer
52
- # outputs into a validation report. No substitution engine — the parent LLM
53
- # writes the final HTML. Fully overridable to match studio branding.
54
- # Uses inline CSS, no external dependencies, and native HTML <details> for
55
- # collapse — no JS.
56
- validation_report_template = "assets/validation-report-template.html"
57
-
58
- # Run folder location. The PRD, optional addendum, decision log, and optional
59
- # validation report all land inside `{output_dir}/{output_folder_name}/`.
60
- output_dir = "{planning_artifacts}/prds"
61
- output_folder_name = "prd-{project_name}-{date}"
62
-
63
- # Document standards applied to human-consumed docs at Finalize. Each entry is
64
- # a `skill:`, `file:`, or plain-text directive; the parent LLM applies the
65
- # findings before the user sees the draft. Empty by default — Game Dev Studio
66
- # ships no editorial-review skills. Add your own in team/user override TOML:
67
- # "skill:my-studio:editorial-review-prose"
68
- # "file:{project-root}/_bmad/style-guides/studio-voice.md"
69
- # "Convert all dates to ISO 8601 format."
70
- doc_standards = []
71
-
72
- # External-source registry. Natural-language directives describing knowledge
73
- # bases, MCP tools, or internal systems the LLM may consult during the workflow
74
- # when a relevant need surfaces. The LLM does NOT query these preemptively —
75
- # it consults them on demand. If a named MCP tool is unavailable at runtime,
76
- # the LLM falls back to standard behavior and notes the gap. Empty by default.
77
- external_sources = []
78
-
79
- # External-handoff routing. Natural-language directives the LLM applies at
80
- # Finalize to route outputs beyond local files (Confluence, Notion, Google
81
- # Drive, ticket systems, etc.). Handoffs run after the artifact is polished
82
- # and before the final user-facing message. URLs or IDs returned by the
83
- # destination are captured and surfaced to the user. Empty by default.
84
- external_handoffs = []