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/executor.d.ts CHANGED
@@ -1,10 +1,13 @@
1
- import type { GhExecutionOptions, GhResult, AuthStatus, RepoMetadata, PluginConfig, GitLocalStatus, GitCommitLog, GitCommitEntry, GitBranchesResult } from './types.js';
1
+ import type { GhExecutionOptions, GhResult, AuthStatus, RepoMetadata, PluginConfig, GitLocalStatus, GitCommitLog, GitCommitEntry, GitBranchesResult, AssociatedRepoItem, PrDetailData, IssueDetailData } from './types.js';
2
2
  import type { ProxyManager } from './proxy.js';
3
3
  import { parseGhVersion, compareGhVersions } from './executor/version-utils.js';
4
4
  import { spawnWithStdin, safeSlice, cutByUtf8Bytes, truncateOutput } from './executor/process-utils.js';
5
- import { parseGitStatusOutput, parseGitLogLines, parseGitBranchLines } from './executor/git-parser.js';
5
+ import { parseGitStatusOutput, parseGitLogLines, parseGitBranchLines, parseUncommittedDiff, parseGitStatText, parseRemoteUrls, parseGitConfig, resolveRemoteTarget, parseGithubUrl, parseRemoteHost, classifyRepoLayout, pickGithubRemote, splitUnifiedDiff } from './executor/git-parser.js';
6
+ import type { ParsedGitConfig, ParsedRemote } from './executor/git-parser.js';
6
7
  import { getPlatformInstallGuide, getGhUpgradeHint } from './executor/platform-guide.js';
7
- export { parseGhVersion, compareGhVersions, spawnWithStdin, safeSlice, cutByUtf8Bytes, truncateOutput, parseGitStatusOutput, parseGitLogLines, parseGitBranchLines, getPlatformInstallGuide, getGhUpgradeHint, };
8
+ import { classifyGhError, describeDeterministicError, isRetryableError, permissionRemedy, isPermissionError, type GhErrorKind } from './executor/error-classifier.js';
9
+ export { parseGhVersion, compareGhVersions, spawnWithStdin, safeSlice, cutByUtf8Bytes, truncateOutput, parseGitStatusOutput, parseGitLogLines, parseGitBranchLines, parseUncommittedDiff, parseGitStatText, parseRemoteUrls, parseGitConfig, resolveRemoteTarget, parseGithubUrl, parseRemoteHost, classifyRepoLayout, pickGithubRemote, splitUnifiedDiff, getPlatformInstallGuide, getGhUpgradeHint, classifyGhError, describeDeterministicError, isRetryableError, permissionRemedy, isPermissionError, };
10
+ export type { GhErrorKind, ParsedGitConfig, ParsedRemote };
8
11
  export declare class GhExecutor {
9
12
  ghPath: string;
10
13
  defaultTimeoutMs: number;
@@ -114,6 +117,31 @@ export declare class GhExecutor {
114
117
  * 符合 global-overview 端点「工作区探测不发网络」的性能红线。
115
118
  */
116
119
  getLocalGitStatus(cwd?: string): Promise<GitLocalStatus | null>;
120
+ /**
121
+ * 分页获取本地未提交文件列表(无网络开销,纯本地毫秒级响应)
122
+ */
123
+ getUncommittedFiles(cwd?: string, limit?: number, skip?: number): Promise<{
124
+ items: Array<{
125
+ status: string;
126
+ path: string;
127
+ }>;
128
+ totalCount: number;
129
+ hasMore: boolean;
130
+ }>;
131
+ /**
132
+ * 获取指定文件的未提交 Git Diff 对比(Unified Diff 格式)。
133
+ * 严格实施路径穿越安全校验;支持已跟踪修改文件、暂存文件与未跟踪新增文件(??)。
134
+ */
135
+ getFileDiff(cwd: string | undefined, relativePath: string): Promise<{
136
+ ok: boolean;
137
+ filePath: string;
138
+ status?: string;
139
+ diff?: string;
140
+ additions?: number;
141
+ deletions?: number;
142
+ isBinary?: boolean;
143
+ error?: string;
144
+ }>;
117
145
  /**
118
146
  * 本地 Git 提交记录(三段一次返回:本地领先 / 远程领先 / 最近提交)。
119
147
  *
@@ -157,6 +185,57 @@ export declare class GhExecutor {
157
185
  currentBranch?: string;
158
186
  error?: string;
159
187
  }>;
188
+ /**
189
+ * 推导当前工作区可供用户切换的全部关联 GitHub 仓库列表。
190
+ *
191
+ * 数据来源(纯本地+拓扑合成,零新增网络开销):
192
+ * 1. 扫描本地 .git/config 的所有 GitHub remote(如 origin, upstream 等);
193
+ * 2. 若当前仓库为 Fork 且存在 parent(上游),合并上游仓库(支持本地未配 upstream 的情形);
194
+ * 3. 标识当前基准 (isCurrent) 及角色 (fork, upstream, origin, parent)。
195
+ */
196
+ getAssociatedRepos(cwd?: string, currentRepo?: RepoMetadata | null, currentUser?: string | null): AssociatedRepoItem[];
197
+ /**
198
+ * 设置当前工作区的默认基准 GitHub 仓库(调用官方 `gh repo set-default` 并在本地 git config 持久化)。
199
+ *
200
+ * 为什么调用 gh repo set-default:
201
+ * 1. 它会在 .git/config 写入 `remote.<name>.gh-resolved = base`,这是官方的持久化事实源;
202
+ * 2. 执行后,不仅本插件的 Web 驾驶舱,外部系统终端中运行的 gh pr, gh issue, gh run 也将 100% 对齐;
203
+ * 3. 严格做参数格式校验,仅允许合法的 owner/repo 或合法 remote 别名,杜绝命令注入。
204
+ */
205
+ setDefaultRepo(cwd: string | undefined, targetRepoOrRemote: string): Promise<{
206
+ ok: boolean;
207
+ target?: string;
208
+ error?: string;
209
+ }>;
210
+ /**
211
+ * 获取 Pull Request 完整详情与变更 Diff 数据(用于 PR 在线审阅与代码比对)。
212
+ *
213
+ * 架构设计:
214
+ * 1. 严格参数白名单校验(PR 编号必须为正整数,repo 必须为合法 owner/repo 格式);
215
+ * 2. 并行拉取元数据 (gh pr view) 与 Unified Diff (gh pr diff);
216
+ * 3. 在内存通过 splitUnifiedDiff 对 Diff 原文按修改文件切片索引,
217
+ * 前端点击任一文件即可 0ms 呈现代码行级增删对比,绝无二次网络往返。
218
+ */
219
+ getPrDetail(cwd?: string, prNumber?: number | string, repo?: string): Promise<{
220
+ ok: boolean;
221
+ data?: PrDetailData;
222
+ error?: string;
223
+ }>;
224
+ /**
225
+ * 获取 Issue 完整详情(供 IssueDetailModal 在线查看消费)。
226
+ *
227
+ * 架构设计:
228
+ * 1. 严格参数白名单(编号必须为正整数,repo 必须为 owner/repo 形态)——与
229
+ * getPrDetail 同一套规则,杜绝任何形式的命令/路径注入;
230
+ * 2. 单次 `gh issue view --json` 往返即可拿到正文、评论时间线、里程碑与关联 PR,
231
+ * 不拆成多路并发(Issue 没有 diff 这类重载荷,多开进程反而增加失败面);
232
+ * 3. 老版本 gh 遇未知字段会整体失败,因此保留 BASE 字段集降级链。
233
+ */
234
+ getIssueDetail(cwd?: string, issueNumber?: number | string, repo?: string): Promise<{
235
+ ok: boolean;
236
+ data?: IssueDetailData;
237
+ error?: string;
238
+ }>;
160
239
  /** 生成当前操作系统平台的推荐安装引导与命令 */
161
240
  private getPlatformInstallGuide;
162
241
  /**
@@ -176,13 +255,77 @@ export declare class GhExecutor {
176
255
  */
177
256
  private probeAuth;
178
257
  /**
179
- * 仅从本地 .git/config 解析仓库身份,绝不发起网络请求。
258
+ * 从 `refs/remotes/<remote>/HEAD` 符号引用中读取仓库默认分支。
259
+ *
260
+ * 为什么只认这一个来源(其余全部实测排除):
261
+ * - `packed-refs` 只存具体 sha,不含 symbolic ref —— 读不到;
262
+ * - 当前 HEAD 指向的是**用户 checkout 的分支**,不是仓库默认分支,语义错误;
263
+ * - 猜 main/master —— 无依据,宁可回落固定值也不猜。
264
+ *
265
+ * @returns 分支名;文件不存在(未 fetch 过)或内容不是符号引用时返回 null,
266
+ * 由调用方回落到既有默认值(保持原有行为,不引入新错误)
267
+ */
268
+ readDefaultBranchFromRef(remoteName: string, cwd?: string): string | null;
269
+ /**
270
+ * 解析工作区真实的 git 目录的**两个**路径:局部目录与共享仓库目录。
271
+ *
272
+ * 为什么必须分开:worktree 的 `HEAD` 在局部目录(`.git/worktrees/<name>/HEAD`),
273
+ * 而 `config` 在共享目录(`commondir` 指向处)。只解析其中一个必然读错:
274
+ * - 只读局部 → 找不到 config(remote 配置在共享目录)
275
+ * - 只读共享 → 拿到**主工作树**的分支名,进而按错误分支查 `branch.*.remote`
276
+ *
277
+ * 实测:主工作树 HEAD 是 `ref: refs/heads/master`,worktree 的是 `ref: refs/heads/feat`。
278
+ *
279
+ * 常规仓库两者指向同一目录。
280
+ *
281
+ * @returns 两个绝对路径;非 Git 目录/指针不可解析时返回 null(绝不抛错)
282
+ */
283
+ resolveGitDirInfo(cwd?: string): {
284
+ localDir: string;
285
+ commonDir: string;
286
+ } | null;
287
+ /**
288
+ * 解析工作区真实的 git 目录(共享仓库目录)。
289
+ * 薄封装自 resolveGitDirInfo:读 `config` 的场景(远程指纹)只需要共享目录。
290
+ */
291
+ resolveGitDirPath(cwd?: string): string | null;
292
+ /**
293
+ * remote 配置指纹:全部 remote url 去重、排序、拼接后取 SHA-256 前 12 位。
294
+ *
295
+ * 为什么不用身份解析做缓存失效判定:身份解析要回答「变成了什么」,
296
+ * 一旦解析器对某种 config 布局有偏差(如双 remote 时取错 remote 段),
297
+ * 比对就永远失配——缓存再也不命中,每次请求都重拉全部 gh 子进程。
298
+ * 指纹只回答「变了没有」,不依赖任何语义理解,因此对该类偏差免疫。
299
+ *
300
+ * 反例(实测):`git remote rename origin upstream` 后 `git remote add origin <fork>`,
301
+ * config 里 upstream 段排在 origin 段前,取「第一个 github.com 匹配」会拿到上游,
302
+ * 而 git 权威解析(branch.<name>.remote → origin)拿到的是 fork。
303
+ *
304
+ * @returns 12 位十六进制指纹;仅当「非 Git 目录 / config 不可读」时返回 null
305
+ * (调用方据此保守判定为「未变更」,绝不因探测失败误清空缓存)。
306
+ * 注意:**config 可读但没有任何 remote 时返回的是空集合的哈希,不是 null**——
307
+ * 「仓库没有远程」是一个确定事实,它与「读不到配置」必须区分开:
308
+ * 否则用户删光 remote 后指纹恒为 null,旧快照再也无法失效。
309
+ */
310
+ getRemoteFingerprint(cwd?: string): string | null;
311
+ /**
312
+ * 读取并解析工作区的 git 远程视图(`.git/config` + `HEAD`)。
313
+ * 内部方法:把「两个路径各读哪个文件」的细节收敛在一处,
314
+ * 避免各调用点自己拼路径读错 worktree 的文件。
315
+ */
316
+ private readParsedGitConfig;
317
+ /**
318
+ * 仅从本地 `.git/config` 解析仓库身份,绝不发起网络请求。
180
319
  *
181
320
  * 为什么需要它:全局驾驶舱要为每个工作区认识别仓库,而 getRepoMetadata 会调用
182
321
  * `gh repo view` 走网络。实测 5 个工作区时该端点冷启动 20.5 秒,其中绝大部分耗在这 5 次
183
322
  * 网络往返上——而有界并发根本救不了它,因为瓶颈是网络延迟而不是进程排队。
184
323
  * 工作区矩阵只需要「这个目录属于哪个仓库」这一身份信息,本地 .git/config 已经完整具备,
185
324
  * 描述/star 数等富字段是当前仓库详情才需要的。这一步把每个工作区从 1 次网络往返降为 0。
325
+ *
326
+ * 解析口径与 git 一致(三级回落),不再「取 config 里第一个 github.com 匹配」——
327
+ * 后者在 `git remote rename origin upstream` + `add origin <fork>` 的布局下会取到上游,
328
+ * 而 git 认的是 origin(实测)。
186
329
  */
187
330
  getRepoIdentityFromLocal(cwd?: string): RepoMetadata | null;
188
331
  /**