CtrlK
BlogDocsLog inGet started
Tessl Logo

jira-issues

Create, read, update, and delete Jira issues. Use when managing Stories, Tasks, Bugs, or Epics - includes field updates and metadata.

Install with Tessl CLI

npx tessl i github:NeverSight/skills_feed --skill jira-issues
What are skills?

86

Quality

82%

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Optimize this skill with Tessl

npx tessl skill review --optimize ./data/skills-md/01000001-01001110/agent-jira-skills/jira-issues/SKILL.md
SKILL.md
Review
Evals

Jira Issues Skill

Purpose

Create, read, update, and delete issues in Jira Cloud. Manage issue fields, transitions, and metadata.

When to Use

  • Creating new issues (Story, Task, Bug, Epic)
  • Updating issue fields (summary, description, assignee, etc.)
  • Reading issue details
  • Deleting issues

Prerequisites

  • Authenticated JiraClient (see jira-auth skill)
  • Project access permissions
  • Issue type IDs for the target project

Implementation Pattern

Step 1: Define Issue Types

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

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),
  });
}

Step 3: Get Issue

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

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

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

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;
}

curl Examples

Create Issue

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

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

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

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

API Endpoints Summary

OperationMethodPath
Create issuePOST/issue
Get issueGET/issue/{issueIdOrKey}
Update issuePUT/issue/{issueIdOrKey}
Delete issueDELETE/issue/{issueIdOrKey}

Required Fields by Issue Type

Story

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

Task

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

Bug

  • project.key (required)
  • summary (required)
  • issuetype.name = "Bug" (required)
  • description (recommended)

Description Format (ADF)

Jira uses Atlassian Document Format for rich text:

{
  "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

References

  • Issues API
  • Atlassian Document Format

Version History

  • 2025-12-10: Created
Repository
NeverSight/skills_feed
Last updated
Created

Is this your skill?

If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.