@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,202 @@
|
|
1
|
+
/**
|
2
|
+
* Path Validation Utilities
|
3
|
+
*
|
4
|
+
* Provides utilities for validating file paths, resolving relative paths,
|
5
|
+
* and ensuring security constraints for the file linking functionality.
|
6
|
+
*/
|
7
|
+
import { access, stat } from 'node:fs/promises';
|
8
|
+
import { resolve, isAbsolute, join, normalize } from 'node:path';
|
9
|
+
import { createLogger } from './logger.js';
|
10
|
+
const logger = createLogger('PathValidationUtils');
|
11
|
+
export class PathValidationUtils {
|
12
|
+
/**
|
13
|
+
* Validate if a string is a known template name
|
14
|
+
*/
|
15
|
+
static isTemplateName(value, availableTemplates) {
|
16
|
+
return availableTemplates.includes(value);
|
17
|
+
}
|
18
|
+
/**
|
19
|
+
* Validate and resolve a file path
|
20
|
+
*/
|
21
|
+
static async validateFilePath(filePath, projectPath) {
|
22
|
+
try {
|
23
|
+
// Resolve the path to absolute
|
24
|
+
const resolvedPath = this.resolvePath(filePath, projectPath);
|
25
|
+
// Security validation - prevent directory traversal
|
26
|
+
if (!this.isPathSafe(resolvedPath, projectPath)) {
|
27
|
+
return {
|
28
|
+
isValid: false,
|
29
|
+
error: 'Path is outside project boundaries for security reasons',
|
30
|
+
};
|
31
|
+
}
|
32
|
+
// Check if file exists and is readable
|
33
|
+
await access(resolvedPath);
|
34
|
+
// Verify it's a file (not a directory)
|
35
|
+
const stats = await stat(resolvedPath);
|
36
|
+
if (!stats.isFile()) {
|
37
|
+
return {
|
38
|
+
isValid: false,
|
39
|
+
error: 'Path points to a directory, not a file',
|
40
|
+
};
|
41
|
+
}
|
42
|
+
logger.debug('File path validated successfully', {
|
43
|
+
originalPath: filePath,
|
44
|
+
resolvedPath,
|
45
|
+
});
|
46
|
+
return {
|
47
|
+
isValid: true,
|
48
|
+
resolvedPath,
|
49
|
+
};
|
50
|
+
}
|
51
|
+
catch (error) {
|
52
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
53
|
+
logger.debug('File path validation failed', {
|
54
|
+
filePath,
|
55
|
+
error: errorMessage,
|
56
|
+
});
|
57
|
+
return {
|
58
|
+
isValid: false,
|
59
|
+
error: `File not found or not accessible: ${errorMessage}`,
|
60
|
+
};
|
61
|
+
}
|
62
|
+
}
|
63
|
+
/**
|
64
|
+
* Validate and resolve a file or directory path
|
65
|
+
*/
|
66
|
+
static async validateFileOrDirectoryPath(filePath, projectPath) {
|
67
|
+
try {
|
68
|
+
// Resolve the path to absolute
|
69
|
+
const resolvedPath = this.resolvePath(filePath, projectPath);
|
70
|
+
// Security validation - prevent directory traversal
|
71
|
+
if (!this.isPathSafe(resolvedPath, projectPath)) {
|
72
|
+
return {
|
73
|
+
isValid: false,
|
74
|
+
error: 'Path is outside project boundaries for security reasons',
|
75
|
+
};
|
76
|
+
}
|
77
|
+
// Check if file or directory exists and is readable
|
78
|
+
await access(resolvedPath);
|
79
|
+
// Verify it's either a file or directory
|
80
|
+
const stats = await stat(resolvedPath);
|
81
|
+
if (!stats.isFile() && !stats.isDirectory()) {
|
82
|
+
return {
|
83
|
+
isValid: false,
|
84
|
+
error: 'Path is neither a file nor a directory',
|
85
|
+
};
|
86
|
+
}
|
87
|
+
logger.debug('File or directory path validated successfully', {
|
88
|
+
originalPath: filePath,
|
89
|
+
resolvedPath,
|
90
|
+
isFile: stats.isFile(),
|
91
|
+
isDirectory: stats.isDirectory(),
|
92
|
+
});
|
93
|
+
return {
|
94
|
+
isValid: true,
|
95
|
+
resolvedPath,
|
96
|
+
};
|
97
|
+
}
|
98
|
+
catch (error) {
|
99
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
100
|
+
logger.debug('File or directory path validation failed', {
|
101
|
+
filePath,
|
102
|
+
error: errorMessage,
|
103
|
+
});
|
104
|
+
return {
|
105
|
+
isValid: false,
|
106
|
+
error: `File or directory not found or not accessible: ${errorMessage}`,
|
107
|
+
};
|
108
|
+
}
|
109
|
+
}
|
110
|
+
/**
|
111
|
+
* Resolve a file path to absolute, handling various formats
|
112
|
+
*/
|
113
|
+
static resolvePath(filePath, projectPath) {
|
114
|
+
// If already absolute, return as-is
|
115
|
+
if (isAbsolute(filePath)) {
|
116
|
+
return normalize(filePath);
|
117
|
+
}
|
118
|
+
// Handle relative paths (./file, ../file, file)
|
119
|
+
return resolve(projectPath, filePath);
|
120
|
+
}
|
121
|
+
/**
|
122
|
+
* Check if a resolved path is within safe boundaries
|
123
|
+
* Prevents directory traversal attacks
|
124
|
+
*/
|
125
|
+
static isPathSafe(resolvedPath, projectPath) {
|
126
|
+
const normalizedResolved = normalize(resolvedPath);
|
127
|
+
const normalizedProject = normalize(projectPath);
|
128
|
+
// Allow paths within the project directory
|
129
|
+
if (normalizedResolved.startsWith(normalizedProject)) {
|
130
|
+
return true;
|
131
|
+
}
|
132
|
+
// Allow paths in common documentation locations relative to project
|
133
|
+
const allowedPaths = [
|
134
|
+
normalize(join(projectPath, '..')), // Parent directory (for monorepos)
|
135
|
+
'/usr/share/doc', // System documentation
|
136
|
+
'/opt/docs', // Optional documentation
|
137
|
+
];
|
138
|
+
return allowedPaths.some(allowedPath => normalizedResolved.startsWith(allowedPath));
|
139
|
+
}
|
140
|
+
/**
|
141
|
+
* Validate parameter as either template name or file path
|
142
|
+
*/
|
143
|
+
static async validateParameter(value, availableTemplates, projectPath) {
|
144
|
+
// First check if it's a template name
|
145
|
+
if (this.isTemplateName(value, availableTemplates)) {
|
146
|
+
return {
|
147
|
+
isTemplate: true,
|
148
|
+
isFilePath: false,
|
149
|
+
};
|
150
|
+
}
|
151
|
+
// Then validate as file or directory path
|
152
|
+
const pathValidation = await this.validateFileOrDirectoryPath(value, projectPath);
|
153
|
+
if (pathValidation.isValid) {
|
154
|
+
return {
|
155
|
+
isTemplate: false,
|
156
|
+
isFilePath: true,
|
157
|
+
resolvedPath: pathValidation.resolvedPath,
|
158
|
+
};
|
159
|
+
}
|
160
|
+
// Neither template nor valid file/directory path
|
161
|
+
return {
|
162
|
+
isTemplate: false,
|
163
|
+
isFilePath: false,
|
164
|
+
error: `Invalid parameter: not a known template (${availableTemplates.join(', ')}) and not a valid file or directory path (${pathValidation.error})`,
|
165
|
+
};
|
166
|
+
}
|
167
|
+
/**
|
168
|
+
* Get common file patterns for documentation
|
169
|
+
*/
|
170
|
+
static getCommonDocumentationPatterns() {
|
171
|
+
return {
|
172
|
+
architecture: [
|
173
|
+
'ARCHITECTURE.md',
|
174
|
+
'ARCHITECTURE.txt',
|
175
|
+
'architecture.md',
|
176
|
+
'Architecture.md',
|
177
|
+
'docs/ARCHITECTURE.md',
|
178
|
+
'docs/architecture.md',
|
179
|
+
'README.md', // Can contain architecture info
|
180
|
+
],
|
181
|
+
requirements: [
|
182
|
+
'REQUIREMENTS.md',
|
183
|
+
'REQUIREMENTS.txt',
|
184
|
+
'requirements.md',
|
185
|
+
'Requirements.md',
|
186
|
+
'docs/REQUIREMENTS.md',
|
187
|
+
'docs/requirements.md',
|
188
|
+
'README.md', // Often contains requirements
|
189
|
+
],
|
190
|
+
design: [
|
191
|
+
'DESIGN.md',
|
192
|
+
'DESIGN.txt',
|
193
|
+
'design.md',
|
194
|
+
'Design.md',
|
195
|
+
'docs/DESIGN.md',
|
196
|
+
'docs/design.md',
|
197
|
+
'README.md', // Can contain design info
|
198
|
+
],
|
199
|
+
};
|
200
|
+
}
|
201
|
+
}
|
202
|
+
//# sourceMappingURL=path-validation-utils.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"path-validation-utils.js","sourceRoot":"","sources":["../src/path-validation-utils.ts"],"names":[],"mappings":"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"}
|
@@ -0,0 +1,65 @@
|
|
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
|
+
import type { YamlStateMachine } from './state-machine-types.js';
|
9
|
+
export interface PlanFileInfo {
|
10
|
+
path: string;
|
11
|
+
exists: boolean;
|
12
|
+
content?: string;
|
13
|
+
}
|
14
|
+
export declare class PlanManager {
|
15
|
+
private stateMachine;
|
16
|
+
/**
|
17
|
+
* Set the state machine definition for dynamic plan generation
|
18
|
+
*/
|
19
|
+
setStateMachine(stateMachine: YamlStateMachine): void;
|
20
|
+
/**
|
21
|
+
* Get plan file information
|
22
|
+
*/
|
23
|
+
getPlanFileInfo(planFilePath: string): Promise<PlanFileInfo>;
|
24
|
+
/**
|
25
|
+
* Create initial plan file if it doesn't exist
|
26
|
+
*/
|
27
|
+
ensurePlanFile(planFilePath: string, projectPath: string, gitBranch: string): Promise<void>;
|
28
|
+
/**
|
29
|
+
* Create initial plan file with template content
|
30
|
+
*/
|
31
|
+
private createInitialPlanFile;
|
32
|
+
/**
|
33
|
+
* Generate initial plan file content based on state machine definition
|
34
|
+
*/
|
35
|
+
private generateInitialPlanContent;
|
36
|
+
/**
|
37
|
+
* Update plan file with new content (this is typically done by the LLM)
|
38
|
+
*/
|
39
|
+
updatePlanFile(planFilePath: string, content: string): Promise<void>;
|
40
|
+
/**
|
41
|
+
* Get plan file content for LLM context
|
42
|
+
*/
|
43
|
+
getPlanFileContent(planFilePath: string): Promise<string>;
|
44
|
+
/**
|
45
|
+
* Generate phase-specific plan file guidance based on state machine
|
46
|
+
*/
|
47
|
+
generatePlanFileGuidance(phase: string): string;
|
48
|
+
/**
|
49
|
+
* Delete plan file
|
50
|
+
*/
|
51
|
+
deletePlanFile(planFilePath: string): Promise<boolean>;
|
52
|
+
/**
|
53
|
+
* Ensure plan file is deleted (verify deletion)
|
54
|
+
*/
|
55
|
+
ensurePlanFileDeleted(planFilePath: string): Promise<boolean>;
|
56
|
+
/**
|
57
|
+
* Capitalize phase name for display
|
58
|
+
*/
|
59
|
+
private capitalizePhase;
|
60
|
+
/**
|
61
|
+
* Generate workflow documentation URL for predefined workflows
|
62
|
+
* Returns undefined for custom workflows
|
63
|
+
*/
|
64
|
+
private generateWorkflowDocumentationUrl;
|
65
|
+
}
|
@@ -0,0 +1,256 @@
|
|
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
|
+
import { writeFile, readFile, access } from 'node:fs/promises';
|
9
|
+
import { dirname } from 'node:path';
|
10
|
+
import { mkdir } from 'node:fs/promises';
|
11
|
+
import { createLogger } from './logger.js';
|
12
|
+
const logger = createLogger('PlanManager');
|
13
|
+
export class PlanManager {
|
14
|
+
stateMachine = null;
|
15
|
+
/**
|
16
|
+
* Set the state machine definition for dynamic plan generation
|
17
|
+
*/
|
18
|
+
setStateMachine(stateMachine) {
|
19
|
+
this.stateMachine = stateMachine;
|
20
|
+
logger.debug('State machine set for plan manager', {
|
21
|
+
name: stateMachine.name,
|
22
|
+
phases: Object.keys(stateMachine.states),
|
23
|
+
});
|
24
|
+
}
|
25
|
+
/**
|
26
|
+
* Get plan file information
|
27
|
+
*/
|
28
|
+
async getPlanFileInfo(planFilePath) {
|
29
|
+
try {
|
30
|
+
await access(planFilePath);
|
31
|
+
const content = await readFile(planFilePath, 'utf-8');
|
32
|
+
return {
|
33
|
+
path: planFilePath,
|
34
|
+
exists: true,
|
35
|
+
content,
|
36
|
+
};
|
37
|
+
}
|
38
|
+
catch (_error) {
|
39
|
+
return {
|
40
|
+
path: planFilePath,
|
41
|
+
exists: false,
|
42
|
+
};
|
43
|
+
}
|
44
|
+
}
|
45
|
+
/**
|
46
|
+
* Create initial plan file if it doesn't exist
|
47
|
+
*/
|
48
|
+
async ensurePlanFile(planFilePath, projectPath, gitBranch) {
|
49
|
+
logger.debug('Ensuring plan file exists', {
|
50
|
+
planFilePath,
|
51
|
+
projectPath,
|
52
|
+
gitBranch,
|
53
|
+
});
|
54
|
+
const planInfo = await this.getPlanFileInfo(planFilePath);
|
55
|
+
if (!planInfo.exists) {
|
56
|
+
logger.info('Plan file not found, creating initial plan', {
|
57
|
+
planFilePath,
|
58
|
+
});
|
59
|
+
await this.createInitialPlanFile(planFilePath, projectPath, gitBranch);
|
60
|
+
logger.info('Initial plan file created successfully', { planFilePath });
|
61
|
+
}
|
62
|
+
else {
|
63
|
+
logger.debug('Plan file already exists', { planFilePath });
|
64
|
+
}
|
65
|
+
}
|
66
|
+
/**
|
67
|
+
* Create initial plan file with template content
|
68
|
+
*/
|
69
|
+
async createInitialPlanFile(planFilePath, projectPath, gitBranch) {
|
70
|
+
logger.debug('Creating initial plan file', { planFilePath });
|
71
|
+
try {
|
72
|
+
// Ensure directory exists
|
73
|
+
await mkdir(dirname(planFilePath), { recursive: true });
|
74
|
+
logger.debug('Plan file directory ensured', {
|
75
|
+
directory: dirname(planFilePath),
|
76
|
+
});
|
77
|
+
const projectName = projectPath.split('/').pop() || 'Unknown Project';
|
78
|
+
const branchInfo = gitBranch !== 'no-git' ? ` (${gitBranch} branch)` : '';
|
79
|
+
const initialContent = this.generateInitialPlanContent(projectName, branchInfo);
|
80
|
+
await writeFile(planFilePath, initialContent, 'utf-8');
|
81
|
+
logger.info('Initial plan file written successfully', {
|
82
|
+
planFilePath,
|
83
|
+
contentLength: initialContent.length,
|
84
|
+
projectName,
|
85
|
+
});
|
86
|
+
}
|
87
|
+
catch (error) {
|
88
|
+
logger.error('Failed to create initial plan file', error, {
|
89
|
+
planFilePath,
|
90
|
+
});
|
91
|
+
throw error;
|
92
|
+
}
|
93
|
+
}
|
94
|
+
/**
|
95
|
+
* Generate initial plan file content based on state machine definition
|
96
|
+
*/
|
97
|
+
generateInitialPlanContent(projectName, branchInfo) {
|
98
|
+
const timestamp = new Date().toISOString().split('T')[0];
|
99
|
+
if (!this.stateMachine) {
|
100
|
+
throw new Error('State machine not set. This should not happen as state machine is always loaded.');
|
101
|
+
}
|
102
|
+
const phases = Object.keys(this.stateMachine.states);
|
103
|
+
const initialPhase = this.stateMachine.initial_state;
|
104
|
+
const documentationUrl = this.generateWorkflowDocumentationUrl(this.stateMachine.name);
|
105
|
+
let content = `# Development Plan: ${projectName}${branchInfo}
|
106
|
+
|
107
|
+
*Generated on ${timestamp} by Vibe Feature MCP*
|
108
|
+
*Workflow: ${documentationUrl
|
109
|
+
? '[' + this.stateMachine.name + ']' + '(' + documentationUrl + ')'
|
110
|
+
: this.stateMachine.name}*
|
111
|
+
|
112
|
+
## Goal
|
113
|
+
*Define what you're building or fixing - this will be updated as requirements are gathered*
|
114
|
+
|
115
|
+
## ${this.capitalizePhase(initialPhase)}
|
116
|
+
### Tasks
|
117
|
+
- [ ] *Tasks will be added as they are identified*
|
118
|
+
|
119
|
+
### Completed
|
120
|
+
- [x] Created development plan file
|
121
|
+
|
122
|
+
`;
|
123
|
+
// Generate simple sections for each phase
|
124
|
+
for (const phase of phases) {
|
125
|
+
if (phase !== initialPhase) {
|
126
|
+
content += `## ${this.capitalizePhase(phase)}
|
127
|
+
### Tasks
|
128
|
+
- [ ] *To be added when this phase becomes active*
|
129
|
+
|
130
|
+
### Completed
|
131
|
+
*None yet*
|
132
|
+
|
133
|
+
`;
|
134
|
+
}
|
135
|
+
}
|
136
|
+
content += `## Key Decisions
|
137
|
+
*Important decisions will be documented here as they are made*
|
138
|
+
|
139
|
+
## Notes
|
140
|
+
*Additional context and observations*
|
141
|
+
|
142
|
+
---
|
143
|
+
*This plan is maintained by the LLM. Tool responses provide guidance on which section to focus on and what tasks to work on.*
|
144
|
+
`;
|
145
|
+
return content;
|
146
|
+
}
|
147
|
+
/**
|
148
|
+
* Update plan file with new content (this is typically done by the LLM)
|
149
|
+
*/
|
150
|
+
async updatePlanFile(planFilePath, content) {
|
151
|
+
// Ensure directory exists
|
152
|
+
await mkdir(dirname(planFilePath), { recursive: true });
|
153
|
+
await writeFile(planFilePath, content, 'utf-8');
|
154
|
+
}
|
155
|
+
/**
|
156
|
+
* Get plan file content for LLM context
|
157
|
+
*/
|
158
|
+
async getPlanFileContent(planFilePath) {
|
159
|
+
const planInfo = await this.getPlanFileInfo(planFilePath);
|
160
|
+
if (!planInfo.exists) {
|
161
|
+
return 'Plan file does not exist yet. It will be created when the LLM updates it.';
|
162
|
+
}
|
163
|
+
return planInfo.content || '';
|
164
|
+
}
|
165
|
+
/**
|
166
|
+
* Generate phase-specific plan file guidance based on state machine
|
167
|
+
*/
|
168
|
+
generatePlanFileGuidance(phase) {
|
169
|
+
if (!this.stateMachine) {
|
170
|
+
throw new Error('State machine not set. This should not happen as state machine is always loaded.');
|
171
|
+
}
|
172
|
+
const phaseDefinition = this.stateMachine.states[phase];
|
173
|
+
if (!phaseDefinition) {
|
174
|
+
logger.warn('Unknown phase for plan file guidance', { phase });
|
175
|
+
return `Update the ${this.capitalizePhase(phase)} section with current progress and mark completed tasks.`;
|
176
|
+
}
|
177
|
+
const capitalizedPhase = this.capitalizePhase(phase);
|
178
|
+
return `Update the ${capitalizedPhase} section with progress. Mark completed tasks with [x] and add new tasks as they are identified.`;
|
179
|
+
}
|
180
|
+
/**
|
181
|
+
* Delete plan file
|
182
|
+
*/
|
183
|
+
async deletePlanFile(planFilePath) {
|
184
|
+
logger.debug('Deleting plan file', { planFilePath });
|
185
|
+
try {
|
186
|
+
// Check if file exists first
|
187
|
+
await access(planFilePath);
|
188
|
+
// Import unlink dynamically to avoid issues
|
189
|
+
const { unlink } = await import('node:fs/promises');
|
190
|
+
await unlink(planFilePath);
|
191
|
+
logger.info('Plan file deleted successfully', { planFilePath });
|
192
|
+
return true;
|
193
|
+
}
|
194
|
+
catch (error) {
|
195
|
+
if (error.code === 'ENOENT') {
|
196
|
+
logger.debug('Plan file does not exist, nothing to delete', {
|
197
|
+
planFilePath,
|
198
|
+
});
|
199
|
+
return true; // Consider it successful if file doesn't exist
|
200
|
+
}
|
201
|
+
logger.error('Failed to delete plan file', error, {
|
202
|
+
planFilePath,
|
203
|
+
});
|
204
|
+
throw error;
|
205
|
+
}
|
206
|
+
}
|
207
|
+
/**
|
208
|
+
* Ensure plan file is deleted (verify deletion)
|
209
|
+
*/
|
210
|
+
async ensurePlanFileDeleted(planFilePath) {
|
211
|
+
logger.debug('Ensuring plan file is deleted', { planFilePath });
|
212
|
+
try {
|
213
|
+
await access(planFilePath);
|
214
|
+
// If we reach here, file still exists
|
215
|
+
logger.warn('Plan file still exists after deletion attempt', {
|
216
|
+
planFilePath,
|
217
|
+
});
|
218
|
+
return false;
|
219
|
+
}
|
220
|
+
catch (error) {
|
221
|
+
if (error.code === 'ENOENT') {
|
222
|
+
logger.debug('Plan file successfully deleted (does not exist)', {
|
223
|
+
planFilePath,
|
224
|
+
});
|
225
|
+
return true;
|
226
|
+
}
|
227
|
+
// Some other error occurred
|
228
|
+
logger.error('Error checking plan file deletion', error, {
|
229
|
+
planFilePath,
|
230
|
+
});
|
231
|
+
throw error;
|
232
|
+
}
|
233
|
+
}
|
234
|
+
/**
|
235
|
+
* Capitalize phase name for display
|
236
|
+
*/
|
237
|
+
capitalizePhase(phase) {
|
238
|
+
return phase
|
239
|
+
.split('_')
|
240
|
+
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
|
241
|
+
.join(' ');
|
242
|
+
}
|
243
|
+
/**
|
244
|
+
* Generate workflow documentation URL for predefined workflows
|
245
|
+
* Returns undefined for custom workflows
|
246
|
+
*/
|
247
|
+
generateWorkflowDocumentationUrl(workflowName) {
|
248
|
+
// Don't generate URL for custom workflows
|
249
|
+
if (workflowName === 'custom') {
|
250
|
+
return undefined;
|
251
|
+
}
|
252
|
+
// Generate URL for predefined workflows
|
253
|
+
return `https://mrsimpson.github.io/responsible-vibe-mcp/workflows/${workflowName}`;
|
254
|
+
}
|
255
|
+
}
|
256
|
+
//# sourceMappingURL=plan-manager.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"plan-manager.js","sourceRoot":"","sources":["../src/plan-manager.ts"],"names":[],"mappings":"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"}
|
@@ -0,0 +1,119 @@
|
|
1
|
+
/**
|
2
|
+
* Project Docs Manager
|
3
|
+
*
|
4
|
+
* Manages project documentation artifacts (architecture.md, requirements.md, design.md)
|
5
|
+
* separate from the workflow-specific plan files. Handles creation, validation, and
|
6
|
+
* path resolution for project documents. Now supports both template creation and
|
7
|
+
* file linking via symlinks.
|
8
|
+
*/
|
9
|
+
import { TemplateManager, TemplateOptions } from './template-manager.js';
|
10
|
+
export interface ProjectDocsInfo {
|
11
|
+
architecture: {
|
12
|
+
path: string;
|
13
|
+
exists: boolean;
|
14
|
+
};
|
15
|
+
requirements: {
|
16
|
+
path: string;
|
17
|
+
exists: boolean;
|
18
|
+
};
|
19
|
+
design: {
|
20
|
+
path: string;
|
21
|
+
exists: boolean;
|
22
|
+
};
|
23
|
+
}
|
24
|
+
export interface CreateOrLinkResult {
|
25
|
+
created: string[];
|
26
|
+
linked: string[];
|
27
|
+
skipped: string[];
|
28
|
+
}
|
29
|
+
export declare class ProjectDocsManager {
|
30
|
+
templateManager: TemplateManager;
|
31
|
+
constructor();
|
32
|
+
/**
|
33
|
+
* Get project docs directory path
|
34
|
+
*/
|
35
|
+
getDocsPath(projectPath: string): string;
|
36
|
+
/**
|
37
|
+
* Determine the appropriate extension for a document based on source path
|
38
|
+
*/
|
39
|
+
private getDocumentExtension;
|
40
|
+
/**
|
41
|
+
* Get the target filename for a document type
|
42
|
+
*/
|
43
|
+
private getDocumentFilename;
|
44
|
+
/**
|
45
|
+
* Get paths for all project documents
|
46
|
+
*/
|
47
|
+
getDocumentPaths(projectPath: string): {
|
48
|
+
architecture: string;
|
49
|
+
requirements: string;
|
50
|
+
design: string;
|
51
|
+
};
|
52
|
+
/**
|
53
|
+
* Get paths for all project documents with dynamic extensions based on source paths
|
54
|
+
*/
|
55
|
+
getDocumentPathsWithExtensions(projectPath: string, sourcePaths?: Partial<{
|
56
|
+
architecture: string;
|
57
|
+
requirements: string;
|
58
|
+
design: string;
|
59
|
+
}>): Promise<{
|
60
|
+
architecture: string;
|
61
|
+
requirements: string;
|
62
|
+
design: string;
|
63
|
+
}>;
|
64
|
+
/**
|
65
|
+
* Check which project documents exist
|
66
|
+
*/
|
67
|
+
getProjectDocsInfo(projectPath: string): Promise<ProjectDocsInfo>;
|
68
|
+
/**
|
69
|
+
* Create project documents using templates (legacy method for backward compatibility)
|
70
|
+
*/
|
71
|
+
createProjectDocs(projectPath: string, options?: TemplateOptions): Promise<{
|
72
|
+
created: string[];
|
73
|
+
skipped: string[];
|
74
|
+
}>;
|
75
|
+
/**
|
76
|
+
* Create or link project documents using templates and/or file paths
|
77
|
+
*/
|
78
|
+
createOrLinkProjectDocs(projectPath: string, templateOptions?: Partial<TemplateOptions>, filePaths?: Partial<{
|
79
|
+
architecture: string;
|
80
|
+
requirements: string;
|
81
|
+
design: string;
|
82
|
+
}>): Promise<CreateOrLinkResult>;
|
83
|
+
/**
|
84
|
+
* Create a symlink to an existing file
|
85
|
+
*/
|
86
|
+
createSymlink(sourcePath: string, targetPath: string): Promise<void>;
|
87
|
+
/**
|
88
|
+
* Remove existing document or symlink
|
89
|
+
*/
|
90
|
+
private removeExistingDocument;
|
91
|
+
/**
|
92
|
+
* Create a single document from template
|
93
|
+
*/
|
94
|
+
private createDocument;
|
95
|
+
/**
|
96
|
+
* Get variable substitutions for workflow instructions
|
97
|
+
*/
|
98
|
+
getVariableSubstitutions(projectPath: string): Record<string, string>;
|
99
|
+
/**
|
100
|
+
* Get variable substitutions for workflow instructions with dynamic paths
|
101
|
+
*/
|
102
|
+
getVariableSubstitutionsWithExtensions(projectPath: string, sourcePaths?: Partial<{
|
103
|
+
architecture: string;
|
104
|
+
requirements: string;
|
105
|
+
design: string;
|
106
|
+
}>): Promise<Record<string, string>>;
|
107
|
+
/**
|
108
|
+
* Read a project document - returns the path for LLM to read as needed
|
109
|
+
*/
|
110
|
+
readDocument(projectPath: string, type: 'architecture' | 'requirements' | 'design'): Promise<string>;
|
111
|
+
/**
|
112
|
+
* Check if all project documents exist
|
113
|
+
*/
|
114
|
+
allDocumentsExist(projectPath: string): Promise<boolean>;
|
115
|
+
/**
|
116
|
+
* Check if a document is a symlink
|
117
|
+
*/
|
118
|
+
isSymlink(projectPath: string, type: 'architecture' | 'requirements' | 'design'): Promise<boolean>;
|
119
|
+
}
|