kiro-spec-engine 1.2.3 → 1.3.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 +74 -0
- package/README.md +172 -0
- package/bin/kiro-spec-engine.js +62 -0
- package/docs/agent-hooks-analysis.md +815 -0
- package/docs/cross-tool-guide.md +554 -0
- package/docs/manual-workflows-guide.md +417 -0
- package/docs/steering-strategy-guide.md +196 -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,323 @@
|
|
|
1
|
+
const EventEmitter = require('events');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* EventDebouncer - 事件防抖和节流器
|
|
5
|
+
*
|
|
6
|
+
* 防止过度执行命令,通过防抖和节流机制控制事件触发频率
|
|
7
|
+
*/
|
|
8
|
+
class EventDebouncer extends EventEmitter {
|
|
9
|
+
constructor(config = {}) {
|
|
10
|
+
super();
|
|
11
|
+
|
|
12
|
+
this.config = {
|
|
13
|
+
defaultDelay: config.defaultDelay || 2000,
|
|
14
|
+
defaultThrottleLimit: config.defaultThrottleLimit || 5000,
|
|
15
|
+
maxQueueSize: config.maxQueueSize || 100,
|
|
16
|
+
...config
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
// 防抖定时器
|
|
20
|
+
this.debounceTimers = new Map();
|
|
21
|
+
|
|
22
|
+
// 节流记录
|
|
23
|
+
this.throttleRecords = new Map();
|
|
24
|
+
|
|
25
|
+
// 事件队列
|
|
26
|
+
this.eventQueue = [];
|
|
27
|
+
|
|
28
|
+
// 统计信息
|
|
29
|
+
this.stats = {
|
|
30
|
+
eventsReceived: 0,
|
|
31
|
+
eventsDebounced: 0,
|
|
32
|
+
eventsThrottled: 0,
|
|
33
|
+
eventsExecuted: 0,
|
|
34
|
+
duplicatesDropped: 0
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* 防抖处理事件
|
|
40
|
+
*
|
|
41
|
+
* @param {string} key - 事件键(通常是文件路径)
|
|
42
|
+
* @param {Function} callback - 回调函数
|
|
43
|
+
* @param {number} delay - 延迟时间(毫秒)
|
|
44
|
+
* @returns {void}
|
|
45
|
+
*/
|
|
46
|
+
debounce(key, callback, delay = null) {
|
|
47
|
+
if (typeof callback !== 'function') {
|
|
48
|
+
throw new Error('Callback must be a function');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const actualDelay = delay !== null ? delay : this.config.defaultDelay;
|
|
52
|
+
|
|
53
|
+
this.stats.eventsReceived++;
|
|
54
|
+
|
|
55
|
+
// 清除之前的定时器
|
|
56
|
+
if (this.debounceTimers.has(key)) {
|
|
57
|
+
clearTimeout(this.debounceTimers.get(key));
|
|
58
|
+
this.stats.eventsDebounced++;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// 设置新的定时器
|
|
62
|
+
const timer = setTimeout(() => {
|
|
63
|
+
this.debounceTimers.delete(key);
|
|
64
|
+
this.stats.eventsExecuted++;
|
|
65
|
+
|
|
66
|
+
this.emit('execute', { key, type: 'debounce' });
|
|
67
|
+
|
|
68
|
+
try {
|
|
69
|
+
callback();
|
|
70
|
+
} catch (error) {
|
|
71
|
+
this.emit('error', { error, key, type: 'debounce' });
|
|
72
|
+
}
|
|
73
|
+
}, actualDelay);
|
|
74
|
+
|
|
75
|
+
this.debounceTimers.set(key, timer);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* 节流处理事件
|
|
80
|
+
*
|
|
81
|
+
* @param {string} key - 事件键
|
|
82
|
+
* @param {Function} callback - 回调函数
|
|
83
|
+
* @param {number} limit - 节流限制(毫秒)
|
|
84
|
+
* @returns {boolean} 是否执行了回调
|
|
85
|
+
*/
|
|
86
|
+
throttle(key, callback, limit = null) {
|
|
87
|
+
if (typeof callback !== 'function') {
|
|
88
|
+
throw new Error('Callback must be a function');
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const actualLimit = limit !== null ? limit : this.config.defaultThrottleLimit;
|
|
92
|
+
|
|
93
|
+
this.stats.eventsReceived++;
|
|
94
|
+
|
|
95
|
+
const now = Date.now();
|
|
96
|
+
const lastExecution = this.throttleRecords.get(key);
|
|
97
|
+
|
|
98
|
+
// 检查是否可以执行
|
|
99
|
+
if (!lastExecution || (now - lastExecution) >= actualLimit) {
|
|
100
|
+
this.throttleRecords.set(key, now);
|
|
101
|
+
this.stats.eventsExecuted++;
|
|
102
|
+
|
|
103
|
+
this.emit('execute', { key, type: 'throttle' });
|
|
104
|
+
|
|
105
|
+
try {
|
|
106
|
+
callback();
|
|
107
|
+
return true;
|
|
108
|
+
} catch (error) {
|
|
109
|
+
this.emit('error', { error, key, type: 'throttle' });
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
} else {
|
|
113
|
+
this.stats.eventsThrottled++;
|
|
114
|
+
this.emit('throttled', {
|
|
115
|
+
key,
|
|
116
|
+
remainingTime: actualLimit - (now - lastExecution)
|
|
117
|
+
});
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* 添加事件到队列(带去重)
|
|
124
|
+
*
|
|
125
|
+
* @param {Object} event - 事件对象
|
|
126
|
+
* @returns {boolean} 是否成功添加
|
|
127
|
+
*/
|
|
128
|
+
enqueue(event) {
|
|
129
|
+
if (!event || !event.key) {
|
|
130
|
+
throw new Error('Event must have a key property');
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// 检查队列大小
|
|
134
|
+
if (this.eventQueue.length >= this.config.maxQueueSize) {
|
|
135
|
+
this.emit('queue:full', {
|
|
136
|
+
size: this.eventQueue.length,
|
|
137
|
+
maxSize: this.config.maxQueueSize
|
|
138
|
+
});
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// 检查重复
|
|
143
|
+
const isDuplicate = this.eventQueue.some(e =>
|
|
144
|
+
e.key === event.key &&
|
|
145
|
+
e.type === event.type &&
|
|
146
|
+
JSON.stringify(e.data) === JSON.stringify(event.data)
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
if (isDuplicate) {
|
|
150
|
+
this.stats.duplicatesDropped++;
|
|
151
|
+
this.emit('duplicate:dropped', { event });
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// 添加到队列
|
|
156
|
+
this.eventQueue.push({
|
|
157
|
+
...event,
|
|
158
|
+
timestamp: Date.now()
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
this.emit('queue:added', {
|
|
162
|
+
event,
|
|
163
|
+
queueSize: this.eventQueue.length
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
return true;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* 从队列中取出事件
|
|
171
|
+
*
|
|
172
|
+
* @returns {Object|null} 事件对象
|
|
173
|
+
*/
|
|
174
|
+
dequeue() {
|
|
175
|
+
if (this.eventQueue.length === 0) {
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const event = this.eventQueue.shift();
|
|
180
|
+
|
|
181
|
+
this.emit('queue:removed', {
|
|
182
|
+
event,
|
|
183
|
+
queueSize: this.eventQueue.length
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
return event;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* 清除所有定时器和队列
|
|
191
|
+
*/
|
|
192
|
+
clear() {
|
|
193
|
+
// 清除所有防抖定时器
|
|
194
|
+
for (const timer of this.debounceTimers.values()) {
|
|
195
|
+
clearTimeout(timer);
|
|
196
|
+
}
|
|
197
|
+
this.debounceTimers.clear();
|
|
198
|
+
|
|
199
|
+
// 清除节流记录
|
|
200
|
+
this.throttleRecords.clear();
|
|
201
|
+
|
|
202
|
+
// 清除队列
|
|
203
|
+
const queueSize = this.eventQueue.length;
|
|
204
|
+
this.eventQueue = [];
|
|
205
|
+
|
|
206
|
+
this.emit('cleared', {
|
|
207
|
+
timersCleared: this.debounceTimers.size,
|
|
208
|
+
queueCleared: queueSize
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* 清除特定键的定时器
|
|
214
|
+
*
|
|
215
|
+
* @param {string} key - 事件键
|
|
216
|
+
*/
|
|
217
|
+
clearKey(key) {
|
|
218
|
+
// 清除防抖定时器
|
|
219
|
+
if (this.debounceTimers.has(key)) {
|
|
220
|
+
clearTimeout(this.debounceTimers.get(key));
|
|
221
|
+
this.debounceTimers.delete(key);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// 清除节流记录
|
|
225
|
+
this.throttleRecords.delete(key);
|
|
226
|
+
|
|
227
|
+
// 从队列中移除
|
|
228
|
+
const originalLength = this.eventQueue.length;
|
|
229
|
+
this.eventQueue = this.eventQueue.filter(e => e.key !== key);
|
|
230
|
+
const removed = originalLength - this.eventQueue.length;
|
|
231
|
+
|
|
232
|
+
if (removed > 0) {
|
|
233
|
+
this.emit('key:cleared', { key, eventsRemoved: removed });
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* 获取队列大小
|
|
239
|
+
*
|
|
240
|
+
* @returns {number} 队列大小
|
|
241
|
+
*/
|
|
242
|
+
getQueueSize() {
|
|
243
|
+
return this.eventQueue.length;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* 获取队列内容
|
|
248
|
+
*
|
|
249
|
+
* @returns {Array} 队列内容
|
|
250
|
+
*/
|
|
251
|
+
getQueue() {
|
|
252
|
+
return [...this.eventQueue];
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* 获取活跃的防抖定时器数量
|
|
257
|
+
*
|
|
258
|
+
* @returns {number} 定时器数量
|
|
259
|
+
*/
|
|
260
|
+
getActiveTimers() {
|
|
261
|
+
return this.debounceTimers.size;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* 获取统计信息
|
|
266
|
+
*
|
|
267
|
+
* @returns {Object} 统计信息
|
|
268
|
+
*/
|
|
269
|
+
getStats() {
|
|
270
|
+
return {
|
|
271
|
+
...this.stats,
|
|
272
|
+
activeTimers: this.debounceTimers.size,
|
|
273
|
+
queueSize: this.eventQueue.length,
|
|
274
|
+
throttleRecords: this.throttleRecords.size
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* 重置统计信息
|
|
280
|
+
*/
|
|
281
|
+
resetStats() {
|
|
282
|
+
this.stats = {
|
|
283
|
+
eventsReceived: 0,
|
|
284
|
+
eventsDebounced: 0,
|
|
285
|
+
eventsThrottled: 0,
|
|
286
|
+
eventsExecuted: 0,
|
|
287
|
+
duplicatesDropped: 0
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
this.emit('stats:reset');
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* 检查键是否在防抖中
|
|
295
|
+
*
|
|
296
|
+
* @param {string} key - 事件键
|
|
297
|
+
* @returns {boolean} 是否在防抖中
|
|
298
|
+
*/
|
|
299
|
+
isDebouncing(key) {
|
|
300
|
+
return this.debounceTimers.has(key);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* 检查键是否在节流中
|
|
305
|
+
*
|
|
306
|
+
* @param {string} key - 事件键
|
|
307
|
+
* @param {number} limit - 节流限制
|
|
308
|
+
* @returns {boolean} 是否在节流中
|
|
309
|
+
*/
|
|
310
|
+
isThrottled(key, limit = null) {
|
|
311
|
+
const actualLimit = limit !== null ? limit : this.config.defaultThrottleLimit;
|
|
312
|
+
const lastExecution = this.throttleRecords.get(key);
|
|
313
|
+
|
|
314
|
+
if (!lastExecution) {
|
|
315
|
+
return false;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const now = Date.now();
|
|
319
|
+
return (now - lastExecution) < actualLimit;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
module.exports = EventDebouncer;
|