@codemcp/workflows-core 3.1.16

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 (114) hide show
  1. package/.turbo/turbo-build.log +4 -0
  2. package/LICENSE +674 -0
  3. package/dist/config-manager.d.ts +24 -0
  4. package/dist/config-manager.js +68 -0
  5. package/dist/config-manager.js.map +1 -0
  6. package/dist/conversation-manager.d.ts +97 -0
  7. package/dist/conversation-manager.js +367 -0
  8. package/dist/conversation-manager.js.map +1 -0
  9. package/dist/database.d.ts +73 -0
  10. package/dist/database.js +500 -0
  11. package/dist/database.js.map +1 -0
  12. package/dist/file-detection-manager.d.ts +53 -0
  13. package/dist/file-detection-manager.js +221 -0
  14. package/dist/file-detection-manager.js.map +1 -0
  15. package/dist/git-manager.d.ts +14 -0
  16. package/dist/git-manager.js +59 -0
  17. package/dist/git-manager.js.map +1 -0
  18. package/dist/index.d.ts +19 -0
  19. package/dist/index.js +25 -0
  20. package/dist/index.js.map +1 -0
  21. package/dist/instruction-generator.d.ts +69 -0
  22. package/dist/instruction-generator.js +133 -0
  23. package/dist/instruction-generator.js.map +1 -0
  24. package/dist/interaction-logger.d.ts +37 -0
  25. package/dist/interaction-logger.js +87 -0
  26. package/dist/interaction-logger.js.map +1 -0
  27. package/dist/logger.d.ts +64 -0
  28. package/dist/logger.js +283 -0
  29. package/dist/logger.js.map +1 -0
  30. package/dist/path-validation-utils.d.ts +51 -0
  31. package/dist/path-validation-utils.js +202 -0
  32. package/dist/path-validation-utils.js.map +1 -0
  33. package/dist/plan-manager.d.ts +65 -0
  34. package/dist/plan-manager.js +256 -0
  35. package/dist/plan-manager.js.map +1 -0
  36. package/dist/project-docs-manager.d.ts +119 -0
  37. package/dist/project-docs-manager.js +357 -0
  38. package/dist/project-docs-manager.js.map +1 -0
  39. package/dist/state-machine-loader.d.ts +60 -0
  40. package/dist/state-machine-loader.js +235 -0
  41. package/dist/state-machine-loader.js.map +1 -0
  42. package/dist/state-machine-types.d.ts +58 -0
  43. package/dist/state-machine-types.js +7 -0
  44. package/dist/state-machine-types.js.map +1 -0
  45. package/dist/state-machine.d.ts +52 -0
  46. package/dist/state-machine.js +256 -0
  47. package/dist/state-machine.js.map +1 -0
  48. package/dist/system-prompt-generator.d.ts +17 -0
  49. package/dist/system-prompt-generator.js +113 -0
  50. package/dist/system-prompt-generator.js.map +1 -0
  51. package/dist/template-manager.d.ts +61 -0
  52. package/dist/template-manager.js +229 -0
  53. package/dist/template-manager.js.map +1 -0
  54. package/dist/transition-engine.d.ts +70 -0
  55. package/dist/transition-engine.js +240 -0
  56. package/dist/transition-engine.js.map +1 -0
  57. package/dist/types.d.ts +56 -0
  58. package/dist/types.js +5 -0
  59. package/dist/types.js.map +1 -0
  60. package/dist/workflow-manager.d.ts +89 -0
  61. package/dist/workflow-manager.js +466 -0
  62. package/dist/workflow-manager.js.map +1 -0
  63. package/package.json +27 -0
  64. package/src/config-manager.ts +96 -0
  65. package/src/conversation-manager.ts +492 -0
  66. package/src/database.ts +685 -0
  67. package/src/file-detection-manager.ts +302 -0
  68. package/src/git-manager.ts +64 -0
  69. package/src/index.ts +28 -0
  70. package/src/instruction-generator.ts +210 -0
  71. package/src/interaction-logger.ts +109 -0
  72. package/src/logger.ts +353 -0
  73. package/src/path-validation-utils.ts +261 -0
  74. package/src/plan-manager.ts +323 -0
  75. package/src/project-docs-manager.ts +522 -0
  76. package/src/state-machine-loader.ts +308 -0
  77. package/src/state-machine-types.ts +72 -0
  78. package/src/state-machine.ts +370 -0
  79. package/src/system-prompt-generator.ts +122 -0
  80. package/src/template-manager.ts +321 -0
  81. package/src/transition-engine.ts +386 -0
  82. package/src/types.ts +60 -0
  83. package/src/workflow-manager.ts +601 -0
  84. package/test/unit/conversation-manager.test.ts +179 -0
  85. package/test/unit/custom-workflow-loading.test.ts +174 -0
  86. package/test/unit/directory-linking-and-extensions.test.ts +338 -0
  87. package/test/unit/file-linking-integration.test.ts +256 -0
  88. package/test/unit/git-commit-integration.test.ts +91 -0
  89. package/test/unit/git-manager.test.ts +86 -0
  90. package/test/unit/install-workflow.test.ts +138 -0
  91. package/test/unit/instruction-generator.test.ts +247 -0
  92. package/test/unit/list-workflows-filtering.test.ts +68 -0
  93. package/test/unit/none-template-functionality.test.ts +224 -0
  94. package/test/unit/project-docs-manager.test.ts +337 -0
  95. package/test/unit/state-machine-loader.test.ts +234 -0
  96. package/test/unit/template-manager.test.ts +217 -0
  97. package/test/unit/validate-workflow-name.test.ts +150 -0
  98. package/test/unit/workflow-domain-filtering.test.ts +75 -0
  99. package/test/unit/workflow-enum-generation.test.ts +92 -0
  100. package/test/unit/workflow-manager-enhanced-path-resolution.test.ts +369 -0
  101. package/test/unit/workflow-manager-path-resolution.test.ts +150 -0
  102. package/test/unit/workflow-migration.test.ts +155 -0
  103. package/test/unit/workflow-override-by-name.test.ts +116 -0
  104. package/test/unit/workflow-prioritization.test.ts +38 -0
  105. package/test/unit/workflow-validation.test.ts +303 -0
  106. package/test/utils/e2e-test-setup.ts +453 -0
  107. package/test/utils/run-server-in-dir.sh +27 -0
  108. package/test/utils/temp-files.ts +308 -0
  109. package/test/utils/test-access.ts +79 -0
  110. package/test/utils/test-helpers.ts +286 -0
  111. package/test/utils/test-setup.ts +78 -0
  112. package/tsconfig.build.json +21 -0
  113. package/tsconfig.json +8 -0
  114. package/vitest.config.ts +18 -0
@@ -0,0 +1,308 @@
1
+ /**
2
+ * State Machine Loader
3
+ *
4
+ * Loads and validates YAML-based state machine definitions
5
+ */
6
+
7
+ import fs from 'node:fs';
8
+ import yaml from 'js-yaml';
9
+ import path from 'node:path';
10
+ import { fileURLToPath } from 'node:url';
11
+ import { createLogger } from './logger.js';
12
+ import { YamlStateMachine, YamlTransition } from './state-machine-types.js';
13
+
14
+ const logger = createLogger('StateMachineLoader');
15
+
16
+ /**
17
+ * Loads and manages YAML-based state machine definitions
18
+ */
19
+ export class StateMachineLoader {
20
+ private stateMachine: YamlStateMachine | null = null;
21
+ private validPhases: Set<string> = new Set();
22
+
23
+ /**
24
+ * Get all valid phases from the loaded state machine
25
+ */
26
+ public getValidPhases(): string[] {
27
+ if (!this.stateMachine) {
28
+ throw new Error('State machine not loaded');
29
+ }
30
+ return Array.from(this.validPhases);
31
+ }
32
+
33
+ /**
34
+ * Get the initial state from the loaded state machine
35
+ */
36
+ public getInitialState(): string {
37
+ if (!this.stateMachine) {
38
+ throw new Error('State machine not loaded');
39
+ }
40
+ return this.stateMachine.initial_state;
41
+ }
42
+
43
+ /**
44
+ * Load state machine from YAML file
45
+ *
46
+ * Checks for custom state machine file in project directory first,
47
+ * then falls back to waterfall workflow as default
48
+ */
49
+ public loadStateMachine(projectPath: string): YamlStateMachine {
50
+ // Check for custom state machine file in project directory
51
+ const customFilePaths = [
52
+ path.join(projectPath, '.vibe', 'workflow.yaml'),
53
+ path.join(projectPath, '.vibe', 'workflow.yml'),
54
+ ];
55
+
56
+ // Try to load custom state machine file
57
+ for (const filePath of customFilePaths) {
58
+ if (fs.existsSync(filePath)) {
59
+ logger.info('Loading custom state machine file', { filePath });
60
+ try {
61
+ return this.loadFromFile(filePath);
62
+ } catch (error) {
63
+ logger.warn(
64
+ 'Failed to load custom state machine, falling back to default',
65
+ {
66
+ filePath,
67
+ error: (error as Error).message,
68
+ }
69
+ );
70
+ // Continue to try next file or fall back to default
71
+ }
72
+ }
73
+ }
74
+
75
+ // Fall back to waterfall workflow as default
76
+ // Use import.meta.url to get the current file's path in ESM
77
+ const currentFileUrl = import.meta.url;
78
+ const currentFilePath = fileURLToPath(currentFileUrl);
79
+ // Go up from packages/core/dist/ to project root (4 levels up)
80
+ const projectRoot = path.dirname(
81
+ path.dirname(path.dirname(path.dirname(currentFilePath)))
82
+ );
83
+ const defaultFilePath = path.join(
84
+ projectRoot,
85
+ 'resources',
86
+ 'workflows',
87
+ 'waterfall.yaml'
88
+ );
89
+
90
+ logger.info('Loading default state machine file', { defaultFilePath });
91
+ return this.loadFromFile(defaultFilePath);
92
+ }
93
+
94
+ /**
95
+ * Load state machine from specific file path
96
+ */
97
+ public loadFromFile(filePath: string): YamlStateMachine {
98
+ try {
99
+ const yamlContent = fs.readFileSync(path.resolve(filePath), 'utf8');
100
+ const stateMachine = yaml.load(yamlContent) as YamlStateMachine;
101
+
102
+ // Validate the state machine
103
+ this.validateStateMachine(stateMachine);
104
+
105
+ // Store valid phases for later validation
106
+ this.validPhases = new Set(Object.keys(stateMachine.states));
107
+
108
+ this.stateMachine = stateMachine;
109
+ logger.info('State machine loaded successfully', {
110
+ name: stateMachine.name,
111
+ stateCount: Object.keys(stateMachine.states).length,
112
+ phases: Array.from(this.validPhases),
113
+ });
114
+
115
+ return stateMachine;
116
+ } catch (error) {
117
+ logger.error('Failed to load state machine', error as Error);
118
+ throw new Error(
119
+ `Failed to load state machine: ${(error as Error).message}`
120
+ );
121
+ }
122
+ }
123
+
124
+ /**
125
+ * Validate the state machine structure and references
126
+ */
127
+ private validateStateMachine(stateMachine: YamlStateMachine): void {
128
+ // Check required properties
129
+ if (
130
+ !stateMachine.name ||
131
+ !stateMachine.description ||
132
+ !stateMachine.initial_state ||
133
+ !stateMachine.states
134
+ ) {
135
+ throw new Error('State machine is missing required properties');
136
+ }
137
+
138
+ // Get all state names
139
+ const stateNames = Object.keys(stateMachine.states);
140
+
141
+ // Check initial state is valid
142
+ if (!stateNames.includes(stateMachine.initial_state)) {
143
+ throw new Error(
144
+ `Initial state "${stateMachine.initial_state}" is not defined in states`
145
+ );
146
+ }
147
+
148
+ // Validate states and transitions
149
+ for (const [stateName, state] of Object.entries(stateMachine.states)) {
150
+ // Check required state properties
151
+ if (!state.description || !state.default_instructions) {
152
+ throw new Error(
153
+ `State "${stateName}" is missing required properties (description or default_instructions)`
154
+ );
155
+ }
156
+
157
+ if (!state.transitions || !Array.isArray(state.transitions)) {
158
+ throw new Error(
159
+ `State "${stateName}" has invalid transitions property`
160
+ );
161
+ }
162
+
163
+ for (const transition of state.transitions) {
164
+ if (!stateNames.includes(transition.to)) {
165
+ throw new Error(
166
+ `State "${stateName}" has transition to unknown state "${transition.to}"`
167
+ );
168
+ }
169
+
170
+ if (!transition.transition_reason) {
171
+ throw new Error(
172
+ `Transition from "${stateName}" to "${transition.to}" is missing transition_reason`
173
+ );
174
+ }
175
+ }
176
+ }
177
+
178
+ logger.debug('State machine validation successful');
179
+ }
180
+
181
+ /**
182
+ * Get transition instructions for a specific state change
183
+ */
184
+ public getTransitionInstructions(
185
+ fromState: string,
186
+ toState: string,
187
+ trigger?: string
188
+ ): { instructions: string; transitionReason: string; isModeled: boolean } {
189
+ if (!this.stateMachine) {
190
+ throw new Error('State machine not loaded');
191
+ }
192
+
193
+ // Get target state definition
194
+ const targetState = this.stateMachine.states[toState];
195
+ if (!targetState) {
196
+ throw new Error(`Target state "${toState}" not found`);
197
+ }
198
+
199
+ // Look for a modeled transition first
200
+ const fromStateDefinition = this.stateMachine.states[fromState];
201
+ if (fromStateDefinition) {
202
+ const transition = fromStateDefinition.transitions.find(
203
+ t => t.to === toState && (!trigger || t.trigger === trigger)
204
+ );
205
+
206
+ if (transition) {
207
+ // For modeled transitions, compose instructions
208
+ let composedInstructions = targetState.default_instructions;
209
+
210
+ // If transition has specific instructions, use those instead of default
211
+ if (transition.instructions) {
212
+ composedInstructions = transition.instructions;
213
+ }
214
+
215
+ // If transition has additional instructions, combine them
216
+ if (transition.additional_instructions) {
217
+ composedInstructions = `${composedInstructions}\n\n**Additional Context:**\n${transition.additional_instructions}`;
218
+ }
219
+
220
+ return {
221
+ instructions: composedInstructions,
222
+ transitionReason: transition.transition_reason,
223
+ isModeled: true,
224
+ };
225
+ }
226
+ }
227
+
228
+ // Fall back to target state's default instructions for unmodeled transitions
229
+ return {
230
+ instructions: targetState.default_instructions,
231
+ transitionReason: `Direct transition to ${toState} phase`,
232
+ isModeled: false,
233
+ };
234
+ }
235
+
236
+ /**
237
+ * Get all possible transitions from a given state
238
+ */
239
+ public getPossibleTransitions(fromState: string): YamlTransition[] {
240
+ if (!this.stateMachine) {
241
+ throw new Error('State machine not loaded');
242
+ }
243
+
244
+ const stateDefinition = this.stateMachine.states[fromState];
245
+ return stateDefinition ? stateDefinition.transitions : [];
246
+ }
247
+
248
+ /**
249
+ * Check if a transition is modeled (shown in state diagram)
250
+ */
251
+ public isModeledTransition(fromState: string, toState: string): boolean {
252
+ if (!this.stateMachine) {
253
+ throw new Error('State machine not loaded');
254
+ }
255
+
256
+ const stateDefinition = this.stateMachine.states[fromState];
257
+ if (!stateDefinition) return false;
258
+
259
+ return stateDefinition.transitions.some(t => t.to === toState);
260
+ }
261
+
262
+ /**
263
+ * Check if a phase is valid in the current state machine
264
+ */
265
+ public isValidPhase(phase: string): boolean {
266
+ return this.validPhases.has(phase);
267
+ }
268
+
269
+ /**
270
+ * Get phase-specific instructions for continuing work in current phase
271
+ */
272
+ public getContinuePhaseInstructions(phase: string): string {
273
+ if (!this.stateMachine) {
274
+ throw new Error('State machine not loaded');
275
+ }
276
+
277
+ const stateDefinition = this.stateMachine.states[phase];
278
+ if (!stateDefinition) {
279
+ logger.error('Unknown phase', new Error(`Unknown phase: ${phase}`));
280
+ throw new Error(`Unknown phase: ${phase}`);
281
+ }
282
+
283
+ // Look for a self-transition (continue in same phase)
284
+ const continueTransition = stateDefinition.transitions.find(
285
+ t => t.to === phase
286
+ );
287
+
288
+ if (continueTransition) {
289
+ // Compose instructions for continue transition
290
+ let composedInstructions = stateDefinition.default_instructions;
291
+
292
+ // If transition has specific instructions, use those instead of default
293
+ if (continueTransition.instructions) {
294
+ composedInstructions = continueTransition.instructions;
295
+ }
296
+
297
+ // If transition has additional instructions, combine them
298
+ if (continueTransition.additional_instructions) {
299
+ composedInstructions = `${composedInstructions}\n\n**Additional Context:**\n${continueTransition.additional_instructions}`;
300
+ }
301
+
302
+ return composedInstructions;
303
+ }
304
+
305
+ // Fall back to default instructions for the phase
306
+ return stateDefinition.default_instructions;
307
+ }
308
+ }
@@ -0,0 +1,72 @@
1
+ /**
2
+ * State Machine Types
3
+ *
4
+ * Type definitions for YAML-based state machine
5
+ */
6
+
7
+ /**
8
+ * Transition between states
9
+ */
10
+ export interface YamlTransition {
11
+ /** Event that triggers this transition */
12
+ trigger: string;
13
+
14
+ /** Target state after transition */
15
+ to: string;
16
+
17
+ /** Instructions to provide when this transition occurs (optional - uses target state default if not provided) */
18
+ instructions?: string;
19
+
20
+ /** Additional instructions to combine with target state's default instructions (optional) */
21
+ additional_instructions?: string;
22
+
23
+ /** Reason for this transition */
24
+ transition_reason: string;
25
+
26
+ /** Optional review perspectives for this transition */
27
+ review_perspectives?: Array<{
28
+ perspective: string;
29
+ prompt: string;
30
+ }>;
31
+ }
32
+
33
+ /**
34
+ * State definition
35
+ */
36
+ export interface YamlState {
37
+ /** Description of this state */
38
+ description: string;
39
+
40
+ /** Default instructions when entering this state */
41
+ default_instructions: string;
42
+
43
+ /** Transitions from this state */
44
+ transitions: YamlTransition[];
45
+ }
46
+
47
+ /**
48
+ * Complete state machine definition
49
+ */
50
+ export interface YamlStateMachine {
51
+ /** Name of the state machine */
52
+ name: string;
53
+
54
+ /** Description of the state machine's purpose */
55
+ description: string;
56
+
57
+ /** The starting state of the machine */
58
+ initial_state: string;
59
+
60
+ /** Map of states in the state machine */
61
+ states: Record<string, YamlState>;
62
+
63
+ /** Optional metadata for enhanced discoverability */
64
+ metadata?: {
65
+ complexity?: 'low' | 'medium' | 'high';
66
+ domain: string;
67
+ bestFor?: string[];
68
+ useCases?: string[];
69
+ examples?: string[];
70
+ requiresDocumentation?: boolean;
71
+ };
72
+ }