kiro-spec-engine 1.47.6 → 1.47.7
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.
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# 331-poc 适配持续推进路线图(KSE 侧)
|
|
2
|
+
|
|
3
|
+
> 范围:`kiro-spec-engine` 侧围绕 331-poc handoff 的持续适配工作,不包含 331 业务实现本身。
|
|
4
|
+
|
|
5
|
+
## 已完成(本轮)
|
|
6
|
+
|
|
7
|
+
1. 新增 handoff 自动化命令:
|
|
8
|
+
- `kse auto handoff plan`
|
|
9
|
+
- `kse auto handoff queue`
|
|
10
|
+
2. 将 handoff manifest 解析为可执行阶段计划(precheck/spec validation/execution/observability)。
|
|
11
|
+
3. 将 handoff manifest 生成 close-loop-batch 目标队列(支持 dry-run、append、known-gaps 开关)。
|
|
12
|
+
4. 补齐单测覆盖(plan/queue/dry-run 分支)。
|
|
13
|
+
5. 更新命令参考与中英文文档入口。
|
|
14
|
+
|
|
15
|
+
## 下一阶段(P1)
|
|
16
|
+
|
|
17
|
+
1. 新增 `kse auto handoff run`:
|
|
18
|
+
- 一条命令串行执行 `plan -> queue -> close-loop-batch -> observability`。
|
|
19
|
+
- 支持 `--dry-run` 与失败自动中断。
|
|
20
|
+
2. 新增 handoff 结果归档:
|
|
21
|
+
- 输出 `.kiro/reports/handoff-runs/<session>.json`。
|
|
22
|
+
- 汇总每个 spec 的校验状态与阻塞项。
|
|
23
|
+
3. 增加 handoff 门禁策略:
|
|
24
|
+
- `--min-spec-success-rate`
|
|
25
|
+
- `--max-risk-level`
|
|
26
|
+
- `--require-ontology-validation`
|
|
27
|
+
|
|
28
|
+
## 中期增强(P2)
|
|
29
|
+
|
|
30
|
+
1. 将 handoff 批次映射为主从 agent 编排输入:
|
|
31
|
+
- 根据 spec 依赖自动分批并发。
|
|
32
|
+
2. 引入 template 差异检测:
|
|
33
|
+
- 对比 handoff 模板与本地模板库差异。
|
|
34
|
+
3. 增加跨轮次回归分析:
|
|
35
|
+
- 对比本轮与上轮 handoff 的质量、风险与收敛速度。
|
|
36
|
+
|
|
37
|
+
## 长期目标(P3)
|
|
38
|
+
|
|
39
|
+
1. 抽象成通用“外部项目 handoff 适配框架”(不只 331-poc)。
|
|
40
|
+
2. 与 release evidence 合并,形成可发布的治理报表。
|
|
41
|
+
3. 将 handoff 质量指标纳入 `kse auto governance close-loop` 的默认评估维度。
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# 331-poc 双轨协同对接手册
|
|
2
|
+
|
|
3
|
+
> 目的:在 `kiro-spec-engine` 侧承接 331-poc 的深度补全成果,完成模板接入、ontology 校验、主从编排与闭环验收。
|
|
4
|
+
|
|
5
|
+
## 1. 输入契约(来自 331-poc)
|
|
6
|
+
|
|
7
|
+
每一轮从 `E:\workspace\331-poc` 接收以下产物:
|
|
8
|
+
|
|
9
|
+
1. 完成态 Spec(requirements/design/tasks + `custom/scene.yaml` + `custom/scene-package.json`)。
|
|
10
|
+
2. 模板导出目录:`.kiro/templates/exports/<template-name>/`。
|
|
11
|
+
3. 交接包:`docs/handoffs/handoff-manifest.json` + 证据文档。
|
|
12
|
+
|
|
13
|
+
若输入不满足以上三类,KSE 侧不进入接入批次。
|
|
14
|
+
|
|
15
|
+
## 2. KSE 侧接入流程
|
|
16
|
+
|
|
17
|
+
## 2.0 一键化入口(推荐)
|
|
18
|
+
|
|
19
|
+
先基于 handoff manifest 生成计划和队列,再执行批次:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npx kse auto handoff plan --manifest ../331-poc/docs/handoffs/handoff-manifest.json --out .kiro/reports/handoff-plan.json --json
|
|
23
|
+
npx kse auto handoff queue --manifest ../331-poc/docs/handoffs/handoff-manifest.json --out .kiro/auto/handoff-goals.lines --json
|
|
24
|
+
npx kse auto close-loop-batch .kiro/auto/handoff-goals.lines --format lines --batch-autonomous --continue-on-error --json
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## 2.1 批次 1:输入预检
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npx kse status --verbose
|
|
31
|
+
npx kse doctor --docs
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
核对 `handoff-manifest.json`:
|
|
35
|
+
1. `specs[]` 非空。
|
|
36
|
+
2. `templates[]` 非空。
|
|
37
|
+
3. `ontology_validation` 有最近一次执行记录。
|
|
38
|
+
|
|
39
|
+
## 2.2 批次 2:scene 包验证与 ontology 回归
|
|
40
|
+
|
|
41
|
+
对每个 handoff spec 执行:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
npx kse scene package-validate --spec <spec-name> --spec-package custom/scene-package.json --strict --json
|
|
45
|
+
npx kse scene ontology validate --package .kiro/specs/<spec-name>/custom --json
|
|
46
|
+
npx kse scene ontology impact --package .kiro/specs/<spec-name>/custom --ref <ref> --max-depth 2 --json
|
|
47
|
+
npx kse scene ontology path --package .kiro/specs/<spec-name>/custom --from <from-ref> --to <to-ref> --json
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## 2.3 批次 3:模板层接入
|
|
51
|
+
|
|
52
|
+
对于 scene package 模板链路(如果本轮包含 scene-package 模板):
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
npx kse scene package-publish --spec <spec-name> --out-dir .kiro/templates/scene-packages --json --force
|
|
56
|
+
npx kse scene package-registry --template-dir .kiro/templates/scene-packages --strict --json
|
|
57
|
+
npx kse scene package-gate-template --out .kiro/templates/scene-package-gate-policy.json --profile three-layer --force --json
|
|
58
|
+
npx kse scene package-gate --registry .kiro/templates/scene-packages/registry.json --policy .kiro/templates/scene-package-gate-policy.json --strict --json
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
说明:Spec 模板(`templates create-from-spec` 导出)与 scene package 模板是两条链路,可并行维护,但推荐在同一交接批次一并校验。
|
|
62
|
+
|
|
63
|
+
## 2.4 批次 4:主从编排闭环验证
|
|
64
|
+
|
|
65
|
+
基于本轮 spec 组合,执行自动闭环 dry-run/正式 run:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
npx kse auto close-loop --specs "<spec-a>,<spec-b>,<spec-c>" --dry-run --json
|
|
69
|
+
npx kse auto close-loop --specs "<spec-a>,<spec-b>,<spec-c>" --json
|
|
70
|
+
npx kse auto observability snapshot --json
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
判定标准:
|
|
74
|
+
1. 编排成功率满足门槛。
|
|
75
|
+
2. 不出现高风险未处置项。
|
|
76
|
+
3. observability 快照可追踪到本轮变更。
|
|
77
|
+
|
|
78
|
+
## 3. KSE 侧当前需持续适配的点
|
|
79
|
+
|
|
80
|
+
1. 对接自动化:把 331 handoff manifest 解析为可执行批次计划。
|
|
81
|
+
2. ontology 深化:将“业务规则/决策逻辑”映射为可量化 gate 指标。
|
|
82
|
+
3. 多 spec 主从调度:按依赖图自动分批并控制并行度。
|
|
83
|
+
4. 发布治理:把 handoff 批次结果写入统一 release evidence。
|
|
84
|
+
|
|
85
|
+
## 4. 角色划分
|
|
86
|
+
|
|
87
|
+
1. 331-poc Agent:交付业务事实、完成态 Spec、模板和证据。
|
|
88
|
+
2. KSE Agent:执行接入验证、治理门禁、主从闭环与发布收口。
|
|
89
|
+
3. Master Agent:维护跨仓上下文与批次节奏。
|
|
90
|
+
|
|
91
|
+
## 5. 失败处理
|
|
92
|
+
|
|
93
|
+
1. 输入缺失:退回 331-poc,标注缺失项,不进入 KSE 接入。
|
|
94
|
+
2. ontology 校验失败:先修 ref/lineage,再跑 gate。
|
|
95
|
+
3. 主从编排失败:降并发、缩批次、保留快照后重试。
|
|
96
|
+
|
|
97
|
+
## 6. 最终交付口径
|
|
98
|
+
|
|
99
|
+
每个接入批次结束后,KSE 侧必须输出:
|
|
100
|
+
1. 接入批次执行记录(命令、结果、失败点)。
|
|
101
|
+
2. gate 与 observability 快照。
|
|
102
|
+
3. 下一批次建议(可直接执行)。
|
|
103
|
+
|
|
104
|
+
## 7. 持续推进参考
|
|
105
|
+
|
|
106
|
+
KSE 侧后续增强计划见:`docs/331-poc-adaptation-roadmap.md`
|
package/docs/README.md
CHANGED
|
@@ -90,6 +90,8 @@ Detailed technical documentation:
|
|
|
90
90
|
- **[Multi-Repository Management Guide](multi-repo-management-guide.md)** - Managing multiple Git repositories
|
|
91
91
|
- **[Value Observability Guide](value-observability-guide.md)** - KPI snapshot, baseline, trend, and gate evidence workflow
|
|
92
92
|
- **[Scene Runtime Guide](scene-runtime-guide.md)** - Scene template engine, quality pipeline, ontology, and Moqui ERP integration
|
|
93
|
+
- **[331-poc Dual-Track Integration Guide](331-poc-dual-track-integration-guide.md)** - Handoff contract and integration playbook between 331-poc and kse
|
|
94
|
+
- **[331-poc Adaptation Roadmap](331-poc-adaptation-roadmap.md)** - Ongoing KSE-side adaptation backlog and rollout phases
|
|
93
95
|
- **[Multi-Agent Coordination Guide](multi-agent-coordination-guide.md)** - Multi-agent parallel coordination for concurrent development
|
|
94
96
|
- **[Troubleshooting](troubleshooting.md)** - Solutions to common problems
|
|
95
97
|
- **[FAQ](faq.md)** - Answers to frequently asked questions
|
|
@@ -184,6 +186,8 @@ Detailed technical documentation:
|
|
|
184
186
|
|
|
185
187
|
### Scene Runtime
|
|
186
188
|
- [Scene Runtime Guide](scene-runtime-guide.md)
|
|
189
|
+
- [331-poc Dual-Track Integration Guide](331-poc-dual-track-integration-guide.md)
|
|
190
|
+
- [331-poc Adaptation Roadmap](331-poc-adaptation-roadmap.md)
|
|
187
191
|
- [Scene Template Engine](command-reference.md#scene-template-engine)
|
|
188
192
|
- [Scene Quality Pipeline](command-reference.md#scene-template-quality-pipeline)
|
|
189
193
|
- [Scene Ontology](command-reference.md#scene-ontology-enhancement)
|
|
@@ -508,6 +508,12 @@ kse auto schema check --json
|
|
|
508
508
|
kse auto schema migrate --json # dry-run by default
|
|
509
509
|
kse auto schema migrate --apply --json # apply schema_version migration
|
|
510
510
|
kse auto schema migrate --only close-loop-session,batch-session --apply --json
|
|
511
|
+
|
|
512
|
+
# Dual-track handoff integration (e.g., 331-poc -> kse)
|
|
513
|
+
kse auto handoff plan --manifest ../331-poc/docs/handoffs/handoff-manifest.json --json
|
|
514
|
+
kse auto handoff plan --manifest ../331-poc/docs/handoffs/handoff-manifest.json --strict --out .kiro/reports/handoff-plan.json --json
|
|
515
|
+
kse auto handoff queue --manifest ../331-poc/docs/handoffs/handoff-manifest.json --out .kiro/auto/handoff-goals.lines --json
|
|
516
|
+
kse auto close-loop-batch .kiro/auto/handoff-goals.lines --format lines --batch-autonomous --continue-on-error --json
|
|
511
517
|
```
|
|
512
518
|
|
|
513
519
|
DoD-related options:
|
|
@@ -738,6 +744,10 @@ Autonomous archive schema compatibility:
|
|
|
738
744
|
- `kse auto schema migrate [--only <scopes>] [--target-version <version>] [--apply] [--json]`: migrate/backfill `schema_version` across autonomous archives.
|
|
739
745
|
- Default mode is dry-run; use `--apply` to persist changes.
|
|
740
746
|
|
|
747
|
+
Dual-track handoff integration:
|
|
748
|
+
- `kse auto handoff plan --manifest <path> [--out <path>] [--strict] [--strict-warnings] [--json]`: parse handoff manifest (source project, specs, templates, known gaps) and generate an executable KSE integration phase plan.
|
|
749
|
+
- `kse auto handoff queue --manifest <path> [--out <path>] [--append] [--no-include-known-gaps] [--dry-run] [--json]`: generate close-loop batch goal queue from handoff manifest and optionally persist line-based queue file (default `.kiro/auto/handoff-goals.lines`).
|
|
750
|
+
|
|
741
751
|
Recommended `.kiro/config/orchestrator.json`:
|
|
742
752
|
|
|
743
753
|
```json
|
package/docs/zh/README.md
CHANGED
|
@@ -171,6 +171,18 @@
|
|
|
171
171
|
- Ontology 图、Action Abstraction、Data Lineage、Agent Hints
|
|
172
172
|
- Moqui ERP 连接、发现、模板提取
|
|
173
173
|
|
|
174
|
+
### [331-poc 双轨协同对接手册](../331-poc-dual-track-integration-guide.md)
|
|
175
|
+
**331-poc 与 kse 协同执行指南** - 交接契约、接入命令链、主从闭环验收
|
|
176
|
+
- 331 深化成果输入约束
|
|
177
|
+
- KSE 侧模板/ontology/gate 验证流程
|
|
178
|
+
- 双仓协同失败回退策略
|
|
179
|
+
|
|
180
|
+
### [331-poc 适配路线图](../331-poc-adaptation-roadmap.md)
|
|
181
|
+
**KSE 侧持续适配清单** - 已完成、下一阶段、中期增强与长期目标
|
|
182
|
+
- handoff 自动化命令演进
|
|
183
|
+
- 主从编排与门禁增强
|
|
184
|
+
- 跨轮次回归与发布治理集成
|
|
185
|
+
|
|
174
186
|
### [Value 可观测指南](value-observability-guide.md)
|
|
175
187
|
**KPI 量化交付指南** - 快照、基线、趋势、门禁证据
|
|
176
188
|
- 周度 KPI 快照生成
|
package/lib/commands/auto.js
CHANGED
|
@@ -12,6 +12,7 @@ const chalk = require('chalk');
|
|
|
12
12
|
|
|
13
13
|
const AUTO_ARCHIVE_SCHEMA_VERSION = '1.0';
|
|
14
14
|
const AUTO_ARCHIVE_SCHEMA_SUPPORTED_VERSIONS = new Set([AUTO_ARCHIVE_SCHEMA_VERSION]);
|
|
15
|
+
const AUTO_HANDOFF_DEFAULT_QUEUE_FILE = '.kiro/auto/handoff-goals.lines';
|
|
15
16
|
|
|
16
17
|
/**
|
|
17
18
|
* Register auto commands
|
|
@@ -1349,6 +1350,86 @@ function registerAutoCommands(program) {
|
|
|
1349
1350
|
}
|
|
1350
1351
|
});
|
|
1351
1352
|
|
|
1353
|
+
const autoHandoff = auto
|
|
1354
|
+
.command('handoff')
|
|
1355
|
+
.description('Plan and stage dual-track handoff integration workflows');
|
|
1356
|
+
|
|
1357
|
+
autoHandoff
|
|
1358
|
+
.command('plan')
|
|
1359
|
+
.description('Build an executable KSE integration plan from a handoff manifest JSON')
|
|
1360
|
+
.requiredOption('--manifest <path>', 'Path to handoff-manifest.json')
|
|
1361
|
+
.option('--out <path>', 'Write generated integration plan JSON to file')
|
|
1362
|
+
.option('--strict', 'Fail when manifest validation contains errors')
|
|
1363
|
+
.option('--strict-warnings', 'Fail when manifest validation contains warnings')
|
|
1364
|
+
.option('--json', 'Output machine-readable JSON')
|
|
1365
|
+
.action(async (options) => {
|
|
1366
|
+
try {
|
|
1367
|
+
const result = await buildAutoHandoffPlan(process.cwd(), options);
|
|
1368
|
+
if (options.out) {
|
|
1369
|
+
await maybeWriteOutput(result, options.out, process.cwd());
|
|
1370
|
+
}
|
|
1371
|
+
if (options.json) {
|
|
1372
|
+
console.log(JSON.stringify(result, null, 2));
|
|
1373
|
+
return;
|
|
1374
|
+
}
|
|
1375
|
+
console.log(chalk.blue('Auto handoff integration plan:'));
|
|
1376
|
+
console.log(chalk.gray(` Manifest: ${result.manifest_path}`));
|
|
1377
|
+
console.log(chalk.gray(` Source project: ${result.source_project || 'unknown'}`));
|
|
1378
|
+
console.log(chalk.gray(` Specs: ${result.handoff.spec_count}`));
|
|
1379
|
+
console.log(chalk.gray(` Templates: ${result.handoff.template_count}`));
|
|
1380
|
+
console.log(chalk.gray(` Validation errors: ${result.validation.errors.length}`));
|
|
1381
|
+
console.log(chalk.gray(` Validation warnings: ${result.validation.warnings.length}`));
|
|
1382
|
+
console.log(chalk.gray(` Phases: ${result.phases.length}`));
|
|
1383
|
+
if (result.output_file) {
|
|
1384
|
+
console.log(chalk.gray(` Output: ${result.output_file}`));
|
|
1385
|
+
}
|
|
1386
|
+
} catch (error) {
|
|
1387
|
+
if (options.json) {
|
|
1388
|
+
console.log(JSON.stringify({ success: false, error: error.message }, null, 2));
|
|
1389
|
+
} else {
|
|
1390
|
+
console.error(chalk.red(`Error: ${error.message}`));
|
|
1391
|
+
}
|
|
1392
|
+
process.exit(1);
|
|
1393
|
+
}
|
|
1394
|
+
});
|
|
1395
|
+
|
|
1396
|
+
autoHandoff
|
|
1397
|
+
.command('queue')
|
|
1398
|
+
.description('Generate close-loop queue goals from a handoff manifest JSON')
|
|
1399
|
+
.requiredOption('--manifest <path>', 'Path to handoff-manifest.json')
|
|
1400
|
+
.option('--out <path>', `Queue output file (default: ${AUTO_HANDOFF_DEFAULT_QUEUE_FILE})`, AUTO_HANDOFF_DEFAULT_QUEUE_FILE)
|
|
1401
|
+
.option('--append', 'Append generated goals to existing queue file')
|
|
1402
|
+
.option('--no-include-known-gaps', 'Exclude known_gaps entries from generated queue goals')
|
|
1403
|
+
.option('--dry-run', 'Preview generated queue goals without writing file')
|
|
1404
|
+
.option('--json', 'Output machine-readable JSON')
|
|
1405
|
+
.action(async (options) => {
|
|
1406
|
+
try {
|
|
1407
|
+
const result = await buildAutoHandoffQueue(process.cwd(), options);
|
|
1408
|
+
if (!result.dry_run) {
|
|
1409
|
+
await writeAutoHandoffQueueFile(process.cwd(), result, options);
|
|
1410
|
+
}
|
|
1411
|
+
if (options.json) {
|
|
1412
|
+
console.log(JSON.stringify(result, null, 2));
|
|
1413
|
+
return;
|
|
1414
|
+
}
|
|
1415
|
+
console.log(chalk.blue('Auto handoff queue generated:'));
|
|
1416
|
+
console.log(chalk.gray(` Manifest: ${result.manifest_path}`));
|
|
1417
|
+
console.log(chalk.gray(` Goals: ${result.goal_count}`));
|
|
1418
|
+
console.log(chalk.gray(` Include known gaps: ${result.include_known_gaps ? 'yes' : 'no'}`));
|
|
1419
|
+
console.log(chalk.gray(` Mode: ${result.dry_run ? 'dry-run' : (result.append ? 'append' : 'overwrite')}`));
|
|
1420
|
+
if (result.output_file) {
|
|
1421
|
+
console.log(chalk.gray(` Queue file: ${result.output_file}`));
|
|
1422
|
+
}
|
|
1423
|
+
} catch (error) {
|
|
1424
|
+
if (options.json) {
|
|
1425
|
+
console.log(JSON.stringify({ success: false, error: error.message }, null, 2));
|
|
1426
|
+
} else {
|
|
1427
|
+
console.error(chalk.red(`Error: ${error.message}`));
|
|
1428
|
+
}
|
|
1429
|
+
process.exit(1);
|
|
1430
|
+
}
|
|
1431
|
+
});
|
|
1432
|
+
|
|
1352
1433
|
const autoSchema = auto
|
|
1353
1434
|
.command('schema')
|
|
1354
1435
|
.description('Check and migrate autonomous archive schema compatibility');
|
|
@@ -5763,6 +5844,418 @@ async function maybeWriteTextOutput(result, content, outCandidate, projectPath)
|
|
|
5763
5844
|
result.output_file = outputPath;
|
|
5764
5845
|
}
|
|
5765
5846
|
|
|
5847
|
+
function normalizeAutoHandoffManifestPath(projectPath, manifestCandidate) {
|
|
5848
|
+
const candidate = typeof manifestCandidate === 'string'
|
|
5849
|
+
? manifestCandidate.trim()
|
|
5850
|
+
: '';
|
|
5851
|
+
if (!candidate) {
|
|
5852
|
+
throw new Error('handoff manifest path is required');
|
|
5853
|
+
}
|
|
5854
|
+
return path.isAbsolute(candidate)
|
|
5855
|
+
? candidate
|
|
5856
|
+
: path.join(projectPath, candidate);
|
|
5857
|
+
}
|
|
5858
|
+
|
|
5859
|
+
function toAutoHandoffCliPath(projectPath, absolutePath) {
|
|
5860
|
+
const relative = path.relative(projectPath, absolutePath);
|
|
5861
|
+
if (relative && !relative.startsWith('..') && !path.isAbsolute(relative)) {
|
|
5862
|
+
return relative.split(path.sep).join('/');
|
|
5863
|
+
}
|
|
5864
|
+
return absolutePath;
|
|
5865
|
+
}
|
|
5866
|
+
|
|
5867
|
+
function quoteCliArg(value) {
|
|
5868
|
+
const raw = `${value || ''}`;
|
|
5869
|
+
if (raw.length === 0) {
|
|
5870
|
+
return '""';
|
|
5871
|
+
}
|
|
5872
|
+
if (!/[\s"'`]/.test(raw)) {
|
|
5873
|
+
return raw;
|
|
5874
|
+
}
|
|
5875
|
+
return `"${raw.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`;
|
|
5876
|
+
}
|
|
5877
|
+
|
|
5878
|
+
function normalizeHandoffText(value) {
|
|
5879
|
+
if (typeof value === 'string') {
|
|
5880
|
+
const normalized = value.trim();
|
|
5881
|
+
return normalized.length > 0 ? normalized : null;
|
|
5882
|
+
}
|
|
5883
|
+
if (typeof value === 'number' && Number.isFinite(value)) {
|
|
5884
|
+
return `${value}`;
|
|
5885
|
+
}
|
|
5886
|
+
return null;
|
|
5887
|
+
}
|
|
5888
|
+
|
|
5889
|
+
function readHandoffPathValue(input, keyPath) {
|
|
5890
|
+
if (!input || typeof input !== 'object') {
|
|
5891
|
+
return null;
|
|
5892
|
+
}
|
|
5893
|
+
const parts = String(keyPath || '')
|
|
5894
|
+
.split('.')
|
|
5895
|
+
.map(part => part.trim())
|
|
5896
|
+
.filter(Boolean);
|
|
5897
|
+
if (parts.length === 0) {
|
|
5898
|
+
return null;
|
|
5899
|
+
}
|
|
5900
|
+
let cursor = input;
|
|
5901
|
+
for (const part of parts) {
|
|
5902
|
+
if (!cursor || typeof cursor !== 'object' || !(part in cursor)) {
|
|
5903
|
+
return null;
|
|
5904
|
+
}
|
|
5905
|
+
cursor = cursor[part];
|
|
5906
|
+
}
|
|
5907
|
+
return cursor;
|
|
5908
|
+
}
|
|
5909
|
+
|
|
5910
|
+
function normalizeHandoffIdentifier(entry, fieldCandidates = []) {
|
|
5911
|
+
const directText = normalizeHandoffText(entry);
|
|
5912
|
+
if (directText) {
|
|
5913
|
+
return directText;
|
|
5914
|
+
}
|
|
5915
|
+
if (!entry || typeof entry !== 'object' || Array.isArray(entry)) {
|
|
5916
|
+
return null;
|
|
5917
|
+
}
|
|
5918
|
+
|
|
5919
|
+
for (const field of fieldCandidates) {
|
|
5920
|
+
const value = readHandoffPathValue(entry, field);
|
|
5921
|
+
const normalized = normalizeHandoffText(value);
|
|
5922
|
+
if (normalized) {
|
|
5923
|
+
return normalized;
|
|
5924
|
+
}
|
|
5925
|
+
}
|
|
5926
|
+
|
|
5927
|
+
return null;
|
|
5928
|
+
}
|
|
5929
|
+
|
|
5930
|
+
function collectUniqueIdentifiers(rawEntries, fieldCandidates, label) {
|
|
5931
|
+
const warnings = [];
|
|
5932
|
+
if (rawEntries === undefined || rawEntries === null) {
|
|
5933
|
+
return { values: [], warnings };
|
|
5934
|
+
}
|
|
5935
|
+
if (!Array.isArray(rawEntries)) {
|
|
5936
|
+
return {
|
|
5937
|
+
values: [],
|
|
5938
|
+
warnings: [`${label} must be an array`]
|
|
5939
|
+
};
|
|
5940
|
+
}
|
|
5941
|
+
|
|
5942
|
+
const values = [];
|
|
5943
|
+
const seen = new Set();
|
|
5944
|
+
rawEntries.forEach((entry, index) => {
|
|
5945
|
+
const normalized = normalizeHandoffIdentifier(entry, fieldCandidates);
|
|
5946
|
+
if (!normalized) {
|
|
5947
|
+
warnings.push(`${label}[${index}] is invalid and was ignored`);
|
|
5948
|
+
return;
|
|
5949
|
+
}
|
|
5950
|
+
if (seen.has(normalized)) {
|
|
5951
|
+
return;
|
|
5952
|
+
}
|
|
5953
|
+
seen.add(normalized);
|
|
5954
|
+
values.push(normalized);
|
|
5955
|
+
});
|
|
5956
|
+
|
|
5957
|
+
return { values, warnings };
|
|
5958
|
+
}
|
|
5959
|
+
|
|
5960
|
+
function collectKnownGaps(rawKnownGaps) {
|
|
5961
|
+
const warnings = [];
|
|
5962
|
+
if (rawKnownGaps === undefined || rawKnownGaps === null) {
|
|
5963
|
+
return { gaps: [], warnings };
|
|
5964
|
+
}
|
|
5965
|
+
if (!Array.isArray(rawKnownGaps)) {
|
|
5966
|
+
return {
|
|
5967
|
+
gaps: [],
|
|
5968
|
+
warnings: ['known_gaps must be an array']
|
|
5969
|
+
};
|
|
5970
|
+
}
|
|
5971
|
+
|
|
5972
|
+
const gaps = [];
|
|
5973
|
+
rawKnownGaps.forEach((entry, index) => {
|
|
5974
|
+
const normalized = normalizeHandoffIdentifier(entry, [
|
|
5975
|
+
'gap',
|
|
5976
|
+
'title',
|
|
5977
|
+
'description',
|
|
5978
|
+
'message',
|
|
5979
|
+
'name',
|
|
5980
|
+
'id'
|
|
5981
|
+
]);
|
|
5982
|
+
if (!normalized) {
|
|
5983
|
+
warnings.push(`known_gaps[${index}] is invalid and was ignored`);
|
|
5984
|
+
return;
|
|
5985
|
+
}
|
|
5986
|
+
gaps.push(normalized);
|
|
5987
|
+
});
|
|
5988
|
+
return { gaps, warnings };
|
|
5989
|
+
}
|
|
5990
|
+
|
|
5991
|
+
function normalizeAutoHandoffManifest(payload = {}) {
|
|
5992
|
+
const validationErrors = [];
|
|
5993
|
+
const validationWarnings = [];
|
|
5994
|
+
|
|
5995
|
+
if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
|
|
5996
|
+
throw new Error('handoff manifest must be a JSON object');
|
|
5997
|
+
}
|
|
5998
|
+
|
|
5999
|
+
const sourceProject = normalizeHandoffText(payload.source_project);
|
|
6000
|
+
if (!sourceProject) {
|
|
6001
|
+
validationWarnings.push('source_project is missing');
|
|
6002
|
+
}
|
|
6003
|
+
|
|
6004
|
+
const timestamp = normalizeHandoffText(payload.timestamp);
|
|
6005
|
+
if (!timestamp) {
|
|
6006
|
+
validationWarnings.push('timestamp is missing');
|
|
6007
|
+
}
|
|
6008
|
+
|
|
6009
|
+
const specsCollected = collectUniqueIdentifiers(
|
|
6010
|
+
payload.specs,
|
|
6011
|
+
['name', 'spec', 'spec_name', 'spec_id', 'id', 'spec.name', 'spec.id'],
|
|
6012
|
+
'specs'
|
|
6013
|
+
);
|
|
6014
|
+
validationWarnings.push(...specsCollected.warnings);
|
|
6015
|
+
if (specsCollected.values.length === 0) {
|
|
6016
|
+
validationErrors.push('specs must include at least one valid spec identifier');
|
|
6017
|
+
}
|
|
6018
|
+
|
|
6019
|
+
const templatesCollected = collectUniqueIdentifiers(
|
|
6020
|
+
payload.templates,
|
|
6021
|
+
['name', 'template', 'template_name', 'id', 'template.id', 'template.name'],
|
|
6022
|
+
'templates'
|
|
6023
|
+
);
|
|
6024
|
+
validationWarnings.push(...templatesCollected.warnings);
|
|
6025
|
+
if (templatesCollected.values.length === 0) {
|
|
6026
|
+
validationWarnings.push('templates is empty');
|
|
6027
|
+
}
|
|
6028
|
+
|
|
6029
|
+
const knownGapCollected = collectKnownGaps(payload.known_gaps);
|
|
6030
|
+
validationWarnings.push(...knownGapCollected.warnings);
|
|
6031
|
+
|
|
6032
|
+
const ontologyValidation = payload.ontology_validation && typeof payload.ontology_validation === 'object'
|
|
6033
|
+
? payload.ontology_validation
|
|
6034
|
+
: null;
|
|
6035
|
+
if (!ontologyValidation) {
|
|
6036
|
+
validationWarnings.push('ontology_validation is missing');
|
|
6037
|
+
}
|
|
6038
|
+
|
|
6039
|
+
const nextBatch = payload.next_batch && typeof payload.next_batch === 'object'
|
|
6040
|
+
? payload.next_batch
|
|
6041
|
+
: null;
|
|
6042
|
+
|
|
6043
|
+
return {
|
|
6044
|
+
source_project: sourceProject,
|
|
6045
|
+
timestamp,
|
|
6046
|
+
specs: specsCollected.values,
|
|
6047
|
+
templates: templatesCollected.values,
|
|
6048
|
+
known_gaps: knownGapCollected.gaps,
|
|
6049
|
+
ontology_validation: ontologyValidation,
|
|
6050
|
+
next_batch: nextBatch,
|
|
6051
|
+
validation: {
|
|
6052
|
+
errors: validationErrors,
|
|
6053
|
+
warnings: validationWarnings
|
|
6054
|
+
}
|
|
6055
|
+
};
|
|
6056
|
+
}
|
|
6057
|
+
|
|
6058
|
+
async function loadAutoHandoffManifest(projectPath, manifestCandidate) {
|
|
6059
|
+
const manifestPath = normalizeAutoHandoffManifestPath(projectPath, manifestCandidate);
|
|
6060
|
+
if (!await fs.pathExists(manifestPath)) {
|
|
6061
|
+
throw new Error(`handoff manifest not found: ${manifestPath}`);
|
|
6062
|
+
}
|
|
6063
|
+
|
|
6064
|
+
let payload;
|
|
6065
|
+
try {
|
|
6066
|
+
payload = await fs.readJson(manifestPath);
|
|
6067
|
+
} catch (error) {
|
|
6068
|
+
throw new Error(`invalid handoff manifest JSON: ${manifestPath} (${error.message})`);
|
|
6069
|
+
}
|
|
6070
|
+
|
|
6071
|
+
const normalized = normalizeAutoHandoffManifest(payload);
|
|
6072
|
+
return {
|
|
6073
|
+
manifest_path: manifestPath,
|
|
6074
|
+
manifest_file: toAutoHandoffCliPath(projectPath, manifestPath),
|
|
6075
|
+
...normalized
|
|
6076
|
+
};
|
|
6077
|
+
}
|
|
6078
|
+
|
|
6079
|
+
function buildAutoHandoffPhaseCommands(projectPath, manifestPath, specs = []) {
|
|
6080
|
+
const manifestCli = quoteCliArg(toAutoHandoffCliPath(projectPath, manifestPath));
|
|
6081
|
+
const phases = [];
|
|
6082
|
+
|
|
6083
|
+
phases.push({
|
|
6084
|
+
id: 'precheck',
|
|
6085
|
+
title: 'Precheck',
|
|
6086
|
+
goal: 'Validate handoff manifest integrity and repository readiness',
|
|
6087
|
+
commands: [
|
|
6088
|
+
`kse auto handoff plan --manifest ${manifestCli} --json`,
|
|
6089
|
+
'kse auto governance stats --json'
|
|
6090
|
+
]
|
|
6091
|
+
});
|
|
6092
|
+
|
|
6093
|
+
const specCommands = [];
|
|
6094
|
+
for (const specName of specs) {
|
|
6095
|
+
const specArg = quoteCliArg(specName);
|
|
6096
|
+
const specPackagePath = quoteCliArg(`.kiro/specs/${specName}/custom`);
|
|
6097
|
+
specCommands.push(`kse auto spec status ${specArg} --json`);
|
|
6098
|
+
specCommands.push(`kse auto spec instructions ${specArg} --json`);
|
|
6099
|
+
specCommands.push(`kse scene package-validate --spec ${specArg} --spec-package custom/scene-package.json --strict --json`);
|
|
6100
|
+
specCommands.push(`kse scene ontology validate --package ${specPackagePath} --json`);
|
|
6101
|
+
}
|
|
6102
|
+
phases.push({
|
|
6103
|
+
id: 'spec-validation',
|
|
6104
|
+
title: 'Spec Validation',
|
|
6105
|
+
goal: 'Validate spec docs, tasks, scene package contract, and ontology consistency',
|
|
6106
|
+
commands: specCommands
|
|
6107
|
+
});
|
|
6108
|
+
|
|
6109
|
+
const queueCli = quoteCliArg(AUTO_HANDOFF_DEFAULT_QUEUE_FILE);
|
|
6110
|
+
phases.push({
|
|
6111
|
+
id: 'execution',
|
|
6112
|
+
title: 'Autonomous Execution',
|
|
6113
|
+
goal: 'Generate queue goals and run autonomous close-loop batch integration',
|
|
6114
|
+
commands: [
|
|
6115
|
+
`kse auto handoff queue --manifest ${manifestCli} --out ${queueCli} --json`,
|
|
6116
|
+
`kse auto close-loop-batch ${queueCli} --format lines --batch-autonomous --continue-on-error --json`
|
|
6117
|
+
]
|
|
6118
|
+
});
|
|
6119
|
+
|
|
6120
|
+
phases.push({
|
|
6121
|
+
id: 'observability',
|
|
6122
|
+
title: 'Observability and Governance',
|
|
6123
|
+
goal: 'Snapshot integration evidence and plan remaining governance actions',
|
|
6124
|
+
commands: [
|
|
6125
|
+
'kse auto observability snapshot --json',
|
|
6126
|
+
'kse auto governance maintain --session-keep 50 --batch-session-keep 50 --controller-session-keep 50 --json'
|
|
6127
|
+
]
|
|
6128
|
+
});
|
|
6129
|
+
|
|
6130
|
+
return phases;
|
|
6131
|
+
}
|
|
6132
|
+
|
|
6133
|
+
async function buildAutoHandoffPlan(projectPath, options = {}) {
|
|
6134
|
+
const handoff = await loadAutoHandoffManifest(projectPath, options.manifest);
|
|
6135
|
+
const validationErrors = Array.isArray(handoff.validation.errors) ? handoff.validation.errors : [];
|
|
6136
|
+
const validationWarnings = Array.isArray(handoff.validation.warnings) ? handoff.validation.warnings : [];
|
|
6137
|
+
|
|
6138
|
+
if (options.strict && validationErrors.length > 0) {
|
|
6139
|
+
throw new Error(`handoff plan validation failed: ${validationErrors.join('; ')}`);
|
|
6140
|
+
}
|
|
6141
|
+
if (options.strictWarnings && validationWarnings.length > 0) {
|
|
6142
|
+
throw new Error(`handoff plan validation warnings: ${validationWarnings.join('; ')}`);
|
|
6143
|
+
}
|
|
6144
|
+
|
|
6145
|
+
const phases = buildAutoHandoffPhaseCommands(projectPath, handoff.manifest_path, handoff.specs);
|
|
6146
|
+
return {
|
|
6147
|
+
mode: 'auto-handoff-plan',
|
|
6148
|
+
generated_at: new Date().toISOString(),
|
|
6149
|
+
manifest_path: handoff.manifest_path,
|
|
6150
|
+
source_project: handoff.source_project,
|
|
6151
|
+
handoff: {
|
|
6152
|
+
timestamp: handoff.timestamp,
|
|
6153
|
+
spec_count: handoff.specs.length,
|
|
6154
|
+
template_count: handoff.templates.length,
|
|
6155
|
+
known_gap_count: handoff.known_gaps.length,
|
|
6156
|
+
specs: handoff.specs,
|
|
6157
|
+
templates: handoff.templates,
|
|
6158
|
+
known_gaps: handoff.known_gaps,
|
|
6159
|
+
ontology_validation: handoff.ontology_validation,
|
|
6160
|
+
next_batch: handoff.next_batch
|
|
6161
|
+
},
|
|
6162
|
+
validation: {
|
|
6163
|
+
is_valid: validationErrors.length === 0,
|
|
6164
|
+
errors: validationErrors,
|
|
6165
|
+
warnings: validationWarnings
|
|
6166
|
+
},
|
|
6167
|
+
phases,
|
|
6168
|
+
recommendations: [
|
|
6169
|
+
`kse auto handoff queue --manifest ${quoteCliArg(handoff.manifest_file)} --out ${quoteCliArg(AUTO_HANDOFF_DEFAULT_QUEUE_FILE)} --json`,
|
|
6170
|
+
`kse auto close-loop-batch ${quoteCliArg(AUTO_HANDOFF_DEFAULT_QUEUE_FILE)} --format lines --batch-autonomous --continue-on-error --json`
|
|
6171
|
+
]
|
|
6172
|
+
};
|
|
6173
|
+
}
|
|
6174
|
+
|
|
6175
|
+
function buildAutoHandoffQueueGoals(handoff, options = {}) {
|
|
6176
|
+
const includeKnownGaps = options.includeKnownGaps !== false;
|
|
6177
|
+
const goals = [];
|
|
6178
|
+
const seen = new Set();
|
|
6179
|
+
const pushGoal = value => {
|
|
6180
|
+
const normalized = normalizeHandoffText(value);
|
|
6181
|
+
if (!normalized || seen.has(normalized)) {
|
|
6182
|
+
return;
|
|
6183
|
+
}
|
|
6184
|
+
seen.add(normalized);
|
|
6185
|
+
goals.push(normalized);
|
|
6186
|
+
};
|
|
6187
|
+
|
|
6188
|
+
for (const specName of handoff.specs) {
|
|
6189
|
+
pushGoal(`integrate handoff spec ${specName} with scene package validation, ontology consistency checks, and close-loop completion`);
|
|
6190
|
+
}
|
|
6191
|
+
|
|
6192
|
+
for (const templateName of handoff.templates) {
|
|
6193
|
+
pushGoal(`validate handoff template ${templateName} for template registry compatibility and release readiness`);
|
|
6194
|
+
}
|
|
6195
|
+
|
|
6196
|
+
if (includeKnownGaps) {
|
|
6197
|
+
for (const gap of handoff.known_gaps) {
|
|
6198
|
+
pushGoal(`remediate handoff known gap: ${gap}`);
|
|
6199
|
+
}
|
|
6200
|
+
}
|
|
6201
|
+
|
|
6202
|
+
pushGoal('generate unified observability snapshot and governance follow-up recommendations for this handoff batch');
|
|
6203
|
+
return goals;
|
|
6204
|
+
}
|
|
6205
|
+
|
|
6206
|
+
async function buildAutoHandoffQueue(projectPath, options = {}) {
|
|
6207
|
+
const handoff = await loadAutoHandoffManifest(projectPath, options.manifest);
|
|
6208
|
+
const validationErrors = Array.isArray(handoff.validation.errors) ? handoff.validation.errors : [];
|
|
6209
|
+
if (validationErrors.length > 0) {
|
|
6210
|
+
throw new Error(`handoff queue validation failed: ${validationErrors.join('; ')}`);
|
|
6211
|
+
}
|
|
6212
|
+
|
|
6213
|
+
const includeKnownGaps = options.includeKnownGaps !== false;
|
|
6214
|
+
const goals = buildAutoHandoffQueueGoals(handoff, { includeKnownGaps });
|
|
6215
|
+
if (goals.length === 0) {
|
|
6216
|
+
throw new Error('handoff queue produced no goals');
|
|
6217
|
+
}
|
|
6218
|
+
|
|
6219
|
+
return {
|
|
6220
|
+
mode: 'auto-handoff-queue',
|
|
6221
|
+
generated_at: new Date().toISOString(),
|
|
6222
|
+
manifest_path: handoff.manifest_path,
|
|
6223
|
+
dry_run: Boolean(options.dryRun),
|
|
6224
|
+
append: Boolean(options.append),
|
|
6225
|
+
include_known_gaps: includeKnownGaps,
|
|
6226
|
+
goal_count: goals.length,
|
|
6227
|
+
goals,
|
|
6228
|
+
validation: {
|
|
6229
|
+
errors: handoff.validation.errors,
|
|
6230
|
+
warnings: handoff.validation.warnings
|
|
6231
|
+
},
|
|
6232
|
+
recommendations: [
|
|
6233
|
+
`kse auto close-loop-batch ${quoteCliArg(options.out || AUTO_HANDOFF_DEFAULT_QUEUE_FILE)} --format lines --batch-autonomous --continue-on-error --json`
|
|
6234
|
+
]
|
|
6235
|
+
};
|
|
6236
|
+
}
|
|
6237
|
+
|
|
6238
|
+
async function writeAutoHandoffQueueFile(projectPath, queueResult, options = {}) {
|
|
6239
|
+
const outCandidate = typeof options.out === 'string' && options.out.trim().length > 0
|
|
6240
|
+
? options.out.trim()
|
|
6241
|
+
: AUTO_HANDOFF_DEFAULT_QUEUE_FILE;
|
|
6242
|
+
const outputPath = path.isAbsolute(outCandidate)
|
|
6243
|
+
? outCandidate
|
|
6244
|
+
: path.join(projectPath, outCandidate);
|
|
6245
|
+
await fs.ensureDir(path.dirname(outputPath));
|
|
6246
|
+
|
|
6247
|
+
const content = `${queueResult.goals.join('\n')}\n`;
|
|
6248
|
+
if (options.append && await fs.pathExists(outputPath)) {
|
|
6249
|
+
const existing = await fs.readFile(outputPath, 'utf8');
|
|
6250
|
+
const separator = existing.length > 0 && !existing.endsWith('\n') ? '\n' : '';
|
|
6251
|
+
await fs.appendFile(outputPath, `${separator}${content}`, 'utf8');
|
|
6252
|
+
} else {
|
|
6253
|
+
await fs.writeFile(outputPath, content, 'utf8');
|
|
6254
|
+
}
|
|
6255
|
+
|
|
6256
|
+
queueResult.output_file = outputPath;
|
|
6257
|
+
}
|
|
6258
|
+
|
|
5766
6259
|
function buildProgramKpiSnapshot(summary) {
|
|
5767
6260
|
const results = Array.isArray(summary && summary.results) ? summary.results : [];
|
|
5768
6261
|
const totalGoals = Number(summary && summary.total_goals) || results.length || 1;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kiro-spec-engine",
|
|
3
|
-
"version": "1.47.
|
|
3
|
+
"version": "1.47.7",
|
|
4
4
|
"description": "kiro-spec-engine (kse) - A CLI tool and npm package for spec-driven development with AI coding assistants. NOT the Kiro IDE desktop application.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|