@norman-else/dsh-claude 0.1.22 → 0.1.23

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/lib/client.js CHANGED
@@ -4,7 +4,7 @@ window.__ModuleLoader__.load({
4
4
  var module = { exports: {} };
5
5
  module.exports;
6
6
  var { Fragment, useCallback, useEffect, useId, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } = require("react");
7
- var { DiffBlock, DisclosureRow, HoverCard, IconApiOutline14, IconBranchOutline16, IconCheckOutline14, IconChevronDownOutline14, IconCloseOutline16, IconFullscreenOutline16, IconSearchOutline16, IconThinkOutline14, MarkdownText, Menu, Modal, StateDot } = require("@deepseek-ai/dsh-client-ui-primitives");
7
+ var { DiffBlock, DisclosureRow, HoverCard, IconApiOutline14, IconBranchOutline16, IconCheckOutline14, IconChevronDownOutline14, IconCloseOutline16, IconFullscreenOutline16, IconSearchOutline16, IconThinkOutline14, MarkdownText, Menu, Modal, StateDot, Tooltip } = require("@deepseek-ai/dsh-client-ui-primitives");
8
8
  var { Fragment: Fragment$1, jsx, jsxs } = require("react/jsx-runtime");
9
9
  var { createPortal } = require("react-dom");
10
10
  /** Claude's subagent dispatch tools; rendered as plugin-owned group cards
@@ -18,6 +18,7 @@ window.__ModuleLoader__.load({
18
18
  const CLAUDE_REPOSITORY_SETUP_PATH = "/plugins/dsh-claude/repository/setup";
19
19
  const CLAUDE_REPOSITORY_ACTION_PATH = "/plugins/dsh-claude/repository/action";
20
20
  const CLAUDE_REVIEW_COMMENT_PATH = "/plugins/dsh-claude/review-comments";
21
+ const CLAUDE_REPOSITORY_FEEDBACK_PATH = "/plugins/dsh-claude/repository/feedback";
21
22
  //#endregion
22
23
  //#region src/client/task-projection.ts
23
24
  /** Tasks UI is reserved for detached work and genuine Claude subagents. */
@@ -657,8 +658,7 @@ window.__ModuleLoader__.load({
657
658
  flex: 1,
658
659
  overflow: "hidden",
659
660
  textOverflow: "ellipsis",
660
- whiteSpace: "nowrap",
661
- fontWeight: 550
661
+ whiteSpace: "nowrap"
662
662
  };
663
663
  const settingSelectChevron = {
664
664
  flex: "none",
@@ -1590,6 +1590,11 @@ window.__ModuleLoader__.load({
1590
1590
  fontWeight: 550,
1591
1591
  whiteSpace: "nowrap"
1592
1592
  };
1593
+ const repositoryItemLabel = {
1594
+ minWidth: 0,
1595
+ overflow: "hidden",
1596
+ textOverflow: "ellipsis"
1597
+ };
1593
1598
  const repositoryItemDot = {
1594
1599
  width: 6,
1595
1600
  height: 6,
@@ -1645,6 +1650,176 @@ window.__ModuleLoader__.load({
1645
1650
  background: "transparent",
1646
1651
  color: "var(--dsw-alias-label-tertiary)"
1647
1652
  };
1653
+ const repositoryAutoFix = {
1654
+ width: 26,
1655
+ minHeight: 26,
1656
+ flex: "none",
1657
+ display: "inline-flex",
1658
+ alignItems: "center",
1659
+ justifyContent: "center",
1660
+ padding: 0,
1661
+ border: "1px solid var(--dsw-alias-border-l2, color-mix(in srgb, currentColor 16%, transparent))",
1662
+ borderRadius: 7,
1663
+ outline: "none",
1664
+ background: "var(--dsw-alias-bg-layer-2)",
1665
+ color: "var(--dsw-alias-label-tertiary)",
1666
+ font: "inherit",
1667
+ lineHeight: "18px",
1668
+ cursor: "pointer"
1669
+ };
1670
+ const repositoryAutoFixActive = {
1671
+ borderColor: "color-mix(in srgb, var(--dsw-static-blue-450) 45%, transparent)",
1672
+ background: "color-mix(in srgb, var(--dsw-static-blue-450) 12%, transparent)",
1673
+ color: "var(--dsw-static-blue-450)"
1674
+ };
1675
+ const repositoryUpdateTrigger = {
1676
+ flex: "none",
1677
+ display: "inline-flex",
1678
+ alignItems: "center",
1679
+ gap: 4,
1680
+ minHeight: 26,
1681
+ padding: "3px 8px",
1682
+ border: "1px solid color-mix(in srgb, var(--dsw-static-blue-450) 45%, transparent)",
1683
+ borderRadius: 7,
1684
+ background: "color-mix(in srgb, var(--dsw-static-blue-450) 12%, transparent)",
1685
+ color: "var(--dsw-static-blue-450)",
1686
+ font: "inherit",
1687
+ fontSize: 12,
1688
+ lineHeight: "18px",
1689
+ fontWeight: 650,
1690
+ whiteSpace: "nowrap",
1691
+ cursor: "pointer"
1692
+ };
1693
+ const repositoryChecksFrame = {
1694
+ position: "relative",
1695
+ display: "inline-flex",
1696
+ minWidth: 0
1697
+ };
1698
+ const repositoryChecksTrigger = {
1699
+ display: "inline-flex",
1700
+ alignItems: "center",
1701
+ minWidth: 0,
1702
+ padding: 0,
1703
+ border: "none",
1704
+ background: "transparent",
1705
+ font: "inherit",
1706
+ color: "inherit",
1707
+ cursor: "pointer"
1708
+ };
1709
+ const repositoryChecksCard = {
1710
+ position: "absolute",
1711
+ zIndex: 1100,
1712
+ bottom: "calc(100% + 8px)",
1713
+ right: 0,
1714
+ width: 320,
1715
+ boxSizing: "border-box",
1716
+ display: "flex",
1717
+ flexDirection: "column",
1718
+ alignItems: "stretch",
1719
+ gap: 8,
1720
+ padding: 12,
1721
+ border: "1px solid var(--dsw-alias-border-l2, color-mix(in srgb, currentColor 16%, transparent))",
1722
+ borderRadius: 10,
1723
+ background: "var(--dsw-specific-menu, var(--dsw-alias-bg-layer-1))",
1724
+ boxShadow: "var(--dsw-shadow-lv3)",
1725
+ textAlign: "left",
1726
+ cursor: "default"
1727
+ };
1728
+ const repositoryChecksTitle = {
1729
+ color: "var(--dsw-alias-label-primary)",
1730
+ fontSize: 12,
1731
+ fontWeight: 650
1732
+ };
1733
+ const repositoryChecksHint = {
1734
+ color: "var(--dsw-alias-label-tertiary)",
1735
+ fontSize: 12
1736
+ };
1737
+ const repositoryChecksError = {
1738
+ color: "var(--dsw-alias-state-error-primary)",
1739
+ fontSize: 12
1740
+ };
1741
+ const repositoryChecksItem = {
1742
+ display: "flex",
1743
+ flexDirection: "column",
1744
+ gap: 2,
1745
+ minWidth: 0
1746
+ };
1747
+ const repositoryChecksName = {
1748
+ color: "var(--dsw-alias-label-primary)",
1749
+ fontSize: 12,
1750
+ fontWeight: 600,
1751
+ overflow: "hidden",
1752
+ textOverflow: "ellipsis",
1753
+ whiteSpace: "nowrap"
1754
+ };
1755
+ const repositoryChecksDesc = {
1756
+ color: "var(--dsw-alias-label-tertiary)",
1757
+ fontSize: 11,
1758
+ overflow: "hidden",
1759
+ textOverflow: "ellipsis",
1760
+ whiteSpace: "nowrap"
1761
+ };
1762
+ const repositoryChecksFix = {
1763
+ alignSelf: "flex-end",
1764
+ minHeight: 26,
1765
+ padding: "3px 10px",
1766
+ border: "none",
1767
+ borderRadius: 7,
1768
+ background: "var(--dsw-static-blue-450)",
1769
+ color: "var(--dsw-alias-label-on-accent, #fff)",
1770
+ font: "inherit",
1771
+ fontSize: 12,
1772
+ fontWeight: 650,
1773
+ cursor: "pointer"
1774
+ };
1775
+ const diffPrCommentsButton = {
1776
+ minHeight: 28,
1777
+ display: "inline-flex",
1778
+ alignItems: "center",
1779
+ gap: 4,
1780
+ padding: "4px 8px",
1781
+ border: "1px solid var(--dsw-alias-border-l2, color-mix(in srgb, currentColor 16%, transparent))",
1782
+ borderRadius: 7,
1783
+ background: "var(--dsw-alias-bg-layer-2)",
1784
+ font: "inherit",
1785
+ fontSize: 12,
1786
+ fontWeight: 650,
1787
+ whiteSpace: "nowrap",
1788
+ cursor: "pointer"
1789
+ };
1790
+ const diffGhCommentAuthor = {
1791
+ color: "var(--dsw-alias-label-tertiary)",
1792
+ fontWeight: 600
1793
+ };
1794
+ const diffGhCommentLink = {
1795
+ color: "var(--dsw-static-blue-450)",
1796
+ textDecoration: "none",
1797
+ fontWeight: 650
1798
+ };
1799
+ const diffModalConflicts = {
1800
+ display: "flex",
1801
+ flexDirection: "column",
1802
+ gap: 4,
1803
+ margin: 0,
1804
+ padding: "8px 10px",
1805
+ borderRadius: 8,
1806
+ background: "var(--dsw-alias-bg-layer-2)",
1807
+ fontSize: 12,
1808
+ listStyle: "none"
1809
+ };
1810
+ const diffModalConflictResolve = {
1811
+ alignSelf: "flex-start",
1812
+ minHeight: 28,
1813
+ padding: "4px 12px",
1814
+ border: "none",
1815
+ borderRadius: 7,
1816
+ background: "var(--dsw-static-blue-450)",
1817
+ color: "var(--dsw-alias-label-on-accent, #fff)",
1818
+ font: "inherit",
1819
+ fontSize: 12,
1820
+ fontWeight: 650,
1821
+ cursor: "pointer"
1822
+ };
1648
1823
  const repositoryMergeTrigger = {
1649
1824
  flex: "none",
1650
1825
  display: "inline-flex",
@@ -1899,11 +2074,19 @@ window.__ModuleLoader__.load({
1899
2074
  display: "flex",
1900
2075
  alignItems: "center",
1901
2076
  gap: 8,
2077
+ overflow: "hidden",
1902
2078
  color: "var(--dsw-alias-label-primary)",
1903
2079
  fontSize: 15,
1904
- fontWeight: 650
2080
+ fontWeight: 650,
2081
+ whiteSpace: "nowrap"
2082
+ };
2083
+ const diffHeaderLabel = {
2084
+ minWidth: 0,
2085
+ overflow: "hidden",
2086
+ textOverflow: "ellipsis"
1905
2087
  };
1906
2088
  const diffHeaderActions = {
2089
+ flex: "none",
1907
2090
  display: "flex",
1908
2091
  alignItems: "center",
1909
2092
  gap: 5
@@ -2647,7 +2830,7 @@ window.__ModuleLoader__.load({
2647
2830
  const MAX_REVIEW_COMMENTS = 50;
2648
2831
  const MAX_REVIEW_COMMENT_CHARS = 2e3;
2649
2832
  const MAX_TRANSCRIPT_CHARS = 64e3;
2650
- function record$4(value) {
2833
+ function record$5(value) {
2651
2834
  return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
2652
2835
  }
2653
2836
  function nonNegativeInteger(value) {
@@ -2657,18 +2840,18 @@ window.__ModuleLoader__.load({
2657
2840
  return value === void 0 || typeof value === "string" && value.length <= MAX_REPOSITORY_TEXT_CHARS;
2658
2841
  }
2659
2842
  function validateRepository(value) {
2660
- const repository = record$4(value);
2843
+ const repository = record$5(value);
2661
2844
  if (repository === void 0 || ![
2662
2845
  "ready",
2663
2846
  "not-repository",
2664
2847
  "unavailable"
2665
2848
  ].includes(String(repository.status)) || typeof repository.cwd !== "string" || repository.cwd.length > MAX_REPOSITORY_TEXT_CHARS || !optionalBoundedString(repository.root) || !optionalBoundedString(repository.branch) || !optionalBoundedString(repository.remote) || repository.detached !== void 0 && typeof repository.detached !== "boolean" || repository.worktree !== void 0 && typeof repository.worktree !== "boolean" || repository.dirty !== void 0 && typeof repository.dirty !== "boolean" || repository.upstream !== void 0 && typeof repository.upstream !== "boolean" || repository.ahead !== void 0 && !nonNegativeInteger(repository.ahead) || repository.behind !== void 0 && !nonNegativeInteger(repository.behind)) return false;
2666
2849
  if (repository.diff !== void 0) {
2667
- const diff = record$4(repository.diff);
2850
+ const diff = record$5(repository.diff);
2668
2851
  if (diff === void 0 || !nonNegativeInteger(diff.additions) || !nonNegativeInteger(diff.deletions) || !nonNegativeInteger(diff.files) || typeof diff.truncated !== "boolean" || diff.patch !== void 0 && (typeof diff.patch !== "string" || diff.patch.length > MAX_DIFF_CHARS)) return false;
2669
2852
  }
2670
2853
  if (repository.pullRequest === void 0) return true;
2671
- const pullRequest = record$4(repository.pullRequest);
2854
+ const pullRequest = record$5(repository.pullRequest);
2672
2855
  if (pullRequest === void 0 || !Number.isSafeInteger(pullRequest.number) || Number(pullRequest.number) <= 0 || typeof pullRequest.title !== "string" || pullRequest.title.length > MAX_REPOSITORY_TEXT_CHARS || typeof pullRequest.url !== "string" || pullRequest.url.length > MAX_REPOSITORY_TEXT_CHARS || ![
2673
2856
  "open",
2674
2857
  "closed",
@@ -2693,24 +2876,24 @@ window.__ModuleLoader__.load({
2693
2876
  }
2694
2877
  /** Validate the public route envelope before publishing it to UI components. */
2695
2878
  function parseClaudeClientProjection(value) {
2696
- const input = record$4(value);
2879
+ const input = record$5(value);
2697
2880
  if (input === void 0 || input.schemaVersion !== 1 || !nonNegativeInteger(input.revision) || typeof input.owned !== "boolean" || !Array.isArray(input.commands) || input.commands.length > MAX_COMMANDS || !Array.isArray(input.activities) || input.activities.length > MAX_ACTIVITIES) throw new Error("invalid Claude sidecar projection");
2698
2881
  for (const item of input.commands) {
2699
- const command = record$4(item);
2882
+ const command = record$5(item);
2700
2883
  if (command === void 0 || typeof command.publicName !== "string" || typeof command.claudeName !== "string" || typeof command.description !== "string" || command.hint !== void 0 && typeof command.hint !== "string" || typeof command.prefixed !== "boolean") throw new Error("invalid Claude command projection");
2701
2884
  }
2702
2885
  for (const item of input.activities) {
2703
- const activity = record$4(item);
2886
+ const activity = record$5(item);
2704
2887
  if (activity === void 0 || !nonNegativeInteger(activity.turn) || !nonNegativeInteger(activity.step) || !nonNegativeInteger(activity.ordinal) || typeof activity.kind !== "string") throw new Error("invalid Claude sidecar activity");
2705
2888
  }
2706
- if (input.contextUsage !== void 0 && record$4(input.contextUsage) === void 0) throw new Error("invalid Claude context projection");
2707
- const tasks = input.tasks === void 0 ? void 0 : record$4(input.tasks);
2889
+ if (input.contextUsage !== void 0 && record$5(input.contextUsage) === void 0) throw new Error("invalid Claude context projection");
2890
+ const tasks = input.tasks === void 0 ? void 0 : record$5(input.tasks);
2708
2891
  if (tasks !== void 0 && !Array.isArray(tasks.tasks)) throw new Error("invalid Claude tasks projection");
2709
2892
  if (input.repository !== void 0 && !validateRepository(input.repository)) throw new Error("invalid Claude repository projection");
2710
2893
  if (input.reviewComments !== void 0) {
2711
2894
  if (!Array.isArray(input.reviewComments) || input.reviewComments.length > MAX_REVIEW_COMMENTS) throw new Error("invalid Claude review comment projection");
2712
2895
  for (const item of input.reviewComments) {
2713
- const comment = record$4(item);
2896
+ const comment = record$5(item);
2714
2897
  if (comment === void 0 || typeof comment.id !== "string" || comment.id.length === 0 || comment.id.length > 128 || typeof comment.path !== "string" || comment.path.length === 0 || comment.path.length > MAX_REPOSITORY_TEXT_CHARS || !nonNegativeInteger(comment.line) || comment.side !== "old" && comment.side !== "new" || typeof comment.text !== "string" || comment.text.length > MAX_REVIEW_COMMENT_CHARS) throw new Error("invalid Claude review comment projection");
2715
2898
  }
2716
2899
  }
@@ -2908,7 +3091,7 @@ window.__ModuleLoader__.load({
2908
3091
  } catch {
2909
3092
  return;
2910
3093
  }
2911
- const event = record$4(value);
3094
+ const event = record$5(value);
2912
3095
  if (event === void 0 || typeof event.type !== "string") return;
2913
3096
  try {
2914
3097
  switch (event.type) {
@@ -3167,7 +3350,7 @@ window.__ModuleLoader__.load({
3167
3350
  return value;
3168
3351
  }
3169
3352
  }
3170
- function record$3(value) {
3353
+ function record$4(value) {
3171
3354
  return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
3172
3355
  }
3173
3356
  function text(value) {
@@ -3181,7 +3364,7 @@ window.__ModuleLoader__.load({
3181
3364
  if (typeof value === "string") return value;
3182
3365
  if (typeof value === "number" || typeof value === "boolean") return String(value);
3183
3366
  if (Array.isArray(value)) return value.map(displayValue).join(", ");
3184
- return record$3(value) === void 0 ? String(value) : Object.entries(value).map(([key, item]) => `${key}: ${displayValue(item)}`).join("\n");
3367
+ return record$4(value) === void 0 ? String(value) : Object.entries(value).map(([key, item]) => `${key}: ${displayValue(item)}`).join("\n");
3185
3368
  }
3186
3369
  function Section({ title, children }) {
3187
3370
  return /* @__PURE__ */ jsxs("section", {
@@ -3251,15 +3434,15 @@ window.__ModuleLoader__.load({
3251
3434
  function ToolPresentation({ tool, t }) {
3252
3435
  const inputValue = parsedValue(tool.input);
3253
3436
  const outputValue = parsedValue(tool.output);
3254
- const input = record$3(inputValue);
3255
- const output = record$3(outputValue);
3437
+ const input = record$4(inputValue);
3438
+ const output = record$4(outputValue);
3256
3439
  const outputTitle = tool.isError === true ? t("toolError") : t("toolOutput");
3257
3440
  if (tool.diffs !== void 0) return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(DiffBlock, { diffs: [...tool.diffs] }), /* @__PURE__ */ jsx(TextDetail, {
3258
3441
  title: outputTitle,
3259
3442
  value: typeof outputValue === "string" ? outputValue : void 0
3260
3443
  })] });
3261
3444
  if (tool.toolName === "Read") {
3262
- const file = record$3(output?.file);
3445
+ const file = record$4(output?.file);
3263
3446
  const path = text(file?.filePath) ?? text(input?.file_path);
3264
3447
  const content = text(file?.content) ?? (typeof outputValue === "string" ? outputValue : void 0);
3265
3448
  const offset = numberValue(input?.offset) ?? 1;
@@ -3810,7 +3993,7 @@ window.__ModuleLoader__.load({
3810
3993
  style: diagnosticGrid,
3811
3994
  children: [/* @__PURE__ */ jsx("span", {
3812
3995
  style: diagnosticLabel,
3813
- children: setting.key === "outputStyle" ? t("outputStyle") : setting.key === "worktreeBranchPrefix" ? t("worktreeBranchPrefix") : setting.key
3996
+ children: setting.key === "outputStyle" ? t("outputStyle") : setting.key === "worktreeBranchPrefix" ? t("worktreeBranchPrefix") : setting.key === "maxProcesses" ? t("maxProcessesSetting") : setting.key === "idleTimeoutMinutes" ? t("idleTimeoutSetting") : setting.key
3814
3997
  }), setting.kind === "select" ? /* @__PURE__ */ jsx(GlobalSettingSelect, {
3815
3998
  setting,
3816
3999
  disabled: globalSettingsBusy,
@@ -3833,6 +4016,10 @@ window.__ModuleLoader__.load({
3833
4016
  style: notice,
3834
4017
  children: t("worktreeBranchPrefixEffect")
3835
4018
  }) : null,
4019
+ globalSettings?.settings.some((setting) => setting.key === "maxProcesses") === true ? /* @__PURE__ */ jsx("p", {
4020
+ style: notice,
4021
+ children: t("limitsSettingEffect")
4022
+ }) : null,
3836
4023
  globalSettingsError === void 0 ? null : /* @__PURE__ */ jsxs("p", {
3837
4024
  role: "alert",
3838
4025
  style: {
@@ -3963,34 +4150,34 @@ window.__ModuleLoader__.load({
3963
4150
  if (commit !== void 0) this.commit = commit;
3964
4151
  }
3965
4152
  };
3966
- function record$2(value) {
4153
+ function record$3(value) {
3967
4154
  return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
3968
4155
  }
3969
4156
  async function response$1(pending) {
3970
4157
  const result = await pending;
3971
4158
  const body = await result.json();
3972
4159
  if (!result.ok) {
3973
- const error = record$2(body);
4160
+ const error = record$3(body);
3974
4161
  throw new RepositoryActionClientError(typeof error?.message === "string" ? error.message : "Repository action failed.", typeof error?.error === "string" ? error.error : void 0, typeof error?.commit === "string" ? error.commit : void 0);
3975
4162
  }
3976
4163
  return body;
3977
4164
  }
3978
4165
  function preview(value) {
3979
- const input = record$2(value);
4166
+ const input = record$3(value);
3980
4167
  if (input === void 0 || typeof input.root !== "string" || typeof input.branch !== "string" || typeof input.head !== "string" || typeof input.fingerprint !== "string" || !Array.isArray(input.files) || typeof input.patch !== "string" || typeof input.truncated !== "boolean" || typeof input.hasStaged !== "boolean" || typeof input.hasUnstaged !== "boolean" || typeof input.hasUntracked !== "boolean" || input.upstream !== void 0 && typeof input.upstream !== "string" || !Array.isArray(input.unpushedCommits) || typeof input.unpushedTruncated !== "boolean") throw new Error("Invalid repository action preview.");
3981
4168
  for (const file of input.files) {
3982
- const item = record$2(file);
4169
+ const item = record$3(file);
3983
4170
  if (item === void 0 || typeof item.path !== "string" || typeof item.staged !== "boolean" || typeof item.unstaged !== "boolean" || typeof item.untracked !== "boolean") throw new Error("Invalid repository action file.");
3984
4171
  }
3985
4172
  for (const commit of input.unpushedCommits) {
3986
- const item = record$2(commit);
4173
+ const item = record$3(commit);
3987
4174
  if (item === void 0 || typeof item.hash !== "string" || typeof item.subject !== "string") throw new Error("Invalid repository action commit.");
3988
4175
  }
3989
4176
  return input;
3990
4177
  }
3991
4178
  function result(value) {
3992
- const input = record$2(value);
3993
- if (input === void 0 || typeof input.commit !== "string" || typeof input.pushed !== "boolean" || input.pullRequestUrl !== void 0 && typeof input.pullRequestUrl !== "string") throw new Error("Invalid repository action result.");
4179
+ const input = record$3(value);
4180
+ if (input === void 0 || typeof input.commit !== "string" || typeof input.pushed !== "boolean" || input.pullRequestUrl !== void 0 && typeof input.pullRequestUrl !== "string" || input.conflicts !== void 0 && (!Array.isArray(input.conflicts) || input.conflicts.some((item) => typeof item !== "string"))) throw new Error("Invalid repository action result.");
3994
4181
  return input;
3995
4182
  }
3996
4183
  function endpoint(path, sessionId) {
@@ -4005,7 +4192,7 @@ window.__ModuleLoader__.load({
4005
4192
  })));
4006
4193
  }
4007
4194
  async function generateCommitMessage(sessionId, fingerprint, signal) {
4008
- const value = record$2(await response$1(fetch(endpoint("/message", sessionId), {
4195
+ const value = record$3(await response$1(fetch(endpoint("/message", sessionId), {
4009
4196
  method: "POST",
4010
4197
  credentials: "same-origin",
4011
4198
  headers: {
@@ -4030,6 +4217,110 @@ window.__ModuleLoader__.load({
4030
4217
  })).then(result);
4031
4218
  }
4032
4219
  //#endregion
4220
+ //#region src/client/pr-feedback-api.ts
4221
+ function record$2(value) {
4222
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
4223
+ }
4224
+ async function loadJson(path, sessionId, pullNumber, signal) {
4225
+ const response = await fetch(`${CLAUDE_REPOSITORY_FEEDBACK_PATH}${path}?sessionId=${encodeURIComponent(sessionId)}&number=${pullNumber}`, {
4226
+ method: "GET",
4227
+ credentials: "same-origin",
4228
+ headers: { accept: "application/json" },
4229
+ ...signal === void 0 ? {} : { signal }
4230
+ });
4231
+ const body = record$2(await response.json());
4232
+ if (!response.ok) throw new Error(typeof body?.message === "string" ? body.message : "Pull request feedback is unavailable.");
4233
+ if (body === void 0) throw new Error("Invalid pull request feedback response.");
4234
+ return body;
4235
+ }
4236
+ async function loadPullRequestComments(sessionId, pullNumber, signal) {
4237
+ const body = await loadJson("/comments", sessionId, pullNumber, signal);
4238
+ if (!Array.isArray(body.comments)) throw new Error("Invalid pull request comments response.");
4239
+ const comments = [];
4240
+ for (const item of body.comments) {
4241
+ const input = record$2(item);
4242
+ if (input === void 0 || typeof input.id !== "number" || typeof input.path !== "string" || typeof input.author !== "string" || typeof input.body !== "string" || typeof input.url !== "string" || input.side !== "new" && input.side !== "old" || input.line !== void 0 && typeof input.line !== "number") continue;
4243
+ comments.push(input);
4244
+ }
4245
+ return comments;
4246
+ }
4247
+ async function loadFailingChecks(sessionId, pullNumber, signal) {
4248
+ const body = await loadJson("/checks", sessionId, pullNumber, signal);
4249
+ if (!Array.isArray(body.checks)) throw new Error("Invalid pull request checks response.");
4250
+ const checks = [];
4251
+ for (const item of body.checks) {
4252
+ const input = record$2(item);
4253
+ if (input === void 0 || typeof input.name !== "string" || input.link !== void 0 && typeof input.link !== "string" || input.description !== void 0 && typeof input.description !== "string" || input.log !== void 0 && typeof input.log !== "string") continue;
4254
+ checks.push(input);
4255
+ }
4256
+ return checks;
4257
+ }
4258
+ /** Draft handed to Claude when the user forwards GitHub review comments. */
4259
+ function composeCommentsPrompt(comments) {
4260
+ return `Please address the following GitHub pull request review comments. Make the requested changes, or explain briefly when a comment should not be applied.\n\n${comments.map((comment) => `- ${comment.path}${comment.line === void 0 ? "" : `:${comment.line}`} (@${comment.author}): ${comment.body.replaceAll("\n", "\n ")}`).join("\n")}`;
4261
+ }
4262
+ /** Draft handed to Claude when the user forwards failing CI checks. */
4263
+ function composeChecksPrompt(checks) {
4264
+ return `The following CI checks are failing on the current pull request. Investigate the failure logs, fix the underlying problems, and re-run the relevant commands locally when possible.\n\n${checks.map((check) => {
4265
+ return `${`## ${check.name}${check.link === void 0 ? "" : ` (${check.link})`}`}${check.description === void 0 ? "" : `\n${check.description}`}${check.log === void 0 ? "" : `\n\n\`\`\`\n${check.log}\n\`\`\``}`;
4266
+ }).join("\n\n")}`;
4267
+ }
4268
+ /** Draft handed to Claude after an update-branch merge left conflicts behind. */
4269
+ function composeConflictsPrompt(baseBranch, conflicts) {
4270
+ return `Merging origin/${baseBranch} into the current branch left merge conflicts in the files below. Resolve each conflict preserving the intent of both sides, then commit the merge.\n\n${conflicts.map((file) => `- ${file}`).join("\n")}`;
4271
+ }
4272
+ //#endregion
4273
+ //#region src/client/auto-fix.ts
4274
+ const AUTO_FIX_INTERVAL_MS = 3e4;
4275
+ const AUTO_FIX_FOOTER = "This request was generated automatically by the pull request watcher. After making the changes, commit and push to the pull request branch so the checks re-run.";
4276
+ const EMPTY_MEMORY = { handledCommentIds: /* @__PURE__ */ new Set() };
4277
+ const sessions = /* @__PURE__ */ new Map();
4278
+ function session(sessionId) {
4279
+ let entry = sessions.get(sessionId);
4280
+ if (entry === void 0) {
4281
+ entry = {
4282
+ enabled: false,
4283
+ memory: EMPTY_MEMORY
4284
+ };
4285
+ sessions.set(sessionId, entry);
4286
+ }
4287
+ return entry;
4288
+ }
4289
+ function autoFixEnabled(sessionId) {
4290
+ return session(sessionId).enabled;
4291
+ }
4292
+ function setAutoFixEnabled(sessionId, enabled) {
4293
+ session(sessionId).enabled = enabled;
4294
+ }
4295
+ function autoFixMemory(sessionId) {
4296
+ return session(sessionId).memory;
4297
+ }
4298
+ function rememberAutoFix(sessionId, memory) {
4299
+ session(sessionId).memory = memory;
4300
+ }
4301
+ /** One failing CI run yields one fix attempt: run links change when CI re-runs. */
4302
+ function checksSignature(checks) {
4303
+ if (checks.length === 0) return void 0;
4304
+ return checks.map((check) => `${check.name}|${check.link ?? ""}`).sort().join("\n");
4305
+ }
4306
+ function planAutoFix(memory, comments, checks) {
4307
+ const fresh = comments.filter((comment) => !memory.handledCommentIds.has(comment.id));
4308
+ const signature = checksSignature(checks);
4309
+ const checksChanged = signature !== void 0 && signature !== memory.handledChecksSignature;
4310
+ const sections = [];
4311
+ if (fresh.length > 0) sections.push(composeCommentsPrompt(fresh));
4312
+ if (checksChanged) sections.push(composeChecksPrompt(checks));
4313
+ if (sections.length === 0) return { memory };
4314
+ const nextSignature = checksChanged ? signature : memory.handledChecksSignature;
4315
+ return {
4316
+ prompt: `${sections.join("\n\n")}\n\n${AUTO_FIX_FOOTER}`,
4317
+ memory: {
4318
+ handledCommentIds: /* @__PURE__ */ new Set([...memory.handledCommentIds, ...fresh.map((comment) => comment.id)]),
4319
+ ...nextSignature === void 0 ? {} : { handledChecksSignature: nextSignature }
4320
+ }
4321
+ };
4322
+ }
4323
+ //#endregion
4033
4324
  //#region src/client/ClaudeRepositoryStatus.tsx
4034
4325
  const RATE_LIMIT_TITLE_PREFIX = "Claude rate limit";
4035
4326
  const RATE_LIMIT_BLOCKING_TITLE = "Claude rate limit is blocking requests";
@@ -4051,7 +4342,10 @@ window.__ModuleLoader__.load({
4051
4342
  children: [/* @__PURE__ */ jsx("span", {
4052
4343
  style: repositoryItemDot,
4053
4344
  "aria-hidden": "true"
4054
- }), label]
4345
+ }), /* @__PURE__ */ jsx("span", {
4346
+ style: repositoryItemLabel,
4347
+ children: label
4348
+ })]
4055
4349
  });
4056
4350
  }
4057
4351
  function PullRequestIcon({ size = 16, merged = false }) {
@@ -4155,7 +4449,8 @@ window.__ModuleLoader__.load({
4155
4449
  children: [
4156
4450
  repositoryName(repository.remote),
4157
4451
  " #",
4158
- pullRequest.number
4452
+ pullRequest.number,
4453
+ pullRequest.baseBranch === void 0 ? "" : ` → ${pullRequest.baseBranch}`
4159
4454
  ]
4160
4455
  }),
4161
4456
  age === void 0 ? null : /* @__PURE__ */ jsx("span", {
@@ -4248,6 +4543,360 @@ window.__ModuleLoader__.load({
4248
4543
  })]
4249
4544
  });
4250
4545
  }
4546
+ /** Watches an open pull request and hands new review comments and failing
4547
+ * CI runs to Claude automatically until the user switches it off. */
4548
+ function AutoFixControl({ sessionId, repository, running, t, submitPrompt }) {
4549
+ const pullRequest = repository.pullRequest;
4550
+ const open = pullRequest?.state === "open";
4551
+ const number = pullRequest?.number;
4552
+ const checks = pullRequest?.checks;
4553
+ const [enabled, setEnabled] = useState(() => autoFixEnabled(sessionId));
4554
+ const [focused, setFocused] = useState(false);
4555
+ useEffect(() => {
4556
+ setEnabled(autoFixEnabled(sessionId));
4557
+ }, [sessionId]);
4558
+ useEffect(() => {
4559
+ if (!enabled || !open || running || number === void 0 || submitPrompt === void 0) return;
4560
+ let cancelled = false;
4561
+ const tick = async () => {
4562
+ const [comments, failing] = await Promise.all([loadPullRequestComments(sessionId, number).catch(() => []), checks === "failing" ? loadFailingChecks(sessionId, number).catch(() => []) : Promise.resolve([])]);
4563
+ if (cancelled) return;
4564
+ const plan = planAutoFix(autoFixMemory(sessionId), comments, failing);
4565
+ if (plan.prompt !== void 0 && submitPrompt(plan.prompt, "idle")) rememberAutoFix(sessionId, plan.memory);
4566
+ };
4567
+ tick();
4568
+ const timer = setInterval(() => {
4569
+ tick();
4570
+ }, AUTO_FIX_INTERVAL_MS);
4571
+ return () => {
4572
+ cancelled = true;
4573
+ clearInterval(timer);
4574
+ };
4575
+ }, [
4576
+ checks,
4577
+ enabled,
4578
+ number,
4579
+ open,
4580
+ running,
4581
+ sessionId,
4582
+ submitPrompt
4583
+ ]);
4584
+ if (!open || submitPrompt === void 0) return null;
4585
+ const toggle = () => {
4586
+ const next = !enabled;
4587
+ setEnabled(next);
4588
+ setAutoFixEnabled(sessionId, next);
4589
+ };
4590
+ return /* @__PURE__ */ jsx(Tooltip, {
4591
+ label: `${t("autoFixLabel")} · ${t("autoFixTitle")}`,
4592
+ side: "top",
4593
+ delayMs: 250,
4594
+ maxWidth: 320,
4595
+ children: /* @__PURE__ */ jsx("button", {
4596
+ type: "button",
4597
+ role: "switch",
4598
+ "aria-checked": enabled,
4599
+ "aria-label": t("autoFixLabel"),
4600
+ style: {
4601
+ ...repositoryAutoFix,
4602
+ ...enabled ? repositoryAutoFixActive : {},
4603
+ ...focused ? heroWorktreeToggleFocused : {}
4604
+ },
4605
+ onFocus: () => {
4606
+ setFocused(true);
4607
+ },
4608
+ onBlur: () => {
4609
+ setFocused(false);
4610
+ },
4611
+ onClick: (event) => {
4612
+ toggle();
4613
+ event.currentTarget.blur();
4614
+ },
4615
+ children: /* @__PURE__ */ jsxs("svg", {
4616
+ width: "14",
4617
+ height: "14",
4618
+ viewBox: "0 0 16 16",
4619
+ fill: "none",
4620
+ stroke: "currentColor",
4621
+ strokeWidth: "1.6",
4622
+ strokeLinecap: "round",
4623
+ strokeLinejoin: "round",
4624
+ "aria-hidden": "true",
4625
+ children: [/* @__PURE__ */ jsx("path", { d: "M13.3 6.5A5.5 5.5 0 0 0 3.6 4.6M2.7 9.5a5.5 5.5 0 0 0 9.7 1.9" }), /* @__PURE__ */ jsx("path", { d: "M13.5 2.8v3.7H9.8M2.5 13.2V9.5h3.7" })]
4626
+ })
4627
+ })
4628
+ });
4629
+ }
4630
+ function FailingChecksControl({ sessionId, pullNumber, t, submitPrompt }) {
4631
+ const [open, setOpen] = useState(false);
4632
+ const [loading, setLoading] = useState(false);
4633
+ const [checks, setChecks] = useState([]);
4634
+ const [error, setError] = useState();
4635
+ const frameRef = useRef(null);
4636
+ const controller = useRef();
4637
+ useEffect(() => () => controller.current?.abort(), []);
4638
+ useEffect(() => {
4639
+ if (!open) return;
4640
+ const closeOnOutsidePointer = (event) => {
4641
+ if (event.target instanceof Node && frameRef.current?.contains(event.target) !== true) setOpen(false);
4642
+ };
4643
+ document.addEventListener("pointerdown", closeOnOutsidePointer);
4644
+ return () => {
4645
+ document.removeEventListener("pointerdown", closeOnOutsidePointer);
4646
+ };
4647
+ }, [open]);
4648
+ const toggle = () => {
4649
+ const next = !open;
4650
+ setOpen(next);
4651
+ if (!next) return;
4652
+ controller.current?.abort();
4653
+ const aborter = new AbortController();
4654
+ controller.current = aborter;
4655
+ setLoading(true);
4656
+ setError(void 0);
4657
+ loadFailingChecks(sessionId, pullNumber, aborter.signal).then((value) => {
4658
+ setChecks(value);
4659
+ setLoading(false);
4660
+ }, (reason) => {
4661
+ if (aborter.signal.aborted) return;
4662
+ setError(reason instanceof Error ? reason.message : t("diffActionFailed"));
4663
+ setLoading(false);
4664
+ });
4665
+ };
4666
+ return /* @__PURE__ */ jsxs("span", {
4667
+ ref: frameRef,
4668
+ style: repositoryChecksFrame,
4669
+ children: [/* @__PURE__ */ jsx("button", {
4670
+ type: "button",
4671
+ style: repositoryChecksTrigger,
4672
+ "aria-haspopup": "dialog",
4673
+ "aria-expanded": open,
4674
+ "aria-label": t("repositoryChecksOpen"),
4675
+ onClick: toggle,
4676
+ children: /* @__PURE__ */ jsx(StatusItem, {
4677
+ label: t("repositoryChecks_failing"),
4678
+ tone: "error"
4679
+ })
4680
+ }), open ? /* @__PURE__ */ jsxs("span", {
4681
+ role: "dialog",
4682
+ "aria-label": t("checksCardTitle"),
4683
+ style: repositoryChecksCard,
4684
+ children: [
4685
+ /* @__PURE__ */ jsx("strong", {
4686
+ style: repositoryChecksTitle,
4687
+ children: t("checksCardTitle")
4688
+ }),
4689
+ loading ? /* @__PURE__ */ jsx("span", {
4690
+ style: repositoryChecksHint,
4691
+ children: t("checksCardLoading")
4692
+ }) : null,
4693
+ error === void 0 ? null : /* @__PURE__ */ jsx("span", {
4694
+ role: "alert",
4695
+ style: repositoryChecksError,
4696
+ children: error
4697
+ }),
4698
+ checks.map((check) => /* @__PURE__ */ jsxs("span", {
4699
+ style: repositoryChecksItem,
4700
+ children: [check.link === void 0 ? /* @__PURE__ */ jsx("span", {
4701
+ style: repositoryChecksName,
4702
+ children: check.name
4703
+ }) : /* @__PURE__ */ jsx("a", {
4704
+ href: check.link,
4705
+ target: "_blank",
4706
+ rel: "noopener noreferrer",
4707
+ style: repositoryChecksName,
4708
+ children: check.name
4709
+ }), check.description === void 0 ? null : /* @__PURE__ */ jsx("span", {
4710
+ style: repositoryChecksDesc,
4711
+ children: check.description
4712
+ })]
4713
+ }, check.name)),
4714
+ !loading && error === void 0 && checks.length === 0 ? /* @__PURE__ */ jsx("span", {
4715
+ style: repositoryChecksHint,
4716
+ children: t("checksCardEmpty")
4717
+ }) : null,
4718
+ submitPrompt !== void 0 && checks.length > 0 ? /* @__PURE__ */ jsx("button", {
4719
+ type: "button",
4720
+ style: repositoryChecksFix,
4721
+ onClick: () => {
4722
+ submitPrompt(composeChecksPrompt(checks));
4723
+ setOpen(false);
4724
+ },
4725
+ children: t("checksCardFix")
4726
+ }) : null
4727
+ ]
4728
+ }) : null]
4729
+ });
4730
+ }
4731
+ function UpdateBranchControl({ sessionId, repository, t, submitPrompt }) {
4732
+ const [dialog, setDialog] = useState();
4733
+ const controller = useRef();
4734
+ const pullRequest = repository.pullRequest;
4735
+ const base = pullRequest?.baseBranch;
4736
+ const behind = repository.baseBehind ?? 0;
4737
+ useEffect(() => () => controller.current?.abort(), []);
4738
+ useEffect(() => {
4739
+ if (dialog?.pushed === void 0 || dialog.error !== void 0) return;
4740
+ const timer = setTimeout(() => setDialog(void 0), 1200);
4741
+ return () => clearTimeout(timer);
4742
+ }, [dialog?.error, dialog?.pushed]);
4743
+ if (pullRequest === void 0 || pullRequest.state !== "open" || base === void 0 || repository.detached === true || repository.dirty === true || behind <= 0) return null;
4744
+ const openDialog = () => {
4745
+ controller.current?.abort();
4746
+ setDialog({
4747
+ loading: true,
4748
+ submitting: false
4749
+ });
4750
+ const aborter = new AbortController();
4751
+ controller.current = aborter;
4752
+ loadRepositoryActionPreview(sessionId, aborter.signal).then((preview) => {
4753
+ setDialog({
4754
+ loading: false,
4755
+ submitting: false,
4756
+ fingerprint: preview.fingerprint
4757
+ });
4758
+ }, (reason) => {
4759
+ if (!aborter.signal.aborted) setDialog({
4760
+ loading: false,
4761
+ submitting: false,
4762
+ error: reason instanceof Error ? reason.message : t("diffActionFailed")
4763
+ });
4764
+ });
4765
+ };
4766
+ const closeDialog = () => {
4767
+ if (dialog?.submitting === true) return;
4768
+ controller.current?.abort();
4769
+ controller.current = void 0;
4770
+ setDialog(void 0);
4771
+ };
4772
+ const confirmUpdate = () => {
4773
+ if (dialog?.fingerprint === void 0 || dialog.submitting) return;
4774
+ const { error: _error, ...pending } = dialog;
4775
+ setDialog({
4776
+ ...pending,
4777
+ submitting: true
4778
+ });
4779
+ executeRepositoryAction(sessionId, {
4780
+ action: "update-branch",
4781
+ fingerprint: dialog.fingerprint,
4782
+ message: "",
4783
+ includeUnstaged: false,
4784
+ baseBranch: base
4785
+ }).then((result) => {
4786
+ setDialog({
4787
+ ...pending,
4788
+ submitting: false,
4789
+ ...result.conflicts !== void 0 && result.conflicts.length > 0 ? { conflicts: result.conflicts } : { pushed: result.commit }
4790
+ });
4791
+ }, (reason) => {
4792
+ setDialog({
4793
+ ...pending,
4794
+ submitting: false,
4795
+ error: reason instanceof Error ? reason.message : t("diffActionFailed")
4796
+ });
4797
+ });
4798
+ };
4799
+ const settled = dialog?.pushed !== void 0 || dialog?.conflicts !== void 0;
4800
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [
4801
+ /* @__PURE__ */ jsxs("button", {
4802
+ type: "button",
4803
+ style: repositoryUpdateTrigger,
4804
+ "aria-label": t("repositoryUpdateBranch"),
4805
+ title: `${t("diffUpdateBranchBehind", {
4806
+ base,
4807
+ count: behind
4808
+ })} · ${t("repositoryUpdateBranch")}`,
4809
+ onClick: openDialog,
4810
+ children: ["↓", behind]
4811
+ }),
4812
+ dialog === void 0 ? null : /* @__PURE__ */ jsx("style", {
4813
+ "data-dsh-claude-repository-modal-styles": true,
4814
+ children: diffModalCss
4815
+ }),
4816
+ /* @__PURE__ */ jsx(Modal, {
4817
+ className: "dshClaudeRepositoryActionModal",
4818
+ contentClassName: "dshClaudeRepositoryActionModalContent",
4819
+ open: dialog !== void 0,
4820
+ onClose: closeDialog,
4821
+ title: t("repositoryUpdateBranch"),
4822
+ closeLabel: t("diffCancel"),
4823
+ description: t("diffUpdateBranchDescription", { base }),
4824
+ footer: /* @__PURE__ */ jsxs("div", {
4825
+ style: diffModalFooter,
4826
+ children: [/* @__PURE__ */ jsx("button", {
4827
+ type: "button",
4828
+ style: {
4829
+ ...button,
4830
+ ...diffModalButton
4831
+ },
4832
+ disabled: dialog?.submitting === true,
4833
+ onClick: closeDialog,
4834
+ children: settled ? t("diffDone") : t("diffCancel")
4835
+ }), !settled ? /* @__PURE__ */ jsx("button", {
4836
+ type: "button",
4837
+ style: {
4838
+ ...primaryButton,
4839
+ ...diffModalButton
4840
+ },
4841
+ disabled: dialog?.loading === true || dialog?.submitting === true || dialog?.fingerprint === void 0,
4842
+ onClick: confirmUpdate,
4843
+ children: dialog?.submitting === true ? t("diffSubmitting") : t("diffConfirm")
4844
+ }) : null]
4845
+ }),
4846
+ children: dialog === void 0 ? null : /* @__PURE__ */ jsxs("div", {
4847
+ style: diffModalBody,
4848
+ children: [
4849
+ /* @__PURE__ */ jsxs("div", {
4850
+ style: diffModalMeta,
4851
+ children: [/* @__PURE__ */ jsxs("strong", {
4852
+ style: diffModalMetaText,
4853
+ title: pullRequest.title,
4854
+ children: [
4855
+ repository.branch ?? t("repositoryUnknownBranch"),
4856
+ " ← origin/",
4857
+ base
4858
+ ]
4859
+ }), /* @__PURE__ */ jsx("span", {
4860
+ style: diffModalFileState,
4861
+ children: t("diffUpdateBranchBehind", {
4862
+ base,
4863
+ count: behind
4864
+ })
4865
+ })]
4866
+ }),
4867
+ dialog.pushed === void 0 ? null : /* @__PURE__ */ jsx("p", {
4868
+ style: diffModalSuccess,
4869
+ children: t("diffUpdateBranchCompleted", { commit: dialog.pushed.slice(0, 8) })
4870
+ }),
4871
+ dialog.conflicts === void 0 ? null : /* @__PURE__ */ jsxs(Fragment$1, { children: [
4872
+ /* @__PURE__ */ jsx("p", {
4873
+ style: diffModalStatus,
4874
+ children: t("diffUpdateBranchConflicts")
4875
+ }),
4876
+ /* @__PURE__ */ jsx("ul", {
4877
+ style: diffModalConflicts,
4878
+ children: dialog.conflicts.map((file) => /* @__PURE__ */ jsx("li", { children: file }, file))
4879
+ }),
4880
+ submitPrompt === void 0 ? null : /* @__PURE__ */ jsx("button", {
4881
+ type: "button",
4882
+ style: diffModalConflictResolve,
4883
+ onClick: () => {
4884
+ submitPrompt(composeConflictsPrompt(base, dialog.conflicts ?? []));
4885
+ closeDialog();
4886
+ },
4887
+ children: t("diffUpdateBranchResolve")
4888
+ })
4889
+ ] }),
4890
+ dialog.error === void 0 ? null : /* @__PURE__ */ jsx("p", {
4891
+ role: "alert",
4892
+ style: diffModalError,
4893
+ children: dialog.error
4894
+ })
4895
+ ]
4896
+ })
4897
+ })
4898
+ ] });
4899
+ }
4251
4900
  const MERGE_METHODS = [
4252
4901
  "merge",
4253
4902
  "squash",
@@ -4417,8 +5066,9 @@ window.__ModuleLoader__.load({
4417
5066
  })
4418
5067
  ] });
4419
5068
  }
4420
- function ClaudeRepositoryStatus({ sessionId, useSessions, useClaudeProjection, t, openDiff }) {
5069
+ function ClaudeRepositoryStatus({ sessionId, useSessions, useClaudeProjection, t, openDiff, submitPrompt }) {
4421
5070
  const blank = useSessions((value) => value.byId[sessionId]?.blank === true);
5071
+ const running = useSessions((value) => value.byId[sessionId]?.running === true);
4422
5072
  const projection = useClaudeProjection((value) => value);
4423
5073
  const repository = projection.repository;
4424
5074
  if (blank || !projection.owned || repository === void 0) return null;
@@ -4514,21 +5164,39 @@ window.__ModuleLoader__.load({
4514
5164
  style: repositoryMergedDot,
4515
5165
  "aria-hidden": "true"
4516
5166
  }),
4517
- t("repositoryMergedInto", { branch: pullRequest.baseBranch ?? t("repositoryUnknownBranch") }),
5167
+ t("repositoryState_merged"),
4518
5168
  mergedAge === void 0 ? null : /* @__PURE__ */ jsxs("span", {
4519
5169
  style: repositoryMergedAge,
4520
5170
  children: ["· ", t("repositoryMergedAgo", { age: mergedAge })]
4521
5171
  })
4522
5172
  ]
4523
5173
  }) : /* @__PURE__ */ jsxs(Fragment$1, { children: [
4524
- /* @__PURE__ */ jsx(StatusItem, {
5174
+ pullRequest.checks === "failing" ? /* @__PURE__ */ jsx(FailingChecksControl, {
5175
+ sessionId,
5176
+ pullNumber: pullRequest.number,
5177
+ t,
5178
+ ...submitPrompt === void 0 ? {} : { submitPrompt }
5179
+ }) : pullRequest.checks === "none" ? null : /* @__PURE__ */ jsx(StatusItem, {
4525
5180
  label: t(`repositoryChecks_${pullRequest.checks}`),
4526
- tone: pullRequest.checks === "passing" ? "success" : pullRequest.checks === "failing" ? "error" : pullRequest.checks === "pending" ? "warning" : "neutral"
5181
+ tone: pullRequest.checks === "passing" ? "success" : "warning"
4527
5182
  }),
4528
- /* @__PURE__ */ jsx(StatusItem, {
5183
+ pullRequest.review === "none" ? null : /* @__PURE__ */ jsx(StatusItem, {
4529
5184
  label: t(`repositoryReview_${pullRequest.review}`),
4530
5185
  tone: pullRequest.review === "approved" ? "success" : pullRequest.review === "changes-requested" ? "error" : "neutral"
4531
5186
  }),
5187
+ /* @__PURE__ */ jsx(AutoFixControl, {
5188
+ sessionId,
5189
+ repository,
5190
+ running,
5191
+ t,
5192
+ ...submitPrompt === void 0 ? {} : { submitPrompt }
5193
+ }),
5194
+ /* @__PURE__ */ jsx(UpdateBranchControl, {
5195
+ sessionId,
5196
+ repository,
5197
+ t,
5198
+ ...submitPrompt === void 0 ? {} : { submitPrompt }
5199
+ }),
4532
5200
  /* @__PURE__ */ jsx(MergePullRequestControl, {
4533
5201
  sessionId,
4534
5202
  repository,
@@ -4837,7 +5505,7 @@ window.__ModuleLoader__.load({
4837
5505
  ]
4838
5506
  });
4839
5507
  }
4840
- function DiffFileSection({ file, initiallyOpen, t, comments, editorAnchor, editorNode, onOpenEditor, onRemoveComment }) {
5508
+ function DiffFileSection({ file, initiallyOpen, t, comments, ghComments, editorAnchor, editorNode, onOpenEditor, onRemoveComment }) {
4841
5509
  const [open, setOpen] = useState(initiallyOpen);
4842
5510
  return /* @__PURE__ */ jsxs("section", {
4843
5511
  style: diffFile,
@@ -4874,6 +5542,7 @@ window.__ModuleLoader__.load({
4874
5542
  children: numberDiffLines(file.lines).map((entry, index) => {
4875
5543
  const anchor = commentAnchorForLine(entry);
4876
5544
  const lineComments = anchor === void 0 ? [] : comments.filter((comment) => comment.line === anchor.line && comment.side === anchor.side);
5545
+ const ghLineComments = anchor === void 0 ? [] : ghComments.filter((comment) => comment.line === anchor.line && comment.side === anchor.side);
4877
5546
  const editorOpen = anchor !== void 0 && editorAnchor !== void 0 && editorAnchor.line === anchor.line && editorAnchor.side === anchor.side;
4878
5547
  return /* @__PURE__ */ jsxs(Fragment, { children: [
4879
5548
  /* @__PURE__ */ jsx(DiffLine, {
@@ -4897,6 +5566,26 @@ window.__ModuleLoader__.load({
4897
5566
  children: comment.text
4898
5567
  })]
4899
5568
  }, comment.id)),
5569
+ ghLineComments.map((comment) => /* @__PURE__ */ jsxs("div", {
5570
+ style: diffCommentBlock,
5571
+ children: [/* @__PURE__ */ jsxs("div", {
5572
+ style: diffCommentCardMeta,
5573
+ children: [/* @__PURE__ */ jsxs("span", {
5574
+ style: diffGhCommentAuthor,
5575
+ children: ["GitHub · @", comment.author]
5576
+ }), /* @__PURE__ */ jsx("a", {
5577
+ href: comment.url,
5578
+ target: "_blank",
5579
+ rel: "noopener noreferrer",
5580
+ style: diffGhCommentLink,
5581
+ "aria-label": comment.url,
5582
+ children: "↗"
5583
+ })]
5584
+ }), /* @__PURE__ */ jsx("p", {
5585
+ style: diffCommentCardText,
5586
+ children: comment.body
5587
+ })]
5588
+ }, `gh-${comment.id}`)),
4900
5589
  editorOpen ? editorNode : null
4901
5590
  ] }, `${index}:${entry.line}`);
4902
5591
  })
@@ -4904,7 +5593,7 @@ window.__ModuleLoader__.load({
4904
5593
  });
4905
5594
  }
4906
5595
  function actionLabel(action, t) {
4907
- return (action === "commit" ? t("diffCommit") : action === "commit-push" ? t("diffCommitPush") : action === "push" ? t("diffPush") : action === "merge-pr" ? t("diffMergePr") : t("diffCreatePr")).replace(/[….]+$/u, "");
5596
+ return (action === "commit" ? t("diffCommit") : action === "commit-push" ? t("diffCommitPush") : action === "push" ? t("diffPush") : action === "merge-pr" ? t("diffMergePr") : action === "update-branch" ? t("diffUpdateBranch") : t("diffCreatePr")).replace(/[….]+$/u, "");
4908
5597
  }
4909
5598
  function repositoryActionAvailability(repository) {
4910
5599
  const ready = repository?.status === "ready" && repository.detached !== true;
@@ -4917,7 +5606,8 @@ window.__ModuleLoader__.load({
4917
5606
  "commit-push": committable && hasRemote,
4918
5607
  "push": pushable,
4919
5608
  "create-pr": (committable || pushable) && hasRemote && !hasOpenPullRequest,
4920
- "merge-pr": ready && hasOpenPullRequest && repository?.pullRequest?.draft !== true
5609
+ "merge-pr": ready && hasOpenPullRequest && repository?.pullRequest?.draft !== true,
5610
+ "update-branch": ready && hasOpenPullRequest && repository?.dirty !== true && (repository?.baseBehind ?? 0) > 0
4921
5611
  };
4922
5612
  }
4923
5613
  function RestorePanelIcon() {
@@ -4930,7 +5620,7 @@ window.__ModuleLoader__.load({
4930
5620
  children: /* @__PURE__ */ jsx("path", { d: "M1.5 5h3V2h1.4v4.4H1.5V5Zm9.9-3h1.4v3h3v1.4h-4.4V2ZM1.5 9.6h4.4V14H4.5v-3h-3V9.6Zm9.9 0h4.4V11h-3v3h-1.4V9.6Z" })
4931
5621
  });
4932
5622
  }
4933
- function ClaudeDiffPanel({ useClaudeProjection, t, sessionId, maximized, closeDetails, toggleMaximized }) {
5623
+ function ClaudeDiffPanel({ useClaudeProjection, t, sessionId, maximized, closeDetails, toggleMaximized, submitPrompt }) {
4934
5624
  const projection = useClaudeProjection((value) => value);
4935
5625
  const repository = projection.repository;
4936
5626
  const diff = repository?.diff;
@@ -4950,6 +5640,17 @@ window.__ModuleLoader__.load({
4950
5640
  const [localComments, setLocalComments] = useState([]);
4951
5641
  const [removedCommentIds, setRemovedCommentIds] = useState(() => /* @__PURE__ */ new Set());
4952
5642
  const actionController = useRef();
5643
+ const [ghComments, setGhComments] = useState([]);
5644
+ const pullNumber = repository?.pullRequest?.state === "open" ? repository.pullRequest.number : void 0;
5645
+ useEffect(() => {
5646
+ setGhComments([]);
5647
+ if (pullNumber === void 0) return;
5648
+ const controller = new AbortController();
5649
+ loadPullRequestComments(sessionId, pullNumber, controller.signal).then(setGhComments, () => void 0);
5650
+ return () => {
5651
+ controller.abort();
5652
+ };
5653
+ }, [pullNumber, sessionId]);
4953
5654
  const diffViewportObserver = useRef();
4954
5655
  const diffBodyRef = useCallback((element) => {
4955
5656
  diffViewportObserver.current?.disconnect();
@@ -5243,7 +5944,10 @@ window.__ModuleLoader__.load({
5243
5944
  "aria-hidden": "true",
5244
5945
  children: "›"
5245
5946
  }),
5246
- /* @__PURE__ */ jsx("span", { children: t("diffWorkingTree") })
5947
+ /* @__PURE__ */ jsx("span", {
5948
+ style: diffHeaderLabel,
5949
+ children: t("diffWorkingTree")
5950
+ })
5247
5951
  ]
5248
5952
  }), /* @__PURE__ */ jsxs("div", {
5249
5953
  style: diffHeaderActions,
@@ -5282,6 +5986,25 @@ window.__ModuleLoader__.load({
5282
5986
  })
5283
5987
  })]
5284
5988
  }),
5989
+ ghComments.length > 0 && submitPrompt !== void 0 ? /* @__PURE__ */ jsxs("button", {
5990
+ type: "button",
5991
+ style: diffPrCommentsButton,
5992
+ title: t("prCommentsSend"),
5993
+ "aria-label": t("prCommentsButton", { count: ghComments.length }),
5994
+ onClick: () => submitPrompt(composeCommentsPrompt(ghComments)),
5995
+ children: [/* @__PURE__ */ jsx("svg", {
5996
+ width: "14",
5997
+ height: "14",
5998
+ viewBox: "0 0 16 16",
5999
+ fill: "none",
6000
+ stroke: "currentColor",
6001
+ strokeWidth: "1.5",
6002
+ strokeLinecap: "round",
6003
+ strokeLinejoin: "round",
6004
+ "aria-hidden": "true",
6005
+ children: /* @__PURE__ */ jsx("path", { d: "M2.75 2.75h10.5a1 1 0 0 1 1 1v6.5a1 1 0 0 1-1 1H8.2l-3.2 2.9v-2.9H2.75a1 1 0 0 1-1-1v-6.5a1 1 0 0 1 1-1Z" })
6006
+ }), ghComments.length]
6007
+ }) : null,
5285
6008
  /* @__PURE__ */ jsx("button", {
5286
6009
  type: "button",
5287
6010
  className: panelIconButtonClass,
@@ -5327,6 +6050,7 @@ window.__ModuleLoader__.load({
5327
6050
  initiallyOpen: index === 0,
5328
6051
  t,
5329
6052
  comments: reviewComments.filter((comment) => comment.path === file.path),
6053
+ ghComments: ghComments.filter((comment) => comment.path === file.path),
5330
6054
  editorAnchor: commentEditor !== void 0 && commentEditor.path === file.path ? commentEditor : void 0,
5331
6055
  editorNode: commentEditorNode,
5332
6056
  onOpenEditor: (anchor) => openCommentEditor(file.path, anchor),
@@ -6477,6 +7201,9 @@ window.__ModuleLoader__.load({
6477
7201
  outputStyle: "Output Style",
6478
7202
  worktreeBranchPrefix: "Worktree 分支前缀",
6479
7203
  worktreeBranchPrefixEffect: "分支前缀修改会应用于之后自动创建的 Worktree 分支;显式指定的完整分支名不会被修改。",
7204
+ maxProcessesSetting: "Claude 进程上限",
7205
+ idleTimeoutSetting: "闲置回收时长(分钟)",
7206
+ limitsSettingEffect: "进程上限在下次启动 Claude 进程时生效;闲置回收时长在下一次回合结束后生效。留空或非法值会被拒绝。",
6480
7207
  pluginUpdate: "插件更新",
6481
7208
  pluginUpdateBody: "检查 npm 上的新版本。只有可唯一识别的 npm 安装才能直接更新;本地开发链接不会被替换。",
6482
7209
  updateNotChecked: "尚未检查更新。",
@@ -6570,7 +7297,6 @@ window.__ModuleLoader__.load({
6570
7297
  repositoryState_open: "开放",
6571
7298
  repositoryState_closed: "已关闭",
6572
7299
  repositoryState_merged: "已合并",
6573
- repositoryMergedInto: "已合并到 {branch}",
6574
7300
  repositoryMergedAgo: "{age}前",
6575
7301
  repositoryChecks: "检查",
6576
7302
  repositoryChecks_passing: "检查通过",
@@ -6631,6 +7357,22 @@ window.__ModuleLoader__.load({
6631
7357
  diffMerge_rebase: "Rebase 合并",
6632
7358
  diffMergeDescription: "使用所选方式合并当前分支的 Pull Request。",
6633
7359
  diffMergeCompleted: "已合并 PR #{number}",
7360
+ diffUpdateBranch: "Update branch…",
7361
+ repositoryUpdateBranch: "Update branch",
7362
+ diffUpdateBranchDescription: "将 origin/{base} 合并进当前分支并推送;出现冲突时可交给 Claude 解决。",
7363
+ diffUpdateBranchBehind: "落后 origin/{base} {count} 个 commit",
7364
+ diffUpdateBranchCompleted: "已合并并推送 commit {commit}",
7365
+ diffUpdateBranchConflicts: "合并产生冲突,以下文件需要解决:",
7366
+ diffUpdateBranchResolve: "让 Claude 解决冲突",
7367
+ repositoryChecksOpen: "查看失败的检查",
7368
+ checksCardTitle: "失败的检查",
7369
+ checksCardLoading: "正在读取检查详情…",
7370
+ checksCardEmpty: "未能读取失败详情",
7371
+ checksCardFix: "让 Claude 修复",
7372
+ prCommentsButton: "GitHub 评论 ({count})",
7373
+ prCommentsSend: "让 Claude 处理 GitHub 上的 PR 评论",
7374
+ autoFixLabel: "Auto fix",
7375
+ autoFixTitle: "自动监听这个 PR 的 review 评论与 CI 失败,交给 Claude 修复并推送,直到全部通过;取消勾选即停止。",
6634
7376
  reviewCommentAdd: "添加行评论",
6635
7377
  reviewCommentPlaceholder: "请求更改",
6636
7378
  reviewCommentSubmit: "注释",
@@ -6686,6 +7428,9 @@ window.__ModuleLoader__.load({
6686
7428
  outputStyle: "Output Style",
6687
7429
  worktreeBranchPrefix: "Worktree branch prefix",
6688
7430
  worktreeBranchPrefixEffect: "Prefix changes apply to subsequently generated Worktree branches. Explicit full branch names remain unchanged.",
7431
+ maxProcessesSetting: "Claude process limit",
7432
+ idleTimeoutSetting: "Idle timeout (minutes)",
7433
+ limitsSettingEffect: "The process limit applies when the next Claude process is admitted; the idle timeout applies after the next completed turn.",
6689
7434
  pluginUpdate: "Plugin updates",
6690
7435
  pluginUpdateBody: "Check npm for new releases. Only a uniquely identified npm installation can update in place; local development links are never replaced.",
6691
7436
  updateNotChecked: "Updates have not been checked yet.",
@@ -6779,7 +7524,6 @@ window.__ModuleLoader__.load({
6779
7524
  repositoryState_open: "Open",
6780
7525
  repositoryState_closed: "Closed",
6781
7526
  repositoryState_merged: "Merged",
6782
- repositoryMergedInto: "Merged into {branch}",
6783
7527
  repositoryMergedAgo: "{age} ago",
6784
7528
  repositoryChecks: "Checks",
6785
7529
  repositoryChecks_passing: "Checks passing",
@@ -6840,6 +7584,22 @@ window.__ModuleLoader__.load({
6840
7584
  diffMerge_rebase: "Rebase and merge",
6841
7585
  diffMergeDescription: "Merge the pull request for this branch with the selected method.",
6842
7586
  diffMergeCompleted: "Merged PR #{number}",
7587
+ diffUpdateBranch: "Update branch…",
7588
+ repositoryUpdateBranch: "Update branch",
7589
+ diffUpdateBranchDescription: "Merge origin/{base} into the current branch and push; hand any conflicts to Claude.",
7590
+ diffUpdateBranchBehind: "{count} commit(s) behind origin/{base}",
7591
+ diffUpdateBranchCompleted: "Merged and pushed commit {commit}",
7592
+ diffUpdateBranchConflicts: "The merge left conflicts in these files:",
7593
+ diffUpdateBranchResolve: "Have Claude resolve the conflicts",
7594
+ repositoryChecksOpen: "Show failing checks",
7595
+ checksCardTitle: "Failing checks",
7596
+ checksCardLoading: "Loading check details…",
7597
+ checksCardEmpty: "No failure details available",
7598
+ checksCardFix: "Have Claude fix these",
7599
+ prCommentsButton: "GitHub comments ({count})",
7600
+ prCommentsSend: "Have Claude address the GitHub pull request comments",
7601
+ autoFixLabel: "Auto fix",
7602
+ autoFixTitle: "Watch this pull request for review comments and failing checks, hand them to Claude to fix and push until everything passes; uncheck to stop.",
6843
7603
  reviewCommentAdd: "Add line comment",
6844
7604
  reviewCommentPlaceholder: "Request changes",
6845
7605
  reviewCommentSubmit: "Comment",
@@ -6854,7 +7614,7 @@ window.__ModuleLoader__.load({
6854
7614
  };
6855
7615
  //#endregion
6856
7616
  //#region src/client/index.tsx
6857
- function MaximizedDiff({ source, t, sessionId, closeDetails, restore }) {
7617
+ function MaximizedDiff({ source, t, sessionId, closeDetails, restore, submitPrompt }) {
6858
7618
  const snapshot = useSyncExternalStore(source.subscribe, source.getSnapshot, source.getSnapshot);
6859
7619
  const useClaudeProjection = (selector) => selector(snapshot);
6860
7620
  return /* @__PURE__ */ jsx(ClaudeDiffOverlay, {
@@ -6865,7 +7625,8 @@ window.__ModuleLoader__.load({
6865
7625
  sessionId,
6866
7626
  maximized: true,
6867
7627
  closeDetails,
6868
- toggleMaximized: restore
7628
+ toggleMaximized: restore,
7629
+ ...submitPrompt === void 0 ? {} : { submitPrompt }
6869
7630
  })
6870
7631
  });
6871
7632
  }
@@ -6893,6 +7654,23 @@ window.__ModuleLoader__.load({
6893
7654
  const workspaces = ctx.get("workspaces");
6894
7655
  const conversation = ctx.get("conversation");
6895
7656
  const connection = ctx.get("connection");
7657
+ /** Composer submit hook shared by the repository feedback affordances.
7658
+ * 'append' keeps any user draft and adds the prompt below it; 'idle'
7659
+ * submits only when the composer is empty and reports false otherwise. */
7660
+ const submitPromptFor = (sessionId) => {
7661
+ if (sessions === void 0 || conversation === void 0) return void 0;
7662
+ return (draft, mode = "append") => {
7663
+ const scope = sessions.scope(sessionId);
7664
+ if (scope === void 0) return false;
7665
+ const input = conversation.input.for(scope);
7666
+ const current = input.state.getSnapshot().draft;
7667
+ if (current.trim() === "") input.setDraft(draft);
7668
+ else if (mode === "append") input.setDraft(`${current}\n\n${draft}`);
7669
+ else return false;
7670
+ input.submit();
7671
+ return true;
7672
+ };
7673
+ };
6896
7674
  if (sessions !== void 0) ctx.effect(() => sessions.provide({
6897
7675
  hooks: ["claudeProjection"],
6898
7676
  resolve: (binding) => ({ hooks: { claudeProjection: projections.source(binding.sessionId) } })
@@ -6955,6 +7733,7 @@ window.__ModuleLoader__.load({
6955
7733
  const openDiffPanel = (sessionId) => {
6956
7734
  closePluginDetails();
6957
7735
  detailsSessionId = sessionId;
7736
+ const submitPrompt = submitPromptFor(sessionId);
6958
7737
  const registerDetails = () => {
6959
7738
  try {
6960
7739
  disposePluginDetails = ctx.slots.register({
@@ -6966,7 +7745,8 @@ window.__ModuleLoader__.load({
6966
7745
  sessionId,
6967
7746
  maximized: false,
6968
7747
  closeDetails: closePluginDetails,
6969
- toggleMaximized: maximizeDiff
7748
+ toggleMaximized: maximizeDiff,
7749
+ ...submitPrompt === void 0 ? {} : { submitPrompt }
6970
7750
  })
6971
7751
  }, ClaudeDiffPanel);
6972
7752
  } catch {
@@ -6994,7 +7774,8 @@ window.__ModuleLoader__.load({
6994
7774
  t,
6995
7775
  sessionId,
6996
7776
  closeDetails: closePluginDetails,
6997
- restore: restoreDiff
7777
+ restore: restoreDiff,
7778
+ ...submitPrompt === void 0 ? {} : { submitPrompt }
6998
7779
  }));
6999
7780
  } catch {
7000
7781
  disposeDiffOverlay = void 0;
@@ -7059,10 +7840,14 @@ window.__ModuleLoader__.load({
7059
7840
  id: "claude-repository-status",
7060
7841
  order: 20.5,
7061
7842
  locale: namespace,
7062
- inject: (sessionId) => ({
7063
- t,
7064
- openDiff: () => openDiffPanel(sessionId)
7065
- })
7843
+ inject: (sessionId) => {
7844
+ const submitPrompt = submitPromptFor(sessionId);
7845
+ return {
7846
+ t,
7847
+ openDiff: () => openDiffPanel(sessionId),
7848
+ ...submitPrompt === void 0 ? {} : { submitPrompt }
7849
+ };
7850
+ }
7066
7851
  }, ClaudeRepositoryStatus));
7067
7852
  if (sessions !== void 0 && workspaces !== void 0 && conversation !== void 0 && connection !== void 0) ctx.slots.inject("conversation.input.dock", () => ctx.slots.register({
7068
7853
  name: "conversation.input.dock",