diff --git a/.opencode/tools/tea-context.ts b/.opencode/tools/tea-context.ts index 34f82f1..d6c0b1e 100644 --- a/.opencode/tools/tea-context.ts +++ b/.opencode/tools/tea-context.ts @@ -7,7 +7,7 @@ export const whoami = tool({ async execute(args, context) { try { const result = await $`tea whoami --output json`.text() - return JSON.parse(result) + return result } catch (error: any) { return { error: "Failed to get current user info", diff --git a/.opencode/tools/tea-issue-deps.ts b/.opencode/tools/tea-issue-deps.ts index be2a165..efc8e10 100644 --- a/.opencode/tools/tea-issue-deps.ts +++ b/.opencode/tools/tea-issue-deps.ts @@ -54,6 +54,182 @@ export const issueDepsList = tool({ }, }) +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 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}` + } + + tea issue comment --output json --issue ${args.issueNumber} --body ${commentText}`.text() + return 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 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: { @@ -97,8 +273,8 @@ export const issueDepsRemove = tool({ 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) + tea issue comment --output json --issue ${args.issueNumber} --body ${commentText}`.text() + return result } catch (error: any) { return { error: "Failed to remove issue dependency", diff --git a/.opencode/tools/tea-issue.ts b/.opencode/tools/tea-issue.ts index 0e4448f..1b0c858 100644 --- a/.opencode/tools/tea-issue.ts +++ b/.opencode/tools/tea-issue.ts @@ -1,160 +1,196 @@ -import { tool } from "@opencode-ai/plugin" -import { $ } from "bun" +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", + 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"), + 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}` + let cmd = $`tea issue list --output json --state ${args.state} --limit ${args.limit ?? 30}`; + + if (args.labels && args.labels.length > 0) { + cmd = cmd`--labels ${args.labels}`; } - - if (args.assignee) { - cmd = cmd`--assignee ${args.assignee}` + + if (args.assignee && args.assignee.length > 0) { + cmd = cmd`--assignee ${args.assignee}`; } - - if (args.milestone) { - cmd = cmd`--milestones ${args.milestone}` + + if (args.milestone && args.milestone.length > 0) { + cmd = cmd`--milestones ${args.milestone}`; } - - if (args.author) { - cmd = cmd`--author ${args.author}` + + if (args.author && args.author.length > 0) { + cmd = cmd`--author ${args.author}`; } - - if (args.keyword) { - cmd = cmd`--keyword ${args.keyword}` + + if (args.keyword && args.keyword.length > 0) { + cmd = cmd`--keyword ${args.keyword}`; } - - const result = await cmd.text() - return JSON.parse(result) + + return await cmd.text(); } 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", + 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"), + 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"), + 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}` - + let cmd = $`tea issue create --output json --title ${args.title}`; + if (args.body) { - cmd = cmd`--body ${args.body}` + cmd = cmd`--body ${args.body}`; } - + if (args.labels) { - cmd = cmd`--labels ${args.labels.join(",")}` + cmd = cmd`--labels ${args.labels.join(",")}`; } - + if (args.milestone) { - cmd = cmd`--milestone ${args.milestone}` + cmd = cmd`--milestone ${args.milestone}`; } - + if (args.assignees) { - cmd = cmd`--assignee ${args.assignees.join(",")}` + cmd = cmd`--assignee ${args.assignees.join(",")}`; } - - const result = await cmd.text() - return JSON.parse(result) + + return await cmd.text(); } 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", + 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"), + 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"), + 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}` - + let cmd = $`tea issue edit --output json ${args.issueNumber}`; + if (args.title) { - cmd = cmd`--title ${args.title}` + cmd = cmd`--title ${args.title}`; } - + if (args.body) { - cmd = cmd`--body ${args.body}` + cmd = cmd`--body ${args.body}`; } - + if (args.labels) { - cmd = cmd`--labels ${args.labels.join(",")}` + cmd = cmd`--labels ${args.labels.join(",")}`; } - + if (args.milestone) { - cmd = cmd`--milestone ${args.milestone}` + cmd = cmd`--milestone ${args.milestone}`; } - + if (args.assignees) { - cmd = cmd`--assignee ${args.assignees.join(",")}` + cmd = cmd`--assignee ${args.assignees.join(",")}`; } - + if (args.state) { - cmd = cmd`--state ${args.state}` + cmd = cmd`--state ${args.state}`; } - - const result = await cmd.text() - return JSON.parse(result) + + return await cmd.text(); } 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", + 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) + return await $`tea issue ${args.issueNumber} --output json --comments`.text(); } catch (error: any) { return { error: "Failed to get issue details", message: error.message, stderr: error.stderr?.toString(), - } + }; } }, -}) \ No newline at end of file +}); \ No newline at end of file diff --git a/.opencode/tools/tea-label.ts b/.opencode/tools/tea-label.ts index 5284ce4..bd34ecb 100644 --- a/.opencode/tools/tea-label.ts +++ b/.opencode/tools/tea-label.ts @@ -7,7 +7,7 @@ export const labelList = tool({ async execute(args, context) { try { const result = await $`tea label list --output json`.text() - return JSON.parse(result) + return result } catch (error: any) { return { error: "Failed to list labels", diff --git a/.opencode/tools/tea-milestone.ts b/.opencode/tools/tea-milestone.ts index 1d5a748..8a87cf2 100644 --- a/.opencode/tools/tea-milestone.ts +++ b/.opencode/tools/tea-milestone.ts @@ -9,7 +9,7 @@ export const milestoneList = tool({ async execute(args, context) { try { const result = await $`tea milestone list --output json --state ${args.state}`.text() - return JSON.parse(result) + return result } catch (error: any) { return { error: "Failed to list milestones", @@ -40,7 +40,7 @@ export const milestoneCreate = tool({ } const result = await cmd.text() - return JSON.parse(result) + return result } catch (error: any) { return { error: "Failed to create milestone", diff --git a/.opencode/tools/tea-notification.ts b/.opencode/tools/tea-notification.ts index 8db7f23..5010fa0 100644 --- a/.opencode/tools/tea-notification.ts +++ b/.opencode/tools/tea-notification.ts @@ -9,7 +9,7 @@ export const notificationList = tool({ async execute(args, context) { try { const result = await $`tea notification list --output json --limit ${args.limit}`.text() - return JSON.parse(result) + return result } catch (error: any) { return { error: "Failed to list notifications", diff --git a/.opencode/tools/tea-pr.ts b/.opencode/tools/tea-pr.ts index fd74043..8d8e916 100644 --- a/.opencode/tools/tea-pr.ts +++ b/.opencode/tools/tea-pr.ts @@ -15,7 +15,7 @@ export const prList = tool({ : $`tea pr list --output json --state ${args.state} --limit ${args.limit}` const result = await cmd.text() - return JSON.parse(result) + return result } catch (error: any) { return { error: "Failed to list pull requests", @@ -53,7 +53,7 @@ export const prCreate = tool({ } const result = await cmd.text() - return JSON.parse(result) + return result } catch (error: any) { return { error: "Failed to create pull request", @@ -106,7 +106,7 @@ export const prMerge = tool({ } const result = await cmd.text() - return JSON.parse(result) + return result } catch (error: any) { return { error: "Failed to merge pull request", @@ -125,7 +125,7 @@ export const prDetails = tool({ async execute(args, context) { try { const result = await $`tea pr ${args.prNumber} --output json --comments`.text() - return JSON.parse(result) + return result } catch (error: any) { return { error: "Failed to get pull request details",