Skip to content
Back to skills

Git Commit

ASecurity

並行審查模式的 Git Commit 流程:Stage → 並行三軌(使用者確認 message + Codex 審查 + code-reviewer 審查)→ Commit → Push。 觸發詞(任一命中即觸發): - 中文:「commit」、「提交」、「上版」、「推上去」、「推上板」、「推」 - 指令式:「git commit」、「git push」、「git add」、「push」 - 組合:「commit and push」、「commit 後 push」、「stage 一下」 **重要**:AI 禁止直接執行 `git commit` / `git push` / `git add` 指令。 只要使用者意圖是「要把程式碼提交或推上去」,一律透過本 skill 走完整流程。

  • 2 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added September 3, 2026
ai-agentspythonrustgoshellbashvuenodecode-reviewgit

Works with

  • claude code
  • cli

Security analysis

A100/100

Pro scans all 3 files and shows the line behind each finding

Scanned October 4, 2026

npx -y skills add abs1294/fulin-claude-plugins --skill git-commit --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Git Commit?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Git Commit
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/abs1294-git-commit/badge)](https://www.skillsdirectory.com/skills/abs1294-git-commit)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: git-commit
description: >
  並行審查模式的 Git Commit 流程:Stage → 並行三軌(使用者確認 message + Codex 審查 + code-reviewer 審查)→ Commit → Push。
  觸發詞(任一命中即觸發):
  - 中文:「commit」、「提交」、「上版」、「推上去」、「推上板」、「推」
  - 指令式:「git commit」、「git push」、「git add」、「push」
  - 組合:「commit and push」、「commit 後 push」、「stage 一下」
  **重要**:AI 禁止直接執行 `git commit` / `git push` / `git add` 指令。
  只要使用者意圖是「要把程式碼提交或推上去」,一律透過本 skill 走完整流程。
---

# Git Commit Skill — 並行審查流程

核心設計:兩軌審查(Codex+code-reviewer)前移到 commit **之前**,與使用者確認 message 三軌並行。BLOCK 只需改碼重 stage,無需 revert/reset,git 歷史永遠乾淨。

| 階段 | 動作 |
|------|------|
| Step 1 | `analyze` 分析 → `prepare` stage → **同一輪並行三軌**(1.3a 預覽+1.3b Codex+1.3c code-reviewer)→ 匯流 → `review-record` 記下兩軌結果 |
| Step 2 | `ship`(commit → push → 驗證);沒有 `review-record` 紀錄會被拒 |

步驟多時可用 TaskCreate 追蹤;流程結束前確認清單沒有殘留未關項目。

## 核心原則(默許機制)

> Commit 觸發條件:**Codex 非 BLOCK ∧ code-reviewer 非 BLOCK ∧ 使用者未明確否決。**

1. 兩軌皆 PASS(或豁免)+使用者未回覆 → **自動 commit**(默許;local)。push 另需使用者當次明確核可(如「commit 並推上去」)才帶 `--push`。預覽時必須明講此行為。
2. **任一軌 BLOCK → 永不自動 commit**,列必修項等使用者——即使使用者已先回 OK。
3. 使用者明確否決(「等等」「先別上」、改 message、調 staging)→ 照使用者意思,不 commit。
4. PASS 附清單=「可 commit 但有建議」,預設放行;使用者要先修 → 改碼 → 重跑 `prepare` → 兩軌重送(diff 變了就重判豁免)。BLOCK 後重做同樣走完整 Step 1。禁止沿用先前確認過的 message 直接 commit(碼可能已變)。
5. **某一軌確定不可用**(額度用完不算,見本條下方)→ 單軌降級(該軌記 `skipped: <原因>`、匯流視為 PASS;C 軌要先走 1.3c 第三層,第三層也失敗才能記 skipped;`review-record` 該軌就填這串),預覽明講已降為單軌,並由主 agent 補做該軌該查的項目。**兩軌都不可用 → 不可自動 commit**,停下請使用者人工確認(`review-record` 收到兩軌都 `skipped` 會直接拒絕;使用者核可後改走 `--exempt`)。
   > 「確定不可用」有嚴格判準,不是「我等不下去」——B 軌見 `references/codex-troubleshooting.md`,**自行判定不可用就 commit = 違規**。
   > **額度/用量上限不算不可用**(錯誤原文如 `You've hit your usage limit ... try again at 8:24 PM`):這是暫時狀態,**不得記 `skipped`、不得單軌降級**——`review-record` 會機械擋下理由含 usage limit/rate limit/quota/credits/try again at/額度/用量上限/配額的 skipped。處置:
   > 1. 從錯誤原文取重置時間(`try again at <時間>`);讀不到就停下問使用者,不要自己猜一個時間了事。
   > 2. **當輪就排好喚醒**,時間設在重置後約 5 分鐘:用 CronCreate(先 `ToolSearch("select:CronCreate")` 載入;`recurring: false`、釘死分/時/日/月)。它只活在本 session,Claude Code 關掉就不會觸發——要在預覽講明這一點。只寫「稍後再審」不排喚醒=空頭承諾。
   >    **載不到 CronCreate**(general-purpose subagent 實測載不到;headless 等其他環境未驗證,只要載不到就照此處理)→ **不得寫「已排」**,也不要改用別的工具硬排:把「Codex 額度用完、重置時間 <時間>、應於 HH:MM 重跑 B 軌、staged diff hash <hash>」原樣回報給呼叫端(主 agent 或使用者),由有排程工具的一方排。goal2 的 `delaylocal` **不是替代品**——它排的是 Claude 本身 5h quota 的重置時間(不是 Codex 的),而且自己最後也是呼叫 CronCreate。
   > 3. 預覽明講「Codex 額度用完,已排 HH:MM 重跑」(沒排上就寫「應於 HH:MM 重跑,喚醒未排上,需 <誰> 排或手動重跑」),commit 維持不做(這一軌沒結論,不是 PASS)。
   > 4. 喚醒後依序:最小題確認額度恢復 → 確認 index 沒被別人改動(外來 staged 閘會擋)→ staged 沒變就沿用 prepare 的 diff hash 直接重送 B 軌;staged 變了就從 prepare 重來。仍是額度錯誤就依新的重置時間再排一次。
6. **兩軌對同一項判不同嚴重度(一軌 BLOCK、一軌 Minor/PASS)→ 主 agent 自己實跑驗證再匯流**,不可取中間值、不可選寬鬆那軌放行(實證:Codex 判 BLOCK/code-reviewer 判 Minor,實測 Codex 對;「需要更好的工具才能正確處理」≠「可以不正確」)。驗證結果貼進預覽,才決定走 1 或 2。

## Review 豁免規則

`Style`(純 CSS/`<style>` 區塊/template class 調整/i18n value/格式化)與 `Docs`(純 `.md`/註解文字/XML doc 內文)可**同時豁免兩軌**(不得只豁免一軌),敏感掃描仍必做,1.3a 預覽仍要出並標示「已豁免——未回覆將自動 commit + push」。豁免也要落紀錄:`flow.sh review-record <repo> --exempt "Style:<為什麼只動到樣式>"`,否則 ship 會拒絕。

判斷原則一句話:**diff 觸及「會被執行到的程式邏輯」一律不豁免**——`.vue` 的 `<script>`、`v-if`/`@click` 綁定、i18n 的 **key**(value 不算)、任何 `.cs`/`.js`/`.ts` 邏輯行,都算。使用者標 Style/Docs 但 diff 含邏輯行 → 告知「diff 含邏輯修改,仍送審」,禁止私下豁免。

## flow.sh(必用)

`.claude/skills/git-commit/flow.sh` 包掉所有純 git 操作,**不要手動組 git 指令**:

| 指令 | 動作 |
|------|------|
| `flow.sh analyze <repo>` | 狀態分類+local-overrides 過濾+敏感字掃描 |
| `flow.sh prepare <repo> <files...>` | 逐檔 `git add` → staged diff 輸出到 `.claude/.git-commit-tmp/staged-<repo>.diff`;重跑會作廢上一輪的審查紀錄。**index 已有不在清單內的 staged 項目就拒絕**(多半是別的 session stage 的;merge 進行中不檢查)。最後印出 C 軌的專案附加審查要求(見 1.3c) |
| `flow.sh prepare <repo> --staged` | 不 `git add`,直接拿當下 index 送審(merge 收尾用:git 已把合併進來的檔案 stage 好)。index 空的拒絕 |
| `flow.sh review-record <repo> --codex "<回覆原文>" --reviewer "<回覆原文>"` | 匯流後把兩軌回覆記下並綁定當下 staged diff 的 hash。回覆第一行須為 `VERDICT: PASS`(BLOCK 不收);不可用的那軌填 `skipped: <原因>`(兩軌都 skipped 不收;C 軌 code-reviewer 只收理由寫明 general-purpose 第三層也試過且失敗的;理由含額度字樣不收——額度用完要排重跑,見核心原則 5)。專案有 `.claude/qa-gate.conf` 時,寫紀錄前先過專案閘(`--exempt` 模式同樣適用):staged 含行為類檔就必帶有效的 `--qa`;設了 `block_staged_overrides=1` 時 staged 含 local-overrides 清單上的檔就拒收(見下一列) |
| `flow.sh review-record <repo> --exempt "<理由>"` | 使用者明示豁免(Style/Docs、POC、plugin 發布、使用者要求跳過審查)。理由寫使用者的原話或豁免依據,會進稽核流水帳 `review-log.tsv` |
| `review-record ... --qa "<QA 狀態>"`(選填;找得到 `.claude/qa-gate.conf` 時視情況必帶——依序從 repo 目錄、主 repo 目錄(worktree 時)、工作目錄往上逐層找) | QA 表態,與審查結果一起寫進紀錄與流水帳。專案有 `.claude/qa-gate.conf`(與 `local-overrides.yml` 同層)時由 flow.sh 強制:staged 含行為類檔(副檔名命中 `behavior_ext`、且不命中 `exclude`)就必須帶,值以 `已QA`(「已」與「QA」間可有空白、QA 大小寫不分)或 `分流例外` 開頭——`已QA:<報告或測試檔路徑+綠的輸出行>`/`分流例外:<為何讀 code 就能確定>`;還沒 QA 就不得記錄,先派 QA 實測。沒有設定檔就不檢查 |
| `review-record ... --allow-overrides "<理由>"` | 只在 `qa-gate.conf` 設了 `block_staged_overrides=1` 時有作用:staged 含 local-overrides 清單上的檔時 review-record 會拒收(多半是誤加,用 `git restore --staged <檔案>` 移出後重跑 prepare);確實要推(例如檔內混有真改動、已用 `git apply --cached` 只 stage 真改動那幾行)才帶這個旗標,理由寫進 `review-log.tsv` 最後一欄 |
| `flow.sh audit <repo> [<range>]` | 體檢既有 commit 的 message,唯讀。抓:空 message/缺 `Type:` 前綴/Type 不在允許清單/描述超長/痕跡命中/含多行 body(軟清單命中另標「待確認」)。exit `0`=乾淨、`1`=有問題、`2`=range 無效 |
| `flow.sh ship <repo> <type> <description> [--push]` | HEREDOC commit → 驗證(內建禁 `--no-verify`/force push、過濾 AI 署名)。**預設只 local commit;帶 `--push` 才推遠端**——push 不可逆,需使用者當次明確核可 |
| `flow.sh amend <repo> --confirm-rewrite [--type <T> --desc <描述>]` | 改寫 HEAD。自動建備份分支、擋已 push 的 commit、沿用 ship 全部真閘,改寫後做 tree 級重現驗證。不帶 `--type`/`--desc` 則沿用既有 message;**只做本地改寫,不 push** |

`<repo>`=`.` 或工作目錄下的 git 子目錄名(多 repo workspace 各自獨立 commit)。**不收絕對路徑**——要操作別處的 repo 先 `cd` 過去再用 `.`。`<type>`=`Feat`/`Modify`/`Style`/`Refactor`/`Perf`/`Chore`/`Docs`/`Test`/`Fix`/`Hotfix`。

腳本不能代勞的:豁免判斷、1.3a 預覽、啟動兩個審查 subagent、匯流決策。但腳本會**檢查結果有沒有落地**:`ship`/有改到碼的 `amend` 找不到對應當下 staged diff 的 `review-record` 紀錄就拒絕,見下方「真閘 7」。

### hook:`hooks/block-bare-git-commit.sh`(他律)

PreToolUse hook,攔截 Bash 工具裡「不經 flow.sh 就建出 commit」的所有路徑:

| 攔 | 為什麼 |
|---|---|
| `git commit`(含 `--amend`) | porcelain 入口 |
| `git commit-tree` | plumbing:建 commit 物件 |
| `git update-ref` / `symbolic-ref`(寫入與刪除) / `branch -f`、`-M`、`-C` | 讓那顆 commit 生效,或把 HEAD 挪走藏掉 commit |
| `git merge --continue` | 收尾衝突 merge 時內部就是 `git commit`,不攔就是旁路(2026-09-24 補) |

後兩列是 2026-09-14 真實事故補上的:headless 引擎被 `git commit` 擋下後,改用 `write-tree` + `commit-tree` + `update-ref` 三件組完成了四個 worktree 的 merge commit,hook 靜默通過。三件組等價於 `git commit`,但字面上完全不像。

**刻意不攔**:`git merge` 本身(含無衝突時自動建的 merge commit;2026-09-24 使用者決定維持放行)、`git write-tree`(單獨用只建 tree、不動 ref,`git stash` 內部會用到)、`reset`/`rebase`/`filter-branch`/`cherry-pick`(屬 §歷史改寫 的使用者意圖層級,且日常常用,攔了頻繁誤擋)、`push`(超出本 hook 職責)。唯讀指令(`status`/`log`/`diff`/`add`)一律不攔。

放行條件**只有一條**:`GIT_COMMIT_FLOW=1`。這個變數由 `flow.sh` 執行時自己 export 給子程序,所以「真的經過 flow.sh」與「這個變數存在」是同一件事——**外部設不進來**(hook 是獨立 process,inline 前綴/`export`/`env` 全無效)。

**沒有「說明原因就能繞」的旁路**。

> **為什麼需要**:上面那句「不要手動組 git 指令」與 frontmatter 的「AI 禁止直接執行 git commit」都是**自律**,AI 會繞;繞過去就等於六道真閘一道都不觸發。skill 的「必須」是自律,只有 hook 是他律。
> hook **fail-open**:自身任何錯誤(空輸入、壞 JSON、無 python)一律放行並印警告,絕不把使用者鎖在無法 commit 的狀態。

### 真閘 7:審查紀錄(`ship`/`amend` 內建)

上面那支 hook 只保證「commit 是經 flow.sh 建的」,**不保證「經過三軌審查」**——`bash flow.sh ship ...` 這條指令裡沒有 `git commit` 字樣,hook 放行;flow.sh 內部的 commit 是子程序,hook 根本看不到。所以三軌原本完全靠自律。

`ship` 與有改到碼的 `amend` 在 commit 前會比對 `review-record` 留下的紀錄:紀錄不存在、或紀錄綁的 diff hash 與當下 staged 不同,一律 exit 1。紀錄在 commit 成功後即清,重跑 `prepare` 也會作廢——每一份 diff 都要重新記一次。**沒有旗標可以繞過**;所有「不審」的正當情境(Style/Docs 豁免、POC、plugin 發布、使用者要求跳過)都走 `review-record --exempt "<理由>"`,並留在 `.claude/.git-commit-tmp/review-log.tsv` 供事後稽核(該檔不隨 commit 清除)。

紀錄同時綁定 repo 身分(該 repo 的 git 目錄):repo 路徑轉成檔名時 `grp/sub` 與 `grp__sub` 會撞名,沒有這一層就能借用別的 repo 的紀錄。

**補推捷徑(`ship ... --push` 且沒有 staged)只認 flow.sh 親手建的那顆**:ship 成功後會記下 commit sha,補推時 HEAD 必須就是那顆。只比 message 字串的舊寫法,可以 `reset --soft` 換掉內容、沿用同一句 message 重新 commit 後直接推。只改 message 的 amend 只有在原 HEAD 本來就是 ship 建的時候才承接這筆紀錄,不能替流程外建的 commit 洗白。副作用:舊版 flow.sh 建的未推 commit 沒有這筆紀錄,補推會被擋。

紀錄內容是呼叫者自陳的:這道閘擋的是「忘了審」,擋不住蓄意填假結果。假結果會在流水帳留下可查的一行,但不會被自動發現——**`--codex`/`--reviewer` 必須貼 agent 回覆原文,不得自己組一行 `VERDICT: PASS`**。

## Step 1

### 1.1–1.2 分析與 Stage

對每個有變更的 repo 跑 `analyze`,輸出已含敏感掃描(password/secret/token/硬編 JWT/console.log/TODO: remove 等 pattern)與 local-overrides 過濾。掃描命中 → 預覽列出請使用者確認是否刻意保留;清單外的可疑 unstaged(`appsettings.Localhost.json`、`BlobStorage/`、含測試 JWT 的檔)→ 預覽提醒。

**local-overrides.yml**(`.claude/local-overrides.yml`,記錄本機常駐覆寫檔——Mock 切換、本地連線、測試 JWT):清單內檔案不告警、不 stage、不進預覽。使用者明示要 commit 清單內檔案 → 本次 override 走完整流程,commit 後問「本地預設值變了嗎?要不要移出清單?」。同一 tracked 檔連續多次未 stage → 主動建議入清單。

**Stage 紀律**:**禁止 `git add -A` / `git add .`**——會把 local-overrides 的本機 hack 整檔混進 staged。一律逐檔 `prepare`;覆寫清單內「混有真改動」的檔案(如 Program.cs 的 DI 註冊)用 `git diff` 切 hunk、`git apply --cached` 精準 stage,commit 前 grep `LocalDevToken|MockSap|MockBPM|mysecret` 確認 staged diff 0 命中。禁 `git update-index --skip-worktree`。

**多 session 共用同一個 repo 時**(index、工作區、`.claude/.git-commit-tmp/` 都是共用的):`prepare` 逐檔 `git add` 前先 `git diff <檔案>` 看工作區有沒有**別人還沒 stage 的改動**混在同一檔——有就不能整檔 add,改用 `git apply --cached` 或 `git hash-object -w` + `git update-index --cacheinfo` 只 stage 自己的部分(腳本分不出一行改動是誰寫的,這一步只能靠人看)。`prepare` 逐檔模式遇到「index 已有不在清單內的 staged 項目」會直接拒絕並列出檔名(外來 staged 閘)——那些是自己的(切 hunk stage 的)就改用 `--staged`;不是自己的就先問使用者,確認後記下那些項目的 mode 與 blob、移出 index,commit 後用同一批 blob 原樣放回並逐項比對 hash。`--staged` 模式不做這道檢查,用之前自己看過 status。碰到 `.git/index.lock` 已存在不要刪,等它消失再重試(2026-09-24 實證:另一 session 送審期間 stage 了 5 個檔,並把歷史段寫進同一份 CHANGELOG,一句 `git add` 就把對方半成品掃進 staged)。

### 1.3 並行三軌(同一輪訊息啟動)

#### 1.3a 預覽(A 軌)

Commit message 用 unicode double-line box 框住+包 fenced code block(高對比,CJK 對齊目視即可);無警示就不出警示區塊。格式:

```markdown
## 📦 {Repository}({分支})

**Staged 檔案:**
- `M` `src/path/file.vue` (+10/-5)

**⚠️ 掃描警示:**(若有才出)
- 敏感字:`<file>:<line>` 發現 `"password"`
- AI 痕跡:`<file>` 新增行引用文件出處(`§`/`CLAUDE.md`/`設計文件`)——ship 會擋,須先改掉

### 📝 建議 Commit Message

\```
╔══════════════════════════════════════════╗
║  {Type}: {描述}                           ║
╚══════════════════════════════════════════╝
\```

🚀 兩軌 Review 非 BLOCK 後將自動 commit + push;要攔截/改 message/調 staging 請在審查完成前回覆
**🔍 Codex Review:**🟡 進行中|✅ 已豁免(理由)— 未回覆將自動 commit + push
**🔍 code-reviewer:**🟡 進行中|✅ 已豁免(理由)— 未回覆將自動 commit + push
```

#### 1.3b Codex(B 軌)

`subagent_type: codex:codex-rescue`(**必須是這個完整字串**)、`run_in_background: true`,與 1.3a/1.3c 同輪送出。diff 已在 `.claude/.git-commit-tmp/staged-<repo>.diff`。

Prompt 開頭**必須**寫兩件事,缺一必死:
1. 「執行任何 codex 指令前先 `cd` 到 `<repo>` 目錄」——subagent 預設 cwd 可能不是 git repo,codex 會拒絕啟動
2. 「`codex exec` 一律加 `< /dev/null`」——否則 CLI 會卡在等一個永遠不會來的 stdin

並要求失敗時回 `VERDICT: UNAVAILABLE` 附錯誤原文,不得靜默退出。

另三條呼叫層規定(2026-09-24/25 實證,違反時 codex 根本沒跑、不是審查結論,詳見 troubleshooting):
3. 「開全新對話,禁止接續舊 thread」——**兩條路寫法不同,不可混用**:走 companion(`node <codex plugin>/scripts/codex-companion.mjs task`)才帶 `--fresh`,並禁止 `--resume-last`/`--resume`(本帳號不支援,exit 1);直接用 `codex exec` 時**不加 `--fresh`**——exec 沒有這個旗標,會回 `error: unexpected argument '--fresh' found`(exit 2),完全沒跑。exec 本來每次就是新 session,接續要另下 `codex exec resume`,一樣禁用
4. 「審查內容以 stdin 傳入(`codex exec ... < 內容檔`),禁止拆成命令列參數」——內容裡的 `-m` 之類會被 companion 當成 `--model`,回 400 `The '<旗標字樣>' model is not supported`;審查內容本身有旗標範例時,短旗標改用文字描述。以內容檔當 stdin 時就**不再加** `< /dev/null`(兩者都是指定 stdin,只能擇一;內容檔有結尾,不會卡在等 stdin),第 2 條的 `< /dev/null` 指的是「不從 stdin 傳內容」的呼叫
5. 「判定這一軌有跑,看輸出裡有沒有 `VERDICT:` 行,不看 exit code」——`codex exec ... | tail` 這類管線的 exit code 是最後一個指令的:codex 報錯 exit 2,管線末端照樣回 0,外觀像跑完了(2026-09-25 實測)。要看 codex 本身的結果用 `${PIPESTATUS[0]}`;輸出沒有 `VERDICT:` 行就是**沒有可採用的結論**(可能沒跑、中途失敗或輸出被截斷,三者都不能當審查完成),要回 `VERDICT: UNAVAILABLE` 附原文,絕不可當成 PASS 或當成「沒意見」

**等待紀律**:逾 10 分鐘告知使用者一次(不必問、不停下),之後安靜續等,至多 1 小時。完整審查本來就要 7 分鐘以上,慢的是思考不是工具。

> 🔧 **B 軌出任何狀況,先讀 `references/codex-troubleshooting.md` 再動作。**
>
> 涵蓋:只收到 idle 沒有 VERDICT/`Agent type not found`/`Not inside a trusted directory`/
> 400 `model is not supported`/卡在 `Reading additional input from stdin`/
> **以及「什麼才算判定 codex 不可用的客觀證據」**(判定前必讀——自行判定不可用就 commit = 違規)。
>
> 那份檔案裡每一條都有日期與實測數據,不要憑印象處置。

> ⚠️ **diff 一律內嵌,不要在 prompt 裡附檔案路徑**——內嵌比讓 agent 自己讀檔可靠,任何環境皆適用。
> 給了路徑它就會去讀,而讀檔是最容易出環境問題的一步。
>
> **若該環境的 codex 沙箱會擋外部 shell**(症狀:`rejected: blocked by policy`,錯誤原文含
> `powershell.exe` / `bash.exe`),讀檔會直接失敗,回 `UNAVAILABLE` 或「無法驗證」型 BLOCK——
> 外觀像 codex 掛了,實際只要改 prompt 就過,誤判代價是白跳一軌審查。
> 本機屬於這種環境(2026-09-11 首見、09-20 再踩三次,實測所有 sandbox 模式皆擋)。
>
> **大 diff 不是例外**:只內嵌需要判斷的部分(JSON 結構 diff、檔案清單、已驗證結果)即可。
> 他律=本 plugin 自帶的 PreToolUse hook `hooks/guard-codex-diff-embed.js`(prompt 含路徑或讀檔指示即擋;隨 git-commit 一起安裝,見 `hooks/hooks.json`)。同批還有 `hooks/check-codex-cwd.js` 擋 prompt 沒指定 cd 目標或目標不是 git repo 的派工。

Prompt 範本(開頭的 cd 指示與 codex exec 用法照 145–147 兩件事實際寫出,不能只在散文段提過):

```
執行任何 codex 指令前,先 cd 到 <repo 的絕對路徑>(`codex exec` 一律加 `< /dev/null`)。
呼叫 codex 一律開全新對話:走 companion 用 `task --fresh`(禁止 `--resume-last`/`--resume`);直接用 `codex exec` 則**不加 `--fresh`**(exec 沒有這個旗標,加了 exit 2、完全沒跑),也不用 `codex exec resume`。以下審查內容寫成檔案後以 stdin 傳入(`codex exec ... < 內容檔`,此時不再另加 `< /dev/null`),禁止拆成命令列參數。
判定跑完與否看輸出有沒有 `VERDICT:` 行,不看 exit code(管線末端的 exit 0 蓋得掉 codex 的 exit 2);沒有就回 `VERDICT: UNAVAILABLE`。
失敗時回 `VERDICT: UNAVAILABLE`,逐字貼錯誤原文(含 exit code 與 stderr),不要轉述。

【嚴格限制】不要執行任何指令、不要讀取任何檔案。
(本環境的 codex 沙箱會擋外部 shell,讀檔必敗。)以下資訊已完整提供,僅根據它判斷。

請審查以下 staged diff(已完整內嵌,**不附檔案路徑**):

<diff 內容直接貼在此>

【任務背景】<一句話:這次改動在做什麼、影響範圍>

【判準】
- BLOCK:會壞功能、資安洞、邏輯錯、敏感資訊外洩(.env / credentials / hardcoded JWT / 連線字串)、
         改名遺漏跨檔、不該 commit 的檔案。
- PASS:其餘一切。有疑問寧可 BLOCK。

【重點檢查】明顯 bug/邊界(rollback、exception、null)/安全性/不該 commit 的檔案或 debug 痕跡/改名跨檔殘留

【回覆格式,嚴格遵守】
第 1 行:`VERDICT: PASS` 或 `VERDICT: BLOCK`
第 2 行起每行 `- <file>:<line> <短描述>`:BLOCK 列所有必修項;PASS 列所有觀察點
(edge case/跨檔殘留/缺測試或 i18n/future risk——觀察到幾項列幾項,不自我省略;真沒有才只回第 1 行)
一句話講清楚即可,不寫「應該怎麼改」;不要分析段、標題、總結。
```

#### 1.3c code-reviewer(C 軌)

`subagent_type` **先用 `code-reviewer`**(專案自訂的審查者,帶該專案規範);Agent 工具回 `Agent type 'code-reviewer' not found` 時,**同輪改用 `git-commit:code-reviewer`**(本 plugin 自帶的通用版)重發,不算失敗、不降級單軌。**名稱解析規則(實測)**:plugin 自帶的 agent 只能用帶前綴的 `git-commit:code-reviewer` 叫到,裸名 `code-reviewer` 只會解析到專案層/使用者層的同名 agent——**即使全環境只有 plugin 這一支,裸名也回 not found**。所以兩段式缺一不可:只寫裸名,沒有自訂審查者的專案就沒有 C 軌;只寫前綴,有自訂審查者的專案會被通用版蓋掉。

**第三層(兩個名稱都 not found 時,同輪再發)**:`git-commit:code-reviewer` 只在**有啟用 git-commit 的專案**才會註冊;從別的專案借用 flow.sh(例如 symlink 或絕對路徑呼叫)時它不存在。這時改用 `subagent_type: general-purpose`,prompt 開頭加一句「先讀 `<本 skill 目錄>/../../agents/code-reviewer.md`,照其中的角色、判準與回報格式執行」,其餘接下方範本。本 skill 目錄或 flow.sh 本身走 symlink 時仍指得到 plugin 本體(flow.sh 先用 `readlink -f` 解析腳本路徑、再用 `pwd -P` 解析目錄;檔案層級 symlink 的情境未實測)。
C 軌的 skipped **只收一種**:第三層 general-purpose 也試過且失敗(理由寫明 general-purpose 並貼它的錯誤原文)。其餘理由——找不到 agent、逾時、連線失敗——一律先走第三層重試。`flow.sh review-record` 以放行清單機械把關:理由裡沒有 general-purpose 就拒收,並印出審查者定義檔的實際路徑。記錄時 `--reviewer` 貼 general-purpose 回覆原文即可。

`run_in_background: true`,同輪觸發。Prompt 範本(VERDICT 格式與 B 軌對齊,利匯流判讀):

```
請審查 staged diff(在 <DIFF_PATH>,請先 `cat` 讀取)。本任務由你親自執行,不得再轉派給其他 agent。

【開工前必讀】(專案有這些檔就讀,沒有就略過)
- CLAUDE.md(有「專案概要」一節就先讀那節)
- CONTEXT.md(專案用語)
- FLOWS.md(改動觸及已收錄鏈路時,確認鏈路其他層有沒有同步;沒觸及就略過)
【產出路徑】staged diff:<DIFF_PATH>
【驗證方式】cat 讀 diff;需要上下文時 Read 對應檔案全檔,不只看 diff 行
【任務背景】<一句話:這次改動在做什麼、影響範圍>
【審查對象】Repository: <repo>;檔案類型:<.vue/.cs/...>;適用規則:code-review Skill+rules/

【驗收條件】(判準)
- BLOCK:🔴 Critical(違反架構原則、資安洞、破壞 DDD 分層、必錯邏輯)
- PASS:無 Critical 即放行;🟡 Important/🟢 Minor 列清單供使用者決定。

【重點檢查】架構規範(DDD/CQRS/Repository/Section component)/規則違反/資安/i18n 完整性/測試覆蓋

【專案附加審查要求】
<prepare 印出的附加審查要求原文,逐字貼上;prepare 印「無」時刪掉這一段>

【回報格式】(嚴格遵守)
第 1 行:`VERDICT: PASS` 或 `VERDICT: BLOCK`
第 2 行起每行 `- <file>:<line> [<Critical|Important|Minor>] <短描述>`
最後一行 `- 已讀:<實際讀過的必讀檔名,逗號分隔>;略過:<檔名(理由:專案沒有這個檔/沒觸及已收錄鏈路)>`
一句話講清楚即可,不寫「應該怎麼改」;不要分析段、標題、總結。
```

**專案附加審查要求**:通用範本不帶專案專屬步驟(例:供應商平台審查前要跑 `rule-scan`)。專案把這類要求寫進工作目錄層的 `.claude/git-commit-reviewer-addendum.md`(與 `local-overrides.yml` 同層;`CLAUDE_PROJECT_DIR` 未設時取 flow.sh 執行時的目錄),`prepare` 最後會整段原文印出,**逐字**貼進範本的【專案附加審查要求】,不摘要、不改寫——專案的派工 hook 可能逐字比對其中的關鍵字。檔案不存在或為空時 `prepare` 印「無」,刪掉那一段。檔案格式:純 Markdown,內容就是要對審查者說的話(條列步驟即可),不需要 frontmatter;一份管工作目錄底下所有 repo,各 repo 要求不同時在檔內分段寫明適用 repo。

多 repo 可合併或拆分送審,但**全部非 BLOCK** 才進 Step 2。

### 1.4 匯流

B+C 皆返回即匯流(不等 A 軌),按核心原則四條決策。補充:

- 自動 commit 前輸出:兩軌狀態+「使用者回覆:尚未(視為默許)」+套用的 message;PASS 附清單時**清單先列給使用者**再告知已開始自動 commit。
- 匯流判定可 commit 後,**先跑 `flow.sh review-record <repo> --codex "<Codex 回覆原文>" --reviewer "<code-reviewer 回覆原文>"`** 再 ship。回覆原文多行時用 `"$(cat <<'EOF' … EOF)"` 帶入。多 repo 各記各的。
- BLOCK 處理:列必修項 → 使用者決定修或強制 commit(需明示)→ 修的話改碼、重 `prepare`、兩軌重送,直到全非 BLOCK。無 revert、無歷史噪音。使用者明示強制 commit → `review-record --exempt "<使用者的原話>"`(BLOCK 的回覆原文不會被 `--codex`/`--reviewer` 收下)。

## Step 2:`flow.sh ship`

- **禁止 AI 署名**(`Co-Authored-By: Claude` 等)——公司禁止揭露 AI 參與;腳本已過濾,description 參數也不得夾帶。
- `ship` 不做 amend——要改寫 HEAD 走 `flow.sh amend`(需使用者明示才加 `--confirm-rewrite`)。禁 force push;push 到 `main`/`master` 前特別確認使用者意圖。
- 使用者要求跳過審查(緊急 hotfix)→ `review-record <repo> --exempt "<使用者的原話>"` 後再 ship,並告知破例。

**pre-commit hook 失敗,先分辨兩種情況:**

- **(a) hook 正常執行但檢查不過**(eslint/測試失敗)→ 依提示修正 → 重跑 `prepare` → 兩軌重審 → 再 `ship`。
- **(b) hook 本身故障**(`Exec format error`、segfault、CRLF/缺 shebang、無法 spawn)→ 這不是程式碼問題,修碼會卡死。處置:①手動補跑 hook 本該做的檢查(prettier/eslint/test)確認乾淨;②明告使用者是 hook 環境故障+已補跑哪些檢查;③**經使用者同意**才可手動 `git commit --no-verify`(本 skill 唯一允許情境,脫離 flow.sh),message 加 `[skip-verify: hook 環境故障,已手動補跑 <檢查項>]`;④建議根治(修 shebang/LF 或 `core.autocrlf=input`),不根治每次都炸。

## Merge 收尾(flow.sh 沒有 merge 子命令,用 ship)

`git merge` 停在衝突或 `--no-commit` 時(MERGE_HEAD 存在),**不要下 `git commit` 或 `git merge --continue`**(hook 會擋),照一般流程走 flow.sh——`ship` 在 MERGE_HEAD 存在時建出的就是雙 parent 的 merge commit(2026-09-24 實測):

1. 解衝突 → `flow.sh prepare <repo> <解完的檔案...>`(逐檔列,會 `git add` 標為已解);或已全部 stage 好就 `flow.sh prepare <repo> --staged`
2. 照 Step 1 送審 → `review-record`
3. `flow.sh ship <repo> Chore "合併 <分支> 進 <目標分支>"`(無 `Merge` Type,merge 一律用 `Chore`)

prepare 發現還有未解衝突(`git diff --diff-filter=U` 有東西)會直接拒絕,並列出檔案。staged diff 是相對第一個 parent(目前分支),審查看到的是「合併進來的全部變更+衝突解法」。
要放棄 merge:`git merge --abort` 不在攔截範圍,照 §歷史改寫 先問使用者。

## Commit Message 規範

格式:`{Type}: {簡短描述}`——Type 首字大寫、冒號後空格、中文描述 1 句。例:`Feat: 相關申請紀錄新增發起人欄位與 Excel 匯出`。

**單行,不寫 body。** `flow.sh ship <repo> <type> <description>` 只吃單行 description,`assert_no_signature` 會機制級擋掉多行。理由要留就留在 PR/issue,不進 git message。

**長度上限:顯示寬度 ≤72**——全形字(中文、全形標點)算 2、半形算 1。由 `flow.sh ship` 機械檢查,超標即 exit 1,無豁免旗標。

| 類型 | 說明 |
|------|------|
| Feat | 新功能 |
| Modify | 既有功能需求調整的修改 |
| Style | UI 調整、格式調整 |
| Refactor | 重構(不改變功能) |
| Perf | 改善效能 |
| Chore | 建置設定、版號等瑣事 |
| Docs | 純文件 |
| Test | 測試 |
| Fix | 錯誤修正 |
| Hotfix | 緊急修正嚴重 bug |

**判準一句話:寫成一般正常人會寫的樣子。**

正常人寫 commit message 是描述「改了什麼」,**不會寫「我用什麼方法確認它是對的」**。後者是作業過程,只有今天在場的人看得懂,而 git log 是給六個月後的人看的。

| ❌ 不是正常人會寫的 | ✅ 正常人會寫的 |
|---|---|
| `Fix: 經語法樹掃描確認 25 個 await 全數位於 try 保護中` | `Fix: DAP 推送的 await 全數移入 try 保護範圍` |
| `Fix: 實測七則官方回應,六則失敗已修正` | `Fix: DAP 回應信封改為型別無關,失敗不再擲例外` |
| `Fix: 補 P0 防護` | `Fix: handleConfirmFinalReply 失敗時保留 dialog` |
| `Chore: 將 .claude 納入 gitignore,該目錄為 Claude Code 的本機設定` | `Chore: 將 .claude 納入 gitignore` |

機制側由 `flow.sh ship` 的 `MESSAGE_TRACE_PATTERN` 擋三類(命中即 exit 1):

1. **AI/工具身分**:`Claude`、`Anthropic`、`Codex`、`code-reviewer`、`subagent`、`紅藍對抗`
2. **作業過程句式**:`經…掃描/確認/比對`、`實測…通過/失敗`、`實跑…通過`、`掃描確認`、`驗證:`後接結論

> 抓的是**詞組不是裸詞**——單一名詞分不出業務語意與作業過程。「審查」是簽核業務、`agent` 是代理商,用裸詞會擋掉正常 commit(`新增審查流程狀態欄位`、`agent-based 架構調整` 都該放行)。作業過程的特徵在句式,不在名詞。
>
> **刻意不抓的**(試過、會誤擋,交由人在審查時判斷):
>
> | 不抓 | 因為它在業務上是 |
> |---|---|
> | `P0`–`P3` | 缺陷單系統的欄位命名(`P1 缺陷`、`P2 問題`) |
> | `本輪`、`第 N 輪` | 招標、面試、報價按輪次進行 |
> | `複查` | 保險、稽核、品管的標準流程名詞 |
> | `紅隊` | 資安演練產品的業務命名 |
> | `審查`、`agent` | 簽核流程、代理商 |
>
> `Fix: 補 P0 防護` 真正的問題是描述太模糊——那該由人在審查時判斷,不是 regex 的事。這道閘只負責攔「一眼就知道是作業過程」的寫法。
>
> 即便如此,文字比對仍必然有誤判,所以這道閘留了出口:確認命中的是業務詞彙,用 `--allow-message-trace` 放行。**長度上限沒有出口**——長度是客觀事實,不會誤判。

> ⚠️ 黑名單只是兜底,**判準是上面那句話**。關鍵字清單永遠有漏網的寫法,寫之前先自問:**這句話,一個拿到 diff 但沒有今天對話的人,能不能自己驗證?**

## 歷史改寫(rebase / amend / reset)

改寫既有 commit(`rebase -i`/`--amend`/`reset`)同樣受本 skill 管轄——`git commit` 以外的改寫指令一樣會繞過全部真閘,禁令不因指令名稱不同而失效。

**amend 走 `flow.sh amend <repo> --confirm-rewrite`**,下面第 1、4、5 條它會自動做(建備份分支、機械驗證、擋已 push 的改寫),第 2、3 條仍是你的責任。`rebase -i`/`reset` 尚無子命令封裝,需人工按本節規則走。

規則:

1. **改寫前必建備份分支**,並把分支名回報給使用者:`git branch backup/pre-<動作>-$(date +%H%M%S)`。回報的分支名一律複製**指令實際輸出**,不要憑記憶寫——名字裡有時戳,記錯了使用者就查不到。
2. **只改 message、不改碼** → 免三軌審查,但**每顆的新 message 都要過 §Commit Message 規範**(含痕跡與寬度)。
3. **有改到碼** → 走完整三軌流程,等同新 commit(`flow.sh amend` 會檢查 `review-record` 紀錄,沒有就拒絕並清掉剛建的備份分支)。
4. **改寫後必須機械驗證**,兩種情況驗法不同(用錯會得到假結論):
   - **只改 message(第 2 條)** → `git diff <備份分支> HEAD` 必須為空。不空代表改 message 的過程動到了碼。
   - **有改到碼(第 3 條)** → diff 必然不為空,上面那條驗不了,改驗「只動了該動的檔案」:
     `git diff --name-only <備份分支> HEAD` 的清單須等於本次 staged 的清單;清單外的檔案 blob hash 須逐一相同;
     決定性驗證是把備份分支的樹套上本次的 blob、`write-tree` 出來的 tree hash 須等於 HEAD 的 tree hash
     (tree hash 是整棵樹的 Merkle hash,相同即 byte 級一致)。
   `flow.sh amend` 會依有無 staged 內容自動選對驗法並印結果,走它就不必手動驗。
5. **已 push 的 branch 禁止改寫**,除非使用者明示且確認無人共用。
6. 改寫完 `git log --format='%h %s' <range>` 逐顆看過再回報,不要只看最上面那顆。

## 交付路徑不只有 push

`format-patch` / `bundle` / `archive` 產出的檔案**內含完整 commit message 原文**,會直接送到客戶或上游手上——比 push 更難收回。

先跑 `flow.sh audit <repo>` 體檢一次 message(空 message、缺 Type、超長、痕跡、多行 body),再對**產出物本身**掃一次,而不是只掃 repo:

```bash
grep -n -i -E 'Claude|Anthropic|Codex|subagent|實測|掃描確認|本輪' <產出的 patch/bundle>
```

> 這是「用目標軟體驗、不要用自己的 parser 驗」的同型教訓:驗 repo 不等於驗交付物。

## 多議題拆 Commit(不要問)

Dirty 檔案涵蓋多個不相關議題 → **直接拆多個 commit,自己決定怎麼拆與 message 用詞**(使用者明示過偏好拆、不要問)。一個議題=一個 commit;同議題跨多檔放同 commit;同檔跨多議題可合併、message 概括。逐個走完整流程(`analyze`→`prepare`→三軌→`review-record`→`ship`),完成一個再 `analyze` 下一個。可以問的例外:檔案歸屬判不明、跨 repo 邊界(內外站誰先誰後)、涉破壞性操作。

變更紀錄見 plugin 根目錄 `../../CHANGELOG.md`。

Files in this skill

  • SKILL.md21.6 KB
  • flow.sh29.2 KB
  • local-overrides.example.yml2.8 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.