shotgun-sh 0.2.17__py3-none-any.whl → 0.3.3.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.
Files changed (112) hide show
  1. shotgun/agents/agent_manager.py +28 -14
  2. shotgun/agents/common.py +1 -1
  3. shotgun/agents/config/README.md +89 -0
  4. shotgun/agents/config/__init__.py +10 -1
  5. shotgun/agents/config/manager.py +323 -53
  6. shotgun/agents/config/models.py +85 -21
  7. shotgun/agents/config/provider.py +51 -13
  8. shotgun/agents/config/streaming_test.py +119 -0
  9. shotgun/agents/context_analyzer/analyzer.py +6 -2
  10. shotgun/agents/conversation/__init__.py +18 -0
  11. shotgun/agents/conversation/filters.py +164 -0
  12. shotgun/agents/conversation/history/chunking.py +278 -0
  13. shotgun/agents/{history → conversation/history}/compaction.py +27 -1
  14. shotgun/agents/{history → conversation/history}/constants.py +5 -0
  15. shotgun/agents/conversation/history/file_content_deduplication.py +216 -0
  16. shotgun/agents/{history → conversation/history}/history_processors.py +267 -3
  17. shotgun/agents/{history → conversation/history}/token_counting/anthropic.py +8 -0
  18. shotgun/agents/{conversation_manager.py → conversation/manager.py} +1 -1
  19. shotgun/agents/{conversation_history.py → conversation/models.py} +8 -94
  20. shotgun/agents/error/__init__.py +11 -0
  21. shotgun/agents/error/models.py +19 -0
  22. shotgun/agents/runner.py +230 -0
  23. shotgun/agents/tools/web_search/openai.py +1 -1
  24. shotgun/build_constants.py +2 -2
  25. shotgun/cli/clear.py +1 -1
  26. shotgun/cli/compact.py +5 -3
  27. shotgun/cli/context.py +44 -1
  28. shotgun/cli/error_handler.py +24 -0
  29. shotgun/cli/export.py +34 -34
  30. shotgun/cli/plan.py +34 -34
  31. shotgun/cli/research.py +17 -9
  32. shotgun/cli/spec/__init__.py +5 -0
  33. shotgun/cli/spec/backup.py +81 -0
  34. shotgun/cli/spec/commands.py +132 -0
  35. shotgun/cli/spec/models.py +48 -0
  36. shotgun/cli/spec/pull_service.py +219 -0
  37. shotgun/cli/specify.py +20 -19
  38. shotgun/cli/tasks.py +34 -34
  39. shotgun/codebase/core/ingestor.py +153 -7
  40. shotgun/codebase/models.py +2 -0
  41. shotgun/exceptions.py +325 -0
  42. shotgun/llm_proxy/__init__.py +17 -0
  43. shotgun/llm_proxy/client.py +215 -0
  44. shotgun/llm_proxy/models.py +137 -0
  45. shotgun/logging_config.py +42 -0
  46. shotgun/main.py +4 -0
  47. shotgun/posthog_telemetry.py +1 -1
  48. shotgun/prompts/agents/partials/common_agent_system_prompt.j2 +28 -3
  49. shotgun/prompts/agents/partials/interactive_mode.j2 +3 -3
  50. shotgun/prompts/agents/plan.j2 +16 -0
  51. shotgun/prompts/agents/research.j2 +16 -3
  52. shotgun/prompts/agents/specify.j2 +54 -1
  53. shotgun/prompts/agents/state/system_state.j2 +0 -2
  54. shotgun/prompts/agents/tasks.j2 +16 -0
  55. shotgun/prompts/history/chunk_summarization.j2 +34 -0
  56. shotgun/prompts/history/combine_summaries.j2 +53 -0
  57. shotgun/sdk/codebase.py +14 -3
  58. shotgun/settings.py +5 -0
  59. shotgun/shotgun_web/__init__.py +67 -1
  60. shotgun/shotgun_web/client.py +42 -1
  61. shotgun/shotgun_web/constants.py +46 -0
  62. shotgun/shotgun_web/exceptions.py +29 -0
  63. shotgun/shotgun_web/models.py +390 -0
  64. shotgun/shotgun_web/shared_specs/__init__.py +32 -0
  65. shotgun/shotgun_web/shared_specs/file_scanner.py +175 -0
  66. shotgun/shotgun_web/shared_specs/hasher.py +83 -0
  67. shotgun/shotgun_web/shared_specs/models.py +71 -0
  68. shotgun/shotgun_web/shared_specs/upload_pipeline.py +329 -0
  69. shotgun/shotgun_web/shared_specs/utils.py +34 -0
  70. shotgun/shotgun_web/specs_client.py +703 -0
  71. shotgun/shotgun_web/supabase_client.py +31 -0
  72. shotgun/tui/app.py +73 -9
  73. shotgun/tui/containers.py +1 -1
  74. shotgun/tui/layout.py +5 -0
  75. shotgun/tui/screens/chat/chat_screen.py +372 -95
  76. shotgun/tui/screens/chat/codebase_index_prompt_screen.py +196 -17
  77. shotgun/tui/screens/chat_screen/command_providers.py +13 -2
  78. shotgun/tui/screens/chat_screen/hint_message.py +76 -1
  79. shotgun/tui/screens/confirmation_dialog.py +40 -0
  80. shotgun/tui/screens/directory_setup.py +45 -41
  81. shotgun/tui/screens/feedback.py +10 -3
  82. shotgun/tui/screens/github_issue.py +11 -2
  83. shotgun/tui/screens/model_picker.py +28 -8
  84. shotgun/tui/screens/onboarding.py +149 -0
  85. shotgun/tui/screens/pipx_migration.py +58 -6
  86. shotgun/tui/screens/provider_config.py +66 -8
  87. shotgun/tui/screens/shared_specs/__init__.py +21 -0
  88. shotgun/tui/screens/shared_specs/create_spec_dialog.py +273 -0
  89. shotgun/tui/screens/shared_specs/models.py +56 -0
  90. shotgun/tui/screens/shared_specs/share_specs_dialog.py +390 -0
  91. shotgun/tui/screens/shared_specs/upload_progress_screen.py +452 -0
  92. shotgun/tui/screens/shotgun_auth.py +110 -16
  93. shotgun/tui/screens/spec_pull.py +288 -0
  94. shotgun/tui/screens/welcome.py +123 -0
  95. shotgun/tui/services/conversation_service.py +5 -2
  96. shotgun/tui/widgets/widget_coordinator.py +1 -1
  97. {shotgun_sh-0.2.17.dist-info → shotgun_sh-0.3.3.dev1.dist-info}/METADATA +9 -2
  98. {shotgun_sh-0.2.17.dist-info → shotgun_sh-0.3.3.dev1.dist-info}/RECORD +112 -77
  99. {shotgun_sh-0.2.17.dist-info → shotgun_sh-0.3.3.dev1.dist-info}/WHEEL +1 -1
  100. /shotgun/agents/{history → conversation/history}/__init__.py +0 -0
  101. /shotgun/agents/{history → conversation/history}/context_extraction.py +0 -0
  102. /shotgun/agents/{history → conversation/history}/history_building.py +0 -0
  103. /shotgun/agents/{history → conversation/history}/message_utils.py +0 -0
  104. /shotgun/agents/{history → conversation/history}/token_counting/__init__.py +0 -0
  105. /shotgun/agents/{history → conversation/history}/token_counting/base.py +0 -0
  106. /shotgun/agents/{history → conversation/history}/token_counting/openai.py +0 -0
  107. /shotgun/agents/{history → conversation/history}/token_counting/sentencepiece_counter.py +0 -0
  108. /shotgun/agents/{history → conversation/history}/token_counting/tokenizer_cache.py +0 -0
  109. /shotgun/agents/{history → conversation/history}/token_counting/utils.py +0 -0
  110. /shotgun/agents/{history → conversation/history}/token_estimation.py +0 -0
  111. {shotgun_sh-0.2.17.dist-info → shotgun_sh-0.3.3.dev1.dist-info}/entry_points.txt +0 -0
  112. {shotgun_sh-0.2.17.dist-info → shotgun_sh-0.3.3.dev1.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,215 @@
1
+ """HTTP client for LiteLLM Proxy API."""
2
+
3
+ import logging
4
+ from typing import Any
5
+
6
+ import httpx
7
+ from tenacity import (
8
+ before_sleep_log,
9
+ retry,
10
+ retry_if_exception,
11
+ stop_after_attempt,
12
+ wait_exponential_jitter,
13
+ )
14
+
15
+ from shotgun.api_endpoints import LITELLM_PROXY_BASE_URL
16
+ from shotgun.logging_config import get_logger
17
+
18
+ from .models import BudgetInfo, KeyInfoResponse, TeamInfoResponse
19
+
20
+ logger = get_logger(__name__)
21
+
22
+
23
+ def _is_retryable_http_error(exception: BaseException) -> bool:
24
+ """Check if HTTP exception should trigger a retry.
25
+
26
+ Args:
27
+ exception: The exception to check
28
+
29
+ Returns:
30
+ True if the exception is a transient error that should be retried
31
+ """
32
+ # Retry on network errors and timeouts
33
+ if isinstance(exception, (httpx.RequestError, httpx.TimeoutException)):
34
+ return True
35
+
36
+ # Retry on server errors (5xx) and rate limits (429)
37
+ if isinstance(exception, httpx.HTTPStatusError):
38
+ status_code = exception.response.status_code
39
+ return status_code >= 500 or status_code == 429
40
+
41
+ # Don't retry on other errors (e.g., 4xx client errors)
42
+ return False
43
+
44
+
45
+ class LiteLLMProxyClient:
46
+ """HTTP client for LiteLLM Proxy API.
47
+
48
+ Provides methods to query budget information and key/team metadata
49
+ from a LiteLLM proxy server.
50
+ """
51
+
52
+ def __init__(
53
+ self,
54
+ api_key: str,
55
+ base_url: str | None = None,
56
+ timeout: float = 10.0,
57
+ ):
58
+ """Initialize LiteLLM Proxy client.
59
+
60
+ Args:
61
+ api_key: LiteLLM API key for authentication
62
+ base_url: Base URL for LiteLLM proxy. If None, uses LITELLM_PROXY_BASE_URL
63
+ timeout: Request timeout in seconds
64
+ """
65
+ self.api_key = api_key
66
+ self.base_url = base_url or LITELLM_PROXY_BASE_URL
67
+ self.timeout = timeout
68
+
69
+ @retry(
70
+ stop=stop_after_attempt(3),
71
+ wait=wait_exponential_jitter(initial=1, max=8),
72
+ retry=retry_if_exception(_is_retryable_http_error),
73
+ before_sleep=before_sleep_log(logger, logging.WARNING),
74
+ reraise=True,
75
+ )
76
+ async def _request_with_retry(
77
+ self,
78
+ method: str,
79
+ url: str,
80
+ **kwargs: Any,
81
+ ) -> httpx.Response:
82
+ """Make async HTTP request with exponential backoff retry and jitter.
83
+
84
+ Uses tenacity to retry on transient errors (5xx, 429, network errors)
85
+ with exponential backoff and jitter. Client errors (4xx except 429)
86
+ are not retried.
87
+
88
+ Args:
89
+ method: HTTP method (GET, POST, etc.)
90
+ url: Request URL
91
+ **kwargs: Additional arguments to pass to httpx request
92
+
93
+ Returns:
94
+ HTTP response
95
+
96
+ Raises:
97
+ httpx.HTTPError: If request fails after all retries
98
+ """
99
+ async with httpx.AsyncClient(timeout=self.timeout) as client:
100
+ response = await client.request(method, url, **kwargs)
101
+ response.raise_for_status()
102
+ return response
103
+
104
+ async def get_key_info(self) -> KeyInfoResponse:
105
+ """Get key information from LiteLLM proxy.
106
+
107
+ Returns:
108
+ Key information including spend, budget, and team_id
109
+
110
+ Raises:
111
+ httpx.HTTPError: If request fails
112
+ """
113
+ url = f"{self.base_url}/key/info"
114
+ params = {"key": self.api_key}
115
+ headers = {"Authorization": f"Bearer {self.api_key}"}
116
+
117
+ logger.debug("Fetching key info from %s", url)
118
+
119
+ response = await self._request_with_retry(
120
+ "GET", url, params=params, headers=headers
121
+ )
122
+
123
+ data = response.json()
124
+ result = KeyInfoResponse.model_validate(data)
125
+
126
+ logger.info(
127
+ "Successfully fetched key info: key_alias=%s, team_id=%s",
128
+ result.info.key_alias,
129
+ result.info.team_id,
130
+ )
131
+ return result
132
+
133
+ async def get_team_info(self, team_id: str) -> TeamInfoResponse:
134
+ """Get team information from LiteLLM proxy.
135
+
136
+ Args:
137
+ team_id: Team identifier
138
+
139
+ Returns:
140
+ Team information including spend and budget
141
+
142
+ Raises:
143
+ httpx.HTTPError: If request fails
144
+ """
145
+ url = f"{self.base_url}/team/info"
146
+ params = {"team_id": team_id}
147
+ headers = {"Authorization": f"Bearer {self.api_key}"}
148
+
149
+ logger.debug("Fetching team info from %s for team_id=%s", url, team_id)
150
+
151
+ response = await self._request_with_retry(
152
+ "GET", url, params=params, headers=headers
153
+ )
154
+
155
+ data = response.json()
156
+ result = TeamInfoResponse.model_validate(data)
157
+
158
+ logger.info(
159
+ "Successfully fetched team info: team_alias=%s",
160
+ result.team_info.team_alias,
161
+ )
162
+ return result
163
+
164
+ async def get_budget_info(self) -> BudgetInfo:
165
+ """Get team-level budget information for this key.
166
+
167
+ Budget is always configured at the team level, never at the key level.
168
+ This method fetches the team_id from the key info, then retrieves
169
+ the team's budget information.
170
+
171
+ Returns:
172
+ Team-level budget information
173
+
174
+ Raises:
175
+ httpx.HTTPError: If request fails
176
+ ValueError: If team has no budget configured
177
+ """
178
+ logger.debug("Fetching budget info")
179
+
180
+ # Get key info to retrieve team_id
181
+ key_response = await self.get_key_info()
182
+ key_info = key_response.info
183
+
184
+ # Fetch team budget (budget is always at team level)
185
+ logger.debug(
186
+ "Fetching team budget for team_id=%s",
187
+ key_info.team_id,
188
+ )
189
+ team_response = await self.get_team_info(key_info.team_id)
190
+ team_info = team_response.team_info
191
+
192
+ if team_info.max_budget is None:
193
+ raise ValueError(
194
+ f"Team (team_id={key_info.team_id}) has no max_budget configured"
195
+ )
196
+
197
+ logger.debug("Using team-level budget: $%.6f", team_info.max_budget)
198
+ return BudgetInfo.from_team_info(team_info)
199
+
200
+
201
+ # Convenience function for standalone use
202
+ async def get_budget_info(api_key: str, base_url: str | None = None) -> BudgetInfo:
203
+ """Get budget information for an API key.
204
+
205
+ Convenience function that creates a client and calls get_budget_info.
206
+
207
+ Args:
208
+ api_key: LiteLLM API key
209
+ base_url: Optional base URL for LiteLLM proxy
210
+
211
+ Returns:
212
+ Budget information
213
+ """
214
+ client = LiteLLMProxyClient(api_key, base_url=base_url)
215
+ return await client.get_budget_info()
@@ -0,0 +1,137 @@
1
+ """Pydantic models for LiteLLM Proxy API."""
2
+
3
+ from enum import StrEnum
4
+
5
+ from pydantic import BaseModel, Field
6
+
7
+
8
+ class BudgetSource(StrEnum):
9
+ """Source of budget information."""
10
+
11
+ KEY = "key"
12
+ TEAM = "team"
13
+
14
+
15
+ class KeyInfoData(BaseModel):
16
+ """Key information data from /key/info endpoint."""
17
+
18
+ key_name: str = Field(description="Key name/identifier")
19
+ key_alias: str | None = Field(default=None, description="Human-readable key alias")
20
+ spend: float = Field(description="Current spend for this key in USD")
21
+ max_budget: float | None = Field(
22
+ default=None, description="Maximum budget for this key in USD"
23
+ )
24
+ team_id: str = Field(description="Team ID associated with this key")
25
+ user_id: str = Field(description="User ID associated with this key")
26
+ models: list[str] = Field(
27
+ default_factory=list, description="List of models available to this key"
28
+ )
29
+
30
+
31
+ class KeyInfoResponse(BaseModel):
32
+ """Response from /key/info endpoint."""
33
+
34
+ key: str = Field(description="The API key")
35
+ info: KeyInfoData = Field(description="Key information data")
36
+
37
+
38
+ class TeamInfoData(BaseModel):
39
+ """Team information data from /team/info endpoint."""
40
+
41
+ team_id: str = Field(description="Team identifier")
42
+ team_alias: str | None = Field(
43
+ default=None, description="Human-readable team alias"
44
+ )
45
+ max_budget: float | None = Field(
46
+ default=None, description="Maximum budget for this team in USD"
47
+ )
48
+ spend: float = Field(description="Current spend for this team in USD")
49
+ models: list[str] = Field(
50
+ default_factory=list, description="List of models available to this team"
51
+ )
52
+
53
+
54
+ class TeamInfoResponse(BaseModel):
55
+ """Response from /team/info endpoint."""
56
+
57
+ team_id: str = Field(description="Team identifier")
58
+ team_info: TeamInfoData = Field(description="Team information data")
59
+
60
+
61
+ class BudgetInfo(BaseModel):
62
+ """Unified budget information.
63
+
64
+ Combines key and team budget information to provide a single view
65
+ of budget status. Budget can come from either key-level or team-level,
66
+ with key-level taking priority if set.
67
+ """
68
+
69
+ max_budget: float = Field(description="Maximum budget in USD")
70
+ spend: float = Field(description="Current spend in USD")
71
+ remaining: float = Field(description="Remaining budget in USD")
72
+ source: BudgetSource = Field(
73
+ description="Source of budget information (key or team)"
74
+ )
75
+ percentage_used: float = Field(description="Percentage of budget used (0-100)")
76
+
77
+ @classmethod
78
+ def from_key_info(cls, key_info: KeyInfoData) -> "BudgetInfo":
79
+ """Create BudgetInfo from key-level budget.
80
+
81
+ Args:
82
+ key_info: Key information containing budget data
83
+
84
+ Returns:
85
+ BudgetInfo instance with key-level budget
86
+
87
+ Raises:
88
+ ValueError: If key does not have max_budget set
89
+ """
90
+ if key_info.max_budget is None:
91
+ raise ValueError("Key does not have max_budget set")
92
+
93
+ remaining = key_info.max_budget - key_info.spend
94
+ percentage_used = (
95
+ (key_info.spend / key_info.max_budget * 100)
96
+ if key_info.max_budget > 0
97
+ else 0.0
98
+ )
99
+
100
+ return cls(
101
+ max_budget=key_info.max_budget,
102
+ spend=key_info.spend,
103
+ remaining=remaining,
104
+ source=BudgetSource.KEY,
105
+ percentage_used=percentage_used,
106
+ )
107
+
108
+ @classmethod
109
+ def from_team_info(cls, team_info: TeamInfoData) -> "BudgetInfo":
110
+ """Create BudgetInfo from team-level budget.
111
+
112
+ Args:
113
+ team_info: Team information containing budget data
114
+
115
+ Returns:
116
+ BudgetInfo instance with team-level budget
117
+
118
+ Raises:
119
+ ValueError: If team does not have max_budget set
120
+ """
121
+ if team_info.max_budget is None:
122
+ raise ValueError("Team does not have max_budget set")
123
+
124
+ remaining = team_info.max_budget - team_info.spend
125
+ percentage_used = (
126
+ (team_info.spend / team_info.max_budget * 100)
127
+ if team_info.max_budget > 0
128
+ else 0.0
129
+ )
130
+
131
+ return cls(
132
+ max_budget=team_info.max_budget,
133
+ spend=team_info.spend,
134
+ remaining=remaining,
135
+ source=BudgetSource.TEAM,
136
+ percentage_used=percentage_used,
137
+ )
shotgun/logging_config.py CHANGED
@@ -27,6 +27,44 @@ def get_log_directory() -> Path:
27
27
  return log_dir
28
28
 
29
29
 
30
+ def cleanup_old_log_files(log_dir: Path, max_files: int) -> None:
31
+ """Remove old log files, keeping only the most recent ones.
32
+
33
+ Also removes the legacy shotgun.log file if it exists.
34
+
35
+ Args:
36
+ log_dir: Directory containing log files
37
+ max_files: Maximum number of log files to keep
38
+ """
39
+ try:
40
+ # Remove legacy non-timestamped log file if it exists
41
+ legacy_log = log_dir / "shotgun.log"
42
+ if legacy_log.exists():
43
+ try:
44
+ legacy_log.unlink()
45
+ except OSError:
46
+ pass # noqa: S110
47
+
48
+ # Find all shotgun log files
49
+ log_files = sorted(
50
+ log_dir.glob("shotgun-*.log"),
51
+ key=lambda p: p.stat().st_mtime,
52
+ reverse=True, # Newest first
53
+ )
54
+
55
+ # Remove files beyond the limit
56
+ files_to_delete = log_files[max_files:]
57
+ for log_file in files_to_delete:
58
+ try:
59
+ log_file.unlink()
60
+ except OSError:
61
+ # Ignore errors when deleting individual files
62
+ pass # noqa: S110
63
+ except Exception: # noqa: S110
64
+ # Silently fail - log cleanup shouldn't break the application
65
+ pass
66
+
67
+
30
68
  class ColoredFormatter(logging.Formatter):
31
69
  """Custom formatter with colors for different log levels."""
32
70
 
@@ -123,6 +161,10 @@ def setup_logger(
123
161
  try:
124
162
  # Create file handler with ISO8601 timestamp for each run
125
163
  log_dir = get_log_directory()
164
+
165
+ # Clean up old log files before creating a new one
166
+ cleanup_old_log_files(log_dir, settings.logging.max_log_files)
167
+
126
168
  log_file = log_dir / f"shotgun-{_RUN_TIMESTAMP}.log"
127
169
 
128
170
  # Use regular FileHandler - each run gets its own isolated log file
shotgun/main.py CHANGED
@@ -32,6 +32,7 @@ from shotgun.cli import (
32
32
  feedback,
33
33
  plan,
34
34
  research,
35
+ spec,
35
36
  specify,
36
37
  tasks,
37
38
  update,
@@ -55,6 +56,8 @@ logger = get_logger(__name__)
55
56
  logger.debug("Logfire observability enabled: %s", _logfire_enabled)
56
57
 
57
58
  # Initialize configuration
59
+ # Note: If config migration fails, ConfigManager will auto-create fresh config
60
+ # and set migration_failed flag for user notification
58
61
  try:
59
62
  import asyncio
60
63
 
@@ -93,6 +96,7 @@ app.add_typer(tasks.app, name="tasks", help="Generate task lists with agentic ap
93
96
  app.add_typer(export.app, name="export", help="Export artifacts to various formats")
94
97
  app.add_typer(update.app, name="update", help="Check for and install updates")
95
98
  app.add_typer(feedback.app, name="feedback", help="Send us feedback")
99
+ app.add_typer(spec.app, name="spec", help="Manage shared specifications")
96
100
 
97
101
 
98
102
  def version_callback(value: bool) -> None:
@@ -8,7 +8,7 @@ from pydantic import BaseModel
8
8
 
9
9
  from shotgun import __version__
10
10
  from shotgun.agents.config import get_config_manager
11
- from shotgun.agents.conversation_manager import ConversationManager
11
+ from shotgun.agents.conversation import ConversationManager
12
12
  from shotgun.logging_config import get_early_logger
13
13
  from shotgun.settings import settings
14
14
 
@@ -4,14 +4,39 @@ Your extensive expertise spans, among other things:
4
4
  * Software Architecture
5
5
  * Software Development
6
6
 
7
+ ## YOUR ROLE IN THE PIPELINE
8
+
9
+ **CRITICAL**: You are a DOCUMENTATION and PLANNING agent, NOT a coding/implementation agent.
10
+
11
+ - You produce DOCUMENTS (research, specifications, plans, tasks) that AI coding agents will consume
12
+ - You do NOT write production code, implement features, or make code changes
13
+ - NEVER offer to "move forward with implementation" or "start coding" - that's not your job
14
+ - NEVER ask "would you like me to implement this?" - implementation is done by separate AI coding tools
15
+ - Your deliverable is always a document file (.md), not code execution
16
+ - When your work is complete, the user will take your documents to a coding agent (Claude Code, Cursor, etc.)
17
+
18
+ ## AGENT FILE PERMISSIONS
19
+
20
+ There are four agents in the pipeline, and each agent can ONLY write to specific files. The user can switch between agents using **Shift+Tab**.
21
+
22
+ The **Research agent** can only write to `research.md`. If the user asks about specifications, plans, or tasks, tell them: "Use **Shift+Tab** to switch to the [appropriate] agent which can edit that file for you."
23
+
24
+ The **Specification agent** can only write to `specification.md` and files inside the `.shotgun/contracts/` directory. If the user asks about research, plans, or tasks, tell them which agent handles that file.
25
+
26
+ The **Plan agent** can only write to `plan.md`. If the user asks about research, specifications, or tasks, tell them which agent handles that file.
27
+
28
+ The **Tasks agent** can only write to `tasks.md`. If the user asks about research, specifications, or plans, tell them which agent handles that file.
29
+
30
+ When a user asks you to edit a file you cannot write to, you MUST tell them which agent can help and how to switch: "I can't edit [filename] - that's handled by the [agent name] agent. Use **Shift+Tab** to switch to that agent and it can edit that file for you."
31
+
7
32
  ## KEY RULES
8
33
 
9
34
  {% if interactive_mode %}
10
- 0. Always ask CLARIFYING QUESTIONS using structured output before doing work.
35
+ 0. Ask CLARIFYING QUESTIONS using structured output for complex or multi-step tasks when the request lacks sufficient detail.
11
36
  - Return your response with the clarifying_questions field populated
12
- - Do not make assumptions about what the user wants, get a clear understanding first.
37
+ - For simple, straightforward requests, make reasonable assumptions and proceed.
38
+ - Only ask the most critical questions to avoid overwhelming the user.
13
39
  - Questions should be clear, specific, and answerable
14
- - Do not ask too many questions that might overwhelm the user; prioritize the most important ones.
15
40
  {% endif %}
16
41
  1. Above all, prefer using tools to do the work and NEVER respond with text.
17
42
  2. IMPORTANT: Always ask for review and go ahead to move forward after using write_file().
@@ -19,10 +19,10 @@ You must return responses using this structured format:
19
19
 
20
20
  ## When to Use Clarifying Questions
21
21
 
22
- - BEFORE GETTING TO WORK: If the user's request is ambiguous, use clarifying_questions to ask what they want
22
+ - BEFORE GETTING TO WORK: For complex or multi-step tasks where the request is ambiguous or lacks sufficient detail, use clarifying_questions to ask what they want
23
23
  - DURING WORK: After using write_file(), you can suggest that the user review it and ask any clarifying questions with clarifying_questions
24
- - Don't assume - ask for confirmation of your understanding
25
- - When in doubt about any aspect of the goal, include clarifying_questions
24
+ - For simple, straightforward requests, make reasonable assumptions and proceed
25
+ - Only ask critical questions that significantly impact the outcome
26
26
 
27
27
  ## Important Notes
28
28
 
@@ -6,6 +6,22 @@ Your job is to help create comprehensive, actionable plans for software projects
6
6
 
7
7
  {% include 'agents/partials/common_agent_system_prompt.j2' %}
8
8
 
9
+ ## YOUR SCOPE AND HANDOFFS
10
+
11
+ You are the **Plan agent**. Your file is `plan.md` - this is the ONLY file you can write to.
12
+
13
+ When your plan is complete, suggest the next step:
14
+ "I've completed the plan. Use **Shift+Tab** to switch to the tasks agent to break this plan into actionable tasks."
15
+
16
+ If the user asks you to edit other files, redirect them helpfully:
17
+ - For research.md: "I can't edit research.md - that's handled by the research agent. Use **Shift+Tab** to switch to that agent and it can edit that file for you."
18
+ - For specification.md or contracts: "I can't edit specification.md - that's handled by the specification agent. Use **Shift+Tab** to switch to that agent and it can edit that file for you."
19
+ - For tasks.md: "I can't edit tasks.md - that's handled by the tasks agent. Use **Shift+Tab** to switch to that agent and it can edit that file for you."
20
+
21
+ NEVER offer to do work outside your scope:
22
+ - Don't offer to write research, specifications, or tasks - redirect the user to the appropriate agent
23
+ - Don't offer to implement code - you are not a coding agent
24
+
9
25
  ## MEMORY MANAGEMENT PROTOCOL
10
26
 
11
27
  - You have exclusive write access to: `plan.md`
@@ -4,6 +4,22 @@ Your job is to help the user research various subjects related to their software
4
4
 
5
5
  {% include 'agents/partials/common_agent_system_prompt.j2' %}
6
6
 
7
+ ## YOUR SCOPE AND HANDOFFS
8
+
9
+ You are the **Research agent**. Your file is `research.md` - this is the ONLY file you can write to.
10
+
11
+ When your research is complete, suggest the next step:
12
+ "I've completed the research and updated research.md. Use **Shift+Tab** to switch to the specification agent to create the specification based on this research."
13
+
14
+ If the user asks you to edit other files, redirect them helpfully:
15
+ - For specification.md or contracts: "I can't edit specification.md - that's handled by the specification agent. Use **Shift+Tab** to switch to that agent and it can edit that file for you."
16
+ - For plan.md: "I can't edit plan.md - that's handled by the plan agent. Use **Shift+Tab** to switch to that agent and it can edit that file for you."
17
+ - For tasks.md: "I can't edit tasks.md - that's handled by the tasks agent. Use **Shift+Tab** to switch to that agent and it can edit that file for you."
18
+
19
+ NEVER offer to do work outside your scope:
20
+ - Don't offer to write specifications, plans, or tasks - redirect the user to the appropriate agent
21
+ - Don't offer to implement code - you are not a coding agent
22
+
7
23
  ## MEMORY MANAGEMENT PROTOCOL
8
24
 
9
25
  - You have exclusive write access to: `research.md`
@@ -38,9 +54,6 @@ For research tasks:
38
54
 
39
55
  ## RESEARCH PRINCIPLES
40
56
 
41
- {% if interactive_mode -%}
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
- {% endif -%}
44
57
  - Build upon existing research rather than starting from scratch
45
58
  - Focus on practical, actionable information over theoretical concepts
46
59
  - Include specific examples, tools, and implementation details
@@ -6,6 +6,22 @@ Transform requirements into detailed, actionable specifications that development
6
6
 
7
7
  {% include 'agents/partials/common_agent_system_prompt.j2' %}
8
8
 
9
+ ## YOUR SCOPE AND HANDOFFS
10
+
11
+ You are the **Specification agent**. Your files are `specification.md` and `.shotgun/contracts/*` - these are the ONLY files you can write to.
12
+
13
+ When your specification is complete, suggest the next step:
14
+ "I've completed the specification. Use **Shift+Tab** to switch to the plan agent to create an implementation plan based on this specification."
15
+
16
+ If the user asks you to edit other files, redirect them helpfully:
17
+ - For research.md: "I can't edit research.md - that's handled by the research agent. Use **Shift+Tab** to switch to that agent and it can edit that file for you."
18
+ - For plan.md: "I can't edit plan.md - that's handled by the plan agent. Use **Shift+Tab** to switch to that agent and it can edit that file for you."
19
+ - For tasks.md: "I can't edit tasks.md - that's handled by the tasks agent. Use **Shift+Tab** to switch to that agent and it can edit that file for you."
20
+
21
+ NEVER offer to do work outside your scope:
22
+ - Don't offer to write research, plans, or tasks - redirect the user to the appropriate agent
23
+ - Don't offer to implement code - you are not a coding agent
24
+
9
25
  ## MEMORY MANAGEMENT PROTOCOL
10
26
 
11
27
  - You have exclusive write access to: `specification.md` and `.shotgun/contracts/*`
@@ -24,6 +40,7 @@ Transform requirements into detailed, actionable specifications that development
24
40
  specification.md is your prose documentation file. It should contain:
25
41
 
26
42
  **INCLUDE in specification.md:**
43
+ - TLDR section at the very top (key points, major features, key concerns if any)
27
44
  - Requirements and business context (what needs to be built and why)
28
45
  - Architecture overview and system design decisions
29
46
  - Component descriptions and how they interact
@@ -43,6 +60,41 @@ specification.md is your prose documentation file. It should contain:
43
60
  **When you need to show structure:** Reference contract files instead of inline code.
44
61
  Example: "User authentication uses OAuth2. See contracts/auth_types.ts for AuthUser and AuthToken types."
45
62
 
63
+ ## TLDR SECTION (REQUIRED)
64
+
65
+ Every specification.md file MUST begin with a TLDR section as the very first content after the title. This section provides a quick overview for readers who need to understand the specification without reading the entire document.
66
+
67
+ **TLDR Section Format:**
68
+
69
+ ```markdown
70
+ # Specification: [Project/Feature Name]
71
+
72
+ ## TLDR
73
+
74
+ **Key Points:**
75
+ - [Brief description of what is being built - 1-2 sentences]
76
+ - [Primary purpose/goal]
77
+
78
+ **Major Features:**
79
+ - [Feature 1 - one line]
80
+ - [Feature 2 - one line]
81
+ - [Feature 3 - one line]
82
+ - ...
83
+
84
+ **Key Concerns:** (only if applicable)
85
+ - [Concern 1 - keep brief, elaborate in relevant sections below]
86
+ - [Concern 2]
87
+ ```
88
+
89
+ **TLDR Guidelines:**
90
+ - Keep the entire TLDR section to 10-15 lines maximum
91
+ - Use bullet points for scannability
92
+ - The "Key Points" should capture the essence in 2-3 bullets
93
+ - "Major Features" lists the main capabilities (not exhaustive, just highlights)
94
+ - **"Key Concerns" is optional** - only include this subsection if there are significant risks, constraints, or decisions that readers should be aware of upfront. Omit it entirely if there are no concerns.
95
+ - Elaborate on concerns in the appropriate sections below, not in the TLDR
96
+ - The TLDR should be self-contained - someone reading only this section should understand what the project is about
97
+
46
98
  ## CONTRACT FILES
47
99
 
48
100
  Contract files define the **interfaces and types** that form contracts between components.
@@ -303,7 +355,8 @@ For specification tasks:
303
355
  2. **Check research**: Read `research.md` if it exists to understand technical context and findings
304
356
  3. **Analyze requirements**: Understand the functional and non-functional requirements
305
357
  4. **Define specifications**: Create detailed technical and functional specifications
306
- 5. **Structure documentation**: Use `write_file("specification.md", content)` to save comprehensive specifications
358
+ 5. **Write TLDR section**: Start specification.md with a TLDR section summarizing key points, major features, and any key concerns
359
+ 6. **Structure documentation**: Use `write_file("specification.md", content)` to save comprehensive specifications
307
360
 
308
361
  ## SPECIFICATION PRINCIPLES
309
362
 
@@ -22,8 +22,6 @@ No files currently exist in your allowed directories. You can create:
22
22
  - `exports/` folder - For exported documents
23
23
  {% endif %}
24
24
 
25
- When updating a file try to add into the footer that this was created using Shotgun (https://shotgun.sh).
26
-
27
25
  {% if markdown_toc %}
28
26
  ## Document Table of Contents - READ THE ENTIRE FILE TO UNDERSTAND MORE
29
27
 
@@ -4,6 +4,22 @@ 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
+ ## YOUR SCOPE AND HANDOFFS
8
+
9
+ You are the **Tasks agent**. Your file is `tasks.md` - this is the ONLY file you can write to.
10
+
11
+ When your tasks are complete, suggest the next step:
12
+ "I've created the task list in tasks.md. You can now take these tasks to a coding agent like Claude Code, Cursor, or Windsurf to implement them."
13
+
14
+ If the user asks you to edit other files, redirect them helpfully:
15
+ - For research.md: "I can't edit research.md - that's handled by the research agent. Use **Shift+Tab** to switch to that agent and it can edit that file for you."
16
+ - For specification.md or contracts: "I can't edit specification.md - that's handled by the specification agent. Use **Shift+Tab** to switch to that agent and it can edit that file for you."
17
+ - For plan.md: "I can't edit plan.md - that's handled by the plan agent. Use **Shift+Tab** to switch to that agent and it can edit that file for you."
18
+
19
+ NEVER offer to do work outside your scope:
20
+ - Don't offer to write research, specifications, or plans - redirect the user to the appropriate agent
21
+ - Don't offer to implement code - you are not a coding agent
22
+
7
23
  ## MEMORY MANAGEMENT PROTOCOL
8
24
 
9
25
  - You have exclusive write access to: `tasks.md`