AI-powered smart commit message generator for coding agents
- Multi-Provider Support: Supports Antigravity CLI (
agy, the successor of Gemini CLI), Codex CLI, Claude Code, opencode, Grok CLI, and Apple Intelligence with automatic fallback. The same provider can appear multiple times with different models or accounts (see "Advanced: Provider Fallback Chain" below). - Smart Cooldown: Automatically demotes failed steps for 1 hour (configurable), keyed per provider+model+account so one rate-limited account or model does not block the others
- Format Detection: Detects commit format from recent commits (Conventional, Bracket, Emoji, etc.)
- Empty Repo Safe: Auto format detection falls back cleanly even when the repository has no commits and Git outputs localized messages
- Interactive: Prompts for confirmation before committing (skip with
-y) - Dry Run: Preview generated messages without committing (
-n) - Quiet Mode: Suppresses progress output for hook/scripting use (
-q) - Body Support: Generate detailed commit messages with bullet points (
-b) - Amend/Squash/Reword: Regenerate messages for existing commits
- Private Temp Files: AI prompts, Codex final-output files, and reword message files are created without group/other read permissions on Unix/macOS
- Agent Context: Integrates with claw-hooks to generate context-aware messages reflecting the agent's intent
- OS: macOS, Linux, Windows
- Git: Required
- AI Provider (at least one):
- Antigravity CLI (
agy, successor of Gemini CLI): see https://antigravity.google/docs/gcli-migration (the legacy Gemini CLI stops serving requests on 2026-06-18) - Codex CLI:
npm install -g @openai/codex - Claude Code:
curl -fsSL https://claude.ai/install.sh | bash - opencode:
curl -fsSL https://opencode.ai/install | bash - Grok CLI (
grok, xAI): bundled with cmux at/Applications/cmux.app/Contents/Resources/bin/grok. Ifgrokis not onPATH, the step is skipped and the chain moves on. - Apple Intelligence: Built-in on macOS (macOS 26+ with Apple Silicon required)
- Antigravity CLI (
brew install owayo/git-sc/git-scwinget install owayo.git-scOpen a new terminal afterwards: the portable package updates your PATH, and already-running shells do not pick up the change.
git clone https://github.com/owayo/git-smart-commit.git
cd git-smart-commit
make installOn macOS, make install signs a temporary copy and then atomically replaces the installed binary. This avoids stale per-inode code-signature validation after reinstalling over an existing command.
Download the latest binary from Releases.
curl -L https://github.com/owayo/git-smart-commit/releases/latest/download/git-sc-aarch64-apple-darwin.tar.gz | tar xz
sudo mv git-sc /usr/local/bin/curl -L https://github.com/owayo/git-smart-commit/releases/latest/download/git-sc-x86_64-apple-darwin.tar.gz | tar xz
sudo mv git-sc /usr/local/bin/curl -L https://github.com/owayo/git-smart-commit/releases/latest/download/git-sc-x86_64-unknown-linux-gnu.tar.gz | tar xz
sudo mv git-sc /usr/local/bin/curl -L https://github.com/owayo/git-smart-commit/releases/latest/download/git-sc-aarch64-unknown-linux-gnu.tar.gz | tar xz
sudo mv git-sc /usr/local/bin/Download git-sc-x86_64-pc-windows-msvc.zip from Releases, extract, and add to PATH. WinGet (above) does this for you.
# Generate commit message for staged changes
git-sc
# Stage all and commit without confirmation
git-sc -a -y
# Preview message (dry run)
git-sc -n| Command | Description |
|---|---|
git-sc |
Generate message for staged changes |
git-sc init |
Initialize configuration file |
git-sc -a |
Stage all changes and generate message |
git-sc --amend |
Regenerate message for last commit |
git-sc --squash <BASE> |
Squash all commits into one |
git-sc --reword <HASH> |
Regenerate message for specific commit |
git-sc -g <HASH> |
Generate from existing commit (output only) |
Concurrency guards worth knowing about:
--rewordrefuses to run while a rebase is in progress. It ends every failed rebase withgit rebase --abort, so starting one on top of yours would discard your in-progress conflict resolution. Finish or abort your rebase first.- Committing aborts if the staged content changed while the message was being generated (git-sc compares the index tree before and after). The generated message describes the old content, so committing the new one would be wrong. Just re-run.
--squashre-checks for newly staged changes the same way, right before it resets. - Committing,
--amend, and--squashalso abort ifHEADmoved during generation. What--squashfolds and--amendrewrites comes from the history rather than the index, so a commit made in another terminal leaves the index clean and slips past the staged-changes check — a--squashwould then fold in a commit the AI never saw, and an--amendwould overwrite a different commit than the one it described.
| Option | Short | Description |
|---|---|---|
--yes |
-y |
Skip confirmation prompt |
--dry-run |
-n |
Show message without committing |
--all |
-a |
Stage all changes |
--body |
-b |
Generate with body (bullet points) |
| Option | Short | Description |
|---|---|---|
--amend |
Regenerate message for last commit | |
--squash |
Squash all commits into one | |
--reword |
Regenerate message for specific commit | |
--generate-for |
-g |
Generate from commit diff (output only) |
Operation modes (--amend, --squash, --reword, --generate-for) are mutually exclusive. Combining them fails during argument parsing instead of silently choosing one mode.
--amend note:
- Also works when the current
HEADis the root commit. - Preserves unrelated staged changes instead of folding them into the amended commit.
--reword note:
- Only accepts commits reachable from the current
HEADhistory. - Passing a hash from another branch (not in current history) fails with an error.
- Passing a merge commit hash also fails, even when the merge commit itself is the reword target.
- Rewording a commit that has a merge commit between it and
HEADis rejected with a clear "cannot reword across merge commits" error (merge-spanning reword is unsupported), rather than a confusing low-level git error such asfatal: ambiguous argument. - Rewording the oldest commit in current history is also supported (internally uses
git rebase -i --rootwhen required). - Rewording
HEADpreserves unrelated staged changes instead of folding them into the rewritten commit. - The internal rebase runs with
--no-autosquash, so a user-levelrebase.autoSquash = trueconfig cannot silently foldfixup!/squash!commits into the reworded commit.
--squash note:
- Fails before rewriting history when unrelated staged changes already exist. Commit, unstage, or stash them first.
- If the squash commit itself fails (e.g. rejected by a
pre-commit/commit-msghook or a GPG signing error), the branch is automatically restored to its originalHEADinstead of being left rewound at the merge-base.
| Option | Short | Description |
|---|---|---|
--provider |
-p |
Use specific AI provider (antigravity, codex, claude, opencode, grok, apple-intelligence). The legacy name gemini is accepted as a backward-compatible alias for antigravity. |
--lang |
-l |
Override commit message language |
| Option | Short | Description |
|---|---|---|
--quiet |
-q |
Suppress progress messages |
--debug |
-d |
Show prompts sent to AI |
--help |
-h |
Print help |
--version |
-V |
Print version |
--yes behavior:
- Required for unattended runs. If the confirmation prompt reaches end-of-file on stdin (for example when git-sc is invoked from a script or hook with stdin closed), the run aborts with an error instead of taking the
[Y/n]default. An empty line typed by a user still means yes; "no input at all" does not, because the same prompt guards--amend,--squash, and--reword
--quiet behavior:
- Suppresses progress, preview, and success/cancel messages in normal/amend/squash/reword flows
- Keeps error output visible
- In
--generate-formode, still prints only the generated commit message (for piping/scripting)
--debug behavior:
- In
--generate-formode, all debug output (config settings, AI prompt, provider command, streaming output) goes to stderr, so stdout remains the generated message only and stays safe to pipe - In every other mode debug output goes to stdout as a single block, including when combined with
--quiet.--quietsuppresses progress messages; it does not move debug output to stderr
# Basic usage
git-sc # Generate for staged changes
git-sc -a -y # Stage all and commit directly
# Preview and body
git-sc -n # Dry run (preview only)
git-sc -b # Include detailed body
# Amend and squash
git-sc --amend # Regenerate last commit message
git-sc --squash origin/main # Squash feature branch commits
# Generate from existing commits
git-sc -g abc1234 # Generate from commit diff
git-sc -g abc1234 -b # With detailed bodyInitialize configuration with git-sc init, or create ~/.config/git-sc/config.toml manually:
git-sc initThis creates a configuration file with default settings at ~/.config/git-sc/config.toml.
Use --force to overwrite an existing configuration:
git-sc init --forcegit-sc supports hierarchical configuration with project-level overrides:
| File | Scope | Description |
|---|---|---|
~/.config/git-sc/config.toml |
Global | User-wide default settings |
.git-sc |
Project | Repository-specific overrides (in repo root) |
Project settings override global settings. Fields not specified in project config inherit from global config. You can specify only the fields you want to override — partial [models] sections are supported.
# AI provider priority
# "antigravity" is the successor of the legacy Gemini CLI (`agy` command).
# Writing "gemini" instead is still accepted as a backward-compatible alias.
providers = ["opencode", "grok", "antigravity", "codex", "claude", "apple-intelligence"]
# Commit message language
language = "Japanese"
# Commit prefix format (optional)
# Values: conventional, bracket, colon, emoji, plain, none
prefix_type = "conventional"
# Auto-push after commit (optional)
auto_push = true
# Codex reasoning effort passed via `-c model_reasoning_effort=<value>`
# Values: "low" (default), "medium", "high", "xhigh", or "" to omit and use codex default
codex_reasoning_effort = "low"
# Model configuration
# Antigravity CLI (`agy`) supports `--model`: the `antigravity` value is passed straight
# to `agy --model "<name>"`. Either spelling works — the display name
# (e.g. "GPT-OSS 120B (Medium)", "Gemini 3.5 Flash (Low)") or the slug
# (e.g. "gpt-oss-120b-medium", "gemini-3.5-flash-low"). Which one `agy models` prints
# varies by agy version (1.0.x: display names, 1.1.10: slugs). An unknown name is
# rejected with a non-zero exit rather than silently falling back, so a typo just
# fails the step. An empty string omits `--model` and lets agy pick its own default. A legacy `gemini = "..."` key is still
# accepted as an input alias and is promoted to `antigravity` (an explicit `antigravity`
# value wins if both are present).
# Grok CLI supports `-m`: pass a model ID from `grok models` (e.g. "grok-4.5").
# An empty string omits `-m` and lets grok pick its own default.
[models]
antigravity = "GPT-OSS 120B (Medium)"
codex = "gpt-5.6-luna"
claude = "haiku"
opencode = ""
grok = ""
# Provider cooldown (minutes)
provider_cooldown_minutes = 60
# Provider timeout (seconds) per call
provider_timeout_seconds = 60| Option | Description | Default |
|---|---|---|
providers |
Provider fallback chain — each entry is a provider name string or a {provider, model, command, env, name} table (see "Advanced: Provider Fallback Chain" below; antigravity recommended, gemini accepted as an alias) |
["opencode", "grok", "antigravity", "codex", "claude", "apple-intelligence"] (apple-intelligence only on macOS builds with the apple-ai feature) |
language |
Commit message language | "Japanese" |
prefix_type |
Commit prefix format | Auto-detect |
auto_push |
Auto-push after commit | false |
codex_reasoning_effort |
Codex -c model_reasoning_effort value (low, medium, high, xhigh, or "" to omit) |
"low" |
models.* |
Model for each provider | See config |
provider_cooldown_minutes |
Failed provider cooldown; extremely large values are treated as effectively indefinite | 60 |
provider_timeout_seconds |
Provider call timeout | 60 |
prefix_rules |
URL-based prefix format | [] |
prefix_scripts |
External prefix scripts | [] |
ai_usage |
Residual quota gate via the ai-usage CLI (see "Residual Quota Gate") |
disabled |
dev_log |
Developer generation log (global config only; see "Developer Generation Log") | disabled |
Existing global config files are not rewritten automatically. The current Codex default is gpt-5.6-luna; to use it in an existing setup, update models.codex in ~/.config/git-sc/config.toml. Check this after any Codex CLI update. The previous default, gpt-5.6-luna, has since been removed from Codex, and naming a removed model does not fall back to anything — it returns HTTP 400, which git-sc counts as a provider failure and puts the step into cooldown, so an outdated models.codex silently drops Codex out of the fallback chain on every run. This default was reselected on September 17, 2026 (JST) by comparing input_tokens for Codex models that are API-visible, listed, and support medium reasoning, using Reply ok. in an empty directory with --ignore-user-config --ignore-rules --ephemeral --sandbox read-only and model_reasoning_effort='medium': gpt-5.6-luna = 19609, gpt-5.5 = 20181, gpt-5.6-sol = 21174, gpt-5.6-terra = 21174, gpt-6-astra = 22035. All runs produced ok with no tool calls, and a second round reproduced every figure exactly.
The default Antigravity (agy) model is GPT-OSS 120B (Medium), chosen by measurement. agy 1.1.10 added --output-format json to print mode, which reports a per-request usage object, so the same input_tokens comparison used for Codex is now possible (earlier agy releases had no machine-readable usage output, and this default originally rested on published pricing instead). Measured August 4, 2026 (JST) with agy 1.1.10 using the fixed prompt Reply ok. in an empty directory: gpt-oss-120b-medium = 13680, gemini-3.5-flash-medium = 16994, gemini-3.5-flash-low = 16998, gemini-3.1-pro-low = 17684, gemini-3.6-flash-low = 18175, gemini-3.6-flash-medium = 18176, claude-sonnet-4-6 = 19346. All runs succeeded in a single turn, and gpt-oss-120b-medium is the minimum by roughly 19%, so it remains the default. This measures minimal per-request overhead only and says nothing about real-workload quality. To use it in an existing setup, add or update models.antigravity in ~/.config/git-sc/config.toml, or set it to "" to defer to agy's own default.
Provider cooldown state normalizes legacy aliases before reordering providers, so gemini/agy cooldown entries still apply to antigravity, and legacy apple-ai / apple_intelligence entries still apply to apple-intelligence. Running with --debug also prints a one-time notice when a legacy gemini provider alias is found in your config, reminding you that it is normalized to antigravity.
Each entry in providers can be either a plain string (provider name only) or a table that also specifies model, command, and env. This lets you build a fallback chain where the same provider appears multiple times with different models or accounts — useful when one provider splits its quota per model family or per account/contract.
providers = [
# Same provider, different accounts (switch via env: CODEX_HOME / CLAUDE_CONFIG_DIR).
{ provider = "codex", model = "gpt-5.6-luna", env = { CODEX_HOME = "~/.codex" } }, # account 1
{ provider = "codex", model = "gpt-5.6-luna", env = { CODEX_HOME = "~/.codex-work" } }, # account 2
# Same provider, different model families (separate quotas).
{ provider = "antigravity", model = "Gemini 3.5 Flash (Low)" },
{ provider = "antigravity", model = "GPT-OSS 120B (Medium)" },
# A plain string is still accepted (provider name only).
"claude",
]Per-step fields:
| Field | Description |
|---|---|
provider |
Required. Provider type that decides the CLI argument convention (codex, antigravity, claude, opencode, grok, apple-intelligence; gemini/agy accepted as aliases). |
model |
Optional. Model for this step. If omitted, falls back to [models].<provider>, then the CLI's own default. |
command |
Optional. Executable (and fixed args) to run instead of the provider's default binary — e.g. a wrapper script. ~ is expanded. The provider's standard arguments (--disable hooks etc. for codex) are still applied. |
env |
Optional. Environment variables set explicitly via Command::env() when launching this step. ~ in values is expanded; the key must be a valid POSIX name. Dynamic-loader / interpreter pre-load keys (LD_PRELOAD, DYLD_INSERT_LIBRARIES, NODE_OPTIONS, PYTHONPATH etc.) are rejected case-insensitively with a config error to prevent code-injection via project-level .git-sc. |
name |
Optional. Identifier used for the cooldown key and log label. If omitted, it is derived deterministically from provider + model + env + command. |
Account switching (recommended: env). Codex and Claude Code pick their account/credentials from CODEX_HOME / CLAUDE_CONFIG_DIR. Setting these per step via env lets you fall back across accounts, each with its own quota. git-sc applies them with an explicit Command::env() override, so the launched CLI is not affected by whatever CODEX_HOME / CLAUDE_CONFIG_DIR happens to be exported in the shell that runs git-sc. (A wrapper script via command works too, but env is preferred because it is explicit and shown in --debug.)
Independent cooldown. The cooldown key includes provider + model + env (+ command, or an explicit name), so each step is demoted independently: if codex on account 1 hits a rate limit, codex on account 2 — and antigravity on a different model — stay available.
| Value | Example | Description |
|---|---|---|
conventional |
feat: add feature |
Conventional Commits format |
bracket |
[feat] add feature |
Bracket-style prefix |
colon |
feat: add feature |
Simple colon prefix |
emoji |
:sparkles: add feature |
Emoji prefix |
plain |
Add feature |
No prefix |
none |
add feature |
No prefix, lowercase |
Specify commit format by remote URL:
[[prefix_rules]]
url_pattern = "github\\.com[:/]myorg/"
prefix_type = "conventional" # conventional, bracket, colon, emoji, plain, noneprefix_type in a matching rule must be one of the valid values above. Invalid matching rules are skipped with a warning, so later prefix rules, configured prefix_type, or auto detection can still apply.
Custom prefix generation via external scripts:
[[prefix_scripts]]
url_pattern = "^https://gitlab\\.example\\.com/"
script = "/path/to/prefix-generate.py"If a prefix script returns a valid prefix_type name (e.g. conventional, bracket, emoji, etc.) instead of a literal prefix string, git-sc interprets it as a Rule mode. This allows scripts to dynamically select the commit format based on branch name or remote URL.
For literal prefix strings, trailing line endings (\n/\r\n) from common script output such as echo are removed, while intentional trailing spaces are preserved.
If a prefix script returns empty output (exit 0 with no text), git-sc keeps the generated message as-is, except it removes a leading Conventional Commit type prefix (for example feat:, fix(scope):, feat!:) when present.
If a prefix script exits with code 1, git-sc keeps the AI-generated message without adding a prefix. Other non-zero exit codes are treated as script execution failures, so git-sc falls back to the next matching prefix script, prefix rule, configured prefix_type, or auto detection.
For project-level .git-sc, relative script paths are resolved from the Git repository root, and the script runs with the Git root as its working directory.
#!/bin/bash
# Example: return "conventional" to use Conventional Commits format
echo "conventional"- Whitespace-only changes excluded
- Binary files excluded
- Quoted diff headers with spaces or non-ASCII file paths are handled correctly
.git-sc-ignorepatterns applied- Truncated at 10,000 characters
- AI prompts may contain staged diff content. When git-sc needs a temporary prompt file for providers such as opencode, or a final-output file for Codex, it creates the file with no group/other permissions on Unix/macOS and removes it automatically after use.
- Reword message temporary files use the same private-file behavior.
- The provider cooldown state file (
~/.config/git-sc/.providers-state) is also created with no group/other permissions, because its cooldown keys embed each step'senvvalues verbatim. .git-sc-ignorefailures stop the run. If the file exists but cannot be read or parsed, git-sc exits with an error instead of continuing without exclusions. A malformed ignore file would otherwise silently send the very files you meant to withhold to the AI provider..git-sc-ignoreis not affected by your diff-formatting Git config. Exclusion works by reading file paths out of thediff --git a/… b/…header, so settings that reshape that line —diff.noprefix,diff.mnemonicPrefix,diff.srcPrefix/diff.dstPrefix,color.ui = always,diff.external— would otherwise make every pattern silently stop matching. git-sc requests the diff with the prefixes, colors, and path base pinned, so your patterns apply the same way regardless of those settings. This also coversdiff.relative, which additionally would have hidden any change outside the directory you ran git-sc from — with it pinned, the message is always written from the full staged diff no matter which subdirectory you are in.- A project-level
.git-sccan run code.providers[].command,prefix_scripts[].script, andai_usage.commandname executables that git-sc launches, and a repository-local.git-scis merged in like any other config. Cloning an untrusted repository and running git-sc in it — including automatically, via an agent stop hook — therefore executes whatever those fields point at.envkeys are validated and dynamic-loader / interpreter pre-load names are rejected, but that does not constrain these three fields. Review a repository's.git-scbefore running git-sc inside it, the same way you would review aMakefileor a git hook.
Patterns are matched against the decoded Git path, so quoted diff headers such as Japanese filenames escaped by Git are excluded correctly as well.
Rename diffs are checked against both the source path and destination path, so moving a file into an ignored directory is excluded consistently too.
Filenames containing spaces are also supported: Git does not quote space-only filenames in diff --git headers, but git-sc still extracts the correct path so that ignore patterns apply consistently.
This also covers rename headers where the source and destination paths differ and both paths contain spaces, as well as mixed headers where only one side is quoted (e.g. renaming old name.txt to a non-ASCII filename that Git quotes).
package-lock.json
yarn.lock
Cargo.lock
*.generated.tsEnable auto-push in your config file:
# In ~/.config/git-sc/config.toml or .git-sc
auto_push = trueWhen enabled, git-sc will run git push after a successful commit or squash.
If you have the ai-usage CLI installed, git-sc can drop providers whose account is nearly out of quota before spending a call on them. Disabled by default.
[ai_usage]
enabled = true
command = ["ai-usage", "--json"] # optional (`~` in the executable path is expanded)
threshold_percent = 95 # skip a step at or above this usage
window = "nearest" # weekly | five_hour | nearest (the higher of the two)
timeout_seconds = 10git-sc runs the command once at startup and checks each step in the fallback chain against the matching account. Steps at or above threshold_percent are removed for that run only — the provider cooldown state is untouched.
Each provider step can say which account it belongs to:
[[providers]]
provider = "codex"
ai_usage_profile = "Work" # exact, case-sensitive match on the ai-usage `profile`
env = { CODEX_HOME = "~/.codex-work" }
[[providers]]
provider = "antigravity"
ai_usage_group = "Claude&GPT" # case-insensitive match on `group_label`ai_usage_group exists because one account's quota can be split per model family — Antigravity reports separate Gemini and Claude&GPT pools that run out independently. Without ai_usage_profile, git-sc judges the step against the least-used account for that provider; note this only affects the decision, since the account a step actually runs as is decided by its env. Set both if you want the gate and the execution to agree.
Failure handling is deliberately asymmetric: if the command cannot be run, times out, or returns unparseable output, the chain is left as-is and the commit proceeds, because a broken helper must never block a commit. But if the usage data is read successfully and every step is over the threshold, git-sc stops with an error rather than falling back to the default chain — falling back would call the very providers the gate just refused.
A project-level .git-sc can override individual fields; anything it does not mention keeps the global value.
Records what prompt was sent and what came back, so prompt changes can be evaluated against real data instead of guesswork. Disabled by default.
# ~/.config/git-sc/config.toml only — a project .git-sc cannot enable this
[dev_log]
enabled = true
content = "metadata" # metadata | full
retention_days = 14
max_total_mb = 500Each run writes one JSON file to ~/.config/git-sc/logs/YYYY-MM-DD/, containing the prompt digest and diff statistics, every provider attempt (raw response before cleanup, model, duration, quality findings, and whether it was accepted, retried, or fell through), and the outcome — including the commit hash when one was made. Files are written to a temporary name and renamed into place, so concurrent git-sc runs never interleave, and partially written records never appear as finished ones.
Analyze them by streaming the files into JSONL:
find ~/.config/git-sc/logs -name '*.json' | sort | xargs jq -c .
# e.g. how often each provider produced a defective subject
find ~/.config/git-sc/logs -name '*.json' | xargs jq -r \
'.attempts[] | select(.findings | length > 0) | "\(.provider)\t\(.findings[0])"' | sort | uniq -cPrivacy. content = "metadata" (the default) keeps prompt statistics and a digest but not the prompt itself, and drops provider stderr as well — Codex echoes the prompt there, so keeping it would put your diff in the log by another route. content = "full" stores the exact prompt, which means your staged diff is written to disk in plain text. Raw provider responses are kept at both levels, since a cleaned-up message alone is not enough to diagnose a bad generation. Environment overrides are recorded by name only, never by value. Logs are created mode 0600 inside 0700 directories, are removed after retention_days, and are trimmed oldest-first once they exceed max_total_mb. Cleanup runs at most once a day. If a log cannot be written, git-sc prints one warning (suppressed under --quiet) and commits anyway.
This setting is global-only on purpose: a cloned repository's .git-sc must not be able to turn on logging or choose where your code gets written. A project-level [dev_log] is ignored with a warning.
Git-SC (Smart Commit) - Available on VS Code Marketplace
Add to ~/.claude/settings.json:
{
"hooks": {
"Stop": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "git-sc --all --yes --quiet"
}
]
}
]
}
}When git-sc is used with claw-hooks, it automatically receives context about what the coding agent was working on via the CLAW_HOOKS_AGENT_MESSAGE environment variable. This context is included in the AI prompt, enabling the generated commit message to reflect the high-level intent rather than just describing the raw diff.
claw-hooks is a companion tool that manages Claude Code's hook lifecycle. When its stop hook fires, it sets CLAW_HOOKS_AGENT_MESSAGE with the agent's last activity summary before invoking git-sc.
# Automatically set by claw-hooks stop hook
# CLAW_HOOKS_AGENT_MESSAGE="Refactored authentication to use JWT tokens"
git-sc -a -y -qWhen the environment variable is set, the prompt includes an "Agent Context" section that guides the AI to prioritize the developer's intent.
This applies to standard commit generation as well as --amend, --reword, --squash, and --generate-for.
flowchart LR
A[Stage Changes] --> B[Get Diff]
B --> C[Detect Format]
C --> D[Generate via AI]
D --> E[Confirm & Commit]
- Verify: Check git repo and AI agent availability
- Config: Load
~/.config/git-sc/config.tomlsettings - Diff: Get staged changes (with exclusions)
- Format: Detect from recent commits or rules
- Generate: Send to AI with fallback
- Commit: Confirm and create commit
The Grok provider drives the Grok Build TUI (grok, xAI). That CLI is a full coding agent — plan mode, cross-session memory, web search, and tool execution are all on by default — so git-sc constrains it to behave as a single-turn pure function:
| Flag | Why |
|---|---|
--output-format plain |
Headless text output instead of the interactive TUI |
--sandbox read-only |
Forbids filesystem writes and network, like Codex's sandbox |
--no-plan / --no-memory |
Blocks plan mode and cross-session memory (both default on) |
--disable-web-search |
Cuts web fetch/search |
--max-turns 1 |
Stops any tool loop after one turn |
--verbatim |
Prevents the CLI from rewriting the prompt |
--prompt-file <temp file> |
Avoids ARG_MAX limits and cmd.exe metacharacter issues for large diffs |
- Model: resolved as
modelon the step >[models].grok> empty (defer to grok's own default). When non-empty, the ID fromgrok models(currently onlygrok-4.5) is passed as-m "<id>". The shipped default for[models].grokis left empty so a cheaper model added later is picked up without a git-sc release. - Availability: the Grok CLI ships bundled with cmux at
/Applications/cmux.app/Contents/Resources/bin/grok. Ifgrokis not onPATH, this step is skipped and the chain moves on to the next provider.
Apple Intelligence provider uses fm-rs (Rust bindings for Apple's Foundation Models framework) for fully on-device inference. No API key or network connection is required.
- Requirements: macOS 26 (Tahoe) or later, Apple Silicon, Apple Intelligence enabled in System Settings
- How it works: With Apple Intelligence enabled (default on macOS), git-sc calls Foundation Models directly via fm-rs. A
LanguageModelSessionis created with commit-message-specific instructions for each generation. The instructions are built from the resolved prefix type, soprefix_type = "none"/"bracket"/"emoji"and auto-detection from recent commits are respected instead of always forcing Conventional Commits. - Context window: the on-device model holds 4096 tokens, which is far smaller than every other provider. git-sc measures the prompt before generating and, when it does not fit, rebuilds it from a compacted diff that keeps the full list of changed files and truncates the bodies. A warning is printed when this happens, because the resulting message was written from a partial view of the change. If even the compacted prompt does not fit, the run moves on to the next provider instead of failing the commit.
- Timeout: bounded by
provider_timeout_seconds(default 60), the same setting the CLI providers use. - Failures: a failure caused by the prompt itself (context size, safety guardrail, refusal, unsupported language) does not put the provider into cooldown — only failures that mean the model is currently unusable (assets not downloaded, rate limited, timed out) do. Typed failure classification requires building against the macOS 27 SDK or later; on macOS 26 all failures are treated as provider failures.
- Build:
cargo build --features apple-ai(automatic withmake build/make installon macOS). Building on macOS 27 or later additionally enables Foundation Models 27 features (exact token counts, typed errors, per-response token usage); macOS 26 remains supported. - Cross-platform: On Linux/Windows, Apple Intelligence is not available and is automatically skipped
- Windows: the Antigravity CLI (
agy) provider is skipped with an explicit error. All providers launch throughcmd /Con Windows (to support npm-installed.cmdshims), but cmd.exe cannot safely receive a multi-line diff prompt as a command-line argument, so passing it would corrupt the command (and is a known command-injection vector class, CVE-2024-24576). The fallback chain simply moves on to the next provider. Providers that read the prompt from stdin or a temp file (codex, claude, opencode, grok) are unaffected.
| Command | Description |
|---|---|
make build |
Build debug version |
make release |
Build release version |
make install |
Build and install to /usr/local/bin |
make test |
Run tests |
make fmt |
Format code |
make check |
Run clippy and cargo check (includes apple-ai on macOS) |
make clean |
Clean build artifacts |
Contributions are welcome! Please feel free to submit a Pull Request.
See Releases for version history.
