ronantakizawa/codereview-bench huggingface dataset:
Repository: cli/cli
File: pkg/cmd/release/shared/fetch_test.go (Go)

Code before:
			name:           "empty response body with 200 status",
			tagName:        "v1.0.0",
			responseStatus: 200,
			responseBody:   `{}`,
			errorMessage:   "release not found",
		},
		{
			name:           "malformed JSON response",
			tagName:        "v1.0.0",
			responseStatus: 200,
			responseBody:   `{"object": {"sha":`,
			errorMessage:   "failed to parse Git ref response: unexpected EOF",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			fakeHTTP := &httpmock.Registry{}
			defer fakeHTTP.Verify(t)

			repo, err := ghrepo.FromFullName("owner/repo")
			require.NoError(t, err)

			path := "repos/owner/repo/git/ref/tags/" + tt.tagName
			if tt.responseStatus == 404 {
				fakeHTTP.Register(httpmock.REST("GET", path), httpmock.StatusStringResponse(404, "Not Found"))
			} else {
				fakeHTTP.Register(httpmock.REST("GET", path), httpmock.StringResponse(tt.responseBody))
			}

			httpClient := &http.Client{Transport: fakeHTTP}
			ctx := context.Background()

			sha, err := FetchRefSHA(ctx, httpClient, repo, tt.tagName)

			if tt.errorMessage != "" {
				require.Error(t, err)
				assert.EqualError(t, err, tt.errorMessage)
			} else {
				require.NoError(t, err)
				assert.Equal(t, tt.expectedSHA, sha)
			}
		})
	}
}


Review comment (refactor):
The response body is actually like this:

```json
{
  "message": "Not Found",
  "documentation_url": "https://docs.github.com/rest/git/refs#get-a-reference",
  "status": "404"
}
```

You can use `httpmock.JSONErrorResponse` helper function to generate a valid API error value.

Code after:
			defer fakeHTTP.Verify(t)

			repo, err := ghrepo.FromFullName("owner/repo")
			require.NoError(t, err)

			path := "repos/owner/repo/git/ref/tags/" + tt.tagName
			if tt.responseStatus == 404 || tt.responseStatus == 500 {
				fakeHTTP.Register(
					httpmock.REST("GET", path),
					httpmock.JSONErrorResponse(tt.responseStatus, api.HTTPError{
						StatusCode: tt.responseStatus,
						Message:    tt.responseMessage,
					}),
				)
			} else {
				fakeHTTP.Register(
					httpmock.REST("GET", path),
					httpmock.StatusStringResponse(tt.responseStatus, tt.responseBody),
				)
			}

			httpClient := &http.Client{Transport: fakeHTTP}
			ctx := context.Background()

			sha, err := FetchRefSHA(ctx, httpClient, repo, tt.tagName)

			if tt.errorMessage != "" {
				assert.Contains(t, err.Error(), tt.errorMessage)
				assert.Empty(t, sha)
			} else {
				require.NoError(t, err)
				assert.Equal(t, tt.expectedSHA, sha)
			}
		})
	}
}
Repository: cli/cli
File: internal/featuredetection/feature_detection.go (Go)

Code before:
	if err != nil {
		return features, err
	}

	for _, field := range featureDetection.Repository.Fields {
		if field.Name == "pullRequestTemplates" {
			features.PullRequestTemplateQuery = true
		}
		if field.Name == "visibility" {
			features.VisibilityField = true
		}
		if field.Name == "autoMergeAllowed" {
			features.AutoMerge = true
		}
	}

	return features, nil
}

const (
	enterpriseProjectsV1Removed = "3.17.0"
)

func (d *detector) ProjectsV1() gh.ProjectsV1Support {
	// Currently, projects v1 support is entirely dependent on the host. As this is deprecated in GHES,
	// we will do feature detection on whether the GHES version has support.
	if !ghauth.IsEnterprise(d.host) {
		return gh.ProjectsV1Unsupported
	}

	hostVersion, hostVersionErr := resolveEnterpriseVersion(d.httpClient, d.host)
	v1ProjectCutoffVersion, v1ProjectCutoffVersionErr := version.NewVersion(enterpriseProjectsV1Removed)

	if hostVersionErr == nil && v1ProjectCutoffVersionErr == nil && hostVersion.LessThan(v1ProjectCutoffVersion) {
		return gh.ProjectsV1Supported
	}

	return gh.ProjectsV1Unsupported
}

func resolveEnterpriseVersion(httpClient *http.Client, host string) (*version.Version, error) {
	var metaResponse struct {
		InstalledVersion string `json:"installed_version"`
	}

	apiClient := api.NewClientFromHTTP(httpClient)
	err := apiClient.REST(host, "GET", "meta", nil, &metaResponse)
	if err != nil {
		return nil, err
	}


Review comment (refactor):
Suggestion:

Could this say something like:

```
GitHub.com does not support V1 projects, but certain versions of enterprise server do. We detect compatibility based on the last enterprise server version.
```

I could also see an argument to just remove this comment also 🤷 

Code after:
	if err != nil {
		return features, err
	}

	for _, field := range featureDetection.Repository.Fields {
		if field.Name == "pullRequestTemplates" {
			features.PullRequestTemplateQuery = true
		}
		if field.Name == "visibility" {
			features.VisibilityField = true
		}
		if field.Name == "autoMergeAllowed" {
			features.AutoMerge = true
		}
	}

	return features, nil
}

const (
	enterpriseProjectsV1Removed = "3.17.0"
)

func (d *detector) ProjectsV1() gh.ProjectsV1Support {
	if !ghauth.IsEnterprise(d.host) {
		return gh.ProjectsV1Unsupported
	}

	hostVersion, hostVersionErr := resolveEnterpriseVersion(d.httpClient, d.host)
	v1ProjectCutoffVersion, v1ProjectCutoffVersionErr := version.NewVersion(enterpriseProjectsV1Removed)

	if hostVersionErr == nil && v1ProjectCutoffVersionErr == nil && hostVersion.LessThan(v1ProjectCutoffVersion) {
		return gh.ProjectsV1Supported
	}

	return gh.ProjectsV1Unsupported
}

func resolveEnterpriseVersion(httpClient *http.Client, host string) (*version.Version, error) {
	var metaResponse struct {
		InstalledVersion string `json:"installed_version"`
	}

	apiClient := api.NewClientFromHTTP(httpClient)
	err := apiClient.REST(host, "GET", "meta", nil, &metaResponse)
	if err != nil {
		return nil, err
	}

	return version.NewVersion(metaResponse.InstalledVersion)
}
Repository: cli/cli
File: pkg/cmd/run/cancel/cancel.go (Go)

Code before:

	cmd := &cobra.Command{
		Use:   "cancel [<run-id>]",
		Short: "Cancel a workflow run",
		Args:  cobra.MaximumNArgs(1),
		RunE: func(cmd *cobra.Command, args []string) error {
			// support `-R, --repo` override
			opts.BaseRepo = f.BaseRepo

			if len(args) > 0 {
				opts.RunID = args[0]
			} else if !opts.IO.CanPrompt() {
				return cmdutil.FlagErrorf("run ID required when not running interactively")
			} else {
				opts.Prompt = true
			}

			if runF != nil {
				return runF(opts)
			}

			return runCancel(opts)
		},
	}

	cmd.Flags().BoolVarP(&opts.Force, "force", "f", false, "Force cancel a workflow run when it doesn't respond to regular cancel")

	return cmd
}

func runCancel(opts *CancelOptions) error {
	if opts.RunID != "" {
		_, err := strconv.Atoi(opts.RunID)
		if err != nil {
			return fmt.Errorf("invalid run-id %#v", opts.RunID)
		}
	}
	httpClient, err := opts.HttpClient()
	if err != nil {
		return fmt.Errorf("failed to create http client: %w", err)
	}
	client := api.NewClientFromHTTP(httpClient)

	cs := opts.IO.ColorScheme()

	repo, err := opts.BaseRepo()
	if err != nil {
		return fmt.Errorf("failed to determine base repo: %w", err)
	}

	runID := opts.RunID

Review comment (suggestion):
Suggestion: I think this should be less prescriptive about when to use it. 🙏 

```suggestion
	cmd.Flags().BoolVarP(&opts.Force, "force", "f", false, "Force cancel a workflow run")
```

Code after:

	cmd := &cobra.Command{
		Use:   "cancel [<run-id>]",
		Short: "Cancel a workflow run",
		Args:  cobra.MaximumNArgs(1),
		RunE: func(cmd *cobra.Command, args []string) error {
			// support `-R, --repo` override
			opts.BaseRepo = f.BaseRepo

			if len(args) > 0 {
				opts.RunID = args[0]
			} else if !opts.IO.CanPrompt() {
				return cmdutil.FlagErrorf("run ID required when not running interactively")
			} else {
				opts.Prompt = true
			}

			if runF != nil {
				return runF(opts)
			}

			return runCancel(opts)
		},
	}

	cmd.Flags().BoolVar(&opts.Force, "force", false, "Force cancel a workflow run")

	return cmd
}

func runCancel(opts *CancelOptions) error {
	if opts.RunID != "" {
		_, err := strconv.Atoi(opts.RunID)
		if err != nil {
			return fmt.Errorf("invalid run-id %#v", opts.RunID)
		}
	}
	httpClient, err := opts.HttpClient()
	if err != nil {
		return fmt.Errorf("failed to create http client: %w", err)
	}
	client := api.NewClientFromHTTP(httpClient)

	cs := opts.IO.ColorScheme()

	repo, err := opts.BaseRepo()
	if err != nil {
		return fmt.Errorf("failed to determine base repo: %w", err)
	}

	runID := opts.RunID
Repository: cli/cli
File: pkg/cmd/repo/delete/delete_test.go (Go)

Code before:
			input:  "OWNER/REPO --yes",
			output: DeleteOptions{RepoArg: "OWNER/REPO", Confirmed: true},
		},
		{
			name:    "no confirmation notty",
			input:   "OWNER/REPO",
			output:  DeleteOptions{RepoArg: "OWNER/REPO"},
			wantErr: true,
			errMsg:  "--yes required when not running interactively",
		},
		{
			name:   "base repo resolution",
			input:  "",
			tty:    true,
			output: DeleteOptions{},
		},
		{
			name:   "yes flag ignored when no argument tty",
			tty:    true,
			input:  "--yes",
			output: DeleteOptions{Confirmed: false}, // --yes should be ignored
		},
		{
			name:    "yes flag error when no argument notty",
			input:   "--yes",
			output:  DeleteOptions{},
			wantErr: true,
			errMsg:  "cannot non-interactively delete current repository. Please specify a repository or run interactively",
		},
		{
			name:   "confirm flag also ignored when no argument tty",
			tty:    true,
			input:  "--confirm",
			output: DeleteOptions{Confirmed: false}, // --confirm should also be ignored
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			ios, _, _, _ := iostreams.Test()
			ios.SetStdinTTY(tt.tty)
			ios.SetStdoutTTY(tt.tty)
			f := &cmdutil.Factory{
				IOStreams: ios,
			}
			argv, err := shlex.Split(tt.input)
			assert.NoError(t, err)
			var gotOpts *DeleteOptions
			cmd := NewCmdDelete(f, func(opts *DeleteOptions) error {
				gotOpts = opts
				return nil
			})

Review comment (suggestion):
@copilot this line is not needed.


```suggestion
```

Code after:
			input:  "OWNER/REPO --yes",
			output: DeleteOptions{RepoArg: "OWNER/REPO", Confirmed: true},
		},
		{
			name:    "no confirmation notty",
			input:   "OWNER/REPO",
			output:  DeleteOptions{RepoArg: "OWNER/REPO"},
			wantErr: true,
			errMsg:  "--yes required when not running interactively",
		},
		{
			name:   "base repo resolution",
			input:  "",
			tty:    true,
			output: DeleteOptions{},
		},
		{
			name:   "yes flag ignored when no argument tty",
			tty:    true,
			input:  "--yes",
			output: DeleteOptions{Confirmed: false}, // --yes should be ignored
		},
		{
			name:    "yes flag error when no argument notty",
			input:   "--yes",
			wantErr: true,
			errMsg:  "cannot non-interactively delete current repository. Please specify a repository or run interactively",
		},
		{
			name:    "confirm flag error when no argument notty",
			input:   "--confirm",
			wantErr: true,
			errMsg:  "cannot non-interactively delete current repository. Please specify a repository or run interactively",
		},
		{
			name:   "confirm flag also ignored when no argument tty",
			tty:    true,
			input:  "--confirm",
			output: DeleteOptions{Confirmed: false}, // --confirm should also be ignored
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			ios, _, _, _ := iostreams.Test()
			ios.SetStdinTTY(tt.tty)
			ios.SetStdoutTTY(tt.tty)
			f := &cmdutil.Factory{
				IOStreams: ios,
			}
			argv, err := shlex.Split(tt.input)
			assert.NoError(t, err)
Repository: cli/cli
File: pkg/cmd/agent-task/list/list.go (Go)

Code before:
	Config     func() (gh.Config, error)
	Limit      int
	CapiClient func() (*capi.CAPIClient, error)
	BaseRepo   func() (ghrepo.Interface, error)
}

// 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,
	}

	cmd := &cobra.Command{
		Use:   "list",
		Short: "List agent tasks",
		Args:  cobra.NoArgs,
		RunE: func(cmd *cobra.Command, args []string) error {
			// Support -R/--repo override
			if f != nil {
				opts.BaseRepo = f.BaseRepo
			}
			if runF != nil {
				return runF(opts)
			}
			return listRun(opts)
		},
	}

	if f != nil {
		cmdutil.EnableRepoOverride(cmd, f)
	}

	opts.CapiClient = func() (*capi.CAPIClient, error) {
		cfg, err := opts.Config()
		if err != nil {
			return nil, err
		}
		httpClient, err := f.HttpClient()
		if err != nil {
			return nil, err
		}
		authCfg := cfg.Authentication()
		return capi.NewCAPIClient(httpClient, authCfg), nil
	}

	return cmd
}

func listRun(opts *ListOptions) error {

Review comment (suggestion):
Also, the limit should not be negative. We can either use `cmd.Flags().UintVarP` or just check for negative values here. I'd personally go with the former, which also requires us to either update the underlying `limit` parameter type in `CapiClient` methods, or just do a type conversion (i.e. `uint(limit)`) at the calling time.

I'm fine either way, so it's up to you.

Code after:
	Config     func() (gh.Config, error)
	Limit      int
	CapiClient func() (*capi.CAPIClient, error)
	BaseRepo   func() (ghrepo.Interface, error)
}

// 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,
	}

	cmd := &cobra.Command{
		Use:   "list",
		Short: "List agent tasks",
		Args:  cobra.NoArgs,
		RunE: func(cmd *cobra.Command, args []string) error {
			// Support -R/--repo override
			if f != nil {
				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)
		},
	}

	if f != nil {
		cmdutil.EnableRepoOverride(cmd, f)
	}

	cmd.Flags().IntVarP(&opts.Limit, "limit", "L", defaultLimit, fmt.Sprintf("Maximum number of agent tasks to fetch (default %d)", defaultLimit))

	opts.CapiClient = func() (*capi.CAPIClient, error) {
		cfg, err := opts.Config()
		if err != nil {
			return nil, err
		}
		httpClient, err := f.HttpClient()
		if err != nil {
			return nil, err
		}
		authCfg := cfg.Authentication()
		return capi.NewCAPIClient(httpClient, authCfg), nil
	}
Repository: cli/cli
File: pkg/cmd/agent-task/list/list_test.go (Go)

Code before:
				tt.stubs(reg)
			}

			cfg := config.NewBlankConfig()
			cfg.Set("github.com", "oauth_token", "OTOKEN")
			authCfg := cfg.Authentication()

			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,
				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
				baseRepoErr := tt.baseRepoErr
				opts.BaseRepo = func() (ghrepo.Interface, error) { return baseRepo, baseRepoErr }
			}

			err := listRun(opts)
			if tt.wantErr != nil {
				assert.Error(t, err)
				require.EqualError(t, err, tt.wantErr.Error())
			} else {
				require.NoError(t, err)
			}
			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 {

Review comment (bug):
How about this? It'd be a less noisy test log this way.

```suggestion
						require.FailNow(t, "CapiClient should not be invoked when --web is set")
```

Code after:
			baseRepo:       ghrepo.New("OWNER", "REPO"),
			wantOut:        "",
			wantStderr:     "Opening https://github.com/copilot/agents in your browser.\n",
			wantBrowserURL: "https://github.com/copilot/agents",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			reg := &httpmock.Registry{}
			if tt.stubs != nil {
				tt.stubs(reg)
			}

			cfg := config.NewBlankConfig()
			cfg.Set("github.com", "oauth_token", "OTOKEN")
			authCfg := cfg.Authentication()

			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,
				Web:     tt.web,
				Browser: br,
				CapiClient: func() (*capi.CAPIClient, error) {
					if tt.web {
						require.FailNow(t, "CapiClient was called with --web")
					}
					return capiClient, nil
				},
			}
			if tt.baseRepo != nil || tt.baseRepoErr != nil {
				baseRepo := tt.baseRepo
				baseRepoErr := tt.baseRepoErr
				opts.BaseRepo = func() (ghrepo.Interface, error) { return baseRepo, baseRepoErr }
			}

			err := listRun(opts)
			if tt.wantErr != nil {
				assert.Error(t, err)
				require.EqualError(t, err, tt.wantErr.Error())
Repository: cli/cli
File: pkg/cmd/agent-task/list/list_test.go (Go)

Code before:
					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
				baseRepoErr := tt.baseRepoErr
				opts.BaseRepo = func() (ghrepo.Interface, error) { return baseRepo, baseRepoErr }
			}

			err := listRun(opts)
			if tt.wantErr != nil {
				assert.Error(t, err)
				require.EqualError(t, err, tt.wantErr.Error())
			} else {
				require.NoError(t, err)
			}
			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)
		})
	}
}

// registerRepoSingleSessionMock mocks repo-scoped endpoint with one session and hydration.
func registerRepoSingleSessionMock(reg *httpmock.Registry, createdAt, owner, repo string) {
	reg.Register(
		httpmock.WithHost(httpmock.REST("GET", "agents/sessions/nwo/"+owner+"/"+repo), "api.githubcopilot.com"),
		httpmock.StringResponse(heredoc.Docf(`{
			"sessions": [
				{
					"id": "sessR1",
					"name": "Repo build",
					"user_id": 1,
					"agent_id": 2,
					"logs": "",
					"state": "completed",
					"owner_id": 10,
					"repo_id": 1000,
					"resource_type": "pull",
					"resource_id": 3000,

Review comment (suggestion):
The `if` is redundant and can be simplified to just this:

```go
require.Equal(t, tt.wantStderr, stderr.String())
```

Code after:
			}

			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,
				Web:     tt.web,
				Browser: br,
				CapiClient: func() (*capi.CAPIClient, error) {
					if tt.web {
						require.FailNow(t, "CapiClient was called with --web")
					}
					return capiClient, nil
				},
			}
			if tt.baseRepo != nil || tt.baseRepoErr != nil {
				baseRepo := tt.baseRepo
				baseRepoErr := tt.baseRepoErr
				opts.BaseRepo = func() (ghrepo.Interface, error) { return baseRepo, baseRepoErr }
			}

			err := listRun(opts)
			if tt.wantErr != nil {
				assert.Error(t, err)
				require.EqualError(t, err, tt.wantErr.Error())
			} else {
				require.NoError(t, err)
			}
			got := stdout.String()
			require.Equal(t, tt.wantOut, got)
			require.Equal(t, tt.wantStderr, stderr.String())
			if tt.web {
				br.Verify(t, tt.wantBrowserURL)
			}
			reg.Verify(t)
		})
	}
}

// registerRepoSingleSessionMock mocks repo-scoped endpoint with one session and hydration.
func registerRepoSingleSessionMock(reg *httpmock.Registry, createdAt, owner, repo string) {
	reg.Register(
		httpmock.WithHost(httpmock.REST("GET", "agents/sessions/nwo/"+owner+"/"+repo), "api.githubcopilot.com"),
		httpmock.StringResponse(heredoc.Docf(`{
			"sessions": [
				{
					"id": "sessR1",
					"name": "Repo build",
					"user_id": 1,
Repository: cli/cli
File: pkg/cmd/agent-task/capi/job.go (Go)

Code before:
		// Attempt to parse error body for message
		var er struct {
			Error struct {
				Message string `json:"message"`
			} `json:"error"`
		}
		_ = json.NewDecoder(res.Body).Decode(&er)
		msg := er.Error.Message
		if msg == "" {
			msg = res.Status
		}
		return nil, fmt.Errorf("failed to create job: %s", msg)
	}
	var j Job
	if err := json.NewDecoder(res.Body).Decode(&j); err != nil {
		return nil, fmt.Errorf("failed to decode create job response: %w", err)
	}
	return &j, nil
}

// GetJob retrieves a agent job
func (c *CAPIClient) GetJob(ctx context.Context, owner, repo, jobID string) (*Job, error) {
	if owner == "" || repo == "" || jobID == "" {
		return nil, errors.New("owner, repo, and jobID are required")
	}
	url := fmt.Sprintf("%s/%s/%s/%s", jobsBasePathV1, owner, repo, jobID)
	req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, http.NoBody)
	if err != nil {
		return nil, err
	}
	res, err := c.httpClient.Do(req)
	if err != nil {
		return nil, err
	}
	defer res.Body.Close()
	if res.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("failed to get job: %s", res.Status)
	}
	var j Job
	if err := json.NewDecoder(res.Body).Decode(&j); err != nil {
		return nil, fmt.Errorf("failed to decode get job response: %w", err)
	}
	return &j, nil
}


Review comment (security):
Same comment:

```suggestion
	url := fmt.Sprintf("%s/%s/%s/%s", url.PathEscape(jobsBasePathV1), url.PathEscape(owner), url.PathEscape(repo), url.PathEscape(jobID))
```


Code after:
		return nil, err
	}
	defer res.Body.Close()

	var j Job
	if err := json.NewDecoder(res.Body).Decode(&j); err != nil {
		return nil, fmt.Errorf("failed to decode create job response: %w", err)
	}

	if res.StatusCode != http.StatusCreated && res.StatusCode != http.StatusOK { // accept 201 or 200
		return nil, fmt.Errorf("failed to create job: %s", j.ErrorInfo.Message)
	}

	return &j, nil
}

// GetJob retrieves a agent job
func (c *CAPIClient) GetJob(ctx context.Context, owner, repo, jobID string) (*Job, error) {
	if owner == "" || repo == "" || jobID == "" {
		return nil, errors.New("owner, repo, and jobID are required")
	}
	url := fmt.Sprintf("%s/%s/%s/%s", jobsBasePathV1, url.PathEscape(owner), url.PathEscape(repo), url.PathEscape(jobID))
	req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, http.NoBody)
	if err != nil {
		return nil, err
	}
	res, err := c.httpClient.Do(req)
	if err != nil {
		return nil, err
	}
	defer res.Body.Close()
	if res.StatusCode != http.StatusOK {
		// Normalize to "<code> <text>" form
		statusText := fmt.Sprintf("%d %s", res.StatusCode, http.StatusText(res.StatusCode))
		return nil, fmt.Errorf("failed to get job: %s", statusText)
	}
	var j Job
	if err := json.NewDecoder(res.Body).Decode(&j); err != nil {
		return nil, fmt.Errorf("failed to decode get job response: %w", err)
	}
	return &j, nil
}