@codemcp/workflows 4.7.0 → 4.9.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.
Files changed (61) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/components/beads/beads-instruction-generator.d.ts +48 -0
  3. package/dist/components/beads/beads-instruction-generator.d.ts.map +1 -0
  4. package/dist/components/beads/beads-instruction-generator.js +182 -0
  5. package/dist/components/beads/beads-instruction-generator.js.map +1 -0
  6. package/dist/components/beads/beads-plan-manager.d.ts +66 -0
  7. package/dist/components/beads/beads-plan-manager.d.ts.map +1 -0
  8. package/dist/components/beads/beads-plan-manager.js +288 -0
  9. package/dist/components/beads/beads-plan-manager.js.map +1 -0
  10. package/dist/components/beads/beads-task-backend-client.d.ts +43 -0
  11. package/dist/components/beads/beads-task-backend-client.d.ts.map +1 -0
  12. package/dist/components/beads/beads-task-backend-client.js +178 -0
  13. package/dist/components/beads/beads-task-backend-client.js.map +1 -0
  14. package/dist/components/server-components-factory.d.ts +39 -0
  15. package/dist/components/server-components-factory.d.ts.map +1 -0
  16. package/dist/components/server-components-factory.js +62 -0
  17. package/dist/components/server-components-factory.js.map +1 -0
  18. package/dist/server-config.d.ts.map +1 -1
  19. package/dist/server-config.js +8 -4
  20. package/dist/server-config.js.map +1 -1
  21. package/dist/server-implementation.d.ts +1 -1
  22. package/dist/tool-handlers/get-tool-info.d.ts.map +1 -1
  23. package/dist/tool-handlers/get-tool-info.js +2 -1
  24. package/dist/tool-handlers/get-tool-info.js.map +1 -1
  25. package/dist/tool-handlers/proceed-to-phase.d.ts +5 -0
  26. package/dist/tool-handlers/proceed-to-phase.d.ts.map +1 -1
  27. package/dist/tool-handlers/proceed-to-phase.js +95 -0
  28. package/dist/tool-handlers/proceed-to-phase.js.map +1 -1
  29. package/dist/tool-handlers/start-development.d.ts.map +1 -1
  30. package/dist/tool-handlers/start-development.js +7 -3
  31. package/dist/tool-handlers/start-development.js.map +1 -1
  32. package/dist/tool-handlers/whats-next.d.ts +0 -12
  33. package/dist/tool-handlers/whats-next.d.ts.map +1 -1
  34. package/dist/tool-handlers/whats-next.js +1 -88
  35. package/dist/tool-handlers/whats-next.js.map +1 -1
  36. package/dist/types.d.ts +7 -4
  37. package/dist/types.d.ts.map +1 -1
  38. package/dist/version-info.d.ts +30 -0
  39. package/dist/version-info.d.ts.map +1 -0
  40. package/dist/version-info.js +178 -0
  41. package/dist/version-info.js.map +1 -0
  42. package/package.json +2 -2
  43. package/src/components/beads/beads-instruction-generator.ts +261 -0
  44. package/src/components/beads/beads-plan-manager.ts +358 -0
  45. package/src/components/beads/beads-task-backend-client.ts +232 -0
  46. package/src/components/server-components-factory.ts +86 -0
  47. package/src/server-config.ts +9 -4
  48. package/src/tool-handlers/get-tool-info.ts +2 -1
  49. package/src/tool-handlers/proceed-to-phase.ts +140 -0
  50. package/src/tool-handlers/start-development.ts +14 -3
  51. package/src/tool-handlers/whats-next.ts +4 -117
  52. package/src/types.ts +7 -4
  53. package/src/version-info.ts +213 -0
  54. package/test/e2e/component-substitution.test.ts +208 -0
  55. package/test/unit/beads-instruction-generator.test.ts +847 -0
  56. package/test/unit/beads-phase-task-id-integration.test.ts +557 -0
  57. package/test/unit/server-components-factory.test.ts +279 -0
  58. package/test/unit/setup-project-docs-handler.test.ts +3 -2
  59. package/test/utils/e2e-test-setup.ts +0 -1
  60. package/test/utils/temp-files.ts +12 -0
  61. package/tsconfig.build.tsbuildinfo +1 -1
@@ -0,0 +1,232 @@
1
+ /**
2
+ * Beads Task Backend Client
3
+ *
4
+ * Implementation of ITaskBackendClient for beads task management system.
5
+ * Handles CLI operations and task validation for beads backend.
6
+ */
7
+
8
+ import {
9
+ type ITaskBackendClient,
10
+ type BackendTask,
11
+ type TaskValidationResult,
12
+ } from '@codemcp/workflows-core';
13
+ import { execSync } from 'node:child_process';
14
+ import { createLogger } from '@codemcp/workflows-core';
15
+
16
+ const logger = createLogger('BeadsTaskBackendClient');
17
+
18
+ /**
19
+ * Beads-specific implementation of task backend client
20
+ */
21
+ export class BeadsTaskBackendClient implements ITaskBackendClient {
22
+ private projectPath: string;
23
+
24
+ constructor(projectPath: string) {
25
+ this.projectPath = projectPath;
26
+ }
27
+
28
+ /**
29
+ * Execute a beads command safely
30
+ */
31
+ private async executeBeadsCommand(
32
+ args: string[]
33
+ ): Promise<{ success: boolean; stdout?: string; stderr?: string }> {
34
+ try {
35
+ const command = `bd ${args.join(' ')}`;
36
+ logger.debug('Executing beads command', {
37
+ command,
38
+ projectPath: this.projectPath,
39
+ });
40
+
41
+ const stdout = execSync(`bd ${args.join(' ')}`, {
42
+ cwd: this.projectPath,
43
+ encoding: 'utf-8',
44
+ stdio: ['ignore', 'pipe', 'pipe'],
45
+ });
46
+
47
+ return { success: true, stdout };
48
+ } catch (error: unknown) {
49
+ const execError = error as {
50
+ stderr?: string;
51
+ stdout?: string;
52
+ status?: number;
53
+ };
54
+ logger.warn('Beads command failed', {
55
+ args,
56
+ error: error instanceof Error ? error.message : String(error),
57
+ stderr: execError.stderr,
58
+ stdout: execError.stdout,
59
+ });
60
+
61
+ return {
62
+ success: false,
63
+ stderr:
64
+ execError.stderr ||
65
+ (error instanceof Error ? error.message : String(error)),
66
+ stdout: execError.stdout,
67
+ };
68
+ }
69
+ }
70
+
71
+ /**
72
+ * Check if beads backend is available
73
+ */
74
+ async isAvailable(): Promise<boolean> {
75
+ try {
76
+ const result = await this.executeBeadsCommand(['--version']);
77
+ return result.success;
78
+ } catch (_error) {
79
+ return false;
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Get all open tasks for a given parent task
85
+ */
86
+ async getOpenTasks(parentTaskId: string): Promise<BackendTask[]> {
87
+ try {
88
+ const result = await this.executeBeadsCommand([
89
+ 'list',
90
+ '--parent',
91
+ parentTaskId,
92
+ '--status',
93
+ 'open',
94
+ ]);
95
+
96
+ if (!result.success || !result.stdout) {
97
+ return [];
98
+ }
99
+
100
+ // Parse text output from beads CLI (since JSON format may not be available)
101
+ // This is a simplified parser - would need to be adjusted based on actual beads output format
102
+ const lines = result.stdout.trim().split('\n');
103
+ const tasks: BackendTask[] = [];
104
+
105
+ for (const line of lines) {
106
+ if (
107
+ line.trim() &&
108
+ !line.startsWith('○') &&
109
+ !line.startsWith('●') &&
110
+ !line.includes('Tip:')
111
+ ) {
112
+ // Extract task ID and title from beads output format
113
+ // This is based on observed beads CLI output format
114
+ const match = line.match(/^○?\s*([^\s]+)\s+.*?\s+-\s+(.+)$/);
115
+ if (match) {
116
+ tasks.push({
117
+ id: match[1] || '',
118
+ title: match[2] || '',
119
+ status: 'open',
120
+ priority: 2,
121
+ parent: parentTaskId,
122
+ });
123
+ }
124
+ }
125
+ }
126
+
127
+ return tasks;
128
+ } catch (_error) {
129
+ return [];
130
+ }
131
+ }
132
+
133
+ /**
134
+ * Validate that all tasks under a parent are completed
135
+ */
136
+ async validateTasksCompleted(
137
+ parentTaskId: string
138
+ ): Promise<TaskValidationResult> {
139
+ const openTasks = await this.getOpenTasks(parentTaskId);
140
+
141
+ return {
142
+ valid: openTasks.length === 0,
143
+ openTasks,
144
+ message:
145
+ openTasks.length > 0
146
+ ? `Found ${openTasks.length} incomplete task(s). All tasks must be completed before proceeding to the next phase.`
147
+ : 'All tasks completed.',
148
+ };
149
+ }
150
+
151
+ /**
152
+ * Create a new task under a parent
153
+ */
154
+ async createTask(
155
+ title: string,
156
+ parentTaskId: string,
157
+ priority = 2
158
+ ): Promise<string> {
159
+ const result = await this.executeBeadsCommand([
160
+ 'create',
161
+ `"${title}"`,
162
+ '--parent',
163
+ parentTaskId,
164
+ '-p',
165
+ priority.toString(),
166
+ ]);
167
+
168
+ if (!result.success) {
169
+ throw new Error(
170
+ `Failed to create task: ${result.stderr || 'Unknown error'}`
171
+ );
172
+ }
173
+
174
+ // Extract task ID from beads output
175
+ // Based on beads CLI output format: "✓ Created issue: task-id"
176
+ const match =
177
+ result.stdout?.match(/✓ Created issue: ([\w\d.-]+)/) ||
178
+ result.stdout?.match(/Created issue: ([\w\d.-]+)/) ||
179
+ result.stdout?.match(/Created (bd-[\w\d.]+)/);
180
+
181
+ if (!match) {
182
+ throw new Error(
183
+ `Failed to extract task ID from beads output: ${result.stdout || 'No output'}`
184
+ );
185
+ }
186
+
187
+ return match[1] || '';
188
+ }
189
+
190
+ /**
191
+ * Update task status
192
+ */
193
+ async updateTaskStatus(
194
+ taskId: string,
195
+ status: 'open' | 'in_progress' | 'completed' | 'cancelled'
196
+ ): Promise<void> {
197
+ const beadsStatus = this.mapStatusToBeads(status);
198
+
199
+ const result = await this.executeBeadsCommand([
200
+ 'update',
201
+ taskId,
202
+ '--status',
203
+ beadsStatus,
204
+ ]);
205
+
206
+ if (!result.success) {
207
+ throw new Error(
208
+ `Failed to update task status: ${result.stderr || 'Unknown error'}`
209
+ );
210
+ }
211
+ }
212
+
213
+ /**
214
+ * Map our status enum to beads CLI status values
215
+ */
216
+ private mapStatusToBeads(
217
+ status: 'open' | 'in_progress' | 'completed' | 'cancelled'
218
+ ): string {
219
+ switch (status) {
220
+ case 'open':
221
+ return 'open';
222
+ case 'in_progress':
223
+ return 'in_progress';
224
+ case 'completed':
225
+ return 'completed';
226
+ case 'cancelled':
227
+ return 'cancelled';
228
+ default:
229
+ return 'open';
230
+ }
231
+ }
232
+ }
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Server Components Factory
3
+ *
4
+ * Factory for creating server components based on configuration.
5
+ * Implements strategy pattern to enable component substitution based on task backend.
6
+ */
7
+
8
+ import {
9
+ TaskBackendManager,
10
+ PlanManager,
11
+ InstructionGenerator,
12
+ type TaskBackendConfig,
13
+ } from '@codemcp/workflows-core';
14
+
15
+ // Beads implementations
16
+ import { BeadsPlanManager } from './beads/beads-plan-manager.js';
17
+ import { BeadsInstructionGenerator } from './beads/beads-instruction-generator.js';
18
+ import { BeadsTaskBackendClient } from './beads/beads-task-backend-client.js';
19
+
20
+ export interface ComponentFactoryOptions {
21
+ taskBackend?: TaskBackendConfig;
22
+ projectPath?: string;
23
+ }
24
+
25
+ /**
26
+ * Factory class for creating server components with appropriate strategy implementations
27
+ */
28
+ export class ServerComponentsFactory {
29
+ private taskBackend: TaskBackendConfig;
30
+ private projectPath?: string;
31
+
32
+ constructor(options: ComponentFactoryOptions = {}) {
33
+ this.taskBackend =
34
+ options.taskBackend || TaskBackendManager.detectTaskBackend();
35
+ this.projectPath = options.projectPath;
36
+ }
37
+
38
+ /**
39
+ * Create the appropriate plan manager implementation
40
+ */
41
+ createPlanManager(): PlanManager | BeadsPlanManager {
42
+ if (this.taskBackend.backend === 'beads' && this.taskBackend.isAvailable) {
43
+ return new BeadsPlanManager();
44
+ }
45
+
46
+ // Default markdown-based plan manager
47
+ return new PlanManager();
48
+ }
49
+
50
+ /**
51
+ * Create the appropriate instruction generator implementation
52
+ */
53
+ createInstructionGenerator():
54
+ | InstructionGenerator
55
+ | BeadsInstructionGenerator {
56
+ if (this.taskBackend.backend === 'beads' && this.taskBackend.isAvailable) {
57
+ return new BeadsInstructionGenerator();
58
+ }
59
+
60
+ // Default markdown-based instruction generator
61
+ return new InstructionGenerator(new PlanManager());
62
+ }
63
+
64
+ /**
65
+ * Create the appropriate task backend client implementation
66
+ */
67
+ createTaskBackendClient(): BeadsTaskBackendClient | null {
68
+ if (
69
+ this.taskBackend.backend === 'beads' &&
70
+ this.taskBackend.isAvailable &&
71
+ this.projectPath
72
+ ) {
73
+ return new BeadsTaskBackendClient(this.projectPath);
74
+ }
75
+
76
+ // No task backend client for markdown mode
77
+ return null;
78
+ }
79
+
80
+ /**
81
+ * Get the current task backend configuration
82
+ */
83
+ getTaskBackend(): TaskBackendConfig {
84
+ return this.taskBackend;
85
+ }
86
+ }
@@ -15,8 +15,6 @@ import { FileStorage } from '@codemcp/workflows-core';
15
15
  import type { IPersistence } from '@codemcp/workflows-core';
16
16
  import { ConversationManager } from '@codemcp/workflows-core';
17
17
  import { TransitionEngine } from '@codemcp/workflows-core';
18
- import { InstructionGenerator } from '@codemcp/workflows-core';
19
- import { PlanManager } from '@codemcp/workflows-core';
20
18
  import { InteractionLogger } from '@codemcp/workflows-core';
21
19
  import { WorkflowManager } from '@codemcp/workflows-core';
22
20
  import { GitManager } from '@codemcp/workflows-core';
@@ -36,6 +34,7 @@ import {
36
34
  generateWorkflowDescription,
37
35
  } from './server-helpers.js';
38
36
  import { notificationService } from './notification-service.js';
37
+ import { ServerComponentsFactory } from './components/server-components-factory.js';
39
38
 
40
39
  const logger = createLogger('ServerConfig');
41
40
 
@@ -113,8 +112,14 @@ export async function initializeServerComponents(
113
112
  );
114
113
  const transitionEngine = new TransitionEngine(projectPath);
115
114
  transitionEngine.setConversationManager(conversationManager);
116
- const planManager = new PlanManager();
117
- const instructionGenerator = new InstructionGenerator(planManager);
115
+
116
+ // Use factory pattern for strategy-based component creation
117
+ const componentsFactory = new ServerComponentsFactory({
118
+ projectPath,
119
+ taskBackend: config.taskBackend, // Pass through task backend config if provided
120
+ });
121
+ const planManager = componentsFactory.createPlanManager();
122
+ const instructionGenerator = componentsFactory.createInstructionGenerator();
118
123
 
119
124
  // Always create interaction logger as it's critical for transition engine logic
120
125
  // (determining first call from initial state)
@@ -9,6 +9,7 @@ import { z } from 'zod';
9
9
  import { BaseToolHandler } from './base-tool-handler.js';
10
10
  import { createLogger } from '@codemcp/workflows-core';
11
11
  import { ServerContext } from '../types.js';
12
+ import { getFormattedVersion } from '../version-info.js';
12
13
 
13
14
  const logger = createLogger('GetToolInfoHandler');
14
15
 
@@ -207,7 +208,7 @@ export class GetToolInfoHandler extends BaseToolHandler<
207
208
  // Build the complete response
208
209
  const response: GetToolInfoResponse = {
209
210
  tool_name: 'Responsible Vibe MCP - Development Workflow Management',
210
- version: '3.1.6-monorepo', // This should ideally come from package.json
211
+ version: getFormattedVersion(),
211
212
  purpose:
212
213
  'Structured development workflows with guided phase transitions and conversation state management',
213
214
  description:
@@ -8,6 +8,8 @@
8
8
  import { ConversationRequiredToolHandler } from './base-tool-handler.js';
9
9
  import { validateRequiredArgs } from '../server-helpers.js';
10
10
  import type { ConversationContext } from '@codemcp/workflows-core';
11
+ import { BeadsStateManager } from '@codemcp/workflows-core';
12
+ import { ServerComponentsFactory } from '../components/server-components-factory.js';
11
13
  import { ServerContext } from '../types.js';
12
14
 
13
15
  /**
@@ -78,6 +80,14 @@ export class ProceedToPhaseHandler extends ConversationRequiredToolHandler<
78
80
  context
79
81
  );
80
82
 
83
+ // Validate beads task completion if in beads mode
84
+ await this.validateBeadsTaskCompletion(
85
+ conversationId,
86
+ currentPhase,
87
+ target_phase,
88
+ conversationContext.projectPath
89
+ );
90
+
81
91
  // Ensure state machine is loaded for this project
82
92
  this.ensureStateMachineForProject(context, conversationContext.projectPath);
83
93
 
@@ -279,4 +289,134 @@ export class ProceedToPhaseHandler extends ConversationRequiredToolHandler<
279
289
  }
280
290
  }
281
291
  }
292
+
293
+ /**
294
+ * Validate that all beads tasks in the current phase are completed
295
+ * before proceeding to the next phase. Only applies when beads mode is active.
296
+ */
297
+ private async validateBeadsTaskCompletion(
298
+ conversationId: string,
299
+ currentPhase: string,
300
+ targetPhase: string,
301
+ projectPath: string
302
+ ): Promise<void> {
303
+ try {
304
+ // Use factory to create task backend client (strategy pattern)
305
+ const factory = new ServerComponentsFactory({ projectPath });
306
+ const taskBackendClient = factory.createTaskBackendClient();
307
+
308
+ if (!taskBackendClient) {
309
+ // Not in beads mode or beads not available, skip validation
310
+ this.logger.debug(
311
+ 'Skipping beads task validation - no task backend client available',
312
+ {
313
+ conversationId,
314
+ currentPhase,
315
+ targetPhase,
316
+ }
317
+ );
318
+ return;
319
+ }
320
+
321
+ // Get beads state for this conversation
322
+ const beadsStateManager = new BeadsStateManager(projectPath);
323
+ const currentPhaseTaskId = await beadsStateManager.getPhaseTaskId(
324
+ conversationId,
325
+ currentPhase
326
+ );
327
+
328
+ if (!currentPhaseTaskId) {
329
+ // No beads state found for this conversation - fallback to graceful handling
330
+ this.logger.debug('No beads phase task ID found for current phase', {
331
+ conversationId,
332
+ currentPhase,
333
+ targetPhase,
334
+ projectPath,
335
+ });
336
+ return;
337
+ }
338
+
339
+ this.logger.debug(
340
+ 'Checking for incomplete beads tasks using task backend client',
341
+ {
342
+ conversationId,
343
+ currentPhase,
344
+ currentPhaseTaskId,
345
+ }
346
+ );
347
+
348
+ // Use task backend client to validate task completion (strategy pattern)
349
+ const validationResult =
350
+ await taskBackendClient.validateTasksCompleted(currentPhaseTaskId);
351
+
352
+ if (!validationResult.valid) {
353
+ // Get the incomplete tasks from the validation result
354
+ const incompleteTasks = validationResult.openTasks;
355
+
356
+ // Create detailed error message with incomplete tasks
357
+ const taskDetails = incompleteTasks
358
+ .map(task => ` • ${task.id} - ${task.title || 'Untitled task'}`)
359
+ .join('\n');
360
+
361
+ const errorMessage = `Cannot proceed to ${targetPhase} - ${incompleteTasks.length} incomplete task(s) in current phase "${currentPhase}":
362
+
363
+ ${taskDetails}
364
+
365
+ To proceed, please complete these tasks using:
366
+ bd close <task-id>
367
+
368
+ Or check remaining work with:
369
+ bd list --parent ${currentPhaseTaskId} --status open
370
+
371
+ You can also defer tasks if they're no longer needed:
372
+ bd defer <task-id> --until tomorrow`;
373
+
374
+ this.logger.info(
375
+ 'Blocking phase transition due to incomplete beads tasks',
376
+ {
377
+ conversationId,
378
+ currentPhase,
379
+ targetPhase,
380
+ currentPhaseTaskId,
381
+ incompleteTaskCount: incompleteTasks.length,
382
+ incompleteTaskIds: incompleteTasks.map(t => t.id),
383
+ }
384
+ );
385
+
386
+ throw new Error(errorMessage);
387
+ }
388
+
389
+ this.logger.info(
390
+ 'All beads tasks completed in current phase, allowing transition',
391
+ {
392
+ conversationId,
393
+ currentPhase,
394
+ targetPhase,
395
+ currentPhaseTaskId,
396
+ }
397
+ );
398
+ } catch (error) {
399
+ // Re-throw validation errors (incomplete tasks)
400
+ if (
401
+ error instanceof Error &&
402
+ error.message.includes('Cannot proceed to')
403
+ ) {
404
+ throw error;
405
+ }
406
+
407
+ // Log other errors but allow transition (graceful degradation)
408
+ const errorMessage =
409
+ error instanceof Error ? error.message : String(error);
410
+ this.logger.warn(
411
+ 'Beads task validation failed, allowing transition to proceed',
412
+ {
413
+ error: errorMessage,
414
+ conversationId,
415
+ currentPhase,
416
+ targetPhase,
417
+ projectPath,
418
+ }
419
+ );
420
+ }
421
+ }
282
422
  }
@@ -17,7 +17,11 @@ import { GitCommitConfig } from '@codemcp/workflows-core';
17
17
  import { GitManager } from '@codemcp/workflows-core';
18
18
  import type { YamlStateMachine } from '@codemcp/workflows-core';
19
19
  import { ProjectDocsManager, ProjectDocsInfo } from '@codemcp/workflows-core';
20
- import { TaskBackendManager, BeadsIntegration } from '@codemcp/workflows-core';
20
+ import {
21
+ TaskBackendManager,
22
+ BeadsIntegration,
23
+ BeadsStateManager,
24
+ } from '@codemcp/workflows-core';
21
25
  import { ServerContext } from '../types.js';
22
26
 
23
27
  /**
@@ -215,7 +219,8 @@ export class StartDevelopmentHandler extends BaseToolHandler<
215
219
  projectPath,
216
220
  stateMachine,
217
221
  selectedWorkflow,
218
- conversationContext.planFilePath
222
+ conversationContext.planFilePath,
223
+ conversationContext.conversationId
219
224
  );
220
225
  }
221
226
 
@@ -647,7 +652,8 @@ ${templateOptionsText}
647
652
  projectPath: string,
648
653
  stateMachine: YamlStateMachine,
649
654
  workflowName: string,
650
- planFilePath: string
655
+ planFilePath: string,
656
+ conversationId: string
651
657
  ): Promise<void> {
652
658
  try {
653
659
  const beadsIntegration = new BeadsIntegration(projectPath);
@@ -673,11 +679,16 @@ ${templateOptionsText}
673
679
  // Update plan file with phase task IDs
674
680
  await this.updatePlanFileWithPhaseTaskIds(planFilePath, phaseTasks);
675
681
 
682
+ // Create beads state for this conversation
683
+ const beadsStateManager = new BeadsStateManager(projectPath);
684
+ await beadsStateManager.createState(conversationId, epicId, phaseTasks);
685
+
676
686
  this.logger.info('Beads integration setup complete', {
677
687
  projectPath,
678
688
  epicId,
679
689
  phaseCount: phaseTasks.length,
680
690
  planFilePath,
691
+ conversationId,
681
692
  });
682
693
  } catch (error) {
683
694
  this.logger.error(