@codemcp/workflows 4.10.0 → 4.10.2

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 (74) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/components/beads/beads-instruction-generator.d.ts +3 -4
  3. package/dist/components/beads/beads-instruction-generator.d.ts.map +1 -1
  4. package/dist/components/beads/beads-instruction-generator.js +12 -7
  5. package/dist/components/beads/beads-instruction-generator.js.map +1 -1
  6. package/dist/components/beads/beads-task-backend-client.d.ts.map +1 -1
  7. package/dist/components/beads/beads-task-backend-client.js +1 -4
  8. package/dist/components/beads/beads-task-backend-client.js.map +1 -1
  9. package/dist/plugin-system/beads-plugin.d.ts +70 -0
  10. package/dist/plugin-system/beads-plugin.d.ts.map +1 -0
  11. package/dist/plugin-system/beads-plugin.js +459 -0
  12. package/dist/plugin-system/beads-plugin.js.map +1 -0
  13. package/dist/plugin-system/index.d.ts +9 -0
  14. package/dist/plugin-system/index.d.ts.map +1 -0
  15. package/dist/plugin-system/index.js +9 -0
  16. package/dist/plugin-system/index.js.map +1 -0
  17. package/dist/plugin-system/plugin-interfaces.d.ts +99 -0
  18. package/dist/plugin-system/plugin-interfaces.d.ts.map +1 -0
  19. package/dist/plugin-system/plugin-interfaces.js +9 -0
  20. package/dist/plugin-system/plugin-interfaces.js.map +1 -0
  21. package/dist/plugin-system/plugin-registry.d.ts +44 -0
  22. package/dist/plugin-system/plugin-registry.d.ts.map +1 -0
  23. package/dist/plugin-system/plugin-registry.js +132 -0
  24. package/dist/plugin-system/plugin-registry.js.map +1 -0
  25. package/dist/server-config.d.ts.map +1 -1
  26. package/dist/server-config.js +28 -8
  27. package/dist/server-config.js.map +1 -1
  28. package/dist/tool-handlers/conduct-review.d.ts.map +1 -1
  29. package/dist/tool-handlers/conduct-review.js +1 -2
  30. package/dist/tool-handlers/conduct-review.js.map +1 -1
  31. package/dist/tool-handlers/proceed-to-phase.d.ts +0 -5
  32. package/dist/tool-handlers/proceed-to-phase.d.ts.map +1 -1
  33. package/dist/tool-handlers/proceed-to-phase.js +15 -93
  34. package/dist/tool-handlers/proceed-to-phase.js.map +1 -1
  35. package/dist/tool-handlers/start-development.d.ts +0 -13
  36. package/dist/tool-handlers/start-development.d.ts.map +1 -1
  37. package/dist/tool-handlers/start-development.js +29 -124
  38. package/dist/tool-handlers/start-development.js.map +1 -1
  39. package/dist/tool-handlers/whats-next.d.ts.map +1 -1
  40. package/dist/tool-handlers/whats-next.js +1 -0
  41. package/dist/tool-handlers/whats-next.js.map +1 -1
  42. package/dist/types.d.ts +2 -0
  43. package/dist/types.d.ts.map +1 -1
  44. package/package.json +2 -2
  45. package/src/components/beads/beads-instruction-generator.ts +12 -12
  46. package/src/components/beads/beads-task-backend-client.ts +1 -4
  47. package/src/plugin-system/beads-plugin.ts +641 -0
  48. package/src/plugin-system/index.ts +20 -0
  49. package/src/plugin-system/plugin-interfaces.ts +154 -0
  50. package/src/plugin-system/plugin-registry.ts +190 -0
  51. package/src/server-config.ts +30 -8
  52. package/src/tool-handlers/conduct-review.ts +1 -2
  53. package/src/tool-handlers/proceed-to-phase.ts +19 -135
  54. package/src/tool-handlers/start-development.ts +35 -205
  55. package/src/tool-handlers/whats-next.ts +1 -0
  56. package/src/types.ts +2 -0
  57. package/test/e2e/beads-plugin-integration.test.ts +1609 -0
  58. package/test/e2e/plugin-system-integration.test.ts +1729 -0
  59. package/test/unit/beads-plugin-behavioral.test.ts +512 -0
  60. package/test/unit/beads-plugin.test.ts +94 -0
  61. package/test/unit/plugin-error-handling.test.ts +240 -0
  62. package/test/unit/proceed-to-phase-plugin-integration.test.ts +150 -0
  63. package/test/unit/server-config-plugin-registry.test.ts +81 -0
  64. package/test/unit/start-development-goal-extraction.test.ts +22 -16
  65. package/test/utils/test-helpers.ts +3 -1
  66. package/tsconfig.build.tsbuildinfo +1 -1
  67. package/dist/components/server-components-factory.d.ts +0 -39
  68. package/dist/components/server-components-factory.d.ts.map +0 -1
  69. package/dist/components/server-components-factory.js +0 -62
  70. package/dist/components/server-components-factory.js.map +0 -1
  71. package/src/components/server-components-factory.ts +0 -86
  72. package/test/e2e/component-substitution.test.ts +0 -208
  73. package/test/unit/beads-integration-filename.test.ts +0 -93
  74. package/test/unit/server-components-factory.test.ts +0 -279
@@ -0,0 +1,240 @@
1
+ /**
2
+ * Tests for Plugin Error Handling and Graceful Degradation
3
+ *
4
+ * Verifies that the plugin system handles errors gracefully:
5
+ * - Plugin failures don't crash the core application
6
+ * - Non-critical plugin errors allow graceful degradation
7
+ * - Validation errors (beforePhaseTransition) are always re-thrown
8
+ */
9
+
10
+ import { describe, it, expect, vi } from 'vitest';
11
+ import { PluginRegistry } from '../../src/plugin-system/plugin-registry.js';
12
+ import type { IPlugin } from '../../src/plugin-system/plugin-interfaces.js';
13
+
14
+ const createMockContext = () => ({
15
+ conversationId: 'test',
16
+ planFilePath: '/test/plan.md',
17
+ projectPath: '/test',
18
+ currentPhase: 'explore',
19
+ workflow: 'epcc',
20
+ gitBranch: 'main',
21
+ });
22
+
23
+ describe('Plugin Error Handling and Graceful Degradation', () => {
24
+ describe('Non-critical hook error handling', () => {
25
+ it('should continue execution when afterStartDevelopment hook fails', async () => {
26
+ const registry = new PluginRegistry();
27
+
28
+ // Register first plugin that throws
29
+ const failingPlugin: IPlugin = {
30
+ getName: () => 'FailingPlugin',
31
+ getSequence: () => 1,
32
+ isEnabled: () => true,
33
+ getHooks: () => ({
34
+ afterStartDevelopment: vi
35
+ .fn()
36
+ .mockRejectedValue(new Error('Beads backend unavailable')),
37
+ }),
38
+ };
39
+
40
+ // Register second plugin that succeeds
41
+ const successHookSpy = vi.fn().mockResolvedValue(undefined);
42
+ const successPlugin: IPlugin = {
43
+ getName: () => 'SuccessPlugin',
44
+ getSequence: () => 2,
45
+ isEnabled: () => true,
46
+ getHooks: () => ({
47
+ afterStartDevelopment: successHookSpy,
48
+ }),
49
+ };
50
+
51
+ registry.registerPlugin(failingPlugin);
52
+ registry.registerPlugin(successPlugin);
53
+
54
+ // Execute hook - should NOT throw despite first plugin failure
55
+ const result = await registry.executeHook(
56
+ 'afterStartDevelopment',
57
+ createMockContext(),
58
+ { workflow: 'epcc', commit_behaviour: 'end' },
59
+ {
60
+ conversationId: 'test',
61
+ planFilePath: '/test/plan.md',
62
+ phase: 'explore',
63
+ workflow: 'epcc',
64
+ }
65
+ );
66
+
67
+ // Should reach here without throwing
68
+ expect(result).toBeUndefined();
69
+ expect(successHookSpy).toHaveBeenCalled();
70
+ });
71
+
72
+ it('should continue execution when afterPlanFileCreated hook fails', async () => {
73
+ const registry = new PluginRegistry();
74
+
75
+ const failingPlugin: IPlugin = {
76
+ getName: () => 'FailingPlugin',
77
+ getSequence: () => 1,
78
+ isEnabled: () => true,
79
+ getHooks: () => ({
80
+ afterPlanFileCreated: vi
81
+ .fn()
82
+ .mockRejectedValue(new Error('Plan file update failed')),
83
+ }),
84
+ };
85
+
86
+ registry.registerPlugin(failingPlugin);
87
+
88
+ // Should not throw despite plugin error
89
+ const result = await registry.executeHook(
90
+ 'afterPlanFileCreated',
91
+ createMockContext(),
92
+ '/test/plan.md',
93
+ 'initial content'
94
+ );
95
+
96
+ expect(result).toBeUndefined();
97
+ });
98
+ });
99
+
100
+ describe('Validation hook error handling', () => {
101
+ it('should re-throw validation errors from beforePhaseTransition', async () => {
102
+ const registry = new PluginRegistry();
103
+
104
+ const validationPlugin: IPlugin = {
105
+ getName: () => 'ValidationPlugin',
106
+ getSequence: () => 1,
107
+ isEnabled: () => true,
108
+ getHooks: () => ({
109
+ beforePhaseTransition: vi
110
+ .fn()
111
+ .mockRejectedValue(
112
+ new Error('Cannot proceed to code - incomplete tasks')
113
+ ),
114
+ }),
115
+ };
116
+
117
+ registry.registerPlugin(validationPlugin);
118
+
119
+ // Should re-throw validation errors
120
+ await expect(
121
+ registry.executeHook(
122
+ 'beforePhaseTransition',
123
+ createMockContext(),
124
+ 'plan',
125
+ 'code'
126
+ )
127
+ ).rejects.toThrow('Cannot proceed to code - incomplete tasks');
128
+ });
129
+
130
+ it('should re-throw any beforePhaseTransition hook errors', async () => {
131
+ const registry = new PluginRegistry();
132
+
133
+ const validationPlugin: IPlugin = {
134
+ getName: () => 'ValidationPlugin',
135
+ getSequence: () => 1,
136
+ isEnabled: () => true,
137
+ getHooks: () => ({
138
+ beforePhaseTransition: vi
139
+ .fn()
140
+ .mockRejectedValue(new Error('Validation failed for any reason')),
141
+ }),
142
+ };
143
+
144
+ registry.registerPlugin(validationPlugin);
145
+
146
+ // Should re-throw validation errors (not just specific messages)
147
+ await expect(
148
+ registry.executeHook(
149
+ 'beforePhaseTransition',
150
+ createMockContext(),
151
+ 'plan',
152
+ 'code'
153
+ )
154
+ ).rejects.toThrow('Validation failed for any reason');
155
+ });
156
+ });
157
+
158
+ describe('Multiple plugin execution', () => {
159
+ it('should execute all plugins even if some fail on non-critical hooks', async () => {
160
+ const registry = new PluginRegistry();
161
+
162
+ const plugin1Spy = vi
163
+ .fn()
164
+ .mockRejectedValue(new Error('Plugin 1 failed'));
165
+ const plugin2Spy = vi.fn().mockResolvedValue(undefined);
166
+ const plugin3Spy = vi.fn().mockResolvedValue(undefined);
167
+
168
+ registry.registerPlugin({
169
+ getName: () => 'Plugin1',
170
+ getSequence: () => 1,
171
+ isEnabled: () => true,
172
+ getHooks: () => ({ afterStartDevelopment: plugin1Spy }),
173
+ });
174
+
175
+ registry.registerPlugin({
176
+ getName: () => 'Plugin2',
177
+ getSequence: () => 2,
178
+ isEnabled: () => true,
179
+ getHooks: () => ({ afterStartDevelopment: plugin2Spy }),
180
+ });
181
+
182
+ registry.registerPlugin({
183
+ getName: () => 'Plugin3',
184
+ getSequence: () => 3,
185
+ isEnabled: () => true,
186
+ getHooks: () => ({ afterStartDevelopment: plugin3Spy }),
187
+ });
188
+
189
+ // Execute should not throw
190
+ const _result = await registry.executeHook(
191
+ 'afterStartDevelopment',
192
+ createMockContext(),
193
+ { workflow: 'epcc', commit_behaviour: 'end' },
194
+ {
195
+ conversationId: 'test',
196
+ planFilePath: '/test/plan.md',
197
+ phase: 'explore',
198
+ workflow: 'epcc',
199
+ }
200
+ );
201
+
202
+ // All plugins should be called
203
+ expect(plugin1Spy).toHaveBeenCalled();
204
+ expect(plugin2Spy).toHaveBeenCalled();
205
+ expect(plugin3Spy).toHaveBeenCalled();
206
+ });
207
+ });
208
+
209
+ describe('Disabled plugin handling', () => {
210
+ it('should not execute hooks from disabled plugins', async () => {
211
+ const registry = new PluginRegistry();
212
+
213
+ const hookSpy = vi.fn();
214
+
215
+ const disabledPlugin: IPlugin = {
216
+ getName: () => 'DisabledPlugin',
217
+ getSequence: () => 1,
218
+ isEnabled: () => false, // Disabled
219
+ getHooks: () => ({ afterStartDevelopment: hookSpy }),
220
+ };
221
+
222
+ registry.registerPlugin(disabledPlugin);
223
+
224
+ await registry.executeHook(
225
+ 'afterStartDevelopment',
226
+ createMockContext(),
227
+ { workflow: 'epcc', commit_behaviour: 'end' },
228
+ {
229
+ conversationId: 'test',
230
+ planFilePath: '/test/plan.md',
231
+ phase: 'explore',
232
+ workflow: 'epcc',
233
+ }
234
+ );
235
+
236
+ // Disabled plugin's hook should not be called
237
+ expect(hookSpy).not.toHaveBeenCalled();
238
+ });
239
+ });
240
+ });
@@ -0,0 +1,150 @@
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
+ });
@@ -0,0 +1,81 @@
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
+
11
+ describe('Server Config Plugin Registration', () => {
12
+ let tempDir: string;
13
+
14
+ beforeEach(async () => {
15
+ vi.clearAllMocks();
16
+ tempDir = await mkdtemp(join(tmpdir(), 'server-config-test-'));
17
+ });
18
+
19
+ afterEach(async () => {
20
+ vi.clearAllMocks();
21
+ try {
22
+ await rm(tempDir, { recursive: true, force: true });
23
+ } catch {
24
+ // Ignore cleanup errors
25
+ }
26
+ });
27
+
28
+ it('should register BeadsPlugin when TASK_BACKEND is beads', async () => {
29
+ vi.stubEnv('TASK_BACKEND', 'beads');
30
+
31
+ const components = await initializeServerComponents({
32
+ projectPath: tempDir,
33
+ });
34
+
35
+ expect(components.context.pluginRegistry).toBeDefined();
36
+
37
+ // Check that BeadsPlugin was registered
38
+ const pluginNames = components.context.pluginRegistry.getPluginNames();
39
+ expect(pluginNames).toContain('BeadsPlugin');
40
+
41
+ // Check that it's enabled
42
+ const enabledPlugins =
43
+ components.context.pluginRegistry.getEnabledPlugins();
44
+ expect(enabledPlugins).toHaveLength(1);
45
+ expect(enabledPlugins[0].getName()).toBe('BeadsPlugin');
46
+ });
47
+
48
+ it('should not register BeadsPlugin when TASK_BACKEND is not beads', async () => {
49
+ vi.stubEnv('TASK_BACKEND', 'none');
50
+
51
+ const components = await initializeServerComponents({
52
+ projectPath: tempDir,
53
+ });
54
+
55
+ expect(components.context.pluginRegistry).toBeDefined();
56
+
57
+ // Check that no plugins are registered
58
+ const pluginNames = components.context.pluginRegistry.getPluginNames();
59
+ expect(pluginNames).toHaveLength(0);
60
+
61
+ // Check that no plugins are enabled
62
+ const enabledPlugins =
63
+ components.context.pluginRegistry.getEnabledPlugins();
64
+ expect(enabledPlugins).toHaveLength(0);
65
+ });
66
+
67
+ it('should initialize empty plugin registry by default', async () => {
68
+ // Don't set TASK_BACKEND environment variable
69
+ vi.unstubAllEnvs();
70
+
71
+ const components = await initializeServerComponents({
72
+ projectPath: tempDir,
73
+ });
74
+
75
+ expect(components.context.pluginRegistry).toBeDefined();
76
+ expect(components.context.pluginRegistry.getPluginNames()).toHaveLength(0);
77
+ expect(components.context.pluginRegistry.getEnabledPlugins()).toHaveLength(
78
+ 0
79
+ );
80
+ });
81
+ });
@@ -1,19 +1,25 @@
1
1
  /**
2
- * Unit tests for StartDevelopmentHandler Goal extraction functionality
2
+ * Unit tests for BeadsPlugin Goal extraction functionality
3
3
  *
4
4
  * Tests the extractGoalFromPlan method that extracts meaningful goal content
5
5
  * from development plan files for use in beads integration
6
6
  */
7
7
 
8
- import { describe, it, expect, beforeEach } from 'vitest';
8
+ import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest';
9
9
  import { TestAccess } from '../utils/test-access.js';
10
- import { StartDevelopmentHandler } from '../../src/tool-handlers/start-development.js';
10
+ import { BeadsPlugin } from '../../src/plugin-system/beads-plugin.js';
11
11
 
12
- describe('StartDevelopmentHandler - Goal Extraction', () => {
13
- let handler: StartDevelopmentHandler;
12
+ describe('BeadsPlugin - Goal Extraction', () => {
13
+ let plugin: BeadsPlugin;
14
14
 
15
15
  beforeEach(() => {
16
- handler = new StartDevelopmentHandler();
16
+ // Mock environment variable for plugin enablement
17
+ vi.stubEnv('TASK_BACKEND', 'beads');
18
+ plugin = new BeadsPlugin({ projectPath: '/test/project' });
19
+ });
20
+
21
+ afterEach(() => {
22
+ vi.unstubAllEnvs();
17
23
  });
18
24
 
19
25
  describe('extractGoalFromPlan', () => {
@@ -29,7 +35,7 @@ Build a user authentication system with JWT tokens and password reset functional
29
35
  `;
30
36
 
31
37
  const result = TestAccess.callMethod(
32
- handler,
38
+ plugin,
33
39
  'extractGoalFromPlan',
34
40
  planContent
35
41
  );
@@ -51,7 +57,7 @@ Build a user authentication system with JWT tokens and password reset functional
51
57
  `;
52
58
 
53
59
  const result = TestAccess.callMethod(
54
- handler,
60
+ plugin,
55
61
  'extractGoalFromPlan',
56
62
  planContent
57
63
  );
@@ -71,7 +77,7 @@ To be defined during exploration
71
77
  `;
72
78
 
73
79
  const result = TestAccess.callMethod(
74
- handler,
80
+ plugin,
75
81
  'extractGoalFromPlan',
76
82
  planContent
77
83
  );
@@ -91,7 +97,7 @@ Fix bug
91
97
  `;
92
98
 
93
99
  const result = TestAccess.callMethod(
94
- handler,
100
+ plugin,
95
101
  'extractGoalFromPlan',
96
102
  planContent
97
103
  );
@@ -117,7 +123,7 @@ The system should support different log levels and output formats.
117
123
  `;
118
124
 
119
125
  const result = TestAccess.callMethod(
120
- handler,
126
+ plugin,
121
127
  'extractGoalFromPlan',
122
128
  planContent
123
129
  );
@@ -141,7 +147,7 @@ The system should support different log levels and output formats.`);
141
147
  `;
142
148
 
143
149
  const result = TestAccess.callMethod(
144
- handler,
150
+ plugin,
145
151
  'extractGoalFromPlan',
146
152
  planContent
147
153
  );
@@ -151,15 +157,15 @@ The system should support different log levels and output formats.`);
151
157
 
152
158
  it('should return undefined for empty or null input', () => {
153
159
  expect(
154
- TestAccess.callMethod(handler, 'extractGoalFromPlan', '')
160
+ TestAccess.callMethod(plugin, 'extractGoalFromPlan', '')
155
161
  ).toBeUndefined();
156
162
 
157
163
  expect(
158
- TestAccess.callMethod(handler, 'extractGoalFromPlan', null)
164
+ TestAccess.callMethod(plugin, 'extractGoalFromPlan', null)
159
165
  ).toBeUndefined();
160
166
 
161
167
  expect(
162
- TestAccess.callMethod(handler, 'extractGoalFromPlan', undefined)
168
+ TestAccess.callMethod(plugin, 'extractGoalFromPlan', undefined)
163
169
  ).toBeUndefined();
164
170
  });
165
171
 
@@ -175,7 +181,7 @@ Build a user authentication system with secure login and registration.
175
181
  `;
176
182
 
177
183
  const result = TestAccess.callMethod(
178
- handler,
184
+ plugin,
179
185
  'extractGoalFromPlan',
180
186
  planContent
181
187
  );
@@ -11,10 +11,11 @@ import { vi, expect } from 'vitest';
11
11
  import {
12
12
  ResponsibleVibeMCPServer,
13
13
  createResponsibleVibeMCPServer,
14
- StartDevelopmentResult,
15
14
  } from '../../src/server.js';
16
15
  import type { ServerContext } from '../../src/types';
16
+ import type { StartDevelopmentResult } from '../../src/tool-handlers/start-development.js';
17
17
  import { TempProject } from './temp-files.js';
18
+ import { PluginRegistry } from '../../src/plugin-system/plugin-registry.js';
18
19
 
19
20
  /**
20
21
  * Mock project documents content
@@ -114,6 +115,7 @@ export class MockContextFactory {
114
115
  ) {
115
116
  return {
116
117
  projectPath,
118
+ pluginRegistry: new PluginRegistry(),
117
119
  workflowManager: {
118
120
  validateWorkflowName: vi.fn().mockReturnValue(true),
119
121
  getWorkflowNames: vi