dsh-github-flow 0.6.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.
- package/README.en.md +98 -15
- package/README.md +101 -15
- package/docs/UPGRADE-NOTES.md +12 -11
- package/docs/architecture.md +56 -13
- package/lib/api/route-helpers.d.ts +19 -0
- package/lib/api/route-helpers.js +54 -0
- package/lib/api/routes-actions.js +75 -1
- package/lib/api/routes-lifecycle.js +8 -1
- package/lib/api/routes-overview.d.ts +26 -0
- package/lib/api/routes-overview.js +276 -34
- package/lib/api/routes-proxy.js +8 -1
- package/lib/api/routes.d.ts +11 -1
- package/lib/api/routes.js +15 -2
- package/lib/cache.d.ts +0 -21
- package/lib/cache.js +0 -37
- package/lib/client.d.ts +2 -1
- package/lib/client.js +1585 -429
- package/lib/destructive.d.ts +59 -0
- package/lib/destructive.js +285 -0
- package/lib/executor/args-guard.d.ts +17 -0
- package/lib/executor/args-guard.js +23 -0
- package/lib/executor/failure-anchors.d.ts +52 -0
- package/lib/executor/failure-anchors.js +116 -0
- package/lib/executor/pr-review.d.ts +95 -0
- package/lib/executor/pr-review.js +175 -0
- package/lib/executor/run-log.d.ts +36 -0
- package/lib/executor/run-log.js +78 -0
- package/lib/executor.d.ts +156 -3
- package/lib/executor.js +594 -28
- package/lib/github-service.d.ts +1 -1
- package/lib/main.d.ts +92 -18
- package/lib/main.js +186 -95
- package/lib/renderers/common.d.ts +0 -3
- package/lib/renderers/common.js +8 -44
- package/lib/renderers/markdown.d.ts +1 -0
- package/lib/renderers/markdown.js +1 -0
- package/lib/renderers/pr.js +26 -1
- package/lib/renderers/task.d.ts +9 -0
- package/lib/renderers/task.js +120 -0
- package/lib/review-rules.d.ts +60 -0
- package/lib/review-rules.js +204 -0
- package/lib/shared/state-badges.d.ts +42 -0
- package/lib/shared/state-badges.js +102 -0
- package/lib/task-primitives.d.ts +109 -0
- package/lib/task-primitives.js +194 -0
- package/lib/tool-tiers.d.ts +57 -0
- package/lib/tool-tiers.js +168 -0
- package/lib/tools/agent-task.js +4 -4
- package/lib/tools/api.js +18 -6
- package/lib/tools/cache.js +42 -40
- package/lib/tools/codespace.js +4 -12
- package/lib/tools/discussion.js +4 -4
- package/lib/tools/gist.d.ts +1 -39
- package/lib/tools/gist.js +4 -14
- package/lib/tools/help.js +52 -13
- package/lib/tools/issue.js +16 -28
- package/lib/tools/label.js +4 -15
- package/lib/tools/pr.d.ts +47 -0
- package/lib/tools/pr.js +170 -33
- package/lib/tools/project.js +4 -13
- package/lib/tools/release.js +11 -22
- package/lib/tools/repo.js +4 -4
- package/lib/tools/ruleset.js +9 -6
- package/lib/tools/run.js +76 -24
- package/lib/tools/secret.js +11 -17
- package/lib/tools/skill.js +18 -22
- package/lib/tools/status.js +4 -4
- package/lib/tools/task.d.ts +219 -0
- package/lib/tools/task.js +457 -0
- package/lib/types.d.ts +183 -1
- package/lib/ui/help-modal.d.ts +1 -1
- package/lib/ui/help-modal.js +20 -10
- package/lib/ui/issue-modal.js +7 -3
- package/lib/ui/pr-modal.js +50 -11
- package/lib/ui/run-modal.d.ts +26 -0
- package/lib/ui/run-modal.js +486 -0
- package/lib/ui/styles.d.ts +1 -1
- package/lib/ui/styles.js +370 -44
- package/lib/ui/sync-settings-modal.js +1 -1
- package/lib/ui/workspace-panel.js +78 -23
- package/lib/version.d.ts +1 -1
- package/lib/version.js +1 -1
- package/package.json +9 -2
package/lib/types.d.ts
CHANGED
|
@@ -36,6 +36,22 @@ export interface PluginConfig {
|
|
|
36
36
|
* 注意:放在 Cordis Config 意味着修改此项会触发插件 reload(静态默认值,可接受)。
|
|
37
37
|
*/
|
|
38
38
|
proxyUrl?: string;
|
|
39
|
+
/**
|
|
40
|
+
* 工具档位:控制注册给模型的工具数量(默认不填 = `full`,18 个全量,零破坏)。
|
|
41
|
+
*
|
|
42
|
+
* 三种预设 `core`(5 个)/ `ci`(7 个)/ `maintain`(8 个),也可直接写工具名
|
|
43
|
+
* (如 `['github_pr','github_release']`)混用。未知名字会 console.warn 一次后忽略。
|
|
44
|
+
* 存在的理由:18 个工具的 schema 常驻约 24,000 字符,等于每轮对话都在为用不到的工具付费。
|
|
45
|
+
*/
|
|
46
|
+
toolsets?: string[];
|
|
47
|
+
/**
|
|
48
|
+
* 只读档位(默认 false)。
|
|
49
|
+
*
|
|
50
|
+
* 硬语义,不做运行时判断:写 action 在**注册期**就从 `enum` 里被剔除,模型连
|
|
51
|
+
* 「可以写」都看不到,宿主参数校验会直接拒绝。`github_api`(端点与 method 均可任意,
|
|
52
|
+
* 无法降级)在只读模式下整体剔除。
|
|
53
|
+
*/
|
|
54
|
+
read_only?: boolean;
|
|
39
55
|
}
|
|
40
56
|
/** 数据同步与自动化感知偏好配置 */
|
|
41
57
|
export interface SyncSettings {
|
|
@@ -245,6 +261,24 @@ export interface GitLocalStatus {
|
|
|
245
261
|
path: string;
|
|
246
262
|
}>;
|
|
247
263
|
}
|
|
264
|
+
/**
|
|
265
|
+
* 工作区干净度闸门结果(`executor.ensureCleanWorkspace`)。
|
|
266
|
+
*
|
|
267
|
+
* 为什么要有 `reason: 'unknown'`:`getLocalGitStatus()` 在 git 不可用或非仓库时返回 null。
|
|
268
|
+
* 旧实现用 `if (gitSt && gitSt.dirtyCount > 0)` 判断,null 时表达式为假 → 闸门静默放行。
|
|
269
|
+
* 安全闸门不能因为「读不到状态」而失效,所以这里把「读不到」与「干净」显式区分开。
|
|
270
|
+
*/
|
|
271
|
+
export type WorkspaceCleanResult = {
|
|
272
|
+
ok: true;
|
|
273
|
+
dirtyCount: 0;
|
|
274
|
+
samples: string[];
|
|
275
|
+
} | {
|
|
276
|
+
ok: false;
|
|
277
|
+
reason: 'dirty' | 'unknown';
|
|
278
|
+
dirtyCount: number;
|
|
279
|
+
samples: string[];
|
|
280
|
+
error: string;
|
|
281
|
+
};
|
|
248
282
|
/** 单条提交记录(结构化解析) */
|
|
249
283
|
export interface GitCommitEntry {
|
|
250
284
|
sha: string;
|
|
@@ -276,7 +310,7 @@ export interface PrItem {
|
|
|
276
310
|
number: number;
|
|
277
311
|
title: string;
|
|
278
312
|
state: 'OPEN' | 'CLOSED' | 'MERGED';
|
|
279
|
-
author
|
|
313
|
+
author?: {
|
|
280
314
|
login: string;
|
|
281
315
|
avatarUrl?: string;
|
|
282
316
|
};
|
|
@@ -361,6 +395,15 @@ export interface PrDetailData {
|
|
|
361
395
|
};
|
|
362
396
|
headRefName: string;
|
|
363
397
|
baseRefName: string;
|
|
398
|
+
/**
|
|
399
|
+
* 分支尖端的 commit SHA。
|
|
400
|
+
*
|
|
401
|
+
* 为什么必须透出:分支名会移动(force-push / 新提交),拿它当审查锚点等于
|
|
402
|
+
* 「我审的到底是哪一版」永远说不清。SHA 是不可移动的事实源,也是行级评注
|
|
403
|
+
* 写入时 `commit_id` 的取值来源。
|
|
404
|
+
*/
|
|
405
|
+
headRefOid?: string;
|
|
406
|
+
baseRefOid?: string;
|
|
364
407
|
headRepoOwner?: string;
|
|
365
408
|
body: string;
|
|
366
409
|
reviewDecision?: string;
|
|
@@ -391,6 +434,25 @@ export interface PrDetailData {
|
|
|
391
434
|
* 静默展示「该文件无差异」会让用户误以为代码没改动——必须显式提示。
|
|
392
435
|
*/
|
|
393
436
|
diffTruncated?: boolean;
|
|
437
|
+
/**
|
|
438
|
+
* PR 实际变更的文件总数(来自 `gh pr view --json changedFiles`)。
|
|
439
|
+
*
|
|
440
|
+
* 为什么必须与 `files.length` 分开:`gh pr view --json files` 在 100 条处
|
|
441
|
+
* **静默截断**(cli/cli#12522),`files.length` 只是「返回了几条」。
|
|
442
|
+
* 把两者混为一谈,界面就会把「被截断」说成「就这么多」。
|
|
443
|
+
*/
|
|
444
|
+
changedFiles?: number;
|
|
445
|
+
/** `files` 清单是否被 gh 的 100 条上限截断(`files.length < changedFiles`) */
|
|
446
|
+
filesTruncated?: boolean;
|
|
447
|
+
/**
|
|
448
|
+
* 是否有文件**没拿到 diff 正文**(该文件在 `files` 里但 `fileDiffs` 无对应切片)。
|
|
449
|
+
*
|
|
450
|
+
* 与 `diffTruncated` 是并列而非同一概念:
|
|
451
|
+
* - `diffTruncated`:整个 diff 输出被阈值裁掉(原因在传输层);
|
|
452
|
+
* - `diffIncomplete`:清单里有这个文件、但切片里没有它(原因在数据缺失)。
|
|
453
|
+
* 两者处置不同(一个是「去看完整 diff」,一个是「这个文件没取到」),不能合并。
|
|
454
|
+
*/
|
|
455
|
+
diffIncomplete?: boolean;
|
|
394
456
|
}
|
|
395
457
|
/**
|
|
396
458
|
* Issue 分类维度契约:
|
|
@@ -535,6 +597,93 @@ export interface WorkflowRunItem {
|
|
|
535
597
|
url: string;
|
|
536
598
|
createdAt: string;
|
|
537
599
|
}
|
|
600
|
+
/**
|
|
601
|
+
* 单次 Run 里的一个 job 步骤(`gh run view --json jobs` 实测字段)。
|
|
602
|
+
*
|
|
603
|
+
* 为什么只有这四个时间/状态字段:step 在 GitHub 上**没有独立 URL**(锚点是页面内
|
|
604
|
+
* `#step:` 片段,API 不返回),也没有自己的一行日志入口。UI 因此只能展示
|
|
605
|
+
* 「序号 + 名称 + 结论 + 耗时」,任何「点击某步看日志」的交互都做不到——
|
|
606
|
+
* 日志的最小粒度是 job。
|
|
607
|
+
*/
|
|
608
|
+
export interface RunStepItem {
|
|
609
|
+
number: number;
|
|
610
|
+
name: string;
|
|
611
|
+
status: string;
|
|
612
|
+
conclusion?: string;
|
|
613
|
+
startedAt?: string;
|
|
614
|
+
completedAt?: string;
|
|
615
|
+
}
|
|
616
|
+
/** 单次 Run 里的一个 job(数据库 ID 即 check-run ID,可用于注解/日志端点) */
|
|
617
|
+
export interface RunJobItem {
|
|
618
|
+
databaseId: number;
|
|
619
|
+
name: string;
|
|
620
|
+
status: string;
|
|
621
|
+
conclusion?: string;
|
|
622
|
+
startedAt?: string;
|
|
623
|
+
completedAt?: string;
|
|
624
|
+
/** 该 job 在 GitHub 的页面地址;step 级别没有对应 URL */
|
|
625
|
+
url?: string;
|
|
626
|
+
steps: RunStepItem[];
|
|
627
|
+
}
|
|
628
|
+
/**
|
|
629
|
+
* Run 完整详情契约(供 RunDetailModal 在线查看消费)。
|
|
630
|
+
*
|
|
631
|
+
* 与 PrDetailData / IssueDetailData 的关键差异:这里**不携带日志正文**。
|
|
632
|
+
* 日志动辄几十 MB 且实测单次拉取 4–40 秒还会被代理掐断,挂在详情上会让
|
|
633
|
+
* 弹窗首屏被慢链路拖死;日志走独立的 /run-logs 通道按需拉取。
|
|
634
|
+
*/
|
|
635
|
+
export interface RunDetailData {
|
|
636
|
+
runId: number;
|
|
637
|
+
/** 重跑次数(>1 时 UI 显示「第 N 次尝试」) */
|
|
638
|
+
attempt?: number;
|
|
639
|
+
name: string;
|
|
640
|
+
displayTitle?: string;
|
|
641
|
+
status: string;
|
|
642
|
+
conclusion?: string;
|
|
643
|
+
event?: string;
|
|
644
|
+
headBranch?: string;
|
|
645
|
+
headSha?: string;
|
|
646
|
+
url: string;
|
|
647
|
+
createdAt: string;
|
|
648
|
+
startedAt?: string;
|
|
649
|
+
updatedAt: string;
|
|
650
|
+
workflowName?: string;
|
|
651
|
+
jobs: RunJobItem[];
|
|
652
|
+
/**
|
|
653
|
+
* 失败口径只算一次。
|
|
654
|
+
* 为什么在服务端算:UI 与将来的其它消费方(Agent 摘要、测试)必须同一口径,
|
|
655
|
+
* 三处各写一遍 `conclusion === 'failure'` 迟早出现「卡片说 1 个失败、弹窗说 2 个」。
|
|
656
|
+
*/
|
|
657
|
+
failedJobs: number;
|
|
658
|
+
failedSteps: number;
|
|
659
|
+
/** gh 版本过低触发过字段降级(供排查,不弹错) */
|
|
660
|
+
fieldsDegraded?: boolean;
|
|
661
|
+
}
|
|
662
|
+
/** 失败日志按需拉取的结果(一 job 一块,单 job 失败不拖垮整体) */
|
|
663
|
+
export interface RunLogExcerptData {
|
|
664
|
+
runId: number;
|
|
665
|
+
jobs: Array<{
|
|
666
|
+
jobId: number;
|
|
667
|
+
jobName: string;
|
|
668
|
+
/** 锚点萃取后的纯文本片段(含尾部回退与显式截断声明) */
|
|
669
|
+
excerpt: string;
|
|
670
|
+
truncated: boolean;
|
|
671
|
+
}>;
|
|
672
|
+
/** 拉取失败但可重试的 job;UI 必须显式展示,不能静默当成「没有失败日志」 */
|
|
673
|
+
errors?: Array<{
|
|
674
|
+
jobName: string;
|
|
675
|
+
error: string;
|
|
676
|
+
}>;
|
|
677
|
+
/**
|
|
678
|
+
* job 名称未能从详情树解析(服务端详情降级/失败)。
|
|
679
|
+
*
|
|
680
|
+
* 为什么必须透出:此时 jobName 会退化成 `job #<id>`,属于用户可见的降级。
|
|
681
|
+
* 本仓纪律是「降级必须显式」——让界面能说一句「名称未取到」,
|
|
682
|
+
* 而不是让人以为这个 job 本来就叫 `job #111`。
|
|
683
|
+
*/
|
|
684
|
+
namesUnavailable?: boolean;
|
|
685
|
+
fetchedAt: string;
|
|
686
|
+
}
|
|
538
687
|
export interface GitHubOverviewData {
|
|
539
688
|
auth: AuthStatus;
|
|
540
689
|
repo?: RepoMetadata;
|
|
@@ -590,6 +739,30 @@ export interface GitHubOverviewData {
|
|
|
590
739
|
prsTruncated?: boolean;
|
|
591
740
|
/** 单仓库 Issue 列表是否触及条数上限(语义同 prsTruncated) */
|
|
592
741
|
issuesTruncated?: boolean;
|
|
742
|
+
/** Actions 运行列表是否还有更多(语义同 prsTruncated;右侧栏分页用) */
|
|
743
|
+
runsTruncated?: boolean;
|
|
744
|
+
/** 仓库全部 PR 数(含 open/merged/closed) */
|
|
745
|
+
prTotal?: number;
|
|
746
|
+
/** 处于 open 的 PR 数 */
|
|
747
|
+
prOpenTotal?: number;
|
|
748
|
+
/** 已合并的 PR 数 */
|
|
749
|
+
prMergedTotal?: number;
|
|
750
|
+
/** 已关闭的 PR 数(GitHub 语义:含已合并) */
|
|
751
|
+
prClosedTotal?: number;
|
|
752
|
+
/** 仓库全部 Issue 数(不含 PR——用 `is:issue` 限定符区分) */
|
|
753
|
+
issueTotal?: number;
|
|
754
|
+
/** 处于 open 的 Issue 数 */
|
|
755
|
+
issueOpenTotal?: number;
|
|
756
|
+
/** 已关闭的 Issue 数 */
|
|
757
|
+
issueClosedTotal?: number;
|
|
758
|
+
/**
|
|
759
|
+
* Actions 运行总数。
|
|
760
|
+
*
|
|
761
|
+
* 来源是 REST `GET /repos/{owner}/{repo}/actions/runs?per_page=1` 的 `total_count`
|
|
762
|
+
* ——GitHub GraphQL 没有 workflow runs 连接,拿不到 totalCount。
|
|
763
|
+
* 同样与分页解耦:`runs.length` 只是已加载条数。
|
|
764
|
+
*/
|
|
765
|
+
runTotal?: number;
|
|
593
766
|
}
|
|
594
767
|
export interface UserRepoItem {
|
|
595
768
|
name: string;
|
|
@@ -662,6 +835,15 @@ export interface GlobalOverviewData {
|
|
|
662
835
|
* 「列表到此为止」与「数据没取到」。
|
|
663
836
|
*/
|
|
664
837
|
prsTruncated?: boolean;
|
|
838
|
+
/** Issues 三路检索是否任一触及上限(有更多可加载) */
|
|
839
|
+
issuesTruncated?: boolean;
|
|
840
|
+
prTotal?: number;
|
|
841
|
+
/** 账号 open PR 数 */
|
|
842
|
+
prOpenTotal?: number;
|
|
843
|
+
/** 账号已合并 PR 数 */
|
|
844
|
+
prMergedTotal?: number;
|
|
845
|
+
/** 账号已关闭 PR 数(GitHub 语义:含已合并) */
|
|
846
|
+
prClosedTotal?: number;
|
|
665
847
|
lastUpdated: string;
|
|
666
848
|
error?: string;
|
|
667
849
|
/** 网络是否发生异常(超时、连接拒绝、代理阻断等) */
|
package/lib/ui/help-modal.d.ts
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*
|
|
11
11
|
* 【内容与代码同步的纪律】
|
|
12
12
|
* 本文件里出现的工具数量、action 数量、Tab 名称、按钮文案,全部以当前代码为准:
|
|
13
|
-
* 工具矩阵 = src/main.ts 的
|
|
13
|
+
* 工具矩阵 = src/main.ts 的 19 处 ctx.tools.register;
|
|
14
14
|
* Tab 布局 = src/client.ts 的 TAB_DEFS 与 src/ui/workspace-panel.ts 的四个标签;
|
|
15
15
|
* 若后续增删工具或 Tab,必须同步改这里,否则用户会照着过时说明去找不存在的入口。
|
|
16
16
|
*/
|
package/lib/ui/help-modal.js
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*
|
|
11
11
|
* 【内容与代码同步的纪律】
|
|
12
12
|
* 本文件里出现的工具数量、action 数量、Tab 名称、按钮文案,全部以当前代码为准:
|
|
13
|
-
* 工具矩阵 = src/main.ts 的
|
|
13
|
+
* 工具矩阵 = src/main.ts 的 19 处 ctx.tools.register;
|
|
14
14
|
* Tab 布局 = src/client.ts 的 TAB_DEFS 与 src/ui/workspace-panel.ts 的四个标签;
|
|
15
15
|
* 若后续增删工具或 Tab,必须同步改这里,否则用户会照着过时说明去找不存在的入口。
|
|
16
16
|
*/
|
|
@@ -49,7 +49,7 @@ export function GithubHelpModal(props) {
|
|
|
49
49
|
{ id: "quickstart", label: "🚀 3分钟极速上手" },
|
|
50
50
|
{ id: "prompts", label: "🤖 Agent 提示词实战" },
|
|
51
51
|
{ id: "panels", label: "🖥️ 两大界面操作图解" },
|
|
52
|
-
{ id: "tools", label: "🧰
|
|
52
|
+
{ id: "tools", label: "🧰 19 大工具速查" },
|
|
53
53
|
{ id: "faq", label: "🔑 常见自愈排错" },
|
|
54
54
|
];
|
|
55
55
|
// 2. 渲染各 Tab 独立视图
|
|
@@ -78,7 +78,7 @@ export function GithubHelpModal(props) {
|
|
|
78
78
|
return h("div", { key: i, className: "dsh-github-help-card", style: { display: "flex", gap: "14px", alignItems: "flex-start" } }, h("div", { className: "dsh-github-help-step-badge" }, s.step), h("div", { style: { flex: 1 } }, h("div", { className: "dsh-github-help-title", style: { marginBottom: "4px" } }, s.title), h("div", { className: "dsh-github-help-desc", style: { marginBottom: "6px" } }, s.desc), h("div", { style: { fontSize: "12px", lineHeight: "1.6", whiteSpace: "pre-line", color: "inherit" } }, s.actionText)));
|
|
79
79
|
}),
|
|
80
80
|
// 能力总览:让用户三秒钟知道这插件到底能干什么,而不是去翻 README
|
|
81
|
-
h("div", { className: "dsh-github-help-card", style: { marginTop: "4px" } }, h("div", { className: "dsh-github-help-title", style: { marginBottom: "8px" } }, "📦 能力总览:
|
|
81
|
+
h("div", { className: "dsh-github-help-card", style: { marginTop: "4px" } }, h("div", { className: "dsh-github-help-title", style: { marginBottom: "8px" } }, "📦 能力总览:19 大工具 · 106 个业务动作 · 2 套可视化工作台"), h("div", { style: { fontSize: "12px", lineHeight: "1.8", color: "var(--dsw-alias-label-secondary, #8b949e)" } }, "Agent 侧可直接调用 19 个 github_* 工具(PR 审查合并、Issue 任务流转、Actions 排障、免 clone 读文件、发版交付、Secrets 管理、Skills 生态、标签与规则集、构建缓存、Gist、跨仓待办大盘、Copilot 智能体、Discussions、Projects v2、Codespaces、运行时遥测与元指南)。", h("br"), "人类侧在每个会话右侧栏拥有 5 个 Tab(Git / 未提交 / PR / Issues / Actions),在最左侧 Octocat 入口拥有 4 个标签的全景驾驶舱。PR / Issue / Actions 的卡片点击都能在线查看详情——Actions 看到的是 job / step 树与失败步骤,失败时还能按需拉一段日志,不用切浏览器。")));
|
|
82
82
|
}
|
|
83
83
|
if (activeTab === "prompts") {
|
|
84
84
|
var promptCards = [
|
|
@@ -92,7 +92,7 @@ export function GithubHelpModal(props) {
|
|
|
92
92
|
title: "🚨 场景二:CI / Actions 门禁报错秒级排查与自动修复",
|
|
93
93
|
desc: "构建挂了不用在网页反复翻几十页日志,直接吩咐 Agent 定位报错步骤并就地改代码。",
|
|
94
94
|
prompt: "检查当前仓库最近一次 CI 失败的原因,精确定位报错步骤并帮我尝试修复代码",
|
|
95
|
-
aiAction: "Agent
|
|
95
|
+
aiAction: "Agent 会调 github_task(action:'diagnose_ci') 或 github_run(action:'log_failed'):先定位失败 job/step,再按框架锚点萃取日志(pytest / node / Go / tsc / npm),最后汇总疑似相关文件。日志被截断时它会明确告诉你「总数未知」,不要当成「就这些失败」。",
|
|
96
96
|
},
|
|
97
97
|
{
|
|
98
98
|
title: "📌 场景三:早会与每日任务待办盘点",
|
|
@@ -164,17 +164,19 @@ export function GithubHelpModal(props) {
|
|
|
164
164
|
"未提交 Tab:实时监听本地未提交文件与状态码(?? 新增 / M 修改 / D 删除 / R 重命名),点击文件可就地下钻查看该文件的 Unified Diff;",
|
|
165
165
|
"PR Tab:展示全部 PR 并按状态过滤;带三态 CI 门禁、冲突检测、审核判定与 Draft 标示;点击卡片在线唤起 PR 审阅工作台(描述 / 评论 / 文件 Diff 双栏比对);",
|
|
166
166
|
"Issues Tab:展示全部 Issue 并按状态过滤;点击卡片在线查看详情(Markdown 正文 / 评论时间线 / 标签色块 / 里程碑 / 关联 PR / 隐藏评论标注);",
|
|
167
|
-
"Actions Tab
|
|
167
|
+
"Actions Tab:最近工作流运行记录与触发方式;点击卡片在线查看本次运行的 job / step 树(每步结论与耗时,失败步骤高亮,默认展开第一个失败 job),失败时可按需提取日志锚点片段(一次最多 3 个失败 job、服务端批次预算 90 秒);",
|
|
168
168
|
"动静解耦架构:修改本地代码只定向查询本地 .git 核心元数据(耗时仅 5ms),绝对不发起远端网络请求,杜绝网络风暴。",
|
|
169
169
|
],
|
|
170
170
|
},
|
|
171
171
|
{
|
|
172
|
-
title: "3.
|
|
172
|
+
title: "3. 三个详情弹窗的通用操作习惯",
|
|
173
173
|
items: [
|
|
174
|
-
"PR / Issue
|
|
174
|
+
"PR / Issue / Actions 三个弹窗都支持:按 Esc 或点击遮罩关闭、右上角「GitHub ↗」直达官方页面;",
|
|
175
175
|
"列表卡片上的「GitHub ↗」按钮是独立的浏览器外跳入口,点它不会触发弹窗(已做事件隔离);",
|
|
176
176
|
"PR 弹窗的文件区可拖拽分隔条调宽、双击或点 ◀ 折叠;布局偏好会记在本地,下次打开保持原样;",
|
|
177
|
-
"长评论默认折叠(显示「⌄ 展开全部」),PR/Issue
|
|
177
|
+
"长评论默认折叠(显示「⌄ 展开全部」),PR/Issue 正文默认展开——避免一屏被一段超长文字占满;",
|
|
178
|
+
"Actions 弹窗按 job 折叠/展开步骤,默认展开第一个失败 job;「📄 提取失败日志」只在存在失败时出现,点一次拉一次(慢链路不做自动重试,失败可手动重试);",
|
|
179
|
+
"日志片段是按失败锚点萃取过的——看到的是「失败块 + 上下文」或尾部 200 行,不是完整日志;要全文请点「GitHub ↗」。",
|
|
178
180
|
],
|
|
179
181
|
},
|
|
180
182
|
].map(function (sec, idx) {
|
|
@@ -188,7 +190,7 @@ export function GithubHelpModal(props) {
|
|
|
188
190
|
items: [
|
|
189
191
|
{ name: "github_pr", desc: "PR 全生命周期:列表/详情/diff/checks/create/merge/review/ready/update_branch/revert/preview/checkout,含行级评论与附件上传" },
|
|
190
192
|
{ name: "github_issue", desc: "Issue 任务流转:list/view/create/comment/edit/close/reopen/develop/transfer/pin/lock/delete/status,含截图查看 view_images" },
|
|
191
|
-
{ name: "github_run", desc: "Actions 排障:list
|
|
193
|
+
{ name: "github_run", desc: "Actions 排障:list、view(run + job/step 树)、log_failed(失败步骤日志秒级提取)/rerun/cancel/trigger/download 与 workflow 启停" },
|
|
192
194
|
{ name: "github_repo", desc: "仓库元数据、免 clone 读文件(read_file/read_dir)、跨仓检索(code/issues/prs/commits)、Fork 同步" },
|
|
193
195
|
],
|
|
194
196
|
},
|
|
@@ -222,7 +224,7 @@ export function GithubHelpModal(props) {
|
|
|
222
224
|
],
|
|
223
225
|
},
|
|
224
226
|
];
|
|
225
|
-
return h("div", null, h("div", { className: "dsh-github-help-desc", style: { fontSize: "13px", marginBottom: "14px" } }, "
|
|
227
|
+
return h("div", null, h("div", { className: "dsh-github-help-desc", style: { fontSize: "13px", marginBottom: "14px" } }, "19 个 Agent 工具、合计 106 个业务动作(不含元指南的 22 个查询主题与 api 逃生门的 4 个 HTTP method)。你不必记住工具名——直接用自然语言描述目标,Agent 会自己挑工具。下表用于你了解能力边界,以及排查「为什么 Agent 说做不到」:"), toolGroups.map(function (g, gi) {
|
|
226
228
|
return h("div", { key: gi, className: "dsh-github-help-card" }, h("div", { className: "dsh-github-help-title", style: { marginBottom: "8px" } }, g.group), g.items.map(function (t, ti) {
|
|
227
229
|
return h("div", { key: ti, style: { display: "flex", gap: "10px", alignItems: "flex-start", padding: "5px 0", borderTop: ti === 0 ? "none" : "1px solid var(--dsw-alias-border-l1, rgba(255,255,255,0.06))" } }, h("code", {
|
|
228
230
|
style: {
|
|
@@ -262,6 +264,14 @@ export function GithubHelpModal(props) {
|
|
|
262
264
|
q: "Q6: 为什么改了代码,插件界面要重启 DSH 才生效?",
|
|
263
265
|
a: "如果你是在开发这个插件本身:后端 HTTP 路由是在插件 `apply()` 时一次性注册到宿主的 WebServer 上的,`pnpm run build` 只更新磁盘产物,不会给已运行的进程热注册新路由。改完代码需重启 DSH 才能挂上新端点。\n若只是普通使用,请忽略此条——正常升级走一键升级即可。",
|
|
264
266
|
},
|
|
267
|
+
{
|
|
268
|
+
q: "Q7: Actions 弹窗里点「提取失败日志」很久没反应,或报超时?",
|
|
269
|
+
a: "GitHub 的日志接口是「先打包再下载」,实测单次 4–40 秒,走代理时还可能被直接重置——这是上游链路特性,不是插件卡死。当前实现有三道闸:\n- 服务端单 job 上限 60 秒、整批总预算 90 秒;超预算的 job 会明确列出「未拉取,可单独重试」,不会静默丢掉;\n- 客户端 100 秒自动中止,避免按钮永远停在「正在下载日志…」;\n- 失败后弹窗内给「🔁 重试」,不做自动重试(重试会把等待时间叠加)。\n急的话点右上角「GitHub ↗」看完整日志,或让 Agent 调 `github_task(action:'diagnose_ci', run_id:'…')` 直接拿结论包。",
|
|
270
|
+
},
|
|
271
|
+
{
|
|
272
|
+
q: "Q8: 为什么 Actions 弹窗只能看 3 个失败 job 的日志?",
|
|
273
|
+
a: "一次点击串行拉 3 个 job 已经是 60–90 秒量级;矩阵构建下失败 job 可能有十几个,全拉会把一次点击变成几分钟。当前策略:只取前 3 个失败 job,并在按钮下方明示「共 N 个,其余请到 GitHub 查看」。需要逐个深挖时,让 Agent 用 `github_run(action:'log_failed', run_id:'…')` 或 `github_task(action:'diagnose_ci')` 分批处理更合适。",
|
|
274
|
+
},
|
|
265
275
|
];
|
|
266
276
|
return h("div", null, h("div", { className: "dsh-github-help-desc", style: { fontSize: "13px", marginBottom: "14px" } }, "常见环境、权限与网络问题的排查与自愈指引:"), faqs.map(function (f, idx) {
|
|
267
277
|
return h("div", { key: idx, className: "dsh-github-help-card" }, h("div", { className: "dsh-github-help-title", style: { marginBottom: "6px", fontSize: "13px" } }, f.q), h("div", { className: "dsh-github-help-desc", style: { lineHeight: "1.6", whiteSpace: "pre-line" } }, f.a));
|
package/lib/ui/issue-modal.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { issueStateBadge } from '../shared/state-badges.js';
|
|
1
2
|
/**
|
|
2
3
|
* GitHub 关闭原因的状态语义映射。
|
|
3
4
|
* 实测 `stateReason` 取值域:COMPLETED / NOT_PLANNED / REOPENED / ''(空串)。
|
|
@@ -201,11 +202,14 @@ export function IssueDetailModal(props) {
|
|
|
201
202
|
var stateBadge = function (d) {
|
|
202
203
|
if (!d)
|
|
203
204
|
return null;
|
|
204
|
-
|
|
205
|
+
// 状态→色调来自 shared/state-badges(与右侧栏 / CLI 输出同一份映射)
|
|
206
|
+
var b = issueStateBadge(d.state);
|
|
207
|
+
if (!b)
|
|
208
|
+
return null;
|
|
205
209
|
return h("span", {
|
|
206
|
-
className: "dsh-github-badge
|
|
210
|
+
className: "dsh-github-badge dsh-badge-" + b.tone,
|
|
207
211
|
style: { fontSize: "10px", padding: "1px 6px", flexShrink: 0 },
|
|
208
|
-
},
|
|
212
|
+
}, b.label);
|
|
209
213
|
};
|
|
210
214
|
/**
|
|
211
215
|
* 元数据条:只渲染真实存在的字段。
|
package/lib/ui/pr-modal.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { prStateBadge } from '../shared/state-badges.js';
|
|
1
2
|
/**
|
|
2
3
|
* 可折叠的 Markdown 内容块。
|
|
3
4
|
*
|
|
@@ -336,18 +337,36 @@ export function PrDetailModal(props) {
|
|
|
336
337
|
},
|
|
337
338
|
}, data ? `#${data.number} ${data.title}` : `PR #${prNumber}`), data
|
|
338
339
|
? h("span", {
|
|
340
|
+
// 色调来自 shared/state-badges(与右侧栏 / CLI 输出同一份映射);
|
|
341
|
+
// 文案保持原样(Draft 用大驼峰、其余直接展示 GitHub 原始 state)。
|
|
342
|
+
// 未知状态沿用旧实现的绿色兜底(旧代码的 else 分支就是 green)。
|
|
339
343
|
className: "dsh-github-badge " +
|
|
340
|
-
(
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
: data.state === "CLOSED"
|
|
345
|
-
? "dsh-badge-red"
|
|
346
|
-
: "dsh-badge-green"),
|
|
344
|
+
(function () {
|
|
345
|
+
var b = prStateBadge(data.state, data.isDraft);
|
|
346
|
+
return "dsh-badge-" + (b && b.tone !== "neutral" ? b.tone : "green");
|
|
347
|
+
})(),
|
|
347
348
|
style: { fontSize: "10px", padding: "1px 6px" },
|
|
348
349
|
}, data.isDraft ? "Draft" : data.state)
|
|
349
350
|
: null), data
|
|
350
|
-
? h("div", { style: { fontSize: "11px", color: "var(--dsw-alias-label-secondary, #656d76)", display: "flex", gap: "6px", alignItems: "center", flexWrap: "wrap" } }, h("span", null, `👤 @${data.author.login}`), data.createdAt ? h("span", null, `· 提单于 ${formatRelativeTime(data.createdAt)}`) : null, h("span", { style: { fontFamily: "monospace", opacity: 0.85 } }, `(${data.headRefName} → ${data.baseRefName})`)
|
|
351
|
+
? h("div", { style: { fontSize: "11px", color: "var(--dsw-alias-label-secondary, #656d76)", display: "flex", gap: "6px", alignItems: "center", flexWrap: "wrap" } }, h("span", null, `👤 @${data.author.login}`), data.createdAt ? h("span", null, `· 提单于 ${formatRelativeTime(data.createdAt)}`) : null, h("span", { style: { fontFamily: "monospace", opacity: 0.85 } }, `(${data.headRefName} → ${data.baseRefName})`),
|
|
352
|
+
/*
|
|
353
|
+
* 审查锚点:分支名会移动,SHA 不会。
|
|
354
|
+
* 点开这个弹窗时先看这一枚 chip,就能确认「我看到的是不是最新一次推送」;
|
|
355
|
+
* title 里放完整 SHA,方便复制去精确引用。老服务端没有该字段时不渲染。
|
|
356
|
+
*/
|
|
357
|
+
typeof data.headRefOid === "string" && data.headRefOid.length >= 7
|
|
358
|
+
? h("span", {
|
|
359
|
+
title: `headRefOid: ${data.headRefOid}`,
|
|
360
|
+
style: {
|
|
361
|
+
fontFamily: "monospace",
|
|
362
|
+
fontSize: "10px",
|
|
363
|
+
padding: "1px 6px",
|
|
364
|
+
borderRadius: "10px",
|
|
365
|
+
border: "1px solid var(--dsw-alias-border-l2, rgba(31,35,40,0.15))",
|
|
366
|
+
opacity: 0.9,
|
|
367
|
+
},
|
|
368
|
+
}, `🔖 ${data.headRefOid.slice(0, 7)}`)
|
|
369
|
+
: null)
|
|
351
370
|
: null)), h("div", { style: { display: "flex", alignItems: "center", gap: "6px", flexShrink: 0 } }, data?.url
|
|
352
371
|
? h("button", {
|
|
353
372
|
className: "dsh-github-btn-sm",
|
|
@@ -390,7 +409,10 @@ export function PrDetailModal(props) {
|
|
|
390
409
|
}, `💬 描述与讨论 (${data.comments ? data.comments.length : 0})`), h("button", {
|
|
391
410
|
className: `dsh-github-pr-tab-btn ${activeTab === "files" ? "active" : ""}`,
|
|
392
411
|
onClick: () => setActiveTab("files"),
|
|
393
|
-
},
|
|
412
|
+
},
|
|
413
|
+
// 截断时显示 N/M:M 来自 changedFiles(权威总数),N 是 gh 实际返回的条数。
|
|
414
|
+
// 只显示 N 会让「被截断」看起来像「就这么多」——这是 cli/cli#12522 的静默截断。
|
|
415
|
+
`📁 变动文件 (${data.files ? data.files.length : 0}${data.filesTruncated && data.changedFiles ? '/' + data.changedFiles : ''} 文件 · +${data.additions || 0} / -${data.deletions || 0})${data.filesTruncated ? ' ⚠️' : ''}`)),
|
|
394
416
|
// Tab 1: 描述与讨论
|
|
395
417
|
activeTab === "conversation"
|
|
396
418
|
? h("div", { className: "dsh-github-modal-body dsh-github-pr-conversation-pane" },
|
|
@@ -461,7 +483,9 @@ export function PrDetailModal(props) {
|
|
|
461
483
|
onClick: toggleFilePane,
|
|
462
484
|
style: { padding: "2px 8px", fontSize: "10px", flexShrink: 0 },
|
|
463
485
|
title: "展开左侧文件列表",
|
|
464
|
-
},
|
|
486
|
+
},
|
|
487
|
+
// 折叠态同样要透出截断事实(N/M),否则展开前的第一眼就被骗了
|
|
488
|
+
`📁 文件列表 (${(data.files && data.files.length) || 0}${data.filesTruncated && data.changedFiles ? '/' + data.changedFiles : ''})`)
|
|
465
489
|
: null, h("span", { style: { fontFamily: "monospace", fontWeight: 600, fontSize: "11px", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" } }, selectedFilePath), data.fileDiffs && data.fileDiffs[selectedFilePath]
|
|
466
490
|
? h("button", {
|
|
467
491
|
className: "dsh-github-btn-sm",
|
|
@@ -469,7 +493,9 @@ export function PrDetailModal(props) {
|
|
|
469
493
|
style: { padding: "2px 8px", fontSize: "10px" },
|
|
470
494
|
}, copied ? "✓ 已复制" : "📋 复制文件 Diff")
|
|
471
495
|
: null), h("div", { style: { flex: 1, overflowY: "auto", minHeight: 0 } },
|
|
472
|
-
//
|
|
496
|
+
// 两种「看不到 diff」的原因必须分开说,否则用户不知道该做什么:
|
|
497
|
+
// ① diffTruncated:整段 diff 被输出阈值裁掉(去 GitHub 看完整内容)
|
|
498
|
+
// ② diffIncomplete:这个文件在清单里但没取到切片(换一个文件/重试)
|
|
473
499
|
data.diffTruncated
|
|
474
500
|
? h("div", {
|
|
475
501
|
style: {
|
|
@@ -483,6 +509,19 @@ export function PrDetailModal(props) {
|
|
|
483
509
|
color: "var(--dsw-alias-state-attention-primary, #9a6700)",
|
|
484
510
|
},
|
|
485
511
|
}, "⚠️ 该 PR 的 diff 体量过大已触达输出上限,部分文件可能无法展示差异。可点击右上角「↗ 浏览器打开」查看完整内容。")
|
|
512
|
+
: null, data.diffIncomplete
|
|
513
|
+
? h("div", {
|
|
514
|
+
style: {
|
|
515
|
+
margin: "8px",
|
|
516
|
+
padding: "8px 12px",
|
|
517
|
+
borderRadius: "6px",
|
|
518
|
+
fontSize: "11px",
|
|
519
|
+
lineHeight: 1.5,
|
|
520
|
+
background: "rgba(210, 153, 34, 0.12)",
|
|
521
|
+
border: "1px solid rgba(210, 153, 34, 0.35)",
|
|
522
|
+
color: "var(--dsw-alias-state-attention-primary, #9a6700)",
|
|
523
|
+
},
|
|
524
|
+
}, "⚠️ 有文件未能取到 diff 正文(二进制、重命名或超出切片范围)。这些文件显示为空不代表没有改动,请到 GitHub 核对。")
|
|
486
525
|
: null, data.fileDiffs && data.fileDiffs[selectedFilePath]
|
|
487
526
|
? renderDiffContent(data.fileDiffs[selectedFilePath])
|
|
488
527
|
: renderDiffContent(""),
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Actions run / job / step 的耗时格式化(导出以便单测直接覆盖)。
|
|
3
|
+
*
|
|
4
|
+
* 为什么不用 formatRelativeTime:那个函数答的是「发生在多久前」,这里是「跑了多久」;
|
|
5
|
+
* 而且 run → job → step 三层共用同一套精度规则(<60s → `12s`、<60m → `3m20s`、
|
|
6
|
+
* 其余 `1h05m`),分钟/秒补零才能让三层的列宽稳定对齐。
|
|
7
|
+
* 任一端缺失或不可解析一律返回空串:排队中/已跳过的 job 没有 startedAt/completedAt,
|
|
8
|
+
* 硬算只会渲染出「0s」这种假精确值,比不显示更误导。
|
|
9
|
+
*/
|
|
10
|
+
export declare function formatRunDuration(startISO?: string, endISO?: string): string;
|
|
11
|
+
export interface RunDetailModalProps {
|
|
12
|
+
runId: number;
|
|
13
|
+
repoNameWithOwner?: string;
|
|
14
|
+
cwd?: string | null;
|
|
15
|
+
onClose: () => void;
|
|
16
|
+
showToast?: (msg: string) => void;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Actions Run 详情弹窗(只读:run 元信息 + job/step 树 + 失败日志片段)。
|
|
20
|
+
*
|
|
21
|
+
* 单列纵向:run 的信息天然是「job 列表 + 一条日志流」的阅读顺序,没有需要并排
|
|
22
|
+
* 对照的第二个信息源(PR 才有 diff 双栏)。
|
|
23
|
+
* 日志刻意渲染成 <pre> 原文而不是 markdown-lite:原始日志里的 `#`、`|`、`---`
|
|
24
|
+
* 是 shell 注释、表格与 diff 分隔符,交给 Markdown 解析会把堆栈与 diff 撕碎。
|
|
25
|
+
*/
|
|
26
|
+
export declare function RunDetailModal(props: RunDetailModalProps): any;
|