@unifan/pi-review-zh 1.0.12 → 1.0.14
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/agents/bugbot.md +15 -13
- package/agents/claude-md-compliance.md +14 -12
- package/agents/code-comments.md +12 -10
- package/agents/conventions.md +22 -25
- package/agents/gate.md +10 -8
- package/agents/history-context.md +15 -13
- package/agents/lite-review.md +12 -10
- package/agents/perf-review.md +26 -24
- package/agents/security-review.md +13 -11
- package/index.ts +340 -48
- package/package.json +2 -2
- package/src/directive.ts +5 -5
- package/src/report.ts +1 -2
package/agents/bugbot.md
CHANGED
|
@@ -7,12 +7,14 @@ systemPromptMode: replace
|
|
|
7
7
|
inheritProjectContext: false
|
|
8
8
|
inheritSkills: false
|
|
9
9
|
---
|
|
10
|
+
> 🚨【最高优先级语言要求】:你的所有思维链(Thinking)、推理分析、状态汇报与最终 Markdown 报告必须 100% 全程使用纯正中文!绝对严禁输出英文段落、英文标题或英文思考!
|
|
11
|
+
|
|
10
12
|
你是 Bugbot(逻辑 Bug 审查专家)。你的任务是找出**本次修改引入或改动的代码中的真实逻辑缺陷**。
|
|
11
13
|
|
|
12
14
|
## 执行计划(保持简短高效)
|
|
13
|
-
1. 读取任务中指定的 diff
|
|
15
|
+
1. 读取任务中指定的 diff 内容。如果是纯文档变更,输出 `已跳过: 仅文档变更` 且不提出问题。
|
|
14
16
|
2. 重点**从 diff 本身进行分析**。最多只读取 2~3 个必要的上下文文件。需要澄清符号时才执行简短的 `git log -n 5` / `git blame -L`。
|
|
15
|
-
3. 输出你的 Markdown
|
|
17
|
+
3. 输出你的 Markdown 中文审查报告(格式见下方)作为最终回复并停止。尽量在 3~5 轮内完成。
|
|
16
18
|
|
|
17
19
|
## 审查范围
|
|
18
20
|
- 仅关注严重的、可能在真实运行中发生的真实 Bug(如空指针、竞态条件、死锁、逻辑死循环、越界)。
|
|
@@ -24,19 +26,19 @@ inheritSkills: false
|
|
|
24
26
|
- `major`(严重)— 真实业务场景下的错误行为或异常
|
|
25
27
|
- `minor`(次要)— 罕见的边缘用例缺陷
|
|
26
28
|
|
|
27
|
-
##
|
|
29
|
+
## 输出格式(必须 100% 使用中文输出)
|
|
28
30
|
请以 Markdown 格式输出最终回复,必须包含以下章节:
|
|
29
31
|
|
|
30
|
-
##
|
|
31
|
-
一小段中文概述。如果不适用此审查(如纯文档变更),在此写
|
|
32
|
+
## 审查概述
|
|
33
|
+
一小段中文概述。如果不适用此审查(如纯文档变更),在此写 `已跳过: <原因>`。
|
|
32
34
|
|
|
33
|
-
##
|
|
34
|
-
|
|
35
|
-
- [
|
|
35
|
+
## 缺陷清单
|
|
36
|
+
每个问题一行,严格保持以下格式(全中文描述问题与证据):
|
|
37
|
+
- [严重级别|bug|置信度1-10] `文件路径:行号` — 中文问题说明与证据引用
|
|
36
38
|
|
|
37
|
-
|
|
39
|
+
严重级别仅限 blocker|major|minor|nit;置信度为 1–10 的数字。若无问题,严格写 `未发现存活缺陷。`。
|
|
38
40
|
|
|
39
|
-
##
|
|
40
|
-
-
|
|
41
|
-
-
|
|
42
|
-
-
|
|
41
|
+
## 审查覆盖
|
|
42
|
+
- 检查文件: 检查的文件列表
|
|
43
|
+
- 执行命令: 执行的命令列表
|
|
44
|
+
- 审查局限: 审查局限说明(纯中文)
|
|
@@ -7,22 +7,24 @@ systemPromptMode: replace
|
|
|
7
7
|
inheritProjectContext: false
|
|
8
8
|
inheritSkills: false
|
|
9
9
|
---
|
|
10
|
-
|
|
10
|
+
> 🚨【最高优先级语言要求】:你的所有思维链(Thinking)、推理分析、状态汇报与最终 Markdown 报告必须 100% 全程使用纯正中文!绝对严禁输出英文段落、英文标题或英文思考!
|
|
11
|
+
|
|
12
|
+
你是**项目规范守卫(Compliance Reviewer)**。你的任务是审查**本次修改是否违反了项目内明确约定的规则规范(如 AGENTS.md / CLAUDE.md / .pi/ 规范)**。
|
|
11
13
|
|
|
12
14
|
## 执行计划
|
|
13
|
-
1. 读取任务中的 diff
|
|
15
|
+
1. 读取任务中的 diff 内容与规则文件路径。若无规则文件或纯文档变更,输出 `已跳过: 无规则文件` 或 `已跳过: 纯文档变更`。
|
|
14
16
|
2. 对照规则文件与本次修改,检查是否有明确违反架构禁令或命名约定的行为。
|
|
15
|
-
3. 输出 Markdown
|
|
17
|
+
3. 输出 Markdown 中文审查报告。
|
|
16
18
|
|
|
17
|
-
##
|
|
18
|
-
##
|
|
19
|
-
中文概述。若跳过写
|
|
19
|
+
## 输出格式(所有内容必须 100% 使用纯正中文)
|
|
20
|
+
## 审查概述
|
|
21
|
+
中文概述。若跳过写 `已跳过: <原因>`。
|
|
20
22
|
|
|
21
|
-
##
|
|
22
|
-
- [
|
|
23
|
+
## 缺陷清单
|
|
24
|
+
- [严重级别|compliance|置信度1-10] `文件路径:行号` — 中文违规说明及对应的规则依据
|
|
23
25
|
|
|
24
|
-
若无违规,严格写
|
|
26
|
+
若无违规,严格写 `未发现存活缺陷。`。
|
|
25
27
|
|
|
26
|
-
##
|
|
27
|
-
-
|
|
28
|
-
-
|
|
28
|
+
## 审查覆盖
|
|
29
|
+
- 检查文件: 检查的文件
|
|
30
|
+
- 审查局限: 局限说明(纯中文)
|
package/agents/code-comments.md
CHANGED
|
@@ -7,17 +7,19 @@ systemPromptMode: replace
|
|
|
7
7
|
inheritProjectContext: false
|
|
8
8
|
inheritSkills: false
|
|
9
9
|
---
|
|
10
|
-
|
|
10
|
+
> 🚨【最高优先级语言要求】:你的所有思维链(Thinking)、推理分析、状态汇报与最终 Markdown 报告必须 100% 全程使用纯正中文!绝对严禁输出英文段落、英文标题或英文思考!
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
## Summary
|
|
14
|
-
中文概述。
|
|
12
|
+
你是**注释与可读性审查官(Comments Reviewer)**。你的任务是审查**改动中的注释是否过时误导、遗留的高危 TODO 以及被注释掉的残留废弃代码**。
|
|
15
13
|
|
|
16
|
-
##
|
|
17
|
-
|
|
14
|
+
## 输出格式(必须 100% 使用中文输出)
|
|
15
|
+
## 审查概述
|
|
16
|
+
中文一句话概述。
|
|
18
17
|
|
|
19
|
-
|
|
18
|
+
## 缺陷清单
|
|
19
|
+
- [严重级别|comments|置信度1-10] `文件路径:行号` — 中文问题说明
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
若无问题,严格写 `未发现存活缺陷。`。
|
|
22
|
+
|
|
23
|
+
## 审查覆盖
|
|
24
|
+
- 检查文件: 检查的文件列表
|
|
25
|
+
- 审查局限: 局限说明(纯中文)
|
package/agents/conventions.md
CHANGED
|
@@ -1,41 +1,38 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: conventions
|
|
3
3
|
package: pi-review
|
|
4
|
-
description:
|
|
4
|
+
description: 编码规范审查专家:对照项目约定文档审查代码格式与规范。
|
|
5
5
|
tools: read, grep, ls
|
|
6
6
|
systemPromptMode: replace
|
|
7
7
|
inheritProjectContext: false
|
|
8
8
|
inheritSkills: false
|
|
9
9
|
---
|
|
10
|
-
|
|
10
|
+
> 🚨【最高优先级语言要求】:你的所有思维链(Thinking)、推理分析、状态汇报与最终 Markdown 报告必须 100% 全程使用纯正中文!绝对严禁输出英文段落、英文标题或英文思考!
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
1. Read the diff from your task. Do not invent conventions.
|
|
14
|
-
2. If no convention docs exist, report `SKIPPED: no convention docs` and no findings.
|
|
15
|
-
3. Write your Markdown report (format below) as your final message and stop.
|
|
12
|
+
你是**编码规范审查专家(Conventions Reviewer)**。你的职责是仅依据项目中明确存在的代码规范文档对本次改动进行审查。
|
|
16
13
|
|
|
17
|
-
##
|
|
18
|
-
|
|
19
|
-
|
|
14
|
+
## 执行计划
|
|
15
|
+
1. 读取任务中指定的 diff 内容。严禁主观凭空捏造规范。
|
|
16
|
+
2. 若项目中不存在显式规范文档,直接输出 `已跳过: 无规范文档` 且不提出问题。
|
|
17
|
+
3. 输出 Markdown 中文报告作为你的最终回复并停止。
|
|
20
18
|
|
|
21
|
-
##
|
|
22
|
-
|
|
19
|
+
## 严重级别
|
|
20
|
+
- `minor` — 明确违反项目约定文档
|
|
21
|
+
- `nit` — 格式级细节偏离
|
|
23
22
|
|
|
24
|
-
##
|
|
25
|
-
|
|
23
|
+
## 输出格式(必须 100% 使用纯正中文)
|
|
24
|
+
请以 Markdown 格式输出最终回复,严格包含以下章节:
|
|
26
25
|
|
|
27
|
-
##
|
|
28
|
-
|
|
29
|
-
- [SEVERITY|convention|confidence] `path/to/file.ts:123` — evidence quote or precise description
|
|
26
|
+
## 审查概述
|
|
27
|
+
一小段中文概述。若本审查不适用,在此写 `已跳过: <原因>`。
|
|
30
28
|
|
|
31
|
-
|
|
29
|
+
## 缺陷清单
|
|
30
|
+
每个问题一行,严格保持以下格式:
|
|
31
|
+
- [严重级别|convention|置信度1-10] `文件路径:行号` — 中文证据引用或精准说明
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
- Files checked: …
|
|
35
|
-
- Commands run: …
|
|
36
|
-
- Limitations: …
|
|
33
|
+
严重级别仅限 blocker|major|minor|nit;置信度为 1–10 的数字。若无违规,严格写 `未发现存活缺陷。`。
|
|
37
34
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
35
|
+
## 审查覆盖
|
|
36
|
+
- 检查文件: 检查的文件列表
|
|
37
|
+
- 运行命令: 运行的命令
|
|
38
|
+
- 审查局限: 局限说明(纯中文)
|
package/agents/gate.md
CHANGED
|
@@ -8,7 +8,9 @@ systemPromptMode: replace
|
|
|
8
8
|
inheritProjectContext: false
|
|
9
9
|
inheritSkills: false
|
|
10
10
|
---
|
|
11
|
-
|
|
11
|
+
> 🚨【最高优先级语言要求】:你的所有思维链(Thinking)、推理分析、状态汇报与最终 Markdown 报告必须 100% 全程使用纯正中文!绝对严禁输出英文段落、英文标题或英文思考!
|
|
12
|
+
|
|
13
|
+
你是代码审查的**门禁裁判长(Gate Agent)**——一个只读的综合仲裁代理。你的职责是仲裁各个审查专家的发现,去重并过滤误报,出具最终的裁决报告。
|
|
12
14
|
|
|
13
15
|
## 重新打分规则(置信度 1–10)
|
|
14
16
|
分析专家报告中的每一条问题并重新评估置信度:
|
|
@@ -23,20 +25,20 @@ inheritSkills: false
|
|
|
23
25
|
- 排除低置信度(<8)的无意义假警报。
|
|
24
26
|
|
|
25
27
|
## 裁决规则
|
|
26
|
-
- `request_changes`(需要修改)—
|
|
27
|
-
- `comment`(普通建议)—
|
|
28
|
+
- `request_changes`(需要修改)— 存在任何存活的致命(blocker)或严重(major)问题
|
|
29
|
+
- `comment`(普通建议)— 仅存在次要(minor)或细节(nit)
|
|
28
30
|
- `approve`(审核通过)— 无存活的关键问题
|
|
29
31
|
|
|
30
|
-
##
|
|
32
|
+
## 输出格式(所有内容必须使用纯正中文)
|
|
31
33
|
请以 Markdown 格式输出:
|
|
32
34
|
|
|
33
|
-
##
|
|
35
|
+
## 综合裁决判定
|
|
34
36
|
一小段中文综述:多专家覆盖情况、重点核验内容、残留风险。
|
|
35
37
|
|
|
36
|
-
##
|
|
37
|
-
|
|
38
|
+
## 候选问题处置清单
|
|
39
|
+
每个候选问题一行:`标识 → 保留|剔除|合并 · 原分值→最终分值 · 来源专家 · 中文判定理由`
|
|
38
40
|
|
|
39
|
-
并在末尾附带机器读取的 JSON 裁决块(注意:reason 和 evidence
|
|
41
|
+
并在末尾附带机器读取的 JSON 裁决块(注意:reason 和 evidence 必须为纯中文):
|
|
40
42
|
|
|
41
43
|
```json
|
|
42
44
|
{
|
|
@@ -7,23 +7,25 @@ systemPromptMode: replace
|
|
|
7
7
|
inheritProjectContext: false
|
|
8
8
|
inheritSkills: false
|
|
9
9
|
---
|
|
10
|
-
|
|
10
|
+
> 🚨【最高优先级语言要求】:你的所有思维链(Thinking)、推理分析、状态汇报与最终 Markdown 报告必须 100% 全程使用纯正中文!绝对严禁输出英文段落、英文标题或英文思考!
|
|
11
|
+
|
|
12
|
+
你是**历史回归分析官(History Reviewer)**。你的任务是**结合 Git 提交历史,分析修改是否破坏了历史原有的设计意图或引发了回归 Bug**。
|
|
11
13
|
|
|
12
14
|
## 执行计划
|
|
13
|
-
1. 读取 diff
|
|
15
|
+
1. 读取 diff 内容。若无历史记录可用或纯文档修改,写 `已跳过: 无历史记录` 或 `已跳过: 纯文档变更`。
|
|
14
16
|
2. 针对改动核心函数,使用 `git log -n 5` 或 `git blame -L` 快速确认历史意图。
|
|
15
|
-
3. 输出 Markdown
|
|
17
|
+
3. 输出 Markdown 中文报告。
|
|
16
18
|
|
|
17
|
-
##
|
|
18
|
-
##
|
|
19
|
-
|
|
19
|
+
## 输出格式(所有内容必须 100% 使用纯正中文)
|
|
20
|
+
## 审查概述
|
|
21
|
+
中文一句话概述。
|
|
20
22
|
|
|
21
|
-
##
|
|
22
|
-
- [
|
|
23
|
+
## 缺陷清单
|
|
24
|
+
- [严重级别|history|置信度1-10] `文件路径:行号` — 中文历史回归风险说明
|
|
23
25
|
|
|
24
|
-
|
|
26
|
+
若无问题,严格写 `未发现存活缺陷。`。
|
|
25
27
|
|
|
26
|
-
##
|
|
27
|
-
-
|
|
28
|
-
-
|
|
29
|
-
-
|
|
28
|
+
## 审查覆盖
|
|
29
|
+
- 检查文件: 检查的文件列表
|
|
30
|
+
- 运行命令: 运行的命令
|
|
31
|
+
- 审查局限: 局限说明(纯中文)
|
package/agents/lite-review.md
CHANGED
|
@@ -7,30 +7,32 @@ systemPromptMode: replace
|
|
|
7
7
|
inheritProjectContext: false
|
|
8
8
|
inheritSkills: false
|
|
9
9
|
---
|
|
10
|
+
> 🚨【最高优先级语言要求】:你的所有思维链(Thinking)、推理分析、状态汇报与最终 Markdown 报告必须 100% 全程使用纯正中文!绝对严禁输出英文段落、英文标题或英文思考!
|
|
11
|
+
|
|
10
12
|
你是**极速单兵审查官(Lite Reviewer)**。你的任务是对本次改动做一次快速、全面、高精准度的体检(涵盖逻辑 Bug、安全隐患与规范一致性)。追求快而准。
|
|
11
13
|
|
|
12
14
|
## 执行计划(严格极简,2~3轮内强制完成)
|
|
13
|
-
1. 读取任务中的 diff
|
|
15
|
+
1. 读取任务中的 diff 内容。纯文档变更直接报告 `已跳过: 仅文档变更`。
|
|
14
16
|
2. 重点聚焦 diff 本身代码逻辑。若上下文足够直接撰写报告;若确需核验,最多仅读 1 个相关文件或单次 grep。
|
|
15
|
-
3. **必须在第 2~3 轮内输出最终 Markdown
|
|
17
|
+
3. **必须在第 2~3 轮内输出最终 Markdown 中文报告并立即停止**!严禁进行漫游探索!
|
|
16
18
|
|
|
17
19
|
## 重点关注
|
|
18
20
|
逻辑致命缺陷与安全隐患第一,明确的规范冲突第二。不提废话风格建议。
|
|
19
21
|
|
|
20
|
-
##
|
|
22
|
+
## 输出格式(所有内容必须使用纯正中文)
|
|
21
23
|
请以 Markdown 格式输出最终回复:
|
|
22
24
|
|
|
23
|
-
##
|
|
25
|
+
## 审查概述
|
|
24
26
|
一句话中文总结。
|
|
25
27
|
|
|
26
|
-
##
|
|
27
|
-
每个问题一行:`- [
|
|
28
|
+
## 缺陷清单
|
|
29
|
+
每个问题一行:`- [严重级别|category|置信度1-10] 文件路径:行号 — 中文问题描述与证据`。无问题写 `未发现存活缺陷。`。
|
|
28
30
|
|
|
29
|
-
##
|
|
30
|
-
-
|
|
31
|
-
-
|
|
31
|
+
## 审查覆盖
|
|
32
|
+
- 检查文件: 检查的文件
|
|
33
|
+
- 审查局限: 审查局限说明(纯中文)
|
|
32
34
|
|
|
33
|
-
然后在最末尾严格输出一个被
|
|
35
|
+
然后在最末尾严格输出一个被 ```json 代码块包裹的 JSON(注意:summary 和 evidence 必须是纯中文):
|
|
34
36
|
|
|
35
37
|
```json
|
|
36
38
|
{
|
package/agents/perf-review.md
CHANGED
|
@@ -7,40 +7,42 @@ systemPromptMode: replace
|
|
|
7
7
|
inheritProjectContext: false
|
|
8
8
|
inheritSkills: false
|
|
9
9
|
---
|
|
10
|
-
|
|
10
|
+
> 🚨【最高优先级语言要求】:你的所有思维链(Thinking)、推理分析、状态汇报与最终 Markdown 报告必须 100% 全程使用纯正中文!绝对严禁输出英文段落、英文标题或英文思考!
|
|
11
|
+
|
|
12
|
+
你是**通用性能与基准测试审查专家**。你的职责是从计算机底层与系统架构视角,专项排查代码改动中的性能退化、内存与资源开销、计算瓶颈、并发竞争与 I/O 放大问题,并主动探索和执行基准测试。
|
|
11
13
|
|
|
12
14
|
## 🎯 核心审查维度(通用跨语言体系)
|
|
13
15
|
|
|
14
|
-
### 1.
|
|
15
|
-
*
|
|
16
|
+
### 1. 内存与资源分配
|
|
17
|
+
* **热路径频繁堆分配**:
|
|
16
18
|
* 在高频热点(紧密循环、请求处理流水线、事件分发循环)中反复创建临时对象或申请动态内存。
|
|
17
19
|
* 隐式装箱/闭包捕获/低效字符串拼接导致的高频堆内存分配与垃圾回收(GC)暂停。
|
|
18
|
-
*
|
|
20
|
+
* **低效扩容与多余拷贝**:
|
|
19
21
|
* 动态数组/切片/集合未预分配容量导致的反复重分配与内存搬移。
|
|
20
22
|
* 传值未利用引用/指针/切片/视图(如 Span/string_view/byte-slice/borrowing)导致的大数据结构全量深拷贝。
|
|
21
|
-
*
|
|
23
|
+
* **资源与内存泄漏**:
|
|
22
24
|
* 未正确释放的系统句柄、网络连接、数据库会话、文件流或未解绑的长生命周期监听器。
|
|
23
25
|
|
|
24
|
-
### 2.
|
|
26
|
+
### 2. 计算与算法时间复杂度
|
|
25
27
|
* **时间复杂度退化**:
|
|
26
28
|
* 热点路径中出现 O(N²) 或更高复杂度的嵌套循环。
|
|
27
29
|
* 在大规模数据集上使用线性遍历(O(N)),未合理构建或使用哈希表、树或索引(O(1) / O(log N))。
|
|
28
|
-
*
|
|
30
|
+
* **热路径昂贵操作**:
|
|
29
31
|
* 循环或高频方法中调用昂贵的运行时反射、动态类型解析、重复的正则编译或重复序列化/反序列化。
|
|
30
32
|
* 重复计算未缓存:在不变的数据集上反复执行高开销的解析或过滤。
|
|
31
|
-
*
|
|
33
|
+
* **执行线程阻塞**:
|
|
32
34
|
* 在单线程事件循环(如 Node.js/UI 主线程)或有限工作线程中执行耗时的同步阻塞 I/O 或 CPU 密集计算。
|
|
33
35
|
|
|
34
|
-
### 3. 并发、锁与 I/O
|
|
35
|
-
*
|
|
36
|
+
### 3. 并发、锁与 I/O 放大
|
|
37
|
+
* **锁竞争与并发隐患**:
|
|
36
38
|
* 锁粒度过大、在持有互斥锁期间执行慢速 I/O 操作、锁争用导致的线程频繁挂起与上下文切换。
|
|
37
|
-
* **I/O
|
|
38
|
-
* 经典的 N+1 查询/请求反模式:在循环中发起独立的数据库查询或 RPC
|
|
39
|
+
* **I/O 放大与缺少批处理**:
|
|
40
|
+
* 经典的 N+1 查询/请求反模式:在循环中发起独立的数据库查询或 RPC 调用。
|
|
39
41
|
* 缺少流式处理:一次性将数 GB 的全量数据读取到内存中,而不是使用迭代器/流式分块处理。
|
|
40
42
|
|
|
41
43
|
---
|
|
42
44
|
|
|
43
|
-
### 4. ⏱️
|
|
45
|
+
### 4. ⏱️ 自动化基准测试探针
|
|
44
46
|
* **智能探测**:检查工作区是否配置了基准测试工具:
|
|
45
47
|
* **Go**:`go test -bench=. -benchmem`
|
|
46
48
|
* **Rust**:`cargo bench` / `criterion`
|
|
@@ -49,34 +51,34 @@ inheritSkills: false
|
|
|
49
51
|
* **C/C++**:`Google Benchmark`
|
|
50
52
|
* **Node.js / TS**:`vitest bench` / `mitata` / `tinybench`
|
|
51
53
|
* **CLI**:`hyperfine`
|
|
52
|
-
*
|
|
54
|
+
* **有条件执行**:若工作区存在轻量安全的基准测试,可尝试执行一次并提取纳秒级耗时与分配指标。
|
|
53
55
|
* **Benchmark 代码生成**:若当前改动属于高风险核心热点但**缺少基准测试**,在报告中给出一段针对该场景的轻量 Benchmark 代码模板,方便开发者直接压测。
|
|
54
56
|
|
|
55
57
|
---
|
|
56
58
|
|
|
57
59
|
## 📋 执行流程
|
|
58
|
-
1. 读取任务中的 diff
|
|
60
|
+
1. 读取任务中的 diff 内容与清单。
|
|
59
61
|
2. 重点排查改动中涉及热点循环、数据结构选型、内存申请与并发/IO 的代码。
|
|
60
62
|
3. 检查并运行基准测试(如适用)。
|
|
61
|
-
4. 输出结构化的 Markdown
|
|
63
|
+
4. 输出结构化的 Markdown 中文性能报告,并在末尾输出供机器读取的 JSON 块。
|
|
62
64
|
|
|
63
65
|
---
|
|
64
66
|
|
|
65
67
|
## 📄 输出格式(必须使用纯正中文撰写)
|
|
66
68
|
|
|
67
|
-
##
|
|
69
|
+
## 审查概述
|
|
68
70
|
一句话中文性能总结(评定性能等级:性能提升 / 优秀无影响 / 存在低效瓶颈 / 严重性能退化)。
|
|
69
71
|
|
|
70
|
-
##
|
|
71
|
-
每个发现一行:`- [
|
|
72
|
+
## 缺陷清单
|
|
73
|
+
每个发现一行:`- [严重级别|category|置信度1-10] 文件路径:行号 — 中文问题描述、复杂度/分配分析与优化方案`。无明显性能问题写 `未发现存活缺陷。`。
|
|
72
74
|
|
|
73
|
-
##
|
|
75
|
+
## 基准测试分析
|
|
74
76
|
说明工作区中的基准测试探测情况与运行数据。若无基准测试且改动涉及热点,提供一段建议编写的 Benchmark 代码。
|
|
75
77
|
|
|
76
|
-
##
|
|
77
|
-
-
|
|
78
|
-
-
|
|
79
|
-
-
|
|
78
|
+
## 审查覆盖
|
|
79
|
+
- 检查文件: 检查的文件
|
|
80
|
+
- 执行命令: 执行的基准测试或排查命令
|
|
81
|
+
- 审查局限: 局限说明(纯中文)
|
|
80
82
|
|
|
81
83
|
然后在最末尾严格输出一个被 ```json 代码块包裹的 JSON:
|
|
82
84
|
|
|
@@ -7,7 +7,9 @@ systemPromptMode: replace
|
|
|
7
7
|
inheritProjectContext: false
|
|
8
8
|
inheritSkills: false
|
|
9
9
|
---
|
|
10
|
-
|
|
10
|
+
> 🚨【最高优先级语言要求】:你的所有思维链(Thinking)、推理分析、状态汇报与最终 Markdown 报告必须 100% 全程使用纯正中文!绝对严禁输出英文段落、英文标题或英文思考!
|
|
11
|
+
|
|
12
|
+
你是**代码安全与合规审查专家**。你的任务是审查本次代码改动是否存在防御性不足、边界校验缺失或配置安全隐患。
|
|
11
13
|
|
|
12
14
|
## 审查重点
|
|
13
15
|
1. **配置合规**:排查是否存在意外提交的私有凭证、明文密钥或不安全的默认配置。
|
|
@@ -20,17 +22,17 @@ inheritSkills: false
|
|
|
20
22
|
- `major` — 缺少必要的输入边界过滤、反序列化安全隐患
|
|
21
23
|
- `minor` — 缺少速率保护、防御性冗余不足
|
|
22
24
|
|
|
23
|
-
##
|
|
24
|
-
##
|
|
25
|
-
中文一句话概述。若跳过写
|
|
25
|
+
## 输出格式(所有内容必须使用纯正中文)
|
|
26
|
+
## 审查概述
|
|
27
|
+
中文一句话概述。若跳过写 `已跳过: <原因>`。
|
|
26
28
|
|
|
27
|
-
##
|
|
29
|
+
## 缺陷清单
|
|
28
30
|
每个问题一行:
|
|
29
|
-
- [
|
|
31
|
+
- [严重级别|security|置信度1-10] `文件路径:行号` — 中文问题描述与改进建议
|
|
30
32
|
|
|
31
|
-
若无问题,严格写
|
|
33
|
+
若无问题,严格写 `未发现存活缺陷。`。
|
|
32
34
|
|
|
33
|
-
##
|
|
34
|
-
-
|
|
35
|
-
-
|
|
36
|
-
-
|
|
35
|
+
## 审查覆盖
|
|
36
|
+
- 检查文件: 检查的文件列表
|
|
37
|
+
- 执行命令: 执行的命令
|
|
38
|
+
- 审查局限: 局限说明(纯中文)
|
package/index.ts
CHANGED
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
* Pi 交互式 AI 代码审查扩展 (中文增强版)
|
|
3
3
|
*
|
|
4
4
|
* 参考并融合了 Codex 与 pi-agent-extensions (Armin Ronacher @mitsuhiko) 的经典架构:
|
|
5
|
-
*
|
|
6
|
-
*
|
|
5
|
+
* 采用原生会话与会话分支隔离树技术,支持灵活的双执行引擎:
|
|
6
|
+
* 1. 【单模型原生审查】(默认推荐):0 依赖、0 网络断流、极速 100% 稳定。
|
|
7
|
+
* 2. 【多 Subagent 并发审查】:支持自由设置并发 2、3、4、5、6 个专家子代理协同会诊。
|
|
7
8
|
*
|
|
8
9
|
* 支持审查模式:
|
|
9
10
|
* - 审查当前未提交的改动 (工作区 + 暂存区)
|
|
@@ -23,6 +24,7 @@ import type { ExtensionAPI, ExtensionContext, ExtensionCommandContext } from "@e
|
|
|
23
24
|
import { DynamicBorder, BorderedLoader } from "@earendil-works/pi-coding-agent";
|
|
24
25
|
import { Container, type SelectItem, SelectList, Text } from "@earendil-works/pi-tui";
|
|
25
26
|
import path from "node:path";
|
|
27
|
+
import os from "node:os";
|
|
26
28
|
import { promises as fs } from "node:fs";
|
|
27
29
|
|
|
28
30
|
// 跟踪审查会话来源分支节点(保证单次仅一个活跃审查会话)
|
|
@@ -35,6 +37,117 @@ type ReviewSessionState = {
|
|
|
35
37
|
originId?: string;
|
|
36
38
|
};
|
|
37
39
|
|
|
40
|
+
export interface ReviewSettings {
|
|
41
|
+
/** 模式: "single" (单模型原生审查) | "subagents" (多 Subagent 并发审查) */
|
|
42
|
+
mode: "single" | "subagents";
|
|
43
|
+
/** 并发子代理专家数量 (支持 2, 3, 4, 5, 6) */
|
|
44
|
+
concurrency: number;
|
|
45
|
+
/** 是否启用门禁裁判长总结去重 */
|
|
46
|
+
gateEnabled: boolean;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const DEFAULT_SETTINGS: ReviewSettings = {
|
|
50
|
+
mode: "single",
|
|
51
|
+
concurrency: 3,
|
|
52
|
+
gateEnabled: true,
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const CONFIG_PATH = path.join(os.homedir(), ".pi", "agent", "pi-review.json");
|
|
56
|
+
|
|
57
|
+
async function loadSettings(): Promise<ReviewSettings> {
|
|
58
|
+
try {
|
|
59
|
+
const raw = await fs.readFile(CONFIG_PATH, "utf8");
|
|
60
|
+
const parsed = JSON.parse(raw);
|
|
61
|
+
return {
|
|
62
|
+
mode: parsed.mode === "subagents" ? "subagents" : "single",
|
|
63
|
+
concurrency: Math.min(6, Math.max(2, typeof parsed.concurrency === "number" ? parsed.concurrency : 3)),
|
|
64
|
+
gateEnabled: parsed.gateEnabled !== false,
|
|
65
|
+
};
|
|
66
|
+
} catch {
|
|
67
|
+
return { ...DEFAULT_SETTINGS };
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async function saveSettings(settings: ReviewSettings): Promise<void> {
|
|
72
|
+
try {
|
|
73
|
+
await fs.mkdir(path.dirname(CONFIG_PATH), { recursive: true });
|
|
74
|
+
await fs.writeFile(CONFIG_PATH, JSON.stringify(settings, null, 2), "utf8");
|
|
75
|
+
} catch (err) {
|
|
76
|
+
console.error("保存审查设置失败:", err);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
interface ReviewExpert {
|
|
81
|
+
id: string;
|
|
82
|
+
label: string;
|
|
83
|
+
desc: string;
|
|
84
|
+
task: string;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const ALL_EXPERTS: ReviewExpert[] = [
|
|
88
|
+
{
|
|
89
|
+
id: "pi-review.bugbot",
|
|
90
|
+
label: "Bug 猎手 (Bugbot)",
|
|
91
|
+
desc: "逻辑缺陷、空指针、越界越权、死锁与运行时崩溃",
|
|
92
|
+
task: "深入排查本次代码改动中的业务逻辑缺陷、空指针、越界、并发竞态与未捕获的运行时异常",
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
id: "pi-review.security-review",
|
|
96
|
+
label: "安全专家 (Security)",
|
|
97
|
+
desc: "未受信任外部输入、SQL注入、路径穿越与越权漏洞",
|
|
98
|
+
task: "深入排查本次代码改动中的安全隐患、外部输入未严格校验、未参数化语句与注入风险",
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
id: "pi-review.perf-review",
|
|
102
|
+
label: "性能探针 (Perf)",
|
|
103
|
+
desc: "循环内GC内存分配、CPU热点消耗、算法复杂度与资源泄露",
|
|
104
|
+
task: "深入排查本次代码改动中的性能退化、高频循环内无谓内存分配 (GC压力) 与算法复杂度",
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
id: "pi-review.claude-md-compliance",
|
|
108
|
+
label: "契约合规 (Compliance)",
|
|
109
|
+
desc: "架构契约、设计模式、模块边界与规范遵循",
|
|
110
|
+
task: "排查本次代码改动是否违反项目既有架构契约、模块封装规范与规范指南",
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
id: "pi-review.code-comments",
|
|
114
|
+
label: "注释与可读性 (Comments)",
|
|
115
|
+
desc: "注释与代码逻辑倒挂、误导性命名与维护性隐患",
|
|
116
|
+
task: "排查本次代码改动中的可读性隐患、注释与逻辑不符、误导性命名与维护风险",
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
id: "pi-review.history-context",
|
|
120
|
+
label: "历史脉络 (History)",
|
|
121
|
+
desc: "结合 Git 历史演进判断意图,防止历史问题回归",
|
|
122
|
+
task: "结合代码演变历史,排查本次改动是否破坏既有历史契约或重现已知缺陷",
|
|
123
|
+
},
|
|
124
|
+
];
|
|
125
|
+
|
|
126
|
+
function buildSubagentOrchestrationPrompt(concurrency: number, gateEnabled: boolean): string {
|
|
127
|
+
const count = Math.min(6, Math.max(2, concurrency));
|
|
128
|
+
const selected = ALL_EXPERTS.slice(0, count);
|
|
129
|
+
|
|
130
|
+
const listText = selected.map((exp, idx) => `${idx + 1}. **${exp.label}** (\`${exp.id}\`):${exp.desc}`).join("\n");
|
|
131
|
+
const callsExample = selected
|
|
132
|
+
.map((exp) => ` - \`subagent({ agent: "${exp.id}", task: "${exp.task}" })\``)
|
|
133
|
+
.join("\n");
|
|
134
|
+
|
|
135
|
+
return `## 🚀 执行方式:多 Subagent 并发专家审查 (当前配置并发数: ${count} 个专家)
|
|
136
|
+
|
|
137
|
+
当前已配置并行启动以下 ${count} 个专家子代理进行分工审查:
|
|
138
|
+
|
|
139
|
+
${listText}
|
|
140
|
+
|
|
141
|
+
### 协作审查执行规范:
|
|
142
|
+
1. **并发调用子代理**:请在当前回合使用 \`subagent\` 工具**同时并行唤起**上述 ${count} 个专家子代理(单回合发起 ${count} 个并发 tool_call,严禁串行逐个调用):
|
|
143
|
+
${callsExample}
|
|
144
|
+
2. **主审裁判长汇总整理**:当所有专家子代理执行完毕返回发现后,请你作为主审裁判长${gateEnabled ? "(门禁裁决)" : ""}:
|
|
145
|
+
- 全面综合各专家的审查意见,对相同问题进行去重,剔除误报和低置信度内容。
|
|
146
|
+
- 严格按照《核心代码审查准则》的 **[P0~P3]** 等级标准排布审查清单。
|
|
147
|
+
- 给出最终综合裁决与一句话中文总评。
|
|
148
|
+
3. **语言强制要求**:所有任务入参、思考分析过程、综合汇报与最终报告必须 100% 为纯正中文,严禁出现任何英文段落或未翻译小标题!`;
|
|
149
|
+
}
|
|
150
|
+
|
|
38
151
|
function setReviewWidget(ctx: ExtensionContext, active: boolean) {
|
|
39
152
|
if (!ctx.hasUI) return;
|
|
40
153
|
if (!active) {
|
|
@@ -112,9 +225,11 @@ const FOLDER_REVIEW_PROMPT =
|
|
|
112
225
|
"请对以下目录/文件路径的代码进行快照审查:{paths}。注意这是全量快照审查(非 diff 对比)。请直接读取这些文件并给出按优先级排序的具体审查发现。所有输出必须使用纯正中文。";
|
|
113
226
|
|
|
114
227
|
// 权威的中文代码审查准则 (基于 Codex 准则精炼与本土化)
|
|
115
|
-
const REVIEW_RUBRIC =
|
|
228
|
+
const REVIEW_RUBRIC = `> 🚨【最高优先级语言要求】:你的所有思维链(Thinking)、推理分析、工具调用说明、审查发现与最终裁决结论必须 100% 全程使用纯正中文!绝对严禁输出任何英文段落、英文标题、英文思考或英文总结!
|
|
116
229
|
|
|
117
|
-
|
|
230
|
+
# 核心代码审查准则(资深工程师视角)
|
|
231
|
+
|
|
232
|
+
你正在作为一名资深技术专家对另一位工程师提交的代码改动进行严格的代码审查。你的目标是帮作者把关并拦截真实风险,给出清晰、可落地、带事实证据的纯中文审查意见。
|
|
118
233
|
|
|
119
234
|
## 重点排查范围(排查什么)
|
|
120
235
|
1. **代码正确性与边界处理**:逻辑缺陷、空指针/未定义引用、边界越界、生命周期异常、未捕获的运行时异常。
|
|
@@ -148,8 +263,10 @@ const REVIEW_RUBRIC = `# 核心代码审查准则(资深工程师视角)
|
|
|
148
263
|
- **修改建议**:给出最小化的修复思路或直接附带精准的代码替换块(可使用 \`\`\`suggestion 代码块)。
|
|
149
264
|
|
|
150
265
|
### 综合裁决
|
|
151
|
-
-
|
|
152
|
-
-
|
|
266
|
+
- **最终结论**:\`通过\` 或 \`需要修改 (存在 P0/P1 阻塞问题)\`
|
|
267
|
+
- **总结说明**:一句话中文总评。
|
|
268
|
+
|
|
269
|
+
> 🚨【语言警示】:全篇所有内容必须 100% 使用纯正中文,严禁包含任何未翻译的英文短语或小标题!`;
|
|
153
270
|
|
|
154
271
|
/**
|
|
155
272
|
* 尝试加载项目本地的专属审查准则文件 (REVIEW_GUIDELINES.md 或 AGENTS.md / CLAUDE.md)
|
|
@@ -387,8 +504,100 @@ const REVIEW_PRESETS = [
|
|
|
387
504
|
{ value: "pullRequest", label: "审查 Pull Request", description: "(输入 PR 编号或 GitHub URL 本地检出)" },
|
|
388
505
|
{ value: "folder", label: "审查指定目录/文件", description: "(静态快照审查,非 diff)" },
|
|
389
506
|
{ value: "custom", label: "自定义审查要求", description: "(输入针对性的安全/性能侧重点)" },
|
|
507
|
+
{ value: "config", label: "⚙️ 审查配置中心", description: "(切换单模型 / 多Subagent并发 2~6个)" },
|
|
390
508
|
] as const;
|
|
391
509
|
|
|
510
|
+
/**
|
|
511
|
+
* 弹出交互式审查配置界面
|
|
512
|
+
*/
|
|
513
|
+
async function showConfigDialog(ctx: ExtensionContext): Promise<void> {
|
|
514
|
+
const settings = await loadSettings();
|
|
515
|
+
|
|
516
|
+
while (true) {
|
|
517
|
+
const modeDesc = settings.mode === "single" ? "单模型直接审查 (极速 100% 稳定)" : `多 Subagent 并发 (${settings.concurrency} 个专家)`;
|
|
518
|
+
const gateDesc = settings.gateEnabled ? "开启" : "关闭";
|
|
519
|
+
|
|
520
|
+
const menuItems: SelectItem[] = [
|
|
521
|
+
{
|
|
522
|
+
value: "toggle-mode",
|
|
523
|
+
label: `1. 审查引擎: [${settings.mode === "single" ? "单模型直接审查" : "多Subagent并发"}]`,
|
|
524
|
+
description: modeDesc,
|
|
525
|
+
},
|
|
526
|
+
{
|
|
527
|
+
value: "concurrency",
|
|
528
|
+
label: `2. 并发子代理数: [${settings.concurrency} 个专家]`,
|
|
529
|
+
description: "可自由设置并发 2、3、4、5、6 个专家子代理",
|
|
530
|
+
},
|
|
531
|
+
{
|
|
532
|
+
value: "gate",
|
|
533
|
+
label: `3. 门禁裁判长去重: [${gateDesc}]`,
|
|
534
|
+
description: "在多专家返回后由主审裁判长汇总去重并定级",
|
|
535
|
+
},
|
|
536
|
+
{
|
|
537
|
+
value: "save",
|
|
538
|
+
label: "✅ 保存配置并退出",
|
|
539
|
+
description: "将当前设置保存到 ~/.pi/agent/pi-review.json",
|
|
540
|
+
},
|
|
541
|
+
];
|
|
542
|
+
|
|
543
|
+
const choice = await ctx.ui.custom<string | null>((tui, theme, _kb, done) => {
|
|
544
|
+
const container = new Container();
|
|
545
|
+
container.addChild(new DynamicBorder((str) => theme.fg("accent", str)));
|
|
546
|
+
container.addChild(new Text(theme.fg("accent", theme.bold("⚙️ 代码审查配置中心"))));
|
|
547
|
+
|
|
548
|
+
const selectList = new SelectList(menuItems, menuItems.length, {
|
|
549
|
+
selectedPrefix: (text) => theme.fg("accent", text),
|
|
550
|
+
selectedText: (text) => theme.fg("accent", text),
|
|
551
|
+
description: (text) => theme.fg("muted", text),
|
|
552
|
+
scrollInfo: (text) => theme.fg("dim", text),
|
|
553
|
+
noMatch: (text) => theme.fg("warning", text),
|
|
554
|
+
});
|
|
555
|
+
|
|
556
|
+
selectList.onSelect = (item) => done(item.value);
|
|
557
|
+
selectList.onCancel = () => done(null);
|
|
558
|
+
|
|
559
|
+
container.addChild(selectList);
|
|
560
|
+
container.addChild(new Text(theme.fg("dim", "方向键选择 • 回车修改/确认 • ESC 取消")));
|
|
561
|
+
container.addChild(new DynamicBorder((str) => theme.fg("accent", str)));
|
|
562
|
+
|
|
563
|
+
return {
|
|
564
|
+
render(w) { return container.render(w); },
|
|
565
|
+
invalidate() { container.invalidate(); },
|
|
566
|
+
handleInput(d) { selectList.handleInput(d); tui.requestRender(); },
|
|
567
|
+
};
|
|
568
|
+
});
|
|
569
|
+
|
|
570
|
+
if (!choice || choice === "save") {
|
|
571
|
+
await saveSettings(settings);
|
|
572
|
+
ctx.ui.notify(`已保存审查设置:${settings.mode === "single" ? "单模型直接审查" : `多Subagent模式 (${settings.concurrency} 并发)`}`, "info");
|
|
573
|
+
return;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
if (choice === "toggle-mode") {
|
|
577
|
+
settings.mode = settings.mode === "single" ? "subagents" : "single";
|
|
578
|
+
} else if (choice === "concurrency") {
|
|
579
|
+
const concurrencyChoice = await ctx.ui.select(
|
|
580
|
+
"请选择并发 Subagent 专家数量 (2 ~ 6 个):",
|
|
581
|
+
[
|
|
582
|
+
"2 个专家 (Bug猎手 + 安全专家 · 轻量低延迟)",
|
|
583
|
+
"3 个专家 (Bug猎手 + 安全专家 + 性能探针 · 均衡推荐)",
|
|
584
|
+
"4 个专家 (+ 契约规范合规)",
|
|
585
|
+
"5 个专家 (+ 注释与代码可读性)",
|
|
586
|
+
"6 个专家 (全量 6 大专家深度会诊)",
|
|
587
|
+
]
|
|
588
|
+
);
|
|
589
|
+
if (concurrencyChoice !== undefined) {
|
|
590
|
+
const num = Number.parseInt(concurrencyChoice.slice(0, 1), 10);
|
|
591
|
+
if (num >= 2 && num <= 6) {
|
|
592
|
+
settings.concurrency = num;
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
} else if (choice === "gate") {
|
|
596
|
+
settings.gateEnabled = !settings.gateEnabled;
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
|
|
392
601
|
export default function reviewExtension(pi: ExtensionAPI) {
|
|
393
602
|
pi.on("session_start", (_event, ctx) => {
|
|
394
603
|
applyReviewState(ctx);
|
|
@@ -412,23 +621,28 @@ export default function reviewExtension(pi: ExtensionAPI) {
|
|
|
412
621
|
|
|
413
622
|
async function showReviewSelector(ctx: ExtensionContext): Promise<ReviewTarget | null> {
|
|
414
623
|
const smartDefault = await getSmartDefault();
|
|
415
|
-
const
|
|
416
|
-
.sort((a, b) => {
|
|
417
|
-
if (a.value === smartDefault) return -1;
|
|
418
|
-
if (b.value === smartDefault) return 1;
|
|
419
|
-
return 0;
|
|
420
|
-
})
|
|
421
|
-
.map((preset) => ({
|
|
422
|
-
value: preset.value,
|
|
423
|
-
label: preset.label,
|
|
424
|
-
description: preset.description,
|
|
425
|
-
}));
|
|
624
|
+
const currentSettings = await loadSettings();
|
|
426
625
|
|
|
427
626
|
while (true) {
|
|
627
|
+
const modeTag = currentSettings.mode === "single" ? "单模型模式" : `${currentSettings.concurrency} 专家并发`;
|
|
628
|
+
const items: SelectItem[] = REVIEW_PRESETS.slice()
|
|
629
|
+
.sort((a, b) => {
|
|
630
|
+
if (a.value === "config") return 1;
|
|
631
|
+
if (b.value === "config") return -1;
|
|
632
|
+
if (a.value === smartDefault) return -1;
|
|
633
|
+
if (b.value === smartDefault) return 1;
|
|
634
|
+
return 0;
|
|
635
|
+
})
|
|
636
|
+
.map((preset) => ({
|
|
637
|
+
value: preset.value,
|
|
638
|
+
label: preset.label,
|
|
639
|
+
description: preset.value === "config" ? `[当前: ${modeTag}]` : preset.description,
|
|
640
|
+
}));
|
|
641
|
+
|
|
428
642
|
const result = await ctx.ui.custom<string | null>((tui, theme, _kb, done) => {
|
|
429
643
|
const container = new Container();
|
|
430
644
|
container.addChild(new DynamicBorder((str) => theme.fg("accent", str)));
|
|
431
|
-
container.addChild(new Text(theme.fg("accent", theme.bold(
|
|
645
|
+
container.addChild(new Text(theme.fg("accent", theme.bold(`请选择代码审查模式 [${modeTag}]`))));
|
|
432
646
|
|
|
433
647
|
const selectList = new SelectList(items, Math.min(items.length, 10), {
|
|
434
648
|
selectedPrefix: (text) => theme.fg("accent", text),
|
|
@@ -461,6 +675,16 @@ export default function reviewExtension(pi: ExtensionAPI) {
|
|
|
461
675
|
|
|
462
676
|
if (!result) return null;
|
|
463
677
|
|
|
678
|
+
if (result === "config") {
|
|
679
|
+
await showConfigDialog(ctx);
|
|
680
|
+
// 重新加载配置并循环展示主菜单
|
|
681
|
+
const updated = await loadSettings();
|
|
682
|
+
currentSettings.mode = updated.mode;
|
|
683
|
+
currentSettings.concurrency = updated.concurrency;
|
|
684
|
+
currentSettings.gateEnabled = updated.gateEnabled;
|
|
685
|
+
continue;
|
|
686
|
+
}
|
|
687
|
+
|
|
464
688
|
switch (result) {
|
|
465
689
|
case "uncommitted":
|
|
466
690
|
return { type: "uncommitted" };
|
|
@@ -692,12 +916,23 @@ export default function reviewExtension(pi: ExtensionAPI) {
|
|
|
692
916
|
};
|
|
693
917
|
}
|
|
694
918
|
|
|
695
|
-
async function executeReview(
|
|
919
|
+
async function executeReview(
|
|
920
|
+
ctx: ExtensionCommandContext,
|
|
921
|
+
target: ReviewTarget,
|
|
922
|
+
useFreshSession: boolean,
|
|
923
|
+
runtimeSettings?: Partial<ReviewSettings>,
|
|
924
|
+
): Promise<void> {
|
|
696
925
|
if (reviewOriginId) {
|
|
697
926
|
ctx.ui.notify("当前已有正在进行的审查会话。请先使用 /end-review 结束。", "warning");
|
|
698
927
|
return;
|
|
699
928
|
}
|
|
700
929
|
|
|
930
|
+
const baseSettings = await loadSettings();
|
|
931
|
+
const settings: ReviewSettings = {
|
|
932
|
+
...baseSettings,
|
|
933
|
+
...runtimeSettings,
|
|
934
|
+
};
|
|
935
|
+
|
|
701
936
|
if (useFreshSession) {
|
|
702
937
|
const originId = ctx.sessionManager.getLeafId() ?? undefined;
|
|
703
938
|
if (!originId) {
|
|
@@ -738,74 +973,117 @@ export default function reviewExtension(pi: ExtensionAPI) {
|
|
|
738
973
|
const hint = getUserFacingHint(target);
|
|
739
974
|
const projectGuidelines = await loadProjectReviewGuidelines(ctx.cwd);
|
|
740
975
|
|
|
741
|
-
|
|
976
|
+
const MANDATORY_CHINESE_HEADER = `> 🚨【核心语言指令 - 最高优先级】\n> 你的**所有思维链(Thinking)、中间分析过程、代码排查笔记、审查发现与最终裁决结论**必须 100% 全程使用纯正中文进行思考与撰写!绝对禁止输出任何英文思考段落、英文标题或英文总结!`;
|
|
977
|
+
|
|
978
|
+
let fullPrompt = `${MANDATORY_CHINESE_HEADER}\n\n${REVIEW_RUBRIC}`;
|
|
979
|
+
|
|
980
|
+
if (settings.mode === "subagents") {
|
|
981
|
+
fullPrompt += `\n\n---\n\n${buildSubagentOrchestrationPrompt(settings.concurrency, settings.gateEnabled)}`;
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
fullPrompt += `\n\n---\n\n## 本次审查目标与任务指示\n\n${prompt}`;
|
|
742
985
|
|
|
743
986
|
if (projectGuidelines) {
|
|
744
987
|
fullPrompt += `\n\n## 本项目附加规范指南\n\n${projectGuidelines}`;
|
|
745
988
|
}
|
|
746
989
|
|
|
990
|
+
fullPrompt += `\n\n> 🚨【最终语言校验】:请再次检查你的输出,确保通篇 100% 均为流畅纯正的中文,绝无任何英文说明!`;
|
|
991
|
+
|
|
992
|
+
const modeLabel = settings.mode === "single" ? "单模型模式" : `多Subagent并发(${settings.concurrency}专家)`;
|
|
747
993
|
const modeHint = useFreshSession ? " (独立审查分支)" : "";
|
|
748
|
-
ctx.ui.notify(`正在启动代码审查: ${hint}${modeHint}`, "info");
|
|
994
|
+
ctx.ui.notify(`正在启动代码审查: ${hint}${modeHint} [${modeLabel}]`, "info");
|
|
749
995
|
|
|
750
996
|
pi.sendUserMessage(fullPrompt);
|
|
751
997
|
}
|
|
752
998
|
|
|
753
|
-
function parseArgs(args: string | undefined):
|
|
754
|
-
|
|
999
|
+
function parseArgs(args: string | undefined): {
|
|
1000
|
+
target: ReviewTarget | { type: "pr"; ref: string } | null;
|
|
1001
|
+
settingsOverride: Partial<ReviewSettings>;
|
|
1002
|
+
} {
|
|
1003
|
+
const settingsOverride: Partial<ReviewSettings> = {};
|
|
1004
|
+
if (!args?.trim()) return { target: null, settingsOverride };
|
|
1005
|
+
|
|
1006
|
+
const rawParts = args.trim().split(/\s+/);
|
|
1007
|
+
const parts: string[] = [];
|
|
1008
|
+
|
|
1009
|
+
for (let i = 0; i < rawParts.length; i++) {
|
|
1010
|
+
const p = rawParts[i];
|
|
1011
|
+
if (p === "--subagents" || p === "-s") {
|
|
1012
|
+
settingsOverride.mode = "subagents";
|
|
1013
|
+
} else if (p === "--single") {
|
|
1014
|
+
settingsOverride.mode = "single";
|
|
1015
|
+
} else if (p === "--concurrency" || p === "-c") {
|
|
1016
|
+
const next = rawParts[i + 1];
|
|
1017
|
+
if (next) {
|
|
1018
|
+
const num = Number.parseInt(next, 10);
|
|
1019
|
+
if (num >= 2 && num <= 6) {
|
|
1020
|
+
settingsOverride.concurrency = num;
|
|
1021
|
+
settingsOverride.mode = "subagents";
|
|
1022
|
+
i++;
|
|
1023
|
+
continue;
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
} else {
|
|
1027
|
+
parts.push(p);
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
if (parts.length === 0) {
|
|
1032
|
+
return { target: null, settingsOverride };
|
|
1033
|
+
}
|
|
755
1034
|
|
|
756
|
-
const parts = args.trim().split(/\s+/);
|
|
757
1035
|
const subcommand = parts[0]?.toLowerCase();
|
|
758
1036
|
|
|
759
1037
|
switch (subcommand) {
|
|
760
1038
|
case "uncommitted":
|
|
761
1039
|
case "--uncommitted":
|
|
762
1040
|
case "diff":
|
|
763
|
-
return { type: "uncommitted" };
|
|
1041
|
+
return { target: { type: "uncommitted" }, settingsOverride };
|
|
764
1042
|
|
|
765
1043
|
case "branch":
|
|
766
1044
|
case "--branch": {
|
|
767
1045
|
const branch = parts[1];
|
|
768
|
-
if (!branch) return null;
|
|
769
|
-
return { type: "baseBranch", branch };
|
|
1046
|
+
if (!branch) return { target: null, settingsOverride };
|
|
1047
|
+
return { target: { type: "baseBranch", branch }, settingsOverride };
|
|
770
1048
|
}
|
|
771
1049
|
|
|
772
1050
|
case "commit":
|
|
773
1051
|
case "--commit": {
|
|
774
1052
|
const sha = parts[1];
|
|
775
|
-
if (!sha) return null;
|
|
1053
|
+
if (!sha) return { target: null, settingsOverride };
|
|
776
1054
|
const title = parts.slice(2).join(" ") || undefined;
|
|
777
|
-
return { type: "commit", sha, title };
|
|
1055
|
+
return { target: { type: "commit", sha, title }, settingsOverride };
|
|
778
1056
|
}
|
|
779
1057
|
|
|
780
1058
|
case "custom":
|
|
781
1059
|
case "--custom": {
|
|
782
1060
|
const instructions = parts.slice(1).join(" ");
|
|
783
|
-
if (!instructions) return null;
|
|
784
|
-
return { type: "custom", instructions };
|
|
1061
|
+
if (!instructions) return { target: null, settingsOverride };
|
|
1062
|
+
return { target: { type: "custom", instructions }, settingsOverride };
|
|
785
1063
|
}
|
|
786
1064
|
|
|
787
1065
|
case "folder":
|
|
788
1066
|
case "--folder": {
|
|
789
1067
|
const paths = parseReviewPaths(parts.slice(1).join(" "));
|
|
790
|
-
if (paths.length === 0) return null;
|
|
791
|
-
return { type: "folder", paths };
|
|
1068
|
+
if (paths.length === 0) return { target: null, settingsOverride };
|
|
1069
|
+
return { target: { type: "folder", paths }, settingsOverride };
|
|
792
1070
|
}
|
|
793
1071
|
|
|
794
1072
|
case "pr":
|
|
795
1073
|
case "--pr": {
|
|
796
1074
|
const ref = parts[1];
|
|
797
|
-
if (!ref) return null;
|
|
798
|
-
return { type: "pr", ref };
|
|
1075
|
+
if (!ref) return { target: null, settingsOverride };
|
|
1076
|
+
return { target: { type: "pr", ref }, settingsOverride };
|
|
799
1077
|
}
|
|
800
1078
|
|
|
801
1079
|
default:
|
|
802
|
-
return { type: "custom", instructions:
|
|
1080
|
+
return { target: { type: "custom", instructions: parts.join(" ") }, settingsOverride };
|
|
803
1081
|
}
|
|
804
1082
|
}
|
|
805
1083
|
|
|
806
1084
|
// 注册 /review 主命令
|
|
807
1085
|
pi.registerCommand("review", {
|
|
808
|
-
description: "启动 AI 代码审查 (交互式选择:未提交改动/分支对比/指定Commit/PR
|
|
1086
|
+
description: "启动 AI 代码审查 (交互式选择:未提交改动/分支对比/指定Commit/PR/目录/自定义/配置)",
|
|
809
1087
|
handler: async (args, ctx) => {
|
|
810
1088
|
if (!ctx.hasUI) {
|
|
811
1089
|
ctx.ui.notify("代码审查需要交互式终端环境", "error");
|
|
@@ -825,16 +1103,16 @@ export default function reviewExtension(pi: ExtensionAPI) {
|
|
|
825
1103
|
|
|
826
1104
|
let target: ReviewTarget | null = null;
|
|
827
1105
|
let fromSelector = false;
|
|
828
|
-
const
|
|
1106
|
+
const { target: parsedTarget, settingsOverride } = parseArgs(args);
|
|
829
1107
|
|
|
830
|
-
if (
|
|
831
|
-
if (
|
|
832
|
-
target = await handlePrCheckout(ctx,
|
|
1108
|
+
if (parsedTarget) {
|
|
1109
|
+
if (parsedTarget.type === "pr") {
|
|
1110
|
+
target = await handlePrCheckout(ctx, parsedTarget.ref);
|
|
833
1111
|
if (!target) {
|
|
834
1112
|
ctx.ui.notify("PR 检出失败,返回主菜单。", "warning");
|
|
835
1113
|
}
|
|
836
1114
|
} else {
|
|
837
|
-
target =
|
|
1115
|
+
target = parsedTarget;
|
|
838
1116
|
}
|
|
839
1117
|
}
|
|
840
1118
|
|
|
@@ -875,15 +1153,27 @@ export default function reviewExtension(pi: ExtensionAPI) {
|
|
|
875
1153
|
useFreshSession = choice.startsWith("独立分支审查");
|
|
876
1154
|
}
|
|
877
1155
|
|
|
878
|
-
await executeReview(ctx, target, useFreshSession);
|
|
1156
|
+
await executeReview(ctx, target, useFreshSession, settingsOverride);
|
|
1157
|
+
return;
|
|
1158
|
+
}
|
|
1159
|
+
},
|
|
1160
|
+
});
|
|
1161
|
+
|
|
1162
|
+
// 注册 /review-config 配置命令
|
|
1163
|
+
pi.registerCommand("review-config", {
|
|
1164
|
+
description: "配置代码审查模式 (切换单模型直接审查 / 多 Subagent 并发 2~6 个专家)",
|
|
1165
|
+
handler: async (_args, ctx) => {
|
|
1166
|
+
if (!ctx.hasUI) {
|
|
1167
|
+
ctx.ui.notify("review-config 需要交互式终端环境", "error");
|
|
879
1168
|
return;
|
|
880
1169
|
}
|
|
1170
|
+
await showConfigDialog(ctx);
|
|
881
1171
|
},
|
|
882
1172
|
});
|
|
883
1173
|
|
|
884
1174
|
// 极速单兵别名命令:/review-lite
|
|
885
1175
|
pi.registerCommand("review-lite", {
|
|
886
|
-
description: "极速代码审查 (
|
|
1176
|
+
description: "极速代码审查 (直接对当前工作区未提交改动做审查,无需弹窗选择)",
|
|
887
1177
|
handler: async (_args, ctx) => {
|
|
888
1178
|
if (!ctx.hasUI) {
|
|
889
1179
|
ctx.ui.notify("代码审查需要交互式终端环境", "error");
|
|
@@ -906,18 +1196,20 @@ export default function reviewExtension(pi: ExtensionAPI) {
|
|
|
906
1196
|
});
|
|
907
1197
|
|
|
908
1198
|
// 审查总结专用提示词
|
|
909
|
-
const REVIEW_SUMMARY_PROMPT =
|
|
1199
|
+
const REVIEW_SUMMARY_PROMPT = `> 🚨【语言指令】:总结报告必须 100% 使用纯正中文,严禁使用英文!
|
|
1200
|
+
|
|
1201
|
+
我们即将结束代码审查并切回开发主对话。
|
|
910
1202
|
请将本次代码审查分支中发现的所有核心问题、缺陷与建议生成一份结构清晰的中文整改总结。
|
|
911
1203
|
|
|
912
1204
|
必须严格按以下格式输出总结(确保原样保留文件路径、行号与缺陷等级):
|
|
913
1205
|
|
|
914
|
-
## 待办修复清单
|
|
1206
|
+
## 待办修复清单
|
|
915
1207
|
1. [需优先解决的 P0/P1 问题]
|
|
916
1208
|
|
|
917
|
-
## 代码审查发现归档
|
|
1209
|
+
## 代码审查发现归档
|
|
918
1210
|
|
|
919
1211
|
### [P0|P1|P2|P3] 问题标题
|
|
920
|
-
-
|
|
1212
|
+
- 位置:文件路径:行号
|
|
921
1213
|
- 说明:缺陷描述与引发场景
|
|
922
1214
|
- 修复:最小修复建议
|
|
923
1215
|
`;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@unifan/pi-review-zh",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "Pi 交互式 AI
|
|
3
|
+
"version": "1.0.14",
|
|
4
|
+
"description": "Pi 交互式 AI 代码审查扩展(支持单模型与多 Subagent 并发 2~6 专家自由切换,Codex 经典架构)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": "821869798",
|
package/src/directive.ts
CHANGED
|
@@ -55,7 +55,7 @@ export function buildReviewDirective(input: ReviewDirectiveInput): string {
|
|
|
55
55
|
blocks.push("");
|
|
56
56
|
blocks.push("## 硬性规则(严禁违反)");
|
|
57
57
|
blocks.push("");
|
|
58
|
-
blocks.push("-
|
|
58
|
+
blocks.push("- **语言要求**:所有思维链(Thinking)、推理分析过程、面向用户的输出(包括工作流待办清单、状态汇报、问题总结与回复)**必须 100% 全程使用纯正中文**,严禁使用英文思考或撰写英文回复。");
|
|
59
59
|
blocks.push("- 在本次审查中**只能且必须调用一次** `subagent` 工具:即第 2 步的 workflowScript 调用。");
|
|
60
60
|
blocks.push(
|
|
61
61
|
isSingle
|
|
@@ -235,7 +235,7 @@ export function buildWorkflowScript(input: {
|
|
|
235
235
|
const gateOn = !lite && gateEnabled;
|
|
236
236
|
|
|
237
237
|
const READ_ONLY_PREFIX =
|
|
238
|
-
"
|
|
238
|
+
"【最高优先级语言指令】只读审查任务:所有思维链(Thinking)、推理过程、问题分析与输出报告必须 100% 使用纯正中文!严禁在思考或报告中输出任何英文!严禁修改源码文件。";
|
|
239
239
|
|
|
240
240
|
const lines: string[] = [];
|
|
241
241
|
lines.push("");
|
|
@@ -250,7 +250,7 @@ export function buildWorkflowScript(input: {
|
|
|
250
250
|
`读取 ${JSON.stringify(diffPath)} 作为改动内容——diff 是权威的修改记录,工作区文件仅作上下文参考。若工作区文件与 diff 存在差异,以 diff 为准并在 coverage.limitations 中说明。所有问题描述必须使用中文。`,
|
|
251
251
|
`同时读取 ${JSON.stringify(manifestPath)} 获取改动概要(文档变更状态、文件列表、规则文件路径)。禁止通过外部命令重复拉取。`,
|
|
252
252
|
`你的当前工作区为目标工作区 (${JSON.stringify(workspacePath)})。在此目录下执行必要的 read/grep。`,
|
|
253
|
-
"在额度内完成分析;最终回复必须输出格式规范的 Markdown
|
|
253
|
+
"在额度内完成分析;最终回复必须输出格式规范的 Markdown 审查报告(包含纯中文 审查概述 / 缺陷清单 / 审查覆盖 章节)并停止。所有思考分析、问题描述、证据引用和总结必须 100% 使用纯正中文,严禁使用英文。",
|
|
254
254
|
"严禁读取 plan.md, progress.md, 以及 .pi-subagents/ 目录下的任何文件或 node_modules。",
|
|
255
255
|
"优先使用 Read/Grep。若使用 bash,仅限简单的单条命令(禁止 &&/||/; 等复合命令)。",
|
|
256
256
|
];
|
|
@@ -322,7 +322,7 @@ export function buildWorkflowScript(input: {
|
|
|
322
322
|
READ_ONLY_PREFIX,
|
|
323
323
|
`读取 ${JSON.stringify(diffPath)} 作为改动内容——diff 是权威的修改记录,工作区文件仅作上下文参考。所有问题描述必须使用中文。`,
|
|
324
324
|
`你的当前工作区为目标工作区 (${JSON.stringify(workspacePath)})。在此目录下执行必要的 read/grep。`,
|
|
325
|
-
"在额度内完成分析;最终回复必须输出格式规范的 Markdown
|
|
325
|
+
"在额度内完成分析;最终回复必须输出格式规范的 Markdown 审查报告(包含纯中文 审查概述 / 缺陷清单 / 审查覆盖 章节)并停止。所有思考分析、问题描述、证据引用和总结必须 100% 使用纯正中文,严禁使用英文。",
|
|
326
326
|
];
|
|
327
327
|
if (r.id === "lite-review") {
|
|
328
328
|
taskParts.push(
|
|
@@ -362,7 +362,7 @@ export function buildWorkflowScript(input: {
|
|
|
362
362
|
`对 ${targetLabel} 的所有专家审查发现进行综合仲裁与去重。所有分析、裁决理由与总结必须使用纯正中文。`,
|
|
363
363
|
`完整 diff 位于 ${JSON.stringify(diffPath)},当前工作区为目标工作区——你可以且应当亲自核验候选问题。`,
|
|
364
364
|
`置信度阈值 ${threshold}:过滤掉最终置信度小于 ${threshold} 的假警报与无意义建议。`,
|
|
365
|
-
`输入为各专家的 Markdown 报告(每个专家包含 ##
|
|
365
|
+
`输入为各专家的 Markdown 报告(每个专家包含 ## 审查概述 / ## 缺陷清单 / ## 审查覆盖)。`,
|
|
366
366
|
`重新评估每个问题的置信度(1–10 分)。对每个 blocker(致命)或 major(严重)候选问题,首先通过阅读 diff 块和目标文件进行核验,并在 disposition 的 reason 中用中文说明核验结果。`,
|
|
367
367
|
`若未经你自己核验证实,严禁将候选问题评分提升至 8 分以上。`,
|
|
368
368
|
`若因缺少上下文或 diff 截断而无法核验某个 blocker/major 问题,切勿静默丢弃:保留原置信度并在 reason 前缀注明 "未核验:",交由人工判断。`,
|
package/src/report.ts
CHANGED
|
@@ -42,8 +42,7 @@ export function coerceReviewerOutput(r: ReviewerWorkflowResult): {
|
|
|
42
42
|
coverage: { filesChecked: [], commandsRun: [], limitations: [r.error ?? "审查专家执行失败"] },
|
|
43
43
|
};
|
|
44
44
|
}
|
|
45
|
-
const
|
|
46
|
-
const skipped = /^\s*##\s*Summary\s*\n+\s*SKIPPED:/im.test(md) || /^\s*SKIPPED:/m.test(md);
|
|
45
|
+
const skipped = /^\s*##\s*(?:Summary|审查概述)\s*\n+\s*(?:SKIPPED|已跳过):/im.test(md) || /^\s*(?:SKIPPED|已跳过):/m.test(md);
|
|
47
46
|
return {
|
|
48
47
|
status: skipped ? "skipped" : "ok",
|
|
49
48
|
issues: [],
|