Skip to content

Commit 90eb999

Browse files
authored
Merge pull request #4 from github/skarim/agent-skill-improvements
Update SKILL.md instructions for branch naming and git ops
2 parents 4e0263f + 1e7f19c commit 90eb999

1 file changed

Lines changed: 52 additions & 37 deletions

File tree

skills/gh-stack/SKILL.md

Lines changed: 52 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ metadata:
1616

1717
```
1818
main (trunk)
19-
└── auth-layer → PR #1 (base: main) - bottom (closest to trunk)
20-
└── api-endpoints → PR #2 (base: auth-layer)
21-
└── frontend → PR #3 (base: api-endpoints) - top (furthest from trunk)
19+
└── feat/auth-layer → PR #1 (base: main) - bottom (closest to trunk)
20+
└── feat/api-endpoints → PR #2 (base: feat/auth-layer)
21+
└── feat/frontend → PR #3 (base: feat/api-endpoints) - top (furthest from trunk)
2222
```
2323

2424
The **bottom** of the stack is the branch closest to the trunk, and the **top** is the branch furthest from the trunk. Each branch inherits from the one below it. Navigation commands (`up`, `down`, `top`, `bottom`) follow this model: `up` moves away from trunk, `down` moves toward it.
@@ -44,13 +44,14 @@ gh extension install github/gh-stack
4444
## Agent rules
4545

4646
1. **Always supply branch names as positional arguments** to `init`, `add`, and `checkout`.
47-
2. **Always use `--auto` when pushing** to skip PR title prompts.
48-
3. **Always use `--json` when viewing** to get structured output.
49-
4. **Use `--remote <name>` when multiple remotes are configured**, or set `remote.pushDefault` in git config.
50-
5. **Avoid branches shared across multiple stacks.** If a branch belongs to multiple stacks, commands exit with code 6. Check out a non-shared branch first.
51-
6. **Plan your stack layers by dependency order before writing code.** Foundational changes (models, APIs, shared utilities) go in lower branches; dependent changes (UI, consumers) go in higher branches. Think through the dependency chain before running `gh stack init`.
52-
7. **Use standard `git add` and `git commit` for staging and committing.** This gives you full control over which changes go into each branch. The `-Am` shortcut is available but should not be the default approach—stacked PRs are most effective when each branch contains a deliberate, logical set of changes.
53-
8. **Navigate down the stack when you need to change a lower layer.** If you're working on a frontend branch and realize you need API changes, don't hack around it at the current layer. Navigate to the appropriate branch (`gh stack down`, `gh stack checkout`, or `gh stack bottom`), make and commit the changes there, run `gh stack rebase --upstack`, then navigate back up to continue.
47+
2. **When a prefix is set, pass only the suffix to `add`.** `gh stack add auth` with prefix `feat``feat/auth`. Passing `feat/auth` creates `feat/feat/auth`.
48+
3. **Always use `--auto` when pushing** to skip PR title prompts.
49+
4. **Always use `--json` when viewing** to get structured output.
50+
5. **Use `--remote <name>` when multiple remotes are configured**, or set `remote.pushDefault` in git config.
51+
6. **Avoid branches shared across multiple stacks.** If a branch belongs to multiple stacks, commands exit with code 6. Check out a non-shared branch first.
52+
7. **Plan your stack layers by dependency order before writing code.** Foundational changes (models, APIs, shared utilities) go in lower branches; dependent changes (UI, consumers) go in higher branches. Think through the dependency chain before running `gh stack init`.
53+
8. **Use standard `git add` and `git commit` for staging and committing.** This gives you full control over which changes go into each branch. The `-Am` shortcut is available but should not be the default approach—stacked PRs are most effective when each branch contains a deliberate, logical set of changes.
54+
9. **Navigate down the stack when you need to change a lower layer.** If you're working on a frontend branch and realize you need API changes, don't hack around it at the current layer. Navigate to the appropriate branch (`gh stack down`, `gh stack checkout`, or `gh stack bottom`), make and commit the changes there, run `gh stack rebase --upstack`, then navigate back up to continue.
5455

5556
## Thinking about stack structure
5657

@@ -64,29 +65,38 @@ Stacked branches form a dependency chain: each branch builds on the one below it
6465

6566
```
6667
main (trunk)
67-
└── data-models ← shared types, database schema
68-
└── api-endpoints ← API routes that use the models
69-
└── frontend-ui ← UI components that call the APIs
70-
└── integration ← tests that exercise the full stack
68+
└── feat/data-models ← shared types, database schema
69+
└── feat/api-endpoints ← API routes that use the models
70+
└── feat/frontend-ui ← UI components that call the APIs
71+
└── feat/integration ← tests that exercise the full stack
7172
```
7273

7374
This is illustrative — choose branch names and layer boundaries that reflect the specific work you're doing. The key principle is: if code in one layer depends on code in another, the dependency must be in the same branch or a lower one.
7475

76+
### Branch naming
77+
78+
Prefer initializing stacks with a prefix (`-p`). Prefixes group branches under a namespace (e.g., `feat/auth`, `feat/api`) and keep branch names clean and consistent. When a prefix is set, pass only the suffix to subsequent `add` calls — the prefix is applied automatically. Without a prefix, you'll need to pass the full branch name each time.
79+
7580
### Staging changes deliberately
7681

77-
Don't dump all changes into a single commit or branch. Stage changes in batches based on logical grouping:
82+
The main reason to use `git add` and `git commit` directly is to control **which changes go into which branch**. When you have multiple files in your working tree, you can stage a subset for the current branch, commit them, then create a new branch and stage the rest there:
7883

7984
```bash
80-
# Stage only the model files for this branch
85+
# You're on feat/data-models with several new files in your working tree.
86+
# Stage only the model files for this branch:
8187
git add internal/models/user.go internal/models/session.go
8288
git commit -m "Add user and session models"
8389

84-
# Stage related migration
8590
git add db/migrations/001_create_users.sql
8691
git commit -m "Add user table migration"
92+
93+
# Now create a new branch for the API layer and stage the API files there:
94+
gh stack add api-routes # created & switched to feat/api-routes branch
95+
git add internal/api/routes.go internal/api/handlers.go
96+
git commit -m "Add user API routes"
8797
```
8898

89-
Multiple commits per branch are fine and encouraged—they make the PR easier to review. The key is that all commits in a branch relate to the same logical concern.
99+
This keeps each branch focused on one concern. Multiple commits per branch are fine — the key is that all commits in a branch relate to the same logical concern, and changes that belong to a different concern go in a different branch.
90100

91101
### When to create a new branch
92102

@@ -111,12 +121,12 @@ Small, incidental fixes (e.g., fixing a typo you noticed) can go in the current
111121

112122
| Task | Command |
113123
|------|---------|
114-
| Create a stack | `gh stack init branch-a` |
115-
| Create a stack with a prefix | `gh stack init -p feat auth` |
124+
| Create a stack (recommended) | `gh stack init -p feat auth` |
125+
| Create a stack without prefix | `gh stack init auth` |
116126
| Adopt existing branches | `gh stack init --adopt branch-a branch-b` |
117127
| Set custom trunk | `gh stack init --base develop branch-a` |
118-
| Add a branch to stack | `gh stack add branch-name` |
119-
| Add branch + stage all + commit (shortcut) | `gh stack add -Am "message" new-branch` |
128+
| Add a branch to stack (suffix only if prefix set) | `gh stack add api-routes` |
129+
| Add branch + stage all + commit | `gh stack add -Am "message" api-routes` |
120130
| Push + create PRs | `gh stack push --auto` |
121131
| Push as drafts | `gh stack push --auto --draft` |
122132
| Push without creating PRs | `gh stack push --skip-prs` |
@@ -174,7 +184,7 @@ git commit -m "Add auth middleware tests"
174184

175185
# 4. When you're ready for a new concern, add the next branch
176186
gh stack add api-routes
177-
# → creates feat/api-routes (prefixed), checks it out
187+
# → creates feat/api-routes (prefix applied automatically — just pass the suffix)
178188

179189
# 5. Write code for the API layer
180190
cat > api.go << 'EOF'
@@ -189,7 +199,7 @@ git commit -m "Add API routes"
189199

190200
# 6. Add a third layer for frontend
191201
gh stack add frontend
192-
# → creates feat/frontend, checks it out
202+
# → creates feat/frontend (just the suffix — prefix is automatic)
193203

194204
cat > frontend.go << 'EOF'
195205
package frontend
@@ -286,15 +296,15 @@ When a PR is squash-merged on GitHub, the original branch's commits no longer ex
286296
# After PR #1 (feat/auth) is squash-merged on GitHub:
287297
gh stack sync
288298
# → fetches latest, detects the merge, fast-forwards trunk
289-
# → rebases feat/api-routes onto updated trunk using --onto (skips merged branch)
290-
# → rebases feat/api-tests onto feat/api-routes
299+
# → rebases feat/api-routes onto updated trunk (skips merged branch)
300+
# → rebases feat/frontend onto feat/api-routes
291301
# → pushes updated branches
292302
# → reports: "Merged: #1"
293303

294304
# Verify the result
295305
gh stack view --json
296306
# → feat/auth shows "isMerged": true, "state": "MERGED"
297-
# → feat/api-routes and feat/api-tests show updated heads
307+
# → feat/api-routes and feat/frontend show updated heads
298308
```
299309

300310
If `sync` hits a conflict during this process, it restores all branches to their pre-rebase state and exits with code 3. See [Handle rebase conflicts](#handle-rebase-conflicts-agent-workflow) for the resolution workflow.
@@ -366,28 +376,33 @@ gh stack init [branches...] [flags]
366376
```
367377

368378
```bash
369-
# Create a stack with new branches (branched from trunk)
379+
# Set a branch prefix (recommended — subsequent `add` calls only need the suffix)
380+
gh stack init -p feat auth
381+
# → creates feat/auth
382+
383+
# Multi-part prefix (slashes are fine — suffix-only rule still applies)
384+
gh stack init -p monalisa/billing auth
385+
# → creates monalisa/billing/auth
386+
387+
# Create a stack with new branches (no prefix — use full branch names)
370388
gh stack init branch-a branch-b branch-c
371389

372390
# Use a different trunk branch
373391
gh stack init --base develop branch-a branch-b
374392

375393
# Adopt existing branches into a stack
376394
gh stack init --adopt branch-a branch-b branch-c
377-
378-
# Set a branch prefix (branch names you provide are automatically prefixed)
379-
gh stack init -p feat auth
380-
# → creates feat/auth
381395
```
382396

383397
| Flag | Description |
384398
|------|-------------|
385399
| `-b, --base <branch>` | Trunk branch (defaults to the repo's default branch) |
386400
| `-a, --adopt` | Adopt existing branches instead of creating new ones |
387-
| `-p, --prefix <string>` | Set a branch name prefix for auto-generated names |
401+
| `-p, --prefix <string>` | Branch name prefix. Subsequent `add` calls only need the suffix (e.g., with `-p feat`, `gh stack add auth` creates `feat/auth`) |
388402

389403
**Behavior:**
390404

405+
- Using `-p` is recommended — it simplifies branch naming for subsequent `add` calls
391406
- Creates any branches that don't already exist (branching from the trunk branch)
392407
- In `--adopt` mode: validates all branches exist, rejects if any is already in a stack or has an existing PR
393408
- Checks out the last branch in the list
@@ -406,7 +421,7 @@ gh stack add [branch] [flags]
406421
**Recommended workflow — create the branch, then use standard git:**
407422

408423
```bash
409-
# Create a new branch and switch to it
424+
# Create a new branch and switch to it (just the suffix — prefix is applied automatically)
410425
gh stack add api-routes
411426

412427
# Write code, stage deliberately, and commit
@@ -438,7 +453,7 @@ gh stack add -um "Fix auth bug" auth-fix
438453

439454
- `-A` and `-u` are mutually exclusive.
440455
- When the current branch has no commits (e.g., right after `init`), `add -Am` commits directly on the current branch instead of creating a new one.
441-
- If a prefix was set during `init`, the prefix is applied to branch names: `prefix/branch-name`.
456+
- **Prefix handling:** Only pass the suffix when a prefix is set. `gh stack add api` with prefix `todo``todo/api`. Passing `todo/api` creates `todo/todo/api`. Without a prefix, pass the full branch name.
442457
- If called from a branch that is not the topmost in the stack, exits with code 5: `"can only add branches on top of the stack"`. Use `gh stack top` to switch first.
443458
- **Uncommitted changes:** When using `gh stack add branch-name` without `-Am`, any uncommitted changes (staged or unstaged) in your working tree carry over to the new branch. This is standard git behavior — the working tree is not touched. Commit or stash changes on the current branch before running `add` if you want a clean starting point on the new branch.
444459

@@ -505,7 +520,7 @@ gh stack sync [flags]
505520

506521
1. **Fetch** latest changes from the remote
507522
2. **Fast-forward trunk** to match remote (skips if already up to date, warns if diverged)
508-
3. **Cascade rebase** all stack branches onto their updated parents (only if trunk moved). Handles squash-merged PRs automatically with `--onto`. If a conflict is detected, **all branches are restored** to their pre-rebase state and the command exits with code 3 — see [Handle rebase conflicts](#handle-rebase-conflicts-agent-workflow) for the resolution workflow
523+
3. **Cascade rebase** all stack branches onto their updated parents (only if trunk moved). Handles squash-merged PRs automatically. If a conflict is detected, **all branches are restored** to their pre-rebase state and the command exits with code 3 — see [Handle rebase conflicts](#handle-rebase-conflicts-agent-workflow) for the resolution workflow
509524
4. **Push** all active branches atomically
510525
5. **Sync PR state** from GitHub and report the status of each PR
511526

@@ -560,7 +575,7 @@ gh stack rebase --abort
560575

561576
**Conflict handling:** See [Handle rebase conflicts](#handle-rebase-conflicts-agent-workflow) in the Workflows section for the full resolution workflow.
562577

563-
**Squash-merge detection:** If a branch's PR was squash-merged on GitHub, the rebase automatically uses `git rebase --onto` to correctly replay commits on top of the merge target. This is handled transparently.
578+
**Squash-merge detection:** If a branch's PR was squash-merged on GitHub, the rebase automatically handles this and correctly replays commits on top of the merge target.
564579

565580
**Rerere (conflict memory):** `git rerere` is enabled by `init` so previously resolved conflicts are auto-resolved in future rebases.
566581

0 commit comments

Comments
 (0)