mirror of
https://github.com/github/gh-stack.git
synced 2026-09-14 20:26:28 +08:00
v0.0.8
77 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
38aba1b710 |
Deprecate branch name prefixes (#182)
* deprecate prefix functionality * clean up branch auto-naming * update docs * preserve literal hyphens when slugifying branch namesv0.0.8 |
||
|
|
9dfef64ffb |
Append to an existing stack by stack number with link (#181)
* link append to stack by number * updated link docs * address review comment |
||
|
|
e0b2104e75 |
Remote unstack by stack number (#180)
* unstack as a pure api wrapper if stack not checked out locally * update unstack docs * address review comments |
||
|
|
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 |
||
|
|
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 |
||
|
|
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
|
||
|
|
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 |
||
|
|
623b7e6fbf |
Fix rebase treating queued PRs as merged (#173)
* Don't treat queued PRs as merged when rebasing the stack gh stack rebase and gh stack sync share cascadeRebase, which skipped branches via IsSkipped() (merged or queued) and then switched to a `git rebase --onto` that drops the skipped branch's commits from every downstream branch. That is right for a merged PR — its commits are already in trunk — but wrong for a queued PR: its commits only exist on its own branch, which is frozen in the merge queue, so the branches above it were rebased onto trunk and lost work they depend on. Handle the two cases separately. A merged branch still activates --onto so its commits are dropped. A queued branch is still skipped (its branch is frozen and is not rebased or pushed), but onto mode is reset so downstream branches rebase normally onto the queued branch, keeping its commits underneath. The --onto target search, the runRebase --onto seed, and the continueRebase display base now key on IsMerged() instead of IsSkipped(), so a queued predecessor no longer forces downstream branches onto trunk. gh stack sync is fixed through the same shared helper. Add rebase coverage for a queued branch mid-stack, a merged branch below a queued branch, and --upstack above a queued branch, plus a sync test that also asserts the queued branch is excluded from the push. The transient queued state is injected through the GitHub mock's merge-queue entry. * Refresh queued PR state when continuing a stack rebase continueRebase reloads the stack from disk, where the Queued flag is transient (json:"-") and therefore lost, and it only called syncStackPRs after the cascade. So if the initial rebase conflicted on a branch below a queued branch, `gh stack rebase --continue` resumed with that branch seen as active: it rebased the frozen merge-queue branch and rebuilt the downstream branches on a local history that differs from the queued branch. Call syncStackPRs right after resolving the stack — before selecting the base and cascading the remaining branches — mirroring the refresh runRebase already does before its cascade. The queued flag is repopulated, so queued branches stay skipped and downstream branches stay stacked on them. Add TestRebase_Continue_QueuedBranchBelowConflict, which conflicts below a queued branch and asserts the frozen branch is not rebased and the branch above stays stacked on it. Verified to fail without the refresh. |
||
|
|
46b5dc4f78 |
Fix link failing when an existing stack PR is queued for merge (#171)
gh stack link validated PR eligibility before it fetched the repository's stacks, so validatePREligibility rejected any queued PR unconditionally — including one already a member of the stack being updated. Because link is additive (every existing stack PR must be re-listed or the update is refused for dropping them), a stack whose bottom PR was in the merge queue could never take new PRs on top: re-listing the queued PR failed eligibility, and omitting it failed the drop check. Fetch the stacks and resolve the target stack before validating eligibility, then skip the eligibility checks (queued, auto-merge, merged, closed) for any PR that is already a member of that stack — those PRs are not being added, so the checks don't apply. PRs not already in the stack, and brand-new stacks, keep the previous strict behavior. prevalidateStack now takes the resolved stack instead of looking it up a second time. Add coverage for linking new PRs onto a stack whose existing PR is queued, merged, or has auto-merge enabled, and for still rejecting a queued PR that is not yet part of the target stack. |
||
|
|
0c51c08255 |
Fix to properly revert after aborting modify (#167)
* Add cherry-pick abort/quit/in-progress git primitives Introduce IsCherryPickInProgress() (detects .git/CHERRY_PICK_HEAD) and split the existing cherry-pick reset into two distinct operations: - CherryPickQuit() runs `git cherry-pick --quit`, clearing the sequencer state without touching the index (used to clear stale state before starting a fresh cherry-pick). - CherryPickAbort() now runs `git cherry-pick --abort`, which fully restores the working tree and index to the pre-cherry-pick state. The previous CherryPickAbort() ran --quit, which leaves an unmerged index and therefore cannot recover a conflicted fold-down. Integration tests cover the in-progress detection, the full abort restore, and the --quit-leaves-index behavior. * Fix modify --abort leaving a broken stack after a conflict When `gh stack modify` hit a rebase or cherry-pick conflict it saved state with phase "conflict" and told the user to run `gh stack modify --abort` to restore. But runModifyAbort had no case for PhaseConflict, so it fell into the default branch that merely printed "unexpected modify state phase" and deleted the state file without unwinding. The in-flight rebase/cherry-pick stayed active, branches were left partially rewritten, and the deleted state file also made --continue impossible: the stack was stuck in limbo. Fixes: - runModifyAbort now unwinds on PhaseConflict (same recovery as PhaseApplying), aborting the in-progress operation, resetting branch tips to their pre-modify SHAs, restoring stack metadata, and clearing state. - Unwind now also aborts an in-progress cherry-pick (fold-down conflicts), not just a rebase. Without this the restore checkouts would fail on the unmerged cherry-pick index. - ContinueApply now records a subsequent cascade-rebase conflict as ConflictType "rebase" instead of leaving a stale "cherry_pick", so the next --continue calls RebaseContinue rather than failing in CherryPickContinue. Adds coverage for the conflict-phase abort, pending-submit no-op abort, Unwind aborting an active cherry-pick, and the cherry-pick to rebase ConflictType transition. * Persist fold-branch removal when a post-fold cascade rebase conflicts ContinueApply removes the folded branch from the in-memory stack after a fold-down cherry-pick is resolved, but a subsequent cascade rebase conflict only saved the modify state file, not the stack metadata. On the next --continue the on-disk metadata (folded branch still present) was re-read, and because ConflictType is now "rebase" the fold-removal block was skipped, so the final save resurrected the folded branch as a phantom entry pointing at an orphaned tip. Persist the stack file alongside the state file on a cascade-rebase conflict, mirroring ApplyPlan's save-on-conflict, so the fold removal survives recovery. Adds an end-to-end regression test covering the fold-then-cascade-conflict path across two --continue calls. |
||
|
|
ae5f889c2f |
Fully-qualify branch refspecs when pushing (#165)
* Fully-qualify branch refspecs when pushing gh-stack builds `git push` arguments from stack branch names in `internal/git`. The force path passed `<branch>:refs/heads/<branch>`, and the non-force path passed bare branch names. A git refspec treats a leading `+` as "force update", and Git allows branch names that begin with `+`, so a branch named `+feature` was parsed as refspec syntax for `feature`: the force path pushed local `feature` into remote `+feature`, and the non-force path force-updated remote `feature`. Build fully-qualified refspecs for both the source and destination of every push: `refs/heads/<branch>:refs/heads/<branch>`. A branch name can no longer be reinterpreted as a refspec modifier. Force updates are still requested via the existing `--force-with-lease` flags, whose ref names were already fully-qualified. `DeleteRemoteBranch` is fully-qualified the same way. The `Push` signature and every call site are unchanged. Add real-git integration tests covering the force and non-force paths with a `+`-prefixed branch. * rm redundant if and simplify |
||
|
|
5975945dfd |
Ignore symlinked pull request templates (#164)
Template discovery in internal/pr read candidate template paths with os.ReadFile, which follows symlinks. A pull request template that is a symlink (for example .github/pull_request_template.md pointing to a file outside the repository) would therefore be read through to its target, and that target's contents would be used as the PR body by `gh stack submit` and `gh stack link`. Reuse cli/cli's githubtemplate package (already a dependency) for template discovery instead of the hand-rolled path list. It is the same code `gh pr create` uses, and it ignores symlinked templates, so only regular template files inside the repository are read. FindTemplate keeps the same signature, so the submit and link callers are unchanged. Two behavior changes come with the switch: - YAML front-matter is stripped from the template, matching `gh pr create`. - Template filename matching is slightly broader; hyphenated and non-.md variants are now recognized. Add tests for FindTemplate and for the `gh stack submit --auto` and `gh stack link` PR-creation flows to confirm symlinked templates are not followed. |
||
|
|
514f5abf86 |
Bump the npm_and_yarn group across 1 directory with 2 updates (#159)
Bumps the npm_and_yarn group with 1 update in the /docs directory: [esbuild](https://github.com/evanw/esbuild). Updates `esbuild` from 0.27.4 to 0.28.1 - [Release notes](https://github.com/evanw/esbuild/releases) - [Changelog](https://github.com/evanw/esbuild/blob/main/CHANGELOG.md) - [Commits](https://github.com/evanw/esbuild/compare/v0.27.4...v0.28.1) Updates `vite` from 7.3.2 to 8.1.0 - [Release notes](https://github.com/vitejs/vite/releases) - [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md) - [Commits](https://github.com/vitejs/vite/commits/create-vite@8.1.0/packages/vite) --- updated-dependencies: - dependency-name: esbuild dependency-version: 0.28.1 dependency-type: indirect dependency-group: npm_and_yarn - dependency-name: vite dependency-version: 8.1.0 dependency-type: indirect dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
97c1b31284 |
Bump astro in /docs in the npm_and_yarn group across 1 directory (#138)
Bumps the npm_and_yarn group with 1 update in the /docs directory: [astro](https://github.com/withastro/astro/tree/HEAD/packages/astro). Updates `astro` from 6.3.1 to 6.4.8 - [Release notes](https://github.com/withastro/astro/releases) - [Changelog](https://github.com/withastro/astro/blob/astro@6.4.8/packages/astro/CHANGELOG.md) - [Commits](https://github.com/withastro/astro/commits/astro@6.4.8/packages/astro) --- updated-dependencies: - dependency-name: astro dependency-version: 6.4.8 dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
e208dfc488 |
Create/update stack on remote during sync (#156)
* Create/update the remote stack on sync and fix false "Stack synced"
`gh stack sync` reported "Stack synced" even when it had not created or
updated the stack object on GitHub. After running `gh stack init` to
adopt existing branches and then opening PRs outside the CLI, `gh stack
sync` detected the open PRs and printed "Stack synced" — but no stack had
ever been created on the server.
There were two distinct bugs:
1. Sync never reconciled the remote stack object. `runSync` called
`syncStackPRs`, which only *reads* PR state and links PRs to local
branches; it never called the create/update path. So the branches were
rebased and pushed and the PRs were detected, but the stack on GitHub
was never created.
2. The final message was unconditional. `runSync` always printed "Stack
synced", which is supposed to mean "the stack object on GitHub now
reflects the local stack" — something that can only be true when two or
more open PRs exist and the remote stack was actually created/updated.
Fix
Reconcile the remote stack from sync, and make the closing message reflect
what actually happened.
* cmd/sync.go
- Add a reconciliation step (5b) after PR-state sync: when the stack has
two or more open PRs, link them into a stack on GitHub via the new
`syncRemoteStack` helper. It inspects existing stacks first and:
- short-circuits quietly when a remote stack already lists exactly
these PRs (records the ID, prints "Stack already up to date on
GitHub") so routine syncs don't issue a redundant, misleading
update;
- otherwise delegates to `syncStack` to create a new stack, adopt an
untracked one, or update a partially-formed one.
Sync never opens PRs — that remains `gh stack submit`'s job.
- Replace the unconditional "Stack synced" with a result-driven message:
"Stack synced" when the remote stack object was created/updated/in
sync, otherwise "Branches synced" (fewer than two PRs, stacked PRs
unavailable, a cross-stack divergence, or no GitHub client).
- Update the command's long description to document the stack-object
step and the two possible closing messages.
* cmd/submit.go
- Thread a `synced bool` return through the existing, tested stack
helpers so sync can tell whether the remote stack object now matches
local: `syncStack`, `createNewStack`, and `updateStack` now return
`bool`; `adoptRemoteStack` returns `(handled, synced)`; and
`handleCreate422` returns `bool` (true only when the PRs are already
stacked together). Extract the shared `stackPRNumbers` helper.
- This is additive: submit's single call site ignores the new return
value, so submit's behavior, output, and tests are unchanged. Reusing
these helpers (instead of duplicating the 404/422 handling in sync)
keeps the create/adopt/update logic in one tested place.
Tests
* cmd/sync_test.go — six new cases covering the reconciliation matrix:
- TestSync_CreatesRemoteStackWhenPRsExist: open PRs but no remote stack
-> CreateStack is called and the new ID is persisted to the stack file;
output contains "Stack created on GitHub" and "Stack synced".
- TestSync_AdoptsExistingEqualRemoteStack: a matching remote stack ->
no create/update, ID recorded, "Stack synced".
- TestSync_UpdatesPartialRemoteStack: a subset stack -> UpdateStack with
the full PR list, "Stack synced".
- TestSync_FewerThanTwoPRs_BranchesSynced: one PR -> no stack API calls,
"Branches synced", not "Stack synced".
- TestSync_StacksUnavailable_BranchesSynced: 404 on create -> warns,
"Branches synced".
- TestSync_PRsSpanMultipleStacks_BranchesSynced: PRs across two stacks ->
divergence warning, no create/update, "Branches synced".
Docs
Document the new stack-object step and the "Stack synced" vs "Branches
synced" distinction in:
- README.md
- docs/src/content/docs/reference/cli.md
- skills/gh-stack/SKILL.md
- docs/src/content/docs/introduction/overview.md
- docs/src/content/docs/guides/stacked-prs.md
- docs/src/content/docs/guides/workflows.md
* Address PR review: one ListStacks per sync, command-neutral guidance
Two follow-ups from the #156 review (both flagged optional / non-blocking).
1. Remove the redundant ListStacks round-trip on sync's create path.
syncRemoteStack fetched the stack list for its already-up-to-date
short-circuit, then delegated to syncStack -> adoptRemoteStack, which
listed the stacks again — two GETs on the first-sync-create and
membership-changed paths. Refactor adoptRemoteStack into a list-accepting
reconcileUntrackedStack(cfg, client, s, prNumbers, stacks): syncStack now
fetches the list once and passes it down, and syncRemoteStack reuses the
list it already fetched. Net: exactly one ListStacks per sync. This also
drops the (handled, synced) tuple. Submit's behavior is unchanged.
2. Make the divergence / dropped-PR guidance command-neutral. The shared
helper emitted submit-specific wording ("reconcile them before
submitting", "...then `gh stack submit`") that is now reachable from
`gh stack sync`. Reword to "reconcile them first" and drop the trailing
`gh stack submit` so it reads correctly from either command.
Tests: assert exactly one ListStacks on the create path and that the
divergence guidance is not submit-specific.
* increment skill file version
* Simplify sync reconciliation: reuse syncStack instead of a parallel path
Review feedback noted the change felt heavier than the fix warranted.
The weight came from `syncRemoteStack` (cmd/sync.go), a near-duplicate of
submit's `syncStack` — same <2-PR guard, ListStacks, and update/create
dispatch — that existed only to add an "already up to date" short-circuit.
That one optimization is what spawned the second entry point, the
pre-fetched-list threading, and the double-ListStacks it then required.
Collapse it to a single reconciliation path:
- Remove `syncRemoteStack`; `gh stack sync` now calls the shared
`syncStack` directly. One path, one ListStacks per sync.
- Fold `createNewStack` into `reconcileUntrackedStack` (renamed from
`adoptRemoteStack`) so it returns a single `synced bool` instead of a
`(handled, synced)` tuple and owns its own ListStacks again.
- Inline `stackPRNumbers` back into `syncStack` (it was only extracted to
share with the now-removed `syncRemoteStack`).
- Drop the now-unused `strconv`/`github` imports from cmd/sync.go.
Behavior note: a routine re-sync of an already-tracked stack now prints
"Stack updated on GitHub with N PRs" instead of "Stack already up to date
on GitHub". This is accurate (sync does PUT the current state) and matches
submit. The "Stack synced" / "Branches synced" summary is unchanged, and
submit's behavior is unchanged.
v0.0.7
|
||
|
|
754d190490 |
Fork unmerged branches into new stack (#154)
* Fork unmerged branches into a new stack when the base stack is fully merged
Once every PR that is officially part of a stack on GitHub has been merged
-- especially after the merged branches are deleted upstream -- you can no
longer add to that stack. A new PR on top would target the trunk directly
instead of chaining onto the merged PRs, so the remote stack's "each PR's
base ref is the previous PR's head ref" invariant no longer holds. On the
next `gh stack submit`, the stack update was rejected and surfaced as a
confusing, dead-end warning:
Failed to update stack on GitHub: Pull requests must form a stack,
where each PR's base ref is the previous PR's head ref
`submit` had no handling for this: `syncStack` always sent the full PR list
(including the merged-and-deleted ones), so the API rejected the broken
chain even though the new PRs had already been created with correct bases.
Fork the survivors into a fresh stack instead of failing. After syncing PR
state and before pushing, `runSubmit` now calls `maybeForkFromMergedBase`:
- It triggers only when every PR officially part of the tracked remote
stack (`s.ID`) has merged. Membership is read from the stacks API, so
open PRs that are not part of the remote stack do not count, and -- this
is the key guard -- a normal partial, bottom-up merge (where the remote
stack still lists an open PR) is left completely untouched. A cheap
pre-check (the local stack must have at least one merged branch) avoids
an extra ListStacks call on the common path.
- The local branches are partitioned: those still in the merged remote
stack stay behind; everything else (new branches, plus open PRs that were
never part of that remote stack) is lifted into a brand-new stack rooted
at the original trunk, with an empty remote ID. The bottom survivor is
re-based onto the trunk.
- `runSubmit` continues with the new stack, so the push loop, PR creation,
and `syncStack` all operate on it; the empty ID routes `syncStack` through
the adopt/create path and a fresh stack is created on GitHub.
- The original, fully merged stack is left untouched on GitHub. Locally it
is kept as a record only if at least one of its branches still exists in
the working copy; otherwise it is dropped. No data is lost -- those PRs
are already merged on GitHub.
To restructure the stack file safely, add `StackFile.IndexOfStack`, which
locates a stack by pointer identity so the fork can capture what it needs
before `AddStack`/`RemoveStack` reallocate the underlying slice.
Also soften the partial-merge case that does not fork: when an `UpdateStack`
call fails with the "must form a stack" 422 and the stack still contains
merged branches, report it as an informational note (the unmerged PRs were
pushed and re-based onto the trunk) rather than a scary failure warning.
Scope is limited to `submit`. `add` and `checkout` keep their existing
"refuse and suggest `gh stack init`" behavior on fully merged stacks.
Tests:
- cmd/submit_test.go: TestSubmit_ForksWhenRemoteStackFullyMerged covers both
disposition variants (the old stack is removed when its merged branches
are gone locally, kept when they still exist) and asserts that only the
new branches are pushed, the fork message is printed, a fresh stack is
created, and the local stack file is split into two stacks.
TestSubmit_NoForkWhenRemoteStackHasOpenPR verifies the everyday bottom-up
merge is not forked and that the broken-chain 422 is reported calmly.
TestUpdateStack_BrokenChainAfterMerge checks the calm-vs-warn branch.
- internal/stack/stack_test.go: TestIndexOfStack covers identity lookup and
the not-found case.
Docs: README, the CLI reference, the stacked-PRs guide, the FAQ, and the
agent SKILL.md note that submitting onto a fully merged stack starts a new
stack rooted at the trunk.
* Handle fully merged stacks gracefully in the view and modify TUIs
Merged branches (and their PRs) are not selectable, so once an entire stack
has landed there is nothing to act on -- yet the TUIs did not reflect that:
- `gh stack view` still drew a highlighted cursor on the top branch even
though it could not be selected. Navigation, checkout, and the per-branch
toggles all silently did nothing, with no indication of why.
- `gh stack modify` opened its full editor on a stack with nothing left to
restructure, instead of short-circuiting like `gh stack submit` does when
there is nothing to submit.
Reflect the "nothing actionable" state in both TUIs.
View (internal/tui/stackview/model.go):
- Hide the cursor when every branch is merged. `New` now starts the cursor
at -1 and only lands it on the current or first non-merged branch; when
none exists the cursor stays hidden, so no row is rendered as focused. The
existing `m.cursor >= 0` guards and merged-skipping `moveCursor` already
make every cursor action a no-op in that state, and mouse-wheel scrolling
still works for tall merged stacks.
- Dim the shortcuts that depend on the cursor. `buildHeaderConfig` marks
navigate, commits, files, open PR, and checkout as `Disabled` (rendered
gray via the existing ShortcutEntry.Disabled styling) when all branches
are merged, leaving only `q quit` active.
Modify (cmd/modify.go):
- Short-circuit before opening the TUI. After preconditions pass and PR
state is synced, `runModify` now returns early when the stack is fully
merged, printing "All branches in this stack have been merged" and
pointing at `gh stack init`, exiting cleanly (exit 0) like submit's
"nothing to submit" path. The linearity and merge-queue precondition
checks already skip merged branches, so they do not fire spuriously.
Tests:
- internal/tui/stackview/model_test.go: the cursor is hidden (-1) when all
branches are merged; up/down/enter do not move it or trigger a checkout;
View renders without panicking on a hidden cursor; buildHeaderConfig
disables every cursor-dependent shortcut (and only those) when all merged,
and leaves them all enabled when active branches remain.
- cmd/modify_test.go: runModify short-circuits on a fully merged stack,
printing the message and returning no error without launching the TUI.
|
||
|
|
9b30b7f381 |
Adopt an existing remote stack on submit instead of erroring (#153)
When a stack existed on GitHub but was not recorded in the local tracking
file (`.git/gh-stack`, i.e. `s.ID == ""`) -- for example a stack created
from the web UI or in another clone -- `gh stack submit` blindly called the
create-stack API. The server rejected the request because the PRs were
already stacked, surfacing a confusing, dead-end error:
Could not create stack: Pull requests are already part of a stack.
`syncStack()` only ever consulted the local `s.ID`. When it was empty it
always POSTed to create, and the only handling for the rejection
(`handleCreate422`) parsed the error string and at best printed "up to
date" -- it never imported the remote stack ID, so the local/remote
mismatch was never resolved and recurred on every submit.
Detect and adopt the existing stack before creating one. When `s.ID` is
empty and there are at least two PRs, `syncStack` now lists the
repository's stacks and reconciles, mirroring the patterns already used by
`checkout` and `link`:
- No remote stack contains our PRs -> create a new one (unchanged).
- The remote stack's PRs are all tracked locally (an exact match, or e.g.
two of three already stacked with a third added on top) -> adopt the
remote stack ID into local tracking and PUT the full, ordered PR list,
appending any new PRs to the top. The adopted ID is persisted by the
existing stack.Save in runSubmit.
- The remote stack contains PRs we are not tracking locally -> refuse and
warn rather than silently dropping them, pointing the user to
`gh stack checkout <pr>` to import the full stack first.
- Our PRs are spread across multiple remote stacks (an unresolvable
divergence, since a PR can belong to only one stack) -> warn and skip.
The new logic reuses `findMatchingStack`, `formatPRList`, and `slicesEqual`
from the link command; `syncStack`'s signature is unchanged, and adoption
is best-effort: listing failures degrade to the previous create path and
never fail the submit, since the PRs are already pushed and created.
Also broaden the create-stack 422 fallback (`handleCreate422`) to recognize
the server's "already part of a stack" phrasing in addition to "already
stacked", so that if the stack listing is unavailable the message is still
actionable instead of the raw "Could not create stack" error above.
Tests: six new cases in cmd/submit_test.go cover exact-match adoption (ID
imported, no create/update call), additive two-of-three adoption (update
with the full list), remote-superset refusal, PRs spanning multiple stacks,
the ListStacks-error fallthrough to create, and the broadened fallback
phrasing. Existing syncStack tests are unchanged.
|
||
|
|
797c62e9b4 |
Adapt theme colors to light and dark terminals (#149)
* Make the TUIs adapt to light and dark terminal backgrounds
The submit, view, and modify TUIs were tuned for dark terminals. On light
or solarized-light backgrounds the result was hard to read and inverted:
primary text used ANSI white (invisible on white), dim chrome used light
grays (too faint), and accents used bright cyan (low contrast) — so
"active" things looked lighter than "disabled" ones.
Introduce a centralized, background-aware color palette and migrate all
three TUIs to it:
- internal/tui/shared/theme.go: a semantic palette of lipgloss.AdaptiveColor
values (primary/muted/faint text, chrome/border, accent, PR-state colors,
badge backgrounds, row shade, button, switch). lipgloss resolves the
light/dark variant per render from the terminal background, which Bubble
Tea detects at startup; terminals that don't report it fall back to dark,
preserving the original look.
- Replace every hardcoded ANSI color in shared/, submitview/, and
modifyview/ with palette roles. The four pre-rendered status icons now
render at use-time so their adaptive colors resolve correctly. The submit
markdown preview picks glamour's light or dark style from the detected
background.
- GH_STACK_THEME=auto|light|dark forces the palette for terminals that
mis-detect (some SSH/tmux setups); wired via the root command's
PersistentPreRun before any render. Documented in the README and CLI docs.
Neutral text/chrome use truecolor hex (GitHub Primer-inspired) for
predictability across themes, including solarized which repurposes ANSI
8-15; lipgloss downsamples on terminals without truecolor.
Tests verify the palette resolves differently for light vs dark and that
GH_STACK_THEME is honored.
* Apply background-aware colors to all command output
Background detection and the GH_STACK_THEME override (added for the TUIs)
only affected the interactive screens. Plain command output -- status
messages and interactive prompts -- went through the mgutz/ansi library
with fixed ANSI palette names (green/red/yellow/cyan/...), so it never
adapted to the terminal background and could read poorly on light or
solarized themes.
Unify everything on the same adaptive palette so all colors react to the
detected background and to GH_STACK_THEME.
- Extract internal/theme, a foundational package with no internal
dependencies, that owns:
- the background-aware lipgloss.AdaptiveColor palette (moved out of
internal/tui/shared),
- ApplyOverride(), the GH_STACK_THEME=auto|light|dark logic, and
- non-TUI colorizers (Success/Error/Warning/Blue/Magenta/Cyan/Gray/
Bold) plus FgSeqs(), which returns the raw start/reset escapes used
to color the user's echoed prompt input.
- internal/tui/shared/theme.go now re-exports the palette, so the TUI code
keeps referring to shared.ColorX unchanged.
- internal/config/config.go wires the Config.Color* funcs to the theme
colorizers and drops mgutz/ansi (now an indirect dependency only).
- cmd/utils.go colors the prompt icon and echoed input via theme.
- cmd/root.go calls theme.ApplyOverride() in PersistentPreRun.
Detection adds no cost: because the command package imports Bubble Tea,
its init() already triggers (and caches) the terminal background query for
every command, so the non-TUI colorizers just read the cached value.
Terminals that don't answer the query fall back to the dark palette;
GH_STACK_THEME=light|dark forces it. Colors are truecolor on capable
terminals and downsample to the nearest ANSI color elsewhere.
Tests: internal/theme covers palette adaptiveness, ApplyOverride, the
colorizers, and FgSeqs; a new internal/config test verifies the wired-up
Config.Color* funcs adapt to the background when color is enabled.
Docs: README and the CLI reference note that GH_STACK_THEME now controls
all colored output, not just the interactive screens.
No behavior change beyond colors.
|
||
|
|
b6bcce1bfe |
Add an interactive submit TUI for customizing each PR's title, description, and draft state (#147)
* Add submitview data model and PR draft override plumbing
Introduce the internal/tui/submitview package that will back the new
interactive `gh stack submit` TUI, and wire its per-PR override contract
into the submit command without changing current behavior.
- submitview: BranchState model (NEW/OPEN/DRAFT/QUEUED/MERGED/CLOSED) with
selectability/editability rules, SubmitNode UI state with edit detection,
PRDraft override type, state derivation, title/description prefill, and
state-badge/panel/tab styles.
- submit: refactor ensurePR/createPR to accept an optional per-branch
override map (title/body/draft/include); deselected NEW branches are
pushed but get no PR.
The override map is nil on the --auto / non-interactive path, so the
agent-compat contract is unchanged. Fully unit tested.
* Add single-screen submit TUI
Introduce an interactive, single-screen editor for `gh stack submit`,
built on Bubble Tea and Lip Gloss.
The left panel renders the stack as a connected tree down to the trunk.
Every branch without a PR is included by default; deselect one with its
checkbox or `^x`. Because each PR builds on the branch below it,
deselecting a branch also deselects the ones stacked above it, and
re-including a branch re-includes the ones below it that it depends on.
The cursor uses its own cyan accent so it reads distinctly from the green
new/included color; existing PRs are shown dimmed with a no-entry glyph.
The right panel edits the focused branch's PR in web-create-PR order: a
header with the branch name and an include chip ("Creating PR" /
"Skipped"), the title, a scrollable description (Glamour markdown preview
and $EDITOR escape, with a scrollbar and mouse click-to-position), and a
ready to draft segmented toggle (defaulting to ready). A footer strip
shows the PR progress, the next branch, and the editor hints. Skipping a
branch dims its body; branches that already have a PR show a read-only
card linking to the PR.
It shares the gh-stack header (art, title, stack info, and keyboard
shortcuts) with `gh stack view` and `gh stack modify` for a unified look.
Submit every included PR at once with Ctrl+S. Full keyboard and mouse
support throughout.
* Wire the single-screen submit TUI into `gh stack submit`
Launch the submit editor from `gh stack submit` in interactive terminals,
collecting per-branch PR drafts and applying them in a single batch. In
non-interactive terminals or with --auto, fall back to auto-generated
titles and skip the editor. Update the README and CLI reference to
describe the single-screen flow.
* Use the API PR title/body for existing PRs and fix new-PR defaults
For existing PRs, the submit TUI showed a commit/template-derived draft
instead of the pull request's real title and body. Fetch the actual title
and body and render them in the read-only card:
- open/draft/queued (tracked) and adopted-open PRs now carry title/body
through the existing batch sync (added the fields to the GraphQL queries
and PRDetails — no extra round trips), and
- merged branches (which skip the live refresh) are filled in by a targeted
enrichment step run only when the submit TUI opens.
Also align the new-PR defaults with the non-TUI submit's defaultPRTitleBody:
- Title: the commit subject only when the branch has exactly one commit,
otherwise the humanized branch name (was: the oldest commit's subject even
for multi-commit branches).
- Description: the PR template, else the single commit's body, else empty
(removed the bulleted commit-subject list for multi-commit branches).
* Stop mouse wheel from leaking escape characters into form fields
Scrolling the mouse wheel while a title or description field was focused
could insert stray characters such as "[<65;54;51M" into the field. The
submit TUI ran the Bubble Tea program with WithMouseAllMotion (mode 1003),
which reports an event on every pointer move. During a wheel scroll that
floods the input stream, and under that volume Bubble Tea splits an SGR
mouse escape sequence ("\x1b[<Cb;Cx;Cy(M|m)") across input reads; the
leftover bytes of a partially-parsed sequence are then emitted as key
runes and inserted into the focused text input.
Two changes fix this:
- Switch to WithMouseCellMotion (mode 1002), which reports clicks, drag,
and wheel but not idle pointer motion. That removes the per-move input
flood, so under a real terminal's reads (up to 256 bytes) the only
fragment that still surfaces is a single, clean burst at each wheel
notch boundary. The TUI never used idle-hover for rendering, so
cell-motion loses nothing.
- Drop any leaked fragments before they reach a field. A split SGR mouse
sequence surfaces as an Alt+"[" (the consumed "\x1b[") followed by
body fragments ("<65;54;5", "1M"), or occasionally the whole body in
one run ("[<65;54;51M"). consumeLeakedMouseKey recognises the start,
swallows the body up to its "M"/"m" terminator, and bails out the
moment a rune does not fit an SGR body, so ordinary typing (including
"<", ";", digits, "M") and bracketed pastes are never eaten.
Tests cover every split point of an SGR sequence, single-run tails,
preserved real typing, a stray Alt+"[", bracketed paste, and that wheel
events never modify the focused field. Verified end-to-end by feeding
1,500 wheel sequences through the real parser under terminal-sized reads
and confirming the field stays empty.
* Re-enable mouse tracking after the external editor closes
Opening the description in $EDITOR with ^e and then quitting left the
mouse unresponsive: clicks and wheel scrolling stopped working while
keyboard navigation still did.
The editor is launched with tea.ExecProcess, which releases the terminal
before running the command and calls Bubble Tea's RestoreTerminal when it
returns. RestoreTerminal re-enables the alt-screen, bracketed paste, and
focus reporting, but it does not re-enable mouse tracking. The editor
(e.g. vim) disables mouse reporting on exit, so once control returns to
the TUI the terminal no longer emits mouse events.
Re-arm mouse mode when the editor-finished message arrives by batching
tea.EnableMouseCellMotion with the handler's command. That re-enables
cell-motion and SGR mouse reporting, matching the WithMouseCellMotion
option the program starts with, on every editor-return path (success or
error).
* dead code cleanup
* support mouse input to move cursor in title field
* use textArea for title to support word wrap for long inputs
|
||
|
|
0efc764f8c |
Redesign TUI shared header and embed the GitHub logo as an image (#143)
* Redesign the shared header and embed the GitHub logo as an image Rework the shared gh-stack header (used by `view` and `modify`, and reused by `submit` later in this stack) for a cleaner, more responsive look, and replace the braille/ASCII Invertocat with a real image of the GitHub mark. - Logo: embed the Invertocat PNG with go:embed and draw it via an inline- image protocol (kitty or iTerm2). It is image-or-nothing: when no protocol is available, stdout is not a TTY, or we are inside tmux/screen, no logo is drawn and the text falls back to the normal left padding. Detection is environment-based and cached so it never blocks the TUI, and a fixed kitty image id lets the header clear or replace the logo in place instead of leaving copies behind. - Layout: place the logo in the top-left corner beside the title and version, with the stack-info lines left-aligned beneath it on the same left margin. Size the box to its content for each view so there is no trailing empty row. - Responsiveness: hide the logo progressively — first when the viewport is too narrow, then a little before the rest of the header at short heights, where a vertical resize could otherwise leave a ghost of the inline image. - Add unit tests for the header's responsive thresholds. * Drop the unused HeaderHeight constant and dedupe the header config build Follow-up to review feedback on the shared-header redesign: - Remove the HeaderHeight constant. Nothing referenced it (callers compute height via HeaderHeightFor), and its doc described a "maximum" that HeaderHeightFor does not actually enforce, so the comment was misleading. - In the view and modify View() methods, build the header config once and reuse it for both RenderHeader and the height reservation instead of rebuilding it twice per frame. The click/scroll handlers keep deriving the height from the same config, so the header's dimensions remain a single source of truth. |
||
|
|
67d118fbf4 |
Unstack Updates: preflight PR eligibility before delete and improve API errors (#136)
* unstack: preflight PR eligibility before delete and improve API errors.Block unstack delete only when all PRs in the stack are ineligible * Update cmd/unstack.go Co-authored-by: Sameen Karim <skarim@github.com> * remove cfg and update help text --------- Co-authored-by: Sameen Karim <skarim@github.com> |
||
|
|
4759126a51 |
Add AGENTS.md and copilot-instructions.md for AI agent onboarding (#133)
* Add AGENTS.md and copilot-instructions.md for AI agent onboarding Add two complementary instruction files so AI coding agents can work effectively in this repository without re-discovering conventions: - AGENTS.md (7K chars): agent-agnostic open standard with full project structure, build/test commands, coding patterns, testing conventions, error handling, key interfaces, and non-obvious gotchas. - .github/copilot-instructions.md (2K chars): concise Copilot-specific instructions under the 4K code review limit, covering the essentials and referencing AGENTS.md for full details. Closes #132 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: update cmd to generate an executable binary Co-authored-by: Sameen Karim <skarim@github.com> * fix: update cmd to get the build output as an exec binary Co-authored-by: Sameen Karim <skarim@github.com> * Fix build command and errors.As usage per review feedback - go build ./... compiles but does not produce a binary. Changed to go build -o gh-stack . which actually outputs the executable. - errors.As(err, &ExitError{}) panics at runtime because the value type ExitError does not satisfy the error interface (only *ExitError does). Updated to the correct two-line pattern matching cmd/root.go. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Sameen Karim <skarim@github.com> |
||
|
|
84d160be10 |
Bump js-yaml in /docs in the npm_and_yarn group across 1 directory (#130)
Bumps the npm_and_yarn group with 1 update in the /docs directory: [js-yaml](https://github.com/nodeca/js-yaml). Updates `js-yaml` from 4.1.1 to 4.2.0 - [Changelog](https://github.com/nodeca/js-yaml/blob/master/CHANGELOG.md) - [Commits](https://github.com/nodeca/js-yaml/commits) --- updated-dependencies: - dependency-name: js-yaml dependency-version: 4.2.0 dependency-type: indirect dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
e45a88c984 |
rebase without trunk (#129)
* flag for rebasing without trunk * update docs with new rebase flagv0.0.6 |
||
|
|
4c05e58b83 |
cache selected remote (#128)
* Save selected remote to gh-stack.remote git config Users with multiple git remotes are prompted to choose a remote on every gh stack operation, which is tedious. This adds the ability to persist that choice so it only needs to be made once. When a user interactively selects a remote (because multiple remotes exist and none is configured as a push default), they are now shown a Y/n follow-up prompt offering to save that remote for all future gh stack operations. If accepted, the choice is written to the local git config key `gh-stack.remote`, and instructions for changing or clearing it are printed. The saved remote is checked in `ResolveRemote` after the standard git push config keys (branch.<name>.pushRemote, remote.pushDefault, branch.<name>.remote) but before falling back to listing all remotes. This means per-branch git push configuration still takes precedence, and the --remote flag on individual commands continues to override everything. All commands that resolve a remote (push, submit, sync, rebase, checkout, link, modify, trunk) go through the shared `pickRemote` helper, so they all benefit automatically. Changes: - Add GetSavedRemote, SaveRemote, ClearRemote to the git Ops interface, defaultOps implementation, public wrappers, and MockOps - Check gh-stack.remote in ResolveRemote's priority chain - Move pickRemote from push.go to utils.go as a shared helper - Add save-remote confirmation prompt after interactive remote selection - Add unit tests for pickRemote save/decline/skip/override flows - Add integration tests for ResolveRemote with saved remote and precedence, and for the SaveRemote/GetSavedRemote/ClearRemote lifecycle * add error message for save failure Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> |
||
|
|
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 |
||
|
|
f8c0100602 |
remove silent prefix detection in init (#126)
* Remove silent prefix detection from args path in init When explicit branch names containing slashes were passed to `gh stack init` (e.g. `gh stack init myprefix/branch`), detectPrefix would silently extract the prefix and store it in the stack config. This caused `gh stack add otherbranch` to unexpectedly produce `myprefix/otherbranch` without the user ever opting in. Remove the automatic prefix detection from the args path so that explicit branch names are taken literally. Users who want a prefix should use `--prefix`. The interactive path (no args) continues to prompt for confirmation before setting a prefix. * Remove dead detectPrefix function and its tests After removing the silent prefix detection from the args path, detectPrefix has no production callers. Remove the function and its table-driven unit test to avoid maintaining unused code. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> |
||
|
|
8c2d9e3da6 |
sync: skip trunk fast-forward silently when local branch doesn't exist (#125)
When the trunk branch (e.g. main) doesn't exist locally — only the
remote tracking ref (origin/main) exists — `fastForwardTrunk` called
`git rev-parse main origin/main` which failed, emitting:
⚠ Could not compare trunk main with remote — skipping trunk update
This also caused `stackNeedsRebase` to always return true (since
`IsAncestor("main", ...)` errors out), forcing an unnecessary rebase
and force-push on every sync.
Add a `BranchExists` check at the top of `fastForwardTrunk`. If the
local trunk doesn't exist, return silently — there's nothing to
fast-forward, and the remote tracking ref is sufficient for rebasing
via git's DWIM resolution.
|
||
|
|
7413e8889d |
Fix force-with-lease push for branches without tracking refs (#124)
gh stack sync could rebase a stack successfully then fail the final force push with "stale info" when a branch lacked a local tracking ref (refs/remotes/<remote>/<branch>). This happened because: 1. FetchBranches pre-filtered branches by existing tracking ref, so a branch with no tracking ref was never fetched and never gained one. 2. Push used a bare --force-with-lease flag, which has no lease basis for a branch without a tracking ref, causing git to reject the push. FetchBranches now uses explicit refspecs for every branch: +refs/heads/<branch>:refs/remotes/<remote>/<branch> This creates or updates tracking refs regardless of prior state. The fast-path (single fetch) and per-branch fallback (for branches absent on the remote) are preserved. Push now builds explicit per-branch lease arguments when force=true: --force-with-lease=refs/heads/<branch>:<tracking-ref-sha> for branches with a tracking ref, or: --force-with-lease=refs/heads/<branch>: (empty expected value = "must not exist") for branches absent on the remote. Explicit destination refspecs (<branch>:refs/heads/<branch>) remove dependence on push.default and upstream configuration. The non-force push path is unchanged. Added 6 integration tests using real bare git remotes: - Branch with current tracking ref: push succeeds - Tracking ref deleted locally (regression test for #118): push succeeds - Remote advanced by another client: push rejected (safety preserved) - New branch absent on remote: created via empty-expect lease - New branch race condition: rejected (safety preserved) - Mixed stack (tracked + untracked branches): all succeed after fetch Fixes #118 |
||
|
|
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> |
||
|
|
a4485f5298 |
submit: disable auto-merge on existing PRs before adding to stack (#120)
When a user runs `gh stack submit` and an existing PR is discovered for
a branch via `FindPRForBranch`, that PR may have auto-merge enabled.
Auto-merge is incompatible with stacked PRs because the PR would merge
on its own, breaking the stack's base chain.
Previously, the eligibility guard for auto-merge was only in the `link`
command (which blocks such PRs with an error). The `submit` command had
no such check, allowing users to add auto-merge-enabled PRs to a stack
by running `init` followed by `submit`.
This change adds auto-merge detection and automatic disabling in
`submit`'s `ensurePR` function. When an existing PR with auto-merge
enabled is discovered, the CLI disables auto-merge via the
`disablePullRequestAutoMerge` GraphQL mutation and warns the user.
If the disable call fails, submit continues with a warning (non-fatal).
The `link` command retains its stricter behavior of blocking auto-merge
PRs outright, since the user explicitly chose those PRs and can fix
them before retrying.
Changes:
internal/github/github.go:
- Add DisableAutoMerge() method using the
disablePullRequestAutoMerge GraphQL mutation
internal/github/client_interface.go:
- Add DisableAutoMerge(prID string) error to ClientOps interface
internal/github/mock_client.go:
- Add DisableAutoMergeFn field and mock implementation
cmd/submit.go:
- In ensurePR, after discovering an existing PR with auto-merge
enabled, call DisableAutoMerge before proceeding. Warns on
success ("Disabled auto-merge for PR #N (incompatible with
stacked PRs)") and on failure ("failed to disable auto-merge").
cmd/submit_test.go:
- Add TestSubmit_DisablesAutoMergeOnExistingPR: verifies auto-merge
is disabled and warning is shown
- Add TestSubmit_DisableAutoMergeFailure_ContinuesWithWarning:
verifies submit continues even if the disable call fails
- Add TestSubmit_NoAutoMerge_SkipsDisable: verifies DisableAutoMerge
is not called for PRs without auto-merge
|
||
|
|
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.
|
||
|
|
d2a390f2ff |
link: block merged, closed, queued, and auto-merge-enabled PRs (#112)
The `link` command previously allowed PRs in any state to be added to a
stack, including PRs that had already been merged, were closed, were
sitting in a merge queue, or had auto-merge enabled. Adding such PRs to
a stack is invalid because they have already been or will soon be merged,
which breaks the stacked PR workflow.
Add a new validation phase (Phase 2b) to `runLink` that checks the
eligibility of every existing PR found during lookup, before any new PRs
are created or stack operations are performed. Only open/draft PRs
without auto-merge enabled are eligible. All ineligible PRs are reported
at once with a clear per-PR error message indicating the specific reason
(merged, closed, in merge queue, or auto-merge enabled).
Changes:
internal/github/github.go:
- Add AutoMergeRequest struct and field on PullRequest
- Add IsAutoMergeEnabled() method on *PullRequest
- Update FindPRByNumber and FindPRForBranch GraphQL queries to fetch
the autoMergeRequest field
internal/github/github_test.go:
- Add TestPullRequest_IsAutoMergeEnabled (nil, non-nil, nil receiver)
cmd/link.go:
- Add pr field to resolvedArg to retain full PR data from lookup
- Add validatePREligibility() that rejects merged/closed/queued/
auto-merge-enabled PRs with descriptive error messages
- Wire validation into runLink between PR lookup and stack operations
cmd/link_test.go:
- Add 7 tests covering each disallowed state by PR number and branch
name, plus a multi-invalid-PR reporting test
|
||
|
|
5ed5693ed6 |
Bump github.com/cli/cli/v2 in the go_modules group across 1 directory (#110)
Bumps the go_modules group with 1 update in the / directory: [github.com/cli/cli/v2](https://github.com/cli/cli). Updates `github.com/cli/cli/v2` from 2.92.0 to 2.93.0 - [Release notes](https://github.com/cli/cli/releases) - [Changelog](https://github.com/cli/cli/blob/trunk/docs/release-process-deep-dive.md) - [Commits](https://github.com/cli/cli/compare/v2.92.0...v2.93.0) --- updated-dependencies: - dependency-name: github.com/cli/cli/v2 dependency-version: 2.93.0 dependency-type: direct:production dependency-group: go_modules ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
bf2358bf12 |
trunk command (#108)
* Add `gh stack trunk` navigation command
Add a new navigation command that checks out the trunk branch of the
current stack.
The command is stack-aware: it requires the user to be on a branch that
is part of a stack, loads the stack metadata, and checks out `s.Trunk.Branch`.
If the user is already on the trunk branch, it prints a message and exits
without calling git checkout.
New files:
- cmd/trunk.go: TrunkCmd (cobra command) + runTrunk implementation
- cmd/trunk_test.go: 7 test cases covering happy path, already on
trunk, from top of stack, not in a stack, checkout failure, custom
trunk branch name, and positional argument rejection
Modified files:
- cmd/root.go: register TrunkCmd in the "nav" command group
- README.md: add `gh stack trunk` to the Navigation section
- docs/src/content/docs/reference/cli.md: add `gh stack trunk`
reference section
* address review comments
* increment skill version
v0.0.5
|
||
|
|
49a753708c |
insert branches with modify (#107)
* add insert branch operation to modify TUI
Add `i` (insert below) and `I` (insert above) key bindings to the
interactive modify view, allowing users to insert new empty branches
into an existing stack. This follows Vim-inspired semantics where
lowercase `i` inserts below the cursor and uppercase `I` inserts above.
## TUI behavior
When the user presses `i` or `I`, the TUI enters an insert input mode
(similar to rename mode) where they type a new branch name. The input
is validated against git ref naming rules, local branch uniqueness, and
in-stack name collisions. On confirm, a placeholder node is inserted at
the correct position in the branch list with a green "✚ insert"
annotation badge and green connector styling.
Insert is a structure operation — it works alongside fold, rename, and
drop, but is mutually exclusive with reorder (consistent with existing
mode exclusivity rules). Undo (`z`) removes the inserted node cleanly.
## Apply engine
At apply time (Step 2 in the pipeline, between renames and folds), the
engine creates the new git branch at the parent branch's tip via
`git.CreateBranch` and inserts a `BranchRef` into the stack metadata at
the correct position. If the insertion changes the base of a branch
that has an open PR, `affectsPRs` is set to trigger a required
`gh stack submit` afterward.
## Header shortcut updates
- Combined the fold shortcuts into a single line: `d/u - fold down/up`
- Added insert shortcuts on their own line: `i/I - insert below/above`
- Reordered fold references throughout to list "down" before "up" for
consistency with the insert shortcut ordering
## Files changed
- types.go: ActionInsertBelow/ActionInsertAbove types, IsInserted field,
InsertedBranches in ApplyResult
- model.go: key bindings, insert input mode, undo, mode exclusivity,
annotation, styling, header shortcuts, effective-index tracking to
prevent false reorder detection when inserts shift node positions
- styles.go: green insert badge/branch/connector styles
- status.go: insert counting in pending change summary
- help.go: new "Insert below / above" section, reordered fold heading
- apply.go: BuildPlan and ApplyPlan handle insert actions
- modify.go: updated command description and success summary
- README.md: updated keybindings table
## Test coverage
- 16 new TUI tests: insert below/above, top/bottom edges, undo, mode
exclusivity, merged branch guard, cancel/empty input, duplicate name
validation, pending summary counting, annotation rendering, mixed
operations with drop/fold, apply acceptance
- 4 new apply tests: BuildPlan produces correct insert actions,
ApplyPlan creates branches and updates stack metadata, insert at
stack start uses trunk as parent, affectsPRs triggered when inserting
before a branch with an open PR
* update add error msg to direct users to modify for inserting branches
* docs updates
* fix insert branch bugs in modify TUI
Fix three bugs with the insert branch feature in the modify TUI, and
adjust rename behavior on inserted nodes.
## Bug 1: False "moved" annotations on existing branches
After inserting a branch, all branches below the insertion point
displayed "↕ moved 1 layer down" annotations. This happened because
`nodeAnnotation` and `toNodeData` compared each node's
`OriginalPosition` against its raw array index, which gets shifted
when an inserted node is added to the slice.
Fix: introduce an `effectiveIdx` parameter that counts only
non-inserted nodes, so position comparisons reflect the original
ordering. The View loop computes effective indices by incrementing
only for non-inserted nodes and passes them to the rendering
functions.
## Bug 2: Header branch count inflated by staged inserts
The branch count in the header ("N branches") included inserted
placeholder nodes, making it appear as though the stack had grown
before changes were applied.
Fix: `buildHeaderConfig` now excludes `IsInserted` nodes from the
branch count. The count reflects only the original branches in the
stack.
## Bug 3: Operations allowed on inserted placeholder nodes
Inserted nodes could be folded into other branches, which makes no
sense for a placeholder with no commits. Additionally, the "last
branch" guard counted inserted nodes as active, allowing users to
drop all original branches and bypass the empty-stack check.
Fix:
- `fold()` rejects inserted nodes with a descriptive error message.
- `toggleDrop()` on an inserted node removes it entirely and pops
the original insert action from the undo stack (clean cancellation
rather than a separate undo entry).
- All three "active branch" guards (`toggleDrop`, `fold`, `tryApply`)
now exclude `IsInserted` nodes, ensuring at least one original
branch always remains in the stack.
## Rename on inserted branches
Instead of blocking renames on inserted nodes, pressing `r` now
enters rename mode and updates the insert action's name in place.
The node's `Ref.Branch` and `PendingAction.NewName` are both updated
directly — no separate rename action is created in the undo stack.
This lets users fix a typo without having to drop and re-insert.
## Tests added
- `TestInsertDoesNotShowMovedAnnotation` — verifies no false move
annotations appear on existing branches after an insert
- `TestBranchCountExcludesInserts` — verifies header count stays
stable after insert
- `TestCannotFoldInsertedBranch` — verifies fold is blocked
- `TestCannotRenameInsertedBranch` — verifies rename updates the
insert name in place
- `TestDropInsertedBranchRemovesIt` — verifies drop removes the node
- `TestDropInsertedBranchCanBeUndone` — verifies drop pops the
original insert from the undo stack
- `TestCannotDropAllOriginalBranchesWithInsert` — verifies the
empty-stack guard excludes inserted nodes
* ensure cannot fold into an inserted branch
* rm dead code
* delete inserted branches during abort
|
||
|
|
9cc827dc78 |
modify: only require submit when changes affect PRs (#106)
* modify: only require submit when changes affect PRs Previously, `gh stack modify` always transitioned to `PhasePendingSubmit` after completing on any stack with a remote ID (`s.ID != ""`). This blocked the user from running another `modify` until they ran `gh stack submit`, even when the modifications only touched local branches without PRs. This was overly restrictive. If a user is working at the top of their stack with branches that haven't been pushed or had PRs created yet, restructuring those branches is a purely local operation — there is no remote state to reconcile, and no reason to force a submit before allowing further modifies. ## What changed The condition for entering `PhasePendingSubmit` is now `s.ID != "" && affectsPRs` instead of just `s.ID != ""`. A new `affectsPRs` flag is tracked throughout the apply process. It is set to `true` when any of the following occurs: - A **renamed** branch has a `PullRequest` ref - A **folded** branch (source or target) has a `PullRequest` ref - A **dropped** branch has a `PullRequest` ref - A **rebased** branch (during cascading rebase) has a `PullRequest` ref If none of these conditions are met, the modify state file is cleared immediately — no pending-submit lock, no "run `gh stack submit`" prompt. ## Changes by file **`internal/modify/state.go`** - Added `AffectsPRs bool` field to `StateFile`. This persists the flag across conflict boundaries so that `ContinueApply` knows whether actions applied before the conflict already affected PR branches. **`internal/modify/apply.go`** - `ApplyPlan`: tracks `affectsPRs` through each step (rename, fold, drop, rebase). Saves the flag into conflict state when a conflict occurs. Uses `s.ID != "" && affectsPRs` for the pending-submit decision. - `ContinueApply`: initializes `affectsPRs` from the saved state file, then checks the conflict branch and remaining branches for PRs during the cascading rebase. Uses the same combined condition. - Both functions set `result.NeedsSubmit` / show the "run submit" message only when the flag is true. **`internal/tui/modifyview/types.go`** - Added `NeedsSubmit bool` to `ApplyResult` so the caller can use it for the success message. **`cmd/modify.go`** - `printModifySuccess` now takes its cue from `result.NeedsSubmit` instead of `s.ID != ""`. The "run `gh stack submit`" hint is only shown when PR branches were actually affected. **`internal/modify/apply_test.go`** - Updated `TestApplyPlan_PendingSubmitForRemoteStack` to use branches with PRs and trigger an actual rebase, validating the pending-submit path correctly. - Added `TestApplyPlan_ClearsStateForRemoteStackWithNoPRBranches`: remote stack where no branches have PRs → state is cleared. - Added `TestApplyPlan_PendingSubmitOnlyWhenPRBranchesAffected`: remote stack with a mix of PR and non-PR branches, only the non-PR branch is renamed → state is cleared, `NeedsSubmit` is false. ## Behavior summary | Scenario | Before | After | |---|---|---| | Modify on local stack (no remote ID) | State cleared | State cleared (unchanged) | | Modify on remote stack, PR branches affected | `PhasePendingSubmit` | `PhasePendingSubmit` (unchanged) | | Modify on remote stack, only local branches affected | `PhasePendingSubmit` ❌ | State cleared ✅ | The `CheckStateGuard` function (used by `add`, `push`, `sync`, `unstack`, `rebase`) already did not block on `PhasePendingSubmit`, so those commands are unaffected by this change. * clear state after saving stack * clarify submit requirement in description * assign value directly |
||
|
|
1085b84e40 |
swtich to nearest surviving branch after modify (#105)
* swtich to nearest surviving branch after modify
After `gh stack modify` applies changes, the user may end up on an
orphaned branch that is no longer part of the stack — for example, if
their checked-out branch was dropped, folded into another branch, or
renamed. Previously, the code blindly restored the original branch
regardless of whether it still existed in the stack.
Add a `resolveCheckoutBranch` helper that inspects the modify plan and
the post-modify stack to determine the best branch to check out:
1. Still in stack → keep the original branch (no-op)
2. Renamed → check out the new name
3. Folded down → check out the fold target (branch below)
4. Folded up → check out the fold target (branch above)
5. Dropped → check out the nearest surviving neighbor
(prefer above, fall back to below)
6. Fallback → topmost branch in the stack
Both `ApplyPlan` and `ContinueApply` (the `--continue` path) now use
this helper instead of unconditionally restoring the original branch.
When the resolved branch differs from the original, a message is
printed so the user knows they've been switched.
The resolution uses the pre-modify snapshot (already persisted in the
state file) to determine original adjacency, so it works correctly even
when multiple branches are removed in the same operation.
Includes 12 new tests:
- 9 unit tests for resolveCheckoutBranch covering all action types,
edge cases (topmost dropped, multiple drops, empty stack), and
the fallback path
- 3 integration tests verifying ApplyPlan checks out the correct
branch after drop, fold-down, and rename operations
* handle CheckoutBranch errors
* handle renamed branches
|
||
|
|
743a249f5e |
update add cmd to adopt existing branches (#101)
* Allow `gh stack add` to adopt existing branches Previously, `gh stack add` rejected any branch name that already existed in git with a blanket "branch already exists" error. This was overly restrictive — users who create branches ahead of time (e.g. from the GitHub UI or via `git branch`) had no way to incorporate them into a stack without deleting and recreating them. Now, if the specified branch exists in git but is not part of any existing stack, `add` adopts it: it skips branch creation, checks out the existing branch, and appends it to the stack metadata. This mirrors the adopt-or-create pattern already used by `gh stack init`. Branches that belong to another stack are still rejected by the existing `ValidateNoDuplicateBranch` guard, so there is no risk of cross-stack conflicts. Behavioral summary: - Existing branch, not in any stack → adopted (checkout only, no create) - Existing branch, already in a stack → error (unchanged) - Non-existent branch → created (unchanged) - Staging/commit flags (-A, -u, -m) work with adopted branches Tests added: - TestAdd_AdoptsExistingBranch - TestAdd_RejectsExistingBranchInStack - TestAdd_AdoptsExistingBranchWithCommit * assert for runAdd err in test |
||
|
|
89643dc8db |
input prompter improvements (#98)
* include prefix in branch name input * custom prompter with colored input text * minor fix: arrow direction for initialized stack * fix comment * handle SetTermMode err |
||
|
|
b219e96fb5 |
rebase with preserve dates opt (#96)
* Add --committer-date-is-author-date flag to gh stack rebase
Introduce an opt-in `--committer-date-is-author-date` flag (with
`--preserve-dates` alias) for `gh stack rebase`. The flag is passed
through to every underlying `git rebase` invocation in the cascade,
keeping committer dates equal to author dates so that identical content
rebased onto an identical parent produces stable SHAs. This reduces
spurious force-push notifications and noisy review timelines, especially
in deep stacks where bottom branches get re-rebased on every merge.
Git layer changes:
- Add `RebaseOpts` struct with `CommitterDateIsAuthorDate` field to
`internal/git/gitops.go`
- Update `Ops` interface, `defaultOps`, public wrappers, and
`rebaseContinueOnce`/`tryAutoResolveRebase` helpers to accept and
forward the flag
- Update `MockOps` to match the new signatures
Command layer changes:
- Register `--committer-date-is-author-date` and `--preserve-dates`
flags on the cobra command in `cmd/rebase.go`
- Add `CommitterDateIsAuthorDate` to `cascadeRebaseOpts` and thread it
to all `git.Rebase`/`git.RebaseOnto` calls in `cmd/utils.go`
- Persist the flag in `rebaseState` JSON so `--continue` resumes with
the same behavior; pass it to `RebaseContinue` and subsequent cascade
calls
- Update `internal/modify/apply.go` callers to pass zero-value
`RebaseOpts{}`
Tests:
- Update all existing mock signatures in rebase, sync, and modify tests
- Add tests for flag passthrough, `--preserve-dates` alias, state
round-trip, `--continue` flag restoration, and conflict state
persistence
Docs:
- Update flag tables and examples in README.md and
docs/src/content/docs/reference/cli.md
* clearer wording in docs and help text
|
||
|
|
e7acfc1b16 |
fix for rebase in sync cmd (#95)
* fix: sync performs cascade rebase even when trunk is already up-to-date Previously, `gh stack sync` gated the cascade rebase on whether trunk or stack branches were fast-forwarded during the current run. This meant that if the user had already updated trunk locally (e.g., `git pull`), sync would skip the rebase entirely even though stack branches hadn't been rebased onto the current trunk. This change: - Adds `stackNeedsRebase()` to detect stale branches regardless of whether trunk was updated in this run - Extracts shared helpers (`fastForwardTrunk`, `cascadeRebase`, `resolveOriginalRefs`) from duplicated code in sync.go and rebase.go into utils.go, reducing ~450 lines of duplication - Fixes rebase.go to skip queued branches (was only skipping merged), consistent with sync's behavior via `IsSkipped()` - Refactors rebase --continue to reuse the shared cascade helper Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * address review feedback: error handling and index validation - resolveOriginalRefs now returns (map, error) instead of silently swallowing RevParseMap failures; sync warns and skips rebase, rebase aborts with a clear error - cascadeRebase uses a new Err field on the result struct to distinguish fatal errors (e.g. checkout failure) from recoverable conflicts; callers no longer enter conflict-recovery flow for non-conflict errors - continueRebase validates that remaining branch indices are contiguous in stack order, erroring out if the stack was reordered between conflict and --continue Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> |
||
|
|
54aa630be9 |
Bump github.com/cli/cli/v2 in the go_modules group across 1 directory (#103)
Bumps the go_modules group with 1 update in the / directory: [github.com/cli/cli/v2](https://github.com/cli/cli). Updates `github.com/cli/cli/v2` from 2.86.0 to 2.92.0 - [Release notes](https://github.com/cli/cli/releases) - [Changelog](https://github.com/cli/cli/blob/trunk/docs/release-process-deep-dive.md) - [Commits](https://github.com/cli/cli/compare/v2.86.0...v2.92.0) --- updated-dependencies: - dependency-name: github.com/cli/cli/v2 dependency-version: 2.92.0 dependency-type: direct:production dependency-group: go_modules ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
d3be1b577e |
prune merged branches (#94)
* prune merged branches * interactively prompt for prune * delete remote tracking ref too * disable selecting merged branches in TUIs * include full list (including merged PRs) in PUT request to stacks API * add prune to docs * addressing review comments * increment skill file versionv0.0.4 |
||
|
|
650a94621d |
docs on rebase workflow (#93)
* docs on rebase workflow * highlight commit signing callout |
||
|
|
d115ca0570 |
support multiple branches during init (#91)
* implicitly adopt branches in init * deprecate adopt flag * update docs * address review comments |
||
|
|
00a9589feb | rm merge command (#89) | ||
|
|
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> |
||
|
|
d159b80057 |
Bump devalue in /docs in the npm_and_yarn group across 1 directory (#90)
Bumps the npm_and_yarn group with 1 update in the /docs directory: [devalue](https://github.com/sveltejs/devalue). Updates `devalue` from 5.6.4 to 5.8.1 - [Release notes](https://github.com/sveltejs/devalue/releases) - [Changelog](https://github.com/sveltejs/devalue/blob/main/CHANGELOG.md) - [Commits](https://github.com/sveltejs/devalue/compare/v5.6.4...v5.8.1) --- updated-dependencies: - dependency-name: devalue dependency-version: 5.8.1 dependency-type: indirect dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
05de7e19b3 |
Bump astro in /docs in the npm_and_yarn group across 1 directory (#86)
Bumps the npm_and_yarn group with 1 update in the /docs directory: [astro](https://github.com/withastro/astro/tree/HEAD/packages/astro). Updates `astro` from 6.1.8 to 6.3.1 - [Release notes](https://github.com/withastro/astro/releases) - [Changelog](https://github.com/withastro/astro/blob/main/packages/astro/CHANGELOG.md) - [Commits](https://github.com/withastro/astro/commits/astro@6.3.1/packages/astro) --- updated-dependencies: - dependency-name: astro dependency-version: 6.3.1 dependency-type: direct:production dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |