dsh-github-flow 0.4.0 → 0.6.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 (82) hide show
  1. package/README.en.md +91 -12
  2. package/README.md +91 -12
  3. package/docs/UPGRADE-NOTES.md +9 -12
  4. package/docs/architecture.md +63 -29
  5. package/docs/images/1.png +0 -0
  6. package/lib/api/routes-actions.js +268 -5
  7. package/lib/api/routes-overview.js +147 -29
  8. package/lib/cache.d.ts +77 -4
  9. package/lib/cache.js +114 -7
  10. package/lib/client.d.ts +9 -4
  11. package/lib/client.js +5293 -942
  12. package/lib/commands/gh.d.ts +6 -1
  13. package/lib/commands/gh.js +144 -71
  14. package/lib/executor/error-classifier.d.ts +55 -0
  15. package/lib/executor/error-classifier.js +199 -0
  16. package/lib/executor/git-parser.d.ts +157 -1
  17. package/lib/executor/git-parser.js +513 -4
  18. package/lib/executor.d.ts +147 -4
  19. package/lib/executor.js +866 -77
  20. package/lib/main.js +28 -4
  21. package/lib/renderers/agent-task.d.ts +16 -0
  22. package/lib/renderers/agent-task.js +107 -0
  23. package/lib/renderers/codespace.d.ts +16 -0
  24. package/lib/renderers/codespace.js +103 -0
  25. package/lib/renderers/discussion.d.ts +16 -0
  26. package/lib/renderers/discussion.js +101 -0
  27. package/lib/renderers/markdown.d.ts +4 -0
  28. package/lib/renderers/markdown.js +4 -0
  29. package/lib/renderers/project.d.ts +20 -0
  30. package/lib/renderers/project.js +128 -0
  31. package/lib/renderers/skill.d.ts +30 -0
  32. package/lib/renderers/skill.js +180 -0
  33. package/lib/renderers/status.d.ts +5 -0
  34. package/lib/renderers/status.js +52 -0
  35. package/lib/telemetry.d.ts +70 -0
  36. package/lib/telemetry.js +161 -0
  37. package/lib/tools/agent-task.d.ts +54 -0
  38. package/lib/tools/agent-task.js +135 -0
  39. package/lib/tools/codespace.d.ts +51 -0
  40. package/lib/tools/codespace.js +154 -0
  41. package/lib/tools/discussion.d.ts +70 -0
  42. package/lib/tools/discussion.js +195 -0
  43. package/lib/tools/help.js +129 -14
  44. package/lib/tools/image-utils.d.ts +27 -0
  45. package/lib/tools/image-utils.js +73 -3
  46. package/lib/tools/project.d.ts +80 -0
  47. package/lib/tools/project.js +241 -0
  48. package/lib/tools/repo.js +13 -1
  49. package/lib/tools/skill.d.ts +24 -1
  50. package/lib/tools/skill.js +115 -5
  51. package/lib/tools/status.d.ts +3 -2
  52. package/lib/tools/status.js +9 -4
  53. package/lib/types.d.ts +336 -0
  54. package/lib/ui/commit-modal.js +144 -14
  55. package/lib/ui/help-modal.d.ts +21 -0
  56. package/lib/ui/help-modal.js +300 -0
  57. package/lib/ui/issue-modal.d.ts +14 -0
  58. package/lib/ui/issue-modal.js +366 -0
  59. package/lib/ui/markdown-lite.d.ts +66 -0
  60. package/lib/ui/markdown-lite.js +312 -0
  61. package/lib/ui/network-banner.d.ts +12 -0
  62. package/lib/ui/network-banner.js +35 -0
  63. package/lib/ui/pr-modal.d.ts +17 -0
  64. package/lib/ui/pr-modal.js +516 -0
  65. package/lib/ui/repo-badges.d.ts +43 -0
  66. package/lib/ui/repo-badges.js +82 -0
  67. package/lib/ui/repo-selector.d.ts +14 -0
  68. package/lib/ui/repo-selector.js +112 -0
  69. package/lib/ui/storage.d.ts +56 -0
  70. package/lib/ui/storage.js +163 -0
  71. package/lib/ui/styles.d.ts +1 -1
  72. package/lib/ui/styles.js +1219 -0
  73. package/lib/ui/sync-settings-modal.js +1 -1
  74. package/lib/ui/upgrade-card.js +39 -14
  75. package/lib/ui/utils.d.ts +35 -0
  76. package/lib/ui/utils.js +172 -0
  77. package/lib/ui/workspace-panel.js +198 -31
  78. package/lib/upgrade-manager.d.ts +5 -1
  79. package/lib/upgrade-manager.js +24 -1
  80. package/lib/version.d.ts +1 -1
  81. package/lib/version.js +1 -1
  82. package/package.json +2 -2
package/lib/types.d.ts CHANGED
@@ -68,6 +68,18 @@ export interface GhExecutionOptions {
68
68
  * 暴露在进程命令行(ps / 任务管理器 / 审计日志可见)的传入通道。
69
69
  */
70
70
  stdin?: string;
71
+ /**
72
+ * 单次调用的输出字符上限覆盖(默认沿用实例配置的 maxOutputChars=24000)。
73
+ *
74
+ * 为什么需要按调用覆盖:这个默认值是按「喂给模型上下文的工具输出」调校的,
75
+ * 目的是防 Token 爆窗。但有些输出是**程序化消费**的——例如 PR 全量 Unified Diff
76
+ * 会被切片成按文件索引、前端一次只渲染一个文件,永远不会整段进模型上下文。
77
+ * 对这类调用沿用 24KB 会静默截断真实数据(实测一个普通 PR 的 diff 已达 39KB,
78
+ * 被截断后后半数文件点开皆空)。消费者不同,阈值就该不同。
79
+ */
80
+ maxOutputChars?: number;
81
+ /** 单次调用的输出 UTF-8 字节上限覆盖,语义同 maxOutputChars */
82
+ maxOutputBytes?: number;
71
83
  }
72
84
  export interface GhResult<T = unknown> {
73
85
  ok: boolean;
@@ -78,6 +90,22 @@ export interface GhResult<T = unknown> {
78
90
  truncated?: boolean;
79
91
  /** 二进制模式下的字节长度(data 为 Buffer 时存在) */
80
92
  binaryBytes?: number;
93
+ /**
94
+ * 失败语义分类(仅 ok=false 时存在)。
95
+ *
96
+ * 为什么挂在结果上而不是让调用方自己 parse error 文本:调用方散落在
97
+ * 各工具与路由里,重复实现文本匹配必然漂移;分类规则只应有一份事实源
98
+ * (src/executor/error-classifier.ts)。上层据此决定「重试 / 回退快照 / 直接降级」。
99
+ */
100
+ errorKind?: 'transient' | 'deterministic' | 'unknown';
101
+ /**
102
+ * 权限不足时的可操作处置指引(仅 errorKind=deterministic 且命中权限特征时存在)。
103
+ *
104
+ * 为什么单独成字段而不直接拼进 error:error 是「发生了什么」,
105
+ * 这是「接下来怎么做」。工具层可以选择只抛 error(静默场景)
106
+ * 或把两者一起抛给模型(需要模型自愈的场景)。
107
+ */
108
+ permissionRemedy?: string;
81
109
  }
82
110
  export interface AuthInstallGuide {
83
111
  /** 推荐包管理器命令,例如 Windows 下 "winget install --id GitHub.cli" */
@@ -107,6 +135,47 @@ export interface AuthStatus {
107
135
  /** 本地是否已检测到持久化凭据(如 hosts.yml 或 Keyring) */
108
136
  hasLocalToken?: boolean;
109
137
  }
138
+ /**
139
+ * 当前账号对仓库的权限档位(gh `viewerPermission` 字段的取值域)。
140
+ * 只读(READ / TRIAGE)意味着任何写操作都会被 GitHub 拒绝,
141
+ * UI 应据此在操作前给出提示,而不是让用户点了才吃 403。
142
+ */
143
+ export type ViewerPermission = 'ADMIN' | 'MAINTAIN' | 'WRITE' | 'TRIAGE' | 'READ';
144
+ /**
145
+ * 工作区的远程布局分类(离线解析得出,用于确定「该用哪个 remote 的身份」)。
146
+ *
147
+ * 为什么要分类:混合布局下若按 `.git/config` 的书写顺序选 remote,
148
+ * 同一个工作区在两次 `git remote` 操作后可能解析出不同仓库。
149
+ * 分类后每种布局的处置规则固定,与书写顺序无关。
150
+ */
151
+ export type RepoLayoutKind =
152
+ /** 全部 remote 都是 github.com */
153
+ 'github-only'
154
+ /** 含 GitHub remote,但 git 选中的那个不是 GitHub */
155
+ | 'github-mixed'
156
+ /** 无任何 GitHub remote(全 GHE 或其他 host) */
157
+ | 'no-github'
158
+ /** 无 remote */
159
+ | 'no-remote';
160
+ /**
161
+ * 关联仓库角色标识:
162
+ * - fork: 属于当前登录用户的个人复刻仓库
163
+ * - upstream: 社区上游主干仓库(名为 upstream 或作为 parent)
164
+ * - origin: 默认 origin 远程仓库
165
+ * - parent: 上游源仓库(在线识别自 repo.parent,本地尚未添加 remote)
166
+ * - remote: 其他自定义名称的远程仓库
167
+ */
168
+ export type AssociatedRepoRole = 'origin' | 'upstream' | 'fork' | 'parent' | 'remote';
169
+ /**
170
+ * 当前工作区可切换的关联 GitHub 仓库项(供仓库切换器下拉菜单消费)。
171
+ */
172
+ export interface AssociatedRepoItem {
173
+ nameWithOwner: string;
174
+ remoteName?: string;
175
+ url: string;
176
+ isCurrent: boolean;
177
+ role: AssociatedRepoRole;
178
+ }
110
179
  export interface RepoMetadata {
111
180
  nameWithOwner: string;
112
181
  name: string;
@@ -116,6 +185,32 @@ export interface RepoMetadata {
116
185
  description?: string;
117
186
  isPrivate?: boolean;
118
187
  url?: string;
188
+ stargazerCount?: number;
189
+ forkCount?: number;
190
+ /** 是否为 fork(影响 star 取谁、是否提示可查看上游) */
191
+ isFork?: boolean;
192
+ /**
193
+ * fork 的上游。
194
+ * gh 的 `parent` 只返回骨架(id/name/owner.login),nameWithOwner 需自行拼接;
195
+ * 上游 star 数拿不到(`--json` 不支持嵌套子字段),故此处不含热度字段。
196
+ */
197
+ parent?: {
198
+ nameWithOwner: string;
199
+ url?: string;
200
+ };
201
+ /**
202
+ * 当前账号对该仓库的权限。
203
+ * 离线解析时为 undefined——**不猜测**:猜错会导致 UI 给出错误的操作许可。
204
+ */
205
+ viewerPermission?: ViewerPermission;
206
+ /** 远程布局分类(仅离线解析产生,供 UI 与诊断区分场景) */
207
+ layout?: RepoLayoutKind;
208
+ /**
209
+ * 当前工作区可供用户手动切换的关联仓库列表。
210
+ * 包含:本地 Git 配置的全部 GitHub remote (origin, upstream 等) 及在线 parent。
211
+ * 仅当数量 > 1 时 UI 渲染切换控件,单仓库场景保持零噪音。
212
+ */
213
+ associatedRepos?: AssociatedRepoItem[];
119
214
  }
120
215
  /** Git 分支详情项(供分支切换器下拉列表消费) */
121
216
  export interface GitBranchItem {
@@ -198,6 +293,105 @@ export interface PrItem {
198
293
  url: string;
199
294
  updatedAt: string;
200
295
  }
296
+ /** PR 变动文件详情项 */
297
+ export interface PrFileItem {
298
+ path: string;
299
+ additions: number;
300
+ deletions: number;
301
+ changeType?: string;
302
+ }
303
+ /** PR 评论项 */
304
+ export interface PrCommentItem {
305
+ id: string;
306
+ author: {
307
+ login: string;
308
+ avatarUrl?: string;
309
+ };
310
+ authorAssociation?: string;
311
+ body: string;
312
+ createdAt: string;
313
+ url: string;
314
+ }
315
+ /** PR 审查记录项 */
316
+ export interface PrReviewItem {
317
+ id: string;
318
+ author: {
319
+ login: string;
320
+ };
321
+ state: string;
322
+ body: string;
323
+ submittedAt: string;
324
+ }
325
+ /**
326
+ * PR 行级审查评论项(代码行上的评论,区别于 Issue 级讨论)。
327
+ *
328
+ * 为什么必须单列:`gh pr view --json comments` 只返回**对话区**的 issue-level 评论,
329
+ * 完全不包含挂在具体代码行上的 review comment。实测 cli/cli#14752 有 10 条行级评论,
330
+ * 而 `--json comments` 一条都拿不到——必须走
331
+ * `GET /repos/{owner}/{repo}/pulls/{number}/comments` 独立通道。
332
+ *
333
+ * 不做的部分:不把评论按行号精确嵌进 diff 的 hunk 中间(需反查 hunk 偏移,
334
+ * 多行/折叠上下文下极易错位)。改为在文件级 diff 下方按行号列出,信息完整且确定。
335
+ */
336
+ export interface PrLineCommentItem {
337
+ id: string;
338
+ /** 所属文件路径,与 fileDiffs 的键一致 */
339
+ path: string;
340
+ /** 评论锚定的行号(新文件侧);纯删除行可能为 null */
341
+ line: number | null;
342
+ side?: string;
343
+ author: {
344
+ login: string;
345
+ };
346
+ body: string;
347
+ createdAt: string;
348
+ }
349
+ /** PR 完整详情与差异数据契约(供 PrDetailModal 在线审阅消费) */
350
+ export interface PrDetailData {
351
+ number: number;
352
+ title: string;
353
+ state: 'OPEN' | 'CLOSED' | 'MERGED';
354
+ isDraft: boolean;
355
+ url: string;
356
+ createdAt: string;
357
+ updatedAt: string;
358
+ author: {
359
+ login: string;
360
+ avatarUrl?: string;
361
+ };
362
+ headRefName: string;
363
+ baseRefName: string;
364
+ headRepoOwner?: string;
365
+ body: string;
366
+ reviewDecision?: string;
367
+ statusCheckRollup?: any;
368
+ additions: number;
369
+ deletions: number;
370
+ files: PrFileItem[];
371
+ comments: PrCommentItem[];
372
+ reviews: PrReviewItem[];
373
+ /**
374
+ * 行级审查评论(挂在具体代码行上的 review comment)。
375
+ * 与 comments 是**两套独立数据源**:comments 来自对话区,lineComments 来自
376
+ * /pulls/{n}/comments。拉取失败时降级为空数组,不阻塞主详情。
377
+ */
378
+ lineComments?: PrLineCommentItem[];
379
+ /** 按文件相对路径切片索引的 Git Unified Diff 字典 */
380
+ fileDiffs: Record<string, string>;
381
+ /**
382
+ * 为什么不回传 fullDiff 原文:`fileDiffs` 已经是全量 diff 的等价切分
383
+ * (每段拼起来就是原文),再附带一份原文会让 HTTP 响应体整整翻倍。
384
+ * 实测一个普通 PR 的 diff 就有 39KB,2MB 上限下翻倍意味着 4MB JSON——
385
+ * 白占带宽、白占浏览器内存、白耗 JSON.parse 时间,且没有任何消费方。
386
+ * 原文仅在服务端用于切片,切片完即丢弃。
387
+ */
388
+ /**
389
+ * diff 是否被输出阈值截断。
390
+ * 为什么必须透出:超巨量 PR 仍可能触顶,此时部分文件拿不到 diff。
391
+ * 静默展示「该文件无差异」会让用户误以为代码没改动——必须显式提示。
392
+ */
393
+ diffTruncated?: boolean;
394
+ }
201
395
  /**
202
396
  * Issue 分类维度契约:
203
397
  * - reported: 用户提起的(他人向我名下项目或本仓库提交的 Issue 反馈)
@@ -205,6 +399,107 @@ export interface PrItem {
205
399
  * - created: 我创建的(由当前账号亲自发起的 Issue)
206
400
  */
207
401
  export type IssueCategory = 'reported' | 'assigned' | 'created';
402
+ /** Issue 评论项(在线查看弹窗的讨论时间线消费) */
403
+ export interface IssueCommentItem {
404
+ id: string;
405
+ author: {
406
+ login: string;
407
+ avatarUrl?: string;
408
+ };
409
+ authorAssociation?: string;
410
+ body: string;
411
+ createdAt: string;
412
+ url: string;
413
+ /**
414
+ * GitHub 侧被折叠/隐藏的评论(垃圾、攻击性内容等)。
415
+ * 为什么必须透出:实测 cli/cli#1 的评论带 `isMinimized: true, minimizedReason: "SPAM"`,
416
+ * 若照常渲染成普通讨论,用户会把垃圾内容当成真实技术意见。
417
+ */
418
+ isMinimized?: boolean;
419
+ minimizedReason?: string;
420
+ includesCreatedEdit?: boolean;
421
+ }
422
+ /**
423
+ * Issue 关联的 PR 引用。
424
+ *
425
+ * 字段集受限于 gh 的实际返回:实测 `gh issue view --json closedByPullRequestsReferences`
426
+ * 的节点只有 `id / number / repository / url`,**没有 title 与 state**。
427
+ * UI 因此只渲染 `#number` 链接,不得假设更多字段存在。
428
+ */
429
+ export interface IssueLinkedPrRef {
430
+ number: number;
431
+ url: string;
432
+ /** `owner/repo`;跨仓关联时与 Issue 自身仓库不同 */
433
+ repository?: string;
434
+ }
435
+ /** Issue 里程碑(实测形态:number/title/description/dueOn,无 state) */
436
+ export interface IssueMilestoneInfo {
437
+ number?: number;
438
+ title: string;
439
+ /** ISO 时间;未设置截止日时为缺省 */
440
+ dueOn?: string;
441
+ }
442
+ /** 子议题完成度摘要(gh `subIssuesSummary` 的直接映射) */
443
+ export interface IssueSubIssuesSummary {
444
+ completed: number;
445
+ total: number;
446
+ }
447
+ /**
448
+ * Issue 完整详情数据契约(供 IssueDetailModal 在线查看消费)。
449
+ *
450
+ * 为什么不复用 `IssueItem`:列表项只需要渲染一行卡片,字段刻意精简;
451
+ * 详情需要正文、评论时间线、里程碑、子议题等重字段,混在一起会让
452
+ * 全局概览的缓存与快照体积翻数倍(每条 Issue 都拖着全部评论)。
453
+ */
454
+ export interface IssueDetailData {
455
+ number: number;
456
+ title: string;
457
+ state: 'OPEN' | 'CLOSED';
458
+ /** COMPLETED / NOT_PLANNED / REOPENED / ''(实测空串形态存在) */
459
+ stateReason?: string;
460
+ url: string;
461
+ createdAt: string;
462
+ updatedAt: string;
463
+ closedAt?: string;
464
+ author: {
465
+ login: string;
466
+ avatarUrl?: string;
467
+ };
468
+ body: string;
469
+ labels: Array<{
470
+ name: string;
471
+ color?: string;
472
+ description?: string;
473
+ }>;
474
+ assignees: Array<{
475
+ login: string;
476
+ }>;
477
+ milestone?: IssueMilestoneInfo;
478
+ isPinned?: boolean;
479
+ issueType?: string;
480
+ comments: IssueCommentItem[];
481
+ linkedPrs: IssueLinkedPrRef[];
482
+ subIssues?: IssueSubIssuesSummary;
483
+ /**
484
+ * 正文与评论中识别到的图片 URL(与 github_issue(view_images) 同源提取)。
485
+ *
486
+ * 为什么只给清单不在弹窗内联渲染:图片可能来自任意 https 外域,
487
+ * 在面板里直接 `<img src>` 会把宿主页面 Referer/来源暴露给第三方,
488
+ * 也绕过了插件的域名白名单防线。UI 只展示「附件 N 张」并给外链。
489
+ */
490
+ images: string[];
491
+ /**
492
+ * 是否发生过字段集降级。
493
+ * 为什么需要:老版本 gh 遇到未知 JSON 字段会整体失败(实测
494
+ * `Unknown JSON field: "closedAt"`),此时回退到 BASE 字段集重试;
495
+ * 该标记供排查「为什么没看到里程碑/关联 PR」这类差异,不弹错。
496
+ *
497
+ * 注意:不设 truncated 标记——响应被输出阈值截断时 executor 直接返回
498
+ * `ok:false` + 浏览器直达链接,绝不会产出「零评论」的空详情对象,
499
+ * 因此该失败语义不需要在数据契约里再表达一次。
500
+ */
501
+ fieldsDegraded?: boolean;
502
+ }
208
503
  export interface IssueStats {
209
504
  total: number;
210
505
  reported: number;
@@ -253,6 +548,14 @@ export interface GitHubOverviewData {
253
548
  networkError?: boolean;
254
549
  /** 网络异常的原因或提示 */
255
550
  networkErrorReason?: string;
551
+ /**
552
+ * 确定性降级说明(非错误)。
553
+ *
554
+ * 为什么与 networkError 分开:仓库关闭 Issues、未启用 Discussions 这类
555
+ * 「配置事实」不是故障,不该触发网络告警横幅与重试。它们只需要在面板上
556
+ * 安静地说明一句「该仓库未开启 Issues」,让用户知道空列表是正常的。
557
+ */
558
+ notices?: string[];
256
559
  /** 插件版本与升级信息(由内存缓存快速读取,不阻塞首屏业务) */
257
560
  updateInfo?: UpdateInfo;
258
561
  /** 本地 Git 状态(ahead/behind/dirty,remote-tracking 快照语义);非 Git 工作区为 undefined */
@@ -276,6 +579,17 @@ export interface GitHubOverviewData {
276
579
  } | null;
277
580
  /** 当前工作区的 Git 分支列表(供右侧栏分支选择器 0ms 秒开) */
278
581
  branches?: GitBranchesResult | null;
582
+ /**
583
+ * 单仓库 PR 列表是否触及条数上限(仅触顶时下发)。
584
+ *
585
+ * 为什么必须透出:查询已放宽为 `--state all`,列表按 number 降序取前 N 条,
586
+ * 历史 PR 超过上限时界面会静默少显示——用户切到「Closed」看到几条
587
+ * 就会误以为仓库只有这么几个已关闭 PR,无法区分「就这么多」与「被截断了」。
588
+ * 与全局面板的 prsTruncated 同一语义。
589
+ */
590
+ prsTruncated?: boolean;
591
+ /** 单仓库 Issue 列表是否触及条数上限(语义同 prsTruncated) */
592
+ issuesTruncated?: boolean;
279
593
  }
280
594
  export interface UserRepoItem {
281
595
  name: string;
@@ -326,14 +640,28 @@ export interface GlobalOverviewData {
326
640
  myPrs: Array<{
327
641
  number: number;
328
642
  title: string;
643
+ /**
644
+ * PR 状态。为什么必须下发:主面板默认展示「全部」而非仅 open
645
+ * (实测账号可能零 open PR,只查 open 会恒为空白),
646
+ * 前端要靠这个字段做状态胶囊筛选与徽章标注。
647
+ */
648
+ state?: 'OPEN' | 'CLOSED' | 'MERGED';
329
649
  repository: {
330
650
  nameWithOwner: string;
331
651
  };
332
652
  url: string;
333
653
  updatedAt: string;
654
+ isDraft?: boolean;
334
655
  }>;
335
656
  myIssues: GlobalIssueItem[];
336
657
  issueStats?: IssueStats;
658
+ /**
659
+ * PR 检索是否触及上限(仅触顶时下发)。
660
+ * 为什么必须透出:去掉 open 过滤后按 updatedAt 降序返回最近 N 条,
661
+ * 若账号历史 PR 超过上限,界面会静默少显示,用户无法区分
662
+ * 「列表到此为止」与「数据没取到」。
663
+ */
664
+ prsTruncated?: boolean;
337
665
  lastUpdated: string;
338
666
  error?: string;
339
667
  /** 网络是否发生异常(超时、连接拒绝、代理阻断等) */
@@ -342,6 +670,11 @@ export interface GlobalOverviewData {
342
670
  networkErrorReason?: string;
343
671
  /** 工作区矩阵读取失败的原因。区分「没有工作区」与「读不到工作区」,避免把故障显示成空状态 */
344
672
  workspaceError?: string;
673
+ /**
674
+ * 确定性降级说明(非错误):仓库关闭 Issues、未启用 Discussions 等配置事实。
675
+ * 与 networkError 互斥——它不是故障,不该触发告警横幅与重试。
676
+ */
677
+ notices?: string[];
345
678
  /** 插件版本与升级信息(由内存缓存快速读取,不阻塞首屏业务) */
346
679
  updateInfo?: UpdateInfo;
347
680
  }
@@ -363,11 +696,13 @@ export interface UpdateInfo {
363
696
  checkedAt: number;
364
697
  }
365
698
  export type UpgradeState = 'idle' | 'upgrading' | 'success' | 'failed';
699
+ export type UpgradeErrorType = 'MINIMUM_RELEASE_AGE_VIOLATION' | 'TIMEOUT' | 'UNKNOWN';
366
700
  export interface UpgradeStatusResult {
367
701
  state: UpgradeState;
368
702
  targetVersion?: string;
369
703
  message?: string;
370
704
  log?: string;
705
+ errorType?: UpgradeErrorType;
371
706
  startedAt?: number;
372
707
  completedAt?: number;
373
708
  /** 识别到的宿主环境类型:desktop 为桌面版,web 为网页/CLI版 */
@@ -377,6 +712,7 @@ export interface UpgradeExecutionResult {
377
712
  ok: boolean;
378
713
  message: string;
379
714
  needRestart: boolean;
715
+ errorType?: UpgradeErrorType;
380
716
  log?: string;
381
717
  environment?: 'desktop' | 'web';
382
718
  }
@@ -36,13 +36,16 @@ export function CommitDetailModal(props) {
36
36
  if (!effectiveBody && !effectiveStat && fetchedDetail) {
37
37
  effectiveStat = fetchedDetail;
38
38
  }
39
+ var parsedStat = React.useMemo(() => {
40
+ return effectiveStat && typeof parseGitStatText === "function" ? parseGitStatText(effectiveStat) : null;
41
+ }, [effectiveStat]);
39
42
  return h("div", {
40
43
  className: "dsh-github-modal-overlay",
41
44
  onClick: props.onClose,
42
45
  style: { zIndex: 100000 },
43
46
  }, h("div", {
44
47
  className: "dsh-github-modal-dialog",
45
- style: { width: "94%", maxWidth: "460px", maxHeight: "84vh", display: "flex", flexDirection: "column", overflow: "hidden" },
48
+ style: { width: "94%", maxWidth: "520px", maxHeight: "86vh", display: "flex", flexDirection: "column", overflow: "hidden" },
46
49
  onClick: function (e) { e.stopPropagation(); },
47
50
  }, h("div", { className: "dsh-github-modal-header", style: { flexShrink: 0 } }, h("div", { style: { display: "flex", alignItems: "center", gap: "8px" } }, renderGitIcon(15, "#1296db"), h("h3", { style: { margin: 0, fontSize: "14px", fontWeight: 600 } }, "提交详情")), h("button", {
48
51
  style: { background: "none", border: "none", cursor: "pointer", fontSize: "18px", color: "var(--dsw-alias-label-secondary, #656d76)", padding: "0 4px" },
@@ -65,22 +68,149 @@ export function CommitDetailModal(props) {
65
68
  },
66
69
  }, effectiveBody)
67
70
  : null, effectiveStat
68
- ? h("div", null, h("div", { style: { fontSize: "11px", fontWeight: 600, marginBottom: "4px", opacity: 0.8 } }, "📁 文件变更统计:"), h("pre", {
69
- className: "dsh-github-commit-pre",
71
+ ? h("div", { style: { display: "flex", flexDirection: "column", gap: "6px" } }, h("div", {
70
72
  style: {
73
+ display: "flex",
74
+ alignItems: "center",
75
+ justifyContent: "space-between",
71
76
  fontSize: "11px",
72
- lineHeight: "1.5",
73
- whiteSpace: "pre-wrap",
74
- wordBreak: "break-word",
75
- margin: 0,
76
- maxHeight: "34vh",
77
- overflowY: "auto",
78
- padding: "8px 10px",
79
- borderRadius: "6px",
80
- background: "var(--dsw-alias-bg-module-platform, #f6f8fa)",
81
- border: "1px solid var(--dsw-alias-border-l2, #e1e4e8)",
77
+ fontWeight: 600,
82
78
  },
83
- }, effectiveStat))
79
+ }, h("div", { style: { opacity: 0.85, display: "flex", alignItems: "center", gap: "6px" } }, "📁 文件变更统计", parsedStat?.summary
80
+ ? h("span", { style: { opacity: 0.65, fontWeight: 400 } }, `(${parsedStat.summary.filesChanged} 个文件)`)
81
+ : parsedStat?.files
82
+ ? h("span", { style: { opacity: 0.65, fontWeight: 400 } }, `(${parsedStat.files.length} 个文件)`)
83
+ : null), parsedStat?.summary
84
+ ? h("div", { style: { display: "flex", gap: "6px", fontFamily: "monospace", fontWeight: 600 } }, parsedStat.summary.insertions > 0
85
+ ? h("span", {
86
+ style: {
87
+ color: "var(--dsw-alias-state-success-primary, #1a7f37)",
88
+ backgroundColor: "var(--dsw-alias-state-success-subtle, rgba(46,160,67,0.12))",
89
+ padding: "1px 6px",
90
+ borderRadius: "10px",
91
+ },
92
+ }, `+${parsedStat.summary.insertions}`)
93
+ : null, parsedStat.summary.deletions > 0
94
+ ? h("span", {
95
+ style: {
96
+ color: "var(--dsw-alias-state-error-primary, #cf222e)",
97
+ backgroundColor: "var(--dsw-alias-state-error-subtle, rgba(248,81,73,0.12))",
98
+ padding: "1px 6px",
99
+ borderRadius: "10px",
100
+ },
101
+ }, `-${parsedStat.summary.deletions}`)
102
+ : null)
103
+ : null), parsedStat && parsedStat.files && parsedStat.files.length > 0
104
+ ? h("div", {
105
+ className: "dsh-github-stat-list",
106
+ style: {
107
+ borderRadius: "6px",
108
+ border: "1px solid var(--dsw-alias-border-l2, #e1e4e8)",
109
+ background: "var(--dsw-alias-bg-module-platform, #f6f8fa)",
110
+ maxHeight: "36vh",
111
+ overflowY: "auto",
112
+ display: "flex",
113
+ flexDirection: "column",
114
+ },
115
+ }, parsedStat.files.map(function (f, idx) {
116
+ var diffBlocks = [];
117
+ if (!f.isBinary && f.total > 0) {
118
+ var totalRatio = f.additions + f.deletions;
119
+ var addBlocks = totalRatio > 0 ? Math.round((f.additions / totalRatio) * 5) : 0;
120
+ var delBlocks = totalRatio > 0 ? Math.round((f.deletions / totalRatio) * 5) : 0;
121
+ if (f.additions > 0 && addBlocks === 0)
122
+ addBlocks = 1;
123
+ if (f.deletions > 0 && delBlocks === 0)
124
+ delBlocks = 1;
125
+ while (addBlocks + delBlocks > 5) {
126
+ if (addBlocks > delBlocks)
127
+ addBlocks--;
128
+ else
129
+ delBlocks--;
130
+ }
131
+ var neutralBlocks = Math.max(0, 5 - addBlocks - delBlocks);
132
+ for (var bi = 0; bi < addBlocks; bi++) {
133
+ diffBlocks.push(h("span", { key: "a" + bi, style: { width: "3px", height: "8px", borderRadius: "1px", backgroundColor: "var(--dsw-alias-state-success-primary, #1a7f37)" } }));
134
+ }
135
+ for (var bj = 0; bj < delBlocks; bj++) {
136
+ diffBlocks.push(h("span", { key: "d" + bj, style: { width: "3px", height: "8px", borderRadius: "1px", backgroundColor: "var(--dsw-alias-state-error-primary, #cf222e)" } }));
137
+ }
138
+ for (var bk = 0; bk < neutralBlocks; bk++) {
139
+ diffBlocks.push(h("span", { key: "n" + bk, style: { width: "3px", height: "8px", borderRadius: "1px", backgroundColor: "var(--dsw-alias-border-subtle, rgba(0,0,0,0.15))" } }));
140
+ }
141
+ }
142
+ return h("div", {
143
+ key: idx,
144
+ className: "dsh-github-stat-row",
145
+ style: {
146
+ display: "flex",
147
+ alignItems: "center",
148
+ justifyContent: "space-between",
149
+ padding: "6px 10px",
150
+ fontSize: "11px",
151
+ lineHeight: "1.4",
152
+ borderBottom: idx < parsedStat.files.length - 1 ? "1px solid var(--dsw-alias-border-subtle, rgba(0,0,0,0.06))" : "none",
153
+ gap: "8px",
154
+ },
155
+ title: f.path + (f.isBinary ? " (二进制文件)" : ` (+${f.additions}, -${f.deletions})`),
156
+ }, h("div", {
157
+ style: {
158
+ fontFamily: "ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, monospace",
159
+ overflow: "hidden",
160
+ textOverflow: "ellipsis",
161
+ whiteSpace: "nowrap",
162
+ flex: 1,
163
+ minWidth: 0,
164
+ color: "var(--dsw-alias-label-primary, #1f2328)",
165
+ },
166
+ }, f.path), h("div", {
167
+ style: {
168
+ display: "flex",
169
+ alignItems: "center",
170
+ gap: "6px",
171
+ flexShrink: 0,
172
+ fontFamily: "monospace",
173
+ fontWeight: 600,
174
+ },
175
+ }, f.isBinary
176
+ ? h("span", { style: { opacity: 0.6, fontSize: "10px" } }, "二进制")
177
+ : [
178
+ f.additions > 0
179
+ ? h("span", {
180
+ key: "add",
181
+ style: { color: "var(--dsw-alias-state-success-primary, #1a7f37)" },
182
+ }, `+${f.additions}`)
183
+ : null,
184
+ f.deletions > 0
185
+ ? h("span", {
186
+ key: "del",
187
+ style: { color: "var(--dsw-alias-state-error-primary, #cf222e)" },
188
+ }, `-${f.deletions}`)
189
+ : null,
190
+ diffBlocks.length > 0
191
+ ? h("div", {
192
+ key: "bar",
193
+ style: { display: "flex", gap: "1px", marginLeft: "2px", alignItems: "center" },
194
+ }, ...diffBlocks)
195
+ : null,
196
+ ]));
197
+ }))
198
+ : h("pre", {
199
+ className: "dsh-github-commit-pre",
200
+ style: {
201
+ fontSize: "11px",
202
+ lineHeight: "1.5",
203
+ whiteSpace: "pre-wrap",
204
+ wordBreak: "break-word",
205
+ margin: 0,
206
+ maxHeight: "34vh",
207
+ overflowY: "auto",
208
+ padding: "8px 10px",
209
+ borderRadius: "6px",
210
+ background: "var(--dsw-alias-bg-module-platform, #f6f8fa)",
211
+ border: "1px solid var(--dsw-alias-border-l2, #e1e4e8)",
212
+ },
213
+ }, effectiveStat))
84
214
  : null, !effectiveBody && !effectiveStat
85
215
  ? (fetchingDetail
86
216
  ? h("div", { style: { fontSize: "12px", opacity: 0.7, padding: "8px 0" } }, "⏳ 正在获取详细变更说明…")
@@ -0,0 +1,21 @@
1
+ /**
2
+ * 独立沉浸式指南大窗口组件 (Dedicated Large Reader Modal)
3
+ *
4
+ * 核心设计理由与交互契约:
5
+ * 1. 彻底从设置小窗口剥离,打造 920px 宽屏、85vh 高度的沉浸式阅读视界;
6
+ * 2. 深度适配 DSH 主题系统,全套样式基于 CSS 类与主题变量,支持深色 (data-ds-dark-theme) 与浅色无缝自适应;
7
+ * 3. 拒绝技术自嗨清单,从用户行动视角组织:3分钟快速上手、Agent实战Prompt、双界面图解与常见排错;
8
+ * 4. 宽屏卡片排列,配备 Prompt 一键复制到剪贴板与短暂反馈气泡;
9
+ * 5. 监听键盘 Esc 键与遮罩点击秒关,复制完指令直接回到聊天输入框发送,零打扰、不中断工作流。
10
+ *
11
+ * 【内容与代码同步的纪律】
12
+ * 本文件里出现的工具数量、action 数量、Tab 名称、按钮文案,全部以当前代码为准:
13
+ * 工具矩阵 = src/main.ts 的 18 处 ctx.tools.register;
14
+ * Tab 布局 = src/client.ts 的 TAB_DEFS 与 src/ui/workspace-panel.ts 的四个标签;
15
+ * 若后续增删工具或 Tab,必须同步改这里,否则用户会照着过时说明去找不存在的入口。
16
+ */
17
+ export declare function GithubHelpModal(props: {
18
+ isOpen: boolean;
19
+ onClose: () => void;
20
+ initialTab?: string;
21
+ }): any;