---
name: jira-issue-ops
description: Deterministic Jira Cloud project and issue operations with OAuth 2.0 (3LO) and API-token basic auth. Use for project search/view and issue CRUD (create/read/update/delete) with JQL search. Includes a production-ready shell script for repeatable API execution.
---

# Jira Issue Ops

Use this skill to perform Jira Cloud API operations with high reliability and low ambiguity.

## Included resources

- `scripts/jira_api.sh`: deterministic CLI wrapper over Jira REST API v3.
- `references/payload-examples.md`: ready-to-use JSON payload templates for create/update/search.

Use the script first. Use raw curl only for debugging.

## Official docs

- OAuth 2.0 (3LO):
  - https://developer.atlassian.com/cloud/confluence/oauth-2-3lo-apps/#3-2-construct-the-request-url
- Basic auth with API token:
  - https://developer.atlassian.com/cloud/jira/platform/basic-auth-for-rest-apis/
- Jira REST API v3 Projects group:
  - https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-projects/#api-group-projects

## Auth modes

### Mode A: OAuth 2.0 (3LO)

Set:
- `JIRA_AUTH_MODE=oauth` (default)
- `JIRA_TOKEN` = OAuth access token
- `JIRA_SITE_URL` = site URL (recommended)
- `JIRA_CLOUD_ID` = optional (auto-resolved from `accessible-resources` if absent)

Routing:
- Script calls `https://api.atlassian.com/ex/jira/{cloudid}/rest/api/3/...`

### Mode B: Basic auth (API token)

Set:
- `JIRA_AUTH_MODE=basic`
- `JIRA_EMAIL` = Atlassian account email
- `JIRA_TOKEN` = API token
- `JIRA_SITE_URL` = site URL (e.g. `https://tidb.atlassian.net`)

Routing:
- Script calls `https://<site>.atlassian.net/rest/api/3/...`
- Header uses `Authorization: Basic base64(email:api_token)`

## Scope of this skill

- Project:
  - search/list (`GET /project/search`) with paging (`startAt`, `maxResults`) and optional ordering (`orderBy`)
  - view (`GET /project/{projectIdOrKey}`)
- Issue:
  - create (`POST /issue`)
  - read (`GET /issue/{issueIdOrKey}`)
  - update (`PUT /issue/{issueIdOrKey}`)
  - delete (`DELETE /issue/{issueIdOrKey}`)
  - JQL search (`POST /search/jql`)

## Deterministic operation workflow

1. Pick auth mode (`oauth` or `basic`).
2. Export required env vars for that mode.
3. Use `.env` auto-load (default) or `JIRA_ENV_FILE` override.
4. Run script subcommands for project/issue operations.

Avoid ad-hoc endpoint switching during the same task.

## Environment contract

- Optional: `JIRA_AUTH_MODE` (`oauth|basic`, default `oauth`)
- Optional: `JIRA_ALLOWED_SITE_HOST` (default `tidb.atlassian.net`)
- Optional: `AUDIT_LOG_DIR` (default `${SKILL_DIR}/audit-logs`)
- Optional: `JIRA_AUDIT_CONTEXT` (reason/background text written to audit logs)
- OAuth mode:
  - `JIRA_TOKEN` (required)
  - `JIRA_SITE_URL` (recommended)
  - `JIRA_CLOUD_ID` (optional)
- Basic mode:
  - `JIRA_EMAIL` (required)
  - `JIRA_TOKEN` (required)
  - `JIRA_SITE_URL` (required)

The script auto-loads `${SKILL_DIR}/.env` by default.
Override with `JIRA_ENV_FILE=/path/to/custom.env`.

Optional retry tuning:
- `JIRA_MAX_RETRIES` (default `3`)
- `JIRA_RETRY_BASE_SEC` (default `2`)

## Script commands

```bash
# Show help
./scripts/jira_api.sh --help

# OAuth: build authorize URL
./scripts/jira_api.sh auth-url \
  --client-id "$CLIENT_ID" \
  --redirect-uri "$REDIRECT_URI" \
  --scopes "read:jira-work write:jira-work read:jira-user offline_access" \
  --state "$STATE"

# OAuth: exchange code for token
./scripts/jira_api.sh token-exchange \
  --client-id "$CLIENT_ID" \
  --client-secret "$CLIENT_SECRET" \
  --code "$AUTH_CODE" \
  --redirect-uri "$REDIRECT_URI"
```

### OAuth operation example

```bash
export JIRA_AUTH_MODE=oauth
export JIRA_TOKEN="<access_token>"
export JIRA_SITE_URL="https://tidb.atlassian.net"

# Optional debug
./scripts/jira_api.sh accessible-resources

./scripts/jira_api.sh project-search --query O11Y --max-results 50 --start-at 0 --order-by "+key"
./scripts/jira_api.sh issue-get --issue O11Y-2185711
```

### Basic operation example

```bash
export JIRA_AUTH_MODE=basic
export JIRA_EMAIL="you@example.com"
export JIRA_TOKEN="<api_token>"
export JIRA_SITE_URL="https://tidb.atlassian.net"

./scripts/jira_api.sh issue-get --issue O11Y-2185711

# high-risk delete (requires human confirmation token)
./scripts/jira_api.sh issue-delete --issue O11Y-2185711 --confirm-delete O11Y-2185711 --human-confirm YES
```

## Reliability rules

- Use the script for all writes.
- Keep payloads in files; avoid inline shell JSON for write operations.
- Fail hard on non-2xx responses.
- Retry only on `429` and `5xx` with backoff.
- Enforce site safety checks before project/issue operations:
  - `JIRA_SITE_URL` must be `https://`
  - host must match `JIRA_ALLOWED_SITE_HOST` (default `tidb.atlassian.net`)
- Require explicit delete confirmation (`--confirm-delete` must match issue key).
- High-risk operations require explicit human confirmation token (delete uses `--human-confirm YES`).

## Security rules

- Never print or persist `client_secret` / access token in logs.
- Never hardcode credentials in script or repository files.
- Redact secrets before sharing outputs.
- For any delete operation, **double check with the user first** before execution.
  - Keep `--confirm-delete` in command.
  - Require explicit user confirmation in chat (for example: `confirm delete ISSUE-123`).
  - Command must include `--human-confirm YES`.
- All write operations must write an audit record *before* execution (daily log split in `AUDIT_LOG_DIR/YYYY-MM-DD.log`).
- Audit logs must not contain secrets or raw payload bodies.

## Error triage

- `401/403`: token invalid/expired, missing scope, or user lacks permission.
- `404`: wrong `cloudid` / project key / issue key, or permission visibility issue.
- `project/search total=0`: often means caller lacks Jira product access or `Browse projects` permission.
- `400`: payload format error or required field missing.
- `429`: rate limit; script retries automatically.

When reporting failures, include:

- command
- endpoint
- HTTP status
- Jira `errorMessages` / `errors`
- exact remediation (field/scope/permission fix)
