paperclip-github-plugin 0.3.2 → 0.3.4
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 +3 -0
- package/dist/manifest.js +1 -1
- package/dist/worker.js +157 -35
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -118,6 +118,8 @@ When a token is saved, the settings page audits the mapped repositories for the
|
|
|
118
118
|
|
|
119
119
|
Imported issues keep the original GitHub title and use the normalized GitHub body as the Paperclip description. The worker also normalizes GitHub HTML that Paperclip descriptions do not render cleanly, including elements such as `<br>`, `<hr>`, `<details>`, `<summary>`, and inline images.
|
|
120
120
|
|
|
121
|
+
To keep imported issues recognizable without cluttering the visible description, the plugin appends a hidden HTML comment footer with the source GitHub issue URL. Agents and repair flows use that marker when the plugin-owned link entity or import registry is missing.
|
|
122
|
+
|
|
121
123
|
Repeated syncs keep existing imports current instead of creating duplicates again. If the plugin's import registry is stale, the worker can repair deduplication by reusing existing Paperclip issues when durable GitHub link metadata is already present.
|
|
122
124
|
|
|
123
125
|
When the local Paperclip API is available, the plugin also syncs labels by name, prefers exact color matches when multiple Paperclip labels share the same name, and creates missing Paperclip labels when needed.
|
|
@@ -136,6 +138,7 @@ When the local Paperclip API is available, the plugin also syncs labels by name,
|
|
|
136
138
|
Additional behavior:
|
|
137
139
|
|
|
138
140
|
- Open imported issues that are already in `backlog` stay in `backlog` until someone changes them in Paperclip.
|
|
141
|
+
- If an imported issue is `done` or `cancelled` and GitHub shows it open again with no linked pull request, sync moves it to `todo` so agents can pick it up again.
|
|
139
142
|
- Trusted new GitHub comments from the original issue author or a verified maintainer/admin can move an open imported issue back to `todo`.
|
|
140
143
|
- When the sync changes a Paperclip issue status, it adds a Paperclip comment explaining what changed and why.
|
|
141
144
|
|
package/dist/manifest.js
CHANGED
|
@@ -435,7 +435,7 @@ var require2 = createRequire(import.meta.url);
|
|
|
435
435
|
var packageJson = require2("../package.json");
|
|
436
436
|
var DASHBOARD_WIDGET_CAPABILITY = "ui.dashboardWidget.register";
|
|
437
437
|
var SCHEDULE_TICK_CRON = "* * * * *";
|
|
438
|
-
var MANIFEST_VERSION = "0.3.
|
|
438
|
+
var MANIFEST_VERSION = "0.3.4"?.trim() || typeof packageJson.version === "string" && packageJson.version.trim() || process.env.npm_package_version?.trim() || "0.0.0-dev";
|
|
439
439
|
var manifest = {
|
|
440
440
|
id: "paperclip-github-plugin",
|
|
441
441
|
apiVersion: 1,
|
package/dist/worker.js
CHANGED
|
@@ -560,6 +560,8 @@ var ISSUE_LINK_ENTITY_TYPE = "paperclip-github-plugin.issue-link";
|
|
|
560
560
|
var PULL_REQUEST_LINK_ENTITY_TYPE = "paperclip-github-plugin.pull-request-link";
|
|
561
561
|
var COMMENT_ANNOTATION_ENTITY_TYPE = "paperclip-github-plugin.comment-annotation";
|
|
562
562
|
var AI_AUTHORED_COMMENT_FOOTER_PREFIX = "Created by a Paperclip AI agent using ";
|
|
563
|
+
var HIDDEN_GITHUB_IMPORT_MARKER_PREFIX = "<!-- paperclip-github-plugin-imported-from: ";
|
|
564
|
+
var HIDDEN_GITHUB_IMPORT_MARKER_SUFFIX = " -->";
|
|
563
565
|
function normalizeCompanyId(value) {
|
|
564
566
|
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
565
567
|
}
|
|
@@ -1157,6 +1159,9 @@ function createMappingId(index) {
|
|
|
1157
1159
|
function normalizeOptionalString2(value) {
|
|
1158
1160
|
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
1159
1161
|
}
|
|
1162
|
+
function stripNullBytes(value) {
|
|
1163
|
+
return value.replace(/\u0000/g, "");
|
|
1164
|
+
}
|
|
1160
1165
|
function getErrorStatus(error) {
|
|
1161
1166
|
if (!error || typeof error !== "object" || !("status" in error)) {
|
|
1162
1167
|
return void 0;
|
|
@@ -1419,7 +1424,11 @@ function normalizeSecretRef(value) {
|
|
|
1419
1424
|
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
1420
1425
|
}
|
|
1421
1426
|
function normalizeGitHubUserLogin(value) {
|
|
1422
|
-
|
|
1427
|
+
if (typeof value !== "string") {
|
|
1428
|
+
return void 0;
|
|
1429
|
+
}
|
|
1430
|
+
const trimmed = stripNullBytes(value).trim();
|
|
1431
|
+
return trimmed ? trimmed.toLowerCase() : void 0;
|
|
1423
1432
|
}
|
|
1424
1433
|
function normalizeGitHubTokenRef(value) {
|
|
1425
1434
|
return normalizeSecretRef(value);
|
|
@@ -1983,8 +1992,8 @@ function doesImportedIssueMatchTarget(issue, target) {
|
|
|
1983
1992
|
}
|
|
1984
1993
|
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;
|
|
1985
1994
|
}
|
|
1986
|
-
async function resolvePaperclipIssueGitHubLink(ctx, issueId, companyId) {
|
|
1987
|
-
const linkRecords = await listGitHubIssueLinkRecords(ctx, {
|
|
1995
|
+
async function resolvePaperclipIssueGitHubLink(ctx, issueId, companyId, options = {}) {
|
|
1996
|
+
const linkRecords = options.linkRecords ?? await listGitHubIssueLinkRecords(ctx, {
|
|
1988
1997
|
paperclipIssueId: issueId
|
|
1989
1998
|
});
|
|
1990
1999
|
const entityMatch = linkRecords.find((record) => !record.data.companyId || record.data.companyId === companyId);
|
|
@@ -1997,7 +2006,8 @@ async function resolvePaperclipIssueGitHubLink(ctx, issueId, companyId) {
|
|
|
1997
2006
|
githubIssueId: entityMatch.data.githubIssueId,
|
|
1998
2007
|
githubIssueNumber: entityMatch.data.githubIssueNumber,
|
|
1999
2008
|
githubIssueUrl: entityMatch.data.githubIssueUrl,
|
|
2000
|
-
linkedPullRequestNumbers: entityMatch.data.linkedPullRequestNumbers
|
|
2009
|
+
linkedPullRequestNumbers: entityMatch.data.linkedPullRequestNumbers,
|
|
2010
|
+
entityRecord: entityMatch
|
|
2001
2011
|
};
|
|
2002
2012
|
}
|
|
2003
2013
|
const importRegistry = normalizeImportRegistry(await ctx.state.get(IMPORT_REGISTRY_SCOPE));
|
|
@@ -2010,7 +2020,7 @@ async function resolvePaperclipIssueGitHubLink(ctx, issueId, companyId) {
|
|
|
2010
2020
|
registryMatch.githubIssueNumber
|
|
2011
2021
|
);
|
|
2012
2022
|
if (githubIssueUrl2) {
|
|
2013
|
-
|
|
2023
|
+
const fallbackLink2 = {
|
|
2014
2024
|
source: "import_registry",
|
|
2015
2025
|
companyId: registryMatch.companyId,
|
|
2016
2026
|
paperclipProjectId: registryMatch.paperclipProjectId,
|
|
@@ -2020,15 +2030,16 @@ async function resolvePaperclipIssueGitHubLink(ctx, issueId, companyId) {
|
|
|
2020
2030
|
githubIssueUrl: githubIssueUrl2,
|
|
2021
2031
|
linkedPullRequestNumbers: []
|
|
2022
2032
|
};
|
|
2033
|
+
return await hydrateRecoveredPaperclipIssueGitHubLink(ctx, issueId, fallbackLink2) ?? fallbackLink2;
|
|
2023
2034
|
}
|
|
2024
2035
|
}
|
|
2025
|
-
const issue = await ctx.issues.get(issueId, companyId);
|
|
2036
|
+
const issue = options.paperclipIssue ?? await ctx.issues.get(issueId, companyId);
|
|
2026
2037
|
const githubIssueUrl = extractImportedGitHubIssueUrlFromDescription(issue?.description);
|
|
2027
2038
|
const githubIssueReference = githubIssueUrl ? parseGitHubIssueHtmlUrl(githubIssueUrl) : null;
|
|
2028
2039
|
if (!githubIssueReference) {
|
|
2029
2040
|
return null;
|
|
2030
2041
|
}
|
|
2031
|
-
|
|
2042
|
+
const fallbackLink = {
|
|
2032
2043
|
source: "description",
|
|
2033
2044
|
companyId,
|
|
2034
2045
|
paperclipProjectId: issue?.projectId ?? void 0,
|
|
@@ -2037,6 +2048,73 @@ async function resolvePaperclipIssueGitHubLink(ctx, issueId, companyId) {
|
|
|
2037
2048
|
githubIssueUrl: githubIssueReference.issueUrl,
|
|
2038
2049
|
linkedPullRequestNumbers: []
|
|
2039
2050
|
};
|
|
2051
|
+
return await hydrateRecoveredPaperclipIssueGitHubLink(ctx, issueId, fallbackLink) ?? fallbackLink;
|
|
2052
|
+
}
|
|
2053
|
+
async function hydrateRecoveredPaperclipIssueGitHubLink(ctx, issueId, fallbackLink) {
|
|
2054
|
+
const repository = parseRepositoryReference(fallbackLink.repositoryUrl);
|
|
2055
|
+
if (!repository) {
|
|
2056
|
+
return null;
|
|
2057
|
+
}
|
|
2058
|
+
let octokit;
|
|
2059
|
+
try {
|
|
2060
|
+
octokit = await createGitHubToolOctokit(ctx);
|
|
2061
|
+
} catch {
|
|
2062
|
+
return null;
|
|
2063
|
+
}
|
|
2064
|
+
try {
|
|
2065
|
+
const response = await octokit.rest.issues.get({
|
|
2066
|
+
owner: repository.owner,
|
|
2067
|
+
repo: repository.repo,
|
|
2068
|
+
issue_number: fallbackLink.githubIssueNumber,
|
|
2069
|
+
headers: {
|
|
2070
|
+
"X-GitHub-Api-Version": GITHUB_API_VERSION
|
|
2071
|
+
}
|
|
2072
|
+
});
|
|
2073
|
+
const githubIssue = normalizeGitHubIssueRecord(response.data);
|
|
2074
|
+
const linkedPullRequests = await listLinkedPullRequestsForIssue(octokit, repository, githubIssue.number);
|
|
2075
|
+
const linkedPullRequestNumbers = linkedPullRequests.map((pullRequest) => pullRequest.number);
|
|
2076
|
+
const entityRecord = buildGitHubIssueLinkRecord(
|
|
2077
|
+
{
|
|
2078
|
+
companyId: fallbackLink.companyId,
|
|
2079
|
+
paperclipProjectId: fallbackLink.paperclipProjectId,
|
|
2080
|
+
repositoryUrl: fallbackLink.repositoryUrl
|
|
2081
|
+
},
|
|
2082
|
+
issueId,
|
|
2083
|
+
githubIssue,
|
|
2084
|
+
linkedPullRequestNumbers
|
|
2085
|
+
);
|
|
2086
|
+
await upsertGitHubIssueLinkRecord(
|
|
2087
|
+
ctx,
|
|
2088
|
+
{
|
|
2089
|
+
companyId: fallbackLink.companyId,
|
|
2090
|
+
paperclipProjectId: fallbackLink.paperclipProjectId,
|
|
2091
|
+
repositoryUrl: fallbackLink.repositoryUrl
|
|
2092
|
+
},
|
|
2093
|
+
issueId,
|
|
2094
|
+
githubIssue,
|
|
2095
|
+
linkedPullRequestNumbers
|
|
2096
|
+
);
|
|
2097
|
+
return {
|
|
2098
|
+
source: "entity",
|
|
2099
|
+
companyId: fallbackLink.companyId,
|
|
2100
|
+
paperclipProjectId: fallbackLink.paperclipProjectId,
|
|
2101
|
+
repositoryUrl: fallbackLink.repositoryUrl,
|
|
2102
|
+
githubIssueId: githubIssue.id,
|
|
2103
|
+
githubIssueNumber: githubIssue.number,
|
|
2104
|
+
githubIssueUrl: normalizeGitHubIssueHtmlUrl(githubIssue.htmlUrl) ?? githubIssue.htmlUrl,
|
|
2105
|
+
linkedPullRequestNumbers,
|
|
2106
|
+
entityRecord
|
|
2107
|
+
};
|
|
2108
|
+
} catch (error) {
|
|
2109
|
+
ctx.logger.warn("Unable to hydrate recovered GitHub issue metadata for a Paperclip issue fallback link.", {
|
|
2110
|
+
issueId,
|
|
2111
|
+
companyId: fallbackLink.companyId,
|
|
2112
|
+
repositoryUrl: fallbackLink.repositoryUrl,
|
|
2113
|
+
githubIssueNumber: fallbackLink.githubIssueNumber,
|
|
2114
|
+
error: getErrorMessage(error)
|
|
2115
|
+
});
|
|
2116
|
+
return null;
|
|
2117
|
+
}
|
|
2040
2118
|
}
|
|
2041
2119
|
async function resolveManualSyncTarget(ctx, settings, input) {
|
|
2042
2120
|
if (input.issueId) {
|
|
@@ -2225,7 +2303,13 @@ async function buildIssueGitHubDetails(ctx, input) {
|
|
|
2225
2303
|
const linkRecords = await listGitHubIssueLinkRecords(ctx, {
|
|
2226
2304
|
paperclipIssueId: issueId
|
|
2227
2305
|
});
|
|
2228
|
-
const
|
|
2306
|
+
const link = await resolvePaperclipIssueGitHubLink(ctx, issueId, companyId, {
|
|
2307
|
+
linkRecords
|
|
2308
|
+
});
|
|
2309
|
+
if (!link) {
|
|
2310
|
+
return null;
|
|
2311
|
+
}
|
|
2312
|
+
const entityMatch = link.entityRecord;
|
|
2229
2313
|
if (entityMatch) {
|
|
2230
2314
|
return {
|
|
2231
2315
|
paperclipIssueId: issueId,
|
|
@@ -2241,17 +2325,13 @@ async function buildIssueGitHubDetails(ctx, input) {
|
|
|
2241
2325
|
syncedAt: entityMatch.data.syncedAt
|
|
2242
2326
|
};
|
|
2243
2327
|
}
|
|
2244
|
-
const fallbackLink = await resolvePaperclipIssueGitHubLink(ctx, issueId, companyId);
|
|
2245
|
-
if (!fallbackLink) {
|
|
2246
|
-
return null;
|
|
2247
|
-
}
|
|
2248
2328
|
return {
|
|
2249
2329
|
paperclipIssueId: issueId,
|
|
2250
|
-
source:
|
|
2251
|
-
githubIssueNumber:
|
|
2252
|
-
githubIssueUrl:
|
|
2253
|
-
repositoryUrl:
|
|
2254
|
-
linkedPullRequestNumbers:
|
|
2330
|
+
source: link.source,
|
|
2331
|
+
githubIssueNumber: link.githubIssueNumber,
|
|
2332
|
+
githubIssueUrl: link.githubIssueUrl,
|
|
2333
|
+
repositoryUrl: link.repositoryUrl,
|
|
2334
|
+
linkedPullRequestNumbers: link.linkedPullRequestNumbers
|
|
2255
2335
|
};
|
|
2256
2336
|
}
|
|
2257
2337
|
async function resolveIssueByIdentifier(ctx, input) {
|
|
@@ -2839,7 +2919,7 @@ function normalizeGitHubUsername(value) {
|
|
|
2839
2919
|
if (typeof value !== "string") {
|
|
2840
2920
|
return void 0;
|
|
2841
2921
|
}
|
|
2842
|
-
const trimmed = value.trim().replace(/^@+/, "");
|
|
2922
|
+
const trimmed = stripNullBytes(value).trim().replace(/^@+/, "");
|
|
2843
2923
|
return trimmed ? trimmed.toLowerCase() : void 0;
|
|
2844
2924
|
}
|
|
2845
2925
|
function buildGitHubUsernameAliases(value) {
|
|
@@ -3126,7 +3206,7 @@ function normalizeGitHubIssueLabels(value) {
|
|
|
3126
3206
|
const seen = /* @__PURE__ */ new Set();
|
|
3127
3207
|
const labels = [];
|
|
3128
3208
|
for (const entry of value) {
|
|
3129
|
-
const name = typeof entry === "string" ? entry.trim() : entry && typeof entry === "object" && typeof entry.name === "string" ? entry.name.trim() : "";
|
|
3209
|
+
const name = typeof entry === "string" ? stripNullBytes(entry).trim() : entry && typeof entry === "object" && typeof entry.name === "string" ? stripNullBytes(entry.name).trim() : "";
|
|
3130
3210
|
if (!name) {
|
|
3131
3211
|
continue;
|
|
3132
3212
|
}
|
|
@@ -3146,8 +3226,8 @@ function normalizeGitHubIssueRecord(issue) {
|
|
|
3146
3226
|
return {
|
|
3147
3227
|
id: issue.id,
|
|
3148
3228
|
number: issue.number,
|
|
3149
|
-
title: issue.title,
|
|
3150
|
-
body: issue.body
|
|
3229
|
+
title: stripNullBytes(issue.title),
|
|
3230
|
+
body: typeof issue.body === "string" ? stripNullBytes(issue.body) : null,
|
|
3151
3231
|
htmlUrl: issue.html_url,
|
|
3152
3232
|
...normalizeGitHubUsername(issue.user?.login) ? { authorLogin: normalizeGitHubUsername(issue.user?.login) } : {},
|
|
3153
3233
|
labels: normalizeGitHubIssueLabels(issue.labels),
|
|
@@ -3632,7 +3712,7 @@ function resolvePaperclipIssueStatus(params) {
|
|
|
3632
3712
|
return defaultImportedStatus;
|
|
3633
3713
|
}
|
|
3634
3714
|
if (currentStatus === "done" || currentStatus === "cancelled") {
|
|
3635
|
-
return "
|
|
3715
|
+
return "todo";
|
|
3636
3716
|
}
|
|
3637
3717
|
return currentStatus;
|
|
3638
3718
|
}
|
|
@@ -3978,7 +4058,7 @@ async function listNewGitHubIssueCommentsSinceCount(octokit, repository, issueNu
|
|
|
3978
4058
|
for (const comment of response.data.slice(remainingOffset)) {
|
|
3979
4059
|
comments.push({
|
|
3980
4060
|
id: comment.id,
|
|
3981
|
-
body: comment.body
|
|
4061
|
+
body: typeof comment.body === "string" ? stripNullBytes(comment.body) : "",
|
|
3982
4062
|
url: comment.html_url ?? void 0,
|
|
3983
4063
|
authorLogin: normalizeGitHubUserLogin(comment.user?.login),
|
|
3984
4064
|
createdAt: comment.created_at ?? void 0,
|
|
@@ -4061,6 +4141,15 @@ function parseGitHubIssueHtmlUrl(value) {
|
|
|
4061
4141
|
function normalizeGitHubIssueHtmlUrl(value) {
|
|
4062
4142
|
return parseGitHubIssueHtmlUrl(value)?.issueUrl;
|
|
4063
4143
|
}
|
|
4144
|
+
function escapeRegExp(value) {
|
|
4145
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
4146
|
+
}
|
|
4147
|
+
function getHiddenGitHubImportMarkerPattern() {
|
|
4148
|
+
return new RegExp(
|
|
4149
|
+
`${escapeRegExp(HIDDEN_GITHUB_IMPORT_MARKER_PREFIX)}(\\S+?)${escapeRegExp(HIDDEN_GITHUB_IMPORT_MARKER_SUFFIX)}`,
|
|
4150
|
+
"i"
|
|
4151
|
+
);
|
|
4152
|
+
}
|
|
4064
4153
|
function normalizeLinkedPullRequestNumbers(values) {
|
|
4065
4154
|
return [...new Set(
|
|
4066
4155
|
values.filter((pullRequestNumber) => Number.isInteger(pullRequestNumber) && pullRequestNumber > 0)
|
|
@@ -4070,6 +4159,10 @@ function extractImportedGitHubIssueUrlFromDescription(description) {
|
|
|
4070
4159
|
if (typeof description !== "string") {
|
|
4071
4160
|
return void 0;
|
|
4072
4161
|
}
|
|
4162
|
+
const hiddenMarkerMatch = description.match(getHiddenGitHubImportMarkerPattern());
|
|
4163
|
+
if (hiddenMarkerMatch) {
|
|
4164
|
+
return normalizeGitHubIssueHtmlUrl(hiddenMarkerMatch[1]);
|
|
4165
|
+
}
|
|
4073
4166
|
const markdownMetadataMatch = description.match(/^\*\s+GitHub issue:\s+\[[^\]]+\]\(([^)]+)\)/m);
|
|
4074
4167
|
if (markdownMetadataMatch) {
|
|
4075
4168
|
return normalizeGitHubIssueHtmlUrl(markdownMetadataMatch[1]);
|
|
@@ -4179,7 +4272,7 @@ function normalizeGitHubIssueBodyForPaperclip(body) {
|
|
|
4179
4272
|
if (typeof body !== "string") {
|
|
4180
4273
|
return void 0;
|
|
4181
4274
|
}
|
|
4182
|
-
const trimmed = body.trim();
|
|
4275
|
+
const trimmed = stripNullBytes(body).trim();
|
|
4183
4276
|
if (!trimmed) {
|
|
4184
4277
|
return void 0;
|
|
4185
4278
|
}
|
|
@@ -4212,8 +4305,27 @@ ${markdownImage}
|
|
|
4212
4305
|
}
|
|
4213
4306
|
function buildPaperclipIssueDescription(issue, linkedPullRequestNumbers = []) {
|
|
4214
4307
|
const normalizedBody = normalizeGitHubIssueBodyForPaperclip(issue.body);
|
|
4308
|
+
const hiddenImportMarker = buildHiddenGitHubImportMarker(issue.htmlUrl);
|
|
4215
4309
|
void linkedPullRequestNumbers;
|
|
4216
|
-
|
|
4310
|
+
if (!hiddenImportMarker) {
|
|
4311
|
+
return normalizedBody ?? "";
|
|
4312
|
+
}
|
|
4313
|
+
if (!normalizedBody) {
|
|
4314
|
+
return hiddenImportMarker;
|
|
4315
|
+
}
|
|
4316
|
+
return `${normalizedBody}
|
|
4317
|
+
|
|
4318
|
+
${hiddenImportMarker}`;
|
|
4319
|
+
}
|
|
4320
|
+
function buildHiddenGitHubImportMarker(githubIssueUrl) {
|
|
4321
|
+
if (typeof githubIssueUrl !== "string") {
|
|
4322
|
+
return void 0;
|
|
4323
|
+
}
|
|
4324
|
+
const normalizedIssueUrl = normalizeGitHubIssueHtmlUrl(githubIssueUrl);
|
|
4325
|
+
if (!normalizedIssueUrl) {
|
|
4326
|
+
return void 0;
|
|
4327
|
+
}
|
|
4328
|
+
return `${HIDDEN_GITHUB_IMPORT_MARKER_PREFIX}${normalizedIssueUrl}${HIDDEN_GITHUB_IMPORT_MARKER_SUFFIX}`;
|
|
4217
4329
|
}
|
|
4218
4330
|
function normalizeIssueDescriptionValue(value) {
|
|
4219
4331
|
return typeof value === "string" ? value : "";
|
|
@@ -4460,19 +4572,17 @@ async function findStoredStatusTransitionCommentAnnotation(ctx, params) {
|
|
|
4460
4572
|
}
|
|
4461
4573
|
return null;
|
|
4462
4574
|
}
|
|
4463
|
-
|
|
4575
|
+
function buildGitHubIssueLinkRecord(target, issueId, githubIssue, linkedPullRequestNumbers) {
|
|
4464
4576
|
const githubIssueUrl = normalizeGitHubIssueHtmlUrl(githubIssue.htmlUrl) ?? githubIssue.htmlUrl;
|
|
4465
|
-
|
|
4466
|
-
|
|
4467
|
-
|
|
4468
|
-
scopeId: issueId,
|
|
4469
|
-
externalId: githubIssueUrl,
|
|
4577
|
+
const repositoryUrl = parseRepositoryReference(target.repositoryUrl)?.url ?? target.repositoryUrl.trim();
|
|
4578
|
+
return {
|
|
4579
|
+
paperclipIssueId: issueId,
|
|
4470
4580
|
title: `GitHub issue #${githubIssue.number}`,
|
|
4471
4581
|
status: githubIssue.state,
|
|
4472
4582
|
data: {
|
|
4473
|
-
...
|
|
4474
|
-
...
|
|
4475
|
-
repositoryUrl
|
|
4583
|
+
...target.companyId ? { companyId: target.companyId } : {},
|
|
4584
|
+
...target.paperclipProjectId ? { paperclipProjectId: target.paperclipProjectId } : {},
|
|
4585
|
+
repositoryUrl,
|
|
4476
4586
|
githubIssueId: githubIssue.id,
|
|
4477
4587
|
githubIssueNumber: githubIssue.number,
|
|
4478
4588
|
githubIssueUrl,
|
|
@@ -4483,6 +4593,18 @@ async function upsertGitHubIssueLinkRecord(ctx, mapping, issueId, githubIssue, l
|
|
|
4483
4593
|
labels: githubIssue.labels,
|
|
4484
4594
|
syncedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
4485
4595
|
}
|
|
4596
|
+
};
|
|
4597
|
+
}
|
|
4598
|
+
async function upsertGitHubIssueLinkRecord(ctx, target, issueId, githubIssue, linkedPullRequestNumbers) {
|
|
4599
|
+
const record = buildGitHubIssueLinkRecord(target, issueId, githubIssue, linkedPullRequestNumbers);
|
|
4600
|
+
await ctx.entities.upsert({
|
|
4601
|
+
entityType: ISSUE_LINK_ENTITY_TYPE,
|
|
4602
|
+
scopeKind: "issue",
|
|
4603
|
+
scopeId: issueId,
|
|
4604
|
+
externalId: record.data.githubIssueUrl,
|
|
4605
|
+
...record.title ? { title: record.title } : {},
|
|
4606
|
+
...record.status ? { status: record.status } : {},
|
|
4607
|
+
data: record.data
|
|
4486
4608
|
});
|
|
4487
4609
|
}
|
|
4488
4610
|
async function upsertGitHubPullRequestLinkRecord(ctx, params) {
|
|
@@ -6405,7 +6527,7 @@ async function listAllGitHubIssueComments(octokit, repository, issueNumber) {
|
|
|
6405
6527
|
for (const comment of response.data) {
|
|
6406
6528
|
comments.push({
|
|
6407
6529
|
id: comment.id,
|
|
6408
|
-
body: comment.body
|
|
6530
|
+
body: typeof comment.body === "string" ? stripNullBytes(comment.body) : "",
|
|
6409
6531
|
url: comment.html_url ?? void 0,
|
|
6410
6532
|
authorLogin: normalizeGitHubUserLogin(comment.user?.login),
|
|
6411
6533
|
authorUrl: comment.user?.html_url ?? void 0,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "paperclip-github-plugin",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.4",
|
|
4
4
|
"description": "Paperclip plugin for synchronizing GitHub issues into Paperclip projects.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"@octokit/rest": "^22.0.1",
|
|
44
|
-
"@paperclipai/plugin-sdk": "^2026.
|
|
44
|
+
"@paperclipai/plugin-sdk": "^2026.416.0",
|
|
45
45
|
"react": "^19.2.5",
|
|
46
46
|
"react-markdown": "^10.1.0",
|
|
47
47
|
"rehype-raw": "^7.0.0",
|