@codemcp/workflows-core 3.1.16
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/.turbo/turbo-build.log +4 -0
- package/LICENSE +674 -0
- package/dist/config-manager.d.ts +24 -0
- package/dist/config-manager.js +68 -0
- package/dist/config-manager.js.map +1 -0
- package/dist/conversation-manager.d.ts +97 -0
- package/dist/conversation-manager.js +367 -0
- package/dist/conversation-manager.js.map +1 -0
- package/dist/database.d.ts +73 -0
- package/dist/database.js +500 -0
- package/dist/database.js.map +1 -0
- package/dist/file-detection-manager.d.ts +53 -0
- package/dist/file-detection-manager.js +221 -0
- package/dist/file-detection-manager.js.map +1 -0
- package/dist/git-manager.d.ts +14 -0
- package/dist/git-manager.js +59 -0
- package/dist/git-manager.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +25 -0
- package/dist/index.js.map +1 -0
- package/dist/instruction-generator.d.ts +69 -0
- package/dist/instruction-generator.js +133 -0
- package/dist/instruction-generator.js.map +1 -0
- package/dist/interaction-logger.d.ts +37 -0
- package/dist/interaction-logger.js +87 -0
- package/dist/interaction-logger.js.map +1 -0
- package/dist/logger.d.ts +64 -0
- package/dist/logger.js +283 -0
- package/dist/logger.js.map +1 -0
- package/dist/path-validation-utils.d.ts +51 -0
- package/dist/path-validation-utils.js +202 -0
- package/dist/path-validation-utils.js.map +1 -0
- package/dist/plan-manager.d.ts +65 -0
- package/dist/plan-manager.js +256 -0
- package/dist/plan-manager.js.map +1 -0
- package/dist/project-docs-manager.d.ts +119 -0
- package/dist/project-docs-manager.js +357 -0
- package/dist/project-docs-manager.js.map +1 -0
- package/dist/state-machine-loader.d.ts +60 -0
- package/dist/state-machine-loader.js +235 -0
- package/dist/state-machine-loader.js.map +1 -0
- package/dist/state-machine-types.d.ts +58 -0
- package/dist/state-machine-types.js +7 -0
- package/dist/state-machine-types.js.map +1 -0
- package/dist/state-machine.d.ts +52 -0
- package/dist/state-machine.js +256 -0
- package/dist/state-machine.js.map +1 -0
- package/dist/system-prompt-generator.d.ts +17 -0
- package/dist/system-prompt-generator.js +113 -0
- package/dist/system-prompt-generator.js.map +1 -0
- package/dist/template-manager.d.ts +61 -0
- package/dist/template-manager.js +229 -0
- package/dist/template-manager.js.map +1 -0
- package/dist/transition-engine.d.ts +70 -0
- package/dist/transition-engine.js +240 -0
- package/dist/transition-engine.js.map +1 -0
- package/dist/types.d.ts +56 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/dist/workflow-manager.d.ts +89 -0
- package/dist/workflow-manager.js +466 -0
- package/dist/workflow-manager.js.map +1 -0
- package/package.json +27 -0
- package/src/config-manager.ts +96 -0
- package/src/conversation-manager.ts +492 -0
- package/src/database.ts +685 -0
- package/src/file-detection-manager.ts +302 -0
- package/src/git-manager.ts +64 -0
- package/src/index.ts +28 -0
- package/src/instruction-generator.ts +210 -0
- package/src/interaction-logger.ts +109 -0
- package/src/logger.ts +353 -0
- package/src/path-validation-utils.ts +261 -0
- package/src/plan-manager.ts +323 -0
- package/src/project-docs-manager.ts +522 -0
- package/src/state-machine-loader.ts +308 -0
- package/src/state-machine-types.ts +72 -0
- package/src/state-machine.ts +370 -0
- package/src/system-prompt-generator.ts +122 -0
- package/src/template-manager.ts +321 -0
- package/src/transition-engine.ts +386 -0
- package/src/types.ts +60 -0
- package/src/workflow-manager.ts +601 -0
- package/test/unit/conversation-manager.test.ts +179 -0
- package/test/unit/custom-workflow-loading.test.ts +174 -0
- package/test/unit/directory-linking-and-extensions.test.ts +338 -0
- package/test/unit/file-linking-integration.test.ts +256 -0
- package/test/unit/git-commit-integration.test.ts +91 -0
- package/test/unit/git-manager.test.ts +86 -0
- package/test/unit/install-workflow.test.ts +138 -0
- package/test/unit/instruction-generator.test.ts +247 -0
- package/test/unit/list-workflows-filtering.test.ts +68 -0
- package/test/unit/none-template-functionality.test.ts +224 -0
- package/test/unit/project-docs-manager.test.ts +337 -0
- package/test/unit/state-machine-loader.test.ts +234 -0
- package/test/unit/template-manager.test.ts +217 -0
- package/test/unit/validate-workflow-name.test.ts +150 -0
- package/test/unit/workflow-domain-filtering.test.ts +75 -0
- package/test/unit/workflow-enum-generation.test.ts +92 -0
- package/test/unit/workflow-manager-enhanced-path-resolution.test.ts +369 -0
- package/test/unit/workflow-manager-path-resolution.test.ts +150 -0
- package/test/unit/workflow-migration.test.ts +155 -0
- package/test/unit/workflow-override-by-name.test.ts +116 -0
- package/test/unit/workflow-prioritization.test.ts +38 -0
- package/test/unit/workflow-validation.test.ts +303 -0
- package/test/utils/e2e-test-setup.ts +453 -0
- package/test/utils/run-server-in-dir.sh +27 -0
- package/test/utils/temp-files.ts +308 -0
- package/test/utils/test-access.ts +79 -0
- package/test/utils/test-helpers.ts +286 -0
- package/test/utils/test-setup.ts +78 -0
- package/tsconfig.build.json +21 -0
- package/tsconfig.json +8 -0
- package/vitest.config.ts +18 -0
@@ -0,0 +1,357 @@
|
|
1
|
+
/**
|
2
|
+
* Project Docs Manager
|
3
|
+
*
|
4
|
+
* Manages project documentation artifacts (architecture.md, requirements.md, design.md)
|
5
|
+
* separate from the workflow-specific plan files. Handles creation, validation, and
|
6
|
+
* path resolution for project documents. Now supports both template creation and
|
7
|
+
* file linking via symlinks.
|
8
|
+
*/
|
9
|
+
import { writeFile, access, mkdir, unlink, symlink, lstat, stat, readdir, } from 'node:fs/promises';
|
10
|
+
import { join, dirname, relative, extname, basename } from 'node:path';
|
11
|
+
import { createLogger } from './logger.js';
|
12
|
+
import { TemplateManager } from './template-manager.js';
|
13
|
+
const logger = createLogger('ProjectDocsManager');
|
14
|
+
export class ProjectDocsManager {
|
15
|
+
templateManager; // Make public for access from other classes
|
16
|
+
constructor() {
|
17
|
+
this.templateManager = new TemplateManager();
|
18
|
+
}
|
19
|
+
/**
|
20
|
+
* Get project docs directory path
|
21
|
+
*/
|
22
|
+
getDocsPath(projectPath) {
|
23
|
+
return join(projectPath, '.vibe', 'docs');
|
24
|
+
}
|
25
|
+
/**
|
26
|
+
* Determine the appropriate extension for a document based on source path
|
27
|
+
*/
|
28
|
+
async getDocumentExtension(sourcePath) {
|
29
|
+
if (!sourcePath) {
|
30
|
+
return '.md'; // Default for templates
|
31
|
+
}
|
32
|
+
try {
|
33
|
+
const stats = await stat(sourcePath);
|
34
|
+
if (stats.isDirectory()) {
|
35
|
+
return ''; // No extension for directories
|
36
|
+
}
|
37
|
+
// For files, preserve the original extension
|
38
|
+
const ext = extname(sourcePath);
|
39
|
+
return ext || '.md'; // Default to .md if no extension
|
40
|
+
}
|
41
|
+
catch (_error) {
|
42
|
+
return '.md'; // Default if we can't stat the source
|
43
|
+
}
|
44
|
+
}
|
45
|
+
/**
|
46
|
+
* Get the target filename for a document type
|
47
|
+
*/
|
48
|
+
async getDocumentFilename(type, sourcePath) {
|
49
|
+
const extension = await this.getDocumentExtension(sourcePath);
|
50
|
+
// Always use the standardized document type name
|
51
|
+
// This ensures consistent symlink names regardless of source path
|
52
|
+
return extension === '' ? type : `${type}${extension}`;
|
53
|
+
}
|
54
|
+
/**
|
55
|
+
* Get paths for all project documents
|
56
|
+
*/
|
57
|
+
getDocumentPaths(projectPath) {
|
58
|
+
const docsPath = this.getDocsPath(projectPath);
|
59
|
+
return {
|
60
|
+
architecture: join(docsPath, 'architecture.md'),
|
61
|
+
requirements: join(docsPath, 'requirements.md'),
|
62
|
+
design: join(docsPath, 'design.md'),
|
63
|
+
};
|
64
|
+
}
|
65
|
+
/**
|
66
|
+
* Get paths for all project documents with dynamic extensions based on source paths
|
67
|
+
*/
|
68
|
+
async getDocumentPathsWithExtensions(projectPath, sourcePaths) {
|
69
|
+
const docsPath = this.getDocsPath(projectPath);
|
70
|
+
const archFilename = await this.getDocumentFilename('architecture', sourcePaths?.architecture);
|
71
|
+
const reqFilename = await this.getDocumentFilename('requirements', sourcePaths?.requirements);
|
72
|
+
const designFilename = await this.getDocumentFilename('design', sourcePaths?.design);
|
73
|
+
return {
|
74
|
+
architecture: join(docsPath, archFilename),
|
75
|
+
requirements: join(docsPath, reqFilename),
|
76
|
+
design: join(docsPath, designFilename),
|
77
|
+
};
|
78
|
+
}
|
79
|
+
/**
|
80
|
+
* Check which project documents exist
|
81
|
+
*/
|
82
|
+
async getProjectDocsInfo(projectPath) {
|
83
|
+
const paths = this.getDocumentPaths(projectPath);
|
84
|
+
const checkExists = async (path) => {
|
85
|
+
try {
|
86
|
+
await access(path);
|
87
|
+
return true;
|
88
|
+
}
|
89
|
+
catch (_error) {
|
90
|
+
return false;
|
91
|
+
}
|
92
|
+
};
|
93
|
+
// Check for documents with different extensions using simple logic
|
94
|
+
const checkExistsWithExtensions = async (basePath, docType) => {
|
95
|
+
// First check the default .md path
|
96
|
+
if (await checkExists(basePath)) {
|
97
|
+
return { exists: true, actualPath: basePath };
|
98
|
+
}
|
99
|
+
// Check for the document type with any extension or as directory
|
100
|
+
const docsDir = dirname(basePath);
|
101
|
+
try {
|
102
|
+
const entries = await readdir(docsDir);
|
103
|
+
// Look for entries that match the document type exactly or start with it
|
104
|
+
for (const entry of entries) {
|
105
|
+
const entryWithoutExt = entry.replace(/\.[^/.]+$/, '');
|
106
|
+
if (entryWithoutExt === docType || entry === docType) {
|
107
|
+
const entryPath = join(docsDir, entry);
|
108
|
+
if (await checkExists(entryPath)) {
|
109
|
+
return { exists: true, actualPath: entryPath };
|
110
|
+
}
|
111
|
+
}
|
112
|
+
}
|
113
|
+
}
|
114
|
+
catch (_error) {
|
115
|
+
// Directory might not exist yet
|
116
|
+
}
|
117
|
+
return { exists: false, actualPath: basePath };
|
118
|
+
};
|
119
|
+
const archResult = await checkExistsWithExtensions(paths.architecture, 'architecture');
|
120
|
+
const reqResult = await checkExistsWithExtensions(paths.requirements, 'requirements');
|
121
|
+
const designResult = await checkExistsWithExtensions(paths.design, 'design');
|
122
|
+
return {
|
123
|
+
architecture: {
|
124
|
+
path: archResult.actualPath,
|
125
|
+
exists: archResult.exists,
|
126
|
+
},
|
127
|
+
requirements: {
|
128
|
+
path: reqResult.actualPath,
|
129
|
+
exists: reqResult.exists,
|
130
|
+
},
|
131
|
+
design: {
|
132
|
+
path: designResult.actualPath,
|
133
|
+
exists: designResult.exists,
|
134
|
+
},
|
135
|
+
};
|
136
|
+
}
|
137
|
+
/**
|
138
|
+
* Create project documents using templates (legacy method for backward compatibility)
|
139
|
+
*/
|
140
|
+
async createProjectDocs(projectPath, options) {
|
141
|
+
const result = await this.createOrLinkProjectDocs(projectPath, options, {});
|
142
|
+
return {
|
143
|
+
created: result.created,
|
144
|
+
skipped: result.skipped,
|
145
|
+
};
|
146
|
+
}
|
147
|
+
/**
|
148
|
+
* Create or link project documents using templates and/or file paths
|
149
|
+
*/
|
150
|
+
async createOrLinkProjectDocs(projectPath, templateOptions, filePaths) {
|
151
|
+
const defaults = await this.templateManager.getDefaults();
|
152
|
+
const finalTemplateOptions = { ...defaults, ...templateOptions };
|
153
|
+
const docsPath = this.getDocsPath(projectPath);
|
154
|
+
// Use dynamic paths that consider source file extensions
|
155
|
+
const paths = await this.getDocumentPathsWithExtensions(projectPath, filePaths);
|
156
|
+
// Check existing documents using the old static paths for backward compatibility
|
157
|
+
const info = await this.getProjectDocsInfo(projectPath);
|
158
|
+
// Ensure docs directory exists
|
159
|
+
await mkdir(docsPath, { recursive: true });
|
160
|
+
const created = [];
|
161
|
+
const linked = [];
|
162
|
+
const skipped = [];
|
163
|
+
// Handle architecture document
|
164
|
+
if (!info.architecture.exists) {
|
165
|
+
if (filePaths?.architecture) {
|
166
|
+
await this.createSymlink(filePaths.architecture, paths.architecture);
|
167
|
+
const filename = basename(paths.architecture);
|
168
|
+
linked.push(filename);
|
169
|
+
}
|
170
|
+
else {
|
171
|
+
await this.createDocument('architecture', finalTemplateOptions.architecture, paths.architecture, docsPath);
|
172
|
+
const filename = basename(paths.architecture);
|
173
|
+
created.push(filename);
|
174
|
+
}
|
175
|
+
}
|
176
|
+
else {
|
177
|
+
skipped.push('architecture.md');
|
178
|
+
}
|
179
|
+
// Handle requirements document
|
180
|
+
if (!info.requirements.exists) {
|
181
|
+
if (filePaths?.requirements) {
|
182
|
+
await this.createSymlink(filePaths.requirements, paths.requirements);
|
183
|
+
const filename = basename(paths.requirements);
|
184
|
+
linked.push(filename);
|
185
|
+
}
|
186
|
+
else {
|
187
|
+
await this.createDocument('requirements', finalTemplateOptions.requirements, paths.requirements, docsPath);
|
188
|
+
const filename = basename(paths.requirements);
|
189
|
+
created.push(filename);
|
190
|
+
}
|
191
|
+
}
|
192
|
+
else {
|
193
|
+
skipped.push('requirements.md');
|
194
|
+
}
|
195
|
+
// Handle design document
|
196
|
+
if (!info.design.exists) {
|
197
|
+
if (filePaths?.design) {
|
198
|
+
await this.createSymlink(filePaths.design, paths.design);
|
199
|
+
const filename = basename(paths.design);
|
200
|
+
linked.push(filename);
|
201
|
+
}
|
202
|
+
else {
|
203
|
+
await this.createDocument('design', finalTemplateOptions.design, paths.design, docsPath);
|
204
|
+
const filename = basename(paths.design);
|
205
|
+
created.push(filename);
|
206
|
+
}
|
207
|
+
}
|
208
|
+
else {
|
209
|
+
skipped.push('design.md');
|
210
|
+
}
|
211
|
+
logger.info('Project docs creation/linking completed', {
|
212
|
+
created,
|
213
|
+
linked,
|
214
|
+
skipped,
|
215
|
+
projectPath,
|
216
|
+
templateOptions: finalTemplateOptions,
|
217
|
+
filePaths,
|
218
|
+
});
|
219
|
+
return { created, linked, skipped };
|
220
|
+
}
|
221
|
+
/**
|
222
|
+
* Create a symlink to an existing file
|
223
|
+
*/
|
224
|
+
async createSymlink(sourcePath, targetPath) {
|
225
|
+
try {
|
226
|
+
// Remove existing file/symlink if it exists
|
227
|
+
await this.removeExistingDocument(targetPath);
|
228
|
+
// Create relative symlink for better portability
|
229
|
+
const targetDir = dirname(targetPath);
|
230
|
+
const relativePath = relative(targetDir, sourcePath);
|
231
|
+
await symlink(relativePath, targetPath);
|
232
|
+
logger.debug('Symlink created successfully', {
|
233
|
+
sourcePath,
|
234
|
+
targetPath,
|
235
|
+
relativePath,
|
236
|
+
});
|
237
|
+
}
|
238
|
+
catch (error) {
|
239
|
+
logger.error('Failed to create symlink', error, {
|
240
|
+
sourcePath,
|
241
|
+
targetPath,
|
242
|
+
});
|
243
|
+
throw new Error(`Failed to create symlink: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
244
|
+
}
|
245
|
+
}
|
246
|
+
/**
|
247
|
+
* Remove existing document or symlink
|
248
|
+
*/
|
249
|
+
async removeExistingDocument(documentPath) {
|
250
|
+
try {
|
251
|
+
const stats = await lstat(documentPath);
|
252
|
+
await unlink(documentPath);
|
253
|
+
logger.debug('Existing document removed', {
|
254
|
+
documentPath,
|
255
|
+
wasSymlink: stats.isSymbolicLink(),
|
256
|
+
});
|
257
|
+
}
|
258
|
+
catch (error) {
|
259
|
+
// File doesn't exist, which is fine
|
260
|
+
if (error instanceof Error &&
|
261
|
+
'code' in error &&
|
262
|
+
error.code !== 'ENOENT') {
|
263
|
+
logger.debug('Failed to remove existing document', {
|
264
|
+
documentPath,
|
265
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
266
|
+
});
|
267
|
+
}
|
268
|
+
}
|
269
|
+
}
|
270
|
+
/**
|
271
|
+
* Create a single document from template
|
272
|
+
*/
|
273
|
+
async createDocument(type, template, documentPath, docsPath) {
|
274
|
+
try {
|
275
|
+
const templateResult = await this.templateManager.loadTemplate(type, template);
|
276
|
+
// Write the main document
|
277
|
+
await writeFile(documentPath, templateResult.content, 'utf-8');
|
278
|
+
// Write additional files (like images for arc42)
|
279
|
+
if (templateResult.additionalFiles) {
|
280
|
+
for (const file of templateResult.additionalFiles) {
|
281
|
+
const filePath = join(docsPath, file.relativePath);
|
282
|
+
const fileDir = dirname(filePath);
|
283
|
+
// Ensure directory exists
|
284
|
+
await mkdir(fileDir, { recursive: true });
|
285
|
+
// Write file
|
286
|
+
await writeFile(filePath, file.content);
|
287
|
+
}
|
288
|
+
}
|
289
|
+
logger.debug(`Created ${type} document`, { documentPath, template });
|
290
|
+
}
|
291
|
+
catch (error) {
|
292
|
+
logger.error(`Failed to create ${type} document`, error, {
|
293
|
+
documentPath,
|
294
|
+
template,
|
295
|
+
});
|
296
|
+
throw error;
|
297
|
+
}
|
298
|
+
}
|
299
|
+
/**
|
300
|
+
* Get variable substitutions for workflow instructions
|
301
|
+
*/
|
302
|
+
getVariableSubstitutions(projectPath) {
|
303
|
+
const paths = this.getDocumentPaths(projectPath);
|
304
|
+
return {
|
305
|
+
$ARCHITECTURE_DOC: paths.architecture,
|
306
|
+
$REQUIREMENTS_DOC: paths.requirements,
|
307
|
+
$DESIGN_DOC: paths.design,
|
308
|
+
};
|
309
|
+
}
|
310
|
+
/**
|
311
|
+
* Get variable substitutions for workflow instructions with dynamic paths
|
312
|
+
*/
|
313
|
+
async getVariableSubstitutionsWithExtensions(projectPath, sourcePaths) {
|
314
|
+
const paths = await this.getDocumentPathsWithExtensions(projectPath, sourcePaths);
|
315
|
+
return {
|
316
|
+
$ARCHITECTURE_DOC: paths.architecture,
|
317
|
+
$REQUIREMENTS_DOC: paths.requirements,
|
318
|
+
$DESIGN_DOC: paths.design,
|
319
|
+
};
|
320
|
+
}
|
321
|
+
/**
|
322
|
+
* Read a project document - returns the path for LLM to read as needed
|
323
|
+
*/
|
324
|
+
async readDocument(projectPath, type) {
|
325
|
+
// Use the dynamic path detection to get the actual document path
|
326
|
+
const docsInfo = await this.getProjectDocsInfo(projectPath);
|
327
|
+
const documentPath = docsInfo[type].path;
|
328
|
+
if (!docsInfo[type].exists) {
|
329
|
+
throw new Error(`${type} document not found: ${documentPath}`);
|
330
|
+
}
|
331
|
+
// Return the pure path for the LLM to read as needed
|
332
|
+
// This is more efficient for large documents and gives LLM full control
|
333
|
+
return documentPath;
|
334
|
+
}
|
335
|
+
/**
|
336
|
+
* Check if all project documents exist
|
337
|
+
*/
|
338
|
+
async allDocumentsExist(projectPath) {
|
339
|
+
const info = await this.getProjectDocsInfo(projectPath);
|
340
|
+
return (info.architecture.exists && info.requirements.exists && info.design.exists);
|
341
|
+
}
|
342
|
+
/**
|
343
|
+
* Check if a document is a symlink
|
344
|
+
*/
|
345
|
+
async isSymlink(projectPath, type) {
|
346
|
+
const paths = this.getDocumentPaths(projectPath);
|
347
|
+
const documentPath = paths[type];
|
348
|
+
try {
|
349
|
+
const stats = await lstat(documentPath);
|
350
|
+
return stats.isSymbolicLink();
|
351
|
+
}
|
352
|
+
catch (_error) {
|
353
|
+
return false;
|
354
|
+
}
|
355
|
+
}
|
356
|
+
}
|
357
|
+
//# sourceMappingURL=project-docs-manager.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"project-docs-manager.js","sourceRoot":"","sources":["../src/project-docs-manager.ts"],"names":[],"mappings":"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"}
|
@@ -0,0 +1,60 @@
|
|
1
|
+
/**
|
2
|
+
* State Machine Loader
|
3
|
+
*
|
4
|
+
* Loads and validates YAML-based state machine definitions
|
5
|
+
*/
|
6
|
+
import { YamlStateMachine, YamlTransition } from './state-machine-types.js';
|
7
|
+
/**
|
8
|
+
* Loads and manages YAML-based state machine definitions
|
9
|
+
*/
|
10
|
+
export declare class StateMachineLoader {
|
11
|
+
private stateMachine;
|
12
|
+
private validPhases;
|
13
|
+
/**
|
14
|
+
* Get all valid phases from the loaded state machine
|
15
|
+
*/
|
16
|
+
getValidPhases(): string[];
|
17
|
+
/**
|
18
|
+
* Get the initial state from the loaded state machine
|
19
|
+
*/
|
20
|
+
getInitialState(): string;
|
21
|
+
/**
|
22
|
+
* Load state machine from YAML file
|
23
|
+
*
|
24
|
+
* Checks for custom state machine file in project directory first,
|
25
|
+
* then falls back to waterfall workflow as default
|
26
|
+
*/
|
27
|
+
loadStateMachine(projectPath: string): YamlStateMachine;
|
28
|
+
/**
|
29
|
+
* Load state machine from specific file path
|
30
|
+
*/
|
31
|
+
loadFromFile(filePath: string): YamlStateMachine;
|
32
|
+
/**
|
33
|
+
* Validate the state machine structure and references
|
34
|
+
*/
|
35
|
+
private validateStateMachine;
|
36
|
+
/**
|
37
|
+
* Get transition instructions for a specific state change
|
38
|
+
*/
|
39
|
+
getTransitionInstructions(fromState: string, toState: string, trigger?: string): {
|
40
|
+
instructions: string;
|
41
|
+
transitionReason: string;
|
42
|
+
isModeled: boolean;
|
43
|
+
};
|
44
|
+
/**
|
45
|
+
* Get all possible transitions from a given state
|
46
|
+
*/
|
47
|
+
getPossibleTransitions(fromState: string): YamlTransition[];
|
48
|
+
/**
|
49
|
+
* Check if a transition is modeled (shown in state diagram)
|
50
|
+
*/
|
51
|
+
isModeledTransition(fromState: string, toState: string): boolean;
|
52
|
+
/**
|
53
|
+
* Check if a phase is valid in the current state machine
|
54
|
+
*/
|
55
|
+
isValidPhase(phase: string): boolean;
|
56
|
+
/**
|
57
|
+
* Get phase-specific instructions for continuing work in current phase
|
58
|
+
*/
|
59
|
+
getContinuePhaseInstructions(phase: string): string;
|
60
|
+
}
|
@@ -0,0 +1,235 @@
|
|
1
|
+
/**
|
2
|
+
* State Machine Loader
|
3
|
+
*
|
4
|
+
* Loads and validates YAML-based state machine definitions
|
5
|
+
*/
|
6
|
+
import fs from 'node:fs';
|
7
|
+
import yaml from 'js-yaml';
|
8
|
+
import path from 'node:path';
|
9
|
+
import { fileURLToPath } from 'node:url';
|
10
|
+
import { createLogger } from './logger.js';
|
11
|
+
const logger = createLogger('StateMachineLoader');
|
12
|
+
/**
|
13
|
+
* Loads and manages YAML-based state machine definitions
|
14
|
+
*/
|
15
|
+
export class StateMachineLoader {
|
16
|
+
stateMachine = null;
|
17
|
+
validPhases = new Set();
|
18
|
+
/**
|
19
|
+
* Get all valid phases from the loaded state machine
|
20
|
+
*/
|
21
|
+
getValidPhases() {
|
22
|
+
if (!this.stateMachine) {
|
23
|
+
throw new Error('State machine not loaded');
|
24
|
+
}
|
25
|
+
return Array.from(this.validPhases);
|
26
|
+
}
|
27
|
+
/**
|
28
|
+
* Get the initial state from the loaded state machine
|
29
|
+
*/
|
30
|
+
getInitialState() {
|
31
|
+
if (!this.stateMachine) {
|
32
|
+
throw new Error('State machine not loaded');
|
33
|
+
}
|
34
|
+
return this.stateMachine.initial_state;
|
35
|
+
}
|
36
|
+
/**
|
37
|
+
* Load state machine from YAML file
|
38
|
+
*
|
39
|
+
* Checks for custom state machine file in project directory first,
|
40
|
+
* then falls back to waterfall workflow as default
|
41
|
+
*/
|
42
|
+
loadStateMachine(projectPath) {
|
43
|
+
// Check for custom state machine file in project directory
|
44
|
+
const customFilePaths = [
|
45
|
+
path.join(projectPath, '.vibe', 'workflow.yaml'),
|
46
|
+
path.join(projectPath, '.vibe', 'workflow.yml'),
|
47
|
+
];
|
48
|
+
// Try to load custom state machine file
|
49
|
+
for (const filePath of customFilePaths) {
|
50
|
+
if (fs.existsSync(filePath)) {
|
51
|
+
logger.info('Loading custom state machine file', { filePath });
|
52
|
+
try {
|
53
|
+
return this.loadFromFile(filePath);
|
54
|
+
}
|
55
|
+
catch (error) {
|
56
|
+
logger.warn('Failed to load custom state machine, falling back to default', {
|
57
|
+
filePath,
|
58
|
+
error: error.message,
|
59
|
+
});
|
60
|
+
// Continue to try next file or fall back to default
|
61
|
+
}
|
62
|
+
}
|
63
|
+
}
|
64
|
+
// Fall back to waterfall workflow as default
|
65
|
+
// Use import.meta.url to get the current file's path in ESM
|
66
|
+
const currentFileUrl = import.meta.url;
|
67
|
+
const currentFilePath = fileURLToPath(currentFileUrl);
|
68
|
+
// Go up from packages/core/dist/ to project root (4 levels up)
|
69
|
+
const projectRoot = path.dirname(path.dirname(path.dirname(path.dirname(currentFilePath))));
|
70
|
+
const defaultFilePath = path.join(projectRoot, 'resources', 'workflows', 'waterfall.yaml');
|
71
|
+
logger.info('Loading default state machine file', { defaultFilePath });
|
72
|
+
return this.loadFromFile(defaultFilePath);
|
73
|
+
}
|
74
|
+
/**
|
75
|
+
* Load state machine from specific file path
|
76
|
+
*/
|
77
|
+
loadFromFile(filePath) {
|
78
|
+
try {
|
79
|
+
const yamlContent = fs.readFileSync(path.resolve(filePath), 'utf8');
|
80
|
+
const stateMachine = yaml.load(yamlContent);
|
81
|
+
// Validate the state machine
|
82
|
+
this.validateStateMachine(stateMachine);
|
83
|
+
// Store valid phases for later validation
|
84
|
+
this.validPhases = new Set(Object.keys(stateMachine.states));
|
85
|
+
this.stateMachine = stateMachine;
|
86
|
+
logger.info('State machine loaded successfully', {
|
87
|
+
name: stateMachine.name,
|
88
|
+
stateCount: Object.keys(stateMachine.states).length,
|
89
|
+
phases: Array.from(this.validPhases),
|
90
|
+
});
|
91
|
+
return stateMachine;
|
92
|
+
}
|
93
|
+
catch (error) {
|
94
|
+
logger.error('Failed to load state machine', error);
|
95
|
+
throw new Error(`Failed to load state machine: ${error.message}`);
|
96
|
+
}
|
97
|
+
}
|
98
|
+
/**
|
99
|
+
* Validate the state machine structure and references
|
100
|
+
*/
|
101
|
+
validateStateMachine(stateMachine) {
|
102
|
+
// Check required properties
|
103
|
+
if (!stateMachine.name ||
|
104
|
+
!stateMachine.description ||
|
105
|
+
!stateMachine.initial_state ||
|
106
|
+
!stateMachine.states) {
|
107
|
+
throw new Error('State machine is missing required properties');
|
108
|
+
}
|
109
|
+
// Get all state names
|
110
|
+
const stateNames = Object.keys(stateMachine.states);
|
111
|
+
// Check initial state is valid
|
112
|
+
if (!stateNames.includes(stateMachine.initial_state)) {
|
113
|
+
throw new Error(`Initial state "${stateMachine.initial_state}" is not defined in states`);
|
114
|
+
}
|
115
|
+
// Validate states and transitions
|
116
|
+
for (const [stateName, state] of Object.entries(stateMachine.states)) {
|
117
|
+
// Check required state properties
|
118
|
+
if (!state.description || !state.default_instructions) {
|
119
|
+
throw new Error(`State "${stateName}" is missing required properties (description or default_instructions)`);
|
120
|
+
}
|
121
|
+
if (!state.transitions || !Array.isArray(state.transitions)) {
|
122
|
+
throw new Error(`State "${stateName}" has invalid transitions property`);
|
123
|
+
}
|
124
|
+
for (const transition of state.transitions) {
|
125
|
+
if (!stateNames.includes(transition.to)) {
|
126
|
+
throw new Error(`State "${stateName}" has transition to unknown state "${transition.to}"`);
|
127
|
+
}
|
128
|
+
if (!transition.transition_reason) {
|
129
|
+
throw new Error(`Transition from "${stateName}" to "${transition.to}" is missing transition_reason`);
|
130
|
+
}
|
131
|
+
}
|
132
|
+
}
|
133
|
+
logger.debug('State machine validation successful');
|
134
|
+
}
|
135
|
+
/**
|
136
|
+
* Get transition instructions for a specific state change
|
137
|
+
*/
|
138
|
+
getTransitionInstructions(fromState, toState, trigger) {
|
139
|
+
if (!this.stateMachine) {
|
140
|
+
throw new Error('State machine not loaded');
|
141
|
+
}
|
142
|
+
// Get target state definition
|
143
|
+
const targetState = this.stateMachine.states[toState];
|
144
|
+
if (!targetState) {
|
145
|
+
throw new Error(`Target state "${toState}" not found`);
|
146
|
+
}
|
147
|
+
// Look for a modeled transition first
|
148
|
+
const fromStateDefinition = this.stateMachine.states[fromState];
|
149
|
+
if (fromStateDefinition) {
|
150
|
+
const transition = fromStateDefinition.transitions.find(t => t.to === toState && (!trigger || t.trigger === trigger));
|
151
|
+
if (transition) {
|
152
|
+
// For modeled transitions, compose instructions
|
153
|
+
let composedInstructions = targetState.default_instructions;
|
154
|
+
// If transition has specific instructions, use those instead of default
|
155
|
+
if (transition.instructions) {
|
156
|
+
composedInstructions = transition.instructions;
|
157
|
+
}
|
158
|
+
// If transition has additional instructions, combine them
|
159
|
+
if (transition.additional_instructions) {
|
160
|
+
composedInstructions = `${composedInstructions}\n\n**Additional Context:**\n${transition.additional_instructions}`;
|
161
|
+
}
|
162
|
+
return {
|
163
|
+
instructions: composedInstructions,
|
164
|
+
transitionReason: transition.transition_reason,
|
165
|
+
isModeled: true,
|
166
|
+
};
|
167
|
+
}
|
168
|
+
}
|
169
|
+
// Fall back to target state's default instructions for unmodeled transitions
|
170
|
+
return {
|
171
|
+
instructions: targetState.default_instructions,
|
172
|
+
transitionReason: `Direct transition to ${toState} phase`,
|
173
|
+
isModeled: false,
|
174
|
+
};
|
175
|
+
}
|
176
|
+
/**
|
177
|
+
* Get all possible transitions from a given state
|
178
|
+
*/
|
179
|
+
getPossibleTransitions(fromState) {
|
180
|
+
if (!this.stateMachine) {
|
181
|
+
throw new Error('State machine not loaded');
|
182
|
+
}
|
183
|
+
const stateDefinition = this.stateMachine.states[fromState];
|
184
|
+
return stateDefinition ? stateDefinition.transitions : [];
|
185
|
+
}
|
186
|
+
/**
|
187
|
+
* Check if a transition is modeled (shown in state diagram)
|
188
|
+
*/
|
189
|
+
isModeledTransition(fromState, toState) {
|
190
|
+
if (!this.stateMachine) {
|
191
|
+
throw new Error('State machine not loaded');
|
192
|
+
}
|
193
|
+
const stateDefinition = this.stateMachine.states[fromState];
|
194
|
+
if (!stateDefinition)
|
195
|
+
return false;
|
196
|
+
return stateDefinition.transitions.some(t => t.to === toState);
|
197
|
+
}
|
198
|
+
/**
|
199
|
+
* Check if a phase is valid in the current state machine
|
200
|
+
*/
|
201
|
+
isValidPhase(phase) {
|
202
|
+
return this.validPhases.has(phase);
|
203
|
+
}
|
204
|
+
/**
|
205
|
+
* Get phase-specific instructions for continuing work in current phase
|
206
|
+
*/
|
207
|
+
getContinuePhaseInstructions(phase) {
|
208
|
+
if (!this.stateMachine) {
|
209
|
+
throw new Error('State machine not loaded');
|
210
|
+
}
|
211
|
+
const stateDefinition = this.stateMachine.states[phase];
|
212
|
+
if (!stateDefinition) {
|
213
|
+
logger.error('Unknown phase', new Error(`Unknown phase: ${phase}`));
|
214
|
+
throw new Error(`Unknown phase: ${phase}`);
|
215
|
+
}
|
216
|
+
// Look for a self-transition (continue in same phase)
|
217
|
+
const continueTransition = stateDefinition.transitions.find(t => t.to === phase);
|
218
|
+
if (continueTransition) {
|
219
|
+
// Compose instructions for continue transition
|
220
|
+
let composedInstructions = stateDefinition.default_instructions;
|
221
|
+
// If transition has specific instructions, use those instead of default
|
222
|
+
if (continueTransition.instructions) {
|
223
|
+
composedInstructions = continueTransition.instructions;
|
224
|
+
}
|
225
|
+
// If transition has additional instructions, combine them
|
226
|
+
if (continueTransition.additional_instructions) {
|
227
|
+
composedInstructions = `${composedInstructions}\n\n**Additional Context:**\n${continueTransition.additional_instructions}`;
|
228
|
+
}
|
229
|
+
return composedInstructions;
|
230
|
+
}
|
231
|
+
// Fall back to default instructions for the phase
|
232
|
+
return stateDefinition.default_instructions;
|
233
|
+
}
|
234
|
+
}
|
235
|
+
//# sourceMappingURL=state-machine-loader.js.map
|