@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 CHANGED
@@ -1,59 +1,60 @@
1
1
  {
2
- "name": "@fyeeme/pi-review",
3
- "version": "2.1.0",
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.1"
43
- },
44
- "peerDependencies": {
45
- "@earendil-works/pi-ai": ">=0.84.4",
46
- "@earendil-works/pi-coding-agent": ">=0.84.4",
47
- "@earendil-works/pi-tui": ">=0.84.4",
48
- "typebox": ">=1.0.0"
49
- },
50
- "devDependencies": {
51
- "@earendil-works/pi-ai": "0.85.1",
52
- "@earendil-works/pi-coding-agent": "0.85.1",
53
- "@earendil-works/pi-tui": "0.85.1",
54
- "@types/node": "22.19.19",
55
- "jiti": "2.7.0",
56
- "typebox": "1.1.38",
57
- "typescript": "5.9.3"
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: FindingInput[];
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
- const findings: FindingInput[] = cleaned.map((f, i) => ({ ...f, note: notes.get(i) }));
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
  },