ma-agents 3.12.3 → 3.13.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 (254) hide show
  1. package/docs/architecture.md +18 -0
  2. package/lib/bmad-cache/bmb/.claude-plugin/marketplace.json +1 -1
  3. package/lib/bmad-cache/bmb/_git_preserved/hooks/commit-msg.sample +52 -2
  4. package/lib/bmad-cache/bmb/_git_preserved/hooks/fsmonitor-watchman.sample +2 -8
  5. package/lib/bmad-cache/bmb/_git_preserved/index +0 -0
  6. package/lib/bmad-cache/bmb/_git_preserved/objects/pack/pack-8f8b045fef5af6911495cf3b2a89f1ed75e120f7.idx +0 -0
  7. package/lib/bmad-cache/bmb/_git_preserved/objects/pack/pack-8f8b045fef5af6911495cf3b2a89f1ed75e120f7.pack +0 -0
  8. package/lib/bmad-cache/bmb/_git_preserved/objects/pack/pack-8f8b045fef5af6911495cf3b2a89f1ed75e120f7.rev +0 -0
  9. package/lib/bmad-cache/bmb/_git_preserved/packed-refs +1 -1
  10. package/lib/bmad-cache/bmb/_git_preserved/refs/heads/main +1 -1
  11. package/lib/bmad-cache/bmb/_git_preserved/shallow +1 -1
  12. package/lib/bmad-cache/bmb/package-lock.json +2 -2
  13. package/lib/bmad-cache/bmb/package.json +1 -1
  14. package/lib/bmad-cache/bmb/samples/bmad-agent-dream-weaver/assets/module-help.csv +1 -1
  15. package/lib/bmad-cache/bmb/samples/bmad-agent-dream-weaver/scripts/merge-config.py +33 -0
  16. package/lib/bmad-cache/bmb/samples/bmad-agent-dream-weaver/scripts/merge-help-csv.py +28 -0
  17. package/lib/bmad-cache/bmb/samples/sample-module-setup/assets/module-help.csv +1 -1
  18. package/lib/bmad-cache/bmb/samples/sample-module-setup/scripts/cleanup-legacy.py +28 -0
  19. package/lib/bmad-cache/bmb/samples/sample-module-setup/scripts/merge-config.py +33 -0
  20. package/lib/bmad-cache/bmb/samples/sample-module-setup/scripts/merge-help-csv.py +28 -0
  21. package/lib/bmad-cache/bmb/skills/bmad-bmb-setup/assets/module-help.csv +1 -1
  22. package/lib/bmad-cache/bmb/skills/bmad-bmb-setup/scripts/cleanup-legacy.py +28 -0
  23. package/lib/bmad-cache/bmb/skills/bmad-bmb-setup/scripts/merge-config.py +33 -0
  24. package/lib/bmad-cache/bmb/skills/bmad-bmb-setup/scripts/merge-help-csv.py +28 -0
  25. package/lib/bmad-cache/bmb/skills/bmad-eval-runner/assets/Dockerfile +29 -0
  26. package/lib/bmad-cache/bmb/skills/bmad-eval-runner/scripts/docker_setup.py +115 -0
  27. package/lib/bmad-cache/bmb/skills/bmad-eval-runner/scripts/generate_report.py +184 -0
  28. package/lib/bmad-cache/bmb/skills/bmad-eval-runner/scripts/pty_runner.py +171 -0
  29. package/lib/bmad-cache/bmb/skills/bmad-eval-runner/scripts/run_evals.py +492 -0
  30. package/lib/bmad-cache/bmb/skills/bmad-eval-runner/scripts/run_triggers.py +366 -0
  31. package/lib/bmad-cache/bmb/skills/bmad-eval-runner/scripts/utils.py +260 -0
  32. package/lib/bmad-cache/bmb/skills/bmad-module-builder/assets/setup-skill-template/assets/module-help.csv +1 -1
  33. package/lib/bmad-cache/bmb/skills/bmad-module-builder/assets/setup-skill-template/scripts/cleanup-legacy.py +28 -0
  34. package/lib/bmad-cache/bmb/skills/bmad-module-builder/assets/setup-skill-template/scripts/merge-config.py +33 -0
  35. package/lib/bmad-cache/bmb/skills/bmad-module-builder/assets/setup-skill-template/scripts/merge-help-csv.py +28 -0
  36. package/lib/bmad-cache/bmb/skills/bmad-module-builder/assets/standalone-module-template/merge-config.py +33 -0
  37. package/lib/bmad-cache/bmb/skills/bmad-module-builder/assets/standalone-module-template/merge-help-csv.py +28 -0
  38. package/lib/bmad-cache/bmb/skills/bmad-module-builder/scripts/tests/test-validate-module.py +74 -1
  39. package/lib/bmad-cache/bmb/skills/bmad-module-builder/scripts/validate-module.py +24 -13
  40. package/lib/bmad-cache/bmb/skills/bmad-workflow-builder/assets/sample-customize-product-brief.toml +48 -33
  41. package/lib/bmad-cache/bmb/skills/bmad-workflow-builder/scripts/extract-report-json.py +287 -0
  42. package/lib/bmad-cache/bmb/skills/bmad-workflow-builder/scripts/generate-html-report.py +57 -8
  43. package/lib/bmad-cache/bmb/skills/bmad-workflow-builder/scripts/prepass-prompt-metrics.py +7 -7
  44. package/lib/bmad-cache/bmb/skills/module-help.csv +1 -1
  45. package/lib/bmad-cache/bmb/website/public/img/eval-test-types.png +0 -0
  46. package/lib/bmad-cache/cache-manifest.json +17 -18
  47. package/lib/bmad-cache/cis/_git_preserved/hooks/commit-msg.sample +52 -2
  48. package/lib/bmad-cache/cis/_git_preserved/hooks/fsmonitor-watchman.sample +2 -8
  49. package/lib/bmad-cache/cis/_git_preserved/index +0 -0
  50. package/lib/bmad-cache/cis/_git_preserved/objects/pack/pack-18c8290560a98bcb7bf0676e6cc9b2ac5ca2823e.idx +0 -0
  51. package/lib/bmad-cache/cis/_git_preserved/objects/pack/{pack-42ffc048f54e58ce94c6331bc6be97ebbb7936f2.pack → pack-18c8290560a98bcb7bf0676e6cc9b2ac5ca2823e.pack} +0 -0
  52. package/lib/bmad-cache/cis/_git_preserved/objects/pack/pack-18c8290560a98bcb7bf0676e6cc9b2ac5ca2823e.rev +0 -0
  53. package/lib/bmad-cache/cis/_git_preserved/packed-refs +1 -1
  54. package/lib/bmad-cache/cis/_git_preserved/refs/heads/main +1 -1
  55. package/lib/bmad-cache/cis/_git_preserved/refs/tags/v0.2.1 +1 -0
  56. package/lib/bmad-cache/cis/_git_preserved/shallow +1 -1
  57. package/lib/bmad-cache/cis/package-lock.json +2 -2
  58. package/lib/bmad-cache/cis/package.json +1 -1
  59. package/lib/bmad-cache/cis/src/module-help.csv +1 -1
  60. package/lib/bmad-cache/gds/.claude-plugin/marketplace.json +4 -7
  61. package/lib/bmad-cache/gds/README.md +3 -1
  62. package/lib/bmad-cache/gds/_git_preserved/hooks/commit-msg.sample +52 -2
  63. package/lib/bmad-cache/gds/_git_preserved/hooks/fsmonitor-watchman.sample +2 -8
  64. package/lib/bmad-cache/gds/_git_preserved/index +0 -0
  65. package/lib/bmad-cache/gds/_git_preserved/objects/pack/pack-dcb7c556d9bb6b6b70d2301e094eaac6d7300552.idx +0 -0
  66. package/lib/bmad-cache/gds/_git_preserved/objects/pack/{pack-9427a146a90c00bb542cba038874bf9671ba4dc0.pack → pack-dcb7c556d9bb6b6b70d2301e094eaac6d7300552.pack} +0 -0
  67. package/lib/bmad-cache/gds/_git_preserved/objects/pack/pack-dcb7c556d9bb6b6b70d2301e094eaac6d7300552.rev +0 -0
  68. package/lib/bmad-cache/gds/_git_preserved/packed-refs +1 -1
  69. package/lib/bmad-cache/gds/_git_preserved/refs/heads/main +1 -1
  70. package/lib/bmad-cache/gds/_git_preserved/shallow +1 -1
  71. package/lib/bmad-cache/gds/package.json +1 -1
  72. package/lib/bmad-cache/gds/src/agents/gds-agent-game-designer/customize.toml +5 -5
  73. package/lib/bmad-cache/gds/src/agents/gds-agent-game-dev/customize.toml +5 -5
  74. package/lib/bmad-cache/gds/src/agents/gds-agent-game-solo-dev/customize.toml +0 -5
  75. package/lib/bmad-cache/gds/src/module-help.csv +6 -12
  76. package/lib/bmad-cache/gds/src/module.yaml +1 -1
  77. package/lib/bmad-cache/gds/src/workflows/1-preproduction/gds-create-game-brief/customize.toml +97 -22
  78. package/lib/bmad-cache/gds/src/workflows/2-design/gds-gdd/assets/validation-report-template.html +190 -0
  79. package/lib/bmad-cache/gds/src/workflows/2-design/gds-gdd/customize.toml +99 -0
  80. package/lib/bmad-cache/gds/src/workflows/2-design/gds-gdd/scripts/render-validation-html.py +290 -0
  81. package/lib/bmad-cache/gds/src/workflows/2-design/gds-prd/assets/validation-report-template.html +190 -0
  82. package/lib/bmad-cache/gds/src/workflows/2-design/gds-prd/customize.toml +84 -0
  83. package/lib/bmad-cache/gds/src/workflows/2-design/gds-ux/assets/validation-report-template.html +319 -0
  84. package/lib/bmad-cache/gds/src/workflows/2-design/gds-ux/customize.toml +101 -0
  85. package/lib/bmad-cache/gds/src/workflows/3-technical/gds-game-architecture/architecture-patterns.yaml +1 -0
  86. package/lib/bmad-cache/gds/src/workflows/3-technical/gds-game-architecture/decision-catalog.yaml +88 -0
  87. package/lib/bmad-cache/gds/src/workflows/3-technical/gds-game-architecture/engine-mcps.yaml +124 -2
  88. package/lib/bmad-cache/gds/src/workflows/4-production/gds-investigate/customize.toml +62 -0
  89. package/lib/bmad-cache/tea/.claude-plugin/marketplace.json +1 -1
  90. package/lib/bmad-cache/tea/.github/workflows/docs.yaml +3 -3
  91. package/lib/bmad-cache/tea/.github/workflows/quality.yaml +10 -10
  92. package/lib/bmad-cache/tea/AGENTS.md +31 -0
  93. package/lib/bmad-cache/tea/CHANGELOG.md +42 -1
  94. package/lib/bmad-cache/tea/README.md +8 -5
  95. package/lib/bmad-cache/tea/_git_preserved/hooks/commit-msg.sample +52 -2
  96. package/lib/bmad-cache/tea/_git_preserved/hooks/fsmonitor-watchman.sample +2 -8
  97. package/lib/bmad-cache/tea/_git_preserved/index +0 -0
  98. package/lib/bmad-cache/tea/_git_preserved/objects/pack/pack-9e4197e37df7763dd7a05c2965ee921dfd2eb617.idx +0 -0
  99. package/lib/bmad-cache/tea/_git_preserved/objects/pack/{pack-f0df537f2649464ff6c5aee241165eb9c8664227.pack → pack-9e4197e37df7763dd7a05c2965ee921dfd2eb617.pack} +0 -0
  100. package/lib/bmad-cache/tea/_git_preserved/objects/pack/pack-9e4197e37df7763dd7a05c2965ee921dfd2eb617.rev +0 -0
  101. package/lib/bmad-cache/tea/_git_preserved/packed-refs +1 -1
  102. package/lib/bmad-cache/tea/_git_preserved/refs/heads/main +1 -1
  103. package/lib/bmad-cache/tea/_git_preserved/refs/tags/v1.19.0 +1 -0
  104. package/lib/bmad-cache/tea/_git_preserved/shallow +1 -1
  105. package/lib/bmad-cache/tea/docs/explanation/engagement-models.md +15 -16
  106. package/lib/bmad-cache/tea/docs/explanation/knowledge-base-system.md +2 -0
  107. package/lib/bmad-cache/tea/docs/explanation/risk-based-testing.md +1 -1
  108. package/lib/bmad-cache/tea/docs/explanation/tea-overview.md +88 -52
  109. package/lib/bmad-cache/tea/docs/explanation/testing-as-engineering.md +13 -12
  110. package/lib/bmad-cache/tea/docs/glossary/index.md +2 -2
  111. package/lib/bmad-cache/tea/docs/how-to/brownfield/use-tea-for-enterprise.md +19 -18
  112. package/lib/bmad-cache/tea/docs/how-to/brownfield/use-tea-with-existing-tests.md +1 -1
  113. package/lib/bmad-cache/tea/docs/how-to/workflows/run-nfr-assess.md +32 -26
  114. package/lib/bmad-cache/tea/docs/how-to/workflows/run-test-design.md +20 -14
  115. package/lib/bmad-cache/tea/docs/how-to/workflows/run-trace.md +3 -3
  116. package/lib/bmad-cache/tea/docs/index.md +13 -11
  117. package/lib/bmad-cache/tea/docs/reference/commands.md +37 -13
  118. package/lib/bmad-cache/tea/docs/reference/knowledge-base.md +2 -2
  119. package/lib/bmad-cache/tea/package-lock.json +2 -2
  120. package/lib/bmad-cache/tea/package.json +1 -1
  121. package/lib/bmad-cache/tea/src/agents/bmad-tea/customize.toml +20 -15
  122. package/lib/bmad-cache/tea/src/agents/bmad-tea/resources/knowledge/confidence-gate.md +73 -0
  123. package/lib/bmad-cache/tea/src/agents/bmad-tea/resources/knowledge/test-quality.md +1 -0
  124. package/lib/bmad-cache/tea/src/agents/bmad-tea/resources/tea-index.csv +2 -1
  125. package/lib/bmad-cache/tea/src/module-help.csv +2 -2
  126. package/lib/bmad-cache/tea/src/workflows/testarch/README.md +5 -4
  127. package/lib/bmad-cache/tea/src/workflows/testarch/bmad-teach-me-testing/data/role-paths.yaml +1 -1
  128. package/lib/bmad-cache/tea/src/workflows/testarch/bmad-teach-me-testing/data/tea-resources-index.yaml +1 -1
  129. package/lib/bmad-cache/tea/src/workflows/testarch/bmad-teach-me-testing/steps-c/step-04-session-01.md +2 -2
  130. package/lib/bmad-cache/tea/src/workflows/testarch/bmad-teach-me-testing/steps-c/step-04-session-07.md +1 -1
  131. package/lib/bmad-cache/tea/src/workflows/testarch/bmad-teach-me-testing/templates/certificate-template.md +1 -1
  132. package/lib/bmad-cache/tea/src/workflows/testarch/bmad-testarch-atdd/resources/tea-index.csv +1 -1
  133. package/lib/bmad-cache/tea/src/workflows/testarch/bmad-testarch-automate/resources/tea-index.csv +1 -1
  134. package/lib/bmad-cache/tea/src/workflows/testarch/bmad-testarch-ci/resources/tea-index.csv +1 -1
  135. package/lib/bmad-cache/tea/src/workflows/testarch/bmad-testarch-framework/resources/tea-index.csv +1 -1
  136. package/lib/bmad-cache/tea/src/workflows/testarch/bmad-testarch-nfr/SKILL.md +3 -3
  137. package/lib/bmad-cache/tea/src/workflows/testarch/bmad-testarch-nfr/checklist.md +11 -11
  138. package/lib/bmad-cache/tea/src/workflows/testarch/bmad-testarch-nfr/instructions.md +4 -2
  139. package/lib/bmad-cache/tea/src/workflows/testarch/bmad-testarch-nfr/nfr-report-template.md +5 -5
  140. package/lib/bmad-cache/tea/src/workflows/testarch/bmad-testarch-nfr/resources/tea-index.csv +1 -1
  141. package/lib/bmad-cache/tea/src/workflows/testarch/bmad-testarch-nfr/steps-c/step-01-load-context.md +1 -1
  142. package/lib/bmad-cache/tea/src/workflows/testarch/bmad-testarch-nfr/steps-c/step-01b-resume.md +1 -1
  143. package/lib/bmad-cache/tea/src/workflows/testarch/bmad-testarch-nfr/steps-c/step-02-define-thresholds.md +14 -3
  144. package/lib/bmad-cache/tea/src/workflows/testarch/bmad-testarch-nfr/steps-c/step-04-evaluate-and-score.md +7 -7
  145. package/lib/bmad-cache/tea/src/workflows/testarch/bmad-testarch-nfr/steps-c/step-04a-subagent-security.md +4 -4
  146. package/lib/bmad-cache/tea/src/workflows/testarch/bmad-testarch-nfr/steps-c/step-04b-subagent-performance.md +4 -4
  147. package/lib/bmad-cache/tea/src/workflows/testarch/bmad-testarch-nfr/steps-c/step-04c-subagent-reliability.md +4 -4
  148. package/lib/bmad-cache/tea/src/workflows/testarch/bmad-testarch-nfr/steps-c/step-04d-subagent-scalability.md +4 -4
  149. package/lib/bmad-cache/tea/src/workflows/testarch/bmad-testarch-nfr/steps-c/step-04e-aggregate-nfr.md +4 -4
  150. package/lib/bmad-cache/tea/src/workflows/testarch/bmad-testarch-nfr/steps-c/step-05-generate-report.md +1 -1
  151. package/lib/bmad-cache/tea/src/workflows/testarch/bmad-testarch-nfr/workflow-plan.md +1 -1
  152. package/lib/bmad-cache/tea/src/workflows/testarch/bmad-testarch-nfr/workflow.yaml +3 -3
  153. package/lib/bmad-cache/tea/src/workflows/testarch/bmad-testarch-test-design/checklist.md +23 -3
  154. package/lib/bmad-cache/tea/src/workflows/testarch/bmad-testarch-test-design/resources/tea-index.csv +1 -1
  155. package/lib/bmad-cache/tea/src/workflows/testarch/bmad-testarch-test-design/steps-c/step-02-load-context.md +7 -0
  156. package/lib/bmad-cache/tea/src/workflows/testarch/bmad-testarch-test-design/steps-c/step-03-risk-and-testability.md +16 -2
  157. package/lib/bmad-cache/tea/src/workflows/testarch/bmad-testarch-test-design/steps-c/step-04-coverage-plan.md +20 -4
  158. package/lib/bmad-cache/tea/src/workflows/testarch/bmad-testarch-test-design/steps-c/step-05-generate-output.md +2 -0
  159. package/lib/bmad-cache/tea/src/workflows/testarch/bmad-testarch-test-design/test-design-architecture-template.md +17 -0
  160. package/lib/bmad-cache/tea/src/workflows/testarch/bmad-testarch-test-design/test-design-qa-template.md +15 -0
  161. package/lib/bmad-cache/tea/src/workflows/testarch/bmad-testarch-test-design/test-design-template.md +16 -0
  162. package/lib/bmad-cache/tea/src/workflows/testarch/bmad-testarch-test-review/resources/tea-index.csv +1 -1
  163. package/lib/bmad-cache/tea/src/workflows/testarch/bmad-testarch-trace/checklist.md +1 -1
  164. package/lib/bmad-cache/tea/src/workflows/testarch/bmad-testarch-trace/resources/tea-index.csv +1 -1
  165. package/lib/bmad-cache/tea/src/workflows/testarch/bmad-testarch-trace/trace-template.md +1 -1
  166. package/lib/bmad-cache/tea/test/test-installation-components.js +49 -0
  167. package/lib/bmad-cache/tea/website/astro.config.mjs +2 -2
  168. package/lib/bmad-cache/wds/README.md +1 -1
  169. package/lib/bmad-cache/wds/_git_preserved/hooks/commit-msg.sample +52 -2
  170. package/lib/bmad-cache/wds/_git_preserved/hooks/fsmonitor-watchman.sample +2 -8
  171. package/lib/bmad-cache/wds/_git_preserved/index +0 -0
  172. package/lib/bmad-cache/wds/_git_preserved/objects/pack/pack-656c3d8d5426e73043b6a7f45eedaab74e3c419e.idx +0 -0
  173. package/lib/bmad-cache/wds/_git_preserved/objects/pack/{pack-96877c1c09123cccb1f91c1412184b11d2b492ad.pack → pack-656c3d8d5426e73043b6a7f45eedaab74e3c419e.pack} +0 -0
  174. package/lib/bmad-cache/wds/_git_preserved/objects/pack/pack-656c3d8d5426e73043b6a7f45eedaab74e3c419e.rev +0 -0
  175. package/lib/bmad-cache/wds/_git_preserved/packed-refs +1 -1
  176. package/lib/bmad-cache/wds/_git_preserved/refs/heads/main +1 -1
  177. package/lib/bmad-cache/wds/_git_preserved/refs/tags/v0.4.3 +1 -0
  178. package/lib/bmad-cache/wds/_git_preserved/shallow +1 -1
  179. package/lib/bmad-cache/wds/eslint.config.mjs +1 -1
  180. package/lib/bmad-cache/wds/package.json +1 -1
  181. package/lib/bmad-cache/wds/src/agents/wds-agent-freya-ux/customize.toml +80 -0
  182. package/lib/bmad-cache/wds/src/agents/wds-agent-mimir-builder/customize.toml +52 -0
  183. package/lib/bmad-cache/wds/src/agents/wds-agent-saga-analyst/customize.toml +70 -0
  184. package/lib/bmad-cache/wds/src/module-help.csv +19 -19
  185. package/lib/bmad-cache/wds/src/module.yaml +28 -0
  186. package/lib/bmad-cache/wds/src/scripts/README.md +155 -0
  187. package/lib/bmad-cache/wds/src/scripts/wds-add-object.js +202 -0
  188. package/lib/bmad-cache/wds/src/scripts/wds-add-spacing.js +158 -0
  189. package/lib/bmad-cache/wds/src/scripts/wds-init-page.js +229 -0
  190. package/lib/bmad-cache/wds/src/scripts/wds-init-scenario.js +120 -0
  191. package/lib/bmad-cache/wds/src/scripts/wds-nav.js +201 -0
  192. package/lib/bmad-cache/wds/src/scripts/wds-validate.js +301 -0
  193. package/lib/bmad-cache/wds/src/workflows/wds-3-scenarios/workflow.xml +450 -0
  194. package/lib/bmad-cache/wds/src/workflows/wds-4-ux-design/workflow-specify.xml +387 -0
  195. package/lib/bmad-extension/.claude-plugin/marketplace.json.template +1 -1
  196. package/lib/bmad-extension-plugin/.claude-plugin/marketplace.json +2 -2
  197. package/lib/bmad.js +91 -7
  198. package/lib/installer.js +28 -6
  199. package/lib/mil498-templates/OCD.md +169 -169
  200. package/lib/mil498-templates/README.md +4 -4
  201. package/lib/mil498-templates/SDD.md +163 -163
  202. package/lib/mil498-templates/SDP.md +307 -307
  203. package/lib/mil498-templates/SRS.md +219 -219
  204. package/lib/mil498-templates/SSDD.md +154 -154
  205. package/lib/mil498-templates/SSS.md +225 -225
  206. package/lib/mil498-templates/STD.md +188 -188
  207. package/lib/templates/instruction-block-git.template.md +25 -0
  208. package/package.json +5 -4
  209. package/scripts/build-bmad-cache.js +143 -42
  210. package/skills/git-workflow-skill/skill.json +21 -21
  211. package/lib/bmad-cache/bmb/_git_preserved/objects/pack/pack-6ecd9fc6445b1281449c5ec49a6c5794708e662e.idx +0 -0
  212. package/lib/bmad-cache/bmb/_git_preserved/objects/pack/pack-6ecd9fc6445b1281449c5ec49a6c5794708e662e.pack +0 -0
  213. package/lib/bmad-cache/bmb/_git_preserved/objects/pack/pack-6ecd9fc6445b1281449c5ec49a6c5794708e662e.rev +0 -0
  214. package/lib/bmad-cache/bmb/_git_preserved/refs/remotes/origin/HEAD +0 -1
  215. package/lib/bmad-cache/bmb/_git_preserved/refs/tags/v1.7.0 +0 -1
  216. package/lib/bmad-cache/bmb/skills/bmad-workflow-builder/scripts/generate-convert-report.py +0 -406
  217. package/lib/bmad-cache/bmb/skills/bmad-workflow-builder/scripts/tests/test_generate_convert_report.py +0 -243
  218. package/lib/bmad-cache/cis/_git_preserved/objects/pack/pack-42ffc048f54e58ce94c6331bc6be97ebbb7936f2.idx +0 -0
  219. package/lib/bmad-cache/cis/_git_preserved/objects/pack/pack-42ffc048f54e58ce94c6331bc6be97ebbb7936f2.rev +0 -0
  220. package/lib/bmad-cache/cis/_git_preserved/refs/remotes/origin/HEAD +0 -1
  221. package/lib/bmad-cache/gds/_git_preserved/objects/pack/pack-9427a146a90c00bb542cba038874bf9671ba4dc0.idx +0 -0
  222. package/lib/bmad-cache/gds/_git_preserved/objects/pack/pack-9427a146a90c00bb542cba038874bf9671ba4dc0.rev +0 -0
  223. package/lib/bmad-cache/gds/_git_preserved/refs/remotes/origin/HEAD +0 -1
  224. package/lib/bmad-cache/gds/src/workflows/2-design/gds-create-gdd/customize.toml +0 -41
  225. package/lib/bmad-cache/gds/src/workflows/2-design/gds-create-prd/customize.toml +0 -41
  226. package/lib/bmad-cache/gds/src/workflows/2-design/gds-create-prd/data/domain-complexity.csv +0 -15
  227. package/lib/bmad-cache/gds/src/workflows/2-design/gds-create-prd/data/project-types.csv +0 -11
  228. package/lib/bmad-cache/gds/src/workflows/2-design/gds-create-ux-design/customize.toml +0 -41
  229. package/lib/bmad-cache/gds/src/workflows/2-design/gds-edit-gdd/customize.toml +0 -41
  230. package/lib/bmad-cache/gds/src/workflows/2-design/gds-edit-prd/customize.toml +0 -41
  231. package/lib/bmad-cache/gds/src/workflows/2-design/gds-validate-gdd/customize.toml +0 -41
  232. package/lib/bmad-cache/gds/src/workflows/2-design/gds-validate-prd/customize.toml +0 -41
  233. package/lib/bmad-cache/gds/src/workflows/2-design/gds-validate-prd/data/domain-complexity.csv +0 -15
  234. package/lib/bmad-cache/gds/src/workflows/2-design/gds-validate-prd/data/project-types.csv +0 -11
  235. package/lib/bmad-cache/tea/_git_preserved/objects/pack/pack-f0df537f2649464ff6c5aee241165eb9c8664227.idx +0 -0
  236. package/lib/bmad-cache/tea/_git_preserved/objects/pack/pack-f0df537f2649464ff6c5aee241165eb9c8664227.rev +0 -0
  237. package/lib/bmad-cache/tea/_git_preserved/refs/remotes/origin/HEAD +0 -1
  238. package/lib/bmad-cache/wds/_git_preserved/objects/pack/pack-96877c1c09123cccb1f91c1412184b11d2b492ad.idx +0 -0
  239. package/lib/bmad-cache/wds/_git_preserved/objects/pack/pack-96877c1c09123cccb1f91c1412184b11d2b492ad.rev +0 -0
  240. package/lib/bmad-cache/wds/_git_preserved/refs/remotes/origin/HEAD +0 -1
  241. package/lib/bmad-cache/wds/src/agents/wds-agent-freya-ux/bmad-skill-manifest.yaml +0 -12
  242. package/lib/bmad-cache/wds/src/agents/wds-agent-saga-analyst/bmad-skill-manifest.yaml +0 -12
  243. package/lib/bmad-cache/wds/src/workflows/wds-0-alignment-signoff/bmad-skill-manifest.yaml +0 -1
  244. package/lib/bmad-cache/wds/src/workflows/wds-0-project-setup/bmad-skill-manifest.yaml +0 -1
  245. package/lib/bmad-cache/wds/src/workflows/wds-1-project-brief/bmad-skill-manifest.yaml +0 -1
  246. package/lib/bmad-cache/wds/src/workflows/wds-2-trigger-mapping/bmad-skill-manifest.yaml +0 -1
  247. package/lib/bmad-cache/wds/src/workflows/wds-3-scenarios/bmad-skill-manifest.yaml +0 -1
  248. package/lib/bmad-cache/wds/src/workflows/wds-4-ux-design/bmad-skill-manifest.yaml +0 -1
  249. package/lib/bmad-cache/wds/src/workflows/wds-5-agentic-development/bmad-skill-manifest.yaml +0 -1
  250. package/lib/bmad-cache/wds/src/workflows/wds-6-asset-generation/bmad-skill-manifest.yaml +0 -1
  251. package/lib/bmad-cache/wds/src/workflows/wds-7-design-system/bmad-skill-manifest.yaml +0 -1
  252. package/lib/bmad-cache/wds/src/workflows/wds-8-product-evolution/bmad-skill-manifest.yaml +0 -1
  253. /package/lib/bmad-cache/gds/src/workflows/2-design/{gds-create-gdd → gds-gdd/assets}/game-types.csv +0 -0
  254. /package/lib/bmad-cache/gds/src/workflows/2-design/{gds-validate-gdd/data → gds-gdd/assets}/genre-complexity.csv +0 -0
@@ -1,406 +0,0 @@
1
- #!/usr/bin/env python3
2
- # /// script
3
- # requires-python = ">=3.9"
4
- # ///
5
- """
6
- Generate an interactive HTML skill conversion comparison report.
7
-
8
- Measures original and rebuilt skill directories, combines with LLM-generated
9
- analysis (cuts, retained content, verdict), and renders a self-contained
10
- HTML report showing the stark before/after comparison.
11
-
12
- Usage:
13
- python3 generate-convert-report.py <original-path> <rebuilt-path> <analysis-json> [-o output.html] [--open]
14
- """
15
-
16
- from __future__ import annotations
17
-
18
- import argparse
19
- import html as html_lib
20
- import json
21
- import platform
22
- import subprocess
23
- import sys
24
- from datetime import datetime, timezone
25
- from pathlib import Path
26
-
27
-
28
- def measure_skill(skill_path: Path) -> dict:
29
- """Measure a skill directory or single file for lines, words, chars, sections, files."""
30
- total_lines = 0
31
- total_words = 0
32
- total_chars = 0
33
- total_sections = 0
34
- md_file_count = 0
35
- non_md_file_count = 0
36
-
37
- if skill_path.is_file():
38
- md_files = [skill_path]
39
- else:
40
- md_files = sorted(skill_path.rglob('*.md'))
41
-
42
- for f in md_files:
43
- content = f.read_text(encoding='utf-8')
44
- lines = content.splitlines()
45
- total_lines += len(lines)
46
- total_words += sum(len(line.split()) for line in lines)
47
- total_chars += len(content)
48
- total_sections += sum(1 for line in lines if line.startswith('## '))
49
- md_file_count += 1
50
-
51
- if skill_path.is_dir():
52
- for f in skill_path.rglob('*'):
53
- if f.is_file() and f.suffix != '.md':
54
- non_md_file_count += 1
55
-
56
- return {
57
- 'lines': total_lines,
58
- 'words': total_words,
59
- 'chars': total_chars,
60
- 'sections': total_sections,
61
- 'files': md_file_count + non_md_file_count,
62
- 'estimated_tokens': int(total_words * 1.3),
63
- }
64
-
65
-
66
- def calculate_reductions(original: dict, rebuilt: dict) -> dict:
67
- """Calculate percentage reductions for each metric."""
68
- reductions = {}
69
- for key in ('lines', 'words', 'chars', 'sections', 'estimated_tokens'):
70
- orig_val = original.get(key, 0)
71
- new_val = rebuilt.get(key, 0)
72
- if orig_val > 0:
73
- reductions[key] = f'{round((1 - new_val / orig_val) * 100)}%'
74
- else:
75
- reductions[key] = 'N/A'
76
- return reductions
77
-
78
-
79
- def build_report_data(original_metrics: dict, rebuilt_metrics: dict,
80
- analysis: dict, reductions: dict) -> dict:
81
- """Assemble the full report data structure."""
82
- return {
83
- 'meta': {
84
- 'skill_name': analysis.get('skill_name', 'Unknown'),
85
- 'original_source': analysis.get('original_source', ''),
86
- 'timestamp': datetime.now(timezone.utc).isoformat(),
87
- },
88
- 'metrics': {
89
- 'original': original_metrics,
90
- 'rebuilt': rebuilt_metrics,
91
- },
92
- 'reductions': reductions,
93
- 'cuts': analysis.get('cuts', []),
94
- 'retained': analysis.get('retained', []),
95
- 'verdict': analysis.get('verdict', ''),
96
- }
97
-
98
-
99
- # ── HTML Template ──────────────────────────────────────────────────────────────
100
-
101
- HTML_TEMPLATE = r"""<!DOCTYPE html>
102
- <html lang="en">
103
- <head>
104
- <meta charset="utf-8">
105
- <meta name="viewport" content="width=device-width, initial-scale=1">
106
- <title>BMad Method &middot; Skill Conversion: SKILL_NAME</title>
107
- <style>
108
- :root {
109
- --bg: #0d1117; --surface: #161b22; --surface2: #21262d; --border: #30363d;
110
- --text: #e6edf3; --text-muted: #8b949e; --text-dim: #6e7681;
111
- --critical: #f85149; --high: #f0883e; --medium: #d29922; --low: #58a6ff;
112
- --strength: #3fb950; --accent: #58a6ff; --accent-hover: #79c0ff;
113
- --purple: #a371f7;
114
- --font: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
115
- --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
116
- }
117
- @media (prefers-color-scheme: light) {
118
- :root {
119
- --bg: #ffffff; --surface: #f6f8fa; --surface2: #eaeef2; --border: #d0d7de;
120
- --text: #1f2328; --text-muted: #656d76; --text-dim: #8c959f;
121
- --critical: #cf222e; --high: #bc4c00; --medium: #9a6700; --low: #0969da;
122
- --strength: #1a7f37; --accent: #0969da; --accent-hover: #0550ae;
123
- --purple: #8250df;
124
- }
125
- }
126
- * { margin: 0; padding: 0; box-sizing: border-box; }
127
- body { font-family: var(--font); background: var(--bg); color: var(--text); line-height: 1.5; padding: 2rem; max-width: 900px; margin: 0 auto; }
128
- h1 { font-size: 1.5rem; margin-bottom: 0.25rem; }
129
- .subtitle { color: var(--text-muted); font-size: 0.85rem; margin-bottom: 1.5rem; }
130
- .hero { text-align: center; padding: 2rem 1rem; margin-bottom: 1.5rem; border: 1px solid var(--border); border-radius: 0.75rem; background: var(--surface); }
131
- .hero-pct { font-size: 4rem; font-weight: 800; color: var(--strength); line-height: 1; }
132
- .hero-label { font-size: 1.1rem; color: var(--text-muted); margin-top: 0.25rem; }
133
- .hero-sub { font-size: 0.9rem; color: var(--text-dim); margin-top: 0.5rem; }
134
- .metrics-table { width: 100%; border-collapse: collapse; margin: 1.5rem 0; }
135
- .metrics-table th { text-align: left; padding: 0.5rem 0.75rem; border-bottom: 2px solid var(--border); font-size: 0.8rem; text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-muted); }
136
- .metrics-table td { padding: 0.5rem 0.75rem; border-bottom: 1px solid var(--border); font-size: 0.95rem; }
137
- .metrics-table .num { font-family: var(--mono); text-align: right; }
138
- .metrics-table .reduction { font-weight: 700; color: var(--strength); text-align: right; }
139
- .bar-cell { width: 30%; }
140
- .bar-container { display: flex; height: 1.25rem; border-radius: 0.25rem; overflow: hidden; background: color-mix(in srgb, var(--critical) 15%, transparent); }
141
- .bar-rebuilt { background: var(--strength); border-radius: 0.25rem 0 0 0.25rem; transition: width 0.3s; }
142
- .section { border: 1px solid var(--border); border-radius: 0.5rem; margin: 0.75rem 0; overflow: hidden; }
143
- .section-header { display: flex; align-items: center; gap: 0.75rem; padding: 0.75rem 1rem; background: var(--surface); cursor: pointer; user-select: none; }
144
- .section-header:hover { background: var(--surface2); }
145
- .section-header .arrow { font-size: 0.7rem; transition: transform 0.15s; color: var(--text-muted); width: 1rem; }
146
- .section-header.open .arrow { transform: rotate(90deg); }
147
- .section-header .label { font-weight: 600; flex: 1; }
148
- .section-body { display: none; }
149
- .section-body.open { display: block; }
150
- .cut-item { padding: 0.75rem 1rem; border-top: 1px solid var(--border); }
151
- .cut-item:hover { background: var(--surface); }
152
- .cut-category { font-weight: 600; font-size: 0.95rem; }
153
- .cut-desc { font-size: 0.85rem; color: var(--text-muted); margin-top: 0.25rem; }
154
- .cut-examples { margin-top: 0.5rem; padding-left: 1.25rem; }
155
- .cut-examples li { font-size: 0.85rem; color: var(--text-dim); padding: 0.1rem 0; }
156
- .badge { display: inline-flex; align-items: center; padding: 0.15rem 0.5rem; border-radius: 2rem; font-size: 0.75rem; font-weight: 600; margin-right: 0.5rem; }
157
- .badge-high { background: color-mix(in srgb, var(--critical) 20%, transparent); color: var(--critical); }
158
- .badge-medium { background: color-mix(in srgb, var(--medium) 20%, transparent); color: var(--medium); }
159
- .badge-low { background: color-mix(in srgb, var(--low) 20%, transparent); color: var(--low); }
160
- .retained-item { padding: 0.5rem 1rem; border-top: 1px solid var(--border); }
161
- .retained-category { font-weight: 600; font-size: 0.9rem; color: var(--strength); }
162
- .retained-desc { font-size: 0.85rem; color: var(--text-muted); }
163
- .verdict { margin-top: 1.5rem; padding: 1.25rem; border: 1px solid var(--border); border-radius: 0.5rem; background: var(--surface); font-size: 1rem; line-height: 1.6; color: var(--text); font-style: italic; }
164
- .verdict::before { content: "Bottom line: "; font-weight: 700; font-style: normal; color: var(--purple); }
165
- .footer { margin-top: 2rem; padding-top: 1rem; border-top: 1px solid var(--border); font-size: 0.8rem; color: var(--text-dim); text-align: center; }
166
- </style>
167
- </head>
168
- <body>
169
-
170
- <div style="color:var(--purple);font-size:0.8rem;font-weight:600;letter-spacing:0.05em;text-transform:uppercase;margin-bottom:0.25rem">BMad Method</div>
171
- <h1>Skill Conversion: <span id="skill-name"></span></h1>
172
- <div class="subtitle" id="subtitle"></div>
173
-
174
- <div class="hero" id="hero"></div>
175
-
176
- <table class="metrics-table">
177
- <thead>
178
- <tr>
179
- <th>Metric</th>
180
- <th class="num">Original</th>
181
- <th class="num">Rebuilt</th>
182
- <th class="num">Reduction</th>
183
- <th class="bar-cell">Comparison</th>
184
- </tr>
185
- </thead>
186
- <tbody id="metrics-body"></tbody>
187
- </table>
188
-
189
- <div id="cuts-section"></div>
190
- <div id="retained-section"></div>
191
- <div class="verdict" id="verdict"></div>
192
-
193
- <div class="footer">
194
- Generated by <strong>BMad Workflow Builder</strong> &middot; <code>--convert</code>
195
- </div>
196
-
197
- <script>
198
- const DATA = JSON.parse(document.getElementById('report-data').textContent);
199
-
200
- function esc(s) {
201
- if (!s) return '';
202
- const d = document.createElement('div');
203
- d.textContent = String(s);
204
- return d.innerHTML;
205
- }
206
- function fmt(n) { return typeof n === 'number' ? n.toLocaleString() : String(n); }
207
-
208
- function init() {
209
- const m = DATA.meta;
210
- document.getElementById('skill-name').textContent = m.skill_name;
211
- const parts = [m.original_source, m.timestamp ? m.timestamp.split('T')[0] : ''].filter(Boolean);
212
- document.getElementById('subtitle').textContent = parts.join(' \u2022 ');
213
-
214
- // Hero — overall token reduction
215
- const tokenRed = DATA.reductions.estimated_tokens || DATA.reductions.words || '0%';
216
- const origTok = DATA.metrics.original.estimated_tokens || 0;
217
- const newTok = DATA.metrics.rebuilt.estimated_tokens || 0;
218
- document.getElementById('hero').innerHTML =
219
- '<div class="hero-pct">' + esc(tokenRed) + '</div>' +
220
- '<div class="hero-label">leaner</div>' +
221
- '<div class="hero-sub">' + fmt(origTok) + ' tokens \u2192 ' + fmt(newTok) + ' tokens</div>';
222
-
223
- // Metrics table
224
- var rows = [
225
- ['Lines', 'lines'], ['Words', 'words'], ['Characters', 'chars'],
226
- ['Sections', 'sections'], ['Files', 'files'], ['Est. Tokens', 'estimated_tokens']
227
- ];
228
- var tbody = '';
229
- rows.forEach(function(r) {
230
- var label = r[0], key = r[1];
231
- var orig = DATA.metrics.original[key] || 0;
232
- var rebuilt = DATA.metrics.rebuilt[key] || 0;
233
- var reduction = DATA.reductions[key] || (key === 'files' ? '' : 'N/A');
234
- var pct = orig > 0 ? (rebuilt / orig * 100) : 0;
235
- tbody += '<tr>';
236
- tbody += '<td>' + label + '</td>';
237
- tbody += '<td class="num">' + fmt(orig) + '</td>';
238
- tbody += '<td class="num">' + fmt(rebuilt) + '</td>';
239
- tbody += '<td class="reduction">' + (reduction || '') + '</td>';
240
- tbody += '<td class="bar-cell"><div class="bar-container">';
241
- tbody += '<div class="bar-rebuilt" style="width:' + pct.toFixed(1) + '%"></div>';
242
- tbody += '</div></td>';
243
- tbody += '</tr>';
244
- });
245
- document.getElementById('metrics-body').innerHTML = tbody;
246
-
247
- renderCuts();
248
- renderRetained();
249
-
250
- // Verdict
251
- var v = DATA.verdict || '';
252
- if (v) document.getElementById('verdict').appendChild(document.createTextNode(v));
253
- else document.getElementById('verdict').style.display = 'none';
254
- }
255
-
256
- function renderCuts() {
257
- var cuts = DATA.cuts || [];
258
- if (!cuts.length) return;
259
- var html = '<div class="section"><div class="section-header open" onclick="toggle(this)">';
260
- html += '<span class="arrow">&#9654;</span>';
261
- html += '<span class="label">What Was Cut (' + cuts.length + ' categories)</span>';
262
- html += '</div><div class="section-body open">';
263
- cuts.forEach(function(cut) {
264
- html += '<div class="cut-item">';
265
- var sev = cut.severity || 'medium';
266
- html += '<span class="badge badge-' + sev + '">' + esc(sev) + '</span>';
267
- html += '<span class="cut-category">' + esc(cut.category) + '</span>';
268
- html += '<div class="cut-desc">' + esc(cut.description) + '</div>';
269
- if (cut.examples && cut.examples.length) {
270
- html += '<ul class="cut-examples">';
271
- cut.examples.forEach(function(ex) { html += '<li>' + esc(ex) + '</li>'; });
272
- html += '</ul>';
273
- }
274
- html += '</div>';
275
- });
276
- html += '</div></div>';
277
- document.getElementById('cuts-section').innerHTML = html;
278
- }
279
-
280
- function renderRetained() {
281
- var items = DATA.retained || [];
282
- if (!items.length) return;
283
- var html = '<div class="section"><div class="section-header open" onclick="toggle(this)">';
284
- html += '<span class="arrow">&#9654;</span>';
285
- html += '<span class="label">What Survived (' + items.length + ' categories)</span>';
286
- html += '</div><div class="section-body open">';
287
- items.forEach(function(r) {
288
- html += '<div class="retained-item">';
289
- html += '<div class="retained-category">' + esc(r.category) + '</div>';
290
- html += '<div class="retained-desc">' + esc(r.description) + '</div>';
291
- html += '</div>';
292
- });
293
- html += '</div></div>';
294
- document.getElementById('retained-section').innerHTML = html;
295
- }
296
-
297
- function toggle(el) {
298
- el.classList.toggle('open');
299
- el.nextElementSibling.classList.toggle('open');
300
- }
301
-
302
- init();
303
- </script>
304
- </body>
305
- </html>"""
306
-
307
-
308
- def generate_html(report_data: dict) -> str:
309
- """Inject report data into the HTML template."""
310
- data_json = json.dumps(report_data, indent=None, ensure_ascii=False)
311
- data_tag = f'<script id="report-data" type="application/json">{data_json}</script>'
312
- html = HTML_TEMPLATE.replace(
313
- '<script>\nconst DATA',
314
- f'{data_tag}\n<script>\nconst DATA',
315
- )
316
- skill_name = report_data.get('meta', {}).get('skill_name', 'Unknown')
317
- html = html.replace('SKILL_NAME', html_lib.escape(skill_name))
318
- return html
319
-
320
-
321
- def main() -> int:
322
- parser = argparse.ArgumentParser(
323
- description='Generate an interactive HTML skill conversion comparison report',
324
- )
325
- parser.add_argument(
326
- 'original_path',
327
- type=Path,
328
- help='Path to original skill (directory or single .md file)',
329
- )
330
- parser.add_argument(
331
- 'rebuilt_path',
332
- type=Path,
333
- help='Path to rebuilt skill directory',
334
- )
335
- parser.add_argument(
336
- 'analysis_json',
337
- type=Path,
338
- help='Path to LLM-generated convert-analysis.json',
339
- )
340
- parser.add_argument(
341
- '--output', '-o',
342
- type=Path,
343
- help='Output HTML file path (default: <analysis-dir>/convert-report.html)',
344
- )
345
- parser.add_argument(
346
- '--open',
347
- action='store_true',
348
- help='Open the HTML report in the default browser',
349
- )
350
- args = parser.parse_args()
351
-
352
- # Validate inputs
353
- for label, path in [('Original', args.original_path),
354
- ('Rebuilt', args.rebuilt_path),
355
- ('Analysis', args.analysis_json)]:
356
- if not path.exists():
357
- print(f'Error: {label} path not found: {path}', file=sys.stderr)
358
- return 2
359
-
360
- # Measure both skills
361
- original_metrics = measure_skill(args.original_path)
362
- rebuilt_metrics = measure_skill(args.rebuilt_path)
363
- reductions = calculate_reductions(original_metrics, rebuilt_metrics)
364
-
365
- # Load LLM analysis
366
- analysis = json.loads(args.analysis_json.read_text(encoding='utf-8'))
367
-
368
- # Build report data
369
- report_data = build_report_data(
370
- original_metrics, rebuilt_metrics, analysis, reductions,
371
- )
372
-
373
- # Save structured report data alongside analysis
374
- report_data_path = args.analysis_json.parent / 'convert-report-data.json'
375
- report_data_path.write_text(
376
- json.dumps(report_data, indent=2, ensure_ascii=False),
377
- encoding='utf-8',
378
- )
379
-
380
- # Generate HTML
381
- html = generate_html(report_data)
382
- output_path = args.output or (args.analysis_json.parent / 'convert-report.html')
383
- output_path.write_text(html, encoding='utf-8')
384
-
385
- # Summary to stdout
386
- print(json.dumps({
387
- 'html_report': str(output_path),
388
- 'original': original_metrics,
389
- 'rebuilt': rebuilt_metrics,
390
- 'reductions': reductions,
391
- }))
392
-
393
- if args.open:
394
- system = platform.system()
395
- if system == 'Darwin':
396
- subprocess.run(['open', str(output_path)])
397
- elif system == 'Linux':
398
- subprocess.run(['xdg-open', str(output_path)])
399
- elif system == 'Windows':
400
- subprocess.run(['start', str(output_path)], shell=True)
401
-
402
- return 0
403
-
404
-
405
- if __name__ == '__main__':
406
- sys.exit(main())
@@ -1,243 +0,0 @@
1
- #!/usr/bin/env python3
2
- # /// script
3
- # requires-python = ">=3.9"
4
- # ///
5
- """Tests for generate-convert-report.py."""
6
-
7
- from __future__ import annotations
8
-
9
- import json
10
- import sys
11
- import tempfile
12
- from importlib.util import module_from_spec, spec_from_file_location
13
- from pathlib import Path
14
-
15
- # Load the script as a module
16
- _script_path = Path(__file__).resolve().parent.parent / 'generate-convert-report.py'
17
- _spec = spec_from_file_location('generate_convert_report', _script_path)
18
- _mod = module_from_spec(_spec)
19
- _spec.loader.exec_module(_mod)
20
-
21
- measure_skill = _mod.measure_skill
22
- calculate_reductions = _mod.calculate_reductions
23
- build_report_data = _mod.build_report_data
24
- generate_html = _mod.generate_html
25
-
26
-
27
- def test_measure_skill_single_file():
28
- """Measure a single .md file."""
29
- with tempfile.TemporaryDirectory() as td:
30
- p = Path(td) / 'SKILL.md'
31
- p.write_text('## Section One\n\nSome words here.\n\n## Section Two\n\nMore words.\n')
32
- result = measure_skill(p)
33
- assert result['lines'] == 7, f"Expected 7 lines, got {result['lines']}"
34
- assert result['sections'] == 2, f"Expected 2 sections, got {result['sections']}"
35
- assert result['files'] == 1
36
- assert result['estimated_tokens'] > 0
37
- assert result['words'] > 0
38
- assert result['chars'] > 0
39
-
40
-
41
- def test_measure_skill_directory():
42
- """Measure a directory with multiple .md files."""
43
- with tempfile.TemporaryDirectory() as td:
44
- td_path = Path(td)
45
- (td_path / 'SKILL.md').write_text('## Overview\n\nHello world.\n')
46
- refs = td_path / 'references'
47
- refs.mkdir()
48
- (refs / 'ref.md').write_text('## Reference\n\nSome reference content.\n')
49
- result = measure_skill(td_path)
50
- assert result['lines'] == 6, f"Expected 6 lines, got {result['lines']}"
51
- assert result['sections'] == 2
52
- assert result['files'] == 2
53
-
54
-
55
- def test_measure_skill_with_non_md_files():
56
- """Non-.md files count toward file total but not line/word/section counts."""
57
- with tempfile.TemporaryDirectory() as td:
58
- td_path = Path(td)
59
- (td_path / 'SKILL.md').write_text('## Overview\n\nHello.\n')
60
- scripts = td_path / 'scripts'
61
- scripts.mkdir()
62
- (scripts / 'run.py').write_text('print("hello")\n')
63
- result = measure_skill(td_path)
64
- assert result['files'] == 2, f"Expected 2 files, got {result['files']}"
65
- assert result['lines'] == 3, f"Expected 3 lines (only .md), got {result['lines']}"
66
-
67
-
68
- def test_calculate_reductions():
69
- """Calculate reduction percentages."""
70
- original = {'lines': 800, 'words': 5000, 'chars': 30000, 'sections': 30, 'estimated_tokens': 6500}
71
- rebuilt = {'lines': 80, 'words': 500, 'chars': 3000, 'sections': 6, 'estimated_tokens': 650}
72
- r = calculate_reductions(original, rebuilt)
73
- assert r['lines'] == '90%'
74
- assert r['words'] == '90%'
75
- assert r['chars'] == '90%'
76
- assert r['sections'] == '80%'
77
- assert r['estimated_tokens'] == '90%'
78
-
79
-
80
- def test_calculate_reductions_zero_original():
81
- """Handle zero values gracefully."""
82
- original = {'lines': 0, 'words': 100, 'chars': 500, 'sections': 0, 'estimated_tokens': 130}
83
- rebuilt = {'lines': 0, 'words': 50, 'chars': 250, 'sections': 0, 'estimated_tokens': 65}
84
- r = calculate_reductions(original, rebuilt)
85
- assert r['lines'] == 'N/A'
86
- assert r['words'] == '50%'
87
- assert r['sections'] == 'N/A'
88
-
89
-
90
- def test_calculate_reductions_no_change():
91
- """No reduction yields 0%."""
92
- original = {'lines': 100, 'words': 500, 'chars': 3000, 'sections': 5, 'estimated_tokens': 650}
93
- r = calculate_reductions(original, original)
94
- assert r['lines'] == '0%'
95
- assert r['words'] == '0%'
96
-
97
-
98
- def test_build_report_data():
99
- """Assemble report data with all fields."""
100
- analysis = {
101
- 'skill_name': 'test-skill',
102
- 'original_source': '/path/to/original',
103
- 'cuts': [{'category': 'Bloat', 'description': 'Removed bloat', 'examples': ['x'], 'severity': 'high'}],
104
- 'retained': [{'category': 'Core', 'description': 'Kept core'}],
105
- 'verdict': 'Much better now.',
106
- }
107
- data = build_report_data(
108
- {'lines': 100, 'words': 500, 'chars': 3000, 'sections': 10, 'files': 1, 'estimated_tokens': 650},
109
- {'lines': 20, 'words': 100, 'chars': 600, 'sections': 3, 'files': 1, 'estimated_tokens': 130},
110
- analysis,
111
- {'lines': '80%', 'words': '80%', 'chars': '80%', 'sections': '70%', 'estimated_tokens': '80%'},
112
- )
113
- assert data['meta']['skill_name'] == 'test-skill'
114
- assert data['meta']['original_source'] == '/path/to/original'
115
- assert 'timestamp' in data['meta']
116
- assert data['metrics']['original']['lines'] == 100
117
- assert data['metrics']['rebuilt']['lines'] == 20
118
- assert data['reductions']['lines'] == '80%'
119
- assert len(data['cuts']) == 1
120
- assert data['cuts'][0]['category'] == 'Bloat'
121
- assert len(data['retained']) == 1
122
- assert data['verdict'] == 'Much better now.'
123
-
124
-
125
- def test_build_report_data_missing_fields():
126
- """Handle analysis with missing optional fields."""
127
- analysis = {'skill_name': 'minimal'}
128
- data = build_report_data({}, {}, analysis, {})
129
- assert data['meta']['skill_name'] == 'minimal'
130
- assert data['cuts'] == []
131
- assert data['retained'] == []
132
- assert data['verdict'] == ''
133
-
134
-
135
- def test_generate_html_structure():
136
- """Generated HTML is valid and contains key elements."""
137
- report_data = {
138
- 'meta': {'skill_name': 'test-skill', 'original_source': 'http://example.com', 'timestamp': '2026-01-01T00:00:00Z'},
139
- 'metrics': {
140
- 'original': {'lines': 100, 'words': 500, 'chars': 3000, 'sections': 10, 'files': 1, 'estimated_tokens': 650},
141
- 'rebuilt': {'lines': 20, 'words': 100, 'chars': 600, 'sections': 3, 'files': 1, 'estimated_tokens': 130},
142
- },
143
- 'reductions': {'lines': '80%', 'words': '80%', 'chars': '80%', 'sections': '70%', 'estimated_tokens': '80%'},
144
- 'cuts': [{'category': 'Waste', 'description': 'Pure waste', 'examples': ['ex1'], 'severity': 'high'}],
145
- 'retained': [{'category': 'Core', 'description': 'Essential'}],
146
- 'verdict': 'Dramatically improved.',
147
- }
148
- html = generate_html(report_data)
149
- assert '<!DOCTYPE html>' in html
150
- assert 'report-data' in html
151
- assert 'test-skill' in html
152
- assert 'BMad Method' in html
153
- assert 'Skill Conversion' in html
154
- assert '--convert' in html
155
-
156
-
157
- def test_generate_html_escapes_data():
158
- """Verify data is embedded as JSON, not raw HTML."""
159
- report_data = {
160
- 'meta': {'skill_name': '<script>alert("xss")</script>', 'original_source': '', 'timestamp': ''},
161
- 'metrics': {'original': {}, 'rebuilt': {}},
162
- 'reductions': {},
163
- 'cuts': [],
164
- 'retained': [],
165
- 'verdict': '',
166
- }
167
- html = generate_html(report_data)
168
- # The skill name in the JSON should be escaped by json.dumps
169
- assert '<script>alert' not in html.split('application/json')[0]
170
-
171
-
172
- def test_end_to_end():
173
- """Full pipeline: create files, measure, analyze, generate HTML."""
174
- with tempfile.TemporaryDirectory() as td:
175
- td_path = Path(td)
176
-
177
- # Original skill — verbose
178
- orig_dir = td_path / 'original'
179
- orig_dir.mkdir()
180
- (orig_dir / 'SKILL.md').write_text(
181
- '## Section 1\n\n' + 'word ' * 500 + '\n\n'
182
- '## Section 2\n\nMore verbose content.\n\n'
183
- '## Section 3\n\nEven more.\n',
184
- )
185
-
186
- # Rebuilt skill — lean
187
- rebuilt_dir = td_path / 'rebuilt'
188
- rebuilt_dir.mkdir()
189
- (rebuilt_dir / 'SKILL.md').write_text('## Core\n\nLean and effective.\n')
190
-
191
- # Measure
192
- orig_m = measure_skill(orig_dir)
193
- rebuilt_m = measure_skill(rebuilt_dir)
194
- reductions = calculate_reductions(orig_m, rebuilt_m)
195
-
196
- assert orig_m['words'] > rebuilt_m['words']
197
- assert orig_m['sections'] > rebuilt_m['sections']
198
-
199
- # Analysis
200
- analysis = {
201
- 'skill_name': 'e2e-test',
202
- 'original_source': str(orig_dir),
203
- 'cuts': [
204
- {'category': 'Bloat', 'description': 'Removed verbose filler', 'examples': ['500 repeated words'], 'severity': 'high'},
205
- ],
206
- 'retained': [
207
- {'category': 'Core Intent', 'description': 'Essential behavioral instructions'},
208
- ],
209
- 'verdict': 'Converted successfully.',
210
- }
211
-
212
- report_data = build_report_data(orig_m, rebuilt_m, analysis, reductions)
213
- html = generate_html(report_data)
214
-
215
- # Write and verify
216
- out = td_path / 'report.html'
217
- out.write_text(html, encoding='utf-8')
218
- assert out.exists()
219
- assert out.stat().st_size > 1000
220
-
221
- # Verify report data roundtrips
222
- data_file = td_path / 'report-data.json'
223
- data_file.write_text(json.dumps(report_data, indent=2), encoding='utf-8')
224
- loaded = json.loads(data_file.read_text(encoding='utf-8'))
225
- assert loaded['meta']['skill_name'] == 'e2e-test'
226
- assert len(loaded['cuts']) == 1
227
- assert int(reductions['words'].rstrip('%')) > 50
228
-
229
-
230
- if __name__ == '__main__':
231
- tests = [name for name in sorted(dir()) if name.startswith('test_')]
232
- passed = 0
233
- failed = 0
234
- for name in tests:
235
- try:
236
- globals()[name]()
237
- print(f' PASS {name}')
238
- passed += 1
239
- except Exception as e:
240
- print(f' FAIL {name}: {e}')
241
- failed += 1
242
- print(f'\n{passed} passed, {failed} failed')
243
- sys.exit(1 if failed else 0)
@@ -1 +0,0 @@
1
- ref: refs/remotes/origin/main
@@ -1 +0,0 @@
1
- ref: refs/remotes/origin/main