sdd-mcp-server 1.0.0
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 +256 -0
- package/dist/__tests__/setup.d.ts +44 -0
- package/dist/__tests__/setup.js +178 -0
- package/dist/__tests__/setup.js.map +1 -0
- package/dist/__tests__/test-helpers/mock-factories.d.ts +26 -0
- package/dist/__tests__/test-helpers/mock-factories.js +466 -0
- package/dist/__tests__/test-helpers/mock-factories.js.map +1 -0
- package/dist/adapters/cli/SDDToolAdapter.d.ts +26 -0
- package/dist/adapters/cli/SDDToolAdapter.js +273 -0
- package/dist/adapters/cli/SDDToolAdapter.js.map +1 -0
- package/dist/application/services/CodebaseAnalysisService.d.ts +38 -0
- package/dist/application/services/CodebaseAnalysisService.js +737 -0
- package/dist/application/services/CodebaseAnalysisService.js.map +1 -0
- package/dist/application/services/LocalizationService.d.ts +184 -0
- package/dist/application/services/LocalizationService.js +536 -0
- package/dist/application/services/LocalizationService.js.map +1 -0
- package/dist/application/services/ProjectContextService.d.ts +61 -0
- package/dist/application/services/ProjectContextService.js +550 -0
- package/dist/application/services/ProjectContextService.js.map +1 -0
- package/dist/application/services/ProjectInitializationService.d.ts +57 -0
- package/dist/application/services/ProjectInitializationService.js +485 -0
- package/dist/application/services/ProjectInitializationService.js.map +1 -0
- package/dist/application/services/ProjectService.d.ts +19 -0
- package/dist/application/services/ProjectService.js +159 -0
- package/dist/application/services/ProjectService.js.map +1 -0
- package/dist/application/services/QualityGateService.d.ts +62 -0
- package/dist/application/services/QualityGateService.js +428 -0
- package/dist/application/services/QualityGateService.js.map +1 -0
- package/dist/application/services/QualityService.d.ts +43 -0
- package/dist/application/services/QualityService.js +245 -0
- package/dist/application/services/QualityService.js.map +1 -0
- package/dist/application/services/SteeringDocumentService.d.ts +62 -0
- package/dist/application/services/SteeringDocumentService.js +694 -0
- package/dist/application/services/SteeringDocumentService.js.map +1 -0
- package/dist/application/services/TemplateService.d.ts +47 -0
- package/dist/application/services/TemplateService.js +438 -0
- package/dist/application/services/TemplateService.js.map +1 -0
- package/dist/application/services/WorkflowEngineService.d.ts +56 -0
- package/dist/application/services/WorkflowEngineService.js +348 -0
- package/dist/application/services/WorkflowEngineService.js.map +1 -0
- package/dist/application/services/WorkflowService.d.ts +22 -0
- package/dist/application/services/WorkflowService.js +147 -0
- package/dist/application/services/WorkflowService.js.map +1 -0
- package/dist/application/services/WorkflowValidationService.d.ts +51 -0
- package/dist/application/services/WorkflowValidationService.js +665 -0
- package/dist/application/services/WorkflowValidationService.js.map +1 -0
- package/dist/domain/context/ProjectContext.d.ts +350 -0
- package/dist/domain/context/ProjectContext.js +138 -0
- package/dist/domain/context/ProjectContext.js.map +1 -0
- package/dist/domain/i18n/index.d.ts +286 -0
- package/dist/domain/i18n/index.js +97 -0
- package/dist/domain/i18n/index.js.map +1 -0
- package/dist/domain/plugins/index.d.ts +498 -0
- package/dist/domain/plugins/index.js +157 -0
- package/dist/domain/plugins/index.js.map +1 -0
- package/dist/domain/ports.d.ts +54 -0
- package/dist/domain/ports.js +3 -0
- package/dist/domain/ports.js.map +1 -0
- package/dist/domain/quality/index.d.ts +361 -0
- package/dist/domain/quality/index.js +113 -0
- package/dist/domain/quality/index.js.map +1 -0
- package/dist/domain/services/DomainService.d.ts +18 -0
- package/dist/domain/services/DomainService.js +71 -0
- package/dist/domain/services/DomainService.js.map +1 -0
- package/dist/domain/templates/index.d.ts +158 -0
- package/dist/domain/templates/index.js +22 -0
- package/dist/domain/templates/index.js.map +1 -0
- package/dist/domain/types.d.ts +115 -0
- package/dist/domain/types.js +37 -0
- package/dist/domain/types.js.map +1 -0
- package/dist/domain/workflow/WorkflowStateMachine.d.ts +62 -0
- package/dist/domain/workflow/WorkflowStateMachine.js +286 -0
- package/dist/domain/workflow/WorkflowStateMachine.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +97 -0
- package/dist/index.js.map +1 -0
- package/dist/infrastructure/adapters/AjvValidationAdapter.d.ts +7 -0
- package/dist/infrastructure/adapters/AjvValidationAdapter.js +37 -0
- package/dist/infrastructure/adapters/AjvValidationAdapter.js.map +1 -0
- package/dist/infrastructure/adapters/ConsoleLoggerAdapter.d.ts +8 -0
- package/dist/infrastructure/adapters/ConsoleLoggerAdapter.js +41 -0
- package/dist/infrastructure/adapters/ConsoleLoggerAdapter.js.map +1 -0
- package/dist/infrastructure/adapters/FileBasedTaskTracker.d.ts +9 -0
- package/dist/infrastructure/adapters/FileBasedTaskTracker.js +41 -0
- package/dist/infrastructure/adapters/FileBasedTaskTracker.js.map +1 -0
- package/dist/infrastructure/adapters/HandlebarsTemplateEngine.d.ts +8 -0
- package/dist/infrastructure/adapters/HandlebarsTemplateEngine.js +38 -0
- package/dist/infrastructure/adapters/HandlebarsTemplateEngine.js.map +1 -0
- package/dist/infrastructure/adapters/JsonConfigurationAdapter.d.ts +7 -0
- package/dist/infrastructure/adapters/JsonConfigurationAdapter.js +24 -0
- package/dist/infrastructure/adapters/JsonConfigurationAdapter.js.map +1 -0
- package/dist/infrastructure/adapters/LinusQualityAnalyzer.d.ts +9 -0
- package/dist/infrastructure/adapters/LinusQualityAnalyzer.js +75 -0
- package/dist/infrastructure/adapters/LinusQualityAnalyzer.js.map +1 -0
- package/dist/infrastructure/adapters/NodeFileSystemAdapter.d.ts +12 -0
- package/dist/infrastructure/adapters/NodeFileSystemAdapter.js +39 -0
- package/dist/infrastructure/adapters/NodeFileSystemAdapter.js.map +1 -0
- package/dist/infrastructure/di/container.d.ts +3 -0
- package/dist/infrastructure/di/container.js +98 -0
- package/dist/infrastructure/di/container.js.map +1 -0
- package/dist/infrastructure/di/types.d.ts +39 -0
- package/dist/infrastructure/di/types.js +45 -0
- package/dist/infrastructure/di/types.js.map +1 -0
- package/dist/infrastructure/i18n/I18nextService.d.ts +27 -0
- package/dist/infrastructure/i18n/I18nextService.js +357 -0
- package/dist/infrastructure/i18n/I18nextService.js.map +1 -0
- package/dist/infrastructure/mcp/CapabilityNegotiator.d.ts +21 -0
- package/dist/infrastructure/mcp/CapabilityNegotiator.js +75 -0
- package/dist/infrastructure/mcp/CapabilityNegotiator.js.map +1 -0
- package/dist/infrastructure/mcp/ErrorHandler.d.ts +29 -0
- package/dist/infrastructure/mcp/ErrorHandler.js +101 -0
- package/dist/infrastructure/mcp/ErrorHandler.js.map +1 -0
- package/dist/infrastructure/mcp/MCPServer.d.ts +25 -0
- package/dist/infrastructure/mcp/MCPServer.js +246 -0
- package/dist/infrastructure/mcp/MCPServer.js.map +1 -0
- package/dist/infrastructure/mcp/PromptManager.d.ts +18 -0
- package/dist/infrastructure/mcp/PromptManager.js +373 -0
- package/dist/infrastructure/mcp/PromptManager.js.map +1 -0
- package/dist/infrastructure/mcp/ResourceManager.d.ts +15 -0
- package/dist/infrastructure/mcp/ResourceManager.js +229 -0
- package/dist/infrastructure/mcp/ResourceManager.js.map +1 -0
- package/dist/infrastructure/mcp/SessionManager.d.ts +64 -0
- package/dist/infrastructure/mcp/SessionManager.js +221 -0
- package/dist/infrastructure/mcp/SessionManager.js.map +1 -0
- package/dist/infrastructure/mcp/ToolRegistry.d.ts +48 -0
- package/dist/infrastructure/mcp/ToolRegistry.js +235 -0
- package/dist/infrastructure/mcp/ToolRegistry.js.map +1 -0
- package/dist/infrastructure/platform/PlatformAdapter.d.ts +46 -0
- package/dist/infrastructure/platform/PlatformAdapter.js +355 -0
- package/dist/infrastructure/platform/PlatformAdapter.js.map +1 -0
- package/dist/infrastructure/plugins/HookSystem.d.ts +40 -0
- package/dist/infrastructure/plugins/HookSystem.js +415 -0
- package/dist/infrastructure/plugins/HookSystem.js.map +1 -0
- package/dist/infrastructure/plugins/PluginManager.d.ts +51 -0
- package/dist/infrastructure/plugins/PluginManager.js +650 -0
- package/dist/infrastructure/plugins/PluginManager.js.map +1 -0
- package/dist/infrastructure/plugins/PluginSteeringRegistry.d.ts +63 -0
- package/dist/infrastructure/plugins/PluginSteeringRegistry.js +439 -0
- package/dist/infrastructure/plugins/PluginSteeringRegistry.js.map +1 -0
- package/dist/infrastructure/plugins/PluginToolRegistry.d.ts +54 -0
- package/dist/infrastructure/plugins/PluginToolRegistry.js +490 -0
- package/dist/infrastructure/plugins/PluginToolRegistry.js.map +1 -0
- package/dist/infrastructure/quality/ASTAnalyzer.d.ts +65 -0
- package/dist/infrastructure/quality/ASTAnalyzer.js +439 -0
- package/dist/infrastructure/quality/ASTAnalyzer.js.map +1 -0
- package/dist/infrastructure/quality/LinusCodeReviewer.d.ts +52 -0
- package/dist/infrastructure/quality/LinusCodeReviewer.js +551 -0
- package/dist/infrastructure/quality/LinusCodeReviewer.js.map +1 -0
- package/dist/infrastructure/repositories/InMemoryProjectRepository.d.ts +10 -0
- package/dist/infrastructure/repositories/InMemoryProjectRepository.js +35 -0
- package/dist/infrastructure/repositories/InMemoryProjectRepository.js.map +1 -0
- package/dist/infrastructure/schemas/project.schema.d.ts +192 -0
- package/dist/infrastructure/schemas/project.schema.js +114 -0
- package/dist/infrastructure/schemas/project.schema.js.map +1 -0
- package/dist/infrastructure/templates/FileGenerator.d.ts +15 -0
- package/dist/infrastructure/templates/FileGenerator.js +385 -0
- package/dist/infrastructure/templates/FileGenerator.js.map +1 -0
- package/dist/infrastructure/templates/HandlebarsRenderer.d.ts +16 -0
- package/dist/infrastructure/templates/HandlebarsRenderer.js +252 -0
- package/dist/infrastructure/templates/HandlebarsRenderer.js.map +1 -0
- package/dist/infrastructure/templates/TemplateManager.d.ts +36 -0
- package/dist/infrastructure/templates/TemplateManager.js +777 -0
- package/dist/infrastructure/templates/TemplateManager.js.map +1 -0
- package/package.json +89 -0
|
@@ -0,0 +1,665 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
11
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
12
|
+
};
|
|
13
|
+
import { injectable, inject } from 'inversify';
|
|
14
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
15
|
+
import { TYPES } from '../../infrastructure/di/types.js';
|
|
16
|
+
import { WorkflowPhase } from '../../domain/types.js';
|
|
17
|
+
import { WorkflowStateMachine } from '../../domain/workflow/WorkflowStateMachine.js';
|
|
18
|
+
let WorkflowValidationService = class WorkflowValidationService {
|
|
19
|
+
projectRepository;
|
|
20
|
+
fileSystem;
|
|
21
|
+
validation;
|
|
22
|
+
logger;
|
|
23
|
+
stateMachine;
|
|
24
|
+
constructor(projectRepository, fileSystem, validation, logger) {
|
|
25
|
+
this.projectRepository = projectRepository;
|
|
26
|
+
this.fileSystem = fileSystem;
|
|
27
|
+
this.validation = validation;
|
|
28
|
+
this.logger = logger;
|
|
29
|
+
this.stateMachine = new WorkflowStateMachine();
|
|
30
|
+
}
|
|
31
|
+
async validateRequirementsCompletion(projectId) {
|
|
32
|
+
const correlationId = uuidv4();
|
|
33
|
+
this.logger.info('Validating requirements completion', {
|
|
34
|
+
correlationId,
|
|
35
|
+
projectId
|
|
36
|
+
});
|
|
37
|
+
const project = await this.projectRepository.findById(projectId);
|
|
38
|
+
if (!project) {
|
|
39
|
+
return {
|
|
40
|
+
isComplete: false,
|
|
41
|
+
missingItems: ['Project not found'],
|
|
42
|
+
qualityIssues: [],
|
|
43
|
+
readinessScore: 0,
|
|
44
|
+
recommendations: []
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
const missingItems = [];
|
|
48
|
+
const qualityIssues = [];
|
|
49
|
+
const recommendations = [];
|
|
50
|
+
let readinessScore = 0;
|
|
51
|
+
try {
|
|
52
|
+
// Check if requirements document exists
|
|
53
|
+
const requirementsPath = `${project.path}/.kiro/specs/${project.name}/requirements.md`;
|
|
54
|
+
if (!(await this.fileSystem.exists(requirementsPath))) {
|
|
55
|
+
missingItems.push('requirements.md file not found');
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
const content = await this.fileSystem.readFile(requirementsPath);
|
|
59
|
+
// Basic content validation
|
|
60
|
+
if (content.trim().length < 100) {
|
|
61
|
+
qualityIssues.push('Requirements document is too short (less than 100 characters)');
|
|
62
|
+
}
|
|
63
|
+
// Check for EARS format patterns
|
|
64
|
+
const earsPatterns = ['WHEN', 'WHERE', 'IF', 'THEN', 'SHALL'];
|
|
65
|
+
const hasEarsFormat = earsPatterns.some(pattern => content.includes(pattern));
|
|
66
|
+
if (!hasEarsFormat) {
|
|
67
|
+
qualityIssues.push('Requirements do not follow EARS format (missing WHEN/WHERE/IF...THEN/SHALL patterns)');
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
readinessScore += 30;
|
|
71
|
+
}
|
|
72
|
+
// Check for acceptance criteria
|
|
73
|
+
if (content.includes('Acceptance Criteria') || content.includes('acceptance criteria')) {
|
|
74
|
+
readinessScore += 20;
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
missingItems.push('Acceptance criteria sections');
|
|
78
|
+
}
|
|
79
|
+
// Check for requirement numbering
|
|
80
|
+
const requirementCount = (content.match(/### Requirement \d+:/g) || []).length;
|
|
81
|
+
if (requirementCount === 0) {
|
|
82
|
+
missingItems.push('Numbered requirement sections');
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
readinessScore += 20;
|
|
86
|
+
}
|
|
87
|
+
if (requirementCount >= 3) {
|
|
88
|
+
readinessScore += 10; // Bonus for having multiple requirements
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
// Check approval status
|
|
92
|
+
if (project.metadata.approvals.requirements.generated) {
|
|
93
|
+
readinessScore += 10;
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
missingItems.push('Requirements generation flag');
|
|
97
|
+
}
|
|
98
|
+
if (project.metadata.approvals.requirements.approved) {
|
|
99
|
+
readinessScore += 10;
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
recommendations.push('Requirements need review and approval before proceeding to design');
|
|
103
|
+
}
|
|
104
|
+
// Generate recommendations
|
|
105
|
+
if (missingItems.length > 0) {
|
|
106
|
+
recommendations.push('Complete missing requirements items before approval');
|
|
107
|
+
}
|
|
108
|
+
if (qualityIssues.length > 0) {
|
|
109
|
+
recommendations.push('Address quality issues to improve requirements clarity');
|
|
110
|
+
}
|
|
111
|
+
if (readinessScore >= 80) {
|
|
112
|
+
recommendations.push('Requirements are ready for design phase');
|
|
113
|
+
}
|
|
114
|
+
else if (readinessScore >= 60) {
|
|
115
|
+
recommendations.push('Requirements need minor improvements before design');
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
recommendations.push('Requirements need significant work before design phase');
|
|
119
|
+
}
|
|
120
|
+
this.logger.info('Requirements validation completed', {
|
|
121
|
+
correlationId,
|
|
122
|
+
projectId,
|
|
123
|
+
readinessScore,
|
|
124
|
+
missingCount: missingItems.length,
|
|
125
|
+
qualityIssueCount: qualityIssues.length
|
|
126
|
+
});
|
|
127
|
+
return {
|
|
128
|
+
isComplete: missingItems.length === 0 && qualityIssues.length === 0,
|
|
129
|
+
missingItems,
|
|
130
|
+
qualityIssues,
|
|
131
|
+
readinessScore: Math.min(readinessScore, 100),
|
|
132
|
+
recommendations
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
this.logger.error('Requirements validation failed', error, {
|
|
137
|
+
correlationId,
|
|
138
|
+
projectId
|
|
139
|
+
});
|
|
140
|
+
return {
|
|
141
|
+
isComplete: false,
|
|
142
|
+
missingItems: [`Validation error: ${error.message}`],
|
|
143
|
+
qualityIssues: [],
|
|
144
|
+
readinessScore: 0,
|
|
145
|
+
recommendations: ['Fix validation errors and try again']
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
async validateDesignApproval(projectId) {
|
|
150
|
+
const correlationId = uuidv4();
|
|
151
|
+
this.logger.info('Validating design approval readiness', {
|
|
152
|
+
correlationId,
|
|
153
|
+
projectId
|
|
154
|
+
});
|
|
155
|
+
const project = await this.projectRepository.findById(projectId);
|
|
156
|
+
if (!project) {
|
|
157
|
+
return {
|
|
158
|
+
isComplete: false,
|
|
159
|
+
missingItems: ['Project not found'],
|
|
160
|
+
qualityIssues: [],
|
|
161
|
+
readinessScore: 0,
|
|
162
|
+
recommendations: []
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
const missingItems = [];
|
|
166
|
+
const qualityIssues = [];
|
|
167
|
+
const recommendations = [];
|
|
168
|
+
let readinessScore = 0;
|
|
169
|
+
try {
|
|
170
|
+
// Check if design document exists
|
|
171
|
+
const designPath = `${project.path}/.kiro/specs/${project.name}/design.md`;
|
|
172
|
+
if (!(await this.fileSystem.exists(designPath))) {
|
|
173
|
+
missingItems.push('design.md file not found');
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
const content = await this.fileSystem.readFile(designPath);
|
|
177
|
+
// Basic content validation
|
|
178
|
+
if (content.trim().length < 200) {
|
|
179
|
+
qualityIssues.push('Design document is too short (less than 200 characters)');
|
|
180
|
+
}
|
|
181
|
+
// Check for key design sections
|
|
182
|
+
const designSections = [
|
|
183
|
+
'Architecture',
|
|
184
|
+
'Components',
|
|
185
|
+
'Interface',
|
|
186
|
+
'Data Model',
|
|
187
|
+
'Technology'
|
|
188
|
+
];
|
|
189
|
+
let foundSections = 0;
|
|
190
|
+
for (const section of designSections) {
|
|
191
|
+
if (content.toLowerCase().includes(section.toLowerCase())) {
|
|
192
|
+
foundSections++;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
readinessScore += (foundSections / designSections.length) * 40;
|
|
196
|
+
if (foundSections < 3) {
|
|
197
|
+
missingItems.push(`Design missing key sections (found ${foundSections}/${designSections.length})`);
|
|
198
|
+
}
|
|
199
|
+
// Check for architectural diagrams or descriptions
|
|
200
|
+
if (content.includes('```') || content.includes('diagram') || content.includes('architecture')) {
|
|
201
|
+
readinessScore += 20;
|
|
202
|
+
}
|
|
203
|
+
else {
|
|
204
|
+
qualityIssues.push('Design lacks architectural diagrams or detailed descriptions');
|
|
205
|
+
}
|
|
206
|
+
// Check for technology decisions
|
|
207
|
+
if (content.toLowerCase().includes('technology') || content.toLowerCase().includes('stack')) {
|
|
208
|
+
readinessScore += 15;
|
|
209
|
+
}
|
|
210
|
+
else {
|
|
211
|
+
missingItems.push('Technology stack decisions');
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
// Validate requirements are approved first
|
|
215
|
+
if (!project.metadata.approvals.requirements.approved) {
|
|
216
|
+
missingItems.push('Requirements must be approved before design approval');
|
|
217
|
+
}
|
|
218
|
+
else {
|
|
219
|
+
readinessScore += 15;
|
|
220
|
+
}
|
|
221
|
+
// Check approval status
|
|
222
|
+
if (project.metadata.approvals.design.generated) {
|
|
223
|
+
readinessScore += 5;
|
|
224
|
+
}
|
|
225
|
+
else {
|
|
226
|
+
missingItems.push('Design generation flag');
|
|
227
|
+
}
|
|
228
|
+
if (project.metadata.approvals.design.approved) {
|
|
229
|
+
readinessScore += 5;
|
|
230
|
+
}
|
|
231
|
+
else {
|
|
232
|
+
recommendations.push('Design needs review and approval before proceeding to tasks');
|
|
233
|
+
}
|
|
234
|
+
// Generate recommendations
|
|
235
|
+
if (readinessScore >= 80) {
|
|
236
|
+
recommendations.push('Design is ready for tasks phase');
|
|
237
|
+
}
|
|
238
|
+
else if (readinessScore >= 60) {
|
|
239
|
+
recommendations.push('Design needs minor improvements before tasks');
|
|
240
|
+
}
|
|
241
|
+
else {
|
|
242
|
+
recommendations.push('Design needs significant work before tasks phase');
|
|
243
|
+
}
|
|
244
|
+
this.logger.info('Design validation completed', {
|
|
245
|
+
correlationId,
|
|
246
|
+
projectId,
|
|
247
|
+
readinessScore,
|
|
248
|
+
missingCount: missingItems.length,
|
|
249
|
+
qualityIssueCount: qualityIssues.length
|
|
250
|
+
});
|
|
251
|
+
return {
|
|
252
|
+
isComplete: missingItems.length === 0 && qualityIssues.length === 0,
|
|
253
|
+
missingItems,
|
|
254
|
+
qualityIssues,
|
|
255
|
+
readinessScore: Math.min(readinessScore, 100),
|
|
256
|
+
recommendations
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
catch (error) {
|
|
260
|
+
this.logger.error('Design validation failed', error, {
|
|
261
|
+
correlationId,
|
|
262
|
+
projectId
|
|
263
|
+
});
|
|
264
|
+
return {
|
|
265
|
+
isComplete: false,
|
|
266
|
+
missingItems: [`Validation error: ${error.message}`],
|
|
267
|
+
qualityIssues: [],
|
|
268
|
+
readinessScore: 0,
|
|
269
|
+
recommendations: ['Fix validation errors and try again']
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
async validateTaskApproval(projectId) {
|
|
274
|
+
const correlationId = uuidv4();
|
|
275
|
+
this.logger.info('Validating task approval readiness', {
|
|
276
|
+
correlationId,
|
|
277
|
+
projectId
|
|
278
|
+
});
|
|
279
|
+
const project = await this.projectRepository.findById(projectId);
|
|
280
|
+
if (!project) {
|
|
281
|
+
return {
|
|
282
|
+
isComplete: false,
|
|
283
|
+
missingItems: ['Project not found'],
|
|
284
|
+
qualityIssues: [],
|
|
285
|
+
readinessScore: 0,
|
|
286
|
+
recommendations: []
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
const missingItems = [];
|
|
290
|
+
const qualityIssues = [];
|
|
291
|
+
const recommendations = [];
|
|
292
|
+
let readinessScore = 0;
|
|
293
|
+
try {
|
|
294
|
+
// Check if tasks document exists
|
|
295
|
+
const tasksPath = `${project.path}/.kiro/specs/${project.name}/tasks.md`;
|
|
296
|
+
if (!(await this.fileSystem.exists(tasksPath))) {
|
|
297
|
+
missingItems.push('tasks.md file not found');
|
|
298
|
+
}
|
|
299
|
+
else {
|
|
300
|
+
const content = await this.fileSystem.readFile(tasksPath);
|
|
301
|
+
// Basic content validation
|
|
302
|
+
if (content.trim().length < 150) {
|
|
303
|
+
qualityIssues.push('Tasks document is too short (less than 150 characters)');
|
|
304
|
+
}
|
|
305
|
+
// Count tasks
|
|
306
|
+
const taskMatches = content.match(/- \[ \] \d+\./g) || [];
|
|
307
|
+
const taskCount = taskMatches.length;
|
|
308
|
+
if (taskCount === 0) {
|
|
309
|
+
missingItems.push('No tasks found in checkbox format');
|
|
310
|
+
}
|
|
311
|
+
else {
|
|
312
|
+
readinessScore += Math.min(taskCount * 5, 30); // Up to 30 points for tasks
|
|
313
|
+
}
|
|
314
|
+
// Check for requirement references
|
|
315
|
+
const requirementRefs = (content.match(/_Requirements: /g) || []).length;
|
|
316
|
+
if (requirementRefs === 0) {
|
|
317
|
+
qualityIssues.push('Tasks missing requirement traceability references');
|
|
318
|
+
}
|
|
319
|
+
else {
|
|
320
|
+
readinessScore += 20;
|
|
321
|
+
}
|
|
322
|
+
// Check for subtasks
|
|
323
|
+
const subtaskMatches = content.match(/ - [A-Z]/g) || [];
|
|
324
|
+
if (subtaskMatches.length === 0) {
|
|
325
|
+
qualityIssues.push('Tasks missing detailed subtasks');
|
|
326
|
+
}
|
|
327
|
+
else {
|
|
328
|
+
readinessScore += 15;
|
|
329
|
+
}
|
|
330
|
+
// Check task categories/organization
|
|
331
|
+
if (content.includes('# Implementation Plan') || content.includes('## ')) {
|
|
332
|
+
readinessScore += 10;
|
|
333
|
+
}
|
|
334
|
+
else {
|
|
335
|
+
qualityIssues.push('Tasks lack proper organization/categorization');
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
// Validate design is approved first
|
|
339
|
+
if (!project.metadata.approvals.design.approved) {
|
|
340
|
+
missingItems.push('Design must be approved before task approval');
|
|
341
|
+
}
|
|
342
|
+
else {
|
|
343
|
+
readinessScore += 15;
|
|
344
|
+
}
|
|
345
|
+
// Check approval status
|
|
346
|
+
if (project.metadata.approvals.tasks.generated) {
|
|
347
|
+
readinessScore += 5;
|
|
348
|
+
}
|
|
349
|
+
else {
|
|
350
|
+
missingItems.push('Tasks generation flag');
|
|
351
|
+
}
|
|
352
|
+
if (project.metadata.approvals.tasks.approved) {
|
|
353
|
+
readinessScore += 5;
|
|
354
|
+
}
|
|
355
|
+
else {
|
|
356
|
+
recommendations.push('Tasks need review and approval before proceeding to implementation');
|
|
357
|
+
}
|
|
358
|
+
// Generate recommendations
|
|
359
|
+
if (readinessScore >= 80) {
|
|
360
|
+
recommendations.push('Tasks are ready for implementation phase');
|
|
361
|
+
recommendations.push('Begin implementing tasks in order of priority');
|
|
362
|
+
}
|
|
363
|
+
else if (readinessScore >= 60) {
|
|
364
|
+
recommendations.push('Tasks need minor improvements before implementation');
|
|
365
|
+
}
|
|
366
|
+
else {
|
|
367
|
+
recommendations.push('Tasks need significant work before implementation phase');
|
|
368
|
+
}
|
|
369
|
+
this.logger.info('Tasks validation completed', {
|
|
370
|
+
correlationId,
|
|
371
|
+
projectId,
|
|
372
|
+
readinessScore,
|
|
373
|
+
missingCount: missingItems.length,
|
|
374
|
+
qualityIssueCount: qualityIssues.length
|
|
375
|
+
});
|
|
376
|
+
return {
|
|
377
|
+
isComplete: missingItems.length === 0 && qualityIssues.length === 0,
|
|
378
|
+
missingItems,
|
|
379
|
+
qualityIssues,
|
|
380
|
+
readinessScore: Math.min(readinessScore, 100),
|
|
381
|
+
recommendations
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
catch (error) {
|
|
385
|
+
this.logger.error('Tasks validation failed', error, {
|
|
386
|
+
correlationId,
|
|
387
|
+
projectId
|
|
388
|
+
});
|
|
389
|
+
return {
|
|
390
|
+
isComplete: false,
|
|
391
|
+
missingItems: [`Validation error: ${error.message}`],
|
|
392
|
+
qualityIssues: [],
|
|
393
|
+
readinessScore: 0,
|
|
394
|
+
recommendations: ['Fix validation errors and try again']
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
async validateCrossPhaseConsistency(projectId) {
|
|
399
|
+
const correlationId = uuidv4();
|
|
400
|
+
this.logger.info('Validating cross-phase consistency', {
|
|
401
|
+
correlationId,
|
|
402
|
+
projectId
|
|
403
|
+
});
|
|
404
|
+
const project = await this.projectRepository.findById(projectId);
|
|
405
|
+
if (!project) {
|
|
406
|
+
return {
|
|
407
|
+
isConsistent: false,
|
|
408
|
+
inconsistencies: ['Project not found'],
|
|
409
|
+
traceabilityIssues: [],
|
|
410
|
+
coverageGaps: []
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
const inconsistencies = [];
|
|
414
|
+
const traceabilityIssues = [];
|
|
415
|
+
const coverageGaps = [];
|
|
416
|
+
try {
|
|
417
|
+
const basePath = `${project.path}/.kiro/specs/${project.name}`;
|
|
418
|
+
// Read all phase documents
|
|
419
|
+
const documents = {};
|
|
420
|
+
const files = ['requirements.md', 'design.md', 'tasks.md'];
|
|
421
|
+
for (const file of files) {
|
|
422
|
+
const filePath = `${basePath}/${file}`;
|
|
423
|
+
if (await this.fileSystem.exists(filePath)) {
|
|
424
|
+
documents[file] = await this.fileSystem.readFile(filePath);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
// Validate Requirements -> Design traceability
|
|
428
|
+
if (documents['requirements.md'] && documents['design.md']) {
|
|
429
|
+
const requirements = documents['requirements.md'];
|
|
430
|
+
const design = documents['design.md'];
|
|
431
|
+
// Extract requirement titles
|
|
432
|
+
const reqMatches = requirements.match(/### Requirement \d+: (.+)/g) || [];
|
|
433
|
+
const requirementTitles = reqMatches.map(match => match.replace(/### Requirement \d+: /, '').trim());
|
|
434
|
+
// Check if design addresses all requirements
|
|
435
|
+
for (const reqTitle of requirementTitles) {
|
|
436
|
+
const keywords = reqTitle.split(' ').filter(word => word.length > 3);
|
|
437
|
+
const hasReference = keywords.some(keyword => design.toLowerCase().includes(keyword.toLowerCase()));
|
|
438
|
+
if (!hasReference) {
|
|
439
|
+
traceabilityIssues.push(`Design may not address requirement: "${reqTitle}"`);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
// Validate Design -> Tasks traceability
|
|
444
|
+
if (documents['design.md'] && documents['tasks.md']) {
|
|
445
|
+
const design = documents['design.md'];
|
|
446
|
+
const tasks = documents['tasks.md'];
|
|
447
|
+
// Look for component mentions in design
|
|
448
|
+
const componentMatches = design.match(/## (.+Component|.+Service|.+Manager)/g) || [];
|
|
449
|
+
const components = componentMatches.map(match => match.replace('## ', '').trim());
|
|
450
|
+
// Check if tasks cover all components
|
|
451
|
+
for (const component of components) {
|
|
452
|
+
const keywords = component.split(' ').filter(word => word.length > 3);
|
|
453
|
+
const hasCoverage = keywords.some(keyword => tasks.toLowerCase().includes(keyword.toLowerCase()));
|
|
454
|
+
if (!hasCoverage) {
|
|
455
|
+
coverageGaps.push(`Tasks may not cover design component: "${component}"`);
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
// Validate phase consistency with project metadata
|
|
460
|
+
const { phase, metadata } = project;
|
|
461
|
+
if (phase === WorkflowPhase.REQUIREMENTS && !metadata.approvals.requirements.generated) {
|
|
462
|
+
inconsistencies.push('Project in requirements phase but requirements not marked as generated');
|
|
463
|
+
}
|
|
464
|
+
if (phase === WorkflowPhase.DESIGN && !metadata.approvals.design.generated) {
|
|
465
|
+
inconsistencies.push('Project in design phase but design not marked as generated');
|
|
466
|
+
}
|
|
467
|
+
if (phase === WorkflowPhase.TASKS && !metadata.approvals.tasks.generated) {
|
|
468
|
+
inconsistencies.push('Project in tasks phase but tasks not marked as generated');
|
|
469
|
+
}
|
|
470
|
+
// Check for orphaned approvals (approved but not generated)
|
|
471
|
+
Object.entries(metadata.approvals).forEach(([phaseName, approval]) => {
|
|
472
|
+
if (approval.approved && !approval.generated) {
|
|
473
|
+
inconsistencies.push(`${phaseName} marked as approved but not generated`);
|
|
474
|
+
}
|
|
475
|
+
});
|
|
476
|
+
this.logger.info('Cross-phase validation completed', {
|
|
477
|
+
correlationId,
|
|
478
|
+
projectId,
|
|
479
|
+
inconsistencyCount: inconsistencies.length,
|
|
480
|
+
traceabilityIssueCount: traceabilityIssues.length,
|
|
481
|
+
coverageGapCount: coverageGaps.length
|
|
482
|
+
});
|
|
483
|
+
return {
|
|
484
|
+
isConsistent: inconsistencies.length === 0 &&
|
|
485
|
+
traceabilityIssues.length === 0 &&
|
|
486
|
+
coverageGaps.length === 0,
|
|
487
|
+
inconsistencies,
|
|
488
|
+
traceabilityIssues,
|
|
489
|
+
coverageGaps
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
catch (error) {
|
|
493
|
+
this.logger.error('Cross-phase validation failed', error, {
|
|
494
|
+
correlationId,
|
|
495
|
+
projectId
|
|
496
|
+
});
|
|
497
|
+
return {
|
|
498
|
+
isConsistent: false,
|
|
499
|
+
inconsistencies: [`Validation error: ${error.message}`],
|
|
500
|
+
traceabilityIssues: [],
|
|
501
|
+
coverageGaps: []
|
|
502
|
+
};
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
async validateWorkflowRollback(projectId, targetPhase) {
|
|
506
|
+
const correlationId = uuidv4();
|
|
507
|
+
this.logger.info('Validating workflow rollback', {
|
|
508
|
+
correlationId,
|
|
509
|
+
projectId,
|
|
510
|
+
targetPhase
|
|
511
|
+
});
|
|
512
|
+
const project = await this.projectRepository.findById(projectId);
|
|
513
|
+
if (!project) {
|
|
514
|
+
return {
|
|
515
|
+
canRollback: false,
|
|
516
|
+
reason: 'Project not found',
|
|
517
|
+
impactedItems: [],
|
|
518
|
+
rollbackActions: []
|
|
519
|
+
};
|
|
520
|
+
}
|
|
521
|
+
const impactedItems = [];
|
|
522
|
+
const rollbackActions = [];
|
|
523
|
+
try {
|
|
524
|
+
// Check if rollback is valid
|
|
525
|
+
const validation = this.stateMachine.canTransition(project, targetPhase);
|
|
526
|
+
if (!validation.allowed) {
|
|
527
|
+
return {
|
|
528
|
+
canRollback: false,
|
|
529
|
+
reason: validation.reason,
|
|
530
|
+
impactedItems: [],
|
|
531
|
+
rollbackActions: []
|
|
532
|
+
};
|
|
533
|
+
}
|
|
534
|
+
// Identify impacted items based on rollback target
|
|
535
|
+
const currentPhase = project.phase;
|
|
536
|
+
switch (currentPhase) {
|
|
537
|
+
case WorkflowPhase.REQUIREMENTS:
|
|
538
|
+
if (targetPhase === WorkflowPhase.INIT) {
|
|
539
|
+
impactedItems.push('Requirements document and approval status');
|
|
540
|
+
rollbackActions.push('Reset requirements approval status');
|
|
541
|
+
rollbackActions.push('Optionally backup requirements.md');
|
|
542
|
+
}
|
|
543
|
+
break;
|
|
544
|
+
case WorkflowPhase.DESIGN:
|
|
545
|
+
if (targetPhase === WorkflowPhase.REQUIREMENTS) {
|
|
546
|
+
impactedItems.push('Design document and approval status');
|
|
547
|
+
rollbackActions.push('Reset design approval status');
|
|
548
|
+
rollbackActions.push('Optionally backup design.md');
|
|
549
|
+
}
|
|
550
|
+
break;
|
|
551
|
+
case WorkflowPhase.TASKS:
|
|
552
|
+
if (targetPhase === WorkflowPhase.DESIGN) {
|
|
553
|
+
impactedItems.push('Tasks document and approval status');
|
|
554
|
+
rollbackActions.push('Reset tasks approval status');
|
|
555
|
+
rollbackActions.push('Optionally backup tasks.md');
|
|
556
|
+
}
|
|
557
|
+
break;
|
|
558
|
+
case WorkflowPhase.IMPLEMENTATION:
|
|
559
|
+
if (targetPhase === WorkflowPhase.TASKS) {
|
|
560
|
+
impactedItems.push('Implementation progress');
|
|
561
|
+
rollbackActions.push('Implementation work may be lost');
|
|
562
|
+
rollbackActions.push('Consider backing up current implementation');
|
|
563
|
+
}
|
|
564
|
+
break;
|
|
565
|
+
}
|
|
566
|
+
// Add general rollback actions
|
|
567
|
+
rollbackActions.push('Update project phase in spec.json');
|
|
568
|
+
rollbackActions.push('Update project metadata timestamps');
|
|
569
|
+
rollbackActions.push('Create audit trail entry');
|
|
570
|
+
this.logger.info('Workflow rollback validation completed', {
|
|
571
|
+
correlationId,
|
|
572
|
+
projectId,
|
|
573
|
+
currentPhase,
|
|
574
|
+
targetPhase,
|
|
575
|
+
impactedCount: impactedItems.length
|
|
576
|
+
});
|
|
577
|
+
return {
|
|
578
|
+
canRollback: true,
|
|
579
|
+
impactedItems,
|
|
580
|
+
rollbackActions
|
|
581
|
+
};
|
|
582
|
+
}
|
|
583
|
+
catch (error) {
|
|
584
|
+
this.logger.error('Workflow rollback validation failed', error, {
|
|
585
|
+
correlationId,
|
|
586
|
+
projectId,
|
|
587
|
+
targetPhase
|
|
588
|
+
});
|
|
589
|
+
return {
|
|
590
|
+
canRollback: false,
|
|
591
|
+
reason: `Validation error: ${error.message}`,
|
|
592
|
+
impactedItems: [],
|
|
593
|
+
rollbackActions: []
|
|
594
|
+
};
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
async generateComprehensiveValidationReport(projectId) {
|
|
598
|
+
const project = await this.projectRepository.findById(projectId);
|
|
599
|
+
if (!project) {
|
|
600
|
+
return null;
|
|
601
|
+
}
|
|
602
|
+
const phases = {
|
|
603
|
+
requirements: await this.validateRequirementsCompletion(projectId),
|
|
604
|
+
design: await this.validateDesignApproval(projectId),
|
|
605
|
+
tasks: await this.validateTaskApproval(projectId)
|
|
606
|
+
};
|
|
607
|
+
const crossPhase = await this.validateCrossPhaseConsistency(projectId);
|
|
608
|
+
// Calculate overall readiness
|
|
609
|
+
const phaseScores = [
|
|
610
|
+
phases.requirements.readinessScore,
|
|
611
|
+
phases.design.readinessScore,
|
|
612
|
+
phases.tasks.readinessScore
|
|
613
|
+
];
|
|
614
|
+
const overallScore = Math.round(phaseScores.reduce((sum, score) => sum + score, 0) / 3);
|
|
615
|
+
// Determine overall validity
|
|
616
|
+
const isValid = phases.requirements.isComplete &&
|
|
617
|
+
phases.design.isComplete &&
|
|
618
|
+
phases.tasks.isComplete &&
|
|
619
|
+
crossPhase.isConsistent;
|
|
620
|
+
// Check if can progress
|
|
621
|
+
const nextPhase = this.stateMachine.getNextPhase(project.phase);
|
|
622
|
+
const canProgress = nextPhase ?
|
|
623
|
+
this.stateMachine.canTransition(project, nextPhase).allowed : false;
|
|
624
|
+
// Collect recommendations
|
|
625
|
+
const recommendations = [
|
|
626
|
+
...phases.requirements.recommendations,
|
|
627
|
+
...phases.design.recommendations,
|
|
628
|
+
...phases.tasks.recommendations
|
|
629
|
+
];
|
|
630
|
+
// Generate next actions
|
|
631
|
+
const nextActions = [];
|
|
632
|
+
if (!canProgress && nextPhase) {
|
|
633
|
+
nextActions.push(`Complete requirements for ${nextPhase} phase progression`);
|
|
634
|
+
}
|
|
635
|
+
if (crossPhase.inconsistencies.length > 0) {
|
|
636
|
+
nextActions.push('Resolve cross-phase consistency issues');
|
|
637
|
+
}
|
|
638
|
+
if (isValid && canProgress) {
|
|
639
|
+
nextActions.push('Project is ready for phase progression');
|
|
640
|
+
}
|
|
641
|
+
return {
|
|
642
|
+
project,
|
|
643
|
+
overall: {
|
|
644
|
+
isValid,
|
|
645
|
+
readinessScore: overallScore,
|
|
646
|
+
phase: project.phase,
|
|
647
|
+
canProgress
|
|
648
|
+
},
|
|
649
|
+
phases,
|
|
650
|
+
crossPhase,
|
|
651
|
+
recommendations: [...new Set(recommendations)], // Remove duplicates
|
|
652
|
+
nextActions
|
|
653
|
+
};
|
|
654
|
+
}
|
|
655
|
+
};
|
|
656
|
+
WorkflowValidationService = __decorate([
|
|
657
|
+
injectable(),
|
|
658
|
+
__param(0, inject(TYPES.ProjectRepository)),
|
|
659
|
+
__param(1, inject(TYPES.FileSystemPort)),
|
|
660
|
+
__param(2, inject(TYPES.ValidationPort)),
|
|
661
|
+
__param(3, inject(TYPES.LoggerPort)),
|
|
662
|
+
__metadata("design:paramtypes", [Object, Object, Object, Object])
|
|
663
|
+
], WorkflowValidationService);
|
|
664
|
+
export { WorkflowValidationService };
|
|
665
|
+
//# sourceMappingURL=WorkflowValidationService.js.map
|