pro-commit · diff

git:20261021.560e3d1 to git:20261022.c750490

95 added, 90 removed. Audit A to A.

---
name: pro-commit
description: "Create the commit with a message built from the issue number in the branch name. Use when committing issue-linked work or auto-generating a commit message, or on /commit."
---
# Commit Mode
## When to use
- - 이슈 연동 커밋, 커밋 메시지 자동 생성이 필요할 때, /commit 호출 시 사용한다.
+ - Committing issue-linked work, generating a commit message automatically, or on `/commit`.
+ - Typical requests: "commit this", "write the commit message" (Korean: "커밋해줘", "커밋 메시지 만들어줘").
- 브랜치명에서 이슈 번호를 추출하고, GitHub API로 이슈 정보를 조회해 **커밋 컨벤션에 맞는 메시지를 자동 완성하고 커밋**한다.
- `../references/common-rules.md`의 커밋 컨벤션 규칙을 따른다. 사용자 입력: $ARGUMENTS
+ Extract the issue number from the branch name, read the issue through the GitHub API, then **build a message that follows the commit convention and commit it**.
+ Follow the commit convention in `../references/common-rules.md`. User input: $ARGUMENTS
- ## 무엇을 하려는가 → 명령 → 자세히
+ Talk to the user in the user's language (or the configured `language` preference). The prompts below are written in English as templates; translate them when you show them.
- 모든 명령은 `{PYTHON} {SCRIPTS}/commit_cli.py <명령>` 이다.
+ ## Goal → command → details
- | 하려는 것 | 명령 | 자세히 |
+ Every command is `{PYTHON} {SCRIPTS}/commit_cli.py <command>`.
+
+ | Goal | Command | Details |
|---|---|---|
- | 이슈 제목·URL 조회 | `get-issue {owner} {repo} {번호}` | 3단계 |
- | 현재 브랜치(cwd)의 이슈 번호 | `get-issue-number` | 3단계 |
- | 커밋 형식·말투 조회 (팀 > 개인) | `resolve-style --repo-root "{PROJECT_ROOT}"` | 4단계 · Customize |
- | 이슈 제목 정제 · 메시지 틀 조립 | `normalize-title "<제목>"` · `get-commit-template "<제목>" "<URL>" [--type fix] [--scope S] --repo-root "{PROJECT_ROOT}"` | `references/message-rules.md` |
- | 커밋 직전 mention trailer 제거 | `sanitize-message "<메시지>"` | `references/message-rules.md` |
- | 자동/수동 승인 판정 · 첫 실행 제안 | (config Read/Write) | `references/approval-gate.md` |
+ | Read issue title and URL | `get-issue {owner} {repo} {number}` | Step 3 |
+ | Issue number of the current branch (cwd) | `get-issue-number` | Step 3 |
+ | Read commit format and tone (team > personal) | `resolve-style --repo-root "{PROJECT_ROOT}"` | Step 4 · Customize |
+ | Clean the issue title · build the message frame | `normalize-title "<title>"` · `get-commit-template "<title>" "<URL>" [--type fix] [--scope S] --repo-root "{PROJECT_ROOT}"` | `references/message-rules.md` |
+ | Strip the mention trailer right before commit | `sanitize-message "<message>"` | `references/message-rules.md` |
+ | Decide auto/manual approval · first-run offer | (config Read/Write) | `references/approval-gate.md` |
- ## 핵심 원칙
+ ## Core principles
- - **사용자 확인 없이 절대 커밋하지 않는다** — 메시지는 반드시 제안 후 승인받고 실행
- - **이슈를 자동 생성하지 않는다** — 이슈 없으면 선택지 제시 후 사용자가 결정
- - **`git add -A`·`git add .`로 일괄 스테이징하지 않는다** — 여러 세션이 같은 작업 트리를 쓰므로 일괄 스테이징하면 다른 세션의 미커밋 변경이 커밋에 섞인다. 변경 목록을 보여주고 **이번 작업에서 건드린 경로만** 명시해 `git add <경로...>` 한다. 의도를 모르는 변경은 건드리지 않고 그대로 둔다
- - **`git push`는 절대 실행하지 않는다** — 커밋까지만 담당 (CLAUDE.md 규칙: push는 명시 허락 시에만)
- - **커밋에 Claude/AI 흔적을 절대 남기지 않는다** — `Co-Authored-By`, `Generated with Claude`, `🤖`, `@claude` 등 AI 서명/푸터/GitHub @mention trailer 일절 금지. 변경설명·푸터 어디에도 `@username` GitHub mention을 포함하지 않는다(이슈 본문에서 가져온 mention도 제거). 사용자의 git 설정으로만 커밋되어 사용자가 직접 작성한 것처럼 보여야 한다. 커밋 메시지는 본문만 작성한다.
- - **자동 모드(`auto_approve == true`)에서도 제안 메시지는 사용자에게 보여준 뒤 커밋한다** — 표시만 하고 즉시 진행. 응답을 기다리지 않는다.
- - **사용자에게 config 키 이름·파일 경로를 노출하지 않는다** — 자동/수동 토글은 자연어 응답("자동으로 진행해줘" / "매번 확인받게 해줘")을 받아 agent가 직접 갱신한다. 승인 설정을 쓸 때마다 `references/approval-gate.md` 규칙 D(형식 고정)를 함께 적용한다.
+ - **Never commit without user confirmation.** Always propose the message, get approval, then run.
+ - **Never create an issue automatically.** If there is no issue, offer choices and let the user decide.
+ - **Never stage everything with `git add -A` or `git add .`.** Several sessions share the same working tree, so bulk staging mixes another session's uncommitted changes into your commit. Show the change list and run `git add <path...>` with **only the paths this task touched**. Leave changes you do not understand alone.
+ - **Never run `git push`.** This skill stops at the commit (CLAUDE.md rule: push only with explicit permission).
+ - **Never leave a Claude/AI trace in the commit.** No `Co-Authored-By`, `Generated with Claude`, `🤖`, `@claude`, or any AI signature, footer or GitHub @mention trailer. Do not put any `@username` GitHub mention anywhere in the description or footer (remove mentions copied from the issue body too). The commit uses only the user's git config, so it looks like the user wrote it. Write only the message body.
+ - **In auto mode (`auto_approve == true`), still show the proposed message before committing.** Show it and proceed at once; do not wait for a reply.
+ - **Do not show config key names or file paths to the user.** The user toggles auto/manual in natural language ("go ahead automatically" / "ask me every time"; Korean: "자동으로 진행해줘" / "매번 확인받게 해줘") and the agent updates the config itself. Every time you write the approval setting, also apply Rule D (format lock) in `references/approval-gate.md`.
- ## 시작 전 — 자동 승인 판정
+ ## Before you start: decide auto approval
- `~/.projectops/config/config.json`(Windows: `C:\Users\<사용자>\.projectops\config\config.json`) 한 곳만 Read 로 읽어
- `AUTO_APPROVE`·`CONFIG_HAS_KEY` 를 정한다. 우선순위(레포별 → 글로벌 → 기본 `false`)와 갱신 방법은 `references/approval-gate.md`.
+ Read only one file, `~/.projectops/config/config.json` (Windows: `C:\Users\<user>\.projectops\config\config.json`), with Read,
+ and set `AUTO_APPROVE` and `CONFIG_HAS_KEY`. Precedence (per repo → global → default `false`) and how to update it: `references/approval-gate.md`.
- ## 스크립트 찾기 (1회)
+ ## Find the scripts (once)
- > **고정 진입점이 있으면 아래 탐색을 건너뛴다** (#942): `~/.projectops/bin/projectops_cli.py` 는 한 번이라도 CLI 를 실행하면 생기고, 부를 때마다 설치된 가장 새 스크립트를 찾아 인자를 그대로 넘긴다. 세션 도중 플러그인이 업데이트돼도 옛 경로에 묶이지 않는다.
- > `PYTHONIOENCODING=utf-8 python3 ~/.projectops/bin/projectops_cli.py commit <서브커맨드> ...` (없거나 `scripts_not_found` 면 아래 탐색). 응답에 `newer_installed` 가 오면 이 진입점으로 다시 부른다.
- > zsh 에서 명령을 변수에 담지 않는다 (`P="python3 …"; $P` 는 exit 127). 줄이려면 함수로 감싼다: `G(){ PYTHONIOENCODING=utf-8 python3 ~/.projectops/bin/projectops_cli.py commit "$@"; }` → `G get-issue-number`.
+ > **Skip the search below if the fixed entry point exists** (#942): `~/.projectops/bin/projectops_cli.py` appears after any CLI has run once. Each call finds the newest installed script and passes the arguments through, so a plugin update mid-session does not pin you to an old path.
+ > `PYTHONIOENCODING=utf-8 python3 ~/.projectops/bin/projectops_cli.py commit <subcommand> ...` (if it is missing or returns `scripts_not_found`, use the search below). If the response has `newer_installed`, call this entry point again.
+ > In zsh, do not store the command in a variable (`P="python3 …"; $P` exits 127). To shorten it, wrap it in a function: `G(){ PYTHONIOENCODING=utf-8 python3 ~/.projectops/bin/projectops_cli.py commit "$@"; }` → `G get-issue-number`.
- **Bash 도구는 호출마다 상태가 초기화된다.** 한 번 찾은 뒤 **실제 경로를 이후 블록에 값으로 직접 써넣는다.**
+ **The Bash tool resets its state on every call.** Find the paths once, then **write the real paths as literal values in later blocks.**
```bash
PROJECT_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
PYTHON=$(for _py in python3 python; do _path=$(command -v "$_py" 2>/dev/null) || continue; "$_path" -c "import sys; sys.exit(0)" 2>/dev/null && echo "$_path" && break; done)
[ -z "$PYTHON" ] && { echo "Python not found"; exit 1; }
- # 이 레포에서 개발 중이면 로컬본이 캐시본보다 최신이므로 로컬 우선 (사용자 프로젝트는 설치본을 쓴다)
+ # When developing in this repo the local copy is newer than the cache, so prefer it (user projects use the installed copy)
SKILL=pro-commit; ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
[ -d "$ROOT/skills/$SKILL/scripts" ] || for B in ~/.claude/plugins/cache ~/.codex/plugins/cache ~/.gemini/extensions ~/.pi/agent/git; do
H=$(find "$B" -maxdepth 8 -type d -path "*/projectops/*skills/$SKILL/scripts" 2>/dev/null | sort -V | tail -1)
[ -n "$H" ] && { ROOT="${H%/skills/$SKILL/scripts}"; break; }
done
+ # Home-copied installs from npx projectops: GitHub Copilot and Cursor (#200)
+ [ -d "$ROOT/skills/$SKILL/scripts" ] || for B in ~/.copilot ~/.cursor; do [ -d "$B/skills/$SKILL/scripts" ] && { ROOT="$B"; break; }; done
SCRIPTS="$ROOT/skills/$SKILL/scripts"
- [ -d "$SCRIPTS" ] || { echo "projectops 스킬 스크립트를 찾지 못했습니다. 플러그인 설치를 확인하세요."; exit 1; }
+ [ -d "$SCRIPTS" ] || { echo "projectops skill scripts not found. Check the plugin installation."; exit 1; }
echo "PYTHON=$PYTHON SCRIPTS=$SCRIPTS PROJECT_ROOT=$PROJECT_ROOT"
```
- ## 프로세스
+ ## Process
- ### 1단계: 변경사항 확인 및 스테이징
+ ### Step 1: Check and stage changes
```bash
git diff --cached --stat
git status --short
```
- **staged 파일이 있으면** 그 목록을 보여주고 진행한다. 이번 작업과 무관해 보이는 파일이 섞여 있으면 커밋 전에 사용자에게 알린다 (임의로 unstage 하지 않는다).
+ **If files are already staged**, show the list and continue. If some look unrelated to this task, tell the user before committing (do not unstage them yourself).
- **staged 파일이 없으면** 변경 목록을 두 묶음으로 나눠 보여주고, 첫 묶음만 경로를 명시해 스테이징한다:
+ **If nothing is staged**, show the changes in two groups and stage only the first group, path by path:
- | 묶음 | 판단 근거 | 처리 |
+ | Group | How to tell | Action |
|------|-----------|------|
- | 이번 작업에서 건드린 경로 | 이 세션에서 직접 수정·생성한 파일, 사용자가 지목한 파일 | `git add <경로...>` 로 명시 스테이징 |
- | 의도를 모르는 변경 | 이 세션이 만들지 않았는데 작업 트리에 있는 파일 | **건드리지 않는다** — 다른 세션의 진행 중 작업일 수 있다 |
+ | Paths this task touched | Files this session edited or created, files the user pointed to | Stage explicitly with `git add <path...>` |
+ | Changes you do not understand | Files in the working tree that this session did not create | **Leave them alone.** They may be another session's work in progress |
```bash
- # 건드린 경로만 하나씩 명시한다. 공백·한글 경로는 따옴표로 감싼다.
- git add -- "{경로1}" "{경로2}"
+ # Name each touched path. Quote paths with spaces or non-ASCII characters.
+ git add -- "{path1}" "{path2}"
git diff --cached --stat
```
- - `git add -A` / `git add .` / `git add -u` 는 쓰지 않는다. 무엇을 건드렸는지 판단이 서지 않으면 목록을 보여주고 사용자에게 고르게 한다.
- - **이번 세션에서 수정한 파일을 알 수 없으면**(새 세션에서 호출) 추측하지 말고 변경 목록을 보여주고 담을 경로를 묻는다.
- - 스테이징할 경로가 하나도 없으면 커밋하지 않고 그 사실만 알린다.
- - 뒤 단계에서 **취소**(2단계 3번 · 5단계 4번)하면 `git restore --staged -- "{이번에 담은 경로}"` 로 **내가 담은 것만** 되돌린다. 처음부터 staged였던 파일은 건드리지 않는다.
+ - Do not use `git add -A`, `git add .` or `git add -u`. If you cannot tell what you touched, show the list and let the user choose.
+ - **If you cannot know which files this session changed** (called from a new session), do not guess. Show the change list and ask which paths to include.
+ - If there is no path to stage, do not commit. Just say so.
+ - If the user **cancels** later (Step 2 option 3 · Step 5 option 4), run `git restore --staged -- "{paths you staged}"` to undo **only what you staged**. Leave files that were staged from the start untouched.
- ### 2단계: 이슈 번호 추출
+ ### Step 2: Extract the issue number
```bash
BRANCH=$(git branch --show-current 2>/dev/null || echo "")
ISSUE_NUMBER=$(echo "$BRANCH" | grep -oE '#[0-9]+' | grep -oE '[0-9]+' | head -1)
REMOTE_URL=$(git remote get-url origin 2>/dev/null || echo "")
OWNER=$(echo "$REMOTE_URL" | sed -E 's|.*github\.com[:/]([^/]+)/.*|\1|')
REPO=$(echo "$REMOTE_URL" | sed -E 's|.*github\.com[:/][^/]+/([^/.]+)(\.git)?$|\1|')
```
- 브랜치명 예: `20260422_#260_기능개선_제목` → `260`. (`{PYTHON} {SCRIPTS}/commit_cli.py get-issue-number` 도 같은 값을 준다.)
+ Branch name example: `20260422_#260_기능개선_제목` → `260`. (`{PYTHON} {SCRIPTS}/commit_cli.py get-issue-number` returns the same value.)
- **이슈 번호가 없으면** 즉시 멈추고 묻는다:
+ **If there is no issue number**, stop and ask:
```
- 브랜치명에서 이슈 번호를 찾을 수 없습니다. (현재 브랜치: {브랜치명})
+ No issue number found in the branch name. (Current branch: {branch})
- 어떻게 할까요?
- 1. 이슈 번호를 직접 입력할게요
- 2. 이슈 없이 자유 형식으로 커밋할게요
- 3. 취소
+ What should I do?
+ 1. I'll enter the issue number
+ 2. Commit in free form without an issue
+ 3. Cancel
```
- 1 → 번호를 받아 3단계 / 2 → 메시지를 직접 받아 5단계(이슈 형식 없이) / 3 → 종료.
+ 1 → take the number and go to Step 3 / 2 → take the message from the user and go to Step 5 (no issue format) / 3 → stop.
- ### 3단계: 이슈 조회 (인라인 Python 금지)
+ ### Step 3: Read the issue (no inline Python)
- PAT는 commit_cli가 config.json에서 자동 로드한다(환경변수가 있으면 우선).
+ commit_cli loads the PAT from config.json automatically (an environment variable wins if set).
```bash
- PYTHONIOENCODING=utf-8 "{PYTHON}" "{SCRIPTS}/commit_cli.py" get-issue {owner} {repo} {이슈번호}
+ PYTHONIOENCODING=utf-8 "{PYTHON}" "{SCRIPTS}/commit_cli.py" get-issue {owner} {repo} {issue_number}
```
- 출력의 `title`·`html_url`을 쓴다. 제목은 **agent가 요약/재작성하지 않고** 정제 규칙(태그 제거 → 앞 이모지 제거 → trim, 비면 원본)만 적용한다 — `references/message-rules.md`.
+ Use `title` and `html_url` from the output. **Do not summarize or rewrite the title.** Apply only the cleaning rule (remove tags → remove leading emoji → trim; if empty, use the original). See `references/message-rules.md`.
- | 실패 | 다음 행동 |
+ | Failure | Next action |
|---|---|
- | `github_api_401` | PAT 만료 안내 → `/pro-github`에서 재등록 유도. 제목은 사용자에게 직접 받는다 |
- | `github_api_404` | 이슈 없음 안내 → 번호 재확인 또는 제목 직접 입력 |
- | `bad_args` | 인자 순서(`owner repo number`) 확인 |
+ | `github_api_401` | Tell the user the PAT expired → point them to re-register it in `/pro-github`. Take the title from the user directly |
+ | `github_api_404` | Tell the user the issue does not exist → recheck the number or take the title directly |
+ | `bad_args` | Check the argument order (`owner repo number`) |
- ### 4단계: 메시지 구성
+ ### Step 4: Build the message
- 형식은 하드코딩이 아니라 설정에서 온다. 순서대로 한다.
+ The format comes from settings, not from hard-coded text. Do this in order.
- 1. `resolve-style --repo-root "{PROJECT_ROOT}"` 로 `template`·`source`·`preset`·`language`·`tone`·`rules`·`examples`·`warnings` 를 받는다. `warnings`·`ignored` 가 비어 있지 않으면 사용자에게 한 줄로 알린다.
- 2. `get-commit-template "<제목>" "<URL>" [--type T] [--scope S] --repo-root "{PROJECT_ROOT}"` 로 틀을 받는다. 틀에는 `{description}` 만 남아 있다. `--repo-root` 를 빼면 cwd 의 git 루트를 쓰지만, 하위 폴더·다른 레포에서 불릴 수 있으므로 항상 명시한다.
- 3. `{description}` 을 staged diff 로 채운다. `language`(개인 설정이 없으면 팀 `version.yml` `language` — `language_source`, #977)·`tone`·`rules` 를 따르고, `examples` 는 문체·길이 참고만 하며 그대로 베끼지 않는다. 비어 있으면 기본 동작이다.
+ 1. Run `resolve-style --repo-root "{PROJECT_ROOT}"` to get `template`, `source`, `preset`, `language`, `tone`, `rules`, `examples`, `warnings`. If `warnings` or `ignored` is not empty, tell the user in one line.
+ 2. Run `get-commit-template "<title>" "<URL>" [--type T] [--scope S] --repo-root "{PROJECT_ROOT}"` to get the frame. Only `{description}` remains in it. Without `--repo-root` it uses the git root of the cwd, but the skill may be called from a subfolder or another repo, so always pass it.
+ 3. Fill `{description}` from the staged diff. Follow `language` (without a personal setting, the team `version.yml` `language` — see `language_source`, #977), `tone`, `rules`. Use `examples` only as a reference for voice and length; never copy them. If they are empty, use the default behavior.
- - 이슈 제목 정제와 URL 은 CLI 결과를 그대로 쓴다 (요약·재작성 금지).
- - 타입은 staged diff 로 추천한다 (feat/fix/refactor/docs/chore/test/style — 표는 `references/message-rules.md`). `--type` 을 생략하면 제목 태그에서 유도한다.
- - **호환성이 깨지는 변경에만 `!`** (`feat!` → 릴리스 major 승격, #546). 확신이 없으면 붙이지 말고 묻는다.
- - `{description}` 에 `@username` mention 을 넣지 않는다.
+ - Use the cleaned issue title and URL exactly as the CLI returns them (no summarizing or rewriting).
+ - Suggest the type from the staged diff (feat/fix/refactor/docs/chore/test/style; table in `references/message-rules.md`). If you omit `--type`, it is derived from the title tag.
+ - **Add `!` only for breaking changes** (`feat!` → major bump on release, #546). If unsure, do not add it; ask.
+ - Do not put `@username` mentions in `{description}`.
- ### 5단계: 승인 게이트
+ ### Step 5: Approval gate
- - `AUTO_APPROVE == true` → 메시지를 보여주기만 하고 바로 6단계.
- - `false` → 제안 후 1(커밋)/2(타입 변경)/3(설명 수정)/4(취소)로 묻고 **응답 전 커밋 금지**.
- - 첫 실행(`CONFIG_HAS_KEY == false`)에서 1을 고르면 커밋 직전 자동화 제안을 **한 번** 한다.
- - `resolve-style` 의 `source` 가 `default` 또는 `legacy-default` 면(형식을 정한 적 없음) 이 세션의 첫 제안 끝에 **한 줄만** 붙인다: "Commit format and tone can be customized; see Customize" (사용자 언어로). 같은 세션에서 다시 붙이지 않는다.
+ - `AUTO_APPROVE == true` → only show the message, then go straight to Step 6.
+ - `false` → propose it and ask 1 (commit) / 2 (change type) / 3 (edit description) / 4 (cancel). **Do not commit before the reply.**
+ - On first run (`CONFIG_HAS_KEY == false`), if the user picks 1, make the automation offer **once** right before committing.
+ - If `source` from `resolve-style` is `default` or `legacy-default` (the user never set a format), add **one line** at the end of this session's first proposal: "Commit format and tone can be customized; see Customize" (in the user's language). Do not add it again in the same session.
- 문구·분기·config 갱신 규칙 전문: `references/approval-gate.md`.
+ Full wording, branches and config update rules: `references/approval-gate.md`.
- ### 6단계: 커밋 실행
+ ### Step 6: Commit
- **AI 서명/푸터를 절대 추가하지 않는다.** `--author`·trailer 없이 사용자 git 설정 그대로 커밋한다.
- 메시지 끝 mention trailer는 `sanitize-message` 로 강제 제거한다(셸 `sed` 금지 — #523, 이유는 `references/message-rules.md`).
+ **Never add an AI signature or footer.** Commit with the user's git config as is, without `--author` or trailers.
+ Force-remove the mention trailer at the end of the message with `sanitize-message` (no shell `sed`, #523; reason in `references/message-rules.md`).
- 메시지를 파일로 저장한 뒤 한 명령으로 정리·스테이징·커밋한다 (JSON 을 셸에서 풀 필요 없다):
+ Save the message to a file, then clean, stage and commit with one command (no need to unpack JSON in the shell):
```bash
- PYTHONIOENCODING=utf-8 "{PYTHON}" "{SCRIPTS}/commit_cli.py" commit --message-file "{메시지 파일}" --root "{PROJECT_ROOT}" --paths "{경로1}" "{경로2}"
+ PYTHONIOENCODING=utf-8 "{PYTHON}" "{SCRIPTS}/commit_cli.py" commit --message-file "{message file}" --root "{PROJECT_ROOT}" --paths "{path1}" "{path2}"
```
- - `--paths` 에는 1단계에서 정한 **이번 작업에서 건드린 경로만** 준다. 이미 스테이징돼 있으면 생략한다.
- - 정리한 메시지만 따로 필요하면 `sanitize-message "$RAW_MSG" --raw`.
+ - Pass to `--paths` **only the paths this task touched**, as decided in Step 1. Omit it if they are already staged.
+ - If you only need the cleaned message, use `sanitize-message "$RAW_MSG" --raw`.
- - 정리 실패·빈 결과면(`empty_message`) **커밋을 중단**한다. 정제 규칙을 문서에 fallback으로 복제하지 않는다.
- - `mention_removed: true` 면 mention이 들어간 경위를 4단계에서 되짚는다. 본문 중간 mention은 보존된다.
+ - If cleaning fails or returns empty (`empty_message`), **stop the commit**. Do not copy the cleaning rule into the docs as a fallback.
+ - If `mention_removed: true`, go back to Step 4 and find how the mention got in. Mentions in the middle of the body are kept.
- 커밋 성공 후:
+ After a successful commit:
```
- ✅ 커밋 완료!
- 메시지: {커밋 메시지}
- 해시: {커밋 해시 앞 7자리}
+ ✅ Committed!
+ Message: {commit message}
+ Hash: {first 7 characters of the commit hash}
- push가 필요하면 직접 실행하세요:
- git push origin {현재 브랜치명}
+ To push, run it yourself:
+ git push origin {current branch}
```
## Customize
Your own language, tone and rules apply: see `../references/preferences.md`. Read them once at the start: run the snippet in `../references/preferences.md` with `SKILL_ID=pro-commit` (or call `resolve-style`, which also returns them).
Preferences change presentation only; approval gates, secret protection and `--dry-run` stay in force.
Keys for this skill (set under `skills.pro-commit` in `~/.projectops/config/config.json`):
| Key | Meaning |
|---|---|
| `preset` | `conventional` (default for new users), `projectops`, `gitmoji`, or `custom` |
| `template` | your own format; beats a `preset` set in the same layer (the preset then becomes `custom`); placeholders `{issue_title}` `{issue_url}` `{issue_number}` `{type}` `{emoji}` `{scope}` `{description}`. Legacy `${issueTitle}`-style placeholders are accepted too |
| `examples` | sample messages to match in voice and length; never copied verbatim |
| `language` / `tone` / `rules` | how `{description}` is written (no personal `language` → the repo's `version.yml` `language`) |
```json
{"skills": {"pro-commit": {"preset": "conventional", "language": "en", "tone": "imperative",
"rules": ["max 72 chars in the first line"]}}}
```
- A repo with projectops installed (`version.yml` has `issue_helper.commit_template`, or `metadata`) uses the team format and it beats your preset and template (`source` is `version.yml` or `team-default`). Your `tone`/`rules`/`language` still apply inside it. The team `commit_type_map` also applies when the type is inferred from title tags.
- `preset` and `template` are one pair: the later layer that sets either one wins (a repo-level `preset` beats a global `template`).
- Existing users keep their format: if you already have approval settings (`github.commit` or `repos[].commit`) and never set `preset`/`template`, the format stays `projectops` (`source` is `legacy-default`) instead of switching to `conventional`.
- Show `warnings` from `resolve-style` / `get-commit-template` to the user.