@codemcp/workflows-core 3.1.21 → 3.2.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 (80) hide show
  1. package/package.json +9 -5
  2. package/resources/templates/architecture/arc42/arc42-template-EN.md +1077 -0
  3. package/resources/templates/architecture/arc42/images/01_2_iso-25010-topics-EN.drawio-2023.png +0 -0
  4. package/resources/templates/architecture/arc42/images/01_2_iso-25010-topics-EN.drawio.png +0 -0
  5. package/resources/templates/architecture/arc42/images/05_building_blocks-EN.png +0 -0
  6. package/resources/templates/architecture/arc42/images/08-concepts-EN.drawio.png +0 -0
  7. package/resources/templates/architecture/arc42/images/arc42-logo.png +0 -0
  8. package/resources/templates/architecture/c4.md +224 -0
  9. package/resources/templates/architecture/freestyle.md +53 -0
  10. package/resources/templates/architecture/none.md +17 -0
  11. package/resources/templates/design/comprehensive.md +207 -0
  12. package/resources/templates/design/freestyle.md +37 -0
  13. package/resources/templates/design/none.md +17 -0
  14. package/resources/templates/requirements/ears.md +90 -0
  15. package/resources/templates/requirements/freestyle.md +42 -0
  16. package/resources/templates/requirements/none.md +17 -0
  17. package/resources/workflows/big-bang-conversion.yaml +539 -0
  18. package/resources/workflows/boundary-testing.yaml +334 -0
  19. package/resources/workflows/bugfix.yaml +185 -0
  20. package/resources/workflows/business-analysis.yaml +671 -0
  21. package/resources/workflows/c4-analysis.yaml +485 -0
  22. package/resources/workflows/epcc.yaml +161 -0
  23. package/resources/workflows/greenfield.yaml +189 -0
  24. package/resources/workflows/minor.yaml +127 -0
  25. package/resources/workflows/posts.yaml +207 -0
  26. package/resources/workflows/slides.yaml +256 -0
  27. package/resources/workflows/tdd.yaml +157 -0
  28. package/resources/workflows/waterfall.yaml +195 -0
  29. package/.turbo/turbo-build.log +0 -4
  30. package/src/config-manager.ts +0 -96
  31. package/src/conversation-manager.ts +0 -489
  32. package/src/database.ts +0 -427
  33. package/src/file-detection-manager.ts +0 -302
  34. package/src/git-manager.ts +0 -64
  35. package/src/index.ts +0 -28
  36. package/src/instruction-generator.ts +0 -210
  37. package/src/interaction-logger.ts +0 -109
  38. package/src/logger.ts +0 -353
  39. package/src/path-validation-utils.ts +0 -261
  40. package/src/plan-manager.ts +0 -323
  41. package/src/project-docs-manager.ts +0 -523
  42. package/src/state-machine-loader.ts +0 -365
  43. package/src/state-machine-types.ts +0 -72
  44. package/src/state-machine.ts +0 -370
  45. package/src/system-prompt-generator.ts +0 -122
  46. package/src/template-manager.ts +0 -328
  47. package/src/transition-engine.ts +0 -386
  48. package/src/types.ts +0 -60
  49. package/src/workflow-manager.ts +0 -606
  50. package/test/unit/conversation-manager.test.ts +0 -179
  51. package/test/unit/custom-workflow-loading.test.ts +0 -174
  52. package/test/unit/directory-linking-and-extensions.test.ts +0 -338
  53. package/test/unit/file-linking-integration.test.ts +0 -256
  54. package/test/unit/git-commit-integration.test.ts +0 -91
  55. package/test/unit/git-manager.test.ts +0 -86
  56. package/test/unit/install-workflow.test.ts +0 -138
  57. package/test/unit/instruction-generator.test.ts +0 -247
  58. package/test/unit/list-workflows-filtering.test.ts +0 -68
  59. package/test/unit/none-template-functionality.test.ts +0 -224
  60. package/test/unit/project-docs-manager.test.ts +0 -337
  61. package/test/unit/state-machine-loader.test.ts +0 -234
  62. package/test/unit/template-manager.test.ts +0 -217
  63. package/test/unit/validate-workflow-name.test.ts +0 -150
  64. package/test/unit/workflow-domain-filtering.test.ts +0 -75
  65. package/test/unit/workflow-enum-generation.test.ts +0 -92
  66. package/test/unit/workflow-manager-enhanced-path-resolution.test.ts +0 -369
  67. package/test/unit/workflow-manager-path-resolution.test.ts +0 -150
  68. package/test/unit/workflow-migration.test.ts +0 -155
  69. package/test/unit/workflow-override-by-name.test.ts +0 -116
  70. package/test/unit/workflow-prioritization.test.ts +0 -38
  71. package/test/unit/workflow-validation.test.ts +0 -303
  72. package/test/utils/e2e-test-setup.ts +0 -453
  73. package/test/utils/run-server-in-dir.sh +0 -27
  74. package/test/utils/temp-files.ts +0 -308
  75. package/test/utils/test-access.ts +0 -79
  76. package/test/utils/test-helpers.ts +0 -286
  77. package/test/utils/test-setup.ts +0 -78
  78. package/tsconfig.build.json +0 -21
  79. package/tsconfig.json +0 -8
  80. package/vitest.config.ts +0 -18
@@ -1,453 +0,0 @@
1
- /**
2
- * End-to-End Test Setup Utilities
3
- *
4
- * Provides utilities for testing the MCP server end-to-end without
5
- * spawning separate processes or using transport layers. Tests the
6
- * server's public interface directly for true consumer perspective testing.
7
- */
8
-
9
- import { vi } from 'vitest';
10
- import {
11
- ResponsibleVibeMCPServer,
12
- createResponsibleVibeMCPServer,
13
- ServerConfig,
14
- } from '../../packages/mcp-server/src/server.js';
15
- import { TempProject } from './temp-files.js';
16
- import { join } from 'node:path';
17
- import { tmpdir } from 'node:os';
18
- import { mkdirSync, rmSync, existsSync } from 'node:fs';
19
- import type { ServerContext } from '../../packages/mcp-server/src/types';
20
- import { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
21
-
22
- // Disable fs mocking for E2E tests
23
- vi.unmock('fs');
24
- vi.unmock('fs/promises');
25
-
26
- /**
27
- * Suite-level test isolation manager
28
- * Ensures each test suite gets its own isolated temporary directory
29
- */
30
- export class TestSuiteIsolation {
31
- private static suiteDirectories = new Map<string, string>();
32
- private static cleanupCallbacks = new Map<string, (() => void)[]>();
33
-
34
- /**
35
- * Get or create an isolated directory for a test suite
36
- */
37
- static getSuiteDirectory(suiteName: string): string {
38
- if (!this.suiteDirectories.has(suiteName)) {
39
- // Create unique directory for this suite
40
- const suiteId = `${suiteName.replace(/[^a-zA-Z0-9]/g, '-')}-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
41
- const suiteDir = join(tmpdir(), 'responsible-vibe-e2e-suites', suiteId);
42
-
43
- // Ensure directory exists
44
- mkdirSync(suiteDir, { recursive: true });
45
-
46
- this.suiteDirectories.set(suiteName, suiteDir);
47
- this.cleanupCallbacks.set(suiteName, []);
48
- }
49
-
50
- const directory = this.suiteDirectories.get(suiteName);
51
- if (!directory) {
52
- throw new Error(`Suite directory not found for ${suiteName}`);
53
- }
54
- return directory;
55
- }
56
-
57
- /**
58
- * Register a cleanup callback for a suite
59
- */
60
- static registerCleanup(suiteName: string, cleanup: () => void): void {
61
- const callbacks = this.cleanupCallbacks.get(suiteName) || [];
62
- callbacks.push(cleanup);
63
- this.cleanupCallbacks.set(suiteName, callbacks);
64
- }
65
-
66
- /**
67
- * Clean up all resources for a test suite
68
- */
69
- static async cleanupSuite(suiteName: string): Promise<void> {
70
- // Run all registered cleanup callbacks
71
- const callbacks = this.cleanupCallbacks.get(suiteName) || [];
72
- for (const cleanup of callbacks) {
73
- try {
74
- cleanup();
75
- } catch (error) {
76
- console.warn(`Cleanup callback failed for suite ${suiteName}:`, error);
77
- }
78
- }
79
-
80
- // Remove the suite directory
81
- const suiteDir = this.suiteDirectories.get(suiteName);
82
- if (suiteDir && existsSync(suiteDir)) {
83
- try {
84
- rmSync(suiteDir, { recursive: true, force: true });
85
- } catch (error) {
86
- console.warn(`Failed to remove suite directory ${suiteDir}:`, error);
87
- }
88
- }
89
-
90
- // Clean up tracking
91
- this.suiteDirectories.delete(suiteName);
92
- this.cleanupCallbacks.delete(suiteName);
93
- }
94
-
95
- /**
96
- * Clean up all suites (for global teardown)
97
- */
98
- static async cleanupAll(): Promise<void> {
99
- const suiteNames = Array.from(this.suiteDirectories.keys());
100
- for (const suiteName of suiteNames) {
101
- await this.cleanupSuite(suiteName);
102
- }
103
- }
104
- }
105
-
106
- /**
107
- * E2E test context that provides direct access to MCP server
108
- */
109
- export interface E2ETestContext {
110
- server: ResponsibleVibeMCPServer;
111
- tempProject: TempProject;
112
- cleanup: () => Promise<void>;
113
- }
114
-
115
- /**
116
- * Direct Server Interface
117
- * Provides a consumer-like interface that calls server methods directly
118
- * without going through MCP transport layer
119
- */
120
- export class DirectServerInterface {
121
- constructor(private server: ResponsibleVibeMCPServer) {}
122
-
123
- /**
124
- * Call a tool on the server directly
125
- */
126
- async callTool<T = unknown>(name: string, arguments_: unknown): Promise<T> {
127
- try {
128
- // Call the server's tool handlers directly based on tool name
129
- switch (name) {
130
- case 'whats_next':
131
- return await this.server.handleWhatsNext(arguments_);
132
-
133
- case 'proceed_to_phase':
134
- return await this.server.handleProceedToPhase(arguments_);
135
-
136
- case 'start_development':
137
- return await this.server.handleStartDevelopment(arguments_);
138
-
139
- default:
140
- throw new Error(`Unknown tool: ${name}`);
141
- }
142
- } catch (error) {
143
- // Return errors as objects instead of throwing them
144
- // This matches the expected behavior in tests
145
- return {
146
- error: error instanceof Error ? error.message : String(error),
147
- };
148
- }
149
- }
150
-
151
- /**
152
- * Read a resource from the server directly
153
- */
154
- async readResource<T = unknown>(uri: string): Promise<T> {
155
- // Call the server's resource handlers directly based on URI
156
- switch (uri) {
157
- case 'state://current':
158
- return await this.getConversationState();
159
-
160
- case 'plan://current':
161
- return await this.getDevelopmentPlan();
162
-
163
- case 'system-prompt://':
164
- return await this.getSystemPrompt();
165
-
166
- default:
167
- throw new Error(`Unknown resource URI: ${uri}`);
168
- }
169
- }
170
-
171
- /**
172
- * Get conversation state directly
173
- */
174
- private async getConversationState(): Promise<unknown> {
175
- const conversationManager = this.server.getConversationManager();
176
- const conversationContext =
177
- await conversationManager.getConversationContext();
178
-
179
- const stateData = {
180
- conversationId: conversationContext.conversationId,
181
- currentPhase: conversationContext.currentPhase,
182
- projectPath: conversationContext.projectPath,
183
- timestamp: new Date().toISOString(),
184
- };
185
-
186
- return {
187
- contents: [
188
- {
189
- uri: 'state://current',
190
- mimeType: 'application/json',
191
- text: JSON.stringify(stateData, null, 2),
192
- },
193
- ],
194
- };
195
- }
196
-
197
- /**
198
- * Get development plan directly
199
- */
200
- private async getDevelopmentPlan(): Promise<unknown> {
201
- const conversationManager = this.server.getConversationManager();
202
- const planManager = this.server.getPlanManager();
203
-
204
- const conversationContext =
205
- await conversationManager.getConversationContext();
206
- const planFilePath = conversationContext.planFilePath;
207
-
208
- let planContent: string;
209
- try {
210
- planContent = await planManager.getPlanFileContent(planFilePath);
211
- } catch {
212
- // If plan file doesn't exist yet, return a default message
213
- planContent = `# Development Plan\n\nPlan file will be created when development begins.\n\nConversation ID: ${conversationContext.conversationId}`;
214
- }
215
-
216
- return {
217
- contents: [
218
- {
219
- uri: 'plan://current',
220
- mimeType: 'text/markdown',
221
- text: planContent,
222
- },
223
- ],
224
- };
225
- }
226
-
227
- /**
228
- * Get system prompt resource directly
229
- */
230
- async getSystemPrompt(): Promise<unknown> {
231
- // Use the system prompt handler directly with the default workflow
232
- const { SystemPromptResourceHandler } = await import(
233
- '../../packages/mcp-server/src/resource-handlers/system-prompt.js'
234
- );
235
- const handler = new SystemPromptResourceHandler();
236
-
237
- // Create a minimal context - system prompt doesn't need full server context
238
- const result = await handler.handle(
239
- new URL('system-prompt://'),
240
- {} as ServerContext
241
- );
242
-
243
- return {
244
- contents: [
245
- {
246
- uri: 'system-prompt://',
247
- mimeType: result.mimeType,
248
- text: result.text,
249
- },
250
- ],
251
- };
252
- }
253
-
254
- /**
255
- * List available tools (for testing completeness)
256
- */
257
- async listTools(): Promise<{ tools: { name: string }[] }> {
258
- return {
259
- tools: [{ name: 'whats_next' }, { name: 'proceed_to_phase' }],
260
- };
261
- }
262
-
263
- /**
264
- * List available resources (for testing completeness)
265
- */
266
- async listResources(): Promise<{ resources: { uri: string }[] }> {
267
- return {
268
- resources: [
269
- { uri: 'state://current' },
270
- { uri: 'plan://current' },
271
- { uri: 'system-prompt://' },
272
- ],
273
- };
274
- }
275
-
276
- /**
277
- * Get a prompt (if we add prompt support later)
278
- */
279
- async getPrompt(name: string, arguments_: unknown = {}): Promise<unknown> {
280
- // For now, just return a placeholder
281
- return {
282
- description: `Prompt for ${name}`,
283
- messages: [
284
- {
285
- role: 'user',
286
- content: {
287
- type: 'text',
288
- text: `Prompt: ${name} with args: ${JSON.stringify(arguments_)}`,
289
- },
290
- },
291
- ],
292
- };
293
- }
294
- }
295
-
296
- /**
297
- * Setup end-to-end test environment with real MCP server
298
- */
299
- export async function setupE2ETest(
300
- options: {
301
- tempProject: TempProject;
302
- serverConfig?: Partial<ServerConfig>;
303
- } = {} as {
304
- tempProject: TempProject;
305
- serverConfig?: Partial<ServerConfig>;
306
- }
307
- ): Promise<E2ETestContext> {
308
- const { tempProject, serverConfig = {} } = options;
309
-
310
- // Create server with test configuration
311
- const server = await createResponsibleVibeMCPServer({
312
- projectPath: tempProject.projectPath,
313
- enableLogging: false, // Disable logging in tests
314
- ...serverConfig,
315
- });
316
-
317
- // Initialize server
318
- await server.initialize();
319
-
320
- return {
321
- server,
322
- tempProject,
323
- cleanup: async () => {
324
- await server.cleanup();
325
- },
326
- };
327
- }
328
-
329
- /**
330
- * Create direct server interface for testing
331
- */
332
- export function createDirectServerInterface(
333
- server: ResponsibleVibeMCPServer
334
- ): DirectServerInterface {
335
- return new DirectServerInterface(server);
336
- }
337
-
338
- /**
339
- * Helper to safely parse JSON responses, handling both success and error cases
340
- */
341
- export function parseToolResponse(result: unknown): unknown {
342
- // If result is already an object (direct server call), return as-is
343
- if (typeof result === 'object' && result !== null) {
344
- return result;
345
- }
346
-
347
- // If it's a string, try to parse as JSON
348
- if (typeof result === 'string') {
349
- try {
350
- return JSON.parse(result);
351
- } catch {
352
- return { error: result };
353
- }
354
- }
355
-
356
- return result;
357
- }
358
-
359
- /**
360
- * Assert that a tool call was successful and return the response
361
- */
362
- export function assertToolSuccess(result: unknown): CallToolResult {
363
- // Parse result if it's a string
364
- const parsed = typeof result === 'string' ? JSON.parse(result) : result;
365
-
366
- if (parsed.error) {
367
- throw new Error(`Tool call failed: ${parsed.error}`);
368
- }
369
-
370
- return parsed;
371
- }
372
-
373
- /**
374
- * Initialize development for tests by calling start_development with a workflow
375
- * This must be called before any other tools in tests due to the new requirement
376
- *
377
- * @param client - The DirectServerInterface instance
378
- * @param workflow - The workflow to use (defaults to 'waterfall')
379
- * @param commitBehaviour - The commit behavior to use (defaults to 'none' for tests)
380
- * @returns The response from start_development
381
- */
382
- export async function initializeDevelopment(
383
- client: DirectServerInterface,
384
- workflow: string = 'waterfall',
385
- commitBehaviour: 'step' | 'phase' | 'end' | 'none' = 'none'
386
- ): Promise<unknown> {
387
- const result = await client.callTool('start_development', {
388
- workflow,
389
- commit_behaviour: commitBehaviour,
390
- });
391
- return assertToolSuccess(result);
392
- }
393
-
394
- /**
395
- * Helper to create a complete E2E test scenario
396
- */
397
- export async function createE2EScenario(options: {
398
- tempProject: TempProject;
399
- serverConfig?: Partial<ServerConfig>;
400
- }): Promise<{
401
- client: DirectServerInterface;
402
- server: ResponsibleVibeMCPServer;
403
- tempProject: TempProject;
404
- cleanup: () => Promise<void>;
405
- }> {
406
- const context = await setupE2ETest(options);
407
- const client = createDirectServerInterface(context.server);
408
-
409
- return {
410
- client,
411
- server: context.server,
412
- tempProject: context.tempProject,
413
- cleanup: context.cleanup,
414
- };
415
- }
416
-
417
- /**
418
- * Create a suite-isolated E2E scenario
419
- * Each test suite gets its own isolated temporary directory
420
- */
421
- export async function createSuiteIsolatedE2EScenario(options: {
422
- suiteName: string;
423
- tempProjectFactory: (baseDir?: string) => TempProject;
424
- serverConfig?: Partial<ServerConfig>;
425
- }): Promise<{
426
- client: DirectServerInterface;
427
- server: ResponsibleVibeMCPServer;
428
- tempProject: TempProject;
429
- cleanup: () => Promise<void>;
430
- }> {
431
- const { suiteName, tempProjectFactory, serverConfig = {} } = options;
432
-
433
- // Get suite-isolated directory
434
- const suiteDir = TestSuiteIsolation.getSuiteDirectory(suiteName);
435
-
436
- // Create temp project in the suite directory
437
- const tempProject = tempProjectFactory(suiteDir);
438
-
439
- // Register temp project cleanup with the suite
440
- TestSuiteIsolation.registerCleanup(suiteName, () => {
441
- tempProject.cleanup();
442
- });
443
-
444
- const context = await setupE2ETest({ tempProject, serverConfig });
445
- const client = createDirectServerInterface(context.server);
446
-
447
- return {
448
- client,
449
- server: context.server,
450
- tempProject: context.tempProject,
451
- cleanup: context.cleanup,
452
- };
453
- }
@@ -1,27 +0,0 @@
1
- #!/bin/bash
2
-
3
- # Server Runner Script
4
- #
5
- # This script changes the current working directory to the specified path
6
- # before running the server. This ensures that the server operates in the
7
- # correct directory context for testing.
8
- #
9
- # This is necessary because the server uses process.cwd() to determine the project path,
10
- # and the StdioClientTransport's cwd option doesn't properly isolate the server process.
11
- # By explicitly changing directory in a wrapper script, we ensure the server operates
12
- # on a clean test directory rather than the current project directory.
13
-
14
- # Get the target directory from command line arguments
15
- TARGET_DIR="$1"
16
- SERVER_PATH="$2"
17
-
18
- if [ -z "$TARGET_DIR" ] || [ -z "$SERVER_PATH" ]; then
19
- echo "Usage: ./run-server-in-dir.sh <target-directory> <server-path>"
20
- exit 1
21
- fi
22
-
23
- # Change to the target directory
24
- cd "$TARGET_DIR" || exit 1
25
-
26
- # Run the server
27
- node "$SERVER_PATH"