@hyperdrive.bot/bmad-workflow 1.0.17 → 1.0.19
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/dist/commands/config/show.js +8 -2
- package/dist/commands/decompose.js +26 -5
- package/dist/commands/epics/create.d.ts +1 -0
- package/dist/commands/mcp/add.d.ts +16 -0
- package/dist/commands/mcp/add.js +77 -0
- package/dist/commands/mcp/credential/get.d.ts +14 -0
- package/dist/commands/mcp/credential/get.js +35 -0
- package/dist/commands/mcp/credential/list.d.ts +17 -0
- package/dist/commands/mcp/credential/list.js +67 -0
- package/dist/commands/mcp/credential/remove.d.ts +18 -0
- package/dist/commands/mcp/credential/remove.js +84 -0
- package/dist/commands/mcp/credential/set.d.ts +16 -0
- package/dist/commands/mcp/credential/set.js +41 -0
- package/dist/commands/mcp/credential/validate.d.ts +12 -0
- package/dist/commands/mcp/credential/validate.js +150 -0
- package/dist/commands/mcp/list.d.ts +17 -0
- package/dist/commands/mcp/list.js +80 -0
- package/dist/commands/mcp/logs.d.ts +15 -0
- package/dist/commands/mcp/logs.js +64 -0
- package/dist/commands/mcp/preset.d.ts +15 -0
- package/dist/commands/mcp/preset.js +84 -0
- package/dist/commands/mcp/remove.d.ts +14 -0
- package/dist/commands/mcp/remove.js +36 -0
- package/dist/commands/mcp/start.d.ts +12 -0
- package/dist/commands/mcp/start.js +80 -0
- package/dist/commands/mcp/status.d.ts +30 -0
- package/dist/commands/mcp/status.js +180 -0
- package/dist/commands/mcp/stop.d.ts +12 -0
- package/dist/commands/mcp/stop.js +47 -0
- package/dist/commands/stories/create.d.ts +1 -0
- package/dist/commands/stories/develop.d.ts +1 -0
- package/dist/commands/stories/qa.js +34 -75
- package/dist/commands/stories/review.d.ts +124 -0
- package/dist/commands/stories/review.js +516 -0
- package/dist/commands/workflow.d.ts +89 -0
- package/dist/commands/workflow.js +487 -14
- package/dist/mcp/types.d.ts +99 -0
- package/dist/mcp/types.js +7 -0
- package/dist/mcp/utils/docker-utils.d.ts +56 -0
- package/dist/mcp/utils/docker-utils.js +108 -0
- package/dist/mcp/utils/template-loader.d.ts +21 -0
- package/dist/mcp/utils/template-loader.js +60 -0
- package/dist/models/agent-options.d.ts +10 -1
- package/dist/models/index.d.ts +1 -0
- package/dist/models/index.js +1 -0
- package/dist/models/workflow-callbacks.d.ts +251 -0
- package/dist/models/workflow-callbacks.js +10 -0
- package/dist/models/workflow-config.d.ts +77 -0
- package/dist/models/workflow-result.d.ts +7 -0
- package/dist/services/WorkflowReporter.d.ts +165 -0
- package/dist/services/WorkflowReporter.js +691 -0
- package/dist/services/agents/claude-agent-runner.js +25 -4
- package/dist/services/file-system/path-resolver.d.ts +10 -0
- package/dist/services/file-system/path-resolver.js +12 -0
- package/dist/services/mcp/mcp-config-manager.d.ts +54 -0
- package/dist/services/mcp/mcp-config-manager.js +146 -0
- package/dist/services/mcp/mcp-context-injector.d.ts +92 -0
- package/dist/services/mcp/mcp-context-injector.js +168 -0
- package/dist/services/mcp/mcp-credential-manager.d.ts +48 -0
- package/dist/services/mcp/mcp-credential-manager.js +124 -0
- package/dist/services/mcp/mcp-health-checker.d.ts +56 -0
- package/dist/services/mcp/mcp-health-checker.js +162 -0
- package/dist/services/mcp/types/health-types.d.ts +31 -0
- package/dist/services/mcp/types/health-types.js +7 -0
- package/dist/services/orchestration/dependency-graph-executor.js +1 -1
- package/dist/services/orchestration/task-decomposition-service.d.ts +2 -1
- package/dist/services/orchestration/task-decomposition-service.js +90 -36
- package/dist/services/orchestration/workflow-orchestrator.d.ts +87 -3
- package/dist/services/orchestration/workflow-orchestrator.js +1169 -289
- package/dist/services/review/ai-review-scanner.d.ts +66 -0
- package/dist/services/review/ai-review-scanner.js +142 -0
- package/dist/services/review/coderabbit-scanner.d.ts +25 -0
- package/dist/services/review/coderabbit-scanner.js +31 -0
- package/dist/services/review/index.d.ts +20 -0
- package/dist/services/review/index.js +15 -0
- package/dist/services/review/lint-scanner.d.ts +46 -0
- package/dist/services/review/lint-scanner.js +172 -0
- package/dist/services/review/review-config.d.ts +62 -0
- package/dist/services/review/review-config.js +91 -0
- package/dist/services/review/review-phase-executor.d.ts +69 -0
- package/dist/services/review/review-phase-executor.js +152 -0
- package/dist/services/review/review-queue.d.ts +98 -0
- package/dist/services/review/review-queue.js +174 -0
- package/dist/services/review/review-reporter.d.ts +94 -0
- package/dist/services/review/review-reporter.js +386 -0
- package/dist/services/review/scanner-factory.d.ts +42 -0
- package/dist/services/review/scanner-factory.js +60 -0
- package/dist/services/review/self-heal-loop.d.ts +58 -0
- package/dist/services/review/self-heal-loop.js +132 -0
- package/dist/services/review/severity-classifier.d.ts +17 -0
- package/dist/services/review/severity-classifier.js +314 -0
- package/dist/services/review/tech-debt-tracker.d.ts +52 -0
- package/dist/services/review/tech-debt-tracker.js +245 -0
- package/dist/services/review/types.d.ts +93 -0
- package/dist/services/review/types.js +23 -0
- package/dist/services/scaffolding/workflow-session-scaffolder.d.ts +182 -0
- package/dist/services/scaffolding/workflow-session-scaffolder.js +236 -0
- package/dist/services/validation/config-validator.d.ts +84 -0
- package/dist/services/validation/config-validator.js +78 -0
- package/dist/utils/colors.d.ts +10 -10
- package/dist/utils/colors.js +15 -15
- package/dist/utils/credential-utils.d.ts +14 -0
- package/dist/utils/credential-utils.js +19 -0
- package/dist/utils/duration.d.ts +41 -0
- package/dist/utils/duration.js +89 -0
- package/dist/utils/listr2-helpers.d.ts +216 -0
- package/dist/utils/listr2-helpers.js +334 -0
- package/dist/utils/shared-flags.d.ts +1 -0
- package/dist/utils/shared-flags.js +11 -2
- package/package.json +6 -3
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Review Types
|
|
3
|
+
*
|
|
4
|
+
* Core type definitions for the automated code review system.
|
|
5
|
+
* Scanners return raw output; classification into severity levels
|
|
6
|
+
* is handled separately by SeverityClassifier (Story 1.2).
|
|
7
|
+
*
|
|
8
|
+
* Severity model maps to BMAD governance:
|
|
9
|
+
* CRITICAL = NON-NEGOTIABLE (blocks pipeline)
|
|
10
|
+
* HIGH = MUST (blocks pipeline)
|
|
11
|
+
* MEDIUM = SHOULD (documented as tech debt)
|
|
12
|
+
* LOW = MAY (noted only)
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* Issue severity levels aligned with BMAD governance tiers
|
|
16
|
+
*/
|
|
17
|
+
export declare enum Severity {
|
|
18
|
+
CRITICAL = "CRITICAL",
|
|
19
|
+
HIGH = "HIGH",
|
|
20
|
+
LOW = "LOW",
|
|
21
|
+
MEDIUM = "MEDIUM"
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Review verdict — PASS allows pipeline to continue, FAIL blocks it
|
|
25
|
+
*/
|
|
26
|
+
export type ReviewVerdict = 'FAIL' | 'PASS';
|
|
27
|
+
/**
|
|
28
|
+
* Context provided to scanners describing what to review
|
|
29
|
+
*/
|
|
30
|
+
export interface ReviewContext {
|
|
31
|
+
/** Base branch to diff against (e.g., "main", "develop") */
|
|
32
|
+
baseBranch: string;
|
|
33
|
+
/** List of changed file paths (relative to projectRoot) */
|
|
34
|
+
changedFiles: string[];
|
|
35
|
+
/** Absolute path to the project root directory */
|
|
36
|
+
projectRoot: string;
|
|
37
|
+
/** List of reference file paths for context (e.g., architecture docs) */
|
|
38
|
+
referenceFiles: string[];
|
|
39
|
+
/** Absolute path to the story markdown file */
|
|
40
|
+
storyFile: string;
|
|
41
|
+
/** Story identifier (e.g., "PROJ-story-1.001") */
|
|
42
|
+
storyId: string;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Raw output from a scanner before classification
|
|
46
|
+
*/
|
|
47
|
+
export interface RawReviewOutput {
|
|
48
|
+
/** Raw scanner output (lint text, AI response, etc.) */
|
|
49
|
+
raw: string;
|
|
50
|
+
/** Identifier of the scanner that produced this output (e.g., "eslint", "claude-ai", "coderabbit") */
|
|
51
|
+
source: string;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* A single classified issue with severity and location
|
|
55
|
+
*/
|
|
56
|
+
export interface ClassifiedIssue {
|
|
57
|
+
/** File path where the issue was found */
|
|
58
|
+
file: string;
|
|
59
|
+
/** Suggested fix (optional) */
|
|
60
|
+
fix?: string;
|
|
61
|
+
/** Description of the issue */
|
|
62
|
+
issue: string;
|
|
63
|
+
/** Line number where the issue occurs */
|
|
64
|
+
line: number;
|
|
65
|
+
/** Severity level of the issue */
|
|
66
|
+
severity: Severity;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Final review result after classification and optional self-heal iterations
|
|
70
|
+
*/
|
|
71
|
+
export interface ReviewResult {
|
|
72
|
+
/** All classified issues found during review */
|
|
73
|
+
issues: ClassifiedIssue[];
|
|
74
|
+
/** Number of self-heal iterations performed (0 if no retries) */
|
|
75
|
+
iterations: number;
|
|
76
|
+
/** Optional summary message */
|
|
77
|
+
message?: string;
|
|
78
|
+
/** Overall verdict — PASS or FAIL */
|
|
79
|
+
verdict: ReviewVerdict;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Scanner interface — all scanner implementations (lint, AI, CodeRabbit) must implement this.
|
|
83
|
+
* Mirrors the AIProviderRunner pattern: single async method with typed input/output.
|
|
84
|
+
*/
|
|
85
|
+
export interface ReviewScanner {
|
|
86
|
+
/**
|
|
87
|
+
* Scan the given context and produce raw review output
|
|
88
|
+
*
|
|
89
|
+
* @param context - Review context describing what to scan
|
|
90
|
+
* @returns Raw output from the scanner
|
|
91
|
+
*/
|
|
92
|
+
scan(context: ReviewContext): Promise<RawReviewOutput>;
|
|
93
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Review Types
|
|
3
|
+
*
|
|
4
|
+
* Core type definitions for the automated code review system.
|
|
5
|
+
* Scanners return raw output; classification into severity levels
|
|
6
|
+
* is handled separately by SeverityClassifier (Story 1.2).
|
|
7
|
+
*
|
|
8
|
+
* Severity model maps to BMAD governance:
|
|
9
|
+
* CRITICAL = NON-NEGOTIABLE (blocks pipeline)
|
|
10
|
+
* HIGH = MUST (blocks pipeline)
|
|
11
|
+
* MEDIUM = SHOULD (documented as tech debt)
|
|
12
|
+
* LOW = MAY (noted only)
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* Issue severity levels aligned with BMAD governance tiers
|
|
16
|
+
*/
|
|
17
|
+
export var Severity;
|
|
18
|
+
(function (Severity) {
|
|
19
|
+
Severity["CRITICAL"] = "CRITICAL";
|
|
20
|
+
Severity["HIGH"] = "HIGH";
|
|
21
|
+
Severity["LOW"] = "LOW";
|
|
22
|
+
Severity["MEDIUM"] = "MEDIUM";
|
|
23
|
+
})(Severity || (Severity = {}));
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WorkflowSessionScaffolder
|
|
3
|
+
*
|
|
4
|
+
* Creates structured session directories with spawn output files and summary reports.
|
|
5
|
+
* Provides persistent, browsable artifacts for troubleshooting and audit trails
|
|
6
|
+
* after each workflow run.
|
|
7
|
+
*/
|
|
8
|
+
import type pino from 'pino';
|
|
9
|
+
import type { FileManager } from '../file-system/file-manager.js';
|
|
10
|
+
import type { WorkflowLogFile } from '../logging/workflow-logger.js';
|
|
11
|
+
/**
|
|
12
|
+
* Configuration for creating a workflow session
|
|
13
|
+
*/
|
|
14
|
+
export interface WorkflowSessionConfig {
|
|
15
|
+
/** Base directory for workflow sessions (e.g., 'docs/workflow-sessions') */
|
|
16
|
+
baseDir: string;
|
|
17
|
+
/** Session prefix (e.g., 'workflow', 'bmad-run') */
|
|
18
|
+
prefix: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Spawn output payload structure
|
|
22
|
+
*/
|
|
23
|
+
export interface SpawnOutputPayload {
|
|
24
|
+
/** Timestamp when spawn completed */
|
|
25
|
+
completedAt?: string;
|
|
26
|
+
/** Spawn output content */
|
|
27
|
+
content: string;
|
|
28
|
+
/** Spawn execution duration in milliseconds */
|
|
29
|
+
duration?: number;
|
|
30
|
+
/** Any errors from the spawn */
|
|
31
|
+
errors?: string;
|
|
32
|
+
/** Unique spawn identifier */
|
|
33
|
+
spawnId: string;
|
|
34
|
+
/** Whether the spawn succeeded */
|
|
35
|
+
success?: boolean;
|
|
36
|
+
/** Type of spawn: epic, story, or dev */
|
|
37
|
+
type: 'dev' | 'epic' | 'story';
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Session report summary structure
|
|
41
|
+
*/
|
|
42
|
+
export interface SessionReportSummary {
|
|
43
|
+
/** Total duration in milliseconds */
|
|
44
|
+
duration: number;
|
|
45
|
+
/** Session end timestamp */
|
|
46
|
+
endedAt: string;
|
|
47
|
+
/** List of errors encountered */
|
|
48
|
+
errors: Array<{
|
|
49
|
+
error: string;
|
|
50
|
+
identifier: string;
|
|
51
|
+
phase: string;
|
|
52
|
+
timestamp: string;
|
|
53
|
+
}>;
|
|
54
|
+
/** Overall success status */
|
|
55
|
+
overallSuccess: boolean;
|
|
56
|
+
/** Phase execution counts */
|
|
57
|
+
phases: {
|
|
58
|
+
developments: {
|
|
59
|
+
completed: number;
|
|
60
|
+
failed: number;
|
|
61
|
+
total: number;
|
|
62
|
+
};
|
|
63
|
+
epics: {
|
|
64
|
+
completed: number;
|
|
65
|
+
failed: number;
|
|
66
|
+
total: number;
|
|
67
|
+
};
|
|
68
|
+
stories: {
|
|
69
|
+
completed: number;
|
|
70
|
+
failed: number;
|
|
71
|
+
total: number;
|
|
72
|
+
};
|
|
73
|
+
};
|
|
74
|
+
/** Session identifier */
|
|
75
|
+
sessionId: string;
|
|
76
|
+
/** List of spawn files written */
|
|
77
|
+
spawnFiles: string[];
|
|
78
|
+
/** Session start timestamp */
|
|
79
|
+
startedAt: string;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Scaffolder for workflow session directory structure
|
|
83
|
+
*/
|
|
84
|
+
export declare class WorkflowSessionScaffolder {
|
|
85
|
+
private readonly fileManager;
|
|
86
|
+
private readonly logger;
|
|
87
|
+
private sessionDir;
|
|
88
|
+
constructor(fileManager: FileManager, logger: pino.Logger);
|
|
89
|
+
/**
|
|
90
|
+
* Create the session directory structure
|
|
91
|
+
*
|
|
92
|
+
* Creates:
|
|
93
|
+
* - Session root directory at `{baseDir}/{prefix}-{timestamp}/`
|
|
94
|
+
* - `spawns/` subdirectory for individual spawn output files
|
|
95
|
+
*
|
|
96
|
+
* @param config - Session configuration
|
|
97
|
+
* @returns Session directory path
|
|
98
|
+
*/
|
|
99
|
+
createSessionStructure(config: WorkflowSessionConfig): Promise<string>;
|
|
100
|
+
/**
|
|
101
|
+
* Get the current session directory path
|
|
102
|
+
*
|
|
103
|
+
* @returns Session directory path or null if not created
|
|
104
|
+
*/
|
|
105
|
+
getSessionDir(): null | string;
|
|
106
|
+
/**
|
|
107
|
+
* Write session report markdown file
|
|
108
|
+
*
|
|
109
|
+
* Generates SESSION_REPORT.md with execution summary, pass/fail counts,
|
|
110
|
+
* durations, and error listings.
|
|
111
|
+
*
|
|
112
|
+
* @param summary - Session execution summary
|
|
113
|
+
*/
|
|
114
|
+
writeSessionReport(summary: SessionReportSummary): Promise<void>;
|
|
115
|
+
/**
|
|
116
|
+
* Write individual spawn output file
|
|
117
|
+
*
|
|
118
|
+
* Writes immediately (no buffering) to ensure partial results survive crashes.
|
|
119
|
+
*
|
|
120
|
+
* @param spawnId - Unique spawn identifier
|
|
121
|
+
* @param type - Type of spawn (epic, story, dev)
|
|
122
|
+
* @param content - Spawn output content
|
|
123
|
+
*/
|
|
124
|
+
writeSpawnOutput(spawnId: string, type: 'dev' | 'epic' | 'story', content: string): Promise<void>;
|
|
125
|
+
/**
|
|
126
|
+
* Write individual spawn output file with full payload
|
|
127
|
+
*
|
|
128
|
+
* Writes immediately (no buffering) to ensure partial results survive crashes.
|
|
129
|
+
*
|
|
130
|
+
* @param payload - Full spawn output payload
|
|
131
|
+
*/
|
|
132
|
+
writeSpawnOutput(payload: SpawnOutputPayload): Promise<void>;
|
|
133
|
+
/**
|
|
134
|
+
* Write workflow log YAML file
|
|
135
|
+
*
|
|
136
|
+
* Generates workflow-log.yaml machine-parseable audit trail file.
|
|
137
|
+
*
|
|
138
|
+
* @param logData - Full workflow log data (WorkflowLogFile interface)
|
|
139
|
+
*/
|
|
140
|
+
writeWorkflowLog(logData: WorkflowLogFile): Promise<void>;
|
|
141
|
+
/**
|
|
142
|
+
* Build markdown content for session report
|
|
143
|
+
*
|
|
144
|
+
* @param summary - Session report summary
|
|
145
|
+
* @returns Formatted markdown content
|
|
146
|
+
* @private
|
|
147
|
+
*/
|
|
148
|
+
private buildSessionReportMarkdown;
|
|
149
|
+
/**
|
|
150
|
+
* Build markdown content for spawn output file
|
|
151
|
+
*
|
|
152
|
+
* @param payload - Spawn output payload
|
|
153
|
+
* @returns Formatted markdown content
|
|
154
|
+
* @private
|
|
155
|
+
*/
|
|
156
|
+
private buildSpawnMarkdown;
|
|
157
|
+
/**
|
|
158
|
+
* Capitalize first letter of a string
|
|
159
|
+
*
|
|
160
|
+
* @param str - String to capitalize
|
|
161
|
+
* @returns Capitalized string
|
|
162
|
+
* @private
|
|
163
|
+
*/
|
|
164
|
+
private capitalizeFirst;
|
|
165
|
+
/**
|
|
166
|
+
* Format duration in human-readable format
|
|
167
|
+
*
|
|
168
|
+
* @param ms - Duration in milliseconds
|
|
169
|
+
* @returns Formatted duration string
|
|
170
|
+
* @private
|
|
171
|
+
*/
|
|
172
|
+
private formatDuration;
|
|
173
|
+
/**
|
|
174
|
+
* Generate timestamp string for session directory name
|
|
175
|
+
*
|
|
176
|
+
* Format: YYYYMMDD-HHmmss
|
|
177
|
+
*
|
|
178
|
+
* @returns Formatted timestamp
|
|
179
|
+
* @private
|
|
180
|
+
*/
|
|
181
|
+
private generateTimestamp;
|
|
182
|
+
}
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WorkflowSessionScaffolder
|
|
3
|
+
*
|
|
4
|
+
* Creates structured session directories with spawn output files and summary reports.
|
|
5
|
+
* Provides persistent, browsable artifacts for troubleshooting and audit trails
|
|
6
|
+
* after each workflow run.
|
|
7
|
+
*/
|
|
8
|
+
import * as yaml from 'js-yaml';
|
|
9
|
+
import { join } from 'node:path';
|
|
10
|
+
/**
|
|
11
|
+
* Scaffolder for workflow session directory structure
|
|
12
|
+
*/
|
|
13
|
+
export class WorkflowSessionScaffolder {
|
|
14
|
+
fileManager;
|
|
15
|
+
logger;
|
|
16
|
+
sessionDir = null;
|
|
17
|
+
constructor(fileManager, logger) {
|
|
18
|
+
this.fileManager = fileManager;
|
|
19
|
+
this.logger = logger;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Create the session directory structure
|
|
23
|
+
*
|
|
24
|
+
* Creates:
|
|
25
|
+
* - Session root directory at `{baseDir}/{prefix}-{timestamp}/`
|
|
26
|
+
* - `spawns/` subdirectory for individual spawn output files
|
|
27
|
+
*
|
|
28
|
+
* @param config - Session configuration
|
|
29
|
+
* @returns Session directory path
|
|
30
|
+
*/
|
|
31
|
+
async createSessionStructure(config) {
|
|
32
|
+
const timestamp = this.generateTimestamp();
|
|
33
|
+
const sessionDirName = `${config.prefix}-${timestamp}`;
|
|
34
|
+
const sessionDir = join(config.baseDir, sessionDirName);
|
|
35
|
+
this.logger.info({ prefix: config.prefix, sessionDir }, 'Creating workflow session structure');
|
|
36
|
+
// Create root session directory
|
|
37
|
+
await this.fileManager.createDirectory(sessionDir);
|
|
38
|
+
// Create spawns subdirectory
|
|
39
|
+
await this.fileManager.createDirectory(join(sessionDir, 'spawns'));
|
|
40
|
+
this.sessionDir = sessionDir;
|
|
41
|
+
this.logger.info({ sessionDir }, 'Workflow session structure created');
|
|
42
|
+
return sessionDir;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Get the current session directory path
|
|
46
|
+
*
|
|
47
|
+
* @returns Session directory path or null if not created
|
|
48
|
+
*/
|
|
49
|
+
getSessionDir() {
|
|
50
|
+
return this.sessionDir;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Write session report markdown file
|
|
54
|
+
*
|
|
55
|
+
* Generates SESSION_REPORT.md with execution summary, pass/fail counts,
|
|
56
|
+
* durations, and error listings.
|
|
57
|
+
*
|
|
58
|
+
* @param summary - Session execution summary
|
|
59
|
+
*/
|
|
60
|
+
async writeSessionReport(summary) {
|
|
61
|
+
if (!this.sessionDir) {
|
|
62
|
+
throw new Error('Session structure must be created before writing session report');
|
|
63
|
+
}
|
|
64
|
+
const reportPath = join(this.sessionDir, 'SESSION_REPORT.md');
|
|
65
|
+
this.logger.info({ reportPath, sessionId: summary.sessionId }, 'Writing session report');
|
|
66
|
+
const markdownContent = this.buildSessionReportMarkdown(summary);
|
|
67
|
+
await this.fileManager.writeFile(reportPath, markdownContent);
|
|
68
|
+
this.logger.info({ reportPath }, 'Session report written');
|
|
69
|
+
}
|
|
70
|
+
async writeSpawnOutput(spawnIdOrPayload, type, content) {
|
|
71
|
+
if (!this.sessionDir) {
|
|
72
|
+
throw new Error('Session structure must be created before writing spawn output');
|
|
73
|
+
}
|
|
74
|
+
// Normalize to payload
|
|
75
|
+
const payload = typeof spawnIdOrPayload === 'string'
|
|
76
|
+
? {
|
|
77
|
+
content: content,
|
|
78
|
+
spawnId: spawnIdOrPayload,
|
|
79
|
+
type: type,
|
|
80
|
+
}
|
|
81
|
+
: spawnIdOrPayload;
|
|
82
|
+
const fileName = `${payload.type}-${payload.spawnId}.md`;
|
|
83
|
+
const filePath = join(this.sessionDir, 'spawns', fileName);
|
|
84
|
+
this.logger.info({ filePath, spawnId: payload.spawnId, type: payload.type }, 'Writing spawn output');
|
|
85
|
+
// Build markdown content with metadata header
|
|
86
|
+
const markdownContent = this.buildSpawnMarkdown(payload);
|
|
87
|
+
// Immediate write for crash resilience
|
|
88
|
+
await this.fileManager.writeFile(filePath, markdownContent);
|
|
89
|
+
this.logger.info({ filePath, spawnId: payload.spawnId }, 'Spawn output written');
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Write workflow log YAML file
|
|
93
|
+
*
|
|
94
|
+
* Generates workflow-log.yaml machine-parseable audit trail file.
|
|
95
|
+
*
|
|
96
|
+
* @param logData - Full workflow log data (WorkflowLogFile interface)
|
|
97
|
+
*/
|
|
98
|
+
async writeWorkflowLog(logData) {
|
|
99
|
+
if (!this.sessionDir) {
|
|
100
|
+
throw new Error('Session structure must be created before writing workflow log');
|
|
101
|
+
}
|
|
102
|
+
const logPath = join(this.sessionDir, 'workflow-log.yaml');
|
|
103
|
+
this.logger.info({ logPath, workflowId: logData.metadata.workflowId }, 'Writing workflow log');
|
|
104
|
+
const yamlContent = yaml.dump(logData, {
|
|
105
|
+
indent: 2,
|
|
106
|
+
lineWidth: 120,
|
|
107
|
+
noRefs: true,
|
|
108
|
+
});
|
|
109
|
+
await this.fileManager.writeFile(logPath, yamlContent);
|
|
110
|
+
this.logger.info({ logPath }, 'Workflow log written');
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Build markdown content for session report
|
|
114
|
+
*
|
|
115
|
+
* @param summary - Session report summary
|
|
116
|
+
* @returns Formatted markdown content
|
|
117
|
+
* @private
|
|
118
|
+
*/
|
|
119
|
+
buildSessionReportMarkdown(summary) {
|
|
120
|
+
const statusIcon = summary.overallSuccess ? '✅' : '❌';
|
|
121
|
+
const durationStr = this.formatDuration(summary.duration);
|
|
122
|
+
let md = `# Workflow Session Report\n\n`;
|
|
123
|
+
// Metadata section
|
|
124
|
+
md += `## Metadata\n\n`;
|
|
125
|
+
md += `- **Session ID:** ${summary.sessionId}\n`;
|
|
126
|
+
md += `- **Status:** ${statusIcon} ${summary.overallSuccess ? 'Completed Successfully' : 'Completed with Failures'}\n`;
|
|
127
|
+
md += `- **Started:** ${summary.startedAt}\n`;
|
|
128
|
+
md += `- **Ended:** ${summary.endedAt}\n`;
|
|
129
|
+
md += `- **Duration:** ${durationStr}\n\n`;
|
|
130
|
+
// Execution summary table
|
|
131
|
+
md += `## Execution Summary\n\n`;
|
|
132
|
+
md += `| Phase | Total | Completed | Failed | Pass Rate |\n`;
|
|
133
|
+
md += `|-------|-------|-----------|--------|------------|\n`;
|
|
134
|
+
for (const [phase, counts] of Object.entries(summary.phases)) {
|
|
135
|
+
const passRate = counts.total > 0 ? ((counts.completed / counts.total) * 100).toFixed(1) : '0.0';
|
|
136
|
+
md += `| ${this.capitalizeFirst(phase)} | ${counts.total} | ${counts.completed} | ${counts.failed} | ${passRate}% |\n`;
|
|
137
|
+
}
|
|
138
|
+
md += `\n`;
|
|
139
|
+
// Error listings (if any)
|
|
140
|
+
if (summary.errors.length > 0) {
|
|
141
|
+
md += `## Errors (${summary.errors.length})\n\n`;
|
|
142
|
+
for (const error of summary.errors) {
|
|
143
|
+
md += `### ${error.phase.toUpperCase()} - ${error.identifier}\n\n`;
|
|
144
|
+
md += `- **Timestamp:** ${error.timestamp}\n`;
|
|
145
|
+
md += `- **Error:** ${error.error}\n\n`;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
// Spawn file inventory
|
|
149
|
+
md += `## Spawn Files\n\n`;
|
|
150
|
+
if (summary.spawnFiles.length > 0) {
|
|
151
|
+
for (const file of summary.spawnFiles) {
|
|
152
|
+
md += `- \`${file}\`\n`;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
md += `_No spawn files generated_\n`;
|
|
157
|
+
}
|
|
158
|
+
md += `\n---\n\n`;
|
|
159
|
+
md += `<!-- Powered by BMAD™ Core -->\n`;
|
|
160
|
+
return md;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Build markdown content for spawn output file
|
|
164
|
+
*
|
|
165
|
+
* @param payload - Spawn output payload
|
|
166
|
+
* @returns Formatted markdown content
|
|
167
|
+
* @private
|
|
168
|
+
*/
|
|
169
|
+
buildSpawnMarkdown(payload) {
|
|
170
|
+
const statusIcon = payload.success === false ? '❌' : payload.success === true ? '✅' : '⏳';
|
|
171
|
+
const durationStr = payload.duration ? this.formatDuration(payload.duration) : 'N/A';
|
|
172
|
+
let md = `# ${payload.type.toUpperCase()} Spawn: ${payload.spawnId}\n\n`;
|
|
173
|
+
md += `## Metadata\n\n`;
|
|
174
|
+
md += `- **Spawn ID:** ${payload.spawnId}\n`;
|
|
175
|
+
md += `- **Type:** ${payload.type}\n`;
|
|
176
|
+
md += `- **Status:** ${statusIcon} ${payload.success === false ? 'Failed' : payload.success === true ? 'Success' : 'Unknown'}\n`;
|
|
177
|
+
md += `- **Duration:** ${durationStr}\n`;
|
|
178
|
+
if (payload.completedAt) {
|
|
179
|
+
md += `- **Completed At:** ${payload.completedAt}\n`;
|
|
180
|
+
}
|
|
181
|
+
md += `\n`;
|
|
182
|
+
if (payload.errors) {
|
|
183
|
+
md += `## Errors\n\n`;
|
|
184
|
+
md += `\`\`\`\n${payload.errors}\n\`\`\`\n\n`;
|
|
185
|
+
}
|
|
186
|
+
md += `## Output\n\n`;
|
|
187
|
+
md += `${payload.content}\n\n`;
|
|
188
|
+
md += `---\n\n`;
|
|
189
|
+
md += `<!-- Powered by BMAD™ Core -->\n`;
|
|
190
|
+
return md;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Capitalize first letter of a string
|
|
194
|
+
*
|
|
195
|
+
* @param str - String to capitalize
|
|
196
|
+
* @returns Capitalized string
|
|
197
|
+
* @private
|
|
198
|
+
*/
|
|
199
|
+
capitalizeFirst(str) {
|
|
200
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Format duration in human-readable format
|
|
204
|
+
*
|
|
205
|
+
* @param ms - Duration in milliseconds
|
|
206
|
+
* @returns Formatted duration string
|
|
207
|
+
* @private
|
|
208
|
+
*/
|
|
209
|
+
formatDuration(ms) {
|
|
210
|
+
if (ms < 1000)
|
|
211
|
+
return `${ms}ms`;
|
|
212
|
+
if (ms < 60_000)
|
|
213
|
+
return `${(ms / 1000).toFixed(1)}s`;
|
|
214
|
+
if (ms < 3_600_000)
|
|
215
|
+
return `${(ms / 60_000).toFixed(1)}m`;
|
|
216
|
+
return `${(ms / 3_600_000).toFixed(1)}h`;
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Generate timestamp string for session directory name
|
|
220
|
+
*
|
|
221
|
+
* Format: YYYYMMDD-HHmmss
|
|
222
|
+
*
|
|
223
|
+
* @returns Formatted timestamp
|
|
224
|
+
* @private
|
|
225
|
+
*/
|
|
226
|
+
generateTimestamp() {
|
|
227
|
+
const now = new Date();
|
|
228
|
+
const year = now.getFullYear();
|
|
229
|
+
const month = String(now.getMonth() + 1).padStart(2, '0');
|
|
230
|
+
const day = String(now.getDate()).padStart(2, '0');
|
|
231
|
+
const hours = String(now.getHours()).padStart(2, '0');
|
|
232
|
+
const minutes = String(now.getMinutes()).padStart(2, '0');
|
|
233
|
+
const seconds = String(now.getSeconds()).padStart(2, '0');
|
|
234
|
+
return `${year}${month}${day}-${hours}${minutes}${seconds}`;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
@@ -8,6 +8,53 @@
|
|
|
8
8
|
import type pino from 'pino';
|
|
9
9
|
import { z } from 'zod';
|
|
10
10
|
import type { FileManager } from '../file-system/file-manager.js';
|
|
11
|
+
/**
|
|
12
|
+
* Zod schema for review configuration section
|
|
13
|
+
*
|
|
14
|
+
* Defines scanners, severity thresholds, self-heal limits, and path rules
|
|
15
|
+
* for automated code review in the BMAD workflow pipeline.
|
|
16
|
+
*/
|
|
17
|
+
export declare const reviewConfigSchema: z.ZodObject<{
|
|
18
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
19
|
+
pathRules: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
20
|
+
focus: z.ZodString;
|
|
21
|
+
pattern: z.ZodString;
|
|
22
|
+
}, z.core.$strip>>>;
|
|
23
|
+
scanners: z.ZodDefault<z.ZodArray<z.ZodEnum<{
|
|
24
|
+
ai: "ai";
|
|
25
|
+
coderabbit: "coderabbit";
|
|
26
|
+
lint: "lint";
|
|
27
|
+
}>>>;
|
|
28
|
+
selfHeal: z.ZodDefault<z.ZodObject<{
|
|
29
|
+
fixAgent: z.ZodDefault<z.ZodString>;
|
|
30
|
+
fixTimeout: z.ZodDefault<z.ZodNumber>;
|
|
31
|
+
maxIterations: z.ZodDefault<z.ZodNumber>;
|
|
32
|
+
}, z.core.$strip>>;
|
|
33
|
+
severity: z.ZodDefault<z.ZodObject<{
|
|
34
|
+
blockOn: z.ZodDefault<z.ZodArray<z.ZodEnum<{
|
|
35
|
+
CRITICAL: "CRITICAL";
|
|
36
|
+
HIGH: "HIGH";
|
|
37
|
+
LOW: "LOW";
|
|
38
|
+
MEDIUM: "MEDIUM";
|
|
39
|
+
}>>>;
|
|
40
|
+
documentOn: z.ZodDefault<z.ZodArray<z.ZodEnum<{
|
|
41
|
+
CRITICAL: "CRITICAL";
|
|
42
|
+
HIGH: "HIGH";
|
|
43
|
+
LOW: "LOW";
|
|
44
|
+
MEDIUM: "MEDIUM";
|
|
45
|
+
}>>>;
|
|
46
|
+
ignoreOn: z.ZodDefault<z.ZodArray<z.ZodEnum<{
|
|
47
|
+
CRITICAL: "CRITICAL";
|
|
48
|
+
HIGH: "HIGH";
|
|
49
|
+
LOW: "LOW";
|
|
50
|
+
MEDIUM: "MEDIUM";
|
|
51
|
+
}>>>;
|
|
52
|
+
}, z.core.$strip>>;
|
|
53
|
+
}, z.core.$strip>;
|
|
54
|
+
/**
|
|
55
|
+
* Inferred TypeScript type from review config Zod schema
|
|
56
|
+
*/
|
|
57
|
+
export type ReviewConfig = z.infer<typeof reviewConfigSchema>;
|
|
11
58
|
/**
|
|
12
59
|
* Zod schema for core configuration
|
|
13
60
|
*
|
|
@@ -22,6 +69,43 @@ declare const configSchema: z.ZodObject<{
|
|
|
22
69
|
qa: z.ZodOptional<z.ZodObject<{
|
|
23
70
|
qaLocation: z.ZodOptional<z.ZodString>;
|
|
24
71
|
}, z.core.$strip>>;
|
|
72
|
+
review: z.ZodOptional<z.ZodObject<{
|
|
73
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
74
|
+
pathRules: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
75
|
+
focus: z.ZodString;
|
|
76
|
+
pattern: z.ZodString;
|
|
77
|
+
}, z.core.$strip>>>;
|
|
78
|
+
scanners: z.ZodDefault<z.ZodArray<z.ZodEnum<{
|
|
79
|
+
ai: "ai";
|
|
80
|
+
coderabbit: "coderabbit";
|
|
81
|
+
lint: "lint";
|
|
82
|
+
}>>>;
|
|
83
|
+
selfHeal: z.ZodDefault<z.ZodObject<{
|
|
84
|
+
fixAgent: z.ZodDefault<z.ZodString>;
|
|
85
|
+
fixTimeout: z.ZodDefault<z.ZodNumber>;
|
|
86
|
+
maxIterations: z.ZodDefault<z.ZodNumber>;
|
|
87
|
+
}, z.core.$strip>>;
|
|
88
|
+
severity: z.ZodDefault<z.ZodObject<{
|
|
89
|
+
blockOn: z.ZodDefault<z.ZodArray<z.ZodEnum<{
|
|
90
|
+
CRITICAL: "CRITICAL";
|
|
91
|
+
HIGH: "HIGH";
|
|
92
|
+
LOW: "LOW";
|
|
93
|
+
MEDIUM: "MEDIUM";
|
|
94
|
+
}>>>;
|
|
95
|
+
documentOn: z.ZodDefault<z.ZodArray<z.ZodEnum<{
|
|
96
|
+
CRITICAL: "CRITICAL";
|
|
97
|
+
HIGH: "HIGH";
|
|
98
|
+
LOW: "LOW";
|
|
99
|
+
MEDIUM: "MEDIUM";
|
|
100
|
+
}>>>;
|
|
101
|
+
ignoreOn: z.ZodDefault<z.ZodArray<z.ZodEnum<{
|
|
102
|
+
CRITICAL: "CRITICAL";
|
|
103
|
+
HIGH: "HIGH";
|
|
104
|
+
LOW: "LOW";
|
|
105
|
+
MEDIUM: "MEDIUM";
|
|
106
|
+
}>>>;
|
|
107
|
+
}, z.core.$strip>>;
|
|
108
|
+
}, z.core.$strip>>;
|
|
25
109
|
}, z.core.$strip>;
|
|
26
110
|
/**
|
|
27
111
|
* Inferred TypeScript type from Zod schema
|