@codemcp/workflows 5.0.1 → 5.1.1

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 (77) hide show
  1. package/package.json +6 -2
  2. package/skill/SKILL.md +23 -0
  3. package/.prettierignore +0 -2
  4. package/.turbo/turbo-build.log +0 -4
  5. package/.vibe/conversation-state.sqlite +0 -0
  6. package/src/components/beads/beads-instruction-generator.ts +0 -230
  7. package/src/components/beads/beads-plan-manager.ts +0 -333
  8. package/src/components/beads/beads-task-backend-client.ts +0 -229
  9. package/src/index.ts +0 -93
  10. package/src/notification-service.ts +0 -23
  11. package/src/plugin-system/beads-plugin.ts +0 -649
  12. package/src/plugin-system/commit-plugin.ts +0 -252
  13. package/src/plugin-system/index.ts +0 -20
  14. package/src/plugin-system/plugin-interfaces.ts +0 -153
  15. package/src/plugin-system/plugin-registry.ts +0 -190
  16. package/src/resource-handlers/conversation-state.ts +0 -55
  17. package/src/resource-handlers/development-plan.ts +0 -48
  18. package/src/resource-handlers/index.ts +0 -73
  19. package/src/resource-handlers/system-prompt.ts +0 -55
  20. package/src/resource-handlers/workflow-resource.ts +0 -132
  21. package/src/response-renderer.ts +0 -116
  22. package/src/server-config.ts +0 -760
  23. package/src/server-helpers.ts +0 -245
  24. package/src/server-implementation.ts +0 -277
  25. package/src/server.ts +0 -9
  26. package/src/tool-handlers/base-tool-handler.ts +0 -151
  27. package/src/tool-handlers/conduct-review.ts +0 -190
  28. package/src/tool-handlers/get-tool-info.ts +0 -273
  29. package/src/tool-handlers/index.ts +0 -115
  30. package/src/tool-handlers/list-workflows.ts +0 -78
  31. package/src/tool-handlers/no-idea.ts +0 -47
  32. package/src/tool-handlers/proceed-to-phase.ts +0 -296
  33. package/src/tool-handlers/reset-development.ts +0 -90
  34. package/src/tool-handlers/resume-workflow.ts +0 -378
  35. package/src/tool-handlers/setup-project-docs.ts +0 -232
  36. package/src/tool-handlers/start-development.ts +0 -746
  37. package/src/tool-handlers/whats-next.ts +0 -246
  38. package/src/types.ts +0 -135
  39. package/src/version-info.ts +0 -213
  40. package/test/e2e/beads-plugin-integration.test.ts +0 -1623
  41. package/test/e2e/commit-plugin-integration.test.ts +0 -222
  42. package/test/e2e/core-functionality.test.ts +0 -167
  43. package/test/e2e/git-branch-detection.test.ts +0 -351
  44. package/test/e2e/mcp-contract.test.ts +0 -509
  45. package/test/e2e/plan-management.test.ts +0 -334
  46. package/test/e2e/plugin-system-integration.test.ts +0 -1410
  47. package/test/e2e/state-management.test.ts +0 -387
  48. package/test/e2e/workflow-integration.test.ts +0 -498
  49. package/test/unit/beads-instruction-generator.test.ts +0 -979
  50. package/test/unit/beads-phase-task-id-integration.test.ts +0 -535
  51. package/test/unit/beads-plugin-behavioral.test.ts +0 -545
  52. package/test/unit/beads-plugin.test.ts +0 -117
  53. package/test/unit/commit-plugin.test.ts +0 -196
  54. package/test/unit/conduct-review.test.ts +0 -151
  55. package/test/unit/conversation-not-found-error.test.ts +0 -120
  56. package/test/unit/plugin-error-handling.test.ts +0 -240
  57. package/test/unit/proceed-to-phase-plugin-integration.test.ts +0 -150
  58. package/test/unit/reset-functionality.test.ts +0 -72
  59. package/test/unit/resume-workflow.test.ts +0 -193
  60. package/test/unit/server-config-plugin-registry.test.ts +0 -99
  61. package/test/unit/server-tools.test.ts +0 -310
  62. package/test/unit/setup-project-docs-handler.test.ts +0 -268
  63. package/test/unit/start-development-artifact-detection.test.ts +0 -387
  64. package/test/unit/start-development-gitignore.test.ts +0 -178
  65. package/test/unit/start-development-goal-extraction.test.ts +0 -226
  66. package/test/unit/system-prompt-resource.test.ts +0 -102
  67. package/test/unit/tool-handlers/no-idea.test.ts +0 -40
  68. package/test/utils/e2e-test-setup.ts +0 -451
  69. package/test/utils/run-server-in-dir.sh +0 -27
  70. package/test/utils/temp-files.ts +0 -320
  71. package/test/utils/test-access.ts +0 -79
  72. package/test/utils/test-helpers.ts +0 -288
  73. package/test/utils/test-setup.ts +0 -77
  74. package/tsconfig.build.json +0 -10
  75. package/tsconfig.build.tsbuildinfo +0 -1
  76. package/tsconfig.json +0 -12
  77. package/vitest.config.ts +0 -19
@@ -1,649 +0,0 @@
1
- /**
2
- * Beads Plugin Implementation
3
- *
4
- * Plugin that integrates beads task management system with responsible-vibe-mcp.
5
- * Encapsulates ALL beads-specific functionality to maintain zero core application
6
- * coupling as specified in plugin architecture design.
7
- *
8
- * Core Principle: This plugin must be completely self-contained and the core
9
- * application must have ZERO knowledge of beads functionality.
10
- */
11
-
12
- import type {
13
- IPlugin,
14
- PluginHooks,
15
- PluginHookContext,
16
- StartDevelopmentArgs,
17
- StartDevelopmentResult,
18
- } from './plugin-interfaces.js';
19
- import type { YamlState } from '@codemcp/workflows-core';
20
- import {
21
- BeadsStateManager,
22
- BeadsIntegration,
23
- createLogger,
24
- PlanManager,
25
- TaskBackendManager,
26
- } from '@codemcp/workflows-core';
27
- import { BeadsTaskBackendClient } from '../components/beads/beads-task-backend-client.js';
28
-
29
- const logger = createLogger('BeadsPlugin');
30
-
31
- /**
32
- * BeadsPlugin class implementing the IPlugin interface
33
- *
34
- * Activation: When beads backend is detected (either via TASK_BACKEND=beads env var
35
- * or auto-detection when bd CLI is available)
36
- * Priority: Sequence 100 (middle priority)
37
- * Encapsulation: All beads functionality contained within this plugin
38
- */
39
- export class BeadsPlugin implements IPlugin {
40
- private projectPath: string;
41
- private beadsStateManager: BeadsStateManager;
42
- private beadsTaskBackendClient: BeadsTaskBackendClient;
43
- private planManager: PlanManager;
44
-
45
- constructor(options: { projectPath: string }) {
46
- this.projectPath = options.projectPath;
47
-
48
- // Initialize internal beads components
49
- this.beadsStateManager = new BeadsStateManager(this.projectPath);
50
- this.beadsTaskBackendClient = new BeadsTaskBackendClient(this.projectPath);
51
- this.planManager = new PlanManager();
52
-
53
- logger.debug('BeadsPlugin initialized', { projectPath: this.projectPath });
54
- }
55
-
56
- getName(): string {
57
- return 'BeadsPlugin';
58
- }
59
-
60
- getSequence(): number {
61
- return 100; // Middle priority as specified
62
- }
63
-
64
- isEnabled(): boolean {
65
- // Use TaskBackendManager to properly detect beads backend,
66
- // which supports both explicit TASK_BACKEND env var and auto-detection
67
- const taskBackendConfig = TaskBackendManager.detectTaskBackend();
68
- const enabled =
69
- taskBackendConfig.backend === 'beads' && taskBackendConfig.isAvailable;
70
- logger.debug('BeadsPlugin enablement check', {
71
- backend: taskBackendConfig.backend,
72
- isAvailable: taskBackendConfig.isAvailable,
73
- autoDetected: !process.env['TASK_BACKEND'],
74
- enabled,
75
- });
76
- return enabled;
77
- }
78
-
79
- getHooks(): PluginHooks {
80
- return {
81
- afterStartDevelopment: this.handleAfterStartDevelopment.bind(this),
82
- beforePhaseTransition: this.handleBeforePhaseTransition.bind(this),
83
- afterPlanFileCreated: this.handleAfterPlanFileCreated.bind(this),
84
- };
85
- }
86
-
87
- /**
88
- * Handle beforePhaseTransition hook
89
- * Replaces validateBeadsTaskCompletion() method from proceed-to-phase.ts
90
- */
91
- private async handleBeforePhaseTransition(
92
- context: PluginHookContext,
93
- currentPhase: string,
94
- targetPhase: string
95
- ): Promise<void> {
96
- logger.info(
97
- 'BeadsPlugin: Validating task completion before phase transition',
98
- {
99
- conversationId: context.conversationId,
100
- currentPhase,
101
- targetPhase,
102
- }
103
- );
104
-
105
- try {
106
- await this.validateBeadsTaskCompletion(
107
- context.conversationId,
108
- currentPhase,
109
- targetPhase,
110
- context.projectPath
111
- );
112
-
113
- logger.info(
114
- 'BeadsPlugin: Task validation passed, allowing phase transition',
115
- {
116
- conversationId: context.conversationId,
117
- currentPhase,
118
- targetPhase,
119
- }
120
- );
121
- } catch (error) {
122
- logger.info(
123
- 'BeadsPlugin: Task validation failed, blocking phase transition',
124
- {
125
- conversationId: context.conversationId,
126
- currentPhase,
127
- targetPhase,
128
- error: error instanceof Error ? error.message : String(error),
129
- }
130
- );
131
-
132
- // Re-throw validation errors to block transitions
133
- throw error;
134
- }
135
- }
136
-
137
- /**
138
- * Handle afterStartDevelopment hook
139
- * Replaces setupBeadsIntegration() method from start-development.ts
140
- * Implements graceful degradation: continues app operation even if beads operations fail
141
- */
142
- private async handleAfterStartDevelopment(
143
- context: PluginHookContext,
144
- args: StartDevelopmentArgs,
145
- _result: StartDevelopmentResult
146
- ): Promise<void> {
147
- logger.info('BeadsPlugin: Setting up beads integration', {
148
- conversationId: context.conversationId,
149
- workflow: args.workflow,
150
- projectPath: context.projectPath,
151
- });
152
-
153
- // Verify we have the required state machine information
154
- if (!context.stateMachine) {
155
- logger.error('BeadsPlugin: State machine not provided in plugin context');
156
- logger.warn(
157
- 'BeadsPlugin: Beads integration disabled - continuing without beads'
158
- );
159
- return; // Graceful degradation: continue without beads
160
- }
161
-
162
- try {
163
- const beadsIntegration = new BeadsIntegration(context.projectPath);
164
- const projectName =
165
- context.projectPath.split('/').pop() || 'Unknown Project';
166
-
167
- // Extract goal from plan file if it exists and has meaningful content
168
- let goalDescription: string | undefined;
169
- try {
170
- const planFileContent = await this.planManager.getPlanFileContent(
171
- context.planFilePath
172
- );
173
- goalDescription = this.extractGoalFromPlan(planFileContent);
174
- } catch (error) {
175
- logger.warn('BeadsPlugin: Could not extract goal from plan file', {
176
- error: error instanceof Error ? error.message : String(error),
177
- planFilePath: context.planFilePath,
178
- });
179
- // Continue without goal - it's optional
180
- }
181
-
182
- // Extract plan filename for use in epic title
183
- const planFilename = context.planFilePath.split('/').pop();
184
-
185
- // Try to create project epic
186
- let epicId: string;
187
- try {
188
- epicId = await beadsIntegration.createProjectEpic(
189
- projectName,
190
- args.workflow,
191
- goalDescription,
192
- planFilename
193
- );
194
- } catch (error) {
195
- const errorMsg = error instanceof Error ? error.message : String(error);
196
- logger.warn(
197
- 'BeadsPlugin: Failed to create beads project epic - continuing without beads integration',
198
- {
199
- error: errorMsg,
200
- projectPath: context.projectPath,
201
- }
202
- );
203
- // Graceful degradation: continue app operation without beads
204
- return;
205
- }
206
-
207
- // Try to create phase tasks
208
- let phaseTasks: Array<{
209
- phaseId: string;
210
- phaseName: string;
211
- taskId: string;
212
- }>;
213
- try {
214
- phaseTasks = await beadsIntegration.createPhaseTasks(
215
- epicId,
216
- context.stateMachine.states as Record<string, YamlState>,
217
- args.workflow
218
- );
219
- } catch (error) {
220
- const errorMsg = error instanceof Error ? error.message : String(error);
221
- logger.warn(
222
- 'BeadsPlugin: Failed to create beads phase tasks - continuing without phase tracking',
223
- {
224
- error: errorMsg,
225
- epicId,
226
- }
227
- );
228
- // Graceful degradation: continue without phase tracking
229
- return;
230
- }
231
-
232
- // Try to create sequential dependencies between phases
233
- try {
234
- await beadsIntegration.createPhaseDependencies(phaseTasks);
235
- } catch (error) {
236
- const errorMsg = error instanceof Error ? error.message : String(error);
237
- logger.warn(
238
- 'BeadsPlugin: Failed to create phase dependencies - continuing without dependencies',
239
- {
240
- error: errorMsg,
241
- phaseCount: phaseTasks.length,
242
- }
243
- );
244
- // Graceful degradation: continue without dependencies
245
- }
246
-
247
- // Try to update plan file with phase task IDs
248
- try {
249
- await this.updatePlanFileWithPhaseTaskIds(
250
- context.planFilePath,
251
- phaseTasks
252
- );
253
- } catch (error) {
254
- const errorMsg = error instanceof Error ? error.message : String(error);
255
- logger.warn(
256
- 'BeadsPlugin: Failed to update plan file with beads task IDs - continuing without plan file updates',
257
- {
258
- error: errorMsg,
259
- planFilePath: context.planFilePath,
260
- }
261
- );
262
- // Graceful degradation: continue without plan file updates
263
- }
264
-
265
- // Try to create beads state for this conversation
266
- try {
267
- await this.beadsStateManager.createState(
268
- context.conversationId,
269
- epicId,
270
- phaseTasks
271
- );
272
- } catch (error) {
273
- const errorMsg = error instanceof Error ? error.message : String(error);
274
- logger.warn(
275
- 'BeadsPlugin: Failed to create beads state - continuing without state persistence',
276
- {
277
- error: errorMsg,
278
- conversationId: context.conversationId,
279
- }
280
- );
281
- // Graceful degradation: continue without state persistence
282
- }
283
-
284
- logger.info('BeadsPlugin: Beads integration setup complete', {
285
- conversationId: context.conversationId,
286
- epicId,
287
- phaseCount: phaseTasks?.length || 0,
288
- planFilePath: context.planFilePath,
289
- });
290
- } catch (error) {
291
- // Catch-all for unexpected errors: log and continue
292
- const errorMsg = error instanceof Error ? error.message : String(error);
293
- logger.warn(
294
- 'BeadsPlugin: Unexpected error during beads integration setup - continuing application without beads',
295
- {
296
- error: errorMsg,
297
- conversationId: context.conversationId,
298
- }
299
- );
300
- // Graceful degradation: never crash the app due to beads errors
301
- }
302
- }
303
-
304
- /**
305
- * Handle afterPlanFileCreated hook
306
- * Enhances the plan file with beads-specific templates and placeholders
307
- *
308
- * This hook is called after a plan file is created. For beads integration,
309
- * it ensures the plan file has TBD placeholders for phase task IDs that
310
- * will be filled in later by afterStartDevelopment.
311
- *
312
- * Note: Task IDs themselves are created in afterStartDevelopment, not here.
313
- * This hook ensures the plan has the proper structure to receive them.
314
- */
315
- private async handleAfterPlanFileCreated(
316
- _context: PluginHookContext,
317
- planFilePath: string,
318
- content: string
319
- ): Promise<string> {
320
- logger.debug('BeadsPlugin: afterPlanFileCreated hook invoked', {
321
- planFilePath,
322
- contentLength: content.length,
323
- });
324
-
325
- // The plan file is already created with TBD placeholders by BeadsPlanManager
326
- // No additional modifications needed at this stage.
327
- // The beads task IDs will be added by afterStartDevelopment hook
328
- // which calls updatePlanFileWithPhaseTaskIds.
329
- //
330
- // This hook currently returns content unchanged, but could be extended in the
331
- // future for additional beads-specific plan enhancements such as:
332
- // - Adding beads CLI usage instructions
333
- // - Adding task templates
334
- // - Adding workflow guidance
335
- return content;
336
- }
337
-
338
- /**
339
- * Validate beads task completion before phase transition
340
- * Implements graceful error handling: logs errors but continues on non-validation failures
341
- */
342
- private async validateBeadsTaskCompletion(
343
- conversationId: string,
344
- currentPhase: string,
345
- targetPhase: string,
346
- projectPath: string
347
- ): Promise<void> {
348
- try {
349
- // Check if beads backend client is available
350
- let isAvailable = false;
351
- try {
352
- isAvailable = await this.beadsTaskBackendClient.isAvailable();
353
- } catch (error) {
354
- const errorMsg = error instanceof Error ? error.message : String(error);
355
- logger.warn('BeadsPlugin: Failed to check beads availability', {
356
- error: errorMsg,
357
- conversationId,
358
- });
359
- // Graceful degradation: assume beads is unavailable and continue
360
- return;
361
- }
362
-
363
- if (!isAvailable) {
364
- // Not in beads mode or beads not available, skip validation
365
- logger.debug(
366
- 'BeadsPlugin: Skipping beads task validation - beads CLI not available',
367
- {
368
- conversationId,
369
- currentPhase,
370
- targetPhase,
371
- }
372
- );
373
- return;
374
- }
375
-
376
- // Get beads state for this conversation
377
- let currentPhaseTaskId: string | null = null;
378
- try {
379
- currentPhaseTaskId = await this.beadsStateManager.getPhaseTaskId(
380
- conversationId,
381
- currentPhase
382
- );
383
- } catch (error) {
384
- const errorMsg = error instanceof Error ? error.message : String(error);
385
- logger.warn('BeadsPlugin: Failed to get beads phase task ID', {
386
- error: errorMsg,
387
- conversationId,
388
- currentPhase,
389
- });
390
- // Graceful degradation: continue without validation
391
- return;
392
- }
393
-
394
- if (!currentPhaseTaskId) {
395
- // No beads state found for this conversation - fallback to graceful handling
396
- logger.debug(
397
- 'BeadsPlugin: No beads phase task ID found for current phase',
398
- {
399
- conversationId,
400
- currentPhase,
401
- targetPhase,
402
- projectPath,
403
- }
404
- );
405
- return;
406
- }
407
-
408
- logger.debug(
409
- 'BeadsPlugin: Checking for incomplete beads tasks using task backend client',
410
- {
411
- conversationId,
412
- currentPhase,
413
- currentPhaseTaskId,
414
- }
415
- );
416
-
417
- // Use task backend client to validate task completion
418
- let validationResult;
419
- try {
420
- validationResult =
421
- await this.beadsTaskBackendClient.validateTasksCompleted(
422
- currentPhaseTaskId
423
- );
424
- } catch (error) {
425
- const errorMsg = error instanceof Error ? error.message : String(error);
426
- logger.warn(
427
- 'BeadsPlugin: Failed to validate tasks with beads backend',
428
- {
429
- error: errorMsg,
430
- conversationId,
431
- currentPhaseTaskId,
432
- }
433
- );
434
- // Graceful degradation: allow transition if validation fails
435
- return;
436
- }
437
-
438
- if (!validationResult.valid) {
439
- // Get the incomplete tasks from the validation result
440
- const incompleteTasks = validationResult.openTasks || [];
441
-
442
- // Create detailed error message with incomplete tasks
443
- const taskDetails = incompleteTasks
444
- .map(task => ` • ${task.id} - ${task.title || 'Untitled task'}`)
445
- .join('\n');
446
-
447
- const errorMessage = `Cannot proceed to ${targetPhase} - ${incompleteTasks.length} incomplete task(s) in current phase "${currentPhase}":
448
-
449
- ${taskDetails}
450
-
451
- To proceed, check the in-progress-tasks using:
452
-
453
- bd list --parent ${currentPhaseTaskId} --status open
454
-
455
- You can also defer tasks if they're no longer needed:
456
- bd defer <task-id> --until tomorrow`;
457
-
458
- logger.info(
459
- 'BeadsPlugin: Blocking phase transition due to incomplete beads tasks',
460
- {
461
- conversationId,
462
- currentPhase,
463
- targetPhase,
464
- currentPhaseTaskId,
465
- incompleteTaskCount: incompleteTasks.length,
466
- incompleteTaskIds: incompleteTasks.map(t => t.id),
467
- }
468
- );
469
-
470
- throw new Error(errorMessage);
471
- }
472
-
473
- logger.info(
474
- 'BeadsPlugin: All beads tasks completed in current phase, allowing transition',
475
- {
476
- conversationId,
477
- currentPhase,
478
- targetPhase,
479
- currentPhaseTaskId,
480
- }
481
- );
482
- } catch (error) {
483
- // Re-throw validation errors (incomplete tasks)
484
- if (
485
- error instanceof Error &&
486
- error.message.includes('Cannot proceed to')
487
- ) {
488
- throw error;
489
- }
490
-
491
- // Log other errors but allow transition (graceful degradation)
492
- const errorMessage =
493
- error instanceof Error ? error.message : String(error);
494
- logger.warn(
495
- 'BeadsPlugin: Beads task validation failed, allowing transition to proceed',
496
- {
497
- error: errorMessage,
498
- conversationId,
499
- currentPhase,
500
- targetPhase,
501
- projectPath,
502
- }
503
- );
504
- // Graceful degradation: continue without beads state validation
505
- }
506
- }
507
-
508
- /**
509
- * Extract Goal section content from plan file
510
- * Returns the goal content if it exists and is meaningful, otherwise undefined
511
- */
512
- private extractGoalFromPlan(planContent: string): string | undefined {
513
- if (!planContent || typeof planContent !== 'string') {
514
- return undefined;
515
- }
516
-
517
- // Split content into lines for more reliable parsing
518
- const lines = planContent.split('\n');
519
- const goalIndex = lines.findIndex(line => line.trim() === '## Goal');
520
-
521
- if (goalIndex === -1) {
522
- return undefined;
523
- }
524
-
525
- // Find the next section (## anything) after the Goal section
526
- const nextSectionIndex = lines.findIndex(
527
- (line, index) => index > goalIndex && line.trim().startsWith('## ')
528
- );
529
-
530
- // Extract content between Goal and next section (or end of content)
531
- const contentLines =
532
- nextSectionIndex === -1
533
- ? lines.slice(goalIndex + 1)
534
- : lines.slice(goalIndex + 1, nextSectionIndex);
535
-
536
- const goalContent = contentLines.join('\n').trim();
537
-
538
- // Check if the goal content is meaningful (not just a placeholder or comment)
539
- const meaninglessPatterns = [
540
- /^\*.*\*$/, // Enclosed in asterisks like "*Define what you're building...*"
541
- /^To be defined/i,
542
- /^TBD$/i,
543
- /^TODO/i,
544
- /^Define what you're building/i,
545
- /^This will be updated/i,
546
- ];
547
-
548
- const isMeaningless = meaninglessPatterns.some(pattern =>
549
- pattern.test(goalContent)
550
- );
551
-
552
- if (isMeaningless || goalContent.length < 10) {
553
- return undefined;
554
- }
555
-
556
- return goalContent;
557
- }
558
-
559
- /**
560
- * Update plan file to include beads phase task IDs in comments
561
- * Implements graceful degradation: logs errors but continues app operation if update fails
562
- */
563
- private async updatePlanFileWithPhaseTaskIds(
564
- planFilePath: string,
565
- phaseTasks: Array<{ phaseId: string; phaseName: string; taskId: string }>
566
- ): Promise<void> {
567
- try {
568
- const { readFile, writeFile } = await import('node:fs/promises');
569
-
570
- // Try to read the plan file
571
- let content: string;
572
- try {
573
- content = await readFile(planFilePath, 'utf-8');
574
- } catch (error) {
575
- const errorMsg = error instanceof Error ? error.message : String(error);
576
- logger.warn('BeadsPlugin: Failed to read plan file for update', {
577
- error: errorMsg,
578
- planFilePath,
579
- });
580
- // Graceful degradation: continue without updating plan file
581
- return;
582
- }
583
-
584
- // Replace TBD placeholders with actual task IDs
585
- for (const phaseTask of phaseTasks) {
586
- const phaseHeader = `## ${phaseTask.phaseName}`;
587
- const placeholderPattern = new RegExp(
588
- `(${phaseHeader.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\s*\n)<!-- beads-phase-id: TBD -->`,
589
- 'g'
590
- );
591
- content = content.replace(
592
- placeholderPattern,
593
- `$1<!-- beads-phase-id: ${phaseTask.taskId} -->`
594
- );
595
- }
596
-
597
- // Validate that all TBD placeholders were replaced
598
- const remainingTBDs = content.match(/<!-- beads-phase-id: TBD -->/g);
599
- if (remainingTBDs && remainingTBDs.length > 0) {
600
- logger.warn(
601
- 'BeadsPlugin: Failed to replace all TBD placeholders in plan file',
602
- {
603
- planFilePath,
604
- unreplacedCount: remainingTBDs.length,
605
- reason:
606
- 'Phase names in plan file may not match workflow phases or beads task creation may have failed for some phases',
607
- }
608
- );
609
- // Graceful degradation: continue without full update
610
- // But still try to write what we have
611
- }
612
-
613
- // Try to write the updated plan file
614
- try {
615
- await writeFile(planFilePath, content, 'utf-8');
616
- } catch (error) {
617
- const errorMsg = error instanceof Error ? error.message : String(error);
618
- logger.warn('BeadsPlugin: Failed to write updated plan file', {
619
- error: errorMsg,
620
- planFilePath,
621
- });
622
- // Graceful degradation: continue without writing to plan file
623
- return;
624
- }
625
-
626
- logger.info(
627
- 'BeadsPlugin: Successfully updated plan file with beads phase task IDs',
628
- {
629
- planFilePath,
630
- phaseTaskCount: phaseTasks.length,
631
- replacedTasks: phaseTasks.map(
632
- task => `${task.phaseName}: ${task.taskId}`
633
- ),
634
- }
635
- );
636
- } catch (error) {
637
- // Catch-all for unexpected errors
638
- const errorMsg = error instanceof Error ? error.message : String(error);
639
- logger.warn(
640
- 'BeadsPlugin: Unexpected error while updating plan file with phase task IDs',
641
- {
642
- error: errorMsg,
643
- planFilePath,
644
- }
645
- );
646
- // Graceful degradation: never crash the app due to plan file updates
647
- }
648
- }
649
- }