@codemcp/workflows-core 3.1.22 → 3.2.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/package.json +8 -3
- package/resources/templates/architecture/arc42/arc42-template-EN.md +1077 -0
- package/resources/templates/architecture/arc42/images/01_2_iso-25010-topics-EN.drawio-2023.png +0 -0
- package/resources/templates/architecture/arc42/images/01_2_iso-25010-topics-EN.drawio.png +0 -0
- package/resources/templates/architecture/arc42/images/05_building_blocks-EN.png +0 -0
- package/resources/templates/architecture/arc42/images/08-concepts-EN.drawio.png +0 -0
- package/resources/templates/architecture/arc42/images/arc42-logo.png +0 -0
- package/resources/templates/architecture/c4.md +224 -0
- package/resources/templates/architecture/freestyle.md +53 -0
- package/resources/templates/architecture/none.md +17 -0
- package/resources/templates/design/comprehensive.md +207 -0
- package/resources/templates/design/freestyle.md +37 -0
- package/resources/templates/design/none.md +17 -0
- package/resources/templates/requirements/ears.md +90 -0
- package/resources/templates/requirements/freestyle.md +42 -0
- package/resources/templates/requirements/none.md +17 -0
- package/resources/workflows/big-bang-conversion.yaml +539 -0
- package/resources/workflows/boundary-testing.yaml +334 -0
- package/resources/workflows/bugfix.yaml +185 -0
- package/resources/workflows/business-analysis.yaml +671 -0
- package/resources/workflows/c4-analysis.yaml +485 -0
- package/resources/workflows/epcc.yaml +161 -0
- package/resources/workflows/greenfield.yaml +189 -0
- package/resources/workflows/minor.yaml +127 -0
- package/resources/workflows/posts.yaml +207 -0
- package/resources/workflows/slides.yaml +256 -0
- package/resources/workflows/tdd.yaml +157 -0
- package/resources/workflows/waterfall.yaml +195 -0
- package/.turbo/turbo-build.log +0 -4
- package/src/config-manager.ts +0 -96
- package/src/conversation-manager.ts +0 -489
- package/src/database.ts +0 -427
- package/src/file-detection-manager.ts +0 -302
- package/src/git-manager.ts +0 -64
- package/src/index.ts +0 -28
- package/src/instruction-generator.ts +0 -210
- package/src/interaction-logger.ts +0 -109
- package/src/logger.ts +0 -353
- package/src/path-validation-utils.ts +0 -261
- package/src/plan-manager.ts +0 -323
- package/src/project-docs-manager.ts +0 -523
- package/src/state-machine-loader.ts +0 -365
- package/src/state-machine-types.ts +0 -72
- package/src/state-machine.ts +0 -370
- package/src/system-prompt-generator.ts +0 -122
- package/src/template-manager.ts +0 -328
- package/src/transition-engine.ts +0 -386
- package/src/types.ts +0 -60
- package/src/workflow-manager.ts +0 -606
- package/test/unit/conversation-manager.test.ts +0 -179
- package/test/unit/custom-workflow-loading.test.ts +0 -174
- package/test/unit/directory-linking-and-extensions.test.ts +0 -338
- package/test/unit/file-linking-integration.test.ts +0 -256
- package/test/unit/git-commit-integration.test.ts +0 -91
- package/test/unit/git-manager.test.ts +0 -86
- package/test/unit/install-workflow.test.ts +0 -138
- package/test/unit/instruction-generator.test.ts +0 -247
- package/test/unit/list-workflows-filtering.test.ts +0 -68
- package/test/unit/none-template-functionality.test.ts +0 -224
- package/test/unit/project-docs-manager.test.ts +0 -337
- package/test/unit/state-machine-loader.test.ts +0 -234
- package/test/unit/template-manager.test.ts +0 -217
- package/test/unit/validate-workflow-name.test.ts +0 -150
- package/test/unit/workflow-domain-filtering.test.ts +0 -75
- package/test/unit/workflow-enum-generation.test.ts +0 -92
- package/test/unit/workflow-manager-enhanced-path-resolution.test.ts +0 -369
- package/test/unit/workflow-manager-path-resolution.test.ts +0 -150
- package/test/unit/workflow-migration.test.ts +0 -155
- package/test/unit/workflow-override-by-name.test.ts +0 -116
- package/test/unit/workflow-prioritization.test.ts +0 -38
- package/test/unit/workflow-validation.test.ts +0 -303
- package/test/utils/e2e-test-setup.ts +0 -453
- package/test/utils/run-server-in-dir.sh +0 -27
- package/test/utils/temp-files.ts +0 -308
- package/test/utils/test-access.ts +0 -79
- package/test/utils/test-helpers.ts +0 -286
- package/test/utils/test-setup.ts +0 -78
- package/tsconfig.build.json +0 -21
- package/tsconfig.json +0 -8
- package/vitest.config.ts +0 -18
@@ -1,369 +0,0 @@
|
|
1
|
-
/**
|
2
|
-
* Enhanced unit tests for WorkflowManager path resolution strategies
|
3
|
-
*
|
4
|
-
* Tests the comprehensive workflow directory finding functionality
|
5
|
-
* including npx scenarios and various npm installation patterns
|
6
|
-
*/
|
7
|
-
|
8
|
-
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
9
|
-
import { existsSync, writeFileSync, mkdirSync, rmSync } from 'node:fs';
|
10
|
-
import { resolve, join } from 'node:path';
|
11
|
-
import { tmpdir } from 'node:os';
|
12
|
-
import { WorkflowManager } from '@codemcp/workflows-core';
|
13
|
-
|
14
|
-
describe('WorkflowManager enhanced path resolution', () => {
|
15
|
-
let tempDir: string;
|
16
|
-
let originalNodePath: string | undefined;
|
17
|
-
let originalHome: string | undefined;
|
18
|
-
let originalUserProfile: string | undefined;
|
19
|
-
|
20
|
-
beforeEach(() => {
|
21
|
-
// Create temporary directory for testing
|
22
|
-
tempDir = resolve(tmpdir(), `workflow-enhanced-test-${Date.now()}`);
|
23
|
-
mkdirSync(tempDir, { recursive: true });
|
24
|
-
|
25
|
-
// Store original environment variables
|
26
|
-
originalNodePath = process.env.NODE_PATH;
|
27
|
-
originalHome = process.env.HOME;
|
28
|
-
originalUserProfile = process.env.USERPROFILE;
|
29
|
-
});
|
30
|
-
|
31
|
-
afterEach(() => {
|
32
|
-
// Clean up temporary directory
|
33
|
-
if (existsSync(tempDir)) {
|
34
|
-
rmSync(tempDir, { recursive: true, force: true });
|
35
|
-
}
|
36
|
-
|
37
|
-
// Restore environment variables
|
38
|
-
if (originalNodePath !== undefined) {
|
39
|
-
process.env.NODE_PATH = originalNodePath;
|
40
|
-
} else {
|
41
|
-
delete process.env.NODE_PATH;
|
42
|
-
}
|
43
|
-
|
44
|
-
if (originalHome !== undefined) {
|
45
|
-
process.env.HOME = originalHome;
|
46
|
-
} else {
|
47
|
-
delete process.env.HOME;
|
48
|
-
}
|
49
|
-
|
50
|
-
if (originalUserProfile !== undefined) {
|
51
|
-
process.env.USERPROFILE = originalUserProfile;
|
52
|
-
} else {
|
53
|
-
delete process.env.USERPROFILE;
|
54
|
-
}
|
55
|
-
});
|
56
|
-
|
57
|
-
function createWorkflowFile(dir: string, name: string = 'test.yaml') {
|
58
|
-
const content = `name: ${name.replace('.yaml', '')}
|
59
|
-
description: Test workflow
|
60
|
-
initial_state: start
|
61
|
-
states:
|
62
|
-
start:
|
63
|
-
description: Test state
|
64
|
-
instructions: Test instructions
|
65
|
-
transitions:
|
66
|
-
- to: end
|
67
|
-
condition: always
|
68
|
-
end:
|
69
|
-
description: End state
|
70
|
-
instructions: Complete
|
71
|
-
`;
|
72
|
-
writeFileSync(join(dir, name), content);
|
73
|
-
}
|
74
|
-
|
75
|
-
function createPackageJson(
|
76
|
-
dir: string,
|
77
|
-
name: string = 'responsible-vibe-mcp'
|
78
|
-
) {
|
79
|
-
const packageJson = {
|
80
|
-
name,
|
81
|
-
version: '1.0.0',
|
82
|
-
description: 'Test package',
|
83
|
-
};
|
84
|
-
writeFileSync(
|
85
|
-
join(dir, 'package.json'),
|
86
|
-
JSON.stringify(packageJson, null, 2)
|
87
|
-
);
|
88
|
-
}
|
89
|
-
|
90
|
-
describe('Strategy 1: Relative to current file', () => {
|
91
|
-
it('should find workflows relative to dist directory', () => {
|
92
|
-
// Simulate dist/workflow-manager.js -> ../resources/workflows structure
|
93
|
-
const projectRoot = join(tempDir, 'project');
|
94
|
-
const distDir = join(projectRoot, 'dist');
|
95
|
-
const workflowsDir = join(projectRoot, 'resources', 'workflows');
|
96
|
-
|
97
|
-
mkdirSync(distDir, { recursive: true });
|
98
|
-
mkdirSync(workflowsDir, { recursive: true });
|
99
|
-
|
100
|
-
createWorkflowFile(workflowsDir, 'waterfall.yaml');
|
101
|
-
createPackageJson(projectRoot);
|
102
|
-
|
103
|
-
const workflowManager = new WorkflowManager();
|
104
|
-
const workflows = workflowManager.getAvailableWorkflows();
|
105
|
-
|
106
|
-
// Should find at least the predefined workflows
|
107
|
-
expect(workflows.length).toBeGreaterThan(0);
|
108
|
-
const workflowNames = workflows.map(w => w.name);
|
109
|
-
expect(workflowNames).toContain('waterfall');
|
110
|
-
});
|
111
|
-
});
|
112
|
-
|
113
|
-
describe('Strategy 2: Package root discovery', () => {
|
114
|
-
it('should find workflows by traversing up to package.json', () => {
|
115
|
-
// Create nested structure where we need to traverse up
|
116
|
-
const projectRoot = join(tempDir, 'project');
|
117
|
-
const deepDir = join(projectRoot, 'dist', 'server', 'handlers');
|
118
|
-
const workflowsDir = join(projectRoot, 'resources', 'workflows');
|
119
|
-
|
120
|
-
mkdirSync(deepDir, { recursive: true });
|
121
|
-
mkdirSync(workflowsDir, { recursive: true });
|
122
|
-
|
123
|
-
createWorkflowFile(workflowsDir, 'epcc.yaml');
|
124
|
-
createPackageJson(projectRoot);
|
125
|
-
|
126
|
-
const workflowManager = new WorkflowManager();
|
127
|
-
const workflows = workflowManager.getAvailableWorkflows();
|
128
|
-
|
129
|
-
expect(workflows.length).toBeGreaterThan(0);
|
130
|
-
const workflowNames = workflows.map(w => w.name);
|
131
|
-
expect(workflowNames).toContain('epcc');
|
132
|
-
});
|
133
|
-
|
134
|
-
it('should ignore package.json files from other packages', () => {
|
135
|
-
// Create structure with multiple package.json files
|
136
|
-
const projectRoot = join(tempDir, 'project');
|
137
|
-
const nodeModulesDir = join(projectRoot, 'node_modules', 'other-package');
|
138
|
-
const workflowsDir = join(projectRoot, 'resources', 'workflows');
|
139
|
-
|
140
|
-
mkdirSync(nodeModulesDir, { recursive: true });
|
141
|
-
mkdirSync(workflowsDir, { recursive: true });
|
142
|
-
|
143
|
-
createWorkflowFile(workflowsDir, 'bugfix.yaml');
|
144
|
-
createPackageJson(projectRoot); // Our package
|
145
|
-
createPackageJson(nodeModulesDir, 'other-package'); // Different package
|
146
|
-
|
147
|
-
const workflowManager = new WorkflowManager();
|
148
|
-
const workflows = workflowManager.getAvailableWorkflows();
|
149
|
-
|
150
|
-
expect(workflows.length).toBeGreaterThan(0);
|
151
|
-
const workflowNames = workflows.map(w => w.name);
|
152
|
-
expect(workflowNames).toContain('bugfix');
|
153
|
-
});
|
154
|
-
});
|
155
|
-
|
156
|
-
describe('Strategy 3: Common npm installation paths', () => {
|
157
|
-
it('should find workflows in local node_modules', () => {
|
158
|
-
// Note: We can't actually create files in the real node_modules during tests,
|
159
|
-
// but we can verify the path resolution logic works
|
160
|
-
const workflowManager = new WorkflowManager();
|
161
|
-
const workflows = workflowManager.getAvailableWorkflows();
|
162
|
-
|
163
|
-
// Should still find predefined workflows from the actual package
|
164
|
-
expect(workflows.length).toBeGreaterThan(0);
|
165
|
-
});
|
166
|
-
|
167
|
-
it('should handle NODE_PATH environment variable', () => {
|
168
|
-
// Set up NODE_PATH scenario
|
169
|
-
const nodePath = join(tempDir, 'global-modules');
|
170
|
-
const packageDir = join(nodePath, 'responsible-vibe-mcp');
|
171
|
-
const workflowsDir = join(packageDir, 'resources', 'workflows');
|
172
|
-
|
173
|
-
mkdirSync(workflowsDir, { recursive: true });
|
174
|
-
createWorkflowFile(workflowsDir, 'greenfield.yaml');
|
175
|
-
createPackageJson(packageDir);
|
176
|
-
|
177
|
-
// Set NODE_PATH
|
178
|
-
process.env.NODE_PATH = nodePath;
|
179
|
-
|
180
|
-
const workflowManager = new WorkflowManager();
|
181
|
-
const workflows = workflowManager.getAvailableWorkflows();
|
182
|
-
|
183
|
-
expect(workflows.length).toBeGreaterThan(0);
|
184
|
-
const workflowNames = workflows.map(w => w.name);
|
185
|
-
expect(workflowNames).toContain('greenfield');
|
186
|
-
});
|
187
|
-
});
|
188
|
-
|
189
|
-
describe('Strategy 4: npx cache locations', () => {
|
190
|
-
it('should search in HOME-based npx cache directories', () => {
|
191
|
-
// Simulate npx cache structure
|
192
|
-
const homeDir = join(tempDir, 'home');
|
193
|
-
const npxCacheDir = join(homeDir, '.npm/_npx');
|
194
|
-
const cacheEntry = join(npxCacheDir, 'abc123');
|
195
|
-
const packageDir = join(
|
196
|
-
cacheEntry,
|
197
|
-
'node_modules',
|
198
|
-
'responsible-vibe-mcp'
|
199
|
-
);
|
200
|
-
const workflowsDir = join(packageDir, 'resources', 'workflows');
|
201
|
-
|
202
|
-
mkdirSync(workflowsDir, { recursive: true });
|
203
|
-
createWorkflowFile(workflowsDir, 'minor.yaml');
|
204
|
-
createPackageJson(packageDir);
|
205
|
-
|
206
|
-
// Set HOME environment variable
|
207
|
-
process.env.HOME = homeDir;
|
208
|
-
|
209
|
-
const workflowManager = new WorkflowManager();
|
210
|
-
const workflows = workflowManager.getAvailableWorkflows();
|
211
|
-
|
212
|
-
expect(workflows.length).toBeGreaterThan(0);
|
213
|
-
const workflowNames = workflows.map(w => w.name);
|
214
|
-
expect(workflowNames).toContain('minor');
|
215
|
-
});
|
216
|
-
|
217
|
-
it('should search in USERPROFILE-based cache directories (Windows)', () => {
|
218
|
-
// Simulate Windows npx cache structure
|
219
|
-
const userProfile = join(tempDir, 'Users', 'testuser');
|
220
|
-
const npxCacheDir = join(userProfile, 'AppData/Local/npm-cache/_npx');
|
221
|
-
const cacheEntry = join(npxCacheDir, 'def456');
|
222
|
-
const packageDir = join(cacheEntry, 'responsible-vibe-mcp');
|
223
|
-
const workflowsDir = join(packageDir, 'resources', 'workflows');
|
224
|
-
|
225
|
-
mkdirSync(workflowsDir, { recursive: true });
|
226
|
-
createWorkflowFile(workflowsDir, 'waterfall.yaml');
|
227
|
-
createPackageJson(packageDir);
|
228
|
-
|
229
|
-
// Set USERPROFILE environment variable (Windows)
|
230
|
-
process.env.USERPROFILE = userProfile;
|
231
|
-
delete process.env.HOME; // Remove HOME to simulate Windows
|
232
|
-
|
233
|
-
const workflowManager = new WorkflowManager();
|
234
|
-
const workflows = workflowManager.getAvailableWorkflows();
|
235
|
-
|
236
|
-
expect(workflows.length).toBeGreaterThan(0);
|
237
|
-
const workflowNames = workflows.map(w => w.name);
|
238
|
-
expect(workflowNames).toContain('waterfall');
|
239
|
-
});
|
240
|
-
|
241
|
-
it('should handle macOS Library cache directories', () => {
|
242
|
-
// Simulate macOS cache structure
|
243
|
-
const homeDir = join(tempDir, 'Users', 'testuser');
|
244
|
-
const npxCacheDir = join(homeDir, 'Library/Caches/npm/_npx');
|
245
|
-
const cacheEntry = join(npxCacheDir, 'ghi789');
|
246
|
-
const packageDir = join(
|
247
|
-
cacheEntry,
|
248
|
-
'node_modules',
|
249
|
-
'responsible-vibe-mcp'
|
250
|
-
);
|
251
|
-
const workflowsDir = join(packageDir, 'resources', 'workflows');
|
252
|
-
|
253
|
-
mkdirSync(workflowsDir, { recursive: true });
|
254
|
-
createWorkflowFile(workflowsDir, 'epcc.yaml');
|
255
|
-
createPackageJson(packageDir);
|
256
|
-
|
257
|
-
// Set HOME environment variable
|
258
|
-
process.env.HOME = homeDir;
|
259
|
-
|
260
|
-
const workflowManager = new WorkflowManager();
|
261
|
-
const workflows = workflowManager.getAvailableWorkflows();
|
262
|
-
|
263
|
-
expect(workflows.length).toBeGreaterThan(0);
|
264
|
-
const workflowNames = workflows.map(w => w.name);
|
265
|
-
expect(workflowNames).toContain('epcc');
|
266
|
-
});
|
267
|
-
});
|
268
|
-
|
269
|
-
describe('Strategy 5: Executable directory', () => {
|
270
|
-
it('should find workflows relative to executable location', () => {
|
271
|
-
// This strategy uses process.argv[1] which is set by Node.js
|
272
|
-
// We can't easily mock this in tests, but we can verify the logic
|
273
|
-
const workflowManager = new WorkflowManager();
|
274
|
-
const workflows = workflowManager.getAvailableWorkflows();
|
275
|
-
|
276
|
-
// Should find predefined workflows
|
277
|
-
expect(workflows.length).toBeGreaterThan(0);
|
278
|
-
});
|
279
|
-
});
|
280
|
-
|
281
|
-
describe('Strategy 6: require.resolve', () => {
|
282
|
-
it('should use require.resolve to find package location', () => {
|
283
|
-
// This strategy attempts to resolve our own package
|
284
|
-
// In the test environment, this should work since we're running from the package
|
285
|
-
const workflowManager = new WorkflowManager();
|
286
|
-
const workflows = workflowManager.getAvailableWorkflows();
|
287
|
-
|
288
|
-
expect(workflows.length).toBeGreaterThan(0);
|
289
|
-
const workflowNames = workflows.map(w => w.name);
|
290
|
-
|
291
|
-
// Should find all predefined workflows
|
292
|
-
expect(workflowNames).toContain('waterfall');
|
293
|
-
expect(workflowNames).toContain('bugfix');
|
294
|
-
expect(workflowNames).toContain('epcc');
|
295
|
-
});
|
296
|
-
});
|
297
|
-
|
298
|
-
describe('Fallback behavior', () => {
|
299
|
-
it('should handle gracefully when no workflows directory is found', () => {
|
300
|
-
// Create a scenario where no workflows can be found
|
301
|
-
// This is difficult to test since our actual package has workflows
|
302
|
-
// But we can verify the manager doesn't crash
|
303
|
-
const workflowManager = new WorkflowManager();
|
304
|
-
|
305
|
-
expect(() => {
|
306
|
-
const workflows = workflowManager.getAvailableWorkflows();
|
307
|
-
expect(workflows).toBeDefined();
|
308
|
-
expect(Array.isArray(workflows)).toBe(true);
|
309
|
-
}).not.toThrow();
|
310
|
-
});
|
311
|
-
|
312
|
-
it('should deduplicate strategies and filter invalid paths', () => {
|
313
|
-
// The implementation should remove duplicates and invalid paths
|
314
|
-
const workflowManager = new WorkflowManager();
|
315
|
-
const workflows = workflowManager.getAvailableWorkflows();
|
316
|
-
|
317
|
-
// Should work without errors despite potential duplicate strategies
|
318
|
-
expect(workflows).toBeDefined();
|
319
|
-
expect(Array.isArray(workflows)).toBe(true);
|
320
|
-
});
|
321
|
-
});
|
322
|
-
|
323
|
-
describe('Integration with actual workflows', () => {
|
324
|
-
it('should load all predefined workflows correctly', () => {
|
325
|
-
const workflowManager = new WorkflowManager();
|
326
|
-
const workflows = workflowManager.getAvailableWorkflows();
|
327
|
-
|
328
|
-
// Verify all expected workflows are present
|
329
|
-
const workflowNames = workflows.map(w => w.name).sort();
|
330
|
-
expect(workflowNames).toContain('waterfall');
|
331
|
-
expect(workflowNames).toContain('bugfix');
|
332
|
-
expect(workflowNames).toContain('epcc');
|
333
|
-
|
334
|
-
// Verify each workflow has required properties
|
335
|
-
for (const workflow of workflows) {
|
336
|
-
expect(workflow.name).toBeDefined();
|
337
|
-
expect(workflow.displayName).toBeDefined();
|
338
|
-
expect(workflow.description).toBeDefined();
|
339
|
-
expect(workflow.initialState).toBeDefined();
|
340
|
-
expect(workflow.phases).toBeDefined();
|
341
|
-
expect(Array.isArray(workflow.phases)).toBe(true);
|
342
|
-
expect(workflow.phases.length).toBeGreaterThan(0);
|
343
|
-
}
|
344
|
-
});
|
345
|
-
|
346
|
-
it('should load specific workflows with correct structure', () => {
|
347
|
-
const workflowManager = new WorkflowManager();
|
348
|
-
|
349
|
-
// Test waterfall workflow
|
350
|
-
const waterfall = workflowManager.getWorkflow('waterfall');
|
351
|
-
expect(waterfall).toBeDefined();
|
352
|
-
expect(waterfall?.name).toBe('waterfall');
|
353
|
-
expect(waterfall?.initial_state).toBe('requirements');
|
354
|
-
expect(Object.keys(waterfall?.states || {})).toContain('requirements');
|
355
|
-
expect(Object.keys(waterfall?.states || {})).toContain('design');
|
356
|
-
expect(Object.keys(waterfall?.states || {})).toContain('implementation');
|
357
|
-
|
358
|
-
// Test bugfix workflow
|
359
|
-
const bugfix = workflowManager.getWorkflow('bugfix');
|
360
|
-
expect(bugfix).toBeDefined();
|
361
|
-
expect(bugfix?.name).toBe('bugfix');
|
362
|
-
expect(bugfix?.initial_state).toBe('reproduce');
|
363
|
-
expect(Object.keys(bugfix?.states || {})).toContain('reproduce');
|
364
|
-
expect(Object.keys(bugfix?.states || {})).toContain('analyze');
|
365
|
-
expect(Object.keys(bugfix?.states || {})).toContain('fix');
|
366
|
-
expect(Object.keys(bugfix?.states || {})).toContain('verify');
|
367
|
-
});
|
368
|
-
});
|
369
|
-
});
|
@@ -1,150 +0,0 @@
|
|
1
|
-
/**
|
2
|
-
* Unit tests for WorkflowManager path resolution
|
3
|
-
*
|
4
|
-
* Tests the robust workflow directory finding functionality
|
5
|
-
*/
|
6
|
-
|
7
|
-
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
8
|
-
import { existsSync, writeFileSync, mkdirSync, rmSync } from 'node:fs';
|
9
|
-
import { resolve, join } from 'node:path';
|
10
|
-
import { tmpdir } from 'node:os';
|
11
|
-
import { WorkflowManager } from '@codemcp/workflows-core';
|
12
|
-
|
13
|
-
describe('WorkflowManager path resolution', () => {
|
14
|
-
let tempDir: string;
|
15
|
-
let originalNodePath: string | undefined;
|
16
|
-
|
17
|
-
beforeEach(() => {
|
18
|
-
// Create temporary directory for testing
|
19
|
-
tempDir = resolve(tmpdir(), `workflow-test-${Date.now()}`);
|
20
|
-
mkdirSync(tempDir, { recursive: true });
|
21
|
-
|
22
|
-
// Store original NODE_PATH
|
23
|
-
originalNodePath = process.env.NODE_PATH;
|
24
|
-
});
|
25
|
-
|
26
|
-
afterEach(() => {
|
27
|
-
// Clean up temporary directory
|
28
|
-
if (existsSync(tempDir)) {
|
29
|
-
rmSync(tempDir, { recursive: true, force: true });
|
30
|
-
}
|
31
|
-
|
32
|
-
// Restore NODE_PATH
|
33
|
-
if (originalNodePath !== undefined) {
|
34
|
-
process.env.NODE_PATH = originalNodePath;
|
35
|
-
} else {
|
36
|
-
delete process.env.NODE_PATH;
|
37
|
-
}
|
38
|
-
});
|
39
|
-
|
40
|
-
describe('findWorkflowsDirectory', () => {
|
41
|
-
it('should find workflows in development environment structure', () => {
|
42
|
-
// Create development-like structure
|
43
|
-
const projectRoot = join(tempDir, 'project');
|
44
|
-
const srcDir = join(projectRoot, 'src');
|
45
|
-
const workflowsDir = join(projectRoot, 'resources', 'workflows');
|
46
|
-
|
47
|
-
mkdirSync(srcDir, { recursive: true });
|
48
|
-
mkdirSync(workflowsDir, { recursive: true });
|
49
|
-
|
50
|
-
// Create a sample workflow file
|
51
|
-
writeFileSync(
|
52
|
-
join(workflowsDir, 'test.yaml'),
|
53
|
-
'name: test\ndescription: test workflow\ninitial_state: start\nstates:\n start:\n description: test'
|
54
|
-
);
|
55
|
-
|
56
|
-
// Create package.json to identify the project
|
57
|
-
writeFileSync(
|
58
|
-
join(projectRoot, 'package.json'),
|
59
|
-
JSON.stringify({
|
60
|
-
name: 'responsible-vibe-mcp',
|
61
|
-
version: '1.0.0',
|
62
|
-
})
|
63
|
-
);
|
64
|
-
|
65
|
-
const workflowManager = new WorkflowManager();
|
66
|
-
|
67
|
-
// The method should find workflows
|
68
|
-
const workflows = workflowManager.getAvailableWorkflows();
|
69
|
-
expect(workflows.length).toBeGreaterThan(0);
|
70
|
-
});
|
71
|
-
|
72
|
-
it('should handle missing workflows directory gracefully', () => {
|
73
|
-
// Create a project without workflows directory
|
74
|
-
const projectRoot = join(tempDir, 'no-workflows');
|
75
|
-
mkdirSync(projectRoot, { recursive: true });
|
76
|
-
|
77
|
-
const workflowManager = new WorkflowManager();
|
78
|
-
|
79
|
-
// Should not throw error, just return empty workflows
|
80
|
-
const workflows = workflowManager.getAvailableWorkflows();
|
81
|
-
expect(workflows).toBeDefined();
|
82
|
-
// Note: In real implementation, it might still find predefined workflows from the actual package
|
83
|
-
});
|
84
|
-
|
85
|
-
it('should load predefined workflows successfully', () => {
|
86
|
-
const workflowManager = new WorkflowManager();
|
87
|
-
const workflows = workflowManager.getAvailableWorkflows();
|
88
|
-
|
89
|
-
// Should have the standard predefined workflows
|
90
|
-
const workflowNames = workflows.map(w => w.name);
|
91
|
-
expect(workflowNames).toContain('waterfall');
|
92
|
-
expect(workflowNames).toContain('bugfix');
|
93
|
-
expect(workflowNames).toContain('epcc');
|
94
|
-
expect(workflowNames).toContain('minor');
|
95
|
-
expect(workflowNames).toContain('greenfield');
|
96
|
-
});
|
97
|
-
|
98
|
-
it('should load specific workflows correctly', () => {
|
99
|
-
const workflowManager = new WorkflowManager();
|
100
|
-
|
101
|
-
// Test loading a specific workflow
|
102
|
-
const waterfall = workflowManager.getWorkflow('waterfall');
|
103
|
-
expect(waterfall).toBeDefined();
|
104
|
-
expect(waterfall?.name).toBe('waterfall');
|
105
|
-
expect(waterfall?.initial_state).toBe('requirements');
|
106
|
-
|
107
|
-
const bugfix = workflowManager.getWorkflow('bugfix');
|
108
|
-
expect(bugfix).toBeDefined();
|
109
|
-
expect(bugfix?.name).toBe('bugfix');
|
110
|
-
expect(bugfix?.initial_state).toBe('reproduce');
|
111
|
-
});
|
112
|
-
|
113
|
-
it('should validate workflow names correctly', () => {
|
114
|
-
const workflowManager = new WorkflowManager();
|
115
|
-
|
116
|
-
// Test predefined workflows
|
117
|
-
expect(workflowManager.validateWorkflowName('waterfall', tempDir)).toBe(
|
118
|
-
true
|
119
|
-
);
|
120
|
-
expect(workflowManager.validateWorkflowName('bugfix', tempDir)).toBe(
|
121
|
-
true
|
122
|
-
);
|
123
|
-
expect(workflowManager.validateWorkflowName('nonexistent', tempDir)).toBe(
|
124
|
-
false
|
125
|
-
);
|
126
|
-
});
|
127
|
-
});
|
128
|
-
|
129
|
-
describe('workflow loading from different directories', () => {
|
130
|
-
it('should work when called from different working directories', () => {
|
131
|
-
// Change to temp directory (different from package location)
|
132
|
-
const originalCwd = process.cwd();
|
133
|
-
|
134
|
-
try {
|
135
|
-
process.chdir(tempDir);
|
136
|
-
|
137
|
-
const workflowManager = new WorkflowManager();
|
138
|
-
const workflows = workflowManager.getAvailableWorkflows();
|
139
|
-
|
140
|
-
// Should still find predefined workflows
|
141
|
-
expect(workflows.length).toBeGreaterThan(0);
|
142
|
-
|
143
|
-
const workflowNames = workflows.map(w => w.name);
|
144
|
-
expect(workflowNames).toContain('waterfall');
|
145
|
-
} finally {
|
146
|
-
process.chdir(originalCwd);
|
147
|
-
}
|
148
|
-
});
|
149
|
-
});
|
150
|
-
});
|
@@ -1,155 +0,0 @@
|
|
1
|
-
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
2
|
-
import { WorkflowManager } from '@codemcp/workflows-core';
|
3
|
-
import fs from 'node:fs';
|
4
|
-
import path from 'node:path';
|
5
|
-
import { tmpdir } from 'node:os';
|
6
|
-
|
7
|
-
describe('Workflow Migration', () => {
|
8
|
-
let testProjectPath: string;
|
9
|
-
|
10
|
-
beforeEach(() => {
|
11
|
-
testProjectPath = fs.mkdtempSync(
|
12
|
-
path.join(tmpdir(), 'workflow-migration-test-')
|
13
|
-
);
|
14
|
-
});
|
15
|
-
|
16
|
-
afterEach(() => {
|
17
|
-
fs.rmSync(testProjectPath, { recursive: true, force: true });
|
18
|
-
});
|
19
|
-
|
20
|
-
it('should migrate workflow.yaml to workflows/custom.yaml', () => {
|
21
|
-
// Create .vibe directory and legacy workflow file
|
22
|
-
const vibeDir = path.join(testProjectPath, '.vibe');
|
23
|
-
fs.mkdirSync(vibeDir, { recursive: true });
|
24
|
-
|
25
|
-
const legacyWorkflow = `
|
26
|
-
name: my-custom-workflow
|
27
|
-
description: 'Legacy custom workflow'
|
28
|
-
initial_state: start
|
29
|
-
states:
|
30
|
-
start:
|
31
|
-
description: 'Start phase'
|
32
|
-
default_instructions: 'Start instructions'
|
33
|
-
transitions:
|
34
|
-
- target: end
|
35
|
-
condition: 'always'
|
36
|
-
end:
|
37
|
-
description: 'End phase'
|
38
|
-
default_instructions: 'End instructions'
|
39
|
-
transitions: []
|
40
|
-
`;
|
41
|
-
|
42
|
-
const legacyPath = path.join(vibeDir, 'workflow.yaml');
|
43
|
-
fs.writeFileSync(legacyPath, legacyWorkflow);
|
44
|
-
|
45
|
-
const manager = new WorkflowManager();
|
46
|
-
|
47
|
-
// Load project workflows (this should trigger migration)
|
48
|
-
manager.loadProjectWorkflows(testProjectPath);
|
49
|
-
|
50
|
-
// Check that legacy file was moved
|
51
|
-
expect(fs.existsSync(legacyPath)).toBe(false);
|
52
|
-
|
53
|
-
// Check that new file exists
|
54
|
-
const newPath = path.join(vibeDir, 'workflows', 'custom.yaml');
|
55
|
-
expect(fs.existsSync(newPath)).toBe(true);
|
56
|
-
|
57
|
-
// Check content is preserved
|
58
|
-
const newContent = fs.readFileSync(newPath, 'utf8');
|
59
|
-
expect(newContent).toContain('my-custom-workflow');
|
60
|
-
expect(newContent).toContain('Legacy custom workflow');
|
61
|
-
});
|
62
|
-
|
63
|
-
it('should migrate workflow.yml to workflows/custom.yaml', () => {
|
64
|
-
// Create .vibe directory and legacy workflow file with .yml extension
|
65
|
-
const vibeDir = path.join(testProjectPath, '.vibe');
|
66
|
-
fs.mkdirSync(vibeDir, { recursive: true });
|
67
|
-
|
68
|
-
const legacyWorkflow = `
|
69
|
-
name: yml-workflow
|
70
|
-
description: 'YML extension workflow'
|
71
|
-
initial_state: start
|
72
|
-
states:
|
73
|
-
start:
|
74
|
-
description: 'Start phase'
|
75
|
-
default_instructions: 'Start instructions'
|
76
|
-
transitions:
|
77
|
-
- target: end
|
78
|
-
condition: 'always'
|
79
|
-
end:
|
80
|
-
description: 'End phase'
|
81
|
-
default_instructions: 'End instructions'
|
82
|
-
transitions: []
|
83
|
-
`;
|
84
|
-
|
85
|
-
const legacyPath = path.join(vibeDir, 'workflow.yml');
|
86
|
-
fs.writeFileSync(legacyPath, legacyWorkflow);
|
87
|
-
|
88
|
-
const manager = new WorkflowManager();
|
89
|
-
|
90
|
-
// Load project workflows (this should trigger migration)
|
91
|
-
manager.loadProjectWorkflows(testProjectPath);
|
92
|
-
|
93
|
-
// Check that legacy file was moved
|
94
|
-
expect(fs.existsSync(legacyPath)).toBe(false);
|
95
|
-
|
96
|
-
// Check that new file exists
|
97
|
-
const newPath = path.join(vibeDir, 'workflows', 'custom.yaml');
|
98
|
-
expect(fs.existsSync(newPath)).toBe(true);
|
99
|
-
|
100
|
-
// Check content is preserved
|
101
|
-
const newContent = fs.readFileSync(newPath, 'utf8');
|
102
|
-
expect(newContent).toContain('yml-workflow');
|
103
|
-
});
|
104
|
-
|
105
|
-
it('should not migrate if target file already exists', () => {
|
106
|
-
// Create .vibe directory and both legacy and new workflow files
|
107
|
-
const vibeDir = path.join(testProjectPath, '.vibe');
|
108
|
-
const workflowsDir = path.join(vibeDir, 'workflows');
|
109
|
-
fs.mkdirSync(workflowsDir, { recursive: true });
|
110
|
-
|
111
|
-
const legacyWorkflow = 'name: legacy\ndescription: Legacy';
|
112
|
-
const newWorkflow = 'name: existing\ndescription: Existing';
|
113
|
-
|
114
|
-
const legacyPath = path.join(vibeDir, 'workflow.yaml');
|
115
|
-
const newPath = path.join(workflowsDir, 'custom.yaml');
|
116
|
-
|
117
|
-
fs.writeFileSync(legacyPath, legacyWorkflow);
|
118
|
-
fs.writeFileSync(newPath, newWorkflow);
|
119
|
-
|
120
|
-
const manager = new WorkflowManager();
|
121
|
-
|
122
|
-
// Load project workflows (should not migrate because target exists)
|
123
|
-
manager.loadProjectWorkflows(testProjectPath);
|
124
|
-
|
125
|
-
// Check that legacy file still exists
|
126
|
-
expect(fs.existsSync(legacyPath)).toBe(true);
|
127
|
-
|
128
|
-
// Check that new file is unchanged
|
129
|
-
const newContent = fs.readFileSync(newPath, 'utf8');
|
130
|
-
expect(newContent).toContain('existing');
|
131
|
-
expect(newContent).not.toContain('legacy');
|
132
|
-
});
|
133
|
-
|
134
|
-
it('should handle migration errors gracefully', () => {
|
135
|
-
// Create .vibe directory and legacy workflow file
|
136
|
-
const vibeDir = path.join(testProjectPath, '.vibe');
|
137
|
-
fs.mkdirSync(vibeDir, { recursive: true });
|
138
|
-
|
139
|
-
const legacyPath = path.join(vibeDir, 'workflow.yaml');
|
140
|
-
fs.writeFileSync(legacyPath, 'name: test');
|
141
|
-
|
142
|
-
// Make the .vibe directory read-only to cause migration failure
|
143
|
-
fs.chmodSync(vibeDir, 0o444);
|
144
|
-
|
145
|
-
const manager = new WorkflowManager();
|
146
|
-
|
147
|
-
// Should not throw error even if migration fails
|
148
|
-
expect(() => {
|
149
|
-
manager.loadProjectWorkflows(testProjectPath);
|
150
|
-
}).not.toThrow();
|
151
|
-
|
152
|
-
// Restore permissions for cleanup
|
153
|
-
fs.chmodSync(vibeDir, 0o755);
|
154
|
-
});
|
155
|
-
});
|