paperclip-github-plugin 0.9.9 → 0.9.11

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
@@ -19,7 +19,7 @@ With this plugin, you can:
19
19
  - import open GitHub issues into Paperclip without adding title prefixes or duplicate issues
20
20
  - keep descriptions, labels, and status aligned with GitHub over time
21
21
  - configure mappings and import defaults per Paperclip company
22
- - on authenticated Paperclip deployments, choose exactly which company agents should receive the saved GitHub token as `GITHUB_TOKEN`
22
+ - choose exactly which company agents should receive the saved GitHub token as `GITHUB_TOKEN`
23
23
  - run sync manually or on a schedule
24
24
  - triage open pull requests from mapped Paperclip projects in a hosted queue
25
25
  - give Paperclip agents native GitHub tools for issues, pull requests, CI, review threads, and org-level projects
@@ -29,13 +29,13 @@ With this plugin, you can:
29
29
  The plugin adds a full in-host workflow instead of a one-off import script:
30
30
 
31
31
  - a hosted settings page for GitHub auth, repository mappings, company defaults, execution-policy handoff fallbacks, and sync controls
32
- - authenticated-only setup controls for Paperclip board access and company-scoped agent token propagation
32
+ - setup controls for Paperclip board access and company-scoped agent token propagation
33
33
  - a dashboard widget that shows sync readiness, current sync status, and run/cancel controls
34
34
  - a separate KPI dashboard widget that tracks GitHub backlog size, GitHub issues closed, and Paperclip pull requests created with recent history and historical comparisons
35
35
  - saved sync diagnostics that let operators inspect the latest per-issue failures, raw errors, and suggested next steps
36
36
  - 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
37
37
  - manual sync actions from global, project, and issue surfaces
38
- - a GitHub detail tab on synced Paperclip issues that includes GitHub-marked action buttons plus the GitHub issue creator with avatar, and lets operators manually link or unlink a Paperclip issue from a GitHub issue or pull request
38
+ - a GitHub detail tab on synced Paperclip issues that includes GitHub-marked action buttons plus the GitHub issue creator with avatar, lets operators manually link or unlink a Paperclip issue from a GitHub issue or pull request, and shows compact troubleshooting details if the host cannot provide a resolvable issue context
39
39
  - GitHub link annotations on sync-generated status transition comments when the host supports comment annotations
40
40
 
41
41
  ## How it works
@@ -137,8 +137,8 @@ npx paperclipai plugin install --local "$PWD"
137
137
 
138
138
  1. Open the plugin settings for **GitHub Sync** from inside the Paperclip company you want to configure.
139
139
  2. Paste a GitHub token, validate it, and save it.
140
- 3. If the deployment is authenticated, connect Paperclip board access from the same settings page and complete the approval flow.
141
- 4. If the deployment is authenticated, choose which agents in the current company should receive the saved GitHub token as `GITHUB_TOKEN`.
140
+ 3. If the deployment is authenticated or local trusted, connect Paperclip board access from the same settings page and complete the approval flow when host API calls need board credentials.
141
+ 4. Choose which agents in the current company should receive the saved GitHub token as `GITHUB_TOKEN`.
142
142
  5. Add one or more repository mappings for the current company.
143
143
  6. For each mapping, either choose an existing GitHub-linked Paperclip project or enter the project name that should receive synced issues.
144
144
  7. Optionally configure company-wide defaults for imported issues, including the default assignee, the default Paperclip status, executor/reviewer/approver handoff assignees for sync-driven transitions, and ignored GitHub usernames. When Paperclip board access is connected, each assignee dropdown also offers `Me` for the connected board user. `Automatic routing` means GitHub Sync follows the issue's Paperclip execution policy first and only uses the saved fallback when Paperclip does not expose the next reviewer, approver, or return assignee yet. Bot aliases such as `renovate[bot]` are matched when you save `renovate`.
@@ -197,10 +197,10 @@ The plugin is designed to avoid persisting raw credentials in plugin state.
197
197
  - GitHub tokens saved through the UI are stored as per-company Paperclip secret references.
198
198
  - Paperclip board access tokens are also stored as per-company secret references.
199
199
  - The settings UI also keeps lightweight non-secret identity labels for those saved connections, so later visits can still show who each company GitHub token and board access are connected as.
200
- - On authenticated deployments, any selected propagation agents receive `GITHUB_TOKEN` as an agent env secret-ref binding that points at the same saved GitHub token secret instead of a copied raw token.
200
+ - Any selected propagation agents receive `GITHUB_TOKEN` as an agent env secret-ref binding that points at the same saved GitHub token secret instead of a copied raw token.
201
201
  - The worker resolves those secret references at runtime instead of storing raw tokens in plugin state.
202
202
  - When the current Paperclip host rejects plugin secret refs, GitHub Sync keeps company-scoped worker-local compatibility copies for GitHub tokens and Paperclip board-access tokens in `${PAPERCLIP_HOME:-~/.paperclip}/plugins/github-sync/config.json`. Reconnect board access once after upgrading if sync still cannot authenticate Paperclip label or issue REST calls.
203
- - On authenticated Paperclip deployments, sync is blocked until the relevant company has connected Paperclip board access.
203
+ - On authenticated Paperclip deployments, sync is blocked until the relevant company has connected Paperclip board access. On local trusted deployments, board access setup remains visible so operators can configure it for host API paths that still require board credentials, but missing board access does not by itself block sync preflight.
204
204
  - KPI API route requests must include `Authorization: Bearer <PAPERCLIP_API_KEY>` from an agent run; the Paperclip host authenticates the token and supplies the agent company before the worker records any metric event.
205
205
 
206
206
  ### Optional worker-local token file
@@ -225,7 +225,7 @@ Notes:
225
225
  - The raw token is never persisted back into plugin state or plugin config.
226
226
  - A GitHub token secret saved through the settings UI is the primary source. If the current Paperclip host rejects plugin secret-ref resolution while company-scoped plugin config is unavailable, GitHub Sync stores the validated token in `githubTokensByCompanyId` as a worker-local compatibility fallback.
227
227
  - A Paperclip board access secret saved through the settings UI is also the primary source. If the host cannot resolve it for plugin workers, reconnecting board access stores the approved board token in `paperclipBoardApiTokensByCompanyId` as a worker-local compatibility fallback for direct Paperclip REST calls.
228
- - On authenticated deployments, selected agents receive `GITHUB_TOKEN` as a latest-version secret-ref env binding, and the settings UI patches agent adapter config with `replaceAdapterConfig: true` so newer Paperclip hosts persist the merged env map.
228
+ - Selected agents receive `GITHUB_TOKEN` as a latest-version secret-ref env binding, and the settings UI patches agent adapter config with `replaceAdapterConfig: true` so newer Paperclip hosts persist the merged env map.
229
229
 
230
230
  ### Worker-facing Paperclip API URL
231
231
 
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.9.9"?.trim() || typeof packageJson.version === "string" && packageJson.version.trim() || process.env.npm_package_version?.trim() || "0.0.0-dev";
645
+ var MANIFEST_VERSION = "0.9.11"?.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/ui/index.js CHANGED
@@ -11996,7 +11996,7 @@ var urlAttributes = {
11996
11996
  ]
11997
11997
  };
11998
11998
 
11999
- // node_modules/.pnpm/react-markdown@10.1.0_@types+react@19.2.14_react@19.2.6/node_modules/react-markdown/lib/index.js
11999
+ // node_modules/.pnpm/react-markdown@10.1.0_@types+react@19.2.15_react@19.2.6/node_modules/react-markdown/lib/index.js
12000
12000
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
12001
12001
  import { useEffect, useState } from "react";
12002
12002
 
@@ -19152,7 +19152,7 @@ function isUint8Array2(value) {
19152
19152
  );
19153
19153
  }
19154
19154
 
19155
- // node_modules/.pnpm/react-markdown@10.1.0_@types+react@19.2.14_react@19.2.6/node_modules/react-markdown/lib/index.js
19155
+ // node_modules/.pnpm/react-markdown@10.1.0_@types+react@19.2.15_react@19.2.6/node_modules/react-markdown/lib/index.js
19156
19156
  var changelog = "https://github.com/remarkjs/react-markdown/blob/main/changelog.md";
19157
19157
  var emptyPlugins = [];
19158
19158
  var emptyRemarkRehypeOptions = { allowDangerousHtml: true };
@@ -22408,10 +22408,21 @@ function normalizePaperclipHealthResponse(value) {
22408
22408
  ...authReady !== void 0 ? { authReady } : {}
22409
22409
  };
22410
22410
  }
22411
- function requiresPaperclipBoardAccess(value) {
22412
- const health = normalizePaperclipHealthResponse(value);
22411
+ function requiresPaperclipBoardAccessForHealth(health) {
22413
22412
  return health?.deploymentMode?.toLowerCase() === "authenticated";
22414
22413
  }
22414
+ function shouldShowPaperclipBoardAccessSettingsForHealth(health) {
22415
+ const deploymentMode = health?.deploymentMode?.toLowerCase();
22416
+ return deploymentMode === "authenticated" || deploymentMode === "local_trusted";
22417
+ }
22418
+ function resolvePaperclipAuthControlsPolicy(value) {
22419
+ const health = normalizePaperclipHealthResponse(value);
22420
+ return {
22421
+ boardAccessRequired: requiresPaperclipBoardAccessForHealth(health),
22422
+ boardAccessSettingsVisible: shouldShowPaperclipBoardAccessSettingsForHealth(health),
22423
+ githubTokenPropagationSettingsVisible: true
22424
+ };
22425
+ }
22415
22426
 
22416
22427
  // src/ui/assignees.ts
22417
22428
  function normalizeCompanyAssigneeOptionsResponse(response) {
@@ -23098,7 +23109,12 @@ function getSyncSetupMessage(issue, hasCompanyContext) {
23098
23109
  }
23099
23110
  }
23100
23111
  function usePaperclipBoardAccessRequirement() {
23101
- const [status, setStatus] = useState2("loading");
23112
+ const [state, setState] = useState2({
23113
+ status: "loading",
23114
+ required: false,
23115
+ settingsVisible: false,
23116
+ githubTokenPropagationSettingsVisible: true
23117
+ });
23102
23118
  useEffect2(() => {
23103
23119
  let cancelled = false;
23104
23120
  void (async () => {
@@ -23107,19 +23123,28 @@ function usePaperclipBoardAccessRequirement() {
23107
23123
  return;
23108
23124
  }
23109
23125
  if (!health) {
23110
- setStatus("unknown");
23126
+ const policy2 = resolvePaperclipAuthControlsPolicy(null);
23127
+ setState({
23128
+ status: "unknown",
23129
+ required: policy2.boardAccessRequired,
23130
+ settingsVisible: policy2.boardAccessSettingsVisible,
23131
+ githubTokenPropagationSettingsVisible: policy2.githubTokenPropagationSettingsVisible
23132
+ });
23111
23133
  return;
23112
23134
  }
23113
- setStatus(requiresPaperclipBoardAccess(health) ? "required" : "not_required");
23135
+ const policy = resolvePaperclipAuthControlsPolicy(health);
23136
+ setState({
23137
+ status: policy.boardAccessRequired ? "required" : "not_required",
23138
+ required: policy.boardAccessRequired,
23139
+ settingsVisible: policy.boardAccessSettingsVisible,
23140
+ githubTokenPropagationSettingsVisible: policy.githubTokenPropagationSettingsVisible
23141
+ });
23114
23142
  })();
23115
23143
  return () => {
23116
23144
  cancelled = true;
23117
23145
  };
23118
23146
  }, []);
23119
- return {
23120
- status,
23121
- required: status === "required"
23122
- };
23147
+ return state;
23123
23148
  }
23124
23149
  function getGitHubRateLimitResourceLabel(resource) {
23125
23150
  switch (resource?.trim().toLowerCase()) {
@@ -26760,6 +26785,15 @@ var EXTENSION_SURFACE_STYLES = `
26760
26785
  line-height: 1.5;
26761
26786
  }
26762
26787
 
26788
+ .ghsync-extension-note p,
26789
+ .ghsync-extension-diagnostics {
26790
+ margin: 8px 0 0;
26791
+ }
26792
+
26793
+ .ghsync-extension-diagnostics {
26794
+ padding-left: 18px;
26795
+ }
26796
+
26763
26797
  .ghsync-issue-detail {
26764
26798
  display: grid;
26765
26799
  gap: 16px;
@@ -28853,7 +28887,7 @@ function getIssueIdentifierFromLocation(pathname) {
28853
28887
  }
28854
28888
  function useResolvedIssueId(params) {
28855
28889
  const pathname = typeof window === "undefined" ? "" : window.location.pathname;
28856
- const issueIdentifier = params.entityType === "issue" ? getIssueIdentifierFromLocation(pathname) : null;
28890
+ const issueIdentifier = getIssueIdentifierFromLocation(pathname);
28857
28891
  const resolution = usePluginData("issue.resolveByIdentifier", {
28858
28892
  ...params.companyId && issueIdentifier ? { companyId: params.companyId } : {},
28859
28893
  ...params.projectId && issueIdentifier ? { projectId: params.projectId } : {},
@@ -28870,16 +28904,19 @@ function useResolvedIssueId(params) {
28870
28904
  }
28871
28905
  }, [issueIdentifier, params.companyId, params.projectId, resolution.refresh]);
28872
28906
  if (issueIdentifier) {
28907
+ const contextIssueId = params.entityId && params.entityId !== issueIdentifier ? params.entityId : null;
28873
28908
  return {
28874
- issueId: resolution.data?.issueId ?? null,
28909
+ issueId: resolution.data?.issueId ?? contextIssueId,
28875
28910
  issueIdentifier,
28876
- loading: resolution.loading && !resolution.data
28911
+ loading: resolution.loading && !resolution.data && !contextIssueId,
28912
+ errorMessage: resolution.error?.message ?? null
28877
28913
  };
28878
28914
  }
28879
28915
  return {
28880
- issueId: params.entityId ?? null,
28916
+ issueId: params.entityType === "issue" || !params.entityType ? params.entityId ?? null : null,
28881
28917
  issueIdentifier: null,
28882
- loading: false
28918
+ loading: false,
28919
+ errorMessage: null
28883
28920
  };
28884
28921
  }
28885
28922
  function formatSyncProgressRepository(repositoryUrl) {
@@ -32384,6 +32421,8 @@ function GitHubSyncSettingsPage() {
32384
32421
  const hasSavedToken = Boolean(form.githubTokenConfigured || showSavedTokenHint);
32385
32422
  const boardAccessConfigured = Boolean(form.paperclipBoardAccessConfigured);
32386
32423
  const boardAccessRequired = boardAccessRequirement.required;
32424
+ const boardAccessSettingsVisible = boardAccessRequirement.settingsVisible;
32425
+ const githubTokenPropagationSettingsVisible = boardAccessRequirement.githubTokenPropagationSettingsVisible;
32387
32426
  const boardAccessReady = !boardAccessRequired || hasCompanyContext && boardAccessConfigured;
32388
32427
  const tokenStatus = tokenStatusOverride ?? (hasSavedToken ? "valid" : "required");
32389
32428
  const tokenTone = tokenStatus === "valid" ? "success" : tokenStatus === "invalid" ? "danger" : "warning";
@@ -32492,7 +32531,7 @@ function GitHubSyncSettingsPage() {
32492
32531
  const manualSyncScopePillLabel = hasCompanyContext ? "This company" : "All companies";
32493
32532
  const manualSyncButtonLabel = hasCompanyContext ? "Run sync for this company" : "Run sync across all companies";
32494
32533
  const advancedSettingsSummary = formatAdvancedSettingsSummary(form.advancedSettings, availableAssignees, {
32495
- includePropagation: boardAccessRequired
32534
+ includePropagation: githubTokenPropagationSettingsVisible
32496
32535
  });
32497
32536
  const assigneeSelectOptions = [
32498
32537
  { value: "", label: "Unassigned" },
@@ -32646,9 +32685,6 @@ function GitHubSyncSettingsPage() {
32646
32685
  });
32647
32686
  }
32648
32687
  async function propagateGitHubTokenToSelectedAgents(options) {
32649
- if (!boardAccessRequired) {
32650
- return;
32651
- }
32652
32688
  const selectedAgentIds = normalizeAgentIds(options.selectedAgentIds);
32653
32689
  const previousAgentIds = normalizeAgentIds(options.previousAgentIds);
32654
32690
  if (selectedAgentIds.length === 0 && previousAgentIds.length === 0) {
@@ -32855,7 +32891,7 @@ function GitHubSyncSettingsPage() {
32855
32891
  }));
32856
32892
  toast({
32857
32893
  title: boardIdentity.label ? `Paperclip board access connected as ${boardIdentity.label}` : "Paperclip board access connected",
32858
- body: "Direct Paperclip REST calls can now authenticate in authenticated deployments.",
32894
+ body: "Direct Paperclip REST calls can now authenticate when the host requires board access.",
32859
32895
  tone: "success"
32860
32896
  });
32861
32897
  notifyGitHubSyncSettingsChanged();
@@ -33224,7 +33260,7 @@ function GitHubSyncSettingsPage() {
33224
33260
  /* @__PURE__ */ jsx2("div", { className: "ghsync__permission-audit-list", children: /* @__PURE__ */ jsx2("div", { className: "ghsync__permission-audit-item", children: /* @__PURE__ */ jsx2("span", { children: tokenPermissionAuditData?.warnings[0] ?? "Add a mapped repository in this company so GitHub Sync can verify the token permissions it needs." }) }) })
33225
33261
  ] }) : null
33226
33262
  ] }),
33227
- boardAccessRequired ? /* @__PURE__ */ jsxs2("section", { className: "ghsync__section", children: [
33263
+ boardAccessSettingsVisible ? /* @__PURE__ */ jsxs2("section", { className: "ghsync__section", children: [
33228
33264
  /* @__PURE__ */ jsxs2("div", { className: "ghsync__section-head", children: [
33229
33265
  /* @__PURE__ */ jsxs2("div", { className: "ghsync__section-copy", children: [
33230
33266
  /* @__PURE__ */ jsxs2("div", { className: "ghsync__section-title-row", children: [
@@ -33565,7 +33601,7 @@ function GitHubSyncSettingsPage() {
33565
33601
  ),
33566
33602
  /* @__PURE__ */ jsx2("p", { className: "ghsync__hint", children: "Comma or newline separated." })
33567
33603
  ] }),
33568
- boardAccessRequired ? /* @__PURE__ */ jsxs2("div", { className: "ghsync__field", children: [
33604
+ githubTokenPropagationSettingsVisible ? /* @__PURE__ */ jsxs2("div", { className: "ghsync__field", children: [
33569
33605
  /* @__PURE__ */ jsx2("label", { htmlFor: "advanced-token-propagation", children: "Propagate GitHub token to agents" }),
33570
33606
  /* @__PURE__ */ jsx2(
33571
33607
  SettingsAgentMultiPicker,
@@ -33769,7 +33805,7 @@ function GitHubSyncSettingsPage() {
33769
33805
  ] }),
33770
33806
  /* @__PURE__ */ jsx2("span", { children: !repositoriesUnlocked ? "Requires a token." : savedMappingCount > 0 ? hasCompanyContext ? `${savedMappingCount} saved.` : `${savedMappingCount} saved.` : hasCompanyContext ? "Add a repository." : "Select a company." })
33771
33807
  ] }),
33772
- boardAccessRequired ? /* @__PURE__ */ jsxs2("div", { className: "ghsync__check", children: [
33808
+ boardAccessSettingsVisible ? /* @__PURE__ */ jsxs2("div", { className: "ghsync__check", children: [
33773
33809
  /* @__PURE__ */ jsxs2("div", { className: "ghsync__check-top", children: [
33774
33810
  /* @__PURE__ */ jsx2("strong", { children: "Paperclip board access" }),
33775
33811
  /* @__PURE__ */ jsx2("span", { className: `ghsync__badge ${getToneClass(boardAccessStatusTone)}`, children: boardAccessStatusLabel })
@@ -34874,8 +34910,7 @@ function GitHubSyncEntityToolbarButton() {
34874
34910
  }
34875
34911
  function GitHubSyncIssueDetailTabContent(props) {
34876
34912
  const details = usePluginData("issue.githubDetails", {
34877
- ...props.companyId ? { companyId: props.companyId } : {},
34878
- ...props.issueId ? { issueId: props.issueId } : {}
34913
+ ...props.companyId && props.issueId ? { companyId: props.companyId, issueId: props.issueId } : {}
34879
34914
  });
34880
34915
  const issueDetails = details.data?.paperclipIssueId === props.issueId ? details.data : null;
34881
34916
  const detailTabState = resolveGitHubIssueDetailTabState({
@@ -34883,7 +34918,10 @@ function GitHubSyncIssueDetailTabContent(props) {
34883
34918
  detailsLoading: details.loading,
34884
34919
  detailsError: Boolean(details.error),
34885
34920
  issueDetails,
34886
- canLinkManually: Boolean(props.companyId && props.issueId)
34921
+ canLinkManually: Boolean(props.companyId && props.issueId),
34922
+ issueIdentifier: props.issueIdentifier,
34923
+ issueResolutionError: Boolean(props.issueResolutionErrorMessage),
34924
+ expectedIssueContext: Boolean(props.expectedIssueContext)
34887
34925
  });
34888
34926
  const issueSyncButton = useGitHubSyncButtonController({
34889
34927
  companyId: props.companyId,
@@ -34990,7 +35028,34 @@ function GitHubSyncIssueDetailTabContent(props) {
34990
35028
  return /* @__PURE__ */ jsxs2("section", { className: "ghsync-issue-detail", style: props.themeVars, children: [
34991
35029
  /* @__PURE__ */ jsx2("style", { children: EXTENSION_SURFACE_STYLES }),
34992
35030
  detailTabState === "loading" ? /* @__PURE__ */ jsx2("p", { className: "ghsync-extension-empty", children: "Loading GitHub sync details\u2026" }) : null,
34993
- detailTabState === "error" && details.error ? /* @__PURE__ */ jsx2("p", { className: "ghsync-extension-empty", children: details.error.message }) : null,
35031
+ detailTabState === "error" ? /* @__PURE__ */ jsxs2("div", { className: "ghsync-issue-detail__intro", children: [
35032
+ /* @__PURE__ */ jsx2("div", { className: "ghsync-extension-heading", children: /* @__PURE__ */ jsxs2("div", { className: "ghsync-issue-detail__headline", children: [
35033
+ /* @__PURE__ */ jsx2("h4", { children: "GitHub sync details unavailable" }),
35034
+ /* @__PURE__ */ jsx2("p", { children: details.error?.message ?? props.issueResolutionErrorMessage ?? "GitHub Sync could not load this issue detail view." })
35035
+ ] }) }),
35036
+ /* @__PURE__ */ jsx2(
35037
+ GitHubIssueDetailTroubleshooting,
35038
+ {
35039
+ companyId: props.companyId,
35040
+ issueId: props.issueId,
35041
+ issueIdentifier: props.issueIdentifier
35042
+ }
35043
+ )
35044
+ ] }) : null,
35045
+ detailTabState === "unresolved" ? /* @__PURE__ */ jsxs2("div", { className: "ghsync-issue-detail__intro", children: [
35046
+ /* @__PURE__ */ jsx2("div", { className: "ghsync-extension-heading", children: /* @__PURE__ */ jsxs2("div", { className: "ghsync-issue-detail__headline", children: [
35047
+ /* @__PURE__ */ jsx2("h4", { children: "GitHub sync details unavailable" }),
35048
+ /* @__PURE__ */ jsx2("p", { children: "GitHub Sync could not resolve the current Paperclip issue context." })
35049
+ ] }) }),
35050
+ /* @__PURE__ */ jsx2(
35051
+ GitHubIssueDetailTroubleshooting,
35052
+ {
35053
+ companyId: props.companyId,
35054
+ issueId: props.issueId,
35055
+ issueIdentifier: props.issueIdentifier
35056
+ }
35057
+ )
35058
+ ] }) : null,
34994
35059
  detailTabState === "unlinked" ? /* @__PURE__ */ jsx2("div", { className: "ghsync-issue-detail__intro", children: /* @__PURE__ */ jsxs2("div", { className: "ghsync-extension-heading", children: [
34995
35060
  /* @__PURE__ */ jsxs2("div", { className: "ghsync-issue-detail__headline", children: [
34996
35061
  /* @__PURE__ */ jsx2("h4", { children: "GitHub link" }),
@@ -35230,16 +35295,59 @@ function resolveGitHubIssueDetailTabState(params) {
35230
35295
  if (params.issueDetails) {
35231
35296
  return "ready";
35232
35297
  }
35233
- if (params.detailsError) {
35298
+ if (params.detailsError || params.issueResolutionError) {
35234
35299
  return "error";
35235
35300
  }
35236
35301
  if (params.canLinkManually) {
35237
35302
  return "unlinked";
35238
35303
  }
35304
+ if (params.issueIdentifier || params.expectedIssueContext) {
35305
+ return "unresolved";
35306
+ }
35239
35307
  return "hidden";
35240
35308
  }
35241
- function GitHubSyncIssueTaskDetailView() {
35242
- const context = useHostContext();
35309
+ function getContextStringValue(context, key) {
35310
+ const value = context?.[key];
35311
+ return typeof value === "string" && value.trim() ? value.trim() : null;
35312
+ }
35313
+ function resolveGitHubIssueTaskDetailContext(params) {
35314
+ const companyId = getContextStringValue(params.slotContext, "companyId") ?? getContextStringValue(params.hostContext, "companyId");
35315
+ const projectId = getContextStringValue(params.slotContext, "projectId") ?? getContextStringValue(params.hostContext, "projectId");
35316
+ const entityId = getContextStringValue(params.slotContext, "entityId") ?? getContextStringValue(params.hostContext, "entityId");
35317
+ const entityType = getContextStringValue(params.slotContext, "entityType") ?? getContextStringValue(params.hostContext, "entityType");
35318
+ return {
35319
+ ...companyId ? { companyId } : {},
35320
+ ...projectId ? { projectId } : {},
35321
+ ...entityId ? { entityId } : {},
35322
+ ...entityType ? { entityType } : {}
35323
+ };
35324
+ }
35325
+ function shouldExpectGitHubIssueTaskDetailContext(params) {
35326
+ if (params.issueIdentifier) {
35327
+ return true;
35328
+ }
35329
+ if (params.entityType === "issue") {
35330
+ return true;
35331
+ }
35332
+ return !params.entityType && Boolean(params.entityId);
35333
+ }
35334
+ function GitHubIssueDetailTroubleshooting(props) {
35335
+ return /* @__PURE__ */ jsxs2("div", { className: "ghsync-extension-note", children: [
35336
+ /* @__PURE__ */ jsx2("strong", { children: "Troubleshooting" }),
35337
+ /* @__PURE__ */ jsxs2("ul", { className: "ghsync-extension-diagnostics", children: [
35338
+ /* @__PURE__ */ jsx2("li", { children: props.companyId ? `Company context: ${props.companyId}` : "Missing company context from the host slot." }),
35339
+ /* @__PURE__ */ jsx2("li", { children: props.issueId ? `Issue id: ${props.issueId}` : "Missing canonical Paperclip issue id." }),
35340
+ props.issueIdentifier ? /* @__PURE__ */ jsx2("li", { children: `Route issue identifier: ${props.issueIdentifier}` }) : null
35341
+ ] }),
35342
+ /* @__PURE__ */ jsx2("p", { children: "Refresh the issue page, then reopen this detail view. If this remains unresolved, include these values when reporting the GitHub Sync plugin issue." })
35343
+ ] });
35344
+ }
35345
+ function GitHubSyncIssueTaskDetailView(props) {
35346
+ const hostContext = useHostContext();
35347
+ const context = resolveGitHubIssueTaskDetailContext({
35348
+ slotContext: props?.context ?? null,
35349
+ hostContext
35350
+ });
35243
35351
  const themeMode = useResolvedThemeMode();
35244
35352
  const theme = themeMode === "light" ? LIGHT_PALETTE : DARK_PALETTE;
35245
35353
  const themeVars = buildThemeVars(theme, themeMode);
@@ -35249,13 +35357,21 @@ function GitHubSyncIssueTaskDetailView() {
35249
35357
  entityId: context.entityId,
35250
35358
  entityType: context.entityType
35251
35359
  });
35360
+ const expectedIssueContext = shouldExpectGitHubIssueTaskDetailContext({
35361
+ entityId: context.entityId,
35362
+ entityType: context.entityType,
35363
+ issueIdentifier: resolvedIssue.issueIdentifier
35364
+ });
35252
35365
  const detailKey = `${context.companyId ?? "company-none"}:${resolvedIssue.issueIdentifier ?? context.entityId ?? "issue-none"}`;
35253
35366
  return /* @__PURE__ */ jsx2(
35254
35367
  GitHubSyncIssueDetailTabContent,
35255
35368
  {
35256
35369
  companyId: context.companyId,
35257
35370
  issueId: resolvedIssue.issueId,
35371
+ issueIdentifier: resolvedIssue.issueIdentifier,
35372
+ expectedIssueContext,
35258
35373
  loadingIssueId: resolvedIssue.loading,
35374
+ issueResolutionErrorMessage: resolvedIssue.errorMessage,
35259
35375
  themeVars
35260
35376
  },
35261
35377
  detailKey
@@ -35316,11 +35432,13 @@ export {
35316
35432
  index_default as default,
35317
35433
  patchPluginConfig,
35318
35434
  resolveGitHubIssueDetailTabState,
35435
+ resolveGitHubIssueTaskDetailContext,
35319
35436
  resolveGitHubTokenSecretRefForPropagation,
35320
35437
  resolveOrCreateProject,
35321
35438
  resolvePreviewPersonLabels,
35322
35439
  resolveSavedTokenUiState,
35323
35440
  resolveToolbarButtonState,
35441
+ shouldExpectGitHubIssueTaskDetailContext,
35324
35442
  syncGitHubTokenPropagationForAgents
35325
35443
  };
35326
35444
  //# sourceMappingURL=index.js.map