|
| 1 | +--- |
| 2 | +description: Commit changes to a new branch, push to GitHub, and open a PR with project template |
| 3 | +argument-hint: "[title] [--branch <name>] [--base <branch>] [--draft]" |
| 4 | +--- |
| 5 | + |
| 6 | +# Submit Pull Request |
| 7 | + |
| 8 | +Commit work, push to a new branch, and open a pull request with the project-specific PR template. |
| 9 | + |
| 10 | +## Arguments |
| 11 | + |
| 12 | +`$ARGUMENTS` may contain: |
| 13 | +- **title** (optional): PR title. If omitted, auto-generate from changes/commits. |
| 14 | +- `--branch <name>` (optional): Branch name. If omitted, auto-generate from title. |
| 15 | +- `--base <branch>` (optional): Base branch for PR. Default: `main`. |
| 16 | +- `--draft` (optional): Create as draft PR. |
| 17 | + |
| 18 | +## Instructions |
| 19 | + |
| 20 | +### 1. Parse Arguments |
| 21 | + |
| 22 | +Parse `$ARGUMENTS` to extract: |
| 23 | +- **title**: Everything before any `--` flags |
| 24 | +- **--branch**: Branch name (if provided) |
| 25 | +- **--base**: Base branch (default: `main`) |
| 26 | +- **--draft**: Boolean flag |
| 27 | + |
| 28 | +### 2. Detect Remote Configuration |
| 29 | + |
| 30 | +Determine if this is a fork-based workflow: |
| 31 | + |
| 32 | +1. **Check for upstream remote**: |
| 33 | + ```bash |
| 34 | + git remote get-url upstream 2>/dev/null |
| 35 | + ``` |
| 36 | + |
| 37 | +2. **Set remote variables**: |
| 38 | + - If `upstream` exists → **fork workflow**: |
| 39 | + - `<base-remote>` = `upstream` (for base comparisons, PR target) |
| 40 | + - `<push-remote>` = `origin` (for pushing branches) |
| 41 | + - Extract `<upstream-owner>/<upstream-repo>` from upstream URL |
| 42 | + - Extract `<fork-owner>` from origin URL |
| 43 | + - If `upstream` does not exist → **direct workflow**: |
| 44 | + - `<base-remote>` = `origin` |
| 45 | + - `<push-remote>` = `origin` |
| 46 | + - Extract `<owner>/<repo>` from origin URL |
| 47 | + |
| 48 | +3. **Fetch from base remote**: |
| 49 | + ```bash |
| 50 | + git fetch <base-remote> |
| 51 | + ``` |
| 52 | + |
| 53 | +### 3. Sync with Remote |
| 54 | + |
| 55 | +1. **Check if behind base branch**: |
| 56 | + ```bash |
| 57 | + git rev-list --count HEAD..<base-remote>/<base-branch> |
| 58 | + ``` |
| 59 | + - If count > 0, we're behind. Warn user and offer options: |
| 60 | + ``` |
| 61 | + Your branch is X commits behind <base-remote>/<base-branch>. |
| 62 | +
|
| 63 | + Options: |
| 64 | + 1. Rebase first: git pull --rebase <base-remote> <base-branch> |
| 65 | + 2. Continue anyway (may have merge conflicts in PR) |
| 66 | + ``` |
| 67 | + - Use AskUserQuestion to let user choose whether to continue or abort |
| 68 | +
|
| 69 | +### 4. Check for Changes |
| 70 | +
|
| 71 | +1. **Check for uncommitted changes**: |
| 72 | + ```bash |
| 73 | + git status --porcelain |
| 74 | + ``` |
| 75 | + - If output is non-empty, there are staged or unstaged changes → proceed to step 5 |
| 76 | + |
| 77 | +2. **Check for unpushed commits** (if no uncommitted changes): |
| 78 | + ```bash |
| 79 | + git rev-list --count <base-remote>/<base-branch>..HEAD |
| 80 | + ``` |
| 81 | + - If count > 0, there are unpushed commits → skip to step 7 |
| 82 | + - If count == 0, inform user and exit: |
| 83 | + ``` |
| 84 | + No changes detected. Your working directory is clean and up-to-date with <base-remote>/<base-branch>. |
| 85 | + Nothing to submit. |
| 86 | + ``` |
| 87 | +
|
| 88 | +### 5. Resolve Branch Name (BEFORE any commits) |
| 89 | +
|
| 90 | +**IMPORTANT**: Always resolve the branch name before staging or committing to avoid commits on the base branch. |
| 91 | +
|
| 92 | +1. **Check current branch**: |
| 93 | + ```bash |
| 94 | + git branch --show-current |
| 95 | + ``` |
| 96 | + |
| 97 | +2. **If on base branch (e.g., `main`)**: |
| 98 | + - Generate or use provided branch name |
| 99 | + - **Generate branch name** (if not provided via `--branch`): |
| 100 | + - Analyze changes to understand the change type: |
| 101 | + ```bash |
| 102 | + git diff --stat # Unstaged changes |
| 103 | + git diff --cached --stat # Staged changes |
| 104 | + git status --porcelain # All changes summary |
| 105 | + ``` |
| 106 | + - **Sanitize the branch name** (from title or generated): |
| 107 | + 1. Lowercase the string |
| 108 | + 2. Replace spaces with hyphens |
| 109 | + 3. Remove invalid git ref characters: `:`, `?`, `*`, `[`, `]`, `^`, `~`, `\`, `@{`, `..` |
| 110 | + 4. Replace consecutive hyphens/underscores with single hyphen |
| 111 | + 5. Trim leading/trailing hyphens |
| 112 | + 6. Truncate to reasonable length (50 chars max for branch name portion) |
| 113 | + - Prefix based on change type: `feature/`, `fix/`, `refactor/`, `docs/` |
| 114 | + - **Validate with git**: |
| 115 | + ```bash |
| 116 | + git check-ref-format --branch "<branch-name>" |
| 117 | + ``` |
| 118 | + - If validation fails, prompt user for a valid branch name |
| 119 | + - If no diff output and no title provided, prompt user for branch name |
| 120 | + - **Create and switch to the new branch BEFORE staging**: |
| 121 | + ```bash |
| 122 | + git checkout -b <branch-name> |
| 123 | + ``` |
| 124 | + |
| 125 | +3. **If already on a feature branch**: |
| 126 | + - Use the current branch name |
| 127 | + - No need to create a new branch |
| 128 | + |
| 129 | +### 6. Stage and Commit Changes |
| 130 | + |
| 131 | +1. **Stage all changes first**: |
| 132 | + ```bash |
| 133 | + git add -A |
| 134 | + ``` |
| 135 | + |
| 136 | +2. **Secret scanning check** (AFTER staging to catch all files): |
| 137 | + - **Run deterministic pattern check** (case-insensitive with expanded patterns): |
| 138 | + ```bash |
| 139 | + git diff --cached | grep -iE "(AKIA[A-Z0-9]{16}|ghp_[a-zA-Z0-9]{36}|sk-[a-zA-Z0-9]{48}|gho_[a-zA-Z0-9]{36}|api[_-]?key\s*[=:]|secret[_-]?key\s*[=:]|password\s*[=:]|private[_-]?key|bearer\s+[a-zA-Z0-9_-]+|token\s*[=:])" || true |
| 140 | + ``` |
| 141 | + - **Check for sensitive file names** (case-insensitive): |
| 142 | + ```bash |
| 143 | + git diff --cached --name-only | grep -iE "(\.env|credentials|secret|\.pem|\.key|\.p12|\.pfx|id_rsa|id_ed25519)$" || true |
| 144 | + ``` |
| 145 | + - **Optional**: For more thorough scanning, use dedicated tools if available: |
| 146 | + ```bash |
| 147 | + # gitleaks detect --staged --no-git # If gitleaks installed |
| 148 | + # trufflehog git file://. --only-verified --fail # If trufflehog installed |
| 149 | + ``` |
| 150 | + - Pay special attention to newly added files: |
| 151 | + ```bash |
| 152 | + git diff --cached --name-only --diff-filter=A |
| 153 | + ``` |
| 154 | + - If pattern check returns matches or sensitive files detected, **unstage and warn**: |
| 155 | + ```bash |
| 156 | + git reset HEAD # Unstage all files |
| 157 | + ``` |
| 158 | + Then use AskUserQuestion: |
| 159 | + ``` |
| 160 | + Warning: Potential secrets detected in files: |
| 161 | + - .env.local (contains API_KEY=) |
| 162 | + - config.json (contains "password":) |
| 163 | +
|
| 164 | + Files have been unstaged for safety. |
| 165 | +
|
| 166 | + Options: |
| 167 | + 1. Abort - review and remove secrets before retrying |
| 168 | + 2. Continue anyway - I confirm these are not real secrets (will re-stage) |
| 169 | + ``` |
| 170 | + - If user chooses to continue, re-stage with `git add -A` |
| 171 | + |
| 172 | +3. **Generate commit message**: |
| 173 | + - Run `git diff --cached --stat` to see what's being committed |
| 174 | + - Analyze the changes and generate a descriptive commit message |
| 175 | + - Use imperative mood ("Add", "Fix", "Update", "Refactor") |
| 176 | + - Format with HEREDOC: |
| 177 | + ```bash |
| 178 | + git commit -m "$(cat <<'EOF' |
| 179 | + <generated commit message> |
| 180 | +
|
| 181 | + Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> |
| 182 | + EOF |
| 183 | + )" |
| 184 | + ``` |
| 185 | +
|
| 186 | +### 7. Push Branch to Remote |
| 187 | +
|
| 188 | +1. **Resolve and validate branch name**: |
| 189 | + ```bash |
| 190 | + git branch --show-current |
| 191 | + ``` |
| 192 | +
|
| 193 | +2. **Guard: Prevent pushing from base branch**: |
| 194 | + - If current branch equals `<base-branch>` (e.g., `main`): |
| 195 | + - This can happen when step 4 skipped to step 7 due to unpushed commits on base |
| 196 | + - **Must create a new branch before proceeding**: |
| 197 | + - Generate branch name from unpushed commits (analyze `git log <base-remote>/<base-branch>..HEAD`) |
| 198 | + - Use provided `--branch` name if available |
| 199 | + - Create and switch: |
| 200 | + ```bash |
| 201 | + git checkout -b <branch-name> |
| 202 | + ``` |
| 203 | + - If branch creation fails or is declined, abort with error: |
| 204 | + ``` |
| 205 | + Error: Cannot create PR from base branch to itself. |
| 206 | + Please create a feature branch first or provide --branch <name>. |
| 207 | + ``` |
| 208 | +
|
| 209 | +3. **Push to push-remote** (always `origin`, even in fork workflows): |
| 210 | + ```bash |
| 211 | + git push -u <push-remote> <branch-name> |
| 212 | + ``` |
| 213 | +
|
| 214 | +### 8. Extract Commit Information for PR Body |
| 215 | +
|
| 216 | +1. Get commits on this branch (compare against base-remote to avoid stale data): |
| 217 | + ```bash |
| 218 | + git log <base-remote>/<base-branch>..HEAD --oneline |
| 219 | + ``` |
| 220 | +
|
| 221 | +2. Get changed files: |
| 222 | + ```bash |
| 223 | + git diff <base-remote>/<base-branch>..HEAD --stat |
| 224 | + ``` |
| 225 | +
|
| 226 | +3. Categorize changes for the template: |
| 227 | + - **Estimator/math changes**: files in `diff_diff/`, `rust/src/`, or `docs/methodology/` |
| 228 | + - Test changes: files in `tests/` |
| 229 | + - Documentation: files in `docs/`, `*.md`, `*.rst` |
| 230 | +
|
| 231 | +### 9. Generate PR Body |
| 232 | +
|
| 233 | +Fill in the template: |
| 234 | +
|
| 235 | +```markdown |
| 236 | +## Summary |
| 237 | +- <bullet point for each commit> |
| 238 | +
|
| 239 | +## Methodology references (required if estimator / math changes) |
| 240 | +- Method name(s): <from code analysis or "N/A - no methodology changes"> |
| 241 | +- Paper / source link(s): <from docstrings or "N/A"> |
| 242 | +- Any intentional deviations from the source (and why): <if applicable or "None"> |
| 243 | +
|
| 244 | +## Validation |
| 245 | +- Tests added/updated: <list test files or "No test changes"> |
| 246 | +- Backtest / simulation / notebook evidence (if applicable): <if tutorials updated or "N/A"> |
| 247 | +
|
| 248 | +## Security / privacy |
| 249 | +- Confirm no secrets/PII in this PR: Yes |
| 250 | +
|
| 251 | +--- |
| 252 | +Generated with Claude Code |
| 253 | +``` |
| 254 | +
|
| 255 | +**Template logic:** |
| 256 | +- **Methodology**: Mark "N/A" only if NO files changed in `diff_diff/`, `rust/src/`, or `docs/methodology/`. If methodology files changed, consult `docs/methodology/REGISTRY.md` for proper citations. |
| 257 | +- **Validation**: List `test_*.py` files changed, note tutorial updates |
| 258 | +- **Security**: Default "Yes", but warn if `.env`, credentials, or API key patterns detected |
| 259 | +
|
| 260 | +### 10. Create Pull Request |
| 261 | +
|
| 262 | +Use the MCP GitHub tool to create the PR: |
| 263 | +
|
| 264 | +``` |
| 265 | +mcp__github__create_pull_request with parameters: |
| 266 | + - owner: <target-owner> # upstream-owner (fork) or owner (direct) |
| 267 | + - repo: <target-repo> # upstream-repo (fork) or repo (direct) |
| 268 | + - title: <PR title> |
| 269 | + - head: <head-ref> # See below for fork vs direct |
| 270 | + - base: <base-branch> |
| 271 | + - body: <generated PR body> |
| 272 | + - draft: <true if --draft flag provided> |
| 273 | +``` |
| 274 | +
|
| 275 | +**Head reference format**: |
| 276 | +- **Direct workflow**: `head: <branch-name>` |
| 277 | +- **Fork workflow**: `head: <fork-owner>:<branch-name>` |
| 278 | +
|
| 279 | +**Extract remote info**: |
| 280 | +```bash |
| 281 | +# For target (where PR is created) |
| 282 | +git remote get-url <base-remote> |
| 283 | +# Parse: git@github.com:owner/repo.git or https://github.com/owner/repo.git |
| 284 | +
|
| 285 | +# For fork owner (if fork workflow) |
| 286 | +git remote get-url origin |
| 287 | +# Extract owner from URL |
| 288 | +``` |
| 289 | +
|
| 290 | +### 11. Report Results |
| 291 | +
|
| 292 | +``` |
| 293 | +Pull request created successfully! |
| 294 | +
|
| 295 | +Branch: <branch-name> |
| 296 | +PR: #<number> - <title> |
| 297 | +URL: https://github.com/<target-owner>/<target-repo>/pull/<number> |
| 298 | +
|
| 299 | +Changes included: |
| 300 | +<list of changed files> |
| 301 | +
|
| 302 | +Next steps: |
| 303 | +- Review the PR at the URL above |
| 304 | +- Request reviewers if needed |
| 305 | +- Run /review-pr <number> to get AI review |
| 306 | +``` |
| 307 | +
|
| 308 | +## Error Handling |
| 309 | +
|
| 310 | +### No Changes to Commit |
| 311 | +``` |
| 312 | +No changes detected. Your working directory is clean. |
| 313 | +Nothing to submit. |
| 314 | +``` |
| 315 | +
|
| 316 | +### Branch Already Exists |
| 317 | +``` |
| 318 | +Branch '<name>' already exists. |
| 319 | +Options: |
| 320 | +1. Provide different name: /submit-pr "title" --branch <new-name> |
| 321 | +2. Delete existing: git branch -D <name> |
| 322 | +``` |
| 323 | +
|
| 324 | +### Push/PR Creation Failed |
| 325 | +Show the error and provide manual fallback commands. |
| 326 | +
|
| 327 | +## Examples |
| 328 | +
|
| 329 | +```bash |
| 330 | +# Auto-generate everything |
| 331 | +/submit-pr |
| 332 | +
|
| 333 | +# With custom title |
| 334 | +/submit-pr "Add pre-trends power analysis" |
| 335 | +
|
| 336 | +# With custom branch |
| 337 | +/submit-pr "Fix bootstrap variance" --branch fix/bootstrap-variance |
| 338 | +
|
| 339 | +# Draft PR against different base |
| 340 | +/submit-pr "Refactor linalg module" --base develop --draft |
| 341 | +``` |
| 342 | +
|
| 343 | +## Notes |
| 344 | +
|
| 345 | +- Always stages ALL changes (`git add -A`). Stage manually first for partial commits. |
| 346 | +- Branch names auto-prefixed: feature/, fix/, refactor/, docs/ |
| 347 | +- Uses MCP GitHub server for PR creation (requires PAT with repo access) |
| 348 | +- Git push uses SSH or HTTPS based on remote URL configuration |
| 349 | +- **Fork workflows supported**: If `upstream` remote exists, PRs target upstream with `<fork-owner>:<branch>` head reference |
0 commit comments