@codemcp/workflows 3.1.21
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/.vibe/conversation-state.sqlite +0 -0
- package/LICENSE +674 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +74 -0
- package/dist/index.js.map +1 -0
- package/dist/notification-service.d.ts +14 -0
- package/dist/notification-service.d.ts.map +1 -0
- package/dist/notification-service.js +18 -0
- package/dist/notification-service.js.map +1 -0
- package/dist/resource-handlers/conversation-state.d.ts +15 -0
- package/dist/resource-handlers/conversation-state.d.ts.map +1 -0
- package/dist/resource-handlers/conversation-state.js +40 -0
- package/dist/resource-handlers/conversation-state.js.map +1 -0
- package/dist/resource-handlers/development-plan.d.ts +14 -0
- package/dist/resource-handlers/development-plan.d.ts.map +1 -0
- package/dist/resource-handlers/development-plan.js +31 -0
- package/dist/resource-handlers/development-plan.js.map +1 -0
- package/dist/resource-handlers/index.d.ts +24 -0
- package/dist/resource-handlers/index.d.ts.map +1 -0
- package/dist/resource-handlers/index.js +62 -0
- package/dist/resource-handlers/index.js.map +1 -0
- package/dist/resource-handlers/system-prompt.d.ts +15 -0
- package/dist/resource-handlers/system-prompt.d.ts.map +1 -0
- package/dist/resource-handlers/system-prompt.js +40 -0
- package/dist/resource-handlers/system-prompt.js.map +1 -0
- package/dist/resource-handlers/workflow-resource.d.ts +15 -0
- package/dist/resource-handlers/workflow-resource.d.ts.map +1 -0
- package/dist/resource-handlers/workflow-resource.js +85 -0
- package/dist/resource-handlers/workflow-resource.js.map +1 -0
- package/dist/response-renderer.d.ts +30 -0
- package/dist/response-renderer.d.ts.map +1 -0
- package/dist/response-renderer.js +94 -0
- package/dist/response-renderer.js.map +1 -0
- package/dist/server-config.d.ts +34 -0
- package/dist/server-config.d.ts.map +1 -0
- package/dist/server-config.js +486 -0
- package/dist/server-config.js.map +1 -0
- package/dist/server-helpers.d.ts +62 -0
- package/dist/server-helpers.d.ts.map +1 -0
- package/dist/server-helpers.js +156 -0
- package/dist/server-helpers.js.map +1 -0
- package/dist/server-implementation.d.ts +74 -0
- package/dist/server-implementation.d.ts.map +1 -0
- package/dist/server-implementation.js +201 -0
- package/dist/server-implementation.js.map +1 -0
- package/dist/server.d.ts +6 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +5 -0
- package/dist/server.js.map +1 -0
- package/dist/tool-handlers/base-tool-handler.d.ts +50 -0
- package/dist/tool-handlers/base-tool-handler.d.ts.map +1 -0
- package/dist/tool-handlers/base-tool-handler.js +74 -0
- package/dist/tool-handlers/base-tool-handler.js.map +1 -0
- package/dist/tool-handlers/conduct-review.d.ts +49 -0
- package/dist/tool-handlers/conduct-review.d.ts.map +1 -0
- package/dist/tool-handlers/conduct-review.js +105 -0
- package/dist/tool-handlers/conduct-review.js.map +1 -0
- package/dist/tool-handlers/get-tool-info.d.ts +76 -0
- package/dist/tool-handlers/get-tool-info.d.ts.map +1 -0
- package/dist/tool-handlers/get-tool-info.js +168 -0
- package/dist/tool-handlers/get-tool-info.js.map +1 -0
- package/dist/tool-handlers/index.d.ts +42 -0
- package/dist/tool-handlers/index.d.ts.map +1 -0
- package/dist/tool-handlers/index.js +74 -0
- package/dist/tool-handlers/index.js.map +1 -0
- package/dist/tool-handlers/install-workflow.d.ts +48 -0
- package/dist/tool-handlers/install-workflow.d.ts.map +1 -0
- package/dist/tool-handlers/install-workflow.js +131 -0
- package/dist/tool-handlers/install-workflow.js.map +1 -0
- package/dist/tool-handlers/list-workflows.d.ts +47 -0
- package/dist/tool-handlers/list-workflows.d.ts.map +1 -0
- package/dist/tool-handlers/list-workflows.js +58 -0
- package/dist/tool-handlers/list-workflows.js.map +1 -0
- package/dist/tool-handlers/no-idea.d.ts +41 -0
- package/dist/tool-handlers/no-idea.d.ts.map +1 -0
- package/dist/tool-handlers/no-idea.js +29 -0
- package/dist/tool-handlers/no-idea.js.map +1 -0
- package/dist/tool-handlers/proceed-to-phase.d.ts +39 -0
- package/dist/tool-handlers/proceed-to-phase.d.ts.map +1 -0
- package/dist/tool-handlers/proceed-to-phase.js +109 -0
- package/dist/tool-handlers/proceed-to-phase.js.map +1 -0
- package/dist/tool-handlers/reset-development.d.ts +31 -0
- package/dist/tool-handlers/reset-development.d.ts.map +1 -0
- package/dist/tool-handlers/reset-development.js +48 -0
- package/dist/tool-handlers/reset-development.js.map +1 -0
- package/dist/tool-handlers/resume-workflow.d.ts +88 -0
- package/dist/tool-handlers/resume-workflow.d.ts.map +1 -0
- package/dist/tool-handlers/resume-workflow.js +213 -0
- package/dist/tool-handlers/resume-workflow.js.map +1 -0
- package/dist/tool-handlers/setup-project-docs.d.ts +36 -0
- package/dist/tool-handlers/setup-project-docs.d.ts.map +1 -0
- package/dist/tool-handlers/setup-project-docs.js +136 -0
- package/dist/tool-handlers/setup-project-docs.js.map +1 -0
- package/dist/tool-handlers/start-development.d.ts +82 -0
- package/dist/tool-handlers/start-development.d.ts.map +1 -0
- package/dist/tool-handlers/start-development.js +448 -0
- package/dist/tool-handlers/start-development.js.map +1 -0
- package/dist/tool-handlers/whats-next.d.ts +42 -0
- package/dist/tool-handlers/whats-next.d.ts.map +1 -0
- package/dist/tool-handlers/whats-next.js +118 -0
- package/dist/tool-handlers/whats-next.js.map +1 -0
- package/dist/types.d.ts +114 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/package.json +29 -0
- package/src/index.ts +93 -0
- package/src/notification-service.ts +23 -0
- package/src/resource-handlers/conversation-state.ts +55 -0
- package/src/resource-handlers/development-plan.ts +48 -0
- package/src/resource-handlers/index.ts +73 -0
- package/src/resource-handlers/system-prompt.ts +55 -0
- package/src/resource-handlers/workflow-resource.ts +126 -0
- package/src/response-renderer.ts +116 -0
- package/src/server-config.ts +744 -0
- package/src/server-helpers.ts +225 -0
- package/src/server-implementation.ts +277 -0
- package/src/server.ts +9 -0
- package/src/tool-handlers/base-tool-handler.ts +141 -0
- package/src/tool-handlers/conduct-review.ts +191 -0
- package/src/tool-handlers/get-tool-info.ts +274 -0
- package/src/tool-handlers/index.ts +117 -0
- package/src/tool-handlers/install-workflow.ts +185 -0
- package/src/tool-handlers/list-workflows.ts +94 -0
- package/src/tool-handlers/no-idea.ts +47 -0
- package/src/tool-handlers/proceed-to-phase.ts +205 -0
- package/src/tool-handlers/reset-development.ts +90 -0
- package/src/tool-handlers/resume-workflow.ts +380 -0
- package/src/tool-handlers/setup-project-docs.ts +226 -0
- package/src/tool-handlers/start-development.ts +685 -0
- package/src/tool-handlers/whats-next.ts +235 -0
- package/src/types.ts +130 -0
- package/test/e2e/core-functionality.test.ts +176 -0
- package/test/e2e/mcp-contract.test.ts +540 -0
- package/test/e2e/plan-management.test.ts +331 -0
- package/test/e2e/state-management.test.ts +392 -0
- package/test/e2e/workflow-integration.test.ts +506 -0
- package/test/unit/commit-behaviour-interface.test.ts +244 -0
- package/test/unit/conduct-review.test.ts +151 -0
- package/test/unit/reset-functionality.test.ts +72 -0
- package/test/unit/resume-workflow.test.ts +192 -0
- package/test/unit/server-tools.test.ts +311 -0
- package/test/unit/setup-project-docs-handler.test.ts +267 -0
- package/test/unit/start-development-artifact-detection.test.ts +387 -0
- package/test/unit/start-development-gitignore.test.ts +178 -0
- package/test/unit/system-prompt-resource.test.ts +101 -0
- package/test/unit/tool-handlers/no-idea.test.ts +40 -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 +9 -0
- package/tsconfig.build.tsbuildinfo +1 -0
- package/tsconfig.json +12 -0
- package/vitest.config.ts +17 -0
|
@@ -0,0 +1,506 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
2
|
+
import {
|
|
3
|
+
TempProject,
|
|
4
|
+
createTempProjectWithDefaultStateMachine,
|
|
5
|
+
} from '../utils/temp-files';
|
|
6
|
+
import {
|
|
7
|
+
DirectServerInterface,
|
|
8
|
+
createSuiteIsolatedE2EScenario,
|
|
9
|
+
assertToolSuccess,
|
|
10
|
+
initializeDevelopment,
|
|
11
|
+
} from '../utils/e2e-test-setup';
|
|
12
|
+
import { promises as fs } from 'node:fs';
|
|
13
|
+
import path from 'node:path';
|
|
14
|
+
import { McpToolResponse } from '../../src/types';
|
|
15
|
+
|
|
16
|
+
vi.unmock('fs');
|
|
17
|
+
vi.unmock('fs/promises');
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Workflow Integration Tests
|
|
21
|
+
*
|
|
22
|
+
* Tests complete end-to-end workflows including:
|
|
23
|
+
* - Full development lifecycle scenarios
|
|
24
|
+
* - Multi-phase project progression
|
|
25
|
+
* - Real-world usage patterns
|
|
26
|
+
* - Integration between all components
|
|
27
|
+
*/
|
|
28
|
+
describe('Workflow Integration', () => {
|
|
29
|
+
let client: DirectServerInterface;
|
|
30
|
+
let tempProject: TempProject;
|
|
31
|
+
let cleanup: () => Promise<void>;
|
|
32
|
+
|
|
33
|
+
beforeEach(async () => {
|
|
34
|
+
const scenario = await createSuiteIsolatedE2EScenario({
|
|
35
|
+
suiteName: 'workflow-integration',
|
|
36
|
+
tempProjectFactory: createTempProjectWithDefaultStateMachine,
|
|
37
|
+
});
|
|
38
|
+
client = scenario.client;
|
|
39
|
+
tempProject = scenario.tempProject;
|
|
40
|
+
cleanup = scenario.cleanup;
|
|
41
|
+
|
|
42
|
+
// Start development for all workflow integration tests
|
|
43
|
+
await initializeDevelopment(client, 'waterfall');
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
afterEach(async () => {
|
|
47
|
+
if (cleanup) {
|
|
48
|
+
await cleanup();
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
describe('Complete Development Lifecycle', () => {
|
|
53
|
+
it('should handle full feature development workflow', async () => {
|
|
54
|
+
// 1. Start with requirements
|
|
55
|
+
const requirements = await client.callTool('whats_next', {
|
|
56
|
+
user_input: 'implement user authentication system',
|
|
57
|
+
context: 'new feature request',
|
|
58
|
+
conversation_summary: 'User wants to add authentication to their app',
|
|
59
|
+
});
|
|
60
|
+
const reqResponse = assertToolSuccess(requirements);
|
|
61
|
+
expect(reqResponse.phase).toBe('requirements');
|
|
62
|
+
expect(reqResponse.instructions).toContain('requirements');
|
|
63
|
+
|
|
64
|
+
// 2. Transition to design
|
|
65
|
+
const design = await client.callTool('proceed_to_phase', {
|
|
66
|
+
target_phase: 'design',
|
|
67
|
+
reason: 'requirements analysis complete',
|
|
68
|
+
review_state: 'not-required',
|
|
69
|
+
});
|
|
70
|
+
const designResponse = assertToolSuccess(design);
|
|
71
|
+
expect(designResponse.phase).toBe('design');
|
|
72
|
+
expect(designResponse.instructions).toContain('design');
|
|
73
|
+
|
|
74
|
+
// 3. Move to implementation
|
|
75
|
+
const implementation = await client.callTool('proceed_to_phase', {
|
|
76
|
+
target_phase: 'implementation',
|
|
77
|
+
reason: 'design approved',
|
|
78
|
+
review_state: 'not-required',
|
|
79
|
+
});
|
|
80
|
+
const implResponse = assertToolSuccess(implementation);
|
|
81
|
+
expect(implResponse.phase).toBe('implementation');
|
|
82
|
+
|
|
83
|
+
// 4. Quality assurance
|
|
84
|
+
const qa = await client.callTool('proceed_to_phase', {
|
|
85
|
+
target_phase: 'qa',
|
|
86
|
+
reason: 'implementation complete',
|
|
87
|
+
review_state: 'not-required',
|
|
88
|
+
});
|
|
89
|
+
const qaResponse = assertToolSuccess(qa);
|
|
90
|
+
expect(qaResponse.phase).toBe('qa');
|
|
91
|
+
|
|
92
|
+
// 5. Testing phase
|
|
93
|
+
const testing = await client.callTool('proceed_to_phase', {
|
|
94
|
+
target_phase: 'testing',
|
|
95
|
+
reason: 'qa passed',
|
|
96
|
+
review_state: 'not-required',
|
|
97
|
+
});
|
|
98
|
+
const testResponse = assertToolSuccess(testing);
|
|
99
|
+
expect(testResponse.phase).toBe('testing');
|
|
100
|
+
|
|
101
|
+
// 6. Finalize
|
|
102
|
+
const finalize = await client.callTool('proceed_to_phase', {
|
|
103
|
+
target_phase: 'finalize',
|
|
104
|
+
reason: 'all tests passed',
|
|
105
|
+
review_state: 'not-required',
|
|
106
|
+
});
|
|
107
|
+
const finalizeResponse = assertToolSuccess(finalize);
|
|
108
|
+
expect(finalizeResponse.phase).toBe('finalize');
|
|
109
|
+
|
|
110
|
+
// Verify final state
|
|
111
|
+
const stateResource = await client.readResource('state://current');
|
|
112
|
+
const stateData = JSON.parse(stateResource.contents[0].text);
|
|
113
|
+
expect(stateData.currentPhase).toBe('finalize');
|
|
114
|
+
expect(stateData.conversationId).toBe(reqResponse.conversation_id);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('should handle iterative development with phase revisiting', async () => {
|
|
118
|
+
// Start project
|
|
119
|
+
await client.callTool('whats_next', { user_input: 'start project' });
|
|
120
|
+
|
|
121
|
+
// Go to implementation
|
|
122
|
+
await client.callTool('proceed_to_phase', {
|
|
123
|
+
target_phase: 'implementation',
|
|
124
|
+
reason: 'quick prototype',
|
|
125
|
+
review_state: 'not-required',
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
// Realize need to go back to design
|
|
129
|
+
const backToDesign = await client.callTool('proceed_to_phase', {
|
|
130
|
+
target_phase: 'design',
|
|
131
|
+
reason: 'need to revise architecture',
|
|
132
|
+
review_state: 'not-required',
|
|
133
|
+
});
|
|
134
|
+
expect(assertToolSuccess(backToDesign).phase).toBe('design');
|
|
135
|
+
|
|
136
|
+
// Forward to implementation again
|
|
137
|
+
const backToImpl = await client.callTool('proceed_to_phase', {
|
|
138
|
+
target_phase: 'implementation',
|
|
139
|
+
reason: 'design revised',
|
|
140
|
+
review_state: 'not-required',
|
|
141
|
+
});
|
|
142
|
+
expect(assertToolSuccess(backToImpl).phase).toBe('implementation');
|
|
143
|
+
|
|
144
|
+
// Verify state consistency throughout
|
|
145
|
+
const stateResource = await client.readResource('state://current');
|
|
146
|
+
const stateData = JSON.parse(stateResource.contents[0].text);
|
|
147
|
+
expect(stateData.currentPhase).toBe('implementation');
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it('should maintain plan file consistency throughout workflow', async () => {
|
|
151
|
+
// Start workflow
|
|
152
|
+
const start = await client.callTool('whats_next', {
|
|
153
|
+
user_input: 'comprehensive project',
|
|
154
|
+
});
|
|
155
|
+
const startResponse = assertToolSuccess(start);
|
|
156
|
+
const planPath = startResponse.plan_file_path;
|
|
157
|
+
|
|
158
|
+
// Progress through phases
|
|
159
|
+
await client.callTool('proceed_to_phase', {
|
|
160
|
+
target_phase: 'design',
|
|
161
|
+
reason: 'test',
|
|
162
|
+
review_state: 'not-required',
|
|
163
|
+
});
|
|
164
|
+
await client.callTool('proceed_to_phase', {
|
|
165
|
+
target_phase: 'implementation',
|
|
166
|
+
reason: 'test',
|
|
167
|
+
review_state: 'not-required',
|
|
168
|
+
});
|
|
169
|
+
await client.callTool('proceed_to_phase', {
|
|
170
|
+
target_phase: 'qa',
|
|
171
|
+
reason: 'test',
|
|
172
|
+
review_state: 'not-required',
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
// Verify plan file exists and is updated
|
|
176
|
+
const planExists = await fs
|
|
177
|
+
.access(planPath)
|
|
178
|
+
.then(() => true)
|
|
179
|
+
.catch(() => false);
|
|
180
|
+
expect(planExists).toBe(true);
|
|
181
|
+
|
|
182
|
+
const planResource = await client.readResource('plan://current');
|
|
183
|
+
const planContent = planResource.contents[0].text;
|
|
184
|
+
|
|
185
|
+
// Should contain all phase sections
|
|
186
|
+
expect(planContent).toContain('Requirements');
|
|
187
|
+
expect(planContent).toContain('Design');
|
|
188
|
+
expect(planContent).toContain('Implementation');
|
|
189
|
+
expect(planContent).toContain('## Qa');
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
describe('Multi-Project Scenarios', () => {
|
|
194
|
+
it('should handle project context switching', async () => {
|
|
195
|
+
// Start first project context
|
|
196
|
+
const project1 = await client.callTool('whats_next', {
|
|
197
|
+
user_input: 'project 1 feature',
|
|
198
|
+
context: 'first project',
|
|
199
|
+
});
|
|
200
|
+
const p1Response = assertToolSuccess(project1);
|
|
201
|
+
const p1ConversationId = p1Response.conversation_id;
|
|
202
|
+
|
|
203
|
+
// Continue with same project
|
|
204
|
+
const project1Continue = await client.callTool('whats_next', {
|
|
205
|
+
user_input: 'continue project 1',
|
|
206
|
+
context: 'same project context',
|
|
207
|
+
});
|
|
208
|
+
const p1ContinueResponse = assertToolSuccess(project1Continue);
|
|
209
|
+
|
|
210
|
+
// Should maintain same conversation
|
|
211
|
+
expect(p1ContinueResponse.conversation_id).toBe(p1ConversationId);
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
it('should maintain separate plan files for different contexts', async () => {
|
|
215
|
+
const result = await client.callTool('whats_next', {
|
|
216
|
+
user_input: 'test plan separation',
|
|
217
|
+
});
|
|
218
|
+
const response = assertToolSuccess(result);
|
|
219
|
+
|
|
220
|
+
// Plan file should be specific to this project/branch context
|
|
221
|
+
expect(response.plan_file_path).toContain(tempProject.projectPath);
|
|
222
|
+
expect(response.plan_file_path).toContain('.vibe');
|
|
223
|
+
});
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
describe('Real-World Usage Patterns', () => {
|
|
227
|
+
it('should handle typical user interaction patterns', async () => {
|
|
228
|
+
// User starts with vague request
|
|
229
|
+
const vague = await client.callTool('whats_next', {
|
|
230
|
+
user_input: 'I need to add some features',
|
|
231
|
+
context: 'user has general idea',
|
|
232
|
+
});
|
|
233
|
+
expect(assertToolSuccess(vague).phase).toBe('requirements');
|
|
234
|
+
|
|
235
|
+
// User provides more specific information
|
|
236
|
+
const specific = await client.callTool('whats_next', {
|
|
237
|
+
user_input: 'I need user login and dashboard',
|
|
238
|
+
context: 'user clarified requirements',
|
|
239
|
+
conversation_summary: 'User wants login and dashboard features',
|
|
240
|
+
});
|
|
241
|
+
const specificResponse = assertToolSuccess(specific);
|
|
242
|
+
expect(specificResponse.phase).toBe('requirements');
|
|
243
|
+
|
|
244
|
+
// User ready to move forward
|
|
245
|
+
const ready = await client.callTool('proceed_to_phase', {
|
|
246
|
+
target_phase: 'design',
|
|
247
|
+
reason: 'requirements clear',
|
|
248
|
+
review_state: 'not-required',
|
|
249
|
+
});
|
|
250
|
+
expect(assertToolSuccess(ready).phase).toBe('design');
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
it('should handle context-rich conversations', async () => {
|
|
254
|
+
const result = await client.callTool('whats_next', {
|
|
255
|
+
user_input: 'implement OAuth integration',
|
|
256
|
+
context: 'user wants third-party authentication',
|
|
257
|
+
conversation_summary:
|
|
258
|
+
'Discussed authentication options, user prefers OAuth with Google and GitHub',
|
|
259
|
+
recent_messages: [
|
|
260
|
+
{ role: 'user', content: 'What authentication options do we have?' },
|
|
261
|
+
{
|
|
262
|
+
role: 'assistant',
|
|
263
|
+
content: 'We can use OAuth, JWT, or traditional sessions',
|
|
264
|
+
},
|
|
265
|
+
{
|
|
266
|
+
role: 'user',
|
|
267
|
+
content: 'OAuth sounds good, especially Google and GitHub',
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
role: 'assistant',
|
|
271
|
+
content: 'Great choice! OAuth is secure and user-friendly',
|
|
272
|
+
},
|
|
273
|
+
],
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
const response = assertToolSuccess(result);
|
|
277
|
+
expect(response.phase).toBeTruthy();
|
|
278
|
+
expect(response.instructions).toBeTruthy();
|
|
279
|
+
expect(response.conversation_id).toBeTruthy();
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
it('should handle rapid development iterations', async () => {
|
|
283
|
+
// Quick succession of development activities
|
|
284
|
+
await client.callTool('whats_next', { user_input: 'rapid prototype' });
|
|
285
|
+
await client.callTool('proceed_to_phase', {
|
|
286
|
+
target_phase: 'implementation',
|
|
287
|
+
reason: 'skip to coding',
|
|
288
|
+
review_state: 'not-required',
|
|
289
|
+
});
|
|
290
|
+
await client.callTool('whats_next', { user_input: 'found issues' });
|
|
291
|
+
await client.callTool('proceed_to_phase', {
|
|
292
|
+
target_phase: 'design',
|
|
293
|
+
reason: 'need better design',
|
|
294
|
+
review_state: 'not-required',
|
|
295
|
+
});
|
|
296
|
+
await client.callTool('proceed_to_phase', {
|
|
297
|
+
target_phase: 'implementation',
|
|
298
|
+
reason: 'design fixed',
|
|
299
|
+
review_state: 'not-required',
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
const final = await client.callTool('whats_next', {
|
|
303
|
+
user_input: 'check status',
|
|
304
|
+
});
|
|
305
|
+
const finalResponse = assertToolSuccess(final);
|
|
306
|
+
|
|
307
|
+
expect(finalResponse.phase).toBe('implementation');
|
|
308
|
+
expect(finalResponse.conversation_id).toBeTruthy();
|
|
309
|
+
});
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
describe('Error Recovery and Resilience', () => {
|
|
313
|
+
it('should recover from invalid phase transitions', async () => {
|
|
314
|
+
await client.callTool('whats_next', { user_input: 'start' });
|
|
315
|
+
|
|
316
|
+
// Try invalid transition
|
|
317
|
+
const result: McpToolResponse = await client.callTool(
|
|
318
|
+
'proceed_to_phase',
|
|
319
|
+
{
|
|
320
|
+
target_phase: 'invalid_phase',
|
|
321
|
+
reason: 'test error handling',
|
|
322
|
+
review_state: 'not-required',
|
|
323
|
+
}
|
|
324
|
+
);
|
|
325
|
+
|
|
326
|
+
// Verify error was handled properly
|
|
327
|
+
expect(result.error).toBeDefined();
|
|
328
|
+
expect(result.error).toContain('Invalid target phase');
|
|
329
|
+
|
|
330
|
+
// Should still be able to continue normally
|
|
331
|
+
const recovery = await client.callTool('whats_next', {
|
|
332
|
+
user_input: 'continue after error',
|
|
333
|
+
});
|
|
334
|
+
const recoveryResponse = assertToolSuccess(recovery);
|
|
335
|
+
|
|
336
|
+
expect(recoveryResponse.phase).toBeTruthy();
|
|
337
|
+
expect(recoveryResponse.instructions).toBeTruthy();
|
|
338
|
+
|
|
339
|
+
// Note: Error logging is mocked and suppressed for this test
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
it('should handle file system issues gracefully', async () => {
|
|
343
|
+
const result = await client.callTool('whats_next', {
|
|
344
|
+
user_input: 'test file system resilience',
|
|
345
|
+
});
|
|
346
|
+
const response = assertToolSuccess(result);
|
|
347
|
+
|
|
348
|
+
// Even if there are file system issues, basic functionality should work
|
|
349
|
+
expect(response.phase).toBeTruthy();
|
|
350
|
+
expect(response.conversation_id).toBeTruthy();
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
it('should maintain functionality under stress', async () => {
|
|
354
|
+
// Rapid fire requests
|
|
355
|
+
const promises = Array.from({ length: 10 }, (_, i) =>
|
|
356
|
+
client.callTool('whats_next', {
|
|
357
|
+
user_input: `stress test ${i}`,
|
|
358
|
+
context: `iteration ${i}`,
|
|
359
|
+
})
|
|
360
|
+
);
|
|
361
|
+
|
|
362
|
+
const results = await Promise.all(promises);
|
|
363
|
+
|
|
364
|
+
// All requests should succeed
|
|
365
|
+
for (const result of results) {
|
|
366
|
+
const response = assertToolSuccess(result);
|
|
367
|
+
expect(response.phase).toBeTruthy();
|
|
368
|
+
expect(response.conversation_id).toBeTruthy();
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// Final state should be consistent
|
|
372
|
+
const stateResource = await client.readResource('state://current');
|
|
373
|
+
const stateData = JSON.parse(stateResource.contents[0].text);
|
|
374
|
+
expect(stateData.currentPhase).toBeTruthy();
|
|
375
|
+
});
|
|
376
|
+
});
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
// Custom Workflow Integration tests need their own setup without start_development in beforeEach
|
|
380
|
+
describe('Workflow Integration - Custom State Machines', () => {
|
|
381
|
+
let client: DirectServerInterface;
|
|
382
|
+
let tempProject: TempProject;
|
|
383
|
+
let cleanup: () => Promise<void>;
|
|
384
|
+
|
|
385
|
+
beforeEach(async () => {
|
|
386
|
+
const scenario = await createSuiteIsolatedE2EScenario({
|
|
387
|
+
suiteName: 'workflow-integration-custom',
|
|
388
|
+
tempProjectFactory: createTempProjectWithDefaultStateMachine,
|
|
389
|
+
});
|
|
390
|
+
client = scenario.client;
|
|
391
|
+
tempProject = scenario.tempProject;
|
|
392
|
+
cleanup = scenario.cleanup;
|
|
393
|
+
// Note: NOT calling start_development here - custom workflow tests need to start fresh
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
afterEach(async () => {
|
|
397
|
+
if (cleanup) {
|
|
398
|
+
await cleanup();
|
|
399
|
+
}
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
describe('Custom Workflow Integration', () => {
|
|
403
|
+
it('should integrate custom state machines with full workflow', async () => {
|
|
404
|
+
// Create custom state machine
|
|
405
|
+
const vibeDir = path.join(tempProject.projectPath, '.vibe');
|
|
406
|
+
await fs.mkdir(vibeDir, { recursive: true });
|
|
407
|
+
|
|
408
|
+
const customWorkflow = `
|
|
409
|
+
name: "custom"
|
|
410
|
+
description: "Custom agile development workflow"
|
|
411
|
+
initial_state: "backlog"
|
|
412
|
+
states:
|
|
413
|
+
backlog:
|
|
414
|
+
description: "Feature backlog"
|
|
415
|
+
default_instructions: "Manage feature backlog and prioritize items"
|
|
416
|
+
transitions:
|
|
417
|
+
- trigger: "ready_for_sprint"
|
|
418
|
+
to: "sprint_planning"
|
|
419
|
+
instructions: "Backlog prioritized, ready for sprint planning"
|
|
420
|
+
transition_reason: "Backlog items prioritized and ready for sprint"
|
|
421
|
+
sprint_planning:
|
|
422
|
+
description: "Sprint planning"
|
|
423
|
+
default_instructions: "Plan sprint activities and estimate effort"
|
|
424
|
+
transitions:
|
|
425
|
+
- trigger: "sprint_planned"
|
|
426
|
+
to: "development"
|
|
427
|
+
instructions: "Sprint planned, ready to start development"
|
|
428
|
+
transition_reason: "Sprint planning complete, ready to develop"
|
|
429
|
+
development:
|
|
430
|
+
description: "Active development"
|
|
431
|
+
default_instructions: "Implement features according to sprint plan"
|
|
432
|
+
transitions:
|
|
433
|
+
- trigger: "development_complete"
|
|
434
|
+
to: "review"
|
|
435
|
+
instructions: "Development complete, ready for code review"
|
|
436
|
+
transition_reason: "Development phase finished, ready for review"
|
|
437
|
+
review:
|
|
438
|
+
description: "Code review"
|
|
439
|
+
default_instructions: "Review code and validate implementation"
|
|
440
|
+
transitions:
|
|
441
|
+
- trigger: "review_approved"
|
|
442
|
+
to: "done"
|
|
443
|
+
instructions: "Review approved, sprint complete"
|
|
444
|
+
transition_reason: "Code review passed, ready to complete sprint"
|
|
445
|
+
done:
|
|
446
|
+
description: "Sprint complete"
|
|
447
|
+
default_instructions: "Sprint complete, prepare for next iteration"
|
|
448
|
+
transitions: []
|
|
449
|
+
`;
|
|
450
|
+
|
|
451
|
+
await fs.writeFile(path.join(vibeDir, 'workflow.yaml'), customWorkflow);
|
|
452
|
+
|
|
453
|
+
// First, initialize development with the custom workflow
|
|
454
|
+
const initResult = await client.callTool('start_development', {
|
|
455
|
+
workflow: 'custom',
|
|
456
|
+
commit_behaviour: 'none',
|
|
457
|
+
});
|
|
458
|
+
assertToolSuccess(initResult);
|
|
459
|
+
|
|
460
|
+
// Then call whats_next to get instructions
|
|
461
|
+
const start = await client.callTool('whats_next', {
|
|
462
|
+
user_input: 'start agile sprint development',
|
|
463
|
+
context: 'new feature request',
|
|
464
|
+
});
|
|
465
|
+
const startResponse = assertToolSuccess(start);
|
|
466
|
+
|
|
467
|
+
// The server may start at any valid phase in the custom state machine
|
|
468
|
+
// Let's accept any of the valid phases from our custom workflow
|
|
469
|
+
expect([
|
|
470
|
+
'backlog',
|
|
471
|
+
'sprint_planning',
|
|
472
|
+
'development',
|
|
473
|
+
'review',
|
|
474
|
+
'done',
|
|
475
|
+
]).toContain(startResponse.phase);
|
|
476
|
+
|
|
477
|
+
// Progress through custom phases - start from whatever phase we're in
|
|
478
|
+
let currentPhase = startResponse.phase;
|
|
479
|
+
|
|
480
|
+
// If we're not already at development, try to get there
|
|
481
|
+
if (currentPhase !== 'development') {
|
|
482
|
+
const development = await client.callTool('proceed_to_phase', {
|
|
483
|
+
target_phase: 'development',
|
|
484
|
+
reason: 'ready to develop',
|
|
485
|
+
review_state: 'not-required',
|
|
486
|
+
});
|
|
487
|
+
const devResponse = assertToolSuccess(development);
|
|
488
|
+
expect(devResponse.phase).toBe('development');
|
|
489
|
+
currentPhase = 'development';
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
// Verify we can transition to review
|
|
493
|
+
const review = await client.callTool('proceed_to_phase', {
|
|
494
|
+
target_phase: 'review',
|
|
495
|
+
reason: 'development complete',
|
|
496
|
+
review_state: 'not-required',
|
|
497
|
+
});
|
|
498
|
+
expect(assertToolSuccess(review).phase).toBe('review');
|
|
499
|
+
|
|
500
|
+
// Verify plan file integration
|
|
501
|
+
const planResource = await client.readResource('plan://current');
|
|
502
|
+
const planContent = planResource.contents[0].text;
|
|
503
|
+
expect(planContent).toContain('Development Plan');
|
|
504
|
+
});
|
|
505
|
+
});
|
|
506
|
+
});
|