shotgun-sh 0.2.23.dev1__py3-none-any.whl → 0.2.29.dev2__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 +3 -3
- shotgun/agents/common.py +1 -1
- shotgun/agents/config/manager.py +36 -21
- shotgun/agents/config/models.py +30 -0
- shotgun/agents/config/provider.py +27 -14
- shotgun/agents/context_analyzer/analyzer.py +6 -2
- shotgun/agents/conversation/__init__.py +18 -0
- shotgun/agents/conversation/filters.py +164 -0
- shotgun/agents/conversation/history/chunking.py +278 -0
- shotgun/agents/{history → conversation/history}/compaction.py +27 -1
- shotgun/agents/{history → conversation/history}/constants.py +5 -0
- shotgun/agents/conversation/history/file_content_deduplication.py +216 -0
- shotgun/agents/{history → conversation/history}/history_processors.py +267 -3
- shotgun/agents/{conversation_manager.py → conversation/manager.py} +1 -1
- shotgun/agents/{conversation_history.py → conversation/models.py} +8 -94
- shotgun/agents/tools/web_search/openai.py +1 -1
- shotgun/cli/clear.py +1 -1
- shotgun/cli/compact.py +5 -3
- shotgun/cli/context.py +1 -1
- shotgun/cli/spec/__init__.py +5 -0
- shotgun/cli/spec/backup.py +81 -0
- shotgun/cli/spec/commands.py +130 -0
- shotgun/cli/spec/models.py +30 -0
- shotgun/cli/spec/pull_service.py +165 -0
- shotgun/codebase/core/ingestor.py +153 -7
- shotgun/codebase/models.py +2 -0
- shotgun/exceptions.py +5 -3
- shotgun/main.py +2 -0
- shotgun/posthog_telemetry.py +1 -1
- shotgun/prompts/agents/partials/common_agent_system_prompt.j2 +3 -3
- shotgun/prompts/agents/partials/interactive_mode.j2 +3 -3
- shotgun/prompts/agents/research.j2 +0 -3
- shotgun/prompts/history/chunk_summarization.j2 +34 -0
- shotgun/prompts/history/combine_summaries.j2 +53 -0
- shotgun/shotgun_web/__init__.py +67 -1
- shotgun/shotgun_web/client.py +42 -1
- shotgun/shotgun_web/constants.py +46 -0
- shotgun/shotgun_web/exceptions.py +29 -0
- shotgun/shotgun_web/models.py +390 -0
- shotgun/shotgun_web/shared_specs/__init__.py +32 -0
- shotgun/shotgun_web/shared_specs/file_scanner.py +175 -0
- shotgun/shotgun_web/shared_specs/hasher.py +83 -0
- shotgun/shotgun_web/shared_specs/models.py +71 -0
- shotgun/shotgun_web/shared_specs/upload_pipeline.py +291 -0
- shotgun/shotgun_web/shared_specs/utils.py +34 -0
- shotgun/shotgun_web/specs_client.py +703 -0
- shotgun/shotgun_web/supabase_client.py +31 -0
- shotgun/tui/app.py +39 -0
- shotgun/tui/containers.py +1 -1
- shotgun/tui/layout.py +5 -0
- shotgun/tui/screens/chat/chat_screen.py +212 -16
- shotgun/tui/screens/chat/codebase_index_prompt_screen.py +147 -19
- shotgun/tui/screens/chat_screen/command_providers.py +10 -0
- shotgun/tui/screens/chat_screen/history/chat_history.py +0 -36
- shotgun/tui/screens/confirmation_dialog.py +40 -0
- shotgun/tui/screens/model_picker.py +7 -1
- shotgun/tui/screens/onboarding.py +149 -0
- shotgun/tui/screens/pipx_migration.py +46 -0
- shotgun/tui/screens/provider_config.py +41 -0
- shotgun/tui/screens/shared_specs/__init__.py +21 -0
- shotgun/tui/screens/shared_specs/create_spec_dialog.py +273 -0
- shotgun/tui/screens/shared_specs/models.py +56 -0
- shotgun/tui/screens/shared_specs/share_specs_dialog.py +390 -0
- shotgun/tui/screens/shared_specs/upload_progress_screen.py +452 -0
- shotgun/tui/screens/shotgun_auth.py +60 -6
- shotgun/tui/screens/spec_pull.py +286 -0
- shotgun/tui/screens/welcome.py +91 -0
- shotgun/tui/services/conversation_service.py +5 -2
- shotgun/tui/widgets/widget_coordinator.py +1 -1
- {shotgun_sh-0.2.23.dev1.dist-info → shotgun_sh-0.2.29.dev2.dist-info}/METADATA +1 -1
- {shotgun_sh-0.2.23.dev1.dist-info → shotgun_sh-0.2.29.dev2.dist-info}/RECORD +86 -59
- {shotgun_sh-0.2.23.dev1.dist-info → shotgun_sh-0.2.29.dev2.dist-info}/WHEEL +1 -1
- /shotgun/agents/{history → conversation/history}/__init__.py +0 -0
- /shotgun/agents/{history → conversation/history}/context_extraction.py +0 -0
- /shotgun/agents/{history → conversation/history}/history_building.py +0 -0
- /shotgun/agents/{history → conversation/history}/message_utils.py +0 -0
- /shotgun/agents/{history → conversation/history}/token_counting/__init__.py +0 -0
- /shotgun/agents/{history → conversation/history}/token_counting/anthropic.py +0 -0
- /shotgun/agents/{history → conversation/history}/token_counting/base.py +0 -0
- /shotgun/agents/{history → conversation/history}/token_counting/openai.py +0 -0
- /shotgun/agents/{history → conversation/history}/token_counting/sentencepiece_counter.py +0 -0
- /shotgun/agents/{history → conversation/history}/token_counting/tokenizer_cache.py +0 -0
- /shotgun/agents/{history → conversation/history}/token_counting/utils.py +0 -0
- /shotgun/agents/{history → conversation/history}/token_estimation.py +0 -0
- {shotgun_sh-0.2.23.dev1.dist-info → shotgun_sh-0.2.29.dev2.dist-info}/entry_points.txt +0 -0
- {shotgun_sh-0.2.23.dev1.dist-info → shotgun_sh-0.2.29.dev2.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""Shared specs TUI screens and dialogs."""
|
|
2
|
+
|
|
3
|
+
from shotgun.tui.screens.shared_specs.create_spec_dialog import CreateSpecDialog
|
|
4
|
+
from shotgun.tui.screens.shared_specs.models import (
|
|
5
|
+
CreateSpecResult,
|
|
6
|
+
ShareSpecsAction,
|
|
7
|
+
ShareSpecsResult,
|
|
8
|
+
UploadScreenResult,
|
|
9
|
+
)
|
|
10
|
+
from shotgun.tui.screens.shared_specs.share_specs_dialog import ShareSpecsDialog
|
|
11
|
+
from shotgun.tui.screens.shared_specs.upload_progress_screen import UploadProgressScreen
|
|
12
|
+
|
|
13
|
+
__all__ = [
|
|
14
|
+
"CreateSpecDialog",
|
|
15
|
+
"CreateSpecResult",
|
|
16
|
+
"ShareSpecsAction",
|
|
17
|
+
"ShareSpecsDialog",
|
|
18
|
+
"ShareSpecsResult",
|
|
19
|
+
"UploadProgressScreen",
|
|
20
|
+
"UploadScreenResult",
|
|
21
|
+
]
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
"""Dialog for creating a new spec."""
|
|
2
|
+
|
|
3
|
+
from textual import on
|
|
4
|
+
from textual.app import ComposeResult
|
|
5
|
+
from textual.containers import Container, Horizontal
|
|
6
|
+
from textual.events import Resize
|
|
7
|
+
from textual.screen import ModalScreen
|
|
8
|
+
from textual.widgets import Button, Input, Label, Static, TextArea
|
|
9
|
+
|
|
10
|
+
from shotgun.logging_config import get_logger
|
|
11
|
+
from shotgun.tui.layout import COMPACT_HEIGHT_THRESHOLD
|
|
12
|
+
from shotgun.tui.screens.shared_specs.models import CreateSpecResult
|
|
13
|
+
|
|
14
|
+
logger = get_logger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class CreateSpecDialog(ModalScreen[CreateSpecResult | None]):
|
|
18
|
+
"""Dialog for creating a new spec.
|
|
19
|
+
|
|
20
|
+
Shows a form with:
|
|
21
|
+
- Name input (required)
|
|
22
|
+
- Description textarea (optional)
|
|
23
|
+
- "Make public?" toggle (default: off)
|
|
24
|
+
|
|
25
|
+
Returns CreateSpecResult or None if cancelled.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
DEFAULT_CSS = """
|
|
29
|
+
CreateSpecDialog {
|
|
30
|
+
align: center middle;
|
|
31
|
+
background: rgba(0, 0, 0, 0.0);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
CreateSpecDialog > #dialog-container {
|
|
35
|
+
width: 70%;
|
|
36
|
+
max-width: 80;
|
|
37
|
+
height: auto;
|
|
38
|
+
border: wide $primary;
|
|
39
|
+
padding: 1 2;
|
|
40
|
+
layout: vertical;
|
|
41
|
+
background: $surface;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
#dialog-title {
|
|
45
|
+
text-style: bold;
|
|
46
|
+
color: $text-accent;
|
|
47
|
+
padding-bottom: 1;
|
|
48
|
+
text-align: center;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.form-row {
|
|
52
|
+
height: auto;
|
|
53
|
+
padding: 1 0;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.form-label {
|
|
57
|
+
padding-bottom: 0;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.form-hint {
|
|
61
|
+
color: $text-muted;
|
|
62
|
+
padding-top: 0;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
#name-input {
|
|
66
|
+
width: 100%;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
#description-input {
|
|
70
|
+
width: 100%;
|
|
71
|
+
height: 5;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.switch-row {
|
|
75
|
+
layout: horizontal;
|
|
76
|
+
height: auto;
|
|
77
|
+
padding: 1 0;
|
|
78
|
+
align-vertical: middle;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.switch-row Switch {
|
|
82
|
+
margin-right: 1;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.switch-label {
|
|
86
|
+
padding-left: 1;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.switch-hint {
|
|
90
|
+
color: $text-muted;
|
|
91
|
+
padding-left: 1;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
#error-label {
|
|
95
|
+
color: $error;
|
|
96
|
+
padding: 1 0;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
#dialog-buttons {
|
|
100
|
+
layout: horizontal;
|
|
101
|
+
align-horizontal: right;
|
|
102
|
+
height: auto;
|
|
103
|
+
padding-top: 1;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
#dialog-buttons Button {
|
|
107
|
+
margin-left: 1;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/* Compact styles for short terminals */
|
|
111
|
+
CreateSpecDialog.compact #dialog-container {
|
|
112
|
+
padding: 0 2;
|
|
113
|
+
max-height: 98%;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
CreateSpecDialog.compact #dialog-title {
|
|
117
|
+
padding-bottom: 0;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
CreateSpecDialog.compact .form-row {
|
|
121
|
+
padding: 0;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
CreateSpecDialog.compact .form-hint {
|
|
125
|
+
display: none;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
CreateSpecDialog.compact #description-input {
|
|
129
|
+
height: 3;
|
|
130
|
+
}
|
|
131
|
+
"""
|
|
132
|
+
|
|
133
|
+
BINDINGS = [
|
|
134
|
+
("escape", "cancel", "Cancel"),
|
|
135
|
+
]
|
|
136
|
+
|
|
137
|
+
def __init__(self) -> None:
|
|
138
|
+
"""Initialize the dialog."""
|
|
139
|
+
super().__init__()
|
|
140
|
+
|
|
141
|
+
def compose(self) -> ComposeResult:
|
|
142
|
+
"""Compose the dialog widgets."""
|
|
143
|
+
with Container(id="dialog-container"):
|
|
144
|
+
yield Label("Create new spec", id="dialog-title")
|
|
145
|
+
|
|
146
|
+
# Name field
|
|
147
|
+
with Container(classes="form-row"):
|
|
148
|
+
yield Label("Name", classes="form-label")
|
|
149
|
+
yield Input(
|
|
150
|
+
placeholder="My Spec",
|
|
151
|
+
id="name-input",
|
|
152
|
+
)
|
|
153
|
+
yield Static(
|
|
154
|
+
"A unique name for your spec (1-255 characters)",
|
|
155
|
+
classes="form-hint",
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
# Description field
|
|
159
|
+
with Container(classes="form-row"):
|
|
160
|
+
yield Label("Description (optional)", classes="form-label")
|
|
161
|
+
yield TextArea(
|
|
162
|
+
id="description-input",
|
|
163
|
+
)
|
|
164
|
+
yield Static(
|
|
165
|
+
"Describe what this spec contains",
|
|
166
|
+
classes="form-hint",
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
# Public toggle - hidden for now
|
|
170
|
+
# with Container(classes="switch-row"):
|
|
171
|
+
# yield Switch(value=False, id="public-switch")
|
|
172
|
+
# yield Label("Make public", classes="switch-label")
|
|
173
|
+
# yield Static(
|
|
174
|
+
# "Public specs can be viewed by anyone with the link",
|
|
175
|
+
# classes="switch-hint",
|
|
176
|
+
# )
|
|
177
|
+
|
|
178
|
+
# Error label
|
|
179
|
+
yield Static("", id="error-label")
|
|
180
|
+
|
|
181
|
+
# Buttons
|
|
182
|
+
with Horizontal(id="dialog-buttons"):
|
|
183
|
+
yield Button("Create", variant="primary", id="create")
|
|
184
|
+
yield Button("Cancel", id="cancel")
|
|
185
|
+
|
|
186
|
+
def on_mount(self) -> None:
|
|
187
|
+
"""Focus name input when dialog is mounted."""
|
|
188
|
+
self.query_one("#name-input", Input).focus()
|
|
189
|
+
self.query_one("#error-label", Static).display = False
|
|
190
|
+
|
|
191
|
+
# Apply compact layout if starting in a short terminal
|
|
192
|
+
self._apply_compact_layout(self.app.size.height < COMPACT_HEIGHT_THRESHOLD)
|
|
193
|
+
|
|
194
|
+
@on(Resize)
|
|
195
|
+
def handle_resize(self, event: Resize) -> None:
|
|
196
|
+
"""Adjust layout based on terminal height."""
|
|
197
|
+
self._apply_compact_layout(event.size.height < COMPACT_HEIGHT_THRESHOLD)
|
|
198
|
+
|
|
199
|
+
def _apply_compact_layout(self, compact: bool) -> None:
|
|
200
|
+
"""Apply or remove compact layout class for short terminals."""
|
|
201
|
+
if compact:
|
|
202
|
+
self.add_class("compact")
|
|
203
|
+
else:
|
|
204
|
+
self.remove_class("compact")
|
|
205
|
+
|
|
206
|
+
def _validate(self) -> str | None:
|
|
207
|
+
"""Validate the form.
|
|
208
|
+
|
|
209
|
+
Returns:
|
|
210
|
+
Error message if validation fails, None if valid.
|
|
211
|
+
"""
|
|
212
|
+
name = self.query_one("#name-input", Input).value.strip()
|
|
213
|
+
|
|
214
|
+
if not name:
|
|
215
|
+
return "Name is required"
|
|
216
|
+
|
|
217
|
+
if len(name) > 255:
|
|
218
|
+
return "Name must be 255 characters or less"
|
|
219
|
+
|
|
220
|
+
return None
|
|
221
|
+
|
|
222
|
+
def _show_error(self, message: str) -> None:
|
|
223
|
+
"""Show an error message."""
|
|
224
|
+
error_label = self.query_one("#error-label", Static)
|
|
225
|
+
error_label.update(f"Error: {message}")
|
|
226
|
+
error_label.display = True
|
|
227
|
+
|
|
228
|
+
def _hide_error(self) -> None:
|
|
229
|
+
"""Hide the error message."""
|
|
230
|
+
self.query_one("#error-label", Static).display = False
|
|
231
|
+
|
|
232
|
+
@on(Button.Pressed, "#create")
|
|
233
|
+
def _on_create(self, event: Button.Pressed) -> None:
|
|
234
|
+
"""Handle create button."""
|
|
235
|
+
event.stop()
|
|
236
|
+
|
|
237
|
+
# Validate
|
|
238
|
+
error = self._validate()
|
|
239
|
+
if error:
|
|
240
|
+
self._show_error(error)
|
|
241
|
+
return
|
|
242
|
+
|
|
243
|
+
self._hide_error()
|
|
244
|
+
|
|
245
|
+
# Get values
|
|
246
|
+
name = self.query_one("#name-input", Input).value.strip()
|
|
247
|
+
description = self.query_one("#description-input", TextArea).text.strip()
|
|
248
|
+
# Public toggle is hidden for now - always create as team-only
|
|
249
|
+
is_public = False
|
|
250
|
+
|
|
251
|
+
self.dismiss(
|
|
252
|
+
CreateSpecResult(
|
|
253
|
+
name=name,
|
|
254
|
+
description=description if description else None,
|
|
255
|
+
is_public=is_public,
|
|
256
|
+
)
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
@on(Button.Pressed, "#cancel")
|
|
260
|
+
def _on_cancel(self, event: Button.Pressed) -> None:
|
|
261
|
+
"""Handle cancel button."""
|
|
262
|
+
event.stop()
|
|
263
|
+
self.dismiss(None)
|
|
264
|
+
|
|
265
|
+
@on(Input.Submitted, "#name-input")
|
|
266
|
+
def _on_name_submitted(self, event: Input.Submitted) -> None:
|
|
267
|
+
"""Move to description when name is submitted."""
|
|
268
|
+
del event # unused
|
|
269
|
+
self.query_one("#description-input", TextArea).focus()
|
|
270
|
+
|
|
271
|
+
def action_cancel(self) -> None:
|
|
272
|
+
"""Handle escape key."""
|
|
273
|
+
self.dismiss(None)
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"""Pydantic models for shared specs TUI screens."""
|
|
2
|
+
|
|
3
|
+
from enum import StrEnum
|
|
4
|
+
|
|
5
|
+
from pydantic import BaseModel
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ShareSpecsAction(StrEnum):
|
|
9
|
+
"""Actions from share specs dialog."""
|
|
10
|
+
|
|
11
|
+
CREATE = "create"
|
|
12
|
+
ADD_VERSION = "add_version"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class UploadScreenResult(BaseModel):
|
|
16
|
+
"""Result from UploadProgressScreen.
|
|
17
|
+
|
|
18
|
+
Attributes:
|
|
19
|
+
success: Whether the upload completed successfully
|
|
20
|
+
web_url: URL to view the spec version (on success)
|
|
21
|
+
cancelled: Whether the upload was cancelled
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
success: bool
|
|
25
|
+
web_url: str | None = None
|
|
26
|
+
cancelled: bool = False
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class ShareSpecsResult(BaseModel):
|
|
30
|
+
"""Result from ShareSpecsDialog.
|
|
31
|
+
|
|
32
|
+
Attributes:
|
|
33
|
+
action: CREATE to create new spec, ADD_VERSION to add to existing, None if cancelled
|
|
34
|
+
workspace_id: Workspace ID (fetched by dialog)
|
|
35
|
+
spec_id: Spec ID if adding version to existing spec
|
|
36
|
+
spec_name: Spec name if adding version to existing spec
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
action: ShareSpecsAction | None = None
|
|
40
|
+
workspace_id: str | None = None
|
|
41
|
+
spec_id: str | None = None
|
|
42
|
+
spec_name: str | None = None
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class CreateSpecResult(BaseModel):
|
|
46
|
+
"""Result from CreateSpecDialog.
|
|
47
|
+
|
|
48
|
+
Attributes:
|
|
49
|
+
name: Spec name (required)
|
|
50
|
+
description: Optional description
|
|
51
|
+
is_public: Whether spec should be public (default: False)
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
name: str
|
|
55
|
+
description: str | None = None
|
|
56
|
+
is_public: bool = False
|