titan-cli 0.1.3__py3-none-any.whl → 0.1.5__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/core/config.py +3 -1
- titan_cli/core/plugins/models.py +35 -7
- titan_cli/core/plugins/plugin_registry.py +11 -2
- titan_cli/core/workflows/__init__.py +2 -1
- titan_cli/core/workflows/project_step_source.py +48 -30
- titan_cli/core/workflows/workflow_filter_service.py +14 -8
- titan_cli/core/workflows/workflow_registry.py +12 -1
- titan_cli/core/workflows/workflow_sources.py +1 -1
- titan_cli/engine/steps/ai_assistant_step.py +42 -7
- titan_cli/engine/workflow_executor.py +6 -1
- titan_cli/ui/tui/screens/plugin_config_wizard.py +40 -9
- titan_cli/ui/tui/screens/workflow_execution.py +8 -28
- titan_cli/ui/tui/textual_components.py +59 -6
- titan_cli/ui/tui/textual_workflow_executor.py +9 -1
- titan_cli/ui/tui/widgets/__init__.py +2 -0
- titan_cli/ui/tui/widgets/step_container.py +70 -0
- {titan_cli-0.1.3.dist-info → titan_cli-0.1.5.dist-info}/METADATA +6 -3
- {titan_cli-0.1.3.dist-info → titan_cli-0.1.5.dist-info}/RECORD +42 -40
- {titan_cli-0.1.3.dist-info → titan_cli-0.1.5.dist-info}/WHEEL +1 -1
- titan_plugin_git/clients/git_client.py +82 -4
- titan_plugin_git/plugin.py +3 -0
- titan_plugin_git/steps/ai_commit_message_step.py +33 -28
- titan_plugin_git/steps/branch_steps.py +18 -37
- titan_plugin_git/steps/commit_step.py +18 -22
- titan_plugin_git/steps/diff_summary_step.py +182 -0
- titan_plugin_git/steps/push_step.py +27 -11
- titan_plugin_git/steps/status_step.py +15 -18
- titan_plugin_git/workflows/commit-ai.yaml +5 -0
- titan_plugin_github/agents/pr_agent.py +15 -2
- titan_plugin_github/steps/ai_pr_step.py +12 -21
- titan_plugin_github/steps/create_pr_step.py +17 -7
- titan_plugin_github/steps/github_prompt_steps.py +52 -0
- titan_plugin_github/steps/issue_steps.py +28 -14
- titan_plugin_github/steps/preview_step.py +11 -0
- titan_plugin_github/utils.py +5 -4
- titan_plugin_github/workflows/create-pr-ai.yaml +5 -0
- titan_plugin_jira/steps/ai_analyze_issue_step.py +8 -3
- titan_plugin_jira/steps/get_issue_step.py +16 -12
- titan_plugin_jira/steps/prompt_select_issue_step.py +22 -9
- titan_plugin_jira/steps/search_saved_query_step.py +21 -19
- {titan_cli-0.1.3.dist-info → titan_cli-0.1.5.dist-info}/entry_points.txt +0 -0
- {titan_cli-0.1.3.dist-info → titan_cli-0.1.5.dist-info/licenses}/LICENSE +0 -0
|
@@ -3,7 +3,6 @@ Prompt user to select an issue from search results
|
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
5
|
from titan_cli.engine import WorkflowContext, WorkflowResult, Success, Error
|
|
6
|
-
from titan_cli.ui.tui.widgets import Panel
|
|
7
6
|
from ..messages import msg
|
|
8
7
|
|
|
9
8
|
|
|
@@ -21,12 +20,19 @@ def prompt_select_issue_step(ctx: WorkflowContext) -> WorkflowResult:
|
|
|
21
20
|
if not ctx.textual:
|
|
22
21
|
return Error("Textual UI context is not available for this step.")
|
|
23
22
|
|
|
23
|
+
# Begin step container
|
|
24
|
+
ctx.textual.begin_step("Select Issue to Analyze")
|
|
25
|
+
|
|
24
26
|
# Get issues from previous search
|
|
25
27
|
issues = ctx.get("jira_issues")
|
|
26
28
|
if not issues:
|
|
29
|
+
ctx.textual.text(msg.Steps.PromptSelectIssue.NO_ISSUES_AVAILABLE, markup="red")
|
|
30
|
+
ctx.textual.end_step("error")
|
|
27
31
|
return Error(msg.Steps.PromptSelectIssue.NO_ISSUES_AVAILABLE)
|
|
28
32
|
|
|
29
33
|
if len(issues) == 0:
|
|
34
|
+
ctx.textual.text(msg.Steps.PromptSelectIssue.NO_ISSUES_AVAILABLE, markup="red")
|
|
35
|
+
ctx.textual.end_step("error")
|
|
30
36
|
return Error(msg.Steps.PromptSelectIssue.NO_ISSUES_AVAILABLE)
|
|
31
37
|
|
|
32
38
|
# Prompt user to select issue (issues already displayed in table from previous step)
|
|
@@ -40,32 +46,37 @@ def prompt_select_issue_step(ctx: WorkflowContext) -> WorkflowResult:
|
|
|
40
46
|
)
|
|
41
47
|
|
|
42
48
|
if not response or not response.strip():
|
|
49
|
+
ctx.textual.text(msg.Steps.PromptSelectIssue.NO_ISSUE_SELECTED, markup="red")
|
|
50
|
+
ctx.textual.end_step("error")
|
|
43
51
|
return Error(msg.Steps.PromptSelectIssue.NO_ISSUE_SELECTED)
|
|
44
52
|
|
|
45
53
|
# Validate it's a number
|
|
46
54
|
try:
|
|
47
55
|
selected_index = int(response.strip())
|
|
48
56
|
except ValueError:
|
|
57
|
+
ctx.textual.text(f"Invalid input: '{response}' is not a number", markup="red")
|
|
58
|
+
ctx.textual.end_step("error")
|
|
49
59
|
return Error(f"Invalid input: '{response}' is not a number")
|
|
50
60
|
|
|
51
61
|
# Validate it's in range
|
|
52
62
|
if selected_index < 1 or selected_index > len(issues):
|
|
63
|
+
ctx.textual.text(f"Invalid selection: must be between 1 and {len(issues)}", markup="red")
|
|
64
|
+
ctx.textual.end_step("error")
|
|
53
65
|
return Error(f"Invalid selection: must be between 1 and {len(issues)}")
|
|
54
66
|
|
|
55
67
|
# Convert to 0-based index
|
|
56
68
|
selected_issue = issues[selected_index - 1]
|
|
57
69
|
|
|
58
70
|
ctx.textual.text("")
|
|
59
|
-
ctx.textual.
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
panel_type="success"
|
|
66
|
-
)
|
|
71
|
+
ctx.textual.text(
|
|
72
|
+
msg.Steps.PromptSelectIssue.ISSUE_SELECTION_CONFIRM.format(
|
|
73
|
+
key=selected_issue.key,
|
|
74
|
+
summary=selected_issue.summary
|
|
75
|
+
),
|
|
76
|
+
markup="green"
|
|
67
77
|
)
|
|
68
78
|
|
|
79
|
+
ctx.textual.end_step("success")
|
|
69
80
|
return Success(
|
|
70
81
|
msg.Steps.PromptSelectIssue.SELECT_SUCCESS.format(key=selected_issue.key),
|
|
71
82
|
metadata={
|
|
@@ -74,6 +85,8 @@ def prompt_select_issue_step(ctx: WorkflowContext) -> WorkflowResult:
|
|
|
74
85
|
}
|
|
75
86
|
)
|
|
76
87
|
except (KeyboardInterrupt, EOFError):
|
|
88
|
+
ctx.textual.text("User cancelled issue selection", markup="red")
|
|
89
|
+
ctx.textual.end_step("error")
|
|
77
90
|
return Error("User cancelled issue selection")
|
|
78
91
|
|
|
79
92
|
|
|
@@ -3,7 +3,7 @@ Search JIRA issues using saved query from utils registry
|
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
5
|
from titan_cli.engine import WorkflowContext, WorkflowResult, Success, Error
|
|
6
|
-
from titan_cli.ui.tui.widgets import
|
|
6
|
+
from titan_cli.ui.tui.widgets import Table
|
|
7
7
|
from ..exceptions import JiraAPIError
|
|
8
8
|
from ..messages import msg
|
|
9
9
|
from ..utils import SAVED_QUERIES, IssueSorter
|
|
@@ -57,14 +57,19 @@ def search_saved_query_step(ctx: WorkflowContext) -> WorkflowResult:
|
|
|
57
57
|
if not ctx.textual:
|
|
58
58
|
return Error("Textual UI context is not available for this step.")
|
|
59
59
|
|
|
60
|
+
# Begin step container
|
|
61
|
+
ctx.textual.begin_step("Search Open Issues")
|
|
62
|
+
|
|
60
63
|
if not ctx.jira:
|
|
61
|
-
ctx.textual.
|
|
64
|
+
ctx.textual.text(msg.Plugin.CLIENT_NOT_AVAILABLE_IN_CONTEXT, markup="red")
|
|
65
|
+
ctx.textual.end_step("error")
|
|
62
66
|
return Error(msg.Plugin.CLIENT_NOT_AVAILABLE_IN_CONTEXT)
|
|
63
67
|
|
|
64
68
|
# Get query name
|
|
65
69
|
query_name = ctx.get("query_name")
|
|
66
70
|
if not query_name:
|
|
67
|
-
ctx.textual.
|
|
71
|
+
ctx.textual.text(msg.Steps.Search.QUERY_NAME_REQUIRED, markup="red")
|
|
72
|
+
ctx.textual.end_step("error")
|
|
68
73
|
return Error(msg.Steps.Search.QUERY_NAME_REQUIRED)
|
|
69
74
|
|
|
70
75
|
# Get all predefined queries from utils
|
|
@@ -102,7 +107,8 @@ def search_saved_query_step(ctx: WorkflowContext) -> WorkflowResult:
|
|
|
102
107
|
error_msg += "\n\n" + msg.Steps.Search.ADD_CUSTOM_HINT + "\n"
|
|
103
108
|
error_msg += msg.Steps.Search.CUSTOM_QUERY_EXAMPLE
|
|
104
109
|
|
|
105
|
-
ctx.textual.
|
|
110
|
+
ctx.textual.text(error_msg, markup="red")
|
|
111
|
+
ctx.textual.end_step("error")
|
|
106
112
|
return Error(error_msg)
|
|
107
113
|
|
|
108
114
|
jql = all_queries[query_name]
|
|
@@ -119,7 +125,8 @@ def search_saved_query_step(ctx: WorkflowContext) -> WorkflowResult:
|
|
|
119
125
|
|
|
120
126
|
if not project:
|
|
121
127
|
error_msg = msg.Steps.Search.PROJECT_REQUIRED.format(query_name=query_name, jql=jql)
|
|
122
|
-
ctx.textual.
|
|
128
|
+
ctx.textual.text(error_msg, markup="red")
|
|
129
|
+
ctx.textual.end_step("error")
|
|
123
130
|
return Error(error_msg)
|
|
124
131
|
|
|
125
132
|
jql = jql.format(project=project)
|
|
@@ -142,12 +149,8 @@ def search_saved_query_step(ctx: WorkflowContext) -> WorkflowResult:
|
|
|
142
149
|
issues = ctx.jira.search_tickets(jql=jql, max_results=max_results)
|
|
143
150
|
|
|
144
151
|
if not issues:
|
|
145
|
-
ctx.textual.
|
|
146
|
-
|
|
147
|
-
text=f"No issues found for query: {query_name}",
|
|
148
|
-
panel_type="info"
|
|
149
|
-
)
|
|
150
|
-
)
|
|
152
|
+
ctx.textual.text(f"No issues found for query: {query_name}", markup="dim")
|
|
153
|
+
ctx.textual.end_step("success")
|
|
151
154
|
return Success(
|
|
152
155
|
"No issues found",
|
|
153
156
|
metadata={
|
|
@@ -158,12 +161,8 @@ def search_saved_query_step(ctx: WorkflowContext) -> WorkflowResult:
|
|
|
158
161
|
)
|
|
159
162
|
|
|
160
163
|
# Show results
|
|
161
|
-
ctx.textual.
|
|
162
|
-
|
|
163
|
-
text=f"Found {len(issues)} issues",
|
|
164
|
-
panel_type="success"
|
|
165
|
-
)
|
|
166
|
-
)
|
|
164
|
+
ctx.textual.text("") # spacing
|
|
165
|
+
ctx.textual.text(f"Found {len(issues)} issues", markup="green")
|
|
167
166
|
ctx.textual.text("")
|
|
168
167
|
|
|
169
168
|
# Show detailed table
|
|
@@ -214,6 +213,7 @@ def search_saved_query_step(ctx: WorkflowContext) -> WorkflowResult:
|
|
|
214
213
|
ctx.textual.text(f"{i}. {issue.key} - {getattr(issue, 'summary', 'N/A')}")
|
|
215
214
|
ctx.textual.text("")
|
|
216
215
|
|
|
216
|
+
ctx.textual.end_step("success")
|
|
217
217
|
return Success(
|
|
218
218
|
f"Found {len(issues)} issues using query: {query_name}",
|
|
219
219
|
metadata={
|
|
@@ -225,13 +225,15 @@ def search_saved_query_step(ctx: WorkflowContext) -> WorkflowResult:
|
|
|
225
225
|
|
|
226
226
|
except JiraAPIError as e:
|
|
227
227
|
error_msg = f"JIRA search failed: {e}"
|
|
228
|
-
ctx.textual.
|
|
228
|
+
ctx.textual.text(error_msg, markup="red")
|
|
229
|
+
ctx.textual.end_step("error")
|
|
229
230
|
return Error(error_msg)
|
|
230
231
|
except Exception as e:
|
|
231
232
|
import traceback
|
|
232
233
|
error_detail = traceback.format_exc()
|
|
233
234
|
error_msg = f"Unexpected error: {e}\n\nTraceback:\n{error_detail}"
|
|
234
|
-
ctx.textual.
|
|
235
|
+
ctx.textual.text(error_msg, markup="red")
|
|
236
|
+
ctx.textual.end_step("error")
|
|
235
237
|
return Error(error_msg)
|
|
236
238
|
|
|
237
239
|
|
|
File without changes
|
|
File without changes
|