summaryrefslogtreecommitdiffhomepage
path: root/.opencode/tool/github-triage.ts
blob: 8ad0212ad074f6caae948047cedfa08e741630c3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
/// <reference path="../env.d.ts" />
import { tool } from "@opencode-ai/plugin"
import DESCRIPTION from "./github-triage.txt"

const TEAM = {
  desktop: ["adamdotdevin", "iamdavidhill", "Brendonovich", "nexxeln"],
  zen: ["fwang", "MrMushrooooom"],
  tui: [
    "thdxr",
    "kommander",
    // "rekram1-node" (on vacation)
  ],
  core: [
    "thdxr",
    // "rekram1-node", (on vacation)
    "jlongster",
  ],
  docs: ["R44VC0RP"],
  windows: ["Hona"],
} as const

const ASSIGNEES = [...new Set(Object.values(TEAM).flat())]

function pick<T>(items: readonly T[]) {
  return items[Math.floor(Math.random() * items.length)]!
}

function getIssueNumber(): number {
  const issue = parseInt(process.env.ISSUE_NUMBER ?? "", 10)
  if (!issue) throw new Error("ISSUE_NUMBER env var not set")
  return issue
}

async function githubFetch(endpoint: string, options: RequestInit = {}) {
  const response = await fetch(`https://api.github.com${endpoint}`, {
    ...options,
    headers: {
      Authorization: `Bearer ${process.env.GITHUB_TOKEN}`,
      Accept: "application/vnd.github+json",
      "Content-Type": "application/json",
      ...options.headers,
    },
  })
  if (!response.ok) {
    throw new Error(`GitHub API error: ${response.status} ${response.statusText}`)
  }
  return response.json()
}

export default tool({
  description: DESCRIPTION,
  args: {
    assignee: tool.schema.enum(ASSIGNEES as [string, ...string[]]).describe("The username of the assignee"),
    labels: tool.schema
      .array(tool.schema.enum(["nix", "opentui", "perf", "web", "desktop", "zen", "docs", "windows", "core"]))
      .describe("The labels(s) to add to the issue")
      .default([]),
  },
  async execute(args) {
    const issue = getIssueNumber()
    const owner = "anomalyco"
    const repo = "opencode"

    const results: string[] = []
    let labels = [...new Set(args.labels.map((x) => (x === "desktop" ? "web" : x)))]
    const web = labels.includes("web")
    const text = `${process.env.ISSUE_TITLE ?? ""}\n${process.env.ISSUE_BODY ?? ""}`.toLowerCase()
    const zen = /\bzen\b/.test(text) || text.includes("opencode black")
    const nix = /\bnix(os)?\b/.test(text)

    if (labels.includes("nix") && !nix) {
      labels = labels.filter((x) => x !== "nix")
      results.push("Dropped label: nix (issue does not mention nix)")
    }

    // const assignee = nix ? "rekram1-node" : web ? pick(TEAM.desktop) : args.assignee
    const assignee = web ? pick(TEAM.desktop) : args.assignee

    if (labels.includes("zen") && !zen) {
      throw new Error("Only add the zen label when issue title/body contains 'zen'")
    }

    if (web && !nix && !(TEAM.desktop as readonly string[]).includes(assignee)) {
      throw new Error("Web issues must be assigned to adamdotdevin, iamdavidhill, Brendonovich, or nexxeln")
    }

    if ((TEAM.zen as readonly string[]).includes(assignee) && !labels.includes("zen")) {
      throw new Error("Only zen issues should be assigned to fwang or MrMushrooooom")
    }

    if (assignee === "Hona" && !labels.includes("windows")) {
      throw new Error("Only windows issues should be assigned to Hona")
    }

    if (assignee === "R44VC0RP" && !labels.includes("docs")) {
      throw new Error("Only docs issues should be assigned to R44VC0RP")
    }

    if (assignee === "kommander" && !labels.includes("opentui")) {
      throw new Error("Only opentui issues should be assigned to kommander")
    }

    await githubFetch(`/repos/${owner}/${repo}/issues/${issue}/assignees`, {
      method: "POST",
      body: JSON.stringify({ assignees: [assignee] }),
    })
    results.push(`Assigned @${assignee} to issue #${issue}`)

    if (labels.length > 0) {
      await githubFetch(`/repos/${owner}/${repo}/issues/${issue}/labels`, {
        method: "POST",
        body: JSON.stringify({ labels }),
      })
      results.push(`Added labels: ${labels.join(", ")}`)
    }

    return results.join("\n")
  },
})