paperclip-github-plugin 0.8.5 → 0.8.7

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 CHANGED
@@ -62,7 +62,7 @@ If a company already has a Paperclip project bound to a GitHub repository worksp
62
62
 
63
63
  ### Status sync with delivery context
64
64
 
65
- The plugin does more than mirror issue text. It looks at linked pull requests, mergeability, CI, review decisions, review threads, and trusted new GitHub comments so imported Paperclip issues can reflect where the work actually is. When GitHub links an issue to a pull request in another repository, GitHub Sync now follows that pull request's actual repository for status checks, review state, and deep links instead of assuming the issue repository. When sync closes an imported issue as `done` or `cancelled`, it also clears any pending Paperclip review or approval execution state so the host accepts the terminal transition cleanly.
65
+ The plugin does more than mirror issue text. It looks at linked pull requests, mergeability, CI, review decisions, review threads, and trusted new GitHub comments so imported Paperclip issues can reflect where the work actually is. When GitHub links an issue to a pull request in another repository, GitHub Sync now follows that pull request's actual repository for status checks, review state, and deep links instead of assuming the issue repository. When sync closes an imported issue as `done` or `cancelled`, it also clears any pending Paperclip review or approval execution policy/state so the host accepts the terminal transition cleanly and does not keep waking stale review participants.
66
66
 
67
67
  ### Company KPI dashboard
68
68
 
@@ -158,7 +158,7 @@ When the local Paperclip API is available, the plugin also syncs labels by name,
158
158
  | --- | --- |
159
159
  | Open issue with no linked pull request, created by a repository maintainer | `todo` on first import |
160
160
  | Open issue with no linked pull request | Configured default status, which defaults to `backlog` |
161
- | Open issue with a linked pull request and unfinished CI | `in_progress` |
161
+ | Open issue with a linked pull request and unfinished CI | `in_progress`; already-blocked pending-only PR waits remain `blocked` |
162
162
  | Open issue with failing CI, a non-mergeable linked pull request, or unresolved review threads | `todo`, or `in_progress` when GitHub Sync can hand the work back to an executor |
163
163
  | Open issue with green CI, a merge-ready linked pull request, and all review threads resolved | `in_review` |
164
164
  | Closed issue completed as finished work | `done` |
@@ -170,7 +170,7 @@ Additional behavior:
170
170
  - If the Paperclip host initially creates that imported maintainer issue in `backlog`, GitHub Sync promotes it to `todo` without replacing the configured default assignee with the executor handoff assignee, so triage ownership stays intact.
171
171
  - When Paperclip board access is connected for a company, the advanced assignee dropdowns list both company agents and `Me` for the connected board user.
172
172
  - Newly imported issues that finish sync in `todo` and are assigned to an agent enqueue an assignee wakeup so the agent can pick them up promptly.
173
- - For linked pull requests, GitHub Sync treats merge-conflict, behind-branch, blocked, draft, unstable merge states, and unresolved review threads as executor work, while merge-ready states such as `CLEAN` and `HAS_HOOKS` can move work into `in_review` when CI is green and review threads are resolved. A stale aggregate `CHANGES_REQUESTED` review decision alone does not move that maintainer wait back to active execution. Transient `UNKNOWN` mergeability also does not move an already `in_review` maintainer wait back to active execution when CI is green and review threads are resolved.
173
+ - For linked pull requests, GitHub Sync treats merge-conflict, behind-branch, blocked, draft, unstable merge states, and unresolved review threads as executor work, while merge-ready states such as `CLEAN` and `HAS_HOOKS` can move work into `in_review` when CI is green and review threads are resolved. If an issue is already `blocked` and GitHub reports only pending external merge requirements while CI is unfinished, sync preserves the external wait instead of waking an executor. A stale aggregate `CHANGES_REQUESTED` review decision alone does not move that maintainer wait back to active execution. Transient `UNKNOWN` mergeability also does not move an already `in_review` maintainer wait back to active execution when CI is green and review threads are resolved.
174
174
  - Imported issues that are already `blocked` stay `blocked` while any first-class `blockedBy` issue is still non-terminal, even if the linked GitHub pull request is otherwise green and review-ready.
175
175
  - When sync moves work into `in_review`, GitHub Sync first follows the Paperclip issue execution policy's current reviewer or approver when that stage is visible on the issue. If Paperclip exposes an internal review or approval stage but not yet the participant, the plugin falls back to the configured reviewer or approver handoff assignee. If the transition is only a healthy linked-PR wait with no visible internal review or approval stage, GitHub Sync leaves the issue unassigned so it can wait on normal maintainer review without waking an internal owner.
176
176
  - When sync moves work back into active execution, GitHub Sync first follows the Paperclip issue execution policy `returnAssignee` when it is available. Otherwise it falls back to the configured executor handoff assignee and then to the default imported assignee.
package/dist/manifest.js CHANGED
@@ -535,7 +535,7 @@ var COMPANY_METRIC_API_ROUTE_URL_PATH = `/api/plugins/${GITHUB_SYNC_PLUGIN_ID}/a
535
535
  var require2 = createRequire(import.meta.url);
536
536
  var packageJson = require2("../package.json");
537
537
  var SCHEDULE_TICK_CRON = "* * * * *";
538
- var MANIFEST_VERSION = "0.8.5"?.trim() || typeof packageJson.version === "string" && packageJson.version.trim() || process.env.npm_package_version?.trim() || "0.0.0-dev";
538
+ var MANIFEST_VERSION = "0.8.7"?.trim() || typeof packageJson.version === "string" && packageJson.version.trim() || process.env.npm_package_version?.trim() || "0.0.0-dev";
539
539
  var manifest = {
540
540
  id: GITHUB_SYNC_PLUGIN_ID,
541
541
  apiVersion: 1,
package/dist/worker.js CHANGED
@@ -1498,6 +1498,88 @@ function getErrorResponseDataMessage(error) {
1498
1498
  const message = data.message;
1499
1499
  return typeof message === "string" && message.trim() ? message.trim() : void 0;
1500
1500
  }
1501
+ function getGitHubRequestId(error) {
1502
+ const requestId = getErrorResponseHeaders(error)["x-github-request-id"]?.trim();
1503
+ return requestId || void 0;
1504
+ }
1505
+ function buildGitHubOctokitLogMetadata(context) {
1506
+ const metadata = {};
1507
+ const companyId = normalizeCompanyId(context.companyId);
1508
+ const operation = normalizeOptionalString2(context.operation);
1509
+ const repositoryUrl = normalizeOptionalString2(context.repositoryUrl);
1510
+ const toolName = normalizeOptionalString2(context.toolName);
1511
+ if (companyId) {
1512
+ metadata.companyId = companyId;
1513
+ }
1514
+ if (operation) {
1515
+ metadata.operation = operation;
1516
+ }
1517
+ if (repositoryUrl) {
1518
+ metadata.repositoryUrl = repositoryUrl;
1519
+ }
1520
+ if (context.syncTrigger) {
1521
+ metadata.syncTrigger = context.syncTrigger;
1522
+ }
1523
+ if (toolName) {
1524
+ metadata.toolName = toolName;
1525
+ }
1526
+ return metadata;
1527
+ }
1528
+ function getGitHubOctokitRequestPath(url, baseUrl) {
1529
+ if (typeof url !== "string" || !url.trim()) {
1530
+ return void 0;
1531
+ }
1532
+ const trimmedUrl = url.trim();
1533
+ if (typeof baseUrl === "string" && baseUrl.trim() && trimmedUrl.startsWith(baseUrl.trim())) {
1534
+ return trimmedUrl.slice(baseUrl.trim().length) || "/";
1535
+ }
1536
+ try {
1537
+ const parsed = new URL(trimmedUrl);
1538
+ return `${parsed.pathname}${parsed.search}`;
1539
+ } catch {
1540
+ return trimmedUrl;
1541
+ }
1542
+ }
1543
+ function createGitHubOctokit(ctx, token, context = {}) {
1544
+ const octokit = new Octokit({
1545
+ auth: token,
1546
+ log: {
1547
+ debug: () => void 0,
1548
+ info: () => void 0,
1549
+ warn: (message) => {
1550
+ ctx.logger.warn("GitHub Octokit warning.", {
1551
+ ...buildGitHubOctokitLogMetadata(context),
1552
+ message
1553
+ });
1554
+ },
1555
+ error: () => void 0
1556
+ }
1557
+ });
1558
+ if (context.logFailures === false) {
1559
+ return octokit;
1560
+ }
1561
+ octokit.hook.wrap("request", async (request, options) => {
1562
+ const start = Date.now();
1563
+ const requestOptions = octokit.request.endpoint.parse(options);
1564
+ try {
1565
+ return await request(options);
1566
+ } catch (error) {
1567
+ const responseMessage = getErrorResponseDataMessage(error);
1568
+ ctx.logger.warn("GitHub API request failed.", {
1569
+ ...buildGitHubOctokitLogMetadata(context),
1570
+ method: requestOptions.method,
1571
+ path: getGitHubOctokitRequestPath(requestOptions.url, options.baseUrl),
1572
+ status: getErrorStatus(error) ?? null,
1573
+ requestId: getGitHubRequestId(error) ?? null,
1574
+ durationMs: Date.now() - start,
1575
+ error: getErrorMessage(error),
1576
+ ...responseMessage ? { responseMessage } : {}
1577
+ });
1578
+ throw error;
1579
+ }
1580
+ });
1581
+ return octokit;
1582
+ }
1501
1583
  function getErrorResponseDataErrors(error) {
1502
1584
  if (!error || typeof error !== "object" || !("response" in error)) {
1503
1585
  return [];
@@ -5247,6 +5329,9 @@ function isHealthyMaintainerWaitTransition(params) {
5247
5329
  const { currentStatus, nextStatus, syncContext } = params;
5248
5330
  return nextStatus === "in_review" && (currentStatus === "done" || currentStatus === "in_review") && syncContext.executionState === null && syncContext.executionPolicy !== null;
5249
5331
  }
5332
+ function shouldClearCompletedSyncExecutionPolicy(params) {
5333
+ return (params.nextStatus === "done" || params.nextStatus === "cancelled") && (params.syncContext.executionPolicy !== null || params.syncContext.executionState !== null);
5334
+ }
5250
5335
  function shouldPreserveImportedTriageAssignee(params) {
5251
5336
  return params.wasImportedThisRun && params.maintainerAuthoredImportedIssue === true && params.currentStatus === "backlog" && params.nextStatus === "todo";
5252
5337
  }
@@ -5436,6 +5521,12 @@ function resolvePaperclipIssueStatus(params) {
5436
5521
  return hasExecutorHandoffTarget ? "in_progress" : "todo";
5437
5522
  }
5438
5523
  if (snapshot.linkedPullRequests.length > 0) {
5524
+ if (shouldPreserveBlockedExternalPullRequestWait({
5525
+ currentStatus,
5526
+ linkedPullRequests: snapshot.linkedPullRequests
5527
+ })) {
5528
+ return "blocked";
5529
+ }
5439
5530
  return resolvePaperclipStatusFromLinkedPullRequests(snapshot.linkedPullRequests, {
5440
5531
  preferInProgress: hasExecutorHandoffTarget,
5441
5532
  preserveTransientUnknownMergeabilityWait: currentStatus === "done" || currentStatus === "in_review"
@@ -5454,6 +5545,12 @@ function resolvePaperclipPullRequestIssueStatus(params) {
5454
5545
  if (currentStatus === "done" || currentStatus === "cancelled") {
5455
5546
  return currentStatus;
5456
5547
  }
5548
+ if (shouldPreserveBlockedExternalPullRequestWait({
5549
+ currentStatus,
5550
+ linkedPullRequests: [pullRequest]
5551
+ })) {
5552
+ return "blocked";
5553
+ }
5457
5554
  return resolvePaperclipStatusFromLinkedPullRequests([pullRequest], {
5458
5555
  preferInProgress: hasExecutorHandoffTarget,
5459
5556
  preserveTransientUnknownMergeabilityWait: currentStatus === "in_review"
@@ -5605,6 +5702,12 @@ function normalizeGitHubPullRequestReviewDecision(value) {
5605
5702
  function isGitHubPullRequestActionRequiredForSync(pullRequest) {
5606
5703
  return pullRequest.mergeability === "conflicting" || ACTION_REQUIRED_GITHUB_PULL_REQUEST_MERGE_STATE_STATUSES.has(pullRequest.mergeStateStatus);
5607
5704
  }
5705
+ function isGitHubPullRequestPendingExternalWaitForSync(pullRequest) {
5706
+ return pullRequest.ciState === "unfinished" && !pullRequest.hasUnresolvedReviewThreads && pullRequest.mergeability !== "conflicting" && (pullRequest.mergeStateStatus === "blocked" || pullRequest.mergeStateStatus === "unstable");
5707
+ }
5708
+ function shouldPreserveBlockedExternalPullRequestWait(params) {
5709
+ return params.currentStatus === "blocked" && params.linkedPullRequests.length > 0 && params.linkedPullRequests.every((pullRequest) => isGitHubPullRequestPendingExternalWaitForSync(pullRequest));
5710
+ }
5608
5711
  function isGitHubPullRequestTransientUnknownMergeabilityWait(pullRequest) {
5609
5712
  return pullRequest.ciState === "green" && !pullRequest.hasUnresolvedReviewThreads && pullRequest.mergeability !== "conflicting" && pullRequest.mergeStateStatus === "unknown";
5610
5713
  }
@@ -9089,6 +9192,10 @@ async function synchronizePaperclipIssueStatuses(ctx, octokit, repository, mappi
9089
9192
  nextStatus,
9090
9193
  syncContext: paperclipIssueSyncContext
9091
9194
  });
9195
+ const shouldClearCompletedExecutionPolicy = shouldClearCompletedSyncExecutionPolicy({
9196
+ nextStatus,
9197
+ syncContext: paperclipIssueSyncContext
9198
+ });
9092
9199
  const shouldPreserveImportedTriageRouting = shouldPreserveImportedTriageAssignee({
9093
9200
  currentStatus: paperclipIssue.status,
9094
9201
  nextStatus,
@@ -9110,7 +9217,7 @@ async function synchronizePaperclipIssueStatuses(ctx, octokit, repository, mappi
9110
9217
  importedIssue.lastSeenGitHubState = snapshot.state;
9111
9218
  importedIssue.linkedPullRequestCommentCounts = currentLinkedPullRequestCommentCounts;
9112
9219
  if (paperclipIssue.status === nextStatus) {
9113
- if (shouldClearTransitionAssignee) {
9220
+ if (shouldClearTransitionAssignee || shouldClearCompletedExecutionPolicy) {
9114
9221
  updateSyncFailureContext(syncFailureContext, {
9115
9222
  phase: "updating_paperclip_status",
9116
9223
  repositoryUrl: repository.url,
@@ -9122,8 +9229,8 @@ async function synchronizePaperclipIssueStatuses(ctx, octokit, repository, mappi
9122
9229
  currentStatus: paperclipIssue.status,
9123
9230
  syncContext: paperclipIssueSyncContext,
9124
9231
  nextStatus,
9125
- clearAssignee: true,
9126
- ...shouldPreserveMaintainerWaitRouting ? { clearExecutionPolicy: true } : {},
9232
+ ...shouldClearTransitionAssignee ? { clearAssignee: true } : {},
9233
+ ...shouldPreserveMaintainerWaitRouting || shouldClearCompletedExecutionPolicy ? { clearExecutionPolicy: true } : {},
9127
9234
  transitionComment: "",
9128
9235
  paperclipApiBaseUrl
9129
9236
  });
@@ -9159,7 +9266,7 @@ async function synchronizePaperclipIssueStatuses(ctx, octokit, repository, mappi
9159
9266
  nextStatus,
9160
9267
  ...nextTransitionAssignee ? { nextAssignee: nextTransitionAssignee.principal } : {},
9161
9268
  ...shouldClearTransitionAssignee ? { clearAssignee: true } : {},
9162
- ...shouldPreserveMaintainerWaitRouting ? { clearExecutionPolicy: true } : {},
9269
+ ...shouldPreserveMaintainerWaitRouting || shouldClearCompletedExecutionPolicy ? { clearExecutionPolicy: true } : {},
9163
9270
  transitionComment: transitionComment.body,
9164
9271
  transitionCommentAnnotation: transitionComment.annotation,
9165
9272
  paperclipApiBaseUrl
@@ -9778,12 +9885,16 @@ async function handleCompanyMetricApiRoute(ctx, input) {
9778
9885
  }
9779
9886
  };
9780
9887
  }
9781
- async function createGitHubToolOctokit(ctx, companyId) {
9888
+ async function createGitHubToolOctokit(ctx, companyId, context = {}) {
9782
9889
  const token = (await resolveGithubToken(ctx, { companyId })).trim();
9783
9890
  if (!token) {
9784
9891
  throw new Error(MISSING_GITHUB_TOKEN_SYNC_MESSAGE);
9785
9892
  }
9786
- return new Octokit({ auth: token });
9893
+ return createGitHubOctokit(ctx, token, {
9894
+ companyId,
9895
+ operation: "github-api",
9896
+ ...context
9897
+ });
9787
9898
  }
9788
9899
  async function listGitHubRepositoryOpenPullRequestNumbers(octokit, repository) {
9789
9900
  const response = await octokit.rest.pulls.list({
@@ -12684,8 +12795,11 @@ function mergeNamedValues(currentValues, params) {
12684
12795
  }
12685
12796
  return [...values.values()];
12686
12797
  }
12687
- async function validateGithubToken(token) {
12688
- const octokit = new Octokit({ auth: token.trim() });
12798
+ async function validateGithubToken(ctx, token) {
12799
+ const octokit = createGitHubOctokit(ctx, token.trim(), {
12800
+ logFailures: false,
12801
+ operation: "settings.validateToken"
12802
+ });
12689
12803
  try {
12690
12804
  const response = await octokit.rest.users.getAuthenticated();
12691
12805
  return {
@@ -12797,7 +12911,12 @@ async function performSync(ctx, trigger, options = {}) {
12797
12911
  return saveSettingsSyncState(ctx, settings, next.syncState, targetCompanyId);
12798
12912
  }
12799
12913
  activePaperclipApiAuthTokensByCompanyId = await resolvePaperclipApiAuthTokens(ctx, settings, config, mappings);
12800
- const octokit = new Octokit({ auth: token });
12914
+ const octokitLogContext = {
12915
+ companyId: targetCompanyId,
12916
+ operation: "sync.github-issues",
12917
+ syncTrigger: trigger
12918
+ };
12919
+ const octokit = createGitHubOctokit(ctx, token, octokitLogContext);
12801
12920
  let syncedIssuesCount = 0;
12802
12921
  let createdIssuesCount = 0;
12803
12922
  let skippedIssuesCount = 0;
@@ -12947,6 +13066,7 @@ async function performSync(ctx, trigger, options = {}) {
12947
13066
  await throwIfSyncCancelled();
12948
13067
  try {
12949
13068
  const repository = requireRepositoryReference(mapping.repositoryUrl);
13069
+ octokitLogContext.repositoryUrl = repository.url;
12950
13070
  const importedIssueRecords = nextRegistry.filter((entry) => doesImportedIssueRecordMatchMapping(entry, mapping)).filter((entry) => doesImportedIssueMatchTarget(entry, options.target));
12951
13071
  const shouldLoadClosedIssues = options.target?.kind === "issue" || importedIssueRecords.length > 0;
12952
13072
  currentProgress = {
@@ -13068,6 +13188,7 @@ async function performSync(ctx, trigger, options = {}) {
13068
13188
  await throwIfSyncCancelled();
13069
13189
  try {
13070
13190
  const { mapping, advancedSettings, repository, repositoryIndex, allIssuesById, issues, pullRequestLinks } = plan;
13191
+ octokitLogContext.repositoryUrl = repository.url;
13071
13192
  const companyId = mapping.companyId;
13072
13193
  let availableLabels = companyId ? companyLabelDirectoryCache.get(companyId) : void 0;
13073
13194
  if (!availableLabels) {
@@ -13509,13 +13630,19 @@ async function startSync(ctx, trigger, options = {}) {
13509
13630
  }
13510
13631
  }
13511
13632
  function registerGitHubAgentTools(ctx) {
13633
+ async function createAgentToolOctokit(runCtx, toolName, repository) {
13634
+ return createGitHubToolOctokit(ctx, runCtx.companyId, {
13635
+ toolName,
13636
+ ...repository ? { repositoryUrl: repository.url } : {}
13637
+ });
13638
+ }
13512
13639
  ctx.tools.register(
13513
13640
  "search_repository_items",
13514
13641
  getGitHubAgentToolDeclaration("search_repository_items"),
13515
13642
  async (params, runCtx) => executeGitHubTool(async () => {
13516
13643
  const input = getToolInputRecord(params);
13517
- const octokit = await createGitHubToolOctokit(ctx, runCtx.companyId);
13518
13644
  const repository = await resolveGitHubToolRepository(ctx, runCtx, input);
13645
+ const octokit = await createAgentToolOctokit(runCtx, "search_repository_items", repository);
13519
13646
  const rawQuery = normalizeOptionalToolString(input.query);
13520
13647
  if (!rawQuery) {
13521
13648
  throw new Error("query is required.");
@@ -13583,7 +13710,7 @@ function registerGitHubAgentTools(ctx) {
13583
13710
  async (params, runCtx) => executeGitHubTool(async () => {
13584
13711
  const input = getToolInputRecord(params);
13585
13712
  const target = await resolveGitHubIssueToolTarget(ctx, runCtx, input);
13586
- const octokit = await createGitHubToolOctokit(ctx, runCtx.companyId);
13713
+ const octokit = await createAgentToolOctokit(runCtx, "get_issue", target.repository);
13587
13714
  const response = await octokit.rest.issues.get({
13588
13715
  owner: target.repository.owner,
13589
13716
  repo: target.repository.repo,
@@ -13630,7 +13757,7 @@ function registerGitHubAgentTools(ctx) {
13630
13757
  async (params, runCtx) => executeGitHubTool(async () => {
13631
13758
  const input = getToolInputRecord(params);
13632
13759
  const target = await resolveGitHubIssueToolTarget(ctx, runCtx, input);
13633
- const octokit = await createGitHubToolOctokit(ctx, runCtx.companyId);
13760
+ const octokit = await createAgentToolOctokit(runCtx, "list_issue_comments", target.repository);
13634
13761
  const comments = await listAllGitHubIssueComments(octokit, target.repository, target.issueNumber);
13635
13762
  return buildToolSuccessResult(
13636
13763
  `Loaded ${comments.length} GitHub ${comments.length === 1 ? "comment" : "comments"} from issue #${target.issueNumber}.`,
@@ -13648,7 +13775,7 @@ function registerGitHubAgentTools(ctx) {
13648
13775
  async (params, runCtx) => executeGitHubTool(async () => {
13649
13776
  const input = getToolInputRecord(params);
13650
13777
  const target = await resolveGitHubIssueToolTarget(ctx, runCtx, input);
13651
- const octokit = await createGitHubToolOctokit(ctx, runCtx.companyId);
13778
+ const octokit = await createAgentToolOctokit(runCtx, "update_issue", target.repository);
13652
13779
  const currentResponse = await octokit.rest.issues.get({
13653
13780
  owner: target.repository.owner,
13654
13781
  repo: target.repository.repo,
@@ -13721,7 +13848,7 @@ function registerGitHubAgentTools(ctx) {
13721
13848
  async (params, runCtx) => executeGitHubTool(async () => {
13722
13849
  const input = getToolInputRecord(params);
13723
13850
  const target = await resolveGitHubIssueToolTarget(ctx, runCtx, input);
13724
- const octokit = await createGitHubToolOctokit(ctx, runCtx.companyId);
13851
+ const octokit = await createAgentToolOctokit(runCtx, "assign_to_current_user", target.repository);
13725
13852
  const [currentResponse, authenticatedUserResponse] = await Promise.all([
13726
13853
  octokit.rest.issues.get({
13727
13854
  owner: target.repository.owner,
@@ -13781,7 +13908,7 @@ function registerGitHubAgentTools(ctx) {
13781
13908
  async (params, runCtx) => executeGitHubTool(async () => {
13782
13909
  const input = getToolInputRecord(params);
13783
13910
  const target = await resolveGitHubIssueToolTarget(ctx, runCtx, input);
13784
- const octokit = await createGitHubToolOctokit(ctx, runCtx.companyId);
13911
+ const octokit = await createAgentToolOctokit(runCtx, "add_issue_comment", target.repository);
13785
13912
  const body = appendAiAuthorshipFooter(String(input.body ?? ""), "comment", normalizeOptionalToolString(input.llmModel));
13786
13913
  const response = await octokit.rest.issues.createComment({
13787
13914
  owner: target.repository.owner,
@@ -13831,7 +13958,7 @@ function registerGitHubAgentTools(ctx) {
13831
13958
  "pull request description",
13832
13959
  normalizeOptionalToolString(input.llmModel)
13833
13960
  ) : void 0;
13834
- const octokit = await createGitHubToolOctokit(ctx, runCtx.companyId);
13961
+ const octokit = await createAgentToolOctokit(runCtx, "create_pull_request", repository);
13835
13962
  const response = await octokit.rest.pulls.create({
13836
13963
  owner: repository.owner,
13837
13964
  repo: repository.repo,
@@ -13907,7 +14034,7 @@ function registerGitHubAgentTools(ctx) {
13907
14034
  async (params, runCtx) => executeGitHubTool(async () => {
13908
14035
  const input = getToolInputRecord(params);
13909
14036
  const target = await resolveGitHubPullRequestToolTarget(ctx, runCtx, input);
13910
- const octokit = await createGitHubToolOctokit(ctx, runCtx.companyId);
14037
+ const octokit = await createAgentToolOctokit(runCtx, "get_pull_request", target.repository);
13911
14038
  const response = await octokit.rest.pulls.get({
13912
14039
  owner: target.repository.owner,
13913
14040
  repo: target.repository.repo,
@@ -13955,7 +14082,7 @@ function registerGitHubAgentTools(ctx) {
13955
14082
  async (params, runCtx) => executeGitHubTool(async () => {
13956
14083
  const input = getToolInputRecord(params);
13957
14084
  const target = await resolveGitHubPullRequestToolTarget(ctx, runCtx, input);
13958
- const octokit = await createGitHubToolOctokit(ctx, runCtx.companyId);
14085
+ const octokit = await createAgentToolOctokit(runCtx, "update_pull_request", target.repository);
13959
14086
  let currentResponse = await octokit.rest.pulls.get({
13960
14087
  owner: target.repository.owner,
13961
14088
  repo: target.repository.repo,
@@ -14021,7 +14148,7 @@ function registerGitHubAgentTools(ctx) {
14021
14148
  async (params, runCtx) => executeGitHubTool(async () => {
14022
14149
  const input = getToolInputRecord(params);
14023
14150
  const target = await resolveGitHubPullRequestToolTarget(ctx, runCtx, input);
14024
- const octokit = await createGitHubToolOctokit(ctx, runCtx.companyId);
14151
+ const octokit = await createAgentToolOctokit(runCtx, "list_pull_request_files", target.repository);
14025
14152
  const files = await listAllPullRequestFiles(octokit, target.repository, target.pullRequestNumber);
14026
14153
  return buildToolSuccessResult(
14027
14154
  `Loaded ${files.length} changed ${files.length === 1 ? "file" : "files"} from pull request #${target.pullRequestNumber}.`,
@@ -14039,7 +14166,7 @@ function registerGitHubAgentTools(ctx) {
14039
14166
  async (params, runCtx) => executeGitHubTool(async () => {
14040
14167
  const input = getToolInputRecord(params);
14041
14168
  const target = await resolveGitHubPullRequestToolTarget(ctx, runCtx, input);
14042
- const octokit = await createGitHubToolOctokit(ctx, runCtx.companyId);
14169
+ const octokit = await createAgentToolOctokit(runCtx, "get_pull_request_checks", target.repository);
14043
14170
  const pullRequestResponse = await octokit.rest.pulls.get({
14044
14171
  owner: target.repository.owner,
14045
14172
  repo: target.repository.repo,
@@ -14138,7 +14265,7 @@ function registerGitHubAgentTools(ctx) {
14138
14265
  async (params, runCtx) => executeGitHubTool(async () => {
14139
14266
  const input = getToolInputRecord(params);
14140
14267
  const target = await resolveGitHubPullRequestToolTarget(ctx, runCtx, input);
14141
- const octokit = await createGitHubToolOctokit(ctx, runCtx.companyId);
14268
+ const octokit = await createAgentToolOctokit(runCtx, "list_pull_request_review_threads", target.repository);
14142
14269
  const threads = await listDetailedPullRequestReviewThreads(octokit, target.repository, target.pullRequestNumber);
14143
14270
  return buildToolSuccessResult(
14144
14271
  `Loaded ${threads.length} review ${threads.length === 1 ? "thread" : "threads"} from pull request #${target.pullRequestNumber}.`,
@@ -14160,7 +14287,7 @@ function registerGitHubAgentTools(ctx) {
14160
14287
  throw new Error("threadId is required.");
14161
14288
  }
14162
14289
  const body = appendAiAuthorshipFooter(String(input.body ?? ""), "comment", normalizeOptionalToolString(input.llmModel));
14163
- const octokit = await createGitHubToolOctokit(ctx, runCtx.companyId);
14290
+ const octokit = await createAgentToolOctokit(runCtx, "reply_to_review_thread");
14164
14291
  const response = await octokit.graphql(
14165
14292
  GITHUB_ADD_PULL_REQUEST_REVIEW_THREAD_REPLY_MUTATION,
14166
14293
  {
@@ -14195,7 +14322,7 @@ function registerGitHubAgentTools(ctx) {
14195
14322
  if (!threadId) {
14196
14323
  throw new Error("threadId is required.");
14197
14324
  }
14198
- const octokit = await createGitHubToolOctokit(ctx, runCtx.companyId);
14325
+ const octokit = await createAgentToolOctokit(runCtx, "resolve_review_thread");
14199
14326
  const response = await octokit.graphql(
14200
14327
  GITHUB_RESOLVE_REVIEW_THREAD_MUTATION,
14201
14328
  {
@@ -14226,7 +14353,7 @@ function registerGitHubAgentTools(ctx) {
14226
14353
  if (!threadId) {
14227
14354
  throw new Error("threadId is required.");
14228
14355
  }
14229
- const octokit = await createGitHubToolOctokit(ctx, runCtx.companyId);
14356
+ const octokit = await createAgentToolOctokit(runCtx, "unresolve_review_thread");
14230
14357
  const response = await octokit.graphql(
14231
14358
  GITHUB_UNRESOLVE_REVIEW_THREAD_MUTATION,
14232
14359
  {
@@ -14259,7 +14386,7 @@ function registerGitHubAgentTools(ctx) {
14259
14386
  if (userReviewers.length === 0 && teamReviewers.length === 0) {
14260
14387
  throw new Error("Provide at least one user reviewer or team reviewer.");
14261
14388
  }
14262
- const octokit = await createGitHubToolOctokit(ctx, runCtx.companyId);
14389
+ const octokit = await createAgentToolOctokit(runCtx, "request_pull_request_reviewers", target.repository);
14263
14390
  const response = await octokit.rest.pulls.requestReviewers({
14264
14391
  owner: target.repository.owner,
14265
14392
  repo: target.repository.repo,
@@ -14290,7 +14417,7 @@ function registerGitHubAgentTools(ctx) {
14290
14417
  if (!organization) {
14291
14418
  throw new Error("organization is required.");
14292
14419
  }
14293
- const octokit = await createGitHubToolOctokit(ctx, runCtx.companyId);
14420
+ const octokit = await createAgentToolOctokit(runCtx, "list_organization_projects");
14294
14421
  const projects = await listGitHubOrganizationProjects(octokit, organization, {
14295
14422
  includeClosed: input.includeClosed === true,
14296
14423
  query: normalizeOptionalToolString(input.query),
@@ -14311,7 +14438,7 @@ function registerGitHubAgentTools(ctx) {
14311
14438
  async (params, runCtx) => executeGitHubTool(async () => {
14312
14439
  const input = getToolInputRecord(params);
14313
14440
  const target = await resolveGitHubPullRequestToolTarget(ctx, runCtx, input);
14314
- const octokit = await createGitHubToolOctokit(ctx, runCtx.companyId);
14441
+ const octokit = await createAgentToolOctokit(runCtx, "add_pull_request_to_project", target.repository);
14315
14442
  const projectTarget = await resolveGitHubProjectToolTarget(octokit, input);
14316
14443
  const pullRequest = await getGitHubPullRequestProjectItems(
14317
14444
  octokit,
@@ -14380,6 +14507,7 @@ function shouldStartWorkerHost(moduleUrl, entry = process.argv[1]) {
14380
14507
  }
14381
14508
  var __testing = {
14382
14509
  buildSyncFallbackExecutionStatePatch,
14510
+ createGitHubToolOctokit,
14383
14511
  hasUnresolvedPaperclipIssueBlocker,
14384
14512
  isHealthyMaintainerWaitTransition,
14385
14513
  resolveSyncTransitionAssignee
@@ -14681,7 +14809,7 @@ var plugin = definePlugin({
14681
14809
  if (!trimmedToken) {
14682
14810
  throw new Error("Enter a GitHub token.");
14683
14811
  }
14684
- return validateGithubToken(trimmedToken);
14812
+ return validateGithubToken(ctx, trimmedToken);
14685
14813
  });
14686
14814
  ctx.actions.register("project.pullRequests.createIssue", async (input) => {
14687
14815
  const record = input && typeof input === "object" ? input : {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "paperclip-github-plugin",
3
- "version": "0.8.5",
3
+ "version": "0.8.7",
4
4
  "description": "Paperclip plugin for synchronizing GitHub issues into Paperclip projects.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",