shotgun-sh 0.2.11.dev3__py3-none-any.whl → 0.2.19__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 (39) hide show
  1. shotgun/agents/agent_manager.py +66 -12
  2. shotgun/agents/config/README.md +89 -0
  3. shotgun/agents/config/__init__.py +10 -1
  4. shotgun/agents/config/manager.py +287 -32
  5. shotgun/agents/config/models.py +21 -1
  6. shotgun/agents/config/provider.py +27 -0
  7. shotgun/agents/config/streaming_test.py +119 -0
  8. shotgun/agents/conversation_manager.py +14 -7
  9. shotgun/agents/history/history_processors.py +99 -3
  10. shotgun/agents/history/token_counting/openai.py +3 -1
  11. shotgun/build_constants.py +3 -3
  12. shotgun/exceptions.py +32 -0
  13. shotgun/logging_config.py +42 -0
  14. shotgun/main.py +2 -0
  15. shotgun/posthog_telemetry.py +18 -25
  16. shotgun/prompts/agents/partials/common_agent_system_prompt.j2 +3 -2
  17. shotgun/sentry_telemetry.py +157 -1
  18. shotgun/settings.py +5 -0
  19. shotgun/tui/app.py +16 -15
  20. shotgun/tui/screens/chat/chat_screen.py +156 -61
  21. shotgun/tui/screens/chat_screen/command_providers.py +13 -2
  22. shotgun/tui/screens/chat_screen/history/chat_history.py +1 -2
  23. shotgun/tui/screens/directory_setup.py +14 -5
  24. shotgun/tui/screens/feedback.py +10 -3
  25. shotgun/tui/screens/github_issue.py +111 -0
  26. shotgun/tui/screens/model_picker.py +8 -1
  27. shotgun/tui/screens/onboarding.py +431 -0
  28. shotgun/tui/screens/pipx_migration.py +12 -6
  29. shotgun/tui/screens/provider_config.py +25 -8
  30. shotgun/tui/screens/shotgun_auth.py +0 -10
  31. shotgun/tui/screens/welcome.py +32 -0
  32. shotgun/tui/services/conversation_service.py +8 -6
  33. shotgun/tui/widgets/widget_coordinator.py +3 -2
  34. shotgun_sh-0.2.19.dist-info/METADATA +465 -0
  35. {shotgun_sh-0.2.11.dev3.dist-info → shotgun_sh-0.2.19.dist-info}/RECORD +38 -33
  36. shotgun_sh-0.2.11.dev3.dist-info/METADATA +0 -130
  37. {shotgun_sh-0.2.11.dev3.dist-info → shotgun_sh-0.2.19.dist-info}/WHEEL +0 -0
  38. {shotgun_sh-0.2.11.dev3.dist-info → shotgun_sh-0.2.19.dist-info}/entry_points.txt +0 -0
  39. {shotgun_sh-0.2.11.dev3.dist-info → shotgun_sh-0.2.19.dist-info}/licenses/LICENSE +0 -0
@@ -5,6 +5,7 @@ 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.chat_screen.hint_message import HintMessage
8
9
  from shotgun.tui.screens.model_picker import ModelPickerScreen
9
10
  from shotgun.tui.screens.provider_config import ProviderConfigScreen
10
11
 
@@ -271,8 +272,8 @@ class DeleteCodebasePaletteProvider(Provider):
271
272
  try:
272
273
  result = await self.chat_screen.codebase_sdk.list_codebases()
273
274
  except Exception as exc: # pragma: no cover - defensive UI path
274
- self.chat_screen.notify(
275
- f"Unable to load codebases: {exc}", severity="error"
275
+ self.chat_screen.agent_manager.add_hint_message(
276
+ HintMessage(message=f"Unable to load codebases: {exc}")
276
277
  )
277
278
  return []
278
279
  return result.graphs
@@ -369,6 +370,11 @@ class UnifiedCommandProvider(Provider):
369
370
  self.chat_screen.action_show_usage,
370
371
  help="Display usage information for the current session",
371
372
  )
373
+ yield DiscoveryHit(
374
+ "View Onboarding",
375
+ self.chat_screen.action_view_onboarding,
376
+ help="View the onboarding tutorial and helpful resources",
377
+ )
372
378
 
373
379
  async def search(self, query: str) -> AsyncGenerator[Hit, None]:
374
380
  """Search for commands in alphabetical order."""
@@ -416,6 +422,11 @@ class UnifiedCommandProvider(Provider):
416
422
  self.chat_screen.action_show_usage,
417
423
  "Display usage information for the current session",
418
424
  ),
425
+ (
426
+ "View Onboarding",
427
+ self.chat_screen.action_view_onboarding,
428
+ "View the onboarding tutorial and helpful resources",
429
+ ),
419
430
  ]
420
431
 
421
432
  for title, callback, help_text in commands:
@@ -47,7 +47,6 @@ class ChatHistory(Widget):
47
47
  super().__init__()
48
48
  self.items: Sequence[ModelMessage | HintMessage] = []
49
49
  self.vertical_tail: VerticalTail | None = None
50
- self.partial_response = None
51
50
  self._rendered_count = 0 # Track how many messages have been mounted
52
51
 
53
52
  def compose(self) -> ComposeResult:
@@ -63,7 +62,7 @@ class ChatHistory(Widget):
63
62
  yield HintMessageWidget(item)
64
63
  elif isinstance(item, ModelResponse):
65
64
  yield AgentResponseWidget(item)
66
- yield PartialResponseWidget(self.partial_response).data_bind(
65
+ yield PartialResponseWidget(None).data_bind(
67
66
  item=ChatHistory.partial_response
68
67
  )
69
68
 
@@ -8,7 +8,7 @@ from textual import on
8
8
  from textual.app import ComposeResult
9
9
  from textual.containers import Horizontal, Vertical
10
10
  from textual.screen import Screen
11
- from textual.widgets import Button, Static
11
+ from textual.widgets import Button, Label, Static
12
12
 
13
13
  from shotgun.utils.file_system_utils import ensure_shotgun_directory_exists
14
14
 
@@ -56,6 +56,14 @@ class DirectorySetupScreen(Screen[None]):
56
56
  #directory-actions > * {
57
57
  margin-right: 2;
58
58
  }
59
+
60
+ #directory-status {
61
+ height: auto;
62
+ padding: 0 1;
63
+ min-height: 1;
64
+ color: $error;
65
+ text-align: center;
66
+ }
59
67
  """
60
68
 
61
69
  BINDINGS = [
@@ -69,6 +77,7 @@ class DirectorySetupScreen(Screen[None]):
69
77
  yield Static("Shotgun keeps workspace data in a .shotgun directory.\n")
70
78
  yield Static("Initialize it in the current directory?\n")
71
79
  yield Static(f"[$foreground-muted]({Path.cwd().resolve()})[/]")
80
+ yield Label("", id="directory-status")
72
81
  with Horizontal(id="directory-actions"):
73
82
  yield Button(
74
83
  "Initialize and proceed \\[ENTER]", variant="primary", id="initialize"
@@ -93,17 +102,17 @@ class DirectorySetupScreen(Screen[None]):
93
102
  self._exit_application()
94
103
 
95
104
  def _initialize_directory(self) -> None:
105
+ status_label = self.query_one("#directory-status", Label)
96
106
  try:
97
107
  path = ensure_shotgun_directory_exists()
98
108
  except Exception as exc: # pragma: no cover - defensive; textual path
99
- self.notify(f"Failed to initialize directory: {exc}", severity="error")
109
+ status_label.update(f"Failed to initialize directory: {exc}")
100
110
  return
101
111
 
102
112
  # Double-check a directory now exists; guard against unexpected filesystem state.
103
113
  if not path.is_dir():
104
- self.notify(
105
- "Unable to initialize .shotgun directory due to filesystem conflict.",
106
- severity="error",
114
+ status_label.update(
115
+ "Unable to initialize .shotgun directory due to filesystem conflict."
107
116
  )
108
117
  return
109
118
 
@@ -76,6 +76,13 @@ class FeedbackScreen(Screen[Feedback | None]):
76
76
  #feedback-type-list {
77
77
  padding: 1;
78
78
  }
79
+
80
+ #feedback-status {
81
+ height: auto;
82
+ padding: 0 1;
83
+ min-height: 1;
84
+ color: $error;
85
+ }
79
86
  """
80
87
 
81
88
  BINDINGS = [
@@ -96,6 +103,7 @@ class FeedbackScreen(Screen[Feedback | None]):
96
103
  "",
97
104
  id="feedback-description",
98
105
  )
106
+ yield Label("", id="feedback-status")
99
107
  with Horizontal(id="feedback-actions"):
100
108
  yield Button("Submit", variant="primary", id="submit")
101
109
  yield Button("Cancel \\[ESC]", id="cancel")
@@ -176,9 +184,8 @@ class FeedbackScreen(Screen[Feedback | None]):
176
184
  description = text_area.text.strip()
177
185
 
178
186
  if not description:
179
- self.notify(
180
- "Please enter a description before submitting.", severity="error"
181
- )
187
+ status_label = self.query_one("#feedback-status", Label)
188
+ status_label.update("Please enter a description before submitting.")
182
189
  return
183
190
 
184
191
  app = cast("ShotgunApp", self.app)
@@ -0,0 +1,111 @@
1
+ """Screen for guiding users to create GitHub issues."""
2
+
3
+ import webbrowser
4
+
5
+ from textual import on
6
+ from textual.app import ComposeResult
7
+ from textual.containers import Container, Vertical
8
+ from textual.screen import ModalScreen
9
+ from textual.widgets import Button, Label, Markdown, Static
10
+
11
+
12
+ class GitHubIssueScreen(ModalScreen[None]):
13
+ """Guide users to create issues on GitHub."""
14
+
15
+ CSS = """
16
+ GitHubIssueScreen {
17
+ align: center middle;
18
+ }
19
+
20
+ #issue-container {
21
+ width: 70;
22
+ max-width: 100;
23
+ height: auto;
24
+ border: thick $primary;
25
+ background: $surface;
26
+ padding: 2;
27
+ }
28
+
29
+ #issue-header {
30
+ text-style: bold;
31
+ color: $text-accent;
32
+ padding-bottom: 1;
33
+ text-align: center;
34
+ }
35
+
36
+ #issue-content {
37
+ padding: 1 0;
38
+ }
39
+
40
+ #issue-buttons {
41
+ height: auto;
42
+ padding: 2 0 0 0;
43
+ align: center middle;
44
+ }
45
+
46
+ #issue-buttons Button {
47
+ margin: 1 1;
48
+ min-width: 20;
49
+ }
50
+
51
+ #issue-status {
52
+ height: auto;
53
+ padding: 1;
54
+ min-height: 1;
55
+ text-align: center;
56
+ }
57
+ """
58
+
59
+ BINDINGS = [
60
+ ("escape", "dismiss", "Close"),
61
+ ]
62
+
63
+ def compose(self) -> ComposeResult:
64
+ """Compose the GitHub issue screen."""
65
+ with Container(id="issue-container"):
66
+ yield Static("Create a GitHub Issue", id="issue-header")
67
+ with Vertical(id="issue-content"):
68
+ yield Markdown(
69
+ """
70
+ ## Report Bugs or Request Features
71
+
72
+ We track all bugs, feature requests, and improvements on GitHub Issues.
73
+
74
+ ### How to Create an Issue:
75
+
76
+ 1. Click the button below to open our GitHub Issues page
77
+ 2. Click **"New Issue"**
78
+ 3. Choose a template:
79
+ - **Bug Report** - Report a bug or unexpected behavior
80
+ - **Feature Request** - Suggest new functionality
81
+ - **Documentation** - Report docs issues or improvements
82
+ 4. Fill in the details and submit
83
+
84
+ We review all issues and will respond as soon as possible!
85
+
86
+ ### Before Creating an Issue:
87
+
88
+ - Search existing issues to avoid duplicates
89
+ - Include steps to reproduce for bugs
90
+ - Be specific about what you'd like for feature requests
91
+ """,
92
+ id="issue-markdown",
93
+ )
94
+ with Vertical(id="issue-buttons"):
95
+ yield Label("", id="issue-status")
96
+ yield Button(
97
+ "🐙 Open GitHub Issues", id="github-button", variant="primary"
98
+ )
99
+ yield Button("Close", id="close-button")
100
+
101
+ @on(Button.Pressed, "#github-button")
102
+ def handle_github(self) -> None:
103
+ """Open GitHub issues page in browser."""
104
+ webbrowser.open("https://github.com/shotgun-sh/shotgun/issues")
105
+ status_label = self.query_one("#issue-status", Label)
106
+ status_label.update("✓ Opening GitHub Issues in your browser...")
107
+
108
+ @on(Button.Pressed, "#close-button")
109
+ def handle_close(self) -> None:
110
+ """Handle close button press."""
111
+ self.dismiss()
@@ -72,6 +72,11 @@ class ModelPickerScreen(Screen[ModelConfigUpdated | None]):
72
72
  padding: 1 0;
73
73
  }
74
74
  }
75
+ #model-picker-status {
76
+ height: auto;
77
+ padding: 0 1;
78
+ color: $error;
79
+ }
75
80
  #model-actions {
76
81
  padding: 1;
77
82
  }
@@ -94,6 +99,7 @@ class ModelPickerScreen(Screen[ModelConfigUpdated | None]):
94
99
  id="model-picker-summary",
95
100
  )
96
101
  yield ListView(id="model-list")
102
+ yield Label("", id="model-picker-status")
97
103
  with Horizontal(id="model-actions"):
98
104
  yield Button("Select \\[ENTER]", variant="primary", id="select")
99
105
  yield Button("Done \\[ESC]", id="done")
@@ -349,4 +355,5 @@ class ModelPickerScreen(Screen[ModelConfigUpdated | None]):
349
355
  )
350
356
  )
351
357
  except Exception as exc: # pragma: no cover - defensive; textual path
352
- self.notify(f"Failed to select model: {exc}", severity="error")
358
+ status_label = self.query_one("#model-picker-status", Label)
359
+ status_label.update(f"❌ Failed to select model: {exc}")