shotgun-sh 0.1.0__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/__init__.py +5 -0
- shotgun/agents/__init__.py +1 -0
- shotgun/agents/agent_manager.py +651 -0
- shotgun/agents/common.py +549 -0
- shotgun/agents/config/__init__.py +13 -0
- shotgun/agents/config/constants.py +17 -0
- shotgun/agents/config/manager.py +294 -0
- shotgun/agents/config/models.py +185 -0
- shotgun/agents/config/provider.py +206 -0
- shotgun/agents/conversation_history.py +106 -0
- shotgun/agents/conversation_manager.py +105 -0
- shotgun/agents/export.py +96 -0
- shotgun/agents/history/__init__.py +5 -0
- shotgun/agents/history/compaction.py +85 -0
- shotgun/agents/history/constants.py +19 -0
- shotgun/agents/history/context_extraction.py +108 -0
- shotgun/agents/history/history_building.py +104 -0
- shotgun/agents/history/history_processors.py +426 -0
- shotgun/agents/history/message_utils.py +84 -0
- shotgun/agents/history/token_counting.py +429 -0
- shotgun/agents/history/token_estimation.py +138 -0
- shotgun/agents/messages.py +35 -0
- shotgun/agents/models.py +275 -0
- shotgun/agents/plan.py +98 -0
- shotgun/agents/research.py +108 -0
- shotgun/agents/specify.py +98 -0
- shotgun/agents/tasks.py +96 -0
- shotgun/agents/tools/__init__.py +34 -0
- shotgun/agents/tools/codebase/__init__.py +28 -0
- shotgun/agents/tools/codebase/codebase_shell.py +256 -0
- shotgun/agents/tools/codebase/directory_lister.py +141 -0
- shotgun/agents/tools/codebase/file_read.py +144 -0
- shotgun/agents/tools/codebase/models.py +252 -0
- shotgun/agents/tools/codebase/query_graph.py +67 -0
- shotgun/agents/tools/codebase/retrieve_code.py +81 -0
- shotgun/agents/tools/file_management.py +218 -0
- shotgun/agents/tools/user_interaction.py +37 -0
- shotgun/agents/tools/web_search/__init__.py +60 -0
- shotgun/agents/tools/web_search/anthropic.py +144 -0
- shotgun/agents/tools/web_search/gemini.py +85 -0
- shotgun/agents/tools/web_search/openai.py +98 -0
- shotgun/agents/tools/web_search/utils.py +20 -0
- shotgun/build_constants.py +20 -0
- shotgun/cli/__init__.py +1 -0
- shotgun/cli/codebase/__init__.py +5 -0
- shotgun/cli/codebase/commands.py +202 -0
- shotgun/cli/codebase/models.py +21 -0
- shotgun/cli/config.py +275 -0
- shotgun/cli/export.py +81 -0
- shotgun/cli/models.py +10 -0
- shotgun/cli/plan.py +73 -0
- shotgun/cli/research.py +85 -0
- shotgun/cli/specify.py +69 -0
- shotgun/cli/tasks.py +78 -0
- shotgun/cli/update.py +152 -0
- shotgun/cli/utils.py +25 -0
- shotgun/codebase/__init__.py +12 -0
- shotgun/codebase/core/__init__.py +46 -0
- shotgun/codebase/core/change_detector.py +358 -0
- shotgun/codebase/core/code_retrieval.py +243 -0
- shotgun/codebase/core/ingestor.py +1497 -0
- shotgun/codebase/core/language_config.py +297 -0
- shotgun/codebase/core/manager.py +1662 -0
- shotgun/codebase/core/nl_query.py +331 -0
- shotgun/codebase/core/parser_loader.py +128 -0
- shotgun/codebase/models.py +111 -0
- shotgun/codebase/service.py +206 -0
- shotgun/logging_config.py +227 -0
- shotgun/main.py +167 -0
- shotgun/posthog_telemetry.py +158 -0
- shotgun/prompts/__init__.py +5 -0
- shotgun/prompts/agents/__init__.py +1 -0
- shotgun/prompts/agents/export.j2 +350 -0
- shotgun/prompts/agents/partials/codebase_understanding.j2 +87 -0
- shotgun/prompts/agents/partials/common_agent_system_prompt.j2 +37 -0
- shotgun/prompts/agents/partials/content_formatting.j2 +65 -0
- shotgun/prompts/agents/partials/interactive_mode.j2 +26 -0
- shotgun/prompts/agents/plan.j2 +144 -0
- shotgun/prompts/agents/research.j2 +69 -0
- shotgun/prompts/agents/specify.j2 +51 -0
- shotgun/prompts/agents/state/codebase/codebase_graphs_available.j2 +19 -0
- shotgun/prompts/agents/state/system_state.j2 +31 -0
- shotgun/prompts/agents/tasks.j2 +143 -0
- shotgun/prompts/codebase/__init__.py +1 -0
- shotgun/prompts/codebase/cypher_query_patterns.j2 +223 -0
- shotgun/prompts/codebase/cypher_system.j2 +28 -0
- shotgun/prompts/codebase/enhanced_query_context.j2 +10 -0
- shotgun/prompts/codebase/partials/cypher_rules.j2 +24 -0
- shotgun/prompts/codebase/partials/graph_schema.j2 +30 -0
- shotgun/prompts/codebase/partials/temporal_context.j2 +21 -0
- shotgun/prompts/history/__init__.py +1 -0
- shotgun/prompts/history/incremental_summarization.j2 +53 -0
- shotgun/prompts/history/summarization.j2 +46 -0
- shotgun/prompts/loader.py +140 -0
- shotgun/py.typed +0 -0
- shotgun/sdk/__init__.py +13 -0
- shotgun/sdk/codebase.py +219 -0
- shotgun/sdk/exceptions.py +17 -0
- shotgun/sdk/models.py +189 -0
- shotgun/sdk/services.py +23 -0
- shotgun/sentry_telemetry.py +87 -0
- shotgun/telemetry.py +93 -0
- shotgun/tui/__init__.py +0 -0
- shotgun/tui/app.py +116 -0
- shotgun/tui/commands/__init__.py +76 -0
- shotgun/tui/components/prompt_input.py +69 -0
- shotgun/tui/components/spinner.py +86 -0
- shotgun/tui/components/splash.py +25 -0
- shotgun/tui/components/vertical_tail.py +13 -0
- shotgun/tui/screens/chat.py +782 -0
- shotgun/tui/screens/chat.tcss +43 -0
- shotgun/tui/screens/chat_screen/__init__.py +0 -0
- shotgun/tui/screens/chat_screen/command_providers.py +219 -0
- shotgun/tui/screens/chat_screen/hint_message.py +40 -0
- shotgun/tui/screens/chat_screen/history.py +221 -0
- shotgun/tui/screens/directory_setup.py +113 -0
- shotgun/tui/screens/provider_config.py +221 -0
- shotgun/tui/screens/splash.py +31 -0
- shotgun/tui/styles.tcss +10 -0
- shotgun/tui/utils/__init__.py +5 -0
- shotgun/tui/utils/mode_progress.py +257 -0
- shotgun/utils/__init__.py +5 -0
- shotgun/utils/env_utils.py +35 -0
- shotgun/utils/file_system_utils.py +36 -0
- shotgun/utils/update_checker.py +375 -0
- shotgun_sh-0.1.0.dist-info/METADATA +466 -0
- shotgun_sh-0.1.0.dist-info/RECORD +130 -0
- shotgun_sh-0.1.0.dist-info/WHEEL +4 -0
- shotgun_sh-0.1.0.dist-info/entry_points.txt +2 -0
- shotgun_sh-0.1.0.dist-info/licenses/LICENSE +21 -0
shotgun/cli/config.py
ADDED
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
"""Configuration management CLI commands."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from typing import Annotated, Any
|
|
5
|
+
|
|
6
|
+
import typer
|
|
7
|
+
from rich.console import Console
|
|
8
|
+
from rich.table import Table
|
|
9
|
+
|
|
10
|
+
from shotgun.agents.config import ProviderType, get_config_manager
|
|
11
|
+
from shotgun.logging_config import get_logger
|
|
12
|
+
|
|
13
|
+
logger = get_logger(__name__)
|
|
14
|
+
console = Console()
|
|
15
|
+
|
|
16
|
+
app = typer.Typer(
|
|
17
|
+
name="config",
|
|
18
|
+
help="Manage Shotgun configuration",
|
|
19
|
+
no_args_is_help=True,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@app.command()
|
|
24
|
+
def init(
|
|
25
|
+
interactive: Annotated[
|
|
26
|
+
bool,
|
|
27
|
+
typer.Option("--interactive", "-i", help="Run interactive setup wizard"),
|
|
28
|
+
] = True,
|
|
29
|
+
) -> None:
|
|
30
|
+
"""Initialize Shotgun configuration."""
|
|
31
|
+
config_manager = get_config_manager()
|
|
32
|
+
|
|
33
|
+
if config_manager.config_path.exists() and not typer.confirm(
|
|
34
|
+
f"Configuration already exists at {config_manager.config_path}. Overwrite?"
|
|
35
|
+
):
|
|
36
|
+
console.print("❌ Configuration initialization cancelled.", style="red")
|
|
37
|
+
raise typer.Exit(1)
|
|
38
|
+
|
|
39
|
+
if interactive:
|
|
40
|
+
console.print(
|
|
41
|
+
"🚀 [bold blue]Welcome to Shotgun Configuration Setup![/bold blue]"
|
|
42
|
+
)
|
|
43
|
+
console.print()
|
|
44
|
+
|
|
45
|
+
# Initialize with defaults
|
|
46
|
+
config = config_manager.initialize()
|
|
47
|
+
|
|
48
|
+
# Ask for default provider
|
|
49
|
+
provider_choices = ["openai", "anthropic", "google"]
|
|
50
|
+
console.print("Choose your default AI provider:")
|
|
51
|
+
for i, provider in enumerate(provider_choices, 1):
|
|
52
|
+
console.print(f" {i}. {provider}")
|
|
53
|
+
|
|
54
|
+
while True:
|
|
55
|
+
try:
|
|
56
|
+
choice = typer.prompt("Enter choice (1-3)", type=int)
|
|
57
|
+
if 1 <= choice <= 3:
|
|
58
|
+
config.default_provider = ProviderType(provider_choices[choice - 1])
|
|
59
|
+
break
|
|
60
|
+
else:
|
|
61
|
+
console.print(
|
|
62
|
+
"❌ Invalid choice. Please enter 1, 2, or 3.", style="red"
|
|
63
|
+
)
|
|
64
|
+
except ValueError:
|
|
65
|
+
console.print("❌ Please enter a valid number.", style="red")
|
|
66
|
+
|
|
67
|
+
# Ask for API key for the selected provider
|
|
68
|
+
provider = config.default_provider
|
|
69
|
+
console.print(f"\n🔑 Setting up {provider.upper()} API key...")
|
|
70
|
+
|
|
71
|
+
api_key = typer.prompt(
|
|
72
|
+
f"Enter your {provider.upper()} API key",
|
|
73
|
+
hide_input=True,
|
|
74
|
+
default="",
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
if api_key:
|
|
78
|
+
config_manager.update_provider(provider, api_key=api_key)
|
|
79
|
+
|
|
80
|
+
config_manager.save()
|
|
81
|
+
console.print(
|
|
82
|
+
f"\n✅ [bold green]Configuration saved to {config_manager.config_path}[/bold green]"
|
|
83
|
+
)
|
|
84
|
+
console.print("🎯 You can now use Shotgun with your configured provider!")
|
|
85
|
+
|
|
86
|
+
else:
|
|
87
|
+
config_manager.initialize()
|
|
88
|
+
console.print(f"✅ Configuration initialized at {config_manager.config_path}")
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
@app.command()
|
|
92
|
+
def set(
|
|
93
|
+
provider: Annotated[
|
|
94
|
+
ProviderType,
|
|
95
|
+
typer.Argument(help="AI provider to configure (openai, anthropic, google)"),
|
|
96
|
+
],
|
|
97
|
+
api_key: Annotated[
|
|
98
|
+
str | None,
|
|
99
|
+
typer.Option("--api-key", "-k", help="API key for the provider"),
|
|
100
|
+
] = None,
|
|
101
|
+
default: Annotated[
|
|
102
|
+
bool,
|
|
103
|
+
typer.Option("--default", "-d", help="Set this provider as default"),
|
|
104
|
+
] = False,
|
|
105
|
+
) -> None:
|
|
106
|
+
"""Set configuration for a specific provider."""
|
|
107
|
+
config_manager = get_config_manager()
|
|
108
|
+
|
|
109
|
+
# If no API key provided via option and not just setting default, prompt for it
|
|
110
|
+
if api_key is None and not default:
|
|
111
|
+
api_key = typer.prompt(
|
|
112
|
+
f"Enter your {provider.upper()} API key",
|
|
113
|
+
hide_input=True,
|
|
114
|
+
default="",
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
try:
|
|
118
|
+
if api_key:
|
|
119
|
+
config_manager.update_provider(provider, api_key=api_key)
|
|
120
|
+
|
|
121
|
+
if default:
|
|
122
|
+
config = config_manager.load()
|
|
123
|
+
config.default_provider = provider
|
|
124
|
+
config_manager.save(config)
|
|
125
|
+
|
|
126
|
+
console.print(f"✅ Configuration updated for {provider}")
|
|
127
|
+
|
|
128
|
+
except Exception as e:
|
|
129
|
+
console.print(f"❌ Failed to update configuration: {e}", style="red")
|
|
130
|
+
raise typer.Exit(1) from e
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
@app.command()
|
|
134
|
+
def set_default(
|
|
135
|
+
provider: Annotated[
|
|
136
|
+
ProviderType,
|
|
137
|
+
typer.Argument(
|
|
138
|
+
help="AI provider to set as default (openai, anthropic, google)"
|
|
139
|
+
),
|
|
140
|
+
],
|
|
141
|
+
) -> None:
|
|
142
|
+
"""Set the default AI provider without modifying API keys."""
|
|
143
|
+
config_manager = get_config_manager()
|
|
144
|
+
|
|
145
|
+
try:
|
|
146
|
+
config = config_manager.load()
|
|
147
|
+
|
|
148
|
+
# Check if the provider has an API key configured
|
|
149
|
+
provider_config = getattr(config, provider.value)
|
|
150
|
+
if not provider_config.api_key:
|
|
151
|
+
console.print(
|
|
152
|
+
f"⚠️ Warning: {provider.upper()} does not have an API key configured.",
|
|
153
|
+
style="yellow",
|
|
154
|
+
)
|
|
155
|
+
console.print(f"Use 'shotgun config set {provider}' to configure it.")
|
|
156
|
+
|
|
157
|
+
# Set as default
|
|
158
|
+
config.default_provider = provider
|
|
159
|
+
config_manager.save(config)
|
|
160
|
+
|
|
161
|
+
console.print(f"✅ Default provider set to: {provider}")
|
|
162
|
+
|
|
163
|
+
except Exception as e:
|
|
164
|
+
console.print(f"❌ Failed to set default provider: {e}", style="red")
|
|
165
|
+
raise typer.Exit(1) from e
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
@app.command()
|
|
169
|
+
def get(
|
|
170
|
+
provider: Annotated[
|
|
171
|
+
ProviderType | None,
|
|
172
|
+
typer.Option("--provider", "-p", help="Show config for specific provider"),
|
|
173
|
+
] = None,
|
|
174
|
+
json_output: Annotated[
|
|
175
|
+
bool,
|
|
176
|
+
typer.Option("--json", "-j", help="Output as JSON"),
|
|
177
|
+
] = False,
|
|
178
|
+
) -> None:
|
|
179
|
+
"""Display current configuration."""
|
|
180
|
+
config_manager = get_config_manager()
|
|
181
|
+
config = config_manager.load()
|
|
182
|
+
|
|
183
|
+
if json_output:
|
|
184
|
+
# Convert to dict and mask secrets
|
|
185
|
+
data = config.model_dump()
|
|
186
|
+
_mask_secrets(data)
|
|
187
|
+
console.print(json.dumps(data, indent=2))
|
|
188
|
+
return
|
|
189
|
+
|
|
190
|
+
if provider:
|
|
191
|
+
# Show specific provider configuration
|
|
192
|
+
_show_provider_config(provider, config)
|
|
193
|
+
else:
|
|
194
|
+
# Show all configuration
|
|
195
|
+
_show_full_config(config)
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def _show_full_config(config: Any) -> None:
|
|
199
|
+
"""Display full configuration in a table."""
|
|
200
|
+
table = Table(title="Shotgun Configuration", show_header=True)
|
|
201
|
+
table.add_column("Setting", style="cyan")
|
|
202
|
+
table.add_column("Value", style="white")
|
|
203
|
+
|
|
204
|
+
# Default provider
|
|
205
|
+
table.add_row("Default Provider", f"[bold]{config.default_provider}[/bold]")
|
|
206
|
+
table.add_row("", "") # Separator
|
|
207
|
+
|
|
208
|
+
# Provider configurations
|
|
209
|
+
for provider_name, provider_config in [
|
|
210
|
+
("OpenAI", config.openai),
|
|
211
|
+
("Anthropic", config.anthropic),
|
|
212
|
+
("Google", config.google),
|
|
213
|
+
]:
|
|
214
|
+
table.add_row(f"[bold]{provider_name}[/bold]", "")
|
|
215
|
+
|
|
216
|
+
# API Key
|
|
217
|
+
api_key_status = "✅ Set" if provider_config.api_key else "❌ Not set"
|
|
218
|
+
table.add_row(" API Key", api_key_status)
|
|
219
|
+
table.add_row("", "") # Separator
|
|
220
|
+
|
|
221
|
+
console.print(table)
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
def _show_provider_config(provider: ProviderType, config: Any) -> None:
|
|
225
|
+
"""Display configuration for a specific provider."""
|
|
226
|
+
provider_str = provider.value if isinstance(provider, ProviderType) else provider
|
|
227
|
+
|
|
228
|
+
if provider_str == "openai":
|
|
229
|
+
provider_config = config.openai
|
|
230
|
+
elif provider_str == "anthropic":
|
|
231
|
+
provider_config = config.anthropic
|
|
232
|
+
elif provider_str == "google":
|
|
233
|
+
provider_config = config.google
|
|
234
|
+
else:
|
|
235
|
+
console.print(f"❌ Unknown provider: {provider}", style="red")
|
|
236
|
+
return
|
|
237
|
+
|
|
238
|
+
table = Table(title=f"{provider.upper()} Configuration")
|
|
239
|
+
table.add_column("Setting", style="cyan")
|
|
240
|
+
table.add_column("Value", style="white")
|
|
241
|
+
|
|
242
|
+
# API Key
|
|
243
|
+
api_key_status = "✅ Set" if provider_config.api_key else "❌ Not set"
|
|
244
|
+
table.add_row("API Key", api_key_status)
|
|
245
|
+
|
|
246
|
+
console.print(table)
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
def _mask_secrets(data: dict[str, Any]) -> None:
|
|
250
|
+
"""Mask secrets in configuration data."""
|
|
251
|
+
for provider in ["openai", "anthropic", "google"]:
|
|
252
|
+
if provider in data and isinstance(data[provider], dict):
|
|
253
|
+
if "api_key" in data[provider] and data[provider]["api_key"]:
|
|
254
|
+
data[provider]["api_key"] = _mask_value(data[provider]["api_key"])
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
def _mask_value(value: str) -> str:
|
|
258
|
+
"""Mask a secret value."""
|
|
259
|
+
if len(value) <= 8:
|
|
260
|
+
return "••••••••"
|
|
261
|
+
return f"{value[:4]}{'•' * (len(value) - 8)}{value[-4:]}"
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
@app.command()
|
|
265
|
+
def get_user_id() -> None:
|
|
266
|
+
"""Get the anonymous user ID from configuration."""
|
|
267
|
+
config_manager = get_config_manager()
|
|
268
|
+
|
|
269
|
+
try:
|
|
270
|
+
user_id = config_manager.get_user_id()
|
|
271
|
+
console.print(f"[green]User ID:[/green] {user_id}")
|
|
272
|
+
except Exception as e:
|
|
273
|
+
logger.error(f"Error getting user ID: {e}")
|
|
274
|
+
console.print(f"❌ Failed to get user ID: {str(e)}", style="red")
|
|
275
|
+
raise typer.Exit(1) from e
|
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/cli/models.py
ADDED
shotgun/cli/plan.py
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"""Plan 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.models import AgentRuntimeOptions
|
|
10
|
+
from shotgun.agents.plan import create_plan_agent, run_plan_agent
|
|
11
|
+
from shotgun.logging_config import get_logger
|
|
12
|
+
|
|
13
|
+
app = typer.Typer(name="plan", help="Generate structured plans", no_args_is_help=True)
|
|
14
|
+
logger = get_logger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@app.callback(invoke_without_command=True)
|
|
18
|
+
def plan(
|
|
19
|
+
goal: Annotated[str, typer.Argument(help="Goal or objective to plan for")],
|
|
20
|
+
non_interactive: Annotated[
|
|
21
|
+
bool,
|
|
22
|
+
typer.Option(
|
|
23
|
+
"--non-interactive", "-n", help="Disable user interaction (for CI/CD)"
|
|
24
|
+
),
|
|
25
|
+
] = False,
|
|
26
|
+
provider: Annotated[
|
|
27
|
+
ProviderType | None,
|
|
28
|
+
typer.Option("--provider", "-p", help="AI provider to use (overrides default)"),
|
|
29
|
+
] = None,
|
|
30
|
+
) -> None:
|
|
31
|
+
"""Generate a structured plan for achieving the given goal.
|
|
32
|
+
|
|
33
|
+
This command will create detailed, actionable plans broken down into steps
|
|
34
|
+
and milestones to help achieve your specified objective. It can also update
|
|
35
|
+
existing plans based on new requirements or refinements.
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
logger.info("📋 Planning Goal: %s", goal)
|
|
39
|
+
|
|
40
|
+
try:
|
|
41
|
+
# Track plan command usage
|
|
42
|
+
from shotgun.posthog_telemetry import track_event
|
|
43
|
+
|
|
44
|
+
track_event(
|
|
45
|
+
"plan_command",
|
|
46
|
+
{
|
|
47
|
+
"non_interactive": non_interactive,
|
|
48
|
+
"provider": provider.value if provider else "default",
|
|
49
|
+
},
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
# Create agent dependencies
|
|
53
|
+
agent_runtime_options = AgentRuntimeOptions(
|
|
54
|
+
interactive_mode=not non_interactive
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
# Create the plan agent with deps and provider
|
|
58
|
+
agent, deps = create_plan_agent(agent_runtime_options, provider)
|
|
59
|
+
|
|
60
|
+
# Start planning process
|
|
61
|
+
logger.info("🎯 Starting planning...")
|
|
62
|
+
result = asyncio.run(run_plan_agent(agent, goal, deps))
|
|
63
|
+
|
|
64
|
+
# Display results
|
|
65
|
+
logger.info("✅ Planning Complete!")
|
|
66
|
+
logger.info("📋 Results:")
|
|
67
|
+
logger.info("%s", result.output)
|
|
68
|
+
|
|
69
|
+
except Exception as e:
|
|
70
|
+
logger.error("❌ Error during planning: %s", str(e))
|
|
71
|
+
import traceback
|
|
72
|
+
|
|
73
|
+
logger.debug("Full traceback:\n%s", traceback.format_exc())
|
shotgun/cli/research.py
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"""Research 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.models import AgentRuntimeOptions
|
|
10
|
+
from shotgun.agents.research import (
|
|
11
|
+
create_research_agent,
|
|
12
|
+
run_research_agent,
|
|
13
|
+
)
|
|
14
|
+
from shotgun.logging_config import get_logger
|
|
15
|
+
|
|
16
|
+
app = typer.Typer(
|
|
17
|
+
name="research", help="Perform research with agentic loops", no_args_is_help=True
|
|
18
|
+
)
|
|
19
|
+
logger = get_logger(__name__)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@app.callback(invoke_without_command=True)
|
|
23
|
+
def research(
|
|
24
|
+
query: Annotated[str, typer.Argument(help="Research query or topic")],
|
|
25
|
+
non_interactive: Annotated[
|
|
26
|
+
bool,
|
|
27
|
+
typer.Option(
|
|
28
|
+
"--non-interactive", "-n", help="Disable user interaction (for CI/CD)"
|
|
29
|
+
),
|
|
30
|
+
] = False,
|
|
31
|
+
provider: Annotated[
|
|
32
|
+
ProviderType | None,
|
|
33
|
+
typer.Option("--provider", "-p", help="AI provider to use (overrides default)"),
|
|
34
|
+
] = None,
|
|
35
|
+
) -> None:
|
|
36
|
+
"""Perform research on a given query using agentic loops.
|
|
37
|
+
|
|
38
|
+
This command will use AI agents to iteratively research the provided topic,
|
|
39
|
+
gathering information from multiple sources and refining the search process.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
logger.info("🔍 Research Query: %s", query)
|
|
43
|
+
|
|
44
|
+
try:
|
|
45
|
+
# Run everything in the same event loop
|
|
46
|
+
asyncio.run(async_research(query, non_interactive, provider))
|
|
47
|
+
|
|
48
|
+
except Exception as e:
|
|
49
|
+
logger.error("❌ Error during research: %s", str(e))
|
|
50
|
+
import traceback
|
|
51
|
+
|
|
52
|
+
logger.debug("Full traceback:\n%s", traceback.format_exc())
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
async def async_research(
|
|
56
|
+
query: str,
|
|
57
|
+
non_interactive: bool,
|
|
58
|
+
provider: ProviderType | None = None,
|
|
59
|
+
) -> None:
|
|
60
|
+
"""Async wrapper for research process."""
|
|
61
|
+
# Track research command usage
|
|
62
|
+
from shotgun.posthog_telemetry import track_event
|
|
63
|
+
|
|
64
|
+
track_event(
|
|
65
|
+
"research_command",
|
|
66
|
+
{
|
|
67
|
+
"non_interactive": non_interactive,
|
|
68
|
+
"provider": provider.value if provider else "default",
|
|
69
|
+
},
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
# Create agent dependencies
|
|
73
|
+
agent_runtime_options = AgentRuntimeOptions(interactive_mode=not non_interactive)
|
|
74
|
+
|
|
75
|
+
# Create the research agent with deps and provider
|
|
76
|
+
agent, deps = create_research_agent(agent_runtime_options, provider)
|
|
77
|
+
|
|
78
|
+
# Start research process
|
|
79
|
+
logger.info("🔬 Starting research...")
|
|
80
|
+
result = await run_research_agent(agent, query, deps)
|
|
81
|
+
|
|
82
|
+
# Display results
|
|
83
|
+
print("✅ Research Complete!")
|
|
84
|
+
print("📋 Findings:")
|
|
85
|
+
print(result.output)
|
shotgun/cli/specify.py
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""Specify 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.models import AgentRuntimeOptions
|
|
10
|
+
from shotgun.agents.specify import (
|
|
11
|
+
create_specify_agent,
|
|
12
|
+
run_specify_agent,
|
|
13
|
+
)
|
|
14
|
+
from shotgun.logging_config import get_logger
|
|
15
|
+
|
|
16
|
+
app = typer.Typer(
|
|
17
|
+
name="specify", help="Generate comprehensive specifications", no_args_is_help=True
|
|
18
|
+
)
|
|
19
|
+
logger = get_logger(__name__)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@app.callback(invoke_without_command=True)
|
|
23
|
+
def specify(
|
|
24
|
+
requirement: Annotated[
|
|
25
|
+
str, typer.Argument(help="Requirement or feature to specify")
|
|
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
|
+
"""Generate comprehensive specifications for software features and systems.
|
|
39
|
+
|
|
40
|
+
This command creates detailed technical specifications including requirements,
|
|
41
|
+
architecture, implementation details, and acceptance criteria based on your
|
|
42
|
+
provided requirement or feature description.
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
logger.info("📝 Specification Requirement: %s", requirement)
|
|
46
|
+
|
|
47
|
+
try:
|
|
48
|
+
# Create agent dependencies
|
|
49
|
+
agent_runtime_options = AgentRuntimeOptions(
|
|
50
|
+
interactive_mode=not non_interactive
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
# Create the specify agent with deps and provider
|
|
54
|
+
agent, deps = create_specify_agent(agent_runtime_options, provider)
|
|
55
|
+
|
|
56
|
+
# Start specification process
|
|
57
|
+
logger.info("📋 Starting specification generation...")
|
|
58
|
+
result = asyncio.run(run_specify_agent(agent, requirement, deps))
|
|
59
|
+
|
|
60
|
+
# Display results
|
|
61
|
+
logger.info("✅ Specification Complete!")
|
|
62
|
+
logger.info("📋 Results:")
|
|
63
|
+
logger.info("%s", result.output)
|
|
64
|
+
|
|
65
|
+
except Exception as e:
|
|
66
|
+
logger.error("❌ Error during specification: %s", str(e))
|
|
67
|
+
import traceback
|
|
68
|
+
|
|
69
|
+
logger.debug("Full traceback:\n%s", traceback.format_exc())
|
shotgun/cli/tasks.py
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"""Tasks 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.models import AgentRuntimeOptions
|
|
10
|
+
from shotgun.agents.tasks import (
|
|
11
|
+
create_tasks_agent,
|
|
12
|
+
run_tasks_agent,
|
|
13
|
+
)
|
|
14
|
+
from shotgun.logging_config import get_logger
|
|
15
|
+
|
|
16
|
+
app = typer.Typer(name="tasks", help="Generate task lists with agentic approach")
|
|
17
|
+
logger = get_logger(__name__)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@app.callback(invoke_without_command=True)
|
|
21
|
+
def tasks(
|
|
22
|
+
instruction: Annotated[
|
|
23
|
+
str, typer.Argument(help="Task creation instruction or project description")
|
|
24
|
+
],
|
|
25
|
+
non_interactive: Annotated[
|
|
26
|
+
bool,
|
|
27
|
+
typer.Option(
|
|
28
|
+
"--non-interactive", "-n", help="Disable user interaction (for CI/CD)"
|
|
29
|
+
),
|
|
30
|
+
] = False,
|
|
31
|
+
provider: Annotated[
|
|
32
|
+
ProviderType | None,
|
|
33
|
+
typer.Option("--provider", "-p", help="AI provider to use (overrides default)"),
|
|
34
|
+
] = None,
|
|
35
|
+
) -> None:
|
|
36
|
+
"""Generate actionable task lists based on existing research and plans.
|
|
37
|
+
|
|
38
|
+
This command creates detailed task breakdowns using AI agents that analyze
|
|
39
|
+
your research and plans to generate prioritized, actionable tasks with
|
|
40
|
+
acceptance criteria and effort estimates.
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
logger.info("📋 Task Creation Instruction: %s", instruction)
|
|
44
|
+
|
|
45
|
+
try:
|
|
46
|
+
# Track tasks command usage
|
|
47
|
+
from shotgun.posthog_telemetry import track_event
|
|
48
|
+
|
|
49
|
+
track_event(
|
|
50
|
+
"tasks_command",
|
|
51
|
+
{
|
|
52
|
+
"non_interactive": non_interactive,
|
|
53
|
+
"provider": provider.value if provider else "default",
|
|
54
|
+
},
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
# Create agent dependencies
|
|
58
|
+
agent_runtime_options = AgentRuntimeOptions(
|
|
59
|
+
interactive_mode=not non_interactive
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
# Create the tasks agent with deps and provider
|
|
63
|
+
agent, deps = create_tasks_agent(agent_runtime_options, provider)
|
|
64
|
+
|
|
65
|
+
# Start task creation process
|
|
66
|
+
logger.info("🎯 Starting task creation...")
|
|
67
|
+
result = asyncio.run(run_tasks_agent(agent, instruction, deps))
|
|
68
|
+
|
|
69
|
+
# Display results
|
|
70
|
+
logger.info("✅ Task Creation Complete!")
|
|
71
|
+
logger.info("📋 Results:")
|
|
72
|
+
logger.info("%s", result.output)
|
|
73
|
+
|
|
74
|
+
except Exception as e:
|
|
75
|
+
logger.error("❌ Error during task creation: %s", str(e))
|
|
76
|
+
import traceback
|
|
77
|
+
|
|
78
|
+
logger.debug("Full traceback:\n%s", traceback.format_exc())
|