@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.
- package/.turbo/turbo-build.log +4 -0
- package/LICENSE +674 -0
- package/dist/config-manager.d.ts +24 -0
- package/dist/config-manager.js +68 -0
- package/dist/config-manager.js.map +1 -0
- package/dist/conversation-manager.d.ts +97 -0
- package/dist/conversation-manager.js +367 -0
- package/dist/conversation-manager.js.map +1 -0
- package/dist/database.d.ts +73 -0
- package/dist/database.js +500 -0
- package/dist/database.js.map +1 -0
- package/dist/file-detection-manager.d.ts +53 -0
- package/dist/file-detection-manager.js +221 -0
- package/dist/file-detection-manager.js.map +1 -0
- package/dist/git-manager.d.ts +14 -0
- package/dist/git-manager.js +59 -0
- package/dist/git-manager.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +25 -0
- package/dist/index.js.map +1 -0
- package/dist/instruction-generator.d.ts +69 -0
- package/dist/instruction-generator.js +133 -0
- package/dist/instruction-generator.js.map +1 -0
- package/dist/interaction-logger.d.ts +37 -0
- package/dist/interaction-logger.js +87 -0
- package/dist/interaction-logger.js.map +1 -0
- package/dist/logger.d.ts +64 -0
- package/dist/logger.js +283 -0
- package/dist/logger.js.map +1 -0
- package/dist/path-validation-utils.d.ts +51 -0
- package/dist/path-validation-utils.js +202 -0
- package/dist/path-validation-utils.js.map +1 -0
- package/dist/plan-manager.d.ts +65 -0
- package/dist/plan-manager.js +256 -0
- package/dist/plan-manager.js.map +1 -0
- package/dist/project-docs-manager.d.ts +119 -0
- package/dist/project-docs-manager.js +357 -0
- package/dist/project-docs-manager.js.map +1 -0
- package/dist/state-machine-loader.d.ts +60 -0
- package/dist/state-machine-loader.js +235 -0
- package/dist/state-machine-loader.js.map +1 -0
- package/dist/state-machine-types.d.ts +58 -0
- package/dist/state-machine-types.js +7 -0
- package/dist/state-machine-types.js.map +1 -0
- package/dist/state-machine.d.ts +52 -0
- package/dist/state-machine.js +256 -0
- package/dist/state-machine.js.map +1 -0
- package/dist/system-prompt-generator.d.ts +17 -0
- package/dist/system-prompt-generator.js +113 -0
- package/dist/system-prompt-generator.js.map +1 -0
- package/dist/template-manager.d.ts +61 -0
- package/dist/template-manager.js +229 -0
- package/dist/template-manager.js.map +1 -0
- package/dist/transition-engine.d.ts +70 -0
- package/dist/transition-engine.js +240 -0
- package/dist/transition-engine.js.map +1 -0
- package/dist/types.d.ts +56 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/dist/workflow-manager.d.ts +89 -0
- package/dist/workflow-manager.js +466 -0
- package/dist/workflow-manager.js.map +1 -0
- package/package.json +27 -0
- package/src/config-manager.ts +96 -0
- package/src/conversation-manager.ts +492 -0
- package/src/database.ts +685 -0
- package/src/file-detection-manager.ts +302 -0
- package/src/git-manager.ts +64 -0
- package/src/index.ts +28 -0
- package/src/instruction-generator.ts +210 -0
- package/src/interaction-logger.ts +109 -0
- package/src/logger.ts +353 -0
- package/src/path-validation-utils.ts +261 -0
- package/src/plan-manager.ts +323 -0
- package/src/project-docs-manager.ts +522 -0
- package/src/state-machine-loader.ts +308 -0
- package/src/state-machine-types.ts +72 -0
- package/src/state-machine.ts +370 -0
- package/src/system-prompt-generator.ts +122 -0
- package/src/template-manager.ts +321 -0
- package/src/transition-engine.ts +386 -0
- package/src/types.ts +60 -0
- package/src/workflow-manager.ts +601 -0
- package/test/unit/conversation-manager.test.ts +179 -0
- package/test/unit/custom-workflow-loading.test.ts +174 -0
- package/test/unit/directory-linking-and-extensions.test.ts +338 -0
- package/test/unit/file-linking-integration.test.ts +256 -0
- package/test/unit/git-commit-integration.test.ts +91 -0
- package/test/unit/git-manager.test.ts +86 -0
- package/test/unit/install-workflow.test.ts +138 -0
- package/test/unit/instruction-generator.test.ts +247 -0
- package/test/unit/list-workflows-filtering.test.ts +68 -0
- package/test/unit/none-template-functionality.test.ts +224 -0
- package/test/unit/project-docs-manager.test.ts +337 -0
- package/test/unit/state-machine-loader.test.ts +234 -0
- package/test/unit/template-manager.test.ts +217 -0
- package/test/unit/validate-workflow-name.test.ts +150 -0
- package/test/unit/workflow-domain-filtering.test.ts +75 -0
- package/test/unit/workflow-enum-generation.test.ts +92 -0
- package/test/unit/workflow-manager-enhanced-path-resolution.test.ts +369 -0
- package/test/unit/workflow-manager-path-resolution.test.ts +150 -0
- package/test/unit/workflow-migration.test.ts +155 -0
- package/test/unit/workflow-override-by-name.test.ts +116 -0
- package/test/unit/workflow-prioritization.test.ts +38 -0
- package/test/unit/workflow-validation.test.ts +303 -0
- package/test/utils/e2e-test-setup.ts +453 -0
- package/test/utils/run-server-in-dir.sh +27 -0
- package/test/utils/temp-files.ts +308 -0
- package/test/utils/test-access.ts +79 -0
- package/test/utils/test-helpers.ts +286 -0
- package/test/utils/test-setup.ts +78 -0
- package/tsconfig.build.json +21 -0
- package/tsconfig.json +8 -0
- package/vitest.config.ts +18 -0
@@ -0,0 +1,323 @@
|
|
1
|
+
/**
|
2
|
+
* Plan Manager
|
3
|
+
*
|
4
|
+
* Handles the creation, updating, and maintenance of project development plan files.
|
5
|
+
* Manages markdown plan files that serve as long-term project memory.
|
6
|
+
* Supports custom state machine definitions for dynamic plan file generation.
|
7
|
+
*/
|
8
|
+
|
9
|
+
import { writeFile, readFile, access } from 'node:fs/promises';
|
10
|
+
import { dirname } from 'node:path';
|
11
|
+
import { mkdir } from 'node:fs/promises';
|
12
|
+
import { createLogger } from './logger.js';
|
13
|
+
|
14
|
+
import type { YamlStateMachine } from './state-machine-types.js';
|
15
|
+
|
16
|
+
const logger = createLogger('PlanManager');
|
17
|
+
|
18
|
+
export interface PlanFileInfo {
|
19
|
+
path: string;
|
20
|
+
exists: boolean;
|
21
|
+
content?: string;
|
22
|
+
}
|
23
|
+
|
24
|
+
export class PlanManager {
|
25
|
+
private stateMachine: YamlStateMachine | null = null;
|
26
|
+
|
27
|
+
/**
|
28
|
+
* Set the state machine definition for dynamic plan generation
|
29
|
+
*/
|
30
|
+
setStateMachine(stateMachine: YamlStateMachine): void {
|
31
|
+
this.stateMachine = stateMachine;
|
32
|
+
logger.debug('State machine set for plan manager', {
|
33
|
+
name: stateMachine.name,
|
34
|
+
phases: Object.keys(stateMachine.states),
|
35
|
+
});
|
36
|
+
}
|
37
|
+
|
38
|
+
/**
|
39
|
+
* Get plan file information
|
40
|
+
*/
|
41
|
+
async getPlanFileInfo(planFilePath: string): Promise<PlanFileInfo> {
|
42
|
+
try {
|
43
|
+
await access(planFilePath);
|
44
|
+
const content = await readFile(planFilePath, 'utf-8');
|
45
|
+
return {
|
46
|
+
path: planFilePath,
|
47
|
+
exists: true,
|
48
|
+
content,
|
49
|
+
};
|
50
|
+
} catch (_error) {
|
51
|
+
return {
|
52
|
+
path: planFilePath,
|
53
|
+
exists: false,
|
54
|
+
};
|
55
|
+
}
|
56
|
+
}
|
57
|
+
|
58
|
+
/**
|
59
|
+
* Create initial plan file if it doesn't exist
|
60
|
+
*/
|
61
|
+
async ensurePlanFile(
|
62
|
+
planFilePath: string,
|
63
|
+
projectPath: string,
|
64
|
+
gitBranch: string
|
65
|
+
): Promise<void> {
|
66
|
+
logger.debug('Ensuring plan file exists', {
|
67
|
+
planFilePath,
|
68
|
+
projectPath,
|
69
|
+
gitBranch,
|
70
|
+
});
|
71
|
+
|
72
|
+
const planInfo = await this.getPlanFileInfo(planFilePath);
|
73
|
+
|
74
|
+
if (!planInfo.exists) {
|
75
|
+
logger.info('Plan file not found, creating initial plan', {
|
76
|
+
planFilePath,
|
77
|
+
});
|
78
|
+
await this.createInitialPlanFile(planFilePath, projectPath, gitBranch);
|
79
|
+
logger.info('Initial plan file created successfully', { planFilePath });
|
80
|
+
} else {
|
81
|
+
logger.debug('Plan file already exists', { planFilePath });
|
82
|
+
}
|
83
|
+
}
|
84
|
+
|
85
|
+
/**
|
86
|
+
* Create initial plan file with template content
|
87
|
+
*/
|
88
|
+
private async createInitialPlanFile(
|
89
|
+
planFilePath: string,
|
90
|
+
projectPath: string,
|
91
|
+
gitBranch: string
|
92
|
+
): Promise<void> {
|
93
|
+
logger.debug('Creating initial plan file', { planFilePath });
|
94
|
+
|
95
|
+
try {
|
96
|
+
// Ensure directory exists
|
97
|
+
await mkdir(dirname(planFilePath), { recursive: true });
|
98
|
+
logger.debug('Plan file directory ensured', {
|
99
|
+
directory: dirname(planFilePath),
|
100
|
+
});
|
101
|
+
|
102
|
+
const projectName = projectPath.split('/').pop() || 'Unknown Project';
|
103
|
+
const branchInfo = gitBranch !== 'no-git' ? ` (${gitBranch} branch)` : '';
|
104
|
+
|
105
|
+
const initialContent = this.generateInitialPlanContent(
|
106
|
+
projectName,
|
107
|
+
branchInfo
|
108
|
+
);
|
109
|
+
|
110
|
+
await writeFile(planFilePath, initialContent, 'utf-8');
|
111
|
+
logger.info('Initial plan file written successfully', {
|
112
|
+
planFilePath,
|
113
|
+
contentLength: initialContent.length,
|
114
|
+
projectName,
|
115
|
+
});
|
116
|
+
} catch (error) {
|
117
|
+
logger.error('Failed to create initial plan file', error as Error, {
|
118
|
+
planFilePath,
|
119
|
+
});
|
120
|
+
throw error;
|
121
|
+
}
|
122
|
+
}
|
123
|
+
|
124
|
+
/**
|
125
|
+
* Generate initial plan file content based on state machine definition
|
126
|
+
*/
|
127
|
+
private generateInitialPlanContent(
|
128
|
+
projectName: string,
|
129
|
+
branchInfo: string
|
130
|
+
): string {
|
131
|
+
const timestamp = new Date().toISOString().split('T')[0];
|
132
|
+
|
133
|
+
if (!this.stateMachine) {
|
134
|
+
throw new Error(
|
135
|
+
'State machine not set. This should not happen as state machine is always loaded.'
|
136
|
+
);
|
137
|
+
}
|
138
|
+
|
139
|
+
const phases = Object.keys(this.stateMachine.states);
|
140
|
+
const initialPhase = this.stateMachine.initial_state;
|
141
|
+
|
142
|
+
const documentationUrl = this.generateWorkflowDocumentationUrl(
|
143
|
+
this.stateMachine.name
|
144
|
+
);
|
145
|
+
|
146
|
+
let content = `# Development Plan: ${projectName}${branchInfo}
|
147
|
+
|
148
|
+
*Generated on ${timestamp} by Vibe Feature MCP*
|
149
|
+
*Workflow: ${
|
150
|
+
documentationUrl
|
151
|
+
? '[' + this.stateMachine.name + ']' + '(' + documentationUrl + ')'
|
152
|
+
: this.stateMachine.name
|
153
|
+
}*
|
154
|
+
|
155
|
+
## Goal
|
156
|
+
*Define what you're building or fixing - this will be updated as requirements are gathered*
|
157
|
+
|
158
|
+
## ${this.capitalizePhase(initialPhase)}
|
159
|
+
### Tasks
|
160
|
+
- [ ] *Tasks will be added as they are identified*
|
161
|
+
|
162
|
+
### Completed
|
163
|
+
- [x] Created development plan file
|
164
|
+
|
165
|
+
`;
|
166
|
+
|
167
|
+
// Generate simple sections for each phase
|
168
|
+
for (const phase of phases) {
|
169
|
+
if (phase !== initialPhase) {
|
170
|
+
content += `## ${this.capitalizePhase(phase)}
|
171
|
+
### Tasks
|
172
|
+
- [ ] *To be added when this phase becomes active*
|
173
|
+
|
174
|
+
### Completed
|
175
|
+
*None yet*
|
176
|
+
|
177
|
+
`;
|
178
|
+
}
|
179
|
+
}
|
180
|
+
|
181
|
+
content += `## Key Decisions
|
182
|
+
*Important decisions will be documented here as they are made*
|
183
|
+
|
184
|
+
## Notes
|
185
|
+
*Additional context and observations*
|
186
|
+
|
187
|
+
---
|
188
|
+
*This plan is maintained by the LLM. Tool responses provide guidance on which section to focus on and what tasks to work on.*
|
189
|
+
`;
|
190
|
+
|
191
|
+
return content;
|
192
|
+
}
|
193
|
+
|
194
|
+
/**
|
195
|
+
* Update plan file with new content (this is typically done by the LLM)
|
196
|
+
*/
|
197
|
+
async updatePlanFile(planFilePath: string, content: string): Promise<void> {
|
198
|
+
// Ensure directory exists
|
199
|
+
await mkdir(dirname(planFilePath), { recursive: true });
|
200
|
+
|
201
|
+
await writeFile(planFilePath, content, 'utf-8');
|
202
|
+
}
|
203
|
+
|
204
|
+
/**
|
205
|
+
* Get plan file content for LLM context
|
206
|
+
*/
|
207
|
+
async getPlanFileContent(planFilePath: string): Promise<string> {
|
208
|
+
const planInfo = await this.getPlanFileInfo(planFilePath);
|
209
|
+
|
210
|
+
if (!planInfo.exists) {
|
211
|
+
return 'Plan file does not exist yet. It will be created when the LLM updates it.';
|
212
|
+
}
|
213
|
+
|
214
|
+
return planInfo.content || '';
|
215
|
+
}
|
216
|
+
|
217
|
+
/**
|
218
|
+
* Generate phase-specific plan file guidance based on state machine
|
219
|
+
*/
|
220
|
+
generatePlanFileGuidance(phase: string): string {
|
221
|
+
if (!this.stateMachine) {
|
222
|
+
throw new Error(
|
223
|
+
'State machine not set. This should not happen as state machine is always loaded.'
|
224
|
+
);
|
225
|
+
}
|
226
|
+
|
227
|
+
const phaseDefinition = this.stateMachine.states[phase];
|
228
|
+
if (!phaseDefinition) {
|
229
|
+
logger.warn('Unknown phase for plan file guidance', { phase });
|
230
|
+
return `Update the ${this.capitalizePhase(phase)} section with current progress and mark completed tasks.`;
|
231
|
+
}
|
232
|
+
|
233
|
+
const capitalizedPhase = this.capitalizePhase(phase);
|
234
|
+
|
235
|
+
return `Update the ${capitalizedPhase} section with progress. Mark completed tasks with [x] and add new tasks as they are identified.`;
|
236
|
+
}
|
237
|
+
|
238
|
+
/**
|
239
|
+
* Delete plan file
|
240
|
+
*/
|
241
|
+
async deletePlanFile(planFilePath: string): Promise<boolean> {
|
242
|
+
logger.debug('Deleting plan file', { planFilePath });
|
243
|
+
|
244
|
+
try {
|
245
|
+
// Check if file exists first
|
246
|
+
await access(planFilePath);
|
247
|
+
|
248
|
+
// Import unlink dynamically to avoid issues
|
249
|
+
const { unlink } = await import('node:fs/promises');
|
250
|
+
await unlink(planFilePath);
|
251
|
+
|
252
|
+
logger.info('Plan file deleted successfully', { planFilePath });
|
253
|
+
return true;
|
254
|
+
} catch (error: unknown) {
|
255
|
+
if ((error as NodeJS.ErrnoException).code === 'ENOENT') {
|
256
|
+
logger.debug('Plan file does not exist, nothing to delete', {
|
257
|
+
planFilePath,
|
258
|
+
});
|
259
|
+
return true; // Consider it successful if file doesn't exist
|
260
|
+
}
|
261
|
+
|
262
|
+
logger.error('Failed to delete plan file', error as Error, {
|
263
|
+
planFilePath,
|
264
|
+
});
|
265
|
+
throw error;
|
266
|
+
}
|
267
|
+
}
|
268
|
+
|
269
|
+
/**
|
270
|
+
* Ensure plan file is deleted (verify deletion)
|
271
|
+
*/
|
272
|
+
async ensurePlanFileDeleted(planFilePath: string): Promise<boolean> {
|
273
|
+
logger.debug('Ensuring plan file is deleted', { planFilePath });
|
274
|
+
|
275
|
+
try {
|
276
|
+
await access(planFilePath);
|
277
|
+
// If we reach here, file still exists
|
278
|
+
logger.warn('Plan file still exists after deletion attempt', {
|
279
|
+
planFilePath,
|
280
|
+
});
|
281
|
+
return false;
|
282
|
+
} catch (error: unknown) {
|
283
|
+
if ((error as NodeJS.ErrnoException).code === 'ENOENT') {
|
284
|
+
logger.debug('Plan file successfully deleted (does not exist)', {
|
285
|
+
planFilePath,
|
286
|
+
});
|
287
|
+
return true;
|
288
|
+
}
|
289
|
+
|
290
|
+
// Some other error occurred
|
291
|
+
logger.error('Error checking plan file deletion', error as Error, {
|
292
|
+
planFilePath,
|
293
|
+
});
|
294
|
+
throw error;
|
295
|
+
}
|
296
|
+
}
|
297
|
+
|
298
|
+
/**
|
299
|
+
* Capitalize phase name for display
|
300
|
+
*/
|
301
|
+
private capitalizePhase(phase: string): string {
|
302
|
+
return phase
|
303
|
+
.split('_')
|
304
|
+
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
|
305
|
+
.join(' ');
|
306
|
+
}
|
307
|
+
|
308
|
+
/**
|
309
|
+
* Generate workflow documentation URL for predefined workflows
|
310
|
+
* Returns undefined for custom workflows
|
311
|
+
*/
|
312
|
+
private generateWorkflowDocumentationUrl(
|
313
|
+
workflowName: string
|
314
|
+
): string | undefined {
|
315
|
+
// Don't generate URL for custom workflows
|
316
|
+
if (workflowName === 'custom') {
|
317
|
+
return undefined;
|
318
|
+
}
|
319
|
+
|
320
|
+
// Generate URL for predefined workflows
|
321
|
+
return `https://mrsimpson.github.io/responsible-vibe-mcp/workflows/${workflowName}`;
|
322
|
+
}
|
323
|
+
}
|