From 1596dd30b26d04bff86944c662b8096a8a38b1e3 Mon Sep 17 00:00:00 2001 From: Hugo Nijhuis Date: Sun, 3 May 2026 16:34:47 +0200 Subject: [PATCH] feat: add tea-issue.ts tool for issue management (issueList, issueCreate, issueEdit, issueDetails) --- .opencode/tools/tea-issue.ts | 160 +++++++++++++++++++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100644 .opencode/tools/tea-issue.ts diff --git a/.opencode/tools/tea-issue.ts b/.opencode/tools/tea-issue.ts new file mode 100644 index 0000000..0e4448f --- /dev/null +++ b/.opencode/tools/tea-issue.ts @@ -0,0 +1,160 @@ +import { tool } from "@opencode-ai/plugin" +import { $ } from "bun" + +export const issueList = tool({ + description: "List issues with filters for state, labels, assignee, milestone, and author", + args: { + state: tool.schema.enum(["open", "closed", "all"]).default("open").describe("Filter by state"), + labels: tool.schema.string().optional().describe("Comma-separated list of labels to match"), + assignee: tool.schema.string().optional().describe("Filter by assignee username"), + milestone: tool.schema.string().optional().describe("Filter by milestone name"), + author: tool.schema.string().optional().describe("Filter by author username"), + limit: tool.schema.number().default(30).describe("Number of issues to return"), + keyword: tool.schema.string().optional().describe("Search keyword in title and body"), + }, + async execute(args, context) { + try { + let cmd = $`tea issue list --output json --state ${args.state} --limit ${args.limit}` + + if (args.labels) { + cmd = cmd`--labels ${args.labels}` + } + + if (args.assignee) { + cmd = cmd`--assignee ${args.assignee}` + } + + if (args.milestone) { + cmd = cmd`--milestones ${args.milestone}` + } + + if (args.author) { + cmd = cmd`--author ${args.author}` + } + + if (args.keyword) { + cmd = cmd`--keyword ${args.keyword}` + } + + const result = await cmd.text() + return JSON.parse(result) + } catch (error: any) { + return { + error: "Failed to list issues", + message: error.message, + stderr: error.stderr?.toString(), + } + } + }, +}) + +export const issueCreate = tool({ + description: "Create a new issue with title, body, labels, milestone, and assignees", + args: { + title: tool.schema.string().describe("Issue title"), + body: tool.schema.string().optional().describe("Issue description/body"), + labels: tool.schema.array(tool.schema.string()).optional().describe("Comma-separated list of label names"), + milestone: tool.schema.string().optional().describe("Milestone name"), + assignees: tool.schema.array(tool.schema.string()).optional().describe("Comma-separated list of assignee usernames"), + }, + async execute(args, context) { + try { + let cmd = $`tea issue create --output json --title ${args.title}` + + if (args.body) { + cmd = cmd`--body ${args.body}` + } + + if (args.labels) { + cmd = cmd`--labels ${args.labels.join(",")}` + } + + if (args.milestone) { + cmd = cmd`--milestone ${args.milestone}` + } + + if (args.assignees) { + cmd = cmd`--assignee ${args.assignees.join(",")}` + } + + const result = await cmd.text() + return JSON.parse(result) + } catch (error: any) { + return { + error: "Failed to create issue", + message: error.message, + stderr: error.stderr?.toString(), + } + } + }, +}) + +export const issueEdit = tool({ + description: "Update an existing issue with partial updates to title, body, labels, milestone, or assignees", + args: { + issueNumber: tool.schema.number().describe("Issue number"), + title: tool.schema.string().optional().describe("New title"), + body: tool.schema.string().optional().describe("New description/body"), + labels: tool.schema.array(tool.schema.string()).optional().describe("New comma-separated list of label names"), + milestone: tool.schema.string().optional().describe("New milestone name"), + assignees: tool.schema.array(tool.schema.string()).optional().describe("New comma-separated list of assignee usernames"), + state: tool.schema.enum(["open", "closed"]).optional().describe("New state"), + }, + async execute(args, context) { + try { + let cmd = $`tea issue edit --output json ${args.issueNumber}` + + if (args.title) { + cmd = cmd`--title ${args.title}` + } + + if (args.body) { + cmd = cmd`--body ${args.body}` + } + + if (args.labels) { + cmd = cmd`--labels ${args.labels.join(",")}` + } + + if (args.milestone) { + cmd = cmd`--milestone ${args.milestone}` + } + + if (args.assignees) { + cmd = cmd`--assignee ${args.assignees.join(",")}` + } + + if (args.state) { + cmd = cmd`--state ${args.state}` + } + + const result = await cmd.text() + return JSON.parse(result) + } catch (error: any) { + return { + error: "Failed to edit issue", + message: error.message, + stderr: error.stderr?.toString(), + } + } + }, +}) + +export const issueDetails = tool({ + description: "Get detailed information about a specific issue including dependencies and comments", + args: { + issueNumber: tool.schema.number().describe("Issue number"), + }, + async execute(args, context) { + try { + const result = await $`tea issue ${args.issueNumber} --output json --comments`.text() + return JSON.parse(result) + } catch (error: any) { + return { + error: "Failed to get issue details", + message: error.message, + stderr: error.stderr?.toString(), + } + } + }, +}) \ No newline at end of file