paperclip-github-plugin 0.7.5 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/worker.js CHANGED
@@ -223,6 +223,21 @@ var GITHUB_AGENT_TOOLS = [
223
223
  }
224
224
  }
225
225
  },
226
+ {
227
+ name: "assign_to_current_user",
228
+ displayName: "Assign To Current User",
229
+ description: "Assign a GitHub issue to the GitHub user authenticated by the saved token, preserving any existing assignees.",
230
+ parametersSchema: {
231
+ type: "object",
232
+ additionalProperties: false,
233
+ ...issueTargetSchema,
234
+ properties: {
235
+ repository: repositoryProperty,
236
+ issueNumber: issueNumberProperty,
237
+ paperclipIssueId: paperclipIssueIdProperty
238
+ }
239
+ }
240
+ },
226
241
  {
227
242
  name: "add_issue_comment",
228
243
  displayName: "Add Issue Comment",
@@ -2243,6 +2258,40 @@ function doesImportedIssueMatchTarget(issue, target) {
2243
2258
  }
2244
2259
  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;
2245
2260
  }
2261
+ function resolvePaperclipIssueOriginGitHubIssueLink(issue, companyId) {
2262
+ if (issue?.originKind !== GITHUB_ISSUE_ORIGIN_KIND || typeof issue.originId !== "string") {
2263
+ return null;
2264
+ }
2265
+ const reference = parseGitHubIssueHtmlUrl(issue.originId);
2266
+ if (!reference) {
2267
+ return null;
2268
+ }
2269
+ return {
2270
+ source: "issue_origin",
2271
+ companyId,
2272
+ paperclipProjectId: issue.projectId ?? void 0,
2273
+ repositoryUrl: reference.repositoryUrl,
2274
+ githubIssueNumber: reference.issueNumber,
2275
+ githubIssueUrl: reference.issueUrl,
2276
+ linkedPullRequestNumbers: [],
2277
+ linkedPullRequests: []
2278
+ };
2279
+ }
2280
+ function resolvePaperclipIssueOriginGitHubPullRequestLink(issue) {
2281
+ if (issue?.originKind !== GITHUB_PULL_REQUEST_ORIGIN_KIND || typeof issue.originId !== "string") {
2282
+ return null;
2283
+ }
2284
+ const reference = parseGitHubPullRequestHtmlUrl(issue.originId);
2285
+ if (!reference) {
2286
+ return null;
2287
+ }
2288
+ return {
2289
+ source: "issue_origin",
2290
+ repositoryUrl: reference.repositoryUrl,
2291
+ githubPullRequestNumber: reference.pullRequestNumber,
2292
+ githubPullRequestUrl: reference.pullRequestUrl
2293
+ };
2294
+ }
2246
2295
  async function resolvePaperclipIssueGitHubLink(ctx, issueId, companyId, options = {}) {
2247
2296
  const linkRecords = options.linkRecords ?? await listGitHubIssueLinkRecords(ctx, {
2248
2297
  paperclipIssueId: issueId
@@ -2287,6 +2336,10 @@ async function resolvePaperclipIssueGitHubLink(ctx, issueId, companyId, options
2287
2336
  }
2288
2337
  }
2289
2338
  const issue = options.paperclipIssue ?? await ctx.issues.get(issueId, companyId);
2339
+ const issueOriginLink = resolvePaperclipIssueOriginGitHubIssueLink(issue, companyId);
2340
+ if (issueOriginLink) {
2341
+ return await hydrateRecoveredPaperclipIssueGitHubLink(ctx, issueId, issueOriginLink) ?? issueOriginLink;
2342
+ }
2290
2343
  const githubIssueUrl = extractImportedGitHubIssueUrlFromDescription(issue?.description);
2291
2344
  const githubIssueReference = githubIssueUrl ? parseGitHubIssueHtmlUrl(githubIssueUrl) : null;
2292
2345
  if (!githubIssueReference) {
@@ -2794,26 +2847,45 @@ async function buildIssueGitHubDetails(ctx, input) {
2794
2847
  const safeLeftTimestamp = Number.isFinite(leftTimestamp) ? leftTimestamp : 0;
2795
2848
  return safeRightTimestamp - safeLeftTimestamp;
2796
2849
  })[0];
2797
- if (!pullRequestLink) {
2850
+ if (pullRequestLink) {
2851
+ return {
2852
+ paperclipIssueId: issueId,
2853
+ kind: "pull_request",
2854
+ source: "pull_request_entity",
2855
+ repositoryUrl: pullRequestLink.data.repositoryUrl,
2856
+ githubPullRequestNumber: pullRequestLink.data.githubPullRequestNumber,
2857
+ githubPullRequestUrl: pullRequestLink.data.githubPullRequestUrl,
2858
+ githubPullRequestState: pullRequestLink.data.githubPullRequestState,
2859
+ title: pullRequestLink.data.title,
2860
+ linkedPullRequestNumbers: [pullRequestLink.data.githubPullRequestNumber],
2861
+ linkedPullRequests: [
2862
+ {
2863
+ number: pullRequestLink.data.githubPullRequestNumber,
2864
+ repositoryUrl: pullRequestLink.data.repositoryUrl
2865
+ }
2866
+ ],
2867
+ syncedAt: pullRequestLink.data.syncedAt
2868
+ };
2869
+ }
2870
+ const paperclipIssue = await ctx.issues.get(issueId, companyId);
2871
+ const pullRequestOriginLink = resolvePaperclipIssueOriginGitHubPullRequestLink(paperclipIssue);
2872
+ if (!pullRequestOriginLink) {
2798
2873
  return null;
2799
2874
  }
2800
2875
  return {
2801
2876
  paperclipIssueId: issueId,
2802
2877
  kind: "pull_request",
2803
- source: "pull_request_entity",
2804
- repositoryUrl: pullRequestLink.data.repositoryUrl,
2805
- githubPullRequestNumber: pullRequestLink.data.githubPullRequestNumber,
2806
- githubPullRequestUrl: pullRequestLink.data.githubPullRequestUrl,
2807
- githubPullRequestState: pullRequestLink.data.githubPullRequestState,
2808
- title: pullRequestLink.data.title,
2809
- linkedPullRequestNumbers: [pullRequestLink.data.githubPullRequestNumber],
2878
+ source: pullRequestOriginLink.source,
2879
+ repositoryUrl: pullRequestOriginLink.repositoryUrl,
2880
+ githubPullRequestNumber: pullRequestOriginLink.githubPullRequestNumber,
2881
+ githubPullRequestUrl: pullRequestOriginLink.githubPullRequestUrl,
2882
+ linkedPullRequestNumbers: [pullRequestOriginLink.githubPullRequestNumber],
2810
2883
  linkedPullRequests: [
2811
2884
  {
2812
- number: pullRequestLink.data.githubPullRequestNumber,
2813
- repositoryUrl: pullRequestLink.data.repositoryUrl
2885
+ number: pullRequestOriginLink.githubPullRequestNumber,
2886
+ repositoryUrl: pullRequestOriginLink.repositoryUrl
2814
2887
  }
2815
- ],
2816
- syncedAt: pullRequestLink.data.syncedAt
2888
+ ]
2817
2889
  };
2818
2890
  }
2819
2891
  async function resolveIssueByIdentifier(ctx, input) {
@@ -13106,6 +13178,66 @@ function registerGitHubAgentTools(ctx) {
13106
13178
  );
13107
13179
  })
13108
13180
  );
13181
+ ctx.tools.register(
13182
+ "assign_to_current_user",
13183
+ getGitHubAgentToolDeclaration("assign_to_current_user"),
13184
+ async (params, runCtx) => executeGitHubTool(async () => {
13185
+ const input = getToolInputRecord(params);
13186
+ const target = await resolveGitHubIssueToolTarget(ctx, runCtx, input);
13187
+ const octokit = await createGitHubToolOctokit(ctx, runCtx.companyId);
13188
+ const [currentResponse, authenticatedUserResponse] = await Promise.all([
13189
+ octokit.rest.issues.get({
13190
+ owner: target.repository.owner,
13191
+ repo: target.repository.repo,
13192
+ issue_number: target.issueNumber,
13193
+ headers: {
13194
+ "X-GitHub-Api-Version": GITHUB_API_VERSION
13195
+ }
13196
+ }),
13197
+ octokit.rest.users.getAuthenticated({
13198
+ headers: {
13199
+ "X-GitHub-Api-Version": GITHUB_API_VERSION
13200
+ }
13201
+ })
13202
+ ]);
13203
+ const assignedLogin = normalizeOptionalToolString(authenticatedUserResponse.data.login);
13204
+ if (!assignedLogin) {
13205
+ throw new Error("GitHub did not return the login for the saved token.");
13206
+ }
13207
+ const currentAssignees = (currentResponse.data.assignees ?? []).map((assignee) => assignee?.login ?? "").filter(Boolean);
13208
+ const nextAssignees = mergeNamedValues(currentAssignees, {
13209
+ setValues: [],
13210
+ addValues: [assignedLogin],
13211
+ removeValues: []
13212
+ });
13213
+ const alreadyAssigned = nextAssignees.length === currentAssignees.length && nextAssignees.every((assignee, index) => assignee.toLowerCase() === currentAssignees[index]?.toLowerCase());
13214
+ const updatedResponse = alreadyAssigned ? currentResponse : await octokit.rest.issues.update({
13215
+ owner: target.repository.owner,
13216
+ repo: target.repository.repo,
13217
+ issue_number: target.issueNumber,
13218
+ assignees: nextAssignees,
13219
+ headers: {
13220
+ "X-GitHub-Api-Version": GITHUB_API_VERSION
13221
+ }
13222
+ });
13223
+ const updatedIssue = normalizeGitHubIssueRecord(updatedResponse.data);
13224
+ return buildToolSuccessResult(
13225
+ alreadyAssigned ? `GitHub issue #${updatedIssue.number} in ${formatRepositoryLabel(target.repository)} was already assigned to ${assignedLogin}.` : `Assigned GitHub issue #${updatedIssue.number} in ${formatRepositoryLabel(target.repository)} to ${assignedLogin}.`,
13226
+ {
13227
+ repository: target.repository.url,
13228
+ assignedLogin,
13229
+ issue: {
13230
+ number: updatedIssue.number,
13231
+ title: updatedIssue.title,
13232
+ url: updatedIssue.htmlUrl,
13233
+ state: updatedIssue.state,
13234
+ labels: normalizeGitHubIssueLabels(updatedResponse.data.labels),
13235
+ assignees: (updatedResponse.data.assignees ?? []).map((assignee) => assignee?.login ?? "").filter(Boolean)
13236
+ }
13237
+ }
13238
+ );
13239
+ })
13240
+ );
13109
13241
  ctx.tools.register(
13110
13242
  "add_issue_comment",
13111
13243
  getGitHubAgentToolDeclaration("add_issue_comment"),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "paperclip-github-plugin",
3
- "version": "0.7.5",
3
+ "version": "0.8.0",
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.427.0",
44
+ "@paperclipai/plugin-sdk": "^2026.428.0",
45
45
  "react": "^19.2.5",
46
46
  "react-markdown": "^10.1.0",
47
47
  "rehype-raw": "^7.0.0",