bmad-method-quarkus 1.0.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 (380) hide show
  1. package/.claude-plugin/marketplace.json +53 -0
  2. package/AGENTS.md +12 -0
  3. package/LICENSE +30 -0
  4. package/README.md +94 -0
  5. package/README_CN.md +108 -0
  6. package/README_VN.md +109 -0
  7. package/bmad-modules.yaml +133 -0
  8. package/package.json +123 -0
  9. package/removals.txt +87 -0
  10. package/src/bmm-skills/agents/bmad-agent-analyst/SKILL.md +76 -0
  11. package/src/bmm-skills/agents/bmad-agent-analyst/customize.toml +105 -0
  12. package/src/bmm-skills/agents/bmad-agent-architect/SKILL.md +76 -0
  13. package/src/bmm-skills/agents/bmad-agent-architect/customize.toml +65 -0
  14. package/src/bmm-skills/agents/bmad-agent-dev/SKILL.md +76 -0
  15. package/src/bmm-skills/agents/bmad-agent-dev/customize.toml +83 -0
  16. package/src/bmm-skills/agents/bmad-agent-pm/SKILL.md +76 -0
  17. package/src/bmm-skills/agents/bmad-agent-pm/customize.toml +75 -0
  18. package/src/bmm-skills/agents/bmad-agent-ux-designer/SKILL.md +76 -0
  19. package/src/bmm-skills/agents/bmad-agent-ux-designer/customize.toml +60 -0
  20. package/src/bmm-skills/agents/bmad-quarkus-architect/.memlog.md +12 -0
  21. package/src/bmm-skills/agents/bmad-quarkus-architect/.memlog.md:Zone.Identifier +0 -0
  22. package/src/bmm-skills/agents/bmad-quarkus-architect/SKILL.md +86 -0
  23. package/src/bmm-skills/agents/bmad-quarkus-architect/SKILL.md:Zone.Identifier +0 -0
  24. package/src/bmm-skills/agents/bmad-quarkus-architect/customize.toml +37 -0
  25. package/src/bmm-skills/agents/bmad-quarkus-architect/customize.toml:Zone.Identifier +0 -0
  26. package/src/bmm-skills/agents/bmad-quarkus-architect/references/enrich-stories.md +19 -0
  27. package/src/bmm-skills/agents/bmad-quarkus-architect/references/enrich-stories.md:Zone.Identifier +0 -0
  28. package/src/bmm-skills/agents/bmad-quarkus-architect/references/prompt-quality-canon.md +79 -0
  29. package/src/bmm-skills/agents/bmad-quarkus-architect/references/prompt-quality-canon.md:Zone.Identifier +0 -0
  30. package/src/bmm-skills/agents/bmad-quarkus-architect/skills/bqa-setup/SKILL.md +80 -0
  31. package/src/bmm-skills/agents/bmad-quarkus-architect/skills/bqa-setup/SKILL.md:Zone.Identifier +0 -0
  32. package/src/bmm-skills/agents/bmad-quarkus-architect/skills/bqa-setup/assets/module-help.csv +9 -0
  33. package/src/bmm-skills/agents/bmad-quarkus-architect/skills/bqa-setup/assets/module-help.csv:Zone.Identifier +0 -0
  34. package/src/bmm-skills/agents/bmad-quarkus-architect/skills/bqa-setup/assets/module.yaml +16 -0
  35. package/src/bmm-skills/agents/bmad-quarkus-architect/skills/bqa-setup/assets/module.yaml:Zone.Identifier +0 -0
  36. package/src/bmm-skills/agents/bmad-quarkus-architect/skills/bqa-setup/scripts/cleanup-legacy.py +287 -0
  37. package/src/bmm-skills/agents/bmad-quarkus-architect/skills/bqa-setup/scripts/cleanup-legacy.py:Zone.Identifier +0 -0
  38. package/src/bmm-skills/agents/bmad-quarkus-architect/skills/bqa-setup/scripts/merge-config.py +441 -0
  39. package/src/bmm-skills/agents/bmad-quarkus-architect/skills/bqa-setup/scripts/merge-config.py:Zone.Identifier +0 -0
  40. package/src/bmm-skills/agents/bmad-quarkus-architect/skills/bqa-setup/scripts/merge-help-csv.py +246 -0
  41. package/src/bmm-skills/agents/bmad-quarkus-architect/skills/bqa-setup/scripts/merge-help-csv.py:Zone.Identifier +0 -0
  42. package/src/bmm-skills/agents/bmad-quarkus-architect/skills/quarkus-error-handling-i18n/SKILL.md +181 -0
  43. package/src/bmm-skills/agents/bmad-quarkus-architect/skills/quarkus-error-handling-i18n/SKILL.md:Zone.Identifier +0 -0
  44. package/src/bmm-skills/agents/bmad-quarkus-architect/skills/quarkus-grpc-services/SKILL.md +141 -0
  45. package/src/bmm-skills/agents/bmad-quarkus-architect/skills/quarkus-grpc-services/SKILL.md:Zone.Identifier +0 -0
  46. package/src/bmm-skills/agents/bmad-quarkus-architect/skills/quarkus-hexagonal-core/SKILL.md +392 -0
  47. package/src/bmm-skills/agents/bmad-quarkus-architect/skills/quarkus-hexagonal-core/SKILL.md:Zone.Identifier +0 -0
  48. package/src/bmm-skills/agents/bmad-quarkus-architect/skills/quarkus-kafka-messaging/SKILL.md +148 -0
  49. package/src/bmm-skills/agents/bmad-quarkus-architect/skills/quarkus-kafka-messaging/SKILL.md:Zone.Identifier +0 -0
  50. package/src/bmm-skills/agents/bmad-quarkus-architect/skills/quarkus-observability-otel/SKILL.md +180 -0
  51. package/src/bmm-skills/agents/bmad-quarkus-architect/skills/quarkus-observability-otel/SKILL.md:Zone.Identifier +0 -0
  52. package/src/bmm-skills/agents/bmad-quarkus-architect/skills/quarkus-openapi-tmforum/SKILL.md +128 -0
  53. package/src/bmm-skills/agents/bmad-quarkus-architect/skills/quarkus-openapi-tmforum/SKILL.md:Zone.Identifier +0 -0
  54. package/src/bmm-skills/agents/bmad-quarkus-architect/skills/quarkus-sql-jdbc-agroal/SKILL.md +245 -0
  55. package/src/bmm-skills/agents/bmad-quarkus-architect/skills/quarkus-sql-jdbc-agroal/SKILL.md:Zone.Identifier +0 -0
  56. package/src/bmm-skills/module-help.csv +19 -0
  57. package/src/bmm-skills/module.yaml +92 -0
  58. package/src/bmm-skills/plan/bmad-architecture/SKILL.md +85 -0
  59. package/src/bmm-skills/plan/bmad-architecture/assets/spine-template.md +79 -0
  60. package/src/bmm-skills/plan/bmad-architecture/customize.toml +102 -0
  61. package/src/bmm-skills/plan/bmad-architecture/references/headless.md +26 -0
  62. package/src/bmm-skills/plan/bmad-architecture/references/reviewer-gate.md +13 -0
  63. package/src/bmm-skills/plan/bmad-architecture/scripts/lint_spine.py +257 -0
  64. package/src/bmm-skills/plan/bmad-architecture/scripts/tests/test_lint_spine.py +270 -0
  65. package/src/bmm-skills/plan/bmad-create-epics-and-stories/SKILL.md +93 -0
  66. package/src/bmm-skills/plan/bmad-create-epics-and-stories/customize.toml +41 -0
  67. package/src/bmm-skills/plan/bmad-create-epics-and-stories/steps/step-01-validate-prerequisites.md +263 -0
  68. package/src/bmm-skills/plan/bmad-create-epics-and-stories/steps/step-02-design-epics.md +242 -0
  69. package/src/bmm-skills/plan/bmad-create-epics-and-stories/steps/step-03-create-stories.md +255 -0
  70. package/src/bmm-skills/plan/bmad-create-epics-and-stories/steps/step-04-final-validation.md +143 -0
  71. package/src/bmm-skills/plan/bmad-create-epics-and-stories/templates/epics-template.md +61 -0
  72. package/src/bmm-skills/plan/bmad-generate-project-context/SKILL.md +10 -0
  73. package/src/bmm-skills/plan/bmad-prd/SKILL.md +94 -0
  74. package/src/bmm-skills/plan/bmad-prd/assets/headless-schemas.md +76 -0
  75. package/src/bmm-skills/plan/bmad-prd/assets/prd-template.md +165 -0
  76. package/src/bmm-skills/plan/bmad-prd/assets/prd-validation-checklist.md +135 -0
  77. package/src/bmm-skills/plan/bmad-prd/assets/validation-report-template.html +325 -0
  78. package/src/bmm-skills/plan/bmad-prd/customize.toml +149 -0
  79. package/src/bmm-skills/plan/bmad-prd/references/headless.md +39 -0
  80. package/src/bmm-skills/plan/bmad-prd/references/validate.md +97 -0
  81. package/src/bmm-skills/plan/bmad-prfaq/SKILL.md +135 -0
  82. package/src/bmm-skills/plan/bmad-prfaq/agents/artifact-analyzer.md +60 -0
  83. package/src/bmm-skills/plan/bmad-prfaq/agents/web-researcher.md +49 -0
  84. package/src/bmm-skills/plan/bmad-prfaq/assets/prfaq-template.md +62 -0
  85. package/src/bmm-skills/plan/bmad-prfaq/bmad-manifest.json +16 -0
  86. package/src/bmm-skills/plan/bmad-prfaq/customize.toml +41 -0
  87. package/src/bmm-skills/plan/bmad-prfaq/references/customer-faq.md +55 -0
  88. package/src/bmm-skills/plan/bmad-prfaq/references/internal-faq.md +51 -0
  89. package/src/bmm-skills/plan/bmad-prfaq/references/press-release.md +60 -0
  90. package/src/bmm-skills/plan/bmad-prfaq/references/verdict.md +83 -0
  91. package/src/bmm-skills/plan/bmad-product-brief/SKILL.md +91 -0
  92. package/src/bmm-skills/plan/bmad-product-brief/assets/brief-template.md +41 -0
  93. package/src/bmm-skills/plan/bmad-product-brief/customize.toml +101 -0
  94. package/src/bmm-skills/plan/bmad-project-context/SKILL.md +110 -0
  95. package/src/bmm-skills/plan/bmad-project-context/customize.toml +24 -0
  96. package/src/bmm-skills/plan/bmad-project-context/references/best-practices.md +65 -0
  97. package/src/bmm-skills/plan/bmad-project-context/references/template.md +55 -0
  98. package/src/bmm-skills/plan/bmad-spec/SKILL.md +160 -0
  99. package/src/bmm-skills/plan/bmad-spec/assets/headless-schemas.md +33 -0
  100. package/src/bmm-skills/plan/bmad-spec/assets/spec-template.md +49 -0
  101. package/src/bmm-skills/plan/bmad-spec/assets/stories-schema.md +44 -0
  102. package/src/bmm-skills/plan/bmad-spec/customize.toml +52 -0
  103. package/src/bmm-skills/plan/bmad-sprint-planning/SKILL.md +62 -0
  104. package/src/bmm-skills/plan/bmad-sprint-planning/customize.toml +41 -0
  105. package/src/bmm-skills/plan/bmad-sprint-planning/references/fix-sprint-status.md +30 -0
  106. package/src/bmm-skills/plan/bmad-sprint-planning/references/generate-tracking.md +25 -0
  107. package/src/bmm-skills/plan/bmad-sprint-planning/references/readiness-gate.md +20 -0
  108. package/src/bmm-skills/plan/bmad-sprint-planning/references/status-view.md +14 -0
  109. package/src/bmm-skills/plan/bmad-sprint-planning/references/validate.md +10 -0
  110. package/src/bmm-skills/plan/bmad-sprint-planning/scripts/__pycache__/sprint_plan.cpython-311.pyc +0 -0
  111. package/src/bmm-skills/plan/bmad-sprint-planning/scripts/sprint_plan.py +697 -0
  112. package/src/bmm-skills/plan/bmad-sprint-planning/scripts/tests/__pycache__/test_sprint_plan.cpython-311-pytest-9.1.1.pyc +0 -0
  113. package/src/bmm-skills/plan/bmad-sprint-planning/scripts/tests/test_sprint_plan.py +524 -0
  114. package/src/bmm-skills/plan/bmad-sprint-planning/sprint-status-template.yaml +71 -0
  115. package/src/bmm-skills/plan/bmad-ux/SKILL.md +90 -0
  116. package/src/bmm-skills/plan/bmad-ux/assets/color-themes.md +9 -0
  117. package/src/bmm-skills/plan/bmad-ux/assets/design-directions.md +9 -0
  118. package/src/bmm-skills/plan/bmad-ux/assets/design-example-editorial.md +158 -0
  119. package/src/bmm-skills/plan/bmad-ux/assets/design-example-mobile.md +93 -0
  120. package/src/bmm-skills/plan/bmad-ux/assets/design-example-shadcn.md +109 -0
  121. package/src/bmm-skills/plan/bmad-ux/assets/excalidraw-wireframe.md +19 -0
  122. package/src/bmm-skills/plan/bmad-ux/assets/experience-example-mobile.md +112 -0
  123. package/src/bmm-skills/plan/bmad-ux/assets/experience-example-shadcn.md +133 -0
  124. package/src/bmm-skills/plan/bmad-ux/assets/headless-schemas.md +84 -0
  125. package/src/bmm-skills/plan/bmad-ux/assets/key-screens.md +29 -0
  126. package/src/bmm-skills/plan/bmad-ux/assets/validation-report-template.html +319 -0
  127. package/src/bmm-skills/plan/bmad-ux/customize.toml +102 -0
  128. package/src/bmm-skills/plan/bmad-ux/references/creative-tools.md +19 -0
  129. package/src/bmm-skills/plan/bmad-ux/references/design-md-spec.md +50 -0
  130. package/src/bmm-skills/plan/bmad-ux/references/headless.md +37 -0
  131. package/src/bmm-skills/plan/bmad-ux/references/validate.md +115 -0
  132. package/src/bmm-skills/ship/bmad-build/SKILL.md +13 -0
  133. package/src/bmm-skills/ship/bmad-build/compile-epic-context.md +62 -0
  134. package/src/bmm-skills/ship/bmad-build/customize.toml +164 -0
  135. package/src/bmm-skills/ship/bmad-build/references/deletion-check.md +14 -0
  136. package/src/bmm-skills/ship/bmad-build/review-prompts/edge-case-hunter.md +88 -0
  137. package/src/bmm-skills/ship/bmad-build/review-prompts/verification-gap.md +113 -0
  138. package/src/bmm-skills/ship/bmad-build/spec-template.md +89 -0
  139. package/src/bmm-skills/ship/bmad-build/step-01-clarify-and-route.md +105 -0
  140. package/src/bmm-skills/ship/bmad-build/step-02-plan.md +47 -0
  141. package/src/bmm-skills/ship/bmad-build/step-03-implement.md +49 -0
  142. package/src/bmm-skills/ship/bmad-build/step-04-review.md +55 -0
  143. package/src/bmm-skills/ship/bmad-build/step-05-present.md +75 -0
  144. package/src/bmm-skills/ship/bmad-build/step-oneshot.md +77 -0
  145. package/src/bmm-skills/ship/bmad-build/sync-sprint-status.md +19 -0
  146. package/src/bmm-skills/ship/bmad-build/workflow.md +84 -0
  147. package/src/bmm-skills/ship/bmad-build-auto/SKILL.md +13 -0
  148. package/src/bmm-skills/ship/bmad-build-auto/compile-epic-context.md +62 -0
  149. package/src/bmm-skills/ship/bmad-build-auto/customize.toml +121 -0
  150. package/src/bmm-skills/ship/bmad-build-auto/references/deletion-check.md +14 -0
  151. package/src/bmm-skills/ship/bmad-build-auto/review-prompts/edge-case-hunter.md +88 -0
  152. package/src/bmm-skills/ship/bmad-build-auto/review-prompts/verification-gap.md +113 -0
  153. package/src/bmm-skills/ship/bmad-build-auto/spec-template.md +99 -0
  154. package/src/bmm-skills/ship/bmad-build-auto/step-01-clarify-and-route.md +83 -0
  155. package/src/bmm-skills/ship/bmad-build-auto/step-02-plan.md +27 -0
  156. package/src/bmm-skills/ship/bmad-build-auto/step-03-implement.md +44 -0
  157. package/src/bmm-skills/ship/bmad-build-auto/step-04-review.md +96 -0
  158. package/src/bmm-skills/ship/bmad-build-auto/workflow.md +104 -0
  159. package/src/bmm-skills/ship/bmad-checkpoint-preview/SKILL.md +68 -0
  160. package/src/bmm-skills/ship/bmad-checkpoint-preview/customize.toml +41 -0
  161. package/src/bmm-skills/ship/bmad-checkpoint-preview/generate-trail.md +38 -0
  162. package/src/bmm-skills/ship/bmad-checkpoint-preview/step-01-orientation.md +105 -0
  163. package/src/bmm-skills/ship/bmad-checkpoint-preview/step-02-walkthrough.md +89 -0
  164. package/src/bmm-skills/ship/bmad-checkpoint-preview/step-03-detail-pass.md +106 -0
  165. package/src/bmm-skills/ship/bmad-checkpoint-preview/step-04-testing.md +74 -0
  166. package/src/bmm-skills/ship/bmad-checkpoint-preview/step-05-wrapup.md +30 -0
  167. package/src/bmm-skills/ship/bmad-code-review/SKILL.md +90 -0
  168. package/src/bmm-skills/ship/bmad-code-review/customize.toml +113 -0
  169. package/src/bmm-skills/ship/bmad-code-review/references/deletion-check.md +14 -0
  170. package/src/bmm-skills/ship/bmad-code-review/review-prompts/edge-case-hunter.md +88 -0
  171. package/src/bmm-skills/ship/bmad-code-review/review-prompts/verification-gap.md +113 -0
  172. package/src/bmm-skills/ship/bmad-code-review/steps/step-01-gather-context.md +87 -0
  173. package/src/bmm-skills/ship/bmad-code-review/steps/step-02-review.md +32 -0
  174. package/src/bmm-skills/ship/bmad-code-review/steps/step-03-triage.md +50 -0
  175. package/src/bmm-skills/ship/bmad-code-review/steps/step-04-present.md +132 -0
  176. package/src/bmm-skills/ship/bmad-correct-course/SKILL.md +300 -0
  177. package/src/bmm-skills/ship/bmad-correct-course/checklist.md +288 -0
  178. package/src/bmm-skills/ship/bmad-correct-course/customize.toml +41 -0
  179. package/src/bmm-skills/ship/bmad-qa-generate-e2e-tests/SKILL.md +176 -0
  180. package/src/bmm-skills/ship/bmad-qa-generate-e2e-tests/checklist.md +33 -0
  181. package/src/bmm-skills/ship/bmad-qa-generate-e2e-tests/customize.toml +41 -0
  182. package/src/bmm-skills/ship/bmad-retrospective/SKILL.md +94 -0
  183. package/src/bmm-skills/ship/bmad-retrospective/customize.toml +41 -0
  184. package/src/bmm-skills/ship/bmad-retrospective/references/acceptance-verdict.md +55 -0
  185. package/src/bmm-skills/ship/bmad-retrospective/references/aggregate-views.md +17 -0
  186. package/src/bmm-skills/ship/bmad-retrospective/references/evidence-gathering.md +30 -0
  187. package/src/bmm-skills/ship/bmad-retrospective/references/retro-document.md +84 -0
  188. package/src/bmm-skills/ship/bmad-retrospective/references/team-discussion.md +22 -0
  189. package/src/bmm-skills/ship/bmad-retrospective/scripts/__pycache__/sprint_status.cpython-311.pyc +0 -0
  190. package/src/bmm-skills/ship/bmad-retrospective/scripts/git_evidence.py +304 -0
  191. package/src/bmm-skills/ship/bmad-retrospective/scripts/sprint_status.py +746 -0
  192. package/src/bmm-skills/ship/bmad-retrospective/scripts/tests/__pycache__/test_git_evidence.cpython-311-pytest-9.1.1.pyc +0 -0
  193. package/src/bmm-skills/ship/bmad-retrospective/scripts/tests/__pycache__/test_sprint_status.cpython-311-pytest-9.1.1.pyc +0 -0
  194. package/src/bmm-skills/ship/bmad-retrospective/scripts/tests/fixtures/sprint-status-template.yaml +71 -0
  195. package/src/bmm-skills/ship/bmad-retrospective/scripts/tests/test_git_evidence.py +750 -0
  196. package/src/bmm-skills/ship/bmad-retrospective/scripts/tests/test_sprint_status.py +1579 -0
  197. package/src/bmm-skills/v6-shims/README.md +28 -0
  198. package/src/bmm-skills/v6-shims/bmad-create-architecture/SKILL.md +30 -0
  199. package/src/bmm-skills/v6-shims/bmad-create-architecture/customize.toml +41 -0
  200. package/src/bmm-skills/v6-shims/bmad-create-prd/SKILL.md +30 -0
  201. package/src/bmm-skills/v6-shims/bmad-create-prd/customize.toml +41 -0
  202. package/src/bmm-skills/v6-shims/bmad-create-story/SKILL.md +434 -0
  203. package/src/bmm-skills/v6-shims/bmad-create-story/checklist.md +357 -0
  204. package/src/bmm-skills/v6-shims/bmad-create-story/customize.toml +41 -0
  205. package/src/bmm-skills/v6-shims/bmad-create-story/discover-inputs.md +88 -0
  206. package/src/bmm-skills/v6-shims/bmad-create-story/template.md +49 -0
  207. package/src/bmm-skills/v6-shims/bmad-dev-auto/SKILL.md +19 -0
  208. package/src/bmm-skills/v6-shims/bmad-dev-story/SKILL.md +502 -0
  209. package/src/bmm-skills/v6-shims/bmad-dev-story/checklist.md +80 -0
  210. package/src/bmm-skills/v6-shims/bmad-dev-story/customize.toml +44 -0
  211. package/src/bmm-skills/v6-shims/bmad-document-project/SKILL.md +14 -0
  212. package/src/bmm-skills/v6-shims/bmad-domain-research/SKILL.md +14 -0
  213. package/src/bmm-skills/v6-shims/bmad-edit-prd/SKILL.md +30 -0
  214. package/src/bmm-skills/v6-shims/bmad-edit-prd/customize.toml +42 -0
  215. package/src/bmm-skills/v6-shims/bmad-market-research/SKILL.md +14 -0
  216. package/src/bmm-skills/v6-shims/bmad-quick-dev/SKILL.md +19 -0
  217. package/src/bmm-skills/v6-shims/bmad-sprint-status/SKILL.md +26 -0
  218. package/src/bmm-skills/v6-shims/bmad-sprint-status/customize.toml +41 -0
  219. package/src/bmm-skills/v6-shims/bmad-technical-research/SKILL.md +14 -0
  220. package/src/bmm-skills/v6-shims/bmad-validate-prd/SKILL.md +30 -0
  221. package/src/bmm-skills/v6-shims/bmad-validate-prd/customize.toml +42 -0
  222. package/src/core-skills/bmad-advanced-elicitation/SKILL.md +65 -0
  223. package/src/core-skills/bmad-advanced-elicitation/assets/methods.csv +72 -0
  224. package/src/core-skills/bmad-advanced-elicitation/customize.toml +54 -0
  225. package/src/core-skills/bmad-advanced-elicitation/scripts/pick_methods.py +233 -0
  226. package/src/core-skills/bmad-advanced-elicitation/scripts/tests/test_pick_methods.py +228 -0
  227. package/src/core-skills/bmad-brainstorming/SKILL.md +80 -0
  228. package/src/core-skills/bmad-brainstorming/assets/brain-icons.json +166 -0
  229. package/src/core-skills/bmad-brainstorming/assets/brain-methods.csv +109 -0
  230. package/src/core-skills/bmad-brainstorming/assets/brain-selector.html +328 -0
  231. package/src/core-skills/bmad-brainstorming/customize.toml +84 -0
  232. package/src/core-skills/bmad-brainstorming/references/converge.md +24 -0
  233. package/src/core-skills/bmad-brainstorming/references/finalize.md +26 -0
  234. package/src/core-skills/bmad-brainstorming/references/headless.md +54 -0
  235. package/src/core-skills/bmad-brainstorming/references/in-chat-techniques.md +18 -0
  236. package/src/core-skills/bmad-brainstorming/references/mode-autonomous.md +10 -0
  237. package/src/core-skills/bmad-brainstorming/references/mode-facilitator.md +11 -0
  238. package/src/core-skills/bmad-brainstorming/references/mode-partner.md +16 -0
  239. package/src/core-skills/bmad-brainstorming/references/resume.md +5 -0
  240. package/src/core-skills/bmad-brainstorming/scripts/brain.py +770 -0
  241. package/src/core-skills/bmad-brainstorming/scripts/tests/test_brain.py +239 -0
  242. package/src/core-skills/bmad-customize/SKILL.md +111 -0
  243. package/src/core-skills/bmad-customize/scripts/list_customizable_skills.py +231 -0
  244. package/src/core-skills/bmad-customize/scripts/tests/test_list_customizable_skills.py +249 -0
  245. package/src/core-skills/bmad-deep-recon/SKILL.md +82 -0
  246. package/src/core-skills/bmad-deep-recon/assets/research.template.md +18 -0
  247. package/src/core-skills/bmad-deep-recon/customize.toml +212 -0
  248. package/src/core-skills/bmad-deep-recon/references/draft.md +8 -0
  249. package/src/core-skills/bmad-deep-recon/references/finalize.md +11 -0
  250. package/src/core-skills/bmad-deep-recon/references/html-briefing.md +16 -0
  251. package/src/core-skills/bmad-deep-recon/references/lifecycle.md +11 -0
  252. package/src/core-skills/bmad-deep-recon/references/process.md +10 -0
  253. package/src/core-skills/bmad-deep-recon/references/run.md +73 -0
  254. package/src/core-skills/bmad-deep-recon/references/selection.md +13 -0
  255. package/src/core-skills/bmad-deep-recon/references/synthesis.md +16 -0
  256. package/src/core-skills/bmad-deep-recon/references/verification.md +29 -0
  257. package/src/core-skills/bmad-deep-recon/scripts/recon_kit.py +322 -0
  258. package/src/core-skills/bmad-deep-recon/scripts/tests/test_recon_kit.py +144 -0
  259. package/src/core-skills/bmad-deep-recon/types/academic-lit.md +19 -0
  260. package/src/core-skills/bmad-deep-recon/types/competitive.md +19 -0
  261. package/src/core-skills/bmad-deep-recon/types/domain.md +19 -0
  262. package/src/core-skills/bmad-deep-recon/types/market.md +19 -0
  263. package/src/core-skills/bmad-deep-recon/types/technical.md +19 -0
  264. package/src/core-skills/bmad-deep-recon/types/user-voice.md +19 -0
  265. package/src/core-skills/bmad-forge-idea/SKILL.md +107 -0
  266. package/src/core-skills/bmad-forge-idea/customize.toml +42 -0
  267. package/src/core-skills/bmad-forge-idea/scripts/resolve_personas.py +275 -0
  268. package/src/core-skills/bmad-forge-idea/scripts/tests/test_resolve_personas.py +138 -0
  269. package/src/core-skills/bmad-help/SKILL.md +75 -0
  270. package/src/core-skills/bmad-party-mode/SKILL.md +60 -0
  271. package/src/core-skills/bmad-party-mode/customize.toml +211 -0
  272. package/src/core-skills/bmad-party-mode/references/create-party.md +70 -0
  273. package/src/core-skills/bmad-party-mode/references/mode-agent-team.md +13 -0
  274. package/src/core-skills/bmad-party-mode/references/mode-auto.md +13 -0
  275. package/src/core-skills/bmad-party-mode/references/mode-subagent.md +31 -0
  276. package/src/core-skills/bmad-party-mode/references/party-memory.md +51 -0
  277. package/src/core-skills/bmad-party-mode/scripts/resolve_party.py +282 -0
  278. package/src/core-skills/bmad-party-mode/scripts/tests/test_resolve_party.py +146 -0
  279. package/src/core-skills/bmad-review/SKILL.md +49 -0
  280. package/src/core-skills/bmad-review/customize.toml +141 -0
  281. package/src/core-skills/bmad-review/references/editorial-common.md +56 -0
  282. package/src/core-skills/bmad-review/references/lens-adversarial.md +19 -0
  283. package/src/core-skills/bmad-review/references/lens-edge-case-hunter.md +54 -0
  284. package/src/core-skills/bmad-review/references/lens-prose.md +7 -0
  285. package/src/core-skills/bmad-review/references/lens-structure.md +9 -0
  286. package/src/core-skills/bmad-review/references/lens-verification-gap.md +92 -0
  287. package/src/core-skills/bmad-review/references/structure-models.md +44 -0
  288. package/src/core-skills/bmad-review/scripts/tests/test_word_metrics.py +62 -0
  289. package/src/core-skills/bmad-review/scripts/word_metrics.py +102 -0
  290. package/src/core-skills/module-help.csv +10 -0
  291. package/src/core-skills/module.yaml +38 -0
  292. package/src/core-skills/v6-shims/README.md +25 -0
  293. package/src/core-skills/v6-shims/bmad-editorial-review/SKILL.md +6 -0
  294. package/src/core-skills/v6-shims/bmad-editorial-review/customize.toml +31 -0
  295. package/src/core-skills/v6-shims/bmad-editorial-review-prose/SKILL.md +6 -0
  296. package/src/core-skills/v6-shims/bmad-editorial-review-structure/SKILL.md +6 -0
  297. package/src/core-skills/v6-shims/bmad-review-adversarial-general/SKILL.md +6 -0
  298. package/src/core-skills/v6-shims/bmad-review-edge-case-hunter/SKILL.md +6 -0
  299. package/src/core-skills/v6-shims/bmad-review-verification-gap/SKILL.md +6 -0
  300. package/src/scripts/__pycache__/config_utils.cpython-311.pyc +0 -0
  301. package/src/scripts/config_utils.py +119 -0
  302. package/src/scripts/memlog.py +224 -0
  303. package/src/scripts/render_skill.py +401 -0
  304. package/src/scripts/resolve_config.py +74 -0
  305. package/src/scripts/resolve_customization.py +99 -0
  306. package/src/scripts/tests/__pycache__/test_config_utils.cpython-311.pyc +0 -0
  307. package/src/scripts/tests/__pycache__/test_resolve_config.cpython-311.pyc +0 -0
  308. package/src/scripts/tests/__pycache__/test_resolve_customization.cpython-311.pyc +0 -0
  309. package/src/scripts/tests/test_config_utils.py +85 -0
  310. package/src/scripts/tests/test_memlog.py +306 -0
  311. package/src/scripts/tests/test_resolve_config.py +89 -0
  312. package/src/scripts/tests/test_resolve_customization.py +77 -0
  313. package/tools/bundle-web-bundles.js +117 -0
  314. package/tools/format-workflow-md.js +263 -0
  315. package/tools/installer/README.md +60 -0
  316. package/tools/installer/bmad-cli.js +105 -0
  317. package/tools/installer/cli-utils.js +57 -0
  318. package/tools/installer/commands/install.js +149 -0
  319. package/tools/installer/commands/status.js +65 -0
  320. package/tools/installer/commands/uninstall.js +167 -0
  321. package/tools/installer/core/config.js +73 -0
  322. package/tools/installer/core/existing-install.js +121 -0
  323. package/tools/installer/core/install-paths.js +132 -0
  324. package/tools/installer/core/installer.js +1767 -0
  325. package/tools/installer/core/legacy-warnings.js +151 -0
  326. package/tools/installer/core/manifest-generator.js +859 -0
  327. package/tools/installer/core/manifest.js +433 -0
  328. package/tools/installer/core/uv-check.js +195 -0
  329. package/tools/installer/core/wsl-node-check.js +109 -0
  330. package/tools/installer/file-ops.js +204 -0
  331. package/tools/installer/fs-native.js +116 -0
  332. package/tools/installer/ide/_config-driven.js +972 -0
  333. package/tools/installer/ide/manager.js +324 -0
  334. package/tools/installer/ide/platform-codes.js +80 -0
  335. package/tools/installer/ide/platform-codes.yaml +343 -0
  336. package/tools/installer/ide/shared/installed-skills.js +50 -0
  337. package/tools/installer/ide/shared/path-utils.js +219 -0
  338. package/tools/installer/ide/shared/skill-manifest.js +57 -0
  339. package/tools/installer/install-messages.yaml +37 -0
  340. package/tools/installer/list-options.js +210 -0
  341. package/tools/installer/message-loader.js +83 -0
  342. package/tools/installer/modules/channel-plan.js +203 -0
  343. package/tools/installer/modules/channel-resolver.js +241 -0
  344. package/tools/installer/modules/custom-module-manager.js +924 -0
  345. package/tools/installer/modules/external-manager.js +671 -0
  346. package/tools/installer/modules/git-env.js +47 -0
  347. package/tools/installer/modules/module-help-schema.js +13 -0
  348. package/tools/installer/modules/official-modules.js +2257 -0
  349. package/tools/installer/modules/plugin-resolver.js +398 -0
  350. package/tools/installer/modules/version-resolver.js +336 -0
  351. package/tools/installer/project-root.js +224 -0
  352. package/tools/installer/prompts.js +791 -0
  353. package/tools/installer/set-overrides.js +330 -0
  354. package/tools/installer/ui.js +2167 -0
  355. package/tools/installer/yaml-format.js +245 -0
  356. package/tools/javascript-conventions.md +5 -0
  357. package/tools/migrate-custom-module-paths.js +124 -0
  358. package/tools/skill-validator.md +378 -0
  359. package/tools/validate-published-implementation-model.mjs +68 -0
  360. package/tools/validate-sidebar-order.js +388 -0
  361. package/tools/validate-skills.js +735 -0
  362. package/tools/validate-svg-changes.sh +356 -0
  363. package/web-bundles/README.md +46 -0
  364. package/web-bundles/brainstorming-coach/INSTRUCTIONS.md +86 -0
  365. package/web-bundles/brainstorming-coach/SKILL.md +83 -0
  366. package/web-bundles/brainstorming-coach/brain-methods.csv +62 -0
  367. package/web-bundles/bundles.json +139 -0
  368. package/web-bundles/market-and-industry-research/INSTRUCTIONS.md +88 -0
  369. package/web-bundles/market-and-industry-research/SKILL.md +59 -0
  370. package/web-bundles/prd-coach/INSTRUCTIONS.md +86 -0
  371. package/web-bundles/prd-coach/SKILL.md +101 -0
  372. package/web-bundles/prd-coach/prd-template.md +165 -0
  373. package/web-bundles/prd-coach/prd-validation-checklist.md +135 -0
  374. package/web-bundles/prfaq-coach/INSTRUCTIONS.md +86 -0
  375. package/web-bundles/prfaq-coach/SKILL.md +139 -0
  376. package/web-bundles/product-brief-coach/INSTRUCTIONS.md +86 -0
  377. package/web-bundles/product-brief-coach/SKILL.md +113 -0
  378. package/web-bundles/ux-coach/INSTRUCTIONS.md +92 -0
  379. package/web-bundles/ux-coach/SKILL.md +187 -0
  380. package/web-bundles/ux-coach/ux-validation.md +100 -0
@@ -0,0 +1,306 @@
1
+ # /// script
2
+ # requires-python = ">=3.10"
3
+ # dependencies = ["pytest>=8.0"]
4
+ # ///
5
+ """Tests for memlog.py. Run: uv run --with pytest pytest scripts/tests/test_memlog.py
6
+
7
+ The spine under test is the flat, append-only, chronological invariant: every entry is
8
+ one line recorded at the end in the order it happened — no sections, no grouping, and no
9
+ lifecycle status the log would have to mutate.
10
+ """
11
+ import json
12
+ import sys
13
+ from pathlib import Path
14
+
15
+ import pytest
16
+
17
+ sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
18
+ import memlog # noqa: E402
19
+
20
+ MEMLOG = ".memlog.md"
21
+
22
+
23
+ @pytest.fixture
24
+ def ws(tmp_path):
25
+ return str(tmp_path)
26
+
27
+
28
+ def read(ws):
29
+ return (Path(ws) / MEMLOG).read_text(encoding="utf-8")
30
+
31
+
32
+ def body_of(ws):
33
+ return memlog.split(read(ws))[1]
34
+
35
+
36
+ def entries(ws):
37
+ return [ln for ln in body_of(ws).splitlines() if ln.startswith("- ")]
38
+
39
+
40
+ def init(ws, **fields):
41
+ fields = fields or {"topic": "Reinvent the lunchbox", "goal": "ideas for a pitch"}
42
+ argv = ["init", "--workspace", ws]
43
+ for k, v in fields.items():
44
+ argv += ["--field", f"{k}={v}"]
45
+ assert memlog.main(argv) == 0
46
+
47
+
48
+ def append(ws, text, entry_type=None, by=None):
49
+ argv = ["append", "--workspace", ws, "--text", text]
50
+ if entry_type:
51
+ argv += ["--type", entry_type]
52
+ if by:
53
+ argv += ["--by", by]
54
+ assert memlog.main(argv) == 0
55
+
56
+
57
+ # --- init ---------------------------------------------------------------
58
+
59
+ def test_init_writes_frontmatter_fields(ws):
60
+ init(ws)
61
+ meta, body = memlog.split(read(ws))
62
+ assert meta["topic"] == "Reinvent the lunchbox"
63
+ assert meta["goal"] == "ideas for a pitch"
64
+ assert "updated" in meta
65
+ assert body.strip() == ""
66
+
67
+
68
+ def test_init_has_no_lifecycle_status(ws):
69
+ # A memory log carries no "status" flag; completion is an appended entry, not frontmatter.
70
+ init(ws)
71
+ meta, _ = memlog.split(read(ws))
72
+ assert "status" not in meta
73
+
74
+
75
+ def test_init_arbitrary_fields(ws):
76
+ init(ws, topic="T", audience="board")
77
+ meta, _ = memlog.split(read(ws))
78
+ assert meta["audience"] == "board"
79
+
80
+
81
+ def test_init_refuses_overwrite(ws):
82
+ init(ws)
83
+ assert memlog.main(["init", "--workspace", ws, "--field", "topic=other"]) == 2
84
+
85
+
86
+ def test_init_creates_missing_workspace(tmp_path):
87
+ nested = str(tmp_path / "a" / "b")
88
+ assert memlog.main(["init", "--workspace", nested, "--field", "topic=T"]) == 0
89
+ assert (Path(nested) / MEMLOG).is_file()
90
+
91
+
92
+ def test_init_rejects_malformed_field(ws):
93
+ assert memlog.main(["init", "--workspace", ws, "--field", "noequals"]) == 2
94
+
95
+
96
+ # --- addressing: --workspace and --path are interchangeable --------------
97
+
98
+ def test_path_addressing_targets_the_file_directly(tmp_path):
99
+ target = tmp_path / "run" / ".memlog.md"
100
+ assert memlog.main(["init", "--path", str(target), "--field", "topic=T"]) == 0
101
+ assert target.is_file()
102
+ assert memlog.main(["append", "--path", str(target), "--text", "an idea", "--type", "idea"]) == 0
103
+ body = memlog.split(target.read_text(encoding="utf-8"))[1]
104
+ assert "- (idea) an idea" in body
105
+
106
+
107
+ def test_workspace_and_path_resolve_to_same_file(ws):
108
+ init(ws)
109
+ via_path = str(Path(ws) / MEMLOG)
110
+ assert memlog.main(["append", "--path", via_path, "--text", "from path"]) == 0
111
+ assert memlog.main(["append", "--workspace", ws, "--text", "from workspace"]) == 0
112
+ assert entries(ws) == ["- from path", "- from workspace"]
113
+
114
+
115
+ def test_target_is_required(ws):
116
+ with pytest.raises(SystemExit):
117
+ memlog.main(["append", "--text", "orphan"]) # neither --workspace nor --path
118
+
119
+
120
+ # --- append: flat chronological order is the whole point -----------------
121
+
122
+ def test_append_lands_at_end_in_order(ws):
123
+ init(ws)
124
+ append(ws, "first")
125
+ append(ws, "second")
126
+ append(ws, "third")
127
+ assert entries(ws) == ["- first", "- second", "- third"]
128
+
129
+
130
+ def test_no_sections_or_headings_ever(ws):
131
+ init(ws)
132
+ append(ws, "started foo", entry_type="technique")
133
+ append(ws, "an idea", entry_type="idea")
134
+ append(ws, "started bar", entry_type="technique")
135
+ assert "## " not in body_of(ws) # the flat log never grows headings
136
+
137
+
138
+ def test_type_renders_as_inline_tag(ws):
139
+ init(ws)
140
+ append(ws, "the earth revolves around the sun", entry_type="idea")
141
+ append(ws, "how do we handle stampede?", entry_type="question")
142
+ body = body_of(ws)
143
+ assert "- (idea) the earth revolves around the sun" in body
144
+ assert "- (question) how do we handle stampede?" in body
145
+
146
+
147
+ def test_append_without_type_is_plain_note(ws):
148
+ init(ws)
149
+ append(ws, "bare entry")
150
+ assert entries(ws) == ["- bare entry"]
151
+
152
+
153
+ def test_completion_is_an_entry_not_a_status(ws):
154
+ # The documented way to mark a session done: append it. Frontmatter never gains a status.
155
+ init(ws)
156
+ append(ws, "session complete", entry_type="event")
157
+ meta, _ = memlog.split(read(ws))
158
+ assert "status" not in meta
159
+ assert entries(ws)[-1] == "- (event) session complete"
160
+
161
+
162
+ def test_append_collapses_newlines_into_one_line(ws):
163
+ init(ws)
164
+ append(ws, "line one\nline two\n spaced out")
165
+ assert entries(ws) == ["- line one line two spaced out"]
166
+
167
+
168
+ def test_revisited_technique_is_just_a_later_entry(ws):
169
+ # the user's model: switching techniques is an entry, not a section to return to
170
+ init(ws)
171
+ append(ws, "started SCAMPER", entry_type="technique")
172
+ append(ws, "magnetic latch", entry_type="idea")
173
+ append(ws, "started Six Hats", entry_type="technique")
174
+ append(ws, "stale data risk", entry_type="idea")
175
+ append(ws, "started SCAMPER", entry_type="technique") # back to SCAMPER — just appended again
176
+ append(ws, "stackable tiers", entry_type="idea")
177
+ assert entries(ws) == [
178
+ "- (technique) started SCAMPER",
179
+ "- (idea) magnetic latch",
180
+ "- (technique) started Six Hats",
181
+ "- (idea) stale data risk",
182
+ "- (technique) started SCAMPER",
183
+ "- (idea) stackable tiers",
184
+ ]
185
+
186
+
187
+ def test_by_renders_attribution_in_tag(ws):
188
+ # Creative Partner mode must record whose idea each one was
189
+ init(ws)
190
+ append(ws, "magnetic latch lid", entry_type="idea", by="user")
191
+ append(ws, "lid doubles as a plate", entry_type="idea", by="coach")
192
+ body = body_of(ws)
193
+ assert "- (idea by user) magnetic latch lid" in body
194
+ assert "- (idea by coach) lid doubles as a plate" in body
195
+
196
+
197
+ def test_by_without_type_renders_alone(ws):
198
+ init(ws)
199
+ append(ws, "off-the-cuff thought", by="coach")
200
+ assert entries(ws) == ["- (by coach) off-the-cuff thought"]
201
+
202
+
203
+ def test_heterogeneous_entry_types_coexist(ws):
204
+ init(ws)
205
+ append(ws, "an idea", entry_type="idea")
206
+ append(ws, "an open question", entry_type="question")
207
+ append(ws, "a decision we made", entry_type="decision")
208
+ append(ws, "user wants mobile-first", entry_type="direction")
209
+ body = body_of(ws)
210
+ for tag in ("(idea)", "(question)", "(decision)", "(direction)"):
211
+ assert tag in body
212
+
213
+
214
+ def test_free_vocabulary_is_not_enforced(ws):
215
+ # The tool is neutral: any --type the host skill names renders verbatim.
216
+ init(ws)
217
+ append(ws, "a custom kind", entry_type="crack")
218
+ append(ws, "another", entry_type="lock")
219
+ body = body_of(ws)
220
+ assert "- (crack) a custom kind" in body
221
+ assert "- (lock) another" in body
222
+
223
+
224
+ # --- set: generic descriptive frontmatter, no lifecycle semantics --------
225
+
226
+ def test_set_adds_field(ws):
227
+ init(ws)
228
+ memlog.main(["set", "--workspace", ws, "--key", "mode", "--value", "partner"])
229
+ assert memlog.split(read(ws))[0]["mode"] == "partner"
230
+
231
+
232
+ def test_set_replaces_field(ws):
233
+ init(ws, topic="T", mode="facilitator")
234
+ memlog.main(["set", "--workspace", ws, "--key", "mode", "--value", "partner"])
235
+ assert memlog.split(read(ws))[0]["mode"] == "partner"
236
+
237
+
238
+ def test_set_preserves_body(ws):
239
+ init(ws)
240
+ append(ws, "keep me", entry_type="idea")
241
+ memlog.main(["set", "--workspace", ws, "--key", "mode", "--value", "partner"])
242
+ meta, body = memlog.split(read(ws))
243
+ assert meta["mode"] == "partner"
244
+ assert "- (idea) keep me" in body
245
+
246
+
247
+ def test_updated_stays_last(ws):
248
+ init(ws)
249
+ memlog.main(["set", "--workspace", ws, "--key", "owner", "--value", "BMad"])
250
+ meta = memlog.split(read(ws))[0]
251
+ assert list(meta)[-1] == "updated"
252
+
253
+
254
+ # --- robustness ---------------------------------------------------------
255
+
256
+ def test_roundtrip_render_is_stable(ws):
257
+ init(ws)
258
+ append(ws, "one", entry_type="idea")
259
+ first = read(ws)
260
+ meta, body = memlog.split(first)
261
+ assert memlog.render(meta, body) == first
262
+
263
+
264
+ def test_commas_in_field_survive(ws):
265
+ init(ws, topic="cars, trains, and planes")
266
+ append(ws, "z", entry_type="idea")
267
+ meta, _ = memlog.split(read(ws))
268
+ assert meta["topic"] == "cars, trains, and planes"
269
+
270
+
271
+ def test_triple_dash_in_field_does_not_corrupt_frontmatter(ws):
272
+ # A `---` inside a value must NOT be read as the closing fence: topic stays intact
273
+ # and the body never leaks frontmatter text.
274
+ init(ws, topic="Pricing --- tiers --- and add-ons")
275
+ append(ws, "an idea", entry_type="idea")
276
+ meta, body = memlog.split(read(ws))
277
+ assert meta["topic"] == "Pricing --- tiers --- and add-ons"
278
+ assert entries(ws) == ["- (idea) an idea"]
279
+ assert "topic:" not in body # frontmatter never bled into the body
280
+
281
+
282
+ def test_newline_in_field_is_neutralized(ws):
283
+ # A value carrying a newline can't break the fence on the next round-trip.
284
+ memlog.main(["init", "--workspace", ws, "--field", "topic=line one\nline two"])
285
+ append(ws, "x", entry_type="idea")
286
+ meta, _ = memlog.split(read(ws))
287
+ assert "\n" not in meta["topic"]
288
+
289
+
290
+ def test_append_emits_json_ack(ws, capsys):
291
+ init(ws)
292
+ append(ws, "x", entry_type="idea")
293
+ out = json.loads(capsys.readouterr().out.strip().splitlines()[-1])
294
+ assert out["ok"] is True
295
+ assert out["entries"] == 1
296
+ assert out["memlog"].endswith(MEMLOG)
297
+ assert "status" not in out # no lifecycle status
298
+ assert "section" not in out # sections are gone
299
+
300
+
301
+ def test_ack_entry_count_climbs(ws, capsys):
302
+ init(ws)
303
+ append(ws, "a")
304
+ append(ws, "b")
305
+ out = json.loads(capsys.readouterr().out.strip().splitlines()[-1])
306
+ assert out["entries"] == 2
@@ -0,0 +1,89 @@
1
+ import json
2
+ import shutil
3
+ import subprocess
4
+ import sys
5
+ import tempfile
6
+ import unittest
7
+ from pathlib import Path
8
+
9
+
10
+ SCRIPT = Path(__file__).resolve().parents[1] / "resolve_config.py"
11
+
12
+
13
+ class ResolveConfigCliTests(unittest.TestCase):
14
+ def test_missing_tomllib_exits_with_actionable_version_error(self):
15
+ with tempfile.TemporaryDirectory() as temp_dir:
16
+ scripts = Path(temp_dir)
17
+ shutil.copy2(SCRIPT, scripts / SCRIPT.name)
18
+ shutil.copy2(SCRIPT.parent / "config_utils.py", scripts / "config_utils.py")
19
+ (scripts / "tomllib.py").write_text(
20
+ 'raise ModuleNotFoundError("No module named tomllib", name="tomllib")\n',
21
+ encoding="utf-8",
22
+ )
23
+
24
+ result = subprocess.run(
25
+ [sys.executable, str(scripts / SCRIPT.name), "--help"],
26
+ text=True,
27
+ stdout=subprocess.PIPE,
28
+ stderr=subprocess.PIPE,
29
+ check=False,
30
+ )
31
+
32
+ self.assertEqual(result.returncode, 3)
33
+ self.assertEqual(
34
+ result.stderr,
35
+ "error: Python 3.11+ is required (stdlib `tomllib` not found).\n",
36
+ )
37
+ self.assertNotIn("Traceback", result.stderr)
38
+
39
+ def test_full_and_repeated_key_output_follow_layer_precedence(self):
40
+ with tempfile.TemporaryDirectory() as temp_dir:
41
+ root = Path(temp_dir)
42
+ custom = root / "_bmad" / "custom"
43
+ custom.mkdir(parents=True)
44
+ (root / "_bmad" / "config.toml").write_text(
45
+ '[core]\nname = "base"\nkeep = "yes"\n', encoding="utf-8"
46
+ )
47
+ (root / "_bmad" / "config.user.toml").write_text(
48
+ '[core]\nname = "base-user"\n', encoding="utf-8"
49
+ )
50
+ (custom / "config.toml").write_text(
51
+ '[core]\nname = "team"\n', encoding="utf-8"
52
+ )
53
+ (custom / "config.user.toml").write_text(
54
+ '[core]\nname = "user"\n', encoding="utf-8"
55
+ )
56
+
57
+ full = self._run(root)
58
+ self.assertEqual(full.returncode, 0, msg=full.stderr)
59
+ self.assertEqual(json.loads(full.stdout)["core"], {"name": "user", "keep": "yes"})
60
+
61
+ keyed = self._run(root, "--key", "core.name", "--key", "missing")
62
+ self.assertEqual(keyed.returncode, 0, msg=keyed.stderr)
63
+ self.assertEqual(json.loads(keyed.stdout), {"core.name": "user"})
64
+
65
+ def test_malformed_present_layer_fails(self):
66
+ with tempfile.TemporaryDirectory() as temp_dir:
67
+ root = Path(temp_dir)
68
+ custom = root / "_bmad" / "custom"
69
+ custom.mkdir(parents=True)
70
+ (root / "_bmad" / "config.toml").write_text("[core]\nvalid = true\n", encoding="utf-8")
71
+ (custom / "config.toml").write_text("[broken\n", encoding="utf-8")
72
+
73
+ result = self._run(root)
74
+ self.assertNotEqual(result.returncode, 0)
75
+ self.assertIn("failed to parse", result.stderr)
76
+
77
+ @staticmethod
78
+ def _run(root: Path, *args: str) -> subprocess.CompletedProcess[str]:
79
+ return subprocess.run(
80
+ [sys.executable, str(SCRIPT), "--project-root", str(root), *args],
81
+ text=True,
82
+ stdout=subprocess.PIPE,
83
+ stderr=subprocess.PIPE,
84
+ check=False,
85
+ )
86
+
87
+
88
+ if __name__ == "__main__":
89
+ unittest.main()
@@ -0,0 +1,77 @@
1
+ import json
2
+ import os
3
+ import shutil
4
+ import subprocess
5
+ import sys
6
+ import tempfile
7
+ import unittest
8
+ from pathlib import Path
9
+
10
+
11
+ SCRIPT = Path(__file__).resolve().parents[1] / "resolve_customization.py"
12
+
13
+
14
+ class ResolveCustomizationStdoutTests(unittest.TestCase):
15
+ def test_missing_tomllib_exits_with_actionable_version_error(self):
16
+ with tempfile.TemporaryDirectory() as temp_dir:
17
+ scripts = Path(temp_dir)
18
+ shutil.copy2(SCRIPT, scripts / SCRIPT.name)
19
+ shutil.copy2(SCRIPT.parent / "config_utils.py", scripts / "config_utils.py")
20
+ (scripts / "tomllib.py").write_text(
21
+ 'raise ModuleNotFoundError("No module named tomllib", name="tomllib")\n',
22
+ encoding="utf-8",
23
+ )
24
+
25
+ result = subprocess.run(
26
+ [sys.executable, str(scripts / SCRIPT.name), "--help"],
27
+ text=True,
28
+ stdout=subprocess.PIPE,
29
+ stderr=subprocess.PIPE,
30
+ check=False,
31
+ )
32
+
33
+ self.assertEqual(result.returncode, 3)
34
+ self.assertEqual(
35
+ result.stderr,
36
+ "error: Python 3.11+ is required (stdlib `tomllib` not found).\n",
37
+ )
38
+ self.assertNotIn("Traceback", result.stderr)
39
+
40
+ def test_writes_emoji_json_when_stdout_encoding_is_cp1252(self):
41
+ with tempfile.TemporaryDirectory() as temp_dir:
42
+ skill_dir = Path(temp_dir) / "emoji-agent"
43
+ skill_dir.mkdir()
44
+ (skill_dir / "customize.toml").write_text(
45
+ '[agent]\nname = "Emoji Agent"\nicon = "🧭"\n',
46
+ encoding="utf-8",
47
+ )
48
+
49
+ env = os.environ.copy()
50
+ env["PYTHONIOENCODING"] = "cp1252"
51
+ result = subprocess.run(
52
+ [
53
+ sys.executable,
54
+ str(SCRIPT),
55
+ "--skill",
56
+ str(skill_dir),
57
+ "--key",
58
+ "agent",
59
+ ],
60
+ stdout=subprocess.PIPE,
61
+ stderr=subprocess.PIPE,
62
+ cwd=temp_dir,
63
+ env=env,
64
+ check=False,
65
+ )
66
+
67
+ stderr = result.stderr.decode("utf-8", errors="replace")
68
+ self.assertEqual(result.returncode, 0, msg=stderr)
69
+
70
+ output = result.stdout.decode("utf-8")
71
+ self.assertIn("🧭", output)
72
+ resolved = json.loads(output)
73
+ self.assertEqual(resolved["agent"]["icon"], "🧭")
74
+
75
+
76
+ if __name__ == "__main__":
77
+ unittest.main()
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Web Bundle Release Packager
3
+ *
4
+ * Zips each bundle under web-bundles/ into dist/web-bundles/{slug}.zip
5
+ * for attachment to a GitHub Release.
6
+ *
7
+ * Usage:
8
+ * node tools/bundle-web-bundles.js
9
+ *
10
+ * After running, the script prints the exact `gh release create` command
11
+ * (with the correct tag from bundles.json) for you to copy.
12
+ */
13
+
14
+ const fs = require('node:fs');
15
+ const path = require('node:path');
16
+ const { execSync, execFileSync } = require('node:child_process');
17
+
18
+ const REPO_ROOT = path.resolve(__dirname, '..');
19
+ const BUNDLES_DIR = path.join(REPO_ROOT, 'web-bundles');
20
+ const DIST_DIR = path.join(REPO_ROOT, 'dist', 'web-bundles');
21
+ const MANIFEST = path.join(BUNDLES_DIR, 'bundles.json');
22
+ const SLUG_RE = /^[a-z0-9][a-z0-9-]*$/;
23
+
24
+ function fail(msg) {
25
+ console.error(`[ERROR] ${msg}`);
26
+ process.exit(1);
27
+ }
28
+
29
+ function requireZipCli() {
30
+ try {
31
+ execSync('zip -v', { stdio: 'ignore' });
32
+ } catch {
33
+ fail("'zip' CLI not found on PATH. Install zip (macOS: preinstalled; Debian/Ubuntu: apt install zip; Alpine: apk add zip) and re-run.");
34
+ }
35
+ }
36
+
37
+ function loadManifest() {
38
+ if (!fs.existsSync(MANIFEST)) {
39
+ fail(`bundles.json not found at ${MANIFEST}`);
40
+ }
41
+ let manifest;
42
+ try {
43
+ manifest = JSON.parse(fs.readFileSync(MANIFEST, 'utf-8'));
44
+ } catch (error) {
45
+ fail(`bundles.json is not valid JSON: ${error.message}`);
46
+ }
47
+ if (!Array.isArray(manifest.bundles) || manifest.bundles.length === 0) {
48
+ fail('bundles.json is missing a non-empty "bundles" array.');
49
+ }
50
+ if (typeof manifest.releaseTag !== 'string' || !manifest.releaseTag) {
51
+ fail('bundles.json is missing "releaseTag".');
52
+ }
53
+ return manifest;
54
+ }
55
+
56
+ function main() {
57
+ requireZipCli();
58
+ const manifest = loadManifest();
59
+ const releaseTag = manifest.releaseTag;
60
+
61
+ fs.mkdirSync(DIST_DIR, { recursive: true });
62
+
63
+ console.log(`Packaging ${manifest.bundles.length} bundles for release ${releaseTag}\n`);
64
+
65
+ const zipped = [];
66
+ const missing = [];
67
+ const invalid = [];
68
+ for (const bundle of manifest.bundles) {
69
+ if (!bundle.slug || !SLUG_RE.test(bundle.slug)) {
70
+ invalid.push(bundle.slug || '(no slug)');
71
+ console.error(` [INVALID] slug must match ${SLUG_RE} — got: ${bundle.slug}`);
72
+ continue;
73
+ }
74
+ const src = path.join(BUNDLES_DIR, bundle.slug);
75
+ if (!fs.existsSync(src)) {
76
+ missing.push(bundle.slug);
77
+ console.error(` [MISSING] ${bundle.slug} — directory not found`);
78
+ continue;
79
+ }
80
+
81
+ const out = path.join(DIST_DIR, `${bundle.slug}.zip`);
82
+ if (fs.existsSync(out)) fs.unlinkSync(out);
83
+
84
+ try {
85
+ execFileSync('zip', ['-r', '-X', '-q', out, bundle.slug, '-x', '*.DS_Store'], {
86
+ cwd: BUNDLES_DIR,
87
+ stdio: 'inherit',
88
+ });
89
+ } catch (error) {
90
+ fail(`zip failed for ${bundle.slug}: ${error.message}`);
91
+ }
92
+
93
+ const size = (fs.statSync(out).size / 1024).toFixed(1);
94
+ console.log(` [OK] ${bundle.slug}.zip (${size} KB)`);
95
+ zipped.push(bundle.slug);
96
+ }
97
+
98
+ if (invalid.length > 0) {
99
+ fail(`Refusing to publish: ${invalid.length} bundle(s) have invalid slugs: ${invalid.join(', ')}`);
100
+ }
101
+ if (missing.length > 0) {
102
+ fail(`Refusing to publish an incomplete release: missing directories for ${missing.join(', ')}`);
103
+ }
104
+ if (zipped.length === 0) {
105
+ fail('No bundles were packaged. Check bundles.json against web-bundles/ subdirectories.');
106
+ }
107
+
108
+ console.log(`\nWrote ${zipped.length} bundles to ${path.relative(REPO_ROOT, DIST_DIR)}/`);
109
+ console.log('\nNext step — create or update the GitHub Release:\n');
110
+ console.log(` gh release create ${releaseTag} dist/web-bundles/*.zip \\`);
111
+ console.log(` --title "${releaseTag}" \\`);
112
+ console.log(` --notes "BMad web bundles for Gemini Gems and ChatGPT Custom GPTs. See https://bmadcode.com/web-bundles/"\n`);
113
+ console.log('Or, to refresh an existing release:\n');
114
+ console.log(` gh release upload ${releaseTag} dist/web-bundles/*.zip --clobber\n`);
115
+ }
116
+
117
+ main();