diff --git a/.opencode/tools/tea-issue-deps.ts b/.opencode/tools/tea-issue-deps.ts new file mode 100644 index 0000000..be2a165 --- /dev/null +++ b/.opencode/tools/tea-issue-deps.ts @@ -0,0 +1,110 @@ +import { tool } from "@opencode-ai/plugin" +import { $ } from "bun" + +export const issueDepsList = tool({ + description: "List dependency relationships for an issue (what it blocks and what blocks it)", + args: { + issueNumber: tool.schema.number().describe("Issue number"), + }, + async execute(args, context) { + try { + const result = await $`tea issue ${args.issueNumber} --output json --comments`.text() + const issue = JSON.parse(result) + + const dependencies = { + issueNumber: issue.index, + title: issue.title, + blocks: [], + blockedBy: [], + } + + if (issue.comments && Array.isArray(issue.comments)) { + for (const comment of issue.comments) { + const text = comment.body || "" + const depMatch = text.match(/blocks\s+#[0-9]+|blocked by\s+#[0-9]+/i) + if (depMatch) { + const relatedIssueMatch = text.match(/#[0-9]+/) + if (relatedIssueMatch) { + const relatedIssueNum = parseInt(relatedIssueMatch[0], 10) + const isBlocks = depMatch[0].toLowerCase().startsWith("blocks") + if (isBlocks) { + dependencies.blocks.push({ + issueNumber: relatedIssueNum, + relationship: "blocks", + }) + } else { + dependencies.blockedBy.push({ + issueNumber: relatedIssueNum, + relationship: "blockedBy", + }) + } + } + } + } + } + + return dependencies + } catch (error: any) { + return { + error: "Failed to get issue dependencies", + message: error.message, + stderr: error.stderr?.toString(), + } + } + }, +}) + +export const issueDepsAdd = tool({ + description: "Add a dependency relationship between issues (issue A blocks issue B, or issue A is blocked by issue B)", + args: { + issueNumber: tool.schema.number().describe("Primary issue number"), + relatedIssueNumber: tool.schema.number().describe("Related issue number"), + relationship: tool.schema.enum(["blocks", "blockedBy"]).describe("Relationship type: 'blocks' means issueNumber blocks relatedIssueNumber, 'blockedBy' means issueNumber is blocked by relatedIssueNumber"), + comment: tool.schema.string().optional().describe("Optional comment to add with the dependency"), + }, + async execute(args, context) { + try { + let commentText = "" + if (args.relationship === "blocks") { + commentText = `@${args.relatedIssueNumber} blocks #${args.issueNumber}` + } else { + commentText = `@${args.issueNumber} is blocked by #${args.relatedIssueNumber}` + } + + if (args.comment) { + commentText += `\n\n${args.comment}` + } + + const result = await $`tea issue comment --output json --issue ${args.issueNumber} --body ${commentText}`.text() + return JSON.parse(result) + } catch (error: any) { + return { + error: "Failed to add issue dependency", + message: error.message, + stderr: error.stderr?.toString(), + } + } + }, +}) + +export const issueDepsRemove = tool({ + description: "Remove a dependency relationship between issues by commenting on the issue", + args: { + issueNumber: tool.schema.number().describe("Primary issue number"), + relatedIssueNumber: tool.schema.number().describe("Related issue number to remove dependency from"), + }, + async execute(args, context) { + try { + const commentText = `@${args.relatedIssueNumber} removing dependency relationship` + + const result = await $`tea issue comment --output json --issue ${args.issueNumber} --body ${commentText}`.text() + return JSON.parse(result) + } catch (error: any) { + return { + error: "Failed to remove issue dependency", + message: error.message, + stderr: error.stderr?.toString(), + } + } + }, +}) \ No newline at end of file