Skill: dejanb/clawhub-jira-pat-skill (version 0.0.1)

# Jira PAT Skill Manage Jira issues on self-hosted/enterprise Jira instances using Personal Access Tokens (PAT). This skill is designed for environments where Basic Auth doesn't work due to SSO/SAML authentication. ## When to Use This Skill Use this skill when working with: - Self-hosted Jira instances (e.g., Red Hat, enterprise deployments) - Jira instances with SSO/SAML authentication - Environments where `jira-cli` or Basic Auth fails **Note:** For Atlassian Cloud with email + API token auth, use the `clawdbot-jira-skill` instead. ## Prerequisites 1. **Personal Access Token (PAT)**: Create one in Jira: - Go to your Jira profile → Personal Access Tokens - Create a new token with appropriate permissions - Store it in environment variable `JIRA_PAT` 2. **Jira Base URL**: Your Jira instance URL in `JIRA_URL` ## Environment Variables ```bash export JIRA_PAT="your-personal-access-token" export JIRA_URL="https://issues.example.com" ``` ## Tools This skill uses `curl` and `jq` for all operations. ## Instructions ### Get Issue Details Fetch full details of a Jira issue: ```bash curl -s -H "Authorization: Bearer $JIRA_PAT" \ "$JIRA_URL/rest/api/2/issue/PROJECT-123" | jq ``` Get specific fields only: ```bash curl -s -H "Authorization: Bearer $JIRA_PAT" \ "$JIRA_URL/rest/api/2/issue/PROJECT-123?fields=summary,status,description" | jq ``` ### Search Issues (JQL) ```bash # Find child issues of an epic curl -s -H "Authorization: Bearer $JIRA_PAT" \ "$JIRA_URL/rest/api/2/search?jql=parent=EPIC-123" | jq # Complex queries (URL-encoded) curl -s -H "Authorization: Bearer $JIRA_PAT" \ "$JIRA_URL/rest/api/2/search?jql=project%3DPROJ%20AND%20status%3DOpen" | jq ``` Common JQL patterns: - `parent=EPIC-123` - Child issues of an epic - `project=PROJ AND status=Open` - Open issues in project - `assignee=currentUser()` - Your assigned issues - `labels=security` - Issues with specific label - `updated >= -7d` - Recently updated ### Get Available Transitions Before changing status, query available transitions: ```bash curl -s -H "Authorization: Bearer $JIRA_PAT" \ "$JIRA_URL/rest/api/2/issue/PROJECT-123/transitions" | jq '.transitions[] | {id, name}' ``` ### Transition (Change Status) Close an issue with a comment: ```bash curl -s -X POST \ -H "Authorization: Bearer $JIRA_PAT" \ -H "Content-Type: application/json" \ -d '{ "transition": {"id": "61"}, "update": { "comment": [{"add": {"body": "Closed via API"}}] } }' \ "$JIRA_URL/rest/api/2/issue/PROJECT-123/transitions" ``` ### Add a Comment ```bash curl -s -X POST \ -H "Authorization: Bearer $JIRA_PAT" \ -H "Content-Type: application/json" \ -d '{"body": "Comment added via API."}' \ "$JIRA_URL/rest/api/2/issue/PROJECT-123/comment" ``` ### Update Issue Fields ```bash curl -s -X PUT \ -H "Authorization: Bearer $JIRA_PAT" \ -H "Content-Type: application/json" \ -d '{ "fields": { "summary": "Updated summary", "labels": ["api", "automated"] } }' \ "$JIRA_URL/rest/api/2/issue/PROJECT-123" ``` ### Create an Issue ```bash curl -s -X POST \ -H "Authorization: Bearer $JIRA_PAT" \ -H "Content-Type: application/json" \ -d '{ "fields": { "project": {"key": "PROJ"}, "summary": "New issue via API", "description": "Issue description", "issuetype": {"name": "Task"}, "parent": {"key": "EPIC-123"} } }' \ "$JIRA_URL/rest/api/2/issue" ``` ## Useful jq Filters ```bash # Summary and status jq '{key: .key, summary: .fields.summary, status: .fields.status.name}' # List search results jq '.issues[] | {key: .key, summary: .fields.summary, status: .fields.status.name}' # Issue links jq '.fields.issuelinks[] | {type: .type.name, key: (.inwardIssue // .outwardIssue).key}' ``` ## Troubleshooting | Error | Cause | Solution | |-------|-------|----------| | 401 Unauthorized | Invalid/expired PAT | Regenerate token, check `Bearer` format | | 404 Not Found | Issue doesn't exist or no access | Verify issue key and permissions | | 400 Bad Request on transition | Invalid transition ID | Query available transitions first | ## Comparison with Basic Auth Skills This skill uses **Bearer token authentication** (`Authorization: Bearer <PAT>`), which works with self-hosted Jira instances using SSO/SAML. For Atlassian Cloud with email + API token, use skills that implement Basic Auth instead.

path: _meta.json
content: {
  "owner": "dejanb",
  "slug": "jira-pat-skill",
  "name": "Jira PAT",
  "description": "Manage Jira issues on self-hosted/enterprise instances using Personal Access Tokens (PAT). For SSO/SAML environments where Basic Auth fails.",
  "tags": ["jira", "enterprise", "sso", "pat", "issue-tracking"],
  "releases": []
}


path: scripts/jira-pat.sh
content: #!/usr/bin/env bash
# Jira PAT Helper Functions
# For self-hosted Jira with SSO/SAML authentication

set -euo pipefail

# Verify required environment variables
check_env() {
  if [[ -z "${JIRA_PAT:-}" ]]; then
    echo "Error: JIRA_PAT environment variable not set" >&2
    exit 1
  fi
  if [[ -z "${JIRA_URL:-}" ]]; then
    echo "Error: JIRA_URL environment variable not set" >&2
    exit 1
  fi
}

# Get issue details
jira_get() {
  check_env
  local issue_key="$1"
  curl -s -H "Authorization: Bearer $JIRA_PAT" \
    "$JIRA_URL/rest/api/2/issue/$issue_key" | jq
}

# Get issue summary (compact view)
jira_summary() {
  check_env
  local issue_key="$1"
  curl -s -H "Authorization: Bearer $JIRA_PAT" \
    "$JIRA_URL/rest/api/2/issue/$issue_key" | \
    jq -r '"\(.key): \(.fields.summary) [\(.fields.status.name)]"'
}

# Search issues with JQL
jira_search() {
  check_env
  local jql="$1"
  curl -s -H "Authorization: Bearer $JIRA_PAT" \
    "$JIRA_URL/rest/api/2/search?jql=$jql" | \
    jq '.issues[] | "\(.key): \(.fields.summary) [\(.fields.status.name)]"' -r
}

# List epic children
jira_children() {
  check_env
  local epic_key="$1"
  curl -s -H "Authorization: Bearer $JIRA_PAT" \
    "$JIRA_URL/rest/api/2/search?jql=parent=$epic_key" | \
    jq '.issues[] | "\(.key): \(.fields.summary) [\(.fields.status.name)]"' -r
}

# Get available transitions
jira_transitions() {
  check_env
  local issue_key="$1"
  curl -s -H "Authorization: Bearer $JIRA_PAT" \
    "$JIRA_URL/rest/api/2/issue/$issue_key/transitions" | \
    jq '.transitions[] | "\(.id): \(.name)"' -r
}

# Transition an issue
jira_transition() {
  check_env
  local issue_key="$1"
  local transition_id="$2"
  local comment="${3:-}"

  local payload
  if [[ -n "$comment" ]]; then
    payload=$(jq -n --arg tid "$transition_id" --arg comment "$comment" \
      '{transition: {id: $tid}, update: {comment: [{add: {body: $comment}}]}}')
  else
    payload=$(jq -n --arg tid "$transition_id" '{transition: {id: $tid}}')
  fi

  curl -s -X POST \
    -H "Authorization: Bearer $JIRA_PAT" \
    -H "Content-Type: application/json" \
    -d "$payload" \
    "$JIRA_URL/rest/api/2/issue/$issue_key/transitions"
  echo "Transitioned $issue_key"
}

# Add a comment
jira_comment() {
  check_env
  local issue_key="$1"
  local body="$2"

  curl -s -X POST \
    -H "Authorization: Bearer $JIRA_PAT" \
    -H "Content-Type: application/json" \
    -d "$(jq -n --arg body "$body" '{body: $body}')" \
    "$JIRA_URL/rest/api/2/issue/$issue_key/comment" | jq
}

# Main dispatcher
case "${1:-help}" in
  get) jira_get "$2" ;;
  summary) jira_summary "$2" ;;
  search) jira_search "$2" ;;
  children) jira_children "$2" ;;
  transitions) jira_transitions "$2" ;;
  transition) jira_transition "$2" "$3" "${4:-}" ;;
  comment) jira_comment "$2" "$3" ;;
  help|*)
    cat <<EOF
Jira PAT Helper - For self-hosted Jira with SSO

Usage: jira-pat.sh <command> [args]

Commands:
  get <issue>           Get full issue details
  summary <issue>       Get compact issue summary
  search <jql>          Search with JQL query
  children <epic>       List children of an epic
  transitions <issue>   List available transitions
  transition <issue> <id> [comment]  Transition issue
  comment <issue> <body>  Add a comment

Environment:
  JIRA_PAT   Personal Access Token (required)
  JIRA_URL   Jira instance URL (required)
EOF
    ;;
esac


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: suspicious | SkillSpector: clean (LOW)