paperclip-github-plugin 0.4.4 → 0.4.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 CHANGED
@@ -34,7 +34,7 @@ The plugin adds a full in-host workflow instead of a one-off import script:
34
34
  - saved sync diagnostics that let operators inspect the latest per-issue failures, raw errors, and suggested next steps
35
35
  - a project sidebar item that opens a live project-scoped Pull Requests page for the mapped repository and can show the open PR count through a lightweight badge read
36
36
  - manual sync actions from global, project, and issue toolbar surfaces
37
- - a GitHub detail tab on synced Paperclip issues
37
+ - a GitHub detail tab on synced Paperclip issues that stays hidden for Paperclip issues with no linked GitHub issue
38
38
  - GitHub link annotations on sync-generated status transition comments when the host supports comment annotations
39
39
 
40
40
  ## How it works
@@ -47,7 +47,7 @@ During sync, the plugin imports one top-level Paperclip issue per GitHub issue,
47
47
 
48
48
  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 or status repair paths when those routes are available.
49
49
 
50
- Long-running syncs continue in the background, so quick actions do not have to wait for the whole import to finish. Once a sync has started, the settings page, dashboard widget, and toolbar actions can request cancellation; the worker stops cooperatively after the current repository or issue step finishes.
50
+ Long-running syncs continue in the background, so quick actions do not have to wait for the whole import to finish. Once a sync has started, the settings page, dashboard widget, and toolbar actions can request cancellation; the worker stops cooperatively after the current repository or issue step finishes. If the worker restarts mid-run, GitHub Sync now recovers that orphaned `running` state on the next read or control action instead of leaving the UI stuck in `running` or silently restarting the old run.
51
51
 
52
52
  ## Highlights
53
53
 
@@ -71,7 +71,7 @@ Paperclip issue linkage on the queue prefers the GitHub issue that the pull requ
71
71
 
72
72
  ### Agent workflows built in
73
73
 
74
- Paperclip agents can search GitHub for duplicates, read and update issues, post comments, create pull requests, inspect changed files and CI, reply to review threads, resolve or unresolve threads, request reviewers, list org-level GitHub Projects, and associate pull requests with those projects without leaving the Paperclip plugin surface.
74
+ Paperclip agents can search GitHub for duplicates, read and update issues, post comments, create pull requests, inspect changed files and CI, reply to review threads, resolve or unresolve threads, request reviewers, search org-level GitHub Projects, and associate pull requests with those projects without leaving the Paperclip plugin surface.
75
75
 
76
76
  ## Requirements
77
77
 
@@ -185,9 +185,9 @@ The plugin exposes GitHub workflow tools to Paperclip agents, including:
185
185
  - issue reads, comment reads, comment writes, and metadata updates
186
186
  - pull request creation, reads, updates, changed-file inspection, and CI-check inspection
187
187
  - review-thread reads, replies, resolve and unresolve actions, and reviewer requests
188
- - organization-level GitHub Project listing and pull-request-to-project association
188
+ - organization-level GitHub Project search/listing and pull-request-to-project association
189
189
 
190
- When an agent posts a GitHub comment or review-thread reply through the plugin, the message includes a footer disclosing that it was created by a Paperclip AI agent and which model was used.
190
+ When an agent sends GitHub body content through the plugin, including issue bodies, pull request descriptions, comments, and review-thread replies, the plugin adds a GitHub-flavored Markdown footer with a horizontal rule and compact heading that discloses AI authorship. If the tool caller supplies `llmModel`, the footer also includes the model name, for example `###### ✨ This comment was AI-generated using gpt-5.4`.
191
191
 
192
192
  Current host caveat: on authenticated Paperclip deployments, the Paperclip host currently guards `GET /api/plugins/tools` and `POST /api/plugins/tools/execute` with board authentication before dispatching to any plugin worker. If an agent run does not have board access for the target company, GitHub Sync tool discovery and execution fail with `403 {"error":"Board access required"}` before this plugin's worker code runs.
193
193
 
@@ -203,6 +203,7 @@ Current host caveat: on authenticated Paperclip deployments, the Paperclip host
203
203
  - If a GitHub-linked project does not show the **Pull requests** sidebar entry, reopen the plugin settings and re-save the mapping. The project pull request surfaces also recover older mappings when saved ids are missing, and they can fall back to the active project's bound GitHub repository when the project already has a GitHub workspace configured.
204
204
  - If GitHub rate limiting is hit, the plugin pauses sync until the reported reset time instead of retrying pointlessly.
205
205
  - If a manual sync takes longer than the host action window, it continues in the background and updates the UI when it finishes or when a cancellation request stops it.
206
+ - If a sync shows `running` after the worker has restarted, the next settings read, toolbar read, cancel action, or scheduler tick will reconcile that stale run into an interrupted error or a cancelled result so you can retry cleanly.
206
207
 
207
208
  ## Development
208
209
 
package/dist/manifest.js CHANGED
@@ -35,7 +35,7 @@ var projectNumberProperty = {
35
35
  };
36
36
  var llmModelProperty = {
37
37
  type: "string",
38
- description: "Exact LLM name used to draft the comment. Required so the plugin can append the mandatory AI-authorship footer."
38
+ description: "Exact LLM name used to draft the GitHub content. When provided, the plugin includes it in the mandatory AI-authorship footer."
39
39
  };
40
40
  var issueTargetSchema = {
41
41
  anyOf: [
@@ -149,7 +149,7 @@ var GITHUB_AGENT_TOOLS = [
149
149
  {
150
150
  name: "update_issue",
151
151
  displayName: "Update Issue",
152
- description: "Update GitHub issue fields such as title, body, state, labels, assignees, or milestone.",
152
+ description: "Update GitHub issue fields such as title, body, state, labels, assignees, or milestone. When a non-empty body is provided, the plugin appends an AI-authorship footer and includes llmModel when supplied.",
153
153
  parametersSchema: {
154
154
  type: "object",
155
155
  additionalProperties: false,
@@ -162,8 +162,10 @@ var GITHUB_AGENT_TOOLS = [
162
162
  type: "string"
163
163
  },
164
164
  body: {
165
- type: "string"
165
+ type: "string",
166
+ description: "Optional human-facing issue description body. If provided, it must remain non-empty after trimming and removing any existing AI footer."
166
167
  },
168
+ llmModel: llmModelProperty,
167
169
  state: {
168
170
  type: "string",
169
171
  enum: ["open", "closed"]
@@ -215,11 +217,11 @@ var GITHUB_AGENT_TOOLS = [
215
217
  {
216
218
  name: "add_issue_comment",
217
219
  displayName: "Add Issue Comment",
218
- description: "Post a comment on a GitHub issue or pull request. Provide only the human-facing message body; include llmModel so the plugin can append the required AI-authorship footer.",
220
+ description: "Post a comment on a GitHub issue or pull request. Provide only the human-facing message body; it must remain non-empty after trimming and removing any existing AI footer. The plugin appends the required AI-authorship footer and includes llmModel when supplied.",
219
221
  parametersSchema: {
220
222
  type: "object",
221
223
  additionalProperties: false,
222
- required: ["body", "llmModel"],
224
+ required: ["body"],
223
225
  ...issueTargetSchema,
224
226
  properties: {
225
227
  repository: repositoryProperty,
@@ -227,7 +229,8 @@ var GITHUB_AGENT_TOOLS = [
227
229
  paperclipIssueId: paperclipIssueIdProperty,
228
230
  body: {
229
231
  type: "string",
230
- description: "Human-facing comment body without the AI footer."
232
+ minLength: 1,
233
+ description: "Human-facing comment body without the AI footer. It must remain non-empty after trimming and removing any existing AI footer."
231
234
  },
232
235
  llmModel: llmModelProperty
233
236
  }
@@ -236,7 +239,7 @@ var GITHUB_AGENT_TOOLS = [
236
239
  {
237
240
  name: "create_pull_request",
238
241
  displayName: "Create Pull Request",
239
- description: "Open a GitHub pull request once the implementation branch is pushed.",
242
+ description: "Open a GitHub pull request once the implementation branch is pushed. When a non-empty body is provided, the plugin appends an AI-authorship footer and includes llmModel when supplied.",
240
243
  parametersSchema: {
241
244
  type: "object",
242
245
  additionalProperties: false,
@@ -255,8 +258,10 @@ var GITHUB_AGENT_TOOLS = [
255
258
  type: "string"
256
259
  },
257
260
  body: {
258
- type: "string"
261
+ type: "string",
262
+ description: "Optional human-facing pull request description. If provided, it must remain non-empty after trimming and removing any existing AI footer."
259
263
  },
264
+ llmModel: llmModelProperty,
260
265
  draft: {
261
266
  type: "boolean"
262
267
  }
@@ -281,7 +286,7 @@ var GITHUB_AGENT_TOOLS = [
281
286
  {
282
287
  name: "update_pull_request",
283
288
  displayName: "Update Pull Request",
284
- description: "Edit pull request title, body, base branch, open or close it, or convert between draft and ready for review.",
289
+ description: "Edit pull request title, body, base branch, open or close it, or convert between draft and ready for review. When a non-empty body is provided, the plugin appends an AI-authorship footer and includes llmModel when supplied.",
285
290
  parametersSchema: {
286
291
  type: "object",
287
292
  additionalProperties: false,
@@ -294,8 +299,10 @@ var GITHUB_AGENT_TOOLS = [
294
299
  type: "string"
295
300
  },
296
301
  body: {
297
- type: "string"
302
+ type: "string",
303
+ description: "Optional human-facing pull request description. If provided, it must remain non-empty after trimming and removing any existing AI footer."
298
304
  },
305
+ llmModel: llmModelProperty,
299
306
  base: {
300
307
  type: "string"
301
308
  },
@@ -358,11 +365,11 @@ var GITHUB_AGENT_TOOLS = [
358
365
  {
359
366
  name: "reply_to_review_thread",
360
367
  displayName: "Reply To Review Thread",
361
- description: "Reply to an existing pull request review thread. Provide only the human-facing body; include llmModel so the plugin can append the required AI-authorship footer.",
368
+ description: "Reply to an existing pull request review thread. Provide only the human-facing body; the plugin appends the required AI-authorship footer and includes llmModel when supplied.",
362
369
  parametersSchema: {
363
370
  type: "object",
364
371
  additionalProperties: false,
365
- required: ["threadId", "body", "llmModel"],
372
+ required: ["threadId", "body"],
366
373
  properties: {
367
374
  threadId: {
368
375
  type: "string",
@@ -370,7 +377,8 @@ var GITHUB_AGENT_TOOLS = [
370
377
  },
371
378
  body: {
372
379
  type: "string",
373
- description: "Human-facing reply body without the AI footer."
380
+ minLength: 1,
381
+ description: "Human-facing reply body without the AI footer. It must remain non-empty after trimming and removing any existing AI footer."
374
382
  },
375
383
  llmModel: llmModelProperty
376
384
  }
@@ -454,7 +462,7 @@ var GITHUB_AGENT_TOOLS = [
454
462
  {
455
463
  name: "list_organization_projects",
456
464
  displayName: "List Organization Projects",
457
- description: "List GitHub organization-level Projects so an agent can choose where to associate pull requests.",
465
+ description: "Search or list GitHub organization-level Projects so an agent can choose where to associate pull requests.",
458
466
  parametersSchema: {
459
467
  type: "object",
460
468
  additionalProperties: false,
@@ -503,7 +511,7 @@ var require2 = createRequire(import.meta.url);
503
511
  var packageJson = require2("../package.json");
504
512
  var DASHBOARD_WIDGET_CAPABILITY = "ui.dashboardWidget.register";
505
513
  var SCHEDULE_TICK_CRON = "* * * * *";
506
- var MANIFEST_VERSION = "0.4.4"?.trim() || typeof packageJson.version === "string" && packageJson.version.trim() || process.env.npm_package_version?.trim() || "0.0.0-dev";
514
+ var MANIFEST_VERSION = "0.4.6"?.trim() || typeof packageJson.version === "string" && packageJson.version.trim() || process.env.npm_package_version?.trim() || "0.0.0-dev";
507
515
  var manifest = {
508
516
  id: "paperclip-github-plugin",
509
517
  apiVersion: 1,
package/dist/ui/index.js CHANGED
@@ -23091,6 +23091,21 @@ function getActiveRateLimitPause(syncState, referenceTimeMs = Date.now()) {
23091
23091
  function isSyncCancellationRequested(syncState) {
23092
23092
  return syncState.status === "running" && Boolean(syncState.cancelRequestedAt?.trim());
23093
23093
  }
23094
+ function resolveToolbarButtonState(params) {
23095
+ const syncPersistedRunning = params.syncState.status === "running";
23096
+ const syncStartPending = params.runningSync && !syncPersistedRunning;
23097
+ const cancellationRequested = syncPersistedRunning && (params.cancellingSync || isSyncCancellationRequested(params.syncState));
23098
+ const loadingVisible = params.loading && !syncPersistedRunning;
23099
+ return {
23100
+ busy: loadingVisible || syncStartPending || cancellationRequested || !params.allowToolbarCancellation && syncPersistedRunning,
23101
+ disabled: loadingVisible || syncStartPending || (syncPersistedRunning ? params.allowToolbarCancellation ? cancellationRequested : true : !params.effectiveCanRun),
23102
+ label: syncPersistedRunning && params.allowToolbarCancellation ? "Cancel sync" : params.effectiveLabel,
23103
+ busyLabel: syncPersistedRunning ? cancellationRequested ? "Cancelling\u2026" : "Syncing\u2026" : loadingVisible ? "Loading\u2026" : "Syncing\u2026",
23104
+ cancellationRequested,
23105
+ syncPersistedRunning,
23106
+ syncStartPending
23107
+ };
23108
+ }
23094
23109
  function getSyncToastTitle(syncState) {
23095
23110
  if (getActiveRateLimitPause(syncState)) {
23096
23111
  return "GitHub sync is paused";
@@ -33162,13 +33177,25 @@ function GitHubSyncToolbarButtonSurface(props) {
33162
33177
  const effectiveCanRun = state.canRun && !boardAccessSetupIssue;
33163
33178
  const effectiveMessage = boardAccessSetupIssue ? getSyncSetupMessage(boardAccessSetupIssue, hasCompanyContext) : state.message;
33164
33179
  const effectiveLabel = boardAccessSetupIssue ? "Board access required" : state.label;
33165
- const syncPersistedRunning = effectiveSyncState.status === "running";
33166
- const syncStartPending = runningSync && !syncPersistedRunning;
33167
33180
  const allowToolbarCancellation = Boolean(props.entityType);
33168
- const cancellationRequested = syncPersistedRunning && (cancellingSync || isSyncCancellationRequested(effectiveSyncState));
33169
- const toolbarButtonBusy = toolbarState.loading || syncStartPending || cancellationRequested || !allowToolbarCancellation && syncPersistedRunning;
33170
- const toolbarButtonLabel = syncPersistedRunning && allowToolbarCancellation ? "Cancel sync" : effectiveLabel;
33171
- const toolbarButtonBusyLabel = toolbarState.loading ? "Loading\u2026" : syncPersistedRunning ? cancellationRequested ? "Cancelling\u2026" : "Syncing\u2026" : "Syncing\u2026";
33181
+ const toolbarButtonState = resolveToolbarButtonState({
33182
+ loading: toolbarState.loading,
33183
+ runningSync,
33184
+ cancellingSync,
33185
+ syncState: effectiveSyncState,
33186
+ allowToolbarCancellation,
33187
+ effectiveCanRun,
33188
+ effectiveLabel
33189
+ });
33190
+ const {
33191
+ busy: toolbarButtonBusy,
33192
+ disabled: toolbarButtonDisabled,
33193
+ label: toolbarButtonLabel,
33194
+ busyLabel: toolbarButtonBusyLabel,
33195
+ syncPersistedRunning,
33196
+ syncStartPending,
33197
+ cancellationRequested
33198
+ } = toolbarButtonState;
33172
33199
  const armSyncCompletionToast = useSyncCompletionToast(effectiveSyncState, toast);
33173
33200
  useEffect2(() => {
33174
33201
  if (effectiveSyncState.status !== "running") {
@@ -33343,7 +33370,7 @@ function GitHubSyncToolbarButtonSurface(props) {
33343
33370
  "data-variant": "outline",
33344
33371
  "data-size": "sm",
33345
33372
  className: props.entityType ? HOST_ENTITY_BUTTON_CLASSNAME : HOST_GLOBAL_BUTTON_CLASSNAME,
33346
- disabled: toolbarState.loading || syncStartPending || (syncPersistedRunning ? allowToolbarCancellation ? cancellationRequested : true : !effectiveCanRun),
33373
+ disabled: toolbarButtonDisabled,
33347
33374
  onClick: syncPersistedRunning && allowToolbarCancellation ? handleCancelSync : handleRunSync,
33348
33375
  children: /* @__PURE__ */ jsx2(
33349
33376
  LoadingButtonContent,
@@ -33385,6 +33412,12 @@ function GitHubSyncIssueDetailTabContent(props) {
33385
33412
  ...props.issueId ? { issueId: props.issueId } : {}
33386
33413
  });
33387
33414
  const issueDetails = details.data?.paperclipIssueId === props.issueId ? details.data : null;
33415
+ const detailTabState = resolveGitHubIssueDetailTabState({
33416
+ loadingIssueId: props.loadingIssueId,
33417
+ detailsLoading: details.loading,
33418
+ detailsError: Boolean(details.error),
33419
+ issueDetails
33420
+ });
33388
33421
  useEffect2(() => {
33389
33422
  if (!props.companyId || !props.issueId) {
33390
33423
  return;
@@ -33395,12 +33428,14 @@ function GitHubSyncIssueDetailTabContent(props) {
33395
33428
  return;
33396
33429
  }
33397
33430
  }, [details.refresh, props.companyId, props.issueId]);
33431
+ if (detailTabState === "hidden") {
33432
+ return null;
33433
+ }
33398
33434
  return /* @__PURE__ */ jsxs2("section", { className: "ghsync-issue-detail", style: props.themeVars, children: [
33399
33435
  /* @__PURE__ */ jsx2("style", { children: EXTENSION_SURFACE_STYLES }),
33400
- props.loadingIssueId || details.loading && !issueDetails ? /* @__PURE__ */ jsx2("p", { className: "ghsync-extension-empty", children: "Loading GitHub sync details\u2026" }) : null,
33401
- details.error ? /* @__PURE__ */ jsx2("p", { className: "ghsync-extension-empty", children: details.error.message }) : null,
33402
- !props.loadingIssueId && !details.loading && !details.error && !issueDetails ? /* @__PURE__ */ jsx2("p", { className: "ghsync-extension-empty", children: "GitHub Sync has not linked this Paperclip issue to a GitHub issue yet." }) : null,
33403
- issueDetails ? /* @__PURE__ */ jsxs2(Fragment2, { children: [
33436
+ detailTabState === "loading" ? /* @__PURE__ */ jsx2("p", { className: "ghsync-extension-empty", children: "Loading GitHub sync details\u2026" }) : null,
33437
+ detailTabState === "error" && details.error ? /* @__PURE__ */ jsx2("p", { className: "ghsync-extension-empty", children: details.error.message }) : null,
33438
+ detailTabState === "ready" && issueDetails ? /* @__PURE__ */ jsxs2(Fragment2, { children: [
33404
33439
  /* @__PURE__ */ jsxs2("div", { className: "ghsync-extension-heading", children: [
33405
33440
  /* @__PURE__ */ jsxs2("div", { children: [
33406
33441
  /* @__PURE__ */ jsxs2("h4", { children: [
@@ -33479,6 +33514,18 @@ function GitHubSyncIssueDetailTabContent(props) {
33479
33514
  ] }) : null
33480
33515
  ] });
33481
33516
  }
33517
+ function resolveGitHubIssueDetailTabState(params) {
33518
+ if (params.loadingIssueId || params.detailsLoading && !params.issueDetails) {
33519
+ return "loading";
33520
+ }
33521
+ if (params.issueDetails) {
33522
+ return "ready";
33523
+ }
33524
+ if (params.detailsError) {
33525
+ return "error";
33526
+ }
33527
+ return "hidden";
33528
+ }
33482
33529
  function GitHubSyncIssueTaskDetailView() {
33483
33530
  const context = useHostContext();
33484
33531
  const themeMode = useResolvedThemeMode();
@@ -33553,8 +33600,10 @@ export {
33553
33600
  GitHubSyncProjectPullRequestsSidebarItem,
33554
33601
  GitHubSyncSettingsPage,
33555
33602
  index_default as default,
33603
+ resolveGitHubIssueDetailTabState,
33556
33604
  resolveOrCreateProject,
33557
33605
  resolveSavedTokenUiState,
33606
+ resolveToolbarButtonState,
33558
33607
  syncGitHubTokenPropagationForAgents
33559
33608
  };
33560
33609
  //# sourceMappingURL=index.js.map