@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,244 @@
1
+ /**
2
+ * Test the new commit_behaviour interface
3
+ */
4
+
5
+ import { describe, it, expect, beforeEach, vi } from 'vitest';
6
+ import { StartDevelopmentHandler } from '../../packages/mcp-server/src/tool-handlers/start-development.js';
7
+ import * as GitManagerModule from '@codemcp/workflows-core';
8
+ import type { ServerContext } from '../../packages/mcp-server/src/types.js';
9
+
10
+ // Spy on GitManager methods
11
+ const isGitRepositorySpy = vi.spyOn(
12
+ GitManagerModule.GitManager,
13
+ 'isGitRepository'
14
+ );
15
+ const getCurrentCommitHashSpy = vi.spyOn(
16
+ GitManagerModule.GitManager,
17
+ 'getCurrentCommitHash'
18
+ );
19
+
20
+ describe('Commit Behaviour Interface', () => {
21
+ beforeEach(() => {
22
+ vi.clearAllMocks();
23
+ isGitRepositorySpy.mockReturnValue(true);
24
+ getCurrentCommitHashSpy.mockReturnValue('abc123');
25
+ });
26
+
27
+ it('should translate commit_behaviour "step" to correct git config', async () => {
28
+ const handler = new StartDevelopmentHandler();
29
+ const mockContext = {
30
+ projectPath: '/test/path',
31
+ conversationManager: {
32
+ createConversationContext: vi
33
+ .fn()
34
+ .mockResolvedValue({ conversationId: 'test' }),
35
+ updateConversationState: vi.fn().mockResolvedValue(undefined),
36
+ },
37
+ workflowManager: {
38
+ validateWorkflowName: vi.fn().mockReturnValue(true),
39
+ loadProjectWorkflows: vi.fn(),
40
+ loadWorkflowForProject: vi
41
+ .fn()
42
+ .mockReturnValue({ name: 'minor', phases: ['explore', 'implement'] }),
43
+ },
44
+ transitionEngine: {
45
+ handleExplicitTransition: vi
46
+ .fn()
47
+ .mockResolvedValue({ newPhase: 'explore' }),
48
+ },
49
+ planManager: {
50
+ setStateMachine: vi.fn(),
51
+ ensurePlanFile: vi.fn().mockResolvedValue('/test/plan.md'),
52
+ },
53
+ instructionGenerator: {
54
+ generateInstructions: vi.fn().mockReturnValue('Test instructions'),
55
+ },
56
+ };
57
+
58
+ const result = await handler.handle(
59
+ {
60
+ workflow: 'minor',
61
+ commit_behaviour: 'step',
62
+ },
63
+ mockContext as ServerContext
64
+ );
65
+
66
+ // Verify the handler processed the commit_behaviour parameter
67
+ expect(result).toBeTruthy();
68
+ expect(
69
+ mockContext.conversationManager.updateConversationState
70
+ ).toHaveBeenCalledWith(
71
+ 'test',
72
+ expect.objectContaining({
73
+ gitCommitConfig: expect.objectContaining({
74
+ enabled: true,
75
+ commitOnStep: true,
76
+ commitOnPhase: false,
77
+ commitOnComplete: true, // Should be true when step commits are enabled
78
+ }),
79
+ })
80
+ );
81
+ });
82
+
83
+ it('should translate commit_behaviour "phase" to correct git config', async () => {
84
+ const handler = new StartDevelopmentHandler();
85
+ const mockContext = {
86
+ projectPath: '/test/path',
87
+ conversationManager: {
88
+ createConversationContext: vi
89
+ .fn()
90
+ .mockResolvedValue({ conversationId: 'test' }),
91
+ updateConversationState: vi.fn().mockResolvedValue(undefined),
92
+ },
93
+ workflowManager: {
94
+ validateWorkflowName: vi.fn().mockReturnValue(true),
95
+ loadProjectWorkflows: vi.fn(),
96
+ loadWorkflowForProject: vi
97
+ .fn()
98
+ .mockReturnValue({ name: 'minor', phases: ['explore', 'implement'] }),
99
+ },
100
+ transitionEngine: {
101
+ handleExplicitTransition: vi
102
+ .fn()
103
+ .mockResolvedValue({ newPhase: 'explore' }),
104
+ },
105
+ planManager: {
106
+ setStateMachine: vi.fn(),
107
+ ensurePlanFile: vi.fn().mockResolvedValue('/test/plan.md'),
108
+ },
109
+ instructionGenerator: {
110
+ generateInstructions: vi.fn().mockReturnValue('Test instructions'),
111
+ },
112
+ };
113
+
114
+ await handler.handle(
115
+ {
116
+ workflow: 'minor',
117
+ commit_behaviour: 'phase',
118
+ },
119
+ mockContext as unknown as ServerContext
120
+ );
121
+
122
+ expect(
123
+ mockContext.conversationManager.updateConversationState
124
+ ).toHaveBeenCalledWith(
125
+ 'test',
126
+ expect.objectContaining({
127
+ gitCommitConfig: expect.objectContaining({
128
+ enabled: true,
129
+ commitOnStep: false,
130
+ commitOnPhase: true,
131
+ commitOnComplete: true, // Should be true when phase commits are enabled
132
+ }),
133
+ })
134
+ );
135
+ });
136
+
137
+ it('should translate commit_behaviour "end" to correct git config', async () => {
138
+ const handler = new StartDevelopmentHandler();
139
+ const mockContext = {
140
+ projectPath: '/test/path',
141
+ conversationManager: {
142
+ createConversationContext: vi
143
+ .fn()
144
+ .mockResolvedValue({ conversationId: 'test' }),
145
+ updateConversationState: vi.fn().mockResolvedValue(undefined),
146
+ },
147
+ workflowManager: {
148
+ validateWorkflowName: vi.fn().mockReturnValue(true),
149
+ loadProjectWorkflows: vi.fn(),
150
+ loadWorkflowForProject: vi
151
+ .fn()
152
+ .mockReturnValue({ name: 'minor', phases: ['explore', 'implement'] }),
153
+ },
154
+ transitionEngine: {
155
+ handleExplicitTransition: vi
156
+ .fn()
157
+ .mockResolvedValue({ newPhase: 'explore' }),
158
+ },
159
+ planManager: {
160
+ setStateMachine: vi.fn(),
161
+ ensurePlanFile: vi.fn().mockResolvedValue('/test/plan.md'),
162
+ },
163
+ instructionGenerator: {
164
+ generateInstructions: vi.fn().mockReturnValue('Test instructions'),
165
+ },
166
+ };
167
+
168
+ await handler.handle(
169
+ {
170
+ workflow: 'minor',
171
+ commit_behaviour: 'end',
172
+ },
173
+ mockContext as ServerContext
174
+ );
175
+
176
+ expect(
177
+ mockContext.conversationManager.updateConversationState
178
+ ).toHaveBeenCalledWith(
179
+ 'test',
180
+ expect.objectContaining({
181
+ gitCommitConfig: expect.objectContaining({
182
+ enabled: true,
183
+ commitOnStep: false,
184
+ commitOnPhase: false,
185
+ commitOnComplete: true,
186
+ }),
187
+ })
188
+ );
189
+ });
190
+
191
+ it('should translate commit_behaviour "none" to disabled git config', async () => {
192
+ const handler = new StartDevelopmentHandler();
193
+ const mockContext = {
194
+ projectPath: '/test/path',
195
+ conversationManager: {
196
+ createConversationContext: vi
197
+ .fn()
198
+ .mockResolvedValue({ conversationId: 'test' }),
199
+ updateConversationState: vi.fn().mockResolvedValue(undefined),
200
+ },
201
+ workflowManager: {
202
+ validateWorkflowName: vi.fn().mockReturnValue(true),
203
+ loadProjectWorkflows: vi.fn(),
204
+ loadWorkflowForProject: vi
205
+ .fn()
206
+ .mockReturnValue({ name: 'minor', phases: ['explore', 'implement'] }),
207
+ },
208
+ transitionEngine: {
209
+ handleExplicitTransition: vi
210
+ .fn()
211
+ .mockResolvedValue({ newPhase: 'explore' }),
212
+ },
213
+ planManager: {
214
+ setStateMachine: vi.fn(),
215
+ ensurePlanFile: vi.fn().mockResolvedValue('/test/plan.md'),
216
+ },
217
+ instructionGenerator: {
218
+ generateInstructions: vi.fn().mockReturnValue('Test instructions'),
219
+ },
220
+ };
221
+
222
+ await handler.handle(
223
+ {
224
+ workflow: 'minor',
225
+ commit_behaviour: 'none',
226
+ },
227
+ mockContext as ServerContext
228
+ );
229
+
230
+ expect(
231
+ mockContext.conversationManager.updateConversationState
232
+ ).toHaveBeenCalledWith(
233
+ 'test',
234
+ expect.objectContaining({
235
+ gitCommitConfig: expect.objectContaining({
236
+ enabled: false,
237
+ commitOnStep: false,
238
+ commitOnPhase: false,
239
+ commitOnComplete: false,
240
+ }),
241
+ })
242
+ );
243
+ });
244
+ });
@@ -0,0 +1,151 @@
1
+ /**
2
+ * Unit tests for ConductReviewHandler
3
+ * Tests the expected behavior of conducting reviews before phase transitions
4
+ */
5
+
6
+ import { describe, it, expect, beforeEach, vi } from 'vitest';
7
+ import { ConductReviewHandler } from '../../packages/mcp-server/src/tool-handlers/conduct-review.js';
8
+ import type { ServerContext } from '../../packages/mcp-server/src/types';
9
+ import type { ConversationContext } from '../../src/types.js';
10
+
11
+ // Mock logger
12
+ vi.mock('../../src/logger', () => ({
13
+ createLogger: () => ({
14
+ debug: vi.fn(),
15
+ info: vi.fn(),
16
+ warn: vi.fn(),
17
+ error: vi.fn(),
18
+ }),
19
+ }));
20
+
21
+ describe('ConductReviewHandler', () => {
22
+ let handler: ConductReviewHandler;
23
+ let mockContext: ServerContext;
24
+
25
+ beforeEach(() => {
26
+ handler = new ConductReviewHandler();
27
+
28
+ mockContext = {
29
+ workflowManager: {
30
+ loadWorkflowForProject: vi.fn(),
31
+ },
32
+ transitionEngine: {
33
+ getStateMachine: vi.fn(),
34
+ },
35
+ planManager: {
36
+ setStateMachine: vi.fn(),
37
+ },
38
+ instructionGenerator: {
39
+ setStateMachine: vi.fn(),
40
+ },
41
+ projectPath: '/test/project',
42
+ } as unknown as ServerContext;
43
+ });
44
+
45
+ it('should conduct review for ideation to architecture transition in greenfield workflow', async () => {
46
+ // Set up greenfield workflow
47
+ const mockWorkflow = {
48
+ states: {
49
+ ideation: {
50
+ transitions: [
51
+ {
52
+ trigger: 'ideation_complete',
53
+ to: 'architecture',
54
+ review_perspectives: [
55
+ {
56
+ perspective: 'business_analyst',
57
+ prompt:
58
+ 'Review the Product Requirements Document for completeness, clarity, and business value.',
59
+ },
60
+ {
61
+ perspective: 'ux_expert',
62
+ prompt:
63
+ 'Evaluate user experience requirements and usability considerations.',
64
+ },
65
+ ],
66
+ },
67
+ ],
68
+ },
69
+ },
70
+ };
71
+
72
+ mockContext.workflowManager.loadWorkflowForProject.mockReturnValue(
73
+ mockWorkflow
74
+ );
75
+
76
+ const conversationContext: ConversationContext = {
77
+ conversationId: 'test-conversation',
78
+ projectPath: '/test/project',
79
+ gitBranch: 'main',
80
+ currentPhase: 'ideation',
81
+ planFilePath: '/test/project/.vibe/plan.md',
82
+ workflowName: 'greenfield',
83
+ requireReviewsBeforePhaseTransition: true,
84
+ };
85
+
86
+ const result = await handler.executeWithConversation(
87
+ { target_phase: 'architecture' },
88
+ mockContext,
89
+ conversationContext
90
+ );
91
+
92
+ expect(result.instructions).toContain('ideation');
93
+ expect(result.instructions).toContain('architecture');
94
+ expect(result.perspectives).toHaveLength(2);
95
+ expect(result.perspectives[0].name).toBe('business_analyst');
96
+ expect(result.perspectives[1].name).toBe('ux_expert');
97
+ });
98
+
99
+ it('should handle the reported bug scenario: ideation_complete transition with reviews', async () => {
100
+ const mockWorkflow = {
101
+ states: {
102
+ ideation: {
103
+ transitions: [
104
+ {
105
+ trigger: 'ideation_complete',
106
+ to: 'architecture',
107
+ review_perspectives: [
108
+ {
109
+ perspective: 'business_analyst',
110
+ prompt:
111
+ 'Review the Product Requirements Document for completeness, clarity, and business value.',
112
+ },
113
+ {
114
+ perspective: 'ux_expert',
115
+ prompt:
116
+ 'Evaluate user experience requirements and usability considerations.',
117
+ },
118
+ ],
119
+ },
120
+ ],
121
+ },
122
+ },
123
+ };
124
+
125
+ mockContext.workflowManager.loadWorkflowForProject.mockReturnValue(
126
+ mockWorkflow
127
+ );
128
+
129
+ const conversationContext: ConversationContext = {
130
+ conversationId: 'test-conversation',
131
+ projectPath: '/test/project',
132
+ gitBranch: 'main',
133
+ currentPhase: 'ideation',
134
+ planFilePath: '/test/project/.vibe/plan.md',
135
+ workflowName: 'greenfield',
136
+ requireReviewsBeforePhaseTransition: true,
137
+ };
138
+
139
+ const result = await handler.executeWithConversation(
140
+ { target_phase: 'architecture' },
141
+ mockContext,
142
+ conversationContext
143
+ );
144
+
145
+ expect(result.instructions).toContain('ideation');
146
+ expect(result.instructions).toContain('architecture');
147
+ expect(result.perspectives).toHaveLength(2);
148
+ expect(result.perspectives[0].name).toBe('business_analyst');
149
+ expect(result.perspectives[1].name).toBe('ux_expert');
150
+ });
151
+ });
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Test for reset functionality
3
+ */
4
+
5
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
6
+ import { ResponsibleVibeMCPServer } from '../../src/server.js';
7
+ import { TempProject } from '../utils/temp-files.js';
8
+ import { ServerTestHelper, MockDocsHelper } from '../utils/test-helpers.js';
9
+
10
+ describe('Reset Functionality', () => {
11
+ let server: ResponsibleVibeMCPServer;
12
+ let tempProject: TempProject;
13
+
14
+ beforeEach(async () => {
15
+ tempProject = new TempProject({ projectName: 'reset-test' });
16
+ MockDocsHelper.addToTempProject(tempProject);
17
+
18
+ server = await ServerTestHelper.createServer(tempProject.projectPath);
19
+ });
20
+
21
+ afterEach(async () => {
22
+ await ServerTestHelper.cleanupServer(server);
23
+ tempProject.cleanup();
24
+ });
25
+
26
+ it('should require confirmation to reset', async () => {
27
+ // Try to reset without confirmation
28
+ try {
29
+ await server.handleResetDevelopment({ confirm: false });
30
+ expect.fail('Should have thrown an error');
31
+ } catch (error) {
32
+ expect(error.message).toContain(
33
+ 'Reset operation requires explicit confirmation'
34
+ );
35
+ }
36
+ });
37
+
38
+ it('should successfully reset with confirmation', async () => {
39
+ // First, initialize development with a workflow
40
+ await server.handleStartDevelopment({
41
+ workflow: 'waterfall',
42
+ });
43
+
44
+ // Then create some state by calling whats_next
45
+ await server.handleWhatsNext({
46
+ context: 'test context',
47
+ user_input: 'test input',
48
+ });
49
+
50
+ // Now reset with confirmation
51
+ const result = await server.handleResetDevelopment({
52
+ confirm: true,
53
+ reason: 'test reset',
54
+ });
55
+
56
+ expect(result.success).toBe(true);
57
+ expect(result.resetItems).toContain('interaction_logs');
58
+ expect(result.resetItems).toContain('conversation_state');
59
+ expect(result.resetItems).toContain('plan_file');
60
+ expect(result.message).toContain('Successfully reset conversation');
61
+ expect(result.message).toContain('test reset');
62
+ });
63
+
64
+ it('should validate confirm parameter type', async () => {
65
+ try {
66
+ await server.handleResetDevelopment({ confirm: 'true' }); // string instead of boolean
67
+ expect.fail('Should have thrown an error');
68
+ } catch (error) {
69
+ expect(error.message).toContain('confirm parameter must be a boolean');
70
+ }
71
+ });
72
+ });
@@ -0,0 +1,192 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
+ import { ResponsibleVibeMCPServer } from '../packages/mcp-server/src/server.js';
3
+ import { tmpdir } from 'node:os';
4
+ import { join } from 'node:path';
5
+ import { mkdtemp, rm, writeFile, mkdir } from 'node:fs/promises';
6
+ import { ServerTestHelper, MockDocsHelper } from '../utils/test-helpers.js';
7
+
8
+ describe('resume_workflow tool', () => {
9
+ let server: ResponsibleVibeMCPServer;
10
+ let tempDir: string;
11
+
12
+ beforeEach(async () => {
13
+ // Create temporary directory for testing
14
+ tempDir = await mkdtemp(join(tmpdir(), 'responsible-vibe-test-'));
15
+ MockDocsHelper.addToProject(tempDir);
16
+
17
+ server = await ServerTestHelper.createServer(tempDir);
18
+
19
+ // Initialize development with waterfall workflow before testing
20
+ await server.handleStartDevelopment({ workflow: 'waterfall' });
21
+ });
22
+
23
+ afterEach(async () => {
24
+ await ServerTestHelper.cleanupServer(server);
25
+ await rm(tempDir, { recursive: true, force: true });
26
+ });
27
+
28
+ it('should return comprehensive workflow resumption information', async () => {
29
+ const result = await server.handleResumeWorkflow({});
30
+
31
+ expect(result).toBeTypeOf('object');
32
+ expect(result.workflow_status).toBeDefined();
33
+ expect(result.plan_status).toBeDefined();
34
+ expect(result.system_prompt).toBeDefined(); // Default: include system prompt
35
+ expect(result.recommendations).toBeDefined();
36
+ expect(result.generated_at).toBeDefined();
37
+ expect(result.tool_version).toBeDefined();
38
+ });
39
+
40
+ it('should include workflow status information', async () => {
41
+ const result = await server.handleResumeWorkflow({});
42
+
43
+ const workflowStatus = result.workflow_status;
44
+ expect(workflowStatus.conversation_id).toBeTypeOf('string');
45
+ expect(workflowStatus.current_phase).toBeTypeOf('string');
46
+ expect(workflowStatus.project_path).toBe(tempDir);
47
+ expect(workflowStatus.git_branch).toBeTypeOf('string');
48
+ expect(workflowStatus.state_machine).toBeDefined();
49
+ });
50
+
51
+ it('should include plan status information', async () => {
52
+ const result = await server.handleResumeWorkflow({});
53
+
54
+ const planStatus = result.plan_status;
55
+ expect(planStatus.exists).toBeTypeOf('boolean');
56
+ expect(planStatus.path).toBeTypeOf('string');
57
+ expect(planStatus.path).toContain('.vibe/development-plan');
58
+ });
59
+
60
+ it('should include system prompt by default', async () => {
61
+ const result = await server.handleResumeWorkflow({});
62
+
63
+ expect(result.system_prompt).toBeTypeOf('string');
64
+ expect(result.system_prompt.length).toBeGreaterThan(1000);
65
+ expect(result.system_prompt).toContain('responsible-vibe-mcp');
66
+ });
67
+
68
+ it('should exclude system prompt when requested', async () => {
69
+ const result = await server.handleResumeWorkflow({
70
+ include_system_prompt: false,
71
+ });
72
+
73
+ expect(result.system_prompt).toBeNull();
74
+ });
75
+
76
+ it('should provide phase-specific recommendations', async () => {
77
+ const result = await server.handleResumeWorkflow({});
78
+
79
+ const recommendations = result.recommendations;
80
+ expect(recommendations.immediate_actions).toBeInstanceOf(Array);
81
+ expect(recommendations.immediate_actions.length).toBeGreaterThan(0);
82
+ expect(recommendations.phase_guidance).toBeTypeOf('string');
83
+ expect(recommendations.potential_issues).toBeInstanceOf(Array);
84
+
85
+ // Should always recommend calling whats_next
86
+ const hasWhatsNext = recommendations.immediate_actions.some(
87
+ (action: string) => action.includes('whats_next')
88
+ );
89
+ expect(hasWhatsNext).toBe(true);
90
+ });
91
+
92
+ it('should analyze plan file content when available', async () => {
93
+ // Create a mock plan file
94
+ const planContent = `# Development Plan
95
+
96
+ ## Implementation
97
+
98
+ ### Tasks
99
+ - [x] Completed task 1
100
+ - [x] Completed task 2
101
+ - [ ] Active task 1
102
+ - [ ] Active task 2
103
+
104
+ ## Decision Log
105
+ - Decision 1: Use TypeScript
106
+ - Decision 2: Use Vitest for testing
107
+ `;
108
+
109
+ // Create .vibe directory and write plan file to expected location
110
+ const planDir = join(tempDir, '.vibe');
111
+ await mkdir(planDir, { recursive: true });
112
+ await writeFile(join(planDir, 'development-plan-default.md'), planContent);
113
+
114
+ const result = await server.handleResumeWorkflow({});
115
+
116
+ const planStatus = result.plan_status;
117
+ expect(planStatus.exists).toBe(true);
118
+ expect(planStatus.analysis).toBeDefined();
119
+
120
+ const analysis = planStatus.analysis;
121
+ expect(analysis.active_tasks).toContain('Active task 1');
122
+ expect(analysis.active_tasks).toContain('Active task 2');
123
+ expect(analysis.completed_tasks).toContain('Completed task 1');
124
+ expect(analysis.completed_tasks).toContain('Completed task 2');
125
+
126
+ // Phase should come from database, not plan file
127
+ expect(result.workflow_status.current_phase).toBeDefined();
128
+ expect(result.workflow_status.current_phase).toBeTypeOf('string');
129
+ });
130
+
131
+ it('should handle missing plan file gracefully', async () => {
132
+ // Create a custom test that mocks the plan manager to report no plan file
133
+ const originalPlanManager = (server as unknown).components?.context
134
+ ?.planManager;
135
+
136
+ try {
137
+ // Replace plan manager with a mock that reports no plan file
138
+ if ((server as unknown).components?.context) {
139
+ (server as unknown).components.context.planManager = {
140
+ getPlanFileInfo: async () => ({
141
+ exists: false,
142
+ path: '/fake/path.md',
143
+ }),
144
+ ensurePlanFile: async () => {},
145
+ setStateMachine: () => {},
146
+ };
147
+ }
148
+
149
+ const result = await server.handleResumeWorkflow({});
150
+
151
+ const planStatus = result.plan_status;
152
+ expect(planStatus.exists).toBe(false);
153
+ expect(planStatus.analysis).toBeNull();
154
+ } finally {
155
+ // Restore original plan manager
156
+ if ((server as unknown).components?.context && originalPlanManager) {
157
+ (server as unknown).components.context.planManager =
158
+ originalPlanManager;
159
+ }
160
+ }
161
+ });
162
+
163
+ it('should provide different recommendations based on phase', async () => {
164
+ // This test would ideally set up different phases and verify recommendations
165
+ // For now, just verify the structure is correct
166
+ const result = await server.handleResumeWorkflow({});
167
+
168
+ const recommendations = result.recommendations;
169
+ expect(recommendations.immediate_actions).toBeInstanceOf(Array);
170
+ expect(recommendations.phase_guidance).toBeTypeOf('string');
171
+ expect(recommendations.potential_issues).toBeInstanceOf(Array);
172
+ });
173
+
174
+ it('should handle errors gracefully', async () => {
175
+ // This test ensures the method doesn't throw for any reasonable input
176
+ await expect(server.handleResumeWorkflow({})).resolves.toBeDefined();
177
+ await expect(
178
+ server.handleResumeWorkflow({ include_system_prompt: true })
179
+ ).resolves.toBeDefined();
180
+ await expect(
181
+ server.handleResumeWorkflow({ include_system_prompt: false })
182
+ ).resolves.toBeDefined();
183
+ });
184
+
185
+ it('should include metadata in response', async () => {
186
+ const result = await server.handleResumeWorkflow({});
187
+
188
+ expect(result.generated_at).toBeTypeOf('string');
189
+ expect(result.tool_version).toBeTypeOf('string');
190
+ expect(new Date(result.generated_at)).toBeInstanceOf(Date);
191
+ });
192
+ });