@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,392 @@
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
+
15
+ vi.unmock('fs');
16
+ vi.unmock('fs/promises');
17
+
18
+ /**
19
+ * State Management Tests
20
+ *
21
+ * Tests state machine functionality including:
22
+ * - Phase transitions and state machine logic
23
+ * - Custom state machine loading and validation
24
+ * - State persistence and consistency
25
+ * - Conversation state management across sessions
26
+ */
27
+ describe('State Management', () => {
28
+ let client: DirectServerInterface;
29
+ let tempProject: TempProject;
30
+ let cleanup: () => Promise<void>;
31
+
32
+ beforeEach(async () => {
33
+ const scenario = await createSuiteIsolatedE2EScenario({
34
+ suiteName: 'state-management',
35
+ tempProjectFactory: createTempProjectWithDefaultStateMachine,
36
+ });
37
+ client = scenario.client;
38
+ tempProject = scenario.tempProject;
39
+ cleanup = scenario.cleanup;
40
+
41
+ // Initialize development with default workflow before each test
42
+ await initializeDevelopment(client);
43
+ });
44
+
45
+ afterEach(async () => {
46
+ if (cleanup) {
47
+ await cleanup();
48
+ }
49
+ });
50
+
51
+ describe('Phase Transitions', () => {
52
+ it('should transition through standard development phases', async () => {
53
+ // Start with requirements
54
+ const start = await client.callTool('whats_next', {
55
+ user_input: 'implement feature',
56
+ });
57
+ expect(assertToolSuccess(start).phase).toBe('requirements');
58
+
59
+ // Transition to design
60
+ const design = await client.callTool('proceed_to_phase', {
61
+ target_phase: 'design',
62
+ reason: 'requirements complete',
63
+ review_state: 'not-required',
64
+ });
65
+ expect(assertToolSuccess(design).phase).toBe('design');
66
+
67
+ // Transition to implementation
68
+ const impl = await client.callTool('proceed_to_phase', {
69
+ target_phase: 'implementation',
70
+ reason: 'design complete',
71
+ review_state: 'not-required',
72
+ });
73
+ expect(assertToolSuccess(impl).phase).toBe('implementation');
74
+
75
+ // Verify state consistency
76
+ const stateResource = await client.readResource('state://current');
77
+ const stateData = JSON.parse(stateResource.contents[0].text);
78
+ expect(stateData.currentPhase).toBe('implementation');
79
+ });
80
+
81
+ it('should support non-linear phase transitions', async () => {
82
+ // Start conversation
83
+ await client.callTool('whats_next', { user_input: 'start' });
84
+
85
+ // Jump to testing phase
86
+ const testing = await client.callTool('proceed_to_phase', {
87
+ target_phase: 'testing',
88
+ reason: 'skip to testing',
89
+ review_state: 'not-required',
90
+ });
91
+ expect(assertToolSuccess(testing).phase).toBe('testing');
92
+
93
+ // Go back to design
94
+ const design = await client.callTool('proceed_to_phase', {
95
+ target_phase: 'design',
96
+ reason: 'need to revise design',
97
+ review_state: 'not-required',
98
+ });
99
+ expect(assertToolSuccess(design).phase).toBe('design');
100
+ });
101
+
102
+ it('should track phase transition history', async () => {
103
+ await client.callTool('whats_next', { user_input: 'start' });
104
+ await client.callTool('proceed_to_phase', {
105
+ target_phase: 'design',
106
+ reason: 'test',
107
+ review_state: 'not-required',
108
+ });
109
+ await client.callTool('proceed_to_phase', {
110
+ target_phase: 'implementation',
111
+ reason: 'test',
112
+ review_state: 'not-required',
113
+ });
114
+
115
+ const stateResource = await client.readResource('state://current');
116
+ const stateData = JSON.parse(stateResource.contents[0].text);
117
+
118
+ expect(stateData.currentPhase).toBe('implementation');
119
+ // Verify conversation maintains consistency
120
+ expect(stateData.conversationId).toBeTruthy();
121
+ });
122
+ });
123
+
124
+ describe('Custom State Machines', () => {
125
+ beforeEach(async () => {
126
+ // Reset any existing development state
127
+ await cleanup();
128
+
129
+ // Create a fresh test environment without initializing development
130
+ const scenario = await createSuiteIsolatedE2EScenario({
131
+ suiteName: 'state-management-custom',
132
+ tempProjectFactory: createTempProjectWithDefaultStateMachine,
133
+ });
134
+
135
+ client = scenario.client;
136
+ tempProject = scenario.tempProject;
137
+ cleanup = scenario.cleanup;
138
+ });
139
+
140
+ it('should load custom state machine from .vibe directory', async () => {
141
+ const vibeDir = path.join(tempProject.projectPath, '.vibe');
142
+ await fs.mkdir(vibeDir, { recursive: true });
143
+
144
+ const customStateMachine = `
145
+ name: "custom"
146
+ description: "Custom development workflow for testing"
147
+ initial_state: "planning"
148
+ states:
149
+ planning:
150
+ description: "Planning phase"
151
+ default_instructions: "Start planning the feature. Define what needs to be built and create a plan."
152
+ transitions:
153
+ - trigger: "planning_complete"
154
+ to: "building"
155
+ instructions: "Planning complete, start building"
156
+ transition_reason: "Planning phase finished, moving to building"
157
+ building:
158
+ description: "Building phase"
159
+ default_instructions: "Build the feature according to the plan. Focus on implementation and testing."
160
+ transitions:
161
+ - trigger: "build_finished"
162
+ to: "complete"
163
+ instructions: "Building complete, feature is ready"
164
+ transition_reason: "Building phase finished, feature complete"
165
+ complete:
166
+ description: "Completed"
167
+ default_instructions: "Feature is complete and ready for delivery."
168
+ transitions: []
169
+ `;
170
+
171
+ await fs.writeFile(
172
+ path.join(vibeDir, 'workflow.yaml'),
173
+ customStateMachine
174
+ );
175
+
176
+ // Initialize development with start_development and specify custom workflow
177
+ const result = await client.callTool('start_development', {
178
+ workflow: 'custom',
179
+ commit_behaviour: 'none',
180
+ });
181
+ const response = assertToolSuccess(result);
182
+
183
+ expect(response.phase).toBe('planning');
184
+ expect(response.instructions).toContain('whats_next()');
185
+ });
186
+
187
+ it('should support .yml extension for state machine files', async () => {
188
+ const vibeDir = path.join(tempProject.projectPath, '.vibe');
189
+ await fs.mkdir(vibeDir, { recursive: true });
190
+
191
+ const customStateMachine = `
192
+ name: "custom"
193
+ description: "Test .yml extension"
194
+ initial_state: "start"
195
+ states:
196
+ start:
197
+ description: "Start state"
198
+ default_instructions: "YML state machine loaded"
199
+ transitions: []
200
+ `;
201
+
202
+ await fs.writeFile(
203
+ path.join(vibeDir, 'workflow.yml'),
204
+ customStateMachine
205
+ );
206
+
207
+ // Initialize development with custom workflow
208
+ await client.callTool('start_development', {
209
+ workflow: 'custom',
210
+ commit_behaviour: 'none',
211
+ });
212
+
213
+ // Then call whats_next
214
+ const result = await client.callTool('whats_next', {
215
+ user_input: 'test yml',
216
+ });
217
+ const response = assertToolSuccess(result);
218
+
219
+ expect(response.phase).toBe('start');
220
+ expect(response.instructions).toContain('YML state machine loaded');
221
+ });
222
+
223
+ it('should fall back to default state machine on invalid custom configuration', async () => {
224
+ // Mock logger only for this test to suppress expected error output
225
+ const mockCreateLogger = vi.fn().mockReturnValue({
226
+ debug: vi.fn(),
227
+ info: vi.fn(),
228
+ warn: vi.fn(),
229
+ error: vi.fn(),
230
+ });
231
+
232
+ // Mock the module
233
+ vi.doMock('@codemcp/workflows-core', async () => {
234
+ const actual = await vi.importActual('@codemcp/workflows-core');
235
+ return {
236
+ ...actual,
237
+ createLogger: mockCreateLogger,
238
+ };
239
+ });
240
+
241
+ const vibeDir = path.join(tempProject.projectPath, '.vibe');
242
+ await fs.mkdir(vibeDir, { recursive: true });
243
+
244
+ // Create invalid YAML
245
+ await fs.writeFile(
246
+ path.join(vibeDir, 'workflow.yaml'),
247
+ 'invalid: yaml: ['
248
+ );
249
+
250
+ // Initialize development with waterfall workflow since custom will fail
251
+ await client.callTool('start_development', {
252
+ workflow: 'waterfall',
253
+ commit_behaviour: 'none',
254
+ });
255
+
256
+ const result = await client.callTool('whats_next', {
257
+ user_input: 'test fallback',
258
+ });
259
+ const response = assertToolSuccess(result);
260
+
261
+ // Should fall back to default state machine
262
+ expect(response.phase).toBeTruthy();
263
+ expect(response.instructions).toBeTruthy();
264
+ });
265
+
266
+ it('should use default state machine when no custom configuration exists', async () => {
267
+ // Initialize development with waterfall workflow
268
+ await client.callTool('start_development', {
269
+ workflow: 'waterfall',
270
+ commit_behaviour: 'none',
271
+ });
272
+
273
+ const result = await client.callTool('whats_next', {
274
+ user_input: 'test default',
275
+ });
276
+ const response = assertToolSuccess(result);
277
+
278
+ // Should use default phases
279
+ expect(['idle', 'requirements']).toContain(response.phase);
280
+ });
281
+ });
282
+
283
+ describe('State Persistence', () => {
284
+ it('should persist state across tool calls', async () => {
285
+ const first = await client.callTool('whats_next', {
286
+ user_input: 'start project',
287
+ });
288
+ const firstResponse = assertToolSuccess(first);
289
+
290
+ await client.callTool('proceed_to_phase', {
291
+ target_phase: 'design',
292
+ reason: 'move to design',
293
+ review_state: 'not-required',
294
+ });
295
+
296
+ const third = await client.callTool('whats_next', {
297
+ user_input: 'continue',
298
+ });
299
+ const thirdResponse = assertToolSuccess(third);
300
+
301
+ expect(firstResponse.conversation_id).toBe(thirdResponse.conversation_id);
302
+ expect(thirdResponse.phase).toBe('design');
303
+ });
304
+
305
+ it('should maintain conversation state consistency', async () => {
306
+ const result = await client.callTool('whats_next', {
307
+ user_input: 'test consistency',
308
+ });
309
+ const response = assertToolSuccess(result);
310
+
311
+ const stateResource = await client.readResource('state://current');
312
+ const stateData = JSON.parse(stateResource.contents[0].text);
313
+
314
+ expect(stateData.conversationId).toBe(response.conversation_id);
315
+ expect(stateData.currentPhase).toBe(response.phase);
316
+ });
317
+
318
+ it('should handle concurrent state updates', async () => {
319
+ // Initialize conversation
320
+ await client.callTool('whats_next', { user_input: 'start' });
321
+
322
+ // Make multiple rapid state changes
323
+ const promises = [
324
+ client.callTool('proceed_to_phase', {
325
+ target_phase: 'design',
326
+ reason: 'test1',
327
+ review_state: 'not-required',
328
+ }),
329
+ client.callTool('proceed_to_phase', {
330
+ target_phase: 'implementation',
331
+ reason: 'test2',
332
+ review_state: 'not-required',
333
+ }),
334
+ client.callTool('proceed_to_phase', {
335
+ target_phase: 'qa',
336
+ reason: 'test3',
337
+ review_state: 'not-required',
338
+ }),
339
+ ];
340
+
341
+ const results = await Promise.all(promises);
342
+
343
+ // All should succeed (though final state may vary)
344
+ for (const result of results) {
345
+ expect(result).toBeTruthy();
346
+ }
347
+
348
+ // Final state should be consistent
349
+ const stateResource = await client.readResource('state://current');
350
+ const stateData = JSON.parse(stateResource.contents[0].text);
351
+ expect(stateData.currentPhase).toBeTruthy();
352
+ });
353
+ });
354
+
355
+ describe('Conversation Context Management', () => {
356
+ it('should handle conversation context in whats_next calls', async () => {
357
+ // Call whats_next with rich context
358
+ const result = await client.callTool('whats_next', {
359
+ user_input: 'I want to implement authentication',
360
+ context: 'Starting new feature development',
361
+ conversation_summary: 'Starting new auth feature',
362
+ recent_messages: [
363
+ { role: 'user', content: 'I need authentication' },
364
+ { role: 'assistant', content: 'I can help with that' },
365
+ ],
366
+ });
367
+
368
+ const response = assertToolSuccess(result);
369
+ expect(response.phase).toBeTruthy();
370
+ expect(response.instructions).toBeTruthy();
371
+ });
372
+
373
+ it('should maintain context across phase transitions', async () => {
374
+ // Start with context
375
+ await client.callTool('whats_next', {
376
+ user_input: 'start feature',
377
+ context: 'new feature development',
378
+ });
379
+
380
+ // Transition with context
381
+ const transition = await client.callTool('proceed_to_phase', {
382
+ target_phase: 'design',
383
+ reason: 'requirements gathered',
384
+ review_state: 'not-required',
385
+ });
386
+
387
+ const response = assertToolSuccess(transition);
388
+ expect(response.phase).toBe('design');
389
+ expect(response.instructions).toBeTruthy();
390
+ });
391
+ });
392
+ });