@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.
Files changed (159) hide show
  1. package/.turbo/turbo-build.log +4 -0
  2. package/.vibe/conversation-state.sqlite +0 -0
  3. package/LICENSE +674 -0
  4. package/dist/index.d.ts +9 -0
  5. package/dist/index.d.ts.map +1 -0
  6. package/dist/index.js +74 -0
  7. package/dist/index.js.map +1 -0
  8. package/dist/notification-service.d.ts +14 -0
  9. package/dist/notification-service.d.ts.map +1 -0
  10. package/dist/notification-service.js +18 -0
  11. package/dist/notification-service.js.map +1 -0
  12. package/dist/resource-handlers/conversation-state.d.ts +15 -0
  13. package/dist/resource-handlers/conversation-state.d.ts.map +1 -0
  14. package/dist/resource-handlers/conversation-state.js +40 -0
  15. package/dist/resource-handlers/conversation-state.js.map +1 -0
  16. package/dist/resource-handlers/development-plan.d.ts +14 -0
  17. package/dist/resource-handlers/development-plan.d.ts.map +1 -0
  18. package/dist/resource-handlers/development-plan.js +31 -0
  19. package/dist/resource-handlers/development-plan.js.map +1 -0
  20. package/dist/resource-handlers/index.d.ts +24 -0
  21. package/dist/resource-handlers/index.d.ts.map +1 -0
  22. package/dist/resource-handlers/index.js +62 -0
  23. package/dist/resource-handlers/index.js.map +1 -0
  24. package/dist/resource-handlers/system-prompt.d.ts +15 -0
  25. package/dist/resource-handlers/system-prompt.d.ts.map +1 -0
  26. package/dist/resource-handlers/system-prompt.js +40 -0
  27. package/dist/resource-handlers/system-prompt.js.map +1 -0
  28. package/dist/resource-handlers/workflow-resource.d.ts +15 -0
  29. package/dist/resource-handlers/workflow-resource.d.ts.map +1 -0
  30. package/dist/resource-handlers/workflow-resource.js +85 -0
  31. package/dist/resource-handlers/workflow-resource.js.map +1 -0
  32. package/dist/response-renderer.d.ts +30 -0
  33. package/dist/response-renderer.d.ts.map +1 -0
  34. package/dist/response-renderer.js +94 -0
  35. package/dist/response-renderer.js.map +1 -0
  36. package/dist/server-config.d.ts +34 -0
  37. package/dist/server-config.d.ts.map +1 -0
  38. package/dist/server-config.js +486 -0
  39. package/dist/server-config.js.map +1 -0
  40. package/dist/server-helpers.d.ts +62 -0
  41. package/dist/server-helpers.d.ts.map +1 -0
  42. package/dist/server-helpers.js +156 -0
  43. package/dist/server-helpers.js.map +1 -0
  44. package/dist/server-implementation.d.ts +74 -0
  45. package/dist/server-implementation.d.ts.map +1 -0
  46. package/dist/server-implementation.js +201 -0
  47. package/dist/server-implementation.js.map +1 -0
  48. package/dist/server.d.ts +6 -0
  49. package/dist/server.d.ts.map +1 -0
  50. package/dist/server.js +5 -0
  51. package/dist/server.js.map +1 -0
  52. package/dist/tool-handlers/base-tool-handler.d.ts +50 -0
  53. package/dist/tool-handlers/base-tool-handler.d.ts.map +1 -0
  54. package/dist/tool-handlers/base-tool-handler.js +74 -0
  55. package/dist/tool-handlers/base-tool-handler.js.map +1 -0
  56. package/dist/tool-handlers/conduct-review.d.ts +49 -0
  57. package/dist/tool-handlers/conduct-review.d.ts.map +1 -0
  58. package/dist/tool-handlers/conduct-review.js +105 -0
  59. package/dist/tool-handlers/conduct-review.js.map +1 -0
  60. package/dist/tool-handlers/get-tool-info.d.ts +76 -0
  61. package/dist/tool-handlers/get-tool-info.d.ts.map +1 -0
  62. package/dist/tool-handlers/get-tool-info.js +168 -0
  63. package/dist/tool-handlers/get-tool-info.js.map +1 -0
  64. package/dist/tool-handlers/index.d.ts +42 -0
  65. package/dist/tool-handlers/index.d.ts.map +1 -0
  66. package/dist/tool-handlers/index.js +74 -0
  67. package/dist/tool-handlers/index.js.map +1 -0
  68. package/dist/tool-handlers/install-workflow.d.ts +48 -0
  69. package/dist/tool-handlers/install-workflow.d.ts.map +1 -0
  70. package/dist/tool-handlers/install-workflow.js +131 -0
  71. package/dist/tool-handlers/install-workflow.js.map +1 -0
  72. package/dist/tool-handlers/list-workflows.d.ts +47 -0
  73. package/dist/tool-handlers/list-workflows.d.ts.map +1 -0
  74. package/dist/tool-handlers/list-workflows.js +58 -0
  75. package/dist/tool-handlers/list-workflows.js.map +1 -0
  76. package/dist/tool-handlers/no-idea.d.ts +41 -0
  77. package/dist/tool-handlers/no-idea.d.ts.map +1 -0
  78. package/dist/tool-handlers/no-idea.js +29 -0
  79. package/dist/tool-handlers/no-idea.js.map +1 -0
  80. package/dist/tool-handlers/proceed-to-phase.d.ts +39 -0
  81. package/dist/tool-handlers/proceed-to-phase.d.ts.map +1 -0
  82. package/dist/tool-handlers/proceed-to-phase.js +109 -0
  83. package/dist/tool-handlers/proceed-to-phase.js.map +1 -0
  84. package/dist/tool-handlers/reset-development.d.ts +31 -0
  85. package/dist/tool-handlers/reset-development.d.ts.map +1 -0
  86. package/dist/tool-handlers/reset-development.js +48 -0
  87. package/dist/tool-handlers/reset-development.js.map +1 -0
  88. package/dist/tool-handlers/resume-workflow.d.ts +88 -0
  89. package/dist/tool-handlers/resume-workflow.d.ts.map +1 -0
  90. package/dist/tool-handlers/resume-workflow.js +213 -0
  91. package/dist/tool-handlers/resume-workflow.js.map +1 -0
  92. package/dist/tool-handlers/setup-project-docs.d.ts +36 -0
  93. package/dist/tool-handlers/setup-project-docs.d.ts.map +1 -0
  94. package/dist/tool-handlers/setup-project-docs.js +136 -0
  95. package/dist/tool-handlers/setup-project-docs.js.map +1 -0
  96. package/dist/tool-handlers/start-development.d.ts +82 -0
  97. package/dist/tool-handlers/start-development.d.ts.map +1 -0
  98. package/dist/tool-handlers/start-development.js +448 -0
  99. package/dist/tool-handlers/start-development.js.map +1 -0
  100. package/dist/tool-handlers/whats-next.d.ts +42 -0
  101. package/dist/tool-handlers/whats-next.d.ts.map +1 -0
  102. package/dist/tool-handlers/whats-next.js +118 -0
  103. package/dist/tool-handlers/whats-next.js.map +1 -0
  104. package/dist/types.d.ts +114 -0
  105. package/dist/types.d.ts.map +1 -0
  106. package/dist/types.js +5 -0
  107. package/dist/types.js.map +1 -0
  108. package/package.json +29 -0
  109. package/src/index.ts +93 -0
  110. package/src/notification-service.ts +23 -0
  111. package/src/resource-handlers/conversation-state.ts +55 -0
  112. package/src/resource-handlers/development-plan.ts +48 -0
  113. package/src/resource-handlers/index.ts +73 -0
  114. package/src/resource-handlers/system-prompt.ts +55 -0
  115. package/src/resource-handlers/workflow-resource.ts +126 -0
  116. package/src/response-renderer.ts +116 -0
  117. package/src/server-config.ts +744 -0
  118. package/src/server-helpers.ts +225 -0
  119. package/src/server-implementation.ts +277 -0
  120. package/src/server.ts +9 -0
  121. package/src/tool-handlers/base-tool-handler.ts +141 -0
  122. package/src/tool-handlers/conduct-review.ts +191 -0
  123. package/src/tool-handlers/get-tool-info.ts +274 -0
  124. package/src/tool-handlers/index.ts +117 -0
  125. package/src/tool-handlers/install-workflow.ts +185 -0
  126. package/src/tool-handlers/list-workflows.ts +94 -0
  127. package/src/tool-handlers/no-idea.ts +47 -0
  128. package/src/tool-handlers/proceed-to-phase.ts +205 -0
  129. package/src/tool-handlers/reset-development.ts +90 -0
  130. package/src/tool-handlers/resume-workflow.ts +380 -0
  131. package/src/tool-handlers/setup-project-docs.ts +226 -0
  132. package/src/tool-handlers/start-development.ts +685 -0
  133. package/src/tool-handlers/whats-next.ts +235 -0
  134. package/src/types.ts +130 -0
  135. package/test/e2e/core-functionality.test.ts +176 -0
  136. package/test/e2e/mcp-contract.test.ts +540 -0
  137. package/test/e2e/plan-management.test.ts +331 -0
  138. package/test/e2e/state-management.test.ts +392 -0
  139. package/test/e2e/workflow-integration.test.ts +506 -0
  140. package/test/unit/commit-behaviour-interface.test.ts +244 -0
  141. package/test/unit/conduct-review.test.ts +151 -0
  142. package/test/unit/reset-functionality.test.ts +72 -0
  143. package/test/unit/resume-workflow.test.ts +192 -0
  144. package/test/unit/server-tools.test.ts +311 -0
  145. package/test/unit/setup-project-docs-handler.test.ts +267 -0
  146. package/test/unit/start-development-artifact-detection.test.ts +387 -0
  147. package/test/unit/start-development-gitignore.test.ts +178 -0
  148. package/test/unit/system-prompt-resource.test.ts +101 -0
  149. package/test/unit/tool-handlers/no-idea.test.ts +40 -0
  150. package/test/utils/e2e-test-setup.ts +453 -0
  151. package/test/utils/run-server-in-dir.sh +27 -0
  152. package/test/utils/temp-files.ts +308 -0
  153. package/test/utils/test-access.ts +79 -0
  154. package/test/utils/test-helpers.ts +286 -0
  155. package/test/utils/test-setup.ts +78 -0
  156. package/tsconfig.build.json +9 -0
  157. package/tsconfig.build.tsbuildinfo +1 -0
  158. package/tsconfig.json +12 -0
  159. package/vitest.config.ts +17 -0
@@ -0,0 +1,225 @@
1
+ /**
2
+ * Server Helper Functions
3
+ *
4
+ * Common utility functions used across the server implementation.
5
+ * These are pure functions that don't depend on server state.
6
+ */
7
+
8
+ import { homedir } from 'node:os';
9
+ import { createLogger } from '@codemcp/workflows-core';
10
+ import { HandlerResult } from './types.js';
11
+
12
+ const logger = createLogger('ServerHelpers');
13
+
14
+ /**
15
+ * Normalize and validate project path
16
+ * Ensures we have a valid project path, defaulting to home directory if needed
17
+ */
18
+ export function normalizeProjectPath(projectPath?: string): string {
19
+ const path = projectPath || process.cwd();
20
+
21
+ if (path === '/' || path === '') {
22
+ const homePath = homedir();
23
+ logger.info('Invalid project path detected, using home directory', {
24
+ originalPath: path,
25
+ normalizedPath: homePath,
26
+ });
27
+ return homePath;
28
+ }
29
+
30
+ return path;
31
+ }
32
+
33
+ /**
34
+ * Create a standardized success result
35
+ */
36
+ export function createSuccessResult<T>(
37
+ data: T,
38
+ metadata?: Record<string, unknown>
39
+ ): HandlerResult<T> {
40
+ return {
41
+ success: true,
42
+ data,
43
+ metadata,
44
+ };
45
+ }
46
+
47
+ /**
48
+ * Create a standardized error result
49
+ */
50
+ export function createErrorResult(
51
+ error: string | Error,
52
+ metadata?: Record<string, unknown>
53
+ ): HandlerResult<never> {
54
+ const errorMessage = error instanceof Error ? error.message : error;
55
+
56
+ return {
57
+ success: false,
58
+ error: errorMessage,
59
+ metadata,
60
+ };
61
+ }
62
+
63
+ /**
64
+ * Safely execute an async operation and return a HandlerResult
65
+ * This provides consistent error handling across all handlers
66
+ */
67
+ export async function safeExecute<T>(
68
+ operation: () => Promise<T>,
69
+ errorContext?: string
70
+ ): Promise<HandlerResult<T>> {
71
+ try {
72
+ const result = await operation();
73
+ return createSuccessResult(result);
74
+ } catch (error) {
75
+ const errorMessage =
76
+ error instanceof Error ? error.message : 'Unknown error';
77
+ const contextualError = errorContext
78
+ ? `${errorContext}: ${errorMessage}`
79
+ : errorMessage;
80
+
81
+ logger.error('Operation failed', error as Error, { errorContext });
82
+ return createErrorResult(contextualError);
83
+ }
84
+ }
85
+
86
+ /**
87
+ * Validate required arguments for tool handlers
88
+ * Throws an error if any required arguments are missing
89
+ */
90
+ export function validateRequiredArgs(
91
+ args: unknown,
92
+ requiredFields: string[]
93
+ ): void {
94
+ const missingFields = requiredFields.filter(
95
+ field =>
96
+ (args as Record<string, unknown>)[field] === undefined ||
97
+ (args as Record<string, unknown>)[field] === null
98
+ );
99
+
100
+ if (missingFields.length > 0) {
101
+ throw new Error(`Missing required arguments: ${missingFields.join(', ')}`);
102
+ }
103
+ }
104
+
105
+ /**
106
+ * Check if a conversation exists and provide helpful error if not
107
+ */
108
+ export function createConversationNotFoundResult(
109
+ availableWorkflows: string[] = []
110
+ ): HandlerResult<never> {
111
+ if (availableWorkflows.length === 0) {
112
+ return createErrorResult(
113
+ 'No development conversation has been started for this project and no workflows are available. Please install a workflow first or adjust the VIBE_WORKFLOW_DOMAINS environment variable.',
114
+ {
115
+ suggestion:
116
+ 'install_workflow({ source: "waterfall" }) or set VIBE_WORKFLOW_DOMAINS=code,architecture,office',
117
+ availableWorkflows: [],
118
+ }
119
+ );
120
+ }
121
+
122
+ const firstWorkflow = availableWorkflows[0];
123
+ return createErrorResult(
124
+ 'No development conversation has been started for this project. Please use the start_development tool first to initialize development with a workflow.',
125
+ {
126
+ suggestion: `start_development({ workflow: "${firstWorkflow}" })`,
127
+ availableWorkflows,
128
+ }
129
+ );
130
+ }
131
+
132
+ /**
133
+ * Extract workflow names for enum generation
134
+ * Used by server configuration to build Zod schemas
135
+ */
136
+ export function buildWorkflowEnum(
137
+ workflowNames: string[]
138
+ ): [string, ...string[]] {
139
+ const allWorkflows = [...workflowNames, 'custom'];
140
+
141
+ // Ensure we have at least one element for TypeScript
142
+ if (allWorkflows.length === 0) {
143
+ return ['waterfall'];
144
+ }
145
+
146
+ return allWorkflows as [string, ...string[]];
147
+ }
148
+
149
+ /**
150
+ * Generate workflow description for tool schemas
151
+ */
152
+ export function generateWorkflowDescription(
153
+ workflows: Array<{
154
+ name: string;
155
+ displayName: string;
156
+ description: string;
157
+ metadata?: {
158
+ complexity?: 'low' | 'medium' | 'high';
159
+ bestFor?: string[];
160
+ useCases?: string[];
161
+ examples?: string[];
162
+ };
163
+ }>
164
+ ): string {
165
+ let description = 'Choose your development workflow:\n\n';
166
+
167
+ for (const workflow of workflows) {
168
+ description += `• **${workflow.name}**: ${workflow.displayName} - ${workflow.description}`;
169
+
170
+ // Add enhanced metadata if available
171
+ if (workflow.metadata) {
172
+ const meta = workflow.metadata;
173
+
174
+ // Add complexity
175
+ if (meta.complexity) {
176
+ description += `\n Complexity: ${meta.complexity}`;
177
+ }
178
+
179
+ // Add best for information
180
+ if (meta.bestFor && meta.bestFor.length > 0) {
181
+ description += `\n Best for: ${meta.bestFor.join(', ')}`;
182
+ }
183
+
184
+ // Add examples
185
+ if (meta.examples && meta.examples.length > 0) {
186
+ description += `\n Examples: ${meta.examples.slice(0, 2).join(', ')}`;
187
+ if (meta.examples.length > 2) {
188
+ description += `, and ${meta.examples.length - 2} more`;
189
+ }
190
+ }
191
+ }
192
+
193
+ description += '\n';
194
+ }
195
+
196
+ description +=
197
+ '• **custom**: Use custom workflow from .vibe/workflows in your project\n\n';
198
+
199
+ return description;
200
+ }
201
+
202
+ /**
203
+ * Log handler execution for debugging
204
+ */
205
+ export function logHandlerExecution(handlerName: string, args: unknown): void {
206
+ logger.debug(`Executing ${handlerName} handler`, {
207
+ handlerName,
208
+ argsKeys: Object.keys(args || {}),
209
+ });
210
+ }
211
+
212
+ /**
213
+ * Log handler completion for debugging
214
+ */
215
+ export function logHandlerCompletion(
216
+ handlerName: string,
217
+ result: HandlerResult<unknown>
218
+ ): void {
219
+ logger.debug(`Completed ${handlerName} handler`, {
220
+ handlerName,
221
+ success: result.success,
222
+ hasData: !!result.data,
223
+ hasError: !!result.error,
224
+ });
225
+ }
@@ -0,0 +1,277 @@
1
+ /**
2
+ * Refactored Vibe Feature MCP Server
3
+ *
4
+ * Main server orchestrator that brings together all the modular components.
5
+ * This replaces the monolithic server.ts with a clean, modular architecture.
6
+ */
7
+
8
+ import { setMcpServerForLogging, createLogger } from '@codemcp/workflows-core';
9
+ import { ServerConfig } from './types.js';
10
+ import {
11
+ initializeServerComponents,
12
+ registerMcpTools,
13
+ registerMcpResources,
14
+ ServerComponents,
15
+ } from './server-config.js';
16
+ import { createToolRegistry } from './tool-handlers/index.js';
17
+ import { createResourceRegistry } from './resource-handlers/index.js';
18
+ import { createResponseRenderer } from './response-renderer.js';
19
+ import type {
20
+ ProceedToPhaseArgs,
21
+ ProceedToPhaseResult,
22
+ StartDevelopmentArgs,
23
+ StartDevelopmentResult,
24
+ ResumeWorkflowArgs,
25
+ ResumeWorkflowResult,
26
+ ResetDevelopmentArgs,
27
+ ResetDevelopmentResult,
28
+ } from './tool-handlers/index.js';
29
+
30
+ const logger = createLogger('ResponsibleVibeMCPServer');
31
+
32
+ /**
33
+ * Factory function to create a server with real components
34
+ */
35
+ export async function createResponsibleVibeMCPServer(
36
+ config: ServerConfig = {}
37
+ ): Promise<ResponsibleVibeMCPServer> {
38
+ const components = await initializeServerComponents(config);
39
+ return new ResponsibleVibeMCPServer(config, components);
40
+ }
41
+
42
+ /**
43
+ * Main server class that orchestrates all components
44
+ * Can be used both as a standalone process and in-process for testing
45
+ */
46
+ export class ResponsibleVibeMCPServer {
47
+ private components: ServerComponents | null = null;
48
+
49
+ constructor(
50
+ private config: ServerConfig,
51
+ private serverComponents: ServerComponents
52
+ ) {
53
+ logger.debug('ResponsibleVibeMCPServer created', {
54
+ config: JSON.stringify(this.config),
55
+ });
56
+ }
57
+
58
+ /**
59
+ * Initialize the server and all its components
60
+ */
61
+ async initialize(): Promise<void> {
62
+ logger.debug('Initializing ResponsibleVibeMCPServer');
63
+
64
+ try {
65
+ // Use injected components
66
+ this.components = this.serverComponents;
67
+
68
+ // Create registries and renderer
69
+ const toolRegistry = createToolRegistry();
70
+ const resourceRegistry = createResourceRegistry();
71
+ const responseRenderer = createResponseRenderer();
72
+
73
+ // Update components with registries and renderer
74
+ this.components.toolRegistry = toolRegistry;
75
+ this.components.resourceRegistry = resourceRegistry;
76
+ this.components.responseRenderer = responseRenderer;
77
+
78
+ // Register MCP server for logging notifications
79
+ setMcpServerForLogging(this.components.mcpServer);
80
+
81
+ // Register tools and resources with MCP server
82
+ await registerMcpTools(
83
+ this.components.mcpServer,
84
+ toolRegistry,
85
+ responseRenderer,
86
+ this.components.context
87
+ );
88
+
89
+ registerMcpResources(
90
+ this.components.mcpServer,
91
+ resourceRegistry,
92
+ responseRenderer,
93
+ this.components.context
94
+ );
95
+ } catch (error) {
96
+ logger.error(
97
+ 'Failed to initialize ResponsibleVibeMCPServer',
98
+ error as Error
99
+ );
100
+ throw error;
101
+ }
102
+ }
103
+
104
+ /**
105
+ * Get the underlying MCP server instance
106
+ * This allows for both transport-based and direct testing
107
+ */
108
+ public getMcpServer() {
109
+ if (!this.components) {
110
+ throw new Error('Server not initialized. Call initialize() first.');
111
+ }
112
+ return this.components.mcpServer;
113
+ }
114
+
115
+ /**
116
+ * Get project path
117
+ */
118
+ public getProjectPath(): string {
119
+ if (!this.components) {
120
+ throw new Error('Server not initialized. Call initialize() first.');
121
+ }
122
+ return this.components.context.projectPath;
123
+ }
124
+
125
+ /**
126
+ * Get conversation manager (for testing)
127
+ */
128
+ public getConversationManager() {
129
+ if (!this.components) {
130
+ throw new Error('Server not initialized. Call initialize() first.');
131
+ }
132
+ return this.components.context.conversationManager;
133
+ }
134
+
135
+ /**
136
+ * Get plan manager (for testing)
137
+ */
138
+ public getPlanManager() {
139
+ if (!this.components) {
140
+ throw new Error('Server not initialized. Call initialize() first.');
141
+ }
142
+ return this.components.context.planManager;
143
+ }
144
+
145
+ /**
146
+ * Direct access to tool handlers for testing
147
+ */
148
+ public async handleWhatsNext(args: unknown): Promise<unknown> {
149
+ if (!this.components) {
150
+ throw new Error('Server not initialized. Call initialize() first.');
151
+ }
152
+
153
+ const handler = this.components.toolRegistry.get('whats_next');
154
+ if (!handler) {
155
+ throw new Error('whats_next handler not found');
156
+ }
157
+
158
+ const result = await handler.handle(args, this.components.context);
159
+ if (!result.success) {
160
+ throw new Error(result.error || 'Handler execution failed');
161
+ }
162
+
163
+ return result.data;
164
+ }
165
+
166
+ /**
167
+ * Direct access to tool handlers for testing
168
+ */
169
+ public async handleProceedToPhase(
170
+ args: ProceedToPhaseArgs
171
+ ): Promise<ProceedToPhaseResult> {
172
+ if (!this.components) {
173
+ throw new Error('Server not initialized. Call initialize() first.');
174
+ }
175
+
176
+ const handler = this.components.toolRegistry.get('proceed_to_phase');
177
+ if (!handler) {
178
+ throw new Error('proceed_to_phase handler not found');
179
+ }
180
+
181
+ const result = await handler.handle(args, this.components.context);
182
+ if (!result.success) {
183
+ throw new Error(result.error || 'Handler execution failed');
184
+ }
185
+
186
+ return result.data as ProceedToPhaseResult;
187
+ }
188
+
189
+ /**
190
+ * Direct access to tool handlers for testing
191
+ */
192
+ public async handleStartDevelopment(
193
+ args: StartDevelopmentArgs
194
+ ): Promise<StartDevelopmentResult> {
195
+ if (!this.components) {
196
+ throw new Error('Server not initialized. Call initialize() first.');
197
+ }
198
+
199
+ const handler = this.components.toolRegistry.get('start_development');
200
+ if (!handler) {
201
+ throw new Error('start_development handler not found');
202
+ }
203
+
204
+ const result = await handler.handle(args, this.components.context);
205
+ if (!result.success) {
206
+ throw new Error(result.error || 'Handler execution failed');
207
+ }
208
+
209
+ return result.data as StartDevelopmentResult;
210
+ }
211
+
212
+ /**
213
+ * Direct access to tool handlers for testing
214
+ */
215
+ public async handleResumeWorkflow(
216
+ args: ResumeWorkflowArgs
217
+ ): Promise<ResumeWorkflowResult> {
218
+ if (!this.components) {
219
+ throw new Error('Server not initialized. Call initialize() first.');
220
+ }
221
+
222
+ const handler = this.components.toolRegistry.get('resume_workflow');
223
+ if (!handler) {
224
+ throw new Error('resume_workflow handler not found');
225
+ }
226
+
227
+ const result = await handler.handle(args, this.components.context);
228
+ if (!result.success) {
229
+ throw new Error(result.error || 'Handler execution failed');
230
+ }
231
+
232
+ return result.data as ResumeWorkflowResult;
233
+ }
234
+
235
+ /**
236
+ * Direct access to tool handlers for testing
237
+ */
238
+ public async handleResetDevelopment(
239
+ args: ResetDevelopmentArgs
240
+ ): Promise<ResetDevelopmentResult> {
241
+ if (!this.components) {
242
+ throw new Error('Server not initialized. Call initialize() first.');
243
+ }
244
+
245
+ const handler = this.components.toolRegistry.get('reset_development');
246
+ if (!handler) {
247
+ throw new Error('reset_development handler not found');
248
+ }
249
+
250
+ const result = await handler.handle(args, this.components.context);
251
+ if (!result.success) {
252
+ throw new Error(result.error || 'Handler execution failed');
253
+ }
254
+
255
+ return result.data as ResetDevelopmentResult;
256
+ }
257
+
258
+ /**
259
+ * Cleanup server resources
260
+ */
261
+ public async cleanup(): Promise<void> {
262
+ logger.debug('Cleaning up server resources');
263
+
264
+ if (this.components?.database) {
265
+ await this.components.database.close();
266
+ }
267
+
268
+ logger.info('Server cleanup completed');
269
+ }
270
+ }
271
+
272
+ // Export all types and utilities for external use
273
+ export * from './types.js';
274
+ export * from './server-helpers.js';
275
+ export * from './response-renderer.js';
276
+ export * from './tool-handlers/index.js';
277
+ export * from './resource-handlers/index.js';
package/src/server.ts ADDED
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Vibe Feature MCP Server Core
3
+ */
4
+
5
+ export {
6
+ ResponsibleVibeMCPServer,
7
+ createResponsibleVibeMCPServer,
8
+ } from './server-implementation.js';
9
+ export type { ServerConfig } from './types.js';
@@ -0,0 +1,141 @@
1
+ /**
2
+ * Base Tool Handler
3
+ *
4
+ * Provides common functionality for all tool handlers including
5
+ * error handling, logging, and conversation state management.
6
+ */
7
+
8
+ import { createLogger } from '@codemcp/workflows-core';
9
+ import { ToolHandler, ServerContext, HandlerResult } from '../types.js';
10
+ import type { ConversationContext } from '@codemcp/workflows-core';
11
+ import {
12
+ safeExecute,
13
+ logHandlerExecution,
14
+ logHandlerCompletion,
15
+ } from '../server-helpers.js';
16
+
17
+ /**
18
+ * Abstract base class for tool handlers
19
+ * Provides common functionality and enforces consistent patterns
20
+ */
21
+ export abstract class BaseToolHandler<TArgs = unknown, TResult = unknown>
22
+ implements ToolHandler<TArgs, TResult>
23
+ {
24
+ protected readonly logger: ReturnType<typeof createLogger>;
25
+
26
+ constructor() {
27
+ this.logger = createLogger(this.constructor.name);
28
+ }
29
+
30
+ /**
31
+ * Main handler method - implements consistent error handling and logging
32
+ */
33
+ async handle(
34
+ args: TArgs,
35
+ context: ServerContext
36
+ ): Promise<HandlerResult<TResult>> {
37
+ const handlerName = this.constructor.name;
38
+ logHandlerExecution(handlerName, args);
39
+
40
+ const result = await safeExecute(
41
+ () => this.executeHandler(args, context),
42
+ `${handlerName} execution failed`
43
+ );
44
+
45
+ logHandlerCompletion(handlerName, result);
46
+ return result;
47
+ }
48
+
49
+ /**
50
+ * Abstract method that subclasses must implement
51
+ * Contains the actual business logic for the tool
52
+ */
53
+ protected abstract executeHandler(
54
+ args: TArgs,
55
+ context: ServerContext
56
+ ): Promise<TResult>;
57
+
58
+ /**
59
+ * Helper method to get conversation context with proper error handling
60
+ */
61
+ protected async getConversationContext(context: ServerContext) {
62
+ try {
63
+ return await context.conversationManager.getConversationContext();
64
+ } catch (error) {
65
+ this.logger.info('Conversation not found', { error });
66
+ throw new Error('CONVERSATION_NOT_FOUND');
67
+ }
68
+ }
69
+
70
+ /**
71
+ * Helper method to ensure state machine is loaded for a project
72
+ */
73
+ protected ensureStateMachineForProject(
74
+ context: ServerContext,
75
+ projectPath: string,
76
+ workflowName?: string
77
+ ): void {
78
+ const stateMachine = context.transitionEngine.getStateMachine(
79
+ projectPath,
80
+ workflowName
81
+ );
82
+ context.planManager.setStateMachine(stateMachine);
83
+ context.instructionGenerator.setStateMachine(stateMachine);
84
+ }
85
+
86
+ /**
87
+ * Helper method to log interactions if logger is available
88
+ */
89
+ protected async logInteraction(
90
+ context: ServerContext,
91
+ conversationId: string,
92
+ toolName: string,
93
+ args: unknown,
94
+ response: unknown,
95
+ phase: string
96
+ ): Promise<void> {
97
+ if (context.interactionLogger) {
98
+ await context.interactionLogger.logInteraction(
99
+ conversationId,
100
+ toolName,
101
+ args,
102
+ response,
103
+ phase
104
+ );
105
+ }
106
+ }
107
+ }
108
+
109
+ /**
110
+ * Base class for tool handlers that require an existing conversation
111
+ * Automatically handles the conversation-not-found case
112
+ */
113
+ export abstract class ConversationRequiredToolHandler<
114
+ TArgs = unknown,
115
+ TResult = unknown,
116
+ > extends BaseToolHandler<TArgs, TResult> {
117
+ protected async executeHandler(
118
+ args: TArgs,
119
+ context: ServerContext
120
+ ): Promise<TResult> {
121
+ let conversationContext;
122
+
123
+ try {
124
+ conversationContext = await this.getConversationContext(context);
125
+ } catch (_error) {
126
+ // Return a special error result that the response renderer can handle
127
+ throw new Error('CONVERSATION_NOT_FOUND');
128
+ }
129
+
130
+ return this.executeWithConversation(args, context, conversationContext);
131
+ }
132
+
133
+ /**
134
+ * Abstract method for handlers that need conversation context
135
+ */
136
+ protected abstract executeWithConversation(
137
+ args: TArgs,
138
+ context: ServerContext,
139
+ conversationContext: ConversationContext
140
+ ): Promise<TResult>;
141
+ }