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,378 @@
|
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* ContextExporter - 上下文导出器
|
|
6
|
+
*
|
|
7
|
+
* 将 Spec 上下文导出为独立的 Markdown 文件,供跨工具使用
|
|
8
|
+
*/
|
|
9
|
+
class ContextExporter {
|
|
10
|
+
constructor() {
|
|
11
|
+
this.exportFileName = 'context-export.md';
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* 导出 Spec 上下文
|
|
16
|
+
*
|
|
17
|
+
* @param {string} projectPath - 项目根目录路径
|
|
18
|
+
* @param {string} specName - Spec 名称
|
|
19
|
+
* @param {Object} options - 导出选项
|
|
20
|
+
* @returns {Promise<Object>} 导出结果
|
|
21
|
+
*/
|
|
22
|
+
async exportContext(projectPath, specName, options = {}) {
|
|
23
|
+
const {
|
|
24
|
+
includeRequirements = true,
|
|
25
|
+
includeDesign = true,
|
|
26
|
+
includeTasks = true,
|
|
27
|
+
includeSteering = false,
|
|
28
|
+
steeringFiles = []
|
|
29
|
+
} = options;
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
const specPath = path.join(projectPath, '.kiro/specs', specName);
|
|
33
|
+
|
|
34
|
+
// 检查 Spec 是否存在
|
|
35
|
+
const specExists = await fs.pathExists(specPath);
|
|
36
|
+
if (!specExists) {
|
|
37
|
+
return {
|
|
38
|
+
success: false,
|
|
39
|
+
error: `Spec not found: ${specName}`
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// 构建导出内容
|
|
44
|
+
const sections = [];
|
|
45
|
+
|
|
46
|
+
// 添加头部
|
|
47
|
+
sections.push(this.generateHeader(specName));
|
|
48
|
+
|
|
49
|
+
// 添加 Requirements
|
|
50
|
+
if (includeRequirements) {
|
|
51
|
+
const requirements = await this.readSpecFile(specPath, 'requirements.md');
|
|
52
|
+
if (requirements) {
|
|
53
|
+
sections.push(this.formatSection('Requirements', requirements));
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// 添加 Design
|
|
58
|
+
if (includeDesign) {
|
|
59
|
+
const design = await this.readSpecFile(specPath, 'design.md');
|
|
60
|
+
if (design) {
|
|
61
|
+
sections.push(this.formatSection('Design', design));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// 添加 Tasks
|
|
66
|
+
if (includeTasks) {
|
|
67
|
+
const tasks = await this.readSpecFile(specPath, 'tasks.md');
|
|
68
|
+
if (tasks) {
|
|
69
|
+
sections.push(this.formatSection('Tasks', tasks));
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// 添加 Steering Rules
|
|
74
|
+
if (includeSteering && steeringFiles.length > 0) {
|
|
75
|
+
const steeringContent = await this.includeSteeringRules(projectPath, steeringFiles);
|
|
76
|
+
if (steeringContent) {
|
|
77
|
+
sections.push(steeringContent);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// 添加使用说明
|
|
82
|
+
sections.push(this.generateUsageInstructions());
|
|
83
|
+
|
|
84
|
+
// 组合所有内容
|
|
85
|
+
const exportContent = sections.join('\n\n---\n\n');
|
|
86
|
+
|
|
87
|
+
// 保存导出文件
|
|
88
|
+
const exportPath = path.join(specPath, this.exportFileName);
|
|
89
|
+
await fs.writeFile(exportPath, exportContent, 'utf8');
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
success: true,
|
|
93
|
+
exportPath,
|
|
94
|
+
specName,
|
|
95
|
+
sections: sections.length,
|
|
96
|
+
size: Buffer.byteLength(exportContent, 'utf8')
|
|
97
|
+
};
|
|
98
|
+
} catch (error) {
|
|
99
|
+
return {
|
|
100
|
+
success: false,
|
|
101
|
+
error: error.message
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* 生成导出文件头部
|
|
108
|
+
*
|
|
109
|
+
* @param {string} specName - Spec 名称
|
|
110
|
+
* @returns {string} 头部内容
|
|
111
|
+
*/
|
|
112
|
+
generateHeader(specName) {
|
|
113
|
+
const timestamp = new Date().toISOString();
|
|
114
|
+
|
|
115
|
+
return `# Context Export: ${specName}
|
|
116
|
+
|
|
117
|
+
**Exported**: ${timestamp}
|
|
118
|
+
**Format**: Standalone Markdown
|
|
119
|
+
**Purpose**: Cross-tool AI coding assistant usage
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## 📋 About This Export
|
|
124
|
+
|
|
125
|
+
This file contains the complete context for the **${specName}** specification. It is designed to be used with AI coding assistants like Claude Code, Cursor, Codex, or any other tool that accepts Markdown context.
|
|
126
|
+
|
|
127
|
+
### What's Included
|
|
128
|
+
|
|
129
|
+
- Requirements document
|
|
130
|
+
- Design document
|
|
131
|
+
- Task list
|
|
132
|
+
- Optional: Steering rules
|
|
133
|
+
|
|
134
|
+
### How to Use
|
|
135
|
+
|
|
136
|
+
1. **Copy this entire file** into your AI coding assistant
|
|
137
|
+
2. **Reference specific sections** when working on tasks
|
|
138
|
+
3. **Update task status** in the original tasks.md after completion
|
|
139
|
+
|
|
140
|
+
---`;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* 格式化章节
|
|
145
|
+
*
|
|
146
|
+
* @param {string} title - 章节标题
|
|
147
|
+
* @param {string} content - 章节内容
|
|
148
|
+
* @returns {string} 格式化后的章节
|
|
149
|
+
*/
|
|
150
|
+
formatSection(title, content) {
|
|
151
|
+
return `## ${title}
|
|
152
|
+
|
|
153
|
+
${content}`;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* 读取 Spec 文件
|
|
158
|
+
*
|
|
159
|
+
* @param {string} specPath - Spec 目录路径
|
|
160
|
+
* @param {string} fileName - 文件名
|
|
161
|
+
* @returns {Promise<string|null>} 文件内容或 null
|
|
162
|
+
*/
|
|
163
|
+
async readSpecFile(specPath, fileName) {
|
|
164
|
+
const filePath = path.join(specPath, fileName);
|
|
165
|
+
|
|
166
|
+
try {
|
|
167
|
+
const exists = await fs.pathExists(filePath);
|
|
168
|
+
if (!exists) {
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return await fs.readFile(filePath, 'utf8');
|
|
173
|
+
} catch (error) {
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* 包含 Steering 规则
|
|
180
|
+
*
|
|
181
|
+
* @param {string} projectPath - 项目根目录路径
|
|
182
|
+
* @param {Array<string>} steeringFiles - Steering 文件列表
|
|
183
|
+
* @returns {Promise<string>} Steering 内容
|
|
184
|
+
*/
|
|
185
|
+
async includeSteeringRules(projectPath, steeringFiles) {
|
|
186
|
+
const steeringPath = path.join(projectPath, '.kiro/steering');
|
|
187
|
+
const sections = ['## Steering Rules\n'];
|
|
188
|
+
|
|
189
|
+
for (const fileName of steeringFiles) {
|
|
190
|
+
const filePath = path.join(steeringPath, fileName);
|
|
191
|
+
|
|
192
|
+
try {
|
|
193
|
+
const exists = await fs.pathExists(filePath);
|
|
194
|
+
if (!exists) {
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const content = await fs.readFile(filePath, 'utf8');
|
|
199
|
+
sections.push(`### ${fileName}\n\n${content}`);
|
|
200
|
+
} catch (error) {
|
|
201
|
+
// Skip files that can't be read
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return sections.length > 1 ? sections.join('\n\n') : null;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* 生成使用说明
|
|
211
|
+
*
|
|
212
|
+
* @returns {string} 使用说明
|
|
213
|
+
*/
|
|
214
|
+
generateUsageInstructions() {
|
|
215
|
+
return `## 💡 Usage Instructions
|
|
216
|
+
|
|
217
|
+
### For Claude Code / Cursor / Codex
|
|
218
|
+
|
|
219
|
+
1. **Load this file** as context in your AI coding assistant
|
|
220
|
+
2. **Reference specific tasks** when implementing features
|
|
221
|
+
3. **Follow the design** outlined in the Design section
|
|
222
|
+
4. **Update task status** in the original \`tasks.md\` after completion
|
|
223
|
+
|
|
224
|
+
### Task Status Updates
|
|
225
|
+
|
|
226
|
+
After completing a task, update the original \`tasks.md\` file:
|
|
227
|
+
|
|
228
|
+
\`\`\`markdown
|
|
229
|
+
- [x] 1.1 Completed task
|
|
230
|
+
- [-] 1.2 In-progress task
|
|
231
|
+
- [ ] 1.3 Not started task
|
|
232
|
+
\`\`\`
|
|
233
|
+
|
|
234
|
+
### Best Practices
|
|
235
|
+
|
|
236
|
+
- **Read Requirements first** to understand the problem
|
|
237
|
+
- **Review Design** to understand the solution approach
|
|
238
|
+
- **Follow Tasks** to implement incrementally
|
|
239
|
+
- **Test thoroughly** before marking tasks as complete
|
|
240
|
+
- **Document changes** in code comments
|
|
241
|
+
|
|
242
|
+
### Getting Help
|
|
243
|
+
|
|
244
|
+
If you encounter issues:
|
|
245
|
+
1. Review the Requirements for clarification
|
|
246
|
+
2. Check the Design for architectural guidance
|
|
247
|
+
3. Consult the original project documentation
|
|
248
|
+
4. Ask the project maintainer for guidance
|
|
249
|
+
|
|
250
|
+
---
|
|
251
|
+
|
|
252
|
+
**Generated by**: kiro-spec-engine
|
|
253
|
+
**Export Format**: Standalone Markdown
|
|
254
|
+
**Compatible with**: Claude Code, Cursor, Codex, and other AI coding assistants`;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* 生成任务特定上下文
|
|
259
|
+
*
|
|
260
|
+
* @param {string} projectPath - 项目根目录路径
|
|
261
|
+
* @param {string} specName - Spec 名称
|
|
262
|
+
* @param {string} taskId - 任务 ID
|
|
263
|
+
* @returns {Promise<Object>} 任务上下文
|
|
264
|
+
*/
|
|
265
|
+
async generateTaskContext(projectPath, specName, taskId) {
|
|
266
|
+
try {
|
|
267
|
+
const specPath = path.join(projectPath, '.kiro/specs', specName);
|
|
268
|
+
|
|
269
|
+
// 读取所有文件
|
|
270
|
+
const requirements = await this.readSpecFile(specPath, 'requirements.md');
|
|
271
|
+
const design = await this.readSpecFile(specPath, 'design.md');
|
|
272
|
+
const tasks = await this.readSpecFile(specPath, 'tasks.md');
|
|
273
|
+
|
|
274
|
+
if (!tasks) {
|
|
275
|
+
return {
|
|
276
|
+
success: false,
|
|
277
|
+
error: 'tasks.md not found'
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// 提取任务信息
|
|
282
|
+
const taskInfo = this.extractTaskInfo(tasks, taskId);
|
|
283
|
+
|
|
284
|
+
if (!taskInfo) {
|
|
285
|
+
return {
|
|
286
|
+
success: false,
|
|
287
|
+
error: `Task not found: ${taskId}`
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// 提取相关 Requirements
|
|
292
|
+
const relevantRequirements = this.extractRelevantRequirements(
|
|
293
|
+
requirements,
|
|
294
|
+
taskInfo
|
|
295
|
+
);
|
|
296
|
+
|
|
297
|
+
// 提取相关 Design
|
|
298
|
+
const relevantDesign = this.extractRelevantDesignSections(
|
|
299
|
+
design,
|
|
300
|
+
taskInfo
|
|
301
|
+
);
|
|
302
|
+
|
|
303
|
+
return {
|
|
304
|
+
success: true,
|
|
305
|
+
taskId,
|
|
306
|
+
taskInfo,
|
|
307
|
+
relevantRequirements,
|
|
308
|
+
relevantDesign
|
|
309
|
+
};
|
|
310
|
+
} catch (error) {
|
|
311
|
+
return {
|
|
312
|
+
success: false,
|
|
313
|
+
error: error.message
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* 提取任务信息
|
|
320
|
+
*
|
|
321
|
+
* @param {string} tasksContent - tasks.md 内容
|
|
322
|
+
* @param {string} taskId - 任务 ID
|
|
323
|
+
* @returns {Object|null} 任务信息
|
|
324
|
+
*/
|
|
325
|
+
extractTaskInfo(tasksContent, taskId) {
|
|
326
|
+
const lines = tasksContent.split('\n');
|
|
327
|
+
const taskPattern = new RegExp(`^-\\s*\\[[\\s\\-x~]\\]\\*?\\s+${taskId}\\s+(.+)$`);
|
|
328
|
+
|
|
329
|
+
for (const line of lines) {
|
|
330
|
+
const match = line.match(taskPattern);
|
|
331
|
+
if (match) {
|
|
332
|
+
return {
|
|
333
|
+
id: taskId,
|
|
334
|
+
title: match[1].replace(/\[@.+\]$/, '').trim(),
|
|
335
|
+
fullLine: line
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
return null;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* 提取相关 Requirements
|
|
345
|
+
*
|
|
346
|
+
* @param {string} requirements - Requirements 内容
|
|
347
|
+
* @param {Object} taskInfo - 任务信息
|
|
348
|
+
* @returns {string} 相关 Requirements
|
|
349
|
+
*/
|
|
350
|
+
extractRelevantRequirements(requirements, taskInfo) {
|
|
351
|
+
if (!requirements) {
|
|
352
|
+
return 'No requirements document found.';
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// 简单实现:返回所有 Requirements
|
|
356
|
+
// 可以根据任务描述中的关键词进行智能过滤
|
|
357
|
+
return requirements;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* 提取相关 Design 章节
|
|
362
|
+
*
|
|
363
|
+
* @param {string} design - Design 内容
|
|
364
|
+
* @param {Object} taskInfo - 任务信息
|
|
365
|
+
* @returns {string} 相关 Design 章节
|
|
366
|
+
*/
|
|
367
|
+
extractRelevantDesignSections(design, taskInfo) {
|
|
368
|
+
if (!design) {
|
|
369
|
+
return 'No design document found.';
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// 简单实现:返回所有 Design
|
|
373
|
+
// 可以根据任务描述中的关键词进行智能过滤
|
|
374
|
+
return design;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
module.exports = ContextExporter;
|