titan-cli 0.1.2__tar.gz → 0.1.4__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 (145) hide show
  1. {titan_cli-0.1.2 → titan_cli-0.1.4}/PKG-INFO +1 -1
  2. {titan_cli-0.1.2 → titan_cli-0.1.4}/pyproject.toml +1 -1
  3. {titan_cli-0.1.2 → titan_cli-0.1.4}/titan_cli/cli.py +15 -4
  4. {titan_cli-0.1.2 → titan_cli-0.1.4}/titan_cli/core/plugins/models.py +35 -7
  5. {titan_cli-0.1.2 → titan_cli-0.1.4}/titan_cli/core/plugins/plugin_registry.py +17 -2
  6. {titan_cli-0.1.2 → titan_cli-0.1.4}/titan_cli/ui/tui/icons.py +3 -2
  7. {titan_cli-0.1.2 → titan_cli-0.1.4}/titan_cli/ui/tui/screens/ai_config.py +1 -1
  8. {titan_cli-0.1.2 → titan_cli-0.1.4}/titan_cli/ui/tui/screens/ai_config_wizard.py +1 -1
  9. {titan_cli-0.1.2 → titan_cli-0.1.4}/titan_cli/ui/tui/screens/main_menu.py +1 -1
  10. {titan_cli-0.1.2 → titan_cli-0.1.4}/titan_cli/ui/tui/screens/plugin_config_wizard.py +40 -9
  11. {titan_cli-0.1.2 → titan_cli-0.1.4}/titan_cli/ui/tui/widgets/status_bar.py +1 -1
  12. {titan_cli-0.1.2 → titan_cli-0.1.4}/LICENSE +0 -0
  13. {titan_cli-0.1.2 → titan_cli-0.1.4}/README.md +0 -0
  14. {titan_cli-0.1.2 → titan_cli-0.1.4}/plugins/titan-plugin-git/titan_plugin_git/__init__.py +0 -0
  15. {titan_cli-0.1.2 → titan_cli-0.1.4}/plugins/titan-plugin-git/titan_plugin_git/clients/__init__.py +0 -0
  16. {titan_cli-0.1.2 → titan_cli-0.1.4}/plugins/titan-plugin-git/titan_plugin_git/clients/git_client.py +0 -0
  17. {titan_cli-0.1.2 → titan_cli-0.1.4}/plugins/titan-plugin-git/titan_plugin_git/exceptions.py +0 -0
  18. {titan_cli-0.1.2 → titan_cli-0.1.4}/plugins/titan-plugin-git/titan_plugin_git/messages.py +0 -0
  19. {titan_cli-0.1.2 → titan_cli-0.1.4}/plugins/titan-plugin-git/titan_plugin_git/models.py +0 -0
  20. {titan_cli-0.1.2 → titan_cli-0.1.4}/plugins/titan-plugin-git/titan_plugin_git/plugin.py +0 -0
  21. {titan_cli-0.1.2 → titan_cli-0.1.4}/plugins/titan-plugin-git/titan_plugin_git/steps/__init__.py +0 -0
  22. {titan_cli-0.1.2 → titan_cli-0.1.4}/plugins/titan-plugin-git/titan_plugin_git/steps/ai_commit_message_step.py +0 -0
  23. {titan_cli-0.1.2 → titan_cli-0.1.4}/plugins/titan-plugin-git/titan_plugin_git/steps/branch_steps.py +0 -0
  24. {titan_cli-0.1.2 → titan_cli-0.1.4}/plugins/titan-plugin-git/titan_plugin_git/steps/commit_step.py +0 -0
  25. {titan_cli-0.1.2 → titan_cli-0.1.4}/plugins/titan-plugin-git/titan_plugin_git/steps/push_step.py +0 -0
  26. {titan_cli-0.1.2 → titan_cli-0.1.4}/plugins/titan-plugin-git/titan_plugin_git/steps/status_step.py +0 -0
  27. {titan_cli-0.1.2 → titan_cli-0.1.4}/plugins/titan-plugin-git/titan_plugin_git/workflows/__previews__/__init__.py +0 -0
  28. {titan_cli-0.1.2 → titan_cli-0.1.4}/plugins/titan-plugin-git/titan_plugin_git/workflows/__previews__/commit_ai_preview.py +0 -0
  29. {titan_cli-0.1.2 → titan_cli-0.1.4}/plugins/titan-plugin-git/titan_plugin_git/workflows/commit-ai.yaml +0 -0
  30. {titan_cli-0.1.2 → titan_cli-0.1.4}/plugins/titan-plugin-github/titan_plugin_github/__init__.py +0 -0
  31. {titan_cli-0.1.2 → titan_cli-0.1.4}/plugins/titan-plugin-github/titan_plugin_github/agents/__init__.py +0 -0
  32. {titan_cli-0.1.2 → titan_cli-0.1.4}/plugins/titan-plugin-github/titan_plugin_github/agents/config_loader.py +0 -0
  33. {titan_cli-0.1.2 → titan_cli-0.1.4}/plugins/titan-plugin-github/titan_plugin_github/agents/issue_generator.py +0 -0
  34. {titan_cli-0.1.2 → titan_cli-0.1.4}/plugins/titan-plugin-github/titan_plugin_github/agents/pr_agent.py +0 -0
  35. {titan_cli-0.1.2 → titan_cli-0.1.4}/plugins/titan-plugin-github/titan_plugin_github/clients/__init__.py +0 -0
  36. {titan_cli-0.1.2 → titan_cli-0.1.4}/plugins/titan-plugin-github/titan_plugin_github/clients/github_client.py +0 -0
  37. {titan_cli-0.1.2 → titan_cli-0.1.4}/plugins/titan-plugin-github/titan_plugin_github/config/__init__.py +0 -0
  38. {titan_cli-0.1.2 → titan_cli-0.1.4}/plugins/titan-plugin-github/titan_plugin_github/config/pr_agent.toml +0 -0
  39. {titan_cli-0.1.2 → titan_cli-0.1.4}/plugins/titan-plugin-github/titan_plugin_github/exceptions.py +0 -0
  40. {titan_cli-0.1.2 → titan_cli-0.1.4}/plugins/titan-plugin-github/titan_plugin_github/messages.py +0 -0
  41. {titan_cli-0.1.2 → titan_cli-0.1.4}/plugins/titan-plugin-github/titan_plugin_github/models.py +0 -0
  42. {titan_cli-0.1.2 → titan_cli-0.1.4}/plugins/titan-plugin-github/titan_plugin_github/plugin.py +0 -0
  43. {titan_cli-0.1.2 → titan_cli-0.1.4}/plugins/titan-plugin-github/titan_plugin_github/steps/__init__.py +0 -0
  44. {titan_cli-0.1.2 → titan_cli-0.1.4}/plugins/titan-plugin-github/titan_plugin_github/steps/ai_pr_step.py +0 -0
  45. {titan_cli-0.1.2 → titan_cli-0.1.4}/plugins/titan-plugin-github/titan_plugin_github/steps/create_pr_step.py +0 -0
  46. {titan_cli-0.1.2 → titan_cli-0.1.4}/plugins/titan-plugin-github/titan_plugin_github/steps/github_prompt_steps.py +0 -0
  47. {titan_cli-0.1.2 → titan_cli-0.1.4}/plugins/titan-plugin-github/titan_plugin_github/steps/issue_steps.py +0 -0
  48. {titan_cli-0.1.2 → titan_cli-0.1.4}/plugins/titan-plugin-github/titan_plugin_github/steps/preview_step.py +0 -0
  49. {titan_cli-0.1.2 → titan_cli-0.1.4}/plugins/titan-plugin-github/titan_plugin_github/utils.py +0 -0
  50. {titan_cli-0.1.2 → titan_cli-0.1.4}/plugins/titan-plugin-github/titan_plugin_github/workflows/__previews__/__init__.py +0 -0
  51. {titan_cli-0.1.2 → titan_cli-0.1.4}/plugins/titan-plugin-github/titan_plugin_github/workflows/__previews__/create_pr_ai_preview.py +0 -0
  52. {titan_cli-0.1.2 → titan_cli-0.1.4}/plugins/titan-plugin-github/titan_plugin_github/workflows/create-issue-ai.yaml +0 -0
  53. {titan_cli-0.1.2 → titan_cli-0.1.4}/plugins/titan-plugin-github/titan_plugin_github/workflows/create-pr-ai.yaml +0 -0
  54. {titan_cli-0.1.2 → titan_cli-0.1.4}/plugins/titan-plugin-jira/titan_plugin_jira/__init__.py +0 -0
  55. {titan_cli-0.1.2 → titan_cli-0.1.4}/plugins/titan-plugin-jira/titan_plugin_jira/agents/__init__.py +0 -0
  56. {titan_cli-0.1.2 → titan_cli-0.1.4}/plugins/titan-plugin-jira/titan_plugin_jira/agents/config_loader.py +0 -0
  57. {titan_cli-0.1.2 → titan_cli-0.1.4}/plugins/titan-plugin-jira/titan_plugin_jira/agents/jira_agent.py +0 -0
  58. {titan_cli-0.1.2 → titan_cli-0.1.4}/plugins/titan-plugin-jira/titan_plugin_jira/agents/prompts.py +0 -0
  59. {titan_cli-0.1.2 → titan_cli-0.1.4}/plugins/titan-plugin-jira/titan_plugin_jira/agents/response_parser.py +0 -0
  60. {titan_cli-0.1.2 → titan_cli-0.1.4}/plugins/titan-plugin-jira/titan_plugin_jira/agents/token_tracker.py +0 -0
  61. {titan_cli-0.1.2 → titan_cli-0.1.4}/plugins/titan-plugin-jira/titan_plugin_jira/agents/validators.py +0 -0
  62. {titan_cli-0.1.2 → titan_cli-0.1.4}/plugins/titan-plugin-jira/titan_plugin_jira/clients/jira_client.py +0 -0
  63. {titan_cli-0.1.2 → titan_cli-0.1.4}/plugins/titan-plugin-jira/titan_plugin_jira/config/jira_agent.toml +0 -0
  64. {titan_cli-0.1.2 → titan_cli-0.1.4}/plugins/titan-plugin-jira/titan_plugin_jira/config/templates/issue_analysis.md.j2 +0 -0
  65. {titan_cli-0.1.2 → titan_cli-0.1.4}/plugins/titan-plugin-jira/titan_plugin_jira/exceptions.py +0 -0
  66. {titan_cli-0.1.2 → titan_cli-0.1.4}/plugins/titan-plugin-jira/titan_plugin_jira/formatters/__init__.py +0 -0
  67. {titan_cli-0.1.2 → titan_cli-0.1.4}/plugins/titan-plugin-jira/titan_plugin_jira/formatters/markdown_formatter.py +0 -0
  68. {titan_cli-0.1.2 → titan_cli-0.1.4}/plugins/titan-plugin-jira/titan_plugin_jira/messages.py +0 -0
  69. {titan_cli-0.1.2 → titan_cli-0.1.4}/plugins/titan-plugin-jira/titan_plugin_jira/models.py +0 -0
  70. {titan_cli-0.1.2 → titan_cli-0.1.4}/plugins/titan-plugin-jira/titan_plugin_jira/plugin.py +0 -0
  71. {titan_cli-0.1.2 → titan_cli-0.1.4}/plugins/titan-plugin-jira/titan_plugin_jira/steps/ai_analyze_issue_step.py +0 -0
  72. {titan_cli-0.1.2 → titan_cli-0.1.4}/plugins/titan-plugin-jira/titan_plugin_jira/steps/get_issue_step.py +0 -0
  73. {titan_cli-0.1.2 → titan_cli-0.1.4}/plugins/titan-plugin-jira/titan_plugin_jira/steps/prompt_select_issue_step.py +0 -0
  74. {titan_cli-0.1.2 → titan_cli-0.1.4}/plugins/titan-plugin-jira/titan_plugin_jira/steps/search_saved_query_step.py +0 -0
  75. {titan_cli-0.1.2 → titan_cli-0.1.4}/plugins/titan-plugin-jira/titan_plugin_jira/utils/__init__.py +0 -0
  76. {titan_cli-0.1.2 → titan_cli-0.1.4}/plugins/titan-plugin-jira/titan_plugin_jira/utils/issue_sorter.py +0 -0
  77. {titan_cli-0.1.2 → titan_cli-0.1.4}/plugins/titan-plugin-jira/titan_plugin_jira/utils/saved_queries.py +0 -0
  78. {titan_cli-0.1.2 → titan_cli-0.1.4}/plugins/titan-plugin-jira/titan_plugin_jira/workflows/analyze-jira-issues.yaml +0 -0
  79. {titan_cli-0.1.2 → titan_cli-0.1.4}/titan_cli/__init__.py +0 -0
  80. {titan_cli-0.1.2 → titan_cli-0.1.4}/titan_cli/__main__.py +0 -0
  81. {titan_cli-0.1.2 → titan_cli-0.1.4}/titan_cli/ai/__init__.py +0 -0
  82. {titan_cli-0.1.2 → titan_cli-0.1.4}/titan_cli/ai/agents/__init__.py +0 -0
  83. {titan_cli-0.1.2 → titan_cli-0.1.4}/titan_cli/ai/agents/base.py +0 -0
  84. {titan_cli-0.1.2 → titan_cli-0.1.4}/titan_cli/ai/client.py +0 -0
  85. {titan_cli-0.1.2 → titan_cli-0.1.4}/titan_cli/ai/constants.py +0 -0
  86. {titan_cli-0.1.2 → titan_cli-0.1.4}/titan_cli/ai/exceptions.py +0 -0
  87. {titan_cli-0.1.2 → titan_cli-0.1.4}/titan_cli/ai/models.py +0 -0
  88. {titan_cli-0.1.2 → titan_cli-0.1.4}/titan_cli/ai/oauth_helper.py +0 -0
  89. {titan_cli-0.1.2 → titan_cli-0.1.4}/titan_cli/ai/providers/__init__.py +0 -0
  90. {titan_cli-0.1.2 → titan_cli-0.1.4}/titan_cli/ai/providers/anthropic.py +0 -0
  91. {titan_cli-0.1.2 → titan_cli-0.1.4}/titan_cli/ai/providers/base.py +0 -0
  92. {titan_cli-0.1.2 → titan_cli-0.1.4}/titan_cli/ai/providers/gemini.py +0 -0
  93. {titan_cli-0.1.2 → titan_cli-0.1.4}/titan_cli/clients/__init__.py +0 -0
  94. {titan_cli-0.1.2 → titan_cli-0.1.4}/titan_cli/clients/gcloud_client.py +0 -0
  95. {titan_cli-0.1.2 → titan_cli-0.1.4}/titan_cli/core/__init__.py +0 -0
  96. {titan_cli-0.1.2 → titan_cli-0.1.4}/titan_cli/core/config.py +0 -0
  97. {titan_cli-0.1.2 → titan_cli-0.1.4}/titan_cli/core/discovery.py +0 -0
  98. {titan_cli-0.1.2 → titan_cli-0.1.4}/titan_cli/core/errors.py +0 -0
  99. {titan_cli-0.1.2 → titan_cli-0.1.4}/titan_cli/core/models.py +0 -0
  100. {titan_cli-0.1.2 → titan_cli-0.1.4}/titan_cli/core/plugins/available.py +0 -0
  101. {titan_cli-0.1.2 → titan_cli-0.1.4}/titan_cli/core/plugins/plugin_base.py +0 -0
  102. {titan_cli-0.1.2 → titan_cli-0.1.4}/titan_cli/core/secrets.py +0 -0
  103. {titan_cli-0.1.2 → titan_cli-0.1.4}/titan_cli/core/workflows/__init__.py +0 -0
  104. {titan_cli-0.1.2 → titan_cli-0.1.4}/titan_cli/core/workflows/models.py +0 -0
  105. {titan_cli-0.1.2 → titan_cli-0.1.4}/titan_cli/core/workflows/project_step_source.py +0 -0
  106. {titan_cli-0.1.2 → titan_cli-0.1.4}/titan_cli/core/workflows/workflow_exceptions.py +0 -0
  107. {titan_cli-0.1.2 → titan_cli-0.1.4}/titan_cli/core/workflows/workflow_filter_service.py +0 -0
  108. {titan_cli-0.1.2 → titan_cli-0.1.4}/titan_cli/core/workflows/workflow_registry.py +0 -0
  109. {titan_cli-0.1.2 → titan_cli-0.1.4}/titan_cli/core/workflows/workflow_sources.py +0 -0
  110. {titan_cli-0.1.2 → titan_cli-0.1.4}/titan_cli/engine/__init__.py +0 -0
  111. {titan_cli-0.1.2 → titan_cli-0.1.4}/titan_cli/engine/builder.py +0 -0
  112. {titan_cli-0.1.2 → titan_cli-0.1.4}/titan_cli/engine/context.py +0 -0
  113. {titan_cli-0.1.2 → titan_cli-0.1.4}/titan_cli/engine/mock_context.py +0 -0
  114. {titan_cli-0.1.2 → titan_cli-0.1.4}/titan_cli/engine/results.py +0 -0
  115. {titan_cli-0.1.2 → titan_cli-0.1.4}/titan_cli/engine/steps/ai_assistant_step.py +0 -0
  116. {titan_cli-0.1.2 → titan_cli-0.1.4}/titan_cli/engine/steps/command_step.py +0 -0
  117. {titan_cli-0.1.2 → titan_cli-0.1.4}/titan_cli/engine/utils/__init__.py +0 -0
  118. {titan_cli-0.1.2 → titan_cli-0.1.4}/titan_cli/engine/utils/venv.py +0 -0
  119. {titan_cli-0.1.2 → titan_cli-0.1.4}/titan_cli/engine/workflow_executor.py +0 -0
  120. {titan_cli-0.1.2 → titan_cli-0.1.4}/titan_cli/external_cli/__init__.py +0 -0
  121. {titan_cli-0.1.2 → titan_cli-0.1.4}/titan_cli/external_cli/configs.py +0 -0
  122. {titan_cli-0.1.2 → titan_cli-0.1.4}/titan_cli/external_cli/launcher.py +0 -0
  123. {titan_cli-0.1.2 → titan_cli-0.1.4}/titan_cli/messages.py +0 -0
  124. {titan_cli-0.1.2 → titan_cli-0.1.4}/titan_cli/ui/tui/__init__.py +0 -0
  125. {titan_cli-0.1.2 → titan_cli-0.1.4}/titan_cli/ui/tui/__previews__/statusbar_preview.py +0 -0
  126. {titan_cli-0.1.2 → titan_cli-0.1.4}/titan_cli/ui/tui/app.py +0 -0
  127. {titan_cli-0.1.2 → titan_cli-0.1.4}/titan_cli/ui/tui/screens/__init__.py +0 -0
  128. {titan_cli-0.1.2 → titan_cli-0.1.4}/titan_cli/ui/tui/screens/base.py +0 -0
  129. {titan_cli-0.1.2 → titan_cli-0.1.4}/titan_cli/ui/tui/screens/cli_launcher.py +0 -0
  130. {titan_cli-0.1.2 → titan_cli-0.1.4}/titan_cli/ui/tui/screens/global_setup_wizard.py +0 -0
  131. {titan_cli-0.1.2 → titan_cli-0.1.4}/titan_cli/ui/tui/screens/plugin_management.py +0 -0
  132. {titan_cli-0.1.2 → titan_cli-0.1.4}/titan_cli/ui/tui/screens/project_setup_wizard.py +0 -0
  133. {titan_cli-0.1.2 → titan_cli-0.1.4}/titan_cli/ui/tui/screens/workflow_execution.py +0 -0
  134. {titan_cli-0.1.2 → titan_cli-0.1.4}/titan_cli/ui/tui/screens/workflows.py +0 -0
  135. {titan_cli-0.1.2 → titan_cli-0.1.4}/titan_cli/ui/tui/textual_components.py +0 -0
  136. {titan_cli-0.1.2 → titan_cli-0.1.4}/titan_cli/ui/tui/textual_workflow_executor.py +0 -0
  137. {titan_cli-0.1.2 → titan_cli-0.1.4}/titan_cli/ui/tui/theme.py +0 -0
  138. {titan_cli-0.1.2 → titan_cli-0.1.4}/titan_cli/ui/tui/widgets/__init__.py +0 -0
  139. {titan_cli-0.1.2 → titan_cli-0.1.4}/titan_cli/ui/tui/widgets/button.py +0 -0
  140. {titan_cli-0.1.2 → titan_cli-0.1.4}/titan_cli/ui/tui/widgets/header.py +0 -0
  141. {titan_cli-0.1.2 → titan_cli-0.1.4}/titan_cli/ui/tui/widgets/panel.py +0 -0
  142. {titan_cli-0.1.2 → titan_cli-0.1.4}/titan_cli/ui/tui/widgets/table.py +0 -0
  143. {titan_cli-0.1.2 → titan_cli-0.1.4}/titan_cli/ui/tui/widgets/text.py +0 -0
  144. {titan_cli-0.1.2 → titan_cli-0.1.4}/titan_cli/utils/__init__.py +0 -0
  145. {titan_cli-0.1.2 → titan_cli-0.1.4}/titan_cli/utils/autoupdate.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: titan-cli
3
- Version: 0.1.2
3
+ Version: 0.1.4
4
4
  Summary: Modular development tools orchestrator - Streamline your workflows with AI integration and intuitive terminal UI
5
5
  Home-page: https://github.com/masmovil/titan-cli
6
6
  License: MIT
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "titan-cli"
3
- version = "0.1.2"
3
+ version = "0.1.4"
4
4
  description = "Modular development tools orchestrator - Streamline your workflows with AI integration and intuitive terminal UI"
5
5
  authors = ["finxo <finxeto@gmail.com>", "r-pedraza <raulpedrazaleon@gmail.com>"]
6
6
  maintainers = ["r-pedraza <raulpedrazaleon@gmail.com>"]
@@ -3,6 +3,8 @@ Titan CLI - Main CLI application
3
3
 
4
4
  Combines all tool commands into a single CLI interface.
5
5
  """
6
+ import subprocess
7
+ import sys
6
8
  import typer
7
9
 
8
10
  from titan_cli import __version__
@@ -31,7 +33,7 @@ def get_version() -> str:
31
33
  def main(ctx: typer.Context):
32
34
  """Titan CLI - Main entry point"""
33
35
  if ctx.invoked_subcommand is None:
34
- # Check for updates (non-blocking, silent on errors)
36
+ # Check for updates BEFORE launching TUI
35
37
  try:
36
38
  update_info = check_for_updates()
37
39
  if update_info["update_available"]:
@@ -44,13 +46,22 @@ def main(ctx: typer.Context):
44
46
  # Ask user if they want to update
45
47
  if typer.confirm("Would you like to update now?", default=True):
46
48
  typer.echo("⏳ Updating Titan CLI...")
49
+ typer.echo()
47
50
  result = perform_update()
48
51
 
49
52
  if result["success"]:
50
53
  typer.echo(f"✅ Successfully updated to v{latest} using {result['method']}")
51
- typer.echo("🔄 Please restart Titan to use the new version")
54
+ typer.echo("🔄 Relaunching Titan with new version...")
52
55
  typer.echo()
53
- # Exit so user can restart
56
+
57
+ # Relaunch titan using subprocess
58
+ # Note: sys.executable and sys.argv are controlled by the Python runtime,
59
+ # not user input, so this is safe from command injection
60
+ subprocess.run(
61
+ [sys.executable, "-m", "titan_cli.cli"] + sys.argv[1:],
62
+ shell=False, # Explicitly disable shell to prevent injection
63
+ check=False # Don't raise on non-zero exit
64
+ )
54
65
  raise typer.Exit(0)
55
66
  else:
56
67
  typer.echo(f"❌ Update failed: {result['error']}")
@@ -64,7 +75,7 @@ def main(ctx: typer.Context):
64
75
  # Silently ignore update check failures
65
76
  pass
66
77
 
67
- # Launch TUI by default
78
+ # Launch TUI (only if no update or update was declined/failed)
68
79
  launch_tui()
69
80
 
70
81
 
@@ -30,15 +30,43 @@ class JiraPluginConfig(BaseModel):
30
30
  Credentials (base_url, email, api_token) should be configured at global level (~/.titan/config.toml).
31
31
  Project-specific settings (default_project) can override at project level (.titan/config.toml).
32
32
  """
33
- base_url: Optional[str] = Field(None, description="JIRA instance URL (e.g., 'https://jira.company.com')")
34
- email: Optional[str] = Field(None, description="User email for authentication")
33
+ base_url: Optional[str] = Field(
34
+ None,
35
+ description="JIRA instance URL (e.g., 'https://jira.company.com')",
36
+ json_schema_extra={"config_scope": "global"}
37
+ )
38
+ email: Optional[str] = Field(
39
+ None,
40
+ description="User email for authentication",
41
+ json_schema_extra={"config_scope": "global"}
42
+ )
35
43
  # api_token is stored in secrets, not in config.toml
36
44
  # It appears in the JSON schema for interactive configuration but is optional in the model
37
- api_token: Optional[str] = Field(None, description="JIRA API token (Personal Access Token)", json_schema_extra={"format": "password", "required_in_schema": True})
38
- default_project: Optional[str] = Field(None, description="Default JIRA project key (e.g., 'ECAPP', 'PROJ')")
39
- timeout: int = Field(30, description="Request timeout in seconds")
40
- enable_cache: bool = Field(True, description="Enable caching for API responses")
41
- cache_ttl: int = Field(300, description="Cache time-to-live in seconds")
45
+ api_token: Optional[str] = Field(
46
+ None,
47
+ description="JIRA API token (Personal Access Token)",
48
+ json_schema_extra={"format": "password", "required_in_schema": True}
49
+ )
50
+ default_project: Optional[str] = Field(
51
+ None,
52
+ description="Default JIRA project key (e.g., 'ECAPP', 'PROJ')",
53
+ json_schema_extra={"config_scope": "project"}
54
+ )
55
+ timeout: int = Field(
56
+ 30,
57
+ description="Request timeout in seconds",
58
+ json_schema_extra={"config_scope": "global"}
59
+ )
60
+ enable_cache: bool = Field(
61
+ True,
62
+ description="Enable caching for API responses",
63
+ json_schema_extra={"config_scope": "global"}
64
+ )
65
+ cache_ttl: int = Field(
66
+ 300,
67
+ description="Cache time-to-live in seconds",
68
+ json_schema_extra={"config_scope": "global"}
69
+ )
42
70
 
43
71
  @field_validator('base_url')
44
72
  @classmethod
@@ -20,10 +20,19 @@ class PluginRegistry:
20
20
  logger = logging.getLogger('titan_cli.ui.tui.screens.project_setup_wizard')
21
21
 
22
22
  discovered = entry_points(group='titan.plugins')
23
- self._discovered_plugin_names = [ep.name for ep in discovered]
24
- logger.debug(f"PluginRegistry.discover() - Found {len(self._discovered_plugin_names)} plugins: {self._discovered_plugin_names}")
25
23
 
24
+ # Deduplicate entry points (can happen in dev mode with editable installs)
25
+ seen = {}
26
+ unique_eps = []
26
27
  for ep in discovered:
28
+ if ep.name not in seen:
29
+ seen[ep.name] = ep
30
+ unique_eps.append(ep)
31
+
32
+ self._discovered_plugin_names = [ep.name for ep in unique_eps]
33
+ logger.debug(f"PluginRegistry.discover() - Found {len(self._discovered_plugin_names)} plugins: {self._discovered_plugin_names}")
34
+
35
+ for ep in unique_eps:
27
36
  try:
28
37
  logger.debug(f"Loading plugin: {ep.name}")
29
38
  plugin_class = ep.load()
@@ -63,6 +72,12 @@ class PluginRegistry:
63
72
  if name in initialized:
64
73
  continue
65
74
 
75
+ # Skip plugins that are disabled in configuration
76
+ if not config.is_plugin_enabled(name):
77
+ logger.debug(f"Skipping disabled plugin: {name}")
78
+ initialized.add(name) # Mark as processed so we don't retry
79
+ continue
80
+
66
81
  plugin = self._plugins[name]
67
82
  dependencies_met = True
68
83
 
@@ -17,8 +17,8 @@ class Icons:
17
17
  # Status indicators
18
18
  SUCCESS = "✅"
19
19
  ERROR = "❌"
20
- WARNING = ""
21
- INFO = ""
20
+ WARNING = "🟡"
21
+ INFO = "🔵"
22
22
  QUESTION = "❓"
23
23
 
24
24
  # Progress states
@@ -56,6 +56,7 @@ class Icons:
56
56
 
57
57
  # AI & Automation
58
58
  AI = "🤖"
59
+ AI_CONFIG = "🧠"
59
60
  ROBOT = "🤖"
60
61
  SPARKLES = "✨"
61
62
 
@@ -282,7 +282,7 @@ class AIConfigScreen(BaseScreen):
282
282
  def __init__(self, config):
283
283
  super().__init__(
284
284
  config,
285
- title=f"{Icons.SETTINGS} AI Configuration",
285
+ title=f"{Icons.AI_CONFIG} AI Configuration",
286
286
  show_back=True
287
287
  )
288
288
 
@@ -152,7 +152,7 @@ class AIConfigWizardScreen(BaseScreen):
152
152
  def __init__(self, config):
153
153
  super().__init__(
154
154
  config,
155
- title=f"{Icons.SETTINGS} Configure AI Provider",
155
+ title=f"{Icons.AI_CONFIG} Configure AI Provider",
156
156
  show_back=True,
157
157
  show_status_bar=False
158
158
  )
@@ -108,7 +108,7 @@ class MainMenuScreen(BaseScreen):
108
108
  options.extend(
109
109
  [
110
110
  Option(f"{Icons.PLUGIN} Plugin Management", id="plugin_management"),
111
- Option(f"{Icons.SETTINGS} AI Configuration", id="ai_config"),
111
+ Option(f"{Icons.AI_CONFIG} AI Configuration", id="ai_config"),
112
112
  ]
113
113
  )
114
114
 
@@ -494,22 +494,43 @@ class PluginConfigWizardScreen(BaseScreen):
494
494
 
495
495
  try:
496
496
  project_cfg_path = self.config.project_config_path
497
+ global_cfg_path = self.config._global_config_path
498
+
497
499
  if not project_cfg_path:
498
500
  self.app.notify("No project config found", severity="error")
499
501
  return
500
502
 
503
+ # Load existing configs
501
504
  project_cfg_dict = {}
502
505
  if project_cfg_path.exists():
503
506
  with open(project_cfg_path, "rb") as f:
504
507
  project_cfg_dict = tomli.load(f)
505
508
 
506
- plugins_table = project_cfg_dict.setdefault("plugins", {})
507
- plugin_specific_table = plugins_table.setdefault(self.plugin_name, {})
508
- plugin_config_table = plugin_specific_table.setdefault("config", {})
509
+ global_cfg_dict = {}
510
+ if global_cfg_path.exists():
511
+ with open(global_cfg_path, "rb") as f:
512
+ global_cfg_dict = tomli.load(f)
513
+
514
+ # Prepare plugin tables
515
+ project_plugins_table = project_cfg_dict.setdefault("plugins", {})
516
+ project_plugin_table = project_plugins_table.setdefault(self.plugin_name, {})
517
+ project_config_table = project_plugin_table.setdefault("config", {})
518
+
519
+ global_plugins_table = global_cfg_dict.setdefault("plugins", {})
520
+ global_plugin_table = global_plugins_table.setdefault(self.plugin_name, {})
521
+ global_config_table = global_plugin_table.setdefault("config", {})
509
522
 
510
- # Separate secrets from regular config
523
+ # Get field metadata from schema
524
+ field_scopes = {}
525
+ if self.schema and "properties" in self.schema:
526
+ for field_name, field_info in self.schema["properties"].items():
527
+ scope = field_info.get("config_scope", "project") # Default to project
528
+ field_scopes[field_name] = scope
529
+
530
+ # Separate secrets and config by scope
511
531
  secrets_to_save = {}
512
- config_values = {}
532
+ global_config_values = {}
533
+ project_config_values = {}
513
534
 
514
535
  for field_name, value in self.config_data.items():
515
536
  if isinstance(value, dict) and value.get("_is_secret"):
@@ -524,15 +545,25 @@ class PluginConfigWizardScreen(BaseScreen):
524
545
  else:
525
546
  secrets_to_save[secret_key] = value["_value"]
526
547
  else:
527
- config_values[field_name] = value
548
+ # Route to global or project based on field scope
549
+ scope = field_scopes.get(field_name, "project")
550
+ if scope == "global":
551
+ global_config_values[field_name] = value
552
+ else:
553
+ project_config_values[field_name] = value
528
554
 
529
- # Update config
530
- plugin_config_table.update(config_values)
555
+ # Update configs
556
+ project_config_table.update(project_config_values)
557
+ global_config_table.update(global_config_values)
531
558
 
532
- # Write config file
559
+ # Write config files
533
560
  with open(project_cfg_path, "wb") as f:
534
561
  tomli_w.dump(project_cfg_dict, f)
535
562
 
563
+ if global_config_values: # Only write global if there are global values
564
+ with open(global_cfg_path, "wb") as f:
565
+ tomli_w.dump(global_cfg_dict, f)
566
+
536
567
  # Save secrets
537
568
  project_name = self.config.get_project_name()
538
569
  for secret_key, secret_value in secrets_to_save.items():
@@ -47,7 +47,7 @@ class StatusBarWidget(Widget):
47
47
 
48
48
  StatusBarWidget #branch-info {
49
49
  text-align: left;
50
- color: blue;
50
+ color: cyan;
51
51
  }
52
52
 
53
53
  StatusBarWidget #ai-info {
File without changes
File without changes