bmad-method 6.7.1 → 6.8.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 (114) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/README.md +10 -0
  3. package/package.json +3 -2
  4. package/removals.txt +8 -0
  5. package/src/bmm-skills/1-analysis/bmad-agent-analyst/SKILL.md +2 -0
  6. package/src/bmm-skills/1-analysis/bmad-agent-tech-writer/SKILL.md +2 -0
  7. package/src/bmm-skills/1-analysis/bmad-document-project/SKILL.md +1 -1
  8. package/src/bmm-skills/1-analysis/bmad-prfaq/SKILL.md +1 -1
  9. package/src/bmm-skills/1-analysis/bmad-product-brief/SKILL.md +5 -2
  10. package/src/bmm-skills/1-analysis/research/bmad-domain-research/SKILL.md +1 -1
  11. package/src/bmm-skills/1-analysis/research/bmad-market-research/SKILL.md +1 -1
  12. package/src/bmm-skills/1-analysis/research/bmad-technical-research/SKILL.md +1 -1
  13. package/src/bmm-skills/2-plan-workflows/bmad-agent-pm/SKILL.md +2 -0
  14. package/src/bmm-skills/2-plan-workflows/bmad-agent-ux-designer/SKILL.md +2 -0
  15. package/src/bmm-skills/2-plan-workflows/bmad-agent-ux-designer/customize.toml +1 -1
  16. package/src/bmm-skills/2-plan-workflows/bmad-prd/SKILL.md +9 -4
  17. package/src/bmm-skills/2-plan-workflows/bmad-prd/assets/prd-template.md +4 -7
  18. package/src/bmm-skills/2-plan-workflows/bmad-prd/assets/prd-validation-checklist.md +4 -4
  19. package/src/bmm-skills/2-plan-workflows/bmad-prd/references/headless.md +2 -2
  20. package/src/bmm-skills/2-plan-workflows/bmad-ux/SKILL.md +90 -0
  21. package/src/bmm-skills/2-plan-workflows/bmad-ux/assets/color-themes.md +9 -0
  22. package/src/bmm-skills/2-plan-workflows/bmad-ux/assets/design-directions.md +9 -0
  23. package/src/bmm-skills/2-plan-workflows/bmad-ux/assets/design-example-editorial.md +158 -0
  24. package/src/bmm-skills/2-plan-workflows/bmad-ux/assets/design-example-mobile.md +93 -0
  25. package/src/bmm-skills/2-plan-workflows/bmad-ux/assets/design-example-shadcn.md +109 -0
  26. package/src/bmm-skills/2-plan-workflows/bmad-ux/assets/excalidraw-wireframe.md +19 -0
  27. package/src/bmm-skills/2-plan-workflows/bmad-ux/assets/experience-example-mobile.md +112 -0
  28. package/src/bmm-skills/2-plan-workflows/bmad-ux/assets/experience-example-shadcn.md +133 -0
  29. package/src/bmm-skills/2-plan-workflows/bmad-ux/assets/headless-schemas.md +84 -0
  30. package/src/bmm-skills/2-plan-workflows/bmad-ux/assets/key-screens.md +29 -0
  31. package/src/bmm-skills/2-plan-workflows/bmad-ux/assets/validation-report-template.html +319 -0
  32. package/src/bmm-skills/2-plan-workflows/bmad-ux/customize.toml +100 -0
  33. package/src/bmm-skills/2-plan-workflows/bmad-ux/references/creative-tools.md +19 -0
  34. package/src/bmm-skills/2-plan-workflows/bmad-ux/references/design-md-spec.md +50 -0
  35. package/src/bmm-skills/2-plan-workflows/bmad-ux/references/headless.md +37 -0
  36. package/src/bmm-skills/2-plan-workflows/bmad-ux/references/validate.md +115 -0
  37. package/src/bmm-skills/3-solutioning/bmad-agent-architect/SKILL.md +2 -0
  38. package/src/bmm-skills/3-solutioning/bmad-check-implementation-readiness/SKILL.md +1 -1
  39. package/src/bmm-skills/3-solutioning/bmad-create-architecture/SKILL.md +1 -1
  40. package/src/bmm-skills/3-solutioning/bmad-create-epics-and-stories/SKILL.md +1 -1
  41. package/src/bmm-skills/3-solutioning/bmad-generate-project-context/SKILL.md +1 -1
  42. package/src/bmm-skills/4-implementation/bmad-agent-dev/SKILL.md +2 -0
  43. package/src/bmm-skills/4-implementation/bmad-checkpoint-preview/SKILL.md +1 -1
  44. package/src/bmm-skills/4-implementation/bmad-code-review/SKILL.md +1 -1
  45. package/src/bmm-skills/4-implementation/bmad-correct-course/SKILL.md +1 -1
  46. package/src/bmm-skills/4-implementation/bmad-create-story/SKILL.md +1 -1
  47. package/src/bmm-skills/4-implementation/bmad-dev-story/SKILL.md +23 -8
  48. package/src/bmm-skills/4-implementation/bmad-investigate/SKILL.md +2 -0
  49. package/src/bmm-skills/4-implementation/bmad-qa-generate-e2e-tests/SKILL.md +1 -1
  50. package/src/bmm-skills/4-implementation/bmad-quick-dev/SKILL.md +1 -1
  51. package/src/bmm-skills/4-implementation/bmad-retrospective/SKILL.md +1 -1
  52. package/src/bmm-skills/4-implementation/bmad-sprint-planning/SKILL.md +2 -1
  53. package/src/bmm-skills/4-implementation/bmad-sprint-status/SKILL.md +2 -1
  54. package/src/bmm-skills/module-help.csv +1 -1
  55. package/src/core-skills/bmad-advanced-elicitation/methods.csv +69 -50
  56. package/src/core-skills/bmad-brainstorming/steps/step-03-technique-execution.md +6 -4
  57. package/src/core-skills/bmad-brainstorming/workflow.md +1 -1
  58. package/src/core-skills/bmad-spec/SKILL.md +129 -0
  59. package/src/core-skills/bmad-spec/assets/headless-schemas.md +33 -0
  60. package/src/core-skills/bmad-spec/assets/spec-template.md +49 -0
  61. package/src/core-skills/bmad-spec/customize.toml +53 -0
  62. package/src/core-skills/module-help.csv +1 -1
  63. package/src/scripts/resolve_customization.py +9 -1
  64. package/src/scripts/tests/test_resolve_customization.py +50 -0
  65. package/tools/bundle-web-bundles.js +117 -0
  66. package/tools/installer/modules/custom-module-manager.js +113 -4
  67. package/tools/installer/modules/official-modules.js +83 -3
  68. package/tools/skill-validator.md +1 -19
  69. package/tools/validate-sidebar-order.js +388 -0
  70. package/tools/validate-skills.js +1 -40
  71. package/web-bundles/README.md +46 -0
  72. package/web-bundles/brainstorming-coach/INSTRUCTIONS.md +86 -0
  73. package/web-bundles/brainstorming-coach/SKILL.md +83 -0
  74. package/web-bundles/brainstorming-coach/brain-methods.csv +62 -0
  75. package/web-bundles/bundles.json +139 -0
  76. package/web-bundles/market-and-industry-research/INSTRUCTIONS.md +88 -0
  77. package/web-bundles/market-and-industry-research/SKILL.md +59 -0
  78. package/web-bundles/prd-coach/INSTRUCTIONS.md +86 -0
  79. package/web-bundles/prd-coach/SKILL.md +101 -0
  80. package/web-bundles/prd-coach/prd-template.md +165 -0
  81. package/web-bundles/prd-coach/prd-validation-checklist.md +135 -0
  82. package/web-bundles/prfaq-coach/INSTRUCTIONS.md +86 -0
  83. package/web-bundles/prfaq-coach/SKILL.md +139 -0
  84. package/web-bundles/product-brief-coach/INSTRUCTIONS.md +86 -0
  85. package/web-bundles/product-brief-coach/SKILL.md +113 -0
  86. package/web-bundles/ux-coach/INSTRUCTIONS.md +92 -0
  87. package/web-bundles/ux-coach/SKILL.md +187 -0
  88. package/web-bundles/ux-coach/ux-validation.md +100 -0
  89. package/src/bmm-skills/2-plan-workflows/bmad-create-ux-design/SKILL.md +0 -75
  90. package/src/bmm-skills/2-plan-workflows/bmad-create-ux-design/customize.toml +0 -41
  91. package/src/bmm-skills/2-plan-workflows/bmad-create-ux-design/steps/step-01-init.md +0 -135
  92. package/src/bmm-skills/2-plan-workflows/bmad-create-ux-design/steps/step-01b-continue.md +0 -127
  93. package/src/bmm-skills/2-plan-workflows/bmad-create-ux-design/steps/step-02-discovery.md +0 -190
  94. package/src/bmm-skills/2-plan-workflows/bmad-create-ux-design/steps/step-03-core-experience.md +0 -217
  95. package/src/bmm-skills/2-plan-workflows/bmad-create-ux-design/steps/step-04-emotional-response.md +0 -220
  96. package/src/bmm-skills/2-plan-workflows/bmad-create-ux-design/steps/step-05-inspiration.md +0 -235
  97. package/src/bmm-skills/2-plan-workflows/bmad-create-ux-design/steps/step-06-design-system.md +0 -253
  98. package/src/bmm-skills/2-plan-workflows/bmad-create-ux-design/steps/step-07-defining-experience.md +0 -255
  99. package/src/bmm-skills/2-plan-workflows/bmad-create-ux-design/steps/step-08-visual-foundation.md +0 -225
  100. package/src/bmm-skills/2-plan-workflows/bmad-create-ux-design/steps/step-09-design-directions.md +0 -225
  101. package/src/bmm-skills/2-plan-workflows/bmad-create-ux-design/steps/step-10-user-journeys.md +0 -242
  102. package/src/bmm-skills/2-plan-workflows/bmad-create-ux-design/steps/step-11-component-strategy.md +0 -249
  103. package/src/bmm-skills/2-plan-workflows/bmad-create-ux-design/steps/step-12-ux-patterns.md +0 -238
  104. package/src/bmm-skills/2-plan-workflows/bmad-create-ux-design/steps/step-13-responsive-accessibility.md +0 -265
  105. package/src/bmm-skills/2-plan-workflows/bmad-create-ux-design/steps/step-14-complete.md +0 -177
  106. package/src/bmm-skills/2-plan-workflows/bmad-create-ux-design/ux-design-template.md +0 -13
  107. package/src/core-skills/bmad-distillator/SKILL.md +0 -177
  108. package/src/core-skills/bmad-distillator/agents/distillate-compressor.md +0 -116
  109. package/src/core-skills/bmad-distillator/agents/round-trip-reconstructor.md +0 -68
  110. package/src/core-skills/bmad-distillator/resources/compression-rules.md +0 -51
  111. package/src/core-skills/bmad-distillator/resources/distillate-format-reference.md +0 -227
  112. package/src/core-skills/bmad-distillator/resources/splitting-strategy.md +0 -78
  113. package/src/core-skills/bmad-distillator/scripts/analyze_sources.py +0 -300
  114. package/src/core-skills/bmad-distillator/scripts/tests/test_analyze_sources.py +0 -204
@@ -0,0 +1,129 @@
1
+ ---
2
+ name: bmad-spec
3
+ description: Distill any intent input into the SPEC kernel + companions — the canonical, preservation-validated machine contract for downstream work. Use when the user says "create a spec", "distill this into a spec", "validate this spec", or "update the spec".
4
+ ---
5
+
6
+ # BMad Spec
7
+ ## Overview
8
+
9
+ Canonical transformer for the BMad spec-kernel ecosystem. Takes any intent input — vague idea, brain dump, PRD, GDD, RFC, brief, Slack thread, customer email, meeting transcript, mockups, mixed multi-source — and produces **SPEC.md** carrying the five-field kernel (Why, Capabilities, Constraints, Non-goals, Success signal) plus companion files for load-bearing content that does not fit or would bloat the kernel with expansive line-item detail. Together they are the machine contract every downstream BMad skill consumes.
10
+
11
+ Multiple skills may call to update the same spec over time.
12
+
13
+ ## Conventions
14
+
15
+ - Bare paths (e.g. `assets/spec-template.md`) resolve from the skill root.
16
+ - `{skill-root}` is this skill's install dir; `{project-root}` is the working dir.
17
+ - `{workflow.<name>}` resolves to fields in `customize.toml`.
18
+
19
+ ## On Activation
20
+
21
+ 1. Resolve customization: `python3 {project-root}/_bmad/scripts/resolve_customization.py --skill {skill-root} --key workflow`. On failure, read `{skill-root}/customize.toml` directly.
22
+ 2. Run `{workflow.activation_steps_prepend}`. Treat `{workflow.persistent_facts}` as foundational context (`file:` entries are loaded).
23
+ 3. Load `{project-root}/_bmad/core/config.yaml` (and `config.user.yaml` if present), root level and `bmm` section. Resolve `{user_name}`, `{communication_language}`, `{document_output_language}`, `{planning_artifacts}`, `{project_name}`, `{date}`.
24
+ 4. Detect mode. **Headless** when any of: no TTY, programmatic caller (another skill or non-interactive runner), or the first message pre-supplies all inputs and asks for an artifact path back. **Interactive** otherwise. In interactive mode, greet by `{user_name}` in `{communication_language}`, stay in that language, and mention that `bmad-party-mode` and `bmad-advanced-elicitation` are available for deeper exploration on any field.
25
+
26
+ Run `{workflow.activation_steps_append}`.
27
+
28
+ Activation is complete. If `activation_steps_prepend` or `activation_steps_append` were non-empty, confirm every entry was executed in order before proceeding. Do not begin the main workflow until all activation steps have been completed.
29
+
30
+ ## Workspace
31
+
32
+ The spec is **always a folder** named `{workflow.spec_output_path}/{workflow.run_folder_pattern}`, resolving by default to `{output_folder}/specs/spec-{slug}/`.
33
+
34
+ `{slug}` describes the thing being specced, not the input shape:
35
+
36
+ - Source artifact already carries a slug (e.g., `prd-foo-bar-2026-05-23/`): inherit (`foo-bar`).
37
+ - Sparse, in-chat, or multi-source input: interactive asks; headless caller provides it as part of the input. If absent and underivable, headless blocks with `error_code: "missing_slug"`.
38
+ - Same slug = same folder. A second invocation with the same `{slug}` lands at the existing spec folder and updates in place, preserving capability IDs.
39
+
40
+ **No input.** Interactive: ask the user to share a file path, paste content, explain the idea in detail, or point to a source. Headless: respond with JSON containing `error_code: "insufficient_intent"`.
41
+
42
+ Inside the spec folder:
43
+
44
+ ```
45
+ <spec-folder>/
46
+ SPEC.md ← uppercase, the kernel
47
+ <companion-1>.md ← optional, content-typed (e.g. glossary.md)
48
+ <companion-2>.md
49
+ .decision-log.md ← canonical memory for this spec
50
+ ```
51
+
52
+ ## The Operation
53
+
54
+ Read the input and its ancillary linked materials. If there is no input, follow the no-input branch in **Workspace** (ask or block). If a prior `SPEC.md` exists at the target folder, read it too — the operation becomes an update. Preserve capability IDs; new capabilities get the next unused `CAP-N`; never reuse retired IDs. Otherwise this is a create.
55
+
56
+ When the input is structured and pre-sorted (a PRD with an addendum, a GDD, a brief produced by an upstream BMad skill), trust the authored separation: lift kernel-fitting content into SPEC.md, lift overflow into appropriately-named companions. When the input is mixed (a brain dump, a transcript, an RFC, a customer email), do the sorting yourself: walk each claim, apply the three-lens load-bearing test (Spec Law rule 7), and route to the kernel field or a companion.
57
+
58
+ Distill the input into the five-field kernel using `{workflow.spec_template}` as the skeleton. When input is rich, extract directly — no elicitation. When input is sparse, choose: **express** (best-effort distill, every gap becomes an `open_questions[]` entry) or **guided** (walk the five fields with the user one at a time). Headless defaults to express and logs the choice. Interactive asks.
59
+
60
+ Write lean from the first pass: every sentence must earn its place. Decoration costs tokens and dilutes downstream readers.
61
+
62
+ If the input is genuinely too thin to distill (e.g. "an app for hikers" with no surrounding context), stop and suggest `bmad-prd` (or sibling ceremony skill). This skill distills; it does not coach.
63
+
64
+ ## Load-bearing
65
+
66
+ A claim is **load-bearing** if any consumer (downstream skill, implementing agent, verification pass) would change a decision without it.
67
+
68
+ ## Companions
69
+
70
+ When load-bearing content does not fit the five-field kernel, it lives in a companion. The kernel cites it; the companion holds it. Companions are part of the contract; every consumer reads `companions:` in SPEC.md frontmatter to discover them. Companions follow the same lean discipline as SPEC.md (Spec Law rule 8).
71
+
72
+ **Spawn a companion when the content needs more than one kernel-shape line:** multi-item catalogs (per-entity matrices like archetypes, drinks, modes, routes), tables, diagrams (always), editorial voice rules, long-form reference material the kernel cites by name (glossary, brownfield notes, project conventions). Single-line decision-benders stay in Constraints; intent+success pairs stay in Capabilities. If a kernel field is starting to bullet into sub-bullets, the content has outgrown the kernel and wants a companion.
73
+
74
+ Companions are either:
75
+
76
+ - **Spec-authored** companions are written by bmad-spec and live as **siblings of SPEC.md** (e.g., `glossary.md`, `patron-archetypes.md`). bmad-spec owns them and may edit them on update operations.
77
+ - **Adopted** companions are load-bearing artifacts written by an upstream skill that downstream still needs to read. bmad-spec references them into `companions:` by relative path but does NOT edit them (e.g., a `DESIGN.md` or `EXPERIENCE.md` from a UX run, an integration partner's API spec). The originating skill owns them.
78
+
79
+ Two rules govern companions:
80
+
81
+ 1. **Name spec-authored companions for the content type they hold.** `glossary.md`, `<entity-class>.md` (e.g. `patron-archetypes.md`, `medication-routes.md`, `flight-modes.md`), `stack.md`, `conventions.md`, `brownfield.md`, `architecture-diagrams.md`, `state-machines.md`, `failure-modes.md`, `compliance-references.md`. The principle: "a reader should know what is inside before opening it." Adopted companions keep whatever name their originating skill gave them.
82
+ 2. **Diagrams always land in a companion**, regardless of size. SPEC.md kernel holds prose only. Mermaid blocks, ASCII diagrams, and image references all live in a companion (e.g. `architecture-diagrams.md`), with sibling image files referenced from there.
83
+
84
+ Pre-existing project-wide docs (e.g. `project-context.md`) that downstream needs are listed as **adopted companions**, never duplicated into SPEC.md or a spec-authored companion.
85
+
86
+ ## Spec Law
87
+
88
+ Every spec must satisfy these eight rules. The operation aims for them; the self-validate sweep enforces them.
89
+
90
+ 1. **Each capability has both `intent` and `success`.** Missing either = not a capability.
91
+ 2. **Intents describe WHAT, not HOW.** Implementation prescription belongs in a companion (stack, conventions).
92
+ 3. **Constraints actually bend design decisions.** A "constraint" that rules nothing out is decoration.
93
+ 4. **Non-goals are explicit.** At least one. Absence means downstream skills fill the vacuum.
94
+ 5. **Success signal is concrete enough to test or demonstrate against.** "Users love it" doesn't qualify.
95
+ 6. **Capability IDs are stable and unique.** Never reused, never renumbered.
96
+ 7. **Preservation.** Every load-bearing source claim lands in SPEC.md or a companion. Wrapper ceremony does not.
97
+ 8. **Lean prose.** Every sentence carries load-bearing content. Cut decoration, hedges, backstory, throat-clearing. Applies to SPEC.md, companions, and `.decision-log.md`.
98
+
99
+ ## Self-Validate
100
+
101
+ After every create or update, sweep the resulting artifact in **two passes** before presenting.
102
+
103
+ **Pass 1 — Coherence.** Judge the spec against Spec Law rules 1–6 and 8. For anything that fails or feels weak, attempt to fix it without inventing content the input did not support. Calls made without direct confirmation become `assumptions[]`; gaps that could not be filled become `open_questions[]`.
104
+
105
+ **Pass 2 — Preservation.** Walk the source claim by claim. Confirm each load-bearing claim landed in SPEC.md or a companion. Wrapper-ceremony drops are logged under "Wrapper-only content" so the drop is on the record, not silent.
106
+
107
+ Append a one-paragraph verdict to `.decision-log.md` covering both passes. In interactive mode, review the verdict with the user. In headless mode, `.decision-log.md` is one of the files returned, so the caller (or its downstream LLM) reads the verdict there.
108
+
109
+ ## Spec with no change signal
110
+
111
+ When the user points the skill at an existing spec folder (or its SPEC.md) with no change signal, offer to review assumptions or open questions, or determine what they want to do.
112
+
113
+ ## Output
114
+
115
+ **Interactive** — share the spec folder path conversationally. Name the capability count, the companions produced, and the verdict in one or two sentences. If `assumptions[]` or `open_questions[]` are non-empty, list them (short — one line each) and invite the user to walk through them. Make clear that addressing them can update the source input (if it was a file), the spec, or both — whichever combination the user prefers. Do not dump JSON or present a wall of output.
116
+
117
+ **Headless** — return JSON per `assets/headless-schemas.md`.
118
+
119
+ Run `{workflow.on_complete}` if set.
120
+
121
+ ## After Spec is Output
122
+
123
+ Any update to spec regarding assumptions, open questions, or other changes should be appended to that source's decision log also and offer to update the source.
124
+
125
+ ## Frontmatter conventions
126
+
127
+ - `companions:` array of `.md` files downstream MUST read alongside SPEC.md to have the full contract. Paths may point inside the spec folder (spec-authored companions like `glossary.md`) or outside it (adopted companions like `../planning-artifacts/ux-designs/ux-foo-bar-2026-05-23/DESIGN.md`). The split between spec-authored and adopted is implicit by path; downstream treats both the same.
128
+ - `sources:` array of paths to files that were **fully absorbed** into the SPEC, with no remaining downstream value (e.g., a PRD whose every load-bearing claim is now in the kernel). Listed for audit and for bmad-spec to re-read on update. Downstream does NOT read these. Files that downstream still needs to read belong in `companions:`, not here.
129
+ - **Do not list** decision logs, README files, organizational artifacts, or any operational record of how upstream skills produced their artifacts. Those are not source content; they are process metadata that downstream consumers don't need.
@@ -0,0 +1,33 @@
1
+ # Headless JSON Response
2
+
3
+ The default invocation is headless: input goes in, JSON comes out. The contract is intentionally tiny — return the outcome and the files touched. Anything else a caller needs is inside those files (SPEC.md, companions, `.decision-log.md`).
4
+
5
+ ## Success
6
+
7
+ ```json
8
+ {
9
+ "status": "complete",
10
+ "files": [
11
+ "_bmad-output/specs/spec-quarter-drop/SPEC.md",
12
+ "_bmad-output/specs/spec-quarter-drop/glossary.md",
13
+ "_bmad-output/specs/spec-quarter-drop/.decision-log.md"
14
+ ]
15
+ }
16
+ ```
17
+
18
+ `files` lists every file written or modified in this run, in any order. The spec folder, kernel filename, decision log location, capabilities, companions, and verdict are all readable from those files; no need to re-encode them in the response.
19
+
20
+ ## Blocked
21
+
22
+ ```json
23
+ {
24
+ "status": "blocked",
25
+ "error_code": "insufficient_intent",
26
+ "reason": "Input was a one-line idea with no surrounding context; too thin to distill. Suggest bmad-prd to draw the vision out first."
27
+ }
28
+ ```
29
+
30
+ Defined `error_code` values:
31
+
32
+ - `insufficient_intent` — input too thin to distill into a kernel.
33
+ - `missing_slug` — input is sparse or multi-source and no slug was provided by the caller or derivable from a source path.
@@ -0,0 +1,49 @@
1
+ ---
2
+ id: SPEC-{slug}
3
+ companions: [] # files downstream MUST read alongside SPEC.md. Paths may point inside the spec folder (spec-authored) or outside it (adopted from an upstream skill).
4
+ sources: [] # files fully absorbed into the SPEC (audit only; downstream does NOT read these). Never decision logs.
5
+ ---
6
+
7
+ > **Canonical contract.** This SPEC and the files in `companions:` are the complete, preservation-validated contract for what to build, test, and validate. Source documents listed in frontmatter are for traceability only — consult them only if you need narrative rationale or prose color this contract intentionally omits.
8
+
9
+ # {Spec Title}
10
+
11
+ ## Why
12
+
13
+ {One paragraph naming the force behind this work. A spec can exist for any of:
14
+ - **a pain to solve** — a user or operator is stuck on a specific gap;
15
+ - **an opportunity to capture** — something newly possible we want to claim;
16
+ - **a vision to realize** — a thing we want to make exist because we want it to exist;
17
+ - **a mandate to meet** — a regulation, deprecation, deadline, or contractual obligation.
18
+
19
+ Name which (or which combination) applies, who is affected, and the backdrop that makes it matter now. This is the anchor every downstream trade-off resolves against.}
20
+
21
+ ## Capabilities
22
+
23
+ - id: CAP-1
24
+ intent: {One sentence. "User or system can do X to achieve Y." WHAT, not HOW.}
25
+ success: {Testable or demonstrable criterion. Something a test or a real demonstration can decide.}
26
+
27
+ ## Constraints
28
+
29
+ - {A non-negotiable that bends design. If it doesn't rule anything out, it doesn't belong.}
30
+
31
+ ## Non-goals
32
+
33
+ - {Explicit out-of-scope item. At least one. Stops downstream from filling the vacuum.}
34
+
35
+ ## Success signal
36
+
37
+ - {One or two sentences. World-change moment, not dashboard. Concrete enough to write a test or run a demonstration against.}
38
+
39
+ ## Assumptions
40
+
41
+ <!-- Optional. Omit this section entirely if empty. Inferred calls made without direct confirmation from the input. -->
42
+
43
+ - {Statement of fact the Spec proceeded under, e.g. "Assumed mobile-first since input mentioned GPS but no platform."}
44
+
45
+ ## Open Questions
46
+
47
+ <!-- Optional. Omit this section entirely if empty. Gaps the input did not resolve that need a human decision before downstream skills consume the Spec. -->
48
+
49
+ - {Question phrased so a human can answer it, e.g. "Is offline playback in scope for CAP-2?"}
@@ -0,0 +1,53 @@
1
+ # DO NOT EDIT -- overwritten on every update.
2
+ #
3
+ # Workflow customization surface for bmad-spec.
4
+ #
5
+ # Override files (not edited here):
6
+ # {project-root}/_bmad/custom/bmad-spec.toml (team)
7
+ # {project-root}/_bmad/custom/bmad-spec.user.toml (personal)
8
+
9
+ [workflow]
10
+
11
+ # --- Configurable below. Overrides merge per BMad structural rules: ---
12
+ # scalars: override wins • arrays: append
13
+
14
+ # Steps to run before the standard activation (config load, greet).
15
+ activation_steps_prepend = []
16
+
17
+ # Steps to run after greet but before the operation begins.
18
+ activation_steps_append = []
19
+
20
+ # Persistent facts the workflow keeps in mind for the whole run.
21
+ # Each entry is either a literal sentence, a skill prefixed with `skill:`,
22
+ # or a `file:`-prefixed path/glob whose contents are loaded as facts.
23
+ # Default points to a single top-level file; override in team/user TOML
24
+ # to widen the scope (e.g. `_bmad/**/project-context.md`) if needed.
25
+ persistent_facts = [
26
+ "file:{project-root}/project-context.md",
27
+ ]
28
+
29
+ # Executed when the workflow completes. Scalar or array of instructions.
30
+ on_complete = ""
31
+
32
+ # Spec template. The five-field kernel skeleton. Override the path in
33
+ # team/user TOML to enforce a different shape (e.g. a hypothesis field
34
+ # for research initiatives, or a mechanics field for games).
35
+ spec_template = "assets/spec-template.md"
36
+
37
+ # Canonical filename for the kernel artifact inside the spec folder.
38
+ # Uppercase by convention to signal "the central source of truth."
39
+ spec_filename = "SPEC.md"
40
+
41
+ # Output path for spec folders. Lands directly under {output_folder}
42
+ # so bmad-spec works in core-only installs and matches the
43
+ # long-term BMad direction of grouping artifacts as siblings under
44
+ # {output_folder}/<type>/ rather than nested inside planning vs
45
+ # implementation folders.
46
+ spec_output_path = "{output_folder}/specs"
47
+
48
+ # Run-folder pattern inside spec_output_path. Resolved against the
49
+ # input-derived slug at activation. Same slug = same folder, so a
50
+ # second invocation updates the existing spec in place (capability
51
+ # IDs preserved). Override to add {date} or other components if a
52
+ # fresh dated history is preferred.
53
+ run_folder_pattern = "spec-{slug}"
@@ -9,5 +9,5 @@ Core,bmad-editorial-review-prose,Editorial Review - Prose,EP,Use after drafting
9
9
  Core,bmad-editorial-review-structure,Editorial Review - Structure,ES,Use when doc produced from multiple subprocesses or needs structural improvement.,,[path],anytime,,,false,report located with target document,
10
10
  Core,bmad-review-adversarial-general,Adversarial Review,AR,"Use for quality assurance or before finalizing deliverables. Code Review in other modules runs this automatically, but also useful for document reviews.",,[path],anytime,,,false,,
11
11
  Core,bmad-review-edge-case-hunter,Edge Case Hunter Review,ECH,Use alongside adversarial review for orthogonal coverage — method-driven not attitude-driven.,,[path],anytime,,,false,,
12
- Core,bmad-distillator,Distillator,DG,Use when you need token-efficient distillates that preserve all information for downstream LLM consumption.,,[path],anytime,,,false,adjacent to source document or specified output_path,distillate markdown file(s)
12
+ Core,bmad-spec,Spec,SP,"Use to distill any intent input (brief, PRD, transcript, brain dump, design folder, mixed multi-source) into a succinct, no-fluff SPEC.md contract + companions that downstream work derives from. Locks the WHAT before the HOW. Works for software, game design, research, editorial, policy, business, anything intent-bearing. Validation mode also available.",,[path],anytime,,,false,{output_folder}/specs/spec-{slug},SPEC.md + companion files
13
13
  Core,bmad-customize,BMad Customize,BC,"Use when you want to change how an agent or workflow behaves — add persistent facts, swap templates, insert activation hooks, or customize menus. Scans what's customizable, picks the right scope (agent vs workflow), writes the override to _bmad/custom/, and verifies the merge. No TOML hand-authoring required.",,,anytime,,,false,{project-root}/_bmad/custom,TOML override files
@@ -177,6 +177,14 @@ def extract_key(data, dotted_key: str):
177
177
  return current
178
178
 
179
179
 
180
+ def write_json_stdout(output):
181
+ """Write JSON as UTF-8 so Windows cp1252 stdout can carry emoji icons."""
182
+ reconfigure = getattr(sys.stdout, "reconfigure", None)
183
+ if reconfigure is not None:
184
+ reconfigure(encoding="utf-8")
185
+ sys.stdout.write(json.dumps(output, indent=2, ensure_ascii=False) + "\n")
186
+
187
+
180
188
  def main():
181
189
  parser = argparse.ArgumentParser(
182
190
  description="Resolve customization for a BMad skill using three-layer TOML merge.",
@@ -223,7 +231,7 @@ def main():
223
231
  else:
224
232
  output = merged
225
233
 
226
- sys.stdout.write(json.dumps(output, indent=2, ensure_ascii=False) + "\n")
234
+ write_json_stdout(output)
227
235
 
228
236
 
229
237
  if __name__ == "__main__":
@@ -0,0 +1,50 @@
1
+ import json
2
+ import os
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_customization.py"
11
+
12
+
13
+ class ResolveCustomizationStdoutTests(unittest.TestCase):
14
+ def test_writes_emoji_json_when_stdout_encoding_is_cp1252(self):
15
+ with tempfile.TemporaryDirectory() as temp_dir:
16
+ skill_dir = Path(temp_dir) / "emoji-agent"
17
+ skill_dir.mkdir()
18
+ (skill_dir / "customize.toml").write_text(
19
+ '[agent]\nname = "Emoji Agent"\nicon = "🧭"\n',
20
+ encoding="utf-8",
21
+ )
22
+
23
+ env = os.environ.copy()
24
+ env["PYTHONIOENCODING"] = "cp1252"
25
+ result = subprocess.run(
26
+ [
27
+ sys.executable,
28
+ str(SCRIPT),
29
+ "--skill",
30
+ str(skill_dir),
31
+ "--key",
32
+ "agent",
33
+ ],
34
+ stdout=subprocess.PIPE,
35
+ stderr=subprocess.PIPE,
36
+ env=env,
37
+ check=False,
38
+ )
39
+
40
+ stderr = result.stderr.decode("utf-8", errors="replace")
41
+ self.assertEqual(result.returncode, 0, msg=stderr)
42
+
43
+ output = result.stdout.decode("utf-8")
44
+ self.assertIn("🧭", output)
45
+ resolved = json.loads(output)
46
+ self.assertEqual(resolved["agent"]["icon"], "🧭")
47
+
48
+
49
+ if __name__ == "__main__":
50
+ 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();
@@ -19,6 +19,10 @@ function quoteCustomRef(ref) {
19
19
  class CustomModuleManager {
20
20
  /** @type {Map<string, Object>} Shared across all instances: module code -> ResolvedModule */
21
21
  static _resolutionCache = new Map();
22
+ /** @type {Set<string>} Repo roots refreshed in the current process (dedupe quick-update fetches). */
23
+ static _refreshedRepoPaths = new Set();
24
+ /** @type {Map<string, Promise<void>>} In-flight refresh operations keyed by repo path. */
25
+ static _refreshInFlight = new Map();
22
26
 
23
27
  // ─── Source Parsing ───────────────────────────────────────────────────────
24
28
 
@@ -111,7 +115,7 @@ class CustomModuleManager {
111
115
  }
112
116
 
113
117
  // SSH URL: git@host:owner/repo.git
114
- const sshMatch = trimmed.match(/^git@([^:]+):([^/]+)\/([^/.]+?)(?:\.git)?$/);
118
+ const sshMatch = trimmed.match(/^git@([^:]+):(.+?)\/([^/.]+?)(?:\.git)?$/);
115
119
  if (sshMatch) {
116
120
  const [, host, owner, repo] = sshMatch;
117
121
  return {
@@ -424,15 +428,39 @@ class CustomModuleManager {
424
428
  stdio: ['ignore', 'pipe', 'pipe'],
425
429
  });
426
430
  } else {
427
- execSync('git reset --hard origin/HEAD', {
431
+ // Resolve the default branch (origin/HEAD) and fetch it explicitly.
432
+ // With shallow clones, `origin/HEAD` is stale and `git reset --hard
433
+ // origin/HEAD` never picks up new commits on the default branch.
434
+ let defaultBranch = 'main';
435
+ try {
436
+ defaultBranch = execSync('git symbolic-ref refs/remotes/origin/HEAD --short', {
437
+ cwd: repoCacheDir,
438
+ stdio: 'pipe',
439
+ })
440
+ .toString()
441
+ .trim()
442
+ .replace('origin/', '');
443
+ } catch {
444
+ // Fallback if origin/HEAD is not set
445
+ }
446
+ execSync(`git fetch --depth 1 origin ${quoteCustomRef(defaultBranch)}`, {
447
+ cwd: repoCacheDir,
448
+ stdio: ['ignore', 'pipe', 'pipe'],
449
+ env: { ...process.env, GIT_TERMINAL_PROMPT: '0' },
450
+ });
451
+ execSync(`git reset --hard origin/${quoteCustomRef(defaultBranch)}`, {
428
452
  cwd: repoCacheDir,
429
453
  stdio: ['ignore', 'pipe', 'pipe'],
430
454
  });
431
455
  }
432
456
  fetchSpinner.stop(`Updated ${displayName}`);
433
457
  } catch {
434
- fetchSpinner.error(`Update failed, re-downloading ${displayName}`);
435
- await fs.remove(repoCacheDir);
458
+ // Fetch failed against an existing cache — most often the remote is
459
+ // unreachable (network down, repo deleted/moved, auth revoked).
460
+ // Preserve the previous clone so re-deploy still works from cached
461
+ // content; surface a warning so the user knows the cache is stale.
462
+ fetchSpinner.error(`Could not refresh ${displayName} — keeping cached copy`);
463
+ await prompts.log.warn(`Custom module ${displayName} was not refreshed (remote unreachable). Using cached copy.`);
436
464
  }
437
465
  }
438
466
 
@@ -466,6 +494,32 @@ class CustomModuleManager {
466
494
  } catch {
467
495
  // swallow — a non-git repo (local path) wouldn't reach here anyway
468
496
  }
497
+ // Best-effort: capture the remote default branch name so channel marker
498
+ // metadata for "next" reflects the actual tracked ref (not always "main").
499
+ let defaultRef = 'main';
500
+ if (!effectiveVersion) {
501
+ try {
502
+ const symbolic = execSync('git symbolic-ref --short refs/remotes/origin/HEAD', {
503
+ cwd: repoCacheDir,
504
+ stdio: 'pipe',
505
+ })
506
+ .toString()
507
+ .trim();
508
+ if (symbolic.startsWith('origin/')) {
509
+ defaultRef = symbolic.slice('origin/'.length) || defaultRef;
510
+ }
511
+ } catch {
512
+ // Fallback to previous marker value when symbolic ref is unavailable.
513
+ try {
514
+ const existingMarker = await fs.readJson(path.join(repoCacheDir, '.bmad-channel.json'));
515
+ if (existingMarker?.channel === 'next' && typeof existingMarker.version === 'string' && existingMarker.version.trim()) {
516
+ defaultRef = existingMarker.version.trim();
517
+ }
518
+ } catch {
519
+ // Keep default fallback.
520
+ }
521
+ }
522
+ }
469
523
 
470
524
  // Write source metadata for later URL reconstruction
471
525
  const metadataPath = path.join(repoCacheDir, '.bmad-source.json');
@@ -478,6 +532,15 @@ class CustomModuleManager {
478
532
  sha: resolvedSha,
479
533
  clonedAt: new Date().toISOString(),
480
534
  });
535
+ // Keep a channel marker in custom cache too so update paths that rely on
536
+ // channel metadata (same as official-module cache) can treat this clone as
537
+ // refreshable. URL + no explicit ref => next, explicit ref => pinned.
538
+ await fs.writeJson(path.join(repoCacheDir, '.bmad-channel.json'), {
539
+ channel: effectiveVersion ? 'pinned' : 'next',
540
+ version: effectiveVersion || defaultRef,
541
+ sha: resolvedSha,
542
+ writtenAt: new Date().toISOString(),
543
+ });
481
544
 
482
545
  // Install dependencies if package.json exists (skip during browsing/analysis)
483
546
  const packageJsonPath = path.join(repoCacheDir, 'package.json');
@@ -642,6 +705,13 @@ class CustomModuleManager {
642
705
  const repoRoots = await this._findCacheRepoRoots(cacheDir);
643
706
 
644
707
  for (const { repoPath, metadata } of repoRoots) {
708
+ // Quick-update path: refresh URL-backed cached repos before reading
709
+ // files from them so re-deploy uses latest commits for `next` and
710
+ // the pinned ref for `pinned`.
711
+ if (options.bmadDir && metadata?.rawInput) {
712
+ await this._refreshRepoCacheOnce(repoPath, metadata);
713
+ }
714
+
645
715
  // Check marketplace.json for matching module code
646
716
  const marketplacePath = path.join(repoPath, '.claude-plugin', 'marketplace.json');
647
717
  if (!(await fs.pathExists(marketplacePath))) continue;
@@ -692,6 +762,45 @@ class CustomModuleManager {
692
762
  return this._findLocalSourceFromManifest(moduleCode, options);
693
763
  }
694
764
 
765
+ /**
766
+ * Refresh one cached repo at most once per process with in-flight dedupe.
767
+ * Prevents concurrent quick-update callers from racing the same cache path.
768
+ * @param {string} repoPath - Absolute cache repo path
769
+ * @param {Object} metadata - Parsed .bmad-source.json metadata
770
+ */
771
+ async _refreshRepoCacheOnce(repoPath, metadata) {
772
+ if (CustomModuleManager._refreshedRepoPaths.has(repoPath)) return;
773
+
774
+ const existing = CustomModuleManager._refreshInFlight.get(repoPath);
775
+ if (existing) {
776
+ await existing;
777
+ return;
778
+ }
779
+
780
+ const refreshPromise = (async () => {
781
+ try {
782
+ await this.cloneRepo(metadata.rawInput, {
783
+ silent: true,
784
+ pinOverride: metadata.version || undefined,
785
+ });
786
+ CustomModuleManager._refreshedRepoPaths.add(repoPath);
787
+ } catch (error_) {
788
+ // cloneRepo only throws here for unrecoverable cases (no cache present
789
+ // and a fresh clone failed, or an unexpected internal error). The
790
+ // common "remote unreachable but cache exists" case is handled inside
791
+ // cloneRepo, which preserves the clone and returns normally. Reaching
792
+ // this catch means we have no usable cache — surface a warning so the
793
+ // failure isn't silent.
794
+ await prompts.log.warn(`Refresh of cached custom module at ${path.basename(repoPath)} failed: ${error_?.message || error_}`);
795
+ } finally {
796
+ CustomModuleManager._refreshInFlight.delete(repoPath);
797
+ }
798
+ })();
799
+
800
+ CustomModuleManager._refreshInFlight.set(repoPath, refreshPromise);
801
+ await refreshPromise;
802
+ }
803
+
695
804
  /**
696
805
  * Check the installation manifest for a localPath entry for this module.
697
806
  * Used as fallback when the module was installed from a local source (no cache entry).