bahaospanov/skills/tree/main/git-gates
git-gates
function hooks를 기반으로 만든 Claude Code mod입니다. 현재 세션에서 사용자가 승인하지 않은 commit, push 또는 merge를 차단하고, 이미 반영된 브랜치의 push를 거부합니다. commit 메시지(Conventional Commits 형식, issue 참조 및 본문에 대한 Haiku 검사)와 MR 설명을 검토하고, push한 커밋 묶음이 각 커밋 시점에도 프로젝트를 작동 상태로 유지하는지 확인하며, 병합되어 깨끗한 작업을 마친 뒤 worktree와 브랜치를 정리합니다.
이 mod 소개
Bakhtiyar Ospanov의 agent skills와 Claude Code mod입니다. 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 편집 포함)를 읽고 세션당 후속 프롬프트를 최대 2회 보냅니다.
git-gates
push는 배포이므로 agent는 현재 세션에서 사용자의 명시적 동의를 필요로 합니다. 시작 프롬프트나 실행 중 입력한 메시지가 해당하며, 백그라운드 작업 보고는 동의를 철회하지 않습니다. 동의 확인 자체가 실패하면 호출을 차단합니다.
| Check | 실행 대상 | 필요한 것 | 그 다음 | | --- | --- | --- | --- | | consent | git commit、push;PR/MR merge | 현재 세션에 commit, push, ship, deploy, pr, mr, tag 또는 release가 입력되고, 보호된 브랜치가 있으면 그 이름도 필요 | 호출 차단 | | grants | 세션 중 후속 commit | 작업마다 commit을 요청한 메시지 또는 승인 메시지 뒤의 grant 도구 | commit이 grant를 사용 | | messages | git commit | Conventional Commits 제목, 미리 작성된 검토 답변이 없고 마지막 줄에 issue 또는 ticket(#87、#BLK-23)가 있어야 합니다. 메시지나 브랜치 이름에 있으면 그 값을 사용 | commit 차단 | | descriptions | MR/PR 설명 설정 | 첫 번째 열의 고정 라벨 블록 | 호출 차단 | | landed branch | git push | push한 브랜치 head가 이미 보호된 브랜치에 들어 있고 새 MR을 언급하지 않음 | push 차단 | | every commit works | 원격에 없는 2~15개 commit push | Sonnet:뒤의 commit이 다시 추가할 내용을 앞의 commit이 삭제하지 않고, 뒤의 commit에서 추가될 내용을 앞의 commit이 사용하지 않아야 합니다. 순서가 괜찮다고 메시지에 쓰면 생략 | push 차단 | | stale work | 세션 종료 시 | 세션에서 commit 또는 push한 브랜치의 head가 통합 브랜치에 들어 있고 worktree가 깨끗함 | 확인 후 로컬과 origin의 worktree 및 브랜치 삭제를 안내 |
보호된 브랜치는 저장소 자체의 push policy 파일에서 가져옵니다. 통합 브랜치는 보호된 브랜치이며 정책이 없으면 원격 기본 브랜치와 존재하는 dev, develop, main, master를 사용합니다.
origin의 브랜치 head가 이미 통합 브랜치에 들어 있으면 원격 브랜치를 삭제할 때 키워드가 필요 없습니다.
단독 #87은 원격이 있는 저장소에서만 유효하며 issue tracker가 없으면 요구하지 않습니다. 입력한 issue는 이 세션의 저장소와 worktree에 있는 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합니다.
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 # 각 mod의 hooks/shared/ 복사본이 같은지 확인
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 서버를 연결하거나 해제함
생성된 결과를 commit합니다. 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.
