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,238 @@
1
+ """
2
+ Search JIRA issues using saved query from utils registry
3
+ """
4
+
5
+ from titan_cli.engine import WorkflowContext, WorkflowResult, Success, Error
6
+ from titan_cli.ui.tui.widgets import Panel, Table
7
+ from ..exceptions import JiraAPIError
8
+ from ..messages import msg
9
+ from ..utils import SAVED_QUERIES, IssueSorter
10
+
11
+
12
+ def search_saved_query_step(ctx: WorkflowContext) -> WorkflowResult:
13
+ """
14
+ Search JIRA issues using a saved query.
15
+
16
+ Queries are predefined in utils.SAVED_QUERIES (from utils/saved_queries.py).
17
+ Projects can override or add custom queries in .titan/config.toml under [jira.saved_queries].
18
+
19
+ Inputs (from ctx.data):
20
+ query_name (str): Name of saved query (e.g., "my_bugs", "team_bugs")
21
+ project (str, optional): Project key for parameterized queries (e.g., "ECAPP")
22
+ max_results (int, optional): Maximum number of results (default: 50)
23
+
24
+ Outputs (saved to ctx.data):
25
+ jira_issues (list): List of JiraTicket objects
26
+ jira_issue_count (int): Number of issues found
27
+ used_query_name (str): Name of the query that was used
28
+
29
+ Returns:
30
+ Success: Issues found
31
+ Error: Query not found or search failed
32
+
33
+ Available predefined queries (from utils):
34
+ Personal: my_open_issues, my_bugs, my_in_review, my_in_progress
35
+ Team: current_sprint, team_open, team_bugs, team_in_review
36
+ Priority: critical_issues, high_priority, blocked_issues
37
+ Time: updated_today, created_this_week, recent_bugs
38
+ Status: todo_issues, in_progress_all, done_recently
39
+
40
+ Example usage in workflow:
41
+ ```yaml
42
+ - id: search
43
+ plugin: jira
44
+ step: search_saved_query
45
+ params:
46
+ query_name: "my_bugs"
47
+
48
+ # For queries with {project} parameter:
49
+ - id: search_team
50
+ plugin: jira
51
+ step: search_saved_query
52
+ params:
53
+ query_name: "team_bugs"
54
+ project: "ECAPP"
55
+ ```
56
+ """
57
+ if not ctx.textual:
58
+ return Error("Textual UI context is not available for this step.")
59
+
60
+ if not ctx.jira:
61
+ ctx.textual.mount(Panel(msg.Plugin.CLIENT_NOT_AVAILABLE_IN_CONTEXT, panel_type="error"))
62
+ return Error(msg.Plugin.CLIENT_NOT_AVAILABLE_IN_CONTEXT)
63
+
64
+ # Get query name
65
+ query_name = ctx.get("query_name")
66
+ if not query_name:
67
+ ctx.textual.mount(Panel(msg.Steps.Search.QUERY_NAME_REQUIRED, panel_type="error"))
68
+ return Error(msg.Steps.Search.QUERY_NAME_REQUIRED)
69
+
70
+ # Get all predefined queries from utils
71
+ predefined_queries = SAVED_QUERIES.get_all()
72
+
73
+ # Get custom queries from config (if any)
74
+ custom_queries = {}
75
+ try:
76
+ if hasattr(ctx, 'plugin_manager') and ctx.plugin_manager is not None:
77
+ jira_plugin = ctx.plugin_manager.get_plugin('jira')
78
+ if jira_plugin and hasattr(jira_plugin, '_config') and jira_plugin._config is not None:
79
+ custom_queries = jira_plugin._config.saved_queries or {}
80
+ except Exception:
81
+ pass
82
+
83
+ # Merge queries (custom queries override predefined)
84
+ all_queries = {**predefined_queries, **custom_queries}
85
+
86
+ # Look up the query
87
+ if query_name not in all_queries:
88
+ # Build helpful error message
89
+ predefined_list = list(predefined_queries.keys())
90
+ custom_list = list(custom_queries.keys())
91
+
92
+ error_msg = msg.Steps.Search.QUERY_NOT_FOUND.format(query_name=query_name) + "\n\n"
93
+ error_msg += msg.Steps.Search.AVAILABLE_PREDEFINED + "\n "
94
+ error_msg += "\n ".join(predefined_list[:15])
95
+ if len(predefined_list) > 15:
96
+ error_msg += "\n" + msg.Steps.Search.MORE_QUERIES.format(count=len(predefined_list) - 15)
97
+
98
+ if custom_list:
99
+ error_msg += "\n\n" + msg.Steps.Search.CUSTOM_QUERIES_HEADER + "\n "
100
+ error_msg += "\n ".join(custom_list)
101
+ else:
102
+ error_msg += "\n\n" + msg.Steps.Search.ADD_CUSTOM_HINT + "\n"
103
+ error_msg += msg.Steps.Search.CUSTOM_QUERY_EXAMPLE
104
+
105
+ ctx.textual.mount(Panel(error_msg, panel_type="error"))
106
+ return Error(error_msg)
107
+
108
+ jql = all_queries[query_name]
109
+
110
+ # Get parameters for query formatting
111
+ project = ctx.get("project")
112
+
113
+ # Format query if it has parameters
114
+ if "{project}" in jql:
115
+ if not project:
116
+ # Try to use default project from JIRA client
117
+ if ctx.jira and hasattr(ctx.jira, 'project_key'):
118
+ project = ctx.jira.project_key
119
+
120
+ if not project:
121
+ error_msg = msg.Steps.Search.PROJECT_REQUIRED.format(query_name=query_name, jql=jql)
122
+ ctx.textual.mount(Panel(error_msg, panel_type="error"))
123
+ return Error(error_msg)
124
+
125
+ jql = jql.format(project=project)
126
+
127
+ # Show which query is being used
128
+ is_custom = query_name in custom_queries
129
+ source_label = "Custom" if is_custom else "Predefined"
130
+
131
+ ctx.textual.text("")
132
+ ctx.textual.text(f"Using {source_label} Query: {query_name}", markup="bold")
133
+ ctx.textual.text(f" JQL: {jql}", markup="dim")
134
+ ctx.textual.text("")
135
+
136
+ # Get max results
137
+ max_results = ctx.get("max_results", 50)
138
+
139
+ try:
140
+ # Execute search with loading indicator
141
+ with ctx.textual.loading("Searching JIRA issues..."):
142
+ issues = ctx.jira.search_tickets(jql=jql, max_results=max_results)
143
+
144
+ if not issues:
145
+ ctx.textual.mount(
146
+ Panel(
147
+ text=f"No issues found for query: {query_name}",
148
+ panel_type="info"
149
+ )
150
+ )
151
+ return Success(
152
+ "No issues found",
153
+ metadata={
154
+ "jira_issues": [],
155
+ "jira_issue_count": 0,
156
+ "used_query_name": query_name
157
+ }
158
+ )
159
+
160
+ # Show results
161
+ ctx.textual.mount(
162
+ Panel(
163
+ text=f"Found {len(issues)} issues",
164
+ panel_type="success"
165
+ )
166
+ )
167
+ ctx.textual.text("")
168
+
169
+ # Show detailed table
170
+ ctx.textual.text("Found Issues:", markup="bold")
171
+ ctx.textual.text("")
172
+
173
+ try:
174
+ # Sort issues intelligently
175
+ sorter = IssueSorter()
176
+ sorted_issues = sorter.sort(issues)
177
+
178
+ # Prepare table data with row numbers for selection
179
+ headers = ["#", "Key", "Status", "Summary", "Assignee", "Type", "Priority"]
180
+ rows = []
181
+ for i, issue in enumerate(sorted_issues, 1):
182
+ assignee = issue.assignee or "Unassigned"
183
+ status = issue.status or "Unknown"
184
+ priority = issue.priority or "Unknown"
185
+ issue_type = issue.issue_type or "Unknown"
186
+ summary = (issue.summary or "No summary")[:60]
187
+
188
+ rows.append([
189
+ str(i),
190
+ issue.key,
191
+ status,
192
+ summary,
193
+ assignee,
194
+ issue_type,
195
+ priority
196
+ ])
197
+
198
+ # Render table using textual widget
199
+ ctx.textual.mount(
200
+ Table(
201
+ headers=headers,
202
+ rows=rows,
203
+ title=f"Issues (sorted by {sorter.get_sort_description()})"
204
+ )
205
+ )
206
+
207
+ # Use sorted issues for downstream steps
208
+ issues = sorted_issues
209
+ except Exception as e:
210
+ # If table rendering fails, show error but continue with raw issue list
211
+ ctx.textual.text(f"Error rendering table: {e}", markup="red")
212
+ ctx.textual.text(f"Found {len(issues)} issues (showing raw data)", markup="cyan")
213
+ for i, issue in enumerate(issues, 1):
214
+ ctx.textual.text(f"{i}. {issue.key} - {getattr(issue, 'summary', 'N/A')}")
215
+ ctx.textual.text("")
216
+
217
+ return Success(
218
+ f"Found {len(issues)} issues using query: {query_name}",
219
+ metadata={
220
+ "jira_issues": issues,
221
+ "jira_issue_count": len(issues),
222
+ "used_query_name": query_name
223
+ }
224
+ )
225
+
226
+ except JiraAPIError as e:
227
+ error_msg = f"JIRA search failed: {e}"
228
+ ctx.textual.mount(Panel(error_msg, panel_type="error"))
229
+ return Error(error_msg)
230
+ except Exception as e:
231
+ import traceback
232
+ error_detail = traceback.format_exc()
233
+ error_msg = f"Unexpected error: {e}\n\nTraceback:\n{error_detail}"
234
+ ctx.textual.mount(Panel(error_msg, panel_type="error"))
235
+ return Error(error_msg)
236
+
237
+
238
+ __all__ = ["search_saved_query_step"]
@@ -0,0 +1,13 @@
1
+ """
2
+ JIRA plugin utilities
3
+ """
4
+
5
+ from .saved_queries import SavedQueries, SAVED_QUERIES
6
+ from .issue_sorter import IssueSorter, IssueSortConfig
7
+
8
+ __all__ = [
9
+ "SavedQueries",
10
+ "SAVED_QUERIES",
11
+ "IssueSorter",
12
+ "IssueSortConfig"
13
+ ]
@@ -0,0 +1,140 @@
1
+ """
2
+ Issue Sorter Utility
3
+
4
+ Provides intelligent sorting for JIRA issues based on status, priority, and key.
5
+ """
6
+
7
+ from typing import List, Dict
8
+ from dataclasses import dataclass
9
+
10
+
11
+ @dataclass
12
+ class IssueSortConfig:
13
+ """Configuration for issue sorting priorities."""
14
+
15
+ status_order: Dict[str, int]
16
+ priority_order: Dict[str, int]
17
+
18
+ @classmethod
19
+ def default(cls) -> "IssueSortConfig":
20
+ """
21
+ Returns default sorting configuration.
22
+
23
+ Status order: To Do → In Progress → Done
24
+ Priority order: Critical → High → Medium → Low
25
+ """
26
+ return cls(
27
+ status_order={
28
+ # To Do / Open states
29
+ "to do": 0,
30
+ "open": 0,
31
+ "backlog": 0,
32
+ # In Progress states
33
+ "in progress": 1,
34
+ "in review": 1,
35
+ "code review": 1,
36
+ "review": 1,
37
+ # Done / Closed states
38
+ "done": 2,
39
+ "closed": 2,
40
+ "resolved": 2,
41
+ "completed": 2
42
+ },
43
+ priority_order={
44
+ # Critical/Highest
45
+ "critical": 0,
46
+ "highest": 0,
47
+ "blocker": 0,
48
+ # High
49
+ "high": 1,
50
+ # Medium/Normal
51
+ "medium": 2,
52
+ "normal": 2,
53
+ # Low/Lowest
54
+ "low": 3,
55
+ "lowest": 3,
56
+ "trivial": 3
57
+ }
58
+ )
59
+
60
+
61
+ class IssueSorter:
62
+ """
63
+ Sorts JIRA issues intelligently based on status, priority, and key.
64
+
65
+ Sorting criteria (in order):
66
+ 1. Status (To Do → In Progress → Done)
67
+ 2. Priority (Critical → High → Medium → Low)
68
+ 3. Key (alphabetical)
69
+
70
+ Unknown statuses/priorities are placed at the end.
71
+
72
+ Example:
73
+ >>> from titan_plugin_jira.models import JiraTicket
74
+ >>> sorter = IssueSorter()
75
+ >>> sorted_issues = sorter.sort(issues)
76
+ """
77
+
78
+ def __init__(self, config: IssueSortConfig = None):
79
+ """
80
+ Initialize the sorter with optional custom configuration.
81
+
82
+ Args:
83
+ config: IssueSortConfig instance. If None, uses default configuration.
84
+ """
85
+ self.config = config or IssueSortConfig.default()
86
+ self._unknown_value = 99 # Value for unknown statuses/priorities
87
+
88
+ def sort(self, issues: List) -> List:
89
+ """
90
+ Sort issues based on status, priority, and key.
91
+
92
+ Args:
93
+ issues: List of JiraTicket objects
94
+
95
+ Returns:
96
+ Sorted list of JiraTicket objects
97
+
98
+ Example:
99
+ >>> sorter = IssueSorter()
100
+ >>> sorted_issues = sorter.sort(my_issues)
101
+ """
102
+ return sorted(issues, key=self._sort_key)
103
+
104
+ def _sort_key(self, issue) -> tuple:
105
+ """
106
+ Generate sort key for an issue.
107
+
108
+ Returns tuple of (status_order, priority_order, key) for sorting.
109
+ """
110
+ status_value = self._get_status_order(issue.status)
111
+ priority_value = self._get_priority_order(issue.priority)
112
+
113
+ return (status_value, priority_value, issue.key)
114
+
115
+ def _get_status_order(self, status: str) -> int:
116
+ """Get sort order for a status (case-insensitive)."""
117
+ if not status:
118
+ return self._unknown_value
119
+ return self.config.status_order.get(
120
+ status.lower(),
121
+ self._unknown_value
122
+ )
123
+
124
+ def _get_priority_order(self, priority: str) -> int:
125
+ """Get sort order for a priority (case-insensitive)."""
126
+ if not priority:
127
+ return self._unknown_value
128
+ return self.config.priority_order.get(
129
+ priority.lower(),
130
+ self._unknown_value
131
+ )
132
+
133
+ def get_sort_description(self) -> str:
134
+ """
135
+ Get human-readable description of the sort order.
136
+
137
+ Returns:
138
+ Description string like "Status → Priority → Key"
139
+ """
140
+ return "Status → Priority → Key"
@@ -0,0 +1,150 @@
1
+ """
2
+ Predefined JIRA saved queries registry.
3
+
4
+ Common JQL queries that can be used across projects.
5
+ Projects can extend these with custom queries in .titan/config.toml
6
+ """
7
+
8
+ from typing import Dict
9
+
10
+
11
+ class SavedQueries:
12
+ """
13
+ Registry of predefined JQL queries.
14
+
15
+ These are common queries that work with any JIRA instance.
16
+ Projects can override or extend these via config.
17
+ """
18
+
19
+ # ==================== PERSONAL QUERIES ====================
20
+
21
+ OPEN_ISSUES = 'project = {project} AND status IN ("Open", "Ready to Dev") ORDER BY updated DESC'
22
+ """All issues that are Open or Ready to Dev in the specified project (regardless of assignee), ordered by last updated"""
23
+
24
+ MY_OPEN_ISSUES = 'project = {project} AND assignee = currentUser() AND status IN ("Open", "Ready to Dev") ORDER BY updated DESC'
25
+ """Issues assigned to you that are Open or Ready to Dev in the specified project"""
26
+
27
+ MY_ISSUES = "assignee = currentUser() ORDER BY updated DESC"
28
+ """All issues assigned to you (including Done)"""
29
+
30
+ MY_BUGS = "assignee = currentUser() AND type = Bug AND status != Done"
31
+ """Open bugs assigned to you"""
32
+
33
+ MY_IN_REVIEW = "assignee = currentUser() AND status = 'In Review'"
34
+ """Issues you have in review status"""
35
+
36
+ MY_IN_PROGRESS = "assignee = currentUser() AND status = 'In Progress'"
37
+ """Issues you're currently working on"""
38
+
39
+ REPORTED_BY_ME = "reporter = currentUser() ORDER BY created DESC"
40
+ """Issues you created"""
41
+
42
+ # ==================== TEAM QUERIES ====================
43
+
44
+ CURRENT_SPRINT = "sprint in openSprints() AND project = {project}"
45
+ """Issues in current sprint (requires project parameter)"""
46
+
47
+ TEAM_OPEN = "project = {project} AND status != Done"
48
+ """All open issues in project"""
49
+
50
+ TEAM_BUGS = "project = {project} AND type = Bug AND status != Done"
51
+ """Open bugs in project"""
52
+
53
+ TEAM_IN_REVIEW = "project = {project} AND status = 'In Review'"
54
+ """Issues in review for project"""
55
+
56
+ TEAM_READY_FOR_QA = "project = {project} AND status = 'Ready for QA'"
57
+ """Issues ready for QA testing"""
58
+
59
+ # ==================== PRIORITY QUERIES ====================
60
+
61
+ CRITICAL_ISSUES = "priority = Highest AND status != Done ORDER BY created ASC"
62
+ """All critical priority issues"""
63
+
64
+ HIGH_PRIORITY = "priority IN (Highest, High) AND status != Done ORDER BY priority DESC"
65
+ """High and highest priority issues"""
66
+
67
+ CRITICAL_MY_PROJECT = "priority = Highest AND status != Done AND project = {project}"
68
+ """Critical issues in specific project"""
69
+
70
+ BLOCKED_ISSUES = "status = Blocked ORDER BY updated DESC"
71
+ """All blocked issues"""
72
+
73
+ # ==================== TIME-BASED QUERIES ====================
74
+
75
+ UPDATED_TODAY = "updated >= startOfDay() ORDER BY updated DESC"
76
+ """Issues updated today"""
77
+
78
+ UPDATED_THIS_WEEK = "updated >= startOfWeek() ORDER BY updated DESC"
79
+ """Issues updated this week"""
80
+
81
+ CREATED_TODAY = "created >= startOfDay() ORDER BY created DESC"
82
+ """Issues created today"""
83
+
84
+ CREATED_THIS_WEEK = "created >= startOfWeek() ORDER BY created DESC"
85
+ """Issues created this week"""
86
+
87
+ RECENT_BUGS = "type = Bug AND created >= -7d ORDER BY created DESC"
88
+ """Bugs created in last 7 days"""
89
+
90
+ # ==================== STATUS QUERIES ====================
91
+
92
+ TODO_ISSUES = "status = 'To Do' ORDER BY priority DESC, created ASC"
93
+ """All issues in To Do status"""
94
+
95
+ IN_PROGRESS_ALL = "status = 'In Progress' ORDER BY updated DESC"
96
+ """All issues currently in progress"""
97
+
98
+ IN_REVIEW_ALL = "status = 'In Review' ORDER BY updated DESC"
99
+ """All issues in review"""
100
+
101
+ DONE_RECENTLY = "status = Done AND updated >= -7d ORDER BY updated DESC"
102
+ """Issues completed in last 7 days"""
103
+
104
+ @classmethod
105
+ def get_all(cls) -> Dict[str, str]:
106
+ """
107
+ Get all predefined queries as a dictionary.
108
+
109
+ Returns:
110
+ Dict mapping query names to JQL strings
111
+ """
112
+ queries = {}
113
+ for attr in dir(cls):
114
+ if attr.isupper() and not attr.startswith('_'):
115
+ value = getattr(cls, attr)
116
+ if isinstance(value, str):
117
+ # Convert attribute name to lowercase with underscores
118
+ key = attr.lower()
119
+ queries[key] = value
120
+ return queries
121
+
122
+ @classmethod
123
+ def format(cls, query_name: str, **params) -> str:
124
+ """
125
+ Format a query with parameters.
126
+
127
+ Args:
128
+ query_name: Name of the query (lowercase with underscores)
129
+ **params: Parameters to format into query
130
+
131
+ Returns:
132
+ Formatted JQL query
133
+
134
+ Example:
135
+ >>> SavedQueries.format('current_sprint', project='ECAPP')
136
+ 'sprint in openSprints() AND project = ECAPP'
137
+ """
138
+ queries = cls.get_all()
139
+ if query_name not in queries:
140
+ raise ValueError(f"Query '{query_name}' not found")
141
+
142
+ query = queries[query_name]
143
+ return query.format(**params)
144
+
145
+
146
+ # Singleton instance
147
+ SAVED_QUERIES = SavedQueries()
148
+
149
+
150
+ __all__ = ["SavedQueries", "SAVED_QUERIES"]
@@ -0,0 +1,34 @@
1
+ name: "Analyze JIRA Open and Ready to Dev Issues"
2
+ description: "List all JIRA issues in Open or Ready to Dev status and analyze selected issue with AI"
3
+
4
+ params:
5
+ # Can override which saved query to use
6
+ query_name: "open_issues"
7
+
8
+ steps:
9
+ - id: search_open_issues
10
+ name: "Search Open Issues"
11
+ plugin: jira
12
+ step: search_saved_query
13
+ params:
14
+ query_name: "open_issues"
15
+ max_results: 20
16
+
17
+ - id: prompt_select_issue
18
+ name: "Select Issue to Analyze"
19
+ plugin: jira
20
+ step: prompt_select_issue
21
+
22
+ - id: get_issue_details
23
+ name: "Get Full Issue Details"
24
+ plugin: jira
25
+ step: get_issue
26
+ requires:
27
+ - jira_issue_key
28
+
29
+ - id: ai_analyze_issue
30
+ name: "AI Analyze Issue"
31
+ plugin: jira
32
+ step: ai_analyze_issue_requirements
33
+ requires:
34
+ - jira_issue