@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,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
|
+
});
|