@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,247 +0,0 @@
1
- /**
2
- * Unit tests for InstructionGenerator
3
- *
4
- * Tests instruction generation and variable substitution functionality
5
- */
6
-
7
- import { describe, it, expect, beforeEach, Mocked, vi } from 'vitest';
8
- import { TestAccess } from '../utils/test-access.js';
9
- import { InstructionGenerator } from '@codemcp/workflows-core';
10
- import { PlanManager } from '@codemcp/workflows-core';
11
- import { ProjectDocsManager } from '@codemcp/workflows-core';
12
- import type { ConversationContext } from '../../src/types.js';
13
- import type { InstructionContext } from '../../src/instruction-generator.js';
14
- import { join } from 'node:path';
15
-
16
- // Mock PlanManager
17
- vi.mock('../../src/plan-manager.js');
18
-
19
- // Mock ProjectDocsManager
20
- vi.mock('../../src/project-docs-manager.js');
21
-
22
- describe('InstructionGenerator', () => {
23
- let instructionGenerator: InstructionGenerator;
24
- let mockPlanManager: Mocked<PlanManager>;
25
- let mockProjectDocsManager: Mocked<ProjectDocsManager>;
26
- let testProjectPath: string;
27
- let mockConversationContext: ConversationContext;
28
- let mockInstructionContext: InstructionContext;
29
-
30
- beforeEach(() => {
31
- testProjectPath = '/test/project';
32
-
33
- // Mock PlanManager
34
- mockPlanManager = {
35
- generatePlanFileGuidance: vi
36
- .fn()
37
- .mockReturnValue('Test plan file guidance'),
38
- } as unknown;
39
-
40
- // Mock ProjectDocsManager
41
- mockProjectDocsManager = {
42
- getVariableSubstitutions: vi.fn().mockReturnValue({
43
- $ARCHITECTURE_DOC: join(
44
- testProjectPath,
45
- '.vibe',
46
- 'docs',
47
- 'architecture.md'
48
- ),
49
- $REQUIREMENTS_DOC: join(
50
- testProjectPath,
51
- '.vibe',
52
- 'docs',
53
- 'requirements.md'
54
- ),
55
- $DESIGN_DOC: join(testProjectPath, '.vibe', 'docs', 'design.md'),
56
- }),
57
- } as unknown;
58
-
59
- // Create instruction generator and inject mocks
60
- instructionGenerator = new InstructionGenerator(mockPlanManager);
61
- TestAccess.injectMock(
62
- instructionGenerator,
63
- 'projectDocsManager',
64
- mockProjectDocsManager
65
- );
66
-
67
- // Mock conversation context
68
- mockConversationContext = {
69
- projectPath: testProjectPath,
70
- planFilePath: join(testProjectPath, '.vibe', 'plan.md'),
71
- gitBranch: 'main',
72
- conversationId: 'test-conversation',
73
- } as ConversationContext;
74
-
75
- // Mock instruction context
76
- mockInstructionContext = {
77
- phase: 'design',
78
- conversationContext: mockConversationContext,
79
- transitionReason: 'Test transition',
80
- isModeled: false,
81
- planFileExists: true,
82
- };
83
- });
84
-
85
- describe('generateInstructions', () => {
86
- it('should apply variable substitution to base instructions', async () => {
87
- const baseInstructions =
88
- 'Review the architecture in $ARCHITECTURE_DOC and update requirements in $REQUIREMENTS_DOC.';
89
-
90
- const result = await instructionGenerator.generateInstructions(
91
- baseInstructions,
92
- mockInstructionContext
93
- );
94
-
95
- expect(result.instructions).toContain(
96
- join(testProjectPath, '.vibe', 'docs', 'architecture.md')
97
- );
98
- expect(result.instructions).toContain(
99
- join(testProjectPath, '.vibe', 'docs', 'requirements.md')
100
- );
101
- expect(result.instructions).not.toContain('$ARCHITECTURE_DOC');
102
- expect(result.instructions).not.toContain('$REQUIREMENTS_DOC');
103
- });
104
-
105
- it('should handle multiple occurrences of the same variable', async () => {
106
- const baseInstructions =
107
- 'Check $DESIGN_DOC for details. Update $DESIGN_DOC with new information.';
108
-
109
- const result = await instructionGenerator.generateInstructions(
110
- baseInstructions,
111
- mockInstructionContext
112
- );
113
-
114
- const designDocPath = join(testProjectPath, '.vibe', 'docs', 'design.md');
115
- const occurrences = (
116
- result.instructions.match(
117
- new RegExp(designDocPath.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g')
118
- ) || []
119
- ).length;
120
- expect(occurrences).toBe(2);
121
- expect(result.instructions).not.toContain('$DESIGN_DOC');
122
- });
123
-
124
- it('should handle instructions with no variables', async () => {
125
- const baseInstructions = 'Continue with the current phase tasks.';
126
-
127
- const result = await instructionGenerator.generateInstructions(
128
- baseInstructions,
129
- mockInstructionContext
130
- );
131
-
132
- expect(result.instructions).toContain(
133
- 'Continue with the current phase tasks.'
134
- );
135
- expect(
136
- mockProjectDocsManager.getVariableSubstitutions
137
- ).toHaveBeenCalledWith(testProjectPath);
138
- });
139
-
140
- it('should handle all three document variables', async () => {
141
- const baseInstructions =
142
- 'Review $ARCHITECTURE_DOC, check $REQUIREMENTS_DOC, and update $DESIGN_DOC.';
143
-
144
- const result = await instructionGenerator.generateInstructions(
145
- baseInstructions,
146
- mockInstructionContext
147
- );
148
-
149
- expect(result.instructions).toContain(
150
- join(testProjectPath, '.vibe', 'docs', 'architecture.md')
151
- );
152
- expect(result.instructions).toContain(
153
- join(testProjectPath, '.vibe', 'docs', 'requirements.md')
154
- );
155
- expect(result.instructions).toContain(
156
- join(testProjectPath, '.vibe', 'docs', 'design.md')
157
- );
158
- expect(result.instructions).not.toContain('$ARCHITECTURE_DOC');
159
- expect(result.instructions).not.toContain('$REQUIREMENTS_DOC');
160
- expect(result.instructions).not.toContain('$DESIGN_DOC');
161
- });
162
-
163
- it('should preserve enhanced instruction structure', async () => {
164
- const baseInstructions = 'Work on design tasks using $DESIGN_DOC.';
165
-
166
- const result = await instructionGenerator.generateInstructions(
167
- baseInstructions,
168
- mockInstructionContext
169
- );
170
-
171
- // Should contain enhanced instruction elements
172
- expect(result.instructions).toContain('Check your plan file');
173
- expect(result.instructions).toContain('**Plan File Guidance:**');
174
- expect(result.instructions).toContain('**Project Context:**');
175
- expect(result.instructions).toContain('Project: /test/project');
176
- expect(result.instructions).toContain('Branch: main');
177
- expect(result.instructions).toContain('Current Phase: design');
178
-
179
- // Should contain substituted variable
180
- expect(result.instructions).toContain(
181
- join(testProjectPath, '.vibe', 'docs', 'design.md')
182
- );
183
- });
184
-
185
- it('should return correct metadata', async () => {
186
- const baseInstructions = 'Test instructions with $ARCHITECTURE_DOC.';
187
-
188
- const result = await instructionGenerator.generateInstructions(
189
- baseInstructions,
190
- mockInstructionContext
191
- );
192
-
193
- expect(result.metadata).toEqual({
194
- phase: 'design',
195
- planFilePath: join(testProjectPath, '.vibe', 'plan.md'),
196
- transitionReason: 'Test transition',
197
- isModeled: false,
198
- });
199
- });
200
-
201
- it('should handle special regex characters in variables', async () => {
202
- // Mock a variable with special characters (though unlikely in practice)
203
- mockProjectDocsManager.getVariableSubstitutions.mockReturnValue({
204
- '$TEST[DOC]': '/test/path/doc.md',
205
- });
206
-
207
- const baseInstructions = 'Check $TEST[DOC] for information.';
208
-
209
- const result = await instructionGenerator.generateInstructions(
210
- baseInstructions,
211
- mockInstructionContext
212
- );
213
-
214
- expect(result.instructions).toContain('/test/path/doc.md');
215
- expect(result.instructions).not.toContain('$TEST[DOC]');
216
- });
217
- });
218
-
219
- describe('variable substitution edge cases', () => {
220
- it('should handle empty substitutions', async () => {
221
- mockProjectDocsManager.getVariableSubstitutions.mockReturnValue({});
222
-
223
- const baseInstructions = 'Work on tasks without variables.';
224
-
225
- const result = await instructionGenerator.generateInstructions(
226
- baseInstructions,
227
- mockInstructionContext
228
- );
229
-
230
- expect(result.instructions).toContain('Work on tasks without variables.');
231
- });
232
-
233
- it('should handle variables that do not exist in instructions', async () => {
234
- const baseInstructions = 'Work on general tasks.';
235
-
236
- const result = await instructionGenerator.generateInstructions(
237
- baseInstructions,
238
- mockInstructionContext
239
- );
240
-
241
- expect(result.instructions).toContain('Work on general tasks.');
242
- expect(
243
- mockProjectDocsManager.getVariableSubstitutions
244
- ).toHaveBeenCalledWith(testProjectPath);
245
- });
246
- });
247
- });
@@ -1,68 +0,0 @@
1
- import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
- import { ListWorkflowsHandler } from '../../packages/mcp-server/src/tool-handlers/list-workflows.js';
3
- import { WorkflowManager } from '@codemcp/workflows-core';
4
-
5
- describe('List Workflows Filtering', () => {
6
- let originalEnv: string | undefined;
7
-
8
- beforeEach(() => {
9
- originalEnv = process.env.VIBE_WORKFLOW_DOMAINS;
10
- });
11
-
12
- afterEach(() => {
13
- if (originalEnv !== undefined) {
14
- process.env.VIBE_WORKFLOW_DOMAINS = originalEnv;
15
- } else {
16
- delete process.env.VIBE_WORKFLOW_DOMAINS;
17
- }
18
- });
19
-
20
- it('should return only loaded workflows by default', async () => {
21
- process.env.VIBE_WORKFLOW_DOMAINS = 'code';
22
-
23
- const handler = new ListWorkflowsHandler();
24
- const workflowManager = new WorkflowManager();
25
-
26
- const context = {
27
- workflowManager,
28
- projectPath: process.cwd(),
29
- } as { workflowManager: WorkflowManager; projectPath: string };
30
-
31
- const result = await handler.executeHandler({}, context);
32
-
33
- // Should only include code workflows
34
- const hasCodeWorkflows = result.workflows.some(
35
- w => w.name === 'waterfall' || w.name === 'epcc'
36
- );
37
- const hasOfficeWorkflows = result.workflows.some(w => w.name === 'posts');
38
-
39
- expect(hasCodeWorkflows).toBe(true);
40
- expect(hasOfficeWorkflows).toBe(false);
41
- });
42
-
43
- it('should return all workflows when include_unloaded is true', async () => {
44
- process.env.VIBE_WORKFLOW_DOMAINS = 'code';
45
-
46
- const handler = new ListWorkflowsHandler();
47
- const workflowManager = new WorkflowManager();
48
-
49
- const context = {
50
- workflowManager,
51
- projectPath: process.cwd(),
52
- } as { workflowManager: WorkflowManager; projectPath: string };
53
-
54
- const result = await handler.executeHandler(
55
- { include_unloaded: true },
56
- context
57
- );
58
-
59
- // Should include workflows from all domains
60
- const hasCodeWorkflows = result.workflows.some(
61
- w => w.name === 'waterfall' || w.name === 'epcc'
62
- );
63
- const hasOfficeWorkflows = result.workflows.some(w => w.name === 'posts');
64
-
65
- expect(hasCodeWorkflows).toBe(true);
66
- expect(hasOfficeWorkflows).toBe(true);
67
- });
68
- });
@@ -1,224 +0,0 @@
1
- /**
2
- * Tests for "none" template functionality
3
- *
4
- * Tests the ability to disable specific document types using "none" templates
5
- */
6
-
7
- import { describe, it, expect, beforeEach, afterEach } from 'vitest';
8
- import { ProjectDocsManager } from '@codemcp/workflows-core';
9
- import { join } from 'node:path';
10
- import { tmpdir } from 'node:os';
11
- import { mkdir, rmdir, readFile } from 'node:fs/promises';
12
-
13
- describe('None Template Functionality', () => {
14
- let testProjectPath: string;
15
- let projectDocsManager: ProjectDocsManager;
16
-
17
- beforeEach(async () => {
18
- // Create test project directory
19
- testProjectPath = join(tmpdir(), `none-template-test-${Date.now()}`);
20
- await mkdir(testProjectPath, { recursive: true });
21
-
22
- projectDocsManager = new ProjectDocsManager();
23
- });
24
-
25
- afterEach(async () => {
26
- // Clean up test directory
27
- try {
28
- await rmdir(testProjectPath, { recursive: true });
29
- } catch {
30
- // Ignore cleanup errors
31
- }
32
- });
33
-
34
- describe('None Template Creation', () => {
35
- it('should create none template for architecture', async () => {
36
- const result = await projectDocsManager.createOrLinkProjectDocs(
37
- testProjectPath,
38
- {
39
- architecture: 'none',
40
- requirements: 'freestyle',
41
- design: 'freestyle',
42
- },
43
- {}
44
- );
45
-
46
- expect(result.created).toContain('architecture.md');
47
- expect(result.created).toContain('requirements.md');
48
- expect(result.created).toContain('design.md');
49
-
50
- // Verify the none template content
51
- const archPath = await projectDocsManager.readDocument(
52
- testProjectPath,
53
- 'architecture'
54
- );
55
- expect(archPath).toContain('architecture.md');
56
-
57
- // Read the actual file to verify placeholder content
58
- const archContent = await readFile(archPath, 'utf-8');
59
- expect(archContent).toContain('Architecture Placeholder');
60
- expect(archContent).toContain('DO NOT EDIT THIS FILE');
61
- expect(archContent).toContain('plan file');
62
- });
63
-
64
- it('should create none template for requirements', async () => {
65
- const result = await projectDocsManager.createOrLinkProjectDocs(
66
- testProjectPath,
67
- {
68
- architecture: 'freestyle',
69
- requirements: 'none',
70
- design: 'freestyle',
71
- },
72
- {}
73
- );
74
-
75
- expect(result.created).toContain('requirements.md');
76
-
77
- // Verify the none template content
78
- const reqPath = await projectDocsManager.readDocument(
79
- testProjectPath,
80
- 'requirements'
81
- );
82
- expect(reqPath).toContain('requirements.md');
83
-
84
- // Read the actual file to verify placeholder content
85
- const reqContent = await readFile(reqPath, 'utf-8');
86
- expect(reqContent).toContain('Requirements Placeholder');
87
- expect(reqContent).toContain('DO NOT EDIT THIS FILE');
88
- expect(reqContent).toContain('plan file');
89
- });
90
-
91
- it('should create none template for design', async () => {
92
- const result = await projectDocsManager.createOrLinkProjectDocs(
93
- testProjectPath,
94
- {
95
- architecture: 'freestyle',
96
- requirements: 'freestyle',
97
- design: 'none',
98
- },
99
- {}
100
- );
101
-
102
- expect(result.created).toContain('design.md');
103
-
104
- // Verify the none template content
105
- const designPath = await projectDocsManager.readDocument(
106
- testProjectPath,
107
- 'design'
108
- );
109
- expect(designPath).toContain('design.md');
110
-
111
- // Read the actual file to verify placeholder content
112
- const designContent = await readFile(designPath, 'utf-8');
113
- expect(designContent).toContain('Design Placeholder');
114
- expect(designContent).toContain('DO NOT EDIT THIS FILE');
115
- expect(designContent).toContain('plan file');
116
- });
117
-
118
- it('should support mixed usage with none templates', async () => {
119
- // Create a test README file
120
- const readmePath = join(testProjectPath, 'README.md');
121
- await mkdir(testProjectPath, { recursive: true });
122
- const fs = await import('node:fs/promises');
123
- await fs.writeFile(
124
- readmePath,
125
- '# Test Project\n\nThis is a test project.'
126
- );
127
-
128
- const result = await projectDocsManager.createOrLinkProjectDocs(
129
- testProjectPath,
130
- {
131
- architecture: 'freestyle', // Template
132
- design: 'none', // None template
133
- },
134
- {
135
- requirements: readmePath, // File link
136
- }
137
- );
138
-
139
- expect(result.created).toContain('architecture.md');
140
- expect(result.created).toContain('design.md');
141
- expect(result.linked).toContain('requirements.md');
142
-
143
- // Verify each document type
144
- const archPath = await projectDocsManager.readDocument(
145
- testProjectPath,
146
- 'architecture'
147
- );
148
- const archContent = await readFile(archPath, 'utf-8');
149
- expect(archContent).toContain('INSTRUCTIONS FOR ARCHITECTURE');
150
-
151
- const reqPath = await projectDocsManager.readDocument(
152
- testProjectPath,
153
- 'requirements'
154
- );
155
- const reqContent = await readFile(reqPath, 'utf-8');
156
- expect(reqContent).toContain('This is a test project');
157
-
158
- const designPath = await projectDocsManager.readDocument(
159
- testProjectPath,
160
- 'design'
161
- );
162
- const designContent = await readFile(designPath, 'utf-8');
163
- expect(designContent).toContain('Design Placeholder');
164
- expect(designContent).toContain('DO NOT EDIT THIS FILE');
165
- });
166
-
167
- it('should create all none templates when all are disabled', async () => {
168
- const result = await projectDocsManager.createOrLinkProjectDocs(
169
- testProjectPath,
170
- {
171
- architecture: 'none',
172
- requirements: 'none',
173
- design: 'none',
174
- },
175
- {}
176
- );
177
-
178
- expect(result.created).toEqual([
179
- 'architecture.md',
180
- 'requirements.md',
181
- 'design.md',
182
- ]);
183
- expect(result.linked).toEqual([]);
184
-
185
- // Verify all contain placeholder content
186
- const archPath = await projectDocsManager.readDocument(
187
- testProjectPath,
188
- 'architecture'
189
- );
190
- const reqPath = await projectDocsManager.readDocument(
191
- testProjectPath,
192
- 'requirements'
193
- );
194
- const designPath = await projectDocsManager.readDocument(
195
- testProjectPath,
196
- 'design'
197
- );
198
-
199
- const archContent = await readFile(archPath, 'utf-8');
200
- const reqContent = await readFile(reqPath, 'utf-8');
201
- const designContent = await readFile(designPath, 'utf-8');
202
-
203
- expect(archContent).toContain('Architecture Placeholder');
204
- expect(reqContent).toContain('Requirements Placeholder');
205
- expect(designContent).toContain('Design Placeholder');
206
-
207
- // All should contain the DO NOT EDIT instruction
208
- expect(archContent).toContain('DO NOT EDIT THIS FILE');
209
- expect(reqContent).toContain('DO NOT EDIT THIS FILE');
210
- expect(designContent).toContain('DO NOT EDIT THIS FILE');
211
- });
212
- });
213
-
214
- describe('Template Discovery', () => {
215
- it('should include none in available templates', async () => {
216
- const availableTemplates =
217
- await projectDocsManager.templateManager.getAvailableTemplates();
218
-
219
- expect(availableTemplates.architecture).toContain('none');
220
- expect(availableTemplates.requirements).toContain('none');
221
- expect(availableTemplates.design).toContain('none');
222
- });
223
- });
224
- });