@codemcp/workflows 5.0.0 → 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,150 +0,0 @@
1
- /**
2
- * Test plugin hook integration in proceed-to-phase
3
- * Focus on testing that plugin hooks are called correctly
4
- */
5
-
6
- import { describe, it, expect, beforeEach, vi } from 'vitest';
7
- import { ProceedToPhaseHandler } from '../../src/tool-handlers/proceed-to-phase.js';
8
- import { PluginRegistry } from '../../src/plugin-system/plugin-registry.js';
9
- import type { ServerContext } from '../../src/types.js';
10
- import type { PluginHookContext } from '../../src/plugin-system/plugin-interfaces.js';
11
-
12
- // Mock dependencies
13
- vi.mock('@codemcp/workflows-core', () => ({
14
- createLogger: vi.fn(() => ({
15
- debug: vi.fn(),
16
- info: vi.fn(),
17
- warn: vi.fn(),
18
- error: vi.fn(),
19
- })),
20
- }));
21
-
22
- describe('ProceedToPhase Plugin Integration', () => {
23
- let handler: ProceedToPhaseHandler;
24
- let mockPluginRegistry: PluginRegistry;
25
- let mockContext: ServerContext;
26
-
27
- beforeEach(() => {
28
- mockPluginRegistry = new PluginRegistry();
29
-
30
- mockContext = {
31
- conversationManager: {
32
- getConversationContext: vi.fn().mockResolvedValue({
33
- conversationId: 'test-conversation',
34
- planFilePath: '/test/plan.md',
35
- currentPhase: 'plan',
36
- workflowName: 'epcc',
37
- projectPath: '/test/project',
38
- gitBranch: 'main',
39
- }),
40
- updateConversationState: vi.fn().mockResolvedValue(undefined),
41
- },
42
- transitionEngine: {
43
- handleExplicitTransition: vi.fn().mockReturnValue({
44
- newPhase: 'code',
45
- transitionReason: 'Test transition',
46
- isModeled: true,
47
- instructions: 'Test transition instructions',
48
- }),
49
- },
50
- planManager: {
51
- getPlanFileInfo: vi.fn().mockResolvedValue({ exists: true }),
52
- },
53
- instructionGenerator: {
54
- generateInstructions: vi.fn().mockResolvedValue({
55
- instructions: 'Test instructions',
56
- }),
57
- },
58
- workflowManager: {
59
- loadWorkflowForProject: vi.fn().mockReturnValue({
60
- name: 'epcc',
61
- states: { plan: {}, code: {} },
62
- }),
63
- },
64
- interactionLogger: {
65
- logInteraction: vi.fn().mockResolvedValue(undefined),
66
- },
67
- projectPath: '/test/project',
68
- pluginRegistry: mockPluginRegistry,
69
- } as unknown as ServerContext;
70
-
71
- handler = new ProceedToPhaseHandler();
72
- });
73
-
74
- it('should call beforePhaseTransition plugin hook during phase transition', async () => {
75
- const hookSpy = vi.fn().mockResolvedValue(undefined);
76
-
77
- // Register a mock plugin with beforePhaseTransition hook
78
- const mockPlugin = {
79
- getName: () => 'TestPlugin',
80
- getSequence: () => 100,
81
- isEnabled: () => true,
82
- getHooks: () => ({
83
- beforePhaseTransition: hookSpy,
84
- }),
85
- };
86
-
87
- mockPluginRegistry.registerPlugin(mockPlugin);
88
-
89
- // Execute the proceed_to_phase handler
90
- await handler.handle(
91
- {
92
- target_phase: 'code',
93
- reason: 'Testing plugin integration',
94
- review_state: 'not-required',
95
- },
96
- mockContext
97
- );
98
-
99
- // Verify the hook was called with correct parameters
100
- expect(hookSpy).toHaveBeenCalledOnce();
101
-
102
- const [pluginContext, currentPhase, targetPhase] = hookSpy.mock.calls[0];
103
-
104
- // Verify plugin context structure
105
- expect(pluginContext).toMatchObject<Partial<PluginHookContext>>({
106
- conversationId: 'test-conversation',
107
- planFilePath: '/test/plan.md',
108
- currentPhase: 'plan',
109
- workflow: 'epcc',
110
- projectPath: '/test/project',
111
- gitBranch: 'main',
112
- targetPhase: 'code',
113
- });
114
-
115
- // Verify phase parameters
116
- expect(currentPhase).toBe('plan');
117
- expect(targetPhase).toBe('code');
118
- });
119
-
120
- it('should handle plugin hook errors by returning error result', async () => {
121
- const hookError = new Error('Plugin validation failed');
122
- const hookSpy = vi.fn().mockRejectedValue(hookError);
123
-
124
- // Register a mock plugin that throws an error
125
- const mockPlugin = {
126
- getName: () => 'TestPlugin',
127
- getSequence: () => 100,
128
- isEnabled: () => true,
129
- getHooks: () => ({
130
- beforePhaseTransition: hookSpy,
131
- }),
132
- };
133
-
134
- mockPluginRegistry.registerPlugin(mockPlugin);
135
-
136
- // Execute the handler and expect it to return error result
137
- const result = await handler.handle(
138
- {
139
- target_phase: 'code',
140
- reason: 'Testing plugin error handling',
141
- review_state: 'not-required',
142
- },
143
- mockContext
144
- );
145
-
146
- expect(result.success).toBe(false);
147
- expect(result.error).toContain('Plugin validation failed');
148
- expect(hookSpy).toHaveBeenCalledOnce();
149
- });
150
- });
@@ -1,72 +0,0 @@
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
- });
@@ -1,193 +0,0 @@
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.current_phase).toBeTypeOf('string');
45
- expect(workflowStatus.project_path).toBe(tempDir);
46
- expect(workflowStatus.git_branch).toBeTypeOf('string');
47
- expect(workflowStatus.state_machine).toBeDefined();
48
- });
49
-
50
- it('should include plan status information', async () => {
51
- const result = await server.handleResumeWorkflow({});
52
-
53
- const planStatus = result.plan_status;
54
- expect(planStatus.exists).toBeTypeOf('boolean');
55
- expect(planStatus.path).toBeTypeOf('string');
56
- expect(planStatus.path).toContain('.vibe/development-plan');
57
- });
58
-
59
- it('should include system prompt by default', async () => {
60
- const result = await server.handleResumeWorkflow({});
61
-
62
- expect(result.system_prompt).toBeTypeOf('string');
63
- // Streamlined prompt is ~400-600 chars (was 2000+ before)
64
- expect(result.system_prompt.length).toBeGreaterThan(200);
65
- expect(result.system_prompt.length).toBeLessThan(1000);
66
- expect(result.system_prompt).toContain('responsible-vibe-mcp');
67
- });
68
-
69
- it('should exclude system prompt when requested', async () => {
70
- const result = await server.handleResumeWorkflow({
71
- include_system_prompt: false,
72
- });
73
-
74
- expect(result.system_prompt).toBeNull();
75
- });
76
-
77
- it('should provide phase-specific recommendations', async () => {
78
- const result = await server.handleResumeWorkflow({});
79
-
80
- const recommendations = result.recommendations;
81
- expect(recommendations.immediate_actions).toBeInstanceOf(Array);
82
- expect(recommendations.immediate_actions.length).toBeGreaterThan(0);
83
- expect(recommendations.phase_guidance).toBeTypeOf('string');
84
- expect(recommendations.potential_issues).toBeInstanceOf(Array);
85
-
86
- // Should always recommend calling whats_next
87
- const hasWhatsNext = recommendations.immediate_actions.some(
88
- (action: string) => action.includes('whats_next')
89
- );
90
- expect(hasWhatsNext).toBe(true);
91
- });
92
-
93
- it('should analyze plan file content when available', async () => {
94
- // Create a mock plan file
95
- const planContent = `# Development Plan
96
-
97
- ## Implementation
98
-
99
- ### Tasks
100
- - [x] Completed task 1
101
- - [x] Completed task 2
102
- - [ ] Active task 1
103
- - [ ] Active task 2
104
-
105
- ## Decision Log
106
- - Decision 1: Use TypeScript
107
- - Decision 2: Use Vitest for testing
108
- `;
109
-
110
- // Create .vibe directory and write plan file to expected location
111
- const planDir = join(tempDir, '.vibe');
112
- await mkdir(planDir, { recursive: true });
113
- await writeFile(join(planDir, 'development-plan-default.md'), planContent);
114
-
115
- const result = await server.handleResumeWorkflow({});
116
-
117
- const planStatus = result.plan_status;
118
- expect(planStatus.exists).toBe(true);
119
- expect(planStatus.analysis).toBeDefined();
120
-
121
- const analysis = planStatus.analysis;
122
- expect(analysis.active_tasks).toContain('Active task 1');
123
- expect(analysis.active_tasks).toContain('Active task 2');
124
- expect(analysis.completed_tasks).toContain('Completed task 1');
125
- expect(analysis.completed_tasks).toContain('Completed task 2');
126
-
127
- // Phase should come from database, not plan file
128
- expect(result.workflow_status.current_phase).toBeDefined();
129
- expect(result.workflow_status.current_phase).toBeTypeOf('string');
130
- });
131
-
132
- it('should handle missing plan file gracefully', async () => {
133
- // Create a custom test that mocks the plan manager to report no plan file
134
- const originalPlanManager = (server as unknown).components?.context
135
- ?.planManager;
136
-
137
- try {
138
- // Replace plan manager with a mock that reports no plan file
139
- if ((server as unknown).components?.context) {
140
- (server as unknown).components.context.planManager = {
141
- getPlanFileInfo: async () => ({
142
- exists: false,
143
- path: '/fake/path.md',
144
- }),
145
- ensurePlanFile: async () => {},
146
- setStateMachine: () => {},
147
- };
148
- }
149
-
150
- const result = await server.handleResumeWorkflow({});
151
-
152
- const planStatus = result.plan_status;
153
- expect(planStatus.exists).toBe(false);
154
- expect(planStatus.analysis).toBeNull();
155
- } finally {
156
- // Restore original plan manager
157
- if ((server as unknown).components?.context && originalPlanManager) {
158
- (server as unknown).components.context.planManager =
159
- originalPlanManager;
160
- }
161
- }
162
- });
163
-
164
- it('should provide different recommendations based on phase', async () => {
165
- // This test would ideally set up different phases and verify recommendations
166
- // For now, just verify the structure is correct
167
- const result = await server.handleResumeWorkflow({});
168
-
169
- const recommendations = result.recommendations;
170
- expect(recommendations.immediate_actions).toBeInstanceOf(Array);
171
- expect(recommendations.phase_guidance).toBeTypeOf('string');
172
- expect(recommendations.potential_issues).toBeInstanceOf(Array);
173
- });
174
-
175
- it('should handle errors gracefully', async () => {
176
- // This test ensures the method doesn't throw for any reasonable input
177
- await expect(server.handleResumeWorkflow({})).resolves.toBeDefined();
178
- await expect(
179
- server.handleResumeWorkflow({ include_system_prompt: true })
180
- ).resolves.toBeDefined();
181
- await expect(
182
- server.handleResumeWorkflow({ include_system_prompt: false })
183
- ).resolves.toBeDefined();
184
- });
185
-
186
- it('should include metadata in response', async () => {
187
- const result = await server.handleResumeWorkflow({});
188
-
189
- expect(result.generated_at).toBeTypeOf('string');
190
- expect(result.tool_version).toBeTypeOf('string');
191
- expect(new Date(result.generated_at)).toBeInstanceOf(Date);
192
- });
193
- });
@@ -1,99 +0,0 @@
1
- /**
2
- * Test plugin registration in server-config
3
- */
4
-
5
- import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest';
6
- import { initializeServerComponents } from '../../src/server-config.js';
7
- import { mkdtemp, rm } from 'node:fs/promises';
8
- import { tmpdir } from 'node:os';
9
- import { join } from 'node:path';
10
- import { execSync } from 'node:child_process';
11
-
12
- // Mock child_process to control bd command availability
13
- vi.mock('node:child_process', () => ({
14
- execSync: vi.fn(),
15
- }));
16
-
17
- describe('Server Config Plugin Registration', () => {
18
- let tempDir: string;
19
-
20
- beforeEach(async () => {
21
- vi.resetAllMocks(); // Reset mock implementations, not just call history
22
- tempDir = await mkdtemp(join(tmpdir(), 'server-config-test-'));
23
- });
24
-
25
- afterEach(async () => {
26
- vi.resetAllMocks();
27
- try {
28
- await rm(tempDir, { recursive: true, force: true });
29
- } catch {
30
- // Ignore cleanup errors
31
- }
32
- });
33
-
34
- it('should register BeadsPlugin when TASK_BACKEND is beads and bd is available', async () => {
35
- vi.stubEnv('TASK_BACKEND', 'beads');
36
-
37
- // Mock bd --version to return success
38
- vi.mocked(execSync).mockReturnValue('beads v1.0.0\n');
39
-
40
- const components = await initializeServerComponents({
41
- projectPath: tempDir,
42
- });
43
-
44
- expect(components.context.pluginRegistry).toBeDefined();
45
-
46
- // Check that BeadsPlugin was registered
47
- const pluginNames = components.context.pluginRegistry.getPluginNames();
48
- expect(pluginNames).toContain('BeadsPlugin');
49
-
50
- // Check that it's enabled
51
- const enabledPlugins =
52
- components.context.pluginRegistry.getEnabledPlugins();
53
- expect(enabledPlugins).toHaveLength(1);
54
- expect(enabledPlugins[0].getName()).toBe('BeadsPlugin');
55
- });
56
-
57
- it('should not register BeadsPlugin when TASK_BACKEND is markdown', async () => {
58
- // Explicitly set markdown to disable beads
59
- vi.stubEnv('TASK_BACKEND', 'markdown');
60
-
61
- const components = await initializeServerComponents({
62
- projectPath: tempDir,
63
- });
64
-
65
- expect(components.context.pluginRegistry).toBeDefined();
66
-
67
- // Check that no plugins are registered
68
- const pluginNames = components.context.pluginRegistry.getPluginNames();
69
- expect(pluginNames).toHaveLength(0);
70
-
71
- // Check that no plugins are enabled
72
- const enabledPlugins =
73
- components.context.pluginRegistry.getEnabledPlugins();
74
- expect(enabledPlugins).toHaveLength(0);
75
- });
76
-
77
- it('should not register BeadsPlugin when bd is not available', async () => {
78
- // Explicitly clear TASK_BACKEND - triggers auto-detection
79
- delete process.env.TASK_BACKEND;
80
-
81
- // Mock bd --version to throw (command not found)
82
- vi.mocked(execSync).mockImplementation(() => {
83
- throw new Error('command not found: bd');
84
- });
85
-
86
- const components = await initializeServerComponents({
87
- projectPath: tempDir,
88
- });
89
-
90
- expect(components.context.pluginRegistry).toBeDefined();
91
- expect(components.context.pluginRegistry.getPluginNames()).toHaveLength(0);
92
- expect(components.context.pluginRegistry.getEnabledPlugins()).toHaveLength(
93
- 0
94
- );
95
- });
96
-
97
- // Note: Auto-detection tests are covered in E2E tests (beads-plugin-integration.test.ts)
98
- // because mocking child_process across package boundaries requires E2E-style server setup
99
- });