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.
- shotgun/agents/agent_manager.py +66 -12
- shotgun/agents/config/README.md +89 -0
- shotgun/agents/config/__init__.py +10 -1
- shotgun/agents/config/manager.py +287 -32
- shotgun/agents/config/models.py +21 -1
- shotgun/agents/config/provider.py +27 -0
- shotgun/agents/config/streaming_test.py +119 -0
- shotgun/agents/conversation_manager.py +14 -7
- shotgun/agents/history/history_processors.py +99 -3
- shotgun/agents/history/token_counting/openai.py +3 -1
- shotgun/build_constants.py +3 -3
- shotgun/exceptions.py +32 -0
- shotgun/logging_config.py +42 -0
- shotgun/main.py +2 -0
- shotgun/posthog_telemetry.py +18 -25
- shotgun/prompts/agents/partials/common_agent_system_prompt.j2 +3 -2
- shotgun/sentry_telemetry.py +157 -1
- shotgun/settings.py +5 -0
- shotgun/tui/app.py +16 -15
- shotgun/tui/screens/chat/chat_screen.py +156 -61
- shotgun/tui/screens/chat_screen/command_providers.py +13 -2
- shotgun/tui/screens/chat_screen/history/chat_history.py +1 -2
- shotgun/tui/screens/directory_setup.py +14 -5
- shotgun/tui/screens/feedback.py +10 -3
- shotgun/tui/screens/github_issue.py +111 -0
- shotgun/tui/screens/model_picker.py +8 -1
- shotgun/tui/screens/onboarding.py +431 -0
- shotgun/tui/screens/pipx_migration.py +12 -6
- shotgun/tui/screens/provider_config.py +25 -8
- shotgun/tui/screens/shotgun_auth.py +0 -10
- shotgun/tui/screens/welcome.py +32 -0
- shotgun/tui/services/conversation_service.py +8 -6
- shotgun/tui/widgets/widget_coordinator.py +3 -2
- shotgun_sh-0.2.19.dist-info/METADATA +465 -0
- {shotgun_sh-0.2.11.dev3.dist-info → shotgun_sh-0.2.19.dist-info}/RECORD +38 -33
- shotgun_sh-0.2.11.dev3.dist-info/METADATA +0 -130
- {shotgun_sh-0.2.11.dev3.dist-info → shotgun_sh-0.2.19.dist-info}/WHEEL +0 -0
- {shotgun_sh-0.2.11.dev3.dist-info → shotgun_sh-0.2.19.dist-info}/entry_points.txt +0 -0
- {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.
|
|
275
|
-
f"Unable to load codebases: {exc}"
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
shotgun/tui/screens/feedback.py
CHANGED
|
@@ -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.
|
|
180
|
-
|
|
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.
|
|
358
|
+
status_label = self.query_one("#model-picker-status", Label)
|
|
359
|
+
status_label.update(f"❌ Failed to select model: {exc}")
|