commit · git:20261031.3177d90 · 2026-10-31 · sha256 406229108559d069

commit git:20261031.3177d90A

Immutable. This exact content never changes and is served at /api/v1/blob/406229108559d069.

---
name: commit
description: "Split the working-tree changes into atomic commits, preflight and push them. Mention --skip-preflight or --no-push in the request to skip a step."
---
<!-- ported-from: plugins/ac/skills/commit/SKILL.md sha256:1998bcc7147b10c20f1e27a1cdf6034c7a712a1fe899c8b933cbd23d447fb058 -->

# commit

Commit the current working-tree changes as atomic commits using the `git-master` skill.

The request text is the input; this skill takes no other arguments.

## Phase 0: Parse the request

1. Scan the request text for `--skip-preflight` and set `SKIP_PREFLIGHT = true | false`.
2. Scan the request text for `--no-push` and set `NO_PUSH = true | false`. Also set `NO_PUSH = true` when `.ac/state/active-auto.json` exists with `harness: "opencode"` and a `session_id` equal to the `root_session_id` the `ac_session` tool returns: that is an `ac/auto` away run, which publishes only from its own close, after its verification gate, and only what the user named. A push from here would land before the gate has judged the work.
3. Treat any other tokens as a free-form note; ignore them for now (the commit-style detection in Phase 2 produces the actual message).

## Phase 1: Context

Capture the current git state up front so the rest of the skill works from real data, not assumptions. Run these with the `shell` tool, in parallel where possible:

- Current branch: `git branch --show-current`
- Working-tree status: `git status --short`
- Staged diff stat: `git diff --staged --stat`
- Unstaged diff stat: `git diff --stat`
- Recent commits (30 for style detection): `git log -30 --pretty=format:"%h %s"`
- Upstream tracking: `git rev-parse --abbrev-ref @{upstream} 2>/dev/null || echo "NO_UPSTREAM"`
- Merge base with main/master: `git merge-base HEAD main 2>/dev/null || git merge-base HEAD master 2>/dev/null || echo "NO_BASE"`
- Local-only commits ahead of base: `git log --oneline $(git merge-base HEAD main 2>/dev/null || git merge-base HEAD master 2>/dev/null)..HEAD 2>/dev/null || echo "NO_BASE"`

If the working tree is clean (status output empty), stop with the message `Nothing to commit. Working tree clean.` and skip every remaining phase.

## Phase 2: Preflight (skip if `SKIP_PREFLIGHT`)

**Goal**: Catch obvious breakage before writing history.

Skip this phase when `SKIP_PREFLIGHT = true`. The execute skill's Phase 4 sets this flag because its Phase 2d checked each wave's touched files and ran the project-wide typecheck, and its Phase 3a ran the build, the full suite and the linter across the whole project; another pass here would be the same commands on the same tree.

When `SKIP_PREFLIGHT = false`:

1. Read `CLAUDE.md` and `CLAUDE.local.md` if present to find typecheck, test, and lint commands (sections labelled `commands`, `scripts`, or under a "Build / Test / Lint" header). Fall back to the `package.json` `scripts` block when those files do not name the commands.
2. Run typecheck, then tests, then linter in that order. Each one through the `shell` tool. Use the project's actual commands, not generic guesses.
3. **Pass**: continue to Phase 3.
4. **Fail**: stop. Print which preflight step failed and the first ~30 lines of the failure output. Do not commit. The user fixes the issue and asks for the commit again.

If no preflight commands are discoverable, log `Preflight: no commands found in CLAUDE.md or package.json; skipping.` and continue. Do not block on missing config.

## Phase 3: Delegate to `git-master` (COMMIT MODE)

**Goal**: Produce atomic commits in the repo's detected style.

1. Load the `git-master` skill with the `skill` tool (id `git-master`). Its body carries the full COMMIT MODE workflow (Phase 0 parallel context, Phase 1 style detection with blocking output, Phase 2 branch safety, Phase 3 atomic-unit planning with blocking output, Phase 4 strategy and execution, Phase 5 verification).
2. Honor every blocking output the skill prints: the STYLE DETECTION block and the COMMIT PLAN block both have to render in the chat before any `git commit` runs.
3. Honor the skill's split discipline. Apply the atomic-unit rule to every change set, not only the first: one commit per unit that can be reviewed and reverted alone, with file count as a hint and not the rule. A commit holding two units is a planning bug; resplit before executing.
4. Keep test files paired with their implementation in the same commit. The skill enumerates the test-path patterns.
5. Stay on the current branch. Never rebase or rewrite published history without a separate explicit user request.
6. Commit with the git identity exactly as `git config` has it: no `-c user.name`, `-c user.email` or `--author` override, and no `Co-Authored-By` or `Signed-off-by` trailer naming the operator.

The skill returns control after every planned commit has been created (verified via `git log --oneline <merge-base>..HEAD`).

## Phase 4: Push (skip if `NO_PUSH` or no upstream)

**Goal**: Publish the new commits.

Skip this phase when `NO_PUSH = true` or the Phase 1 upstream check printed `NO_UPSTREAM`.

1. **Branch guard**: if the current branch is `main` or `master`, print a warning and ask before pushing.
   ```
   question({
     questions: [{
       header: "Push main?",
       question: "Current branch is `<branch>`. Push directly?",
       options: [
         {label: "Push", description: "Push to <upstream>."},
         {label: "Skip push", description: "Leave commits local; user pushes manually."}
       ]
     }]
   })
   ```
   On "Skip push", jump to Phase 5 with a note that the push was skipped.
2. **Push strategy** (from the skill's Phase 5):
   - New commits only, no rewritten history: `git push`.
   - Fixup commits applied or rebase happened: the push rewrites published history, which a commit request did not ask for. Ask first with the `question` tool (header `Force push?`, options `Push with --force-with-lease` / `Skip push`), the same way the branch guard asks; on `Skip push`, jump to Phase 5 with a note. Plain `--force` is never used.
3. Capture the push output (success line + remote ref). On failure, print the error and stop; the user resolves and asks again.

## Phase 5: Report

Render one block at the end so the user sees the outcome at a glance:

```
## Commit Complete

Branch: <name> [<upstream> | local-only]
Style: <SEMANTIC | PLAIN | SENTENCE | SHORT>
Commits created: <N>
  <hash> <message>
  <hash> <message>
Push: <pushed to <remote>/<branch> | skipped (no upstream) | skipped (--no-push) | skipped (user declined)>
Preflight: <PASS | SKIPPED | NOT CONFIGURED>
```

When this skill was started from the execute skill's Phase 4, the report block is the handoff signal that the execution pipeline is done.

## Error Handling

- **Nothing to commit**: Phase 1 detected a clean tree. Print the clean-tree line and exit; do not run preflight, the skill, or push.
- **Preflight failure**: print the failing step and first ~30 lines of output. Do not stage anything, do not load the skill, do not push. The user fixes and asks again.
- **Skill split refused**: the skill prints a `COMMIT PLAN` that leaves two atomic units in one commit. Tell the user the split is too coarse, name the units that were merged, and re-run the planning step (the skill's own validation should catch this; this is a backstop).
- **Merge conflicts mid-rebase**: the skill is in COMMIT MODE, not REBASE MODE; conflicts here mean a fixup or `--autosquash` raced with concurrent edits. Stop, print `git status`, surface the conflict to the user, and abort the operation (`git rebase --abort` if a rebase is in progress).
- **Push rejected (non-fast-forward)**: the remote has commits the local branch does not. Stop. Do not force-push without an explicit user request. Print the remote's view (`git log <upstream>..HEAD` and `git log HEAD..<upstream>`) and let the user decide.
- **Force-push needed on protected branch**: refuse and ask. Force-pushing `main`/`master` requires an explicit user "yes" via the `question` tool; plain `--force` stays off the table regardless.

## Reminders

- The `git-master` skill drives the actual commit planning and message writing. Phase 3 of this skill is the entry point; do not duplicate its style-detection or splitting logic here.
- Apply the split rule to every change set, not only the first. One commit holding two atomic units is a planning bug.
- Use `--force-with-lease` when rewriting history. Plain `--force` is not used by this skill.
- The execute skill always starts this skill with `--skip-preflight` because its per-wave 4-layer check and the Phase 3 review already covered the same ground.