@hyperdrive.bot/bmad-workflow 1.0.2
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/LICENSE +21 -0
- package/README.md +1017 -0
- package/bin/dev +5 -0
- package/bin/dev.cmd +3 -0
- package/bin/dev.js +5 -0
- package/bin/run +5 -0
- package/bin/run.cmd +3 -0
- package/bin/run.js +5 -0
- package/dist/commands/config/show.d.ts +34 -0
- package/dist/commands/config/show.js +108 -0
- package/dist/commands/config/validate.d.ts +29 -0
- package/dist/commands/config/validate.js +131 -0
- package/dist/commands/decompose.d.ts +79 -0
- package/dist/commands/decompose.js +327 -0
- package/dist/commands/demo.d.ts +18 -0
- package/dist/commands/demo.js +107 -0
- package/dist/commands/epics/create.d.ts +123 -0
- package/dist/commands/epics/create.js +459 -0
- package/dist/commands/epics/list.d.ts +120 -0
- package/dist/commands/epics/list.js +280 -0
- package/dist/commands/hello/index.d.ts +12 -0
- package/dist/commands/hello/index.js +34 -0
- package/dist/commands/hello/world.d.ts +8 -0
- package/dist/commands/hello/world.js +24 -0
- package/dist/commands/prd/fix.d.ts +39 -0
- package/dist/commands/prd/fix.js +140 -0
- package/dist/commands/prd/validate.d.ts +112 -0
- package/dist/commands/prd/validate.js +302 -0
- package/dist/commands/stories/create.d.ts +95 -0
- package/dist/commands/stories/create.js +431 -0
- package/dist/commands/stories/develop.d.ts +91 -0
- package/dist/commands/stories/develop.js +460 -0
- package/dist/commands/stories/list.d.ts +84 -0
- package/dist/commands/stories/list.js +291 -0
- package/dist/commands/stories/move.d.ts +66 -0
- package/dist/commands/stories/move.js +273 -0
- package/dist/commands/stories/qa.d.ts +99 -0
- package/dist/commands/stories/qa.js +530 -0
- package/dist/commands/workflow.d.ts +97 -0
- package/dist/commands/workflow.js +390 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/models/agent-options.d.ts +50 -0
- package/dist/models/agent-options.js +1 -0
- package/dist/models/agent-result.d.ts +29 -0
- package/dist/models/agent-result.js +1 -0
- package/dist/models/index.d.ts +10 -0
- package/dist/models/index.js +10 -0
- package/dist/models/phase-result.d.ts +65 -0
- package/dist/models/phase-result.js +7 -0
- package/dist/models/provider.d.ts +28 -0
- package/dist/models/provider.js +18 -0
- package/dist/models/story.d.ts +154 -0
- package/dist/models/story.js +18 -0
- package/dist/models/workflow-config.d.ts +148 -0
- package/dist/models/workflow-config.js +1 -0
- package/dist/models/workflow-result.d.ts +164 -0
- package/dist/models/workflow-result.js +7 -0
- package/dist/services/agents/agent-runner-factory.d.ts +31 -0
- package/dist/services/agents/agent-runner-factory.js +44 -0
- package/dist/services/agents/agent-runner.d.ts +46 -0
- package/dist/services/agents/agent-runner.js +29 -0
- package/dist/services/agents/claude-agent-runner.d.ts +81 -0
- package/dist/services/agents/claude-agent-runner.js +332 -0
- package/dist/services/agents/gemini-agent-runner.d.ts +82 -0
- package/dist/services/agents/gemini-agent-runner.js +350 -0
- package/dist/services/agents/index.d.ts +7 -0
- package/dist/services/agents/index.js +7 -0
- package/dist/services/file-system/file-manager.d.ts +110 -0
- package/dist/services/file-system/file-manager.js +223 -0
- package/dist/services/file-system/glob-matcher.d.ts +75 -0
- package/dist/services/file-system/glob-matcher.js +126 -0
- package/dist/services/file-system/path-resolver.d.ts +183 -0
- package/dist/services/file-system/path-resolver.js +400 -0
- package/dist/services/logging/workflow-logger.d.ts +232 -0
- package/dist/services/logging/workflow-logger.js +552 -0
- package/dist/services/orchestration/batch-processor.d.ts +113 -0
- package/dist/services/orchestration/batch-processor.js +187 -0
- package/dist/services/orchestration/dependency-graph-executor.d.ts +60 -0
- package/dist/services/orchestration/dependency-graph-executor.js +447 -0
- package/dist/services/orchestration/index.d.ts +10 -0
- package/dist/services/orchestration/index.js +8 -0
- package/dist/services/orchestration/input-detector.d.ts +125 -0
- package/dist/services/orchestration/input-detector.js +381 -0
- package/dist/services/orchestration/story-queue.d.ts +94 -0
- package/dist/services/orchestration/story-queue.js +170 -0
- package/dist/services/orchestration/story-type-detector.d.ts +80 -0
- package/dist/services/orchestration/story-type-detector.js +258 -0
- package/dist/services/orchestration/task-decomposition-service.d.ts +67 -0
- package/dist/services/orchestration/task-decomposition-service.js +607 -0
- package/dist/services/orchestration/workflow-orchestrator.d.ts +659 -0
- package/dist/services/orchestration/workflow-orchestrator.js +2201 -0
- package/dist/services/parsers/epic-parser.d.ts +117 -0
- package/dist/services/parsers/epic-parser.js +264 -0
- package/dist/services/parsers/prd-fixer.d.ts +86 -0
- package/dist/services/parsers/prd-fixer.js +194 -0
- package/dist/services/parsers/prd-parser.d.ts +123 -0
- package/dist/services/parsers/prd-parser.js +286 -0
- package/dist/services/parsers/standalone-story-parser.d.ts +114 -0
- package/dist/services/parsers/standalone-story-parser.js +255 -0
- package/dist/services/parsers/story-parser-factory.d.ts +81 -0
- package/dist/services/parsers/story-parser-factory.js +108 -0
- package/dist/services/parsers/story-parser.d.ts +122 -0
- package/dist/services/parsers/story-parser.js +262 -0
- package/dist/services/scaffolding/decompose-session-scaffolder.d.ts +74 -0
- package/dist/services/scaffolding/decompose-session-scaffolder.js +315 -0
- package/dist/services/scaffolding/file-scaffolder.d.ts +94 -0
- package/dist/services/scaffolding/file-scaffolder.js +314 -0
- package/dist/services/validation/config-validator.d.ts +88 -0
- package/dist/services/validation/config-validator.js +167 -0
- package/dist/types/task-graph.d.ts +142 -0
- package/dist/types/task-graph.js +5 -0
- package/dist/utils/colors.d.ts +49 -0
- package/dist/utils/colors.js +50 -0
- package/dist/utils/error-formatter.d.ts +64 -0
- package/dist/utils/error-formatter.js +279 -0
- package/dist/utils/errors.d.ts +170 -0
- package/dist/utils/errors.js +233 -0
- package/dist/utils/formatters.d.ts +84 -0
- package/dist/utils/formatters.js +162 -0
- package/dist/utils/logger.d.ts +63 -0
- package/dist/utils/logger.js +78 -0
- package/dist/utils/progress.d.ts +104 -0
- package/dist/utils/progress.js +161 -0
- package/dist/utils/retry.d.ts +114 -0
- package/dist/utils/retry.js +160 -0
- package/dist/utils/shared-flags.d.ts +28 -0
- package/dist/utils/shared-flags.js +43 -0
- package/package.json +119 -0
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
/**
|
|
3
|
+
* Workflow Command
|
|
4
|
+
*
|
|
5
|
+
* Orchestrates the complete PRD → Epic → Story → Dev pipeline from a single entry point.
|
|
6
|
+
* Provides visual progress indicators, dry-run support, and graceful cancellation.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```bash
|
|
10
|
+
* # Start workflow from PRD
|
|
11
|
+
* bmad-workflow workflow docs/PRD-feature.md
|
|
12
|
+
*
|
|
13
|
+
* # Start from epic, skip dev
|
|
14
|
+
* bmad-workflow workflow docs/epics/epic-1.md --skip-dev
|
|
15
|
+
*
|
|
16
|
+
* # Dry run to preview actions
|
|
17
|
+
* bmad-workflow workflow docs/PRD-feature.md --dry-run
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
export default class Workflow extends Command {
|
|
21
|
+
static args: {
|
|
22
|
+
input: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
23
|
+
};
|
|
24
|
+
static description: string;
|
|
25
|
+
static examples: string[];
|
|
26
|
+
static flags: {
|
|
27
|
+
'auto-fix': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
28
|
+
cwd: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
29
|
+
'dry-run': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
30
|
+
'epic-interval': import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
|
|
31
|
+
parallel: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
|
|
32
|
+
pipeline: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
33
|
+
'prd-interval': import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
|
|
34
|
+
prefix: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
35
|
+
provider: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
36
|
+
qa: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
37
|
+
'qa-prompt': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
38
|
+
'qa-retries': import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
|
|
39
|
+
reference: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
40
|
+
'skip-dev': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
41
|
+
'skip-epics': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
42
|
+
'skip-stories': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
43
|
+
'story-interval': import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
|
|
44
|
+
verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
45
|
+
};
|
|
46
|
+
private cancelled;
|
|
47
|
+
private logger;
|
|
48
|
+
private orchestrator;
|
|
49
|
+
/**
|
|
50
|
+
* Main command execution
|
|
51
|
+
*
|
|
52
|
+
* Orchestrates the complete workflow with progress tracking and summary display.
|
|
53
|
+
*/
|
|
54
|
+
run(): Promise<void>;
|
|
55
|
+
/**
|
|
56
|
+
* Display dry-run banner
|
|
57
|
+
*
|
|
58
|
+
* Shows prominent message that workflow is in dry-run mode.
|
|
59
|
+
* @private
|
|
60
|
+
*/
|
|
61
|
+
private displayDryRunBanner;
|
|
62
|
+
/**
|
|
63
|
+
* Display final summary with phase results
|
|
64
|
+
*
|
|
65
|
+
* Shows table with success/failure counts and durations for each phase.
|
|
66
|
+
*
|
|
67
|
+
* @param result - Workflow result with all phase data
|
|
68
|
+
* @param config - Workflow configuration used
|
|
69
|
+
* @private
|
|
70
|
+
*/
|
|
71
|
+
private displayFinalSummary;
|
|
72
|
+
/**
|
|
73
|
+
* Display phase header with visual separator
|
|
74
|
+
*
|
|
75
|
+
* @param phaseNumber - Current phase number (1-based)
|
|
76
|
+
* @param totalPhases - Total number of phases
|
|
77
|
+
* @param phaseName - Name of the phase
|
|
78
|
+
* @private
|
|
79
|
+
*/
|
|
80
|
+
private displayPhaseHeader;
|
|
81
|
+
/**
|
|
82
|
+
* Initialize services and dependencies
|
|
83
|
+
*
|
|
84
|
+
* Creates all service instances needed for workflow orchestration.
|
|
85
|
+
* @param maxConcurrency - Maximum number of concurrent operations
|
|
86
|
+
* @param provider - AI provider to use (claude or gemini)
|
|
87
|
+
* @private
|
|
88
|
+
*/
|
|
89
|
+
private initializeServices;
|
|
90
|
+
/**
|
|
91
|
+
* Register SIGINT handler for graceful cancellation
|
|
92
|
+
*
|
|
93
|
+
* Allows user to cancel workflow with Ctrl+C, completing current operation first.
|
|
94
|
+
* @private
|
|
95
|
+
*/
|
|
96
|
+
private registerSignalHandlers;
|
|
97
|
+
}
|
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
import { Args, Command, Flags } from '@oclif/core';
|
|
2
|
+
import { createAgentRunner, isProviderSupported } from '../services/agents/agent-runner-factory.js';
|
|
3
|
+
import { FileManager } from '../services/file-system/file-manager.js';
|
|
4
|
+
import { PathResolver } from '../services/file-system/path-resolver.js';
|
|
5
|
+
import { BatchProcessor } from '../services/orchestration/batch-processor.js';
|
|
6
|
+
import { InputDetector } from '../services/orchestration/input-detector.js';
|
|
7
|
+
import { StoryTypeDetector } from '../services/orchestration/story-type-detector.js';
|
|
8
|
+
import { WorkflowOrchestrator } from '../services/orchestration/workflow-orchestrator.js';
|
|
9
|
+
import { EpicParser } from '../services/parsers/epic-parser.js';
|
|
10
|
+
import { PrdParser } from '../services/parsers/prd-parser.js';
|
|
11
|
+
import * as colors from '../utils/colors.js';
|
|
12
|
+
import { formatBox, formatTable } from '../utils/formatters.js';
|
|
13
|
+
import { createLogger } from '../utils/logger.js';
|
|
14
|
+
/**
|
|
15
|
+
* Workflow Command
|
|
16
|
+
*
|
|
17
|
+
* Orchestrates the complete PRD → Epic → Story → Dev pipeline from a single entry point.
|
|
18
|
+
* Provides visual progress indicators, dry-run support, and graceful cancellation.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```bash
|
|
22
|
+
* # Start workflow from PRD
|
|
23
|
+
* bmad-workflow workflow docs/PRD-feature.md
|
|
24
|
+
*
|
|
25
|
+
* # Start from epic, skip dev
|
|
26
|
+
* bmad-workflow workflow docs/epics/epic-1.md --skip-dev
|
|
27
|
+
*
|
|
28
|
+
* # Dry run to preview actions
|
|
29
|
+
* bmad-workflow workflow docs/PRD-feature.md --dry-run
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export default class Workflow extends Command {
|
|
33
|
+
static args = {
|
|
34
|
+
input: Args.string({
|
|
35
|
+
description: 'PRD file, epic file, or story pattern to start workflow from',
|
|
36
|
+
required: true,
|
|
37
|
+
}),
|
|
38
|
+
};
|
|
39
|
+
static description = 'Execute complete PRD → Epic → Story → Dev workflow with progress tracking';
|
|
40
|
+
static examples = [
|
|
41
|
+
'<%= config.bin %> <%= command.id %> docs/PRD-feature.md',
|
|
42
|
+
'<%= config.bin %> <%= command.id %> docs/epics/epic-1.md --skip-dev',
|
|
43
|
+
'<%= config.bin %> <%= command.id %> docs/PRD-feature.md --dry-run --verbose',
|
|
44
|
+
'<%= config.bin %> <%= command.id %> "docs/stories/STORY-*.md" --skip-epics --skip-stories',
|
|
45
|
+
];
|
|
46
|
+
static flags = {
|
|
47
|
+
'auto-fix': Flags.boolean({
|
|
48
|
+
default: false,
|
|
49
|
+
description: 'Auto-fix PRD format using AI when parsing fails',
|
|
50
|
+
}),
|
|
51
|
+
cwd: Flags.string({
|
|
52
|
+
description: 'Working directory path to pass to AI agents. Agents will operate in this directory.',
|
|
53
|
+
}),
|
|
54
|
+
'dry-run': Flags.boolean({
|
|
55
|
+
default: false,
|
|
56
|
+
description: 'Preview actions without execution',
|
|
57
|
+
}),
|
|
58
|
+
'epic-interval': Flags.integer({
|
|
59
|
+
default: 60,
|
|
60
|
+
description: 'Seconds between story creation batches',
|
|
61
|
+
}),
|
|
62
|
+
parallel: Flags.integer({
|
|
63
|
+
default: 3,
|
|
64
|
+
description: 'Max concurrent epic/story creations',
|
|
65
|
+
}),
|
|
66
|
+
pipeline: Flags.boolean({
|
|
67
|
+
allowNo: true,
|
|
68
|
+
default: true,
|
|
69
|
+
description: 'Enable pipelined workflow (start dev on stories sequentially as they are created)',
|
|
70
|
+
}),
|
|
71
|
+
'prd-interval': Flags.integer({
|
|
72
|
+
default: 60,
|
|
73
|
+
description: 'Seconds between epic creation batches',
|
|
74
|
+
}),
|
|
75
|
+
prefix: Flags.string({
|
|
76
|
+
default: '',
|
|
77
|
+
description: 'Filename prefix for generated files',
|
|
78
|
+
}),
|
|
79
|
+
provider: Flags.string({
|
|
80
|
+
default: 'claude',
|
|
81
|
+
description: 'AI provider to use (claude or gemini)',
|
|
82
|
+
options: ['claude', 'gemini'],
|
|
83
|
+
}),
|
|
84
|
+
qa: Flags.boolean({
|
|
85
|
+
default: false,
|
|
86
|
+
description: 'Run QA workflow after development completes',
|
|
87
|
+
helpGroup: 'QA Workflow',
|
|
88
|
+
}),
|
|
89
|
+
'qa-prompt': Flags.string({
|
|
90
|
+
description: 'Custom prompt/instructions for QA review phase',
|
|
91
|
+
helpGroup: 'QA Workflow',
|
|
92
|
+
}),
|
|
93
|
+
'qa-retries': Flags.integer({
|
|
94
|
+
default: 2,
|
|
95
|
+
description: 'Maximum QA → Dev fix cycles (only used with --qa)',
|
|
96
|
+
helpGroup: 'QA Workflow',
|
|
97
|
+
}),
|
|
98
|
+
reference: Flags.string({
|
|
99
|
+
description: 'Additional context files for AI agents (can be used multiple times)',
|
|
100
|
+
multiple: true,
|
|
101
|
+
}),
|
|
102
|
+
'skip-dev': Flags.boolean({
|
|
103
|
+
default: false,
|
|
104
|
+
description: 'Skip development phase',
|
|
105
|
+
}),
|
|
106
|
+
'skip-epics': Flags.boolean({
|
|
107
|
+
default: false,
|
|
108
|
+
description: 'Skip epic creation phase',
|
|
109
|
+
}),
|
|
110
|
+
'skip-stories': Flags.boolean({
|
|
111
|
+
default: false,
|
|
112
|
+
description: 'Skip story creation phase',
|
|
113
|
+
}),
|
|
114
|
+
'story-interval': Flags.integer({
|
|
115
|
+
default: 60,
|
|
116
|
+
description: 'Seconds between story development',
|
|
117
|
+
}),
|
|
118
|
+
verbose: Flags.boolean({
|
|
119
|
+
char: 'v',
|
|
120
|
+
default: false,
|
|
121
|
+
description: 'Detailed output mode',
|
|
122
|
+
}),
|
|
123
|
+
};
|
|
124
|
+
cancelled = false;
|
|
125
|
+
logger;
|
|
126
|
+
orchestrator;
|
|
127
|
+
/**
|
|
128
|
+
* Main command execution
|
|
129
|
+
*
|
|
130
|
+
* Orchestrates the complete workflow with progress tracking and summary display.
|
|
131
|
+
*/
|
|
132
|
+
async run() {
|
|
133
|
+
const { args, flags } = await this.parse(Workflow);
|
|
134
|
+
try {
|
|
135
|
+
// Validate provider
|
|
136
|
+
if (!isProviderSupported(flags.provider)) {
|
|
137
|
+
this.error(`Unsupported provider: ${flags.provider}. Use 'claude' or 'gemini'.`, { exit: 1 });
|
|
138
|
+
}
|
|
139
|
+
// Initialize services with parallel concurrency and provider
|
|
140
|
+
await this.initializeServices(flags.parallel, flags.provider);
|
|
141
|
+
// Register signal handlers
|
|
142
|
+
this.registerSignalHandlers();
|
|
143
|
+
// Show dry-run banner if applicable
|
|
144
|
+
if (flags['dry-run']) {
|
|
145
|
+
this.displayDryRunBanner();
|
|
146
|
+
}
|
|
147
|
+
// Build workflow configuration
|
|
148
|
+
const config = {
|
|
149
|
+
autoFix: flags['auto-fix'],
|
|
150
|
+
cwd: flags.cwd,
|
|
151
|
+
dryRun: flags['dry-run'],
|
|
152
|
+
epicInterval: flags['epic-interval'],
|
|
153
|
+
input: args.input,
|
|
154
|
+
parallel: flags.parallel,
|
|
155
|
+
pipeline: flags.pipeline,
|
|
156
|
+
prdInterval: flags['prd-interval'],
|
|
157
|
+
prefix: flags.prefix,
|
|
158
|
+
provider: flags.provider,
|
|
159
|
+
qa: flags.qa,
|
|
160
|
+
qaPrompt: flags['qa-prompt'],
|
|
161
|
+
qaRetries: flags['qa-retries'],
|
|
162
|
+
references: flags.reference || [],
|
|
163
|
+
skipDev: flags['skip-dev'],
|
|
164
|
+
skipEpics: flags['skip-epics'],
|
|
165
|
+
skipStories: flags['skip-stories'],
|
|
166
|
+
storyInterval: flags['story-interval'],
|
|
167
|
+
verbose: flags.verbose,
|
|
168
|
+
};
|
|
169
|
+
// Log configuration if verbose
|
|
170
|
+
if (flags.verbose) {
|
|
171
|
+
this.logger.info({ config }, 'Workflow configuration');
|
|
172
|
+
this.log(colors.info(`Pipeline mode: ${config.pipeline ? 'enabled' : 'disabled'}`));
|
|
173
|
+
}
|
|
174
|
+
// Execute workflow
|
|
175
|
+
this.log(colors.info('\nStarting workflow orchestration...\n'));
|
|
176
|
+
const result = await this.orchestrator.execute(config);
|
|
177
|
+
// Check for cancellation
|
|
178
|
+
if (this.cancelled) {
|
|
179
|
+
this.log('\n' + colors.warning('Workflow cancelled by user'));
|
|
180
|
+
this.displayFinalSummary(result, config);
|
|
181
|
+
process.exit(130); // Standard interrupted exit code
|
|
182
|
+
}
|
|
183
|
+
// Display final summary
|
|
184
|
+
this.displayFinalSummary(result, config);
|
|
185
|
+
// Exit with appropriate code
|
|
186
|
+
if (!result.overallSuccess) {
|
|
187
|
+
this.error('Workflow completed with failures', { exit: 1 });
|
|
188
|
+
}
|
|
189
|
+
this.log('\n' + colors.success('✓ Workflow completed successfully!'));
|
|
190
|
+
}
|
|
191
|
+
catch (error) {
|
|
192
|
+
this.logger.error({ error: error.message }, 'Workflow command failed');
|
|
193
|
+
// Display user-friendly error
|
|
194
|
+
this.log('\n' + colors.error('✗ Workflow failed:'));
|
|
195
|
+
this.log(colors.error(` ${error.message}`));
|
|
196
|
+
this.error('Workflow execution failed', { exit: 1 });
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Display dry-run banner
|
|
201
|
+
*
|
|
202
|
+
* Shows prominent message that workflow is in dry-run mode.
|
|
203
|
+
* @private
|
|
204
|
+
*/
|
|
205
|
+
displayDryRunBanner() {
|
|
206
|
+
const banner = formatBox('DRY RUN MODE', 'Actions will be previewed but NOT executed.\nNo files will be created or modified.');
|
|
207
|
+
this.log('\n' + colors.warning(banner));
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Display final summary with phase results
|
|
211
|
+
*
|
|
212
|
+
* Shows table with success/failure counts and durations for each phase.
|
|
213
|
+
*
|
|
214
|
+
* @param result - Workflow result with all phase data
|
|
215
|
+
* @param config - Workflow configuration used
|
|
216
|
+
* @private
|
|
217
|
+
*/
|
|
218
|
+
// eslint-disable-next-line complexity -- Display method with multiple phases to format
|
|
219
|
+
displayFinalSummary(result, config) {
|
|
220
|
+
this.log('\n' + colors.bold('╔════════════════════════════════════════════════════════╗'));
|
|
221
|
+
this.log(colors.bold('║ Workflow Summary ║'));
|
|
222
|
+
this.log(colors.bold('╚════════════════════════════════════════════════════════╝') + '\n');
|
|
223
|
+
// Build table rows
|
|
224
|
+
const rows = [];
|
|
225
|
+
if (result.epicPhase) {
|
|
226
|
+
const status = result.epicPhase.skipped
|
|
227
|
+
? colors.dim('Skipped')
|
|
228
|
+
: result.epicPhase.failures.length > 0
|
|
229
|
+
? colors.error(`${result.epicPhase.failures.length} failed`)
|
|
230
|
+
: colors.success('✓ Complete');
|
|
231
|
+
rows.push([
|
|
232
|
+
'Epic Creation',
|
|
233
|
+
result.epicPhase.success.toString(),
|
|
234
|
+
result.epicPhase.failures.length.toString(),
|
|
235
|
+
`${(result.epicPhase.duration / 1000).toFixed(1)}s`,
|
|
236
|
+
status,
|
|
237
|
+
]);
|
|
238
|
+
}
|
|
239
|
+
if (result.storyPhase) {
|
|
240
|
+
const status = result.storyPhase.skipped
|
|
241
|
+
? colors.dim('Skipped')
|
|
242
|
+
: result.storyPhase.failures.length > 0
|
|
243
|
+
? colors.error(`${result.storyPhase.failures.length} failed`)
|
|
244
|
+
: colors.success('✓ Complete');
|
|
245
|
+
rows.push([
|
|
246
|
+
'Story Creation',
|
|
247
|
+
result.storyPhase.success.toString(),
|
|
248
|
+
result.storyPhase.failures.length.toString(),
|
|
249
|
+
`${(result.storyPhase.duration / 1000).toFixed(1)}s`,
|
|
250
|
+
status,
|
|
251
|
+
]);
|
|
252
|
+
}
|
|
253
|
+
if (result.devPhase) {
|
|
254
|
+
const status = result.devPhase.skipped
|
|
255
|
+
? colors.dim('Skipped')
|
|
256
|
+
: result.devPhase.failures.length > 0
|
|
257
|
+
? colors.error(`${result.devPhase.failures.length} failed`)
|
|
258
|
+
: colors.success('✓ Complete');
|
|
259
|
+
rows.push([
|
|
260
|
+
'Development',
|
|
261
|
+
result.devPhase.success.toString(),
|
|
262
|
+
result.devPhase.failures.length.toString(),
|
|
263
|
+
`${(result.devPhase.duration / 1000).toFixed(1)}s`,
|
|
264
|
+
status,
|
|
265
|
+
]);
|
|
266
|
+
}
|
|
267
|
+
if (result.qaPhase && !result.qaPhase.skipped) {
|
|
268
|
+
const status = result.qaPhase.failures.length > 0
|
|
269
|
+
? colors.error(`${result.qaPhase.failures.length} failed`)
|
|
270
|
+
: colors.success('✓ Complete');
|
|
271
|
+
rows.push([
|
|
272
|
+
'QA Review',
|
|
273
|
+
result.qaPhase.success.toString(),
|
|
274
|
+
result.qaPhase.failures.length.toString(),
|
|
275
|
+
`${(result.qaPhase.duration / 1000).toFixed(1)}s`,
|
|
276
|
+
status,
|
|
277
|
+
]);
|
|
278
|
+
}
|
|
279
|
+
// Add totals row
|
|
280
|
+
rows.push([
|
|
281
|
+
colors.bold('Total'),
|
|
282
|
+
colors.bold(result.totalFilesProcessed.toString()),
|
|
283
|
+
colors.bold(result.totalFailures.toString()),
|
|
284
|
+
colors.bold(`${(result.totalDuration / 1000).toFixed(1)}s`),
|
|
285
|
+
result.overallSuccess ? colors.success('✓ Success') : colors.error('✗ Failed'),
|
|
286
|
+
]);
|
|
287
|
+
const table = formatTable(['Phase', 'Success', 'Failed', 'Duration', 'Status'], rows);
|
|
288
|
+
this.log(table);
|
|
289
|
+
// Display failures if any
|
|
290
|
+
if (result.totalFailures > 0) {
|
|
291
|
+
this.log('\n' + colors.error('Failures:'));
|
|
292
|
+
if (result.epicPhase?.failures.length) {
|
|
293
|
+
this.log(colors.error(` Epic Phase:`));
|
|
294
|
+
for (const failure of result.epicPhase.failures) {
|
|
295
|
+
this.log(colors.error(` • ${failure.identifier}: ${failure.error}`));
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
if (result.storyPhase?.failures.length) {
|
|
299
|
+
this.log(colors.error(` Story Phase:`));
|
|
300
|
+
for (const failure of result.storyPhase.failures) {
|
|
301
|
+
this.log(colors.error(` • ${failure.identifier}: ${failure.error}`));
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
if (result.devPhase?.failures.length) {
|
|
305
|
+
this.log(colors.error(` Development Phase:`));
|
|
306
|
+
for (const failure of result.devPhase.failures) {
|
|
307
|
+
this.log(colors.error(` • ${failure.identifier}: ${failure.error}`));
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
if (result.qaPhase?.failures.length) {
|
|
311
|
+
this.log(colors.error(` QA Phase:`));
|
|
312
|
+
for (const failure of result.qaPhase.failures) {
|
|
313
|
+
this.log(colors.error(` • ${failure.identifier}: ${failure.error}`));
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
// Dry run reminder
|
|
318
|
+
if (config.dryRun) {
|
|
319
|
+
this.log('\n' + colors.warning('No files were created (dry-run mode)'));
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Display phase header with visual separator
|
|
324
|
+
*
|
|
325
|
+
* @param phaseNumber - Current phase number (1-based)
|
|
326
|
+
* @param totalPhases - Total number of phases
|
|
327
|
+
* @param phaseName - Name of the phase
|
|
328
|
+
* @private
|
|
329
|
+
*/
|
|
330
|
+
displayPhaseHeader(phaseNumber, totalPhases, phaseName) {
|
|
331
|
+
const header = `Phase ${phaseNumber}/${totalPhases}: ${phaseName}`;
|
|
332
|
+
const separator = '═'.repeat(header.length + 4);
|
|
333
|
+
this.log('\n' + colors.bold(`╔${separator}╗`));
|
|
334
|
+
this.log(colors.bold(`║ ${header} ║`));
|
|
335
|
+
this.log(colors.bold(`╚${separator}╝`) + '\n');
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Initialize services and dependencies
|
|
339
|
+
*
|
|
340
|
+
* Creates all service instances needed for workflow orchestration.
|
|
341
|
+
* @param maxConcurrency - Maximum number of concurrent operations
|
|
342
|
+
* @param provider - AI provider to use (claude or gemini)
|
|
343
|
+
* @private
|
|
344
|
+
*/
|
|
345
|
+
async initializeServices(maxConcurrency = 3, provider = 'claude') {
|
|
346
|
+
// Create logger
|
|
347
|
+
this.logger = createLogger({ namespace: 'commands:workflow' });
|
|
348
|
+
this.logger.info({ provider }, 'Initializing services with AI provider');
|
|
349
|
+
// Create file system services
|
|
350
|
+
const fileManager = new FileManager(this.logger);
|
|
351
|
+
const pathResolver = new PathResolver(fileManager, this.logger);
|
|
352
|
+
// Create parser services
|
|
353
|
+
const prdParser = new PrdParser(this.logger);
|
|
354
|
+
const epicParser = new EpicParser(this.logger);
|
|
355
|
+
// Create agent runner using factory (supports Claude or Gemini)
|
|
356
|
+
const agentRunner = createAgentRunner(provider, this.logger);
|
|
357
|
+
// Create batch processor with configured concurrency
|
|
358
|
+
const batchProcessor = new BatchProcessor(maxConcurrency, 0, this.logger);
|
|
359
|
+
// Create input detector
|
|
360
|
+
const inputDetector = new InputDetector(fileManager, this.logger);
|
|
361
|
+
// Create story type detector for auto-documentation
|
|
362
|
+
const storyTypeDetector = new StoryTypeDetector(this.logger);
|
|
363
|
+
// Create orchestrator
|
|
364
|
+
this.orchestrator = new WorkflowOrchestrator({
|
|
365
|
+
agentRunner,
|
|
366
|
+
batchProcessor,
|
|
367
|
+
epicParser,
|
|
368
|
+
fileManager,
|
|
369
|
+
inputDetector,
|
|
370
|
+
logger: this.logger,
|
|
371
|
+
pathResolver,
|
|
372
|
+
prdParser,
|
|
373
|
+
storyTypeDetector,
|
|
374
|
+
});
|
|
375
|
+
this.logger.info({ provider }, 'Services initialized successfully');
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Register SIGINT handler for graceful cancellation
|
|
379
|
+
*
|
|
380
|
+
* Allows user to cancel workflow with Ctrl+C, completing current operation first.
|
|
381
|
+
* @private
|
|
382
|
+
*/
|
|
383
|
+
registerSignalHandlers() {
|
|
384
|
+
process.on('SIGINT', () => {
|
|
385
|
+
this.cancelled = true;
|
|
386
|
+
this.logger.warn('Cancellation requested... completing current operation');
|
|
387
|
+
this.log('\n' + colors.warning('⚠️ Cancellation requested... completing current operation'));
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { run } from '@oclif/core';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { run } from '@oclif/core';
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { AgentResult } from './agent-result.js';
|
|
2
|
+
/**
|
|
3
|
+
* Available BMAD agent types
|
|
4
|
+
*/
|
|
5
|
+
export type AgentType = 'analyst' | 'architect' | 'dev' | 'pm' | 'prd-fixer' | 'quick-flow-solo-dev' | 'sm' | 'tea' | 'tech-writer' | 'ux-designer';
|
|
6
|
+
/**
|
|
7
|
+
* Agent options without prompt and callbacks (used internally)
|
|
8
|
+
*/
|
|
9
|
+
export type AgentOptionsWithoutPrompt = Omit<AgentOptions, 'onPrompt' | 'onResponse' | 'prompt'>;
|
|
10
|
+
/**
|
|
11
|
+
* Callback invoked when a prompt is about to be sent to Claude
|
|
12
|
+
*/
|
|
13
|
+
export type OnPromptCallback = (prompt: string, options: AgentOptionsWithoutPrompt) => Promise<void> | void;
|
|
14
|
+
/**
|
|
15
|
+
* Callback invoked when a response is received from Claude
|
|
16
|
+
*/
|
|
17
|
+
export type OnResponseCallback = (result: AgentResult) => Promise<void> | void;
|
|
18
|
+
/**
|
|
19
|
+
* Options for executing a Claude AI agent
|
|
20
|
+
*/
|
|
21
|
+
export interface AgentOptions {
|
|
22
|
+
/**
|
|
23
|
+
* The type of agent to execute
|
|
24
|
+
*/
|
|
25
|
+
agentType: AgentType;
|
|
26
|
+
/**
|
|
27
|
+
* Additional CLI flags to pass to the Claude executable
|
|
28
|
+
*/
|
|
29
|
+
flags?: string[];
|
|
30
|
+
/**
|
|
31
|
+
* Callback invoked when prompt is sent (for logging)
|
|
32
|
+
*/
|
|
33
|
+
onPrompt?: OnPromptCallback;
|
|
34
|
+
/**
|
|
35
|
+
* Callback invoked when response is received (for logging)
|
|
36
|
+
*/
|
|
37
|
+
onResponse?: OnResponseCallback;
|
|
38
|
+
/**
|
|
39
|
+
* The prompt to execute with the agent
|
|
40
|
+
*/
|
|
41
|
+
prompt: string;
|
|
42
|
+
/**
|
|
43
|
+
* File references to include in the agent execution
|
|
44
|
+
*/
|
|
45
|
+
references?: string[];
|
|
46
|
+
/**
|
|
47
|
+
* Timeout in milliseconds (default: 1800000ms = 30 minutes)
|
|
48
|
+
*/
|
|
49
|
+
timeout?: number;
|
|
50
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Result of executing a Claude AI agent
|
|
3
|
+
*/
|
|
4
|
+
export interface AgentResult {
|
|
5
|
+
/**
|
|
6
|
+
* The type of agent that was executed
|
|
7
|
+
*/
|
|
8
|
+
agentType: string;
|
|
9
|
+
/**
|
|
10
|
+
* Execution duration in milliseconds
|
|
11
|
+
*/
|
|
12
|
+
duration: number;
|
|
13
|
+
/**
|
|
14
|
+
* Error messages from stderr
|
|
15
|
+
*/
|
|
16
|
+
errors: string;
|
|
17
|
+
/**
|
|
18
|
+
* The process exit code
|
|
19
|
+
*/
|
|
20
|
+
exitCode: number;
|
|
21
|
+
/**
|
|
22
|
+
* The output from stdout
|
|
23
|
+
*/
|
|
24
|
+
output: string;
|
|
25
|
+
/**
|
|
26
|
+
* Whether the agent execution was successful
|
|
27
|
+
*/
|
|
28
|
+
success: boolean;
|
|
29
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Model exports
|
|
3
|
+
*/
|
|
4
|
+
export * from './agent-options.js';
|
|
5
|
+
export * from './agent-result.js';
|
|
6
|
+
export * from './phase-result.js';
|
|
7
|
+
export * from './provider.js';
|
|
8
|
+
export * from './story.js';
|
|
9
|
+
export * from './workflow-config.js';
|
|
10
|
+
export * from './workflow-result.js';
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Model exports
|
|
3
|
+
*/
|
|
4
|
+
export * from './agent-options.js';
|
|
5
|
+
export * from './agent-result.js';
|
|
6
|
+
export * from './phase-result.js';
|
|
7
|
+
export * from './provider.js';
|
|
8
|
+
export * from './story.js';
|
|
9
|
+
export * from './workflow-config.js';
|
|
10
|
+
export * from './workflow-result.js';
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase Result
|
|
3
|
+
*
|
|
4
|
+
* Represents the result of executing a single workflow phase
|
|
5
|
+
* (epic creation, story creation, or development).
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Result of a single workflow phase execution
|
|
9
|
+
*
|
|
10
|
+
* Tracks successes, failures, and execution metrics for a phase.
|
|
11
|
+
* Phases include epic creation, story creation, and development.
|
|
12
|
+
*/
|
|
13
|
+
export interface PhaseResult {
|
|
14
|
+
/**
|
|
15
|
+
* Execution time in milliseconds
|
|
16
|
+
*
|
|
17
|
+
* Time from phase start to phase completion.
|
|
18
|
+
*/
|
|
19
|
+
duration: number;
|
|
20
|
+
/**
|
|
21
|
+
* Details of failed operations
|
|
22
|
+
*
|
|
23
|
+
* Empty array if all operations succeeded.
|
|
24
|
+
*/
|
|
25
|
+
failures: PhaseFailure[];
|
|
26
|
+
/**
|
|
27
|
+
* Name of the phase that was executed
|
|
28
|
+
*
|
|
29
|
+
* One of: 'epic', 'story', 'dev'
|
|
30
|
+
*/
|
|
31
|
+
phaseName: string;
|
|
32
|
+
/**
|
|
33
|
+
* Phase was skipped
|
|
34
|
+
*
|
|
35
|
+
* True if phase was skipped due to flag (skipEpics, skipStories, skipDev)
|
|
36
|
+
* or due to input type (e.g., starting from epic file skips epic phase).
|
|
37
|
+
* @default false
|
|
38
|
+
*/
|
|
39
|
+
skipped: boolean;
|
|
40
|
+
/**
|
|
41
|
+
* Count of successful operations in this phase
|
|
42
|
+
*
|
|
43
|
+
* For epic phase: number of epics created successfully
|
|
44
|
+
* For story phase: number of stories created successfully
|
|
45
|
+
* For dev phase: number of stories developed successfully
|
|
46
|
+
*/
|
|
47
|
+
success: number;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Details of a single failure within a phase
|
|
51
|
+
*/
|
|
52
|
+
export interface PhaseFailure {
|
|
53
|
+
/**
|
|
54
|
+
* Error message describing the failure
|
|
55
|
+
*/
|
|
56
|
+
error: string;
|
|
57
|
+
/**
|
|
58
|
+
* Identifier for the failed operation
|
|
59
|
+
*
|
|
60
|
+
* For epic phase: epic number (e.g., '1', '2')
|
|
61
|
+
* For story phase: story number (e.g., '1.1', '2.3')
|
|
62
|
+
* For dev phase: story file path
|
|
63
|
+
*/
|
|
64
|
+
identifier: string;
|
|
65
|
+
}
|