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,735 @@
1
+ /**
2
+ * Deterministic Skill Validator
3
+ *
4
+ * Validates 12 deterministic rules across all skill directories.
5
+ * Acts as a fast first-pass complement to the inference-based skill validator.
6
+ *
7
+ * What it checks:
8
+ * - SKILL-01: SKILL.md exists
9
+ * - SKILL-02: SKILL.md frontmatter has name
10
+ * - SKILL-03: SKILL.md frontmatter has description
11
+ * - SKILL-04: name format (lowercase, hyphens, no forbidden substrings)
12
+ * - SKILL-05: name matches directory basename
13
+ * - SKILL-06: description quality (length, "Use when"/"Use if")
14
+ * - SKILL-07: SKILL.md has body content after frontmatter
15
+ * - PATH-02: no installed_path variable
16
+ * - STEP-01: step filename format
17
+ * - STEP-06: step frontmatter has no name/description
18
+ * - STEP-07: step count 2-10
19
+ * - SEQ-02: no time estimates
20
+ * - TPL-01: template files must not contain compile-time {{.var}} substitutions
21
+ *
22
+ * Usage:
23
+ * node tools/validate-skills.js # All skills, human-readable
24
+ * node tools/validate-skills.js path/to/skill-dir # Single skill
25
+ * node tools/validate-skills.js --strict # Exit 1 on HIGH+ findings
26
+ * node tools/validate-skills.js --json # JSON output
27
+ */
28
+
29
+ const fs = require('node:fs');
30
+ const path = require('node:path');
31
+
32
+ const PROJECT_ROOT = path.resolve(__dirname, '..');
33
+ const SRC_DIR = path.join(PROJECT_ROOT, 'src');
34
+
35
+ // --- CLI Parsing ---
36
+
37
+ const args = process.argv.slice(2);
38
+ const STRICT = args.includes('--strict');
39
+ const JSON_OUTPUT = args.includes('--json');
40
+ const positionalArgs = args.filter((a) => !a.startsWith('--'));
41
+
42
+ // --- Constants ---
43
+
44
+ const NAME_REGEX = /^bmad-[a-z0-9]+(-[a-z0-9]+)*$/;
45
+ const STEP_FILENAME_REGEX = /^step-\d{2}[a-z]?-[a-z0-9-]+\.md$/;
46
+ const TIME_ESTIMATE_PATTERNS = [/takes?\s+\d+\s*min/i, /~\s*\d+\s*min/i, /estimated\s+time/i, /\bETA\b/];
47
+ const TEMPLATE_FILENAME_REGEX = /template/i;
48
+ const COMPILE_TIME_SUB_REGEX = /\{\{\.\w+\}\}/;
49
+
50
+ const SEVERITY_ORDER = { CRITICAL: 0, HIGH: 1, MEDIUM: 2, LOW: 3 };
51
+
52
+ // --- Output Escaping ---
53
+
54
+ function escapeAnnotation(str) {
55
+ return str.replaceAll('%', '%25').replaceAll('\r', '%0D').replaceAll('\n', '%0A');
56
+ }
57
+
58
+ function escapeTableCell(str) {
59
+ return String(str).replaceAll('|', String.raw`\|`);
60
+ }
61
+
62
+ // --- Frontmatter Parsing ---
63
+
64
+ /**
65
+ * Parse YAML frontmatter from a markdown file.
66
+ * Returns an object with key-value pairs, or null if no frontmatter.
67
+ */
68
+ function parseFrontmatter(content) {
69
+ const trimmed = content.trimStart();
70
+ if (!trimmed.startsWith('---')) return null;
71
+
72
+ let endIndex = trimmed.indexOf('\n---\n', 3);
73
+ if (endIndex === -1) {
74
+ // Handle file ending with \n---
75
+ if (trimmed.endsWith('\n---')) {
76
+ endIndex = trimmed.length - 4;
77
+ } else {
78
+ return null;
79
+ }
80
+ }
81
+
82
+ const fmBlock = trimmed.slice(3, endIndex).trim();
83
+ if (fmBlock === '') return {};
84
+
85
+ const result = {};
86
+ for (const line of fmBlock.split('\n')) {
87
+ const colonIndex = line.indexOf(':');
88
+ if (colonIndex === -1) continue;
89
+ // Skip indented lines (nested YAML values)
90
+ if (line[0] === ' ' || line[0] === '\t') continue;
91
+ const key = line.slice(0, colonIndex).trim();
92
+ let value = line.slice(colonIndex + 1).trim();
93
+ // Strip surrounding quotes (single or double)
94
+ if ((value.startsWith("'") && value.endsWith("'")) || (value.startsWith('"') && value.endsWith('"'))) {
95
+ value = value.slice(1, -1);
96
+ }
97
+ result[key] = value;
98
+ }
99
+
100
+ return result;
101
+ }
102
+
103
+ /**
104
+ * Parse YAML frontmatter, handling multiline values (description often spans lines).
105
+ * Returns an object with key-value pairs, or null if no frontmatter.
106
+ */
107
+ function parseFrontmatterMultiline(content) {
108
+ const trimmed = content.trimStart();
109
+ if (!trimmed.startsWith('---')) return null;
110
+
111
+ let endIndex = trimmed.indexOf('\n---\n', 3);
112
+ if (endIndex === -1) {
113
+ // Handle file ending with \n---
114
+ if (trimmed.endsWith('\n---')) {
115
+ endIndex = trimmed.length - 4;
116
+ } else {
117
+ return null;
118
+ }
119
+ }
120
+
121
+ const fmBlock = trimmed.slice(3, endIndex).trim();
122
+ if (fmBlock === '') return {};
123
+
124
+ const result = {};
125
+ let currentKey = null;
126
+ let currentValue = '';
127
+
128
+ for (const line of fmBlock.split('\n')) {
129
+ const colonIndex = line.indexOf(':');
130
+ // New key-value pair: must start at column 0 (no leading whitespace) and have a colon
131
+ if (colonIndex > 0 && line[0] !== ' ' && line[0] !== '\t') {
132
+ // Save previous key
133
+ if (currentKey !== null) {
134
+ result[currentKey] = stripQuotes(currentValue.trim());
135
+ }
136
+ currentKey = line.slice(0, colonIndex).trim();
137
+ currentValue = line.slice(colonIndex + 1);
138
+ } else if (currentKey !== null) {
139
+ // Skip YAML comment lines
140
+ if (line.trimStart().startsWith('#')) continue;
141
+ // Continuation of multiline value
142
+ currentValue += '\n' + line;
143
+ }
144
+ }
145
+
146
+ // Save last key
147
+ if (currentKey !== null) {
148
+ result[currentKey] = stripQuotes(currentValue.trim());
149
+ }
150
+
151
+ return result;
152
+ }
153
+
154
+ function stripQuotes(value) {
155
+ if ((value.startsWith("'") && value.endsWith("'")) || (value.startsWith('"') && value.endsWith('"'))) {
156
+ return value.slice(1, -1);
157
+ }
158
+ return value;
159
+ }
160
+
161
+ // --- Safe File Reading ---
162
+
163
+ /**
164
+ * Read a file safely, returning null on error.
165
+ * Pushes a warning finding if the file cannot be read.
166
+ */
167
+ function safeReadFile(filePath, findings, relFile) {
168
+ try {
169
+ return fs.readFileSync(filePath, 'utf-8');
170
+ } catch (error) {
171
+ findings.push({
172
+ rule: 'READ-ERR',
173
+ title: 'File Read Error',
174
+ severity: 'MEDIUM',
175
+ file: relFile || path.basename(filePath),
176
+ detail: `Cannot read file: ${error.message}`,
177
+ fix: 'Check file permissions and ensure the file exists.',
178
+ });
179
+ return null;
180
+ }
181
+ }
182
+
183
+ // --- Code Block Stripping ---
184
+
185
+ function stripCodeBlocks(content) {
186
+ return content.replaceAll(/```[\s\S]*?```/g, (m) => m.replaceAll(/[^\n]/g, ''));
187
+ }
188
+
189
+ // --- Skill Discovery ---
190
+
191
+ function discoverSkillDirs(rootDirs) {
192
+ const skillDirs = [];
193
+
194
+ function walk(dir) {
195
+ if (!fs.existsSync(dir)) return;
196
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
197
+
198
+ for (const entry of entries) {
199
+ if (!entry.isDirectory()) continue;
200
+ if (entry.name === 'node_modules' || entry.name === '.git') continue;
201
+
202
+ const fullPath = path.join(dir, entry.name);
203
+ const skillMd = path.join(fullPath, 'SKILL.md');
204
+
205
+ if (fs.existsSync(skillMd)) {
206
+ skillDirs.push(fullPath);
207
+ }
208
+
209
+ // Keep walking into subdirectories to find nested skills
210
+ walk(fullPath);
211
+ }
212
+ }
213
+
214
+ for (const rootDir of rootDirs) {
215
+ walk(rootDir);
216
+ }
217
+
218
+ return skillDirs.sort();
219
+ }
220
+
221
+ // --- File Collection ---
222
+
223
+ function collectSkillFiles(skillDir) {
224
+ const files = [];
225
+
226
+ function walk(dir) {
227
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
228
+ for (const entry of entries) {
229
+ if (entry.name === 'node_modules' || entry.name === '.git') continue;
230
+ const fullPath = path.join(dir, entry.name);
231
+ if (entry.isDirectory()) {
232
+ walk(fullPath);
233
+ } else if (entry.isFile()) {
234
+ files.push(fullPath);
235
+ }
236
+ }
237
+ }
238
+
239
+ walk(skillDir);
240
+ return files;
241
+ }
242
+
243
+ // --- Rule Checks ---
244
+
245
+ function validateSkill(skillDir) {
246
+ const findings = [];
247
+ const dirName = path.basename(skillDir);
248
+ const skillMdPath = path.join(skillDir, 'SKILL.md');
249
+ const workflowMdPath = path.join(skillDir, 'workflow.md');
250
+ const stepsDir = path.join(skillDir, 'steps');
251
+
252
+ // Collect all files in the skill for PATH-02 and SEQ-02
253
+ const allFiles = collectSkillFiles(skillDir);
254
+
255
+ // --- SKILL-01: SKILL.md must exist ---
256
+ if (!fs.existsSync(skillMdPath)) {
257
+ findings.push({
258
+ rule: 'SKILL-01',
259
+ title: 'SKILL.md Must Exist',
260
+ severity: 'CRITICAL',
261
+ file: 'SKILL.md',
262
+ detail: 'SKILL.md not found in skill directory.',
263
+ fix: 'Create SKILL.md as the skill entrypoint.',
264
+ });
265
+ // Cannot check SKILL-02 through SKILL-07 without SKILL.md
266
+ return findings;
267
+ }
268
+
269
+ const skillContent = safeReadFile(skillMdPath, findings, 'SKILL.md');
270
+ if (skillContent === null) return findings;
271
+ const skillFm = parseFrontmatterMultiline(skillContent);
272
+
273
+ // --- SKILL-02: frontmatter has name ---
274
+ if (!skillFm || !('name' in skillFm)) {
275
+ findings.push({
276
+ rule: 'SKILL-02',
277
+ title: 'SKILL.md Must Have name in Frontmatter',
278
+ severity: 'CRITICAL',
279
+ file: 'SKILL.md',
280
+ detail: 'Frontmatter is missing the `name` field.',
281
+ fix: 'Add `name: <skill-name>` to the frontmatter.',
282
+ });
283
+ } else if (skillFm.name === '') {
284
+ findings.push({
285
+ rule: 'SKILL-02',
286
+ title: 'SKILL.md Must Have name in Frontmatter',
287
+ severity: 'CRITICAL',
288
+ file: 'SKILL.md',
289
+ detail: 'Frontmatter `name` field is empty.',
290
+ fix: 'Set `name` to the skill directory name (kebab-case).',
291
+ });
292
+ }
293
+
294
+ // --- SKILL-03: frontmatter has description ---
295
+ if (!skillFm || !('description' in skillFm)) {
296
+ findings.push({
297
+ rule: 'SKILL-03',
298
+ title: 'SKILL.md Must Have description in Frontmatter',
299
+ severity: 'CRITICAL',
300
+ file: 'SKILL.md',
301
+ detail: 'Frontmatter is missing the `description` field.',
302
+ fix: 'Add `description: <what it does and when to use it>` to the frontmatter.',
303
+ });
304
+ } else if (skillFm.description === '') {
305
+ findings.push({
306
+ rule: 'SKILL-03',
307
+ title: 'SKILL.md Must Have description in Frontmatter',
308
+ severity: 'CRITICAL',
309
+ file: 'SKILL.md',
310
+ detail: 'Frontmatter `description` field is empty.',
311
+ fix: 'Add a description stating what the skill does and when to use it.',
312
+ });
313
+ }
314
+
315
+ const name = skillFm && skillFm.name;
316
+ const description = skillFm && skillFm.description;
317
+
318
+ // Deprecated skills are thin compatibility shims that forward to a replacement.
319
+ // They intentionally omit a "Use when" trigger so users are steered to the new
320
+ // skill instead, so exempt them from the SKILL-06 trigger-phrase requirement.
321
+ const isDeprecated = typeof description === 'string' && /^\s*deprecated\b/i.test(description);
322
+
323
+ // --- SKILL-04: name format ---
324
+ if (name && !NAME_REGEX.test(name)) {
325
+ findings.push({
326
+ rule: 'SKILL-04',
327
+ title: 'name Format',
328
+ severity: 'HIGH',
329
+ file: 'SKILL.md',
330
+ detail: `name "${name}" does not match pattern: ${NAME_REGEX}`,
331
+ fix: 'Rename to comply with lowercase letters, numbers, and hyphens only (max 64 chars).',
332
+ });
333
+ }
334
+
335
+ // --- SKILL-05: name matches directory ---
336
+ if (name && name !== dirName) {
337
+ findings.push({
338
+ rule: 'SKILL-05',
339
+ title: 'name Must Match Directory Name',
340
+ severity: 'HIGH',
341
+ file: 'SKILL.md',
342
+ detail: `name "${name}" does not match directory name "${dirName}".`,
343
+ fix: `Change name to "${dirName}" or rename the directory.`,
344
+ });
345
+ }
346
+
347
+ // --- SKILL-06: description quality ---
348
+ if (description) {
349
+ if (description.length > 1024) {
350
+ findings.push({
351
+ rule: 'SKILL-06',
352
+ title: 'description Quality',
353
+ severity: 'MEDIUM',
354
+ file: 'SKILL.md',
355
+ detail: `description is ${description.length} characters (max 1024).`,
356
+ fix: 'Shorten the description to 1024 characters or less.',
357
+ });
358
+ }
359
+
360
+ if (!isDeprecated && !/use\s+when\b/i.test(description) && !/use\s+if\b/i.test(description)) {
361
+ findings.push({
362
+ rule: 'SKILL-06',
363
+ title: 'description Quality',
364
+ severity: 'MEDIUM',
365
+ file: 'SKILL.md',
366
+ detail: 'description does not contain "Use when" or "Use if" trigger phrase.',
367
+ fix: 'Append a "Use when..." clause to explain when to invoke this skill.',
368
+ });
369
+ }
370
+ }
371
+
372
+ // --- SKILL-07: SKILL.md must have body content after frontmatter ---
373
+ {
374
+ const trimmed = skillContent.trimStart();
375
+ let bodyStart = -1;
376
+ if (trimmed.startsWith('---')) {
377
+ let endIdx = trimmed.indexOf('\n---\n', 3);
378
+ if (endIdx !== -1) {
379
+ bodyStart = endIdx + 4;
380
+ } else if (trimmed.endsWith('\n---')) {
381
+ bodyStart = trimmed.length; // no body at all
382
+ }
383
+ } else {
384
+ bodyStart = 0; // no frontmatter, entire file is body
385
+ }
386
+ const body = bodyStart >= 0 ? trimmed.slice(bodyStart).trim() : '';
387
+ if (body === '') {
388
+ findings.push({
389
+ rule: 'SKILL-07',
390
+ title: 'SKILL.md Must Have Body Content',
391
+ severity: 'HIGH',
392
+ file: 'SKILL.md',
393
+ detail: 'SKILL.md has no content after frontmatter. L2 instructions are required.',
394
+ fix: 'Add markdown body with skill instructions after the closing ---.',
395
+ });
396
+ }
397
+ }
398
+
399
+ // --- PATH-02: no installed_path ---
400
+ for (const filePath of allFiles) {
401
+ // Only check markdown and yaml files
402
+ const ext = path.extname(filePath);
403
+ if (!['.md', '.yaml', '.yml'].includes(ext)) continue;
404
+
405
+ const relFile = path.relative(skillDir, filePath);
406
+ const content = safeReadFile(filePath, findings, relFile);
407
+ if (content === null) continue;
408
+
409
+ // Check frontmatter for installed_path key
410
+ const fm = parseFrontmatter(content);
411
+ if (fm && 'installed_path' in fm) {
412
+ findings.push({
413
+ rule: 'PATH-02',
414
+ title: 'No installed_path Variable',
415
+ severity: 'HIGH',
416
+ file: relFile,
417
+ detail: 'Frontmatter contains `installed_path:` key.',
418
+ fix: 'Remove `installed_path` from frontmatter. Use relative paths instead.',
419
+ });
420
+ }
421
+
422
+ // Check content for any mention of installed_path (variable ref, prose, bare text)
423
+ const stripped = stripCodeBlocks(content);
424
+ const lines = stripped.split('\n');
425
+ for (const [i, line] of lines.entries()) {
426
+ if (/installed_path/i.test(line)) {
427
+ findings.push({
428
+ rule: 'PATH-02',
429
+ title: 'No installed_path Variable',
430
+ severity: 'HIGH',
431
+ file: relFile,
432
+ line: i + 1,
433
+ detail: '`installed_path` reference found in content.',
434
+ fix: 'Remove all installed_path usage. Use relative paths (`./path` or `../path`) instead.',
435
+ });
436
+ }
437
+ }
438
+ }
439
+
440
+ // --- STEP-01: step filename format ---
441
+ // --- STEP-06: step frontmatter no name/description ---
442
+ // --- STEP-07: step count ---
443
+ // Only check the literal steps/ directory (variant directories like steps-c, steps-v
444
+ // use different naming conventions and are excluded per the rule specification)
445
+ if (fs.existsSync(stepsDir) && fs.statSync(stepsDir).isDirectory()) {
446
+ const stepDirName = 'steps';
447
+ const stepFiles = fs.readdirSync(stepsDir).filter((f) => f.endsWith('.md'));
448
+
449
+ // STEP-01: filename format
450
+ for (const stepFile of stepFiles) {
451
+ if (!STEP_FILENAME_REGEX.test(stepFile)) {
452
+ findings.push({
453
+ rule: 'STEP-01',
454
+ title: 'Step File Naming',
455
+ severity: 'MEDIUM',
456
+ file: path.join(stepDirName, stepFile),
457
+ detail: `Filename "${stepFile}" does not match pattern: ${STEP_FILENAME_REGEX}`,
458
+ fix: 'Rename to step-NN-description.md (NN = zero-padded number, optional letter suffix).',
459
+ });
460
+ }
461
+ }
462
+
463
+ // STEP-06: step frontmatter has no name/description
464
+ for (const stepFile of stepFiles) {
465
+ const stepPath = path.join(stepsDir, stepFile);
466
+ const stepContent = safeReadFile(stepPath, findings, path.join(stepDirName, stepFile));
467
+ if (stepContent === null) continue;
468
+ const stepFm = parseFrontmatter(stepContent);
469
+
470
+ if (stepFm) {
471
+ if ('name' in stepFm) {
472
+ findings.push({
473
+ rule: 'STEP-06',
474
+ title: 'Step File Frontmatter: No name or description',
475
+ severity: 'MEDIUM',
476
+ file: path.join(stepDirName, stepFile),
477
+ detail: 'Step file frontmatter contains `name:` — this is metadata noise.',
478
+ fix: 'Remove `name:` from step file frontmatter.',
479
+ });
480
+ }
481
+ if ('description' in stepFm) {
482
+ findings.push({
483
+ rule: 'STEP-06',
484
+ title: 'Step File Frontmatter: No name or description',
485
+ severity: 'MEDIUM',
486
+ file: path.join(stepDirName, stepFile),
487
+ detail: 'Step file frontmatter contains `description:` — this is metadata noise.',
488
+ fix: 'Remove `description:` from step file frontmatter.',
489
+ });
490
+ }
491
+ }
492
+ }
493
+
494
+ // STEP-07: step count 2-10
495
+ const stepCount = stepFiles.filter((f) => f.startsWith('step-')).length;
496
+ if (stepCount > 0 && (stepCount < 2 || stepCount > 10)) {
497
+ const detail =
498
+ stepCount < 2
499
+ ? `Only ${stepCount} step file found — consider inlining into workflow.md.`
500
+ : `${stepCount} step files found — more than 10 risks LLM context degradation.`;
501
+ findings.push({
502
+ rule: 'STEP-07',
503
+ title: 'Step Count',
504
+ severity: 'LOW',
505
+ file: stepDirName + '/',
506
+ detail,
507
+ fix: stepCount > 10 ? 'Consider consolidating steps.' : 'Consider expanding or inlining.',
508
+ });
509
+ }
510
+ }
511
+
512
+ // --- SEQ-02: no time estimates ---
513
+ for (const filePath of allFiles) {
514
+ const ext = path.extname(filePath);
515
+ if (!['.md', '.yaml', '.yml'].includes(ext)) continue;
516
+
517
+ const relFile = path.relative(skillDir, filePath);
518
+ const content = safeReadFile(filePath, findings, relFile);
519
+ if (content === null) continue;
520
+ const stripped = stripCodeBlocks(content);
521
+ const lines = stripped.split('\n');
522
+
523
+ for (const [i, line] of lines.entries()) {
524
+ for (const pattern of TIME_ESTIMATE_PATTERNS) {
525
+ if (pattern.test(line)) {
526
+ findings.push({
527
+ rule: 'SEQ-02',
528
+ title: 'No Time Estimates',
529
+ severity: 'LOW',
530
+ file: relFile,
531
+ line: i + 1,
532
+ detail: `Time estimate pattern found: "${line.trim()}"`,
533
+ fix: 'Remove time estimates — AI execution speed varies too much.',
534
+ });
535
+ break; // Only report once per line
536
+ }
537
+ }
538
+ }
539
+ }
540
+
541
+ // --- TPL-01: template files must not contain compile-time {{.var}} substitutions ---
542
+ // Template files seed durable, version-controlled artifacts (spec files) that
543
+ // execute on other machines. Baking a {{.var}} at render time would freeze a
544
+ // machine-local value into every downstream artifact.
545
+ for (const filePath of allFiles) {
546
+ if (path.extname(filePath) !== '.md') continue;
547
+ const base = path.basename(filePath);
548
+ if (!TEMPLATE_FILENAME_REGEX.test(base)) continue;
549
+
550
+ const relFile = path.relative(skillDir, filePath);
551
+ const content = safeReadFile(filePath, findings, relFile);
552
+ if (content === null) continue;
553
+
554
+ const lines = content.split('\n');
555
+ for (const [i, line] of lines.entries()) {
556
+ const match = line.match(COMPILE_TIME_SUB_REGEX);
557
+ if (match) {
558
+ findings.push({
559
+ rule: 'TPL-01',
560
+ title: 'Template files must not contain compile-time substitutions',
561
+ severity: 'HIGH',
562
+ file: relFile,
563
+ line: i + 1,
564
+ detail: `Template file contains compile-time substitution \`${match[0]}\` — this would be baked at render time and leak a machine-local value into every spec produced from the template.`,
565
+ fix: 'Remove the `{{.var}}` reference. Use single-curly `{var}` if the value should be resolved at LLM runtime by the consumer of the generated spec.',
566
+ });
567
+ }
568
+ }
569
+ }
570
+
571
+ return findings;
572
+ }
573
+
574
+ // --- Output Formatting ---
575
+
576
+ function formatHumanReadable(results) {
577
+ const output = [];
578
+ let totalFindings = 0;
579
+ const severityCounts = { CRITICAL: 0, HIGH: 0, MEDIUM: 0, LOW: 0 };
580
+
581
+ output.push(
582
+ `\nValidating skills in: ${SRC_DIR}`,
583
+ `Mode: ${STRICT ? 'STRICT (exit 1 on HIGH+)' : 'WARNING (exit 0)'}${JSON_OUTPUT ? ' + JSON' : ''}\n`,
584
+ );
585
+
586
+ let totalSkills = 0;
587
+ let skillsWithFindings = 0;
588
+
589
+ for (const { skillDir, findings } of results) {
590
+ totalSkills++;
591
+ const relDir = path.relative(PROJECT_ROOT, skillDir);
592
+
593
+ if (findings.length > 0) {
594
+ skillsWithFindings++;
595
+ output.push(`\n${relDir}`);
596
+
597
+ for (const f of findings) {
598
+ totalFindings++;
599
+ severityCounts[f.severity]++;
600
+ const location = f.line ? ` (line ${f.line})` : '';
601
+ output.push(` [${f.severity}] ${f.rule} — ${f.title}`, ` File: ${f.file}${location}`, ` ${f.detail}`);
602
+
603
+ if (process.env.GITHUB_ACTIONS) {
604
+ const absFile = path.join(skillDir, f.file);
605
+ const ghFile = path.relative(PROJECT_ROOT, absFile);
606
+ const line = f.line || 1;
607
+ const level = f.severity === 'LOW' ? 'notice' : 'warning';
608
+ console.log(`::${level} file=${ghFile},line=${line}::${escapeAnnotation(`${f.rule}: ${f.detail}`)}`);
609
+ }
610
+ }
611
+ }
612
+ }
613
+
614
+ // Summary
615
+ output.push(
616
+ `\n${'─'.repeat(60)}`,
617
+ `\nSummary:`,
618
+ ` Skills scanned: ${totalSkills}`,
619
+ ` Skills with findings: ${skillsWithFindings}`,
620
+ ` Total findings: ${totalFindings}`,
621
+ );
622
+
623
+ if (totalFindings > 0) {
624
+ output.push('', ` | Severity | Count |`, ` |----------|-------|`);
625
+ for (const sev of ['CRITICAL', 'HIGH', 'MEDIUM', 'LOW']) {
626
+ if (severityCounts[sev] > 0) {
627
+ output.push(` | ${sev.padEnd(8)} | ${String(severityCounts[sev]).padStart(5)} |`);
628
+ }
629
+ }
630
+ }
631
+
632
+ const hasHighPlus = severityCounts.CRITICAL > 0 || severityCounts.HIGH > 0;
633
+
634
+ if (totalFindings === 0) {
635
+ output.push(`\n All skills passed validation!`);
636
+ } else if (STRICT && hasHighPlus) {
637
+ output.push(`\n [STRICT MODE] HIGH+ findings found — exiting with failure.`);
638
+ } else if (STRICT) {
639
+ output.push(`\n [STRICT MODE] Only MEDIUM/LOW findings — pass.`);
640
+ } else {
641
+ output.push(`\n Run with --strict to treat HIGH+ findings as errors.`);
642
+ }
643
+
644
+ output.push('');
645
+
646
+ // Write GitHub Actions step summary
647
+ if (process.env.GITHUB_STEP_SUMMARY) {
648
+ let summary = '## Skill Validation\n\n';
649
+ if (totalFindings > 0) {
650
+ summary += '| Skill | Rule | Severity | File | Detail |\n';
651
+ summary += '|-------|------|----------|------|--------|\n';
652
+ for (const { skillDir, findings } of results) {
653
+ const relDir = path.relative(PROJECT_ROOT, skillDir);
654
+ for (const f of findings) {
655
+ summary += `| ${escapeTableCell(relDir)} | ${f.rule} | ${f.severity} | ${escapeTableCell(f.file)} | ${escapeTableCell(f.detail)} |\n`;
656
+ }
657
+ }
658
+ summary += '\n';
659
+ }
660
+ summary += `**${totalSkills} skills scanned, ${totalFindings} findings**\n`;
661
+ fs.appendFileSync(process.env.GITHUB_STEP_SUMMARY, summary);
662
+ }
663
+
664
+ return { output: output.join('\n'), hasHighPlus };
665
+ }
666
+
667
+ function formatJson(results) {
668
+ const allFindings = [];
669
+ for (const { skillDir, findings } of results) {
670
+ const relDir = path.relative(PROJECT_ROOT, skillDir);
671
+ for (const f of findings) {
672
+ allFindings.push({
673
+ skill: relDir,
674
+ rule: f.rule,
675
+ title: f.title,
676
+ severity: f.severity,
677
+ file: f.file,
678
+ line: f.line || null,
679
+ detail: f.detail,
680
+ fix: f.fix,
681
+ });
682
+ }
683
+ }
684
+
685
+ // Sort by severity
686
+ allFindings.sort((a, b) => SEVERITY_ORDER[a.severity] - SEVERITY_ORDER[b.severity]);
687
+
688
+ const hasHighPlus = allFindings.some((f) => f.severity === 'CRITICAL' || f.severity === 'HIGH');
689
+
690
+ return { output: JSON.stringify(allFindings, null, 2), hasHighPlus };
691
+ }
692
+
693
+ // --- Main ---
694
+
695
+ if (require.main === module) {
696
+ // Determine which skills to validate
697
+ let skillDirs;
698
+
699
+ if (positionalArgs.length > 0) {
700
+ // Single skill directory specified
701
+ const target = path.resolve(positionalArgs[0]);
702
+ if (!fs.existsSync(target) || !fs.statSync(target).isDirectory()) {
703
+ console.error(`Error: "${positionalArgs[0]}" is not a valid directory.`);
704
+ process.exit(2);
705
+ }
706
+ skillDirs = [target];
707
+ } else {
708
+ // Discover all skills
709
+ skillDirs = discoverSkillDirs([SRC_DIR]);
710
+ }
711
+
712
+ if (skillDirs.length === 0) {
713
+ console.error('No skill directories found.');
714
+ process.exit(2);
715
+ }
716
+
717
+ // Validate each skill
718
+ const results = [];
719
+ for (const skillDir of skillDirs) {
720
+ const findings = validateSkill(skillDir);
721
+ results.push({ skillDir, findings });
722
+ }
723
+
724
+ // Format output
725
+ const { output, hasHighPlus } = JSON_OUTPUT ? formatJson(results) : formatHumanReadable(results);
726
+ console.log(output);
727
+
728
+ // Exit code
729
+ if (STRICT && hasHighPlus) {
730
+ process.exit(1);
731
+ }
732
+ }
733
+
734
+ // --- Exports (for testing) ---
735
+ module.exports = { parseFrontmatter, parseFrontmatterMultiline, validateSkill, discoverSkillDirs };