@codemcp/workflows 4.7.0 → 4.8.0
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 +1 -1
- package/dist/components/beads/beads-instruction-generator.d.ts +48 -0
- package/dist/components/beads/beads-instruction-generator.d.ts.map +1 -0
- package/dist/components/beads/beads-instruction-generator.js +182 -0
- package/dist/components/beads/beads-instruction-generator.js.map +1 -0
- package/dist/components/beads/beads-plan-manager.d.ts +66 -0
- package/dist/components/beads/beads-plan-manager.d.ts.map +1 -0
- package/dist/components/beads/beads-plan-manager.js +288 -0
- package/dist/components/beads/beads-plan-manager.js.map +1 -0
- package/dist/components/beads/beads-task-backend-client.d.ts +43 -0
- package/dist/components/beads/beads-task-backend-client.d.ts.map +1 -0
- package/dist/components/beads/beads-task-backend-client.js +178 -0
- package/dist/components/beads/beads-task-backend-client.js.map +1 -0
- package/dist/components/server-components-factory.d.ts +39 -0
- package/dist/components/server-components-factory.d.ts.map +1 -0
- package/dist/components/server-components-factory.js +62 -0
- package/dist/components/server-components-factory.js.map +1 -0
- package/dist/server-config.d.ts.map +1 -1
- package/dist/server-config.js +8 -4
- package/dist/server-config.js.map +1 -1
- package/dist/server-implementation.d.ts +1 -1
- package/dist/tool-handlers/proceed-to-phase.d.ts +5 -0
- package/dist/tool-handlers/proceed-to-phase.d.ts.map +1 -1
- package/dist/tool-handlers/proceed-to-phase.js +95 -0
- package/dist/tool-handlers/proceed-to-phase.js.map +1 -1
- package/dist/tool-handlers/start-development.d.ts.map +1 -1
- package/dist/tool-handlers/start-development.js +7 -3
- package/dist/tool-handlers/start-development.js.map +1 -1
- package/dist/tool-handlers/whats-next.d.ts +0 -12
- package/dist/tool-handlers/whats-next.d.ts.map +1 -1
- package/dist/tool-handlers/whats-next.js +1 -88
- package/dist/tool-handlers/whats-next.js.map +1 -1
- package/dist/types.d.ts +7 -4
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/components/beads/beads-instruction-generator.ts +261 -0
- package/src/components/beads/beads-plan-manager.ts +358 -0
- package/src/components/beads/beads-task-backend-client.ts +232 -0
- package/src/components/server-components-factory.ts +86 -0
- package/src/server-config.ts +9 -4
- package/src/tool-handlers/proceed-to-phase.ts +140 -0
- package/src/tool-handlers/start-development.ts +14 -3
- package/src/tool-handlers/whats-next.ts +4 -117
- package/src/types.ts +7 -4
- package/test/e2e/component-substitution.test.ts +208 -0
- package/test/unit/beads-instruction-generator.test.ts +847 -0
- package/test/unit/beads-phase-task-id-integration.test.ts +557 -0
- package/test/unit/server-components-factory.test.ts +279 -0
- package/test/unit/setup-project-docs-handler.test.ts +3 -2
- package/test/utils/e2e-test-setup.ts +0 -1
- package/test/utils/temp-files.ts +12 -0
- package/tsconfig.build.tsbuildinfo +1 -1
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ServerComponentsFactory Tests
|
|
3
|
+
*
|
|
4
|
+
* Tests the factory pattern implementation for component creation
|
|
5
|
+
* based on task backend configuration.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest';
|
|
9
|
+
import { ServerComponentsFactory } from '../../src/components/server-components-factory.js';
|
|
10
|
+
import {
|
|
11
|
+
TaskBackendManager,
|
|
12
|
+
type TaskBackendConfig,
|
|
13
|
+
} from '@codemcp/workflows-core';
|
|
14
|
+
|
|
15
|
+
// Mock TaskBackendManager
|
|
16
|
+
vi.mock('@codemcp/workflows-core', async () => {
|
|
17
|
+
const actual = await vi.importActual('@codemcp/workflows-core');
|
|
18
|
+
return {
|
|
19
|
+
...actual,
|
|
20
|
+
TaskBackendManager: {
|
|
21
|
+
detectTaskBackend: vi.fn(),
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
describe('ServerComponentsFactory', () => {
|
|
27
|
+
let factory: ServerComponentsFactory;
|
|
28
|
+
let mockDetectTaskBackend: ReturnType<typeof vi.fn>;
|
|
29
|
+
|
|
30
|
+
beforeEach(() => {
|
|
31
|
+
mockDetectTaskBackend = vi.mocked(TaskBackendManager.detectTaskBackend);
|
|
32
|
+
// Default to markdown backend
|
|
33
|
+
mockDetectTaskBackend.mockReturnValue({
|
|
34
|
+
backend: 'markdown',
|
|
35
|
+
isAvailable: true,
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
afterEach(() => {
|
|
40
|
+
vi.clearAllMocks();
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
describe('Constructor', () => {
|
|
44
|
+
it('should use TaskBackendManager.detectTaskBackend() when no options provided', () => {
|
|
45
|
+
factory = new ServerComponentsFactory();
|
|
46
|
+
|
|
47
|
+
expect(mockDetectTaskBackend).toHaveBeenCalled();
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('should use provided task backend configuration', () => {
|
|
51
|
+
const customBackend: TaskBackendConfig = {
|
|
52
|
+
backend: 'beads',
|
|
53
|
+
isAvailable: true,
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
factory = new ServerComponentsFactory({ taskBackend: customBackend });
|
|
57
|
+
|
|
58
|
+
// Should not call detectTaskBackend when explicit config provided
|
|
59
|
+
expect(mockDetectTaskBackend).not.toHaveBeenCalled();
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('should handle undefined options gracefully', () => {
|
|
63
|
+
factory = new ServerComponentsFactory(undefined);
|
|
64
|
+
|
|
65
|
+
expect(mockDetectTaskBackend).toHaveBeenCalled();
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
describe('createPlanManager', () => {
|
|
70
|
+
it('should create default PlanManager for markdown backend', () => {
|
|
71
|
+
mockDetectTaskBackend.mockReturnValue({
|
|
72
|
+
backend: 'markdown',
|
|
73
|
+
isAvailable: true,
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
factory = new ServerComponentsFactory();
|
|
77
|
+
const planManager = factory.createPlanManager();
|
|
78
|
+
|
|
79
|
+
expect(planManager).toBeDefined();
|
|
80
|
+
expect(planManager.constructor.name).toBe('PlanManager');
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('should create default PlanManager when beads backend unavailable', () => {
|
|
84
|
+
mockDetectTaskBackend.mockReturnValue({
|
|
85
|
+
backend: 'beads',
|
|
86
|
+
isAvailable: false,
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
factory = new ServerComponentsFactory();
|
|
90
|
+
const planManager = factory.createPlanManager();
|
|
91
|
+
|
|
92
|
+
expect(planManager).toBeDefined();
|
|
93
|
+
expect(planManager.constructor.name).toBe('PlanManager');
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('should create BeadsPlanManager for beads backend when available', () => {
|
|
97
|
+
// Now that BeadsPlanManager is implemented, test that it's created correctly
|
|
98
|
+
const beadsBackend: TaskBackendConfig = {
|
|
99
|
+
backend: 'beads',
|
|
100
|
+
isAvailable: true,
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
factory = new ServerComponentsFactory({ taskBackend: beadsBackend });
|
|
104
|
+
const planManager = factory.createPlanManager();
|
|
105
|
+
|
|
106
|
+
expect(planManager).toBeDefined();
|
|
107
|
+
expect(planManager.constructor.name).toBe('BeadsPlanManager');
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('should create consistent instances across multiple calls', () => {
|
|
111
|
+
factory = new ServerComponentsFactory();
|
|
112
|
+
|
|
113
|
+
const planManager1 = factory.createPlanManager();
|
|
114
|
+
const planManager2 = factory.createPlanManager();
|
|
115
|
+
|
|
116
|
+
// Should create new instances (not singletons)
|
|
117
|
+
expect(planManager1).not.toBe(planManager2);
|
|
118
|
+
expect(planManager1.constructor.name).toBe(planManager2.constructor.name);
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
describe('createInstructionGenerator', () => {
|
|
123
|
+
it('should create default InstructionGenerator for markdown backend', () => {
|
|
124
|
+
mockDetectTaskBackend.mockReturnValue({
|
|
125
|
+
backend: 'markdown',
|
|
126
|
+
isAvailable: true,
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
factory = new ServerComponentsFactory();
|
|
130
|
+
const instructionGenerator = factory.createInstructionGenerator();
|
|
131
|
+
|
|
132
|
+
expect(instructionGenerator).toBeDefined();
|
|
133
|
+
expect(instructionGenerator.constructor.name).toBe(
|
|
134
|
+
'InstructionGenerator'
|
|
135
|
+
);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it('should create default InstructionGenerator when beads backend unavailable', () => {
|
|
139
|
+
mockDetectTaskBackend.mockReturnValue({
|
|
140
|
+
backend: 'beads',
|
|
141
|
+
isAvailable: false,
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
factory = new ServerComponentsFactory();
|
|
145
|
+
const instructionGenerator = factory.createInstructionGenerator();
|
|
146
|
+
|
|
147
|
+
expect(instructionGenerator).toBeDefined();
|
|
148
|
+
expect(instructionGenerator.constructor.name).toBe(
|
|
149
|
+
'InstructionGenerator'
|
|
150
|
+
);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it('should create BeadsInstructionGenerator for beads backend when available', () => {
|
|
154
|
+
// Now that BeadsInstructionGenerator is implemented, test that it's created correctly
|
|
155
|
+
const beadsBackend: TaskBackendConfig = {
|
|
156
|
+
backend: 'beads',
|
|
157
|
+
isAvailable: true,
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
factory = new ServerComponentsFactory({ taskBackend: beadsBackend });
|
|
161
|
+
const instructionGenerator = factory.createInstructionGenerator();
|
|
162
|
+
|
|
163
|
+
expect(instructionGenerator).toBeDefined();
|
|
164
|
+
expect(instructionGenerator.constructor.name).toBe(
|
|
165
|
+
'BeadsInstructionGenerator'
|
|
166
|
+
);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it('should create InstructionGenerator with proper dependency injection', () => {
|
|
170
|
+
factory = new ServerComponentsFactory();
|
|
171
|
+
const instructionGenerator = factory.createInstructionGenerator();
|
|
172
|
+
|
|
173
|
+
expect(instructionGenerator).toBeDefined();
|
|
174
|
+
expect(instructionGenerator.constructor.name).toBe(
|
|
175
|
+
'InstructionGenerator'
|
|
176
|
+
);
|
|
177
|
+
|
|
178
|
+
// Verify the instance is functional (dependency was injected correctly)
|
|
179
|
+
// without checking internal implementation details
|
|
180
|
+
expect(typeof instructionGenerator.generateInstructions).toBe('function');
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it('should create consistent instances across multiple calls', () => {
|
|
184
|
+
factory = new ServerComponentsFactory();
|
|
185
|
+
|
|
186
|
+
const instructionGenerator1 = factory.createInstructionGenerator();
|
|
187
|
+
const instructionGenerator2 = factory.createInstructionGenerator();
|
|
188
|
+
|
|
189
|
+
// Should create new instances (not singletons)
|
|
190
|
+
expect(instructionGenerator1).not.toBe(instructionGenerator2);
|
|
191
|
+
expect(instructionGenerator1.constructor.name).toBe(
|
|
192
|
+
instructionGenerator2.constructor.name
|
|
193
|
+
);
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
describe('getTaskBackend', () => {
|
|
198
|
+
it('should return the current task backend configuration', () => {
|
|
199
|
+
const customBackend: TaskBackendConfig = {
|
|
200
|
+
backend: 'beads',
|
|
201
|
+
isAvailable: true,
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
factory = new ServerComponentsFactory({ taskBackend: customBackend });
|
|
205
|
+
const taskBackend = factory.getTaskBackend();
|
|
206
|
+
|
|
207
|
+
expect(taskBackend).toEqual(customBackend);
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
it('should return detected task backend when no explicit config provided', () => {
|
|
211
|
+
const detectedBackend: TaskBackendConfig = {
|
|
212
|
+
backend: 'markdown',
|
|
213
|
+
isAvailable: true,
|
|
214
|
+
};
|
|
215
|
+
mockDetectTaskBackend.mockReturnValue(detectedBackend);
|
|
216
|
+
|
|
217
|
+
factory = new ServerComponentsFactory();
|
|
218
|
+
const taskBackend = factory.getTaskBackend();
|
|
219
|
+
|
|
220
|
+
expect(taskBackend).toEqual(detectedBackend);
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
describe('Backend Integration', () => {
|
|
225
|
+
it('should handle unknown backend types gracefully', () => {
|
|
226
|
+
const unknownBackend: TaskBackendConfig = {
|
|
227
|
+
backend: 'beads' as TaskBackendConfig['backend'],
|
|
228
|
+
isAvailable: true,
|
|
229
|
+
};
|
|
230
|
+
// Simulate an unknown backend type for testing
|
|
231
|
+
(unknownBackend as unknown as { backend: string }).backend =
|
|
232
|
+
'unknown-backend';
|
|
233
|
+
|
|
234
|
+
factory = new ServerComponentsFactory({ taskBackend: unknownBackend });
|
|
235
|
+
|
|
236
|
+
// Should fall back to default implementations
|
|
237
|
+
const planManager = factory.createPlanManager();
|
|
238
|
+
const instructionGenerator = factory.createInstructionGenerator();
|
|
239
|
+
|
|
240
|
+
expect(planManager.constructor.name).toBe('PlanManager');
|
|
241
|
+
expect(instructionGenerator.constructor.name).toBe(
|
|
242
|
+
'InstructionGenerator'
|
|
243
|
+
);
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
it('should handle backend availability flag correctly', () => {
|
|
247
|
+
const unavailableBackend: TaskBackendConfig = {
|
|
248
|
+
backend: 'beads',
|
|
249
|
+
isAvailable: false,
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
factory = new ServerComponentsFactory({
|
|
253
|
+
taskBackend: unavailableBackend,
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
// Should fall back to default implementations when backend unavailable
|
|
257
|
+
const planManager = factory.createPlanManager();
|
|
258
|
+
const instructionGenerator = factory.createInstructionGenerator();
|
|
259
|
+
|
|
260
|
+
expect(planManager.constructor.name).toBe('PlanManager');
|
|
261
|
+
expect(instructionGenerator.constructor.name).toBe(
|
|
262
|
+
'InstructionGenerator'
|
|
263
|
+
);
|
|
264
|
+
});
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
describe('Future Extensibility', () => {
|
|
268
|
+
// These tests document the expected behavior once beads implementations are created
|
|
269
|
+
it.todo(
|
|
270
|
+
'should create BeadsPlanManager when beads backend is available and implemented'
|
|
271
|
+
);
|
|
272
|
+
it.todo(
|
|
273
|
+
'should create BeadsInstructionGenerator when beads backend is available and implemented'
|
|
274
|
+
);
|
|
275
|
+
it.todo(
|
|
276
|
+
'should support additional backends (github, linear) through same factory pattern'
|
|
277
|
+
);
|
|
278
|
+
});
|
|
279
|
+
});
|
|
@@ -20,7 +20,8 @@ import { TemplateManager } from '@codemcp/workflows-core';
|
|
|
20
20
|
import { ServerContext } from '../../src/types';
|
|
21
21
|
import { join } from 'node:path';
|
|
22
22
|
import { tmpdir } from 'node:os';
|
|
23
|
-
import { mkdir
|
|
23
|
+
import { mkdir } from 'node:fs/promises';
|
|
24
|
+
import { cleanupDirectory } from '../utils/temp-files.js';
|
|
24
25
|
|
|
25
26
|
// Mock ProjectDocsManager and TemplateManager
|
|
26
27
|
vi.mock('../../src/project-docs-manager.js');
|
|
@@ -72,7 +73,7 @@ describe('SetupProjectDocsHandler', () => {
|
|
|
72
73
|
afterEach(async () => {
|
|
73
74
|
// Clean up test directory
|
|
74
75
|
try {
|
|
75
|
-
await
|
|
76
|
+
await cleanupDirectory(testProjectPath);
|
|
76
77
|
} catch {
|
|
77
78
|
// Ignore cleanup errors
|
|
78
79
|
}
|
|
@@ -17,7 +17,6 @@ import { join } from 'node:path';
|
|
|
17
17
|
import { tmpdir } from 'node:os';
|
|
18
18
|
import { mkdirSync, rmSync, existsSync } from 'node:fs';
|
|
19
19
|
import type { ServerContext } from '../../src/types';
|
|
20
|
-
import { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
|
|
21
20
|
|
|
22
21
|
// Disable fs mocking for E2E tests
|
|
23
22
|
vi.unmock('fs');
|
package/test/utils/temp-files.ts
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { mkdirSync, writeFileSync, rmSync, existsSync } from 'node:fs';
|
|
9
|
+
import { rm } from 'node:fs/promises';
|
|
9
10
|
import { join } from 'node:path';
|
|
10
11
|
import { tmpdir } from 'node:os';
|
|
11
12
|
|
|
@@ -306,3 +307,14 @@ export function safeParseServerResponse(
|
|
|
306
307
|
);
|
|
307
308
|
}
|
|
308
309
|
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Async helper to clean up a directory
|
|
313
|
+
* Uses fs.rm which is the Node.js 14.14+ recommended approach
|
|
314
|
+
* @param dirPath - Path to directory to remove
|
|
315
|
+
*/
|
|
316
|
+
export async function cleanupDirectory(dirPath: string): Promise<void> {
|
|
317
|
+
if (existsSync(dirPath)) {
|
|
318
|
+
await rm(dirPath, { recursive: true, force: true });
|
|
319
|
+
}
|
|
320
|
+
}
|