@hyperdrive.bot/bmad-workflow 1.0.17 → 1.0.19

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 (110) hide show
  1. package/dist/commands/config/show.js +8 -2
  2. package/dist/commands/decompose.js +26 -5
  3. package/dist/commands/epics/create.d.ts +1 -0
  4. package/dist/commands/mcp/add.d.ts +16 -0
  5. package/dist/commands/mcp/add.js +77 -0
  6. package/dist/commands/mcp/credential/get.d.ts +14 -0
  7. package/dist/commands/mcp/credential/get.js +35 -0
  8. package/dist/commands/mcp/credential/list.d.ts +17 -0
  9. package/dist/commands/mcp/credential/list.js +67 -0
  10. package/dist/commands/mcp/credential/remove.d.ts +18 -0
  11. package/dist/commands/mcp/credential/remove.js +84 -0
  12. package/dist/commands/mcp/credential/set.d.ts +16 -0
  13. package/dist/commands/mcp/credential/set.js +41 -0
  14. package/dist/commands/mcp/credential/validate.d.ts +12 -0
  15. package/dist/commands/mcp/credential/validate.js +150 -0
  16. package/dist/commands/mcp/list.d.ts +17 -0
  17. package/dist/commands/mcp/list.js +80 -0
  18. package/dist/commands/mcp/logs.d.ts +15 -0
  19. package/dist/commands/mcp/logs.js +64 -0
  20. package/dist/commands/mcp/preset.d.ts +15 -0
  21. package/dist/commands/mcp/preset.js +84 -0
  22. package/dist/commands/mcp/remove.d.ts +14 -0
  23. package/dist/commands/mcp/remove.js +36 -0
  24. package/dist/commands/mcp/start.d.ts +12 -0
  25. package/dist/commands/mcp/start.js +80 -0
  26. package/dist/commands/mcp/status.d.ts +30 -0
  27. package/dist/commands/mcp/status.js +180 -0
  28. package/dist/commands/mcp/stop.d.ts +12 -0
  29. package/dist/commands/mcp/stop.js +47 -0
  30. package/dist/commands/stories/create.d.ts +1 -0
  31. package/dist/commands/stories/develop.d.ts +1 -0
  32. package/dist/commands/stories/qa.js +34 -75
  33. package/dist/commands/stories/review.d.ts +124 -0
  34. package/dist/commands/stories/review.js +516 -0
  35. package/dist/commands/workflow.d.ts +89 -0
  36. package/dist/commands/workflow.js +487 -14
  37. package/dist/mcp/types.d.ts +99 -0
  38. package/dist/mcp/types.js +7 -0
  39. package/dist/mcp/utils/docker-utils.d.ts +56 -0
  40. package/dist/mcp/utils/docker-utils.js +108 -0
  41. package/dist/mcp/utils/template-loader.d.ts +21 -0
  42. package/dist/mcp/utils/template-loader.js +60 -0
  43. package/dist/models/agent-options.d.ts +10 -1
  44. package/dist/models/index.d.ts +1 -0
  45. package/dist/models/index.js +1 -0
  46. package/dist/models/workflow-callbacks.d.ts +251 -0
  47. package/dist/models/workflow-callbacks.js +10 -0
  48. package/dist/models/workflow-config.d.ts +77 -0
  49. package/dist/models/workflow-result.d.ts +7 -0
  50. package/dist/services/WorkflowReporter.d.ts +165 -0
  51. package/dist/services/WorkflowReporter.js +691 -0
  52. package/dist/services/agents/claude-agent-runner.js +25 -4
  53. package/dist/services/file-system/path-resolver.d.ts +10 -0
  54. package/dist/services/file-system/path-resolver.js +12 -0
  55. package/dist/services/mcp/mcp-config-manager.d.ts +54 -0
  56. package/dist/services/mcp/mcp-config-manager.js +146 -0
  57. package/dist/services/mcp/mcp-context-injector.d.ts +92 -0
  58. package/dist/services/mcp/mcp-context-injector.js +168 -0
  59. package/dist/services/mcp/mcp-credential-manager.d.ts +48 -0
  60. package/dist/services/mcp/mcp-credential-manager.js +124 -0
  61. package/dist/services/mcp/mcp-health-checker.d.ts +56 -0
  62. package/dist/services/mcp/mcp-health-checker.js +162 -0
  63. package/dist/services/mcp/types/health-types.d.ts +31 -0
  64. package/dist/services/mcp/types/health-types.js +7 -0
  65. package/dist/services/orchestration/dependency-graph-executor.js +1 -1
  66. package/dist/services/orchestration/task-decomposition-service.d.ts +2 -1
  67. package/dist/services/orchestration/task-decomposition-service.js +90 -36
  68. package/dist/services/orchestration/workflow-orchestrator.d.ts +87 -3
  69. package/dist/services/orchestration/workflow-orchestrator.js +1169 -289
  70. package/dist/services/review/ai-review-scanner.d.ts +66 -0
  71. package/dist/services/review/ai-review-scanner.js +142 -0
  72. package/dist/services/review/coderabbit-scanner.d.ts +25 -0
  73. package/dist/services/review/coderabbit-scanner.js +31 -0
  74. package/dist/services/review/index.d.ts +20 -0
  75. package/dist/services/review/index.js +15 -0
  76. package/dist/services/review/lint-scanner.d.ts +46 -0
  77. package/dist/services/review/lint-scanner.js +172 -0
  78. package/dist/services/review/review-config.d.ts +62 -0
  79. package/dist/services/review/review-config.js +91 -0
  80. package/dist/services/review/review-phase-executor.d.ts +69 -0
  81. package/dist/services/review/review-phase-executor.js +152 -0
  82. package/dist/services/review/review-queue.d.ts +98 -0
  83. package/dist/services/review/review-queue.js +174 -0
  84. package/dist/services/review/review-reporter.d.ts +94 -0
  85. package/dist/services/review/review-reporter.js +386 -0
  86. package/dist/services/review/scanner-factory.d.ts +42 -0
  87. package/dist/services/review/scanner-factory.js +60 -0
  88. package/dist/services/review/self-heal-loop.d.ts +58 -0
  89. package/dist/services/review/self-heal-loop.js +132 -0
  90. package/dist/services/review/severity-classifier.d.ts +17 -0
  91. package/dist/services/review/severity-classifier.js +314 -0
  92. package/dist/services/review/tech-debt-tracker.d.ts +52 -0
  93. package/dist/services/review/tech-debt-tracker.js +245 -0
  94. package/dist/services/review/types.d.ts +93 -0
  95. package/dist/services/review/types.js +23 -0
  96. package/dist/services/scaffolding/workflow-session-scaffolder.d.ts +182 -0
  97. package/dist/services/scaffolding/workflow-session-scaffolder.js +236 -0
  98. package/dist/services/validation/config-validator.d.ts +84 -0
  99. package/dist/services/validation/config-validator.js +78 -0
  100. package/dist/utils/colors.d.ts +10 -10
  101. package/dist/utils/colors.js +15 -15
  102. package/dist/utils/credential-utils.d.ts +14 -0
  103. package/dist/utils/credential-utils.js +19 -0
  104. package/dist/utils/duration.d.ts +41 -0
  105. package/dist/utils/duration.js +89 -0
  106. package/dist/utils/listr2-helpers.d.ts +216 -0
  107. package/dist/utils/listr2-helpers.js +334 -0
  108. package/dist/utils/shared-flags.d.ts +1 -0
  109. package/dist/utils/shared-flags.js +11 -2
  110. package/package.json +6 -3
@@ -0,0 +1,334 @@
1
+ /**
2
+ * listr2 Helper Functions
3
+ *
4
+ * Utility functions for creating and managing listr2 task structures
5
+ * used by the WorkflowReporter for visualizing workflow progress.
6
+ */
7
+ import chalk from 'chalk';
8
+ import { formatDuration } from './formatters.js';
9
+ /**
10
+ * Default terminal width constraint for output formatting
11
+ */
12
+ export const DEFAULT_TERMINAL_WIDTH = 80;
13
+ /**
14
+ * Regex pattern for detecting ANSI escape codes
15
+ */
16
+ export const ANSI_ESCAPE_PATTERN = /\u001B\[[0-9;]*[a-zA-Z]/g;
17
+ /**
18
+ * Strip ANSI escape codes from text
19
+ *
20
+ * @param text - Text potentially containing ANSI codes
21
+ * @returns Clean text without ANSI escape sequences
22
+ */
23
+ export const stripAnsi = (text) => text.replaceAll(ANSI_ESCAPE_PATTERN, '');
24
+ /**
25
+ * Check if the current environment is a TTY
26
+ *
27
+ * @returns true if stdout is a TTY, false otherwise
28
+ */
29
+ export const isTTY = () => process.stdout.isTTY === true;
30
+ /**
31
+ * Plain-text status indicators (no ANSI codes)
32
+ */
33
+ export const PLAIN_STATUS_INDICATORS = {
34
+ completed: '[+]',
35
+ failed: '[X]',
36
+ queued: '[.]',
37
+ running: '[*]',
38
+ };
39
+ /**
40
+ * Format a plain-text phase header for non-TTY output
41
+ *
42
+ * @param phaseName - Phase name (epic, story, dev, qa)
43
+ * @returns Plain-text header string (e.g., "=== Phase: Epic Generation ===")
44
+ */
45
+ export const formatPlainPhaseHeader = (phaseName) => {
46
+ const title = PHASE_TITLES[phaseName.toLowerCase()] || `${phaseName} Phase`;
47
+ return `=== Phase: ${title} ===`;
48
+ };
49
+ /**
50
+ * Format a plain-text spawn marker for non-TTY output
51
+ *
52
+ * @param itemId - Item identifier
53
+ * @param status - Spawn status (STARTED, COMPLETED, FAILED)
54
+ * @param durationMs - Optional duration in milliseconds
55
+ * @param error - Optional error message for failed spawns
56
+ * @returns Plain-text marker string (e.g., "[SPAWN] story-1.001 STARTED")
57
+ */
58
+ export const formatPlainSpawnMarker = (itemId, status, durationMs, error) => {
59
+ const duration = durationMs === undefined ? '' : ` (${formatDuration(durationMs)})`;
60
+ const errorSuffix = error ? `: ${error}` : '';
61
+ return `[SPAWN] ${itemId} ${status}${duration}${errorSuffix}`;
62
+ };
63
+ /**
64
+ * Format a plain-text summary for non-TTY output
65
+ *
66
+ * @param totalSpawns - Total number of spawns
67
+ * @param passedCount - Number of passed spawns
68
+ * @param failedCount - Number of failed spawns
69
+ * @param durationMs - Total duration in milliseconds
70
+ * @returns Plain-text summary string
71
+ */
72
+ export const formatPlainSummary = (totalSpawns, passedCount, failedCount, durationMs) => {
73
+ const lines = [
74
+ '--- Summary ---',
75
+ `Total: ${totalSpawns} spawns`,
76
+ `Passed: ${passedCount}`,
77
+ `Failed: ${failedCount}`,
78
+ `Duration: ${formatDuration(durationMs)}`,
79
+ ];
80
+ return lines.join('\n');
81
+ };
82
+ /**
83
+ * Format spawn output with visual delimiters for verbose mode
84
+ *
85
+ * @param spawnId - Spawn identifier
86
+ * @param output - Output content from the spawn
87
+ * @param maxWidth - Maximum line width (default: 80)
88
+ * @returns Formatted output with header/footer delimiters
89
+ */
90
+ export const formatVerboseSpawnOutput = (spawnId, output, maxWidth = DEFAULT_TERMINAL_WIDTH) => {
91
+ // Create delimiter line that fits within maxWidth
92
+ const headerText = `── ${spawnId} output ──`;
93
+ const footerText = `── end ${spawnId} ──`;
94
+ // Pad the delimiter to maxWidth with dashes
95
+ const headerPadding = Math.max(0, maxWidth - headerText.length);
96
+ const footerPadding = Math.max(0, maxWidth - footerText.length);
97
+ const header = headerText + '─'.repeat(headerPadding);
98
+ const footer = footerText + '─'.repeat(footerPadding);
99
+ // Wrap output lines to respect width constraint
100
+ const wrappedLines = wrapLines(output, maxWidth);
101
+ return `${header}\n${wrappedLines}\n${footer}`;
102
+ };
103
+ /**
104
+ * Wrap text lines to fit within a maximum width
105
+ *
106
+ * @param text - Text to wrap
107
+ * @param maxWidth - Maximum line width
108
+ * @returns Wrapped text
109
+ */
110
+ export const wrapLines = (text, maxWidth) => {
111
+ const lines = text.split('\n');
112
+ const wrappedLines = [];
113
+ for (const line of lines) {
114
+ if (line.length <= maxWidth) {
115
+ wrappedLines.push(line);
116
+ }
117
+ else {
118
+ // Simple word-based wrapping
119
+ let currentLine = '';
120
+ const words = line.split(' ');
121
+ for (const word of words) {
122
+ if (currentLine.length === 0) {
123
+ currentLine = word;
124
+ }
125
+ else if (currentLine.length + 1 + word.length <= maxWidth) {
126
+ currentLine += ' ' + word;
127
+ }
128
+ else {
129
+ wrappedLines.push(currentLine);
130
+ currentLine = word;
131
+ }
132
+ }
133
+ if (currentLine.length > 0) {
134
+ wrappedLines.push(currentLine);
135
+ }
136
+ }
137
+ }
138
+ return wrappedLines.join('\n');
139
+ };
140
+ /**
141
+ * Phase emoji mapping for visual consistency
142
+ */
143
+ export const PHASE_EMOJI = {
144
+ dev: '🛠️',
145
+ epic: '📋',
146
+ qa: '✅',
147
+ story: '📝',
148
+ };
149
+ /**
150
+ * Phase display names for titles
151
+ */
152
+ export const PHASE_TITLES = {
153
+ dev: 'Dev',
154
+ epic: 'Epics',
155
+ qa: 'QA',
156
+ story: 'Stories',
157
+ };
158
+ /**
159
+ * Action verbs for spawn labels — maps phase to what the spawn is DOING (lowercase)
160
+ */
161
+ export const PHASE_ACTION_VERBS = {
162
+ dev: 'developing',
163
+ epic: 'generating',
164
+ qa: 'reviewing',
165
+ story: 'creating',
166
+ };
167
+ /**
168
+ * Get the action verb for a phase (e.g., "Creating" for story phase)
169
+ */
170
+ export const getPhaseActionVerb = (phaseName) => PHASE_ACTION_VERBS[phaseName.toLowerCase()] || 'Processing';
171
+ /**
172
+ * Status indicators for spawn states
173
+ */
174
+ export const STATUS_INDICATORS = {
175
+ completed: chalk.bold('✓'),
176
+ failed: chalk.bold('✗'),
177
+ queued: chalk.dim('◯'),
178
+ running: chalk.bold('◉'),
179
+ };
180
+ /**
181
+ * Get emoji for a phase name
182
+ *
183
+ * @param phaseName - Phase name (epic, story, dev, qa)
184
+ * @returns Emoji string for the phase
185
+ */
186
+ export const getPhaseEmoji = (phaseName) => PHASE_EMOJI[phaseName.toLowerCase()] || '⚙️';
187
+ /**
188
+ * Create a formatted phase title with emoji
189
+ *
190
+ * @param phaseName - Phase name (epic, story, dev, qa)
191
+ * @returns Formatted title string with emoji (e.g., "📋 Epic Phase")
192
+ */
193
+ export const createPhaseTitle = (phaseName) => {
194
+ const emoji = getPhaseEmoji(phaseName);
195
+ const title = PHASE_TITLES[phaseName.toLowerCase()] || `${phaseName} Phase`;
196
+ return `${emoji} ${title}`;
197
+ };
198
+ /**
199
+ * Create a phase task group configuration for listr2
200
+ *
201
+ * @param phaseName - Phase name (epic, story, dev, qa)
202
+ * @returns Object with title and phase metadata
203
+ */
204
+ export const createPhaseTaskGroup = (phaseName) => ({
205
+ emoji: getPhaseEmoji(phaseName),
206
+ phaseName: phaseName.toLowerCase(),
207
+ title: createPhaseTitle(phaseName),
208
+ });
209
+ /**
210
+ * Create a layer task group configuration for listr2
211
+ *
212
+ * @param layerIndex - Zero-based layer index
213
+ * @param spawnCount - Number of spawns in the layer
214
+ * @param totalLayers - Total number of layers in the phase
215
+ * @returns Object with title and layer metadata
216
+ */
217
+ export const createLayerTaskGroup = (layerIndex, spawnCount, totalLayers) => {
218
+ const layerNumber = layerIndex + 1;
219
+ const title = `Layer ${layerNumber}/${totalLayers} (${spawnCount} ${spawnCount === 1 ? 'spawn' : 'spawns'})`;
220
+ return {
221
+ layerIndex,
222
+ spawnCount,
223
+ title,
224
+ totalLayers,
225
+ };
226
+ };
227
+ /**
228
+ * Format spawn status for display
229
+ *
230
+ * @param status - Current spawn status
231
+ * @param durationMs - Duration in milliseconds (optional)
232
+ * @param itemCount - Number of items processed (optional, for completed status)
233
+ * @param errorMessage - Error message (optional, for failed status)
234
+ * @returns Formatted status string
235
+ */
236
+ export const formatSpawnStatus = (status, durationMs, itemCount, errorMessage) => {
237
+ const indicator = STATUS_INDICATORS[status];
238
+ const duration = durationMs === undefined ? '' : formatDuration(durationMs);
239
+ switch (status) {
240
+ case 'completed': {
241
+ if (itemCount !== undefined) {
242
+ return duration
243
+ ? `${indicator} completed (${duration}) → ${itemCount} ${itemCount === 1 ? 'item' : 'items'}`
244
+ : `${indicator} completed → ${itemCount} ${itemCount === 1 ? 'item' : 'items'}`;
245
+ }
246
+ return duration ? `${indicator} completed (${duration})` : `${indicator} completed`;
247
+ }
248
+ case 'failed': {
249
+ return errorMessage
250
+ ? `${indicator} failed: ${errorMessage}`
251
+ : `${indicator} failed`;
252
+ }
253
+ case 'queued': {
254
+ return `${indicator} queued`;
255
+ }
256
+ case 'running': {
257
+ return duration ? `${indicator} running (${duration})` : `${indicator} running`;
258
+ }
259
+ default: {
260
+ return `${indicator} ${status}`;
261
+ }
262
+ }
263
+ };
264
+ /**
265
+ * Format a layer summary line for collapsed display
266
+ *
267
+ * @param result - Layer execution results
268
+ * @returns Formatted summary string
269
+ */
270
+ export const formatLayerSummary = (result) => {
271
+ const layerNumber = result.layerIndex + 1;
272
+ const duration = formatDuration(result.durationMs);
273
+ const passedText = `${result.successCount}/${result.spawnCount} passed`;
274
+ const statusColor = result.failureCount > 0
275
+ ? chalk.bold
276
+ : result.successCount === result.spawnCount
277
+ ? chalk.dim
278
+ : chalk.underline;
279
+ return `Layer ${layerNumber} (${result.spawnCount} ${result.spawnCount === 1 ? 'spawn' : 'spawns'}) — completed in ${duration} [${statusColor(passedText)}]`;
280
+ };
281
+ /**
282
+ * Format a phase summary for completion display
283
+ *
284
+ * @param result - Phase execution results
285
+ * @returns Formatted summary string
286
+ */
287
+ export const formatPhaseSummary = (result) => {
288
+ const emoji = getPhaseEmoji(result.phaseName);
289
+ const title = PHASE_TITLES[result.phaseName.toLowerCase()] || `${result.phaseName} Phase`;
290
+ const duration = formatDuration(result.durationMs);
291
+ const passedText = `${result.successCount}/${result.itemCount}`;
292
+ const statusIcon = result.failureCount > 0
293
+ ? chalk.bold('✗')
294
+ : result.successCount === result.itemCount
295
+ ? chalk.bold('✓')
296
+ : chalk.bold('⚠');
297
+ return `${emoji} ${title} ${statusIcon} — ${passedText} in ${duration}`;
298
+ };
299
+ /**
300
+ * Create a spawn task title
301
+ *
302
+ * @param itemId - Item identifier (epic number, story number, etc.)
303
+ * @param itemTitle - Optional item title/description
304
+ * @param agentType - Type of agent being spawned
305
+ * @returns Formatted spawn task title
306
+ */
307
+ export const createSpawnTitle = (itemId, itemTitle, agentType) => {
308
+ const prefix = agentType ? chalk.dim(`[${agentType}]`) : '';
309
+ const title = itemTitle ? `${itemId}: ${itemTitle}` : itemId;
310
+ return prefix ? `${prefix} ${title}` : title;
311
+ };
312
+ /**
313
+ * Truncate a string to a maximum length with ellipsis
314
+ *
315
+ * @param text - Text to truncate
316
+ * @param maxLength - Maximum length (default: 60)
317
+ * @returns Truncated string with ellipsis if needed
318
+ */
319
+ export const truncateText = (text, maxLength = 60) => {
320
+ if (text.length <= maxLength) {
321
+ return text;
322
+ }
323
+ return `${text.slice(0, maxLength - 3)}...`;
324
+ };
325
+ /**
326
+ * Format elapsed time for running tasks (updates every second)
327
+ *
328
+ * @param startTime - Start timestamp in milliseconds
329
+ * @returns Formatted elapsed time string
330
+ */
331
+ export const formatElapsedTime = (startTime) => {
332
+ const elapsed = Date.now() - startTime;
333
+ return formatDuration(elapsed);
334
+ };
@@ -27,6 +27,7 @@ export declare const agentFlags: {
27
27
  provider: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
28
28
  task: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
29
29
  timeout: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
30
+ 'review-timeout': import("@oclif/core/interfaces").OptionFlag<number | undefined, import("@oclif/core/interfaces").CustomOptions>;
30
31
  'max-retries': import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
31
32
  'retry-backoff': import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
32
33
  };
@@ -15,6 +15,7 @@
15
15
  * ```
16
16
  */
17
17
  import { Flags } from '@oclif/core';
18
+ import { parseDuration } from './duration.js';
18
19
  /**
19
20
  * Agent, Task, and Provider flags for customizing command behavior
20
21
  *
@@ -44,9 +45,17 @@ export const agentFlags = {
44
45
  description: 'Override which task command to execute (e.g., develop-story, draft, review-implementation). Defaults to command-appropriate task.',
45
46
  helpGroup: 'Agent Customization',
46
47
  }),
47
- timeout: Flags.integer({
48
+ timeout: Flags.custom({
49
+ parse: async (input) => parseDuration(input),
50
+ })({
48
51
  default: 2_700_000,
49
- description: 'Agent execution timeout in milliseconds (default: 2700000 = 45 minutes)',
52
+ description: 'Agent execution timeout accepts durations like 30s, 5m, 1h, 90m, or raw milliseconds (default: 45m)',
53
+ helpGroup: 'Resilience',
54
+ }),
55
+ 'review-timeout': Flags.custom({
56
+ parse: async (input) => parseDuration(input),
57
+ })({
58
+ description: 'AI review scanner timeout — overrides --timeout for review phase only (default: 5m)',
50
59
  helpGroup: 'Resilience',
51
60
  }),
52
61
  'max-retries': Flags.integer({
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@hyperdrive.bot/bmad-workflow",
3
3
  "description": "AI-driven development workflow orchestration CLI for BMAD projects",
4
- "version": "1.0.17",
4
+ "version": "1.0.19",
5
5
  "author": {
6
6
  "name": "DevSquad",
7
7
  "email": "marcelo@devsquad.email",
@@ -14,6 +14,7 @@
14
14
  "url": "https://gitlab.com/dev_squad/repo/cli/bmad-orchestrator/issues"
15
15
  },
16
16
  "dependencies": {
17
+ "@hyperdrive.bot/plugin-telemetry": "file:../telemetry-plugin",
17
18
  "@oclif/core": "^4",
18
19
  "@oclif/plugin-help": "^6",
19
20
  "bcrypt": "^6.0.0",
@@ -23,6 +24,7 @@
23
24
  "fs-extra": "^11.3.2",
24
25
  "js-yaml": "^4.1.0",
25
26
  "jsonwebtoken": "^9.0.2",
27
+ "listr2": "^10.1.0",
26
28
  "nodemailer": "^7.0.9",
27
29
  "ora": "^9.0.0",
28
30
  "pino": "^10.0.0",
@@ -52,10 +54,10 @@
52
54
  "eslint": "^9",
53
55
  "eslint-config-oclif": "^6",
54
56
  "eslint-config-prettier": "^10",
57
+ "esmock": "^2.7.3",
55
58
  "mocha": "^10.8.2",
56
59
  "oclif": "^4",
57
60
  "prettier": "^3.6.2",
58
- "esmock": "^2.7.3",
59
61
  "proxyquire": "^2.1.3",
60
62
  "sinon": "^17.0.1",
61
63
  "ts-node": "^10.9.2",
@@ -88,7 +90,8 @@
88
90
  "dirname": "bmad-workflow",
89
91
  "commands": "./dist/commands",
90
92
  "plugins": [
91
- "@oclif/plugin-help"
93
+ "@oclif/plugin-help",
94
+ "@hyperdrive.bot/plugin-telemetry"
92
95
  ],
93
96
  "topicSeparator": " "
94
97
  },