ez-stack 0.1.3

A CLI tool for managing stacked PRs with GitHub
# ez-stack Problems & Improvement Opportunities

This document captures detailed problems, confusions, and missing features encountered while using `ez` during a real multi-branch stacked PR session. It is intended to be fed to an agent to improve the CLI.

The session involved:
- Two stacked branches: `worktree-onyx-cli` (primary) → `roshan/cli-pypi-wheel` (stacked on top)
- Dozens of rebases, force-pushes, code review cycles, and PR updates across both branches
- Multiple subsidiary branches created for small focused changes

---

## 1. Documentation Issues

### 1.1 README doesn't explain `ez create` commit behavior

The README shows:
```bash
ez create feat/parse-config
# ... make changes ...
ez commit -m "add config parser"
```

This implies `ez create` only creates the branch, and you commit separately. But `ez create` also accepts `-m` to commit in one step. The README never explains:
- When to use `ez create <name> -m "msg"` vs `ez create <name>` + `ez commit`
- That `ez create -m "msg"` requires changes to already be staged — if nothing is staged, it errors
- What the error looks like when you use `-m` with no staged changes (the error message doesn't clearly say "nothing staged")

**Fix**: Document the `-m` shorthand clearly. Explain that it requires staged changes. Show both patterns side by side.

### 1.2 `ez push` PR creation behavior is undocumented

The README says `ez push` force-pushes all branches. It does NOT mention that `ez push` also auto-creates GitHub PRs if they don't exist yet (equivalent to running `ez submit` as well). This caused confusion when I ran `gh pr create` after `ez push` and got an error saying a PR already existed.

**Fix**: README should clearly state: "`ez push` force-pushes branches AND creates PRs for any branch that doesn't have one. Running `gh pr create` after `ez push` will fail because the PR already exists."

### 1.3 No guidance on PR title/body

Neither `ez push` nor `ez submit` has any documented way to set the PR title or body. When a PR is auto-created, the title defaults to the branch name and the body is empty. In practice, you need to either:
1. Run `gh pr edit` manually after `ez push` to set title/body
2. Or use `gh pr create` directly (but `ez push` already created it, causing a conflict)

This is a significant workflow gap. Setting PR content is a core part of creating a PR, and having no integrated way to do it means every `ez push` requires a follow-up `gh pr edit`.

**Fix**: Add `--title` and `--body` (or `--body-file`) flags to both `ez push` and `ez submit`. Alternatively, after creating a new PR, drop the user into an interactive editor (like `gh pr create` does) to fill in the title and body.

### 1.4 CLAUDE.md doesn't mention `ez push` creates PRs

The global CLAUDE.md instructions say:
> Push + create/update PR for current branch: `ez push`

This is close but ambiguous — "create/update PR" makes it sound like it always creates. The table entry for `ez submit` says "Create or update GitHub PRs for all branches in the stack." The distinction between `ez push` (one branch) and `ez submit` (all branches) is unclear from the table. Does `ez push` also call the equivalent of `ez submit` for the current branch? The answer appears to be yes, but this is not clear.

**Fix**: Clarify in CLAUDE.md and README what the exact difference is between `ez push` and `ez submit`. If `ez push` also creates PRs, say so explicitly.

---

## 2. Missing Features

### 2.1 `ez create` should support committing in one step without requiring pre-staged changes

Current behavior: `ez create <name> -m "msg"` errors if nothing is staged.

Expected behavior (based on analogy with `git commit -am`): `ez create <name> -am "msg"` should stage all tracked changes and commit, matching `ez commit -am`.

This is a papercut that slows down the "create branch + commit" workflow significantly. You have to remember to stage first, or omit `-m` and use a separate `ez commit`.

### 2.2 `ez push` needs `--title` and `--body` / `--body-file` flags

When `ez push` creates a new PR, it should accept:
- `--title "My PR title"` — set the PR title (default: branch name)
- `--body "Description"` — set the PR body inline
- `--body-file path/to/file.md` — set PR body from a file
- `--edit` — open an editor (like `gh pr create` does) before submitting

Without this, every new PR needs a manual `gh pr edit` call, defeating the purpose of the integrated workflow.

### 2.3 `ez push` with `--base` is not a valid flag

I tried `ez push --base main` to override the base branch when pushing — this flag doesn't exist. The only way to change a PR's base branch after creation is `gh pr edit --base <branch>`. This should either be documented or `ez push` should accept a `--base` flag.

### 2.4 `ez create` should fail gracefully if the branch already exists

When I accidentally tried to create a branch that already existed locally, I got an error that was not clearly actionable. The error didn't say "this branch already exists — use `ez checkout <name>` to switch to it." I had to `git branch -D <name>` manually and re-run `ez create`.

**Fix**: When `ez create` encounters an existing local branch, show a clear message:
```
Branch 'roshan/release-workflow-fixes' already exists.
  → To switch to it: ez checkout roshan/release-workflow-fixes
  → To delete and recreate: ez delete roshan/release-workflow-fixes
```

### 2.5 No `ez push --current` or per-branch push

`ez push` pushes the entire stack. If you want to push only the current branch (e.g., you have uncommitted work on other branches), there's no clean way to do it without manually running `git push --force-with-lease origin <branch>`. The distinction should be documented and ideally `ez push --current` should exist.

---

## 3. Workflow Confusion

### 3.1 `ez create <name> -m "msg"` with no staged changes errors silently-ish

This happened multiple times. I'd run `ez create some-branch -m "some message"` immediately after creating a file but before staging it. The error message was about git commit failing, not about `ez create`. It would be better to detect "nothing staged" before attempting the commit and show: "Nothing is staged. Either stage changes first or use `ez create <name>` without `-m` and commit separately."

### 3.2 Rebasing after trunk moves is confusing

After `worktree-onyx-cli` (the parent branch) merged into main, I needed to:
1. Sync main locally
2. Rebase `roshan/cli-pypi-wheel` onto the new main

The `ez sync` command is supposed to handle this, but the behavior when the parent branch has been merged and deleted from remote is not clearly documented. The README example shows `ez sync` after a merge, but doesn't cover the case where the local branch is ahead of remote, the remote branch is deleted, and you need to restack onto the updated main.

In practice I had to:
- `git fetch origin`
- `git rebase origin/main` on the child branch
- `git push --force-with-lease origin <child-branch>`
- Then manually update the PR base via `gh pr edit --base main`

None of this was obvious from the `ez` docs. `ez sync` should handle this entire flow.

### 3.3 `gh pr create` conflicts with `ez push` auto-PR creation

Multiple times during the session, I ran `gh pr create` thinking the PR hadn't been created yet, only to get:
```
a pull request for branch 'roshan/cli-pypi-wheel' into branch 'worktree-onyx-cli' already exists
```

The root cause is that `ez push` silently creates PRs. If you don't know this and your mental model is "push = push, submit = create PRs," you will be confused.

**Fix**: When `ez push` creates a PR, print a clear confirmation line:
```
✓ Created PR #123: https://github.com/org/repo/pull/123
```
This makes it obvious the PR was created and prevents the user from running `gh pr create` again.

### 3.4 Stale remote ref errors after force-pushes are not handled

Multiple times I got errors like:
```
! [rejected] worktree-onyx-cli -> worktree-onyx-cli (stale info)
error: failed to push some refs
```

This happened because `ez push` uses `--force-with-lease` but the local remote-tracking ref was stale. The fix was always `git fetch origin <branch>` first. `ez` should either:
- Automatically fetch the latest remote ref before pushing with `--force-with-lease`
- Or detect the "stale info" error and suggest `git fetch origin <branch>` as the fix

### 3.5 `ez log` doesn't show remote PR status clearly

`ez log` shows the stack tree, but during the session it wasn't always clear which branches had open PRs, which had merged PRs, and which had no PR yet. A clearer visual distinction (e.g., color coding: green = open, purple = merged, grey = no PR) would help.

---

## 4. Error Messages

### 4.1 `ez create` with `-m` and nothing staged

Current (approximate): `error: nothing to commit, working tree clean`

Expected:
```
Nothing staged to commit. Stage your changes first:
  git add <files>
  ez create <name> -m "your message"

Or create the branch without committing:
  ez create <name>
```

### 4.2 Branch already exists

Current: git error about branch existing

Expected:
```
Branch 'roshan/release-workflow-fixes' already exists locally.
Use 'ez checkout roshan/release-workflow-fixes' to switch to it.
```

### 4.3 Stale remote ref on push

Current: raw git error about rejected push

Expected:
```
Push rejected: remote ref is stale. Run:
  git fetch origin roshan/cli-pypi-wheel
Then retry: ez push
```

---

## 5. Feature Requests (Nice to Have)

### 5.1 `ez push --body-file <path>` for PR body from file

When writing longer PR descriptions (which often live in a markdown file), it's useful to reference a file. `gh pr create --body-file` supports this; `ez push` should too.

### 5.2 `ez pr edit` command

A thin wrapper around `gh pr edit` that knows the current branch and its PR number, so you can run:
```bash
ez pr edit --title "New title" --body "Updated description"
```
Instead of having to look up the PR number and run `gh pr edit <number> ...`.

### 5.3 `ez sync --dry-run`

Show what `ez sync` would do (which branches would be rebased, which would be deleted) without actually doing it. Useful before running `ez sync` in a complex stack.

### 5.4 `ez status` should show PR URL

`ez status` shows the current branch and stack position. It should also show the PR URL for the current branch (if one exists) so you can quickly open it.

### 5.5 Interactive `ez create` that prompts for PR title/body

When `ez create` is run, optionally prompt:
```
Branch name: feat/my-feature
Commit message (optional): add my feature
PR title (optional, press Enter to skip): My Feature
PR body (optional, press Enter to skip):
```

This would capture PR metadata upfront, before any code is written, making `ez push` able to create a well-described PR without any follow-up commands.

---

## Summary Table

| Problem | Severity | Type |
|---------|----------|------|
| `ez push` silently creates PRs without documenting it | High | Documentation + UX |
| No way to set PR title/body via `ez push` | High | Missing feature |
| `ez create -m` errors silently with nothing staged | Medium | Error message + feature |
| Branch-already-exists has no actionable error | Medium | Error message |
| Stale remote ref on push is not handled | Medium | Error handling |
| `ez push --base` flag doesn't exist | Medium | Missing feature |
| No `ez push --current` for single-branch push | Low | Missing feature |
| `ez log` PR status not visually distinct | Low | UX |
| `ez sync` doesn't document post-merge restack flow | Medium | Documentation |
| `ez status` doesn't show PR URL | Low | Missing feature |