@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
package/src/types.ts DELETED
@@ -1,60 +0,0 @@
1
- /**
2
- * Common types used across the application
3
- */
4
-
5
- /**
6
- * Interface for interaction log entries
7
- */
8
- export interface InteractionLog {
9
- id?: number;
10
- conversationId: string;
11
- toolName: string;
12
- inputParams: string;
13
- responseData: string;
14
- currentPhase: string;
15
- timestamp: string;
16
- isReset?: boolean;
17
- resetAt?: string;
18
- }
19
-
20
- /**
21
- * Interface for conversation state
22
- */
23
- /**
24
- * Git commit configuration options
25
- */
26
- export interface GitCommitConfig {
27
- enabled: boolean;
28
- commitOnStep: boolean; // Commit after each step (before whats_next)
29
- commitOnPhase: boolean; // Commit after each phase (before phase transition)
30
- commitOnComplete: boolean; // Final commit at development end with rebase+squash
31
- initialMessage: string; // Initial user message for commit context
32
- startCommitHash?: string; // Hash of commit when development started (for squashing)
33
- }
34
-
35
- export interface ConversationState {
36
- conversationId: string;
37
- projectPath: string;
38
- gitBranch: string;
39
- currentPhase: string;
40
- planFilePath: string;
41
- workflowName: string;
42
- gitCommitConfig?: GitCommitConfig;
43
- requireReviewsBeforePhaseTransition: boolean;
44
- createdAt: string;
45
- updatedAt: string;
46
- }
47
-
48
- /**
49
- * Interface for conversation context
50
- */
51
- export interface ConversationContext {
52
- conversationId: string;
53
- projectPath: string;
54
- gitBranch: string;
55
- currentPhase: string;
56
- planFilePath: string;
57
- workflowName: string;
58
- gitCommitConfig?: GitCommitConfig;
59
- requireReviewsBeforePhaseTransition?: boolean;
60
- }
@@ -1,606 +0,0 @@
1
- /**
2
- * Workflow Manager
3
- *
4
- * Manages multiple predefined workflows and provides workflow discovery and selection
5
- */
6
-
7
- import fs from 'node:fs';
8
- import path from 'node:path';
9
- import { createRequire } from 'node:module';
10
- import { fileURLToPath } from 'node:url';
11
- import { createLogger } from './logger.js';
12
- import { StateMachineLoader } from './state-machine-loader.js';
13
- import { YamlStateMachine } from './state-machine-types.js';
14
- import { ConfigManager } from './config-manager.js';
15
-
16
- const logger = createLogger('WorkflowManager');
17
-
18
- export interface WorkflowInfo {
19
- name: string;
20
- displayName: string;
21
- description: string;
22
- initialState: string;
23
- phases: string[];
24
- // Enhanced metadata for better discoverability
25
- metadata?: {
26
- domain?: string;
27
- complexity?: 'low' | 'medium' | 'high';
28
- bestFor?: string[];
29
- useCases?: string[];
30
- examples?: string[];
31
- };
32
- }
33
-
34
- /**
35
- * Manages predefined workflows and provides workflow discovery
36
- */
37
- export class WorkflowManager {
38
- private predefinedWorkflows: Map<string, YamlStateMachine> = new Map();
39
- private projectWorkflows: Map<string, YamlStateMachine> = new Map();
40
- private workflowInfos: Map<string, WorkflowInfo> = new Map();
41
- private stateMachineLoader: StateMachineLoader;
42
- private lastProjectPath: string | null = null; // Track last loaded project path
43
- private enabledDomains: Set<string>;
44
-
45
- constructor() {
46
- this.stateMachineLoader = new StateMachineLoader();
47
- this.enabledDomains = this.parseEnabledDomains();
48
- this.loadPredefinedWorkflows();
49
- }
50
-
51
- /**
52
- * Parse enabled domains from environment variable
53
- */
54
- private parseEnabledDomains(): Set<string> {
55
- const domainsEnv = process.env.VIBE_WORKFLOW_DOMAINS;
56
- if (!domainsEnv) {
57
- return new Set(['code']);
58
- }
59
-
60
- return new Set(
61
- domainsEnv
62
- .split(',')
63
- .map(d => d.trim())
64
- .filter(d => d)
65
- );
66
- }
67
-
68
- /**
69
- * Load project-specific workflows from .vibe/workflows/
70
- */
71
- public loadProjectWorkflows(projectPath: string): void {
72
- // Clear project workflows cache if project path changed
73
- if (this.lastProjectPath !== projectPath) {
74
- this.projectWorkflows.clear();
75
- this.lastProjectPath = projectPath;
76
- }
77
-
78
- // First, migrate any legacy workflow files
79
- this.migrateLegacyWorkflow(projectPath);
80
-
81
- const workflowsDir = path.join(projectPath, '.vibe', 'workflows');
82
-
83
- if (!fs.existsSync(workflowsDir)) {
84
- return;
85
- }
86
-
87
- try {
88
- const files = fs.readdirSync(workflowsDir);
89
- const yamlFiles = files.filter(
90
- file => file.endsWith('.yaml') || file.endsWith('.yml')
91
- );
92
-
93
- for (const file of yamlFiles) {
94
- try {
95
- const filePath = path.join(workflowsDir, file);
96
- const workflow = this.stateMachineLoader.loadFromFile(filePath);
97
- const workflowName = workflow.name; // Use name from YAML, not filename
98
-
99
- // Project workflows are always loaded (no domain filtering)
100
- this.projectWorkflows.set(workflowName, workflow);
101
-
102
- const workflowInfo: WorkflowInfo = {
103
- name: workflowName,
104
- displayName: workflow.name,
105
- description: workflow.description,
106
- initialState: workflow.initial_state,
107
- phases: Object.keys(workflow.states),
108
- metadata: workflow.metadata,
109
- };
110
-
111
- this.workflowInfos.set(workflowName, workflowInfo);
112
-
113
- logger.info('Loaded project workflow', {
114
- name: workflowName,
115
- domain: workflow.metadata?.domain,
116
- });
117
- } catch (error) {
118
- logger.error('Failed to load project workflow', error as Error, {
119
- file,
120
- });
121
- }
122
- }
123
- } catch (error) {
124
- logger.error(
125
- 'Failed to scan project workflows directory',
126
- error as Error,
127
- { workflowsDir }
128
- );
129
- }
130
- }
131
-
132
- /**
133
- * Migrate legacy workflow.yaml to new workflows directory
134
- */
135
- private migrateLegacyWorkflow(projectPath: string): void {
136
- const legacyPaths = [
137
- path.join(projectPath, '.vibe', 'workflow.yaml'),
138
- path.join(projectPath, '.vibe', 'workflow.yml'),
139
- ];
140
-
141
- const workflowsDir = path.join(projectPath, '.vibe', 'workflows');
142
- const targetPath = path.join(workflowsDir, 'custom.yaml');
143
-
144
- for (const legacyPath of legacyPaths) {
145
- if (fs.existsSync(legacyPath) && !fs.existsSync(targetPath)) {
146
- try {
147
- // Create workflows directory if it doesn't exist
148
- if (!fs.existsSync(workflowsDir)) {
149
- fs.mkdirSync(workflowsDir, { recursive: true });
150
- }
151
-
152
- // Copy the file to new location
153
- fs.copyFileSync(legacyPath, targetPath);
154
-
155
- // Remove the old file
156
- fs.unlinkSync(legacyPath);
157
-
158
- logger.info('Migrated legacy workflow to new location', {
159
- from: legacyPath,
160
- to: targetPath,
161
- });
162
- break;
163
- } catch (_error) {
164
- logger.error('Failed to migrate legacy workflow');
165
- }
166
- }
167
- }
168
- }
169
- /**
170
- * Get all available workflows regardless of domain filtering
171
- */
172
- public getAllAvailableWorkflows(): WorkflowInfo[] {
173
- // Create a temporary manager with all domains enabled
174
- const originalEnv = process.env.VIBE_WORKFLOW_DOMAINS;
175
- process.env.VIBE_WORKFLOW_DOMAINS = 'code,architecture,office';
176
-
177
- try {
178
- const tempManager = new WorkflowManager();
179
- return tempManager.getAvailableWorkflows();
180
- } finally {
181
- if (originalEnv !== undefined) {
182
- process.env.VIBE_WORKFLOW_DOMAINS = originalEnv;
183
- } else {
184
- delete process.env.VIBE_WORKFLOW_DOMAINS;
185
- }
186
- }
187
- }
188
-
189
- public getAvailableWorkflows(): WorkflowInfo[] {
190
- return Array.from(this.workflowInfos.values());
191
- }
192
-
193
- /**
194
- * Get available workflows for a specific project
195
- * Applies configuration filtering and loads project-specific workflows
196
- */
197
- public getAvailableWorkflowsForProject(projectPath: string): WorkflowInfo[] {
198
- // Load project workflows first
199
- this.loadProjectWorkflows(projectPath);
200
-
201
- const allWorkflows = this.getAvailableWorkflows();
202
-
203
- // Load project configuration
204
- const config = ConfigManager.loadProjectConfig(projectPath);
205
-
206
- // Apply configuration filtering if enabled_workflows is specified
207
- let filteredWorkflows = allWorkflows;
208
- if (config?.enabled_workflows) {
209
- // Validate that all configured workflows exist
210
- for (const workflowName of config.enabled_workflows) {
211
- if (
212
- workflowName !== 'custom' &&
213
- !this.isPredefinedWorkflow(workflowName)
214
- ) {
215
- throw new Error(
216
- `Invalid workflow '${workflowName}' in configuration. Available workflows: ${this.getWorkflowNames().join(', ')}, custom`
217
- );
218
- }
219
- }
220
-
221
- // Filter to only enabled workflows
222
- filteredWorkflows = allWorkflows.filter(
223
- w => config.enabled_workflows?.includes(w.name) ?? false
224
- );
225
- }
226
-
227
- // Handle custom workflow (only if custom is in enabled list or no config)
228
- const customEnabled =
229
- !config?.enabled_workflows || config.enabled_workflows.includes('custom');
230
- if (customEnabled) {
231
- const hasCustomWorkflow = this.validateWorkflowName(
232
- 'custom',
233
- projectPath
234
- );
235
- if (hasCustomWorkflow) {
236
- // Add custom workflow to the list if it exists and is enabled
237
- const customWorkflowInfo: WorkflowInfo = {
238
- name: 'custom',
239
- displayName: 'Custom Workflow',
240
- description: 'Project-specific custom workflow',
241
- initialState: 'unknown', // Will be determined when loaded
242
- phases: [], // Will be determined when loaded
243
- };
244
- filteredWorkflows.push(customWorkflowInfo);
245
- }
246
- }
247
-
248
- return filteredWorkflows;
249
- }
250
-
251
- /**
252
- * Get workflow information by name
253
- */
254
- public getWorkflowInfo(name: string): WorkflowInfo | undefined {
255
- return this.workflowInfos.get(name);
256
- }
257
-
258
- /**
259
- * Get a specific workflow by name (checks both predefined and project workflows)
260
- */
261
- public getWorkflow(name: string): YamlStateMachine | undefined {
262
- return (
263
- this.projectWorkflows.get(name) || this.predefinedWorkflows.get(name)
264
- );
265
- }
266
-
267
- /**
268
- * Check if a workflow name is a predefined workflow
269
- */
270
- public isPredefinedWorkflow(name: string): boolean {
271
- return this.predefinedWorkflows.has(name);
272
- }
273
-
274
- /**
275
- * Get workflow names as enum values for tool schema
276
- * Includes both predefined and project workflows
277
- */
278
- public getWorkflowNames(): string[] {
279
- const predefinedNames = Array.from(this.predefinedWorkflows.keys());
280
- const projectNames = Array.from(this.projectWorkflows.keys());
281
-
282
- // Combine and deduplicate (project workflows override predefined ones)
283
- const allNames = [...predefinedNames];
284
- for (const projectName of projectNames) {
285
- if (!allNames.includes(projectName)) {
286
- allNames.push(projectName);
287
- }
288
- }
289
-
290
- return allNames;
291
- }
292
-
293
- /**
294
- * Load a workflow (predefined or custom) for a project
295
- * FIXED: Now respects the workflow parameter correctly
296
- */
297
- public loadWorkflowForProject(
298
- projectPath: string,
299
- workflowName?: string
300
- ): YamlStateMachine {
301
- // Load project workflows first
302
- this.loadProjectWorkflows(projectPath);
303
-
304
- // If no workflow specified, use first available workflow
305
- if (!workflowName) {
306
- const availableWorkflows =
307
- this.getAvailableWorkflowsForProject(projectPath);
308
- if (availableWorkflows.length === 0) {
309
- throw new Error(
310
- 'No workflows available. Please install a workflow or adjust VIBE_WORKFLOW_DOMAINS environment variable.'
311
- );
312
- }
313
- workflowName = availableWorkflows[0].name;
314
- }
315
-
316
- // If it's a predefined workflow, return it
317
- if (this.isPredefinedWorkflow(workflowName)) {
318
- const workflow = this.getWorkflow(workflowName);
319
- if (workflow) {
320
- logger.info('Loading predefined workflow', { workflowName });
321
- return workflow;
322
- }
323
- }
324
-
325
- // Check project workflows
326
- if (this.projectWorkflows.has(workflowName)) {
327
- const workflow = this.projectWorkflows.get(workflowName);
328
- if (workflow) {
329
- logger.info('Loading project workflow', { workflowName });
330
- return workflow;
331
- }
332
- }
333
-
334
- throw new Error(`Unknown workflow: ${workflowName}`);
335
- }
336
-
337
- /**
338
- * Find the workflows directory using multiple strategies
339
- * This handles both development and npm package deployment scenarios
340
- */
341
- private findWorkflowsDirectory(): string | null {
342
- const currentFileUrl = import.meta.url;
343
- const currentFilePath = fileURLToPath(currentFileUrl);
344
- const strategies: string[] = [];
345
-
346
- // Strategy 1: Local resources directory (symlinked from root)
347
- strategies.push(
348
- path.join(path.dirname(currentFilePath), '../resources/workflows')
349
- );
350
-
351
- // Strategy 2: Relative to current file (development and direct npm scenarios)
352
- // From packages/core/dist/workflow-manager.js -> ../../../../resources/workflows
353
- strategies.push(
354
- path.resolve(
355
- path.dirname(currentFilePath),
356
- '../../../../resources/workflows'
357
- )
358
- );
359
-
360
- // Strategy 3: Find package root by looking for package.json with our package name
361
- let currentDir = path.dirname(currentFilePath);
362
- for (let i = 0; i < 10; i++) {
363
- // Limit search depth
364
- const packageJsonPath = path.join(currentDir, 'package.json');
365
- if (fs.existsSync(packageJsonPath)) {
366
- try {
367
- const packageJson = JSON.parse(
368
- fs.readFileSync(packageJsonPath, 'utf-8')
369
- );
370
- if (packageJson.name === '@codemcp/workflows-core') {
371
- strategies.push(path.join(currentDir, 'resources/workflows'));
372
- break;
373
- }
374
- } catch (_error) {
375
- // Ignore JSON parse errors and continue searching
376
- }
377
- }
378
- const parentDir = path.dirname(currentDir);
379
- if (parentDir === currentDir) break; // Reached filesystem root
380
- currentDir = parentDir;
381
- }
382
-
383
- // Strategy 3: Common npm installation paths
384
- // Local node_modules (when used as dependency)
385
- strategies.push(
386
- path.join(
387
- process.cwd(),
388
- 'node_modules/@codemcp/workflows-core/resources/workflows'
389
- )
390
- );
391
-
392
- // Global npm installation (when installed globally)
393
- if (process.env.NODE_PATH) {
394
- strategies.push(
395
- path.join(
396
- process.env.NODE_PATH,
397
- '@codemcp/workflows-core/resources/workflows'
398
- )
399
- );
400
- }
401
-
402
- // Strategy 4: npx cache locations (for npx responsible-vibe-mcp@latest)
403
- // npx typically caches packages in ~/.npm/_npx or similar locations
404
- const homeDir = process.env.HOME || process.env.USERPROFILE;
405
- if (homeDir) {
406
- // Common npx cache locations
407
- const npxCachePaths = [
408
- path.join(homeDir, '.npm/_npx'),
409
- path.join(homeDir, '.npm/_cacache'),
410
- path.join(homeDir, 'AppData/Local/npm-cache/_npx'), // Windows
411
- path.join(homeDir, 'Library/Caches/npm/_npx'), // macOS
412
- ];
413
-
414
- for (const cachePath of npxCachePaths) {
415
- if (fs.existsSync(cachePath)) {
416
- try {
417
- // Look for responsible-vibe-mcp in cache subdirectories
418
- const cacheEntries = fs.readdirSync(cachePath);
419
- for (const entry of cacheEntries) {
420
- const entryPath = path.join(cachePath, entry);
421
- if (fs.statSync(entryPath).isDirectory()) {
422
- // Look for our package in this cache entry
423
- const possiblePaths = [
424
- path.join(
425
- entryPath,
426
- 'node_modules/@codemcp/workflows-core/resources/workflows'
427
- ),
428
- path.join(
429
- entryPath,
430
- '@codemcp/workflows-core/resources/workflows'
431
- ),
432
- ];
433
- strategies.push(...possiblePaths);
434
- }
435
- }
436
- } catch (_error) {
437
- // Ignore errors reading cache directories
438
- }
439
- }
440
- }
441
- }
442
-
443
- // Strategy 5: Look in the directory where the current executable is located
444
- // This handles cases where npx runs the package from a temporary location
445
- const executableDir = path.dirname(process.argv[1] || '');
446
- if (executableDir) {
447
- strategies.push(path.join(executableDir, '../resources/workflows'));
448
- strategies.push(path.join(executableDir, 'resources/workflows'));
449
- }
450
-
451
- // Strategy 6: Use require.resolve to find the package location
452
- try {
453
- // Try to resolve the package.json of our own package
454
- const require = createRequire(import.meta.url);
455
- const packagePath = require.resolve('responsible-vibe-mcp/package.json');
456
- const packageDir = path.dirname(packagePath);
457
- strategies.push(path.join(packageDir, 'resources/workflows'));
458
- } catch (_error) {
459
- // require.resolve might fail in some environments, that's okay
460
- }
461
-
462
- // Remove duplicates and invalid paths
463
- const uniqueStrategies = [...new Set(strategies)].filter(
464
- p => p.trim() !== '/resources/workflows'
465
- );
466
-
467
- // Test each strategy
468
- for (const workflowsDir of uniqueStrategies) {
469
- logger.debug('Trying workflows directory', { workflowsDir });
470
- if (fs.existsSync(workflowsDir)) {
471
- // Verify it contains workflow files
472
- try {
473
- const files = fs.readdirSync(workflowsDir);
474
- const yamlFiles = files.filter(
475
- file => file.endsWith('.yaml') || file.endsWith('.yml')
476
- );
477
- if (yamlFiles.length > 0) {
478
- logger.info('Found workflows directory', {
479
- workflowsDir,
480
- yamlFiles: yamlFiles.length,
481
- });
482
- return workflowsDir;
483
- }
484
- } catch (error) {
485
- // Directory exists but can't read it, continue to next strategy
486
- logger.debug('Cannot read workflows directory', {
487
- workflowsDir,
488
- error,
489
- });
490
- }
491
- }
492
- }
493
-
494
- logger.error(
495
- 'Could not find workflows directory',
496
- new Error('Workflows directory not found'),
497
- {
498
- strategiesCount: uniqueStrategies.length,
499
- currentFilePath,
500
- strategies: uniqueStrategies,
501
- }
502
- );
503
- return null;
504
- }
505
-
506
- /**
507
- * Load all predefined workflows from resources/workflows directory
508
- */
509
- private loadPredefinedWorkflows(): void {
510
- try {
511
- const workflowsDir = this.findWorkflowsDirectory();
512
-
513
- if (!workflowsDir || !fs.existsSync(workflowsDir)) {
514
- logger.warn('Workflows directory not found', { workflowsDir });
515
- return;
516
- }
517
-
518
- // Read all YAML files in the workflows directory
519
- const files = fs.readdirSync(workflowsDir);
520
- const yamlFiles = files.filter(
521
- file => file.endsWith('.yaml') || file.endsWith('.yml')
522
- );
523
-
524
- logger.info('Loading predefined workflows', {
525
- workflowsDir,
526
- yamlFiles: yamlFiles.length,
527
- });
528
-
529
- for (const file of yamlFiles) {
530
- try {
531
- const filePath = path.join(workflowsDir, file);
532
- const workflow = this.stateMachineLoader.loadFromFile(filePath);
533
- const workflowName = path.basename(file, path.extname(file));
534
-
535
- // Apply domain filtering
536
- if (this.enabledDomains.size > 0 && workflow.metadata?.domain) {
537
- if (!this.enabledDomains.has(workflow.metadata.domain)) {
538
- logger.debug('Skipping workflow due to domain filter', {
539
- name: workflowName,
540
- domain: workflow.metadata.domain,
541
- enabledDomains: Array.from(this.enabledDomains),
542
- });
543
- continue;
544
- }
545
- }
546
-
547
- this.predefinedWorkflows.set(workflowName, workflow);
548
-
549
- const workflowInfo: WorkflowInfo = {
550
- name: workflowName,
551
- displayName: workflow.name,
552
- description: workflow.description,
553
- initialState: workflow.initial_state,
554
- phases: Object.keys(workflow.states),
555
- metadata: workflow.metadata,
556
- };
557
-
558
- this.workflowInfos.set(workflowName, workflowInfo);
559
-
560
- logger.info('Loaded predefined workflow', {
561
- name: workflowName,
562
- domain: workflow.metadata?.domain,
563
- phases: workflowInfo.phases.length,
564
- });
565
- } catch (error) {
566
- logger.error('Failed to load workflow file', error as Error, {
567
- file,
568
- });
569
- }
570
- }
571
-
572
- logger.info('Predefined workflows loaded', {
573
- count: this.predefinedWorkflows.size,
574
- workflows: Array.from(this.predefinedWorkflows.keys()),
575
- });
576
- } catch (error) {
577
- logger.error('Failed to load predefined workflows', error as Error);
578
- }
579
- }
580
-
581
- /**
582
- * Validate a workflow name
583
- */
584
- public validateWorkflowName(
585
- workflowName: string,
586
- projectPath: string
587
- ): boolean {
588
- // Check if it's a predefined workflow
589
- if (this.isPredefinedWorkflow(workflowName)) {
590
- return true;
591
- }
592
-
593
- // Check if it's a project workflow (load project workflows first)
594
- this.loadProjectWorkflows(projectPath);
595
- if (this.projectWorkflows.has(workflowName)) {
596
- return true;
597
- }
598
-
599
- // Also check workflow infos in case workflow failed to load but info was created
600
- if (this.workflowInfos.has(workflowName)) {
601
- return true;
602
- }
603
-
604
- return false;
605
- }
606
- }