cli/pkg/cmd/agent-task/list/list.go
Babak K. Shandiz 7824b43f8a
refactor(agent-task list): simplify cmd initialisation
Signed-off-by: Babak K. Shandiz <babakks@github.com>
2025-09-04 21:06:38 +01:00

168 lines
4.3 KiB
Go

package list
import (
"context"
"fmt"
"time"
"github.com/cli/cli/v2/internal/browser"
"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"
"github.com/cli/cli/v2/pkg/cmdutil"
"github.com/cli/cli/v2/pkg/iostreams"
"github.com/spf13/cobra"
)
const defaultLimit = 30
// ListOptions are the options for the list command
type ListOptions struct {
IO *iostreams.IOStreams
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,
CapiClient: shared.CapiClientFunc(f),
Limit: defaultLimit,
Browser: f.Browser,
}
cmd := &cobra.Command{
Use: "list",
Short: "List agent tasks",
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
// Support -R/--repo override
opts.BaseRepo = f.BaseRepo
if opts.Limit < 1 {
return cmdutil.FlagErrorf("invalid limit: %v", opts.Limit)
}
if runF != nil {
return runF(opts)
}
return listRun(opts)
},
}
cmdutil.EnableRepoOverride(cmd, f)
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")
return cmd
}
func listRun(opts *ListOptions) error {
if opts.Web {
// Currently the web GUI does not have a page that supports filtering
// based on repo, so we just open the agents dashboard with no args.
// If that page is ever added in the future, we should route to that
// page instead of the global one when --repo is set.
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
}
capiClient, err := opts.CapiClient()
if err != nil {
return err
}
opts.IO.StartProgressIndicatorWithLabel("Fetching agent tasks...")
defer opts.IO.StopProgressIndicator()
var sessions []*capi.Session
ctx := context.Background()
var repo ghrepo.Interface
if opts.BaseRepo != nil {
// We swallow this error because when CWD is not a repo and
// the --repo flag is not set, we use the global/user session listing.
repo, _ = opts.BaseRepo()
}
if repo != nil && repo.RepoOwner() != "" && repo.RepoName() != "" {
sessions, err = capiClient.ListSessionsForRepo(ctx, repo.RepoOwner(), repo.RepoName(), opts.Limit)
if err != nil {
return err
}
} else {
sessions, err = capiClient.ListSessionsForViewer(ctx, opts.Limit)
if err != nil {
return err
}
}
opts.IO.StopProgressIndicator()
if len(sessions) == 0 {
return cmdutil.NewNoResultsError("no agent tasks found")
}
if err := opts.IO.StartPager(); err == nil {
defer opts.IO.StopPager()
} else {
fmt.Fprintf(opts.IO.ErrOut, "error starting pager: %v\n", err)
}
cs := opts.IO.ColorScheme()
tp := tableprinter.New(opts.IO, tableprinter.WithHeader("Session ID", "Pull Request", "Repo", "Session State", "Created"))
for _, s := range sessions {
if s.ResourceType != "pull" || s.PullRequest == nil || s.PullRequest.Repository == nil {
// Skip these sessions in case they happen, for now.
continue
}
pr := fmt.Sprintf("#%d", s.PullRequest.Number)
repo := s.PullRequest.Repository.NameWithOwner
// ID
tp.AddField(s.ID)
if tp.IsTTY() {
tp.AddField(pr, tableprinter.WithColor(cs.ColorFromString(prShared.ColorForPRState(*s.PullRequest))))
} else {
tp.AddField(pr)
}
// Repo
tp.AddField(repo, tableprinter.WithColor(cs.Muted))
// State
if tp.IsTTY() {
tp.AddField(s.State, tableprinter.WithColor(shared.ColorFuncForSessionState(*s, cs)))
} else {
tp.AddField(s.State)
}
// Created
if tp.IsTTY() {
tp.AddTimeField(time.Now(), s.CreatedAt, cs.Muted)
} else {
tp.AddField(s.CreatedAt.Format(time.RFC3339))
}
tp.EndRow()
}
if err := tp.Render(); err != nil {
return err
}
return nil
}