| name | sayit-release |
| description | SayIt 發版流程編排 — 起草 CHANGELOG、同步 5 語系升級彈窗、對齊 upgradeNoticeItemCount、最後呼叫 ./scripts/release.sh。當使用者說「準備發新版」「要 release vX.Y.Z」「準備 release v0.10.0」「要發版了」「更新 CHANGELOG」「要更新升級彈窗」「同步多語系升級提示」之類的話時必須觸發;即使對方沒講「sayit-release」這幾個字、只說「我們來發 v0.11.0」也要觸發。負責 release.sh 之前的所有準備工作,呼叫 release.sh 前一定要先取得使用者明確同意。 |
SayIt 發版流程
這個 skill 編排 SayIt 從「準備發新版」到「呼叫 release.sh」之間的所有準備工作。release.sh 自身負責 4 點版本號 bump、commit、tag、push;這個 skill 負責把 release.sh 需要的前置條件全部準備好,並產生使用者體感得到的 release notes(CHANGELOG)和升級彈窗(5 語系 upgradeNotice)。
為什麼分成 skill + release.sh 兩段
release.sh 的 guard 設計(working tree 乾淨、CHANGELOG 含目標版本區塊、tag 不存在、不在 detached HEAD)讓它一定能 idempotent 地完成或乾淨地失敗。skill 不繞過這些 guard、也不重做 release.sh 已經會做的事,只負責生產 release.sh 需要的「材料」。這個分工讓兩邊各自單純:skill 出錯不會誤觸 push;release.sh 改邏輯不會牽連到內容生成。
整體流程
使用者:「準備發 v0.11.0」
│
▼
① 對齊版本號參數(X.Y.Z 是什麼?建議下一版)
│
▼
② 蒐集材料(git log 上一個 tag..HEAD、git status)
│
▼
③ 起草 CHANGELOG(分類 → 寫入頂部 → 等使用者遷訂)
│
▼
④ 起草 upgradeNotice(詢問亮點 → zh-TW → 翻譯 4 語 → 同步 itemCount)
│
▼
⑤ Sanity check(5 語系 key 對齊、itemCount 對得上、CHANGELOG 含目標版本區塊)
│
▼
⑥ 詢問使用者「要跑 release.sh 嗎?」
│
│ 使用者明確同意(「跑」「部署」「go」「發吧」之類)
▼
⑦ 跑 ./scripts/release.sh X.Y.Z(只在使用者明確同意時跑)
步驟 ① 對齊版本號
在做任何事情之前先確定目標版本號 X.Y.Z。
讀取當前版本:
jq -r .version /Users/jackle/workspace/say-it/src-tauri/tauri.conf.json
如果使用者已經在指令裡明說(「發 v0.11.0」),直接用。如果沒明說,用 semver 規則推薦:
- 只有 bug fix → patch(0.10.0 → 0.10.1)
- 有新功能但不破壞相容性 → minor(0.10.0 → 0.11.0)
- 破壞相容性 → major(0.10.0 → 1.0.0)
把推薦版本號告訴使用者,等他確認或修改。版本號未確認前不要往下走。
步驟 ② 蒐集材料
兩件事並行做:
git -C /Users/jackle/workspace/say-it log "$(git -C /Users/jackle/workspace/say-it describe --tags --abbrev=0)..HEAD" --no-merges --pretty='%h %s'
git -C /Users/jackle/workspace/say-it status --short
如果 working tree 不乾淨,先告知使用者「目前有 N 個未 commit 變更,release.sh 會擋下來,要先處理」。讓他決定是先 commit 那些變更、還是先繼續 skill 流程(變更可能會被一起包進這次 release)。
步驟 ③ 起草 CHANGELOG
CHANGELOG.md 在專案根目錄,格式固定。
標題格式
## [X.Y.Z] - YYYY-MM-DD
日期用今天的日期(執行時取 date +%Y-%m-%d,不要寫死)。
子分類
只用三個分類:
| 分類 | 何時放這裡 |
|---|
### Added | 新功能、新介面、新檔案、新支援 |
### Fixed | bug fix、錯誤行為修正 |
### Improved | 效能優化、重構、開發體驗(DX)改進、CI/CD 升級 |
不用 ### Changed / ### Deprecated / ### Removed 這些 keep-a-changelog 的其他分類,SayIt 的 CHANGELOG 慣例只用上面三個。
從 commit 推斷分類
| commit prefix | 分類 |
|---|
feat: feat(*): | Added |
fix: fix(*): | Fixed |
refactor: perf: chore(ci): chore(deps): | Improved |
docs: chore: test: | 不寫進 CHANGELOG(內部變更,使用者無感) |
例外:如果 chore 的內容其實使用者有感(例如「同步多語系」「修預設值」),仍要寫進 CHANGELOG,分類取決於影響面。
條目寫法
每條 bullet 的結構:
- [簡述使用者感受到的事]:[為什麼出現問題或為什麼這樣設計],[實際做的事和取捨](#issue)
範例:
- Gemini 2.5 系列做 AI 整理時長轉錄文字被截斷的問題(#23、#34):根因是 Gemini 把 thinking tokens 計入 `maxOutputTokens` 配額,原本對所有 provider 統一給 2048 token 預算被 thinking 吃掉一部分後不夠用。改為 per-provider 預設:Gemini / OpenAI 16384、Anthropic / Groq 8192(後者模型上限 8192,給 16384 會被 API reject)
注意三件事:
- 使用者語言而非開發者語言:寫「長轉錄文字被截斷」不寫「response.choices[0].message.content 不完整」
- 解釋 why:不只說「修了 X」,要說「為什麼 X 會壞」、「為什麼選這個解法」
- 保留技術細節:API 名稱、token 數字、檔案行為、CSP 規則這些技術細節要留著(讀者裡有開發者)
寫入位置
寫在 CHANGELOG.md 的 # Changelog 標題之下,緊接著現有最新版本之前。
# Changelog
SayIt 版本更新紀錄。
## [X.Y.Z] - YYYY-MM-DD ← 寫在這裡
### Added
- ...
### Fixed
- ...
### Improved
- ...
## [上一個版本] - ... ← 已存在
起草後的檢查
寫完先把草稿展示給使用者,不要直接寫進檔案。等使用者說「OK」或「改 X」再實際 Edit 寫入。
理由:CHANGELOG 是面向使用者的文案,每個發版的人對「什麼算亮點、用什麼語氣、要不要提技術細節」都有不同直覺,先給使用者看草稿可以避免一改再改。
步驟 ④ 起草 upgradeNotice
機制背景
升級彈窗由 Dashboard 啟動時 consumeUpgradeNotice() 觸發,比對 lastSeenVersion(存在 tauri-plugin-store)和 __APP_VERSION__(build-time 從 package.json 注入)。不相等就顯示。
需要動 7 個檔案:
src/MainApp.vue:upgradeNoticeItemCount 常數(控制顯示幾個 item)
src/i18n/locales/zh-TW.json:mainView.upgradeNotice 區塊
src/i18n/locales/zh-CN.json:同上
src/i18n/locales/en.json:同上
src/i18n/locales/ja.json:同上
src/i18n/locales/ko.json:同上
內容策略
每次發版只展示 1-3 個本版最有感的亮點。亮點要從 CHANGELOG 篩選,不是把 CHANGELOG 全貼進來。判準:
- 使用者每天都會用到、能被立刻感受到 → 優先放(例:新功能、UI 改善)
- 修一個過去常被回報的痛點 → 優先放(例:常見 bug fix)
- 內部優化、CI/CD、refactor → 不放
- 超技術的根因說明 → 放但要轉成白話
每個 item 的寫法:
[亮點主題冒號]:[使用者場景 + 之前的問題 + 現在的體驗]
翻譯流程
使用者只寫 zh-TW,skill 自動翻 4 種。不要叫使用者寫 5 種。
翻譯時的 5 語系語感
| 語系 | 語感方向 | 注意 |
|---|
| zh-TW | 口語、用日常詞,如「剪貼簿」「貼上」「設定」 | 標點全形 |
| zh-CN | 簡體 + 中國大陸用語:「设置」(不是「設定」)、「粘贴」(不是「貼上」)、「连接」(不是「連線」) | 全形標點 |
| en | plain English、技術細節保留,避免 marketing 腔 | 用 em-dash — 連接補述 |
| ja | 丁寧体(です・ます調)、技術文書風 | 全形標點,専門用語保留英文 |
| ko | -합니다 体、技術用語自然 | 半形標點 + 空格 |
翻譯品質檢查清單
寫入步驟
① 詢問使用者本版 1-3 個亮點主題
② 使用者用 zh-TW 描述(一兩句話即可)
③ skill 把 zh-TW 整理成「主題冒號 + 使用者場景 + why + how」格式
④ skill 翻譯 4 語系(zh-CN / en / ja / ko)
⑤ 把整組 upgradeNotice(5 語系 × N 個 item)展示給使用者遷訂
⑥ 使用者 OK 後實際 Edit 6 個檔案:
- 5 個 .json 的 mainView.upgradeNotice 區塊
- MainApp.vue 的 upgradeNoticeItemCount
重要:itemN 處理策略
每次發版只保留新版本的 item,不要累積上一版的。理由:
- 升級彈窗的目的是讓使用者快速知道「這次升級多了什麼」,過往版本的 item 已經沒價值
- 累積會讓彈窗越來越長,最終沒人讀
- 保留舊 i18n key(item3, item4...)會讓 grep / refactor 出現假陽性
所以 Edit 時:
- 新版有 N 個 item → 5 個 .json 都只留
title + item1..itemN + dismiss
- 舊版的
item3..item10 整批刪掉
MainApp.vue 的 upgradeNoticeItemCount 改成 N
步驟 ⑤ Sanity check
實際呼叫 release.sh 之前確認三件事,不對就回頭修:
rg -n '"upgradeNotice"' /Users/jackle/workspace/say-it/src/i18n/locales/ -A $((N+2))
rg -n 'upgradeNoticeItemCount = ' /Users/jackle/workspace/say-it/src/MainApp.vue
rg -n "^## \[X.Y.Z\]" /Users/jackle/workspace/say-it/CHANGELOG.md
任何一項對不上,回去把它修好再走步驟 ⑥。
步驟 ⑥ 取得跑 release.sh 的明確同意
不要自動跑 release.sh。用 AskUserQuestion 問使用者:
- 問題:「要不要現在跑 ./scripts/release.sh X.Y.Z?這會自動 bump 4 處版本號、commit、打 tag、push 到 remote 觸發 CI/CD(不可逆)。」
- 選項:
- 「跑 release.sh」
- 「先看一下 git diff 再決定」
- 「先別跑,我手動處理」
只有第一個選項才往下跑步驟 ⑦。
步驟 ⑦ 跑 release.sh
cd /Users/jackle/workspace/say-it && ./scripts/release.sh X.Y.Z
release.sh 可能擋下來的情況
| 訊息 | 原因 | 處理方式 |
|---|
CHANGELOG.md 缺少 vX.Y.Z 的紀錄 | 步驟 ③ 沒寫進去 | 回到步驟 ③ |
有未 commit 的變更 | 之前有殘留 | 提示使用者「skill 改的檔案還沒 commit,跑 release 之前要先 commit」並協助 git add + git commit |
tag vX.Y.Z 已存在 | 版本號用過了 | 提示使用者要不同版本號 |
目前不在 git branch 上 | detached HEAD | 提示 git switch main |
注意:skill 完成步驟 ④ 的 Edit 後,這些變更需要先 commit 才能跑 release.sh。skill 在步驟 ⑥ 應該主動建議「我已經改了 CHANGELOG.md / 5 個 i18n .json / MainApp.vue 共 7 個檔,要不要我 commit 起來?」,使用者同意後再 commit、再進步驟 ⑦。
Commit message 範例
docs: add CHANGELOG entry for vX.Y.Z
chore: update upgradeNotice for vX.Y.Z highlights
或一個合併 commit:
docs(release): prepare vX.Y.Z release notes
- CHANGELOG.md: add vX.Y.Z section
- i18n: update upgradeNotice for 5 locales
- MainApp.vue: bump upgradeNoticeItemCount to N
共通注意事項
不要動 Cargo.lock
Cargo.lock 是 release.sh 自動處理的(透過 cargo build 同步 sayit crate 版本)。skill 不要手動編輯 Cargo.lock,那是 hard-block 的保護檔案。
分支歸屬
主要發版從 main 出。如果使用者在 feature branch 上跑這個 skill,先確認意圖:
- 「PR 已 merge 進 main、我剛切回 main」→ OK
- 「我在 feature branch 上想直接發」→ 提示「release.sh 不擋這個但通常不是你想要的,CI/CD release.yml 也只認 tag 不認 branch」,讓使用者自己決定
日期一致性
CHANGELOG 標題的日期應該等於今天日期,不是亮點被開發的日期。執行時取 date +%Y-%m-%d,不要寫死字串。
跨檔案修改後的交叉驗證
修改完 7 個檔案(CHANGELOG + 5 個 .json + MainApp.vue),用步驟 ⑤ 的 sanity check 命令交叉驗證一次。CLAUDE.md 規定「同時修改多個相關文件時必須交叉驗證」,這一步是硬性的。
語音通知
每次觸發此 skill 都遵守 CLAUDE.md 的語音通知規範:開始時 say、執行中 say、完成前 say。內容反映當前任務(「我來起草 CHANGELOG」「翻譯 4 語完成」「等你決定要不要跑 release.sh」),20 字以內。