paperclip-github-plugin 0.8.12 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -5
- package/dist/manifest.js +1 -1
- package/dist/worker.js +181 -87
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -44,7 +44,7 @@ The plugin adds a full in-host workflow instead of a one-off import script:
|
|
|
44
44
|
2. Connect one or more GitHub repositories to Paperclip projects.
|
|
45
45
|
3. Run a sync manually or let the scheduled job keep things up to date.
|
|
46
46
|
|
|
47
|
-
During sync, the plugin imports one top-level Paperclip issue per GitHub issue, stamps it with a namespaced GitHub Sync plugin origin, updates already imported issues instead of recreating them, maps GitHub labels into Paperclip labels, and keeps GitHub-specific metadata in dedicated Paperclip surfaces rather than stuffing everything into the issue description. On Paperclip `2026.
|
|
47
|
+
During sync, the plugin imports one top-level Paperclip issue per GitHub issue, stamps it with a namespaced GitHub Sync plugin origin, updates already imported issues instead of recreating them, maps GitHub labels into Paperclip labels, and keeps GitHub-specific metadata in dedicated Paperclip surfaces rather than stuffing everything into the issue description. On Paperclip `2026.512.0` and newer, the plugin passes the intended initial import status explicitly so Paperclip's assigned-issue creation defaults cannot override GitHub Sync's routing, and the detail surfaces can recover GitHub issue and pull request links from Paperclip's own `originKind` / `originId` fields when the plugin registry or legacy hidden marker is missing.
|
|
48
48
|
|
|
49
49
|
When the host exposes plugin issue creation, imported GitHub issues are created through the Paperclip plugin SDK path so they are not attributed to the connected board user. The worker still uses direct local Paperclip REST calls for label sync and for description, assignee, or status repair paths when those routes are available.
|
|
50
50
|
|
|
@@ -104,7 +104,7 @@ They can also link a Paperclip issue to a GitHub issue or pull request in any ac
|
|
|
104
104
|
## Requirements
|
|
105
105
|
|
|
106
106
|
- Node.js 20+
|
|
107
|
-
- a Paperclip host with plugin installation enabled. GitHub Sync is built and tested against Paperclip `2026.
|
|
107
|
+
- a Paperclip host with plugin installation enabled. GitHub Sync is built and tested against Paperclip `2026.512.0`; the manifest relies on explicit capabilities instead of a strict host-version gate because current latest/development hosts can report `0.0.0` during plugin upgrade.
|
|
108
108
|
- a GitHub token with API access to the repositories you want to sync
|
|
109
109
|
|
|
110
110
|
## Install from npm
|
|
@@ -219,7 +219,7 @@ Notes:
|
|
|
219
219
|
|
|
220
220
|
GitHub Sync uses direct worker-side Paperclip REST calls for host paths that are not fully covered by the plugin SDK, such as label reconciliation and some issue repair paths. By default, manual setup and sync actions use the current browser origin. If that origin is not reachable from the plugin worker, set **Worker Paperclip API URL** in GitHub Sync settings; the value is saved internally as plugin config `paperclipApiBaseUrl`.
|
|
221
221
|
|
|
222
|
-
For private LAN, Docker, Kubernetes, custom DNS, or self-signed-certificate deployments, set that field to a local route the plugin worker can reach, such as `http://localhost:3100`, and port-forward or route that address to the Paperclip API when needed. Do not rely on exporting a process environment variable named `PAPERCLIP_API_URL` to Paperclip;
|
|
222
|
+
For private LAN, Docker, Kubernetes, custom DNS, or self-signed-certificate deployments, set that field to a local route the plugin worker can reach, such as `http://localhost:3100`, and port-forward or route that address to the Paperclip API when needed. Do not rely on exporting a process environment variable named `PAPERCLIP_API_URL` to Paperclip; release-target verification keeps this as explicit plugin configuration because worker runtime environments do not guarantee that process variable.
|
|
223
223
|
|
|
224
224
|
## GitHub agent tools
|
|
225
225
|
|
|
@@ -354,8 +354,8 @@ Useful scripts:
|
|
|
354
354
|
|
|
355
355
|
- `pnpm dev` watches the manifest, worker, and UI bundles and rebuilds them into `dist/`
|
|
356
356
|
- `pnpm dev:ui` starts a local Paperclip plugin UI dev server from `dist/ui` on port `4177`
|
|
357
|
-
- `pnpm test:e2e` builds the plugin, boots an isolated Paperclip `2026.
|
|
358
|
-
- `pnpm verify:manual` builds the plugin, boots a local-trusted Paperclip `2026.
|
|
357
|
+
- `pnpm test:e2e` builds the plugin, boots an isolated Paperclip `2026.512.0` instance, installs the plugin, and verifies the hosted settings page renders
|
|
358
|
+
- `pnpm verify:manual` builds the plugin, boots a local-trusted Paperclip `2026.512.0` instance for manual inspection, seeds a `Dummy Company` with a mapped review project and a `CEO` agent on the Codex local adapter using model `gpt-5.4`, installs the plugin, and opens the company dashboard without seeding KPI history.
|
|
359
359
|
|
|
360
360
|
For fast hosted UI iteration, run `pnpm dev` in one terminal and `pnpm dev:ui` in another.
|
|
361
361
|
|
package/dist/manifest.js
CHANGED
|
@@ -642,7 +642,7 @@ var PULL_REQUEST_ASSET_API_ROUTE_URL_PATH = `/api/plugins/${GITHUB_SYNC_PLUGIN_I
|
|
|
642
642
|
var require2 = createRequire(import.meta.url);
|
|
643
643
|
var packageJson = require2("../package.json");
|
|
644
644
|
var SCHEDULE_TICK_CRON = "* * * * *";
|
|
645
|
-
var MANIFEST_VERSION = "0.
|
|
645
|
+
var MANIFEST_VERSION = "0.9.0"?.trim() || typeof packageJson.version === "string" && packageJson.version.trim() || process.env.npm_package_version?.trim() || "0.0.0-dev";
|
|
646
646
|
var manifest = {
|
|
647
647
|
id: GITHUB_SYNC_PLUGIN_ID,
|
|
648
648
|
apiVersion: 1,
|
package/dist/worker.js
CHANGED
|
@@ -5681,9 +5681,19 @@ function buildPaperclipIssueStatusTransitionComment(params) {
|
|
|
5681
5681
|
};
|
|
5682
5682
|
}
|
|
5683
5683
|
function buildPaperclipPullRequestIssueStatusTransitionComment(params) {
|
|
5684
|
-
const reason =
|
|
5684
|
+
const reason = describeGitHubDirectPullRequestIssueStatusReason(params.pullRequests);
|
|
5685
5685
|
return `GitHub Sync updated the status from \`${formatPaperclipIssueStatus(params.previousStatus)}\` to \`${formatPaperclipIssueStatus(params.nextStatus)}\` because ${reason}.`;
|
|
5686
5686
|
}
|
|
5687
|
+
function describeGitHubDirectPullRequestIssueStatusReason(pullRequests) {
|
|
5688
|
+
const openPullRequests = pullRequests.map((entry) => entry.pullRequest).filter((pullRequest) => Boolean(pullRequest));
|
|
5689
|
+
if (openPullRequests.length > 0) {
|
|
5690
|
+
return describeGitHubLinkedPullRequestsStatusReason(openPullRequests);
|
|
5691
|
+
}
|
|
5692
|
+
if (pullRequests.some((entry) => entry.lifecycleState === "merged")) {
|
|
5693
|
+
return pullRequests.length === 1 ? "the linked pull request was merged" : "at least one linked pull request was merged";
|
|
5694
|
+
}
|
|
5695
|
+
return pullRequests.length === 1 ? "the linked pull request was closed without merge" : "all linked pull requests were closed without merge";
|
|
5696
|
+
}
|
|
5687
5697
|
function resolvePaperclipIssueStatus(params) {
|
|
5688
5698
|
const {
|
|
5689
5699
|
currentStatus,
|
|
@@ -5736,6 +5746,23 @@ function resolvePaperclipPullRequestIssueStatus(params) {
|
|
|
5736
5746
|
preserveTransientUnknownMergeabilityWait: currentStatus === "done" || currentStatus === "in_review"
|
|
5737
5747
|
});
|
|
5738
5748
|
}
|
|
5749
|
+
function resolvePaperclipDirectPullRequestIssueStatus(params) {
|
|
5750
|
+
const { currentStatus, pullRequests, hasExecutorHandoffTarget } = params;
|
|
5751
|
+
const openPullRequests = pullRequests.map((entry) => entry.pullRequest).filter((pullRequest) => Boolean(pullRequest));
|
|
5752
|
+
if (openPullRequests.length > 0) {
|
|
5753
|
+
if (shouldPreserveBlockedExternalPullRequestWait({
|
|
5754
|
+
currentStatus,
|
|
5755
|
+
linkedPullRequests: openPullRequests
|
|
5756
|
+
})) {
|
|
5757
|
+
return "blocked";
|
|
5758
|
+
}
|
|
5759
|
+
return resolvePaperclipStatusFromLinkedPullRequests(openPullRequests, {
|
|
5760
|
+
preferInProgress: hasExecutorHandoffTarget,
|
|
5761
|
+
preserveTransientUnknownMergeabilityWait: currentStatus === "done" || currentStatus === "in_review"
|
|
5762
|
+
});
|
|
5763
|
+
}
|
|
5764
|
+
return pullRequests.some((entry) => entry.lifecycleState === "merged") ? "done" : "cancelled";
|
|
5765
|
+
}
|
|
5739
5766
|
async function listLinkedPullRequestsForIssue(octokit, repository, issueNumber) {
|
|
5740
5767
|
const linkedPullRequests = [];
|
|
5741
5768
|
const seenPullRequestKeys = /* @__PURE__ */ new Set();
|
|
@@ -8975,6 +9002,16 @@ async function assignImportedPaperclipIssueToUser(ctx, params) {
|
|
|
8975
9002
|
});
|
|
8976
9003
|
}
|
|
8977
9004
|
}
|
|
9005
|
+
async function ensurePaperclipIssueStandardWorkMode(ctx, issue, companyId) {
|
|
9006
|
+
if (!("workMode" in issue) || issue.workMode === "standard") {
|
|
9007
|
+
return issue;
|
|
9008
|
+
}
|
|
9009
|
+
return ctx.issues.update(
|
|
9010
|
+
issue.id,
|
|
9011
|
+
{ workMode: "standard" },
|
|
9012
|
+
companyId
|
|
9013
|
+
);
|
|
9014
|
+
}
|
|
8978
9015
|
async function createPaperclipIssue(ctx, mapping, advancedSettings, issue, availableLabels, paperclipApiBaseUrl, syncFailureContext) {
|
|
8979
9016
|
if (!mapping.companyId || !mapping.paperclipProjectId) {
|
|
8980
9017
|
throw new Error(`Mapping ${mapping.id} is missing resolved Paperclip project identifiers.`);
|
|
@@ -8982,15 +9019,20 @@ async function createPaperclipIssue(ctx, mapping, advancedSettings, issue, avail
|
|
|
8982
9019
|
const title = issue.title;
|
|
8983
9020
|
const description = buildPaperclipIssueDescription(issue);
|
|
8984
9021
|
const defaultAssignee = getConfiguredAdvancedAssigneePrincipal(advancedSettings, "default");
|
|
8985
|
-
const createdIssue = await
|
|
8986
|
-
|
|
8987
|
-
|
|
8988
|
-
|
|
8989
|
-
|
|
8990
|
-
|
|
8991
|
-
|
|
8992
|
-
|
|
8993
|
-
|
|
9022
|
+
const createdIssue = await ensurePaperclipIssueStandardWorkMode(
|
|
9023
|
+
ctx,
|
|
9024
|
+
await ctx.issues.create({
|
|
9025
|
+
companyId: mapping.companyId,
|
|
9026
|
+
projectId: mapping.paperclipProjectId,
|
|
9027
|
+
title,
|
|
9028
|
+
...description ? { description } : {},
|
|
9029
|
+
status: advancedSettings.defaultStatus,
|
|
9030
|
+
originKind: GITHUB_ISSUE_ORIGIN_KIND,
|
|
9031
|
+
originId: normalizeGitHubIssueHtmlUrl(issue.htmlUrl) ?? issue.htmlUrl,
|
|
9032
|
+
...defaultAssignee?.kind === "agent" ? { assigneeAgentId: defaultAssignee.id } : defaultAssignee?.kind === "user" ? { assigneeUserId: defaultAssignee.id } : {}
|
|
9033
|
+
}),
|
|
9034
|
+
mapping.companyId
|
|
9035
|
+
);
|
|
8994
9036
|
const ensuredCreatedIssueId = createdIssue.id;
|
|
8995
9037
|
const normalizedCreatedIssueDescription = createdIssue.description ?? void 0;
|
|
8996
9038
|
const createPath = "sdk";
|
|
@@ -9708,6 +9750,18 @@ async function synchronizePaperclipIssueStatuses(ctx, octokit, repository, mappi
|
|
|
9708
9750
|
updatedDescriptionsCount
|
|
9709
9751
|
};
|
|
9710
9752
|
}
|
|
9753
|
+
function groupGitHubPullRequestLinksByPaperclipIssue(pullRequestLinks) {
|
|
9754
|
+
const groups = /* @__PURE__ */ new Map();
|
|
9755
|
+
const sortedLinks = [...pullRequestLinks].sort(
|
|
9756
|
+
(left, right) => left.paperclipIssueId.localeCompare(right.paperclipIssueId) || left.data.repositoryUrl.localeCompare(right.data.repositoryUrl) || left.data.githubPullRequestNumber - right.data.githubPullRequestNumber
|
|
9757
|
+
);
|
|
9758
|
+
for (const pullRequestLink of sortedLinks) {
|
|
9759
|
+
const group = groups.get(pullRequestLink.paperclipIssueId) ?? [];
|
|
9760
|
+
group.push(pullRequestLink);
|
|
9761
|
+
groups.set(pullRequestLink.paperclipIssueId, group);
|
|
9762
|
+
}
|
|
9763
|
+
return groups;
|
|
9764
|
+
}
|
|
9711
9765
|
async function synchronizePaperclipPullRequestIssueStatuses(ctx, octokit, mapping, advancedSettings, pullRequestLinks, paperclipApiBaseUrl, pullRequestStatusCache, syncFailureContext, failures, assertNotCancelled, onProgress) {
|
|
9712
9766
|
if (!mapping.companyId || !ctx.issues || typeof ctx.issues.get !== "function" || typeof ctx.issues.update !== "function") {
|
|
9713
9767
|
return {
|
|
@@ -9719,52 +9773,93 @@ async function synchronizePaperclipPullRequestIssueStatuses(ctx, octokit, mappin
|
|
|
9719
9773
|
const mappingCompanyId = mapping.companyId;
|
|
9720
9774
|
const mappingProjectId = mapping.paperclipProjectId;
|
|
9721
9775
|
const totalIssueCount = pullRequestLinks.length;
|
|
9776
|
+
const pullRequestLinkGroups = groupGitHubPullRequestLinksByPaperclipIssue(pullRequestLinks);
|
|
9722
9777
|
const queuedIssueWakeups = [];
|
|
9723
|
-
for (const
|
|
9724
|
-
|
|
9725
|
-
|
|
9726
|
-
|
|
9727
|
-
|
|
9728
|
-
|
|
9729
|
-
|
|
9730
|
-
|
|
9731
|
-
|
|
9732
|
-
|
|
9733
|
-
|
|
9734
|
-
const pullRequestResponse = await octokit.rest.pulls.get({
|
|
9735
|
-
owner: pullRequestRepository.owner,
|
|
9736
|
-
repo: pullRequestRepository.repo,
|
|
9737
|
-
pull_number: pullRequestLink.data.githubPullRequestNumber,
|
|
9738
|
-
headers: {
|
|
9739
|
-
"X-GitHub-Api-Version": GITHUB_API_VERSION
|
|
9740
|
-
}
|
|
9741
|
-
});
|
|
9742
|
-
const livePullRequestState = getPullRequestApiState({
|
|
9743
|
-
state: pullRequestResponse.data.state,
|
|
9744
|
-
merged: pullRequestResponse.data.merged
|
|
9745
|
-
}) === "open" ? "open" : "closed";
|
|
9746
|
-
if (livePullRequestState !== pullRequestLink.data.githubPullRequestState || pullRequestResponse.data.html_url !== pullRequestLink.data.githubPullRequestUrl || pullRequestResponse.data.title !== pullRequestLink.data.title) {
|
|
9747
|
-
await upsertGitHubPullRequestLinkRecord(ctx, {
|
|
9748
|
-
companyId: pullRequestLink.data.companyId ?? mappingCompanyId,
|
|
9749
|
-
projectId: pullRequestLink.data.paperclipProjectId ?? mappingProjectId,
|
|
9750
|
-
issueId: pullRequestLink.paperclipIssueId,
|
|
9778
|
+
for (const [paperclipIssueId, issuePullRequestLinks] of pullRequestLinkGroups.entries()) {
|
|
9779
|
+
const pullRequestSnapshots = [];
|
|
9780
|
+
let groupHadFailure = false;
|
|
9781
|
+
for (const pullRequestLink of issuePullRequestLinks) {
|
|
9782
|
+
if (assertNotCancelled) {
|
|
9783
|
+
await assertNotCancelled();
|
|
9784
|
+
}
|
|
9785
|
+
try {
|
|
9786
|
+
const pullRequestRepository = requireRepositoryReference(pullRequestLink.data.repositoryUrl);
|
|
9787
|
+
updateSyncFailureContext(syncFailureContext, {
|
|
9788
|
+
phase: "evaluating_github_status",
|
|
9751
9789
|
repositoryUrl: pullRequestRepository.url,
|
|
9752
|
-
|
|
9753
|
-
pullRequestUrl: pullRequestResponse.data.html_url ?? pullRequestLink.data.githubPullRequestUrl,
|
|
9754
|
-
pullRequestTitle: pullRequestResponse.data.title || pullRequestLink.data.title || `Pull request #${pullRequestLink.data.githubPullRequestNumber}`,
|
|
9755
|
-
pullRequestState: livePullRequestState
|
|
9790
|
+
githubIssueNumber: void 0
|
|
9756
9791
|
});
|
|
9792
|
+
const pullRequestResponse = await octokit.rest.pulls.get({
|
|
9793
|
+
owner: pullRequestRepository.owner,
|
|
9794
|
+
repo: pullRequestRepository.repo,
|
|
9795
|
+
pull_number: pullRequestLink.data.githubPullRequestNumber,
|
|
9796
|
+
headers: {
|
|
9797
|
+
"X-GitHub-Api-Version": GITHUB_API_VERSION
|
|
9798
|
+
}
|
|
9799
|
+
});
|
|
9800
|
+
const livePullRequestLifecycleState = getPullRequestApiState({
|
|
9801
|
+
state: pullRequestResponse.data.state,
|
|
9802
|
+
merged: pullRequestResponse.data.merged
|
|
9803
|
+
});
|
|
9804
|
+
const livePullRequestLinkState = livePullRequestLifecycleState === "open" ? "open" : "closed";
|
|
9805
|
+
if (livePullRequestLinkState !== pullRequestLink.data.githubPullRequestState || pullRequestResponse.data.html_url !== pullRequestLink.data.githubPullRequestUrl || pullRequestResponse.data.title !== pullRequestLink.data.title) {
|
|
9806
|
+
await upsertGitHubPullRequestLinkRecord(ctx, {
|
|
9807
|
+
companyId: pullRequestLink.data.companyId ?? mappingCompanyId,
|
|
9808
|
+
projectId: pullRequestLink.data.paperclipProjectId ?? mappingProjectId,
|
|
9809
|
+
issueId: pullRequestLink.paperclipIssueId,
|
|
9810
|
+
repositoryUrl: pullRequestRepository.url,
|
|
9811
|
+
pullRequestNumber: pullRequestLink.data.githubPullRequestNumber,
|
|
9812
|
+
pullRequestUrl: pullRequestResponse.data.html_url ?? pullRequestLink.data.githubPullRequestUrl,
|
|
9813
|
+
pullRequestTitle: pullRequestResponse.data.title || pullRequestLink.data.title || `Pull request #${pullRequestLink.data.githubPullRequestNumber}`,
|
|
9814
|
+
pullRequestState: livePullRequestLinkState
|
|
9815
|
+
});
|
|
9816
|
+
}
|
|
9817
|
+
if (livePullRequestLifecycleState === "open") {
|
|
9818
|
+
const pullRequest = await getGitHubPullRequestStatusSnapshot(
|
|
9819
|
+
octokit,
|
|
9820
|
+
pullRequestRepository,
|
|
9821
|
+
pullRequestLink.data.githubPullRequestNumber,
|
|
9822
|
+
pullRequestStatusCache
|
|
9823
|
+
);
|
|
9824
|
+
pullRequestSnapshots.push({
|
|
9825
|
+
pullRequestLink,
|
|
9826
|
+
repository: pullRequestRepository,
|
|
9827
|
+
lifecycleState: "open",
|
|
9828
|
+
pullRequest
|
|
9829
|
+
});
|
|
9830
|
+
} else {
|
|
9831
|
+
pullRequestSnapshots.push({
|
|
9832
|
+
pullRequestLink,
|
|
9833
|
+
repository: pullRequestRepository,
|
|
9834
|
+
lifecycleState: livePullRequestLifecycleState
|
|
9835
|
+
});
|
|
9836
|
+
}
|
|
9837
|
+
} catch (error) {
|
|
9838
|
+
if (isGitHubRateLimitError(error)) {
|
|
9839
|
+
throw error;
|
|
9840
|
+
}
|
|
9841
|
+
groupHadFailure = true;
|
|
9842
|
+
recordRecoverableSyncFailure(ctx, failures, error, syncFailureContext);
|
|
9843
|
+
} finally {
|
|
9844
|
+
completedIssueCount += 1;
|
|
9845
|
+
if (onProgress) {
|
|
9846
|
+
await onProgress({
|
|
9847
|
+
pullRequestLink,
|
|
9848
|
+
completedIssueCount,
|
|
9849
|
+
totalIssueCount
|
|
9850
|
+
});
|
|
9851
|
+
}
|
|
9757
9852
|
}
|
|
9758
|
-
|
|
9759
|
-
|
|
9853
|
+
}
|
|
9854
|
+
if (groupHadFailure || pullRequestSnapshots.length === 0) {
|
|
9855
|
+
continue;
|
|
9856
|
+
}
|
|
9857
|
+
const primaryRepository = pullRequestSnapshots[0]?.repository;
|
|
9858
|
+
try {
|
|
9859
|
+
if (assertNotCancelled) {
|
|
9860
|
+
await assertNotCancelled();
|
|
9760
9861
|
}
|
|
9761
|
-
const
|
|
9762
|
-
octokit,
|
|
9763
|
-
pullRequestRepository,
|
|
9764
|
-
pullRequestLink.data.githubPullRequestNumber,
|
|
9765
|
-
pullRequestStatusCache
|
|
9766
|
-
);
|
|
9767
|
-
const paperclipIssue = await ctx.issues.get(pullRequestLink.paperclipIssueId, mapping.companyId);
|
|
9862
|
+
const paperclipIssue = await ctx.issues.get(paperclipIssueId, mapping.companyId);
|
|
9768
9863
|
if (!paperclipIssue) {
|
|
9769
9864
|
continue;
|
|
9770
9865
|
}
|
|
@@ -9773,12 +9868,12 @@ async function synchronizePaperclipPullRequestIssueStatuses(ctx, octokit, mappin
|
|
|
9773
9868
|
paperclipIssueSyncContext,
|
|
9774
9869
|
advancedSettings
|
|
9775
9870
|
);
|
|
9776
|
-
let nextStatus =
|
|
9871
|
+
let nextStatus = resolvePaperclipDirectPullRequestIssueStatus({
|
|
9777
9872
|
currentStatus: paperclipIssue.status,
|
|
9778
|
-
|
|
9873
|
+
pullRequests: pullRequestSnapshots,
|
|
9779
9874
|
hasExecutorHandoffTarget: Boolean(executorTransitionAssignee)
|
|
9780
9875
|
});
|
|
9781
|
-
if (paperclipIssue.status === "blocked" && nextStatus !== "blocked" && await hasUnresolvedPaperclipIssueBlocker(ctx, paperclipIssue, mapping.companyId)) {
|
|
9876
|
+
if (paperclipIssue.status === "blocked" && nextStatus !== "blocked" && pullRequestSnapshots.some((entry) => entry.lifecycleState === "open") && await hasUnresolvedPaperclipIssueBlocker(ctx, paperclipIssue, mapping.companyId)) {
|
|
9782
9877
|
nextStatus = "blocked";
|
|
9783
9878
|
}
|
|
9784
9879
|
const shouldPreserveMaintainerWaitRouting = isHealthyMaintainerWaitTransition({
|
|
@@ -9786,6 +9881,10 @@ async function synchronizePaperclipPullRequestIssueStatuses(ctx, octokit, mappin
|
|
|
9786
9881
|
nextStatus,
|
|
9787
9882
|
syncContext: paperclipIssueSyncContext
|
|
9788
9883
|
});
|
|
9884
|
+
const shouldClearCompletedExecutionPolicy = shouldClearCompletedSyncExecutionPolicy({
|
|
9885
|
+
nextStatus,
|
|
9886
|
+
syncContext: paperclipIssueSyncContext
|
|
9887
|
+
});
|
|
9789
9888
|
const nextTransitionAssignee = resolveSyncTransitionAssignee({
|
|
9790
9889
|
currentStatus: paperclipIssue.status,
|
|
9791
9890
|
nextStatus,
|
|
@@ -9796,20 +9895,20 @@ async function synchronizePaperclipPullRequestIssueStatuses(ctx, octokit, mappin
|
|
|
9796
9895
|
const nextAssigneeChanged = nextTransitionAssignee ? !doesPaperclipIssueAssigneeMatch(paperclipIssueSyncContext.assignee, nextTransitionAssignee.principal) : false;
|
|
9797
9896
|
const shouldWakeTransitionAssignee = paperclipIssue.status !== nextStatus && nextTransitionAssignee?.principal.kind === "agent" && isActionablePaperclipIssueStatus(nextStatus) && (nextAssigneeChanged || paperclipIssue.status !== nextStatus);
|
|
9798
9897
|
if (paperclipIssue.status === nextStatus) {
|
|
9799
|
-
if (shouldClearTransitionAssignee) {
|
|
9898
|
+
if (shouldClearTransitionAssignee || shouldClearCompletedExecutionPolicy) {
|
|
9800
9899
|
updateSyncFailureContext(syncFailureContext, {
|
|
9801
9900
|
phase: "updating_paperclip_status",
|
|
9802
|
-
repositoryUrl:
|
|
9901
|
+
repositoryUrl: primaryRepository?.url,
|
|
9803
9902
|
githubIssueNumber: void 0
|
|
9804
9903
|
});
|
|
9805
9904
|
await updatePaperclipIssueState(ctx, {
|
|
9806
9905
|
companyId: mapping.companyId,
|
|
9807
|
-
issueId:
|
|
9906
|
+
issueId: paperclipIssueId,
|
|
9808
9907
|
currentStatus: paperclipIssue.status,
|
|
9809
9908
|
syncContext: paperclipIssueSyncContext,
|
|
9810
9909
|
nextStatus,
|
|
9811
|
-
clearAssignee: true,
|
|
9812
|
-
...shouldPreserveMaintainerWaitRouting ? { clearExecutionPolicy: true } : {},
|
|
9910
|
+
...shouldClearTransitionAssignee ? { clearAssignee: true } : {},
|
|
9911
|
+
...shouldPreserveMaintainerWaitRouting || shouldClearCompletedExecutionPolicy ? { clearExecutionPolicy: true } : {},
|
|
9813
9912
|
transitionComment: "",
|
|
9814
9913
|
paperclipApiBaseUrl
|
|
9815
9914
|
});
|
|
@@ -9819,22 +9918,22 @@ async function synchronizePaperclipPullRequestIssueStatuses(ctx, octokit, mappin
|
|
|
9819
9918
|
const transitionComment = buildPaperclipPullRequestIssueStatusTransitionComment({
|
|
9820
9919
|
previousStatus: paperclipIssue.status,
|
|
9821
9920
|
nextStatus,
|
|
9822
|
-
|
|
9921
|
+
pullRequests: pullRequestSnapshots
|
|
9823
9922
|
});
|
|
9824
9923
|
updateSyncFailureContext(syncFailureContext, {
|
|
9825
9924
|
phase: "updating_paperclip_status",
|
|
9826
|
-
repositoryUrl:
|
|
9925
|
+
repositoryUrl: primaryRepository?.url,
|
|
9827
9926
|
githubIssueNumber: void 0
|
|
9828
9927
|
});
|
|
9829
9928
|
await updatePaperclipIssueState(ctx, {
|
|
9830
9929
|
companyId: mapping.companyId,
|
|
9831
|
-
issueId:
|
|
9930
|
+
issueId: paperclipIssueId,
|
|
9832
9931
|
currentStatus: paperclipIssue.status,
|
|
9833
9932
|
syncContext: paperclipIssueSyncContext,
|
|
9834
9933
|
nextStatus,
|
|
9835
9934
|
...nextTransitionAssignee ? { nextAssignee: nextTransitionAssignee.principal } : {},
|
|
9836
9935
|
...shouldClearTransitionAssignee ? { clearAssignee: true } : {},
|
|
9837
|
-
...shouldPreserveMaintainerWaitRouting ? { clearExecutionPolicy: true } : {},
|
|
9936
|
+
...shouldPreserveMaintainerWaitRouting || shouldClearCompletedExecutionPolicy ? { clearExecutionPolicy: true } : {},
|
|
9838
9937
|
transitionComment,
|
|
9839
9938
|
paperclipApiBaseUrl
|
|
9840
9939
|
});
|
|
@@ -9842,7 +9941,7 @@ async function synchronizePaperclipPullRequestIssueStatuses(ctx, octokit, mappin
|
|
|
9842
9941
|
if (shouldWakeTransitionAssignee && nextTransitionAssignee?.principal.kind === "agent") {
|
|
9843
9942
|
queuedIssueWakeups.push({
|
|
9844
9943
|
assigneeAgentId: nextTransitionAssignee.principal.id,
|
|
9845
|
-
paperclipIssueId
|
|
9944
|
+
paperclipIssueId,
|
|
9846
9945
|
reason: STATUS_TRANSITION_WAKE_REASON,
|
|
9847
9946
|
mutation: "status_transition",
|
|
9848
9947
|
previousStatus: paperclipIssue.status,
|
|
@@ -9854,16 +9953,6 @@ async function synchronizePaperclipPullRequestIssueStatuses(ctx, octokit, mappin
|
|
|
9854
9953
|
throw error;
|
|
9855
9954
|
}
|
|
9856
9955
|
recordRecoverableSyncFailure(ctx, failures, error, syncFailureContext);
|
|
9857
|
-
continue;
|
|
9858
|
-
} finally {
|
|
9859
|
-
completedIssueCount += 1;
|
|
9860
|
-
if (onProgress) {
|
|
9861
|
-
await onProgress({
|
|
9862
|
-
pullRequestLink,
|
|
9863
|
-
completedIssueCount,
|
|
9864
|
-
totalIssueCount
|
|
9865
|
-
});
|
|
9866
|
-
}
|
|
9867
9956
|
}
|
|
9868
9957
|
}
|
|
9869
9958
|
await mapWithConcurrency(
|
|
@@ -12888,19 +12977,24 @@ async function createProjectPullRequestPaperclipIssue(ctx, input) {
|
|
|
12888
12977
|
};
|
|
12889
12978
|
}
|
|
12890
12979
|
const requestedTitle = typeof input.title === "string" && input.title.trim() ? input.title.trim() : pullRequest.title.trim();
|
|
12891
|
-
const createdIssue = await
|
|
12892
|
-
|
|
12893
|
-
|
|
12894
|
-
|
|
12895
|
-
|
|
12896
|
-
|
|
12897
|
-
|
|
12898
|
-
|
|
12899
|
-
|
|
12900
|
-
|
|
12901
|
-
|
|
12902
|
-
|
|
12903
|
-
|
|
12980
|
+
const createdIssue = await ensurePaperclipIssueStandardWorkMode(
|
|
12981
|
+
ctx,
|
|
12982
|
+
await ctx.issues.create({
|
|
12983
|
+
companyId: scope.companyId,
|
|
12984
|
+
projectId: scope.projectId,
|
|
12985
|
+
title: requestedTitle,
|
|
12986
|
+
status: "todo",
|
|
12987
|
+
originKind: GITHUB_PULL_REQUEST_ORIGIN_KIND,
|
|
12988
|
+
originId: pullRequestUrl,
|
|
12989
|
+
description: buildPaperclipIssueDescriptionFromPullRequest({
|
|
12990
|
+
repository: scope.repository,
|
|
12991
|
+
pullRequestNumber,
|
|
12992
|
+
pullRequestUrl,
|
|
12993
|
+
body: pullRequest.body
|
|
12994
|
+
})
|
|
12995
|
+
}),
|
|
12996
|
+
scope.companyId
|
|
12997
|
+
);
|
|
12904
12998
|
const resolvedIssue = await ctx.issues.get(createdIssue.id, scope.companyId) ?? createdIssue;
|
|
12905
12999
|
await upsertGitHubPullRequestLinkRecord(ctx, {
|
|
12906
13000
|
companyId: scope.companyId,
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "paperclip-github-plugin",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Paperclip plugin for synchronizing GitHub issues into Paperclip projects.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
7
|
-
"packageManager": "pnpm@
|
|
7
|
+
"packageManager": "pnpm@11.0.9",
|
|
8
8
|
"engines": {
|
|
9
9
|
"node": ">=20"
|
|
10
10
|
},
|
|
@@ -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.512.0",
|
|
45
45
|
"react": "^19.2.6",
|
|
46
46
|
"react-markdown": "^10.1.0",
|
|
47
47
|
"rehype-raw": "^7.0.0",
|