kiro-spec-engine 1.45.13 → 1.46.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 +14 -0
- package/README.md +15 -4
- package/README.zh.md +15 -3
- package/bin/kiro-spec-engine.js +84 -0
- package/docs/adoption-guide.md +3 -2
- package/docs/command-reference.md +30 -12
- package/docs/cross-tool-guide.md +2 -1
- package/docs/document-governance.md +2 -1
- package/docs/faq.md +14 -13
- package/docs/manual-workflows-guide.md +2 -1
- package/docs/quick-start-with-ai-tools.md +4 -3
- package/docs/quick-start.md +21 -7
- package/docs/spec-workflow.md +3 -2
- package/docs/tools/claude-guide.md +3 -2
- package/docs/tools/cursor-guide.md +3 -2
- package/docs/tools/generic-guide.md +3 -2
- package/docs/tools/vscode-guide.md +3 -2
- package/docs/tools/windsurf-guide.md +3 -2
- package/docs/troubleshooting.md +10 -9
- package/docs/zh/quick-start.md +30 -14
- package/docs/zh/tools/claude-guide.md +2 -1
- package/docs/zh/tools/cursor-guide.md +2 -1
- package/docs/zh/tools/generic-guide.md +8 -7
- package/docs/zh/tools/vscode-guide.md +2 -1
- package/docs/zh/tools/windsurf-guide.md +2 -1
- package/lib/commands/orchestrate.js +123 -95
- package/lib/commands/spec-bootstrap.js +147 -0
- package/lib/commands/spec-gate.js +157 -0
- package/lib/commands/spec-pipeline.js +205 -0
- package/lib/spec/bootstrap/context-collector.js +48 -0
- package/lib/spec/bootstrap/draft-generator.js +158 -0
- package/lib/spec/bootstrap/questionnaire-engine.js +70 -0
- package/lib/spec/bootstrap/trace-emitter.js +59 -0
- package/lib/spec/multi-spec-orchestrate.js +93 -0
- package/lib/spec/pipeline/constants.js +6 -0
- package/lib/spec/pipeline/stage-adapters.js +118 -0
- package/lib/spec/pipeline/stage-runner.js +146 -0
- package/lib/spec/pipeline/state-store.js +119 -0
- package/lib/spec-gate/engine/gate-engine.js +165 -0
- package/lib/spec-gate/policy/default-policy.js +22 -0
- package/lib/spec-gate/policy/policy-loader.js +103 -0
- package/lib/spec-gate/result-emitter.js +81 -0
- package/lib/spec-gate/rules/default-rules.js +156 -0
- package/lib/spec-gate/rules/rule-registry.js +51 -0
- package/package.json +2 -1
package/docs/troubleshooting.md
CHANGED
|
@@ -249,7 +249,7 @@ No Specs found in .kiro/specs/
|
|
|
249
249
|
|
|
250
250
|
**Create your first Spec:**
|
|
251
251
|
```bash
|
|
252
|
-
kse
|
|
252
|
+
kse spec bootstrap --name 01-00-my-feature --non-interactive
|
|
253
253
|
```
|
|
254
254
|
|
|
255
255
|
**Or check if Specs exist:**
|
|
@@ -263,7 +263,7 @@ ls -la .kiro/specs/
|
|
|
263
263
|
|
|
264
264
|
**Symptoms:**
|
|
265
265
|
```bash
|
|
266
|
-
$ kse
|
|
266
|
+
$ kse spec bootstrap --name my-feature --non-interactive
|
|
267
267
|
Error: Invalid Spec name format
|
|
268
268
|
Expected: {number}-{number}-{kebab-case-name}
|
|
269
269
|
```
|
|
@@ -275,13 +275,13 @@ Expected: {number}-{number}-{kebab-case-name}
|
|
|
275
275
|
**Use correct format:**
|
|
276
276
|
```bash
|
|
277
277
|
# ✅ Correct
|
|
278
|
-
kse
|
|
279
|
-
kse
|
|
278
|
+
kse spec bootstrap --name 01-00-user-login --non-interactive
|
|
279
|
+
kse spec bootstrap --name 02-01-fix-auth-bug --non-interactive
|
|
280
280
|
|
|
281
281
|
# ❌ Wrong
|
|
282
|
-
kse
|
|
283
|
-
kse
|
|
284
|
-
kse
|
|
282
|
+
kse spec bootstrap --name user-login --non-interactive
|
|
283
|
+
kse spec bootstrap --name 01-user-login --non-interactive
|
|
284
|
+
kse spec bootstrap --name UserLogin --non-interactive
|
|
285
285
|
```
|
|
286
286
|
|
|
287
287
|
---
|
|
@@ -302,7 +302,7 @@ Error: Failed to export context
|
|
|
302
302
|
ls .kiro/specs/01-00-user-login/
|
|
303
303
|
|
|
304
304
|
# If not, create it
|
|
305
|
-
kse
|
|
305
|
+
kse spec bootstrap --name 01-00-user-login --non-interactive
|
|
306
306
|
```
|
|
307
307
|
|
|
308
308
|
**2. Missing Spec files:**
|
|
@@ -1063,7 +1063,7 @@ ls -la .kiro/
|
|
|
1063
1063
|
Context export fails for Spec with Chinese characters in filename
|
|
1064
1064
|
|
|
1065
1065
|
## Steps to Reproduce
|
|
1066
|
-
1. Create Spec: kse
|
|
1066
|
+
1. Create Spec: kse spec bootstrap --name 01-00-用户登录 --non-interactive
|
|
1067
1067
|
2. Run: kse context export 01-00-用户登录
|
|
1068
1068
|
3. Error occurs
|
|
1069
1069
|
|
|
@@ -1132,3 +1132,4 @@ kse docs cleanup --dry-run
|
|
|
1132
1132
|
|
|
1133
1133
|
**Version**: 1.42.0
|
|
1134
1134
|
**Last Updated**: 2026-02-11
|
|
1135
|
+
|
package/docs/zh/quick-start.md
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
**版本**: 1.
|
|
8
|
-
**最后更新**: 2026-02-
|
|
7
|
+
**版本**: 1.46.0
|
|
8
|
+
**最后更新**: 2026-02-13
|
|
9
9
|
**预计时间**: 5 分钟
|
|
10
10
|
**目标读者**: 初学者
|
|
11
11
|
|
|
@@ -100,7 +100,7 @@ kse adopt
|
|
|
100
100
|
✅ 项目成功采用 kse!
|
|
101
101
|
|
|
102
102
|
下一步:
|
|
103
|
-
1. 创建你的第一个 Spec:kse
|
|
103
|
+
1. 创建你的第一个 Spec:kse spec bootstrap --name 01-00-my-feature --non-interactive
|
|
104
104
|
2. 阅读指南:.kiro/README.md
|
|
105
105
|
```
|
|
106
106
|
|
|
@@ -130,20 +130,20 @@ Status: Ready
|
|
|
130
130
|
|
|
131
131
|
---
|
|
132
132
|
|
|
133
|
-
## 步骤 3
|
|
133
|
+
## 步骤 3:生成你的第一个 Spec 初稿(2 分钟)
|
|
134
134
|
|
|
135
|
-
|
|
135
|
+
让我们为用户登录功能生成一个 Spec 初稿:
|
|
136
136
|
|
|
137
137
|
```bash
|
|
138
|
-
kse
|
|
138
|
+
kse spec bootstrap --name 01-00-user-login --non-interactive
|
|
139
139
|
```
|
|
140
140
|
|
|
141
141
|
**预期输出:**
|
|
142
142
|
```
|
|
143
|
-
✓
|
|
144
|
-
✓
|
|
145
|
-
✓
|
|
146
|
-
✓
|
|
143
|
+
✓ 生成 Spec 初稿:01-00-user-login
|
|
144
|
+
✓ 生成 requirements.md
|
|
145
|
+
✓ 生成 design.md
|
|
146
|
+
✓ 生成 tasks.md
|
|
147
147
|
|
|
148
148
|
📝 下一步:
|
|
149
149
|
1. 编辑 requirements.md 定义你要构建什么
|
|
@@ -151,6 +151,14 @@ kse create-spec 01-00-user-login
|
|
|
151
151
|
3. 编辑 tasks.md 分解实现步骤
|
|
152
152
|
```
|
|
153
153
|
|
|
154
|
+
多 Spec 场景下可直接使用:
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
kse spec bootstrap --specs "01-00-user-login,01-01-user-session" --max-parallel 3
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
上述命令会默认切换到 orchestrate 模式并并行推进。
|
|
161
|
+
|
|
154
162
|
### 3.1 编写需求
|
|
155
163
|
|
|
156
164
|
打开 `.kiro/specs/01-00-user-login/requirements.md` 并编写:
|
|
@@ -477,6 +485,13 @@ graph TD
|
|
|
477
485
|
|
|
478
486
|
现在你的 Spec 已完成,为你的 AI 工具导出它:
|
|
479
487
|
|
|
488
|
+
(可选)先执行标准化流程和闸口检查:
|
|
489
|
+
|
|
490
|
+
```bash
|
|
491
|
+
kse spec pipeline run --spec 01-00-user-login
|
|
492
|
+
kse spec gate run --spec 01-00-user-login --json
|
|
493
|
+
```
|
|
494
|
+
|
|
480
495
|
```bash
|
|
481
496
|
kse context export 01-00-user-login
|
|
482
497
|
```
|
|
@@ -696,16 +711,17 @@ ls .kiro/specs/
|
|
|
696
711
|
|
|
697
712
|
**kse 工作流:**
|
|
698
713
|
```
|
|
699
|
-
|
|
714
|
+
bootstrap Spec → pipeline/gate → 导出上下文 → AI 实现 → 更新任务 → 重复
|
|
700
715
|
```
|
|
701
716
|
|
|
702
717
|
**准备好构建你的下一个功能了吗?** 🚀
|
|
703
718
|
|
|
704
719
|
```bash
|
|
705
|
-
kse
|
|
720
|
+
kse spec bootstrap --name 02-00-your-next-feature --non-interactive
|
|
706
721
|
```
|
|
707
722
|
|
|
708
723
|
---
|
|
709
724
|
|
|
710
|
-
**版本**: 1.
|
|
711
|
-
**最后更新**: 2026-02-
|
|
725
|
+
**版本**: 1.46.0
|
|
726
|
+
**最后更新**: 2026-02-13
|
|
727
|
+
|
|
@@ -129,7 +129,7 @@ Claude 通过 Web 界面工作,无需特殊配置。只需导出上下文并
|
|
|
129
129
|
|
|
130
130
|
```bash
|
|
131
131
|
# 1. 创建 Spec
|
|
132
|
-
kse
|
|
132
|
+
kse spec bootstrap --name 01-00-user-login --non-interactive
|
|
133
133
|
|
|
134
134
|
# 2. 编写 requirements.md、design.md、tasks.md
|
|
135
135
|
|
|
@@ -346,3 +346,4 @@ Claude 擅长解释。询问:
|
|
|
346
346
|
|
|
347
347
|
**版本**: 1.42.0
|
|
348
348
|
**最后更新**: 2026-02-11
|
|
349
|
+
|
|
@@ -173,7 +173,7 @@
|
|
|
173
173
|
|
|
174
174
|
```bash
|
|
175
175
|
# 1. 创建 Spec
|
|
176
|
-
kse
|
|
176
|
+
kse spec bootstrap --name 01-00-user-login --non-interactive
|
|
177
177
|
|
|
178
178
|
# 2. 编写 requirements.md、design.md、tasks.md
|
|
179
179
|
|
|
@@ -278,3 +278,4 @@ kse prompt generate 01-00-user-login 1.1
|
|
|
278
278
|
|
|
279
279
|
**版本**: 1.42.0
|
|
280
280
|
**最后更新**: 2026-02-11
|
|
281
|
+
|
|
@@ -50,7 +50,7 @@ kse --version
|
|
|
50
50
|
kse adopt
|
|
51
51
|
|
|
52
52
|
# 创建 Spec
|
|
53
|
-
kse
|
|
53
|
+
kse spec bootstrap --name 01-00-my-feature --non-interactive
|
|
54
54
|
```
|
|
55
55
|
|
|
56
56
|
**使用:**
|
|
@@ -79,7 +79,7 @@ npm install -g kiro-spec-engine
|
|
|
79
79
|
kse adopt
|
|
80
80
|
|
|
81
81
|
# 创建 Spec
|
|
82
|
-
kse
|
|
82
|
+
kse spec bootstrap --name 01-00-my-feature --non-interactive
|
|
83
83
|
```
|
|
84
84
|
|
|
85
85
|
**使用:**
|
|
@@ -128,7 +128,7 @@ kse watch add --pattern ".kiro/specs/*/design.md" --action "kse context export {
|
|
|
128
128
|
### 步骤 1:创建 Spec
|
|
129
129
|
|
|
130
130
|
```bash
|
|
131
|
-
kse
|
|
131
|
+
kse spec bootstrap --name 01-00-my-feature --non-interactive
|
|
132
132
|
```
|
|
133
133
|
|
|
134
134
|
### 步骤 2:编写 Spec
|
|
@@ -292,7 +292,7 @@ kse prompt generate 01-00-my-feature 1.1
|
|
|
292
292
|
|
|
293
293
|
```bash
|
|
294
294
|
# 1. 创建并编写 Spec
|
|
295
|
-
kse
|
|
295
|
+
kse spec bootstrap --name 01-00-user-login --non-interactive
|
|
296
296
|
# 编辑 requirements.md、design.md、tasks.md
|
|
297
297
|
|
|
298
298
|
# 2. 导出上下文
|
|
@@ -315,7 +315,7 @@ cat .kiro/specs/01-00-user-login/context-export.md | pbcopy
|
|
|
315
315
|
|
|
316
316
|
```bash
|
|
317
317
|
# 1. 创建并编写 Spec
|
|
318
|
-
kse
|
|
318
|
+
kse spec bootstrap --name 01-00-user-login --non-interactive
|
|
319
319
|
|
|
320
320
|
# 2. 生成任务特定提示
|
|
321
321
|
kse prompt generate 01-00-user-login 1.1
|
|
@@ -334,7 +334,7 @@ kse prompt generate 01-00-user-login 1.1
|
|
|
334
334
|
|
|
335
335
|
```bash
|
|
336
336
|
# 1. 创建并编写 Spec
|
|
337
|
-
kse
|
|
337
|
+
kse spec bootstrap --name 01-00-user-login --non-interactive
|
|
338
338
|
|
|
339
339
|
# 2. 在 Windsurf 中告诉 AI:
|
|
340
340
|
"使用 kse 检查 01-00-user-login 的 spec 并实现任务 1.1"
|
|
@@ -352,7 +352,7 @@ kse create-spec 01-00-user-login
|
|
|
352
352
|
|
|
353
353
|
```bash
|
|
354
354
|
# 1. 创建并编写 Spec
|
|
355
|
-
kse
|
|
355
|
+
kse spec bootstrap --name 01-00-user-login --non-interactive
|
|
356
356
|
|
|
357
357
|
# 2. 创建实现文件
|
|
358
358
|
# src/auth/AuthController.js
|
|
@@ -496,3 +496,4 @@ cp -r .kiro/templates/api-feature-template .kiro/specs/02-00-new-feature
|
|
|
496
496
|
|
|
497
497
|
**版本**: 1.42.0
|
|
498
498
|
**最后更新**: 2026-02-11
|
|
499
|
+
|
|
@@ -211,7 +211,7 @@
|
|
|
211
211
|
|
|
212
212
|
```bash
|
|
213
213
|
# 1. 创建 Spec
|
|
214
|
-
kse
|
|
214
|
+
kse spec bootstrap --name 01-00-user-login --non-interactive
|
|
215
215
|
|
|
216
216
|
# 2. 在 VS Code 中编写 Spec
|
|
217
217
|
# - 打开 requirements.md
|
|
@@ -446,3 +446,4 @@ Copilot 使用所有打开文件的上下文。
|
|
|
446
446
|
|
|
447
447
|
**版本**: 1.42.0
|
|
448
448
|
**最后更新**: 2026-02-11
|
|
449
|
+
|
|
@@ -150,7 +150,7 @@ kse watch start
|
|
|
150
150
|
|
|
151
151
|
```bash
|
|
152
152
|
# 1. 创建 Spec
|
|
153
|
-
kse
|
|
153
|
+
kse spec bootstrap --name 01-00-user-login --non-interactive
|
|
154
154
|
|
|
155
155
|
# 2. 编写 requirements.md、design.md、tasks.md
|
|
156
156
|
|
|
@@ -375,3 +375,4 @@ kse watch stop
|
|
|
375
375
|
|
|
376
376
|
**版本**: 1.42.0
|
|
377
377
|
**最后更新**: 2026-02-11
|
|
378
|
+
|
|
@@ -14,6 +14,108 @@ const fs = require('fs-extra');
|
|
|
14
14
|
const SPECS_DIR = '.kiro/specs';
|
|
15
15
|
const STATUS_FILE = '.kiro/config/orchestration-status.json';
|
|
16
16
|
|
|
17
|
+
/**
|
|
18
|
+
* Run orchestration programmatically.
|
|
19
|
+
*
|
|
20
|
+
* @param {object} options
|
|
21
|
+
* @param {string} [options.specs] - Comma separated spec names
|
|
22
|
+
* @param {string[]} [options.specNames] - Explicit spec names array
|
|
23
|
+
* @param {number} [options.maxParallel]
|
|
24
|
+
* @param {boolean} [options.json]
|
|
25
|
+
* @param {boolean} [options.silent]
|
|
26
|
+
* @param {object} dependencies
|
|
27
|
+
* @param {string} [dependencies.workspaceRoot]
|
|
28
|
+
* @returns {Promise<object>}
|
|
29
|
+
*/
|
|
30
|
+
async function runOrchestration(options = {}, dependencies = {}) {
|
|
31
|
+
const workspaceRoot = dependencies.workspaceRoot || process.cwd();
|
|
32
|
+
const specNames = Array.isArray(options.specNames)
|
|
33
|
+
? options.specNames.map(name => `${name}`.trim()).filter(Boolean)
|
|
34
|
+
: _parseSpecNamesOption(options.specs);
|
|
35
|
+
|
|
36
|
+
if (specNames.length === 0) {
|
|
37
|
+
throw new Error('No specs specified');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const maxParallel = options.maxParallel;
|
|
41
|
+
if (maxParallel !== undefined && (isNaN(maxParallel) || maxParallel < 1)) {
|
|
42
|
+
throw new Error('--max-parallel must be >= 1');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const missing = await _validateSpecs(workspaceRoot, specNames);
|
|
46
|
+
if (missing.length > 0) {
|
|
47
|
+
throw new Error(`Specs not found: ${missing.join(', ')}`);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const { OrchestratorConfig } = require('../orchestrator/orchestrator-config');
|
|
51
|
+
const { BootstrapPromptBuilder } = require('../orchestrator/bootstrap-prompt-builder');
|
|
52
|
+
const { AgentSpawner } = require('../orchestrator/agent-spawner');
|
|
53
|
+
const { StatusMonitor } = require('../orchestrator/status-monitor');
|
|
54
|
+
const { OrchestrationEngine } = require('../orchestrator/orchestration-engine');
|
|
55
|
+
|
|
56
|
+
const DependencyManager = require('../collab/dependency-manager');
|
|
57
|
+
const MetadataManager = require('../collab/metadata-manager');
|
|
58
|
+
const { AgentRegistry } = require('../collab/agent-registry');
|
|
59
|
+
const { SpecLifecycleManager } = require('../collab/spec-lifecycle-manager');
|
|
60
|
+
const { MachineIdentifier } = require('../lock/machine-identifier');
|
|
61
|
+
|
|
62
|
+
let contextSyncManager = null;
|
|
63
|
+
try {
|
|
64
|
+
const { ContextSyncManager } = require('../steering/context-sync-manager');
|
|
65
|
+
contextSyncManager = new ContextSyncManager(workspaceRoot);
|
|
66
|
+
} catch (_err) {
|
|
67
|
+
// Non-fatal — status sync will be skipped
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const orchestratorConfig = new OrchestratorConfig(workspaceRoot);
|
|
71
|
+
const config = await orchestratorConfig.getConfig();
|
|
72
|
+
const effectiveMaxParallel = maxParallel || config.maxParallel;
|
|
73
|
+
|
|
74
|
+
const bootstrapPromptBuilder = new BootstrapPromptBuilder(workspaceRoot, orchestratorConfig);
|
|
75
|
+
const machineIdentifier = new MachineIdentifier(
|
|
76
|
+
path.join(workspaceRoot, '.kiro', 'config')
|
|
77
|
+
);
|
|
78
|
+
const agentRegistry = new AgentRegistry(workspaceRoot, machineIdentifier);
|
|
79
|
+
const agentSpawner = new AgentSpawner(
|
|
80
|
+
workspaceRoot, orchestratorConfig, agentRegistry, bootstrapPromptBuilder
|
|
81
|
+
);
|
|
82
|
+
const metadataManager = new MetadataManager(workspaceRoot);
|
|
83
|
+
const dependencyManager = new DependencyManager(metadataManager);
|
|
84
|
+
const specLifecycleManager = new SpecLifecycleManager(
|
|
85
|
+
workspaceRoot, contextSyncManager, agentRegistry
|
|
86
|
+
);
|
|
87
|
+
const statusMonitor = new StatusMonitor(specLifecycleManager, contextSyncManager);
|
|
88
|
+
|
|
89
|
+
const engine = new OrchestrationEngine(workspaceRoot, {
|
|
90
|
+
agentSpawner,
|
|
91
|
+
dependencyManager,
|
|
92
|
+
specLifecycleManager,
|
|
93
|
+
statusMonitor,
|
|
94
|
+
orchestratorConfig,
|
|
95
|
+
agentRegistry,
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
if (!options.silent && !options.json) {
|
|
99
|
+
console.log(
|
|
100
|
+
chalk.blue('🚀'),
|
|
101
|
+
`Starting orchestration for ${specNames.length} spec(s) (max-parallel: ${effectiveMaxParallel})...`
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const result = await engine.start(specNames, { maxParallel: effectiveMaxParallel });
|
|
106
|
+
await _writeStatus(workspaceRoot, engine.getStatus());
|
|
107
|
+
|
|
108
|
+
if (!options.silent) {
|
|
109
|
+
if (options.json) {
|
|
110
|
+
console.log(JSON.stringify(result, null, 2));
|
|
111
|
+
} else {
|
|
112
|
+
_printResult(result);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return result;
|
|
117
|
+
}
|
|
118
|
+
|
|
17
119
|
/**
|
|
18
120
|
* Register orchestrate commands on the given Commander program.
|
|
19
121
|
* @param {import('commander').Command} program
|
|
@@ -32,100 +134,7 @@ function registerOrchestrateCommands(program) {
|
|
|
32
134
|
.option('--json', 'Output in JSON format')
|
|
33
135
|
.action(async (options) => {
|
|
34
136
|
try {
|
|
35
|
-
const
|
|
36
|
-
const specNames = options.specs
|
|
37
|
-
.split(',')
|
|
38
|
-
.map(s => s.trim())
|
|
39
|
-
.filter(Boolean);
|
|
40
|
-
|
|
41
|
-
if (specNames.length === 0) {
|
|
42
|
-
_errorAndExit('No specs specified', options.json);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// Validate maxParallel
|
|
46
|
-
const maxParallel = options.maxParallel;
|
|
47
|
-
if (maxParallel !== undefined && (isNaN(maxParallel) || maxParallel < 1)) {
|
|
48
|
-
_errorAndExit('--max-parallel must be >= 1', options.json);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// Validate spec existence
|
|
52
|
-
const missing = await _validateSpecs(workspaceRoot, specNames);
|
|
53
|
-
if (missing.length > 0) {
|
|
54
|
-
_errorAndExit(
|
|
55
|
-
`Specs not found: ${missing.join(', ')}`,
|
|
56
|
-
options.json
|
|
57
|
-
);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// Lazy-require orchestrator modules
|
|
61
|
-
const { OrchestratorConfig } = require('../orchestrator/orchestrator-config');
|
|
62
|
-
const { BootstrapPromptBuilder } = require('../orchestrator/bootstrap-prompt-builder');
|
|
63
|
-
const { AgentSpawner } = require('../orchestrator/agent-spawner');
|
|
64
|
-
const { StatusMonitor } = require('../orchestrator/status-monitor');
|
|
65
|
-
const { OrchestrationEngine } = require('../orchestrator/orchestration-engine');
|
|
66
|
-
|
|
67
|
-
// Lazy-require collab modules
|
|
68
|
-
const DependencyManager = require('../collab/dependency-manager');
|
|
69
|
-
const MetadataManager = require('../collab/metadata-manager');
|
|
70
|
-
const { AgentRegistry } = require('../collab/agent-registry');
|
|
71
|
-
const { SpecLifecycleManager } = require('../collab/spec-lifecycle-manager');
|
|
72
|
-
const { MachineIdentifier } = require('../lock/machine-identifier');
|
|
73
|
-
|
|
74
|
-
// Optional: ContextSyncManager
|
|
75
|
-
let contextSyncManager = null;
|
|
76
|
-
try {
|
|
77
|
-
const { ContextSyncManager } = require('../steering/context-sync-manager');
|
|
78
|
-
contextSyncManager = new ContextSyncManager(workspaceRoot);
|
|
79
|
-
} catch (_err) {
|
|
80
|
-
// Non-fatal — status sync will be skipped
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// Wire dependencies
|
|
84
|
-
const orchestratorConfig = new OrchestratorConfig(workspaceRoot);
|
|
85
|
-
const config = await orchestratorConfig.getConfig();
|
|
86
|
-
const effectiveMaxParallel = maxParallel || config.maxParallel;
|
|
87
|
-
|
|
88
|
-
const bootstrapPromptBuilder = new BootstrapPromptBuilder(workspaceRoot, orchestratorConfig);
|
|
89
|
-
const machineIdentifier = new MachineIdentifier(
|
|
90
|
-
path.join(workspaceRoot, '.kiro', 'config')
|
|
91
|
-
);
|
|
92
|
-
const agentRegistry = new AgentRegistry(workspaceRoot, machineIdentifier);
|
|
93
|
-
const agentSpawner = new AgentSpawner(
|
|
94
|
-
workspaceRoot, orchestratorConfig, agentRegistry, bootstrapPromptBuilder
|
|
95
|
-
);
|
|
96
|
-
const metadataManager = new MetadataManager(workspaceRoot);
|
|
97
|
-
const dependencyManager = new DependencyManager(metadataManager);
|
|
98
|
-
const specLifecycleManager = new SpecLifecycleManager(
|
|
99
|
-
workspaceRoot, contextSyncManager, agentRegistry
|
|
100
|
-
);
|
|
101
|
-
const statusMonitor = new StatusMonitor(specLifecycleManager, contextSyncManager);
|
|
102
|
-
|
|
103
|
-
const engine = new OrchestrationEngine(workspaceRoot, {
|
|
104
|
-
agentSpawner,
|
|
105
|
-
dependencyManager,
|
|
106
|
-
specLifecycleManager,
|
|
107
|
-
statusMonitor,
|
|
108
|
-
orchestratorConfig,
|
|
109
|
-
agentRegistry,
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
if (!options.json) {
|
|
113
|
-
console.log(
|
|
114
|
-
chalk.blue('🚀'),
|
|
115
|
-
`Starting orchestration for ${specNames.length} spec(s) (max-parallel: ${effectiveMaxParallel})...`
|
|
116
|
-
);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
const result = await engine.start(specNames, { maxParallel: effectiveMaxParallel });
|
|
120
|
-
|
|
121
|
-
// Persist status for the `status` subcommand
|
|
122
|
-
await _writeStatus(workspaceRoot, engine.getStatus());
|
|
123
|
-
|
|
124
|
-
if (options.json) {
|
|
125
|
-
console.log(JSON.stringify(result, null, 2));
|
|
126
|
-
} else {
|
|
127
|
-
_printResult(result);
|
|
128
|
-
}
|
|
137
|
+
const result = await runOrchestration(options);
|
|
129
138
|
|
|
130
139
|
if (result.status === 'failed') {
|
|
131
140
|
process.exit(1);
|
|
@@ -312,4 +321,23 @@ function _errorAndExit(message, json) {
|
|
|
312
321
|
process.exit(1);
|
|
313
322
|
}
|
|
314
323
|
|
|
315
|
-
|
|
324
|
+
/**
|
|
325
|
+
* Parse a comma-separated specs option.
|
|
326
|
+
* @param {string} specsOption
|
|
327
|
+
* @returns {string[]}
|
|
328
|
+
*/
|
|
329
|
+
function _parseSpecNamesOption(specsOption) {
|
|
330
|
+
if (!specsOption || typeof specsOption !== 'string') {
|
|
331
|
+
return [];
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
return specsOption
|
|
335
|
+
.split(',')
|
|
336
|
+
.map(item => item.trim())
|
|
337
|
+
.filter(Boolean);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
module.exports = {
|
|
341
|
+
registerOrchestrateCommands,
|
|
342
|
+
runOrchestration,
|
|
343
|
+
};
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const chalk = require('chalk');
|
|
4
|
+
const { runOrchestration } = require('./orchestrate');
|
|
5
|
+
const {
|
|
6
|
+
parseSpecTargets,
|
|
7
|
+
runMultiSpecViaOrchestrate
|
|
8
|
+
} = require('../spec/multi-spec-orchestrate');
|
|
9
|
+
|
|
10
|
+
const { ContextCollector } = require('../spec/bootstrap/context-collector');
|
|
11
|
+
const { QuestionnaireEngine } = require('../spec/bootstrap/questionnaire-engine');
|
|
12
|
+
const { DraftGenerator } = require('../spec/bootstrap/draft-generator');
|
|
13
|
+
const { TraceEmitter } = require('../spec/bootstrap/trace-emitter');
|
|
14
|
+
|
|
15
|
+
async function runSpecBootstrap(options = {}, dependencies = {}) {
|
|
16
|
+
const projectPath = dependencies.projectPath || process.cwd();
|
|
17
|
+
|
|
18
|
+
const specTargets = parseSpecTargets({
|
|
19
|
+
spec: options.spec || options.name,
|
|
20
|
+
specs: options.specs
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
if (specTargets.length > 1) {
|
|
24
|
+
const executeOrchestration = dependencies.runOrchestration || runOrchestration;
|
|
25
|
+
return runMultiSpecViaOrchestrate({
|
|
26
|
+
specTargets,
|
|
27
|
+
projectPath,
|
|
28
|
+
commandOptions: options,
|
|
29
|
+
runOrchestration: executeOrchestration,
|
|
30
|
+
commandLabel: 'Multi-spec bootstrap',
|
|
31
|
+
nextActionLabel: 'Multi-spec bootstrap defaulted to orchestrate mode.'
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const contextCollector = dependencies.contextCollector || new ContextCollector(projectPath);
|
|
36
|
+
const questionnaireEngine = dependencies.questionnaireEngine || new QuestionnaireEngine({
|
|
37
|
+
prompt: dependencies.prompt,
|
|
38
|
+
maxQuestions: dependencies.maxQuestions
|
|
39
|
+
});
|
|
40
|
+
const draftGenerator = dependencies.draftGenerator || new DraftGenerator();
|
|
41
|
+
const traceEmitter = dependencies.traceEmitter || new TraceEmitter();
|
|
42
|
+
|
|
43
|
+
if (options.nonInteractive && specTargets.length === 0) {
|
|
44
|
+
throw new Error('--name is required in non-interactive mode');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const context = await contextCollector.collect();
|
|
48
|
+
const answers = await questionnaireEngine.collect({
|
|
49
|
+
nonInteractive: options.nonInteractive,
|
|
50
|
+
specName: specTargets[0] || options.name,
|
|
51
|
+
profile: options.profile,
|
|
52
|
+
template: options.template
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
const specName = (specTargets[0] || options.name || answers.specName || '').trim();
|
|
56
|
+
if (!specName) {
|
|
57
|
+
throw new Error('Spec name is required');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const draft = draftGenerator.generate({
|
|
61
|
+
specName,
|
|
62
|
+
profile: options.profile || 'general',
|
|
63
|
+
template: options.template || 'default',
|
|
64
|
+
context,
|
|
65
|
+
answers
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
const specPath = path.join(projectPath, '.kiro', 'specs', specName);
|
|
69
|
+
const files = {
|
|
70
|
+
requirements: path.join(specPath, 'requirements.md'),
|
|
71
|
+
design: path.join(specPath, 'design.md'),
|
|
72
|
+
tasks: path.join(specPath, 'tasks.md')
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
if (!options.dryRun) {
|
|
76
|
+
await fs.ensureDir(specPath);
|
|
77
|
+
await fs.writeFile(files.requirements, draft.requirements, 'utf8');
|
|
78
|
+
await fs.writeFile(files.design, draft.design, 'utf8');
|
|
79
|
+
await fs.writeFile(files.tasks, draft.tasks, 'utf8');
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const result = {
|
|
83
|
+
success: true,
|
|
84
|
+
specName,
|
|
85
|
+
specPath: path.relative(projectPath, specPath),
|
|
86
|
+
dryRun: !!options.dryRun,
|
|
87
|
+
files: {
|
|
88
|
+
requirements: path.relative(projectPath, files.requirements),
|
|
89
|
+
design: path.relative(projectPath, files.design),
|
|
90
|
+
tasks: path.relative(projectPath, files.tasks)
|
|
91
|
+
},
|
|
92
|
+
trace: {
|
|
93
|
+
template: options.template || 'default',
|
|
94
|
+
profile: options.profile || 'general',
|
|
95
|
+
parameters: {
|
|
96
|
+
nonInteractive: !!options.nonInteractive,
|
|
97
|
+
dryRun: !!options.dryRun,
|
|
98
|
+
json: !!options.json
|
|
99
|
+
},
|
|
100
|
+
context: {
|
|
101
|
+
totalSpecs: context.totalSpecs,
|
|
102
|
+
preferredLanguage: context.preferredLanguage
|
|
103
|
+
},
|
|
104
|
+
mapping: draft.metadata.mapping
|
|
105
|
+
},
|
|
106
|
+
preview: {
|
|
107
|
+
requirements: draft.requirements,
|
|
108
|
+
design: draft.design,
|
|
109
|
+
tasks: draft.tasks
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
traceEmitter.emit(result, { json: options.json });
|
|
114
|
+
return result;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function registerSpecBootstrapCommand(program) {
|
|
118
|
+
program
|
|
119
|
+
.command('spec-bootstrap')
|
|
120
|
+
.description('Bootstrap requirements/design/tasks draft (use: kse spec bootstrap)')
|
|
121
|
+
.option('--name <spec-name>', 'Spec name to generate')
|
|
122
|
+
.option('--spec <name>', 'Alias of --name')
|
|
123
|
+
.option('--specs <names>', 'Comma-separated Spec identifiers (multi-spec defaults to orchestrate mode)')
|
|
124
|
+
.option('--template <template-id>', 'Template hint for draft generation')
|
|
125
|
+
.option('--profile <profile-id>', 'Profile for default generation strategy')
|
|
126
|
+
.option('--non-interactive', 'Disable prompts and use arguments/defaults only')
|
|
127
|
+
.option('--dry-run', 'Preview generation result without writing files')
|
|
128
|
+
.option('--json', 'Output machine-readable JSON')
|
|
129
|
+
.option('--max-parallel <n>', 'Maximum parallel agents when orchestrate mode is used', parseInt)
|
|
130
|
+
.action(async (options) => {
|
|
131
|
+
try {
|
|
132
|
+
await runSpecBootstrap(options);
|
|
133
|
+
} catch (error) {
|
|
134
|
+
if (options.json) {
|
|
135
|
+
console.log(JSON.stringify({ success: false, error: error.message }, null, 2));
|
|
136
|
+
} else {
|
|
137
|
+
console.error(chalk.red('❌ Spec bootstrap failed:'), error.message);
|
|
138
|
+
}
|
|
139
|
+
process.exit(1);
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
module.exports = {
|
|
145
|
+
registerSpecBootstrapCommand,
|
|
146
|
+
runSpecBootstrap
|
|
147
|
+
};
|