Skip to content

Commit 5f55af1

Browse files
committed
update jira client
1 parent 79b0856 commit 5f55af1

4 files changed

Lines changed: 61 additions & 15 deletions

File tree

JIRA_SETUP.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ No cloud ID resolution, no OAuth token exchange, no session state.
8989

9090
| Variable | Required | Description |
9191
|---|---|---|
92-
| `JIRA_EMAIL` | Yes | Your Atlassian account email |
92+
| `JIRA_EMAIL` | Yes | Your Atlassian account email. Also used as your identity — issues are auto-assigned to this account when transitioning to In Progress, and `jira-assign-issue` accepts `"me"` to assign to this address. |
9393
| `JIRA_API_TOKEN` | Yes | API token from id.atlassian.com/manage-api-tokens |
9494
| `JIRA_BASE_URL` | No | Overrides `agent-config.json` base_url at runtime |
9595
| `JIRA_PROJECT_KEY` | No | Overrides `agent-config.json` project_key at runtime |

tools/jira-assign-issue.ts

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,44 @@
11
import { tool } from "@opencode-ai/plugin"
2-
import { getJiraClient } from "./lib/jira-client"
2+
import { getJiraClient, resolveAccountIdByEmail } from "./lib/jira-client"
33

44
export default tool({
55
description:
6-
"Assign a Jira issue to a user by their accountId. Use jira-search-users to find the accountId for a user by display name or email. Pass empty string to unassign. Requires Jira credentials — see JIRA_SETUP.md.",
6+
"Assign a Jira issue to a user. Pass 'me' to assign to yourself (uses JIRA_EMAIL), a Jira accountId to assign to someone else, or empty string to unassign. Use jira-search-users to find accountIds by name or email. Requires Jira credentials — see JIRA_SETUP.md.",
77
args: {
88
issue_key: tool.schema.string().describe("Issue key, e.g. 'PROJ-123'"),
99
account_id: tool.schema
1010
.string()
1111
.describe(
12-
"The Jira accountId of the user to assign. Use jira-search-users to find this. Pass empty string to unassign."
12+
"Pass 'me' to assign to yourself, a Jira accountId to assign to a specific user, or empty string to unassign."
1313
),
1414
},
1515
async execute(args) {
1616
const result = getJiraClient()
1717
if ("error" in result) return result.error
18-
const { client } = result
18+
const { client, currentUserEmail } = result
1919

2020
try {
21+
let accountId: string | null = args.account_id || null
22+
23+
// Resolve "me" to the current user's accountId
24+
if (args.account_id === "me") {
25+
if (!currentUserEmail) {
26+
return "Cannot resolve 'me' — JIRA_EMAIL is not set."
27+
}
28+
accountId = await resolveAccountIdByEmail(client, currentUserEmail)
29+
if (!accountId) {
30+
return `Could not find a Jira account for ${currentUserEmail}. Check that this email matches a Jira user.`
31+
}
32+
}
33+
2134
await client.issues.assignIssue({
2235
issueIdOrKey: args.issue_key,
23-
accountId: args.account_id || null,
36+
accountId,
2437
})
2538

26-
return args.account_id
27-
? `${args.issue_key} assigned to accountId ${args.account_id}`
28-
: `${args.issue_key} unassigned`
39+
if (!accountId) return `${args.issue_key} unassigned`
40+
if (args.account_id === "me") return `${args.issue_key} assigned to you (${currentUserEmail})`
41+
return `${args.issue_key} assigned to accountId ${accountId}`
2942
} catch (error: unknown) {
3043
const e = error as { status?: number; message?: string }
3144
return `Failed to assign ${args.issue_key}: ${e.status ?? ""} ${e.message ?? String(error)}`

tools/jira-transition-issue.ts

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { tool } from "@opencode-ai/plugin"
2-
import { getJiraClient } from "./lib/jira-client"
2+
import { getJiraClient, resolveAccountIdByEmail } from "./lib/jira-client"
33

44
export default tool({
55
description:
6-
"Change the status of a Jira issue by applying a workflow transition. Omit transition_name to list all available transitions for the issue first. Requires Jira credentials — see JIRA_SETUP.md.",
6+
"Change the status of a Jira issue by applying a workflow transition. When transitioning to 'In Progress', automatically assigns the issue to the current user (JIRA_EMAIL). Omit transition_name to list all available transitions. Requires Jira credentials — see JIRA_SETUP.md.",
77
args: {
88
issue_key: tool.schema.string().describe("Issue key, e.g. 'PROJ-123'"),
99
transition_name: tool.schema
@@ -16,7 +16,7 @@ export default tool({
1616
async execute(args) {
1717
const result = getJiraClient()
1818
if ("error" in result) return result.error
19-
const { client } = result
19+
const { client, currentUserEmail } = result
2020

2121
try {
2222
// Fetch available transitions
@@ -45,7 +45,19 @@ export default tool({
4545
transition: { id: match.id },
4646
})
4747

48-
return `${args.issue_key} transitioned to "${args.transition_name}"`
48+
const notes: string[] = [`${args.issue_key} transitioned to "${args.transition_name}"`]
49+
50+
// Auto-assign to current user when moving to In Progress
51+
const isInProgress = args.transition_name.toLowerCase() === "in progress"
52+
if (isInProgress && currentUserEmail) {
53+
const accountId = await resolveAccountIdByEmail(client, currentUserEmail)
54+
if (accountId) {
55+
await client.issues.assignIssue({ issueIdOrKey: args.issue_key, accountId })
56+
notes.push(`Assigned to ${currentUserEmail}`)
57+
}
58+
}
59+
60+
return notes.join("\n")
4961
} catch (error: unknown) {
5062
const e = error as { status?: number; message?: string }
5163
return `Failed to transition ${args.issue_key}: ${e.status ?? ""} ${e.message ?? String(error)}`

tools/lib/jira-client.ts

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,15 @@ function readJiraConfig(): { base_url?: string; project_key?: string } {
1212
}
1313

1414
export function getJiraClient():
15-
| { client: Version3Client; projectKey: string; host: string }
15+
| { client: Version3Client; projectKey: string; host: string; currentUserEmail: string | null }
1616
| { error: string } {
1717
const jiraConfig = readJiraConfig()
1818
const host = (process.env.JIRA_BASE_URL ?? jiraConfig.base_url ?? "").replace(/\/$/, "")
1919
const projectKey = process.env.JIRA_PROJECT_KEY ?? jiraConfig.project_key ?? ""
2020
const email = process.env.JIRA_EMAIL
2121
const apiToken = process.env.JIRA_API_TOKEN
22+
// The auth email is the current user
23+
const currentUserEmail = process.env.JIRA_EMAIL ?? null
2224

2325
if (!host) {
2426
return {
@@ -39,7 +41,26 @@ export function getJiraClient():
3941
authentication: { basic: { email, apiToken } },
4042
})
4143

42-
return { client, projectKey, host }
44+
return { client, projectKey, host, currentUserEmail }
45+
}
46+
47+
/**
48+
* Resolve the accountId for a given email by searching Jira users.
49+
* Returns null if not found.
50+
*/
51+
export async function resolveAccountIdByEmail(
52+
client: Version3Client,
53+
email: string
54+
): Promise<string | null> {
55+
try {
56+
const users = await client.userSearch.findUsers({ query: email, maxResults: 5 })
57+
const match = users.find(
58+
(u) => u.emailAddress?.toLowerCase() === email.toLowerCase()
59+
) ?? users[0]
60+
return match?.accountId ?? null
61+
} catch {
62+
return null
63+
}
4364
}
4465

4566
/** Convert plain text to Atlassian Document Format (ADF) for Jira API v3 */

0 commit comments

Comments
 (0)