@codemcp/workflows 5.0.1 → 5.1.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.
Files changed (77) hide show
  1. package/SKILL.md +23 -0
  2. package/package.json +6 -2
  3. package/.prettierignore +0 -2
  4. package/.turbo/turbo-build.log +0 -4
  5. package/.vibe/conversation-state.sqlite +0 -0
  6. package/src/components/beads/beads-instruction-generator.ts +0 -230
  7. package/src/components/beads/beads-plan-manager.ts +0 -333
  8. package/src/components/beads/beads-task-backend-client.ts +0 -229
  9. package/src/index.ts +0 -93
  10. package/src/notification-service.ts +0 -23
  11. package/src/plugin-system/beads-plugin.ts +0 -649
  12. package/src/plugin-system/commit-plugin.ts +0 -252
  13. package/src/plugin-system/index.ts +0 -20
  14. package/src/plugin-system/plugin-interfaces.ts +0 -153
  15. package/src/plugin-system/plugin-registry.ts +0 -190
  16. package/src/resource-handlers/conversation-state.ts +0 -55
  17. package/src/resource-handlers/development-plan.ts +0 -48
  18. package/src/resource-handlers/index.ts +0 -73
  19. package/src/resource-handlers/system-prompt.ts +0 -55
  20. package/src/resource-handlers/workflow-resource.ts +0 -132
  21. package/src/response-renderer.ts +0 -116
  22. package/src/server-config.ts +0 -760
  23. package/src/server-helpers.ts +0 -245
  24. package/src/server-implementation.ts +0 -277
  25. package/src/server.ts +0 -9
  26. package/src/tool-handlers/base-tool-handler.ts +0 -151
  27. package/src/tool-handlers/conduct-review.ts +0 -190
  28. package/src/tool-handlers/get-tool-info.ts +0 -273
  29. package/src/tool-handlers/index.ts +0 -115
  30. package/src/tool-handlers/list-workflows.ts +0 -78
  31. package/src/tool-handlers/no-idea.ts +0 -47
  32. package/src/tool-handlers/proceed-to-phase.ts +0 -296
  33. package/src/tool-handlers/reset-development.ts +0 -90
  34. package/src/tool-handlers/resume-workflow.ts +0 -378
  35. package/src/tool-handlers/setup-project-docs.ts +0 -232
  36. package/src/tool-handlers/start-development.ts +0 -746
  37. package/src/tool-handlers/whats-next.ts +0 -246
  38. package/src/types.ts +0 -135
  39. package/src/version-info.ts +0 -213
  40. package/test/e2e/beads-plugin-integration.test.ts +0 -1623
  41. package/test/e2e/commit-plugin-integration.test.ts +0 -222
  42. package/test/e2e/core-functionality.test.ts +0 -167
  43. package/test/e2e/git-branch-detection.test.ts +0 -351
  44. package/test/e2e/mcp-contract.test.ts +0 -509
  45. package/test/e2e/plan-management.test.ts +0 -334
  46. package/test/e2e/plugin-system-integration.test.ts +0 -1410
  47. package/test/e2e/state-management.test.ts +0 -387
  48. package/test/e2e/workflow-integration.test.ts +0 -498
  49. package/test/unit/beads-instruction-generator.test.ts +0 -979
  50. package/test/unit/beads-phase-task-id-integration.test.ts +0 -535
  51. package/test/unit/beads-plugin-behavioral.test.ts +0 -545
  52. package/test/unit/beads-plugin.test.ts +0 -117
  53. package/test/unit/commit-plugin.test.ts +0 -196
  54. package/test/unit/conduct-review.test.ts +0 -151
  55. package/test/unit/conversation-not-found-error.test.ts +0 -120
  56. package/test/unit/plugin-error-handling.test.ts +0 -240
  57. package/test/unit/proceed-to-phase-plugin-integration.test.ts +0 -150
  58. package/test/unit/reset-functionality.test.ts +0 -72
  59. package/test/unit/resume-workflow.test.ts +0 -193
  60. package/test/unit/server-config-plugin-registry.test.ts +0 -99
  61. package/test/unit/server-tools.test.ts +0 -310
  62. package/test/unit/setup-project-docs-handler.test.ts +0 -268
  63. package/test/unit/start-development-artifact-detection.test.ts +0 -387
  64. package/test/unit/start-development-gitignore.test.ts +0 -178
  65. package/test/unit/start-development-goal-extraction.test.ts +0 -226
  66. package/test/unit/system-prompt-resource.test.ts +0 -102
  67. package/test/unit/tool-handlers/no-idea.test.ts +0 -40
  68. package/test/utils/e2e-test-setup.ts +0 -451
  69. package/test/utils/run-server-in-dir.sh +0 -27
  70. package/test/utils/temp-files.ts +0 -320
  71. package/test/utils/test-access.ts +0 -79
  72. package/test/utils/test-helpers.ts +0 -288
  73. package/test/utils/test-setup.ts +0 -77
  74. package/tsconfig.build.json +0 -10
  75. package/tsconfig.build.tsbuildinfo +0 -1
  76. package/tsconfig.json +0 -12
  77. package/vitest.config.ts +0 -19
@@ -1,509 +0,0 @@
1
- import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
2
- import { Client } from '@modelcontextprotocol/sdk/client/index.js';
3
- import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
4
- import path from 'node:path';
5
- import { promises as fs } from 'node:fs';
6
- import {
7
- TempProject,
8
- createTempProjectWithDefaultStateMachine,
9
- } from '../utils/temp-files';
10
-
11
- vi.unmock('fs');
12
- vi.unmock('fs/promises');
13
-
14
- /**
15
- * MCP Contract Validation Tests
16
- *
17
- * Tests the Model Context Protocol contract by using a real MCP TypeScript client
18
- * from the official SDK to connect to our responsible-vibe-mcp-server and validate:
19
- * - Server initialization and capability negotiation
20
- * - All exposed resources (plan://current, state://current)
21
- * - All exposed tools (whats_next, proceed_to_phase)
22
- * - Protocol compliance and message formats
23
- * - Error handling and edge cases
24
- */
25
- describe('MCP Contract Validation', () => {
26
- let client: Client;
27
- let transport: StdioClientTransport;
28
- let tempProject: TempProject;
29
- let cleanup: () => Promise<void>;
30
-
31
- beforeEach(async () => {
32
- // Create temporary project for testing
33
- const scenario = await createTempProjectWithDefaultStateMachine();
34
- tempProject = scenario;
35
- cleanup = async () => {
36
- await scenario.cleanup();
37
- };
38
-
39
- // Build the server if needed
40
- const serverPath = path.resolve(__dirname, '../../../cli/dist/index.js');
41
- const serverExists = await fs
42
- .access(serverPath)
43
- .then(() => true)
44
- .catch(() => false);
45
-
46
- if (!serverExists) {
47
- throw new Error(
48
- `Server not built. Please run 'npm run build' first. Looking for: ${serverPath}`
49
- );
50
- }
51
-
52
- // Create MCP client and transport (this will spawn the server process)
53
- // Note: Due to limitations in StdioClientTransport, environment variables
54
- // are not properly passed to the spawned server process, so this test
55
- // may show INFO level logs despite our attempts to suppress them.
56
-
57
- // Use a shell script wrapper that explicitly changes the working directory
58
- // This ensures the server operates in the temporary directory and not the current project directory
59
- // which is essential for clean test isolation
60
- const wrapperScriptPath = path.resolve(
61
- __dirname,
62
- '../utils/run-server-in-dir.sh'
63
- );
64
-
65
- transport = new StdioClientTransport({
66
- command: wrapperScriptPath,
67
- args: [tempProject.projectPath, serverPath],
68
- env: {
69
- ...process.env,
70
- LOG_LEVEL: 'ERROR',
71
- NODE_ENV: 'test',
72
- VITEST: 'true',
73
- },
74
- });
75
-
76
- client = new Client(
77
- {
78
- name: 'mcp-contract-test-client',
79
- version: '1.0.0',
80
- },
81
- {
82
- capabilities: {
83
- roots: {},
84
- sampling: {},
85
- },
86
- }
87
- );
88
-
89
- // Connect to the server
90
- await client.connect(transport);
91
-
92
- // Start development for all MCP contract tests
93
- await client.callTool({
94
- name: 'start_development',
95
- arguments: {
96
- workflow: 'waterfall',
97
- commit_behaviour: 'none', // Use 'none' for test isolation
98
- },
99
- });
100
- });
101
-
102
- afterEach(async () => {
103
- // Clean up client and transport
104
- if (client) {
105
- await client.close();
106
- }
107
- if (transport) {
108
- await transport.close();
109
- }
110
- if (cleanup) {
111
- await cleanup();
112
- }
113
- });
114
-
115
- describe('Server Initialization and Capabilities', () => {
116
- it('should successfully initialize with proper server info', async () => {
117
- // The connection in beforeEach validates basic initialization
118
- // Here we verify the server info was properly exchanged
119
- expect(client).toBeDefined();
120
-
121
- // Verify we can make basic requests (this confirms initialization succeeded)
122
- const tools = await client.listTools();
123
- expect(tools).toBeDefined();
124
- });
125
-
126
- it('should expose correct capabilities', async () => {
127
- // Test that server exposes the expected capabilities
128
- // by verifying we can call the expected methods without errors
129
-
130
- // Should support tools
131
- const tools = await client.listTools();
132
- expect(tools.tools).toBeDefined();
133
- expect(Array.isArray(tools.tools)).toBe(true);
134
-
135
- // Should support resources
136
- const resources = await client.listResources();
137
- expect(resources.resources).toBeDefined();
138
- expect(Array.isArray(resources.resources)).toBe(true);
139
- });
140
- });
141
-
142
- describe('Tools Contract Validation', () => {
143
- it('should expose whats_next tool with correct schema', async () => {
144
- const tools = await client.listTools();
145
-
146
- const whatsNextTool = tools.tools.find(
147
- tool => tool.name === 'whats_next'
148
- );
149
- expect(whatsNextTool).toBeDefined();
150
- expect(whatsNextTool!.description).toBeTruthy();
151
- expect(whatsNextTool!.inputSchema).toBeDefined();
152
-
153
- // Verify the input schema allows the expected parameters
154
- const schema = whatsNextTool!.inputSchema;
155
- expect(schema.type).toBe('object');
156
- expect(schema.properties).toBeDefined();
157
-
158
- // Should accept optional parameters like user_input, context, etc.
159
- const properties = schema.properties as Record<string, unknown>;
160
- expect(properties.user_input).toBeDefined();
161
- expect(properties.context).toBeDefined();
162
- expect(properties.conversation_summary).toBeDefined();
163
- expect(properties.recent_messages).toBeDefined();
164
- });
165
-
166
- it('should expose proceed_to_phase tool with correct schema', async () => {
167
- const tools = await client.listTools();
168
-
169
- const proceedTool = tools.tools.find(
170
- tool => tool.name === 'proceed_to_phase'
171
- );
172
- expect(proceedTool).toBeDefined();
173
- expect(proceedTool!.description).toBeTruthy();
174
- expect(proceedTool!.inputSchema).toBeDefined();
175
-
176
- const schema = proceedTool!.inputSchema;
177
- expect(schema.type).toBe('object');
178
- expect(schema.properties).toBeDefined();
179
-
180
- const properties = schema.properties as Record<string, unknown>;
181
- expect(properties.target_phase).toBeDefined();
182
- expect(properties.reason).toBeDefined();
183
-
184
- // target_phase should be required
185
- expect(schema.required).toContain('target_phase');
186
- });
187
-
188
- it('should execute whats_next tool successfully', async () => {
189
- const result = await client.callTool({
190
- name: 'whats_next',
191
- arguments: {
192
- user_input: 'implement authentication system',
193
- },
194
- });
195
-
196
- expect(result.content).toBeDefined();
197
- expect(Array.isArray(result.content)).toBe(true);
198
- expect(result.content.length).toBeGreaterThan(0);
199
-
200
- // Should return text content with phase information
201
- const textContent = result.content.find(c => c.type === 'text');
202
- expect(textContent).toBeDefined();
203
- expect(textContent!.text).toBeTruthy();
204
-
205
- // Response should contain structured data about the phase
206
- const responseText = textContent!.text;
207
- expect(responseText).toContain('phase');
208
- expect(responseText).toContain('instructions');
209
- });
210
-
211
- it('should execute proceed_to_phase tool successfully', async () => {
212
- // First call whats_next to establish a conversation
213
- await client.callTool({
214
- name: 'whats_next',
215
- arguments: {
216
- user_input: 'start project',
217
- },
218
- });
219
-
220
- // Then proceed to a different phase
221
- const result = await client.callTool({
222
- name: 'proceed_to_phase',
223
- arguments: {
224
- target_phase: 'design',
225
- reason: 'requirements complete',
226
- review_state: 'not-required',
227
- },
228
- });
229
-
230
- expect(result.content).toBeDefined();
231
- expect(Array.isArray(result.content)).toBe(true);
232
- expect(result.content.length).toBeGreaterThan(0);
233
-
234
- const textContent = result.content.find(c => c.type === 'text');
235
- expect(textContent).toBeDefined();
236
- expect(textContent!.text).toBeTruthy();
237
-
238
- const responseText = textContent!.text;
239
- expect(responseText).toContain('phase');
240
- expect(responseText).toContain('design');
241
- });
242
-
243
- it('should handle tool errors gracefully', async () => {
244
- // Test invalid phase transition
245
- const result = await client.callTool({
246
- name: 'proceed_to_phase',
247
- arguments: {
248
- target_phase: 'invalid_phase',
249
- reason: 'test error handling',
250
- review_state: 'not-required',
251
- },
252
- });
253
-
254
- // Should return error information rather than throwing
255
- expect(result.content).toBeDefined();
256
- expect(result.isError).toBe(true);
257
- });
258
- });
259
-
260
- describe('Resources Contract Validation', () => {
261
- it('should expose plan://current resource', async () => {
262
- const resources = await client.listResources();
263
-
264
- const planResource = resources.resources.find(
265
- r => r.uri === 'plan://current'
266
- );
267
- expect(planResource).toBeDefined();
268
- expect(planResource!.name).toBeTruthy();
269
- expect(planResource!.description).toBeTruthy();
270
- expect(planResource!.mimeType).toBe('text/markdown');
271
- });
272
-
273
- it('should expose state://current resource', async () => {
274
- const resources = await client.listResources();
275
-
276
- const stateResource = resources.resources.find(
277
- r => r.uri === 'state://current'
278
- );
279
- expect(stateResource).toBeDefined();
280
- expect(stateResource!.name).toBeTruthy();
281
- expect(stateResource!.description).toBeTruthy();
282
- expect(stateResource!.mimeType).toBe('application/json');
283
- });
284
-
285
- it('should read plan resource successfully', async () => {
286
- // First establish a conversation to ensure plan file exists
287
- await client.callTool({
288
- name: 'whats_next',
289
- arguments: {
290
- user_input: 'test plan resource',
291
- },
292
- });
293
-
294
- const result = await client.readResource({
295
- uri: 'plan://current',
296
- });
297
-
298
- expect(result.contents).toBeDefined();
299
- expect(Array.isArray(result.contents)).toBe(true);
300
- expect(result.contents.length).toBeGreaterThan(0);
301
-
302
- const content = result.contents[0];
303
- expect(content.uri).toBe('plan://current');
304
- expect(content.mimeType).toBe('text/markdown');
305
- expect(content.text).toBeTruthy();
306
-
307
- // Should contain markdown plan structure
308
- expect(content.text).toContain('# Development Plan');
309
- expect(content.text).toContain('## Goal');
310
- });
311
-
312
- it('should read state resource successfully', async () => {
313
- // First establish a conversation to ensure state exists
314
- await client.callTool({
315
- name: 'whats_next',
316
- arguments: {
317
- user_input: 'test state resource',
318
- },
319
- });
320
-
321
- const result = await client.readResource({
322
- uri: 'state://current',
323
- });
324
-
325
- expect(result.contents).toBeDefined();
326
- expect(Array.isArray(result.contents)).toBe(true);
327
- expect(result.contents.length).toBeGreaterThan(0);
328
-
329
- const content = result.contents[0];
330
- expect(content.uri).toBe('state://current');
331
- expect(content.mimeType).toBe('application/json');
332
- expect(content.text).toBeTruthy();
333
-
334
- // Should contain valid JSON with state information
335
- const stateData = JSON.parse(content.text);
336
- expect(stateData.currentPhase).toBeTruthy();
337
- expect(stateData.projectPath).toBeTruthy();
338
- });
339
-
340
- it('should handle resource read errors gracefully', async () => {
341
- // Test reading non-existent resource
342
- try {
343
- await client.readResource({
344
- uri: 'nonexistent://resource',
345
- });
346
- // Should not reach here
347
- expect(true).toBe(false);
348
- } catch (error) {
349
- // Should throw an appropriate error
350
- expect(error).toBeDefined();
351
- }
352
- });
353
- });
354
-
355
- describe('Protocol Compliance', () => {
356
- it('should handle concurrent requests properly', async () => {
357
- // Make multiple concurrent requests to test protocol handling
358
- const promises = [
359
- client.callTool({
360
- name: 'whats_next',
361
- arguments: { user_input: 'concurrent test 1' },
362
- }),
363
- client.callTool({
364
- name: 'whats_next',
365
- arguments: { user_input: 'concurrent test 2' },
366
- }),
367
- client.listTools(),
368
- client.listResources(),
369
- ];
370
-
371
- const results = await Promise.all(promises);
372
-
373
- // All requests should succeed
374
- for (const result of results) {
375
- expect(result).toBeDefined();
376
- }
377
-
378
- // Tool results should have content
379
- expect(results[0].content).toBeDefined();
380
- expect(results[1].content).toBeDefined();
381
-
382
- // List results should have arrays
383
- expect(Array.isArray(results[2].tools)).toBe(true);
384
- expect(Array.isArray(results[3].resources)).toBe(true);
385
- });
386
-
387
- it('should handle malformed requests appropriately', async () => {
388
- // Test with missing required parameters
389
- try {
390
- await client.callTool({
391
- name: 'proceed_to_phase',
392
- arguments: {
393
- // Missing required target_phase
394
- reason: 'test malformed request',
395
- review_state: 'not-required',
396
- },
397
- });
398
- // Should not reach here if validation works
399
- expect(true).toBe(false);
400
- } catch (error) {
401
- // Should handle validation error appropriately
402
- expect(error).toBeDefined();
403
- }
404
- });
405
- });
406
-
407
- describe('Integration Scenarios', () => {
408
- it('should support complete development workflow', async () => {
409
- // Start with requirements
410
- const start = await client.callTool({
411
- name: 'whats_next',
412
- arguments: {
413
- user_input: 'implement user authentication system',
414
- context: 'new feature development',
415
- },
416
- });
417
-
418
- const startResponse = JSON.parse(start.content[0].text);
419
-
420
- // Check if we're using a custom state machine by looking at the phase
421
- // Default state machine uses: idle, requirements, design, implementation, qa, testing, complete
422
- // If we get other phases, skip the test as it's using a custom state machine
423
- const defaultPhases = [
424
- 'idle',
425
- 'requirements',
426
- 'design',
427
- 'implementation',
428
- 'qa',
429
- 'testing',
430
- 'complete',
431
- ];
432
-
433
- if (!defaultPhases.includes(startResponse.phase)) {
434
- console.log(
435
- `Skipping test: Custom state machine detected (phase: ${startResponse.phase})`
436
- );
437
- return; // Skip test
438
- }
439
-
440
- // The server intelligently determines the appropriate starting phase
441
- expect(['requirements', 'design']).toContain(startResponse.phase);
442
-
443
- // Transition to design (if not already there)
444
- let currentPhase = startResponse.phase;
445
- if (currentPhase !== 'design') {
446
- const design = await client.callTool({
447
- name: 'proceed_to_phase',
448
- arguments: {
449
- target_phase: 'design',
450
- reason: 'requirements analysis complete',
451
- review_state: 'not-required',
452
- },
453
- });
454
-
455
- const designResponse = JSON.parse(design.content[0].text);
456
- expect(designResponse.phase).toBe('design');
457
- currentPhase = 'design';
458
- }
459
-
460
- // Verify state resource reflects the current phase
461
- const stateResult = await client.readResource({
462
- uri: 'state://current',
463
- });
464
-
465
- const stateData = JSON.parse(stateResult.contents[0].text);
466
- expect(stateData.currentPhase).toBe(currentPhase);
467
-
468
- // Verify plan resource contains relevant phase information
469
- const planResult = await client.readResource({
470
- uri: 'plan://current',
471
- });
472
-
473
- const planContent = planResult.contents[0].text;
474
- expect(planContent).toContain('# Development Plan');
475
- expect(planContent).toContain('## Goal');
476
- });
477
-
478
- it('should handle complex conversation context', async () => {
479
- const result = await client.callTool({
480
- name: 'whats_next',
481
- arguments: {
482
- user_input: 'implement OAuth integration',
483
- context: 'user wants third-party authentication',
484
- conversation_summary:
485
- 'Discussed authentication options, user prefers OAuth with Google and GitHub',
486
- recent_messages: [
487
- {
488
- role: 'user',
489
- content: 'What authentication options do we have?',
490
- },
491
- {
492
- role: 'assistant',
493
- content: 'We can use OAuth, JWT, or traditional sessions',
494
- },
495
- {
496
- role: 'user',
497
- content: 'OAuth sounds good, especially Google and GitHub',
498
- },
499
- ],
500
- },
501
- });
502
-
503
- expect(result.content).toBeDefined();
504
- const response = JSON.parse(result.content[0].text);
505
- expect(response.phase).toBeTruthy();
506
- expect(response.instructions).toBeTruthy();
507
- });
508
- });
509
- });