# Commit

> Standardized git commit, push, and PR creation workflow.

- Skill: `djnsty23/commit` (Agent Skill)
- Install (CLI): `npx skillmds@latest add djnsty23/commit`
- Raw SKILL.md: https://api.skillmd.com/api/skills/djnsty23/commit/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Productivity
- Author: djnsty23 (https://skillmd.com/u/djnsty23)
- Updated: 2026-09-10
- Page: https://skillmd.com/skills/djnsty23/commit

---


# Commit Workflow

> **Browser access.** Use the built-in browser tools. `mcp__Claude_Browser__*`
> covers navigation, DOM reads (`read_page`), screenshots and `resize_window`;
> reach for chrome-devtools `emulate` when a mobile *device* gate has to fire,
> which `resize_window` alone does not guarantee. The `browser` skill and the
> `agent-browser` steps were dropped in 8.79.0 — do not reach for that CLI here.
> (The binary itself is still installed for kb-factory's JS-rendered crawls;
> that is a separate consumer, not a fallback for page verification.)

## Working Tree
!`git status --short 2>/dev/null`
!`git diff --stat HEAD 2>/dev/null | tail -5`
!`git log --oneline -5 2>/dev/null`

## Quick Commit

```bash
# 1. Check what changed
git status --short
git diff --stat

# 2. Stage specific files (prefer targeted adds over git add -A)
git add src/components/new-feature.tsx src/lib/utils.ts

# 3. Commit with conventional format
git commit -m "feat: add playlist drag-drop reorder"
```

## Evidence goes IN the commit

If the change fixes a defect or moves a visible surface, the `prove` skill's
before/after pair belongs in this commit, not beside it. Two reasons, and the
second is the one that bites:

- A reviewer reading the commit later has no other route to it.
- `.claude/evidence/` is tracked, so evidence left uncommitted dirties the tree
  and any gate refusing a dirty tree then refuses to run at all.

Name the paths in the body and state the delta in one line. A difference you
cannot state in a sentence is one you have not checked.

## Conventional Commits (Required)

```
<type>: <short description>

[optional body]
```

| Type | When |
|------|------|
| `feat` | New feature |
| `fix` | Bug fix |
| `refactor` | Code restructure, no behavior change |
| `chore` | Dependencies, config, tooling |
| `docs` | Documentation only |
| `test` | Add or update tests |
| `perf` | Performance improvement |

**Rules:**
- Subject line < 70 chars
- Imperative mood: "add" not "added"
- No period at end
- Body explains WHY, not WHAT
- Include story ID when available: `feat(S13-001): add playlist UI`

## Commit, then ask before pushing

```bash
git add <files>
git commit -m "feat: description"
```

**Stop at the commit.** `rule-local-first/SKILL.md` holds that an ad-hoc
`git push`, PR or merge needs the operator to say so in that turn, and that "it is
ready to push" is a status line rather than a licence. Report the commit and ask.
Once the answer is yes, in that turn:

```bash
git push origin HEAD
```

### When a git hook refuses the commit or the push

A `commit-msg` or `pre-push` hook that exits non-zero is a gate, and
`--no-verify` skips it. The PreToolUse guard asks before that flag runs, and
its reason names the hook file and the script it runs. The question that decides
whether skipping is right is not *is the gate red* but **is it red at the base
branch too**, and it costs one detached worktree to answer:

```bash
git fetch -q origin
BASE=$(mktemp -d)
git worktree add -q --detach "$BASE" origin/main        # or the default branch
( cd "$BASE" && node <the script the hook runs> ); echo "base exit $?"
node <the same script>; echo "branch exit $?"
git worktree remove --force "$BASE"
```

Compare the FAIL lines, not the exit codes: two reds with different lines are
two different findings.

| base | branch | verdict |
|---|---|---|
| green | red | the red is this change's; fix it, no bypass |
| red | red, same lines | a trunk red; the bypass is correct and the RECORD is the deliverable |
| red | red, more lines | both; fix the extra lines, then the rest is trunk's |

Then, with the operator's yes in that turn, push with `--no-verify` and put one
line in the commit or PR body naming the gate skipped, why it was red, and that
it reproduces at the base. The PostToolUse note after the push asks for exactly
this line. `[measured 2026-09-07]` four sessions bypassed a trunk-red pre-push in
one night and every one was right; the bypass that could be told from a skipped
gate afterwards was the one that wrote the line down.

### Branch Strategy

Check before branching:
```bash
# Solo project? (1 contributor, no branch protection)
CONTRIBUTORS=$(git shortlog -sn --all 2>/dev/null | wc -l)
HAS_REMOTE=$(git remote 2>/dev/null | head -1)
```

- **Solo project** (1 contributor or no remote): commit directly to main — branching adds ceremony with zero value.
- **Team project** (2+ contributors or CI/branch protection): create a feature branch from main.

```bash
# Only branch for team projects
if [ "$CONTRIBUTORS" -gt 1 ] && [ -n "$HAS_REMOTE" ]; then
  BRANCH=$(git branch --show-current)
  if [ "$BRANCH" = "main" ] || [ "$BRANCH" = "master" ]; then
    git checkout -b feat/[descriptive-name]
  fi
fi
```

## Post-Commit Quick Check

After every commit, run a 5-second sanity check:
```bash
npm run build 2>&1 | tail -3
# If dev server running, check for console errors
curl -s http://localhost:3000 > /dev/null 2>&1 && echo "server up - verify with navigate + read_page"
```

If errors found, fix immediately and amend the commit.

## Full PR Flow (commit-push-pr)

```bash
# 1. Create branch if on main
BRANCH=$(git branch --show-current)
if [ "$BRANCH" = "main" ] || [ "$BRANCH" = "master" ]; then
  git checkout -b feat/[descriptive-name]
fi

# 2. Stage and commit
git add <files>
git commit -m "feat: add playlist UI with drag-drop"

# 3. Push -- ONLY once the operator has said so in this turn
git push -u origin HEAD
```

### Auto-Generate PR Description from prd.json

If prd.json exists and has completed stories, generate the PR body from them:
```bash
node -e "
const p=require('./prd.json');
const stories=p.stories||{};
const done=Object.entries(stories).filter(([,s])=>s.passes===true);
if(done.length){
  console.log('## Changes');
  done.forEach(([id,s])=>console.log('- **'+id+'**: '+s.title+(s.resolution?' ('+s.resolution+')':'')));
  console.log('\n## Test Plan');
  done.forEach(([id,s])=>console.log('- [ ] Verify '+s.title));
}
"
```

Use this output as the PR body:
```bash
gh pr create --title "[Sprint summary]" --body "[generated from prd.json]"
```

## Safety Checks

**Before committing (if ANY fail, fix before proceeding):**
- [ ] `npm run typecheck` passes
- [ ] `npm run build` passes
- [ ] `npm test -- --watchAll=false --passWithNoTests` passes
- [ ] No `.env` files staged — unstage if found
- [ ] No `console.log` in staged files — remove if found
- [ ] No hardcoded secrets — remove if found

**Before pushing:**
- [ ] Branch is correct (not pushing to main accidentally)
- [ ] Branch is up-to-date with remote: `git fetch && git status`
- [ ] Commit messages are clean

If issues found: fix them, re-stage, re-run checks, THEN commit.

## Version Sync Check (claude-auto-dev repo only)

When committing to this repo, check for stale version strings before staging:

```bash
# Read current version
VERSION=$(cat VERSION 2>/dev/null)

# Grep for previous version references (skip CHANGELOG.md - it's historical)
grep -rn "4\.9\.4\|v4\.9" --include="*.md" --include="*.json" --include="*.ps1" --include="*.sh" . \
  | grep -v CHANGELOG.md | grep -v node_modules | grep -v .git
```

If stale versions found: **fix them before committing.**

In a repo that ships Claude Code plugins, the version lives in `VERSION`,
`package.json`, `.claude-plugin/marketplace.json`, and every
`plugins/*/.claude-plugin/plugin.json`. Do not edit them by hand — run the
repo's bumper (`node tooling/bump.js <x.y.z>` in this project) so they cannot
drift, then update the README badge.

The current version is: !`cat VERSION 2>/dev/null`

## Batch Commit (During Auto Mode)

During `auto`, commit every 3 tasks:
```bash
git add -A -- ':!.env*' ':!*.pem' ':!*.key' ':!*.secret'
git commit -m "feat: complete S9-1 through S9-3

- S9-1: Playlist UI with drag-drop
- S9-2: Song extend from timestamp
- S9-3: Onboarding wizard"
```

## Amend Last Commit

Only if not pushed yet:
```bash
git add <missed-files>
git commit --amend --no-edit
```

## Undo Last Commit (Keep Changes)

```bash
git reset --soft HEAD~1
```

