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.
- titan_cli/__init__.py +3 -0
- titan_cli/__main__.py +4 -0
- titan_cli/ai/__init__.py +0 -0
- titan_cli/ai/agents/__init__.py +15 -0
- titan_cli/ai/agents/base.py +152 -0
- titan_cli/ai/client.py +170 -0
- titan_cli/ai/constants.py +56 -0
- titan_cli/ai/exceptions.py +48 -0
- titan_cli/ai/models.py +34 -0
- titan_cli/ai/oauth_helper.py +120 -0
- titan_cli/ai/providers/__init__.py +9 -0
- titan_cli/ai/providers/anthropic.py +117 -0
- titan_cli/ai/providers/base.py +75 -0
- titan_cli/ai/providers/gemini.py +278 -0
- titan_cli/cli.py +59 -0
- titan_cli/clients/__init__.py +1 -0
- titan_cli/clients/gcloud_client.py +52 -0
- titan_cli/core/__init__.py +3 -0
- titan_cli/core/config.py +274 -0
- titan_cli/core/discovery.py +51 -0
- titan_cli/core/errors.py +81 -0
- titan_cli/core/models.py +52 -0
- titan_cli/core/plugins/available.py +36 -0
- titan_cli/core/plugins/models.py +67 -0
- titan_cli/core/plugins/plugin_base.py +108 -0
- titan_cli/core/plugins/plugin_registry.py +163 -0
- titan_cli/core/secrets.py +141 -0
- titan_cli/core/workflows/__init__.py +22 -0
- titan_cli/core/workflows/models.py +88 -0
- titan_cli/core/workflows/project_step_source.py +86 -0
- titan_cli/core/workflows/workflow_exceptions.py +17 -0
- titan_cli/core/workflows/workflow_filter_service.py +137 -0
- titan_cli/core/workflows/workflow_registry.py +419 -0
- titan_cli/core/workflows/workflow_sources.py +307 -0
- titan_cli/engine/__init__.py +39 -0
- titan_cli/engine/builder.py +159 -0
- titan_cli/engine/context.py +82 -0
- titan_cli/engine/mock_context.py +176 -0
- titan_cli/engine/results.py +91 -0
- titan_cli/engine/steps/ai_assistant_step.py +185 -0
- titan_cli/engine/steps/command_step.py +93 -0
- titan_cli/engine/utils/__init__.py +3 -0
- titan_cli/engine/utils/venv.py +31 -0
- titan_cli/engine/workflow_executor.py +187 -0
- titan_cli/external_cli/__init__.py +0 -0
- titan_cli/external_cli/configs.py +17 -0
- titan_cli/external_cli/launcher.py +65 -0
- titan_cli/messages.py +121 -0
- titan_cli/ui/tui/__init__.py +205 -0
- titan_cli/ui/tui/__previews__/statusbar_preview.py +88 -0
- titan_cli/ui/tui/app.py +113 -0
- titan_cli/ui/tui/icons.py +70 -0
- titan_cli/ui/tui/screens/__init__.py +24 -0
- titan_cli/ui/tui/screens/ai_config.py +498 -0
- titan_cli/ui/tui/screens/ai_config_wizard.py +882 -0
- titan_cli/ui/tui/screens/base.py +110 -0
- titan_cli/ui/tui/screens/cli_launcher.py +151 -0
- titan_cli/ui/tui/screens/global_setup_wizard.py +363 -0
- titan_cli/ui/tui/screens/main_menu.py +162 -0
- titan_cli/ui/tui/screens/plugin_config_wizard.py +550 -0
- titan_cli/ui/tui/screens/plugin_management.py +377 -0
- titan_cli/ui/tui/screens/project_setup_wizard.py +686 -0
- titan_cli/ui/tui/screens/workflow_execution.py +592 -0
- titan_cli/ui/tui/screens/workflows.py +249 -0
- titan_cli/ui/tui/textual_components.py +537 -0
- titan_cli/ui/tui/textual_workflow_executor.py +405 -0
- titan_cli/ui/tui/theme.py +102 -0
- titan_cli/ui/tui/widgets/__init__.py +40 -0
- titan_cli/ui/tui/widgets/button.py +108 -0
- titan_cli/ui/tui/widgets/header.py +116 -0
- titan_cli/ui/tui/widgets/panel.py +81 -0
- titan_cli/ui/tui/widgets/status_bar.py +115 -0
- titan_cli/ui/tui/widgets/table.py +77 -0
- titan_cli/ui/tui/widgets/text.py +177 -0
- titan_cli/utils/__init__.py +0 -0
- titan_cli/utils/autoupdate.py +155 -0
- titan_cli-0.1.0.dist-info/METADATA +149 -0
- titan_cli-0.1.0.dist-info/RECORD +146 -0
- titan_cli-0.1.0.dist-info/WHEEL +4 -0
- titan_cli-0.1.0.dist-info/entry_points.txt +9 -0
- titan_cli-0.1.0.dist-info/licenses/LICENSE +201 -0
- titan_plugin_git/__init__.py +1 -0
- titan_plugin_git/clients/__init__.py +8 -0
- titan_plugin_git/clients/git_client.py +772 -0
- titan_plugin_git/exceptions.py +40 -0
- titan_plugin_git/messages.py +112 -0
- titan_plugin_git/models.py +39 -0
- titan_plugin_git/plugin.py +118 -0
- titan_plugin_git/steps/__init__.py +1 -0
- titan_plugin_git/steps/ai_commit_message_step.py +171 -0
- titan_plugin_git/steps/branch_steps.py +104 -0
- titan_plugin_git/steps/commit_step.py +80 -0
- titan_plugin_git/steps/push_step.py +63 -0
- titan_plugin_git/steps/status_step.py +59 -0
- titan_plugin_git/workflows/__previews__/__init__.py +1 -0
- titan_plugin_git/workflows/__previews__/commit_ai_preview.py +124 -0
- titan_plugin_git/workflows/commit-ai.yaml +28 -0
- titan_plugin_github/__init__.py +11 -0
- titan_plugin_github/agents/__init__.py +6 -0
- titan_plugin_github/agents/config_loader.py +130 -0
- titan_plugin_github/agents/issue_generator.py +353 -0
- titan_plugin_github/agents/pr_agent.py +528 -0
- titan_plugin_github/clients/__init__.py +8 -0
- titan_plugin_github/clients/github_client.py +1105 -0
- titan_plugin_github/config/__init__.py +0 -0
- titan_plugin_github/config/pr_agent.toml +85 -0
- titan_plugin_github/exceptions.py +28 -0
- titan_plugin_github/messages.py +88 -0
- titan_plugin_github/models.py +330 -0
- titan_plugin_github/plugin.py +131 -0
- titan_plugin_github/steps/__init__.py +12 -0
- titan_plugin_github/steps/ai_pr_step.py +172 -0
- titan_plugin_github/steps/create_pr_step.py +86 -0
- titan_plugin_github/steps/github_prompt_steps.py +171 -0
- titan_plugin_github/steps/issue_steps.py +143 -0
- titan_plugin_github/steps/preview_step.py +40 -0
- titan_plugin_github/utils.py +82 -0
- titan_plugin_github/workflows/__previews__/__init__.py +1 -0
- titan_plugin_github/workflows/__previews__/create_pr_ai_preview.py +140 -0
- titan_plugin_github/workflows/create-issue-ai.yaml +32 -0
- titan_plugin_github/workflows/create-pr-ai.yaml +49 -0
- titan_plugin_jira/__init__.py +8 -0
- titan_plugin_jira/agents/__init__.py +6 -0
- titan_plugin_jira/agents/config_loader.py +154 -0
- titan_plugin_jira/agents/jira_agent.py +553 -0
- titan_plugin_jira/agents/prompts.py +364 -0
- titan_plugin_jira/agents/response_parser.py +435 -0
- titan_plugin_jira/agents/token_tracker.py +223 -0
- titan_plugin_jira/agents/validators.py +246 -0
- titan_plugin_jira/clients/jira_client.py +745 -0
- titan_plugin_jira/config/jira_agent.toml +92 -0
- titan_plugin_jira/config/templates/issue_analysis.md.j2 +78 -0
- titan_plugin_jira/exceptions.py +37 -0
- titan_plugin_jira/formatters/__init__.py +6 -0
- titan_plugin_jira/formatters/markdown_formatter.py +245 -0
- titan_plugin_jira/messages.py +115 -0
- titan_plugin_jira/models.py +89 -0
- titan_plugin_jira/plugin.py +264 -0
- titan_plugin_jira/steps/ai_analyze_issue_step.py +105 -0
- titan_plugin_jira/steps/get_issue_step.py +82 -0
- titan_plugin_jira/steps/prompt_select_issue_step.py +80 -0
- titan_plugin_jira/steps/search_saved_query_step.py +238 -0
- titan_plugin_jira/utils/__init__.py +13 -0
- titan_plugin_jira/utils/issue_sorter.py +140 -0
- titan_plugin_jira/utils/saved_queries.py +150 -0
- 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
|