Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions docs/project-config-contract.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,30 @@ that skills need to create issues, query tasks, and update fields.
| GitHub Issue custom field | Custom field ID containing a GitHub issue URL (plain string or ADF) | `customfield_10747` |
| Default labels | Labels to apply to AI-generated issues | `ai-generated-jira` |

#### Discovery vs. Configuration

The Jira Configuration section stores **project-level identifiers** that remain constant across all tasks and workflows. These are discovered once during `/setup` and reused by all skills.

**What goes in CLAUDE.md** (project-level, stable):
- **Project key** - never changes for a project (e.g., `TC`)
- **Cloud ID** - identifies your Jira instance
- **Feature issue type ID** - discovered via `get_project_metadata` during `/setup`
- **Custom field IDs** (optional) - discovered via `get_project_metadata`, prompted during `/setup`

**What is discovered at runtime** (task-specific, varies by workflow):
- **Transition IDs** - vary by issue type and workflow configuration, discovered via `get_transitions`
- **User account IDs** - discovered via `get_user_info` when assigning issues
- **Available transitions for an issue** - queried via `get_transitions` before each transition

Skills never hardcode transition IDs or assume specific workflow configurations. They always query available transitions and match by status name.

**Example**: To transition an issue to "In Review" status:
1. Query available transitions: `get_transitions TC-123`
2. Find the transition ID where `name == "In Review"`
3. Execute transition: `transition_issue TC-123 --transition-id <discovered-id>`

This ensures the workflow adapts to each project's specific Jira configuration.

#### Optional subsection: REST API Credentials (MCP Fallback)

When Atlassian MCP is unavailable due to organizational policies, skills can fall back to JIRA REST API v3. This subsection stores the credentials needed for REST API access.
Expand Down
45 changes: 39 additions & 6 deletions plugins/sdlc-workflow/shared/jira-access-strategy.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,9 @@ Use the REST API only when:

**How to get an API token**: https://id.atlassian.com/manage-profile/security/api-tokens

### Using the Python Client
### Using the JIRA REST API Python Client

All REST API operations use the Python client at `scripts/jira-client.py`.
All REST API operations use the JIRA REST API Python client script at `scripts/jira-client.py`.

**Script Location:**
The script is in the plugin cache. Extract the plugin root from the skill base directory (shown in the skill invocation header) and cd to it before running commands. See `shared/jira-rest-fallback.md` for details.
Expand Down Expand Up @@ -87,7 +87,19 @@ cd <plugin-root> && \
--comment-md "Comment text in markdown"

# Transition issue
cd <plugin-root> && python3 scripts/jira-client.py transition_issue TC-123 --transition-id 31
# Step 1: Get available transitions for the issue
cd <plugin-root> && python3 scripts/jira-client.py get_transitions TC-123
# Output: [{"id": "31", "name": "In Progress"}, {"id": "41", "name": "In Review"}, ...]

# Step 2: Find the transition ID for your desired status by name
# Example: Extract ID for "In Review"
TRANSITION_ID=$(cd <plugin-root> && python3 scripts/jira-client.py get_transitions TC-123 | \
python3 -c "import json,sys; transitions=json.load(sys.stdin); \
print(next((t['id'] for t in transitions if t['name']=='In Review'), None))")

# Step 3: Apply the transition
cd <plugin-root> && python3 scripts/jira-client.py transition_issue TC-123 \
--transition-id "$TRANSITION_ID"

# Create issue link
cd <plugin-root> && \
Expand Down Expand Up @@ -146,16 +158,37 @@ When REST API is used, credentials are stored in CLAUDE.md:

- Project key: TC
- Cloud ID: 2b9e35e3-6bd3-4cec-b838-f4249ee02432
- Feature issue type ID: 10142
- Git Pull Request custom field: customfield_10875
- GitHub Issue custom field: customfield_10747
- Feature issue type ID: 10142 # Discovered via /setup skill → get_project_metadata
- Git Pull Request custom field: customfield_10875 # Optional - discovered via get_project_metadata
- GitHub Issue custom field: customfield_10747 # Optional - discovered via get_project_metadata

<!-- Note: Transition IDs are NOT stored in CLAUDE.md. They vary by workflow
and must be discovered at runtime via get_transitions. -->

### REST API Credentials (MCP Fallback)
- Server URL: https://your-domain.atlassian.net
- Email: user@example.com
- API Token: $JIRA_API_TOKEN # or actual token if full storage chosen
```

### Configuration Categories

Values in Jira Configuration fall into two categories:

**1. Project-level configuration** (stored in CLAUDE.md, discovered once via `/setup`):
- Project key - identifies your Jira project (e.g., `TC`, `PROJ`)
- Cloud ID - your Jira instance identifier
- Feature issue type ID - discovered via `get_project_metadata` during setup
- Custom field IDs (optional) - discovered via `get_project_metadata`

**2. Runtime discovery** (queried dynamically as needed):
- Transition IDs - vary by issue type and workflow, discovered via `get_transitions`
- User account IDs - discovered via `get_user_info` when assigning issues
- Available statuses for an issue - queried via `get_transitions` before each transition

Skills never hardcode transition IDs or assume specific workflow configurations.
They always query available transitions and match by status name (e.g., "In Review").

## Error Handling

### MCP Errors
Expand Down
89 changes: 80 additions & 9 deletions plugins/sdlc-workflow/shared/jira-rest-fallback.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ The **plugin root** is 2 directory levels up from the skill base:
- Skill base: `.../sdlc-workflow/0.6.0/skills/define-feature`
- Plugin root: `.../sdlc-workflow/0.6.0` (go up 2 levels)

**All script invocations must use this pattern:**
**All JIRA REST API Python client script invocations must use this pattern:**
```bash
cd <plugin-root> && python3 scripts/jira-client.py <command>
```
Expand Down Expand Up @@ -240,7 +240,7 @@ Add to ~/.bashrc or ~/.zshrc to persist across sessions.
**Option 3: Don't store**
Skip credential storage. Credentials will be asked for on every MCP failure.

## Using the Python Client
## Using the JIRA REST API Python Client Script

All REST API operations use `scripts/jira-client.py`.

Expand Down Expand Up @@ -289,7 +289,7 @@ cd <plugin-root> && \
--project TC \
--summary "New feature request" \
--description-md "This is the **description** in markdown." \
--issue-type "10142" \
--issue-type "<issue-type-id>" \ # See "Discovering Issue Types" below
--labels ai-generated-jira,feature
```

Expand All @@ -316,14 +316,37 @@ cd <plugin-root> && \
```

**Transition Issue:**

Transitions are workflow-specific and must be discovered at runtime.

```bash
# First, get available transitions
# Step 1: Get available transitions for the issue
cd <plugin-root> && python3 scripts/jira-client.py get_transitions TC-123
```

# Then transition using the ID
cd <plugin-root> && python3 scripts/jira-client.py transition_issue TC-123 --transition-id 31
Example output:
```json
[
{"id": "31", "name": "In Progress"},
{"id": "41", "name": "In Review"},
{"id": "51", "name": "Done"}
]
```

```bash
# Step 2: Parse output to find desired transition by name
# Extract the ID where name matches "In Review"
TRANSITION_ID=$(cd <plugin-root> && python3 scripts/jira-client.py get_transitions TC-123 | \
python3 -c "import json,sys; transitions=json.load(sys.stdin); \
print(next((t['id'] for t in transitions if t['name']=='In Review'), None))")

# Step 3: Apply the transition using discovered ID
cd <plugin-root> && python3 scripts/jira-client.py transition_issue TC-123 \
--transition-id "$TRANSITION_ID"
```

**Note**: Transition IDs vary by project workflow configuration. Always discover them dynamically rather than hardcoding.

**Search with JQL:**
```bash
cd <plugin-root> && \
Expand Down Expand Up @@ -352,9 +375,57 @@ cd <plugin-root> && python3 scripts/jira-client.py get_user_info
cd <plugin-root> && python3 scripts/jira-client.py get_project_metadata TC
```

### Discovering Issue Types

Issue type IDs are project-specific and should be discovered, not hardcoded.

**For one-time configuration** (when setting up CLAUDE.md):

Run the `/setup` skill, which uses `get_project_metadata` to discover available issue types and prompts you to select the Feature type. The ID is then stored in CLAUDE.md.

**For ad-hoc discovery**:

```bash
cd <plugin-root> && python3 scripts/jira-client.py get_project_metadata <project-key>
```

Example output (excerpt):
```json
{
"key": "TC",
"name": "Trustify Konveyor",
"issueTypes": [
{"id": "10142", "name": "Feature"},
{"id": "10143", "name": "Task"},
{"id": "10144", "name": "Story"}
]
}
```

Extract the ID for your desired issue type from the `issueTypes` array.

### Discovering Custom Fields

Custom field IDs (e.g., for Git Pull Request URLs or GitHub Issue links) are also project-specific.

**To discover custom fields**:

```bash
# Get full issue details including all custom fields
cd <plugin-root> && python3 scripts/jira-client.py get_issue <issue-key> --fields "*all"
```

Look for fields named `customfield_*` in the response. Match them to their display names by checking the field values or consulting your Jira admin.

Common custom fields used by sdlc-workflow:
- **Git Pull Request** - stores PR URLs (requires ADF format: inlineCard)
- **GitHub Issue** - stores GitHub issue URLs (plain string or ADF)

Once discovered, add them to your project's CLAUDE.md under `## Jira Configuration`.

## Error Handling

The Python client automatically maps HTTP errors to user-friendly messages:
The JIRA REST API Python client script automatically maps HTTP errors to user-friendly messages:

**401 Unauthorized:**
```
Expand Down Expand Up @@ -405,7 +476,7 @@ If "1. Update token":
## Token Security

**Masking:**
The Python client automatically masks tokens in all output:
The JIRA REST API Python client script automatically masks tokens in all output:
- Full token: `ATATT3xFfGF0T8JxQkVmNzY5MjE3NjE3MDk2`
- Masked: `ATATT3xF...3MDk2`

Expand All @@ -421,7 +492,7 @@ The Python client automatically masks tokens in all output:

## Markdown to ADF Conversion

The Python client automatically converts markdown to Atlassian Document Format (ADF) when creating issues or adding comments.
The JIRA REST API Python client script automatically converts markdown to Atlassian Document Format (ADF) when creating issues or adding comments.

**Supported Markdown:**
- Headings: `# H1` and `## H2`
Expand Down
18 changes: 11 additions & 7 deletions plugins/sdlc-workflow/skills/define-feature/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ When Atlassian MCP is unavailable, this skill may use the Bash tool to invoke th
- ✅ Allowed: `bash -c "python3 scripts/jira-client.py <command>"`
- ❌ Forbidden: any other Bash file modification commands

**Script execution pattern:** See `shared/jira-rest-fallback.md` § "Script Execution Context" for the `cd <plugin-root> && python3 scripts/jira-client.py <command>` pattern and complete implementation guidance.

## Comment Footnote

Every comment posted to Jira by this skill MUST end with the following footnote,
Expand Down Expand Up @@ -116,7 +118,7 @@ Before attempting JIRA operations, determine the access method. This initializat
- If credentials do not exist:
- Follow credential collection flow (see `shared/jira-rest-fallback.md`)
- Collect: Server URL, Email, API Token
- Validate with: `python3 scripts/jira-client.py get_user_info`
- Validate with: `cd <plugin-root> && python3 scripts/jira-client.py get_user_info`
- On success: Display "✅ Authentication successful! Logged in as: {displayName}"
- Ask storage preference (all in CLAUDE.md / URL+email with env var / don't store)
- Store if requested
Expand Down Expand Up @@ -307,7 +309,7 @@ getJiraProjectIssueTypesMetadata(cloudId, projectKey)

**On MCP failure, if REST API chosen (Step 0.5):**
```bash
python3 scripts/jira-client.py get_project_metadata <project-key>
cd <plugin-root> && python3 scripts/jira-client.py get_project_metadata <project-key>
```

Find the issue type whose ID matches the configured Feature issue type ID and use its name.
Expand Down Expand Up @@ -336,13 +338,14 @@ additional_fields: { "labels": ["ai-generated-jira"], "assignee": { "accountId":

First, if self-assignment was chosen, get the current user's account ID:
```bash
USER_INFO=$(python3 scripts/jira-client.py get_user_info)
USER_INFO=$(cd <plugin-root> && python3 scripts/jira-client.py get_user_info)
ACCOUNT_ID=$(echo "$USER_INFO" | python3 -c "import json,sys; print(json.load(sys.stdin)['accountId'])")
```

Then create the issue:
```bash
python3 scripts/jira-client.py create_issue \
cd <plugin-root> && \
python3 scripts/jira-client.py create_issue \
--project <project-key> \
--summary "<collected-summary>" \
--description-md "<composed-description>" \
Expand All @@ -351,7 +354,7 @@ python3 scripts/jira-client.py create_issue \
--assignee-id "$ACCOUNT_ID" # omit if no self-assignment
```

The Python client automatically converts markdown to ADF.
The JIRA REST API Python client automatically converts markdown to ADF.

Record the created issue key and URL from the JSON response.

Expand All @@ -367,11 +370,12 @@ addCommentToJiraIssue(cloudId, issueIdOrKey=<created-issue-key>, comment=<summar

**On MCP failure, if REST API chosen (Step 0.5):**
```bash
python3 scripts/jira-client.py add_comment <created-issue-key> \
cd <plugin-root> && \
python3 scripts/jira-client.py add_comment <created-issue-key> \
--comment-md "<summary-comment>"
```

The Python client automatically converts markdown to ADF.
The JIRA REST API Python client automatically converts markdown to ADF.

**Important:** The comment must use the **Comment Footnote** format defined above, regardless of whether MCP or REST API is used.

Expand Down
16 changes: 8 additions & 8 deletions plugins/sdlc-workflow/skills/implement-task/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,21 +43,21 @@ Before attempting any JIRA operations (Steps 1, 2, 3, 11), determine the access
Choose (1/2/3):
```

3. **If "1. Yes":** Check CLAUDE.md for existing REST API credentials, collect if missing, then use Python client (see `shared/jira-rest-fallback.md`)
3. **If "1. Yes":** Check CLAUDE.md for existing REST API credentials, collect if missing, then use JIRA REST API Python client (see `shared/jira-rest-fallback.md`)
4. **If "2. No":** Skip the JIRA operation and inform user
5. **If "3. Retry":** Retry MCP once

**REST API equivalents for this skill's operations:**
- `jira.get_issue(id)` → `python3 scripts/jira-client.py get_issue <id> --fields "*all"`
- `jira.user_info()` → `python3 scripts/jira-client.py get_user_info`
- `jira.edit_issue(id, assignee=accountId)` → `python3 scripts/jira-client.py update_issue <id> --fields-json '{"assignee": {"id": "<accountId>"}}'`
- `jira.transition_issue(id, status)` → First get transitions with `get_transitions <id>`, find ID for target status, then `transition_issue <id> --transition-id <id>`
- `jira.update_issue(id, fields)` → `python3 scripts/jira-client.py update_issue <id> --fields-json '<json>'`
- `jira.add_comment(id, text)` → `python3 scripts/jira-client.py add_comment <id> --comment-md "<text>"`
- `jira.get_issue(id)` → `cd <plugin-root> && python3 scripts/jira-client.py get_issue <id> --fields "*all"`
- `jira.user_info()` → `cd <plugin-root> && python3 scripts/jira-client.py get_user_info`
- `jira.edit_issue(id, assignee=accountId)` → `cd <plugin-root> && python3 scripts/jira-client.py update_issue <id> --fields-json '{"assignee": {"id": "<accountId>"}}'`
- `jira.transition_issue(id, status)` → First get transitions with `cd <plugin-root> && python3 scripts/jira-client.py get_transitions <id>`, find ID for target status, then `cd <plugin-root> && python3 scripts/jira-client.py transition_issue <id> --transition-id <id>`
- `jira.update_issue(id, fields)` → `cd <plugin-root> && python3 scripts/jira-client.py update_issue <id> --fields-json '<json>'`
- `jira.add_comment(id, text)` → `cd <plugin-root> && python3 scripts/jira-client.py add_comment <id> --comment-md "<text>"`

**Exception for Bash tool:** When using REST API fallback, this skill may use `bash -c "python3 scripts/jira-client.py <command>"` for JIRA operations only.

Refer to `shared/jira-rest-fallback.md` for complete implementation details.
**Script execution pattern:** See `shared/jira-rest-fallback.md` § "Script Execution Context" for the `cd <plugin-root> && python3 scripts/jira-client.py <command>` pattern and complete implementation guidance.

## Inputs

Expand Down
12 changes: 7 additions & 5 deletions plugins/sdlc-workflow/skills/plan-feature/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ When Atlassian MCP is unavailable, this skill may use the Bash tool to invoke th
- ✅ Allowed: `bash -c "python3 scripts/jira-client.py <command>"`
- ❌ Forbidden: any other Bash file modification commands

**Script execution pattern:** See `shared/jira-rest-fallback.md` § "Script Execution Context" for the `cd <plugin-root> && python3 scripts/jira-client.py <command>` pattern and complete implementation guidance.

## Comment Footnote

Every comment posted to Jira by this skill MUST end with the following footnote,
Expand Down Expand Up @@ -102,15 +104,15 @@ Before attempting any JIRA operations (Steps 1, 4, 6), determine the access meth
Choose (1/2/3):
```

3. **If "1. Yes":** Check CLAUDE.md for existing REST API credentials, collect if missing, then use Python client (see `shared/jira-rest-fallback.md`)
3. **If "1. Yes":** Check CLAUDE.md for existing REST API credentials, collect if missing, then use JIRA REST API Python client (see `shared/jira-rest-fallback.md`)
4. **If "2. No":** Skip the JIRA operation and inform user
5. **If "3. Retry":** Retry MCP once

**REST API equivalents for this skill's operations:**
- `jira.get_issue(id)` → `python3 scripts/jira-client.py get_issue <id> --fields "*all"`
- `jira.add_comment(id, text)` → `python3 scripts/jira-client.py add_comment <id> --comment-md "<text>"`
- `jira.create_issue(...)` → `python3 scripts/jira-client.py create_issue --project <key> --summary "<summary>" --description-md "<desc>" --issue-type Task --labels <labels>`
- `jira.create_issue_link(...)` → `python3 scripts/jira-client.py create_link --inward <issue1> --outward <issue2> --link-type <type>`
- `jira.get_issue(id)` → `cd <plugin-root> && python3 scripts/jira-client.py get_issue <id> --fields "*all"`
- `jira.add_comment(id, text)` → `cd <plugin-root> && python3 scripts/jira-client.py add_comment <id> --comment-md "<text>"`
- `jira.create_issue(...)` → `cd <plugin-root> && python3 scripts/jira-client.py create_issue --project <key> --summary "<summary>" --description-md "<desc>" --issue-type Task --labels <labels>`
- `jira.create_issue_link(...)` → `cd <plugin-root> && python3 scripts/jira-client.py create_link --inward <issue1> --outward <issue2> --link-type <type>`

Refer to `shared/jira-rest-fallback.md` for complete implementation details.

Expand Down
Loading
Loading