Commit Changes

SkillDev tools

Stages and commits the current changes onto a safe working branch, enforcing branch discipline and optionally pushing upstream. TRIGGER when: the user wants to commit, save, or check in the current changes. DO NOT TRIGGER when: opening a pull request → pr.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the Commit Changes skill

What this skill tells your AI

The instructions your AI receives, as published by opsmill/infrahub in .agents/skills/commit/SKILL.md and read by ahel’s review.

Introduction

Stage and commit the current changes onto a safe working branch. This skill enforces branch discipline: when the current branch is unsafe to commit to (the canonical rules live in step 2 below), it proposes a new fix/, feat/, docs/, etc. branch and switches to it after approval. It works equally well for brand-new work and for additional commits on an existing feature branch. If the user passes push, the branch is pushed upstream after the commit.

Arguments

$ARGUMENTS

Supported arguments:

  • push — After committing, push the branch upstream (git push -u origin <branch> on first push, otherwise git push).

Main Tasks

1. Assess Current State

  1. Run git status to see staged, unstaged, and untracked files.
  2. Run git branch --show-current to identify the current branch.
  3. Run git diff --stat and git diff --cached --stat to summarise the change footprint.
  4. If there are no changes to commit (working tree clean, nothing staged) — STOP and tell the user there's nothing to commit. Do not create an empty commit.
  5. If a merge, rebase, or cherry-pick is in progress (git status reports it, or .git/MERGE_HEAD / .git/rebase-* / .git/CHERRY_PICK_HEAD exist) — STOP and surface it to the user rather than committing into the middle of that operation.

2. Branch Safety — Never Commit to a Protected or Placeholder Branch

A branch is unsafe to commit to if any of the following hold:

  • It is the repository's default branch (resolve with git symbolic-ref refs/remotes/origin/HEAD --short 2>/dev/null | sed 's@^origin/@@', falling back to git remote show origin | sed -n 's/ *HEAD branch: //p').
  • It is a long-standing integration branch by name: main, master, stable, develop, dev, trunk.
  • It is a release branch: matches release/*, release-*, or is otherwise clearly named release-something (e.g. release-2026.05, releases/v3).
  • It is a placeholder / scratch branch generated by the harness or a worktree — specifically the auto-generated adjective-animal pattern (e.g. wary-cuckoo, lumbar-gorilla, wacky-otter, silent-fox), where the second word is an animal. These exist only to host a session and should not be committed to directly. Be careful not to over-match: legitimate two-word branches like dark-mode, rate-limit, cache-layer, or user-auth are real feature branches, not scratch branches. When a name is ambiguous (two hyphenated words but not clearly adjective-animal), do not assert it is unsafe — instead ask the user "this looks like it might be a generated scratch branch — is it, or is it a real branch you want to commit to?" and proceed on their answer.

If the current branch is unsafe by any of those rules:

  1. Do NOT commit on the current branch.
  2. Analyse the changes (diffs from step 1) to understand whether the work is a bug fix or new/extended behaviour.
  3. Propose a branch name using the conventional prefix that matches the change:
    • Bug fixfix/<short-description> (e.g. fix/graphql-codegen-missing-types).
    • New feature or enhancementfeat/<short-description> (e.g. feat/add-commit-skill).
    • Docs-only, chore, refactor, etc. → mirror the conventional-commit type: docs/<…>, chore/<…>, refactor/<…>.
    • Use a concise kebab-case slug. If the repo has a visible naming convention in git branch -a or in AGENTS.md / CONTRIBUTING.md, follow that instead.
  4. Present the proposed branch name to the user and wait for explicit approval (the user may suggest a different name).
  5. After approval, create and switch to the branch: git checkout -b <branch-name>. The uncommitted changes carry across automatically.

If the current branch is already a real feature branch (i.e. not in any of the unsafe categories above), continue on it. Do not create a new branch unless the user explicitly asks for one.

3. Stage Changes

  1. Review what will be staged. Prefer adding files explicitly by path rather than git add -A or git add ., especially when untracked files are present — this avoids accidentally committing secrets (.env, credentials, key files) or large/generated artefacts.
  2. If staged changes already exist (the user pre-staged), respect that staging and only add additional files when it's clearly intended.
  3. Warn the user and require explicit confirmation before staging any file that looks sensitive (.env*, *secret*, *credential*, *.key, *.pem) or generated/large (node_modules/, build artefacts, dist/, __pycache__/, lockfiles changed unexpectedly).

4. Draft the Commit Message

Follow the repo's conventional-commit style (visible in git log):

  • Use a type prefix: feat:, fix:, refactor:, docs:, test:, chore:, ci:, etc.
  • Optional scope in parentheses: feat(backend): ..., fix(e2e): ....
  • Subject line under ~72 characters, imperative mood, no trailing period.
  • Focus on the why — what changed in terms of behaviour or capability, not a file list.
  • For multi-area changes, add a short body explaining the motivation.
  • Respect any repo or harness commit-trailer convention (e.g. a Co-Authored-By trailer the harness expects to append). Don't strip trailers that are already part of the project's workflow.
  • Never write a session-link trailer into the commit — strip it if the harness added one. Some harnesses append a line pointing at the private agent session, e.g. Claude-Session: https://claude.ai/code/session_… (or any trailer carrying a URL to an agent, chat, or coding session). These leak an internal, usually unshareable URL into the project's permanent — often public — git history, and they reference session state no future reader can open, so they're pure noise at best and a disclosure at worst. This is the one exception to "don't strip trailers" above: if the harness has inserted such a line, remove it before committing, and never author one yourself. Legitimate project trailers (Co-Authored-By, Signed-off-by, Reviewed-by, etc.) still stay.

Inspect the most recent ~20 commits with git log --oneline -20 and mirror the style you see (scope conventions, capitalisation, whether bodies are used). Generic examples:

  • fix: correct off-by-one in pagination cursor
  • docs: archive completed specs and extract durable knowledge
  • feat: add commit skill for safe branch discipline

Present the proposed commit message to the user before committing. Adjust based on feedback.

5. Create the Commit

  1. Run git commit -m "<message>". For multi-line messages, use a HEREDOC:

    git commit -m "$(cat <<'EOF'
    <subject>
    
    <body>
    EOF
    )"
    
  2. Do NOT pass --no-verify — let pre-commit hooks run. If a hook fails:

    • Read the hook output carefully.
    • Fix the underlying issue (formatting, lint, etc.) rather than bypassing.
    • Re-stage the fixed files and create a NEW commit so each fix stays traceable. (Don't reach for --amend to absorb hook fixes — only amend a commit you deliberately intend to rewrite.)
  3. Run git status after the commit to confirm a clean tree and verify success.

  4. Run git log -1 --stat so the user can see what landed.

6. Push Upstream (only when push argument is provided)

Skip this phase entirely if push was NOT passed.

When push IS provided:

  1. Re-verify the branch is not unsafe per the rules in step 2 (defence in depth, though step 2 should have prevented this).
  2. Check whether the branch already tracks a remote:
    • git rev-parse --abbrev-ref --symbolic-full-name @{u} 2>/dev/null
  3. If no upstream exists: git push -u origin <branch-name>.
  4. If an upstream exists: git push.
  5. Never use --force or --force-with-lease unless the user explicitly asks for it. Regular git push will fail if the remote has diverged — surface that error to the user instead of overwriting.
  6. Report the push result and, if a remote URL is configured (git config --get remote.origin.url), the branch URL the user can open.

Notes

Branch Safety:

  • The canonical list of unsafe branches lives in step 2 — that is the single source of truth; don't re-derive it from memory.
  • If asked to override the rule, refuse and explain — the user can switch branches themselves first if they truly intend to commit there (in which case this skill no longer applies).

This is a discipline skill — hold the line under pressure. Refusing to commit to a protected branch is the whole point, and the excuses below will appear. None of them change where the commit should land:

Excuse / pressureReality
"It's urgent / it's an emergency, just commit to main."Urgency doesn't change where the commit lands. A feat//fix/ branch takes seconds and is just as fast to merge.
"Just this once, skip the branch."There is no "just once" — the rule exists precisely for the tempting one-off. Propose a branch.
"It's a tiny change, the branch is overkill."Size is irrelevant to branch safety; a one-line fix on main is still a direct commit to a protected branch.
"The hook is failing, just use --no-verify."Fix the violation instead. Bypassing the hook defeats its purpose.
"The remote diverged, just --force."Never force-push unless the user explicitly asks. Surface the divergence instead.

Red-flags self-check — if you catch yourself thinking any of these, STOP and re-read step 2:

  • "This branch is probably fine to commit to."
  • "I'll commit here and sort the branch out later."
  • "The user clearly wants this on main, so the rule doesn't apply."

Secret Hygiene:

  • Prefer explicit git add <path> over wholesale git add -A.
  • Warn before staging anything that looks like a secret or credential.
  • A session-link trailer (Claude-Session: / any agent-session URL) is a disclosure too — strip it from the commit message before committing, per step 4.

Hook Discipline:

  • Pre-commit hooks exist for good reasons. Fix violations rather than bypassing with --no-verify.

Idempotency:

  • Safe to invoke repeatedly. With no changes to commit, the skill exits cleanly without creating an empty commit.

Expected Outcome

  • All staged/unstaged changes that the user wanted to capture are committed on a safe working branch.
  • The branch is named meaningfully (existing real feature branch preserved, or a new fix/<…> / feat/<…> / docs/<…> / etc. name approved by the user).
  • The commit message follows the repo's existing conventional-commit style.
  • If push was provided, the branch is pushed upstream.
  • Every branch deemed unsafe in step 2 is left untouched in all cases.

Signals

GitHub stars
516
Forks
59
Last commit
Sep 2026
Advanced
Catalog kind
skill
Gateway key
commit-opsmill
Source
github.com/opsmill/infrahub