الصفحات · Claude Github PMS
gh-push
Ship an in-progress issue — commit local changes on the feature branch, push, open the PR with `Closes #N`, and (optionally) merge. Composes Gate 4 + Gate 5 of the gh-pms lifecycle. Auto-invoke when the user says "push #N", "ship #N", "open PR for #N", "/push", or any "ready to merge" intent.
Ship completed work in one command: commit on the feature branch, push, PR, merge, advance the linked issue to status:done.
gh-push is the gh-pms counterpart to the popular /push skill — but PR-aware. It enforces the "every feature must end with a linked PR" rule from workflows/default.yaml#branching and refuses any path that would write feature work directly to the protected base branch.
When to invoke
- User runs
/gh-pms:gh-push #N(or shorthand/pushif no other skill claims it) - User says "ship #N", "push #N", "open PR for #N", "ready to merge #N"
- An issue is
status:documentedand the user signals they're ready to merge
Pre-flight
-
Resolve the target issue.
- If the user passed
#N, use it. - Otherwise read
~/.cache/gh-pms/state.jsonand pick the issue withcurrent_status=in-progress,ready-for-testing,in-testing,ready-for-docs, ordocumented. - If still ambiguous, run
gh issue list --assignee @me --state open --label status:in-progressand pick if exactly one matches; otherwise ASK the user viaAskUserQuestion.
- If the user passed
-
Read issue + state.
gh issue view {N} --json number,title,labels,body,milestone- Extract: kind (from
type:*label or native Issue Type), currentstatus:*, branch (from state file).
-
Branch policy check.
- Get current branch:
git rev-parse --abbrev-ref HEAD. - If it is in
branching.protected_base(default:main,master):- If the kind is exempt (e.g.
plan), proceed without branching. - Otherwise REFUSE:
text
- If the kind is exempt (e.g.
- If the issue's recorded branch (from state file) does not match the current branch, WARN but allow — the user may have renamed.
- Get current branch:
-
Working-tree check.
git status --porcelain— if dirty, that's expected; we'll commit it.- If clean AND no commits ahead of the upstream, REFUSE: nothing to ship.
Pipeline
Step 1 — Stage & commit local changes (only if dirty)
If git status --porcelain shows changes:
- Run
git diff --statandgit diff --cached --statto summarize. - Run
git log --oneline origin/{base}..HEAD(orgit log --oneline -5) to match the repo's commit style. - Generate a conventional-commit message (or use the
--messageflag value if provided):- Prefix:
feat:forfeaturekind,fix:forbug/hotfix,chore:forchore,test:fortestcase,docs:for doc-only diffs. - First line: short imperative summary, ≤72 chars, ending with
(#{N})so the issue is referenced. - Body: 1–3 sentences on why, plus a
Refs #{N}line if needed.
- Prefix:
- Stage carefully. Use specific paths from
git statusoutput. EXCLUDE:.env*,credentials*,*.key,*.pem,service-account*.json, anything insecrets/node_modules/,.next/,dist/,out/,build/,*.tsbuildinfo- Editor cruft:
.DS_Store,Thumbs.db,.idea/,.vscode/launch.jsonunless explicitly needed
git commit -m "$(cat <<'EOF' ... EOF)"via HEREDOC. NoCo-Authored-Bytrailer unless the user explicitly asks.
Step 2 — Push the branch
If the push is rejected because the remote has commits we don't:
- Run
git pull --rebase origin {branch}. - If rebase has conflicts, STOP and tell the user which files conflict.
- After successful rebase, retry the push.
NEVER force-push unless the user explicitly types --force.
Step 3 — Find or open the PR
- Look for an existing PR for this branch:
gh pr list --head {branch} --json number,url,title,body. - If one exists:
- Verify its body contains
Closes #{N}(case-insensitive). If missing, appendCloses #{N}viagh pr edit {PR} --body-file .... - Skip to Step 4.
- Verify its body contains
- If none exists, create one:
PR body template (HEREDOC into the temp file):bashmarkdown
Step 4 — Compose Gate 4 (documented → in-review)
If the issue's current status is documented:
- Build the Gate 4 self-review evidence comment:
markdown
- Validate via
lib/validate-evidence.sh(or a manual section-length + file-existence check on macOS bash 3 — seegh-advancenotes). - CI gate — run
${CLAUDE_PLUGIN_ROOT}/lib/check-pr-checks.sh <PR>. Refuse Gate 4 if exit code is non-zero (failing or still-pending checks). The script prints the failure list and the override hint. Override path:--ignore-checks "<reason>"skips the refusal but appends a## Check overridessection to the evidence comment with the reason. Use sparingly — the override is an audit trail, not a free pass. - Post the comment via
gh issue comment {N} --body-file .... - Flip status:
${CLAUDE_PLUGIN_ROOT}/lib/ghcall.sh set-status {N} "In Review". - Update the state file (
last_transition_at,current_status: in-review).
If the issue is not yet at documented (e.g. user is shipping mid-flight), STOP after Step 3 and tell them:
Step 5 — Request user approval (Gate 5)
Use AskUserQuestion:
| Question | Options |
|---|---|
PR #{X} is open closing #{N}. {commit_count} commits, {file_count} files. Approve and merge? | Approve & merge · Needs edits · Cancel |
Branch on the answer:
-
Approve & merge:
gh pr review {X} --approve --body "Approved via /gh-pms:gh-push."- Pick merge style from
branching.merge_style(default:--squash). Confirm with the user the first time per repo (cache the answer). gh pr merge {X} --squash --delete-branch=true(or--merge/--rebaseper setting).- If checks are still running, ASK whether to use
--admin(skip checks) or wait. - If conflicts, STOP and tell the user which files conflict.
- If checks are still running, ASK whether to use
git checkout {base} && git pull origin {base}to bring local in sync.- The
Closes #{N}keyword auto-closes the issue. Verify:gh issue view {N} --json state -q .stateshould beCLOSED. - Manually flip the status label to
done(GitHub closes the issue but doesn't update status labels):${CLAUDE_PLUGIN_ROOT}/lib/ghcall.sh set-status {N} "Done" - Update state file:
current_status: done,merged_at: now,pr: {X}. - Comment on the issue:
✅ Merged via #{X} on {timestamp}. - Report:
text
-
Needs edits:
- ASK for the reason via free-text input.
- Comment on PR #{X} via
gh pr commentwith🔄 Needs edits: {reason}. - Flip issue status back to
in-progress:${CLAUDE_PLUGIN_ROOT}/lib/ghcall.sh set-status {N} "In Progress". - Reset the cooldown timestamp.
- Report:
#{N} back to in-progress on branch {branch}. Address feedback, then re-run gh-push.
-
Cancel:
- ASK for the reason.
- Close PR:
gh pr close {X} --comment "❌ Cancelled: {reason}". - Close issue with
not planned:gh issue close {N} --reason "not planned" --comment "❌ Cancelled: {reason}". - Apply
wontfixlabel if it exists. - Report:
#{N} cancelled. Branch {branch} retained for archival.
Flags
| Flag | Behavior |
|---|---|
/gh-pms:gh-push #N | Default — auto-detect everything, walk the full pipeline, ASK for approval at Gate 5. |
--message "<msg>" | Use the provided commit message instead of auto-generating. Must still include (#N) suffix. |
--no-merge | Stop after PR open + Gate 4 evidence post. Skip Gate 5. Useful when CI takes a long time. |
--admin | Pass --admin to gh pr merge so it bypasses required checks. Use sparingly; the user must have admin rights. |
--squash / --merge / --rebase | Force the merge style for this run. Default per branching.merge_style. |
--dry | Print every command that would run. Don't execute. |
--force | Allow git push --force-with-lease. Refuse plain --force. Reserved for explicit user request. |
Safety rules
- NEVER push directly to a
protected_base(main / master). Refuse with the error in pre-flight step 3. - NEVER force-push without
--forceflag from the user. - NEVER skip the
Closes #{N}keyword — without it, the issue won't auto-close on merge. - NEVER include secrets / build artifacts in the commit (see Step 1 EXCLUDE list).
- NEVER delete the protected base branch.
- NEVER advance to
donewithout going through theAskUserQuestionapproval gate (Step 5). This mirrors Gate 5'srequired_user_approval. - NEVER add
Co-Authored-Bytrailers unless the user explicitly asks.
Cross-skill contract
After Approve & merge:
- The skill should pick the next ready sub-issue from the same plan (milestone) —
gh issue list --milestone {plan} --label status:todo --state open --limit 1— and offer to run/gh-pms:gh-currenton it. - If no plan parent or no ready siblings, run
/gh-pms:gh-statusfor the dashboard view.
If the user's intent was to ship work that wasn't yet on a feature branch (legacy state — work was committed to main before this rule existed), gh-push should:
- Detect the situation (HEAD is on protected base, issue is in-progress/documented, has commits ahead of
origin/{base}). - Offer a one-shot retro-rescue:
- Create the feature branch at HEAD:
git branch {kind_short}/{N}-{slug} - Reset main back to
origin/{base}:git reset --hard origin/{base}(requires explicit user OK because this is destructive — confirm first). - Switch to the new branch:
git checkout {kind_short}/{N}-{slug} - Push and PR as normal.
- Create the feature branch at HEAD:
- If the user prefers not to rewrite local main (e.g. main was already pushed), open a comparison PR documenting the work + add a
[gh-pms: branch-exception]marker to the issue body so future Gate-1 checks don't trip on the same issue.