issue-map 0.3.0 → 0.4.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.ja.md +85 -108
- package/README.md +88 -108
- package/README.zh-CN.md +77 -83
- package/README.zh-TW.md +77 -81
- package/package.json +5 -4
- package/scripts/issue-map-i18n.ts +11 -5
- package/scripts/issue-map-model.ts +11 -4
- package/scripts/issue-map-page.ts +109 -713
- package/scripts/issue-map-serve.ts +17 -18
- package/scripts/issue-map-view.ts +650 -0
- package/scripts/issue-map.html +31 -5
- package/scripts/issue-map.ts +272 -84
package/README.zh-CN.md
CHANGED
|
@@ -6,130 +6,124 @@
|
|
|
6
6
|
|
|
7
7
|
把 GitHub Issues 的阻挡关系画成一页开发地图:**哪几张票现在可以动、哪几张在等谁、关键路径是哪一条。**
|
|
8
8
|
|
|
9
|
-
状态的权威永远是 GitHub Issues。这一页只是快照,页面上不能改状态——所以不会长出第二个事实来源。
|
|
10
|
-
|
|
11
|
-
## 为什么有这个项目
|
|
12
|
-
|
|
13
|
-
起点是 [mattpocock/skills](https://github.com/mattpocock/skills)。团队照它那套「把工作流写成
|
|
14
|
-
skill、让 agent 照着跑」开始做事之后,开票变得很便宜:想到一件事就开一张票,交给 skill 去接。
|
|
15
|
-
票因此长得很快——那是流程在运作的证据,不是问题。
|
|
16
|
-
|
|
17
|
-
问题在下一步。Agent 一轮吃一张票,所以每一轮真正要决定的是**派哪一张**,而这个答案不在任何
|
|
18
|
-
单一张票里,它在票与票之间:谁挡着谁、哪一组子票还差几张、最长的那条链有多长。GitHub Issues
|
|
19
|
-
一次只让你读一张票,要凑出那张图就得一张一张点开来,而且每天都要重凑一次。
|
|
20
|
-
|
|
21
|
-
这一页就是那张图。
|
|
22
|
-
|
|
23
|
-
## 用法
|
|
24
|
-
|
|
25
|
-
在**要看的那个 repo** 里跑:
|
|
26
|
-
|
|
27
9
|
```bash
|
|
28
10
|
bunx issue-map@latest
|
|
29
11
|
```
|
|
30
12
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
起在 `http://localhost:4747` **并直接开浏览器**。每次刷新都重抓 GitHub,看到的一定是现在的状态。repo 是 `gh` 从 cwd 的 git 推断的,不必填。
|
|
13
|
+
在要看的那个 repo 里跑。会直接开浏览器标签页,每次刷新都重抓 GitHub。状态的权威永远是 GitHub
|
|
14
|
+
Issues——这一页只是快照,不能改状态。
|
|
34
15
|
|
|
35
|
-
|
|
16
|
+
## 为什么有这个项目
|
|
36
17
|
|
|
37
|
-
|
|
18
|
+
Agent 一轮吃一张票,所以每一轮真正要决定的是**派哪一张**。这个答案不在任何单一张票里,它在票与
|
|
19
|
+
票之间:谁挡着谁、哪一组子票还差几张、最长的那条链有多长。GitHub 一次只让你读一张票。这一页就是
|
|
20
|
+
那张图。
|
|
38
21
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
bunx -p issue-map@latest issue-map-build out.html
|
|
42
|
-
```
|
|
22
|
+
它是绕着 [mattpocock/skills](https://github.com/mattpocock/skills) 那套做法建的——把工作流写成
|
|
23
|
+
skill、让 agent 照着跑——默认的标签与指令也是从那里来的。
|
|
43
24
|
|
|
44
|
-
|
|
25
|
+
## 用法
|
|
45
26
|
|
|
46
|
-
|
|
27
|
+
| 指令 | 得到什么 |
|
|
28
|
+
| --------------------------------------------------- | -------------------------------- |
|
|
29
|
+
| `bunx issue-map@latest` | server 起在空端口,自动开浏览器 |
|
|
30
|
+
| `bunx -p issue-map@latest issue-map-build` | 静态文件到 `dist/issue-map.html` |
|
|
31
|
+
| `bunx -p issue-map@latest issue-map-build out.html` | 静态文件到你指定的路径 |
|
|
47
32
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
33
|
+
- repo 是 `gh` 从 cwd 的 git 推断的,不必填。
|
|
34
|
+
- `ISSUE_MAP_PORT` 固定端口。固定就是严格的:被占住时直接失败,不会偷偷换一个。
|
|
35
|
+
- `ISSUE_MAP_OPEN=0` 不自动开标签页。
|
|
36
|
+
- 静态文件会过期,要看现在的状态就用 server。
|
|
37
|
+
- 在跑不了 script 的地方(严格 CSP、某些预览窗),文件会退回一份纯文字的票清单,而不是一片空白。
|
|
38
|
+
- `@latest` 取 npm 上最新的一版;要钉住就写 `bunx issue-map@0.2.0`。
|
|
51
39
|
|
|
52
|
-
|
|
40
|
+
## 前置条件
|
|
53
41
|
|
|
54
|
-
-
|
|
55
|
-
-
|
|
56
|
-
|
|
57
|
-
- 英文要分单复数的键写成 `{ one, other }`,中日文写一句字符串就好(`Intl.PluralRules` 对这几种
|
|
58
|
-
语言只有 `other`)。
|
|
42
|
+
- **Bun**——这几支用了 `Bun.build`、`Bun.serve`、`Bun.file`,Node 跑不起来。
|
|
43
|
+
- **`gh` CLI 已登录**,而且对目标 repo 有读取权。
|
|
44
|
+
- 目标 repo 有 git remote 指向 GitHub。
|
|
59
45
|
|
|
60
|
-
|
|
61
|
-
的值,分组名字也一样,话在 i18n 那一层才组出来。快照里存中文句子的话,换一次语言就得重抓一次
|
|
62
|
-
GitHub。
|
|
46
|
+
没有 runtime 依赖。
|
|
63
47
|
|
|
64
|
-
|
|
48
|
+
## 语言
|
|
65
49
|
|
|
66
|
-
|
|
50
|
+
页面右上角切换:英文(默认)、繁体中文、简体中文、日文。选了哪一种记在浏览器,跟 repo 无关——语言
|
|
51
|
+
是看的人的偏好,不是某个项目的设置。
|
|
67
52
|
|
|
68
|
-
|
|
69
|
-
- **`gh` CLI 已登录**,而且对目标 repo 有读取权。
|
|
70
|
-
- 目标 repo 有 git remote 指向 GitHub。
|
|
71
|
-
- 没有 runtime 依赖;devDependencies 只有类型与 lint/format 工具。
|
|
53
|
+
CLI 那一侧(产文件消息、错误)只有英文。
|
|
72
54
|
|
|
73
55
|
## 配置
|
|
74
56
|
|
|
75
57
|
全部有默认值,一个都不设也跑得起来。默认值长在 `scripts/issue-map.ts` 的 `CONFIG`。
|
|
76
58
|
|
|
77
|
-
| 环境变量 | 默认 | 意思
|
|
78
|
-
| -------------------------- | --------------------------------- |
|
|
79
|
-
| `GH_REPO` | 从 cwd 的 git 推断 | 要画别的 repo 时设它(`gh` 自己的变量,fork
|
|
80
|
-
| `ISSUE_MAP_PARENT_HEADING` | `Parent` |
|
|
81
|
-
| `ISSUE_MAP_LABELS_UNREADY` | `needs-triage,needs-info` |
|
|
82
|
-
| `ISSUE_MAP_LABELS_READY` | `ready-for-agent,ready-for-human` |
|
|
83
|
-
| `ISSUE_MAP_LABELS_ACTIVE` | `in-progress` |
|
|
84
|
-
| `ISSUE_MAP_LABELS_HUMAN` | `ready-for-human` |
|
|
85
|
-
| `ISSUE_MAP_CMD_IMPLEMENT` | `/implement` | 可以动工时图上叫人跑的指令
|
|
86
|
-
| `ISSUE_MAP_CMD_TRIAGE` | `/triage` | 还要评估时图上叫人跑的指令
|
|
87
|
-
| `ISSUE_MAP_PORT` |
|
|
88
|
-
| `ISSUE_MAP_OPEN` | 开 | 设 `0`
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
-
|
|
93
|
-
|
|
59
|
+
| 环境变量 | 默认 | 意思 |
|
|
60
|
+
| -------------------------- | --------------------------------- | ------------------------------------------------------ |
|
|
61
|
+
| `GH_REPO` | 从 cwd 的 git 推断 | 要画别的 repo 时设它(`gh` 自己的变量,fork 也交给它) |
|
|
62
|
+
| `ISSUE_MAP_PARENT_HEADING` | `Parent` | 子票在正文指向母票的段落标题 |
|
|
63
|
+
| `ISSUE_MAP_LABELS_UNREADY` | `needs-triage,needs-info` | 还没评估完,不能交给谁做 |
|
|
64
|
+
| `ISSUE_MAP_LABELS_READY` | `ready-for-agent,ready-for-human` | 评估完、可以动工 |
|
|
65
|
+
| `ISSUE_MAP_LABELS_ACTIVE` | `in-progress` | 有人在做,不必有 assignee |
|
|
66
|
+
| `ISSUE_MAP_LABELS_HUMAN` | `ready-for-human` | 要人做,下一步不写实作指令 |
|
|
67
|
+
| `ISSUE_MAP_CMD_IMPLEMENT` | `/implement` | 可以动工时图上叫人跑的指令 |
|
|
68
|
+
| `ISSUE_MAP_CMD_TRIAGE` | `/triage` | 还要评估时图上叫人跑的指令 |
|
|
69
|
+
| `ISSUE_MAP_PORT` | OS 指派的空端口 | server 的端口 |
|
|
70
|
+
| `ISSUE_MAP_OPEN` | 开 | 设 `0` 就不自动开浏览器 |
|
|
71
|
+
|
|
72
|
+
三个要特别想过的:
|
|
73
|
+
|
|
74
|
+
- **标签词汇。** ready/unready 的默认值是 mattpocock/skills 五个[标准 triage 标签](https://github.com/mattpocock/skills/blob/main/skills/engineering/setup-matt-pocock-skills/triage-labels.md)
|
|
75
|
+
里的四个。目标 repo 没在用这套就换成它自己的名字。快照里完全没出现这些标签时,就不拿 triage 当
|
|
76
|
+
闸门,否则每张票都会变成「待评估」。(`in-progress` 是这个工具自己加的,那套 skill 没有「有人在
|
|
77
|
+
做」这个标签。)
|
|
78
|
+
- **指令名。** `/implement`、`/triage` 就是那边的 [`implement`](https://github.com/mattpocock/skills/tree/main/skills/engineering/implement) 与
|
|
79
|
+
[`triage`](https://github.com/mattpocock/skills/tree/main/skills/engineering/triage) skill。要指向目标 repo 真的有的东西,不然图上会叫人跑不存在的。
|
|
80
|
+
- **已完成的兄弟票要靠原生 sub-issue。** 地图只跟 GitHub 要 open 票还牵着的 closed 票,而子票是从
|
|
81
|
+
原生的 sub-issue 关系拿的。用 `## Parent` 正文惯例的 repo 看不到一组里**已完成**的子票,那一组的
|
|
82
|
+
进度会比实际少。把子票在票页的 Sub-issues 关联上去一次就会回来;正文惯例可以留着,原生的本来
|
|
83
|
+
就优先。
|
|
94
84
|
|
|
95
85
|
## 常见失败
|
|
96
86
|
|
|
97
|
-
|
|
98
|
-
- `open issue 超過 100 張,這支要改成分頁抓` — GraphQL 的 `first` 上限就是 100。要支持更多票得在 `issue-map.ts` 的 `query()` 加分页;这是要改程序,不是配置。
|
|
99
|
-
|
|
100
|
-
(CLI 消息是繁体中文,所以这里照它实际印出来的样子引用。)
|
|
87
|
+
`gh api graphql failed: …` — `gh` 没登录,或 cwd 不在目标 repo 的 git 树里。
|
|
101
88
|
|
|
102
89
|
## 文件
|
|
103
90
|
|
|
104
|
-
| 文件 | 职责
|
|
105
|
-
| ---------------------------- |
|
|
106
|
-
| `scripts/issue-map.ts` |
|
|
107
|
-
| `scripts/issue-map-model.ts` |
|
|
108
|
-
| `scripts/issue-map-i18n.ts` |
|
|
109
|
-
| `scripts/issue-map-page.ts` | 浏览器端代码,构建时被打包进 HTML
|
|
110
|
-
| `scripts/issue-map.html` |
|
|
111
|
-
| `scripts/issue-map-serve.ts` | 本机 server,每个请求重抓一次
|
|
112
|
-
| `scripts/mutate.ts` |
|
|
91
|
+
| 文件 | 职责 |
|
|
92
|
+
| ---------------------------- | -------------------------------------------------------- |
|
|
93
|
+
| `scripts/issue-map.ts` | 抓快照、算状态与下一步、产出 HTML。配置在里面的 `CONFIG` |
|
|
94
|
+
| `scripts/issue-map-model.ts` | 纯数据模型:分组、关键路径、排版。前后端共用 |
|
|
95
|
+
| `scripts/issue-map-i18n.ts` | 四种语言的文案与查表 |
|
|
96
|
+
| `scripts/issue-map-page.ts` | 浏览器端代码,构建时被打包进 HTML |
|
|
97
|
+
| `scripts/issue-map.html` | 模板。两个占位区块会被填入 |
|
|
98
|
+
| `scripts/issue-map-serve.ts` | 本机 server,每个请求重抓一次 |
|
|
99
|
+
| `scripts/mutate.ts` | 变异测试:改坏一行看测试会不会红 |
|
|
113
100
|
|
|
114
101
|
## 在这个 repo 里开发
|
|
115
102
|
|
|
116
103
|
```bash
|
|
117
104
|
bun install
|
|
118
|
-
bun run issue-map:serve # --watch
|
|
105
|
+
bun run issue-map:serve # --watch;不自动开标签页(每存一次档就会多一个)
|
|
119
106
|
bun run issue-map # 只产文件到 dist/issue-map.html
|
|
120
107
|
bun run check # lint + format:check + typecheck
|
|
121
|
-
bun test #
|
|
108
|
+
bun test # 纯模型那一层
|
|
122
109
|
```
|
|
123
110
|
|
|
124
111
|
这个 repo 自己还没有 issue,`GH_REPO=<owner>/<repo>` 指到有票的 repo 才画得出东西。
|
|
125
112
|
|
|
126
|
-
`tests/`
|
|
113
|
+
**测试。** `tests/` 只守会让地图说谎或不能看的事,外观(颜色、形状、间距)刻意不验。新增守门测试
|
|
114
|
+
要走反向验证——把它宣称要挡的缺陷放回产品代码,确认它会红:
|
|
127
115
|
|
|
128
116
|
```bash
|
|
129
117
|
bun run mutate scripts/issue-map-model.ts tests/issue-map-layout.test.ts
|
|
130
118
|
```
|
|
131
119
|
|
|
132
|
-
|
|
120
|
+
**要动 `scripts/issue-map-i18n.ts`。** `EN` 是原稿,也是键的定义处;三份翻译的类型由它推导,少一个
|
|
121
|
+
键或少一个 `{n}` 代入名,`bun run typecheck` 就会红。英文要分单复数的键写成 `{ one, other }`,中日
|
|
122
|
+
文写一句字符串就好。模型那一侧不算句子——`nextStep` 是 `{ kind: 'waitChildren', count: 2 }` 这种结构
|
|
123
|
+
化的值,话在这里才组出来。
|
|
124
|
+
|
|
125
|
+
## 两个设计上的决定
|
|
133
126
|
|
|
134
|
-
- **这一页不能改状态。** 没有按钮会回写 GitHub
|
|
135
|
-
-
|
|
127
|
+
- **这一页不能改状态。** 没有按钮会回写 GitHub。状态只有一个事实来源,多一个入口就会不一致。
|
|
128
|
+
- **不另设短名字段。** 站点标的是标题的开头几个字。在票里手动维护一个短名会变成票名的第二个
|
|
129
|
+
事实来源,改标题不会跟着改。完整标题在下方清单。
|
package/README.zh-TW.md
CHANGED
|
@@ -6,128 +6,124 @@
|
|
|
6
6
|
|
|
7
7
|
把 GitHub Issues 的阻擋關係畫成一頁開發地圖:**哪幾張票現在可以動、哪幾張在等誰、關鍵路徑是哪一條。**
|
|
8
8
|
|
|
9
|
-
狀態的權威永遠是 GitHub Issues。這一頁只是快照,頁面上不能改狀態——所以不會長出第二個事實來源。
|
|
10
|
-
|
|
11
|
-
## 為什麼有這個專案
|
|
12
|
-
|
|
13
|
-
起點是 [mattpocock/skills](https://github.com/mattpocock/skills)。團隊照它那套「把工作流寫成
|
|
14
|
-
skill、讓 agent 照著跑」開始做事之後,開票變得很便宜:想到一件事就開一張票,交給 skill 去接。
|
|
15
|
-
票因此長得很快——那是流程在運作的證據,不是問題。
|
|
16
|
-
|
|
17
|
-
問題在下一步。Agent 一輪吃一張票,所以每一輪真正要決定的是**派哪一張**,而這個答案不在任何
|
|
18
|
-
單一張票裡,它在票與票之間:誰擋著誰、哪一組子票還差幾張、最長的那條鏈有多長。GitHub Issues
|
|
19
|
-
一次只讓你讀一張票,要湊出那張圖就得一張一張點開來,而且每天都要重湊一次。
|
|
20
|
-
|
|
21
|
-
這一頁就是那張圖。
|
|
22
|
-
|
|
23
|
-
## 用法
|
|
24
|
-
|
|
25
|
-
在**要看的那個 repo** 裡跑:
|
|
26
|
-
|
|
27
9
|
```bash
|
|
28
10
|
bunx issue-map@latest
|
|
29
11
|
```
|
|
30
12
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
起在 `http://localhost:4747` **並直接開瀏覽器**。每次重新整理都重抓 GitHub,看到的一定是現在的狀態。repo 是 `gh` 從 cwd 的 git 推斷的,不必填。
|
|
13
|
+
在要看的那個 repo 裡跑。會直接開瀏覽器分頁,每次重新整理都重抓 GitHub。狀態的權威永遠是 GitHub
|
|
14
|
+
Issues——這一頁只是快照,不能改狀態。
|
|
34
15
|
|
|
35
|
-
|
|
16
|
+
## 為什麼有這個專案
|
|
36
17
|
|
|
37
|
-
|
|
18
|
+
Agent 一輪吃一張票,所以每一輪真正要決定的是**派哪一張**。這個答案不在任何單一張票裡,它在票與
|
|
19
|
+
票之間:誰擋著誰、哪一組子票還差幾張、最長的那條鏈有多長。GitHub 一次只讓你讀一張票。這一頁就是
|
|
20
|
+
那張圖。
|
|
38
21
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
bunx -p issue-map@latest issue-map-build out.html
|
|
42
|
-
```
|
|
22
|
+
它是繞著 [mattpocock/skills](https://github.com/mattpocock/skills) 那套做法建的——把工作流寫成
|
|
23
|
+
skill、讓 agent 照著跑——預設的標籤與指令也是從那裡來的。
|
|
43
24
|
|
|
44
|
-
|
|
25
|
+
## 用法
|
|
45
26
|
|
|
46
|
-
|
|
27
|
+
| 指令 | 得到什麼 |
|
|
28
|
+
| --------------------------------------------------- | -------------------------------- |
|
|
29
|
+
| `bunx issue-map@latest` | server 起在空 port,自動開瀏覽器 |
|
|
30
|
+
| `bunx -p issue-map@latest issue-map-build` | 靜態檔到 `dist/issue-map.html` |
|
|
31
|
+
| `bunx -p issue-map@latest issue-map-build out.html` | 靜態檔到你指定的路徑 |
|
|
47
32
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
33
|
+
- repo 是 `gh` 從 cwd 的 git 推斷的,不必填。
|
|
34
|
+
- `ISSUE_MAP_PORT` 固定 port。固定就是嚴格的:被佔住時直接失敗,不會偷偷換一個。
|
|
35
|
+
- `ISSUE_MAP_OPEN=0` 不自動開分頁。
|
|
36
|
+
- 靜態檔會過期,要看現在的狀態就用 server。
|
|
37
|
+
- 在跑不了 script 的地方(嚴格 CSP、某些預覽窗),檔案會退回一份純文字的票清單,而不是一片空白。
|
|
38
|
+
- `@latest` 取 npm 上最新的一版;要釘住就寫 `bunx issue-map@0.2.0`。
|
|
51
39
|
|
|
52
|
-
|
|
40
|
+
## 前置條件
|
|
53
41
|
|
|
54
|
-
-
|
|
55
|
-
-
|
|
56
|
-
|
|
57
|
-
- 英文要分單複數的鍵寫成 `{ one, other }`,中日文寫一句字串就好(`Intl.PluralRules` 對這幾種
|
|
58
|
-
語言只有 `other`)。
|
|
42
|
+
- **Bun**——這幾支用了 `Bun.build`、`Bun.serve`、`Bun.file`,Node 跑不起來。
|
|
43
|
+
- **`gh` CLI 已登入**,而且對目標 repo 有讀取權。
|
|
44
|
+
- 目標 repo 有 git remote 指向 GitHub。
|
|
59
45
|
|
|
60
|
-
|
|
61
|
-
的值,分組名字也一樣,話在 i18n 那一層才組出來。快照裡存中文句子的話,換一次語言就得重抓一次
|
|
62
|
-
GitHub。
|
|
46
|
+
沒有 runtime 依賴。
|
|
63
47
|
|
|
64
|
-
|
|
48
|
+
## 語言
|
|
65
49
|
|
|
66
|
-
|
|
50
|
+
頁面右上角切換:英文(預設)、繁體中文、簡體中文、日文。選了哪一種記在瀏覽器,跟 repo 無關——語言
|
|
51
|
+
是看的人的偏好,不是某個專案的設定。
|
|
67
52
|
|
|
68
|
-
|
|
69
|
-
- **`gh` CLI 已登入**,而且對目標 repo 有讀取權。
|
|
70
|
-
- 目標 repo 有 git remote 指向 GitHub。
|
|
71
|
-
- 沒有 runtime 依賴;devDependencies 只有型別與 lint/format 工具。
|
|
53
|
+
CLI 那一側(產檔訊息、錯誤)只有英文。
|
|
72
54
|
|
|
73
55
|
## 設定
|
|
74
56
|
|
|
75
57
|
全部有預設值,一個都不設也跑得起來。預設值長在 `scripts/issue-map.ts` 的 `CONFIG`。
|
|
76
58
|
|
|
77
|
-
| 環境變數 | 預設 | 意思
|
|
78
|
-
| -------------------------- | --------------------------------- |
|
|
79
|
-
| `GH_REPO` | 從 cwd 的 git 推斷 | 要畫別的 repo 時設它(`gh` 自己的變數,fork
|
|
80
|
-
| `ISSUE_MAP_PARENT_HEADING` | `Parent` |
|
|
81
|
-
| `ISSUE_MAP_LABELS_UNREADY` | `needs-triage,needs-info` |
|
|
82
|
-
| `ISSUE_MAP_LABELS_READY` | `ready-for-agent,ready-for-human` |
|
|
83
|
-
| `ISSUE_MAP_LABELS_ACTIVE` | `in-progress` |
|
|
84
|
-
| `ISSUE_MAP_LABELS_HUMAN` | `ready-for-human` |
|
|
85
|
-
| `ISSUE_MAP_CMD_IMPLEMENT` | `/implement` | 可以動工時圖上叫人跑的指令
|
|
86
|
-
| `ISSUE_MAP_CMD_TRIAGE` | `/triage` | 還要評估時圖上叫人跑的指令
|
|
87
|
-
| `ISSUE_MAP_PORT` |
|
|
88
|
-
| `ISSUE_MAP_OPEN` | 開 | 設 `0`
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
-
|
|
93
|
-
|
|
59
|
+
| 環境變數 | 預設 | 意思 |
|
|
60
|
+
| -------------------------- | --------------------------------- | ------------------------------------------------------ |
|
|
61
|
+
| `GH_REPO` | 從 cwd 的 git 推斷 | 要畫別的 repo 時設它(`gh` 自己的變數,fork 也交給它) |
|
|
62
|
+
| `ISSUE_MAP_PARENT_HEADING` | `Parent` | 子票在內文指向母票的段落標題 |
|
|
63
|
+
| `ISSUE_MAP_LABELS_UNREADY` | `needs-triage,needs-info` | 還沒評估完,不能交給誰做 |
|
|
64
|
+
| `ISSUE_MAP_LABELS_READY` | `ready-for-agent,ready-for-human` | 評估完、可以動工 |
|
|
65
|
+
| `ISSUE_MAP_LABELS_ACTIVE` | `in-progress` | 有人在做,不必有 assignee |
|
|
66
|
+
| `ISSUE_MAP_LABELS_HUMAN` | `ready-for-human` | 要人做,下一步不寫實作指令 |
|
|
67
|
+
| `ISSUE_MAP_CMD_IMPLEMENT` | `/implement` | 可以動工時圖上叫人跑的指令 |
|
|
68
|
+
| `ISSUE_MAP_CMD_TRIAGE` | `/triage` | 還要評估時圖上叫人跑的指令 |
|
|
69
|
+
| `ISSUE_MAP_PORT` | OS 指派的空 port | server 的 port |
|
|
70
|
+
| `ISSUE_MAP_OPEN` | 開 | 設 `0` 就不自動開瀏覽器 |
|
|
71
|
+
|
|
72
|
+
三個要特別想過的:
|
|
73
|
+
|
|
74
|
+
- **標籤字彙。** ready/unready 的預設值是 mattpocock/skills 五個[標準 triage 標籤](https://github.com/mattpocock/skills/blob/main/skills/engineering/setup-matt-pocock-skills/triage-labels.md)
|
|
75
|
+
裡的四個。目標 repo 沒在用這套就換成它自己的名字。快照裡完全沒出現這些標籤時,就不拿 triage 當
|
|
76
|
+
閘門,否則每張票都會變成「待評估」。(`in-progress` 是這個工具自己加的,那套 skill 沒有「有人在
|
|
77
|
+
做」這個標籤。)
|
|
78
|
+
- **指令名。** `/implement`、`/triage` 就是那邊的 [`implement`](https://github.com/mattpocock/skills/tree/main/skills/engineering/implement) 與
|
|
79
|
+
[`triage`](https://github.com/mattpocock/skills/tree/main/skills/engineering/triage) skill。要指向目標 repo 真的有的東西,不然圖上會叫人跑不存在的。
|
|
80
|
+
- **已完成的兄弟票要靠原生 sub-issue。** 地圖只跟 GitHub 要 open 票還牽著的 closed 票,而子票是從
|
|
81
|
+
原生的 sub-issue 關係拿的。用 `## Parent` 內文慣例的 repo 看不到一組裡**已完成**的子票,那一組的
|
|
82
|
+
進度會比實際少。把子票在票頁的 Sub-issues 關聯上去一次就會回來;內文慣例可以留著,原生的本來
|
|
83
|
+
就優先。
|
|
94
84
|
|
|
95
85
|
## 常見失敗
|
|
96
86
|
|
|
97
|
-
|
|
98
|
-
- `open issue 超過 100 張,這支要改成分頁抓` — GraphQL 的 `first` 上限就是 100。要支援更多票得在 `issue-map.ts` 的 `query()` 加分頁;這是要改程式,不是設定。
|
|
87
|
+
`gh api graphql failed: …` — `gh` 沒登入,或 cwd 不在目標 repo 的 git 樹裡。
|
|
99
88
|
|
|
100
89
|
## 檔案
|
|
101
90
|
|
|
102
|
-
| 檔案 | 責任
|
|
103
|
-
| ---------------------------- |
|
|
104
|
-
| `scripts/issue-map.ts` |
|
|
105
|
-
| `scripts/issue-map-model.ts` |
|
|
106
|
-
| `scripts/issue-map-i18n.ts` |
|
|
107
|
-
| `scripts/issue-map-page.ts` | 瀏覽器端程式碼,建置時被打包進 HTML
|
|
108
|
-
| `scripts/issue-map.html` |
|
|
109
|
-
| `scripts/issue-map-serve.ts` | 本機 server,每個請求重抓一次
|
|
110
|
-
| `scripts/mutate.ts` |
|
|
91
|
+
| 檔案 | 責任 |
|
|
92
|
+
| ---------------------------- | -------------------------------------------------------- |
|
|
93
|
+
| `scripts/issue-map.ts` | 抓快照、算狀態與下一步、產出 HTML。設定在裡面的 `CONFIG` |
|
|
94
|
+
| `scripts/issue-map-model.ts` | 純資料模型:分組、關鍵路徑、排版。前後端共用 |
|
|
95
|
+
| `scripts/issue-map-i18n.ts` | 四種語言的文案與查表 |
|
|
96
|
+
| `scripts/issue-map-page.ts` | 瀏覽器端程式碼,建置時被打包進 HTML |
|
|
97
|
+
| `scripts/issue-map.html` | 樣板。兩個佔位區塊會被填入 |
|
|
98
|
+
| `scripts/issue-map-serve.ts` | 本機 server,每個請求重抓一次 |
|
|
99
|
+
| `scripts/mutate.ts` | 突變測試:改壞一行看測試會不會紅 |
|
|
111
100
|
|
|
112
101
|
## 在這個 repo 裡開發
|
|
113
102
|
|
|
114
103
|
```bash
|
|
115
104
|
bun install
|
|
116
|
-
bun run issue-map:serve # --watch
|
|
105
|
+
bun run issue-map:serve # --watch;不自動開分頁(每存一次檔就會多一個)
|
|
117
106
|
bun run issue-map # 只產檔到 dist/issue-map.html
|
|
118
107
|
bun run check # lint + format:check + typecheck
|
|
119
|
-
bun test #
|
|
108
|
+
bun test # 純模型那一層
|
|
120
109
|
```
|
|
121
110
|
|
|
122
111
|
這個 repo 自己還沒有 issue,`GH_REPO=<owner>/<repo>` 指到有票的 repo 才畫得出東西。
|
|
123
112
|
|
|
124
|
-
`tests/`
|
|
113
|
+
**測試。** `tests/` 只守會讓地圖說謊或不能看的事,外觀(顏色、形狀、間距)刻意不驗。新增守門測試
|
|
114
|
+
要走反向驗證——把它宣稱要擋的缺陷放回產品碼,確認它會紅:
|
|
125
115
|
|
|
126
116
|
```bash
|
|
127
117
|
bun run mutate scripts/issue-map-model.ts tests/issue-map-layout.test.ts
|
|
128
118
|
```
|
|
129
119
|
|
|
130
|
-
|
|
120
|
+
**要動 `scripts/issue-map-i18n.ts`。** `EN` 是原稿,也是鍵的定義處;三份翻譯的型別由它推導,少一個
|
|
121
|
+
鍵或少一個 `{n}` 代入名,`bun run typecheck` 就會紅。英文要分單複數的鍵寫成 `{ one, other }`,中日
|
|
122
|
+
文寫一句字串就好。模型那一側不算句子——`nextStep` 是 `{ kind: 'waitChildren', count: 2 }` 這種結構
|
|
123
|
+
化的值,話在這裡才組出來。
|
|
124
|
+
|
|
125
|
+
## 兩個設計上的決定
|
|
131
126
|
|
|
132
|
-
- **這一頁不能改狀態。** 沒有按鈕會回寫 GitHub
|
|
133
|
-
-
|
|
127
|
+
- **這一頁不能改狀態。** 沒有按鈕會回寫 GitHub。狀態只有一個事實來源,多一個入口就會不一致。
|
|
128
|
+
- **不另設短名欄位。** 站點標的是標題的開頭幾個字。在票裡手動維護一個短名會變成票名的第二個
|
|
129
|
+
事實來源,改標題不會跟著改。完整標題在下方清單。
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "issue-map",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Draws the blocking relationships between your GitHub Issues as a one-page dev map: which issues can be picked up now, which are waiting on what, and where the critical path runs.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"bun",
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
"scripts/issue-map-i18n.ts",
|
|
27
27
|
"scripts/issue-map-model.ts",
|
|
28
28
|
"scripts/issue-map-page.ts",
|
|
29
|
+
"scripts/issue-map-view.ts",
|
|
29
30
|
"scripts/issue-map-serve.ts",
|
|
30
31
|
"scripts/issue-map.html"
|
|
31
32
|
],
|
|
@@ -42,10 +43,10 @@
|
|
|
42
43
|
"check": "bun run lint && bun run format:check && bun run typecheck"
|
|
43
44
|
},
|
|
44
45
|
"devDependencies": {
|
|
45
|
-
"@types/bun": "
|
|
46
|
-
"oxfmt": "^0.
|
|
46
|
+
"@types/bun": "^1.4.2",
|
|
47
|
+
"oxfmt": "^0.67.0",
|
|
47
48
|
"oxlint": "^1.76.0",
|
|
48
|
-
"typescript": "^
|
|
49
|
+
"typescript": "^7.0.2"
|
|
49
50
|
},
|
|
50
51
|
"engines": {
|
|
51
52
|
"bun": ">=1.0.0"
|
|
@@ -2,15 +2,15 @@
|
|
|
2
2
|
* 頁面文案。**沒有 Bun、沒有 DOM**——它只是字典與一個查表函式,所以抓資料那一側與畫面那一側
|
|
3
3
|
* 都能 import。
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* 這裡是頁面上所有給人看的字的唯一來源。畫面那一側不寫死任何一句話,模型那一側送的是結構化
|
|
6
|
+
* 的值(`nextStep`、分組名字),句子在這裡才組出來——否則同一句話會同時長在模型、樣板與畫面
|
|
7
|
+
* 三處,換語言只會換到其中一處。
|
|
8
8
|
*
|
|
9
|
-
* `en` 是原稿,也是鍵的定義處:`Messages`
|
|
9
|
+
* `en` 是原稿,也是鍵的定義處:`Messages` 由它推導,少翻一個鍵就編不過。翻譯缺鍵不 fallback
|
|
10
10
|
* 成英文——那會安靜地留下半英半中的畫面。
|
|
11
11
|
*
|
|
12
12
|
* 字典的值會進 `innerHTML`(`lede.*` 刻意帶 `<strong>`),所以**翻譯裡不要放意料外的標記**;
|
|
13
|
-
*
|
|
13
|
+
* 代入的值由呼叫端負責逃脫。
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
16
|
/** 支援的語言。第一個是預設。 */
|
|
@@ -100,6 +100,8 @@ const EN = {
|
|
|
100
100
|
'group.linkedSub': 'linked by prerequisites, but under no parent issue',
|
|
101
101
|
'group.spec': '#{n} parent spec',
|
|
102
102
|
'group.progress': 'sub-issues {done} / {total} done · parent closes only when all do',
|
|
103
|
+
'group.undrawn':
|
|
104
|
+
'Map not drawn — nothing here blocks anything. The issues are in the list below.',
|
|
103
105
|
|
|
104
106
|
'status.ready': 'ready',
|
|
105
107
|
'status.active': 'in progress',
|
|
@@ -199,6 +201,7 @@ const ZH_TW: Messages = {
|
|
|
199
201
|
'group.linkedSub': '有前置關係,但不屬於任何母票',
|
|
200
202
|
'group.spec': '#{n} 母票規格',
|
|
201
203
|
'group.progress': '子票 {done} / {total} 已完成 · 全關後才關 parent',
|
|
204
|
+
'group.undrawn': '沒有畫圖——這一組裡沒有任何阻擋關係。票在下方清單。',
|
|
202
205
|
|
|
203
206
|
'status.ready': '可接手',
|
|
204
207
|
'status.active': '進行中',
|
|
@@ -288,6 +291,7 @@ const ZH_CN: Messages = {
|
|
|
288
291
|
'group.linkedSub': '有前置关系,但不属于任何母票',
|
|
289
292
|
'group.spec': '#{n} 母票规格',
|
|
290
293
|
'group.progress': '子票 {done} / {total} 已完成 · 全关后才关 parent',
|
|
294
|
+
'group.undrawn': '没有画图——这一组里没有任何阻挡关系。票在下方清单。',
|
|
291
295
|
|
|
292
296
|
'status.ready': '可接手',
|
|
293
297
|
'status.active': '进行中',
|
|
@@ -381,6 +385,8 @@ const JA: Messages = {
|
|
|
381
385
|
'group.linkedSub': '依存関係はあるが、親チケットには属さない',
|
|
382
386
|
'group.spec': '#{n} 親チケットの仕様',
|
|
383
387
|
'group.progress': 'サブチケット {done} / {total} 完了 · すべて閉じてから親を閉じる',
|
|
388
|
+
'group.undrawn':
|
|
389
|
+
'図は描いていません——このグループにはブロック関係がありません。チケットは下の一覧にあります。',
|
|
384
390
|
|
|
385
391
|
'status.ready': '着手可',
|
|
386
392
|
'status.active': '対応中',
|
|
@@ -3,8 +3,7 @@
|
|
|
3
3
|
* 那一側(`issue-map-page.ts`)都 import 它,所以它不能碰任何一邊的專屬 API。
|
|
4
4
|
*
|
|
5
5
|
* 這裡住的是「同一份定義只有一份」的東西:狀態的五個值、一張票的形狀、分群規則、關鍵路徑,
|
|
6
|
-
*
|
|
7
|
-
* 是共用的,抄錯編不過。
|
|
6
|
+
* 以及線路圖的排版。兩側共用同一份型別,任何一邊自己抄一份都編不過。
|
|
8
7
|
*/
|
|
9
8
|
|
|
10
9
|
/** 一張票現在的處境。同時是頁面的顏色與篩選分頁。 */
|
|
@@ -75,7 +74,6 @@ export type Snapshot = {
|
|
|
75
74
|
readonly repo: string
|
|
76
75
|
/** 這一次實際生效的標籤字彙。圖例照它寫,不然改了設定圖例就會說謊。 */
|
|
77
76
|
readonly labels: { readonly ready: readonly string[]; readonly unready: readonly string[] }
|
|
78
|
-
/** 一張圖一群。 */
|
|
79
77
|
readonly groups: readonly Group[]
|
|
80
78
|
/** 最長的一條依序未完成鏈,也就是最少要幾輪。 */
|
|
81
79
|
readonly criticalPath: number
|
|
@@ -185,6 +183,13 @@ export type Layout = {
|
|
|
185
183
|
readonly height: number
|
|
186
184
|
}
|
|
187
185
|
|
|
186
|
+
/** 月台一列至少幾站。票很少時不要排成細細一條。 */
|
|
187
|
+
const ISLAND_MIN_PER_ROW = 4
|
|
188
|
+
/**
|
|
189
|
+
* 月台一列最多幾站。再寬下去橫向要捲很遠,而月台上的左右位置本來就不帶意義——寧可多幾列。
|
|
190
|
+
*/
|
|
191
|
+
const ISLAND_MAX_PER_ROW = 12
|
|
192
|
+
|
|
188
193
|
/**
|
|
189
194
|
* 把一組票排成線路圖。
|
|
190
195
|
*
|
|
@@ -235,7 +240,9 @@ export function layoutOf(members: readonly MapIssue[]): Layout {
|
|
|
235
240
|
}
|
|
236
241
|
|
|
237
242
|
const depth = wired.length ? Math.max(...wired.map((m) => levelOf(m.number))) : 1
|
|
238
|
-
|
|
243
|
+
// 月台排成接近正方形,不然沒有阻擋關係的 repo 會把整批票疊成一條幾千 px 高的直條。
|
|
244
|
+
const squarish = Math.ceil(Math.sqrt(island.length))
|
|
245
|
+
const perRow = Math.max(depth, Math.min(squarish, ISLAND_MAX_PER_ROW), ISLAND_MIN_PER_ROW)
|
|
239
246
|
const xy = new Map<number, Point>()
|
|
240
247
|
tracks.forEach((chain, index) => {
|
|
241
248
|
for (const n of chain) {
|