diff --git a/docs/project-config-contract.md b/docs/project-config-contract.md index ad2f0f18..94c227e7 100644 --- a/docs/project-config-contract.md +++ b/docs/project-config-contract.md @@ -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 ` + +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. diff --git a/plugins/sdlc-workflow/shared/jira-access-strategy.md b/plugins/sdlc-workflow/shared/jira-access-strategy.md index 08c17cfc..1f8f21ed 100644 --- a/plugins/sdlc-workflow/shared/jira-access-strategy.md +++ b/plugins/sdlc-workflow/shared/jira-access-strategy.md @@ -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. @@ -87,7 +87,19 @@ cd && \ --comment-md "Comment text in markdown" # Transition issue -cd && python3 scripts/jira-client.py transition_issue TC-123 --transition-id 31 +# Step 1: Get available transitions for the issue +cd && 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 && 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 && python3 scripts/jira-client.py transition_issue TC-123 \ + --transition-id "$TRANSITION_ID" # Create issue link cd && \ @@ -146,9 +158,12 @@ 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 + + ### REST API Credentials (MCP Fallback) - Server URL: https://your-domain.atlassian.net @@ -156,6 +171,24 @@ When REST API is used, credentials are stored in CLAUDE.md: - 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 diff --git a/plugins/sdlc-workflow/shared/jira-rest-fallback.md b/plugins/sdlc-workflow/shared/jira-rest-fallback.md index c5dd6ba7..578e8768 100644 --- a/plugins/sdlc-workflow/shared/jira-rest-fallback.md +++ b/plugins/sdlc-workflow/shared/jira-rest-fallback.md @@ -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 && python3 scripts/jira-client.py ``` @@ -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`. @@ -289,7 +289,7 @@ cd && \ --project TC \ --summary "New feature request" \ --description-md "This is the **description** in markdown." \ - --issue-type "10142" \ + --issue-type "" \ # See "Discovering Issue Types" below --labels ai-generated-jira,feature ``` @@ -316,14 +316,37 @@ cd && \ ``` **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 && python3 scripts/jira-client.py get_transitions TC-123 +``` -# Then transition using the ID -cd && 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 && 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 && 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 && \ @@ -352,9 +375,57 @@ cd && python3 scripts/jira-client.py get_user_info cd && 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 && python3 scripts/jira-client.py get_project_metadata +``` + +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 && python3 scripts/jira-client.py get_issue --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:** ``` @@ -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` @@ -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` diff --git a/plugins/sdlc-workflow/skills/define-feature/SKILL.md b/plugins/sdlc-workflow/skills/define-feature/SKILL.md index 45dc5dad..200f6fba 100644 --- a/plugins/sdlc-workflow/skills/define-feature/SKILL.md +++ b/plugins/sdlc-workflow/skills/define-feature/SKILL.md @@ -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 "` - ❌ Forbidden: any other Bash file modification commands +**Script execution pattern:** See `shared/jira-rest-fallback.md` § "Script Execution Context" for the `cd && python3 scripts/jira-client.py ` pattern and complete implementation guidance. + ## Comment Footnote Every comment posted to Jira by this skill MUST end with the following footnote, @@ -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 && 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 @@ -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 +cd && python3 scripts/jira-client.py get_project_metadata ``` Find the issue type whose ID matches the configured Feature issue type ID and use its name. @@ -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 && 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 && \ + python3 scripts/jira-client.py create_issue \ --project \ --summary "" \ --description-md "" \ @@ -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. @@ -367,11 +370,12 @@ addCommentToJiraIssue(cloudId, issueIdOrKey=, comment= \ +cd && \ + python3 scripts/jira-client.py add_comment \ --comment-md "" ``` -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. diff --git a/plugins/sdlc-workflow/skills/implement-task/SKILL.md b/plugins/sdlc-workflow/skills/implement-task/SKILL.md index ec2ecf37..2bfbb609 100644 --- a/plugins/sdlc-workflow/skills/implement-task/SKILL.md +++ b/plugins/sdlc-workflow/skills/implement-task/SKILL.md @@ -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 --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 --fields-json '{"assignee": {"id": ""}}'` -- `jira.transition_issue(id, status)` → First get transitions with `get_transitions `, find ID for target status, then `transition_issue --transition-id ` -- `jira.update_issue(id, fields)` → `python3 scripts/jira-client.py update_issue --fields-json ''` -- `jira.add_comment(id, text)` → `python3 scripts/jira-client.py add_comment --comment-md ""` +- `jira.get_issue(id)` → `cd && python3 scripts/jira-client.py get_issue --fields "*all"` +- `jira.user_info()` → `cd && python3 scripts/jira-client.py get_user_info` +- `jira.edit_issue(id, assignee=accountId)` → `cd && python3 scripts/jira-client.py update_issue --fields-json '{"assignee": {"id": ""}}'` +- `jira.transition_issue(id, status)` → First get transitions with `cd && python3 scripts/jira-client.py get_transitions `, find ID for target status, then `cd && python3 scripts/jira-client.py transition_issue --transition-id ` +- `jira.update_issue(id, fields)` → `cd && python3 scripts/jira-client.py update_issue --fields-json ''` +- `jira.add_comment(id, text)` → `cd && python3 scripts/jira-client.py add_comment --comment-md ""` **Exception for Bash tool:** When using REST API fallback, this skill may use `bash -c "python3 scripts/jira-client.py "` 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 && python3 scripts/jira-client.py ` pattern and complete implementation guidance. ## Inputs diff --git a/plugins/sdlc-workflow/skills/plan-feature/SKILL.md b/plugins/sdlc-workflow/skills/plan-feature/SKILL.md index b7620c5c..fdebfee8 100644 --- a/plugins/sdlc-workflow/skills/plan-feature/SKILL.md +++ b/plugins/sdlc-workflow/skills/plan-feature/SKILL.md @@ -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 "` - ❌ Forbidden: any other Bash file modification commands +**Script execution pattern:** See `shared/jira-rest-fallback.md` § "Script Execution Context" for the `cd && python3 scripts/jira-client.py ` pattern and complete implementation guidance. + ## Comment Footnote Every comment posted to Jira by this skill MUST end with the following footnote, @@ -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 --fields "*all"` -- `jira.add_comment(id, text)` → `python3 scripts/jira-client.py add_comment --comment-md ""` -- `jira.create_issue(...)` → `python3 scripts/jira-client.py create_issue --project --summary "" --description-md "" --issue-type Task --labels ` -- `jira.create_issue_link(...)` → `python3 scripts/jira-client.py create_link --inward --outward --link-type ` +- `jira.get_issue(id)` → `cd && python3 scripts/jira-client.py get_issue --fields "*all"` +- `jira.add_comment(id, text)` → `cd && python3 scripts/jira-client.py add_comment --comment-md ""` +- `jira.create_issue(...)` → `cd && python3 scripts/jira-client.py create_issue --project --summary "" --description-md "" --issue-type Task --labels ` +- `jira.create_issue_link(...)` → `cd && python3 scripts/jira-client.py create_link --inward --outward --link-type ` Refer to `shared/jira-rest-fallback.md` for complete implementation details. diff --git a/plugins/sdlc-workflow/skills/setup/SKILL.md b/plugins/sdlc-workflow/skills/setup/SKILL.md index 2037cec3..e8a2c5e4 100644 --- a/plugins/sdlc-workflow/skills/setup/SKILL.md +++ b/plugins/sdlc-workflow/skills/setup/SKILL.md @@ -22,6 +22,8 @@ When Atlassian MCP is unavailable, this skill may use the Bash tool to invoke th - ✅ Allowed: `bash -c "python3 scripts/jira-client.py "` - ❌ Forbidden: any other Bash file modification commands +**Script execution pattern:** See `shared/jira-rest-fallback.md` § "Script Execution Context" for the `cd && python3 scripts/jira-client.py ` pattern and complete implementation guidance. + ## Template Read the file `project-config.template.md` in this skill's directory. Use it as the structural reference for generating the `# Project Configuration` section. Replace the `{{placeholder}}` markers with actual values gathered during the steps below. Preserve the exact headings, table format, and section order from the template. @@ -93,7 +95,7 @@ Choose (1/2/3): 3. 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 && 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 under `### REST API Credentials (MCP Fallback)` in `## Jira Configuration` @@ -108,11 +110,11 @@ Choose (1/2/3): ### Step 3.3 – Use REST API for Discovery -Use the Python client to gather Jira configuration: +Use the JIRA REST API Python client script to gather Jira configuration: 1. **Get project metadata:** ```bash - python3 scripts/jira-client.py get_project_metadata + cd && python3 scripts/jira-client.py get_project_metadata ``` Ask user which project key to use if not already known. The response provides: - Project key @@ -120,7 +122,7 @@ Use the Python client to gather Jira configuration: 2. **Get user info:** ```bash - python3 scripts/jira-client.py get_user_info + cd && python3 scripts/jira-client.py get_user_info ``` Extract Cloud ID from the response (if present in user metadata). If Cloud ID is not available via API, ask user to provide it manually. diff --git a/plugins/sdlc-workflow/skills/setup/project-config.template.md b/plugins/sdlc-workflow/skills/setup/project-config.template.md index 7c713e80..7719a678 100644 --- a/plugins/sdlc-workflow/skills/setup/project-config.template.md +++ b/plugins/sdlc-workflow/skills/setup/project-config.template.md @@ -8,11 +8,11 @@ ## Jira Configuration -- Project key: {{project-key}} -- Cloud ID: {{cloud-id}} -- Feature issue type ID: {{feature-issue-type-id}} -- Git Pull Request custom field: {{custom-field-id}} -- GitHub Issue custom field: {{custom-field-id}} +- Project key: {{project-key}} # Your Jira project key (e.g., TC, PROJ) +- Cloud ID: {{cloud-id}} # Jira cloud instance ID (from get_user_info) +- Feature issue type ID: {{feature-issue-type-id}} # Discovered via get_project_metadata +- Git Pull Request custom field: {{custom-field-id}} # Optional - from get_project_metadata +- GitHub Issue custom field: {{custom-field-id}} # Optional - from get_project_metadata ## Code Intelligence diff --git a/plugins/sdlc-workflow/skills/verify-pr/SKILL.md b/plugins/sdlc-workflow/skills/verify-pr/SKILL.md index b1fd02d0..844b84d5 100644 --- a/plugins/sdlc-workflow/skills/verify-pr/SKILL.md +++ b/plugins/sdlc-workflow/skills/verify-pr/SKILL.md @@ -43,20 +43,20 @@ Before attempting any JIRA operations throughout this skill, determine the acces 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 --fields "*all"` -- `jira.create_issue(...)` → `python3 scripts/jira-client.py create_issue --project --summary "" --description-md "" --issue-type Task --labels ` -- `jira.create_issue_link(...)` → `python3 scripts/jira-client.py create_link --inward --outward --link-type ` -- `jira.add_comment(id, text)` → `python3 scripts/jira-client.py add_comment --comment-md ""` -- `jira.transition_issue(id, status)` → Get transitions, find ID, then `transition_issue --transition-id ` +- `jira.get_issue(id)` → `cd && python3 scripts/jira-client.py get_issue --fields "*all"` +- `jira.create_issue(...)` → `cd && python3 scripts/jira-client.py create_issue --project --summary "" --description-md "" --issue-type Task --labels ` +- `jira.create_issue_link(...)` → `cd && python3 scripts/jira-client.py create_link --inward --outward --link-type ` +- `jira.add_comment(id, text)` → `cd && python3 scripts/jira-client.py add_comment --comment-md ""` +- `jira.transition_issue(id, status)` → Get transitions, find ID, then `cd && python3 scripts/jira-client.py transition_issue --transition-id ` **Exception for Bash tool:** When using REST API fallback, this skill may use `bash -c "python3 scripts/jira-client.py "` 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 && python3 scripts/jira-client.py ` pattern and complete implementation guidance. ## Inputs