shotgun-sh 0.2.1.dev3__tar.gz → 0.2.1.dev4__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.

Potentially problematic release.


This version of shotgun-sh might be problematic. Click here for more details.

Files changed (148) hide show
  1. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/PKG-INFO +1 -1
  2. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/pyproject.toml +1 -1
  3. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/agents/config/manager.py +22 -6
  4. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/agents/config/provider.py +2 -1
  5. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/tui/app.py +5 -3
  6. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/tui/screens/chat_screen/command_providers.py +6 -4
  7. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/tui/screens/model_picker.py +92 -17
  8. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/tui/screens/provider_config.py +7 -0
  9. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/.gitignore +0 -0
  10. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/LICENSE +0 -0
  11. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/README.md +0 -0
  12. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/hatch_build.py +0 -0
  13. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/__init__.py +0 -0
  14. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/agents/__init__.py +0 -0
  15. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/agents/agent_manager.py +0 -0
  16. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/agents/common.py +0 -0
  17. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/agents/config/__init__.py +0 -0
  18. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/agents/config/constants.py +0 -0
  19. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/agents/config/models.py +0 -0
  20. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/agents/conversation_history.py +0 -0
  21. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/agents/conversation_manager.py +0 -0
  22. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/agents/export.py +0 -0
  23. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/agents/history/__init__.py +0 -0
  24. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/agents/history/compaction.py +0 -0
  25. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/agents/history/constants.py +0 -0
  26. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/agents/history/context_extraction.py +0 -0
  27. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/agents/history/history_building.py +0 -0
  28. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/agents/history/history_processors.py +0 -0
  29. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/agents/history/message_utils.py +0 -0
  30. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/agents/history/token_counting/__init__.py +0 -0
  31. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/agents/history/token_counting/anthropic.py +0 -0
  32. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/agents/history/token_counting/base.py +0 -0
  33. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/agents/history/token_counting/openai.py +0 -0
  34. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/agents/history/token_counting/sentencepiece_counter.py +0 -0
  35. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/agents/history/token_counting/tokenizer_cache.py +0 -0
  36. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/agents/history/token_counting/utils.py +0 -0
  37. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/agents/history/token_estimation.py +0 -0
  38. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/agents/llm.py +0 -0
  39. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/agents/messages.py +0 -0
  40. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/agents/models.py +0 -0
  41. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/agents/plan.py +0 -0
  42. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/agents/research.py +0 -0
  43. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/agents/specify.py +0 -0
  44. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/agents/tasks.py +0 -0
  45. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/agents/tools/__init__.py +0 -0
  46. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/agents/tools/codebase/__init__.py +0 -0
  47. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/agents/tools/codebase/codebase_shell.py +0 -0
  48. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/agents/tools/codebase/directory_lister.py +0 -0
  49. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/agents/tools/codebase/file_read.py +0 -0
  50. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/agents/tools/codebase/models.py +0 -0
  51. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/agents/tools/codebase/query_graph.py +0 -0
  52. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/agents/tools/codebase/retrieve_code.py +0 -0
  53. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/agents/tools/file_management.py +0 -0
  54. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/agents/tools/user_interaction.py +0 -0
  55. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/agents/tools/web_search/__init__.py +0 -0
  56. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/agents/tools/web_search/anthropic.py +0 -0
  57. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/agents/tools/web_search/gemini.py +0 -0
  58. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/agents/tools/web_search/openai.py +0 -0
  59. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/agents/tools/web_search/utils.py +0 -0
  60. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/agents/usage_manager.py +0 -0
  61. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/build_constants.py +0 -0
  62. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/cli/__init__.py +0 -0
  63. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/cli/codebase/__init__.py +0 -0
  64. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/cli/codebase/commands.py +0 -0
  65. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/cli/codebase/models.py +0 -0
  66. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/cli/config.py +0 -0
  67. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/cli/export.py +0 -0
  68. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/cli/feedback.py +0 -0
  69. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/cli/models.py +0 -0
  70. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/cli/plan.py +0 -0
  71. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/cli/research.py +0 -0
  72. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/cli/specify.py +0 -0
  73. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/cli/tasks.py +0 -0
  74. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/cli/update.py +0 -0
  75. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/cli/utils.py +0 -0
  76. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/codebase/__init__.py +0 -0
  77. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/codebase/core/__init__.py +0 -0
  78. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/codebase/core/change_detector.py +0 -0
  79. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/codebase/core/code_retrieval.py +0 -0
  80. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/codebase/core/cypher_models.py +0 -0
  81. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/codebase/core/ingestor.py +0 -0
  82. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/codebase/core/language_config.py +0 -0
  83. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/codebase/core/manager.py +0 -0
  84. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/codebase/core/nl_query.py +0 -0
  85. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/codebase/core/parser_loader.py +0 -0
  86. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/codebase/models.py +0 -0
  87. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/codebase/service.py +0 -0
  88. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/llm_proxy/__init__.py +0 -0
  89. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/llm_proxy/clients.py +0 -0
  90. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/llm_proxy/constants.py +0 -0
  91. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/logging_config.py +0 -0
  92. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/main.py +0 -0
  93. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/posthog_telemetry.py +0 -0
  94. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/prompts/__init__.py +0 -0
  95. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/prompts/agents/__init__.py +0 -0
  96. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/prompts/agents/export.j2 +0 -0
  97. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/prompts/agents/partials/codebase_understanding.j2 +0 -0
  98. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/prompts/agents/partials/common_agent_system_prompt.j2 +0 -0
  99. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/prompts/agents/partials/content_formatting.j2 +0 -0
  100. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/prompts/agents/partials/interactive_mode.j2 +0 -0
  101. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/prompts/agents/plan.j2 +0 -0
  102. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/prompts/agents/research.j2 +0 -0
  103. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/prompts/agents/specify.j2 +0 -0
  104. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/prompts/agents/state/codebase/codebase_graphs_available.j2 +0 -0
  105. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/prompts/agents/state/system_state.j2 +0 -0
  106. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/prompts/agents/tasks.j2 +0 -0
  107. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/prompts/codebase/__init__.py +0 -0
  108. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/prompts/codebase/cypher_query_patterns.j2 +0 -0
  109. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/prompts/codebase/cypher_system.j2 +0 -0
  110. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/prompts/codebase/enhanced_query_context.j2 +0 -0
  111. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/prompts/codebase/partials/cypher_rules.j2 +0 -0
  112. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/prompts/codebase/partials/graph_schema.j2 +0 -0
  113. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/prompts/codebase/partials/temporal_context.j2 +0 -0
  114. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/prompts/history/__init__.py +0 -0
  115. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/prompts/history/incremental_summarization.j2 +0 -0
  116. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/prompts/history/summarization.j2 +0 -0
  117. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/prompts/loader.py +0 -0
  118. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/py.typed +0 -0
  119. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/sdk/__init__.py +0 -0
  120. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/sdk/codebase.py +0 -0
  121. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/sdk/exceptions.py +0 -0
  122. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/sdk/models.py +0 -0
  123. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/sdk/services.py +0 -0
  124. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/sentry_telemetry.py +0 -0
  125. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/telemetry.py +0 -0
  126. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/tui/__init__.py +0 -0
  127. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/tui/commands/__init__.py +0 -0
  128. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/tui/components/prompt_input.py +0 -0
  129. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/tui/components/spinner.py +0 -0
  130. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/tui/components/splash.py +0 -0
  131. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/tui/components/vertical_tail.py +0 -0
  132. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/tui/filtered_codebase_service.py +0 -0
  133. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/tui/screens/chat.py +0 -0
  134. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/tui/screens/chat.tcss +0 -0
  135. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/tui/screens/chat_screen/__init__.py +0 -0
  136. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/tui/screens/chat_screen/hint_message.py +0 -0
  137. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/tui/screens/chat_screen/history.py +0 -0
  138. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/tui/screens/directory_setup.py +0 -0
  139. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/tui/screens/feedback.py +0 -0
  140. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/tui/screens/splash.py +0 -0
  141. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/tui/styles.tcss +0 -0
  142. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/tui/utils/__init__.py +0 -0
  143. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/tui/utils/mode_progress.py +0 -0
  144. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/utils/__init__.py +0 -0
  145. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/utils/env_utils.py +0 -0
  146. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/utils/file_system_utils.py +0 -0
  147. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/utils/source_detection.py +0 -0
  148. {shotgun_sh-0.2.1.dev3 → shotgun_sh-0.2.1.dev4}/src/shotgun/utils/update_checker.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: shotgun-sh
3
- Version: 0.2.1.dev3
3
+ Version: 0.2.1.dev4
4
4
  Summary: AI-powered research, planning, and task management CLI tool
5
5
  Project-URL: Homepage, https://shotgun.sh/
6
6
  Project-URL: Repository, https://github.com/shotgun-sh/shotgun
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "shotgun-sh"
3
- version = "0.2.1.dev3"
3
+ version = "0.2.1.dev4"
4
4
  description = "AI-powered research, planning, and task management CLI tool"
5
5
  readme = "README.md"
6
6
  license = { text = "MIT" }
@@ -46,13 +46,16 @@ class ConfigManager:
46
46
 
47
47
  self._config: ShotgunConfig | None = None
48
48
 
49
- def load(self) -> ShotgunConfig:
49
+ def load(self, force_reload: bool = True) -> ShotgunConfig:
50
50
  """Load configuration from file.
51
51
 
52
+ Args:
53
+ force_reload: If True, reload from disk even if cached (default: True)
54
+
52
55
  Returns:
53
56
  ShotgunConfig: Loaded configuration or default config if file doesn't exist
54
57
  """
55
- if self._config is not None:
58
+ if self._config is not None and not force_reload:
56
59
  return self._config
57
60
 
58
61
  if not self.config_path.exists():
@@ -243,7 +246,8 @@ class ConfigManager:
243
246
 
244
247
  This checks only the configuration file.
245
248
  """
246
- config = self.load()
249
+ # Use force_reload=False to avoid infinite loop when called from load()
250
+ config = self.load(force_reload=False)
247
251
  provider_enum = self._ensure_provider_enum(provider)
248
252
  provider_config = self._get_provider_config(config, provider_enum)
249
253
 
@@ -251,7 +255,8 @@ class ConfigManager:
251
255
 
252
256
  def has_any_provider_key(self) -> bool:
253
257
  """Determine whether any provider has a configured API key."""
254
- config = self.load()
258
+ # Use force_reload=False to avoid infinite loop when called from load()
259
+ config = self.load(force_reload=False)
255
260
  # Check LLM provider keys (BYOK)
256
261
  has_llm_key = any(
257
262
  self._provider_has_api_key(self._get_provider_config(config, provider))
@@ -381,6 +386,17 @@ class ConfigManager:
381
386
  return config.user_id
382
387
 
383
388
 
389
+ # Global singleton instance
390
+ _config_manager_instance: ConfigManager | None = None
391
+
392
+
384
393
  def get_config_manager() -> ConfigManager:
385
- """Get the global ConfigManager instance."""
386
- return ConfigManager()
394
+ """Get the global singleton ConfigManager instance.
395
+
396
+ Returns:
397
+ The singleton ConfigManager instance
398
+ """
399
+ global _config_manager_instance
400
+ if _config_manager_instance is None:
401
+ _config_manager_instance = ConfigManager()
402
+ return _config_manager_instance
@@ -139,7 +139,8 @@ def get_provider_model(
139
139
  ValueError: If provider is not configured properly or model not found
140
140
  """
141
141
  config_manager = get_config_manager()
142
- config = config_manager.load()
142
+ # Use cached config for read-only access (performance)
143
+ config = config_manager.load(force_reload=False)
143
144
 
144
145
  # Priority 1: Check if Shotgun key exists - if so, use it for ANY model
145
146
  shotgun_api_key = _get_api_key(config.shotgun.api_key)
@@ -64,7 +64,8 @@ class ShotgunApp(App[None]):
64
64
  return
65
65
 
66
66
  self.push_screen(
67
- "provider_config", callback=lambda _arg: self.refresh_startup_screen()
67
+ ProviderConfigScreen(),
68
+ callback=lambda _arg: self.refresh_startup_screen(),
68
69
  )
69
70
  return
70
71
 
@@ -73,7 +74,8 @@ class ShotgunApp(App[None]):
73
74
  return
74
75
 
75
76
  self.push_screen(
76
- "directory_setup", callback=lambda _arg: self.refresh_startup_screen()
77
+ DirectorySetupScreen(),
78
+ callback=lambda _arg: self.refresh_startup_screen(),
77
79
  )
78
80
  return
79
81
 
@@ -110,7 +112,7 @@ class ShotgunApp(App[None]):
110
112
  submit_feedback_survey(feedback)
111
113
  self.notify("Feedback sent. Thank you!")
112
114
 
113
- self.push_screen("feedback", callback=handle_feedback)
115
+ self.push_screen(FeedbackScreen(), callback=handle_feedback)
114
116
 
115
117
 
116
118
  def run(no_update_check: bool = False, continue_session: bool = False) -> None:
@@ -5,6 +5,8 @@ from textual.command import DiscoveryHit, Hit, Provider
5
5
 
6
6
  from shotgun.agents.models import AgentType
7
7
  from shotgun.codebase.models import CodebaseGraph
8
+ from shotgun.tui.screens.model_picker import ModelPickerScreen
9
+ from shotgun.tui.screens.provider_config import ProviderConfigScreen
8
10
 
9
11
  if TYPE_CHECKING:
10
12
  from shotgun.tui.screens.chat import ChatScreen
@@ -139,11 +141,11 @@ class ProviderSetupProvider(Provider):
139
141
 
140
142
  def open_provider_config(self) -> None:
141
143
  """Show the provider configuration screen."""
142
- self.chat_screen.app.push_screen("provider_config")
144
+ self.chat_screen.app.push_screen(ProviderConfigScreen())
143
145
 
144
146
  def open_model_picker(self) -> None:
145
147
  """Show the model picker screen."""
146
- self.chat_screen.app.push_screen("model_picker")
148
+ self.chat_screen.app.push_screen(ModelPickerScreen())
147
149
 
148
150
  async def discover(self) -> AsyncGenerator[DiscoveryHit, None]:
149
151
  yield DiscoveryHit(
@@ -282,11 +284,11 @@ class UnifiedCommandProvider(Provider):
282
284
 
283
285
  def open_provider_config(self) -> None:
284
286
  """Show the provider configuration screen."""
285
- self.chat_screen.app.push_screen("provider_config")
287
+ self.chat_screen.app.push_screen(ProviderConfigScreen())
286
288
 
287
289
  def open_model_picker(self) -> None:
288
290
  """Show the model picker screen."""
289
- self.chat_screen.app.push_screen("model_picker")
291
+ self.chat_screen.app.push_screen(ModelPickerScreen())
290
292
 
291
293
  async def discover(self) -> AsyncGenerator[DiscoveryHit, None]:
292
294
  """Provide commands in alphabetical order when palette opens."""
@@ -12,11 +12,14 @@ from textual.screen import Screen
12
12
  from textual.widgets import Button, Label, ListItem, ListView, Static
13
13
 
14
14
  from shotgun.agents.config import ConfigManager
15
- from shotgun.agents.config.models import MODEL_SPECS, ModelName
15
+ from shotgun.agents.config.models import MODEL_SPECS, ModelName, ShotgunConfig
16
+ from shotgun.logging_config import get_logger
16
17
 
17
18
  if TYPE_CHECKING:
18
19
  from ..app import ShotgunApp
19
20
 
21
+ logger = get_logger(__name__)
22
+
20
23
 
21
24
  # Available models for selection
22
25
  AVAILABLE_MODELS = list(ModelName)
@@ -82,20 +85,52 @@ class ModelPickerScreen(Screen[None]):
82
85
  "Select the AI model you want to use for your tasks.",
83
86
  id="model-picker-summary",
84
87
  )
85
- yield ListView(*self._build_model_items(), id="model-list")
88
+ yield ListView(id="model-list")
86
89
  with Horizontal(id="model-actions"):
87
90
  yield Button("Select \\[ENTER]", variant="primary", id="select")
88
91
  yield Button("Done \\[ESC]", id="done")
89
92
 
90
- def on_mount(self) -> None:
91
- # Load current selection
93
+ def _rebuild_model_list(self) -> None:
94
+ """Rebuild the model list from current config.
95
+
96
+ This method is called both on first show and when screen is resumed
97
+ to ensure the list always reflects the current configuration.
98
+ """
99
+ logger.debug("Rebuilding model list from current config")
100
+
101
+ # Load current config with force_reload to get latest API keys
92
102
  config_manager = self.config_manager
93
- config = config_manager.load()
103
+ config = config_manager.load(force_reload=True)
104
+
105
+ # Log provider key status
106
+ logger.debug(
107
+ "Provider keys: openai=%s, anthropic=%s, google=%s, shotgun=%s",
108
+ config_manager._provider_has_api_key(config.openai),
109
+ config_manager._provider_has_api_key(config.anthropic),
110
+ config_manager._provider_has_api_key(config.google),
111
+ config_manager._provider_has_api_key(config.shotgun),
112
+ )
113
+
94
114
  current_model = config.selected_model or ModelName.CLAUDE_SONNET_4_5
95
115
  self.selected_model = current_model
116
+ logger.debug("Current selected model: %s", current_model)
96
117
 
97
- # Find and highlight current selection (if it's in the filtered list)
118
+ # Rebuild the model list with current available models
98
119
  list_view = self.query_one(ListView)
120
+
121
+ # Remove all existing items
122
+ old_count = len(list(list_view.children))
123
+ for child in list(list_view.children):
124
+ child.remove()
125
+ logger.debug("Removed %d existing model items from list", old_count)
126
+
127
+ # Add new items (labels already have correct text including current indicator)
128
+ new_items = self._build_model_items(config)
129
+ for item in new_items:
130
+ list_view.append(item)
131
+ logger.debug("Added %d available model items to list", len(new_items))
132
+
133
+ # Find and highlight current selection (if it's in the filtered list)
99
134
  if list_view.children:
100
135
  for i, child in enumerate(list_view.children):
101
136
  if isinstance(child, ListItem) and child.id:
@@ -106,7 +141,20 @@ class ModelPickerScreen(Screen[None]):
106
141
  if model_name == current_model:
107
142
  list_view.index = i
108
143
  break
109
- self.refresh_model_labels()
144
+
145
+ def on_show(self) -> None:
146
+ """Rebuild model list when screen is first shown."""
147
+ logger.debug("ModelPickerScreen.on_show() called")
148
+ self._rebuild_model_list()
149
+
150
+ def on_screenresume(self) -> None:
151
+ """Rebuild model list when screen is resumed (subsequent visits).
152
+
153
+ This is called when returning to the screen after it was suspended,
154
+ ensuring the model list reflects any config changes made while away.
155
+ """
156
+ logger.debug("ModelPickerScreen.on_screenresume() called")
157
+ self._rebuild_model_list()
110
158
 
111
159
  def action_done(self) -> None:
112
160
  self.dismiss()
@@ -138,13 +186,19 @@ class ModelPickerScreen(Screen[None]):
138
186
  return app.config_manager
139
187
 
140
188
  def refresh_model_labels(self) -> None:
141
- """Update the list view entries to reflect current selection."""
142
- current_model = (
143
- self.config_manager.load().selected_model or ModelName.CLAUDE_SONNET_4_5
144
- )
189
+ """Update the list view entries to reflect current selection.
190
+
191
+ Note: This method only updates labels for currently displayed models.
192
+ To rebuild the entire list after provider changes, on_show() should be used.
193
+ """
194
+ # Load config once with force_reload
195
+ config = self.config_manager.load(force_reload=True)
196
+ current_model = config.selected_model or ModelName.CLAUDE_SONNET_4_5
197
+
145
198
  # Update labels for available models only
146
199
  for model_name in AVAILABLE_MODELS:
147
- if not self._is_model_available(model_name):
200
+ # Pass config to avoid multiple force reloads
201
+ if not self._is_model_available(model_name, config):
148
202
  continue
149
203
  label = self.query_one(
150
204
  f"#label-{_sanitize_model_name_for_id(model_name)}", Label
@@ -153,12 +207,15 @@ class ModelPickerScreen(Screen[None]):
153
207
  self._model_label(model_name, is_current=model_name == current_model)
154
208
  )
155
209
 
156
- def _build_model_items(self) -> list[ListItem]:
210
+ def _build_model_items(self, config: ShotgunConfig | None = None) -> list[ListItem]:
211
+ if config is None:
212
+ config = self.config_manager.load(force_reload=True)
213
+
157
214
  items: list[ListItem] = []
158
215
  current_model = self.selected_model
159
216
  for model_name in AVAILABLE_MODELS:
160
217
  # Only add models that are available
161
- if not self._is_model_available(model_name):
218
+ if not self._is_model_available(model_name, config):
162
219
  continue
163
220
 
164
221
  label = Label(
@@ -181,7 +238,9 @@ class ModelPickerScreen(Screen[None]):
181
238
  return model_name
182
239
  return None
183
240
 
184
- def _is_model_available(self, model_name: ModelName) -> bool:
241
+ def _is_model_available(
242
+ self, model_name: ModelName, config: ShotgunConfig | None = None
243
+ ) -> bool:
185
244
  """Check if a model is available based on provider key configuration.
186
245
 
187
246
  A model is available if:
@@ -190,22 +249,38 @@ class ModelPickerScreen(Screen[None]):
190
249
 
191
250
  Args:
192
251
  model_name: The model to check availability for
252
+ config: Optional pre-loaded config to avoid multiple reloads
193
253
 
194
254
  Returns:
195
255
  True if the model can be used, False otherwise
196
256
  """
197
- config = self.config_manager.load()
257
+ if config is None:
258
+ config = self.config_manager.load(force_reload=True)
198
259
 
199
260
  # If Shotgun Account is configured, all models are available
200
261
  if self.config_manager._provider_has_api_key(config.shotgun):
262
+ logger.debug("Model %s available (Shotgun Account configured)", model_name)
201
263
  return True
202
264
 
203
265
  # In BYOK mode, check if the model's provider has a key
204
266
  if model_name not in MODEL_SPECS:
267
+ logger.debug("Model %s not available (not in MODEL_SPECS)", model_name)
205
268
  return False
206
269
 
207
270
  spec = MODEL_SPECS[model_name]
208
- return self.config_manager.has_provider_key(spec.provider)
271
+ # Check provider key directly using the loaded config to avoid stale cache
272
+ provider_config = self.config_manager._get_provider_config(
273
+ config, spec.provider
274
+ )
275
+ has_key = self.config_manager._provider_has_api_key(provider_config)
276
+ logger.debug(
277
+ "Model %s available=%s (provider=%s, has_key=%s)",
278
+ model_name,
279
+ has_key,
280
+ spec.provider,
281
+ has_key,
282
+ )
283
+ return has_key
209
284
 
210
285
  def _model_label(self, model_name: ModelName, is_current: bool) -> str:
211
286
  """Generate label for model with specs and current indicator."""
@@ -119,6 +119,13 @@ class ProviderConfigScreen(Screen[None]):
119
119
  self.selected_provider = "openai"
120
120
  self.set_focus(self.query_one("#api-key", Input))
121
121
 
122
+ def on_screenresume(self) -> None:
123
+ """Refresh provider status when screen is resumed.
124
+
125
+ This ensures the UI reflects any provider changes made elsewhere.
126
+ """
127
+ self.refresh_provider_status()
128
+
122
129
  def action_done(self) -> None:
123
130
  self.dismiss()
124
131
 
File without changes