Template · Intermediate · 5 min
5 must-have Claude Code commands, ready to copy
Five skills for Claude Code: commit, PR description, fix the tests, explain code with Haiku and a review before you push.
Tested with Claude Code 2.1.316 · reviewed
See the files on GitHub Free · MIT license
What's included
A pack of 5 skills for Claude Code 2.1.316, free in the repository ctrlia-pack-comandos (the English version is in its en/ folder). Each one teaches a different frontmatter technique:
| Command | What it does | Technique it teaches |
|---|---|---|
/conventional-commit | Commit with a conventional message in English | Context injection with ! + command, allowed-tools, disable-model-invocation |
/pr-description | PR description from the commits and the diff | Helper script that always exits with 0, optional argument |
/fix-tests | Test loop until green, with no cheating | $ARGUMENTS and effort: high |
/explain-code | Explains code with an Explore subagent on Haiku 5.5, cheap and without touching anything | context: fork, agent, model: haiku, effort: low |
/pre-push-check | Checks for secrets, large files and leftover debugging before the push | Review script + the checks in your CLAUDE.md |
Command or skill: what we are installing
For the user, custom commands and skills are the same thing: a skill in .claude/skills/<name>/SKILL.md is invoked with /name, just like a .claude/commands/<name>.md file, which the documentation calls the old format and which still works. We use skills because they allow helper files (the scripts in /pr-description and /pre-push-check).
Where to install them: in ~/.claude/skills/ they work for all your projects on this machine; in the repository's .claude/skills/, for the whole team once committed. If there is one with the same name in both places, the personal one wins.
We chose names that do not clash with any built-in command in 2.1.316 (there are more than 100: /review, /verify, /simplify, /code-review…): if one of your skills has the same name as a built-in one, it replaces it in your terminal, and that gets confusing. Claude Code watches those folders, so there is no need to restart; only if the skills folder did not exist when the session started, run /reload-skills. If this is the first skill you install, the your first skill recipe explains each field step by step.
1. /conventional-commit and 2. /pr-description
/conventional-commit injects git status --short and the staged diff summary with lines that start with ! and carry the command in backticks, which the documentation describes like this: the command runs before the skill is sent and Claude receives its output, not the command. Then Claude reads the diff and writes a type(scope): summary message in English, in the imperative. It has disable-model-invocation: true because it has side effects (it commits): Claude cannot launch it on its own, only you with /conventional-commit, and the documentation says that this way its description does not even take up context. allowed-tools approves git add, commit, status and diff only during that turn.
/pr-description runs its own script (context.sh) that gets the branch, commits and files against the base branch (optional argument; by default origin/HEAD or main) and always exits with 0, because the documentation warns that a failing injected command aborts the whole skill. Claude returns Markdown with what changes, why, how to test it and risks; it does not create the PR or push. If you also want an AI to review your PRs on GitHub, see the PR review with GitHub Actions recipe.
3. /fix-tests
This is the skill for the most repeated sentence: "fix the tests". It takes the test command as an argument (/fix-tests npm test) thanks to $ARGUMENTS; if you do not give one, it uses the one in CLAUDE.md or AGENTS.md or detects it, and tells you which one it will use. It defines a short loop: first real failure, cause in one sentence before editing, fix in the production code, single test and full suite, and stop after 3 failed attempts on the same test. And it explicitly forbids what lets an agent "win" by cheating: skip, xfail, .only or deleting asserts.
It has effort: high in the frontmatter: according to the official documentation, a skill's effort applies until the end of the turn in which it runs and overrides the session's (except CLAUDE_CODE_EFFORT_LEVEL), and the effort table recommends high precisely for fixing bugs in existing code (more in Opus vs Sonnet vs Haiku). It does not have disable-model-invocation: Claude can use it on its own when it sees red tests. Edits still go through your permission mode.
4. /explain-code (cheap and risk-free)
context: fork makes the skill run in a separate subagent; with agent: Explore it is the built-in exploration subagent, which according to the documentation starts without CLAUDE.md or git status to keep a small context, which is why the skill contains all the instructions. With model: haiku and effort: low (with context: fork, those fields set the subagent's model and effort), the explanation runs on Haiku 5.5 (0.10/0.50 USD per million tokens) even if your session is on Opus.
The result comes back to your conversation as a summary of at most 25 lines: what it does, inputs and outputs, who uses it with path:line and odd cases. It does not modify files. Use it when you land in a new repository: /explain-code src/auth/ or /explain-code calculateTotal. If you want it to wait for the result in the same turn, add background: false; by default the fork runs in the background and lets you know when it finishes. More about subagents in the subagents recipe.
5. /pre-push-check
It runs a review script that looks at what you are about to push : unpushed commits against the upstream (or origin/HEAD or main if there is no upstream) and uncommitted changes, including new files. In the added lines it looks for secret patterns (sk-ant-, GitHub tokens, AKIA keys, private keys, "password/secret/token ="), .env or key files (except .env.example), files over 1 MB and leftover debugging (console.log, debugger, breakpoint(), pdb). Then Claude runs the checks from your CLAUDE.md or AGENTS.md and returns a ✅/❌ list. A possible secret is blocking. It has disable-model-invocation: true and it does not push or commit.
Careful with the name: the documentation explains that if there is a skill called verify or simplify, Claude runs it before every commit (2.1.286 or later). If you want that automatic behavior, you can copy this skill (pre-push-check) as .claude/skills/verify/ without disable-model-invocation, knowing that it replaces the built-in /verify.
How to use it
1. Download the pack
git clone https://github.com/carlosp5751258-lgtm/ctrlia-pack-comandos
2. Copy it where you want to use it
For all your projects on this machine:
mkdir -p ~/.claude/skills && cp -r ctrlia-pack-comandos/en/.claude/skills/. ~/.claude/skills/
chmod +x ~/.claude/skills/pr-description/context.sh ~/.claude/skills/pre-push-check/check.sh
Or for a single repository only (and for your team), from that repository's root:
mkdir -p .claude/skills && cp -r ../ctrlia-pack-comandos/en/.claude/skills/. .claude/skills/ && git add .claude/skills
3. Check that they show up
Open Claude Code, type / and you will see all five; or run:
/skills
4. Try them in a toy repository
Make a change, git add, and then /conventional-commit. Then /explain-code <file>, /pre-push-check and /pr-description main.
5. If one does not show up or aborts
Check that the first line of SKILL.md is exactly --- (the documentation says that otherwise the whole file is treated as content) and that the YAML is valid: the descriptions are quoted because they contain ":", which would otherwise break the YAML and the skill would load with no fields. If a skill aborts with "Shell command permission check failed", a deny or ask rule matches the injected command: check /permissions (and the permissions recipe). To remove a skill, delete its folder. To stop Claude from launching one on its own, add disable-model-invocation: true.
The files
These are the complete files from the repository's en/ folder, as they are (the Spanish originals are at the repository root). Both scripts use set -euo pipefail, check that they are inside a git repository and always exit with 0.
conventional-commit/SKILL.md
---
name: conventional-commit
description: "Creates a commit with an English message (Conventional Commits) from the staged changes. Use it when someone asks to \"commit this\", \"make a commit\" or for a commit message."
argument-hint: "[optional hint about the change]"
disable-model-invocation: true
allowed-tools: Bash(git add *) Bash(git commit *) Bash(git status *) Bash(git diff *)
---
# Commit in English
## Status
!`git status --short`
## Staged changes
!`git diff --cached --stat`
## Instructions
1. If nothing is staged above, show me `git status` and ask what to add. Don't use `git add -A` without asking.
2. Read the staged diff with `git diff --cached` (only what you need).
3. Write the message: `type(scope): summary` in the imperative, in English, 72 characters max. Types: feat, fix, docs, refactor, test, chore. User's hint: $ARGUMENTS
4. If the change isn't obvious, add a 1-3 line body with the why.
5. Run `git commit -m "<summary>" -m "<body>"`. Never `--no-verify` or `--amend` unless I ask.
6. Show the hash and the final message.
pr-description/SKILL.md
---
name: pr-description
description: "Writes a pull request description in English from the commits and the diff of the current branch against the base branch. Use it when someone asks for a PR description, a branch summary or \"what does this PR change\"."
argument-hint: "[base-branch]"
allowed-tools: Bash(bash *pr-description/context.sh*) Bash(git diff *) Bash(git log *)
---
# PR description
## Branch context
!`bash "${CLAUDE_SKILL_DIR}/context.sh" $ARGUMENTS`
## Instructions
Write Markdown, in English, without making up anything that isn't in the commits or the diff:
- **What changes**: 2-4 bullets for the reviewer, most important first.
- **Why**: one sentence (if the commits don't say, write "Why: pending" and ask me).
- **How to test it**: concrete commands or steps.
- **Risks**: migrations, API changes, new configuration. If there are none, "None known".
Return only the Markdown. Don't create the PR or push.
pr-description/context.sh
#!/usr/bin/env bash
# Commits and diff summary of the current branch against the base (by default, the remote's default branch or main).
# Always exits with 0: if a command injected into a skill fails, the whole skill is aborted.
set -euo pipefail
git rev-parse --git-dir >/dev/null 2>&1 || { echo "Not a git repository."; exit 0; }
base="${1:-$(git symbolic-ref --quiet --short refs/remotes/origin/HEAD 2>/dev/null || echo main)}"
echo "Branch: $(git branch --show-current 2>/dev/null || true) · Base: $base"
echo "## Commits"; { git log --oneline --no-merges "$base"..HEAD 2>&1 || true; } | head -50
echo "## Files"; { git diff --stat "$base"...HEAD 2>&1 || true; } | tail -40
exit 0
fix-tests/SKILL.md
---
name: fix-tests
description: "Runs the tests, finds the cause of each failure and fixes it until they pass, without touching the tests unless they are wrong. Use it when tests fail or someone asks to \"fix the tests\"."
argument-hint: "[test command]"
effort: high
---
# Fix the tests
Test command: $ARGUMENTS
(If empty, use the one in CLAUDE.md or AGENTS.md; if there is none, detect it: `npm test`, `pnpm test`, `python3 -m pytest -q`, `go test ./...`, `cargo test`. Tell me which one you'll use before you start.)
## Loop
1. Run the command and take the first real failure (ignore the ones that follow from it).
2. Explain the cause in one sentence **before** editing. If you don't know it, add a temporary check instead of guessing.
3. Fix the production code. Only change a test if you show the test is wrong, and tell me explicitly.
4. Re-run just that test; then the full suite.
5. Repeat. If the same test still fails after 3 attempts, stop and summarize what you tried.
## Forbidden
- Skipping or deleting tests (`skip`, `xfail`, `.only`, commenting out asserts) to make it pass.
- Changing dependencies or the test runner configuration without asking.
When done: list of fixed failures with their cause and the final output of the suite.
explain-code/SKILL.md
---
name: explain-code
description: "Explains in English what a file, folder or function of the project does and how it fits with the rest, without changing anything. Use it when someone asks \"what does this do\", \"explain this code\" or when joining a repository."
argument-hint: "<path or function name>"
context: fork
agent: Explore
model: haiku
effort: low
---
Explain in English, for someone who just joined the project, this target: $ARGUMENTS
Steps:
1. Find the target (if it's a function name, search for it with Grep). If it doesn't exist, say so and stop.
2. Read the code and at most 3 files that call it or that it uses.
3. Don't modify any file.
Answer format (25 lines max):
- **What it does**: 2-3 sentences.
- **Inputs and outputs**: parameters, return values, side effects (files, network, database).
- **Who uses it**: paths with line numbers (`path:line`).
- **Watch out for**: edge cases or odd behavior you see in the code.
pre-push-check/SKILL.md
---
name: pre-push-check
description: "Review before git push: runs the project's checks and looks for secrets, large files and leftover debugging in the changes. Use it before pushing a branch or opening a PR."
disable-model-invocation: true
allowed-tools: Bash(bash *pre-push-check/check.sh*)
---
# Before pushing
## Automatic review of the changes
!`bash "${CLAUDE_SKILL_DIR}/check.sh"`
## Instructions
1. Summarize what the script above found. A possible secret is blocking: tell me file and line and do **not** continue until I confirm.
2. Run the project checks listed in CLAUDE.md or AGENTS.md (tests, lint, types). If there are none, ask me which ones.
3. Return a list with ✅ / ❌ per item: secrets, large files, leftover debugging, tests, lint, types.
4. Don't push or commit: just report.
pre-push-check/check.sh
#!/usr/bin/env bash
# Reviews what you're about to push: unpushed commits + uncommitted changes.
# Always exits with 0: if a command injected into a skill fails, the whole skill is aborted.
set -euo pipefail
git rev-parse --git-dir >/dev/null 2>&1 || { echo "Not a git repository."; exit 0; }
base=$(git rev-parse --abbrev-ref --symbolic-full-name '@{u}' 2>/dev/null \
|| git symbolic-ref --quiet --short refs/remotes/origin/HEAD 2>/dev/null \
|| { [ "$(git branch --show-current 2>/dev/null)" != main ] && git rev-parse --verify -q main >/dev/null && echo main; } \
|| echo HEAD)
since=$(git merge-base "$base" HEAD 2>/dev/null || echo HEAD)
files=$( { git diff --name-only "$since" 2>/dev/null || true; git ls-files --others --exclude-standard 2>/dev/null || true; } | sed '/^$/d' | sort -u)
[ -z "$files" ] && { echo "No changes against $base."; exit 0; }
changes=$(git diff "$since" -U0 2>/dev/null || true)
show() { if [ -n "$1" ]; then echo "$1"; else echo "None"; fi; }
search() { grep -E "$@" || true; } # grep with no matches is not an error here
echo "Base: $base · Changed files: $(echo "$files" | wc -l)"
echo "## Possible secrets"
show "$(echo "$changes" | search '^\+.*(sk-ant-|sk-[A-Za-z0-9]{20,}|gh[pousr]_[A-Za-z0-9]{20,}|AKIA[0-9A-Z]{16}|BEGIN [A-Z ]*PRIVATE KEY|(password|secret|token)["'"'"']?[[:space:]]*[:=])' | cut -c1-160 | head -20)"
echo "## .env or key files"
show "$(echo "$files" | search '(^|/)\.env($|\.)|\.pem$|\.key$|id_rsa' | search -v '\.env\.(example|sample|template)$')"
echo "## Files over 1 MB"
show "$(while read -r f; do if [ -f "$f" ] && [ "$(wc -c < "$f")" -gt 1048576 ]; then echo "$f"; fi; done <<< "$files")"
echo "## Leftover debugging added"
show "$(echo "$changes" | search '^\+.*(console\.log\(|debugger;|breakpoint\(\)|pdb\.set_trace)' | cut -c1-160 | head -20)"
exit 0
Real output of check.sh
In a test repository: a branch with a commit that adds a token and a debugger, plus an unpushed .env.local and .env.example.
Base: main · Changed files: 3
## Possible secrets
+const k = { token: "abcdefghij" }; console.log(1)
## .env or key files
.env.local
## Files over 1 MB
None
## Leftover debugging added
+const k = { token: "abcdefghij" }; console.log(1)
+debugger;
It detects the .env.local (not the .env.example), the token and the debugger, and exits with 0. Sources: skills, commands, model configuration and subagents.
FAQ
What is the difference between a skill and a command in .claude/commands?
For invoking them, none: both are used with /name and accept the same frontmatter (except name and paths). A skill allows helper files and is the recommended format; if both exist with the same name, the skill wins.
Do I need to restart Claude Code after installing them?
No: Claude Code watches ~/.claude/skills and .claude/skills. Only if the skills folder did not exist when the session started, run /reload-skills.
Why doesn't /conventional-commit run on its own?
Because it has disable-model-invocation: true: it has side effects (it creates a commit) and the documentation recommends keeping those skills for you to launch.
Does /explain-code cost a lot?
Very little: it runs in a subagent with model: haiku and effort: low, that is Haiku 5.5, even if your session is on Opus.
Can I change the names?
Yes, change the folder name and the name field. Avoid the names of built-in commands (/review, /verify, /simplify…): your skill would replace them.
Do they work on Windows?
The SKILL.md files do; the two scripts are bash, so they need Git Bash or WSL. Without bash, the documentation says injected commands run in PowerShell and these scripts would not work as they are.