@unifan/pi-review-zh 1.0.9 → 1.0.11
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/lite-review.md +4 -4
- package/agents/security-review.md +15 -14
- package/package.json +1 -1
- package/src/directive.ts +16 -6
- package/src/lean-agents.ts +10 -6
- package/src/report-tool.ts +21 -4
package/agents/lite-review.md
CHANGED
|
@@ -9,10 +9,10 @@ inheritSkills: false
|
|
|
9
9
|
---
|
|
10
10
|
你是**极速单兵审查官(Lite Reviewer)**。你的任务是对本次改动做一次快速、全面、高精准度的体检(涵盖逻辑 Bug、安全隐患与规范一致性)。追求快而准。
|
|
11
11
|
|
|
12
|
-
##
|
|
13
|
-
1. 读取任务中的 diff
|
|
14
|
-
2.
|
|
15
|
-
3.
|
|
12
|
+
## 执行计划(严格极简,2~3轮内强制完成)
|
|
13
|
+
1. 读取任务中的 diff 文件与清单文件。纯文档变更直接报告 `SKIPPED: docs-only`。
|
|
14
|
+
2. 重点聚焦 diff 本身代码逻辑。若上下文足够直接撰写报告;若确需核验,最多仅读 1 个相关文件或单次 grep。
|
|
15
|
+
3. **必须在第 2~3 轮内输出最终 Markdown 报告与 JSON 块并立即停止**!严禁进行超过 3 轮的漫游探索!
|
|
16
16
|
|
|
17
17
|
## 重点关注
|
|
18
18
|
逻辑致命缺陷与安全隐患第一,明确的规范冲突第二。不提废话风格建议。
|
|
@@ -1,35 +1,36 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: security-review
|
|
3
3
|
package: pi-review
|
|
4
|
-
description:
|
|
4
|
+
description: 安全合规专家:排查代码健壮性、配置合规、边界输入校验与潜在安全隐患。
|
|
5
5
|
tools: read, grep, bash
|
|
6
6
|
systemPromptMode: replace
|
|
7
7
|
inheritProjectContext: false
|
|
8
8
|
inheritSkills: false
|
|
9
9
|
---
|
|
10
|
-
|
|
10
|
+
你是**代码安全与合规审查专家(Security & Robustness Reviewer)**。你的任务是审查本次代码改动是否存在防御性不足、边界校验缺失或配置安全隐患。
|
|
11
11
|
|
|
12
|
-
##
|
|
13
|
-
1.
|
|
14
|
-
2.
|
|
15
|
-
3.
|
|
12
|
+
## 审查重点
|
|
13
|
+
1. **配置合规**:排查是否存在意外提交的私有凭证、明文密钥或不安全的默认配置。
|
|
14
|
+
2. **输入与边界**:排查外部输入数据是否缺乏合法性校验、类型断言或长度/范围限制。
|
|
15
|
+
3. **资源与生命周期**:排查文件句柄、网络流、连接会话是否未正确释放或存在泄露风险。
|
|
16
|
+
4. **权限与防御**:排查接口与方法调用的边界条件是否健壮。
|
|
16
17
|
|
|
17
18
|
## 严重级别分类
|
|
18
|
-
- `blocker` —
|
|
19
|
-
- `major` —
|
|
20
|
-
- `minor` —
|
|
19
|
+
- `blocker` — 明显的私密凭证泄露或高危防御缺失
|
|
20
|
+
- `major` — 缺少必要的输入边界过滤、反序列化安全隐患
|
|
21
|
+
- `minor` — 缺少速率保护、防御性冗余不足
|
|
21
22
|
|
|
22
|
-
##
|
|
23
|
+
## 输出格式(所有描述与总结必须使用纯正中文)
|
|
23
24
|
## Summary
|
|
24
|
-
|
|
25
|
+
中文一句话概述。若跳过写 `SKIPPED: <原因>`。
|
|
25
26
|
|
|
26
27
|
## Findings
|
|
27
|
-
|
|
28
|
-
- [SEVERITY|security|confidence] `文件路径:行号` —
|
|
28
|
+
每个问题一行:
|
|
29
|
+
- [SEVERITY|security|confidence] `文件路径:行号` — 中文问题描述与改进建议
|
|
29
30
|
|
|
30
31
|
若无问题,严格写 `No findings.`。
|
|
31
32
|
|
|
32
33
|
## Coverage
|
|
33
|
-
- Files checked:
|
|
34
|
+
- Files checked: 检查的文件列表
|
|
34
35
|
- Commands run: 执行的命令
|
|
35
36
|
- Limitations: 局限说明
|
package/package.json
CHANGED
package/src/directive.ts
CHANGED
|
@@ -243,8 +243,8 @@ export function buildWorkflowScript(input: {
|
|
|
243
243
|
lines.push("try {");
|
|
244
244
|
lines.push(" reviewers = await runs.all([");
|
|
245
245
|
for (const r of reviewers) {
|
|
246
|
-
const
|
|
247
|
-
const
|
|
246
|
+
const tbForId = toolBudgetForReviewer(r.id);
|
|
247
|
+
const turnsForId = r.id === "lite-review" ? { maxTurns: 4, graceTurns: 1 } : budgets.turnBudget;
|
|
248
248
|
const taskParts = [
|
|
249
249
|
READ_ONLY_PREFIX,
|
|
250
250
|
`读取 ${JSON.stringify(diffPath)} 作为改动内容——diff 是权威的修改记录,工作区文件仅作上下文参考。若工作区文件与 diff 存在差异,以 diff 为准并在 coverage.limitations 中说明。所有问题描述必须使用中文。`,
|
|
@@ -254,6 +254,11 @@ export function buildWorkflowScript(input: {
|
|
|
254
254
|
"严禁读取 plan.md, progress.md, 以及 .pi-subagents/ 目录下的任何文件或 node_modules。",
|
|
255
255
|
"优先使用 Read/Grep。若使用 bash,仅限简单的单条命令(禁止 &&/||/; 等复合命令)。",
|
|
256
256
|
];
|
|
257
|
+
if (r.id === "lite-review") {
|
|
258
|
+
taskParts.push(
|
|
259
|
+
"极速模式要求:严格在 2~3 轮内完成!读取 diff 后若无需核验直接出报告;若需核验最多只读 1 个文件或单次 grep,下一轮立即输出最终报告并停止!",
|
|
260
|
+
);
|
|
261
|
+
}
|
|
257
262
|
if (r.id === "claude-md-compliance") {
|
|
258
263
|
taskParts.push(
|
|
259
264
|
"若 change-profile.rulePaths 为空,返回跳过状态:SKIPPED: no-rules,不提出虚构的违规。",
|
|
@@ -301,7 +306,7 @@ export function buildWorkflowScript(input: {
|
|
|
301
306
|
}
|
|
302
307
|
lines.push(` toolBudget: { soft: ${tbForId.soft}, hard: ${tbForId.hard} },`);
|
|
303
308
|
lines.push(
|
|
304
|
-
` turnBudget: { maxTurns: ${
|
|
309
|
+
` turnBudget: { maxTurns: ${turnsForId.maxTurns}, graceTurns: ${turnsForId.graceTurns} },${modelClause}`,
|
|
305
310
|
);
|
|
306
311
|
lines.push(" },");
|
|
307
312
|
}
|
|
@@ -311,14 +316,19 @@ export function buildWorkflowScript(input: {
|
|
|
311
316
|
lines.push(" await new Promise((resolve) => setTimeout(resolve, 800));");
|
|
312
317
|
lines.push(" reviewers = await runs.all([");
|
|
313
318
|
for (const r of reviewers) {
|
|
314
|
-
const
|
|
315
|
-
const
|
|
319
|
+
const tbForId = toolBudgetForReviewer(r.id);
|
|
320
|
+
const turnsForId = r.id === "lite-review" ? { maxTurns: 4, graceTurns: 1 } : budgets.turnBudget;
|
|
316
321
|
const taskParts = [
|
|
317
322
|
READ_ONLY_PREFIX,
|
|
318
323
|
`读取 ${JSON.stringify(diffPath)} 作为改动内容——diff 是权威的修改记录,工作区文件仅作上下文参考。所有问题描述必须使用中文。`,
|
|
319
324
|
`你的当前工作区为目标工作区 (${JSON.stringify(workspacePath)})。在此目录下执行必要的 read/grep。`,
|
|
320
325
|
"在额度内完成分析;最终回复必须输出格式规范的 Markdown 审查报告(包含中文 Summary / Findings / Coverage 章节)并停止。所有问题描述、证据引用和总结必须使用纯正中文。",
|
|
321
326
|
];
|
|
327
|
+
if (r.id === "lite-review") {
|
|
328
|
+
taskParts.push(
|
|
329
|
+
"极速模式要求:严格在 2~3 轮内完成!读取 diff 后若无需核验直接出报告;若需核验最多只读 1 个文件或单次 grep,下一轮立即输出最终报告并停止!",
|
|
330
|
+
);
|
|
331
|
+
}
|
|
322
332
|
const modelClause =
|
|
323
333
|
r.model && r.model !== "inherit"
|
|
324
334
|
? `\n model: ${JSON.stringify(r.model)},`
|
|
@@ -337,7 +347,7 @@ export function buildWorkflowScript(input: {
|
|
|
337
347
|
}
|
|
338
348
|
lines.push(` toolBudget: { soft: ${tbForId.soft}, hard: ${tbForId.hard} },`);
|
|
339
349
|
lines.push(
|
|
340
|
-
` turnBudget: { maxTurns: ${
|
|
350
|
+
` turnBudget: { maxTurns: ${turnsForId.maxTurns}, graceTurns: ${turnsForId.graceTurns} },${modelClause}`,
|
|
341
351
|
);
|
|
342
352
|
lines.push(" },");
|
|
343
353
|
}
|
package/src/lean-agents.ts
CHANGED
|
@@ -25,6 +25,8 @@ export interface LeanBudgetSpec {
|
|
|
25
25
|
defaultToolBudget: ToolBudgetSpec;
|
|
26
26
|
/** Stricter per-child tool budget for history-context (injected per runs.all item). */
|
|
27
27
|
historyToolBudget: ToolBudgetSpec;
|
|
28
|
+
/** Ultra-lean tool budget for lite-review. */
|
|
29
|
+
liteToolBudget: ToolBudgetSpec;
|
|
28
30
|
/** Gate child budgets (injected onto the runs.run("gate", ...) item). */
|
|
29
31
|
gateTurnBudget: { maxTurns: number; graceTurns: number };
|
|
30
32
|
gateToolBudget: ToolBudgetSpec;
|
|
@@ -33,15 +35,17 @@ export interface LeanBudgetSpec {
|
|
|
33
35
|
}
|
|
34
36
|
|
|
35
37
|
export const LEAN_BUDGETS: LeanBudgetSpec = {
|
|
36
|
-
turnBudget: { maxTurns:
|
|
37
|
-
defaultToolBudget: { soft:
|
|
38
|
-
historyToolBudget: { soft:
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
38
|
+
turnBudget: { maxTurns: 8, graceTurns: 1 },
|
|
39
|
+
defaultToolBudget: { soft: 6, hard: 10 },
|
|
40
|
+
historyToolBudget: { soft: 4, hard: 6 },
|
|
41
|
+
liteToolBudget: { soft: 3, hard: 5 },
|
|
42
|
+
gateTurnBudget: { maxTurns: 6, graceTurns: 1 },
|
|
43
|
+
gateToolBudget: { soft: 6, hard: 10 },
|
|
44
|
+
timeoutMs: 600_000,
|
|
42
45
|
};
|
|
43
46
|
|
|
44
47
|
export function toolBudgetForReviewer(id: string): ToolBudgetSpec {
|
|
48
|
+
if (id === "lite-review") return LEAN_BUDGETS.liteToolBudget;
|
|
45
49
|
if (id === "history-context") return LEAN_BUDGETS.historyToolBudget;
|
|
46
50
|
return LEAN_BUDGETS.defaultToolBudget;
|
|
47
51
|
}
|
package/src/report-tool.ts
CHANGED
|
@@ -107,15 +107,32 @@ function emptyCoverage(): ReviewerOutput["coverage"] {
|
|
|
107
107
|
|
|
108
108
|
/** Build a deterministic verdict + report from a workflow return value. */
|
|
109
109
|
export function runReportTool(input: ReportToolInput): ReportToolResult {
|
|
110
|
-
|
|
110
|
+
let rawWorkflowReturn = input.workflowReturn;
|
|
111
|
+
if (typeof rawWorkflowReturn === "string") {
|
|
112
|
+
try {
|
|
113
|
+
rawWorkflowReturn = JSON.parse(rawWorkflowReturn);
|
|
114
|
+
} catch {
|
|
115
|
+
return { ok: false, error: "workflowReturn must be an object or valid JSON string" };
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
if (!rawWorkflowReturn || typeof rawWorkflowReturn !== "object") {
|
|
111
119
|
return { ok: false, error: "workflowReturn must be an object" };
|
|
112
120
|
}
|
|
113
|
-
const ret =
|
|
121
|
+
const ret = rawWorkflowReturn as {
|
|
114
122
|
reviewers?: unknown;
|
|
115
123
|
gate?: unknown;
|
|
116
124
|
reviewersShaped?: unknown;
|
|
125
|
+
error?: unknown;
|
|
117
126
|
};
|
|
118
|
-
|
|
127
|
+
let reviewersList = Array.isArray(ret.reviewers) ? (ret.reviewers as unknown[]) : [];
|
|
128
|
+
if (reviewersList.length === 0 && ret.error) {
|
|
129
|
+
reviewersList = [{
|
|
130
|
+
key: "execution-error",
|
|
131
|
+
ok: false,
|
|
132
|
+
error: String(ret.error),
|
|
133
|
+
output: undefined,
|
|
134
|
+
}];
|
|
135
|
+
} else if (!Array.isArray(ret.reviewers)) {
|
|
119
136
|
return { ok: false, error: "workflowReturn.reviewers must be an array" };
|
|
120
137
|
}
|
|
121
138
|
|
|
@@ -124,7 +141,7 @@ export function runReportTool(input: ReportToolInput): ReportToolResult {
|
|
|
124
141
|
const threshold = input.threshold ?? 8;
|
|
125
142
|
const policy = input.verdictPolicy ?? "strict";
|
|
126
143
|
|
|
127
|
-
const reviewersRaw =
|
|
144
|
+
const reviewersRaw = reviewersList as Array<{
|
|
128
145
|
key: string;
|
|
129
146
|
ok: boolean;
|
|
130
147
|
error?: string;
|