paperclip-github-plugin 0.3.3 → 0.3.5
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/README.md +9 -3
- package/dist/manifest.js +69 -1
- package/dist/ui/index.js +6 -2
- package/dist/ui/index.js.map +2 -2
- package/dist/worker.js +648 -35
- package/package.json +3 -3
package/dist/worker.js
CHANGED
|
@@ -15,6 +15,10 @@ var repositoryProperty = {
|
|
|
15
15
|
type: "string",
|
|
16
16
|
description: "GitHub repository as owner/repo or https://github.com/owner/repo. Omit when the current Paperclip project has exactly one mapped repository."
|
|
17
17
|
};
|
|
18
|
+
var organizationProperty = {
|
|
19
|
+
type: "string",
|
|
20
|
+
description: "GitHub organization login that owns the Projects."
|
|
21
|
+
};
|
|
18
22
|
var paperclipIssueIdProperty = {
|
|
19
23
|
type: "string",
|
|
20
24
|
description: "Paperclip issue id used to infer the linked GitHub issue and repository when available."
|
|
@@ -29,6 +33,15 @@ var pullRequestNumberProperty = {
|
|
|
29
33
|
minimum: 1,
|
|
30
34
|
description: "GitHub pull request number."
|
|
31
35
|
};
|
|
36
|
+
var projectIdProperty = {
|
|
37
|
+
type: "string",
|
|
38
|
+
description: "GitHub ProjectV2 node id. You can use the id returned by list_organization_projects."
|
|
39
|
+
};
|
|
40
|
+
var projectNumberProperty = {
|
|
41
|
+
type: "integer",
|
|
42
|
+
minimum: 1,
|
|
43
|
+
description: "GitHub organization project number. Requires organization when projectId is not provided."
|
|
44
|
+
};
|
|
32
45
|
var llmModelProperty = {
|
|
33
46
|
type: "string",
|
|
34
47
|
description: "Exact LLM name used to draft the comment. Required so the plugin can append the mandatory AI-authorship footer."
|
|
@@ -53,6 +66,16 @@ var pullRequestTargetSchema = {
|
|
|
53
66
|
}
|
|
54
67
|
]
|
|
55
68
|
};
|
|
69
|
+
var organizationProjectTargetSchema = {
|
|
70
|
+
anyOf: [
|
|
71
|
+
{
|
|
72
|
+
required: ["projectId"]
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
required: ["organization", "projectNumber"]
|
|
76
|
+
}
|
|
77
|
+
]
|
|
78
|
+
};
|
|
56
79
|
var GITHUB_AGENT_TOOLS = [
|
|
57
80
|
{
|
|
58
81
|
name: "search_repository_items",
|
|
@@ -436,6 +459,51 @@ var GITHUB_AGENT_TOOLS = [
|
|
|
436
459
|
}
|
|
437
460
|
]
|
|
438
461
|
}
|
|
462
|
+
},
|
|
463
|
+
{
|
|
464
|
+
name: "list_organization_projects",
|
|
465
|
+
displayName: "List Organization Projects",
|
|
466
|
+
description: "List GitHub organization-level Projects so an agent can choose where to associate pull requests.",
|
|
467
|
+
parametersSchema: {
|
|
468
|
+
type: "object",
|
|
469
|
+
additionalProperties: false,
|
|
470
|
+
required: ["organization"],
|
|
471
|
+
properties: {
|
|
472
|
+
organization: organizationProperty,
|
|
473
|
+
includeClosed: {
|
|
474
|
+
type: "boolean",
|
|
475
|
+
description: "Include closed Projects in the results. Defaults to false."
|
|
476
|
+
},
|
|
477
|
+
query: {
|
|
478
|
+
type: "string",
|
|
479
|
+
description: "Optional free-text filter matched against project titles and descriptions after loading the organization projects."
|
|
480
|
+
},
|
|
481
|
+
limit: {
|
|
482
|
+
type: "integer",
|
|
483
|
+
minimum: 1,
|
|
484
|
+
maximum: 100,
|
|
485
|
+
description: "Maximum number of projects to return."
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
},
|
|
490
|
+
{
|
|
491
|
+
name: "add_pull_request_to_project",
|
|
492
|
+
displayName: "Add Pull Request To Project",
|
|
493
|
+
description: "Associate a GitHub pull request with an organization-level GitHub Project.",
|
|
494
|
+
parametersSchema: {
|
|
495
|
+
type: "object",
|
|
496
|
+
additionalProperties: false,
|
|
497
|
+
allOf: [pullRequestTargetSchema, organizationProjectTargetSchema],
|
|
498
|
+
properties: {
|
|
499
|
+
repository: repositoryProperty,
|
|
500
|
+
pullRequestNumber: pullRequestNumberProperty,
|
|
501
|
+
paperclipIssueId: paperclipIssueIdProperty,
|
|
502
|
+
projectId: projectIdProperty,
|
|
503
|
+
organization: organizationProperty,
|
|
504
|
+
projectNumber: projectNumberProperty
|
|
505
|
+
}
|
|
506
|
+
}
|
|
439
507
|
}
|
|
440
508
|
];
|
|
441
509
|
function getGitHubAgentToolDeclaration(name) {
|
|
@@ -560,6 +628,8 @@ var ISSUE_LINK_ENTITY_TYPE = "paperclip-github-plugin.issue-link";
|
|
|
560
628
|
var PULL_REQUEST_LINK_ENTITY_TYPE = "paperclip-github-plugin.pull-request-link";
|
|
561
629
|
var COMMENT_ANNOTATION_ENTITY_TYPE = "paperclip-github-plugin.comment-annotation";
|
|
562
630
|
var AI_AUTHORED_COMMENT_FOOTER_PREFIX = "Created by a Paperclip AI agent using ";
|
|
631
|
+
var HIDDEN_GITHUB_IMPORT_MARKER_PREFIX = "<!-- paperclip-github-plugin-imported-from: ";
|
|
632
|
+
var HIDDEN_GITHUB_IMPORT_MARKER_SUFFIX = " -->";
|
|
563
633
|
function normalizeCompanyId(value) {
|
|
564
634
|
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
565
635
|
}
|
|
@@ -638,6 +708,7 @@ var FAILED_CHECK_RUN_CONCLUSIONS = /* @__PURE__ */ new Set([
|
|
|
638
708
|
var SUCCESSFUL_STATUS_CONTEXT_STATES = /* @__PURE__ */ new Set(["SUCCESS"]);
|
|
639
709
|
var FAILED_STATUS_CONTEXT_STATES = /* @__PURE__ */ new Set(["ERROR", "FAILURE"]);
|
|
640
710
|
var PENDING_STATUS_CONTEXT_STATES = /* @__PURE__ */ new Set(["EXPECTED", "PENDING"]);
|
|
711
|
+
var GITHUB_REPOSITORY_MAINTAINER_WARMUP_CONCURRENCY = 4;
|
|
641
712
|
var GITHUB_REPOSITORY_MAINTAINER_ROLE_NAMES = /* @__PURE__ */ new Set(["admin", "maintain"]);
|
|
642
713
|
var GITHUB_ISSUE_STATUS_SNAPSHOT_QUERY = `
|
|
643
714
|
query GitHubIssueStatusSnapshot($owner: String!, $repo: String!, $issueNumber: Int!, $after: String) {
|
|
@@ -1037,6 +1108,114 @@ var GITHUB_PULL_REQUEST_REVIEW_THREADS_DETAILED_QUERY = `
|
|
|
1037
1108
|
}
|
|
1038
1109
|
}
|
|
1039
1110
|
`;
|
|
1111
|
+
var GITHUB_ORGANIZATION_PROJECTS_QUERY = `
|
|
1112
|
+
query GitHubOrganizationProjects($organization: String!, $after: String, $first: Int!) {
|
|
1113
|
+
organization(login: $organization) {
|
|
1114
|
+
projectsV2(first: $first, after: $after, orderBy: { field: UPDATED_AT, direction: DESC }) {
|
|
1115
|
+
pageInfo {
|
|
1116
|
+
hasNextPage
|
|
1117
|
+
endCursor
|
|
1118
|
+
}
|
|
1119
|
+
nodes {
|
|
1120
|
+
id
|
|
1121
|
+
number
|
|
1122
|
+
title
|
|
1123
|
+
shortDescription
|
|
1124
|
+
url
|
|
1125
|
+
closed
|
|
1126
|
+
updatedAt
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
`;
|
|
1132
|
+
var GITHUB_ORGANIZATION_PROJECT_BY_NUMBER_QUERY = `
|
|
1133
|
+
query GitHubOrganizationProjectByNumber($organization: String!, $projectNumber: Int!) {
|
|
1134
|
+
organization(login: $organization) {
|
|
1135
|
+
projectV2(number: $projectNumber) {
|
|
1136
|
+
id
|
|
1137
|
+
number
|
|
1138
|
+
title
|
|
1139
|
+
url
|
|
1140
|
+
closed
|
|
1141
|
+
owner {
|
|
1142
|
+
__typename
|
|
1143
|
+
... on Organization {
|
|
1144
|
+
login
|
|
1145
|
+
}
|
|
1146
|
+
... on User {
|
|
1147
|
+
login
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
`;
|
|
1154
|
+
var GITHUB_PULL_REQUEST_PROJECT_ITEMS_QUERY = `
|
|
1155
|
+
query GitHubPullRequestProjectItems($owner: String!, $repo: String!, $pullRequestNumber: Int!, $after: String) {
|
|
1156
|
+
repository(owner: $owner, name: $repo) {
|
|
1157
|
+
pullRequest(number: $pullRequestNumber) {
|
|
1158
|
+
id
|
|
1159
|
+
number
|
|
1160
|
+
title
|
|
1161
|
+
url
|
|
1162
|
+
projectItems(first: 100, after: $after) {
|
|
1163
|
+
pageInfo {
|
|
1164
|
+
hasNextPage
|
|
1165
|
+
endCursor
|
|
1166
|
+
}
|
|
1167
|
+
nodes {
|
|
1168
|
+
id
|
|
1169
|
+
project {
|
|
1170
|
+
id
|
|
1171
|
+
number
|
|
1172
|
+
title
|
|
1173
|
+
url
|
|
1174
|
+
closed
|
|
1175
|
+
owner {
|
|
1176
|
+
__typename
|
|
1177
|
+
... on Organization {
|
|
1178
|
+
login
|
|
1179
|
+
}
|
|
1180
|
+
... on User {
|
|
1181
|
+
login
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
`;
|
|
1191
|
+
var GITHUB_ADD_PULL_REQUEST_TO_PROJECT_MUTATION = `
|
|
1192
|
+
mutation GitHubAddPullRequestToProject($projectId: ID!, $contentId: ID!) {
|
|
1193
|
+
addProjectV2ItemById(input: {
|
|
1194
|
+
projectId: $projectId
|
|
1195
|
+
contentId: $contentId
|
|
1196
|
+
}) {
|
|
1197
|
+
item {
|
|
1198
|
+
id
|
|
1199
|
+
project {
|
|
1200
|
+
id
|
|
1201
|
+
number
|
|
1202
|
+
title
|
|
1203
|
+
url
|
|
1204
|
+
closed
|
|
1205
|
+
owner {
|
|
1206
|
+
__typename
|
|
1207
|
+
... on Organization {
|
|
1208
|
+
login
|
|
1209
|
+
}
|
|
1210
|
+
... on User {
|
|
1211
|
+
login
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
}
|
|
1218
|
+
`;
|
|
1040
1219
|
var GITHUB_ADD_PULL_REQUEST_REVIEW_THREAD_REPLY_MUTATION = `
|
|
1041
1220
|
mutation GitHubAddPullRequestReviewThreadReply($pullRequestReviewThreadId: ID!, $body: String!) {
|
|
1042
1221
|
addPullRequestReviewThreadReply(input: {
|
|
@@ -1990,8 +2169,8 @@ function doesImportedIssueMatchTarget(issue, target) {
|
|
|
1990
2169
|
}
|
|
1991
2170
|
return target.issueId !== void 0 && issue.paperclipIssueId === target.issueId || target.githubIssueId !== void 0 && issue.githubIssueId === target.githubIssueId || target.githubIssueNumber !== void 0 && issue.githubIssueNumber === target.githubIssueNumber;
|
|
1992
2171
|
}
|
|
1993
|
-
async function resolvePaperclipIssueGitHubLink(ctx, issueId, companyId) {
|
|
1994
|
-
const linkRecords = await listGitHubIssueLinkRecords(ctx, {
|
|
2172
|
+
async function resolvePaperclipIssueGitHubLink(ctx, issueId, companyId, options = {}) {
|
|
2173
|
+
const linkRecords = options.linkRecords ?? await listGitHubIssueLinkRecords(ctx, {
|
|
1995
2174
|
paperclipIssueId: issueId
|
|
1996
2175
|
});
|
|
1997
2176
|
const entityMatch = linkRecords.find((record) => !record.data.companyId || record.data.companyId === companyId);
|
|
@@ -2004,7 +2183,8 @@ async function resolvePaperclipIssueGitHubLink(ctx, issueId, companyId) {
|
|
|
2004
2183
|
githubIssueId: entityMatch.data.githubIssueId,
|
|
2005
2184
|
githubIssueNumber: entityMatch.data.githubIssueNumber,
|
|
2006
2185
|
githubIssueUrl: entityMatch.data.githubIssueUrl,
|
|
2007
|
-
linkedPullRequestNumbers: entityMatch.data.linkedPullRequestNumbers
|
|
2186
|
+
linkedPullRequestNumbers: entityMatch.data.linkedPullRequestNumbers,
|
|
2187
|
+
entityRecord: entityMatch
|
|
2008
2188
|
};
|
|
2009
2189
|
}
|
|
2010
2190
|
const importRegistry = normalizeImportRegistry(await ctx.state.get(IMPORT_REGISTRY_SCOPE));
|
|
@@ -2017,7 +2197,7 @@ async function resolvePaperclipIssueGitHubLink(ctx, issueId, companyId) {
|
|
|
2017
2197
|
registryMatch.githubIssueNumber
|
|
2018
2198
|
);
|
|
2019
2199
|
if (githubIssueUrl2) {
|
|
2020
|
-
|
|
2200
|
+
const fallbackLink2 = {
|
|
2021
2201
|
source: "import_registry",
|
|
2022
2202
|
companyId: registryMatch.companyId,
|
|
2023
2203
|
paperclipProjectId: registryMatch.paperclipProjectId,
|
|
@@ -2027,15 +2207,16 @@ async function resolvePaperclipIssueGitHubLink(ctx, issueId, companyId) {
|
|
|
2027
2207
|
githubIssueUrl: githubIssueUrl2,
|
|
2028
2208
|
linkedPullRequestNumbers: []
|
|
2029
2209
|
};
|
|
2210
|
+
return await hydrateRecoveredPaperclipIssueGitHubLink(ctx, issueId, fallbackLink2) ?? fallbackLink2;
|
|
2030
2211
|
}
|
|
2031
2212
|
}
|
|
2032
|
-
const issue = await ctx.issues.get(issueId, companyId);
|
|
2213
|
+
const issue = options.paperclipIssue ?? await ctx.issues.get(issueId, companyId);
|
|
2033
2214
|
const githubIssueUrl = extractImportedGitHubIssueUrlFromDescription(issue?.description);
|
|
2034
2215
|
const githubIssueReference = githubIssueUrl ? parseGitHubIssueHtmlUrl(githubIssueUrl) : null;
|
|
2035
2216
|
if (!githubIssueReference) {
|
|
2036
2217
|
return null;
|
|
2037
2218
|
}
|
|
2038
|
-
|
|
2219
|
+
const fallbackLink = {
|
|
2039
2220
|
source: "description",
|
|
2040
2221
|
companyId,
|
|
2041
2222
|
paperclipProjectId: issue?.projectId ?? void 0,
|
|
@@ -2044,6 +2225,73 @@ async function resolvePaperclipIssueGitHubLink(ctx, issueId, companyId) {
|
|
|
2044
2225
|
githubIssueUrl: githubIssueReference.issueUrl,
|
|
2045
2226
|
linkedPullRequestNumbers: []
|
|
2046
2227
|
};
|
|
2228
|
+
return await hydrateRecoveredPaperclipIssueGitHubLink(ctx, issueId, fallbackLink) ?? fallbackLink;
|
|
2229
|
+
}
|
|
2230
|
+
async function hydrateRecoveredPaperclipIssueGitHubLink(ctx, issueId, fallbackLink) {
|
|
2231
|
+
const repository = parseRepositoryReference(fallbackLink.repositoryUrl);
|
|
2232
|
+
if (!repository) {
|
|
2233
|
+
return null;
|
|
2234
|
+
}
|
|
2235
|
+
let octokit;
|
|
2236
|
+
try {
|
|
2237
|
+
octokit = await createGitHubToolOctokit(ctx);
|
|
2238
|
+
} catch {
|
|
2239
|
+
return null;
|
|
2240
|
+
}
|
|
2241
|
+
try {
|
|
2242
|
+
const response = await octokit.rest.issues.get({
|
|
2243
|
+
owner: repository.owner,
|
|
2244
|
+
repo: repository.repo,
|
|
2245
|
+
issue_number: fallbackLink.githubIssueNumber,
|
|
2246
|
+
headers: {
|
|
2247
|
+
"X-GitHub-Api-Version": GITHUB_API_VERSION
|
|
2248
|
+
}
|
|
2249
|
+
});
|
|
2250
|
+
const githubIssue = normalizeGitHubIssueRecord(response.data);
|
|
2251
|
+
const linkedPullRequests = await listLinkedPullRequestsForIssue(octokit, repository, githubIssue.number);
|
|
2252
|
+
const linkedPullRequestNumbers = linkedPullRequests.map((pullRequest) => pullRequest.number);
|
|
2253
|
+
const entityRecord = buildGitHubIssueLinkRecord(
|
|
2254
|
+
{
|
|
2255
|
+
companyId: fallbackLink.companyId,
|
|
2256
|
+
paperclipProjectId: fallbackLink.paperclipProjectId,
|
|
2257
|
+
repositoryUrl: fallbackLink.repositoryUrl
|
|
2258
|
+
},
|
|
2259
|
+
issueId,
|
|
2260
|
+
githubIssue,
|
|
2261
|
+
linkedPullRequestNumbers
|
|
2262
|
+
);
|
|
2263
|
+
await upsertGitHubIssueLinkRecord(
|
|
2264
|
+
ctx,
|
|
2265
|
+
{
|
|
2266
|
+
companyId: fallbackLink.companyId,
|
|
2267
|
+
paperclipProjectId: fallbackLink.paperclipProjectId,
|
|
2268
|
+
repositoryUrl: fallbackLink.repositoryUrl
|
|
2269
|
+
},
|
|
2270
|
+
issueId,
|
|
2271
|
+
githubIssue,
|
|
2272
|
+
linkedPullRequestNumbers
|
|
2273
|
+
);
|
|
2274
|
+
return {
|
|
2275
|
+
source: "entity",
|
|
2276
|
+
companyId: fallbackLink.companyId,
|
|
2277
|
+
paperclipProjectId: fallbackLink.paperclipProjectId,
|
|
2278
|
+
repositoryUrl: fallbackLink.repositoryUrl,
|
|
2279
|
+
githubIssueId: githubIssue.id,
|
|
2280
|
+
githubIssueNumber: githubIssue.number,
|
|
2281
|
+
githubIssueUrl: normalizeGitHubIssueHtmlUrl(githubIssue.htmlUrl) ?? githubIssue.htmlUrl,
|
|
2282
|
+
linkedPullRequestNumbers,
|
|
2283
|
+
entityRecord
|
|
2284
|
+
};
|
|
2285
|
+
} catch (error) {
|
|
2286
|
+
ctx.logger.warn("Unable to hydrate recovered GitHub issue metadata for a Paperclip issue fallback link.", {
|
|
2287
|
+
issueId,
|
|
2288
|
+
companyId: fallbackLink.companyId,
|
|
2289
|
+
repositoryUrl: fallbackLink.repositoryUrl,
|
|
2290
|
+
githubIssueNumber: fallbackLink.githubIssueNumber,
|
|
2291
|
+
error: getErrorMessage(error)
|
|
2292
|
+
});
|
|
2293
|
+
return null;
|
|
2294
|
+
}
|
|
2047
2295
|
}
|
|
2048
2296
|
async function resolveManualSyncTarget(ctx, settings, input) {
|
|
2049
2297
|
if (input.issueId) {
|
|
@@ -2232,7 +2480,13 @@ async function buildIssueGitHubDetails(ctx, input) {
|
|
|
2232
2480
|
const linkRecords = await listGitHubIssueLinkRecords(ctx, {
|
|
2233
2481
|
paperclipIssueId: issueId
|
|
2234
2482
|
});
|
|
2235
|
-
const
|
|
2483
|
+
const link = await resolvePaperclipIssueGitHubLink(ctx, issueId, companyId, {
|
|
2484
|
+
linkRecords
|
|
2485
|
+
});
|
|
2486
|
+
if (!link) {
|
|
2487
|
+
return null;
|
|
2488
|
+
}
|
|
2489
|
+
const entityMatch = link.entityRecord;
|
|
2236
2490
|
if (entityMatch) {
|
|
2237
2491
|
return {
|
|
2238
2492
|
paperclipIssueId: issueId,
|
|
@@ -2248,17 +2502,13 @@ async function buildIssueGitHubDetails(ctx, input) {
|
|
|
2248
2502
|
syncedAt: entityMatch.data.syncedAt
|
|
2249
2503
|
};
|
|
2250
2504
|
}
|
|
2251
|
-
const fallbackLink = await resolvePaperclipIssueGitHubLink(ctx, issueId, companyId);
|
|
2252
|
-
if (!fallbackLink) {
|
|
2253
|
-
return null;
|
|
2254
|
-
}
|
|
2255
2505
|
return {
|
|
2256
2506
|
paperclipIssueId: issueId,
|
|
2257
|
-
source:
|
|
2258
|
-
githubIssueNumber:
|
|
2259
|
-
githubIssueUrl:
|
|
2260
|
-
repositoryUrl:
|
|
2261
|
-
linkedPullRequestNumbers:
|
|
2507
|
+
source: link.source,
|
|
2508
|
+
githubIssueNumber: link.githubIssueNumber,
|
|
2509
|
+
githubIssueUrl: link.githubIssueUrl,
|
|
2510
|
+
repositoryUrl: link.repositoryUrl,
|
|
2511
|
+
linkedPullRequestNumbers: link.linkedPullRequestNumbers
|
|
2262
2512
|
};
|
|
2263
2513
|
}
|
|
2264
2514
|
async function resolveIssueByIdentifier(ctx, input) {
|
|
@@ -3542,7 +3792,7 @@ function normalizePaperclipIssueStatus(value) {
|
|
|
3542
3792
|
return PAPERCLIP_ISSUE_STATUSES.includes(value) ? value : void 0;
|
|
3543
3793
|
}
|
|
3544
3794
|
function describeGitHubStatusTransitionReason(params) {
|
|
3545
|
-
const { snapshot, previousCommentCount, hasTrustedNewComment } = params;
|
|
3795
|
+
const { snapshot, previousCommentCount, hasTrustedNewComment, maintainerAuthoredImportedIssue } = params;
|
|
3546
3796
|
if (snapshot.state === "closed") {
|
|
3547
3797
|
switch (snapshot.stateReason) {
|
|
3548
3798
|
case "duplicate":
|
|
@@ -3558,6 +3808,9 @@ function describeGitHubStatusTransitionReason(params) {
|
|
|
3558
3808
|
return "a new GitHub comment from the issue author or a repository maintainer was added";
|
|
3559
3809
|
}
|
|
3560
3810
|
if (snapshot.linkedPullRequests.length === 0) {
|
|
3811
|
+
if (maintainerAuthoredImportedIssue) {
|
|
3812
|
+
return "the GitHub issue is open with no linked pull requests and was created by a repository maintainer";
|
|
3813
|
+
}
|
|
3561
3814
|
return "the GitHub issue is open with no linked pull requests";
|
|
3562
3815
|
}
|
|
3563
3816
|
const linkedPullRequestSubject = snapshot.linkedPullRequests.length === 1 ? "the linked pull request" : "linked pull requests";
|
|
@@ -3596,11 +3849,20 @@ function buildStatusTransitionCommentAnnotation(params) {
|
|
|
3596
3849
|
};
|
|
3597
3850
|
}
|
|
3598
3851
|
function buildPaperclipIssueStatusTransitionComment(params) {
|
|
3599
|
-
const {
|
|
3852
|
+
const {
|
|
3853
|
+
previousStatus,
|
|
3854
|
+
nextStatus,
|
|
3855
|
+
repository,
|
|
3856
|
+
snapshot,
|
|
3857
|
+
previousCommentCount,
|
|
3858
|
+
hasTrustedNewComment,
|
|
3859
|
+
maintainerAuthoredImportedIssue
|
|
3860
|
+
} = params;
|
|
3600
3861
|
const reason = describeGitHubStatusTransitionReason({
|
|
3601
3862
|
snapshot,
|
|
3602
3863
|
previousCommentCount,
|
|
3603
|
-
hasTrustedNewComment
|
|
3864
|
+
hasTrustedNewComment,
|
|
3865
|
+
maintainerAuthoredImportedIssue
|
|
3604
3866
|
});
|
|
3605
3867
|
return {
|
|
3606
3868
|
body: `GitHub Sync updated the status from \`${formatPaperclipIssueStatus(previousStatus)}\` to \`${formatPaperclipIssueStatus(nextStatus)}\` because ${reason}.`,
|
|
@@ -3620,7 +3882,8 @@ function resolvePaperclipIssueStatus(params) {
|
|
|
3620
3882
|
previousCommentCount,
|
|
3621
3883
|
hasTrustedNewComment,
|
|
3622
3884
|
wasImportedThisRun,
|
|
3623
|
-
defaultImportedStatus
|
|
3885
|
+
defaultImportedStatus,
|
|
3886
|
+
maintainerAuthoredImportedIssue
|
|
3624
3887
|
} = params;
|
|
3625
3888
|
if (snapshot.state === "closed") {
|
|
3626
3889
|
return snapshot.stateReason === "duplicate" || snapshot.stateReason === "not_planned" ? "cancelled" : "done";
|
|
@@ -3636,10 +3899,10 @@ function resolvePaperclipIssueStatus(params) {
|
|
|
3636
3899
|
return resolvePaperclipStatusFromLinkedPullRequests(snapshot.linkedPullRequests);
|
|
3637
3900
|
}
|
|
3638
3901
|
if (wasImportedThisRun) {
|
|
3639
|
-
return defaultImportedStatus;
|
|
3902
|
+
return maintainerAuthoredImportedIssue ? "todo" : defaultImportedStatus;
|
|
3640
3903
|
}
|
|
3641
3904
|
if (currentStatus === "done" || currentStatus === "cancelled") {
|
|
3642
|
-
return "
|
|
3905
|
+
return "todo";
|
|
3643
3906
|
}
|
|
3644
3907
|
return currentStatus;
|
|
3645
3908
|
}
|
|
@@ -4043,6 +4306,40 @@ async function hasTrustedNewGitHubIssueComment(params) {
|
|
|
4043
4306
|
}
|
|
4044
4307
|
return false;
|
|
4045
4308
|
}
|
|
4309
|
+
async function isMaintainerAuthoredGitHubIssue(params) {
|
|
4310
|
+
const authorLogin = normalizeGitHubUserLogin(params.githubIssue.authorLogin);
|
|
4311
|
+
if (!authorLogin) {
|
|
4312
|
+
return false;
|
|
4313
|
+
}
|
|
4314
|
+
return isGitHubUserRepositoryMaintainer(
|
|
4315
|
+
params.octokit,
|
|
4316
|
+
params.repository,
|
|
4317
|
+
authorLogin,
|
|
4318
|
+
params.maintainerCache
|
|
4319
|
+
);
|
|
4320
|
+
}
|
|
4321
|
+
async function warmGitHubRepositoryMaintainerCache(params) {
|
|
4322
|
+
const uniqueAuthorLogins = [...new Set(
|
|
4323
|
+
params.githubIssues.map((issue) => normalizeGitHubUserLogin(issue.authorLogin)).filter((authorLogin) => Boolean(authorLogin))
|
|
4324
|
+
)].filter((authorLogin) => !params.maintainerCache.has(buildGitHubRepositoryActorCacheKey(params.repository, authorLogin)));
|
|
4325
|
+
if (uniqueAuthorLogins.length === 0) {
|
|
4326
|
+
return;
|
|
4327
|
+
}
|
|
4328
|
+
await mapWithConcurrency(uniqueAuthorLogins, GITHUB_REPOSITORY_MAINTAINER_WARMUP_CONCURRENCY, async (authorLogin) => {
|
|
4329
|
+
try {
|
|
4330
|
+
await isGitHubUserRepositoryMaintainer(
|
|
4331
|
+
params.octokit,
|
|
4332
|
+
params.repository,
|
|
4333
|
+
authorLogin,
|
|
4334
|
+
params.maintainerCache
|
|
4335
|
+
);
|
|
4336
|
+
} catch (error) {
|
|
4337
|
+
if (isGitHubRateLimitError(error)) {
|
|
4338
|
+
throw error;
|
|
4339
|
+
}
|
|
4340
|
+
}
|
|
4341
|
+
});
|
|
4342
|
+
}
|
|
4046
4343
|
function parseGitHubIssueHtmlUrl(value) {
|
|
4047
4344
|
try {
|
|
4048
4345
|
const url = new URL(value.trim());
|
|
@@ -4068,6 +4365,15 @@ function parseGitHubIssueHtmlUrl(value) {
|
|
|
4068
4365
|
function normalizeGitHubIssueHtmlUrl(value) {
|
|
4069
4366
|
return parseGitHubIssueHtmlUrl(value)?.issueUrl;
|
|
4070
4367
|
}
|
|
4368
|
+
function escapeRegExp(value) {
|
|
4369
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
4370
|
+
}
|
|
4371
|
+
function getHiddenGitHubImportMarkerPattern() {
|
|
4372
|
+
return new RegExp(
|
|
4373
|
+
`${escapeRegExp(HIDDEN_GITHUB_IMPORT_MARKER_PREFIX)}(\\S+?)${escapeRegExp(HIDDEN_GITHUB_IMPORT_MARKER_SUFFIX)}`,
|
|
4374
|
+
"i"
|
|
4375
|
+
);
|
|
4376
|
+
}
|
|
4071
4377
|
function normalizeLinkedPullRequestNumbers(values) {
|
|
4072
4378
|
return [...new Set(
|
|
4073
4379
|
values.filter((pullRequestNumber) => Number.isInteger(pullRequestNumber) && pullRequestNumber > 0)
|
|
@@ -4077,6 +4383,10 @@ function extractImportedGitHubIssueUrlFromDescription(description) {
|
|
|
4077
4383
|
if (typeof description !== "string") {
|
|
4078
4384
|
return void 0;
|
|
4079
4385
|
}
|
|
4386
|
+
const hiddenMarkerMatch = description.match(getHiddenGitHubImportMarkerPattern());
|
|
4387
|
+
if (hiddenMarkerMatch) {
|
|
4388
|
+
return normalizeGitHubIssueHtmlUrl(hiddenMarkerMatch[1]);
|
|
4389
|
+
}
|
|
4080
4390
|
const markdownMetadataMatch = description.match(/^\*\s+GitHub issue:\s+\[[^\]]+\]\(([^)]+)\)/m);
|
|
4081
4391
|
if (markdownMetadataMatch) {
|
|
4082
4392
|
return normalizeGitHubIssueHtmlUrl(markdownMetadataMatch[1]);
|
|
@@ -4219,8 +4529,27 @@ ${markdownImage}
|
|
|
4219
4529
|
}
|
|
4220
4530
|
function buildPaperclipIssueDescription(issue, linkedPullRequestNumbers = []) {
|
|
4221
4531
|
const normalizedBody = normalizeGitHubIssueBodyForPaperclip(issue.body);
|
|
4532
|
+
const hiddenImportMarker = buildHiddenGitHubImportMarker(issue.htmlUrl);
|
|
4222
4533
|
void linkedPullRequestNumbers;
|
|
4223
|
-
|
|
4534
|
+
if (!hiddenImportMarker) {
|
|
4535
|
+
return normalizedBody ?? "";
|
|
4536
|
+
}
|
|
4537
|
+
if (!normalizedBody) {
|
|
4538
|
+
return hiddenImportMarker;
|
|
4539
|
+
}
|
|
4540
|
+
return `${normalizedBody}
|
|
4541
|
+
|
|
4542
|
+
${hiddenImportMarker}`;
|
|
4543
|
+
}
|
|
4544
|
+
function buildHiddenGitHubImportMarker(githubIssueUrl) {
|
|
4545
|
+
if (typeof githubIssueUrl !== "string") {
|
|
4546
|
+
return void 0;
|
|
4547
|
+
}
|
|
4548
|
+
const normalizedIssueUrl = normalizeGitHubIssueHtmlUrl(githubIssueUrl);
|
|
4549
|
+
if (!normalizedIssueUrl) {
|
|
4550
|
+
return void 0;
|
|
4551
|
+
}
|
|
4552
|
+
return `${HIDDEN_GITHUB_IMPORT_MARKER_PREFIX}${normalizedIssueUrl}${HIDDEN_GITHUB_IMPORT_MARKER_SUFFIX}`;
|
|
4224
4553
|
}
|
|
4225
4554
|
function normalizeIssueDescriptionValue(value) {
|
|
4226
4555
|
return typeof value === "string" ? value : "";
|
|
@@ -4467,19 +4796,17 @@ async function findStoredStatusTransitionCommentAnnotation(ctx, params) {
|
|
|
4467
4796
|
}
|
|
4468
4797
|
return null;
|
|
4469
4798
|
}
|
|
4470
|
-
|
|
4799
|
+
function buildGitHubIssueLinkRecord(target, issueId, githubIssue, linkedPullRequestNumbers) {
|
|
4471
4800
|
const githubIssueUrl = normalizeGitHubIssueHtmlUrl(githubIssue.htmlUrl) ?? githubIssue.htmlUrl;
|
|
4472
|
-
|
|
4473
|
-
|
|
4474
|
-
|
|
4475
|
-
scopeId: issueId,
|
|
4476
|
-
externalId: githubIssueUrl,
|
|
4801
|
+
const repositoryUrl = parseRepositoryReference(target.repositoryUrl)?.url ?? target.repositoryUrl.trim();
|
|
4802
|
+
return {
|
|
4803
|
+
paperclipIssueId: issueId,
|
|
4477
4804
|
title: `GitHub issue #${githubIssue.number}`,
|
|
4478
4805
|
status: githubIssue.state,
|
|
4479
4806
|
data: {
|
|
4480
|
-
...
|
|
4481
|
-
...
|
|
4482
|
-
repositoryUrl
|
|
4807
|
+
...target.companyId ? { companyId: target.companyId } : {},
|
|
4808
|
+
...target.paperclipProjectId ? { paperclipProjectId: target.paperclipProjectId } : {},
|
|
4809
|
+
repositoryUrl,
|
|
4483
4810
|
githubIssueId: githubIssue.id,
|
|
4484
4811
|
githubIssueNumber: githubIssue.number,
|
|
4485
4812
|
githubIssueUrl,
|
|
@@ -4490,6 +4817,18 @@ async function upsertGitHubIssueLinkRecord(ctx, mapping, issueId, githubIssue, l
|
|
|
4490
4817
|
labels: githubIssue.labels,
|
|
4491
4818
|
syncedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
4492
4819
|
}
|
|
4820
|
+
};
|
|
4821
|
+
}
|
|
4822
|
+
async function upsertGitHubIssueLinkRecord(ctx, target, issueId, githubIssue, linkedPullRequestNumbers) {
|
|
4823
|
+
const record = buildGitHubIssueLinkRecord(target, issueId, githubIssue, linkedPullRequestNumbers);
|
|
4824
|
+
await ctx.entities.upsert({
|
|
4825
|
+
entityType: ISSUE_LINK_ENTITY_TYPE,
|
|
4826
|
+
scopeKind: "issue",
|
|
4827
|
+
scopeId: issueId,
|
|
4828
|
+
externalId: record.data.githubIssueUrl,
|
|
4829
|
+
...record.title ? { title: record.title } : {},
|
|
4830
|
+
...record.status ? { status: record.status } : {},
|
|
4831
|
+
data: record.data
|
|
4493
4832
|
});
|
|
4494
4833
|
}
|
|
4495
4834
|
async function upsertGitHubPullRequestLinkRecord(ctx, params) {
|
|
@@ -5795,13 +6134,21 @@ async function synchronizePaperclipIssueStatuses(ctx, octokit, repository, mappi
|
|
|
5795
6134
|
currentCommentCount: snapshot.commentCount,
|
|
5796
6135
|
maintainerCache: repositoryMaintainerCache
|
|
5797
6136
|
});
|
|
6137
|
+
const wasImportedThisRun = createdIssueIds.has(importedIssue.githubIssueId);
|
|
6138
|
+
const maintainerAuthoredImportedIssue = wasImportedThisRun && advancedSettings.defaultStatus !== "todo" && snapshot.state === "open" && snapshot.linkedPullRequests.length === 0 ? await isMaintainerAuthoredGitHubIssue({
|
|
6139
|
+
octokit,
|
|
6140
|
+
repository,
|
|
6141
|
+
githubIssue,
|
|
6142
|
+
maintainerCache: repositoryMaintainerCache
|
|
6143
|
+
}) : false;
|
|
5798
6144
|
const nextStatus = resolvePaperclipIssueStatus({
|
|
5799
6145
|
currentStatus: paperclipIssue.status,
|
|
5800
6146
|
snapshot,
|
|
5801
6147
|
previousCommentCount,
|
|
5802
6148
|
hasTrustedNewComment,
|
|
5803
|
-
wasImportedThisRun
|
|
5804
|
-
defaultImportedStatus: advancedSettings.defaultStatus
|
|
6149
|
+
wasImportedThisRun,
|
|
6150
|
+
defaultImportedStatus: advancedSettings.defaultStatus,
|
|
6151
|
+
maintainerAuthoredImportedIssue
|
|
5805
6152
|
});
|
|
5806
6153
|
importedIssue.githubIssueNumber = githubIssue.number;
|
|
5807
6154
|
importedIssue.lastSeenCommentCount = snapshot.commentCount;
|
|
@@ -5814,7 +6161,8 @@ async function synchronizePaperclipIssueStatuses(ctx, octokit, repository, mappi
|
|
|
5814
6161
|
repository,
|
|
5815
6162
|
snapshot,
|
|
5816
6163
|
previousCommentCount,
|
|
5817
|
-
hasTrustedNewComment
|
|
6164
|
+
hasTrustedNewComment,
|
|
6165
|
+
maintainerAuthoredImportedIssue
|
|
5818
6166
|
});
|
|
5819
6167
|
updateSyncFailureContext(syncFailureContext, {
|
|
5820
6168
|
phase: "updating_paperclip_status",
|
|
@@ -6381,6 +6729,170 @@ async function resolveGitHubPullRequestToolTarget(ctx, runCtx, input) {
|
|
|
6381
6729
|
pullRequestNumber
|
|
6382
6730
|
};
|
|
6383
6731
|
}
|
|
6732
|
+
function normalizeGitHubProjectRecord(project, fallbackOwnerLogin) {
|
|
6733
|
+
const id = normalizeOptionalString2(project?.id);
|
|
6734
|
+
const title = normalizeOptionalString2(project?.title);
|
|
6735
|
+
const url = normalizeOptionalString2(project?.url);
|
|
6736
|
+
const number = typeof project?.number === "number" && Number.isInteger(project.number) && project.number > 0 ? Math.floor(project.number) : null;
|
|
6737
|
+
if (!id || !title || !url || number === null) {
|
|
6738
|
+
return null;
|
|
6739
|
+
}
|
|
6740
|
+
const shortDescription = normalizeOptionalString2(project?.shortDescription);
|
|
6741
|
+
const updatedAt = normalizeOptionalString2(project?.updatedAt);
|
|
6742
|
+
const ownerLogin = normalizeOptionalString2(project?.owner?.login) ?? fallbackOwnerLogin;
|
|
6743
|
+
return {
|
|
6744
|
+
id,
|
|
6745
|
+
number,
|
|
6746
|
+
title,
|
|
6747
|
+
url,
|
|
6748
|
+
closed: project?.closed === true,
|
|
6749
|
+
...shortDescription ? { shortDescription } : {},
|
|
6750
|
+
...updatedAt ? { updatedAt } : {},
|
|
6751
|
+
...ownerLogin ? { ownerLogin } : {}
|
|
6752
|
+
};
|
|
6753
|
+
}
|
|
6754
|
+
function buildGitHubProjectToolData(project, options) {
|
|
6755
|
+
return {
|
|
6756
|
+
id: project.id,
|
|
6757
|
+
number: project.number,
|
|
6758
|
+
title: project.title,
|
|
6759
|
+
...project.shortDescription ? { shortDescription: project.shortDescription } : {},
|
|
6760
|
+
url: project.url,
|
|
6761
|
+
closed: project.closed,
|
|
6762
|
+
...project.updatedAt ? { updatedAt: project.updatedAt } : {},
|
|
6763
|
+
...options?.includeOwnerLogin && project.ownerLogin ? { ownerLogin: project.ownerLogin } : {}
|
|
6764
|
+
};
|
|
6765
|
+
}
|
|
6766
|
+
function matchesGitHubProjectFilter(project, query) {
|
|
6767
|
+
const normalizedQuery = normalizeOptionalString2(query)?.toLowerCase();
|
|
6768
|
+
if (!normalizedQuery) {
|
|
6769
|
+
return true;
|
|
6770
|
+
}
|
|
6771
|
+
return [project.title, project.shortDescription].filter((value) => Boolean(value)).some((value) => value.toLowerCase().includes(normalizedQuery));
|
|
6772
|
+
}
|
|
6773
|
+
async function listGitHubOrganizationProjects(octokit, organization, options) {
|
|
6774
|
+
const normalizedOrganization = normalizeOptionalString2(organization);
|
|
6775
|
+
if (!normalizedOrganization) {
|
|
6776
|
+
throw new Error("organization is required.");
|
|
6777
|
+
}
|
|
6778
|
+
const includeClosed = options?.includeClosed === true;
|
|
6779
|
+
const limit = Math.min(normalizeToolPositiveInteger(options?.limit) ?? 20, 100);
|
|
6780
|
+
const projects = [];
|
|
6781
|
+
let after;
|
|
6782
|
+
do {
|
|
6783
|
+
const response = await octokit.graphql(
|
|
6784
|
+
GITHUB_ORGANIZATION_PROJECTS_QUERY,
|
|
6785
|
+
{
|
|
6786
|
+
organization: normalizedOrganization,
|
|
6787
|
+
first: Math.min(50, Math.max(1, limit)),
|
|
6788
|
+
after
|
|
6789
|
+
}
|
|
6790
|
+
);
|
|
6791
|
+
if (!response.organization) {
|
|
6792
|
+
throw new Error(`GitHub organization ${normalizedOrganization} was not found or is not visible to the configured token.`);
|
|
6793
|
+
}
|
|
6794
|
+
const connection = response.organization.projectsV2;
|
|
6795
|
+
for (const node of connection?.nodes ?? []) {
|
|
6796
|
+
const project = normalizeGitHubProjectRecord(node, normalizedOrganization);
|
|
6797
|
+
if (!project) {
|
|
6798
|
+
continue;
|
|
6799
|
+
}
|
|
6800
|
+
if (!includeClosed && project.closed) {
|
|
6801
|
+
continue;
|
|
6802
|
+
}
|
|
6803
|
+
if (!matchesGitHubProjectFilter(project, options?.query)) {
|
|
6804
|
+
continue;
|
|
6805
|
+
}
|
|
6806
|
+
projects.push(project);
|
|
6807
|
+
if (projects.length >= limit) {
|
|
6808
|
+
return projects;
|
|
6809
|
+
}
|
|
6810
|
+
}
|
|
6811
|
+
after = getPageCursor(connection?.pageInfo);
|
|
6812
|
+
} while (after);
|
|
6813
|
+
return projects;
|
|
6814
|
+
}
|
|
6815
|
+
async function resolveGitHubProjectToolTarget(octokit, input) {
|
|
6816
|
+
const explicitProjectId = normalizeOptionalString2(input.projectId);
|
|
6817
|
+
if (explicitProjectId) {
|
|
6818
|
+
return {
|
|
6819
|
+
projectId: explicitProjectId
|
|
6820
|
+
};
|
|
6821
|
+
}
|
|
6822
|
+
const organization = normalizeOptionalToolString(input.organization);
|
|
6823
|
+
const projectNumber = normalizeToolPositiveInteger(input.projectNumber);
|
|
6824
|
+
if (!organization || projectNumber === void 0) {
|
|
6825
|
+
throw new Error("Provide either projectId or both organization and projectNumber.");
|
|
6826
|
+
}
|
|
6827
|
+
const response = await octokit.graphql(
|
|
6828
|
+
GITHUB_ORGANIZATION_PROJECT_BY_NUMBER_QUERY,
|
|
6829
|
+
{
|
|
6830
|
+
organization,
|
|
6831
|
+
projectNumber
|
|
6832
|
+
}
|
|
6833
|
+
);
|
|
6834
|
+
const project = normalizeGitHubProjectRecord(response.organization?.projectV2, organization);
|
|
6835
|
+
if (!project) {
|
|
6836
|
+
throw new Error(`GitHub organization project #${projectNumber} was not found in ${organization}.`);
|
|
6837
|
+
}
|
|
6838
|
+
return {
|
|
6839
|
+
projectId: project.id,
|
|
6840
|
+
project
|
|
6841
|
+
};
|
|
6842
|
+
}
|
|
6843
|
+
async function getGitHubPullRequestProjectItems(octokit, repository, pullRequestNumber) {
|
|
6844
|
+
const projectItems = [];
|
|
6845
|
+
let after;
|
|
6846
|
+
let pullRequestId;
|
|
6847
|
+
let pullRequestTitle;
|
|
6848
|
+
let pullRequestUrl;
|
|
6849
|
+
do {
|
|
6850
|
+
const response = await octokit.graphql(
|
|
6851
|
+
GITHUB_PULL_REQUEST_PROJECT_ITEMS_QUERY,
|
|
6852
|
+
{
|
|
6853
|
+
owner: repository.owner,
|
|
6854
|
+
repo: repository.repo,
|
|
6855
|
+
pullRequestNumber,
|
|
6856
|
+
after
|
|
6857
|
+
}
|
|
6858
|
+
);
|
|
6859
|
+
const pullRequest = response.repository?.pullRequest;
|
|
6860
|
+
const currentPullRequestId = normalizeOptionalString2(pullRequest?.id);
|
|
6861
|
+
const currentPullRequestTitle = normalizeOptionalString2(pullRequest?.title);
|
|
6862
|
+
const currentPullRequestUrl = normalizeOptionalString2(pullRequest?.url);
|
|
6863
|
+
if (!currentPullRequestId || !currentPullRequestTitle || !currentPullRequestUrl) {
|
|
6864
|
+
throw new Error(`GitHub pull request #${pullRequestNumber} was not found in ${formatRepositoryLabel(repository)}.`);
|
|
6865
|
+
}
|
|
6866
|
+
pullRequestId ??= currentPullRequestId;
|
|
6867
|
+
pullRequestTitle ??= currentPullRequestTitle;
|
|
6868
|
+
pullRequestUrl ??= currentPullRequestUrl;
|
|
6869
|
+
const connection = response.repository?.pullRequest?.projectItems;
|
|
6870
|
+
for (const node of connection?.nodes ?? []) {
|
|
6871
|
+
const itemId = normalizeOptionalString2(node?.id);
|
|
6872
|
+
const project = normalizeGitHubProjectRecord(node?.project);
|
|
6873
|
+
if (!itemId || !project) {
|
|
6874
|
+
continue;
|
|
6875
|
+
}
|
|
6876
|
+
projectItems.push({
|
|
6877
|
+
id: itemId,
|
|
6878
|
+
project
|
|
6879
|
+
});
|
|
6880
|
+
}
|
|
6881
|
+
after = getPageCursor(connection?.pageInfo);
|
|
6882
|
+
} while (after);
|
|
6883
|
+
if (!pullRequestId || !pullRequestTitle || !pullRequestUrl) {
|
|
6884
|
+
throw new Error(`GitHub pull request #${pullRequestNumber} was not found in ${formatRepositoryLabel(repository)}.`);
|
|
6885
|
+
}
|
|
6886
|
+
return {
|
|
6887
|
+
pullRequestId,
|
|
6888
|
+
pullRequest: {
|
|
6889
|
+
number: pullRequestNumber,
|
|
6890
|
+
title: pullRequestTitle,
|
|
6891
|
+
url: pullRequestUrl
|
|
6892
|
+
},
|
|
6893
|
+
projectItems
|
|
6894
|
+
};
|
|
6895
|
+
}
|
|
6384
6896
|
function formatAiAuthorshipFooter(llmModel) {
|
|
6385
6897
|
return `
|
|
6386
6898
|
|
|
@@ -9029,6 +9541,22 @@ async function performSync(ctx, trigger, options = {}) {
|
|
|
9029
9541
|
const importedIssuesForSynchronization = [...importRegistryByIssueId.values()].filter(
|
|
9030
9542
|
(importedIssue) => allIssuesById.has(importedIssue.githubIssueId) && doesImportedIssueMatchTarget(importedIssue, options.target)
|
|
9031
9543
|
);
|
|
9544
|
+
const newlyImportedIssuesForMaintainerWarmup = advancedSettings.defaultStatus === "todo" ? [] : importedIssuesForSynchronization.filter((importedIssue) => createdIssueIds.has(importedIssue.githubIssueId)).map((importedIssue) => allIssuesById.get(importedIssue.githubIssueId)).filter(
|
|
9545
|
+
(githubIssue) => githubIssue !== void 0 && githubIssue.state === "open"
|
|
9546
|
+
).filter(
|
|
9547
|
+
(githubIssue) => !(linkedPullRequestsByIssueNumber.get(githubIssue.number) ?? []).some(
|
|
9548
|
+
(pullRequest) => pullRequest.state === "OPEN"
|
|
9549
|
+
)
|
|
9550
|
+
);
|
|
9551
|
+
if (newlyImportedIssuesForMaintainerWarmup.length > 0) {
|
|
9552
|
+
await warmGitHubRepositoryMaintainerCache({
|
|
9553
|
+
octokit,
|
|
9554
|
+
repository,
|
|
9555
|
+
githubIssues: newlyImportedIssuesForMaintainerWarmup,
|
|
9556
|
+
maintainerCache: repositoryMaintainerCache
|
|
9557
|
+
});
|
|
9558
|
+
await throwIfSyncCancelled();
|
|
9559
|
+
}
|
|
9032
9560
|
currentProgress = {
|
|
9033
9561
|
phase: "syncing",
|
|
9034
9562
|
totalRepositoryCount: mappings.length,
|
|
@@ -9928,6 +10456,91 @@ function registerGitHubAgentTools(ctx) {
|
|
|
9928
10456
|
);
|
|
9929
10457
|
})
|
|
9930
10458
|
);
|
|
10459
|
+
ctx.tools.register(
|
|
10460
|
+
"list_organization_projects",
|
|
10461
|
+
getGitHubAgentToolDeclaration("list_organization_projects"),
|
|
10462
|
+
async (params) => executeGitHubTool(async () => {
|
|
10463
|
+
const input = getToolInputRecord(params);
|
|
10464
|
+
const organization = normalizeOptionalToolString(input.organization);
|
|
10465
|
+
if (!organization) {
|
|
10466
|
+
throw new Error("organization is required.");
|
|
10467
|
+
}
|
|
10468
|
+
const octokit = await createGitHubToolOctokit(ctx);
|
|
10469
|
+
const projects = await listGitHubOrganizationProjects(octokit, organization, {
|
|
10470
|
+
includeClosed: input.includeClosed === true,
|
|
10471
|
+
query: normalizeOptionalToolString(input.query),
|
|
10472
|
+
limit: normalizeToolPositiveInteger(input.limit)
|
|
10473
|
+
});
|
|
10474
|
+
return buildToolSuccessResult(
|
|
10475
|
+
`Loaded ${projects.length} GitHub organization ${projects.length === 1 ? "project" : "projects"} from ${organization}.`,
|
|
10476
|
+
{
|
|
10477
|
+
organization,
|
|
10478
|
+
projects: projects.map((project) => buildGitHubProjectToolData(project))
|
|
10479
|
+
}
|
|
10480
|
+
);
|
|
10481
|
+
})
|
|
10482
|
+
);
|
|
10483
|
+
ctx.tools.register(
|
|
10484
|
+
"add_pull_request_to_project",
|
|
10485
|
+
getGitHubAgentToolDeclaration("add_pull_request_to_project"),
|
|
10486
|
+
async (params, runCtx) => executeGitHubTool(async () => {
|
|
10487
|
+
const input = getToolInputRecord(params);
|
|
10488
|
+
const target = await resolveGitHubPullRequestToolTarget(ctx, runCtx, input);
|
|
10489
|
+
const octokit = await createGitHubToolOctokit(ctx);
|
|
10490
|
+
const projectTarget = await resolveGitHubProjectToolTarget(octokit, input);
|
|
10491
|
+
const pullRequest = await getGitHubPullRequestProjectItems(
|
|
10492
|
+
octokit,
|
|
10493
|
+
target.repository,
|
|
10494
|
+
target.pullRequestNumber
|
|
10495
|
+
);
|
|
10496
|
+
const existingProjectItem = pullRequest.projectItems.find((item) => item.project.id === projectTarget.projectId);
|
|
10497
|
+
if (existingProjectItem) {
|
|
10498
|
+
return buildToolSuccessResult(
|
|
10499
|
+
`Pull request #${target.pullRequestNumber} is already associated with GitHub project #${existingProjectItem.project.number}.`,
|
|
10500
|
+
{
|
|
10501
|
+
repository: target.repository.url,
|
|
10502
|
+
pullRequest: pullRequest.pullRequest,
|
|
10503
|
+
project: buildGitHubProjectToolData(existingProjectItem.project, {
|
|
10504
|
+
includeOwnerLogin: true
|
|
10505
|
+
}),
|
|
10506
|
+
projectItem: {
|
|
10507
|
+
id: existingProjectItem.id
|
|
10508
|
+
},
|
|
10509
|
+
alreadyAssociated: true
|
|
10510
|
+
}
|
|
10511
|
+
);
|
|
10512
|
+
}
|
|
10513
|
+
const response = await octokit.graphql(
|
|
10514
|
+
GITHUB_ADD_PULL_REQUEST_TO_PROJECT_MUTATION,
|
|
10515
|
+
{
|
|
10516
|
+
projectId: projectTarget.projectId,
|
|
10517
|
+
contentId: pullRequest.pullRequestId
|
|
10518
|
+
}
|
|
10519
|
+
);
|
|
10520
|
+
const projectItemId = normalizeOptionalString2(response.addProjectV2ItemById?.item?.id);
|
|
10521
|
+
const project = normalizeGitHubProjectRecord(
|
|
10522
|
+
response.addProjectV2ItemById?.item?.project,
|
|
10523
|
+
projectTarget.project?.ownerLogin
|
|
10524
|
+
) ?? projectTarget.project;
|
|
10525
|
+
if (!projectItemId || !project) {
|
|
10526
|
+
throw new Error("GitHub did not return the created project item.");
|
|
10527
|
+
}
|
|
10528
|
+
return buildToolSuccessResult(
|
|
10529
|
+
`Added pull request #${target.pullRequestNumber} to GitHub project #${project.number}.`,
|
|
10530
|
+
{
|
|
10531
|
+
repository: target.repository.url,
|
|
10532
|
+
pullRequest: pullRequest.pullRequest,
|
|
10533
|
+
project: buildGitHubProjectToolData(project, {
|
|
10534
|
+
includeOwnerLogin: true
|
|
10535
|
+
}),
|
|
10536
|
+
projectItem: {
|
|
10537
|
+
id: projectItemId
|
|
10538
|
+
},
|
|
10539
|
+
alreadyAssociated: false
|
|
10540
|
+
}
|
|
10541
|
+
);
|
|
10542
|
+
})
|
|
10543
|
+
);
|
|
9931
10544
|
}
|
|
9932
10545
|
function shouldStartWorkerHost(moduleUrl, entry = process.argv[1]) {
|
|
9933
10546
|
if (typeof entry !== "string" || !entry.trim()) {
|