@codemcp/workflows 4.10.1 → 4.10.2

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