paseo-req-kit 1.11.0 → 1.12.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +26 -0
- package/bin/req.mjs +1 -0
- package/config.example.json +3 -1
- package/contracts/cli-json.md +2 -0
- package/contracts/state.md +3 -1
- package/contracts/usage-record.md +4 -1
- package/docs/adr/0031-fewer-interventions.md +33 -0
- package/docs/adr/README.md +1 -0
- package/docs/guide//345/256/211/350/243/205/344/270/216/345/215/207/347/272/247.md +3 -2
- package/docs/guide//351/241/271/347/233/256/345/256/232/345/210/266/344/270/216/350/277/220/350/241/214/350/247/204/345/210/231.md +21 -3
- package/engine/prompts/04b-design-revise.md +15 -0
- package/engine/src/api.ts +33 -1
- package/engine/src/card-cmd.ts +8 -1
- package/engine/src/card.ts +9 -2
- package/engine/src/config.ts +23 -2
- package/engine/src/configure.ts +161 -36
- package/engine/src/control.ts +1 -0
- package/engine/src/factory.ts +48 -4
- package/engine/src/main.ts +1 -0
- package/engine/src/paseo-profiles.ts +2 -0
- package/engine/src/pipeline.ts +161 -26
- package/engine/src/records.ts +8 -0
- package/engine/src/state.ts +30 -0
- package/engine/src/stats.ts +90 -0
- package/engine/src/steps.ts +4 -1
- package/install.mjs +5 -1
- package/package.json +2 -2
- package/paseo-plugin/OVERVIEW.md +2 -0
- package/paseo-plugin/client/data.ts +15 -1
- package/paseo-plugin/client/models.tsx +117 -0
- package/paseo-plugin/client/settings.tsx +3 -1
- package/paseo-plugin/index.server.ts +3 -1
- package/paseo-plugin/package.json +1 -1
- package/paseo-plugin/server/handlers.ts +14 -1
- package/paseo-plugin/shared/contracts.ts +37 -0
- package/skills/req/playbooks/new.md +14 -14
- package/skills/req/playbooks/small.md +3 -3
- package/skills/req/playbooks/unattended.md +4 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,32 @@
|
|
|
2
2
|
|
|
3
3
|
每个版本分为"使用者会看到的变化"和"升级须知"。升级步骤见 [docs/guide/安装与升级.md](docs/guide/安装与升级.md)。
|
|
4
4
|
|
|
5
|
+
## 1.12.0-beta.2
|
|
6
|
+
|
|
7
|
+
1.12.0-beta.1 的内容(见下),加一处修正:
|
|
8
|
+
|
|
9
|
+
- **修正**:安装时自动选模型,Paseo 没有运行时会一直等待连接;现在 20 秒连不上就放弃,改为提示之后运行 `req configure --auto`。1.12.0-beta.1 因此没有发布。
|
|
10
|
+
|
|
11
|
+
## 1.12.0-beta.1
|
|
12
|
+
|
|
13
|
+
少等人:能自动处理的中断不再停下等人,选择题改为默认值,装好就选好模型。设计决定见 ADR 0031。
|
|
14
|
+
|
|
15
|
+
### 使用者会看到的变化
|
|
16
|
+
|
|
17
|
+
- **自愈**:步骤超时而 agent 还在做时,再等它一轮;agent 报错、没写出有效结果时,新开一个 agent 重做一次。限流重试用完时,预约在 Paseo 显示的额度恢复时间之后自动继续,而不是固定 30 分钟。
|
|
18
|
+
- **备用模型**(可选):配置 `agents.fallback` 后,报错重做、限流重试用完时换它把这一步做完。`req configure` 和插件的"模型"设置页会推荐一个。
|
|
19
|
+
- **方案审核的问题先自答**:审核说需要需求方决定时,先由 builder 从代码和文档中找答案,只把真正的业务取舍连同推荐答案交给需求方;都能回答时不停下。
|
|
20
|
+
- **后台进程意外退出后自动继续**:定时检查默认打开(每 60 分钟);接单器每次检查时也会发现并请对话继续。
|
|
21
|
+
- **装好就选好模型**:安装时自动选好已登录的 agent(两个不同厂商分别做 builder 和 reviewer,再选一个做备用),不再需要回答配置向导;`req configure --auto` 同样。插件设置中新增"模型"一页,可以直接修改。
|
|
22
|
+
- **`/req` 少问**:不再问是否新建工作区、档位、执行方式、现在开始还是预约,按推荐值直接走,确认摘要最后一行写明,想改再说;确认需求说明后立即开始。
|
|
23
|
+
- **卡片回复更宽松**:写"好的""没问题""按你说的来"也算同意;回复区只写了一句"好的"也算确认。卡片已经写清需求时,不再为提问而提问;问题附上推荐答案。
|
|
24
|
+
- **`req stats` 新增"人工介入"一节**:停下等人的原因、自动处理的次数、到达提测的需求中没有非必要介入的比例。
|
|
25
|
+
|
|
26
|
+
### 升级须知
|
|
27
|
+
|
|
28
|
+
- 新配置 `selfHeal`、`answerQuestionsFirst`(默认都是 `true`)、`agents.fallback`(可选);`watchdogMinutes` 默认从 0 改为 60。想保持以前的行为,分别设为 `false`、`false`、`0`。
|
|
29
|
+
- 状态文件新增 `stopKind`、`recoveries`,使用记录的 `run` 新增 `stopKind`、`recoveries`;旧文件照常可用,统计按状态推断旧记录的原因。
|
|
30
|
+
|
|
5
31
|
## 1.11.0
|
|
6
32
|
|
|
7
33
|
稳定版:包含 1.10 的"从卡片开始的需求"和 1.11 的接单器(详见下面的 beta 记录),并修正步骤超时后会重复开 agent 的问题。
|
package/bin/req.mjs
CHANGED
|
@@ -56,6 +56,7 @@ Paseo 插件(在 Paseo 中查看和管理需求):
|
|
|
56
56
|
本机维护:
|
|
57
57
|
req doctor [--no-project] 体检:安装、配置、daemon、当前项目
|
|
58
58
|
req configure 配置向导:选择 builder 和 reviewer 用的 agent
|
|
59
|
+
req configure --auto 不提问,按推荐选好 builder、reviewer 和备用模型
|
|
59
60
|
req upgrade [版本] [--beta] 升级到最新版本(或指定版本),自动选能支持本机 Paseo 的版本;
|
|
60
61
|
req upgrade --check 只看有没有新版本
|
|
61
62
|
req versions 已安装的版本和 npm 上的版本
|
package/config.example.json
CHANGED
package/contracts/cli-json.md
CHANGED
|
@@ -28,6 +28,8 @@
|
|
|
28
28
|
| `req api stats [--since 30d] [--month 2026-10]` | `req stats --json` 的内容,见 [使用记录](usage-record.md) |
|
|
29
29
|
| `req api index [--rebuild <目录>…] [--prune]` | 不带选项:索引的全部条目;`--rebuild`:`{ scanned, registered, pruned, total }` |
|
|
30
30
|
| `req api run <approve\|stop\|resume\|verify\|sync> <需求> [--repo <目录>] [选项]` | `{ command, featureDir, ok, exitCode, message }`。在 `--repo`(默认当前目录)中执行与 `req <命令> <需求>` 完全相同的操作;`ok` 为 false 时退出码为 1 |
|
|
31
|
+
| `req api models` | 1.12 起。`{ current, choices, recommended }`:本机配置中现在的 `agents`;daemon 上能无人值守运行的选择(`{ id, label, provider, profile }`,Paseo profile 在前);按推荐的 `agents`。连不上 daemon 时报错 |
|
|
32
|
+
| `req api set-agents --json <设置>` | 1.12 起。`{ ok, message }`。`<设置>` 是 `{ builder, reviewer, fallback }`(`fallback` 可以为 null),检查后写入本机配置的 `agents`,原文件备份 |
|
|
31
33
|
|
|
32
34
|
`<需求>` 的写法与 `req status` 相同:功能目录、目录名或名称中能唯一确定的一部分。需求在为它新建的工作区中开发时,`--repo` 写对话所在的目录或那个工作区都可以。
|
|
33
35
|
|
package/contracts/state.md
CHANGED
|
@@ -16,8 +16,10 @@
|
|
|
16
16
|
| `pid` | number | 流水线进程 |
|
|
17
17
|
| `startedAt` / `updatedAt` | ISO 时间 | |
|
|
18
18
|
| `results` | object | 已完成步骤的结果,键为步骤键(如 `code_review-r2`) |
|
|
19
|
-
| `history` | array | 每次步骤执行的记录:agent、起止时间、结果、错误;`usage`(可选,1.3.0 起)为 agent 报告的用量:`inputTokens`、`cachedInputTokens`、`outputTokens`、`totalCostUsd`,agent 没有报告时为 null
|
|
19
|
+
| `history` | array | 每次步骤执行的记录:agent、起止时间、结果、错误;`usage`(可选,1.3.0 起)为 agent 报告的用量:`inputTokens`、`cachedInputTokens`、`outputTokens`、`totalCostUsd`,agent 没有报告时为 null 或缺少;`leftRunning`(可选,1.11 起)为 true 表示失败时 agent 可能还在运行,下一次运行这一步时先看它;`reattached`(可选,1.11 起)为接着等上一次 agent 的次数 |
|
|
20
20
|
| `stopReason` / `outcome` | string \| null | 停止原因;最终结论 |
|
|
21
|
+
| `stopKind` | string \| null | 可选,1.12 起。停下等人的原因代码:必要的决定 `approval`(确认方案)、`phase_gate`(研发完成,等待测试)、`business`(需求方决定)、`reproduce`(缺陷复现不了);中断 `timeout`、`agent_error`、`invalid_result`、`transient`、`stopped`、`process_exit`、`other`。运行中或完成时为 null |
|
|
22
|
+
| `recoveries` | array | 可选,1.12 起。流水线自己处理掉的问题(最多保留 200 条):`at`、`key`(步骤键)、`kind`(`retry` 重试 / `reattach` 接着等上一次的 agent / `reuse_result` 沿用上一次 agent 的结果 / `extend_timeout` 超时后多等一轮 / `redo` 新开 agent 重做 / `fallback` 换备用模型 / `auto_resume` 预约自动继续 / `quota_wait` 预约在额度恢复后继续 / `self_answer` 方案审核的问题由 builder 自答)、`detail` |
|
|
21
23
|
| `profile` | `quick` \| `standard` | 档位 |
|
|
22
24
|
| `branch` | string \| null | 工作分支,启动时确定 |
|
|
23
25
|
| `milestones` | string[] | 已发送的进展通知,续跑时不重复发送 |
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
| `schema` / `kind` | `1` / `"run"` |
|
|
31
31
|
| `machine` / `kitVersion` | 主机名、req-kit 版本 |
|
|
32
32
|
| `requirement` | `{ key, project, name, title, type, profile }`;从卡片开始的需求(1.10 起)另有 `requester`(卡片的提出人)和 `source`(`{ system, id }`) |
|
|
33
|
-
| `run` | `{ startedAt, endedAt, attempt, phase, phaseMode, status, outcome, stopReason }`;`status` 是运行结束时的状态,`outcome`
|
|
33
|
+
| `run` | `{ startedAt, endedAt, attempt, phase, phaseMode, status, outcome, stopReason, stopKind, recoveries }`;`status` 是运行结束时的状态,`outcome` 是完成时报告的结论;`stopKind`(1.12 起)是停下等人的原因代码(见 [state.md](state.md)),`recoveries`(1.12 起)是这次运行中自动处理的次数,按种类,如 `{ "retry": 2 }`。更早的记录没有这两项 |
|
|
34
34
|
| `agents` | `{ builder, reviewer }`:当时配置的 agent(如 `claude/opus(bypassPermissions)`) |
|
|
35
35
|
| `steps` | 这次运行中开始的步骤:`{ key, stepId, phase, attempt, round, startedAt, endedAt, minutes, outcome, inputTokens, outputTokens, costUsd }`;agent 没有报告的用量为 null |
|
|
36
36
|
| `gate` | `{ runs, failed }`:这次运行中质量门运行的次数、发现新失败的次数 |
|
|
@@ -43,6 +43,9 @@
|
|
|
43
43
|
- 交付周期:需求第一次开始到完成(最后一次运行以"已完成"结束)的小时数,用这个需求的全部运行,算在完成的那段时间。
|
|
44
44
|
- 代码评审轮数、缺陷修复轮数:不同的(尝试、轮次、仓库)个数;评审分方向或评审小组的几个成员算一轮。
|
|
45
45
|
- 等待:相邻两次运行之间的间隔,按前一次结束时的状态归类:等待确认方案、等待需求方决定、研发完成到开始测试验证、中断到继续。
|
|
46
|
+
- 停下等人的原因(1.12 起):这段时间结束的运行按 `stopKind` 计数;更早的记录按结束时的状态推断。确认方案、研发完成等待测试是必要的决定。
|
|
47
|
+
- 无人工到提测的比例(1.12 起):这段时间有进展、已经研发完成或完成的需求中,所有运行都只停在必要决定处的比例。
|
|
48
|
+
- 自动处理的比例(1.12 起):自动处理的次数 ÷(自动处理的次数 + 以中断结束的运行数);需求方决定、缺陷复现不了不算中断。
|
|
46
49
|
- 合并多台机器的导出时,同一机器、同一需求、同一开始时间的运行只算一次。
|
|
47
50
|
|
|
48
51
|
`req stats --json` 和 `req api stats` 输出同样的对象(`schema: 1`):`period`、`machines`、`active`、`completed`、`byType`、`cycleHours`、`steps`、`rework`、`waits`、`usage`、`byBuilder`、`requirements`(每个需求一项,字段与 `req stats --csv` 的列对应)。
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# 0031 少等人:每种中断都有自动的下一步
|
|
2
|
+
|
|
3
|
+
- 状态:已采纳(1.12.0-beta.1)
|
|
4
|
+
|
|
5
|
+
## 背景
|
|
6
|
+
|
|
7
|
+
目标是需求方只在三处介入:确认需求、确认方案(只有界面改动时)、验收合并。核对代码、默认配置和 `/req` 手册后,现在要人的地方共 16 处,其中只有这 3 处是必要的决定;其余多数来自两类:
|
|
8
|
+
|
|
9
|
+
- **中断等人继续**:步骤超时、agent 报错、结果文件无效直接中断;限流重试用完只按固定间隔自动继续两次;后台进程意外退出的定时检查默认关闭。
|
|
10
|
+
- **可以有默认值的选择题**:是否新建工作区、档位、执行方式、现在开始还是预约;装好后要运行配置向导选模型;方案审核提出的问题不加筛选地交给需求方。
|
|
11
|
+
|
|
12
|
+
另外,过去没有记录每次停下等人的原因,无法用数据判断先改哪里。
|
|
13
|
+
|
|
14
|
+
## 决定
|
|
15
|
+
|
|
16
|
+
- **先记录**:状态文件和使用记录加上 `stopKind`(停下等人的原因代码)和 `recoveries`(自己处理掉的问题),`req stats` 新增"人工介入"一节。用两周的真实数据复核后续的优先级。
|
|
17
|
+
- **自愈**(`selfHeal`,默认开):
|
|
18
|
+
- 超时:agent 还在做就再等同样长的时间,不打断它;仍未完成才中断(1.11 已保证继续时不会另起一个 agent)。
|
|
19
|
+
- agent 报错、结果文件无效:新开一个 agent 重做一次。只重做一次,避免一个确实做不了的步骤反复消耗额度。
|
|
20
|
+
- **备用模型**(`agents.fallback`,可选):报错重做、限流重试用完时换它做一次。只用一次、只在这一步,下一步仍用主模型。
|
|
21
|
+
- **按额度恢复时间预约**:限流中断时,读 Paseo 报告的额度恢复时间,预约在恢复后 5 分钟自动继续;读不到时仍按 `autoResumeMinutes`。
|
|
22
|
+
- **后台进程退出后续跑**:个人机器的定时检查默认打开(60 分钟);接单器每次检查时发现"运行中但进程已不在"的流水线,请它的对话检查并继续,每个进程只提醒一次。
|
|
23
|
+
- **方案审核的问题先自答**(`answerQuestionsFirst`,默认开):审核结论为"需要需求方决定"时,先让 builder 在方案修订中从代码、文档和需求说明里找答案;只把真正的业务取舍连同推荐答案交给需求方,都能回答时不停下。
|
|
24
|
+
- **装好就能用**:安装时 `req configure --auto` 不提问,按推荐选好已登录的 agent(两个不同厂商分别做 builder、reviewer,再选一个做备用,都用免确认模式)。插件新增"模型"设置页,列出 Paseo 中已登录的 agent,选择后写回本机配置,与 `req configure` 用同一套逻辑。
|
|
25
|
+
- **默认值代替选择题**:`/req` 不再问是否新建工作区、档位、执行方式、现在开始还是预约,按推荐值直接走,在确认摘要的最后一行写明,想改再说;确认需求说明就是同意开始。
|
|
26
|
+
- **卡片回复更宽松**:回复区写"好的""没问题""按你说的来"也算同意;只写了这样一句、没有别的内容时,按正在等待的确认(需求说明或方案)处理。卡片已经写清需求时,需求会话不为提问而提问。
|
|
27
|
+
|
|
28
|
+
## 影响
|
|
29
|
+
|
|
30
|
+
- 新配置:`selfHeal`、`answerQuestionsFirst`、`agents.fallback`;`watchdogMinutes` 默认从 0 改为 60。想保持以前行为的,把它们设为 `false` / `0`。
|
|
31
|
+
- 方案修订的结果文件可以带 `business_questions`。
|
|
32
|
+
- 新命令:`req configure --auto`、`req api models`、`req api set-agents`。
|
|
33
|
+
- 自动处理会多用一些额度(多等一轮、重做一次);每次都写进进度日志和使用记录,可以查到。
|
package/docs/adr/README.md
CHANGED
|
@@ -32,3 +32,4 @@ req-kit 自身的设计决定。修改其中任何一条,请新增一条 ADR
|
|
|
32
32
|
- [0028 外部系统对接:命令写法在配置里,内容由 AI 整理,调用是确定的](0028-external-integrations.md)
|
|
33
33
|
- [0029 从卡片开始的需求:需求会话代替人,卡片描述代替对话](0029-requirements-from-cards.md)
|
|
34
34
|
- [0030 接单器:共享机器上定时接卡片、转达回复](0030-card-intake-process.md)
|
|
35
|
+
- [0031 少等人:每种中断都有自动的下一步](0031-fewer-interventions.md)
|
|
@@ -53,7 +53,7 @@ curl -fsSL https://paseo-req-kit.vercel.app/install.sh | sh
|
|
|
53
53
|
irm https://paseo-req-kit.vercel.app/install.ps1 | iex
|
|
54
54
|
```
|
|
55
55
|
|
|
56
|
-
脚本依次:检查 Node.js 22+、npm、git;从 npm 安装 `paseo-req-kit`,npm 上没有时改用 GitHub Release 的安装包;npm 全局目录不可写时装到 `~/.req-kit/npm`(不需要 sudo);**检查 Paseo**(见下);运行 `req setup`;本机配置还没填时运行 `req configure
|
|
56
|
+
脚本依次:检查 Node.js 22+、npm、git;从 npm 安装 `paseo-req-kit`,npm 上没有时改用 GitHub Release 的安装包;npm 全局目录不可写时装到 `~/.req-kit/npm`(不需要 sudo);**检查 Paseo**(见下);运行 `req setup`;本机配置还没填时运行 `req configure --auto`(1.12:不提问,按推荐选好已登录的 agent:两个不同厂商的分别做 builder 和 reviewer,再选一个做备用模型,都用免确认模式;自动选不了时改为逐项选择);最后提示把 `req` 加入 PATH(如需要)。选项:`install.sh` 写在 `sh -s --` 之后(`--beta`、`--version <版本>`、`--from npm|release`、`--no-configure`、`--skip-paseo`);`install.ps1` 用变量(`$ReqKitBeta = $true; irm … | iex`,还有 `$ReqKitVersion`、`$ReqKitFrom`、`$ReqKitNoConfigure`、`$ReqKitSkipPaseo`)。
|
|
57
57
|
|
|
58
58
|
检查 Paseo 这一步:
|
|
59
59
|
|
|
@@ -69,7 +69,8 @@ irm https://paseo-req-kit.vercel.app/install.ps1 | iex
|
|
|
69
69
|
```bash
|
|
70
70
|
npm i -g paseo-req-kit # beta 版:npm i -g paseo-req-kit@beta
|
|
71
71
|
req setup # 装进 ~/tools/req-kit/versions/<版本>,安装 skill,生成本机配置
|
|
72
|
-
req configure # 配置向导:选 builder
|
|
72
|
+
req configure # 配置向导:选 builder、reviewer 和备用模型用的 agent
|
|
73
|
+
req configure --auto # 不提问,按推荐选好(也可以在 Paseo 的 设置 → 插件 → req-kit → 模型 中修改)
|
|
73
74
|
req doctor --no-project # 体检
|
|
74
75
|
```
|
|
75
76
|
|
|
@@ -268,7 +268,7 @@ Paseo 自带的 `/paseo-advisor`、`/paseo-committee` 等编排 skill 也可以
|
|
|
268
268
|
| `waitingReminderHours` | 24 | 等待需求方回答、或研发完成后等待测试验证超过这么久,在对话里提醒一次;0 关闭 |
|
|
269
269
|
| (启动参数 `--verify-at`) | — | 分阶段时,研发完成后在这个时间自动开始测试验证(写法同 `--at`;隐含分阶段)。研发完成时时间已过就直接继续 |
|
|
270
270
|
| `autoResumeMinutes` / `autoResumeMax` | 30 / 2 | 临时错误(重试用完)中断后,这么久之后在对话里自动继续;每次尝试最多几次;0 关闭 |
|
|
271
|
-
| `watchdogMinutes` |
|
|
271
|
+
| `watchdogMinutes` | 60(1.12 起默认打开) | 运行期间对话定时检查,后台进程意外退出时自动继续。每次检查对话 agent 会运行一个回合;0 关闭。接单器(`req factory`)每次检查时也会发现这样的流水线并请它的对话继续 |
|
|
272
272
|
| `maxConcurrentPipelines` | 0(不限) | 本机同时运行的流水线上限,满了排队。只能写在本机配置中才有意义 |
|
|
273
273
|
|
|
274
274
|
## 需求总览
|
|
@@ -471,7 +471,7 @@ Paseo 自带的 `/paseo-advisor`、`/paseo-committee` 等编排 skill 也可以
|
|
|
471
471
|
|
|
472
472
|
**结果文件**:每步必须写一个 JSON 结果文件,流水线只看它,不看 agent 的回复文字。文件缺失或格式不对时,会在同一个会话里提醒 agent 补写,最多 2 次,仍不对则判定该步骤失败。
|
|
473
473
|
|
|
474
|
-
**等待与超时**:每步有总时限(`stepTimeoutMinutes
|
|
474
|
+
**等待与超时**:每步有总时限(`stepTimeoutMinutes`)。到时 agent 还在做时,先再给它同样长的时间(`selfHeal`,见下);仍未完成才判定失败,但不停止 agent。继续(`--resume`、自动重试)时先看这一步上一次的 agent:已经做完、结果文件有效就直接用它的结果;还在做就接着等它一轮;第二次仍超时才停止它、另起一个 agent。同一步不会有两个 agent 同时在做(它们会改同样的文件、在同一个工作区提交、写同一个结果文件)。连续读不到 agent 状态时同样处理。等待被拆成每段 10 分钟,连接断开时自动重试。agent 等待权限批准时,同一个请求只通知一次,然后继续等待。
|
|
475
475
|
|
|
476
476
|
**临时错误自动重试**:步骤因限流(429)、服务暂时不可用(502/503/504/529、overloaded)、网络中断等临时错误失败时,等待后用新的 agent 重做这一步,并告诉它工作区里可能留有上一次没做完的改动。默认最多重试 2 次,分别等 1 分钟和 5 分钟:
|
|
477
477
|
|
|
@@ -479,7 +479,25 @@ Paseo 自带的 `/paseo-advisor`、`/paseo-committee` 等编排 skill 也可以
|
|
|
479
479
|
"retry": { "attempts": 2, "backoffSeconds": [60, 300] }
|
|
480
480
|
```
|
|
481
481
|
|
|
482
|
-
`attempts` 设为 0 关闭;`backoffSeconds` 的个数少于重试次数时,后面的重试使用最后一个值。超时、结果文件不合格、agent
|
|
482
|
+
`attempts` 设为 0 关闭;`backoffSeconds` 的个数少于重试次数时,后面的重试使用最后一个值。超时、结果文件不合格、agent 自己判定失败等不是临时错误,不按这里重试。重试用完仍失败时,中断通知会注明已重试的次数。
|
|
483
|
+
|
|
484
|
+
**自愈**(1.12,`selfHeal`,默认 `true`):能安全自动处理的中断不再等人。
|
|
485
|
+
|
|
486
|
+
| 情况 | 自动处理 |
|
|
487
|
+
| --- | --- |
|
|
488
|
+
| 步骤超时,agent 还在做 | 不打断它,再等同样长的时间;仍未完成才中断 |
|
|
489
|
+
| agent 报错(不是临时错误) | 新开一个 agent 重做这一步一次;配置了备用模型时用备用模型 |
|
|
490
|
+
| 提醒后仍没有有效的结果文件 | 新开一个 agent 重做这一步一次 |
|
|
491
|
+
| 限流、额度用完,重试用完 | 配置了备用模型时,用备用模型再做一次;仍不行就中断,并按 Paseo 显示的额度恢复时间预约自动继续(恢复后 5 分钟;没有恢复时间时按 `autoResumeMinutes`) |
|
|
492
|
+
| 方案审核说需要需求方决定 | 先请 builder 从代码、文档和需求说明中找答案(`answerQuestionsFirst`,默认 `true`),只把真正的业务取舍连同推荐答案交给需求方;都能回答时不停下 |
|
|
493
|
+
|
|
494
|
+
**备用模型**(1.12,可选):`agents.fallback`,写法同 builder、reviewer,可以用 `paseoProfile`。`req configure` 和插件的"模型"设置页会推荐一个与主模型不同厂商的模型。
|
|
495
|
+
|
|
496
|
+
```json
|
|
497
|
+
"agents": { "builder": { … }, "reviewer": { … }, "fallback": { "provider": "opencode/<模型 ID>" } }
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
自动处理的每一次都记在进度日志、状态文件的 `recoveries` 和使用记录中,`req stats` 的"人工介入"一节汇总:停下等人的原因、自动处理了多少次、到达提测的需求中没有非必要介入的比例。
|
|
483
501
|
|
|
484
502
|
**同一个目录一次只运行一条流水线**,因为各步骤都在这个目录中修改和提交。需要并行时,为每个需求使用单独的 worktree 工作区。
|
|
485
503
|
|
|
@@ -39,3 +39,18 @@
|
|
|
39
39
|
```
|
|
40
40
|
|
|
41
41
|
- `status`:所有"必须修改"的意见都已处理(修改或说明了不采纳的依据)时为 `"done"`;有意见没能处理时为 `"partial"`,并在修订记录中说明。
|
|
42
|
+
|
|
43
|
+
## 审核要求需求方决定时
|
|
44
|
+
|
|
45
|
+
`<pipeline-context>` 中写着"审核结论:需要需求方决定"时,审核者认为有问题必须由需求方回答。需求方不读代码,每多问一个问题,开发就要停下来等一次,所以先自己找答案:
|
|
46
|
+
|
|
47
|
+
1. 逐个问题,到 `spec.md`、`alignment.md`(有的话)、项目文档、术语表、相关代码和同类功能的现有做法中找依据。
|
|
48
|
+
2. 能确定答案的(例如现有功能已经有一致的做法、spec 中其实写了、或者是纯技术选择):直接在方案中采用,在修订记录中写明问题、答案和依据(文件或代码位置)。
|
|
49
|
+
3. 只有需要需求方在业务上取舍、而且选错了返工很大的问题,才写进结果文件的 `business_questions`。每个问题写成需求方能直接回答的样子,并附上你的推荐答案和理由,例如:"历史订单是否也显示新的配送状态?推荐:只有新订单显示(理由:历史订单没有这项数据,补录要另做)"。
|
|
50
|
+
4. 方案中其余部分照常按审核意见修订。
|
|
51
|
+
|
|
52
|
+
结果文件中 `business_questions` 为空或省略时,流水线不停下,继续审核或实现:
|
|
53
|
+
|
|
54
|
+
```json
|
|
55
|
+
{ "status": "done", "business_questions": ["历史订单是否也显示新的配送状态?推荐:只有新订单显示(理由:…)"] }
|
|
56
|
+
```
|
package/engine/src/api.ts
CHANGED
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
// req api stats [--since 30d] [--month 2026-10]
|
|
11
11
|
// req api index [--rebuild <dir>...] [--prune]
|
|
12
12
|
// req api run <approve|stop|resume|verify> <需求> [--repo <dir>] [其他选项]
|
|
13
|
+
// req api models agents the daemon offers, the current and the recommended ones (1.12)
|
|
14
|
+
// req api set-agents --json <设置> writes builder, reviewer and fallback to the local config (1.12)
|
|
13
15
|
//
|
|
14
16
|
// Reading never changes anything. `run` performs the same command as `req <command>` in that
|
|
15
17
|
// directory, so the engine stays the only one that changes a requirement's state.
|
|
@@ -313,6 +315,26 @@ export function apiRun(command: string, repoArg: string, feature: string, extra:
|
|
|
313
315
|
return { command, featureDir: feature, ok: r.status === 0, exitCode: r.status, message: r.error ? r.error.message : output };
|
|
314
316
|
}
|
|
315
317
|
|
|
318
|
+
/** Runs the configuration program in one of its machine modes and returns its JSON line. */
|
|
319
|
+
function configureJson(args: string[]): Record<string, unknown> {
|
|
320
|
+
const r = spawnSync(process.execPath, [path.join(KIT_ROOT, "bin", "req.mjs"), "configure", ...args], {
|
|
321
|
+
encoding: "utf8",
|
|
322
|
+
timeout: 120_000,
|
|
323
|
+
windowsHide: true,
|
|
324
|
+
env: process.env,
|
|
325
|
+
});
|
|
326
|
+
const line = (r.stdout ?? "").trim().split("\n").pop() ?? "";
|
|
327
|
+
let parsed: Record<string, unknown> | null = null;
|
|
328
|
+
try {
|
|
329
|
+
parsed = JSON.parse(line) as Record<string, unknown>;
|
|
330
|
+
} catch {
|
|
331
|
+
parsed = null;
|
|
332
|
+
}
|
|
333
|
+
if (!parsed) throw new ApiError("failed", `读取 agent 设置失败:${(r.stderr || r.error?.message || "没有输出").trim().split("\n").slice(-2).join(" ")}`);
|
|
334
|
+
if (parsed.ok === false) throw new ApiError("failed", String(parsed.message ?? "失败"));
|
|
335
|
+
return parsed;
|
|
336
|
+
}
|
|
337
|
+
|
|
316
338
|
// ---------------------------------------------------------------------------
|
|
317
339
|
// CLI
|
|
318
340
|
|
|
@@ -376,8 +398,18 @@ export function apiMain(argvIn: string[], cwd = process.cwd()): unknown {
|
|
|
376
398
|
const what = need("命令(approve、stop、resume、verify)");
|
|
377
399
|
return apiRun(what, repo, need("需求"), argv.splice(0));
|
|
378
400
|
}
|
|
401
|
+
case "models": {
|
|
402
|
+
const { current, choices, recommended } = configureJson(["--catalog-json"]);
|
|
403
|
+
return { current, choices, recommended };
|
|
404
|
+
}
|
|
405
|
+
case "set-agents": {
|
|
406
|
+
const value = option(argv, "--json");
|
|
407
|
+
if (!value) throw new ApiError("bad_arguments", "缺少 --json <设置>");
|
|
408
|
+
const { message } = configureJson(["--set", value]);
|
|
409
|
+
return { ok: true, message };
|
|
410
|
+
}
|
|
379
411
|
default:
|
|
380
|
-
throw new ApiError("bad_arguments", `没有 "${command ?? ""}" 这个 api 命令。可用:version、list、status、file、stats、index、run`);
|
|
412
|
+
throw new ApiError("bad_arguments", `没有 "${command ?? ""}" 这个 api 命令。可用:version、list、status、file、stats、index、run、models、set-agents`);
|
|
381
413
|
}
|
|
382
414
|
}
|
|
383
415
|
|
package/engine/src/card-cmd.ts
CHANGED
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
hashText,
|
|
22
22
|
listCardEntries,
|
|
23
23
|
locateCardRequirement,
|
|
24
|
+
isYes,
|
|
24
25
|
parseReply,
|
|
25
26
|
planAppendix,
|
|
26
27
|
publishCard,
|
|
@@ -95,7 +96,7 @@ function today(now = new Date()): string {
|
|
|
95
96
|
return `${now.getFullYear()}${String(now.getMonth() + 1).padStart(2, "0")}${String(now.getDate()).padStart(2, "0")}`;
|
|
96
97
|
}
|
|
97
98
|
|
|
98
|
-
async function withRuntime<T>(config: PipelineConfig, use: (runtime: AgentRuntime, config: PipelineConfig) => Promise<T>): Promise<T> {
|
|
99
|
+
export async function withRuntime<T>(config: PipelineConfig, use: (runtime: AgentRuntime, config: PipelineConfig) => Promise<T>): Promise<T> {
|
|
99
100
|
const runtime = await (await loadRuntimeFactory())({ config, log: () => undefined });
|
|
100
101
|
try {
|
|
101
102
|
const resolved = usesPaseoProfiles(config) ? resolveConfigProfiles(config, await runtime.listPaseoProfiles()) : config;
|
|
@@ -316,6 +317,12 @@ export async function checkOne(target: Located, opts: CardCmdOptions, notify: (a
|
|
|
316
317
|
const key = replyKey(reply);
|
|
317
318
|
let confirmed = false;
|
|
318
319
|
let planConfirmed = false;
|
|
320
|
+
// 1.12: a bare "好的" written anywhere in the reply section answers what is being waited for.
|
|
321
|
+
if (!reply.confirmText && !reply.planConfirmText && reply.notes && isYes(reply.notes) && Object.keys(reply.answers).length === 0) {
|
|
322
|
+
const state = fs.existsSync(statePath(target.repo, target.entry.featureDir)) ? readState(statePath(target.repo, target.entry.featureDir)) : null;
|
|
323
|
+
if (state?.status === "waiting_for_approval") reply.planConfirmed = true;
|
|
324
|
+
else if (!source.confirmedAt) reply.confirmed = true;
|
|
325
|
+
}
|
|
319
326
|
if (parts.reply !== null && source.replyHash !== null && key !== source.replyHash) {
|
|
320
327
|
const answered = Object.keys(reply.answers);
|
|
321
328
|
if (answered.length) changes.push(`需求方回答了 ${answered.join("、")}`);
|
package/engine/src/card.ts
CHANGED
|
@@ -186,8 +186,15 @@ export interface Reply {
|
|
|
186
186
|
planConfirmText: string;
|
|
187
187
|
}
|
|
188
188
|
|
|
189
|
-
// A plain yes, nothing after it but punctuation: "是否可以改成…" is not a yes.
|
|
190
|
-
|
|
189
|
+
// A plain yes, nothing after it but particles and punctuation: "是否可以改成…" is not a yes.
|
|
190
|
+
// 1.12: the everyday ways of saying it count too ("好的", "没问题", "按你说的来", "👍").
|
|
191
|
+
const YES =
|
|
192
|
+
/^(同意|确认|确定|认可|是|是的|对|对的|可以|好|好的|行|没问题|没有问题|通过|就这样|按这个来|按你说的(来|做|办)?|按方案(来|做)?|ok|okay|yes|y|lgtm|👍)\s*(的|了|吧|啊|呀|哈)?\s*[。.!!~~]*\s*$/i;
|
|
193
|
+
|
|
194
|
+
/** Whether a reply is a plain yes. */
|
|
195
|
+
export function isYes(text: string): boolean {
|
|
196
|
+
return YES.test(text.trim());
|
|
197
|
+
}
|
|
191
198
|
|
|
192
199
|
export function parseReply(reply: string | null): Reply {
|
|
193
200
|
const out: Reply = { answers: {}, notes: "", confirmed: false, planConfirmed: false, confirmText: "", planConfirmText: "" };
|
package/engine/src/config.ts
CHANGED
|
@@ -151,7 +151,23 @@ export interface PipelineConfig {
|
|
|
151
151
|
daemonUrl: string;
|
|
152
152
|
/** Name of the environment variable holding the daemon password, if the daemon has one. */
|
|
153
153
|
daemonPasswordEnv: string;
|
|
154
|
-
|
|
154
|
+
/**
|
|
155
|
+
* builder and reviewer; `fallback` (1.12, optional) is used when a step's own agent keeps
|
|
156
|
+
* failing (rate limits or quota after the retries, an agent error): the step is run once
|
|
157
|
+
* more with it instead of stopping.
|
|
158
|
+
*/
|
|
159
|
+
agents: Record<"builder" | "reviewer", AgentProfile> & { fallback?: AgentProfile | null };
|
|
160
|
+
/**
|
|
161
|
+
* 1.12: handle failures without a person where that is safe: wait once more for a step that
|
|
162
|
+
* runs over its time, redo a step once after an agent error or an invalid result file.
|
|
163
|
+
*/
|
|
164
|
+
selfHeal: boolean;
|
|
165
|
+
/**
|
|
166
|
+
* 1.12: questions the design review says only the requester can decide go to the builder
|
|
167
|
+
* first, who answers what the code, the documents and the spec settle; only the real
|
|
168
|
+
* business choices, each with a recommended answer, reach the requester.
|
|
169
|
+
*/
|
|
170
|
+
answerQuestionsFirst: boolean;
|
|
155
171
|
/** Profile used when neither --profile nor the spec names one. */
|
|
156
172
|
defaultProfile: ProfileName;
|
|
157
173
|
profiles: Record<ProfileName, ProfileSettings>;
|
|
@@ -317,7 +333,9 @@ export const DEFAULT_CONFIG: PipelineConfig = {
|
|
|
317
333
|
waitingReminderHours: 24,
|
|
318
334
|
autoResumeMinutes: 30,
|
|
319
335
|
autoResumeMax: 2,
|
|
320
|
-
watchdogMinutes:
|
|
336
|
+
watchdogMinutes: 60,
|
|
337
|
+
selfHeal: true,
|
|
338
|
+
answerQuestionsFirst: true,
|
|
321
339
|
phases: { mode: "continuous", verify: { environment: "local", agents: {}, defects: "fix", serviceStart: null } },
|
|
322
340
|
requirementTypes: ["defect", "refactor", "assessment"],
|
|
323
341
|
checks: [],
|
|
@@ -532,6 +550,9 @@ export function resolveRelatedRepo(repo: RelatedRepo, projectDir: string): strin
|
|
|
532
550
|
|
|
533
551
|
function validate(config: PipelineConfig): void {
|
|
534
552
|
for (const role of ["builder", "reviewer"] as const) validateProvider(`agents.${role}`, config.agents[role]);
|
|
553
|
+
if (config.agents.fallback !== undefined && config.agents.fallback !== null) validateProvider("agents.fallback", config.agents.fallback);
|
|
554
|
+
if (typeof config.selfHeal !== "boolean") throw new ConfigError("selfHeal 必须是 true 或 false");
|
|
555
|
+
if (typeof config.answerQuestionsFirst !== "boolean") throw new ConfigError("answerQuestionsFirst 必须是 true 或 false");
|
|
535
556
|
if (!PROFILE_NAMES.includes(config.defaultProfile)) {
|
|
536
557
|
throw new ConfigError(`defaultProfile 必须是 ${PROFILE_NAMES.join(" 或 ")}`);
|
|
537
558
|
}
|