@codemcp/workflows 4.4.2 → 4.6.0

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.
@@ -5,6 +5,7 @@
5
5
  * determines the next development phase with specific instructions for the LLM.
6
6
  */
7
7
  import { ConversationRequiredToolHandler } from './base-tool-handler.js';
8
+ import { TaskBackendManager, BeadsIntegration } from '@codemcp/workflows-core';
8
9
  /**
9
10
  * WhatsNext tool handler implementation
10
11
  */
@@ -85,6 +86,11 @@ export class WhatsNextHandler extends ConversationRequiredToolHandler {
85
86
  const commitMessage = requestContext || 'Step completion';
86
87
  finalInstructions += `\n\n**Git Commit Required**: Create a commit for this step using:\n\`\`\`bash\ngit add . && git commit -m "${commitMessage}"\n\`\`\``;
87
88
  }
89
+ // Add beads instructions if beads backend is active
90
+ const beadsInstructions = await this.generateBeadsInstructions(conversationContext, transitionResult.newPhase);
91
+ if (beadsInstructions) {
92
+ finalInstructions += '\n\n' + beadsInstructions;
93
+ }
88
94
  // Prepare response
89
95
  const response = {
90
96
  phase: transitionResult.newPhase,
@@ -125,5 +131,87 @@ export class WhatsNextHandler extends ConversationRequiredToolHandler {
125
131
  }
126
132
  return true;
127
133
  }
134
+ /**
135
+ * Generate beads-specific instructions if beads backend is active
136
+ */
137
+ async generateBeadsInstructions(conversationContext, currentPhase) {
138
+ // Check if beads backend is configured
139
+ const taskBackendConfig = TaskBackendManager.detectTaskBackend();
140
+ if (taskBackendConfig.backend !== 'beads' ||
141
+ !taskBackendConfig.isAvailable) {
142
+ return null;
143
+ }
144
+ try {
145
+ // Read plan file to extract current phase task ID
146
+ const phaseTaskId = await this.extractPhaseTaskId(conversationContext.planFilePath, currentPhase);
147
+ if (!phaseTaskId) {
148
+ this.logger.warn('Could not find beads phase task ID for current phase', {
149
+ phase: currentPhase,
150
+ planFilePath: conversationContext.planFilePath,
151
+ });
152
+ return null;
153
+ }
154
+ // Generate beads instructions using BeadsIntegration utility
155
+ const beadsIntegration = new BeadsIntegration(conversationContext.projectPath);
156
+ const phaseName = this.capitalizePhase(currentPhase);
157
+ return beadsIntegration.generateBeadsInstructions(phaseTaskId, phaseName);
158
+ }
159
+ catch (error) {
160
+ this.logger.warn('Failed to generate beads instructions', {
161
+ phase: currentPhase,
162
+ projectPath: conversationContext.projectPath,
163
+ error: error instanceof Error ? error.message : String(error),
164
+ });
165
+ return null;
166
+ }
167
+ }
168
+ /**
169
+ * Extract beads phase task ID from plan file for the given phase
170
+ */
171
+ async extractPhaseTaskId(planFilePath, phase) {
172
+ try {
173
+ const { readFile } = await import('node:fs/promises');
174
+ const content = await readFile(planFilePath, 'utf-8');
175
+ const phaseName = this.capitalizePhase(phase);
176
+ const phaseHeader = `## ${phaseName}`;
177
+ // Look for the phase header followed by beads-phase-id comment
178
+ const phaseSection = content.split('\n');
179
+ let foundPhaseHeader = false;
180
+ for (const line of phaseSection) {
181
+ if (line.trim() === phaseHeader) {
182
+ foundPhaseHeader = true;
183
+ continue;
184
+ }
185
+ if (foundPhaseHeader && line.includes('beads-phase-id:')) {
186
+ const match = line.match(/beads-phase-id:\s*([\w\d-]+)/);
187
+ if (match) {
188
+ return match[1] || null;
189
+ }
190
+ }
191
+ // Stop looking if we hit the next phase header
192
+ if (foundPhaseHeader && line.startsWith('##') && line !== phaseHeader) {
193
+ break;
194
+ }
195
+ }
196
+ return null;
197
+ }
198
+ catch (error) {
199
+ this.logger.warn('Failed to read plan file for phase task ID extraction', {
200
+ planFilePath,
201
+ phase,
202
+ error: error instanceof Error ? error.message : String(error),
203
+ });
204
+ return null;
205
+ }
206
+ }
207
+ /**
208
+ * Capitalize phase name for display
209
+ */
210
+ capitalizePhase(phase) {
211
+ return phase
212
+ .split('_')
213
+ .map(word => word.charAt(0).toUpperCase() + word.slice(1))
214
+ .join(' ');
215
+ }
128
216
  }
129
217
  //# sourceMappingURL=whats-next.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"whats-next.js","sourceRoot":"","sources":["../../src/tool-handlers/whats-next.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,+BAA+B,EAAE,MAAM,wBAAwB,CAAC;AA4BzE;;GAEG;AACH,MAAM,OAAO,gBAAiB,SAAQ,+BAGrC;IACoB,KAAK,CAAC,cAAc,CACrC,IAAmB,EACnB,OAAsB;QAEtB,IAAI,mBAAmB,CAAC;QAExB,IAAI,CAAC;YACH,mBAAmB,GAAG,MAAM,IAAI,CAAC,sBAAsB,CAAC,OAAO,CAAC,CAAC;QACnE,CAAC;QAAC,OAAO,MAAM,EAAE,CAAC;YAChB,4CAA4C;YAC5C,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;QAC5C,CAAC;QAED,OAAO,IAAI,CAAC,uBAAuB,CAAC,IAAI,EAAE,OAAO,EAAE,mBAAmB,CAAC,CAAC;IAC1E,CAAC;IAES,KAAK,CAAC,uBAAuB,CACrC,IAAmB,EACnB,OAAsB,EACtB,mBAAwC;QAExC,MAAM,EACJ,OAAO,EAAE,cAAc,GAAG,EAAE,EAC5B,UAAU,GAAG,EAAE,EACf,oBAAoB,GAAG,EAAE,EACzB,eAAe,GAAG,EAAE,GACrB,GAAG,IAAI,CAAC;QAET,MAAM,cAAc,GAAG,mBAAmB,CAAC,cAAc,CAAC;QAC1D,MAAM,YAAY,GAAG,mBAAmB,CAAC,YAAY,CAAC;QAEtD,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,+BAA+B,EAAE;YACjD,cAAc;YACd,YAAY;YACZ,UAAU,EAAE,CAAC,CAAC,cAAc;YAC5B,YAAY,EAAE,CAAC,CAAC,UAAU;SAC3B,CAAC,CAAC;QAEH,kDAAkD;QAClD,IAAI,CAAC,4BAA4B,CAC/B,OAAO,EACP,mBAAmB,CAAC,WAAW,EAC/B,mBAAmB,CAAC,YAAY,CACjC,CAAC;QAEF,0BAA0B;QAC1B,MAAM,OAAO,CAAC,WAAW,CAAC,cAAc,CACtC,mBAAmB,CAAC,YAAY,EAChC,mBAAmB,CAAC,WAAW,EAC/B,mBAAmB,CAAC,SAAS,CAC9B,CAAC;QAEF,2BAA2B;QAC3B,MAAM,gBAAgB,GACpB,MAAM,OAAO,CAAC,gBAAgB,CAAC,sBAAsB,CAAC;YACpD,YAAY;YACZ,WAAW,EAAE,mBAAmB,CAAC,WAAW;YAC5C,SAAS,EAAE,UAAU;YACrB,OAAO,EAAE,cAAc;YACvB,mBAAmB,EAAE,oBAAoB;YACzC,cAAc,EAAE,eAAe;YAC/B,cAAc,EAAE,mBAAmB,CAAC,cAAc;SACnD,CAAC,CAAC;QAEL,6CAA6C;QAC7C,IAAI,gBAAgB,CAAC,QAAQ,KAAK,YAAY,EAAE,CAAC;YAC/C,MAAM,iBAAiB,GAAG,MAAM,IAAI,CAAC,6BAA6B,CAChE,YAAY,EACZ,gBAAgB,CAAC,QAAQ,EACzB,mBAAmB,EACnB,OAAO,CACR,CAAC;YAEF,IAAI,iBAAiB,EAAE,CAAC;gBACtB,MAAM,OAAO,CAAC,mBAAmB,CAAC,uBAAuB,CACvD,cAAc,EACd,EAAE,YAAY,EAAE,gBAAgB,CAAC,QAAQ,EAAE,CAC5C,CAAC;YACJ,CAAC;YAED,qEAAqE;YACrE,IACE,gBAAgB,CAAC,gBAAgB,CAAC,QAAQ,CACxC,0CAA0C,CAC3C,EACD,CAAC;gBACD,IAAI,CAAC,MAAM,CAAC,IAAI,CACd,yDAAyD,EACzD;oBACE,IAAI,EAAE,YAAY;oBAClB,EAAE,EAAE,gBAAgB,CAAC,QAAQ;oBAC7B,YAAY,EAAE,mBAAmB,CAAC,YAAY;iBAC/C,CACF,CAAC;gBAEF,MAAM,OAAO,CAAC,WAAW,CAAC,cAAc,CACtC,mBAAmB,CAAC,YAAY,EAChC,mBAAmB,CAAC,WAAW,EAC/B,mBAAmB,CAAC,SAAS,CAC9B,CAAC;YACJ,CAAC;YAED,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,4BAA4B,EAAE;gBAC7C,IAAI,EAAE,YAAY;gBAClB,EAAE,EAAE,gBAAgB,CAAC,QAAQ;gBAC7B,MAAM,EAAE,gBAAgB,CAAC,gBAAgB;aAC1C,CAAC,CAAC;QACL,CAAC;QAED,4BAA4B;QAC5B,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,WAAW,CAAC,eAAe,CACxD,mBAAmB,CAAC,YAAY,CACjC,CAAC;QAEF,iCAAiC;QACjC,MAAM,YAAY,GAChB,MAAM,OAAO,CAAC,oBAAoB,CAAC,oBAAoB,CACrD,gBAAgB,CAAC,YAAY,EAC7B;YACE,KAAK,EAAE,gBAAgB,CAAC,QAAQ;YAChC,mBAAmB,EAAE;gBACnB,GAAG,mBAAmB;gBACtB,YAAY,EAAE,gBAAgB,CAAC,QAAQ;aACxC;YACD,gBAAgB,EAAE,gBAAgB,CAAC,gBAAgB;YACnD,SAAS,EAAE,gBAAgB,CAAC,SAAS;YACrC,cAAc,EAAE,QAAQ,CAAC,MAAM;SAChC,CACF,CAAC;QAEJ,wCAAwC;QACxC,IAAI,iBAAiB,GAAG,YAAY,CAAC,YAAY,CAAC;QAClD,IACE,mBAAmB,CAAC,eAAe,EAAE,OAAO;YAC5C,mBAAmB,CAAC,eAAe,CAAC,YAAY,EAChD,CAAC;YACD,MAAM,aAAa,GAAG,cAAc,IAAI,iBAAiB,CAAC;YAC1D,iBAAiB,IAAI,8GAA8G,aAAa,WAAW,CAAC;QAC9J,CAAC;QAED,mBAAmB;QACnB,MAAM,QAAQ,GAAoB;YAChC,KAAK,EAAE,gBAAgB,CAAC,QAAQ;YAChC,YAAY,EAAE,iBAAiB;YAC/B,cAAc,EAAE,mBAAmB,CAAC,YAAY;YAChD,qBAAqB,EAAE,gBAAgB,CAAC,SAAS;YACjD,eAAe,EAAE,mBAAmB,CAAC,cAAc;SACpD,CAAC;QAEF,kBAAkB;QAClB,MAAM,IAAI,CAAC,cAAc,CACvB,OAAO,EACP,cAAc,EACd,YAAY,EACZ,IAAI,EACJ,QAAQ,EACR,gBAAgB,CAAC,QAAQ,CAC1B,CAAC;QAEF,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,6BAA6B,CACzC,YAAoB,EACpB,QAAgB,EAChB,mBAAwC,EACxC,OAAsB;QAEtB,IAAI,CAAC,mBAAmB,CAAC,mCAAmC,EAAE,CAAC;YAC7D,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,YAAY,GAAG,OAAO,CAAC,eAAe,CAAC,sBAAsB,CACjE,mBAAmB,CAAC,WAAW,EAC/B,mBAAmB,CAAC,YAAY,CACjC,CAAC;QAEF,MAAM,YAAY,GAAG,YAAY,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;QACvD,IAAI,CAAC,YAAY,EAAE,CAAC;YAClB,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,UAAU,GAAG,YAAY,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,QAAQ,CAAC,CAAC;QACzE,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,qBAAqB,GACzB,UAAU,CAAC,mBAAmB;YAC9B,UAAU,CAAC,mBAAmB,CAAC,MAAM,GAAG,CAAC,CAAC;QAE5C,IAAI,qBAAqB,EAAE,CAAC;YAC1B,IAAI,CAAC,MAAM,CAAC,KAAK,CACf,0DAA0D,EAC1D;gBACE,IAAI,EAAE,YAAY;gBAClB,EAAE,EAAE,QAAQ;gBACZ,kBAAkB,EAAE,UAAU,CAAC,mBAAmB,EAAE,MAAM,IAAI,CAAC;aAChE,CACF,CAAC;YACF,OAAO,KAAK,CAAC;QACf,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;CACF"}
1
+ {"version":3,"file":"whats-next.js","sourceRoot":"","sources":["../../src/tool-handlers/whats-next.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemcp/workflows",
3
- "version": "4.4.2",
3
+ "version": "4.6.0",
4
4
  "description": "MCP server implementation for responsible-vibe",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -9,7 +9,7 @@
9
9
  },
10
10
  "dependencies": {
11
11
  "@modelcontextprotocol/sdk": "1.17.5",
12
- "@codemcp/workflows-core": "4.4.2"
12
+ "@codemcp/workflows-core": "4.6.0"
13
13
  },
14
14
  "devDependencies": {
15
15
  "@types/node": "^20.19.23",
@@ -325,6 +325,12 @@ export async function registerMcpTools(
325
325
  .describe(
326
326
  'Whether to require reviews before phase transitions. When enabled, use conduct_review tool before proceeding to next phase.'
327
327
  ),
328
+ project_path: z
329
+ .string()
330
+ .optional()
331
+ .describe(
332
+ 'Project directory path. Pass the .vibe subdirectory path if a .vibe directory exists in your project, otherwise pass the project root directory. The implementation will automatically detect and use the correct project root.'
333
+ ),
328
334
  },
329
335
  annotations: {
330
336
  title: 'Development Initializer',
@@ -481,6 +487,12 @@ export async function registerMcpTools(
481
487
  .describe(
482
488
  `Design documentation: template name (${availableTemplates.design.join(', ')}, none) OR file path to existing document`
483
489
  ),
490
+ project_path: z
491
+ .string()
492
+ .optional()
493
+ .describe(
494
+ 'Project directory path. Pass the .vibe subdirectory path if a .vibe directory exists in your project, otherwise pass the project root directory. The implementation will automatically detect and use the correct project root.'
495
+ ),
484
496
  },
485
497
  annotations: {
486
498
  title: 'Project Documentation Setup Tool',
@@ -222,3 +222,24 @@ export function logHandlerCompletion(
222
222
  hasError: !!result.error,
223
223
  });
224
224
  }
225
+
226
+ /**
227
+ * Strip /.vibe suffix from project path if present
228
+ *
229
+ * @param providedPath - Optional project path provided by user
230
+ * @param defaultPath - Default project path from context
231
+ * @returns Normalized project root path
232
+ */
233
+ export function stripVibePathSuffix(
234
+ providedPath: string | undefined,
235
+ defaultPath: string
236
+ ): string {
237
+ if (!providedPath) {
238
+ return defaultPath;
239
+ }
240
+
241
+ // Strip /.vibe suffix if present to get project root
242
+ return providedPath.endsWith('/.vibe')
243
+ ? providedPath.slice(0, -6) // Remove '/.vibe'
244
+ : providedPath;
245
+ }
@@ -7,6 +7,7 @@
7
7
  */
8
8
 
9
9
  import { BaseToolHandler } from './base-tool-handler.js';
10
+ import { stripVibePathSuffix } from '../server-helpers.js';
10
11
  import { ProjectDocsManager } from '@codemcp/workflows-core';
11
12
  import { TemplateOptions } from '@codemcp/workflows-core';
12
13
  import { PathValidationUtils } from '@codemcp/workflows-core';
@@ -16,6 +17,7 @@ export interface SetupProjectDocsArgs {
16
17
  architecture: string; // Template name OR file path
17
18
  requirements: string; // Template name OR file path
18
19
  design: string; // Template name OR file path
20
+ project_path?: string; // Optional project path override
19
21
  }
20
22
 
21
23
  export interface SetupProjectDocsResult {
@@ -46,7 +48,11 @@ export class SetupProjectDocsHandler extends BaseToolHandler<
46
48
  args: SetupProjectDocsArgs,
47
49
  context: ServerContext
48
50
  ): Promise<SetupProjectDocsResult> {
49
- const projectPath = context.projectPath || process.cwd();
51
+ // Normalize project path - strip /.vibe suffix if present
52
+ const projectPath = stripVibePathSuffix(
53
+ args.project_path,
54
+ context.projectPath || process.cwd()
55
+ );
50
56
 
51
57
  this.logger.info(
52
58
  'Setting up project docs with enhanced file linking support',
@@ -6,7 +6,10 @@
6
6
  */
7
7
 
8
8
  import { BaseToolHandler } from './base-tool-handler.js';
9
- import { validateRequiredArgs } from '../server-helpers.js';
9
+ import {
10
+ validateRequiredArgs,
11
+ stripVibePathSuffix,
12
+ } from '../server-helpers.js';
10
13
  import { basename } from 'node:path';
11
14
  import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs';
12
15
  import { resolve } from 'node:path';
@@ -14,6 +17,7 @@ import { GitCommitConfig } from '@codemcp/workflows-core';
14
17
  import { GitManager } from '@codemcp/workflows-core';
15
18
  import type { YamlStateMachine } from '@codemcp/workflows-core';
16
19
  import { ProjectDocsManager, ProjectDocsInfo } from '@codemcp/workflows-core';
20
+ import { TaskBackendManager, BeadsIntegration } from '@codemcp/workflows-core';
17
21
  import { ServerContext } from '../types.js';
18
22
 
19
23
  /**
@@ -23,6 +27,7 @@ export interface StartDevelopmentArgs {
23
27
  workflow: string;
24
28
  commit_behaviour?: 'step' | 'phase' | 'end' | 'none';
25
29
  require_reviews?: boolean;
30
+ project_path?: string;
26
31
  }
27
32
 
28
33
  /**
@@ -58,11 +63,20 @@ export class StartDevelopmentHandler extends BaseToolHandler<
58
63
  // Validate required arguments
59
64
  validateRequiredArgs(args, ['workflow']);
60
65
 
66
+ // Validate task backend configuration
67
+ const taskBackendConfig = TaskBackendManager.validateTaskBackend();
68
+
61
69
  const selectedWorkflow = args.workflow;
62
70
  const requireReviews = args.require_reviews ?? false;
63
71
 
72
+ // Normalize project path - strip /.vibe suffix if present
73
+ const projectPath = stripVibePathSuffix(
74
+ args.project_path,
75
+ context.projectPath
76
+ );
77
+
64
78
  // Process git commit configuration
65
- const isGitRepository = GitManager.isGitRepository(context.projectPath);
79
+ const isGitRepository = GitManager.isGitRepository(projectPath);
66
80
 
67
81
  // Translate commit_behaviour to internal git config
68
82
  const commitBehaviour =
@@ -77,22 +91,22 @@ export class StartDevelopmentHandler extends BaseToolHandler<
77
91
  commitBehaviour === 'phase',
78
92
  initialMessage: 'Development session',
79
93
  startCommitHash:
80
- GitManager.getCurrentCommitHash(context.projectPath) || undefined,
94
+ GitManager.getCurrentCommitHash(projectPath) || undefined,
81
95
  };
82
96
 
83
97
  this.logger.debug('Processing start_development request', {
84
98
  selectedWorkflow,
85
- projectPath: context.projectPath,
99
+ projectPath: projectPath,
86
100
  commitBehaviour,
87
101
  gitCommitConfig,
88
102
  });
89
103
 
90
104
  // Validate workflow selection (ensure project workflows are loaded first)
91
- context.workflowManager.loadProjectWorkflows(context.projectPath);
105
+ context.workflowManager.loadProjectWorkflows(projectPath);
92
106
  if (
93
107
  !context.workflowManager.validateWorkflowName(
94
108
  selectedWorkflow,
95
- context.projectPath
109
+ projectPath
96
110
  )
97
111
  ) {
98
112
  const availableWorkflows = context.workflowManager.getWorkflowNames();
@@ -103,7 +117,7 @@ export class StartDevelopmentHandler extends BaseToolHandler<
103
117
 
104
118
  // Check for project documentation artifacts and guide setup if needed
105
119
  const artifactGuidance = await this.checkProjectArtifacts(
106
- context.projectPath,
120
+ projectPath,
107
121
  selectedWorkflow,
108
122
  context
109
123
  );
@@ -112,7 +126,7 @@ export class StartDevelopmentHandler extends BaseToolHandler<
112
126
  }
113
127
 
114
128
  // Check if user is on main/master branch and prompt for branch creation
115
- const currentBranch = this.getCurrentGitBranch(context.projectPath);
129
+ const currentBranch = this.getCurrentGitBranch(projectPath);
116
130
  if (currentBranch === 'main' || currentBranch === 'master') {
117
131
  const suggestedBranchName = this.generateBranchSuggestion();
118
132
  const branchPromptResponse: StartDevelopmentResult = {
@@ -137,7 +151,8 @@ export class StartDevelopmentHandler extends BaseToolHandler<
137
151
  // Create or get conversation context with the selected workflow
138
152
  const conversationContext =
139
153
  await context.conversationManager.createConversationContext(
140
- selectedWorkflow
154
+ selectedWorkflow,
155
+ args.project_path ? projectPath : undefined
141
156
  );
142
157
  const currentPhase = conversationContext.currentPhase;
143
158
 
@@ -163,7 +178,7 @@ export class StartDevelopmentHandler extends BaseToolHandler<
163
178
  await context.transitionEngine.handleExplicitTransition(
164
179
  currentPhase,
165
180
  targetPhase,
166
- conversationContext.projectPath,
181
+ projectPath,
167
182
  'Development initialization',
168
183
  selectedWorkflow
169
184
  );
@@ -182,15 +197,30 @@ export class StartDevelopmentHandler extends BaseToolHandler<
182
197
  // Set state machine on plan manager before creating plan file
183
198
  context.planManager.setStateMachine(stateMachine);
184
199
 
200
+ // Set task backend configuration if supported (for backwards compatibility)
201
+ if (typeof context.planManager.setTaskBackend === 'function') {
202
+ context.planManager.setTaskBackend(taskBackendConfig);
203
+ }
204
+
185
205
  // Ensure plan file exists
186
206
  await context.planManager.ensurePlanFile(
187
207
  conversationContext.planFilePath,
188
- conversationContext.projectPath,
208
+ projectPath,
189
209
  conversationContext.gitBranch
190
210
  );
191
211
 
212
+ // Handle beads integration if beads backend is configured
213
+ if (taskBackendConfig.backend === 'beads') {
214
+ await this.setupBeadsIntegration(
215
+ projectPath,
216
+ stateMachine,
217
+ selectedWorkflow,
218
+ conversationContext.planFilePath
219
+ );
220
+ }
221
+
192
222
  // Ensure .vibe/.gitignore exists to exclude SQLite files for git repositories
193
- this.ensureGitignoreEntry(conversationContext.projectPath);
223
+ this.ensureGitignoreEntry(projectPath);
194
224
 
195
225
  // Generate workflow documentation URL
196
226
  const workflowDocumentationUrl =
@@ -610,6 +640,119 @@ ${templateOptionsText}
610
640
  return `feature/development-${timestamp}`;
611
641
  }
612
642
 
643
+ /**
644
+ * Setup beads integration - create project epic and phase tasks
645
+ */
646
+ private async setupBeadsIntegration(
647
+ projectPath: string,
648
+ stateMachine: YamlStateMachine,
649
+ workflowName: string,
650
+ planFilePath: string
651
+ ): Promise<void> {
652
+ try {
653
+ const beadsIntegration = new BeadsIntegration(projectPath);
654
+ const projectName = projectPath.split('/').pop() || 'Unknown Project';
655
+
656
+ // Create project epic
657
+ const epicId = await beadsIntegration.createProjectEpic(
658
+ projectName,
659
+ workflowName
660
+ );
661
+
662
+ // Create phase tasks for all workflow phases
663
+ const phases = Object.keys(stateMachine.states);
664
+ const phaseTasks = await beadsIntegration.createPhaseTasks(
665
+ epicId,
666
+ phases,
667
+ workflowName
668
+ );
669
+
670
+ // Update plan file with phase task IDs
671
+ await this.updatePlanFileWithPhaseTaskIds(planFilePath, phaseTasks);
672
+
673
+ this.logger.info('Beads integration setup complete', {
674
+ projectPath,
675
+ epicId,
676
+ phaseCount: phaseTasks.length,
677
+ planFilePath,
678
+ });
679
+ } catch (error) {
680
+ this.logger.error(
681
+ 'Failed to setup beads integration',
682
+ error instanceof Error ? error : new Error(String(error)),
683
+ {
684
+ projectPath,
685
+ workflowName,
686
+ }
687
+ );
688
+ throw new Error(
689
+ `Failed to setup beads integration: ${error instanceof Error ? error.message : String(error)}`
690
+ );
691
+ }
692
+ }
693
+
694
+ /**
695
+ * Update plan file to include beads phase task IDs in comments
696
+ */
697
+ private async updatePlanFileWithPhaseTaskIds(
698
+ planFilePath: string,
699
+ phaseTasks: Array<{ phaseId: string; phaseName: string; taskId: string }>
700
+ ): Promise<void> {
701
+ try {
702
+ const { readFile, writeFile } = await import('node:fs/promises');
703
+ let content = await readFile(planFilePath, 'utf-8');
704
+
705
+ // Replace TBD placeholders with actual task IDs
706
+ for (const phaseTask of phaseTasks) {
707
+ const phaseHeader = `## ${phaseTask.phaseName}`;
708
+ const placeholderPattern = new RegExp(
709
+ `(${phaseHeader.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\s*\n)<!-- beads-phase-id: TBD -->`,
710
+ 'g'
711
+ );
712
+ content = content.replace(
713
+ placeholderPattern,
714
+ `$1<!-- beads-phase-id: ${phaseTask.taskId} -->`
715
+ );
716
+ }
717
+
718
+ // Validate that all TBD placeholders were replaced
719
+ const remainingTBDs = content.match(/<!-- beads-phase-id: TBD -->/g);
720
+ if (remainingTBDs && remainingTBDs.length > 0) {
721
+ throw new Error(
722
+ `Failed to replace ${remainingTBDs.length} TBD placeholder(s) in plan file. ` +
723
+ `This indicates that phase names in the plan file don't match the workflow phases, ` +
724
+ `or the beads task creation process failed for some phases.`
725
+ );
726
+ }
727
+
728
+ await writeFile(planFilePath, content, 'utf-8');
729
+
730
+ this.logger.info(
731
+ 'Successfully updated plan file with beads phase task IDs',
732
+ {
733
+ planFilePath,
734
+ phaseTaskCount: phaseTasks.length,
735
+ replacedTasks: phaseTasks.map(
736
+ task => `${task.phaseName}: ${task.taskId}`
737
+ ),
738
+ }
739
+ );
740
+ } catch (error) {
741
+ this.logger.error(
742
+ 'Failed to update plan file with phase task IDs',
743
+ error instanceof Error ? error : new Error(String(error)),
744
+ {
745
+ planFilePath,
746
+ }
747
+ );
748
+ // For beads integration, TBD replacement failure is critical
749
+ throw new Error(
750
+ `Failed to update plan file with beads task IDs: ${error instanceof Error ? error.message : String(error)}. ` +
751
+ `This indicates beads integration setup is incomplete. Check that beads CLI is working and task IDs were created successfully.`
752
+ );
753
+ }
754
+ }
755
+
613
756
  /**
614
757
  * Ensure .gitignore exists in .vibe folder to exclude SQLite files
615
758
  * This function is idempotent and self-contained within the .vibe directory
@@ -7,6 +7,7 @@
7
7
 
8
8
  import { ConversationRequiredToolHandler } from './base-tool-handler.js';
9
9
  import type { ConversationContext } from '@codemcp/workflows-core';
10
+ import { TaskBackendManager, BeadsIntegration } from '@codemcp/workflows-core';
10
11
  import { ServerContext } from '../types.js';
11
12
 
12
13
  /**
@@ -180,6 +181,15 @@ export class WhatsNextHandler extends ConversationRequiredToolHandler<
180
181
  finalInstructions += `\n\n**Git Commit Required**: Create a commit for this step using:\n\`\`\`bash\ngit add . && git commit -m "${commitMessage}"\n\`\`\``;
181
182
  }
182
183
 
184
+ // Add beads instructions if beads backend is active
185
+ const beadsInstructions = await this.generateBeadsInstructions(
186
+ conversationContext,
187
+ transitionResult.newPhase
188
+ );
189
+ if (beadsInstructions) {
190
+ finalInstructions += '\n\n' + beadsInstructions;
191
+ }
192
+
183
193
  // Prepare response
184
194
  const response: WhatsNextResult = {
185
195
  phase: transitionResult.newPhase,
@@ -248,4 +258,114 @@ export class WhatsNextHandler extends ConversationRequiredToolHandler<
248
258
 
249
259
  return true;
250
260
  }
261
+
262
+ /**
263
+ * Generate beads-specific instructions if beads backend is active
264
+ */
265
+ private async generateBeadsInstructions(
266
+ conversationContext: ConversationContext,
267
+ currentPhase: string
268
+ ): Promise<string | null> {
269
+ // Check if beads backend is configured
270
+ const taskBackendConfig = TaskBackendManager.detectTaskBackend();
271
+ if (
272
+ taskBackendConfig.backend !== 'beads' ||
273
+ !taskBackendConfig.isAvailable
274
+ ) {
275
+ return null;
276
+ }
277
+
278
+ try {
279
+ // Read plan file to extract current phase task ID
280
+ const phaseTaskId = await this.extractPhaseTaskId(
281
+ conversationContext.planFilePath,
282
+ currentPhase
283
+ );
284
+ if (!phaseTaskId) {
285
+ this.logger.warn(
286
+ 'Could not find beads phase task ID for current phase',
287
+ {
288
+ phase: currentPhase,
289
+ planFilePath: conversationContext.planFilePath,
290
+ }
291
+ );
292
+ return null;
293
+ }
294
+
295
+ // Generate beads instructions using BeadsIntegration utility
296
+ const beadsIntegration = new BeadsIntegration(
297
+ conversationContext.projectPath
298
+ );
299
+ const phaseName = this.capitalizePhase(currentPhase);
300
+ return beadsIntegration.generateBeadsInstructions(phaseTaskId, phaseName);
301
+ } catch (error) {
302
+ this.logger.warn('Failed to generate beads instructions', {
303
+ phase: currentPhase,
304
+ projectPath: conversationContext.projectPath,
305
+ error: error instanceof Error ? error.message : String(error),
306
+ });
307
+ return null;
308
+ }
309
+ }
310
+
311
+ /**
312
+ * Extract beads phase task ID from plan file for the given phase
313
+ */
314
+ private async extractPhaseTaskId(
315
+ planFilePath: string,
316
+ phase: string
317
+ ): Promise<string | null> {
318
+ try {
319
+ const { readFile } = await import('node:fs/promises');
320
+ const content = await readFile(planFilePath, 'utf-8');
321
+
322
+ const phaseName = this.capitalizePhase(phase);
323
+ const phaseHeader = `## ${phaseName}`;
324
+
325
+ // Look for the phase header followed by beads-phase-id comment
326
+ const phaseSection = content.split('\n');
327
+ let foundPhaseHeader = false;
328
+
329
+ for (const line of phaseSection) {
330
+ if (line.trim() === phaseHeader) {
331
+ foundPhaseHeader = true;
332
+ continue;
333
+ }
334
+
335
+ if (foundPhaseHeader && line.includes('beads-phase-id:')) {
336
+ const match = line.match(/beads-phase-id:\s*([\w\d-]+)/);
337
+ if (match) {
338
+ return match[1] || null;
339
+ }
340
+ }
341
+
342
+ // Stop looking if we hit the next phase header
343
+ if (foundPhaseHeader && line.startsWith('##') && line !== phaseHeader) {
344
+ break;
345
+ }
346
+ }
347
+
348
+ return null;
349
+ } catch (error) {
350
+ this.logger.warn(
351
+ 'Failed to read plan file for phase task ID extraction',
352
+ {
353
+ planFilePath,
354
+ phase,
355
+ error: error instanceof Error ? error.message : String(error),
356
+ }
357
+ );
358
+ return null;
359
+ }
360
+ }
361
+
362
+ /**
363
+ * Capitalize phase name for display
364
+ */
365
+ private capitalizePhase(phase: string): string {
366
+ return phase
367
+ .split('_')
368
+ .map(word => word.charAt(0).toUpperCase() + word.slice(1))
369
+ .join(' ');
370
+ }
251
371
  }