bahaospanov/skills/tree/main/git-gates
git-gates
一個基於 function hooks 的 Claude Code mod,用來替 git 工作設門檻:沒有使用者在目前工作階段授權就阻止 commit、push 或 merge;拒絕推送已經合併的分支;審查 commit 訊息(Conventional Commits 格式、issue 參照,以及對內文的 Haiku 檢查)和 MR 描述;檢查推送的一組 commit 是否在每個 commit 上都維持專案可用;並在工作合併且乾淨後清理工作樹和分支。
關於這個 mod
Bakhtiyar Ospanov 的 agent skills 和 Claude Code 外掛:基於 function hooks 建立的外掛。
Skills
對於 skills CLI 支援的任何 agent:
npx skills add bahaospanov/skills --skill <skill>
或者在 Claude Code 中把它們作為一個外掛全部使用,透過 /bahaospanov-skills:<skill> 呼叫:
/plugin marketplace add bahaospanov/skills
/plugin install bahaospanov-skills@bahaospanov
| Skill | 用途 |
| --- | --- |
| prototype-stages | mattpocock/skills prototype(MIT)的 UI 分支重製版:完整流程、按階段分組的選項,以及一鍵面板 |
| scheme | 用 ASCII 圖表示程式碼變更:控制流程、資料流程、前後對照或分層 |
Mods
早期體驗版;API 會隨版本變化。
每個用途使用一個 mod。
| Mod | 用途 | | --- | --- | | git-gates | git 工作經過授權並保持整潔 | | lean-docs | 值得保留的文件 | | lean-comments | 值得保留的註解 | | lean-scripts | 值得保留的腳本 |
Haiku 審查先在程式碼中設門檻,因此無法讓審查失敗的呼叫不會產生模型呼叫。每個工作階段結束時,檢查會讀取該階段觸及的儲存庫中的 git diff(也包括 Bash 編輯),每個工作階段最多傳送兩次後續提示。
git-gates
推送屬於部署,因此 agent 需要使用者在目前工作階段明確表示同意:開啟它的提示,或執行期間輸入的訊息都算;背景任務的報告不會撤回同意。若同意檢查本身失敗,該呼叫會被阻止。
| Check | 執行於 | 需要 | 然後 | | --- | --- | --- | --- | | consent | git commit、push;PR/MR merge | 目前工作階段輸入了 commit、push、ship、deploy、pr、mr、tag 或 release,而且受保護分支必須點名 | 阻止呼叫 | | grants | 工作階段後續的 commit | 目前工作階段有每個任務一次的 commit 請求,或在授權訊息後使用 grant 工具 | commit 消耗一次授權 | | messages | git commit | Conventional Commits 標題、沒有預先寫好的審查結論,而且最後一行包含 issue 或 ticket(#87、#BLK-23);如果有訊息或分支名稱,就使用其中一個 | 阻止 commit | | descriptions | 設定 MR/PR 描述 | 第 0 欄的固定標籤區塊 | 阻止呼叫 | | landed branch | git push | 分支已推送的 head 已經位於受保護分支中,而且訊息沒有提到新 MR | 阻止 push | | every commit works | 推送 2 到 15 個沒有遠端的 commit | Sonnet:不能有某個 commit 刪除之後 commit 才會恢復使用的內容,也不能引用之後 commit 才會加入的內容;訊息說明順序沒問題時跳過 | 阻止 push | | stale work | 工作階段結束時 | 該工作階段曾 commit 或 push 的分支,其 head 位於整合分支中,且工作樹乾淨 | 檢查後提示刪除本機及 origin 上的工作樹和分支 |
受保護分支來自儲存庫自己的 push policy 檔案。整合分支是受保護的分支;沒有策略時,則使用遠端預設分支,以及存在的 dev、develop、main、master。
只要 origin 上的分支 head 已經位於整合分支中,刪除該遠端分支就不需要關鍵字。
只有在有遠端的儲存庫中,單獨的 #87 才算有效;沒有 issue tracker 時不會提出要求。你輸入的 issue 會綁定到該工作階段儲存庫及其工作樹中的 commit;以 issue 編號結尾的分支(perf/mobile-lcp-89)可以讓訊息用該編號結尾。
lean-docs
| Check | 執行於 | 標記 | 然後 | | --- | --- | --- | --- | | docs-review | git checkout 中新增或擴充的文件 | Haiku:工作結束後不會再有人閱讀的文件(runbook、設定頁、旁白) | Claude 會收到原因 | | docs-no-repeat-code | 正在撰寫文件的行 | 已在某個程式碼檔案中一起出現的識別字 | 拒絕寫入 | | limit-docs | 工作階段結束時 | 新增或擴充的文件、正文多於程式碼、文件重複程式碼 | 後續提示 |
lean-comments
預設不寫註解:保留記錄測量值、陷阱或不變量的註解,刪除複述程式碼或敘述變更的註解。
| Check | 執行於 | 標記 | 然後 | | --- | --- | --- | --- | | limit-edits | Write 或 Edit | 新增超過 3 行註解,或編輯周圍區域的註解過多 | Claude 會收到指引 | | limit-turns | 工作階段結束時 | 該工作階段每個檔案 diff 中新增超過 3 行註解 | 後續提示 |
lean-scripts
| Check | 執行於 | 標記 | 然後 | | --- | --- | --- | --- | | scripts-review | 在 git checkout 中撰寫或擴充的腳本 | Haiku:其實需要時重新輸入即可的腳本 | Claude 會收到原因 |
安裝 mods
只有啟用 function hooks 後 mods 才會載入,所以先在 shell profile 中匯出:
export CLAUDE_CODE_ENABLE_FUNCTION_HOOKS=1
沒有這項設定,Claude Code 會靜默跳過 mods。接著在 Claude Code 中:
/plugin marketplace add bahaospanov/skills
/plugin install <mod>@bahaospanov
開發
每個 mod 使用一個資料夾。共用 tsconfig.json 和 types/。已安裝的 mod 只攜帶自己的資料夾,因此兩個 mod 共用的程式碼會複製到各自的 hooks/shared/。
CLAUDE_CODE_ENABLE_FUNCTION_HOOKS=1 claude --plugin-dir ./<mod> --debug
儲存 <mod>/hooks/ 下的檔案會重新載入 mod。重複 --plugin-dir 即可載入多個 mod。
檢查
npm run typecheck # 對每個 mod 及其測試執行 tsc
npm run check:shared # 檢查 hooks/shared/ 副本在各 mod 中是否一致
claude plugin validate ./<mod> # 引擎看到的模組 hook 和呼叫
claude plugin test ./<mod> # 該 mod 的測試
Types
types/ 由 /plugin-types types 寫入,該命令要在按上述方式啟動的工作階段中執行。以下情況需要重新產生,不能手動編輯:
- Claude Code 更新(比較
head -1 types/claude-code.d.ts與claude --version) - 啟用或停用會向
$增加內容的外掛 - 連接或中斷 MCP 伺服器
提交產生的結果;git diff types/ 會顯示有哪些變更。
安裝
請先查看作者 README,確認 marketplace 與外掛名稱;指令可能隨儲存庫結構而變動。
claude plugin marketplace add bahaospanov/skills claude plugin install git-gates
原文 / README
bahaospanov
Bakhtiyar Ospanov's agent skills, and Claude Code mods: plugins built on function hooks.
Skills
For any agent the skills CLI supports:
npx skills add bahaospanov/skills --skill <skill>
Or in Claude Code, all of them as one plugin, invoked as /bahaospanov-skills:<skill>:
/plugin marketplace add bahaospanov/skills
/plugin install bahaospanov-skills@bahaospanov
| Skill | Purpose |
| --- | --- |
| prototype-stages | mattpocock/skills prototype (MIT), its UI branch reworked: whole flows, options grouped by stage in a one-click panel |
| scheme | ASCII schematic of a code change: control flow, data flow, before/after, or layers |
Mods
Early access; the API changes between releases.
One mod per purpose.
| Mod | Purpose | | --- | --- | | git-gates | Git work is authorized and tidy | | lean-docs | Docs worth keeping | | lean-comments | Comments worth keeping | | lean-scripts | Scripts worth keeping |
Haiku reviews are gated in code first, so a call that cannot fail the review costs no model call. End-of-turn checks read the git diff of repos the turn touched, Bash edits included, and send at most two follow-up prompts a session.
git-gates
Pushing is a deploy, so the agent needs the user's word in the current turn: the prompt that opened it or one typed while it ran. If a consent check itself fails, the call is blocked.
| Check | Runs on | Needs | Then | | --- | --- | --- | --- | | consent | git commit, push; PR/MR merge | commit, push, ship, deploy, pr, mr, tag or release typed in the current turn (a message typed while it runs or a background task's report does not withdraw it); merge needs "merge"; a protected branch must be named | Call denied | | grants | Later commits in the session | A message asking for a commit per task, or the grant tool after an authorizing message | Commits spend the grant; pushes never | | messages | A git commit | Conventional Commits subject, no reviewer pre-answers, a last line with the issue or ticket (#87, #BLK-23) when your messages or the branch name one; then Haiku: a body only when the cause is subtle | Commit denied | | descriptions | Setting an MR/PR description | Fixed-label blocks at column 0 | Call denied | | landed branch | A git push | The branch's pushed head already sits in a protected branch, and the message names no new MR | Push denied | | every commit works | A git push of 2 to 15 commits no remote has | Sonnet: no commit removes something a later one stops using, or uses something a later one adds; skipped when the message says the order is fine | Push denied | | stale work | The end of a turn | A branch the session committed to or pushed whose head sits in an integration branch, its worktree clean | Follow-up prompt to remove the worktree and the branch, local and on origin, once checked |
Protected branches come from a repo's own push policy file.
Integration branches are the protected ones; with no policy, the remote's default branch and any of dev, develop, main, master that exist.
Deleting a branch on origin needs no keyword when origin's head of it already sits in an integration branch.
A bare #87 counts only in a repo with a remote; with no issue tracker, nothing is asked.
Issues you typed bind only commits in the session's repo and its worktrees; a branch ending in its issue number (perf/mobile-lcp-89) lets the message end with that one instead.
lean-docs
| Check | Runs on | Flags | Then | | --- | --- | --- | --- | | docs-review | A doc grown in a git checkout | Haiku: text nobody reads after the task (runbooks, setup pages, narration) | Claude gets the reason | | docs-no-repeat-code | A doc line being written | Identifiers that already appear together in one code file | Write denied | | limit-docs | The end of a turn | New or grown docs, prose outweighing code, doc lines repeating code | Follow-up prompt |
lean-comments
No comments by default: keep the ones that record a measured number, a trap or an invariant, cut the ones that restate the code or narrate the change.
| Check | Runs on | Flags | Then | | --- | --- | --- | --- | | limit-edits | A Write or Edit | More than 3 added comment lines, or a comment-heavy region around the edit | Claude gets the guidance | | limit-turns | The end of a turn | More than 3 new comment lines per file in the turn's diff | Follow-up prompt |
lean-scripts
| Check | Runs on | Flags | Then | | --- | --- | --- | --- | | scripts-review | A script written or grown in a git checkout | Haiku: scripts you could just type again when needed | Claude gets the reason |
Install mods
Mods load only with function hooks enabled, so export this in your shell profile first:
export CLAUDE_CODE_ENABLE_FUNCTION_HOOKS=1
Without it Claude Code skips the mods silently. Then, in Claude Code:
/plugin marketplace add bahaospanov/skills
/plugin install <mod>@bahaospanov
Develop
One folder per mod. tsconfig.json and types/ are shared. An installed mod
carries only its own folder, so code two mods share is copied into each one's
hooks/shared/.
CLAUDE_CODE_ENABLE_FUNCTION_HOOKS=1 claude --plugin-dir ./<mod> --debug
Saving a file under <mod>/hooks/ reloads the mod. Repeat --plugin-dir to
load several.
Check
npm run typecheck # tsc over every mod and its tests
npm run check:shared # hooks/shared/ copies are identical across mods
claude plugin validate ./<mod> # what the engine sees the module hook and call
claude plugin test ./<mod> # the mod's tests/
Types
types/ is written by /plugin-types types, run inside a session started as
above. Regenerate, never edit, when:
- Claude Code updates (
head -1 types/claude-code.d.tsvsclaude --version) - a plugin that adds to
$is enabled or disabled - an MCP server is connected or disconnected
Commit the result; git diff types/ shows what the update changed.
