bmad-method 6.2.2 → 6.2.3-next.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.
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "bmad-method",
|
|
3
|
+
"owner": {
|
|
4
|
+
"name": "Brian (BMad) Madison"
|
|
5
|
+
},
|
|
6
|
+
"description": "Breakthrough Method of Agile AI-driven Development — a full-lifecycle framework with agents and workflows for analysis, planning, architecture, and implementation.",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"homepage": "https://github.com/bmad-code-org/BMAD-METHOD",
|
|
9
|
+
"repository": "https://github.com/bmad-code-org/BMAD-METHOD",
|
|
10
|
+
"keywords": ["bmad", "agile", "ai", "orchestrator", "development", "methodology", "agents"],
|
|
11
|
+
"plugins": [
|
|
12
|
+
{
|
|
13
|
+
"name": "bmad-pro-skills",
|
|
14
|
+
"source": "./",
|
|
15
|
+
"description": "Next level skills for power users — advanced prompting techniques, agent management, and more.",
|
|
16
|
+
"version": "6.3.0",
|
|
17
|
+
"author": {
|
|
18
|
+
"name": "Brian (BMad) Madison"
|
|
19
|
+
},
|
|
20
|
+
"skills": [
|
|
21
|
+
"./src/core-skills/bmad-help",
|
|
22
|
+
"./src/core-skills/bmad-init",
|
|
23
|
+
"./src/core-skills/bmad-brainstorming",
|
|
24
|
+
"./src/core-skills/bmad-distillator",
|
|
25
|
+
"./src/core-skills/bmad-party-mode",
|
|
26
|
+
"./src/core-skills/bmad-shard-doc",
|
|
27
|
+
"./src/core-skills/bmad-advanced-elicitation",
|
|
28
|
+
"./src/core-skills/bmad-editorial-review-prose",
|
|
29
|
+
"./src/core-skills/bmad-editorial-review-structure",
|
|
30
|
+
"./src/core-skills/bmad-index-docs",
|
|
31
|
+
"./src/core-skills/bmad-review-adversarial-general",
|
|
32
|
+
"./src/core-skills/bmad-review-edge-case-hunter"
|
|
33
|
+
]
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"name": "bmad-method-lifecycle",
|
|
37
|
+
"source": "./",
|
|
38
|
+
"description": "Full-lifecycle AI development framework — agents and workflows for product analysis, planning, architecture, and implementation.",
|
|
39
|
+
"version": "6.3.0",
|
|
40
|
+
"author": {
|
|
41
|
+
"name": "Brian (BMad) Madison"
|
|
42
|
+
},
|
|
43
|
+
"skills": [
|
|
44
|
+
"./src/bmm-skills/1-analysis/bmad-product-brief",
|
|
45
|
+
"./src/bmm-skills/1-analysis/bmad-agent-analyst",
|
|
46
|
+
"./src/bmm-skills/1-analysis/bmad-agent-tech-writer",
|
|
47
|
+
"./src/bmm-skills/1-analysis/bmad-document-project",
|
|
48
|
+
"./src/bmm-skills/1-analysis/research/bmad-domain-research",
|
|
49
|
+
"./src/bmm-skills/1-analysis/research/bmad-market-research",
|
|
50
|
+
"./src/bmm-skills/1-analysis/research/bmad-technical-research",
|
|
51
|
+
"./src/bmm-skills/2-plan-workflows/bmad-agent-pm",
|
|
52
|
+
"./src/bmm-skills/2-plan-workflows/bmad-agent-ux-designer",
|
|
53
|
+
"./src/bmm-skills/2-plan-workflows/bmad-create-prd",
|
|
54
|
+
"./src/bmm-skills/2-plan-workflows/bmad-edit-prd",
|
|
55
|
+
"./src/bmm-skills/2-plan-workflows/bmad-validate-prd",
|
|
56
|
+
"./src/bmm-skills/2-plan-workflows/bmad-create-ux-design",
|
|
57
|
+
"./src/bmm-skills/3-solutioning/bmad-agent-architect",
|
|
58
|
+
"./src/bmm-skills/3-solutioning/bmad-create-architecture",
|
|
59
|
+
"./src/bmm-skills/3-solutioning/bmad-check-implementation-readiness",
|
|
60
|
+
"./src/bmm-skills/3-solutioning/bmad-create-epics-and-stories",
|
|
61
|
+
"./src/bmm-skills/3-solutioning/bmad-generate-project-context",
|
|
62
|
+
"./src/bmm-skills/4-implementation/bmad-agent-dev",
|
|
63
|
+
"./src/bmm-skills/4-implementation/bmad-agent-sm",
|
|
64
|
+
"./src/bmm-skills/4-implementation/bmad-agent-qa",
|
|
65
|
+
"./src/bmm-skills/4-implementation/bmad-agent-quick-flow-solo-dev",
|
|
66
|
+
"./src/bmm-skills/4-implementation/bmad-dev-story",
|
|
67
|
+
"./src/bmm-skills/4-implementation/bmad-quick-dev",
|
|
68
|
+
"./src/bmm-skills/4-implementation/bmad-sprint-planning",
|
|
69
|
+
"./src/bmm-skills/4-implementation/bmad-sprint-status",
|
|
70
|
+
"./src/bmm-skills/4-implementation/bmad-code-review",
|
|
71
|
+
"./src/bmm-skills/4-implementation/bmad-create-story",
|
|
72
|
+
"./src/bmm-skills/4-implementation/bmad-correct-course",
|
|
73
|
+
"./src/bmm-skills/4-implementation/bmad-retrospective",
|
|
74
|
+
"./src/bmm-skills/4-implementation/bmad-qa-generate-e2e-tests"
|
|
75
|
+
]
|
|
76
|
+
}
|
|
77
|
+
]
|
|
78
|
+
}
|
package/package.json
CHANGED
|
@@ -166,9 +166,27 @@ def resolve_project_root_placeholder(value, project_root):
|
|
|
166
166
|
"""Replace {project-root} placeholder with actual path."""
|
|
167
167
|
if not value or not isinstance(value, str):
|
|
168
168
|
return value
|
|
169
|
-
if '{project-root}' in value:
|
|
170
|
-
return value
|
|
171
|
-
|
|
169
|
+
if '{project-root}' not in value:
|
|
170
|
+
return value
|
|
171
|
+
|
|
172
|
+
# Strip the {project-root} token to inspect what remains, so we can
|
|
173
|
+
# correctly handle absolute paths stored as "{project-root}//absolute/path"
|
|
174
|
+
# (produced by the "{project-root}/{value}" template applied to an absolute value).
|
|
175
|
+
suffix = value.replace('{project-root}', '', 1)
|
|
176
|
+
|
|
177
|
+
# Strip the one path separator that follows the token (if any)
|
|
178
|
+
if suffix.startswith('/') or suffix.startswith('\\'):
|
|
179
|
+
remainder = suffix[1:]
|
|
180
|
+
else:
|
|
181
|
+
remainder = suffix
|
|
182
|
+
|
|
183
|
+
if os.path.isabs(remainder):
|
|
184
|
+
# The original value was an absolute path stored with a {project-root}/ prefix.
|
|
185
|
+
# Return the absolute path directly — no joining needed.
|
|
186
|
+
return remainder
|
|
187
|
+
|
|
188
|
+
# Relative path: join with project root and normalize to resolve any .. segments.
|
|
189
|
+
return os.path.normpath(os.path.join(str(project_root), remainder))
|
|
172
190
|
|
|
173
191
|
|
|
174
192
|
def parse_var_specs(vars_string):
|
|
@@ -222,9 +240,22 @@ def apply_result_template(var_def, raw_value, context):
|
|
|
222
240
|
if not result_template:
|
|
223
241
|
return raw_value
|
|
224
242
|
|
|
243
|
+
# If the user supplied an absolute path and the template would prefix it with
|
|
244
|
+
# "{project-root}/", skip the template entirely to avoid producing a broken path
|
|
245
|
+
# like "/my/project//absolute/path".
|
|
246
|
+
if isinstance(raw_value, str) and os.path.isabs(raw_value):
|
|
247
|
+
return raw_value
|
|
248
|
+
|
|
225
249
|
ctx = dict(context)
|
|
226
250
|
ctx['value'] = raw_value
|
|
227
|
-
|
|
251
|
+
result = expand_template(result_template, ctx)
|
|
252
|
+
|
|
253
|
+
# Normalize the resulting path to resolve any ".." segments (e.g. when the user
|
|
254
|
+
# entered a relative path such as "../../outside-dir").
|
|
255
|
+
if isinstance(result, str) and '{' not in result and os.path.isabs(result):
|
|
256
|
+
result = os.path.normpath(result)
|
|
257
|
+
|
|
258
|
+
return result
|
|
228
259
|
|
|
229
260
|
|
|
230
261
|
# =============================================================================
|
|
@@ -110,6 +110,37 @@ class TestResolveProjectRootPlaceholder(unittest.TestCase):
|
|
|
110
110
|
def test_non_string(self):
|
|
111
111
|
self.assertEqual(resolve_project_root_placeholder(42, Path('/test')), 42)
|
|
112
112
|
|
|
113
|
+
def test_absolute_path_stored_with_prefix(self):
|
|
114
|
+
"""Absolute output_folder entered by user is stored as '{project-root}//abs/path'
|
|
115
|
+
by the '{project-root}/{value}' template. It must resolve to '/abs/path', not
|
|
116
|
+
'/project//abs/path'."""
|
|
117
|
+
result = resolve_project_root_placeholder(
|
|
118
|
+
'{project-root}//Users/me/outside', Path('/Users/me/myproject')
|
|
119
|
+
)
|
|
120
|
+
self.assertEqual(result, '/Users/me/outside')
|
|
121
|
+
|
|
122
|
+
def test_relative_path_with_traversal_is_normalized(self):
|
|
123
|
+
"""A relative path like '../../sibling' produces '{project-root}/../../sibling'
|
|
124
|
+
after the template. It must resolve to the normalized absolute path, not the
|
|
125
|
+
un-normalized string '/project/../../sibling'."""
|
|
126
|
+
result = resolve_project_root_placeholder(
|
|
127
|
+
'{project-root}/../../sibling', Path('/Users/me/myproject')
|
|
128
|
+
)
|
|
129
|
+
self.assertEqual(result, '/Users/sibling')
|
|
130
|
+
|
|
131
|
+
def test_relative_path_one_level_up(self):
|
|
132
|
+
result = resolve_project_root_placeholder(
|
|
133
|
+
'{project-root}/../outside-outputs', Path('/project/root')
|
|
134
|
+
)
|
|
135
|
+
self.assertEqual(result, '/project/outside-outputs')
|
|
136
|
+
|
|
137
|
+
def test_standard_relative_path_unchanged(self):
|
|
138
|
+
"""Normal in-project relative paths continue to work correctly."""
|
|
139
|
+
result = resolve_project_root_placeholder(
|
|
140
|
+
'{project-root}/_bmad-output', Path('/project/root')
|
|
141
|
+
)
|
|
142
|
+
self.assertEqual(result, '/project/root/_bmad-output')
|
|
143
|
+
|
|
113
144
|
|
|
114
145
|
class TestExpandTemplate(unittest.TestCase):
|
|
115
146
|
|
|
@@ -147,6 +178,39 @@ class TestApplyResultTemplate(unittest.TestCase):
|
|
|
147
178
|
result = apply_result_template(var_def, 'English', {})
|
|
148
179
|
self.assertEqual(result, 'English')
|
|
149
180
|
|
|
181
|
+
def test_absolute_value_skips_project_root_template(self):
|
|
182
|
+
"""When the user enters an absolute path, the '{project-root}/{value}' template
|
|
183
|
+
must not be applied — doing so would produce '/project//absolute/path'."""
|
|
184
|
+
var_def = {'result': '{project-root}/{value}'}
|
|
185
|
+
result = apply_result_template(
|
|
186
|
+
var_def, '/Users/me/shared-outputs', {'project-root': '/Users/me/myproject'}
|
|
187
|
+
)
|
|
188
|
+
self.assertEqual(result, '/Users/me/shared-outputs')
|
|
189
|
+
|
|
190
|
+
def test_relative_traversal_value_is_normalized(self):
|
|
191
|
+
"""A relative path like '../../outside' combined with the project-root template
|
|
192
|
+
must produce a clean normalized absolute path, not '/project/../../outside'."""
|
|
193
|
+
var_def = {'result': '{project-root}/{value}'}
|
|
194
|
+
result = apply_result_template(
|
|
195
|
+
var_def, '../../outside-dir', {'project-root': '/Users/me/myproject'}
|
|
196
|
+
)
|
|
197
|
+
self.assertEqual(result, '/Users/outside-dir')
|
|
198
|
+
|
|
199
|
+
def test_relative_one_level_up_is_normalized(self):
|
|
200
|
+
var_def = {'result': '{project-root}/{value}'}
|
|
201
|
+
result = apply_result_template(
|
|
202
|
+
var_def, '../sibling-outputs', {'project-root': '/project/root'}
|
|
203
|
+
)
|
|
204
|
+
self.assertEqual(result, '/project/sibling-outputs')
|
|
205
|
+
|
|
206
|
+
def test_normal_relative_value_unchanged(self):
|
|
207
|
+
"""Standard in-project relative paths still produce the expected joined path."""
|
|
208
|
+
var_def = {'result': '{project-root}/{value}'}
|
|
209
|
+
result = apply_result_template(
|
|
210
|
+
var_def, '_bmad-output', {'project-root': '/project/root'}
|
|
211
|
+
)
|
|
212
|
+
self.assertEqual(result, '/project/root/_bmad-output')
|
|
213
|
+
|
|
150
214
|
|
|
151
215
|
class TestLoadModuleYaml(unittest.TestCase):
|
|
152
216
|
|