From Z Skills
Safe commit workflow with optional scope hint. Inventories all changes, classifies related vs. unrelated files, traces dependencies, protects other agents' work, and optionally pushes, lands worktree commits, or opens a PR via /land-pr. Positional auto enables auto-merge (PR mode only).
How this skill is triggered — by the user, by Claude, or both
Slash command
/zs:commit [pr] [scope] [push|land] [auto][pr] [scope] [push|land] [auto]The summary Claude sees in its skill listing — used to decide when to auto-load this skill
Commit current work without picking up or harming unrelated changes.
Commit current work without picking up or harming unrelated changes.
Arguments:
/commit — commit only, infer scope from diffs/commit skill updates — commit, scope guided by "skill updates"/commit push — commit and push to remote/commit parser reset button fix push — scope-guided commit + push/commit land — cherry-pick worktree commits into main (worktree only)/commit pr — push current branch and create a PR to main (requires clean working tree)/commit pr fix pr comments — PR mode with scope hint "fix pr comments"Parsing: pr is recognized ONLY when it is the FIRST token in
$ARGUMENTS. This prevents false-triggering on scope hints that contain
"pr" mid-string. push and land are reserved keywords for non-PR mode.
When no explicit mode token is supplied, the skill consults
execution.landing in .claude/zskills-config.json to pick the default
(see the config-driven default-mode block below).
if [ -n "${ZSH_VERSION:-}" ]; then setopt KSH_ARRAYS BASH_REMATCH SH_WORD_SPLIT 2>/dev/null || true; fi
FIRST_TOKEN=$(echo "$ARGUMENTS" | awk '{print $1}')
# Positional `auto` token (case-insensitive). Recognized ONLY in PR mode
# (`/commit pr auto` or — via the config-default-to-pr branch below —
# `/commit auto`). Matches the convention in /run-plan, /fix-issues, /do.
# The token never falls through to SCOPE_HINT (stripped in both PR-mode
# parse paths). Setting AUTO_FLAG outside PR mode is a no-op — `push`
# and `land` modes do not consult it.
AUTO_FLAG=0
if [[ "$ARGUMENTS" =~ (^|[[:space:]])([aA][uU][tT][oO])($|[[:space:]]) ]]; then
AUTO_FLAG=1
fi
if [[ "$FIRST_TOKEN" == "pr" ]]; then
# PR subcommand mode
SCOPE_HINT=$(echo "$ARGUMENTS" | cut -d' ' -f2-) # rest after 'pr' (may be empty)
# Strip the positional `auto` token from SCOPE_HINT so it does not leak
# into the PR title / scope-hint downstream. Match `auto` as a
# whitespace-bounded token (case-insensitive).
SCOPE_HINT=$(echo "$SCOPE_HINT" | sed -E 's/(^|[[:space:]])[aA][uU][tT][oO]($|[[:space:]])/\1\2/g' | sed -E 's/^[[:space:]]+|[[:space:]]+$//g; s/[[:space:]]+/ /g')
# ... see Phase 6 (PR mode) below
fi
Config-driven default mode (when no explicit mode token is given):
If $ARGUMENTS contains no explicit mode token (pr as first token, or
push/land anywhere), read execution.landing from
.claude/zskills-config.json to pick the default. This mirrors the
landing-mode resolution in /run-plan, /fix-issues, and /do so
projects with execution.landing: "pr" (the default preset, which also
sets main_protected: true) get PR mode for /commit without users
needing to retype pr every time. Bash regex only — no jq, matching the
co-author read in Phase 5.
# Detect any explicit mode signal in the arguments. `pr` is first-token-only
# (to avoid false-triggering on scope hints); `push` and `land` are
# recognized anywhere (matching their parsing throughout the rest of this
# skill, e.g., `/commit skill updates push`).
if [ -n "${ZSH_VERSION:-}" ]; then setopt KSH_ARRAYS BASH_REMATCH SH_WORD_SPLIT 2>/dev/null || true; fi
HAS_EXPLICIT_MODE=0
if [[ "$FIRST_TOKEN" == "pr" ]]; then
HAS_EXPLICIT_MODE=1
elif [[ "$ARGUMENTS" =~ (^|[[:space:]])(push|land)($|[[:space:]]) ]]; then
HAS_EXPLICIT_MODE=1
fi
if [ "$HAS_EXPLICIT_MODE" -eq 0 ]; then
# No explicit mode — consult execution.landing from config. CASCADE v2
# (ENFORCEMENT_V2 Phase 4): source the canonical lane-portable resolver and
# read the resolved $ZSKILLS_CFG_LANDING (project > user > empty) instead of
# an inline BASH_REMATCH read. NO behavior change intended — the enum
# dispatch below maps the resolved value into DEFAULT_MODE exactly as before;
# $ZSKILLS_CFG_LANDING is EMPTY when neither tier sets execution.landing, in
# which case the empty-string arm preserves the commit-only default. The
# user-tier fill is the only added effect (per #1159 scope).
if [ -f "${CLAUDE_PLUGIN_ROOT}/skills/update-zskills/scripts/zskills-resolve-config.sh" ]; then
. "${CLAUDE_PLUGIN_ROOT}/skills/update-zskills/scripts/zskills-resolve-config.sh"
else
. "$CLAUDE_PROJECT_DIR/.claude/skills/update-zskills/scripts/zskills-resolve-config.sh"
fi
# $ZSKILLS_CFG_LANDING is empty when neither tier sets execution.landing —
# the `direct|""` arm below maps that (and an explicit `direct`) to the
# commit-only default, byte-equivalent to the old "no config / unmatched
# regex → commit" path.
CFG_LANDING="$ZSKILLS_CFG_LANDING"
case "$CFG_LANDING" in
pr)
# Treat as `/commit pr` — drop into PR subcommand mode below.
# SCOPE_HINT keeps any scope words from $ARGUMENTS (no `pr` prefix
# to strip, since the user didn't type it). Strip the positional
# `auto` token (AUTO_FLAG was already set above) so it does not
# leak into the PR title / downstream scope-hint usage.
SCOPE_HINT=$(echo "$ARGUMENTS" | sed -E 's/(^|[[:space:]])[aA][uU][tT][oO]($|[[:space:]])/\1\2/g' | sed -E 's/^[[:space:]]+|[[:space:]]+$//g; s/[[:space:]]+/ /g')
DEFAULT_MODE="pr"
;;
direct|"")
# Commit-only default — current behavior preserved.
DEFAULT_MODE="commit"
;;
cherry-pick)
# `/commit land` is the cherry-pick subcommand for landing
# worktree commits onto main, NOT a default-mode selector. A
# config of `cherry-pick` here is almost certainly a misconfig
# (the user probably wanted `pr` or `direct`). Surface loudly
# rather than silently picking a behavior.
echo "ERROR: execution.landing=\"cherry-pick\" is not a valid default for /commit." >&2
echo " /commit land is the cherry-pick subcommand for landing worktree" >&2
echo " commits onto main; it is NOT a default-mode selector. Set" >&2
echo " execution.landing to \"pr\" or \"direct\" in" >&2
echo " .claude/zskills-config.json, or invoke /commit land explicitly." >&2
exit 1
;;
*)
# Unknown value — fall back to commit-only.
DEFAULT_MODE="commit"
;;
esac
# If config says `pr`, behave exactly as `/commit pr` from here on:
# skip Phases 1–5 and run modes/pr.md end-to-end.
if [ "$DEFAULT_MODE" = "pr" ]; then
FIRST_TOKEN="pr" # so downstream "is FIRST_TOKEN pr?" branches engage
fi
fi
Disambiguation:
/commit pr → PR mode (first token is pr)/commit pr comments fix → PR mode (first token is pr), scope hint: "comments fix"/commit fix pr format → scope hint "fix pr format", regular commit (first token is "fix")/commit with execution.landing: "pr" in config → PR mode (config default)/commit with execution.landing: "direct" or no config → commit-only (preserved default)/commit push or /commit land always overrides config — explicit wins/commit pr auto → PR mode + auto-merge via /land-pr --auto/commit auto (config default = pr) → PR mode + auto-mergeExamples:
/commit → scope: (none), action: commit/commit codegen fixes → scope: "codegen fixes", action: commit/commit skill updates push → scope: "skill updates", action: commit + push/commit push → scope: (none), action: commit + push/commit land → scope: (none), action: land/commit parser fixes land → scope: "parser fixes", action: land/commit pr → action: PR mode (push + create PR; settles at pr-ready)/commit pr fix pr comments → PR mode, scope hint: "fix pr comments"/commit pr auto → PR mode + auto-merge (passes --auto to /land-pr)/commit auto (config default = pr) → PR mode + auto-mergePR subcommand behavior is defined in modes/pr.md; land behavior in modes/land.md.
Run these in parallel:
git status -s # all changes (never use -uall)
git diff # unstaged changes
git diff --cached # staged changes
git log --oneline -10 # recent commit style
Also determine context:
git rev-parse --show-toplevel differs from main repo)For every changed and untracked file from git status -s, decide:
The scope hint tells you what this commit is about. Use it to drive classification:
Keyword grep — search the file list for scope-related terms:
git status -s | grep -i <keyword>
For /commit skill updates, grep for skill, SKILL, .claude/skills/.
For /commit codegen fixes, grep for codegen, block-emitter, Rust.
Diff-check remaining files — for files that don't match the keyword, read the diff to confirm they're unrelated. Some related files won't match a simple grep (e.g., a memory file updated alongside skill changes).
Confidence is higher — with a scope hint, you can be more decisive about what's in vs. out. Without one, you have to read every diff and infer.
Fall back to the original approach:
??): are they part of this feature? If unsure, ask.For every file classified as "related":
git status -s | grep -i <feature-keyword>
If a scope hint was provided, use terms from the hint as keywords..claude/logs/ — include session logs for this session.Common mistakes to avoid:
A.js which imports B.js without committing B.js → 404??)Check for pre-staged files before adding anything:
git diff --cached --stat
If files are already staged that you didn't stage, another session left
them in the index. STOP and report — do not commit on top of someone
else's staged work. Past failure: commit b69ec3f swept in 146 lines of
another session's codegen changes because they were pre-staged in the index.
Stage only the related files by name:
git add file1 file2 ...
NEVER use git add . or git add -A — these grab everything.
Review what's staged — verify the count matches what you just added:
git diff --cached --stat
If the file count is higher than the number of files you staged in step 2, something else was already in the index. Investigate before committing.
Present the staged file list to the user and confirm it looks correct.
Draft a commit message:
git log --oneline -10)Run tests if code was staged — if any staged files are code (.js,
.css, .html, .rs), run the full test suite before committing using
the canonical capture idiom:
if [ -f "${CLAUDE_PLUGIN_ROOT}/skills/update-zskills/scripts/zskills-resolve-config.sh" ]; then
export CLAUDE_PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT}"
. "${CLAUDE_PLUGIN_ROOT}/skills/update-zskills/scripts/zskills-resolve-config.sh"
else
. "$CLAUDE_PROJECT_DIR/.claude/skills/update-zskills/scripts/zskills-resolve-config.sh"
fi
if [ -z "$FULL_TEST_CMD" ]; then
echo "ERROR: testing.full_cmd not configured. Run /update-zskills." >&2
exit 1
fi
TEST_OUT="/tmp/zskills-tests/$(basename "$(pwd)")"
mkdir -p "$TEST_OUT"
$FULL_TEST_CMD > "$TEST_OUT/${TEST_OUTPUT_FILE:-.test-results.txt}" 2>&1
Read "$TEST_OUT/${TEST_OUTPUT_FILE:-.test-results.txt}" to check
results. All suites must pass. If tests fail, read the captured output
file to diagnose. If tests fail after two fix attempts on the same error,
STOP and report to the user (CLAUDE.md: "NEVER thrash on a failing fix").
Skip this step for content-only commits (.md, .jpg, .png, logs).
2.5. Skill-version bump check. For every staged file under
skills/<owner>/..., verify each affected
skill's metadata.version was correctly bumped (date refreshed AND hash
matches recomputed projection):
bash "$CLAUDE_PROJECT_DIR/scripts/skill-version-stage-check.sh" || {
echo "STOP: skill version check failed; see message above." >&2
exit 1
}
Exit non-zero halts /commit until the agent fixes. The script's STOP
message includes the exact bump command for each affected skill.
Dispatch a fresh agent to review the staged changes before committing.
The agent receives git diff --cached and the proposed commit message.
It checks:
Do NOT review the staged changes yourself — you selected the files, so you have selection bias. A fresh agent catches files you missed or included by mistake.
Dispatch shape. Use the Agent tool with subagent_type: "verifier". The dispatch prompt MUST include the verbatim preamble: "You are reviewing the staged diff. Read-only review — do NOT git stash, checkout, restore, reset, add, rm, commit, push, merge, rebase, cherry-pick, revert, tag, or branch -D. Do NOT edit, write, or delete any file. Read the diff, run any read-only checks (git diff, git log, git show), and report concerns or approve." After the dispatch returns, pipe $VERIFIER_RESPONSE through bash "$ZSKILLS_SKILLS_ROOT/update-zskills/scripts/verify-response-validate.sh"; on exit 1 STOP without committing.
The reviewer is READ-ONLY. Include this verbatim in the dispatch prompt (defense-in-depth, preserved alongside the Dispatch-shape preamble above):
You are read-only. Allowed: Read files, run read-only git (
diff,log,show,show-ref,ls-files,ls-remote,status), run existing tests. FORBIDDEN:git stash(push/-u/save/bare),checkout,restore,reset,add,rm,commit, editing files, creating worktrees. For pre-fix behavior, usegit show <commit>:<file>— don't modify reality. (Past failure: reviewer rangit stash -u && test && git stash pop; the pop silently unstaged the caller's staged files.)
Layer 3 — verifier response validation. Immediately after the dispatch returns:
# Resolve $ZSKILLS_SKILLS_ROOT (lane-portable) — canonical dual-lane
# prelude, references/canonical-config-prelude.md §1.
if [ -f "${CLAUDE_PLUGIN_ROOT}/skills/update-zskills/scripts/zskills-resolve-config.sh" ]; then
export CLAUDE_PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT}"
. "${CLAUDE_PLUGIN_ROOT}/skills/update-zskills/scripts/zskills-resolve-config.sh"
else
. "$CLAUDE_PROJECT_DIR/.claude/skills/update-zskills/scripts/zskills-resolve-config.sh"
fi
printf '%s' "$VERIFIER_RESPONSE" | bash "$ZSKILLS_SKILLS_ROOT/update-zskills/scripts/verify-response-validate.sh"
VALIDATE_EXIT=$?
On VALIDATE_EXIT=1 — STOP without committing. Do NOT stage anything else, do NOT amend, do NOT proceed to step 4. Emit:
STOP: verifier returned without meaningful results.
$(cat /tmp/last-validate-stderr)
This is a verification FAIL, not a license to commit. Resolve the
verifier failure (re-dispatch only after confirming the verifier
agent file is installed: .claude/agents/verifier.md exists; bash
$CLAUDE_PROJECT_DIR/.claude/hooks/inject-bash-timeout.sh < /dev/null
exits 0). If the verifier agent is missing, run /update-zskills.
If the agent raises concerns: STOP. Report the concerns to the user. Do not commit until concerns are resolved.
If the agent approves AND VALIDATE_EXIT=0: proceed to step 4.
Resolve the Co-Authored-By trailer from config, then commit.
The trailer value lives in .claude/zskills-config.json under
commit.co_author. Source the canonical helper to populate
$COMMIT_CO_AUTHOR. If the field is absent or the file is missing,
$COMMIT_CO_AUTHOR is empty — that's a valid consumer opt-out
(no Co-Authored-By trailer):
if [ -f "${CLAUDE_PLUGIN_ROOT}/skills/update-zskills/scripts/zskills-resolve-config.sh" ]; then
export CLAUDE_PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT}"
. "${CLAUDE_PLUGIN_ROOT}/skills/update-zskills/scripts/zskills-resolve-config.sh"
else
. "$CLAUDE_PROJECT_DIR/.claude/skills/update-zskills/scripts/zskills-resolve-config.sh"
fi
Commit using a HEREDOC for clean formatting. The heredoc delimiter
must stay quoted (<<'EOF') so that any $(...), backticks, or
$VAR substrings that happen to appear in <type> or <message>
are treated as literal text rather than executed by the shell. The
trailer is injected separately via git commit --trailer, which
handles RFC 5322 formatting and keeps $COMMIT_CO_AUTHOR out of the
heredoc body entirely. Emit the trailer ONLY if $COMMIT_CO_AUTHOR
is non-empty:
if [ -f "${CLAUDE_PLUGIN_ROOT}/skills/update-zskills/scripts/zskills-resolve-config.sh" ]; then
export CLAUDE_PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT}"
. "${CLAUDE_PLUGIN_ROOT}/skills/update-zskills/scripts/zskills-resolve-config.sh"
else
. "$CLAUDE_PROJECT_DIR/.claude/skills/update-zskills/scripts/zskills-resolve-config.sh"
fi
if [ -n "$COMMIT_CO_AUTHOR" ]; then
git commit \
--trailer "Co-Authored-By: $COMMIT_CO_AUTHOR" \
-m "$(cat <<'EOF'
<type>: <message>
EOF
)"
else
git commit \
-m "$(cat <<'EOF'
<type>: <message>
EOF
)"
fi
Verify:
git status -s
If a pre-commit hook fails: fix the issue, re-stage, and create a NEW
commit. NEVER use --amend after a hook failure — the commit didn't happen,
so amend would modify the PREVIOUS commit. Maximum 2 attempts on the same
hook error — if it fails twice, STOP and report.
push argument)Only if push was in the arguments:
git push
If there's no upstream tracking branch:
git push -u origin <branch-name>
NEVER force-push to main/master. If push is rejected, tell the user why and ask what to do.
pr is the first token)When the first argument token is pr, this flow replaces Phases 1–5.
Read modes/pr.md in full and follow its procedure end-to-end. Do not proceed until you have read that file.
land argument)Read modes/land.md in full and follow its procedure end-to-end. Do not proceed until you have read that file.
git add . or git add -A — stage files by name only.git checkout --, git restore, git reset, or otherwise discard
changes you didn't make.push argument — commit only by default.git stash push/-u/save/bare. For
cherry-pick flows, use the try-without-stash pattern (Phase 7 step 3)..claude/logs/ — session logs should be committed alongside
code changes./commit pr requires a clean working tree — all changes must be
committed before running PR mode. The pre-check enforces this./commit pr keyword is first-token-only — prevents false-triggering
on scope hints that contain "pr" mid-string.git log origin/main..HEAD — not git log main..HEAD
(local main may be stale after rebase).gh pr view — bare view uses ambient
branch autodetect, which is subject to race conditions.Provides UI/UX resources: 50+ styles, color palettes, font pairings, guidelines, charts for web/mobile across React, Next.js, Vue, Svelte, Tailwind, React Native, Flutter. Aids planning, building, reviewing interfaces.
Fetches up-to-date documentation from Context7 for libraries and frameworks like React, Next.js, Prisma. Use for setup questions, API references, and code examples.
npx claudepluginhub zeveck/zskills-dev --plugin zs