bmad-method 6.3.1-next.8 → 6.4.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.
- package/package.json +3 -2
- package/src/bmm-skills/1-analysis/bmad-agent-analyst/SKILL.md +51 -36
- package/src/bmm-skills/1-analysis/bmad-agent-analyst/customize.toml +90 -0
- package/src/bmm-skills/1-analysis/bmad-agent-tech-writer/SKILL.md +50 -33
- package/src/bmm-skills/1-analysis/bmad-agent-tech-writer/customize.toml +81 -0
- package/src/bmm-skills/1-analysis/bmad-document-project/SKILL.md +57 -1
- package/src/bmm-skills/1-analysis/bmad-document-project/customize.toml +41 -0
- package/src/bmm-skills/1-analysis/bmad-document-project/workflows/deep-dive-instructions.md +1 -0
- package/src/bmm-skills/1-analysis/bmad-document-project/workflows/full-scan-instructions.md +1 -0
- package/src/bmm-skills/1-analysis/bmad-prfaq/SKILL.md +48 -9
- package/src/bmm-skills/1-analysis/bmad-prfaq/customize.toml +41 -0
- package/src/bmm-skills/1-analysis/bmad-prfaq/references/verdict.md +4 -0
- package/src/bmm-skills/1-analysis/bmad-product-brief/SKILL.md +44 -9
- package/src/bmm-skills/1-analysis/bmad-product-brief/customize.toml +47 -0
- package/src/bmm-skills/1-analysis/bmad-product-brief/prompts/contextual-discovery.md +8 -7
- package/src/bmm-skills/1-analysis/bmad-product-brief/prompts/draft-and-review.md +6 -5
- package/src/bmm-skills/1-analysis/bmad-product-brief/prompts/finalize.md +4 -1
- package/src/bmm-skills/1-analysis/bmad-product-brief/prompts/guided-elicitation.md +3 -2
- package/src/bmm-skills/1-analysis/research/bmad-domain-research/SKILL.md +91 -1
- package/src/bmm-skills/1-analysis/research/bmad-domain-research/customize.toml +41 -0
- package/src/bmm-skills/1-analysis/research/bmad-domain-research/domain-steps/step-06-research-synthesis.md +6 -0
- package/src/bmm-skills/1-analysis/research/bmad-market-research/SKILL.md +91 -1
- package/src/bmm-skills/1-analysis/research/bmad-market-research/customize.toml +41 -0
- package/src/bmm-skills/1-analysis/research/bmad-market-research/steps/step-06-research-completion.md +6 -0
- package/src/bmm-skills/1-analysis/research/bmad-technical-research/SKILL.md +91 -1
- package/src/bmm-skills/1-analysis/research/bmad-technical-research/customize.toml +41 -0
- package/src/bmm-skills/1-analysis/research/bmad-technical-research/technical-steps/step-06-research-synthesis.md +6 -0
- package/src/bmm-skills/2-plan-workflows/bmad-agent-pm/SKILL.md +50 -35
- package/src/bmm-skills/2-plan-workflows/bmad-agent-pm/customize.toml +85 -0
- package/src/bmm-skills/2-plan-workflows/bmad-agent-ux-designer/SKILL.md +50 -31
- package/src/bmm-skills/2-plan-workflows/bmad-agent-ux-designer/customize.toml +60 -0
- package/src/bmm-skills/2-plan-workflows/bmad-create-prd/SKILL.md +99 -1
- package/src/bmm-skills/2-plan-workflows/bmad-create-prd/customize.toml +41 -0
- package/src/bmm-skills/2-plan-workflows/bmad-create-prd/steps-c/step-12-complete.md +6 -0
- package/src/bmm-skills/2-plan-workflows/bmad-create-ux-design/SKILL.md +70 -1
- package/src/bmm-skills/2-plan-workflows/bmad-create-ux-design/customize.toml +41 -0
- package/src/bmm-skills/2-plan-workflows/bmad-create-ux-design/steps/step-14-complete.md +6 -0
- package/src/bmm-skills/2-plan-workflows/bmad-edit-prd/SKILL.md +97 -1
- package/src/bmm-skills/2-plan-workflows/bmad-edit-prd/customize.toml +42 -0
- package/src/bmm-skills/2-plan-workflows/bmad-edit-prd/steps-e/step-e-04-complete.md +2 -0
- package/src/bmm-skills/2-plan-workflows/bmad-validate-prd/SKILL.md +99 -1
- package/src/bmm-skills/2-plan-workflows/bmad-validate-prd/customize.toml +42 -0
- package/src/bmm-skills/2-plan-workflows/bmad-validate-prd/steps-v/step-v-13-report-complete.md +1 -0
- package/src/bmm-skills/3-solutioning/bmad-agent-architect/SKILL.md +50 -30
- package/src/bmm-skills/3-solutioning/bmad-agent-architect/customize.toml +65 -0
- package/src/bmm-skills/3-solutioning/bmad-check-implementation-readiness/SKILL.md +86 -1
- package/src/bmm-skills/3-solutioning/bmad-check-implementation-readiness/customize.toml +41 -0
- package/src/bmm-skills/3-solutioning/bmad-check-implementation-readiness/steps/step-06-final-assessment.md +6 -0
- package/src/bmm-skills/3-solutioning/bmad-create-architecture/SKILL.md +69 -1
- package/src/bmm-skills/3-solutioning/bmad-create-architecture/customize.toml +41 -0
- package/src/bmm-skills/3-solutioning/bmad-create-architecture/steps/step-08-complete.md +6 -0
- package/src/bmm-skills/3-solutioning/bmad-create-epics-and-stories/SKILL.md +88 -1
- package/src/bmm-skills/3-solutioning/bmad-create-epics-and-stories/customize.toml +41 -0
- package/src/bmm-skills/3-solutioning/bmad-create-epics-and-stories/steps/step-04-final-validation.md +6 -0
- package/src/bmm-skills/3-solutioning/bmad-generate-project-context/SKILL.md +76 -1
- package/src/bmm-skills/3-solutioning/bmad-generate-project-context/customize.toml +41 -0
- package/src/bmm-skills/3-solutioning/bmad-generate-project-context/steps/step-03-complete.md +6 -0
- package/src/bmm-skills/4-implementation/bmad-agent-dev/SKILL.md +48 -43
- package/src/bmm-skills/4-implementation/bmad-agent-dev/customize.toml +90 -0
- package/src/bmm-skills/4-implementation/bmad-checkpoint-preview/SKILL.md +46 -7
- package/src/bmm-skills/4-implementation/bmad-checkpoint-preview/customize.toml +41 -0
- package/src/bmm-skills/4-implementation/bmad-checkpoint-preview/step-05-wrapup.md +6 -0
- package/src/bmm-skills/4-implementation/bmad-code-review/SKILL.md +85 -1
- package/src/bmm-skills/4-implementation/bmad-code-review/customize.toml +41 -0
- package/src/bmm-skills/4-implementation/bmad-code-review/steps/step-04-present.md +6 -0
- package/src/bmm-skills/4-implementation/bmad-correct-course/SKILL.md +296 -1
- package/src/bmm-skills/4-implementation/bmad-correct-course/customize.toml +41 -0
- package/src/bmm-skills/4-implementation/bmad-create-story/SKILL.md +424 -1
- package/src/bmm-skills/4-implementation/bmad-create-story/customize.toml +41 -0
- package/src/bmm-skills/4-implementation/bmad-dev-story/SKILL.md +480 -1
- package/src/bmm-skills/4-implementation/bmad-dev-story/customize.toml +41 -0
- package/src/bmm-skills/4-implementation/bmad-qa-generate-e2e-tests/SKILL.md +171 -1
- package/src/bmm-skills/4-implementation/bmad-qa-generate-e2e-tests/customize.toml +41 -0
- package/src/bmm-skills/4-implementation/bmad-quick-dev/SKILL.md +106 -1
- package/src/bmm-skills/4-implementation/bmad-quick-dev/customize.toml +41 -0
- package/src/bmm-skills/4-implementation/bmad-quick-dev/step-05-present.md +6 -0
- package/src/bmm-skills/4-implementation/bmad-quick-dev/step-oneshot.md +6 -0
- package/src/bmm-skills/4-implementation/bmad-retrospective/SKILL.md +1507 -1
- package/src/bmm-skills/4-implementation/bmad-retrospective/customize.toml +41 -0
- package/src/bmm-skills/4-implementation/bmad-sprint-planning/SKILL.md +294 -1
- package/src/bmm-skills/4-implementation/bmad-sprint-planning/customize.toml +41 -0
- package/src/bmm-skills/4-implementation/bmad-sprint-status/SKILL.md +292 -1
- package/src/bmm-skills/4-implementation/bmad-sprint-status/customize.toml +41 -0
- package/src/bmm-skills/module.yaml +49 -0
- package/src/core-skills/bmad-advanced-elicitation/SKILL.md +7 -1
- package/src/core-skills/bmad-customize/SKILL.md +111 -0
- package/src/core-skills/bmad-customize/scripts/list_customizable_skills.py +231 -0
- package/src/core-skills/bmad-customize/scripts/tests/test_list_customizable_skills.py +249 -0
- package/src/core-skills/bmad-distillator/resources/distillate-format-reference.md +1 -1
- package/src/core-skills/bmad-party-mode/SKILL.md +13 -10
- package/src/core-skills/module-help.csv +1 -0
- package/src/core-skills/module.yaml +2 -0
- package/src/scripts/resolve_config.py +176 -0
- package/src/scripts/resolve_customization.py +230 -0
- package/tools/installer/commands/install.js +13 -0
- package/tools/installer/core/config.js +4 -1
- package/tools/installer/core/install-paths.js +11 -5
- package/tools/installer/core/installer.js +181 -94
- package/tools/installer/core/manifest-generator.js +339 -184
- package/tools/installer/core/manifest.js +86 -86
- package/tools/installer/fs-native.js +5 -0
- package/tools/installer/ide/platform-codes.yaml +6 -0
- package/tools/installer/modules/channel-plan.js +203 -0
- package/tools/installer/modules/channel-resolver.js +241 -0
- package/tools/installer/modules/community-manager.js +130 -23
- package/tools/installer/modules/custom-module-manager.js +160 -19
- package/tools/installer/modules/external-manager.js +235 -32
- package/tools/installer/modules/official-modules.js +58 -12
- package/tools/installer/modules/registry-client.js +139 -7
- package/tools/installer/modules/registry-fallback.yaml +8 -0
- package/tools/installer/modules/version-resolver.js +336 -0
- package/tools/installer/project-root.js +54 -0
- package/tools/installer/ui.js +561 -50
- package/tools/platform-codes.yaml +6 -0
- package/src/bmm-skills/1-analysis/bmad-agent-analyst/bmad-skill-manifest.yaml +0 -11
- package/src/bmm-skills/1-analysis/bmad-agent-tech-writer/bmad-skill-manifest.yaml +0 -11
- package/src/bmm-skills/1-analysis/bmad-document-project/workflow.md +0 -25
- package/src/bmm-skills/1-analysis/research/bmad-domain-research/workflow.md +0 -51
- package/src/bmm-skills/1-analysis/research/bmad-market-research/workflow.md +0 -51
- package/src/bmm-skills/1-analysis/research/bmad-technical-research/workflow.md +0 -52
- package/src/bmm-skills/2-plan-workflows/bmad-agent-pm/bmad-skill-manifest.yaml +0 -11
- package/src/bmm-skills/2-plan-workflows/bmad-agent-ux-designer/bmad-skill-manifest.yaml +0 -11
- package/src/bmm-skills/2-plan-workflows/bmad-create-prd/workflow.md +0 -61
- package/src/bmm-skills/2-plan-workflows/bmad-create-ux-design/workflow.md +0 -35
- package/src/bmm-skills/2-plan-workflows/bmad-edit-prd/workflow.md +0 -62
- package/src/bmm-skills/2-plan-workflows/bmad-validate-prd/workflow.md +0 -61
- package/src/bmm-skills/3-solutioning/bmad-agent-architect/bmad-skill-manifest.yaml +0 -11
- package/src/bmm-skills/3-solutioning/bmad-check-implementation-readiness/workflow.md +0 -47
- package/src/bmm-skills/3-solutioning/bmad-create-architecture/workflow.md +0 -32
- package/src/bmm-skills/3-solutioning/bmad-create-epics-and-stories/workflow.md +0 -51
- package/src/bmm-skills/3-solutioning/bmad-generate-project-context/workflow.md +0 -39
- package/src/bmm-skills/4-implementation/bmad-agent-dev/bmad-skill-manifest.yaml +0 -11
- package/src/bmm-skills/4-implementation/bmad-code-review/workflow.md +0 -55
- package/src/bmm-skills/4-implementation/bmad-correct-course/workflow.md +0 -267
- package/src/bmm-skills/4-implementation/bmad-create-story/workflow.md +0 -380
- package/src/bmm-skills/4-implementation/bmad-dev-story/workflow.md +0 -450
- package/src/bmm-skills/4-implementation/bmad-qa-generate-e2e-tests/workflow.md +0 -136
- package/src/bmm-skills/4-implementation/bmad-quick-dev/workflow.md +0 -76
- package/src/bmm-skills/4-implementation/bmad-retrospective/workflow.md +0 -1479
- package/src/bmm-skills/4-implementation/bmad-sprint-planning/workflow.md +0 -263
- package/src/bmm-skills/4-implementation/bmad-sprint-status/workflow.md +0 -261
|
@@ -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()
|
|
@@ -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 {
|
|
@@ -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
|
|
|
@@ -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
|
-
|
|
55
|
-
return path.join(this.
|
|
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');
|