@codemcp/workflows-core 3.1.21 → 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.
Files changed (80) hide show
  1. package/package.json +9 -5
  2. package/resources/templates/architecture/arc42/arc42-template-EN.md +1077 -0
  3. package/resources/templates/architecture/arc42/images/01_2_iso-25010-topics-EN.drawio-2023.png +0 -0
  4. package/resources/templates/architecture/arc42/images/01_2_iso-25010-topics-EN.drawio.png +0 -0
  5. package/resources/templates/architecture/arc42/images/05_building_blocks-EN.png +0 -0
  6. package/resources/templates/architecture/arc42/images/08-concepts-EN.drawio.png +0 -0
  7. package/resources/templates/architecture/arc42/images/arc42-logo.png +0 -0
  8. package/resources/templates/architecture/c4.md +224 -0
  9. package/resources/templates/architecture/freestyle.md +53 -0
  10. package/resources/templates/architecture/none.md +17 -0
  11. package/resources/templates/design/comprehensive.md +207 -0
  12. package/resources/templates/design/freestyle.md +37 -0
  13. package/resources/templates/design/none.md +17 -0
  14. package/resources/templates/requirements/ears.md +90 -0
  15. package/resources/templates/requirements/freestyle.md +42 -0
  16. package/resources/templates/requirements/none.md +17 -0
  17. package/resources/workflows/big-bang-conversion.yaml +539 -0
  18. package/resources/workflows/boundary-testing.yaml +334 -0
  19. package/resources/workflows/bugfix.yaml +185 -0
  20. package/resources/workflows/business-analysis.yaml +671 -0
  21. package/resources/workflows/c4-analysis.yaml +485 -0
  22. package/resources/workflows/epcc.yaml +161 -0
  23. package/resources/workflows/greenfield.yaml +189 -0
  24. package/resources/workflows/minor.yaml +127 -0
  25. package/resources/workflows/posts.yaml +207 -0
  26. package/resources/workflows/slides.yaml +256 -0
  27. package/resources/workflows/tdd.yaml +157 -0
  28. package/resources/workflows/waterfall.yaml +195 -0
  29. package/.turbo/turbo-build.log +0 -4
  30. package/src/config-manager.ts +0 -96
  31. package/src/conversation-manager.ts +0 -489
  32. package/src/database.ts +0 -427
  33. package/src/file-detection-manager.ts +0 -302
  34. package/src/git-manager.ts +0 -64
  35. package/src/index.ts +0 -28
  36. package/src/instruction-generator.ts +0 -210
  37. package/src/interaction-logger.ts +0 -109
  38. package/src/logger.ts +0 -353
  39. package/src/path-validation-utils.ts +0 -261
  40. package/src/plan-manager.ts +0 -323
  41. package/src/project-docs-manager.ts +0 -523
  42. package/src/state-machine-loader.ts +0 -365
  43. package/src/state-machine-types.ts +0 -72
  44. package/src/state-machine.ts +0 -370
  45. package/src/system-prompt-generator.ts +0 -122
  46. package/src/template-manager.ts +0 -328
  47. package/src/transition-engine.ts +0 -386
  48. package/src/types.ts +0 -60
  49. package/src/workflow-manager.ts +0 -606
  50. package/test/unit/conversation-manager.test.ts +0 -179
  51. package/test/unit/custom-workflow-loading.test.ts +0 -174
  52. package/test/unit/directory-linking-and-extensions.test.ts +0 -338
  53. package/test/unit/file-linking-integration.test.ts +0 -256
  54. package/test/unit/git-commit-integration.test.ts +0 -91
  55. package/test/unit/git-manager.test.ts +0 -86
  56. package/test/unit/install-workflow.test.ts +0 -138
  57. package/test/unit/instruction-generator.test.ts +0 -247
  58. package/test/unit/list-workflows-filtering.test.ts +0 -68
  59. package/test/unit/none-template-functionality.test.ts +0 -224
  60. package/test/unit/project-docs-manager.test.ts +0 -337
  61. package/test/unit/state-machine-loader.test.ts +0 -234
  62. package/test/unit/template-manager.test.ts +0 -217
  63. package/test/unit/validate-workflow-name.test.ts +0 -150
  64. package/test/unit/workflow-domain-filtering.test.ts +0 -75
  65. package/test/unit/workflow-enum-generation.test.ts +0 -92
  66. package/test/unit/workflow-manager-enhanced-path-resolution.test.ts +0 -369
  67. package/test/unit/workflow-manager-path-resolution.test.ts +0 -150
  68. package/test/unit/workflow-migration.test.ts +0 -155
  69. package/test/unit/workflow-override-by-name.test.ts +0 -116
  70. package/test/unit/workflow-prioritization.test.ts +0 -38
  71. package/test/unit/workflow-validation.test.ts +0 -303
  72. package/test/utils/e2e-test-setup.ts +0 -453
  73. package/test/utils/run-server-in-dir.sh +0 -27
  74. package/test/utils/temp-files.ts +0 -308
  75. package/test/utils/test-access.ts +0 -79
  76. package/test/utils/test-helpers.ts +0 -286
  77. package/test/utils/test-setup.ts +0 -78
  78. package/tsconfig.build.json +0 -21
  79. package/tsconfig.json +0 -8
  80. package/vitest.config.ts +0 -18
@@ -1,256 +0,0 @@
1
- /**
2
- * Integration tests for file linking functionality
3
- *
4
- * Tests the complete file linking workflow including path validation,
5
- * file detection, and symlink creation.
6
- */
7
-
8
- import { describe, it, expect, beforeEach, afterEach } from 'vitest';
9
- import { PathValidationUtils } from '@codemcp/workflows-core';
10
- import { FileDetectionManager } from '@codemcp/workflows-core';
11
- import { ProjectDocsManager } from '@codemcp/workflows-core';
12
- import { join } from 'node:path';
13
- import { tmpdir } from 'node:os';
14
- import { mkdir, writeFile, rmdir, lstat } from 'node:fs/promises';
15
-
16
- describe('File Linking Integration', () => {
17
- let testProjectPath: string;
18
- let projectDocsManager: ProjectDocsManager;
19
- let fileDetectionManager: FileDetectionManager;
20
-
21
- beforeEach(async () => {
22
- // Create test project directory
23
- testProjectPath = join(tmpdir(), `file-linking-test-${Date.now()}`);
24
- await mkdir(testProjectPath, { recursive: true });
25
-
26
- // Create test files
27
- await writeFile(
28
- join(testProjectPath, 'README.md'),
29
- '# Test Project\n\nThis is a test project with requirements and architecture info.'
30
- );
31
- await writeFile(
32
- join(testProjectPath, 'ARCHITECTURE.md'),
33
- '# Architecture\n\nSystem architecture details.'
34
- );
35
-
36
- // Create docs directory with files
37
- await mkdir(join(testProjectPath, 'docs'), { recursive: true });
38
- await writeFile(
39
- join(testProjectPath, 'docs', 'design.md'),
40
- '# Design\n\nDetailed design specifications.'
41
- );
42
-
43
- projectDocsManager = new ProjectDocsManager();
44
- fileDetectionManager = new FileDetectionManager(testProjectPath);
45
- });
46
-
47
- afterEach(async () => {
48
- // Clean up test directory
49
- try {
50
- await rmdir(testProjectPath, { recursive: true });
51
- } catch {
52
- // Ignore cleanup errors
53
- }
54
- });
55
-
56
- describe('PathValidationUtils', () => {
57
- it('should validate template names correctly', () => {
58
- const availableTemplates = ['arc42', 'freestyle'];
59
-
60
- expect(
61
- PathValidationUtils.isTemplateName('arc42', availableTemplates)
62
- ).toBe(true);
63
- expect(
64
- PathValidationUtils.isTemplateName('freestyle', availableTemplates)
65
- ).toBe(true);
66
- expect(
67
- PathValidationUtils.isTemplateName('invalid', availableTemplates)
68
- ).toBe(false);
69
- });
70
-
71
- it('should validate file paths correctly', async () => {
72
- const result = await PathValidationUtils.validateFilePath(
73
- 'README.md',
74
- testProjectPath
75
- );
76
-
77
- expect(result.isValid).toBe(true);
78
- expect(result.resolvedPath).toBe(join(testProjectPath, 'README.md'));
79
- });
80
-
81
- it('should reject non-existent files', async () => {
82
- const result = await PathValidationUtils.validateFilePath(
83
- 'nonexistent.md',
84
- testProjectPath
85
- );
86
-
87
- expect(result.isValid).toBe(false);
88
- expect(result.error).toContain('File not found');
89
- });
90
-
91
- it('should validate mixed parameters correctly', async () => {
92
- const availableTemplates = ['arc42', 'freestyle'];
93
-
94
- // Template name
95
- const templateResult = await PathValidationUtils.validateParameter(
96
- 'arc42',
97
- availableTemplates,
98
- testProjectPath
99
- );
100
- expect(templateResult.isTemplate).toBe(true);
101
- expect(templateResult.isFilePath).toBe(false);
102
-
103
- // File path
104
- const fileResult = await PathValidationUtils.validateParameter(
105
- 'README.md',
106
- availableTemplates,
107
- testProjectPath
108
- );
109
- expect(fileResult.isTemplate).toBe(false);
110
- expect(fileResult.isFilePath).toBe(true);
111
- expect(fileResult.resolvedPath).toBe(join(testProjectPath, 'README.md'));
112
-
113
- // Invalid parameter
114
- const invalidResult = await PathValidationUtils.validateParameter(
115
- 'invalid',
116
- availableTemplates,
117
- testProjectPath
118
- );
119
- expect(invalidResult.isTemplate).toBe(false);
120
- expect(invalidResult.isFilePath).toBe(false);
121
- expect(invalidResult.error).toBeDefined();
122
- });
123
- });
124
-
125
- describe('FileDetectionManager', () => {
126
- it('should detect existing documentation files', async () => {
127
- const result = await fileDetectionManager.detectDocumentationFiles();
128
-
129
- expect(result.architecture.length).toBeGreaterThan(0);
130
- expect(result.requirements.length).toBeGreaterThan(0);
131
- expect(result.design.length).toBeGreaterThan(0);
132
-
133
- // Check that README.md is detected for multiple types
134
- const readmeInRequirements = result.requirements.some(
135
- file => file.relativePath === 'README.md'
136
- );
137
- expect(readmeInRequirements).toBe(true);
138
- });
139
-
140
- it('should format suggestions correctly', async () => {
141
- const result = await fileDetectionManager.detectDocumentationFiles();
142
- const suggestions = fileDetectionManager.formatSuggestions(result);
143
-
144
- expect(suggestions).toContain('Existing documentation files detected');
145
- expect(suggestions).toContain('README.md');
146
- expect(suggestions).toContain('setup_project_docs');
147
- });
148
- });
149
-
150
- describe('ProjectDocsManager Symlink Creation', () => {
151
- it('should create symlinks for file paths', async () => {
152
- const result = await projectDocsManager.createOrLinkProjectDocs(
153
- testProjectPath,
154
- {}, // No templates
155
- {
156
- architecture: join(testProjectPath, 'ARCHITECTURE.md'),
157
- requirements: join(testProjectPath, 'README.md'),
158
- design: join(testProjectPath, 'docs', 'design.md'),
159
- }
160
- );
161
-
162
- expect(result.created).toEqual([]);
163
- expect(result.linked).toEqual([
164
- 'architecture.md',
165
- 'requirements.md',
166
- 'design.md',
167
- ]);
168
- expect(result.skipped).toEqual([]);
169
-
170
- // Verify symlinks were created
171
- const paths = projectDocsManager.getDocumentPaths(testProjectPath);
172
-
173
- const archStats = await lstat(paths.architecture);
174
- expect(archStats.isSymbolicLink()).toBe(true);
175
-
176
- const reqStats = await lstat(paths.requirements);
177
- expect(reqStats.isSymbolicLink()).toBe(true);
178
-
179
- const designStats = await lstat(paths.design);
180
- expect(designStats.isSymbolicLink()).toBe(true);
181
- });
182
-
183
- it('should handle mixed template and file path scenarios', async () => {
184
- const result = await projectDocsManager.createOrLinkProjectDocs(
185
- testProjectPath,
186
- {
187
- architecture: 'freestyle', // Template
188
- },
189
- {
190
- requirements: join(testProjectPath, 'README.md'), // File path
191
- design: join(testProjectPath, 'docs', 'design.md'), // File path
192
- }
193
- );
194
-
195
- expect(result.created).toEqual(['architecture.md']);
196
- expect(result.linked).toEqual(['requirements.md', 'design.md']);
197
- expect(result.skipped).toEqual([]);
198
- });
199
-
200
- it('should check if documents are symlinks', async () => {
201
- // Create a symlink
202
- await projectDocsManager.createOrLinkProjectDocs(
203
- testProjectPath,
204
- {},
205
- { requirements: join(testProjectPath, 'README.md') }
206
- );
207
-
208
- const isSymlink = await projectDocsManager.isSymlink(
209
- testProjectPath,
210
- 'requirements'
211
- );
212
- expect(isSymlink).toBe(true);
213
-
214
- const isArchSymlink = await projectDocsManager.isSymlink(
215
- testProjectPath,
216
- 'architecture'
217
- );
218
- expect(isArchSymlink).toBe(false);
219
- });
220
- });
221
-
222
- describe('End-to-End File Linking', () => {
223
- it('should support complete file linking workflow', async () => {
224
- // 1. Detect existing files
225
- const detectionResult =
226
- await fileDetectionManager.detectDocumentationFiles();
227
- expect(detectionResult.requirements.length).toBeGreaterThan(0);
228
-
229
- // 2. Validate file paths
230
- const readmePath = join(testProjectPath, 'README.md');
231
- const validation = await PathValidationUtils.validateFilePath(
232
- 'README.md',
233
- testProjectPath
234
- );
235
- expect(validation.isValid).toBe(true);
236
-
237
- // 3. Create symlinks
238
- const linkResult = await projectDocsManager.createOrLinkProjectDocs(
239
- testProjectPath,
240
- { architecture: 'freestyle' }, // Mix of template and file
241
- { requirements: readmePath }
242
- );
243
-
244
- expect(linkResult.created).toContain('architecture.md');
245
- expect(linkResult.linked).toContain('requirements.md');
246
-
247
- // 4. Verify symlinks work
248
- const requirementsPath = await projectDocsManager.readDocument(
249
- testProjectPath,
250
- 'requirements'
251
- );
252
- expect(requirementsPath).toContain('requirements.md');
253
- expect(requirementsPath).toContain('.vibe/docs');
254
- });
255
- });
256
- });
@@ -1,91 +0,0 @@
1
- /**
2
- * Git Commit Integration Tests
3
- *
4
- * Simple tests to verify that the commit_behaviour parameter is handled correctly
5
- * and that the dynamic tool descriptions work as expected
6
- */
7
-
8
- import { describe, it, expect } from 'vitest';
9
- import { GitManager } from '@codemcp/workflows-core';
10
-
11
- describe('Git Commit Integration', () => {
12
- describe('GitManager Repository Detection', () => {
13
- it('should detect git repositories correctly', () => {
14
- // This test verifies that GitManager can detect git repositories
15
- // The actual detection logic is tested in git-manager.test.ts
16
- expect(typeof GitManager.isGitRepository).toBe('function');
17
- expect(typeof GitManager.getCurrentBranch).toBe('function');
18
- expect(typeof GitManager.getCurrentCommitHash).toBe('function');
19
- });
20
- });
21
-
22
- describe('Commit Behaviour Parameter', () => {
23
- it('should define all expected commit behaviour options', () => {
24
- // This test verifies that all expected commit behaviour options are available
25
- const expectedOptions = ['step', 'phase', 'end', 'none'];
26
-
27
- // These are the options that should be available in the MCP tool description
28
- for (const option of expectedOptions) {
29
- expect(typeof option).toBe('string');
30
- expect(option.length).toBeGreaterThan(0);
31
- }
32
- });
33
-
34
- it('should have meaningful option descriptions', () => {
35
- // This test verifies that the commit behaviour options have meaningful descriptions
36
- const optionDescriptions = {
37
- step: 'commit after each development step',
38
- phase: 'commit before phase transitions',
39
- end: 'final commit only',
40
- none: 'no automatic commits',
41
- };
42
-
43
- for (const [option, description] of Object.entries(optionDescriptions)) {
44
- expect(typeof option).toBe('string');
45
- expect(typeof description).toBe('string');
46
- expect(description.length).toBeGreaterThan(10);
47
- }
48
- });
49
- });
50
-
51
- describe('Dynamic Tool Description Logic', () => {
52
- it('should provide different guidance for git vs non-git projects', () => {
53
- // This test verifies the core logic of our dynamic tool descriptions
54
-
55
- // Simulate git repository detection
56
- const isGitRepo = true;
57
- const gitDescription = isGitRepo
58
- ? 'Use "end" unless the user specifically requests different behavior.'
59
- : 'Use "none" as this is not a git repository. Other options are not applicable for non-git projects.';
60
-
61
- expect(gitDescription).toContain('Use "end"');
62
- expect(gitDescription).toContain('unless the user specifically requests');
63
-
64
- // Simulate non-git directory detection
65
- const isNonGitRepo = false;
66
- const nonGitDescription = isNonGitRepo
67
- ? 'Use "end" unless the user specifically requests different behavior.'
68
- : 'Use "none" as this is not a git repository. Other options are not applicable for non-git projects.';
69
-
70
- expect(nonGitDescription).toContain('Use "none"');
71
- expect(nonGitDescription).toContain('not a git repository');
72
- expect(nonGitDescription).toContain('not applicable');
73
-
74
- // Verify the descriptions are different
75
- expect(gitDescription).not.toBe(nonGitDescription);
76
- });
77
-
78
- it('should maintain all commit behavior options regardless of project type', () => {
79
- // This test verifies that all options remain available regardless of git detection
80
- const allOptions = ['step', 'phase', 'end', 'none'];
81
-
82
- // Both git and non-git projects should have access to all options
83
- // (the difference is in the guidance, not the available options)
84
- for (const option of allOptions) {
85
- expect(allOptions).toContain(option);
86
- }
87
-
88
- expect(allOptions).toHaveLength(4);
89
- });
90
- });
91
- });
@@ -1,86 +0,0 @@
1
- /**
2
- * GitManager Tests
3
- */
4
-
5
- import { describe, it, expect, beforeEach, afterEach } from 'vitest';
6
- import { GitManager } from '@codemcp/workflows-core';
7
- import { execSync } from 'node:child_process';
8
- import { mkdirSync, rmSync, writeFileSync } from 'node:fs';
9
- import { resolve } from 'node:path';
10
-
11
- describe('GitManager', () => {
12
- const testDir = resolve(__dirname, 'test-git-repo');
13
-
14
- beforeEach(() => {
15
- // Clean up any existing test directory
16
- try {
17
- rmSync(testDir, { recursive: true, force: true });
18
- } catch {
19
- // Ignore if directory doesn't exist
20
- }
21
-
22
- // Create test directory
23
- mkdirSync(testDir, { recursive: true });
24
-
25
- // Initialize git repository
26
- execSync('git init', { cwd: testDir, stdio: 'ignore' });
27
- execSync('git config user.name "Test User"', {
28
- cwd: testDir,
29
- stdio: 'ignore',
30
- });
31
- execSync('git config user.email "test@example.com"', {
32
- cwd: testDir,
33
- stdio: 'ignore',
34
- });
35
-
36
- // Create initial commit
37
- writeFileSync(resolve(testDir, 'README.md'), '# Test Repository');
38
- execSync('git add .', { cwd: testDir, stdio: 'ignore' });
39
- execSync('git commit -m "Initial commit"', {
40
- cwd: testDir,
41
- stdio: 'ignore',
42
- });
43
- });
44
-
45
- afterEach(() => {
46
- // Clean up test directory
47
- try {
48
- rmSync(testDir, { recursive: true, force: true });
49
- } catch {
50
- // Ignore cleanup errors
51
- }
52
- });
53
-
54
- describe('isGitRepository', () => {
55
- it('should detect git repository', () => {
56
- expect(GitManager.isGitRepository(testDir)).toBe(true);
57
- });
58
-
59
- it('should detect non-git directory', () => {
60
- const nonGitDir = resolve(__dirname, 'non-git-dir');
61
- mkdirSync(nonGitDir, { recursive: true });
62
-
63
- try {
64
- expect(GitManager.isGitRepository(nonGitDir)).toBe(false);
65
- } finally {
66
- rmSync(nonGitDir, { recursive: true, force: true });
67
- }
68
- });
69
- });
70
-
71
- describe('getCurrentBranch', () => {
72
- it('should get current branch name', () => {
73
- const branch = GitManager.getCurrentBranch(testDir);
74
- // CI environments may use different default branch names
75
- expect(['main', 'master']).toContain(branch);
76
- });
77
- });
78
-
79
- describe('getCurrentCommitHash', () => {
80
- it('should get current commit hash', () => {
81
- const hash = GitManager.getCurrentCommitHash(testDir);
82
- expect(hash).toBeTruthy();
83
- expect(hash).toMatch(/^[a-f0-9]{40}$/); // SHA-1 hash format
84
- });
85
- });
86
- });
@@ -1,138 +0,0 @@
1
- import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
- import { InstallWorkflowHandler } from '../../packages/mcp-server/src/tool-handlers/install-workflow.js';
3
- import { WorkflowManager } from '@codemcp/workflows-core';
4
- import fs from 'node:fs';
5
- import path from 'node:path';
6
- import { tmpdir } from 'node:os';
7
-
8
- describe('Install Workflow', () => {
9
- let testProjectPath: string;
10
- let originalEnv: string | undefined;
11
-
12
- beforeEach(() => {
13
- originalEnv = process.env.VIBE_WORKFLOW_DOMAINS;
14
- testProjectPath = fs.mkdtempSync(
15
- path.join(tmpdir(), 'install-workflow-test-')
16
- );
17
- });
18
-
19
- afterEach(() => {
20
- if (originalEnv !== undefined) {
21
- process.env.VIBE_WORKFLOW_DOMAINS = originalEnv;
22
- } else {
23
- delete process.env.VIBE_WORKFLOW_DOMAINS;
24
- }
25
-
26
- // Clean up test directory
27
- fs.rmSync(testProjectPath, { recursive: true, force: true });
28
- });
29
-
30
- it('should install workflow and make it immediately available', async () => {
31
- process.env.VIBE_WORKFLOW_DOMAINS = 'code';
32
-
33
- const handler = new InstallWorkflowHandler();
34
- const workflowManager = new WorkflowManager();
35
- const context = { workflowManager, projectPath: testProjectPath } as {
36
- workflowManager: WorkflowManager;
37
- projectPath: string;
38
- };
39
-
40
- // Before installation - posts should not be available (office domain)
41
- let workflows =
42
- workflowManager.getAvailableWorkflowsForProject(testProjectPath);
43
- expect(workflows.some(w => w.name === 'posts')).toBe(false);
44
-
45
- // Install posts workflow
46
- const result = await handler.executeHandler({ source: 'posts' }, context);
47
- expect(result.success).toBe(true);
48
-
49
- // After installation - posts should be available (project workflows ignore domain filtering)
50
- workflows =
51
- workflowManager.getAvailableWorkflowsForProject(testProjectPath);
52
- expect(workflows.some(w => w.name === 'posts')).toBe(true);
53
- });
54
-
55
- it('should prevent overwriting existing workflows', async () => {
56
- const handler = new InstallWorkflowHandler();
57
- const workflowManager = new WorkflowManager();
58
- const context = { workflowManager, projectPath: testProjectPath } as {
59
- workflowManager: WorkflowManager;
60
- projectPath: string;
61
- };
62
-
63
- // Install workflow first time
64
- const result1 = await handler.executeHandler({ source: 'posts' }, context);
65
- expect(result1.success).toBe(true);
66
-
67
- // Try to install same workflow again
68
- const result2 = await handler.executeHandler({ source: 'posts' }, context);
69
- expect(result2.success).toBe(false);
70
- expect(result2.message).toContain('already exists');
71
- });
72
-
73
- it('should install with custom name', async () => {
74
- const handler = new InstallWorkflowHandler();
75
- const workflowManager = new WorkflowManager();
76
- const context = { workflowManager, projectPath: testProjectPath } as {
77
- workflowManager: WorkflowManager;
78
- projectPath: string;
79
- };
80
-
81
- // Install with custom name
82
- const result = await handler.executeHandler(
83
- {
84
- source: 'posts',
85
- name: 'my-posts',
86
- },
87
- context
88
- );
89
-
90
- expect(result.success).toBe(true);
91
- expect(result.installedPath).toContain('my-posts.yaml');
92
-
93
- // Check file exists with custom name
94
- const customFile = path.join(
95
- testProjectPath,
96
- '.vibe',
97
- 'workflows',
98
- 'my-posts.yaml'
99
- );
100
- expect(fs.existsSync(customFile)).toBe(true);
101
- });
102
-
103
- it('should handle non-existent workflow', async () => {
104
- const handler = new InstallWorkflowHandler();
105
- const workflowManager = new WorkflowManager();
106
- const context = { workflowManager, projectPath: testProjectPath } as {
107
- workflowManager: WorkflowManager;
108
- projectPath: string;
109
- };
110
-
111
- const result = await handler.executeHandler(
112
- { source: 'nonexistent' },
113
- context
114
- );
115
- expect(result.success).toBe(false);
116
- expect(result.message).toContain('not found');
117
- });
118
-
119
- it('should create .vibe/workflows directory if it does not exist', async () => {
120
- const handler = new InstallWorkflowHandler();
121
- const workflowManager = new WorkflowManager();
122
- const context = { workflowManager, projectPath: testProjectPath } as {
123
- workflowManager: WorkflowManager;
124
- projectPath: string;
125
- };
126
-
127
- // Ensure directory doesn't exist
128
- const workflowsDir = path.join(testProjectPath, '.vibe', 'workflows');
129
- expect(fs.existsSync(workflowsDir)).toBe(false);
130
-
131
- // Install workflow
132
- const result = await handler.executeHandler({ source: 'posts' }, context);
133
- expect(result.success).toBe(true);
134
-
135
- // Directory should now exist
136
- expect(fs.existsSync(workflowsDir)).toBe(true);
137
- });
138
- });