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,499 @@
1
+ const chokidar = require('chokidar');
2
+ const EventEmitter = require('events');
3
+ const path = require('path');
4
+ const { minimatch } = require('minimatch');
5
+
6
+ /**
7
+ * FileWatcher - 文件监控器
8
+ *
9
+ * 监控文件系统变化并触发事件
10
+ */
11
+ class FileWatcher extends EventEmitter {
12
+ constructor(config = {}) {
13
+ super();
14
+
15
+ this.config = {
16
+ patterns: config.patterns || [],
17
+ ignored: config.ignored || ['**/node_modules/**', '**/.git/**', '**/coverage/**'],
18
+ persistent: config.persistent !== false,
19
+ ignoreInitial: config.ignoreInitial !== false,
20
+ awaitWriteFinish: config.awaitWriteFinish !== false ? {
21
+ stabilityThreshold: 2000,
22
+ pollInterval: 100
23
+ } : false,
24
+ maxRetries: config.maxRetries || 3,
25
+ retryDelay: config.retryDelay || 1000,
26
+ ...config
27
+ };
28
+
29
+ // 验证和规范化模式
30
+ this._validateAndNormalizePatterns();
31
+
32
+ this.watcher = null;
33
+ this.isWatching = false;
34
+ this.watchedFiles = new Set();
35
+ this.stats = {
36
+ filesWatched: 0,
37
+ eventsEmitted: 0,
38
+ errors: 0,
39
+ recoveries: 0,
40
+ startedAt: null
41
+ };
42
+ this.retryCount = 0;
43
+ this.lastError = null;
44
+ }
45
+
46
+ /**
47
+ * 验证和规范化模式
48
+ *
49
+ * @private
50
+ */
51
+ _validateAndNormalizePatterns() {
52
+ // 验证 patterns
53
+ if (!Array.isArray(this.config.patterns)) {
54
+ throw new Error('Patterns must be an array');
55
+ }
56
+
57
+ this.config.patterns = this.config.patterns.map(pattern => {
58
+ if (typeof pattern !== 'string') {
59
+ throw new Error('Pattern must be a string');
60
+ }
61
+
62
+ // 规范化路径分隔符
63
+ return pattern.replace(/\\/g, '/');
64
+ });
65
+
66
+ // 验证 ignored patterns
67
+ if (!Array.isArray(this.config.ignored)) {
68
+ throw new Error('Ignored patterns must be an array');
69
+ }
70
+
71
+ this.config.ignored = this.config.ignored.map(pattern => {
72
+ if (typeof pattern !== 'string') {
73
+ throw new Error('Ignored pattern must be a string');
74
+ }
75
+
76
+ return pattern.replace(/\\/g, '/');
77
+ });
78
+ }
79
+
80
+ /**
81
+ * 启动文件监控
82
+ *
83
+ * @param {string} basePath - 基础路径
84
+ * @returns {Promise<void>}
85
+ */
86
+ async start(basePath = process.cwd()) {
87
+ if (this.isWatching) {
88
+ throw new Error('FileWatcher is already running');
89
+ }
90
+
91
+ if (!this.config.patterns || this.config.patterns.length === 0) {
92
+ throw new Error('No patterns specified for watching');
93
+ }
94
+
95
+ try {
96
+ // 将相对路径转换为绝对路径
97
+ const absolutePatterns = this.config.patterns.map(pattern => {
98
+ if (path.isAbsolute(pattern)) {
99
+ return pattern;
100
+ }
101
+ return path.join(basePath, pattern);
102
+ });
103
+
104
+ // 创建 watcher
105
+ this.watcher = chokidar.watch(absolutePatterns, {
106
+ ignored: this.config.ignored,
107
+ persistent: this.config.persistent,
108
+ ignoreInitial: this.config.ignoreInitial,
109
+ awaitWriteFinish: this.config.awaitWriteFinish,
110
+ cwd: basePath
111
+ });
112
+
113
+ // 设置事件监听
114
+ this.watcher
115
+ .on('add', (filePath) => this._handleFileAdded(filePath))
116
+ .on('change', (filePath) => this._handleFileChanged(filePath))
117
+ .on('unlink', (filePath) => this._handleFileDeleted(filePath))
118
+ .on('error', (error) => this._handleError(error))
119
+ .on('ready', () => this._handleReady());
120
+
121
+ // 等待 watcher 准备就绪
122
+ await new Promise((resolve, reject) => {
123
+ const timeout = setTimeout(() => {
124
+ reject(new Error('FileWatcher initialization timeout'));
125
+ }, 10000);
126
+
127
+ this.watcher.once('ready', () => {
128
+ clearTimeout(timeout);
129
+ this.isWatching = true;
130
+ this.stats.startedAt = new Date();
131
+ resolve();
132
+ });
133
+
134
+ this.watcher.once('error', (error) => {
135
+ clearTimeout(timeout);
136
+ reject(error);
137
+ });
138
+ });
139
+
140
+ this.emit('started', { patterns: this.config.patterns });
141
+ } catch (error) {
142
+ this.isWatching = false;
143
+ throw error;
144
+ }
145
+ }
146
+
147
+ /**
148
+ * 停止文件监控
149
+ *
150
+ * @returns {Promise<void>}
151
+ */
152
+ async stop() {
153
+ if (!this.isWatching) {
154
+ return;
155
+ }
156
+
157
+ try {
158
+ if (this.watcher) {
159
+ await this.watcher.close();
160
+ this.watcher = null;
161
+ }
162
+
163
+ this.isWatching = false;
164
+ this.watchedFiles.clear();
165
+
166
+ this.emit('stopped', { stats: this.getStats() });
167
+ } catch (error) {
168
+ this.emit('error', { error, context: 'stop' });
169
+ throw error;
170
+ }
171
+ }
172
+
173
+ /**
174
+ * 获取监控状态
175
+ *
176
+ * @returns {Object} 状态信息
177
+ */
178
+ getStatus() {
179
+ return {
180
+ isWatching: this.isWatching,
181
+ patterns: this.config.patterns,
182
+ filesWatched: this.watchedFiles.size,
183
+ stats: this.getStats()
184
+ };
185
+ }
186
+
187
+ /**
188
+ * 获取统计信息
189
+ *
190
+ * @returns {Object} 统计信息
191
+ */
192
+ getStats() {
193
+ const stats = { ...this.stats };
194
+
195
+ if (stats.startedAt) {
196
+ stats.uptime = Date.now() - stats.startedAt.getTime();
197
+ }
198
+
199
+ return stats;
200
+ }
201
+
202
+ /**
203
+ * 处理文件添加事件
204
+ *
205
+ * @private
206
+ * @param {string} filePath - 文件路径
207
+ */
208
+ _handleFileAdded(filePath) {
209
+ this.watchedFiles.add(filePath);
210
+ this.stats.filesWatched = this.watchedFiles.size;
211
+ this.stats.eventsEmitted++;
212
+
213
+ this.emit('file:added', {
214
+ path: filePath,
215
+ timestamp: new Date(),
216
+ event: 'add'
217
+ });
218
+ }
219
+
220
+ /**
221
+ * 处理文件变化事件
222
+ *
223
+ * @private
224
+ * @param {string} filePath - 文件路径
225
+ */
226
+ _handleFileChanged(filePath) {
227
+ this.stats.eventsEmitted++;
228
+
229
+ this.emit('file:changed', {
230
+ path: filePath,
231
+ timestamp: new Date(),
232
+ event: 'change'
233
+ });
234
+ }
235
+
236
+ /**
237
+ * 处理文件删除事件
238
+ *
239
+ * @private
240
+ * @param {string} filePath - 文件路径
241
+ */
242
+ _handleFileDeleted(filePath) {
243
+ this.watchedFiles.delete(filePath);
244
+ this.stats.filesWatched = this.watchedFiles.size;
245
+ this.stats.eventsEmitted++;
246
+
247
+ this.emit('file:deleted', {
248
+ path: filePath,
249
+ timestamp: new Date(),
250
+ event: 'unlink'
251
+ });
252
+ }
253
+
254
+ /**
255
+ * 处理错误事件
256
+ *
257
+ * @private
258
+ * @param {Error} error - 错误对象
259
+ */
260
+ _handleError(error) {
261
+ this.stats.errors++;
262
+ this.lastError = error;
263
+
264
+ this.emit('error', {
265
+ error,
266
+ timestamp: new Date(),
267
+ context: 'watcher',
268
+ retryCount: this.retryCount
269
+ });
270
+
271
+ // 尝试恢复
272
+ this._attemptRecovery(error);
273
+ }
274
+
275
+ /**
276
+ * 尝试从错误中恢复
277
+ *
278
+ * @private
279
+ * @param {Error} error - 错误对象
280
+ */
281
+ async _attemptRecovery(error) {
282
+ if (this.retryCount >= this.config.maxRetries) {
283
+ this.emit('recovery:failed', {
284
+ error,
285
+ retryCount: this.retryCount,
286
+ message: 'Max retries exceeded'
287
+ });
288
+
289
+ // 停止监控
290
+ await this.stop();
291
+ return;
292
+ }
293
+
294
+ this.retryCount++;
295
+
296
+ this.emit('recovery:attempt', {
297
+ error,
298
+ retryCount: this.retryCount,
299
+ delay: this.config.retryDelay
300
+ });
301
+
302
+ // 延迟后重试
303
+ setTimeout(async () => {
304
+ try {
305
+ // 重启 watcher
306
+ const basePath = this.watcher ? this.watcher.options.cwd : process.cwd();
307
+
308
+ await this.stop();
309
+ await this.start(basePath);
310
+
311
+ this.stats.recoveries++;
312
+ this.retryCount = 0;
313
+
314
+ this.emit('recovery:success', {
315
+ retryCount: this.retryCount,
316
+ recoveries: this.stats.recoveries
317
+ });
318
+ } catch (recoveryError) {
319
+ this.emit('recovery:error', {
320
+ error: recoveryError,
321
+ originalError: error,
322
+ retryCount: this.retryCount
323
+ });
324
+
325
+ // 递归尝试恢复
326
+ await this._attemptRecovery(recoveryError);
327
+ }
328
+ }, this.config.retryDelay);
329
+ }
330
+
331
+ /**
332
+ * 处理准备就绪事件
333
+ *
334
+ * @private
335
+ */
336
+ _handleReady() {
337
+ this.emit('ready', {
338
+ filesWatched: this.watchedFiles.size,
339
+ patterns: this.config.patterns
340
+ });
341
+ }
342
+
343
+ /**
344
+ * 检查文件是否匹配模式
345
+ *
346
+ * @param {string} filePath - 文件路径
347
+ * @returns {boolean} 是否匹配
348
+ */
349
+ matchesPattern(filePath) {
350
+ // 规范化文件路径
351
+ const normalizedPath = filePath.replace(/\\/g, '/');
352
+
353
+ // 检查是否匹配任何 watch 模式
354
+ const matchesWatch = this.config.patterns.some(pattern => {
355
+ return minimatch(normalizedPath, pattern, { dot: true });
356
+ });
357
+
358
+ if (!matchesWatch) {
359
+ return false;
360
+ }
361
+
362
+ // 检查是否匹配任何 ignored 模式
363
+ const matchesIgnored = this.config.ignored.some(pattern => {
364
+ return minimatch(normalizedPath, pattern, { dot: true });
365
+ });
366
+
367
+ return !matchesIgnored;
368
+ }
369
+
370
+ /**
371
+ * 添加新的监控模式
372
+ *
373
+ * @param {string|Array<string>} patterns - 要添加的模式
374
+ * @returns {Promise<void>}
375
+ */
376
+ async addPatterns(patterns) {
377
+ if (!this.isWatching) {
378
+ throw new Error('FileWatcher is not running');
379
+ }
380
+
381
+ const newPatterns = Array.isArray(patterns) ? patterns : [patterns];
382
+
383
+ // 验证模式
384
+ newPatterns.forEach(pattern => {
385
+ if (typeof pattern !== 'string') {
386
+ throw new Error('Pattern must be a string');
387
+ }
388
+ });
389
+
390
+ // 添加到配置
391
+ this.config.patterns.push(...newPatterns);
392
+
393
+ // 更新 watcher
394
+ if (this.watcher) {
395
+ newPatterns.forEach(pattern => {
396
+ this.watcher.add(pattern);
397
+ });
398
+ }
399
+
400
+ this.emit('patterns:added', { patterns: newPatterns });
401
+ }
402
+
403
+ /**
404
+ * 移除监控模式
405
+ *
406
+ * @param {string|Array<string>} patterns - 要移除的模式
407
+ * @returns {Promise<void>}
408
+ */
409
+ async removePatterns(patterns) {
410
+ if (!this.isWatching) {
411
+ throw new Error('FileWatcher is not running');
412
+ }
413
+
414
+ const removePatterns = Array.isArray(patterns) ? patterns : [patterns];
415
+
416
+ // 从配置中移除
417
+ this.config.patterns = this.config.patterns.filter(
418
+ pattern => !removePatterns.includes(pattern)
419
+ );
420
+
421
+ // 更新 watcher
422
+ if (this.watcher) {
423
+ removePatterns.forEach(pattern => {
424
+ this.watcher.unwatch(pattern);
425
+ });
426
+ }
427
+
428
+ this.emit('patterns:removed', { patterns: removePatterns });
429
+ }
430
+
431
+ /**
432
+ * 获取当前监控的模式
433
+ *
434
+ * @returns {Array<string>} 模式列表
435
+ */
436
+ getPatterns() {
437
+ return [...this.config.patterns];
438
+ }
439
+
440
+ /**
441
+ * 获取忽略的模式
442
+ *
443
+ * @returns {Array<string>} 忽略模式列表
444
+ */
445
+ getIgnoredPatterns() {
446
+ return [...this.config.ignored];
447
+ }
448
+
449
+ /**
450
+ * 获取所有监控的文件
451
+ *
452
+ * @returns {Array<string>} 文件路径列表
453
+ */
454
+ getWatchedFiles() {
455
+ return Array.from(this.watchedFiles);
456
+ }
457
+
458
+ /**
459
+ * 获取最后一个错误
460
+ *
461
+ * @returns {Error|null} 最后一个错误
462
+ */
463
+ getLastError() {
464
+ return this.lastError;
465
+ }
466
+
467
+ /**
468
+ * 重置错误计数
469
+ */
470
+ resetErrorCount() {
471
+ this.stats.errors = 0;
472
+ this.retryCount = 0;
473
+ this.lastError = null;
474
+
475
+ this.emit('errors:reset');
476
+ }
477
+
478
+ /**
479
+ * 检查是否健康
480
+ *
481
+ * @returns {Object} 健康状态
482
+ */
483
+ getHealth() {
484
+ const errorRate = this.stats.eventsEmitted > 0
485
+ ? this.stats.errors / this.stats.eventsEmitted
486
+ : 0;
487
+
488
+ return {
489
+ isHealthy: this.isWatching && errorRate < 0.1,
490
+ isWatching: this.isWatching,
491
+ errorCount: this.stats.errors,
492
+ errorRate,
493
+ recoveries: this.stats.recoveries,
494
+ lastError: this.lastError ? this.lastError.message : null
495
+ };
496
+ }
497
+ }
498
+
499
+ module.exports = FileWatcher;