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,385 @@
|
|
|
1
|
+
// File generation and directory management implementation
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
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;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
|
+
};
|
|
14
|
+
import { injectable, inject } from 'inversify';
|
|
15
|
+
import { promises as fs } from 'fs';
|
|
16
|
+
import path from 'path';
|
|
17
|
+
import { randomBytes } from 'crypto';
|
|
18
|
+
import { TYPES } from '../di/types.js';
|
|
19
|
+
const DEFAULT_FILE_OPTIONS = {
|
|
20
|
+
backup: true,
|
|
21
|
+
overwrite: false,
|
|
22
|
+
createDirectories: true,
|
|
23
|
+
atomic: true
|
|
24
|
+
};
|
|
25
|
+
let FileGenerator = class FileGenerator {
|
|
26
|
+
logger;
|
|
27
|
+
renderer;
|
|
28
|
+
backupDirectory;
|
|
29
|
+
constructor(logger, renderer, backupDirectory = './.backups') {
|
|
30
|
+
this.logger = logger;
|
|
31
|
+
this.renderer = renderer;
|
|
32
|
+
this.backupDirectory = path.resolve(backupDirectory);
|
|
33
|
+
}
|
|
34
|
+
async generateFile(filePath, template, data, context, options = DEFAULT_FILE_OPTIONS) {
|
|
35
|
+
const resolvedPath = path.resolve(filePath);
|
|
36
|
+
const finalOptions = { ...DEFAULT_FILE_OPTIONS, ...options };
|
|
37
|
+
try {
|
|
38
|
+
// Validate the target path
|
|
39
|
+
const pathValidation = await this.validatePath(resolvedPath);
|
|
40
|
+
if (!pathValidation.isValid) {
|
|
41
|
+
return {
|
|
42
|
+
filePath: resolvedPath,
|
|
43
|
+
success: false,
|
|
44
|
+
bytesWritten: 0,
|
|
45
|
+
error: `Invalid path: ${pathValidation.errors.join(', ')}`,
|
|
46
|
+
warnings: []
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
// Check if file exists and handle accordingly
|
|
50
|
+
if (pathValidation.exists && pathValidation.isFile && !finalOptions.overwrite) {
|
|
51
|
+
return {
|
|
52
|
+
filePath: resolvedPath,
|
|
53
|
+
success: false,
|
|
54
|
+
bytesWritten: 0,
|
|
55
|
+
error: 'File exists and overwrite is disabled',
|
|
56
|
+
warnings: []
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
// Create backup if requested and file exists
|
|
60
|
+
let backupPath;
|
|
61
|
+
if (finalOptions.backup && pathValidation.exists && pathValidation.isFile) {
|
|
62
|
+
backupPath = await this.backupFile(resolvedPath);
|
|
63
|
+
}
|
|
64
|
+
// Create parent directories if needed
|
|
65
|
+
if (finalOptions.createDirectories) {
|
|
66
|
+
const parentDir = path.dirname(resolvedPath);
|
|
67
|
+
await fs.mkdir(parentDir, { recursive: true });
|
|
68
|
+
}
|
|
69
|
+
// Render the template
|
|
70
|
+
const renderedContent = await this.renderer.renderString(template, data, context);
|
|
71
|
+
// Write file atomically or directly
|
|
72
|
+
const bytesWritten = await this.writeFile(resolvedPath, renderedContent, finalOptions.atomic);
|
|
73
|
+
// Set permissions if specified
|
|
74
|
+
if (finalOptions.permissions) {
|
|
75
|
+
await fs.chmod(resolvedPath, finalOptions.permissions);
|
|
76
|
+
}
|
|
77
|
+
this.logger.info('File generated successfully', {
|
|
78
|
+
filePath: resolvedPath,
|
|
79
|
+
bytesWritten,
|
|
80
|
+
backup: !!backupPath
|
|
81
|
+
});
|
|
82
|
+
return {
|
|
83
|
+
filePath: resolvedPath,
|
|
84
|
+
success: true,
|
|
85
|
+
backupPath,
|
|
86
|
+
bytesWritten,
|
|
87
|
+
warnings: []
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
this.logger.error('File generation failed', {
|
|
92
|
+
filePath: resolvedPath,
|
|
93
|
+
error: error instanceof Error ? error.message : String(error)
|
|
94
|
+
});
|
|
95
|
+
return {
|
|
96
|
+
filePath: resolvedPath,
|
|
97
|
+
success: false,
|
|
98
|
+
bytesWritten: 0,
|
|
99
|
+
error: error instanceof Error ? error.message : String(error),
|
|
100
|
+
warnings: []
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
async generateDirectory(dirPath, structure, context, options = DEFAULT_FILE_OPTIONS) {
|
|
105
|
+
const resolvedDirPath = path.resolve(dirPath);
|
|
106
|
+
const filesGenerated = [];
|
|
107
|
+
const directoriesCreated = [];
|
|
108
|
+
const errors = [];
|
|
109
|
+
const warnings = [];
|
|
110
|
+
try {
|
|
111
|
+
// Validate the target directory path
|
|
112
|
+
const pathValidation = await this.validatePath(resolvedDirPath);
|
|
113
|
+
// Create the root directory if it doesn't exist
|
|
114
|
+
if (!pathValidation.exists) {
|
|
115
|
+
await fs.mkdir(resolvedDirPath, { recursive: true });
|
|
116
|
+
directoriesCreated.push(resolvedDirPath);
|
|
117
|
+
}
|
|
118
|
+
// Generate files in the current directory
|
|
119
|
+
for (const file of structure.files) {
|
|
120
|
+
const filePath = path.join(resolvedDirPath, file.name);
|
|
121
|
+
try {
|
|
122
|
+
const result = await this.generateFile(filePath, file.template, file.data, context, options);
|
|
123
|
+
filesGenerated.push(result);
|
|
124
|
+
if (!result.success) {
|
|
125
|
+
errors.push(`Failed to generate ${file.name}: ${result.error}`);
|
|
126
|
+
}
|
|
127
|
+
if (result.warnings.length > 0) {
|
|
128
|
+
warnings.push(...result.warnings.map(w => `${file.name}: ${w}`));
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
catch (error) {
|
|
132
|
+
const errorMessage = `Error generating file ${file.name}: ${error instanceof Error ? error.message : String(error)}`;
|
|
133
|
+
errors.push(errorMessage);
|
|
134
|
+
filesGenerated.push({
|
|
135
|
+
filePath,
|
|
136
|
+
success: false,
|
|
137
|
+
bytesWritten: 0,
|
|
138
|
+
error: errorMessage,
|
|
139
|
+
warnings: []
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
// Recursively generate subdirectories
|
|
144
|
+
for (const [subdirName, subdirStructure] of Object.entries(structure.subdirectories)) {
|
|
145
|
+
const subdirPath = path.join(resolvedDirPath, subdirName);
|
|
146
|
+
try {
|
|
147
|
+
const result = await this.generateDirectory(subdirPath, subdirStructure, context, options);
|
|
148
|
+
filesGenerated.push(...result.filesGenerated);
|
|
149
|
+
directoriesCreated.push(...result.directoriesCreated);
|
|
150
|
+
if (result.errors.length > 0) {
|
|
151
|
+
errors.push(...result.errors.map(e => `${subdirName}/${e}`));
|
|
152
|
+
}
|
|
153
|
+
if (result.warnings.length > 0) {
|
|
154
|
+
warnings.push(...result.warnings.map(w => `${subdirName}/${w}`));
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
catch (error) {
|
|
158
|
+
errors.push(`Failed to generate subdirectory ${subdirName}: ${error instanceof Error ? error.message : String(error)}`);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
const success = errors.length === 0;
|
|
162
|
+
this.logger.info('Directory generation completed', {
|
|
163
|
+
dirPath: resolvedDirPath,
|
|
164
|
+
success,
|
|
165
|
+
filesGenerated: filesGenerated.length,
|
|
166
|
+
directoriesCreated: directoriesCreated.length,
|
|
167
|
+
errors: errors.length
|
|
168
|
+
});
|
|
169
|
+
return {
|
|
170
|
+
dirPath: resolvedDirPath,
|
|
171
|
+
success,
|
|
172
|
+
filesGenerated,
|
|
173
|
+
directoriesCreated,
|
|
174
|
+
errors,
|
|
175
|
+
warnings
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
catch (error) {
|
|
179
|
+
const errorMessage = `Directory generation failed: ${error instanceof Error ? error.message : String(error)}`;
|
|
180
|
+
this.logger.error('Directory generation failed', {
|
|
181
|
+
dirPath: resolvedDirPath,
|
|
182
|
+
error: errorMessage
|
|
183
|
+
});
|
|
184
|
+
return {
|
|
185
|
+
dirPath: resolvedDirPath,
|
|
186
|
+
success: false,
|
|
187
|
+
filesGenerated,
|
|
188
|
+
directoriesCreated,
|
|
189
|
+
errors: [errorMessage],
|
|
190
|
+
warnings
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
async validatePath(targetPath) {
|
|
195
|
+
const errors = [];
|
|
196
|
+
try {
|
|
197
|
+
// Check if path contains invalid characters
|
|
198
|
+
if (/[<>:"|?*]/.test(targetPath)) {
|
|
199
|
+
errors.push('Path contains invalid characters');
|
|
200
|
+
}
|
|
201
|
+
// Check if path is absolute or relative
|
|
202
|
+
if (!path.isAbsolute(targetPath)) {
|
|
203
|
+
// This is actually fine, we'll resolve it
|
|
204
|
+
}
|
|
205
|
+
const resolvedPath = path.resolve(targetPath);
|
|
206
|
+
// Check if path exists and get stats
|
|
207
|
+
let stats = null;
|
|
208
|
+
let exists = false;
|
|
209
|
+
try {
|
|
210
|
+
stats = await fs.stat(resolvedPath);
|
|
211
|
+
exists = true;
|
|
212
|
+
}
|
|
213
|
+
catch (error) {
|
|
214
|
+
if (error.code !== 'ENOENT') {
|
|
215
|
+
errors.push(`Unable to access path: ${error instanceof Error ? error.message : String(error)}`);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
// Check permissions if file/directory exists
|
|
219
|
+
let permissions = {
|
|
220
|
+
read: false,
|
|
221
|
+
write: false,
|
|
222
|
+
execute: false
|
|
223
|
+
};
|
|
224
|
+
if (exists && stats) {
|
|
225
|
+
try {
|
|
226
|
+
await fs.access(resolvedPath, fs.constants.R_OK);
|
|
227
|
+
permissions.read = true;
|
|
228
|
+
}
|
|
229
|
+
catch {
|
|
230
|
+
// No read permission
|
|
231
|
+
}
|
|
232
|
+
try {
|
|
233
|
+
await fs.access(resolvedPath, fs.constants.W_OK);
|
|
234
|
+
permissions.write = true;
|
|
235
|
+
}
|
|
236
|
+
catch {
|
|
237
|
+
// No write permission
|
|
238
|
+
}
|
|
239
|
+
try {
|
|
240
|
+
await fs.access(resolvedPath, fs.constants.X_OK);
|
|
241
|
+
permissions.execute = true;
|
|
242
|
+
}
|
|
243
|
+
catch {
|
|
244
|
+
// No execute permission
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
else {
|
|
248
|
+
// Check parent directory permissions
|
|
249
|
+
const parentDir = path.dirname(resolvedPath);
|
|
250
|
+
try {
|
|
251
|
+
await fs.access(parentDir, fs.constants.W_OK);
|
|
252
|
+
permissions.write = true;
|
|
253
|
+
}
|
|
254
|
+
catch {
|
|
255
|
+
errors.push('Parent directory is not writable');
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
return {
|
|
259
|
+
isValid: errors.length === 0,
|
|
260
|
+
exists,
|
|
261
|
+
isFile: exists && stats?.isFile() || false,
|
|
262
|
+
isDirectory: exists && stats?.isDirectory() || false,
|
|
263
|
+
permissions,
|
|
264
|
+
errors
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
catch (error) {
|
|
268
|
+
errors.push(`Path validation failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
269
|
+
return {
|
|
270
|
+
isValid: false,
|
|
271
|
+
exists: false,
|
|
272
|
+
isFile: false,
|
|
273
|
+
isDirectory: false,
|
|
274
|
+
permissions: { read: false, write: false, execute: false },
|
|
275
|
+
errors
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
async backupFile(filePath) {
|
|
280
|
+
const resolvedPath = path.resolve(filePath);
|
|
281
|
+
const fileName = path.basename(resolvedPath);
|
|
282
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
283
|
+
const randomSuffix = randomBytes(4).toString('hex');
|
|
284
|
+
const backupName = `${fileName}.${timestamp}.${randomSuffix}.backup`;
|
|
285
|
+
const backupPath = path.join(this.backupDirectory, backupName);
|
|
286
|
+
try {
|
|
287
|
+
await fs.mkdir(this.backupDirectory, { recursive: true });
|
|
288
|
+
await fs.copyFile(resolvedPath, backupPath);
|
|
289
|
+
this.logger.debug('File backup created', {
|
|
290
|
+
original: resolvedPath,
|
|
291
|
+
backup: backupPath
|
|
292
|
+
});
|
|
293
|
+
return backupPath;
|
|
294
|
+
}
|
|
295
|
+
catch (error) {
|
|
296
|
+
this.logger.error('File backup failed', {
|
|
297
|
+
filePath: resolvedPath,
|
|
298
|
+
backupPath,
|
|
299
|
+
error: error instanceof Error ? error.message : String(error)
|
|
300
|
+
});
|
|
301
|
+
throw new Error(`Backup failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
async restoreBackup(backupPath, originalPath) {
|
|
305
|
+
try {
|
|
306
|
+
await fs.copyFile(backupPath, originalPath);
|
|
307
|
+
this.logger.info('File restored from backup', {
|
|
308
|
+
backup: backupPath,
|
|
309
|
+
restored: originalPath
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
catch (error) {
|
|
313
|
+
this.logger.error('File restore failed', {
|
|
314
|
+
backupPath,
|
|
315
|
+
originalPath,
|
|
316
|
+
error: error instanceof Error ? error.message : String(error)
|
|
317
|
+
});
|
|
318
|
+
throw new Error(`Restore failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
async cleanupBackups(olderThan) {
|
|
322
|
+
const deletedBackups = [];
|
|
323
|
+
try {
|
|
324
|
+
await fs.mkdir(this.backupDirectory, { recursive: true });
|
|
325
|
+
const backupFiles = await fs.readdir(this.backupDirectory);
|
|
326
|
+
for (const fileName of backupFiles) {
|
|
327
|
+
if (!fileName.endsWith('.backup'))
|
|
328
|
+
continue;
|
|
329
|
+
const filePath = path.join(this.backupDirectory, fileName);
|
|
330
|
+
const stats = await fs.stat(filePath);
|
|
331
|
+
if (stats.mtime < olderThan) {
|
|
332
|
+
await fs.unlink(filePath);
|
|
333
|
+
deletedBackups.push(filePath);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
this.logger.info('Backup cleanup completed', {
|
|
337
|
+
deleted: deletedBackups.length,
|
|
338
|
+
olderThan: olderThan.toISOString()
|
|
339
|
+
});
|
|
340
|
+
return deletedBackups;
|
|
341
|
+
}
|
|
342
|
+
catch (error) {
|
|
343
|
+
this.logger.error('Backup cleanup failed', {
|
|
344
|
+
error: error instanceof Error ? error.message : String(error)
|
|
345
|
+
});
|
|
346
|
+
throw new Error(`Backup cleanup failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
async writeFile(filePath, content, atomic) {
|
|
350
|
+
if (atomic) {
|
|
351
|
+
// Write to temporary file first, then rename
|
|
352
|
+
const tempPath = `${filePath}.tmp.${randomBytes(8).toString('hex')}`;
|
|
353
|
+
try {
|
|
354
|
+
await fs.writeFile(tempPath, content, 'utf-8');
|
|
355
|
+
await fs.rename(tempPath, filePath);
|
|
356
|
+
const stats = await fs.stat(filePath);
|
|
357
|
+
return stats.size;
|
|
358
|
+
}
|
|
359
|
+
catch (error) {
|
|
360
|
+
// Clean up temp file if it exists
|
|
361
|
+
try {
|
|
362
|
+
await fs.unlink(tempPath);
|
|
363
|
+
}
|
|
364
|
+
catch {
|
|
365
|
+
// Ignore cleanup errors
|
|
366
|
+
}
|
|
367
|
+
throw error;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
else {
|
|
371
|
+
// Write directly
|
|
372
|
+
await fs.writeFile(filePath, content, 'utf-8');
|
|
373
|
+
const stats = await fs.stat(filePath);
|
|
374
|
+
return stats.size;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
};
|
|
378
|
+
FileGenerator = __decorate([
|
|
379
|
+
injectable(),
|
|
380
|
+
__param(0, inject(TYPES.LoggerPort)),
|
|
381
|
+
__param(1, inject(TYPES.TemplateRendererPort)),
|
|
382
|
+
__metadata("design:paramtypes", [Object, Object, Object])
|
|
383
|
+
], FileGenerator);
|
|
384
|
+
export { FileGenerator };
|
|
385
|
+
//# sourceMappingURL=FileGenerator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FileGenerator.js","sourceRoot":"","sources":["../../../src/infrastructure/templates/FileGenerator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { TemplateRendererPort, CompiledTemplate, TemplateData, TemplateContext } from '../../domain/templates/index.js';
|
|
2
|
+
import type { LoggerPort } from '../../domain/ports.js';
|
|
3
|
+
export declare class HandlebarsRenderer implements TemplateRendererPort {
|
|
4
|
+
private readonly logger;
|
|
5
|
+
private readonly templateCache;
|
|
6
|
+
private readonly handlebarsInstance;
|
|
7
|
+
constructor(logger: LoggerPort);
|
|
8
|
+
compile(template: string, name?: string): Promise<CompiledTemplate>;
|
|
9
|
+
render(templateName: string, data: TemplateData, context: TemplateContext): Promise<string>;
|
|
10
|
+
renderString(template: string, data: TemplateData, context: TemplateContext): Promise<string>;
|
|
11
|
+
registerHelper(name: string, helper: (...args: unknown[]) => unknown): void;
|
|
12
|
+
registerPartial(name: string, template: string): Promise<void>;
|
|
13
|
+
clearCache(): void;
|
|
14
|
+
private registerBuiltInHelpers;
|
|
15
|
+
private getRelativeTime;
|
|
16
|
+
}
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
// Handlebars template renderer implementation
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
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;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
|
+
};
|
|
14
|
+
import { injectable } from 'inversify';
|
|
15
|
+
import Handlebars from 'handlebars';
|
|
16
|
+
import { inject } from 'inversify';
|
|
17
|
+
import { TYPES } from '../di/types.js';
|
|
18
|
+
class HandlebarsCompiledTemplate {
|
|
19
|
+
name;
|
|
20
|
+
template;
|
|
21
|
+
compiledAt;
|
|
22
|
+
handlebarsTemplate;
|
|
23
|
+
constructor(name, template, compiledAt, handlebarsTemplate) {
|
|
24
|
+
this.name = name;
|
|
25
|
+
this.template = template;
|
|
26
|
+
this.compiledAt = compiledAt;
|
|
27
|
+
this.handlebarsTemplate = handlebarsTemplate;
|
|
28
|
+
}
|
|
29
|
+
render(data, context) {
|
|
30
|
+
const combinedData = {
|
|
31
|
+
...data,
|
|
32
|
+
context,
|
|
33
|
+
helpers: {
|
|
34
|
+
formatDate: (date, format = 'YYYY-MM-DD') => {
|
|
35
|
+
const d = typeof date === 'string' ? new Date(date) : date;
|
|
36
|
+
return d.toISOString().split('T')[0];
|
|
37
|
+
},
|
|
38
|
+
uppercase: (str) => str?.toUpperCase() ?? '',
|
|
39
|
+
lowercase: (str) => str?.toLowerCase() ?? '',
|
|
40
|
+
capitalize: (str) => {
|
|
41
|
+
if (!str)
|
|
42
|
+
return '';
|
|
43
|
+
return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
|
|
44
|
+
},
|
|
45
|
+
join: (array, separator = ', ') => {
|
|
46
|
+
if (!Array.isArray(array))
|
|
47
|
+
return '';
|
|
48
|
+
return array.join(separator);
|
|
49
|
+
},
|
|
50
|
+
default: (value, defaultValue) => value ?? defaultValue
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
return this.handlebarsTemplate(combinedData);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
let HandlebarsRenderer = class HandlebarsRenderer {
|
|
57
|
+
logger;
|
|
58
|
+
templateCache = new Map();
|
|
59
|
+
handlebarsInstance;
|
|
60
|
+
constructor(logger) {
|
|
61
|
+
this.logger = logger;
|
|
62
|
+
this.handlebarsInstance = Handlebars.create();
|
|
63
|
+
this.registerBuiltInHelpers();
|
|
64
|
+
}
|
|
65
|
+
async compile(template, name) {
|
|
66
|
+
try {
|
|
67
|
+
const templateName = name ?? `template_${Date.now()}`;
|
|
68
|
+
const handlebarsTemplate = this.handlebarsInstance.compile(template);
|
|
69
|
+
const compiled = new HandlebarsCompiledTemplate(templateName, template, new Date(), handlebarsTemplate);
|
|
70
|
+
if (name) {
|
|
71
|
+
this.templateCache.set(name, compiled);
|
|
72
|
+
}
|
|
73
|
+
this.logger.debug('Template compiled successfully', { name: templateName });
|
|
74
|
+
return compiled;
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
this.logger.error('Template compilation failed', {
|
|
78
|
+
name,
|
|
79
|
+
error: error instanceof Error ? error.message : String(error)
|
|
80
|
+
});
|
|
81
|
+
throw new Error(`Template compilation failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
async render(templateName, data, context) {
|
|
85
|
+
const compiled = this.templateCache.get(templateName);
|
|
86
|
+
if (!compiled) {
|
|
87
|
+
throw new Error(`Template '${templateName}' not found in cache`);
|
|
88
|
+
}
|
|
89
|
+
try {
|
|
90
|
+
const result = compiled.render(data, context);
|
|
91
|
+
this.logger.debug('Template rendered successfully', { templateName });
|
|
92
|
+
return result;
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
this.logger.error('Template rendering failed', {
|
|
96
|
+
templateName,
|
|
97
|
+
error: error instanceof Error ? error.message : String(error)
|
|
98
|
+
});
|
|
99
|
+
throw new Error(`Template rendering failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
async renderString(template, data, context) {
|
|
103
|
+
try {
|
|
104
|
+
const compiled = await this.compile(template);
|
|
105
|
+
return compiled.render(data, context);
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
this.logger.error('String template rendering failed', {
|
|
109
|
+
error: error instanceof Error ? error.message : String(error)
|
|
110
|
+
});
|
|
111
|
+
throw error;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
registerHelper(name, helper) {
|
|
115
|
+
this.handlebarsInstance.registerHelper(name, helper);
|
|
116
|
+
this.logger.debug('Template helper registered', { name });
|
|
117
|
+
}
|
|
118
|
+
async registerPartial(name, template) {
|
|
119
|
+
try {
|
|
120
|
+
this.handlebarsInstance.registerPartial(name, template);
|
|
121
|
+
this.logger.debug('Template partial registered', { name });
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
this.logger.error('Template partial registration failed', {
|
|
125
|
+
name,
|
|
126
|
+
error: error instanceof Error ? error.message : String(error)
|
|
127
|
+
});
|
|
128
|
+
throw new Error(`Partial registration failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
clearCache() {
|
|
132
|
+
this.templateCache.clear();
|
|
133
|
+
this.logger.debug('Template cache cleared');
|
|
134
|
+
}
|
|
135
|
+
registerBuiltInHelpers() {
|
|
136
|
+
// Date formatting helper
|
|
137
|
+
this.registerHelper('formatDate', (date, format = 'YYYY-MM-DD') => {
|
|
138
|
+
if (!date)
|
|
139
|
+
return '';
|
|
140
|
+
const d = typeof date === 'string' ? new Date(date) : date;
|
|
141
|
+
if (isNaN(d.getTime()))
|
|
142
|
+
return '';
|
|
143
|
+
switch (format) {
|
|
144
|
+
case 'YYYY-MM-DD':
|
|
145
|
+
return d.toISOString().split('T')[0];
|
|
146
|
+
case 'MM/DD/YYYY':
|
|
147
|
+
return `${(d.getMonth() + 1).toString().padStart(2, '0')}/${d.getDate().toString().padStart(2, '0')}/${d.getFullYear()}`;
|
|
148
|
+
case 'DD/MM/YYYY':
|
|
149
|
+
return `${d.getDate().toString().padStart(2, '0')}/${(d.getMonth() + 1).toString().padStart(2, '0')}/${d.getFullYear()}`;
|
|
150
|
+
case 'relative':
|
|
151
|
+
return this.getRelativeTime(d);
|
|
152
|
+
default:
|
|
153
|
+
return d.toLocaleDateString();
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
// String manipulation helpers
|
|
157
|
+
this.registerHelper('uppercase', (str) => str?.toUpperCase() ?? '');
|
|
158
|
+
this.registerHelper('lowercase', (str) => str?.toLowerCase() ?? '');
|
|
159
|
+
this.registerHelper('capitalize', (str) => {
|
|
160
|
+
if (!str)
|
|
161
|
+
return '';
|
|
162
|
+
return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
|
|
163
|
+
});
|
|
164
|
+
this.registerHelper('kebabCase', (str) => {
|
|
165
|
+
if (!str)
|
|
166
|
+
return '';
|
|
167
|
+
return str.replace(/\s+/g, '-').toLowerCase();
|
|
168
|
+
});
|
|
169
|
+
this.registerHelper('camelCase', (str) => {
|
|
170
|
+
if (!str)
|
|
171
|
+
return '';
|
|
172
|
+
return str.replace(/[-_\s]+(.)?/g, (_, char) => char ? char.toUpperCase() : '');
|
|
173
|
+
});
|
|
174
|
+
// Array and object helpers
|
|
175
|
+
this.registerHelper('join', (array, separator = ', ') => {
|
|
176
|
+
if (!Array.isArray(array))
|
|
177
|
+
return '';
|
|
178
|
+
return array.join(separator);
|
|
179
|
+
});
|
|
180
|
+
this.registerHelper('length', (value) => {
|
|
181
|
+
if (Array.isArray(value))
|
|
182
|
+
return value.length;
|
|
183
|
+
if (typeof value === 'string')
|
|
184
|
+
return value.length;
|
|
185
|
+
if (value && typeof value === 'object')
|
|
186
|
+
return Object.keys(value).length;
|
|
187
|
+
return 0;
|
|
188
|
+
});
|
|
189
|
+
// Conditional helpers
|
|
190
|
+
this.registerHelper('default', (value, defaultValue) => {
|
|
191
|
+
return value ?? defaultValue;
|
|
192
|
+
});
|
|
193
|
+
this.registerHelper('eq', (a, b) => a === b);
|
|
194
|
+
this.registerHelper('ne', (a, b) => a !== b);
|
|
195
|
+
this.registerHelper('gt', (a, b) => a > b);
|
|
196
|
+
this.registerHelper('lt', (a, b) => a < b);
|
|
197
|
+
this.registerHelper('gte', (a, b) => a >= b);
|
|
198
|
+
this.registerHelper('lte', (a, b) => a <= b);
|
|
199
|
+
// SDD-specific helpers
|
|
200
|
+
this.registerHelper('taskStatus', (completed) => {
|
|
201
|
+
return completed ? '[x]' : '[ ]';
|
|
202
|
+
});
|
|
203
|
+
this.registerHelper('phaseStatus', (phase, currentPhase) => {
|
|
204
|
+
const phases = ['INIT', 'REQUIREMENTS', 'DESIGN', 'TASKS', 'IMPLEMENTATION'];
|
|
205
|
+
const phaseIndex = phases.indexOf(phase);
|
|
206
|
+
const currentIndex = phases.indexOf(currentPhase);
|
|
207
|
+
if (phaseIndex < currentIndex)
|
|
208
|
+
return 'completed';
|
|
209
|
+
if (phaseIndex === currentIndex)
|
|
210
|
+
return 'active';
|
|
211
|
+
return 'pending';
|
|
212
|
+
});
|
|
213
|
+
this.registerHelper('generateId', () => {
|
|
214
|
+
return Math.random().toString(36).substr(2, 9);
|
|
215
|
+
});
|
|
216
|
+
this.registerHelper('indent', (text, spaces = 2) => {
|
|
217
|
+
if (!text)
|
|
218
|
+
return '';
|
|
219
|
+
const indent = ' '.repeat(spaces);
|
|
220
|
+
return text.split('\n').map(line => indent + line).join('\n');
|
|
221
|
+
});
|
|
222
|
+
this.logger.debug('Built-in template helpers registered');
|
|
223
|
+
}
|
|
224
|
+
getRelativeTime(date) {
|
|
225
|
+
const now = new Date();
|
|
226
|
+
const diffMs = now.getTime() - date.getTime();
|
|
227
|
+
const diffSeconds = Math.floor(diffMs / 1000);
|
|
228
|
+
const diffMinutes = Math.floor(diffSeconds / 60);
|
|
229
|
+
const diffHours = Math.floor(diffMinutes / 60);
|
|
230
|
+
const diffDays = Math.floor(diffHours / 24);
|
|
231
|
+
if (diffSeconds < 60)
|
|
232
|
+
return 'just now';
|
|
233
|
+
if (diffMinutes < 60)
|
|
234
|
+
return `${diffMinutes} minutes ago`;
|
|
235
|
+
if (diffHours < 24)
|
|
236
|
+
return `${diffHours} hours ago`;
|
|
237
|
+
if (diffDays < 7)
|
|
238
|
+
return `${diffDays} days ago`;
|
|
239
|
+
if (diffDays < 30)
|
|
240
|
+
return `${Math.floor(diffDays / 7)} weeks ago`;
|
|
241
|
+
if (diffDays < 365)
|
|
242
|
+
return `${Math.floor(diffDays / 30)} months ago`;
|
|
243
|
+
return `${Math.floor(diffDays / 365)} years ago`;
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
HandlebarsRenderer = __decorate([
|
|
247
|
+
injectable(),
|
|
248
|
+
__param(0, inject(TYPES.LoggerPort)),
|
|
249
|
+
__metadata("design:paramtypes", [Object])
|
|
250
|
+
], HandlebarsRenderer);
|
|
251
|
+
export { HandlebarsRenderer };
|
|
252
|
+
//# sourceMappingURL=HandlebarsRenderer.js.map
|