Skip to content

feat: Add foundation for Jira issue creation workflow#166

Open
r-pedraza wants to merge 33 commits intomasterfrom
feat/crerate-issue-jira-core
Open

feat: Add foundation for Jira issue creation workflow#166
r-pedraza wants to merge 33 commits intomasterfrom
feat/crerate-issue-jira-core

Conversation

@r-pedraza
Copy link
Copy Markdown
Contributor

Pull Request

📝 Summary

This PR lays the groundwork for integrating a Jira issue creation workflow. It enables the Jira plugin in the configuration and refactors the create_branch_step to use a more robust Result object pattern for handling git operations. This shift from exception-based error handling to a match/case structure improves code clarity and resilience.

🔧 Changes Made

  • Enabled the [plugins.jira] in .titan/config.toml.
  • Refactored create_branch_step.py to use ClientSuccess and ClientError result objects instead of raising GitError exceptions.
  • Replaced try/except blocks with match statements for handling the outcomes of all git commands (get_branches, checkout, delete_branch, create_branch).
  • Updated imports to use the new result types from titan_cli.core.result.

🧪 Testing

  • Unit tests added/updated (poetry run pytest)
  • All tests passing (make test)
  • Manual testing with titan-dev

The refactoring touches core git functionality. Existing unit tests for create_branch_step should be updated to validate the new Result-based logic, ensuring all success and error paths are handled correctly.

📊 Logs

  • No new log events

✅ Checklist

  • Self-review done
  • Follows the project's logging rules (no secrets, no content in logs)
  • New and existing tests pass
  • Documentation updated if needed

@r-pedraza r-pedraza self-assigned this Feb 24, 2026
@wiz-b6e4a6c509
Copy link
Copy Markdown

wiz-b6e4a6c509 bot commented Feb 24, 2026

Wiz Scan Summary

Scanner Findings
Vulnerability Finding Vulnerabilities -
Data Finding Sensitive Data -
Secret Finding Secrets -
IaC Misconfiguration IaC Misconfigurations -
SAST Finding SAST Findings 1 Medium
Software Management Finding Software Management Findings -
Total 1 Medium

View scan details in Wiz

To detect these findings earlier in the dev lifecycle, try using Wiz Code VS Code Extension.


def find_ready_to_dev_transition(
jira_client: "JiraClient", issue_key: str
) -> ClientResult["UITransition"]:
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should not return the ClientResult, just the UITransition

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


def transition_issue_to_ready_for_dev(
jira_client: "JiraClient", issue_key: str
) -> ClientResult[None]:
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ClientResult, should not be returned in operations. It's just to Client/Service. The step that calls this should call try/except

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Already fixed. The function now returns None and raises exceptions instead of returning ClientResult[None], following the operations layer pattern.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Already resolved. Function returns UITransition (UI model) and raises exceptions, following the operations pattern correctly.


selected_type = issue_types[index]

if not selected_type:
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should never be false here

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

match transition_result:
case ClientSuccess():
# Get transition details to show user
find_result = ctx.jira.get_transitions(issue_key)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are calling the api twice. In line 128 you have already called the api. You need to add the necessary data to the clientSuccess.message, or in the data model

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

value = int(selection)
index = value - 1

if index < 0 or index >= max_value:
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should validate min_value as well

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"""
ctx.textual.begin_step(StepTitles.PRIORITY)

ctx.textual.markdown("## 🔥 Priority")
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These should not be a markdown. Check the rest of the steps, cause the title of something's should not be a markdown,

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Already fixed in ccf3519. All step headers now use bold_text() instead of markdown():

ctx.textual.bold_text("🔥 Priority")

Changed across all step files (select_issue_priority_step, select_issue_type_step, prompt_issue_description_step).

Copy link
Copy Markdown
Collaborator

@finxo finxo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also testing the PR I found this bug:

────────────────────────────────────────────────────────────────────────────────
 SESSION START  2026-03-02 10:14:41 UTC   PID 85720
────────────────────────────────────────────────────────────────────────────────
{"version": "0.1.11", "mode": "development", "log_level": "WARNING", "pid": 85720, "log_file": "/home/alex/.local/state/titan/logs/titan.log", "event": "session_started", "level": "info", "logger": "titan", "timestamp": "2026-03-02T10:14:41.119017Z"}
{"name": "git", "event": "plugin_initialized", "level": "info", "logger": "titan_cli.core.plugins.plugin_registry", "timestamp": "2026-03-02T10:14:41.150177Z"}
{"name": "jira", "event": "plugin_initialized", "level": "info", "logger": "titan_cli.core.plugins.plugin_registry", "timestamp": "2026-03-02T10:14:41.226858Z"}
{"name": "github", "event": "plugin_initialized", "level": "info", "logger": "titan_cli.core.plugins.plugin_registry", "timestamp": "2026-03-02T10:14:41.591243Z"}
{"message": "Status retrieved", "result_type": "UIGitStatus", "duration": 0.015, "event": "get_status_success", "level": "info", "logger": "titan_plugin_git.clients.services.status_service", "timestamp": "2026-03-02T10:14:41.623199Z"}
{"message": "Status retrieved", "result_type": "UIGitStatus", "duration": 0.022, "event": "get_status_success", "level": "info", "logger": "titan_plugin_git.clients.services.status_service", "timestamp": "2026-03-02T10:14:42.668828Z"}
{"message": "Status retrieved", "result_type": "UIGitStatus", "duration": 0.021, "event": "get_status_success", "level": "info", "logger": "titan_plugin_git.clients.services.status_service", "timestamp": "2026-03-02T10:14:45.829840Z"}
{"message": "Status retrieved", "result_type": "UIGitStatus", "duration": 0.021, "event": "get_status_success", "level": "info", "logger": "titan_plugin_git.clients.services.status_service", "timestamp": "2026-03-02T10:15:00.475315Z"}
{"workflow": "Create Jira Issue", "source": "plugin", "total_steps": 7, "is_nested": false, "event": "workflow_started", "level": "info", "logger": "titan_cli.ui.tui.textual_workflow_executor", "timestamp": "2026-03-02T10:15:00.581044Z"}
{"workflow": "Create Jira Issue", "step_id": "description", "message": "Brief description captured: 87 characters", "duration": 16.898, "event": "step_success", "level": "info", "logger": "titan_cli.ui.tui.textual_workflow_executor", "timestamp": "2026-03-02T10:15:17.485003Z"}
{"message": "Found 21 issue types", "result_type": "list", "duration": 0.164, "event": "get_issue_types_success", "level": "info", "logger": "titan_plugin_jira.clients.services.metadata_service", "timestamp": "2026-03-02T10:15:17.657802Z"}
{"workflow": "Create Jira Issue", "step_id": "issue_type", "message": "Issue type selected: Epic", "duration": 13.333, "event": "step_success", "level": "info", "logger": "titan_cli.ui.tui.textual_workflow_executor", "timestamp": "2026-03-02T10:15:30.846453Z"}
{"workflow": "Create Jira Issue", "step_id": "priority", "message": "Priority selected: Highest", "duration": 10.62, "event": "step_success", "level": "info", "logger": "titan_cli.ui.tui.textual_workflow_executor", "timestamp": "2026-03-02T10:15:41.478548Z"}
{"event": "HTTP Request: POST https://llm.tools.cloud.masorange.es/v1/messages \"HTTP/1.1 200 OK\"", "timestamp": "2026-03-02T10:15:58.747430Z"}
{"workflow": "Create Jira Issue", "step_id": "generate_with_ai", "error": "Error executing step 'ai_enhance_issue_description' from plugin 'jira': object of type 'NoneType' has no len()", "on_error": "fail", "duration": 17.276, "event": "step_failed", "level": "error", "logger": "titan_cli.ui.tui.textual_workflow_executor", "timestamp": "2026-03-02T10:15:58.755240Z"}
{"workflow": "Create Jira Issue", "failed_at_step": "generate_with_ai", "error": "Error executing step 'ai_enhance_issue_description' from plugin 'jira': object of type 'NoneType' has no len()", "steps_completed": 4, "duration": 58.186, "event": "workflow_failed", "level": "error", "logger": "titan_cli.ui.tui.textual_workflow_executor", "timestamp": "2026-03-02T10:15:58.761589Z"}
● Encontré el bug. La cadena del problema:                                                                                                                                                                                         
                             
  1. _parse_ai_response inicializa "title": ""                                                                                                                                                                                     
  2. En el cleanup final: si queda vacío, lo pone a None → sections["title"] = None                                                                                                                                                
  3. parsed.pop("title", DEFAULT_TITLE) devuelve None (la clave existe pero vale None — el default solo aplica si la clave no existe)                                                                                              
  4. len(None) → TypeError                                                                                                                                                                                                                                              

@r-pedraza
Copy link
Copy Markdown
Contributor Author

@r-pedraza r-pedraza requested a review from finxo March 19, 2026 09:40
)

except Exception as e:
import traceback
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

imports goes up in the file not here

@@ -0,0 +1,167 @@
# Plantillas Personalizadas para Issues
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Never add docs in spanish

result = transition_issue_to_ready_for_dev(mock_client, "TEST-123")

# Assert
assert result is None
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function transition_issue_to_ready_to_dev returns UIJiraTransition not none. This is wrong test

result = transition_issue_to_ready_for_dev(mock_client, "TEST-123")

# Assert
assert result is None
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function transition_issue_to_ready_to_dev returns UIJiraTransition not none. This is wrong test

error_code="CREATE_SUBTASK_ERROR"
)

# ==================== INTERNAL HELPERS ====================
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this kind of comments



def find_ready_to_dev_transition(
jira_client: "JiraClient", issue_key: str
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is the problem of type_checking, you are returning a string not a class. SAme in the rest of the file

# Call AI
with ctx.textual.loading("Generating description with AI..."):
try:
from titan_cli.ai.models import AIMessage
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

imports goes up in the file

from .steps.ai_analyze_issue_step import ai_analyze_issue_requirements_step
from .steps.list_versions_step import list_versions_step

# Technical Specification Workflow steps (COMMENTED - steps don't exist)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this does not exist remove them

"ai_analyze_issue_requirements": ai_analyze_issue_requirements_step,
"list_versions": list_versions_step,

# Technical Specification Workflow steps (COMMENTED - steps don't exist)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this steps does not exist remove them

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should return a UIModel not Network one

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should return a UIModel not a dict

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should return an UI Model not dict

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should return a UIModel not List[dict]

@r-pedraza
Copy link
Copy Markdown
Contributor Author

1 similar comment
@r-pedraza
Copy link
Copy Markdown
Contributor Author

@r-pedraza r-pedraza requested a review from finxo March 24, 2026 18:43
priority=priority
)
# Find issue type (delegated to operation)
issue_type_result = find_issue_type_by_name(self, project_key, issue_type)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Client is calling an operation and passing itself as a parameter. This is backwards - operations should call the client, not vice versa. This violates the 5-layer architecture where Client (layer 3) should never call Operations (layer 2).

description=description
)
# Find subtask issue type (delegated to operation)
subtask_result = find_subtask_issue_type(self, self.project_key)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Client is calling an operation and passing itself as a parameter. This is backwards - operations should call the client, not vice versa. This violates the 5-layer architecture where Client (layer 3) should never call Operations (layer 2).

)

except Exception as e:
import traceback
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

imports go up in the file, not in the exception handler. Move this to the top of the file with other imports.

r-pedraza and others added 13 commits March 26, 2026 07:21
Refactor create_issue to follow 5-layer architecture:
- Move issue type search logic from Client to Service
- Move Epic name preparation from Client to Service
- Create IssueService.create_issue_with_type_search() method
- Simplify JiraClient.create_issue() to pure delegation

Resolves PR #166 Issue #1 (CRITICAL): Business logic in Client

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace ctx.textual.markdown() with bold_text() for simple title headers
following Textual TUI conventions.

Changes:
- Remove markdown syntax (**) from PREVIEW_LABEL and GENERATED_DESC_LABEL constants
- Replace markdown() with bold_text() in 4 step files:
  * ai_enhance_issue_description_step.py (lines 52, 125)
  * confirm_auto_assign_step.py (line 32)
  * create_generic_issue_step.py (line 60)
  * review_issue_description_step.py (lines 40, 44)
- Maintain i18n by using constants instead of hardcoded strings

Markdown should only be used for complex formatted content (AI analysis,
descriptions, previews), not simple headers/labels.

Resolves PR #166 Issue #2 (MINOR): Markdown for simple titles

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@r-pedraza
Copy link
Copy Markdown
Contributor Author

Issue #2 (MINOR) - Markdown for simple titles: Fixed in ccf3519

Replaced ctx.textual.markdown() with bold_text() for simple headers in 4 step files.

Changes:

  • ✅ Removed markdown syntax (**) from PREVIEW_LABEL and GENERATED_DESC_LABEL constants
  • ✅ Updated 4 steps to use bold_text() instead of markdown() for headers
  • Preserved i18n: Used constants instead of hardcoding strings

Files modified:

  • ai_enhance_issue_description_step.py (lines 52, 125)
  • confirm_auto_assign_step.py (line 32)
  • create_generic_issue_step.py (line 60)
  • review_issue_description_step.py (lines 40, 44)

Pattern applied:

# Before
ctx.textual.markdown("## 🤖 Title")
ctx.textual.markdown(InfoMessages.PREVIEW_LABEL)  # "**Preview:**"

# After
ctx.textual.bold_text("🤖 Title")
ctx.textual.bold_text(InfoMessages.PREVIEW_LABEL)  # "Preview:" (constant updated)

Markdown now only used for complex formatted content (AI analysis, descriptions, previews), not simple headers. ✅

Copy link
Copy Markdown
Collaborator

@finxo finxo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

plugins/titan-plugin-jira/titan_plugin_jira/operations/issue_operations.py (line 0):
Operations layer methods are returning ClientResult[T] when they should return plain UIModel objects or domain data. This violates the 5-layer architecture pattern (Network → Models → Services handle ClientResult → Operations → Steps). ClientResult should only exist at the client/service boundary.

Change operation signatures to return UITransition, UIJiraIssue, etc. directly. Let the service layer wrap these in ClientResult. Example: def transition_issue(...) -> UITransition: not -> ClientResult[UITransition]:

plugins/titan-plugin-jira/titan_plugin_jira/steps/select_issue_type_step.py (line 1):
Assertions should never be used for runtime validation in production code. If this condition "should never be false," assertions are silently disabled in optimized Python (python -O), leaving no error handling.

Replace with proper error handling: if not condition: return Error("Detailed error message") or raise a specific exception with context.

@r-pedraza
Copy link
Copy Markdown
Contributor Author

Thanks for the review! Both issues have already been addressed:

Issue #1 (CRITICAL) - Operations returning ClientResult[T]: ✅ Fixed in d7de2ba

What was fixed: All operations in issue_operations.py now return plain UI models instead of ClientResult[T]:

  • find_ready_to_dev_transition() → returns UITransition (line 21)
  • transition_issue_to_ready_for_dev() → returns UITransition (line 58)
  • find_issue_type_by_name() → returns UIJiraIssueType (line 88)
  • find_subtask_issue_type() → returns UIJiraIssueType (line 143)

Pattern now used: Operations handle ClientResult internally with match/case, but return plain objects. Services wrap these in ClientResult when needed.

Issue #2 (MINOR) - Assertions in production code: ✅ No asserts found

Verified: no assert statements exist in production code (steps, operations, clients). All assertions are only in tests (as they should be).

Both issues comply with the 5-layer architecture pattern. ✅

r-pedraza and others added 2 commits March 27, 2026 16:09
**Issue #1 - Add StrEnum for Jira priorities**:
- Created JiraPriority StrEnum in models/enums.py with values: Highest, High, Medium, Low, Lowest
- Added icon and label properties to enum for type-safe priority handling
- Updated defaults.py to use JiraPriority enum instead of string literals
- Maintains string compatibility via StrEnum while providing type safety

**Issue #2 - Remove docstring examples**:
- Removed all doctest examples (>>>) from production code docstrings
- Replaced examples with clear textual descriptions
- Affected files: jira_client.py, jira_network.py, prompts.py, markdown_formatter.py,
  saved_queries.py, formatting.py, issue_formatting_operations.py
- Follows project guidelines: no examples in docstrings, only clear documentation

**Benefits**:
- Type safety: IDE autocomplete and type checking for priorities
- Cleaner docs: Focus on what the code does, not examples that can become stale
- Consistency: All docstrings follow same documentation pattern

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
**Issue**: metadata_service.py had quoted type annotations (forward references)
using TYPE_CHECKING, but there were no circular imports.

**Changes**:
- Removed TYPE_CHECKING guard (no circular imports exist)
- Moved UI model imports from TYPE_CHECKING block to regular imports
- Removed quotes from all type annotations:
  - ClientResult[List["UIJiraIssueType"]] → ClientResult[List[UIJiraIssueType]]
  - ClientResult[List["UIJiraStatus"]] → ClientResult[List[UIJiraStatus]]
  - ClientResult["UIJiraUser"] → ClientResult[UIJiraUser]
  - ClientResult[List["UIJiraVersion"]] → ClientResult[List[UIJiraVersion]]
  - ClientResult[List["UIPriority"]] → ClientResult[List[UIPriority]]
  - ClientResult["UIJiraIssueType"] → ClientResult[UIJiraIssueType]

**Benefits**:
- Proper type checking at runtime
- Better IDE autocomplete and type hints
- Cleaner code without unnecessary forward references
- Follows Python typing best practices

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@r-pedraza
Copy link
Copy Markdown
Contributor Author

Fixed: StrEnum for Priorities + Remove Docstring Examples ✅

Commit: 4fd6d68

Issue #1 - Add StrEnum for Jira Priorities

Created JiraPriority StrEnum in models/enums.py:

class JiraPriority(StrEnum):
    HIGHEST = "Highest"
    HIGH = "High"
    MEDIUM = "Medium"
    LOW = "Low"
    LOWEST = "Lowest"
    
    @property
    def icon(self) -> str: ...
    
    @property
    def label(self) -> str: ...

Benefits:

  • ✅ Type safety: IDE autocomplete for priority values
  • ✅ String compatibility: Works everywhere strings work (StrEnum)
  • ✅ Centralized logic: Icon/label properties on the enum

Updated:

  • defaults.py - Uses JiraPriority.HIGHEST instead of "Highest"
  • formatting.py - Updated docstring to reference standard values

Issue #2 - Remove Docstring Examples

Removed all doctest examples (>>>) from production code docstrings:

  • jira_client.py - 3 examples removed
  • jira_network.py - 1 example removed
  • prompts.py - 1 example removed
  • markdown_formatter.py - 1 example removed
  • saved_queries.py - 1 example removed
  • formatting.py - 1 example removed
  • issue_formatting_operations.py - 1 example removed

Replaced with: Clear textual descriptions of behavior

Follows project guidelines: No examples in docstrings, only documentation

@r-pedraza
Copy link
Copy Markdown
Contributor Author

Fixed: Quoted Type Annotations ✅

Commit: e7c30f6

Issue - Types as Strings in Annotations

Found and fixed 6 methods in metadata_service.py with unnecessary quoted type annotations:

Before:

def get_issue_types(...) -> ClientResult[List["UIJiraIssueType"]]:
def list_statuses(...) -> ClientResult[List["UIJiraStatus"]]:
def get_current_user(...) -> ClientResult["UIJiraUser"]:
def list_project_versions(...) -> ClientResult[List["UIJiraVersion"]]:
def get_priorities(...) -> ClientResult[List["UIPriority"]]:
def find_subtask_issue_type(...) -> ClientResult["UIJiraIssueType"]:

After:

def get_issue_types(...) -> ClientResult[List[UIJiraIssueType]]:
def list_statuses(...) -> ClientResult[List[UIJiraStatus]]:
def get_current_user(...) -> ClientResult[UIJiraUser]:
def list_project_versions(...) -> ClientResult[List[UIJiraVersion]]:
def get_priorities(...) -> ClientResult[List[UIPriority]]:
def find_subtask_issue_type(...) -> ClientResult[UIJiraIssueType]:

Changes:

  • Removed TYPE_CHECKING guard (no circular imports exist - verified)
  • Moved UI model imports to regular imports
  • Removed quotes from all type annotations

Benefits:

  • ✅ Proper runtime type checking
  • ✅ Better IDE autocomplete and navigation
  • ✅ Cleaner code without unnecessary forward references
  • ✅ Follows Python typing best practices

**Changes**:

1. **Extended JiraPriority enum**:
   - Added `get_icon()` class method for any priority name
   - Handles standard priorities + aliases (Blocker, Critical, Major, Minor, Trivial)
   - Removed redundant PRIORITY_ICONS dict from priority_mapper.py

2. **Created JiraIssueType enum**:
   - Standard issue types: Bug, Story, Task, Epic, Sub-task, Improvement, etc.
   - Added `icon` property and `get_icon()` class method
   - Replaced ISSUE_TYPE_ICONS dict in issue_type_mapper.py

3. **Created JiraStatusCategory enum**:
   - Status categories: TO_DO, IN_PROGRESS, DONE
   - Added `icon` property and `get_icon()` class method
   - Handles both category keys and names (case-insensitive)
   - Replaced STATUS_CATEGORY_ICONS dict in status_mapper.py

**Benefits**:
- ✅ Type safety: IDE autocomplete and type checking
- ✅ Single source of truth: Icons defined once in enums
- ✅ Maintainability: Centralized logic, easier to extend
- ✅ Consistency: All mappers use same enum pattern

**Migration**:
- All mappers now use enum class methods instead of dict lookups
- Backward compatible: Handles unknown values gracefully with default icons

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants