From a08e4c96514b791391c9b81ade129f6634ad57f7 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Sun, 3 May 2026 01:21:17 -0400 Subject: core: simplify triage workflow to focus on issue ownership Switch triage agent to gpt-5.4-nano for faster issue assignment. Remove label management from the triage tool so it only assigns owners based on team ownership rules. This reduces noise in the issue tracker and ensures issues get to the right team member immediately without unnecessary labels. Update team structures to reflect current ownership and add script for processing unassigned issues. --- script/triage-unassigned.ts | 129 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 script/triage-unassigned.ts (limited to 'script') diff --git a/script/triage-unassigned.ts b/script/triage-unassigned.ts new file mode 100644 index 000000000..a71c6af31 --- /dev/null +++ b/script/triage-unassigned.ts @@ -0,0 +1,129 @@ +#!/usr/bin/env bun + +import { parseArgs } from "util" + +async function run(command: string, args: string[], options: Bun.SpawnOptions.OptionsObject = {}) { + const process = Bun.spawn([command, ...args], options) + const status = await process.exited + if (status !== 0) throw new Error(`${command} ${args.join(" ")} exited with ${status}`) + return process +} + +async function text(command: string, args: string[]) { + const process = await run(command, args, { stdout: "pipe", stderr: "inherit" }) + return new Response(process.stdout).text() +} + +async function main() { + const { values } = parseArgs({ + args: Bun.argv.slice(2), + options: { + days: { type: "string", short: "d", default: "30" }, + limit: { type: "string", short: "l", default: "200" }, + "dry-run": { type: "boolean", default: false }, + help: { type: "boolean", short: "h", default: false }, + }, + }) + + if (values.help) { + console.log(` +Usage: bun script/triage-unassigned.ts [options] + +Triage open GitHub issues created in the last 30 days with no assignee. + +Options: + -d, --days Look back this many days (default: 30) + -l, --limit Maximum issues to process (default: 200) + --dry-run Print matching issues without running triage + -h, --help Show this help message + +Examples: + bun script/triage-unassigned.ts + bun script/triage-unassigned.ts --limit 3 + bun script/triage-unassigned.ts --dry-run +`) + process.exit(0) + } + + const days = Number(values.days) + const limit = Number(values.limit) + if (!Number.isInteger(days) || days < 1) throw new Error("--days must be a positive integer") + if (!Number.isInteger(limit) || limit < 1) throw new Error("--limit must be a positive integer") + + const created = new Date(Date.now() - days * 24 * 60 * 60 * 1000).toISOString().slice(0, 10) + const query = `no:assignee created:>=${created}` + const issues = JSON.parse( + await text("gh", [ + "issue", + "list", + "--state", + "open", + "--search", + query, + "--limit", + String(limit), + "--json", + "number,title,body", + ]), + ) as Array<{ number: number; title: string; body?: string | null }> + + console.log(`Found ${issues.length} open unassigned issues created since ${created}`) + if (issues.length === 0) return + + if (values["dry-run"]) { + for (const issue of issues) console.log(`#${issue.number} ${issue.title}`) + return + } + + const githubToken = process.env.GITHUB_TOKEN || (await text("gh", ["auth", "token"])).trim() + const failures: Array<{ issue: number; error: string }> = [] + + for (const [index, issue] of issues.entries()) { + console.log(`\n[${index + 1}/${issues.length}] Triaging #${issue.number} ${issue.title}`) + const result = Bun.spawn( + [ + "opencode", + "run", + "--agent", + "triage", + `The following issue was just opened, triage it: + +Issue: #${issue.number} +Title: ${issue.title} + +Body: +${issue.body ?? ""}`, + ], + { + env: { + ...process.env, + GITHUB_TOKEN: githubToken, + ISSUE_NUMBER: String(issue.number), + ISSUE_TITLE: issue.title, + ISSUE_BODY: issue.body ?? "", + }, + stdin: "inherit", + stdout: "inherit", + stderr: "inherit", + }, + ) + const status = await result.exited + + if (status === 0) { + console.log(`[${index + 1}/${issues.length}] Done #${issue.number}`) + continue + } + + failures.push({ issue: issue.number, error: `opencode exited with ${status}` }) + console.error(`[${index + 1}/${issues.length}] Failed #${issue.number}: opencode exited with ${status}`) + } + + console.log(`\nFinished triaging ${issues.length - failures.length}/${issues.length} issues`) + if (failures.length === 0) return + + console.error("Failures:") + for (const failure of failures) console.error(`#${failure.issue}: ${failure.error}`) + process.exit(1) +} + +void main() -- cgit v1.2.3