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.
Files changed (42) hide show
  1. titan_cli/core/config.py +3 -1
  2. titan_cli/core/plugins/models.py +35 -7
  3. titan_cli/core/plugins/plugin_registry.py +11 -2
  4. titan_cli/core/workflows/__init__.py +2 -1
  5. titan_cli/core/workflows/project_step_source.py +48 -30
  6. titan_cli/core/workflows/workflow_filter_service.py +14 -8
  7. titan_cli/core/workflows/workflow_registry.py +12 -1
  8. titan_cli/core/workflows/workflow_sources.py +1 -1
  9. titan_cli/engine/steps/ai_assistant_step.py +42 -7
  10. titan_cli/engine/workflow_executor.py +6 -1
  11. titan_cli/ui/tui/screens/plugin_config_wizard.py +40 -9
  12. titan_cli/ui/tui/screens/workflow_execution.py +8 -28
  13. titan_cli/ui/tui/textual_components.py +59 -6
  14. titan_cli/ui/tui/textual_workflow_executor.py +9 -1
  15. titan_cli/ui/tui/widgets/__init__.py +2 -0
  16. titan_cli/ui/tui/widgets/step_container.py +70 -0
  17. {titan_cli-0.1.3.dist-info → titan_cli-0.1.5.dist-info}/METADATA +6 -3
  18. {titan_cli-0.1.3.dist-info → titan_cli-0.1.5.dist-info}/RECORD +42 -40
  19. {titan_cli-0.1.3.dist-info → titan_cli-0.1.5.dist-info}/WHEEL +1 -1
  20. titan_plugin_git/clients/git_client.py +82 -4
  21. titan_plugin_git/plugin.py +3 -0
  22. titan_plugin_git/steps/ai_commit_message_step.py +33 -28
  23. titan_plugin_git/steps/branch_steps.py +18 -37
  24. titan_plugin_git/steps/commit_step.py +18 -22
  25. titan_plugin_git/steps/diff_summary_step.py +182 -0
  26. titan_plugin_git/steps/push_step.py +27 -11
  27. titan_plugin_git/steps/status_step.py +15 -18
  28. titan_plugin_git/workflows/commit-ai.yaml +5 -0
  29. titan_plugin_github/agents/pr_agent.py +15 -2
  30. titan_plugin_github/steps/ai_pr_step.py +12 -21
  31. titan_plugin_github/steps/create_pr_step.py +17 -7
  32. titan_plugin_github/steps/github_prompt_steps.py +52 -0
  33. titan_plugin_github/steps/issue_steps.py +28 -14
  34. titan_plugin_github/steps/preview_step.py +11 -0
  35. titan_plugin_github/utils.py +5 -4
  36. titan_plugin_github/workflows/create-pr-ai.yaml +5 -0
  37. titan_plugin_jira/steps/ai_analyze_issue_step.py +8 -3
  38. titan_plugin_jira/steps/get_issue_step.py +16 -12
  39. titan_plugin_jira/steps/prompt_select_issue_step.py +22 -9
  40. titan_plugin_jira/steps/search_saved_query_step.py +21 -19
  41. {titan_cli-0.1.3.dist-info → titan_cli-0.1.5.dist-info}/entry_points.txt +0 -0
  42. {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.mount(
60
- Panel(
61
- text=msg.Steps.PromptSelectIssue.ISSUE_SELECTION_CONFIRM.format(
62
- key=selected_issue.key,
63
- summary=selected_issue.summary
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 Panel, Table
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.mount(Panel(msg.Plugin.CLIENT_NOT_AVAILABLE_IN_CONTEXT, panel_type="error"))
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.mount(Panel(msg.Steps.Search.QUERY_NAME_REQUIRED, panel_type="error"))
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.mount(Panel(error_msg, panel_type="error"))
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.mount(Panel(error_msg, panel_type="error"))
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.mount(
146
- Panel(
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.mount(
162
- Panel(
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.mount(Panel(error_msg, panel_type="error"))
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.mount(Panel(error_msg, panel_type="error"))
235
+ ctx.textual.text(error_msg, markup="red")
236
+ ctx.textual.end_step("error")
235
237
  return Error(error_msg)
236
238
 
237
239