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.
- shotgun/agents/agent_manager.py +524 -58
- shotgun/agents/common.py +62 -62
- shotgun/agents/config/constants.py +0 -6
- shotgun/agents/config/manager.py +14 -3
- shotgun/agents/config/models.py +16 -0
- shotgun/agents/config/provider.py +68 -13
- shotgun/agents/context_analyzer/__init__.py +28 -0
- shotgun/agents/context_analyzer/analyzer.py +493 -0
- shotgun/agents/context_analyzer/constants.py +9 -0
- shotgun/agents/context_analyzer/formatter.py +115 -0
- shotgun/agents/context_analyzer/models.py +212 -0
- shotgun/agents/conversation_history.py +125 -2
- shotgun/agents/conversation_manager.py +24 -2
- shotgun/agents/export.py +4 -5
- shotgun/agents/history/compaction.py +9 -4
- shotgun/agents/history/context_extraction.py +93 -6
- shotgun/agents/history/history_processors.py +14 -2
- shotgun/agents/history/token_counting/anthropic.py +32 -10
- shotgun/agents/models.py +50 -2
- shotgun/agents/plan.py +4 -5
- shotgun/agents/research.py +4 -5
- shotgun/agents/specify.py +4 -5
- shotgun/agents/tasks.py +4 -5
- shotgun/agents/tools/__init__.py +0 -2
- shotgun/agents/tools/codebase/codebase_shell.py +6 -0
- shotgun/agents/tools/codebase/directory_lister.py +6 -0
- shotgun/agents/tools/codebase/file_read.py +6 -0
- shotgun/agents/tools/codebase/query_graph.py +6 -0
- shotgun/agents/tools/codebase/retrieve_code.py +6 -0
- shotgun/agents/tools/file_management.py +71 -9
- shotgun/agents/tools/registry.py +217 -0
- shotgun/agents/tools/web_search/__init__.py +24 -12
- shotgun/agents/tools/web_search/anthropic.py +24 -3
- shotgun/agents/tools/web_search/gemini.py +22 -10
- shotgun/agents/tools/web_search/openai.py +21 -12
- shotgun/api_endpoints.py +7 -3
- shotgun/build_constants.py +1 -1
- shotgun/cli/clear.py +52 -0
- shotgun/cli/compact.py +186 -0
- shotgun/cli/context.py +111 -0
- shotgun/cli/models.py +1 -0
- shotgun/cli/update.py +16 -2
- shotgun/codebase/core/manager.py +10 -1
- shotgun/llm_proxy/__init__.py +5 -2
- shotgun/llm_proxy/clients.py +12 -7
- shotgun/logging_config.py +8 -10
- shotgun/main.py +70 -10
- shotgun/posthog_telemetry.py +9 -3
- shotgun/prompts/agents/export.j2 +18 -1
- shotgun/prompts/agents/partials/common_agent_system_prompt.j2 +5 -1
- shotgun/prompts/agents/partials/interactive_mode.j2 +24 -7
- shotgun/prompts/agents/plan.j2 +1 -1
- shotgun/prompts/agents/research.j2 +1 -1
- shotgun/prompts/agents/specify.j2 +270 -3
- shotgun/prompts/agents/state/system_state.j2 +4 -0
- shotgun/prompts/agents/tasks.j2 +1 -1
- shotgun/prompts/loader.py +2 -2
- shotgun/prompts/tools/web_search.j2 +14 -0
- shotgun/sentry_telemetry.py +4 -15
- shotgun/settings.py +238 -0
- shotgun/telemetry.py +15 -32
- shotgun/tui/app.py +203 -9
- shotgun/tui/commands/__init__.py +1 -1
- shotgun/tui/components/context_indicator.py +136 -0
- shotgun/tui/components/mode_indicator.py +70 -0
- shotgun/tui/components/status_bar.py +48 -0
- shotgun/tui/containers.py +93 -0
- shotgun/tui/dependencies.py +39 -0
- shotgun/tui/protocols.py +45 -0
- shotgun/tui/screens/chat/__init__.py +5 -0
- shotgun/tui/screens/chat/chat.tcss +54 -0
- shotgun/tui/screens/chat/chat_screen.py +1110 -0
- shotgun/tui/screens/chat/codebase_index_prompt_screen.py +64 -0
- shotgun/tui/screens/chat/codebase_index_selection.py +12 -0
- shotgun/tui/screens/chat/help_text.py +39 -0
- shotgun/tui/screens/chat/prompt_history.py +48 -0
- shotgun/tui/screens/chat.tcss +11 -0
- shotgun/tui/screens/chat_screen/command_providers.py +68 -2
- shotgun/tui/screens/chat_screen/history/__init__.py +22 -0
- shotgun/tui/screens/chat_screen/history/agent_response.py +66 -0
- shotgun/tui/screens/chat_screen/history/chat_history.py +116 -0
- shotgun/tui/screens/chat_screen/history/formatters.py +115 -0
- shotgun/tui/screens/chat_screen/history/partial_response.py +43 -0
- shotgun/tui/screens/chat_screen/history/user_question.py +42 -0
- shotgun/tui/screens/confirmation_dialog.py +151 -0
- shotgun/tui/screens/model_picker.py +30 -6
- shotgun/tui/screens/pipx_migration.py +153 -0
- shotgun/tui/screens/welcome.py +24 -5
- shotgun/tui/services/__init__.py +5 -0
- shotgun/tui/services/conversation_service.py +182 -0
- shotgun/tui/state/__init__.py +7 -0
- shotgun/tui/state/processing_state.py +185 -0
- shotgun/tui/widgets/__init__.py +5 -0
- shotgun/tui/widgets/widget_coordinator.py +247 -0
- shotgun/utils/datetime_utils.py +77 -0
- shotgun/utils/file_system_utils.py +3 -2
- shotgun/utils/update_checker.py +69 -14
- shotgun_sh-0.2.11.dev1.dist-info/METADATA +129 -0
- shotgun_sh-0.2.11.dev1.dist-info/RECORD +190 -0
- {shotgun_sh-0.2.3.dev2.dist-info → shotgun_sh-0.2.11.dev1.dist-info}/entry_points.txt +1 -0
- {shotgun_sh-0.2.3.dev2.dist-info → shotgun_sh-0.2.11.dev1.dist-info}/licenses/LICENSE +1 -1
- shotgun/agents/tools/user_interaction.py +0 -37
- shotgun/tui/screens/chat.py +0 -804
- shotgun/tui/screens/chat_screen/history.py +0 -352
- shotgun_sh-0.2.3.dev2.dist-info/METADATA +0 -467
- shotgun_sh-0.2.3.dev2.dist-info/RECORD +0 -154
- {shotgun_sh-0.2.3.dev2.dist-info → shotgun_sh-0.2.11.dev1.dist-info}/WHEEL +0 -0
|
@@ -6,20 +6,37 @@
|
|
|
6
6
|
{% if interactive_mode -%}
|
|
7
7
|
IMPORTANT: USER INTERACTION IS ENABLED (interactive mode).
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
## Structured Output Format
|
|
10
|
+
|
|
11
|
+
You must return responses using this structured format:
|
|
12
|
+
|
|
13
|
+
```json
|
|
14
|
+
{
|
|
15
|
+
"response": "Your main response text here",
|
|
16
|
+
"clarifying_questions": ["Question 1?", "Question 2?"] // Optional, only when needed
|
|
17
|
+
}
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## When to Use Clarifying Questions
|
|
21
|
+
|
|
22
|
+
- BEFORE GETTING TO WORK: If the user's request is ambiguous, use clarifying_questions to ask what they want
|
|
23
|
+
- DURING WORK: After using write_file(), you can suggest that the user review it and ask any clarifying questions with clarifying_questions
|
|
12
24
|
- Don't assume - ask for confirmation of your understanding
|
|
13
|
-
- When in doubt about any aspect of the goal,
|
|
25
|
+
- When in doubt about any aspect of the goal, include clarifying_questions
|
|
26
|
+
|
|
27
|
+
## Important Notes
|
|
28
|
+
|
|
29
|
+
- If you don't need to ask questions, set clarifying_questions to null or omit it
|
|
30
|
+
- Keep response field concise - a paragraph at most for user communication
|
|
31
|
+
- Questions should be clear, specific, and independently answerable
|
|
32
|
+
- Don't ask multiple questions in one string - use separate array items
|
|
14
33
|
|
|
15
34
|
{% else -%}
|
|
16
35
|
|
|
17
36
|
IMPORTANT: USER INTERACTION IS DISABLED (non-interactive mode).
|
|
18
|
-
- You cannot ask clarifying questions
|
|
37
|
+
- You cannot ask clarifying questions (clarifying_questions will be ignored)
|
|
19
38
|
- Make reasonable assumptions based on best practices
|
|
20
39
|
- Use sensible defaults when information is missing
|
|
21
|
-
- Make reasonable assumptions based on industry best practices
|
|
22
|
-
- Use sensible defaults when specific details are not provided
|
|
23
40
|
- When in doubt, make reasonable assumptions and proceed with best practices
|
|
24
41
|
{% endif %}
|
|
25
42
|
|
shotgun/prompts/agents/plan.j2
CHANGED
|
@@ -118,7 +118,7 @@ USER INTERACTION - REDUCE UNCERTAINTY:
|
|
|
118
118
|
- FIRST read `research.md` and `specification.md` before asking ANY questions
|
|
119
119
|
- ONLY ask clarifying questions AFTER reading the context files
|
|
120
120
|
- Questions should be about gaps not covered in research/specification
|
|
121
|
-
- Use
|
|
121
|
+
- Use clarifying questions to gather specific details about:
|
|
122
122
|
- Information not found in the context files
|
|
123
123
|
- Clarifications on ambiguous specifications
|
|
124
124
|
- Priorities when multiple options exist
|
|
@@ -39,7 +39,7 @@ For research tasks:
|
|
|
39
39
|
## RESEARCH PRINCIPLES
|
|
40
40
|
|
|
41
41
|
{% if interactive_mode -%}
|
|
42
|
-
- CRITICAL: BEFORE RUNNING ANY SEARCH TOOL, ASK THE USER FOR APPROVAL
|
|
42
|
+
- CRITICAL: BEFORE RUNNING ANY SEARCH TOOL, ASK THE USER FOR APPROVAL using clarifying questions. Include what you plan to search for and ask if they want you to proceed.
|
|
43
43
|
{% endif -%}
|
|
44
44
|
- Build upon existing research rather than starting from scratch
|
|
45
45
|
- Focus on practical, actionable information over theoretical concepts
|
|
@@ -8,14 +8,281 @@ Transform requirements into detailed, actionable specifications that development
|
|
|
8
8
|
|
|
9
9
|
## MEMORY MANAGEMENT PROTOCOL
|
|
10
10
|
|
|
11
|
-
- You have exclusive write access to: `specification.md`
|
|
11
|
+
- You have exclusive write access to: `specification.md` and `.shotgun/contracts/*`
|
|
12
12
|
- SHOULD READ `research.md` for context but CANNOT write to it
|
|
13
|
-
-
|
|
13
|
+
- **specification.md is for PROSE ONLY** - no code, no implementation details, no type definitions
|
|
14
|
+
- **All code goes in .shotgun/contracts/** - types, interfaces, schemas
|
|
15
|
+
- specification.md describes WHAT and WHY, contracts/ show HOW with actual code
|
|
16
|
+
- This is your persistent memory store - ALWAYS load specification.md first
|
|
14
17
|
- Compress content regularly to stay within context limits
|
|
15
|
-
- Keep your
|
|
18
|
+
- Keep your files updated as you work - they're your memory across sessions
|
|
16
19
|
- When adding new specifications, review and consolidate overlapping requirements
|
|
17
20
|
- Structure specifications for easy reference by the next agents
|
|
18
21
|
|
|
22
|
+
## WHAT GOES IN SPECIFICATION.MD
|
|
23
|
+
|
|
24
|
+
specification.md is your prose documentation file. It should contain:
|
|
25
|
+
|
|
26
|
+
**INCLUDE in specification.md:**
|
|
27
|
+
- Requirements and business context (what needs to be built and why)
|
|
28
|
+
- Architecture overview and system design decisions
|
|
29
|
+
- Component descriptions and how they interact
|
|
30
|
+
- User workflows and use cases
|
|
31
|
+
- Directory structure as succinct prose (e.g., "src/ contains main code, tests/ contains test files")
|
|
32
|
+
- Dependencies listed in prose (e.g., "Requires TypeScript 5.0+, React 18, and PostgreSQL")
|
|
33
|
+
- Configuration requirements described (e.g., "App needs database URL and API key in environment")
|
|
34
|
+
- Testing strategies and acceptance criteria
|
|
35
|
+
- References to contract files (e.g., "See contracts/user_models.py for User type definition")
|
|
36
|
+
|
|
37
|
+
**DO NOT INCLUDE in specification.md:**
|
|
38
|
+
- Code blocks, type definitions, or function signatures (those go in contracts/)
|
|
39
|
+
- Implementation details or algorithms (describe behavior instead)
|
|
40
|
+
- Actual configuration files or build manifests (describe what's needed instead)
|
|
41
|
+
- Directory trees or file listings (keep structure descriptions succinct)
|
|
42
|
+
|
|
43
|
+
**When you need to show structure:** Reference contract files instead of inline code.
|
|
44
|
+
Example: "User authentication uses OAuth2. See contracts/auth_types.ts for AuthUser and AuthToken types."
|
|
45
|
+
|
|
46
|
+
## CONTRACT FILES
|
|
47
|
+
|
|
48
|
+
Contract files define the **interfaces and types** that form contracts between components.
|
|
49
|
+
They contain actual code that shows structure, not prose descriptions.
|
|
50
|
+
|
|
51
|
+
**ONLY put these in `.shotgun/contracts/` (language-agnostic):**
|
|
52
|
+
- **Type definitions ONLY** - Shape and structure, NO behavior or logic:
|
|
53
|
+
- Python: Pydantic models, dataclasses, `typing.Protocol` classes (interface definitions)
|
|
54
|
+
- TypeScript: interfaces, type aliases
|
|
55
|
+
- Rust: struct definitions
|
|
56
|
+
- Java: interfaces, POJOs
|
|
57
|
+
- C++: header files with class/struct declarations
|
|
58
|
+
- Go: interface types, struct definitions
|
|
59
|
+
- **Schema definitions**: API contracts and data schemas
|
|
60
|
+
- OpenAPI/Swagger specs (openapi.json, openapi.yaml)
|
|
61
|
+
- JSON Schema definitions
|
|
62
|
+
- GraphQL schemas
|
|
63
|
+
- Protobuf definitions
|
|
64
|
+
- **Protocol/Interface classes**: Pure interface definitions with method signatures only
|
|
65
|
+
- Python: `class Storage(Protocol): def save(self, data: str) -> None: ...`
|
|
66
|
+
- Use `...` (Ellipsis) for protocol methods, NOT `pass`
|
|
67
|
+
|
|
68
|
+
**NEVER put these in `.shotgun/contracts/` - NO EXECUTABLE CODE:**
|
|
69
|
+
- ❌ **Functions or methods with implementations** (even with `pass` or empty bodies)
|
|
70
|
+
- ❌ **Helper functions** with any logic whatsoever
|
|
71
|
+
- ❌ **Classes with method implementations** (use Protocol classes instead)
|
|
72
|
+
- ❌ **Standalone functions** like `def main(): pass` or `def validate_input(x): ...`
|
|
73
|
+
- ❌ **Code with behavior**: loops, conditionals, data manipulation, computations
|
|
74
|
+
- ❌ **Data constants**: dictionaries, lists, or any runtime values
|
|
75
|
+
- ❌ **`if __name__ == "__main__":` blocks** or any executable code
|
|
76
|
+
- Build/dependency configs (pyproject.toml, package.json, Cargo.toml, requirements.txt)
|
|
77
|
+
- Directory structure files (directory_structure.txt)
|
|
78
|
+
- Configuration templates (.env, config.yaml, example configs)
|
|
79
|
+
- Documentation or markdown files
|
|
80
|
+
- SQL migration files or database dumps
|
|
81
|
+
|
|
82
|
+
**These belong in specification.md instead:**
|
|
83
|
+
- Directory structure (as succinct prose: "src/ contains modules, tests/ has unit tests")
|
|
84
|
+
- Dependencies (as prose: "Requires Rust 1.70+, tokio, serde")
|
|
85
|
+
- Configuration needs (describe: "App needs DB_URL and API_KEY environment variables")
|
|
86
|
+
|
|
87
|
+
**Guidelines for contract files:**
|
|
88
|
+
- Keep each file focused on a single domain (e.g., user_types.ts, payment_models.py)
|
|
89
|
+
- Reference from specification.md: "See contracts/user_types.ts for User and Profile types"
|
|
90
|
+
- Use descriptive filenames: `auth_models.py`, `api_spec.json`, `database_types.rs`
|
|
91
|
+
- Keep files under 500 lines to avoid truncation
|
|
92
|
+
- When contracts grow large, split into focused files
|
|
93
|
+
|
|
94
|
+
**Example workflow:**
|
|
95
|
+
1. In specification.md: "Authentication system with JWT tokens. See contracts/auth_types.ts for types."
|
|
96
|
+
2. Create contract file: `write_file("contracts/auth_types.ts", content)` with actual TypeScript interfaces
|
|
97
|
+
3. Create contract file: `write_file("contracts/auth_api.json", content)` with actual OpenAPI spec
|
|
98
|
+
4. Coding agents can directly use these contracts to implement features
|
|
99
|
+
|
|
100
|
+
## HOW TO WRITE CONTRACT FILES
|
|
101
|
+
|
|
102
|
+
**CRITICAL - Always use correct file paths with write_file():**
|
|
103
|
+
|
|
104
|
+
Your working directory is `.shotgun/`, so paths should be relative to that directory.
|
|
105
|
+
|
|
106
|
+
<GOOD_EXAMPLES>
|
|
107
|
+
✅ `write_file("contracts/user_models.py", content)` - Correct path for Python models
|
|
108
|
+
✅ `write_file("contracts/auth_types.ts", content)` - Correct path for TypeScript types
|
|
109
|
+
✅ `write_file("contracts/api_spec.json", content)` - Correct path for OpenAPI spec
|
|
110
|
+
✅ `write_file("contracts/payment_service.rs", content)` - Correct path for Rust code
|
|
111
|
+
</GOOD_EXAMPLES>
|
|
112
|
+
|
|
113
|
+
<BAD_EXAMPLES>
|
|
114
|
+
❌ `write_file(".shotgun/contracts/user_models.py", content)` - WRONG! Don't include .shotgun/ prefix
|
|
115
|
+
❌ `write_file("contracts/directory_structure.txt", content)` - WRONG! No documentation files
|
|
116
|
+
❌ `write_file("contracts/pyproject.toml", content)` - WRONG! No build configs in contracts/
|
|
117
|
+
❌ `write_file("contracts/requirements.txt", content)` - WRONG! No dependency lists in contracts/
|
|
118
|
+
❌ `write_file("contracts/config.yaml", content)` - WRONG! No config templates in contracts/
|
|
119
|
+
</BAD_EXAMPLES>
|
|
120
|
+
|
|
121
|
+
**Path format rule:** Always use `contracts/filename.ext`, never `.shotgun/contracts/filename.ext`
|
|
122
|
+
|
|
123
|
+
**Language-specific examples:**
|
|
124
|
+
|
|
125
|
+
<PYTHON_EXAMPLE>
|
|
126
|
+
# Python Pydantic model contract
|
|
127
|
+
from pydantic import BaseModel, Field
|
|
128
|
+
from typing import Optional
|
|
129
|
+
|
|
130
|
+
class User(BaseModel):
|
|
131
|
+
"""User model contract."""
|
|
132
|
+
id: int
|
|
133
|
+
email: str = Field(..., description="User email address")
|
|
134
|
+
username: str
|
|
135
|
+
is_active: bool = True
|
|
136
|
+
role: Optional[str] = None
|
|
137
|
+
|
|
138
|
+
# Save as: write_file("contracts/user_models.py", content)
|
|
139
|
+
</PYTHON_EXAMPLE>
|
|
140
|
+
|
|
141
|
+
<TYPESCRIPT_EXAMPLE>
|
|
142
|
+
// TypeScript interface contract
|
|
143
|
+
interface User {
|
|
144
|
+
id: number;
|
|
145
|
+
email: string;
|
|
146
|
+
username: string;
|
|
147
|
+
isActive: boolean;
|
|
148
|
+
role?: string;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
interface AuthToken {
|
|
152
|
+
token: string;
|
|
153
|
+
expiresAt: Date;
|
|
154
|
+
userId: number;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Save as: write_file("contracts/auth_types.ts", content)
|
|
158
|
+
</TYPESCRIPT_EXAMPLE>
|
|
159
|
+
|
|
160
|
+
<RUST_EXAMPLE>
|
|
161
|
+
// Rust struct contract
|
|
162
|
+
use serde::{Deserialize, Serialize};
|
|
163
|
+
|
|
164
|
+
#[derive(Debug, Serialize, Deserialize)]
|
|
165
|
+
pub struct User {
|
|
166
|
+
pub id: u64,
|
|
167
|
+
pub email: String,
|
|
168
|
+
pub username: String,
|
|
169
|
+
pub is_active: bool,
|
|
170
|
+
pub role: Option<String>,
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Save as: write_file("contracts/user_types.rs", content)
|
|
174
|
+
</RUST_EXAMPLE>
|
|
175
|
+
|
|
176
|
+
<OPENAPI_EXAMPLE>
|
|
177
|
+
{
|
|
178
|
+
"openapi": "3.0.0",
|
|
179
|
+
"info": {
|
|
180
|
+
"title": "User API",
|
|
181
|
+
"version": "1.0.0"
|
|
182
|
+
},
|
|
183
|
+
"paths": {
|
|
184
|
+
"/users": {
|
|
185
|
+
"get": {
|
|
186
|
+
"summary": "List users",
|
|
187
|
+
"responses": {
|
|
188
|
+
"200": {
|
|
189
|
+
"description": "Successful response",
|
|
190
|
+
"content": {
|
|
191
|
+
"application/json": {
|
|
192
|
+
"schema": {
|
|
193
|
+
"type": "array",
|
|
194
|
+
"items": { "$ref": "#/components/schemas/User" }
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
},
|
|
203
|
+
"components": {
|
|
204
|
+
"schemas": {
|
|
205
|
+
"User": {
|
|
206
|
+
"type": "object",
|
|
207
|
+
"properties": {
|
|
208
|
+
"id": { "type": "integer" },
|
|
209
|
+
"email": { "type": "string" },
|
|
210
|
+
"username": { "type": "string" }
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Save as: write_file("contracts/user_api.json", content)
|
|
218
|
+
</OPENAPI_EXAMPLE>
|
|
219
|
+
|
|
220
|
+
## WHAT IS ALLOWED vs WHAT IS FORBIDDEN
|
|
221
|
+
|
|
222
|
+
**✅ ALLOWED - Type Definitions (Shape and Structure):**
|
|
223
|
+
|
|
224
|
+
```python
|
|
225
|
+
# ✅ GOOD: Pydantic model (type definition)
|
|
226
|
+
from pydantic import BaseModel
|
|
227
|
+
|
|
228
|
+
class User(BaseModel):
|
|
229
|
+
id: int
|
|
230
|
+
email: str
|
|
231
|
+
username: str
|
|
232
|
+
|
|
233
|
+
# ✅ GOOD: Protocol class (interface definition)
|
|
234
|
+
from typing import Protocol
|
|
235
|
+
|
|
236
|
+
class Storage(Protocol):
|
|
237
|
+
def save(self, data: str) -> None: ...
|
|
238
|
+
def load(self) -> str: ...
|
|
239
|
+
|
|
240
|
+
# ✅ GOOD: Type aliases and enums
|
|
241
|
+
from typing import Literal
|
|
242
|
+
from enum import Enum
|
|
243
|
+
|
|
244
|
+
UserRole = Literal["admin", "user", "guest"]
|
|
245
|
+
|
|
246
|
+
class Status(Enum):
|
|
247
|
+
ACTIVE = "active"
|
|
248
|
+
INACTIVE = "inactive"
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
**❌ FORBIDDEN - Executable Code (Behavior and Logic):**
|
|
252
|
+
|
|
253
|
+
```python
|
|
254
|
+
# ❌ BAD: Function with pass (executable code)
|
|
255
|
+
def main() -> int:
|
|
256
|
+
pass
|
|
257
|
+
|
|
258
|
+
# ❌ BAD: Function with implementation
|
|
259
|
+
def validate_input(x: str) -> str:
|
|
260
|
+
return x.strip()
|
|
261
|
+
|
|
262
|
+
# ❌ BAD: Class with method implementations
|
|
263
|
+
class HistoryManager:
|
|
264
|
+
def __init__(self):
|
|
265
|
+
pass
|
|
266
|
+
|
|
267
|
+
def add_message(self, msg: str):
|
|
268
|
+
pass
|
|
269
|
+
|
|
270
|
+
# ❌ BAD: Data constants (runtime values)
|
|
271
|
+
SUPPORTED_PROVIDERS = [
|
|
272
|
+
{"name": "openai", "key": "OPENAI_API_KEY"}
|
|
273
|
+
]
|
|
274
|
+
|
|
275
|
+
# ❌ BAD: Helper functions
|
|
276
|
+
def get_default_config() -> dict:
|
|
277
|
+
return {"model": "gpt-4"}
|
|
278
|
+
|
|
279
|
+
# ❌ BAD: Executable code blocks
|
|
280
|
+
if __name__ == "__main__":
|
|
281
|
+
main()
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
**Remember**: Contracts define **SHAPES** (types, interfaces, schemas), NOT **BEHAVIOR** (functions, logic, implementations).
|
|
285
|
+
|
|
19
286
|
## AI AGENT PIPELINE AWARENESS
|
|
20
287
|
|
|
21
288
|
**CRITICAL**: Your output will be consumed by AI coding agents (Claude Code, Cursor, Windsurf, etc.)
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
## System Status
|
|
2
2
|
|
|
3
|
+
Your training data may be old. The current date and time is: {{ current_datetime }} in {{ timezone_name }} (UTC{{ utc_offset }})
|
|
4
|
+
|
|
3
5
|
{% include 'agents/state/codebase/codebase_graphs_available.j2' %}
|
|
4
6
|
|
|
5
7
|
## Available Files
|
|
@@ -20,6 +22,8 @@ No files currently exist in your allowed directories. You can create:
|
|
|
20
22
|
- `exports/` folder - For exported documents
|
|
21
23
|
{% endif %}
|
|
22
24
|
|
|
25
|
+
When updating a file try to add into the footer that this was created using Shotgun (https://shotgun.sh).
|
|
26
|
+
|
|
23
27
|
{% if markdown_toc %}
|
|
24
28
|
## Document Table of Contents - READ THE ENTIRE FILE TO UNDERSTAND MORE
|
|
25
29
|
|
shotgun/prompts/agents/tasks.j2
CHANGED
|
@@ -99,7 +99,7 @@ Then organize tasks into logical sections:
|
|
|
99
99
|
USER INTERACTION - ASK CLARIFYING QUESTIONS:
|
|
100
100
|
|
|
101
101
|
- ALWAYS ask clarifying questions when the request is vague or ambiguous
|
|
102
|
-
- Use
|
|
102
|
+
- Use clarifying questions to gather specific details about:
|
|
103
103
|
- Specific features or functionality to prioritize
|
|
104
104
|
- Technical constraints or preferences
|
|
105
105
|
- Timeline and resource constraints
|
shotgun/prompts/loader.py
CHANGED
|
@@ -5,7 +5,7 @@ from datetime import datetime
|
|
|
5
5
|
from pathlib import Path
|
|
6
6
|
from typing import Any
|
|
7
7
|
|
|
8
|
-
from jinja2 import Environment, FileSystemLoader, Template
|
|
8
|
+
from jinja2 import Environment, FileSystemLoader, Template
|
|
9
9
|
|
|
10
10
|
from shotgun.logging_config import setup_logger
|
|
11
11
|
|
|
@@ -32,7 +32,7 @@ class PromptLoader:
|
|
|
32
32
|
self.templates_dir = templates_dir
|
|
33
33
|
self.env = Environment(
|
|
34
34
|
loader=FileSystemLoader(str(templates_dir)),
|
|
35
|
-
autoescape=
|
|
35
|
+
autoescape=False, # noqa: S701 - These are LLM prompts, not HTML
|
|
36
36
|
trim_blocks=True,
|
|
37
37
|
lstrip_blocks=True,
|
|
38
38
|
)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
Your training data may be old. The current date and time is: {{ current_datetime }} ({{ timezone_name }}, {{ utc_offset }})
|
|
2
|
+
|
|
3
|
+
Please provide current and accurate information about the following query:
|
|
4
|
+
|
|
5
|
+
Query: {{ query }}
|
|
6
|
+
|
|
7
|
+
Instructions:
|
|
8
|
+
- Provide comprehensive, factual information
|
|
9
|
+
- Include relevant details and context
|
|
10
|
+
- Focus on current and recent information
|
|
11
|
+
- Be specific and accurate in your response
|
|
12
|
+
- You can't ask the user for details, so assume the most relevant details for the query
|
|
13
|
+
|
|
14
|
+
ALWAYS PROVIDE THE SOURCES (urls) TO BACK UP THE INFORMATION YOU PROVIDE.
|
shotgun/sentry_telemetry.py
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
"""Sentry observability setup for Shotgun."""
|
|
2
2
|
|
|
3
|
-
import os
|
|
4
|
-
|
|
5
3
|
from shotgun import __version__
|
|
6
4
|
from shotgun.logging_config import get_early_logger
|
|
5
|
+
from shotgun.settings import settings
|
|
7
6
|
|
|
8
7
|
# Use early logger to prevent automatic StreamHandler creation
|
|
9
8
|
logger = get_early_logger(__name__)
|
|
@@ -23,24 +22,14 @@ def setup_sentry_observability() -> bool:
|
|
|
23
22
|
logger.debug("Sentry is already initialized, skipping")
|
|
24
23
|
return True
|
|
25
24
|
|
|
26
|
-
#
|
|
27
|
-
dsn =
|
|
28
|
-
try:
|
|
29
|
-
from shotgun import build_constants
|
|
30
|
-
|
|
31
|
-
dsn = build_constants.SENTRY_DSN
|
|
32
|
-
logger.debug("Using Sentry DSN from build constants")
|
|
33
|
-
except ImportError:
|
|
34
|
-
# Fallback to environment variable (development)
|
|
35
|
-
dsn = os.getenv("SENTRY_DSN", "")
|
|
36
|
-
if dsn:
|
|
37
|
-
logger.debug("Using Sentry DSN from environment variable")
|
|
25
|
+
# Get DSN from settings (handles build constants + env vars automatically)
|
|
26
|
+
dsn = settings.telemetry.sentry_dsn
|
|
38
27
|
|
|
39
28
|
if not dsn:
|
|
40
29
|
logger.debug("No Sentry DSN configured, skipping Sentry initialization")
|
|
41
30
|
return False
|
|
42
31
|
|
|
43
|
-
logger.debug("
|
|
32
|
+
logger.debug("Using Sentry DSN from settings, proceeding with setup")
|
|
44
33
|
|
|
45
34
|
# Determine environment based on version
|
|
46
35
|
# Dev versions contain "dev", "rc", "alpha", or "beta"
|
shotgun/settings.py
ADDED
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
"""Centralized application settings using Pydantic Settings.
|
|
2
|
+
|
|
3
|
+
All environment variables use the SHOTGUN_ prefix to avoid conflicts with other tools.
|
|
4
|
+
Settings are loaded with the following priority:
|
|
5
|
+
1. Environment variables (highest priority)
|
|
6
|
+
2. Build constants (embedded at build time)
|
|
7
|
+
3. Default values (lowest priority)
|
|
8
|
+
|
|
9
|
+
Example usage:
|
|
10
|
+
from shotgun.settings import settings
|
|
11
|
+
|
|
12
|
+
# Access telemetry settings
|
|
13
|
+
if settings.telemetry.sentry_dsn:
|
|
14
|
+
sentry_sdk.init(dsn=settings.telemetry.sentry_dsn)
|
|
15
|
+
|
|
16
|
+
# Access logging settings
|
|
17
|
+
logger.setLevel(settings.logging.log_level)
|
|
18
|
+
|
|
19
|
+
# Access API settings
|
|
20
|
+
response = httpx.get(settings.api.web_base_url)
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
from typing import Any
|
|
24
|
+
|
|
25
|
+
from pydantic import Field, field_validator
|
|
26
|
+
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _get_build_constant(name: str, default: Any = None) -> Any:
|
|
30
|
+
"""Get a value from build_constants.py, falling back to default.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
name: The constant name to retrieve (e.g., "SENTRY_DSN")
|
|
34
|
+
default: Default value if constant not found
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
The constant value, or default if not found/import fails
|
|
38
|
+
"""
|
|
39
|
+
try:
|
|
40
|
+
from shotgun import build_constants
|
|
41
|
+
|
|
42
|
+
return getattr(build_constants, name, default)
|
|
43
|
+
except ImportError:
|
|
44
|
+
return default
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class TelemetrySettings(BaseSettings):
|
|
48
|
+
"""Telemetry and observability settings.
|
|
49
|
+
|
|
50
|
+
These settings control error tracking (Sentry), analytics (PostHog),
|
|
51
|
+
and observability (Logfire) integrations.
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
sentry_dsn: str = Field(
|
|
55
|
+
default_factory=lambda: _get_build_constant("SENTRY_DSN", ""),
|
|
56
|
+
description="Sentry DSN for error tracking",
|
|
57
|
+
)
|
|
58
|
+
posthog_api_key: str = Field(
|
|
59
|
+
default_factory=lambda: _get_build_constant("POSTHOG_API_KEY", ""),
|
|
60
|
+
description="PostHog API key for analytics",
|
|
61
|
+
)
|
|
62
|
+
posthog_project_id: str = Field(
|
|
63
|
+
default_factory=lambda: _get_build_constant("POSTHOG_PROJECT_ID", ""),
|
|
64
|
+
description="PostHog project ID",
|
|
65
|
+
)
|
|
66
|
+
logfire_enabled: bool = Field(
|
|
67
|
+
default_factory=lambda: _get_build_constant("LOGFIRE_ENABLED", False),
|
|
68
|
+
description="Enable Logfire observability (dev builds only)",
|
|
69
|
+
)
|
|
70
|
+
logfire_token: str = Field(
|
|
71
|
+
default_factory=lambda: _get_build_constant("LOGFIRE_TOKEN", ""),
|
|
72
|
+
description="Logfire authentication token",
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
model_config = SettingsConfigDict(
|
|
76
|
+
env_prefix="SHOTGUN_",
|
|
77
|
+
env_file=".env",
|
|
78
|
+
env_file_encoding="utf-8",
|
|
79
|
+
extra="ignore",
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
@field_validator("logfire_enabled", mode="before")
|
|
83
|
+
@classmethod
|
|
84
|
+
def parse_bool(cls, v: Any) -> bool:
|
|
85
|
+
"""Parse boolean values from strings (matches is_truthy behavior)."""
|
|
86
|
+
if isinstance(v, bool):
|
|
87
|
+
return v
|
|
88
|
+
if isinstance(v, str):
|
|
89
|
+
return v.lower() in ("true", "1", "yes")
|
|
90
|
+
return bool(v)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class LoggingSettings(BaseSettings):
|
|
94
|
+
"""Logging configuration settings.
|
|
95
|
+
|
|
96
|
+
Controls log level, console output, and file logging behavior.
|
|
97
|
+
"""
|
|
98
|
+
|
|
99
|
+
log_level: str = Field(
|
|
100
|
+
default="INFO",
|
|
101
|
+
description="Logging level (DEBUG, INFO, WARNING, ERROR, CRITICAL)",
|
|
102
|
+
)
|
|
103
|
+
logging_to_console: bool = Field(
|
|
104
|
+
default=False,
|
|
105
|
+
description="Enable console logging output",
|
|
106
|
+
)
|
|
107
|
+
logging_to_file: bool = Field(
|
|
108
|
+
default=True,
|
|
109
|
+
description="Enable file logging output",
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
model_config = SettingsConfigDict(
|
|
113
|
+
env_prefix="SHOTGUN_",
|
|
114
|
+
env_file=".env",
|
|
115
|
+
env_file_encoding="utf-8",
|
|
116
|
+
extra="ignore",
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
@field_validator("log_level")
|
|
120
|
+
@classmethod
|
|
121
|
+
def validate_log_level(cls, v: str) -> str:
|
|
122
|
+
"""Validate log level is one of the allowed values."""
|
|
123
|
+
v = v.upper()
|
|
124
|
+
valid_levels = ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]
|
|
125
|
+
if v not in valid_levels:
|
|
126
|
+
return "INFO" # Default to INFO if invalid
|
|
127
|
+
return v
|
|
128
|
+
|
|
129
|
+
@field_validator("logging_to_console", "logging_to_file", mode="before")
|
|
130
|
+
@classmethod
|
|
131
|
+
def parse_bool(cls, v: Any) -> bool:
|
|
132
|
+
"""Parse boolean values from strings (matches is_truthy behavior)."""
|
|
133
|
+
if isinstance(v, bool):
|
|
134
|
+
return v
|
|
135
|
+
if isinstance(v, str):
|
|
136
|
+
return v.lower() in ("true", "1", "yes")
|
|
137
|
+
return bool(v)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
class ApiSettings(BaseSettings):
|
|
141
|
+
"""API endpoint settings.
|
|
142
|
+
|
|
143
|
+
Configuration for Shotgun backend services.
|
|
144
|
+
"""
|
|
145
|
+
|
|
146
|
+
web_base_url: str = Field(
|
|
147
|
+
default="https://api-219702594231.us-east4.run.app",
|
|
148
|
+
description="Shotgun Web API base URL (authentication/subscription)",
|
|
149
|
+
)
|
|
150
|
+
account_llm_base_url: str = Field(
|
|
151
|
+
default="https://litellm-219702594231.us-east4.run.app",
|
|
152
|
+
description="Shotgun's LiteLLM proxy base URL (AI model requests)",
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
model_config = SettingsConfigDict(
|
|
156
|
+
env_prefix="SHOTGUN_",
|
|
157
|
+
env_file=".env",
|
|
158
|
+
env_file_encoding="utf-8",
|
|
159
|
+
extra="ignore",
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
class DevelopmentSettings(BaseSettings):
|
|
164
|
+
"""Development and testing settings.
|
|
165
|
+
|
|
166
|
+
These settings are primarily used for testing and development purposes.
|
|
167
|
+
"""
|
|
168
|
+
|
|
169
|
+
home: str | None = Field(
|
|
170
|
+
default=None,
|
|
171
|
+
description="Override Shotgun home directory (for testing)",
|
|
172
|
+
)
|
|
173
|
+
pipx_simulate: bool = Field(
|
|
174
|
+
default=False,
|
|
175
|
+
description="Simulate pipx installation (for testing)",
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
model_config = SettingsConfigDict(
|
|
179
|
+
env_prefix="SHOTGUN_",
|
|
180
|
+
env_file=".env",
|
|
181
|
+
env_file_encoding="utf-8",
|
|
182
|
+
extra="ignore",
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
@field_validator("pipx_simulate", mode="before")
|
|
186
|
+
@classmethod
|
|
187
|
+
def parse_bool(cls, v: Any) -> bool:
|
|
188
|
+
"""Parse boolean values from strings (matches is_truthy behavior)."""
|
|
189
|
+
if isinstance(v, bool):
|
|
190
|
+
return v
|
|
191
|
+
if isinstance(v, str):
|
|
192
|
+
return v.lower() in ("true", "1", "yes")
|
|
193
|
+
return bool(v)
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
class Settings(BaseSettings):
|
|
197
|
+
"""Main application settings with SHOTGUN_ prefix.
|
|
198
|
+
|
|
199
|
+
This is the main settings class that composes all other settings groups.
|
|
200
|
+
Access settings via the global `settings` singleton instance.
|
|
201
|
+
|
|
202
|
+
Example:
|
|
203
|
+
from shotgun.settings import settings
|
|
204
|
+
|
|
205
|
+
# Telemetry settings
|
|
206
|
+
settings.telemetry.sentry_dsn
|
|
207
|
+
settings.telemetry.posthog_api_key
|
|
208
|
+
settings.telemetry.logfire_enabled
|
|
209
|
+
|
|
210
|
+
# Logging settings
|
|
211
|
+
settings.logging.log_level
|
|
212
|
+
settings.logging.logging_to_console
|
|
213
|
+
|
|
214
|
+
# API settings
|
|
215
|
+
settings.api.web_base_url
|
|
216
|
+
settings.api.account_llm_base_url
|
|
217
|
+
|
|
218
|
+
# Development settings
|
|
219
|
+
settings.dev.home
|
|
220
|
+
settings.dev.pipx_simulate
|
|
221
|
+
"""
|
|
222
|
+
|
|
223
|
+
telemetry: TelemetrySettings = Field(default_factory=TelemetrySettings)
|
|
224
|
+
logging: LoggingSettings = Field(default_factory=LoggingSettings)
|
|
225
|
+
api: ApiSettings = Field(default_factory=ApiSettings)
|
|
226
|
+
dev: DevelopmentSettings = Field(default_factory=DevelopmentSettings)
|
|
227
|
+
|
|
228
|
+
model_config = SettingsConfigDict(
|
|
229
|
+
env_prefix="SHOTGUN_",
|
|
230
|
+
env_file=".env",
|
|
231
|
+
env_file_encoding="utf-8",
|
|
232
|
+
extra="ignore",
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
# Global settings singleton
|
|
237
|
+
# Import this in your modules: from shotgun.settings import settings
|
|
238
|
+
settings = Settings()
|