@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.
- package/.turbo/turbo-build.log +1 -1
- package/dist/server-config.d.ts.map +1 -1
- package/dist/server-config.js +8 -0
- package/dist/server-config.js.map +1 -1
- package/dist/server-helpers.d.ts +8 -0
- package/dist/server-helpers.d.ts.map +1 -1
- package/dist/server-helpers.js +16 -0
- package/dist/server-helpers.js.map +1 -1
- package/dist/tool-handlers/setup-project-docs.d.ts +1 -0
- package/dist/tool-handlers/setup-project-docs.d.ts.map +1 -1
- package/dist/tool-handlers/setup-project-docs.js +3 -1
- package/dist/tool-handlers/setup-project-docs.js.map +1 -1
- package/dist/tool-handlers/start-development.d.ts +9 -0
- package/dist/tool-handlers/start-development.d.ts.map +1 -1
- package/dist/tool-handlers/start-development.js +90 -12
- package/dist/tool-handlers/start-development.js.map +1 -1
- package/dist/tool-handlers/whats-next.d.ts +12 -0
- package/dist/tool-handlers/whats-next.d.ts.map +1 -1
- package/dist/tool-handlers/whats-next.js +88 -0
- package/dist/tool-handlers/whats-next.js.map +1 -1
- package/package.json +2 -2
- package/src/server-config.ts +12 -0
- package/src/server-helpers.ts +21 -0
- package/src/tool-handlers/setup-project-docs.ts +7 -1
- package/src/tool-handlers/start-development.ts +155 -12
- package/src/tool-handlers/whats-next.ts +120 -0
- package/tsconfig.build.tsbuildinfo +1 -1
|
@@ -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;
|
|
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.
|
|
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.
|
|
12
|
+
"@codemcp/workflows-core": "4.6.0"
|
|
13
13
|
},
|
|
14
14
|
"devDependencies": {
|
|
15
15
|
"@types/node": "^20.19.23",
|
package/src/server-config.ts
CHANGED
|
@@ -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',
|
package/src/server-helpers.ts
CHANGED
|
@@ -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
|
-
|
|
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 {
|
|
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(
|
|
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(
|
|
94
|
+
GitManager.getCurrentCommitHash(projectPath) || undefined,
|
|
81
95
|
};
|
|
82
96
|
|
|
83
97
|
this.logger.debug('Processing start_development request', {
|
|
84
98
|
selectedWorkflow,
|
|
85
|
-
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(
|
|
105
|
+
context.workflowManager.loadProjectWorkflows(projectPath);
|
|
92
106
|
if (
|
|
93
107
|
!context.workflowManager.validateWorkflowName(
|
|
94
108
|
selectedWorkflow,
|
|
95
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
}
|