@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.
Files changed (159) hide show
  1. package/.turbo/turbo-build.log +4 -0
  2. package/.vibe/conversation-state.sqlite +0 -0
  3. package/LICENSE +674 -0
  4. package/dist/index.d.ts +9 -0
  5. package/dist/index.d.ts.map +1 -0
  6. package/dist/index.js +74 -0
  7. package/dist/index.js.map +1 -0
  8. package/dist/notification-service.d.ts +14 -0
  9. package/dist/notification-service.d.ts.map +1 -0
  10. package/dist/notification-service.js +18 -0
  11. package/dist/notification-service.js.map +1 -0
  12. package/dist/resource-handlers/conversation-state.d.ts +15 -0
  13. package/dist/resource-handlers/conversation-state.d.ts.map +1 -0
  14. package/dist/resource-handlers/conversation-state.js +40 -0
  15. package/dist/resource-handlers/conversation-state.js.map +1 -0
  16. package/dist/resource-handlers/development-plan.d.ts +14 -0
  17. package/dist/resource-handlers/development-plan.d.ts.map +1 -0
  18. package/dist/resource-handlers/development-plan.js +31 -0
  19. package/dist/resource-handlers/development-plan.js.map +1 -0
  20. package/dist/resource-handlers/index.d.ts +24 -0
  21. package/dist/resource-handlers/index.d.ts.map +1 -0
  22. package/dist/resource-handlers/index.js +62 -0
  23. package/dist/resource-handlers/index.js.map +1 -0
  24. package/dist/resource-handlers/system-prompt.d.ts +15 -0
  25. package/dist/resource-handlers/system-prompt.d.ts.map +1 -0
  26. package/dist/resource-handlers/system-prompt.js +40 -0
  27. package/dist/resource-handlers/system-prompt.js.map +1 -0
  28. package/dist/resource-handlers/workflow-resource.d.ts +15 -0
  29. package/dist/resource-handlers/workflow-resource.d.ts.map +1 -0
  30. package/dist/resource-handlers/workflow-resource.js +85 -0
  31. package/dist/resource-handlers/workflow-resource.js.map +1 -0
  32. package/dist/response-renderer.d.ts +30 -0
  33. package/dist/response-renderer.d.ts.map +1 -0
  34. package/dist/response-renderer.js +94 -0
  35. package/dist/response-renderer.js.map +1 -0
  36. package/dist/server-config.d.ts +34 -0
  37. package/dist/server-config.d.ts.map +1 -0
  38. package/dist/server-config.js +486 -0
  39. package/dist/server-config.js.map +1 -0
  40. package/dist/server-helpers.d.ts +62 -0
  41. package/dist/server-helpers.d.ts.map +1 -0
  42. package/dist/server-helpers.js +156 -0
  43. package/dist/server-helpers.js.map +1 -0
  44. package/dist/server-implementation.d.ts +74 -0
  45. package/dist/server-implementation.d.ts.map +1 -0
  46. package/dist/server-implementation.js +201 -0
  47. package/dist/server-implementation.js.map +1 -0
  48. package/dist/server.d.ts +6 -0
  49. package/dist/server.d.ts.map +1 -0
  50. package/dist/server.js +5 -0
  51. package/dist/server.js.map +1 -0
  52. package/dist/tool-handlers/base-tool-handler.d.ts +50 -0
  53. package/dist/tool-handlers/base-tool-handler.d.ts.map +1 -0
  54. package/dist/tool-handlers/base-tool-handler.js +74 -0
  55. package/dist/tool-handlers/base-tool-handler.js.map +1 -0
  56. package/dist/tool-handlers/conduct-review.d.ts +49 -0
  57. package/dist/tool-handlers/conduct-review.d.ts.map +1 -0
  58. package/dist/tool-handlers/conduct-review.js +105 -0
  59. package/dist/tool-handlers/conduct-review.js.map +1 -0
  60. package/dist/tool-handlers/get-tool-info.d.ts +76 -0
  61. package/dist/tool-handlers/get-tool-info.d.ts.map +1 -0
  62. package/dist/tool-handlers/get-tool-info.js +168 -0
  63. package/dist/tool-handlers/get-tool-info.js.map +1 -0
  64. package/dist/tool-handlers/index.d.ts +42 -0
  65. package/dist/tool-handlers/index.d.ts.map +1 -0
  66. package/dist/tool-handlers/index.js +74 -0
  67. package/dist/tool-handlers/index.js.map +1 -0
  68. package/dist/tool-handlers/install-workflow.d.ts +48 -0
  69. package/dist/tool-handlers/install-workflow.d.ts.map +1 -0
  70. package/dist/tool-handlers/install-workflow.js +131 -0
  71. package/dist/tool-handlers/install-workflow.js.map +1 -0
  72. package/dist/tool-handlers/list-workflows.d.ts +47 -0
  73. package/dist/tool-handlers/list-workflows.d.ts.map +1 -0
  74. package/dist/tool-handlers/list-workflows.js +58 -0
  75. package/dist/tool-handlers/list-workflows.js.map +1 -0
  76. package/dist/tool-handlers/no-idea.d.ts +41 -0
  77. package/dist/tool-handlers/no-idea.d.ts.map +1 -0
  78. package/dist/tool-handlers/no-idea.js +29 -0
  79. package/dist/tool-handlers/no-idea.js.map +1 -0
  80. package/dist/tool-handlers/proceed-to-phase.d.ts +39 -0
  81. package/dist/tool-handlers/proceed-to-phase.d.ts.map +1 -0
  82. package/dist/tool-handlers/proceed-to-phase.js +109 -0
  83. package/dist/tool-handlers/proceed-to-phase.js.map +1 -0
  84. package/dist/tool-handlers/reset-development.d.ts +31 -0
  85. package/dist/tool-handlers/reset-development.d.ts.map +1 -0
  86. package/dist/tool-handlers/reset-development.js +48 -0
  87. package/dist/tool-handlers/reset-development.js.map +1 -0
  88. package/dist/tool-handlers/resume-workflow.d.ts +88 -0
  89. package/dist/tool-handlers/resume-workflow.d.ts.map +1 -0
  90. package/dist/tool-handlers/resume-workflow.js +213 -0
  91. package/dist/tool-handlers/resume-workflow.js.map +1 -0
  92. package/dist/tool-handlers/setup-project-docs.d.ts +36 -0
  93. package/dist/tool-handlers/setup-project-docs.d.ts.map +1 -0
  94. package/dist/tool-handlers/setup-project-docs.js +136 -0
  95. package/dist/tool-handlers/setup-project-docs.js.map +1 -0
  96. package/dist/tool-handlers/start-development.d.ts +82 -0
  97. package/dist/tool-handlers/start-development.d.ts.map +1 -0
  98. package/dist/tool-handlers/start-development.js +448 -0
  99. package/dist/tool-handlers/start-development.js.map +1 -0
  100. package/dist/tool-handlers/whats-next.d.ts +42 -0
  101. package/dist/tool-handlers/whats-next.d.ts.map +1 -0
  102. package/dist/tool-handlers/whats-next.js +118 -0
  103. package/dist/tool-handlers/whats-next.js.map +1 -0
  104. package/dist/types.d.ts +114 -0
  105. package/dist/types.d.ts.map +1 -0
  106. package/dist/types.js +5 -0
  107. package/dist/types.js.map +1 -0
  108. package/package.json +29 -0
  109. package/src/index.ts +93 -0
  110. package/src/notification-service.ts +23 -0
  111. package/src/resource-handlers/conversation-state.ts +55 -0
  112. package/src/resource-handlers/development-plan.ts +48 -0
  113. package/src/resource-handlers/index.ts +73 -0
  114. package/src/resource-handlers/system-prompt.ts +55 -0
  115. package/src/resource-handlers/workflow-resource.ts +126 -0
  116. package/src/response-renderer.ts +116 -0
  117. package/src/server-config.ts +744 -0
  118. package/src/server-helpers.ts +225 -0
  119. package/src/server-implementation.ts +277 -0
  120. package/src/server.ts +9 -0
  121. package/src/tool-handlers/base-tool-handler.ts +141 -0
  122. package/src/tool-handlers/conduct-review.ts +191 -0
  123. package/src/tool-handlers/get-tool-info.ts +274 -0
  124. package/src/tool-handlers/index.ts +117 -0
  125. package/src/tool-handlers/install-workflow.ts +185 -0
  126. package/src/tool-handlers/list-workflows.ts +94 -0
  127. package/src/tool-handlers/no-idea.ts +47 -0
  128. package/src/tool-handlers/proceed-to-phase.ts +205 -0
  129. package/src/tool-handlers/reset-development.ts +90 -0
  130. package/src/tool-handlers/resume-workflow.ts +380 -0
  131. package/src/tool-handlers/setup-project-docs.ts +226 -0
  132. package/src/tool-handlers/start-development.ts +685 -0
  133. package/src/tool-handlers/whats-next.ts +235 -0
  134. package/src/types.ts +130 -0
  135. package/test/e2e/core-functionality.test.ts +176 -0
  136. package/test/e2e/mcp-contract.test.ts +540 -0
  137. package/test/e2e/plan-management.test.ts +331 -0
  138. package/test/e2e/state-management.test.ts +392 -0
  139. package/test/e2e/workflow-integration.test.ts +506 -0
  140. package/test/unit/commit-behaviour-interface.test.ts +244 -0
  141. package/test/unit/conduct-review.test.ts +151 -0
  142. package/test/unit/reset-functionality.test.ts +72 -0
  143. package/test/unit/resume-workflow.test.ts +192 -0
  144. package/test/unit/server-tools.test.ts +311 -0
  145. package/test/unit/setup-project-docs-handler.test.ts +267 -0
  146. package/test/unit/start-development-artifact-detection.test.ts +387 -0
  147. package/test/unit/start-development-gitignore.test.ts +178 -0
  148. package/test/unit/system-prompt-resource.test.ts +101 -0
  149. package/test/unit/tool-handlers/no-idea.test.ts +40 -0
  150. package/test/utils/e2e-test-setup.ts +453 -0
  151. package/test/utils/run-server-in-dir.sh +27 -0
  152. package/test/utils/temp-files.ts +308 -0
  153. package/test/utils/test-access.ts +79 -0
  154. package/test/utils/test-helpers.ts +286 -0
  155. package/test/utils/test-setup.ts +78 -0
  156. package/tsconfig.build.json +9 -0
  157. package/tsconfig.build.tsbuildinfo +1 -0
  158. package/tsconfig.json +12 -0
  159. 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
+ });