From pebkac-push
PEBKAC Push: A guided git workflow for non-technical contributors to push code changes with proper hygiene. Use this skill whenever a user wants to commit, push, or submit a PR and they are not a professional developer — or whenever anyone asks for help with git push hygiene, safe commits, PR preparation, or code submission guardrails. Also trigger when a user says things like 'push my changes', 'submit a PR', 'I'm done with my changes', 'get this ready for review', 'submit my changes', 'clean up and push', or 'PEBKAC push'. This skill is especially important when the user has been working in an AI coding assistant and wants to safely get their work into the repo without breaking things or pushing to the wrong branch.
How this skill is triggered — by the user, by Claude, or both
Slash command
/pebkac-push:pebkac-pushThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
**P**roblem **E**xists **B**etween **K**eyboard **A**nd **C**hair — and that's fine. This skill exists so that non-engineers can contribute to a codebase with the same hygiene a senior developer would enforce, without needing to know how git works under the hood.
Problem Exists Between Keyboard And Chair — and that's fine. This skill exists so that non-engineers can contribute to a codebase with the same hygiene a senior developer would enforce, without needing to know how git works under the hood.
The user is likely a founder, designer, PM, or other non-technical contributor who has been making changes — possibly with the help of an AI coding assistant — and now needs to get those changes safely into the repo for review. They may not know what a "rebase" is, and they shouldn't have to.
Your job is to be the knowledgeable friend who walks them through it. Be warm, be clear, explain what you're doing and why in plain language, but don't over-explain things that aren't relevant to the decision at hand. Move briskly. The goal is safe code in a clean PR — not a git tutorial.
Read references/safety-checks.md — it contains the full checklists for workspace auditing and code safety scanning. You'll reference it during Phase 2.
Read references/production-checks.md — it contains patterns for catching code that works locally but could cause problems at scale. You'll reference it during Phase 3.
Before doing any work, make sure the user's machine can actually talk to the remote. There's nothing worse than doing all the prep work and then failing on push.
"Before we start, let me make sure your machine can talk to GitHub (or wherever your code lives)..."
git ls-remote --heads origin 2>&1 | head -1
If this succeeds, you're good — move to Step 2. If it fails, determine why:
SSH auth failure (Permission denied (publickey)):
ls ~/.ssh/id_ed25519.pub ~/.ssh/id_rsa.pub 2>/dev/nullssh-keygen -t ed25519 -C "[email protected]"
Then help them add it to GitHub/GitLab (link to the web UI, or use gh ssh-key add if gh is available).ssh -T [email protected] for more details.HTTPS auth failure (fatal: Authentication failed):
git config credential.helpergh CLI is available, suggest gh auth loginRemote not configured (fatal: 'origin' does not appear to be a git repository):
"Great, your machine can push to the remote. Let's keep going."
git config commit.gpgsign
git config tag.gpgsign
If signing is required (true), verify the signing key is available:
# For GPG signing
git config user.signingkey
gpg --list-secret-keys --keyid-format=long 2>/dev/null
# For SSH signing (newer git feature)
git config gpg.format
git config user.signingkey
If signing is required but no key is configured or available:
"This repo requires signed commits, but I can't find a signing key set up on your machine. We have two options: I can help you set up commit signing, or we can proceed without it for now (the reviewer can help sort this out). What would you prefer?"
If the user wants to skip signing for now, note it for the PR description but do not modify git config — let the user make that decision explicitly.
Detect what hooks are installed so we're not surprised later:
# Check for hook managers
ls .husky/_/ 2>/dev/null && echo "Husky hooks detected"
ls .git/hooks/pre-commit 2>/dev/null && echo "Git pre-commit hook detected"
ls .pre-commit-config.yaml 2>/dev/null && echo "pre-commit framework detected"
ls .lefthook.yml 2>/dev/null && echo "Lefthook detected"
ls .lintstagedrc* 2>/dev/null && echo "lint-staged detected"
If hooks are detected, note them. We'll run them proactively in Phase 3 before committing, so the user isn't surprised by a silent failure.
"I see this project has some automated checks that run before each commit. I'll run those checks ahead of time so we catch any issues early."
Figure out where we are and make sure we're not about to do something dangerous.
git rev-parse --show-toplevel # repo root
git branch --show-current # current branch
git remote -v # remote info
git log --oneline -5 # recent commits for context
Check if the current branch is main, master, develop, production, staging, or any branch that looks like a shared/protected branch. If it is, stop immediately and tell the user:
"You're working directly on [branch], which is a shared branch that other people depend on. We need to move your changes to a feature branch before we do anything else. I'll handle this for you."
Then:
git stash
git switch -c feature/[descriptive-name]-[date]
git stash pop
Use context from the changes to name the branch descriptively (e.g., feature/update-pricing-page-2026-03-15).
Ask the user one clear question:
"What were you working on? Give me a sentence or two about what this branch is supposed to accomplish."
Store this answer — it's the lens for everything that follows. You'll use it to evaluate whether changes are in-scope and to write the PR description.
This is the most important phase. Take a holistic look at everything that's been touched and make sure the user intends all of it.
git status # staged, unstaged, untracked
git diff --stat # summary of changes
git diff --name-only # changed files list
git diff --name-only --cached # staged files
ls -la $(git ls-files --others --exclude-standard) # untracked files
If all of the following are true, offer the user a streamlined path:
references/safety-checks.md)package.json, requirements.txt, etc.)"This is a small, clean set of changes — just [N] files, all related to what you described. I can do a quick review and get this pushed fast, or I can do the full deep-dive. Which do you prefer?"
Quick path skips Phase 3 Steps 5–7 (scoped tests, regression tests, production-readiness scan) and streamlines Phase 2 to a single confirmation of the change list. The safety scan (Phase 2 Step 3), lint checks (Phase 3 Step 3), and pre-commit hook dry run (Phase 3 Step 4) always run regardless.
Read and follow the checklists in references/safety-checks.md. This scans for secrets, debug code, sensitive files, large binaries, dependency changes, hardcoded URLs, and other hazards. Anything flagged gets surfaced to the user before proceeding.
If anything critical is found (secrets, API keys, credentials), stop and fix it immediately — do not continue until these are resolved. For non-critical findings (debug logs, TODOs), flag them and let the user decide.
Group every touched file into one of three buckets:
Present this to the user as a simple summary. For suspect files, show a brief description of what changed and ask whether it was intentional. Something like:
"Here's everything that changed. Most of it lines up with what you described. A few things I want to check on:"
src/utils/formatting.ts— you added a helper function. Was this part of the pricing page work, or something separate?package.json— a new dependency was added (date-fns). Intentional?
The user's answers determine what gets committed. If something wasn't intentional, help them stash or revert it.
For changes the user wants to exclude:
git restore path/to/file # revert a modified file
git restore --staged path/to/file # unstage a staged file
git stash push -m "unrelated changes" -- path/to/file # save for later
Explain what you're doing: "I'm setting aside those formatting changes so they don't go into this PR. They'll still be here when you want them."
Auto-detect the project's tooling and run quality checks. The user doesn't need to know what tools are installed — just find them and run them.
Steps 2–4 (build, lint, pre-commit hooks) form a fix-and-verify loop. Every time you fix something — a build error, a lint warning, an auto-fix side effect — you may introduce a new issue. The loop works like this:
"I've done a few rounds of fixes, but there are still some issues I can't fully resolve automatically. Here's what's left: [list]. Want me to keep working on these, or should we flag them for the reviewer?"
The first pass is usually the big one. Subsequent passes tend to catch small regressions from fixes — a missing import after removing an unused variable, a type error after adding a hook dependency, etc.
Don't loop Steps 5–7 (tests, regression tests, production scan). Those are read-only checks that don't produce fixes, so they only need to run once.
Tell the user what's happening if it takes more than one pass:
"My fixes introduced a couple new issues — totally normal. Running the checks again to clean those up..."
Look for these signals (check in order):
| Signal | Likely stack |
|---|---|
package.json | Node.js / JavaScript / TypeScript |
requirements.txt or pyproject.toml | Python |
Cargo.toml | Rust |
go.mod | Go |
Gemfile | Ruby |
pom.xml or build.gradle | Java/Kotlin |
Read the relevant config files to find the project's build, lint, and test commands. Check for scripts in package.json, Makefile targets, or CI config files (.github/workflows/, .gitlab-ci.yml) which often reveal the exact commands the team uses.
Run the project's build command. If it fails, show the user the error in plain language and help fix it. Do not proceed until the build passes.
Run the project's linter with --fix (or equivalent) to auto-fix what it can. Scope to changed files so you're not fixing pre-existing issues across the whole codebase:
# Get all changed files (staged + unstaged) vs HEAD, excluding deleted files
CHANGED=$(git diff --name-only --diff-filter=d HEAD)
# JavaScript/TypeScript
JS_FILES=$(echo "$CHANGED" | grep -E '\.(ts|tsx|js|jsx)$')
[ -n "$JS_FILES" ] && npx eslint --fix $JS_FILES
# Python
PY_FILES=$(echo "$CHANGED" | grep -E '\.py$')
if [ -n "$PY_FILES" ]; then
if command -v ruff >/dev/null; then
ruff check --fix $PY_FILES
elif command -v autopep8 >/dev/null; then
autopep8 --in-place $PY_FILES
fi
fi
# Rust
[ -f Cargo.toml ] && cargo clippy --fix --allow-dirty 2>/dev/null
# Go
GO_FILES=$(echo "$CHANGED" | grep -E '\.go$')
[ -n "$GO_FILES" ] && gofmt -w $GO_FILES
For issues that can't be auto-fixed and require human judgment, explain them simply and ask.
Most people ignore lint warnings because the build still passes. But certain categories of warnings cause real bugs that are painful to track down — things that work most of the time, then break under specific timing or state conditions.
Run the linter on the same changed files from Step 3a (without --fix this time, to see remaining warnings):
# JavaScript/TypeScript (ESLint)
[ -n "$JS_FILES" ] && npx eslint $JS_FILES
# Python — check which tool is available, then run it
if [ -n "$PY_FILES" ]; then
if command -v ruff >/dev/null; then
ruff check $PY_FILES
elif command -v flake8 >/dev/null; then
flake8 $PY_FILES
elif command -v pylint >/dev/null; then
pylint $PY_FILES
fi
fi
# Rust
[ -f Cargo.toml ] && cargo clippy 2>&1 | grep "warning:"
# Go
[ -n "$GO_FILES" ] && go vet ./... 2>&1
# For other stacks: run the project's lint command and filter output
# to only warnings in changed files. Check CI config or Makefile for
# the team's standard lint command.
Triage warnings into two groups:
Group 1 — Warnings that cause bugs (fix these):
These are the warning rules you're looking for. The technical details below are for your reasoning — explain findings to the user in plain language (see user-facing examples at the end of this section).
react-hooks/exhaustive-deps — The highest-priority warning. Missing dependencies in useEffect, useCallback, or useMemo cause stale closures — the hook captures an old version of a variable and never sees updates. Symptoms: UI shows stale data, effects don't re-fire when they should, callbacks use outdated state. These bugs are intermittent and maddening to reproduce.
useCallback or useMemo upstream.// eslint-disable-next-line react-hooks/exhaustive-deps as a last resort, and always add a comment explaining why the suppression is safe.Unused variables that suggest dead code paths — An unused import or variable can mean a code path was accidentally disconnected. Check whether something should be using it.
Unreachable code after return/throw — Usually means a logic error where code was added in the wrong place.
Implicit type coercions (eqeqeq, no-implicit-coercion) — == instead of === can cause subtle type bugs.
Group 2 — Cosmetic warnings (note but don't block):
Present to the user in plain language — don't expose internal jargon like "stale closures" or "dependency arrays":
"The linter found some warnings. Most people ignore these, but a few of them can actually cause bugs that are really hard to track down. Let me check each one..."
Then show findings grouped:
Warnings that can cause bugs:
src/hooks/useChatAgent.ts:86— There's a spot where the code remembers an old version of your project info and doesn't notice when it changes. This can make the UI show outdated data. I'll fix it so it always uses the latest.src/hooks/useChatAgent.ts:492— Same kind of issue — a few functions are "frozen in time" and won't pick up changes. Fixing this so they stay current.Cosmetic (won't cause bugs):
- 2 minor style issues — I'll clean these up while I'm in there.
Fix the bug-risk warnings, then re-run the linter to confirm they're resolved. If any fixes change behavior, re-test the affected UI.
If hooks were detected in Phase 0, run them proactively now — before the user tries to commit — so failures aren't surprising:
# For Husky / standard git hooks
# Stage the confirmed files first, then test the hook
git stash --keep-index # isolate staged changes
.git/hooks/pre-commit 2>&1 || .husky/pre-commit 2>&1
git stash pop
# For pre-commit framework
pre-commit run --files $(git diff --name-only --cached)
# For lint-staged (usually triggered by pre-commit hook)
npx lint-staged --diff="HEAD" 2>&1
If a hook fails:
"The project has an automated check that runs before every commit, and it caught something. Here's what it found: [plain-language explanation]. Let me fix this before we go further."
Fix the issues, then re-run to confirm they pass.
Don't run the full test suite unless necessary. The goal is fast, relevant feedback.
How to scope tests:
foo.test.ts alongside foo.ts, test_foo.py for foo.py, __tests__/foo.spec.js, etc.).# Jest (JS/TS)
npx jest --findRelatedTests src/pricing.ts src/utils/format.ts
# Pytest (Python)
pytest tests/test_pricing.py tests/test_format.py
# Go
go test ./pkg/pricing/... ./pkg/format/...
# Vitest
npx vitest run --reporter=verbose src/pricing.ts src/utils/format.ts
If tests fail:
This step is a suggestion, not a gate. Offer it, but don't push hard.
"If you want, I can write a quick test to make sure this feature keeps working after future changes. Otherwise I'll note it in the PR for the reviewer. Up to you."
If the user opts in:
calculateDiscount() returns the right numberIf the user declines:
Read and follow the checklists in references/production-checks.md. This catches patterns that work fine locally but could cause problems at scale — unnecessary API calls, missing caching, expensive imports, memory leaks.
Present findings conversationally:
"I noticed a few things that work fine locally but could cause problems in production:"
src/pricing.ts:42— This API call runs on every page load. At 10K users/day, that's 10K extra requests. Should this be cached or loaded once at build time?src/utils/format.ts:15— You're importing all ofmoment.js(300KB) but only usingformat(). Want me to switch to a lighter alternative?
If nothing is found, skip silently. Don't mention this step if there's nothing to flag.
Note: In quick mode, this step is skipped.
Make sure the feature branch is up to date with the branch it'll merge into.
git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's|refs/remotes/origin/||'
If that doesn't work, fall back to checking for main or master:
git branch -r | grep -E 'origin/(main|master)$' | head -1 | sed 's|origin/||' | tr -d ' '
Confirm with the user: "This will be merged into main — is that right?"
Check what this repo prefers — merge or rebase:
# Check git config
git config pull.rebase
# Look at recent history for signals
echo "=== Recent merges ==="
git log --merges --oneline -5
echo "=== Recent non-merge commits ==="
git log --no-merges --oneline -5
If pull.rebase is set to true, or the history shows mostly linear commits (few merge commits), the repo likely prefers rebase. If there are many merge commits, it prefers merge.
If unclear, ask:
"This repo seems to prefer [merge/rebase] based on the history. Want me to use that approach?"
git fetch origin
If repo prefers merge:
git merge origin/[target-branch] --no-edit
If repo prefers rebase:
git pull --rebase origin [target-branch]
If there are merge/rebase conflicts, walk the user through each one. Explain what the conflict is in plain language:
"There's a conflict in
src/pricing.ts. Someone else changed line 42 while you were also editing nearby. Here's what they changed vs. what you changed — which version should we keep?"
When multiple PRs are open against the same target branch, merging one can cause conflicts in the others — even if each PR looks clean against the target right now. The target "moves ahead" as PRs land, and what was conflict-free yesterday can be a mess tomorrow.
This step catches that problem before it surprises anyone.
Check for sibling PRs (requires gh CLI):
CURRENT_BRANCH=$(git branch --show-current)
gh pr list --base [target-branch] --state open --json number,headRefName,title \
| jq --arg cur "$CURRENT_BRANCH" '[.[] | select(.headRefName != $cur)]'
This filters out the current branch's own PR so we don't try to merge ourselves into ourselves.
If gh isn't available or not authenticated, skip this step and note it in the PR description: "Could not check for conflicts with other open PRs."
If there are no other open PRs (after filtering), skip this step silently.
If there are other open PRs (up to ~5), tell the user what's happening:
"There are N other PRs waiting to merge into [target]. If any of them merge before yours, new conflicts could appear. Let me check if your branch is compatible with theirs too."
For each sibling PR branch, test for conflicts without actually merging. First, verify the working tree is clean (it should be by this point in the workflow), then use stash as a safety net:
git fetch origin [sibling-branch]
# Safety: stash any uncommitted work (should be none, but just in case)
DIRTY=false
git diff --quiet HEAD && git diff --cached --quiet || DIRTY=true
[ "$DIRTY" = true ] && git stash --include-untracked -m "pebkac-push: safety stash before sibling check"
BEFORE=$(git rev-parse HEAD)
git merge --no-commit --no-ff origin/[sibling-branch] 2>&1
# Always reset to where we were — git merge --abort doesn't work
# if the merge completed cleanly, so reset --hard is more reliable
git reset --hard $BEFORE
# Restore stashed work if any was stashed
[ "$DIRTY" = true ] && git stash pop
If there are more than 5 open sibling PRs, ask the user:
"There are [N] other PRs targeting [target]. That's a lot to check. Do you know which ones are most likely to merge soon? I'll focus on those."
If no conflicts are found with any sibling:
"Good news — your branch is clean against all the other open PRs. No surprises when things start merging."
If conflicts are found with a sibling branch:
"Your branch conflicts with PR #[N] ('[title]'). If that PR merges first, you'll have conflicts to resolve. We have two options:"
- Resolve now — I'll merge their branch into yours so you're ready regardless of merge order. This is the safer option if their PR is close to merging.
- Note it and move on — I'll flag it in your PR description so the reviewer knows. You'd deal with it later if and when it comes up.
If the user opts to resolve now:
git merge origin/[sibling-branch] --no-edit
# Resolve any conflicts with the user
Then re-run build and lint to make sure nothing broke.
In the PR description, add a line under Merge notes:
After syncing, re-run build, lint, and the scoped tests from Phase 3. Syncing can introduce breakage even when there are no textual conflicts. If anything fails now that didn't fail before, it's likely a sync-related issue — help the user resolve it.
Only stage the files the user confirmed as intentional in Phase 2:
git add [list of confirmed files]
Generate a clear, descriptive commit message based on the user's stated intent and the actual changes. Follow conventional commit format if the repo uses it (check recent commits for style), otherwise write a clear summary + body.
Show the commit message to the user for approval before committing.
git commit -m "[approved message]"
git push origin [branch-name]
If the branch doesn't exist on the remote yet, use:
git push -u origin [branch-name]
If the commit fails (e.g., due to a pre-commit hook that wasn't caught in Phase 3 Step 4):
"The commit was blocked by an automated check. Let me see what happened..."
Diagnose the hook failure, fix the issue, re-stage, and create a new commit. Never use --no-verify to bypass hooks.
If the push fails due to auth issues, refer back to the Phase 0 troubleshooting steps.
Generate a comprehensive PR and help the user submit it.
Check if the project uses GitHub, GitLab, or Bitbucket (look at the remote URL). Use the appropriate CLI if available (gh, glab), otherwise generate the PR body for the user to paste into the web UI.
Use this structure:
## Summary
[1-2 sentence description based on the user's stated intent]
## Changes
[Structured list of what changed, grouped logically — not just a file list.
Describe what each change *does*, not just which files were touched.]
## Out-of-scope changes
[If any files were flagged as suspect but confirmed intentional, explain
WHY they're in this PR. Be specific: "Updated formatting utility because
the new pricing component depends on the currency formatter added here."]
## Testing
- Build: ✅ passing
- Lint: ✅ passing (N auto-fixed issues; lint warnings reviewed)
- Quality gates verified in N pass(es) — all clean
- Scoped tests (N files related to changes): ✅ N passed, 0 failed
- [Note if full suite was run instead, and why]
- [New regression test added for: (feature intent) — or: "No regression test added, see reviewer notes"]
## Production readiness
- [Any production concerns flagged and addressed — or: "No production concerns detected"]
## Merge notes
- Synced with latest `[target-branch]` via [merge/rebase] — no conflicts
[or: resolved N conflicts in X, Y, Z files — details below]
- [Sibling PR sync: describe status — e.g. checked N open PRs with no
conflicts, pre-resolved conflicts with specific PRs, known conflicts
flagged, or check skipped if gh CLI was unavailable]
- [Any pre-existing test failures noted here]
## Reviewer notes
[Anything the reviewer should pay attention to — complex logic,
areas where the contributor wasn't sure, architectural decisions,
pre-commit hook status if signing was skipped, etc.]
If gh CLI is available:
gh pr create --title "[title]" --body "[body]" --base [target-branch]
If not, tell the user exactly where to go and give them the formatted PR body to paste.
The user just contributed to a codebase with proper hygiene. Acknowledge it:
"PR is up and ready for review. Clean build, tests passing, good documentation. Nice work — your reviewer is going to love this."
Things will go wrong. Here's how to handle common situations:
The user has uncommitted work and the build is broken: Don't panic. Stash everything, get back to a clean state, then work through the issues one at a time.
The user doesn't know what branch they should target: Check the repo's default branch and recent PR history. If still unclear, ask: "Who will review this? What branch do they usually merge into?"
The merge has horrible conflicts: If there are more than 3-4 conflicts or they're in complex files, suggest the user loop in a technical teammate before resolving. Generate a summary of what's conflicting and why so the conversation is productive.
Tests are failing and the user can't fix them: Note the failures in the PR description, mark the PR as draft if possible, and flag it for technical review. Don't block a non-technical contributor indefinitely on test failures they can't resolve.
The user accidentally committed to main: Help them move it safely:
git log --oneline -3 # find the commit
git stash # save any uncommitted work
git branch feature/[name] # create branch at current point (keeps the commit)
git reset --soft HEAD~1 # undo the commit on main, keep changes staged
git stash push -m "changes from main" # stash the changes
git switch feature/[name] # switch to the new branch
git stash pop # apply the changes there
git add .
git commit -m "[original message]" # recommit on the feature branch
Now fix main:
git switch main
Before resetting, confirm with the user:
"I'm about to reset
mainto match the remote. Your changes are safely on the feature branch. Proceed?"
git reset --hard origin/main # restore main to remote state
git switch feature/[name] # back to the feature branch
Explain every step: "We copied your changes to a safe branch and put the shared branch back to where it was. Nothing is lost."
npx claudepluginhub stessodiy/pebkac-push --plugin pebkac-pushManages Git commit workflow using Conventional Commits format with safety protocols. Creates, validates, executes commits; handles hooks, PRs, and safety checks before operations.
Orchestrates multi-agent git workflow from code review and quality checks through testing, Conventional Commits, PR creation, and deployment readiness. Supports trunk-based and feature-branch strategies.
Automates code submission workflow: lint check, non-destructive review, verification doc, smart staging, clean commit, MR creation for Android/iOS/backend projects. Triggers on 'submit code' etc. phrases.