issue-map 0.2.0 → 0.3.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 ADDED
@@ -0,0 +1,163 @@
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
+ あって、ページの一部ではありません。
79
+
80
+ ## 前提条件
81
+
82
+ - **Bun**。`Bun.build`、`Bun.serve`、`Bun.file` と bun の `spawnSync` を使っているので、Node では
83
+ 動きません。
84
+ - **`gh` CLI にログイン済み**で、対象 repo への読み取り権限があること。
85
+ - 対象 repo に GitHub を指す git remote があること。
86
+ - runtime 依存はなし。devDependencies は型と lint/format のツールだけです。
87
+
88
+ ## 設定
89
+
90
+ すべて既定値があり、何も設定しなくても動きます。既定値は `scripts/issue-map.ts` の `CONFIG` に
91
+ あります。
92
+
93
+ | 環境変数 | 既定 | 意味 |
94
+ | -------------------------- | --------------------------------- | -------------------------------------------------------------------------------------- |
95
+ | `GH_REPO` | cwd の git から推測 | 別の repo を描くときに設定(`gh` 自身の変数。fork や複数 remote の判断も任せます) |
96
+ | `ISSUE_MAP_PARENT_HEADING` | `Parent` | サブチケットが本文で親を指す見出し。GitHub ネイティブの sub-issue があればそちらを優先 |
97
+ | `ISSUE_MAP_LABELS_UNREADY` | `needs-triage,needs-info` | 付いていれば未評価で、まだ誰にも渡せない |
98
+ | `ISSUE_MAP_LABELS_READY` | `ready-for-agent,ready-for-human` | 付いて初めて評価済み・着手可とみなす |
99
+ | `ISSUE_MAP_LABELS_ACTIVE` | `in-progress` | 付いていれば誰かが対応中(assignee がなくてもよい) |
100
+ | `ISSUE_MAP_LABELS_HUMAN` | `ready-for-human` | 人がやるもの。次の一手に実装コマンドを出さない |
101
+ | `ISSUE_MAP_CMD_IMPLEMENT` | `/implement` | 着手できるときにマップが促すコマンド |
102
+ | `ISSUE_MAP_CMD_TRIAGE` | `/triage` | まだ評価が要るときにマップが促すコマンド |
103
+ | `ISSUE_MAP_PORT` | `4747` | server のポート |
104
+ | `ISSUE_MAP_OPEN` | 有効 | `0` にするとブラウザを開かない(`bun --watch` の開発モードは既定で無効) |
105
+
106
+ とくに考えておくべきものが 2 つ:
107
+
108
+ - **ラベルの語彙**:対象 repo がこのセットを使っていないなら、その repo の名前に置き換えてください。
109
+ コード側で検知します——スナップショットに ready/unready のどのラベルも現れないときは triage を
110
+ ゲートにしません。そうしないと全チケットが「未評価」になってしまいます。
111
+ - **コマンド名**:`/implement` と `/triage` は Claude Code の skill です。対象 repo にそれがない
112
+ 場合は必ず差し替えてください。さもないと、存在しないものを走らせろとマップが指示します。
113
+
114
+ ## よくある失敗
115
+
116
+ - `gh api graphql 失敗:…` — `gh` にログインしていないか、cwd が対象 repo の git ツリーの中に
117
+ ありません。
118
+ - `open issue 超過 100 張,這支要改成分頁抓` — GraphQL の `first` の上限が 100 です。それ以上の
119
+ チケットに対応するには `issue-map.ts` の `query()` にページングを足す必要があります。これは設定
120
+ ではなくコードの変更です。
121
+
122
+ (CLI のメッセージは中国語なので、実際に出力される通りに引用しています。)
123
+
124
+ ## ファイル
125
+
126
+ | ファイル | 責務 |
127
+ | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
128
+ | `scripts/issue-map.ts` | スナップショット取得、各チケットの状態と次の一手の導出、HTML の生成。移植時に触る設定は中の `CONFIG` |
129
+ | `scripts/issue-map-model.ts` | 純粋なデータモデル:グルーピング、クリティカルパス。両側で共用 |
130
+ | `scripts/issue-map-i18n.ts` | 4 言語の文言と引き当て。ページ上のあらゆる一文の唯一の出どころ |
131
+ | `scripts/issue-map-page.ts` | ブラウザ側のコード。ビルド時に HTML へバンドルされる |
132
+ | `scripts/issue-map.html` | テンプレート。2 つのプレースホルダ(`issue-map-data`、`issue-map-code`)が埋められる |
133
+ | `scripts/issue-map-serve.ts` | ローカル server。リクエストごとに取り直す |
134
+ | `scripts/mutate.ts` | ミューテーションテスト:1 行を壊してテストが赤くなるか見る。ガードテストの逆検証はこれで行い、手でファイルを書き換えない |
135
+
136
+ ## この repo で開発する
137
+
138
+ ```bash
139
+ bun install
140
+ bun run issue-map:serve # --watch。コードを変えると自動で再起動。ブラウザは意図的に開かない(保存のたびにタブが増えるため)
141
+ bun run issue-map # dist/issue-map.html を生成するだけ
142
+ bun run check # lint + format:check + typecheck
143
+ bun test # 純粋なモデル層(グルーピング、クリティカルパス、レイアウト)
144
+ ```
145
+
146
+ この repo 自体にはまだ issue がないので、`GH_REPO=<owner>/<repo>` でチケットのある repo を指さない
147
+ と何も描けません。
148
+
149
+ `tests/` が守るのは「マップが嘘をつく」「読めなくなる」ことだけで、見た目(色・形・間隔)は意図的に
150
+ 検証しません。ガードテストを足すときは逆検証で——そのテストが防ぐと主張する欠陥を製品コードに
151
+ 戻し、赤くなることを確かめます:
152
+
153
+ ```bash
154
+ bun run mutate scripts/issue-map-model.ts tests/issue-map-layout.test.ts
155
+ ```
156
+
157
+ ## 変える前に知っておく 2 つの設計判断
158
+
159
+ - **このページでは状態を変えられません。** GitHub に書き戻すボタンはありません。意図的です:状態の
160
+ 正は 1 つだけで、入口が増えれば必ず食い違います。
161
+ - **チケット名はマップに載せません。** ノードに載るのは番号だけで、名前は下の一覧にあります。本文に
162
+ 短縮名の節を置く案は試しましたが、それはタイトルの 2 つ目の正であり、タイトルを直しても追随しま
163
+ せん。機械的に短縮したものは読めませんでした。
package/README.md CHANGED
@@ -1,96 +1,161 @@
1
1
  # issue-map
2
2
 
3
- GitHub Issues 的阻擋關係畫成一頁開發地圖:**哪幾張票現在可以動、哪幾張在等誰、關鍵路徑是哪一條。**
3
+ **English** · [繁體中文](README.zh-TW.md) · [简体中文](README.zh-CN.md) · [日本語](README.ja.md)
4
4
 
5
- 狀態的權威永遠是 GitHub Issues。這一頁只是快照,頁面上不能改狀態——所以不會長出第二個事實來源。
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
- 在**要看的那個 repo** 裡跑:
11
+ ## Why this exists
12
+
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.
17
+
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.
23
+
24
+ This page is that picture.
25
+
26
+ ## Usage
27
+
28
+ Run it inside **the repo you want to look at**:
10
29
 
11
30
  ```bash
12
31
  bunx issue-map@latest
13
32
  ```
14
33
 
15
- `@latest` npm 上最新的一版;要釘住特定版本就寫 `bunx issue-map@0.1.0`。
34
+ `@latest` takes the newest version on npm; pin one with `bunx issue-map@0.2.0`.
16
35
 
17
- 起在 `http://localhost:4747` **並直接開瀏覽器**。每次重新整理都重抓 GitHub,看到的一定是現在的狀態。repo `gh` cwd git 推斷的,不必填。
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.
18
39
 
19
- 不要自動開分頁就設 `ISSUE_MAP_OPEN=0`。
40
+ Set `ISSUE_MAP_OPEN=0` if you don't want the tab.
20
41
 
21
- 只要一份靜態 HTML 的話(`-p` 是用來選另一個 bin 的,少了它會變成起 server):
42
+ If all you want is a static HTML file (`-p` is what picks the other bin; without it you get the
43
+ server):
22
44
 
23
45
  ```bash
24
- bunx -p issue-map@latest issue-map-build # 寫到 dist/issue-map.html
46
+ bunx -p issue-map@latest issue-map-build # writes dist/issue-map.html
25
47
  bunx -p issue-map@latest issue-map-build out.html
26
48
  ```
27
49
 
28
- 快照就是快照——狀態會過期,要看現在的狀態就用上面的 server
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 deliberately left in Chinese: that output is for whoever
75
+ develops this tool, not part of the page.
76
+
77
+ ## Requirements
29
78
 
30
- ## 前置條件
79
+ - **Bun.** These scripts use `Bun.build`, `Bun.serve`, `Bun.file` and bun's `spawnSync`; Node will
80
+ not run them.
81
+ - **`gh` CLI, logged in**, with read access to the target repo.
82
+ - The target repo has a git remote pointing at GitHub.
83
+ - No runtime dependencies; devDependencies are only types and lint/format tools.
31
84
 
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 工具。
85
+ ## Configuration
36
86
 
37
- ## 設定
87
+ Everything has a default — it runs with nothing set. The defaults live in `CONFIG` in
88
+ `scripts/issue-map.ts`.
38
89
 
39
- 全部有預設值,一個都不設也跑得起來。預設值長在 `scripts/issue-map.ts` `CONFIG`。
90
+ | Environment variable | Default | Meaning |
91
+ | -------------------------- | --------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
92
+ | `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) |
93
+ | `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 |
94
+ | `ISSUE_MAP_LABELS_UNREADY` | `needs-triage,needs-info` | Carrying one means it is not assessed yet and cannot be handed to anyone |
95
+ | `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 |
96
+ | `ISSUE_MAP_LABELS_ACTIVE` | `in-progress` | Carrying one means somebody is on it, with or without an assignee |
97
+ | `ISSUE_MAP_LABELS_HUMAN` | `ready-for-human` | These need a person, so the next step is not an implementation command |
98
+ | `ISSUE_MAP_CMD_IMPLEMENT` | `/implement` | The command the map tells you to run when an issue is ready |
99
+ | `ISSUE_MAP_CMD_TRIAGE` | `/triage` | The command the map tells you to run when it still needs assessing |
100
+ | `ISSUE_MAP_PORT` | `4747` | Port for the server |
101
+ | `ISSUE_MAP_OPEN` | on | Set `0` to stop opening the browser (the `bun --watch` dev mode has it off) |
40
102
 
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` 的開發模式預設關掉) |
103
+ Two worth thinking through:
53
104
 
54
- 兩個要特別想過的:
105
+ - **Label vocabulary**: if the target repo does not use this set, replace them with its own names.
106
+ The code detects it — when the snapshot contains none of the ready/unready labels at all, triage
107
+ is not used as a gate; otherwise every issue would come out as "needs triage".
108
+ - **Command names**: `/implement` and `/triage` are Claude Code skills. If the target repo has no
109
+ such skills you must change them, or the map will tell people to run something that does not
110
+ exist.
55
111
 
56
- - **標籤字彙**:目標 repo 沒在用這套標籤就要換成它自己的名字。程式會偵測——快照裡完全沒出現 ready/unready 任何一個標籤時,就不拿 triage 當閘門,否則每張票都會變成「待評估」。
57
- - **指令名**:`/implement`、`/triage` 是 Claude Code 的 skill。目標 repo 沒有的話一定要換掉,不然圖上會叫人跑不存在的東西。
112
+ ## When it fails
58
113
 
59
- ## 常見失敗
114
+ - `gh api graphql 失敗:…` — `gh` is not logged in, or your cwd is not inside the target repo's git
115
+ tree.
116
+ - `open issue 超過 100 張,這支要改成分頁抓` — 100 is the hard cap on GraphQL's `first`. Supporting
117
+ more issues means adding pagination to `query()` in `issue-map.ts`; that is a code change, not
118
+ configuration.
60
119
 
61
- - `gh api graphql 失敗:…` `gh` 沒登入,或 cwd 不在目標 repo git 樹裡。
62
- - `open issue 超過 100 張,這支要改成分頁抓` — GraphQL 的 `first` 上限就是 100。要支援更多票得在 `issue-map.ts` 的 `query()` 加分頁;這是要改程式,不是設定。
120
+ (CLI messages are in Chinese, so they are quoted here as they actually appear.)
63
121
 
64
- ## 檔案
122
+ ## Files
65
123
 
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` | 突變測試:改壞一行看測試會不會紅。守門測試的反向驗證用它,不要手改檔案 |
124
+ | File | Responsibility |
125
+ | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
126
+ | `scripts/issue-map.ts` | Takes the snapshot, derives each issue's status and next step, produces the HTML. Porting knobs live in its `CONFIG` |
127
+ | `scripts/issue-map-model.ts` | Pure data model: grouping, critical path. Shared by both sides |
128
+ | `scripts/issue-map-i18n.ts` | Strings for the four languages, plus the lookup. Single source for every sentence on the page |
129
+ | `scripts/issue-map-page.ts` | Browser-side code, bundled into the HTML at build time |
130
+ | `scripts/issue-map.html` | The template. Two placeholder blocks (`issue-map-data`, `issue-map-code`) get filled in |
131
+ | `scripts/issue-map-serve.ts` | Local server, re-fetches on every request |
132
+ | `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 |
74
133
 
75
- ## 在這個 repo 裡開發
134
+ ## Developing in this repo
76
135
 
77
136
  ```bash
78
137
  bun install
79
- bun run issue-map:serve # --watch,改程式碼會自動重啟;刻意不自動開瀏覽器(每存一次檔就會多一個分頁)
80
- bun run issue-map # 只產檔到 dist/issue-map.html
138
+ bun run issue-map:serve # --watch, restarts on change; deliberately does not open a tab (you would get one per save)
139
+ bun run issue-map # build the file only, to dist/issue-map.html
81
140
  bun run check # lint + format:check + typecheck
82
- bun test # 純模型那一層(分組、關鍵路徑、排版)
141
+ bun test # the pure model layer (grouping, critical path, layout)
83
142
  ```
84
143
 
85
- 這個 repo 自己還沒有 issue,`GH_REPO=<owner>/<repo>` 指到有票的 repo 才畫得出東西。
144
+ This repo has no issues of its own yet, so point `GH_REPO=<owner>/<repo>` at one that has tickets to
145
+ get anything drawn.
86
146
 
87
- `tests/` 只守會讓地圖說謊或不能看的事,外觀(顏色、形狀、間距)刻意不驗。新增守門測試要走反向驗證——把它宣稱要擋的缺陷放回產品碼,確認它會紅:
147
+ `tests/` only guards things that would make the map lie or make it unreadable; looks (colour, shape,
148
+ spacing) are deliberately not asserted. A new guard test has to be verified in reverse — put the
149
+ defect it claims to catch back into the product code and confirm it goes red:
88
150
 
89
151
  ```bash
90
152
  bun run mutate scripts/issue-map-model.ts tests/issue-map-layout.test.ts
91
153
  ```
92
154
 
93
- ## 兩個設計上的決定,改之前先知道
155
+ ## Two design decisions, know them before you change things
94
156
 
95
- - **這一頁不能改狀態。** 沒有按鈕會回寫 GitHub。刻意的:狀態只有一個事實來源,多一個入口就會不一致。
96
- - **票名不進地圖。** 節點只掛票號,名字在下方清單。試過在內文加短名段落,那是票名的第二個事實來源,改標題不會改它;機械縮短標題讀不通。
157
+ - **This page cannot change status.** No button writes back to GitHub. That is deliberate: status
158
+ has exactly one source of truth, and a second entry point makes them disagree.
159
+ - **Issue titles stay off the map.** Nodes carry only the number; names are in the list below. A
160
+ short-name section in the body was tried, and it is a second source of truth for the title —
161
+ editing the title does not update it; shortening titles mechanically does not read.
@@ -0,0 +1,135 @@
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 那一侧(产文件消息、错误)刻意留中文:那是给开发者看的,不是页面的一部分。
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
+
95
+ ## 常见失败
96
+
97
+ - `gh api graphql 失敗:…` — `gh` 没登录,或 cwd 不在目标 repo 的 git 树里。
98
+ - `open issue 超過 100 張,這支要改成分頁抓` — GraphQL 的 `first` 上限就是 100。要支持更多票得在 `issue-map.ts` 的 `query()` 加分页;这是要改程序,不是配置。
99
+
100
+ (CLI 消息是繁体中文,所以这里照它实际印出来的样子引用。)
101
+
102
+ ## 文件
103
+
104
+ | 文件 | 职责 |
105
+ | ---------------------------- | ---------------------------------------------------------------------- |
106
+ | `scripts/issue-map.ts` | 抓快照、算每张票的状态与下一步、产出 HTML。移植配置在里面的 `CONFIG` |
107
+ | `scripts/issue-map-model.ts` | 纯数据模型:分组、关键路径。前后端共用 |
108
+ | `scripts/issue-map-i18n.ts` | 四种语言的文案与查表。页面上每一句话的唯一来源 |
109
+ | `scripts/issue-map-page.ts` | 浏览器端代码,构建时被打包进 HTML |
110
+ | `scripts/issue-map.html` | 模板。两个占位区块(`issue-map-data`、`issue-map-code`)会被填入 |
111
+ | `scripts/issue-map-serve.ts` | 本机 server,每个请求重抓一次 |
112
+ | `scripts/mutate.ts` | 变异测试:改坏一行看测试会不会红。守门测试的反向验证用它,不要手改文件 |
113
+
114
+ ## 在这个 repo 里开发
115
+
116
+ ```bash
117
+ bun install
118
+ bun run issue-map:serve # --watch,改代码会自动重启;刻意不自动开浏览器(每存一次档就会多一个标签页)
119
+ bun run issue-map # 只产文件到 dist/issue-map.html
120
+ bun run check # lint + format:check + typecheck
121
+ bun test # 纯模型那一层(分组、关键路径、排版)
122
+ ```
123
+
124
+ 这个 repo 自己还没有 issue,`GH_REPO=<owner>/<repo>` 指到有票的 repo 才画得出东西。
125
+
126
+ `tests/` 只守会让地图说谎或不能看的事,外观(颜色、形状、间距)刻意不验。新增守门测试要走反向验证——把它宣称要挡的缺陷放回产品代码,确认它会红:
127
+
128
+ ```bash
129
+ bun run mutate scripts/issue-map-model.ts tests/issue-map-layout.test.ts
130
+ ```
131
+
132
+ ## 两个设计上的决定,改之前先知道
133
+
134
+ - **这一页不能改状态。** 没有按钮会回写 GitHub。刻意的:状态只有一个事实来源,多一个入口就会不一致。
135
+ - **票名不进地图。** 节点只挂票号,名字在下方清单。试过在正文加短名段落,那是票名的第二个事实来源,改标题不会改它;机械缩短标题读不通。