@usepipr/runtime 0.5.0 → 0.6.0
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/dist/{commands-CF59DZXx.d.mts → commands-BHXh7P28.d.mts} +2 -2
- package/dist/{commands-CF59DZXx.d.mts.map → commands-BHXh7P28.d.mts.map} +1 -1
- package/dist/{commands-DgZU7tf7.mjs → commands-GWHklzvq.mjs} +1111 -105
- package/dist/commands-GWHklzvq.mjs.map +1 -0
- package/dist/index.d.mts +3 -3
- package/dist/index.mjs +2 -2
- package/dist/internal/action-result.d.mts +1 -1
- package/dist/internal/docs.d.mts +1 -1
- package/dist/internal/docs.d.mts.map +1 -1
- package/dist/internal/docs.mjs +1 -1
- package/dist/internal/pipr-result.d.mts +1 -1
- package/dist/internal/testing.d.mts +3 -2
- package/dist/internal/testing.d.mts.map +1 -1
- package/dist/internal/testing.mjs +1 -1
- package/dist/{official-github-workflow-DNzV5Z70.mjs → official-github-workflow-B3yeaCgq.mjs} +240 -83
- package/dist/official-github-workflow-B3yeaCgq.mjs.map +1 -0
- package/dist/pi/runtime-tools-extension.d.mts.map +1 -1
- package/dist/pi/runtime-tools-extension.mjs +128 -2
- package/dist/pi/runtime-tools-extension.mjs.map +1 -1
- package/dist/{recipes-w72EpAOi.d.mts → recipes-C1kxJCgQ.d.mts} +2 -1
- package/dist/recipes-C1kxJCgQ.d.mts.map +1 -0
- package/dist/runtime-tools-core-BF8rSeNS.mjs +476 -0
- package/dist/runtime-tools-core-BF8rSeNS.mjs.map +1 -0
- package/dist/{types-Dn1U3MDG.d.mts → types-w22wbdo9.d.mts} +43 -2
- package/dist/types-w22wbdo9.d.mts.map +1 -0
- package/package.json +2 -2
- package/dist/commands-DgZU7tf7.mjs.map +0 -1
- package/dist/official-github-workflow-DNzV5Z70.mjs.map +0 -1
- package/dist/recipes-w72EpAOi.d.mts.map +0 -1
- package/dist/runtime-tools-core-D8_WsbL_.mjs +0 -179
- package/dist/runtime-tools-core-D8_WsbL_.mjs.map +0 -1
- package/dist/types-Dn1U3MDG.d.mts.map +0 -1
package/dist/{official-github-workflow-DNzV5Z70.mjs → official-github-workflow-B3yeaCgq.mjs}
RENAMED
|
@@ -16,7 +16,7 @@ export default definePipr((pipr) => {
|
|
|
16
16
|
provider: "deepseek",
|
|
17
17
|
model: "deepseek-v4-pro",
|
|
18
18
|
apiKey: pipr.secret({ name: "DEEPSEEK_API_KEY" }),
|
|
19
|
-
|
|
19
|
+
thinking: "high",
|
|
20
20
|
});
|
|
21
21
|
|
|
22
22
|
const fallback = pipr.model({
|
|
@@ -24,29 +24,29 @@ export default definePipr((pipr) => {
|
|
|
24
24
|
provider: "deepseek",
|
|
25
25
|
model: "deepseek-v4-pro",
|
|
26
26
|
apiKey: pipr.secret({ name: "DEEPSEEK_API_KEY" }),
|
|
27
|
-
|
|
27
|
+
thinking: "medium",
|
|
28
28
|
});
|
|
29
29
|
|
|
30
30
|
pipr.config({ publication: { maxInlineComments: 8 } });
|
|
31
31
|
|
|
32
|
-
const reviewer = pipr.reviewer({
|
|
33
|
-
name: "bug-hunter",
|
|
34
|
-
model: primary,
|
|
35
|
-
fallbacks: [fallback],
|
|
36
|
-
instructions: \`
|
|
37
|
-
Review only defects with a reproducible failure path or a violated
|
|
38
|
-
repository contract: broken logic, edge cases, concurrency risks, data
|
|
39
|
-
loss, performance regressions, and behavior changes missing meaningful
|
|
40
|
-
tests. For API, async, state, and concurrency changes, inspect relevant
|
|
41
|
-
callers and tests before reporting. Suppress generic maintainability,
|
|
42
|
-
style-only, and broad refactor feedback.
|
|
43
|
-
\`,
|
|
44
|
-
timeout: "7m",
|
|
45
|
-
});
|
|
46
|
-
|
|
47
32
|
pipr.review({
|
|
48
33
|
id: "bug-hunter",
|
|
49
|
-
|
|
34
|
+
model: primary,
|
|
35
|
+
fallbacks: [fallback],
|
|
36
|
+
instructions: {
|
|
37
|
+
findings: \`
|
|
38
|
+
Review only defects with a reproducible failure path or a violated
|
|
39
|
+
repository contract: broken logic, edge cases, concurrency risks, data
|
|
40
|
+
loss, performance regressions, and behavior changes missing meaningful
|
|
41
|
+
tests. For API, async, state, and concurrency changes, inspect relevant
|
|
42
|
+
callers and tests before reporting. Suppress generic maintainability,
|
|
43
|
+
style-only, and broad refactor feedback.
|
|
44
|
+
\`,
|
|
45
|
+
summary: \`
|
|
46
|
+
Summarize the changed behavior and concrete defect risk. Use merged
|
|
47
|
+
findings as evidence and omit generic praise or speculative concerns.
|
|
48
|
+
\`,
|
|
49
|
+
},
|
|
50
50
|
paths: {
|
|
51
51
|
exclude: ["docs/**", "**/*.md"],
|
|
52
52
|
},
|
|
@@ -77,7 +77,7 @@ export default definePipr((pipr) => {
|
|
|
77
77
|
provider: "deepseek",
|
|
78
78
|
model: "deepseek-v4-pro",
|
|
79
79
|
apiKey: pipr.secret({ name: "DEEPSEEK_API_KEY" }),
|
|
80
|
-
|
|
80
|
+
thinking: "medium",
|
|
81
81
|
});
|
|
82
82
|
|
|
83
83
|
const changelogOutput = pipr.schema({
|
|
@@ -140,7 +140,7 @@ export default definePipr((pipr) => {
|
|
|
140
140
|
provider: "deepseek",
|
|
141
141
|
model: "deepseek-v4-pro",
|
|
142
142
|
apiKey: pipr.secret({ name: "DEEPSEEK_API_KEY" }),
|
|
143
|
-
|
|
143
|
+
thinking: "high",
|
|
144
144
|
});
|
|
145
145
|
|
|
146
146
|
const ciTriageOutput = pipr.schema({
|
|
@@ -241,7 +241,7 @@ export default definePipr((pipr) => {
|
|
|
241
241
|
provider: "deepseek",
|
|
242
242
|
model: "deepseek-v4-pro",
|
|
243
243
|
apiKey: pipr.secret({ name: "DEEPSEEK_API_KEY" }),
|
|
244
|
-
|
|
244
|
+
thinking: "high",
|
|
245
245
|
});
|
|
246
246
|
|
|
247
247
|
pipr.config({ publication: { maxInlineComments: 5 } });
|
|
@@ -249,12 +249,18 @@ export default definePipr((pipr) => {
|
|
|
249
249
|
pipr.review({
|
|
250
250
|
id: "review",
|
|
251
251
|
model,
|
|
252
|
-
instructions:
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
252
|
+
instructions: {
|
|
253
|
+
findings: \`
|
|
254
|
+
Review changed behavior for correctness, security, maintainability, and
|
|
255
|
+
meaningful regression gaps. Focus on concrete impact and compatibility
|
|
256
|
+
with repository contracts. Return only actionable findings that target
|
|
257
|
+
valid diff ranges.
|
|
258
|
+
\`,
|
|
259
|
+
summary: \`
|
|
260
|
+
Summarize the changed behavior, overall risk, and useful reviewer focus.
|
|
261
|
+
Use merged findings as evidence without introducing new defects.
|
|
262
|
+
\`,
|
|
263
|
+
},
|
|
258
264
|
timeout: "10m",
|
|
259
265
|
comment: (result, context) => {
|
|
260
266
|
const inlineFindingSummary =
|
|
@@ -304,7 +310,7 @@ export default definePipr((pipr) => {
|
|
|
304
310
|
provider: "deepseek",
|
|
305
311
|
model: "deepseek-v4-pro",
|
|
306
312
|
apiKey: pipr.secret({ name: "DEEPSEEK_API_KEY" }),
|
|
307
|
-
|
|
313
|
+
thinking: "high",
|
|
308
314
|
});
|
|
309
315
|
|
|
310
316
|
const dependencyOutput = pipr.schema({
|
|
@@ -404,7 +410,7 @@ export default definePipr((pipr) => {
|
|
|
404
410
|
provider: "deepseek",
|
|
405
411
|
model: "deepseek-v4-pro",
|
|
406
412
|
apiKey: pipr.secret({ name: "DEEPSEEK_API_KEY" }),
|
|
407
|
-
|
|
413
|
+
thinking: "high",
|
|
408
414
|
});
|
|
409
415
|
|
|
410
416
|
const diagnosticOutput = pipr.schema({
|
|
@@ -480,7 +486,7 @@ export default definePipr((pipr) => {
|
|
|
480
486
|
provider: "deepseek",
|
|
481
487
|
model: "deepseek-v4-pro",
|
|
482
488
|
apiKey: pipr.secret({ name: "DEEPSEEK_API_KEY" }),
|
|
483
|
-
|
|
489
|
+
thinking: "high",
|
|
484
490
|
});
|
|
485
491
|
|
|
486
492
|
pipr.config({ publication: { maxInlineComments: 6 } });
|
|
@@ -1063,7 +1069,7 @@ export default definePipr((pipr) => {
|
|
|
1063
1069
|
provider: "deepseek",
|
|
1064
1070
|
model: "deepseek-v4-pro",
|
|
1065
1071
|
apiKey: pipr.secret({ name: "DEEPSEEK_API_KEY" }),
|
|
1066
|
-
|
|
1072
|
+
thinking: "high",
|
|
1067
1073
|
});
|
|
1068
1074
|
|
|
1069
1075
|
const askAgent = pipr.agent({
|
|
@@ -1109,6 +1115,7 @@ export default definePipr((pipr) => {
|
|
|
1109
1115
|
//#region src/config/recipes/multi-agent-review.ts
|
|
1110
1116
|
const multiAgentReviewRecipe = {
|
|
1111
1117
|
id: "multi-agent-review",
|
|
1118
|
+
requiresChecksPermission: true,
|
|
1112
1119
|
title: "Multi-agent Review",
|
|
1113
1120
|
description: "Security, test, and maintainability agents with an aggregator agent.",
|
|
1114
1121
|
sourceTools: [
|
|
@@ -1124,7 +1131,7 @@ export default definePipr((pipr) => {
|
|
|
1124
1131
|
provider: "deepseek",
|
|
1125
1132
|
model: "deepseek-v4-pro",
|
|
1126
1133
|
apiKey: pipr.secret({ name: "DEEPSEEK_API_KEY" }),
|
|
1127
|
-
|
|
1134
|
+
thinking: "high",
|
|
1128
1135
|
});
|
|
1129
1136
|
|
|
1130
1137
|
const fast = pipr.model({
|
|
@@ -1132,7 +1139,7 @@ export default definePipr((pipr) => {
|
|
|
1132
1139
|
provider: "deepseek",
|
|
1133
1140
|
model: "deepseek-v4-pro",
|
|
1134
1141
|
apiKey: pipr.secret({ name: "DEEPSEEK_API_KEY" }),
|
|
1135
|
-
|
|
1142
|
+
thinking: "medium",
|
|
1136
1143
|
});
|
|
1137
1144
|
|
|
1138
1145
|
const specialistPrompt = (input: { manifest: unknown; focus: string }) => pipr.prompt\`
|
|
@@ -1232,7 +1239,7 @@ export default definePipr((pipr) => {
|
|
|
1232
1239
|
provider: "deepseek",
|
|
1233
1240
|
model: "deepseek-v4-pro",
|
|
1234
1241
|
apiKey: pipr.secret({ name: "DEEPSEEK_API_KEY" }),
|
|
1235
|
-
|
|
1242
|
+
thinking: "high",
|
|
1236
1243
|
});
|
|
1237
1244
|
|
|
1238
1245
|
const memory = pipr.use(
|
|
@@ -1656,7 +1663,7 @@ export default definePipr((pipr) => {
|
|
|
1656
1663
|
provider: "deepseek",
|
|
1657
1664
|
model: "deepseek-v4-pro",
|
|
1658
1665
|
apiKey: pipr.secret({ name: "DEEPSEEK_API_KEY" }),
|
|
1659
|
-
|
|
1666
|
+
thinking: "medium",
|
|
1660
1667
|
});
|
|
1661
1668
|
|
|
1662
1669
|
pipr.config({ publication: { maxInlineComments: 0 } });
|
|
@@ -1843,6 +1850,7 @@ function markdownFenceFor(value: string): string {
|
|
|
1843
1850
|
//#region src/config/recipes/pr-hygiene.ts
|
|
1844
1851
|
const prHygieneRecipe = {
|
|
1845
1852
|
id: "pr-hygiene",
|
|
1853
|
+
requiresChecksPermission: true,
|
|
1846
1854
|
title: "PR Hygiene",
|
|
1847
1855
|
description: "Change request hygiene checks for tests, docs, lockfiles, and size.",
|
|
1848
1856
|
sourceTools: ["Danger JS"],
|
|
@@ -1854,7 +1862,7 @@ export default definePipr((pipr) => {
|
|
|
1854
1862
|
provider: "deepseek",
|
|
1855
1863
|
model: "deepseek-v4-pro",
|
|
1856
1864
|
apiKey: pipr.secret({ name: "DEEPSEEK_API_KEY" }),
|
|
1857
|
-
|
|
1865
|
+
thinking: "medium",
|
|
1858
1866
|
});
|
|
1859
1867
|
|
|
1860
1868
|
pipr.config({ publication: { maxInlineComments: 5 } });
|
|
@@ -2002,6 +2010,7 @@ function policyTable(checks: PolicyCheck[]): string {
|
|
|
2002
2010
|
//#region src/config/recipes/quality-gate.ts
|
|
2003
2011
|
const qualityGateRecipe = {
|
|
2004
2012
|
id: "quality-gate",
|
|
2013
|
+
requiresChecksPermission: true,
|
|
2005
2014
|
title: "Quality Gate",
|
|
2006
2015
|
description: "Required review check that fails on blocking correctness and test risks.",
|
|
2007
2016
|
sourceTools: ["SonarQube", "Snyk"],
|
|
@@ -2013,7 +2022,7 @@ export default definePipr((pipr) => {
|
|
|
2013
2022
|
provider: "deepseek",
|
|
2014
2023
|
model: "deepseek-v4-pro",
|
|
2015
2024
|
apiKey: pipr.secret({ name: "DEEPSEEK_API_KEY" }),
|
|
2016
|
-
|
|
2025
|
+
thinking: "high",
|
|
2017
2026
|
});
|
|
2018
2027
|
|
|
2019
2028
|
pipr.config({
|
|
@@ -2294,7 +2303,11 @@ const structuredReviewRecipe = {
|
|
|
2294
2303
|
"Greptile"
|
|
2295
2304
|
],
|
|
2296
2305
|
configTs: `import { definePipr, z } from "@usepipr/sdk";
|
|
2297
|
-
import type {
|
|
2306
|
+
import type {
|
|
2307
|
+
DefaultReviewSummaryManifest,
|
|
2308
|
+
DiffManifest,
|
|
2309
|
+
ReviewFinding,
|
|
2310
|
+
} from "@usepipr/sdk";
|
|
2298
2311
|
|
|
2299
2312
|
type ReviewSummary = {
|
|
2300
2313
|
headline: string;
|
|
@@ -2305,7 +2318,7 @@ type ReviewSummary = {
|
|
|
2305
2318
|
};
|
|
2306
2319
|
|
|
2307
2320
|
const categorizedFindingSchema = z.strictObject({
|
|
2308
|
-
title: z.string().
|
|
2321
|
+
title: z.string().min(1).max(160),
|
|
2309
2322
|
severity: z.enum(["critical", "high", "medium", "low"]),
|
|
2310
2323
|
category: z.enum([
|
|
2311
2324
|
"correctness",
|
|
@@ -2316,24 +2329,121 @@ const categorizedFindingSchema = z.strictObject({
|
|
|
2316
2329
|
"maintainability",
|
|
2317
2330
|
"documentation",
|
|
2318
2331
|
]),
|
|
2319
|
-
rationale: z.string(),
|
|
2320
|
-
body: z.string(),
|
|
2321
|
-
path: z.string(),
|
|
2322
|
-
rangeId: z.string(),
|
|
2332
|
+
rationale: z.string().min(1).max(1200),
|
|
2333
|
+
body: z.string().min(1).max(700),
|
|
2334
|
+
path: z.string().min(1),
|
|
2335
|
+
rangeId: z.string().min(1),
|
|
2323
2336
|
side: z.enum(["RIGHT", "LEFT"]),
|
|
2324
2337
|
startLine: z.number().int().positive(),
|
|
2325
2338
|
endLine: z.number().int().positive(),
|
|
2326
|
-
suggestedFix: z.string().optional(),
|
|
2339
|
+
suggestedFix: z.string().min(1).optional(),
|
|
2327
2340
|
});
|
|
2341
|
+
type CategorizedFinding = ReviewFinding & {
|
|
2342
|
+
title: string;
|
|
2343
|
+
severity: "critical" | "high" | "medium" | "low";
|
|
2344
|
+
category:
|
|
2345
|
+
| "correctness"
|
|
2346
|
+
| "security"
|
|
2347
|
+
| "reliability"
|
|
2348
|
+
| "performance"
|
|
2349
|
+
| "test-coverage"
|
|
2350
|
+
| "maintainability"
|
|
2351
|
+
| "documentation";
|
|
2352
|
+
rationale: string;
|
|
2353
|
+
};
|
|
2328
2354
|
|
|
2329
2355
|
const reviewSummarySchema = z.strictObject({
|
|
2330
|
-
headline: z.string(),
|
|
2331
|
-
changeSummary: z.array(z.string()).min(1).max(4),
|
|
2356
|
+
headline: z.string().min(1).max(160),
|
|
2357
|
+
changeSummary: z.array(z.string().min(1).max(500)).min(1).max(4),
|
|
2332
2358
|
riskLevel: z.enum(["low", "medium", "high"]),
|
|
2333
|
-
riskSummary: z.string(),
|
|
2334
|
-
reviewerFocus: z.array(z.string()).max(4),
|
|
2359
|
+
riskSummary: z.string().min(1).max(500),
|
|
2360
|
+
reviewerFocus: z.array(z.string().min(1).max(500)).max(4),
|
|
2335
2361
|
});
|
|
2336
2362
|
|
|
2363
|
+
const severityRank = { critical: 0, high: 1, medium: 2, low: 3 } as const;
|
|
2364
|
+
|
|
2365
|
+
function hasCommentableAnchor(
|
|
2366
|
+
finding: ReviewFinding,
|
|
2367
|
+
manifest: DiffManifest,
|
|
2368
|
+
): boolean {
|
|
2369
|
+
const range = manifest.files
|
|
2370
|
+
.find((file) => file.path === finding.path)
|
|
2371
|
+
?.commentableRanges.find(
|
|
2372
|
+
(candidate) =>
|
|
2373
|
+
candidate.id === finding.rangeId &&
|
|
2374
|
+
candidate.path === finding.path &&
|
|
2375
|
+
candidate.side === finding.side,
|
|
2376
|
+
);
|
|
2377
|
+
return Boolean(
|
|
2378
|
+
range &&
|
|
2379
|
+
finding.startLine <= finding.endLine &&
|
|
2380
|
+
finding.startLine >= range.startLine &&
|
|
2381
|
+
finding.endLine <= range.endLine,
|
|
2382
|
+
);
|
|
2383
|
+
}
|
|
2384
|
+
|
|
2385
|
+
function deduplicateFindings(findings: CategorizedFinding[]): CategorizedFinding[] {
|
|
2386
|
+
const seen = new Set<string>();
|
|
2387
|
+
return findings.filter((finding) => {
|
|
2388
|
+
const key = JSON.stringify([
|
|
2389
|
+
finding.path,
|
|
2390
|
+
finding.rangeId,
|
|
2391
|
+
finding.side,
|
|
2392
|
+
finding.startLine,
|
|
2393
|
+
finding.endLine,
|
|
2394
|
+
finding.body,
|
|
2395
|
+
]);
|
|
2396
|
+
if (seen.has(key)) {
|
|
2397
|
+
return false;
|
|
2398
|
+
}
|
|
2399
|
+
seen.add(key);
|
|
2400
|
+
return true;
|
|
2401
|
+
});
|
|
2402
|
+
}
|
|
2403
|
+
|
|
2404
|
+
function summaryManifest(manifest: DiffManifest): DefaultReviewSummaryManifest {
|
|
2405
|
+
const files: DefaultReviewSummaryManifest["files"][number][] = [];
|
|
2406
|
+
let serializedCharacters = 0;
|
|
2407
|
+
|
|
2408
|
+
for (const file of manifest.files) {
|
|
2409
|
+
const projected = {
|
|
2410
|
+
path: file.path.slice(0, 1_000),
|
|
2411
|
+
...(file.previousPath
|
|
2412
|
+
? { previousPath: file.previousPath.slice(0, 1_000) }
|
|
2413
|
+
: {}),
|
|
2414
|
+
status: file.status,
|
|
2415
|
+
...(file.language ? { language: file.language.slice(0, 100) } : {}),
|
|
2416
|
+
additions: file.additions,
|
|
2417
|
+
deletions: file.deletions,
|
|
2418
|
+
...(file.changedSymbols?.length
|
|
2419
|
+
? {
|
|
2420
|
+
changedSymbols: file.changedSymbols
|
|
2421
|
+
.slice(0, 20)
|
|
2422
|
+
.map((symbol) => symbol.slice(0, 200)),
|
|
2423
|
+
}
|
|
2424
|
+
: {}),
|
|
2425
|
+
...(file.excludedReason
|
|
2426
|
+
? { excludedReason: file.excludedReason.slice(0, 500) }
|
|
2427
|
+
: {}),
|
|
2428
|
+
};
|
|
2429
|
+
const projectedCharacters = JSON.stringify(projected).length;
|
|
2430
|
+
if (serializedCharacters + projectedCharacters > 40_000) {
|
|
2431
|
+
continue;
|
|
2432
|
+
}
|
|
2433
|
+
files.push(projected);
|
|
2434
|
+
serializedCharacters += projectedCharacters;
|
|
2435
|
+
}
|
|
2436
|
+
|
|
2437
|
+
return {
|
|
2438
|
+
baseSha: manifest.baseSha,
|
|
2439
|
+
headSha: manifest.headSha,
|
|
2440
|
+
mergeBaseSha: manifest.mergeBaseSha,
|
|
2441
|
+
fileCount: manifest.files.length,
|
|
2442
|
+
omittedFileCount: manifest.files.length - files.length,
|
|
2443
|
+
files,
|
|
2444
|
+
};
|
|
2445
|
+
}
|
|
2446
|
+
|
|
2337
2447
|
function escapeInlineCommentHtml(value: string): string {
|
|
2338
2448
|
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">");
|
|
2339
2449
|
}
|
|
@@ -2343,21 +2453,33 @@ export default definePipr((pipr) => {
|
|
|
2343
2453
|
provider: "deepseek",
|
|
2344
2454
|
model: "deepseek-v4-pro",
|
|
2345
2455
|
apiKey: pipr.secret({ name: "DEEPSEEK_API_KEY" }),
|
|
2346
|
-
|
|
2456
|
+
thinking: "high",
|
|
2347
2457
|
});
|
|
2348
2458
|
|
|
2349
|
-
pipr.config({
|
|
2459
|
+
pipr.config({
|
|
2460
|
+
publication: {
|
|
2461
|
+
maxInlineComments: 8,
|
|
2462
|
+
autoResolve: {
|
|
2463
|
+
enabled: true,
|
|
2464
|
+
synchronize: true,
|
|
2465
|
+
userReplies: {
|
|
2466
|
+
enabled: true,
|
|
2467
|
+
respondWhenStillValid: true,
|
|
2468
|
+
allowedActors: "write",
|
|
2469
|
+
},
|
|
2470
|
+
},
|
|
2471
|
+
},
|
|
2472
|
+
});
|
|
2350
2473
|
|
|
2351
|
-
const
|
|
2474
|
+
const findingsOutput = pipr.schema({
|
|
2352
2475
|
id: "review/categorized-findings",
|
|
2353
2476
|
schema: z.strictObject({
|
|
2354
|
-
|
|
2355
|
-
findings: z.array(categorizedFindingSchema),
|
|
2477
|
+
inlineFindings: z.array(categorizedFindingSchema),
|
|
2356
2478
|
}),
|
|
2357
2479
|
});
|
|
2358
2480
|
|
|
2359
|
-
const
|
|
2360
|
-
name: "reviewer",
|
|
2481
|
+
const findingsReviewer = pipr.agent({
|
|
2482
|
+
name: "findings-reviewer",
|
|
2361
2483
|
model,
|
|
2362
2484
|
instructions: \`
|
|
2363
2485
|
Review the change request diff for correctness, security, reliability,
|
|
@@ -2367,30 +2489,67 @@ export default definePipr((pipr) => {
|
|
|
2367
2489
|
concrete non-blocking defects; and low for small but actionable issues. Each
|
|
2368
2490
|
rationale must connect repository evidence to the defect and its concrete
|
|
2369
2491
|
impact. Keep each finding title to one line. Put supporting evidence and
|
|
2370
|
-
reasoning in rationale instead of appending it to the body.
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
reviewer focus only for useful human follow-up.
|
|
2492
|
+
reasoning in rationale instead of appending it to the body. Return no more
|
|
2493
|
+
than 20 findings for each diff shard.
|
|
2494
|
+
Never copy secret-looking literals into title, body, rationale, or
|
|
2495
|
+
suggestedFix. Describe only the secret kind and location.
|
|
2375
2496
|
\`,
|
|
2376
|
-
output:
|
|
2497
|
+
output: findingsOutput,
|
|
2377
2498
|
tools: pipr.tools.readOnly,
|
|
2378
2499
|
retry: { invalidOutput: 1, transientFailure: 1 },
|
|
2379
2500
|
timeout: "10m",
|
|
2380
2501
|
prompt: () => "Review this change with severity and category metadata.",
|
|
2381
2502
|
});
|
|
2382
2503
|
|
|
2504
|
+
const summaryOutput = pipr.schema({
|
|
2505
|
+
id: "review/rich-summary",
|
|
2506
|
+
schema: reviewSummarySchema,
|
|
2507
|
+
});
|
|
2508
|
+
|
|
2509
|
+
const summaryReviewer = pipr.agent({
|
|
2510
|
+
name: "summary-reviewer",
|
|
2511
|
+
model,
|
|
2512
|
+
instructions: \`
|
|
2513
|
+
Make the summary maintainer-facing and scannable: one concrete headline,
|
|
2514
|
+
one to four behavior-focused change bullets, a risk level with rationale,
|
|
2515
|
+
and reviewer focus only for useful human follow-up. Use the selected
|
|
2516
|
+
findings as evidence, but do not invent additional defects or copy
|
|
2517
|
+
secret-looking literals into any summary field.
|
|
2518
|
+
\`,
|
|
2519
|
+
output: summaryOutput,
|
|
2520
|
+
tools: pipr.tools.readOnly,
|
|
2521
|
+
retry: { invalidOutput: 1, transientFailure: 1 },
|
|
2522
|
+
timeout: "10m",
|
|
2523
|
+
prompt: ({ inlineFindings, manifestSummary }) =>
|
|
2524
|
+
pipr.prompt\`
|
|
2525
|
+
Summarize this change using the selected findings.
|
|
2526
|
+
|
|
2527
|
+
\${pipr.section("Scoped compressed manifest", pipr.json(manifestSummary, { maxCharacters: 60_000 }))}
|
|
2528
|
+
|
|
2529
|
+
\${pipr.section("Selected findings", pipr.json(inlineFindings, { maxCharacters: 60_000 }))}
|
|
2530
|
+
\`,
|
|
2531
|
+
});
|
|
2532
|
+
|
|
2383
2533
|
const task = pipr.task({
|
|
2384
2534
|
name: "review",
|
|
2385
2535
|
async run(ctx) {
|
|
2386
2536
|
const manifest = await ctx.change.diffManifest({ compressed: true });
|
|
2387
|
-
const result = await ctx.pi.run(
|
|
2388
|
-
const
|
|
2537
|
+
const result = await ctx.pi.run(findingsReviewer, { manifest });
|
|
2538
|
+
const selectedFindings = deduplicateFindings(
|
|
2539
|
+
result.inlineFindings.filter((finding) => hasCommentableAnchor(finding, manifest)),
|
|
2540
|
+
)
|
|
2541
|
+
.sort((left, right) => severityRank[left.severity] - severityRank[right.severity])
|
|
2542
|
+
.slice(0, 8);
|
|
2543
|
+
const summary = await ctx.pi.run(summaryReviewer, {
|
|
2544
|
+
manifestSummary: summaryManifest(manifest),
|
|
2545
|
+
inlineFindings: selectedFindings,
|
|
2546
|
+
});
|
|
2547
|
+
const inlineFindings: ReviewFinding[] = selectedFindings.map((finding) => {
|
|
2389
2548
|
const severity = finding.severity.charAt(0).toUpperCase() + finding.severity.slice(1);
|
|
2390
2549
|
const category = finding.category.replaceAll("-", " ");
|
|
2391
2550
|
return {
|
|
2392
2551
|
body: [
|
|
2393
|
-
\`**\${severity} \${category}:** \${escapeInlineCommentHtml(finding.title)}\`,
|
|
2552
|
+
\`**\${severity} \${category}:** \${escapeInlineCommentHtml(lineText(finding.title))}\`,
|
|
2394
2553
|
"",
|
|
2395
2554
|
escapeInlineCommentHtml(finding.body),
|
|
2396
2555
|
"",
|
|
@@ -2413,17 +2572,17 @@ export default definePipr((pipr) => {
|
|
|
2413
2572
|
main: [
|
|
2414
2573
|
"## Summary",
|
|
2415
2574
|
"",
|
|
2416
|
-
\`**\${
|
|
2575
|
+
\`**\${lineText(summary.headline)}**\`,
|
|
2417
2576
|
"",
|
|
2418
|
-
summaryTable(
|
|
2577
|
+
summaryTable(summary),
|
|
2419
2578
|
"",
|
|
2420
2579
|
"## What Changed",
|
|
2421
2580
|
"",
|
|
2422
|
-
bulletList(
|
|
2581
|
+
bulletList(summary.changeSummary, "No changed behavior summarized."),
|
|
2423
2582
|
"",
|
|
2424
2583
|
"## Reviewer Focus",
|
|
2425
2584
|
"",
|
|
2426
|
-
bulletList(
|
|
2585
|
+
bulletList(summary.reviewerFocus, "No special reviewer focus."),
|
|
2427
2586
|
"",
|
|
2428
2587
|
].join("\\n"),
|
|
2429
2588
|
inlineFindings,
|
|
@@ -2457,7 +2616,7 @@ function labelValue(value: string): string {
|
|
|
2457
2616
|
}
|
|
2458
2617
|
|
|
2459
2618
|
function lineText(value: string): string {
|
|
2460
|
-
return value.
|
|
2619
|
+
return value.replace(/\\r\\n?|\\n/g, " ").trim();
|
|
2461
2620
|
}
|
|
2462
2621
|
|
|
2463
2622
|
function tableCell(value: string): string {
|
|
@@ -2469,6 +2628,7 @@ function tableCell(value: string): string {
|
|
|
2469
2628
|
//#region src/config/recipes/security-sast.ts
|
|
2470
2629
|
const securitySastRecipe = {
|
|
2471
2630
|
id: "security-sast",
|
|
2631
|
+
requiresChecksPermission: true,
|
|
2472
2632
|
title: "Security SAST",
|
|
2473
2633
|
description: "Security review with custom severity and category output.",
|
|
2474
2634
|
sourceTools: [
|
|
@@ -2504,7 +2664,7 @@ export default definePipr((pipr) => {
|
|
|
2504
2664
|
provider: "deepseek",
|
|
2505
2665
|
model: "deepseek-v4-pro",
|
|
2506
2666
|
apiKey: pipr.secret({ name: "DEEPSEEK_API_KEY" }),
|
|
2507
|
-
|
|
2667
|
+
thinking: "high",
|
|
2508
2668
|
});
|
|
2509
2669
|
|
|
2510
2670
|
const securityOutput = pipr.jsonSchema<SecurityReview>({
|
|
@@ -2843,6 +3003,9 @@ function officialInitRecipeFiles(recipe) {
|
|
|
2843
3003
|
function officialInitRecipeWorkflowEnvSecrets(recipe) {
|
|
2844
3004
|
return resolveOfficialInitRecipe(recipe).workflowEnvSecrets ?? [];
|
|
2845
3005
|
}
|
|
3006
|
+
function officialInitRecipeRequiresChecksPermission(recipe) {
|
|
3007
|
+
return resolveOfficialInitRecipe(recipe).requiresChecksPermission ?? false;
|
|
3008
|
+
}
|
|
2846
3009
|
function resolveOfficialInitRecipe(recipe) {
|
|
2847
3010
|
const id = recipe ?? "default-review";
|
|
2848
3011
|
if (!isOfficialInitRecipeId(id)) throw new Error(`Unsupported pipr init recipe '${id}'. Supported recipes: ${supportedOfficialInitRecipes.join(", ")}.`);
|
|
@@ -2853,7 +3016,7 @@ function isOfficialInitRecipeId(recipe) {
|
|
|
2853
3016
|
}
|
|
2854
3017
|
//#endregion
|
|
2855
3018
|
//#region src/config/official-github-workflow.ts
|
|
2856
|
-
const defaultWorkflowActionRef = "somus/pipr@v0.
|
|
3019
|
+
const defaultWorkflowActionRef = "somus/pipr@v0.6.0";
|
|
2857
3020
|
/** Internal shared renderer for `pipr init` and generated recipe docs. */
|
|
2858
3021
|
function renderOfficialGithubWorkflow(options = {}) {
|
|
2859
3022
|
const relativeConfigDir = options.relativeConfigDir ?? ".pipr";
|
|
@@ -2862,25 +3025,19 @@ function renderOfficialGithubWorkflow(options = {}) {
|
|
|
2862
3025
|
"",
|
|
2863
3026
|
"on:",
|
|
2864
3027
|
" pull_request:",
|
|
3028
|
+
" types: [opened, synchronize, reopened, ready_for_review]",
|
|
2865
3029
|
" issue_comment:",
|
|
2866
3030
|
" types: [created]",
|
|
2867
3031
|
" pull_request_review_comment:",
|
|
2868
3032
|
" types: [created]",
|
|
2869
3033
|
"",
|
|
2870
3034
|
"permissions:",
|
|
2871
|
-
" contents:
|
|
3035
|
+
" contents: read",
|
|
2872
3036
|
" pull-requests: write",
|
|
2873
|
-
" issues: write"
|
|
2874
|
-
" checks: write",
|
|
2875
|
-
"",
|
|
2876
|
-
"jobs:",
|
|
2877
|
-
" review:",
|
|
2878
|
-
" runs-on: ubuntu-latest",
|
|
2879
|
-
" steps:",
|
|
2880
|
-
" - uses: actions/checkout@v6",
|
|
2881
|
-
" with:",
|
|
2882
|
-
" fetch-depth: 0"
|
|
3037
|
+
" issues: write"
|
|
2883
3038
|
];
|
|
3039
|
+
if (officialInitRecipeRequiresChecksPermission(options.recipe)) lines.push(" checks: write");
|
|
3040
|
+
lines.push("", "jobs:", " review:", " runs-on: ubuntu-latest", " steps:", " - uses: actions/checkout@v6", " with:", " fetch-depth: 0");
|
|
2884
3041
|
if (!options.minimal) lines.push(" - uses: actions/cache@v4", " with:", " path: /home/runner/work/_temp/_github_home/.bun/install/cache", ` key: pipr-bun-${githubExpression(`hashFiles('${relativeConfigDir}/bun.lock')`)}`);
|
|
2885
3042
|
lines.push(` - uses: ${defaultWorkflowActionRef}${options.includeReleasePleaseVersionMarker ? " # x-release-please-version" : ""}`, " env:", ` DEEPSEEK_API_KEY: ${githubExpression("secrets.DEEPSEEK_API_KEY")}`, ` GITHUB_TOKEN: ${githubExpression("github.token")}`);
|
|
2886
3043
|
for (const secret of officialInitRecipeWorkflowEnvSecrets(options.recipe)) lines.push(` ${secret.env}: ${githubExpression(`secrets.${secret.secret}`)}`);
|
|
@@ -2898,4 +3055,4 @@ function githubExpression(expression) {
|
|
|
2898
3055
|
//#endregion
|
|
2899
3056
|
export { officialInitRecipeWorkflowEnvSecrets as a, officialInitRecipeFiles as i, listOfficialInitRecipes as n, supportedOfficialInitRecipes as o, officialInitRecipeConfigTs as r, renderOfficialGithubWorkflow as t };
|
|
2900
3057
|
|
|
2901
|
-
//# sourceMappingURL=official-github-workflow-
|
|
3058
|
+
//# sourceMappingURL=official-github-workflow-B3yeaCgq.mjs.map
|