iterate-plugin 3.10.0 → 3.11.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 +7 -7
- package/README.zh-CN.md +4 -4
- package/dist/report-render.js +33 -2
- package/dist/tools/report.js +20 -8
- package/lib/client.js +39 -5
- package/package.json +22 -20
- package/src/client/i18n.ts +3 -0
- package/src/client/index.ts +15 -4
- package/src/report-render.ts +69 -3
- package/src/tools/report.ts +19 -8
package/README.md
CHANGED
|
@@ -107,7 +107,7 @@ Both modes share:
|
|
|
107
107
|
### UI layer (build-free client slots, v3.7: 11 tabs, bilingual)
|
|
108
108
|
|
|
109
109
|
- **ConvergenceDashboard** (`conversation.input.dock`) — live round progress bar, severity stats, dimension badges, trend mini-chart above the input; normal mode also shows fix-count badges; plus a live workflow-phase chip (current phase + running/stopped) and a **task_mode indicator (code / iterate)**, now backed by the persisted transcript (`iterate_status` / `iterate_transcript` emit `taskMode`). While a run is active the dashboard switches to a running state (round + phase) instead of the static empty state, and the start buttons are hidden.
|
|
110
|
-
- **ObservatoryPanel** (`conversation.input.dock`) — an **eleven-tab** runtime observatory below the input: **live activity stream** (default tab, type filter) + **F1** review threads (expand / collapse all), **F2** convergence trend (incl. the per-round comparison table: findings raised / fixed / severity mix / validation outcomes), **F3** finding locations (severity / dimension / search filter), **F4** fixes + rollback, **F5** checkpoint resume, **F6** run console, **F7** decision timeline (type / round filter + search), **F8** Quality Gate, **F9** Experience Bank, **F10** Defense Events. Number-key shortcuts: `1`–`9` jump to F1–F9, `0` to F10, `-` back to the live stream. **F8/F9/F10 are live** — they scan the session stream for the latest `iterate_quality_gate` / `iterate_experience` / `iterate_defense_events` results and render real data (PASS/FAIL chip + per-dimension score bars, experience entries with adopt buttons + client-side search, defense type-count chips + filtered event stream); when the session stream has none they distinguish "no data on disk" from "not pulled yet" and offer a **拉取磁盘快照 (pull disk snapshot)** instruction that has the model call the read-side tools and echo the `.iterate/` artifacts back into the stream. One-click JSON export of all observatory data — manifest + live + `qualityGate` / `experienceBank` / `defenseEvents` / `report` (download, copy fallback). **v3.9:
|
|
110
|
+
- **ObservatoryPanel** (`conversation.input.dock`) — an **eleven-tab** runtime observatory below the input: **live activity stream** (default tab, type filter) + **F1** review threads (expand / collapse all), **F2** convergence trend (incl. the per-round comparison table: findings raised / fixed / severity mix / validation outcomes), **F3** finding locations (severity / dimension / search filter), **F4** fixes + rollback, **F5** checkpoint resume, **F6** run console, **F7** decision timeline (type / round filter + search), **F8** Quality Gate, **F9** Experience Bank, **F10** Defense Events. Number-key shortcuts: `1`–`9` jump to F1–F9, `0` to F10, `-` back to the live stream. **F8/F9/F10 are live** — they scan the session stream for the latest `iterate_quality_gate` / `iterate_experience` / `iterate_defense_events` results and render real data (PASS/FAIL chip + per-dimension score bars, experience entries with adopt buttons + client-side search, defense type-count chips + filtered event stream); when the session stream has none they distinguish "no data on disk" from "not pulled yet" and offer a **拉取磁盘快照 (pull disk snapshot)** instruction that has the model call the read-side tools and echo the `.iterate/` artifacts back into the stream. One-click JSON export of all observatory data — manifest + live + `qualityGate` / `experienceBank` / `defenseEvents` / `report` (download, copy fallback). **v3.9: report-export buttons** (导出报告 / 导出 SARIF, **v3.11 + 导出 JSON 报告**) render the *same shared data model* as the `iterate_report` tool — a PR-pasteable Markdown run report, a SARIF 2.1.0 log for CI / GitHub Code Scanning, and (v3.11) the complete normalized run model as machine-readable JSON. **v3.5.3: reset actions surfaced** — F5 copies an `iterate_checkpoint clear` instruction, F8 an `iterate_quality_gate clear` instruction, F10 an `iterate_defense_events clear` instruction.
|
|
111
111
|
- **TriagePanel** (`conversation.chat.turnTail`) — per-finding triage with `y` (keep → fix), `n` (skip), `a` (known-intentional) keyboard shortcuts and `↑`/`↓` navigation (focus-guarded: only active while the panel has focus), filtering, batch ops (incl. select-all), localStorage persistence. **Write-back closed loop**: the primary button copies an `iterate_triage apply` instruction for the marked entries (the tool validates, dedupes, backs up, rolls back on failure); a secondary button copies the raw `known_intentional` YAML; **回读已写入条目** copies an `iterate_triage list` instruction to confirm what actually landed. Copy payloads are aligned to the target tool schemas. **v3.2: native command buttons** (approve architectural fix, trigger new round, rollback to checkpoint). **v3.3: §8 指派修复 (assign)** — one click copies an `iterate_fix` instruction carrying every in-scope finding (respecting the batch toggle).
|
|
112
112
|
- **StatsCard** (`conversation.chat.turnTail`) — convergence stats, round history, trend chart and completion summary when no findings remain.
|
|
113
113
|
- **iterate theme skin** (`theme.overrideTokens`) — a warm-amber 13-token `--dsw-*` override, light/dark modes, togglable in settings.
|
|
@@ -307,7 +307,7 @@ validation:
|
|
|
307
307
|
- `iterate_experience` — **v3.2** query the experience bank (list / search / get), `add` a new verified fix (re-adding the same pattern+dimension bumps its hit count instead of duplicating it), or **v3.5 remove a stale/incorrect entry by `id`** so bad experiences never resurface. Persists to `.iterate/experience.json`
|
|
308
308
|
- `iterate_quality_gate` — **v3.2** read the quality certificate (`read`), or recompute + persist a fresh one (`compute`) from findings, validation results, `findingsByRound`, and `fixedByDimension`. Real per-dimension convergence rates. **v3.5: `clear` removes the persisted certificate to reset a stale gate before a fresh iteration**
|
|
309
309
|
- `iterate_defense_events` — **v3.2** query defense events (list / counts), `record` a new one, or **v3.5.3 `clear` the persisted stream** so a fresh iteration starts without stale defensive data. Human-readable labels follow the project language (en / zh)
|
|
310
|
-
- `iterate_report` — **v3.9** export the run as a portable report. `format:"markdown"` (default) renders a PR-pasteable report (run header, quality gate, findings, fixes, validations, defense events, experience bank); `format:"sarif"` renders a SARIF 2.1.0 log for CI / GitHub Code Scanning (one rule per review dimension, severity → error/warning/note, file+line locations, suggested fix attached). `write:true` persists it to `.iterate/report.md` / `.iterate/report.sarif`; `lang` (zh | en) selects the report language. Read-only apart from the optional report file
|
|
310
|
+
- `iterate_report` — **v3.9** export the run as a portable report. `format:"markdown"` (default) renders a PR-pasteable report (run header, quality gate, findings, fixes, validations, defense events, experience bank); `format:"sarif"` renders a SARIF 2.1.0 log for CI / GitHub Code Scanning (one rule per review dimension, severity → error/warning/note, file+line locations, suggested fix attached). **v3.11: `format:"json"` renders the COMPLETE normalized run model as machine-readable JSON** (findings, fixes, validations, convergence, quality gate, experience bank, defense events — the lossless format for CI dashboards / PR bots). `write:true` persists it to `.iterate/report.md` / `.iterate/report.sarif` / `.iterate/report.json`; `lang` (zh | en) selects the report language. Read-only apart from the optional report file
|
|
311
311
|
|
|
312
312
|
---
|
|
313
313
|
|
|
@@ -345,14 +345,14 @@ All runtime state lives under `.iterate/` at the project root (can be excluded v
|
|
|
345
345
|
`0.1.7-alpha.1`, `0.1.7-alpha.2`, `0.1.7-rc.1`, `0.1.7-rc.2`, `0.2.0-rc.1`,
|
|
346
346
|
`0.2.0-rc.2`, `0.2.1-alpha.1`, `0.2.1-alpha.2`, `0.2.1-alpha.3`,
|
|
347
347
|
`0.2.1-alpha.4`, `0.2.1-alpha.5`, `0.2.1-rc.1`, `0.2.1-rc.2`,
|
|
348
|
-
`0.2.1-rc.3` (
|
|
348
|
+
`0.2.1-rc.3`, `0.2.1-rc.4`, `0.2.2-alpha.2` (27 releases,
|
|
349
349
|
`0.1.1-rc.1` …
|
|
350
|
-
`0.2.
|
|
350
|
+
`0.2.2-alpha.2`).
|
|
351
351
|
The plugin is built against `@deepseek-ai/dsh-tools` /
|
|
352
|
-
`@deepseek-ai/dsh-util-values` `0.2.
|
|
352
|
+
`@deepseek-ai/dsh-util-values` `0.2.2-alpha.2` and uses only public contracts
|
|
353
353
|
(tool registration, client slots + theme, bundle patch, and — since v3.9 —
|
|
354
354
|
the session state-record API for session-linked run records).
|
|
355
|
-
It also declares `engines.dsh` (`>=0.1.1-rc.1`) so the dsh ≥ 0.2.
|
|
355
|
+
It also declares `engines.dsh` (`>=0.1.1-rc.1`) so the dsh ≥ 0.2.2-alpha.2
|
|
356
356
|
runtime evaluates compatibility from that single range (earlier runtimes fall
|
|
357
357
|
back to the `@deepseek-ai/dsh-*` peer versions listed in `package.json`).
|
|
358
358
|
- **Disposable-Profile evidence (real, `dsh` CLI `0.1.1-rc.1`)**: on a temp
|
|
@@ -488,7 +488,7 @@ npm test
|
|
|
488
488
|
All tests pass:
|
|
489
489
|
|
|
490
490
|
- **Full unit suite + type-check clean** (`npm test`, `npm run typecheck`) — the exact count is intentionally not pinned here to avoid doc drift; see `CHANGELOG.md` for per-release magnitudes
|
|
491
|
-
- Coverage: dedupe, filter, sort, multi-round convergence, meta-review audit, path safety, timeout clamping, config read/write + rollback, triage merge, diff computation, checkpoint validation, fix registry, history read + filter, prune cleanup report + dry-run semantics, UI pure functions (select-all key, runtime status guide), **v3.2: experience bank, quality gate, defense events, approval-gate fail-open path**.
|
|
491
|
+
- Coverage: dedupe, filter, sort, multi-round convergence, meta-review audit, path safety, timeout clamping, config read/write + rollback, triage merge, diff computation, checkpoint validation, fix registry, history read + filter, prune cleanup report + dry-run semantics, UI pure functions (select-all key, runtime status guide), **v3.2: experience bank, quality gate, defense events, approval-gate fail-open path**, **v3.9: portable report (markdown + SARIF)**, **v3.11: lossless JSON report (full normalized run model + `.iterate/report.json` persistence)**.
|
|
492
492
|
|
|
493
493
|
---
|
|
494
494
|
|
package/README.zh-CN.md
CHANGED
|
@@ -106,7 +106,7 @@ dsh plugin --profile web add iterate-plugin
|
|
|
106
106
|
### UI 层(客户端免构建槽位,v3.7:11 个页签,双语)
|
|
107
107
|
|
|
108
108
|
- **收敛看板 `ConvergenceDashboard`**(`conversation.input.dock`)— 输入框上方实时显示轮次进度条、严重度统计、维度徽章、趋势迷你图;normal 模式另显示修复计数徽章;还有运行阶段芯片(当前工作流阶段 + 运行中/已结束)与 **task_mode 指示器(code / iterate)**。运行期间看板切换为"运行中"状态(显示轮次与阶段),并隐藏启动按钮。
|
|
109
|
-
- **运行时观测台 `ObservatoryPanel`**(`conversation.input.dock`)— 输入框下方**十一个页签**运行时观测台:**实时活动流**(默认页,支持按活动类型筛选)+ **F1** 审查线程(全部展开/全部收起)、**F2** 收敛趋势(含跨轮对比表:每轮 findings / fixed / 严重度分布 / 验证结果)、**F3** 发现定位(按严重度/维度/关键词筛选)、**F4** 修复与回滚、**F5** 断点恢复、**F6** 运行控制台、**F7** 决策时间线(按类型/轮次筛选与关键词搜索)、**F8** 质量门禁、**F9** 经验银行、**F10** 防御事件。数字键快捷键:`1`–`9` 跳到 F1–F9,`0` 到 F10,`-` 回实时活动流。**F8/F9/F10 为实时数据**——扫描会话流中最近一次 `iterate_quality_gate` / `iterate_experience` / `iterate_defense_events` 结果并渲染真实数据(PASS/FAIL 徽章 + 分维度评分条、经验条目(含采纳按钮 + 客户端搜索)、防御类型计数芯片 + 筛选后的事件流);会话流中没有数据时区分"磁盘无数据"与"尚未拉取",并提供**拉取磁盘快照**指令(让模型调用只读工具、把 `.iterate/` 产物回显进会话流)。一键导出全部观测数据为 JSON——manifest + live + `qualityGate` / `experienceBank` / `defenseEvents` / `report`(优先下载,失败回退复制)。**v3.9
|
|
109
|
+
- **运行时观测台 `ObservatoryPanel`**(`conversation.input.dock`)— 输入框下方**十一个页签**运行时观测台:**实时活动流**(默认页,支持按活动类型筛选)+ **F1** 审查线程(全部展开/全部收起)、**F2** 收敛趋势(含跨轮对比表:每轮 findings / fixed / 严重度分布 / 验证结果)、**F3** 发现定位(按严重度/维度/关键词筛选)、**F4** 修复与回滚、**F5** 断点恢复、**F6** 运行控制台、**F7** 决策时间线(按类型/轮次筛选与关键词搜索)、**F8** 质量门禁、**F9** 经验银行、**F10** 防御事件。数字键快捷键:`1`–`9` 跳到 F1–F9,`0` 到 F10,`-` 回实时活动流。**F8/F9/F10 为实时数据**——扫描会话流中最近一次 `iterate_quality_gate` / `iterate_experience` / `iterate_defense_events` 结果并渲染真实数据(PASS/FAIL 徽章 + 分维度评分条、经验条目(含采纳按钮 + 客户端搜索)、防御类型计数芯片 + 筛选后的事件流);会话流中没有数据时区分"磁盘无数据"与"尚未拉取",并提供**拉取磁盘快照**指令(让模型调用只读工具、把 `.iterate/` 产物回显进会话流)。一键导出全部观测数据为 JSON——manifest + live + `qualityGate` / `experienceBank` / `defenseEvents` / `report`(优先下载,失败回退复制)。**v3.9 新增报告导出按钮**(导出报告 / 导出 SARIF,**v3.11 新增导出 JSON 报告**),与 `iterate_report` 工具共用同一份数据模型——分别产出可直接粘贴进 PR 的 Markdown 运行报告、供 CI / GitHub Code Scanning 消费的 SARIF 2.1.0 日志,以及(v3.11)完整的归一化运行模型 JSON(无损格式)。**v3.5.3 新增重置操作**:F5 复制 `iterate_checkpoint clear`、F8 复制 `iterate_quality_gate clear`、F10 复制 `iterate_defense_events clear` 指令。
|
|
110
110
|
- **Findings 分诊面板 `TriagePanel`**(`conversation.chat.turnTail`)— 逐条分诊,键盘快捷键 `y`(保留→修复)、`n`(跳过)、`a`(已知有意)与 `↑`/`↓` 移动(焦点守卫:仅面板内焦点时生效),支持筛选、批量(含一键全选所有 findings)、localStorage 持久化。**写回闭环**:主按钮复制已标记条目的 `iterate_triage apply` 指令(工具负责校验、去重、备份、失败回滚);次按钮复制裸 `known_intentional` YAML;**回读已写入条目**复制 `iterate_triage list` 指令核对实际落盘内容。复制载荷与目标工具 schema 对齐。**v3.2:原生命令按钮**(批准架构修复、触发新一轮、回滚到断点)。
|
|
111
111
|
- **收敛统计卡片 `StatsCard`**(`conversation.chat.turnTail`)— 无 findings 时显示收敛统计、历史轮次、趋势图、完成摘要。
|
|
112
112
|
- **iterate 主题皮肤**(`theme.overrideTokens`)— 暖琥珀配色的 13 个 `--dsw-*` token 覆盖,明暗双模式,可在设置页开关。
|
|
@@ -306,7 +306,7 @@ validation:
|
|
|
306
306
|
- `iterate_experience` — **v3.2** 查询经验银行(list / search / get),或 `add` 一条新的已验证修复:重复添加同一 pattern + dimension 累加命中次数而非重复写入。**v3.5 新增 `remove`:按 `id` 删除过时或错误的经验条目**。持久化到 `.iterate/experience.json`
|
|
307
307
|
- `iterate_quality_gate` — **v3.2** 读取质量凭证(`read`),或基于 findings、验证结果、`findingsByRound` 与 `fixedByDimension` 重新计算并持久化一份新凭证(`compute`)。真实的逐维度收敛率。**v3.5 新增 `clear`:删除持久化的质量凭证**,用于新一轮迭代前重置陈旧的门禁
|
|
308
308
|
- `iterate_defense_events` — **v3.2** 查询防御事件(list / counts),`record` 记录一条新事件,或 **v3.5.3 `clear` 清除持久化事件流**,让新一轮迭代不带陈旧防御数据起跑。可读标签跟随项目语言(en / zh)
|
|
309
|
-
- `iterate_report` — **v3.9** 把本次运行导出为可移植报告。`format:"markdown"`(默认)产出可直接粘贴进 PR 的报告(运行头部、质量门禁、发现、修复、验证、防御事件、经验银行);`format:"sarif"` 产出 SARIF 2.1.0 日志供 CI / GitHub Code Scanning 消费(每个审查维度一条规则,严重度 → error/warning/note
|
|
309
|
+
- `iterate_report` — **v3.9** 把本次运行导出为可移植报告。`format:"markdown"`(默认)产出可直接粘贴进 PR 的报告(运行头部、质量门禁、发现、修复、验证、防御事件、经验银行);`format:"sarif"` 产出 SARIF 2.1.0 日志供 CI / GitHub Code Scanning 消费(每个审查维度一条规则,严重度 → error/warning/note,携带文件+行号位置与建议修复)。**v3.11:`format:"json"` 产出完整归一化运行模型为机器可读 JSON**(发现、修复、验证、收敛率、质量门禁、经验银行、防御事件——供 CI 仪表盘 / PR 机器人消费的无损格式)。`write:true` 持久化到 `.iterate/report.md` / `.iterate/report.sarif` / `.iterate/report.json`;`lang`(zh | en)选择报告语言。除可选的报告文件外均为只读
|
|
310
310
|
|
|
311
311
|
---
|
|
312
312
|
|
|
@@ -335,7 +335,7 @@ validation:
|
|
|
335
335
|
### Node.js / DSH 兼容范围
|
|
336
336
|
|
|
337
337
|
- **Node.js**:`>=20`(`package.json` 的 `engines.node`)。
|
|
338
|
-
- **DSH**:`package.json` 的 `dsh.compatibility.dshReleases` 字段是发布兼容性的**唯一真源**(本 README 逐字镜像该字段;两者需同步更新)。以下官方版本声明为 `compatible`:`0.1.1-rc.1`、`0.1.2-alpha.4`、`0.1.2-alpha.5`、`0.1.2-rc.1`、`0.1.3-alpha.1`、`0.1.5-alpha.1`、`0.1.5-alpha.2`、`0.1.5-rc.1`、`0.1.5-rc.2`、`0.1.6-alpha.1`、`0.1.6-alpha.2`、`0.1.7-alpha.1`、`0.1.7-alpha.2`、`0.1.7-rc.1`、`0.1.7-rc.2`、`0.2.0-rc.1`、`0.2.0-rc.2`、`0.2.1-alpha.1`、`0.2.1-alpha.2`、`0.2.1-alpha.3`、`0.2.1-alpha.4`、`0.2.1-alpha.5`、`0.2.1-rc.1`、`0.2.1-rc.2`、`0.2.1-rc.3`(共
|
|
338
|
+
- **DSH**:`package.json` 的 `dsh.compatibility.dshReleases` 字段是发布兼容性的**唯一真源**(本 README 逐字镜像该字段;两者需同步更新)。以下官方版本声明为 `compatible`:`0.1.1-rc.1`、`0.1.2-alpha.4`、`0.1.2-alpha.5`、`0.1.2-rc.1`、`0.1.3-alpha.1`、`0.1.5-alpha.1`、`0.1.5-alpha.2`、`0.1.5-rc.1`、`0.1.5-rc.2`、`0.1.6-alpha.1`、`0.1.6-alpha.2`、`0.1.7-alpha.1`、`0.1.7-alpha.2`、`0.1.7-rc.1`、`0.1.7-rc.2`、`0.2.0-rc.1`、`0.2.0-rc.2`、`0.2.1-alpha.1`、`0.2.1-alpha.2`、`0.2.1-alpha.3`、`0.2.1-alpha.4`、`0.2.1-alpha.5`、`0.2.1-rc.1`、`0.2.1-rc.2`、`0.2.1-rc.3`、`0.2.1-rc.4`、`0.2.2-alpha.2`(共 27 个版本,`0.1.1-rc.1` … `0.2.2-alpha.2`)。插件基于 `@deepseek-ai/dsh-tools` / `@deepseek-ai/dsh-util-values` `0.2.2-alpha.2` 构建,只使用公开契约(工具注册、客户端 slots + theme、bundle patch,以及 v3.9 起的会话状态记录接口)。插件同时声明 `engines.dsh`(`>=0.1.1-rc.1`),使 dsh ≥ 0.2.2-alpha.2 运行时只依据该范围判断兼容性(更早的运行时仍回退校验 `package.json` 中列出的 `@deepseek-ai/dsh-*` peer 版本)。
|
|
339
339
|
- **一次性 Profile 证据(真实,`dsh` CLI `0.1.1-rc.1`)**:在临时 `$DSH_HOME` 下
|
|
340
340
|
`dsh plugin --profile <p> add <本仓库>` 约 449ms 完成安装;`dsh --profile <p>
|
|
341
341
|
--dump-config` 正确合成 `iterate-plugin` bundle patch;`dsh plugin --profile <p>
|
|
@@ -445,7 +445,7 @@ npm test
|
|
|
445
445
|
所有测试通过:
|
|
446
446
|
|
|
447
447
|
- **全量单元测试 + 类型检查通过**(`npm test`、`npm run typecheck`——此处刻意不写具体数量以根除文档漂移,各版本量级见 `CHANGELOG.md`)
|
|
448
|
-
- 覆盖:去重、过滤、排序、多轮收敛、meta-review 审计、路径安全、超时钳制、配置读写与回滚、triage 合并、diff 计算、checkpoint 校验、修复注册表、历史读取与过滤、prune 清理报告与 dry-run 语义、UI 纯函数(select-all 键、运行时状态指引)、**v3.2:经验银行、质量门禁、防御事件、审批门禁 fail-open
|
|
448
|
+
- 覆盖:去重、过滤、排序、多轮收敛、meta-review 审计、路径安全、超时钳制、配置读写与回滚、triage 合并、diff 计算、checkpoint 校验、修复注册表、历史读取与过滤、prune 清理报告与 dry-run 语义、UI 纯函数(select-all 键、运行时状态指引)、**v3.2:经验银行、质量门禁、防御事件、审批门禁 fail-open 路径**、**v3.9:可移植报告(markdown + SARIF)**、**v3.11:无损 JSON 报告(完整归一化运行模型 + `.iterate/report.json` 持久化)**等
|
|
449
449
|
|
|
450
450
|
---
|
|
451
451
|
|
package/dist/report-render.js
CHANGED
|
@@ -20,9 +20,14 @@
|
|
|
20
20
|
* - **sarif** — SARIF 2.1.0 for CI / GitHub Code Scanning (ruleId =
|
|
21
21
|
* review dimension, severity → error | warning | note, file+line
|
|
22
22
|
* locations, suggested fix attached).
|
|
23
|
+
* - **json** — the complete normalized run model (see {@link RunReportData})
|
|
24
|
+
* as a machine-readable JSON document. This is the ONLY format that carries
|
|
25
|
+
* the full model (findings + fixes + validations + quality gate + experience
|
|
26
|
+
* bank + defense events) without lossy projection, so CI dashboards and PR
|
|
27
|
+
* bots can consume the exact same facts the human report shows.
|
|
23
28
|
*/
|
|
24
29
|
/** Report formats accepted by the tool and the export buttons. */
|
|
25
|
-
export const REPORT_FORMATS = ['markdown', 'sarif'];
|
|
30
|
+
export const REPORT_FORMATS = ['markdown', 'sarif', 'json'];
|
|
26
31
|
/** Severity ordering (lowest index = most severe). */
|
|
27
32
|
const SEVERITY_ORDER = ['critical', 'high', 'medium', 'low'];
|
|
28
33
|
/** Human labels for the report, keyed by language. */
|
|
@@ -442,6 +447,30 @@ export function renderRunReportSarif(manifest, extra, opts) {
|
|
|
442
447
|
],
|
|
443
448
|
};
|
|
444
449
|
}
|
|
450
|
+
/** Render a JSON document from the same normalized data. */
|
|
451
|
+
export function renderRunReportJson(manifest, extra) {
|
|
452
|
+
const data = buildRunReportData(manifest, extra);
|
|
453
|
+
return {
|
|
454
|
+
$schema: 'iterate-run-report/1',
|
|
455
|
+
project: data.project,
|
|
456
|
+
goal: data.goal,
|
|
457
|
+
mode: data.mode,
|
|
458
|
+
taskMode: data.taskMode,
|
|
459
|
+
round: data.round,
|
|
460
|
+
maxRounds: data.maxRounds,
|
|
461
|
+
active: data.active,
|
|
462
|
+
stoppedReason: data.stoppedReason,
|
|
463
|
+
updatedAt: data.updatedAt,
|
|
464
|
+
convergence: data.convergence,
|
|
465
|
+
severityCounts: data.severityCounts,
|
|
466
|
+
findings: data.findings,
|
|
467
|
+
fixes: data.fixes,
|
|
468
|
+
validations: data.validations,
|
|
469
|
+
qualityGate: data.qualityGate,
|
|
470
|
+
experienceBank: data.experienceBank,
|
|
471
|
+
defenseEvents: data.defenseEvents,
|
|
472
|
+
};
|
|
473
|
+
}
|
|
445
474
|
/**
|
|
446
475
|
* Render a report in the requested format. Unknown formats fall back to
|
|
447
476
|
* Markdown (the tool's default).
|
|
@@ -449,6 +478,8 @@ export function renderRunReportSarif(manifest, extra, opts) {
|
|
|
449
478
|
export function renderRunReport(format, manifest, extra, opts) {
|
|
450
479
|
if (format === 'sarif')
|
|
451
480
|
return renderRunReportSarif(manifest, extra, opts);
|
|
481
|
+
if (format === 'json')
|
|
482
|
+
return renderRunReportJson(manifest, extra);
|
|
452
483
|
return renderRunReportMarkdown(manifest, extra, opts);
|
|
453
484
|
}
|
|
454
485
|
/**
|
|
@@ -456,7 +487,7 @@ export function renderRunReport(format, manifest, extra, opts) {
|
|
|
456
487
|
* `iterate-report-2026-10-18T01-02-03-000Z.md`.
|
|
457
488
|
*/
|
|
458
489
|
export function runReportFilename(format, opts) {
|
|
459
|
-
const ext = format === 'sarif' ? 'sarif' : 'md';
|
|
490
|
+
const ext = format === 'sarif' ? 'sarif' : format === 'json' ? 'json' : 'md';
|
|
460
491
|
const stamp = opts?.now && opts.now ? opts.now.replace(/[:.]/g, '-') : new Date().toISOString().replace(/[:.]/g, '-');
|
|
461
492
|
return `iterate-report-${stamp}.${ext}`;
|
|
462
493
|
}
|
package/dist/tools/report.js
CHANGED
|
@@ -29,7 +29,7 @@ import { readQualityGate } from "./quality-store.js";
|
|
|
29
29
|
import { readExperienceBank } from "./experience-store.js";
|
|
30
30
|
import { readDefenseEvents } from "./defense-store.js";
|
|
31
31
|
import { resultHeadline } from "./present.js";
|
|
32
|
-
import { renderRunReportMarkdown, renderRunReportSarif, REPORT_FORMATS } from "../report-render.js";
|
|
32
|
+
import { renderRunReportMarkdown, renderRunReportSarif, renderRunReportJson, REPORT_FORMATS } from "../report-render.js";
|
|
33
33
|
/** Formats accepted by the tool (kept in sync with the shared renderer). */
|
|
34
34
|
const FORMATS = new Set(REPORT_FORMATS);
|
|
35
35
|
/** Read the persisted transcript manifest, tolerating a missing/garbled file. */
|
|
@@ -68,19 +68,21 @@ export function registerReportTool(ctx) {
|
|
|
68
68
|
'quality-gate table, findings table, fixes table, per-round validations, defense events, and the ' +
|
|
69
69
|
'experience bank. `format:"sarif"` renders a SARIF 2.1.0 log for CI / GitHub Code Scanning ' +
|
|
70
70
|
'(one rule per review dimension; finding severity maps to error/warning/note; findings carry ' +
|
|
71
|
-
'file + line locations and the suggested fix). ' +
|
|
71
|
+
'file + line locations and the suggested fix). `format:"json"` renders the COMPLETE normalized run ' +
|
|
72
|
+
'model as machine-readable JSON (findings, fixes, validations, convergence, quality gate, ' +
|
|
73
|
+
'experience bank, defense events) — the lossless format for CI dashboards and PR bots. ' +
|
|
72
74
|
'Set `write:true` to also persist the report under `.iterate/report.md` / `.iterate/report.sarif` ' +
|
|
73
|
-
'and get the path back. `lang` selects the report language (zh | en). ' +
|
|
75
|
+
'/ `.iterate/report.json` and get the path back. `lang` selects the report language (zh | en). ' +
|
|
74
76
|
'Read-only apart from the optional report file; never touches source files.',
|
|
75
77
|
parameters: {
|
|
76
78
|
format: {
|
|
77
79
|
type: 'string',
|
|
78
|
-
description: 'Report format: "markdown" (default) or "
|
|
79
|
-
enum: ['markdown', 'sarif'],
|
|
80
|
+
description: 'Report format: "markdown" (default), "sarif", or "json".',
|
|
81
|
+
enum: ['markdown', 'sarif', 'json'],
|
|
80
82
|
},
|
|
81
83
|
write: {
|
|
82
84
|
type: 'boolean',
|
|
83
|
-
description: 'When true, persist the report to `.iterate/report.md` / `.iterate/report.sarif` and return its path. Default: false.',
|
|
85
|
+
description: 'When true, persist the report to `.iterate/report.md` / `.iterate/report.sarif` / `.iterate/report.json` and return its path. Default: false.',
|
|
84
86
|
},
|
|
85
87
|
lang: {
|
|
86
88
|
type: 'string',
|
|
@@ -100,10 +102,12 @@ export function registerReportTool(ctx) {
|
|
|
100
102
|
ok: { type: 'boolean', required: true },
|
|
101
103
|
kind: { type: 'string' },
|
|
102
104
|
format: { type: 'string' },
|
|
103
|
-
/** The rendered report text (Markdown, or pretty-printed SARIF
|
|
105
|
+
/** The rendered report text (Markdown, or pretty-printed SARIF/JSON). */
|
|
104
106
|
content: { type: 'string' },
|
|
105
107
|
/** The SARIF log object, when format is "sarif". */
|
|
106
108
|
sarif: { type: 'json' },
|
|
109
|
+
/** The full normalized run model, when format is "json". */
|
|
110
|
+
json: { type: 'json' },
|
|
107
111
|
/** Whether a source transcript manifest was found on disk. */
|
|
108
112
|
source: { type: 'string' },
|
|
109
113
|
written: { type: 'boolean' },
|
|
@@ -156,6 +160,7 @@ export function registerReportTool(ctx) {
|
|
|
156
160
|
const extra = { qualityGate, experienceBank, defenseEvents };
|
|
157
161
|
let content;
|
|
158
162
|
let sarif;
|
|
163
|
+
let json;
|
|
159
164
|
let findings = 0;
|
|
160
165
|
try {
|
|
161
166
|
if (format === 'sarif') {
|
|
@@ -166,6 +171,12 @@ export function registerReportTool(ctx) {
|
|
|
166
171
|
content = JSON.stringify(doc, null, 2);
|
|
167
172
|
findings = Array.isArray(doc?.runs?.[0]?.results) ? doc.runs[0].results.length : 0;
|
|
168
173
|
}
|
|
174
|
+
else if (format === 'json') {
|
|
175
|
+
const doc = renderRunReportJson(manifest, extra);
|
|
176
|
+
json = doc;
|
|
177
|
+
content = JSON.stringify(doc, null, 2);
|
|
178
|
+
findings = doc.findings.length;
|
|
179
|
+
}
|
|
169
180
|
else {
|
|
170
181
|
content = renderRunReportMarkdown(manifest, extra, { lang });
|
|
171
182
|
findings = countMarkdownFindings(content);
|
|
@@ -185,7 +196,7 @@ export function registerReportTool(ctx) {
|
|
|
185
196
|
try {
|
|
186
197
|
const dir = iterateDir(projectRoot);
|
|
187
198
|
mkdirSync(dir, { recursive: true });
|
|
188
|
-
reportPath = `${dir}/report.${format === 'sarif' ? 'sarif' : 'md'}`;
|
|
199
|
+
reportPath = `${dir}/report.${format === 'sarif' ? 'sarif' : format === 'json' ? 'json' : 'md'}`;
|
|
189
200
|
writeFileSync(reportPath, content, 'utf-8');
|
|
190
201
|
written = true;
|
|
191
202
|
}
|
|
@@ -204,6 +215,7 @@ export function registerReportTool(ctx) {
|
|
|
204
215
|
format,
|
|
205
216
|
content,
|
|
206
217
|
...(sarif !== undefined ? { sarif } : {}),
|
|
218
|
+
...(json !== undefined ? { json } : {}),
|
|
207
219
|
source,
|
|
208
220
|
written,
|
|
209
221
|
...(reportPath ? { reportPath } : {}),
|
package/lib/client.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* iterate-plugin client source-digest:
|
|
1
|
+
/* iterate-plugin client source-digest: e3baa31a1f6965343996a15a146b539f5c1d02547014ae0cf0b2f90809967b7e */
|
|
2
2
|
window.__ModuleLoader__.load({ id: "iterate-plugin", factory: (require) => {
|
|
3
3
|
var module = { exports: {} };
|
|
4
4
|
var exports = module.exports;
|
|
@@ -2311,8 +2311,31 @@ function renderRunReportSarif(manifest, extra, opts) {
|
|
|
2311
2311
|
]
|
|
2312
2312
|
};
|
|
2313
2313
|
}
|
|
2314
|
+
function renderRunReportJson(manifest, extra) {
|
|
2315
|
+
const data = buildRunReportData(manifest, extra);
|
|
2316
|
+
return {
|
|
2317
|
+
$schema: "iterate-run-report/1",
|
|
2318
|
+
project: data.project,
|
|
2319
|
+
goal: data.goal,
|
|
2320
|
+
mode: data.mode,
|
|
2321
|
+
taskMode: data.taskMode,
|
|
2322
|
+
round: data.round,
|
|
2323
|
+
maxRounds: data.maxRounds,
|
|
2324
|
+
active: data.active,
|
|
2325
|
+
stoppedReason: data.stoppedReason,
|
|
2326
|
+
updatedAt: data.updatedAt,
|
|
2327
|
+
convergence: data.convergence,
|
|
2328
|
+
severityCounts: data.severityCounts,
|
|
2329
|
+
findings: data.findings,
|
|
2330
|
+
fixes: data.fixes,
|
|
2331
|
+
validations: data.validations,
|
|
2332
|
+
qualityGate: data.qualityGate,
|
|
2333
|
+
experienceBank: data.experienceBank,
|
|
2334
|
+
defenseEvents: data.defenseEvents
|
|
2335
|
+
};
|
|
2336
|
+
}
|
|
2314
2337
|
function runReportFilename(format, opts) {
|
|
2315
|
-
const ext = format === "sarif" ? "sarif" : "md";
|
|
2338
|
+
const ext = format === "sarif" ? "sarif" : format === "json" ? "json" : "md";
|
|
2316
2339
|
const stamp = opts?.now && opts.now ? opts.now.replace(/[:.]/g, "-") : (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
2317
2340
|
return `iterate-report-${stamp}.${ext}`;
|
|
2318
2341
|
}
|
|
@@ -2484,6 +2507,8 @@ var EN = {
|
|
|
2484
2507
|
"\u5BFC\u51FA\u53EF\u7C98\u8D34\u8FDB PR \u7684 Markdown \u8FD0\u884C\u62A5\u544A\uFF08\u4F18\u5148\u4E0B\u8F7D\uFF0C\u5931\u8D25\u5219\u590D\u5236\uFF09": "Export a PR-pasteable Markdown run report (download first, copy on failure)",
|
|
2485
2508
|
"\u5BFC\u51FA SARIF": "Export SARIF",
|
|
2486
2509
|
"\u5BFC\u51FA SARIF 2.1.0\uFF08\u4F9B CI / GitHub Code Scanning \u6D88\u8D39\uFF09": "Export SARIF 2.1.0 for CI / GitHub Code Scanning",
|
|
2510
|
+
"\u5BFC\u51FA JSON \u62A5\u544A": "Export JSON report",
|
|
2511
|
+
"\u5BFC\u51FA\u5B8C\u6574\u8FD0\u884C\u6A21\u578B\u7684 JSON\uFF08\u4F9B CI \u4EEA\u8868\u76D8 / PR \u673A\u5668\u4EBA\u6D88\u8D39\uFF0C\u65E0\u635F\u683C\u5F0F\uFF09": "Export the complete run model as JSON (lossless, for CI dashboards / PR bots)",
|
|
2487
2512
|
"\u89C2\u6D4B\u53F0\u9875\u7B7E\uFF08\u5FEB\u6377\u952E 1-9 / 0 / -\uFF09": "Observatory tabs (keys 1-9 / 0 / -)",
|
|
2488
2513
|
"{a0}\uFF08\u6309 {a1} \u5207\u6362\uFF09": "{a0} (press {a1})",
|
|
2489
2514
|
"{a0}\uFF08\u6309 - \u5207\u6362\uFF09": "{a0} (press -)",
|
|
@@ -4495,7 +4520,7 @@ function ObservatoryPanel(props) {
|
|
|
4495
4520
|
const exportReport = (format) => {
|
|
4496
4521
|
if (!manifest) return;
|
|
4497
4522
|
const extra = { qualityGate, experienceBank, defenseEvents };
|
|
4498
|
-
const content = format === "sarif" ? JSON.stringify(renderRunReportSarif(manifest, extra), null, 2) : renderRunReportMarkdown(manifest, extra, { lang: getLang() });
|
|
4523
|
+
const content = format === "sarif" ? JSON.stringify(renderRunReportSarif(manifest, extra), null, 2) : format === "json" ? JSON.stringify(renderRunReportJson(manifest, extra), null, 2) : renderRunReportMarkdown(manifest, extra, { lang: getLang() });
|
|
4499
4524
|
const filename = runReportFilename(format);
|
|
4500
4525
|
const key = `report-${format}`;
|
|
4501
4526
|
const flash = () => {
|
|
@@ -5711,8 +5736,8 @@ function ObservatoryPanel(props) {
|
|
|
5711
5736
|
title: t("\u5C06\u89C2\u6D4B\u53F0\u5168\u90E8\u6570\u636E\u5BFC\u51FA\u4E3A JSON\uFF08\u4F18\u5148\u4E0B\u8F7D\uFF0C\u5931\u8D25\u5219\u590D\u5236\uFF09")
|
|
5712
5737
|
}, copiedKey === "export" ? t("\u5DF2\u5BFC\u51FA") : t("\u5BFC\u51FA JSON")),
|
|
5713
5738
|
// v3.9: portable report export — Markdown for a PR body, SARIF for
|
|
5714
|
-
// CI / GitHub Code Scanning
|
|
5715
|
-
// the `iterate_report` tool.
|
|
5739
|
+
// CI / GitHub Code Scanning, JSON for CI dashboards / PR bots.
|
|
5740
|
+
// Rendered from the same data model as the `iterate_report` tool.
|
|
5716
5741
|
React.createElement("button", {
|
|
5717
5742
|
className: "iterate-btn",
|
|
5718
5743
|
"data-copied": copiedKey === "report-markdown" ? "" : void 0,
|
|
@@ -5725,6 +5750,15 @@ function ObservatoryPanel(props) {
|
|
|
5725
5750
|
onClick: () => exportReport("sarif"),
|
|
5726
5751
|
title: t("\u5BFC\u51FA SARIF 2.1.0\uFF08\u4F9B CI / GitHub Code Scanning \u6D88\u8D39\uFF09")
|
|
5727
5752
|
}, copiedKey === "report-sarif" ? t("\u5DF2\u5BFC\u51FA") : t("\u5BFC\u51FA SARIF")),
|
|
5753
|
+
// v3.11: lossless JSON export — the complete normalized run model
|
|
5754
|
+
// (findings + fixes + validations + quality gate + experience bank
|
|
5755
|
+
// + defense events) for CI dashboards and PR bots.
|
|
5756
|
+
React.createElement("button", {
|
|
5757
|
+
className: "iterate-btn",
|
|
5758
|
+
"data-copied": copiedKey === "report-json" ? "" : void 0,
|
|
5759
|
+
onClick: () => exportReport("json"),
|
|
5760
|
+
title: t("\u5BFC\u51FA\u5B8C\u6574\u8FD0\u884C\u6A21\u578B\u7684 JSON\uFF08\u4F9B CI \u4EEA\u8868\u76D8 / PR \u673A\u5668\u4EBA\u6D88\u8D39\uFF0C\u65E0\u635F\u683C\u5F0F\uFF09")
|
|
5761
|
+
}, copiedKey === "report-json" ? t("\u5DF2\u5BFC\u51FA") : t("\u5BFC\u51FA JSON \u62A5\u544A")),
|
|
5728
5762
|
// Gap #4: `.iterate/` artifacts are unreachable from the browser;
|
|
5729
5763
|
// this copies the one instruction that makes the model echo them
|
|
5730
5764
|
// into the session stream (which is what every panel scans).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "iterate-plugin",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.11.0",
|
|
4
4
|
"description": "dsh plugin that turns the iterate skill into an autonomous closed-loop harness with quality command center and experience bank (v3.3). Features: plan -> parallel review xN -> atomic fixes -> validate -> loop -> auto-stop, plus dry-run pure-review mode, quality gate compute/persist, writable experience bank, defense events stream (record + bilingual labels), and native command buttons — with a live quality command center (F8/F9/F10 render real session data), §8 assign-fix instruction, and task_mode indicator wired end to end.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -52,7 +52,9 @@
|
|
|
52
52
|
"0.2.1-alpha.5": "compatible",
|
|
53
53
|
"0.2.1-rc.1": "compatible",
|
|
54
54
|
"0.2.1-rc.2": "compatible",
|
|
55
|
-
"0.2.1-rc.3": "compatible"
|
|
55
|
+
"0.2.1-rc.3": "compatible",
|
|
56
|
+
"0.2.1-rc.4": "compatible",
|
|
57
|
+
"0.2.2-alpha.2": "compatible"
|
|
56
58
|
}
|
|
57
59
|
},
|
|
58
60
|
"client": {
|
|
@@ -93,10 +95,10 @@
|
|
|
93
95
|
"js-yaml": "4.3.2"
|
|
94
96
|
},
|
|
95
97
|
"peerDependencies": {
|
|
96
|
-
"@deepseek-ai/cordis": "^4.0.5-alpha.2",
|
|
97
|
-
"@deepseek-ai/dsh-session": "0.1.1-rc.1 || 0.1.2-alpha.4 || 0.1.2-alpha.5 || 0.1.2-rc.1 || 0.1.5-alpha.1 || 0.1.5-alpha.2 || 0.1.5-rc.1 || 0.1.5-rc.2 || 0.1.6-alpha.1 || 0.1.6-alpha.2 || 0.1.7-alpha.1 || 0.1.7-alpha.2 || 0.1.7-rc.1 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2 || 0.2.1-alpha.3 || 0.2.1-alpha.4 || 0.2.1-alpha.5 || 0.2.1-rc.1 || 0.2.1-rc.2 || 0.2.1-rc.3",
|
|
98
|
-
"@deepseek-ai/dsh-tools": "0.1.1-rc.1 || 0.1.2-alpha.4 || 0.1.2-alpha.5 || 0.1.2-rc.1 || 0.1.5-alpha.1 || 0.1.5-alpha.2 || 0.1.5-rc.1 || 0.1.5-rc.2 || 0.1.6-alpha.1 || 0.1.6-alpha.2 || 0.1.7-alpha.1 || 0.1.7-alpha.2 || 0.1.7-rc.1 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2 || 0.2.1-alpha.3 || 0.2.1-alpha.4 || 0.2.1-alpha.5 || 0.2.1-rc.1 || 0.2.1-rc.2 || 0.2.1-rc.3",
|
|
99
|
-
"@deepseek-ai/dsh-util-values": "0.1.2-alpha.5 || 0.1.2-rc.1 || 0.1.5-alpha.1 || 0.1.5-alpha.2 || 0.1.5-rc.1 || 0.1.5-rc.2 || 0.1.6-alpha.1 || 0.1.6-alpha.2 || 0.1.7-alpha.1 || 0.1.7-alpha.2 || 0.1.7-rc.1 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2 || 0.2.1-alpha.3 || 0.2.1-alpha.4 || 0.2.1-alpha.5 || 0.2.1-rc.1 || 0.2.1-rc.2 || 0.2.1-rc.3"
|
|
98
|
+
"@deepseek-ai/cordis": "^4.0.5-alpha.2 || ~4.0.6-alpha.1",
|
|
99
|
+
"@deepseek-ai/dsh-session": "0.1.1-rc.1 || 0.1.2-alpha.4 || 0.1.2-alpha.5 || 0.1.2-rc.1 || 0.1.5-alpha.1 || 0.1.5-alpha.2 || 0.1.5-rc.1 || 0.1.5-rc.2 || 0.1.6-alpha.1 || 0.1.6-alpha.2 || 0.1.7-alpha.1 || 0.1.7-alpha.2 || 0.1.7-rc.1 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2 || 0.2.1-alpha.3 || 0.2.1-alpha.4 || 0.2.1-alpha.5 || 0.2.1-rc.1 || 0.2.1-rc.2 || 0.2.1-rc.3 || 0.2.1-rc.4 || 0.2.2-alpha.2",
|
|
100
|
+
"@deepseek-ai/dsh-tools": "0.1.1-rc.1 || 0.1.2-alpha.4 || 0.1.2-alpha.5 || 0.1.2-rc.1 || 0.1.5-alpha.1 || 0.1.5-alpha.2 || 0.1.5-rc.1 || 0.1.5-rc.2 || 0.1.6-alpha.1 || 0.1.6-alpha.2 || 0.1.7-alpha.1 || 0.1.7-alpha.2 || 0.1.7-rc.1 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2 || 0.2.1-alpha.3 || 0.2.1-alpha.4 || 0.2.1-alpha.5 || 0.2.1-rc.1 || 0.2.1-rc.2 || 0.2.1-rc.3 || 0.2.1-rc.4 || 0.2.2-alpha.2",
|
|
101
|
+
"@deepseek-ai/dsh-util-values": "0.1.2-alpha.5 || 0.1.2-rc.1 || 0.1.5-alpha.1 || 0.1.5-alpha.2 || 0.1.5-rc.1 || 0.1.5-rc.2 || 0.1.6-alpha.1 || 0.1.6-alpha.2 || 0.1.7-alpha.1 || 0.1.7-alpha.2 || 0.1.7-rc.1 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2 || 0.2.1-alpha.3 || 0.2.1-alpha.4 || 0.2.1-alpha.5 || 0.2.1-rc.1 || 0.2.1-rc.2 || 0.2.1-rc.3 || 0.2.1-rc.4 || 0.2.2-alpha.2"
|
|
100
102
|
},
|
|
101
103
|
"peerDependenciesMeta": {
|
|
102
104
|
"@deepseek-ai/cordis": {
|
|
@@ -113,20 +115,20 @@
|
|
|
113
115
|
}
|
|
114
116
|
},
|
|
115
117
|
"devDependencies": {
|
|
116
|
-
"@deepseek-ai/cordis": "4.0.
|
|
117
|
-
"@deepseek-ai/dsh-agent": "0.2.
|
|
118
|
-
"@deepseek-ai/dsh-jobs": "0.2.
|
|
119
|
-
"@deepseek-ai/dsh-llm": "0.2.
|
|
120
|
-
"@deepseek-ai/dsh-ptc-runtime": "0.2.
|
|
121
|
-
"@deepseek-ai/dsh-sandbox": "0.2.
|
|
122
|
-
"@deepseek-ai/dsh-sandbox-policy": "0.2.
|
|
123
|
-
"@deepseek-ai/dsh-scope": "0.2.
|
|
124
|
-
"@deepseek-ai/dsh-session": "0.2.
|
|
125
|
-
"@deepseek-ai/dsh-system-prompt": "0.2.
|
|
126
|
-
"@deepseek-ai/dsh-tools": "0.2.
|
|
127
|
-
"@deepseek-ai/dsh-user-approval": "0.2.
|
|
128
|
-
"@deepseek-ai/dsh-util-values": "0.2.
|
|
129
|
-
"@deepseek-ai/dsh-working-directory": "0.2.
|
|
118
|
+
"@deepseek-ai/cordis": "4.0.6-alpha.1",
|
|
119
|
+
"@deepseek-ai/dsh-agent": "0.2.2-alpha.2",
|
|
120
|
+
"@deepseek-ai/dsh-jobs": "0.2.2-alpha.2",
|
|
121
|
+
"@deepseek-ai/dsh-llm": "0.2.2-alpha.2",
|
|
122
|
+
"@deepseek-ai/dsh-ptc-runtime": "0.2.2-alpha.2",
|
|
123
|
+
"@deepseek-ai/dsh-sandbox": "0.2.2-alpha.2",
|
|
124
|
+
"@deepseek-ai/dsh-sandbox-policy": "0.2.2-alpha.2",
|
|
125
|
+
"@deepseek-ai/dsh-scope": "0.2.2-alpha.2",
|
|
126
|
+
"@deepseek-ai/dsh-session": "0.2.2-alpha.2",
|
|
127
|
+
"@deepseek-ai/dsh-system-prompt": "0.2.2-alpha.2",
|
|
128
|
+
"@deepseek-ai/dsh-tools": "0.2.2-alpha.2",
|
|
129
|
+
"@deepseek-ai/dsh-user-approval": "0.2.2-alpha.2",
|
|
130
|
+
"@deepseek-ai/dsh-util-values": "0.2.2-alpha.2",
|
|
131
|
+
"@deepseek-ai/dsh-working-directory": "0.2.2-alpha.2",
|
|
130
132
|
"@types/js-yaml": "4.0.9",
|
|
131
133
|
"@types/node": "22.15.0",
|
|
132
134
|
"@types/react": "19.2.2",
|
package/src/client/i18n.ts
CHANGED
|
@@ -259,6 +259,9 @@ export const EN: Record<string, string> = {
|
|
|
259
259
|
'导出 SARIF': 'Export SARIF',
|
|
260
260
|
'导出 SARIF 2.1.0(供 CI / GitHub Code Scanning 消费)':
|
|
261
261
|
'Export SARIF 2.1.0 for CI / GitHub Code Scanning',
|
|
262
|
+
'导出 JSON 报告': 'Export JSON report',
|
|
263
|
+
'导出完整运行模型的 JSON(供 CI 仪表盘 / PR 机器人消费,无损格式)':
|
|
264
|
+
'Export the complete run model as JSON (lossless, for CI dashboards / PR bots)',
|
|
262
265
|
'观测台页签(快捷键 1-9 / 0 / -)': 'Observatory tabs (keys 1-9 / 0 / -)',
|
|
263
266
|
'{a0}(按 {a1} 切换)': '{a0} (press {a1})',
|
|
264
267
|
'{a0}(按 - 切换)': '{a0} (press -)',
|
package/src/client/index.ts
CHANGED
|
@@ -113,6 +113,7 @@ import {
|
|
|
113
113
|
import {
|
|
114
114
|
renderRunReportMarkdown, // Markdown run report (PR-pasteable)
|
|
115
115
|
renderRunReportSarif, // SARIF 2.1.0 log (CI / Code Scanning)
|
|
116
|
+
renderRunReportJson, // full normalized run model (CI dashboards / PR bots)
|
|
116
117
|
runReportFilename, // deterministic download filename
|
|
117
118
|
} from '../report-render.ts'
|
|
118
119
|
import {
|
|
@@ -2419,12 +2420,14 @@ function ObservatoryPanel(props: SlotProps) {
|
|
|
2419
2420
|
* SARIF 2.1.0 (CI / GitHub Code Scanning). Rendered from the SAME shared
|
|
2420
2421
|
* data model as the `iterate_report` tool, so the button and the tool can
|
|
2421
2422
|
* never disagree. Download-first with a copy fallback, like `exportObservatory`. */
|
|
2422
|
-
const exportReport = (format: 'markdown' | 'sarif') => {
|
|
2423
|
+
const exportReport = (format: 'markdown' | 'sarif' | 'json') => {
|
|
2423
2424
|
if (!manifest) return
|
|
2424
2425
|
const extra = { qualityGate, experienceBank, defenseEvents }
|
|
2425
2426
|
const content = format === 'sarif'
|
|
2426
2427
|
? JSON.stringify(renderRunReportSarif(manifest, extra), null, 2)
|
|
2427
|
-
:
|
|
2428
|
+
: format === 'json'
|
|
2429
|
+
? JSON.stringify(renderRunReportJson(manifest, extra), null, 2)
|
|
2430
|
+
: renderRunReportMarkdown(manifest, extra, { lang: getLang() })
|
|
2428
2431
|
const filename = runReportFilename(format)
|
|
2429
2432
|
const key = `report-${format}`
|
|
2430
2433
|
const flash = () => {
|
|
@@ -3481,8 +3484,8 @@ function ObservatoryPanel(props: SlotProps) {
|
|
|
3481
3484
|
title: t('将观测台全部数据导出为 JSON(优先下载,失败则复制)'),
|
|
3482
3485
|
}, copiedKey === 'export' ? t('已导出') : t('导出 JSON')),
|
|
3483
3486
|
// v3.9: portable report export — Markdown for a PR body, SARIF for
|
|
3484
|
-
// CI / GitHub Code Scanning
|
|
3485
|
-
// the `iterate_report` tool.
|
|
3487
|
+
// CI / GitHub Code Scanning, JSON for CI dashboards / PR bots.
|
|
3488
|
+
// Rendered from the same data model as the `iterate_report` tool.
|
|
3486
3489
|
React.createElement('button', {
|
|
3487
3490
|
className: 'iterate-btn', 'data-copied': copiedKey === 'report-markdown' ? '' : undefined,
|
|
3488
3491
|
onClick: () => exportReport('markdown'),
|
|
@@ -3493,6 +3496,14 @@ function ObservatoryPanel(props: SlotProps) {
|
|
|
3493
3496
|
onClick: () => exportReport('sarif'),
|
|
3494
3497
|
title: t('导出 SARIF 2.1.0(供 CI / GitHub Code Scanning 消费)'),
|
|
3495
3498
|
}, copiedKey === 'report-sarif' ? t('已导出') : t('导出 SARIF')),
|
|
3499
|
+
// v3.11: lossless JSON export — the complete normalized run model
|
|
3500
|
+
// (findings + fixes + validations + quality gate + experience bank
|
|
3501
|
+
// + defense events) for CI dashboards and PR bots.
|
|
3502
|
+
React.createElement('button', {
|
|
3503
|
+
className: 'iterate-btn', 'data-copied': copiedKey === 'report-json' ? '' : undefined,
|
|
3504
|
+
onClick: () => exportReport('json'),
|
|
3505
|
+
title: t('导出完整运行模型的 JSON(供 CI 仪表盘 / PR 机器人消费,无损格式)'),
|
|
3506
|
+
}, copiedKey === 'report-json' ? t('已导出') : t('导出 JSON 报告')),
|
|
3496
3507
|
// Gap #4: `.iterate/` artifacts are unreachable from the browser;
|
|
3497
3508
|
// this copies the one instruction that makes the model echo them
|
|
3498
3509
|
// into the session stream (which is what every panel scans).
|
package/src/report-render.ts
CHANGED
|
@@ -20,10 +20,15 @@
|
|
|
20
20
|
* - **sarif** — SARIF 2.1.0 for CI / GitHub Code Scanning (ruleId =
|
|
21
21
|
* review dimension, severity → error | warning | note, file+line
|
|
22
22
|
* locations, suggested fix attached).
|
|
23
|
+
* - **json** — the complete normalized run model (see {@link RunReportData})
|
|
24
|
+
* as a machine-readable JSON document. This is the ONLY format that carries
|
|
25
|
+
* the full model (findings + fixes + validations + quality gate + experience
|
|
26
|
+
* bank + defense events) without lossy projection, so CI dashboards and PR
|
|
27
|
+
* bots can consume the exact same facts the human report shows.
|
|
23
28
|
*/
|
|
24
29
|
|
|
25
30
|
/** Report formats accepted by the tool and the export buttons. */
|
|
26
|
-
export const REPORT_FORMATS = ['markdown', 'sarif'] as const
|
|
31
|
+
export const REPORT_FORMATS = ['markdown', 'sarif', 'json'] as const
|
|
27
32
|
|
|
28
33
|
/** Severity ordering (lowest index = most severe). */
|
|
29
34
|
const SEVERITY_ORDER = ['critical', 'high', 'medium', 'low'] as const
|
|
@@ -563,6 +568,66 @@ export function renderRunReportSarif(
|
|
|
563
568
|
}
|
|
564
569
|
}
|
|
565
570
|
|
|
571
|
+
/**
|
|
572
|
+
* Render the complete normalized run model as a JSON document.
|
|
573
|
+
*
|
|
574
|
+
* This is the lossless format: unlike Markdown (human projection) and SARIF
|
|
575
|
+
* (findings-only code-scanning log), the JSON carries EVERY fact the run
|
|
576
|
+
* recorded — findings, fixes, validations, convergence, the quality gate, the
|
|
577
|
+
* experience bank, and defense events — as structured data. CI dashboards and
|
|
578
|
+
* PR bots can consume it without re-parsing a human report. A missing or
|
|
579
|
+
* partial transcript still yields a valid document (empty arrays, null stores),
|
|
580
|
+
* so an export never fails because a run was interrupted.
|
|
581
|
+
*/
|
|
582
|
+
export interface RunReportJson {
|
|
583
|
+
$schema: 'iterate-run-report/1'
|
|
584
|
+
project: string
|
|
585
|
+
goal: string
|
|
586
|
+
mode: string | null
|
|
587
|
+
taskMode: string | null
|
|
588
|
+
round: number
|
|
589
|
+
maxRounds: number
|
|
590
|
+
active: boolean
|
|
591
|
+
stoppedReason: string | null
|
|
592
|
+
updatedAt: string
|
|
593
|
+
convergence: number[]
|
|
594
|
+
severityCounts: { critical: number; high: number; medium: number; low: number }
|
|
595
|
+
findings: ReportFinding[]
|
|
596
|
+
fixes: ReportFix[]
|
|
597
|
+
validations: ReportValidation[]
|
|
598
|
+
qualityGate: Record<string, unknown> | null
|
|
599
|
+
experienceBank: Record<string, unknown> | null
|
|
600
|
+
defenseEvents: Record<string, unknown> | null
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
/** Render a JSON document from the same normalized data. */
|
|
604
|
+
export function renderRunReportJson(
|
|
605
|
+
manifest: unknown,
|
|
606
|
+
extra?: { qualityGate?: unknown; experienceBank?: unknown; defenseEvents?: unknown } | null,
|
|
607
|
+
): RunReportJson {
|
|
608
|
+
const data = buildRunReportData(manifest, extra)
|
|
609
|
+
return {
|
|
610
|
+
$schema: 'iterate-run-report/1',
|
|
611
|
+
project: data.project,
|
|
612
|
+
goal: data.goal,
|
|
613
|
+
mode: data.mode,
|
|
614
|
+
taskMode: data.taskMode,
|
|
615
|
+
round: data.round,
|
|
616
|
+
maxRounds: data.maxRounds,
|
|
617
|
+
active: data.active,
|
|
618
|
+
stoppedReason: data.stoppedReason,
|
|
619
|
+
updatedAt: data.updatedAt,
|
|
620
|
+
convergence: data.convergence,
|
|
621
|
+
severityCounts: data.severityCounts,
|
|
622
|
+
findings: data.findings,
|
|
623
|
+
fixes: data.fixes,
|
|
624
|
+
validations: data.validations,
|
|
625
|
+
qualityGate: data.qualityGate,
|
|
626
|
+
experienceBank: data.experienceBank,
|
|
627
|
+
defenseEvents: data.defenseEvents,
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
|
|
566
631
|
/**
|
|
567
632
|
* Render a report in the requested format. Unknown formats fall back to
|
|
568
633
|
* Markdown (the tool's default).
|
|
@@ -572,8 +637,9 @@ export function renderRunReport(
|
|
|
572
637
|
manifest: unknown,
|
|
573
638
|
extra?: { qualityGate?: unknown; experienceBank?: unknown; defenseEvents?: unknown } | null,
|
|
574
639
|
opts?: ReportRenderOptions,
|
|
575
|
-
): string | SarifLog {
|
|
640
|
+
): string | SarifLog | RunReportJson {
|
|
576
641
|
if (format === 'sarif') return renderRunReportSarif(manifest, extra, opts)
|
|
642
|
+
if (format === 'json') return renderRunReportJson(manifest, extra)
|
|
577
643
|
return renderRunReportMarkdown(manifest, extra, opts)
|
|
578
644
|
}
|
|
579
645
|
|
|
@@ -582,7 +648,7 @@ export function renderRunReport(
|
|
|
582
648
|
* `iterate-report-2026-10-18T01-02-03-000Z.md`.
|
|
583
649
|
*/
|
|
584
650
|
export function runReportFilename(format: string, opts?: { now?: string }): string {
|
|
585
|
-
const ext = format === 'sarif' ? 'sarif' : 'md'
|
|
651
|
+
const ext = format === 'sarif' ? 'sarif' : format === 'json' ? 'json' : 'md'
|
|
586
652
|
const stamp =
|
|
587
653
|
opts?.now && opts.now ? opts.now.replace(/[:.]/g, '-') : new Date().toISOString().replace(/[:.]/g, '-')
|
|
588
654
|
return `iterate-report-${stamp}.${ext}`
|
package/src/tools/report.ts
CHANGED
|
@@ -31,7 +31,7 @@ import { readQualityGate } from './quality-store.ts'
|
|
|
31
31
|
import { readExperienceBank } from './experience-store.ts'
|
|
32
32
|
import { readDefenseEvents } from './defense-store.ts'
|
|
33
33
|
import { resultHeadline } from './present.ts'
|
|
34
|
-
import { renderRunReportMarkdown, renderRunReportSarif, REPORT_FORMATS } from '../report-render.ts'
|
|
34
|
+
import { renderRunReportMarkdown, renderRunReportSarif, renderRunReportJson, REPORT_FORMATS } from '../report-render.ts'
|
|
35
35
|
|
|
36
36
|
/** Formats accepted by the tool (kept in sync with the shared renderer). */
|
|
37
37
|
const FORMATS: ReadonlySet<string> = new Set(REPORT_FORMATS)
|
|
@@ -73,19 +73,21 @@ export function registerReportTool(ctx: { tools: { register: (def: ReturnType<ty
|
|
|
73
73
|
'quality-gate table, findings table, fixes table, per-round validations, defense events, and the ' +
|
|
74
74
|
'experience bank. `format:"sarif"` renders a SARIF 2.1.0 log for CI / GitHub Code Scanning ' +
|
|
75
75
|
'(one rule per review dimension; finding severity maps to error/warning/note; findings carry ' +
|
|
76
|
-
'file + line locations and the suggested fix). ' +
|
|
76
|
+
'file + line locations and the suggested fix). `format:"json"` renders the COMPLETE normalized run ' +
|
|
77
|
+
'model as machine-readable JSON (findings, fixes, validations, convergence, quality gate, ' +
|
|
78
|
+
'experience bank, defense events) — the lossless format for CI dashboards and PR bots. ' +
|
|
77
79
|
'Set `write:true` to also persist the report under `.iterate/report.md` / `.iterate/report.sarif` ' +
|
|
78
|
-
'and get the path back. `lang` selects the report language (zh | en). ' +
|
|
80
|
+
'/ `.iterate/report.json` and get the path back. `lang` selects the report language (zh | en). ' +
|
|
79
81
|
'Read-only apart from the optional report file; never touches source files.',
|
|
80
82
|
parameters: {
|
|
81
83
|
format: {
|
|
82
84
|
type: 'string',
|
|
83
|
-
description: 'Report format: "markdown" (default) or "
|
|
84
|
-
enum: ['markdown', 'sarif'],
|
|
85
|
+
description: 'Report format: "markdown" (default), "sarif", or "json".',
|
|
86
|
+
enum: ['markdown', 'sarif', 'json'],
|
|
85
87
|
},
|
|
86
88
|
write: {
|
|
87
89
|
type: 'boolean',
|
|
88
|
-
description: 'When true, persist the report to `.iterate/report.md` / `.iterate/report.sarif` and return its path. Default: false.',
|
|
90
|
+
description: 'When true, persist the report to `.iterate/report.md` / `.iterate/report.sarif` / `.iterate/report.json` and return its path. Default: false.',
|
|
89
91
|
},
|
|
90
92
|
lang: {
|
|
91
93
|
type: 'string',
|
|
@@ -106,10 +108,12 @@ export function registerReportTool(ctx: { tools: { register: (def: ReturnType<ty
|
|
|
106
108
|
ok: { type: 'boolean', required: true },
|
|
107
109
|
kind: { type: 'string' },
|
|
108
110
|
format: { type: 'string' },
|
|
109
|
-
/** The rendered report text (Markdown, or pretty-printed SARIF
|
|
111
|
+
/** The rendered report text (Markdown, or pretty-printed SARIF/JSON). */
|
|
110
112
|
content: { type: 'string' },
|
|
111
113
|
/** The SARIF log object, when format is "sarif". */
|
|
112
114
|
sarif: { type: 'json' },
|
|
115
|
+
/** The full normalized run model, when format is "json". */
|
|
116
|
+
json: { type: 'json' },
|
|
113
117
|
/** Whether a source transcript manifest was found on disk. */
|
|
114
118
|
source: { type: 'string' },
|
|
115
119
|
written: { type: 'boolean' },
|
|
@@ -161,6 +165,7 @@ export function registerReportTool(ctx: { tools: { register: (def: ReturnType<ty
|
|
|
161
165
|
const extra = { qualityGate, experienceBank, defenseEvents }
|
|
162
166
|
let content: string
|
|
163
167
|
let sarif: JsonValue | undefined
|
|
168
|
+
let json: JsonValue | undefined
|
|
164
169
|
let findings = 0
|
|
165
170
|
try {
|
|
166
171
|
if (format === 'sarif') {
|
|
@@ -170,6 +175,11 @@ export function registerReportTool(ctx: { tools: { register: (def: ReturnType<ty
|
|
|
170
175
|
sarif = doc as unknown as JsonValue
|
|
171
176
|
content = JSON.stringify(doc, null, 2)
|
|
172
177
|
findings = Array.isArray(doc?.runs?.[0]?.results) ? (doc.runs[0].results as unknown[]).length : 0
|
|
178
|
+
} else if (format === 'json') {
|
|
179
|
+
const doc = renderRunReportJson(manifest, extra)
|
|
180
|
+
json = doc as unknown as JsonValue
|
|
181
|
+
content = JSON.stringify(doc, null, 2)
|
|
182
|
+
findings = doc.findings.length
|
|
173
183
|
} else {
|
|
174
184
|
content = renderRunReportMarkdown(manifest, extra, { lang }) as unknown as string
|
|
175
185
|
findings = countMarkdownFindings(content)
|
|
@@ -189,7 +199,7 @@ export function registerReportTool(ctx: { tools: { register: (def: ReturnType<ty
|
|
|
189
199
|
try {
|
|
190
200
|
const dir = iterateDir(projectRoot)
|
|
191
201
|
mkdirSync(dir, { recursive: true })
|
|
192
|
-
reportPath = `${dir}/report.${format === 'sarif' ? 'sarif' : 'md'}`
|
|
202
|
+
reportPath = `${dir}/report.${format === 'sarif' ? 'sarif' : format === 'json' ? 'json' : 'md'}`
|
|
193
203
|
writeFileSync(reportPath, content, 'utf-8')
|
|
194
204
|
written = true
|
|
195
205
|
} catch (err) {
|
|
@@ -208,6 +218,7 @@ export function registerReportTool(ctx: { tools: { register: (def: ReturnType<ty
|
|
|
208
218
|
format,
|
|
209
219
|
content,
|
|
210
220
|
...(sarif !== undefined ? { sarif } : {}),
|
|
221
|
+
...(json !== undefined ? { json } : {}),
|
|
211
222
|
source,
|
|
212
223
|
written,
|
|
213
224
|
...(reportPath ? { reportPath } : {}),
|