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,249 @@
1
+ """
2
+ Workflows Screen
3
+
4
+ Screen for listing and executing workflows.
5
+ """
6
+ from typing import List
7
+
8
+ from textual.app import ComposeResult
9
+ from textual.widgets import Static, OptionList
10
+ from textual.widgets.option_list import Option
11
+ from textual.containers import Container
12
+ from textual.containers import Horizontal
13
+ from textual.css.query import NoMatches
14
+
15
+ from titan_cli.core.workflows.workflow_filter_service import WorkflowFilterService
16
+ from titan_cli.core.workflows.workflow_sources import WorkflowInfo
17
+ from titan_cli.ui.tui.screens.workflow_execution import WorkflowExecutionScreen
18
+ from titan_cli.ui.tui.icons import Icons
19
+ from .base import BaseScreen
20
+
21
+ class WorkflowsScreen(BaseScreen):
22
+ """
23
+ Workflows screen for selecting and executing workflows.
24
+
25
+ Lists all available workflows and allows the user to:
26
+ - View workflow details
27
+ - Execute workflows
28
+ - Go back to main menu
29
+ """
30
+
31
+ BINDINGS = [
32
+ ("escape", "go_back", "Back"),
33
+ ("q", "go_back", "Back"),
34
+ ("left", "focus_plugins", "Plugins"),
35
+ ("right", "focus_workflows", "Workflows"),
36
+ ("tab", "focus_next", "Next Panel"),
37
+ ]
38
+
39
+ def __init__(self, config):
40
+ super().__init__(config, title="⚡ Available Workflows", show_back=True)
41
+ self.selected_plugin = "all" # Track selected plugin filter (start with "all")
42
+ self._is_mounting = False # Flag to prevent auto-update during mount
43
+ self._all_workflows = None # Cache for discovered workflows
44
+ self._plugin_source_map = {} # Map plugin names to their source identifiers
45
+
46
+ def on_mount(self) -> None:
47
+ """Initialize the screen with first options highlighted."""
48
+ self._is_mounting = True
49
+
50
+ try:
51
+ # Highlight first plugin option
52
+ plugin_list = self.query_one("#plugin-list", OptionList)
53
+ if len(plugin_list._options) > 0:
54
+ plugin_list.highlighted = 0
55
+
56
+ # Highlight first workflow option
57
+ workflow_list = self.query_one("#workflow-list", OptionList)
58
+ if len(workflow_list._options) > 0:
59
+ workflow_list.highlighted = 0
60
+ except (NoMatches, AttributeError):
61
+ pass
62
+ finally:
63
+ # Re-enable auto-filtering after mount completes
64
+ self._is_mounting = False
65
+
66
+ CSS = """
67
+ WorkflowsScreen {
68
+ align: center middle;
69
+ }
70
+
71
+ #workflows-container {
72
+ width: 100%;
73
+ height: 1fr;
74
+ background: $surface-lighten-1;
75
+ }
76
+
77
+ #workflows-container Horizontal {
78
+ width: 100%;
79
+ height: 1fr;
80
+ padding: 1;
81
+ }
82
+
83
+ #left-panel {
84
+ width: 20%;
85
+ height: 100%;
86
+ border: round $primary;
87
+ border-title-align: center;
88
+ background: $surface-lighten-1;
89
+ padding: 0;
90
+ }
91
+
92
+ #left-panel OptionList {
93
+ height: 100%;
94
+ width: 100%;
95
+ padding: 1 0;
96
+ margin: 0;
97
+ }
98
+
99
+ #right-panel {
100
+ width: 80%;
101
+ height: 100%;
102
+ border: round $primary;
103
+ border-title-align: center;
104
+ background: $surface-lighten-1;
105
+ padding: 0;
106
+ }
107
+
108
+ #right-panel OptionList {
109
+ height: 100%;
110
+ width: 100%;
111
+ padding: 1 0;
112
+ margin: 0;
113
+ }
114
+
115
+ OptionList > .option-list--option {
116
+ padding: 1;
117
+ }
118
+ """
119
+
120
+ def compose_content(self) -> ComposeResult:
121
+ """
122
+ Compose the workflows screen content.
123
+
124
+ Creates a two-panel layout:
125
+ - Left panel (20%): Plugin filter list
126
+ - Right panel (80%): Workflows list (filtered by selected plugin)
127
+ """
128
+ all_workflows = self.config.workflows.discover()
129
+
130
+ # Cache and remove duplicates using service
131
+ self._all_workflows = WorkflowFilterService.remove_duplicates(all_workflows)
132
+
133
+ with Container(id="workflows-container"):
134
+ if not self._all_workflows:
135
+ yield Static("No workflows found.", id="no-workflows")
136
+ else:
137
+ # Group workflows by plugin using service
138
+ self._plugin_source_map = WorkflowFilterService.group_by_plugin(self._all_workflows)
139
+ plugin_names = WorkflowFilterService.get_unique_plugin_names(self._all_workflows)
140
+
141
+ # Build plugin filter options
142
+ plugin_options = [Option(f"{Icons.PACKAGE} All Plugins", id="all")]
143
+ for plugin_name in sorted(plugin_names):
144
+ plugin_options.append(Option(f"{Icons.PLUGIN} {plugin_name}", id=plugin_name))
145
+
146
+ # Build workflow options (initially show all)
147
+ workflow_options = self._build_workflow_options(self._all_workflows)
148
+
149
+ with Horizontal():
150
+ left_panel = Container(id="left-panel")
151
+ left_panel.border_title = "Plugins"
152
+ with left_panel:
153
+ yield OptionList(*plugin_options, id="plugin-list")
154
+
155
+ right_panel = Container(id="right-panel")
156
+ right_panel.border_title = "Workflows"
157
+ with right_panel:
158
+ yield OptionList(*workflow_options, id="workflow-list")
159
+
160
+ def _build_workflow_options(self, workflows: List[WorkflowInfo], selected_plugin: str = None) -> List[Option]:
161
+ """Build workflow options, optionally filtered by plugin."""
162
+ options = []
163
+
164
+ # Get workflows for selected plugin
165
+ if selected_plugin and selected_plugin != "all":
166
+ # Use the pre-mapped workflows for this plugin
167
+ workflows_to_show = self._plugin_source_map.get(selected_plugin, [])
168
+ else:
169
+ # Show all workflows
170
+ workflows_to_show = workflows
171
+
172
+ for wf_info in workflows_to_show:
173
+ label = f"{wf_info.name.capitalize()}"
174
+ description = f"{wf_info.description}"
175
+ options.append(
176
+ Option(f"{label}\n[dim]{description}[/dim]", id=wf_info.name)
177
+ )
178
+
179
+ return options if options else [Option("No workflows found", id="none", disabled=True)]
180
+
181
+ def on_option_list_option_highlighted(self, event: OptionList.OptionHighlighted) -> None:
182
+ """Handle plugin navigation - auto-filter when highlighting."""
183
+ # Ignore during mount to prevent duplicate updates
184
+ if self._is_mounting:
185
+ return
186
+
187
+ if event.option_list.id == "plugin-list":
188
+ # Only update if plugin actually changed to avoid duplicate updates
189
+ if self.selected_plugin != event.option.id:
190
+ self.selected_plugin = event.option.id
191
+ self._update_workflow_list()
192
+
193
+ def on_option_list_option_selected(self, event: OptionList.OptionSelected) -> None:
194
+ """Handle workflow selection (Enter key)."""
195
+ if event.option_list.id == "workflow-list":
196
+ # Workflow selected - execute it
197
+ workflow_name = event.option.id
198
+ if workflow_name != "none": # Don't execute disabled placeholder
199
+ self.execute_workflow(workflow_name)
200
+
201
+ def _update_workflow_list(self) -> None:
202
+ """Update the workflow list based on selected plugin filter."""
203
+ # Use cached workflows instead of discovering again
204
+ workflow_options = self._build_workflow_options(self._all_workflows, self.selected_plugin)
205
+
206
+ # Get the workflow list widget and replace its options
207
+ workflow_list = self.query_one("#workflow-list", OptionList)
208
+
209
+ # Clear and add in one operation
210
+ workflow_list.clear_options()
211
+ workflow_list.add_options(workflow_options)
212
+
213
+ # Force refresh to update display
214
+ workflow_list.refresh()
215
+
216
+ def execute_workflow(self, workflow_name: str) -> None:
217
+ """
218
+ Execute a workflow.
219
+
220
+ Args:
221
+ workflow_name: Name of the workflow to execute
222
+ """
223
+
224
+ # Navigate to workflow execution screen
225
+ execution_screen = WorkflowExecutionScreen(self.config, workflow_name)
226
+ self.app.push_screen(execution_screen)
227
+
228
+ def action_go_back(self) -> None:
229
+ """Go back to main menu."""
230
+ self.app.pop_screen()
231
+
232
+ def action_focus_plugins(self) -> None:
233
+ """Focus on the plugins panel."""
234
+ try:
235
+ plugin_list = self.query_one("#plugin-list", OptionList)
236
+ plugin_list.focus()
237
+ except NoMatches:
238
+ pass
239
+
240
+ def action_focus_workflows(self) -> None:
241
+ """Focus on the workflows panel."""
242
+ try:
243
+ workflow_list = self.query_one("#workflow-list", OptionList)
244
+ workflow_list.focus()
245
+ # Ensure first item is highlighted if nothing is selected
246
+ if workflow_list.highlighted is None and len(workflow_list._options) > 0:
247
+ workflow_list.highlighted = 0
248
+ except NoMatches:
249
+ pass