Analyze the working tree, stage relevant changes, create a branch if needed, commit, and open a pull request
Examine all uncommitted changes, identify what belongs to the current task,
prompt the user about anything ambiguous, create a branch (if on main),
commit with a descriptive message, and open a pull request.
When to use this skill:
lessons/--all (optional): Include all uncommitted changes without prompting.
Skips the relevance check in Phase 2. Use when you know every change in the
tree belongs to the same task.branch-name (optional): Explicit branch name to use. If omitted, the
skill infers one from the changes (see Phase 3).Work through each phase in order.
Gather a complete picture of what has changed.
Run these commands in parallel:
git status --short
git diff --stat
git diff --cached --stat
git branch --show-current
git log --oneline -5
If there are no changes (status and both diffs are empty):
Collect:
Goal: Every file in the commit must belong to the same logical unit of work. Unrelated changes must not silently slip in, and must not be silently excluded.
If --all was passed, skip this phase — stage everything and go to
Phase 3.
Determine what the current work is about by examining:
lesson-30-new-topic or fix-checkbox-click
tells you the scope.git diff --cached) — these are the most explicit
signal of intent.main) — shows what has been
committed so far for this task.Formulate a one-line description of the task, e.g.: "Add UI Lesson 11 — Widget ID System" or "Fix checkbox click detection".
For every modified, added, deleted, or untracked file, assign one of:
| Classification | Meaning |
|---|---|
| Relevant | Clearly part of the current task |
| Unclear | Could belong to this task or could be unrelated |
| Unrelated | Clearly not part of the current task |
Heuristics for classification:
.o files → Unrelated (and likely
gitignored already)CMakeLists.txt, README.md, PLAN.md) → check
if the changes reference the current task; if so Relevant, otherwise
Unclear.claude/skills/ files → Relevant if the skill matches the task topicRelevant files: Stage them. No prompt needed.
Unclear files: Present each to the user with context and ask:
This file changed but may not belong to the current task:
M common/ui/forge_ui.h (+12 -3)
Diff summary: Added forge_ui_widget_id() declaration
Include in this PR?
Use AskUserQuestion with options:
Unrelated files: Present them as a group and confirm exclusion:
These files appear unrelated to the current task and will NOT be included:
?? build/CMakeCache.txt (build artifact)
M lessons/gpu/05-mipmaps/main.c (different lesson)
?? .DS_Store (OS file)
Proceed without these files?
Use AskUserQuestion with options:
CRITICAL — never silently exclude. If a file has real code changes (not build artifacts or OS junk), the user must be told it is being left out. The purpose of this skill is to prevent both accidental inclusions AND accidental omissions.
If already on a feature branch (not main), skip to Phase 4.
If on main:
Ensure main is up to date:
git pull origin main
Derive a branch name from the changes. Use the task description from Phase 2 to generate a kebab-case name:
| Task description | Branch name |
|---|---|
| Add UI Lesson 11 — Widget ID System | ui-lesson-11-widget-id |
| Fix checkbox click detection | fix-checkbox-click |
| Add dev-create-pr skill | add-dev-create-pr-skill |
| Update math library with cross product | math-cross-product |
If the user provided a branch-name argument, use that instead.
Create and switch to the branch:
git checkout -b <branch-name>
MANDATORY: Run
/dev-local-reviewbefore committing. No exceptions. Do NOT proceed to Phase 5 until the local review passes with zero findings.
Stage all relevant files (those classified as Relevant in Phase 2, plus any Unclear files the user chose to include):
git add <file1> <file2> ...
Stage files by explicit name — never use git add -A or git add ..
Run quality checks on staged content:
If any .md files are staged, lint only those files:
git diff --cached --name-only -- '*.md' \
| xargs -r npx markdownlint-cli2
If errors are found in staged files, fix them, re-stage, and re-check. Never bypass lint rules.
If any scripts/*.py or pipeline/*.py files are staged, lint only those files:
STAGED_PY=$(git diff --cached --name-only -- 'scripts/*.py' 'pipeline/*.py' 'tests/pipeline/*.py')
[ -n "$STAGED_PY" ] && ruff check $STAGED_PY && ruff format --check $STAGED_PY
Fix issues if found.
If any Python files are staged (scripts/, pipeline/, or tests/pipeline/),
run Pyright type checking:
STAGED_PY=$(git diff --cached --name-only -- '*.py')
[ -n "$STAGED_PY" ] && pyright
Fix issues if found.
Show the final staging summary:
git diff --cached --stat
Report the file count and total lines changed.
Run /dev-local-review.
This is a mandatory gate. Run the local review skill and fix any findings it reports. Do NOT proceed to Phase 5 until it completes with zero findings. This catches problems before they reach the PR and burn a GitHub review round.
Analyze the staged diff to write a descriptive commit message.
git diff --cached
Commit message format:
<type>: <short summary under 70 chars>
<body — what changed and why, wrapped at 72 chars>
Files changed:
- path/to/file.c — brief description of change
- path/to/other.h — brief description of change
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Type prefixes:
| Prefix | When to use |
|---|---|
Add | New feature, lesson, skill, or file |
Fix | Bug fix |
Update | Enhancement to existing functionality |
Refactor | Code restructuring without behavior change |
Docs | Documentation-only changes |
Chore | Build, CI, config, or tooling changes |
Rules for the message:
Present the message to the user before committing. Allow them to edit it.
Block secrets before commit:
Scan staged file names for .env files or credential patterns. If any
are found, refuse to proceed:
# Block .env files
git diff --cached --name-only \
| grep -E '(^|/)\.env(\.|$)' && {
echo "Refusing to commit .env files. Unstage them first."
exit 1
}
# Block credential and key files
if git diff --cached --name-only \
| grep -Eq '(^|/)(credentials\.json|[^/]+\.pem|[^/]+\.key)$'; then
echo "Refusing to commit credential/key files. Unstage them first."
exit 1
fi
Commit:
git commit -m "$(cat <<'EOF'
<commit message from Phase 5>
EOF
)"
Push with upstream tracking:
git push -u origin <branch-name>
Verify the push succeeded. If it fails (e.g. network error, permission denied), report the error and stop.
Compose the PR title and body.
Create the PR:
gh pr create --title "<title>" --body "$(cat <<'EOF'
## Summary
<2-4 bullet points describing what changed and why>
## Changes
<bulleted list of files with descriptions>
## Test plan
- [ ] <relevant verification steps>
🤖 Generated with [Claude Code](https://claude.com/claude-code)
EOF
)"
Report the PR URL to the user.
-2, -3) or ask the user for a name.gh CLI not authenticated: Provide setup instructions
(gh auth login) and stop.main, but
if they do, report and stop.git add -A or git add . — always stages files by name..env files — if detected in the staged set,
block the commit and exit so the user can unstage them.