@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
package/src/logger.ts ADDED
@@ -0,0 +1,353 @@
1
+ /**
2
+ * Logging utility for Vibe Feature MCP Server
3
+ *
4
+ * Provides structured logging with proper MCP compliance:
5
+ * - Uses stderr for all local logging (MCP requirement)
6
+ * - Supports MCP log message notifications to client
7
+ * - Provides structured logging with proper levels:
8
+ * - debug: Tracing and detailed execution flow
9
+ * - info: Success operations and important milestones
10
+ * - warn: Expected errors and recoverable issues
11
+ * - error: Caught but unexpected errors
12
+ */
13
+
14
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
15
+
16
+ export enum LogLevel {
17
+ DEBUG = 0,
18
+ INFO = 1,
19
+ WARN = 2,
20
+ ERROR = 3,
21
+ SILENT = 4, // Suppress all logging
22
+ }
23
+
24
+ export interface LogContext {
25
+ component?: string;
26
+ conversationId?: string;
27
+ phase?: string;
28
+ operation?: string;
29
+ [key: string]: unknown;
30
+ }
31
+
32
+ // Global MCP server reference for log notifications
33
+ let mcpServerInstance: McpServer | null = null;
34
+
35
+ // Unified logging level - can be set by MCP client or environment
36
+ let currentLoggingLevel: LogLevel | null = null;
37
+
38
+ // Test mode detection function to check at runtime
39
+ function isTestMode(): boolean {
40
+ // Check explicit environment variables
41
+ if (process.env.NODE_ENV === 'test' || process.env.VITEST === 'true') {
42
+ return true;
43
+ }
44
+
45
+ // Check if running in a temporary directory (common for tests)
46
+ const cwd = process.cwd();
47
+ if (cwd.includes('/tmp/') || cwd.includes('temp') || cwd.includes('test-')) {
48
+ return true;
49
+ }
50
+
51
+ // Check if LOG_LEVEL is explicitly set to ERROR
52
+ if (process.env.LOG_LEVEL === 'ERROR') {
53
+ return true;
54
+ }
55
+
56
+ return false;
57
+ }
58
+
59
+ /**
60
+ * Set the MCP server instance for log notifications
61
+ */
62
+ export function setMcpServerForLogging(server: McpServer): void {
63
+ mcpServerInstance = server;
64
+ }
65
+
66
+ /**
67
+ * Set the logging level from MCP client request
68
+ */
69
+ export function setMcpLoggingLevel(level: string): void {
70
+ // Map MCP levels to our internal levels
71
+ const levelMap: Record<string, LogLevel> = {
72
+ debug: LogLevel.DEBUG,
73
+ info: LogLevel.INFO,
74
+ notice: LogLevel.INFO,
75
+ warning: LogLevel.WARN,
76
+ error: LogLevel.ERROR,
77
+ critical: LogLevel.ERROR,
78
+ alert: LogLevel.ERROR,
79
+ emergency: LogLevel.ERROR,
80
+ };
81
+
82
+ currentLoggingLevel = levelMap[level] ?? LogLevel.INFO;
83
+ }
84
+
85
+ class Logger {
86
+ private component: string;
87
+ private explicitLogLevel?: LogLevel;
88
+
89
+ constructor(component: string, logLevel?: LogLevel) {
90
+ this.component = component;
91
+ this.explicitLogLevel = logLevel;
92
+ }
93
+
94
+ private getCurrentLogLevel(): LogLevel {
95
+ // Check environment variable first (allows SILENT to override test mode)
96
+ const envLevel = this.getLogLevelFromEnv();
97
+ if (envLevel === LogLevel.SILENT) {
98
+ return LogLevel.SILENT;
99
+ }
100
+
101
+ // Force ERROR level in test environments (unless SILENT)
102
+ if (isTestMode()) {
103
+ return LogLevel.ERROR;
104
+ }
105
+
106
+ // Use MCP-set level if available (takes precedence)
107
+ if (currentLoggingLevel !== null) {
108
+ return currentLoggingLevel;
109
+ }
110
+
111
+ // Use environment variable level
112
+ if (envLevel !== null) {
113
+ return envLevel;
114
+ }
115
+
116
+ // If explicit log level was provided, use it
117
+ if (this.explicitLogLevel !== undefined) {
118
+ return this.explicitLogLevel;
119
+ }
120
+
121
+ // Default to INFO
122
+ return LogLevel.INFO;
123
+ }
124
+
125
+ private getLogLevelFromEnv(): LogLevel | null {
126
+ const envLevel = process.env.LOG_LEVEL?.toUpperCase();
127
+ switch (envLevel) {
128
+ case 'DEBUG':
129
+ return LogLevel.DEBUG;
130
+ case 'INFO':
131
+ return LogLevel.INFO;
132
+ case 'WARN':
133
+ return LogLevel.WARN;
134
+ case 'ERROR':
135
+ return LogLevel.ERROR;
136
+ case 'SILENT':
137
+ return LogLevel.SILENT;
138
+ default:
139
+ return null;
140
+ }
141
+ }
142
+
143
+ private shouldLog(level: LogLevel): boolean {
144
+ return level >= this.getCurrentLogLevel();
145
+ }
146
+
147
+ private formatMessage(
148
+ level: string,
149
+ message: string,
150
+ context?: LogContext
151
+ ): string {
152
+ const timestamp = new Date().toISOString();
153
+ const contextStr = context ? ` ${JSON.stringify(context)}` : '';
154
+ return `[${timestamp}] ${level.toUpperCase()} [${this.component}] ${message}${contextStr}`;
155
+ }
156
+
157
+ /**
158
+ * Send log message to MCP client if server is available and level is appropriate
159
+ */
160
+ private async sendMcpLogMessage(
161
+ level: 'debug' | 'info' | 'warning' | 'error',
162
+ message: string,
163
+ context?: LogContext
164
+ ): Promise<void> {
165
+ if (mcpServerInstance) {
166
+ try {
167
+ // Safely serialize context to avoid JSON issues
168
+ let logData = message;
169
+ if (context) {
170
+ try {
171
+ const contextStr = JSON.stringify(context, null, 0);
172
+ logData = `${message} ${contextStr}`;
173
+ } catch (_error) {
174
+ // If JSON serialization fails, just use the message
175
+ logData = `${message} [context serialization failed]`;
176
+ }
177
+ }
178
+
179
+ await mcpServerInstance.server.notification({
180
+ method: 'notifications/message',
181
+ params: {
182
+ level,
183
+ logger: this.component,
184
+ data: logData,
185
+ },
186
+ });
187
+ } catch (error) {
188
+ // Fallback to stderr if MCP notification fails
189
+ // Don't use this.error to avoid infinite recursion
190
+ if (!isTestMode()) {
191
+ process.stderr.write(
192
+ `[MCP-LOG-ERROR] Failed to send log notification: ${error}\n`
193
+ );
194
+ }
195
+ }
196
+ }
197
+ }
198
+
199
+ debug(message: string, context?: LogContext): void {
200
+ if (this.shouldLog(LogLevel.DEBUG)) {
201
+ const formattedMessage = this.formatMessage('debug', message, context);
202
+ // Always log to stderr for MCP compliance
203
+ process.stderr.write(formattedMessage + '\n');
204
+ // Also send to MCP client if available (only for debug level)
205
+ this.sendMcpLogMessage('debug', message, context).catch(() => {
206
+ // Ignore MCP notification errors for debug messages
207
+ });
208
+ }
209
+ }
210
+
211
+ info(message: string, context?: LogContext): void {
212
+ if (this.shouldLog(LogLevel.INFO)) {
213
+ const formattedMessage = this.formatMessage('info', message, context);
214
+ // Always log to stderr for MCP compliance
215
+ process.stderr.write(formattedMessage + '\n');
216
+
217
+ // Send enhanced notifications for important events
218
+ this.sendEnhancedMcpNotification('info', message, context).catch(() => {
219
+ // Ignore MCP notification errors for info messages
220
+ });
221
+ }
222
+ }
223
+
224
+ /**
225
+ * Send enhanced MCP notifications with better formatting for important events
226
+ */
227
+ private async sendEnhancedMcpNotification(
228
+ level: 'debug' | 'info' | 'warning' | 'error',
229
+ message: string,
230
+ context?: LogContext
231
+ ): Promise<void> {
232
+ if (mcpServerInstance) {
233
+ try {
234
+ let enhancedMessage = message;
235
+ let notificationLevel = level;
236
+
237
+ // Enhance phase transition messages
238
+ if (
239
+ context &&
240
+ (context.from || context.to) &&
241
+ message.includes('transition')
242
+ ) {
243
+ const from = context.from
244
+ ? this.capitalizePhase(context.from as string)
245
+ : '';
246
+ const to = context.to
247
+ ? this.capitalizePhase(context.to as string)
248
+ : '';
249
+ if (from && to) {
250
+ enhancedMessage = `Phase Transition: ${from} → ${to}`;
251
+ notificationLevel = 'info';
252
+ }
253
+ }
254
+
255
+ // Enhance initialization messages
256
+ if (message.includes('initialized successfully')) {
257
+ enhancedMessage = '🚀 Vibe Feature MCP Server Ready';
258
+ notificationLevel = 'info';
259
+ }
260
+
261
+ // Safely serialize context to avoid JSON issues
262
+ let logData = enhancedMessage;
263
+ if (context) {
264
+ try {
265
+ const contextStr = JSON.stringify(context, null, 0);
266
+ logData = `${enhancedMessage} ${contextStr}`;
267
+ } catch (_error) {
268
+ // If JSON serialization fails, just use the message
269
+ logData = `${enhancedMessage} [context serialization failed]`;
270
+ }
271
+ }
272
+
273
+ // Use the underlying server's notification method
274
+ await mcpServerInstance.server.notification({
275
+ method: 'notifications/message',
276
+ params: {
277
+ level: notificationLevel,
278
+ logger: this.component,
279
+ data: logData,
280
+ },
281
+ });
282
+ } catch (error) {
283
+ // Fallback to stderr if MCP notification fails
284
+ // Don't use this.error to avoid infinite recursion
285
+ if (!isTestMode()) {
286
+ process.stderr.write(
287
+ `[MCP-LOG-ERROR] Failed to send log notification: ${error}\n`
288
+ );
289
+ }
290
+ }
291
+ }
292
+ }
293
+
294
+ /**
295
+ * Capitalize phase name for display
296
+ */
297
+ private capitalizePhase(phase: string): string {
298
+ return phase
299
+ .split('_')
300
+ .map(word => word.charAt(0).toUpperCase() + word.slice(1))
301
+ .join(' ');
302
+ }
303
+
304
+ warn(message: string, context?: LogContext): void {
305
+ if (this.shouldLog(LogLevel.WARN)) {
306
+ const formattedMessage = this.formatMessage('warn', message, context);
307
+ // Always log to stderr for MCP compliance
308
+ process.stderr.write(formattedMessage + '\n');
309
+ // Also send to MCP client if available
310
+ this.sendEnhancedMcpNotification('warning', message, context).catch(
311
+ () => {
312
+ // Ignore MCP notification errors for warn messages
313
+ }
314
+ );
315
+ }
316
+ }
317
+
318
+ error(message: string, error?: Error, context?: LogContext): void {
319
+ if (this.shouldLog(LogLevel.ERROR)) {
320
+ const errorContext = error
321
+ ? { ...context, error: error.message, stack: error.stack }
322
+ : context;
323
+ const formattedMessage = this.formatMessage(
324
+ 'error',
325
+ message,
326
+ errorContext
327
+ );
328
+ // Always log to stderr for MCP compliance
329
+ process.stderr.write(formattedMessage + '\n');
330
+ // Also send to MCP client if available
331
+ this.sendEnhancedMcpNotification('error', message, errorContext).catch(
332
+ () => {
333
+ // Ignore MCP notification errors for error messages
334
+ }
335
+ );
336
+ }
337
+ }
338
+
339
+ child(childComponent: string): Logger {
340
+ return new Logger(
341
+ `${this.component}:${childComponent}`,
342
+ this.explicitLogLevel
343
+ );
344
+ }
345
+ }
346
+
347
+ // Factory function to create loggers
348
+ export function createLogger(component: string, logLevel?: LogLevel): Logger {
349
+ return new Logger(component, logLevel);
350
+ }
351
+
352
+ // Default logger for the main application
353
+ export const logger = createLogger('VibeFeatureMCP');
@@ -0,0 +1,261 @@
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
+
8
+ import { access, stat } from 'node:fs/promises';
9
+ import { resolve, isAbsolute, join, normalize } from 'node:path';
10
+ import { createLogger } from './logger.js';
11
+
12
+ const logger = createLogger('PathValidationUtils');
13
+
14
+ export interface PathValidationResult {
15
+ isValid: boolean;
16
+ resolvedPath?: string;
17
+ error?: string;
18
+ }
19
+
20
+ export class PathValidationUtils {
21
+ /**
22
+ * Validate if a string is a known template name
23
+ */
24
+ static isTemplateName(value: string, availableTemplates: string[]): boolean {
25
+ return availableTemplates.includes(value);
26
+ }
27
+
28
+ /**
29
+ * Validate and resolve a file path
30
+ */
31
+ static async validateFilePath(
32
+ filePath: string,
33
+ projectPath: string
34
+ ): Promise<PathValidationResult> {
35
+ try {
36
+ // Resolve the path to absolute
37
+ const resolvedPath = this.resolvePath(filePath, projectPath);
38
+
39
+ // Security validation - prevent directory traversal
40
+ if (!this.isPathSafe(resolvedPath, projectPath)) {
41
+ return {
42
+ isValid: false,
43
+ error: 'Path is outside project boundaries for security reasons',
44
+ };
45
+ }
46
+
47
+ // Check if file exists and is readable
48
+ await access(resolvedPath);
49
+
50
+ // Verify it's a file (not a directory)
51
+ const stats = await stat(resolvedPath);
52
+ if (!stats.isFile()) {
53
+ return {
54
+ isValid: false,
55
+ error: 'Path points to a directory, not a file',
56
+ };
57
+ }
58
+
59
+ logger.debug('File path validated successfully', {
60
+ originalPath: filePath,
61
+ resolvedPath,
62
+ });
63
+
64
+ return {
65
+ isValid: true,
66
+ resolvedPath,
67
+ };
68
+ } catch (error) {
69
+ const errorMessage =
70
+ error instanceof Error ? error.message : 'Unknown error';
71
+
72
+ logger.debug('File path validation failed', {
73
+ filePath,
74
+ error: errorMessage,
75
+ });
76
+
77
+ return {
78
+ isValid: false,
79
+ error: `File not found or not accessible: ${errorMessage}`,
80
+ };
81
+ }
82
+ }
83
+
84
+ /**
85
+ * Validate and resolve a file or directory path
86
+ */
87
+ static async validateFileOrDirectoryPath(
88
+ filePath: string,
89
+ projectPath: string
90
+ ): Promise<PathValidationResult> {
91
+ try {
92
+ // Resolve the path to absolute
93
+ const resolvedPath = this.resolvePath(filePath, projectPath);
94
+
95
+ // Security validation - prevent directory traversal
96
+ if (!this.isPathSafe(resolvedPath, projectPath)) {
97
+ return {
98
+ isValid: false,
99
+ error: 'Path is outside project boundaries for security reasons',
100
+ };
101
+ }
102
+
103
+ // Check if file or directory exists and is readable
104
+ await access(resolvedPath);
105
+
106
+ // Verify it's either a file or directory
107
+ const stats = await stat(resolvedPath);
108
+ if (!stats.isFile() && !stats.isDirectory()) {
109
+ return {
110
+ isValid: false,
111
+ error: 'Path is neither a file nor a directory',
112
+ };
113
+ }
114
+
115
+ logger.debug('File or directory path validated successfully', {
116
+ originalPath: filePath,
117
+ resolvedPath,
118
+ isFile: stats.isFile(),
119
+ isDirectory: stats.isDirectory(),
120
+ });
121
+
122
+ return {
123
+ isValid: true,
124
+ resolvedPath,
125
+ };
126
+ } catch (error) {
127
+ const errorMessage =
128
+ error instanceof Error ? error.message : 'Unknown error';
129
+
130
+ logger.debug('File or directory path validation failed', {
131
+ filePath,
132
+ error: errorMessage,
133
+ });
134
+
135
+ return {
136
+ isValid: false,
137
+ error: `File or directory not found or not accessible: ${errorMessage}`,
138
+ };
139
+ }
140
+ }
141
+
142
+ /**
143
+ * Resolve a file path to absolute, handling various formats
144
+ */
145
+ static resolvePath(filePath: string, projectPath: string): string {
146
+ // If already absolute, return as-is
147
+ if (isAbsolute(filePath)) {
148
+ return normalize(filePath);
149
+ }
150
+
151
+ // Handle relative paths (./file, ../file, file)
152
+ return resolve(projectPath, filePath);
153
+ }
154
+
155
+ /**
156
+ * Check if a resolved path is within safe boundaries
157
+ * Prevents directory traversal attacks
158
+ */
159
+ static isPathSafe(resolvedPath: string, projectPath: string): boolean {
160
+ const normalizedResolved = normalize(resolvedPath);
161
+ const normalizedProject = normalize(projectPath);
162
+
163
+ // Allow paths within the project directory
164
+ if (normalizedResolved.startsWith(normalizedProject)) {
165
+ return true;
166
+ }
167
+
168
+ // Allow paths in common documentation locations relative to project
169
+ const allowedPaths = [
170
+ normalize(join(projectPath, '..')), // Parent directory (for monorepos)
171
+ '/usr/share/doc', // System documentation
172
+ '/opt/docs', // Optional documentation
173
+ ];
174
+
175
+ return allowedPaths.some(allowedPath =>
176
+ normalizedResolved.startsWith(allowedPath)
177
+ );
178
+ }
179
+
180
+ /**
181
+ * Validate parameter as either template name or file path
182
+ */
183
+ static async validateParameter(
184
+ value: string,
185
+ availableTemplates: string[],
186
+ projectPath: string
187
+ ): Promise<{
188
+ isTemplate: boolean;
189
+ isFilePath: boolean;
190
+ resolvedPath?: string;
191
+ error?: string;
192
+ }> {
193
+ // First check if it's a template name
194
+ if (this.isTemplateName(value, availableTemplates)) {
195
+ return {
196
+ isTemplate: true,
197
+ isFilePath: false,
198
+ };
199
+ }
200
+
201
+ // Then validate as file or directory path
202
+ const pathValidation = await this.validateFileOrDirectoryPath(
203
+ value,
204
+ projectPath
205
+ );
206
+
207
+ if (pathValidation.isValid) {
208
+ return {
209
+ isTemplate: false,
210
+ isFilePath: true,
211
+ resolvedPath: pathValidation.resolvedPath,
212
+ };
213
+ }
214
+
215
+ // Neither template nor valid file/directory path
216
+ return {
217
+ isTemplate: false,
218
+ isFilePath: false,
219
+ error: `Invalid parameter: not a known template (${availableTemplates.join(', ')}) and not a valid file or directory path (${pathValidation.error})`,
220
+ };
221
+ }
222
+
223
+ /**
224
+ * Get common file patterns for documentation
225
+ */
226
+ static getCommonDocumentationPatterns(): {
227
+ architecture: string[];
228
+ requirements: string[];
229
+ design: string[];
230
+ } {
231
+ return {
232
+ architecture: [
233
+ 'ARCHITECTURE.md',
234
+ 'ARCHITECTURE.txt',
235
+ 'architecture.md',
236
+ 'Architecture.md',
237
+ 'docs/ARCHITECTURE.md',
238
+ 'docs/architecture.md',
239
+ 'README.md', // Can contain architecture info
240
+ ],
241
+ requirements: [
242
+ 'REQUIREMENTS.md',
243
+ 'REQUIREMENTS.txt',
244
+ 'requirements.md',
245
+ 'Requirements.md',
246
+ 'docs/REQUIREMENTS.md',
247
+ 'docs/requirements.md',
248
+ 'README.md', // Often contains requirements
249
+ ],
250
+ design: [
251
+ 'DESIGN.md',
252
+ 'DESIGN.txt',
253
+ 'design.md',
254
+ 'Design.md',
255
+ 'docs/DESIGN.md',
256
+ 'docs/design.md',
257
+ 'README.md', // Can contain design info
258
+ ],
259
+ };
260
+ }
261
+ }