titan-cli 0.1.5__tar.gz → 0.1.7__tar.gz

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 (161) hide show
  1. {titan_cli-0.1.5 → titan_cli-0.1.7}/PKG-INFO +5 -10
  2. {titan_cli-0.1.5 → titan_cli-0.1.7}/plugins/titan-plugin-git/titan_plugin_git/clients/git_client.py +59 -2
  3. {titan_cli-0.1.5 → titan_cli-0.1.7}/plugins/titan-plugin-git/titan_plugin_git/plugin.py +10 -0
  4. {titan_cli-0.1.5 → titan_cli-0.1.7}/plugins/titan-plugin-git/titan_plugin_git/steps/ai_commit_message_step.py +8 -8
  5. {titan_cli-0.1.5 → titan_cli-0.1.7}/plugins/titan-plugin-git/titan_plugin_git/steps/branch_steps.py +6 -6
  6. titan_cli-0.1.7/plugins/titan-plugin-git/titan_plugin_git/steps/checkout_step.py +66 -0
  7. {titan_cli-0.1.5 → titan_cli-0.1.7}/plugins/titan-plugin-git/titan_plugin_git/steps/commit_step.py +3 -3
  8. titan_cli-0.1.7/plugins/titan-plugin-git/titan_plugin_git/steps/create_branch_step.py +131 -0
  9. {titan_cli-0.1.5 → titan_cli-0.1.7}/plugins/titan-plugin-git/titan_plugin_git/steps/diff_summary_step.py +11 -13
  10. titan_cli-0.1.7/plugins/titan-plugin-git/titan_plugin_git/steps/pull_step.py +70 -0
  11. {titan_cli-0.1.5 → titan_cli-0.1.7}/plugins/titan-plugin-git/titan_plugin_git/steps/push_step.py +3 -3
  12. titan_cli-0.1.7/plugins/titan-plugin-git/titan_plugin_git/steps/restore_original_branch_step.py +97 -0
  13. titan_cli-0.1.7/plugins/titan-plugin-git/titan_plugin_git/steps/save_current_branch_step.py +82 -0
  14. {titan_cli-0.1.5 → titan_cli-0.1.7}/plugins/titan-plugin-git/titan_plugin_git/steps/status_step.py +23 -13
  15. {titan_cli-0.1.5 → titan_cli-0.1.7}/plugins/titan-plugin-git/titan_plugin_git/workflows/commit-ai.yaml +4 -3
  16. {titan_cli-0.1.5 → titan_cli-0.1.7}/plugins/titan-plugin-github/titan_plugin_github/steps/ai_pr_step.py +90 -22
  17. {titan_cli-0.1.5 → titan_cli-0.1.7}/plugins/titan-plugin-github/titan_plugin_github/steps/create_pr_step.py +8 -8
  18. {titan_cli-0.1.5 → titan_cli-0.1.7}/plugins/titan-plugin-github/titan_plugin_github/steps/github_prompt_steps.py +13 -13
  19. {titan_cli-0.1.5 → titan_cli-0.1.7}/plugins/titan-plugin-github/titan_plugin_github/steps/issue_steps.py +14 -15
  20. {titan_cli-0.1.5 → titan_cli-0.1.7}/plugins/titan-plugin-github/titan_plugin_github/steps/preview_step.py +8 -8
  21. {titan_cli-0.1.5 → titan_cli-0.1.7}/plugins/titan-plugin-github/titan_plugin_github/workflows/create-pr-ai.yaml +1 -11
  22. {titan_cli-0.1.5 → titan_cli-0.1.7}/plugins/titan-plugin-jira/titan_plugin_jira/messages.py +12 -0
  23. {titan_cli-0.1.5 → titan_cli-0.1.7}/plugins/titan-plugin-jira/titan_plugin_jira/plugin.py +4 -0
  24. {titan_cli-0.1.5 → titan_cli-0.1.7}/plugins/titan-plugin-jira/titan_plugin_jira/steps/ai_analyze_issue_step.py +6 -6
  25. {titan_cli-0.1.5 → titan_cli-0.1.7}/plugins/titan-plugin-jira/titan_plugin_jira/steps/get_issue_step.py +7 -7
  26. titan_cli-0.1.7/plugins/titan-plugin-jira/titan_plugin_jira/steps/list_versions_step.py +133 -0
  27. {titan_cli-0.1.5 → titan_cli-0.1.7}/plugins/titan-plugin-jira/titan_plugin_jira/steps/prompt_select_issue_step.py +8 -9
  28. titan_cli-0.1.7/plugins/titan-plugin-jira/titan_plugin_jira/steps/search_jql_step.py +191 -0
  29. {titan_cli-0.1.5 → titan_cli-0.1.7}/plugins/titan-plugin-jira/titan_plugin_jira/steps/search_saved_query_step.py +13 -13
  30. {titan_cli-0.1.5 → titan_cli-0.1.7}/plugins/titan-plugin-jira/titan_plugin_jira/utils/__init__.py +1 -1
  31. {titan_cli-0.1.5 → titan_cli-0.1.7}/pyproject.toml +9 -9
  32. {titan_cli-0.1.5 → titan_cli-0.1.7}/titan_cli/core/workflows/project_step_source.py +52 -7
  33. {titan_cli-0.1.5 → titan_cli-0.1.7}/titan_cli/core/workflows/workflow_filter_service.py +6 -4
  34. {titan_cli-0.1.5 → titan_cli-0.1.7}/titan_cli/engine/__init__.py +5 -1
  35. {titan_cli-0.1.5 → titan_cli-0.1.7}/titan_cli/engine/results.py +31 -1
  36. {titan_cli-0.1.5 → titan_cli-0.1.7}/titan_cli/engine/steps/ai_assistant_step.py +18 -18
  37. {titan_cli-0.1.5 → titan_cli-0.1.7}/titan_cli/engine/workflow_executor.py +7 -2
  38. {titan_cli-0.1.5 → titan_cli-0.1.7}/titan_cli/ui/tui/screens/plugin_config_wizard.py +16 -0
  39. {titan_cli-0.1.5 → titan_cli-0.1.7}/titan_cli/ui/tui/screens/workflow_execution.py +22 -24
  40. {titan_cli-0.1.5 → titan_cli-0.1.7}/titan_cli/ui/tui/screens/workflows.py +8 -4
  41. {titan_cli-0.1.5 → titan_cli-0.1.7}/titan_cli/ui/tui/textual_components.py +293 -189
  42. {titan_cli-0.1.5 → titan_cli-0.1.7}/titan_cli/ui/tui/textual_workflow_executor.py +30 -2
  43. {titan_cli-0.1.5 → titan_cli-0.1.7}/titan_cli/ui/tui/theme.py +34 -5
  44. {titan_cli-0.1.5 → titan_cli-0.1.7}/titan_cli/ui/tui/widgets/__init__.py +15 -0
  45. titan_cli-0.1.7/titan_cli/ui/tui/widgets/multiline_input.py +32 -0
  46. titan_cli-0.1.7/titan_cli/ui/tui/widgets/prompt_choice.py +138 -0
  47. titan_cli-0.1.7/titan_cli/ui/tui/widgets/prompt_input.py +74 -0
  48. titan_cli-0.1.7/titan_cli/ui/tui/widgets/prompt_selection_list.py +150 -0
  49. titan_cli-0.1.7/titan_cli/ui/tui/widgets/prompt_textarea.py +87 -0
  50. titan_cli-0.1.7/titan_cli/ui/tui/widgets/styled_option_list.py +107 -0
  51. titan_cli-0.1.7/titan_cli/ui/tui/widgets/text.py +98 -0
  52. titan_cli-0.1.5/titan_cli/ui/tui/widgets/text.py +0 -177
  53. {titan_cli-0.1.5 → titan_cli-0.1.7}/LICENSE +0 -0
  54. {titan_cli-0.1.5 → titan_cli-0.1.7}/README.md +0 -0
  55. {titan_cli-0.1.5 → titan_cli-0.1.7}/plugins/titan-plugin-git/titan_plugin_git/__init__.py +0 -0
  56. {titan_cli-0.1.5 → titan_cli-0.1.7}/plugins/titan-plugin-git/titan_plugin_git/clients/__init__.py +0 -0
  57. {titan_cli-0.1.5 → titan_cli-0.1.7}/plugins/titan-plugin-git/titan_plugin_git/exceptions.py +0 -0
  58. {titan_cli-0.1.5 → titan_cli-0.1.7}/plugins/titan-plugin-git/titan_plugin_git/messages.py +0 -0
  59. {titan_cli-0.1.5 → titan_cli-0.1.7}/plugins/titan-plugin-git/titan_plugin_git/models.py +0 -0
  60. {titan_cli-0.1.5 → titan_cli-0.1.7}/plugins/titan-plugin-git/titan_plugin_git/steps/__init__.py +0 -0
  61. {titan_cli-0.1.5 → titan_cli-0.1.7}/plugins/titan-plugin-git/titan_plugin_git/workflows/__previews__/__init__.py +0 -0
  62. {titan_cli-0.1.5 → titan_cli-0.1.7}/plugins/titan-plugin-git/titan_plugin_git/workflows/__previews__/commit_ai_preview.py +0 -0
  63. {titan_cli-0.1.5 → titan_cli-0.1.7}/plugins/titan-plugin-github/titan_plugin_github/__init__.py +0 -0
  64. {titan_cli-0.1.5 → titan_cli-0.1.7}/plugins/titan-plugin-github/titan_plugin_github/agents/__init__.py +0 -0
  65. {titan_cli-0.1.5 → titan_cli-0.1.7}/plugins/titan-plugin-github/titan_plugin_github/agents/config_loader.py +0 -0
  66. {titan_cli-0.1.5 → titan_cli-0.1.7}/plugins/titan-plugin-github/titan_plugin_github/agents/issue_generator.py +0 -0
  67. {titan_cli-0.1.5 → titan_cli-0.1.7}/plugins/titan-plugin-github/titan_plugin_github/agents/pr_agent.py +0 -0
  68. {titan_cli-0.1.5 → titan_cli-0.1.7}/plugins/titan-plugin-github/titan_plugin_github/clients/__init__.py +0 -0
  69. {titan_cli-0.1.5 → titan_cli-0.1.7}/plugins/titan-plugin-github/titan_plugin_github/clients/github_client.py +0 -0
  70. {titan_cli-0.1.5 → titan_cli-0.1.7}/plugins/titan-plugin-github/titan_plugin_github/config/__init__.py +0 -0
  71. {titan_cli-0.1.5 → titan_cli-0.1.7}/plugins/titan-plugin-github/titan_plugin_github/config/pr_agent.toml +0 -0
  72. {titan_cli-0.1.5 → titan_cli-0.1.7}/plugins/titan-plugin-github/titan_plugin_github/exceptions.py +0 -0
  73. {titan_cli-0.1.5 → titan_cli-0.1.7}/plugins/titan-plugin-github/titan_plugin_github/messages.py +0 -0
  74. {titan_cli-0.1.5 → titan_cli-0.1.7}/plugins/titan-plugin-github/titan_plugin_github/models.py +0 -0
  75. {titan_cli-0.1.5 → titan_cli-0.1.7}/plugins/titan-plugin-github/titan_plugin_github/plugin.py +0 -0
  76. {titan_cli-0.1.5 → titan_cli-0.1.7}/plugins/titan-plugin-github/titan_plugin_github/steps/__init__.py +0 -0
  77. {titan_cli-0.1.5 → titan_cli-0.1.7}/plugins/titan-plugin-github/titan_plugin_github/utils.py +0 -0
  78. {titan_cli-0.1.5 → titan_cli-0.1.7}/plugins/titan-plugin-github/titan_plugin_github/workflows/__previews__/__init__.py +0 -0
  79. {titan_cli-0.1.5 → titan_cli-0.1.7}/plugins/titan-plugin-github/titan_plugin_github/workflows/__previews__/create_pr_ai_preview.py +0 -0
  80. {titan_cli-0.1.5 → titan_cli-0.1.7}/plugins/titan-plugin-github/titan_plugin_github/workflows/create-issue-ai.yaml +0 -0
  81. {titan_cli-0.1.5 → titan_cli-0.1.7}/plugins/titan-plugin-jira/titan_plugin_jira/__init__.py +0 -0
  82. {titan_cli-0.1.5 → titan_cli-0.1.7}/plugins/titan-plugin-jira/titan_plugin_jira/agents/__init__.py +0 -0
  83. {titan_cli-0.1.5 → titan_cli-0.1.7}/plugins/titan-plugin-jira/titan_plugin_jira/agents/config_loader.py +0 -0
  84. {titan_cli-0.1.5 → titan_cli-0.1.7}/plugins/titan-plugin-jira/titan_plugin_jira/agents/jira_agent.py +0 -0
  85. {titan_cli-0.1.5 → titan_cli-0.1.7}/plugins/titan-plugin-jira/titan_plugin_jira/agents/prompts.py +0 -0
  86. {titan_cli-0.1.5 → titan_cli-0.1.7}/plugins/titan-plugin-jira/titan_plugin_jira/agents/response_parser.py +0 -0
  87. {titan_cli-0.1.5 → titan_cli-0.1.7}/plugins/titan-plugin-jira/titan_plugin_jira/agents/token_tracker.py +0 -0
  88. {titan_cli-0.1.5 → titan_cli-0.1.7}/plugins/titan-plugin-jira/titan_plugin_jira/agents/validators.py +0 -0
  89. {titan_cli-0.1.5 → titan_cli-0.1.7}/plugins/titan-plugin-jira/titan_plugin_jira/clients/jira_client.py +0 -0
  90. {titan_cli-0.1.5 → titan_cli-0.1.7}/plugins/titan-plugin-jira/titan_plugin_jira/config/jira_agent.toml +0 -0
  91. {titan_cli-0.1.5 → titan_cli-0.1.7}/plugins/titan-plugin-jira/titan_plugin_jira/config/templates/issue_analysis.md.j2 +0 -0
  92. {titan_cli-0.1.5 → titan_cli-0.1.7}/plugins/titan-plugin-jira/titan_plugin_jira/exceptions.py +0 -0
  93. {titan_cli-0.1.5 → titan_cli-0.1.7}/plugins/titan-plugin-jira/titan_plugin_jira/formatters/__init__.py +0 -0
  94. {titan_cli-0.1.5 → titan_cli-0.1.7}/plugins/titan-plugin-jira/titan_plugin_jira/formatters/markdown_formatter.py +0 -0
  95. {titan_cli-0.1.5 → titan_cli-0.1.7}/plugins/titan-plugin-jira/titan_plugin_jira/models.py +0 -0
  96. {titan_cli-0.1.5 → titan_cli-0.1.7}/plugins/titan-plugin-jira/titan_plugin_jira/utils/issue_sorter.py +0 -0
  97. {titan_cli-0.1.5 → titan_cli-0.1.7}/plugins/titan-plugin-jira/titan_plugin_jira/utils/saved_queries.py +0 -0
  98. {titan_cli-0.1.5 → titan_cli-0.1.7}/plugins/titan-plugin-jira/titan_plugin_jira/workflows/analyze-jira-issues.yaml +0 -0
  99. {titan_cli-0.1.5 → titan_cli-0.1.7}/titan_cli/__init__.py +0 -0
  100. {titan_cli-0.1.5 → titan_cli-0.1.7}/titan_cli/__main__.py +0 -0
  101. {titan_cli-0.1.5 → titan_cli-0.1.7}/titan_cli/ai/__init__.py +0 -0
  102. {titan_cli-0.1.5 → titan_cli-0.1.7}/titan_cli/ai/agents/__init__.py +0 -0
  103. {titan_cli-0.1.5 → titan_cli-0.1.7}/titan_cli/ai/agents/base.py +0 -0
  104. {titan_cli-0.1.5 → titan_cli-0.1.7}/titan_cli/ai/client.py +0 -0
  105. {titan_cli-0.1.5 → titan_cli-0.1.7}/titan_cli/ai/constants.py +0 -0
  106. {titan_cli-0.1.5 → titan_cli-0.1.7}/titan_cli/ai/exceptions.py +0 -0
  107. {titan_cli-0.1.5 → titan_cli-0.1.7}/titan_cli/ai/models.py +0 -0
  108. {titan_cli-0.1.5 → titan_cli-0.1.7}/titan_cli/ai/oauth_helper.py +0 -0
  109. {titan_cli-0.1.5 → titan_cli-0.1.7}/titan_cli/ai/providers/__init__.py +0 -0
  110. {titan_cli-0.1.5 → titan_cli-0.1.7}/titan_cli/ai/providers/anthropic.py +0 -0
  111. {titan_cli-0.1.5 → titan_cli-0.1.7}/titan_cli/ai/providers/base.py +0 -0
  112. {titan_cli-0.1.5 → titan_cli-0.1.7}/titan_cli/ai/providers/gemini.py +0 -0
  113. {titan_cli-0.1.5 → titan_cli-0.1.7}/titan_cli/cli.py +0 -0
  114. {titan_cli-0.1.5 → titan_cli-0.1.7}/titan_cli/clients/__init__.py +0 -0
  115. {titan_cli-0.1.5 → titan_cli-0.1.7}/titan_cli/clients/gcloud_client.py +0 -0
  116. {titan_cli-0.1.5 → titan_cli-0.1.7}/titan_cli/core/__init__.py +0 -0
  117. {titan_cli-0.1.5 → titan_cli-0.1.7}/titan_cli/core/config.py +0 -0
  118. {titan_cli-0.1.5 → titan_cli-0.1.7}/titan_cli/core/discovery.py +0 -0
  119. {titan_cli-0.1.5 → titan_cli-0.1.7}/titan_cli/core/errors.py +0 -0
  120. {titan_cli-0.1.5 → titan_cli-0.1.7}/titan_cli/core/models.py +0 -0
  121. {titan_cli-0.1.5 → titan_cli-0.1.7}/titan_cli/core/plugins/available.py +0 -0
  122. {titan_cli-0.1.5 → titan_cli-0.1.7}/titan_cli/core/plugins/models.py +0 -0
  123. {titan_cli-0.1.5 → titan_cli-0.1.7}/titan_cli/core/plugins/plugin_base.py +0 -0
  124. {titan_cli-0.1.5 → titan_cli-0.1.7}/titan_cli/core/plugins/plugin_registry.py +0 -0
  125. {titan_cli-0.1.5 → titan_cli-0.1.7}/titan_cli/core/secrets.py +0 -0
  126. {titan_cli-0.1.5 → titan_cli-0.1.7}/titan_cli/core/workflows/__init__.py +0 -0
  127. {titan_cli-0.1.5 → titan_cli-0.1.7}/titan_cli/core/workflows/models.py +0 -0
  128. {titan_cli-0.1.5 → titan_cli-0.1.7}/titan_cli/core/workflows/workflow_exceptions.py +0 -0
  129. {titan_cli-0.1.5 → titan_cli-0.1.7}/titan_cli/core/workflows/workflow_registry.py +0 -0
  130. {titan_cli-0.1.5 → titan_cli-0.1.7}/titan_cli/core/workflows/workflow_sources.py +0 -0
  131. {titan_cli-0.1.5 → titan_cli-0.1.7}/titan_cli/engine/builder.py +0 -0
  132. {titan_cli-0.1.5 → titan_cli-0.1.7}/titan_cli/engine/context.py +0 -0
  133. {titan_cli-0.1.5 → titan_cli-0.1.7}/titan_cli/engine/mock_context.py +0 -0
  134. {titan_cli-0.1.5 → titan_cli-0.1.7}/titan_cli/engine/steps/command_step.py +0 -0
  135. {titan_cli-0.1.5 → titan_cli-0.1.7}/titan_cli/engine/utils/__init__.py +0 -0
  136. {titan_cli-0.1.5 → titan_cli-0.1.7}/titan_cli/engine/utils/venv.py +0 -0
  137. {titan_cli-0.1.5 → titan_cli-0.1.7}/titan_cli/external_cli/__init__.py +0 -0
  138. {titan_cli-0.1.5 → titan_cli-0.1.7}/titan_cli/external_cli/configs.py +0 -0
  139. {titan_cli-0.1.5 → titan_cli-0.1.7}/titan_cli/external_cli/launcher.py +0 -0
  140. {titan_cli-0.1.5 → titan_cli-0.1.7}/titan_cli/messages.py +0 -0
  141. {titan_cli-0.1.5 → titan_cli-0.1.7}/titan_cli/ui/tui/__init__.py +0 -0
  142. {titan_cli-0.1.5 → titan_cli-0.1.7}/titan_cli/ui/tui/__previews__/statusbar_preview.py +0 -0
  143. {titan_cli-0.1.5 → titan_cli-0.1.7}/titan_cli/ui/tui/app.py +0 -0
  144. {titan_cli-0.1.5 → titan_cli-0.1.7}/titan_cli/ui/tui/icons.py +0 -0
  145. {titan_cli-0.1.5 → titan_cli-0.1.7}/titan_cli/ui/tui/screens/__init__.py +0 -0
  146. {titan_cli-0.1.5 → titan_cli-0.1.7}/titan_cli/ui/tui/screens/ai_config.py +0 -0
  147. {titan_cli-0.1.5 → titan_cli-0.1.7}/titan_cli/ui/tui/screens/ai_config_wizard.py +0 -0
  148. {titan_cli-0.1.5 → titan_cli-0.1.7}/titan_cli/ui/tui/screens/base.py +0 -0
  149. {titan_cli-0.1.5 → titan_cli-0.1.7}/titan_cli/ui/tui/screens/cli_launcher.py +0 -0
  150. {titan_cli-0.1.5 → titan_cli-0.1.7}/titan_cli/ui/tui/screens/global_setup_wizard.py +0 -0
  151. {titan_cli-0.1.5 → titan_cli-0.1.7}/titan_cli/ui/tui/screens/main_menu.py +0 -0
  152. {titan_cli-0.1.5 → titan_cli-0.1.7}/titan_cli/ui/tui/screens/plugin_management.py +0 -0
  153. {titan_cli-0.1.5 → titan_cli-0.1.7}/titan_cli/ui/tui/screens/project_setup_wizard.py +0 -0
  154. {titan_cli-0.1.5 → titan_cli-0.1.7}/titan_cli/ui/tui/widgets/button.py +0 -0
  155. {titan_cli-0.1.5 → titan_cli-0.1.7}/titan_cli/ui/tui/widgets/header.py +0 -0
  156. {titan_cli-0.1.5 → titan_cli-0.1.7}/titan_cli/ui/tui/widgets/panel.py +0 -0
  157. {titan_cli-0.1.5 → titan_cli-0.1.7}/titan_cli/ui/tui/widgets/status_bar.py +0 -0
  158. {titan_cli-0.1.5 → titan_cli-0.1.7}/titan_cli/ui/tui/widgets/step_container.py +0 -0
  159. {titan_cli-0.1.5 → titan_cli-0.1.7}/titan_cli/ui/tui/widgets/table.py +0 -0
  160. {titan_cli-0.1.5 → titan_cli-0.1.7}/titan_cli/utils/__init__.py +0 -0
  161. {titan_cli-0.1.5 → titan_cli-0.1.7}/titan_cli/utils/autoupdate.py +0 -0
@@ -1,14 +1,12 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.1
2
2
  Name: titan-cli
3
- Version: 0.1.5
3
+ Version: 0.1.7
4
4
  Summary: Modular development tools orchestrator - Streamline your workflows with AI integration and intuitive terminal UI
5
+ Home-page: https://github.com/masmovil/titan-cli
5
6
  License: MIT
6
- License-File: LICENSE
7
7
  Keywords: cli,workflow,orchestrator,automation,devtools,ai
8
- Author: finxo
9
- Author-email: finxeto@gmail.com
10
- Maintainer: r-pedraza
11
- Maintainer-email: raulpedrazaleon@gmail.com
8
+ Author: MasOrange Apps Team
9
+ Author-email: apps-management-stores@masorange.es
12
10
  Requires-Python: >=3.10,<4.0.0
13
11
  Classifier: Development Status :: 5 - Production/Stable
14
12
  Classifier: Environment :: Console
@@ -19,8 +17,6 @@ Classifier: Programming Language :: Python :: 3
19
17
  Classifier: Programming Language :: Python :: 3.10
20
18
  Classifier: Programming Language :: Python :: 3.11
21
19
  Classifier: Programming Language :: Python :: 3.12
22
- Classifier: Programming Language :: Python :: 3.13
23
- Classifier: Programming Language :: Python :: 3.14
24
20
  Requires-Dist: anthropic (>=0.75.0,<0.76.0)
25
21
  Requires-Dist: google-auth (>=2.43.0,<3.0.0)
26
22
  Requires-Dist: google-genai (>=1.58.0,<2.0.0)
@@ -36,7 +32,6 @@ Requires-Dist: tomli (>=2.0.0,<3.0.0)
36
32
  Requires-Dist: tomli-w (>=1.0.0,<2.0.0)
37
33
  Requires-Dist: typer (>=0.20.0,<1.0.0)
38
34
  Project-URL: Documentation, https://github.com/masmovil/titan-cli
39
- Project-URL: Homepage, https://github.com/masmovil/titan-cli
40
35
  Project-URL: Repository, https://github.com/masmovil/titan-cli
41
36
  Description-Content-Type: text/markdown
42
37
 
@@ -109,6 +109,27 @@ class GitClient:
109
109
  """
110
110
  return self._run_command(["git", "rev-parse", "--abbrev-ref", "HEAD"])
111
111
 
112
+ def get_current_commit(self) -> str:
113
+ """
114
+ Get current commit SHA (HEAD)
115
+
116
+ Returns:
117
+ Full SHA of current commit
118
+ """
119
+ return self._run_command(["git", "rev-parse", "HEAD"])
120
+
121
+ def get_commit_sha(self, ref: str) -> str:
122
+ """
123
+ Get commit SHA for any git ref (branch, tag, remote branch, etc.)
124
+
125
+ Args:
126
+ ref: Git reference (e.g., "HEAD", "develop", "origin/main", "v1.0.0")
127
+
128
+ Returns:
129
+ Full SHA of the commit
130
+ """
131
+ return self._run_command(["git", "rev-parse", ref])
132
+
112
133
  def get_status(self) -> GitStatus:
113
134
  """
114
135
  Get repository status
@@ -282,7 +303,7 @@ class GitClient:
282
303
  """
283
304
  self._run_command(["git", "branch", branch_name, start_point])
284
305
 
285
- def commit(self, message: str, all: bool = False, no_verify: bool = False) -> str:
306
+ def commit(self, message: str, all: bool = False, no_verify: bool = True) -> str:
286
307
  """
287
308
  Create a commit
288
309
 
@@ -390,12 +411,13 @@ class GitClient:
390
411
 
391
412
  self._run_command(args)
392
413
 
393
- def fetch(self, remote: str = "origin", all: bool = False) -> None:
414
+ def fetch(self, remote: str = "origin", branch: Optional[str] = None, all: bool = False) -> None:
394
415
  """
395
416
  Fetch from remote
396
417
 
397
418
  Args:
398
419
  remote: Remote name
420
+ branch: Specific branch to fetch (optional)
399
421
  all: Fetch from all remotes
400
422
  """
401
423
  args = ["git", "fetch"]
@@ -404,6 +426,8 @@ class GitClient:
404
426
  args.append("--all")
405
427
  else:
406
428
  args.append(remote)
429
+ if branch:
430
+ args.append(branch)
407
431
 
408
432
  self._run_command(args)
409
433
 
@@ -736,6 +760,39 @@ class GitClient:
736
760
  except GitCommandError:
737
761
  return ""
738
762
 
763
+ def get_uncommitted_diff_stat(self) -> str:
764
+ """
765
+ Get diff stat summary of uncommitted changes (git diff --stat HEAD).
766
+
767
+ Returns a summary showing files changed, insertions, and deletions.
768
+
769
+ Returns:
770
+ Diff stat output as string
771
+ """
772
+ try:
773
+ return self._run_command(["git", "diff", "--stat", "HEAD"], check=False)
774
+ except GitCommandError:
775
+ return ""
776
+
777
+ def get_branch_diff_stat(self, base_branch: str, head_branch: str) -> str:
778
+ """
779
+ Get diff stat summary between two branches.
780
+
781
+ Args:
782
+ base_branch: Base branch name
783
+ head_branch: Head branch name
784
+
785
+ Returns:
786
+ Diff stat output as string
787
+ """
788
+ try:
789
+ return self._run_command(
790
+ ["git", "diff", "--stat", f"{base_branch}...{head_branch}"],
791
+ check=False
792
+ )
793
+ except GitCommandError:
794
+ return ""
795
+
739
796
  def get_branch_commits(self, base_branch: str, head_branch: str) -> list[str]:
740
797
  """
741
798
  Get list of commits in head_branch that are not in base_branch.
@@ -101,6 +101,11 @@ class GitPlugin(TitanPlugin):
101
101
  from .steps.branch_steps import get_current_branch_step, get_base_branch_step
102
102
  from .steps.ai_commit_message_step import ai_generate_commit_message
103
103
  from .steps.diff_summary_step import show_uncommitted_diff_summary, show_branch_diff_summary
104
+ from .steps.save_current_branch_step import save_current_branch_step
105
+ from .steps.restore_original_branch_step import restore_original_branch_step
106
+ from .steps.checkout_step import checkout_branch_step
107
+ from .steps.pull_step import pull_step
108
+ from .steps.create_branch_step import create_branch_step
104
109
 
105
110
  return {
106
111
  "get_status": get_git_status_step,
@@ -111,6 +116,11 @@ class GitPlugin(TitanPlugin):
111
116
  "ai_generate_commit_message": ai_generate_commit_message,
112
117
  "show_uncommitted_diff_summary": show_uncommitted_diff_summary,
113
118
  "show_branch_diff_summary": show_branch_diff_summary,
119
+ "save_current_branch": save_current_branch_step,
120
+ "restore_original_branch": restore_original_branch_step,
121
+ "checkout": checkout_branch_step,
122
+ "pull": pull_step,
123
+ "create_branch": create_branch_step,
114
124
  }
115
125
 
116
126
  @property
@@ -33,7 +33,7 @@ def ai_generate_commit_message(ctx: WorkflowContext) -> WorkflowResult:
33
33
 
34
34
  # Check if AI is configured
35
35
  if not ctx.ai or not ctx.ai.is_available():
36
- ctx.textual.text(msg.Steps.AICommitMessage.AI_NOT_CONFIGURED, markup="dim")
36
+ ctx.textual.dim_text(msg.Steps.AICommitMessage.AI_NOT_CONFIGURED)
37
37
  ctx.textual.end_step("skip")
38
38
  return Skip(msg.Steps.AICommitMessage.AI_NOT_CONFIGURED)
39
39
 
@@ -45,13 +45,13 @@ def ai_generate_commit_message(ctx: WorkflowContext) -> WorkflowResult:
45
45
  # Get git status
46
46
  git_status = ctx.get('git_status')
47
47
  if not git_status or git_status.is_clean:
48
- ctx.textual.text(msg.Steps.AICommitMessage.NO_CHANGES_TO_COMMIT, markup="dim")
48
+ ctx.textual.dim_text(msg.Steps.AICommitMessage.NO_CHANGES_TO_COMMIT)
49
49
  ctx.textual.end_step("skip")
50
50
  return Skip(msg.Steps.AICommitMessage.NO_CHANGES_TO_COMMIT)
51
51
 
52
52
  try:
53
53
  # Get diff of uncommitted changes
54
- ctx.textual.text(msg.Steps.AICommitMessage.ANALYZING_CHANGES, markup="dim")
54
+ ctx.textual.dim_text(msg.Steps.AICommitMessage.ANALYZING_CHANGES)
55
55
 
56
56
  # Get diff of all uncommitted changes
57
57
  diff_text = ctx.git.get_uncommitted_diff()
@@ -125,12 +125,12 @@ Return ONLY the single-line commit message, absolutely nothing else."""
125
125
 
126
126
  # Show preview to user
127
127
  ctx.textual.text("") # spacing
128
- ctx.textual.text(msg.Steps.AICommitMessage.GENERATED_MESSAGE_TITLE, markup="bold")
129
- ctx.textual.text(f" {commit_message}", markup="bold cyan")
128
+ ctx.textual.bold_text(msg.Steps.AICommitMessage.GENERATED_MESSAGE_TITLE)
129
+ ctx.textual.bold_primary_text(f" {commit_message}")
130
130
 
131
131
  # Warn if message is too long
132
132
  if len(commit_message) > 72:
133
- ctx.textual.text(msg.Steps.AICommitMessage.MESSAGE_LENGTH_WARNING.format(length=len(commit_message)), markup="yellow")
133
+ ctx.textual.warning_text(msg.Steps.AICommitMessage.MESSAGE_LENGTH_WARNING.format(length=len(commit_message)))
134
134
 
135
135
  ctx.textual.text("") # spacing
136
136
 
@@ -165,8 +165,8 @@ Return ONLY the single-line commit message, absolutely nothing else."""
165
165
  )
166
166
 
167
167
  except Exception as e:
168
- ctx.textual.text(msg.Steps.AICommitMessage.GENERATION_FAILED.format(e=e), markup="yellow")
169
- ctx.textual.text(msg.Steps.AICommitMessage.FALLBACK_TO_MANUAL, markup="dim")
168
+ ctx.textual.warning_text(msg.Steps.AICommitMessage.GENERATION_FAILED.format(e=e))
169
+ ctx.textual.dim_text(msg.Steps.AICommitMessage.FALLBACK_TO_MANUAL)
170
170
 
171
171
  ctx.textual.end_step("skip")
172
172
  return Skip(msg.Steps.AICommitMessage.GENERATION_FAILED.format(e=e))
@@ -24,14 +24,14 @@ def get_current_branch_step(ctx: WorkflowContext) -> WorkflowResult:
24
24
 
25
25
  if not ctx.git:
26
26
  error_msg = msg.Steps.Status.GIT_CLIENT_NOT_AVAILABLE
27
- ctx.textual.text(error_msg, markup="red")
27
+ ctx.textual.error_text(error_msg)
28
28
  ctx.textual.end_step("error")
29
29
  return Error(error_msg)
30
30
 
31
31
  try:
32
32
  current_branch = ctx.git.get_current_branch()
33
33
  success_msg = msg.Steps.Branch.GET_CURRENT_BRANCH_SUCCESS.format(branch=current_branch)
34
- ctx.textual.text(success_msg, markup="green")
34
+ ctx.textual.success_text(success_msg)
35
35
  ctx.textual.end_step("success")
36
36
  return Success(
37
37
  success_msg,
@@ -39,7 +39,7 @@ def get_current_branch_step(ctx: WorkflowContext) -> WorkflowResult:
39
39
  )
40
40
  except Exception as e:
41
41
  error_msg = msg.Steps.Branch.GET_CURRENT_BRANCH_FAILED.format(e=e)
42
- ctx.textual.text(error_msg, markup="red")
42
+ ctx.textual.error_text(error_msg)
43
43
  ctx.textual.end_step("error")
44
44
  return Error(error_msg, exception=e)
45
45
 
@@ -65,14 +65,14 @@ def get_base_branch_step(ctx: WorkflowContext) -> WorkflowResult:
65
65
 
66
66
  if not ctx.git:
67
67
  error_msg = msg.Steps.Status.GIT_CLIENT_NOT_AVAILABLE
68
- ctx.textual.text(error_msg, markup="red")
68
+ ctx.textual.error_text(error_msg)
69
69
  ctx.textual.end_step("error")
70
70
  return Error(error_msg)
71
71
 
72
72
  try:
73
73
  base_branch = ctx.git.main_branch
74
74
  success_msg = msg.Steps.Branch.GET_BASE_BRANCH_SUCCESS.format(branch=base_branch)
75
- ctx.textual.text(success_msg, markup="green")
75
+ ctx.textual.success_text(success_msg)
76
76
  ctx.textual.end_step("success")
77
77
  return Success(
78
78
  success_msg,
@@ -80,6 +80,6 @@ def get_base_branch_step(ctx: WorkflowContext) -> WorkflowResult:
80
80
  )
81
81
  except Exception as e:
82
82
  error_msg = msg.Steps.Branch.GET_BASE_BRANCH_FAILED.format(e=e)
83
- ctx.textual.text(error_msg, markup="red")
83
+ ctx.textual.error_text(error_msg)
84
84
  ctx.textual.end_step("error")
85
85
  return Error(error_msg, exception=e)
@@ -0,0 +1,66 @@
1
+ """
2
+ Checkout a Git branch.
3
+ """
4
+
5
+ from titan_cli.engine import WorkflowContext, WorkflowResult, Success, Error
6
+ from titan_plugin_git.exceptions import GitError
7
+
8
+
9
+ def checkout_branch_step(ctx: WorkflowContext) -> WorkflowResult:
10
+ """
11
+ Checkout a Git branch.
12
+
13
+ Inputs (from ctx.data):
14
+ branch (str): Branch name to checkout
15
+
16
+ Returns:
17
+ Success: Branch checked out successfully
18
+ Error: Git operation failed
19
+ """
20
+ if ctx.textual:
21
+ ctx.textual.begin_step("Checkout Branch")
22
+
23
+ if not ctx.textual:
24
+ return Error("Textual UI context is not available for this step.")
25
+
26
+ if not ctx.git:
27
+ ctx.textual.error_text("Git client not available in context")
28
+ ctx.textual.end_step("error")
29
+ return Error("Git client not available in context")
30
+
31
+ try:
32
+ # Get branch from params, or use main_branch from git config
33
+ branch = ctx.get("branch")
34
+ if not branch:
35
+ branch = ctx.git.main_branch
36
+ ctx.textual.dim_text(f"Using main branch from config: {branch}")
37
+
38
+ ctx.textual.text("")
39
+ ctx.textual.dim_text(f"Checking out: {branch}")
40
+
41
+ # Checkout branch
42
+ try:
43
+ ctx.git.checkout(branch)
44
+ ctx.textual.success_text(f"✓ Checked out {branch}")
45
+ except GitError as e:
46
+ ctx.textual.text("")
47
+ ctx.textual.error_text(f"Failed to checkout {branch}: {str(e)}")
48
+ ctx.textual.end_step("error")
49
+ return Error(f"Failed to checkout: {str(e)}")
50
+
51
+ ctx.textual.text("")
52
+ ctx.textual.end_step("success")
53
+ return Success(
54
+ f"Checked out {branch}",
55
+ metadata={"branch": branch}
56
+ )
57
+
58
+ except Exception as e:
59
+ ctx.textual.text("")
60
+ ctx.textual.error_text(f"Failed to checkout branch: {str(e)}")
61
+ ctx.textual.text("")
62
+ ctx.textual.end_step("error")
63
+ return Error(f"Failed to checkout: {str(e)}")
64
+
65
+
66
+ __all__ = ["checkout_branch_step"]
@@ -37,7 +37,7 @@ def create_git_commit_step(ctx: WorkflowContext) -> WorkflowResult:
37
37
  # Skip if there's nothing to commit
38
38
  git_status = ctx.data.get("git_status")
39
39
  if git_status and git_status.is_clean:
40
- ctx.textual.text(msg.Steps.Commit.WORKING_DIRECTORY_CLEAN, markup="dim")
40
+ ctx.textual.dim_text(msg.Steps.Commit.WORKING_DIRECTORY_CLEAN)
41
41
  ctx.textual.end_step("skip")
42
42
  return Skip(msg.Steps.Commit.WORKING_DIRECTORY_CLEAN)
43
43
 
@@ -47,7 +47,7 @@ def create_git_commit_step(ctx: WorkflowContext) -> WorkflowResult:
47
47
 
48
48
  commit_message = ctx.get('commit_message')
49
49
  if not commit_message:
50
- ctx.textual.text(msg.Steps.Commit.NO_COMMIT_MESSAGE, markup="dim")
50
+ ctx.textual.dim_text(msg.Steps.Commit.NO_COMMIT_MESSAGE)
51
51
  ctx.textual.end_step("skip")
52
52
  return Skip(msg.Steps.Commit.NO_COMMIT_MESSAGE)
53
53
 
@@ -58,7 +58,7 @@ def create_git_commit_step(ctx: WorkflowContext) -> WorkflowResult:
58
58
  commit_hash = ctx.git.commit(message=commit_message, all=all_files, no_verify=no_verify)
59
59
 
60
60
  # Show success message
61
- ctx.textual.text(f"Commit created: {commit_hash[:7]}", markup="green")
61
+ ctx.textual.success_text(f"Commit created: {commit_hash[:7]}")
62
62
 
63
63
  ctx.textual.end_step("success")
64
64
  return Success(
@@ -0,0 +1,131 @@
1
+ """
2
+ Create a new Git branch.
3
+ """
4
+
5
+ from titan_cli.engine import WorkflowContext, WorkflowResult, Success, Error
6
+ from titan_plugin_git.exceptions import GitError
7
+
8
+
9
+ def create_branch_step(ctx: WorkflowContext) -> WorkflowResult:
10
+ """
11
+ Create a new Git branch.
12
+
13
+ Inputs (from ctx.data):
14
+ new_branch (str): Name of the branch to create
15
+ start_point (str, optional): Starting point for the branch (defaults to HEAD)
16
+ delete_if_exists (bool, optional): Delete the branch if it already exists (default: False)
17
+ checkout (bool, optional): Checkout the new branch after creation (default: True)
18
+
19
+ Returns:
20
+ Success: Branch created successfully
21
+ Error: Git operation failed
22
+ """
23
+ if ctx.textual:
24
+ ctx.textual.begin_step("Create Branch")
25
+
26
+ if not ctx.textual:
27
+ return Error("Textual UI context is not available for this step.")
28
+
29
+ if not ctx.git:
30
+ ctx.textual.error_text("Git client not available in context")
31
+ ctx.textual.end_step("error")
32
+ return Error("Git client not available in context")
33
+
34
+ try:
35
+ # Get params from context
36
+ new_branch = ctx.get("new_branch")
37
+ if not new_branch:
38
+ ctx.textual.error_text("No branch name specified")
39
+ ctx.textual.dim_text("Set 'new_branch' in workflow params or previous step")
40
+ ctx.textual.end_step("error")
41
+ return Error("No branch name specified")
42
+
43
+ start_point = ctx.get("start_point", "HEAD")
44
+ delete_if_exists = ctx.get("delete_if_exists", False)
45
+ checkout = ctx.get("checkout", True)
46
+
47
+ ctx.textual.text("")
48
+ ctx.textual.dim_text(f"Creating branch: {new_branch}")
49
+ ctx.textual.dim_text(f"From: {start_point}")
50
+
51
+ # Check if branch exists
52
+ all_branches = ctx.git.get_branches()
53
+ branch_names = [b.name for b in all_branches]
54
+ branch_exists = new_branch in branch_names
55
+
56
+ # Delete if exists and requested
57
+ if branch_exists and delete_if_exists:
58
+ ctx.textual.text("")
59
+ ctx.textual.warning_text(f"Branch {new_branch} exists, deleting...")
60
+
61
+ # If we're on the branch, checkout another one first
62
+ current_branch = ctx.git.get_current_branch()
63
+ if current_branch == new_branch:
64
+ # Checkout main branch before deleting
65
+ main_branch = ctx.git.main_branch
66
+ if main_branch in branch_names and main_branch != new_branch:
67
+ try:
68
+ ctx.git.checkout(main_branch)
69
+ ctx.textual.dim_text(f"Switched to {main_branch}")
70
+ except GitError as e:
71
+ ctx.textual.error_text(f"Failed to checkout {main_branch}: {str(e)}")
72
+ ctx.textual.end_step("error")
73
+ return Error(f"Cannot checkout {main_branch}: {str(e)}")
74
+ else:
75
+ ctx.textual.error_text(f"Cannot delete current branch {new_branch}")
76
+ ctx.textual.end_step("error")
77
+ return Error("Cannot delete current branch")
78
+
79
+ # Delete the branch
80
+ try:
81
+ ctx.git.safe_delete_branch(new_branch, force=True)
82
+ ctx.textual.success_text(f"✓ Deleted existing branch {new_branch}")
83
+ except GitError as e:
84
+ ctx.textual.error_text(f"Failed to delete {new_branch}: {str(e)}")
85
+ ctx.textual.end_step("error")
86
+ return Error(f"Failed to delete branch: {str(e)}")
87
+
88
+ elif branch_exists:
89
+ ctx.textual.error_text(f"Branch {new_branch} already exists")
90
+ ctx.textual.dim_text("Set 'delete_if_exists: true' to recreate it")
91
+ ctx.textual.end_step("error")
92
+ return Error(f"Branch {new_branch} already exists")
93
+
94
+ # Create the branch
95
+ ctx.textual.text("")
96
+ try:
97
+ ctx.git.create_branch(new_branch, start_point=start_point)
98
+ ctx.textual.success_text(f"✓ Created branch {new_branch}")
99
+ except GitError as e:
100
+ ctx.textual.error_text(f"Failed to create {new_branch}: {str(e)}")
101
+ ctx.textual.end_step("error")
102
+ return Error(f"Failed to create branch: {str(e)}")
103
+
104
+ # Checkout if requested
105
+ if checkout:
106
+ try:
107
+ ctx.git.checkout(new_branch)
108
+ ctx.textual.success_text(f"✓ Checked out {new_branch}")
109
+ except GitError as e:
110
+ ctx.textual.warning_text(f"Branch created but failed to checkout: {str(e)}")
111
+
112
+ ctx.textual.text("")
113
+ ctx.textual.end_step("success")
114
+ return Success(
115
+ f"Created branch {new_branch}",
116
+ metadata={
117
+ "new_branch": new_branch,
118
+ "start_point": start_point,
119
+ "checked_out": checkout
120
+ }
121
+ )
122
+
123
+ except Exception as e:
124
+ ctx.textual.text("")
125
+ ctx.textual.error_text(f"Failed to create branch: {str(e)}")
126
+ ctx.textual.text("")
127
+ ctx.textual.end_step("error")
128
+ return Error(f"Failed to create branch: {str(e)}")
129
+
130
+
131
+ __all__ = ["create_branch_step"]
@@ -24,16 +24,16 @@ def show_uncommitted_diff_summary(ctx: WorkflowContext) -> WorkflowResult:
24
24
 
25
25
  try:
26
26
  # Get diff stat for uncommitted changes
27
- stat_output = ctx.git._run_command(["git", "diff", "--stat", "HEAD"])
27
+ stat_output = ctx.git.get_uncommitted_diff_stat()
28
28
 
29
29
  if not stat_output or not stat_output.strip():
30
- ctx.textual.text("No uncommitted changes to show", markup="dim")
30
+ ctx.textual.dim_text("No uncommitted changes to show")
31
31
  ctx.textual.end_step("success")
32
32
  return Success("No changes")
33
33
 
34
34
  # Show the stat summary with colors
35
35
  ctx.textual.text("") # spacing
36
- ctx.textual.text("Changes summary:", markup="bold")
36
+ ctx.textual.bold_text("Changes summary:")
37
37
  ctx.textual.text("") # spacing
38
38
 
39
39
  # Parse lines to find max filename length for alignment
@@ -69,7 +69,7 @@ def show_uncommitted_diff_summary(ctx: WorkflowContext) -> WorkflowResult:
69
69
  for line in summary_lines:
70
70
  colored_line = line.replace('(+)', '[green](+)[/green]')
71
71
  colored_line = colored_line.replace('(-)', '[red](-)[/red]')
72
- ctx.textual.text(f" {colored_line}", markup="dim")
72
+ ctx.textual.dim_text(f" {colored_line}")
73
73
 
74
74
  ctx.textual.text("") # spacing
75
75
 
@@ -104,13 +104,13 @@ def show_branch_diff_summary(ctx: WorkflowContext) -> WorkflowResult:
104
104
  ctx.textual.begin_step("Show Branch Changes Summary")
105
105
 
106
106
  if not ctx.git:
107
- ctx.textual.text(msg.Steps.Push.GIT_CLIENT_NOT_AVAILABLE, markup="red")
107
+ ctx.textual.error_text(msg.Steps.Push.GIT_CLIENT_NOT_AVAILABLE)
108
108
  ctx.textual.end_step("error")
109
109
  return Error(msg.Steps.Push.GIT_CLIENT_NOT_AVAILABLE)
110
110
 
111
111
  head_branch = ctx.get("pr_head_branch")
112
112
  if not head_branch:
113
- ctx.textual.text("No head branch specified", markup="dim")
113
+ ctx.textual.dim_text("No head branch specified")
114
114
  ctx.textual.end_step("skip")
115
115
  return Skip("No head branch specified")
116
116
 
@@ -118,18 +118,16 @@ def show_branch_diff_summary(ctx: WorkflowContext) -> WorkflowResult:
118
118
 
119
119
  try:
120
120
  # Get diff stat between branches
121
- stat_output = ctx.git._run_command([
122
- "git", "diff", "--stat", f"{base_branch}...{head_branch}"
123
- ])
121
+ stat_output = ctx.git.get_branch_diff_stat(base_branch, head_branch)
124
122
 
125
123
  if not stat_output or not stat_output.strip():
126
- ctx.textual.text(f"No changes between {base_branch} and {head_branch}", markup="dim")
124
+ ctx.textual.dim_text(f"No changes between {base_branch} and {head_branch}")
127
125
  ctx.textual.end_step("success")
128
126
  return Success("No changes")
129
127
 
130
128
  # Show the stat summary with colors
131
129
  ctx.textual.text("") # spacing
132
- ctx.textual.text(f"Changes in {head_branch} vs {base_branch}:", markup="bold")
130
+ ctx.textual.bold_text(f"Changes in {head_branch} vs {base_branch}:")
133
131
  ctx.textual.text("") # spacing
134
132
 
135
133
  # Parse lines to find max filename length for alignment
@@ -165,7 +163,7 @@ def show_branch_diff_summary(ctx: WorkflowContext) -> WorkflowResult:
165
163
  for line in summary_lines:
166
164
  colored_line = line.replace('(+)', '[green](+)[/green]')
167
165
  colored_line = colored_line.replace('(-)', '[red](-)[/red]')
168
- ctx.textual.text(f" {colored_line}", markup="dim")
166
+ ctx.textual.dim_text(f" {colored_line}")
169
167
 
170
168
  ctx.textual.text("") # spacing
171
169
 
@@ -174,7 +172,7 @@ def show_branch_diff_summary(ctx: WorkflowContext) -> WorkflowResult:
174
172
 
175
173
  except Exception as e:
176
174
  # Don't fail the workflow, just skip
177
- ctx.textual.text(f"Could not show branch diff summary: {e}", markup="yellow")
175
+ ctx.textual.warning_text(f"Could not show branch diff summary: {e}")
178
176
  ctx.textual.end_step("skip")
179
177
  return Skip(f"Could not show branch diff summary: {e}")
180
178
 
@@ -0,0 +1,70 @@
1
+ """
2
+ Pull from Git remote.
3
+ """
4
+
5
+ from typing import Optional
6
+ from titan_cli.engine import WorkflowContext, WorkflowResult, Success, Error
7
+ from titan_plugin_git.exceptions import GitError
8
+
9
+
10
+ def pull_step(ctx: WorkflowContext) -> WorkflowResult:
11
+ """
12
+ Pull from Git remote.
13
+
14
+ Inputs (from ctx.data):
15
+ remote (str, optional): Remote name (defaults to "origin")
16
+ branch (str, optional): Branch name (defaults to current branch)
17
+
18
+ Returns:
19
+ Success: Pull completed successfully
20
+ Error: Git operation failed
21
+ """
22
+ if ctx.textual:
23
+ ctx.textual.begin_step("Pull from Remote")
24
+
25
+ if not ctx.textual:
26
+ return Error("Textual UI context is not available for this step.")
27
+
28
+ if not ctx.git:
29
+ ctx.textual.error_text("Git client not available in context")
30
+ ctx.textual.end_step("error")
31
+ return Error("Git client not available in context")
32
+
33
+ try:
34
+ # Get params from context (optional)
35
+ remote = ctx.get("remote", "origin")
36
+ branch: Optional[str] = ctx.get("pull_branch") # Optional, defaults to current
37
+
38
+ ctx.textual.text("")
39
+ if branch:
40
+ ctx.textual.dim_text(f"Pulling {remote}/{branch}...")
41
+ else:
42
+ ctx.textual.dim_text(f"Pulling from {remote}...")
43
+
44
+ # Pull
45
+ try:
46
+ with ctx.textual.loading("Pulling from remote..."):
47
+ ctx.git.pull(remote=remote, branch=branch)
48
+ ctx.textual.success_text(f"✓ Pulled from {remote}")
49
+ except GitError as e:
50
+ ctx.textual.text("")
51
+ ctx.textual.error_text(f"Failed to pull: {str(e)}")
52
+ ctx.textual.end_step("error")
53
+ return Error(f"Failed to pull: {str(e)}")
54
+
55
+ ctx.textual.text("")
56
+ ctx.textual.end_step("success")
57
+ return Success(
58
+ f"Pulled from {remote}",
59
+ metadata={"remote": remote, "branch": branch}
60
+ )
61
+
62
+ except Exception as e:
63
+ ctx.textual.text("")
64
+ ctx.textual.error_text(f"Failed to pull: {str(e)}")
65
+ ctx.textual.text("")
66
+ ctx.textual.end_step("error")
67
+ return Error(f"Failed to pull: {str(e)}")
68
+
69
+
70
+ __all__ = ["pull_step"]
@@ -60,7 +60,7 @@ def create_git_push_step(ctx: WorkflowContext) -> WorkflowResult:
60
60
  if push_tags:
61
61
  success_msg += " (with tags)"
62
62
 
63
- ctx.textual.text(success_msg, markup="green")
63
+ ctx.textual.success_text(success_msg)
64
64
 
65
65
  ctx.textual.end_step("success")
66
66
  return Success(
@@ -69,11 +69,11 @@ def create_git_push_step(ctx: WorkflowContext) -> WorkflowResult:
69
69
  )
70
70
  except GitCommandError as e:
71
71
  error_msg = msg.Steps.Push.PUSH_FAILED.format(e=e)
72
- ctx.textual.text(error_msg, markup="red")
72
+ ctx.textual.error_text(error_msg)
73
73
  ctx.textual.end_step("error")
74
74
  return Error(error_msg)
75
75
  except Exception as e:
76
76
  error_msg = msg.Git.UNEXPECTED_ERROR.format(e=e)
77
- ctx.textual.text(error_msg, markup="red")
77
+ ctx.textual.error_text(error_msg)
78
78
  ctx.textual.end_step("error")
79
79
  return Error(error_msg)