titan-cli 0.1.4__py3-none-any.whl → 0.1.6__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 (61) hide show
  1. titan_cli/core/config.py +3 -1
  2. titan_cli/core/workflows/__init__.py +2 -1
  3. titan_cli/core/workflows/project_step_source.py +95 -32
  4. titan_cli/core/workflows/workflow_filter_service.py +16 -8
  5. titan_cli/core/workflows/workflow_registry.py +12 -1
  6. titan_cli/core/workflows/workflow_sources.py +1 -1
  7. titan_cli/engine/__init__.py +5 -1
  8. titan_cli/engine/results.py +31 -1
  9. titan_cli/engine/steps/ai_assistant_step.py +47 -12
  10. titan_cli/engine/workflow_executor.py +13 -3
  11. titan_cli/ui/tui/screens/plugin_config_wizard.py +16 -0
  12. titan_cli/ui/tui/screens/workflow_execution.py +28 -50
  13. titan_cli/ui/tui/screens/workflows.py +8 -4
  14. titan_cli/ui/tui/textual_components.py +342 -185
  15. titan_cli/ui/tui/textual_workflow_executor.py +39 -3
  16. titan_cli/ui/tui/theme.py +34 -5
  17. titan_cli/ui/tui/widgets/__init__.py +17 -0
  18. titan_cli/ui/tui/widgets/multiline_input.py +32 -0
  19. titan_cli/ui/tui/widgets/prompt_choice.py +138 -0
  20. titan_cli/ui/tui/widgets/prompt_input.py +74 -0
  21. titan_cli/ui/tui/widgets/prompt_selection_list.py +150 -0
  22. titan_cli/ui/tui/widgets/prompt_textarea.py +87 -0
  23. titan_cli/ui/tui/widgets/step_container.py +70 -0
  24. titan_cli/ui/tui/widgets/styled_option_list.py +107 -0
  25. titan_cli/ui/tui/widgets/text.py +51 -130
  26. {titan_cli-0.1.4.dist-info → titan_cli-0.1.6.dist-info}/METADATA +3 -5
  27. {titan_cli-0.1.4.dist-info → titan_cli-0.1.6.dist-info}/RECORD +61 -46
  28. titan_plugin_git/clients/git_client.py +140 -5
  29. titan_plugin_git/plugin.py +13 -0
  30. titan_plugin_git/steps/ai_commit_message_step.py +39 -34
  31. titan_plugin_git/steps/branch_steps.py +18 -37
  32. titan_plugin_git/steps/checkout_step.py +66 -0
  33. titan_plugin_git/steps/commit_step.py +18 -22
  34. titan_plugin_git/steps/create_branch_step.py +131 -0
  35. titan_plugin_git/steps/diff_summary_step.py +180 -0
  36. titan_plugin_git/steps/pull_step.py +70 -0
  37. titan_plugin_git/steps/push_step.py +27 -11
  38. titan_plugin_git/steps/restore_original_branch_step.py +97 -0
  39. titan_plugin_git/steps/save_current_branch_step.py +82 -0
  40. titan_plugin_git/steps/status_step.py +32 -25
  41. titan_plugin_git/workflows/commit-ai.yaml +9 -3
  42. titan_plugin_github/agents/pr_agent.py +15 -2
  43. titan_plugin_github/steps/ai_pr_step.py +99 -40
  44. titan_plugin_github/steps/create_pr_step.py +18 -8
  45. titan_plugin_github/steps/github_prompt_steps.py +53 -1
  46. titan_plugin_github/steps/issue_steps.py +31 -18
  47. titan_plugin_github/steps/preview_step.py +15 -4
  48. titan_plugin_github/utils.py +5 -4
  49. titan_plugin_github/workflows/create-pr-ai.yaml +6 -11
  50. titan_plugin_jira/messages.py +12 -0
  51. titan_plugin_jira/plugin.py +4 -0
  52. titan_plugin_jira/steps/ai_analyze_issue_step.py +12 -7
  53. titan_plugin_jira/steps/get_issue_step.py +17 -13
  54. titan_plugin_jira/steps/list_versions_step.py +133 -0
  55. titan_plugin_jira/steps/prompt_select_issue_step.py +20 -8
  56. titan_plugin_jira/steps/search_jql_step.py +191 -0
  57. titan_plugin_jira/steps/search_saved_query_step.py +26 -24
  58. titan_plugin_jira/utils/__init__.py +1 -1
  59. {titan_cli-0.1.4.dist-info → titan_cli-0.1.6.dist-info}/LICENSE +0 -0
  60. {titan_cli-0.1.4.dist-info → titan_cli-0.1.6.dist-info}/WHEEL +0 -0
  61. {titan_cli-0.1.4.dist-info → titan_cli-0.1.6.dist-info}/entry_points.txt +0 -0
@@ -146,16 +146,16 @@ class WorkflowExecutionScreen(BaseScreen):
146
146
  thread=True
147
147
  )
148
148
 
149
- except (WorkflowNotFoundError, WorkflowExecutionError):
150
- pass
151
- # TODO Create empty error screen
152
- # self._update_workflow_info(f"[red]Error: {e}[/red]")
153
- except Exception:
154
- pass
155
- # TODO Create empty error screen
156
- # self._update_workflow_info(
157
- # f"[red]Unexpected error: {type(e).__name__} - {e}[/red]"
158
- # )
149
+ except (WorkflowNotFoundError, WorkflowExecutionError) as e:
150
+ self._output(f"[red]{Icons.ERROR} Error loading workflow: {e}[/red]")
151
+ self._output("[dim]Press ESC or Q to return[/dim]")
152
+ except Exception as e:
153
+ import traceback
154
+ error_details = traceback.format_exc()
155
+ self._output(f"[red]{Icons.ERROR} Unexpected error loading workflow:[/red]")
156
+ self._output(f"[red]{type(e).__name__}: {e}[/red]")
157
+ self._output(f"[dim]{error_details}[/dim]")
158
+ self._output("[dim]Press ESC or Q to return[/dim]")
159
159
 
160
160
  def _execute_workflow(self) -> None:
161
161
  """Execute the workflow in a background thread."""
@@ -493,18 +493,19 @@ class WorkflowExecutionContent(Widget):
493
493
 
494
494
  def on_descendant_mount(self, event) -> None:
495
495
  """Auto-scroll when any widget is mounted as a descendant."""
496
- # Don't auto-scroll if we're mounting a PromptInput (it will handle its own scroll)
497
- from titan_cli.ui.tui.textual_components import PromptInput
496
+ from titan_cli.ui.tui.widgets import PromptInput, StepContainer
498
497
 
499
- # Skip scroll only if the widget itself is a PromptInput
500
- # (not if it's a child of PromptInput, to avoid blocking scroll after PromptInput is removed)
501
- if not isinstance(event.widget, PromptInput):
498
+ # Auto-scroll for StepContainers (new steps being added)
499
+ # StepContainer itself triggers scroll via begin_step(), but this is a safety net
500
+ if isinstance(event.widget, StepContainer):
501
+ self._scroll_to_end()
502
+ # Also scroll for other widgets, but skip PromptInput (it handles its own scroll)
503
+ elif not isinstance(event.widget, PromptInput):
502
504
  self._scroll_to_end()
503
505
 
504
506
  def handle_event(self, message) -> None:
505
507
  """Handle workflow events generically."""
506
508
  from titan_cli.ui.tui.textual_workflow_executor import TextualWorkflowExecutor
507
- from titan_cli.ui.tui.widgets import Panel
508
509
 
509
510
  if isinstance(message, TextualWorkflowExecutor.WorkflowStarted):
510
511
  # Track nested workflow depth
@@ -513,44 +514,21 @@ class WorkflowExecutionContent(Widget):
513
514
  self.append_output(f"\n[bold cyan]🚀 Starting workflow: {message.workflow_name}[/bold cyan]")
514
515
 
515
516
  elif isinstance(message, TextualWorkflowExecutor.StepStarted):
516
- # Format differently for nested workflows
517
- if self._workflow_depth > 0:
518
- # Nested workflow: show with indentation, no step number
519
- indent = " " * self._workflow_depth
520
- self.append_output(f"[cyan]{indent}→ Step {message.step_index}: {message.step_name}[/cyan]")
521
- else:
522
- # Top-level workflow: show with step number
523
- self.append_output(f"[cyan]→ Step {message.step_index}: {message.step_name}[/cyan]")
517
+ # StepContainer now handles step titles, so we don't display anything here
518
+ pass
524
519
 
525
520
  elif isinstance(message, TextualWorkflowExecutor.StepCompleted):
526
- # Apply indentation for nested workflows
527
- if self._workflow_depth > 0:
528
- indent = " " * self._workflow_depth
529
- self.append_output(f"[green]{indent}{Icons.SUCCESS} Completed: {message.step_name}[/green]\n")
530
- else:
531
- self.append_output(f"[green]{Icons.SUCCESS} Completed: {message.step_name}[/green]\n")
521
+ # StepContainer now handles step completion (green border), so we don't display anything here
522
+ pass
532
523
 
533
524
  elif isinstance(message, TextualWorkflowExecutor.StepFailed):
534
- # Mount error panel
535
- try:
536
- self.mount(Panel(f"Failed: {message.step_name} - {message.error_message}", panel_type="error"))
537
- self._scroll_to_end()
538
- except Exception:
539
- pass
540
-
541
- if message.on_error == "continue":
542
- indent = " " * self._workflow_depth if self._workflow_depth > 0 else ""
543
- self.append_output(f"[yellow]{indent} {Icons.WARNING} Continuing despite error[/yellow]\n")
544
- else:
545
- self.append_output("")
525
+ # Steps handle their own error display via ctx.textual
526
+ # Executor only coordinates execution, no visual output needed
527
+ pass
546
528
 
547
529
  elif isinstance(message, TextualWorkflowExecutor.StepSkipped):
548
- # Mount warning panel for skipped steps
549
- try:
550
- self.mount(Panel(f"Skipped: {message.step_name}", panel_type="warning"))
551
- self._scroll_to_end()
552
- except Exception:
553
- pass
530
+ # StepContainer now handles step skips (yellow border), so we don't display the panel
531
+ pass
554
532
 
555
533
  elif isinstance(message, TextualWorkflowExecutor.WorkflowCompleted):
556
534
  # Track nested workflow depth
@@ -586,7 +564,7 @@ class WorkflowExecutionContent(Widget):
586
564
  pass
587
565
 
588
566
  elif isinstance(message, TextualWorkflowExecutor.WorkflowFailed):
589
- # Show error toast for workflow failure
567
+ # Steps handle their own error display via ctx.textual
568
+ # Just show a simple notification without duplicating the error message
590
569
  self.app.notify(f"❌ Workflow failed at step: {message.step_name}", severity="error", timeout=10)
591
- self.append_output(f"[red]{message.error_message}[/red]")
592
570
 
@@ -16,6 +16,7 @@ from titan_cli.core.workflows.workflow_filter_service import WorkflowFilterServi
16
16
  from titan_cli.core.workflows.workflow_sources import WorkflowInfo
17
17
  from titan_cli.ui.tui.screens.workflow_execution import WorkflowExecutionScreen
18
18
  from titan_cli.ui.tui.icons import Icons
19
+ from titan_cli.ui.tui.widgets import StyledOption
19
20
  from .base import BaseScreen
20
21
 
21
22
  class WorkflowsScreen(BaseScreen):
@@ -170,11 +171,14 @@ class WorkflowsScreen(BaseScreen):
170
171
  workflows_to_show = workflows
171
172
 
172
173
  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)
174
+ styled_opt = StyledOption(
175
+ id=wf_info.name,
176
+ title=wf_info.name.capitalize(),
177
+ description=wf_info.description
177
178
  )
179
+ # Convert StyledOption to Option with markup
180
+ prompt = f"[bold]{styled_opt.title}[/bold]\n[dim]{styled_opt.description}[/dim]"
181
+ options.append(Option(prompt, id=styled_opt.id))
178
182
 
179
183
  return options if options else [Option("No workflows found", id="none", disabled=True)]
180
184