jira-issues

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Jira Issues Skill

Jira Issues Skill

Purpose

用途

Create, read, update, and delete issues in Jira Cloud. Manage issue fields, transitions, and metadata.
在Jira Cloud中创建、读取、更新和删除问题。管理问题字段、状态流转和元数据。

When to Use

使用场景

  • Creating new issues (Story, Task, Bug, Epic)
  • Updating issue fields (summary, description, assignee, etc.)
  • Reading issue details
  • Deleting issues
  • 创建新问题(Story、Task、Bug、Epic)
  • 更新问题字段(摘要、描述、经办人等)
  • 读取问题详情
  • 删除问题

Prerequisites

前提条件

  • Authenticated JiraClient (see jira-auth skill)
  • Project access permissions
  • Issue type IDs for the target project
  • 已认证的JiraClient(请查看jira-auth技能)
  • 项目访问权限
  • 目标项目的问题类型ID

Implementation Pattern

实现模式

Step 1: Define Issue Types

步骤1:定义问题类型

typescript
interface JiraIssue {
  id: string;
  key: string;
  self: string;
  fields: {
    summary: string;
    description?: {
      type: 'doc';
      version: 1;
      content: Array<{
        type: string;
        content?: Array<{
          type: string;
          text: string;
        }>;
      }>;
    };
    status: { name: string; id: string };
    assignee?: { accountId: string; displayName: string };
    reporter?: { accountId: string; displayName: string };
    priority?: { name: string; id: string };
    issuetype: { name: string; id: string };
    project: { key: string; id: string };
    created: string;
    updated: string;
    labels?: string[];
    components?: Array<{ id: string; name: string }>;
  };
}

interface CreateIssueInput {
  projectKey: string;
  summary: string;
  issueType: 'Story' | 'Task' | 'Bug' | 'Epic' | string;
  description?: string;
  assigneeAccountId?: string;
  labels?: string[];
  priority?: string;
}
typescript
interface JiraIssue {
  id: string;
  key: string;
  self: string;
  fields: {
    summary: string;
    description?: {
      type: 'doc';
      version: 1;
      content: Array<{
        type: string;
        content?: Array<{
          type: string;
          text: string;
        }>;
      }>;
    };
    status: { name: string; id: string };
    assignee?: { accountId: string; displayName: string };
    reporter?: { accountId: string; displayName: string };
    priority?: { name: string; id: string };
    issuetype: { name: string; id: string };
    project: { key: string; id: string };
    created: string;
    updated: string;
    labels?: string[];
    components?: Array<{ id: string; name: string }>;
  };
}

interface CreateIssueInput {
  projectKey: string;
  summary: string;
  issueType: 'Story' | 'Task' | 'Bug' | 'Epic' | string;
  description?: string;
  assigneeAccountId?: string;
  labels?: string[];
  priority?: string;
}

Step 2: Create Issue

步骤2:创建问题

typescript
async function createIssue(
  client: JiraClient,
  input: CreateIssueInput
): Promise<{ id: string; key: string; self: string }> {
  const body: any = {
    fields: {
      project: { key: input.projectKey },
      summary: input.summary,
      issuetype: { name: input.issueType },
    },
  };

  // Add description in Atlassian Document Format (ADF)
  if (input.description) {
    body.fields.description = {
      type: 'doc',
      version: 1,
      content: [
        {
          type: 'paragraph',
          content: [
            {
              type: 'text',
              text: input.description,
            },
          ],
        },
      ],
    };
  }

  if (input.assigneeAccountId) {
    body.fields.assignee = { id: input.assigneeAccountId };
  }

  if (input.labels) {
    body.fields.labels = input.labels;
  }

  if (input.priority) {
    body.fields.priority = { name: input.priority };
  }

  return client.request<{ id: string; key: string; self: string }>('/issue', {
    method: 'POST',
    body: JSON.stringify(body),
  });
}
typescript
async function createIssue(
  client: JiraClient,
  input: CreateIssueInput
): Promise<{ id: string; key: string; self: string }> {
  const body: any = {
    fields: {
      project: { key: input.projectKey },
      summary: input.summary,
      issuetype: { name: input.issueType },
    },
  };

  // 添加Atlassian文档格式(ADF)的描述
  if (input.description) {
    body.fields.description = {
      type: 'doc',
      version: 1,
      content: [
        {
          type: 'paragraph',
          content: [
            {
              type: 'text',
              text: input.description,
            },
          ],
        },
      ],
    };
  }

  if (input.assigneeAccountId) {
    body.fields.assignee = { id: input.assigneeAccountId };
  }

  if (input.labels) {
    body.fields.labels = input.labels;
  }

  if (input.priority) {
    body.fields.priority = { name: input.priority };
  }

  return client.request<{ id: string; key: string; self: string }>('/issue', {
    method: 'POST',
    body: JSON.stringify(body),
  });
}

Step 3: Get Issue

步骤3:获取问题

typescript
async function getIssue(
  client: JiraClient,
  issueKeyOrId: string,
  options: {
    fields?: string[];
    expand?: string[];
  } = {}
): Promise<JiraIssue> {
  const params = new URLSearchParams();
  if (options.fields) params.set('fields', options.fields.join(','));
  if (options.expand) params.set('expand', options.expand.join(','));

  const query = params.toString() ? `?${params.toString()}` : '';
  return client.request<JiraIssue>(`/issue/${issueKeyOrId}${query}`);
}
typescript
async function getIssue(
  client: JiraClient,
  issueKeyOrId: string,
  options: {
    fields?: string[];
    expand?: string[];
  } = {}
): Promise<JiraIssue> {
  const params = new URLSearchParams();
  if (options.fields) params.set('fields', options.fields.join(','));
  if (options.expand) params.set('expand', options.expand.join(','));

  const query = params.toString() ? `?${params.toString()}` : '';
  return client.request<JiraIssue>(`/issue/${issueKeyOrId}${query}`);
}

Step 4: Update Issue

步骤4:更新问题

typescript
interface UpdateIssueInput {
  summary?: string;
  description?: string;
  assigneeAccountId?: string | null;
  labels?: string[];
  priority?: string;
}

async function updateIssue(
  client: JiraClient,
  issueKeyOrId: string,
  input: UpdateIssueInput
): Promise<void> {
  const body: any = { fields: {} };

  if (input.summary) {
    body.fields.summary = input.summary;
  }

  if (input.description !== undefined) {
    body.fields.description = input.description
      ? {
          type: 'doc',
          version: 1,
          content: [
            {
              type: 'paragraph',
              content: [{ type: 'text', text: input.description }],
            },
          ],
        }
      : null;
  }

  if (input.assigneeAccountId !== undefined) {
    body.fields.assignee = input.assigneeAccountId
      ? { id: input.assigneeAccountId }
      : null;
  }

  if (input.labels) {
    body.fields.labels = input.labels;
  }

  if (input.priority) {
    body.fields.priority = { name: input.priority };
  }

  await client.request(`/issue/${issueKeyOrId}`, {
    method: 'PUT',
    body: JSON.stringify(body),
  });
}
typescript
interface UpdateIssueInput {
  summary?: string;
  description?: string;
  assigneeAccountId?: string | null;
  labels?: string[];
  priority?: string;
}

async function updateIssue(
  client: JiraClient,
  issueKeyOrId: string,
  input: UpdateIssueInput
): Promise<void> {
  const body: any = { fields: {} };

  if (input.summary) {
    body.fields.summary = input.summary;
  }

  if (input.description !== undefined) {
    body.fields.description = input.description
      ? {
          type: 'doc',
          version: 1,
          content: [
            {
              type: 'paragraph',
              content: [{ type: 'text', text: input.description }],
            },
          ],
        }
      : null;
  }

  if (input.assigneeAccountId !== undefined) {
    body.fields.assignee = input.assigneeAccountId
      ? { id: input.assigneeAccountId }
      : null;
  }

  if (input.labels) {
    body.fields.labels = input.labels;
  }

  if (input.priority) {
    body.fields.priority = { name: input.priority };
  }

  await client.request(`/issue/${issueKeyOrId}`, {
    method: 'PUT',
    body: JSON.stringify(body),
  });
}

Step 5: Delete Issue

步骤5:删除问题

typescript
async function deleteIssue(
  client: JiraClient,
  issueKeyOrId: string,
  deleteSubtasks: boolean = false
): Promise<void> {
  const query = deleteSubtasks ? '?deleteSubtasks=true' : '';
  await client.request(`/issue/${issueKeyOrId}${query}`, {
    method: 'DELETE',
  });
}
typescript
async function deleteIssue(
  client: JiraClient,
  issueKeyOrId: string,
  deleteSubtasks: boolean = false
): Promise<void> {
  const query = deleteSubtasks ? '?deleteSubtasks=true' : '';
  await client.request(`/issue/${issueKeyOrId}${query}`, {
    method: 'DELETE',
  });
}

Step 6: Bulk Create Issues

步骤6:批量创建问题

typescript
async function bulkCreateIssues(
  client: JiraClient,
  issues: CreateIssueInput[]
): Promise<Array<{ id: string; key: string; self: string }>> {
  const results: Array<{ id: string; key: string; self: string }> = [];

  // Jira doesn't have a native bulk create, so we batch with Promise.all
  const batches = [];
  const batchSize = 10;

  for (let i = 0; i < issues.length; i += batchSize) {
    batches.push(issues.slice(i, i + batchSize));
  }

  for (const batch of batches) {
    const batchResults = await Promise.all(
      batch.map((issue) => createIssue(client, issue))
    );
    results.push(...batchResults);
  }

  return results;
}
typescript
async function bulkCreateIssues(
  client: JiraClient,
  issues: CreateIssueInput[]
): Promise<Array<{ id: string; key: string; self: string }>> {
  const results: Array<{ id: string; key: string; self: string }> = [];

  // Jira没有原生的批量创建接口,因此我们使用Promise.all进行分批处理
  const batches = [];
  const batchSize = 10;

  for (let i = 0; i < issues.length; i += batchSize) {
    batches.push(issues.slice(i, i + batchSize));
  }

  for (const batch of batches) {
    const batchResults = await Promise.all(
      batch.map((issue) => createIssue(client, issue))
    );
    results.push(...batchResults);
  }

  return results;
}

curl Examples

curl示例

Create Issue

创建问题

bash
curl -X POST "$JIRA_BASE_URL/rest/api/3/issue" \
  -H "Authorization: Basic $(echo -n 'email:token' | base64)" \
  -H "Content-Type: application/json" \
  -d '{
    "fields": {
      "project": { "key": "SCRUM" },
      "summary": "New feature implementation",
      "issuetype": { "name": "Story" },
      "description": {
        "type": "doc",
        "version": 1,
        "content": [
          {
            "type": "paragraph",
            "content": [{ "type": "text", "text": "Description here" }]
          }
        ]
      }
    }
  }'
bash
curl -X POST "$JIRA_BASE_URL/rest/api/3/issue" \
  -H "Authorization: Basic $(echo -n 'email:token' | base64)" \
  -H "Content-Type: application/json" \
  -d '{
    "fields": {
      "project": { "key": "SCRUM" },
      "summary": "New feature implementation",
      "issuetype": { "name": "Story" },
      "description": {
        "type": "doc",
        "version": 1,
        "content": [
          {
            "type": "paragraph",
            "content": [{ "type": "text", "text": "Description here" }]
          }
        ]
      }
    }
  }'

Get Issue

获取问题

bash
curl -X GET "$JIRA_BASE_URL/rest/api/3/issue/SCRUM-123" \
  -H "Authorization: Basic $(echo -n 'email:token' | base64)" \
  -H "Accept: application/json"
bash
curl -X GET "$JIRA_BASE_URL/rest/api/3/issue/SCRUM-123" \
  -H "Authorization: Basic $(echo -n 'email:token' | base64)" \
  -H "Accept: application/json"

Update Issue

更新问题

bash
curl -X PUT "$JIRA_BASE_URL/rest/api/3/issue/SCRUM-123" \
  -H "Authorization: Basic $(echo -n 'email:token' | base64)" \
  -H "Content-Type: application/json" \
  -d '{
    "fields": {
      "summary": "Updated summary"
    }
  }'
bash
curl -X PUT "$JIRA_BASE_URL/rest/api/3/issue/SCRUM-123" \
  -H "Authorization: Basic $(echo -n 'email:token' | base64)" \
  -H "Content-Type: application/json" \
  -d '{
    "fields": {
      "summary": "Updated summary"
    }
  }'

Delete Issue

删除问题

bash
curl -X DELETE "$JIRA_BASE_URL/rest/api/3/issue/SCRUM-123" \
  -H "Authorization: Basic $(echo -n 'email:token' | base64)"
bash
curl -X DELETE "$JIRA_BASE_URL/rest/api/3/issue/SCRUM-123" \
  -H "Authorization: Basic $(echo -n 'email:token' | base64)"

API Endpoints Summary

API端点汇总

OperationMethodPath
Create issuePOST
/issue
Get issueGET
/issue/{issueIdOrKey}
Update issuePUT
/issue/{issueIdOrKey}
Delete issueDELETE
/issue/{issueIdOrKey}
操作方法路径
创建问题POST
/issue
获取问题GET
/issue/{issueIdOrKey}
更新问题PUT
/issue/{issueIdOrKey}
删除问题DELETE
/issue/{issueIdOrKey}

Required Fields by Issue Type

按问题类型划分的必填字段

Story

Story

  • project.key
    (required)
  • summary
    (required)
  • issuetype.name
    = "Story" (required)
  • project.key
    (必填)
  • summary
    (必填)
  • issuetype.name
    = "Story"(必填)

Task

Task

  • project.key
    (required)
  • summary
    (required)
  • issuetype.name
    = "Task" (required)
  • project.key
    (必填)
  • summary
    (必填)
  • issuetype.name
    = "Task"(必填)

Bug

Bug

  • project.key
    (required)
  • summary
    (required)
  • issuetype.name
    = "Bug" (required)
  • description
    (recommended)
  • project.key
    (必填)
  • summary
    (必填)
  • issuetype.name
    = "Bug"(必填)
  • description
    (推荐填写)

Description Format (ADF)

描述格式(ADF)

Jira uses Atlassian Document Format for rich text:
json
{
  "type": "doc",
  "version": 1,
  "content": [
    {
      "type": "paragraph",
      "content": [
        { "type": "text", "text": "Normal text" }
      ]
    },
    {
      "type": "paragraph",
      "content": [
        {
          "type": "text",
          "text": "Bold text",
          "marks": [{ "type": "strong" }]
        }
      ]
    }
  ]
}
Jira使用Atlassian文档格式(ADF)来处理富文本:
json
{
  "type": "doc",
  "version": 1,
  "content": [
    {
      "type": "paragraph",
      "content": [
        { "type": "text", "text": "Normal text" }
      ]
    },
    {
      "type": "paragraph",
      "content": [
        {
          "type": "text",
          "text": "Bold text",
          "marks": [{ "type": "strong" }]
        }
      ]
    }
  ]
}

Common Mistakes

常见错误

  • Using plain text for description instead of ADF format
  • Not using account ID for assignee (email doesn't work)
  • Forgetting project key in create request
  • Using issue type name that doesn't exist in project
  • 使用纯文本格式填写描述而非ADF格式
  • 未使用账户ID指定经办人(邮箱格式无效)
  • 创建请求时遗漏项目Key
  • 使用项目中不存在的问题类型名称

References

参考资料

Version History

版本历史

  • 2025-12-10: Created
  • 2025-12-10:创建