---
name: git-commit
description: Cleepi's commit-message convention. Conventional Commits + AngularJS types, with monorepo-aware domain (package short name), optional ticket-id, subject-line only (no body, no footers, NEVER Co-authored-by). Load when about to write a git commit message in a cleepi-style repo, or when asked about commit conventions.
---

# git-commit

You are about to write a git commit message in a cleepi-style
repo. Follow this convention exactly. The rules below are not
suggestions.

## The format

```
<type>(<domain>): [<ticket-id> ]<message>
```

Examples (the only shape you produce):

```
feat(crew): AC-201 add reviewer pre-step
refactor(sdd): AC-202 collapse Why-now into Why
docs(crew): fix README install path
chore(meta): bump prettier
fix(atlassian): DRAFT-007 handle empty JQL response
test(git): cover ticket-id detection
build(crew): pin pi-subagents to 0.25.0
ci(meta): run smoke tests on pull request
revert(sdd): undo phase-3 AGENTS.md change
```

Real `git log` from a cleepi repo should read as a clean list
of one-liners like the above. No multi-line messages anywhere.

## Hard rules

These are non-negotiable. AI tooling and humans both break them.
The skill exists to stop that.

- **Subject line only.** One line. No body. No footers.
- **NEVER `Co-authored-by:`.** Not for AI assistants. Not for
  pair programmers. Not for "credit." If your tooling tries to
  append one, **strip it before committing.** This is a hard
  rule, not a preference.
- **No `Signed-off-by:`** either. Same reason. The skill doesn't
  ship a DCO workflow; commits stay clean.
- **No emoji, no decorations.** `feat(crew):` not `✨ feat(crew):`.
- **No breaking change footer.** If a change is breaking, use
  the bang syntax (`feat(crew)!: ...`) and put the *rationale*
  in the spec, not in the commit.
- **No trailing period** on the subject line.
- **Imperative, present tense, lowercase first letter** of the
  message after the colon (or after the ticket-id if present).
  "add x," not "Adds x" or "Added x" or "Add x.".

## Types

Fixed AngularJS / Conventional Commits set. No extension via
config; teams wanting other types edit a project-scope copy of
this skill.

| Type | Use for |
|---|---|
| `feat` | New feature or user-visible capability |
| `fix` | Bug fix |
| `docs` | Documentation only (README, comments, skill files) |
| `style` | Formatting, whitespace, no code semantics change |
| `refactor` | Code change that's neither feat nor fix |
| `perf` | Performance improvement |
| `test` | Adding or fixing tests |
| `chore` | Build glue, deps, tooling that's neither ci nor build |
| `build` | Build system, package config, dependency bumps |
| `ci` | CI config, workflows, automation |
| `revert` | Reverting a previous commit |

If you can't pick between two types, prefer the more specific
one. `feat` is overused; if the change is an internal cleanup,
it's `refactor`, not `feat`.

## Domain

In a cleepi-style monorepo, **domain = the affected package's
short name** (`crew`, `sdd`, `git`, `atlassian`, `coding`,
`base`, etc.). One package per commit. If a change spans two
packages, split into two commits.

Outside the `packages/` tree, domain is a free-form feature
area:

- `meta` — repo-level meta files (root README, root CHANGELOG,
  AGENTS.md, CONSTITUTION.md)
- `docs` — repo-level docs/ and specs (when not tied to a
  package)
- `ci` — repo-level CI config
- `infra` — infra-as-code, deployment glue
- `release` — release prep, version bumps, tags

Don't use sub-areas of a package as the domain (`crew-agents`
or `sdd-skill`). Use the package name (`crew`, `sdd`); the
commit message says what changed.

## Ticket-id

**Highly recommended. Not required.**

Format: `[A-Z]+-[0-9]+`. Cleepi ticket prefixes include
`AC-NNN` (Atlassian), `LIN-NNN` (Linear), `gh-NN` (GitHub
issue), `DRAFT-NNN` (pre-tracker work that's still spec'd).

When present, the ticket-id sits **directly after the colon**,
single space before the message:

```
feat(crew): AC-201 add reviewer pre-step
```

NOT `[AC-201]`, NOT `(AC-201)`, NOT `AC-201:`. Just the bare
token, then space, then message.

When omitted (small chores, typos, doc tweaks):

```
docs(crew): fix README install path
chore(meta): bump prettier
```

The skill won't fail a commit for missing ticket-id, but it
will gently flag the omission for `feat`/`fix`/`refactor`/`perf`
commits, since those are the ones that should anchor on a
spec'd ticket.

## Message style

- **Imperative, present tense.** "add X," "fix Y," "remove Z."
  Not "added," "fixes," "removing."
- **Lowercase first letter** of the message (after the ticket-id
  if present). Acronyms and proper nouns stay cased.
- **No trailing period.**
- **Concise but specific.** "fix bug" is useless. "fix off-by-one
  in pagination" is a commit message.
- **Around 50–72 characters total** is the target. Hard cap 100.
- **No "and"** if you can avoid it. "feat(crew): add X and refactor
  Y" → two commits.

## Anti-patterns (don't do these)

| ❌ Bad | ✓ Good | Why |
|---|---|---|
| `feat: add reviewer` | `feat(crew): AC-201 add reviewer pre-step` | Missing domain, ticket, specificity. |
| `Update README` | `docs(crew): fix install path in README` | No type, no domain, vague. |
| `feat(crew): Added reviewer.` | `feat(crew): AC-201 add reviewer pre-step` | Past tense + trailing period + missing ticket. |
| `feat(crew-agents): add reviewer` | `feat(crew): AC-201 add reviewer pre-step` | Domain is package, not sub-area. |
| `feat(crew): AC-201 add reviewer\n\nCo-authored-by: Claude` | `feat(crew): AC-201 add reviewer pre-step` | Subject-line only. NEVER Co-authored-by. |
| `feat(crew): ✨ add reviewer` | `feat(crew): add reviewer pre-step` | No emoji. |
| `feat(crew,sdd): cross-package change` | `feat(crew): ...` + `feat(sdd): ...` | Split into two commits. |
| `wip: stuff` | (don't commit yet, or `chore(<domain>): wip <specific>`) | "wip" as type isn't in the set. If you must, use `chore` and be specific. |

## Resolution chain for `/commit`

When invoked via the `/commit` slash command, the prompt resolves
context in this order (manual args always win):

1. **Type** — first arg to `/commit`. Required. Validated against
   the type table above.
2. **Domain** — nearest `packages/<n>/` ancestor of cwd → package
   short name. Outside packages, prompt asks (or accepts a
   free-form arg).
3. **Ticket-id** — explicit token in args → ticket-id pattern in
   any **staged file path** (e.g. `docs/AC-NNN-slug/...`) →
   candidate surfaced from recently-modified `docs/<ticket>-slug/`
   folders in the repo (asks for confirmation, never auto-picks)
   → git branch name (grep `[A-Z]+-[0-9]+`) → ask user.

   This differs from `@cleepi/sdd`'s `/journal` chain because
   `/commit` runs from inside *code*, not the ticket folder.
   `/journal` walks up from cwd; `/commit` reads staged paths
   and branch name instead.
4. **Message** — remaining args to `/commit`.
5. **Co-authored-by strip** — defence-in-depth: scan the
   assembled message for any `Co-authored-by:` line and remove
   it before passing to `git commit`. This rule overrides
   everything else, including the user's explicit input.

**Resolution is non-skippable.** Even when the user says "just
commit" or auto-confirms, type/domain/ticket-id resolution
still runs. The user can skip the *confirmation* step; they
cannot skip resolution. A bare `feat: <msg>` commit is a
resolution failure, not a valid output.

## Voice and length

The skill mirrors the `@cleepi/sdd` discipline:

- **Concise.** Cleepi commits are one-liners. So is the message.
- **Specific.** "fix bug" is not a commit message; it's a
  shrug. Name the thing.
- **No filler.** "Various improvements" → no. List the actual
  changes in separate commits.
- **Imperative.** Read the message after "If applied, this
  commit will…" — it should fit naturally.

If you cannot describe a change as one specific imperative line
under ~70 characters, the commit is too big. Split it.

## When the user disagrees

If the user wants a body, footer, or `Co-authored-by:`, push
back once with a one-liner referencing this skill. If they
insist, **don't comply silently** — instead, write the message
their way *and* add a one-line note in your reply that you
overrode the cleepi convention at their explicit request.
That way the next session sees the deviation logged.
