bmad-fh 6.0.0-alpha.23 → 6.0.0-alpha.23.3b00cb36

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,68 @@
1
+ name: Publish
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - feat/multi-artifact-support
7
+
8
+ permissions:
9
+ contents: read
10
+ packages: write
11
+
12
+ jobs:
13
+ publish:
14
+ runs-on: ubuntu-latest
15
+ steps:
16
+ - name: Checkout
17
+ uses: actions/checkout@v4
18
+ with:
19
+ fetch-depth: 0
20
+
21
+ - name: Setup Node.js
22
+ uses: actions/setup-node@v4
23
+ with:
24
+ node-version-file: ".nvmrc"
25
+ cache: npm
26
+ registry-url: https://registry.npmjs.org
27
+
28
+ - name: Install dependencies
29
+ run: npm ci
30
+
31
+ - name: Set version with commit hash
32
+ id: version
33
+ run: |
34
+ BASE_VERSION=$(node -p "require('./package.json').version")
35
+ SHORT_SHA=$(git rev-parse --short HEAD)
36
+ NEW_VERSION="${BASE_VERSION}.${SHORT_SHA}"
37
+ npm version "${NEW_VERSION}" --no-git-tag-version
38
+ echo "version=${NEW_VERSION}" >> $GITHUB_OUTPUT
39
+
40
+ - name: Publish to NPM
41
+ env:
42
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
43
+ run: |
44
+ VERSION="${{ steps.version.outputs.version }}"
45
+ echo "Checking if bmad-fh@${VERSION} already exists..."
46
+
47
+ # Check if version already exists on npm
48
+ if npm view "bmad-fh@${VERSION}" version 2>/dev/null; then
49
+ echo "Version ${VERSION} already exists on npm, skipping publish"
50
+ echo "SKIPPED=true" >> $GITHUB_ENV
51
+ else
52
+ echo "Publishing bmad-fh@${VERSION}"
53
+ npm publish --ignore-scripts
54
+ echo "SKIPPED=false" >> $GITHUB_ENV
55
+ fi
56
+
57
+ - name: Summary
58
+ run: |
59
+ if [ "$SKIPPED" = "true" ]; then
60
+ echo "## Skipped - bmad-fh@${{ steps.version.outputs.version }} already exists" >> $GITHUB_STEP_SUMMARY
61
+ else
62
+ echo "## Published bmad-fh@${{ steps.version.outputs.version }}" >> $GITHUB_STEP_SUMMARY
63
+ fi
64
+ echo "" >> $GITHUB_STEP_SUMMARY
65
+ echo "### Installation" >> $GITHUB_STEP_SUMMARY
66
+ echo "\`\`\`bash" >> $GITHUB_STEP_SUMMARY
67
+ echo "npx bmad-fh install" >> $GITHUB_STEP_SUMMARY
68
+ echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
package/.husky/pre-commit CHANGED
@@ -1,5 +1,20 @@
1
1
  #!/usr/bin/env sh
2
2
 
3
+ # =============================================================================
4
+ # Call .githooks/pre-commit first (if exists)
5
+ # =============================================================================
6
+ if [ -x ".githooks/pre-commit" ]; then
7
+ .githooks/pre-commit "$@"
8
+ GITHOOKS_EXIT=$?
9
+ if [ $GITHOOKS_EXIT -ne 0 ]; then
10
+ exit $GITHOOKS_EXIT
11
+ fi
12
+ fi
13
+
14
+ # =============================================================================
15
+ # Husky-specific: lint-staged and tests
16
+ # =============================================================================
17
+
3
18
  # Auto-fix changed files and stage them
4
19
  npx --no-install lint-staged
5
20
 
@@ -10,11 +25,11 @@ npm test
10
25
  if command -v rg >/dev/null 2>&1; then
11
26
  if git diff --cached --name-only | rg -q '^docs/'; then
12
27
  npm run docs:validate-links
13
- npm run docs:build
28
+ npm run docs:build
14
29
  fi
15
30
  else
16
31
  if git diff --cached --name-only | grep -Eq '^docs/'; then
17
32
  npm run docs:validate-links
18
- npm run docs:build
33
+ npm run docs:build
19
34
  fi
20
35
  fi
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env sh
2
+
3
+ # Delegate to .githooks/pre-push for comprehensive checks
4
+ # (upstream sync, rebase check, single-commit enforcement)
5
+
6
+ if [ -x ".githooks/pre-push" ]; then
7
+ .githooks/pre-push "$@"
8
+ else
9
+ echo "Warning: .githooks/pre-push not found, skipping custom checks"
10
+ fi
package/README.md CHANGED
@@ -23,7 +23,7 @@ Traditional AI tools do the thinking for you, producing average results. BMad ag
23
23
  **Prerequisites**: [Node.js](https://nodejs.org) v20+
24
24
 
25
25
  ```bash
26
- npx bmad-fh@multi-artifact install
26
+ npx bmad-fh install
27
27
  ```
28
28
 
29
29
  Follow the installer prompts to configure your project. Then run:
@@ -66,31 +66,134 @@ BMad supports running multiple workflows in parallel across different terminal s
66
66
  - **Multi-team projects** — Each team works in their own scope
67
67
  - **Parallel feature development** — Develop auth, payments, and catalog simultaneously
68
68
  - **Microservices** — One scope per service with shared contracts
69
+ - **Experimentation** — Create isolated scopes for spikes and prototypes
70
+
71
+ ### Quick Start
69
72
 
70
73
  ```bash
71
74
  # Initialize scope system
72
- bmad scope init
75
+ npx bmad-fh scope init
73
76
 
74
77
  # Create scopes for different services
75
- bmad scope create auth --name "Authentication"
76
- bmad scope create payments --name "Payments"
78
+ npx bmad-fh scope create auth --name "Authentication Service"
79
+ npx bmad-fh scope create payments --name "Payment Processing" --deps auth
80
+
81
+ # Set the active scope for your session
82
+ npx bmad-fh scope set auth
77
83
 
78
84
  # Run workflows in parallel (different terminals)
79
- bmad workflow create-prd --scope auth # Terminal 1
80
- bmad workflow create-prd --scope payments # Terminal 2
85
+ # Terminal 1: Set scope to auth, run agent workflows
86
+ # Terminal 2: Set scope to payments, run agent workflows
81
87
 
82
88
  # Share artifacts between scopes
83
- bmad scope sync-up auth # Promote to shared layer
84
- bmad scope sync-down payments # Pull shared updates
89
+ npx bmad-fh scope sync-up auth # Promote to shared layer
90
+ npx bmad-fh scope sync-down payments # Pull shared updates
91
+ ```
92
+
93
+ ### CLI Reference
94
+
95
+ | Command | Description |
96
+ | ---------------------------------- | ------------------------------------------- |
97
+ | `npx bmad-fh scope init` | Initialize the scope system in your project |
98
+ | `npx bmad-fh scope list` | List all scopes (alias: `ls`) |
99
+ | `npx bmad-fh scope create <id>` | Create a new scope (alias: `new`) |
100
+ | `npx bmad-fh scope info <id>` | Show scope details (alias: `show`) |
101
+ | `npx bmad-fh scope set [id]` | Set active scope for session (alias: `use`) |
102
+ | `npx bmad-fh scope unset` | Clear active scope (alias: `clear`) |
103
+ | `npx bmad-fh scope remove <id>` | Remove a scope (aliases: `rm`, `delete`) |
104
+ | `npx bmad-fh scope archive <id>` | Archive a completed scope |
105
+ | `npx bmad-fh scope activate <id>` | Reactivate an archived scope |
106
+ | `npx bmad-fh scope sync-up <id>` | Promote artifacts to shared layer |
107
+ | `npx bmad-fh scope sync-down <id>` | Pull shared updates into scope |
108
+ | `npx bmad-fh scope help [cmd]` | Show help (add command for detailed help) |
109
+
110
+ ### Create Options
111
+
112
+ ```bash
113
+ npx bmad-fh scope create auth \
114
+ --name "Authentication Service" \
115
+ --description "User auth, SSO, and session management" \
116
+ --deps users,notifications \
117
+ --context # Create scope-specific project-context.md
118
+ ```
119
+
120
+ ### Directory Structure
121
+
122
+ After initialization and scope creation:
123
+
124
+ ```
125
+ project-root/
126
+ ├── _bmad/
127
+ │ ├── _config/
128
+ │ │ └── scopes.yaml # Scope registry and settings
129
+ │ └── _events/
130
+ │ ├── event-log.yaml # Event history
131
+ │ └── subscriptions.yaml # Cross-scope subscriptions
132
+
133
+ ├── _bmad-output/
134
+ │ ├── _shared/ # Shared knowledge layer
135
+ │ │ ├── project-context.md # Global project context
136
+ │ │ ├── contracts/ # Integration contracts
137
+ │ │ └── principles/ # Architecture principles
138
+ │ │
139
+ │ ├── auth/ # Auth scope artifacts
140
+ │ │ ├── planning-artifacts/
141
+ │ │ ├── implementation-artifacts/
142
+ │ │ └── tests/
143
+ │ │
144
+ │ └── payments/ # Payments scope artifacts
145
+ │ └── ...
146
+
147
+ └── .bmad-scope # Session-sticky active scope (gitignored)
148
+ ```
149
+
150
+ ### Access Model
151
+
152
+ Scopes follow a "read-any, write-own" isolation model:
153
+
154
+ | Operation | Own Scope | Other Scopes | \_shared/ |
155
+ | --------- | --------- | ------------ | ----------- |
156
+ | **Read** | Allowed | Allowed | Allowed |
157
+ | **Write** | Allowed | Blocked | via sync-up |
158
+
159
+ ### Workflow Integration
160
+
161
+ Workflows (run via agent menus like `CP` for Create PRD, `DS` for Dev Story) automatically detect and use scope context. Resolution order:
162
+
163
+ 1. Session context from `.bmad-scope` file (set via `scope set`)
164
+ 2. `BMAD_SCOPE` environment variable
165
+ 3. Prompt user to select or create scope
166
+
167
+ **Setting your active scope:**
168
+
169
+ ```bash
170
+ # Set scope for your terminal session
171
+ npx bmad-fh scope set auth
172
+
173
+ # Or use environment variable (useful for CI/CD)
174
+ export BMAD_SCOPE=auth
85
175
  ```
86
176
 
87
- **Key Features:**
88
- - Read-any, write-own isolation model
89
- - Shared knowledge layer (`_shared/`) for cross-scope artifacts
90
- - Event system for dependency notifications
91
- - Session-sticky scope context
177
+ **Scope-aware path variables in workflows:**
178
+
179
+ - `{scope}` Scope ID (e.g., "auth")
180
+ - `{scope_path}` `_bmad-output/auth`
181
+ - `{scope_planning}` `_bmad-output/auth/planning-artifacts`
182
+ - `{scope_implementation}` → `_bmad-output/auth/implementation-artifacts`
183
+ - `{scope_tests}` → `_bmad-output/auth/tests`
184
+
185
+ ### Getting Help
186
+
187
+ ```bash
188
+ # Show comprehensive help for all scope commands
189
+ npx bmad-fh scope help
190
+
191
+ # Get detailed help for a specific command
192
+ npx bmad-fh scope help create
193
+ npx bmad-fh scope help sync-up
194
+ ```
92
195
 
93
- See [Multi-Scope Guide](docs/multi-scope-guide.md) for details.
196
+ See [Multi-Scope Guide](docs/multi-scope-guide.md) for complete documentation.
94
197
 
95
198
  ## Community
96
199
 
package/eslint.config.mjs CHANGED
@@ -81,9 +81,9 @@ export default [
81
81
  },
82
82
  },
83
83
 
84
- // CLI scripts under tools/** and test/**
84
+ // CLI scripts under tools/**, test/**, and src/core/lib/**
85
85
  {
86
- files: ['tools/**/*.js', 'tools/**/*.mjs', 'test/**/*.js'],
86
+ files: ['tools/**/*.js', 'tools/**/*.mjs', 'test/**/*.js', 'src/core/lib/**/*.js'],
87
87
  rules: {
88
88
  // Allow CommonJS patterns for Node CLI scripts
89
89
  'unicorn/prefer-module': 'off',
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "bmad-fh",
4
- "version": "6.0.0-alpha.23",
4
+ "version": "6.0.0-alpha.23.3b00cb36",
5
5
  "description": "Breakthrough Method of Agile AI-driven Development",
6
6
  "keywords": [
7
7
  "agile",
@@ -24,7 +24,6 @@
24
24
  },
25
25
  "scripts": {
26
26
  "bmad:install": "node tools/cli/bmad-cli.js install",
27
- "publish:multi-artifact": "npm publish --tag multi-artifact",
28
27
  "bundle": "node tools/cli/bundlers/bundle-web.js all",
29
28
  "docs:build": "node tools/build-docs.js",
30
29
  "docs:dev": "astro dev --root website",
@@ -34,13 +34,13 @@ user_skill_level:
34
34
  scope:
35
35
  default: ""
36
36
  result: "{value}"
37
- runtime: true # Indicates this is resolved at runtime, not during install
37
+ runtime: true # Indicates this is resolved at runtime, not during install
38
38
 
39
39
  # Scope-aware path helper - resolves to output_folder/scope or just output_folder
40
40
  scope_path:
41
41
  default: "{output_folder}"
42
42
  result: "{value}"
43
- runtime: true # Updated at runtime when scope is active
43
+ runtime: true # Updated at runtime when scope is active
44
44
 
45
45
  planning_artifacts: # Phase 1-3 artifacts
46
46
  prompt: "Where should planning artifacts be stored? (Brainstorming, Briefs, PRDs, UX Designs, Architecture, Epics)"
@@ -3,15 +3,15 @@ const path = require('node:path');
3
3
  /**
4
4
  * Resolves and enforces scope-based artifact access
5
5
  * Implements read-any/write-own access model
6
- *
6
+ *
7
7
  * @class ArtifactResolver
8
- *
8
+ *
9
9
  * @example
10
10
  * const resolver = new ArtifactResolver({
11
11
  * currentScope: 'auth',
12
12
  * basePath: '/path/to/_bmad-output'
13
13
  * });
14
- *
14
+ *
15
15
  * if (resolver.canWrite('/path/to/_bmad-output/auth/file.md')) {
16
16
  * // Write operation allowed
17
17
  * }
@@ -52,7 +52,7 @@ class ArtifactResolver {
52
52
  extractScopeFromPath(filePath) {
53
53
  // Normalize path
54
54
  const normalizedPath = path.normalize(filePath);
55
-
55
+
56
56
  // Find the base path in the file path
57
57
  const baseIndex = normalizedPath.indexOf(this.basePath);
58
58
  if (baseIndex === -1) {
@@ -61,16 +61,16 @@ class ArtifactResolver {
61
61
 
62
62
  // Get the relative path from base
63
63
  const relativePath = normalizedPath.slice(Math.max(0, baseIndex + this.basePath.length + 1));
64
-
64
+
65
65
  // Split to get the first segment (scope name)
66
66
  const segments = relativePath.split(path.sep).filter(Boolean);
67
-
67
+
68
68
  if (segments.length === 0) {
69
69
  return null;
70
70
  }
71
71
 
72
72
  const firstSegment = segments[0];
73
-
73
+
74
74
  // Check if it's a reserved path
75
75
  if (this.reservedPaths.includes(firstSegment)) {
76
76
  return firstSegment; // Return the reserved path name
@@ -109,7 +109,7 @@ class ArtifactResolver {
109
109
  // Read is always allowed for all paths
110
110
  return {
111
111
  allowed: true,
112
- reason: 'Read access is always allowed in read-any model'
112
+ reason: 'Read access is always allowed in read-any model',
113
113
  };
114
114
  }
115
115
 
@@ -124,7 +124,7 @@ class ArtifactResolver {
124
124
  return {
125
125
  allowed: true,
126
126
  reason: 'No scope active, operating in legacy mode',
127
- warning: null
127
+ warning: null,
128
128
  };
129
129
  }
130
130
 
@@ -135,7 +135,7 @@ class ArtifactResolver {
135
135
  return {
136
136
  allowed: false,
137
137
  reason: `Cannot write directly to '${this.sharedPath}'. Use: bmad scope sync-up`,
138
- warning: null
138
+ warning: null,
139
139
  };
140
140
  }
141
141
 
@@ -144,7 +144,7 @@ class ArtifactResolver {
144
144
  return {
145
145
  allowed: false,
146
146
  reason: `Cannot write to reserved path '${targetScope}'`,
147
- warning: null
147
+ warning: null,
148
148
  };
149
149
  }
150
150
 
@@ -153,7 +153,7 @@ class ArtifactResolver {
153
153
  return {
154
154
  allowed: true,
155
155
  reason: `Write allowed to current scope '${this.currentScope}'`,
156
- warning: null
156
+ warning: null,
157
157
  };
158
158
  }
159
159
 
@@ -164,31 +164,31 @@ class ArtifactResolver {
164
164
  return {
165
165
  allowed: false,
166
166
  reason: `Cannot write to scope '${targetScope}' while in scope '${this.currentScope}'`,
167
- warning: null
167
+ warning: null,
168
168
  };
169
169
  }
170
-
170
+
171
171
  case 'warn': {
172
172
  return {
173
173
  allowed: true,
174
174
  reason: 'Write allowed with warning in warn mode',
175
- warning: `Warning: Writing to scope '${targetScope}' from scope '${this.currentScope}'`
175
+ warning: `Warning: Writing to scope '${targetScope}' from scope '${this.currentScope}'`,
176
176
  };
177
177
  }
178
-
178
+
179
179
  case 'permissive': {
180
180
  return {
181
181
  allowed: true,
182
182
  reason: 'Write allowed in permissive mode',
183
- warning: null
183
+ warning: null,
184
184
  };
185
185
  }
186
-
186
+
187
187
  default: {
188
188
  return {
189
189
  allowed: false,
190
190
  reason: 'Unknown isolation mode',
191
- warning: null
191
+ warning: null,
192
192
  };
193
193
  }
194
194
  }
@@ -198,7 +198,7 @@ class ArtifactResolver {
198
198
  return {
199
199
  allowed: true,
200
200
  reason: 'Path is outside scope system',
201
- warning: null
201
+ warning: null,
202
202
  };
203
203
  }
204
204
 
@@ -209,11 +209,11 @@ class ArtifactResolver {
209
209
  */
210
210
  validateWrite(filePath) {
211
211
  const result = this.canWrite(filePath);
212
-
212
+
213
213
  if (!result.allowed) {
214
214
  throw new Error(result.reason);
215
215
  }
216
-
216
+
217
217
  if (result.warning) {
218
218
  console.warn(result.warning);
219
219
  }
@@ -227,7 +227,7 @@ class ArtifactResolver {
227
227
  */
228
228
  resolveScopePath(relativePath, scopeId = null) {
229
229
  const scope = scopeId || this.currentScope;
230
-
230
+
231
231
  if (!scope) {
232
232
  // No scope - return path relative to base
233
233
  return path.join(this.basePath, relativePath);
@@ -254,7 +254,7 @@ class ArtifactResolver {
254
254
  currentScope: this.currentScope ? path.join(this.basePath, this.currentScope) : null,
255
255
  shared: path.join(this.basePath, this.sharedPath),
256
256
  allScopes: `${this.basePath}/*`,
257
- description: 'Read access is allowed to all scopes and shared directories'
257
+ description: 'Read access is allowed to all scopes and shared directories',
258
258
  };
259
259
  }
260
260
 
@@ -266,13 +266,13 @@ class ArtifactResolver {
266
266
  if (!this.currentScope) {
267
267
  return {
268
268
  all: this.basePath,
269
- description: 'No scope active - all paths writable (legacy mode)'
269
+ description: 'No scope active - all paths writable (legacy mode)',
270
270
  };
271
271
  }
272
272
 
273
273
  return {
274
274
  currentScope: path.join(this.basePath, this.currentScope),
275
- description: `Write access limited to scope '${this.currentScope}'`
275
+ description: `Write access limited to scope '${this.currentScope}'`,
276
276
  };
277
277
  }
278
278