@sentry/junior-github 0.199.0 → 0.201.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/{chunk-6OPIFILN.js → chunk-F2PJG6AF.js} +31 -2
- package/dist/index.js +576 -177
- package/dist/resource-events/pull-request.d.ts +2 -0
- package/dist/testing.js +1 -1
- package/dist/tools/submit-pull-request-review.d.ts +42 -0
- package/dist/tools/update-pull-request-feedback.d.ts +59 -0
- package/dist/webhooks/handler.d.ts +7 -0
- package/package.json +2 -2
- package/skills/github-code/SKILL.md +25 -24
- package/skills/github-code/references/api-surface.md +42 -40
- package/skills/github-code/references/troubleshooting-workarounds.md +28 -29
package/dist/index.js
CHANGED
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
credentialUnavailable,
|
|
24
24
|
gitHubDeploymentSourceSubscribable,
|
|
25
25
|
gitHubIssueSubscribable,
|
|
26
|
+
gitHubPullRequestEventGuidance,
|
|
26
27
|
gitHubPullRequestSubscribable,
|
|
27
28
|
gitHubReleaseSourceSubscribable,
|
|
28
29
|
gitHubRepositorySubscribable,
|
|
@@ -42,7 +43,7 @@ import {
|
|
|
42
43
|
readGrantPermissions,
|
|
43
44
|
requireEnv,
|
|
44
45
|
resolveUserAccount
|
|
45
|
-
} from "./chunk-
|
|
46
|
+
} from "./chunk-F2PJG6AF.js";
|
|
46
47
|
|
|
47
48
|
// src/plugin.ts
|
|
48
49
|
import {
|
|
@@ -1001,7 +1002,9 @@ var createPullRequestToolInputSchema = z4.object({
|
|
|
1001
1002
|
title: z4.string().describe("Pull request title."),
|
|
1002
1003
|
head: z4.string().describe("Head branch or owner:branch ref."),
|
|
1003
1004
|
base: z4.string().describe("Base branch."),
|
|
1004
|
-
body: z4.string().describe(
|
|
1005
|
+
body: z4.string().describe(
|
|
1006
|
+
"Pull request body. The runtime appends the conversation footer."
|
|
1007
|
+
).optional(),
|
|
1005
1008
|
draft: z4.boolean().describe("Whether to open the pull request as a draft.").optional()
|
|
1006
1009
|
}).strict();
|
|
1007
1010
|
var createPullRequestStateSchema = Type2.Union([
|
|
@@ -1252,6 +1255,7 @@ function createGitHubPullRequestTool(ctx, subscriptionConfig) {
|
|
|
1252
1255
|
readOnlyHint: false
|
|
1253
1256
|
},
|
|
1254
1257
|
description: "Create a GitHub pull request with a runtime-owned conversation footer. Use this instead of shelling out to gh pr create when creating pull requests.",
|
|
1258
|
+
exposure: "direct",
|
|
1255
1259
|
inputSchema: createPullRequestToolInputSchema,
|
|
1256
1260
|
outputSchema: gitHubPullRequestOutputSchema,
|
|
1257
1261
|
async execute(input, options) {
|
|
@@ -1994,7 +1998,7 @@ function createGitHubUpdatePullRequestTool(ctx) {
|
|
|
1994
1998
|
});
|
|
1995
1999
|
}
|
|
1996
2000
|
|
|
1997
|
-
// src/tools/
|
|
2001
|
+
// src/tools/update-pull-request-feedback.ts
|
|
1998
2002
|
import {
|
|
1999
2003
|
definePluginTool as definePluginTool10,
|
|
2000
2004
|
PluginToolInputError as PluginToolInputError11,
|
|
@@ -2019,21 +2023,33 @@ function botUserIdFromEmail(value) {
|
|
|
2019
2023
|
return parseGitHubBotIdentity(value)?.userId;
|
|
2020
2024
|
}
|
|
2021
2025
|
|
|
2022
|
-
// src/tools/
|
|
2026
|
+
// src/tools/update-pull-request-feedback.ts
|
|
2027
|
+
var STATUS_TO_REACTION_CONTENT = {
|
|
2028
|
+
reviewing: "eyes",
|
|
2029
|
+
addressed: "+1",
|
|
2030
|
+
declined: "-1"
|
|
2031
|
+
};
|
|
2032
|
+
var FEEDBACK_REACTION_CONTENTS = new Set(
|
|
2033
|
+
Object.values(STATUS_TO_REACTION_CONTENT)
|
|
2034
|
+
);
|
|
2023
2035
|
var inputSchema8 = z10.object({
|
|
2024
|
-
repo: z10.string().describe(
|
|
2025
|
-
|
|
2036
|
+
repo: z10.string().describe('Repository in "owner/name" format.'),
|
|
2037
|
+
commentKind: z10.enum(["conversation", "review"]).describe(
|
|
2038
|
+
'Comment kind: "conversation" for a pull request conversation comment, "review" for an inline review comment.'
|
|
2026
2039
|
),
|
|
2027
|
-
|
|
2028
|
-
"GitHub
|
|
2040
|
+
commentId: z10.number().int().positive().describe(
|
|
2041
|
+
"GitHub comment id (conversation comment id or inline review comment id)."
|
|
2042
|
+
),
|
|
2043
|
+
status: z10.enum(["reviewing", "addressed", "declined"]).describe(
|
|
2044
|
+
"Feedback status. Maps to a reaction: reviewing -> eyes, addressed -> +1, declined -> -1."
|
|
2029
2045
|
)
|
|
2030
2046
|
}).strict();
|
|
2031
2047
|
var outputSchema8 = pluginToolOutputSchema10.extend({
|
|
2032
|
-
target: z10.literal("
|
|
2048
|
+
target: z10.literal("updatePullRequestFeedback"),
|
|
2033
2049
|
repo: z10.string(),
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2050
|
+
commentId: z10.number(),
|
|
2051
|
+
status: z10.enum(["reviewing", "addressed", "declined"]),
|
|
2052
|
+
reactionId: z10.number()
|
|
2037
2053
|
});
|
|
2038
2054
|
function parseRepo10(value) {
|
|
2039
2055
|
const parts = value.split("/").map((part) => part.trim());
|
|
@@ -2042,6 +2058,13 @@ function parseRepo10(value) {
|
|
|
2042
2058
|
}
|
|
2043
2059
|
return { owner: parts[0], name: parts[1], ref: `${parts[0]}/${parts[1]}` };
|
|
2044
2060
|
}
|
|
2061
|
+
function pullRequestCommentReactionsPath(input) {
|
|
2062
|
+
const segment = input.commentKind === "conversation" ? "issues" : "pulls";
|
|
2063
|
+
return `/repos/${encodeURIComponent(input.owner)}/${encodeURIComponent(input.name)}/${segment}/comments/${input.commentId}/reactions`;
|
|
2064
|
+
}
|
|
2065
|
+
function pullRequestFeedbackReactionContent(status) {
|
|
2066
|
+
return STATUS_TO_REACTION_CONTENT[status];
|
|
2067
|
+
}
|
|
2045
2068
|
async function readJson7(response) {
|
|
2046
2069
|
const text2 = await response.text();
|
|
2047
2070
|
if (!text2) return void 0;
|
|
@@ -2058,7 +2081,12 @@ function githubError(payload) {
|
|
|
2058
2081
|
}
|
|
2059
2082
|
return "GitHub request failed";
|
|
2060
2083
|
}
|
|
2061
|
-
|
|
2084
|
+
var reactionSchema = z10.object({
|
|
2085
|
+
id: z10.number(),
|
|
2086
|
+
content: z10.string(),
|
|
2087
|
+
user: z10.object({ id: z10.number() }).nullable()
|
|
2088
|
+
});
|
|
2089
|
+
function createGitHubUpdatePullRequestFeedbackTool(ctx, botEmail) {
|
|
2062
2090
|
return definePluginTool10({
|
|
2063
2091
|
annotations: {
|
|
2064
2092
|
destructiveHint: true,
|
|
@@ -2066,22 +2094,175 @@ function createGitHubResolvePullRequestReviewThreadTool(ctx, botEmail) {
|
|
|
2066
2094
|
openWorldHint: true,
|
|
2067
2095
|
readOnlyHint: false
|
|
2068
2096
|
},
|
|
2069
|
-
description: "
|
|
2097
|
+
description: "Set Junior's status reaction on GitHub pull request feedback: reviewing (eyes), addressed (+1), or declined (-1). Use the commentId and commentKind from the resource event. Replaces only Junior's prior status reaction on that comment.",
|
|
2098
|
+
exposure: "direct",
|
|
2070
2099
|
inputSchema: inputSchema8,
|
|
2071
2100
|
outputSchema: outputSchema8,
|
|
2072
2101
|
async execute(input) {
|
|
2073
2102
|
const parsedInput = inputSchema8.safeParse(input);
|
|
2074
2103
|
if (!parsedInput.success) {
|
|
2075
2104
|
throw new PluginToolInputError11(
|
|
2076
|
-
"Invalid GitHub
|
|
2105
|
+
"Invalid GitHub updatePullRequestFeedback input.",
|
|
2077
2106
|
{ cause: parsedInput.error }
|
|
2078
2107
|
);
|
|
2079
2108
|
}
|
|
2109
|
+
const { commentId, commentKind, status } = parsedInput.data;
|
|
2080
2110
|
const repo = parseRepo10(parsedInput.data.repo);
|
|
2081
2111
|
const botUserId = botUserIdFromEmail(botEmail);
|
|
2082
2112
|
if (botUserId === void 0) {
|
|
2083
2113
|
throw new Error("GitHub App bot identity is not configured.");
|
|
2084
2114
|
}
|
|
2115
|
+
const targetContent = pullRequestFeedbackReactionContent(status);
|
|
2116
|
+
const baseUrl = `https://api.github.com${pullRequestCommentReactionsPath({
|
|
2117
|
+
owner: repo.owner,
|
|
2118
|
+
name: repo.name,
|
|
2119
|
+
commentKind,
|
|
2120
|
+
commentId
|
|
2121
|
+
})}`;
|
|
2122
|
+
const listResponse = await ctx.egress.fetch({
|
|
2123
|
+
provider: "github",
|
|
2124
|
+
operation: "github.pull.comment-reaction.list",
|
|
2125
|
+
request: new Request(`${baseUrl}?per_page=100`, {
|
|
2126
|
+
headers: { Accept: "application/vnd.github+json" }
|
|
2127
|
+
})
|
|
2128
|
+
});
|
|
2129
|
+
const listPayload = await readJson7(listResponse);
|
|
2130
|
+
if (!listResponse.ok) {
|
|
2131
|
+
throw new Error(
|
|
2132
|
+
`GitHub reaction lookup failed with HTTP ${listResponse.status}: ${githubError(listPayload)}`
|
|
2133
|
+
);
|
|
2134
|
+
}
|
|
2135
|
+
const botReactions = z10.array(reactionSchema).parse(listPayload).filter(
|
|
2136
|
+
(reaction) => reaction.user?.id === botUserId && FEEDBACK_REACTION_CONTENTS.has(reaction.content)
|
|
2137
|
+
);
|
|
2138
|
+
const existing = botReactions.find(
|
|
2139
|
+
(reaction) => reaction.content === targetContent
|
|
2140
|
+
);
|
|
2141
|
+
for (const stale of botReactions) {
|
|
2142
|
+
if (stale.content === targetContent) continue;
|
|
2143
|
+
const deleteResponse = await ctx.egress.fetch({
|
|
2144
|
+
provider: "github",
|
|
2145
|
+
operation: "github.pull.comment-reaction.delete",
|
|
2146
|
+
request: new Request(`${baseUrl}/${stale.id}`, {
|
|
2147
|
+
method: "DELETE"
|
|
2148
|
+
})
|
|
2149
|
+
});
|
|
2150
|
+
if (!deleteResponse.ok && deleteResponse.status !== 404) {
|
|
2151
|
+
const deletePayload = await readJson7(deleteResponse);
|
|
2152
|
+
throw new Error(
|
|
2153
|
+
`GitHub reaction removal failed with HTTP ${deleteResponse.status}: ${githubError(deletePayload)}`
|
|
2154
|
+
);
|
|
2155
|
+
}
|
|
2156
|
+
}
|
|
2157
|
+
if (existing) {
|
|
2158
|
+
return {
|
|
2159
|
+
target: "updatePullRequestFeedback",
|
|
2160
|
+
repo: repo.ref,
|
|
2161
|
+
commentId,
|
|
2162
|
+
status,
|
|
2163
|
+
reactionId: existing.id
|
|
2164
|
+
};
|
|
2165
|
+
}
|
|
2166
|
+
const createResponse = await ctx.egress.fetch({
|
|
2167
|
+
provider: "github",
|
|
2168
|
+
operation: "github.pull.comment-reaction.create",
|
|
2169
|
+
request: new Request(baseUrl, {
|
|
2170
|
+
method: "POST",
|
|
2171
|
+
headers: {
|
|
2172
|
+
Accept: "application/vnd.github+json",
|
|
2173
|
+
"Content-Type": "application/json"
|
|
2174
|
+
},
|
|
2175
|
+
body: JSON.stringify({ content: targetContent })
|
|
2176
|
+
})
|
|
2177
|
+
});
|
|
2178
|
+
const createPayload = await readJson7(createResponse);
|
|
2179
|
+
if (!createResponse.ok) {
|
|
2180
|
+
throw new Error(
|
|
2181
|
+
`GitHub reaction creation failed with HTTP ${createResponse.status}: ${githubError(createPayload)}`
|
|
2182
|
+
);
|
|
2183
|
+
}
|
|
2184
|
+
const created = reactionSchema.parse(createPayload);
|
|
2185
|
+
return {
|
|
2186
|
+
target: "updatePullRequestFeedback",
|
|
2187
|
+
repo: repo.ref,
|
|
2188
|
+
commentId,
|
|
2189
|
+
status,
|
|
2190
|
+
reactionId: created.id
|
|
2191
|
+
};
|
|
2192
|
+
}
|
|
2193
|
+
});
|
|
2194
|
+
}
|
|
2195
|
+
|
|
2196
|
+
// src/tools/resolve-pull-request-review-thread.ts
|
|
2197
|
+
import {
|
|
2198
|
+
definePluginTool as definePluginTool11,
|
|
2199
|
+
PluginToolInputError as PluginToolInputError12,
|
|
2200
|
+
pluginToolOutputSchema as pluginToolOutputSchema11
|
|
2201
|
+
} from "@sentry/junior-plugin-api";
|
|
2202
|
+
import { z as z11 } from "zod";
|
|
2203
|
+
var inputSchema9 = z11.object({
|
|
2204
|
+
repo: z11.string().describe(
|
|
2205
|
+
'Repository in "owner/name" format. Required because GraphQL has no repo path.'
|
|
2206
|
+
),
|
|
2207
|
+
threadId: z11.string().trim().min(1).describe(
|
|
2208
|
+
"GitHub pull request review thread node ID (the same `threadId` / `id` variable used by `gh api graphql` resolveReviewThread)."
|
|
2209
|
+
)
|
|
2210
|
+
}).strict();
|
|
2211
|
+
var outputSchema9 = pluginToolOutputSchema11.extend({
|
|
2212
|
+
target: z11.literal("resolvePullRequestReviewThread"),
|
|
2213
|
+
repo: z11.string(),
|
|
2214
|
+
number: z11.number(),
|
|
2215
|
+
threadId: z11.string(),
|
|
2216
|
+
resolved: z11.boolean()
|
|
2217
|
+
});
|
|
2218
|
+
function parseRepo11(value) {
|
|
2219
|
+
const parts = value.split("/").map((part) => part.trim());
|
|
2220
|
+
if (parts.length !== 2 || !parts[0] || !parts[1]) {
|
|
2221
|
+
throw new PluginToolInputError12('repo must use "owner/name" format');
|
|
2222
|
+
}
|
|
2223
|
+
return { owner: parts[0], name: parts[1], ref: `${parts[0]}/${parts[1]}` };
|
|
2224
|
+
}
|
|
2225
|
+
async function readJson8(response) {
|
|
2226
|
+
const text2 = await response.text();
|
|
2227
|
+
if (!text2) return void 0;
|
|
2228
|
+
try {
|
|
2229
|
+
return JSON.parse(text2);
|
|
2230
|
+
} catch {
|
|
2231
|
+
return text2;
|
|
2232
|
+
}
|
|
2233
|
+
}
|
|
2234
|
+
function githubError2(payload) {
|
|
2235
|
+
if (payload && typeof payload === "object" && !Array.isArray(payload)) {
|
|
2236
|
+
const message = payload.message;
|
|
2237
|
+
if (typeof message === "string" && message.trim()) return message.trim();
|
|
2238
|
+
}
|
|
2239
|
+
return "GitHub request failed";
|
|
2240
|
+
}
|
|
2241
|
+
function createGitHubResolvePullRequestReviewThreadTool(ctx, botEmail) {
|
|
2242
|
+
return definePluginTool11({
|
|
2243
|
+
annotations: {
|
|
2244
|
+
destructiveHint: true,
|
|
2245
|
+
idempotentHint: true,
|
|
2246
|
+
openWorldHint: true,
|
|
2247
|
+
readOnlyHint: false
|
|
2248
|
+
},
|
|
2249
|
+
description: "Resolve a GitHub pull request review thread after acting on its feedback. Only works on pull requests the bot authored.",
|
|
2250
|
+
exposure: "direct",
|
|
2251
|
+
inputSchema: inputSchema9,
|
|
2252
|
+
outputSchema: outputSchema9,
|
|
2253
|
+
async execute(input) {
|
|
2254
|
+
const parsedInput = inputSchema9.safeParse(input);
|
|
2255
|
+
if (!parsedInput.success) {
|
|
2256
|
+
throw new PluginToolInputError12(
|
|
2257
|
+
"Invalid GitHub resolvePullRequestReviewThread input.",
|
|
2258
|
+
{ cause: parsedInput.error }
|
|
2259
|
+
);
|
|
2260
|
+
}
|
|
2261
|
+
const repo = parseRepo11(parsedInput.data.repo);
|
|
2262
|
+
const botUserId = botUserIdFromEmail(botEmail);
|
|
2263
|
+
if (botUserId === void 0) {
|
|
2264
|
+
throw new Error("GitHub App bot identity is not configured.");
|
|
2265
|
+
}
|
|
2085
2266
|
const query = `query ReviewThreadOwnership($threadId: ID!) {
|
|
2086
2267
|
node(id: $threadId) {
|
|
2087
2268
|
... on PullRequestReviewThread {
|
|
@@ -2108,32 +2289,32 @@ function createGitHubResolvePullRequestReviewThreadTool(ctx, botEmail) {
|
|
|
2108
2289
|
})
|
|
2109
2290
|
})
|
|
2110
2291
|
});
|
|
2111
|
-
const lookupPayload = await
|
|
2292
|
+
const lookupPayload = await readJson8(lookupResponse);
|
|
2112
2293
|
if (!lookupResponse.ok) {
|
|
2113
2294
|
throw new Error(
|
|
2114
|
-
`GitHub review thread lookup failed with HTTP ${lookupResponse.status}: ${
|
|
2295
|
+
`GitHub review thread lookup failed with HTTP ${lookupResponse.status}: ${githubError2(lookupPayload)}`
|
|
2115
2296
|
);
|
|
2116
2297
|
}
|
|
2117
|
-
const thread =
|
|
2118
|
-
data:
|
|
2119
|
-
node:
|
|
2120
|
-
id:
|
|
2121
|
-
isResolved:
|
|
2122
|
-
pullRequest:
|
|
2123
|
-
author:
|
|
2124
|
-
number:
|
|
2125
|
-
repository:
|
|
2298
|
+
const thread = z11.object({
|
|
2299
|
+
data: z11.object({
|
|
2300
|
+
node: z11.object({
|
|
2301
|
+
id: z11.string(),
|
|
2302
|
+
isResolved: z11.boolean(),
|
|
2303
|
+
pullRequest: z11.object({
|
|
2304
|
+
author: z11.object({ databaseId: z11.number().optional() }).nullable(),
|
|
2305
|
+
number: z11.number(),
|
|
2306
|
+
repository: z11.object({ nameWithOwner: z11.string() })
|
|
2126
2307
|
})
|
|
2127
2308
|
}).nullable()
|
|
2128
2309
|
})
|
|
2129
2310
|
}).parse(lookupPayload).data.node;
|
|
2130
2311
|
if (!thread) {
|
|
2131
|
-
throw new
|
|
2312
|
+
throw new PluginToolInputError12("GitHub review thread was not found.");
|
|
2132
2313
|
}
|
|
2133
2314
|
const pullRequest = thread.pullRequest;
|
|
2134
2315
|
const ownsPullRequest = pullRequest.repository.nameWithOwner.toLowerCase() === repo.ref.toLowerCase() && pullRequest.author?.databaseId === botUserId;
|
|
2135
2316
|
if (!ownsPullRequest) {
|
|
2136
|
-
throw new
|
|
2317
|
+
throw new PluginToolInputError12(
|
|
2137
2318
|
"This bot can only resolve review threads on pull requests it authored."
|
|
2138
2319
|
);
|
|
2139
2320
|
}
|
|
@@ -2164,16 +2345,16 @@ function createGitHubResolvePullRequestReviewThreadTool(ctx, botEmail) {
|
|
|
2164
2345
|
})
|
|
2165
2346
|
})
|
|
2166
2347
|
});
|
|
2167
|
-
const resolvePayload = await
|
|
2348
|
+
const resolvePayload = await readJson8(resolveResponse);
|
|
2168
2349
|
if (!resolveResponse.ok) {
|
|
2169
2350
|
throw new Error(
|
|
2170
|
-
`GitHub review thread resolution failed with HTTP ${resolveResponse.status}: ${
|
|
2351
|
+
`GitHub review thread resolution failed with HTTP ${resolveResponse.status}: ${githubError2(resolvePayload)}`
|
|
2171
2352
|
);
|
|
2172
2353
|
}
|
|
2173
|
-
const resolved =
|
|
2174
|
-
data:
|
|
2175
|
-
resolveReviewThread:
|
|
2176
|
-
thread:
|
|
2354
|
+
const resolved = z11.object({
|
|
2355
|
+
data: z11.object({
|
|
2356
|
+
resolveReviewThread: z11.object({
|
|
2357
|
+
thread: z11.object({ id: z11.string(), isResolved: z11.boolean() })
|
|
2177
2358
|
})
|
|
2178
2359
|
})
|
|
2179
2360
|
}).parse(resolvePayload).data.resolveReviewThread.thread;
|
|
@@ -2191,6 +2372,129 @@ function createGitHubResolvePullRequestReviewThreadTool(ctx, botEmail) {
|
|
|
2191
2372
|
});
|
|
2192
2373
|
}
|
|
2193
2374
|
|
|
2375
|
+
// src/tools/submit-pull-request-review.ts
|
|
2376
|
+
import {
|
|
2377
|
+
definePluginTool as definePluginTool12,
|
|
2378
|
+
PluginToolInputError as PluginToolInputError13,
|
|
2379
|
+
pluginToolOutputSchema as pluginToolOutputSchema12
|
|
2380
|
+
} from "@sentry/junior-plugin-api";
|
|
2381
|
+
import { z as z12 } from "zod";
|
|
2382
|
+
var reviewCommentSchema = z12.object({
|
|
2383
|
+
path: z12.string().trim().min(1).describe("File path in the pull request."),
|
|
2384
|
+
body: z12.string().trim().min(1).describe("Inline review comment."),
|
|
2385
|
+
line: z12.number().int().positive().describe("Line in the pull request diff."),
|
|
2386
|
+
side: z12.enum(["LEFT", "RIGHT"]).describe("Side of the pull request diff."),
|
|
2387
|
+
startLine: z12.number().int().positive().optional().describe("First line of a multi-line comment."),
|
|
2388
|
+
startSide: z12.enum(["LEFT", "RIGHT"]).optional().describe("First side of a multi-line comment.")
|
|
2389
|
+
}).strict().refine(
|
|
2390
|
+
({ startLine, startSide }) => startLine === void 0 === (startSide === void 0),
|
|
2391
|
+
{ message: "startLine and startSide must be provided together." }
|
|
2392
|
+
);
|
|
2393
|
+
var inputSchema10 = z12.object({
|
|
2394
|
+
repo: z12.string().describe('Repository in "owner/name" format.'),
|
|
2395
|
+
number: z12.number().int().positive().describe("Pull request number."),
|
|
2396
|
+
event: z12.enum(["COMMENT", "REQUEST_CHANGES"]).describe("Review result. Junior cannot approve pull requests."),
|
|
2397
|
+
body: z12.string().trim().min(1).describe("Review summary."),
|
|
2398
|
+
comments: z12.array(reviewCommentSchema).optional().describe("Optional inline review comments.")
|
|
2399
|
+
}).strict();
|
|
2400
|
+
var outputSchema10 = pluginToolOutputSchema12.extend({
|
|
2401
|
+
target: z12.literal("submitPullRequestReview"),
|
|
2402
|
+
repo: z12.string(),
|
|
2403
|
+
number: z12.number(),
|
|
2404
|
+
reviewId: z12.number(),
|
|
2405
|
+
state: z12.string(),
|
|
2406
|
+
url: z12.string()
|
|
2407
|
+
});
|
|
2408
|
+
function parseRepo12(value) {
|
|
2409
|
+
const parts = value.split("/").map((part) => part.trim());
|
|
2410
|
+
if (parts.length !== 2 || !parts[0] || !parts[1]) {
|
|
2411
|
+
throw new PluginToolInputError13('repo must use "owner/name" format');
|
|
2412
|
+
}
|
|
2413
|
+
return { owner: parts[0], name: parts[1], ref: `${parts[0]}/${parts[1]}` };
|
|
2414
|
+
}
|
|
2415
|
+
async function readJson9(response) {
|
|
2416
|
+
const text2 = await response.text();
|
|
2417
|
+
if (!text2) return void 0;
|
|
2418
|
+
try {
|
|
2419
|
+
return JSON.parse(text2);
|
|
2420
|
+
} catch {
|
|
2421
|
+
return text2;
|
|
2422
|
+
}
|
|
2423
|
+
}
|
|
2424
|
+
function githubError3(body) {
|
|
2425
|
+
if (body && typeof body === "object" && !Array.isArray(body)) {
|
|
2426
|
+
const message = body.message;
|
|
2427
|
+
if (typeof message === "string" && message.trim()) return message.trim();
|
|
2428
|
+
}
|
|
2429
|
+
if (typeof body === "string" && body.trim()) return body.trim();
|
|
2430
|
+
return "GitHub request failed";
|
|
2431
|
+
}
|
|
2432
|
+
function throwReviewError(status, body) {
|
|
2433
|
+
const message = `GitHub pull request review failed with HTTP ${status}: ${githubError3(body)}`;
|
|
2434
|
+
if (status === 404 || status === 422) {
|
|
2435
|
+
throw new PluginToolInputError13(message);
|
|
2436
|
+
}
|
|
2437
|
+
throw new Error(message);
|
|
2438
|
+
}
|
|
2439
|
+
function createGitHubSubmitPullRequestReviewTool(egress) {
|
|
2440
|
+
return definePluginTool12({
|
|
2441
|
+
annotations: {
|
|
2442
|
+
destructiveHint: true,
|
|
2443
|
+
idempotentHint: false,
|
|
2444
|
+
openWorldHint: true,
|
|
2445
|
+
readOnlyHint: false
|
|
2446
|
+
},
|
|
2447
|
+
description: "Submit a GitHub pull request review as a comment or change request. Use this instead of `gh pr review`, GraphQL, or direct REST calls. Junior cannot approve pull requests.",
|
|
2448
|
+
exposure: "direct",
|
|
2449
|
+
inputSchema: inputSchema10,
|
|
2450
|
+
outputSchema: outputSchema10,
|
|
2451
|
+
async execute(review) {
|
|
2452
|
+
const repo = parseRepo12(review.repo);
|
|
2453
|
+
const comments = review.comments?.map((comment) => ({
|
|
2454
|
+
path: comment.path,
|
|
2455
|
+
body: comment.body,
|
|
2456
|
+
line: comment.line,
|
|
2457
|
+
side: comment.side,
|
|
2458
|
+
...comment.startLine !== void 0 ? { start_line: comment.startLine } : void 0,
|
|
2459
|
+
...comment.startSide !== void 0 ? { start_side: comment.startSide } : void 0
|
|
2460
|
+
}));
|
|
2461
|
+
const response = await egress.fetch({
|
|
2462
|
+
provider: "github",
|
|
2463
|
+
operation: "github.pull.review.create",
|
|
2464
|
+
request: new Request(
|
|
2465
|
+
`https://api.github.com/repos/${encodeURIComponent(repo.owner)}/${encodeURIComponent(repo.name)}/pulls/${review.number}/reviews`,
|
|
2466
|
+
{
|
|
2467
|
+
method: "POST",
|
|
2468
|
+
headers: {
|
|
2469
|
+
Accept: "application/vnd.github+json",
|
|
2470
|
+
"Content-Type": "application/json",
|
|
2471
|
+
"X-GitHub-Api-Version": "2022-11-28"
|
|
2472
|
+
},
|
|
2473
|
+
body: JSON.stringify({
|
|
2474
|
+
body: review.body,
|
|
2475
|
+
event: review.event,
|
|
2476
|
+
...comments ? { comments } : void 0
|
|
2477
|
+
})
|
|
2478
|
+
}
|
|
2479
|
+
)
|
|
2480
|
+
});
|
|
2481
|
+
const parsed = await readJson9(response);
|
|
2482
|
+
if (!response.ok) {
|
|
2483
|
+
throwReviewError(response.status, parsed);
|
|
2484
|
+
}
|
|
2485
|
+
const providerResult = z12.object({ id: z12.number(), html_url: z12.string(), state: z12.string() }).parse(parsed);
|
|
2486
|
+
return {
|
|
2487
|
+
target: "submitPullRequestReview",
|
|
2488
|
+
repo: repo.ref,
|
|
2489
|
+
number: review.number,
|
|
2490
|
+
reviewId: providerResult.id,
|
|
2491
|
+
state: providerResult.state,
|
|
2492
|
+
url: providerResult.html_url
|
|
2493
|
+
};
|
|
2494
|
+
}
|
|
2495
|
+
});
|
|
2496
|
+
}
|
|
2497
|
+
|
|
2194
2498
|
// src/tools.ts
|
|
2195
2499
|
function createGitHubTools(ctx, botEmail, pullRequestSubscription) {
|
|
2196
2500
|
return {
|
|
@@ -2205,8 +2509,15 @@ function createGitHubTools(ctx, botEmail, pullRequestSubscription) {
|
|
|
2205
2509
|
getRelease: createGitHubGetReleaseTool(ctx),
|
|
2206
2510
|
getRepository: createGitHubGetRepositoryTool(ctx),
|
|
2207
2511
|
resolvePullRequestReviewThread: createGitHubResolvePullRequestReviewThreadTool(ctx, botEmail),
|
|
2512
|
+
submitPullRequestReview: createGitHubSubmitPullRequestReviewTool(
|
|
2513
|
+
ctx.egress
|
|
2514
|
+
),
|
|
2208
2515
|
updateIssue: createGitHubUpdateIssueTool(ctx),
|
|
2209
|
-
updatePullRequest: createGitHubUpdatePullRequestTool(ctx)
|
|
2516
|
+
updatePullRequest: createGitHubUpdatePullRequestTool(ctx),
|
|
2517
|
+
updatePullRequestFeedback: createGitHubUpdatePullRequestFeedbackTool(
|
|
2518
|
+
ctx,
|
|
2519
|
+
botEmail
|
|
2520
|
+
)
|
|
2210
2521
|
};
|
|
2211
2522
|
}
|
|
2212
2523
|
|
|
@@ -2215,7 +2526,7 @@ import { createHmac, timingSafeEqual } from "crypto";
|
|
|
2215
2526
|
|
|
2216
2527
|
// src/issue-outcomes/store.ts
|
|
2217
2528
|
import { and, eq, lte, sql as sql2 } from "drizzle-orm";
|
|
2218
|
-
import { z as
|
|
2529
|
+
import { z as z14 } from "zod";
|
|
2219
2530
|
|
|
2220
2531
|
// src/db/schema.ts
|
|
2221
2532
|
import { sql } from "drizzle-orm";
|
|
@@ -2227,18 +2538,18 @@ import {
|
|
|
2227
2538
|
text,
|
|
2228
2539
|
timestamp
|
|
2229
2540
|
} from "drizzle-orm/pg-core";
|
|
2230
|
-
import { z as
|
|
2231
|
-
var githubPullRequestStateSchema =
|
|
2541
|
+
import { z as z13 } from "zod";
|
|
2542
|
+
var githubPullRequestStateSchema = z13.enum([
|
|
2232
2543
|
"closed_unmerged",
|
|
2233
2544
|
"merged",
|
|
2234
2545
|
"open"
|
|
2235
2546
|
]);
|
|
2236
|
-
var githubPullRequestCommitCompositionSchema =
|
|
2547
|
+
var githubPullRequestCommitCompositionSchema = z13.enum([
|
|
2237
2548
|
"junior_only",
|
|
2238
2549
|
"mixed"
|
|
2239
2550
|
]);
|
|
2240
|
-
var githubIssueStateSchema =
|
|
2241
|
-
var githubIssueStateReasonSchema =
|
|
2551
|
+
var githubIssueStateSchema = z13.enum(["closed", "open"]);
|
|
2552
|
+
var githubIssueStateReasonSchema = z13.enum([
|
|
2242
2553
|
"completed",
|
|
2243
2554
|
"duplicate",
|
|
2244
2555
|
"not_planned",
|
|
@@ -2308,21 +2619,21 @@ var juniorGitHubPullRequestIssues = pgTable(
|
|
|
2308
2619
|
);
|
|
2309
2620
|
|
|
2310
2621
|
// src/issue-outcomes/store.ts
|
|
2311
|
-
var githubIssueOutcomeInputSchema =
|
|
2312
|
-
candidateOwned:
|
|
2313
|
-
closedAt:
|
|
2314
|
-
issueId:
|
|
2315
|
-
number:
|
|
2316
|
-
openedAt:
|
|
2317
|
-
repositoryFullName:
|
|
2318
|
-
repositoryId:
|
|
2622
|
+
var githubIssueOutcomeInputSchema = z14.object({
|
|
2623
|
+
candidateOwned: z14.boolean(),
|
|
2624
|
+
closedAt: z14.date().optional(),
|
|
2625
|
+
issueId: z14.string().min(1),
|
|
2626
|
+
number: z14.number().int().positive(),
|
|
2627
|
+
openedAt: z14.date(),
|
|
2628
|
+
repositoryFullName: z14.string().min(1),
|
|
2629
|
+
repositoryId: z14.string().min(1),
|
|
2319
2630
|
state: githubIssueStateSchema,
|
|
2320
2631
|
stateReason: githubIssueStateReasonSchema.optional(),
|
|
2321
|
-
updatedAt:
|
|
2632
|
+
updatedAt: z14.date()
|
|
2322
2633
|
}).strict();
|
|
2323
|
-
var githubIssueConversationsInputSchema =
|
|
2324
|
-
conversationIds:
|
|
2325
|
-
issueId:
|
|
2634
|
+
var githubIssueConversationsInputSchema = z14.object({
|
|
2635
|
+
conversationIds: z14.array(z14.string().min(1)).min(1),
|
|
2636
|
+
issueId: z14.string().min(1)
|
|
2326
2637
|
}).strict();
|
|
2327
2638
|
function projectionValues(input) {
|
|
2328
2639
|
return {
|
|
@@ -2386,33 +2697,33 @@ async function recordGitHubIssueConversations(db, input) {
|
|
|
2386
2697
|
|
|
2387
2698
|
// src/pull-request-outcomes/store.ts
|
|
2388
2699
|
import { and as and2, eq as eq2, lte as lte2, ne, sql as sql3 } from "drizzle-orm";
|
|
2389
|
-
import { z as
|
|
2390
|
-
var githubPullRequestOutcomeInputSchema =
|
|
2391
|
-
candidateOwned:
|
|
2392
|
-
closedAt:
|
|
2700
|
+
import { z as z15 } from "zod";
|
|
2701
|
+
var githubPullRequestOutcomeInputSchema = z15.object({
|
|
2702
|
+
candidateOwned: z15.boolean(),
|
|
2703
|
+
closedAt: z15.date().optional(),
|
|
2393
2704
|
commitComposition: githubPullRequestCommitCompositionSchema.optional(),
|
|
2394
|
-
mergedAt:
|
|
2395
|
-
number:
|
|
2396
|
-
openedAt:
|
|
2397
|
-
pullRequestId:
|
|
2398
|
-
repositoryFullName:
|
|
2399
|
-
repositoryId:
|
|
2705
|
+
mergedAt: z15.date().optional(),
|
|
2706
|
+
number: z15.number().int().positive(),
|
|
2707
|
+
openedAt: z15.date(),
|
|
2708
|
+
pullRequestId: z15.string().min(1),
|
|
2709
|
+
repositoryFullName: z15.string().min(1),
|
|
2710
|
+
repositoryId: z15.string().min(1),
|
|
2400
2711
|
state: githubPullRequestStateSchema,
|
|
2401
|
-
title:
|
|
2402
|
-
updatedAt:
|
|
2712
|
+
title: z15.string().min(1).optional(),
|
|
2713
|
+
updatedAt: z15.date()
|
|
2403
2714
|
}).strict();
|
|
2404
|
-
var githubPullRequestConversationsInputSchema =
|
|
2405
|
-
conversationIds:
|
|
2406
|
-
pullRequestId:
|
|
2715
|
+
var githubPullRequestConversationsInputSchema = z15.object({
|
|
2716
|
+
conversationIds: z15.array(z15.string().min(1)).min(1),
|
|
2717
|
+
pullRequestId: z15.string().min(1)
|
|
2407
2718
|
}).strict();
|
|
2408
|
-
var githubPullRequestLinkedIssuesInputSchema =
|
|
2409
|
-
linkedIssues:
|
|
2410
|
-
|
|
2411
|
-
number:
|
|
2412
|
-
repositoryFullName:
|
|
2719
|
+
var githubPullRequestLinkedIssuesInputSchema = z15.object({
|
|
2720
|
+
linkedIssues: z15.array(
|
|
2721
|
+
z15.object({
|
|
2722
|
+
number: z15.number().int().positive(),
|
|
2723
|
+
repositoryFullName: z15.string().min(1)
|
|
2413
2724
|
}).strict()
|
|
2414
2725
|
).min(1),
|
|
2415
|
-
pullRequestId:
|
|
2726
|
+
pullRequestId: z15.string().min(1)
|
|
2416
2727
|
}).strict();
|
|
2417
2728
|
function projectionValues2(input) {
|
|
2418
2729
|
return {
|
|
@@ -2577,39 +2888,39 @@ async function recordGitHubPullRequestLinkedIssues(db, input) {
|
|
|
2577
2888
|
}
|
|
2578
2889
|
|
|
2579
2890
|
// src/webhooks/issue-outcome.ts
|
|
2580
|
-
import { z as
|
|
2581
|
-
var canonicalIssueOutcomeSchema =
|
|
2582
|
-
action:
|
|
2583
|
-
issue:
|
|
2584
|
-
body:
|
|
2585
|
-
closed_at:
|
|
2586
|
-
created_at:
|
|
2587
|
-
id:
|
|
2588
|
-
number:
|
|
2891
|
+
import { z as z16 } from "zod";
|
|
2892
|
+
var canonicalIssueOutcomeSchema = z16.object({
|
|
2893
|
+
action: z16.enum(["opened", "closed", "reopened"]),
|
|
2894
|
+
issue: z16.object({
|
|
2895
|
+
body: z16.string().nullable().optional(),
|
|
2896
|
+
closed_at: z16.string().nullable().optional(),
|
|
2897
|
+
created_at: z16.string(),
|
|
2898
|
+
id: z16.number().int().positive(),
|
|
2899
|
+
number: z16.number().int().positive(),
|
|
2589
2900
|
state_reason: githubIssueStateReasonSchema.nullable().optional(),
|
|
2590
|
-
updated_at:
|
|
2591
|
-
user:
|
|
2901
|
+
updated_at: z16.string(),
|
|
2902
|
+
user: z16.object({ login: z16.string().min(1) }).strict()
|
|
2592
2903
|
}).strict(),
|
|
2593
|
-
repository:
|
|
2594
|
-
full_name:
|
|
2595
|
-
id:
|
|
2904
|
+
repository: z16.object({
|
|
2905
|
+
full_name: z16.string().min(1),
|
|
2906
|
+
id: z16.number().int().positive()
|
|
2596
2907
|
}).strict()
|
|
2597
2908
|
}).strict();
|
|
2598
|
-
var issueOutcomeSchema =
|
|
2599
|
-
action:
|
|
2600
|
-
issue:
|
|
2601
|
-
body:
|
|
2602
|
-
closed_at:
|
|
2603
|
-
created_at:
|
|
2604
|
-
id:
|
|
2605
|
-
number:
|
|
2909
|
+
var issueOutcomeSchema = z16.object({
|
|
2910
|
+
action: z16.enum(["opened", "closed", "reopened"]),
|
|
2911
|
+
issue: z16.object({
|
|
2912
|
+
body: z16.string().nullable().optional(),
|
|
2913
|
+
closed_at: z16.string().nullable().optional(),
|
|
2914
|
+
created_at: z16.string(),
|
|
2915
|
+
id: z16.number().int().positive(),
|
|
2916
|
+
number: z16.number().int().positive(),
|
|
2606
2917
|
state_reason: githubIssueStateReasonSchema.nullable().optional(),
|
|
2607
|
-
updated_at:
|
|
2608
|
-
user:
|
|
2918
|
+
updated_at: z16.string(),
|
|
2919
|
+
user: z16.object({ login: z16.string().min(1) }).passthrough()
|
|
2609
2920
|
}).passthrough(),
|
|
2610
|
-
repository:
|
|
2611
|
-
full_name:
|
|
2612
|
-
id:
|
|
2921
|
+
repository: z16.object({
|
|
2922
|
+
full_name: z16.string().min(1),
|
|
2923
|
+
id: z16.number().int().positive()
|
|
2613
2924
|
}).passthrough()
|
|
2614
2925
|
}).passthrough().transform(
|
|
2615
2926
|
(provider) => canonicalIssueOutcomeSchema.parse({
|
|
@@ -2630,7 +2941,7 @@ var issueOutcomeSchema = z14.object({
|
|
|
2630
2941
|
}
|
|
2631
2942
|
})
|
|
2632
2943
|
);
|
|
2633
|
-
var issueLifecycleActionSchema =
|
|
2944
|
+
var issueLifecycleActionSchema = z16.object({ action: z16.string() }).passthrough();
|
|
2634
2945
|
function timestamp2(value) {
|
|
2635
2946
|
if (!value) return void 0;
|
|
2636
2947
|
const parsed = new Date(value);
|
|
@@ -2676,21 +2987,21 @@ function normalizeGitHubIssueOutcome(args) {
|
|
|
2676
2987
|
updatedAt
|
|
2677
2988
|
};
|
|
2678
2989
|
}
|
|
2679
|
-
var canonicalIssueConversationSchema =
|
|
2680
|
-
issue:
|
|
2681
|
-
body:
|
|
2682
|
-
id:
|
|
2683
|
-
user:
|
|
2990
|
+
var canonicalIssueConversationSchema = z16.object({
|
|
2991
|
+
issue: z16.object({
|
|
2992
|
+
body: z16.string().nullable().optional(),
|
|
2993
|
+
id: z16.number().int().positive(),
|
|
2994
|
+
user: z16.object({ login: z16.string().min(1) }).strict()
|
|
2684
2995
|
}).strict(),
|
|
2685
|
-
sender:
|
|
2996
|
+
sender: z16.object({ login: z16.string().min(1) }).strict().optional()
|
|
2686
2997
|
}).strict();
|
|
2687
|
-
var issueConversationSchema =
|
|
2688
|
-
issue:
|
|
2689
|
-
body:
|
|
2690
|
-
id:
|
|
2691
|
-
user:
|
|
2998
|
+
var issueConversationSchema = z16.object({
|
|
2999
|
+
issue: z16.object({
|
|
3000
|
+
body: z16.string().nullable().optional(),
|
|
3001
|
+
id: z16.number().int().positive(),
|
|
3002
|
+
user: z16.object({ login: z16.string().min(1) }).passthrough()
|
|
2692
3003
|
}).passthrough(),
|
|
2693
|
-
sender:
|
|
3004
|
+
sender: z16.object({ login: z16.string().min(1) }).passthrough().optional()
|
|
2694
3005
|
}).passthrough().transform(
|
|
2695
3006
|
(provider) => canonicalIssueConversationSchema.parse({
|
|
2696
3007
|
issue: {
|
|
@@ -2717,43 +3028,43 @@ function normalizeGitHubIssueConversations(args) {
|
|
|
2717
3028
|
}
|
|
2718
3029
|
|
|
2719
3030
|
// src/webhooks/pull-request-outcome.ts
|
|
2720
|
-
import { z as
|
|
2721
|
-
var canonicalPullRequestOutcomeSchema =
|
|
2722
|
-
action:
|
|
2723
|
-
pull_request:
|
|
2724
|
-
body:
|
|
2725
|
-
closed_at:
|
|
2726
|
-
created_at:
|
|
2727
|
-
id:
|
|
2728
|
-
merged:
|
|
2729
|
-
merged_at:
|
|
2730
|
-
number:
|
|
2731
|
-
title:
|
|
2732
|
-
updated_at:
|
|
2733
|
-
user:
|
|
3031
|
+
import { z as z17 } from "zod";
|
|
3032
|
+
var canonicalPullRequestOutcomeSchema = z17.object({
|
|
3033
|
+
action: z17.enum(["opened", "closed", "reopened"]),
|
|
3034
|
+
pull_request: z17.object({
|
|
3035
|
+
body: z17.string().nullable().optional(),
|
|
3036
|
+
closed_at: z17.string().nullable().optional(),
|
|
3037
|
+
created_at: z17.string(),
|
|
3038
|
+
id: z17.number().int().positive(),
|
|
3039
|
+
merged: z17.boolean(),
|
|
3040
|
+
merged_at: z17.string().nullable().optional(),
|
|
3041
|
+
number: z17.number().int().positive(),
|
|
3042
|
+
title: z17.string().min(1).optional(),
|
|
3043
|
+
updated_at: z17.string(),
|
|
3044
|
+
user: z17.object({ login: z17.string().min(1) }).strict()
|
|
2734
3045
|
}).strict(),
|
|
2735
|
-
repository:
|
|
2736
|
-
full_name:
|
|
2737
|
-
id:
|
|
3046
|
+
repository: z17.object({
|
|
3047
|
+
full_name: z17.string().min(1),
|
|
3048
|
+
id: z17.number().int().positive()
|
|
2738
3049
|
}).strict()
|
|
2739
3050
|
}).strict();
|
|
2740
|
-
var pullRequestOutcomeSchema =
|
|
2741
|
-
action:
|
|
2742
|
-
pull_request:
|
|
2743
|
-
body:
|
|
2744
|
-
closed_at:
|
|
2745
|
-
created_at:
|
|
2746
|
-
id:
|
|
2747
|
-
merged:
|
|
2748
|
-
merged_at:
|
|
2749
|
-
number:
|
|
2750
|
-
title:
|
|
2751
|
-
updated_at:
|
|
2752
|
-
user:
|
|
3051
|
+
var pullRequestOutcomeSchema = z17.object({
|
|
3052
|
+
action: z17.enum(["opened", "closed", "reopened"]),
|
|
3053
|
+
pull_request: z17.object({
|
|
3054
|
+
body: z17.string().nullable().optional(),
|
|
3055
|
+
closed_at: z17.string().nullable().optional(),
|
|
3056
|
+
created_at: z17.string(),
|
|
3057
|
+
id: z17.number().int().positive(),
|
|
3058
|
+
merged: z17.boolean(),
|
|
3059
|
+
merged_at: z17.string().nullable().optional(),
|
|
3060
|
+
number: z17.number().int().positive(),
|
|
3061
|
+
title: z17.string().min(1).optional(),
|
|
3062
|
+
updated_at: z17.string(),
|
|
3063
|
+
user: z17.object({ login: z17.string().min(1) }).passthrough()
|
|
2753
3064
|
}).passthrough(),
|
|
2754
|
-
repository:
|
|
2755
|
-
full_name:
|
|
2756
|
-
id:
|
|
3065
|
+
repository: z17.object({
|
|
3066
|
+
full_name: z17.string().min(1),
|
|
3067
|
+
id: z17.number().int().positive()
|
|
2757
3068
|
}).passthrough()
|
|
2758
3069
|
}).passthrough().transform(
|
|
2759
3070
|
(provider) => canonicalPullRequestOutcomeSchema.parse({
|
|
@@ -2776,24 +3087,24 @@ var pullRequestOutcomeSchema = z15.object({
|
|
|
2776
3087
|
}
|
|
2777
3088
|
})
|
|
2778
3089
|
);
|
|
2779
|
-
var pullRequestLifecycleActionSchema =
|
|
2780
|
-
var canonicalPullRequestConversationSchema =
|
|
2781
|
-
pull_request:
|
|
2782
|
-
body:
|
|
2783
|
-
id:
|
|
2784
|
-
user:
|
|
3090
|
+
var pullRequestLifecycleActionSchema = z17.object({ action: z17.string() }).passthrough();
|
|
3091
|
+
var canonicalPullRequestConversationSchema = z17.object({
|
|
3092
|
+
pull_request: z17.object({
|
|
3093
|
+
body: z17.string().nullable().optional(),
|
|
3094
|
+
id: z17.number().int().positive(),
|
|
3095
|
+
user: z17.object({ login: z17.string().min(1) }).strict()
|
|
2785
3096
|
}).strict(),
|
|
2786
|
-
repository:
|
|
2787
|
-
sender:
|
|
3097
|
+
repository: z17.object({ full_name: z17.string().min(1) }).strict(),
|
|
3098
|
+
sender: z17.object({ login: z17.string().min(1) }).strict()
|
|
2788
3099
|
}).strict();
|
|
2789
|
-
var pullRequestConversationSchema =
|
|
2790
|
-
pull_request:
|
|
2791
|
-
body:
|
|
2792
|
-
id:
|
|
2793
|
-
user:
|
|
3100
|
+
var pullRequestConversationSchema = z17.object({
|
|
3101
|
+
pull_request: z17.object({
|
|
3102
|
+
body: z17.string().nullable().optional(),
|
|
3103
|
+
id: z17.number().int().positive(),
|
|
3104
|
+
user: z17.object({ login: z17.string().min(1) }).passthrough()
|
|
2794
3105
|
}).passthrough(),
|
|
2795
|
-
repository:
|
|
2796
|
-
sender:
|
|
3106
|
+
repository: z17.object({ full_name: z17.string().min(1) }).passthrough(),
|
|
3107
|
+
sender: z17.object({ login: z17.string().min(1) }).passthrough()
|
|
2797
3108
|
}).passthrough().transform(
|
|
2798
3109
|
(provider) => canonicalPullRequestConversationSchema.parse({
|
|
2799
3110
|
pull_request: {
|
|
@@ -2913,6 +3224,20 @@ function webhookInstallationId(body) {
|
|
|
2913
3224
|
}
|
|
2914
3225
|
return parseInstallationId(installation.id);
|
|
2915
3226
|
}
|
|
3227
|
+
function pullRequestFeedbackTarget(events) {
|
|
3228
|
+
const event = events.find(
|
|
3229
|
+
(candidate) => candidate.eventType === "pull_request.comment.created" || candidate.eventType === "pull_request.review_comment.created"
|
|
3230
|
+
);
|
|
3231
|
+
const data = event?.data;
|
|
3232
|
+
if (!data || typeof data.repo !== "string" || typeof data.commentId !== "number" || data.commentKind !== "conversation" && data.commentKind !== "review") {
|
|
3233
|
+
return void 0;
|
|
3234
|
+
}
|
|
3235
|
+
return {
|
|
3236
|
+
commentId: data.commentId,
|
|
3237
|
+
commentKind: data.commentKind,
|
|
3238
|
+
repo: data.repo
|
|
3239
|
+
};
|
|
3240
|
+
}
|
|
2916
3241
|
function githubCodeChange(outcome, conversationIds) {
|
|
2917
3242
|
return {
|
|
2918
3243
|
closedAt: outcome.closedAt,
|
|
@@ -3063,6 +3388,21 @@ function createGitHubWebhookRoute(args) {
|
|
|
3063
3388
|
deliveryId,
|
|
3064
3389
|
eventName
|
|
3065
3390
|
});
|
|
3391
|
+
const feedbackTarget = pullRequestFeedbackTarget(resourceEvents);
|
|
3392
|
+
const hasMatch = args.resourceEvents.hasMatch;
|
|
3393
|
+
const feedbackHasMatch = feedbackTarget && hasMatch ? (await Promise.all(resourceEvents.map((event) => hasMatch(event)))).some(Boolean) : false;
|
|
3394
|
+
if (feedbackTarget && feedbackHasMatch) {
|
|
3395
|
+
try {
|
|
3396
|
+
await args.markFeedbackReviewing(feedbackTarget);
|
|
3397
|
+
} catch (error) {
|
|
3398
|
+
args.log?.error("GitHub pull request feedback reaction failed", {
|
|
3399
|
+
commentId: feedbackTarget.commentId,
|
|
3400
|
+
deliveryId,
|
|
3401
|
+
errorType: error instanceof Error ? error.name : "UnknownError",
|
|
3402
|
+
repository: feedbackTarget.repo
|
|
3403
|
+
});
|
|
3404
|
+
}
|
|
3405
|
+
}
|
|
3066
3406
|
for (const event of resourceEvents) {
|
|
3067
3407
|
await args.resourceEvents.publish(event);
|
|
3068
3408
|
}
|
|
@@ -3075,18 +3415,18 @@ function createGitHubWebhookRoute(args) {
|
|
|
3075
3415
|
}
|
|
3076
3416
|
|
|
3077
3417
|
// src/pull-request-outcomes/commit-composition.ts
|
|
3078
|
-
import { z as
|
|
3079
|
-
var canonicalCommitSchema =
|
|
3080
|
-
authorEmail:
|
|
3081
|
-
authorLogin:
|
|
3418
|
+
import { z as z18 } from "zod";
|
|
3419
|
+
var canonicalCommitSchema = z18.object({
|
|
3420
|
+
authorEmail: z18.string().nullable(),
|
|
3421
|
+
authorLogin: z18.string().nullable()
|
|
3082
3422
|
}).strict();
|
|
3083
|
-
var providerCommitSchema =
|
|
3084
|
-
author:
|
|
3085
|
-
commit:
|
|
3086
|
-
author:
|
|
3423
|
+
var providerCommitSchema = z18.object({
|
|
3424
|
+
author: z18.object({ login: z18.string() }).passthrough().nullable(),
|
|
3425
|
+
commit: z18.object({
|
|
3426
|
+
author: z18.object({ email: z18.string() }).passthrough().nullable()
|
|
3087
3427
|
}).passthrough()
|
|
3088
3428
|
}).passthrough();
|
|
3089
|
-
var commitPageSchema =
|
|
3429
|
+
var commitPageSchema = z18.array(providerCommitSchema).transform(
|
|
3090
3430
|
(commits) => commits.map(
|
|
3091
3431
|
(commit) => canonicalCommitSchema.parse({
|
|
3092
3432
|
authorEmail: commit.commit.author?.email ?? null,
|
|
@@ -3950,6 +4290,7 @@ function shouldInspectGitHubGraphqlResponse(ctx) {
|
|
|
3950
4290
|
const contentType = ctx.response.headers.get("content-type");
|
|
3951
4291
|
return contentType ? /\bjson\b/i.test(contentType) : false;
|
|
3952
4292
|
}
|
|
4293
|
+
var PULL_REQUEST_COMMENT_REACTIONS_PATH = /^\/repos\/[^/]+\/[^/]+\/(issues|pulls)\/comments\/[^/]+\/reactions(?:\/[^/]+)?$/;
|
|
3953
4294
|
var GITHUB_BODY_WRITES = [
|
|
3954
4295
|
{
|
|
3955
4296
|
denialMessage: `GitHub issue creation must use the github_createIssue tool so Junior can own idempotency and the conversation footer. ${CREATE_TOOL_ROUTING_GUIDANCE}`,
|
|
@@ -3965,6 +4306,13 @@ var GITHUB_BODY_WRITES = [
|
|
|
3965
4306
|
operation: "github.pull.create",
|
|
3966
4307
|
restPath: /^\/repos\/[^/]+\/[^/]+\/pulls$/
|
|
3967
4308
|
},
|
|
4309
|
+
{
|
|
4310
|
+
denialMessage: `GitHub pull request reviews must use the github_submitPullRequestReview tool. The tool posts to /repos/{owner}/{repo}/pulls/{number}/reviews. ${CREATE_TOOL_ROUTING_GUIDANCE}`,
|
|
4311
|
+
graphqlField: "addPullRequestReview",
|
|
4312
|
+
method: "POST",
|
|
4313
|
+
operation: "github.pull.review.create",
|
|
4314
|
+
restPath: /^\/repos\/[^/]+\/[^/]+\/pulls\/[^/]+\/reviews$/
|
|
4315
|
+
},
|
|
3968
4316
|
{
|
|
3969
4317
|
denialMessage: `GitHub issue updates must use the github_updateIssue tool so Junior can own requester attribution and the conversation footer. ${CREATE_TOOL_ROUTING_GUIDANCE}`,
|
|
3970
4318
|
graphqlField: "updateIssue",
|
|
@@ -4027,6 +4375,9 @@ function githubApiWriteGrantName(method, upstreamUrl) {
|
|
|
4027
4375
|
) && !HTTP_READ_METHODS.has(method)) {
|
|
4028
4376
|
return "installation-write";
|
|
4029
4377
|
}
|
|
4378
|
+
if ((method === "POST" || method === "DELETE") && PULL_REQUEST_COMMENT_REACTIONS_PATH.test(pathname)) {
|
|
4379
|
+
return "installation-write";
|
|
4380
|
+
}
|
|
4030
4381
|
return void 0;
|
|
4031
4382
|
}
|
|
4032
4383
|
function reviewThreadResolveRepository(operation, method, upstreamUrl, bodyText) {
|
|
@@ -4054,6 +4405,16 @@ function isGitHubGraphqlMutation(method, upstreamUrl, bodyText, field) {
|
|
|
4054
4405
|
).test(parsed.normalized);
|
|
4055
4406
|
}
|
|
4056
4407
|
function applyGitHubEgressPolicy(input) {
|
|
4408
|
+
const reactionWrite = isGitHubApiUrl(input.upstreamUrl) && (input.method === "POST" || input.method === "DELETE") && PULL_REQUEST_COMMENT_REACTIONS_PATH.test(
|
|
4409
|
+
input.upstreamUrl.pathname.toLowerCase()
|
|
4410
|
+
);
|
|
4411
|
+
if (reactionWrite) {
|
|
4412
|
+
const expectedOperation = input.method === "POST" ? "github.pull.comment-reaction.create" : "github.pull.comment-reaction.delete";
|
|
4413
|
+
enforceEgressPolicy({
|
|
4414
|
+
allowed: input.operation === expectedOperation,
|
|
4415
|
+
denialMessage: "GitHub pull request feedback reactions must use the github_updatePullRequestFeedback tool."
|
|
4416
|
+
});
|
|
4417
|
+
}
|
|
4057
4418
|
assertGitHubPullRequestApprovalDenied({
|
|
4058
4419
|
...input.bodyText !== void 0 ? { bodyText: input.bodyText } : void 0,
|
|
4059
4420
|
method: input.method,
|
|
@@ -4087,7 +4448,7 @@ function requireRepositoryTarget(upstreamUrl) {
|
|
|
4087
4448
|
return;
|
|
4088
4449
|
}
|
|
4089
4450
|
throw new EgressPolicyDenied2(
|
|
4090
|
-
"GitHub write request
|
|
4451
|
+
"GitHub write request has no repository in its URL. Use a /repos/{owner}/{repo}/... URL or a GitHub tool for the operation."
|
|
4091
4452
|
);
|
|
4092
4453
|
}
|
|
4093
4454
|
async function githubGrantForEgress(ctx) {
|
|
@@ -4154,7 +4515,7 @@ async function githubGrantForEgress(ctx) {
|
|
|
4154
4515
|
if (graphqlAccess) {
|
|
4155
4516
|
if (graphqlAccess === "write") {
|
|
4156
4517
|
throw new EgressPolicyDenied2(
|
|
4157
|
-
"GitHub GraphQL mutations are not enabled for runtime credentials."
|
|
4518
|
+
"GitHub GraphQL mutations are not enabled for runtime credentials. Use the matching GitHub tool or a supported REST endpoint. For pull request reviews, use github_submitPullRequestReview. It posts to /repos/{owner}/{repo}/pulls/{number}/reviews."
|
|
4158
4519
|
);
|
|
4159
4520
|
}
|
|
4160
4521
|
return grantForAccess(
|
|
@@ -4166,7 +4527,7 @@ async function githubGrantForEgress(ctx) {
|
|
|
4166
4527
|
const access = HTTP_READ_METHODS.has(method) ? "read" : "write";
|
|
4167
4528
|
if (access === "write") {
|
|
4168
4529
|
throw new EgressPolicyDenied2(
|
|
4169
|
-
"GitHub write request
|
|
4530
|
+
"Junior does not support this GitHub write request. Use the matching GitHub tool or a supported REST endpoint."
|
|
4170
4531
|
);
|
|
4171
4532
|
}
|
|
4172
4533
|
return grantForAccess(access, "github.api-read", "installation-read");
|
|
@@ -4205,7 +4566,9 @@ function githubPlugin(options = {}) {
|
|
|
4205
4566
|
supportedEvents: [...GITHUB_PULL_REQUEST_EVENTS],
|
|
4206
4567
|
suggestedEvents: [...GITHUB_PULL_REQUEST_SUGGESTED_EVENTS],
|
|
4207
4568
|
matchFields: GITHUB_PULL_REQUEST_MATCH_FIELDS,
|
|
4208
|
-
|
|
4569
|
+
guidance: gitHubPullRequestEventGuidance(
|
|
4570
|
+
options.pullRequestEvents?.guidance
|
|
4571
|
+
)
|
|
4209
4572
|
},
|
|
4210
4573
|
{
|
|
4211
4574
|
type: "release_source",
|
|
@@ -4214,14 +4577,20 @@ function githubPlugin(options = {}) {
|
|
|
4214
4577
|
},
|
|
4215
4578
|
{
|
|
4216
4579
|
type: "repository",
|
|
4217
|
-
supportedEvents: [
|
|
4580
|
+
supportedEvents: [
|
|
4581
|
+
...GITHUB_ISSUE_EVENTS,
|
|
4582
|
+
...GITHUB_PULL_REQUEST_EVENTS
|
|
4583
|
+
],
|
|
4218
4584
|
suggestedEvents: [
|
|
4219
4585
|
"issue.opened",
|
|
4220
4586
|
"pull_request.opened",
|
|
4221
4587
|
...GITHUB_ISSUE_SUGGESTED_EVENTS,
|
|
4222
4588
|
...GITHUB_PULL_REQUEST_SUGGESTED_EVENTS
|
|
4223
4589
|
],
|
|
4224
|
-
matchFields: GITHUB_PULL_REQUEST_MATCH_FIELDS
|
|
4590
|
+
matchFields: GITHUB_PULL_REQUEST_MATCH_FIELDS,
|
|
4591
|
+
guidance: gitHubPullRequestEventGuidance(
|
|
4592
|
+
options.pullRequestEvents?.guidance
|
|
4593
|
+
)
|
|
4225
4594
|
}
|
|
4226
4595
|
],
|
|
4227
4596
|
isEnabled: () => Boolean(readEnv("GITHUB_WEBHOOK_SECRET")),
|
|
@@ -4343,6 +4712,36 @@ function githubPlugin(options = {}) {
|
|
|
4343
4712
|
installationId: () => readEnv(installationIdEnv),
|
|
4344
4713
|
installationIdEnv,
|
|
4345
4714
|
log: ctx.log,
|
|
4715
|
+
markFeedbackReviewing: async ({ commentId, commentKind, repo }) => {
|
|
4716
|
+
const [owner, name, ...extra] = repo.split("/");
|
|
4717
|
+
if (!owner || !name || extra.length > 0) {
|
|
4718
|
+
throw new Error(
|
|
4719
|
+
"GitHub pull request feedback received an invalid repository name"
|
|
4720
|
+
);
|
|
4721
|
+
}
|
|
4722
|
+
const token = await issueInstallationToken({
|
|
4723
|
+
appIdEnv,
|
|
4724
|
+
privateKeyEnv,
|
|
4725
|
+
installationIdEnv,
|
|
4726
|
+
repositories: [name]
|
|
4727
|
+
});
|
|
4728
|
+
await githubRequest(
|
|
4729
|
+
"https://api.github.com",
|
|
4730
|
+
pullRequestCommentReactionsPath({
|
|
4731
|
+
owner,
|
|
4732
|
+
name,
|
|
4733
|
+
commentKind,
|
|
4734
|
+
commentId
|
|
4735
|
+
}),
|
|
4736
|
+
{
|
|
4737
|
+
body: {
|
|
4738
|
+
content: pullRequestFeedbackReactionContent("reviewing")
|
|
4739
|
+
},
|
|
4740
|
+
method: "POST",
|
|
4741
|
+
token: token.token
|
|
4742
|
+
}
|
|
4743
|
+
);
|
|
4744
|
+
},
|
|
4346
4745
|
privateKeyEnv,
|
|
4347
4746
|
resourceEvents: ctx.resourceEvents,
|
|
4348
4747
|
webhookSecret: () => readEnv("GITHUB_WEBHOOK_SECRET")
|