@fyeeme/pi-review 2.1.0 → 2.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +58 -57
- package/src/tools/review_report.ts +63 -29
package/package.json
CHANGED
|
@@ -1,59 +1,60 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
2
|
+
"name": "@fyeeme/pi-review",
|
|
3
|
+
"version": "2.1.2",
|
|
4
|
+
"description": "Review & cleanup assets for pi: /code-review and /code-simplify commands dispatching declarative prompt templates (parallel strategy as frontmatter data) plus review methodology skills and finder/verifier agent definitions. Spawning lives in @fyeeme/pi-subagents; this package registers the review_report findings sink and the generic dispatcher.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"author": "fyeeme",
|
|
8
|
+
"engines": {
|
|
9
|
+
"node": ">=18"
|
|
10
|
+
},
|
|
11
|
+
"keywords": [
|
|
12
|
+
"pi",
|
|
13
|
+
"pi-package",
|
|
14
|
+
"code-review",
|
|
15
|
+
"simplify",
|
|
16
|
+
"cleanup",
|
|
17
|
+
"review"
|
|
18
|
+
],
|
|
19
|
+
"files": [
|
|
20
|
+
"*.ts",
|
|
21
|
+
"src/**/*.ts",
|
|
22
|
+
"skills/**/*.md",
|
|
23
|
+
"prompts/**/*.md",
|
|
24
|
+
"agents/**/*.md",
|
|
25
|
+
"README.md",
|
|
26
|
+
"LICENSE"
|
|
27
|
+
],
|
|
28
|
+
"pi": {
|
|
29
|
+
"extensions": [
|
|
30
|
+
"./index.ts"
|
|
31
|
+
],
|
|
32
|
+
"skills": [
|
|
33
|
+
"skills/code-review",
|
|
34
|
+
"skills/code-simplify"
|
|
35
|
+
]
|
|
36
|
+
},
|
|
37
|
+
"scripts": {
|
|
38
|
+
"test": "vitest --run",
|
|
39
|
+
"typecheck": "tsc"
|
|
40
|
+
},
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"@fyeeme/pi-subagents": "2.1.3"
|
|
43
|
+
},
|
|
44
|
+
"peerDependencies": {
|
|
45
|
+
"@earendil-works/pi-ai": ">=0.99.0",
|
|
46
|
+
"@earendil-works/pi-coding-agent": ">=0.99.0",
|
|
47
|
+
"@earendil-works/pi-tui": ">=0.99.0",
|
|
48
|
+
"typebox": ">=1.0.0"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"@earendil-works/pi-ai": "1.0.0",
|
|
52
|
+
"@earendil-works/pi-coding-agent": "1.0.0",
|
|
53
|
+
"@earendil-works/pi-agent-core": "1.0.0",
|
|
54
|
+
"@earendil-works/pi-tui": "1.0.0",
|
|
55
|
+
"@types/node": "22.19.19",
|
|
56
|
+
"jiti": "2.7.0",
|
|
57
|
+
"typebox": "1.1.38",
|
|
58
|
+
"typescript": "5.9.3"
|
|
59
|
+
}
|
|
59
60
|
}
|
|
@@ -99,6 +99,31 @@ const ReviewReportParams = Type.Object({
|
|
|
99
99
|
),
|
|
100
100
|
});
|
|
101
101
|
|
|
102
|
+
/**
|
|
103
|
+
* 结果 schema——与落盘 `.pi/review/*.json` 对象同构(design D2 单一事实源):
|
|
104
|
+
* `structuredContent` 与磁盘 JSON 消费方(codemode 脚本 vs CI/--fix)观察同值。
|
|
105
|
+
* 字段单一来源:复用 FindingParams.properties(入参与出参同形,仅追加 note)。
|
|
106
|
+
*/
|
|
107
|
+
const FindingOutput = Type.Object({
|
|
108
|
+
...FindingParams.properties,
|
|
109
|
+
/** normalize 附注(非法 outcome 归一化说明),仅详情渲染用。 */
|
|
110
|
+
note: Type.Optional(Type.String()),
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
const ReviewReportOutput = Type.Object({
|
|
114
|
+
level: Level,
|
|
115
|
+
reportId: Type.Union([Type.String(), Type.Null()]),
|
|
116
|
+
target: Type.Union([Type.String(), Type.Null()]),
|
|
117
|
+
filesChanged: Type.Union([Type.Number(), Type.Null()]),
|
|
118
|
+
fannedOut: Type.Union([Type.Boolean(), Type.Null()]),
|
|
119
|
+
generatedAt: Type.String(),
|
|
120
|
+
findings: Type.Array(FindingOutput),
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
/** Static 派生类型:reportData 的类型即出参 schema,类型层单一事实源(消除断言)。 */
|
|
124
|
+
type FindingOut = Static<typeof FindingOutput>;
|
|
125
|
+
type ReviewReportOut = Static<typeof ReviewReportOutput>;
|
|
126
|
+
|
|
102
127
|
interface ReviewReportDetails {
|
|
103
128
|
level: string;
|
|
104
129
|
findingsCount: number;
|
|
@@ -162,26 +187,13 @@ function normalizeFindings(findings: LooseFinding[]): { findings: LooseFinding[]
|
|
|
162
187
|
|
|
163
188
|
// --- render -----------------------------------------------------------------
|
|
164
189
|
|
|
165
|
-
interface FindingInput {
|
|
166
|
-
file: string;
|
|
167
|
-
line?: number;
|
|
168
|
-
category: string;
|
|
169
|
-
verdict?: string;
|
|
170
|
-
priority?: string;
|
|
171
|
-
short_summary?: string;
|
|
172
|
-
summary: string;
|
|
173
|
-
failure_scenario: string;
|
|
174
|
-
outcome?: string;
|
|
175
|
-
/** normalize 附注(如非法 outcome 归一化说明),仅渲染进详情块。 */
|
|
176
|
-
note?: string;
|
|
177
|
-
}
|
|
178
190
|
interface ReportInput {
|
|
179
191
|
level: string;
|
|
180
192
|
target?: string;
|
|
181
193
|
files_changed?: number;
|
|
182
194
|
fanned_out?: boolean;
|
|
183
195
|
reportId?: string;
|
|
184
|
-
findings:
|
|
196
|
+
findings: FindingOut[];
|
|
185
197
|
}
|
|
186
198
|
|
|
187
199
|
function fmtLoc(f: { file: string; line?: number }): string {
|
|
@@ -247,6 +259,9 @@ export const reviewReportTool = defineTool<typeof ReviewReportParams, ReviewRepo
|
|
|
247
259
|
"Use `review_report` only when the code-review skill instructs reporting findings; otherwise follow the active output format.",
|
|
248
260
|
],
|
|
249
261
|
parameters: ReviewReportParams,
|
|
262
|
+
// codemode 脚本/程序化调用方拿到 structuredContent(与磁盘 JSON 同构)而非文本;
|
|
263
|
+
// 模型侧行为(content/renderResult/落盘)不变。
|
|
264
|
+
outputSchema: ReviewReportOutput,
|
|
250
265
|
|
|
251
266
|
// 主防御:schema 校验之前清洗非法值(模型路径下校验失败即 throw、工具不执行,
|
|
252
267
|
// 因此 execute 内的防御对模型不可达)。返回符合 schema 的对象——非法 verdict
|
|
@@ -268,7 +283,38 @@ export const reviewReportTool = defineTool<typeof ReviewReportParams, ReviewRepo
|
|
|
268
283
|
const { findings: cleaned, notes } = normalizeFindings(
|
|
269
284
|
(params.findings ?? []) as unknown as LooseFinding[],
|
|
270
285
|
);
|
|
271
|
-
|
|
286
|
+
// 仅含已赋值字段的 finding:显式 undefined 键会让磁盘 JSON(serialize 丢弃)与
|
|
287
|
+
// structuredContent(原样返回)键集分叉;条件赋值保证两个视图逐键一致。
|
|
288
|
+
const findings: FindingOut[] = cleaned.map((f, i) => {
|
|
289
|
+
const note = notes.get(i);
|
|
290
|
+
const out: FindingOut = {
|
|
291
|
+
file: f.file,
|
|
292
|
+
category: f.category,
|
|
293
|
+
summary: f.summary,
|
|
294
|
+
failure_scenario: f.failure_scenario,
|
|
295
|
+
};
|
|
296
|
+
if (f.line !== undefined) out.line = f.line;
|
|
297
|
+
// 值域由 sanitizeFinding/normalizeFindings 保证(非法值已剔除/归一化),枚举收窄安全。
|
|
298
|
+
if (f.verdict !== undefined) out.verdict = f.verdict as FindingOut["verdict"];
|
|
299
|
+
if (f.priority !== undefined) out.priority = f.priority as FindingOut["priority"];
|
|
300
|
+
if (f.short_summary !== undefined) out.short_summary = f.short_summary;
|
|
301
|
+
if (f.outcome !== undefined) out.outcome = f.outcome as FindingOut["outcome"];
|
|
302
|
+
if (note !== undefined) out.note = note;
|
|
303
|
+
return out;
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
// 单一事实源(design D2):同一对象写盘 + 作 structuredContent;类型即出参
|
|
307
|
+
// schema 的 Static 派生,无需断言。
|
|
308
|
+
const now = new Date();
|
|
309
|
+
const reportData: ReviewReportOut = {
|
|
310
|
+
level: params.level,
|
|
311
|
+
reportId: params.report_id ?? null,
|
|
312
|
+
target: params.target ?? null,
|
|
313
|
+
filesChanged: params.files_changed ?? null,
|
|
314
|
+
fannedOut: params.fanned_out ?? null,
|
|
315
|
+
generatedAt: now.toISOString(),
|
|
316
|
+
findings,
|
|
317
|
+
};
|
|
272
318
|
|
|
273
319
|
const report = renderReport({
|
|
274
320
|
level: params.level,
|
|
@@ -281,7 +327,6 @@ export const reviewReportTool = defineTool<typeof ReviewReportParams, ReviewRepo
|
|
|
281
327
|
|
|
282
328
|
let outFile: string | null = null;
|
|
283
329
|
let writeError: string | null = null;
|
|
284
|
-
const now = new Date();
|
|
285
330
|
try {
|
|
286
331
|
const dir = path.join(ctx.cwd, CONFIG_DIR_NAME, "review");
|
|
287
332
|
await fs.promises.mkdir(dir, { recursive: true });
|
|
@@ -290,19 +335,7 @@ export const reviewReportTool = defineTool<typeof ReviewReportParams, ReviewRepo
|
|
|
290
335
|
const fp = path.join(dir, `${ts}-${safeId}.json`);
|
|
291
336
|
await fs.promises.writeFile(
|
|
292
337
|
fp,
|
|
293
|
-
JSON.stringify(
|
|
294
|
-
{
|
|
295
|
-
level: params.level,
|
|
296
|
-
reportId: params.report_id ?? null,
|
|
297
|
-
target: params.target ?? null,
|
|
298
|
-
filesChanged: params.files_changed ?? null,
|
|
299
|
-
fannedOut: params.fanned_out ?? null,
|
|
300
|
-
generatedAt: now.toISOString(),
|
|
301
|
-
findings,
|
|
302
|
-
},
|
|
303
|
-
null,
|
|
304
|
-
2,
|
|
305
|
-
),
|
|
338
|
+
JSON.stringify(reportData, null, 2),
|
|
306
339
|
{ encoding: "utf-8", mode: 0o600 },
|
|
307
340
|
);
|
|
308
341
|
outFile = fp;
|
|
@@ -322,6 +355,7 @@ export const reviewReportTool = defineTool<typeof ReviewReportParams, ReviewRepo
|
|
|
322
355
|
};
|
|
323
356
|
return {
|
|
324
357
|
content: [{ type: "text" as const, text: report + tail }],
|
|
358
|
+
structuredContent: reportData,
|
|
325
359
|
details,
|
|
326
360
|
};
|
|
327
361
|
},
|