paperclip-github-plugin 0.3.4 → 0.3.6
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 +8 -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 +516 -97
- package/package.json +2 -2
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) {
|
|
@@ -640,6 +708,7 @@ var FAILED_CHECK_RUN_CONCLUSIONS = /* @__PURE__ */ new Set([
|
|
|
640
708
|
var SUCCESSFUL_STATUS_CONTEXT_STATES = /* @__PURE__ */ new Set(["SUCCESS"]);
|
|
641
709
|
var FAILED_STATUS_CONTEXT_STATES = /* @__PURE__ */ new Set(["ERROR", "FAILURE"]);
|
|
642
710
|
var PENDING_STATUS_CONTEXT_STATES = /* @__PURE__ */ new Set(["EXPECTED", "PENDING"]);
|
|
711
|
+
var GITHUB_REPOSITORY_MAINTAINER_WARMUP_CONCURRENCY = 4;
|
|
643
712
|
var GITHUB_REPOSITORY_MAINTAINER_ROLE_NAMES = /* @__PURE__ */ new Set(["admin", "maintain"]);
|
|
644
713
|
var GITHUB_ISSUE_STATUS_SNAPSHOT_QUERY = `
|
|
645
714
|
query GitHubIssueStatusSnapshot($owner: String!, $repo: String!, $issueNumber: Int!, $after: String) {
|
|
@@ -1039,6 +1108,114 @@ var GITHUB_PULL_REQUEST_REVIEW_THREADS_DETAILED_QUERY = `
|
|
|
1039
1108
|
}
|
|
1040
1109
|
}
|
|
1041
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
|
+
`;
|
|
1042
1219
|
var GITHUB_ADD_PULL_REQUEST_REVIEW_THREAD_REPLY_MUTATION = `
|
|
1043
1220
|
mutation GitHubAddPullRequestReviewThreadReply($pullRequestReviewThreadId: ID!, $body: String!) {
|
|
1044
1221
|
addPullRequestReviewThreadReply(input: {
|
|
@@ -3615,7 +3792,7 @@ function normalizePaperclipIssueStatus(value) {
|
|
|
3615
3792
|
return PAPERCLIP_ISSUE_STATUSES.includes(value) ? value : void 0;
|
|
3616
3793
|
}
|
|
3617
3794
|
function describeGitHubStatusTransitionReason(params) {
|
|
3618
|
-
const { snapshot, previousCommentCount, hasTrustedNewComment } = params;
|
|
3795
|
+
const { snapshot, previousCommentCount, hasTrustedNewComment, maintainerAuthoredImportedIssue } = params;
|
|
3619
3796
|
if (snapshot.state === "closed") {
|
|
3620
3797
|
switch (snapshot.stateReason) {
|
|
3621
3798
|
case "duplicate":
|
|
@@ -3631,6 +3808,9 @@ function describeGitHubStatusTransitionReason(params) {
|
|
|
3631
3808
|
return "a new GitHub comment from the issue author or a repository maintainer was added";
|
|
3632
3809
|
}
|
|
3633
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
|
+
}
|
|
3634
3814
|
return "the GitHub issue is open with no linked pull requests";
|
|
3635
3815
|
}
|
|
3636
3816
|
const linkedPullRequestSubject = snapshot.linkedPullRequests.length === 1 ? "the linked pull request" : "linked pull requests";
|
|
@@ -3669,11 +3849,20 @@ function buildStatusTransitionCommentAnnotation(params) {
|
|
|
3669
3849
|
};
|
|
3670
3850
|
}
|
|
3671
3851
|
function buildPaperclipIssueStatusTransitionComment(params) {
|
|
3672
|
-
const {
|
|
3852
|
+
const {
|
|
3853
|
+
previousStatus,
|
|
3854
|
+
nextStatus,
|
|
3855
|
+
repository,
|
|
3856
|
+
snapshot,
|
|
3857
|
+
previousCommentCount,
|
|
3858
|
+
hasTrustedNewComment,
|
|
3859
|
+
maintainerAuthoredImportedIssue
|
|
3860
|
+
} = params;
|
|
3673
3861
|
const reason = describeGitHubStatusTransitionReason({
|
|
3674
3862
|
snapshot,
|
|
3675
3863
|
previousCommentCount,
|
|
3676
|
-
hasTrustedNewComment
|
|
3864
|
+
hasTrustedNewComment,
|
|
3865
|
+
maintainerAuthoredImportedIssue
|
|
3677
3866
|
});
|
|
3678
3867
|
return {
|
|
3679
3868
|
body: `GitHub Sync updated the status from \`${formatPaperclipIssueStatus(previousStatus)}\` to \`${formatPaperclipIssueStatus(nextStatus)}\` because ${reason}.`,
|
|
@@ -3693,7 +3882,8 @@ function resolvePaperclipIssueStatus(params) {
|
|
|
3693
3882
|
previousCommentCount,
|
|
3694
3883
|
hasTrustedNewComment,
|
|
3695
3884
|
wasImportedThisRun,
|
|
3696
|
-
defaultImportedStatus
|
|
3885
|
+
defaultImportedStatus,
|
|
3886
|
+
maintainerAuthoredImportedIssue
|
|
3697
3887
|
} = params;
|
|
3698
3888
|
if (snapshot.state === "closed") {
|
|
3699
3889
|
return snapshot.stateReason === "duplicate" || snapshot.stateReason === "not_planned" ? "cancelled" : "done";
|
|
@@ -3709,7 +3899,7 @@ function resolvePaperclipIssueStatus(params) {
|
|
|
3709
3899
|
return resolvePaperclipStatusFromLinkedPullRequests(snapshot.linkedPullRequests);
|
|
3710
3900
|
}
|
|
3711
3901
|
if (wasImportedThisRun) {
|
|
3712
|
-
return defaultImportedStatus;
|
|
3902
|
+
return maintainerAuthoredImportedIssue ? "todo" : defaultImportedStatus;
|
|
3713
3903
|
}
|
|
3714
3904
|
if (currentStatus === "done" || currentStatus === "cancelled") {
|
|
3715
3905
|
return "todo";
|
|
@@ -4116,6 +4306,40 @@ async function hasTrustedNewGitHubIssueComment(params) {
|
|
|
4116
4306
|
}
|
|
4117
4307
|
return false;
|
|
4118
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
|
+
}
|
|
4119
4343
|
function parseGitHubIssueHtmlUrl(value) {
|
|
4120
4344
|
try {
|
|
4121
4345
|
const url = new URL(value.trim());
|
|
@@ -4709,9 +4933,6 @@ function selectPaperclipLabelForGitHubLabel(githubLabel, directory) {
|
|
|
4709
4933
|
function getPaperclipLabelsEndpoint(baseUrl, companyId) {
|
|
4710
4934
|
return new URL(`/api/companies/${companyId}/labels`, baseUrl).toString();
|
|
4711
4935
|
}
|
|
4712
|
-
function getPaperclipIssuesEndpoint(baseUrl, companyId) {
|
|
4713
|
-
return new URL(`/api/companies/${companyId}/issues`, baseUrl).toString();
|
|
4714
|
-
}
|
|
4715
4936
|
function getPaperclipIssueEndpoint(baseUrl, issueId) {
|
|
4716
4937
|
return new URL(`/api/issues/${issueId}`, baseUrl).toString();
|
|
4717
4938
|
}
|
|
@@ -4762,17 +4983,6 @@ async function detectPaperclipBoardAccessRequirement(paperclipApiBaseUrl) {
|
|
|
4762
4983
|
return false;
|
|
4763
4984
|
}
|
|
4764
4985
|
}
|
|
4765
|
-
function parsePaperclipIssueId(value) {
|
|
4766
|
-
if (!value || typeof value !== "object") {
|
|
4767
|
-
return null;
|
|
4768
|
-
}
|
|
4769
|
-
const id = value.id;
|
|
4770
|
-
if (typeof id !== "string") {
|
|
4771
|
-
return null;
|
|
4772
|
-
}
|
|
4773
|
-
const trimmedId = id.trim();
|
|
4774
|
-
return trimmedId || null;
|
|
4775
|
-
}
|
|
4776
4986
|
function parsePaperclipIssueDescription(value) {
|
|
4777
4987
|
if (!value || typeof value !== "object") {
|
|
4778
4988
|
return void 0;
|
|
@@ -5577,87 +5787,22 @@ function shouldIgnoreGitHubIssue(issue, advancedSettings) {
|
|
|
5577
5787
|
(ignoredUsername) => buildGitHubUsernameAliases(ignoredUsername).some((alias) => issueAuthorAliases.has(alias))
|
|
5578
5788
|
);
|
|
5579
5789
|
}
|
|
5580
|
-
async function applyDefaultAssigneeToPaperclipIssue(ctx, params) {
|
|
5581
|
-
const { companyId, issueId, defaultAssigneeAgentId } = params;
|
|
5582
|
-
if (!defaultAssigneeAgentId) {
|
|
5583
|
-
return;
|
|
5584
|
-
}
|
|
5585
|
-
try {
|
|
5586
|
-
await ctx.issues.update(issueId, { assigneeAgentId: defaultAssigneeAgentId }, companyId);
|
|
5587
|
-
} catch (error) {
|
|
5588
|
-
ctx.logger.warn("Unable to apply the default assignee to an imported GitHub issue.", {
|
|
5589
|
-
companyId,
|
|
5590
|
-
issueId,
|
|
5591
|
-
assigneeAgentId: defaultAssigneeAgentId,
|
|
5592
|
-
error: getErrorMessage(error)
|
|
5593
|
-
});
|
|
5594
|
-
}
|
|
5595
|
-
}
|
|
5596
5790
|
async function createPaperclipIssue(ctx, mapping, advancedSettings, issue, availableLabels, paperclipApiBaseUrl, syncFailureContext) {
|
|
5597
5791
|
if (!mapping.companyId || !mapping.paperclipProjectId) {
|
|
5598
5792
|
throw new Error(`Mapping ${mapping.id} is missing resolved Paperclip project identifiers.`);
|
|
5599
5793
|
}
|
|
5600
5794
|
const title = issue.title;
|
|
5601
5795
|
const description = buildPaperclipIssueDescription(issue);
|
|
5602
|
-
|
|
5603
|
-
|
|
5604
|
-
|
|
5605
|
-
|
|
5606
|
-
|
|
5607
|
-
|
|
5608
|
-
|
|
5609
|
-
|
|
5610
|
-
|
|
5611
|
-
|
|
5612
|
-
accept: "application/json",
|
|
5613
|
-
"content-type": "application/json"
|
|
5614
|
-
},
|
|
5615
|
-
body: JSON.stringify({
|
|
5616
|
-
projectId: mapping.paperclipProjectId,
|
|
5617
|
-
title,
|
|
5618
|
-
...description ? { description } : {}
|
|
5619
|
-
})
|
|
5620
|
-
},
|
|
5621
|
-
{
|
|
5622
|
-
companyId: mapping.companyId
|
|
5623
|
-
}
|
|
5624
|
-
);
|
|
5625
|
-
const payloadResult = await readPaperclipApiJsonResponse(response, {
|
|
5626
|
-
operationLabel: "issue create"
|
|
5627
|
-
});
|
|
5628
|
-
if (!payloadResult.failure) {
|
|
5629
|
-
const createdIssue = payloadResult.data;
|
|
5630
|
-
createdIssueId = parsePaperclipIssueId(createdIssue);
|
|
5631
|
-
createdIssueDescription = parsePaperclipIssueDescription(createdIssue);
|
|
5632
|
-
createPath = "local_api";
|
|
5633
|
-
}
|
|
5634
|
-
} catch {
|
|
5635
|
-
}
|
|
5636
|
-
}
|
|
5637
|
-
if (!createdIssueId) {
|
|
5638
|
-
const createdIssue = await ctx.issues.create({
|
|
5639
|
-
companyId: mapping.companyId,
|
|
5640
|
-
projectId: mapping.paperclipProjectId,
|
|
5641
|
-
title,
|
|
5642
|
-
...description ? { description } : {},
|
|
5643
|
-
...advancedSettings.defaultAssigneeAgentId ? { assigneeAgentId: advancedSettings.defaultAssigneeAgentId } : {}
|
|
5644
|
-
});
|
|
5645
|
-
createdIssueId = createdIssue.id;
|
|
5646
|
-
createdIssueDescription = createdIssue.description;
|
|
5647
|
-
createPath = "sdk";
|
|
5648
|
-
}
|
|
5649
|
-
const ensuredCreatedIssueId = createdIssueId;
|
|
5650
|
-
if (!ensuredCreatedIssueId) {
|
|
5651
|
-
throw new Error("GitHub sync could not resolve the created Paperclip issue id.");
|
|
5652
|
-
}
|
|
5653
|
-
const normalizedCreatedIssueDescription = createdIssueDescription ?? void 0;
|
|
5654
|
-
if (createPath !== "sdk") {
|
|
5655
|
-
await applyDefaultAssigneeToPaperclipIssue(ctx, {
|
|
5656
|
-
companyId: mapping.companyId,
|
|
5657
|
-
issueId: ensuredCreatedIssueId,
|
|
5658
|
-
defaultAssigneeAgentId: advancedSettings.defaultAssigneeAgentId
|
|
5659
|
-
});
|
|
5660
|
-
}
|
|
5796
|
+
const createdIssue = await ctx.issues.create({
|
|
5797
|
+
companyId: mapping.companyId,
|
|
5798
|
+
projectId: mapping.paperclipProjectId,
|
|
5799
|
+
title,
|
|
5800
|
+
...description ? { description } : {},
|
|
5801
|
+
...advancedSettings.defaultAssigneeAgentId ? { assigneeAgentId: advancedSettings.defaultAssigneeAgentId } : {}
|
|
5802
|
+
});
|
|
5803
|
+
const ensuredCreatedIssueId = createdIssue.id;
|
|
5804
|
+
const normalizedCreatedIssueDescription = createdIssue.description ?? void 0;
|
|
5805
|
+
const createPath = "sdk";
|
|
5661
5806
|
if (normalizeIssueDescriptionValue(normalizedCreatedIssueDescription) !== description) {
|
|
5662
5807
|
logIssueDescriptionDiagnostic(
|
|
5663
5808
|
ctx,
|
|
@@ -5910,13 +6055,21 @@ async function synchronizePaperclipIssueStatuses(ctx, octokit, repository, mappi
|
|
|
5910
6055
|
currentCommentCount: snapshot.commentCount,
|
|
5911
6056
|
maintainerCache: repositoryMaintainerCache
|
|
5912
6057
|
});
|
|
6058
|
+
const wasImportedThisRun = createdIssueIds.has(importedIssue.githubIssueId);
|
|
6059
|
+
const maintainerAuthoredImportedIssue = wasImportedThisRun && advancedSettings.defaultStatus !== "todo" && snapshot.state === "open" && snapshot.linkedPullRequests.length === 0 ? await isMaintainerAuthoredGitHubIssue({
|
|
6060
|
+
octokit,
|
|
6061
|
+
repository,
|
|
6062
|
+
githubIssue,
|
|
6063
|
+
maintainerCache: repositoryMaintainerCache
|
|
6064
|
+
}) : false;
|
|
5913
6065
|
const nextStatus = resolvePaperclipIssueStatus({
|
|
5914
6066
|
currentStatus: paperclipIssue.status,
|
|
5915
6067
|
snapshot,
|
|
5916
6068
|
previousCommentCount,
|
|
5917
6069
|
hasTrustedNewComment,
|
|
5918
|
-
wasImportedThisRun
|
|
5919
|
-
defaultImportedStatus: advancedSettings.defaultStatus
|
|
6070
|
+
wasImportedThisRun,
|
|
6071
|
+
defaultImportedStatus: advancedSettings.defaultStatus,
|
|
6072
|
+
maintainerAuthoredImportedIssue
|
|
5920
6073
|
});
|
|
5921
6074
|
importedIssue.githubIssueNumber = githubIssue.number;
|
|
5922
6075
|
importedIssue.lastSeenCommentCount = snapshot.commentCount;
|
|
@@ -5929,7 +6082,8 @@ async function synchronizePaperclipIssueStatuses(ctx, octokit, repository, mappi
|
|
|
5929
6082
|
repository,
|
|
5930
6083
|
snapshot,
|
|
5931
6084
|
previousCommentCount,
|
|
5932
|
-
hasTrustedNewComment
|
|
6085
|
+
hasTrustedNewComment,
|
|
6086
|
+
maintainerAuthoredImportedIssue
|
|
5933
6087
|
});
|
|
5934
6088
|
updateSyncFailureContext(syncFailureContext, {
|
|
5935
6089
|
phase: "updating_paperclip_status",
|
|
@@ -6496,6 +6650,170 @@ async function resolveGitHubPullRequestToolTarget(ctx, runCtx, input) {
|
|
|
6496
6650
|
pullRequestNumber
|
|
6497
6651
|
};
|
|
6498
6652
|
}
|
|
6653
|
+
function normalizeGitHubProjectRecord(project, fallbackOwnerLogin) {
|
|
6654
|
+
const id = normalizeOptionalString2(project?.id);
|
|
6655
|
+
const title = normalizeOptionalString2(project?.title);
|
|
6656
|
+
const url = normalizeOptionalString2(project?.url);
|
|
6657
|
+
const number = typeof project?.number === "number" && Number.isInteger(project.number) && project.number > 0 ? Math.floor(project.number) : null;
|
|
6658
|
+
if (!id || !title || !url || number === null) {
|
|
6659
|
+
return null;
|
|
6660
|
+
}
|
|
6661
|
+
const shortDescription = normalizeOptionalString2(project?.shortDescription);
|
|
6662
|
+
const updatedAt = normalizeOptionalString2(project?.updatedAt);
|
|
6663
|
+
const ownerLogin = normalizeOptionalString2(project?.owner?.login) ?? fallbackOwnerLogin;
|
|
6664
|
+
return {
|
|
6665
|
+
id,
|
|
6666
|
+
number,
|
|
6667
|
+
title,
|
|
6668
|
+
url,
|
|
6669
|
+
closed: project?.closed === true,
|
|
6670
|
+
...shortDescription ? { shortDescription } : {},
|
|
6671
|
+
...updatedAt ? { updatedAt } : {},
|
|
6672
|
+
...ownerLogin ? { ownerLogin } : {}
|
|
6673
|
+
};
|
|
6674
|
+
}
|
|
6675
|
+
function buildGitHubProjectToolData(project, options) {
|
|
6676
|
+
return {
|
|
6677
|
+
id: project.id,
|
|
6678
|
+
number: project.number,
|
|
6679
|
+
title: project.title,
|
|
6680
|
+
...project.shortDescription ? { shortDescription: project.shortDescription } : {},
|
|
6681
|
+
url: project.url,
|
|
6682
|
+
closed: project.closed,
|
|
6683
|
+
...project.updatedAt ? { updatedAt: project.updatedAt } : {},
|
|
6684
|
+
...options?.includeOwnerLogin && project.ownerLogin ? { ownerLogin: project.ownerLogin } : {}
|
|
6685
|
+
};
|
|
6686
|
+
}
|
|
6687
|
+
function matchesGitHubProjectFilter(project, query) {
|
|
6688
|
+
const normalizedQuery = normalizeOptionalString2(query)?.toLowerCase();
|
|
6689
|
+
if (!normalizedQuery) {
|
|
6690
|
+
return true;
|
|
6691
|
+
}
|
|
6692
|
+
return [project.title, project.shortDescription].filter((value) => Boolean(value)).some((value) => value.toLowerCase().includes(normalizedQuery));
|
|
6693
|
+
}
|
|
6694
|
+
async function listGitHubOrganizationProjects(octokit, organization, options) {
|
|
6695
|
+
const normalizedOrganization = normalizeOptionalString2(organization);
|
|
6696
|
+
if (!normalizedOrganization) {
|
|
6697
|
+
throw new Error("organization is required.");
|
|
6698
|
+
}
|
|
6699
|
+
const includeClosed = options?.includeClosed === true;
|
|
6700
|
+
const limit = Math.min(normalizeToolPositiveInteger(options?.limit) ?? 20, 100);
|
|
6701
|
+
const projects = [];
|
|
6702
|
+
let after;
|
|
6703
|
+
do {
|
|
6704
|
+
const response = await octokit.graphql(
|
|
6705
|
+
GITHUB_ORGANIZATION_PROJECTS_QUERY,
|
|
6706
|
+
{
|
|
6707
|
+
organization: normalizedOrganization,
|
|
6708
|
+
first: Math.min(50, Math.max(1, limit)),
|
|
6709
|
+
after
|
|
6710
|
+
}
|
|
6711
|
+
);
|
|
6712
|
+
if (!response.organization) {
|
|
6713
|
+
throw new Error(`GitHub organization ${normalizedOrganization} was not found or is not visible to the configured token.`);
|
|
6714
|
+
}
|
|
6715
|
+
const connection = response.organization.projectsV2;
|
|
6716
|
+
for (const node of connection?.nodes ?? []) {
|
|
6717
|
+
const project = normalizeGitHubProjectRecord(node, normalizedOrganization);
|
|
6718
|
+
if (!project) {
|
|
6719
|
+
continue;
|
|
6720
|
+
}
|
|
6721
|
+
if (!includeClosed && project.closed) {
|
|
6722
|
+
continue;
|
|
6723
|
+
}
|
|
6724
|
+
if (!matchesGitHubProjectFilter(project, options?.query)) {
|
|
6725
|
+
continue;
|
|
6726
|
+
}
|
|
6727
|
+
projects.push(project);
|
|
6728
|
+
if (projects.length >= limit) {
|
|
6729
|
+
return projects;
|
|
6730
|
+
}
|
|
6731
|
+
}
|
|
6732
|
+
after = getPageCursor(connection?.pageInfo);
|
|
6733
|
+
} while (after);
|
|
6734
|
+
return projects;
|
|
6735
|
+
}
|
|
6736
|
+
async function resolveGitHubProjectToolTarget(octokit, input) {
|
|
6737
|
+
const explicitProjectId = normalizeOptionalString2(input.projectId);
|
|
6738
|
+
if (explicitProjectId) {
|
|
6739
|
+
return {
|
|
6740
|
+
projectId: explicitProjectId
|
|
6741
|
+
};
|
|
6742
|
+
}
|
|
6743
|
+
const organization = normalizeOptionalToolString(input.organization);
|
|
6744
|
+
const projectNumber = normalizeToolPositiveInteger(input.projectNumber);
|
|
6745
|
+
if (!organization || projectNumber === void 0) {
|
|
6746
|
+
throw new Error("Provide either projectId or both organization and projectNumber.");
|
|
6747
|
+
}
|
|
6748
|
+
const response = await octokit.graphql(
|
|
6749
|
+
GITHUB_ORGANIZATION_PROJECT_BY_NUMBER_QUERY,
|
|
6750
|
+
{
|
|
6751
|
+
organization,
|
|
6752
|
+
projectNumber
|
|
6753
|
+
}
|
|
6754
|
+
);
|
|
6755
|
+
const project = normalizeGitHubProjectRecord(response.organization?.projectV2, organization);
|
|
6756
|
+
if (!project) {
|
|
6757
|
+
throw new Error(`GitHub organization project #${projectNumber} was not found in ${organization}.`);
|
|
6758
|
+
}
|
|
6759
|
+
return {
|
|
6760
|
+
projectId: project.id,
|
|
6761
|
+
project
|
|
6762
|
+
};
|
|
6763
|
+
}
|
|
6764
|
+
async function getGitHubPullRequestProjectItems(octokit, repository, pullRequestNumber) {
|
|
6765
|
+
const projectItems = [];
|
|
6766
|
+
let after;
|
|
6767
|
+
let pullRequestId;
|
|
6768
|
+
let pullRequestTitle;
|
|
6769
|
+
let pullRequestUrl;
|
|
6770
|
+
do {
|
|
6771
|
+
const response = await octokit.graphql(
|
|
6772
|
+
GITHUB_PULL_REQUEST_PROJECT_ITEMS_QUERY,
|
|
6773
|
+
{
|
|
6774
|
+
owner: repository.owner,
|
|
6775
|
+
repo: repository.repo,
|
|
6776
|
+
pullRequestNumber,
|
|
6777
|
+
after
|
|
6778
|
+
}
|
|
6779
|
+
);
|
|
6780
|
+
const pullRequest = response.repository?.pullRequest;
|
|
6781
|
+
const currentPullRequestId = normalizeOptionalString2(pullRequest?.id);
|
|
6782
|
+
const currentPullRequestTitle = normalizeOptionalString2(pullRequest?.title);
|
|
6783
|
+
const currentPullRequestUrl = normalizeOptionalString2(pullRequest?.url);
|
|
6784
|
+
if (!currentPullRequestId || !currentPullRequestTitle || !currentPullRequestUrl) {
|
|
6785
|
+
throw new Error(`GitHub pull request #${pullRequestNumber} was not found in ${formatRepositoryLabel(repository)}.`);
|
|
6786
|
+
}
|
|
6787
|
+
pullRequestId ??= currentPullRequestId;
|
|
6788
|
+
pullRequestTitle ??= currentPullRequestTitle;
|
|
6789
|
+
pullRequestUrl ??= currentPullRequestUrl;
|
|
6790
|
+
const connection = response.repository?.pullRequest?.projectItems;
|
|
6791
|
+
for (const node of connection?.nodes ?? []) {
|
|
6792
|
+
const itemId = normalizeOptionalString2(node?.id);
|
|
6793
|
+
const project = normalizeGitHubProjectRecord(node?.project);
|
|
6794
|
+
if (!itemId || !project) {
|
|
6795
|
+
continue;
|
|
6796
|
+
}
|
|
6797
|
+
projectItems.push({
|
|
6798
|
+
id: itemId,
|
|
6799
|
+
project
|
|
6800
|
+
});
|
|
6801
|
+
}
|
|
6802
|
+
after = getPageCursor(connection?.pageInfo);
|
|
6803
|
+
} while (after);
|
|
6804
|
+
if (!pullRequestId || !pullRequestTitle || !pullRequestUrl) {
|
|
6805
|
+
throw new Error(`GitHub pull request #${pullRequestNumber} was not found in ${formatRepositoryLabel(repository)}.`);
|
|
6806
|
+
}
|
|
6807
|
+
return {
|
|
6808
|
+
pullRequestId,
|
|
6809
|
+
pullRequest: {
|
|
6810
|
+
number: pullRequestNumber,
|
|
6811
|
+
title: pullRequestTitle,
|
|
6812
|
+
url: pullRequestUrl
|
|
6813
|
+
},
|
|
6814
|
+
projectItems
|
|
6815
|
+
};
|
|
6816
|
+
}
|
|
6499
6817
|
function formatAiAuthorshipFooter(llmModel) {
|
|
6500
6818
|
return `
|
|
6501
6819
|
|
|
@@ -9144,6 +9462,22 @@ async function performSync(ctx, trigger, options = {}) {
|
|
|
9144
9462
|
const importedIssuesForSynchronization = [...importRegistryByIssueId.values()].filter(
|
|
9145
9463
|
(importedIssue) => allIssuesById.has(importedIssue.githubIssueId) && doesImportedIssueMatchTarget(importedIssue, options.target)
|
|
9146
9464
|
);
|
|
9465
|
+
const newlyImportedIssuesForMaintainerWarmup = advancedSettings.defaultStatus === "todo" ? [] : importedIssuesForSynchronization.filter((importedIssue) => createdIssueIds.has(importedIssue.githubIssueId)).map((importedIssue) => allIssuesById.get(importedIssue.githubIssueId)).filter(
|
|
9466
|
+
(githubIssue) => githubIssue !== void 0 && githubIssue.state === "open"
|
|
9467
|
+
).filter(
|
|
9468
|
+
(githubIssue) => !(linkedPullRequestsByIssueNumber.get(githubIssue.number) ?? []).some(
|
|
9469
|
+
(pullRequest) => pullRequest.state === "OPEN"
|
|
9470
|
+
)
|
|
9471
|
+
);
|
|
9472
|
+
if (newlyImportedIssuesForMaintainerWarmup.length > 0) {
|
|
9473
|
+
await warmGitHubRepositoryMaintainerCache({
|
|
9474
|
+
octokit,
|
|
9475
|
+
repository,
|
|
9476
|
+
githubIssues: newlyImportedIssuesForMaintainerWarmup,
|
|
9477
|
+
maintainerCache: repositoryMaintainerCache
|
|
9478
|
+
});
|
|
9479
|
+
await throwIfSyncCancelled();
|
|
9480
|
+
}
|
|
9147
9481
|
currentProgress = {
|
|
9148
9482
|
phase: "syncing",
|
|
9149
9483
|
totalRepositoryCount: mappings.length,
|
|
@@ -10043,6 +10377,91 @@ function registerGitHubAgentTools(ctx) {
|
|
|
10043
10377
|
);
|
|
10044
10378
|
})
|
|
10045
10379
|
);
|
|
10380
|
+
ctx.tools.register(
|
|
10381
|
+
"list_organization_projects",
|
|
10382
|
+
getGitHubAgentToolDeclaration("list_organization_projects"),
|
|
10383
|
+
async (params) => executeGitHubTool(async () => {
|
|
10384
|
+
const input = getToolInputRecord(params);
|
|
10385
|
+
const organization = normalizeOptionalToolString(input.organization);
|
|
10386
|
+
if (!organization) {
|
|
10387
|
+
throw new Error("organization is required.");
|
|
10388
|
+
}
|
|
10389
|
+
const octokit = await createGitHubToolOctokit(ctx);
|
|
10390
|
+
const projects = await listGitHubOrganizationProjects(octokit, organization, {
|
|
10391
|
+
includeClosed: input.includeClosed === true,
|
|
10392
|
+
query: normalizeOptionalToolString(input.query),
|
|
10393
|
+
limit: normalizeToolPositiveInteger(input.limit)
|
|
10394
|
+
});
|
|
10395
|
+
return buildToolSuccessResult(
|
|
10396
|
+
`Loaded ${projects.length} GitHub organization ${projects.length === 1 ? "project" : "projects"} from ${organization}.`,
|
|
10397
|
+
{
|
|
10398
|
+
organization,
|
|
10399
|
+
projects: projects.map((project) => buildGitHubProjectToolData(project))
|
|
10400
|
+
}
|
|
10401
|
+
);
|
|
10402
|
+
})
|
|
10403
|
+
);
|
|
10404
|
+
ctx.tools.register(
|
|
10405
|
+
"add_pull_request_to_project",
|
|
10406
|
+
getGitHubAgentToolDeclaration("add_pull_request_to_project"),
|
|
10407
|
+
async (params, runCtx) => executeGitHubTool(async () => {
|
|
10408
|
+
const input = getToolInputRecord(params);
|
|
10409
|
+
const target = await resolveGitHubPullRequestToolTarget(ctx, runCtx, input);
|
|
10410
|
+
const octokit = await createGitHubToolOctokit(ctx);
|
|
10411
|
+
const projectTarget = await resolveGitHubProjectToolTarget(octokit, input);
|
|
10412
|
+
const pullRequest = await getGitHubPullRequestProjectItems(
|
|
10413
|
+
octokit,
|
|
10414
|
+
target.repository,
|
|
10415
|
+
target.pullRequestNumber
|
|
10416
|
+
);
|
|
10417
|
+
const existingProjectItem = pullRequest.projectItems.find((item) => item.project.id === projectTarget.projectId);
|
|
10418
|
+
if (existingProjectItem) {
|
|
10419
|
+
return buildToolSuccessResult(
|
|
10420
|
+
`Pull request #${target.pullRequestNumber} is already associated with GitHub project #${existingProjectItem.project.number}.`,
|
|
10421
|
+
{
|
|
10422
|
+
repository: target.repository.url,
|
|
10423
|
+
pullRequest: pullRequest.pullRequest,
|
|
10424
|
+
project: buildGitHubProjectToolData(existingProjectItem.project, {
|
|
10425
|
+
includeOwnerLogin: true
|
|
10426
|
+
}),
|
|
10427
|
+
projectItem: {
|
|
10428
|
+
id: existingProjectItem.id
|
|
10429
|
+
},
|
|
10430
|
+
alreadyAssociated: true
|
|
10431
|
+
}
|
|
10432
|
+
);
|
|
10433
|
+
}
|
|
10434
|
+
const response = await octokit.graphql(
|
|
10435
|
+
GITHUB_ADD_PULL_REQUEST_TO_PROJECT_MUTATION,
|
|
10436
|
+
{
|
|
10437
|
+
projectId: projectTarget.projectId,
|
|
10438
|
+
contentId: pullRequest.pullRequestId
|
|
10439
|
+
}
|
|
10440
|
+
);
|
|
10441
|
+
const projectItemId = normalizeOptionalString2(response.addProjectV2ItemById?.item?.id);
|
|
10442
|
+
const project = normalizeGitHubProjectRecord(
|
|
10443
|
+
response.addProjectV2ItemById?.item?.project,
|
|
10444
|
+
projectTarget.project?.ownerLogin
|
|
10445
|
+
) ?? projectTarget.project;
|
|
10446
|
+
if (!projectItemId || !project) {
|
|
10447
|
+
throw new Error("GitHub did not return the created project item.");
|
|
10448
|
+
}
|
|
10449
|
+
return buildToolSuccessResult(
|
|
10450
|
+
`Added pull request #${target.pullRequestNumber} to GitHub project #${project.number}.`,
|
|
10451
|
+
{
|
|
10452
|
+
repository: target.repository.url,
|
|
10453
|
+
pullRequest: pullRequest.pullRequest,
|
|
10454
|
+
project: buildGitHubProjectToolData(project, {
|
|
10455
|
+
includeOwnerLogin: true
|
|
10456
|
+
}),
|
|
10457
|
+
projectItem: {
|
|
10458
|
+
id: projectItemId
|
|
10459
|
+
},
|
|
10460
|
+
alreadyAssociated: false
|
|
10461
|
+
}
|
|
10462
|
+
);
|
|
10463
|
+
})
|
|
10464
|
+
);
|
|
10046
10465
|
}
|
|
10047
10466
|
function shouldStartWorkerHost(moduleUrl, entry = process.argv[1]) {
|
|
10048
10467
|
if (typeof entry !== "string" || !entry.trim()) {
|