@hyperdrive.bot/bmad-workflow 1.0.22 → 1.0.23
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/assets/agents/dev-barry.md +69 -0
- package/assets/agents/dev.md +323 -0
- package/assets/agents/qa.md +92 -0
- package/assets/agents/sm-bob.md +65 -0
- package/assets/agents/sm.md +296 -0
- package/assets/config/default-config.yaml +6 -0
- package/assets/templates/epic-tmpl.yaml +277 -0
- package/assets/templates/prd-tmpl.yaml +261 -0
- package/assets/templates/qa-gate-tmpl.yaml +103 -0
- package/assets/templates/story-tmpl.yaml +138 -0
- package/dist/commands/eject.d.ts +76 -0
- package/dist/commands/eject.js +232 -0
- package/dist/commands/init.d.ts +47 -0
- package/dist/commands/init.js +265 -0
- package/dist/commands/stories/develop.js +1 -0
- package/dist/commands/stories/qa.d.ts +1 -0
- package/dist/commands/stories/qa.js +7 -0
- package/dist/commands/workflow.d.ts +6 -3
- package/dist/commands/workflow.js +106 -26
- package/dist/models/bmad-config-schema.d.ts +51 -0
- package/dist/models/bmad-config-schema.js +53 -0
- package/dist/services/agents/gemini-agent-runner.js +7 -2
- package/dist/services/agents/opencode-agent-runner.js +7 -2
- package/dist/services/file-system/asset-resolver.d.ts +117 -0
- package/dist/services/file-system/asset-resolver.js +234 -0
- package/dist/services/file-system/file-manager.d.ts +13 -0
- package/dist/services/file-system/file-manager.js +32 -0
- package/dist/services/file-system/path-resolver.d.ts +22 -1
- package/dist/services/file-system/path-resolver.js +36 -9
- package/dist/services/orchestration/dependency-graph-executor.js +1 -0
- package/dist/services/orchestration/workflow-orchestrator.d.ts +4 -0
- package/dist/services/orchestration/workflow-orchestrator.js +20 -6
- package/dist/utils/config-merge.d.ts +60 -0
- package/dist/utils/config-merge.js +52 -0
- package/package.json +4 -2
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { Args, Command, Flags } from '@oclif/core';
|
|
2
|
+
import { execSync } from 'node:child_process';
|
|
2
3
|
import { basename } from 'node:path';
|
|
3
4
|
import { parseDuration } from '../utils/duration.js';
|
|
5
|
+
import { AssetResolver } from '../services/file-system/asset-resolver.js';
|
|
6
|
+
import { mergeExecutionConfig } from '../utils/config-merge.js';
|
|
4
7
|
import { createAgentRunner, isProviderSupported } from '../services/agents/agent-runner-factory.js';
|
|
5
8
|
import { getRegisteredScannerNames } from '../services/review/scanner-factory.js';
|
|
6
9
|
import { Severity } from '../services/review/types.js';
|
|
@@ -55,7 +58,21 @@ export default class Workflow extends Command {
|
|
|
55
58
|
description: 'Auto-fix PRD format using AI when parsing fails',
|
|
56
59
|
}),
|
|
57
60
|
cwd: Flags.string({
|
|
58
|
-
description: 'Working directory path to pass to AI agents. Agents will operate in this directory.',
|
|
61
|
+
description: 'Working directory path to pass to AI agents. Agents will operate in this directory. When using --worktree, this is set automatically.',
|
|
62
|
+
}),
|
|
63
|
+
worktree: Flags.boolean({
|
|
64
|
+
default: false,
|
|
65
|
+
description: 'Create an isolated gut worktree before running the workflow. Requires --gut-entities.',
|
|
66
|
+
helpGroup: 'Worktree Isolation',
|
|
67
|
+
}),
|
|
68
|
+
'gut-entities': Flags.string({
|
|
69
|
+
description: 'Comma-separated gut entity names to focus before worktree creation (e.g., "serverless-api,sign"). Required with --worktree.',
|
|
70
|
+
helpGroup: 'Worktree Isolation',
|
|
71
|
+
}),
|
|
72
|
+
'worktree-install': Flags.boolean({
|
|
73
|
+
default: false,
|
|
74
|
+
description: 'Run pnpm install in the worktree after creation',
|
|
75
|
+
helpGroup: 'Worktree Isolation',
|
|
59
76
|
}),
|
|
60
77
|
'dev-agent': Flags.string({
|
|
61
78
|
description: 'Absolute path to a custom dev agent file (default: .bmad-core/agents/dev.md)',
|
|
@@ -72,13 +89,11 @@ export default class Workflow extends Command {
|
|
|
72
89
|
description: 'Seconds between story creation batches',
|
|
73
90
|
}),
|
|
74
91
|
parallel: Flags.integer({
|
|
75
|
-
default: 3,
|
|
76
|
-
description: 'Max concurrent epic/story creations',
|
|
92
|
+
description: 'Max concurrent epic/story creations (default: 3, overridden by .bmad-workflow.yaml)',
|
|
77
93
|
}),
|
|
78
94
|
pipeline: Flags.boolean({
|
|
79
95
|
allowNo: true,
|
|
80
|
-
default: true,
|
|
81
|
-
description: 'Enable pipelined workflow (start dev on stories sequentially as they are created)',
|
|
96
|
+
description: 'Enable pipelined workflow (default: true, overridden by .bmad-workflow.yaml)',
|
|
82
97
|
}),
|
|
83
98
|
'prd-interval': Flags.integer({
|
|
84
99
|
default: 60,
|
|
@@ -95,8 +110,7 @@ export default class Workflow extends Command {
|
|
|
95
110
|
description: 'Session directory prefix (default: derived from input filename, e.g., PRD-feature.md → feature)',
|
|
96
111
|
}),
|
|
97
112
|
provider: Flags.string({
|
|
98
|
-
default:
|
|
99
|
-
description: 'AI provider to use (claude, gemini, or opencode)',
|
|
113
|
+
description: 'AI provider to use (default: claude, overridden by .bmad-workflow.yaml)',
|
|
100
114
|
options: ['claude', 'gemini', 'opencode'],
|
|
101
115
|
}),
|
|
102
116
|
mcp: Flags.boolean({
|
|
@@ -131,8 +145,8 @@ export default class Workflow extends Command {
|
|
|
131
145
|
multiple: true,
|
|
132
146
|
}),
|
|
133
147
|
qa: Flags.boolean({
|
|
134
|
-
|
|
135
|
-
description: 'Run QA workflow after development completes',
|
|
148
|
+
allowNo: true,
|
|
149
|
+
description: 'Run QA workflow after development completes (default: false, overridden by qa_enabled in .bmad-workflow.yaml)',
|
|
136
150
|
helpGroup: 'QA Workflow',
|
|
137
151
|
}),
|
|
138
152
|
'qa-prompt': Flags.string({
|
|
@@ -167,8 +181,7 @@ export default class Workflow extends Command {
|
|
|
167
181
|
timeout: Flags.custom({
|
|
168
182
|
parse: async (input) => parseDuration(input),
|
|
169
183
|
})({
|
|
170
|
-
default:
|
|
171
|
-
description: 'Agent execution timeout — accepts durations like 30s, 5m, 1h, 90m, or raw milliseconds (default: 45m)',
|
|
184
|
+
description: 'Agent execution timeout — accepts durations like 30s, 5m, 1h, 90m, or raw milliseconds (default: 45m, overridden by .bmad-workflow.yaml)',
|
|
172
185
|
}),
|
|
173
186
|
'review-timeout': Flags.custom({
|
|
174
187
|
parse: async (input) => parseDuration(input),
|
|
@@ -212,12 +225,79 @@ export default class Workflow extends Command {
|
|
|
212
225
|
this.error(`File not found: ${inputPath}`, { exit: 1 });
|
|
213
226
|
}
|
|
214
227
|
}
|
|
228
|
+
// Worktree isolation: validate flags and create worktree if requested
|
|
229
|
+
if (flags.worktree) {
|
|
230
|
+
if (!flags['gut-entities']) {
|
|
231
|
+
this.error('--gut-entities is required when using --worktree. Specify comma-separated entity names (e.g., --gut-entities "serverless-api,sign").', { exit: 1 });
|
|
232
|
+
}
|
|
233
|
+
if (flags.cwd) {
|
|
234
|
+
this.error('--cwd and --worktree are mutually exclusive. --worktree sets cwd automatically.', { exit: 1 });
|
|
235
|
+
}
|
|
236
|
+
const entities = flags['gut-entities'].split(',').map((e) => e.trim()).filter(Boolean);
|
|
237
|
+
const branchSlug = `workflow/${basename(inputPath, '.md').replace(/^PRD-/, '').toLowerCase()}-${Date.now()}`;
|
|
238
|
+
const installFlag = flags['worktree-install'] ? '--install' : '';
|
|
239
|
+
this.log(colors.info(`Creating isolated worktree: ${branchSlug}`));
|
|
240
|
+
this.log(colors.info(`Entities: ${entities.join(', ')}`));
|
|
241
|
+
try {
|
|
242
|
+
// Focus entities
|
|
243
|
+
for (const entity of entities) {
|
|
244
|
+
execSync(`gut focus ${entity}`, { cwd: process.cwd(), stdio: 'pipe' });
|
|
245
|
+
}
|
|
246
|
+
// Create worktree
|
|
247
|
+
const wtOutput = execSync(`gut worktree create ${branchSlug} ${installFlag}`.trim(), { cwd: process.cwd(), encoding: 'utf8', stdio: 'pipe' });
|
|
248
|
+
// Extract worktree path from output (gut prints "Path: /tmp/gut-worktrees/...")
|
|
249
|
+
const pathMatch = wtOutput.match(/Path:\s+(.+)/);
|
|
250
|
+
if (!pathMatch) {
|
|
251
|
+
this.error('Failed to parse worktree path from gut output. Run gut worktree create manually.', { exit: 1 });
|
|
252
|
+
}
|
|
253
|
+
flags.cwd = pathMatch[1].trim();
|
|
254
|
+
this.log(colors.info(`Worktree created at: ${flags.cwd}`));
|
|
255
|
+
// Copy PRD and reference files to worktree (they may not exist in the worktree branch)
|
|
256
|
+
const { copyFileSync, mkdirSync } = await import('node:fs');
|
|
257
|
+
const { dirname, join } = await import('node:path');
|
|
258
|
+
const filesToCopy = [inputPath, ...(flags.reference || [])];
|
|
259
|
+
for (const file of filesToCopy) {
|
|
260
|
+
const destPath = join(flags.cwd, file);
|
|
261
|
+
try {
|
|
262
|
+
mkdirSync(dirname(destPath), { recursive: true });
|
|
263
|
+
copyFileSync(file, destPath);
|
|
264
|
+
}
|
|
265
|
+
catch {
|
|
266
|
+
// File may already exist in worktree — ignore
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
catch (error) {
|
|
271
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
272
|
+
this.error(`Worktree creation failed: ${message}`, { exit: 1 });
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
// Three-layer config merge: CLI flags → .bmad-workflow.yaml → hardcoded defaults
|
|
276
|
+
const assetResolver = new AssetResolver({
|
|
277
|
+
devAgent: flags['dev-agent'],
|
|
278
|
+
smAgent: flags['sm-agent'],
|
|
279
|
+
});
|
|
280
|
+
const configDefaults = assetResolver.getExecutionDefaults();
|
|
281
|
+
const cliFlags = {
|
|
282
|
+
epicInterval: flags['epic-interval'],
|
|
283
|
+
maxRetries: flags['max-retries'],
|
|
284
|
+
model: flags.model,
|
|
285
|
+
parallel: flags.parallel,
|
|
286
|
+
pipeline: flags.pipeline,
|
|
287
|
+
prdInterval: flags['prd-interval'],
|
|
288
|
+
provider: flags.provider,
|
|
289
|
+
qa: flags.qa,
|
|
290
|
+
retryBackoffMs: flags['retry-backoff'],
|
|
291
|
+
storyInterval: flags['story-interval'],
|
|
292
|
+
timeout: flags.timeout,
|
|
293
|
+
};
|
|
294
|
+
const merged = mergeExecutionConfig(cliFlags, configDefaults);
|
|
215
295
|
// Validate provider
|
|
216
|
-
if (!isProviderSupported(
|
|
217
|
-
this.error(`Unsupported provider: ${
|
|
296
|
+
if (!isProviderSupported(merged.provider)) {
|
|
297
|
+
this.error(`Unsupported provider: ${merged.provider}. Use 'claude', 'gemini', or 'opencode'.`, { exit: 1 });
|
|
218
298
|
}
|
|
219
299
|
// Initialize services with parallel concurrency and provider
|
|
220
|
-
await this.initializeServices(
|
|
300
|
+
await this.initializeServices(merged.parallel, merged.provider, flags.verbose);
|
|
221
301
|
// Register signal handlers
|
|
222
302
|
this.registerSignalHandlers();
|
|
223
303
|
// Initialize session scaffolder and create session structure
|
|
@@ -255,29 +335,29 @@ export default class Workflow extends Command {
|
|
|
255
335
|
low: Severity.LOW,
|
|
256
336
|
medium: Severity.MEDIUM,
|
|
257
337
|
};
|
|
258
|
-
// Build workflow configuration
|
|
338
|
+
// Build workflow configuration using merged execution defaults
|
|
259
339
|
const config = {
|
|
260
340
|
autoFix: flags['auto-fix'],
|
|
261
341
|
cwd: flags.cwd,
|
|
262
342
|
devAgent: flags['dev-agent'],
|
|
263
343
|
dryRun: flags['dry-run'],
|
|
264
|
-
epicInterval:
|
|
344
|
+
epicInterval: merged.epicInterval,
|
|
265
345
|
input: args.input,
|
|
266
|
-
maxRetries:
|
|
346
|
+
maxRetries: merged.maxRetries,
|
|
267
347
|
mcp: flags.mcp || undefined,
|
|
268
348
|
mcpPhases: flags['mcp-phases'] ? flags['mcp-phases'].split(',').map((s) => s.trim()) : undefined,
|
|
269
349
|
mcpPreset: flags['mcp-preset'],
|
|
270
|
-
model:
|
|
271
|
-
parallel:
|
|
272
|
-
pipeline:
|
|
273
|
-
prdInterval:
|
|
350
|
+
model: merged.model,
|
|
351
|
+
parallel: merged.parallel,
|
|
352
|
+
pipeline: merged.pipeline,
|
|
353
|
+
prdInterval: merged.prdInterval,
|
|
274
354
|
prefix: flags.prefix,
|
|
275
|
-
provider:
|
|
276
|
-
qa:
|
|
355
|
+
provider: merged.provider,
|
|
356
|
+
qa: merged.qa,
|
|
277
357
|
qaPrompt: flags['qa-prompt'],
|
|
278
358
|
qaRetries: flags['qa-retries'],
|
|
279
359
|
references: flags.reference || [],
|
|
280
|
-
retryBackoffMs:
|
|
360
|
+
retryBackoffMs: merged.retryBackoffMs,
|
|
281
361
|
review: flags.review,
|
|
282
362
|
reviewBlockOn: flags['review-block-on'] ? severityMap[flags['review-block-on']] : undefined,
|
|
283
363
|
reviewMaxFix: flags['review-max-fix'],
|
|
@@ -287,8 +367,8 @@ export default class Workflow extends Command {
|
|
|
287
367
|
smAgent: flags['sm-agent'],
|
|
288
368
|
skipEpics: flags['skip-epics'],
|
|
289
369
|
skipStories: flags['skip-stories'],
|
|
290
|
-
storyInterval:
|
|
291
|
-
timeout:
|
|
370
|
+
storyInterval: merged.storyInterval,
|
|
371
|
+
timeout: merged.timeout,
|
|
292
372
|
verbose: flags.verbose,
|
|
293
373
|
};
|
|
294
374
|
// Validate --mcp-phases when --mcp is enabled
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zod schema for .bmad-workflow.yaml configuration file.
|
|
3
|
+
*
|
|
4
|
+
* Defines validation, defaults, and type inference for the CLI config.
|
|
5
|
+
*/
|
|
6
|
+
import { z } from 'zod';
|
|
7
|
+
/**
|
|
8
|
+
* Built-in agent names grouped by role
|
|
9
|
+
*/
|
|
10
|
+
export declare const BUILT_IN_AGENTS: {
|
|
11
|
+
readonly dev: readonly ["pirlo", "barry"];
|
|
12
|
+
readonly qa: readonly ["quinn"];
|
|
13
|
+
readonly sm: readonly ["lena", "bob"];
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* All valid built-in agent names (flat list)
|
|
17
|
+
*/
|
|
18
|
+
export declare const ALL_BUILT_IN_AGENTS: readonly ["pirlo", "barry", "lena", "bob", "quinn"];
|
|
19
|
+
/**
|
|
20
|
+
* Agent descriptions for interactive menus
|
|
21
|
+
*/
|
|
22
|
+
export declare const AGENT_DESCRIPTIONS: Record<string, string>;
|
|
23
|
+
/**
|
|
24
|
+
* Zod schema for .bmad-workflow.yaml
|
|
25
|
+
*/
|
|
26
|
+
export declare const BmadConfigSchema: z.ZodObject<{
|
|
27
|
+
bmad_path: z.ZodOptional<z.ZodString>;
|
|
28
|
+
communication_language: z.ZodDefault<z.ZodString>;
|
|
29
|
+
dev_agent: z.ZodDefault<z.ZodString>;
|
|
30
|
+
document_output_language: z.ZodDefault<z.ZodString>;
|
|
31
|
+
epic_location: z.ZodDefault<z.ZodString>;
|
|
32
|
+
model: z.ZodOptional<z.ZodString>;
|
|
33
|
+
output_folder: z.ZodDefault<z.ZodString>;
|
|
34
|
+
parallel: z.ZodDefault<z.ZodNumber>;
|
|
35
|
+
provider: z.ZodDefault<z.ZodEnum<{
|
|
36
|
+
claude: "claude";
|
|
37
|
+
gemini: "gemini";
|
|
38
|
+
opencode: "opencode";
|
|
39
|
+
}>>;
|
|
40
|
+
qa_agent: z.ZodDefault<z.ZodString>;
|
|
41
|
+
qa_enabled: z.ZodDefault<z.ZodBoolean>;
|
|
42
|
+
qa_location: z.ZodDefault<z.ZodString>;
|
|
43
|
+
sm_agent: z.ZodDefault<z.ZodString>;
|
|
44
|
+
story_location: z.ZodDefault<z.ZodString>;
|
|
45
|
+
timeout: z.ZodDefault<z.ZodString>;
|
|
46
|
+
user_name: z.ZodDefault<z.ZodString>;
|
|
47
|
+
}, z.core.$strip>;
|
|
48
|
+
/**
|
|
49
|
+
* Inferred TypeScript type from the schema
|
|
50
|
+
*/
|
|
51
|
+
export type BmadConfig = z.infer<typeof BmadConfigSchema>;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zod schema for .bmad-workflow.yaml configuration file.
|
|
3
|
+
*
|
|
4
|
+
* Defines validation, defaults, and type inference for the CLI config.
|
|
5
|
+
*/
|
|
6
|
+
import { z } from 'zod';
|
|
7
|
+
/**
|
|
8
|
+
* Built-in agent names grouped by role
|
|
9
|
+
*/
|
|
10
|
+
export const BUILT_IN_AGENTS = {
|
|
11
|
+
dev: ['pirlo', 'barry'],
|
|
12
|
+
qa: ['quinn'],
|
|
13
|
+
sm: ['lena', 'bob'],
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* All valid built-in agent names (flat list)
|
|
17
|
+
*/
|
|
18
|
+
export const ALL_BUILT_IN_AGENTS = [
|
|
19
|
+
...BUILT_IN_AGENTS.dev,
|
|
20
|
+
...BUILT_IN_AGENTS.sm,
|
|
21
|
+
...BUILT_IN_AGENTS.qa,
|
|
22
|
+
];
|
|
23
|
+
/**
|
|
24
|
+
* Agent descriptions for interactive menus
|
|
25
|
+
*/
|
|
26
|
+
export const AGENT_DESCRIPTIONS = {
|
|
27
|
+
barry: 'Quick Flow Solo Dev — lean, fast, minimal ceremony',
|
|
28
|
+
bob: 'Classic SM — standard agile stories',
|
|
29
|
+
lena: 'Pirlo-Aware SM — deploy subtasks, real-infra ACs',
|
|
30
|
+
pirlo: 'Full-Stack Orchestrator — backend, deploy, E2E',
|
|
31
|
+
quinn: 'QA Engineer — rapid test coverage',
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Zod schema for .bmad-workflow.yaml
|
|
35
|
+
*/
|
|
36
|
+
export const BmadConfigSchema = z.object({
|
|
37
|
+
bmad_path: z.string().optional(),
|
|
38
|
+
communication_language: z.string().default('English'),
|
|
39
|
+
dev_agent: z.string().default('pirlo'),
|
|
40
|
+
document_output_language: z.string().default('English'),
|
|
41
|
+
epic_location: z.string().default('docs/epics'),
|
|
42
|
+
model: z.string().optional(),
|
|
43
|
+
output_folder: z.string().default('./docs'),
|
|
44
|
+
parallel: z.number().default(3),
|
|
45
|
+
provider: z.enum(['claude', 'gemini', 'opencode']).default('claude'),
|
|
46
|
+
qa_agent: z.string().default('quinn'),
|
|
47
|
+
qa_enabled: z.boolean().default(true),
|
|
48
|
+
qa_location: z.string().default('docs/qa/stories'),
|
|
49
|
+
sm_agent: z.string().default('lena'),
|
|
50
|
+
story_location: z.string().default('docs/stories'),
|
|
51
|
+
timeout: z.string().default('45m'),
|
|
52
|
+
user_name: z.string().default(''),
|
|
53
|
+
});
|
|
@@ -161,12 +161,17 @@ export class GeminiAgentRunner {
|
|
|
161
161
|
let stderrData = '';
|
|
162
162
|
try {
|
|
163
163
|
// Use exec instead of spawn for better shell compatibility
|
|
164
|
-
const
|
|
164
|
+
const execOptions = {
|
|
165
165
|
env: process.env,
|
|
166
166
|
maxBuffer: 10 * 1024 * 1024, // 10MB buffer
|
|
167
167
|
shell: process.env.SHELL || '/bin/bash',
|
|
168
168
|
timeout,
|
|
169
|
-
}
|
|
169
|
+
};
|
|
170
|
+
if (options.cwd) {
|
|
171
|
+
execOptions.cwd = options.cwd;
|
|
172
|
+
this.logger.info({ cwd: options.cwd }, 'Setting working directory for Gemini agent');
|
|
173
|
+
}
|
|
174
|
+
const { stderr, stdout } = await execAsync(command, execOptions);
|
|
170
175
|
stdoutData = stdout;
|
|
171
176
|
stderrData = stderr;
|
|
172
177
|
const duration = Date.now() - startTime;
|
|
@@ -167,12 +167,17 @@ export class OpenCodeAgentRunner {
|
|
|
167
167
|
tempFile,
|
|
168
168
|
}, 'Executing command with temp file');
|
|
169
169
|
// Use exec for better shell compatibility
|
|
170
|
-
const
|
|
170
|
+
const execOptions = {
|
|
171
171
|
env: process.env,
|
|
172
172
|
maxBuffer: 10 * 1024 * 1024, // 10MB buffer
|
|
173
173
|
shell: process.env.SHELL || '/bin/bash',
|
|
174
174
|
timeout,
|
|
175
|
-
}
|
|
175
|
+
};
|
|
176
|
+
if (options.cwd) {
|
|
177
|
+
execOptions.cwd = options.cwd;
|
|
178
|
+
this.logger.info({ cwd: options.cwd }, 'Setting working directory for OpenCode agent');
|
|
179
|
+
}
|
|
180
|
+
const { stderr, stdout } = await execAsync(command, execOptions);
|
|
176
181
|
stdoutData = stdout;
|
|
177
182
|
stderrData = stderr;
|
|
178
183
|
const duration = Date.now() - startTime;
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AssetResolver Service
|
|
3
|
+
*
|
|
4
|
+
* Resolves agents, templates, and config through a three-level chain:
|
|
5
|
+
* 1. CLI flags (highest priority)
|
|
6
|
+
* 2. .bmad-workflow.yaml config file
|
|
7
|
+
* 3. Bundled defaults (shipped inside npm package)
|
|
8
|
+
*
|
|
9
|
+
* This enables the CLI to work both standalone (zero-config) and
|
|
10
|
+
* in existing BMAD setups with .bmad-core/.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Resolved asset with its source
|
|
14
|
+
*/
|
|
15
|
+
export interface ResolvedAsset {
|
|
16
|
+
path: string;
|
|
17
|
+
source: 'bundled' | 'config' | 'flag';
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* CLI flags for agent overrides
|
|
21
|
+
*/
|
|
22
|
+
export interface AssetResolverFlags {
|
|
23
|
+
devAgent?: string;
|
|
24
|
+
qaAgent?: string;
|
|
25
|
+
smAgent?: string;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Execution defaults parsed from .bmad-workflow.yaml
|
|
29
|
+
*/
|
|
30
|
+
export interface ConfigExecutionDefaults {
|
|
31
|
+
model?: string;
|
|
32
|
+
parallel?: number;
|
|
33
|
+
pipeline?: boolean;
|
|
34
|
+
provider?: 'claude' | 'gemini' | 'opencode';
|
|
35
|
+
qa_enabled?: boolean;
|
|
36
|
+
timeout?: number;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* AssetResolver resolves asset paths using a three-level resolution chain.
|
|
40
|
+
*/
|
|
41
|
+
export declare class AssetResolver {
|
|
42
|
+
private readonly bundledPath;
|
|
43
|
+
private readonly config;
|
|
44
|
+
private readonly flags;
|
|
45
|
+
/**
|
|
46
|
+
* Create a new AssetResolver
|
|
47
|
+
*
|
|
48
|
+
* @param flags - CLI flag overrides for agent selection
|
|
49
|
+
*/
|
|
50
|
+
constructor(flags?: Partial<AssetResolverFlags>);
|
|
51
|
+
/**
|
|
52
|
+
* Resolve an agent file path.
|
|
53
|
+
*
|
|
54
|
+
* Resolution order: CLI flag → config file → bundled default
|
|
55
|
+
*
|
|
56
|
+
* @param role - Agent role (dev, sm, qa)
|
|
57
|
+
* @param flagValue - Optional CLI flag override
|
|
58
|
+
* @returns Resolved asset with path and source
|
|
59
|
+
*/
|
|
60
|
+
resolveAgent(role: 'dev' | 'qa' | 'sm', flagValue?: string): ResolvedAsset;
|
|
61
|
+
/**
|
|
62
|
+
* Resolve a template file path.
|
|
63
|
+
*
|
|
64
|
+
* Checks bmad_path config directory first, falls back to bundled.
|
|
65
|
+
*
|
|
66
|
+
* @param name - Template filename (e.g., 'epic-tmpl.yaml')
|
|
67
|
+
* @returns Resolved asset with path and source
|
|
68
|
+
*/
|
|
69
|
+
resolveTemplate(name: string): ResolvedAsset;
|
|
70
|
+
/**
|
|
71
|
+
* Resolve the config file path.
|
|
72
|
+
*
|
|
73
|
+
* Returns the absolute path to the bundled default-config.yaml.
|
|
74
|
+
* Callers (PathResolver) decide whether to prefer local .bmad-core/ over this.
|
|
75
|
+
*
|
|
76
|
+
* @returns Absolute path to bundled config file
|
|
77
|
+
*/
|
|
78
|
+
resolveConfig(): string;
|
|
79
|
+
/**
|
|
80
|
+
* Extract execution defaults from the loaded config file.
|
|
81
|
+
*
|
|
82
|
+
* Parses and validates: parallel, timeout, provider, model, qa_enabled, pipeline.
|
|
83
|
+
* Invalid values are silently ignored (field omitted from result).
|
|
84
|
+
*
|
|
85
|
+
* @returns Parsed execution defaults, or empty object if no config file
|
|
86
|
+
*/
|
|
87
|
+
getExecutionDefaults(): ConfigExecutionDefaults;
|
|
88
|
+
/**
|
|
89
|
+
* Compute the bundled assets root path.
|
|
90
|
+
*
|
|
91
|
+
* From compiled dist/services/file-system/asset-resolver.js,
|
|
92
|
+
* the assets directory is three levels up: ../../.. → <pkg>/assets/
|
|
93
|
+
*
|
|
94
|
+
* @returns Absolute path to bundled assets directory
|
|
95
|
+
*/
|
|
96
|
+
private computeBundledPath;
|
|
97
|
+
/**
|
|
98
|
+
* Get CLI flag value for a given role.
|
|
99
|
+
*/
|
|
100
|
+
private getFlagForRole;
|
|
101
|
+
/**
|
|
102
|
+
* Load .bmad-workflow.yaml from project root (if exists).
|
|
103
|
+
*
|
|
104
|
+
* @returns Parsed config or null if file absent
|
|
105
|
+
*/
|
|
106
|
+
private loadConfigFile;
|
|
107
|
+
/**
|
|
108
|
+
* Resolve an agent value — could be a built-in name or a file path.
|
|
109
|
+
*
|
|
110
|
+
* Built-in names (no path separators, no .md extension) map to bundled agents.
|
|
111
|
+
* File paths are resolved relative to project root.
|
|
112
|
+
*
|
|
113
|
+
* @param value - Agent name or file path
|
|
114
|
+
* @returns Absolute path to agent file
|
|
115
|
+
*/
|
|
116
|
+
private resolveAgentValue;
|
|
117
|
+
}
|