issue-map 0.3.1 → 0.5.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.zh-CN.md CHANGED
@@ -6,128 +6,127 @@
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
- `@latest` npm 上最新的一版;要钉住特定版本就写 `bunx issue-map@0.2.0`。
13
+ 在要看的那个 repo 里跑。会直接开浏览器标签页,每次刷新都重抓 GitHub。状态的权威永远是 GitHub
14
+ Issues——这一页只是快照,不能改状态。
32
15
 
33
- 起在 `http://localhost:4747` **并直接开浏览器**。每次刷新都重抓 GitHub,看到的一定是现在的状态。repo 是 `gh` 从 cwd 的 git 推断的,不必填。
16
+ ## 为什么有这个项目
34
17
 
35
- 不要自动开标签页就设 `ISSUE_MAP_OPEN=0`。
18
+ Agent 一轮吃一张票,所以每一轮真正要决定的是**派哪一张**。这个答案不在任何单一张票里,它在票与
19
+ 票之间:谁挡着谁、哪一组子票还差几张、最长的那条链有多长。GitHub 一次只让你读一张票。这一页就是
20
+ 那张图。
36
21
 
37
- 只要一份静态 HTML 的话(`-p` 是用来选另一个 bin 的,少了它会变成起 server):
22
+ 它是绕着 [mattpocock/skills](https://github.com/mattpocock/skills) 那套做法建的——把工作流写成
23
+ skill、让 agent 照着跑——默认的标签与指令也是从那里来的。
38
24
 
39
- ```bash
40
- bunx -p issue-map@latest issue-map-build # 写到 dist/issue-map.html
41
- bunx -p issue-map@latest issue-map-build out.html
42
- ```
43
-
44
- 快照就是快照——状态会过期,要看现在的状态就用上面的 server。
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
- (localStorage `issue-map:locale`),跟 repo 无关——语言是看的人的偏好,不是某个项目的设置。
50
- 刻意不看 `navigator.language`:默认就是英文,猜错了反而每次进来都要改回去。
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
- 文案全部在 `scripts/issue-map-i18n.ts`,那是页面上每一句话的唯一来源:
40
+ ## 前置条件
53
41
 
54
- - `EN` 是原稿,也是键的定义处。三份翻译的类型由它推导,少翻一个键 `bun run typecheck` 就会红。
55
- - 句子里的代入名(`{n}`、`{issues}`)也是类型的一部分,少传一个编不过——不然缺的那个会以
56
- `{n}` 的样子印在画面上,而那要真的跑到那一格才看得到。
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
- 模型与抓取那一侧**不再算好句子**:`nextStep` `{ kind: 'waitChildren', count: 2 }` 这种结构化
61
- 的值,分组名字也一样,话在 i18n 那一层才组出来。快照里存中文句子的话,换一次语言就得重抓一次
62
- GitHub。
46
+ 没有 runtime 依赖。
63
47
 
64
- CLI 那一侧(产文件消息、错误)只有英文,也不跟着页面的语言走:那是 `bunx issue-map` 的输出,给下指令的人看的,不是页面的一部分。例外是 `scripts/mutate.ts`——它是这个 repo 内部的工具,留中文。
48
+ ## 语言
65
49
 
66
- ## 前置条件
50
+ 页面右上角切换:英文(默认)、繁体中文、简体中文、日文。选了哪一种记在浏览器,跟 repo 无关——语言
51
+ 是看的人的偏好,不是某个项目的设置。
67
52
 
68
- - **Bun**。这几支用了 `Bun.build`、`Bun.serve`、`Bun.file` 与 bun 的 `spawnSync`,Node 跑不起来。
69
- - **`gh` CLI 已登录**,而且对目标 repo 有读取权。
70
- - 目标 repo 有 git remote 指向 GitHub。
71
- - 没有 runtime 依赖;devDependencies 只有类型与 lint/format 工具。
53
+ CLI 那一侧(产文件消息、错误)只有英文。
72
54
 
73
55
  ## 配置
74
56
 
75
- 全部有默认值,一个都不设也跑得起来。默认值长在 `scripts/issue-map.ts` 的 `CONFIG`。
76
-
77
- | 环境变量 | 默认 | 意思 |
78
- | -------------------------- | --------------------------------- | ---------------------------------------------------------------------------------------------------------- |
79
- | `GH_REPO` | 从 cwd 的 git 推断 | 要画别的 repo 时设它(`gh` 自己的变量,fork 与多 remote 也交给它) |
80
- | `ISSUE_MAP_PARENT_HEADING` | `Parent` | 子票在正文指向母票的段落标题。GitHub 原生 sub-issue 有值时优先,而且只有原生的看得到已完成的兄弟票(见下) |
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` | 挂了代表有人在做,不必有 assignee |
84
- | `ISSUE_MAP_LABELS_HUMAN` | `ready-for-human` | 这些要人做,下一步不写实作指令 |
85
- | `ISSUE_MAP_CMD_IMPLEMENT` | `/implement` | 可以动工时图上叫人跑的指令 |
86
- | `ISSUE_MAP_CMD_TRIAGE` | `/triage` | 还要评估时图上叫人跑的指令 |
87
- | `ISSUE_MAP_PORT` | `4747` | server 的端口 |
88
- | `ISSUE_MAP_OPEN` | 开 | 设 `0` 就不自动开浏览器(`bun --watch` 的开发模式默认关掉) |
57
+ 全部有默认值,一个都不设也跑得起来。默认值长在 `src/issue-map.ts` 的 `CONFIG`。
58
+
59
+ | 环境变量 | 默认 | 意思 |
60
+ | -------------------------- | --------------------------------- | ------------------------------------------------------ |
61
+ | `GH_REPO` | 从 cwd 的 git 推断 | 要画别的 repo 时设它(`gh` 自己的变量,fork 也交给它) |
62
+ | `ISSUE_MAP_PARENT_HEADING` | 未设(不抓正文) | 子票在正文指向母票的段落标题。设了才会读正文 |
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` 就不自动开浏览器 |
89
71
 
90
72
  三个要特别想过的:
91
73
 
92
- - **标签词汇**:目标 repo 没在用这套标签就要换成它自己的名字。程序会侦测——快照里完全没出现 ready/unready 任何一个标签时,就不拿 triage 当闸门,否则每张票都会变成「待评估」。
93
- - **指令名**:`/implement`、`/triage` Claude Code 的 skill。目标 repo 没有的话一定要换掉,不然图上会叫人跑不存在的东西。
94
- - **已完成的兄弟票要靠原生 sub-issue**:地图只跟 GitHub 要 open 票还牵着的 closed 票——它的阻挡者、它的 parent,以及那个 parent 底下的子票。子票是从 GitHub 原生的 sub-issue 关系拿的,所以用 `## Parent` 正文惯例的 repo 看不到一组里**已完成**的子票,那一组的进度会比实际少。open 票不受影响。另一条路是整包扫过 repo 里所有 closed 票,而那在老 repo 上是几十次请求换个位数张票。一组票如果是原生 sub-issue 之前开的,把子票关联上去一次(票页的 Sub-issues)就会回来;正文惯例可以留着,原生的本来就优先。
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
+ - **正文惯例要明讲才生效。** 正文占了 GitHub 响应九成以上的重量,而它只喂一条 regex,页面上
81
+ 没有别的地方用得到。所以只有设了 `ISSUE_MAP_PARENT_HEADING` 才会去抓正文。原生 sub-issue
82
+ 不必设置,而且一律优先。
83
+ - **已完成的兄弟票要靠原生 sub-issue。** 地图只跟 GitHub 要 open 票还牵着的 closed 票,而子票是从
84
+ 原生的 sub-issue 关系拿的。用 `## Parent` 正文惯例的 repo 看不到一组里**已完成**的子票,那一组的
85
+ 进度会比实际少。把子票在票页的 Sub-issues 关联上去一次就会回来;正文惯例可以留着,原生的本来
86
+ 就优先。
95
87
 
96
88
  ## 常见失败
97
89
 
98
- - `gh api graphql failed: …` — `gh` 没登录,或 cwd 不在目标 repo 的 git 树里。
90
+ `gh api graphql failed: …` — `gh` 没登录,或 cwd 不在目标 repo 的 git 树里。
99
91
 
100
92
  ## 文件
101
93
 
102
- | 文件 | 职责 |
103
- | ---------------------------- | ---------------------------------------------------------------------- |
104
- | `scripts/issue-map.ts` | 抓快照、算每张票的状态与下一步、产出 HTML。移植配置在里面的 `CONFIG` |
105
- | `scripts/issue-map-model.ts` | 纯数据模型:分组、关键路径。前后端共用 |
106
- | `scripts/issue-map-i18n.ts` | 四种语言的文案与查表。页面上每一句话的唯一来源 |
107
- | `scripts/issue-map-page.ts` | 浏览器端代码,构建时被打包进 HTML |
108
- | `scripts/issue-map.html` | 模板。两个占位区块(`issue-map-data`、`issue-map-code`)会被填入 |
109
- | `scripts/issue-map-serve.ts` | 本机 server,每个请求重抓一次 |
110
- | `scripts/mutate.ts` | 变异测试:改坏一行看测试会不会红。守门测试的反向验证用它,不要手改文件 |
94
+ | 文件 | 职责 |
95
+ | ------------------------ | -------------------------------------------------------- |
96
+ | `src/issue-map.ts` | 抓快照、算状态与下一步、产出 HTML。配置在里面的 `CONFIG` |
97
+ | `src/issue-map-model.ts` | 纯数据模型:分组、关键路径、排版。前后端共用 |
98
+ | `src/issue-map-i18n.ts` | 四种语言的文案与查表 |
99
+ | `src/issue-map-page.ts` | 浏览器端代码,构建时被打包进 HTML |
100
+ | `src/issue-map.html` | 模板。两个占位区块会被填入 |
101
+ | `src/issue-map-serve.ts` | 本机 server,每个请求重抓一次 |
102
+ | `tools/mutate.ts` | 变异测试:改坏一行看测试会不会红 |
111
103
 
112
104
  ## 在这个 repo 里开发
113
105
 
114
106
  ```bash
115
107
  bun install
116
- bun run issue-map:serve # --watch,改代码会自动重启;刻意不自动开浏览器(每存一次档就会多一个标签页)
108
+ bun run issue-map:serve # --watch;不自动开标签页(每存一次档就会多一个)
117
109
  bun run issue-map # 只产文件到 dist/issue-map.html
118
110
  bun run check # lint + format:check + typecheck
119
- bun test # 纯模型那一层(分组、关键路径、排版)
111
+ bun test # 纯模型那一层
120
112
  ```
121
113
 
122
114
  这个 repo 自己还没有 issue,`GH_REPO=<owner>/<repo>` 指到有票的 repo 才画得出东西。
123
115
 
124
- `tests/` 只守会让地图说谎或不能看的事,外观(颜色、形状、间距)刻意不验。新增守门测试要走反向验证——把它宣称要挡的缺陷放回产品代码,确认它会红:
116
+ **测试。** `tests/` 只守会让地图说谎或不能看的事,外观(颜色、形状、间距)刻意不验。新增守门测试
117
+ 要走反向验证——把它宣称要挡的缺陷放回产品代码,确认它会红:
125
118
 
126
119
  ```bash
127
- bun run mutate scripts/issue-map-model.ts tests/issue-map-layout.test.ts
120
+ bun run mutate src/issue-map-model.ts tests/issue-map-layout.test.ts
128
121
  ```
129
122
 
130
- ## 两个设计上的决定,改之前先知道
123
+ **要动 `src/issue-map-i18n.ts`。** `EN` 是原稿,也是键的定义处;三份翻译的类型由它推导,少一个
124
+ 键或少一个 `{n}` 代入名,`bun run typecheck` 就会红。英文要分单复数的键写成 `{ one, other }`,中日
125
+ 文写一句字符串就好。模型那一侧不算句子——`nextStep` 是 `{ kind: 'waitChildren', count: 2 }` 这种结构
126
+ 化的值,话在这里才组出来。
127
+
128
+ ## 两个设计上的决定
131
129
 
132
- - **这一页不能改状态。** 没有按钮会回写 GitHub。刻意的:状态只有一个事实来源,多一个入口就会不一致。
133
- - **票名不进地图。** 节点只挂票号,名字在下方清单。试过在正文加短名段落,那是票名的第二个事实来源,改标题不会改它;机械缩短标题读不通。
130
+ - **这一页不能改状态。** 没有按钮会回写 GitHub。状态只有一个事实来源,多一个入口就会不一致。
131
+ - **不另设短名字段。** 站点标的是标题的开头几个字。在票里手动维护一个短名会变成票名的第二个
132
+ 事实来源,改标题不会跟着改。完整标题在下方清单。
package/README.zh-TW.md CHANGED
@@ -6,128 +6,127 @@
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
- `@latest` npm 上最新的一版;要釘住特定版本就寫 `bunx issue-map@0.2.0`。
13
+ 在要看的那個 repo 裡跑。會直接開瀏覽器分頁,每次重新整理都重抓 GitHub。狀態的權威永遠是 GitHub
14
+ Issues——這一頁只是快照,不能改狀態。
32
15
 
33
- 起在 `http://localhost:4747` **並直接開瀏覽器**。每次重新整理都重抓 GitHub,看到的一定是現在的狀態。repo 是 `gh` 從 cwd 的 git 推斷的,不必填。
16
+ ## 為什麼有這個專案
34
17
 
35
- 不要自動開分頁就設 `ISSUE_MAP_OPEN=0`。
18
+ Agent 一輪吃一張票,所以每一輪真正要決定的是**派哪一張**。這個答案不在任何單一張票裡,它在票與
19
+ 票之間:誰擋著誰、哪一組子票還差幾張、最長的那條鏈有多長。GitHub 一次只讓你讀一張票。這一頁就是
20
+ 那張圖。
36
21
 
37
- 只要一份靜態 HTML 的話(`-p` 是用來選另一個 bin 的,少了它會變成起 server):
22
+ 它是繞著 [mattpocock/skills](https://github.com/mattpocock/skills) 那套做法建的——把工作流寫成
23
+ skill、讓 agent 照著跑——預設的標籤與指令也是從那裡來的。
38
24
 
39
- ```bash
40
- bunx -p issue-map@latest issue-map-build # 寫到 dist/issue-map.html
41
- bunx -p issue-map@latest issue-map-build out.html
42
- ```
43
-
44
- 快照就是快照——狀態會過期,要看現在的狀態就用上面的 server。
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
- (localStorage `issue-map:locale`),跟 repo 無關——語言是看的人的偏好,不是某個專案的設定。
50
- 刻意不看 `navigator.language`:預設就是英文,猜錯了反而每次進來都要改回去。
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
- 文案全部在 `scripts/issue-map-i18n.ts`,那是頁面上每一句話的唯一來源:
40
+ ## 前置條件
53
41
 
54
- - `EN` 是原稿,也是鍵的定義處。三份翻譯的型別由它推導,少翻一個鍵 `bun run typecheck` 就會紅。
55
- - 句子裡的代入名(`{n}`、`{issues}`)也是型別的一部分,少傳一個編不過——不然缺的那個會以
56
- `{n}` 的樣子印在畫面上,而那要真的跑到那一格才看得到。
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
- 模型與抓取那一側**不再算好句子**:`nextStep` `{ kind: 'waitChildren', count: 2 }` 這種結構化
61
- 的值,分組名字也一樣,話在 i18n 那一層才組出來。快照裡存中文句子的話,換一次語言就得重抓一次
62
- GitHub。
46
+ 沒有 runtime 依賴。
63
47
 
64
- CLI 那一側(產檔訊息、錯誤)只有英文,也不跟著頁面的語言走:那是 `bunx issue-map` 的輸出,給下指令的人看的,不是頁面的一部分。例外是 `scripts/mutate.ts`——它是這個 repo 內部的工具,留中文。
48
+ ## 語言
65
49
 
66
- ## 前置條件
50
+ 頁面右上角切換:英文(預設)、繁體中文、簡體中文、日文。選了哪一種記在瀏覽器,跟 repo 無關——語言
51
+ 是看的人的偏好,不是某個專案的設定。
67
52
 
68
- - **Bun**。這幾支用了 `Bun.build`、`Bun.serve`、`Bun.file` 與 bun 的 `spawnSync`,Node 跑不起來。
69
- - **`gh` CLI 已登入**,而且對目標 repo 有讀取權。
70
- - 目標 repo 有 git remote 指向 GitHub。
71
- - 沒有 runtime 依賴;devDependencies 只有型別與 lint/format 工具。
53
+ CLI 那一側(產檔訊息、錯誤)只有英文。
72
54
 
73
55
  ## 設定
74
56
 
75
- 全部有預設值,一個都不設也跑得起來。預設值長在 `scripts/issue-map.ts` 的 `CONFIG`。
76
-
77
- | 環境變數 | 預設 | 意思 |
78
- | -------------------------- | --------------------------------- | ---------------------------------------------------------------------------------------------------------- |
79
- | `GH_REPO` | 從 cwd 的 git 推斷 | 要畫別的 repo 時設它(`gh` 自己的變數,fork 與多 remote 也交給它) |
80
- | `ISSUE_MAP_PARENT_HEADING` | `Parent` | 子票在內文指向母票的段落標題。GitHub 原生 sub-issue 有值時優先,而且只有原生的看得到已完成的兄弟票(見下) |
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` | 掛了代表有人在做,不必有 assignee |
84
- | `ISSUE_MAP_LABELS_HUMAN` | `ready-for-human` | 這些要人做,下一步不寫實作指令 |
85
- | `ISSUE_MAP_CMD_IMPLEMENT` | `/implement` | 可以動工時圖上叫人跑的指令 |
86
- | `ISSUE_MAP_CMD_TRIAGE` | `/triage` | 還要評估時圖上叫人跑的指令 |
87
- | `ISSUE_MAP_PORT` | `4747` | server 的 port |
88
- | `ISSUE_MAP_OPEN` | 開 | 設 `0` 就不自動開瀏覽器(`bun --watch` 的開發模式預設關掉) |
57
+ 全部有預設值,一個都不設也跑得起來。預設值長在 `src/issue-map.ts` 的 `CONFIG`。
58
+
59
+ | 環境變數 | 預設 | 意思 |
60
+ | -------------------------- | --------------------------------- | ------------------------------------------------------ |
61
+ | `GH_REPO` | 從 cwd 的 git 推斷 | 要畫別的 repo 時設它(`gh` 自己的變數,fork 也交給它) |
62
+ | `ISSUE_MAP_PARENT_HEADING` | 未設(不抓內文) | 子票在內文指向母票的段落標題。設了才會讀內文 |
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` 就不自動開瀏覽器 |
89
71
 
90
72
  三個要特別想過的:
91
73
 
92
- - **標籤字彙**:目標 repo 沒在用這套標籤就要換成它自己的名字。程式會偵測——快照裡完全沒出現 ready/unready 任何一個標籤時,就不拿 triage 當閘門,否則每張票都會變成「待評估」。
93
- - **指令名**:`/implement`、`/triage` Claude Code 的 skill。目標 repo 沒有的話一定要換掉,不然圖上會叫人跑不存在的東西。
94
- - **已完成的兄弟票要靠原生 sub-issue**:地圖只跟 GitHub 要 open 票還牽著的 closed 票——它的阻擋者、它的 parent,以及那個 parent 底下的子票。子票是從 GitHub 原生的 sub-issue 關係拿的,所以用 `## Parent` 內文慣例的 repo 看不到一組裡**已完成**的子票,那一組的進度會比實際少。open 票不受影響。另一條路是整包掃過 repo 裡所有 closed 票,而那在老 repo 上是幾十次請求換個位數張票。一組票如果是原生 sub-issue 之前開的,把子票關聯上去一次(票頁的 Sub-issues)就會回來;內文慣例可以留著,原生的本來就優先。
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
+ - **內文慣例要明講才生效。** 內文佔了 GitHub 回應九成以上的重量,而它只餵一條 regex,頁面上
81
+ 沒有別的地方用得到。所以只有設了 `ISSUE_MAP_PARENT_HEADING` 才會去抓內文。原生 sub-issue
82
+ 不必設定,而且一律優先。
83
+ - **已完成的兄弟票要靠原生 sub-issue。** 地圖只跟 GitHub 要 open 票還牽著的 closed 票,而子票是從
84
+ 原生的 sub-issue 關係拿的。用 `## Parent` 內文慣例的 repo 看不到一組裡**已完成**的子票,那一組的
85
+ 進度會比實際少。把子票在票頁的 Sub-issues 關聯上去一次就會回來;內文慣例可以留著,原生的本來
86
+ 就優先。
95
87
 
96
88
  ## 常見失敗
97
89
 
98
- - `gh api graphql failed: …` — `gh` 沒登入,或 cwd 不在目標 repo 的 git 樹裡。
90
+ `gh api graphql failed: …` — `gh` 沒登入,或 cwd 不在目標 repo 的 git 樹裡。
99
91
 
100
92
  ## 檔案
101
93
 
102
- | 檔案 | 責任 |
103
- | ---------------------------- | ---------------------------------------------------------------------- |
104
- | `scripts/issue-map.ts` | 抓快照、算每張票的狀態與下一步、產出 HTML。移植設定在裡面的 `CONFIG` |
105
- | `scripts/issue-map-model.ts` | 純資料模型:分組、關鍵路徑。前後端共用 |
106
- | `scripts/issue-map-i18n.ts` | 四種語言的文案與查表。頁面上每一句話的唯一來源 |
107
- | `scripts/issue-map-page.ts` | 瀏覽器端程式碼,建置時被打包進 HTML |
108
- | `scripts/issue-map.html` | 樣板。兩個佔位區塊(`issue-map-data`、`issue-map-code`)會被填入 |
109
- | `scripts/issue-map-serve.ts` | 本機 server,每個請求重抓一次 |
110
- | `scripts/mutate.ts` | 突變測試:改壞一行看測試會不會紅。守門測試的反向驗證用它,不要手改檔案 |
94
+ | 檔案 | 責任 |
95
+ | ------------------------ | -------------------------------------------------------- |
96
+ | `src/issue-map.ts` | 抓快照、算狀態與下一步、產出 HTML。設定在裡面的 `CONFIG` |
97
+ | `src/issue-map-model.ts` | 純資料模型:分組、關鍵路徑、排版。前後端共用 |
98
+ | `src/issue-map-i18n.ts` | 四種語言的文案與查表 |
99
+ | `src/issue-map-page.ts` | 瀏覽器端程式碼,建置時被打包進 HTML |
100
+ | `src/issue-map.html` | 樣板。兩個佔位區塊會被填入 |
101
+ | `src/issue-map-serve.ts` | 本機 server,每個請求重抓一次 |
102
+ | `tools/mutate.ts` | 突變測試:改壞一行看測試會不會紅 |
111
103
 
112
104
  ## 在這個 repo 裡開發
113
105
 
114
106
  ```bash
115
107
  bun install
116
- bun run issue-map:serve # --watch,改程式碼會自動重啟;刻意不自動開瀏覽器(每存一次檔就會多一個分頁)
108
+ bun run issue-map:serve # --watch;不自動開分頁(每存一次檔就會多一個)
117
109
  bun run issue-map # 只產檔到 dist/issue-map.html
118
110
  bun run check # lint + format:check + typecheck
119
- bun test # 純模型那一層(分組、關鍵路徑、排版)
111
+ bun test # 純模型那一層
120
112
  ```
121
113
 
122
114
  這個 repo 自己還沒有 issue,`GH_REPO=<owner>/<repo>` 指到有票的 repo 才畫得出東西。
123
115
 
124
- `tests/` 只守會讓地圖說謊或不能看的事,外觀(顏色、形狀、間距)刻意不驗。新增守門測試要走反向驗證——把它宣稱要擋的缺陷放回產品碼,確認它會紅:
116
+ **測試。** `tests/` 只守會讓地圖說謊或不能看的事,外觀(顏色、形狀、間距)刻意不驗。新增守門測試
117
+ 要走反向驗證——把它宣稱要擋的缺陷放回產品碼,確認它會紅:
125
118
 
126
119
  ```bash
127
- bun run mutate scripts/issue-map-model.ts tests/issue-map-layout.test.ts
120
+ bun run mutate src/issue-map-model.ts tests/issue-map-layout.test.ts
128
121
  ```
129
122
 
130
- ## 兩個設計上的決定,改之前先知道
123
+ **要動 `src/issue-map-i18n.ts`。** `EN` 是原稿,也是鍵的定義處;三份翻譯的型別由它推導,少一個
124
+ 鍵或少一個 `{n}` 代入名,`bun run typecheck` 就會紅。英文要分單複數的鍵寫成 `{ one, other }`,中日
125
+ 文寫一句字串就好。模型那一側不算句子——`nextStep` 是 `{ kind: 'waitChildren', count: 2 }` 這種結構
126
+ 化的值,話在這裡才組出來。
127
+
128
+ ## 兩個設計上的決定
131
129
 
132
- - **這一頁不能改狀態。** 沒有按鈕會回寫 GitHub。刻意的:狀態只有一個事實來源,多一個入口就會不一致。
133
- - **票名不進地圖。** 節點只掛票號,名字在下方清單。試過在內文加短名段落,那是票名的第二個事實來源,改標題不會改它;機械縮短標題讀不通。
130
+ - **這一頁不能改狀態。** 沒有按鈕會回寫 GitHub。狀態只有一個事實來源,多一個入口就會不一致。
131
+ - **不另設短名欄位。** 站點標的是標題的開頭幾個字。在票裡手動維護一個短名會變成票名的第二個
132
+ 事實來源,改標題不會跟著改。完整標題在下方清單。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "issue-map",
3
- "version": "0.3.1",
3
+ "version": "0.5.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",
@@ -15,26 +15,21 @@
15
15
  "url": "git+https://github.com/gunter1020/issue-map.git"
16
16
  },
17
17
  "bin": {
18
- "issue-map": "./scripts/issue-map-serve.ts",
19
- "issue-map-build": "./scripts/issue-map.ts"
18
+ "issue-map": "./src/issue-map-serve.ts",
19
+ "issue-map-build": "./src/issue-map.ts"
20
20
  },
21
21
  "files": [
22
22
  "README.zh-TW.md",
23
23
  "README.zh-CN.md",
24
24
  "README.ja.md",
25
- "scripts/issue-map.ts",
26
- "scripts/issue-map-i18n.ts",
27
- "scripts/issue-map-model.ts",
28
- "scripts/issue-map-page.ts",
29
- "scripts/issue-map-serve.ts",
30
- "scripts/issue-map.html"
25
+ "src"
31
26
  ],
32
27
  "type": "module",
33
28
  "scripts": {
34
- "issue-map": "bun run scripts/issue-map.ts",
35
- "issue-map:serve": "ISSUE_MAP_OPEN=0 bun --watch scripts/issue-map-serve.ts",
29
+ "issue-map": "bun run src/issue-map.ts",
30
+ "issue-map:serve": "ISSUE_MAP_OPEN=0 bun --watch src/issue-map-serve.ts",
36
31
  "typecheck": "tsc --noEmit",
37
- "mutate": "bun run scripts/mutate.ts",
32
+ "mutate": "bun run tools/mutate.ts",
38
33
  "format": "oxfmt .",
39
34
  "format:check": "oxfmt --check .",
40
35
  "lint": "oxlint .",
@@ -42,10 +37,10 @@
42
37
  "check": "bun run lint && bun run format:check && bun run typecheck"
43
38
  },
44
39
  "devDependencies": {
45
- "@types/bun": "latest",
46
- "oxfmt": "^0.62.0",
40
+ "@types/bun": "^1.4.2",
41
+ "oxfmt": "^0.67.0",
47
42
  "oxlint": "^1.76.0",
48
- "typescript": "^5"
43
+ "typescript": "^7.0.2"
49
44
  },
50
45
  "engines": {
51
46
  "bun": ">=1.0.0"
@@ -2,15 +2,15 @@
2
2
  * 頁面文案。**沒有 Bun、沒有 DOM**——它只是字典與一個查表函式,所以抓資料那一側與畫面那一側
3
3
  * 都能 import。
4
4
  *
5
- * 這裡是頁面上所有給人看的字的唯一來源。畫面那一側不寫死任何一句話,模型那一側也不再算好
6
- * 中文句子送過來——`nextStep` 與分組名字都是結構化的值,句子在這裡才組出來。少了這一層,同一
7
- * 句話會同時長在模型、樣板與畫面三處,換語言只會換到其中一處。
5
+ * 這裡是頁面上所有給人看的字的唯一來源。畫面那一側不寫死任何一句話,模型那一側送的是結構化
6
+ * 的值(`nextStep`、分組名字),句子在這裡才組出來——否則同一句話會同時長在模型、樣板與畫面
7
+ * 三處,換語言只會換到其中一處。
8
8
  *
9
- * `en` 是原稿,也是鍵的定義處:`Messages` 由它推導,少翻一個鍵就編不過。翻譯缺鍵不會 fallback
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': '対応中',