shotgun-sh 0.1.0.dev22__py3-none-any.whl → 0.1.0.dev23__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of shotgun-sh might be problematic. Click here for more details.

Files changed (51) hide show
  1. shotgun/agents/agent_manager.py +95 -15
  2. shotgun/agents/common.py +139 -24
  3. shotgun/agents/conversation_history.py +56 -0
  4. shotgun/agents/conversation_manager.py +105 -0
  5. shotgun/agents/export.py +5 -2
  6. shotgun/agents/models.py +16 -7
  7. shotgun/agents/plan.py +2 -1
  8. shotgun/agents/research.py +2 -1
  9. shotgun/agents/specify.py +2 -1
  10. shotgun/agents/tasks.py +5 -2
  11. shotgun/agents/tools/file_management.py +67 -2
  12. shotgun/main.py +9 -1
  13. shotgun/prompts/agents/export.j2 +14 -11
  14. shotgun/prompts/agents/partials/common_agent_system_prompt.j2 +6 -9
  15. shotgun/prompts/agents/plan.j2 +9 -13
  16. shotgun/prompts/agents/research.j2 +11 -14
  17. shotgun/prompts/agents/specify.j2 +9 -12
  18. shotgun/prompts/agents/state/system_state.j2 +27 -5
  19. shotgun/prompts/agents/tasks.j2 +12 -12
  20. shotgun/sdk/services.py +0 -14
  21. shotgun/tui/app.py +9 -4
  22. shotgun/tui/screens/chat.py +57 -8
  23. shotgun/tui/screens/chat_screen/command_providers.py +1 -1
  24. shotgun/tui/screens/chat_screen/history.py +6 -0
  25. shotgun/tui/utils/mode_progress.py +111 -78
  26. {shotgun_sh-0.1.0.dev22.dist-info → shotgun_sh-0.1.0.dev23.dist-info}/METADATA +8 -9
  27. {shotgun_sh-0.1.0.dev22.dist-info → shotgun_sh-0.1.0.dev23.dist-info}/RECORD +30 -49
  28. shotgun/agents/artifact_state.py +0 -58
  29. shotgun/agents/tools/artifact_management.py +0 -481
  30. shotgun/artifacts/__init__.py +0 -17
  31. shotgun/artifacts/exceptions.py +0 -89
  32. shotgun/artifacts/manager.py +0 -530
  33. shotgun/artifacts/models.py +0 -334
  34. shotgun/artifacts/service.py +0 -463
  35. shotgun/artifacts/templates/__init__.py +0 -10
  36. shotgun/artifacts/templates/loader.py +0 -252
  37. shotgun/artifacts/templates/models.py +0 -136
  38. shotgun/artifacts/templates/plan/delivery_and_release_plan.yaml +0 -66
  39. shotgun/artifacts/templates/research/market_research.yaml +0 -585
  40. shotgun/artifacts/templates/research/sdk_comparison.yaml +0 -257
  41. shotgun/artifacts/templates/specify/prd.yaml +0 -331
  42. shotgun/artifacts/templates/specify/product_spec.yaml +0 -301
  43. shotgun/artifacts/utils.py +0 -76
  44. shotgun/prompts/agents/partials/artifact_system.j2 +0 -32
  45. shotgun/prompts/agents/state/artifact_templates_available.j2 +0 -20
  46. shotgun/prompts/agents/state/existing_artifacts_available.j2 +0 -25
  47. shotgun/sdk/artifact_models.py +0 -186
  48. shotgun/sdk/artifacts.py +0 -448
  49. {shotgun_sh-0.1.0.dev22.dist-info → shotgun_sh-0.1.0.dev23.dist-info}/WHEEL +0 -0
  50. {shotgun_sh-0.1.0.dev22.dist-info → shotgun_sh-0.1.0.dev23.dist-info}/entry_points.txt +0 -0
  51. {shotgun_sh-0.1.0.dev22.dist-info → shotgun_sh-0.1.0.dev23.dist-info}/licenses/LICENSE +0 -0
@@ -1,252 +0,0 @@
1
- """Template loader for artifact templates."""
2
-
3
- from pathlib import Path
4
-
5
- import yaml
6
-
7
- from shotgun.artifacts.models import AgentMode
8
- from shotgun.logging_config import setup_logger
9
- from shotgun.utils.file_system_utils import get_shotgun_home
10
-
11
- from .models import ArtifactTemplate, TemplateSummary
12
-
13
- logger = setup_logger(__name__)
14
-
15
- TEMPLATES_DIR = Path(__file__).parent
16
-
17
-
18
- class ArtifactTemplateLoader:
19
- """Loads and manages artifact templates from the filesystem."""
20
-
21
- def __init__(
22
- self, templates_dir: Path | None = None, include_user_templates: bool = True
23
- ):
24
- """Initialize the template loader.
25
-
26
- Args:
27
- templates_dir: Directory containing template files. Defaults to package templates.
28
- include_user_templates: Whether to include user templates from ~/.shotgun-sh/templates/
29
- """
30
- self.templates_dir = templates_dir or TEMPLATES_DIR
31
- self.include_user_templates = include_user_templates
32
- self.user_templates_dir = (
33
- get_shotgun_home() / "templates" if include_user_templates else None
34
- )
35
-
36
- def _discover_templates(self) -> dict[str, tuple[Path, AgentMode]]:
37
- """Discover all template files from both built-in and user directories."""
38
- # Start with built-in templates
39
- templates = self._discover_templates_from_dir(self.templates_dir, "built-in")
40
-
41
- # Add user templates (they can override built-in ones)
42
- if (
43
- self.include_user_templates
44
- and self.user_templates_dir
45
- and self.user_templates_dir.exists()
46
- ):
47
- user_templates = self._discover_templates_from_dir(
48
- self.user_templates_dir, "user"
49
- )
50
- templates.update(
51
- user_templates
52
- ) # User templates override built-in ones with same ID
53
-
54
- return templates
55
-
56
- def _discover_templates_from_dir(
57
- self, templates_dir: Path, source_type: str
58
- ) -> dict[str, tuple[Path, AgentMode]]:
59
- """Discover template files from a specific directory.
60
-
61
- Args:
62
- templates_dir: Directory to search for templates
63
- source_type: Type of templates ("built-in" or "user") for logging
64
-
65
- Returns:
66
- Dictionary mapping template IDs to (path, agent_mode) tuples
67
- """
68
- templates: dict[str, tuple[Path, AgentMode]] = {}
69
-
70
- if not templates_dir.exists():
71
- if source_type == "built-in":
72
- logger.warning("Templates directory does not exist: %s", templates_dir)
73
- else:
74
- logger.debug(
75
- "User templates directory does not exist: %s", templates_dir
76
- )
77
- return templates
78
-
79
- logger.debug("Discovering %s templates from: %s", source_type, templates_dir)
80
-
81
- for mode_dir in templates_dir.iterdir():
82
- if not mode_dir.is_dir() or mode_dir.name.startswith("."):
83
- continue
84
-
85
- # Skip common non-agent directories without warnings
86
- if mode_dir.name in ("__pycache__", "node_modules", ".git", ".DS_Store"):
87
- continue
88
-
89
- try:
90
- agent_mode = AgentMode(mode_dir.name)
91
- except ValueError:
92
- logger.warning(
93
- "Unknown agent mode directory in %s templates: %s",
94
- source_type,
95
- mode_dir.name,
96
- )
97
- continue
98
-
99
- # Find all YAML files in this mode directory
100
- for template_file in mode_dir.glob("*.yaml"):
101
- template_id = template_file.stem
102
- full_template_id = f"{agent_mode.value}/{template_id}"
103
- templates[full_template_id] = (template_file, agent_mode)
104
- logger.debug("Found %s template: %s", source_type, full_template_id)
105
-
106
- return templates
107
-
108
- def _load_template_file(
109
- self, template_path: Path, agent_mode: AgentMode, template_id: str
110
- ) -> ArtifactTemplate | None:
111
- """Load a single template file."""
112
- try:
113
- with open(template_path, encoding="utf-8") as f:
114
- data = yaml.safe_load(f)
115
-
116
- # Validate required fields
117
- required_fields = ["name", "purpose", "prompt"]
118
- for field in required_fields:
119
- if field not in data:
120
- logger.error(
121
- "Template %s missing required field '%s'", template_path, field
122
- )
123
- return None
124
-
125
- template = ArtifactTemplate.from_yaml_dict(data, agent_mode, template_id)
126
-
127
- # Validate dependencies
128
- dependency_errors = template.validate_dependencies()
129
- if dependency_errors:
130
- logger.error(
131
- "Template %s has dependency errors: %s",
132
- template_path,
133
- dependency_errors,
134
- )
135
- return None
136
-
137
- return template
138
-
139
- except yaml.YAMLError as e:
140
- logger.error("Failed to parse template YAML %s: %s", template_path, e)
141
- return None
142
- except Exception as e:
143
- logger.error("Failed to load template %s: %s", template_path, e)
144
- return None
145
-
146
- def get_template(self, template_id: str) -> ArtifactTemplate | None:
147
- """Get a template by ID.
148
-
149
- Args:
150
- template_id: Full template ID (e.g., 'research/market_research')
151
-
152
- Returns:
153
- Template instance or None if not found
154
- """
155
- discovered = self._discover_templates()
156
- if template_id not in discovered:
157
- return None
158
-
159
- template_path, agent_mode = discovered[template_id]
160
- return self._load_template_file(template_path, agent_mode, template_id)
161
-
162
- def list_templates(
163
- self, agent_mode: AgentMode | None = None
164
- ) -> list[TemplateSummary]:
165
- """List all available templates, optionally filtered by agent mode.
166
-
167
- Args:
168
- agent_mode: Optional agent mode filter
169
-
170
- Returns:
171
- List of template summaries
172
- """
173
- discovered = self._discover_templates()
174
- summaries = []
175
-
176
- for full_template_id, (
177
- template_path,
178
- template_agent_mode,
179
- ) in discovered.items():
180
- if agent_mode and template_agent_mode != agent_mode:
181
- continue
182
-
183
- template = self._load_template_file(
184
- template_path, template_agent_mode, full_template_id
185
- )
186
- if template:
187
- summaries.append(
188
- TemplateSummary(
189
- template_id=full_template_id,
190
- name=template.name,
191
- purpose=template.purpose,
192
- agent_mode=template.agent_mode,
193
- section_count=len(template.sections),
194
- )
195
- )
196
-
197
- # Sort by agent mode, then by template name
198
- summaries.sort(key=lambda s: (s.agent_mode.value, s.name))
199
- return summaries
200
-
201
- def get_templates_for_mode(self, agent_mode: AgentMode) -> list[ArtifactTemplate]:
202
- """Get all templates for a specific agent mode.
203
-
204
- Args:
205
- agent_mode: Agent mode to filter by
206
-
207
- Returns:
208
- List of templates for the specified mode
209
- """
210
- discovered = self._discover_templates()
211
- templates = []
212
-
213
- for full_template_id, (
214
- template_path,
215
- template_agent_mode,
216
- ) in discovered.items():
217
- if template_agent_mode == agent_mode:
218
- template = self._load_template_file(
219
- template_path, template_agent_mode, full_template_id
220
- )
221
- if template:
222
- templates.append(template)
223
-
224
- templates.sort(key=lambda t: t.name)
225
- return templates
226
-
227
- def template_exists(self, template_id: str) -> bool:
228
- """Check if a template exists.
229
-
230
- Args:
231
- template_id: Template ID to check
232
-
233
- Returns:
234
- True if template exists, False otherwise
235
- """
236
- discovered = self._discover_templates()
237
- return template_id in discovered
238
-
239
- def get_template_by_short_id(
240
- self, short_id: str, agent_mode: AgentMode
241
- ) -> ArtifactTemplate | None:
242
- """Get template by short ID (without agent mode prefix).
243
-
244
- Args:
245
- short_id: Short template ID (e.g., 'market_research')
246
- agent_mode: Agent mode to search within
247
-
248
- Returns:
249
- Template instance or None if not found
250
- """
251
- full_id = f"{agent_mode.value}/{short_id}"
252
- return self.get_template(full_id)
@@ -1,136 +0,0 @@
1
- """Models for artifact templates."""
2
-
3
- from typing import Any
4
-
5
- from pydantic import BaseModel, Field
6
-
7
- from shotgun.artifacts.models import AgentMode
8
-
9
-
10
- class TemplateSection(BaseModel):
11
- """Represents a section in an artifact template."""
12
-
13
- instructions: str = Field(..., description="Detailed instructions for this section")
14
- depends_on: list[str] = Field(
15
- default_factory=list, description="List of section keys this section depends on"
16
- )
17
-
18
- @property
19
- def formatted_instructions(self) -> str:
20
- """Return formatted instructions for display."""
21
- return self.instructions.strip()
22
-
23
-
24
- class ArtifactTemplate(BaseModel):
25
- """Represents an artifact template with structured guidance."""
26
-
27
- name: str = Field(..., description="Display name of the template")
28
- purpose: str = Field(..., description="Description of what this template is for")
29
- prompt: str = Field(..., description="System prompt for agents using this template")
30
- sections: dict[str, TemplateSection] = Field(
31
- default_factory=dict, description="Template sections with instructions"
32
- )
33
- agent_mode: AgentMode = Field(
34
- ..., description="Agent mode this template belongs to"
35
- )
36
- template_id: str = Field(..., description="Unique identifier for the template")
37
-
38
- def get_section(self, section_key: str) -> TemplateSection | None:
39
- """Get a template section by key."""
40
- return self.sections.get(section_key)
41
-
42
- def get_ordered_sections(self) -> list[tuple[str, TemplateSection]]:
43
- """Get sections in dependency order."""
44
- # Simple topological sort for section dependencies
45
- ordered = []
46
- processed = set()
47
-
48
- def process_section(key: str, section: TemplateSection) -> None:
49
- if key in processed:
50
- return
51
-
52
- # Process dependencies first
53
- for dep_key in section.depends_on:
54
- if dep_key in self.sections and dep_key not in processed:
55
- process_section(dep_key, self.sections[dep_key])
56
-
57
- ordered.append((key, section))
58
- processed.add(key)
59
-
60
- for key, section in self.sections.items():
61
- process_section(key, section)
62
-
63
- return ordered
64
-
65
- def get_section_keys(self) -> list[str]:
66
- """Get all section keys."""
67
- return list(self.sections.keys())
68
-
69
- def validate_dependencies(self) -> list[str]:
70
- """Validate that all section dependencies exist. Returns list of errors."""
71
- errors = []
72
-
73
- for section_key, section in self.sections.items():
74
- for dep_key in section.depends_on:
75
- if dep_key not in self.sections:
76
- errors.append(
77
- f"Section '{section_key}' depends on non-existent section '{dep_key}'"
78
- )
79
-
80
- return errors
81
-
82
- def to_yaml_dict(self) -> dict[str, Any]:
83
- """Convert to dictionary suitable for YAML serialization."""
84
- return {
85
- "name": self.name,
86
- "purpose": self.purpose,
87
- "prompt": self.prompt,
88
- "template_id": self.template_id,
89
- "sections": {
90
- key: {
91
- "instructions": section.instructions,
92
- **(
93
- {"depends_on": section.depends_on} if section.depends_on else {}
94
- ),
95
- }
96
- for key, section in self.sections.items()
97
- },
98
- }
99
-
100
- @classmethod
101
- def from_yaml_dict(
102
- cls, data: dict[str, Any], agent_mode: AgentMode, template_id: str
103
- ) -> "ArtifactTemplate":
104
- """Create template from YAML dictionary."""
105
- sections = {}
106
-
107
- for section_key, section_data in data.get("sections", {}).items():
108
- sections[section_key] = TemplateSection(
109
- instructions=section_data.get("instructions", ""),
110
- depends_on=section_data.get("depends_on", []),
111
- )
112
-
113
- return cls(
114
- name=data["name"],
115
- purpose=data["purpose"],
116
- prompt=data["prompt"],
117
- sections=sections,
118
- agent_mode=agent_mode,
119
- template_id=template_id,
120
- )
121
-
122
-
123
- class TemplateSummary(BaseModel):
124
- """Summary information about a template for listing purposes."""
125
-
126
- template_id: str = Field(..., description="Unique identifier for the template")
127
- name: str = Field(..., description="Display name of the template")
128
- purpose: str = Field(..., description="Description of what this template is for")
129
- agent_mode: AgentMode = Field(
130
- ..., description="Agent mode this template belongs to"
131
- )
132
- section_count: int = Field(..., description="Number of sections in the template")
133
-
134
- def __str__(self) -> str:
135
- """String representation for CLI display."""
136
- return f"{self.template_id}: {self.purpose}"
@@ -1,66 +0,0 @@
1
- name: Delivery & Release Plan
2
- purpose: |
3
- Convert scope and roadmap into a staffed, scheduled, dependency-aware execution plan with
4
- clear ownership, milestones, risk management, and cutover/rollback strategies.
5
- prompt: |
6
- Use this template to build a delivery plan we have the specification ready.
7
- The output should enable both engineering and product stakeholders to track progress,
8
- manage dependencies, and prepare for a safe release.
9
- sections:
10
- delivery.workstreams:
11
- instructions: |
12
- Define parallel streams of work (e.g., Backend, Frontend, Data, DevOps, QA, Security).
13
- For each: owner (DRI), objectives, deliverables, and relation to PRD scope.
14
- wbs.estimates:
15
- instructions: |
16
- Create a Work Breakdown Structure. Break epics → features → stories → tasks.
17
- Provide estimates (T-shirt sizes + P50/P90 time in days). Highlight critical path items.
18
- Include buffers for integration and testing.
19
- capacity_and_staffing:
20
- instructions: |
21
- List team members, roles, and availability (holidays, partial allocations).
22
- Define velocity assumptions and utilization factors.
23
- Highlight gaps and hiring needs if capacity < required.
24
- dependency_map:
25
- instructions: |
26
- Capture internal and external dependencies (libraries, vendors, APIs).
27
- Define lead times and SLAs. Show dependency graph (Mermaid or table).
28
- Suggest decoupling via feature flags, mocks, or phased integration.
29
- schedule.plan:
30
- instructions: |
31
- Translate WBS + capacity into a timeline. Provide both:
32
- - High-level milestones (alpha, beta, GA)
33
- - Detailed Gantt/roadmap (Mermaid or markdown tables)
34
- Show critical path and dependency-driven slips.
35
- raci_and_ceremonies:
36
- instructions: |
37
- Define RACI (Responsible, Accountable, Consulted, Informed) for decisions.
38
- Define governance cadences: daily standups, weekly demos, fortnightly checkpoints.
39
- Specify escalation path for blockers.
40
- quality_and_testing:
41
- instructions: |
42
- Define QA strategy: test types (unit, integration, regression, perf).
43
- Define gates: test coverage %, perf benchmarks, security scans.
44
- Specify environments: staging, pre-prod, canary.
45
- cutover_and_rollback:
46
- instructions: |
47
- Provide release checklist: build, config, migrations, monitoring, comms.
48
- Define dark launch/canary steps, health checks, rollback triggers, and procedures.
49
- Specify post-release monitoring window and owner on-call.
50
- release_comms:
51
- instructions: |
52
- Draft internal and external comms: release notes, stakeholder briefings, customer updates.
53
- Specify timing relative to rollout phases.
54
- delivery_risks:
55
- instructions: |
56
- Maintain a risk register: description, prob×impact, owner, mitigation, contingency.
57
- Track status weekly. Mark high-priority risks explicitly.
58
- metrics_and_tracking:
59
- instructions: |
60
- Link to PRD success metrics and Experiment Plan metrics.
61
- Define delivery metrics: burn-down, velocity, on-time %.
62
- Provide dashboards or reporting cadence.
63
- status_reporting:
64
- instructions: |
65
- Define status update format (traffic-light summary, burndown chart, risk log).
66
- Specify frequency (weekly) and audience (exec, product, eng).