@fitlab-ai/agent-infra 0.7.3 → 0.7.5
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 +32 -790
- package/README.zh-CN.md +32 -763
- package/bin/cli.ts +13 -11
- package/dist/bin/cli.js +13 -11
- package/dist/lib/init.js +1 -1
- package/dist/lib/merge.js +1 -1
- package/dist/lib/sandbox/commands/create.js +44 -3
- package/dist/lib/sandbox/commands/rm.js +99 -19
- package/dist/lib/sandbox/index.js +24 -22
- package/dist/lib/sandbox/readme-scaffold.js +6 -6
- package/dist/lib/task/artifacts.js +58 -0
- package/dist/lib/task/commands/cat.js +38 -0
- package/dist/lib/task/commands/files.js +47 -0
- package/dist/lib/task/commands/grep.js +143 -0
- package/dist/lib/task/commands/log.js +75 -0
- package/dist/lib/task/commands/show.js +5 -114
- package/dist/lib/task/commands/status.js +239 -0
- package/dist/lib/task/index.js +37 -0
- package/dist/lib/task/resolve-ref.js +150 -0
- package/dist/lib/update.js +1 -1
- package/lib/init.ts +1 -1
- package/lib/merge.ts +1 -1
- package/lib/sandbox/commands/create.ts +47 -4
- package/lib/sandbox/commands/rm.ts +128 -19
- package/lib/sandbox/index.ts +24 -22
- package/lib/sandbox/readme-scaffold.ts +6 -6
- package/lib/task/artifacts.ts +72 -0
- package/lib/task/commands/cat.ts +39 -0
- package/lib/task/commands/files.ts +53 -0
- package/lib/task/commands/grep.ts +147 -0
- package/lib/task/commands/log.ts +80 -0
- package/lib/task/commands/show.ts +5 -117
- package/lib/task/commands/status.ts +302 -0
- package/lib/task/index.ts +37 -0
- package/lib/task/resolve-ref.ts +160 -0
- package/lib/update.ts +1 -1
- package/package.json +1 -1
- package/templates/.agents/README.en.md +1 -0
- package/templates/.agents/README.zh-CN.md +1 -0
- package/templates/.agents/rules/README.en.md +45 -0
- package/templates/.agents/rules/README.zh-CN.md +44 -0
- package/templates/.agents/rules/cli-help-format.en.md +49 -0
- package/templates/.agents/rules/cli-help-format.zh-CN.md +49 -0
- package/templates/.agents/rules/debugging-guide.en.md +25 -0
- package/templates/.agents/rules/debugging-guide.zh-CN.md +25 -0
- package/templates/.agents/rules/no-mid-flow-questions.en.md +14 -2
- package/templates/.agents/rules/no-mid-flow-questions.zh-CN.md +14 -2
- package/templates/.agents/rules/pr-sync.github.en.md +8 -6
- package/templates/.agents/rules/pr-sync.github.zh-CN.md +8 -6
- package/templates/.agents/rules/review-handshake.en.md +83 -0
- package/templates/.agents/rules/review-handshake.zh-CN.md +83 -0
- package/templates/.agents/scripts/lib/post-review-commit.js +56 -0
- package/templates/.agents/scripts/lib/review-artifacts.js +117 -0
- package/templates/.agents/scripts/review-diff-fingerprint.js +99 -0
- package/templates/.agents/scripts/validate-artifact.js +240 -0
- package/templates/.agents/skills/analyze-task/SKILL.en.md +52 -6
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +52 -6
- package/templates/.agents/skills/code-task/SKILL.en.md +2 -0
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +2 -0
- package/templates/.agents/skills/code-task/config/verify.en.json +3 -0
- package/templates/.agents/skills/code-task/config/verify.zh-CN.json +3 -0
- package/templates/.agents/skills/code-task/reference/fix-mode.en.md +5 -3
- package/templates/.agents/skills/code-task/reference/fix-mode.zh-CN.md +5 -3
- package/templates/.agents/skills/code-task/reference/report-template.en.md +4 -4
- package/templates/.agents/skills/code-task/reference/report-template.zh-CN.md +4 -4
- package/templates/.agents/skills/code-task/scripts/detect-mode.js +2 -107
- package/templates/.agents/skills/commit/SKILL.en.md +6 -0
- package/templates/.agents/skills/commit/SKILL.zh-CN.md +6 -0
- package/templates/.agents/skills/commit/reference/task-status-update.en.md +8 -0
- package/templates/.agents/skills/commit/reference/task-status-update.zh-CN.md +8 -0
- package/templates/.agents/skills/complete-task/SKILL.en.md +10 -0
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +10 -0
- package/templates/.agents/skills/complete-task/config/verify.en.json +2 -0
- package/templates/.agents/skills/complete-task/config/verify.zh-CN.json +2 -0
- package/templates/.agents/skills/create-pr/reference/comment-publish.en.md +1 -1
- package/templates/.agents/skills/create-pr/reference/comment-publish.zh-CN.md +1 -1
- package/templates/.agents/skills/plan-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/plan-task/config/verify.en.json +3 -0
- package/templates/.agents/skills/plan-task/config/verify.zh-CN.json +3 -0
- package/templates/.agents/skills/review-analysis/config/verify.en.json +2 -1
- package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +2 -1
- package/templates/.agents/skills/review-analysis/reference/output-templates.en.md +5 -4
- package/templates/.agents/skills/review-analysis/reference/output-templates.zh-CN.md +5 -4
- package/templates/.agents/skills/review-analysis/reference/report-template.en.md +4 -0
- package/templates/.agents/skills/review-analysis/reference/report-template.zh-CN.md +4 -0
- package/templates/.agents/skills/review-code/SKILL.en.md +4 -1
- package/templates/.agents/skills/review-code/SKILL.zh-CN.md +4 -1
- package/templates/.agents/skills/review-code/config/verify.en.json +5 -2
- package/templates/.agents/skills/review-code/config/verify.zh-CN.json +5 -2
- package/templates/.agents/skills/review-code/reference/output-templates.en.md +5 -4
- package/templates/.agents/skills/review-code/reference/output-templates.zh-CN.md +5 -4
- package/templates/.agents/skills/review-code/reference/report-template.en.md +6 -0
- package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +6 -0
- package/templates/.agents/skills/review-plan/config/verify.en.json +2 -1
- package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +2 -1
- package/templates/.agents/skills/review-plan/reference/output-templates.en.md +5 -4
- package/templates/.agents/skills/review-plan/reference/output-templates.zh-CN.md +5 -4
- package/templates/.agents/skills/review-plan/reference/report-template.en.md +4 -0
- package/templates/.agents/skills/review-plan/reference/report-template.zh-CN.md +4 -0
- package/templates/.agents/skills/watch-pr/SKILL.en.md +1 -1
- package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +1 -1
- package/templates/.agents/templates/task.en.md +7 -0
- package/templates/.agents/templates/task.zh-CN.md +7 -0
- package/templates/.github/workflows/metadata-sync.yml +1 -1
- package/templates/.github/workflows/pr-label.yml +1 -1
- package/templates/.github/workflows/status-label.yml +1 -1
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# CLI help text conventions
|
|
2
|
+
|
|
3
|
+
Unify the help text display structure, display name, and command ordering of the `ai` / `agent-infra` CLI so newly added subcommands follow them automatically and never drift across levels again. Read this file before adding or changing CLI help text.
|
|
4
|
+
|
|
5
|
+
## Scope
|
|
6
|
+
|
|
7
|
+
- **Display name `ai`**: applies to **all** user-facing help / usage / banner text — top-level, namespace-level, and the single-line usage / startup banners of leaf commands such as `merge` / `init` / `update`. The only exceptions: the top-level help first line keeps the brand + version line `agent-infra ${VERSION}`, and `@fitlab-ai/agent-infra` in package names / install commands / repo URLs stays as-is.
|
|
8
|
+
- **Structure & ordering** (`Usage:` + `Commands:` structure, alphabetical command order): applies only to levels that carry a `Commands:` listing — top-level help (`bin/cli.ts`) and namespace-level help (e.g. `ai sandbox` / `ai task`). Leaf commands have only a single-line usage and need no `Commands:` structure.
|
|
9
|
+
|
|
10
|
+
## Display name
|
|
11
|
+
|
|
12
|
+
- Use **`ai`** as the command display name in help text (the recommended short form; `package.json`'s `bin` registers both `ai` and `agent-infra`).
|
|
13
|
+
- Keep the top-level help first line as the brand + version line `agent-infra ${VERSION} - bootstrap ...` (it is the brand and version marker that several tests anchor on).
|
|
14
|
+
- Keep `@fitlab-ai/agent-infra` in install methods, package names, and repo URLs as-is (those are package names, not command display names).
|
|
15
|
+
|
|
16
|
+
## List structure
|
|
17
|
+
|
|
18
|
+
Namespace-level and top-level help follow:
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
Usage: ai <ns> <command> [options]
|
|
22
|
+
|
|
23
|
+
Commands:
|
|
24
|
+
<command> <description aligned from two spaces>
|
|
25
|
+
...
|
|
26
|
+
|
|
27
|
+
Run 'ai <ns> <command> --help' for details.
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
- The `Commands:` block uses bare command names (no repeated binary name), two-space indent, descriptions aligned to the longest command name.
|
|
31
|
+
- Namespace-level help ends with a `Run 'ai <ns> <command> --help' for details.` footer.
|
|
32
|
+
- Top-level help has no uniform subcommand `--help` convention, so the footer is not required there; if an `Examples:` section exists, its command display name is also `ai`.
|
|
33
|
+
|
|
34
|
+
## Ordering
|
|
35
|
+
|
|
36
|
+
Command lists, `Examples`, and command enumerations embedded in descriptions are all sorted by the **first token of the command, in ascending alphabetical order**:
|
|
37
|
+
|
|
38
|
+
- Multi-token commands (e.g. `vm status|start|stop`) sort by the first token (`vm`).
|
|
39
|
+
- Commands with angle/square-bracket parameters sort by the command name (the bare word before the parameters).
|
|
40
|
+
- Case-insensitive.
|
|
41
|
+
|
|
42
|
+
## Checklist for adding a subcommand
|
|
43
|
+
|
|
44
|
+
When adding a subcommand:
|
|
45
|
+
|
|
46
|
+
1. Insert the command at the correct alphabetical position in `Commands:`.
|
|
47
|
+
2. If it has examples, insert them at the alphabetical position in `Examples:`.
|
|
48
|
+
3. If a top-level `task` / `sandbox` description has an embedded command enumeration, update its alphabetical order too.
|
|
49
|
+
4. Sync the corresponding help test's **structural** assertions (whether the command appears, whether the `Usage:` / `Commands:` header exists); do not bind to full sentences (see [`testing-discipline.md`](testing-discipline.md)).
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# CLI help 文案约定
|
|
2
|
+
|
|
3
|
+
统一 `ai` / `agent-infra` CLI 的 help 文案展示结构、展示名与命令排序,让后续新增子命令自动遵守,避免跨层级再次漂移。新增或调整 CLI help 文案前先读取本文件。
|
|
4
|
+
|
|
5
|
+
## 适用范围
|
|
6
|
+
|
|
7
|
+
- **展示名 `ai`**:适用于**所有**面向用户的 help / usage / 交互横幅文案——顶层、命名空间级,以及 `merge` / `init` / `update` 等叶子命令的单行 usage 与启动横幅,统一用 `ai`。唯一例外:顶层 help 首行保留品牌 + 版本行 `agent-infra ${VERSION}`;包名 / 安装命令 / 仓库 URL 中的 `@fitlab-ai/agent-infra` 保持原样。
|
|
8
|
+
- **结构与排序**(`Usage:` + `Commands:` 结构、命令按字母序):仅适用于带 `Commands:` 子清单的层级——顶层 help(`bin/cli.ts`)与命名空间级 help(如 `ai sandbox` / `ai task`)。叶子命令只有单行 usage,无需 `Commands:` 结构。
|
|
9
|
+
|
|
10
|
+
## 展示名
|
|
11
|
+
|
|
12
|
+
- help 文案中的命令展示名统一用 **`ai`**(推荐简写,`package.json` 的 `bin` 同时注册 `ai` 与 `agent-infra`)。
|
|
13
|
+
- 顶层 help 首行保留品牌 + 版本行 `agent-infra ${VERSION} - bootstrap ...`(这是品牌与版本标识,多处测试锚定它)。
|
|
14
|
+
- 安装方式、包名、仓库 URL 中的 `@fitlab-ai/agent-infra` 等保持原样(是包名而非命令展示名)。
|
|
15
|
+
|
|
16
|
+
## 列表结构
|
|
17
|
+
|
|
18
|
+
命名空间级与顶层 help 统一为:
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
Usage: ai <ns> <command> [options]
|
|
22
|
+
|
|
23
|
+
Commands:
|
|
24
|
+
<command> <两空格起对齐的描述>
|
|
25
|
+
...
|
|
26
|
+
|
|
27
|
+
Run 'ai <ns> <command> --help' for details.
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
- `Commands:` 块用裸命令名(不重复二进制名),两空格缩进,描述按最长命令名对齐。
|
|
31
|
+
- 命名空间级 help 末尾加 `Run 'ai <ns> <command> --help' for details.` footer。
|
|
32
|
+
- 顶层 help 无统一子命令 `--help` 约定,故不强制加该 footer;如有 `Examples:` 段,命令展示名同样用 `ai`。
|
|
33
|
+
|
|
34
|
+
## 排序
|
|
35
|
+
|
|
36
|
+
命令清单、`Examples`、描述中内嵌的命令枚举,一律按**命令首 token 的字母升序**排列:
|
|
37
|
+
|
|
38
|
+
- 多 token 命令(如 `vm status|start|stop`)按首 token(`vm`)排序。
|
|
39
|
+
- 带尖括号 / 方括号参数的命令按命令名(参数前的裸词)排序。
|
|
40
|
+
- 大小写不敏感。
|
|
41
|
+
|
|
42
|
+
## 新增子命令检查清单
|
|
43
|
+
|
|
44
|
+
新增一个子命令时:
|
|
45
|
+
|
|
46
|
+
1. 把命令插入 `Commands:` 的字母序正确位置。
|
|
47
|
+
2. 如有示例,插入 `Examples:` 的字母序位置。
|
|
48
|
+
3. 若顶层 `task` / `sandbox` 等描述中有内嵌命令枚举,同步更新其字母序。
|
|
49
|
+
4. 同步对应 help 测试的**结构性**断言(命令是否出现、`Usage:` / `Commands:` 头是否存在),不要绑定整句文案(见 [`testing-discipline.md`](testing-discipline.md))。
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# General Rule - Structured Debugging Guide
|
|
2
|
+
|
|
3
|
+
> This file defines the structured triage flow for "test failure / behavior not as expected"; SKILLs that modify code in response to failures (e.g. `code-task`, `watch-pr`) load it on demand before attempting a fix.
|
|
4
|
+
|
|
5
|
+
## Triggers
|
|
6
|
+
|
|
7
|
+
When any of the following happens, run this flow before changing code:
|
|
8
|
+
|
|
9
|
+
- A test fails, or a build / type-check / lint error appears
|
|
10
|
+
- Runtime behavior differs from expectations (output, state, or side effects)
|
|
11
|
+
|
|
12
|
+
## Core Anti-pattern: No Blind Patch-and-Retry
|
|
13
|
+
|
|
14
|
+
The "tweak one spot → rerun → still broken → guess another spot" loop hides the real root cause, introduces new defects, and wastes time. A change with no supporting evidence is not a fix.
|
|
15
|
+
|
|
16
|
+
## Four-phase Flow
|
|
17
|
+
|
|
18
|
+
1. **Gather evidence**: Read the full error message and stack trace (not just the last line) and pinpoint where it fails; reproduce minimally when needed, and record "actual vs expected behavior".
|
|
19
|
+
2. **Form a hypothesis**: From the evidence, propose a root-cause hypothesis that explains **all** the symptoms rather than a surface symptom; if there are several, rank them by likelihood and testability.
|
|
20
|
+
3. **Verify the hypothesis**: Before changing anything, confirm the hypothesis cheaply—add logging, add a breakpoint, shrink the input, or write a failing test that reproduces it; if it is disproven, return to phase 2.
|
|
21
|
+
4. **Fix the root cause**: Change only the verified root cause (not the symptom), then rerun the relevant tests to confirm they pass; if they still fail, return to phase 1 with the new evidence instead of trial-and-error without evidence.
|
|
22
|
+
|
|
23
|
+
## Relation to Project Principles
|
|
24
|
+
|
|
25
|
+
This flow is the debugging-specific form of AGENTS.md's "Think Before Coding" and "Goal-Driven Execution": pin the problem with a reproducible failing case first, then make the fix turn it green.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# 通用规则 - 结构化调试指导
|
|
2
|
+
|
|
3
|
+
> 本文件定义「测试失败 / 行为不符合预期」时的结构化排查流程;`code-task`、`watch-pr` 等会因失败而修改代码的 SKILL 在动手修复前按需加载。
|
|
4
|
+
|
|
5
|
+
## 触发条件
|
|
6
|
+
|
|
7
|
+
出现以下任一情况时,先按本流程排查,再改代码:
|
|
8
|
+
|
|
9
|
+
- 测试失败,或构建 / 类型检查 / lint 报错
|
|
10
|
+
- 运行结果与预期不符(输出、状态或副作用异常)
|
|
11
|
+
|
|
12
|
+
## 核心反模式:禁止盲目改代码重试
|
|
13
|
+
|
|
14
|
+
「改一处 → 重跑 → 还错 → 再猜一处」的循环会掩盖真实根因、引入新缺陷、浪费时间。没有证据支撑的修改不算修复。
|
|
15
|
+
|
|
16
|
+
## 四阶段流程
|
|
17
|
+
|
|
18
|
+
1. **收集证据**:完整读取错误信息与堆栈(不要只看最后一行),定位失败的具体位置;必要时最小化复现,记录「实际行为 vs 预期行为」。
|
|
19
|
+
2. **形成假设**:基于证据提出能解释**全部**现象的根因假设,而不是停留在表层症状;若有多个假设,按可能性与可验证性排序。
|
|
20
|
+
3. **验证假设**:动手改之前,用最小代价确认假设成立——加日志、加断点、缩小输入,或写一个能复现的失败用例;假设被证伪就回到阶段 2。
|
|
21
|
+
4. **修复根因**:只针对已验证的根因修改(而非症状),改完重跑相关测试确认通过;仍失败则带着新证据回到阶段 1,不在无证据时反复试错。
|
|
22
|
+
|
|
23
|
+
## 与项目准则的关系
|
|
24
|
+
|
|
25
|
+
本流程是 AGENTS.md「先思考再动手」「目标驱动执行」在调试场景的具体化:先用可复现的失败用例锁定问题,再让修复使其通过。
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# General Rule - No Mid-Flow Questions During SKILL Execution
|
|
2
2
|
|
|
3
3
|
> **Scope**: this rule applies to **all SKILL** executions.
|
|
4
|
-
> Only the
|
|
4
|
+
> Only the exemption categories listed below may ask the user; any other mid-flow question is a violation.
|
|
5
5
|
|
|
6
6
|
## Exemption Categories
|
|
7
7
|
|
|
@@ -27,9 +27,21 @@ SKILLs currently covered by this exemption:
|
|
|
27
27
|
- `init-labels`: may confirm before deleting legacy labels not in the final mapping
|
|
28
28
|
- `commit`: may stop and confirm when its plan conflicts with the user's uncommitted changes
|
|
29
29
|
|
|
30
|
+
### Exemption 3: Entry-point requirement-sufficiency clarification
|
|
31
|
+
|
|
32
|
+
Allowed only when a SKILL judges, **at its entry point**, whether the current task's requirement information is sufficient for a reliable analysis; it may then ask the user about the **missing requirement information** to converge the requirements. Constraints:
|
|
33
|
+
|
|
34
|
+
- Limited to the `analyze-task` entry point; ask one question at a time and wait for the answer before asking the next;
|
|
35
|
+
- Used only to fill requirement-sufficiency gaps; it must **not** be used to solicit implementation / technical-choice preferences (those still go into the artifact's `## Open Questions` per the default clause);
|
|
36
|
+
- Exit the questioning and proceed to normal analysis once the question budget is reached or the user says "just analyze / skip".
|
|
37
|
+
|
|
38
|
+
SKILLs currently covered by this exemption:
|
|
39
|
+
|
|
40
|
+
- `analyze-task`: when the task description/requirements are insufficient for a reliable analysis, it may ask questions one at a time at the entry point to converge the requirements
|
|
41
|
+
|
|
30
42
|
## No-Mid-Flow-Questions Clause (default behavior)
|
|
31
43
|
|
|
32
|
-
For every SKILL execution context not covered by
|
|
44
|
+
For every SKILL execution context not covered by any exemption above, the default behavior is:
|
|
33
45
|
|
|
34
46
|
1. Do not call any user-question tool, including but not limited to `AskUserQuestion` and equivalent mechanisms that ask the user to choose.
|
|
35
47
|
2. When uncertain, proceed with the most robust option without interrupting the flow. Use this priority order:
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# 通用规则 - SKILL 执行禁言
|
|
2
2
|
|
|
3
3
|
> **适用范围**:本规则适用于**所有 SKILL** 的执行过程。
|
|
4
|
-
>
|
|
4
|
+
> 仅以下列出的例外可向用户提问;不属于这些例外的发问一律按违规处理。
|
|
5
5
|
|
|
6
6
|
## 例外类型
|
|
7
7
|
|
|
@@ -27,9 +27,21 @@
|
|
|
27
27
|
- `init-labels`:删除不在最终映射中的旧 label 前可确认
|
|
28
28
|
- `commit`:检测到与用户未提交改动冲突时可停下确认
|
|
29
29
|
|
|
30
|
+
### 例外 3:入口式需求充分性澄清
|
|
31
|
+
|
|
32
|
+
仅当 SKILL 在**入口处**判断「当前任务的需求信息是否充分到可以可靠分析」时,可就**缺失的需求信息**向用户提问以收敛需求。约束:
|
|
33
|
+
|
|
34
|
+
- 仅限 `analyze-task` 入口;一次只问一个问题,等用户回答后再问下一个;
|
|
35
|
+
- 仅用于补齐需求充分性,**不得**借此征求实现方案 / 技术选型偏好(这些仍按禁言铁律写入产物的 `## 未决问题`);
|
|
36
|
+
- 达到提问预算上限或用户表示「直接分析 / skip」即退出提问,进入正常分析。
|
|
37
|
+
|
|
38
|
+
当前归入本例外的 SKILL:
|
|
39
|
+
|
|
40
|
+
- `analyze-task`:任务描述/需求信息不足以支撑可靠分析时,可在入口处逐个提问收敛需求
|
|
41
|
+
|
|
30
42
|
## 禁言条款(默认行为)
|
|
31
43
|
|
|
32
|
-
|
|
44
|
+
不属于上述任一例外的所有 SKILL 执行场景,遵循以下默认行为:
|
|
33
45
|
|
|
34
46
|
1. **禁止调用**任何向用户发问的工具(包括但不限于 `AskUserQuestion` 及等价的「征求用户选择」机制)。
|
|
35
47
|
2. **不确定时**,按「最稳健方案」自主推进,不中断对话。最稳健方案的判定优先级:
|
|
@@ -32,7 +32,11 @@ Aggregation rules:
|
|
|
32
32
|
- build the review-history table from `review-code*` and `code*`
|
|
33
33
|
- extract the test summary from `code*`
|
|
34
34
|
- if one artifact class is missing, treat it as "no data for this stage" and continue
|
|
35
|
-
- Manual verification section:
|
|
35
|
+
- Manual verification section: include only post-code-stage checks that still require a human to execute or judge and that the AI cannot close on its own.
|
|
36
|
+
- **Admission boundary**: the verification result depends on a real environment, permissions, account, external system, or human judgment, and cannot be closed by an agent rerunning tests, adding checks, or continuing the fix loop.
|
|
37
|
+
- **Sources**: `review-code*` "Environment-Blocked Findings", plus `code*` items that satisfy the boundary above.
|
|
38
|
+
- **Wording**: each retained item must state at least "what to verify + location (file/change/scope) + why only a human can verify it".
|
|
39
|
+
- **Empty rendering**: when there are no retained items, do NOT use the ⚠️ alarm style (it falsely implies a problem). Render the whole block as: heading `### ✅ No Manual Verification Needed` and a single line `No items in this change require manual confirmation.`, with no item list. Only use the `### ⚠️ Manual Verification Required` heading + item list when retained items exist.
|
|
36
40
|
|
|
37
41
|
## Comment Body Template
|
|
38
42
|
|
|
@@ -47,11 +51,7 @@ Use this canonical comment body template:
|
|
|
47
51
|
|
|
48
52
|
**Updated At**: {current-time}
|
|
49
53
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
> Items in this change that need human confirmation/fallback; reviewers can reply under this comment once verified.
|
|
53
|
-
|
|
54
|
-
- {manual-verify-item}
|
|
54
|
+
{manual-verify-section}
|
|
55
55
|
|
|
56
56
|
### Key Technical Decisions
|
|
57
57
|
|
|
@@ -72,6 +72,8 @@ Use this canonical comment body template:
|
|
|
72
72
|
*Generated by {agent} · Internal tracking: {task-id}*
|
|
73
73
|
```
|
|
74
74
|
|
|
75
|
+
> Render `{manual-verify-section}` per the "manual verification section" aggregation rule above: with retained items → `### ⚠️ Manual Verification Required` heading + quote + item list; with none → `### ✅ No Manual Verification Needed` heading + a single line `No items in this change require manual confirmation.` (no ⚠️, no list).
|
|
76
|
+
|
|
75
77
|
## Comment Lookup And Update
|
|
76
78
|
|
|
77
79
|
Fetch existing comments through the Issues comments API, not the dedicated PR comments API.
|
|
@@ -32,7 +32,11 @@
|
|
|
32
32
|
- 用 `review-code*` 与 `code*` 构建审查历程表
|
|
33
33
|
- 从 `code*` 提取测试结果摘要
|
|
34
34
|
- 某一类产物缺失时,按“无该阶段数据”处理并继续生成
|
|
35
|
-
-
|
|
35
|
+
- 需人工校验段落:只收进入 code 阶段后仍需人实际执行或判断、AI 无法自行关闭的校验点。
|
|
36
|
+
- **准入边界**:校验结论依赖真实环境、权限、账号、外部系统或人工判断,且无法通过 agent 重跑测试、补充检查或继续修复自行关闭。
|
|
37
|
+
- **来源**:`review-code*` 的「环境性遗留」,以及 `code*` 中满足上述边界的校验点。
|
|
38
|
+
- **写法**:每条保留项至少写明「校验什么 + 定位(文件/改动/范围)+ 为什么只能由人校验」。
|
|
39
|
+
- **空集渲染**:无保留项时,不要使用 ⚠️ 告警样式(会让人误以为有问题)。整段降级渲染为:标题 `### ✅ 无需人工校验`,正文一行 `本次改动无需人工确认事项。`,不带条目列表。有保留项时才用 `### ⚠️ 需人工校验` 标题 + 条目列表。
|
|
36
40
|
|
|
37
41
|
## 评论体模板
|
|
38
42
|
|
|
@@ -47,11 +51,7 @@
|
|
|
47
51
|
|
|
48
52
|
**更新时间**:{当前时间}
|
|
49
53
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
> 本次改动中需人工确认/兜底的事项;reviewer 校验后可在本评论下回复收尾。
|
|
53
|
-
|
|
54
|
-
- {manual-verify-item}
|
|
54
|
+
{manual-verify-section}
|
|
55
55
|
|
|
56
56
|
### 关键技术决策
|
|
57
57
|
|
|
@@ -72,6 +72,8 @@
|
|
|
72
72
|
*由 {agent} 自动生成 · 内部追踪:{task-id}*
|
|
73
73
|
```
|
|
74
74
|
|
|
75
|
+
> `{manual-verify-section}` 按上文「需人工校验段落」聚合规则渲染:有保留项 → `### ⚠️ 需人工校验` 标题 + 引用说明 + 条目列表;无保留项 → `### ✅ 无需人工校验` 标题 + 一行 `本次改动无需人工确认事项。`(不带 ⚠️、不带列表)。
|
|
76
|
+
|
|
75
77
|
## 评论查找与更新
|
|
76
78
|
|
|
77
79
|
已有评论必须通过 Issues comments API 获取,而不是单独的 PR comments API。
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Bidirectional Review Handshake Protocol
|
|
2
|
+
|
|
3
|
+
> Shared by executor and reviewer across all three stages (analysis / plan / code) when running the `review-*` and `*-task` skills.
|
|
4
|
+
> This file is the **single source of truth** for the protocol; each SKILL only `Read`s it and never re-copies the vocabulary.
|
|
5
|
+
|
|
6
|
+
## Core principles
|
|
7
|
+
|
|
8
|
+
- **A review finding is input to be verified, not a command to execute.** The executor must verify each finding before disposing of it — neither rubber-stamping nor blindly refuting.
|
|
9
|
+
- **Symmetric evidence burden**: every disposition, whether accept or refute, must carry **commensurate evidence**. "Accept" is not a zero-cost default path.
|
|
10
|
+
- **Converge before advancing**: while any unclosed disagreement, alternative fix, cannot-judge, or post-review commit exists, do not silently advance to the next stage, archive, or merge.
|
|
11
|
+
|
|
12
|
+
## Executor four-state disposition (`*-task` skills, when responding to the prior review round in Round ≥ 2)
|
|
13
|
+
|
|
14
|
+
For each finding in the latest `review-*`, first Read/Grep the cited `file:line` / command, then assign one status:
|
|
15
|
+
|
|
16
|
+
| Status | Meaning | Required evidence |
|
|
17
|
+
|--------|---------|-------------------|
|
|
18
|
+
| `accepted` | Valid; will fix as suggested | `file:line` of the fix, or the change to be applied this round |
|
|
19
|
+
| `adjusted` | Valid, but an alternative fix is used | the alternative + why it is better; awaits reviewer confirmation |
|
|
20
|
+
| `refuted` | After verification, judged invalid / hallucinated / based on a wrong `file:line` | counter-evidence (`file:line` or raw command output); awaits reviewer confirmation |
|
|
21
|
+
| `cannot-judge` | Insufficient evidence to decide | the verification path attempted; handed to reviewer/human |
|
|
22
|
+
|
|
23
|
+
## Reviewer hand-back duty (`review-*` skills, when re-reviewing the executor response)
|
|
24
|
+
|
|
25
|
+
After the executor gives `adjusted` / `refuted` / `cannot-judge`, the reviewer must respond per item — never re-reading the original finding nor ignoring the hand-back:
|
|
26
|
+
|
|
27
|
+
- **Withdraw the finding** → set the ledger row to `confirmed` (accepts the refutation).
|
|
28
|
+
- **Accept the alternative fix** → set to `confirmed`.
|
|
29
|
+
- **Hold with new evidence** → set back to `open` (with new evidence, returned to the executor).
|
|
30
|
+
- **Escalate to human** → set to `needs-human-decision`.
|
|
31
|
+
|
|
32
|
+
## Convergence termination (loop guard)
|
|
33
|
+
|
|
34
|
+
- The per-finding handshake round limit is `MAX_HANDSHAKE_ROUNDS`, default **3**, overridable via `review.maxHandshakeRounds` in `.agents/.airc.json`.
|
|
35
|
+
- When a finding's `round` reaches the limit without entering a terminal state, it must be forced to `needs-human-decision`; the gate rejects rows that hit the limit without escalating.
|
|
36
|
+
- `needs-human-decision` keeps blocking completion until a human records a ruling in the task.md `## 人工裁决` section and flips the row to `human-decided`.
|
|
37
|
+
|
|
38
|
+
## Same-model convergence-bias mitigation (documentation-level discipline)
|
|
39
|
+
|
|
40
|
+
The executor and reviewer are often the same/similar model and are naturally inclined to agree. When reviewing:
|
|
41
|
+
|
|
42
|
+
1. **Read the evidence before the conclusion**: read the `git diff` / artifact itself and form findings independently **before** reading the executor's conclusions and responses, to avoid being anchored.
|
|
43
|
+
2. **Default-skeptical framing**: treat "looks fine" as unverified; every clearance needs reproducible evidence (see the `Evidence` hard gate in each `review-*`).
|
|
44
|
+
|
|
45
|
+
> The only mechanical lever is the **symmetric-evidence gate** (non-`open` ledger rows must carry evidence); model homogeneity itself is not mechanically checkable, so this section is discipline rather than a gate.
|
|
46
|
+
|
|
47
|
+
## Mechanical ledger (task.md `## 审查分歧账本`)
|
|
48
|
+
|
|
49
|
+
The single source of truth for disagreement state is the fixed `## 审查分歧账本` section in task.md — one parseable Markdown table. The phase-advance and `complete-task` gates read this section.
|
|
50
|
+
|
|
51
|
+
```markdown
|
|
52
|
+
## 审查分歧账本
|
|
53
|
+
|
|
54
|
+
<!-- One row per review finding; state machine / evidence rules in .agents/rules/review-handshake.md. The phase-advance and complete-task gates read this section. -->
|
|
55
|
+
|
|
56
|
+
| id | stage | round | severity | status | evidence |
|
|
57
|
+
|----|-------|-------|----------|--------|----------|
|
|
58
|
+
| CD-1 | code | 1 | blocker | open | review-code.md#1 |
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
- `id`: stage prefix + ordinal — analysis→`AN-`, plan→`PL-`, code→`CD-`.
|
|
62
|
+
- `stage` ∈ `{analysis, plan, code}` (plus the reserved value `post-review-commit`, used only for post-review exemption rows).
|
|
63
|
+
- `status` legal enum: `open` / `accepted` / `adjusted` / `refuted` / `cannot-judge` / `confirmed` / `needs-human-decision` / `closed` / `human-decided`.
|
|
64
|
+
- **Terminal set (gate passes)**: `{confirmed, closed, human-decided}`; everything else is blocking.
|
|
65
|
+
- **Write responsibility**: `review-*` raises a finding → upsert an `open` row; `*-task` responds → set four-state and fill `evidence`, `round` +1; next `review-*` → `confirmed` / back to `open` / `needs-human-decision`; an executor fix verified by the next review → `closed`; a human ruling → `human-decided`.
|
|
66
|
+
- **Backward compatible**: when task.md has no such section the gate treats it as no open disagreements and passes.
|
|
67
|
+
|
|
68
|
+
## post-review commit gate (code stage only)
|
|
69
|
+
|
|
70
|
+
- The highest-round `review-code` report records `Review Baseline Commit` (R, `git rev-parse HEAD`) and `Reviewed Diff Fingerprint` (F, full worktree diff fingerprint).
|
|
71
|
+
- `commit` reads only the highest-round `review-code` artifact. When that artifact is Approved, the pre-commit HEAD equals R, and the staged diff fingerprint equals F, task.md receives `last_reviewed_commit` (B, the new commit SHA).
|
|
72
|
+
- The `complete-task` `post-review-commit` gate prefers B; when B is absent or invalid, it falls back to R from the highest-round `review-code` artifact.
|
|
73
|
+
- If new commits touch code / rule paths after B / R, the gate blocks and requires a fresh `review-code`.
|
|
74
|
+
- **Exemption**: append a ledger row `| PRC-1 | post-review-commit | - | - | human-decided | <ruling note> |` recording that a human explicitly allowed those commits without re-review.
|
|
75
|
+
|
|
76
|
+
## Gate behavior cheat sheet
|
|
77
|
+
|
|
78
|
+
| Caller | `review-ledger` scope | `post-review-commit` |
|
|
79
|
+
|--------|-----------------------|----------------------|
|
|
80
|
+
| `plan-task` | only `analysis`-stage rows must be terminal | not attached |
|
|
81
|
+
| `code-task` | `analysis` + `plan`-stage rows must be terminal | not attached |
|
|
82
|
+
| `complete-task` | all stage rows must be terminal | attached (see above) |
|
|
83
|
+
| `analyze-task` | not attached (first stage) | not attached |
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# 双向审查握手协议
|
|
2
|
+
|
|
3
|
+
> 三阶段(analysis / plan / code)的执行方与检视方在执行 `review-*` 与 `*-task` 技能时共用本协议。
|
|
4
|
+
> 这是协议的**单一事实源**;各 SKILL 只 `Read` 本文件,不重复抄写词表。
|
|
5
|
+
|
|
6
|
+
## 核心原则
|
|
7
|
+
|
|
8
|
+
- **检视意见是待验证输入,不是执行命令**。执行方必须逐条核实后再处置,不默认认账、不盲目反驳。
|
|
9
|
+
- **对称证据负担**:无论接受还是反驳,每条处置都要附**相称证据**。"接受"不是零成本默认路径。
|
|
10
|
+
- **达成一致再推进**:存在未关闭分歧、替代修法、无法判断或 review 后新增提交时,不得静默进入下一阶段、归档或合并。
|
|
11
|
+
|
|
12
|
+
## 执行方四态处置(`*-task` 技能,Round ≥ 2 响应上一轮审查时)
|
|
13
|
+
|
|
14
|
+
对上一轮 `review-*` 的每条 finding,先 Read/Grep 核实其引用的 `file:line` / 命令,再落一个状态:
|
|
15
|
+
|
|
16
|
+
| 状态 | 含义 | 必附证据 |
|
|
17
|
+
|------|------|----------|
|
|
18
|
+
| `accepted` | 成立,将按建议修复 | 指向修复点的 `file:line` 或本轮将施加的改动说明 |
|
|
19
|
+
| `adjusted` | 成立,但采用替代修法 | 替代修法说明 + 为何更优;待检视方确认 |
|
|
20
|
+
| `refuted` | 核实后判定不成立 / 幻觉 / 基于错误 `file:line` | 反证(`file:line` 或命令原文);待检视方确认 |
|
|
21
|
+
| `cannot-judge` | 证据不足,无法判断 | 已尝试的核实路径;交检视方/人工 |
|
|
22
|
+
|
|
23
|
+
## 检视方回交义务(`review-*` 技能,对执行方响应复核时)
|
|
24
|
+
|
|
25
|
+
执行方给出 `adjusted` / `refuted` / `cannot-judge` 后,检视方必须逐条回应,不得复读原意见或无视:
|
|
26
|
+
|
|
27
|
+
- **撤回 finding** → 账本置 `confirmed`(接受反驳)。
|
|
28
|
+
- **接受替代修法** → 账本置 `confirmed`。
|
|
29
|
+
- **补充新证据后坚持** → 账本置回 `open`(带新证据,回到执行方)。
|
|
30
|
+
- **升级人工裁决** → 账本置 `needs-human-decision`。
|
|
31
|
+
|
|
32
|
+
## 收敛终止语义(防死循环)
|
|
33
|
+
|
|
34
|
+
- 单条 finding 的握手轮次上限 `MAX_HANDSHAKE_ROUNDS`,默认 **3**,可在 `.agents/.airc.json` 的 `review.maxHandshakeRounds` 覆盖。
|
|
35
|
+
- 某条 finding 的 `round` 达到上限仍未进入终态,必须强制置 `needs-human-decision`;gate 会拦截"达限却未升级"的行。
|
|
36
|
+
- `needs-human-decision` 持续阻塞完成,直到人工在 task.md `## 人工裁决` 段记录裁定并把该行翻为 `human-decided`。
|
|
37
|
+
|
|
38
|
+
## 同源模型收敛偏差缓解(文档级纪律)
|
|
39
|
+
|
|
40
|
+
执行方与检视方常由相近模型承担,天然容易互相同意。检视时遵守:
|
|
41
|
+
|
|
42
|
+
1. **先看证据再看结论**:先读 `git diff` / 产物本体并独立形成 findings,**再**读执行方的结论与响应,避免被其结论锚定。
|
|
43
|
+
2. **默认怀疑框架**:把"看起来没问题"视为未验证;每条放行都要有可复现证据支撑(见各 `review-*` 的 `证据原文` 段硬门禁)。
|
|
44
|
+
|
|
45
|
+
> 唯一的机械杠杆是**对称证据 gate**(账本非 `open` 行必须有证据);模型同源性本身不可机械校验,故本节为纪律而非门禁。
|
|
46
|
+
|
|
47
|
+
## 机械账本(task.md `## 审查分歧账本`)
|
|
48
|
+
|
|
49
|
+
分歧状态的**单一事实源**是 task.md 的固定段 `## 审查分歧账本`,单张可解析表。阶段推进与 `complete-task` 的 gate 读取本段。
|
|
50
|
+
|
|
51
|
+
```markdown
|
|
52
|
+
## 审查分歧账本
|
|
53
|
+
|
|
54
|
+
<!-- 每条 review finding 一行;状态机/证据规则见 .agents/rules/review-handshake.md。阶段推进与 complete-task gate 读取本段。 -->
|
|
55
|
+
|
|
56
|
+
| id | stage | round | severity | status | evidence |
|
|
57
|
+
|----|-------|-------|----------|--------|----------|
|
|
58
|
+
| CD-1 | code | 1 | blocker | open | review-code.md#1 |
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
- `id`:阶段前缀 + 序号——analysis→`AN-`、plan→`PL-`、code→`CD-`。
|
|
62
|
+
- `stage` ∈ `{analysis, plan, code}`(外加保留值 `post-review-commit`,仅用于 post-review 豁免行)。
|
|
63
|
+
- `status` 合法枚举:`open` / `accepted` / `adjusted` / `refuted` / `cannot-judge` / `confirmed` / `needs-human-decision` / `closed` / `human-decided`。
|
|
64
|
+
- **终态集合(gate 放行)**:`{confirmed, closed, human-decided}`;其余为阻塞态。
|
|
65
|
+
- **写入责任**:`review-*` 提 finding → upsert `open` 行;`*-task` 响应 → 改四态并填 `evidence`、`round` +1;下一轮 `review-*` → `confirmed` / 置回 `open` / `needs-human-decision`;执行方修复经下一轮 review 验证通过 → `closed`;人工裁决 → `human-decided`。
|
|
66
|
+
- **向后兼容**:task.md 无此段时,gate 视为无未决分歧而放行。
|
|
67
|
+
|
|
68
|
+
## post-review commit 门禁(仅 code 阶段)
|
|
69
|
+
|
|
70
|
+
- `review-code` 在最高轮报告中记录 `审查基线提交`(R,`git rev-parse HEAD`)和 `审查差异指纹`(F,完整工作区 diff fingerprint)。
|
|
71
|
+
- `commit` 只读取最高轮 `review-code` 产物;当该产物 Approved、提交前 HEAD 等于 R、且 staged diff fingerprint 等于 F 时,在 task.md 写入 `last_reviewed_commit`(B,新提交 SHA)。
|
|
72
|
+
- `complete-task` 的 `post-review-commit` gate 优先使用 B;B 缺失或非法时回退最高轮 `review-code` 的 R。
|
|
73
|
+
- 若 B / R 之后代码 / 规则路径出现新提交,gate 会拦截,要求重新 `review-code`。
|
|
74
|
+
- **豁免**:在账本追加一行 `| PRC-1 | post-review-commit | - | - | human-decided | <裁定说明> |`,记录人工明确允许该批提交免复审。
|
|
75
|
+
|
|
76
|
+
## gate 行为速查
|
|
77
|
+
|
|
78
|
+
| 调用方 | `review-ledger` 作用域 | `post-review-commit` |
|
|
79
|
+
|--------|------------------------|----------------------|
|
|
80
|
+
| `plan-task` | 仅 `analysis` 阶段行须终态 | 不挂 |
|
|
81
|
+
| `code-task` | `analysis` + `plan` 阶段行须终态 | 不挂 |
|
|
82
|
+
| `complete-task` | 全部阶段行须终态 | 挂(见上) |
|
|
83
|
+
| `analyze-task` | 不挂(首阶段) | 不挂 |
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
import { artifactName, maxRound } from "./review-artifacts.js";
|
|
5
|
+
|
|
6
|
+
export const DEFAULT_POST_REVIEW_GLOBS = [
|
|
7
|
+
".agents/skills",
|
|
8
|
+
".agents/scripts",
|
|
9
|
+
".agents/rules",
|
|
10
|
+
".agents/workflows",
|
|
11
|
+
"bin",
|
|
12
|
+
"lib",
|
|
13
|
+
"src",
|
|
14
|
+
"templates"
|
|
15
|
+
];
|
|
16
|
+
|
|
17
|
+
export function resolvePostReviewGlobs(config = {}, reviewConfig = {}) {
|
|
18
|
+
if (Array.isArray(config.post_review_globs)) {
|
|
19
|
+
return config.post_review_globs;
|
|
20
|
+
}
|
|
21
|
+
if (Array.isArray(reviewConfig.post_review_globs)) {
|
|
22
|
+
return reviewConfig.post_review_globs;
|
|
23
|
+
}
|
|
24
|
+
return DEFAULT_POST_REVIEW_GLOBS;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function findAuthoritativeReviewCodeArtifact(taskDir) {
|
|
28
|
+
const entries = fs.existsSync(taskDir) ? fs.readdirSync(taskDir) : [];
|
|
29
|
+
const round = maxRound(entries, "review-code");
|
|
30
|
+
if (round === 0) {
|
|
31
|
+
return { ok: false, round: 0, fileName: null, path: null };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const fileName = artifactName("review-code", round);
|
|
35
|
+
return {
|
|
36
|
+
ok: true,
|
|
37
|
+
round,
|
|
38
|
+
fileName,
|
|
39
|
+
path: path.join(taskDir, fileName)
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function extractReviewBaseline(content) {
|
|
44
|
+
const match = String(content).match(/^[-*]?\s*\*\*(?:审查基线提交|Review Baseline Commit)\*\*[::]\s*(.*?)\s*$/m);
|
|
45
|
+
return match ? match[1].trim().replace(/`/g, "") : "";
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function extractReviewDiffFingerprint(content) {
|
|
49
|
+
const match = String(content).match(/^[-*]?\s*\*\*(?:审查差异指纹|Reviewed Diff Fingerprint)\*\*[::]\s*(.*?)\s*$/m);
|
|
50
|
+
return match ? match[1].trim().replace(/`/g, "") : "";
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function parseReviewVerdict(content) {
|
|
54
|
+
const match = String(content).match(/^[-*]?\s*\*\*(?:总体结论|Overall Verdict)\*\*[::]\s*(.*?)\s*$/m);
|
|
55
|
+
return match ? match[1].trim() : "";
|
|
56
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
// Shared helpers for review-artifact parsing.
|
|
2
|
+
// Imported by both .agents/skills/code-task/scripts/detect-mode.js and
|
|
3
|
+
// .agents/scripts/validate-artifact.js so the round/verdict vocabulary stays
|
|
4
|
+
// in a single source of truth (prevents the cross-file drift this lifecycle
|
|
5
|
+
// is designed to eliminate).
|
|
6
|
+
import fs from "node:fs";
|
|
7
|
+
import path from "node:path";
|
|
8
|
+
|
|
9
|
+
export function escapeRegExp(value) {
|
|
10
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function maxRound(entries, stem) {
|
|
14
|
+
let max = 0;
|
|
15
|
+
for (const entry of entries) {
|
|
16
|
+
if (entry === `${stem}.md`) {
|
|
17
|
+
max = Math.max(max, 1);
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const match = entry.match(new RegExp(`^${escapeRegExp(stem)}-r(\\d+)\\.md$`));
|
|
22
|
+
if (match) {
|
|
23
|
+
max = Math.max(max, Number(match[1]));
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return max;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function artifactName(stem, round) {
|
|
30
|
+
return round === 1 ? `${stem}.md` : `${stem}-r${round}.md`;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function normalizeVerdict(raw) {
|
|
34
|
+
const value = String(raw).trim().toLowerCase();
|
|
35
|
+
if (value === "通过" || value === "approved") {
|
|
36
|
+
return "Approved";
|
|
37
|
+
}
|
|
38
|
+
if (value === "需要修改" || value === "changes requested") {
|
|
39
|
+
return "Changes Requested";
|
|
40
|
+
}
|
|
41
|
+
if (value === "拒绝" || value === "rejected") {
|
|
42
|
+
return "Rejected";
|
|
43
|
+
}
|
|
44
|
+
return "";
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function extractSection(content, names) {
|
|
48
|
+
const lines = content.split(/\r?\n/);
|
|
49
|
+
const nameSet = new Set(names);
|
|
50
|
+
const start = lines.findIndex((line) => {
|
|
51
|
+
const match = line.trim().match(/^##\s+(.+?)\s*$/);
|
|
52
|
+
return match ? nameSet.has(match[1]) : false;
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
if (start === -1) {
|
|
56
|
+
return "";
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const sectionLines = [];
|
|
60
|
+
for (let index = start + 1; index < lines.length; index += 1) {
|
|
61
|
+
if (/^##\s+/.test(lines[index])) {
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
sectionLines.push(lines[index]);
|
|
65
|
+
}
|
|
66
|
+
return sectionLines.join("\n");
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Parse the canonical verdict out of a review-* artifact.
|
|
70
|
+
// Returns { ok, verdict, message }. Verdict collapses Approved into
|
|
71
|
+
// "Approved-with-issues" when the findings counts are non-zero.
|
|
72
|
+
export function parseVerdict(reviewPath) {
|
|
73
|
+
if (!fs.existsSync(reviewPath)) {
|
|
74
|
+
return { ok: false, verdict: null, message: `Review artifact not found: ${path.basename(reviewPath)}` };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const content = fs.readFileSync(reviewPath, "utf8");
|
|
78
|
+
const summary = extractSection(content, ["审查摘要", "Review Summary"]);
|
|
79
|
+
const fileName = path.basename(reviewPath);
|
|
80
|
+
if (!summary) {
|
|
81
|
+
return { ok: false, verdict: null, message: `cannot locate review summary section in ${fileName}` };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const verdictMatch = summary.match(/^[-*]?\s*\*\*(?:总体结论|Overall Verdict)\*\*[::]\s*(.+?)\s*$/im);
|
|
85
|
+
if (!verdictMatch) {
|
|
86
|
+
return { ok: false, verdict: null, message: `cannot parse verdict in ${fileName}` };
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const verdict = normalizeVerdict(verdictMatch[1]);
|
|
90
|
+
if (!verdict) {
|
|
91
|
+
return {
|
|
92
|
+
ok: false,
|
|
93
|
+
verdict: null,
|
|
94
|
+
message: `unrecognized verdict '${verdictMatch[1].trim()}' in ${fileName}`
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (verdict !== "Approved") {
|
|
99
|
+
return { ok: true, verdict };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const findingsMatch = summary.match(/^[-*]?\s*\*\*(?:发现(AI 可处理)|Findings \(AI-actionable\))\*\*[::]\s*(.+?)\s*$/im);
|
|
103
|
+
if (!findingsMatch) {
|
|
104
|
+
return { ok: false, verdict, message: `cannot parse findings count in ${fileName}` };
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const counts = findingsMatch[1].match(/(\d+)\s*(?:阻塞项|blockers?).*?(\d+)\s*(?:主要|majors?).*?(\d+)\s*(?:次要|minors?)/i);
|
|
108
|
+
if (!counts) {
|
|
109
|
+
return { ok: false, verdict, message: `cannot parse findings count in ${fileName}` };
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const [, blockers, majors, minors] = counts.map(Number);
|
|
113
|
+
return {
|
|
114
|
+
ok: true,
|
|
115
|
+
verdict: blockers === 0 && majors === 0 && minors === 0 ? "Approved" : "Approved-with-issues"
|
|
116
|
+
};
|
|
117
|
+
}
|