atomic-commit
Turn the uncommitted work from a session into a clean series of atomic Conventional Commits. The skill reads the repo's own conventions, splits the changes into the smallest sensible commits, and either commits them after your confirmation — or, when you ask it not to, just shows exactly what would go where.
Opted out? If the repo config sets commit to false, this skill is disabled for the repo — stop immediately and tell the user the atomic-commit skill is turned off in .tituskirch-skills.json. An absent commit block is not disabled (it falls back to detection/defaults). Check .commit == false on the resolved config before any action — and before indexing .commit.*. A missing jq or config exits non-zero too, so a pass is not evidence the config was read.
Workflow
1. Detect conventions (read the repo — never assume)
Conventions are repo-wide, not branch-specific, and change rarely — so cache them per-repo instead of re-detecting every run. The cache is shared across TitusKirch skills (e.g. pull-request reads the same convention for its PR title) at $(git rev-parse --git-common-dir)/tituskirch-skills/conventions. Before sampling, check for a fresh cache there: reuse it on a commitlint-config hash match regardless of age when a hashable config exists — the match already proves the conventions are unchanged; with no hashable config (conventions inferred from git log) fall back to a 3-day TTL. Re-detect with the recipes below — and rewrite the cache — when it is missing, the config hash differs, the TTL-only fallback has expired, or the user asks to refresh ("neu prüfen", "refresh", "--refresh"). Cache mechanics, the shared namespace, and the one-time migration from the old atomic-commit-cache: REFERENCE.md.
A committed, optional config can override detection: if $(git rev-parse --show-toplevel)/.tituskirch-skills.json exists it wins per setting — commit.language (or the shared root language) for message language, commit.scopes (true/false/auto) to force scope usage, commit.scopeVocab to seed the scope vocabulary, and commit.instructions for free-text wording guidance. Resolve it via templates/resolve-config.sh, never by reading the raw file (REFERENCE.md states how, missing jq included). Resolution per setting: config → detected/native → built-in default — except that a commitlint rule (a hard constraint the commit-msg hook enforces) always wins over a soft config preference like commit.scopes/commit.scopeVocab. Keys this skill reads: REFERENCE.md.
- Scopes on/off —
commit.scopes wins when set (true/false); on auto (the default) sample git log --pretty='%s' -n 80 and count subjects matching type(scope): vs type: (majority wins; ties or thin history fall back to a commitlint scope-enum, else off). A commitlint scope-enum/scope-empty rule overrides either way.
- Types & scopes in use — collect the types and scope words already present and prefer them; union the scope words with
commit.scopeVocab when set, dropping any the commitlint scope-enum forbids.
- Scope drift (soft signal) — when
commit.scopeVocab is set and a commit's chosen scope isn't in it (a new or renamed skill / package / area), the vocab is behind the repo. Note it once in the plan and hand off to tituskirch-skills-config — never edit the config from here. A commitlint scope-enum, not the vocab, still decides what is a legal scope.
- commitlint — look for
commitlint.config.*, .commitlintrc*, or a commitlint key in package.json. If found, treat its rules (type-enum, scope-enum, header-max-length, subject-case, body-max-line-length) as hard constraints.
- Language —
.tituskirch-skills.json commit.language (then root language) if set, else match the language of existing subjects; default to English.
- Instructions — when
commit.instructions is set, apply its free-text guidance to subject/body wording; it never overrides commitlint or the release-type rules below.
Exact detection recipes: REFERENCE.md.
2. Survey the changes
git status --porcelain + git diff (unstaged) + git diff --staged + list untracked files.
- Read the diffs so you understand what changed, not just which files. Fold anything already staged into the plan.
3. Plan atomic commits
- One logical change per commit. Split feature vs. fix vs. refactor vs. docs vs. tooling/config vs. tests. If you'd join two changes with the word "and", they are probably two commits.
- Order by dependency — deps/config/scaffolding first, then the core change, then tests, then docs/chores. Each commit should ideally leave the tree building.
- Hunk-level when needed — if one file mixes concerns, assign individual hunks to different commits (REFERENCE.md).
- Pick
type(scope): subject per group from the detected conventions. Subject = imperative, lowercase, no trailing period, within the max length. Add a body only when the why isn't obvious; when you do, hard-wrap every body line to the repo's body-max-line-length (100 by default under config-conventional, unless disabled) — one long line is the most common commit-msg hook rejection.
- Release-relevant changes must be
feat/fix — when in doubt, never refactor. Under release-please (release-please-config.json, .release-please-manifest.json, or a release-please workflow), only feat:/fix: cut a release and reach the changelog; refactor/chore/perf/… ship nothing and stay invisible to users. The error is asymmetric — a change mis-typed refactor is silently dropped from the release, far worse than a slightly noisy changelog — so if a change could be release-relevant, type it feat/fix. Reserve refactor for changes you are certain are effect-free and invisible to consumers; when the changed files are the shipped product (a skills/library/template repo), improving them is a feat/fix, never a refactor. Details: REFERENCE.md.
- Reference the issue when in issue context. If this session is clearly working on a specific issue (its
#N came up, or you created/updated one via the issue skill), add a Refs #N footer to the commit(s) that relate to it — reference only, not Closes (closing is the PR's job). Don't tag unrelated commits.
4. Present the plan (always, before any commit)
Show the detected conventions, then a numbered commit plan: each commit's message and the files/hunks it includes, plus a one-line rationale. Flag any leftover/unassigned changes. Format in REFERENCE.md.
5. Commit — or stop
- Plan-only triggers ("nicht committen", "nur den Plan", "wie würdest du das committen", "don't commit", "dry run", "just show me"): stop after the plan and print the exact
git commands the user could run. Do not run git commit.
- Otherwise: ask for confirmation, then execute group by group — stage exactly that group's files/hunks, commit, verify with
git diff --cached --stat before and git log -1 after. Reset staging between groups so commits stay atomic.
Presenting the plan
Everything this skill puts in front of a human — plan, preview, candidate list, findings report —
is read once, in a terminal, and answered there. So every section of it renders on arrival,
with no interaction needed to reveal it: prose, lists, tables, fenced code.
Never fold content behind a control. <details>/<summary> is a browser widget, and a
terminal has no way to open it: the summary line prints and everything under it does not. The plan
then arrives as headings with nothing beneath them, and the failure is silent on both sides —
the skill believes it reported, and the reader sees no marker saying anything is missing, so a
human confirms a plan whose contents never reached them. What gets folded is whatever ran long,
which is to say the part the decision actually rested on. The same holds for anything else needing
a click: a tab strip, an accordion, a "show more".
Length is handled by shortening, never by hiding. This is a fixed rule of the skill, not a
per-run judgement, so it holds however long the list runs. Trim to what the decision needs, group
the rest by something the reader already thinks in (ecosystem, kind, verdict) with a count per
group, or split it across sections. What is left out is left out visibly: say how many, why,
and the exact command that shows the rest.
This binds what the skill presents, not what it writes. A <details> block inside a README, an
issue body, a pull request description or a docs page is rendered by a browser and is entirely
legitimate there. The rule is about the message a human reads to decide — never about the content
of a file.
Guardrails
- Never
--no-verify or --no-gpg-sign. Let husky/commitlint hooks run; respect the repo's git setting commit.gpgsign (git config, not the commit section of .tituskirch-skills.json — this skill has no key for signing) so signed repos stay signed. If a hook rejects a commit, surface the error and stop.
- Don't assume signing is broken. A failing
git commit is almost always a hook (commitlint message format), not signing — read the actual error. git log --show-signature printing N / "allowedSignersFile" locally is not a failure: the commit is still signed — confirm with native git via git cat-file -p <sha> (look for a gpgsig header) or git verify-commit <sha> (SSH signatures need gpg.ssh.allowedSignersFile configured to verify). GitHub's server-side "verified" badge has no native-git equivalent — for that, fall back to gh api repos/<owner>/<repo>/commits/<sha> --jq .commit.verification.
- Commit only — never push, amend, or rebase unless explicitly asked.
- Keep the message attribution-free — it describes the change, never the tool that made it. No
Co-authored-by: naming an assistant, no Generated with/🤖 line, no session/permalink URL, no agent self-naming (Claude, Codex, Copilot, Cursor, or any future assistant). Strip it if the harness injects it, regardless of any attribution setting.
- Don't commit secrets. Scan staged content for
.env, keys, tokens; warn and exclude.
- Respect the branch. If on
main/master and the repo works on feature branches, confirm (or offer to branch) before committing.
- Keep commits atomic and buildable — don't split a symbol from its only call site if that breaks the build; note the trade-off when unavoidable.
- Verify each step. After every commit, confirm it landed and the remaining tree matches the plan.
Reference
Open it at step 1 for the recipes that read a repo's conventions instead of assuming them — the commitlint rules in force, the scope vocabulary, and whether the repo is release-gated. At step 3, the moment one file carries two concerns, for the hunk-level staging mechanics and a worked split of exactly that. Whenever a commit's type or grouping is not obvious, for the type catalogue, the grouping heuristics and the plan's output shape: REFERENCE.md.
1---2name: atomic-commit3description: Commits the current session's working-tree changes as a series of atomic Conventional Commits in the repo's own detected conventions (scopes, types, commitlint rules, message language). Always presents the full plan first and commits only after confirmation; switches to plan-only when asked. Use when the user wants to commit session or feature work, mentions conventional or atomic commits, splitting changes into multiple commits, or says things like "how would you commit this", "just show me the plan", "don't commit yet", "nicht committen".4---56# atomic-commit78Turn the uncommitted work from a session into a clean series of **atomic Conventional Commits**. The skill reads the repo's own conventions, splits the changes into the smallest sensible commits, and either commits them after your confirmation — or, when you ask it not to, just shows exactly what would go where.910**Opted out?** If the repo config sets `commit` to `false`, this skill is **disabled** for the repo — stop immediately and tell the user the atomic-commit skill is turned off in `.tituskirch-skills.json`. An _absent_ `commit` block is **not** disabled (it falls back to detection/defaults). Check `.commit == false` on the resolved config before any action — and before indexing `.commit.*`. A missing `jq` or config exits non-zero too, so a pass is not evidence the config was read.1112## Workflow1314### 1. Detect conventions (read the repo — never assume)1516Conventions are **repo-wide, not branch-specific**, and change rarely — so cache them per-repo instead of re-detecting every run. The cache is **shared across TitusKirch skills** (e.g. `pull-request` reads the same convention for its PR title) at `$(git rev-parse --git-common-dir)/tituskirch-skills/conventions`. Before sampling, check for a fresh cache there: **reuse it on a commitlint-config hash match regardless of age** when a hashable config exists — the match already proves the conventions are unchanged; with no hashable config (conventions inferred from `git log`) fall back to a 3-day TTL. Re-detect with the recipes below — and rewrite the cache — when it is missing, the config hash differs, the TTL-only fallback has expired, or the user asks to refresh ("neu prüfen", "refresh", "--refresh"). Cache mechanics, the shared namespace, and the one-time migration from the old `atomic-commit-cache`: [REFERENCE.md](REFERENCE.md#convention-cache).1718A committed, optional config can override detection: if `$(git rev-parse --show-toplevel)/.tituskirch-skills.json` exists it wins per setting — `commit.language` (or the shared root `language`) for message language, `commit.scopes` (`true`/`false`/`auto`) to force scope usage, `commit.scopeVocab` to seed the scope vocabulary, and `commit.instructions` for free-text wording guidance. Resolve it via [`templates/resolve-config.sh`](templates/resolve-config.sh), never by reading the raw file ([REFERENCE.md](REFERENCE.md#reading-the-config) states how, missing `jq` included). Resolution per setting: **config → detected/native → built-in default** — **except** that a commitlint rule (a hard constraint the `commit-msg` hook enforces) always wins over a soft config preference like `commit.scopes`/`commit.scopeVocab`. Keys this skill reads: [REFERENCE.md](REFERENCE.md#config).1920- **Scopes on/off** — `commit.scopes` wins when set (`true`/`false`); on `auto` (the default) sample `git log --pretty='%s' -n 80` and count subjects matching `type(scope):` vs `type:` (majority wins; ties or thin history fall back to a commitlint `scope-enum`, else off). A commitlint `scope-enum`/`scope-empty` rule overrides either way.21- **Types & scopes in use** — collect the types and scope words already present and prefer them; union the scope words with `commit.scopeVocab` when set, dropping any the commitlint `scope-enum` forbids.22- **Scope drift (soft signal)** — when `commit.scopeVocab` is set and a commit's chosen scope isn't in it (a new or renamed skill / package / area), the vocab is behind the repo. Note it once in the plan and hand off to `tituskirch-skills-config` — never edit the config from here. A commitlint `scope-enum`, not the vocab, still decides what is a legal scope.23- **commitlint** — look for `commitlint.config.*`, `.commitlintrc*`, or a `commitlint` key in `package.json`. If found, treat its rules (`type-enum`, `scope-enum`, `header-max-length`, `subject-case`, `body-max-line-length`) as hard constraints.24- **Language** — `.tituskirch-skills.json` `commit.language` (then root `language`) if set, else match the language of existing subjects; default to English.25- **Instructions** — when `commit.instructions` is set, apply its free-text guidance to subject/body wording; it never overrides commitlint or the release-type rules below.2627Exact detection recipes: [REFERENCE.md](REFERENCE.md#detecting-conventions).2829### 2. Survey the changes3031- `git status --porcelain` + `git diff` (unstaged) + `git diff --staged` + list untracked files.32- Read the diffs so you understand _what_ changed, not just _which files_. Fold anything already staged into the plan.3334### 3. Plan atomic commits3536- **One logical change per commit.** Split feature vs. fix vs. refactor vs. docs vs. tooling/config vs. tests. If you'd join two changes with the word "and", they are probably two commits.37- **Order by dependency** — deps/config/scaffolding first, then the core change, then tests, then docs/chores. Each commit should ideally leave the tree building.38- **Hunk-level when needed** — if one file mixes concerns, assign individual hunks to different commits ([REFERENCE.md](REFERENCE.md#hunk-level-staging)).39- Pick `type(scope): subject` per group from the detected conventions. Subject = imperative, lowercase, no trailing period, within the max length. Add a body only when the _why_ isn't obvious; when you do, **hard-wrap every body line** to the repo's `body-max-line-length` (100 by default under config-conventional, unless disabled) — one long line is the most common `commit-msg` hook rejection.40- **Release-relevant changes must be `feat`/`fix` — when in doubt, never `refactor`.** Under release-please (`release-please-config.json`, `.release-please-manifest.json`, or a release-please workflow), **only `feat:`/`fix:` cut a release and reach the changelog**; `refactor`/`chore`/`perf`/… ship nothing and stay invisible to users. The error is asymmetric — a change mis-typed `refactor` is silently dropped from the release, far worse than a slightly noisy changelog — so **if a change _could_ be release-relevant, type it `feat`/`fix`.** Reserve `refactor` for changes you are certain are effect-free and invisible to consumers; when the changed files _are_ the shipped product (a skills/library/template repo), improving them is a `feat`/`fix`, never a `refactor`. Details: [REFERENCE.md](REFERENCE.md#release-gated-repos).41- **Reference the issue when in issue context.** If this session is clearly working on a specific issue (its `#N` came up, or you created/updated one via the `issue` skill), add a `Refs #N` footer to the commit(s) that relate to it — reference only, not `Closes` (closing is the PR's job). Don't tag unrelated commits.4243### 4. Present the plan (always, before any commit)4445Show the detected conventions, then a numbered commit plan: each commit's message and the files/hunks it includes, plus a one-line rationale. Flag any leftover/unassigned changes. Format in [REFERENCE.md](REFERENCE.md#plan-output).4647### 5. Commit — or stop4849- **Plan-only triggers** ("nicht committen", "nur den Plan", "wie würdest du das committen", "don't commit", "dry run", "just show me"): **stop after the plan** and print the exact `git` commands the user could run. Do **not** run `git commit`.50- **Otherwise**: ask for confirmation, then execute group by group — stage exactly that group's files/hunks, commit, verify with `git diff --cached --stat` before and `git log -1` after. Reset staging between groups so commits stay atomic.5152<skills-plan>5354## Presenting the plan5556Everything this skill puts in front of a human — plan, preview, candidate list, findings report —57is read **once, in a terminal**, and answered there. So **every section of it renders on arrival**,58with no interaction needed to reveal it: prose, lists, tables, fenced code.5960**Never fold content behind a control.** `<details>`/`<summary>` is a browser widget, and a61terminal has no way to open it: the summary line prints and everything under it does not. The plan62then arrives as headings with nothing beneath them, and the failure is silent on **both** sides —63the skill believes it reported, and the reader sees no marker saying anything is missing, so a64human confirms a plan whose contents never reached them. What gets folded is whatever ran long,65which is to say the part the decision actually rested on. The same holds for anything else needing66a click: a tab strip, an accordion, a "show more".6768**Length is handled by shortening, never by hiding.** This is a fixed rule of the skill, not a69per-run judgement, so it holds however long the list runs. Trim to what the decision needs, group70the rest by something the reader already thinks in (ecosystem, kind, verdict) with a count per71group, or split it across sections. What is left out is left out **visibly**: say how many, why,72and the exact command that shows the rest.7374**This binds what the skill presents, not what it writes.** A `<details>` block inside a README, an75issue body, a pull request description or a docs page is rendered by a browser and is entirely76legitimate there. The rule is about the message a human reads to decide — never about the content77of a file.7879</skills-plan>8081## Guardrails8283- **Never `--no-verify` or `--no-gpg-sign`.** Let husky/commitlint hooks run; respect the repo's **git** setting `commit.gpgsign` (`git config`, not the `commit` section of `.tituskirch-skills.json` — this skill has no key for signing) so signed repos stay signed. If a hook rejects a commit, surface the error and stop.84- **Don't assume signing is broken.** A failing `git commit` is almost always a hook (commitlint message format), not signing — read the actual error. `git log --show-signature` printing `N` / "allowedSignersFile" locally is not a failure: the commit is still signed — confirm with native git via `git cat-file -p <sha>` (look for a `gpgsig` header) or `git verify-commit <sha>` (SSH signatures need `gpg.ssh.allowedSignersFile` configured to verify). GitHub's server-side "verified" badge has no native-git equivalent — for that, fall back to `gh api repos/<owner>/<repo>/commits/<sha> --jq .commit.verification`.85- **Commit only — never push, amend, or rebase** unless explicitly asked.86- **Keep the message attribution-free** — it describes the change, never the tool that made it. No `Co-authored-by:` naming an assistant, no `Generated with`/🤖 line, no session/permalink URL, no agent self-naming (Claude, Codex, Copilot, Cursor, or any future assistant). Strip it if the harness injects it, regardless of any `attribution` setting.87- **Don't commit secrets.** Scan staged content for `.env`, keys, tokens; warn and exclude.88- **Respect the branch.** If on `main`/`master` and the repo works on feature branches, confirm (or offer to branch) before committing.89- **Keep commits atomic _and_ buildable** — don't split a symbol from its only call site if that breaks the build; note the trade-off when unavoidable.90- **Verify each step.** After every commit, confirm it landed and the remaining tree matches the plan.9192## Reference9394**Open it at step 1** for the recipes that read a repo's conventions instead of assuming them — the commitlint rules in force, the scope vocabulary, and whether the repo is release-gated. **At step 3, the moment one file carries two concerns**, for the hunk-level staging mechanics and a worked split of exactly that. **Whenever a commit's type or grouping is not obvious**, for the type catalogue, the grouping heuristics and the plan's output shape: [REFERENCE.md](REFERENCE.md).