shotgun-sh 0.2.6.dev1__tar.gz → 0.2.6.dev2__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 (158) hide show
  1. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/PKG-INFO +1 -1
  2. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/pyproject.toml +1 -1
  3. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/agents/agent_manager.py +3 -2
  4. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/agents/common.py +16 -6
  5. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/agents/models.py +26 -2
  6. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/agents/tools/__init__.py +3 -1
  7. shotgun_sh-0.2.6.dev2/src/shotgun/agents/tools/ask_questions.py +55 -0
  8. shotgun_sh-0.2.6.dev1/src/shotgun/agents/tools/user_interaction.py → shotgun_sh-0.2.6.dev2/src/shotgun/agents/tools/ask_user.py +1 -1
  9. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/prompts/agents/partials/common_agent_system_prompt.j2 +1 -1
  10. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/tui/screens/chat.py +126 -25
  11. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/tui/screens/chat_screen/history.py +12 -2
  12. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/.gitignore +0 -0
  13. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/LICENSE +0 -0
  14. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/README.md +0 -0
  15. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/hatch_build.py +0 -0
  16. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/__init__.py +0 -0
  17. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/agents/__init__.py +0 -0
  18. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/agents/config/__init__.py +0 -0
  19. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/agents/config/constants.py +0 -0
  20. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/agents/config/manager.py +0 -0
  21. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/agents/config/models.py +0 -0
  22. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/agents/config/provider.py +0 -0
  23. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/agents/conversation_history.py +0 -0
  24. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/agents/conversation_manager.py +0 -0
  25. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/agents/export.py +0 -0
  26. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/agents/history/__init__.py +0 -0
  27. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/agents/history/compaction.py +0 -0
  28. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/agents/history/constants.py +0 -0
  29. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/agents/history/context_extraction.py +0 -0
  30. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/agents/history/history_building.py +0 -0
  31. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/agents/history/history_processors.py +0 -0
  32. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/agents/history/message_utils.py +0 -0
  33. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/agents/history/token_counting/__init__.py +0 -0
  34. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/agents/history/token_counting/anthropic.py +0 -0
  35. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/agents/history/token_counting/base.py +0 -0
  36. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/agents/history/token_counting/openai.py +0 -0
  37. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/agents/history/token_counting/sentencepiece_counter.py +0 -0
  38. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/agents/history/token_counting/tokenizer_cache.py +0 -0
  39. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/agents/history/token_counting/utils.py +0 -0
  40. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/agents/history/token_estimation.py +0 -0
  41. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/agents/llm.py +0 -0
  42. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/agents/messages.py +0 -0
  43. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/agents/plan.py +0 -0
  44. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/agents/research.py +0 -0
  45. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/agents/specify.py +0 -0
  46. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/agents/tasks.py +0 -0
  47. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/agents/tools/codebase/__init__.py +0 -0
  48. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/agents/tools/codebase/codebase_shell.py +0 -0
  49. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/agents/tools/codebase/directory_lister.py +0 -0
  50. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/agents/tools/codebase/file_read.py +0 -0
  51. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/agents/tools/codebase/models.py +0 -0
  52. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/agents/tools/codebase/query_graph.py +0 -0
  53. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/agents/tools/codebase/retrieve_code.py +0 -0
  54. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/agents/tools/file_management.py +0 -0
  55. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/agents/tools/web_search/__init__.py +0 -0
  56. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/agents/tools/web_search/anthropic.py +0 -0
  57. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/agents/tools/web_search/gemini.py +0 -0
  58. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/agents/tools/web_search/openai.py +0 -0
  59. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/agents/tools/web_search/utils.py +0 -0
  60. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/agents/usage_manager.py +0 -0
  61. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/api_endpoints.py +0 -0
  62. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/build_constants.py +0 -0
  63. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/cli/__init__.py +0 -0
  64. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/cli/codebase/__init__.py +0 -0
  65. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/cli/codebase/commands.py +0 -0
  66. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/cli/codebase/models.py +0 -0
  67. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/cli/config.py +0 -0
  68. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/cli/export.py +0 -0
  69. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/cli/feedback.py +0 -0
  70. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/cli/models.py +0 -0
  71. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/cli/plan.py +0 -0
  72. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/cli/research.py +0 -0
  73. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/cli/specify.py +0 -0
  74. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/cli/tasks.py +0 -0
  75. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/cli/update.py +0 -0
  76. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/cli/utils.py +0 -0
  77. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/codebase/__init__.py +0 -0
  78. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/codebase/core/__init__.py +0 -0
  79. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/codebase/core/change_detector.py +0 -0
  80. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/codebase/core/code_retrieval.py +0 -0
  81. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/codebase/core/cypher_models.py +0 -0
  82. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/codebase/core/ingestor.py +0 -0
  83. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/codebase/core/language_config.py +0 -0
  84. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/codebase/core/manager.py +0 -0
  85. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/codebase/core/nl_query.py +0 -0
  86. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/codebase/core/parser_loader.py +0 -0
  87. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/codebase/models.py +0 -0
  88. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/codebase/service.py +0 -0
  89. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/llm_proxy/__init__.py +0 -0
  90. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/llm_proxy/clients.py +0 -0
  91. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/llm_proxy/constants.py +0 -0
  92. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/logging_config.py +0 -0
  93. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/main.py +0 -0
  94. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/posthog_telemetry.py +0 -0
  95. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/prompts/__init__.py +0 -0
  96. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/prompts/agents/__init__.py +0 -0
  97. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/prompts/agents/export.j2 +0 -0
  98. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/prompts/agents/partials/codebase_understanding.j2 +0 -0
  99. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/prompts/agents/partials/content_formatting.j2 +0 -0
  100. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/prompts/agents/partials/interactive_mode.j2 +0 -0
  101. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/prompts/agents/plan.j2 +0 -0
  102. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/prompts/agents/research.j2 +0 -0
  103. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/prompts/agents/specify.j2 +0 -0
  104. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/prompts/agents/state/codebase/codebase_graphs_available.j2 +0 -0
  105. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/prompts/agents/state/system_state.j2 +0 -0
  106. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/prompts/agents/tasks.j2 +0 -0
  107. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/prompts/codebase/__init__.py +0 -0
  108. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/prompts/codebase/cypher_query_patterns.j2 +0 -0
  109. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/prompts/codebase/cypher_system.j2 +0 -0
  110. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/prompts/codebase/enhanced_query_context.j2 +0 -0
  111. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/prompts/codebase/partials/cypher_rules.j2 +0 -0
  112. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/prompts/codebase/partials/graph_schema.j2 +0 -0
  113. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/prompts/codebase/partials/temporal_context.j2 +0 -0
  114. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/prompts/history/__init__.py +0 -0
  115. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/prompts/history/incremental_summarization.j2 +0 -0
  116. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/prompts/history/summarization.j2 +0 -0
  117. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/prompts/loader.py +0 -0
  118. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/prompts/tools/web_search.j2 +0 -0
  119. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/py.typed +0 -0
  120. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/sdk/__init__.py +0 -0
  121. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/sdk/codebase.py +0 -0
  122. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/sdk/exceptions.py +0 -0
  123. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/sdk/models.py +0 -0
  124. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/sdk/services.py +0 -0
  125. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/sentry_telemetry.py +0 -0
  126. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/shotgun_web/__init__.py +0 -0
  127. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/shotgun_web/client.py +0 -0
  128. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/shotgun_web/constants.py +0 -0
  129. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/shotgun_web/models.py +0 -0
  130. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/telemetry.py +0 -0
  131. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/tui/__init__.py +0 -0
  132. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/tui/app.py +0 -0
  133. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/tui/commands/__init__.py +0 -0
  134. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/tui/components/prompt_input.py +0 -0
  135. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/tui/components/spinner.py +0 -0
  136. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/tui/components/splash.py +0 -0
  137. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/tui/components/vertical_tail.py +0 -0
  138. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/tui/filtered_codebase_service.py +0 -0
  139. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/tui/screens/chat.tcss +0 -0
  140. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/tui/screens/chat_screen/__init__.py +0 -0
  141. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/tui/screens/chat_screen/command_providers.py +0 -0
  142. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/tui/screens/chat_screen/hint_message.py +0 -0
  143. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/tui/screens/directory_setup.py +0 -0
  144. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/tui/screens/feedback.py +0 -0
  145. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/tui/screens/model_picker.py +0 -0
  146. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/tui/screens/provider_config.py +0 -0
  147. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/tui/screens/shotgun_auth.py +0 -0
  148. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/tui/screens/splash.py +0 -0
  149. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/tui/screens/welcome.py +0 -0
  150. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/tui/styles.tcss +0 -0
  151. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/tui/utils/__init__.py +0 -0
  152. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/tui/utils/mode_progress.py +0 -0
  153. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/utils/__init__.py +0 -0
  154. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/utils/datetime_utils.py +0 -0
  155. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/utils/env_utils.py +0 -0
  156. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/utils/file_system_utils.py +0 -0
  157. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/src/shotgun/utils/source_detection.py +0 -0
  158. {shotgun_sh-0.2.6.dev1 → shotgun_sh-0.2.6.dev2}/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.6.dev1
3
+ Version: 0.2.6.dev2
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.6.dev1"
3
+ version = "0.2.6.dev2"
4
4
  description = "AI-powered research, planning, and task management CLI tool"
5
5
  readme = "README.md"
6
6
  license = { text = "MIT" }
@@ -706,12 +706,13 @@ class AgentManager(Widget):
706
706
  if not self.message_history:
707
707
  return None
708
708
  self.last_response = self.message_history[-1]
709
- ## we're searching for unanswered ask_user parts
709
+ ## we're searching for unanswered ask_user or ask_questions parts
710
710
  found_tool = next(
711
711
  (
712
712
  part
713
713
  for part in self.message_history[-1].parts
714
- if isinstance(part, ToolCallPart) and part.tool_name == "ask_user"
714
+ if isinstance(part, ToolCallPart)
715
+ and part.tool_name in ("ask_user", "ask_questions")
715
716
  ),
716
717
  None,
717
718
  )
@@ -33,6 +33,7 @@ from .messages import AgentSystemPrompt, SystemStatusPrompt
33
33
  from .models import AgentDeps, AgentRuntimeOptions, PipelineConfigEntry
34
34
  from .tools import (
35
35
  append_file,
36
+ ask_questions,
36
37
  ask_user,
37
38
  codebase_shell,
38
39
  directory_lister,
@@ -179,10 +180,13 @@ def create_base_agent(
179
180
  for tool in additional_tools or []:
180
181
  agent.tool_plain(tool)
181
182
 
182
- # Register interactive tool conditionally based on deps
183
+ # Register interactive tools conditionally based on deps
183
184
  if deps.interactive_mode:
184
185
  agent.tool(ask_user)
185
- logger.debug("📞 Interactive mode enabled - ask_user tool registered")
186
+ agent.tool(ask_questions)
187
+ logger.debug(
188
+ "📞 Interactive mode enabled - ask_user and ask_questions tools registered"
189
+ )
186
190
 
187
191
  # Register common file management tools (always available)
188
192
  agent.tool(write_file)
@@ -323,7 +327,9 @@ def extract_markdown_toc(agent_mode: AgentType | None) -> str | None:
323
327
  if prior_toc:
324
328
  # Add section with XML tags
325
329
  toc_sections.append(
326
- f'<TABLE_OF_CONTENTS file_name="{prior_file}">\n{prior_toc}\n</TABLE_OF_CONTENTS>'
330
+ f'<TABLE_OF_CONTENTS file_name="{prior_file}">\n'
331
+ f"{prior_toc}\n"
332
+ f"</TABLE_OF_CONTENTS>"
327
333
  )
328
334
 
329
335
  # Extract TOC from own file (full detail)
@@ -334,7 +340,9 @@ def extract_markdown_toc(agent_mode: AgentType | None) -> str | None:
334
340
  # Put own file TOC at the beginning with XML tags
335
341
  toc_sections.insert(
336
342
  0,
337
- f'<TABLE_OF_CONTENTS file_name="{config.own_file}">\n{own_toc}\n</TABLE_OF_CONTENTS>',
343
+ f'<TABLE_OF_CONTENTS file_name="{config.own_file}">\n'
344
+ f"{own_toc}\n"
345
+ f"</TABLE_OF_CONTENTS>",
338
346
  )
339
347
 
340
348
  # Combine all sections
@@ -476,7 +484,8 @@ async def add_system_prompt_message(
476
484
  message_history = message_history or []
477
485
 
478
486
  # Create a minimal RunContext to call the system prompt function
479
- # We'll pass None for model and usage since they're not used by our system prompt functions
487
+ # We'll pass None for model and usage since they're not used
488
+ # by our system prompt functions
480
489
  context = type(
481
490
  "RunContext", (), {"deps": deps, "retry": 0, "model": None, "usage": None}
482
491
  )()
@@ -544,7 +553,8 @@ async def run_agent(
544
553
  message_history=messages,
545
554
  deferred_tool_results=results,
546
555
  )
547
- # Apply persistent compaction to prevent cascading token growth in multi-turn loops
556
+ # Apply persistent compaction to prevent cascading token growth
557
+ # in multi-turn loops
548
558
  messages = await apply_persistent_compaction(result.all_messages(), deps)
549
559
 
550
560
  # Log file operations summary if any files were modified
@@ -73,6 +73,30 @@ class UserQuestion(BaseModel):
73
73
  )
74
74
 
75
75
 
76
+ class MultipleUserQuestions(BaseModel):
77
+ """Multiple questions to ask the user sequentially."""
78
+
79
+ model_config = ConfigDict(arbitrary_types_allowed=True)
80
+
81
+ questions: list[str] = Field(
82
+ description="List of questions to ask the user",
83
+ )
84
+ current_index: int = Field(
85
+ default=0,
86
+ description="Current question index being asked",
87
+ )
88
+ answers: list[str] = Field(
89
+ default_factory=list,
90
+ description="Accumulated answers from the user",
91
+ )
92
+ tool_call_id: str = Field(
93
+ description="Tool call id",
94
+ )
95
+ result: Future[UserAnswer] = Field(
96
+ description="Future that will contain all answers formatted as Q&A pairs"
97
+ )
98
+
99
+
76
100
  class AgentRuntimeOptions(BaseModel):
77
101
  """User interface options for agents."""
78
102
 
@@ -100,9 +124,9 @@ class AgentRuntimeOptions(BaseModel):
100
124
  description="Maximum number of iterations for agent loops",
101
125
  )
102
126
 
103
- queue: Queue[UserQuestion] = Field(
127
+ queue: Queue[UserQuestion | MultipleUserQuestions] = Field(
104
128
  default_factory=Queue,
105
- description="Queue for storing user responses",
129
+ description="Queue for storing user questions (single or multiple)",
106
130
  )
107
131
 
108
132
  tasks: list[Future[UserAnswer]] = Field(
@@ -1,5 +1,7 @@
1
1
  """Tools package for Pydantic AI agents."""
2
2
 
3
+ from .ask_questions import ask_questions
4
+ from .ask_user import ask_user
3
5
  from .codebase import (
4
6
  codebase_shell,
5
7
  directory_lister,
@@ -8,7 +10,6 @@ from .codebase import (
8
10
  retrieve_code,
9
11
  )
10
12
  from .file_management import append_file, read_file, write_file
11
- from .user_interaction import ask_user
12
13
  from .web_search import (
13
14
  anthropic_web_search_tool,
14
15
  gemini_web_search_tool,
@@ -22,6 +23,7 @@ __all__ = [
22
23
  "gemini_web_search_tool",
23
24
  "get_available_web_search_tools",
24
25
  "ask_user",
26
+ "ask_questions",
25
27
  "read_file",
26
28
  "write_file",
27
29
  "append_file",
@@ -0,0 +1,55 @@
1
+ """Ask multiple questions tool for Pydantic AI agents."""
2
+
3
+ from asyncio import get_running_loop
4
+
5
+ from pydantic_ai import CallDeferred, RunContext
6
+
7
+ from shotgun.agents.models import AgentDeps, MultipleUserQuestions
8
+ from shotgun.logging_config import get_logger
9
+
10
+ logger = get_logger(__name__)
11
+
12
+
13
+ async def ask_questions(ctx: RunContext[AgentDeps], questions: list[str]) -> str:
14
+ """Ask the human multiple questions sequentially and return all Q&A pairs.
15
+
16
+ This tool will display questions one at a time with progress indicators
17
+ (e.g., "Question 1 of 5") and collect all answers before returning them
18
+ formatted together. This provides a better UX than asking questions one by one,
19
+ as users can see their progress and don't need to use multiline input.
20
+
21
+ Do not ask 1 question with multiple parts, or multiple questions inside of it.
22
+ Structure it so each question can be answered independently.
23
+
24
+ Args:
25
+ questions: List of questions to ask the user. Each question should be readable,
26
+ clear, and easy to understand. Use Markdown formatting. Make key phrases
27
+ and words stand out. Questions will be asked in the order provided.
28
+
29
+ Returns:
30
+ All questions and answers formatted as:
31
+ "Q1: {question1}\\nA1: {answer1}\\n\\nQ2: {question2}\\nA2: {answer2}\\n\\n..."
32
+ """
33
+ tool_call_id = ctx.tool_call_id
34
+ assert tool_call_id is not None # noqa: S101
35
+
36
+ try:
37
+ logger.debug("\n👉 Asking %d questions\n", len(questions))
38
+ future = get_running_loop().create_future()
39
+ await ctx.deps.queue.put(
40
+ MultipleUserQuestions(
41
+ questions=questions,
42
+ tool_call_id=tool_call_id,
43
+ result=future,
44
+ )
45
+ )
46
+ ctx.deps.tasks.append(future)
47
+ # Use first question as deferred message preview
48
+ preview = questions[0] if questions else "No questions"
49
+ raise CallDeferred(
50
+ f"Asking {len(questions)} questions starting with: {preview}"
51
+ )
52
+
53
+ except (EOFError, KeyboardInterrupt):
54
+ logger.warning("User input interrupted or unavailable")
55
+ return "User input not available or interrupted"
@@ -1,4 +1,4 @@
1
- """User interaction tools for Pydantic AI agents."""
1
+ """Ask user tool for Pydantic AI agents."""
2
2
 
3
3
  from asyncio import get_running_loop
4
4
 
@@ -7,7 +7,7 @@ Your extensive expertise spans, among other things:
7
7
  ## KEY RULES
8
8
 
9
9
  {% if interactive_mode %}
10
- 0. Always ask CLARIFYING QUESTIONS if the user's request is ambiguous or lacks sufficient detail. Do not make assumptions about what the user wants.
10
+ 0. Always ask CLARIFYING QUESTIONS using the ask_questions() tool if the user's request is ambiguous or lacks sufficient detail. Do not make assumptions about what the user wants.
11
11
  {% endif %}
12
12
  1. Above all, prefer using tools to do the work and NEVER respond with text.
13
13
  2. IMPORTANT: Always ask for review and go ahead to move forward after using write_file().
@@ -20,7 +20,7 @@ from textual.keys import Keys
20
20
  from textual.reactive import reactive
21
21
  from textual.screen import ModalScreen, Screen
22
22
  from textual.widget import Widget
23
- from textual.widgets import Button, Label, Markdown, Static
23
+ from textual.widgets import Button, Label, Static
24
24
 
25
25
  from shotgun.agents.agent_manager import (
26
26
  AgentManager,
@@ -37,6 +37,7 @@ from shotgun.agents.models import (
37
37
  AgentDeps,
38
38
  AgentType,
39
39
  FileOperationTracker,
40
+ MultipleUserQuestions,
40
41
  UserQuestion,
41
42
  )
42
43
  from shotgun.codebase.core.manager import CodebaseAlreadyIndexedError
@@ -114,9 +115,18 @@ class StatusBar(Widget):
114
115
 
115
116
  def render(self) -> str:
116
117
  if self.working:
117
- return """[$foreground-muted][bold $text]esc[/] to stop • [bold $text]enter[/] to send • [bold $text]ctrl+j[/] for newline • [bold $text]ctrl+p[/] command palette • [bold $text]shift+tab[/] cycle modes • /help for commands[/]"""
118
+ return (
119
+ "[$foreground-muted][bold $text]esc[/] to stop • "
120
+ "[bold $text]enter[/] to send • [bold $text]ctrl+j[/] for newline • "
121
+ "[bold $text]ctrl+p[/] command palette • [bold $text]shift+tab[/] cycle modes • "
122
+ "/help for commands[/]"
123
+ )
118
124
  else:
119
- return """[$foreground-muted][bold $text]enter[/] to send • [bold $text]ctrl+j[/] for newline • [bold $text]ctrl+p[/] command palette • [bold $text]shift+tab[/] cycle modes • /help for commands[/]"""
125
+ return (
126
+ "[$foreground-muted][bold $text]enter[/] to send • "
127
+ "[bold $text]ctrl+j[/] for newline • [bold $text]ctrl+p[/] command palette • "
128
+ "[bold $text]shift+tab[/] cycle modes • /help for commands[/]"
129
+ )
120
130
 
121
131
 
122
132
  class ModeIndicator(Widget):
@@ -149,10 +159,16 @@ class ModeIndicator(Widget):
149
159
  AgentType.EXPORT: "Export",
150
160
  }
151
161
  mode_description = {
152
- AgentType.RESEARCH: "Research topics with web search and synthesize findings",
162
+ AgentType.RESEARCH: (
163
+ "Research topics with web search and synthesize findings"
164
+ ),
153
165
  AgentType.PLAN: "Create comprehensive, actionable plans with milestones",
154
- AgentType.TASKS: "Generate specific, actionable tasks from research and plans",
155
- AgentType.SPECIFY: "Create detailed specifications and requirements documents",
166
+ AgentType.TASKS: (
167
+ "Generate specific, actionable tasks from research and plans"
168
+ ),
169
+ AgentType.SPECIFY: (
170
+ "Create detailed specifications and requirements documents"
171
+ ),
156
172
  AgentType.EXPORT: "Export artifacts and findings to various formats",
157
173
  }
158
174
 
@@ -163,7 +179,10 @@ class ModeIndicator(Widget):
163
179
  has_content = self.progress_checker.has_mode_content(self.mode)
164
180
  status_icon = " ✓" if has_content else ""
165
181
 
166
- return f"[bold $text-accent]{mode_title}{status_icon} mode[/][$foreground-muted] ({description})[/]"
182
+ return (
183
+ f"[bold $text-accent]{mode_title}{status_icon} mode[/]"
184
+ f"[$foreground-muted] ({description})[/]"
185
+ )
167
186
 
168
187
 
169
188
  class CodebaseIndexPromptScreen(ModalScreen[bool]):
@@ -199,7 +218,8 @@ class CodebaseIndexPromptScreen(ModalScreen[bool]):
199
218
  yield Static(
200
219
  f"Would you like to index the codebase at:\n{Path.cwd()}\n\n"
201
220
  "This is required for the agent to understand your code and answer "
202
- "questions about it. Without indexing, the agent cannot analyze your codebase."
221
+ "questions about it. Without indexing, the agent cannot analyze "
222
+ "your codebase."
203
223
  )
204
224
  with Container(id="index-prompt-buttons"):
205
225
  yield Button(
@@ -238,7 +258,7 @@ class ChatScreen(Screen[None]):
238
258
  history: PromptHistory = PromptHistory()
239
259
  messages = reactive(list[ModelMessage | HintMessage]())
240
260
  working = reactive(False)
241
- question: reactive[UserQuestion | None] = reactive(None)
261
+ question: reactive[UserQuestion | MultipleUserQuestions | None] = reactive(None)
242
262
  indexing_job: reactive[CodebaseIndexSelection | None] = reactive(None)
243
263
  partial_message: reactive[ModelMessage | None] = reactive(None)
244
264
  _current_worker = None # Track the current running worker for cancellation
@@ -378,17 +398,6 @@ class ChatScreen(Screen[None]):
378
398
  chat_history = self.query_one(ChatHistory)
379
399
  chat_history.update_messages(messages)
380
400
 
381
- def watch_question(self, question: UserQuestion | None) -> None:
382
- """Update the question display."""
383
- if self.is_mounted:
384
- question_display = self.query_one("#question-display", Markdown)
385
- if question:
386
- question_display.update(f"Question:\n\n{question.question}")
387
- question_display.display = True
388
- else:
389
- question_display.update("")
390
- question_display.display = False
391
-
392
401
  def action_toggle_mode(self) -> None:
393
402
  modes = [
394
403
  AgentType.RESEARCH,
@@ -414,8 +423,38 @@ class ChatScreen(Screen[None]):
414
423
  async def add_question_listener(self) -> None:
415
424
  while True:
416
425
  question = await self.deps.queue.get()
417
- self.question = question
418
- await question.result
426
+
427
+ if isinstance(question, MultipleUserQuestions):
428
+ # Set question state - handle_submit will add Q&A to chat
429
+ question.current_index = 0
430
+ self.question = question
431
+
432
+ # Show intro message with total question count
433
+ num_questions = len(question.questions)
434
+ self.agent_manager.add_hint_message(
435
+ HintMessage(message=f"I'm going to ask {num_questions} questions:")
436
+ )
437
+
438
+ # Show all questions in a numbered list so users can see what's coming
439
+ if question.questions:
440
+ questions_list = "\n".join(
441
+ f"{i + 1}. {q}" for i, q in enumerate(question.questions)
442
+ )
443
+ self.agent_manager.add_hint_message(
444
+ HintMessage(message=questions_list)
445
+ )
446
+
447
+ # Now show the first question prompt to indicate where to start answering
448
+ first_q = question.questions[0]
449
+ self.agent_manager.add_hint_message(
450
+ HintMessage(message=f"**Q1:** {first_q}")
451
+ )
452
+ else:
453
+ # Handle single question (original behavior)
454
+ self.question = question
455
+ await question.result
456
+ self.question = None
457
+
419
458
  self.deps.queue.task_done()
420
459
 
421
460
  def compose(self) -> ComposeResult:
@@ -423,7 +462,6 @@ class ChatScreen(Screen[None]):
423
462
  with Container(id="window"):
424
463
  yield self.agent_manager
425
464
  yield ChatHistory()
426
- yield Markdown(markdown="", id="question-display")
427
465
  with Container(id="footer"):
428
466
  yield Spinner(
429
467
  text="Processing...",
@@ -510,6 +548,8 @@ class ChatScreen(Screen[None]):
510
548
 
511
549
  @on(PromptInput.Submitted)
512
550
  async def handle_submit(self, message: PromptInput.Submitted) -> None:
551
+ from shotgun.agents.models import UserAnswer
552
+
513
553
  text = message.text.strip()
514
554
 
515
555
  # If empty text, just clear input and return
@@ -519,6 +559,63 @@ class ChatScreen(Screen[None]):
519
559
  self.value = ""
520
560
  return
521
561
 
562
+ # Check if we're in a multi-question flow
563
+ if self.question and isinstance(self.question, MultipleUserQuestions):
564
+ q_num = self.question.current_index + 1
565
+
566
+ # Q1 already shown by handle_message_history_updated,
567
+ # Q2+ shown after previous answer. So we only need to add the answer to chat
568
+ self.agent_manager.add_hint_message(
569
+ HintMessage(message=f"**A{q_num}:** {text}")
570
+ )
571
+
572
+ # Store the answer
573
+ self.question.answers.append(text)
574
+
575
+ # Move to next question or finish
576
+ self.question.current_index += 1
577
+
578
+ if self.question.current_index < len(self.question.questions):
579
+ # Show the next question immediately after the answer
580
+ next_q = self.question.questions[self.question.current_index]
581
+ next_q_num = self.question.current_index + 1
582
+ self.agent_manager.add_hint_message(
583
+ HintMessage(message=f"**Q{next_q_num}:** {next_q}")
584
+ )
585
+ else:
586
+ # All questions answered! Format and resolve
587
+ formatted_qa = "\n\n".join(
588
+ f"Q{i + 1}: {q}\nA{i + 1}: {a}"
589
+ for i, (q, a) in enumerate(
590
+ zip(self.question.questions, self.question.answers, strict=True)
591
+ )
592
+ )
593
+
594
+ # Resolve the original future with formatted Q&A (this goes to the agent)
595
+ final_answer = UserAnswer(
596
+ answer=formatted_qa,
597
+ tool_call_id=self.question.tool_call_id,
598
+ )
599
+ self.question.result.set_result(final_answer)
600
+
601
+ # Clear question state
602
+ self.question = None
603
+
604
+ # Clear input first
605
+ prompt_input = self.query_one(PromptInput)
606
+ prompt_input.clear()
607
+ self.value = ""
608
+
609
+ # Send the formatted Q&A directly to the agent (no prefix needed)
610
+ self.run_agent(formatted_qa)
611
+ return
612
+
613
+ # Clear input and return
614
+ prompt_input = self.query_one(PromptInput)
615
+ prompt_input.clear()
616
+ self.value = ""
617
+ return
618
+
522
619
  # Check if it's a command
523
620
  if self.command_handler.is_command(text):
524
621
  success, response = self.command_handler.handle_command(text)
@@ -785,7 +882,9 @@ class ChatScreen(Screen[None]):
785
882
 
786
883
  def help_text_with_codebase(already_indexed: bool = False) -> str:
787
884
  return (
788
- "Howdy! Welcome to Shotgun - the context tool for software engineering. \n\nYou can research, build specs, plan, create tasks, and export context to your favorite code-gen agents.\n\n"
885
+ "Howdy! Welcome to Shotgun - the context tool for software engineering. \n\n"
886
+ "You can research, build specs, plan, create tasks, and export context to your "
887
+ "favorite code-gen agents.\n\n"
789
888
  f"{'' if already_indexed else 'Once your codebase is indexed, '}I can help with:\n\n"
790
889
  "- Speccing out a new feature\n"
791
890
  "- Onboarding you onto this project\n"
@@ -796,7 +895,9 @@ def help_text_with_codebase(already_indexed: bool = False) -> str:
796
895
 
797
896
  def help_text_empty_dir() -> str:
798
897
  return (
799
- "Howdy! Welcome to Shotgun - the context tool for software engineering.\n\nYou can research, build specs, plan, create tasks, and export context to your favorite code-gen agents.\n\n"
898
+ "Howdy! Welcome to Shotgun - the context tool for software engineering.\n\n"
899
+ "You can research, build specs, plan, create tasks, and export context to your "
900
+ "favorite code-gen agents.\n\n"
800
901
  "What would you like to build? Here are some examples:\n\n"
801
902
  "- Research FastAPI vs Django\n"
802
903
  "- Plan my new web app using React\n"
@@ -114,7 +114,7 @@ class ChatHistory(Widget):
114
114
  part
115
115
  for part in prev_item.parts
116
116
  if isinstance(part, ToolReturnPart)
117
- and part.tool_name == "ask_user"
117
+ and part.tool_name in ("ask_user", "ask_questions")
118
118
  ),
119
119
  None,
120
120
  )
@@ -124,7 +124,7 @@ class ChatHistory(Widget):
124
124
  part
125
125
  for part in next_item.parts
126
126
  if isinstance(part, ToolCallPart)
127
- and part.tool_name == "ask_user"
127
+ and part.tool_name in ("ask_user", "ask_questions")
128
128
  ),
129
129
  None,
130
130
  )
@@ -289,6 +289,9 @@ class AgentResponseWidget(Widget):
289
289
  if part.tool_name == "ask_user":
290
290
  return self._format_ask_user_part(part)
291
291
 
292
+ if part.tool_name == "ask_questions":
293
+ return self._format_ask_questions_part(part)
294
+
292
295
  # Parse args once (handles both JSON string and dict)
293
296
  args = self._parse_args(part.args)
294
297
 
@@ -399,3 +402,10 @@ class AgentResponseWidget(Widget):
399
402
  return f"{_args['question']}"
400
403
  else:
401
404
  return "❓ "
405
+
406
+ def _format_ask_questions_part(
407
+ self,
408
+ part: ToolCallPart,
409
+ ) -> str:
410
+ """Hide ask_questions tool calls - Q&A shown as HintMessages instead."""
411
+ return ""
File without changes