@codemcp/workflows 3.1.21

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 (159) hide show
  1. package/.turbo/turbo-build.log +4 -0
  2. package/.vibe/conversation-state.sqlite +0 -0
  3. package/LICENSE +674 -0
  4. package/dist/index.d.ts +9 -0
  5. package/dist/index.d.ts.map +1 -0
  6. package/dist/index.js +74 -0
  7. package/dist/index.js.map +1 -0
  8. package/dist/notification-service.d.ts +14 -0
  9. package/dist/notification-service.d.ts.map +1 -0
  10. package/dist/notification-service.js +18 -0
  11. package/dist/notification-service.js.map +1 -0
  12. package/dist/resource-handlers/conversation-state.d.ts +15 -0
  13. package/dist/resource-handlers/conversation-state.d.ts.map +1 -0
  14. package/dist/resource-handlers/conversation-state.js +40 -0
  15. package/dist/resource-handlers/conversation-state.js.map +1 -0
  16. package/dist/resource-handlers/development-plan.d.ts +14 -0
  17. package/dist/resource-handlers/development-plan.d.ts.map +1 -0
  18. package/dist/resource-handlers/development-plan.js +31 -0
  19. package/dist/resource-handlers/development-plan.js.map +1 -0
  20. package/dist/resource-handlers/index.d.ts +24 -0
  21. package/dist/resource-handlers/index.d.ts.map +1 -0
  22. package/dist/resource-handlers/index.js +62 -0
  23. package/dist/resource-handlers/index.js.map +1 -0
  24. package/dist/resource-handlers/system-prompt.d.ts +15 -0
  25. package/dist/resource-handlers/system-prompt.d.ts.map +1 -0
  26. package/dist/resource-handlers/system-prompt.js +40 -0
  27. package/dist/resource-handlers/system-prompt.js.map +1 -0
  28. package/dist/resource-handlers/workflow-resource.d.ts +15 -0
  29. package/dist/resource-handlers/workflow-resource.d.ts.map +1 -0
  30. package/dist/resource-handlers/workflow-resource.js +85 -0
  31. package/dist/resource-handlers/workflow-resource.js.map +1 -0
  32. package/dist/response-renderer.d.ts +30 -0
  33. package/dist/response-renderer.d.ts.map +1 -0
  34. package/dist/response-renderer.js +94 -0
  35. package/dist/response-renderer.js.map +1 -0
  36. package/dist/server-config.d.ts +34 -0
  37. package/dist/server-config.d.ts.map +1 -0
  38. package/dist/server-config.js +486 -0
  39. package/dist/server-config.js.map +1 -0
  40. package/dist/server-helpers.d.ts +62 -0
  41. package/dist/server-helpers.d.ts.map +1 -0
  42. package/dist/server-helpers.js +156 -0
  43. package/dist/server-helpers.js.map +1 -0
  44. package/dist/server-implementation.d.ts +74 -0
  45. package/dist/server-implementation.d.ts.map +1 -0
  46. package/dist/server-implementation.js +201 -0
  47. package/dist/server-implementation.js.map +1 -0
  48. package/dist/server.d.ts +6 -0
  49. package/dist/server.d.ts.map +1 -0
  50. package/dist/server.js +5 -0
  51. package/dist/server.js.map +1 -0
  52. package/dist/tool-handlers/base-tool-handler.d.ts +50 -0
  53. package/dist/tool-handlers/base-tool-handler.d.ts.map +1 -0
  54. package/dist/tool-handlers/base-tool-handler.js +74 -0
  55. package/dist/tool-handlers/base-tool-handler.js.map +1 -0
  56. package/dist/tool-handlers/conduct-review.d.ts +49 -0
  57. package/dist/tool-handlers/conduct-review.d.ts.map +1 -0
  58. package/dist/tool-handlers/conduct-review.js +105 -0
  59. package/dist/tool-handlers/conduct-review.js.map +1 -0
  60. package/dist/tool-handlers/get-tool-info.d.ts +76 -0
  61. package/dist/tool-handlers/get-tool-info.d.ts.map +1 -0
  62. package/dist/tool-handlers/get-tool-info.js +168 -0
  63. package/dist/tool-handlers/get-tool-info.js.map +1 -0
  64. package/dist/tool-handlers/index.d.ts +42 -0
  65. package/dist/tool-handlers/index.d.ts.map +1 -0
  66. package/dist/tool-handlers/index.js +74 -0
  67. package/dist/tool-handlers/index.js.map +1 -0
  68. package/dist/tool-handlers/install-workflow.d.ts +48 -0
  69. package/dist/tool-handlers/install-workflow.d.ts.map +1 -0
  70. package/dist/tool-handlers/install-workflow.js +131 -0
  71. package/dist/tool-handlers/install-workflow.js.map +1 -0
  72. package/dist/tool-handlers/list-workflows.d.ts +47 -0
  73. package/dist/tool-handlers/list-workflows.d.ts.map +1 -0
  74. package/dist/tool-handlers/list-workflows.js +58 -0
  75. package/dist/tool-handlers/list-workflows.js.map +1 -0
  76. package/dist/tool-handlers/no-idea.d.ts +41 -0
  77. package/dist/tool-handlers/no-idea.d.ts.map +1 -0
  78. package/dist/tool-handlers/no-idea.js +29 -0
  79. package/dist/tool-handlers/no-idea.js.map +1 -0
  80. package/dist/tool-handlers/proceed-to-phase.d.ts +39 -0
  81. package/dist/tool-handlers/proceed-to-phase.d.ts.map +1 -0
  82. package/dist/tool-handlers/proceed-to-phase.js +109 -0
  83. package/dist/tool-handlers/proceed-to-phase.js.map +1 -0
  84. package/dist/tool-handlers/reset-development.d.ts +31 -0
  85. package/dist/tool-handlers/reset-development.d.ts.map +1 -0
  86. package/dist/tool-handlers/reset-development.js +48 -0
  87. package/dist/tool-handlers/reset-development.js.map +1 -0
  88. package/dist/tool-handlers/resume-workflow.d.ts +88 -0
  89. package/dist/tool-handlers/resume-workflow.d.ts.map +1 -0
  90. package/dist/tool-handlers/resume-workflow.js +213 -0
  91. package/dist/tool-handlers/resume-workflow.js.map +1 -0
  92. package/dist/tool-handlers/setup-project-docs.d.ts +36 -0
  93. package/dist/tool-handlers/setup-project-docs.d.ts.map +1 -0
  94. package/dist/tool-handlers/setup-project-docs.js +136 -0
  95. package/dist/tool-handlers/setup-project-docs.js.map +1 -0
  96. package/dist/tool-handlers/start-development.d.ts +82 -0
  97. package/dist/tool-handlers/start-development.d.ts.map +1 -0
  98. package/dist/tool-handlers/start-development.js +448 -0
  99. package/dist/tool-handlers/start-development.js.map +1 -0
  100. package/dist/tool-handlers/whats-next.d.ts +42 -0
  101. package/dist/tool-handlers/whats-next.d.ts.map +1 -0
  102. package/dist/tool-handlers/whats-next.js +118 -0
  103. package/dist/tool-handlers/whats-next.js.map +1 -0
  104. package/dist/types.d.ts +114 -0
  105. package/dist/types.d.ts.map +1 -0
  106. package/dist/types.js +5 -0
  107. package/dist/types.js.map +1 -0
  108. package/package.json +29 -0
  109. package/src/index.ts +93 -0
  110. package/src/notification-service.ts +23 -0
  111. package/src/resource-handlers/conversation-state.ts +55 -0
  112. package/src/resource-handlers/development-plan.ts +48 -0
  113. package/src/resource-handlers/index.ts +73 -0
  114. package/src/resource-handlers/system-prompt.ts +55 -0
  115. package/src/resource-handlers/workflow-resource.ts +126 -0
  116. package/src/response-renderer.ts +116 -0
  117. package/src/server-config.ts +744 -0
  118. package/src/server-helpers.ts +225 -0
  119. package/src/server-implementation.ts +277 -0
  120. package/src/server.ts +9 -0
  121. package/src/tool-handlers/base-tool-handler.ts +141 -0
  122. package/src/tool-handlers/conduct-review.ts +191 -0
  123. package/src/tool-handlers/get-tool-info.ts +274 -0
  124. package/src/tool-handlers/index.ts +117 -0
  125. package/src/tool-handlers/install-workflow.ts +185 -0
  126. package/src/tool-handlers/list-workflows.ts +94 -0
  127. package/src/tool-handlers/no-idea.ts +47 -0
  128. package/src/tool-handlers/proceed-to-phase.ts +205 -0
  129. package/src/tool-handlers/reset-development.ts +90 -0
  130. package/src/tool-handlers/resume-workflow.ts +380 -0
  131. package/src/tool-handlers/setup-project-docs.ts +226 -0
  132. package/src/tool-handlers/start-development.ts +685 -0
  133. package/src/tool-handlers/whats-next.ts +235 -0
  134. package/src/types.ts +130 -0
  135. package/test/e2e/core-functionality.test.ts +176 -0
  136. package/test/e2e/mcp-contract.test.ts +540 -0
  137. package/test/e2e/plan-management.test.ts +331 -0
  138. package/test/e2e/state-management.test.ts +392 -0
  139. package/test/e2e/workflow-integration.test.ts +506 -0
  140. package/test/unit/commit-behaviour-interface.test.ts +244 -0
  141. package/test/unit/conduct-review.test.ts +151 -0
  142. package/test/unit/reset-functionality.test.ts +72 -0
  143. package/test/unit/resume-workflow.test.ts +192 -0
  144. package/test/unit/server-tools.test.ts +311 -0
  145. package/test/unit/setup-project-docs-handler.test.ts +267 -0
  146. package/test/unit/start-development-artifact-detection.test.ts +387 -0
  147. package/test/unit/start-development-gitignore.test.ts +178 -0
  148. package/test/unit/system-prompt-resource.test.ts +101 -0
  149. package/test/unit/tool-handlers/no-idea.test.ts +40 -0
  150. package/test/utils/e2e-test-setup.ts +453 -0
  151. package/test/utils/run-server-in-dir.sh +27 -0
  152. package/test/utils/temp-files.ts +308 -0
  153. package/test/utils/test-access.ts +79 -0
  154. package/test/utils/test-helpers.ts +286 -0
  155. package/test/utils/test-setup.ts +78 -0
  156. package/tsconfig.build.json +9 -0
  157. package/tsconfig.build.tsbuildinfo +1 -0
  158. package/tsconfig.json +12 -0
  159. package/vitest.config.ts +17 -0
@@ -0,0 +1,685 @@
1
+ /**
2
+ * StartDevelopment Tool Handler
3
+ *
4
+ * Handles initialization of development workflow and transition to the initial
5
+ * development phase. Allows users to choose from predefined workflows or use a custom workflow.
6
+ */
7
+
8
+ import { BaseToolHandler } from './base-tool-handler.js';
9
+ import { validateRequiredArgs } from '../server-helpers.js';
10
+ import { basename } from 'node:path';
11
+ import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs';
12
+ import { resolve } from 'node:path';
13
+ import { GitCommitConfig } from '@codemcp/workflows-core';
14
+ import { GitManager } from '@codemcp/workflows-core';
15
+ import type { YamlStateMachine } from '@codemcp/workflows-core';
16
+ import { ProjectDocsManager, ProjectDocsInfo } from '@codemcp/workflows-core';
17
+ import { ServerContext } from '../types.js';
18
+
19
+ /**
20
+ * Arguments for the start_development tool
21
+ */
22
+ export interface StartDevelopmentArgs {
23
+ workflow: string;
24
+ commit_behaviour?: 'step' | 'phase' | 'end' | 'none';
25
+ require_reviews?: boolean;
26
+ }
27
+
28
+ /**
29
+ * Response from the start_development tool
30
+ */
31
+ export interface StartDevelopmentResult {
32
+ phase: string;
33
+ instructions: string;
34
+ plan_file_path: string;
35
+ conversation_id: string;
36
+ workflow: YamlStateMachine;
37
+ workflowDocumentationUrl?: string;
38
+ }
39
+
40
+ /**
41
+ * StartDevelopment tool handler implementation
42
+ */
43
+ export class StartDevelopmentHandler extends BaseToolHandler<
44
+ StartDevelopmentArgs,
45
+ StartDevelopmentResult
46
+ > {
47
+ private projectDocsManager: ProjectDocsManager;
48
+
49
+ constructor() {
50
+ super();
51
+ this.projectDocsManager = new ProjectDocsManager();
52
+ }
53
+
54
+ protected async executeHandler(
55
+ args: StartDevelopmentArgs,
56
+ context: ServerContext
57
+ ): Promise<StartDevelopmentResult> {
58
+ // Validate required arguments
59
+ validateRequiredArgs(args, ['workflow']);
60
+
61
+ const selectedWorkflow = args.workflow;
62
+ const requireReviews = args.require_reviews ?? false;
63
+
64
+ // Process git commit configuration
65
+ const isGitRepository = GitManager.isGitRepository(context.projectPath);
66
+
67
+ // Translate commit_behaviour to internal git config
68
+ const commitBehaviour =
69
+ args.commit_behaviour ?? (isGitRepository ? 'end' : 'none');
70
+ const gitCommitConfig: GitCommitConfig = {
71
+ enabled: commitBehaviour !== 'none',
72
+ commitOnStep: commitBehaviour === 'step',
73
+ commitOnPhase: commitBehaviour === 'phase',
74
+ commitOnComplete:
75
+ commitBehaviour === 'end' ||
76
+ commitBehaviour === 'step' ||
77
+ commitBehaviour === 'phase',
78
+ initialMessage: 'Development session',
79
+ startCommitHash:
80
+ GitManager.getCurrentCommitHash(context.projectPath) || undefined,
81
+ };
82
+
83
+ this.logger.debug('Processing start_development request', {
84
+ selectedWorkflow,
85
+ projectPath: context.projectPath,
86
+ commitBehaviour,
87
+ gitCommitConfig,
88
+ });
89
+
90
+ // Validate workflow selection (ensure project workflows are loaded first)
91
+ context.workflowManager.loadProjectWorkflows(context.projectPath);
92
+ if (
93
+ !context.workflowManager.validateWorkflowName(
94
+ selectedWorkflow,
95
+ context.projectPath
96
+ )
97
+ ) {
98
+ const availableWorkflows = context.workflowManager.getWorkflowNames();
99
+ throw new Error(
100
+ `Invalid workflow: ${selectedWorkflow}. Available workflows: ${availableWorkflows.join(', ')}`
101
+ );
102
+ }
103
+
104
+ // Check for project documentation artifacts and guide setup if needed
105
+ const artifactGuidance = await this.checkProjectArtifacts(
106
+ context.projectPath,
107
+ selectedWorkflow,
108
+ context
109
+ );
110
+ if (artifactGuidance) {
111
+ return artifactGuidance;
112
+ }
113
+
114
+ // Check if user is on main/master branch and prompt for branch creation
115
+ const currentBranch = this.getCurrentGitBranch(context.projectPath);
116
+ if (currentBranch === 'main' || currentBranch === 'master') {
117
+ const suggestedBranchName = this.generateBranchSuggestion();
118
+ const branchPromptResponse: StartDevelopmentResult = {
119
+ phase: 'branch-prompt',
120
+ instructions: `You're currently on the ${currentBranch} branch. It's recommended to create a feature branch for development. Propose a branch creation by suggesting a branch command to the user call start_development again.\n\nSuggested command: \`git checkout -b ${suggestedBranchName}\`\n\nPlease create a new branch and then call start_development again to begin development.`,
121
+ plan_file_path: '',
122
+ conversation_id: '',
123
+ workflow: {} as YamlStateMachine,
124
+ };
125
+
126
+ this.logger.debug(
127
+ 'User on main/master branch, prompting for branch creation',
128
+ {
129
+ currentBranch,
130
+ suggestedBranchName,
131
+ }
132
+ );
133
+
134
+ return branchPromptResponse;
135
+ }
136
+
137
+ // Create or get conversation context with the selected workflow
138
+ const conversationContext =
139
+ await context.conversationManager.createConversationContext(
140
+ selectedWorkflow
141
+ );
142
+ const currentPhase = conversationContext.currentPhase;
143
+
144
+ // Load the selected workflow
145
+ const stateMachine = context.workflowManager.loadWorkflowForProject(
146
+ conversationContext.projectPath,
147
+ selectedWorkflow
148
+ );
149
+ const initialState = stateMachine.initial_state;
150
+
151
+ // Check if development is already started
152
+ if (currentPhase !== initialState) {
153
+ throw new Error(
154
+ `Development already started. Current phase is '${currentPhase}', not initial state '${initialState}'. Use whats_next() to continue development.`
155
+ );
156
+ }
157
+
158
+ // The initial state IS the first development phase - it's explicitly modeled
159
+ const targetPhase = initialState;
160
+
161
+ // Transition to the initial development phase
162
+ const transitionResult =
163
+ await context.transitionEngine.handleExplicitTransition(
164
+ currentPhase,
165
+ targetPhase,
166
+ conversationContext.projectPath,
167
+ 'Development initialization',
168
+ selectedWorkflow
169
+ );
170
+
171
+ // Update conversation state with workflow, phase, and git commit configuration
172
+ await context.conversationManager.updateConversationState(
173
+ conversationContext.conversationId,
174
+ {
175
+ currentPhase: transitionResult.newPhase,
176
+ workflowName: selectedWorkflow,
177
+ gitCommitConfig: gitCommitConfig,
178
+ requireReviewsBeforePhaseTransition: requireReviews,
179
+ }
180
+ );
181
+
182
+ // Set state machine on plan manager before creating plan file
183
+ context.planManager.setStateMachine(stateMachine);
184
+
185
+ // Ensure plan file exists
186
+ await context.planManager.ensurePlanFile(
187
+ conversationContext.planFilePath,
188
+ conversationContext.projectPath,
189
+ conversationContext.gitBranch
190
+ );
191
+
192
+ // Ensure .vibe/.gitignore exists to exclude SQLite files for git repositories
193
+ this.ensureGitignoreEntry(conversationContext.projectPath);
194
+
195
+ // Generate workflow documentation URL
196
+ const workflowDocumentationUrl =
197
+ this.generateWorkflowDocumentationUrl(selectedWorkflow);
198
+
199
+ // Generate instructions with simple i18n guidance
200
+ const baseInstructions = `Look at the plan file (${conversationContext.planFilePath}). Define entrance criteria for each phase of the workflow except the initial phase. Those criteria shall be based on the contents of the previous phase. \n Example: \n \`\`\`\n ## Design\n\n ### Phase Entrance Criteria:\n - [ ] The requirements have been thoroughly defined.\n - [ ] Alternatives have been evaluated and are documented. \n - [ ] It's clear what's in scope and out of scope\n \`\`\`\n \n IMPORTANT: Once you added reasonable entrance call the whats_next() tool to get guided instructions for the next current phase.`;
201
+
202
+ const i18nGuidance = `\n\nNOTE: If the user is communicating in a non-English language, please translate the plan file content to that language while keeping the structure intact, and continue all interactions in the user's language.`;
203
+
204
+ // Add workflow documentation information if available
205
+ const workflowDocumentationInfo = workflowDocumentationUrl
206
+ ? `\n\nInform the user about the chose workflow: He can visit: ${workflowDocumentationUrl} to get detailed information.`
207
+ : '';
208
+
209
+ const finalInstructions =
210
+ baseInstructions + workflowDocumentationInfo + i18nGuidance;
211
+
212
+ const response: StartDevelopmentResult = {
213
+ phase: transitionResult.newPhase,
214
+ instructions: finalInstructions,
215
+ plan_file_path: conversationContext.planFilePath,
216
+ conversation_id: conversationContext.conversationId,
217
+ workflow: stateMachine,
218
+ workflowDocumentationUrl,
219
+ };
220
+
221
+ // Log interaction
222
+ await this.logInteraction(
223
+ context,
224
+ conversationContext.conversationId,
225
+ 'start_development',
226
+ args,
227
+ response,
228
+ transitionResult.newPhase
229
+ );
230
+
231
+ return response;
232
+ }
233
+
234
+ /**
235
+ * Check if project documentation artifacts exist and provide setup guidance if needed
236
+ * Dynamically analyzes the selected workflow to determine which documents are referenced
237
+ * Blocks workflow start if the workflow requires documentation
238
+ */
239
+ private async checkProjectArtifacts(
240
+ projectPath: string,
241
+ workflowName: string,
242
+ context: ServerContext
243
+ ): Promise<StartDevelopmentResult | null> {
244
+ try {
245
+ // Load the workflow to analyze its content
246
+ const stateMachine = context.workflowManager.loadWorkflowForProject(
247
+ projectPath,
248
+ workflowName
249
+ );
250
+
251
+ // Check if this workflow requires documentation (defaults to false)
252
+ const requiresDocumentation =
253
+ stateMachine.metadata?.requiresDocumentation ?? false;
254
+
255
+ // If workflow doesn't require documentation, skip artifact check entirely
256
+ if (!requiresDocumentation) {
257
+ this.logger.debug(
258
+ 'Workflow does not require documentation, skipping artifact check',
259
+ { workflowName, requiresDocumentation }
260
+ );
261
+ return null;
262
+ }
263
+
264
+ // Analyze workflow content to detect referenced document variables
265
+ const referencedVariables = this.analyzeWorkflowDocumentReferences(
266
+ stateMachine,
267
+ projectPath
268
+ );
269
+
270
+ // If no document variables are referenced, skip artifact check
271
+ if (referencedVariables.length === 0) {
272
+ this.logger.debug(
273
+ 'No document variables found in workflow, skipping artifact check',
274
+ { workflowName }
275
+ );
276
+ return null;
277
+ }
278
+
279
+ // Check which referenced documents are missing
280
+ const docsInfo =
281
+ await this.projectDocsManager.getProjectDocsInfo(projectPath);
282
+ const missingDocs = this.getMissingReferencedDocuments(
283
+ referencedVariables,
284
+ docsInfo,
285
+ projectPath
286
+ );
287
+
288
+ // If all referenced documents exist, continue with normal flow
289
+ if (missingDocs.length === 0) {
290
+ this.logger.debug(
291
+ 'All referenced project artifacts exist, continuing with development',
292
+ {
293
+ workflowName,
294
+ referencedVariables,
295
+ }
296
+ );
297
+ return null;
298
+ }
299
+
300
+ // Generate guidance for setting up missing artifacts
301
+ const setupGuidance = await this.generateArtifactSetupGuidance(
302
+ missingDocs,
303
+ workflowName,
304
+ docsInfo,
305
+ referencedVariables
306
+ );
307
+
308
+ this.logger.info(
309
+ 'Missing required project artifacts detected for workflow that requires documentation',
310
+ {
311
+ workflowName,
312
+ requiresDocumentation,
313
+ referencedVariables,
314
+ missingDocs,
315
+ projectPath,
316
+ }
317
+ );
318
+
319
+ return {
320
+ phase: 'artifact-setup',
321
+ instructions: setupGuidance,
322
+ plan_file_path: '',
323
+ conversation_id: '',
324
+ workflow: {} as YamlStateMachine,
325
+ };
326
+ } catch (error) {
327
+ this.logger.warn(
328
+ 'Failed to analyze workflow for document references, proceeding without artifact check',
329
+ {
330
+ workflowName,
331
+ error: error instanceof Error ? error.message : String(error),
332
+ }
333
+ );
334
+ return null;
335
+ }
336
+ }
337
+
338
+ /**
339
+ * Analyze workflow content to detect document variable references
340
+ */
341
+ private analyzeWorkflowDocumentReferences(
342
+ stateMachine: YamlStateMachine,
343
+ projectPath: string
344
+ ): string[] {
345
+ // Get available document variables from ProjectDocsManager
346
+ const variableSubstitutions =
347
+ this.projectDocsManager.getVariableSubstitutions(projectPath);
348
+ const documentVariables = Object.keys(variableSubstitutions);
349
+ const referencedVariables: Set<string> = new Set();
350
+
351
+ // Convert the entire state machine to a string for analysis
352
+ const workflowContent = JSON.stringify(stateMachine);
353
+
354
+ // Check for each document variable
355
+ for (const variable of documentVariables) {
356
+ if (workflowContent.includes(variable)) {
357
+ referencedVariables.add(variable);
358
+ }
359
+ }
360
+
361
+ this.logger.debug('Analyzed workflow for document references', {
362
+ workflowContent: workflowContent.length + ' characters',
363
+ availableVariables: documentVariables,
364
+ referencedVariables: Array.from(referencedVariables),
365
+ });
366
+
367
+ return Array.from(referencedVariables);
368
+ }
369
+
370
+ /**
371
+ * Determine which referenced documents are missing
372
+ */
373
+ private getMissingReferencedDocuments(
374
+ referencedVariables: string[],
375
+ docsInfo: ProjectDocsInfo,
376
+ projectPath: string
377
+ ): string[] {
378
+ const missingDocs: string[] = [];
379
+
380
+ // Get variable substitutions to derive the mapping
381
+ const variableSubstitutions =
382
+ this.projectDocsManager.getVariableSubstitutions(projectPath);
383
+
384
+ // Create reverse mapping from variable to document type
385
+ const variableToDocMap: { [key: string]: string } = {};
386
+ for (const [variable, path] of Object.entries(variableSubstitutions)) {
387
+ // Extract document type from path (e.g., 'architecture' from 'architecture.md')
388
+ const filename = path.split('/').pop() || '';
389
+ const docType = filename.replace('.md', '');
390
+ variableToDocMap[variable] = docType;
391
+ }
392
+
393
+ for (const variable of referencedVariables) {
394
+ const docType = variableToDocMap[variable];
395
+ if (docType && docType in docsInfo) {
396
+ const docInfo = docsInfo[docType as keyof ProjectDocsInfo];
397
+ if (docInfo && !docInfo.exists) {
398
+ missingDocs.push(`${docType}.md`);
399
+ }
400
+ }
401
+ }
402
+
403
+ return missingDocs;
404
+ }
405
+
406
+ /**
407
+ * Generate guidance for setting up missing project artifacts
408
+ */
409
+ private async generateArtifactSetupGuidance(
410
+ missingDocs: string[],
411
+ workflowName: string,
412
+ docsInfo: ProjectDocsInfo,
413
+ referencedVariables: string[]
414
+ ): Promise<string> {
415
+ const missingList = missingDocs.map(doc => `- ${doc}`).join('\n');
416
+ const existingDocs = [];
417
+
418
+ if (docsInfo.architecture.exists) {
419
+ const fileName = basename(docsInfo.architecture.path);
420
+ existingDocs.push(`✅ ${fileName}`);
421
+ }
422
+ if (docsInfo.requirements.exists) {
423
+ const fileName = basename(docsInfo.requirements.path);
424
+ existingDocs.push(`✅ ${fileName}`);
425
+ }
426
+ if (docsInfo.design.exists) {
427
+ const fileName = basename(docsInfo.design.path);
428
+ existingDocs.push(`✅ ${fileName}`);
429
+ }
430
+
431
+ const existingList =
432
+ existingDocs.length > 0
433
+ ? `\n\n**Existing Documents:**\n${existingDocs.join('\n')}`
434
+ : '';
435
+
436
+ const referencedVariablesList = referencedVariables
437
+ .map(v => `\`${v}\``)
438
+ .join(', ');
439
+
440
+ // Get available templates dynamically
441
+ const availableTemplates =
442
+ await this.projectDocsManager.templateManager.getAvailableTemplates();
443
+ const defaults =
444
+ await this.projectDocsManager.templateManager.getDefaults();
445
+
446
+ // Generate template options dynamically
447
+ const templateOptionsText =
448
+ this.generateTemplateOptionsText(availableTemplates);
449
+
450
+ return `## Project Documentation Setup Required
451
+
452
+ The **${workflowName}** workflow references project documentation that doesn't exist yet.
453
+
454
+ **Referenced Variables:** ${referencedVariablesList}
455
+
456
+ **Missing Documents:**
457
+ ${missingList}${existingList}
458
+
459
+ ## 🚀 **Quick Setup**
460
+
461
+ Use the \`setup_project_docs\` tool to create these documents with templates:
462
+
463
+ \`\`\`
464
+ setup_project_docs({
465
+ architecture: "${defaults.architecture}", // or other available options
466
+ requirements: "${defaults.requirements}", // or other available options
467
+ design: "${defaults.design}" // or other available options
468
+ })
469
+ \`\`\`
470
+
471
+ ${templateOptionsText}
472
+
473
+ ## ⚡ **Next Steps**
474
+
475
+ 1. **Call \`setup_project_docs\`** with your preferred templates
476
+ 2. **Call \`start_development\`** again to begin the ${workflowName} workflow
477
+ 3. The workflow will reference these documents using the detected variables: ${referencedVariablesList}
478
+
479
+ **Note:** You can also proceed without structured docs, but the workflow instructions will reference missing files.`;
480
+ }
481
+
482
+ /**
483
+ * Generate template options text dynamically
484
+ */
485
+ private generateTemplateOptionsText(availableTemplates: {
486
+ architecture: string[];
487
+ requirements: string[];
488
+ design: string[];
489
+ }): string {
490
+ const sections = [];
491
+
492
+ if (availableTemplates.architecture.length > 0) {
493
+ const archOptions = availableTemplates.architecture
494
+ .map(template => {
495
+ const description = this.getTemplateDescription(
496
+ template,
497
+ 'architecture'
498
+ );
499
+ return `- **${template}**: ${description}`;
500
+ })
501
+ .join('\n');
502
+ sections.push(`**Architecture Templates:**\n${archOptions}`);
503
+ }
504
+
505
+ if (availableTemplates.requirements.length > 0) {
506
+ const reqOptions = availableTemplates.requirements
507
+ .map(template => {
508
+ const description = this.getTemplateDescription(
509
+ template,
510
+ 'requirements'
511
+ );
512
+ return `- **${template}**: ${description}`;
513
+ })
514
+ .join('\n');
515
+ sections.push(`**Requirements Templates:**\n${reqOptions}`);
516
+ }
517
+
518
+ if (availableTemplates.design.length > 0) {
519
+ const designOptions = availableTemplates.design
520
+ .map(template => {
521
+ const description = this.getTemplateDescription(template, 'design');
522
+ return `- **${template}**: ${description}`;
523
+ })
524
+ .join('\n');
525
+ sections.push(`**Design Templates:**\n${designOptions}`);
526
+ }
527
+
528
+ return sections.length > 0
529
+ ? `## 📋 **Template Options**\n\n${sections.join('\n\n')}`
530
+ : '';
531
+ }
532
+
533
+ /**
534
+ * Get description for a template based on its name and type
535
+ */
536
+ private getTemplateDescription(template: string, type: string): string {
537
+ switch (template) {
538
+ case 'arc42':
539
+ return 'Comprehensive software architecture template with diagrams';
540
+ case 'ears':
541
+ return 'WHEN...THEN format for clear, testable requirements';
542
+ case 'comprehensive':
543
+ return 'Full implementation guide with testing strategy';
544
+ case 'freestyle':
545
+ return `Simple, flexible ${type} documentation`;
546
+ default:
547
+ return `${template} format for ${type} documentation`;
548
+ }
549
+ }
550
+
551
+ /**
552
+ * Generate workflow documentation URL for predefined workflows
553
+ * Returns undefined for custom workflows
554
+ */
555
+ private generateWorkflowDocumentationUrl(
556
+ workflowName: string
557
+ ): string | undefined {
558
+ // Don't generate URL for custom workflows
559
+ if (workflowName === 'custom') {
560
+ return undefined;
561
+ }
562
+
563
+ // Generate URL for predefined workflows
564
+ return `https://mrsimpson.github.io/responsible-vibe-mcp/workflows/${workflowName}`;
565
+ }
566
+
567
+ /**
568
+ * Get the current git branch for a project
569
+ * Uses the same logic as ConversationManager but locally accessible
570
+ */
571
+ private getCurrentGitBranch(projectPath: string): string {
572
+ try {
573
+ const { execSync } = require('node:child_process');
574
+ const { existsSync } = require('node:fs');
575
+
576
+ // Check if this is a git repository
577
+ if (!existsSync(`${projectPath}/.git`)) {
578
+ this.logger.debug(
579
+ 'Not a git repository, using "default" as branch name',
580
+ { projectPath }
581
+ );
582
+ return 'default';
583
+ }
584
+
585
+ // Get current branch name
586
+ const branch = execSync('git rev-parse --abbrev-ref HEAD', {
587
+ cwd: projectPath,
588
+ encoding: 'utf-8',
589
+ stdio: ['ignore', 'pipe', 'ignore'], // Suppress stderr
590
+ }).trim();
591
+
592
+ this.logger.debug('Detected git branch', { projectPath, branch });
593
+
594
+ return branch;
595
+ } catch (_error) {
596
+ this.logger.debug(
597
+ 'Failed to get git branch, using "default" as branch name',
598
+ { projectPath }
599
+ );
600
+ return 'default';
601
+ }
602
+ }
603
+
604
+ /**
605
+ * Generate a suggested branch name for feature development
606
+ */
607
+ private generateBranchSuggestion(): string {
608
+ const timestamp = new Date().toISOString().slice(0, 10).replace(/-/g, '');
609
+ return `feature/development-${timestamp}`;
610
+ }
611
+
612
+ /**
613
+ * Ensure .gitignore exists in .vibe folder to exclude SQLite files
614
+ * This function is idempotent and self-contained within the .vibe directory
615
+ */
616
+ private ensureGitignoreEntry(projectPath: string): void {
617
+ try {
618
+ // Check if this is a git repository
619
+ if (!existsSync(`${projectPath}/.git`)) {
620
+ this.logger.debug(
621
+ 'Not a git repository, skipping .gitignore management',
622
+ { projectPath }
623
+ );
624
+ return;
625
+ }
626
+
627
+ const vibeDir = resolve(projectPath, '.vibe');
628
+ const gitignorePath = resolve(vibeDir, '.gitignore');
629
+
630
+ // Ensure .vibe directory exists
631
+ if (!existsSync(vibeDir)) {
632
+ mkdirSync(vibeDir, { recursive: true });
633
+ }
634
+
635
+ // Content for .vibe/.gitignore
636
+ const gitignoreContent = `# Exclude SQLite database files
637
+ *.sqlite
638
+ *.sqlite-*
639
+ conversation-state.sqlite*
640
+ `;
641
+
642
+ // Check if .gitignore already exists and has the right content
643
+ if (existsSync(gitignorePath)) {
644
+ try {
645
+ const existingContent = readFileSync(gitignorePath, 'utf-8');
646
+ if (
647
+ existingContent.includes('*.sqlite') &&
648
+ existingContent.includes('conversation-state.sqlite')
649
+ ) {
650
+ this.logger.debug(
651
+ '.vibe/.gitignore already exists with SQLite exclusions',
652
+ { gitignorePath }
653
+ );
654
+ return;
655
+ }
656
+ } catch (error) {
657
+ this.logger.warn(
658
+ 'Failed to read existing .vibe/.gitignore, will recreate',
659
+ {
660
+ gitignorePath,
661
+ error: error instanceof Error ? error.message : String(error),
662
+ }
663
+ );
664
+ }
665
+ }
666
+
667
+ // Write the .gitignore file
668
+ writeFileSync(gitignorePath, gitignoreContent, 'utf-8');
669
+
670
+ this.logger.info('Created .vibe/.gitignore to exclude SQLite files', {
671
+ projectPath,
672
+ gitignorePath,
673
+ });
674
+ } catch (error) {
675
+ // Log warning but don't fail development start
676
+ this.logger.warn(
677
+ 'Failed to create .vibe/.gitignore, continuing with development start',
678
+ {
679
+ projectPath,
680
+ error: error instanceof Error ? error.message : String(error),
681
+ }
682
+ );
683
+ }
684
+ }
685
+ }