issue-map 0.2.0 → 0.3.1
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 +167 -0
- package/README.md +133 -64
- package/README.zh-CN.md +133 -0
- package/README.zh-TW.md +133 -0
- package/package.json +6 -2
- package/scripts/issue-map-i18n.ts +486 -0
- package/scripts/issue-map-model.ts +33 -13
- package/scripts/issue-map-page.ts +241 -75
- package/scripts/issue-map-serve.ts +4 -3
- package/scripts/issue-map.html +43 -12
- package/scripts/issue-map.ts +192 -61
package/README.ja.md
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# issue-map
|
|
2
|
+
|
|
3
|
+
[English](README.md) · [繁體中文](README.zh-TW.md) · [简体中文](README.zh-CN.md) · **日本語**
|
|
4
|
+
|
|
5
|
+
> 原稿は[英語版](README.md)です。翻訳が遅れている場合はそちらが正です。
|
|
6
|
+
|
|
7
|
+
GitHub Issues のブロック関係を 1 ページの開発マップにします:**いま着手できるのはどれか、どれが
|
|
8
|
+
何を待っているか、クリティカルパスはどこを通るか。**
|
|
9
|
+
|
|
10
|
+
状態の正は常に GitHub Issues です。このページはスナップショットにすぎず、ここでは状態を変えられ
|
|
11
|
+
ません——だから 2 つ目の正が生まれません。
|
|
12
|
+
|
|
13
|
+
## なぜこれを作ったか
|
|
14
|
+
|
|
15
|
+
出発点は [mattpocock/skills](https://github.com/mattpocock/skills) です。「ワークフローを skill と
|
|
16
|
+
して書き、agent に走らせる」というやり方をチームで採り入れてから、チケットを起票するコストが
|
|
17
|
+
下がりました。思いついたら 1 枚起票して skill に渡す。結果としてチケットは一気に増えます。それは
|
|
18
|
+
プロセスが機能している証拠であって、問題ではありません。
|
|
19
|
+
|
|
20
|
+
問題は次の一手です。Agent は 1 ラウンドに 1 枚しか扱えないので、毎ラウンド実際に決めるのは**どれを
|
|
21
|
+
渡すか**です。その答えはどの 1 枚の中にもなく、チケットとチケットの間にあります——誰が誰を
|
|
22
|
+
ブロックしているか、親はあと何枚のサブチケットを待っているか、いちばん長い鎖は何段か。GitHub
|
|
23
|
+
Issues は一度に 1 枚しか見せてくれないので、その絵を組み立てるには 1 枚ずつ開くしかなく、しかも
|
|
24
|
+
明日もまた同じことをします。
|
|
25
|
+
|
|
26
|
+
このページがその絵です。
|
|
27
|
+
|
|
28
|
+
## 使い方
|
|
29
|
+
|
|
30
|
+
**見たい repo の中で**実行します:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
bunx issue-map@latest
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
`@latest` は npm 上の最新版を取ります。特定のバージョンに固定するなら `bunx issue-map@0.2.0`。
|
|
37
|
+
|
|
38
|
+
`http://localhost:4747` で起動し、**そのままブラウザを開きます**。更新するたびに GitHub を取り直す
|
|
39
|
+
ので、見えているのは常に現在の状態です。repo は `gh` が cwd の git から推測するため、指定は不要
|
|
40
|
+
です。
|
|
41
|
+
|
|
42
|
+
タブを勝手に開いてほしくなければ `ISSUE_MAP_OPEN=0` を設定します。
|
|
43
|
+
|
|
44
|
+
静的な HTML が 1 枚欲しいだけなら(`-p` はもう一方の bin を選ぶためのもので、これがないと server が
|
|
45
|
+
起動します):
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
bunx -p issue-map@latest issue-map-build # dist/issue-map.html に書き出す
|
|
49
|
+
bunx -p issue-map@latest issue-map-build out.html
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
スナップショットはスナップショットです——状態は古くなります。いまの状態を見たいときは上の server
|
|
53
|
+
を使ってください。
|
|
54
|
+
|
|
55
|
+
## 言語
|
|
56
|
+
|
|
57
|
+
右上で切り替えます。**既定は英語**で、ほかに繁体字中国語・簡体字中国語・日本語に対応しています。
|
|
58
|
+
選んだ言語はブラウザ(localStorage の `issue-map:locale`)に記憶され、repo ごとではありません
|
|
59
|
+
——言語は読む人の好みであって、プロジェクトの設定ではないからです。`navigator.language` は意図的に
|
|
60
|
+
見ていません:既定が英語である以上、推測を外すと毎回戻す手間だけが増えます。
|
|
61
|
+
|
|
62
|
+
文言はすべて `scripts/issue-map-i18n.ts` にあり、ページ上のあらゆる一文はそこが唯一の出どころです:
|
|
63
|
+
|
|
64
|
+
- `EN` が原稿であり、キーの定義場所でもあります。3 つの翻訳の型はそこから導かれるので、キーを 1 つ
|
|
65
|
+
訳し忘れると `bun run typecheck` が赤くなります。
|
|
66
|
+
- 文中の差し込み名(`{n}`、`{issues}`)も型の一部で、1 つ渡し忘れるとコンパイルが通りません。
|
|
67
|
+
そうでなければ、欠けたものが `{n}` のまま画面に出てしまい、そのマスに到達して初めて気づくことに
|
|
68
|
+
なります。
|
|
69
|
+
- 英語で単複を分けるキーは `{ one, other }` で書き、中国語と日本語は 1 本の文字列で構いません
|
|
70
|
+
(`Intl.PluralRules` はこれらの言語では `other` しか返しません)。
|
|
71
|
+
|
|
72
|
+
モデルと取得の側は**もう文を組み立てません**:`nextStep` は
|
|
73
|
+
`{ kind: 'waitChildren', count: 2 }` のような構造化された値で、グループ名も同じです。言葉になるのは
|
|
74
|
+
i18n の層です。スナップショットに文を保存していたら、言語を切り替えるたびに GitHub を取り直す
|
|
75
|
+
ことになります。
|
|
76
|
+
|
|
77
|
+
CLI 側(生成メッセージ、エラー)は英語のみで、ページの言語設定には従いません。あれは
|
|
78
|
+
`bunx issue-map` の出力であり、コマンドを打った人が読むもので、ページの一部ではありません。
|
|
79
|
+
例外は `scripts/mutate.ts` で、この repo 内部のツールなので中国語のままです。
|
|
80
|
+
|
|
81
|
+
## 前提条件
|
|
82
|
+
|
|
83
|
+
- **Bun**。`Bun.build`、`Bun.serve`、`Bun.file` と bun の `spawnSync` を使っているので、Node では
|
|
84
|
+
動きません。
|
|
85
|
+
- **`gh` CLI にログイン済み**で、対象 repo への読み取り権限があること。
|
|
86
|
+
- 対象 repo に GitHub を指す git remote があること。
|
|
87
|
+
- runtime 依存はなし。devDependencies は型と lint/format のツールだけです。
|
|
88
|
+
|
|
89
|
+
## 設定
|
|
90
|
+
|
|
91
|
+
すべて既定値があり、何も設定しなくても動きます。既定値は `scripts/issue-map.ts` の `CONFIG` に
|
|
92
|
+
あります。
|
|
93
|
+
|
|
94
|
+
| 環境変数 | 既定 | 意味 |
|
|
95
|
+
| -------------------------- | --------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
96
|
+
| `GH_REPO` | cwd の git から推測 | 別の repo を描くときに設定(`gh` 自身の変数。fork や複数 remote の判断も任せます) |
|
|
97
|
+
| `ISSUE_MAP_PARENT_HEADING` | `Parent` | サブチケットが本文で親を指す見出し。GitHub ネイティブの sub-issue があればそちらを優先。クローズ済みの兄弟が見えるのはネイティブの場合だけ(下記) |
|
|
98
|
+
| `ISSUE_MAP_LABELS_UNREADY` | `needs-triage,needs-info` | 付いていれば未評価で、まだ誰にも渡せない |
|
|
99
|
+
| `ISSUE_MAP_LABELS_READY` | `ready-for-agent,ready-for-human` | 付いて初めて評価済み・着手可とみなす |
|
|
100
|
+
| `ISSUE_MAP_LABELS_ACTIVE` | `in-progress` | 付いていれば誰かが対応中(assignee がなくてもよい) |
|
|
101
|
+
| `ISSUE_MAP_LABELS_HUMAN` | `ready-for-human` | 人がやるもの。次の一手に実装コマンドを出さない |
|
|
102
|
+
| `ISSUE_MAP_CMD_IMPLEMENT` | `/implement` | 着手できるときにマップが促すコマンド |
|
|
103
|
+
| `ISSUE_MAP_CMD_TRIAGE` | `/triage` | まだ評価が要るときにマップが促すコマンド |
|
|
104
|
+
| `ISSUE_MAP_PORT` | `4747` | server のポート |
|
|
105
|
+
| `ISSUE_MAP_OPEN` | 有効 | `0` にするとブラウザを開かない(`bun --watch` の開発モードは既定で無効) |
|
|
106
|
+
|
|
107
|
+
とくに考えておくべきものが 3 つ:
|
|
108
|
+
|
|
109
|
+
- **ラベルの語彙**:対象 repo がこのセットを使っていないなら、その repo の名前に置き換えてください。
|
|
110
|
+
コード側で検知します——スナップショットに ready/unready のどのラベルも現れないときは triage を
|
|
111
|
+
ゲートにしません。そうしないと全チケットが「未評価」になってしまいます。
|
|
112
|
+
- **コマンド名**:`/implement` と `/triage` は Claude Code の skill です。対象 repo にそれがない
|
|
113
|
+
場合は必ず差し替えてください。さもないと、存在しないものを走らせろとマップが指示します。
|
|
114
|
+
- **クローズ済みの兄弟にはネイティブ sub-issue が要る**:マップが GitHub に問い合わせるクローズ済み
|
|
115
|
+
チケットは、open なチケットがまだ指しているものだけです——そのブロッカー、その親、そして親の
|
|
116
|
+
子チケット。子チケットは GitHub ネイティブの sub-issue 関係から取るので、`## Parent` の本文慣例を
|
|
117
|
+
使う repo ではグループ内の**クローズ済み**の子が出てこず、進捗が実際より少なく見えます。open な
|
|
118
|
+
チケットは影響を受けません。代わりに repo のクローズ済みチケットを全部走査する手もありますが、
|
|
119
|
+
古い repo ではわずか数枚のために数十回のリクエストを払うことになります。ネイティブ sub-issue より
|
|
120
|
+
前に作られたグループなら、子チケットを一度リンクし直す(チケットの Sub-issues)だけでクローズ済みの
|
|
121
|
+
子が戻ります。本文の慣例は残しておいて構いません——ネイティブのほうが優先されます。
|
|
122
|
+
|
|
123
|
+
## よくある失敗
|
|
124
|
+
|
|
125
|
+
- `gh api graphql failed: …` — `gh` にログインしていないか、cwd が対象 repo の git ツリーの中に
|
|
126
|
+
ありません。
|
|
127
|
+
|
|
128
|
+
## ファイル
|
|
129
|
+
|
|
130
|
+
| ファイル | 責務 |
|
|
131
|
+
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
|
|
132
|
+
| `scripts/issue-map.ts` | スナップショット取得、各チケットの状態と次の一手の導出、HTML の生成。移植時に触る設定は中の `CONFIG` |
|
|
133
|
+
| `scripts/issue-map-model.ts` | 純粋なデータモデル:グルーピング、クリティカルパス。両側で共用 |
|
|
134
|
+
| `scripts/issue-map-i18n.ts` | 4 言語の文言と引き当て。ページ上のあらゆる一文の唯一の出どころ |
|
|
135
|
+
| `scripts/issue-map-page.ts` | ブラウザ側のコード。ビルド時に HTML へバンドルされる |
|
|
136
|
+
| `scripts/issue-map.html` | テンプレート。2 つのプレースホルダ(`issue-map-data`、`issue-map-code`)が埋められる |
|
|
137
|
+
| `scripts/issue-map-serve.ts` | ローカル server。リクエストごとに取り直す |
|
|
138
|
+
| `scripts/mutate.ts` | ミューテーションテスト:1 行を壊してテストが赤くなるか見る。ガードテストの逆検証はこれで行い、手でファイルを書き換えない |
|
|
139
|
+
|
|
140
|
+
## この repo で開発する
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
bun install
|
|
144
|
+
bun run issue-map:serve # --watch。コードを変えると自動で再起動。ブラウザは意図的に開かない(保存のたびにタブが増えるため)
|
|
145
|
+
bun run issue-map # dist/issue-map.html を生成するだけ
|
|
146
|
+
bun run check # lint + format:check + typecheck
|
|
147
|
+
bun test # 純粋なモデル層(グルーピング、クリティカルパス、レイアウト)
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
この repo 自体にはまだ issue がないので、`GH_REPO=<owner>/<repo>` でチケットのある repo を指さない
|
|
151
|
+
と何も描けません。
|
|
152
|
+
|
|
153
|
+
`tests/` が守るのは「マップが嘘をつく」「読めなくなる」ことだけで、見た目(色・形・間隔)は意図的に
|
|
154
|
+
検証しません。ガードテストを足すときは逆検証で——そのテストが防ぐと主張する欠陥を製品コードに
|
|
155
|
+
戻し、赤くなることを確かめます:
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
bun run mutate scripts/issue-map-model.ts tests/issue-map-layout.test.ts
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## 変える前に知っておく 2 つの設計判断
|
|
162
|
+
|
|
163
|
+
- **このページでは状態を変えられません。** GitHub に書き戻すボタンはありません。意図的です:状態の
|
|
164
|
+
正は 1 つだけで、入口が増えれば必ず食い違います。
|
|
165
|
+
- **チケット名はマップに載せません。** ノードに載るのは番号だけで、名前は下の一覧にあります。本文に
|
|
166
|
+
短縮名の節を置く案は試しましたが、それはタイトルの 2 つ目の正であり、タイトルを直しても追随しま
|
|
167
|
+
せん。機械的に短縮したものは読めませんでした。
|
package/README.md
CHANGED
|
@@ -1,96 +1,165 @@
|
|
|
1
1
|
# issue-map
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**English** · [繁體中文](README.zh-TW.md) · [简体中文](README.zh-CN.md) · [日本語](README.ja.md)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Draws the blocking relationships between your GitHub Issues as a one-page dev map: **which issues
|
|
6
|
+
can be picked up now, which are waiting on what, and where the critical path runs.**
|
|
6
7
|
|
|
7
|
-
|
|
8
|
+
GitHub Issues stays the source of truth. This page is only a snapshot — you cannot change status on
|
|
9
|
+
it, so no second source of truth grows out of it.
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
## Why this exists
|
|
10
12
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
It started with [mattpocock/skills](https://github.com/mattpocock/skills). Once the team adopted
|
|
14
|
+
that way of working — write the workflow as a skill, let the agent run it — filing an issue got
|
|
15
|
+
cheap: think of something, open a ticket, hand it to a skill. So the issue count grew fast. That is
|
|
16
|
+
the process working, not a problem.
|
|
14
17
|
|
|
15
|
-
|
|
18
|
+
The problem is the next step. An agent takes one issue per round, so what you actually decide each
|
|
19
|
+
round is **which one**. That answer is in no single issue; it lives between them: who blocks whom,
|
|
20
|
+
how many sub-issues a parent is still waiting on, how long the longest chain is. GitHub Issues only
|
|
21
|
+
ever shows you one issue at a time, so assembling that picture means opening them one by one — and
|
|
22
|
+
doing it again tomorrow.
|
|
16
23
|
|
|
17
|
-
|
|
24
|
+
This page is that picture.
|
|
18
25
|
|
|
19
|
-
|
|
26
|
+
## Usage
|
|
20
27
|
|
|
21
|
-
|
|
28
|
+
Run it inside **the repo you want to look at**:
|
|
22
29
|
|
|
23
30
|
```bash
|
|
24
|
-
bunx
|
|
25
|
-
bunx -p issue-map@latest issue-map-build out.html
|
|
31
|
+
bunx issue-map@latest
|
|
26
32
|
```
|
|
27
33
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
## 前置條件
|
|
31
|
-
|
|
32
|
-
- **Bun**。這幾支用了 `Bun.build`、`Bun.serve`、`Bun.file` 與 bun 的 `spawnSync`,Node 跑不起來。
|
|
33
|
-
- **`gh` CLI 已登入**,而且對目標 repo 有讀取權。
|
|
34
|
-
- 目標 repo 有 git remote 指向 GitHub。
|
|
35
|
-
- 沒有 runtime 依賴;devDependencies 只有型別與 lint/format 工具。
|
|
36
|
-
|
|
37
|
-
## 設定
|
|
34
|
+
`@latest` takes the newest version on npm; pin one with `bunx issue-map@0.2.0`.
|
|
38
35
|
|
|
39
|
-
|
|
36
|
+
It serves `http://localhost:4747` **and opens your browser**. Every refresh re-fetches from GitHub,
|
|
37
|
+
so what you see is the current state. The repo is what `gh` infers from the git remote in your cwd —
|
|
38
|
+
nothing to fill in.
|
|
40
39
|
|
|
41
|
-
|
|
42
|
-
| -------------------------- | --------------------------------- | ------------------------------------------------------------------ |
|
|
43
|
-
| `GH_REPO` | 從 cwd 的 git 推斷 | 要畫別的 repo 時設它(`gh` 自己的變數,fork 與多 remote 也交給它) |
|
|
44
|
-
| `ISSUE_MAP_PARENT_HEADING` | `Parent` | 子票在內文指向母票的段落標題。GitHub 原生 sub-issue 有值時優先 |
|
|
45
|
-
| `ISSUE_MAP_LABELS_UNREADY` | `needs-triage,needs-info` | 掛了就是還沒評估完,不能交給誰做 |
|
|
46
|
-
| `ISSUE_MAP_LABELS_READY` | `ready-for-agent,ready-for-human` | 掛了才算評估完、可以動工 |
|
|
47
|
-
| `ISSUE_MAP_LABELS_ACTIVE` | `in-progress` | 掛了代表有人在做,不必有 assignee |
|
|
48
|
-
| `ISSUE_MAP_LABELS_HUMAN` | `ready-for-human` | 這些要人做,下一步不寫實作指令 |
|
|
49
|
-
| `ISSUE_MAP_CMD_IMPLEMENT` | `/implement` | 可以動工時圖上叫人跑的指令 |
|
|
50
|
-
| `ISSUE_MAP_CMD_TRIAGE` | `/triage` | 還要評估時圖上叫人跑的指令 |
|
|
51
|
-
| `ISSUE_MAP_PORT` | `4747` | server 的 port |
|
|
52
|
-
| `ISSUE_MAP_OPEN` | 開 | 設 `0` 就不自動開瀏覽器(`bun --watch` 的開發模式預設關掉) |
|
|
40
|
+
Set `ISSUE_MAP_OPEN=0` if you don't want the tab.
|
|
53
41
|
|
|
54
|
-
|
|
42
|
+
If all you want is a static HTML file (`-p` is what picks the other bin; without it you get the
|
|
43
|
+
server):
|
|
55
44
|
|
|
56
|
-
|
|
57
|
-
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
- `gh api graphql 失敗:…` — `gh` 沒登入,或 cwd 不在目標 repo 的 git 樹裡。
|
|
62
|
-
- `open issue 超過 100 張,這支要改成分頁抓` — GraphQL 的 `first` 上限就是 100。要支援更多票得在 `issue-map.ts` 的 `query()` 加分頁;這是要改程式,不是設定。
|
|
63
|
-
|
|
64
|
-
## 檔案
|
|
65
|
-
|
|
66
|
-
| 檔案 | 責任 |
|
|
67
|
-
| ---------------------------- | ---------------------------------------------------------------------- |
|
|
68
|
-
| `scripts/issue-map.ts` | 抓快照、算每張票的狀態與下一步、產出 HTML。移植設定在裡面的 `CONFIG` |
|
|
69
|
-
| `scripts/issue-map-model.ts` | 純資料模型:分組、關鍵路徑。前後端共用 |
|
|
70
|
-
| `scripts/issue-map-page.ts` | 瀏覽器端程式碼,建置時被打包進 HTML |
|
|
71
|
-
| `scripts/issue-map.html` | 樣板。兩個佔位區塊(`issue-map-data`、`issue-map-code`)會被填入 |
|
|
72
|
-
| `scripts/issue-map-serve.ts` | 本機 server,每個請求重抓一次 |
|
|
73
|
-
| `scripts/mutate.ts` | 突變測試:改壞一行看測試會不會紅。守門測試的反向驗證用它,不要手改檔案 |
|
|
45
|
+
```bash
|
|
46
|
+
bunx -p issue-map@latest issue-map-build # writes dist/issue-map.html
|
|
47
|
+
bunx -p issue-map@latest issue-map-build out.html
|
|
48
|
+
```
|
|
74
49
|
|
|
75
|
-
|
|
50
|
+
A snapshot is a snapshot — it goes stale. Use the server above when you need the current state.
|
|
51
|
+
|
|
52
|
+
## Language
|
|
53
|
+
|
|
54
|
+
Switch in the top right. **English is the default**; Traditional Chinese, Simplified Chinese and
|
|
55
|
+
Japanese are also supported. The choice is remembered in the browser (`issue-map:locale` in
|
|
56
|
+
localStorage) and is not tied to a repo — language is the preference of whoever is reading, not a
|
|
57
|
+
setting of the project. `navigator.language` is deliberately ignored: English is the default, and
|
|
58
|
+
guessing wrong just means changing it back on every visit.
|
|
59
|
+
|
|
60
|
+
Every string lives in `scripts/issue-map-i18n.ts`, the single source for every sentence on the page:
|
|
61
|
+
|
|
62
|
+
- `EN` is the original, and the place the keys are defined. The three translations are typed from
|
|
63
|
+
it, so missing a key turns `bun run typecheck` red.
|
|
64
|
+
- The placeholder names inside a sentence (`{n}`, `{issues}`) are part of the type too — miss one
|
|
65
|
+
and it will not compile. Otherwise the missing one prints as a literal `{n}` on the page, and
|
|
66
|
+
you would only find it by reaching that exact cell.
|
|
67
|
+
- Keys that need English plurals are written `{ one, other }`; Chinese and Japanese take a single
|
|
68
|
+
string (`Intl.PluralRules` only has `other` for those languages).
|
|
69
|
+
|
|
70
|
+
The model and fetch side **no longer builds sentences**: `nextStep` is a structured value such as
|
|
71
|
+
`{ kind: 'waitChildren', count: 2 }`, and so are group names. The words are assembled in the i18n
|
|
72
|
+
layer. If the snapshot stored sentences, switching language would mean re-fetching from GitHub.
|
|
73
|
+
|
|
74
|
+
The CLI side (build messages, errors) is English only and does not follow the page's language
|
|
75
|
+
setting: it is the output of `bunx issue-map`, read by whoever ran the command, not part of the
|
|
76
|
+
page. `scripts/mutate.ts` is the exception — it is an in-repo tool, so it stays Chinese.
|
|
77
|
+
|
|
78
|
+
## Requirements
|
|
79
|
+
|
|
80
|
+
- **Bun.** These scripts use `Bun.build`, `Bun.serve`, `Bun.file` and bun's `spawnSync`; Node will
|
|
81
|
+
not run them.
|
|
82
|
+
- **`gh` CLI, logged in**, with read access to the target repo.
|
|
83
|
+
- The target repo has a git remote pointing at GitHub.
|
|
84
|
+
- No runtime dependencies; devDependencies are only types and lint/format tools.
|
|
85
|
+
|
|
86
|
+
## Configuration
|
|
87
|
+
|
|
88
|
+
Everything has a default — it runs with nothing set. The defaults live in `CONFIG` in
|
|
89
|
+
`scripts/issue-map.ts`.
|
|
90
|
+
|
|
91
|
+
| Environment variable | Default | Meaning |
|
|
92
|
+
| -------------------------- | --------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
93
|
+
| `GH_REPO` | inferred from the git in cwd | Set it to map another repo (`gh`'s own variable; forks and multiple remotes are its job too) |
|
|
94
|
+
| `ISSUE_MAP_PARENT_HEADING` | `Parent` | The body heading under which a sub-issue points at its parent. A native GitHub sub-issue relation wins when present, and only native ones show _closed_ siblings (see below) |
|
|
95
|
+
| `ISSUE_MAP_LABELS_UNREADY` | `needs-triage,needs-info` | Carrying one means it is not assessed yet and cannot be handed to anyone |
|
|
96
|
+
| `ISSUE_MAP_LABELS_READY` | `ready-for-agent,ready-for-human` | Only with one of these does an issue count as assessed and ready to work on |
|
|
97
|
+
| `ISSUE_MAP_LABELS_ACTIVE` | `in-progress` | Carrying one means somebody is on it, with or without an assignee |
|
|
98
|
+
| `ISSUE_MAP_LABELS_HUMAN` | `ready-for-human` | These need a person, so the next step is not an implementation command |
|
|
99
|
+
| `ISSUE_MAP_CMD_IMPLEMENT` | `/implement` | The command the map tells you to run when an issue is ready |
|
|
100
|
+
| `ISSUE_MAP_CMD_TRIAGE` | `/triage` | The command the map tells you to run when it still needs assessing |
|
|
101
|
+
| `ISSUE_MAP_PORT` | `4747` | Port for the server |
|
|
102
|
+
| `ISSUE_MAP_OPEN` | on | Set `0` to stop opening the browser (the `bun --watch` dev mode has it off) |
|
|
103
|
+
|
|
104
|
+
Three worth thinking through:
|
|
105
|
+
|
|
106
|
+
- **Label vocabulary**: if the target repo does not use this set, replace them with its own names.
|
|
107
|
+
The code detects it — when the snapshot contains none of the ready/unready labels at all, triage
|
|
108
|
+
is not used as a gate; otherwise every issue would come out as "needs triage".
|
|
109
|
+
- **Command names**: `/implement` and `/triage` are Claude Code skills. If the target repo has no
|
|
110
|
+
such skills you must change them, or the map will tell people to run something that does not
|
|
111
|
+
exist.
|
|
112
|
+
- **Closed siblings need native sub-issues**: the map asks GitHub only for the closed issues an
|
|
113
|
+
open one still points at — its blockers, its parent, and that parent's children. Children come
|
|
114
|
+
from GitHub's native sub-issue relation, so with the `## Parent` body convention a group's
|
|
115
|
+
_closed_ children never appear and its progress looks smaller than it is. Open issues are not
|
|
116
|
+
affected. The alternative is scanning every closed issue in the repo, which on an old repo means
|
|
117
|
+
dozens of requests to find a handful of issues. If a group predates native sub-issues, linking
|
|
118
|
+
its children once (the issue's Sub-issues panel) brings the closed ones back; the body convention
|
|
119
|
+
can stay, the native relation wins anyway.
|
|
120
|
+
|
|
121
|
+
## When it fails
|
|
122
|
+
|
|
123
|
+
- `gh api graphql failed: …` — `gh` is not logged in, or your cwd is not inside the target repo's
|
|
124
|
+
git tree.
|
|
125
|
+
|
|
126
|
+
## Files
|
|
127
|
+
|
|
128
|
+
| File | Responsibility |
|
|
129
|
+
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
130
|
+
| `scripts/issue-map.ts` | Takes the snapshot, derives each issue's status and next step, produces the HTML. Porting knobs live in its `CONFIG` |
|
|
131
|
+
| `scripts/issue-map-model.ts` | Pure data model: grouping, critical path. Shared by both sides |
|
|
132
|
+
| `scripts/issue-map-i18n.ts` | Strings for the four languages, plus the lookup. Single source for every sentence on the page |
|
|
133
|
+
| `scripts/issue-map-page.ts` | Browser-side code, bundled into the HTML at build time |
|
|
134
|
+
| `scripts/issue-map.html` | The template. Two placeholder blocks (`issue-map-data`, `issue-map-code`) get filled in |
|
|
135
|
+
| `scripts/issue-map-serve.ts` | Local server, re-fetches on every request |
|
|
136
|
+
| `scripts/mutate.ts` | Mutation testing: break one line and see whether a test goes red. Use it to verify guard tests in reverse instead of editing files by hand |
|
|
137
|
+
|
|
138
|
+
## Developing in this repo
|
|
76
139
|
|
|
77
140
|
```bash
|
|
78
141
|
bun install
|
|
79
|
-
bun run issue-map:serve # --watch
|
|
80
|
-
bun run issue-map #
|
|
142
|
+
bun run issue-map:serve # --watch, restarts on change; deliberately does not open a tab (you would get one per save)
|
|
143
|
+
bun run issue-map # build the file only, to dist/issue-map.html
|
|
81
144
|
bun run check # lint + format:check + typecheck
|
|
82
|
-
bun test #
|
|
145
|
+
bun test # the pure model layer (grouping, critical path, layout)
|
|
83
146
|
```
|
|
84
147
|
|
|
85
|
-
|
|
148
|
+
This repo has no issues of its own yet, so point `GH_REPO=<owner>/<repo>` at one that has tickets to
|
|
149
|
+
get anything drawn.
|
|
86
150
|
|
|
87
|
-
`tests/`
|
|
151
|
+
`tests/` only guards things that would make the map lie or make it unreadable; looks (colour, shape,
|
|
152
|
+
spacing) are deliberately not asserted. A new guard test has to be verified in reverse — put the
|
|
153
|
+
defect it claims to catch back into the product code and confirm it goes red:
|
|
88
154
|
|
|
89
155
|
```bash
|
|
90
156
|
bun run mutate scripts/issue-map-model.ts tests/issue-map-layout.test.ts
|
|
91
157
|
```
|
|
92
158
|
|
|
93
|
-
##
|
|
159
|
+
## Two design decisions, know them before you change things
|
|
94
160
|
|
|
95
|
-
-
|
|
96
|
-
|
|
161
|
+
- **This page cannot change status.** No button writes back to GitHub. That is deliberate: status
|
|
162
|
+
has exactly one source of truth, and a second entry point makes them disagree.
|
|
163
|
+
- **Issue titles stay off the map.** Nodes carry only the number; names are in the list below. A
|
|
164
|
+
short-name section in the body was tried, and it is a second source of truth for the title —
|
|
165
|
+
editing the title does not update it; shortening titles mechanically does not read.
|
package/README.zh-CN.md
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# issue-map
|
|
2
|
+
|
|
3
|
+
[English](README.md) · [繁體中文](README.zh-TW.md) · **简体中文** · [日本語](README.ja.md)
|
|
4
|
+
|
|
5
|
+
> 原稿是[英文版](README.md)。翻译落后时以它为准。
|
|
6
|
+
|
|
7
|
+
把 GitHub Issues 的阻挡关系画成一页开发地图:**哪几张票现在可以动、哪几张在等谁、关键路径是哪一条。**
|
|
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
|
+
```bash
|
|
28
|
+
bunx issue-map@latest
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
`@latest` 取 npm 上最新的一版;要钉住特定版本就写 `bunx issue-map@0.2.0`。
|
|
32
|
+
|
|
33
|
+
起在 `http://localhost:4747` **并直接开浏览器**。每次刷新都重抓 GitHub,看到的一定是现在的状态。repo 是 `gh` 从 cwd 的 git 推断的,不必填。
|
|
34
|
+
|
|
35
|
+
不要自动开标签页就设 `ISSUE_MAP_OPEN=0`。
|
|
36
|
+
|
|
37
|
+
只要一份静态 HTML 的话(`-p` 是用来选另一个 bin 的,少了它会变成起 server):
|
|
38
|
+
|
|
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。
|
|
45
|
+
|
|
46
|
+
## 语言
|
|
47
|
+
|
|
48
|
+
页面右上角切换,**默认英文**,另外支持繁体中文、简体中文、日文。选了哪一种记在浏览器
|
|
49
|
+
(localStorage 的 `issue-map:locale`),跟 repo 无关——语言是看的人的偏好,不是某个项目的设置。
|
|
50
|
+
刻意不看 `navigator.language`:默认就是英文,猜错了反而每次进来都要改回去。
|
|
51
|
+
|
|
52
|
+
文案全部在 `scripts/issue-map-i18n.ts`,那是页面上每一句话的唯一来源:
|
|
53
|
+
|
|
54
|
+
- `EN` 是原稿,也是键的定义处。三份翻译的类型由它推导,少翻一个键 `bun run typecheck` 就会红。
|
|
55
|
+
- 句子里的代入名(`{n}`、`{issues}`)也是类型的一部分,少传一个编不过——不然缺的那个会以
|
|
56
|
+
`{n}` 的样子印在画面上,而那要真的跑到那一格才看得到。
|
|
57
|
+
- 英文要分单复数的键写成 `{ one, other }`,中日文写一句字符串就好(`Intl.PluralRules` 对这几种
|
|
58
|
+
语言只有 `other`)。
|
|
59
|
+
|
|
60
|
+
模型与抓取那一侧**不再算好句子**:`nextStep` 是 `{ kind: 'waitChildren', count: 2 }` 这种结构化
|
|
61
|
+
的值,分组名字也一样,话在 i18n 那一层才组出来。快照里存中文句子的话,换一次语言就得重抓一次
|
|
62
|
+
GitHub。
|
|
63
|
+
|
|
64
|
+
CLI 那一侧(产文件消息、错误)只有英文,也不跟着页面的语言走:那是 `bunx issue-map` 的输出,给下指令的人看的,不是页面的一部分。例外是 `scripts/mutate.ts`——它是这个 repo 内部的工具,留中文。
|
|
65
|
+
|
|
66
|
+
## 前置条件
|
|
67
|
+
|
|
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 工具。
|
|
72
|
+
|
|
73
|
+
## 配置
|
|
74
|
+
|
|
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` 的开发模式默认关掉) |
|
|
89
|
+
|
|
90
|
+
三个要特别想过的:
|
|
91
|
+
|
|
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)就会回来;正文惯例可以留着,原生的本来就优先。
|
|
95
|
+
|
|
96
|
+
## 常见失败
|
|
97
|
+
|
|
98
|
+
- `gh api graphql failed: …` — `gh` 没登录,或 cwd 不在目标 repo 的 git 树里。
|
|
99
|
+
|
|
100
|
+
## 文件
|
|
101
|
+
|
|
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` | 变异测试:改坏一行看测试会不会红。守门测试的反向验证用它,不要手改文件 |
|
|
111
|
+
|
|
112
|
+
## 在这个 repo 里开发
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
bun install
|
|
116
|
+
bun run issue-map:serve # --watch,改代码会自动重启;刻意不自动开浏览器(每存一次档就会多一个标签页)
|
|
117
|
+
bun run issue-map # 只产文件到 dist/issue-map.html
|
|
118
|
+
bun run check # lint + format:check + typecheck
|
|
119
|
+
bun test # 纯模型那一层(分组、关键路径、排版)
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
这个 repo 自己还没有 issue,`GH_REPO=<owner>/<repo>` 指到有票的 repo 才画得出东西。
|
|
123
|
+
|
|
124
|
+
`tests/` 只守会让地图说谎或不能看的事,外观(颜色、形状、间距)刻意不验。新增守门测试要走反向验证——把它宣称要挡的缺陷放回产品代码,确认它会红:
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
bun run mutate scripts/issue-map-model.ts tests/issue-map-layout.test.ts
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## 两个设计上的决定,改之前先知道
|
|
131
|
+
|
|
132
|
+
- **这一页不能改状态。** 没有按钮会回写 GitHub。刻意的:状态只有一个事实来源,多一个入口就会不一致。
|
|
133
|
+
- **票名不进地图。** 节点只挂票号,名字在下方清单。试过在正文加短名段落,那是票名的第二个事实来源,改标题不会改它;机械缩短标题读不通。
|