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.
- package/CHANGELOG.md +135 -0
- package/README.md +239 -213
- package/README.zh.md +0 -330
- package/bin/kiro-spec-engine.js +62 -0
- package/docs/README.md +223 -0
- package/docs/agent-hooks-analysis.md +815 -0
- package/docs/command-reference.md +252 -0
- package/docs/cross-tool-guide.md +554 -0
- package/docs/examples/add-export-command/design.md +194 -0
- package/docs/examples/add-export-command/requirements.md +110 -0
- package/docs/examples/add-export-command/tasks.md +88 -0
- package/docs/examples/add-rest-api/design.md +855 -0
- package/docs/examples/add-rest-api/requirements.md +323 -0
- package/docs/examples/add-rest-api/tasks.md +355 -0
- package/docs/examples/add-user-dashboard/design.md +192 -0
- package/docs/examples/add-user-dashboard/requirements.md +143 -0
- package/docs/examples/add-user-dashboard/tasks.md +91 -0
- package/docs/faq.md +696 -0
- package/docs/integration-modes.md +525 -0
- package/docs/integration-philosophy.md +313 -0
- package/docs/manual-workflows-guide.md +417 -0
- package/docs/quick-start-with-ai-tools.md +374 -0
- package/docs/quick-start.md +711 -0
- package/docs/spec-workflow.md +453 -0
- package/docs/steering-strategy-guide.md +196 -0
- package/docs/tools/claude-guide.md +653 -0
- package/docs/tools/cursor-guide.md +705 -0
- package/docs/tools/generic-guide.md +445 -0
- package/docs/tools/kiro-guide.md +308 -0
- package/docs/tools/vscode-guide.md +444 -0
- package/docs/tools/windsurf-guide.md +390 -0
- package/docs/troubleshooting.md +795 -0
- package/docs/zh/README.md +275 -0
- package/docs/zh/quick-start.md +711 -0
- package/docs/zh/tools/claude-guide.md +348 -0
- package/docs/zh/tools/cursor-guide.md +280 -0
- package/docs/zh/tools/generic-guide.md +498 -0
- package/docs/zh/tools/kiro-guide.md +342 -0
- package/docs/zh/tools/vscode-guide.md +448 -0
- package/docs/zh/tools/windsurf-guide.md +377 -0
- package/lib/adoption/detection-engine.js +14 -4
- package/lib/commands/adopt.js +117 -3
- package/lib/commands/context.js +99 -0
- package/lib/commands/prompt.js +105 -0
- package/lib/commands/status.js +225 -0
- package/lib/commands/task.js +199 -0
- package/lib/commands/watch.js +569 -0
- package/lib/commands/workflows.js +240 -0
- package/lib/commands/workspace.js +189 -0
- package/lib/context/context-exporter.js +378 -0
- package/lib/context/prompt-generator.js +482 -0
- package/lib/steering/adoption-config.js +164 -0
- package/lib/steering/steering-manager.js +289 -0
- package/lib/task/task-claimer.js +430 -0
- package/lib/utils/tool-detector.js +383 -0
- package/lib/watch/action-executor.js +458 -0
- package/lib/watch/event-debouncer.js +323 -0
- package/lib/watch/execution-logger.js +550 -0
- package/lib/watch/file-watcher.js +499 -0
- package/lib/watch/presets.js +266 -0
- package/lib/watch/watch-manager.js +533 -0
- package/lib/workspace/workspace-manager.js +370 -0
- package/lib/workspace/workspace-sync.js +356 -0
- package/package.json +3 -1
- package/template/.kiro/tools/backup_manager.py +295 -0
- package/template/.kiro/tools/configuration_manager.py +218 -0
- package/template/.kiro/tools/document_evaluator.py +550 -0
- package/template/.kiro/tools/enhancement_logger.py +168 -0
- package/template/.kiro/tools/error_handler.py +335 -0
- package/template/.kiro/tools/improvement_identifier.py +444 -0
- package/template/.kiro/tools/modification_applicator.py +737 -0
- package/template/.kiro/tools/quality_gate_enforcer.py +207 -0
- package/template/.kiro/tools/quality_scorer.py +305 -0
- package/template/.kiro/tools/report_generator.py +154 -0
- package/template/.kiro/tools/ultrawork_enhancer_refactored.py +0 -0
- package/template/.kiro/tools/ultrawork_enhancer_v2.py +463 -0
- package/template/.kiro/tools/ultrawork_enhancer_v3.py +606 -0
- 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
|
+
};
|