bmad-method 6.3.1-next.2 → 6.3.1-next.21

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 (160) hide show
  1. package/package.json +3 -3
  2. package/src/bmm-skills/1-analysis/bmad-agent-analyst/SKILL.md +51 -36
  3. package/src/bmm-skills/1-analysis/bmad-agent-analyst/customize.toml +90 -0
  4. package/src/bmm-skills/1-analysis/bmad-agent-tech-writer/SKILL.md +50 -33
  5. package/src/bmm-skills/1-analysis/bmad-agent-tech-writer/customize.toml +81 -0
  6. package/src/bmm-skills/1-analysis/bmad-document-project/SKILL.md +57 -1
  7. package/src/bmm-skills/1-analysis/bmad-document-project/customize.toml +41 -0
  8. package/src/bmm-skills/1-analysis/bmad-document-project/workflows/deep-dive-instructions.md +1 -0
  9. package/src/bmm-skills/1-analysis/bmad-document-project/workflows/full-scan-instructions.md +1 -0
  10. package/src/bmm-skills/1-analysis/bmad-prfaq/SKILL.md +48 -9
  11. package/src/bmm-skills/1-analysis/bmad-prfaq/customize.toml +41 -0
  12. package/src/bmm-skills/1-analysis/bmad-prfaq/references/verdict.md +4 -0
  13. package/src/bmm-skills/1-analysis/bmad-product-brief/SKILL.md +44 -9
  14. package/src/bmm-skills/1-analysis/bmad-product-brief/customize.toml +47 -0
  15. package/src/bmm-skills/1-analysis/bmad-product-brief/prompts/contextual-discovery.md +8 -7
  16. package/src/bmm-skills/1-analysis/bmad-product-brief/prompts/draft-and-review.md +6 -5
  17. package/src/bmm-skills/1-analysis/bmad-product-brief/prompts/finalize.md +4 -1
  18. package/src/bmm-skills/1-analysis/bmad-product-brief/prompts/guided-elicitation.md +3 -2
  19. package/src/bmm-skills/1-analysis/research/bmad-domain-research/SKILL.md +91 -1
  20. package/src/bmm-skills/1-analysis/research/bmad-domain-research/customize.toml +41 -0
  21. package/src/bmm-skills/1-analysis/research/bmad-domain-research/domain-steps/step-06-research-synthesis.md +6 -0
  22. package/src/bmm-skills/1-analysis/research/bmad-market-research/SKILL.md +91 -1
  23. package/src/bmm-skills/1-analysis/research/bmad-market-research/customize.toml +41 -0
  24. package/src/bmm-skills/1-analysis/research/bmad-market-research/steps/step-06-research-completion.md +6 -0
  25. package/src/bmm-skills/1-analysis/research/bmad-technical-research/SKILL.md +91 -1
  26. package/src/bmm-skills/1-analysis/research/bmad-technical-research/customize.toml +41 -0
  27. package/src/bmm-skills/1-analysis/research/bmad-technical-research/technical-steps/step-06-research-synthesis.md +6 -0
  28. package/src/bmm-skills/2-plan-workflows/bmad-agent-pm/SKILL.md +50 -35
  29. package/src/bmm-skills/2-plan-workflows/bmad-agent-pm/customize.toml +85 -0
  30. package/src/bmm-skills/2-plan-workflows/bmad-agent-ux-designer/SKILL.md +50 -31
  31. package/src/bmm-skills/2-plan-workflows/bmad-agent-ux-designer/customize.toml +60 -0
  32. package/src/bmm-skills/2-plan-workflows/bmad-create-prd/SKILL.md +99 -1
  33. package/src/bmm-skills/2-plan-workflows/bmad-create-prd/customize.toml +41 -0
  34. package/src/bmm-skills/2-plan-workflows/bmad-create-prd/steps-c/step-08-scoping.md +70 -23
  35. package/src/bmm-skills/2-plan-workflows/bmad-create-prd/steps-c/step-11-polish.md +1 -1
  36. package/src/bmm-skills/2-plan-workflows/bmad-create-prd/steps-c/step-12-complete.md +6 -0
  37. package/src/bmm-skills/2-plan-workflows/bmad-create-ux-design/SKILL.md +70 -1
  38. package/src/bmm-skills/2-plan-workflows/bmad-create-ux-design/customize.toml +41 -0
  39. package/src/bmm-skills/2-plan-workflows/bmad-create-ux-design/steps/step-14-complete.md +6 -0
  40. package/src/bmm-skills/2-plan-workflows/bmad-edit-prd/SKILL.md +97 -1
  41. package/src/bmm-skills/2-plan-workflows/bmad-edit-prd/customize.toml +42 -0
  42. package/src/bmm-skills/2-plan-workflows/bmad-edit-prd/steps-e/step-e-04-complete.md +2 -0
  43. package/src/bmm-skills/2-plan-workflows/bmad-validate-prd/SKILL.md +99 -1
  44. package/src/bmm-skills/2-plan-workflows/bmad-validate-prd/customize.toml +42 -0
  45. package/src/bmm-skills/2-plan-workflows/bmad-validate-prd/steps-v/step-v-13-report-complete.md +1 -0
  46. package/src/bmm-skills/3-solutioning/bmad-agent-architect/SKILL.md +50 -30
  47. package/src/bmm-skills/3-solutioning/bmad-agent-architect/customize.toml +65 -0
  48. package/src/bmm-skills/3-solutioning/bmad-check-implementation-readiness/SKILL.md +86 -1
  49. package/src/bmm-skills/3-solutioning/bmad-check-implementation-readiness/customize.toml +41 -0
  50. package/src/bmm-skills/3-solutioning/bmad-check-implementation-readiness/steps/step-06-final-assessment.md +6 -0
  51. package/src/bmm-skills/3-solutioning/bmad-create-architecture/SKILL.md +69 -1
  52. package/src/bmm-skills/3-solutioning/bmad-create-architecture/customize.toml +41 -0
  53. package/src/bmm-skills/3-solutioning/bmad-create-architecture/steps/step-08-complete.md +6 -0
  54. package/src/bmm-skills/3-solutioning/bmad-create-epics-and-stories/SKILL.md +88 -1
  55. package/src/bmm-skills/3-solutioning/bmad-create-epics-and-stories/customize.toml +41 -0
  56. package/src/bmm-skills/3-solutioning/bmad-create-epics-and-stories/steps/step-04-final-validation.md +6 -0
  57. package/src/bmm-skills/3-solutioning/bmad-generate-project-context/SKILL.md +76 -1
  58. package/src/bmm-skills/3-solutioning/bmad-generate-project-context/customize.toml +41 -0
  59. package/src/bmm-skills/3-solutioning/bmad-generate-project-context/steps/step-03-complete.md +6 -0
  60. package/src/bmm-skills/4-implementation/bmad-agent-dev/SKILL.md +48 -43
  61. package/src/bmm-skills/4-implementation/bmad-agent-dev/customize.toml +90 -0
  62. package/src/bmm-skills/4-implementation/bmad-correct-course/SKILL.md +296 -1
  63. package/src/bmm-skills/4-implementation/bmad-correct-course/customize.toml +41 -0
  64. package/src/bmm-skills/4-implementation/bmad-create-story/SKILL.md +412 -1
  65. package/src/bmm-skills/4-implementation/bmad-create-story/customize.toml +41 -0
  66. package/src/bmm-skills/4-implementation/bmad-qa-generate-e2e-tests/SKILL.md +171 -1
  67. package/src/bmm-skills/4-implementation/bmad-qa-generate-e2e-tests/customize.toml +41 -0
  68. package/src/bmm-skills/4-implementation/bmad-retrospective/SKILL.md +1507 -1
  69. package/src/bmm-skills/4-implementation/bmad-retrospective/customize.toml +41 -0
  70. package/src/bmm-skills/module.yaml +49 -0
  71. package/src/core-skills/bmad-advanced-elicitation/SKILL.md +7 -1
  72. package/src/core-skills/bmad-customize/SKILL.md +111 -0
  73. package/src/core-skills/bmad-customize/scripts/list_customizable_skills.py +231 -0
  74. package/src/core-skills/bmad-customize/scripts/tests/test_list_customizable_skills.py +249 -0
  75. package/src/core-skills/bmad-distillator/resources/distillate-format-reference.md +1 -1
  76. package/src/core-skills/bmad-party-mode/SKILL.md +13 -10
  77. package/src/core-skills/module-help.csv +1 -0
  78. package/src/core-skills/module.yaml +3 -0
  79. package/src/scripts/resolve_config.py +176 -0
  80. package/src/scripts/resolve_customization.py +230 -0
  81. package/tools/installer/cli-utils.js +0 -137
  82. package/tools/installer/commands/install.js +13 -0
  83. package/tools/installer/commands/status.js +1 -1
  84. package/tools/installer/commands/uninstall.js +1 -1
  85. package/tools/installer/core/config.js +4 -1
  86. package/tools/installer/core/existing-install.js +1 -1
  87. package/tools/installer/core/install-paths.js +12 -6
  88. package/tools/installer/core/installer.js +182 -95
  89. package/tools/installer/core/manifest-generator.js +347 -190
  90. package/tools/installer/core/manifest.js +49 -642
  91. package/tools/installer/file-ops.js +1 -1
  92. package/tools/installer/fs-native.js +116 -0
  93. package/tools/installer/ide/_config-driven.js +1 -1
  94. package/tools/installer/ide/platform-codes.js +1 -1
  95. package/tools/installer/ide/shared/path-utils.js +0 -145
  96. package/tools/installer/ide/shared/skill-manifest.js +1 -1
  97. package/tools/installer/message-loader.js +1 -1
  98. package/tools/installer/modules/channel-plan.js +203 -0
  99. package/tools/installer/modules/channel-resolver.js +241 -0
  100. package/tools/installer/modules/community-manager.js +131 -24
  101. package/tools/installer/modules/custom-module-manager.js +161 -47
  102. package/tools/installer/modules/external-manager.js +236 -73
  103. package/tools/installer/modules/official-modules.js +61 -63
  104. package/tools/installer/modules/plugin-resolver.js +1 -1
  105. package/tools/installer/modules/registry-client.js +133 -12
  106. package/tools/installer/modules/registry-fallback.yaml +8 -0
  107. package/tools/installer/modules/version-resolver.js +336 -0
  108. package/tools/installer/project-root.js +55 -1
  109. package/tools/installer/prompts.js +0 -106
  110. package/tools/installer/ui.js +457 -51
  111. package/tools/migrate-custom-module-paths.js +1 -1
  112. package/src/bmm-skills/1-analysis/bmad-agent-analyst/bmad-skill-manifest.yaml +0 -11
  113. package/src/bmm-skills/1-analysis/bmad-agent-tech-writer/bmad-skill-manifest.yaml +0 -11
  114. package/src/bmm-skills/1-analysis/bmad-document-project/workflow.md +0 -25
  115. package/src/bmm-skills/1-analysis/research/bmad-domain-research/workflow.md +0 -51
  116. package/src/bmm-skills/1-analysis/research/bmad-market-research/workflow.md +0 -51
  117. package/src/bmm-skills/1-analysis/research/bmad-technical-research/workflow.md +0 -52
  118. package/src/bmm-skills/2-plan-workflows/bmad-agent-pm/bmad-skill-manifest.yaml +0 -11
  119. package/src/bmm-skills/2-plan-workflows/bmad-agent-ux-designer/bmad-skill-manifest.yaml +0 -11
  120. package/src/bmm-skills/2-plan-workflows/bmad-create-prd/workflow.md +0 -61
  121. package/src/bmm-skills/2-plan-workflows/bmad-create-ux-design/workflow.md +0 -35
  122. package/src/bmm-skills/2-plan-workflows/bmad-edit-prd/workflow.md +0 -62
  123. package/src/bmm-skills/2-plan-workflows/bmad-validate-prd/workflow.md +0 -61
  124. package/src/bmm-skills/3-solutioning/bmad-agent-architect/bmad-skill-manifest.yaml +0 -11
  125. package/src/bmm-skills/3-solutioning/bmad-check-implementation-readiness/workflow.md +0 -47
  126. package/src/bmm-skills/3-solutioning/bmad-create-architecture/workflow.md +0 -32
  127. package/src/bmm-skills/3-solutioning/bmad-create-epics-and-stories/workflow.md +0 -51
  128. package/src/bmm-skills/3-solutioning/bmad-generate-project-context/workflow.md +0 -39
  129. package/src/bmm-skills/4-implementation/bmad-agent-dev/bmad-skill-manifest.yaml +0 -11
  130. package/src/bmm-skills/4-implementation/bmad-correct-course/workflow.md +0 -267
  131. package/src/bmm-skills/4-implementation/bmad-create-story/workflow.md +0 -380
  132. package/src/bmm-skills/4-implementation/bmad-qa-generate-e2e-tests/workflow.md +0 -136
  133. package/src/bmm-skills/4-implementation/bmad-retrospective/workflow.md +0 -1479
  134. package/tools/installer/ide/shared/agent-command-generator.js +0 -180
  135. package/tools/installer/ide/shared/bmad-artifacts.js +0 -208
  136. package/tools/installer/ide/shared/module-injections.js +0 -136
  137. package/tools/installer/ide/templates/agent-command-template.md +0 -14
  138. package/tools/installer/ide/templates/combined/antigravity.md +0 -8
  139. package/tools/installer/ide/templates/combined/default-agent.md +0 -15
  140. package/tools/installer/ide/templates/combined/default-task.md +0 -10
  141. package/tools/installer/ide/templates/combined/default-tool.md +0 -10
  142. package/tools/installer/ide/templates/combined/default-workflow.md +0 -6
  143. package/tools/installer/ide/templates/combined/gemini-agent.toml +0 -14
  144. package/tools/installer/ide/templates/combined/gemini-task.toml +0 -11
  145. package/tools/installer/ide/templates/combined/gemini-tool.toml +0 -11
  146. package/tools/installer/ide/templates/combined/gemini-workflow-yaml.toml +0 -16
  147. package/tools/installer/ide/templates/combined/gemini-workflow.toml +0 -14
  148. package/tools/installer/ide/templates/combined/kiro-agent.md +0 -16
  149. package/tools/installer/ide/templates/combined/kiro-task.md +0 -9
  150. package/tools/installer/ide/templates/combined/kiro-tool.md +0 -9
  151. package/tools/installer/ide/templates/combined/kiro-workflow.md +0 -7
  152. package/tools/installer/ide/templates/combined/opencode-agent.md +0 -15
  153. package/tools/installer/ide/templates/combined/opencode-task.md +0 -13
  154. package/tools/installer/ide/templates/combined/opencode-tool.md +0 -13
  155. package/tools/installer/ide/templates/combined/opencode-workflow-yaml.md +0 -16
  156. package/tools/installer/ide/templates/combined/opencode-workflow.md +0 -16
  157. package/tools/installer/ide/templates/combined/rovodev.md +0 -9
  158. package/tools/installer/ide/templates/combined/trae.md +0 -9
  159. package/tools/installer/ide/templates/combined/windsurf-workflow.md +0 -10
  160. package/tools/installer/ide/templates/split/.gitkeep +0 -0
@@ -0,0 +1,230 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Resolve customization for a BMad skill using three-layer TOML merge.
4
+
5
+ Reads customization from three layers (highest priority first):
6
+ 1. {project-root}/_bmad/custom/{name}.user.toml (personal, gitignored)
7
+ 2. {project-root}/_bmad/custom/{name}.toml (team/org, committed)
8
+ 3. {skill-root}/customize.toml (skill defaults)
9
+
10
+ Skill name is derived from the basename of the skill directory.
11
+
12
+ Outputs merged JSON to stdout. Errors go to stderr.
13
+
14
+ Requires Python 3.11+ (uses stdlib `tomllib`). No `uv`, no `pip install`,
15
+ no virtualenv — plain `python3` is sufficient.
16
+
17
+ python3 resolve_customization.py --skill /abs/path/to/skill-dir
18
+ python3 resolve_customization.py --skill ... --key agent
19
+ python3 resolve_customization.py --skill ... --key agent.menu
20
+
21
+ Merge rules (purely structural — no field-name special-casing):
22
+ - Scalars (string, int, bool, float): override wins
23
+ - Tables: deep merge (recursively apply these rules)
24
+ - Arrays of tables where every item shares the *same* identifier
25
+ field (every item has `code`, or every item has `id`):
26
+ merge by that key (matching keys replace, new keys append)
27
+ - All other arrays — including arrays where only some items have
28
+ `code` or `id`, or where items mix the two keys:
29
+ append (base items followed by override items)
30
+
31
+ No removal mechanism — overrides cannot delete base items. To suppress
32
+ a default, fork the skill or override the item by code with a no-op
33
+ description/prompt.
34
+ """
35
+
36
+ import argparse
37
+ import json
38
+ import sys
39
+ from pathlib import Path
40
+
41
+ try:
42
+ import tomllib
43
+ except ImportError:
44
+ sys.stderr.write(
45
+ "error: Python 3.11+ is required (stdlib `tomllib` not found).\n"
46
+ "Install a newer Python or run the resolution manually per the\n"
47
+ "fallback instructions in the skill's SKILL.md.\n"
48
+ )
49
+ sys.exit(3)
50
+
51
+
52
+ _MISSING = object()
53
+ _KEYED_MERGE_FIELDS = ("code", "id")
54
+
55
+
56
+ def find_project_root(start: Path):
57
+ current = start.resolve()
58
+ while True:
59
+ if (current / "_bmad").exists() or (current / ".git").exists():
60
+ return current
61
+ parent = current.parent
62
+ if parent == current:
63
+ return None
64
+ current = parent
65
+
66
+
67
+ def load_toml(file_path: Path, required: bool = False) -> dict:
68
+ if not file_path.exists():
69
+ if required:
70
+ sys.stderr.write(f"error: required customization file not found: {file_path}\n")
71
+ sys.exit(1)
72
+ return {}
73
+ try:
74
+ with file_path.open("rb") as f:
75
+ parsed = tomllib.load(f)
76
+ if not isinstance(parsed, dict):
77
+ if required:
78
+ sys.stderr.write(f"error: {file_path} did not parse to a table\n")
79
+ sys.exit(1)
80
+ return {}
81
+ return parsed
82
+ except tomllib.TOMLDecodeError as error:
83
+ level = "error" if required else "warning"
84
+ sys.stderr.write(f"{level}: failed to parse {file_path}: {error}\n")
85
+ if required:
86
+ sys.exit(1)
87
+ return {}
88
+ except OSError as error:
89
+ level = "error" if required else "warning"
90
+ sys.stderr.write(f"{level}: failed to read {file_path}: {error}\n")
91
+ if required:
92
+ sys.exit(1)
93
+ return {}
94
+
95
+
96
+ def _detect_keyed_merge_field(items):
97
+ """Return 'code' or 'id' if every table item carries that *same* field.
98
+
99
+ All items must share the same identifier (all `code`, or all `id`).
100
+ Mixed arrays — where some items use `code` and others use `id` —
101
+ return None and fall through to append semantics. This is intentional:
102
+ mixing identifier keys within one array is a schema smell, and
103
+ append-fallback is safer than guessing which key should merge.
104
+ """
105
+ if not items or not all(isinstance(item, dict) for item in items):
106
+ return None
107
+ for candidate in _KEYED_MERGE_FIELDS:
108
+ if all(item.get(candidate) is not None for item in items):
109
+ return candidate
110
+ return None
111
+
112
+
113
+ def _merge_by_key(base, override, key_name):
114
+ result = []
115
+ index_by_key = {}
116
+
117
+ for item in base:
118
+ if not isinstance(item, dict):
119
+ continue
120
+ if item.get(key_name) is not None:
121
+ index_by_key[item[key_name]] = len(result)
122
+ result.append(dict(item))
123
+
124
+ for item in override:
125
+ if not isinstance(item, dict):
126
+ result.append(item)
127
+ continue
128
+ key = item.get(key_name)
129
+ if key is not None and key in index_by_key:
130
+ result[index_by_key[key]] = dict(item)
131
+ else:
132
+ if key is not None:
133
+ index_by_key[key] = len(result)
134
+ result.append(dict(item))
135
+
136
+ return result
137
+
138
+
139
+ def _merge_arrays(base, override):
140
+ """Shape-aware array merge. Base + override combined tables may opt into
141
+ keyed merge if every item has `code` or `id`. Otherwise: append."""
142
+ base_arr = base if isinstance(base, list) else []
143
+ override_arr = override if isinstance(override, list) else []
144
+ keyed_field = _detect_keyed_merge_field(base_arr + override_arr)
145
+ if keyed_field:
146
+ return _merge_by_key(base_arr, override_arr, keyed_field)
147
+ return base_arr + override_arr
148
+
149
+
150
+ def deep_merge(base, override):
151
+ """Recursively merge override into base using structural rules.
152
+ - Table + table: deep merge
153
+ - Array + array: shape-aware (keyed merge if all items have code/id, else append)
154
+ - Anything else: override wins
155
+ """
156
+ if isinstance(base, dict) and isinstance(override, dict):
157
+ result = dict(base)
158
+ for key, over_val in override.items():
159
+ if key in result:
160
+ result[key] = deep_merge(result[key], over_val)
161
+ else:
162
+ result[key] = over_val
163
+ return result
164
+ if isinstance(base, list) and isinstance(override, list):
165
+ return _merge_arrays(base, override)
166
+ return override
167
+
168
+
169
+ def extract_key(data, dotted_key: str):
170
+ parts = dotted_key.split(".")
171
+ current = data
172
+ for part in parts:
173
+ if isinstance(current, dict) and part in current:
174
+ current = current[part]
175
+ else:
176
+ return _MISSING
177
+ return current
178
+
179
+
180
+ def main():
181
+ parser = argparse.ArgumentParser(
182
+ description="Resolve customization for a BMad skill using three-layer TOML merge.",
183
+ add_help=True,
184
+ )
185
+ parser.add_argument(
186
+ "--skill", "-s", required=True,
187
+ help="Absolute path to the skill directory (must contain customize.toml)",
188
+ )
189
+ parser.add_argument(
190
+ "--key", "-k", action="append", default=[],
191
+ help="Dotted field path to resolve (repeatable). Omit for full dump.",
192
+ )
193
+ args = parser.parse_args()
194
+
195
+ skill_dir = Path(args.skill).resolve()
196
+ skill_name = skill_dir.name
197
+ defaults_path = skill_dir / "customize.toml"
198
+
199
+ defaults = load_toml(defaults_path, required=True)
200
+
201
+ # Prefer the project that contains this skill. Only fall back to cwd if
202
+ # the skill isn't inside a recognizable project tree (unusual but possible
203
+ # for standalone skills invoked directly). Using cwd first is unsafe when
204
+ # an ancestor of cwd happens to have a stray _bmad/ from another project.
205
+ project_root = find_project_root(skill_dir) or find_project_root(Path.cwd())
206
+
207
+ team = {}
208
+ user = {}
209
+ if project_root:
210
+ custom_dir = project_root / "_bmad" / "custom"
211
+ team = load_toml(custom_dir / f"{skill_name}.toml")
212
+ user = load_toml(custom_dir / f"{skill_name}.user.toml")
213
+
214
+ merged = deep_merge(defaults, team)
215
+ merged = deep_merge(merged, user)
216
+
217
+ if args.key:
218
+ output = {}
219
+ for key in args.key:
220
+ value = extract_key(merged, key)
221
+ if value is not _MISSING:
222
+ output[key] = value
223
+ else:
224
+ output = merged
225
+
226
+ sys.stdout.write(json.dumps(output, indent=2, ensure_ascii=False) + "\n")
227
+
228
+
229
+ if __name__ == "__main__":
230
+ main()
@@ -1,20 +1,6 @@
1
- const path = require('node:path');
2
- const os = require('node:os');
3
1
  const prompts = require('./prompts');
4
2
 
5
3
  const CLIUtils = {
6
- /**
7
- * Get version from package.json
8
- */
9
- getVersion() {
10
- try {
11
- const packageJson = require(path.join(__dirname, '..', '..', 'package.json'));
12
- return packageJson.version || 'Unknown';
13
- } catch {
14
- return 'Unknown';
15
- }
16
- },
17
-
18
4
  /**
19
5
  * Display BMAD logo and version using @clack intro + box
20
6
  */
@@ -52,37 +38,6 @@ const CLIUtils = {
52
38
  });
53
39
  },
54
40
 
55
- /**
56
- * Display section header
57
- * @param {string} title - Section title
58
- * @param {string} subtitle - Optional subtitle
59
- */
60
- async displaySection(title, subtitle = null) {
61
- await prompts.note(subtitle || '', title);
62
- },
63
-
64
- /**
65
- * Display info box
66
- * @param {string|Array} content - Content to display
67
- * @param {Object} options - Box options
68
- */
69
- async displayBox(content, options = {}) {
70
- let text = content;
71
- if (Array.isArray(content)) {
72
- text = content.join('\n\n');
73
- }
74
-
75
- const color = await prompts.getColor();
76
- const borderColor = options.borderColor || 'cyan';
77
- const colorMap = { green: color.green, red: color.red, yellow: color.yellow, cyan: color.cyan, blue: color.blue };
78
- const formatBorder = colorMap[borderColor] || color.cyan;
79
-
80
- await prompts.box(text, options.title, {
81
- rounded: options.borderStyle === 'round' || options.borderStyle === undefined,
82
- formatBorder,
83
- });
84
- },
85
-
86
41
  /**
87
42
  * Display module configuration header
88
43
  * @param {string} moduleName - Module name (fallback if no custom header)
@@ -93,98 +48,6 @@ const CLIUtils = {
93
48
  const title = header || `Configuring ${moduleName.toUpperCase()} Module`;
94
49
  await prompts.note(subheader || '', title);
95
50
  },
96
-
97
- /**
98
- * Display module with no custom configuration
99
- * @param {string} moduleName - Module name (fallback if no custom header)
100
- * @param {string} header - Custom header from module.yaml
101
- * @param {string} subheader - Custom subheader from module.yaml
102
- */
103
- async displayModuleNoConfig(moduleName, header = null, subheader = null) {
104
- const title = header || `${moduleName.toUpperCase()} Module - No Custom Configuration`;
105
- await prompts.note(subheader || '', title);
106
- },
107
-
108
- /**
109
- * Display step indicator
110
- * @param {number} current - Current step
111
- * @param {number} total - Total steps
112
- * @param {string} description - Step description
113
- */
114
- async displayStep(current, total, description) {
115
- const progress = `[${current}/${total}]`;
116
- await prompts.log.step(`${progress} ${description}`);
117
- },
118
-
119
- /**
120
- * Display completion message
121
- * @param {string} message - Completion message
122
- */
123
- async displayComplete(message) {
124
- const color = await prompts.getColor();
125
- await prompts.box(`\u2728 ${message}`, 'Complete', {
126
- rounded: true,
127
- formatBorder: color.green,
128
- });
129
- },
130
-
131
- /**
132
- * Display error message
133
- * @param {string} message - Error message
134
- */
135
- async displayError(message) {
136
- const color = await prompts.getColor();
137
- await prompts.box(`\u2717 ${message}`, 'Error', {
138
- rounded: true,
139
- formatBorder: color.red,
140
- });
141
- },
142
-
143
- /**
144
- * Format list for display
145
- * @param {Array} items - Items to display
146
- * @param {string} prefix - Item prefix
147
- */
148
- formatList(items, prefix = '\u2022') {
149
- return items.map((item) => ` ${prefix} ${item}`).join('\n');
150
- },
151
-
152
- /**
153
- * Clear previous lines
154
- * @param {number} lines - Number of lines to clear
155
- */
156
- clearLines(lines) {
157
- for (let i = 0; i < lines; i++) {
158
- process.stdout.moveCursor(0, -1);
159
- process.stdout.clearLine(1);
160
- }
161
- },
162
-
163
- /**
164
- * Display module completion message
165
- * @param {string} moduleName - Name of the completed module
166
- * @param {boolean} clearScreen - Whether to clear the screen first (deprecated, always false now)
167
- */
168
- displayModuleComplete(moduleName, clearScreen = false) {
169
- // No longer clear screen or show boxes - just a simple completion message
170
- // This is deprecated but kept for backwards compatibility
171
- },
172
-
173
- /**
174
- * Expand path with ~ expansion
175
- * @param {string} inputPath - Path to expand
176
- * @returns {string} Expanded path
177
- */
178
- expandPath(inputPath) {
179
- if (!inputPath) return inputPath;
180
-
181
- // Expand ~ to home directory
182
- if (inputPath.startsWith('~')) {
183
- return path.join(os.homedir(), inputPath.slice(1));
184
- }
185
-
186
- return inputPath;
187
- },
188
51
  };
189
52
 
190
53
  module.exports = { CLIUtils };
@@ -24,6 +24,19 @@ module.exports = {
24
24
  ['--output-folder <path>', 'Output folder path relative to project root (default: _bmad-output)'],
25
25
  ['--custom-source <sources>', 'Comma-separated Git URLs or local paths to install custom modules from'],
26
26
  ['-y, --yes', 'Accept all defaults and skip prompts where possible'],
27
+ [
28
+ '--channel <channel>',
29
+ 'Apply channel (stable|next) to all external modules being installed. --all-stable and --all-next are aliases.',
30
+ ],
31
+ ['--all-stable', 'Alias for --channel=stable. Resolves externals to the highest stable release tag.'],
32
+ ['--all-next', 'Alias for --channel=next. Resolves externals to main HEAD.'],
33
+ ['--next <code>', 'Install module <code> from main HEAD (next channel). Repeatable.', (value, prev) => [...(prev || []), value], []],
34
+ [
35
+ '--pin <spec>',
36
+ 'Pin module to a specific tag: --pin CODE=TAG (e.g. --pin bmb=v1.7.0). Repeatable.',
37
+ (value, prev) => [...(prev || []), value],
38
+ [],
39
+ ],
27
40
  ],
28
41
  action: async (options) => {
29
42
  try {
@@ -19,7 +19,7 @@ module.exports = {
19
19
  const { bmadDir } = await installer.findBmadDir(projectDir);
20
20
 
21
21
  // Check if bmad directory exists
22
- const fs = require('fs-extra');
22
+ const fs = require('../fs-native');
23
23
  if (!(await fs.pathExists(bmadDir))) {
24
24
  await prompts.log.warn('No BMAD installation found in the current directory.');
25
25
  await prompts.log.message(`Expected location: ${bmadDir}`);
@@ -1,5 +1,5 @@
1
1
  const path = require('node:path');
2
- const fs = require('fs-extra');
2
+ const fs = require('../fs-native');
3
3
  const prompts = require('../prompts');
4
4
  const { Installer } = require('../core/installer');
5
5
 
@@ -3,7 +3,7 @@
3
3
  * User input comes from either UI answers or headless CLI flags.
4
4
  */
5
5
  class Config {
6
- constructor({ directory, modules, ides, skipPrompts, verbose, actionType, coreConfig, moduleConfigs, quickUpdate }) {
6
+ constructor({ directory, modules, ides, skipPrompts, verbose, actionType, coreConfig, moduleConfigs, quickUpdate, channelOptions }) {
7
7
  this.directory = directory;
8
8
  this.modules = Object.freeze([...modules]);
9
9
  this.ides = Object.freeze([...ides]);
@@ -13,6 +13,8 @@ class Config {
13
13
  this.coreConfig = coreConfig;
14
14
  this.moduleConfigs = moduleConfigs;
15
15
  this._quickUpdate = quickUpdate;
16
+ // channelOptions carry a Map + Set; don't deep-freeze.
17
+ this.channelOptions = channelOptions || null;
16
18
  Object.freeze(this);
17
19
  }
18
20
 
@@ -37,6 +39,7 @@ class Config {
37
39
  coreConfig: userInput.coreConfig || {},
38
40
  moduleConfigs: userInput.moduleConfigs || null,
39
41
  quickUpdate: userInput._quickUpdate || false,
42
+ channelOptions: userInput.channelOptions || null,
40
43
  });
41
44
  }
42
45
 
@@ -1,5 +1,5 @@
1
1
  const path = require('node:path');
2
- const fs = require('fs-extra');
2
+ const fs = require('../fs-native');
3
3
  const yaml = require('yaml');
4
4
  const { Manifest } = require('./manifest');
5
5
 
@@ -1,5 +1,5 @@
1
1
  const path = require('node:path');
2
- const fs = require('fs-extra');
2
+ const fs = require('../fs-native');
3
3
  const { getProjectRoot } = require('../project-root');
4
4
  const { BMAD_FOLDER_NAME } = require('../ide/shared/path-utils');
5
5
 
@@ -19,14 +19,16 @@ class InstallPaths {
19
19
  const isUpdate = await fs.pathExists(bmadDir);
20
20
 
21
21
  const configDir = path.join(bmadDir, '_config');
22
- const agentsDir = path.join(configDir, 'agents');
23
22
  const coreDir = path.join(bmadDir, 'core');
23
+ const scriptsDir = path.join(bmadDir, 'scripts');
24
+ const customDir = path.join(bmadDir, 'custom');
24
25
 
25
26
  for (const [dir, label] of [
26
27
  [bmadDir, 'bmad directory'],
27
28
  [configDir, 'config directory'],
28
- [agentsDir, 'agents config directory'],
29
29
  [coreDir, 'core module directory'],
30
+ [scriptsDir, 'shared scripts directory'],
31
+ [customDir, 'customizations directory'],
30
32
  ]) {
31
33
  await ensureWritableDir(dir, label);
32
34
  }
@@ -37,8 +39,9 @@ class InstallPaths {
37
39
  projectRoot,
38
40
  bmadDir,
39
41
  configDir,
40
- agentsDir,
41
42
  coreDir,
43
+ scriptsDir,
44
+ customDir,
42
45
  isUpdate,
43
46
  });
44
47
  }
@@ -51,8 +54,11 @@ class InstallPaths {
51
54
  manifestFile() {
52
55
  return path.join(this.configDir, 'manifest.yaml');
53
56
  }
54
- agentManifest() {
55
- return path.join(this.configDir, 'agent-manifest.csv');
57
+ centralConfig() {
58
+ return path.join(this.bmadDir, 'config.toml');
59
+ }
60
+ centralUserConfig() {
61
+ return path.join(this.bmadDir, 'config.user.toml');
56
62
  }
57
63
  filesManifest() {
58
64
  return path.join(this.configDir, 'files-manifest.csv');