12 Commits

Author SHA1 Message Date
Sameen Karim cf65746bd7 checkout: resolve remote stack by branch name (#477)
* checkout by remote branch name

* address review comments
2026-08-27 10:56:31 -04:00
Sameen Karim 1d9a20bd60 checkout: detect remote stack from current branch (#475)
* detect remote stack from current branch

* clean up phrasing
2026-08-27 10:56:31 -04:00
Sameen Karim ceffffc50a Interactive stack picker for checkout (#179)
* Add an interactive stack picker for checkout

Running `gh stack checkout` with no argument previously showed a plain
text prompt of locally tracked stacks only. It could not surface stacks
that exist only on the remote, gave no sense of a stack's state, and
listed fully merged stacks that can no longer be added to.

Replace it with an interactive picker (new package
internal/tui/checkoutview) that lists every stack available to you,
reconciling the local stack file with the Stacks REST API:

- Merges local and remote stacks, matched by stack id/number, and labels
  each Local (present locally, even if also tracked on the remote) or
  Remote (only on GitHub). Fully merged stacks are filtered out.
- Shows compact columns: stack number, first...last branch, base branch,
  a muted status bar summarizing merged/open/closed/unpushed PRs, type,
  and relative created time.
- Offers All / Local / Remote tabs and `/` type-to-filter search.

The picker renders inline rather than taking over the screen: it shows
up to ten rows with a scroll indicator, shrinks to fit short terminals,
and clears itself on exit.

Selecting a locally available stack checks out its top unmerged branch;
selecting a remote-only stack clones it down through the existing
checkout-by-number import flow.

When the Stacks API is unavailable (stacks not enabled, no auth, or a
network error), the picker degrades gracefully to a local-only list.

Also update the README, overview, and CLI reference for the new behavior.

* search by entire branch list

* address review comments
2026-07-15 12:07:45 -04:00
Sameen Karim f880f0d469 Stack number as primary identifier (#178)
* Support addressing a stack by its stack number

checkout now interprets a bare integer as a stack number first (the
identifier shown in the github.com stack UI), falling back to a locally
tracked PR number, then a PR number discovered from GitHub, then a branch
name. A new checkoutStackByNumber resolves the stack via GetStack and
checks out its top-most unmerged branch; the reconcile/import logic is
shared with the PR-number path.

unstack gains an optional <stack-number> positional argument to unstack a
specific locally tracked stack instead of the current one.

Copilot-Session: 03673c26-a245-42da-93ed-dfcebc92a740

* Surface the stack number in output and TUIs

Show the human-facing stack number wherever it is known:
- Append a "(stack #N)" label to submit, link, checkout, and unstack
  success messages.
- Add a "Stack #N" header line to the view command (short and static)
  and the stackview TUI header.
- Add a "Stack #N" info line to the submit TUI header when submitting
  an already-created stack.

Copilot-Session: 03673c26-a245-42da-93ed-dfcebc92a740

* Update docs and agent instructions for the new API

- cli.md: document checkout/unstack by stack number and drop the
  "PATs are not supported" note (any gh-authenticated user can now run
  stack operations).
- quick-start.md: drop the PAT-not-supported note.
- AGENTS.md / copilot-instructions.md: ClientOps is now 13 methods over
  the public Stacks REST API; remove the TokenForHostFn test hook; note
  the stack file's id/number identity.
- SKILL.md: add checkout/unstack-by-stack-number quick references.

Copilot-Session: 03673c26-a245-42da-93ed-dfcebc92a740

* address review comments
2026-07-15 12:07:44 -04:00
Sameen Karim a82dc3ef1d Migrate to new Stacks REST API (#177)
* Add stack Number field to local model and schema

The new Stacks REST API exposes a human-facing stack number (shown in the
github.com UI) alongside the internal stack id. Add a Number field to the
stack.Stack model and document it in schema.json so it can be persisted in
the .git/gh-stack file. Purely additive; behavior is unchanged until callers
populate it.

Copilot-Session: 03673c26-a245-42da-93ed-dfcebc92a740

* Cut over stack operations to the public Stacks REST API

Replace the private cli_internal stack endpoints with the new public
Stacks REST API (/repos/{owner}/{repo}/stacks):
- ListStacks / FindStackForPR (?pull_request= filter) / GetStack for reads
- CreateStack, which now returns the created stack including its number
- AddToStack for delta-only appends (there is no full-replace endpoint)
- Unstack for server-driven removal (204 dissolved / 200 partial / 422)

Migrate all callers (checkout, submit, link, sync, unstack, utils) and
drop the client-side unstack eligibility pre-check — the server now
decides which PRs can be unstacked. checkout discovers stacks via the
pull_request filter; submit/link express updates as append-only deltas;
unstack adopts partial-unstack semantics, keeping local tracking when
PRs remain stacked on GitHub.

RemoteStack now carries the stack number, and stack updates resolve a
stack's number from its internal id for stack files that predate the
Number field.

Copilot-Session: 03673c26-a245-42da-93ed-dfcebc92a740

* Remove the personal access token (PAT) limitation

The new Stacks REST API is public, so any user authenticated with the
GitHub CLI (including via a PAT with repo scope) can perform stack
operations once the feature is enabled for their repository. Remove the
PAT detection and the private-preview gating:

- Delete Config.WarnIfPAT / IsPersonalAccessToken and the TokenForHostFn
  test hook (internal/config/auth.go is no longer needed).
- Drop the submit pre-flight that aborted on a PAT.
- Rename warnStacksUnavailableOrPAT to warnStacksUnavailable and simplify
  it to the "stacked PRs not enabled" message.

Copilot-Session: 03673c26-a245-42da-93ed-dfcebc92a740

* address review comments
2026-07-15 12:07:44 -04:00
Sameen Karim 95f04b8fed Sync remote stack with local stack state (#175)
* pull in remote updates during sync

* cancel aborts operation

* switch to nearest surviving branch

* simplified options to remote delete

* stack existing PRs from submit TUI

* docs updates

* address review comments
2026-07-15 12:07:44 -04:00
Sameen Karim 756002b4c9 ensure local trunk branch for required operations (#127)
* Ensure trunk branch exists locally before commands that need it

When a user starts a stack after renaming their initial branch
(e.g. `git branch -m newbranch`), the trunk branch (e.g. main) may
not exist as a local branch. Commands that pass the trunk name to git
operations like merge-base, rebase, or rev-parse then fail with:

  fatal: Not a valid object name main

Add an `ensureLocalTrunk` helper that checks whether the trunk branch
exists locally and, if not, fetches it from the remote and creates a
local tracking branch. This mirrors the pattern already used in the
checkout command for importing stacks.

Commands updated:
- modify: call ensureLocalTrunk before the linearity check in
  CheckStackLinearity, which uses IsAncestor(trunk, branch). This was
  the originally reported failure.
- rebase: call ensureLocalTrunk after fetch and before fastForwardTrunk
  and the cascade rebase. git rebase requires a locally resolvable ref;
  the remote tracking ref alone is not sufficient.
- trunk: call ensureLocalTrunk before CheckoutBranch so that
  `gh stack trunk` works even when trunk was never created locally.
- checkout: refactor the existing inline BranchExists + CreateBranch
  block to use the shared helper.

Also fix an incorrect comment in fastForwardTrunk that claimed "the
remote tracking ref is sufficient for rebasing" — verified empirically
that `git rebase main` fails when main has no local branch, even after
fetching origin/main.

Commands that were already safe and required no changes:
- sync: fetches trunk explicitly and fastForwardTrunk guards with
  BranchExists
- push, switch, navigate, unstack: do not reference trunk
- add, submit: do not require trunk as a local git ref
- view: handles IsAncestor errors gracefully (false positive is
  acceptable since rebase will fix it)

* add check to avoid unnecessary remote selection prompt
2026-06-15 13:54:20 -04:00
Sameen Karim 499985210a parse PR URLs in args (#122)
* Accept PR URLs in link and checkout commands

Add support for GitHub PR URLs (e.g. https://github.com/owner/repo/pull/42)
as arguments to `gh stack link` and `gh stack checkout`, in addition to
the existing PR number and branch name support.

For `link`: PR URLs are parsed in findExistingPR before the numeric check.
Unlike numeric args, if a URL-extracted PR number doesn't exist, the command
errors immediately rather than falling through to branch name lookup (since
a URL can never be a valid branch name).

For `checkout`: PR URLs are parsed in runCheckout before the numeric check,
routing to resolveNumericTarget which supports both local and remote API
fallback — same behavior as passing a PR number directly.

Closes #115

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* update docs

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-15 13:54:18 -04:00
Sameen Karim d235a21a9c alert for unsupported auth tokens (#113)
When users authenticate the GitHub CLI with a personal access token
(PAT) instead of OAuth (`gh auth login`), the `cli_internal` stacks
API endpoints return 404. The CLI previously interpreted this as
"Stacked PRs are not enabled for this repository," which is misleading
— the feature may be enabled, but the token type simply cannot access
the internal endpoints.

This is a recurring source of user confusion. The docs already note
that PATs are not supported, but users don't always read them before
hitting the error.

This change adds token-type detection by inspecting the `gh` auth
token prefix:

  - `gho_`        → OAuth (supported)
  - `ghs_`        → GitHub App installation token (supported)
  - `ghp_`        → Classic PAT (NOT supported)
  - `github_pat_` → Fine-grained PAT (NOT supported)

When a PAT is detected, the CLI now shows:

  ⚠ Personal access tokens are not supported by gh stack
    Run `gh auth login` to authenticate with OAuth instead.

Instead of the misleading:

  ⚠ Stacked PRs are not enabled for this repository

Changes:

- Add `internal/config/auth.go` with auth detection methods on Config:
  `IsPersonalAccessToken()`, `WarnIfPAT()`, and `RepoHost()`. Uses a
  `TokenForHostFn` field on Config for test overrides, following the
  same pattern as `GitHubClientOverride`.

- Add a pre-flight PAT check in `cmd/submit.go` before the
  `ListStacks` call. If a PAT is detected, the command aborts early
  with a clear error instead of making a doomed API call.

- Update all 404 handlers for `cli_internal` endpoints to check the
  token type and show the appropriate message:
  - `cmd/submit.go` (createNewStack)
  - `cmd/link.go` (listStacksSafe, createLink)
  - `cmd/checkout.go` (checkoutRemoteStack)

- Add `warnStacksUnavailableOrPAT()` helper in `cmd/utils.go` that
  shows the PAT-specific warning when applicable, falling back to the
  generic "not enabled" message for non-PAT tokens.

- Add unit tests in `internal/config/auth_test.go` for token prefix
  detection and warning output.

- Add integration tests in `cmd/submit_test.go` verifying that both
  classic PATs (`ghp_`) and fine-grained PATs (`github_pat_`) trigger
  the pre-flight check and abort before any API calls.

- Add `warnStacksUnavailableOrPAT` tests in `cmd/utils_test.go`
  verifying correct message selection based on token type.

- Update existing 404 tests to explicitly set an OAuth token so they
  continue exercising the ListStacks 404 path.
2026-06-15 13:54:17 -04:00
Sameen Karim 8a9f1c86f8 commands help text (#88)
* more help text for commands

* improved root help

* fix typo

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* updated examples for add cmd

Co-authored-by: Sameen Karim <skarim@github.com>

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-15 14:01:02 -04:00
Sameen Karim e06284ff5c skip pulling merged branches during remote checkout (#16)
* skip pulling merged branches during remote checkout

* show message if stack fully merged
2026-04-13 18:36:35 -04:00
Sameen Karim b01754e4a9 Initial release 2026-04-10 03:32:08 -04:00