shotgun-sh 0.3.3.dev1__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.
Files changed (58) hide show
  1. shotgun/agents/agent_manager.py +191 -23
  2. shotgun/agents/common.py +78 -77
  3. shotgun/agents/config/manager.py +42 -1
  4. shotgun/agents/config/models.py +16 -0
  5. shotgun/agents/conversation/history/file_content_deduplication.py +66 -43
  6. shotgun/agents/export.py +12 -13
  7. shotgun/agents/models.py +66 -1
  8. shotgun/agents/plan.py +12 -13
  9. shotgun/agents/research.py +13 -10
  10. shotgun/agents/router/__init__.py +47 -0
  11. shotgun/agents/router/models.py +376 -0
  12. shotgun/agents/router/router.py +185 -0
  13. shotgun/agents/router/tools/__init__.py +18 -0
  14. shotgun/agents/router/tools/delegation_tools.py +503 -0
  15. shotgun/agents/router/tools/plan_tools.py +322 -0
  16. shotgun/agents/specify.py +12 -13
  17. shotgun/agents/tasks.py +12 -13
  18. shotgun/agents/tools/file_management.py +49 -1
  19. shotgun/agents/tools/registry.py +2 -0
  20. shotgun/agents/tools/web_search/__init__.py +1 -2
  21. shotgun/agents/tools/web_search/gemini.py +1 -3
  22. shotgun/codebase/core/change_detector.py +1 -1
  23. shotgun/codebase/core/ingestor.py +1 -1
  24. shotgun/codebase/core/manager.py +1 -1
  25. shotgun/prompts/agents/export.j2 +2 -0
  26. shotgun/prompts/agents/partials/common_agent_system_prompt.j2 +5 -10
  27. shotgun/prompts/agents/partials/router_delegation_mode.j2 +36 -0
  28. shotgun/prompts/agents/plan.j2 +24 -12
  29. shotgun/prompts/agents/research.j2 +70 -31
  30. shotgun/prompts/agents/router.j2 +440 -0
  31. shotgun/prompts/agents/specify.j2 +39 -16
  32. shotgun/prompts/agents/state/system_state.j2 +15 -6
  33. shotgun/prompts/agents/tasks.j2 +58 -34
  34. shotgun/tui/app.py +5 -6
  35. shotgun/tui/components/mode_indicator.py +120 -25
  36. shotgun/tui/components/status_bar.py +2 -2
  37. shotgun/tui/dependencies.py +64 -9
  38. shotgun/tui/protocols.py +37 -0
  39. shotgun/tui/screens/chat/chat.tcss +9 -1
  40. shotgun/tui/screens/chat/chat_screen.py +643 -11
  41. shotgun/tui/screens/chat_screen/command_providers.py +0 -87
  42. shotgun/tui/screens/chat_screen/history/agent_response.py +7 -3
  43. shotgun/tui/screens/chat_screen/history/chat_history.py +12 -0
  44. shotgun/tui/screens/chat_screen/history/formatters.py +53 -15
  45. shotgun/tui/screens/chat_screen/history/partial_response.py +11 -1
  46. shotgun/tui/screens/chat_screen/messages.py +219 -0
  47. shotgun/tui/screens/onboarding.py +30 -26
  48. shotgun/tui/utils/mode_progress.py +20 -86
  49. shotgun/tui/widgets/__init__.py +2 -1
  50. shotgun/tui/widgets/approval_widget.py +152 -0
  51. shotgun/tui/widgets/cascade_confirmation_widget.py +203 -0
  52. shotgun/tui/widgets/plan_panel.py +129 -0
  53. shotgun/tui/widgets/step_checkpoint_widget.py +180 -0
  54. {shotgun_sh-0.3.3.dev1.dist-info → shotgun_sh-0.4.0.dev1.dist-info}/METADATA +3 -3
  55. {shotgun_sh-0.3.3.dev1.dist-info → shotgun_sh-0.4.0.dev1.dist-info}/RECORD +58 -45
  56. {shotgun_sh-0.3.3.dev1.dist-info → shotgun_sh-0.4.0.dev1.dist-info}/WHEEL +0 -0
  57. {shotgun_sh-0.3.3.dev1.dist-info → shotgun_sh-0.4.0.dev1.dist-info}/entry_points.txt +0 -0
  58. {shotgun_sh-0.3.3.dev1.dist-info → shotgun_sh-0.4.0.dev1.dist-info}/licenses/LICENSE +0 -0
@@ -3,7 +3,6 @@ from typing import TYPE_CHECKING, cast
3
3
 
4
4
  from textual.command import DiscoveryHit, Hit, Provider
5
5
 
6
- from shotgun.agents.models import AgentType
7
6
  from shotgun.codebase.models import CodebaseGraph
8
7
  from shotgun.tui.screens.chat_screen.hint_message import HintMessage
9
8
  from shotgun.tui.screens.model_picker import ModelPickerScreen
@@ -13,92 +12,6 @@ if TYPE_CHECKING:
13
12
  from shotgun.tui.screens.chat import ChatScreen
14
13
 
15
14
 
16
- class AgentModeProvider(Provider):
17
- """Command provider for agent mode switching."""
18
-
19
- @property
20
- def chat_screen(self) -> "ChatScreen":
21
- from shotgun.tui.screens.chat import ChatScreen
22
-
23
- return cast(ChatScreen, self.screen)
24
-
25
- def set_mode(self, mode: AgentType) -> None:
26
- """Switch to research mode."""
27
- self.chat_screen.mode = mode
28
-
29
- async def discover(self) -> AsyncGenerator[DiscoveryHit, None]:
30
- """Provide default mode switching commands when palette opens."""
31
- yield DiscoveryHit(
32
- "Switch to Research Mode",
33
- lambda: self.set_mode(AgentType.RESEARCH),
34
- help="🔬 Research topics with web search and synthesize findings",
35
- )
36
- yield DiscoveryHit(
37
- "Switch to Specify Mode",
38
- lambda: self.set_mode(AgentType.SPECIFY),
39
- help="📝 Create detailed specifications and requirements documents",
40
- )
41
- yield DiscoveryHit(
42
- "Switch to Plan Mode",
43
- lambda: self.set_mode(AgentType.PLAN),
44
- help="📋 Create comprehensive, actionable plans with milestones",
45
- )
46
- yield DiscoveryHit(
47
- "Switch to Tasks Mode",
48
- lambda: self.set_mode(AgentType.TASKS),
49
- help="✅ Generate specific, actionable tasks from research and plans",
50
- )
51
- yield DiscoveryHit(
52
- "Switch to Export Mode",
53
- lambda: self.set_mode(AgentType.EXPORT),
54
- help="📤 Export artifacts and findings to various formats",
55
- )
56
-
57
- async def search(self, query: str) -> AsyncGenerator[Hit, None]:
58
- """Search for mode commands."""
59
- matcher = self.matcher(query)
60
-
61
- commands = [
62
- (
63
- "Switch to Research Mode",
64
- "🔬 Research topics with web search and synthesize findings",
65
- lambda: self.set_mode(AgentType.RESEARCH),
66
- AgentType.RESEARCH,
67
- ),
68
- (
69
- "Switch to Specify Mode",
70
- "📝 Create detailed specifications and requirements documents",
71
- lambda: self.set_mode(AgentType.SPECIFY),
72
- AgentType.SPECIFY,
73
- ),
74
- (
75
- "Switch to Plan Mode",
76
- "📋 Create comprehensive, actionable plans with milestones",
77
- lambda: self.set_mode(AgentType.PLAN),
78
- AgentType.PLAN,
79
- ),
80
- (
81
- "Switch to Tasks Mode",
82
- "✅ Generate specific, actionable tasks from research and plans",
83
- lambda: self.set_mode(AgentType.TASKS),
84
- AgentType.TASKS,
85
- ),
86
- (
87
- "Switch to Export Mode",
88
- "📤 Export artifacts and findings to various formats",
89
- lambda: self.set_mode(AgentType.EXPORT),
90
- AgentType.EXPORT,
91
- ),
92
- ]
93
-
94
- for title, help_text, callback, mode in commands:
95
- if self.chat_screen.mode == mode:
96
- continue
97
- score = matcher.match(title)
98
- if score > 0:
99
- yield Hit(score, matcher.highlight(title), callback, help=help_text)
100
-
101
-
102
15
  class UsageProvider(Provider):
103
16
  """Command provider for agent mode switching."""
104
17
 
@@ -18,9 +18,10 @@ from .formatters import ToolFormatter
18
18
  class AgentResponseWidget(Widget):
19
19
  """Widget that displays agent responses in the chat history."""
20
20
 
21
- def __init__(self, item: ModelResponse | None) -> None:
21
+ def __init__(self, item: ModelResponse | None, is_sub_agent: bool = False) -> None:
22
22
  super().__init__()
23
23
  self.item = item
24
+ self.is_sub_agent = is_sub_agent
24
25
 
25
26
  def compose(self) -> ComposeResult:
26
27
  self.display = self.item is not None
@@ -35,11 +36,14 @@ class AgentResponseWidget(Widget):
35
36
  if self.item is None:
36
37
  return ""
37
38
 
39
+ # Use different prefix for sub-agent responses
40
+ prefix = "**⏺** " if not self.is_sub_agent else " **↳** "
41
+
38
42
  for idx, part in enumerate(self.item.parts):
39
43
  if isinstance(part, TextPart):
40
- # Only show the circle prefix if there's actual content
44
+ # Only show the prefix if there's actual content
41
45
  if part.content and part.content.strip():
42
- acc += f"**⏺** {part.content}\n\n"
46
+ acc += f"{prefix}{part.content}\n\n"
43
47
  elif isinstance(part, ToolCallPart):
44
48
  parts_str = ToolFormatter.format_tool_call_part(part)
45
49
  if parts_str: # Only add if there's actual content
@@ -8,10 +8,12 @@ from pydantic_ai.messages import (
8
8
  ModelResponse,
9
9
  UserPromptPart,
10
10
  )
11
+ from textual import events
11
12
  from textual.app import ComposeResult
12
13
  from textual.reactive import reactive
13
14
  from textual.widget import Widget
14
15
 
16
+ from shotgun.tui.components.prompt_input import PromptInput
15
17
  from shotgun.tui.components.vertical_tail import VerticalTail
16
18
  from shotgun.tui.screens.chat_screen.hint_message import HintMessage, HintMessageWidget
17
19
 
@@ -113,3 +115,13 @@ class ChatHistory(Widget):
113
115
 
114
116
  # Scroll to bottom to show newly added messages
115
117
  self.vertical_tail.scroll_end(animate=False)
118
+
119
+ def on_click(self, event: events.Click) -> None:
120
+ """Focus the prompt input when clicking on the history area."""
121
+ # Only handle clicks that weren't already handled by a child widget
122
+ if event.button == 1: # Left click
123
+ results = self.screen.query(PromptInput)
124
+ if results:
125
+ prompt_input = results.first()
126
+ if prompt_input.display:
127
+ prompt_input.focus()
@@ -29,6 +29,53 @@ class ToolFormatter:
29
29
  return {}
30
30
  return args if isinstance(args, dict) else {}
31
31
 
32
+ @classmethod
33
+ def _extract_key_arg(
34
+ cls,
35
+ args: dict[str, object],
36
+ key_arg: str,
37
+ tool_name: str | None = None,
38
+ ) -> str | None:
39
+ """Extract key argument value, handling nested args and special cases.
40
+
41
+ Supports:
42
+ - Direct key access: key_arg="query" -> args["query"]
43
+ - Nested access: key_arg="task" -> args["input"]["task"] (for Pydantic model inputs)
44
+ - Special handling for codebase_shell
45
+
46
+ Args:
47
+ args: Parsed tool arguments dict
48
+ key_arg: The key argument to extract
49
+ tool_name: Optional tool name for special handling
50
+
51
+ Returns:
52
+ The extracted value as a string, or None if not found
53
+ """
54
+ if not args or not isinstance(args, dict):
55
+ return None
56
+
57
+ # Special handling for codebase_shell which needs command + args
58
+ if tool_name == "codebase_shell" and "command" in args:
59
+ command = args.get("command", "")
60
+ cmd_args = args.get("args", [])
61
+ if isinstance(cmd_args, list):
62
+ args_str = " ".join(str(arg) for arg in cmd_args)
63
+ else:
64
+ args_str = ""
65
+ return f"{command} {args_str}".strip()
66
+
67
+ # Direct key access
68
+ if key_arg in args:
69
+ return str(args[key_arg])
70
+
71
+ # Try nested access through "input" (for Pydantic model inputs)
72
+ if "input" in args and isinstance(args["input"], dict):
73
+ input_dict = args["input"]
74
+ if key_arg in input_dict:
75
+ return str(input_dict[key_arg])
76
+
77
+ return None
78
+
32
79
  @classmethod
33
80
  def format_tool_call_part(cls, part: ToolCallPart) -> str:
34
81
  """Format a tool call part using the tool display registry."""
@@ -44,19 +91,10 @@ class ToolFormatter:
44
91
  args = cls.parse_args(part.args)
45
92
 
46
93
  # Get the key argument value
47
- if args and isinstance(args, dict) and display_config.key_arg in args:
48
- # Special handling for codebase_shell which needs command + args
49
- if part.tool_name == "codebase_shell" and "command" in args:
50
- command = args.get("command", "")
51
- cmd_args = args.get("args", [])
52
- if isinstance(cmd_args, list):
53
- args_str = " ".join(str(arg) for arg in cmd_args)
54
- else:
55
- args_str = ""
56
- key_value = f"{command} {args_str}".strip()
57
- else:
58
- key_value = str(args[display_config.key_arg])
59
-
94
+ key_value = cls._extract_key_arg(
95
+ args, display_config.key_arg, part.tool_name
96
+ )
97
+ if key_value:
60
98
  # Format: "display_text: key_value"
61
99
  return f"{display_config.display_text}: {cls.truncate(key_value)}"
62
100
  else:
@@ -95,8 +133,8 @@ class ToolFormatter:
95
133
 
96
134
  args = cls.parse_args(part.args)
97
135
  # Get the key argument value
98
- if args and isinstance(args, dict) and display_config.key_arg in args:
99
- key_value = str(args[display_config.key_arg])
136
+ key_value = cls._extract_key_arg(args, display_config.key_arg)
137
+ if key_value:
100
138
  # Format: "display_text: key_value"
101
139
  return f"{display_config.display_text}: {cls.truncate(key_value)}"
102
140
  else:
@@ -5,6 +5,8 @@ from textual.app import ComposeResult
5
5
  from textual.reactive import reactive
6
6
  from textual.widget import Widget
7
7
 
8
+ from shotgun.tui.protocols import ActiveSubAgentProvider
9
+
8
10
  from .agent_response import AgentResponseWidget
9
11
  from .user_question import UserQuestionWidget
10
12
 
@@ -27,11 +29,19 @@ class PartialResponseWidget(Widget): # TODO: doesn't work lol
27
29
  super().__init__()
28
30
  self.item = item
29
31
 
32
+ def _is_sub_agent_active(self) -> bool:
33
+ """Check if a sub-agent is currently active."""
34
+ if isinstance(self.screen, ActiveSubAgentProvider):
35
+ return self.screen.active_sub_agent is not None
36
+ return False
37
+
30
38
  def compose(self) -> ComposeResult:
31
39
  if self.item is None:
32
40
  pass
33
41
  elif self.item.kind == "response":
34
- yield AgentResponseWidget(self.item)
42
+ yield AgentResponseWidget(
43
+ self.item, is_sub_agent=self._is_sub_agent_active()
44
+ )
35
45
  elif self.item.kind == "request":
36
46
  yield UserQuestionWidget(self.item)
37
47
 
@@ -0,0 +1,219 @@
1
+ """Message types for ChatScreen communication.
2
+
3
+ This module defines Textual message types used for communication
4
+ between widgets and the ChatScreen, particularly for step checkpoints
5
+ and cascade confirmation in the Router's Planning mode.
6
+ """
7
+
8
+ from textual.message import Message
9
+
10
+ from shotgun.agents.models import AgentType
11
+ from shotgun.agents.router.models import CascadeScope, ExecutionPlan, ExecutionStep
12
+
13
+ __all__ = [
14
+ # Step checkpoint messages (Stage 4)
15
+ "StepCompleted",
16
+ "CheckpointContinue",
17
+ "CheckpointModify",
18
+ "CheckpointStop",
19
+ # Cascade confirmation messages (Stage 5)
20
+ "CascadeConfirmationRequired",
21
+ "CascadeConfirmed",
22
+ "CascadeDeclined",
23
+ # Plan approval messages (Stage 7)
24
+ "PlanApprovalRequired",
25
+ "PlanApproved",
26
+ "PlanRejected",
27
+ # Sub-agent lifecycle messages (Stage 8)
28
+ "SubAgentStarted",
29
+ "SubAgentCompleted",
30
+ # Plan panel messages (Stage 11)
31
+ "PlanUpdated",
32
+ "PlanPanelClosed",
33
+ ]
34
+
35
+
36
+ class StepCompleted(Message):
37
+ """Posted when a plan step completes in Planning mode.
38
+
39
+ This message triggers the checkpoint UI to appear, allowing the user
40
+ to choose whether to continue, modify the plan, or stop execution.
41
+
42
+ Attributes:
43
+ step: The step that was just completed.
44
+ next_step: The next step to execute, or None if this was the last step.
45
+ """
46
+
47
+ def __init__(self, step: ExecutionStep, next_step: ExecutionStep | None) -> None:
48
+ super().__init__()
49
+ self.step = step
50
+ self.next_step = next_step
51
+
52
+
53
+ class CheckpointContinue(Message):
54
+ """Posted when user chooses to continue to next step.
55
+
56
+ This message indicates the user wants to proceed with the next
57
+ step in the execution plan.
58
+ """
59
+
60
+
61
+ class CheckpointModify(Message):
62
+ """Posted when user wants to modify the plan.
63
+
64
+ This message indicates the user wants to return to the prompt input
65
+ to make adjustments to the plan before continuing.
66
+ """
67
+
68
+
69
+ class CheckpointStop(Message):
70
+ """Posted when user wants to stop execution.
71
+
72
+ This message indicates the user wants to halt execution while
73
+ keeping the remaining steps in the plan as pending.
74
+ """
75
+
76
+
77
+ # =============================================================================
78
+ # Cascade Confirmation Messages (Stage 5)
79
+ # =============================================================================
80
+
81
+
82
+ class CascadeConfirmationRequired(Message):
83
+ """Posted when a file with dependents was modified and needs cascade confirmation.
84
+
85
+ In Planning mode, after modifying a file like specification.md that has
86
+ dependent files (plan.md, tasks.md), this message triggers the cascade
87
+ confirmation UI to appear.
88
+
89
+ Attributes:
90
+ updated_file: The file that was just updated (e.g., "specification.md").
91
+ dependent_files: List of files that depend on the updated file.
92
+ """
93
+
94
+ def __init__(self, updated_file: str, dependent_files: list[str]) -> None:
95
+ super().__init__()
96
+ self.updated_file = updated_file
97
+ self.dependent_files = dependent_files
98
+
99
+
100
+ class CascadeConfirmed(Message):
101
+ """Posted when user confirms cascade update.
102
+
103
+ This message indicates the user wants to proceed with updating
104
+ dependent files based on the selected scope.
105
+
106
+ Attributes:
107
+ scope: The scope of files to update (ALL, PLAN_ONLY, TASKS_ONLY, NONE).
108
+ """
109
+
110
+ def __init__(self, scope: CascadeScope) -> None:
111
+ super().__init__()
112
+ self.scope = scope
113
+
114
+
115
+ class CascadeDeclined(Message):
116
+ """Posted when user declines cascade update.
117
+
118
+ This message indicates the user does not want to update dependent
119
+ files and will handle them manually.
120
+ """
121
+
122
+
123
+ # =============================================================================
124
+ # Plan Approval Messages (Stage 7)
125
+ # =============================================================================
126
+
127
+
128
+ class PlanApprovalRequired(Message):
129
+ """Posted when a multi-step plan is created and needs user approval.
130
+
131
+ In Planning mode, after the router creates a plan with multiple steps,
132
+ this message triggers the approval UI to appear.
133
+
134
+ Attributes:
135
+ plan: The execution plan that needs user approval.
136
+ """
137
+
138
+ def __init__(self, plan: ExecutionPlan) -> None:
139
+ super().__init__()
140
+ self.plan = plan
141
+
142
+
143
+ class PlanApproved(Message):
144
+ """Posted when user approves the plan.
145
+
146
+ This message indicates the user wants to proceed with executing
147
+ the plan ("Go Ahead").
148
+ """
149
+
150
+
151
+ class PlanRejected(Message):
152
+ """Posted when user rejects the plan to clarify/modify.
153
+
154
+ This message indicates the user wants to return to the prompt input
155
+ to modify or clarify the request ("No, Let Me Clarify").
156
+ """
157
+
158
+
159
+ # =============================================================================
160
+ # Sub-Agent Lifecycle Messages (Stage 8)
161
+ # =============================================================================
162
+
163
+
164
+ class SubAgentStarted(Message):
165
+ """Posted when router starts delegating to a sub-agent.
166
+
167
+ This message triggers the mode indicator to show the active sub-agent
168
+ in the format "📋 Planning → Research".
169
+
170
+ Attributes:
171
+ agent_type: The type of sub-agent that started executing.
172
+ """
173
+
174
+ def __init__(self, agent_type: AgentType) -> None:
175
+ super().__init__()
176
+ self.agent_type = agent_type
177
+
178
+
179
+ class SubAgentCompleted(Message):
180
+ """Posted when sub-agent delegation completes.
181
+
182
+ This message triggers the mode indicator to clear the sub-agent
183
+ display and return to showing just the mode.
184
+
185
+ Attributes:
186
+ agent_type: The type of sub-agent that completed.
187
+ """
188
+
189
+ def __init__(self, agent_type: AgentType) -> None:
190
+ super().__init__()
191
+ self.agent_type = agent_type
192
+
193
+
194
+ # =============================================================================
195
+ # Plan Panel Messages (Stage 11)
196
+ # =============================================================================
197
+
198
+
199
+ class PlanUpdated(Message):
200
+ """Posted when the current plan changes.
201
+
202
+ This message triggers the plan panel to auto-show/hide based on
203
+ whether a plan exists.
204
+
205
+ Attributes:
206
+ plan: The updated execution plan, or None if plan was cleared.
207
+ """
208
+
209
+ def __init__(self, plan: ExecutionPlan | None) -> None:
210
+ super().__init__()
211
+ self.plan = plan
212
+
213
+
214
+ class PlanPanelClosed(Message):
215
+ """Posted when user closes the plan panel with × button.
216
+
217
+ This message indicates the user wants to dismiss the plan panel
218
+ temporarily. The panel will reopen when the plan changes.
219
+ """
@@ -427,40 +427,45 @@ Here are some helpful resources to get you up to speed with Shotgun:
427
427
  """
428
428
 
429
429
  def _page_2_modes(self) -> str:
430
- """Page 2: Explanation of the 5 modes."""
430
+ """Page 2: Explanation of the Router and its modes."""
431
431
  return """
432
- ## Understanding Shotgun's 5 Modes
432
+ ## Understanding Shotgun's Router
433
433
 
434
- Shotgun has 5 specialized modes, each designed for specific tasks. Each mode writes to its own dedicated file in `.shotgun/`:
434
+ Shotgun uses an intelligent **Router** that orchestrates your workflow automatically. Just describe what you need, and the Router will coordinate research, specifications, planning, and tasks for you.
435
435
 
436
- ### 🔬 Research Mode
437
- Research topics with web search and synthesize findings. Perfect for gathering information and exploring new concepts.
436
+ ### Two Operating Modes
438
437
 
439
- **Writes to:** `.shotgun/research.md`
438
+ The Router operates in two modes, which you can toggle with `Shift+Tab`:
440
439
 
441
- ### 📝 Specify Mode
442
- Create detailed specifications and requirements documents. Great for planning features and documenting requirements.
440
+ ### 📋 Planning Mode (Default)
441
+ - **Incremental execution**: Does one step at a time
442
+ - **Asks clarifying questions** before complex tasks
443
+ - **Shows plan for approval** before executing multi-step work
444
+ - **Confirms before cascading** changes to dependent files
443
445
 
444
- **Writes to:** `.shotgun/specification.md`
446
+ Best for: Complex tasks, learning the workflow, staying in control
445
447
 
446
- ### 📋 Plan Mode
447
- Create comprehensive, actionable plans with milestones. Ideal for breaking down large projects into manageable steps.
448
+ ### ✍️ Drafting Mode
449
+ - **Auto-executes** plans without stopping for approval
450
+ - **Makes reasonable assumptions** instead of asking questions
451
+ - **Updates all dependent files** automatically
448
452
 
449
- **Writes to:** `.shotgun/plan.md`
453
+ Best for: Routine tasks, experienced users, speed-focused work
450
454
 
451
- ### ✅ Tasks Mode
452
- Generate specific, actionable tasks from research and plans. Best for getting concrete next steps and action items.
453
-
454
- **Writes to:** `.shotgun/tasks.md`
455
+ ---
455
456
 
456
- ### 📤 Export Mode
457
- Export artifacts and findings to various formats. Creates documentation like Claude.md (AI instructions), Agent.md (agent specs), PRDs, and other deliverables. Can write to any file in `.shotgun/` except the mode-specific files above.
457
+ ### Files Created
458
458
 
459
- **Writes to:** `.shotgun/Claude.md`, `.shotgun/Agent.md`, `.shotgun/PRD.md`, etc.
459
+ The Router manages these files in `.shotgun/`:
460
+ - `research.md` - Research findings
461
+ - `specification.md` - Detailed specifications
462
+ - `plan.md` - Implementation plans
463
+ - `tasks.md` - Actionable task lists
464
+ - `exports/` - Documentation exports
460
465
 
461
466
  ---
462
467
 
463
- **Tip:** You can switch between modes using `Shift+Tab` or `Ctrl+P` to open the command palette!
468
+ **Tip:** Press `Shift+Tab` to toggle between Planning and Drafting modes!
464
469
  """
465
470
 
466
471
  def _page_3_prompts(self) -> str:
@@ -485,12 +490,11 @@ Provide relevant context about what you're trying to accomplish:
485
490
 
486
491
  > "I'm working on the payment flow. I need to add support for refunds."
487
492
 
488
- ### 4. Use the Right Mode
489
- Switch to the appropriate mode for your task:
490
- - Use **Research** for exploration
491
- - Use **Specify** for requirements
492
- - Use **Plan** for implementation strategy
493
- - Use **Tasks** for actionable next steps
493
+ ### 4. Let the Router Guide You
494
+ The Router will automatically coordinate the right workflow:
495
+ - Describe what you want to accomplish
496
+ - In Planning mode, it will ask clarifying questions first
497
+ - In Drafting mode, it will execute immediately
494
498
 
495
499
  ---
496
500