kiro-spec-engine 1.44.0 → 1.45.2

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 CHANGED
@@ -7,6 +7,38 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [1.45.2] - 2026-02-13
11
+
12
+ ### Fixed
13
+ - **AgentSpawner auth fallback**: Added `~/.codex/auth.json` fallback when `CODEX_API_KEY` env var is not set, supporting users who configured auth via `codex auth`
14
+ - **AgentSpawner codex command**: Added `codexCommand` config option (e.g. `"npx @openai/codex"`) for users without global Codex CLI install
15
+ - **OrchestratorConfig**: Added `codexCommand` to known config keys and defaults
16
+
17
+ ## [1.45.1] - 2026-02-12
18
+
19
+ ### Fixed
20
+ - **StatusMonitor property test**: Replaced `fc.date()` with `fc.integer`-based timestamp generator to prevent `Invalid Date` during fast-check shrinking
21
+ - **ExecutionLogger rotation test**: Replaced `Array(200000).fill() + JSON.stringify` with string repeat for large file generation, fixing CI timeout (10s → 45ms)
22
+
23
+ ## [1.45.0] - 2026-02-12
24
+
25
+ ### Added
26
+ - **Agent Orchestrator**: Multi-agent parallel Spec execution via Codex CLI
27
+ - **OrchestratorConfig** (`lib/orchestrator/orchestrator-config.js`): Configuration management for orchestrator settings (agent backend, parallelism, timeout, retries)
28
+ - **BootstrapPromptBuilder** (`lib/orchestrator/bootstrap-prompt-builder.js`): Builds bootstrap prompts with Spec path, steering context, and execution instructions for sub-agents
29
+ - **AgentSpawner** (`lib/orchestrator/agent-spawner.js`): Process manager for Codex CLI sub-agents with timeout detection, graceful termination (SIGTERM → SIGKILL), and event emission
30
+ - **StatusMonitor** (`lib/orchestrator/status-monitor.js`): Codex JSON Lines event parsing, per-Spec status tracking, orchestration-level status aggregation
31
+ - **OrchestrationEngine** (`lib/orchestrator/orchestration-engine.js`): Core engine with DAG-based dependency analysis, batch scheduling, parallel execution (≤ maxParallel), failure propagation, and retry mechanism
32
+ - **CLI Commands** (`kse orchestrate`):
33
+ - `kse orchestrate run --specs "spec-a,spec-b" --max-parallel 3` — Start multi-agent orchestration
34
+ - `kse orchestrate status` — View orchestration progress
35
+ - `kse orchestrate stop` — Gracefully stop all sub-agents
36
+ - 11 correctness properties verified via property-based testing (fast-check)
37
+ - 236+ new tests across unit, property, and integration test suites
38
+
39
+ ### Fixed
40
+ - **StatusMonitor property test**: Fixed `fc.date()` generating invalid dates causing `RangeError: Invalid time value` in `toISOString()` — constrained date range to 2000-2100
41
+
10
42
  ## [1.44.0] - 2026-02-12
11
43
 
12
44
  ### Added
package/README.md CHANGED
@@ -332,6 +332,28 @@ Structure your work with Requirements → Design → Tasks workflow
332
332
 
333
333
  [Learn more about Multi-Agent Coordination →](docs/multi-agent-coordination-guide.md)
334
334
 
335
+ ### Agent Orchestrator 🚀 NEW in v1.45.0
336
+ - **Automated Multi-Agent Spec Execution**: Replace manual multi-terminal workflows with a single command
337
+ - **DAG-Based Scheduling**: Analyze Spec dependencies, compute execution batches via topological sort
338
+ - **Parallel Execution**: Run up to N Specs simultaneously via Codex CLI sub-agents (`--max-parallel`)
339
+ - **Failure Propagation**: Failed Spec's dependents automatically marked as skipped
340
+ - **Retry Mechanism**: Configurable automatic retry for failed Specs
341
+ - **Real-Time Monitoring**: Track per-Spec status and overall orchestration progress
342
+ - **Graceful Termination**: Stop all sub-agents cleanly (SIGTERM → SIGKILL)
343
+ - **Configurable**: Agent backend, parallelism, timeout, retries via `.kiro/config/orchestrator.json`
344
+
345
+ **Quick Start**:
346
+ ```bash
347
+ # Run 3 Specs in parallel via Codex CLI
348
+ kse orchestrate run --specs "spec-a,spec-b,spec-c" --max-parallel 3
349
+
350
+ # Check orchestration progress
351
+ kse orchestrate status
352
+
353
+ # Stop all sub-agents
354
+ kse orchestrate stop
355
+ ```
356
+
335
357
  ### Spec-Level Steering & Context Sync 🚀 NEW in v1.44.0
336
358
  - **Spec Steering (L4)**: Independent `steering.md` per Spec with constraints, notes, and decisions — zero cross-agent conflict
337
359
  - **Steering Loader**: Unified L1-L4 four-layer steering loader with priority-based merging
@@ -500,6 +522,11 @@ kse scene ontology lineage --ref <ref> # Show data lineage
500
522
  kse scene ontology agent-info --package <path> # Show agent hints
501
523
  kse scene contribute --package <path> # One-stop validate → lint → score → publish
502
524
 
525
+ # Agent orchestration (NEW in v1.45.0)
526
+ kse orchestrate run --specs "<spec-list>" --max-parallel <N> # Start multi-agent orchestration
527
+ kse orchestrate status # View orchestration progress
528
+ kse orchestrate stop # Stop all sub-agents
529
+
503
530
  # DevOps operations
504
531
  kse ops init <project-name> # Initialize operations specs
505
532
  kse ops validate [<project>] # Validate operations completeness
@@ -623,5 +650,5 @@ A deep conversation about AI development trends, Neo-Confucian philosophy, and s
623
650
 
624
651
  ---
625
652
 
626
- **Version**: 1.43.0
627
- **Last Updated**: 2026-02-11
653
+ **Version**: 1.45.0
654
+ **Last Updated**: 2026-02-12
package/README.zh.md CHANGED
@@ -288,6 +288,28 @@ sequenceDiagram
288
288
 
289
289
  [了解更多多 Agent 协调 →](docs/multi-agent-coordination-guide.md)
290
290
 
291
+ ### Agent 编排器 🚀 v1.45.0 新增
292
+ - **自动化多 Agent Spec 执行**: 一条命令替代手工开多个终端分配 Spec 给 Codex Agent
293
+ - **DAG 依赖调度**: 分析 Spec 间依赖关系,拓扑排序计算执行批次
294
+ - **并行执行**: 通过 Codex CLI 子进程同时运行多个 Spec(`--max-parallel` 控制并行度)
295
+ - **失败传播**: 失败 Spec 的下游依赖自动标记为 skipped
296
+ - **重试机制**: 可配置的失败自动重试
297
+ - **实时监控**: 跟踪每个 Spec 状态和整体编排进度
298
+ - **优雅终止**: 干净停止所有子 Agent(SIGTERM → SIGKILL)
299
+ - **可配置**: 通过 `.kiro/config/orchestrator.json` 配置 Agent 后端、并行度、超时、重试次数
300
+
301
+ **快速开始**:
302
+ ```bash
303
+ # 并行运行 3 个 Spec
304
+ kse orchestrate run --specs "spec-a,spec-b,spec-c" --max-parallel 3
305
+
306
+ # 查看编排进度
307
+ kse orchestrate status
308
+
309
+ # 停止所有子 Agent
310
+ kse orchestrate stop
311
+ ```
312
+
291
313
  ### Spec 级 Steering 与上下文同步 🚀 v1.44.0 新增
292
314
  - **Spec Steering (L4)**: 每个 Spec 独立的 `steering.md`,包含约束、注意事项、决策记录 — 跨 Agent 零冲突
293
315
  - **Steering 加载器**: 统一 L1-L4 四层 Steering 加载,优先级合并
@@ -403,6 +425,11 @@ kse scene ontology actions --ref <ref> # 显示 Action Abstraction
403
425
  kse scene ontology lineage --ref <ref> # 显示数据血缘
404
426
  kse scene ontology agent-info --package <path> # 显示 Agent Hints
405
427
 
428
+ # Agent 编排 (v1.45.0 新增)
429
+ kse orchestrate run --specs "<spec列表>" --max-parallel <N> # 启动多 Agent 编排
430
+ kse orchestrate status # 查看编排进度
431
+ kse orchestrate stop # 停止所有子 Agent
432
+
406
433
  # DevOps 运维
407
434
  kse ops init <project-name> # 初始化运维 specs
408
435
  kse ops validate [<project>] # 验证运维完整性
@@ -486,5 +513,5 @@ kse create-spec 01-00-my-first-feature
486
513
 
487
514
  ---
488
515
 
489
- **版本**:1.43.0
490
- **最后更新**:2026-02-11
516
+ **版本**:1.45.0
517
+ **最后更新**:2026-02-12
@@ -571,6 +571,10 @@ registerLockCommands(program);
571
571
  const { registerKnowledgeCommands } = require('../lib/commands/knowledge');
572
572
  registerKnowledgeCommands(program);
573
573
 
574
+ // Orchestration commands
575
+ const { registerOrchestrateCommands } = require('../lib/commands/orchestrate');
576
+ registerOrchestrateCommands(program);
577
+
574
578
  // Template management commands
575
579
  const templatesCommand = require('../lib/commands/templates');
576
580
 
@@ -0,0 +1,315 @@
1
+ /**
2
+ * CLI commands for Agent Orchestration
3
+ *
4
+ * Provides `kse orchestrate run|status|stop` subcommands for managing
5
+ * parallel Spec execution via Codex CLI sub-agents.
6
+ *
7
+ * Requirements: 6.1 (run), 6.2 (status), 6.3 (stop), 6.4 (spec validation), 6.5 (maxParallel validation)
8
+ */
9
+
10
+ const chalk = require('chalk');
11
+ const path = require('path');
12
+ const fs = require('fs-extra');
13
+
14
+ const SPECS_DIR = '.kiro/specs';
15
+ const STATUS_FILE = '.kiro/config/orchestration-status.json';
16
+
17
+ /**
18
+ * Register orchestrate commands on the given Commander program.
19
+ * @param {import('commander').Command} program
20
+ */
21
+ function registerOrchestrateCommands(program) {
22
+ const orchestrate = program
23
+ .command('orchestrate')
24
+ .description('Manage agent orchestration for parallel Spec execution');
25
+
26
+ // ── kse orchestrate run ──────────────────────────────────────────
27
+ orchestrate
28
+ .command('run')
29
+ .description('Start orchestration for specified Specs')
30
+ .requiredOption('--specs <specs>', 'Comma-separated list of Spec names')
31
+ .option('--max-parallel <n>', 'Maximum parallel agents', parseInt)
32
+ .option('--json', 'Output in JSON format')
33
+ .action(async (options) => {
34
+ 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
+ }
129
+
130
+ if (result.status === 'failed') {
131
+ process.exit(1);
132
+ }
133
+ } catch (err) {
134
+ _errorAndExit(err.message, options.json);
135
+ }
136
+ });
137
+
138
+ // ── kse orchestrate status ───────────────────────────────────────
139
+ orchestrate
140
+ .command('status')
141
+ .description('Show current orchestration status')
142
+ .option('--json', 'Output in JSON format')
143
+ .action(async (options) => {
144
+ try {
145
+ const workspaceRoot = process.cwd();
146
+ const status = await _readStatus(workspaceRoot);
147
+
148
+ if (!status) {
149
+ if (options.json) {
150
+ console.log(JSON.stringify({ status: 'idle', message: 'No orchestration data found' }));
151
+ } else {
152
+ console.log(chalk.gray('No orchestration data found. Run `kse orchestrate run` first.'));
153
+ }
154
+ return;
155
+ }
156
+
157
+ if (options.json) {
158
+ console.log(JSON.stringify(status, null, 2));
159
+ } else {
160
+ _printStatus(status);
161
+ }
162
+ } catch (err) {
163
+ _errorAndExit(err.message, options.json);
164
+ }
165
+ });
166
+
167
+ // ── kse orchestrate stop ─────────────────────────────────────────
168
+ orchestrate
169
+ .command('stop')
170
+ .description('Stop all running agents')
171
+ .action(async () => {
172
+ try {
173
+ const workspaceRoot = process.cwd();
174
+ const status = await _readStatus(workspaceRoot);
175
+
176
+ if (!status || status.status !== 'running') {
177
+ console.log(chalk.gray('No running orchestration to stop.'));
178
+ return;
179
+ }
180
+
181
+ // Write a stop signal into the status file
182
+ status.status = 'stopped';
183
+ status.completedAt = new Date().toISOString();
184
+ await _writeStatus(workspaceRoot, status);
185
+
186
+ console.log(chalk.yellow('⏹'), 'Stop signal sent. Running agents will be terminated.');
187
+ } catch (err) {
188
+ console.error(chalk.red('Error:'), err.message);
189
+ process.exit(1);
190
+ }
191
+ });
192
+ }
193
+
194
+ // ── Helpers ──────────────────────────────────────────────────────────
195
+
196
+ /**
197
+ * Validate that all spec directories exist.
198
+ * @param {string} workspaceRoot
199
+ * @param {string[]} specNames
200
+ * @returns {Promise<string[]>} Missing spec names
201
+ */
202
+ async function _validateSpecs(workspaceRoot, specNames) {
203
+ const missing = [];
204
+ for (const name of specNames) {
205
+ const specDir = path.join(workspaceRoot, SPECS_DIR, name);
206
+ const exists = await fs.pathExists(specDir);
207
+ if (!exists) {
208
+ missing.push(name);
209
+ }
210
+ }
211
+ return missing;
212
+ }
213
+
214
+ /**
215
+ * Read persisted orchestration status.
216
+ * @param {string} workspaceRoot
217
+ * @returns {Promise<object|null>}
218
+ */
219
+ async function _readStatus(workspaceRoot) {
220
+ const statusPath = path.join(workspaceRoot, STATUS_FILE);
221
+ try {
222
+ return await fs.readJson(statusPath);
223
+ } catch (_err) {
224
+ return null;
225
+ }
226
+ }
227
+
228
+ /**
229
+ * Persist orchestration status to disk.
230
+ * @param {string} workspaceRoot
231
+ * @param {object} status
232
+ */
233
+ async function _writeStatus(workspaceRoot, status) {
234
+ const statusPath = path.join(workspaceRoot, STATUS_FILE);
235
+ await fs.ensureDir(path.dirname(statusPath));
236
+ await fs.writeJson(statusPath, status, { spaces: 2 });
237
+ }
238
+
239
+ /**
240
+ * Print a human-readable orchestration result.
241
+ * @param {object} result
242
+ */
243
+ function _printResult(result) {
244
+ const icon = result.status === 'completed' ? chalk.green('✓') : chalk.red('✗');
245
+ console.log(`${icon} Orchestration ${result.status}`);
246
+ if (result.totalSpecs !== undefined) {
247
+ console.log(chalk.gray(` Total: ${result.totalSpecs} Completed: ${result.completedSpecs} Failed: ${result.failedSpecs}`));
248
+ }
249
+ if (result.specs) {
250
+ for (const [name, info] of Object.entries(result.specs)) {
251
+ const sym = _statusSymbol(info.status);
252
+ const extra = info.error ? chalk.gray(` — ${info.error}`) : '';
253
+ console.log(` ${sym} ${name}${extra}`);
254
+ }
255
+ }
256
+ }
257
+
258
+ /**
259
+ * Print a human-readable orchestration status.
260
+ * @param {object} status
261
+ */
262
+ function _printStatus(status) {
263
+ console.log(chalk.bold('Orchestration Status'));
264
+ console.log(chalk.gray('===================='));
265
+ console.log(`Status: ${_statusSymbol(status.status)} ${status.status}`);
266
+ if (status.totalSpecs !== undefined) {
267
+ console.log(`Total: ${status.totalSpecs} Completed: ${status.completedSpecs || 0} Failed: ${status.failedSpecs || 0} Running: ${status.runningSpecs || 0}`);
268
+ }
269
+ if (status.currentBatch !== undefined && status.totalBatches !== undefined) {
270
+ console.log(`Batch: ${status.currentBatch} / ${status.totalBatches}`);
271
+ }
272
+ if (status.specs) {
273
+ console.log('');
274
+ for (const [name, info] of Object.entries(status.specs)) {
275
+ const sym = _statusSymbol(info.status);
276
+ const extra = info.error ? chalk.gray(` — ${info.error}`) : '';
277
+ console.log(` ${sym} ${name}${extra}`);
278
+ }
279
+ }
280
+ }
281
+
282
+ /**
283
+ * Map status string to a coloured symbol.
284
+ * @param {string} status
285
+ * @returns {string}
286
+ */
287
+ function _statusSymbol(status) {
288
+ const map = {
289
+ completed: chalk.green('✓'),
290
+ running: chalk.yellow('⧗'),
291
+ pending: chalk.gray('○'),
292
+ failed: chalk.red('✗'),
293
+ timeout: chalk.red('⏱'),
294
+ skipped: chalk.gray('⊘'),
295
+ idle: chalk.gray('○'),
296
+ stopped: chalk.yellow('⏹'),
297
+ };
298
+ return map[status] || '?';
299
+ }
300
+
301
+ /**
302
+ * Print an error and exit. Respects --json mode.
303
+ * @param {string} message
304
+ * @param {boolean} [json]
305
+ */
306
+ function _errorAndExit(message, json) {
307
+ if (json) {
308
+ console.log(JSON.stringify({ error: message }));
309
+ } else {
310
+ console.error(chalk.red('Error:'), message);
311
+ }
312
+ process.exit(1);
313
+ }
314
+
315
+ module.exports = { registerOrchestrateCommands };