dsh-github-flow 0.5.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/README.en.md +112 -17
  2. package/README.md +115 -17
  3. package/docs/UPGRADE-NOTES.md +12 -11
  4. package/docs/architecture.md +81 -22
  5. package/docs/images/1.png +0 -0
  6. package/lib/api/route-helpers.d.ts +19 -0
  7. package/lib/api/route-helpers.js +54 -0
  8. package/lib/api/routes-actions.js +214 -9
  9. package/lib/api/routes-lifecycle.js +8 -1
  10. package/lib/api/routes-overview.d.ts +26 -0
  11. package/lib/api/routes-overview.js +396 -36
  12. package/lib/api/routes-proxy.js +8 -1
  13. package/lib/api/routes.d.ts +11 -1
  14. package/lib/api/routes.js +15 -2
  15. package/lib/cache.d.ts +56 -4
  16. package/lib/cache.js +77 -7
  17. package/lib/client.d.ts +8 -3
  18. package/lib/client.js +5542 -1391
  19. package/lib/destructive.d.ts +59 -0
  20. package/lib/destructive.js +285 -0
  21. package/lib/executor/args-guard.d.ts +17 -0
  22. package/lib/executor/args-guard.js +23 -0
  23. package/lib/executor/error-classifier.d.ts +55 -0
  24. package/lib/executor/error-classifier.js +199 -0
  25. package/lib/executor/failure-anchors.d.ts +52 -0
  26. package/lib/executor/failure-anchors.js +116 -0
  27. package/lib/executor/git-parser.d.ts +124 -0
  28. package/lib/executor/git-parser.js +397 -0
  29. package/lib/executor/pr-review.d.ts +95 -0
  30. package/lib/executor/pr-review.js +175 -0
  31. package/lib/executor/run-log.d.ts +36 -0
  32. package/lib/executor/run-log.js +78 -0
  33. package/lib/executor.d.ts +277 -6
  34. package/lib/executor.js +1265 -94
  35. package/lib/github-service.d.ts +1 -1
  36. package/lib/main.d.ts +92 -18
  37. package/lib/main.js +186 -95
  38. package/lib/renderers/common.d.ts +0 -3
  39. package/lib/renderers/common.js +8 -44
  40. package/lib/renderers/markdown.d.ts +1 -0
  41. package/lib/renderers/markdown.js +1 -0
  42. package/lib/renderers/pr.js +26 -1
  43. package/lib/renderers/task.d.ts +9 -0
  44. package/lib/renderers/task.js +120 -0
  45. package/lib/review-rules.d.ts +60 -0
  46. package/lib/review-rules.js +204 -0
  47. package/lib/shared/state-badges.d.ts +42 -0
  48. package/lib/shared/state-badges.js +102 -0
  49. package/lib/task-primitives.d.ts +109 -0
  50. package/lib/task-primitives.js +194 -0
  51. package/lib/tool-tiers.d.ts +57 -0
  52. package/lib/tool-tiers.js +168 -0
  53. package/lib/tools/agent-task.js +4 -4
  54. package/lib/tools/api.js +18 -6
  55. package/lib/tools/cache.js +42 -40
  56. package/lib/tools/codespace.js +4 -12
  57. package/lib/tools/discussion.js +4 -4
  58. package/lib/tools/gist.d.ts +1 -39
  59. package/lib/tools/gist.js +4 -14
  60. package/lib/tools/help.js +72 -18
  61. package/lib/tools/image-utils.d.ts +27 -0
  62. package/lib/tools/image-utils.js +73 -3
  63. package/lib/tools/issue.js +16 -28
  64. package/lib/tools/label.js +4 -15
  65. package/lib/tools/pr.d.ts +47 -0
  66. package/lib/tools/pr.js +170 -33
  67. package/lib/tools/project.js +4 -13
  68. package/lib/tools/release.js +11 -22
  69. package/lib/tools/repo.js +4 -4
  70. package/lib/tools/ruleset.js +9 -6
  71. package/lib/tools/run.js +76 -24
  72. package/lib/tools/secret.js +11 -17
  73. package/lib/tools/skill.js +18 -22
  74. package/lib/tools/status.js +4 -4
  75. package/lib/tools/task.d.ts +219 -0
  76. package/lib/tools/task.js +457 -0
  77. package/lib/types.d.ts +514 -1
  78. package/lib/ui/help-modal.d.ts +6 -0
  79. package/lib/ui/help-modal.js +124 -16
  80. package/lib/ui/issue-modal.d.ts +14 -0
  81. package/lib/ui/issue-modal.js +370 -0
  82. package/lib/ui/markdown-lite.d.ts +66 -0
  83. package/lib/ui/markdown-lite.js +312 -0
  84. package/lib/ui/network-banner.d.ts +12 -0
  85. package/lib/ui/network-banner.js +35 -0
  86. package/lib/ui/pr-modal.d.ts +17 -0
  87. package/lib/ui/pr-modal.js +555 -0
  88. package/lib/ui/repo-badges.d.ts +43 -0
  89. package/lib/ui/repo-badges.js +82 -0
  90. package/lib/ui/repo-selector.d.ts +14 -0
  91. package/lib/ui/repo-selector.js +112 -0
  92. package/lib/ui/run-modal.d.ts +26 -0
  93. package/lib/ui/run-modal.js +486 -0
  94. package/lib/ui/storage.d.ts +47 -0
  95. package/lib/ui/storage.js +139 -0
  96. package/lib/ui/styles.d.ts +1 -1
  97. package/lib/ui/styles.js +1330 -44
  98. package/lib/ui/sync-settings-modal.js +1 -1
  99. package/lib/ui/utils.js +13 -1
  100. package/lib/ui/workspace-panel.js +226 -29
  101. package/lib/version.d.ts +1 -1
  102. package/lib/version.js +1 -1
  103. package/package.json +9 -2
@@ -265,7 +265,7 @@ export function GlobalSyncSettingsModal(props) {
265
265
  ? "var(--dsw-alias-state-error-primary, #cf222e)"
266
266
  : "var(--dsw-alias-label-secondary, #656d76)",
267
267
  },
268
- }, updateMsg.text) : null)) : activeSection === "about" ? h("div", null, h("div", { style: { display: "flex", alignItems: "center", gap: "10px", marginBottom: "14px" } }, renderOctocatBadge(40), h("div", null, h("div", { style: { fontSize: "15px", fontWeight: 600 } }, "DSH GitHub Flow"), h("div", { style: { fontSize: "12px", color: "var(--dsw-alias-label-secondary, #656d76)", marginTop: "2px" } }, "当前版本: v" + (aboutVersion || "…")))), h("div", { style: { fontSize: "12px", color: "var(--dsw-alias-label-secondary, #656d76)", marginBottom: "14px", lineHeight: "1.6" } }, "让 DeepSeek Harness Agent 拥有操作 GitHub 的原生手脚。原生复用宿主机 gh CLI 安全凭据,深度打通本地 Git 工作区与云端协作,提供 18 大领域 Agent 工具、人类 Slash 指令、Web 全景驾驶舱与一键跨平台升级。"), h("div", { style: { display: "flex", flexDirection: "column", gap: "8px", marginBottom: "14px" } }, h("div", { style: { display: "flex", alignItems: "center", gap: "8px", fontSize: "12px" } }, h("span", { style: { minWidth: "64px", color: "var(--dsw-alias-label-secondary, #656d76)" } }, "仓库地址"), h("a", { href: "https://github.com/ItQianChen/dsh-github-flow", target: "_blank", rel: "noopener noreferrer", style: { color: "var(--dsw-alias-link, #0969da)" } }, "github.com/ItQianChen/dsh-github-flow")), h("div", { style: { display: "flex", alignItems: "center", gap: "8px", fontSize: "12px" } }, h("span", { style: { minWidth: "64px", color: "var(--dsw-alias-label-secondary, #656d76)" } }, "npm 包"), h("a", { href: "https://www.npmjs.com/package/dsh-github-flow", target: "_blank", rel: "noopener noreferrer", style: { color: "var(--dsw-alias-link, #0969da)" } }, "npmjs.com/package/dsh-github-flow")), h("div", { style: { display: "flex", alignItems: "center", gap: "8px", fontSize: "12px" } }, h("span", { style: { minWidth: "64px", color: "var(--dsw-alias-label-secondary, #656d76)" } }, "开源协议"), h("a", { href: "https://github.com/ItQianChen/dsh-github-flow/blob/master/LICENSE", target: "_blank", rel: "noopener noreferrer", style: { color: "var(--dsw-alias-link, #0969da)" } }, "MIT License"))), h("div", {
268
+ }, updateMsg.text) : null)) : activeSection === "about" ? h("div", null, h("div", { style: { display: "flex", alignItems: "center", gap: "10px", marginBottom: "14px" } }, renderOctocatBadge(40), h("div", null, h("div", { style: { fontSize: "15px", fontWeight: 600 } }, "DSH GitHub Flow"), h("div", { style: { fontSize: "12px", color: "var(--dsw-alias-label-secondary, #656d76)", marginTop: "2px" } }, "当前版本: v" + (aboutVersion || "…")))), h("div", { style: { fontSize: "12px", color: "var(--dsw-alias-label-secondary, #656d76)", marginBottom: "14px", lineHeight: "1.6" } }, "让 DeepSeek Harness Agent 拥有操作 GitHub 的原生手脚。原生复用宿主机 gh CLI 安全凭据,深度打通本地 Git 工作区与云端协作,提供 19 大领域 Agent 工具、人类 Slash 指令、Web 全景驾驶舱与一键跨平台升级。"), h("div", { style: { display: "flex", flexDirection: "column", gap: "8px", marginBottom: "14px" } }, h("div", { style: { display: "flex", alignItems: "center", gap: "8px", fontSize: "12px" } }, h("span", { style: { minWidth: "64px", color: "var(--dsw-alias-label-secondary, #656d76)" } }, "仓库地址"), h("a", { href: "https://github.com/ItQianChen/dsh-github-flow", target: "_blank", rel: "noopener noreferrer", style: { color: "var(--dsw-alias-link, #0969da)" } }, "github.com/ItQianChen/dsh-github-flow")), h("div", { style: { display: "flex", alignItems: "center", gap: "8px", fontSize: "12px" } }, h("span", { style: { minWidth: "64px", color: "var(--dsw-alias-label-secondary, #656d76)" } }, "npm 包"), h("a", { href: "https://www.npmjs.com/package/dsh-github-flow", target: "_blank", rel: "noopener noreferrer", style: { color: "var(--dsw-alias-link, #0969da)" } }, "npmjs.com/package/dsh-github-flow")), h("div", { style: { display: "flex", alignItems: "center", gap: "8px", fontSize: "12px" } }, h("span", { style: { minWidth: "64px", color: "var(--dsw-alias-label-secondary, #656d76)" } }, "开源协议"), h("a", { href: "https://github.com/ItQianChen/dsh-github-flow/blob/master/LICENSE", target: "_blank", rel: "noopener noreferrer", style: { color: "var(--dsw-alias-link, #0969da)" } }, "MIT License"))), h("div", {
269
269
  className: "dsh-github-about-star-box",
270
270
  style: {
271
271
  display: "flex",
package/lib/ui/utils.js CHANGED
@@ -64,6 +64,15 @@ export function eventLabel(event) {
64
64
  return "🔀 PR";
65
65
  return e || "";
66
66
  }
67
+ /**
68
+ * 确定性失败的特征(客户端侧兜底判定)。
69
+ *
70
+ * 为什么这里还要再写一份:后端 GhExecutor 已统一分类并通过 errorKind 分流,
71
+ * 但浏览器端拿不到 errorKind,只能看到拼接后的错误文本。这类错误重试一万次
72
+ * 也不会变好(仓库就是关着 Issues),必须阻止阶梯退避把 7 秒白白烧掉。
73
+ * 与服务端共用同一组特征串,改动时两边同步。
74
+ */
75
+ var DETERMINISTIC_HINT_RE = /repository has disabled|Could not resolve to a Repository|Branch not found|HTTP 403|Not Found \(HTTP 404\)|rate limit/i;
67
76
  /**
68
77
  * 带有阶梯退避重试(Backoff Retry)的通用异步拉取函数
69
78
  *
@@ -107,7 +116,10 @@ export async function fetchWithRetry(url, options = {}, config = {}) {
107
116
  currentJson.data?.auth?.networkError));
108
117
  if (isNetworkError) {
109
118
  failureReason = currentJson.error || currentJson.data?.networkErrorReason || "云端接口网络抖动";
110
- shouldRetry = true;
119
+ // 后端已把「确定性失败」(仓库关了 Issues / 仓库不存在 / 无权限)从
120
+ // networkError 里剥离,只留真正的传输层故障。但历史版本或第三方实现
121
+ // 仍可能把确定性文本塞进同一条链路,这里再兜一道:命中即放弃重试。
122
+ shouldRetry = !DETERMINISTIC_HINT_RE.test(String(failureReason));
111
123
  }
112
124
  else {
113
125
  // 成功获取完整且无网络错误的数据
@@ -1,3 +1,4 @@
1
+ import { prStateBadge, issueStateBadge } from '../shared/state-badges.js';
1
2
  /**
2
3
  * 全局工作区仪表盘主面板组件 (Dashboard View)
3
4
  */
@@ -17,9 +18,23 @@ export function GithubWorkspacePanel() {
17
18
  var [activeTab, setActiveTab] = React.useState("workspaces");
18
19
  var [searchQuery, setSearchQuery] = React.useState("");
19
20
  var [issueFilter, setIssueFilter] = React.useState("all");
21
+ // PR 状态筛选:默认「全部」而不是「Open」。
22
+ // 实测账号可能零 open PR(当前账号 7 个 PR 全为 merged/closed),
23
+ // 默认 Open 会让列表恒为空白,等于没修「看不到数据」这个原始问题。
24
+ var [prStateFilter, setPrStateFilter] = React.useState("all");
20
25
  var [toastMsg, setToastMsg] = React.useState(null);
26
+ // PR 在线审阅弹窗状态:{ number, repo } 二元组;为 null 时弹窗不渲染
27
+ var [activePrModal, setActivePrModal] = React.useState(null);
28
+ // Issue 在线查看弹窗状态:与 PR 完全同构的 { number, repo } 二元组
29
+ var [activeIssueModal, setActiveIssueModal] = React.useState(null);
21
30
  var cloudTimerRef = React.useRef(null);
22
31
  var dataRef = React.useRef(data);
32
+ // 全局面板分页游标:PR 与 Issues 独立维护,防止点击加载更多相互膨胀
33
+ var prLimitRef = React.useRef(15);
34
+ var issueLimitRef = React.useRef(15);
35
+ // 「正在加载更多」UI 标记:请求往返期间页脚必须给反馈,否则用户会连点触发重复请求
36
+ var [loadingPrs, setLoadingPrs] = React.useState(false);
37
+ var [loadingIssues, setLoadingIssues] = React.useState(false);
23
38
  dataRef.current = data;
24
39
  var showToast = (msg) => {
25
40
  setToastMsg(msg);
@@ -102,8 +117,14 @@ export function GithubWorkspacePanel() {
102
117
  if (forceRefresh)
103
118
  await fetch("/api/github/refresh", { method: "POST", signal: controller.signal }).catch(() => { });
104
119
  var url = "/api/github/global-overview";
120
+ var params = [];
105
121
  if (forceRefresh) {
106
- url += "?force=true";
122
+ params.push("force=true");
123
+ }
124
+ params.push("prLimit=" + prLimitRef.current);
125
+ params.push("issueLimit=" + issueLimitRef.current);
126
+ if (params.length > 0) {
127
+ url += "?" + params.join("&");
107
128
  }
108
129
  // 接入 1s/2s/4s 阶梯退避重试,平滑吸收代理节点与网络的偶发抖动断流
109
130
  var { res, json } = await fetchWithRetry(url, {}, {
@@ -339,6 +360,21 @@ export function GithubWorkspacePanel() {
339
360
  var workspaceMatrix = (data && data.workspaceMatrix) || [];
340
361
  var myPrs = (data && data.myPrs) || [];
341
362
  var myIssues = (data && data.myIssues) || [];
363
+ // 主面板列表分页:PR 与 Issue 各自独立游标,每页步长 15,避免点击加载更多相互膨胀
364
+ var loadMorePrs = function () {
365
+ if (loadingPrs)
366
+ return;
367
+ prLimitRef.current = prLimitRef.current + 15;
368
+ setLoadingPrs(true);
369
+ loadData(true).finally(function () { setLoadingPrs(false); });
370
+ };
371
+ var loadMoreIssues = function () {
372
+ if (loadingIssues)
373
+ return;
374
+ issueLimitRef.current = issueLimitRef.current + 15;
375
+ setLoadingIssues(true);
376
+ loadData(true).finally(function () { setLoadingIssues(false); });
377
+ };
342
378
  var errorEl = error ? h("div", { style: { color: "#f85149", background: "rgba(248,81,73,0.1)", border: "1px solid rgba(248,81,73,0.3)", padding: "8px 12px", borderRadius: "6px", marginBottom: "16px", fontSize: "12px" } }, `⚠️ 加载失败: ${error}`) : null;
343
379
  var statsCardsEl = h("div", { className: "dsh-github-overview-cards" }, h("div", { className: "dsh-github-card" }, h("div", { className: "dsh-github-card-title" }, "CLI 认证账号"), h("div", { className: "dsh-github-card-value" }, auth?.networkError
344
380
  ? (auth.user ? `👤 ${auth.user} (离线)` : "🌐 网络连接受限")
@@ -358,7 +394,7 @@ export function GithubWorkspacePanel() {
358
394
  var authGuideEl = (!auth?.loggedIn && !auth?.networkError && syncStatus !== "failed")
359
395
  ? h(AuthGuideCard, { auth, onRefresh: () => loadData(true), isRightbar: false })
360
396
  : null;
361
- var tabsEl = h("div", { className: "dsh-github-tabs" }, h("button", { className: `dsh-github-tab-btn ${activeTab === 'workspaces' ? 'active' : ''}`, onClick: () => setActiveTab('workspaces') }, `📂 本地工作区矩阵 (${workspaceMatrix.length})`), h("button", { className: `dsh-github-tab-btn ${activeTab === 'repos' ? 'active' : ''}`, onClick: () => setActiveTab('repos') }, `☁️ 我的 GitHub 仓库 (${userRepos.length})`), h("button", { className: `dsh-github-tab-btn ${activeTab === 'mywork' ? 'active' : ''}`, onClick: () => setActiveTab('mywork') }, `📋 任务与待办 (PR: ${myPrs.length} · Issue: ${myIssues.length})`));
397
+ var tabsEl = h("div", { className: "dsh-github-tabs" }, h("button", { className: `dsh-github-tab-btn ${activeTab === 'workspaces' ? 'active' : ''}`, onClick: () => setActiveTab('workspaces') }, `📂 本地工作区矩阵 (${workspaceMatrix.length})`), h("button", { className: `dsh-github-tab-btn ${activeTab === 'repos' ? 'active' : ''}`, onClick: () => setActiveTab('repos') }, `☁️ 我的 GitHub 仓库 (${userRepos.length})`), h("button", { className: `dsh-github-tab-btn ${activeTab === 'prs' ? 'active' : ''}`, onClick: () => setActiveTab('prs') }, `🔀 Pull Requests (${myPrs.length})`), h("button", { className: `dsh-github-tab-btn ${activeTab === 'issues' ? 'active' : ''}`, onClick: () => setActiveTab('issues') }, `📝 任务与待办 Issues (${myIssues.length})`));
362
398
  var contentEl = null;
363
399
  if (activeTab === 'workspaces') {
364
400
  contentEl = h("div", { className: "dsh-github-list" }, workspaceMatrix.map((ws) => h("div", { key: ws.id, className: "dsh-github-card dsh-github-workspace-card" }, h("div", { style: { display: "flex", flexDirection: "column", gap: "4px" } }, h("div", { className: "dsh-github-ws-title" }, ws.title), h("div", { className: "dsh-github-ws-path" }, ws.path), h("div", { style: { fontSize: "12px", marginTop: "4px", display: "flex", gap: "8px", alignItems: "center" } }, ws.repo
@@ -396,6 +432,100 @@ export function GithubWorkspacePanel() {
396
432
  : null)))));
397
433
  }
398
434
  }
435
+ else if (activeTab === 'prs') {
436
+ // ---- PR 独立标签页:完整大屏空间,点击卡片在线唤起审阅工作台 ----
437
+ if (!auth?.loggedIn) {
438
+ contentEl = h("div", { className: "dsh-github-empty" }, "当前尚未认证登录 GitHub 账号,无法读取个人 PR 列表。请先完成上方的安装或登录指引。");
439
+ }
440
+ else {
441
+ // 状态分组统计(供胶囊计数与空态判断)
442
+ var prByState = { open: 0, merged: 0, closed: 0 };
443
+ myPrs.forEach(function (pr) {
444
+ var s = String(pr.state || '').toLowerCase();
445
+ if (s === 'open' || s === 'merged' || s === 'closed')
446
+ prByState[s]++;
447
+ });
448
+ var filteredPrs = myPrs.filter(function (pr) {
449
+ if (prStateFilter === 'all')
450
+ return true;
451
+ return String(pr.state || '').toLowerCase() === prStateFilter;
452
+ });
453
+ var prPillsEl = h("div", { className: "dsh-github-pills" },
454
+ // 计数优先用账号级真实总数(服务端 GraphQL search.issueCount),拿不到才回落已加载条数
455
+ h("button", { className: `dsh-github-pill-btn ${prStateFilter === 'all' ? 'active' : ''}`, onClick: () => setPrStateFilter('all') }, `全部 (${typeof data?.prTotal === 'number' ? data.prTotal : myPrs.length})`), h("button", { className: `dsh-github-pill-btn ${prStateFilter === 'open' ? 'active' : ''}`, onClick: () => setPrStateFilter('open') }, `🟢 Open (${typeof data?.prOpenTotal === 'number' ? data.prOpenTotal : prByState.open})`), h("button", { className: `dsh-github-pill-btn ${prStateFilter === 'merged' ? 'active' : ''}`, onClick: () => setPrStateFilter('merged') }, `🟣 Merged (${typeof data?.prMergedTotal === 'number' ? data.prMergedTotal : prByState.merged})`), h("button", { className: `dsh-github-pill-btn ${prStateFilter === 'closed' ? 'active' : ''}`, onClick: () => setPrStateFilter('closed') }, `⚪ Closed (${typeof data?.prClosedTotal === 'number' ? data.prClosedTotal : prByState.closed})`));
456
+ // 空态必须区分「账号确实没有」与「当前筛选下没有」——否则用户会把筛空当成数据丢失
457
+ var prEmptyEl = myPrs.length === 0
458
+ ? h("div", { className: "dsh-github-empty" }, "该账号下暂无任何 Pull Request 记录")
459
+ : h("div", { className: "dsh-github-empty" }, `当前筛选「${prStateFilter}」下无结果,共 ${myPrs.length} 条记录(切换到「全部」查看)`);
460
+ contentEl = h("div", null, h("h3", { className: "dsh-github-subheading" }, `🔀 我发起与参与的 Pull Requests(${filteredPrs.length} 条符合筛选 / 共 ${typeof data?.prTotal === 'number' ? data.prTotal : myPrs.length})`), prPillsEl,
461
+ // 「加载更多」页脚:服务端说还有更多(本次拉满)时渲染,点击即 +15 重取。
462
+ // 旧文案是「已触及检索上限,请到 GitHub 查看」——那是在没有分页能力时的兜底,
463
+ // 现在数据可以继续拉,改成可点击的入口。
464
+ data && data.prsTruncated
465
+ ? h("div", {
466
+ onClick: loadingPrs ? undefined : loadMorePrs,
467
+ title: loadingPrs ? "正在加载更多 PR…" : "点击加载更多(滚动到底部也会自动加载)", "aria-busy": loadingPrs ? "true" : "false",
468
+ style: {
469
+ margin: "8px 0",
470
+ padding: "8px 12px",
471
+ borderRadius: "6px",
472
+ fontSize: "11px",
473
+ lineHeight: 1.5,
474
+ textAlign: "center",
475
+ cursor: loadingPrs ? "default" : "pointer",
476
+ background: "rgba(210, 153, 34, 0.12)",
477
+ border: "1px solid rgba(210, 153, 34, 0.35)",
478
+ color: "var(--dsw-alias-state-attention-primary, #9a6700)",
479
+ },
480
+ }, loadingPrs ? "⏳ 正在加载中…" : `⬇️ 下滑或点击加载更多 PR(已显示 ${myPrs.length} 条)`)
481
+ : (typeof data?.prTotal === 'number' && myPrs.length < data.prTotal
482
+ // 顶到单次 100 条上限后不再有加载入口,必须说明「更早记录在哪」,否则用户以为界面坏了
483
+ ? h("div", { style: { margin: "8px 0", fontSize: "11px", opacity: 0.6, textAlign: "center" } }, `已加载 ${myPrs.length} / 共 ${data.prTotal} 条,更早记录请到 GitHub 查看`)
484
+ : null), filteredPrs.length === 0 ? prEmptyEl : h("div", { className: "dsh-github-list" }, filteredPrs.map((pr) => {
485
+ // 点击卡片主体 → 在线审阅弹窗
486
+ return h("div", {
487
+ key: pr.number,
488
+ className: "dsh-github-item",
489
+ /*
490
+ * 只加 cursor,绝不覆盖 display/alignItems/flexWrap。
491
+ * 这里的 inline style 曾写成 display:flex + alignItems:center + flexWrap:wrap,
492
+ * 直接把 .dsh-github-item 的 flex-direction:column 顶掉,
493
+ * 导致标题、状态徽章、时间全部横向挤成一行并整体居中——
494
+ * 卡片布局就是被这一行内联样式毁掉的。
495
+ */
496
+ style: { cursor: "pointer" },
497
+ title: "点击在线审阅该 PR(描述 / 评论 / 文件 Diff)",
498
+ onClick: () => {
499
+ setActivePrModal({
500
+ number: Number(pr.number),
501
+ repo: pr.repository?.nameWithOwner || '',
502
+ });
503
+ },
504
+ },
505
+ // 标题行:标题 → 状态徽章 → GitHub 跳转按钮,全部同一行
506
+ h("div", { className: "dsh-github-pr-title-row" }, h("span", { className: "dsh-github-item-title", style: { cursor: "pointer" } }, `${pr.isDraft ? "⚪ " : ""}[${pr.repository?.nameWithOwner || 'Repo'}] #${pr.number} ${pr.title}`), pr.isDraft ? h("span", { className: "dsh-github-badge dsh-badge-yellow", style: { flexShrink: 0 } }, "DRAFT") : null,
507
+ // 状态徽章:列表含 merged/closed,必须标明才能一眼区分
508
+ (() => {
509
+ // 状态→色调来自 shared/state-badges(与右侧栏、CLI 输出同一份)
510
+ var b = prStateBadge(pr.state);
511
+ if (!b || b.tone === 'neutral')
512
+ return null;
513
+ return h("span", { className: "dsh-github-badge dsh-badge-" + b.tone, style: { flexShrink: 0 } }, b.label);
514
+ })(), h("a", {
515
+ className: "dsh-github-pr-gh-btn",
516
+ href: pr.url,
517
+ target: "_blank",
518
+ rel: "noopener noreferrer",
519
+ onClick: (e) => e.stopPropagation(),
520
+ title: "在浏览器打开该 PR(不进入在线审阅)",
521
+ }, "GitHub ↗")),
522
+ // 第二行只保留时间,避免与标题行的徽章重复
523
+ formatRelativeTime(pr.updatedAt)
524
+ ? h("div", { className: "dsh-github-item-meta" }, h("span", { style: { whiteSpace: "nowrap" } }, formatRelativeTime(pr.updatedAt)))
525
+ : null);
526
+ })));
527
+ }
528
+ }
399
529
  else {
400
530
  if (!auth?.loggedIn) {
401
531
  contentEl = h("div", { className: "dsh-github-empty" }, "当前尚未认证登录 GitHub 账号,无法读取个人待办 PR 与 Issues。请先完成上方的安装或登录指引。");
@@ -416,7 +546,7 @@ export function GithubWorkspacePanel() {
416
546
  var issuePillsEl = h("div", { className: "dsh-github-pills" }, h("button", {
417
547
  className: `dsh-github-pill-btn ${issueFilter === 'all' ? 'active' : ''}`,
418
548
  onClick: () => setIssueFilter('all'),
419
- }, `全部 (${issueStats.total || myIssues.length})`), h("button", {
549
+ }, `全部 (${myIssues.length})`), h("button", {
420
550
  className: `dsh-github-pill-btn ${issueFilter === 'reported' ? 'active' : ''}`,
421
551
  onClick: () => setIssueFilter('reported'),
422
552
  }, `📥 用户提起的 (${issueStats.reported || 0})`), h("button", {
@@ -426,31 +556,80 @@ export function GithubWorkspacePanel() {
426
556
  className: `dsh-github-pill-btn ${issueFilter === 'created' ? 'active' : ''}`,
427
557
  onClick: () => setIssueFilter('created'),
428
558
  }, `✍️ 我创建的 (${issueStats.created || 0})`));
429
- contentEl = h("div", null, h("h3", { className: "dsh-github-subheading" }, `🔀 我发起的 PR (${myPrs.length})`), myPrs.length === 0 ? h("div", { className: "dsh-github-empty", style: { marginBottom: "16px" } }, "暂无开放中的 PR") : h("div", { className: "dsh-github-list", style: { marginBottom: "16px" } }, myPrs.map((pr) => h("div", { key: pr.number, className: "dsh-github-item", style: { display: "flex", alignItems: "center", gap: "8px", flexWrap: "wrap" } }, h("a", {
430
- className: "dsh-github-item-title",
431
- href: pr.url,
432
- target: "_blank",
433
- title: pr.updatedAt ? `更新于 ${pr.updatedAt}` : undefined,
434
- }, `${pr.isDraft ? "⚪ " : ""}[${pr.repository?.nameWithOwner || 'Repo'}] #${pr.number} ${pr.title}`), pr.isDraft ? h("span", { className: "dsh-github-badge dsh-badge-yellow" }, "DRAFT") : null, formatRelativeTime(pr.updatedAt)
435
- ? h("span", { style: { fontSize: "11px", color: "var(--dsw-alias-label-secondary, #656d76)", whiteSpace: "nowrap" } }, formatRelativeTime(pr.updatedAt))
436
- : null))), h("h3", { className: "dsh-github-subheading" }, `📝 任务与待办 Issues (${filteredIssues.length})`), issuePillsEl, filteredIssues.length === 0 ? h("div", { className: "dsh-github-empty" }, "当前分类下暂无开放中的 Issue") : h("div", { className: "dsh-github-list" }, filteredIssues.map((is) => {
437
- var cats = is.categories || [];
438
- var badges = [];
439
- if (cats.includes("reported")) {
440
- badges.push(h("span", { key: "rep", className: "dsh-github-badge dsh-badge-purple" }, "用户提单"));
441
- }
442
- if (cats.includes("assigned")) {
443
- badges.push(h("span", { key: "asn", className: "dsh-github-badge dsh-badge-blue" }, "分配待办"));
444
- }
445
- if (cats.includes("created")) {
446
- badges.push(h("span", { key: "crt", className: "dsh-github-badge dsh-badge-green" }, "我发起的"));
447
- }
448
- return h("div", { key: is.url || is.number, className: "dsh-github-item" }, h("div", { style: { display: "flex", alignItems: "center", gap: "6px", flexWrap: "wrap" } }, h("a", { className: "dsh-github-item-title", href: is.url, target: "_blank" }, `[${is.repository?.nameWithOwner || 'Repo'}] #${is.number} ${is.title}`), ...badges), h("div", { className: "dsh-github-item-meta" }, is.author ? h("span", null, `由 @${is.author.login} 提出`) : null, is.assignees && is.assignees.length > 0
449
- ? h("span", null, `指派: ${is.assignees.map((a) => '@' + a.login).join(', ')}`)
450
- : null, is.labels && is.labels.length > 0
451
- ? is.labels.map((lbl) => h("span", { key: lbl.name, className: "dsh-github-badge dsh-badge-yellow" }, lbl.name))
452
- : null, is.updatedAt ? h("span", null, `更新于: ${new Date(is.updatedAt).toLocaleDateString()}`) : null));
453
- })));
559
+ contentEl = h("div", null,
560
+ // Issue 侧没有可信总数(三路并集无法用单条查询计数),老实标「已加载」,不假装是总数
561
+ h("h3", { className: "dsh-github-subheading" }, `📝 我发起与参与的 Issues(${filteredIssues.length} 条符合筛选 / 已加载 ${myIssues.length})`), issuePillsEl,
562
+ // 「加载更多」页脚:与 PR 列表同一口径(服务端 issuesTruncated = 还能继续拉)
563
+ data && data.issuesTruncated
564
+ ? h("div", {
565
+ onClick: loadingIssues ? undefined : loadMoreIssues,
566
+ title: loadingIssues ? "正在加载更多 Issue…" : "点击加载更多(滚动到底部也会自动加载)", "aria-busy": loadingIssues ? "true" : "false",
567
+ style: {
568
+ margin: "8px 0",
569
+ padding: "8px 12px",
570
+ borderRadius: "6px",
571
+ fontSize: "11px",
572
+ lineHeight: 1.5,
573
+ textAlign: "center",
574
+ cursor: loadingIssues ? "default" : "pointer",
575
+ background: "rgba(210, 153, 34, 0.12)",
576
+ border: "1px solid rgba(210, 153, 34, 0.35)",
577
+ color: "var(--dsw-alias-state-attention-primary, #9a6700)",
578
+ },
579
+ }, loadingIssues ? "⏳ 正在加载中…" : `⬇️ 下滑或点击加载更多 Issue(已显示 ${myIssues.length} 条)`)
580
+ : null, filteredIssues.length === 0
581
+ ? (myIssues.length === 0
582
+ ? h("div", { className: "dsh-github-empty" }, "该账号下暂无任何 Issue 记录")
583
+ : h("div", { className: "dsh-github-empty" }, `当前分类「${issueFilter}」下无结果,共 ${myIssues.length} 条记录(切换到「全部」查看)`))
584
+ : h("div", { className: "dsh-github-list" }, filteredIssues.map((is) => {
585
+ var cats = is.categories || [];
586
+ var badges = [];
587
+ if (cats.includes("reported")) {
588
+ badges.push(h("span", { key: "rep", className: "dsh-github-badge dsh-badge-purple" }, "用户提单"));
589
+ }
590
+ if (cats.includes("assigned")) {
591
+ badges.push(h("span", { key: "asn", className: "dsh-github-badge dsh-badge-blue" }, "分配待办"));
592
+ }
593
+ if (cats.includes("created")) {
594
+ badges.push(h("span", { key: "crt", className: "dsh-github-badge dsh-badge-green" }, "我发起的"));
595
+ }
596
+ return h("div", {
597
+ key: is.url || is.number,
598
+ className: "dsh-github-item",
599
+ /*
600
+ * 只加 cursor,绝不覆盖 display/alignItems/flexWrap。
601
+ * .dsh-github-item 是 flex-direction:column,任何内联 flex 布局
602
+ * 都会把标题、徽章、元信息横向挤成一行(PR 卡片上踩过的坑)。
603
+ */
604
+ style: { cursor: "pointer" },
605
+ title: "点击在线查看该 Issue(正文 / 评论时间线 / 标签与里程碑)",
606
+ onClick: () => {
607
+ setActiveIssueModal({
608
+ number: Number(is.number),
609
+ repo: is.repository?.nameWithOwner || '',
610
+ });
611
+ },
612
+ },
613
+ // 标题行:标题 → 分类徽章 → 状态徽章 → GitHub 外跳,全部同一行
614
+ h("div", { className: "dsh-github-pr-title-row" }, h("span", { className: "dsh-github-item-title", style: { cursor: "pointer" } }, `[${is.repository?.nameWithOwner || 'Repo'}] #${is.number} ${is.title}`), ...badges, (() => {
615
+ // 状态徽章:全局检索含 closed,不标注会让用户把已关闭议题当成待办
616
+ var b = issueStateBadge(is.state);
617
+ if (!b || b.tone === 'neutral')
618
+ return null;
619
+ return h("span", { className: "dsh-github-badge dsh-badge-" + b.tone, style: { flexShrink: 0 } }, b.label);
620
+ })(), h("a", {
621
+ className: "dsh-github-pr-gh-btn",
622
+ href: is.url,
623
+ target: "_blank",
624
+ rel: "noopener noreferrer",
625
+ onClick: (e) => e.stopPropagation(),
626
+ title: "在浏览器打开该 Issue(不进入在线查看)",
627
+ }, "GitHub ↗")), h("div", { className: "dsh-github-item-meta" }, is.author ? h("span", null, `由 @${is.author.login} 提出`) : null, is.assignees && is.assignees.length > 0
628
+ ? h("span", null, `指派: ${is.assignees.map((a) => '@' + a.login).join(', ')}`)
629
+ : null, is.labels && is.labels.length > 0
630
+ ? is.labels.map((lbl) => h("span", { key: lbl.name, className: "dsh-github-badge dsh-badge-yellow" }, lbl.name))
631
+ : null, is.updatedAt ? h("span", null, `更新于: ${new Date(is.updatedAt).toLocaleDateString()}`) : null));
632
+ })));
454
633
  }
455
634
  }
456
635
  var toastEl = toastMsg
@@ -485,5 +664,23 @@ export function GithubWorkspacePanel() {
485
664
  return h("div", { className: "dsh-github-panel", ref: panelRef }, toastEl, headerEl, networkAlertBannerEl, upgradeBannerEl, errorEl, statsCardsEl, authGuideEl, tabsEl, contentEl, settingsModalEl, h(GithubHelpModal, {
486
665
  isOpen: isHelpModalOpen,
487
666
  onClose: () => setIsHelpModalOpen(false),
488
- }));
667
+ }),
668
+ // PR 在线审阅工作台:仅在用户点击某个 PR 时挂载,关闭即卸载(零常驻开销)
669
+ activePrModal
670
+ ? h(PrDetailModal, {
671
+ prNumber: activePrModal.number,
672
+ repoNameWithOwner: activePrModal.repo,
673
+ onClose: () => setActivePrModal(null),
674
+ showToast: showToast,
675
+ })
676
+ : null,
677
+ // Issue 在线查看弹窗:同上,点击即挂载、关闭即卸载
678
+ activeIssueModal
679
+ ? h(IssueDetailModal, {
680
+ issueNumber: activeIssueModal.number,
681
+ repoNameWithOwner: activeIssueModal.repo,
682
+ onClose: () => setActiveIssueModal(null),
683
+ showToast: showToast,
684
+ })
685
+ : null);
489
686
  }
package/lib/version.d.ts CHANGED
@@ -6,7 +6,7 @@
6
6
  * 2. 避免在 ESM 运行时动态读取 package.json 引入 fs 权限和路径解析边界问题;
7
7
  * 3. 方便在 CI 发布流水线或 build 脚本中自动化校准。
8
8
  */
9
- export declare const CURRENT_PLUGIN_VERSION = "0.5.0";
9
+ export declare const CURRENT_PLUGIN_VERSION = "0.7.0";
10
10
  export declare const PLUGIN_PACKAGE_NAME = "dsh-github-flow";
11
11
  export declare const NPM_PACKAGE_URL = "https://www.npmjs.com/package/dsh-github-flow";
12
12
  export declare const NPM_REGISTRY_MIRROR = "https://registry.npmmirror.com";
package/lib/version.js CHANGED
@@ -6,7 +6,7 @@
6
6
  * 2. 避免在 ESM 运行时动态读取 package.json 引入 fs 权限和路径解析边界问题;
7
7
  * 3. 方便在 CI 发布流水线或 build 脚本中自动化校准。
8
8
  */
9
- export const CURRENT_PLUGIN_VERSION = '0.5.0';
9
+ export const CURRENT_PLUGIN_VERSION = '0.7.0';
10
10
  export const PLUGIN_PACKAGE_NAME = 'dsh-github-flow';
11
11
  export const NPM_PACKAGE_URL = 'https://www.npmjs.com/package/dsh-github-flow';
12
12
  export const NPM_REGISTRY_MIRROR = 'https://registry.npmmirror.com';
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "dsh-github-flow",
3
- "version": "0.5.0",
3
+ "version": "0.7.0",
4
4
  "icon": "icon.svg",
5
- "description": "GitHub 工作流引擎:为 DeepSeek Harness 提供原生 GitHub CLI (gh) 集成——18 大 Agent 领域工具(PR 审查合并、Issue 任务流转、CI 失败诊断、发版交付、密钥管理、Agent Skills、标签与保护规则集、构建缓存治理、代码片段 Gist、跨仓待办大盘、Copilot 智能体调度、社区 Discussions、Projects v2 看板、云端 Codespaces、运行时遥测与自愈健康度)、/gh 人类快捷指令、Web UI 全景驾驶舱与一键跨平台自动升级;零凭证侵入复用宿主机 gh 登录态,内置 Token 防溢出截断、高危操作二次确认与密文安全输入通道。",
5
+ "description": "GitHub 工作流引擎:为 DeepSeek Harness 提供原生 GitHub CLI (gh) 集成——19 大 Agent 领域工具(PR 审查合并、Issue 任务流转、CI 失败诊断、发版交付、密钥管理、Agent Skills、标签与保护规则集、构建缓存治理、代码片段 Gist、跨仓待办大盘、Copilot 智能体调度、社区 Discussions、Projects v2 看板、云端 Codespaces、运行时遥测与自愈健康度)、/gh 人类快捷指令、Web UI 全景驾驶舱与一键跨平台自动升级;零凭证侵入复用宿主机 gh 登录态,内置 Token 防溢出截断、高危操作二次确认与密文安全输入通道。",
6
6
  "type": "module",
7
7
  "main": "./lib/main.js",
8
8
  "repository": {
@@ -17,6 +17,7 @@
17
17
  ".": "./lib/main.js",
18
18
  "./entry": "./lib/main.js",
19
19
  "./client": "./lib/client.js",
20
+ "./host": "./lib/github-service.js",
20
21
  "./package.json": "./package.json"
21
22
  },
22
23
  "files": [
@@ -55,9 +56,15 @@
55
56
  "access": "public",
56
57
  "registry": "https://registry.npmjs.org/"
57
58
  },
59
+ "peerDependencies": {
60
+ "@deepseek-ai/cordis": "^4.0.4"
61
+ },
58
62
  "devDependencies": {
59
63
  "@deepseek-ai/cordis": "^4.0.4",
60
64
  "@types/node": "^26.6.3",
61
65
  "typescript": "^7.0.2"
66
+ },
67
+ "dependencies": {
68
+ "@deepseek-ai/schemastery": "^3.18.5"
62
69
  }
63
70
  }