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
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "bmad-method",
4
- "version": "6.2.2",
4
+ "version": "6.2.3-next.0",
5
5
  "description": "Breakthrough Method of Agile AI-driven Development",
6
6
  "keywords": [
7
7
  "agile",
@@ -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.replace('{project-root}', str(project_root))
171
- return value
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
- return expand_template(result_template, ctx)
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