shotgun-sh 0.3.3.dev1__py3-none-any.whl → 0.6.2__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.
- shotgun/agents/agent_manager.py +497 -30
- shotgun/agents/cancellation.py +103 -0
- shotgun/agents/common.py +90 -77
- shotgun/agents/config/README.md +0 -1
- shotgun/agents/config/manager.py +52 -8
- shotgun/agents/config/models.py +21 -27
- shotgun/agents/config/provider.py +44 -27
- shotgun/agents/conversation/history/file_content_deduplication.py +66 -43
- shotgun/agents/conversation/history/token_counting/base.py +51 -9
- shotgun/agents/export.py +12 -13
- shotgun/agents/file_read.py +176 -0
- shotgun/agents/messages.py +15 -3
- shotgun/agents/models.py +90 -2
- shotgun/agents/plan.py +12 -13
- shotgun/agents/research.py +13 -10
- shotgun/agents/router/__init__.py +47 -0
- shotgun/agents/router/models.py +384 -0
- shotgun/agents/router/router.py +185 -0
- shotgun/agents/router/tools/__init__.py +18 -0
- shotgun/agents/router/tools/delegation_tools.py +557 -0
- shotgun/agents/router/tools/plan_tools.py +403 -0
- shotgun/agents/runner.py +17 -2
- shotgun/agents/specify.py +12 -13
- shotgun/agents/tasks.py +12 -13
- shotgun/agents/tools/__init__.py +8 -0
- shotgun/agents/tools/codebase/directory_lister.py +27 -39
- shotgun/agents/tools/codebase/file_read.py +26 -35
- shotgun/agents/tools/codebase/query_graph.py +9 -0
- shotgun/agents/tools/codebase/retrieve_code.py +9 -0
- shotgun/agents/tools/file_management.py +81 -3
- shotgun/agents/tools/file_read_tools/__init__.py +7 -0
- shotgun/agents/tools/file_read_tools/multimodal_file_read.py +167 -0
- shotgun/agents/tools/markdown_tools/__init__.py +62 -0
- shotgun/agents/tools/markdown_tools/insert_section.py +148 -0
- shotgun/agents/tools/markdown_tools/models.py +86 -0
- shotgun/agents/tools/markdown_tools/remove_section.py +114 -0
- shotgun/agents/tools/markdown_tools/replace_section.py +119 -0
- shotgun/agents/tools/markdown_tools/utils.py +453 -0
- shotgun/agents/tools/registry.py +46 -6
- shotgun/agents/tools/web_search/__init__.py +1 -2
- shotgun/agents/tools/web_search/gemini.py +1 -3
- shotgun/agents/tools/web_search/openai.py +42 -23
- shotgun/attachments/__init__.py +41 -0
- shotgun/attachments/errors.py +60 -0
- shotgun/attachments/models.py +107 -0
- shotgun/attachments/parser.py +257 -0
- shotgun/attachments/processor.py +193 -0
- shotgun/build_constants.py +4 -7
- shotgun/cli/clear.py +2 -2
- shotgun/cli/codebase/commands.py +181 -65
- shotgun/cli/compact.py +2 -2
- shotgun/cli/context.py +2 -2
- shotgun/cli/error_handler.py +2 -2
- shotgun/cli/run.py +90 -0
- shotgun/cli/spec/backup.py +2 -1
- shotgun/codebase/__init__.py +2 -0
- shotgun/codebase/benchmarks/__init__.py +35 -0
- shotgun/codebase/benchmarks/benchmark_runner.py +309 -0
- shotgun/codebase/benchmarks/exporters.py +119 -0
- shotgun/codebase/benchmarks/formatters/__init__.py +49 -0
- shotgun/codebase/benchmarks/formatters/base.py +34 -0
- shotgun/codebase/benchmarks/formatters/json_formatter.py +106 -0
- shotgun/codebase/benchmarks/formatters/markdown.py +136 -0
- shotgun/codebase/benchmarks/models.py +129 -0
- shotgun/codebase/core/__init__.py +4 -0
- shotgun/codebase/core/call_resolution.py +91 -0
- shotgun/codebase/core/change_detector.py +11 -6
- shotgun/codebase/core/errors.py +159 -0
- shotgun/codebase/core/extractors/__init__.py +23 -0
- shotgun/codebase/core/extractors/base.py +138 -0
- shotgun/codebase/core/extractors/factory.py +63 -0
- shotgun/codebase/core/extractors/go/__init__.py +7 -0
- shotgun/codebase/core/extractors/go/extractor.py +122 -0
- shotgun/codebase/core/extractors/javascript/__init__.py +7 -0
- shotgun/codebase/core/extractors/javascript/extractor.py +132 -0
- shotgun/codebase/core/extractors/protocol.py +109 -0
- shotgun/codebase/core/extractors/python/__init__.py +7 -0
- shotgun/codebase/core/extractors/python/extractor.py +141 -0
- shotgun/codebase/core/extractors/rust/__init__.py +7 -0
- shotgun/codebase/core/extractors/rust/extractor.py +139 -0
- shotgun/codebase/core/extractors/types.py +15 -0
- shotgun/codebase/core/extractors/typescript/__init__.py +7 -0
- shotgun/codebase/core/extractors/typescript/extractor.py +92 -0
- shotgun/codebase/core/gitignore.py +252 -0
- shotgun/codebase/core/ingestor.py +644 -354
- shotgun/codebase/core/kuzu_compat.py +119 -0
- shotgun/codebase/core/language_config.py +239 -0
- shotgun/codebase/core/manager.py +256 -46
- shotgun/codebase/core/metrics_collector.py +310 -0
- shotgun/codebase/core/metrics_types.py +347 -0
- shotgun/codebase/core/parallel_executor.py +424 -0
- shotgun/codebase/core/work_distributor.py +254 -0
- shotgun/codebase/core/worker.py +768 -0
- shotgun/codebase/indexing_state.py +86 -0
- shotgun/codebase/models.py +94 -0
- shotgun/codebase/service.py +13 -0
- shotgun/exceptions.py +9 -9
- shotgun/main.py +3 -16
- shotgun/posthog_telemetry.py +165 -24
- shotgun/prompts/agents/export.j2 +2 -0
- shotgun/prompts/agents/file_read.j2 +48 -0
- shotgun/prompts/agents/partials/common_agent_system_prompt.j2 +19 -52
- shotgun/prompts/agents/partials/content_formatting.j2 +12 -33
- shotgun/prompts/agents/partials/interactive_mode.j2 +9 -32
- shotgun/prompts/agents/partials/router_delegation_mode.j2 +35 -0
- shotgun/prompts/agents/plan.j2 +38 -12
- shotgun/prompts/agents/research.j2 +70 -31
- shotgun/prompts/agents/router.j2 +713 -0
- shotgun/prompts/agents/specify.j2 +53 -16
- shotgun/prompts/agents/state/codebase/codebase_graphs_available.j2 +14 -1
- shotgun/prompts/agents/state/system_state.j2 +24 -13
- shotgun/prompts/agents/tasks.j2 +72 -34
- shotgun/settings.py +49 -10
- shotgun/tui/app.py +154 -24
- shotgun/tui/commands/__init__.py +9 -1
- shotgun/tui/components/attachment_bar.py +87 -0
- shotgun/tui/components/mode_indicator.py +120 -25
- shotgun/tui/components/prompt_input.py +25 -28
- shotgun/tui/components/status_bar.py +14 -7
- shotgun/tui/dependencies.py +58 -8
- shotgun/tui/protocols.py +55 -0
- shotgun/tui/screens/chat/chat.tcss +24 -1
- shotgun/tui/screens/chat/chat_screen.py +1376 -213
- shotgun/tui/screens/chat/codebase_index_prompt_screen.py +8 -4
- shotgun/tui/screens/chat_screen/attachment_hint.py +40 -0
- shotgun/tui/screens/chat_screen/command_providers.py +0 -97
- shotgun/tui/screens/chat_screen/history/agent_response.py +7 -3
- shotgun/tui/screens/chat_screen/history/chat_history.py +58 -6
- shotgun/tui/screens/chat_screen/history/formatters.py +75 -15
- shotgun/tui/screens/chat_screen/history/partial_response.py +11 -1
- shotgun/tui/screens/chat_screen/history/user_question.py +25 -3
- shotgun/tui/screens/chat_screen/messages.py +219 -0
- shotgun/tui/screens/database_locked_dialog.py +219 -0
- shotgun/tui/screens/database_timeout_dialog.py +158 -0
- shotgun/tui/screens/kuzu_error_dialog.py +135 -0
- shotgun/tui/screens/model_picker.py +1 -3
- shotgun/tui/screens/models.py +11 -0
- shotgun/tui/state/processing_state.py +19 -0
- shotgun/tui/utils/mode_progress.py +20 -86
- shotgun/tui/widgets/__init__.py +2 -1
- shotgun/tui/widgets/approval_widget.py +152 -0
- shotgun/tui/widgets/cascade_confirmation_widget.py +203 -0
- shotgun/tui/widgets/plan_panel.py +129 -0
- shotgun/tui/widgets/step_checkpoint_widget.py +180 -0
- shotgun/tui/widgets/widget_coordinator.py +18 -0
- shotgun/utils/file_system_utils.py +4 -1
- {shotgun_sh-0.3.3.dev1.dist-info → shotgun_sh-0.6.2.dist-info}/METADATA +88 -35
- shotgun_sh-0.6.2.dist-info/RECORD +291 -0
- shotgun/cli/export.py +0 -81
- shotgun/cli/plan.py +0 -73
- shotgun/cli/research.py +0 -93
- shotgun/cli/specify.py +0 -70
- shotgun/cli/tasks.py +0 -78
- shotgun/sentry_telemetry.py +0 -232
- shotgun/tui/screens/onboarding.py +0 -580
- shotgun_sh-0.3.3.dev1.dist-info/RECORD +0 -229
- {shotgun_sh-0.3.3.dev1.dist-info → shotgun_sh-0.6.2.dist-info}/WHEEL +0 -0
- {shotgun_sh-0.3.3.dev1.dist-info → shotgun_sh-0.6.2.dist-info}/entry_points.txt +0 -0
- {shotgun_sh-0.3.3.dev1.dist-info → shotgun_sh-0.6.2.dist-info}/licenses/LICENSE +0 -0
shotgun/cli/plan.py
DELETED
|
@@ -1,73 +0,0 @@
|
|
|
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.cli.error_handler import print_agent_error
|
|
12
|
-
from shotgun.exceptions import ErrorNotPickedUpBySentry
|
|
13
|
-
from shotgun.logging_config import get_logger
|
|
14
|
-
from shotgun.posthog_telemetry import track_event
|
|
15
|
-
|
|
16
|
-
app = typer.Typer(name="plan", help="Generate structured plans", no_args_is_help=True)
|
|
17
|
-
logger = get_logger(__name__)
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
@app.callback(invoke_without_command=True)
|
|
21
|
-
def plan(
|
|
22
|
-
goal: Annotated[str, typer.Argument(help="Goal or objective to plan for")],
|
|
23
|
-
non_interactive: Annotated[
|
|
24
|
-
bool,
|
|
25
|
-
typer.Option(
|
|
26
|
-
"--non-interactive", "-n", help="Disable user interaction (for CI/CD)"
|
|
27
|
-
),
|
|
28
|
-
] = False,
|
|
29
|
-
provider: Annotated[
|
|
30
|
-
ProviderType | None,
|
|
31
|
-
typer.Option("--provider", "-p", help="AI provider to use (overrides default)"),
|
|
32
|
-
] = None,
|
|
33
|
-
) -> None:
|
|
34
|
-
"""Generate a structured plan for achieving the given goal.
|
|
35
|
-
|
|
36
|
-
This command will create detailed, actionable plans broken down into steps
|
|
37
|
-
and milestones to help achieve your specified objective. It can also update
|
|
38
|
-
existing plans based on new requirements or refinements.
|
|
39
|
-
"""
|
|
40
|
-
|
|
41
|
-
logger.info("📋 Planning Goal: %s", goal)
|
|
42
|
-
|
|
43
|
-
# Track plan command usage
|
|
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(interactive_mode=not non_interactive)
|
|
54
|
-
|
|
55
|
-
# Create the plan agent with deps and provider
|
|
56
|
-
agent, deps = asyncio.run(create_plan_agent(agent_runtime_options, provider))
|
|
57
|
-
|
|
58
|
-
# Start planning process with error handling
|
|
59
|
-
logger.info("🎯 Starting planning...")
|
|
60
|
-
|
|
61
|
-
async def async_plan() -> None:
|
|
62
|
-
try:
|
|
63
|
-
result = await run_plan_agent(agent, goal, deps)
|
|
64
|
-
logger.info("✅ Planning Complete!")
|
|
65
|
-
logger.info("📋 Results:")
|
|
66
|
-
logger.info("%s", result.output)
|
|
67
|
-
except ErrorNotPickedUpBySentry as e:
|
|
68
|
-
print_agent_error(e)
|
|
69
|
-
except Exception as e:
|
|
70
|
-
logger.exception("Unexpected error in plan command")
|
|
71
|
-
print(f"⚠️ An unexpected error occurred: {str(e)}")
|
|
72
|
-
|
|
73
|
-
asyncio.run(async_plan())
|
shotgun/cli/research.py
DELETED
|
@@ -1,93 +0,0 @@
|
|
|
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.cli.error_handler import print_agent_error
|
|
15
|
-
from shotgun.exceptions import ErrorNotPickedUpBySentry
|
|
16
|
-
from shotgun.logging_config import get_logger
|
|
17
|
-
from shotgun.posthog_telemetry import track_event
|
|
18
|
-
|
|
19
|
-
app = typer.Typer(
|
|
20
|
-
name="research", help="Perform research with agentic loops", no_args_is_help=True
|
|
21
|
-
)
|
|
22
|
-
logger = get_logger(__name__)
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
@app.callback(invoke_without_command=True)
|
|
26
|
-
def research(
|
|
27
|
-
query: Annotated[str, typer.Argument(help="Research query or topic")],
|
|
28
|
-
non_interactive: Annotated[
|
|
29
|
-
bool,
|
|
30
|
-
typer.Option(
|
|
31
|
-
"--non-interactive", "-n", help="Disable user interaction (for CI/CD)"
|
|
32
|
-
),
|
|
33
|
-
] = False,
|
|
34
|
-
provider: Annotated[
|
|
35
|
-
ProviderType | None,
|
|
36
|
-
typer.Option("--provider", "-p", help="AI provider to use (overrides default)"),
|
|
37
|
-
] = None,
|
|
38
|
-
) -> None:
|
|
39
|
-
"""Perform research on a given query using agentic loops.
|
|
40
|
-
|
|
41
|
-
This command will use AI agents to iteratively research the provided topic,
|
|
42
|
-
gathering information from multiple sources and refining the search process.
|
|
43
|
-
"""
|
|
44
|
-
|
|
45
|
-
logger.info("🔍 Research Query: %s", query)
|
|
46
|
-
|
|
47
|
-
try:
|
|
48
|
-
# Run everything in the same event loop
|
|
49
|
-
asyncio.run(async_research(query, non_interactive, provider))
|
|
50
|
-
|
|
51
|
-
except Exception as e:
|
|
52
|
-
logger.error("❌ Error during research: %s", str(e))
|
|
53
|
-
import traceback
|
|
54
|
-
|
|
55
|
-
logger.debug("Full traceback:\n%s", traceback.format_exc())
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
async def async_research(
|
|
59
|
-
query: str,
|
|
60
|
-
non_interactive: bool,
|
|
61
|
-
provider: ProviderType | None = None,
|
|
62
|
-
) -> None:
|
|
63
|
-
"""Async wrapper for research process."""
|
|
64
|
-
# Track research command usage
|
|
65
|
-
track_event(
|
|
66
|
-
"research_command",
|
|
67
|
-
{
|
|
68
|
-
"non_interactive": non_interactive,
|
|
69
|
-
"provider": provider.value if provider else "default",
|
|
70
|
-
},
|
|
71
|
-
)
|
|
72
|
-
|
|
73
|
-
# Create agent dependencies
|
|
74
|
-
agent_runtime_options = AgentRuntimeOptions(interactive_mode=not non_interactive)
|
|
75
|
-
|
|
76
|
-
# Create the research agent with deps and provider
|
|
77
|
-
agent, deps = await create_research_agent(agent_runtime_options, provider)
|
|
78
|
-
|
|
79
|
-
# Start research process with error handling
|
|
80
|
-
logger.info("🔬 Starting research...")
|
|
81
|
-
try:
|
|
82
|
-
result = await run_research_agent(agent, query, deps)
|
|
83
|
-
# Display results
|
|
84
|
-
print("✅ Research Complete!")
|
|
85
|
-
print("📋 Findings:")
|
|
86
|
-
print(result.output)
|
|
87
|
-
except ErrorNotPickedUpBySentry as e:
|
|
88
|
-
# All user-actionable errors - display with plain text
|
|
89
|
-
print_agent_error(e)
|
|
90
|
-
except Exception as e:
|
|
91
|
-
# Unexpected errors that weren't wrapped (shouldn't happen)
|
|
92
|
-
logger.exception("Unexpected error in research command")
|
|
93
|
-
print(f"⚠️ An unexpected error occurred: {str(e)}")
|
shotgun/cli/specify.py
DELETED
|
@@ -1,70 +0,0 @@
|
|
|
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.cli.error_handler import print_agent_error
|
|
15
|
-
from shotgun.exceptions import ErrorNotPickedUpBySentry
|
|
16
|
-
from shotgun.logging_config import get_logger
|
|
17
|
-
|
|
18
|
-
app = typer.Typer(
|
|
19
|
-
name="specify", help="Generate comprehensive specifications", no_args_is_help=True
|
|
20
|
-
)
|
|
21
|
-
logger = get_logger(__name__)
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
@app.callback(invoke_without_command=True)
|
|
25
|
-
def specify(
|
|
26
|
-
requirement: Annotated[
|
|
27
|
-
str, typer.Argument(help="Requirement or feature to specify")
|
|
28
|
-
],
|
|
29
|
-
non_interactive: Annotated[
|
|
30
|
-
bool,
|
|
31
|
-
typer.Option(
|
|
32
|
-
"--non-interactive", "-n", help="Disable user interaction (for CI/CD)"
|
|
33
|
-
),
|
|
34
|
-
] = False,
|
|
35
|
-
provider: Annotated[
|
|
36
|
-
ProviderType | None,
|
|
37
|
-
typer.Option("--provider", "-p", help="AI provider to use (overrides default)"),
|
|
38
|
-
] = None,
|
|
39
|
-
) -> None:
|
|
40
|
-
"""Generate comprehensive specifications for software features and systems.
|
|
41
|
-
|
|
42
|
-
This command creates detailed technical specifications including requirements,
|
|
43
|
-
architecture, implementation details, and acceptance criteria based on your
|
|
44
|
-
provided requirement or feature description.
|
|
45
|
-
"""
|
|
46
|
-
|
|
47
|
-
logger.info("📝 Specification Requirement: %s", requirement)
|
|
48
|
-
|
|
49
|
-
# Create agent dependencies
|
|
50
|
-
agent_runtime_options = AgentRuntimeOptions(interactive_mode=not non_interactive)
|
|
51
|
-
|
|
52
|
-
# Create the specify agent with deps and provider
|
|
53
|
-
agent, deps = asyncio.run(create_specify_agent(agent_runtime_options, provider))
|
|
54
|
-
|
|
55
|
-
# Start specification process with error handling
|
|
56
|
-
logger.info("📋 Starting specification generation...")
|
|
57
|
-
|
|
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)}")
|
|
69
|
-
|
|
70
|
-
asyncio.run(async_specify())
|
shotgun/cli/tasks.py
DELETED
|
@@ -1,78 +0,0 @@
|
|
|
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.cli.error_handler import print_agent_error
|
|
15
|
-
from shotgun.exceptions import ErrorNotPickedUpBySentry
|
|
16
|
-
from shotgun.logging_config import get_logger
|
|
17
|
-
from shotgun.posthog_telemetry import track_event
|
|
18
|
-
|
|
19
|
-
app = typer.Typer(name="tasks", help="Generate task lists with agentic approach")
|
|
20
|
-
logger = get_logger(__name__)
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
@app.callback(invoke_without_command=True)
|
|
24
|
-
def tasks(
|
|
25
|
-
instruction: Annotated[
|
|
26
|
-
str, typer.Argument(help="Task creation instruction or project description")
|
|
27
|
-
],
|
|
28
|
-
non_interactive: Annotated[
|
|
29
|
-
bool,
|
|
30
|
-
typer.Option(
|
|
31
|
-
"--non-interactive", "-n", help="Disable user interaction (for CI/CD)"
|
|
32
|
-
),
|
|
33
|
-
] = False,
|
|
34
|
-
provider: Annotated[
|
|
35
|
-
ProviderType | None,
|
|
36
|
-
typer.Option("--provider", "-p", help="AI provider to use (overrides default)"),
|
|
37
|
-
] = None,
|
|
38
|
-
) -> None:
|
|
39
|
-
"""Generate actionable task lists based on existing research and plans.
|
|
40
|
-
|
|
41
|
-
This command creates detailed task breakdowns using AI agents that analyze
|
|
42
|
-
your research and plans to generate prioritized, actionable tasks with
|
|
43
|
-
acceptance criteria and effort estimates.
|
|
44
|
-
"""
|
|
45
|
-
|
|
46
|
-
logger.info("📋 Task Creation Instruction: %s", instruction)
|
|
47
|
-
|
|
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())
|
shotgun/sentry_telemetry.py
DELETED
|
@@ -1,232 +0,0 @@
|
|
|
1
|
-
"""Sentry observability setup for Shotgun."""
|
|
2
|
-
|
|
3
|
-
from pathlib import Path
|
|
4
|
-
from typing import Any
|
|
5
|
-
|
|
6
|
-
from shotgun import __version__
|
|
7
|
-
from shotgun.logging_config import get_early_logger
|
|
8
|
-
from shotgun.settings import settings
|
|
9
|
-
|
|
10
|
-
# Use early logger to prevent automatic StreamHandler creation
|
|
11
|
-
logger = get_early_logger(__name__)
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
def _scrub_path(path: str) -> str:
|
|
15
|
-
"""Scrub sensitive information from file paths.
|
|
16
|
-
|
|
17
|
-
Removes home directory and current working directory prefixes to prevent
|
|
18
|
-
leaking usernames that might be part of the path.
|
|
19
|
-
|
|
20
|
-
Args:
|
|
21
|
-
path: The file path to scrub
|
|
22
|
-
|
|
23
|
-
Returns:
|
|
24
|
-
The scrubbed path with sensitive prefixes removed
|
|
25
|
-
"""
|
|
26
|
-
if not path:
|
|
27
|
-
return path
|
|
28
|
-
|
|
29
|
-
try:
|
|
30
|
-
# Get home and cwd as Path objects for comparison
|
|
31
|
-
home = Path.home()
|
|
32
|
-
cwd = Path.cwd()
|
|
33
|
-
|
|
34
|
-
# Convert path to Path object
|
|
35
|
-
path_obj = Path(path)
|
|
36
|
-
|
|
37
|
-
# Try to make path relative to cwd first (most common case)
|
|
38
|
-
try:
|
|
39
|
-
relative_to_cwd = path_obj.relative_to(cwd)
|
|
40
|
-
return str(relative_to_cwd)
|
|
41
|
-
except ValueError:
|
|
42
|
-
pass
|
|
43
|
-
|
|
44
|
-
# Try to replace home directory with ~
|
|
45
|
-
try:
|
|
46
|
-
relative_to_home = path_obj.relative_to(home)
|
|
47
|
-
return f"~/{relative_to_home}"
|
|
48
|
-
except ValueError:
|
|
49
|
-
pass
|
|
50
|
-
|
|
51
|
-
# If path is absolute but not under cwd or home, just return filename
|
|
52
|
-
if path_obj.is_absolute():
|
|
53
|
-
return path_obj.name
|
|
54
|
-
|
|
55
|
-
# Return as-is if already relative
|
|
56
|
-
return path
|
|
57
|
-
|
|
58
|
-
except Exception:
|
|
59
|
-
# If anything goes wrong, return the original path
|
|
60
|
-
# Better to leak a path than break error reporting
|
|
61
|
-
return path
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
def _scrub_sensitive_paths(event: dict[str, Any]) -> None:
|
|
65
|
-
"""Scrub sensitive paths from Sentry event data.
|
|
66
|
-
|
|
67
|
-
Modifies the event in-place to remove:
|
|
68
|
-
- Home directory paths (might contain usernames)
|
|
69
|
-
- Current working directory paths (might contain usernames)
|
|
70
|
-
- Server name/hostname
|
|
71
|
-
- Paths in sys.argv
|
|
72
|
-
|
|
73
|
-
Args:
|
|
74
|
-
event: The Sentry event dictionary to scrub
|
|
75
|
-
"""
|
|
76
|
-
extra = event.get("extra", {})
|
|
77
|
-
if "sys.argv" in extra:
|
|
78
|
-
argv = extra["sys.argv"]
|
|
79
|
-
if isinstance(argv, list):
|
|
80
|
-
extra["sys.argv"] = [
|
|
81
|
-
_scrub_path(arg) if isinstance(arg, str) else arg for arg in argv
|
|
82
|
-
]
|
|
83
|
-
|
|
84
|
-
# Scrub server name if present
|
|
85
|
-
if "server_name" in event:
|
|
86
|
-
event["server_name"] = ""
|
|
87
|
-
|
|
88
|
-
# Scrub contexts that might contain paths
|
|
89
|
-
if "contexts" in event:
|
|
90
|
-
contexts = event["contexts"]
|
|
91
|
-
# Remove runtime context if it has CWD
|
|
92
|
-
if "runtime" in contexts:
|
|
93
|
-
if "cwd" in contexts["runtime"]:
|
|
94
|
-
del contexts["runtime"]["cwd"]
|
|
95
|
-
# Scrub sys.argv to remove paths
|
|
96
|
-
if "sys.argv" in contexts["runtime"]:
|
|
97
|
-
argv = contexts["runtime"]["sys.argv"]
|
|
98
|
-
if isinstance(argv, list):
|
|
99
|
-
contexts["runtime"]["sys.argv"] = [
|
|
100
|
-
_scrub_path(arg) if isinstance(arg, str) else arg
|
|
101
|
-
for arg in argv
|
|
102
|
-
]
|
|
103
|
-
|
|
104
|
-
# Scrub exception stack traces
|
|
105
|
-
if "exception" in event and "values" in event["exception"]:
|
|
106
|
-
for exception in event["exception"]["values"]:
|
|
107
|
-
if "stacktrace" in exception and "frames" in exception["stacktrace"]:
|
|
108
|
-
for frame in exception["stacktrace"]["frames"]:
|
|
109
|
-
# Scrub file paths
|
|
110
|
-
if "abs_path" in frame:
|
|
111
|
-
frame["abs_path"] = _scrub_path(frame["abs_path"])
|
|
112
|
-
if "filename" in frame:
|
|
113
|
-
frame["filename"] = _scrub_path(frame["filename"])
|
|
114
|
-
|
|
115
|
-
# Scrub local variables that might contain paths
|
|
116
|
-
if "vars" in frame:
|
|
117
|
-
for var_name, var_value in frame["vars"].items():
|
|
118
|
-
if isinstance(var_value, str):
|
|
119
|
-
frame["vars"][var_name] = _scrub_path(var_value)
|
|
120
|
-
|
|
121
|
-
# Scrub breadcrumbs that might contain paths
|
|
122
|
-
if "breadcrumbs" in event and "values" in event["breadcrumbs"]:
|
|
123
|
-
for breadcrumb in event["breadcrumbs"]["values"]:
|
|
124
|
-
if "data" in breadcrumb:
|
|
125
|
-
for key, value in breadcrumb["data"].items():
|
|
126
|
-
if isinstance(value, str):
|
|
127
|
-
breadcrumb["data"][key] = _scrub_path(value)
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
def setup_sentry_observability() -> bool:
|
|
131
|
-
"""Set up Sentry observability for error tracking.
|
|
132
|
-
|
|
133
|
-
Returns:
|
|
134
|
-
True if Sentry was successfully set up, False otherwise
|
|
135
|
-
"""
|
|
136
|
-
try:
|
|
137
|
-
import sentry_sdk
|
|
138
|
-
|
|
139
|
-
# Check if Sentry is already initialized
|
|
140
|
-
if sentry_sdk.is_initialized():
|
|
141
|
-
logger.debug("Sentry is already initialized, skipping")
|
|
142
|
-
return True
|
|
143
|
-
|
|
144
|
-
# Get DSN from settings (handles build constants + env vars automatically)
|
|
145
|
-
dsn = settings.telemetry.sentry_dsn
|
|
146
|
-
|
|
147
|
-
if not dsn:
|
|
148
|
-
logger.debug("No Sentry DSN configured, skipping Sentry initialization")
|
|
149
|
-
return False
|
|
150
|
-
|
|
151
|
-
logger.debug("Using Sentry DSN from settings, proceeding with setup")
|
|
152
|
-
|
|
153
|
-
# Determine environment based on version
|
|
154
|
-
# Dev versions contain "dev", "rc", "alpha", "beta"
|
|
155
|
-
if any(marker in __version__ for marker in ["dev", "rc", "alpha", "beta"]):
|
|
156
|
-
environment = "development"
|
|
157
|
-
else:
|
|
158
|
-
environment = "production"
|
|
159
|
-
|
|
160
|
-
def before_send(event: Any, hint: dict[str, Any]) -> Any:
|
|
161
|
-
"""Filter out user-actionable errors and scrub sensitive paths.
|
|
162
|
-
|
|
163
|
-
User-actionable errors (like context size limits) are expected conditions
|
|
164
|
-
that users need to resolve, not bugs that need tracking.
|
|
165
|
-
|
|
166
|
-
Also scrubs sensitive information like usernames from file paths and
|
|
167
|
-
working directories to protect user privacy.
|
|
168
|
-
"""
|
|
169
|
-
|
|
170
|
-
log_record = hint.get("log_record")
|
|
171
|
-
if log_record:
|
|
172
|
-
# Scrub pathname using the helper function
|
|
173
|
-
log_record.pathname = _scrub_path(log_record.pathname)
|
|
174
|
-
|
|
175
|
-
# Scrub traceback text if it exists
|
|
176
|
-
if hasattr(log_record, "exc_text") and isinstance(
|
|
177
|
-
log_record.exc_text, str
|
|
178
|
-
):
|
|
179
|
-
# Replace home directory in traceback text
|
|
180
|
-
home = Path.home()
|
|
181
|
-
log_record.exc_text = log_record.exc_text.replace(str(home), "~")
|
|
182
|
-
|
|
183
|
-
if "exc_info" in hint:
|
|
184
|
-
_, exc_value, _ = hint["exc_info"]
|
|
185
|
-
from shotgun.exceptions import ErrorNotPickedUpBySentry
|
|
186
|
-
|
|
187
|
-
if isinstance(exc_value, ErrorNotPickedUpBySentry):
|
|
188
|
-
# Don't send to Sentry - this is user-actionable, not a bug
|
|
189
|
-
return None
|
|
190
|
-
|
|
191
|
-
# Scrub sensitive paths from the event
|
|
192
|
-
_scrub_sensitive_paths(event)
|
|
193
|
-
return event
|
|
194
|
-
|
|
195
|
-
# Initialize Sentry
|
|
196
|
-
sentry_sdk.init(
|
|
197
|
-
dsn=dsn,
|
|
198
|
-
release=f"shotgun-sh@{__version__}",
|
|
199
|
-
environment=environment,
|
|
200
|
-
send_default_pii=False, # Privacy-first: never send PII
|
|
201
|
-
server_name="", # Privacy: don't send hostname (may contain username)
|
|
202
|
-
traces_sample_rate=0.1 if environment == "production" else 1.0,
|
|
203
|
-
profiles_sample_rate=0.1 if environment == "production" else 1.0,
|
|
204
|
-
before_send=before_send,
|
|
205
|
-
)
|
|
206
|
-
|
|
207
|
-
# Set user context with anonymous shotgun instance ID from config
|
|
208
|
-
try:
|
|
209
|
-
import asyncio
|
|
210
|
-
|
|
211
|
-
from shotgun.agents.config import get_config_manager
|
|
212
|
-
|
|
213
|
-
config_manager = get_config_manager()
|
|
214
|
-
shotgun_instance_id = asyncio.run(config_manager.get_shotgun_instance_id())
|
|
215
|
-
sentry_sdk.set_user({"id": shotgun_instance_id})
|
|
216
|
-
logger.debug("Sentry user context set with anonymous ID")
|
|
217
|
-
except Exception as e:
|
|
218
|
-
logger.warning("Failed to set Sentry user context: %s", e)
|
|
219
|
-
|
|
220
|
-
logger.debug(
|
|
221
|
-
"Sentry observability configured successfully (environment: %s, version: %s)",
|
|
222
|
-
environment,
|
|
223
|
-
__version__,
|
|
224
|
-
)
|
|
225
|
-
return True
|
|
226
|
-
|
|
227
|
-
except ImportError as e:
|
|
228
|
-
logger.error("Sentry SDK not available: %s", e)
|
|
229
|
-
return False
|
|
230
|
-
except Exception as e:
|
|
231
|
-
logger.warning("Failed to setup Sentry observability: %s", e)
|
|
232
|
-
return False
|