titan-cli 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (146) hide show
  1. titan_cli/__init__.py +3 -0
  2. titan_cli/__main__.py +4 -0
  3. titan_cli/ai/__init__.py +0 -0
  4. titan_cli/ai/agents/__init__.py +15 -0
  5. titan_cli/ai/agents/base.py +152 -0
  6. titan_cli/ai/client.py +170 -0
  7. titan_cli/ai/constants.py +56 -0
  8. titan_cli/ai/exceptions.py +48 -0
  9. titan_cli/ai/models.py +34 -0
  10. titan_cli/ai/oauth_helper.py +120 -0
  11. titan_cli/ai/providers/__init__.py +9 -0
  12. titan_cli/ai/providers/anthropic.py +117 -0
  13. titan_cli/ai/providers/base.py +75 -0
  14. titan_cli/ai/providers/gemini.py +278 -0
  15. titan_cli/cli.py +59 -0
  16. titan_cli/clients/__init__.py +1 -0
  17. titan_cli/clients/gcloud_client.py +52 -0
  18. titan_cli/core/__init__.py +3 -0
  19. titan_cli/core/config.py +274 -0
  20. titan_cli/core/discovery.py +51 -0
  21. titan_cli/core/errors.py +81 -0
  22. titan_cli/core/models.py +52 -0
  23. titan_cli/core/plugins/available.py +36 -0
  24. titan_cli/core/plugins/models.py +67 -0
  25. titan_cli/core/plugins/plugin_base.py +108 -0
  26. titan_cli/core/plugins/plugin_registry.py +163 -0
  27. titan_cli/core/secrets.py +141 -0
  28. titan_cli/core/workflows/__init__.py +22 -0
  29. titan_cli/core/workflows/models.py +88 -0
  30. titan_cli/core/workflows/project_step_source.py +86 -0
  31. titan_cli/core/workflows/workflow_exceptions.py +17 -0
  32. titan_cli/core/workflows/workflow_filter_service.py +137 -0
  33. titan_cli/core/workflows/workflow_registry.py +419 -0
  34. titan_cli/core/workflows/workflow_sources.py +307 -0
  35. titan_cli/engine/__init__.py +39 -0
  36. titan_cli/engine/builder.py +159 -0
  37. titan_cli/engine/context.py +82 -0
  38. titan_cli/engine/mock_context.py +176 -0
  39. titan_cli/engine/results.py +91 -0
  40. titan_cli/engine/steps/ai_assistant_step.py +185 -0
  41. titan_cli/engine/steps/command_step.py +93 -0
  42. titan_cli/engine/utils/__init__.py +3 -0
  43. titan_cli/engine/utils/venv.py +31 -0
  44. titan_cli/engine/workflow_executor.py +187 -0
  45. titan_cli/external_cli/__init__.py +0 -0
  46. titan_cli/external_cli/configs.py +17 -0
  47. titan_cli/external_cli/launcher.py +65 -0
  48. titan_cli/messages.py +121 -0
  49. titan_cli/ui/tui/__init__.py +205 -0
  50. titan_cli/ui/tui/__previews__/statusbar_preview.py +88 -0
  51. titan_cli/ui/tui/app.py +113 -0
  52. titan_cli/ui/tui/icons.py +70 -0
  53. titan_cli/ui/tui/screens/__init__.py +24 -0
  54. titan_cli/ui/tui/screens/ai_config.py +498 -0
  55. titan_cli/ui/tui/screens/ai_config_wizard.py +882 -0
  56. titan_cli/ui/tui/screens/base.py +110 -0
  57. titan_cli/ui/tui/screens/cli_launcher.py +151 -0
  58. titan_cli/ui/tui/screens/global_setup_wizard.py +363 -0
  59. titan_cli/ui/tui/screens/main_menu.py +162 -0
  60. titan_cli/ui/tui/screens/plugin_config_wizard.py +550 -0
  61. titan_cli/ui/tui/screens/plugin_management.py +377 -0
  62. titan_cli/ui/tui/screens/project_setup_wizard.py +686 -0
  63. titan_cli/ui/tui/screens/workflow_execution.py +592 -0
  64. titan_cli/ui/tui/screens/workflows.py +249 -0
  65. titan_cli/ui/tui/textual_components.py +537 -0
  66. titan_cli/ui/tui/textual_workflow_executor.py +405 -0
  67. titan_cli/ui/tui/theme.py +102 -0
  68. titan_cli/ui/tui/widgets/__init__.py +40 -0
  69. titan_cli/ui/tui/widgets/button.py +108 -0
  70. titan_cli/ui/tui/widgets/header.py +116 -0
  71. titan_cli/ui/tui/widgets/panel.py +81 -0
  72. titan_cli/ui/tui/widgets/status_bar.py +115 -0
  73. titan_cli/ui/tui/widgets/table.py +77 -0
  74. titan_cli/ui/tui/widgets/text.py +177 -0
  75. titan_cli/utils/__init__.py +0 -0
  76. titan_cli/utils/autoupdate.py +155 -0
  77. titan_cli-0.1.0.dist-info/METADATA +149 -0
  78. titan_cli-0.1.0.dist-info/RECORD +146 -0
  79. titan_cli-0.1.0.dist-info/WHEEL +4 -0
  80. titan_cli-0.1.0.dist-info/entry_points.txt +9 -0
  81. titan_cli-0.1.0.dist-info/licenses/LICENSE +201 -0
  82. titan_plugin_git/__init__.py +1 -0
  83. titan_plugin_git/clients/__init__.py +8 -0
  84. titan_plugin_git/clients/git_client.py +772 -0
  85. titan_plugin_git/exceptions.py +40 -0
  86. titan_plugin_git/messages.py +112 -0
  87. titan_plugin_git/models.py +39 -0
  88. titan_plugin_git/plugin.py +118 -0
  89. titan_plugin_git/steps/__init__.py +1 -0
  90. titan_plugin_git/steps/ai_commit_message_step.py +171 -0
  91. titan_plugin_git/steps/branch_steps.py +104 -0
  92. titan_plugin_git/steps/commit_step.py +80 -0
  93. titan_plugin_git/steps/push_step.py +63 -0
  94. titan_plugin_git/steps/status_step.py +59 -0
  95. titan_plugin_git/workflows/__previews__/__init__.py +1 -0
  96. titan_plugin_git/workflows/__previews__/commit_ai_preview.py +124 -0
  97. titan_plugin_git/workflows/commit-ai.yaml +28 -0
  98. titan_plugin_github/__init__.py +11 -0
  99. titan_plugin_github/agents/__init__.py +6 -0
  100. titan_plugin_github/agents/config_loader.py +130 -0
  101. titan_plugin_github/agents/issue_generator.py +353 -0
  102. titan_plugin_github/agents/pr_agent.py +528 -0
  103. titan_plugin_github/clients/__init__.py +8 -0
  104. titan_plugin_github/clients/github_client.py +1105 -0
  105. titan_plugin_github/config/__init__.py +0 -0
  106. titan_plugin_github/config/pr_agent.toml +85 -0
  107. titan_plugin_github/exceptions.py +28 -0
  108. titan_plugin_github/messages.py +88 -0
  109. titan_plugin_github/models.py +330 -0
  110. titan_plugin_github/plugin.py +131 -0
  111. titan_plugin_github/steps/__init__.py +12 -0
  112. titan_plugin_github/steps/ai_pr_step.py +172 -0
  113. titan_plugin_github/steps/create_pr_step.py +86 -0
  114. titan_plugin_github/steps/github_prompt_steps.py +171 -0
  115. titan_plugin_github/steps/issue_steps.py +143 -0
  116. titan_plugin_github/steps/preview_step.py +40 -0
  117. titan_plugin_github/utils.py +82 -0
  118. titan_plugin_github/workflows/__previews__/__init__.py +1 -0
  119. titan_plugin_github/workflows/__previews__/create_pr_ai_preview.py +140 -0
  120. titan_plugin_github/workflows/create-issue-ai.yaml +32 -0
  121. titan_plugin_github/workflows/create-pr-ai.yaml +49 -0
  122. titan_plugin_jira/__init__.py +8 -0
  123. titan_plugin_jira/agents/__init__.py +6 -0
  124. titan_plugin_jira/agents/config_loader.py +154 -0
  125. titan_plugin_jira/agents/jira_agent.py +553 -0
  126. titan_plugin_jira/agents/prompts.py +364 -0
  127. titan_plugin_jira/agents/response_parser.py +435 -0
  128. titan_plugin_jira/agents/token_tracker.py +223 -0
  129. titan_plugin_jira/agents/validators.py +246 -0
  130. titan_plugin_jira/clients/jira_client.py +745 -0
  131. titan_plugin_jira/config/jira_agent.toml +92 -0
  132. titan_plugin_jira/config/templates/issue_analysis.md.j2 +78 -0
  133. titan_plugin_jira/exceptions.py +37 -0
  134. titan_plugin_jira/formatters/__init__.py +6 -0
  135. titan_plugin_jira/formatters/markdown_formatter.py +245 -0
  136. titan_plugin_jira/messages.py +115 -0
  137. titan_plugin_jira/models.py +89 -0
  138. titan_plugin_jira/plugin.py +264 -0
  139. titan_plugin_jira/steps/ai_analyze_issue_step.py +105 -0
  140. titan_plugin_jira/steps/get_issue_step.py +82 -0
  141. titan_plugin_jira/steps/prompt_select_issue_step.py +80 -0
  142. titan_plugin_jira/steps/search_saved_query_step.py +238 -0
  143. titan_plugin_jira/utils/__init__.py +13 -0
  144. titan_plugin_jira/utils/issue_sorter.py +140 -0
  145. titan_plugin_jira/utils/saved_queries.py +150 -0
  146. titan_plugin_jira/workflows/analyze-jira-issues.yaml +34 -0
@@ -0,0 +1,92 @@
1
+ [agent]
2
+ name = "JiraAgent"
3
+ description = "AI agent specialized in JIRA issue analysis for iOS, Android, and BFF development"
4
+ version = "1.0.0"
5
+
6
+ [agent.capabilities]
7
+ issue_analysis = true
8
+ requirements_extraction = true
9
+ subtask_generation = true
10
+ description_enhancement = true
11
+ label_suggestion = true
12
+ risk_scoring = true
13
+ gherkin_generation = true
14
+
15
+ [agent.limits]
16
+ max_description_length = 8000
17
+ max_subtasks = 6
18
+ max_comments_to_analyze = 8
19
+ max_linked_issues = 5
20
+ temperature = 0.3
21
+ max_tokens = 2000
22
+
23
+ [agent.prompts.requirements_analysis]
24
+ system = """You are a technical analyst. Extract requirements from JIRA issues.
25
+ CRITICAL: You MUST follow the exact format specified in the user prompt.
26
+ Do not add extra sections or change the format."""
27
+ [agent.prompts.description_enhancement]
28
+ system = """You are a Technical Writer and QA Specialist.
29
+ Structure:
30
+ - **Overview**: What and Why.
31
+ - **Technical Context**: Specifics for iOS, Android, or BFF.
32
+ - **Acceptance Criteria**: Using [ ] checkboxes.
33
+ - **Gherkin Scenarios**:
34
+ Generate Given/When/Then scenarios for the main success path and at least one edge case.
35
+ Use code blocks:
36
+ Scenario: [Name]
37
+ Given [context]
38
+ When [action]
39
+ Then [outcome]
40
+ """
41
+
42
+ [agent.prompts.subtask_suggestion]
43
+ system = """You are a Tech Lead. Suggest subtasks ONLY for iOS, Android, or BFF.
44
+ Include always:
45
+ - 1 subtask for Implementation.
46
+ - 1 subtask for QA/Gherkin validation.
47
+ - 1 subtask for Documentation/Code Review.
48
+ Complexity: (XS, S, M, L, XL).
49
+ """
50
+
51
+ [agent.prompts.smart_labeling]
52
+ system = """You are a classification bot. STRICTLY use only these labels. DO NOT create new ones:
53
+ - Platforms: [iOS, Android, BFF]
54
+ - Types: [bug, feature, tech-debt, spike, refactor]
55
+ - Priority: [blocker, high-impact, low-priority]
56
+
57
+ If the issue does not fit any platform, flag it as 'Needs Triage'.
58
+ Output: comma-separated list only."""
59
+
60
+ [agent.prompts.comment_generation]
61
+ system = """You are an experienced software engineer providing technical guidance.
62
+ Your task is to generate helpful comments for JIRA issues.
63
+
64
+ Guidelines:
65
+ 1. Be constructive and helpful
66
+ 2. Provide technical insights specific to iOS, Android, or BFF
67
+ 3. Suggest solutions or alternatives
68
+ 4. Ask clarifying questions when needed
69
+ 5. Reference related issues when relevant
70
+ 6. Keep it concise but informative
71
+ 7. Consider multi-platform implications
72
+ """
73
+
74
+ [agent.features]
75
+ enable_requirement_extraction = true
76
+ enable_subtasks = true
77
+ enable_risk_analysis = true # Enable risk and complexity analysis
78
+ enable_dependency_detection = true # Enable dependency detection
79
+ enable_acceptance_criteria = true # Enable acceptance criteria extraction
80
+ enable_debug_output = true
81
+
82
+ [agent.formatting]
83
+ # Optional: Specify a Jinja2 template for formatting analysis output
84
+ # If not set or template not found, uses built-in Python formatter
85
+ # Template must be in config/templates/ directory
86
+ template = "issue_analysis.md.j2"
87
+
88
+ [agent.metadata]
89
+ author = "Titan CLI Team"
90
+ updated = "2024-12-22"
91
+ stack = ["iOS", "Android", "BFF"]
92
+ testing_framework = "Gherkin/Cucumber"
@@ -0,0 +1,78 @@
1
+ # JIRA Issue Analysis
2
+
3
+ {% if functional_requirements or non_functional_requirements %}
4
+ ## 1. Requirements Breakdown
5
+
6
+ {% if functional_requirements %}
7
+ **Functional Requirements:**
8
+ {% for req in functional_requirements %}
9
+ - {{ req }}
10
+ {% endfor %}
11
+ {% endif %}
12
+
13
+ {% if non_functional_requirements %}
14
+ **Non-Functional Requirements:**
15
+ {% for req in non_functional_requirements %}
16
+ - {{ req }}
17
+ {% endfor %}
18
+ {% endif %}
19
+ {% endif %}
20
+
21
+ {% if acceptance_criteria %}
22
+ ## 2. Acceptance Criteria
23
+
24
+ {% for criterion in acceptance_criteria %}
25
+ - [ ] {{ criterion }}
26
+ {% endfor %}
27
+ {% endif %}
28
+
29
+ {% if technical_approach %}
30
+ ## 3. Technical Approach
31
+
32
+ {{ technical_approach }}
33
+ {% endif %}
34
+
35
+ {% if dependencies %}
36
+ ## 4. Dependencies
37
+
38
+ {% for dep in dependencies %}
39
+ - {{ dep }}
40
+ {% endfor %}
41
+ {% endif %}
42
+
43
+ {% if risks %}
44
+ ## 5. Potential Risks
45
+
46
+ {% for risk in risks %}
47
+ - ⚠️ {{ risk }}
48
+ {% endfor %}
49
+ {% endif %}
50
+
51
+ {% if edge_cases %}
52
+ ## 6. Edge Cases to Consider
53
+
54
+ {% for edge_case in edge_cases %}
55
+ - {{ edge_case }}
56
+ {% endfor %}
57
+ {% endif %}
58
+
59
+ {% if suggested_subtasks %}
60
+ ## 7. Suggested Subtasks
61
+
62
+ {% for subtask in suggested_subtasks %}
63
+ **{{ loop.index }}. {{ subtask.summary }}**
64
+ {{ subtask.description }}
65
+
66
+ {% endfor %}
67
+ {% endif %}
68
+
69
+ {% if complexity_score or estimated_effort %}
70
+ ## 8. Complexity Assessment
71
+
72
+ {% if complexity_score %}
73
+ **Complexity:** {{ complexity_score|title }}
74
+ {% endif %}
75
+ {% if estimated_effort %}
76
+ **Estimated Effort:** {{ estimated_effort }}
77
+ {% endif %}
78
+ {% endif %}
@@ -0,0 +1,37 @@
1
+ # plugins/titan-plugin-jira/titan_plugin_jira/exceptions.py
2
+ """Custom exceptions for JIRA plugin."""
3
+
4
+ from typing import Dict, Optional
5
+
6
+
7
+ class JiraPluginError(Exception):
8
+ """Base exception for JIRA plugin errors."""
9
+ pass
10
+
11
+
12
+ class JiraConfigurationError(JiraPluginError):
13
+ """Raised when JIRA plugin configuration is invalid or missing."""
14
+ pass
15
+
16
+
17
+ class JiraClientError(JiraPluginError):
18
+ """Raised when JIRA client operations fail."""
19
+ pass
20
+
21
+
22
+ class JiraAPIError(Exception):
23
+ """Exception raised for JIRA API errors"""
24
+
25
+ def __init__(self, message: str, status_code: Optional[int] = None, response: Optional[Dict] = None):
26
+ self.message = message
27
+ self.status_code = status_code
28
+ self.response = response
29
+ super().__init__(self.message)
30
+
31
+
32
+ __all__ = [
33
+ "JiraPluginError",
34
+ "JiraConfigurationError",
35
+ "JiraClientError",
36
+ "JiraAPIError",
37
+ ]
@@ -0,0 +1,6 @@
1
+ # plugins/titan-plugin-jira/titan_plugin_jira/formatters/__init__.py
2
+ """Formatters for JIRA analysis output."""
3
+
4
+ from .markdown_formatter import IssueAnalysisMarkdownFormatter
5
+
6
+ __all__ = ["IssueAnalysisMarkdownFormatter"]
@@ -0,0 +1,245 @@
1
+ # plugins/titan-plugin-jira/titan_plugin_jira/formatters/markdown_formatter.py
2
+ """Markdown formatter for JIRA issue analysis."""
3
+
4
+ from typing import List, Optional
5
+ from pathlib import Path
6
+ from dataclasses import asdict
7
+ from ..agents.jira_agent import IssueAnalysis
8
+
9
+
10
+ class IssueAnalysisMarkdownFormatter:
11
+ """
12
+ Formats IssueAnalysis into markdown for display.
13
+
14
+ This class separates presentation logic from business logic,
15
+ making it easy to:
16
+ - Modify output format without touching agent code
17
+ - Test formatting independently
18
+ - Add other formatters (HTML, JSON, etc.) in the future
19
+ - Optionally use Jinja2 templates for custom formatting
20
+
21
+ Example:
22
+ >>> # Use built-in formatter (default)
23
+ >>> formatter = IssueAnalysisMarkdownFormatter()
24
+ >>> markdown = formatter.format(analysis)
25
+
26
+ >>> # Use custom Jinja2 template
27
+ >>> formatter = IssueAnalysisMarkdownFormatter(template_path="custom.md.j2")
28
+ >>> markdown = formatter.format(analysis)
29
+ """
30
+
31
+ def __init__(self, template_path: Optional[str] = None):
32
+ """
33
+ Initialize the formatter.
34
+
35
+ Args:
36
+ template_path: Optional path to Jinja2 template file.
37
+ If None, uses built-in Python formatter.
38
+ If provided, must be relative to config/templates/
39
+ """
40
+ self.template = None
41
+ if template_path:
42
+ self.template = self._load_template(template_path)
43
+
44
+ def _load_template(self, template_name: str):
45
+ """
46
+ Load Jinja2 template from config/templates/.
47
+
48
+ Args:
49
+ template_name: Template filename (e.g., "issue_analysis.md.j2")
50
+
51
+ Returns:
52
+ Jinja2 Template object or None if Jinja2 not available
53
+ """
54
+ try:
55
+ from jinja2 import Environment, FileSystemLoader
56
+ except ImportError:
57
+ # Jinja2 not installed, fall back to built-in formatter
58
+ return None
59
+
60
+ try:
61
+ # Templates directory is relative to this file
62
+ # This works both in development and when installed
63
+ templates_dir = Path(__file__).parent.parent / "config" / "templates"
64
+
65
+ if not templates_dir.exists():
66
+ return None
67
+
68
+ # Enable autoescape to prevent XSS vulnerabilities (CWE-116)
69
+ # This ensures all template variables are HTML-escaped by default
70
+ env = Environment(
71
+ loader=FileSystemLoader(str(templates_dir)),
72
+ autoescape=False
73
+ )
74
+ return env.get_template(template_name)
75
+ except Exception:
76
+ # Template not found or other error, fall back to built-in formatter
77
+ # Silently fail and use built-in formatter
78
+ return None
79
+
80
+ def format(self, analysis: IssueAnalysis) -> str:
81
+ """
82
+ Format IssueAnalysis into markdown.
83
+
84
+ Args:
85
+ analysis: IssueAnalysis object from JiraAgent
86
+
87
+ Returns:
88
+ Formatted markdown string
89
+ """
90
+ if self.template:
91
+ # Use Jinja2 template
92
+ return self._format_with_template(analysis)
93
+ else:
94
+ # Use built-in Python formatter
95
+ return self._format_builtin(analysis)
96
+
97
+ def _format_with_template(self, analysis: IssueAnalysis) -> str:
98
+ """
99
+ Format using Jinja2 template.
100
+
101
+ Args:
102
+ analysis: IssueAnalysis object
103
+
104
+ Returns:
105
+ Rendered markdown string
106
+ """
107
+ # Convert dataclass to dict for template
108
+ analysis_dict = asdict(analysis)
109
+ return self.template.render(**analysis_dict)
110
+
111
+ def _format_builtin(self, analysis: IssueAnalysis) -> str:
112
+ """
113
+ Format using built-in Python methods.
114
+
115
+ Args:
116
+ analysis: IssueAnalysis object
117
+
118
+ Returns:
119
+ Formatted markdown string
120
+ """
121
+ sections = []
122
+
123
+ # 1. Requirements Breakdown
124
+ if analysis.functional_requirements or analysis.non_functional_requirements:
125
+ sections.extend(self._format_requirements(analysis))
126
+
127
+ # 2. Acceptance Criteria
128
+ if analysis.acceptance_criteria:
129
+ sections.extend(self._format_acceptance_criteria(analysis))
130
+
131
+ # 3. Technical Approach
132
+ if analysis.technical_approach:
133
+ sections.extend(self._format_technical_approach(analysis))
134
+
135
+ # 4. Dependencies
136
+ if analysis.dependencies:
137
+ sections.extend(self._format_dependencies(analysis))
138
+
139
+ # 5. Potential Risks
140
+ if analysis.risks:
141
+ sections.extend(self._format_risks(analysis))
142
+
143
+ # 6. Edge Cases
144
+ if analysis.edge_cases:
145
+ sections.extend(self._format_edge_cases(analysis))
146
+
147
+ # 7. Suggested Subtasks
148
+ if analysis.suggested_subtasks:
149
+ sections.extend(self._format_subtasks(analysis))
150
+
151
+ # 8. Complexity & Effort
152
+ if analysis.complexity_score or analysis.estimated_effort:
153
+ sections.extend(self._format_complexity(analysis))
154
+
155
+ return "\n".join(sections)
156
+
157
+ def _format_requirements(self, analysis: IssueAnalysis) -> List[str]:
158
+ """Format requirements section."""
159
+ sections = ["## 1. Requirements Breakdown"]
160
+
161
+ if analysis.functional_requirements:
162
+ sections.append("\n**Functional Requirements:**")
163
+ for req in analysis.functional_requirements:
164
+ sections.append(f"- {req}")
165
+
166
+ if analysis.non_functional_requirements:
167
+ sections.append("\n**Non-Functional Requirements:**")
168
+ for req in analysis.non_functional_requirements:
169
+ sections.append(f"- {req}")
170
+
171
+ sections.append("")
172
+ return sections
173
+
174
+ def _format_acceptance_criteria(self, analysis: IssueAnalysis) -> List[str]:
175
+ """Format acceptance criteria section."""
176
+ sections = ["## 2. Acceptance Criteria"]
177
+
178
+ for criterion in analysis.acceptance_criteria:
179
+ sections.append(f"- [ ] {criterion}")
180
+
181
+ sections.append("")
182
+ return sections
183
+
184
+ def _format_technical_approach(self, analysis: IssueAnalysis) -> List[str]:
185
+ """Format technical approach section."""
186
+ sections = [
187
+ "## 3. Technical Approach",
188
+ analysis.technical_approach,
189
+ ""
190
+ ]
191
+ return sections
192
+
193
+ def _format_dependencies(self, analysis: IssueAnalysis) -> List[str]:
194
+ """Format dependencies section."""
195
+ sections = ["## 4. Dependencies"]
196
+
197
+ for dep in analysis.dependencies:
198
+ sections.append(f"- {dep}")
199
+
200
+ sections.append("")
201
+ return sections
202
+
203
+ def _format_risks(self, analysis: IssueAnalysis) -> List[str]:
204
+ """Format risks section."""
205
+ sections = ["## 5. Potential Risks"]
206
+
207
+ for risk in analysis.risks:
208
+ sections.append(f"- ⚠️ {risk}")
209
+
210
+ sections.append("")
211
+ return sections
212
+
213
+ def _format_edge_cases(self, analysis: IssueAnalysis) -> List[str]:
214
+ """Format edge cases section."""
215
+ sections = ["## 6. Edge Cases to Consider"]
216
+
217
+ for edge_case in analysis.edge_cases:
218
+ sections.append(f"- {edge_case}")
219
+
220
+ sections.append("")
221
+ return sections
222
+
223
+ def _format_subtasks(self, analysis: IssueAnalysis) -> List[str]:
224
+ """Format subtasks section."""
225
+ sections = ["## 7. Suggested Subtasks"]
226
+
227
+ for i, subtask in enumerate(analysis.suggested_subtasks, 1):
228
+ sections.append(f"\n**{i}. {subtask.get('summary', 'Subtask')}**")
229
+ sections.append(f"{subtask.get('description', '')}")
230
+
231
+ sections.append("")
232
+ return sections
233
+
234
+ def _format_complexity(self, analysis: IssueAnalysis) -> List[str]:
235
+ """Format complexity assessment section."""
236
+ sections = ["## 8. Complexity Assessment"]
237
+
238
+ if analysis.complexity_score:
239
+ sections.append(f"**Complexity:** {analysis.complexity_score.title()}")
240
+
241
+ if analysis.estimated_effort:
242
+ sections.append(f"**Estimated Effort:** {analysis.estimated_effort}")
243
+
244
+ sections.append("")
245
+ return sections
@@ -0,0 +1,115 @@
1
+ """
2
+ User-facing messages for JIRA plugin
3
+ """
4
+
5
+
6
+ class Messages:
7
+ """Container for all user-facing messages"""
8
+
9
+ class Plugin:
10
+ """Plugin-level messages"""
11
+ CLIENT_INIT_WARNING: str = "Warning: JiraPlugin could not initialize JiraClient: {e}"
12
+ CLIENT_NOT_AVAILABLE: str = "JiraPlugin not initialized or JIRA API not available."
13
+ CLIENT_NOT_AVAILABLE_IN_CONTEXT: str = "JIRA client is not available in the workflow context."
14
+ JIRA_CLIENT_NOT_AVAILABLE: str = "JIRA client not initialized. Please configure the plugin first."
15
+
16
+ class Steps:
17
+ """Step-level messages"""
18
+
19
+ class Search:
20
+ """Search issues step messages"""
21
+ SEARCHING: str = "Searching JIRA issues with JQL: {jql}"
22
+ SEARCH_SUCCESS: str = "Found {count} JIRA issue(s)"
23
+ NO_RESULTS: str = "No JIRA issues found matching the query"
24
+ SEARCH_FAILED: str = "Failed to search JIRA issues: {e}"
25
+ QUERY_NOT_FOUND: str = "Saved query '{query_name}' not found."
26
+ AVAILABLE_PREDEFINED: str = "Available predefined queries (first 15):"
27
+ MORE_QUERIES: str = " ... and {count} more"
28
+ CUSTOM_QUERIES_HEADER: str = "Custom queries from config:"
29
+ ADD_CUSTOM_HINT: str = "💡 Add custom queries to .titan/config.toml:"
30
+ CUSTOM_QUERY_EXAMPLE: str = "[jira.saved_queries]\nmy_custom = \"assignee = currentUser() AND status != Done\""
31
+ QUERY_NAME_REQUIRED: str = "query_name parameter is required"
32
+ PROJECT_REQUIRED: str = (
33
+ "Query '{query_name}' requires a 'project' parameter.\n"
34
+ "JQL template: {jql}\n\n"
35
+ "Provide it in workflow:\n"
36
+ " params:\n"
37
+ " query_name: \"{query_name}\"\n"
38
+ " project: \"PROJ\""
39
+ )
40
+
41
+ class GetIssue:
42
+ """Get issue step messages"""
43
+ GETTING_ISSUE: str = "Fetching JIRA issue: {issue_key}"
44
+ GET_SUCCESS: str = "Retrieved JIRA issue: {issue_key}"
45
+ GET_FAILED: str = "Failed to get JIRA issue: {e}"
46
+ ISSUE_NOT_FOUND: str = "JIRA issue not found: {issue_key}"
47
+
48
+ class CreateIssue:
49
+ """Create issue step messages"""
50
+ CREATING_ISSUE: str = "Creating JIRA issue: {summary}"
51
+ CREATE_SUCCESS: str = "Created JIRA issue: {issue_key}"
52
+ CREATE_FAILED: str = "Failed to create JIRA issue: {e}"
53
+ MISSING_SUMMARY: str = "Issue summary is required"
54
+
55
+ class UpdateStatus:
56
+ """Update status step messages"""
57
+ UPDATING_STATUS: str = "Updating JIRA issue {issue_key} to status: {status}"
58
+ UPDATE_SUCCESS: str = "Updated JIRA issue {issue_key} to {status}"
59
+ UPDATE_FAILED: str = "Failed to update JIRA issue status: {e}"
60
+ INVALID_TRANSITION: str = "Cannot transition to '{status}'. Available: {transitions}"
61
+
62
+ class AddComment:
63
+ """Add comment step messages"""
64
+ ADDING_COMMENT: str = "Adding comment to JIRA issue: {issue_key}"
65
+ COMMENT_SUCCESS: str = "Added comment to JIRA issue {issue_key}"
66
+ COMMENT_FAILED: str = "Failed to add comment: {e}"
67
+
68
+ class LinkPR:
69
+ """Link PR step messages"""
70
+ LINKING_PR: str = "Linking PR to JIRA issue: {issue_key}"
71
+ LINK_SUCCESS: str = "Linked PR to JIRA issue {issue_key}"
72
+ LINK_FAILED: str = "Failed to link PR: {e}"
73
+
74
+ class AIIssue:
75
+ """AI issue generation step messages"""
76
+ AI_NOT_CONFIGURED: str = "AI not configured. Run 'titan ai configure' to enable AI features."
77
+ AI_NOT_CONFIGURED_SKIP: str = "AI not configured - skipping analysis"
78
+ GENERATING_ISSUE: str = "Generating JIRA issue with AI..."
79
+ GENERATION_SUCCESS: str = "AI generated JIRA issue successfully"
80
+ GENERATION_FAILED: str = "AI generation failed: {e}"
81
+ INVALID_ISSUE_TYPE: str = "Invalid issue type: {issue_type}. Use: bug, feature, or task"
82
+ USER_REJECTED: str = "User rejected AI-generated issue"
83
+ CONFIRM_USE_AI: str = "Use this AI-generated issue?"
84
+ NO_ISSUE_FOUND: str = "No issue found to analyze"
85
+ ANALYZING: str = "Analyzing issue with AI..."
86
+
87
+ class ExtractKey:
88
+ """Extract issue key step messages"""
89
+ EXTRACTING_KEY: str = "Extracting JIRA key from branch: {branch}"
90
+ KEY_FOUND: str = "Found JIRA key: {issue_key}"
91
+ KEY_NOT_FOUND: str = "No JIRA key found in branch name: {branch}"
92
+ INVALID_BRANCH_FORMAT: str = "Branch name doesn't follow pattern: feature/PROJ-123-description"
93
+
94
+ class PromptSelectIssue:
95
+ """Prompt select issue step messages"""
96
+ NO_ISSUES_AVAILABLE: str = "No JIRA issues available to select from"
97
+ NO_ISSUE_SELECTED: str = "No issue selected"
98
+ UI_NOT_AVAILABLE: str = "UI not available for prompting"
99
+ ASK_ISSUE_NUMBER: str = "Enter issue number to analyze"
100
+ ISSUE_SELECTED: str = "Selected: {key} - {summary}"
101
+ ISSUE_SELECTION_CONFIRM: str = "Selected: {key} - {summary}"
102
+ SELECT_SUCCESS: str = "Selected issue: {key}"
103
+
104
+ class JIRA:
105
+ """JIRA-specific messages"""
106
+ AUTHENTICATION_FAILED: str = "JIRA authentication failed. Check your API token."
107
+ RATE_LIMIT_EXCEEDED: str = "JIRA API rate limit exceeded. Please wait and try again."
108
+ NETWORK_ERROR: str = "Network error connecting to JIRA: {e}"
109
+ INVALID_PROJECT: str = "Invalid JIRA project: {project}"
110
+ INVALID_JQL: str = "Invalid JQL query: {jql}"
111
+
112
+
113
+ msg = Messages()
114
+
115
+ __all__ = ["msg", "Messages"]
@@ -0,0 +1,89 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ JIRA Plugin Data Models
4
+
5
+ All Pydantic models and dataclasses for the JIRA plugin.
6
+ """
7
+
8
+ from dataclasses import dataclass
9
+ from enum import Enum
10
+ from typing import Dict, List, Optional, Any
11
+
12
+
13
+ class IssueStatus(str, Enum):
14
+ """Common JIRA issue statuses"""
15
+ TODO = "To Do"
16
+ IN_PROGRESS = "In Progress"
17
+ IN_REVIEW = "In Review"
18
+ READY_FOR_QA = "Ready for QA"
19
+ IN_QA = "In QA"
20
+ DONE = "Done"
21
+ BLOCKED = "Blocked"
22
+
23
+
24
+ @dataclass
25
+ class JiraProject:
26
+ """Represents a JIRA project"""
27
+ id: str
28
+ key: str
29
+ name: str
30
+ description: Optional[str] = None
31
+ project_type: Optional[str] = None
32
+ lead: Optional[str] = None
33
+
34
+
35
+ @dataclass
36
+ class JiraIssueType:
37
+ """Represents a JIRA issue type"""
38
+ id: str
39
+ name: str
40
+ description: Optional[str] = None
41
+ subtask: bool = False
42
+
43
+
44
+ @dataclass
45
+ class JiraTransition:
46
+ """Represents a JIRA workflow transition"""
47
+ id: str
48
+ name: str
49
+ to_status: str
50
+
51
+
52
+ @dataclass
53
+ class JiraComment:
54
+ """Represents a JIRA comment"""
55
+ id: str
56
+ author: str
57
+ body: str
58
+ created: str
59
+ updated: Optional[str] = None
60
+
61
+
62
+ @dataclass
63
+ class JiraTicket:
64
+ """Represents a JIRA ticket/issue"""
65
+ key: str
66
+ id: str
67
+ summary: str
68
+ description: Optional[str]
69
+ status: str
70
+ issue_type: str
71
+ assignee: Optional[str]
72
+ reporter: str
73
+ priority: str
74
+ created: str
75
+ updated: str
76
+ labels: List[str]
77
+ components: List[str]
78
+ fix_versions: List[str]
79
+ raw: Dict[str, Any] # Original API response
80
+
81
+
82
+ __all__ = [
83
+ "IssueStatus",
84
+ "JiraProject",
85
+ "JiraIssueType",
86
+ "JiraTransition",
87
+ "JiraComment",
88
+ "JiraTicket",
89
+ ]