@@ -292,7 +292,7 @@ func checkoutRemoteStackByBranch(cfg *config.Config, sf *stack.StackFile, gitDir
292292 if len (matches ) > 1 {
293293 stackNumbers := make ([]string , len (matches ))
294294 for i , match := range matches {
295- stackNumbers [i ] = strconv .Itoa (match .Number )
295+ stackNumbers [i ] = cfg . Link ( strconv .Itoa (match .Number ), cfg . StackURL ( match . Number ) )
296296 }
297297 cfg .Errorf ("branch %q belongs to multiple stacks on GitHub (%s)" ,
298298 branch , strings .Join (stackNumbers , ", " ))
@@ -384,9 +384,10 @@ func checkoutStackByNumber(cfg *config.Config, sf *stack.StackFile, gitDir strin
384384 }
385385
386386 remoteStack , err := client .GetStack (stackNumber )
387- if err != nil || remoteStack == nil || len (remoteStack .PullRequests ) == 0 {
388- // No such stack, stacks unavailable, or a transient failure — let the
389- // caller try the number as a PR number or branch name.
387+ if err != nil {
388+ return nil , "" , errors .Join (errStackNumberNotFound , err )
389+ }
390+ if remoteStack == nil || len (remoteStack .PullRequests ) == 0 {
390391 return nil , "" , errStackNumberNotFound
391392 }
392393
@@ -474,7 +475,7 @@ func reconcileAndImportRemoteStack(cfg *config.Config, client github.ClientOps,
474475 if err := stack .Save (gitDir , sf ); err != nil {
475476 return nil , "" , stackSaveError (cfg , err )
476477 }
477- cfg .Successf ("Local stack matches remote%s" , stackLabel ( remoteStack .Number ))
478+ cfg .Successf ("Local stack matches remote%s" , linkedStackLabel ( cfg , remoteStack .Number ))
478479 return localStack , targetBranch , nil
479480 }
480481
@@ -712,7 +713,7 @@ func importRemoteStack(
712713 // Update base SHAs from actual local refs
713714 updateBaseSHAs (s )
714715
715- cfg .Successf ("Imported stack with %d branches from GitHub%s" , len (prs ), stackLabel ( remoteStackNumber ))
716+ cfg .Successf ("Imported stack with %d branches from GitHub%s" , len (prs ), linkedStackLabel ( cfg , remoteStackNumber ))
716717 return s , nil
717718}
718719
@@ -748,7 +749,7 @@ func interactiveCheckout(cfg *config.Config, sf *stack.StackFile, gitDir string)
748749 return nil , "" , fmt .Errorf ("no target specified; provide a branch name or PR number, or run interactively to select a stack" )
749750 }
750751
751- rows , remoteStacks := gatherCheckoutRows (cfg , sf )
752+ rows , remoteStacks , links := gatherCheckoutRows (cfg , sf )
752753 if currentBranch , branchErr := git .CurrentBranch (); branchErr == nil {
753754 stackNumber , confirmed , confirmErr := offerRemoteStackForBranch (cfg , sf , remoteStacks , currentBranch )
754755 if confirmErr != nil {
@@ -772,7 +773,7 @@ func interactiveCheckout(cfg *config.Config, sf *stack.StackFile, gitDir string)
772773 return nil , "" , nil
773774 }
774775
775- selected , ok , err := launchCheckoutPicker (rows )
776+ selected , ok , err := launchCheckoutPicker (rows , links )
776777 if err != nil {
777778 return nil , "" , err
778779 }
@@ -789,14 +790,29 @@ func interactiveCheckout(cfg *config.Config, sf *stack.StackFile, gitDir string)
789790// with the local stacks into the picker's rows. Any GitHub failure (stacks not
790791// enabled for the repo, no auth, network error) gracefully degrades to a
791792// local-only list.
792- func gatherCheckoutRows (cfg * config.Config , sf * stack.StackFile ) ([]checkoutview.StackRow , []github.RemoteStack ) {
793+ func gatherCheckoutRows (cfg * config.Config , sf * stack.StackFile ) ([]checkoutview.StackRow , []github.RemoteStack , map [ int ] string ) {
793794 var remote []github.RemoteStack
795+ var remoteNumbers map [int ]bool // nil means the remote listing was unavailable
794796 if client , err := cfg .GitHubClient (); err == nil {
795797 if stacks , err := client .ListStacks (); err == nil {
796798 remote = stacks
799+ remoteNumbers = make (map [int ]bool , len (stacks ))
800+ for _ , s := range stacks {
801+ remoteNumbers [s .Number ] = true
802+ }
797803 }
798804 }
799- return checkoutview .BuildRows (sf .Stacks , remote ), remote
805+ rows := checkoutview .BuildRows (sf .Stacks , remote )
806+ links := make (map [int ]string )
807+ if repo , err := cfg .Repo (); err == nil && repo .Owner != "" && repo .Name != "" {
808+ for _ , row := range rows {
809+ if row .Number > 0 && (remoteNumbers == nil || remoteNumbers [row .Number ]) {
810+ url := github .StackURL (repo .Host , repo .Owner , repo .Name , row .Number )
811+ links [row .Number ] = cfg .TerminalLink (row .NumberDisplay (), url )
812+ }
813+ }
814+ }
815+ return rows , remote , links
800816}
801817
802818// offerRemoteStackForBranch asks to check out the unique active remote stack
@@ -814,7 +830,8 @@ func offerRemoteStackForBranch(cfg *config.Config, sf *stack.StackFile, remote [
814830 }
815831
816832 stackNumber := matches [0 ].Number
817- prompt := fmt .Sprintf ("Found stack #%d that includes branch %q. Check out stack #%d?" , stackNumber , branch , stackNumber )
833+ cfg .Infof ("Found stack %s that includes branch %q." , cfg .StackLink (stackNumber ), branch )
834+ prompt := fmt .Sprintf ("Check out stack #%d?" , stackNumber )
818835 confirmed , err := confirmRemoteStackCheckout (cfg , prompt )
819836 if err != nil {
820837 if errors .Is (err , errInterrupt ) {
@@ -886,7 +903,11 @@ func resolveCheckoutSelection(cfg *config.Config, sf *stack.StackFile, gitDir st
886903 s , targetBranch , err := checkoutStackByNumber (cfg , sf , gitDir , selected .Number )
887904 if err != nil {
888905 if errors .Is (err , errStackNumberNotFound ) {
889- cfg .Errorf ("stack #%d could not be loaded from GitHub" , selected .Number )
906+ label := fmt .Sprintf ("#%d" , selected .Number )
907+ if ! isNotFound (err ) {
908+ label = cfg .StackLink (selected .Number )
909+ }
910+ cfg .Errorf ("stack %s could not be loaded from GitHub" , label )
890911 return nil , "" , ErrAPIFailure
891912 }
892913 return nil , "" , err
@@ -899,8 +920,8 @@ func resolveCheckoutSelection(cfg *config.Config, sf *stack.StackFile, gitDir st
899920// picker occupies only a few lines and leaves the surrounding terminal output
900921// intact. Mouse motion is intentionally not enabled so the search field never
901922// receives stray mouse bytes.
902- func launchCheckoutPicker (rows []checkoutview.StackRow ) (checkoutview.StackRow , bool , error ) {
903- p := tea .NewProgram (checkoutview .New (rows ))
923+ func launchCheckoutPicker (rows []checkoutview.StackRow , links map [ int ] string ) (checkoutview.StackRow , bool , error ) {
924+ p := tea .NewProgram (checkoutview .New (rows ). WithNumberLinks ( links ) )
904925 finalModel , err := p .Run ()
905926 if err != nil {
906927 return checkoutview.StackRow {}, false , fmt .Errorf ("running stack picker: %w" , err )
0 commit comments