shotgun-sh 0.2.17__py3-none-any.whl → 0.3.3.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.
Files changed (112) hide show
  1. shotgun/agents/agent_manager.py +28 -14
  2. shotgun/agents/common.py +1 -1
  3. shotgun/agents/config/README.md +89 -0
  4. shotgun/agents/config/__init__.py +10 -1
  5. shotgun/agents/config/manager.py +323 -53
  6. shotgun/agents/config/models.py +85 -21
  7. shotgun/agents/config/provider.py +51 -13
  8. shotgun/agents/config/streaming_test.py +119 -0
  9. shotgun/agents/context_analyzer/analyzer.py +6 -2
  10. shotgun/agents/conversation/__init__.py +18 -0
  11. shotgun/agents/conversation/filters.py +164 -0
  12. shotgun/agents/conversation/history/chunking.py +278 -0
  13. shotgun/agents/{history → conversation/history}/compaction.py +27 -1
  14. shotgun/agents/{history → conversation/history}/constants.py +5 -0
  15. shotgun/agents/conversation/history/file_content_deduplication.py +216 -0
  16. shotgun/agents/{history → conversation/history}/history_processors.py +267 -3
  17. shotgun/agents/{history → conversation/history}/token_counting/anthropic.py +8 -0
  18. shotgun/agents/{conversation_manager.py → conversation/manager.py} +1 -1
  19. shotgun/agents/{conversation_history.py → conversation/models.py} +8 -94
  20. shotgun/agents/error/__init__.py +11 -0
  21. shotgun/agents/error/models.py +19 -0
  22. shotgun/agents/runner.py +230 -0
  23. shotgun/agents/tools/web_search/openai.py +1 -1
  24. shotgun/build_constants.py +2 -2
  25. shotgun/cli/clear.py +1 -1
  26. shotgun/cli/compact.py +5 -3
  27. shotgun/cli/context.py +44 -1
  28. shotgun/cli/error_handler.py +24 -0
  29. shotgun/cli/export.py +34 -34
  30. shotgun/cli/plan.py +34 -34
  31. shotgun/cli/research.py +17 -9
  32. shotgun/cli/spec/__init__.py +5 -0
  33. shotgun/cli/spec/backup.py +81 -0
  34. shotgun/cli/spec/commands.py +132 -0
  35. shotgun/cli/spec/models.py +48 -0
  36. shotgun/cli/spec/pull_service.py +219 -0
  37. shotgun/cli/specify.py +20 -19
  38. shotgun/cli/tasks.py +34 -34
  39. shotgun/codebase/core/ingestor.py +153 -7
  40. shotgun/codebase/models.py +2 -0
  41. shotgun/exceptions.py +325 -0
  42. shotgun/llm_proxy/__init__.py +17 -0
  43. shotgun/llm_proxy/client.py +215 -0
  44. shotgun/llm_proxy/models.py +137 -0
  45. shotgun/logging_config.py +42 -0
  46. shotgun/main.py +4 -0
  47. shotgun/posthog_telemetry.py +1 -1
  48. shotgun/prompts/agents/partials/common_agent_system_prompt.j2 +28 -3
  49. shotgun/prompts/agents/partials/interactive_mode.j2 +3 -3
  50. shotgun/prompts/agents/plan.j2 +16 -0
  51. shotgun/prompts/agents/research.j2 +16 -3
  52. shotgun/prompts/agents/specify.j2 +54 -1
  53. shotgun/prompts/agents/state/system_state.j2 +0 -2
  54. shotgun/prompts/agents/tasks.j2 +16 -0
  55. shotgun/prompts/history/chunk_summarization.j2 +34 -0
  56. shotgun/prompts/history/combine_summaries.j2 +53 -0
  57. shotgun/sdk/codebase.py +14 -3
  58. shotgun/settings.py +5 -0
  59. shotgun/shotgun_web/__init__.py +67 -1
  60. shotgun/shotgun_web/client.py +42 -1
  61. shotgun/shotgun_web/constants.py +46 -0
  62. shotgun/shotgun_web/exceptions.py +29 -0
  63. shotgun/shotgun_web/models.py +390 -0
  64. shotgun/shotgun_web/shared_specs/__init__.py +32 -0
  65. shotgun/shotgun_web/shared_specs/file_scanner.py +175 -0
  66. shotgun/shotgun_web/shared_specs/hasher.py +83 -0
  67. shotgun/shotgun_web/shared_specs/models.py +71 -0
  68. shotgun/shotgun_web/shared_specs/upload_pipeline.py +329 -0
  69. shotgun/shotgun_web/shared_specs/utils.py +34 -0
  70. shotgun/shotgun_web/specs_client.py +703 -0
  71. shotgun/shotgun_web/supabase_client.py +31 -0
  72. shotgun/tui/app.py +73 -9
  73. shotgun/tui/containers.py +1 -1
  74. shotgun/tui/layout.py +5 -0
  75. shotgun/tui/screens/chat/chat_screen.py +372 -95
  76. shotgun/tui/screens/chat/codebase_index_prompt_screen.py +196 -17
  77. shotgun/tui/screens/chat_screen/command_providers.py +13 -2
  78. shotgun/tui/screens/chat_screen/hint_message.py +76 -1
  79. shotgun/tui/screens/confirmation_dialog.py +40 -0
  80. shotgun/tui/screens/directory_setup.py +45 -41
  81. shotgun/tui/screens/feedback.py +10 -3
  82. shotgun/tui/screens/github_issue.py +11 -2
  83. shotgun/tui/screens/model_picker.py +28 -8
  84. shotgun/tui/screens/onboarding.py +149 -0
  85. shotgun/tui/screens/pipx_migration.py +58 -6
  86. shotgun/tui/screens/provider_config.py +66 -8
  87. shotgun/tui/screens/shared_specs/__init__.py +21 -0
  88. shotgun/tui/screens/shared_specs/create_spec_dialog.py +273 -0
  89. shotgun/tui/screens/shared_specs/models.py +56 -0
  90. shotgun/tui/screens/shared_specs/share_specs_dialog.py +390 -0
  91. shotgun/tui/screens/shared_specs/upload_progress_screen.py +452 -0
  92. shotgun/tui/screens/shotgun_auth.py +110 -16
  93. shotgun/tui/screens/spec_pull.py +288 -0
  94. shotgun/tui/screens/welcome.py +123 -0
  95. shotgun/tui/services/conversation_service.py +5 -2
  96. shotgun/tui/widgets/widget_coordinator.py +1 -1
  97. {shotgun_sh-0.2.17.dist-info → shotgun_sh-0.3.3.dev1.dist-info}/METADATA +9 -2
  98. {shotgun_sh-0.2.17.dist-info → shotgun_sh-0.3.3.dev1.dist-info}/RECORD +112 -77
  99. {shotgun_sh-0.2.17.dist-info → shotgun_sh-0.3.3.dev1.dist-info}/WHEEL +1 -1
  100. /shotgun/agents/{history → conversation/history}/__init__.py +0 -0
  101. /shotgun/agents/{history → conversation/history}/context_extraction.py +0 -0
  102. /shotgun/agents/{history → conversation/history}/history_building.py +0 -0
  103. /shotgun/agents/{history → conversation/history}/message_utils.py +0 -0
  104. /shotgun/agents/{history → conversation/history}/token_counting/__init__.py +0 -0
  105. /shotgun/agents/{history → conversation/history}/token_counting/base.py +0 -0
  106. /shotgun/agents/{history → conversation/history}/token_counting/openai.py +0 -0
  107. /shotgun/agents/{history → conversation/history}/token_counting/sentencepiece_counter.py +0 -0
  108. /shotgun/agents/{history → conversation/history}/token_counting/tokenizer_cache.py +0 -0
  109. /shotgun/agents/{history → conversation/history}/token_counting/utils.py +0 -0
  110. /shotgun/agents/{history → conversation/history}/token_estimation.py +0 -0
  111. {shotgun_sh-0.2.17.dist-info → shotgun_sh-0.3.3.dev1.dist-info}/entry_points.txt +0 -0
  112. {shotgun_sh-0.2.17.dist-info → shotgun_sh-0.3.3.dev1.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,31 @@
1
+ """Supabase Storage download utilities."""
2
+
3
+ import httpx
4
+
5
+ from shotgun.logging_config import get_logger
6
+
7
+ logger = get_logger(__name__)
8
+
9
+
10
+ async def download_file_from_url(download_url: str) -> bytes:
11
+ """Download a file from a presigned Supabase Storage URL.
12
+
13
+ The API returns presigned URLs with embedded tokens that don't require
14
+ any authentication headers.
15
+
16
+ Args:
17
+ download_url: Presigned Supabase Storage URL
18
+ (e.g., "https://...supabase.co/storage/v1/object/sign/...?token=...")
19
+
20
+ Returns:
21
+ File contents as bytes
22
+
23
+ Raises:
24
+ httpx.HTTPStatusError: If download fails
25
+ """
26
+ logger.debug("Downloading file from: %s", download_url)
27
+
28
+ async with httpx.AsyncClient(timeout=60.0) as client:
29
+ response = await client.get(download_url)
30
+ response.raise_for_status()
31
+ return response.content
shotgun/tui/app.py CHANGED
@@ -6,12 +6,18 @@ from textual.binding import Binding
6
6
  from textual.screen import Screen
7
7
 
8
8
  from shotgun.agents.agent_manager import AgentManager
9
- from shotgun.agents.config import ConfigManager, get_config_manager
9
+ from shotgun.agents.config import (
10
+ ConfigManager,
11
+ get_config_manager,
12
+ )
10
13
  from shotgun.agents.models import AgentType
11
14
  from shotgun.logging_config import get_logger
12
15
  from shotgun.tui.containers import TUIContainer
13
16
  from shotgun.tui.screens.splash import SplashScreen
14
- from shotgun.utils.file_system_utils import get_shotgun_base_path
17
+ from shotgun.utils.file_system_utils import (
18
+ ensure_shotgun_directory_exists,
19
+ get_shotgun_base_path,
20
+ )
15
21
  from shotgun.utils.update_checker import (
16
22
  detect_installation_method,
17
23
  perform_auto_update_async,
@@ -31,10 +37,10 @@ logger = get_logger(__name__)
31
37
  class ShotgunApp(App[None]):
32
38
  # ChatScreen removed from SCREENS dict since it requires dependency injection
33
39
  # and is instantiated manually in refresh_startup_screen()
40
+ # DirectorySetupScreen also removed since it requires error_message parameter
34
41
  SCREENS = {
35
42
  "provider_config": ProviderConfigScreen,
36
43
  "model_picker": ModelPickerScreen,
37
- "directory_setup": DirectorySetupScreen,
38
44
  "github_issue": GitHubIssueScreen,
39
45
  }
40
46
  BINDINGS = [
@@ -48,12 +54,16 @@ class ShotgunApp(App[None]):
48
54
  no_update_check: bool = False,
49
55
  continue_session: bool = False,
50
56
  force_reindex: bool = False,
57
+ show_pull_hint: bool = False,
58
+ pull_version_id: str | None = None,
51
59
  ) -> None:
52
60
  super().__init__()
53
61
  self.config_manager: ConfigManager = get_config_manager()
54
62
  self.no_update_check = no_update_check
55
63
  self.continue_session = continue_session
56
64
  self.force_reindex = force_reindex
65
+ self.show_pull_hint = show_pull_hint
66
+ self.pull_version_id = pull_version_id
57
67
 
58
68
  # Initialize dependency injection container
59
69
  self.container = TUIContainer()
@@ -71,6 +81,8 @@ class ShotgunApp(App[None]):
71
81
  "tui_started",
72
82
  {
73
83
  "installation_method": detect_installation_method(),
84
+ "terminal_width": self.size.width,
85
+ "terminal_height": self.size.height,
74
86
  },
75
87
  )
76
88
 
@@ -99,7 +111,10 @@ class ShotgunApp(App[None]):
99
111
  # Run async config loading in worker
100
112
  async def _check_config() -> None:
101
113
  # Show welcome screen if no providers are configured OR if user hasn't seen it yet
114
+ # Note: If config migration fails, ConfigManager will auto-create fresh config
115
+ # and set migration_failed flag, which WelcomeScreen will display
102
116
  config = await self.config_manager.load()
117
+
103
118
  has_any_key = await self.config_manager.has_any_provider_key()
104
119
  if not has_any_key or not config.shown_welcome_screen:
105
120
  if isinstance(self.screen, WelcomeScreen):
@@ -111,17 +126,43 @@ class ShotgunApp(App[None]):
111
126
  )
112
127
  return
113
128
 
129
+ # Try to create .shotgun directory if it doesn't exist
114
130
  if not self.check_local_shotgun_directory_exists():
115
- if isinstance(self.screen, DirectorySetupScreen):
131
+ try:
132
+ path = ensure_shotgun_directory_exists()
133
+ # Verify directory was created successfully
134
+ if not path.is_dir():
135
+ # Show error screen if creation failed
136
+ if isinstance(self.screen, DirectorySetupScreen):
137
+ return
138
+ self.push_screen(
139
+ DirectorySetupScreen(
140
+ error_message="Unable to create .shotgun directory due to filesystem conflict."
141
+ ),
142
+ callback=lambda _arg: self.refresh_startup_screen(),
143
+ )
144
+ return
145
+ except Exception as exc:
146
+ # Show error screen if creation failed with exception
147
+ if isinstance(self.screen, DirectorySetupScreen):
148
+ return
149
+ self.push_screen(
150
+ DirectorySetupScreen(error_message=str(exc)),
151
+ callback=lambda _arg: self.refresh_startup_screen(),
152
+ )
116
153
  return
117
154
 
118
- self.push_screen(
119
- DirectorySetupScreen(),
120
- callback=lambda _arg: self.refresh_startup_screen(),
121
- )
155
+ if isinstance(self.screen, ChatScreen):
122
156
  return
123
157
 
124
- if isinstance(self.screen, ChatScreen):
158
+ # If we have a version to pull, show pull screen first
159
+ if self.pull_version_id:
160
+ from .screens.spec_pull import SpecPullScreen
161
+
162
+ self.push_screen(
163
+ SpecPullScreen(self.pull_version_id),
164
+ callback=self._handle_pull_complete,
165
+ )
125
166
  return
126
167
 
127
168
  # Create ChatScreen with all dependencies injected from container
@@ -155,6 +196,7 @@ class ShotgunApp(App[None]):
155
196
  deps=agent_deps,
156
197
  continue_session=self.continue_session,
157
198
  force_reindex=self.force_reindex,
199
+ show_pull_hint=self.show_pull_hint,
158
200
  )
159
201
 
160
202
  # Update the ProcessingStateManager and WidgetCoordinator with the actual ChatScreen instance
@@ -170,6 +212,22 @@ class ShotgunApp(App[None]):
170
212
  shotgun_dir = get_shotgun_base_path()
171
213
  return shotgun_dir.exists() and shotgun_dir.is_dir()
172
214
 
215
+ def _handle_pull_complete(self, success: bool | None) -> None:
216
+ """Handle completion of spec pull screen.
217
+
218
+ Args:
219
+ success: Whether the pull was successful, or None if dismissed.
220
+ """
221
+ # Clear version_id so we don't pull again on next refresh
222
+ self.pull_version_id = None
223
+
224
+ if success:
225
+ # Enable hint for ChatScreen
226
+ self.show_pull_hint = True
227
+
228
+ # Continue to ChatScreen
229
+ self.refresh_startup_screen()
230
+
173
231
  async def action_quit(self) -> None:
174
232
  """Quit the application."""
175
233
  # Shut down PostHog client to prevent threading errors
@@ -196,6 +254,8 @@ def run(
196
254
  no_update_check: bool = False,
197
255
  continue_session: bool = False,
198
256
  force_reindex: bool = False,
257
+ show_pull_hint: bool = False,
258
+ pull_version_id: str | None = None,
199
259
  ) -> None:
200
260
  """Run the TUI application.
201
261
 
@@ -203,6 +263,8 @@ def run(
203
263
  no_update_check: If True, disable automatic update checks.
204
264
  continue_session: If True, continue from previous conversation.
205
265
  force_reindex: If True, force re-indexing of codebase (ignores existing index).
266
+ show_pull_hint: If True, show hint about recently pulled spec.
267
+ pull_version_id: If provided, pull this spec version before showing ChatScreen.
206
268
  """
207
269
  # Clean up any corrupted databases BEFORE starting the TUI
208
270
  # This prevents crashes from corrupted databases during initialization
@@ -228,6 +290,8 @@ def run(
228
290
  no_update_check=no_update_check,
229
291
  continue_session=continue_session,
230
292
  force_reindex=force_reindex,
293
+ show_pull_hint=show_pull_hint,
294
+ pull_version_id=pull_version_id,
231
295
  )
232
296
  app.run(inline_no_clear=True)
233
297
 
shotgun/tui/containers.py CHANGED
@@ -5,7 +5,7 @@ from typing import TYPE_CHECKING
5
5
  from dependency_injector import containers, providers
6
6
  from pydantic_ai import RunContext
7
7
 
8
- from shotgun.agents.conversation_manager import ConversationManager
8
+ from shotgun.agents.conversation import ConversationManager
9
9
  from shotgun.agents.models import AgentDeps
10
10
  from shotgun.sdk.codebase import CodebaseSDK
11
11
  from shotgun.tui.commands import CommandHandler
shotgun/tui/layout.py ADDED
@@ -0,0 +1,5 @@
1
+ """Layout utilities for responsive terminal UI."""
2
+
3
+ # Height thresholds for responsive layouts
4
+ TINY_HEIGHT_THRESHOLD = 25 # Below this: minimal UI, hide most content
5
+ COMPACT_HEIGHT_THRESHOLD = 35 # Below this: reduced padding, hide verbose text