upstream-pr-plugin/skills/upstream-pr/SKILL.md
Submit a diverged-fork commit to upstream as a clean PR via cherry-pick with re-derive fallback, message scrubbing, and regression checks. Use when direct rebase fails.
npx skillsauth add laurigates/claude-plugins upstream-prInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Submit a single commit from a heavily-diverged fork back to upstream as a clean, regression-free PR.
| Use this skill when... | Use /git:upstream-pr instead when... |
|------------------------|----------------------------------------|
| Fork has substantially diverged from upstream | Fork and upstream are roughly aligned |
| Cherry-pick may produce many conflicts on shared modules | Single commit applies cleanly |
| You need patch-id matching for already-applied content | You want a quick cherry-pick + cross-fork PR |
| The change touches files that may be fork-only | All touched files exist upstream |
| The PR needs commit-message scrubbing (fork issue refs, Claude trailers) | Commit messages are already upstream-clean |
| Pre-flight regression check against upstream baseline matters | The change is trivial enough to skip pre-flight |
Per-project configuration lives at .claude/upstream-pr.local.md (gitignored). All fields are optional; sensible defaults apply.
---
upstream_remote: upstream
upstream_repo: owner/repo
branch_prefix: pr-upstream/
linter_cmd: uv run ruff check
test_cmd: uv run pytest -q
pr_body_template_path: docs/UPSTREAM_PR_TEMPLATE.md
---
# Notes
Free-form notes — fork drift hotspots, files to never touch upstream, etc.
| Field | Default | Used by |
|-------|---------|---------|
| upstream_remote | upstream | All scripts |
| upstream_repo | Parsed from git remote get-url <upstream_remote> | gh pr create --repo |
| branch_prefix | pr-upstream/ | Branch name in prepare-branch.sh |
| linter_cmd | (empty — pre-flight skipped) | Pre-flight stash-roundtrip |
| test_cmd | (empty — pre-flight skipped) | Pre-flight stash-roundtrip |
| pr_body_template_path | (empty — built-in template) | PR body |
Add .claude/*.local.md to .gitignore.
git branch --show-currentgit status --porcelain=v2 --branchgit remote -vfind .claude -maxdepth 1 -name 'upstream-pr.local.md'Parse from $ARGUMENTS:
| Parameter | Required | Description |
|-----------|----------|-------------|
| <sha> | Yes | Commit SHA on the fork to send upstream |
| --topic <slug> | No | Topic slug for branch name; auto-derived from commit subject if omitted |
Execute this upstream-PR workflow:
Run the eligibility check before touching any branch:
bash "${CLAUDE_SKILL_DIR}/scripts/check-eligibility.sh" <sha>
Exit codes drive next steps:
| Exit | Meaning | Action |
|------|---------|--------|
| 0 | Every touched file exists upstream and content is novel | Proceed to Step 2 |
| 1 | At least one fork-only file | Stop. Tell the user the change isn't standalone-PR-able; predecessor feature must land first |
| 3 | Content already applied upstream under a different SHA | Stop. Report the matching upstream commit; nothing to PR |
The patch-id check (git patch-id --stable) catches re-applied content from maintainer re-merges or squashes — even when the SHA differs.
Derive a topic slug from the commit subject if --topic was not provided. Then:
bash "${CLAUDE_SKILL_DIR}/scripts/prepare-branch.sh" <topic-slug> <sha>
The script:
<branch_prefix><topic-slug> from <upstream_remote>/main (or /master)<sha>When the cherry-pick produces conflicts, preserve upstream's surrounding shape, not the fork's. The goal is the smallest readable diff against upstream — a maintainer must see the change make sense in upstream's current code, not in the fork's.
If the cherry-pick produces dozens of conflict blocks across multiple files (typical when upstream and fork have drifted heavily on shared modules), abort and re-derive instead of fighting hunks:
git cherry-pick --abort
git checkout -b <branch> <upstream_remote>/main
# Re-apply the change against upstream's actual current files.
Re-derive is the right call when:
print() → logging, deprecation rename, lint-rule auto-fixes) — the rules transfer cleanly even if line numbers don't.Heuristic: extract the original commit's before → after map (e.g. git show <sha> | grep -E '^[-+].*pattern') and use it as the rulebook when re-applying against upstream. The PR body should disclose the re-derive (see template below).
For refactor / cleanup PRs, verify lint and test parity against the pristine upstream baseline, not against the fork. This catches stray formatter touches, accidental import reordering, and indentation drift from Edit-tool replacements.
If linter_cmd and test_cmd are configured, run a stash roundtrip:
# Baseline (pristine upstream):
git stash push -- <changed-files>
<linter_cmd> <changed-files> 2>&1 | tail -1 # error count
<test_cmd> 2>&1 | tail -1 # pass count
git stash pop
# After (with your changes): run the same two commands and compare.
Both numbers must match (or improve). Quote both in the PR body's "Testing Performed" section.
For Python files, also run python3 -c "import ast; ast.parse(open(F).read())" on every edited file — catches indentation breaks that ruff might miss on already-warning-laden upstream code.
Run the scrub helper:
bash "${CLAUDE_SKILL_DIR}/scripts/scrub-commit.sh" --check
If violations are reported, run without --check to amend interactively:
bash "${CLAUDE_SKILL_DIR}/scripts/scrub-commit.sh"
The scrub rules:
Closes #74, Addresses #19, etc. point at the fork's tracker, not upstream's.Co-authored-by: Claude ..., Generated with [Claude Code]. Upstream doesn't follow that convention.bandit B607, ty, vulture), describe the underlying problem instead.fix(security):, feat(parser):, etc.The amend wraps git commit --amend with PRE_COMMIT_ALLOW_NO_CONFIG=1 because upstream may have no .pre-commit-config.yaml and a locally-installed pre-commit hook would otherwise refuse the commit.
Before pushing, verify with:
git diff <upstream_remote>/main..HEAD
Every hunk should be defensible to a maintainer who has never seen the fork.
git push origin <branch>
gh pr create --repo <upstream_repo> --base main \
--head <fork-owner>:<branch> \
--title "<conventional-commit subject>" \
--body "<see body template below>"
If pr_body_template_path is configured, use that template; otherwise use the built-in template:
## Description
<one or two paragraphs explaining the problem and the fix from the
maintainer's perspective — not from the fork's perspective>
## Related Issue(s)
None. <or upstream issue numbers only>
## Type of Change
- [x] Bug fix (non-breaking change that fixes an issue)
## Testing Performed
- [x] `<linter_cmd> <changed-files>` — N issues (matches upstream baseline)
- [x] `<test_cmd>` — M passed (matches upstream baseline)
## Notes
Originally authored on a downstream fork; the branch was cut from
`<upstream_repo>:main` and a single commit cherry-picked onto it so
it applies cleanly.
<If re-derived: "The fork's version of this change collided heavily with
upstream's current code; the diff was re-derived against upstream's files
using the original commit's transformation rules.">
| Context | Command |
|---------|---------|
| Eligibility check | bash check-eligibility.sh <sha> (exit 0/1/3) |
| Prepare branch | bash prepare-branch.sh <topic> <sha> |
| Scrub check (CI mode) | bash scrub-commit.sh --check |
| Diff against upstream | git diff <upstream_remote>/main..HEAD --stat |
| Cross-fork PR | gh pr create --repo <upstream_repo> --head <fork-owner>:<branch> |
| Patch-id of HEAD | git show HEAD \| git patch-id --stable |
tools
Scaffold a new ComfyUI custom-node repo (pyproject, CI, release-please, vitest+pytest, JS extension skeleton) in the picker/gesture vein. Use when bootstrapping or init-ing a comfyui node pack.
tools
Orchestrate a ComfyUI node pack from idea to registry: scaffold, create + seed the repo, open the gitops adoption PR. Use when releasing or spinning up a new comfyui node pack.
testing
macOS EndpointSecurity/EDR high CPU & battery drain. Use when Kandji ESF / XProtect pegs a core; trace the exec storm via powermetrics + eslogger.
development
odiff pixel-by-pixel image diffing. Use when comparing screenshots, detecting visual regressions, diffing before/after PNGs, asserting golden images.