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.
Files changed (45) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +15 -4
  3. package/README.zh.md +15 -3
  4. package/bin/kiro-spec-engine.js +84 -0
  5. package/docs/adoption-guide.md +3 -2
  6. package/docs/command-reference.md +30 -12
  7. package/docs/cross-tool-guide.md +2 -1
  8. package/docs/document-governance.md +2 -1
  9. package/docs/faq.md +14 -13
  10. package/docs/manual-workflows-guide.md +2 -1
  11. package/docs/quick-start-with-ai-tools.md +4 -3
  12. package/docs/quick-start.md +21 -7
  13. package/docs/spec-workflow.md +3 -2
  14. package/docs/tools/claude-guide.md +3 -2
  15. package/docs/tools/cursor-guide.md +3 -2
  16. package/docs/tools/generic-guide.md +3 -2
  17. package/docs/tools/vscode-guide.md +3 -2
  18. package/docs/tools/windsurf-guide.md +3 -2
  19. package/docs/troubleshooting.md +10 -9
  20. package/docs/zh/quick-start.md +30 -14
  21. package/docs/zh/tools/claude-guide.md +2 -1
  22. package/docs/zh/tools/cursor-guide.md +2 -1
  23. package/docs/zh/tools/generic-guide.md +8 -7
  24. package/docs/zh/tools/vscode-guide.md +2 -1
  25. package/docs/zh/tools/windsurf-guide.md +2 -1
  26. package/lib/commands/orchestrate.js +123 -95
  27. package/lib/commands/spec-bootstrap.js +147 -0
  28. package/lib/commands/spec-gate.js +157 -0
  29. package/lib/commands/spec-pipeline.js +205 -0
  30. package/lib/spec/bootstrap/context-collector.js +48 -0
  31. package/lib/spec/bootstrap/draft-generator.js +158 -0
  32. package/lib/spec/bootstrap/questionnaire-engine.js +70 -0
  33. package/lib/spec/bootstrap/trace-emitter.js +59 -0
  34. package/lib/spec/multi-spec-orchestrate.js +93 -0
  35. package/lib/spec/pipeline/constants.js +6 -0
  36. package/lib/spec/pipeline/stage-adapters.js +118 -0
  37. package/lib/spec/pipeline/stage-runner.js +146 -0
  38. package/lib/spec/pipeline/state-store.js +119 -0
  39. package/lib/spec-gate/engine/gate-engine.js +165 -0
  40. package/lib/spec-gate/policy/default-policy.js +22 -0
  41. package/lib/spec-gate/policy/policy-loader.js +103 -0
  42. package/lib/spec-gate/result-emitter.js +81 -0
  43. package/lib/spec-gate/rules/default-rules.js +156 -0
  44. package/lib/spec-gate/rules/rule-registry.js +51 -0
  45. package/package.json +2 -1
@@ -249,7 +249,7 @@ No Specs found in .kiro/specs/
249
249
 
250
250
  **Create your first Spec:**
251
251
  ```bash
252
- kse create-spec 01-00-my-feature
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 create-spec my-feature
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 create-spec 01-00-user-login
279
- kse create-spec 02-01-fix-auth-bug
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 create-spec user-login
283
- kse create-spec 01-user-login
284
- kse create-spec UserLogin
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 create-spec 01-00-user-login
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 create-spec 01-00-用户登录
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
+
@@ -4,8 +4,8 @@
4
4
 
5
5
  ---
6
6
 
7
- **版本**: 1.42.0
8
- **最后更新**: 2026-02-11
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 create-spec 01-00-my-feature
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:创建你的第一个 Spec2 分钟)
133
+ ## 步骤 3:生成你的第一个 Spec 初稿(2 分钟)
134
134
 
135
- 让我们为用户登录功能创建一个 Spec
135
+ 让我们为用户登录功能生成一个 Spec 初稿:
136
136
 
137
137
  ```bash
138
- kse create-spec 01-00-user-login
138
+ kse spec bootstrap --name 01-00-user-login --non-interactive
139
139
  ```
140
140
 
141
141
  **预期输出:**
142
142
  ```
143
- 创建 spec:01-00-user-login
144
- 创建 requirements.md
145
- 创建 design.md
146
- 创建 tasks.md
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
- 创建 Spec → 导出上下文 → AI 实现 → 更新任务 → 重复
714
+ bootstrap Spec → pipeline/gate → 导出上下文 → AI 实现 → 更新任务 → 重复
700
715
  ```
701
716
 
702
717
  **准备好构建你的下一个功能了吗?** 🚀
703
718
 
704
719
  ```bash
705
- kse create-spec 02-00-your-next-feature
720
+ kse spec bootstrap --name 02-00-your-next-feature --non-interactive
706
721
  ```
707
722
 
708
723
  ---
709
724
 
710
- **版本**: 1.42.0
711
- **最后更新**: 2026-02-11
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 create-spec 01-00-user-login
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 create-spec 01-00-user-login
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 create-spec 01-00-my-feature
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 create-spec 01-00-my-feature
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 create-spec 01-00-my-feature
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 create-spec 01-00-user-login
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 create-spec 01-00-user-login
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 create-spec 01-00-user-login
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 create-spec 01-00-user-login
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 create-spec 01-00-user-login
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 create-spec 01-00-user-login
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 workspaceRoot = process.cwd();
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
- module.exports = { registerOrchestrateCommands };
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
+ };