shotgun-sh 0.1.0.dev17__py3-none-any.whl → 0.1.0.dev19__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of shotgun-sh might be problematic. Click here for more details.
- shotgun/agents/agent_manager.py +35 -13
- shotgun/agents/common.py +2 -1
- shotgun/agents/config/models.py +1 -1
- shotgun/agents/config/provider.py +34 -4
- shotgun/agents/export.py +93 -0
- shotgun/agents/tools/user_interaction.py +2 -1
- shotgun/cli/export.py +81 -0
- shotgun/codebase/core/manager.py +9 -3
- shotgun/main.py +2 -1
- shotgun/prompts/agents/export.j2 +83 -0
- shotgun/prompts/agents/partials/common_agent_system_prompt.j2 +14 -10
- shotgun/prompts/agents/partials/interactive_mode.j2 +1 -0
- shotgun/prompts/agents/research.j2 +1 -1
- shotgun/tui/commands/__init__.py +2 -0
- shotgun/tui/screens/chat.py +29 -2
- shotgun/tui/screens/chat_screen/command_providers.py +16 -0
- shotgun/tui/screens/chat_screen/history.py +81 -30
- {shotgun_sh-0.1.0.dev17.dist-info → shotgun_sh-0.1.0.dev19.dist-info}/METADATA +1 -1
- {shotgun_sh-0.1.0.dev17.dist-info → shotgun_sh-0.1.0.dev19.dist-info}/RECORD +22 -19
- {shotgun_sh-0.1.0.dev17.dist-info → shotgun_sh-0.1.0.dev19.dist-info}/WHEEL +0 -0
- {shotgun_sh-0.1.0.dev17.dist-info → shotgun_sh-0.1.0.dev19.dist-info}/entry_points.txt +0 -0
- {shotgun_sh-0.1.0.dev17.dist-info → shotgun_sh-0.1.0.dev19.dist-info}/licenses/LICENSE +0 -0
shotgun/agents/agent_manager.py
CHANGED
|
@@ -17,17 +17,24 @@ from pydantic_ai.agent import AgentRunResult
|
|
|
17
17
|
from pydantic_ai.messages import (
|
|
18
18
|
AgentStreamEvent,
|
|
19
19
|
FinalResultEvent,
|
|
20
|
+
FunctionToolCallEvent,
|
|
21
|
+
FunctionToolResultEvent,
|
|
20
22
|
ModelMessage,
|
|
21
23
|
ModelRequest,
|
|
22
24
|
ModelResponse,
|
|
23
25
|
ModelResponsePart,
|
|
24
26
|
PartDeltaEvent,
|
|
25
27
|
PartStartEvent,
|
|
28
|
+
SystemPromptPart,
|
|
29
|
+
ToolCallPart,
|
|
26
30
|
ToolCallPartDelta,
|
|
27
31
|
)
|
|
28
32
|
from textual.message import Message
|
|
29
33
|
from textual.widget import Widget
|
|
30
34
|
|
|
35
|
+
from shotgun.agents.common import add_system_prompt_message
|
|
36
|
+
|
|
37
|
+
from .export import create_export_agent
|
|
31
38
|
from .history.compaction import apply_persistent_compaction
|
|
32
39
|
from .models import AgentDeps, AgentRuntimeOptions, FileOperation
|
|
33
40
|
from .plan import create_plan_agent
|
|
@@ -45,6 +52,7 @@ class AgentType(Enum):
|
|
|
45
52
|
PLAN = "plan"
|
|
46
53
|
TASKS = "tasks"
|
|
47
54
|
SPECIFY = "specify"
|
|
55
|
+
EXPORT = "export"
|
|
48
56
|
|
|
49
57
|
|
|
50
58
|
class MessageHistoryUpdated(Message):
|
|
@@ -132,6 +140,9 @@ class AgentManager(Widget):
|
|
|
132
140
|
self.specify_agent, self.specify_deps = create_specify_agent(
|
|
133
141
|
agent_runtime_options=agent_runtime_options
|
|
134
142
|
)
|
|
143
|
+
self.export_agent, self.export_deps = create_export_agent(
|
|
144
|
+
agent_runtime_options=agent_runtime_options
|
|
145
|
+
)
|
|
135
146
|
|
|
136
147
|
# Track current active agent
|
|
137
148
|
self._current_agent_type: AgentType = initial_type
|
|
@@ -167,6 +178,7 @@ class AgentManager(Widget):
|
|
|
167
178
|
AgentType.PLAN: self.plan_agent,
|
|
168
179
|
AgentType.TASKS: self.tasks_agent,
|
|
169
180
|
AgentType.SPECIFY: self.specify_agent,
|
|
181
|
+
AgentType.EXPORT: self.export_agent,
|
|
170
182
|
}
|
|
171
183
|
return agent_map[agent_type]
|
|
172
184
|
|
|
@@ -184,6 +196,7 @@ class AgentManager(Widget):
|
|
|
184
196
|
AgentType.PLAN: self.plan_deps,
|
|
185
197
|
AgentType.TASKS: self.tasks_deps,
|
|
186
198
|
AgentType.SPECIFY: self.specify_deps,
|
|
199
|
+
AgentType.EXPORT: self.export_deps,
|
|
187
200
|
}
|
|
188
201
|
return deps_map[agent_type]
|
|
189
202
|
|
|
@@ -264,11 +277,6 @@ class AgentManager(Widget):
|
|
|
264
277
|
self.ui_message_history.append(ModelRequest.user_text_prompt(prompt))
|
|
265
278
|
self._post_messages_updated()
|
|
266
279
|
|
|
267
|
-
# Ensure system prompt is added to message history before running agent
|
|
268
|
-
from pydantic_ai.messages import SystemPromptPart
|
|
269
|
-
|
|
270
|
-
from shotgun.agents.common import add_system_prompt_message
|
|
271
|
-
|
|
272
280
|
# Start with persistent message history
|
|
273
281
|
message_history = self.message_history
|
|
274
282
|
|
|
@@ -389,19 +397,33 @@ class AgentManager(Widget):
|
|
|
389
397
|
state.latest_partial = partial_message
|
|
390
398
|
self._post_partial_message(partial_message, False)
|
|
391
399
|
|
|
400
|
+
elif isinstance(event, FunctionToolCallEvent):
|
|
401
|
+
existing_call_idx = next(
|
|
402
|
+
(
|
|
403
|
+
i
|
|
404
|
+
for i, part in enumerate(partial_parts)
|
|
405
|
+
if isinstance(part, ToolCallPart)
|
|
406
|
+
and part.tool_call_id == event.part.tool_call_id
|
|
407
|
+
),
|
|
408
|
+
None,
|
|
409
|
+
)
|
|
410
|
+
if existing_call_idx is not None:
|
|
411
|
+
partial_parts[existing_call_idx] = event.part
|
|
412
|
+
else:
|
|
413
|
+
partial_parts.append(event.part)
|
|
414
|
+
partial_message = self._build_partial_response(partial_parts)
|
|
415
|
+
if partial_message is not None:
|
|
416
|
+
state.latest_partial = partial_message
|
|
417
|
+
self._post_partial_message(partial_message, False)
|
|
418
|
+
elif isinstance(event, FunctionToolResultEvent):
|
|
419
|
+
self.ui_message_history.append(ModelRequest(parts=[event.result]))
|
|
420
|
+
self._post_messages_updated() ## this is what the user responded with
|
|
392
421
|
elif isinstance(event, FinalResultEvent):
|
|
393
422
|
final_message = (
|
|
394
423
|
state.latest_partial
|
|
395
424
|
or self._build_partial_response(partial_parts)
|
|
396
425
|
)
|
|
397
|
-
self._post_partial_message(final_message,
|
|
398
|
-
state.latest_partial = None
|
|
399
|
-
state.final_sent = True
|
|
400
|
-
partial_parts.clear()
|
|
401
|
-
self._stream_state = None
|
|
402
|
-
break
|
|
403
|
-
|
|
404
|
-
# Ignore other AgentStreamEvent variants (e.g. tool call notifications) for partial UI updates.
|
|
426
|
+
self._post_partial_message(final_message, False)
|
|
405
427
|
|
|
406
428
|
except Exception: # pragma: no cover - defensive logging
|
|
407
429
|
logger.exception(
|
shotgun/agents/common.py
CHANGED
|
@@ -160,6 +160,7 @@ def create_base_agent(
|
|
|
160
160
|
deps_type=AgentDeps,
|
|
161
161
|
instrument=True,
|
|
162
162
|
history_processors=[history_processor],
|
|
163
|
+
retries=3, # Default retry count for tool calls and output validation
|
|
163
164
|
)
|
|
164
165
|
|
|
165
166
|
# System prompt function is stored in deps and will be called manually in run_agent
|
|
@@ -176,9 +177,9 @@ def create_base_agent(
|
|
|
176
177
|
logger.debug("📞 Interactive mode enabled - ask_user tool registered")
|
|
177
178
|
|
|
178
179
|
# Register common file management tools (always available)
|
|
179
|
-
agent.tool(read_file)
|
|
180
180
|
agent.tool(write_file)
|
|
181
181
|
agent.tool(append_file)
|
|
182
|
+
agent.tool(read_file)
|
|
182
183
|
|
|
183
184
|
# Register artifact management tools (always available)
|
|
184
185
|
agent.tool(create_artifact)
|
shotgun/agents/config/models.py
CHANGED
|
@@ -102,7 +102,7 @@ MODEL_SPECS: dict[str, ModelSpec] = {
|
|
|
102
102
|
name="claude-3-5-sonnet-latest",
|
|
103
103
|
provider=ProviderType.ANTHROPIC,
|
|
104
104
|
max_input_tokens=200_000,
|
|
105
|
-
max_output_tokens=
|
|
105
|
+
max_output_tokens=8_192,
|
|
106
106
|
),
|
|
107
107
|
"gemini-2.5-pro": ModelSpec(
|
|
108
108
|
name="gemini-2.5-pro",
|
|
@@ -4,12 +4,13 @@ import os
|
|
|
4
4
|
|
|
5
5
|
from pydantic import SecretStr
|
|
6
6
|
from pydantic_ai.models import Model
|
|
7
|
-
from pydantic_ai.models.anthropic import AnthropicModel
|
|
7
|
+
from pydantic_ai.models.anthropic import AnthropicModel, AnthropicModelSettings
|
|
8
8
|
from pydantic_ai.models.google import GoogleModel
|
|
9
9
|
from pydantic_ai.models.openai import OpenAIChatModel
|
|
10
10
|
from pydantic_ai.providers.anthropic import AnthropicProvider
|
|
11
11
|
from pydantic_ai.providers.google import GoogleProvider
|
|
12
12
|
from pydantic_ai.providers.openai import OpenAIProvider
|
|
13
|
+
from pydantic_ai.settings import ModelSettings
|
|
13
14
|
|
|
14
15
|
from shotgun.logging_config import get_logger
|
|
15
16
|
|
|
@@ -47,18 +48,47 @@ def get_or_create_model(provider: ProviderType, model_name: str, api_key: str) -
|
|
|
47
48
|
logger.debug("Creating new %s model instance: %s", provider.value, model_name)
|
|
48
49
|
|
|
49
50
|
if provider == ProviderType.OPENAI:
|
|
51
|
+
# Get max_tokens from MODEL_SPECS to use full capacity
|
|
52
|
+
if model_name in MODEL_SPECS:
|
|
53
|
+
max_tokens = MODEL_SPECS[model_name].max_output_tokens
|
|
54
|
+
else:
|
|
55
|
+
max_tokens = 16_000 # Default for GPT models
|
|
56
|
+
|
|
50
57
|
openai_provider = OpenAIProvider(api_key=api_key)
|
|
51
58
|
_model_cache[cache_key] = OpenAIChatModel(
|
|
52
|
-
model_name,
|
|
59
|
+
model_name,
|
|
60
|
+
provider=openai_provider,
|
|
61
|
+
settings=ModelSettings(max_tokens=max_tokens),
|
|
53
62
|
)
|
|
54
63
|
elif provider == ProviderType.ANTHROPIC:
|
|
64
|
+
# Get max_tokens from MODEL_SPECS to use full capacity
|
|
65
|
+
if model_name in MODEL_SPECS:
|
|
66
|
+
max_tokens = MODEL_SPECS[model_name].max_output_tokens
|
|
67
|
+
else:
|
|
68
|
+
max_tokens = 32_000 # Default for Claude models
|
|
69
|
+
|
|
55
70
|
anthropic_provider = AnthropicProvider(api_key=api_key)
|
|
56
71
|
_model_cache[cache_key] = AnthropicModel(
|
|
57
|
-
model_name,
|
|
72
|
+
model_name,
|
|
73
|
+
provider=anthropic_provider,
|
|
74
|
+
settings=AnthropicModelSettings(
|
|
75
|
+
max_tokens=max_tokens,
|
|
76
|
+
timeout=600, # 10 minutes timeout for large responses
|
|
77
|
+
),
|
|
58
78
|
)
|
|
59
79
|
elif provider == ProviderType.GOOGLE:
|
|
80
|
+
# Get max_tokens from MODEL_SPECS to use full capacity
|
|
81
|
+
if model_name in MODEL_SPECS:
|
|
82
|
+
max_tokens = MODEL_SPECS[model_name].max_output_tokens
|
|
83
|
+
else:
|
|
84
|
+
max_tokens = 64_000 # Default for Gemini models
|
|
85
|
+
|
|
60
86
|
google_provider = GoogleProvider(api_key=api_key)
|
|
61
|
-
_model_cache[cache_key] = GoogleModel(
|
|
87
|
+
_model_cache[cache_key] = GoogleModel(
|
|
88
|
+
model_name,
|
|
89
|
+
provider=google_provider,
|
|
90
|
+
settings=ModelSettings(max_tokens=max_tokens),
|
|
91
|
+
)
|
|
62
92
|
else:
|
|
63
93
|
raise ValueError(f"Unsupported provider: {provider}")
|
|
64
94
|
else:
|
shotgun/agents/export.py
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"""Export agent factory and functions using Pydantic AI with file-based memory."""
|
|
2
|
+
|
|
3
|
+
from functools import partial
|
|
4
|
+
|
|
5
|
+
from pydantic_ai import (
|
|
6
|
+
Agent,
|
|
7
|
+
DeferredToolRequests,
|
|
8
|
+
)
|
|
9
|
+
from pydantic_ai.agent import AgentRunResult
|
|
10
|
+
from pydantic_ai.messages import ModelMessage
|
|
11
|
+
|
|
12
|
+
from shotgun.agents.config import ProviderType
|
|
13
|
+
from shotgun.logging_config import get_logger
|
|
14
|
+
|
|
15
|
+
from .common import (
|
|
16
|
+
add_system_status_message,
|
|
17
|
+
build_agent_system_prompt,
|
|
18
|
+
create_base_agent,
|
|
19
|
+
create_usage_limits,
|
|
20
|
+
run_agent,
|
|
21
|
+
)
|
|
22
|
+
from .models import AgentDeps, AgentRuntimeOptions
|
|
23
|
+
|
|
24
|
+
logger = get_logger(__name__)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def create_export_agent(
|
|
28
|
+
agent_runtime_options: AgentRuntimeOptions, provider: ProviderType | None = None
|
|
29
|
+
) -> tuple[Agent[AgentDeps, str | DeferredToolRequests], AgentDeps]:
|
|
30
|
+
"""Create an export agent with file management capabilities.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
agent_runtime_options: Agent runtime options for the agent
|
|
34
|
+
provider: Optional provider override. If None, uses configured default
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
Tuple of (Configured Pydantic AI agent for export management, Agent dependencies)
|
|
38
|
+
"""
|
|
39
|
+
logger.debug("Initializing export agent")
|
|
40
|
+
# Use partial to create system prompt function for export agent
|
|
41
|
+
system_prompt_fn = partial(build_agent_system_prompt, "export")
|
|
42
|
+
|
|
43
|
+
agent, deps = create_base_agent(
|
|
44
|
+
system_prompt_fn, agent_runtime_options, provider=provider
|
|
45
|
+
)
|
|
46
|
+
return agent, deps
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
async def run_export_agent(
|
|
50
|
+
agent: Agent[AgentDeps, str | DeferredToolRequests],
|
|
51
|
+
instruction: str,
|
|
52
|
+
deps: AgentDeps,
|
|
53
|
+
message_history: list[ModelMessage] | None = None,
|
|
54
|
+
) -> AgentRunResult[str | DeferredToolRequests]:
|
|
55
|
+
"""Export artifacts based on the given instruction.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
agent: The configured export agent
|
|
59
|
+
instruction: The export instruction
|
|
60
|
+
deps: Agent dependencies
|
|
61
|
+
message_history: Optional message history for conversation continuity
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
AgentRunResult containing the export process output
|
|
65
|
+
"""
|
|
66
|
+
logger.debug("📤 Starting export for instruction: %s", instruction)
|
|
67
|
+
|
|
68
|
+
message_history = await add_system_status_message(deps, message_history)
|
|
69
|
+
|
|
70
|
+
# Let the agent use its tools to read existing artifacts and export them
|
|
71
|
+
full_prompt = f"Export artifacts or findings based on: {instruction}"
|
|
72
|
+
|
|
73
|
+
try:
|
|
74
|
+
# Create usage limits for responsible API usage
|
|
75
|
+
usage_limits = create_usage_limits()
|
|
76
|
+
|
|
77
|
+
result = await run_agent(
|
|
78
|
+
agent=agent,
|
|
79
|
+
prompt=full_prompt,
|
|
80
|
+
deps=deps,
|
|
81
|
+
message_history=message_history,
|
|
82
|
+
usage_limits=usage_limits,
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
logger.debug("✅ Export completed successfully")
|
|
86
|
+
return result
|
|
87
|
+
|
|
88
|
+
except Exception as e:
|
|
89
|
+
import traceback
|
|
90
|
+
|
|
91
|
+
logger.error("Full traceback:\n%s", traceback.format_exc())
|
|
92
|
+
logger.error("❌ Export failed: %s", str(e))
|
|
93
|
+
raise
|
|
@@ -13,8 +13,9 @@ logger = get_logger(__name__)
|
|
|
13
13
|
async def ask_user(ctx: RunContext[AgentDeps], question: str) -> str:
|
|
14
14
|
"""Ask the human a question and return the answer.
|
|
15
15
|
|
|
16
|
+
|
|
16
17
|
Args:
|
|
17
|
-
question: The question to ask the user
|
|
18
|
+
question: The question to ask the user with a clear CTA at the end. Needs to be is readable, clear, and easy to understand. Use Markdown formatting. Make key phrases and words stand out.
|
|
18
19
|
|
|
19
20
|
Returns:
|
|
20
21
|
The user's response as a string
|
shotgun/cli/export.py
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"""Export command for shotgun CLI."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
from typing import Annotated
|
|
5
|
+
|
|
6
|
+
import typer
|
|
7
|
+
|
|
8
|
+
from shotgun.agents.config import ProviderType
|
|
9
|
+
from shotgun.agents.export import (
|
|
10
|
+
create_export_agent,
|
|
11
|
+
run_export_agent,
|
|
12
|
+
)
|
|
13
|
+
from shotgun.agents.models import AgentRuntimeOptions
|
|
14
|
+
from shotgun.logging_config import get_logger
|
|
15
|
+
|
|
16
|
+
app = typer.Typer(
|
|
17
|
+
name="export", help="Export artifacts to various formats with agentic approach"
|
|
18
|
+
)
|
|
19
|
+
logger = get_logger(__name__)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@app.callback(invoke_without_command=True)
|
|
23
|
+
def export(
|
|
24
|
+
instruction: Annotated[
|
|
25
|
+
str, typer.Argument(help="Export instruction or format specification")
|
|
26
|
+
],
|
|
27
|
+
non_interactive: Annotated[
|
|
28
|
+
bool,
|
|
29
|
+
typer.Option(
|
|
30
|
+
"--non-interactive", "-n", help="Disable user interaction (for CI/CD)"
|
|
31
|
+
),
|
|
32
|
+
] = False,
|
|
33
|
+
provider: Annotated[
|
|
34
|
+
ProviderType | None,
|
|
35
|
+
typer.Option("--provider", "-p", help="AI provider to use (overrides default)"),
|
|
36
|
+
] = None,
|
|
37
|
+
) -> None:
|
|
38
|
+
"""Export artifacts and findings to various formats.
|
|
39
|
+
|
|
40
|
+
This command exports research, plans, tasks, and other project artifacts
|
|
41
|
+
to different formats like Markdown, HTML, JSON, CSV, or project management
|
|
42
|
+
tool formats. The AI agent will analyze available content and transform
|
|
43
|
+
it according to your export requirements.
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
logger.info("📤 Export Instruction: %s", instruction)
|
|
47
|
+
|
|
48
|
+
try:
|
|
49
|
+
# Track export command usage
|
|
50
|
+
from shotgun.posthog_telemetry import track_event
|
|
51
|
+
|
|
52
|
+
track_event(
|
|
53
|
+
"export_command",
|
|
54
|
+
{
|
|
55
|
+
"non_interactive": non_interactive,
|
|
56
|
+
"provider": provider.value if provider else "default",
|
|
57
|
+
},
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
# Create agent dependencies
|
|
61
|
+
agent_runtime_options = AgentRuntimeOptions(
|
|
62
|
+
interactive_mode=not non_interactive
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
# Create the export agent with deps and provider
|
|
66
|
+
agent, deps = create_export_agent(agent_runtime_options, provider)
|
|
67
|
+
|
|
68
|
+
# Start export process
|
|
69
|
+
logger.info("🎯 Starting export...")
|
|
70
|
+
result = asyncio.run(run_export_agent(agent, instruction, deps))
|
|
71
|
+
|
|
72
|
+
# Display results
|
|
73
|
+
logger.info("✅ Export Complete!")
|
|
74
|
+
logger.info("📤 Results:")
|
|
75
|
+
logger.info("%s", result.output)
|
|
76
|
+
|
|
77
|
+
except Exception as e:
|
|
78
|
+
logger.error("❌ Error during export: %s", str(e))
|
|
79
|
+
import traceback
|
|
80
|
+
|
|
81
|
+
logger.debug("Full traceback:\n%s", traceback.format_exc())
|
shotgun/codebase/core/manager.py
CHANGED
|
@@ -27,6 +27,14 @@ from shotgun.logging_config import get_logger
|
|
|
27
27
|
logger = get_logger(__name__)
|
|
28
28
|
|
|
29
29
|
|
|
30
|
+
class CodebaseAlreadyIndexedError(Exception):
|
|
31
|
+
"""Raised when a codebase is already indexed."""
|
|
32
|
+
|
|
33
|
+
def __init__(self, repo_path: str):
|
|
34
|
+
self.repo_path = repo_path
|
|
35
|
+
super().__init__(f"Codebase already indexed: {repo_path}")
|
|
36
|
+
|
|
37
|
+
|
|
30
38
|
class CodebaseFileHandler(FileSystemEventHandler):
|
|
31
39
|
"""Handles file system events for code graph updates."""
|
|
32
40
|
|
|
@@ -339,9 +347,7 @@ class CodebaseGraphManager:
|
|
|
339
347
|
|
|
340
348
|
# Check if graph already exists
|
|
341
349
|
if graph_path.exists():
|
|
342
|
-
raise
|
|
343
|
-
f"Graph already exists for {repo_path}. Use update_graph() to modify it."
|
|
344
|
-
)
|
|
350
|
+
raise CodebaseAlreadyIndexedError(repo_path)
|
|
345
351
|
|
|
346
352
|
# Import the builder from local core module
|
|
347
353
|
from shotgun.codebase.core import CodebaseIngestor
|
shotgun/main.py
CHANGED
|
@@ -7,7 +7,7 @@ from dotenv import load_dotenv
|
|
|
7
7
|
|
|
8
8
|
from shotgun import __version__
|
|
9
9
|
from shotgun.agents.config import get_config_manager
|
|
10
|
-
from shotgun.cli import codebase, config, plan, research, specify, tasks, update
|
|
10
|
+
from shotgun.cli import codebase, config, export, plan, research, specify, tasks, update
|
|
11
11
|
from shotgun.logging_config import configure_root_logger, get_logger
|
|
12
12
|
from shotgun.posthog_telemetry import setup_posthog_observability
|
|
13
13
|
from shotgun.sentry_telemetry import setup_sentry_observability
|
|
@@ -66,6 +66,7 @@ app.add_typer(research.app, name="research", help="Perform research with agentic
|
|
|
66
66
|
app.add_typer(plan.app, name="plan", help="Generate structured plans")
|
|
67
67
|
app.add_typer(specify.app, name="specify", help="Generate comprehensive specifications")
|
|
68
68
|
app.add_typer(tasks.app, name="tasks", help="Generate task lists with agentic approach")
|
|
69
|
+
app.add_typer(export.app, name="export", help="Export artifacts to various formats")
|
|
69
70
|
app.add_typer(update.app, name="update", help="Check for and install updates")
|
|
70
71
|
|
|
71
72
|
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
You are an experienced Export Specialist and Data Transformation Expert.
|
|
2
|
+
|
|
3
|
+
Your job is to help export artifacts, research findings, plans, and other project data to various formats and destinations.
|
|
4
|
+
|
|
5
|
+
{% include 'agents/partials/common_agent_system_prompt.j2' %}
|
|
6
|
+
|
|
7
|
+
## EXPORT WORKFLOW
|
|
8
|
+
|
|
9
|
+
For export tasks:
|
|
10
|
+
1. **Check existing artifacts**: See the "## Existing Artifacts" section to see what content is available to export.
|
|
11
|
+
2. **Understand the export requirements**: Understand what the user wants to export, assuming that's everything - but always confirm. For example some research might not be relevant for export. Priority: Tasks, Plan, Specify, Research.
|
|
12
|
+
3. **Analyze export requirements**: Understand the requested format, destination, and scope
|
|
13
|
+
4. **Read source content**: Read the content of all the relevant artifacts.
|
|
14
|
+
5. **Transform and format**: Convert content to the requested format
|
|
15
|
+
5. **Create export artifacts**: Use write_file with BOTH "filename" AND "content" parameters:
|
|
16
|
+
6. **Validate output**: Ensure the export meets requirements and is properly formatted using read_file()
|
|
17
|
+
|
|
18
|
+
## SUPPORTED EXPORT FORMATS
|
|
19
|
+
|
|
20
|
+
- **AGENTS.md**: See below
|
|
21
|
+
- **Markdown (.md)**: Nicely formatted Markdown file with everything the user wants to export
|
|
22
|
+
|
|
23
|
+
### AGENTS.md
|
|
24
|
+
|
|
25
|
+
Your task: generate an **AGENTS.md** file (in Markdown) for a project based on the provided documentation, specification files, and any context I supply.
|
|
26
|
+
|
|
27
|
+
**Instructions / constraints**:
|
|
28
|
+
- Use clear headings (e.g. “Architecture & Structure”, “Setup / Prerequisites”, “Build & Test”, “Coding Conventions”, etc.).
|
|
29
|
+
- For sections with commands, wrap them in backticks so they can be executed directly.
|
|
30
|
+
- Provide enough detail so that an AI coding agent can understand how to build, test, validate, deploy, and work with the project, without needing to hunt in scattered docs.
|
|
31
|
+
- Link or refer to deeper docs (README, design docs) rather than duplicating large content.
|
|
32
|
+
- If the project is a monorepo or modular, mention whether nested AGENTS.md may override parts.
|
|
33
|
+
- Do **not** include large prose — focus on actionable instructions and bullet lists.
|
|
34
|
+
- Assume the agent must obey programmatic checks listed in the file after generating code.
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
## EXPORT PRINCIPLES
|
|
38
|
+
|
|
39
|
+
- Preserve content structure and meaning during transformation
|
|
40
|
+
- Include proper headers, metadata, and formatting for target format
|
|
41
|
+
- Maintain links, references, and cross-references where applicable
|
|
42
|
+
- Create self-contained exports that can be used independently
|
|
43
|
+
- Add appropriate file extensions and format indicators
|
|
44
|
+
- Include export timestamp and source information
|
|
45
|
+
- Validate exported content is properly formatted and complete
|
|
46
|
+
- Handle missing or incomplete source data gracefully
|
|
47
|
+
- Consider target audience and use case for formatting decisions
|
|
48
|
+
|
|
49
|
+
{% if interactive_mode %}
|
|
50
|
+
USER INTERACTION - CLARIFY EXPORT REQUIREMENTS:
|
|
51
|
+
|
|
52
|
+
- ALWAYS ask clarifying questions when export requirements are unclear
|
|
53
|
+
- Use ask_user tool to gather specific details about:
|
|
54
|
+
- Target format and file type preferences
|
|
55
|
+
- Intended use case and audience for the export
|
|
56
|
+
- Specific content sections or artifacts to include/exclude
|
|
57
|
+
- Output structure and organization preferences
|
|
58
|
+
- Destination (where to save the file)
|
|
59
|
+
- Ask follow-up questions to ensure exports meet exact needs
|
|
60
|
+
- Confirm export scope and format before proceeding with large exports
|
|
61
|
+
- Better to ask 2-3 targeted questions than create generic exports
|
|
62
|
+
{% else %}
|
|
63
|
+
NON-INTERACTIVE MODE - MAKE REASONABLE EXPORT DECISIONS:
|
|
64
|
+
|
|
65
|
+
- Make reasonable assumptions about format based on content type
|
|
66
|
+
- Use standard formats and conventions for the target format
|
|
67
|
+
- Include comprehensive content unless scope is clearly limited
|
|
68
|
+
- Apply sensible default formatting and structure
|
|
69
|
+
- Export to commonly used and widely compatible formats
|
|
70
|
+
- Include standard metadata and documentation
|
|
71
|
+
{% endif %}
|
|
72
|
+
|
|
73
|
+
IMPORTANT RULES:
|
|
74
|
+
- Always verify source content exists before attempting export
|
|
75
|
+
- Preserve all critical information during format conversion
|
|
76
|
+
- Include source attribution and export metadata
|
|
77
|
+
- Create well-structured, properly formatted output
|
|
78
|
+
{% if interactive_mode %}
|
|
79
|
+
- When export requirements are ambiguous, ASK before proceeding
|
|
80
|
+
{% else %}
|
|
81
|
+
- When requirements are unclear, use industry standard practices
|
|
82
|
+
{% endif %}
|
|
83
|
+
- Ensure exported content is self-contained and usable
|
|
@@ -7,16 +7,20 @@ Your extensive expertise spans, among other things:
|
|
|
7
7
|
|
|
8
8
|
## KEY RULES
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
10
|
+
{% if interactive_mode %}
|
|
11
|
+
0. Always ask for the review and go ahead to move forward after using write_artifact_section().
|
|
12
|
+
{% endif %}
|
|
13
|
+
1. Above all, prefer using tools to do the work and NEVER respond with text.
|
|
14
|
+
2. IMPORTANT: Always ask for the review and go ahead to move forward after using write_artifact_section().
|
|
15
|
+
3. **Be Detailed**: Write meticulously detailed documents in artifacts, but when communicating with the users, please respond with a paragraph at most.
|
|
16
|
+
4. **Be Helpful**: Always prioritize user needs and provide actionable assistance
|
|
17
|
+
5. **Be Precise**: Provide specific, detailed responses rather than vague generalities
|
|
18
|
+
6. **Be Collaborative**: Work effectively with other agents when needed
|
|
19
|
+
7. **Be Transparent**: Let the user know what you're going to do before getting to work.
|
|
20
|
+
8. **Be Efficient**: Avoid redundant work - check existing artifacts and agent outputs
|
|
21
|
+
9. **Be Creative**: If the user seems not to know something, always be creative and come up with ideas that fit their thinking.
|
|
22
|
+
10. Greet the user when you're just starting to work.
|
|
23
|
+
11. DO NOT repeat yourself.
|
|
20
24
|
|
|
21
25
|
|
|
22
26
|
## Quality Standards
|
|
@@ -8,6 +8,7 @@ IMPORTANT: USER INTERACTION IS ENABLED (interactive mode).
|
|
|
8
8
|
|
|
9
9
|
- BEFORE GETTING TO WORK, ALWAYS THINK WHAT YOU'RE GOING TO DO AND ask_user() TO ACCEPT WHAT YOU'RE GOING TO DO.
|
|
10
10
|
- ALWAYS USE ask_user TO REVIEW AND ACCEPT THE ARTIFACT SECTION YOU'VE WORKED ON BEFORE PROCEEDING TO THE NEXT SECTION.
|
|
11
|
+
- AFTER USING write_artifact_section(), ALWAYS USE ask_user() TO REVIEW AND ACCEPT THE ARTIFACT SECTION YOU'VE WORKED ON BEFORE PROCEEDING TO THE NEXT SECTION.
|
|
11
12
|
- Don't assume - ask for confirmation of your understanding
|
|
12
13
|
- When in doubt about any aspect of the goal, ASK before proceeding
|
|
13
14
|
|
|
@@ -23,7 +23,7 @@ Use meaningful artifact IDs like: "api-design-patterns", "microservices-study",
|
|
|
23
23
|
## RESEARCH PRINCIPLES
|
|
24
24
|
|
|
25
25
|
{% if interactive_mode -%}
|
|
26
|
-
- CRITICAL: BEFORE RUNNING ANY SEARCH TOOL, ASK THE USER FOR
|
|
26
|
+
- CRITICAL: BEFORE RUNNING ANY SEARCH TOOL, ASK THE USER FOR APPROVAL USING ask_user(). FINISH THE QUESTION WITH ASKING FOR A GO AHEAD.
|
|
27
27
|
{% endif -%}
|
|
28
28
|
- Build upon existing research rather than starting from scratch
|
|
29
29
|
- Focus on practical, actionable information over theoretical concepts
|
shotgun/tui/commands/__init__.py
CHANGED
|
@@ -62,8 +62,10 @@ class CommandHandler:
|
|
|
62
62
|
|
|
63
63
|
**Agent Modes:**
|
|
64
64
|
• **Research** - Research topics with web search and synthesize findings
|
|
65
|
+
• **Specify** - Create detailed specifications and requirements documents
|
|
65
66
|
• **Planning** - Create comprehensive, actionable plans with milestones
|
|
66
67
|
• **Tasks** - Generate specific, actionable tasks from research and plans
|
|
68
|
+
• **Export** - Export artifacts and findings to various formats
|
|
67
69
|
|
|
68
70
|
**Usage:**
|
|
69
71
|
Type your message and press Enter to chat with the AI. The AI will respond based on the current mode."""
|
shotgun/tui/screens/chat.py
CHANGED
|
@@ -33,6 +33,7 @@ from shotgun.agents.models import (
|
|
|
33
33
|
UserAnswer,
|
|
34
34
|
UserQuestion,
|
|
35
35
|
)
|
|
36
|
+
from shotgun.codebase.core.manager import CodebaseAlreadyIndexedError
|
|
36
37
|
from shotgun.sdk.codebase import CodebaseSDK
|
|
37
38
|
from shotgun.sdk.exceptions import CodebaseNotFoundError, InvalidPathError
|
|
38
39
|
from shotgun.sdk.services import get_artifact_service, get_codebase_service
|
|
@@ -123,12 +124,14 @@ class ModeIndicator(Widget):
|
|
|
123
124
|
AgentType.PLAN: "Planning",
|
|
124
125
|
AgentType.TASKS: "Tasks",
|
|
125
126
|
AgentType.SPECIFY: "Specify",
|
|
127
|
+
AgentType.EXPORT: "Export",
|
|
126
128
|
}
|
|
127
129
|
mode_description = {
|
|
128
130
|
AgentType.RESEARCH: "Research topics with web search and synthesize findings",
|
|
129
131
|
AgentType.PLAN: "Create comprehensive, actionable plans with milestones",
|
|
130
132
|
AgentType.TASKS: "Generate specific, actionable tasks from research and plans",
|
|
131
133
|
AgentType.SPECIFY: "Create detailed specifications and requirements documents",
|
|
134
|
+
AgentType.EXPORT: "Export artifacts and findings to various formats",
|
|
132
135
|
}
|
|
133
136
|
|
|
134
137
|
mode_title = mode_display.get(self.mode, self.mode.value.title())
|
|
@@ -248,6 +251,12 @@ class CodebaseIndexScreen(ModalScreen[CodebaseIndexSelection | None]):
|
|
|
248
251
|
disabled=True,
|
|
249
252
|
)
|
|
250
253
|
|
|
254
|
+
def on_mount(self) -> None:
|
|
255
|
+
name_input = self.query_one("#index-codebase-name", Input)
|
|
256
|
+
if not name_input.value and self.selected_path:
|
|
257
|
+
name_input.value = self.selected_path.name
|
|
258
|
+
self._update_confirm()
|
|
259
|
+
|
|
251
260
|
def _update_confirm(self) -> None:
|
|
252
261
|
confirm = self.query_one("#index-confirm", Button)
|
|
253
262
|
name_input = self.query_one("#index-codebase-name", Input)
|
|
@@ -310,6 +319,9 @@ class ChatScreen(Screen[None]):
|
|
|
310
319
|
AgentType.SPECIFY: (
|
|
311
320
|
"Request detailed specifications, e.g. create a comprehensive spec for user authentication system"
|
|
312
321
|
),
|
|
322
|
+
AgentType.EXPORT: (
|
|
323
|
+
"Request export tasks, e.g. export research findings to Markdown or convert tasks to CSV"
|
|
324
|
+
),
|
|
313
325
|
}
|
|
314
326
|
|
|
315
327
|
value = reactive("")
|
|
@@ -423,7 +435,13 @@ class ChatScreen(Screen[None]):
|
|
|
423
435
|
question_display.display = False
|
|
424
436
|
|
|
425
437
|
def action_toggle_mode(self) -> None:
|
|
426
|
-
modes = [
|
|
438
|
+
modes = [
|
|
439
|
+
AgentType.RESEARCH,
|
|
440
|
+
AgentType.SPECIFY,
|
|
441
|
+
AgentType.PLAN,
|
|
442
|
+
AgentType.TASKS,
|
|
443
|
+
AgentType.EXPORT,
|
|
444
|
+
]
|
|
427
445
|
self.mode = modes[(modes.index(self.mode) + 1) % len(modes)]
|
|
428
446
|
self.agent_manager.set_agent(self.mode)
|
|
429
447
|
# whoops it actually changes focus. Let's be brutal for now
|
|
@@ -475,9 +493,14 @@ class ChatScreen(Screen[None]):
|
|
|
475
493
|
if event.is_last:
|
|
476
494
|
partial_response_widget.partial_response = None
|
|
477
495
|
|
|
496
|
+
def _clear_partial_response(self) -> None:
|
|
497
|
+
partial_response_widget = self.query_one(ChatHistory)
|
|
498
|
+
partial_response_widget.partial_response = None
|
|
499
|
+
|
|
478
500
|
@on(MessageHistoryUpdated)
|
|
479
501
|
def handle_message_history_updated(self, event: MessageHistoryUpdated) -> None:
|
|
480
502
|
"""Handle message history updates from the agent manager."""
|
|
503
|
+
self._clear_partial_response()
|
|
481
504
|
self.messages = event.messages
|
|
482
505
|
|
|
483
506
|
# If there are file operations, add a message showing the modified files
|
|
@@ -611,6 +634,11 @@ class ChatScreen(Screen[None]):
|
|
|
611
634
|
severity="information",
|
|
612
635
|
timeout=8,
|
|
613
636
|
)
|
|
637
|
+
|
|
638
|
+
self.mount_hint(codebase_indexed_hint(selection.name))
|
|
639
|
+
except CodebaseAlreadyIndexedError as exc:
|
|
640
|
+
self.notify(str(exc), severity="warning")
|
|
641
|
+
return
|
|
614
642
|
except InvalidPathError as exc:
|
|
615
643
|
self.notify(str(exc), severity="error")
|
|
616
644
|
|
|
@@ -619,7 +647,6 @@ class ChatScreen(Screen[None]):
|
|
|
619
647
|
finally:
|
|
620
648
|
label.update("")
|
|
621
649
|
label.refresh()
|
|
622
|
-
self.mount_hint(codebase_indexed_hint(selection.name))
|
|
623
650
|
|
|
624
651
|
@work
|
|
625
652
|
async def run_agent(self, message: str) -> None:
|
|
@@ -30,6 +30,11 @@ class AgentModeProvider(Provider):
|
|
|
30
30
|
lambda: self.set_mode(AgentType.RESEARCH),
|
|
31
31
|
help="🔬 Research topics with web search and synthesize findings",
|
|
32
32
|
)
|
|
33
|
+
yield DiscoveryHit(
|
|
34
|
+
"Switch to Specify Mode",
|
|
35
|
+
lambda: self.set_mode(AgentType.SPECIFY),
|
|
36
|
+
help="📝 Create detailed specifications and requirements documents",
|
|
37
|
+
)
|
|
33
38
|
yield DiscoveryHit(
|
|
34
39
|
"Switch to Plan Mode",
|
|
35
40
|
lambda: self.set_mode(AgentType.PLAN),
|
|
@@ -40,6 +45,11 @@ class AgentModeProvider(Provider):
|
|
|
40
45
|
lambda: self.set_mode(AgentType.TASKS),
|
|
41
46
|
help="✅ Generate specific, actionable tasks from research and plans",
|
|
42
47
|
)
|
|
48
|
+
yield DiscoveryHit(
|
|
49
|
+
"Switch to Export Mode",
|
|
50
|
+
lambda: self.set_mode(AgentType.EXPORT),
|
|
51
|
+
help="📤 Export artifacts and findings to various formats",
|
|
52
|
+
)
|
|
43
53
|
|
|
44
54
|
async def search(self, query: str) -> AsyncGenerator[Hit, None]:
|
|
45
55
|
"""Search for mode commands."""
|
|
@@ -70,6 +80,12 @@ class AgentModeProvider(Provider):
|
|
|
70
80
|
lambda: self.set_mode(AgentType.TASKS),
|
|
71
81
|
AgentType.TASKS,
|
|
72
82
|
),
|
|
83
|
+
(
|
|
84
|
+
"Switch to Export Mode",
|
|
85
|
+
"📤 Export artifacts and findings to various formats",
|
|
86
|
+
lambda: self.set_mode(AgentType.EXPORT),
|
|
87
|
+
AgentType.EXPORT,
|
|
88
|
+
),
|
|
73
89
|
]
|
|
74
90
|
|
|
75
91
|
for title, help_text, callback, mode in commands:
|
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
import json
|
|
2
|
+
from collections.abc import Sequence
|
|
2
3
|
|
|
3
4
|
from pydantic_ai.messages import (
|
|
4
5
|
BuiltinToolCallPart,
|
|
5
6
|
BuiltinToolReturnPart,
|
|
6
7
|
ModelMessage,
|
|
7
8
|
ModelRequest,
|
|
9
|
+
ModelRequestPart,
|
|
8
10
|
ModelResponse,
|
|
9
11
|
TextPart,
|
|
10
12
|
ThinkingPart,
|
|
11
13
|
ToolCallPart,
|
|
14
|
+
ToolReturnPart,
|
|
12
15
|
)
|
|
13
16
|
from textual.app import ComposeResult
|
|
14
17
|
from textual.reactive import reactive
|
|
@@ -78,27 +81,32 @@ class ChatHistory(Widget):
|
|
|
78
81
|
|
|
79
82
|
def compose(self) -> ComposeResult:
|
|
80
83
|
self.vertical_tail = VerticalTail()
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
84
|
+
with self.vertical_tail:
|
|
85
|
+
for item in self.items:
|
|
86
|
+
if isinstance(item, ModelRequest):
|
|
87
|
+
yield UserQuestionWidget(item)
|
|
88
|
+
elif isinstance(item, ModelResponse):
|
|
89
|
+
yield AgentResponseWidget(item)
|
|
90
|
+
yield PartialResponseWidget(self.partial_response).data_bind(
|
|
91
|
+
item=ChatHistory.partial_response
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
def watch_partial_response(self, _partial_response: ModelMessage | None) -> None:
|
|
95
|
+
self.call_after_refresh(self.autoscroll)
|
|
85
96
|
|
|
86
97
|
def update_messages(self, messages: list[ModelMessage]) -> None:
|
|
87
98
|
"""Update the displayed messages without recomposing."""
|
|
88
99
|
if not self.vertical_tail:
|
|
89
100
|
return
|
|
90
101
|
|
|
91
|
-
|
|
92
|
-
self.
|
|
102
|
+
self.items = messages
|
|
103
|
+
self.refresh(recompose=True)
|
|
93
104
|
|
|
94
|
-
|
|
95
|
-
for item in messages:
|
|
96
|
-
if isinstance(item, ModelRequest):
|
|
97
|
-
self.vertical_tail.mount(UserQuestionWidget(item))
|
|
98
|
-
elif isinstance(item, ModelResponse):
|
|
99
|
-
self.vertical_tail.mount(AgentResponseWidget(item))
|
|
105
|
+
self.autoscroll()
|
|
100
106
|
|
|
101
|
-
|
|
107
|
+
def autoscroll(self) -> None:
|
|
108
|
+
if self.vertical_tail:
|
|
109
|
+
self.vertical_tail.scroll_end(animate=False)
|
|
102
110
|
|
|
103
111
|
|
|
104
112
|
class UserQuestionWidget(Widget):
|
|
@@ -116,6 +124,20 @@ class UserQuestionWidget(Widget):
|
|
|
116
124
|
)
|
|
117
125
|
yield Markdown(markdown=f"**>** {prompt}")
|
|
118
126
|
|
|
127
|
+
def format_prompt_parts(self, parts: Sequence[ModelRequestPart]) -> str:
|
|
128
|
+
acc = ""
|
|
129
|
+
for part in parts:
|
|
130
|
+
if isinstance(part, TextPart):
|
|
131
|
+
acc += (
|
|
132
|
+
f"**>** {part.content if isinstance(part.content, str) else ''}\n\n"
|
|
133
|
+
)
|
|
134
|
+
elif isinstance(part, ToolCallPart):
|
|
135
|
+
if part.tool_name == "ask_user" and isinstance(part.content, dict):
|
|
136
|
+
acc += f"**>** {part.content['answer']}\n\n"
|
|
137
|
+
else:
|
|
138
|
+
acc += "∟ finished\n\n" # let's not show anything yet
|
|
139
|
+
return acc
|
|
140
|
+
|
|
119
141
|
|
|
120
142
|
class AgentResponseWidget(Widget):
|
|
121
143
|
def __init__(self, item: ModelResponse | None) -> None:
|
|
@@ -133,34 +155,38 @@ class AgentResponseWidget(Widget):
|
|
|
133
155
|
acc = ""
|
|
134
156
|
if self.item is None:
|
|
135
157
|
return ""
|
|
136
|
-
for part in self.item.parts:
|
|
158
|
+
for idx, part in enumerate(self.item.parts):
|
|
137
159
|
if isinstance(part, TextPart):
|
|
138
160
|
acc += part.content + "\n\n"
|
|
139
161
|
elif isinstance(part, ToolCallPart):
|
|
140
162
|
parts_str = self._format_tool_call_part(part)
|
|
141
163
|
acc += parts_str + "\n\n"
|
|
164
|
+
elif isinstance(part, ToolReturnPart):
|
|
165
|
+
acc += (
|
|
166
|
+
f"tool ({part.tool_name}) return: "
|
|
167
|
+
+ self._format_tool_return_call_part(part)
|
|
168
|
+
+ "\n\n"
|
|
169
|
+
)
|
|
142
170
|
elif isinstance(part, BuiltinToolCallPart):
|
|
143
|
-
acc += f"{part.tool_name}
|
|
171
|
+
acc += f"builtin tool ({part.tool_name}): {part.args}\n\n"
|
|
144
172
|
elif isinstance(part, BuiltinToolReturnPart):
|
|
145
|
-
acc += f"{part.tool_name}
|
|
173
|
+
acc += f"builtin tool ({part.tool_name}) return: {part.content}\n\n"
|
|
146
174
|
elif isinstance(part, ThinkingPart):
|
|
147
|
-
|
|
175
|
+
if (
|
|
176
|
+
idx == len(self.item.parts) - 1
|
|
177
|
+
): # show the thinking part only if it's the last part
|
|
178
|
+
acc += (
|
|
179
|
+
f"thinking: {part.content}\n\n"
|
|
180
|
+
if part.content
|
|
181
|
+
else "Thinking..."
|
|
182
|
+
)
|
|
183
|
+
else:
|
|
184
|
+
continue
|
|
148
185
|
return acc.strip()
|
|
149
186
|
|
|
150
187
|
def _format_tool_call_part(self, part: ToolCallPart) -> str:
|
|
151
188
|
if part.tool_name == "ask_user":
|
|
152
|
-
|
|
153
|
-
try:
|
|
154
|
-
_args = json.loads(part.args) if part.args.strip() else {}
|
|
155
|
-
except json.JSONDecodeError:
|
|
156
|
-
_args = {}
|
|
157
|
-
else:
|
|
158
|
-
_args = part.args
|
|
159
|
-
|
|
160
|
-
if isinstance(_args, dict) and "question" in _args:
|
|
161
|
-
return f"{_args['question']}"
|
|
162
|
-
else:
|
|
163
|
-
return "❓ "
|
|
189
|
+
return self._format_ask_user_part(part)
|
|
164
190
|
if part.tool_name == "write_artifact_section":
|
|
165
191
|
if isinstance(part.args, dict) and "section_title" in part.args:
|
|
166
192
|
return f"{part.tool_name}({part.args['section_title']})"
|
|
@@ -170,6 +196,31 @@ class AgentResponseWidget(Widget):
|
|
|
170
196
|
if isinstance(part.args, dict) and "name" in part.args:
|
|
171
197
|
return f"{part.tool_name}({part.args['name']})"
|
|
172
198
|
else:
|
|
173
|
-
return f"{part.tool_name}()"
|
|
199
|
+
return f"▪ {part.tool_name}()"
|
|
174
200
|
|
|
175
201
|
return f"{part.tool_name}({part.args})"
|
|
202
|
+
|
|
203
|
+
def _format_ask_user_part(
|
|
204
|
+
self,
|
|
205
|
+
part: ToolCallPart,
|
|
206
|
+
) -> str:
|
|
207
|
+
return "*Answer to continue*"
|
|
208
|
+
if isinstance(part.args, str):
|
|
209
|
+
try:
|
|
210
|
+
_args = json.loads(part.args) if part.args.strip() else {}
|
|
211
|
+
except json.JSONDecodeError:
|
|
212
|
+
_args = {}
|
|
213
|
+
else:
|
|
214
|
+
_args = part.args
|
|
215
|
+
|
|
216
|
+
if isinstance(_args, dict) and "question" in _args:
|
|
217
|
+
return f"{_args['question']}"
|
|
218
|
+
else:
|
|
219
|
+
return "❓ "
|
|
220
|
+
|
|
221
|
+
def _format_tool_return_call_part(self, part: ToolReturnPart) -> str:
|
|
222
|
+
content = part.content
|
|
223
|
+
if part.tool_name == "ask_user":
|
|
224
|
+
response = content.get("answer", "") if isinstance(content, dict) else ""
|
|
225
|
+
return f"**⏺** {response}"
|
|
226
|
+
return f"∟ {content}"
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
shotgun/__init__.py,sha256=P40K0fnIsb7SKcQrFnXZ4aREjpWchVDhvM1HxI4cyIQ,104
|
|
2
2
|
shotgun/build_constants.py,sha256=RXNxMz46HaB5jucgMVpw8a2yCJqjbhTOh0PddyEVMN8,713
|
|
3
3
|
shotgun/logging_config.py,sha256=qWPTKu6IhaA_qiHQFhx8zTphm6oHMZFXox2nieyV32M,6795
|
|
4
|
-
shotgun/main.py,sha256=
|
|
4
|
+
shotgun/main.py,sha256=pVyyDjIK0P_s-ptqp_GOjLbj1IhG6UXhny5kfKl2s-U,4883
|
|
5
5
|
shotgun/posthog_telemetry.py,sha256=7drAXtedvZmpPqq4_9dI19kJ_xEHGk7XKXQk797QvCM,4954
|
|
6
6
|
shotgun/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
7
|
shotgun/sentry_telemetry.py,sha256=3r9on0GQposn9aX6Dkb9mrfaVQl_dIZzhu9BjE838AU,2854
|
|
8
8
|
shotgun/telemetry.py,sha256=aBwCRFU97oiIK5K13OhT7yYCQUAVQyrvnoG-aX3k2ZE,3109
|
|
9
9
|
shotgun/agents/__init__.py,sha256=8Jzv1YsDuLyNPFJyckSr_qI4ehTVeDyIMDW4omsfPGc,25
|
|
10
|
-
shotgun/agents/agent_manager.py,sha256=
|
|
10
|
+
shotgun/agents/agent_manager.py,sha256=JviJvtbfDb4KLg4jT9dcMahUsCnZeNIlo0dk-s3_xPM,17120
|
|
11
11
|
shotgun/agents/artifact_state.py,sha256=WkspYQe-9CvBS90PapBsX997yvJ5KWH-qtSXIWmfvp0,2121
|
|
12
|
-
shotgun/agents/common.py,sha256=
|
|
12
|
+
shotgun/agents/common.py,sha256=7IRpfxgSJLcib6KgzWpbxKS5Mh2VOAVKUl66U1c5B-I,11448
|
|
13
|
+
shotgun/agents/export.py,sha256=wGsGoucbonzPu9DExFrxDY02jr5niTy1xEJrhdCM43M,2904
|
|
13
14
|
shotgun/agents/models.py,sha256=6edILmN7caDAmf8x-qgl7lA42tl2b-ZsiC3da8tS3Xw,7127
|
|
14
15
|
shotgun/agents/plan.py,sha256=mn0S4r-uXWjerMTzfJLJo7n0pweNp_v8TI53wOxMdZ4,2984
|
|
15
16
|
shotgun/agents/research.py,sha256=tee5gHT0d1Iupr5MI9ogNTh35xqh0j2N71rmb4dUtG0,3224
|
|
@@ -18,8 +19,8 @@ shotgun/agents/tasks.py,sha256=ChPet4pw1cIJUWqBM05CKIKzEXOFAlAov9QqQOcPK-w,2935
|
|
|
18
19
|
shotgun/agents/config/__init__.py,sha256=Fl8K_81zBpm-OfOW27M_WWLSFdaHHek6lWz95iDREjQ,318
|
|
19
20
|
shotgun/agents/config/constants.py,sha256=c1k7LFvx29rzQCUXxyfr2PukwS5ol8rH8AXQaxALPVM,526
|
|
20
21
|
shotgun/agents/config/manager.py,sha256=kwMbPjz0kEH_WCQAamESGjHdE8d_P-ztel4NL4FWNUw,10662
|
|
21
|
-
shotgun/agents/config/models.py,sha256=
|
|
22
|
-
shotgun/agents/config/provider.py,sha256=
|
|
22
|
+
shotgun/agents/config/models.py,sha256=bX4zVTY5n7qQRlEOcA0SFmyPef9MvxcFW9hKLf_thLs,6018
|
|
23
|
+
shotgun/agents/config/provider.py,sha256=ScT1NfmaXJdewP1UAAD51MLR00YbmPAM53oeMfrVzq8,7202
|
|
23
24
|
shotgun/agents/history/__init__.py,sha256=XFQj2a6fxDqVg0Q3juvN9RjV_RJbgvFZtQOCOjVJyp4,147
|
|
24
25
|
shotgun/agents/history/compaction.py,sha256=KY_ZvRvvlrB6eLPGqtlC6H8h4HPPOtuPcUkgQJUjK5I,2890
|
|
25
26
|
shotgun/agents/history/constants.py,sha256=yWY8rrTZarLA3flCCMB_hS2NMvUDRDTwP4D4j7MIh1w,446
|
|
@@ -32,7 +33,7 @@ shotgun/agents/history/token_estimation.py,sha256=iNqhDSqFzG0YYxGijMRzj54GALFglO
|
|
|
32
33
|
shotgun/agents/tools/__init__.py,sha256=QaN80IqWvB5qEcjHqri1-PYvYlO74vdhcwLugoEdblo,772
|
|
33
34
|
shotgun/agents/tools/artifact_management.py,sha256=f8WvCCcXb_sMK0U4Y8D1RLUhcj2rhT6CrxB3bURYxcs,17205
|
|
34
35
|
shotgun/agents/tools/file_management.py,sha256=6ru6DXAl-S6DiCt2HLGTDrK2rJBJpn-t6RkSHzYbxc4,4571
|
|
35
|
-
shotgun/agents/tools/user_interaction.py,sha256=
|
|
36
|
+
shotgun/agents/tools/user_interaction.py,sha256=b3ncEpvoD06Cz4hwsS-ppVbQajQj640iWnVfA5WBjAA,1236
|
|
36
37
|
shotgun/agents/tools/codebase/__init__.py,sha256=ceAGkK006NeOYaIJBLQsw7Q46sAyCRK9PYDs8feMQVw,661
|
|
37
38
|
shotgun/agents/tools/codebase/codebase_shell.py,sha256=2zEq8YXzdcYttYAfKso_JGRqXHyy3xgAuWlf0unopFg,8635
|
|
38
39
|
shotgun/agents/tools/codebase/directory_lister.py,sha256=MCLGDEc0F-4J8-UrquxdJrIQYs5xzYgws65mjf7Q7X4,4724
|
|
@@ -61,6 +62,7 @@ shotgun/artifacts/templates/specify/prd.yaml,sha256=LTtTOERjYe1iGV7Wj-WJEExcbHp-
|
|
|
61
62
|
shotgun/artifacts/templates/specify/product_spec.yaml,sha256=BAubivc75VnNZVRqGK9kX1TxZDn-e2_eYbuZGaKvk2U,13953
|
|
62
63
|
shotgun/cli/__init__.py,sha256=_F1uW2g87y4bGFxz8Gp8u7mq2voHp8vQIUtCmm8Tojo,40
|
|
63
64
|
shotgun/cli/config.py,sha256=LbjxDNPdetYJiwlcyOYLnqwzALfgU-m54cfstUshbrs,8715
|
|
65
|
+
shotgun/cli/export.py,sha256=3hIwK2_OM1MFYSTfzBxsGuuBGm5fo0XdxASfQ5Uqb3Y,2471
|
|
64
66
|
shotgun/cli/models.py,sha256=LoajeEK7MEDUSnZXb1Li-dbhXqne812YZglx-LcVpiQ,181
|
|
65
67
|
shotgun/cli/plan.py,sha256=T-eu-I9z-dSoKqJ-KI8X5i5Mm0VL1BfornxRiUjTgnk,2324
|
|
66
68
|
shotgun/cli/research.py,sha256=qvBBtX3Wyn6pDZlJpcEvbeK-0iTOXegi71tm8HKVYaE,2490
|
|
@@ -79,21 +81,22 @@ shotgun/codebase/core/change_detector.py,sha256=kWCYLWzRzb3IGGOj71KBn7UOCOKMpINJ
|
|
|
79
81
|
shotgun/codebase/core/code_retrieval.py,sha256=_JVyyQKHDFm3dxOOua1mw9eIIOHIVz3-I8aZtEsEj1E,7927
|
|
80
82
|
shotgun/codebase/core/ingestor.py,sha256=zMjadeqDOEr2v3vhTS25Jvx0WsLPXpgwquZfbdiz57o,59810
|
|
81
83
|
shotgun/codebase/core/language_config.py,sha256=vsqHyuFnumRPRBV1lMOxWKNOIiClO6FyfKQR0fGrtl4,8934
|
|
82
|
-
shotgun/codebase/core/manager.py,sha256=
|
|
84
|
+
shotgun/codebase/core/manager.py,sha256=GpLeyxC25HJKxh3wQxTiTkXv9NUDQdH6Mi9IdaMUmVQ,55586
|
|
83
85
|
shotgun/codebase/core/nl_query.py,sha256=iV6NbsyDd1SQpT9U9BtgxcZPsNoEW3OHrkk475r_jAY,11410
|
|
84
86
|
shotgun/codebase/core/parser_loader.py,sha256=LZRrDS8Sp518jIu3tQW-BxdwJ86lnsTteI478ER9Td8,4278
|
|
85
87
|
shotgun/prompts/__init__.py,sha256=RswUm0HMdfm2m2YKUwUsEdRIwoczdbI7zlucoEvHYRo,132
|
|
86
88
|
shotgun/prompts/loader.py,sha256=jy24-E02pCSmz2651aCT2NgHfRrHAGMYvKrD6gs0Er8,4424
|
|
87
89
|
shotgun/prompts/agents/__init__.py,sha256=YRIJMbzpArojNX1BP5gfxxois334z_GQga8T-xyWMbY,39
|
|
90
|
+
shotgun/prompts/agents/export.j2,sha256=W_OiTRTBmQiB5zZp54Xi2Je4nSeYQoG29QXcq_T04ZY,4333
|
|
88
91
|
shotgun/prompts/agents/plan.j2,sha256=mogmrjqLx5xSXYqWdvLqcObZSfLVLQI39JpVs3Z-V84,2494
|
|
89
|
-
shotgun/prompts/agents/research.j2,sha256=
|
|
92
|
+
shotgun/prompts/agents/research.j2,sha256=cLwPdIvv1MvVaHZYLdBhON-8qk-c3kjrRTF1-7Bc-vI,3069
|
|
90
93
|
shotgun/prompts/agents/specify.j2,sha256=1CC2SHsxA1Yma0gSFsq-k3VpwtEohN9nh2qeRMMRPRA,1809
|
|
91
94
|
shotgun/prompts/agents/tasks.j2,sha256=OYW1zsYRJxoQF4yVMqJNgi4SNz3YmcP4sljHmmqAu4Q,3613
|
|
92
95
|
shotgun/prompts/agents/partials/artifact_system.j2,sha256=kaqkMU-t2x3M7z-4HU4KffSFug1WM5VDsin6tCkxjHg,1528
|
|
93
96
|
shotgun/prompts/agents/partials/codebase_understanding.j2,sha256=AQmN04VRzGmLbxKKthMK4hkJZ9mIU1NMKIpTDOHJrPM,5051
|
|
94
|
-
shotgun/prompts/agents/partials/common_agent_system_prompt.j2,sha256=
|
|
97
|
+
shotgun/prompts/agents/partials/common_agent_system_prompt.j2,sha256=FFqN6C9pT64_yo5EZ4rmUNIn4LmHjSzwE_IT9QlHueA,1906
|
|
95
98
|
shotgun/prompts/agents/partials/content_formatting.j2,sha256=MG0JB7SSp8YV5akDWpbs2f9DcdREIYqLp38NnoWLeQ0,1854
|
|
96
|
-
shotgun/prompts/agents/partials/interactive_mode.j2,sha256=
|
|
99
|
+
shotgun/prompts/agents/partials/interactive_mode.j2,sha256=9sYPbyc46HXg3k1FT_LugIQvOyNDnMQwsMIgOgN-_aY,1100
|
|
97
100
|
shotgun/prompts/agents/state/artifact_templates_available.j2,sha256=Jb31uOnURfLSG8RKQxHZE8zivans_sG6hKgPbC91pfk,819
|
|
98
101
|
shotgun/prompts/agents/state/existing_artifacts_available.j2,sha256=gb5gKYeOXcJmIkkDS1JOm68AEymLutlxNow5HTtev7I,739
|
|
99
102
|
shotgun/prompts/agents/state/system_state.j2,sha256=NFuBOo7cGy0tQrnDUs3wGO19oytGNHK2K8tgoG4cw1M,274
|
|
@@ -118,25 +121,25 @@ shotgun/sdk/services.py,sha256=WJi_AANWJZPdWdq9LntP5nlYgLxLlsFyXt28DGYNoJo,1132
|
|
|
118
121
|
shotgun/tui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
119
122
|
shotgun/tui/app.py,sha256=LjqQ4ylwVS7278bB6zZ5Uey6oD2pH9dljWymUVgYyPE,3661
|
|
120
123
|
shotgun/tui/styles.tcss,sha256=ETyyw1bpMBOqTi5RLcAJUScdPWTvAWEqE9YcT0kVs_E,121
|
|
121
|
-
shotgun/tui/commands/__init__.py,sha256=
|
|
124
|
+
shotgun/tui/commands/__init__.py,sha256=Wy8s8TBSA3twK6agtsf57EFV2VfL8HGvEBF1VOg5-mM,2535
|
|
122
125
|
shotgun/tui/components/prompt_input.py,sha256=Ss-htqraHZAPaehGE4x86ij0veMjc4UgadMXpbdXr40,2229
|
|
123
126
|
shotgun/tui/components/spinner.py,sha256=ovTDeaJ6FD6chZx_Aepia6R3UkPOVJ77EKHfRmn39MY,2427
|
|
124
127
|
shotgun/tui/components/splash.py,sha256=vppy9vEIEvywuUKRXn2y11HwXSRkQZHLYoVjhDVdJeU,1267
|
|
125
128
|
shotgun/tui/components/vertical_tail.py,sha256=kkCH0WjAh54jDvRzIaOffRZXUKn_zHFZ_ichfUpgzaE,1071
|
|
126
|
-
shotgun/tui/screens/chat.py,sha256=
|
|
129
|
+
shotgun/tui/screens/chat.py,sha256=OpB4z6mnK7_uITUX50blSOm_Ox3h4gMAkp1XwCT-OLE,24916
|
|
127
130
|
shotgun/tui/screens/chat.tcss,sha256=2Yq3E23jxsySYsgZf4G1AYrYVcpX0UDW6kNNI0tDmtM,437
|
|
128
131
|
shotgun/tui/screens/directory_setup.py,sha256=lIZ1J4A6g5Q2ZBX8epW7BhR96Dmdcg22CyiM5S-I5WU,3237
|
|
129
132
|
shotgun/tui/screens/provider_config.py,sha256=A_tvDHF5KLP5PV60LjMJ_aoOdT3TjI6_g04UIUqGPqM,7126
|
|
130
133
|
shotgun/tui/screens/splash.py,sha256=E2MsJihi3c9NY1L28o_MstDxGwrCnnV7zdq00MrGAsw,706
|
|
131
134
|
shotgun/tui/screens/chat_screen/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
132
|
-
shotgun/tui/screens/chat_screen/command_providers.py,sha256=
|
|
133
|
-
shotgun/tui/screens/chat_screen/history.py,sha256=
|
|
135
|
+
shotgun/tui/screens/chat_screen/command_providers.py,sha256=O7ZiKb6Id4pbWC2SnQxYBJ09zNklylQrnVTmCkDVC0Q,7847
|
|
136
|
+
shotgun/tui/screens/chat_screen/history.py,sha256=0rSb512ZhkF8zzqkWfwHp-Cmu5VIqL-LzPkohwlHe4Q,7447
|
|
134
137
|
shotgun/utils/__init__.py,sha256=WinIEp9oL2iMrWaDkXz2QX4nYVPAm8C9aBSKTeEwLtE,198
|
|
135
138
|
shotgun/utils/env_utils.py,sha256=8QK5aw_f_V2AVTleQQlcL0RnD4sPJWXlDG46fsHu0d8,1057
|
|
136
139
|
shotgun/utils/file_system_utils.py,sha256=l-0p1bEHF34OU19MahnRFdClHufThfGAjQ431teAIp0,1004
|
|
137
140
|
shotgun/utils/update_checker.py,sha256=Xf-7w3Pos3etzCoT771gJe2HLkA8_V2GrqWy7ni9UqA,11373
|
|
138
|
-
shotgun_sh-0.1.0.
|
|
139
|
-
shotgun_sh-0.1.0.
|
|
140
|
-
shotgun_sh-0.1.0.
|
|
141
|
-
shotgun_sh-0.1.0.
|
|
142
|
-
shotgun_sh-0.1.0.
|
|
141
|
+
shotgun_sh-0.1.0.dev19.dist-info/METADATA,sha256=eAIoOVktZkTdhABGod9zV-95nwFvv5DZe3C_VMO_ICU,11271
|
|
142
|
+
shotgun_sh-0.1.0.dev19.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
143
|
+
shotgun_sh-0.1.0.dev19.dist-info/entry_points.txt,sha256=asZxLU4QILneq0MWW10saVCZc4VWhZfb0wFZvERnzfA,45
|
|
144
|
+
shotgun_sh-0.1.0.dev19.dist-info/licenses/LICENSE,sha256=YebsZl590zCHrF_acCU5pmNt0pnAfD2DmAnevJPB1tY,1065
|
|
145
|
+
shotgun_sh-0.1.0.dev19.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|