shotgun-sh 0.2.17__py3-none-any.whl → 0.3.3.dev1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. shotgun/agents/agent_manager.py +28 -14
  2. shotgun/agents/common.py +1 -1
  3. shotgun/agents/config/README.md +89 -0
  4. shotgun/agents/config/__init__.py +10 -1
  5. shotgun/agents/config/manager.py +323 -53
  6. shotgun/agents/config/models.py +85 -21
  7. shotgun/agents/config/provider.py +51 -13
  8. shotgun/agents/config/streaming_test.py +119 -0
  9. shotgun/agents/context_analyzer/analyzer.py +6 -2
  10. shotgun/agents/conversation/__init__.py +18 -0
  11. shotgun/agents/conversation/filters.py +164 -0
  12. shotgun/agents/conversation/history/chunking.py +278 -0
  13. shotgun/agents/{history → conversation/history}/compaction.py +27 -1
  14. shotgun/agents/{history → conversation/history}/constants.py +5 -0
  15. shotgun/agents/conversation/history/file_content_deduplication.py +216 -0
  16. shotgun/agents/{history → conversation/history}/history_processors.py +267 -3
  17. shotgun/agents/{history → conversation/history}/token_counting/anthropic.py +8 -0
  18. shotgun/agents/{conversation_manager.py → conversation/manager.py} +1 -1
  19. shotgun/agents/{conversation_history.py → conversation/models.py} +8 -94
  20. shotgun/agents/error/__init__.py +11 -0
  21. shotgun/agents/error/models.py +19 -0
  22. shotgun/agents/runner.py +230 -0
  23. shotgun/agents/tools/web_search/openai.py +1 -1
  24. shotgun/build_constants.py +2 -2
  25. shotgun/cli/clear.py +1 -1
  26. shotgun/cli/compact.py +5 -3
  27. shotgun/cli/context.py +44 -1
  28. shotgun/cli/error_handler.py +24 -0
  29. shotgun/cli/export.py +34 -34
  30. shotgun/cli/plan.py +34 -34
  31. shotgun/cli/research.py +17 -9
  32. shotgun/cli/spec/__init__.py +5 -0
  33. shotgun/cli/spec/backup.py +81 -0
  34. shotgun/cli/spec/commands.py +132 -0
  35. shotgun/cli/spec/models.py +48 -0
  36. shotgun/cli/spec/pull_service.py +219 -0
  37. shotgun/cli/specify.py +20 -19
  38. shotgun/cli/tasks.py +34 -34
  39. shotgun/codebase/core/ingestor.py +153 -7
  40. shotgun/codebase/models.py +2 -0
  41. shotgun/exceptions.py +325 -0
  42. shotgun/llm_proxy/__init__.py +17 -0
  43. shotgun/llm_proxy/client.py +215 -0
  44. shotgun/llm_proxy/models.py +137 -0
  45. shotgun/logging_config.py +42 -0
  46. shotgun/main.py +4 -0
  47. shotgun/posthog_telemetry.py +1 -1
  48. shotgun/prompts/agents/partials/common_agent_system_prompt.j2 +28 -3
  49. shotgun/prompts/agents/partials/interactive_mode.j2 +3 -3
  50. shotgun/prompts/agents/plan.j2 +16 -0
  51. shotgun/prompts/agents/research.j2 +16 -3
  52. shotgun/prompts/agents/specify.j2 +54 -1
  53. shotgun/prompts/agents/state/system_state.j2 +0 -2
  54. shotgun/prompts/agents/tasks.j2 +16 -0
  55. shotgun/prompts/history/chunk_summarization.j2 +34 -0
  56. shotgun/prompts/history/combine_summaries.j2 +53 -0
  57. shotgun/sdk/codebase.py +14 -3
  58. shotgun/settings.py +5 -0
  59. shotgun/shotgun_web/__init__.py +67 -1
  60. shotgun/shotgun_web/client.py +42 -1
  61. shotgun/shotgun_web/constants.py +46 -0
  62. shotgun/shotgun_web/exceptions.py +29 -0
  63. shotgun/shotgun_web/models.py +390 -0
  64. shotgun/shotgun_web/shared_specs/__init__.py +32 -0
  65. shotgun/shotgun_web/shared_specs/file_scanner.py +175 -0
  66. shotgun/shotgun_web/shared_specs/hasher.py +83 -0
  67. shotgun/shotgun_web/shared_specs/models.py +71 -0
  68. shotgun/shotgun_web/shared_specs/upload_pipeline.py +329 -0
  69. shotgun/shotgun_web/shared_specs/utils.py +34 -0
  70. shotgun/shotgun_web/specs_client.py +703 -0
  71. shotgun/shotgun_web/supabase_client.py +31 -0
  72. shotgun/tui/app.py +73 -9
  73. shotgun/tui/containers.py +1 -1
  74. shotgun/tui/layout.py +5 -0
  75. shotgun/tui/screens/chat/chat_screen.py +372 -95
  76. shotgun/tui/screens/chat/codebase_index_prompt_screen.py +196 -17
  77. shotgun/tui/screens/chat_screen/command_providers.py +13 -2
  78. shotgun/tui/screens/chat_screen/hint_message.py +76 -1
  79. shotgun/tui/screens/confirmation_dialog.py +40 -0
  80. shotgun/tui/screens/directory_setup.py +45 -41
  81. shotgun/tui/screens/feedback.py +10 -3
  82. shotgun/tui/screens/github_issue.py +11 -2
  83. shotgun/tui/screens/model_picker.py +28 -8
  84. shotgun/tui/screens/onboarding.py +149 -0
  85. shotgun/tui/screens/pipx_migration.py +58 -6
  86. shotgun/tui/screens/provider_config.py +66 -8
  87. shotgun/tui/screens/shared_specs/__init__.py +21 -0
  88. shotgun/tui/screens/shared_specs/create_spec_dialog.py +273 -0
  89. shotgun/tui/screens/shared_specs/models.py +56 -0
  90. shotgun/tui/screens/shared_specs/share_specs_dialog.py +390 -0
  91. shotgun/tui/screens/shared_specs/upload_progress_screen.py +452 -0
  92. shotgun/tui/screens/shotgun_auth.py +110 -16
  93. shotgun/tui/screens/spec_pull.py +288 -0
  94. shotgun/tui/screens/welcome.py +123 -0
  95. shotgun/tui/services/conversation_service.py +5 -2
  96. shotgun/tui/widgets/widget_coordinator.py +1 -1
  97. {shotgun_sh-0.2.17.dist-info → shotgun_sh-0.3.3.dev1.dist-info}/METADATA +9 -2
  98. {shotgun_sh-0.2.17.dist-info → shotgun_sh-0.3.3.dev1.dist-info}/RECORD +112 -77
  99. {shotgun_sh-0.2.17.dist-info → shotgun_sh-0.3.3.dev1.dist-info}/WHEEL +1 -1
  100. /shotgun/agents/{history → conversation/history}/__init__.py +0 -0
  101. /shotgun/agents/{history → conversation/history}/context_extraction.py +0 -0
  102. /shotgun/agents/{history → conversation/history}/history_building.py +0 -0
  103. /shotgun/agents/{history → conversation/history}/message_utils.py +0 -0
  104. /shotgun/agents/{history → conversation/history}/token_counting/__init__.py +0 -0
  105. /shotgun/agents/{history → conversation/history}/token_counting/base.py +0 -0
  106. /shotgun/agents/{history → conversation/history}/token_counting/openai.py +0 -0
  107. /shotgun/agents/{history → conversation/history}/token_counting/sentencepiece_counter.py +0 -0
  108. /shotgun/agents/{history → conversation/history}/token_counting/tokenizer_cache.py +0 -0
  109. /shotgun/agents/{history → conversation/history}/token_counting/utils.py +0 -0
  110. /shotgun/agents/{history → conversation/history}/token_estimation.py +0 -0
  111. {shotgun_sh-0.2.17.dist-info → shotgun_sh-0.3.3.dev1.dist-info}/entry_points.txt +0 -0
  112. {shotgun_sh-0.2.17.dist-info → shotgun_sh-0.3.3.dev1.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,132 @@
1
+ """Spec management commands for shotgun CLI."""
2
+
3
+ import asyncio
4
+ from typing import Annotated
5
+
6
+ import typer
7
+ from rich.console import Console
8
+ from rich.progress import BarColumn, Progress, SpinnerColumn, TaskID, TextColumn
9
+
10
+ from shotgun.logging_config import get_logger
11
+ from shotgun.shotgun_web.exceptions import (
12
+ ForbiddenError,
13
+ NotFoundError,
14
+ UnauthorizedError,
15
+ )
16
+ from shotgun.tui import app as tui_app
17
+ from shotgun.utils.file_system_utils import get_shotgun_base_path
18
+
19
+ from .models import PullSource
20
+ from .pull_service import CancelledError, PullProgress, SpecPullService
21
+
22
+ app = typer.Typer(
23
+ name="spec",
24
+ help="Manage shared specifications",
25
+ no_args_is_help=True,
26
+ )
27
+ logger = get_logger(__name__)
28
+ console = Console()
29
+
30
+
31
+ @app.command()
32
+ def pull(
33
+ version_id: Annotated[str, typer.Argument(help="Version ID to pull")],
34
+ no_tui: Annotated[
35
+ bool,
36
+ typer.Option("--no-tui", help="Run in CLI-only mode (requires existing auth)"),
37
+ ] = False,
38
+ ) -> None:
39
+ """Pull a spec version from the cloud to local .shotgun/ directory.
40
+
41
+ Downloads all files for the specified version and writes them to the
42
+ local .shotgun/ directory. If the directory already has content, it
43
+ will be backed up to ~/.shotgun-sh/backups/ before being replaced.
44
+
45
+ By default, launches the TUI which handles authentication and shows
46
+ the pull progress. Use --no-tui for scripted/headless use (requires
47
+ existing authentication).
48
+
49
+ Example:
50
+ shotgun spec pull 2532e1c7-7068-4d23-9379-58ea439c592f
51
+ """
52
+ if no_tui:
53
+ # CLI-only mode: do pull directly (requires existing auth)
54
+ success = asyncio.run(_async_pull(version_id))
55
+ if not success:
56
+ raise typer.Exit(1)
57
+ else:
58
+ # TUI mode: launch TUI which handles auth and pull
59
+ tui_app.run(pull_version_id=version_id)
60
+
61
+
62
+ async def _async_pull(version_id: str) -> bool:
63
+ """Async implementation of spec pull command.
64
+
65
+ Returns:
66
+ True if pull was successful, False otherwise.
67
+ """
68
+ shotgun_dir = get_shotgun_base_path()
69
+ service = SpecPullService()
70
+
71
+ # Track current progress state for rich display
72
+ current_task_id: TaskID | None = None
73
+ progress_ctx: Progress | None = None
74
+
75
+ def on_progress(p: PullProgress) -> None:
76
+ nonlocal current_task_id, progress_ctx
77
+ # For CLI, we just update the description - progress bar handled by result
78
+ if progress_ctx and current_task_id is not None:
79
+ progress_ctx.update(current_task_id, description=p.phase)
80
+ if p.total_files and p.file_index is not None:
81
+ pct = ((p.file_index + 1) / p.total_files) * 100
82
+ progress_ctx.update(current_task_id, completed=pct)
83
+
84
+ try:
85
+ with Progress(
86
+ SpinnerColumn(),
87
+ TextColumn("[progress.description]{task.description}"),
88
+ BarColumn(),
89
+ TextColumn("[progress.percentage]{task.percentage:>3.0f}%"),
90
+ ) as progress:
91
+ progress_ctx = progress
92
+ current_task_id = progress.add_task("Starting...", total=100)
93
+
94
+ result = await service.pull_version(
95
+ version_id=version_id,
96
+ shotgun_dir=shotgun_dir,
97
+ on_progress=on_progress,
98
+ source=PullSource.CLI,
99
+ )
100
+
101
+ if result.success:
102
+ console.print()
103
+ console.print(f"[green]Successfully pulled '{result.spec_name}'[/green]")
104
+ console.print(f" [dim]Files downloaded:[/dim] {result.file_count}")
105
+ if result.backup_path:
106
+ console.print(f" [dim]Previous backup:[/dim] {result.backup_path}")
107
+ if result.web_url:
108
+ console.print(f" [blue]View in browser:[/blue] {result.web_url}")
109
+ return True
110
+ else:
111
+ console.print(f"[red]Error: {result.error}[/red]")
112
+ return False
113
+
114
+ except UnauthorizedError:
115
+ console.print(
116
+ "[red]Not authenticated. Please re-run the command to login.[/red]"
117
+ )
118
+ raise typer.Exit(1) from None
119
+ except NotFoundError:
120
+ console.print(f"[red]Version not found: {version_id}[/red]")
121
+ console.print("[dim]Check the version ID and try again.[/dim]")
122
+ raise typer.Exit(1) from None
123
+ except ForbiddenError:
124
+ console.print("[red]You don't have access to this spec.[/red]")
125
+ raise typer.Exit(1) from None
126
+ except CancelledError:
127
+ console.print("[yellow]Pull cancelled.[/yellow]")
128
+ raise typer.Exit(1) from None
129
+ except Exception as e:
130
+ logger.exception("Unexpected error in spec pull")
131
+ console.print(f"[red]Unexpected error: {e}[/red]")
132
+ raise typer.Exit(1) from None
@@ -0,0 +1,48 @@
1
+ """Pydantic models for spec CLI commands."""
2
+
3
+ from datetime import datetime
4
+ from enum import StrEnum
5
+
6
+ from pydantic import BaseModel, Field
7
+
8
+
9
+ class PullSource(StrEnum):
10
+ """Source of spec pull operation for analytics."""
11
+
12
+ CLI = "cli"
13
+ TUI = "tui"
14
+
15
+
16
+ class PullPhase(StrEnum):
17
+ """Phases during spec pull operation for analytics."""
18
+
19
+ STARTING = "starting"
20
+ FETCHING = "fetching"
21
+ BACKUP = "backup"
22
+ DOWNLOADING = "downloading"
23
+ FINALIZING = "finalizing"
24
+
25
+
26
+ class SpecMeta(BaseModel):
27
+ """Metadata stored in .shotgun/meta.json after pulling a spec.
28
+
29
+ This file tracks the source of the local spec files and is used
30
+ by the TUI to display version information and enable future sync operations.
31
+ """
32
+
33
+ version_id: str = Field(description="Pulled version UUID")
34
+ spec_id: str = Field(description="Spec UUID")
35
+ spec_name: str = Field(description="Spec name at time of pull")
36
+ workspace_id: str = Field(description="Workspace UUID")
37
+ is_latest: bool = Field(
38
+ description="Whether this was the latest version when pulled"
39
+ )
40
+ pulled_at: datetime = Field(description="Timestamp when spec was pulled (UTC)")
41
+ backup_path: str | None = Field(
42
+ default=None,
43
+ description="Path where previous .shotgun/ files were backed up",
44
+ )
45
+ web_url: str | None = Field(
46
+ default=None,
47
+ description="URL to view this version in the web UI",
48
+ )
@@ -0,0 +1,219 @@
1
+ """Shared spec pull service for CLI and TUI."""
2
+
3
+ import time
4
+ from collections.abc import Callable
5
+ from dataclasses import dataclass
6
+ from datetime import datetime, timezone
7
+ from pathlib import Path
8
+
9
+ from shotgun.logging_config import get_logger
10
+ from shotgun.posthog_telemetry import track_event
11
+ from shotgun.shotgun_web.specs_client import SpecsClient
12
+ from shotgun.shotgun_web.supabase_client import download_file_from_url
13
+
14
+ from .backup import clear_shotgun_dir, create_backup
15
+ from .models import PullPhase, PullSource, SpecMeta
16
+
17
+ logger = get_logger(__name__)
18
+
19
+
20
+ @dataclass
21
+ class PullProgress:
22
+ """Progress update during spec pull."""
23
+
24
+ phase: str
25
+ file_index: int | None = None
26
+ total_files: int | None = None
27
+ current_file: str | None = None
28
+
29
+
30
+ @dataclass
31
+ class PullResult:
32
+ """Result of a spec pull operation."""
33
+
34
+ success: bool
35
+ spec_name: str | None = None
36
+ file_count: int = 0
37
+ backup_path: str | None = None
38
+ web_url: str | None = None
39
+ error: str | None = None
40
+
41
+
42
+ class CancelledError(Exception):
43
+ """Raised when pull is cancelled."""
44
+
45
+
46
+ class SpecPullService:
47
+ """Service for pulling spec versions from cloud."""
48
+
49
+ def __init__(self) -> None:
50
+ self._client = SpecsClient()
51
+
52
+ async def pull_version(
53
+ self,
54
+ version_id: str,
55
+ shotgun_dir: Path,
56
+ on_progress: Callable[[PullProgress], None] | None = None,
57
+ is_cancelled: Callable[[], bool] | None = None,
58
+ source: PullSource = PullSource.CLI,
59
+ ) -> PullResult:
60
+ """Pull a spec version to the local directory.
61
+
62
+ Args:
63
+ version_id: The version UUID to pull
64
+ shotgun_dir: Target directory (typically .shotgun/)
65
+ on_progress: Optional callback for progress updates
66
+ is_cancelled: Optional callback to check if cancelled
67
+ source: Source of the pull request (CLI or TUI)
68
+
69
+ Returns:
70
+ PullResult with success status and details
71
+ """
72
+ start_time = time.time()
73
+ current_phase: PullPhase = PullPhase.STARTING
74
+ track_event("spec_pull_started", {"source": source.value})
75
+
76
+ def report(
77
+ phase: str,
78
+ file_index: int | None = None,
79
+ total_files: int | None = None,
80
+ current_file: str | None = None,
81
+ ) -> None:
82
+ if on_progress:
83
+ on_progress(
84
+ PullProgress(
85
+ phase=phase,
86
+ file_index=file_index,
87
+ total_files=total_files,
88
+ current_file=current_file,
89
+ )
90
+ )
91
+
92
+ def check_cancelled() -> None:
93
+ nonlocal current_phase
94
+ if is_cancelled and is_cancelled():
95
+ track_event(
96
+ "spec_pull_cancelled",
97
+ {"source": source.value, "phase": current_phase.value},
98
+ )
99
+ raise CancelledError()
100
+
101
+ try:
102
+ # Phase 1: Fetch version metadata
103
+ current_phase = PullPhase.FETCHING
104
+ report("Fetching version info...")
105
+ check_cancelled()
106
+
107
+ response = await self._client.get_version_with_files(version_id)
108
+ spec_name = response.spec_name
109
+ files = response.files
110
+
111
+ if not files:
112
+ track_event(
113
+ "spec_pull_failed",
114
+ {
115
+ "source": source.value,
116
+ "error_type": "EmptyVersion",
117
+ "phase": current_phase.value,
118
+ },
119
+ )
120
+ return PullResult(
121
+ success=False,
122
+ spec_name=spec_name,
123
+ error="No files in this version.",
124
+ )
125
+
126
+ # Phase 2: Backup existing content
127
+ current_phase = PullPhase.BACKUP
128
+ backup_path: str | None = None
129
+ if shotgun_dir.exists():
130
+ report("Backing up existing files...")
131
+ check_cancelled()
132
+
133
+ backup_path = await create_backup(shotgun_dir)
134
+ if backup_path:
135
+ clear_shotgun_dir(shotgun_dir)
136
+
137
+ # Ensure directory exists
138
+ shotgun_dir.mkdir(parents=True, exist_ok=True)
139
+
140
+ # Phase 3: Download files
141
+ current_phase = PullPhase.DOWNLOADING
142
+ total_files = len(files)
143
+ total_bytes = 0
144
+ for idx, file_info in enumerate(files):
145
+ check_cancelled()
146
+
147
+ report(
148
+ f"Downloading files ({idx + 1}/{total_files})...",
149
+ file_index=idx,
150
+ total_files=total_files,
151
+ current_file=file_info.relative_path,
152
+ )
153
+
154
+ if not file_info.download_url:
155
+ logger.warning(
156
+ "Skipping file without download URL: %s",
157
+ file_info.relative_path,
158
+ )
159
+ continue
160
+
161
+ content = await download_file_from_url(file_info.download_url)
162
+ total_bytes += file_info.size_bytes
163
+
164
+ local_path = shotgun_dir / file_info.relative_path
165
+ local_path.parent.mkdir(parents=True, exist_ok=True)
166
+ local_path.write_bytes(content)
167
+
168
+ # Phase 4: Write meta.json
169
+ current_phase = PullPhase.FINALIZING
170
+ report("Finalizing...")
171
+ check_cancelled()
172
+
173
+ meta = SpecMeta(
174
+ version_id=response.version.id,
175
+ spec_id=response.spec_id,
176
+ spec_name=response.spec_name,
177
+ workspace_id=response.workspace_id,
178
+ is_latest=response.version.is_latest,
179
+ pulled_at=datetime.now(timezone.utc),
180
+ backup_path=backup_path,
181
+ web_url=response.web_url,
182
+ )
183
+ meta_path = shotgun_dir / "meta.json"
184
+ meta_path.write_text(meta.model_dump_json(indent=2))
185
+
186
+ # Track successful completion
187
+ duration = time.time() - start_time
188
+ track_event(
189
+ "spec_pull_completed",
190
+ {
191
+ "source": source.value,
192
+ "file_count": total_files,
193
+ "total_bytes": total_bytes,
194
+ "duration_seconds": round(duration, 2),
195
+ "had_backup": backup_path is not None,
196
+ },
197
+ )
198
+
199
+ return PullResult(
200
+ success=True,
201
+ spec_name=spec_name,
202
+ file_count=total_files,
203
+ backup_path=backup_path,
204
+ web_url=response.web_url,
205
+ )
206
+
207
+ except CancelledError:
208
+ # Already tracked in check_cancelled()
209
+ raise
210
+ except Exception as e:
211
+ track_event(
212
+ "spec_pull_failed",
213
+ {
214
+ "source": source.value,
215
+ "error_type": type(e).__name__,
216
+ "phase": current_phase.value,
217
+ },
218
+ )
219
+ raise
shotgun/cli/specify.py CHANGED
@@ -11,6 +11,8 @@ from shotgun.agents.specify import (
11
11
  create_specify_agent,
12
12
  run_specify_agent,
13
13
  )
14
+ from shotgun.cli.error_handler import print_agent_error
15
+ from shotgun.exceptions import ErrorNotPickedUpBySentry
14
16
  from shotgun.logging_config import get_logger
15
17
 
16
18
  app = typer.Typer(
@@ -44,26 +46,25 @@ def specify(
44
46
 
45
47
  logger.info("📝 Specification Requirement: %s", requirement)
46
48
 
47
- try:
48
- # Create agent dependencies
49
- agent_runtime_options = AgentRuntimeOptions(
50
- interactive_mode=not non_interactive
51
- )
49
+ # Create agent dependencies
50
+ agent_runtime_options = AgentRuntimeOptions(interactive_mode=not non_interactive)
52
51
 
53
- # Create the specify agent with deps and provider
54
- agent, deps = asyncio.run(create_specify_agent(agent_runtime_options, provider))
52
+ # Create the specify agent with deps and provider
53
+ agent, deps = asyncio.run(create_specify_agent(agent_runtime_options, provider))
55
54
 
56
- # Start specification process
57
- logger.info("📋 Starting specification generation...")
58
- result = asyncio.run(run_specify_agent(agent, requirement, deps))
55
+ # Start specification process with error handling
56
+ logger.info("📋 Starting specification generation...")
59
57
 
60
- # Display results
61
- logger.info("✅ Specification Complete!")
62
- logger.info("📋 Results:")
63
- logger.info("%s", result.output)
58
+ async def async_specify() -> None:
59
+ try:
60
+ result = await run_specify_agent(agent, requirement, deps)
61
+ logger.info("✅ Specification Complete!")
62
+ logger.info("📋 Results:")
63
+ logger.info("%s", result.output)
64
+ except ErrorNotPickedUpBySentry as e:
65
+ print_agent_error(e)
66
+ except Exception as e:
67
+ logger.exception("Unexpected error in specify command")
68
+ print(f"⚠️ An unexpected error occurred: {str(e)}")
64
69
 
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())
70
+ asyncio.run(async_specify())
shotgun/cli/tasks.py CHANGED
@@ -11,7 +11,10 @@ from shotgun.agents.tasks import (
11
11
  create_tasks_agent,
12
12
  run_tasks_agent,
13
13
  )
14
+ from shotgun.cli.error_handler import print_agent_error
15
+ from shotgun.exceptions import ErrorNotPickedUpBySentry
14
16
  from shotgun.logging_config import get_logger
17
+ from shotgun.posthog_telemetry import track_event
15
18
 
16
19
  app = typer.Typer(name="tasks", help="Generate task lists with agentic approach")
17
20
  logger = get_logger(__name__)
@@ -42,37 +45,34 @@ def tasks(
42
45
 
43
46
  logger.info("📋 Task Creation Instruction: %s", instruction)
44
47
 
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 = asyncio.run(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())
48
+ # Track tasks command usage
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(interactive_mode=not non_interactive)
59
+
60
+ # Create the tasks agent with deps and provider
61
+ agent, deps = asyncio.run(create_tasks_agent(agent_runtime_options, provider))
62
+
63
+ # Start task creation process with error handling
64
+ logger.info("🎯 Starting task creation...")
65
+
66
+ async def async_tasks() -> None:
67
+ try:
68
+ result = await run_tasks_agent(agent, instruction, deps)
69
+ logger.info(" Task Creation Complete!")
70
+ logger.info("📋 Results:")
71
+ logger.info("%s", result.output)
72
+ except ErrorNotPickedUpBySentry as e:
73
+ print_agent_error(e)
74
+ except Exception as e:
75
+ logger.exception("Unexpected error in tasks command")
76
+ print(f"⚠️ An unexpected error occurred: {str(e)}")
77
+
78
+ asyncio.run(async_tasks())