diff --git a/pkg/cmd/agent-task/list/list.go b/pkg/cmd/agent-task/list/list.go index 43f409ffe..d2e717b72 100644 --- a/pkg/cmd/agent-task/list/list.go +++ b/pkg/cmd/agent-task/list/list.go @@ -5,9 +5,11 @@ import ( "fmt" "time" + "github.com/cli/cli/v2/internal/browser" "github.com/cli/cli/v2/internal/gh" "github.com/cli/cli/v2/internal/ghrepo" "github.com/cli/cli/v2/internal/tableprinter" + "github.com/cli/cli/v2/internal/text" "github.com/cli/cli/v2/pkg/cmd/agent-task/capi" "github.com/cli/cli/v2/pkg/cmd/agent-task/shared" prShared "github.com/cli/cli/v2/pkg/cmd/pr/shared" @@ -25,14 +27,17 @@ type ListOptions struct { Limit int CapiClient func() (*capi.CAPIClient, error) BaseRepo func() (ghrepo.Interface, error) + Web bool + Browser browser.Browser } // NewCmdList creates the list command func NewCmdList(f *cmdutil.Factory, runF func(*ListOptions) error) *cobra.Command { opts := &ListOptions{ - IO: f.IOStreams, - Config: f.Config, - Limit: defaultLimit, + IO: f.IOStreams, + Config: f.Config, + Limit: defaultLimit, + Browser: f.Browser, } cmd := &cobra.Command{ @@ -59,6 +64,7 @@ func NewCmdList(f *cmdutil.Factory, runF func(*ListOptions) error) *cobra.Comman } cmd.Flags().IntVarP(&opts.Limit, "limit", "L", defaultLimit, fmt.Sprintf("Maximum number of agent tasks to fetch (default %d)", defaultLimit)) + cmd.Flags().BoolVarP(&opts.Web, "web", "w", false, "Open agent tasks in the browser") opts.CapiClient = func() (*capi.CAPIClient, error) { cfg, err := opts.Config() @@ -77,6 +83,14 @@ func NewCmdList(f *cmdutil.Factory, runF func(*ListOptions) error) *cobra.Comman } func listRun(opts *ListOptions) error { + if opts.Web { + const webURL = "https://github.com/copilot/agents" + if opts.IO.IsStdoutTTY() { + fmt.Fprintf(opts.IO.ErrOut, "Opening %s in your browser.\n", text.DisplayURL(webURL)) + } + return opts.Browser.Browse(webURL) + } + if opts.Limit <= 0 { opts.Limit = defaultLimit } diff --git a/pkg/cmd/agent-task/list/list_test.go b/pkg/cmd/agent-task/list/list_test.go index f05d61b9e..fc45b34d2 100644 --- a/pkg/cmd/agent-task/list/list_test.go +++ b/pkg/cmd/agent-task/list/list_test.go @@ -8,6 +8,7 @@ import ( "time" "github.com/MakeNowJust/heredoc" + "github.com/cli/cli/v2/internal/browser" "github.com/cli/cli/v2/internal/config" "github.com/cli/cli/v2/internal/gh" "github.com/cli/cli/v2/internal/ghrepo" @@ -44,6 +45,14 @@ func TestNewCmdList(t *testing.T) { args: "--limit 0", wantErr: "invalid limit: 0", }, + { + name: "web flag", + args: "--web", + wantOpts: ListOptions{ + Limit: defaultLimit, + Web: true, + }, + }, } for _, tt := range tests { @@ -62,6 +71,7 @@ func TestNewCmdList(t *testing.T) { } require.NoError(t, err) assert.Equal(t, tt.wantOpts.Limit, gotOpts.Limit) + assert.Equal(t, tt.wantOpts.Web, gotOpts.Web) }) } } @@ -72,14 +82,17 @@ func Test_listRun(t *testing.T) { createdAt := sixHoursAgo.Format(time.RFC3339) tests := []struct { - name string - tty bool - stubs func(*httpmock.Registry) - baseRepo ghrepo.Interface - baseRepoErr error - limit int - wantOut string - wantErr error + name string + tty bool + stubs func(*httpmock.Registry) + baseRepo ghrepo.Interface + baseRepoErr error + limit int + web bool + wantOut string + wantErr error + wantStderr string + wantBrowserURL string }{ { name: "no sessions", @@ -166,6 +179,14 @@ func Test_listRun(t *testing.T) { r6 #306 OWNER/REPO mystery about 6 hours ago `), }, + { + name: "web mode", + tty: true, + web: true, + wantOut: "", + wantStderr: "Opening https://github.com/copilot/agents in your browser.\n", + wantBrowserURL: "https://github.com/copilot/agents", + }, } for _, tt := range tests { @@ -179,16 +200,28 @@ func Test_listRun(t *testing.T) { cfg.Set("github.com", "oauth_token", "OTOKEN") authCfg := cfg.Authentication() - ios, _, stdout, _ := iostreams.Test() + ios, _, stdout, stderr := iostreams.Test() ios.SetStdoutTTY(tt.tty) + var br *browser.Stub + if tt.web { + br = &browser.Stub{} + } + httpClient := &http.Client{Transport: reg} capiClient := capi.NewCAPIClient(httpClient, authCfg) opts := &ListOptions{ - IO: ios, - Config: func() (gh.Config, error) { return cfg, nil }, - Limit: tt.limit, - CapiClient: func() (*capi.CAPIClient, error) { return capiClient, nil }, + IO: ios, + Config: func() (gh.Config, error) { return cfg, nil }, + Limit: tt.limit, + Web: tt.web, + Browser: br, + CapiClient: func() (*capi.CAPIClient, error) { + if tt.web { + panic("CapiClient should not be invoked when --web is set") + } + return capiClient, nil + }, } if tt.baseRepo != nil || tt.baseRepoErr != nil { baseRepo := tt.baseRepo @@ -205,6 +238,14 @@ func Test_listRun(t *testing.T) { } got := stdout.String() require.Equal(t, tt.wantOut, got) + if tt.wantStderr != "" { + require.Equal(t, tt.wantStderr, stderr.String()) + } else { + require.Equal(t, "", stderr.String()) + } + if tt.web { + br.Verify(t, tt.wantBrowserURL) + } reg.Verify(t) }) }