diff --git a/.opencode/agent/triage.md b/.opencode/agent/triage.md
index 5d1147a88..ccf3f0c33 100644
--- a/.opencode/agent/triage.md
+++ b/.opencode/agent/triage.md
@@ -1,7 +1,7 @@
---
mode: primary
hidden: true
-model: opencode/claude-haiku-4-5
+model: opencode/minimax-m2.5
color: "#44BA81"
tools:
"*": false
@@ -12,6 +12,8 @@ You are a triage agent responsible for triaging github issues.
Use your github-triage tool to triage issues.
+This file is the source of truth for ownership/routing rules.
+
## Labels
### windows
@@ -43,12 +45,30 @@ Desktop app issues:
**Only** add if the issue explicitly mentions nix.
+If the issue does not mention nix, do not add nix.
+
+If the issue mentions nix, assign to `rekram1-node`.
+
#### zen
**Only** add if the issue mentions "zen" or "opencode zen" or "opencode black".
If the issue doesn't have "zen" or "opencode black" in it then don't add zen label
+#### core
+
+Use for core server issues in `packages/opencode/`, excluding `packages/opencode/src/cli/cmd/tui/`.
+
+Examples:
+
+- LSP server behavior
+- Harness behavior (agent + tools)
+- Feature requests for server behavior
+- Agent context construction
+- API endpoints
+- Provider integration issues
+- New, broken, or poor-quality models
+
#### docs
Add if the issue requests better documentation or docs updates.
@@ -66,13 +86,47 @@ TUI issues potentially caused by our underlying TUI library:
When assigning to people here are the following rules:
-adamdotdev:
-ONLY assign adam if the issue will have the "desktop" label.
+Desktop / Web:
+Use for desktop-labeled issues only.
-fwang:
-ONLY assign fwang if the issue will have the "zen" label.
+- adamdotdevin
+- iamdavidhill
+- Brendonovich
+- nexxeln
-jayair:
-ONLY assign jayair if the issue will have the "docs" label.
+Zen:
+ONLY assign if the issue will have the "zen" label.
-In all other cases use best judgment. Avoid assigning to kommander needlessly, when in doubt assign to rekram1-node.
+- fwang
+- MrMushrooooom
+
+TUI (`packages/opencode/src/cli/cmd/tui/...`):
+
+- thdxr for TUI UX/UI product decisions and interaction flow
+- kommander for OpenTUI engine issues: rendering artifacts, keybind handling, terminal compatibility, SSH behavior, and low-level perf bottlenecks
+- rekram1-node for TUI bugs that are not clearly OpenTUI engine issues
+
+Core (`packages/opencode/...`, excluding TUI subtree):
+
+- thdxr for sqlite/snapshot/memory bugs and larger architectural core features
+- jlongster for opencode server + API feature work (tool currently remaps jlongster -> thdxr until assignable)
+- rekram1-node for harness issues, provider issues, and other bug-squashing
+
+For core bugs that do not clearly map, either thdxr or rekram1-node is acceptable.
+
+Docs:
+
+- R44VC0RP
+
+Windows:
+
+- Hona (assign any issue that mentions Windows or is likely Windows-specific)
+
+Determinism rules:
+
+- If title + body does not contain "zen", do not add the "zen" label
+- If "nix" label is added but title + body does not mention nix/nixos, the tool will drop "nix"
+- If title + body mentions nix/nixos, assign to `rekram1-node`
+- If "desktop" label is added, the tool will override assignee and randomly pick one Desktop / Web owner
+
+In all other cases, choose the team/section with the most overlap with the issue and assign a member from that team at random.
diff --git a/.opencode/tool/github-triage.ts b/.opencode/tool/github-triage.ts
index 1e216f1c8..3a70c4e00 100644
--- a/.opencode/tool/github-triage.ts
+++ b/.opencode/tool/github-triage.ts
@@ -1,8 +1,22 @@
///
-// import { Octokit } from "@octokit/rest"
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"],
+ core: ["thdxr", "rekram1-node", "jlongster"],
+ docs: ["R44VC0RP"],
+ windows: ["Hona"],
+} as const
+
+const ASSIGNEES = [...new Set(Object.values(TEAM).flat())]
+
+function pick(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")
@@ -29,60 +43,79 @@ export default tool({
description: DESCRIPTION,
args: {
assignee: tool.schema
- .enum(["thdxr", "adamdotdevin", "rekram1-node", "fwang", "jayair", "kommander"])
+ .enum(ASSIGNEES as [string, ...string[]])
.describe("The username of the assignee")
.default("rekram1-node"),
labels: tool.schema
- .array(tool.schema.enum(["nix", "opentui", "perf", "desktop", "zen", "docs", "windows"]))
+ .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 octokit = new Octokit({ auth: process.env.GITHUB_TOKEN })
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 (args.assignee === "adamdotdevin" && !args.labels.includes("desktop")) {
- throw new Error("Only desktop issues should be assigned to adamdotdevin")
+ if (labels.includes("nix") && !nix) {
+ labels = labels.filter((x) => x !== "nix")
+ results.push("Dropped label: nix (issue does not mention nix)")
}
- if (args.assignee === "fwang" && !args.labels.includes("zen")) {
- throw new Error("Only zen issues should be assigned to fwang")
+ const assignee = nix
+ ? "rekram1-node"
+ : web
+ ? pick(TEAM.desktop)
+ : args.assignee === "jlongster"
+ ? "thdxr"
+ : args.assignee
+
+ if (args.assignee === "jlongster" && assignee === "thdxr") {
+ results.push("Remapped assignee: jlongster -> thdxr (jlongster not assignable yet)")
}
- if (args.assignee === "kommander" && !args.labels.includes("opentui")) {
+ 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 octokit.rest.issues.addAssignees({
- // owner,
- // repo,
- // issue_number: issue,
- // assignees: [args.assignee],
- // })
await githubFetch(`/repos/${owner}/${repo}/issues/${issue}/assignees`, {
method: "POST",
- body: JSON.stringify({ assignees: [args.assignee] }),
+ body: JSON.stringify({ assignees: [assignee] }),
})
- results.push(`Assigned @${args.assignee} to issue #${issue}`)
-
- const labels: string[] = args.labels.map((label) => (label === "desktop" ? "web" : label))
+ results.push(`Assigned @${assignee} to issue #${issue}`)
if (labels.length > 0) {
- // await octokit.rest.issues.addLabels({
- // owner,
- // repo,
- // issue_number: issue,
- // labels,
- // })
await githubFetch(`/repos/${owner}/${repo}/issues/${issue}/labels`, {
method: "POST",
body: JSON.stringify({ labels }),
})
- results.push(`Added labels: ${args.labels.join(", ")}`)
+ results.push(`Added labels: ${labels.join(", ")}`)
}
return results.join("\n")
diff --git a/.opencode/tool/github-triage.txt b/.opencode/tool/github-triage.txt
index ae47cf4cb..4369ed235 100644
--- a/.opencode/tool/github-triage.txt
+++ b/.opencode/tool/github-triage.txt
@@ -1,88 +1,6 @@
Use this tool to assign and/or label a GitHub issue.
-You can assign the following users:
-- thdxr
-- adamdotdevin
-- fwang
-- jayair
-- kommander
-- rekram1-node
+Choose labels and assignee using the current triage policy and ownership rules.
+Pick the most fitting labels for the issue and assign one owner.
-
-You can use the following labels:
-- nix
-- opentui
-- perf
-- web
-- zen
-- docs
-
-Always try to assign an issue, if in doubt, assign rekram1-node to it.
-
-## Breakdown of responsibilities:
-
-### thdxr
-
-Dax is responsible for managing core parts of the application, for large feature requests, api changes, or things that require significant changes to the codebase assign him.
-
-This relates to OpenCode server primarily but has overlap with just about anything
-
-### adamdotdevin
-
-Adam is responsible for managing the Desktop/Web app. If there is an issue relating to the desktop app or `opencode web` command. Assign him.
-
-
-### fwang
-
-Frank is responsible for managing Zen, if you see complaints about OpenCode Zen, maybe it's the dashboard, the model quality, billing issues, etc. Assign him to the issue.
-
-### jayair
-
-Jay is responsible for documentation. If there is an issue relating to documentation assign him.
-
-### kommander
-
-Sebastian is responsible for managing an OpenTUI (a library for building terminal user interfaces). OpenCode's TUI is built with OpenTUI. If there are issues about:
-- random characters on screen
-- keybinds not working on different terminals
-- general terminal stuff
-Then assign the issue to Him.
-
-### rekram1-node
-
-ALL BUGS SHOULD BE assigned to rekram1-node unless they have the `opentui` label.
-
-Assign Aiden to an issue as a catch all, if you can't assign anyone else. Most of the time this will be bugs/polish things.
-If no one else makes sense to assign, assign rekram1-node to it.
-
-Always assign to aiden if the issue mentions "acp", "zed", or model performance issues
-
-## Breakdown of Labels:
-
-### nix
-
-Any issue that mentions nix, or nixos should have a nix label
-
-### opentui
-
-Anything relating to the TUI itself should have an opentui label
-
-### perf
-
-Anything related to slow performance, high ram, high cpu usage, or any other performance related issue should have a perf label
-
-### desktop
-
-Anything related to `opencode web` command or the desktop app should have a desktop label. Never add this label for anything terminal/tui related
-
-### zen
-
-Anything related to OpenCode Zen, billing, or model quality from Zen should have a zen label
-
-### docs
-
-Anything related to the documentation should have a docs label
-
-### windows
-
-Use for any issue that involves the windows OS
+If unsure, choose the team/section with the most overlap with the issue and assign a member from that team at random.
diff --git a/packages/script/src/index.ts b/packages/script/src/index.ts
index a3f5e7a8e..1f39d3138 100644
--- a/packages/script/src/index.ts
+++ b/packages/script/src/index.ts
@@ -54,8 +54,13 @@ const team = [
"kommander",
"jayair",
"fwang",
+ "MrMushrooooom",
"adamdotdevin",
"iamdavidhill",
+ "Brendonovich",
+ "nexxeln",
+ "Hona",
+ "jlongster",
"opencode-agent[bot]",
"R44VC0RP",
]