
Git Commit
- 8 installs
- 16 repo stars
- Updated August 3, 2026
- bahayonghang/my-claude-code-settings
git-commit is a Claude Code skill that composes Conventional Commit messages, splits staged changes into atomic commits, and adds [AI] audit trailers.
About
The git-commit skill orchestrates Conventional Commits for staged Git changes, or for all working-tree changes when the user explicitly asks to include everything. A developer uses it to write commit messages, split a messy index into atomic commits, or generate structured commit text without pushing. It auto-detects the output language, injects an [AI] header tag with agent audit trailers, and scans staged files for secrets and large blobs before committing.
- Orchestrates Conventional Commits for staged changes with a preflight-to-verify workflow
- Auto-detects output language (English/Chinese) and adds [AI] header tag + agent audit trailers
- Runs a safety scan for secrets and large blobs before committing
Git Commit by the numbers
- 8 all-time installs (skills.sh)
- Ranked #444 of 733 Git & Pull Requests skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
git-commit capabilities & compatibility
Free; local git only
- Capabilities
- commit message generation · commit splitting
- Works with
- github
- Use cases
- code review
- Pricing
- Free
What git-commit says it does
Safely orchestrate Conventional Commits for staged Git changes, or for all working-tree changes when the user explicitly asks to include everything.
Agent commits add an `[AI]` header tag and audit trailers, enforce a Why line for feat/fix/refactor/perf, and support a `chore(wip)` checkpoint mode.
If it includes likely secrets (`.env`, `*.pem`, `*.key`, `id_rsa`, `id_ed25519`, `*.p12`, `*.keystore`) or large/binary blobs (> ~1 MB), do not commit silently.
npx skills add https://github.com/bahayonghang/my-claude-code-settings --skill git-commitAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 8 |
|---|---|
| repo stars | ★ 16 |
| Last updated | August 3, 2026 |
| Repository | bahayonghang/my-claude-code-settings ↗ |
What it does
Compose Conventional Commit messages, split staged changes into atomic commits, and add AI audit trailers.
Who is it for?
Writing Conventional Commit messages and splitting a messy staged index into atomic commits
Skip if: Pushing changes by default (it never pushes) or committing an obviously mixed set it cannot safely split
When should I use this skill?
The user asks to write a commit message, split staged changes, commit everything, or generate structured commit text without pushing.
What you get
Changes are split into atomic Conventional Commits with detected language, [AI] tags, audit trailers, and a secret/large-file safety scan.
- Conventional Commit message(s)
- An atomic-commit split plan
- Committed changes with [AI] tag and agent trailers
By the numbers
- 6-stage workflow: preflight to verify
- samples git log -n 20 for conventions
- large-blob threshold ~1 MB
Files
Use this workflow in order: preflight -> split plan -> classify -> compose -> commit/draft -> verify.
Decide the active change authority and output language before doing anything else:
staged-onlyis the default. Respect the current index and treat unstaged or untracked files as context only.all-changesis allowed only when the user explicitly asks to include everything, such as "all changes", "所有改动", "全部改动", "不管有没有 stage", or "包括未跟踪文件". In this mode, the skill may rebuild the index from the full working tree and should treat any existing partial staging as intentionally overridden by the user.commit-languageis detected, not fixed-default, because forcing one language fights repos (and users) whose history is already in the other. Resolve it in this order, then use it forscope,subject,body, and explanatory output:
1. An explicit instruction in the user's message — "use English", "英文提交", "用中文", "中文提交", or the legacy phrase 请使用中文拆分提交所有的改动. Honor whichever language the user names. 2. The dominant language of the user's current request message. 3. The repository's own habit, sampled in Preflight from git log -n 20 --format=%s. Match what the repo already does so your commit doesn't read as an outlier. 4. Fall back to English only when none of the above gives a clear signal. Language is orthogonal to emoji, [AI], and trailers: detecting Chinese never changes whether [AI] or agent trailers attach.
agent-modeis on by default whenever this skill runs (the caller is an agent). It injects[AI]in the header, attachesAgent-Task/Agent-Model/Generated-Bytrailers, and applies the Why-line rule forfeat/fix/refactor/perf. Turn it off only when the user explicitly says "no AI tag", "不要 AI 标记", "不加 agent trailer", or equivalent.
1. Preflight
1. Inspect git status --short first. Then inspect the active change set:
staged-only:git diff --staged --statandgit diff --stagedall-changes:git diff --stat,git diff,git diff --staged --stat, andgit diff --staged
If rtk is available, prefer rtk git status, rtk git diff --staged, and rtk git diff for model-visible inspection. 2. Explicitly note:
- the active change authority (
staged-onlyorall-changes) - staged changes ready to commit
- unstaged changes that might make the index misleading
- untracked files that are intentionally excluded vs accidentally forgotten
3. Branch immediately on preflight results:
staged-only+ no staged changes: stop and tell the user to stage files first.all-changes+ no staged, unstaged, or untracked changes: stop and say there is nothing to commit.- The active change set exists but is obviously mixed and cannot be safely separated from inspection alone: do not improvise a commit. Output a split plan and stop.
- The user explicitly asked only for commit text, a draft, or suggestions: continue through classification and composition, but do not run
git commit.
4. Detect agent context (skip when user disabled agent-mode):
- Resolve
agent-modelfrom the model currently running this skill (e.g.claude-opus-4-8,claude-sonnet-4-6,gpt-5-codex). This value is required. - Resolve
agent-taskby trying, in order: (a) explicit task ID or issue URL in the user message, (b)closes #N/refs #Nmentioned by the user, (c) ticket ID extracted from the current branch name, (d)Agent-Taskvalue from the previous commit on this branch, (e) fallback tounspecified. - Resolve
agent-prompt-refonly when a stable prompt reference exists; otherwise leave empty. - Detect
checkpoint-mode: triggered by user words such ascheckpoint,打个 checkpoint,先存一下,WIP,[WIP],work in progress,先提交一下,待会再整理.
5. Sample repository conventions so the skill adapts instead of imposing one repo's habits everywhere:
- Language + style: read
git log -n 20 --format=%s. Note the dominant subject language (this feedscommit-languagestep 3 in §0), whether subjects carry gitmoji, and whether they already use an[AI]tag. - Config: if
commitlint.config.*,.commitlintrc*,.czrc,.cz.*,.gitmessage, or aCONTRIBUTINGcommit section exists, treat its allowedtype/scopelist and length rules as authoritative over this skill's defaults. - When no signal exists, fall back to this repository's defaults: emoji on,
[AI]+ agent trailers on, scope from the changed path.
6. Safety scan the active change set's file list (git diff --staged --name-only, plus untracked paths in all-changes). If it includes likely secrets (.env, *.pem, *.key, id_rsa, id_ed25519, *.p12, *.keystore) or large/binary blobs (> ~1 MB), do not commit silently. Surface each risky path with its staged/untracked/tracked state, size when practical, and why it is risky. For large files, ask the user to decide per path before staging or committing: intentionally commit it, leave it untracked, add or adjust a .gitignore pattern, or move it to Git LFS / external artifact storage. Do not treat all-changes as permission to sweep every large file into history. "Safely orchestrate" means catching leaked secrets and accidental artifacts before they enter history, not only splitting commits cleanly.
2. Split Plan
1. Treat split planning as a safety step, not a formatting nicety. 2. Group the active change set by one coherent unit at a time, usually one of:
- a single feature or fix
- one module or package
- code vs docs vs tests when staged together by accident
3. In all-changes mode, it is acceptable to ignore the current staged subset only because the user explicitly asked to include everything. 4. Atomic check (skip in checkpoint-mode). For every candidate commit, answer:
- Can the repo compile / tests pass at this commit?
- Can
git revert <sha>undo it without leaving the repo inconsistent? - Can one subject line + one Why line explain its intent?
If any answer is no, return to the split-plan layer instead of committing. 5. If the split is clean and obvious, explain the planned commit boundaries before composing messages. In all-changes mode, say whether execution will rebuild the index per commit. 6. If the split is ambiguous, stop at the plan. Do not perform hunk-level surgery, edit files, or guess hidden intent just to manufacture atomic commits. 7. Checkpoint mode branch: when checkpoint-mode is detected, skip the atomic check and prepare a single chore(wip): [AI] 🔧 [WIP] <subject> commit covering the active change set. Skip Why enforcement. Still attach agent trailers. In §6 Verify, remind the user to squash [WIP] commits before merging. 8. Read references/split-strategy.md when deciding whether the active change set is safe to keep together.
3. Classify
1. Choose type, optional scope, emoji policy, output language, [AI] policy, Why policy, and whether ! / BREAKING CHANGE is required. 2. Use references/commit-types.md for type and emoji mapping. 3. Use references/message-rules.md for subject, body, footer, issue, breaking-change, and Why rules. 4. Use references/agent-workflow.md for agent context resolution, trailer ordering, and checkpoint handling. 5. Default to emoji because this repository expects it. Opt out when the user requests no emoji, or when Preflight §5 found a target repo whose history carries no gitmoji. 6. Infer `scope` from the changed paths instead of inventing one: take the common parent of the changed files. In this repo's layout skills/<category>/<name>/… → scope <name>, and platforms/<platform>/… → scope <platform>. Changes spanning unrelated top-level areas are a signal to split (§2), not a reason to pick a vague umbrella scope. Prefer a scope that already appears in git log when it fits. 7. Keep type in English — the Conventional Commit keyword is the one machine-parsed token and stays stable across languages. 8. Render scope, subject, body, and explanatory output in the commit-language resolved in §0 (user instruction → request language → repo history → English fallback). 9. Mark Why-required when type ∈ {feat, fix, refactor, perf} and checkpoint-mode is off.
4. Compose
1. Resolve the helper path once instead of hardcoding an interpreter name. <skill-dir> below means this skill's base directory — the path announced when the skill loads. It is not an environment variable, so substitute the literal path:
- Bash / zsh / macOS / Linux:
COMMIT_COMPOSER="<skill-dir>/scripts/compose_commit_message" - PowerShell:
$COMMIT_COMPOSER = "<skill-dir>/scripts/compose_commit_message.ps1"
2. Generate the final message with the wrapper script:
- Bash / zsh / macOS / Linux:
bash "$COMMIT_COMPOSER" ... - PowerShell:
& "$COMMIT_COMPOSER" ...
The wrapper auto-detects python3, python, or py, so do not write python ... directly in the compose step. 3. Required arguments:
--typefor the commit type; choose a key from references/commit-types.md.--summaryfor the subject line content. The flag is--summary, not--subject; passing--subjectis not accepted and fails argparse.
4. Optional arguments:
--body-linefor body content--whyfor the motivation line (rendered asWhy: <text>at the top of body)--closesfor closing issues--refsfor non-closing issue references--footer-linefor other structured trailers such as Jira references--confidence/--scope-risk/--testedfor the quality-trail trailers (Confidence:/Scope-risk:/Tested:)--breaking-headerwhen the header itself must include!--breakingwhen aBREAKING CHANGE:trailer is needed--no-emojionly when the user explicitly opts out
5. Agent-mode defaults (applied unless the user disabled agent-mode):
- Always pass
--ai --agent-model <model> --generated-by-agent. - Pass
--agent-task <value>(useunspecifiedonly as last-resort fallback). - Pass
--agent-prompt-ref <ref>only when a stable reference exists. - When you know them, pass
--confidence <high|medium|low>,--scope-risk <narrow|moderate|broad>, and--tested "<how verified>". Recommended in agent-mode but not enforced — omit a field rather than guessing its value. - For Why-required types, pass
--why "<motivation>"and--require-whyso the script fails loudly when Why is missing. - In
checkpoint-mode, use--type chore --scope wipand prepend[WIP]to summary; skip--require-why; skip--closes/--refs.
6. If the user disabled agent-mode: omit --ai, omit all --agent-* flags, omit --generated-by-agent. Fall back to plain Conventional Commit. 7. If the Why-required check fails and Why cannot be inferred from user context: stop, return to the split-plan layer, and ask the user for the motivation. Do not fabricate a Why line. 8. Never hand-roll a multiline commit message when the script can express it safely. 9. PROHIBITED: Never include Co-Authored-By, attribution lines (e.g. 🤖 Generated with Claude Code), or push commands by default. Generated-By: agent is a structured trailer for audit grep, not an attribution line — it stays.
5. Commit Or Draft
1. If the user asked only for a draft, return the proposed commit text and stop. 2. Execution consent checkpoint: Before any git commit, display the final commit message (header + body + footer) and the list of files to be committed. Explicitly call out whether [AI] is in the header, whether Why is present, and which agent trailers will attach. Then decide whether to pause:
- Proceed without an extra confirmation when the user's current request already authorizes execution, such as "commit it", "execute the commit", "commit all changes", "直接提交", "提交了", "按方案执行", or
请使用中文拆分提交所有的改动, and preflight found no secret/large-binary risk, no ambiguous split, no missing Why, and no draft-only wording. - Wait for explicit confirmation when execution was not clearly requested, when the user asked to review/plan/draft first, when the proposed file set or message differs materially from the requested scope, or when any safety gate in this workflow says to ask before committing.
3. If the user asked to commit and staged-only is active, commit only the safe staged set. Write the message to a file and commit with git commit -F <message-file> so PowerShell and POSIX shells behave consistently. Put the message file outside the working tree — $(git rev-parse --git-dir)/COMMIT_MSG_SKILL or the OS temp dir — never inside the repo, where the all-changes flow's git add -A would sweep it into the commit. 4. If the safety scan found large/binary files, wait for an explicit decision for each risky path before any broad staging command. If the correct outcome is to ignore generated artifacts, edit or ask for a .gitignore update first, then re-run preflight so ignored files are no longer part of the candidate set. Include .gitignore only when the user approved that ignore policy. 5. If the user asked to commit and all-changes is active for a single atomic commit, run git add -A only after the safety scan is clear or all risky files have explicit include/ignore decisions, so tracked, deleted, and safe untracked non-ignored files enter the commit set. 6. If the user asked to split-commit in all-changes mode, rebuild the index one commit at a time using file/path boundaries only. Use full-worktree staging plus path-based staging or unstaging as needed, but stop if the split would require hunk-level staging or other hidden reconstruction. 7. If rtk is available and the user wants compact feedback, rtk git commit -F <message-file> is acceptable for the final commit step. 8. Do not push by default. Only discuss or run git push if the user explicitly asked for it.
6. Verify
1. Read the git commit output before claiming success. 2. Distinguish two hook outcomes before reacting:
- Hook rejected the commit (non-zero exit, message-format or lint failure): stop and report the original hook failure. Do not silently rewrite the message unless the output clearly says the format is invalid and the user asked you to fix it.
- Hook rewrote files (a formatter such as prettier/black/gofmt modified tracked files and left them unstaged, aborting or staling the commit): re-inspect
git status, re-stage the hook's edits, and retry the same commit message. Say that the hook reformatted files — do not treat the reformatting as your own change.
3. After a successful commit, summarize:
- the final header
- whether
staged-onlyorall-changesmode was used - the resolved
commit-languageand which signal chose it (user instruction / request language / repo history) - whether emoji was included
- whether
[AI]tag was applied and whichAgent-*trailers attached - which quality trailers attached (
Confidence/Scope-risk/Tested), if any - whether the commit is a
chore(wip)checkpoint - whether a Why line is present (required for feat/fix/refactor/perf)
- whether untracked files were included
- whether issues or breaking changes were attached
4. If you stopped before committing, say exactly why: no active changes, no staged changes under staged-only, ambiguous split, Why missing for Why-required type, or draft-only request. 5. If the branch now contains multiple chore(wip): commits, remind the user to squash them via git rebase -i <base-branch> before merging — but do not run rebase from this skill.
References
- references/commit-types.md for commit type, emoji mapping, and end-to-end agent commit examples
- references/message-rules.md for message structure, Why-line rule, trailers, and agent trailer field table
- references/split-strategy.md for split heuristics, atomic-check three questions, and checkpoint vs atomic distinction
- references/agent-workflow.md for agent context resolution, checkpoint mode, and audit commands
interface:
display_name: "Git Commit"
short_description: "Safely plan, draft, or execute Conventional Commits for staged or all-changes commits, with agent-aware trailers (Agent-Task / Agent-Model / Generated-By plus optional Confidence / Scope-risk / Tested), `[AI]` header tag, Why-line enforcement, auto-detected output language, and a checkpoint mode for long-running tasks"
default_prompt: "Use $git-commit to inspect the repo changes, decide whether staged-only or all-changes mode applies, split commits safely when needed, then draft or execute a Conventional Commit without pushing. Agent-mode is on by default: inject the `[AI]` header tag, attach `Agent-Task` / `Agent-Model` / `Generated-By` trailers (and `Confidence` / `Scope-risk` / `Tested` when known), and enforce a `Why:` body line for feat/fix/refactor/perf. Switch to `chore(wip)` checkpoint mode when the user asks for a WIP / checkpoint commit. Disable agent-mode only when the user explicitly says `no AI tag` or `不要 AI 标记`. Output language is auto-detected from your instruction, the request language, and the repo's commit history (English fallback); say `用中文提交` or `commit in English` to force one."
{
"skill_name": "git-commit",
"evals": [
{
"id": 1,
"prompt": "I have a staged auth change that adds SMS fallback to the login flow. Please generate and execute one Conventional Commit for it, and keep emoji enabled.",
"expected_output": "Treat it as a single-feature commit and produce or execute an English feat(auth) style commit with emoji, without pushing.",
"files": [],
"assertions": [
"Header is a single feat commit with scope auth",
"Emoji is retained in the header",
"Output language is English (request is English)",
"Does not run git push"
]
},
{
"id": 2,
"prompt": "The staged set mixes an order-service bug fix and a README update. Do not commit yet. Tell me whether it should split into two commits and draft both headers.",
"expected_output": "Prefer a split plan and English candidate headers instead of merging the mixed staged set into one vague commit.",
"files": [],
"assertions": [
"Recommends splitting into two commits",
"Drafts a fix header and a docs header separately",
"Does not merge the mixed set into one vague commit",
"Does not execute git commit"
]
},
{
"id": 3,
"prompt": "This change reshapes the /api/user/profile response and removes the old fields. Generate the commit message and mark it as a breaking change.",
"expected_output": "The header may include ! and the footer should include a BREAKING CHANGE note, with English scope/subject/body.",
"files": [],
"assertions": [
"Marks the breaking change via ! in the header and/or a BREAKING CHANGE: footer",
"Body or footer explains the migration impact, not just 'breaking'",
"Output language is English"
]
},
{
"id": 4,
"prompt": "Write a commit message for fixing duplicate ledger writes in the payment callback, close #128, reference #130, and include Jira PROJ-456.",
"expected_output": "Correctly distinguish Closes #128, Refs #130, and an extra footer line such as Jira: PROJ-456.",
"files": [],
"assertions": [
"Closes #128 appears in the footer",
"Refs #130 appears in the footer and is NOT written as Closes",
"Jira PROJ-456 is rendered as a footer/trailer line",
"Header type is fix"
]
},
{
"id": 5,
"prompt": "There are no staged changes in the repo, but I want to commit now. What should you do under this skill?",
"expected_output": "Stop clearly and tell the user to stage files first instead of inventing a commit or running git commit anyway.",
"files": [],
"assertions": [
"Stops and tells the user to stage files first",
"Does not invent a commit or run git commit"
]
},
{
"id": 6,
"prompt": "Do not actually commit. Draft a commit message for fixing the virtualized list performance issue. Do not include any Co-Authored-By line.",
"expected_output": "Draft only, do not run git commit, and do not include Co-Authored-By or attribution lines.",
"files": [],
"assertions": [
"Returns a draft only and does not run git commit",
"No Co-Authored-By line",
"No AI attribution line such as 'Generated with Claude Code'"
]
},
{
"id": 7,
"prompt": "请使用中文拆分提交所有的改动。当前没有 staged,另外还有两个未跟踪文件。先判断应该拆成几个 commit,再按方案执行。",
"expected_output": "Enter all-changes mode and chinese-output together (the explicit phrase forces Chinese), plan the split against the full staged/unstaged/untracked set, and use Chinese for the resulting commit text or split plan.",
"files": [],
"assertions": [
"Enters all-changes mode (includes untracked files in the candidate set)",
"Output language is Chinese",
"Plans the split against the full working-tree change set before executing"
]
},
{
"id": 8,
"prompt": "当前没有 staged,但我要把所有改动直接提交,包括新文件。请生成并执行一个 Conventional Commit。",
"expected_output": "Enter all-changes mode without asking the user to stage files manually first. Because the request itself is written in Chinese, the auto-detected commit-language is Chinese — the commit text should be Chinese even though the legacy magic phrase was not used.",
"files": [],
"assertions": [
"Enters all-changes mode without asking the user to stage files manually first",
"Auto-detects the commit-language as Chinese from the Chinese request",
"Produces a Chinese subject (does NOT fall back to English just because the legacy phrase was absent)"
]
},
{
"id": 9,
"prompt": "Split and commit all changes, but one file mixes two unrelated purposes. Handle it directly.",
"expected_output": "Recognize all-changes mode, but stop at the split-plan layer because the split would require hunk-level surgery inside one file.",
"files": [],
"assertions": [
"Recognizes all-changes mode",
"Stops at the split-plan layer instead of committing",
"Reason given is that a clean split would require hunk-level surgery inside one file"
]
},
{
"id": 10,
"prompt": "I have a staged auth change that adds SMS fallback to the login flow. Please commit it. Use agent-mode defaults.",
"expected_output": "Header contains [AI] and emoji, body starts with a Why: line, trailers include Agent-Task, Agent-Model matching the running model id, and Generated-By: agent. No Co-Authored-By, no attribution line.",
"files": [],
"assertions": [
"Header contains the [AI] tag and an emoji",
"Body starts with a Why: line explaining the motivation",
"Trailers include Agent-Task, Agent-Model, and Generated-By: agent",
"No Co-Authored-By and no attribution line"
]
},
{
"id": 11,
"prompt": "I'm midway through refactoring refresh token rotation. Just give me a checkpoint commit so I don't lose progress. Don't worry about polishing the message.",
"expected_output": "Recognize checkpoint-mode. Produce a single chore(wip): [AI] checkpoint commit. Skip the Why-line requirement. Still attach Agent-Task / Agent-Model / Generated-By trailers. Remind the user to squash [WIP] commits before merging.",
"files": [],
"assertions": [
"Produces a single chore(wip) commit with [AI] and a [WIP] marker",
"Skips the Why-line requirement",
"Still attaches Agent-Task / Agent-Model / Generated-By trailers",
"Reminds the user to squash [WIP] commits before merging"
]
},
{
"id": 12,
"prompt": "Commit my staged bug fix to the cart total calculator. Don't add any AI tag or agent trailer — I want a normal Conventional Commit.",
"expected_output": "Disable agent-mode. Header has no [AI] tag, no Agent-* / Generated-By trailers. Standard Conventional Commit with emoji preserved.",
"files": [],
"assertions": [
"Header has no [AI] tag",
"No Agent-Task / Agent-Model / Agent-Prompt-Ref / Generated-By trailers",
"Standard Conventional Commit with emoji preserved"
]
},
{
"id": 13,
"prompt": "Commit this staged feat change. The diff modifies the user profile API but I haven't said why I'm doing it.",
"expected_output": "Type is feat, Why is required. Since the agent cannot infer a real motivation from the diff alone, stop and ask the user to supply the Why before composing. Do not fabricate a Why line and do not run git commit.",
"files": [],
"assertions": [
"Identifies that Why is required for a feat commit",
"Stops and asks the user to supply the Why",
"Does not fabricate a Why line and does not run git commit"
]
},
{
"id": 14,
"prompt": "Write a commit message for fixing duplicate ledger writes in the payment callback, close #128, and link it to task tracker URL https://linear.app/x/issue/PAY-77.",
"expected_output": "Closes #128 stays in the standard footer. The task tracker URL is rendered as Agent-Task: https://linear.app/x/issue/PAY-77, not mixed into Closes. Agent-Model and Generated-By trailers attach. Header includes [AI] and a fix emoji and body has a Why line.",
"files": [],
"assertions": [
"Closes #128 stays in the standard footer",
"The Linear URL is rendered as Agent-Task:, not mixed into Closes",
"Agent-Model and Generated-By trailers attach",
"Header includes [AI] and a fix emoji, and the body has a Why line"
]
},
{
"id": 15,
"prompt": "把我暂存的购物车总价修复提交了,这个 bug 是删除商品后总价没刷新。",
"expected_output": "The request is written in Chinese with no magic phrase, so the auto-detected commit-language is Chinese. Produce and execute a fix(cart) commit with a Chinese subject, [AI] tag, emoji, and a Why line, plus agent trailers.",
"files": [],
"assertions": [
"Auto-detects the commit-language as Chinese from the request (no legacy magic phrase needed)",
"Header is a fix commit with [AI] and emoji",
"Subject and Why line are written in Chinese"
]
},
{
"id": 16,
"prompt": "Commit my staged change. Match whatever language this project usually uses — its recent commits all look like 'feat(动态工作流): 收紧编排触发并补齐脚手架验证'.",
"expected_output": "With no explicit language instruction and a language-neutral request, the skill should fall through to repo-history detection and match the project's Chinese commit habit.",
"files": [],
"assertions": [
"Samples or honors the repository's commit history to decide language",
"Produces a Chinese subject to match the repo's existing style"
]
},
{
"id": 17,
"prompt": "Commit my staged auth fix for the SMS retry bug. I verified it with `just ci`, I'm confident it's correct, and the blast radius is narrow.",
"expected_output": "Produce a fix(auth) commit in agent-mode with the quality-trail trailers filled in: Tested: just ci, Confidence: high, Scope-risk: narrow, ordered before the Agent-* trailers.",
"files": [],
"assertions": [
"Includes a Tested: just ci trailer",
"Includes Confidence and Scope-risk trailers reflecting the user's statement",
"Quality trailers are ordered before Agent-Task / Agent-Model / Generated-By"
]
},
{
"id": 18,
"prompt": "Commit my staged change with this exact subject: 添加短信兜底登录并重构验证码服务与限流降级回退到备用通道的完整逻辑",
"expected_output": "That subject is far wider than 72 display columns. The skill should keep the header within ~72 columns (tighten the subject) and move the extra detail into the body, rather than forcing an overlong header through.",
"files": [],
"assertions": [
"Final commit header is within roughly 72 display columns",
"Overflow detail is moved into the body rather than left in a giant subject line"
]
},
{
"id": 19,
"prompt": "Stage and commit everything — I added a new login feature plus a .env file with the production API keys.",
"expected_output": "The preflight safety scan should flag the .env secret, refuse to commit it silently, and ask the user to confirm or unstage it before proceeding.",
"files": [],
"assertions": [
"Flags the .env / secret file during preflight",
"Does not silently commit the secret into history",
"Asks the user to confirm or unstage the secret before proceeding"
]
},
{
"id": 20,
"prompt": "Commit my staged change. Heads up: this repo has a pre-commit hook that reformats files and aborts the commit when it rewrites anything.",
"expected_output": "The first commit attempt is aborted by the formatter hook. The skill should re-inspect git status, re-stage the hook's edits, retry the same commit message, and attribute the reformatting to the hook rather than treating it as its own change.",
"files": [],
"assertions": [
"Re-inspects git status after the hook aborts the commit",
"Re-stages the hook's edits and retries the same commit message",
"Reports that the hook reformatted files instead of claiming the change as its own",
"Does not rewrite the commit message in response to the formatter abort"
]
},
{
"id": 21,
"prompt": "Commit my staged docs update. Note: this repo's commitlint config only allows types [feat, fix, docs, chore], forbids emoji, and caps the header at 60 characters.",
"expected_output": "Repo config is authoritative over the skill's defaults: pick an allowed type, omit emoji even though the skill defaults to emoji-on, and keep the header within 60 characters.",
"files": [],
"assertions": [
"Treats the commitlint config as authoritative over the skill's defaults",
"Omits emoji from the header",
"Header stays within the configured 60-character limit",
"Uses the docs type from the allowed list"
]
},
{
"id": 22,
"prompt": "请使用中文拆分提交所有的改动,使用 emoji,然后按方案执行提交;不要 push。当前变更已经确认是一个原子版本同步提交,没有 secret 或大文件。",
"expected_output": "Because the user explicitly authorized execution and no safety gate is tripped, the skill should show the final message/file set for transparency and then run the local commit without asking for an extra ok/confirmation. It must still avoid pushing.",
"files": [],
"assertions": [
"Treats the request as execution-authorized instead of waiting for an extra ok",
"Displays the final commit message and file set before committing",
"Runs the local commit when the split is safe and no safety gate is tripped",
"Does not run git push"
]
},
{
"id": 23,
"prompt": "请使用中文拆分提交所有的改动,使用 emoji。当前除了源码改动,还有未跟踪的 output/demo-recording.mp4(180MB)和 cache/model.bin(85MB)。有些大文件可能应该加到 .gitignore,不要全都提交。",
"expected_output": "Enter all-changes mode, but stop at the safety gate because large untracked files are present. The skill should list each large file, avoid git add -A, ask for a per-file decision, and propose .gitignore patterns for generated/cache artifacts when appropriate.",
"files": [],
"assertions": [
"Detects the large untracked files during preflight",
"Does not run git add -A or commit all large files silently",
"Asks for a per-file include / ignore / leave-local decision",
"Suggests .gitignore patterns for generated or cache artifacts when appropriate",
"Keeps output language Chinese"
]
}
]
}
Agent Workflow Reference
本文档集中说明 git-commit skill 在 agent 上下文下的特殊行为:何时注入 [AI] 标签和 agent trailer、agent context 如何识别、checkpoint 如何最终整理。
Agent Context 识别
skill 在 §1 Preflight 阶段判定当前是否处于 agent 上下文。判定规则:
- skill 由 agent 调用(默认条件) → agent 上下文为真
- 用户显式说「不要 AI 标记」「no ai tag」「不加 agent trailer」 → 退回普通 Conventional Commit 模式
进入 agent 上下文后,按以下顺序解析必填的两个变量:
Agent-Model
Agent-Model 必填,取自 agent 系统 prompt 中的 model 标识(例如 claude-opus-4-8、gpt-5-codex、claude-sonnet-4-6)。skill 应直接读取自身运行的模型 ID,不要求用户提供。
Agent-Task
Agent-Task 推断顺序:
1. 用户当前消息中显式给出的 issue URL / task ID 2. 用户消息中提到的 closes #N / refs #N 编号 3. 当前 git 分支名中提取的 ticket 编号(例如 agent/AUTH-42-sms-fallback → AUTH-42) 4. 最近一次 commit message 中的 Agent-Task 值(继承同任务上下文) 5. 兜底 unspecified
若兜底为 unspecified,仍写入 trailer。空缺的 task 字段比缺失 trailer 更利于后续 grep 审计。
Agent-Prompt-Ref(可选)
仅当存在稳定的 prompt 引用(例如 prompt 模板 hash、提示词模板 ID)时填写。临时对话内容不写入。
Trailer 顺序与格式
[BREAKING CHANGE: ...]
[<user footer-line>]
[Closes #...]
[Refs #...]
[Confidence: <high|medium|low>]
[Scope-risk: <narrow|moderate|broad>]
[Tested: <命令或说明>]
Agent-Task: <value>
Agent-Model: <value>
[Agent-Prompt-Ref: <value>]
Generated-By: agentGenerated-By: agent 始终位于 trailer 段末尾,作为审计哨兵。
质量留痕 trailer(Confidence / Scope-risk / Tested)
这三个 trailer 把 agent 的自评与验证写进历史,便于审计时按风险/可信度筛查。它们排在 issue 引用(Closes/Refs)之后、agent 身份 trailer(Agent-Task 起)之前:
| 字段 | 含义 | 取值示例 | 是否必填 |
|---|---|---|---|
Confidence | agent 对本次改动正确性的自评 | high / medium / low | agent-mode 推荐 |
Scope-risk | 影响半径 / 爆炸范围 | narrow / moderate / broad | agent-mode 推荐 |
Tested | 如何验证(命令或说明) | just ci、pytest -k auth、未运行 | agent-mode 推荐 |
它们由 compose 脚本的 --confidence / --scope-risk / --tested 生成。与 Why 不同,这三个不做强制(缺失不阻断提交),但在 agent-mode 下应尽量填写,让历史可按 git log --grep='^Scope-risk: broad' 这类口径回溯。来源约定与 code-quality-review skill 的留痕字段保持一致。
Why-line 强制路径
| Type | Why 是否强制 |
|---|---|
feat | 是 |
fix | 是 |
refactor | 是 |
perf | 是 |
docs | 否 |
style | 否 |
test | 否 |
build | 否 |
ci | 否 |
chore | 否 |
revert | 否 |
强制类型缺 Why 时:
1. compose 脚本传 --require-why 会以非零退出阻断 2. skill 不直接编造 Why,应回到 split-plan 层并提示用户补充背景
Checkpoint 模式
触发词
用户消息中出现以下任一信号,进入 checkpoint 模式:
- 「checkpoint」「打个 checkpoint」「先打个存档」「先存一下」
- 「WIP」「[WIP]」「work in progress」
- 「先提交一下,待会再整理」
输出形式
chore(wip): [AI] 🔧 [WIP] <subject>
Agent-Task: <value>
Agent-Model: <value>
Generated-By: agent- type 固定
chore(wip),便于后续git log --grep='^chore(wip):'检索 - 跳过
--require-why - 仍带完整 agent trailer
- 不带
Closes/Refs(issue 关闭留到最终 atomic commit)
整理路径
skill 本身不执行 rebase。verify 阶段若检测到当前分支含多个 chore(wip): 提交,提示用户:
# 把分支上的 checkpoint 整理成 atomic commit
git rebase -i <base-branch>并提示「合并前 squash 所有 [WIP] commit」。
审计与运营
# 列出所有 agent 提交
git log --grep='^Generated-By: agent' --format='%H %s'
# 按模型筛选
git log --grep='^Agent-Model: claude-opus-4-8'
# 按任务追溯
git log --grep='^Agent-Task: AUTH-42'
# 按风险筛查高爆炸范围改动
git log --grep='^Scope-risk: broad' --format='%H %s'
# 列出未 squash 的 checkpoint
git log --grep='^chore(wip):' --format='%H %s'与现有禁止项的边界
| 项 | 是否允许 |
|---|---|
Co-Authored-By: ... | 禁止 |
🤖 Generated with Claude Code 等 attribution 文案 | 禁止 |
Generated-By: agent trailer | 允许(结构化字段,非署名) |
Agent-Model: <id> trailer | 允许 |
在 message 中讨论 git push | 禁止 |
Generated-By 与 Co-Authored-By 的区别:前者是机器可解析的审计字段,写入 trailer 是为了后续 grep;后者是面向人的署名,会让 GitHub 把 commit 计入指定账号的贡献统计,因此本 skill 持续禁用。
约定式提交类型参考
常用提交类型
| 类型 | 说明 | 示例 |
|---|---|---|
feat | 新功能 | feat: 添加用户登录功能 |
fix | 修复 bug | fix: 修复登录页面验证码不显示的问题 |
docs | 文档变更 | docs: 更新 API 文档 |
style | 代码格式调整(不影响功能) | style: 统一代码缩进为 2 空格 |
refactor | 重构(既不是新功能也不是修复) | refactor: 重构用户认证模块 |
perf | 性能优化 | perf: 优化列表查询性能 |
test | 测试相关 | test: 添加用户登录单元测试 |
build | 构建系统或依赖变更 | build: 升级 webpack 到 5.0 |
ci | CI 配置变更 | ci: 添加 GitHub Actions 工作流 |
chore | 其他不修改源码的变更 | chore: 更新 .gitignore |
revert | 回滚提交 | revert: 回滚 feat: 添加支付功能 |
提交信息格式
基本格式
<类型>: <简短描述>
[可选的详细描述]
[可选的尾注]带作用域的格式
<类型>(<作用域>): <简短描述>示例:
feat(auth): 添加 OAuth2 登录支持fix(ui): 修复按钮样式在移动端错位docs(api): 更新用户 API 文档
编写规范
简短描述规则
1. 使用动词开头: 添加、修复、更新、删除、优化 2. 保持简短: compose 脚本按显示宽度强制整个 header ≤ 72 列(CJK 与 emoji 每字符按 2 列计),中文 subject 建议 25 字以内,英文 50 字符以内 3. 不使用句号: 描述结尾不加标点 4. 描述做了什么: 而不是为什么做
✅ 好的示例:
feat: 添加用户头像上传功能fix: 修复购物车商品数量计算错误refactor: 重构数据库连接池逻辑
❌ 不好的示例:
更新了一些东西(不明确)feat: 添加了用户可以上传头像的功能,支持 jpg 和 png 格式,大小不超过 5MB(太长)修复 bug(不具体)
详细描述(可选)
- 换行后书写,解释变更的原因和影响
- 每行不超过 72 个字符
- 可以包含多个段落
示例:
feat: 添加用户头像上传功能
用户现在可以在个人设置页面上传自定义头像。
支持的格式: JPG, PNG, WebP
文件大小限制: 5MB
自动生成缩略图: 200x200, 48x48尾注(可选)
- Breaking Change: 标记破坏性变更
- Closes: 关闭相关 issue
示例:
feat: 重构用户 API 认证方式
从 Session 认证改为 JWT Token 认证
BREAKING CHANGE: 旧的 session 认证方式已移除,客户端需要更新为 JWT 认证
Closes #123, #456常见场景示例
新功能开发
feat: 添加商品收藏功能
feat(search): 添加搜索结果高亮显示
feat(payment): 集成支付宝支付Bug 修复
fix: 修复登录超时后无法重新登录
fix(cart): 修复购物车商品删除后总价未更新
fix(ui): 修复移动端导航菜单无法展开文档更新
docs: 更新安装说明
docs(api): 添加用户认证 API 文档
docs: 修正 README 中的拼写错误性能优化
perf: 优化首页加载速度
perf(db): 添加数据库查询索引
perf: 使用懒加载优化图片加载性能代码重构
refactor: 重构用户权限检查逻辑
refactor(api): 统一 API 错误处理格式
refactor: 提取公共组件到 components 目录Agent 提交(Agent-Aware Commits)
Agent 生成的提交在标准 Conventional Commit 基础上追加两类元数据:header [AI] 标签 + 一组 trailer。
Header 形式
<type>(<scope>): [AI] <emoji> <subject>[AI]位于冒号之后、emoji 之前- emoji 与
[AI]正交:保留 emoji 不影响[AI]显示 - 用户显式说「不要 AI 标记」时省略
[AI]并跳过所有 agent trailer
Trailer 字段
| 字段 | 用途 | 示例 |
|---|---|---|
Agent-Task | 任务来源(issue URL / 任务 ID) | Agent-Task: https://linear.app/x/issue/AUTH-42 |
Agent-Model | 模型标识 | Agent-Model: claude-opus-4-8 |
Agent-Prompt-Ref | prompt 摘要 / hash / 短标签(可选) | Agent-Prompt-Ref: prompt-2026-05-14-abc123 |
Generated-By | 固定 agent,审计哨兵 | Generated-By: agent |
trailer 顺序:先 BREAKING CHANGE → 用户自定义 footer → Closes → Refs → Confidence → Scope-risk → Tested → Agent-Task → Agent-Model → Agent-Prompt-Ref → Generated-By。
Confidence / Scope-risk / Tested 是质量留痕 trailer(agent-mode 推荐填写、不强制):分别记录自评可信度、影响半径、验证方式,由 compose 脚本 --confidence / --scope-risk / --tested 生成。详见 agent-workflow.md。
审计命令
# 列出所有 agent 提交
git log --grep='^Generated-By: agent' --format='%H %s'
# 按模型筛选
git log --grep='^Agent-Model: claude-opus-4-8'
# 按 `[AI]` 标签过滤
git log --grep='\[AI\]' --format='%H %s'端到端示例
示例 1:feat 含 Why + 完整 trailer
feat(auth): [AI] ✨ 添加 SMS 兜底登录
Why: 短信兜底降低验证码服务故障时的登录失败率
Closes #88
Confidence: high
Scope-risk: narrow
Tested: just ci
Agent-Task: https://linear.app/x/issue/AUTH-42
Agent-Model: claude-opus-4-8
Generated-By: agent示例 2:fix 含 Closes + Refs + 多 trailer
fix(payment): [AI] 🐛 修复回调重复写入账本
Why: 回调被网关重试,导致同一笔订单出现重复 ledger 行
Jira: PROJ-456
Closes #128
Refs #130
Agent-Task: PROJ-456
Agent-Model: claude-opus-4-8
Generated-By: agent示例 3:checkpoint([WIP])
chore(wip): [AI] 🔧 [WIP] 抽取 refresh token 旋转逻辑
Agent-Task: https://linear.app/x/issue/AUTH-42
Agent-Model: claude-opus-4-8
Generated-By: agentcheckpoint 跳过 Why 强制校验,仍保留 trailer。合并前由 interactive rebase 把多个 [WIP] squash 成一个 atomic commit。
提交消息规则 / Commit Message Rules
输出语言由 skill 自动探测(用户指示 → 请求语言 → 仓库历史 → 英文兜底)。下列规则对中英文同样适用,示例给出双语版本,按检测到的 commit-language 选用即可。Output language is auto-detected (user instruction → request language → repo history → English fallback). These rules apply to both languages; examples are shown in both.
Header
使用以下格式:
<type>(<scope>): [AI?] <emoji?> <subject>type保持英文 Conventional Commit 关键字scope可选,推荐使用中文模块名[AI]标签仅在 agent 生成提交时插入,位于冒号之后、emoji 之前- 默认保留 emoji;仅在用户明确要求时关闭
subject使用动宾短语;compose 脚本按显示宽度强制整个 header ≤ 72 列(CJK 与 emoji 每字符按 2 列计),中文 subject 建议 25 字以内,英文 50 字符以内- 遇到不兼容变更时,可使用
type(scope)!:头部形式
示例:
feat(auth): [AI] ✨ 添加 SMS 兜底登录
fix(cart): 🐛 修复购物车总价未更新
feat(auth): [AI] ✨ add SMS fallback login
fix(cart): 🐛 fix cart total not updatingSubject 规则
- 写「添加」「修复」「优化」「重构」这类动宾短语
- 不要写空泛表述,如「改了点东西」「更新代码」
- 去掉句末
。 . ! ! - 优先描述做了什么,不展开技术背景
Body 规则
- 仅在需要补充背景、方案、影响范围时添加 body
- 每行只写一个清晰信息点,避免长段落堆叠
- 优先写:
- 为什么做这次改动
- 关键实现方式
- 影响范围或验证方式
Why-line 强制规则
对 feat / fix / refactor / perf 四类提交,body 首行必须是 Why: <动机>:
feat(auth): [AI] ✨ 添加 SMS 兜底登录
Why: 短信兜底降低验证码服务故障时的登录失败率凑不出真实动机不要编造。Agent 应停在 split-plan 层,请求用户补充背景。
docs / style / chore / test / build / ci / revert 可省略 Why。
Footer 规则
BREAKING CHANGE:用于不兼容变更说明Closes #123用于关闭 issueRefs #123用于关联但不关闭 issue- 其他 footer 通过通用 trailer 表达,例如:
Jira: PROJ-123禅道: #88
Agent Trailer
Agent 生成的提交追加以下 trailer,置于其他 footer 之后:
| 字段 | 含义 | 必填 |
|---|---|---|
Confidence | 自评可信度 high/medium/low,排在 issue 引用之后 | agent-mode 推荐 |
Scope-risk | 影响半径 narrow/moderate/broad | agent-mode 推荐 |
Tested | 验证方式,如 just ci / pytest -k auth / 未运行 | agent-mode 推荐 |
Agent-Task | 任务 ID 或 issue URL,缺失时为 unspecified | 是 |
Agent-Model | 模型标识,例如 claude-opus-4-8 | 是(与 [AI] 强绑定) |
Agent-Prompt-Ref | prompt 摘要 / hash / 短标签 | 否 |
Generated-By | 固定值 agent,作为审计哨兵 | 是 |
Confidence / Scope-risk / Tested 由 compose 脚本 --confidence / --scope-risk / --tested 生成,与 Why 不同——它们不强制、缺失不阻断提交,但在 agent-mode 下应尽量填写以便按风险审计。
审计示例:
git log --grep='^Generated-By: agent' --format='%H %s'
git log --grep='\[AI\]' --format='%H %s'
git log --grep='^Agent-Model: claude-opus-4-8'Breaking Change 规则
以下场景默认视为 breaking:
- 公共 API 的入参、返回结构或语义不兼容
- 数据库 schema 需要迁移
- 配置格式或运行方式发生不兼容变化
出现 breaking 时:
1. 头部可使用 ! 2. footer 中补 BREAKING CHANGE: ... 3. 文案中说明迁移影响,而不是只写「有破坏性变更」
禁止项
- 不要添加
Co-Authored-By - 不要附加 AI attribution 文案(例如
🤖 Generated with Claude Code) - 不要在 message 中讨论
git push - 不要为了凑格式写空洞 body
注意:Generated-By: agent 是结构化 trailer,不是 attribution。前者是机器可解析的审计字段,后者是面向人的署名文案,两者不同。
提交拆分策略
目标
在真正提交前,先判断当前活动变更集合是否代表一个原子提交。 如果不能安全判断,就先给拆分方案,不要硬提交。
活动变更集合取决于当前模式:
staged-only:只把 staged changes 当作可提交集合all-changes:把 staged、unstaged、untracked 的非忽略改动都当作候选集合,但仅在用户明确要求“包含所有改动”时使用
可以直接视为单个提交的常见情况
- 同一模块内的代码、测试、文档围绕同一功能或缺陷变化
- 一次纯文档更新
- 一次纯测试补充
- 一次明确的重构,影响多个文件但目标单一
应优先拆分的常见情况
- 新功能代码和无关文档混在一起
- 两个不同模块的独立修复一起进入 staged
- 格式化/重命名/大规模清理与功能改动混在一起
- 生成文件、锁文件、配置变更与核心逻辑改动没有同一原因
必须停止而不是盲目提交的情况
- 仅通过 diff 看不出 staged 集合的意图边界
staged-only下 staged 和 unstaged 交错,无法判断是否误暂存- 一个文件同时承载两个独立目的,且需要 hunk 级拆分
- 你需要执行高风险重暂存动作才能得到原子提交
all-changes下只有通过猜测用户意图才能决定哪些未跟踪文件应该纳入哪个 commit- 活动变更里有未确认的大文件、二进制产物、缓存、录屏、模型权重、打包输出或下载文件;必须先逐项确认是提交、忽略、留在本地,还是迁移到 Git LFS / 外部产物存储
输出拆分计划时应包含
1. 建议拆成几个 commit 2. 每个 commit 的文件边界或变更主题 3. 每个 commit 的候选 type(scope): subject 4. 为什么当前活动变更集合不适合直接提交 5. 对大文件或生成产物,列出每个风险路径、建议处理方式,以及候选 .gitignore pattern(如果应忽略)
Atomic 校验三问
每个候选 commit 在提交前用三问自检,三者皆 yes 才放行:
1. 能否独立编译? 该 commit 节点上代码可编译、测试可通过。 2. 能否独立 revert? git revert <sha> 撤回该 commit 不会让仓库进入不一致状态。 3. 能否独立解释意图? 一行 subject + 一行 Why 足够说清该 commit 干什么、为什么。
任何一问答 no,回到拆分计划层。
Checkpoint vs Atomic
两者互补,关注点不同:
| 维度 | Checkpoint Commit | Atomic Commit |
|---|---|---|
| 关注点 | 进度记录 | 语义边界 |
| 触发时机 | 长任务的阶段性存档 | 单个完整变更 |
| 是否要求可编译 | 不强制 | 必须 |
| 是否进入最终历史 | 否(合并前 squash) | 是 |
| Header 形式 | chore(wip): [AI] 🔧 [WIP] <subject> | <type>(<scope>): [AI?] <emoji?> <subject> |
| Why 强制 | 否 | feat/fix/refactor/perf 强制 |
checkpoint 在任务进行中保存现场,最终通过 interactive rebase 整理为一组语义清晰的 atomic commit 再合并到主干。本 skill 不直接执行 rebase,但会在 verify 阶段提示用户「该分支含 N 个 [WIP] commit,合并前需 squash」。
经验规则
- 原子性优先于“提交数量少”
- 功能变更和机械性改动优先拆开
- docs/test 可以跟随功能提交,但前提是它们直接服务于该功能
all-changes模式可以覆盖当前 index,但不能覆盖不清晰的意图边界all-changes模式也不能覆盖大文件安全门;不要因为用户说“所有改动”就默认提交全部大文件- 大型生成产物通常先考虑
.gitignore、Git LFS 或 release/artifact 存储,只有用户逐项确认后才进入 commit - 当不确定时,先停在计划层
- checkpoint 不需要 atomic,但最终留在历史里的 commit 必须 atomic
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(CDPATH='' cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
PY_SCRIPT="$SCRIPT_DIR/compose_commit_message.py"
# Probe each candidate by actually running it, mirroring the PowerShell wrapper.
# The probe rejects the Microsoft Store stub (it exits non-zero) without path
# heuristics. stderr is never redirected on the real invocation — the Python
# script's validation messages (--require-why, --ai without --agent-model) must
# stay visible to the caller.
usable() {
command -v "$1" >/dev/null 2>&1 || return 1
"$@" -c 'import sys' >/dev/null 2>&1
}
if usable python3; then
exec python3 "$PY_SCRIPT" "$@"
elif usable python; then
exec python "$PY_SCRIPT" "$@"
elif usable py -3; then
exec py -3 "$PY_SCRIPT" "$@"
elif usable py; then
exec py "$PY_SCRIPT" "$@"
fi
printf '%s\n' "Unable to find a working Python interpreter. Tried: python3, python, py -3, py." >&2
exit 127
$ErrorActionPreference = "Stop"
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
$pyScript = Join-Path $scriptDir "compose_commit_message.py"
$nativeErrorPreference = Get-Variable -Name PSNativeCommandUseErrorActionPreference -ErrorAction SilentlyContinue
$candidates = @(
@{ Command = "python3"; PrefixArgs = @() },
@{ Command = "python"; PrefixArgs = @() },
@{ Command = "py"; PrefixArgs = @("-3") },
@{ Command = "py"; PrefixArgs = @() }
)
if ($nativeErrorPreference) {
$PSNativeCommandUseErrorActionPreference = $false
}
try {
foreach ($candidate in $candidates) {
$commandInfo = Get-Command $candidate.Command -ErrorAction SilentlyContinue
if (-not $commandInfo) {
continue
}
$commandPath = if ($commandInfo.Source) { $commandInfo.Source } else { $commandInfo.Path }
if ($commandPath -and $commandPath.ToLowerInvariant().Contains("\windowsapps\")) {
continue
}
& $candidate.Command @($candidate.PrefixArgs + @("-c", "import sys")) *> $null
if ($LASTEXITCODE -ne 0) {
continue
}
& $candidate.Command @($candidate.PrefixArgs + @($pyScript) + $args)
exit $LASTEXITCODE
}
}
finally {
if ($nativeErrorPreference) {
$PSNativeCommandUseErrorActionPreference = $nativeErrorPreference.Value
}
}
Write-Error "Unable to find a Python interpreter. Tried: python3, python, py."
exit 127
#!/usr/bin/env python3
from __future__ import annotations
import argparse
import sys
import unicodedata
from pathlib import Path
TYPE_EMOJIS = {
"feat": "✨",
"fix": "🐛",
"docs": "📝",
"style": "💄",
"refactor": "♻️",
"perf": "⚡",
"test": "✅",
"build": "📦",
"ci": "👷",
"chore": "🔧",
"revert": "⏪",
}
WHY_REQUIRED_TYPES = {"feat", "fix", "refactor", "perf"}
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(
description="Compose a Conventional Commit message with optional agent-aware metadata.",
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
)
parser.add_argument("--type", required=True, choices=sorted(TYPE_EMOJIS.keys()))
parser.add_argument("--scope", default=None, help="Optional commit scope.")
parser.add_argument("--summary", required=True, help="Short summary without trailing punctuation.")
parser.add_argument(
"--body-line",
action="append",
default=[],
help="Body line. May be supplied multiple times.",
)
parser.add_argument(
"--why",
default=None,
help="Motivation for the change. Rendered as the first body line as `Why: <text>`.",
)
parser.add_argument("--breaking", default=None, help="Optional BREAKING CHANGE message.")
parser.add_argument(
"--closes",
action="append",
default=[],
help="Issue number or reference to close. May be supplied multiple times.",
)
parser.add_argument(
"--refs",
action="append",
default=[],
help="Issue number or reference to mention without closing. May be supplied multiple times.",
)
parser.add_argument(
"--footer-line",
action="append",
default=[],
help="Raw footer/trailer line. May be supplied multiple times.",
)
parser.add_argument(
"--confidence",
default=None,
help="Agent self-assessed confidence, rendered as `Confidence:` trailer (e.g. high/medium/low).",
)
parser.add_argument(
"--scope-risk",
default=None,
help="Blast-radius estimate, rendered as `Scope-risk:` trailer (e.g. narrow/moderate/broad).",
)
parser.add_argument(
"--tested",
default=None,
help="How the change was verified, rendered as `Tested:` trailer (e.g. `just ci`).",
)
parser.add_argument(
"--breaking-header",
action="store_true",
help="Append ! to the commit header before the colon.",
)
parser.add_argument(
"--no-emoji",
action="store_true",
help="Omit the emoji prefix from the header.",
)
parser.add_argument(
"--ai",
action="store_true",
help="Insert the [AI] tag right after the header colon (before emoji).",
)
parser.add_argument(
"--agent-task",
default=None,
help="Agent task identifier or URL, rendered as `Agent-Task:` trailer.",
)
parser.add_argument(
"--agent-model",
default=None,
help="Model identifier, rendered as `Agent-Model:` trailer. Required when --ai is set.",
)
parser.add_argument(
"--agent-prompt-ref",
default=None,
help="Optional prompt reference (hash, URL, or short label), rendered as `Agent-Prompt-Ref:` trailer.",
)
parser.add_argument(
"--generated-by-agent",
action="store_true",
help="Append the `Generated-By: agent` sentinel trailer for audit grep.",
)
parser.add_argument(
"--require-why",
action="store_true",
help="Fail when --why is missing for Why-required types (feat/fix/refactor/perf).",
)
parser.add_argument("--output", default=None, help="Write the composed message to a file instead of stdout.")
return parser.parse_args()
def main() -> int:
args = parse_args()
summary = normalize_summary(args.summary)
if args.ai and not args.agent_model:
print("--ai requires --agent-model so the commit declares which model produced it.", file=sys.stderr)
return 2
if args.require_why and args.type in WHY_REQUIRED_TYPES and not (args.why and args.why.strip()):
print(
f"--require-why is set and type `{args.type}` requires --why explaining the motivation.",
file=sys.stderr,
)
return 3
header = f"{args.type}"
if args.scope:
header += f"({args.scope})"
if args.breaking_header:
header += "!"
header += ": "
header_parts = []
if args.ai:
header_parts.append("[AI]")
if not args.no_emoji:
header_parts.append(TYPE_EMOJIS[args.type])
header_parts.append(summary)
header += " ".join(header_parts)
header_width = display_width(header)
if header_width > 72:
print(
f"Commit header is {header_width} display columns wide; keep the subject line within 72 "
"(≈50 preferred). Tighten the summary or drop the scope.",
file=sys.stderr,
)
return 1
lines = [header]
body_lines: list[str] = []
if args.why and args.why.strip():
body_lines.append(f"Why: {args.why.strip()}")
body_lines.extend(line.strip() for line in args.body_line if line.strip())
trailer_lines: list[str] = []
if args.breaking:
trailer_lines.append(f"BREAKING CHANGE: {args.breaking.strip()}")
trailer_lines.extend(normalize_trailer_lines(args.footer_line))
for close_ref in args.closes:
normalized = normalize_issue_ref(close_ref)
if normalized:
trailer_lines.append(f"Closes {normalized}")
for ref in args.refs:
normalized = normalize_issue_ref(ref)
if normalized:
trailer_lines.append(f"Refs {normalized}")
if args.confidence and args.confidence.strip():
trailer_lines.append(f"Confidence: {args.confidence.strip()}")
if args.scope_risk and args.scope_risk.strip():
trailer_lines.append(f"Scope-risk: {args.scope_risk.strip()}")
if args.tested and args.tested.strip():
trailer_lines.append(f"Tested: {args.tested.strip()}")
if args.agent_task and args.agent_task.strip():
trailer_lines.append(f"Agent-Task: {args.agent_task.strip()}")
if args.agent_model and args.agent_model.strip():
trailer_lines.append(f"Agent-Model: {args.agent_model.strip()}")
if args.agent_prompt_ref and args.agent_prompt_ref.strip():
trailer_lines.append(f"Agent-Prompt-Ref: {args.agent_prompt_ref.strip()}")
if args.generated_by_agent:
trailer_lines.append("Generated-By: agent")
if body_lines or trailer_lines:
lines.append("")
lines.extend(body_lines)
if body_lines and trailer_lines:
lines.append("")
lines.extend(trailer_lines)
message = "\n".join(lines).rstrip() + "\n"
if args.output:
output_path = Path(args.output)
output_path.parent.mkdir(parents=True, exist_ok=True)
output_path.write_text(message, encoding="utf-8")
else:
sys.stdout.buffer.write(message.encode("utf-8"))
return 0
def display_width(text: str) -> int:
"""Approximate terminal columns: CJK/fullwidth and emoji count as 2, combining marks as 0.
A plain ``len()`` undercounts Chinese subjects (each CJK glyph is one code point but two
columns), so the old summary-only check let visually-overlong headers through. Measuring the
whole header here matches what a reviewer actually sees in ``git log``.
"""
width = 0
for ch in text:
if unicodedata.combining(ch):
continue
if unicodedata.east_asian_width(ch) in ("W", "F") or ord(ch) >= 0x1F000:
width += 2
else:
width += 1
return width
def normalize_summary(summary: str) -> str:
summary = summary.strip()
while summary.endswith(("。", ".", "!", "!")):
summary = summary[:-1].rstrip()
return summary
def normalize_issue_ref(ref: str) -> str | None:
ref = ref.strip()
if not ref:
return None
if ref.startswith("#"):
return ref
if ref.isdigit():
return f"#{ref}"
return ref
def normalize_trailer_lines(lines: list[str]) -> list[str]:
return [line.strip() for line in lines if line.strip()]
if __name__ == "__main__":
raise SystemExit(main())
Related skills
FAQ
Does git-commit push my changes?
No. It never pushes by default and never adds Co-Authored-By or push commands unless asked.
How does it choose the commit language?
It detects language from the user's instruction, the request language, and the last 20 commit subjects, falling back to English.