@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,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error Handling Utilities
|
|
3
|
+
*
|
|
4
|
+
* Provides custom error classes with standardized error codes and context.
|
|
5
|
+
* All errors extend BaseError for consistent handling across the CLI.
|
|
6
|
+
*/
|
|
7
|
+
import type pino from 'pino';
|
|
8
|
+
/**
|
|
9
|
+
* Error code constants for different error categories
|
|
10
|
+
*/
|
|
11
|
+
export declare const ERR_VALIDATION = "ERR_VALIDATION";
|
|
12
|
+
export declare const ERR_PARSER = "ERR_PARSER";
|
|
13
|
+
export declare const ERR_FILE_SYSTEM = "ERR_FILE_SYSTEM";
|
|
14
|
+
export declare const ERR_AGENT = "ERR_AGENT";
|
|
15
|
+
export declare const ERR_CONFIG = "ERR_CONFIG";
|
|
16
|
+
/**
|
|
17
|
+
* Type for error codes
|
|
18
|
+
*/
|
|
19
|
+
export type ErrorCode = typeof ERR_AGENT | typeof ERR_CONFIG | typeof ERR_FILE_SYSTEM | typeof ERR_PARSER | typeof ERR_VALIDATION;
|
|
20
|
+
/**
|
|
21
|
+
* Context object for additional error information
|
|
22
|
+
*/
|
|
23
|
+
export interface ErrorContext {
|
|
24
|
+
[key: string]: unknown;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Base error class with error codes and context
|
|
28
|
+
*
|
|
29
|
+
* All custom errors should extend this class for consistent handling.
|
|
30
|
+
*/
|
|
31
|
+
export declare class BaseError extends Error {
|
|
32
|
+
/**
|
|
33
|
+
* Error code for categorization
|
|
34
|
+
*/
|
|
35
|
+
readonly code: ErrorCode;
|
|
36
|
+
/**
|
|
37
|
+
* Additional context information
|
|
38
|
+
*/
|
|
39
|
+
readonly context?: ErrorContext;
|
|
40
|
+
/**
|
|
41
|
+
* Actionable suggestion for resolving the error
|
|
42
|
+
*/
|
|
43
|
+
readonly suggestion?: string;
|
|
44
|
+
/**
|
|
45
|
+
* Create a new BaseError
|
|
46
|
+
*
|
|
47
|
+
* @param message - Human-readable error message
|
|
48
|
+
* @param code - Error code for categorization
|
|
49
|
+
* @param context - Additional context information
|
|
50
|
+
* @param suggestion - Actionable suggestion for resolving the error
|
|
51
|
+
* @example
|
|
52
|
+
* throw new BaseError('Operation failed', 'ERR_VALIDATION', { field: 'email' }, 'Provide a valid email address')
|
|
53
|
+
*/
|
|
54
|
+
constructor(message: string, code: ErrorCode, context?: ErrorContext, suggestion?: string);
|
|
55
|
+
/**
|
|
56
|
+
* Convert error to JSON for logging
|
|
57
|
+
*
|
|
58
|
+
* @returns JSON representation of the error
|
|
59
|
+
*/
|
|
60
|
+
toJSON(): Record<string, unknown>;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Validation error for input validation failures
|
|
64
|
+
*
|
|
65
|
+
* Used when user input or configuration is invalid.
|
|
66
|
+
*/
|
|
67
|
+
export declare class ValidationError extends BaseError {
|
|
68
|
+
/**
|
|
69
|
+
* Create a new ValidationError
|
|
70
|
+
*
|
|
71
|
+
* @param message - Human-readable error message
|
|
72
|
+
* @param context - Additional context (e.g., field name, validation rule)
|
|
73
|
+
* @param suggestion - Actionable suggestion for resolving the error
|
|
74
|
+
* @example
|
|
75
|
+
* throw new ValidationError('Email is required', { field: 'email', value: '' }, 'Provide a valid email address in the format: user@example.com')
|
|
76
|
+
*/
|
|
77
|
+
constructor(message: string, context?: ErrorContext, suggestion?: string);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* File system error for file operation failures
|
|
81
|
+
*
|
|
82
|
+
* Used when file operations (read, write, move) fail.
|
|
83
|
+
*/
|
|
84
|
+
export declare class FileSystemError extends BaseError {
|
|
85
|
+
/**
|
|
86
|
+
* Create a new FileSystemError
|
|
87
|
+
*
|
|
88
|
+
* @param message - Human-readable error message
|
|
89
|
+
* @param context - Additional context (e.g., file path, operation type)
|
|
90
|
+
* @param suggestion - Actionable suggestion for resolving the error
|
|
91
|
+
* @example
|
|
92
|
+
* throw new FileSystemError('File not found', { filePath: '/path/to/file.md' }, "Check that you're in the project root directory and the path is correct.")
|
|
93
|
+
*/
|
|
94
|
+
constructor(message: string, context?: ErrorContext, suggestion?: string);
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Agent error for Claude CLI execution failures
|
|
98
|
+
*
|
|
99
|
+
* Used when spawning or executing Claude CLI processes fails.
|
|
100
|
+
*/
|
|
101
|
+
export declare class AgentError extends BaseError {
|
|
102
|
+
/**
|
|
103
|
+
* Create a new AgentError
|
|
104
|
+
*
|
|
105
|
+
* @param message - Human-readable error message
|
|
106
|
+
* @param context - Additional context (e.g., exit code, stderr, agent type)
|
|
107
|
+
* @param suggestion - Actionable suggestion for resolving the error
|
|
108
|
+
* @example
|
|
109
|
+
* throw new AgentError('Agent execution failed', { exitCode: 1, stderr: 'Error output' }, 'Ensure Claude CLI is installed and accessible in your PATH.')
|
|
110
|
+
*/
|
|
111
|
+
constructor(message: string, context?: ErrorContext, suggestion?: string);
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Configuration error for config validation failures
|
|
115
|
+
*
|
|
116
|
+
* Used when configuration files are invalid or missing required fields.
|
|
117
|
+
*/
|
|
118
|
+
export declare class ConfigurationError extends BaseError {
|
|
119
|
+
/**
|
|
120
|
+
* Create a new ConfigurationError
|
|
121
|
+
*
|
|
122
|
+
* @param message - Human-readable error message
|
|
123
|
+
* @param context - Additional context (e.g., config field, expected value, actual value)
|
|
124
|
+
* @param suggestion - Actionable suggestion for resolving the error
|
|
125
|
+
* @example
|
|
126
|
+
* throw new ConfigurationError('Invalid field "prdFile"', { field: 'prdFile', expectedType: 'string', actualType: 'number' }, "Field 'prdFile' expects string, got number. Example: 'docs/prd.md'")
|
|
127
|
+
*/
|
|
128
|
+
constructor(message: string, context?: ErrorContext, suggestion?: string);
|
|
129
|
+
/**
|
|
130
|
+
* Get example value for common config fields
|
|
131
|
+
* @param field - Config field name
|
|
132
|
+
* @param type - Expected type
|
|
133
|
+
* @returns Example value
|
|
134
|
+
*/
|
|
135
|
+
private static getExampleValue;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Parser error for markdown parsing failures
|
|
139
|
+
*
|
|
140
|
+
* Used when parsing PRD, epic, or story files fails.
|
|
141
|
+
*/
|
|
142
|
+
export declare class ParserError extends BaseError {
|
|
143
|
+
/**
|
|
144
|
+
* Create a new ParserError
|
|
145
|
+
*
|
|
146
|
+
* @param message - Human-readable error message
|
|
147
|
+
* @param context - Additional context (e.g., file path, line number, problematic line content)
|
|
148
|
+
* @param suggestion - Actionable suggestion for resolving the error
|
|
149
|
+
* @example
|
|
150
|
+
* throw new ParserError('Invalid epic format at line 42', { filePath: 'prd.md', line: 42, lineContent: '## Epic A: Title' }, "Expected format: '## Epic 1: Title'")
|
|
151
|
+
*/
|
|
152
|
+
constructor(message: string, context?: ErrorContext, suggestion?: string);
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Handle fatal errors by logging and exiting the process
|
|
156
|
+
*
|
|
157
|
+
* This function should be used for unrecoverable errors that require
|
|
158
|
+
* the CLI to exit immediately. It logs the error with full context
|
|
159
|
+
* and exits with code 1.
|
|
160
|
+
*
|
|
161
|
+
* @param error - The error to handle
|
|
162
|
+
* @param logger - Logger instance for error logging
|
|
163
|
+
* @example
|
|
164
|
+
* try {
|
|
165
|
+
* // critical operation
|
|
166
|
+
* } catch (error) {
|
|
167
|
+
* handleFatalError(error as Error, logger)
|
|
168
|
+
* }
|
|
169
|
+
*/
|
|
170
|
+
export declare function handleFatalError(error: Error, logger: pino.Logger): never;
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error Handling Utilities
|
|
3
|
+
*
|
|
4
|
+
* Provides custom error classes with standardized error codes and context.
|
|
5
|
+
* All errors extend BaseError for consistent handling across the CLI.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Error code constants for different error categories
|
|
9
|
+
*/
|
|
10
|
+
export const ERR_VALIDATION = 'ERR_VALIDATION';
|
|
11
|
+
export const ERR_PARSER = 'ERR_PARSER';
|
|
12
|
+
export const ERR_FILE_SYSTEM = 'ERR_FILE_SYSTEM';
|
|
13
|
+
export const ERR_AGENT = 'ERR_AGENT';
|
|
14
|
+
export const ERR_CONFIG = 'ERR_CONFIG';
|
|
15
|
+
/**
|
|
16
|
+
* Base error class with error codes and context
|
|
17
|
+
*
|
|
18
|
+
* All custom errors should extend this class for consistent handling.
|
|
19
|
+
*/
|
|
20
|
+
export class BaseError extends Error {
|
|
21
|
+
/**
|
|
22
|
+
* Error code for categorization
|
|
23
|
+
*/
|
|
24
|
+
code;
|
|
25
|
+
/**
|
|
26
|
+
* Additional context information
|
|
27
|
+
*/
|
|
28
|
+
context;
|
|
29
|
+
/**
|
|
30
|
+
* Actionable suggestion for resolving the error
|
|
31
|
+
*/
|
|
32
|
+
suggestion;
|
|
33
|
+
/**
|
|
34
|
+
* Create a new BaseError
|
|
35
|
+
*
|
|
36
|
+
* @param message - Human-readable error message
|
|
37
|
+
* @param code - Error code for categorization
|
|
38
|
+
* @param context - Additional context information
|
|
39
|
+
* @param suggestion - Actionable suggestion for resolving the error
|
|
40
|
+
* @example
|
|
41
|
+
* throw new BaseError('Operation failed', 'ERR_VALIDATION', { field: 'email' }, 'Provide a valid email address')
|
|
42
|
+
*/
|
|
43
|
+
constructor(message, code, context, suggestion) {
|
|
44
|
+
super(message);
|
|
45
|
+
this.name = this.constructor.name;
|
|
46
|
+
this.code = code;
|
|
47
|
+
this.context = context;
|
|
48
|
+
this.suggestion = suggestion;
|
|
49
|
+
// Maintain proper stack trace (V8 only)
|
|
50
|
+
if (Error.captureStackTrace) {
|
|
51
|
+
Error.captureStackTrace(this, this.constructor);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Convert error to JSON for logging
|
|
56
|
+
*
|
|
57
|
+
* @returns JSON representation of the error
|
|
58
|
+
*/
|
|
59
|
+
toJSON() {
|
|
60
|
+
return {
|
|
61
|
+
code: this.code,
|
|
62
|
+
context: this.context,
|
|
63
|
+
message: this.message,
|
|
64
|
+
name: this.name,
|
|
65
|
+
stack: this.stack,
|
|
66
|
+
suggestion: this.suggestion,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Validation error for input validation failures
|
|
72
|
+
*
|
|
73
|
+
* Used when user input or configuration is invalid.
|
|
74
|
+
*/
|
|
75
|
+
export class ValidationError extends BaseError {
|
|
76
|
+
/**
|
|
77
|
+
* Create a new ValidationError
|
|
78
|
+
*
|
|
79
|
+
* @param message - Human-readable error message
|
|
80
|
+
* @param context - Additional context (e.g., field name, validation rule)
|
|
81
|
+
* @param suggestion - Actionable suggestion for resolving the error
|
|
82
|
+
* @example
|
|
83
|
+
* throw new ValidationError('Email is required', { field: 'email', value: '' }, 'Provide a valid email address in the format: user@example.com')
|
|
84
|
+
*/
|
|
85
|
+
constructor(message, context, suggestion) {
|
|
86
|
+
super(message, ERR_VALIDATION, context, suggestion || 'Check that all required fields are provided and have valid values.');
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* File system error for file operation failures
|
|
91
|
+
*
|
|
92
|
+
* Used when file operations (read, write, move) fail.
|
|
93
|
+
*/
|
|
94
|
+
export class FileSystemError extends BaseError {
|
|
95
|
+
/**
|
|
96
|
+
* Create a new FileSystemError
|
|
97
|
+
*
|
|
98
|
+
* @param message - Human-readable error message
|
|
99
|
+
* @param context - Additional context (e.g., file path, operation type)
|
|
100
|
+
* @param suggestion - Actionable suggestion for resolving the error
|
|
101
|
+
* @example
|
|
102
|
+
* throw new FileSystemError('File not found', { filePath: '/path/to/file.md' }, "Check that you're in the project root directory and the path is correct.")
|
|
103
|
+
*/
|
|
104
|
+
constructor(message, context, suggestion) {
|
|
105
|
+
const defaultSuggestion = context?.filePath
|
|
106
|
+
? `Check that you're in the project root directory and the path '${context.filePath}' is correct.`
|
|
107
|
+
: "Check that you're in the project root directory and the path is correct.";
|
|
108
|
+
super(message, ERR_FILE_SYSTEM, context, suggestion || defaultSuggestion);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Agent error for Claude CLI execution failures
|
|
113
|
+
*
|
|
114
|
+
* Used when spawning or executing Claude CLI processes fails.
|
|
115
|
+
*/
|
|
116
|
+
export class AgentError extends BaseError {
|
|
117
|
+
/**
|
|
118
|
+
* Create a new AgentError
|
|
119
|
+
*
|
|
120
|
+
* @param message - Human-readable error message
|
|
121
|
+
* @param context - Additional context (e.g., exit code, stderr, agent type)
|
|
122
|
+
* @param suggestion - Actionable suggestion for resolving the error
|
|
123
|
+
* @example
|
|
124
|
+
* throw new AgentError('Agent execution failed', { exitCode: 1, stderr: 'Error output' }, 'Ensure Claude CLI is installed and accessible in your PATH.')
|
|
125
|
+
*/
|
|
126
|
+
constructor(message, context, suggestion) {
|
|
127
|
+
super(message, ERR_AGENT, context, suggestion || 'Ensure Claude CLI is installed and accessible in your PATH. Run "claude --version" to verify.');
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Configuration error for config validation failures
|
|
132
|
+
*
|
|
133
|
+
* Used when configuration files are invalid or missing required fields.
|
|
134
|
+
*/
|
|
135
|
+
export class ConfigurationError extends BaseError {
|
|
136
|
+
/**
|
|
137
|
+
* Create a new ConfigurationError
|
|
138
|
+
*
|
|
139
|
+
* @param message - Human-readable error message
|
|
140
|
+
* @param context - Additional context (e.g., config field, expected value, actual value)
|
|
141
|
+
* @param suggestion - Actionable suggestion for resolving the error
|
|
142
|
+
* @example
|
|
143
|
+
* throw new ConfigurationError('Invalid field "prdFile"', { field: 'prdFile', expectedType: 'string', actualType: 'number' }, "Field 'prdFile' expects string, got number. Example: 'docs/prd.md'")
|
|
144
|
+
*/
|
|
145
|
+
constructor(message, context, suggestion) {
|
|
146
|
+
let defaultSuggestion = 'Check your .bmad-core/core-config.yaml file for missing or invalid configuration fields.';
|
|
147
|
+
// Build specific suggestion based on context
|
|
148
|
+
if (context?.field && context?.expectedType && context?.actualType) {
|
|
149
|
+
defaultSuggestion = `Field '${context.field}' expects ${context.expectedType}, got ${context.actualType}. Example: ${ConfigurationError.getExampleValue(context.field, context.expectedType)}`;
|
|
150
|
+
}
|
|
151
|
+
else if (context?.field) {
|
|
152
|
+
defaultSuggestion = `Check the '${context.field}' field in your configuration file.`;
|
|
153
|
+
}
|
|
154
|
+
super(message, ERR_CONFIG, context, suggestion || defaultSuggestion);
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Get example value for common config fields
|
|
158
|
+
* @param field - Config field name
|
|
159
|
+
* @param type - Expected type
|
|
160
|
+
* @returns Example value
|
|
161
|
+
*/
|
|
162
|
+
static getExampleValue(field, type) {
|
|
163
|
+
const examples = {
|
|
164
|
+
epicDir: "'docs/epics'",
|
|
165
|
+
prdFile: "'docs/prd.md'",
|
|
166
|
+
prdSharded: 'true',
|
|
167
|
+
storyDir: "'docs/stories'",
|
|
168
|
+
};
|
|
169
|
+
return examples[field] || `<${type} value>`;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Parser error for markdown parsing failures
|
|
174
|
+
*
|
|
175
|
+
* Used when parsing PRD, epic, or story files fails.
|
|
176
|
+
*/
|
|
177
|
+
export class ParserError extends BaseError {
|
|
178
|
+
/**
|
|
179
|
+
* Create a new ParserError
|
|
180
|
+
*
|
|
181
|
+
* @param message - Human-readable error message
|
|
182
|
+
* @param context - Additional context (e.g., file path, line number, problematic line content)
|
|
183
|
+
* @param suggestion - Actionable suggestion for resolving the error
|
|
184
|
+
* @example
|
|
185
|
+
* throw new ParserError('Invalid epic format at line 42', { filePath: 'prd.md', line: 42, lineContent: '## Epic A: Title' }, "Expected format: '## Epic 1: Title'")
|
|
186
|
+
*/
|
|
187
|
+
constructor(message, context, suggestion) {
|
|
188
|
+
let defaultSuggestion = 'Check the file format and ensure it follows the expected markdown structure.';
|
|
189
|
+
// Build specific suggestion based on context
|
|
190
|
+
if (context?.lineContent && context?.line) {
|
|
191
|
+
defaultSuggestion = `Problem at line ${context.line}: "${context.lineContent}". Check the expected format for this section.`;
|
|
192
|
+
}
|
|
193
|
+
else if (context?.line) {
|
|
194
|
+
defaultSuggestion = `Problem at line ${context.line}. Check the expected format for this section.`;
|
|
195
|
+
}
|
|
196
|
+
super(message, ERR_PARSER, context, suggestion || defaultSuggestion);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Handle fatal errors by logging and exiting the process
|
|
201
|
+
*
|
|
202
|
+
* This function should be used for unrecoverable errors that require
|
|
203
|
+
* the CLI to exit immediately. It logs the error with full context
|
|
204
|
+
* and exits with code 1.
|
|
205
|
+
*
|
|
206
|
+
* @param error - The error to handle
|
|
207
|
+
* @param logger - Logger instance for error logging
|
|
208
|
+
* @example
|
|
209
|
+
* try {
|
|
210
|
+
* // critical operation
|
|
211
|
+
* } catch (error) {
|
|
212
|
+
* handleFatalError(error as Error, logger)
|
|
213
|
+
* }
|
|
214
|
+
*/
|
|
215
|
+
export function handleFatalError(error, logger) {
|
|
216
|
+
// Log the error with full context
|
|
217
|
+
if (error instanceof BaseError) {
|
|
218
|
+
logger.fatal({
|
|
219
|
+
context: error.context,
|
|
220
|
+
errorCode: error.code,
|
|
221
|
+
errorMessage: error.message,
|
|
222
|
+
stack: error.stack,
|
|
223
|
+
}, 'Fatal error occurred');
|
|
224
|
+
}
|
|
225
|
+
else {
|
|
226
|
+
logger.fatal({
|
|
227
|
+
errorMessage: error.message,
|
|
228
|
+
stack: error.stack,
|
|
229
|
+
}, 'Fatal error occurred');
|
|
230
|
+
}
|
|
231
|
+
// Exit with error code
|
|
232
|
+
process.exit(1);
|
|
233
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Format data as a table with headers and rows
|
|
3
|
+
*
|
|
4
|
+
* @param headers - Array of column header strings
|
|
5
|
+
* @param rows - Array of row data, where each row is an array of cell values
|
|
6
|
+
* @returns Formatted table string
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* const table = formatTable(
|
|
11
|
+
* ['Name', 'Status'],
|
|
12
|
+
* [['Epic 1', 'Done'], ['Epic 2', 'In Progress']]
|
|
13
|
+
* )
|
|
14
|
+
* console.log(table)
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
export declare const formatTable: (headers: string[], rows: string[][]) => string;
|
|
18
|
+
/**
|
|
19
|
+
* Format an array of items as a list
|
|
20
|
+
*
|
|
21
|
+
* @param items - Array of items to format
|
|
22
|
+
* @param ordered - Whether to use numbered list (default: false)
|
|
23
|
+
* @returns Formatted list string
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```typescript
|
|
27
|
+
* formatList(['Item 1', 'Item 2'], false) // • Item 1\n• Item 2
|
|
28
|
+
* formatList(['Item 1', 'Item 2'], true) // 1. Item 1\n2. Item 2
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export declare const formatList: (items: string[], ordered?: boolean) => string;
|
|
32
|
+
/**
|
|
33
|
+
* Format content in a bordered box with optional title
|
|
34
|
+
*
|
|
35
|
+
* @param title - Title to display at top of box
|
|
36
|
+
* @param content - Content to display inside box
|
|
37
|
+
* @returns Formatted box string
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```typescript
|
|
41
|
+
* const box = formatBox('Summary', 'Total: 5 items\nSuccess: 4\nFailed: 1')
|
|
42
|
+
* console.log(box)
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
export declare const formatBox: (title: string, content: string) => string;
|
|
46
|
+
/**
|
|
47
|
+
* Format seconds as a human-readable countdown string
|
|
48
|
+
*
|
|
49
|
+
* @param seconds - Number of seconds to format
|
|
50
|
+
* @returns Formatted time string (e.g., "2m 30s", "45s", "1h 25m")
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* ```typescript
|
|
54
|
+
* formatCountdown(0) // "0s"
|
|
55
|
+
* formatCountdown(45) // "45s"
|
|
56
|
+
* formatCountdown(90) // "1m 30s"
|
|
57
|
+
* formatCountdown(3600) // "1h 0m"
|
|
58
|
+
* formatCountdown(3665) // "1h 1m"
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
export declare const formatCountdown: (seconds: number) => string;
|
|
62
|
+
/**
|
|
63
|
+
* Format a duration in milliseconds as a human-readable string
|
|
64
|
+
*
|
|
65
|
+
* @param milliseconds - Duration in milliseconds
|
|
66
|
+
* @returns Formatted duration string
|
|
67
|
+
*/
|
|
68
|
+
export declare const formatDuration: (milliseconds: number) => string;
|
|
69
|
+
/**
|
|
70
|
+
* Format bytes as a human-readable string
|
|
71
|
+
*
|
|
72
|
+
* @param bytes - Number of bytes to format
|
|
73
|
+
* @param decimals - Number of decimal places (default: 2)
|
|
74
|
+
* @returns Formatted byte string (e.g., "1.50 MB", "256.00 KB")
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* ```typescript
|
|
78
|
+
* formatBytes(1024) // "1.00 KB"
|
|
79
|
+
* formatBytes(1048576) // "1.00 MB"
|
|
80
|
+
* formatBytes(1536, 0) // "2 KB"
|
|
81
|
+
* formatBytes(1536000, 2) // "1.46 MB"
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
84
|
+
export declare const formatBytes: (bytes: number, decimals?: number) => string;
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Format data as a table with headers and rows
|
|
3
|
+
*
|
|
4
|
+
* @param headers - Array of column header strings
|
|
5
|
+
* @param rows - Array of row data, where each row is an array of cell values
|
|
6
|
+
* @returns Formatted table string
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* const table = formatTable(
|
|
11
|
+
* ['Name', 'Status'],
|
|
12
|
+
* [['Epic 1', 'Done'], ['Epic 2', 'In Progress']]
|
|
13
|
+
* )
|
|
14
|
+
* console.log(table)
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
export const formatTable = (headers, rows) => {
|
|
18
|
+
if (headers.length === 0) {
|
|
19
|
+
return '';
|
|
20
|
+
}
|
|
21
|
+
// Calculate column widths
|
|
22
|
+
const columnWidths = headers.map((header, i) => {
|
|
23
|
+
const maxRowWidth = Math.max(...rows.map((row) => (row[i] || '').length));
|
|
24
|
+
return Math.max(header.length, maxRowWidth);
|
|
25
|
+
});
|
|
26
|
+
// Create separator line
|
|
27
|
+
const separator = columnWidths.map((width) => '─'.repeat(width + 2)).join('┼');
|
|
28
|
+
// Format header
|
|
29
|
+
const headerRow = headers.map((header, i) => ` ${header.padEnd(columnWidths[i])} `).join('│');
|
|
30
|
+
// Format rows
|
|
31
|
+
const formattedRows = rows.map((row) => row.map((cell, i) => ` ${(cell || '').padEnd(columnWidths[i])} `).join('│'));
|
|
32
|
+
// Assemble table
|
|
33
|
+
return [
|
|
34
|
+
'┌' + separator.replaceAll('┼', '┬') + '┐',
|
|
35
|
+
'│' + headerRow + '│',
|
|
36
|
+
'├' + separator + '┤',
|
|
37
|
+
...formattedRows.map((row) => '│' + row + '│'),
|
|
38
|
+
'└' + separator.replaceAll('┼', '┴') + '┘',
|
|
39
|
+
].join('\n');
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Format an array of items as a list
|
|
43
|
+
*
|
|
44
|
+
* @param items - Array of items to format
|
|
45
|
+
* @param ordered - Whether to use numbered list (default: false)
|
|
46
|
+
* @returns Formatted list string
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* ```typescript
|
|
50
|
+
* formatList(['Item 1', 'Item 2'], false) // • Item 1\n• Item 2
|
|
51
|
+
* formatList(['Item 1', 'Item 2'], true) // 1. Item 1\n2. Item 2
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
export const formatList = (items, ordered = false) => {
|
|
55
|
+
if (items.length === 0) {
|
|
56
|
+
return '';
|
|
57
|
+
}
|
|
58
|
+
return items
|
|
59
|
+
.map((item, index) => {
|
|
60
|
+
const prefix = ordered ? `${index + 1}. ` : '• ';
|
|
61
|
+
return `${prefix}${item}`;
|
|
62
|
+
})
|
|
63
|
+
.join('\n');
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* Format content in a bordered box with optional title
|
|
67
|
+
*
|
|
68
|
+
* @param title - Title to display at top of box
|
|
69
|
+
* @param content - Content to display inside box
|
|
70
|
+
* @returns Formatted box string
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* ```typescript
|
|
74
|
+
* const box = formatBox('Summary', 'Total: 5 items\nSuccess: 4\nFailed: 1')
|
|
75
|
+
* console.log(box)
|
|
76
|
+
* ```
|
|
77
|
+
*/
|
|
78
|
+
export const formatBox = (title, content) => {
|
|
79
|
+
const lines = content.split('\n');
|
|
80
|
+
const maxLength = Math.max(title.length + 2, ...lines.map((line) => line.length));
|
|
81
|
+
const topBorder = '┏' + '━'.repeat(maxLength + 2) + '┓';
|
|
82
|
+
const titleLine = title ? `┃ ${title.padEnd(maxLength)} ┃` : '';
|
|
83
|
+
const separator = title ? '┣' + '━'.repeat(maxLength + 2) + '┫' : '';
|
|
84
|
+
const contentLines = lines.map((line) => `┃ ${line.padEnd(maxLength)} ┃`);
|
|
85
|
+
const bottomBorder = '┗' + '━'.repeat(maxLength + 2) + '┛';
|
|
86
|
+
const parts = [topBorder];
|
|
87
|
+
if (titleLine)
|
|
88
|
+
parts.push(titleLine);
|
|
89
|
+
if (separator)
|
|
90
|
+
parts.push(separator);
|
|
91
|
+
parts.push(...contentLines, bottomBorder);
|
|
92
|
+
return parts.join('\n');
|
|
93
|
+
};
|
|
94
|
+
/**
|
|
95
|
+
* Format seconds as a human-readable countdown string
|
|
96
|
+
*
|
|
97
|
+
* @param seconds - Number of seconds to format
|
|
98
|
+
* @returns Formatted time string (e.g., "2m 30s", "45s", "1h 25m")
|
|
99
|
+
*
|
|
100
|
+
* @example
|
|
101
|
+
* ```typescript
|
|
102
|
+
* formatCountdown(0) // "0s"
|
|
103
|
+
* formatCountdown(45) // "45s"
|
|
104
|
+
* formatCountdown(90) // "1m 30s"
|
|
105
|
+
* formatCountdown(3600) // "1h 0m"
|
|
106
|
+
* formatCountdown(3665) // "1h 1m"
|
|
107
|
+
* ```
|
|
108
|
+
*/
|
|
109
|
+
export const formatCountdown = (seconds) => {
|
|
110
|
+
if (seconds < 0) {
|
|
111
|
+
return '0s';
|
|
112
|
+
}
|
|
113
|
+
if (seconds === 0) {
|
|
114
|
+
return '0s';
|
|
115
|
+
}
|
|
116
|
+
const hours = Math.floor(seconds / 3600);
|
|
117
|
+
const minutes = Math.floor((seconds % 3600) / 60);
|
|
118
|
+
const secs = seconds % 60;
|
|
119
|
+
if (hours > 0) {
|
|
120
|
+
return `${hours}h ${minutes}m`;
|
|
121
|
+
}
|
|
122
|
+
if (minutes > 0) {
|
|
123
|
+
return `${minutes}m ${secs}s`;
|
|
124
|
+
}
|
|
125
|
+
return `${secs}s`;
|
|
126
|
+
};
|
|
127
|
+
/**
|
|
128
|
+
* Format a duration in milliseconds as a human-readable string
|
|
129
|
+
*
|
|
130
|
+
* @param milliseconds - Duration in milliseconds
|
|
131
|
+
* @returns Formatted duration string
|
|
132
|
+
*/
|
|
133
|
+
export const formatDuration = (milliseconds) => {
|
|
134
|
+
const seconds = Math.floor(milliseconds / 1000);
|
|
135
|
+
return formatCountdown(seconds);
|
|
136
|
+
};
|
|
137
|
+
/**
|
|
138
|
+
* Format bytes as a human-readable string
|
|
139
|
+
*
|
|
140
|
+
* @param bytes - Number of bytes to format
|
|
141
|
+
* @param decimals - Number of decimal places (default: 2)
|
|
142
|
+
* @returns Formatted byte string (e.g., "1.50 MB", "256.00 KB")
|
|
143
|
+
*
|
|
144
|
+
* @example
|
|
145
|
+
* ```typescript
|
|
146
|
+
* formatBytes(1024) // "1.00 KB"
|
|
147
|
+
* formatBytes(1048576) // "1.00 MB"
|
|
148
|
+
* formatBytes(1536, 0) // "2 KB"
|
|
149
|
+
* formatBytes(1536000, 2) // "1.46 MB"
|
|
150
|
+
* ```
|
|
151
|
+
*/
|
|
152
|
+
export const formatBytes = (bytes, decimals = 2) => {
|
|
153
|
+
if (bytes === 0) {
|
|
154
|
+
return '0 Bytes';
|
|
155
|
+
}
|
|
156
|
+
const k = 1024;
|
|
157
|
+
const dm = Math.max(decimals, 0);
|
|
158
|
+
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB'];
|
|
159
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
160
|
+
const value = bytes / k ** i;
|
|
161
|
+
return `${value.toFixed(dm)} ${sizes[i]}`;
|
|
162
|
+
};
|