ai-spec-dev 0.37.0 → 0.41.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/README.md +381 -1796
- package/RELEASE_LOG.md +231 -0
- package/cli/commands/create.ts +9 -1176
- package/cli/commands/dashboard.ts +1 -1
- package/cli/pipeline/helpers.ts +34 -0
- package/cli/pipeline/multi-repo.ts +483 -0
- package/cli/pipeline/single-repo.ts +755 -0
- package/cli/utils.ts +2 -0
- package/core/code-generator.ts +52 -341
- package/core/codegen/helpers.ts +219 -0
- package/core/codegen/topo-sort.ts +98 -0
- package/core/constitution-consolidator.ts +2 -2
- package/core/dsl-coverage-checker.ts +298 -0
- package/core/dsl-extractor.ts +19 -46
- package/core/dsl-feedback.ts +1 -1
- package/core/dsl-validator.ts +74 -0
- package/core/error-feedback.ts +95 -11
- package/core/frontend-context-loader.ts +27 -5
- package/core/knowledge-memory.ts +52 -0
- package/core/mock/fixtures.ts +89 -0
- package/core/mock/proxy.ts +380 -0
- package/core/mock-server-generator.ts +12 -460
- package/core/requirement-decomposer.ts +4 -28
- package/core/reviewer.ts +1 -1
- package/core/safe-json.ts +76 -0
- package/core/spec-updater.ts +5 -21
- package/core/token-budget.ts +124 -0
- package/core/vcr.ts +20 -1
- package/dist/cli/index.js +4110 -3534
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/index.mjs +4237 -3661
- package/dist/cli/index.mjs.map +1 -1
- package/dist/index.d.mts +18 -16
- package/dist/index.d.ts +18 -16
- package/dist/index.js +310 -182
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +308 -180
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/purpose.md +173 -33
- package/tests/auto-consolidation.test.ts +109 -0
- package/tests/combined-generator.test.ts +81 -0
- package/tests/constitution-consolidator.test.ts +161 -0
- package/tests/constitution-generator.test.ts +94 -0
- package/tests/contract-bridge.test.ts +201 -0
- package/tests/design-dialogue.test.ts +108 -0
- package/tests/dsl-coverage-checker.test.ts +230 -0
- package/tests/dsl-feedback.test.ts +45 -0
- package/tests/dsl-validator-xref.test.ts +99 -0
- package/tests/error-feedback-repair.test.ts +319 -0
- package/tests/error-feedback-validation.test.ts +91 -0
- package/tests/frontend-context-loader.test.ts +609 -0
- package/tests/global-constitution.test.ts +110 -0
- package/tests/key-store.test.ts +73 -0
- package/tests/knowledge-memory.test.ts +327 -0
- package/tests/project-index.test.ts +206 -0
- package/tests/prompt-hasher.test.ts +19 -0
- package/tests/requirement-decomposer.test.ts +171 -0
- package/tests/reviewer.test.ts +4 -1
- package/tests/run-logger.test.ts +289 -0
- package/tests/run-snapshot.test.ts +113 -0
- package/tests/safe-json.test.ts +63 -0
- package/tests/spec-updater.test.ts +161 -0
- package/tests/test-generator.test.ts +146 -0
- package/tests/token-budget.test.ts +124 -0
- package/tests/vcr-hash.test.ts +101 -0
- package/tests/workspace-loader.test.ts +277 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai-spec-dev",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.41.0",
|
|
4
4
|
"description": "AI-driven Development Orchestrator SDK & CLI",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -27,12 +27,12 @@
|
|
|
27
27
|
"@google/generative-ai": "^0.21.0",
|
|
28
28
|
"@inquirer/editor": "^5.0.10",
|
|
29
29
|
"@inquirer/prompts": "^8.3.2",
|
|
30
|
+
"@rollup/rollup-darwin-arm64": "^4.60.1",
|
|
30
31
|
"axios": "^1.13.6",
|
|
31
32
|
"chalk": "^4.1.2",
|
|
32
33
|
"commander": "^13.1.0",
|
|
33
34
|
"dotenv": "^16.4.7",
|
|
34
35
|
"fs-extra": "^11.3.0",
|
|
35
|
-
"inquirer": "^8.2.6",
|
|
36
36
|
"openai": "^6.31.0",
|
|
37
37
|
"undici": "^7.24.4"
|
|
38
38
|
},
|
package/purpose.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
> 痛点 · 架构创新 · 边界处理 · DSL 的意义 · 当前局限 · 未来方向
|
|
7
7
|
>
|
|
8
|
-
> 当前版本:v0.
|
|
8
|
+
> 当前版本:v0.38.0 · 最后更新:2026-04-02
|
|
9
9
|
|
|
10
10
|
***
|
|
11
11
|
|
|
@@ -19,6 +19,8 @@
|
|
|
19
19
|
- 2.14 [两条 Pipeline 反馈环:让流水线可纠偏(v0.33.0+)](#214-两条-pipeline-反馈环让流水线可纠偏v0330)
|
|
20
20
|
- 2.15 [DSL 的多出口价值:类型、Dashboard 与可观测性(v0.34.0+)](#215-dsl-的多出口价值类型dashboard-与可观测性v0340)
|
|
21
21
|
- 2.16 [Pipeline 可靠性强化与 VCR 离线回放(v0.35.0+)](#216-pipeline-可靠性强化与-vcr-离线回放v0350)
|
|
22
|
+
- 2.17 [安全加固 + 测试工程化 + 质量门禁(v0.36.0–v0.37.0)](#217-安全加固--测试工程化--质量门禁v0360v0370)
|
|
23
|
+
- 2.18 [Design Options Dialogue + Pass 0 Spec Compliance + 项目索引(v0.38.0)](#218-design-options-dialogue--pass-0-spec-compliance--项目索引v0380)
|
|
22
24
|
3. [DSL 层的意义](#3-dsl-层的意义)
|
|
23
25
|
4. [完整功能矩阵](#4-完整功能矩阵)
|
|
24
26
|
5. [边界情况与兜底机制](#5-边界情况与兜底机制)
|
|
@@ -26,7 +28,7 @@
|
|
|
26
28
|
7. [当前局限](#7-当前局限)
|
|
27
29
|
8. [未来优化方向](#8-未来优化方向)
|
|
28
30
|
|
|
29
|
-
> **版本记录速览**:v0.17.0 宪法截断修复 · v0.18.0 `learn` + `minSpecScore` + 行为契约提取 · v0.19.0 错误解析重写 + Auto Gate 修复 · v0.20.0 `mock --serve` 一键联调 · v0.21.0 store 公开 API 提取修复 · v0.22.0 service/api 层分离 · v0.23.0 view/route 双层 + 文件名幻觉修复 · v0.24.0 四项质量修复(export default、impliesRegistration、依赖拓扑排序、lesson 计数)· v0.25.0 HTTP import 正则、分页提取、isToolCrash 三项修复 · v0.26.0 多仓库 review 目录、batch 容错、tasks JSON 健壮性 · **v0.27.0 可靠性三件套**(Provider retry/timeout/分类、文件快照 + `restore`、RunId 结构化日志)· **v0.28.0 3-pass review**(Pass 3 影响面评估 + 代码复杂度评估)· **v0.29.0 全量审查修复**(RunLogger 完整插桩、update 快照/日志/knowledge、Score Trend 显示影响/复杂度等级、死代码清理)· **v0.30.0 错误修复依赖图排序 + 前端 Import 多行感知解析** · **v0.31.0 Harness Engineer:Prompt Hash + Create 内联 Self-Eval** · **v0.32.0 logs / trend + DSL Coverage 细化评分** · **v0.33.0 两条 Pipeline 反馈环(DSL Gap Loop + Review→DSL Loop)** · **v0.34.0 Harness Dashboard + DSL → TypeScript 类型生成** · **v0.35.0 VCR 录制回放 + JSONL 崩溃恢复 + 熔断 + §9 知识闭环 + Approval Gate 预估**
|
|
31
|
+
> **版本记录速览**:v0.17.0 宪法截断修复 · v0.18.0 `learn` + `minSpecScore` + 行为契约提取 · v0.19.0 错误解析重写 + Auto Gate 修复 · v0.20.0 `mock --serve` 一键联调 · v0.21.0 store 公开 API 提取修复 · v0.22.0 service/api 层分离 · v0.23.0 view/route 双层 + 文件名幻觉修复 · v0.24.0 四项质量修复(export default、impliesRegistration、依赖拓扑排序、lesson 计数)· v0.25.0 HTTP import 正则、分页提取、isToolCrash 三项修复 · v0.26.0 多仓库 review 目录、batch 容错、tasks JSON 健壮性 · **v0.27.0 可靠性三件套**(Provider retry/timeout/分类、文件快照 + `restore`、RunId 结构化日志)· **v0.28.0 3-pass review**(Pass 3 影响面评估 + 代码复杂度评估)· **v0.29.0 全量审查修复**(RunLogger 完整插桩、update 快照/日志/knowledge、Score Trend 显示影响/复杂度等级、死代码清理)· **v0.30.0 错误修复依赖图排序 + 前端 Import 多行感知解析** · **v0.31.0 Harness Engineer:Prompt Hash + Create 内联 Self-Eval** · **v0.32.0 logs / trend + DSL Coverage 细化评分** · **v0.33.0 两条 Pipeline 反馈环(DSL Gap Loop + Review→DSL Loop)** · **v0.34.0 Harness Dashboard + DSL → TypeScript 类型生成** · **v0.35.0 VCR 录制回放 + JSONL 崩溃恢复 + 熔断 + §9 知识闭环 + Approval Gate 预估** · **v0.36.0 Shell 注入防护 + API Key 权限时序 + DSL 唯一性校验 + 核心模块测试覆盖** · **v0.36.1 质量硬门禁(minHarnessScore + maxErrorCycles)+ Provider 错误诊断增强** · **v0.37.0 Mock/Types/VCR 测试覆盖(409 cases / 18 modules / 45%)** · **v0.38.0 Design Options Dialogue + Pass 0 Spec Compliance + 项目索引 scan + 抗幻觉 Skills + Task verificationSteps**
|
|
30
32
|
|
|
31
33
|
***
|
|
32
34
|
|
|
@@ -60,7 +62,7 @@ ai-spec 对每个痛点都有对应的架构设计,不是功能堆砌,而是
|
|
|
60
62
|
| 流水线纠偏成本高 | 局部反馈环 | DSL 稀疏时先补 Spec 再提取;Review 发现结构性问题时先修契约再 `update --codegen` |
|
|
61
63
|
| 经验不断流失 | 知识记忆机制 | 审查 issue 自动写入宪法 §9,下次运行即生效;`init --consolidate` 定期精简 |
|
|
62
64
|
| 跨 task 一致性 | Generated File Cache | 已生成的 API/store 文件内容缓存,后续 task 可见真实导出名 |
|
|
63
|
-
| 难以判断系统是否在变好 | Harness 可观测层
|
|
65
|
+
| 难以判断系统是否在变好 | Harness 可观测层 + 质量门禁 | `promptHash` + `harnessScore` + `logs/trend/dashboard` + `minHarnessScore` 阈值阻断,把生成质量变成可比较、可管控的数据 |
|
|
64
66
|
|
|
65
67
|
**核心定位**:ai-spec 不是代码补全工具,而是一个「AI 辅助工程流程编排器」。它的目标是让工程师用最少的时间获得一个符合项目规范、通过基本质检、可直接进入 Review 的代码分支。
|
|
66
68
|
|
|
@@ -135,8 +137,11 @@ ai-spec 对每个痛点都有对应的架构设计,不是功能堆砌,而是
|
|
|
135
137
|
Step 1 · 加载项目上下文
|
|
136
138
|
ContextLoader 扫描代码结构 / 依赖 / 路由 / schema
|
|
137
139
|
↓
|
|
140
|
+
Step 1.5 · Design Options Dialogue(v0.38.0)
|
|
141
|
+
AI 提出 2-3 个架构方案,用户选择后注入 spec prompt
|
|
142
|
+
↓
|
|
138
143
|
Step 2 · Spec + Tasks 生成
|
|
139
|
-
宪法全文注入 prompt 最高优先级
|
|
144
|
+
宪法全文注入 prompt 最高优先级 + architectureDecision
|
|
140
145
|
↓
|
|
141
146
|
Step 3 · 交互式润色
|
|
142
147
|
Diff 预览,可多轮修改
|
|
@@ -165,7 +170,7 @@ Step 3.4 · Spec 质量评估
|
|
|
165
170
|
运行 test / lint / tsc
|
|
166
171
|
↓
|
|
167
172
|
判断:全部通过?
|
|
168
|
-
├─ 通过 → Step 9 · 3-pass 代码审查
|
|
173
|
+
├─ 通过 → Step 9 · Pass 0 合规 + 3-pass 代码审查
|
|
169
174
|
│ Pass1 架构 · Pass2 实现 · Pass3 影响面/复杂度
|
|
170
175
|
├─ 有错误,且 cycle ≤ 2
|
|
171
176
|
│ → 依赖图排序
|
|
@@ -173,13 +178,13 @@ Step 3.4 · Spec 质量评估
|
|
|
173
178
|
│ → 回到 test / lint / tsc
|
|
174
179
|
└─ cycle 2 仍失败
|
|
175
180
|
→ ⚠️ 黄色警告,继续
|
|
176
|
-
→ Step 9 · 3-pass 代码审查
|
|
181
|
+
→ Step 9 · Pass 0 合规 + 3-pass 代码审查
|
|
177
182
|
↓
|
|
178
183
|
§9 知识积累
|
|
179
184
|
审查 issue 自动追加宪法
|
|
180
185
|
↓
|
|
181
186
|
Step 10 · Harness Self-Eval
|
|
182
|
-
DSL 覆盖 + Compile + Review → harnessScore
|
|
187
|
+
Compliance + DSL 覆盖 + Compile + Review → harnessScore
|
|
183
188
|
PromptHash 关联,零 AI 调用
|
|
184
189
|
↓
|
|
185
190
|
✔ Done
|
|
@@ -831,6 +836,120 @@ Approval Gate 新增基于正则的 DSL 规模预估(无 AI 调用),在用
|
|
|
831
836
|
|
|
832
837
|
***
|
|
833
838
|
|
|
839
|
+
### 2.17 安全加固 + 测试工程化 + 质量门禁(v0.36.0–v0.37.0)
|
|
840
|
+
|
|
841
|
+
v0.36.0–v0.37.0 是一组围绕**安全性、可测试性和质量可控性**的版本迭代,将 ai-spec 从「功能完备」推向「工程可信赖」。
|
|
842
|
+
|
|
843
|
+
#### 安全加固(v0.36.0)
|
|
844
|
+
|
|
845
|
+
两处安全修复消除了已知攻击面:
|
|
846
|
+
|
|
847
|
+
- **Shell 命令注入防护**:`execSync` 拼接 shell 字符串时,AI 生成的 prompt 内容可能包含 `$`、`;`、`|` 等 shell 元字符。全部替换为 `spawnSync(cmd, [args], { shell: false })`,从根本上绕过 shell 解析
|
|
848
|
+
- **API Key 存储权限时序**:`writeJson()` → `chmod(0o600)` 改为 `ensureFile()` → `chmod(0o600)` → `writeJson()`,消除写入与权限设置之间的窗口期
|
|
849
|
+
|
|
850
|
+
#### DSL 校验增强(v0.36.0)
|
|
851
|
+
|
|
852
|
+
- **Endpoint ID 唯一性检查**:AI 经常生成重复 ID(如两个 `EP-001`),导致下游 types-generator、mock-server 产生覆盖冲突。新增 `Set<string>` 去重
|
|
853
|
+
- **Model 字段名唯一性检查**:同一 Model 内重复字段名会导致 Prisma schema 和 TypeScript interface 冲突
|
|
854
|
+
- **`missing_errors` 误报修复**:从「任何 endpoint 缺 errors 就报 gap」改为「所有 endpoint 都缺时才报」
|
|
855
|
+
|
|
856
|
+
#### 测试工程化(v0.36.0–v0.37.0)
|
|
857
|
+
|
|
858
|
+
三个版本累计新增 **158 个测试用例**(251 → 409),覆盖模块从 9 个增加到 18 个,覆盖率从 22.5% 提升到 45%:
|
|
859
|
+
|
|
860
|
+
| 版本 | 新增测试模块 | 测试数 | 累计 |
|
|
861
|
+
|------|-----------|--------|------|
|
|
862
|
+
| v0.36.0 | spec-generator / reviewer / code-generator | +8 tests | 259 |
|
|
863
|
+
| v0.36.1 | context-loader / openapi-exporter / spec-versioning | +72 tests | 331 |
|
|
864
|
+
| v0.37.0 | mock-server-generator / types-generator / vcr | +78 tests | 409 |
|
|
865
|
+
|
|
866
|
+
测试策略:不追求行覆盖率指标,而是**覆盖每个模块的核心决策路径**——正则匹配边界、容错降级、类型映射、序列化/反序列化往返。
|
|
867
|
+
|
|
868
|
+
#### 质量硬门禁(v0.36.1)
|
|
869
|
+
|
|
870
|
+
将质量评分从「仅展示」升级为「可阻断」:
|
|
871
|
+
|
|
872
|
+
- **`minHarnessScore`**:配置文件设置阈值(默认 0 = 禁用),自评分数低于阈值时 `exit(1)`,`--force` 可绕过
|
|
873
|
+
- **`maxErrorCycles`**:错误反馈修复轮次从硬编码 2 改为可配置(默认 2,TDD 默认 3,范围 1-10)
|
|
874
|
+
- **Provider 错误诊断增强**:401/403 提示检查 Key、429 提示等待或切换 Provider、网络错误提示代理设置、余额不足提示计费面板——从「报错退出」变为「报错 + 具体行动建议」
|
|
875
|
+
|
|
876
|
+
**设计原则**:质量门禁的目的不是拦住所有低分运行(`--force` 始终可用),而是让团队能定义「什么分数以下不应该自动进入 Review」的基线——这是从个人工具走向团队流程的关键能力。
|
|
877
|
+
|
|
878
|
+
***
|
|
879
|
+
|
|
880
|
+
### 2.18 Design Options Dialogue + Pass 0 Spec Compliance + 项目索引(v0.38.0)
|
|
881
|
+
|
|
882
|
+
v0.38.0 的主题是**决策前置 + 审查细化 + 跨项目感知**,在流水线的两端(生成前 / 生成后)分别加入新的质量控制点。
|
|
883
|
+
|
|
884
|
+
#### Design Options Dialogue(Step 1.5)
|
|
885
|
+
|
|
886
|
+
受 Superpowers brainstorming 启发,在 Spec 生成前新增架构方案对话:
|
|
887
|
+
|
|
888
|
+
```
|
|
889
|
+
Context Load → [Step 1.5] Design Options → Spec Generation → ...
|
|
890
|
+
```
|
|
891
|
+
|
|
892
|
+
- AI 针对需求提出 2-3 个架构方案,每个方案含 Approach / Trade-offs / Best when
|
|
893
|
+
- 用户可选择 Option A/B/C、Blend(AI 融合多方案)、或 Skip(跳过直接生成)
|
|
894
|
+
- 选定方案以 `architectureDecision` 注入 Spec prompt,确保 Spec 生成时已有明确的架构方向
|
|
895
|
+
- `--fast` / `--auto` / `--vcr-replay` 模式自动跳过,不增加自动化流程的延迟
|
|
896
|
+
|
|
897
|
+
**核心价值**:将「Spec 生成完后才发现方向不对」的问题前移到「Spec 生成前就对齐架构选型」。Approval Gate 保证了「需求理解是否正确」的检查点,Design Options 保证了「技术方案是否合理」的检查点——两者配合,覆盖了生成前的两类主要风险。
|
|
898
|
+
|
|
899
|
+
#### Pass 0 Spec Compliance Check
|
|
900
|
+
|
|
901
|
+
在现有 3-pass Review 前新增 Pass 0:穷举式 Spec 合规性审计。
|
|
902
|
+
|
|
903
|
+
```
|
|
904
|
+
Code Review: Pass 0 Compliance → Pass 1 Architecture → Pass 2 Implementation → Pass 3 Impact
|
|
905
|
+
```
|
|
906
|
+
|
|
907
|
+
- 从 Spec 中提取所有需求条目(endpoints / models / business rules / auth / error cases / side effects),逐条标记 ✅ / ⚠️ / ❌
|
|
908
|
+
- 输出 `ComplianceScore: X/10` + Blockers 列表
|
|
909
|
+
- Pass 1 去除原有「是否覆盖所有需求」条款(Pass 0 已处理),聚焦层分离 / 契约设计 / 安全姿态
|
|
910
|
+
- Pass 1 prompt 注入 Pass 0 合规报告,避免重复发现
|
|
911
|
+
- `harnessScore` 权重更新:compliance 0.30 · DSL coverage 0.25 · compile 0.20 · review 0.25
|
|
912
|
+
|
|
913
|
+
**设计思考**:原有 Pass 1 架构审查既要检查架构合理性,又要核对需求覆盖度,两个维度混在一起导致审查深度不够。拆分后,Pass 0 做「是否做全了」的清单式审计,Pass 1 做「做得是否对」的架构评判——职责更清晰,漏检率更低。
|
|
914
|
+
|
|
915
|
+
#### Task verificationSteps
|
|
916
|
+
|
|
917
|
+
每个 Task 新增 `verificationSteps: string[]` 字段,要求具体可执行的验证命令 + 预期结果:
|
|
918
|
+
|
|
919
|
+
```json
|
|
920
|
+
{
|
|
921
|
+
"verificationSteps": [
|
|
922
|
+
"curl -X POST /api/login -d '{\"email\":\"test@example.com\",\"password\":\"123\"}' → 200 + JWT token",
|
|
923
|
+
"curl -X POST /api/login -d '{\"email\":\"bad\"}' → 400 INVALID_EMAIL"
|
|
924
|
+
]
|
|
925
|
+
}
|
|
926
|
+
```
|
|
927
|
+
|
|
928
|
+
规则:每条必须是可执行命令 + 可观察预期结果(禁止 "works correctly" 式模糊描述),2-5 条/task,后端必含 HTTP 检查,前端必含 UI render + state 检查。
|
|
929
|
+
|
|
930
|
+
#### 项目索引 `ai-spec scan`
|
|
931
|
+
|
|
932
|
+
新增跨项目感知能力:
|
|
933
|
+
|
|
934
|
+
- `ai-spec scan` — 扫描根目录下所有子项目(识别 `package.json` / `go.mod` / `Cargo.toml` / `pom.xml` 等 manifest),持久化到 `.ai-spec-index.json`
|
|
935
|
+
- 增量更新:新项目添加 `firstSeen`,已有项目更新 `techStack / hasConstitution / lastSeen`,目录消失标记 `missing:true`
|
|
936
|
+
- Git Worktree 过滤:防止 ai-spec 生成的 worktree 被误识别为项目
|
|
937
|
+
- `ai-spec init --global` 联动:读取 index 为全局宪法生成提供多项目上下文
|
|
938
|
+
|
|
939
|
+
#### 抗幻觉 Skills
|
|
940
|
+
|
|
941
|
+
从 ai-spec 内部实践中提炼 5 个可复用的 Claude Code slash command,供团队日常使用:
|
|
942
|
+
|
|
943
|
+
| Skill | 用途 |
|
|
944
|
+
|-------|------|
|
|
945
|
+
| `/scan-singletons` | 扫描单例 config 文件,输出"只能修改、绝不重建"清单 |
|
|
946
|
+
| `/add-lesson` | 零摩擦写入宪法 §9,含去重 + 分类 + 时间戳 |
|
|
947
|
+
| `/installed-deps` | 列出所有依赖作为 codegen 白名单 |
|
|
948
|
+
| `/recall-lessons` | 按相关度筛选 §9 历史教训 |
|
|
949
|
+
| `/verify-imports` | 验证所有 import 路径,输出 broken imports 及修复建议 |
|
|
950
|
+
|
|
951
|
+
***
|
|
952
|
+
|
|
834
953
|
## 3. DSL 层的意义
|
|
835
954
|
|
|
836
955
|
DSL 是整个系统中设计投入最大的模块,也是最容易被误解为「多此一举」的部分。
|
|
@@ -890,42 +1009,45 @@ DSL 提取本身是高幻觉风险操作。ai-spec 做了几个针对性设计
|
|
|
890
1009
|
|
|
891
1010
|
## 4. 完整功能矩阵
|
|
892
1011
|
|
|
893
|
-
截至 v0.
|
|
1012
|
+
截至 v0.38.0,ai-spec 的完整能力覆盖:
|
|
894
1013
|
|
|
895
1014
|
| 阶段 | 命令 | 核心能力 |
|
|
896
1015
|
| -------------- | ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
897
1016
|
| **初始化** | `ai-spec init` | 扫描项目 → 生成宪法 §1-§8(`--global` 生成全局宪法,`--consolidate` 整合 §9) |
|
|
898
|
-
| **新功能** | `ai-spec create` | 宪法(全文注入)→ context → Spec+Tasks → **Spec 质量评估 + minSpecScore Gate** → Approval Gate → DSL → **DSL Gap Feedback** → Worktree → 逐 task codegen(**七层顺序 + 层内拓扑排序 + 行为契约缓存**)→ **TDD(`--tdd`)** →
|
|
1017
|
+
| **新功能** | `ai-spec create` | 宪法(全文注入)→ context → **Design Options Dialogue** → Spec+Tasks → **Spec 质量评估 + minSpecScore Gate** → Approval Gate → DSL → **DSL Gap Feedback** → Worktree → 逐 task codegen(**七层顺序 + 层内拓扑排序 + 行为契约缓存**)→ **TDD(`--tdd`)** → 错误反馈闭环(全文扫描,≤`maxErrorCycles` 轮)→ 测试骨架 → **Pass 0 Spec Compliance + 3-pass review** → **Review→DSL Loop** → **Harness Self-Eval(minHarnessScore Gate)** |
|
|
899
1018
|
| **变更迭代** | `ai-spec update` | 最小化更新 Spec → 定向更新 DSL(delta 对比)→ 识别受影响文件 → 可选重新生成(**`--codegen`** **附带写前快照 + RunId 日志 + knowledge 积累,v0.29.0**) |
|
|
900
1019
|
| **接口导出** | `ai-spec export` | DSL → OpenAPI 3.1.0 YAML/JSON,纯 TypeScript 实现,零外部依赖 |
|
|
901
1020
|
| **前后端联调** | `ai-spec mock` | DSL → Express Mock Server + Proxy 配置 + MSW Handlers;`--serve` 一键后台启动服务器 + 自动 patch 前端 Proxy(Vite/CRA);`--restore` 一键还原 |
|
|
902
1021
|
| **类型产物** | `ai-spec types` | DSL → TypeScript 类型文件(models、endpoint request types、`API_ENDPOINTS`、component props) |
|
|
903
1022
|
| **知识注入** | `ai-spec learn` | 零摩擦向宪法 §9 注入工程教训,不调用 AI,实时去重 |
|
|
904
|
-
| **代码审查** | `ai-spec review` | git diff + Spec → AI **3-pass** 审查(架构层 + 实现层 + 影响面/复杂度)→ issue 写入宪法 §9
|
|
1023
|
+
| **代码审查** | `ai-spec review` | git diff + Spec → AI **Pass 0 合规审计 + 3-pass** 审查(架构层 + 实现层 + 影响面/复杂度)→ issue 写入宪法 §9 |
|
|
905
1024
|
| **运行观测** | `ai-spec logs` / `trend` / `dashboard` | 基于 RunLog 做单次回看、跨运行趋势分析、Prompt 版本对比、阶段耗时与错误频次可视化 |
|
|
906
1025
|
| **快照回滚** | `ai-spec restore <runId>` | 按 RunId 回滚本次生成写入的所有文件(v0.27.0+) |
|
|
1026
|
+
| **项目索引** | `ai-spec scan` | 扫描根目录子项目 → 持久化 `.ai-spec-index.json` → 增量更新 → `init --global` 联动多项目上下文 |
|
|
907
1027
|
| **多 Repo 工作区** | workspace 模式 | 一句需求 → AI 拆分职责+UX 决策 → \[后端流水线 → DSL 契约] → \[前端流水线(注入后端契约)];`--serve` 完成后自动启动联调环境 |
|
|
908
1028
|
|
|
909
|
-
**单 Repo 流水线总图(v0.
|
|
1029
|
+
**单 Repo 流水线总图(v0.38.0):**
|
|
910
1030
|
|
|
911
1031
|
```
|
|
912
1032
|
需求描述
|
|
913
1033
|
→ 项目宪法(全文注入)+ 多语言 Context 感知 + 前端 Context 提取
|
|
914
|
-
→
|
|
915
|
-
→
|
|
916
|
-
→
|
|
917
|
-
→
|
|
918
|
-
→
|
|
919
|
-
→
|
|
920
|
-
→
|
|
921
|
-
→
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
→
|
|
926
|
-
→
|
|
927
|
-
→
|
|
928
|
-
→
|
|
1034
|
+
→ Design Options Dialogue(2-3 架构方案选择,v0.38.0)
|
|
1035
|
+
→ Spec + Tasks(合并生成,含 verificationSteps)
|
|
1036
|
+
→ 交互式润色(Diff 预览)
|
|
1037
|
+
→ Spec 质量评估(minSpecScore 阈值)
|
|
1038
|
+
→ Approval Gate(含 DSL 范围预估)
|
|
1039
|
+
→ RunId 生成 + RunSnapshot 初始化 + JSONL Shadow
|
|
1040
|
+
→ DSL 提取 + 校验(抗幻觉,≤2 次 retry,ID/字段唯一性)
|
|
1041
|
+
→ Git Worktree 隔离
|
|
1042
|
+
→ 逐 task 代码生成(每次写文件前先快照原内容)
|
|
1043
|
+
data → infra → service → api → view → route → test
|
|
1044
|
+
同层内:拓扑排序分 batch → batch 内并行 → 批次间串行更新缓存
|
|
1045
|
+
→ 错误反馈闭环(全文扫描,≤maxErrorCycles 轮,无进展熔断)
|
|
1046
|
+
→ 测试骨架生成
|
|
1047
|
+
→ AI 代码审查(Pass 0 合规 + 3-pass:架构 + 实现 + 影响面)
|
|
1048
|
+
→ Harness Self-Eval(minHarnessScore Gate)
|
|
1049
|
+
→ Run 结构化日志写入(JSON + JSONL)
|
|
1050
|
+
→ 经验写入宪法 §9(异步 fire-and-await)
|
|
929
1051
|
```
|
|
930
1052
|
|
|
931
1053
|
**多 Repo 工作区 + 一键联调(v0.20.0+):**
|
|
@@ -1071,13 +1193,12 @@ DSL 设计主要针对 REST API 场景(HTTP 端点 + 数据模型)。对于
|
|
|
1071
1193
|
|
|
1072
1194
|
### 7.5 错误修复能力有上限
|
|
1073
1195
|
|
|
1074
|
-
|
|
1196
|
+
错误反馈循环以文件为粒度修复。仍存在的限制:
|
|
1075
1197
|
|
|
1076
|
-
- 修复上限固定为 2 cycle,无法动态调整
|
|
1077
1198
|
- 同一文件内多个相互依赖的逻辑错误需要多轮才能全部消除
|
|
1078
1199
|
- Python、Java 等语言的 import 语法暂不参与依赖排序(当前仅解析 TS/JS 相对 import)
|
|
1079
1200
|
|
|
1080
|
-
> v0.19.0 改进了错误 **解析** 质量(全文扫描 + file:line 过滤)。v0.30.0 新增 `buildRepairOrder()` 对出错文件按 import 依赖拓扑排序,被依赖文件优先修复,cascade 错误在 cycle 1 的消除率提升。2
|
|
1201
|
+
> v0.19.0 改进了错误 **解析** 质量(全文扫描 + file:line 过滤)。v0.30.0 新增 `buildRepairOrder()` 对出错文件按 import 依赖拓扑排序,被依赖文件优先修复,cascade 错误在 cycle 1 的消除率提升。v0.35.0 新增无进展熔断(错误数未减少时立即退出)。**v0.36.1 将修复轮次从硬编码 2 改为 `maxErrorCycles` 可配置**(默认 2,TDD 默认 3,范围 1-10)。
|
|
1081
1202
|
|
|
1082
1203
|
***
|
|
1083
1204
|
|
|
@@ -1097,7 +1218,7 @@ DSL 设计主要针对 REST API 场景(HTTP 端点 + 数据模型)。对于
|
|
|
1097
1218
|
|
|
1098
1219
|
***
|
|
1099
1220
|
|
|
1100
|
-
*ai-spec v0.
|
|
1221
|
+
*ai-spec v0.38.0 · Design Rationale Document · 2026-04-02*
|
|
1101
1222
|
|
|
1102
1223
|
</details>
|
|
1103
1224
|
|
|
@@ -1207,7 +1328,7 @@ The review stage evolved from a simpler review flow into a 3-pass process:
|
|
|
1207
1328
|
|
|
1208
1329
|
This helps distinguish local code issues from broader system risk.
|
|
1209
1330
|
|
|
1210
|
-
### 2.13 Harness Engineer: from Prompt Hash to a quality data loop
|
|
1331
|
+
### 2.13 Harness Engineer: from Prompt Hash to a quality data loop (v0.31.0+)
|
|
1211
1332
|
|
|
1212
1333
|
Harness Engineer is not just a label for a self-eval step. In ai-spec it is becoming an upper-layer design principle:
|
|
1213
1334
|
|
|
@@ -1218,6 +1339,25 @@ Harness Engineer is not just a label for a self-eval step. In ai-spec it is beco
|
|
|
1218
1339
|
|
|
1219
1340
|
The purpose is to turn the generation system into something measurable, comparable, and continuously optimizable, rather than a black-box sequence of prompts.
|
|
1220
1341
|
|
|
1342
|
+
### 2.14 Security hardening + test engineering + quality gates (v0.36.0–v0.37.0)
|
|
1343
|
+
|
|
1344
|
+
These releases focus on making the pipeline **trustworthy** rather than just functional:
|
|
1345
|
+
|
|
1346
|
+
- **Security**: shell command injection eliminated via `spawnSync` (no shell parsing); API key file permissions set before writing sensitive data
|
|
1347
|
+
- **DSL validation**: endpoint ID uniqueness and model field name uniqueness checks prevent downstream conflicts
|
|
1348
|
+
- **Test engineering**: 158 new test cases across 9 modules (mock-server, types-generator, VCR, context-loader, openapi-exporter, spec-versioning, spec-generator, reviewer, code-generator), reaching 409 total / 18 modules / 45% coverage
|
|
1349
|
+
- **Quality gates**: `minHarnessScore` blocks low-quality runs at exit; `maxErrorCycles` makes repair loop depth configurable; provider errors now include actionable remediation suggestions
|
|
1350
|
+
|
|
1351
|
+
### 2.15 Design Options Dialogue + Pass 0 Compliance + project index (v0.38.0)
|
|
1352
|
+
|
|
1353
|
+
v0.38.0 adds quality control points at both ends of the pipeline:
|
|
1354
|
+
|
|
1355
|
+
- **Design Options Dialogue** (Step 1.5): AI proposes 2-3 architecture options before spec generation. Users pick one (or blend), and the decision constrains the spec prompt. This prevents "wrong direction discovered too late."
|
|
1356
|
+
- **Pass 0 Spec Compliance Check**: exhaustive audit of every requirement in the Spec (endpoints, models, auth, errors, side effects) before the existing 3-pass review. Outputs `ComplianceScore` + blockers list. Pass 1 is freed to focus purely on architecture quality.
|
|
1357
|
+
- **Task verificationSteps**: each task now requires concrete, executable verification commands with expected outcomes — no more "works correctly" acceptance criteria.
|
|
1358
|
+
- **Project index (`ai-spec scan`)**: discovers sub-projects across a directory tree, persists to `.ai-spec-index.json`, and feeds multi-project context into global constitution generation.
|
|
1359
|
+
- **Anti-hallucination skills**: 5 reusable Claude Code slash commands extracted from ai-spec's internal practices (`/scan-singletons`, `/add-lesson`, `/installed-deps`, `/recall-lessons`, `/verify-imports`).
|
|
1360
|
+
|
|
1221
1361
|
## 3. Why the DSL layer matters
|
|
1222
1362
|
|
|
1223
1363
|
### 3.1 Removing ambiguity between spec and code
|
|
@@ -1287,8 +1427,8 @@ Documented future work includes:
|
|
|
1287
1427
|
- deeper CI/CD integration
|
|
1288
1428
|
- stronger cross-run observability and evaluation signals
|
|
1289
1429
|
|
|
1290
|
-
The overall direction is clear: ai-spec is moving from “AI writes code for me” toward “AI-assisted delivery becomes a measurable engineering system.”
|
|
1430
|
+
The overall direction is clear: ai-spec is moving from “AI writes code for me” toward “AI-assisted delivery becomes a measurable, trustworthy engineering system.”
|
|
1291
1431
|
|
|
1292
|
-
*ai-spec v0.
|
|
1432
|
+
*ai-spec v0.38.0 · Design Rationale Document · 2026-04-02*
|
|
1293
1433
|
|
|
1294
1434
|
</details>
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
|
|
2
|
+
import * as fs from "fs-extra";
|
|
3
|
+
import * as path from "path";
|
|
4
|
+
import * as os from "os";
|
|
5
|
+
import { maybeAutoConsolidate } from "../core/knowledge-memory";
|
|
6
|
+
import { CONSTITUTION_FILE } from "../core/constitution-generator";
|
|
7
|
+
|
|
8
|
+
const logSpy = vi.spyOn(console, "log").mockImplementation(() => {});
|
|
9
|
+
|
|
10
|
+
describe("maybeAutoConsolidate", () => {
|
|
11
|
+
let tmpDir: string;
|
|
12
|
+
const mockProvider = {
|
|
13
|
+
generate: vi.fn(),
|
|
14
|
+
providerName: "test",
|
|
15
|
+
modelName: "test-model",
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
beforeEach(async () => {
|
|
19
|
+
tmpDir = path.join(os.tmpdir(), `ac-test-${Date.now()}`);
|
|
20
|
+
await fs.ensureDir(tmpDir);
|
|
21
|
+
mockProvider.generate.mockReset();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
afterEach(async () => {
|
|
25
|
+
await fs.remove(tmpDir);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it("returns false when no constitution file", async () => {
|
|
29
|
+
const result = await maybeAutoConsolidate(mockProvider, tmpDir);
|
|
30
|
+
expect(result).toBe(false);
|
|
31
|
+
expect(mockProvider.generate).not.toHaveBeenCalled();
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it("returns false when lesson count below threshold", async () => {
|
|
35
|
+
const lessons = Array.from({ length: 5 }, (_, i) =>
|
|
36
|
+
`- 📝 **[2026-01-0${i + 1}]** Lesson ${i + 1}`
|
|
37
|
+
).join("\n");
|
|
38
|
+
await fs.writeFile(
|
|
39
|
+
path.join(tmpDir, CONSTITUTION_FILE),
|
|
40
|
+
`# Constitution\n## 9. 积累教训 (Accumulated Lessons)\n${lessons}\n`
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
const result = await maybeAutoConsolidate(mockProvider, tmpDir, { threshold: 12 });
|
|
44
|
+
expect(result).toBe(false);
|
|
45
|
+
expect(mockProvider.generate).not.toHaveBeenCalled();
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it("triggers consolidation when lesson count meets threshold", async () => {
|
|
49
|
+
const lessons = Array.from({ length: 15 }, (_, i) =>
|
|
50
|
+
`- 📝 **[2026-01-${String(i + 1).padStart(2, "0")}]** Lesson number ${i + 1} with detail text`
|
|
51
|
+
).join("\n");
|
|
52
|
+
await fs.writeFile(
|
|
53
|
+
path.join(tmpDir, CONSTITUTION_FILE),
|
|
54
|
+
`# Constitution\n\n## 9. 积累教训 (Accumulated Lessons)\n${lessons}\n`
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
mockProvider.generate.mockResolvedValueOnce(
|
|
58
|
+
"# Constitution\n\n## 9. 积累教训 (Accumulated Lessons)\n- 📝 **[2026-04-02]** Consolidated lesson\n"
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
const result = await maybeAutoConsolidate(mockProvider, tmpDir, { threshold: 12 });
|
|
62
|
+
expect(result).toBe(true);
|
|
63
|
+
expect(mockProvider.generate).toHaveBeenCalled();
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it("returns false when consolidation fails", async () => {
|
|
67
|
+
const lessons = Array.from({ length: 15 }, (_, i) =>
|
|
68
|
+
`- 📝 **[2026-01-${String(i + 1).padStart(2, "0")}]** Lesson ${i + 1} detail`
|
|
69
|
+
).join("\n");
|
|
70
|
+
await fs.writeFile(
|
|
71
|
+
path.join(tmpDir, CONSTITUTION_FILE),
|
|
72
|
+
`# Constitution\n\n## 9. 积累教训 (Accumulated Lessons)\n${lessons}\n`
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
mockProvider.generate.mockRejectedValueOnce(new Error("API error"));
|
|
76
|
+
|
|
77
|
+
const result = await maybeAutoConsolidate(mockProvider, tmpDir, { threshold: 12 });
|
|
78
|
+
expect(result).toBe(false);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it("respects custom threshold", async () => {
|
|
82
|
+
const lessons = Array.from({ length: 4 }, (_, i) =>
|
|
83
|
+
`- 📝 **[2026-01-0${i + 1}]** Lesson ${i + 1} text`
|
|
84
|
+
).join("\n");
|
|
85
|
+
await fs.writeFile(
|
|
86
|
+
path.join(tmpDir, CONSTITUTION_FILE),
|
|
87
|
+
`# Constitution\n\n## 9. 积累教训 (Accumulated Lessons)\n${lessons}\n`
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
mockProvider.generate.mockResolvedValueOnce("# Constitution\n## 9. 积累教训\n- consolidated\n");
|
|
91
|
+
|
|
92
|
+
const result = await maybeAutoConsolidate(mockProvider, tmpDir, { threshold: 3 });
|
|
93
|
+
expect(result).toBe(true);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it("uses default threshold of 12", async () => {
|
|
97
|
+
const lessons = Array.from({ length: 10 }, (_, i) =>
|
|
98
|
+
`- 📝 **[2026-01-${String(i + 1).padStart(2, "0")}]** Lesson ${i + 1}`
|
|
99
|
+
).join("\n");
|
|
100
|
+
await fs.writeFile(
|
|
101
|
+
path.join(tmpDir, CONSTITUTION_FILE),
|
|
102
|
+
`# Constitution\n\n## 9. 积累教训 (Accumulated Lessons)\n${lessons}\n`
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
const result = await maybeAutoConsolidate(mockProvider, tmpDir);
|
|
106
|
+
expect(result).toBe(false); // 10 < 12 default
|
|
107
|
+
expect(mockProvider.generate).not.toHaveBeenCalled();
|
|
108
|
+
});
|
|
109
|
+
});
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
2
|
+
import { generateSpecWithTasks } from "../core/combined-generator";
|
|
3
|
+
|
|
4
|
+
const mockProvider = {
|
|
5
|
+
generate: vi.fn(),
|
|
6
|
+
providerName: "test",
|
|
7
|
+
modelName: "test-model",
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
mockProvider.generate.mockReset();
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
describe("generateSpecWithTasks", () => {
|
|
15
|
+
it("parses spec and tasks from combined output", async () => {
|
|
16
|
+
mockProvider.generate.mockResolvedValueOnce(
|
|
17
|
+
`# Feature Spec\n\nSome spec content.\n\n---TASKS_JSON---\n[{"id":"TASK-001","title":"Create model","description":"Create Order model","layer":"data","filesToTouch":["src/models/order.ts"],"acceptanceCriteria":["Model exists"],"verificationSteps":["Check file"],"dependencies":[],"priority":"high"}]`
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
const result = await generateSpecWithTasks(mockProvider, "Build order system");
|
|
21
|
+
expect(result.spec).toContain("Feature Spec");
|
|
22
|
+
expect(result.spec).not.toContain("TASKS_JSON");
|
|
23
|
+
expect(result.tasks).toHaveLength(1);
|
|
24
|
+
expect(result.tasks[0].id).toBe("TASK-001");
|
|
25
|
+
expect(result.tasks[0].layer).toBe("data");
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it("returns empty tasks when separator is missing", async () => {
|
|
29
|
+
mockProvider.generate.mockResolvedValueOnce(
|
|
30
|
+
"# Feature Spec\n\nNo tasks separator here."
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
const result = await generateSpecWithTasks(mockProvider, "Build feature");
|
|
34
|
+
expect(result.spec).toContain("Feature Spec");
|
|
35
|
+
expect(result.tasks).toEqual([]);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("returns empty tasks when JSON after separator is invalid", async () => {
|
|
39
|
+
mockProvider.generate.mockResolvedValueOnce(
|
|
40
|
+
"# Spec\n---TASKS_JSON---\nnot valid json"
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
const result = await generateSpecWithTasks(mockProvider, "Build feature");
|
|
44
|
+
expect(result.spec).toBe("# Spec");
|
|
45
|
+
expect(result.tasks).toEqual([]);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it("includes architecture decision in prompt when provided", async () => {
|
|
49
|
+
mockProvider.generate.mockResolvedValueOnce("# Spec\n---TASKS_JSON---\n[]");
|
|
50
|
+
|
|
51
|
+
await generateSpecWithTasks(mockProvider, "Build feature", undefined, "Use microservices");
|
|
52
|
+
const prompt = mockProvider.generate.mock.calls[0][0] as string;
|
|
53
|
+
expect(prompt).toContain("Architecture Decision");
|
|
54
|
+
expect(prompt).toContain("Use microservices");
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it("includes context when ProjectContext is provided", async () => {
|
|
58
|
+
mockProvider.generate.mockResolvedValueOnce("# Spec\n---TASKS_JSON---\n[]");
|
|
59
|
+
|
|
60
|
+
const context = {
|
|
61
|
+
techStack: ["express", "prisma"],
|
|
62
|
+
dependencies: ["express", "prisma"],
|
|
63
|
+
apiStructure: ["src/routes/user.ts"],
|
|
64
|
+
fileStructure: ["src/index.ts"],
|
|
65
|
+
} as any;
|
|
66
|
+
|
|
67
|
+
await generateSpecWithTasks(mockProvider, "Build order feature", context);
|
|
68
|
+
const prompt = mockProvider.generate.mock.calls[0][0] as string;
|
|
69
|
+
expect(prompt).toContain("Build order feature");
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it("trims spec and tasks", async () => {
|
|
73
|
+
mockProvider.generate.mockResolvedValueOnce(
|
|
74
|
+
" \n# Spec \n\n---TASKS_JSON---\n [{\"id\":\"T1\",\"title\":\"t\",\"description\":\"d\",\"layer\":\"data\",\"filesToTouch\":[],\"acceptanceCriteria\":[],\"verificationSteps\":[],\"dependencies\":[],\"priority\":\"high\"}] \n"
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
const result = await generateSpecWithTasks(mockProvider, "idea");
|
|
78
|
+
expect(result.spec).toBe("# Spec");
|
|
79
|
+
expect(result.tasks).toHaveLength(1);
|
|
80
|
+
});
|
|
81
|
+
});
|