shotgun-sh 0.2.17__py3-none-any.whl → 0.4.0.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.
- shotgun/agents/agent_manager.py +219 -37
- shotgun/agents/common.py +79 -78
- shotgun/agents/config/README.md +89 -0
- shotgun/agents/config/__init__.py +10 -1
- shotgun/agents/config/manager.py +364 -53
- shotgun/agents/config/models.py +101 -21
- shotgun/agents/config/provider.py +51 -13
- shotgun/agents/config/streaming_test.py +119 -0
- 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 +239 -0
- shotgun/agents/{history → conversation/history}/history_processors.py +267 -3
- shotgun/agents/{history → conversation/history}/token_counting/anthropic.py +8 -0
- shotgun/agents/{conversation_manager.py → conversation/manager.py} +1 -1
- shotgun/agents/{conversation_history.py → conversation/models.py} +8 -94
- shotgun/agents/error/__init__.py +11 -0
- shotgun/agents/error/models.py +19 -0
- shotgun/agents/export.py +12 -13
- shotgun/agents/models.py +66 -1
- shotgun/agents/plan.py +12 -13
- shotgun/agents/research.py +13 -10
- shotgun/agents/router/__init__.py +47 -0
- shotgun/agents/router/models.py +376 -0
- shotgun/agents/router/router.py +185 -0
- shotgun/agents/router/tools/__init__.py +18 -0
- shotgun/agents/router/tools/delegation_tools.py +503 -0
- shotgun/agents/router/tools/plan_tools.py +322 -0
- shotgun/agents/runner.py +230 -0
- shotgun/agents/specify.py +12 -13
- shotgun/agents/tasks.py +12 -13
- shotgun/agents/tools/file_management.py +49 -1
- shotgun/agents/tools/registry.py +2 -0
- shotgun/agents/tools/web_search/__init__.py +1 -2
- shotgun/agents/tools/web_search/gemini.py +1 -3
- shotgun/agents/tools/web_search/openai.py +1 -1
- shotgun/build_constants.py +2 -2
- shotgun/cli/clear.py +1 -1
- shotgun/cli/compact.py +5 -3
- shotgun/cli/context.py +44 -1
- shotgun/cli/error_handler.py +24 -0
- shotgun/cli/export.py +34 -34
- shotgun/cli/plan.py +34 -34
- shotgun/cli/research.py +17 -9
- shotgun/cli/spec/__init__.py +5 -0
- shotgun/cli/spec/backup.py +81 -0
- shotgun/cli/spec/commands.py +132 -0
- shotgun/cli/spec/models.py +48 -0
- shotgun/cli/spec/pull_service.py +219 -0
- shotgun/cli/specify.py +20 -19
- shotgun/cli/tasks.py +34 -34
- shotgun/codebase/core/change_detector.py +1 -1
- shotgun/codebase/core/ingestor.py +154 -8
- shotgun/codebase/core/manager.py +1 -1
- shotgun/codebase/models.py +2 -0
- shotgun/exceptions.py +325 -0
- shotgun/llm_proxy/__init__.py +17 -0
- shotgun/llm_proxy/client.py +215 -0
- shotgun/llm_proxy/models.py +137 -0
- shotgun/logging_config.py +42 -0
- shotgun/main.py +4 -0
- shotgun/posthog_telemetry.py +1 -1
- shotgun/prompts/agents/export.j2 +2 -0
- shotgun/prompts/agents/partials/common_agent_system_prompt.j2 +23 -3
- shotgun/prompts/agents/partials/interactive_mode.j2 +3 -3
- shotgun/prompts/agents/partials/router_delegation_mode.j2 +36 -0
- shotgun/prompts/agents/plan.j2 +29 -1
- shotgun/prompts/agents/research.j2 +75 -23
- shotgun/prompts/agents/router.j2 +440 -0
- shotgun/prompts/agents/specify.j2 +80 -4
- shotgun/prompts/agents/state/system_state.j2 +15 -8
- shotgun/prompts/agents/tasks.j2 +63 -23
- shotgun/prompts/history/chunk_summarization.j2 +34 -0
- shotgun/prompts/history/combine_summaries.j2 +53 -0
- shotgun/sdk/codebase.py +14 -3
- shotgun/settings.py +5 -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 +329 -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 +78 -15
- shotgun/tui/components/mode_indicator.py +120 -25
- shotgun/tui/components/status_bar.py +2 -2
- shotgun/tui/containers.py +1 -1
- shotgun/tui/dependencies.py +64 -9
- shotgun/tui/layout.py +5 -0
- shotgun/tui/protocols.py +37 -0
- shotgun/tui/screens/chat/chat.tcss +9 -1
- shotgun/tui/screens/chat/chat_screen.py +1015 -106
- shotgun/tui/screens/chat/codebase_index_prompt_screen.py +196 -17
- shotgun/tui/screens/chat_screen/command_providers.py +13 -89
- shotgun/tui/screens/chat_screen/hint_message.py +76 -1
- shotgun/tui/screens/chat_screen/history/agent_response.py +7 -3
- shotgun/tui/screens/chat_screen/history/chat_history.py +12 -0
- shotgun/tui/screens/chat_screen/history/formatters.py +53 -15
- shotgun/tui/screens/chat_screen/history/partial_response.py +11 -1
- shotgun/tui/screens/chat_screen/messages.py +219 -0
- shotgun/tui/screens/confirmation_dialog.py +40 -0
- shotgun/tui/screens/directory_setup.py +45 -41
- shotgun/tui/screens/feedback.py +10 -3
- shotgun/tui/screens/github_issue.py +11 -2
- shotgun/tui/screens/model_picker.py +28 -8
- shotgun/tui/screens/onboarding.py +179 -26
- shotgun/tui/screens/pipx_migration.py +58 -6
- shotgun/tui/screens/provider_config.py +66 -8
- 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 +110 -16
- shotgun/tui/screens/spec_pull.py +288 -0
- shotgun/tui/screens/welcome.py +123 -0
- shotgun/tui/services/conversation_service.py +5 -2
- shotgun/tui/utils/mode_progress.py +20 -86
- shotgun/tui/widgets/__init__.py +2 -1
- shotgun/tui/widgets/approval_widget.py +152 -0
- shotgun/tui/widgets/cascade_confirmation_widget.py +203 -0
- shotgun/tui/widgets/plan_panel.py +129 -0
- shotgun/tui/widgets/step_checkpoint_widget.py +180 -0
- shotgun/tui/widgets/widget_coordinator.py +1 -1
- {shotgun_sh-0.2.17.dist-info → shotgun_sh-0.4.0.dev1.dist-info}/METADATA +11 -4
- shotgun_sh-0.4.0.dev1.dist-info/RECORD +242 -0
- {shotgun_sh-0.2.17.dist-info → shotgun_sh-0.4.0.dev1.dist-info}/WHEEL +1 -1
- shotgun_sh-0.2.17.dist-info/RECORD +0 -194
- /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/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.17.dist-info → shotgun_sh-0.4.0.dev1.dist-info}/entry_points.txt +0 -0
- {shotgun_sh-0.2.17.dist-info → shotgun_sh-0.4.0.dev1.dist-info}/licenses/LICENSE +0 -0
shotgun/prompts/agents/tasks.j2
CHANGED
|
@@ -4,6 +4,34 @@ Your job is to help create and manage actionable tasks for software projects and
|
|
|
4
4
|
|
|
5
5
|
{% include 'agents/partials/common_agent_system_prompt.j2' %}
|
|
6
6
|
|
|
7
|
+
## CRITICAL: CREATE MINIMAL TASKS, ASK QUESTIONS
|
|
8
|
+
|
|
9
|
+
**DO NOT generate a comprehensive task list on the first pass.**
|
|
10
|
+
|
|
11
|
+
Instead:
|
|
12
|
+
1. **Create only essential tasks** - The minimum to start work
|
|
13
|
+
2. **Ask clarifying questions** - What's the priority? What can wait?
|
|
14
|
+
3. **Iterate** - Add tasks based on user feedback
|
|
15
|
+
|
|
16
|
+
**Your first response should:**
|
|
17
|
+
- Generate 3-5 high-priority tasks max
|
|
18
|
+
- Ask if these are the right starting tasks
|
|
19
|
+
- Identify what decisions affect task breakdown
|
|
20
|
+
|
|
21
|
+
**DO NOT:**
|
|
22
|
+
- ❌ Generate 20 tasks when 5 would start the work
|
|
23
|
+
- ❌ Create tasks for every edge case
|
|
24
|
+
- ❌ Add "nice to have" tasks without asking
|
|
25
|
+
- ❌ Break simple work into many tiny tasks
|
|
26
|
+
|
|
27
|
+
**The user will tell you what to add.** Start with essentials.
|
|
28
|
+
|
|
29
|
+
{% include 'agents/partials/router_delegation_mode.j2' %}
|
|
30
|
+
|
|
31
|
+
## YOUR SCOPE
|
|
32
|
+
|
|
33
|
+
You are the **Tasks agent**. Your file is `tasks.md` - this is the ONLY file you can write to.
|
|
34
|
+
|
|
7
35
|
## MEMORY MANAGEMENT PROTOCOL
|
|
8
36
|
|
|
9
37
|
- You have exclusive write access to: `tasks.md`
|
|
@@ -11,9 +39,9 @@ Your job is to help create and manage actionable tasks for software projects and
|
|
|
11
39
|
- This is your persistent memory store - ALWAYS load it first
|
|
12
40
|
- Compress content regularly to stay within context limits
|
|
13
41
|
- Keep your file updated as you work - it's your memory across sessions
|
|
14
|
-
-
|
|
42
|
+
- Keep completed tasks marked with `[X]` for reference
|
|
15
43
|
- Consolidate similar or duplicate tasks when compressing
|
|
16
|
-
- Maintain structure:
|
|
44
|
+
- Maintain structure: Stages with numbered tasks (Stage 1, Stage 2, etc.)
|
|
17
45
|
|
|
18
46
|
## AI AGENT PIPELINE AWARENESS
|
|
19
47
|
|
|
@@ -40,9 +68,10 @@ Example task format:
|
|
|
40
68
|
For task management:
|
|
41
69
|
1. **Load existing tasks**: ALWAYS first use `read_file("tasks.md")` to see what tasks already exist (if the file exists)
|
|
42
70
|
2. **Review context**: Read `plan.md` and `specification.md` if they exist to understand project context
|
|
43
|
-
3. **
|
|
44
|
-
4. **
|
|
45
|
-
5. **
|
|
71
|
+
3. **Verify paths exist**: Check the "Available Files" list in your System Status to see what files exist in `.shotgun/`. If a path doesn't exist, tasks must CREATE it, not modify files within it.
|
|
72
|
+
4. **Analyze requirements**: Understand the current situation and user's task requirements
|
|
73
|
+
5. **Create structured tasks**: Use `write_file("tasks.md", content)` to save organized tasks
|
|
74
|
+
6. **Build incrementally**: Update and refine tasks based on new information
|
|
46
75
|
|
|
47
76
|
## TASK FORMAT
|
|
48
77
|
|
|
@@ -53,33 +82,36 @@ For task management:
|
|
|
53
82
|
|
|
54
83
|
## TASK FILE STRUCTURE
|
|
55
84
|
|
|
56
|
-
Start tasks.md with
|
|
85
|
+
**CRITICAL**: Start tasks.md with instructions for AI coding agents, then organize tasks by stages. Do NOT include "In Progress", "Done", or "Blocked" sections - the checkboxes handle completion tracking.
|
|
86
|
+
|
|
57
87
|
```markdown
|
|
58
88
|
# Task Management
|
|
59
89
|
|
|
60
|
-
## Instructions
|
|
61
|
-
- Mark tasks as complete by replacing `[ ]` with `[X]`
|
|
62
|
-
- Tasks without an `[X]` are not finished yet
|
|
63
|
-
```
|
|
90
|
+
## Instructions for AI Coding Agents
|
|
64
91
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
92
|
+
When working on these tasks:
|
|
93
|
+
1. Focus on ONE stage at a time, completing all tasks in that stage before moving to the next
|
|
94
|
+
2. Mark each task complete by replacing `[ ]` with `[X]` as you finish it
|
|
95
|
+
3. Do NOT modify any other content in this file unless explicitly instructed by the user
|
|
96
|
+
4. Tasks without an `[X]` are not finished yet
|
|
70
97
|
|
|
71
|
-
|
|
72
|
-
- [ ] Currently working on this task
|
|
73
|
-
- [ ] Task being actively developed
|
|
98
|
+
---
|
|
74
99
|
|
|
75
|
-
|
|
76
|
-
- [
|
|
77
|
-
- [
|
|
100
|
+
### Stage 1: [Stage Name]
|
|
101
|
+
- [ ] Task description with clear action
|
|
102
|
+
- [ ] Another specific task to complete
|
|
78
103
|
|
|
79
|
-
|
|
80
|
-
- [ ] Task
|
|
104
|
+
### Stage 2: [Stage Name]
|
|
105
|
+
- [ ] Task in the next stage
|
|
106
|
+
- [ ] Another task in this stage
|
|
81
107
|
```
|
|
82
108
|
|
|
109
|
+
**IMPORTANT**:
|
|
110
|
+
- Group related tasks under numbered stages (Stage 1, Stage 2, etc.)
|
|
111
|
+
- Do NOT create separate "In Progress", "Done", or "Blocked" sections
|
|
112
|
+
- The `[ ]` / `[X]` checkboxes are the ONLY mechanism for tracking completion
|
|
113
|
+
- Each stage should be completable independently before moving to the next
|
|
114
|
+
|
|
83
115
|
## TASK CREATION PRINCIPLES
|
|
84
116
|
|
|
85
117
|
- **ALWAYS use checkbox format `[ ]` for every task**
|
|
@@ -129,6 +161,14 @@ INTEGRATION WITH RESEARCH & PLAN:
|
|
|
129
161
|
- Create validation/testing tasks for success criteria from plan
|
|
130
162
|
- Break down high-level plan steps into granular, executable tasks
|
|
131
163
|
|
|
164
|
+
PATH VERIFICATION (CRITICAL):
|
|
165
|
+
- Before generating tasks that reference specific file paths in `.shotgun/`, check the "Available Files" list in your System Status
|
|
166
|
+
- If specification.md references files under `.shotgun/contracts/` that don't appear in "Available Files", DO NOT generate tasks for those files
|
|
167
|
+
- The Specification agent is responsible for creating contract files, not downstream coding agents
|
|
168
|
+
- Tasks should reference files that EXIST or will be created in the project codebase (src/, tests/, etc.), not in `.shotgun/`
|
|
169
|
+
- Only generate tasks for `.shotgun/` files if they appear in the "Available Files" list and the task is about modifying them
|
|
170
|
+
- Do NOT generate tasks referencing `.shotgun/contracts/*` files unless those files already exist
|
|
171
|
+
|
|
132
172
|
IMPORTANT RULES:
|
|
133
173
|
- Make at most 1 tasks file write per request
|
|
134
174
|
- Always base tasks on available research and plan when relevant
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
You are summarizing chunk {{ chunk_index }} of {{ total_chunks }} from a conversation that is too large to summarize at once.
|
|
2
|
+
|
|
3
|
+
Focus on preserving:
|
|
4
|
+
1. ALL file paths, function names, variable names, URLs, and identifiers VERBATIM
|
|
5
|
+
2. Key decisions and their rationale
|
|
6
|
+
3. Tool call results (summarize large outputs, keep critical data)
|
|
7
|
+
4. Important errors or issues encountered
|
|
8
|
+
5. User requests and how they were addressed
|
|
9
|
+
|
|
10
|
+
<CHUNK_CONTENT>
|
|
11
|
+
{{ chunk_content }}
|
|
12
|
+
</CHUNK_CONTENT>
|
|
13
|
+
|
|
14
|
+
<OUTPUT_FORMAT>
|
|
15
|
+
## Topics Covered
|
|
16
|
+
Brief bullet points of main topics discussed in this chunk.
|
|
17
|
+
|
|
18
|
+
## Entities & References
|
|
19
|
+
- Files: [list all file paths mentioned, verbatim]
|
|
20
|
+
- Functions/Classes: [list all function/method/class names, verbatim]
|
|
21
|
+
- Variables/Keys: [configuration keys, env vars, etc., verbatim]
|
|
22
|
+
- Links/IDs: [any URLs, ticket IDs, etc., verbatim]
|
|
23
|
+
|
|
24
|
+
## Actions & Results
|
|
25
|
+
Summarized tool calls and their outcomes. Preserve important data verbatim.
|
|
26
|
+
|
|
27
|
+
## Important Decisions
|
|
28
|
+
Key decisions made and their rationale.
|
|
29
|
+
|
|
30
|
+
## Status at Chunk End
|
|
31
|
+
Current active task or objective at the end of this chunk (if any), or "N/A" if this chunk completes without active work.
|
|
32
|
+
</OUTPUT_FORMAT>
|
|
33
|
+
|
|
34
|
+
Be concise but preserve all critical information. This summary will be combined with others to create a complete conversation summary.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
You are combining {{ num_summaries }} conversation chunk summaries into a unified summary.
|
|
2
|
+
|
|
3
|
+
<CHUNK_SUMMARIES>
|
|
4
|
+
{% for summary in chunk_summaries %}
|
|
5
|
+
--- Chunk {{ loop.index }} of {{ num_summaries }} ---
|
|
6
|
+
{{ summary }}
|
|
7
|
+
|
|
8
|
+
{% endfor %}
|
|
9
|
+
</CHUNK_SUMMARIES>
|
|
10
|
+
|
|
11
|
+
Instructions:
|
|
12
|
+
1. Merge overlapping information - don't repeat the same facts
|
|
13
|
+
2. Preserve ALL entity references (files, functions, variables) from ALL chunks
|
|
14
|
+
3. Maintain chronological timeline of actions
|
|
15
|
+
4. Identify patterns and evolution across chunks
|
|
16
|
+
5. The current status should reflect the LAST chunk's end state
|
|
17
|
+
|
|
18
|
+
<OUTPUT_FORMAT>
|
|
19
|
+
# Context
|
|
20
|
+
|
|
21
|
+
Short summary of the discussion, grouped by topics or tasks if any.
|
|
22
|
+
Present it as clear bullet points. Be brief but do not lose information that might be important for the current state, task or objectives.
|
|
23
|
+
|
|
24
|
+
# Key elements, learnings and entities
|
|
25
|
+
|
|
26
|
+
Present the important elements of context, learnings and entities from the discussion. Be very detailed.
|
|
27
|
+
|
|
28
|
+
Present it as clear bullet points. Be brief but do not lose information that might be important for the current state, task or objectives.
|
|
29
|
+
|
|
30
|
+
If the agent obtained information via tools, preserve it verbatim if it was short, summarize it if it was long focusing on preserving all of the most important facts and information.
|
|
31
|
+
|
|
32
|
+
If there are any links mentioned, IDs, filenames, etc. - preserve them verbatim.
|
|
33
|
+
|
|
34
|
+
# Timeline
|
|
35
|
+
|
|
36
|
+
For important tasks and content, provide
|
|
37
|
+
- User: asked to handle a complex task X: <summary of the task>
|
|
38
|
+
- Assistant: we handle it by doing A, B, C...
|
|
39
|
+
- Tools used and output summary
|
|
40
|
+
|
|
41
|
+
...
|
|
42
|
+
- User: asked to handle a complex task Y: <summary of the task>
|
|
43
|
+
- Assistant: we handle it by doing A, B, C...
|
|
44
|
+
- Tools used and output summary
|
|
45
|
+
|
|
46
|
+
Do not hesitate to merge original messages, remove noise. We want to be as concise and brief as possible.
|
|
47
|
+
|
|
48
|
+
# Current status, task and objectives
|
|
49
|
+
|
|
50
|
+
Based on the conversation, include here the current status, task and objectives.
|
|
51
|
+
|
|
52
|
+
CRITICAL: This is not about summarizing or compacting. We are talking about the status, task and objectives currently active in the conversation to keep as much context as possible regarding the last messages.
|
|
53
|
+
</OUTPUT_FORMAT>
|
shotgun/sdk/codebase.py
CHANGED
|
@@ -93,6 +93,19 @@ class CodebaseSDK:
|
|
|
93
93
|
if indexed_from_cwd is None:
|
|
94
94
|
indexed_from_cwd = str(Path.cwd().resolve())
|
|
95
95
|
|
|
96
|
+
# Track codebase indexing started event
|
|
97
|
+
source = detect_source()
|
|
98
|
+
logger.debug(
|
|
99
|
+
"Tracking codebase_index_started event: source=%s",
|
|
100
|
+
source,
|
|
101
|
+
)
|
|
102
|
+
track_event(
|
|
103
|
+
"codebase_index_started",
|
|
104
|
+
{
|
|
105
|
+
"source": source,
|
|
106
|
+
},
|
|
107
|
+
)
|
|
108
|
+
|
|
96
109
|
graph = await self.service.create_graph(
|
|
97
110
|
resolved_path,
|
|
98
111
|
name,
|
|
@@ -101,9 +114,7 @@ class CodebaseSDK:
|
|
|
101
114
|
)
|
|
102
115
|
file_count = sum(graph.language_stats.values()) if graph.language_stats else 0
|
|
103
116
|
|
|
104
|
-
# Track codebase indexing event
|
|
105
|
-
# Detect if called from TUI by checking the call stack
|
|
106
|
-
source = detect_source()
|
|
117
|
+
# Track codebase indexing completion event (reuse source from start event)
|
|
107
118
|
|
|
108
119
|
logger.debug(
|
|
109
120
|
"Tracking codebase_indexed event: file_count=%d, node_count=%d, relationship_count=%d, source=%s",
|
shotgun/settings.py
CHANGED
|
@@ -108,6 +108,11 @@ class LoggingSettings(BaseSettings):
|
|
|
108
108
|
default=True,
|
|
109
109
|
description="Enable file logging output",
|
|
110
110
|
)
|
|
111
|
+
max_log_files: int = Field(
|
|
112
|
+
default=10,
|
|
113
|
+
description="Maximum number of log files to keep (older files are deleted)",
|
|
114
|
+
ge=1,
|
|
115
|
+
)
|
|
111
116
|
|
|
112
117
|
model_config = SettingsConfigDict(
|
|
113
118
|
env_prefix="SHOTGUN_",
|
shotgun/shotgun_web/__init__.py
CHANGED
|
@@ -1,14 +1,48 @@
|
|
|
1
|
-
"""Shotgun Web API client for subscription and
|
|
1
|
+
"""Shotgun Web API client for subscription, authentication, and shared specs."""
|
|
2
2
|
|
|
3
3
|
from .client import ShotgunWebClient, check_token_status, create_unification_token
|
|
4
|
+
from .exceptions import (
|
|
5
|
+
ConflictError,
|
|
6
|
+
ForbiddenError,
|
|
7
|
+
NotFoundError,
|
|
8
|
+
PayloadTooLargeError,
|
|
9
|
+
RateLimitExceededError,
|
|
10
|
+
ShotgunWebError,
|
|
11
|
+
UnauthorizedError,
|
|
12
|
+
)
|
|
4
13
|
from .models import (
|
|
14
|
+
# Specs models
|
|
15
|
+
ErrorDetail,
|
|
16
|
+
ErrorResponse,
|
|
17
|
+
FileListResponse,
|
|
18
|
+
FileMetadata,
|
|
19
|
+
FileUploadRequest,
|
|
20
|
+
FileUploadResponse,
|
|
21
|
+
PermissionCheckResponse,
|
|
22
|
+
PublicSpecResponse,
|
|
23
|
+
SpecCreateRequest,
|
|
24
|
+
SpecCreateResponse,
|
|
25
|
+
SpecFileResponse,
|
|
26
|
+
SpecListResponse,
|
|
27
|
+
SpecResponse,
|
|
28
|
+
SpecUpdateRequest,
|
|
29
|
+
SpecVersionCreateRequest,
|
|
30
|
+
SpecVersionResponse,
|
|
31
|
+
SpecVersionState,
|
|
32
|
+
# Token models
|
|
5
33
|
TokenCreateRequest,
|
|
6
34
|
TokenCreateResponse,
|
|
7
35
|
TokenStatus,
|
|
8
36
|
TokenStatusResponse,
|
|
37
|
+
VersionCloseResponse,
|
|
38
|
+
VersionCreateResponse,
|
|
39
|
+
VersionListResponse,
|
|
40
|
+
WorkspaceRole,
|
|
9
41
|
)
|
|
42
|
+
from .specs_client import SpecsClient
|
|
10
43
|
|
|
11
44
|
__all__ = [
|
|
45
|
+
# Existing exports
|
|
12
46
|
"ShotgunWebClient",
|
|
13
47
|
"create_unification_token",
|
|
14
48
|
"check_token_status",
|
|
@@ -16,4 +50,36 @@ __all__ = [
|
|
|
16
50
|
"TokenCreateResponse",
|
|
17
51
|
"TokenStatus",
|
|
18
52
|
"TokenStatusResponse",
|
|
53
|
+
# Specs client
|
|
54
|
+
"SpecsClient",
|
|
55
|
+
# Exceptions
|
|
56
|
+
"ShotgunWebError",
|
|
57
|
+
"UnauthorizedError",
|
|
58
|
+
"ForbiddenError",
|
|
59
|
+
"NotFoundError",
|
|
60
|
+
"ConflictError",
|
|
61
|
+
"PayloadTooLargeError",
|
|
62
|
+
"RateLimitExceededError",
|
|
63
|
+
# Specs models
|
|
64
|
+
"ErrorDetail",
|
|
65
|
+
"ErrorResponse",
|
|
66
|
+
"FileListResponse",
|
|
67
|
+
"FileMetadata",
|
|
68
|
+
"FileUploadRequest",
|
|
69
|
+
"FileUploadResponse",
|
|
70
|
+
"PermissionCheckResponse",
|
|
71
|
+
"PublicSpecResponse",
|
|
72
|
+
"SpecCreateRequest",
|
|
73
|
+
"SpecCreateResponse",
|
|
74
|
+
"SpecFileResponse",
|
|
75
|
+
"SpecListResponse",
|
|
76
|
+
"SpecResponse",
|
|
77
|
+
"SpecUpdateRequest",
|
|
78
|
+
"SpecVersionCreateRequest",
|
|
79
|
+
"SpecVersionResponse",
|
|
80
|
+
"SpecVersionState",
|
|
81
|
+
"VersionCloseResponse",
|
|
82
|
+
"VersionCreateResponse",
|
|
83
|
+
"VersionListResponse",
|
|
84
|
+
"WorkspaceRole",
|
|
19
85
|
]
|
shotgun/shotgun_web/client.py
CHANGED
|
@@ -5,11 +5,13 @@ import httpx
|
|
|
5
5
|
from shotgun.logging_config import get_logger
|
|
6
6
|
|
|
7
7
|
from .constants import (
|
|
8
|
+
ME_PATH,
|
|
8
9
|
SHOTGUN_WEB_BASE_URL,
|
|
9
10
|
UNIFICATION_TOKEN_CREATE_PATH,
|
|
10
11
|
UNIFICATION_TOKEN_STATUS_PATH,
|
|
11
12
|
)
|
|
12
13
|
from .models import (
|
|
14
|
+
MeResponse,
|
|
13
15
|
TokenCreateRequest,
|
|
14
16
|
TokenCreateResponse,
|
|
15
17
|
TokenStatusResponse,
|
|
@@ -66,7 +68,7 @@ class ShotgunWebClient:
|
|
|
66
68
|
return result
|
|
67
69
|
|
|
68
70
|
except httpx.HTTPError as e:
|
|
69
|
-
logger.error("Failed to create unification token: %s", e)
|
|
71
|
+
logger.error("Failed to create unification token at %s: %s", url, e)
|
|
70
72
|
raise
|
|
71
73
|
|
|
72
74
|
def check_token_status(self, token: str) -> TokenStatusResponse:
|
|
@@ -106,6 +108,45 @@ class ShotgunWebClient:
|
|
|
106
108
|
logger.error("Failed to check token status: %s", e)
|
|
107
109
|
raise
|
|
108
110
|
|
|
111
|
+
def get_me(self, jwt: str) -> MeResponse:
|
|
112
|
+
"""Get current user info including workspace.
|
|
113
|
+
|
|
114
|
+
Args:
|
|
115
|
+
jwt: Supabase JWT for authentication
|
|
116
|
+
|
|
117
|
+
Returns:
|
|
118
|
+
User info including workspace details
|
|
119
|
+
|
|
120
|
+
Raises:
|
|
121
|
+
httpx.HTTPStatusError: If authentication fails (401) or other HTTP errors
|
|
122
|
+
httpx.HTTPError: For other request failures
|
|
123
|
+
"""
|
|
124
|
+
url = f"{self.base_url}{ME_PATH}"
|
|
125
|
+
|
|
126
|
+
logger.debug("Fetching user info from /api/me")
|
|
127
|
+
|
|
128
|
+
try:
|
|
129
|
+
response = httpx.get(
|
|
130
|
+
url,
|
|
131
|
+
headers={"Authorization": f"Bearer {jwt}"},
|
|
132
|
+
timeout=self.timeout,
|
|
133
|
+
)
|
|
134
|
+
response.raise_for_status()
|
|
135
|
+
|
|
136
|
+
data = response.json()
|
|
137
|
+
result = MeResponse.model_validate(data)
|
|
138
|
+
|
|
139
|
+
logger.info("Successfully fetched user info for %s", result.email)
|
|
140
|
+
return result
|
|
141
|
+
|
|
142
|
+
except httpx.HTTPStatusError as e:
|
|
143
|
+
if e.response.status_code == 401:
|
|
144
|
+
logger.error("Authentication failed for /api/me")
|
|
145
|
+
raise
|
|
146
|
+
except httpx.HTTPError as e:
|
|
147
|
+
logger.error("Failed to fetch user info: %s", e)
|
|
148
|
+
raise
|
|
149
|
+
|
|
109
150
|
|
|
110
151
|
# Convenience functions for standalone use
|
|
111
152
|
def create_unification_token(shotgun_instance_id: str) -> TokenCreateResponse:
|
shotgun/shotgun_web/constants.py
CHANGED
|
@@ -6,16 +6,62 @@ from shotgun.api_endpoints import SHOTGUN_WEB_BASE_URL
|
|
|
6
6
|
# API endpoints
|
|
7
7
|
UNIFICATION_TOKEN_CREATE_PATH = "/api/unification/token/create" # noqa: S105
|
|
8
8
|
UNIFICATION_TOKEN_STATUS_PATH = "/api/unification/token/{token}/status" # noqa: S105
|
|
9
|
+
ME_PATH = "/api/me"
|
|
9
10
|
|
|
10
11
|
# Polling configuration
|
|
11
12
|
DEFAULT_POLL_INTERVAL_SECONDS = 3
|
|
12
13
|
DEFAULT_TOKEN_TIMEOUT_SECONDS = 1800 # 30 minutes
|
|
13
14
|
|
|
15
|
+
# Workspaces API endpoint
|
|
16
|
+
WORKSPACES_PATH = "/api/workspaces"
|
|
17
|
+
|
|
18
|
+
# Specs API endpoints
|
|
19
|
+
PERMISSIONS_PATH = "/api/workspaces/{workspace_id}/specs/permissions"
|
|
20
|
+
SPECS_BASE_PATH = "/api/workspaces/{workspace_id}/specs"
|
|
21
|
+
SPECS_DETAIL_PATH = "/api/workspaces/{workspace_id}/specs/{spec_id}"
|
|
22
|
+
VERSIONS_PATH = "/api/workspaces/{workspace_id}/specs/{spec_id}/versions"
|
|
23
|
+
VERSION_DETAIL_PATH = (
|
|
24
|
+
"/api/workspaces/{workspace_id}/specs/{spec_id}/versions/{version_id}"
|
|
25
|
+
)
|
|
26
|
+
VERSION_CLOSE_PATH = (
|
|
27
|
+
"/api/workspaces/{workspace_id}/specs/{spec_id}/versions/{version_id}/close"
|
|
28
|
+
)
|
|
29
|
+
VERSION_SET_LATEST_PATH = (
|
|
30
|
+
"/api/workspaces/{workspace_id}/specs/{spec_id}/versions/{version_id}/set-latest"
|
|
31
|
+
)
|
|
32
|
+
FILES_PATH = (
|
|
33
|
+
"/api/workspaces/{workspace_id}/specs/{spec_id}/versions/{version_id}/files"
|
|
34
|
+
)
|
|
35
|
+
FILE_DETAIL_PATH = "/api/workspaces/{workspace_id}/specs/{spec_id}/versions/{version_id}/files/{file_id}"
|
|
36
|
+
PUBLIC_SPEC_PATH = "/api/public/specs/{spec_id}"
|
|
37
|
+
PUBLIC_SPEC_FILES_PATH = "/api/public/specs/{spec_id}/files"
|
|
38
|
+
PUBLIC_FILE_PATH = "/api/public/specs/{spec_id}/files/{file_id}"
|
|
39
|
+
|
|
40
|
+
# CLI convenience endpoint (version lookup by ID only)
|
|
41
|
+
VERSION_BY_ID_PATH = "/api/versions/{version_id}"
|
|
42
|
+
|
|
14
43
|
# Re-export for backward compatibility
|
|
15
44
|
__all__ = [
|
|
16
45
|
"SHOTGUN_WEB_BASE_URL",
|
|
17
46
|
"UNIFICATION_TOKEN_CREATE_PATH",
|
|
18
47
|
"UNIFICATION_TOKEN_STATUS_PATH",
|
|
48
|
+
"ME_PATH",
|
|
19
49
|
"DEFAULT_POLL_INTERVAL_SECONDS",
|
|
20
50
|
"DEFAULT_TOKEN_TIMEOUT_SECONDS",
|
|
51
|
+
# Workspaces endpoint
|
|
52
|
+
"WORKSPACES_PATH",
|
|
53
|
+
# Specs endpoints
|
|
54
|
+
"PERMISSIONS_PATH",
|
|
55
|
+
"SPECS_BASE_PATH",
|
|
56
|
+
"SPECS_DETAIL_PATH",
|
|
57
|
+
"VERSIONS_PATH",
|
|
58
|
+
"VERSION_DETAIL_PATH",
|
|
59
|
+
"VERSION_CLOSE_PATH",
|
|
60
|
+
"VERSION_SET_LATEST_PATH",
|
|
61
|
+
"FILES_PATH",
|
|
62
|
+
"FILE_DETAIL_PATH",
|
|
63
|
+
"PUBLIC_SPEC_PATH",
|
|
64
|
+
"PUBLIC_SPEC_FILES_PATH",
|
|
65
|
+
"PUBLIC_FILE_PATH",
|
|
66
|
+
"VERSION_BY_ID_PATH",
|
|
21
67
|
]
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""Typed exceptions for Shotgun Web API operations."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class ShotgunWebError(Exception):
|
|
5
|
+
"""Base exception for Shotgun Web API operations."""
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class UnauthorizedError(ShotgunWebError):
|
|
9
|
+
"""401 - Missing or invalid authentication token."""
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ForbiddenError(ShotgunWebError):
|
|
13
|
+
"""403 - Insufficient permissions for the requested operation."""
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class NotFoundError(ShotgunWebError):
|
|
17
|
+
"""404 - Resource not found."""
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class ConflictError(ShotgunWebError):
|
|
21
|
+
"""409 - Name conflict or duplicate path."""
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class PayloadTooLargeError(ShotgunWebError):
|
|
25
|
+
"""413 - File exceeds maximum size limit."""
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class RateLimitExceededError(ShotgunWebError):
|
|
29
|
+
"""429 - Rate limit exceeded."""
|