shotgun-sh 0.2.3.dev2__py3-none-any.whl → 0.2.11.dev1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


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

Files changed (107) hide show
  1. shotgun/agents/agent_manager.py +524 -58
  2. shotgun/agents/common.py +62 -62
  3. shotgun/agents/config/constants.py +0 -6
  4. shotgun/agents/config/manager.py +14 -3
  5. shotgun/agents/config/models.py +16 -0
  6. shotgun/agents/config/provider.py +68 -13
  7. shotgun/agents/context_analyzer/__init__.py +28 -0
  8. shotgun/agents/context_analyzer/analyzer.py +493 -0
  9. shotgun/agents/context_analyzer/constants.py +9 -0
  10. shotgun/agents/context_analyzer/formatter.py +115 -0
  11. shotgun/agents/context_analyzer/models.py +212 -0
  12. shotgun/agents/conversation_history.py +125 -2
  13. shotgun/agents/conversation_manager.py +24 -2
  14. shotgun/agents/export.py +4 -5
  15. shotgun/agents/history/compaction.py +9 -4
  16. shotgun/agents/history/context_extraction.py +93 -6
  17. shotgun/agents/history/history_processors.py +14 -2
  18. shotgun/agents/history/token_counting/anthropic.py +32 -10
  19. shotgun/agents/models.py +50 -2
  20. shotgun/agents/plan.py +4 -5
  21. shotgun/agents/research.py +4 -5
  22. shotgun/agents/specify.py +4 -5
  23. shotgun/agents/tasks.py +4 -5
  24. shotgun/agents/tools/__init__.py +0 -2
  25. shotgun/agents/tools/codebase/codebase_shell.py +6 -0
  26. shotgun/agents/tools/codebase/directory_lister.py +6 -0
  27. shotgun/agents/tools/codebase/file_read.py +6 -0
  28. shotgun/agents/tools/codebase/query_graph.py +6 -0
  29. shotgun/agents/tools/codebase/retrieve_code.py +6 -0
  30. shotgun/agents/tools/file_management.py +71 -9
  31. shotgun/agents/tools/registry.py +217 -0
  32. shotgun/agents/tools/web_search/__init__.py +24 -12
  33. shotgun/agents/tools/web_search/anthropic.py +24 -3
  34. shotgun/agents/tools/web_search/gemini.py +22 -10
  35. shotgun/agents/tools/web_search/openai.py +21 -12
  36. shotgun/api_endpoints.py +7 -3
  37. shotgun/build_constants.py +1 -1
  38. shotgun/cli/clear.py +52 -0
  39. shotgun/cli/compact.py +186 -0
  40. shotgun/cli/context.py +111 -0
  41. shotgun/cli/models.py +1 -0
  42. shotgun/cli/update.py +16 -2
  43. shotgun/codebase/core/manager.py +10 -1
  44. shotgun/llm_proxy/__init__.py +5 -2
  45. shotgun/llm_proxy/clients.py +12 -7
  46. shotgun/logging_config.py +8 -10
  47. shotgun/main.py +70 -10
  48. shotgun/posthog_telemetry.py +9 -3
  49. shotgun/prompts/agents/export.j2 +18 -1
  50. shotgun/prompts/agents/partials/common_agent_system_prompt.j2 +5 -1
  51. shotgun/prompts/agents/partials/interactive_mode.j2 +24 -7
  52. shotgun/prompts/agents/plan.j2 +1 -1
  53. shotgun/prompts/agents/research.j2 +1 -1
  54. shotgun/prompts/agents/specify.j2 +270 -3
  55. shotgun/prompts/agents/state/system_state.j2 +4 -0
  56. shotgun/prompts/agents/tasks.j2 +1 -1
  57. shotgun/prompts/loader.py +2 -2
  58. shotgun/prompts/tools/web_search.j2 +14 -0
  59. shotgun/sentry_telemetry.py +4 -15
  60. shotgun/settings.py +238 -0
  61. shotgun/telemetry.py +15 -32
  62. shotgun/tui/app.py +203 -9
  63. shotgun/tui/commands/__init__.py +1 -1
  64. shotgun/tui/components/context_indicator.py +136 -0
  65. shotgun/tui/components/mode_indicator.py +70 -0
  66. shotgun/tui/components/status_bar.py +48 -0
  67. shotgun/tui/containers.py +93 -0
  68. shotgun/tui/dependencies.py +39 -0
  69. shotgun/tui/protocols.py +45 -0
  70. shotgun/tui/screens/chat/__init__.py +5 -0
  71. shotgun/tui/screens/chat/chat.tcss +54 -0
  72. shotgun/tui/screens/chat/chat_screen.py +1110 -0
  73. shotgun/tui/screens/chat/codebase_index_prompt_screen.py +64 -0
  74. shotgun/tui/screens/chat/codebase_index_selection.py +12 -0
  75. shotgun/tui/screens/chat/help_text.py +39 -0
  76. shotgun/tui/screens/chat/prompt_history.py +48 -0
  77. shotgun/tui/screens/chat.tcss +11 -0
  78. shotgun/tui/screens/chat_screen/command_providers.py +68 -2
  79. shotgun/tui/screens/chat_screen/history/__init__.py +22 -0
  80. shotgun/tui/screens/chat_screen/history/agent_response.py +66 -0
  81. shotgun/tui/screens/chat_screen/history/chat_history.py +116 -0
  82. shotgun/tui/screens/chat_screen/history/formatters.py +115 -0
  83. shotgun/tui/screens/chat_screen/history/partial_response.py +43 -0
  84. shotgun/tui/screens/chat_screen/history/user_question.py +42 -0
  85. shotgun/tui/screens/confirmation_dialog.py +151 -0
  86. shotgun/tui/screens/model_picker.py +30 -6
  87. shotgun/tui/screens/pipx_migration.py +153 -0
  88. shotgun/tui/screens/welcome.py +24 -5
  89. shotgun/tui/services/__init__.py +5 -0
  90. shotgun/tui/services/conversation_service.py +182 -0
  91. shotgun/tui/state/__init__.py +7 -0
  92. shotgun/tui/state/processing_state.py +185 -0
  93. shotgun/tui/widgets/__init__.py +5 -0
  94. shotgun/tui/widgets/widget_coordinator.py +247 -0
  95. shotgun/utils/datetime_utils.py +77 -0
  96. shotgun/utils/file_system_utils.py +3 -2
  97. shotgun/utils/update_checker.py +69 -14
  98. shotgun_sh-0.2.11.dev1.dist-info/METADATA +129 -0
  99. shotgun_sh-0.2.11.dev1.dist-info/RECORD +190 -0
  100. {shotgun_sh-0.2.3.dev2.dist-info → shotgun_sh-0.2.11.dev1.dist-info}/entry_points.txt +1 -0
  101. {shotgun_sh-0.2.3.dev2.dist-info → shotgun_sh-0.2.11.dev1.dist-info}/licenses/LICENSE +1 -1
  102. shotgun/agents/tools/user_interaction.py +0 -37
  103. shotgun/tui/screens/chat.py +0 -804
  104. shotgun/tui/screens/chat_screen/history.py +0 -352
  105. shotgun_sh-0.2.3.dev2.dist-info/METADATA +0 -467
  106. shotgun_sh-0.2.3.dev2.dist-info/RECORD +0 -154
  107. {shotgun_sh-0.2.3.dev2.dist-info → shotgun_sh-0.2.11.dev1.dist-info}/WHEEL +0 -0
@@ -0,0 +1,217 @@
1
+ """Tool category registry using decorators for automatic registration.
2
+
3
+ This module provides a decorator-based system for categorizing tools used by agents.
4
+ Tools can be decorated with @register_tool to automatically register their category,
5
+ which is then used by the context analyzer to break down token usage by tool type.
6
+
7
+ It also provides a display registry system for tool formatting in the TUI, allowing
8
+ tools to declare how they should be displayed when streaming.
9
+ """
10
+
11
+ from collections.abc import Callable
12
+ from enum import StrEnum
13
+ from typing import TypeVar, overload
14
+
15
+ import sentry_sdk
16
+ from pydantic import BaseModel
17
+
18
+ from shotgun.logging_config import get_logger
19
+
20
+ logger = get_logger(__name__)
21
+
22
+ # Type variable for decorated functions
23
+ F = TypeVar("F", bound=Callable[..., object])
24
+
25
+
26
+ class ToolCategory(StrEnum):
27
+ """Categories for agent tools used in context analysis."""
28
+
29
+ CODEBASE_UNDERSTANDING = "codebase_understanding"
30
+ ARTIFACT_MANAGEMENT = "artifact_management"
31
+ WEB_RESEARCH = "web_research"
32
+ AGENT_RESPONSE = "agent_response"
33
+ UNKNOWN = "unknown"
34
+
35
+
36
+ class ToolDisplayConfig(BaseModel):
37
+ """Configuration for how a tool should be displayed in the TUI.
38
+
39
+ Attributes:
40
+ display_text: Text to show (e.g., "Reading file", "Querying code")
41
+ key_arg: Primary argument to extract from tool args for display
42
+ hide: Whether to completely hide this tool call from the UI
43
+ """
44
+
45
+ display_text: str
46
+ key_arg: str
47
+ hide: bool = False
48
+
49
+
50
+ # Global registry mapping tool names to categories
51
+ _TOOL_REGISTRY: dict[str, ToolCategory] = {}
52
+
53
+ # Global registry mapping tool names to display configs
54
+ _TOOL_DISPLAY_REGISTRY: dict[str, ToolDisplayConfig] = {}
55
+
56
+
57
+ @overload
58
+ def register_tool(
59
+ category: ToolCategory,
60
+ display_text: str,
61
+ key_arg: str,
62
+ ) -> Callable[[F], F]: ...
63
+
64
+
65
+ @overload
66
+ def register_tool(
67
+ category: ToolCategory,
68
+ display_text: str,
69
+ key_arg: str,
70
+ *,
71
+ hide: bool,
72
+ ) -> Callable[[F], F]: ...
73
+
74
+
75
+ def register_tool(
76
+ category: ToolCategory,
77
+ display_text: str,
78
+ key_arg: str,
79
+ *,
80
+ hide: bool = False,
81
+ ) -> Callable[[F], F]:
82
+ """Decorator to register a tool's category and display configuration.
83
+
84
+ Args:
85
+ category: The ToolCategory enum value for this tool
86
+ display_text: Text to show (e.g., "Reading file", "Querying code")
87
+ key_arg: Primary argument name to extract for display (e.g., "query", "filename")
88
+ hide: Whether to hide this tool call completely from the UI (default: False)
89
+
90
+ Returns:
91
+ Decorator function that registers the tool and returns it unchanged
92
+
93
+ Display Format:
94
+ - When key_arg value is missing: Shows just display_text (e.g., "Reading file")
95
+ - When key_arg value is present: Shows "display_text: key_arg_value" (e.g., "Reading file: foo.py")
96
+
97
+ Example:
98
+ @register_tool(
99
+ category=ToolCategory.CODEBASE_UNDERSTANDING,
100
+ display_text="Querying code",
101
+ key_arg="query",
102
+ )
103
+ async def query_graph(ctx: RunContext[AgentDeps], query: str) -> str:
104
+ ...
105
+ """
106
+
107
+ def decorator(func: F) -> F:
108
+ tool_name = func.__name__
109
+ _TOOL_REGISTRY[tool_name] = category
110
+ logger.debug(f"Registered tool '{tool_name}' as category '{category.value}'")
111
+
112
+ # Register display config
113
+ config = ToolDisplayConfig(
114
+ display_text=display_text,
115
+ key_arg=key_arg,
116
+ hide=hide,
117
+ )
118
+ _TOOL_DISPLAY_REGISTRY[tool_name] = config
119
+ logger.debug(f"Registered display config for tool '{tool_name}'")
120
+
121
+ return func
122
+
123
+ return decorator
124
+
125
+
126
+ # Backwards compatibility alias
127
+ tool_category = register_tool
128
+
129
+
130
+ def get_tool_category(tool_name: str) -> ToolCategory:
131
+ """Get category for a tool, logging unknown tools to Sentry.
132
+
133
+ Args:
134
+ tool_name: Name of the tool to look up
135
+
136
+ Returns:
137
+ ToolCategory enum value for the tool, or UNKNOWN if not registered
138
+ """
139
+ category = _TOOL_REGISTRY.get(tool_name)
140
+
141
+ if category is None:
142
+ logger.warning(f"Unknown tool encountered in context analysis: {tool_name}")
143
+ sentry_sdk.capture_message(
144
+ f"Unknown tool in context analysis: {tool_name}",
145
+ level="warning",
146
+ extras={"tool_name": tool_name},
147
+ )
148
+ return ToolCategory.UNKNOWN
149
+
150
+ return category
151
+
152
+
153
+ def register_special_tool(tool_name: str, category: ToolCategory) -> None:
154
+ """Register a special tool that doesn't have a decorator.
155
+
156
+ Used for tools like 'final_result' that aren't actual Python functions
157
+ but need to be categorized.
158
+
159
+ Args:
160
+ tool_name: Name of the special tool
161
+ category: Category to assign to this tool
162
+ """
163
+ _TOOL_REGISTRY[tool_name] = category
164
+ logger.debug(
165
+ f"Registered special tool '{tool_name}' as category '{category.value}'"
166
+ )
167
+
168
+
169
+ def get_tool_display_config(tool_name: str) -> ToolDisplayConfig | None:
170
+ """Get display configuration for a tool.
171
+
172
+ Args:
173
+ tool_name: Name of the tool to look up
174
+
175
+ Returns:
176
+ ToolDisplayConfig for the tool, or None if not registered
177
+ """
178
+ return _TOOL_DISPLAY_REGISTRY.get(tool_name)
179
+
180
+
181
+ def register_tool_display(
182
+ tool_name: str,
183
+ display_text: str,
184
+ key_arg: str,
185
+ *,
186
+ hide: bool = False,
187
+ ) -> None:
188
+ """Register a display config for a special tool that doesn't have a decorator.
189
+
190
+ Used for tools like 'final_result' or builtin tools that aren't actual Python functions.
191
+
192
+ Args:
193
+ tool_name: Name of the special tool
194
+ display_text: Text to show (e.g., "Reading file", "Querying code")
195
+ key_arg: Primary argument name to extract for display
196
+ hide: Whether to hide this tool call completely
197
+ """
198
+ config = ToolDisplayConfig(
199
+ display_text=display_text,
200
+ key_arg=key_arg,
201
+ hide=hide,
202
+ )
203
+ _TOOL_DISPLAY_REGISTRY[tool_name] = config
204
+ logger.debug(f"Registered display config for special tool '{tool_name}'")
205
+
206
+
207
+ # Register special tools that don't have decorators
208
+ register_special_tool("final_result", ToolCategory.AGENT_RESPONSE)
209
+ register_tool_display("final_result", display_text="", key_arg="", hide=True)
210
+
211
+ # Register builtin tools (tools that come from Pydantic AI or model providers)
212
+ # These don't have Python function definitions but need display formatting
213
+ register_tool_display(
214
+ "web_search",
215
+ display_text="Searching",
216
+ key_arg="query",
217
+ )
@@ -3,7 +3,10 @@
3
3
  Provides web search capabilities for multiple LLM providers:
4
4
  - OpenAI: Uses Responses API with web_search tool (BYOK only)
5
5
  - Anthropic: Uses Messages API with web_search_20250305 tool (BYOK only)
6
- - Gemini: Uses grounding with Google Search via Pydantic AI (works with Shotgun Account)
6
+ - Gemini: Uses grounding with Google Search via Pydantic AI (Shotgun Account and BYOK)
7
+
8
+ Shotgun Account: Only Gemini web search is available
9
+ BYOK: All tools work with direct provider API keys
7
10
  """
8
11
 
9
12
  from collections.abc import Awaitable, Callable
@@ -26,11 +29,12 @@ WebSearchTool = Callable[[str], Awaitable[str]]
26
29
  def get_available_web_search_tools() -> list[WebSearchTool]:
27
30
  """Get list of available web search tools based on configured API keys.
28
31
 
29
- When using Shotgun Account (via LiteLLM proxy):
30
- Only Gemini web search is available (others use provider-specific APIs)
32
+ Works with both Shotgun Account (via LiteLLM proxy) and BYOK (individual provider keys).
31
33
 
32
- When using BYOK (individual provider keys):
33
- All provider tools are available based on their respective keys
34
+ Available tools:
35
+ - Gemini: Available for both Shotgun Account and BYOK
36
+ - Anthropic: BYOK only (uses Messages API with web search)
37
+ - OpenAI: BYOK only (uses Responses API not compatible with LiteLLM proxy)
34
38
 
35
39
  Returns:
36
40
  List of web search tool functions that have API keys configured
@@ -43,15 +47,23 @@ def get_available_web_search_tools() -> list[WebSearchTool]:
43
47
  has_shotgun_key = config.shotgun.api_key is not None
44
48
 
45
49
  if has_shotgun_key:
46
- # Shotgun Account mode: Only Gemini supports web search via LiteLLM
50
+ logger.debug("🔑 Shotgun Account - only Gemini web search available")
51
+
52
+ # Gemini: Only search tool available for Shotgun Account
47
53
  if is_provider_available(ProviderType.GOOGLE):
48
- logger.info("🔑 Shotgun Account detected - using Gemini web search only")
49
- logger.debug(" OpenAI and Anthropic web search require direct API keys")
54
+ logger.debug(" Gemini web search tool available")
50
55
  tools.append(gemini_web_search_tool)
51
- else:
52
- logger.warning(
53
- "⚠️ Shotgun Account configured but no Gemini key - "
54
- "web search unavailable"
56
+
57
+ # Anthropic: Not available for Shotgun Account (Gemini-only for Shotgun)
58
+ if is_provider_available(ProviderType.ANTHROPIC):
59
+ logger.debug(
60
+ "⚠️ Anthropic web search requires BYOK (Shotgun Account uses Gemini only)"
61
+ )
62
+
63
+ # OpenAI: Not available for Shotgun Account (Responses API incompatible with proxy)
64
+ if is_provider_available(ProviderType.OPENAI):
65
+ logger.debug(
66
+ "⚠️ OpenAI web search requires BYOK (Responses API not supported via proxy)"
55
67
  )
56
68
  else:
57
69
  # BYOK mode: Load all available tools based on individual provider keys
@@ -8,11 +8,22 @@ from shotgun.agents.config import get_provider_model
8
8
  from shotgun.agents.config.constants import MEDIUM_TEXT_8K_TOKENS
9
9
  from shotgun.agents.config.models import ProviderType
10
10
  from shotgun.agents.llm import shotgun_model_request
11
+ from shotgun.agents.tools.registry import ToolCategory, register_tool
11
12
  from shotgun.logging_config import get_logger
13
+ from shotgun.prompts import PromptLoader
14
+ from shotgun.utils.datetime_utils import get_datetime_context
12
15
 
13
16
  logger = get_logger(__name__)
14
17
 
18
+ # Global prompt loader instance
19
+ prompt_loader = PromptLoader()
15
20
 
21
+
22
+ @register_tool(
23
+ category=ToolCategory.WEB_RESEARCH,
24
+ display_text="Searching web",
25
+ key_arg="query",
26
+ )
16
27
  async def anthropic_web_search_tool(query: str) -> str:
17
28
  """Perform a web search using Anthropic's Claude API.
18
29
 
@@ -42,10 +53,20 @@ async def anthropic_web_search_tool(query: str) -> str:
42
53
  span.set_attribute("output.value", f"**Error:**\n {error_msg}\n")
43
54
  return error_msg
44
55
 
56
+ # Get datetime context for the search prompt
57
+ dt_context = get_datetime_context()
58
+
59
+ # Render search prompt from template
60
+ search_prompt = prompt_loader.render(
61
+ "tools/web_search.j2",
62
+ query=query,
63
+ current_datetime=dt_context.datetime_formatted,
64
+ timezone_name=dt_context.timezone_name,
65
+ utc_offset=dt_context.utc_offset,
66
+ )
67
+
45
68
  # Build the request messages
46
- messages: list[ModelMessage] = [
47
- ModelRequest.user_text_prompt(f"Search for: {query}")
48
- ]
69
+ messages: list[ModelMessage] = [ModelRequest.user_text_prompt(search_prompt)]
49
70
 
50
71
  # Use the Messages API with web search tool
51
72
  try:
@@ -8,11 +8,22 @@ from shotgun.agents.config import get_provider_model
8
8
  from shotgun.agents.config.constants import MEDIUM_TEXT_8K_TOKENS
9
9
  from shotgun.agents.config.models import ModelName
10
10
  from shotgun.agents.llm import shotgun_model_request
11
+ from shotgun.agents.tools.registry import ToolCategory, register_tool
11
12
  from shotgun.logging_config import get_logger
13
+ from shotgun.prompts import PromptLoader
14
+ from shotgun.utils.datetime_utils import get_datetime_context
12
15
 
13
16
  logger = get_logger(__name__)
14
17
 
18
+ # Global prompt loader instance
19
+ prompt_loader = PromptLoader()
15
20
 
21
+
22
+ @register_tool(
23
+ category=ToolCategory.WEB_RESEARCH,
24
+ display_text="Searching web",
25
+ key_arg="query",
26
+ )
16
27
  async def gemini_web_search_tool(query: str) -> str:
17
28
  """Perform a web search using Google's Gemini API with grounding.
18
29
 
@@ -42,16 +53,17 @@ async def gemini_web_search_tool(query: str) -> str:
42
53
  span.set_attribute("output.value", f"**Error:**\n {error_msg}\n")
43
54
  return error_msg
44
55
 
45
- # Create a search-optimized prompt
46
- search_prompt = f"""Please provide current and accurate information about the following query:
47
-
48
- Query: {query}
49
-
50
- Instructions:
51
- - Provide comprehensive, factual information
52
- - Include relevant details and context
53
- - Focus on current and recent information
54
- - Be specific and accurate in your response"""
56
+ # Get datetime context for the search prompt
57
+ dt_context = get_datetime_context()
58
+
59
+ # Render search prompt from template
60
+ search_prompt = prompt_loader.render(
61
+ "tools/web_search.j2",
62
+ query=query,
63
+ current_datetime=dt_context.datetime_formatted,
64
+ timezone_name=dt_context.timezone_name,
65
+ utc_offset=dt_context.utc_offset,
66
+ )
55
67
 
56
68
  # Build the request messages
57
69
  messages: list[ModelMessage] = [ModelRequest.user_text_prompt(search_prompt)]
@@ -5,11 +5,22 @@ from opentelemetry import trace
5
5
 
6
6
  from shotgun.agents.config import get_provider_model
7
7
  from shotgun.agents.config.models import ProviderType
8
+ from shotgun.agents.tools.registry import ToolCategory, register_tool
8
9
  from shotgun.logging_config import get_logger
10
+ from shotgun.prompts import PromptLoader
11
+ from shotgun.utils.datetime_utils import get_datetime_context
9
12
 
10
13
  logger = get_logger(__name__)
11
14
 
15
+ # Global prompt loader instance
16
+ prompt_loader = PromptLoader()
12
17
 
18
+
19
+ @register_tool(
20
+ category=ToolCategory.WEB_RESEARCH,
21
+ display_text="Searching web",
22
+ key_arg="query",
23
+ )
13
24
  async def openai_web_search_tool(query: str) -> str:
14
25
  """Perform a web search and return results.
15
26
 
@@ -40,19 +51,17 @@ async def openai_web_search_tool(query: str) -> str:
40
51
  span.set_attribute("output.value", f"**Error:**\n {error_msg}\n")
41
52
  return error_msg
42
53
 
43
- prompt = f"""Please provide current and accurate information about the following query:
44
-
45
- Query: {query}
54
+ # Get datetime context for the search prompt
55
+ dt_context = get_datetime_context()
46
56
 
47
- Instructions:
48
- - Provide comprehensive, factual information
49
- - Include relevant details and context
50
- - Focus on current and recent information
51
- - Be specific and accurate in your response
52
- - You can't ask the user for details, so assume the most relevant details for the query
53
-
54
- ALWAYS PROVIDE THE SOURCES (urls) TO BACK UP THE INFORMATION YOU PROVIDE.
55
- """
57
+ # Render search prompt from template
58
+ prompt = prompt_loader.render(
59
+ "tools/web_search.j2",
60
+ query=query,
61
+ current_datetime=dt_context.datetime_formatted,
62
+ timezone_name=dt_context.timezone_name,
63
+ utc_offset=dt_context.utc_offset,
64
+ )
56
65
 
57
66
  client = AsyncOpenAI(api_key=api_key)
58
67
  response = await client.responses.create( # type: ignore[call-overload]
shotgun/api_endpoints.py CHANGED
@@ -1,10 +1,14 @@
1
1
  """Shotgun backend service API endpoints and URLs."""
2
2
 
3
+ from shotgun.settings import settings
4
+
3
5
  # Shotgun Web API base URL (for authentication/subscription)
4
- # Can be overridden with environment variable
5
- SHOTGUN_WEB_BASE_URL = "https://api-219702594231.us-east4.run.app"
6
+ # Can be overridden with SHOTGUN_WEB_BASE_URL environment variable
7
+ SHOTGUN_WEB_BASE_URL = settings.api.web_base_url
8
+
6
9
  # Shotgun's LiteLLM proxy base URL (for AI model requests)
7
- LITELLM_PROXY_BASE_URL = "https://litellm-219702594231.us-east4.run.app"
10
+ # Can be overridden with SHOTGUN_ACCOUNT_LLM_BASE_URL environment variable
11
+ LITELLM_PROXY_BASE_URL = settings.api.account_llm_base_url
8
12
 
9
13
  # Provider-specific LiteLLM proxy endpoints
10
14
  LITELLM_PROXY_ANTHROPIC_BASE = f"{LITELLM_PROXY_BASE_URL}/anthropic"
@@ -13,7 +13,7 @@ POSTHOG_PROJECT_ID = '191396'
13
13
 
14
14
  # Logfire configuration embedded at build time (only for dev builds)
15
15
  LOGFIRE_ENABLED = 'true'
16
- LOGFIRE_TOKEN = 'pylf_v1_us_KZ5NM1pP3NwgJkbBJt6Ftdzk8mMhmrXcGJHQQgDJ1LfK'
16
+ LOGFIRE_TOKEN = 'pylf_v1_us_RwZMlJm1tX6j0PL5RWWbmZpzK2hLBNtFWStNKlySfjh8'
17
17
 
18
18
  # Build metadata
19
19
  BUILD_TIME_ENV = "production" if SENTRY_DSN else "development"
shotgun/cli/clear.py ADDED
@@ -0,0 +1,52 @@
1
+ """Clear command for shotgun CLI."""
2
+
3
+ from pathlib import Path
4
+
5
+ import typer
6
+ from rich.console import Console
7
+
8
+ from shotgun.agents.conversation_manager import ConversationManager
9
+ from shotgun.logging_config import get_logger
10
+
11
+ app = typer.Typer(
12
+ name="clear", help="Clear the conversation history", no_args_is_help=False
13
+ )
14
+ logger = get_logger(__name__)
15
+ console = Console()
16
+
17
+
18
+ @app.callback(invoke_without_command=True)
19
+ def clear() -> None:
20
+ """Clear the current conversation history.
21
+
22
+ This command deletes the conversation file at ~/.shotgun-sh/conversation.json,
23
+ removing all conversation history. Other files in ~/.shotgun-sh/ (config, usage,
24
+ codebases, logs) are preserved.
25
+ """
26
+ try:
27
+ # Get conversation file path
28
+ conversation_file = Path.home() / ".shotgun-sh" / "conversation.json"
29
+
30
+ # Check if file exists
31
+ if not conversation_file.exists():
32
+ console.print(
33
+ "[yellow]No conversation file found.[/yellow] Nothing to clear.",
34
+ style="bold",
35
+ )
36
+ return
37
+
38
+ # Clear the conversation
39
+ manager = ConversationManager(conversation_file)
40
+ manager.clear()
41
+
42
+ console.print(
43
+ "[green]✓[/green] Conversation cleared successfully", style="bold"
44
+ )
45
+ logger.info("Conversation cleared successfully")
46
+
47
+ except Exception as e:
48
+ console.print(
49
+ f"[red]Error:[/red] Failed to clear conversation: {e}", style="bold"
50
+ )
51
+ logger.debug("Full traceback:", exc_info=True)
52
+ raise typer.Exit(code=1) from e