kiro-spec-engine 1.2.3 → 1.4.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 (78) hide show
  1. package/CHANGELOG.md +135 -0
  2. package/README.md +239 -213
  3. package/README.zh.md +0 -330
  4. package/bin/kiro-spec-engine.js +62 -0
  5. package/docs/README.md +223 -0
  6. package/docs/agent-hooks-analysis.md +815 -0
  7. package/docs/command-reference.md +252 -0
  8. package/docs/cross-tool-guide.md +554 -0
  9. package/docs/examples/add-export-command/design.md +194 -0
  10. package/docs/examples/add-export-command/requirements.md +110 -0
  11. package/docs/examples/add-export-command/tasks.md +88 -0
  12. package/docs/examples/add-rest-api/design.md +855 -0
  13. package/docs/examples/add-rest-api/requirements.md +323 -0
  14. package/docs/examples/add-rest-api/tasks.md +355 -0
  15. package/docs/examples/add-user-dashboard/design.md +192 -0
  16. package/docs/examples/add-user-dashboard/requirements.md +143 -0
  17. package/docs/examples/add-user-dashboard/tasks.md +91 -0
  18. package/docs/faq.md +696 -0
  19. package/docs/integration-modes.md +525 -0
  20. package/docs/integration-philosophy.md +313 -0
  21. package/docs/manual-workflows-guide.md +417 -0
  22. package/docs/quick-start-with-ai-tools.md +374 -0
  23. package/docs/quick-start.md +711 -0
  24. package/docs/spec-workflow.md +453 -0
  25. package/docs/steering-strategy-guide.md +196 -0
  26. package/docs/tools/claude-guide.md +653 -0
  27. package/docs/tools/cursor-guide.md +705 -0
  28. package/docs/tools/generic-guide.md +445 -0
  29. package/docs/tools/kiro-guide.md +308 -0
  30. package/docs/tools/vscode-guide.md +444 -0
  31. package/docs/tools/windsurf-guide.md +390 -0
  32. package/docs/troubleshooting.md +795 -0
  33. package/docs/zh/README.md +275 -0
  34. package/docs/zh/quick-start.md +711 -0
  35. package/docs/zh/tools/claude-guide.md +348 -0
  36. package/docs/zh/tools/cursor-guide.md +280 -0
  37. package/docs/zh/tools/generic-guide.md +498 -0
  38. package/docs/zh/tools/kiro-guide.md +342 -0
  39. package/docs/zh/tools/vscode-guide.md +448 -0
  40. package/docs/zh/tools/windsurf-guide.md +377 -0
  41. package/lib/adoption/detection-engine.js +14 -4
  42. package/lib/commands/adopt.js +117 -3
  43. package/lib/commands/context.js +99 -0
  44. package/lib/commands/prompt.js +105 -0
  45. package/lib/commands/status.js +225 -0
  46. package/lib/commands/task.js +199 -0
  47. package/lib/commands/watch.js +569 -0
  48. package/lib/commands/workflows.js +240 -0
  49. package/lib/commands/workspace.js +189 -0
  50. package/lib/context/context-exporter.js +378 -0
  51. package/lib/context/prompt-generator.js +482 -0
  52. package/lib/steering/adoption-config.js +164 -0
  53. package/lib/steering/steering-manager.js +289 -0
  54. package/lib/task/task-claimer.js +430 -0
  55. package/lib/utils/tool-detector.js +383 -0
  56. package/lib/watch/action-executor.js +458 -0
  57. package/lib/watch/event-debouncer.js +323 -0
  58. package/lib/watch/execution-logger.js +550 -0
  59. package/lib/watch/file-watcher.js +499 -0
  60. package/lib/watch/presets.js +266 -0
  61. package/lib/watch/watch-manager.js +533 -0
  62. package/lib/workspace/workspace-manager.js +370 -0
  63. package/lib/workspace/workspace-sync.js +356 -0
  64. package/package.json +3 -1
  65. package/template/.kiro/tools/backup_manager.py +295 -0
  66. package/template/.kiro/tools/configuration_manager.py +218 -0
  67. package/template/.kiro/tools/document_evaluator.py +550 -0
  68. package/template/.kiro/tools/enhancement_logger.py +168 -0
  69. package/template/.kiro/tools/error_handler.py +335 -0
  70. package/template/.kiro/tools/improvement_identifier.py +444 -0
  71. package/template/.kiro/tools/modification_applicator.py +737 -0
  72. package/template/.kiro/tools/quality_gate_enforcer.py +207 -0
  73. package/template/.kiro/tools/quality_scorer.py +305 -0
  74. package/template/.kiro/tools/report_generator.py +154 -0
  75. package/template/.kiro/tools/ultrawork_enhancer_refactored.py +0 -0
  76. package/template/.kiro/tools/ultrawork_enhancer_v2.py +463 -0
  77. package/template/.kiro/tools/ultrawork_enhancer_v3.py +606 -0
  78. package/template/.kiro/tools/workflow_quality_gate.py +100 -0
@@ -0,0 +1,569 @@
1
+ /**
2
+ * Watch Command Group
3
+ *
4
+ * Manages watch mode for automated file monitoring and command execution
5
+ */
6
+
7
+ const chalk = require('chalk');
8
+ const path = require('path');
9
+ const fs = require('fs-extra');
10
+ const inquirer = require('inquirer');
11
+ const WatchManager = require('../watch/watch-manager');
12
+ const { listPresets, getPreset, mergePreset, validatePreset } = require('../watch/presets');
13
+
14
+ /**
15
+ * Start watch mode
16
+ *
17
+ * @param {Object} options - Command options
18
+ * @param {string} options.config - Custom config file path
19
+ * @param {string} options.patterns - Override patterns (comma-separated)
20
+ * @returns {Promise<void>}
21
+ */
22
+ async function startWatch(options = {}) {
23
+ const projectPath = process.cwd();
24
+ const configPath = options.config || path.join(projectPath, '.kiro/watch-config.json');
25
+
26
+ console.log(chalk.red('🔥') + ' Starting Watch Mode');
27
+ console.log();
28
+
29
+ try {
30
+ const watchManager = new WatchManager({ configPath });
31
+
32
+ // Load configuration
33
+ await watchManager.loadConfig();
34
+
35
+ // Override patterns if specified
36
+ if (options.patterns) {
37
+ const patterns = options.patterns.split(',').map(p => p.trim());
38
+ watchManager.config.patterns = patterns;
39
+ console.log('Using custom patterns:', chalk.cyan(patterns.join(', ')));
40
+ console.log();
41
+ }
42
+
43
+ // Start watch mode
44
+ await watchManager.start();
45
+
46
+ console.log(chalk.green('✅ Watch mode started'));
47
+ console.log();
48
+ console.log('Watching patterns:');
49
+ for (const pattern of watchManager.config.patterns) {
50
+ console.log(` ${chalk.gray('•')} ${pattern}`);
51
+ }
52
+ console.log();
53
+ console.log('Actions configured:');
54
+ const actionCount = Object.keys(watchManager.config.actions || {}).length;
55
+ console.log(` ${chalk.cyan(actionCount)} action(s)`);
56
+ console.log();
57
+ console.log('Commands:');
58
+ console.log(` ${chalk.cyan('kse watch status')} - Check status`);
59
+ console.log(` ${chalk.cyan('kse watch logs')} - View logs`);
60
+ console.log(` ${chalk.cyan('kse watch stop')} - Stop watch mode`);
61
+ console.log();
62
+ console.log(chalk.gray('Press Ctrl+C to stop'));
63
+
64
+ // Keep process running
65
+ process.on('SIGINT', async () => {
66
+ console.log();
67
+ console.log('Stopping watch mode...');
68
+ await watchManager.stop();
69
+ console.log(chalk.green('✅ Watch mode stopped'));
70
+ process.exit(0);
71
+ });
72
+
73
+ } catch (error) {
74
+ console.log(chalk.red('❌ Error:'), error.message);
75
+ if (process.env.NODE_ENV !== 'test') {
76
+ process.exit(1);
77
+ }
78
+ throw error;
79
+ }
80
+ }
81
+
82
+ /**
83
+ * Stop watch mode
84
+ *
85
+ * @param {Object} options - Command options
86
+ * @returns {Promise<void>}
87
+ */
88
+ async function stopWatch(options = {}) {
89
+ const projectPath = process.cwd();
90
+ const configPath = path.join(projectPath, '.kiro/watch-config.json');
91
+
92
+ console.log(chalk.red('🔥') + ' Stopping Watch Mode');
93
+ console.log();
94
+
95
+ try {
96
+ const watchManager = new WatchManager({ configPath });
97
+ await watchManager.stop();
98
+
99
+ console.log(chalk.green('✅ Watch mode stopped'));
100
+ } catch (error) {
101
+ console.log(chalk.red('❌ Error:'), error.message);
102
+ if (process.env.NODE_ENV !== 'test') {
103
+ process.exit(1);
104
+ }
105
+ throw error;
106
+ }
107
+ }
108
+
109
+ /**
110
+ * Show watch mode status
111
+ *
112
+ * @param {Object} options - Command options
113
+ * @returns {Promise<void>}
114
+ */
115
+ async function statusWatch(options = {}) {
116
+ const projectPath = process.cwd();
117
+ const configPath = path.join(projectPath, '.kiro/watch-config.json');
118
+
119
+ console.log(chalk.red('🔥') + ' Watch Mode Status');
120
+ console.log();
121
+
122
+ try {
123
+ const watchManager = new WatchManager({ configPath });
124
+ const status = watchManager.getStatus();
125
+
126
+ // Running status
127
+ const runningStatus = status.running ? chalk.green('Running') : chalk.gray('Stopped');
128
+ console.log(`Status: ${runningStatus}`);
129
+ console.log();
130
+
131
+ if (status.running) {
132
+ // Patterns
133
+ console.log('Watching patterns:');
134
+ for (const pattern of status.patterns || []) {
135
+ console.log(` ${chalk.gray('•')} ${pattern}`);
136
+ }
137
+ console.log();
138
+
139
+ // Actions
140
+ console.log('Actions:');
141
+ const actionCount = Object.keys(status.actions || {}).length;
142
+ console.log(` ${chalk.cyan(actionCount)} action(s) configured`);
143
+ console.log();
144
+
145
+ // Recent activity
146
+ if (status.recentActivity && status.recentActivity.length > 0) {
147
+ console.log('Recent activity:');
148
+ for (const activity of status.recentActivity.slice(0, 5)) {
149
+ const time = new Date(activity.timestamp).toLocaleTimeString();
150
+ const result = activity.result === 'success' ? chalk.green('✓') : chalk.red('✗');
151
+ console.log(` ${result} ${chalk.gray(time)} ${activity.file}`);
152
+ }
153
+ console.log();
154
+ }
155
+
156
+ // Error count
157
+ if (status.errorCount > 0) {
158
+ console.log(chalk.yellow(`⚠️ ${status.errorCount} error(s) occurred`));
159
+ console.log(`Run ${chalk.cyan('kse watch logs')} to view details`);
160
+ console.log();
161
+ }
162
+ } else {
163
+ console.log(chalk.gray('Watch mode is not running'));
164
+ console.log();
165
+ console.log(`Run ${chalk.cyan('kse watch start')} to start`);
166
+ }
167
+ } catch (error) {
168
+ console.log(chalk.red('❌ Error:'), error.message);
169
+ if (process.env.NODE_ENV !== 'test') {
170
+ process.exit(1);
171
+ }
172
+ throw error;
173
+ }
174
+ }
175
+
176
+ /**
177
+ * Display execution logs
178
+ *
179
+ * @param {Object} options - Command options
180
+ * @param {number} options.tail - Number of lines to show
181
+ * @param {boolean} options.follow - Follow mode (tail -f)
182
+ * @returns {Promise<void>}
183
+ */
184
+ async function logsWatch(options = {}) {
185
+ const projectPath = process.cwd();
186
+ const configPath = path.join(projectPath, '.kiro/watch-config.json');
187
+
188
+ console.log(chalk.red('🔥') + ' Watch Mode Logs');
189
+ console.log();
190
+
191
+ try {
192
+ const watchManager = new WatchManager({ configPath });
193
+ const lines = options.tail || 50;
194
+ const logs = await watchManager.getLogs(lines);
195
+
196
+ if (logs.length === 0) {
197
+ console.log(chalk.gray('No logs found'));
198
+ return;
199
+ }
200
+
201
+ console.log(`Showing last ${chalk.cyan(logs.length)} log entries:`);
202
+ console.log();
203
+
204
+ for (const log of logs) {
205
+ const time = new Date(log.timestamp).toLocaleTimeString();
206
+ const level = formatLogLevel(log.level);
207
+ console.log(`${chalk.gray(time)} ${level} ${log.message}`);
208
+
209
+ if (log.error) {
210
+ console.log(` ${chalk.red('Error:')} ${log.error}`);
211
+ }
212
+ }
213
+
214
+ if (options.follow) {
215
+ console.log();
216
+ console.log(chalk.gray('Following logs... (Press Ctrl+C to stop)'));
217
+ // TODO: Implement follow mode with file watching
218
+ }
219
+ } catch (error) {
220
+ console.log(chalk.red('❌ Error:'), error.message);
221
+ if (process.env.NODE_ENV !== 'test') {
222
+ process.exit(1);
223
+ }
224
+ throw error;
225
+ }
226
+ }
227
+
228
+ /**
229
+ * Display automation metrics
230
+ *
231
+ * @param {Object} options - Command options
232
+ * @param {string} options.format - Output format (text/json)
233
+ * @returns {Promise<void>}
234
+ */
235
+ async function metricsWatch(options = {}) {
236
+ const projectPath = process.cwd();
237
+ const configPath = path.join(projectPath, '.kiro/watch-config.json');
238
+
239
+ console.log(chalk.red('🔥') + ' Watch Mode Metrics');
240
+ console.log();
241
+
242
+ try {
243
+ const watchManager = new WatchManager({ configPath });
244
+ const metrics = watchManager.getMetrics();
245
+
246
+ if (options.format === 'json') {
247
+ console.log(JSON.stringify(metrics, null, 2));
248
+ return;
249
+ }
250
+
251
+ // Text format
252
+ console.log('Execution Statistics:');
253
+ console.log(` Total executions: ${chalk.cyan(metrics.totalExecutions || 0)}`);
254
+ console.log(` Successful: ${chalk.green(metrics.successfulExecutions || 0)}`);
255
+ console.log(` Failed: ${chalk.red(metrics.failedExecutions || 0)}`);
256
+ console.log(` Success rate: ${chalk.cyan(((metrics.successRate || 0) * 100).toFixed(1))}%`);
257
+ console.log();
258
+
259
+ console.log('Performance:');
260
+ console.log(` Average duration: ${chalk.cyan((metrics.averageDuration || 0).toFixed(0))}ms`);
261
+ console.log(` Time saved: ${chalk.cyan(formatTimeSaved(metrics.timeSaved || 0))}`);
262
+ console.log();
263
+
264
+ if (metrics.byAction && Object.keys(metrics.byAction).length > 0) {
265
+ console.log('By Action:');
266
+ for (const [action, count] of Object.entries(metrics.byAction)) {
267
+ console.log(` ${action}: ${chalk.cyan(count)}`);
268
+ }
269
+ console.log();
270
+ }
271
+
272
+ if (metrics.errors && metrics.errors.length > 0) {
273
+ console.log(chalk.yellow(`Recent Errors (${metrics.errors.length}):`));
274
+ for (const error of metrics.errors.slice(0, 5)) {
275
+ const time = new Date(error.timestamp).toLocaleTimeString();
276
+ console.log(` ${chalk.gray(time)} ${error.message}`);
277
+ }
278
+ }
279
+ } catch (error) {
280
+ console.log(chalk.red('❌ Error:'), error.message);
281
+ if (process.env.NODE_ENV !== 'test') {
282
+ process.exit(1);
283
+ }
284
+ throw error;
285
+ }
286
+ }
287
+
288
+ /**
289
+ * Initialize watch configuration
290
+ *
291
+ * @param {Object} options - Command options
292
+ * @param {boolean} options.force - Overwrite existing config
293
+ * @returns {Promise<void>}
294
+ */
295
+ async function initWatch(options = {}) {
296
+ const projectPath = process.cwd();
297
+ const configPath = path.join(projectPath, '.kiro/watch-config.json');
298
+
299
+ console.log(chalk.red('🔥') + ' Initialize Watch Configuration');
300
+ console.log();
301
+
302
+ try {
303
+ // Check if config already exists
304
+ if (await fs.pathExists(configPath) && !options.force) {
305
+ console.log(chalk.yellow('⚠️ Configuration already exists'));
306
+ console.log();
307
+ console.log(`Path: ${chalk.gray(configPath)}`);
308
+ console.log();
309
+ console.log(`Use ${chalk.cyan('--force')} to overwrite`);
310
+ return;
311
+ }
312
+
313
+ // Create default configuration
314
+ const defaultConfig = {
315
+ enabled: true,
316
+ patterns: [
317
+ '**/tasks.md',
318
+ '**/.kiro/specs/*/requirements.md',
319
+ '**/.kiro/specs/*/design.md'
320
+ ],
321
+ ignored: [
322
+ '**/node_modules/**',
323
+ '**/.git/**',
324
+ '**/coverage/**'
325
+ ],
326
+ actions: {
327
+ '**/tasks.md': {
328
+ command: 'kse workspace sync',
329
+ debounce: 2000,
330
+ description: 'Sync workspace when tasks are updated'
331
+ }
332
+ },
333
+ debounce: {
334
+ default: 2000
335
+ },
336
+ logging: {
337
+ enabled: true,
338
+ level: 'info',
339
+ maxSize: '10MB',
340
+ rotation: true
341
+ },
342
+ retry: {
343
+ enabled: true,
344
+ maxAttempts: 3,
345
+ backoff: 'exponential'
346
+ }
347
+ };
348
+
349
+ // Ensure directory exists
350
+ await fs.ensureDir(path.dirname(configPath));
351
+
352
+ // Write configuration
353
+ await fs.writeJson(configPath, defaultConfig, { spaces: 2 });
354
+
355
+ console.log(chalk.green('✅ Configuration created'));
356
+ console.log();
357
+ console.log(`Path: ${chalk.gray(configPath)}`);
358
+ console.log();
359
+ console.log('Default patterns:');
360
+ for (const pattern of defaultConfig.patterns) {
361
+ console.log(` ${chalk.gray('•')} ${pattern}`);
362
+ }
363
+ console.log();
364
+ console.log('Next steps:');
365
+ console.log(` 1. Edit config: ${chalk.cyan(configPath)}`);
366
+ console.log(` 2. Start watch: ${chalk.cyan('kse watch start')}`);
367
+ } catch (error) {
368
+ console.log(chalk.red('❌ Error:'), error.message);
369
+ if (process.env.NODE_ENV !== 'test') {
370
+ process.exit(1);
371
+ }
372
+ throw error;
373
+ }
374
+ }
375
+
376
+ /**
377
+ * Format log level with color
378
+ *
379
+ * @param {string} level - Log level
380
+ * @returns {string} Formatted level
381
+ */
382
+ function formatLogLevel(level) {
383
+ const levels = {
384
+ debug: chalk.gray('[DEBUG]'),
385
+ info: chalk.blue('[INFO]'),
386
+ warn: chalk.yellow('[WARN]'),
387
+ error: chalk.red('[ERROR]')
388
+ };
389
+ return levels[level] || chalk.gray(`[${level.toUpperCase()}]`);
390
+ }
391
+
392
+ /**
393
+ * Format time saved in human-readable format
394
+ *
395
+ * @param {number} ms - Milliseconds
396
+ * @returns {string} Formatted time
397
+ */
398
+ function formatTimeSaved(ms) {
399
+ if (ms < 1000) {
400
+ return `${ms}ms`;
401
+ } else if (ms < 60000) {
402
+ return `${(ms / 1000).toFixed(1)}s`;
403
+ } else if (ms < 3600000) {
404
+ return `${(ms / 60000).toFixed(1)}m`;
405
+ } else {
406
+ return `${(ms / 3600000).toFixed(1)}h`;
407
+ }
408
+ }
409
+
410
+ /**
411
+ * List available presets
412
+ *
413
+ * @param {Object} options - Command options
414
+ * @returns {Promise<void>}
415
+ */
416
+ async function listPresetsWatch(options = {}) {
417
+ console.log(chalk.red('🔥') + ' Available Watch Presets');
418
+ console.log();
419
+
420
+ const presets = listPresets();
421
+
422
+ for (const preset of presets) {
423
+ console.log(`${chalk.cyan(preset.name)}`);
424
+ console.log(` ${chalk.gray(preset.description)}`);
425
+ console.log();
426
+ }
427
+
428
+ console.log('Install a preset:');
429
+ console.log(` ${chalk.cyan('kse watch install <preset-name>')}`);
430
+ }
431
+
432
+ /**
433
+ * Install a preset
434
+ *
435
+ * @param {string} presetName - Preset name
436
+ * @param {Object} options - Command options
437
+ * @param {boolean} options.force - Overwrite existing actions
438
+ * @returns {Promise<void>}
439
+ */
440
+ async function installPresetWatch(presetName, options = {}) {
441
+ const projectPath = process.cwd();
442
+ const configPath = path.join(projectPath, '.kiro/watch-config.json');
443
+
444
+ console.log(chalk.red('🔥') + ` Installing Preset: ${chalk.cyan(presetName)}`);
445
+ console.log();
446
+
447
+ try {
448
+ // Validate preset exists
449
+ const validation = validatePreset(presetName);
450
+ if (!validation.valid) {
451
+ console.log(chalk.red('❌ Invalid preset'));
452
+ console.log();
453
+ for (const error of validation.errors) {
454
+ console.log(` ${chalk.red('•')} ${error}`);
455
+ }
456
+ console.log();
457
+ console.log('Available presets:');
458
+ const presets = listPresets();
459
+ for (const preset of presets) {
460
+ console.log(` ${chalk.cyan('•')} ${preset.name}`);
461
+ }
462
+ if (process.env.NODE_ENV !== 'test') {
463
+ process.exit(1);
464
+ }
465
+ throw new Error('Invalid preset');
466
+ }
467
+
468
+ // Get preset
469
+ const preset = getPreset(presetName);
470
+
471
+ // Check if config exists
472
+ let existingConfig = {};
473
+ if (await fs.pathExists(configPath)) {
474
+ existingConfig = await fs.readJson(configPath);
475
+ } else {
476
+ // Create default config
477
+ existingConfig = {
478
+ enabled: true,
479
+ patterns: [],
480
+ ignored: ['**/node_modules/**', '**/.git/**', '**/coverage/**'],
481
+ actions: {},
482
+ debounce: { default: 2000 },
483
+ logging: {
484
+ enabled: true,
485
+ level: 'info',
486
+ maxSize: '10MB',
487
+ rotation: true
488
+ },
489
+ retry: {
490
+ enabled: true,
491
+ maxAttempts: 3,
492
+ backoff: 'exponential'
493
+ }
494
+ };
495
+ }
496
+
497
+ // Check for conflicts
498
+ const conflicts = [];
499
+ for (const pattern of Object.keys(preset.actions)) {
500
+ if (existingConfig.actions && existingConfig.actions[pattern]) {
501
+ conflicts.push(pattern);
502
+ }
503
+ }
504
+
505
+ if (conflicts.length > 0 && !options.force) {
506
+ console.log(chalk.yellow('⚠️ Conflicts detected'));
507
+ console.log();
508
+ console.log('The following patterns already have actions:');
509
+ for (const pattern of conflicts) {
510
+ console.log(` ${chalk.yellow('•')} ${pattern}`);
511
+ }
512
+ console.log();
513
+
514
+ const { proceed } = await inquirer.prompt([{
515
+ type: 'confirm',
516
+ name: 'proceed',
517
+ message: 'Overwrite existing actions?',
518
+ default: false
519
+ }]);
520
+
521
+ if (!proceed) {
522
+ console.log('Installation cancelled');
523
+ return;
524
+ }
525
+ }
526
+
527
+ // Merge preset
528
+ const mergedConfig = mergePreset(existingConfig, presetName);
529
+
530
+ // Ensure directory exists
531
+ await fs.ensureDir(path.dirname(configPath));
532
+
533
+ // Save configuration
534
+ await fs.writeJson(configPath, mergedConfig, { spaces: 2 });
535
+
536
+ console.log(chalk.green('✅ Preset installed successfully'));
537
+ console.log();
538
+ console.log('Added patterns:');
539
+ for (const pattern of preset.patterns) {
540
+ console.log(` ${chalk.gray('•')} ${pattern}`);
541
+ }
542
+ console.log();
543
+ console.log('Added actions:');
544
+ for (const [pattern, action] of Object.entries(preset.actions)) {
545
+ console.log(` ${chalk.gray('•')} ${pattern}`);
546
+ console.log(` ${chalk.gray(action.description)}`);
547
+ }
548
+ console.log();
549
+ console.log('Next steps:');
550
+ console.log(` ${chalk.cyan('kse watch start')} - Start watch mode`);
551
+ } catch (error) {
552
+ console.log(chalk.red('❌ Error:'), error.message);
553
+ if (process.env.NODE_ENV !== 'test') {
554
+ process.exit(1);
555
+ }
556
+ throw error;
557
+ }
558
+ }
559
+
560
+ module.exports = {
561
+ startWatch,
562
+ stopWatch,
563
+ statusWatch,
564
+ logsWatch,
565
+ metricsWatch,
566
+ initWatch,
567
+ listPresetsWatch,
568
+ installPresetWatch
569
+ };