import { getAgentCwd } from "std::index"

import {
  _ghClampPerPage,
  _ghClampPage,
  _ghCheckNumber,
 } from "agency-lang/stdlib-lib/github/args.js"
import {
  _ghIssueGet,
  _ghIssueList,
  _ghIssueComments,
  _ghIssueSearch,
  _ghScopedSearchQuery,
  _ghIssueCreate,
  _ghIssueComment,
  _ghIssueClose,
  _ghIssueLabel,
 } from "agency-lang/stdlib-lib/github/issues.js"
import {
  _ghPrGet,
  _ghPrList,
  _ghPrDiff,
  _ghPrFiles,
  _ghPrReviews,
  _ghPrReviewComments,
  _ghPrChecks,
  _ghPrHeadSha,
  _ghPrReviewComment,
  _ghPrReview,
  _ghPrApprove,
  _ghCheckReview,
 } from "agency-lang/stdlib-lib/github/prs.js"
import { _ghResolveRepo } from "agency-lang/stdlib-lib/github/repo.js"

/** @module
  @summary Read and manage GitHub issues and pull request reviews.
  Inspect issues, pull requests, diffs, and checks, then post comments or
  reviews. Each operation has its own effect. Pull request approval is
  separate from commenting or requesting changes, so a policy can allow
  reviews while rejecting approvals. Write interrupts include the text
  about to be posted under the authenticated account. GitHub treats a pull request as an issue, so the issue
  tools (ghIssueComment, ghIssueClose, ghIssueLabel) take pull request
  numbers too. There is no function to create, update, or merge a pull
  request.
  Every tool operates on one repository. With `owner` and `repo` left empty it
  uses the origin remote of the agent working directory, which `agency agent`
  sets for you; under plain `agency run` nothing sets it, so pass both names
  or call `setAgentCwd` first. Approving an effect "always" pins the approval
  to that one repository.
  The coding agent carries all of these tools and the review agent carries
  the reads, through the githubTools and githubReadTools bundles in
  std::agents/lib/toolkits.

  The credential comes from GITHUB_TOKEN / GH_TOKEN, then `gh auth token`,
  then the system keyring (setSecret("github-token", ...)). No tool ever
  takes or returns a token.

  ```ts
  import { ghPrGet, ghPrFiles, ghPrReview } from "std::github"

  node main() {
    const pr = ghPrGet(1002) with approve
    print(pr.title)
    const files = ghPrFiles(1002) with approve
    for (file in files) {
      print("${file.path}: +${file.additions} -${file.deletions}")
    }
    ghPrReview(1002, "COMMENT", "Two small things.", [
      { path: "lib/a.ts", line: 12, body: "This can be a const." },
    ]) with approve
  }
  ```
*/

// These types mirror the zod transforms in lib/stdlib/github/prs.ts and
// issues.ts; keep the two in sync.

export type PrState = "open" | "closed" | "all"

export type IssueState = "open" | "closed" | "all"
// APPROVE is left out on purpose: ghPrApprove is its own effect.
export type ReviewEvent = "COMMENT" | "REQUEST_CHANGES"

export type DiffSide = "LEFT" | "RIGHT"

export type CloseReason = "completed" | "not_planned"

export type ReviewComment = {
  path: string;
  line: number;
  body: string;
  side?: DiffSide
}

export type PrListItem = {
  number: number;
  title: string;
  state: string;
  author: string;
  base: string;
  head: string;
  headSha: string;
  draft: boolean;
  body: string;
  url: string
}

export type PrSummary = {
  number: number;
  title: string;
  state: string;
  author: string;
  base: string;
  head: string;
  headSha: string;
  draft: boolean;
  body: string;
  url: string;
  additions: number;
  deletions: number;
  changedFiles: number
}

export type PrFile = {
  path: string;
  status: string;
  additions: number;
  deletions: number;
  patch: string
}

export type ReviewSummary = {
  id: number;
  author: string;
  state: string;
  body: string;
  submittedAt: string
}

export type ReviewCommentInfo = {
  id: number;
  path: string;
  line?: number;
  author: string;
  body: string;
  url: string
}

export type CheckRun = {
  name: string;
  status: string;
  conclusion?: string;
  url: string
}

export type IssueSummary = {
  number: number;
  title: string;
  state: string;
  author: string;
  labels: string[];
  assignees: string[];
  body: string;
  url: string
}

export type CommentInfo = {
  id: number;
  author: string;
  body: string;
  createdAt: string;
  url: string
}

// @always(owner, repo): an "approve always" answer pins to one repository
// instead of every repository the token can reach.
@always(owner, repo)
effect std::github::prGet {
  owner: string;
  repo: string;
  number: number
}
@always(owner, repo)
effect std::github::prList {
  owner: string;
  repo: string;
  state: string;
  base: string;
  perPage: number;
  page: number
}
@always(owner, repo)
effect std::github::prDiff {
  owner: string;
  repo: string;
  number: number
}
@always(owner, repo)
effect std::github::prFiles {
  owner: string;
  repo: string;
  number: number;
  perPage: number;
  page: number
}
@always(owner, repo)
effect std::github::prReviewList {
  owner: string;
  repo: string;
  number: number;
  perPage: number;
  page: number
}
@always(owner, repo)
effect std::github::prReviewCommentList {
  owner: string;
  repo: string;
  number: number;
  perPage: number;
  page: number
}
@always(owner, repo)
effect std::github::prChecks {
  owner: string;
  repo: string;
  number: number;
  perPage: number;
  page: number
}
@always(owner, repo)
effect std::github::issueGet {
  owner: string;
  repo: string;
  number: number
}
@always(owner, repo)
effect std::github::issueList {
  owner: string;
  repo: string;
  state: string;
  labels: string[];
  perPage: number;
  page: number
}
@always(owner, repo)
effect std::github::issueCommentList {
  owner: string;
  repo: string;
  number: number;
  perPage: number;
  page: number
}
@always(owner, repo)
effect std::github::issueSearch {
  owner: string;
  repo: string;
  query: string;
  perPage: number;
  page: number
}

// Write effects carry the text about to be posted, so the approver sees it.
@always(owner, repo)
effect std::github::prReviewComment {
  owner: string;
  repo: string;
  number: number;
  path: string;
  line: number;
  body: string
}
@always(owner, repo)
effect std::github::prReview {
  owner: string;
  repo: string;
  number: number;
  event: string;
  body: string;
  comments: ReviewComment[]
}
@always(owner, repo)
effect std::github::prApprove {
  owner: string;
  repo: string;
  number: number;
  body: string
}
@always(owner, repo)
effect std::github::issueCreate {
  owner: string;
  repo: string;
  title: string;
  body: string;
  labels: string[];
  assignees: string[]
}
@always(owner, repo)
effect std::github::issueComment {
  owner: string;
  repo: string;
  number: number;
  body: string
}
@always(owner, repo)
effect std::github::issueUpdate {
  owner: string;
  repo: string;
  number: number;
  state: string;
  reason: string
}
@always(owner, repo)
effect std::github::issueLabel {
  owner: string;
  repo: string;
  number: number;
  labels: string[]
}

// ---- Reads -----------------------------------------------------------------
// Every function checks its arguments and resolves owner/repo BEFORE raising,
// so the payload shows exactly the request that will be sent.

@capabilities(fileRead, network, subprocess, env)
export idempotent def ghPrGet(
  number: number,
  owner: string = "",
  repo: string = "",
): PrSummary raises <std::github::prGet> {
  """
  Read a pull request's details and description.
  @param owner - Pass with repo, or leave both empty to use the agent working directory's origin remote.
  @param repo - The repository name without the owner, supplied together with owner.
  """
  _ghCheckNumber(number)
  const coord = _ghResolveRepo(owner, repo, getAgentCwd())
  return interrupt std::github::prGet("Read pull request #${number} on ${coord.owner}/${coord.repo}", {
    owner: coord.owner,
    repo: coord.repo,
    number: number
  })
  return _ghPrGet(number, coord.owner, coord.repo)
}

@capabilities(fileRead, network, subprocess, env)
export idempotent def ghPrList(
  state: PrState = "open",
  base: string = "",
  perPage: number = 30,
  page: number = 1,
  owner: string = "",
  repo: string = "",
): PrListItem[] raises <std::github::prList> {
  """
  List pull requests. Each item has no change counts; ghPrGet returns those.
  @param base - Only pull requests targeting this base branch ("" for any).
  @param perPage - Results per page, at most 100.
  @param page - Page number, starting at 1.
  @param owner - Pass with repo, or leave both empty to use the agent working directory's origin remote.
  @param repo - The repository name without the owner, supplied together with owner.
  """
  const coord = _ghResolveRepo(owner, repo, getAgentCwd())
  const pageSize = _ghClampPerPage(perPage)
  const pageNumber = _ghClampPage(page)
  return interrupt std::github::prList("List ${state} pull requests on ${coord.owner}/${coord.repo}", {
    owner: coord.owner,
    repo: coord.repo,
    state: state,
    base: base,
    perPage: pageSize,
    page: pageNumber
  })
  return _ghPrList(state, base, pageSize, pageNumber, coord.owner, coord.repo)
}

@capabilities(fileRead, network, subprocess, env)
export idempotent def ghPrDiff(
  number: number,
  owner: string = "",
  repo: string = "",
): string raises <std::github::prDiff> {
  """
  Read the full unified diff of a pull request as one string. GitHub refuses
  this for very large pull requests; use ghPrFiles for those.
  @param owner - Pass with repo, or leave both empty to use the agent working directory's origin remote.
  @param repo - The repository name without the owner, supplied together with owner.
  """
  _ghCheckNumber(number)
  const coord = _ghResolveRepo(owner, repo, getAgentCwd())
  return interrupt std::github::prDiff("Read the diff of pull request #${number} on ${coord.owner}/${coord.repo}", {
    owner: coord.owner,
    repo: coord.repo,
    number: number
  })
  return _ghPrDiff(number, coord.owner, coord.repo)
}

@capabilities(fileRead, network, subprocess, env)
export idempotent def ghPrFiles(
  number: number,
  perPage: number = 100,
  page: number = 1,
  owner: string = "",
  repo: string = "",
): PrFile[] raises <std::github::prFiles> {
  """
  List the files a pull request changes, with per-file add/delete counts and patch hunks.
  @param perPage - Results per page, at most 100.
  @param page - Page number, starting at 1.
  @param owner - Pass with repo, or leave both empty to use the agent working directory's origin remote.
  @param repo - The repository name without the owner, supplied together with owner.
  """
  _ghCheckNumber(number)
  const coord = _ghResolveRepo(owner, repo, getAgentCwd())
  const pageSize = _ghClampPerPage(perPage)
  const pageNumber = _ghClampPage(page)
  return interrupt std::github::prFiles("List the changed files of pull request #${number} on ${coord.owner}/${coord.repo}", {
    owner: coord.owner,
    repo: coord.repo,
    number: number,
    perPage: pageSize,
    page: pageNumber
  })
  return _ghPrFiles(number, pageSize, pageNumber, coord.owner, coord.repo)
}

@capabilities(fileRead, network, subprocess, env)
export idempotent def ghPrReviews(
  number: number,
  perPage: number = 30,
  page: number = 1,
  owner: string = "",
  repo: string = "",
): ReviewSummary[] raises <std::github::prReviewList> {
  """
  Read the submitted reviews on a pull request.
  @param perPage - Results per page, at most 100.
  @param page - Page number, starting at 1.
  @param owner - Pass with repo, or leave both empty to use the agent working directory's origin remote.
  @param repo - The repository name without the owner, supplied together with owner.
  """
  _ghCheckNumber(number)
  const coord = _ghResolveRepo(owner, repo, getAgentCwd())
  const pageSize = _ghClampPerPage(perPage)
  const pageNumber = _ghClampPage(page)
  return interrupt std::github::prReviewList("List reviews on pull request #${number} on ${coord.owner}/${coord.repo}", {
    owner: coord.owner,
    repo: coord.repo,
    number: number,
    perPage: pageSize,
    page: pageNumber
  })
  return _ghPrReviews(number, pageSize, pageNumber, coord.owner, coord.repo)
}

@capabilities(fileRead, network, subprocess, env)
export idempotent def ghPrReviewComments(
  number: number,
  perPage: number = 30,
  page: number = 1,
  owner: string = "",
  repo: string = "",
): ReviewCommentInfo[] raises <std::github::prReviewCommentList> {
  """
  List the inline review comments on a pull request, with file and line.
  @param perPage - Results per page, at most 100.
  @param page - Page number, starting at 1.
  @param owner - Pass with repo, or leave both empty to use the agent working directory's origin remote.
  @param repo - The repository name without the owner, supplied together with owner.
  """
  _ghCheckNumber(number)
  const coord = _ghResolveRepo(owner, repo, getAgentCwd())
  const pageSize = _ghClampPerPage(perPage)
  const pageNumber = _ghClampPage(page)
  return interrupt std::github::prReviewCommentList("List inline review comments on pull request #${number} on ${coord.owner}/${coord.repo}", {
    owner: coord.owner,
    repo: coord.repo,
    number: number,
    perPage: pageSize,
    page: pageNumber
  })
  return _ghPrReviewComments(number, pageSize, pageNumber, coord.owner, coord.repo)
}

@capabilities(fileRead, network, subprocess, env)
export idempotent def ghPrChecks(
  number: number,
  perPage: number = 30,
  page: number = 1,
  owner: string = "",
  repo: string = "",
): CheckRun[] raises <std::github::prChecks> {
  """
  List the CI check runs on the head commit of a pull request.
  @param perPage - Results per page, at most 100.
  @param page - Page number, starting at 1.
  @param owner - Pass with repo, or leave both empty to use the agent working directory's origin remote.
  @param repo - The repository name without the owner, supplied together with owner.
  """
  _ghCheckNumber(number)
  const coord = _ghResolveRepo(owner, repo, getAgentCwd())
  const pageSize = _ghClampPerPage(perPage)
  const pageNumber = _ghClampPage(page)
  return interrupt std::github::prChecks("Read CI check runs for pull request #${number} on ${coord.owner}/${coord.repo}", {
    owner: coord.owner,
    repo: coord.repo,
    number: number,
    perPage: pageSize,
    page: pageNumber
  })
  return _ghPrChecks(number, pageSize, pageNumber, coord.owner, coord.repo)
}

@capabilities(fileRead, network, subprocess, env)
export idempotent def ghIssueGet(
  number: number,
  owner: string = "",
  repo: string = "",
): IssueSummary raises <std::github::issueGet> {
  """
  Read an issue's details and description. Pull request numbers are rejected.
  @param owner - Pass with repo, or leave both empty to use the agent working directory's origin remote.
  @param repo - The repository name without the owner, supplied together with owner.
  """
  _ghCheckNumber(number)
  const coord = _ghResolveRepo(owner, repo, getAgentCwd())
  return interrupt std::github::issueGet("Read issue #${number} on ${coord.owner}/${coord.repo}", {
    owner: coord.owner,
    repo: coord.repo,
    number: number
  })
  return _ghIssueGet(number, coord.owner, coord.repo)
}

/**
  GitHub includes pull requests in the underlying page, then this tool
  removes them. Use `ghIssueSearch` with `is:issue` when you need pagination
  that counts issues alone.
*/
@capabilities(fileRead, network, subprocess, env)
export idempotent def ghIssueList(
  state: IssueState = "open",
  labels: string[] = [],
  perPage: number = 30,
  page: number = 1,
  owner: string = "",
  repo: string = "",
): IssueSummary[] raises <std::github::issueList> {
  """
  List issues, optionally filtered by labels. A short or empty page can
  still have later pages because pull requests are excluded from results.
  To page through every issue exactly, use ghIssueSearch with "is:issue".
  @param labels - Only issues carrying all of these labels ([] for any).
  @param perPage - Results per page, at most 100.
  @param page - Page number, starting at 1.
  @param owner - Pass with repo, or leave both empty to use the agent working directory's origin remote.
  @param repo - The repository name without the owner, supplied together with owner.
  """
  const coord = _ghResolveRepo(owner, repo, getAgentCwd())
  const pageSize = _ghClampPerPage(perPage)
  const pageNumber = _ghClampPage(page)
  return interrupt std::github::issueList("List ${state} issues on ${coord.owner}/${coord.repo}", {
    owner: coord.owner,
    repo: coord.repo,
    state: state,
    labels: labels,
    perPage: pageSize,
    page: pageNumber
  })
  return _ghIssueList(state, labels, pageSize, pageNumber, coord.owner, coord.repo)
}

@capabilities(fileRead, network, subprocess, env)
export idempotent def ghIssueComments(
  number: number,
  perPage: number = 30,
  page: number = 1,
  owner: string = "",
  repo: string = "",
): CommentInfo[] raises <std::github::issueCommentList> {
  """
  List the comments on an issue.
  @param perPage - Results per page, at most 100.
  @param page - Page number, starting at 1.
  @param owner - Pass with repo, or leave both empty to use the agent working directory's origin remote.
  @param repo - The repository name without the owner, supplied together with owner.
  """
  _ghCheckNumber(number)
  const coord = _ghResolveRepo(owner, repo, getAgentCwd())
  const pageSize = _ghClampPerPage(perPage)
  const pageNumber = _ghClampPage(page)
  return interrupt std::github::issueCommentList("List comments on issue #${number} on ${coord.owner}/${coord.repo}", {
    owner: coord.owner,
    repo: coord.repo,
    number: number,
    perPage: pageSize,
    page: pageNumber
  })
  return _ghIssueComments(number, pageSize, pageNumber, coord.owner, coord.repo)
}

@capabilities(fileRead, network, subprocess, env)
export idempotent def ghIssueSearch(
  query: string,
  perPage: number = 30,
  page: number = 1,
  owner: string = "",
  repo: string = "",
): IssueSummary[] raises <std::github::issueSearch> {
  """
  Search issues and pull requests in one repository. The query must not
  contain a repo:, org:, or user: qualifier.
  @param query - GitHub search syntax, e.g. "crash in:title label:bug".
  @param perPage - Results per page, at most 100.
  @param page - Page number, starting at 1.
  @param owner - Pass with repo, or leave both empty to use the agent working directory's origin remote.
  @param repo - The repository name without the owner, supplied together with owner.
  """
  const coord = _ghResolveRepo(owner, repo, getAgentCwd())
  const pageSize = _ghClampPerPage(perPage)
  const pageNumber = _ghClampPage(page)
  const scopedQuery = _ghScopedSearchQuery(coord.owner, coord.repo, query)
  return interrupt std::github::issueSearch("Search issues on ${coord.owner}/${coord.repo}: ${query}", {
    owner: coord.owner,
    repo: coord.repo,
    query: scopedQuery,
    perPage: pageSize,
    page: pageNumber
  })
  return _ghIssueSearch(scopedQuery, pageSize, pageNumber)
}

// ---- Writes ----------------------------------------------------------------
// Destructive blocks mark writes so a retry does not repeat them.

/**
  Use `ghPrReview` to submit several inline comments in one review. The
  comment attaches to the selected commit, defaulting to the PR head.
*/
@capabilities(fileRead, network, subprocess, env)
export def ghPrReviewComment(
  number: number,
  path: string,
  line: number,
  body: string,
  side: DiffSide = "RIGHT",
  commitSha: string = "",
  owner: string = "",
  repo: string = "",
): ReviewCommentInfo raises <std::github::prReviewComment> {
  """
  Post one inline comment on a pull request diff. For several comments at
  once, use ghPrReview: one call, one approval.
  @param path - The repository-relative path shown in the pull request diff.
  @param line - The one-based file line on the chosen diff side, within a changed hunk.
  @param body - The comment text (markdown).
  @param side - Which side of the diff: "RIGHT" (new code) or "LEFT" (old).
  @param commitSha - The commit to anchor to. Empty means the PR head commit.
  @param owner - Pass with repo, or leave both empty to use the agent working directory's origin remote.
  @param repo - The repository name without the owner, supplied together with owner.
  """
  _ghCheckNumber(number)
  _ghCheckNumber(line)
  const coord = _ghResolveRepo(owner, repo, getAgentCwd())
  return interrupt std::github::prReviewComment("Post an inline review comment on ${path}:${line} of pull request #${number} on ${coord.owner}/${coord.repo}", {
    owner: coord.owner,
    repo: coord.repo,
    number: number,
    path: path,
    line: line,
    body: body
  })
  // The head-SHA read stays outside the destructive block: a failed read
  // must not count as a failed mutation.
  let sha = commitSha
  if (sha == "") {
    sha = _ghPrHeadSha(number, coord.owner, coord.repo)
  }
  destructive {
    return _ghPrReviewComment(
      number,
      path,
      line,
      body,
      side,
      sha,
      coord.owner,
      coord.repo,
    )
  }
}

/**
  Submit overall feedback and inline comments together. Use `ghPrApprove`
  for a formal approval, which has its own effect.
*/
@capabilities(fileRead, network, subprocess, env)
export def ghPrReview(
  number: number,
  event: ReviewEvent = "COMMENT",
  body: string = "",
  comments: ReviewComment[] = [],
  owner: string = "",
  repo: string = "",
): ReviewSummary raises <std::github::prReview> {
  """
  Submit a pull request review with an overall comment and optional inline
  comments, either commenting or requesting changes.
  @param body - Overall review text in Markdown, required when requesting changes.
  @param comments - Inline comments, each with path, line, body, and an optional side.
  @param owner - Pass with repo, or leave both empty to use the agent working directory's origin remote.
  @param repo - The repository name without the owner, supplied together with owner.
  """
  _ghCheckNumber(number)
  _ghCheckReview(event, body, comments)
  const coord = _ghResolveRepo(owner, repo, getAgentCwd())
  return interrupt std::github::prReview("Submit a ${event} review with ${comments.length} inline comments on pull request #${number} on ${coord.owner}/${coord.repo}", {
    owner: coord.owner,
    repo: coord.repo,
    number: number,
    event: event,
    body: body,
    comments: comments
  })
  destructive {
    return _ghPrReview(number, event, body, comments, coord.owner, coord.repo)
  }
}

@capabilities(fileRead, network, subprocess, env)
export def ghPrApprove(
  number: number,
  body: string = "",
  owner: string = "",
  repo: string = "",
): ReviewSummary raises <std::github::prApprove> {
  """
  Approve a pull request. This is a formal review approval that can satisfy
  branch protection and unblock a merge.
  @param body - Optional approval text (markdown).
  @param owner - Pass with repo, or leave both empty to use the agent working directory's origin remote.
  @param repo - The repository name without the owner, supplied together with owner.
  """
  _ghCheckNumber(number)
  const coord = _ghResolveRepo(owner, repo, getAgentCwd())
  return interrupt std::github::prApprove("APPROVE pull request #${number} on ${coord.owner}/${coord.repo}", {
    owner: coord.owner,
    repo: coord.repo,
    number: number,
    body: body
  })
  destructive {
    return _ghPrApprove(number, body, coord.owner, coord.repo)
  }
}

@capabilities(fileRead, network, subprocess, env)
export def ghIssueCreate(
  title: string,
  body: string,
  labels: string[] = [],
  assignees: string[] = [],
  owner: string = "",
  repo: string = "",
): IssueSummary raises <std::github::issueCreate> {
  """
  Create an issue.
  @param body - The issue body (markdown).
  @param labels - Labels to apply on creation.
  @param assignees - Usernames to assign on creation.
  @param owner - Pass with repo, or leave both empty to use the agent working directory's origin remote.
  @param repo - The repository name without the owner, supplied together with owner.
  """
  const coord = _ghResolveRepo(owner, repo, getAgentCwd())
  return interrupt std::github::issueCreate("Create issue \"${title}\" on ${coord.owner}/${coord.repo}", {
    owner: coord.owner,
    repo: coord.repo,
    title: title,
    body: body,
    labels: labels,
    assignees: assignees
  })
  destructive {
    return _ghIssueCreate(title, body, labels, assignees, coord.owner, coord.repo)
  }
}

@capabilities(fileRead, network, subprocess, env)
export def ghIssueComment(
  number: number,
  body: string,
  owner: string = "",
  repo: string = "",
): CommentInfo raises <std::github::issueComment> {
  """
  Post a comment on an issue or pull request.
  @param number - The issue or pull request number.
  @param body - The comment text (markdown).
  @param owner - Pass with repo, or leave both empty to use the agent working directory's origin remote.
  @param repo - The repository name without the owner, supplied together with owner.
  """
  _ghCheckNumber(number)
  const coord = _ghResolveRepo(owner, repo, getAgentCwd())
  return interrupt std::github::issueComment("Post a comment on issue #${number} on ${coord.owner}/${coord.repo}", {
    owner: coord.owner,
    repo: coord.repo,
    number: number,
    body: body
  })
  destructive {
    return _ghIssueComment(number, body, coord.owner, coord.repo)
  }
}

@capabilities(fileRead, network, subprocess, env)
export def ghIssueClose(
  number: number,
  reason: CloseReason = "completed",
  owner: string = "",
  repo: string = "",
): IssueSummary raises <std::github::issueUpdate> {
  """
  Close an issue or pull request.
  @param number - The issue or pull request number.
  @param owner - Pass with repo, or leave both empty to use the agent working directory's origin remote.
  @param repo - The repository name without the owner, supplied together with owner.
  """
  _ghCheckNumber(number)
  const coord = _ghResolveRepo(owner, repo, getAgentCwd())
  return interrupt std::github::issueUpdate("Close issue #${number} on ${coord.owner}/${coord.repo} as ${reason}", {
    owner: coord.owner,
    repo: coord.repo,
    number: number,
    state: "closed",
    reason: reason
  })
  destructive {
    return _ghIssueClose(number, reason, coord.owner, coord.repo)
  }
}

@capabilities(fileRead, network, subprocess, env)
export def ghIssueLabel(
  number: number,
  labels: string[],
  owner: string = "",
  repo: string = "",
): string[] raises <std::github::issueLabel> {
  """
  Add labels to an issue or pull request. Returns the full label list after the change.
  @param number - The issue or pull request number.
  @param labels - The labels to add.
  @param owner - Pass with repo, or leave both empty to use the agent working directory's origin remote.
  @param repo - The repository name without the owner, supplied together with owner.
  """
  _ghCheckNumber(number)
  const coord = _ghResolveRepo(owner, repo, getAgentCwd())
  return interrupt std::github::issueLabel("Add labels ${JSON.stringify(labels)} to issue #${number} on ${coord.owner}/${coord.repo}", {
    owner: coord.owner,
    repo: coord.repo,
    number: number,
    labels: labels
  })
  destructive {
    return _ghIssueLabel(number, labels, coord.owner, coord.repo)
  }
}
