shotgun-sh 0.3.1.dev1__tar.gz → 0.3.3.dev2__tar.gz
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_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/PKG-INFO +2 -2
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/pyproject.toml +2 -2
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/agents/agent_manager.py +116 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/agents/common.py +17 -71
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/agents/config/models.py +36 -27
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/agents/config/provider.py +0 -2
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/agents/conversation/history/file_content_deduplication.py +66 -43
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/agents/tools/file_management.py +4 -1
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/agents/tools/web_search/gemini.py +1 -3
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/prompts/agents/partials/common_agent_system_prompt.j2 +25 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/prompts/agents/plan.j2 +16 -0
- shotgun_sh-0.3.3.dev2/src/shotgun/prompts/agents/research.j2 +107 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/prompts/agents/specify.j2 +54 -1
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/prompts/agents/state/system_state.j2 +1 -8
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/prompts/agents/tasks.j2 +16 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/tui/screens/chat/chat_screen.py +25 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/tui/screens/model_picker.py +14 -7
- shotgun_sh-0.3.1.dev1/src/shotgun/prompts/agents/research.j2 +0 -66
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/.gitignore +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/LICENSE +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/README.md +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/hatch_build.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/__init__.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/agents/__init__.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/agents/config/README.md +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/agents/config/__init__.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/agents/config/constants.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/agents/config/manager.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/agents/config/streaming_test.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/agents/context_analyzer/__init__.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/agents/context_analyzer/analyzer.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/agents/context_analyzer/constants.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/agents/context_analyzer/formatter.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/agents/context_analyzer/models.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/agents/conversation/__init__.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/agents/conversation/filters.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/agents/conversation/history/__init__.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/agents/conversation/history/chunking.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/agents/conversation/history/compaction.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/agents/conversation/history/constants.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/agents/conversation/history/context_extraction.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/agents/conversation/history/history_building.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/agents/conversation/history/history_processors.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/agents/conversation/history/message_utils.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/agents/conversation/history/token_counting/__init__.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/agents/conversation/history/token_counting/anthropic.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/agents/conversation/history/token_counting/base.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/agents/conversation/history/token_counting/openai.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/agents/conversation/history/token_counting/sentencepiece_counter.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/agents/conversation/history/token_counting/tokenizer_cache.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/agents/conversation/history/token_counting/utils.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/agents/conversation/history/token_estimation.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/agents/conversation/manager.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/agents/conversation/models.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/agents/error/__init__.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/agents/error/models.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/agents/export.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/agents/llm.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/agents/messages.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/agents/models.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/agents/plan.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/agents/research.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/agents/runner.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/agents/specify.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/agents/tasks.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/agents/tools/__init__.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/agents/tools/codebase/__init__.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/agents/tools/codebase/codebase_shell.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/agents/tools/codebase/directory_lister.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/agents/tools/codebase/file_read.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/agents/tools/codebase/models.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/agents/tools/codebase/query_graph.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/agents/tools/codebase/retrieve_code.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/agents/tools/registry.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/agents/tools/web_search/__init__.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/agents/tools/web_search/anthropic.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/agents/tools/web_search/openai.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/agents/tools/web_search/utils.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/agents/usage_manager.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/api_endpoints.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/build_constants.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/cli/__init__.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/cli/clear.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/cli/codebase/__init__.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/cli/codebase/commands.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/cli/codebase/models.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/cli/compact.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/cli/config.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/cli/context.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/cli/error_handler.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/cli/export.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/cli/feedback.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/cli/models.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/cli/plan.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/cli/research.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/cli/spec/__init__.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/cli/spec/backup.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/cli/spec/commands.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/cli/spec/models.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/cli/spec/pull_service.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/cli/specify.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/cli/tasks.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/cli/update.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/cli/utils.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/codebase/__init__.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/codebase/core/__init__.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/codebase/core/change_detector.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/codebase/core/code_retrieval.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/codebase/core/cypher_models.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/codebase/core/ingestor.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/codebase/core/language_config.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/codebase/core/manager.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/codebase/core/nl_query.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/codebase/core/parser_loader.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/codebase/models.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/codebase/service.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/exceptions.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/llm_proxy/__init__.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/llm_proxy/client.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/llm_proxy/clients.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/llm_proxy/constants.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/llm_proxy/models.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/logging_config.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/main.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/posthog_telemetry.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/prompts/__init__.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/prompts/agents/__init__.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/prompts/agents/export.j2 +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/prompts/agents/partials/codebase_understanding.j2 +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/prompts/agents/partials/content_formatting.j2 +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/prompts/agents/partials/interactive_mode.j2 +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/prompts/agents/state/codebase/codebase_graphs_available.j2 +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/prompts/codebase/__init__.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/prompts/codebase/cypher_query_patterns.j2 +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/prompts/codebase/cypher_system.j2 +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/prompts/codebase/enhanced_query_context.j2 +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/prompts/codebase/partials/cypher_rules.j2 +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/prompts/codebase/partials/graph_schema.j2 +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/prompts/codebase/partials/temporal_context.j2 +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/prompts/history/__init__.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/prompts/history/chunk_summarization.j2 +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/prompts/history/combine_summaries.j2 +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/prompts/history/incremental_summarization.j2 +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/prompts/history/summarization.j2 +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/prompts/loader.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/prompts/tools/web_search.j2 +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/py.typed +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/sdk/__init__.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/sdk/codebase.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/sdk/exceptions.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/sdk/models.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/sdk/services.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/sentry_telemetry.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/settings.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/shotgun_web/__init__.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/shotgun_web/client.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/shotgun_web/constants.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/shotgun_web/exceptions.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/shotgun_web/models.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/shotgun_web/shared_specs/__init__.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/shotgun_web/shared_specs/file_scanner.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/shotgun_web/shared_specs/hasher.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/shotgun_web/shared_specs/models.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/shotgun_web/shared_specs/upload_pipeline.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/shotgun_web/shared_specs/utils.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/shotgun_web/specs_client.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/shotgun_web/supabase_client.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/telemetry.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/tui/__init__.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/tui/app.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/tui/commands/__init__.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/tui/components/context_indicator.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/tui/components/mode_indicator.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/tui/components/prompt_input.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/tui/components/spinner.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/tui/components/splash.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/tui/components/status_bar.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/tui/components/vertical_tail.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/tui/containers.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/tui/dependencies.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/tui/filtered_codebase_service.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/tui/layout.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/tui/protocols.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/tui/screens/chat/__init__.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/tui/screens/chat/chat.tcss +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/tui/screens/chat/codebase_index_prompt_screen.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/tui/screens/chat/codebase_index_selection.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/tui/screens/chat/help_text.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/tui/screens/chat/prompt_history.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/tui/screens/chat.tcss +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/tui/screens/chat_screen/__init__.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/tui/screens/chat_screen/command_providers.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/tui/screens/chat_screen/hint_message.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/tui/screens/chat_screen/history/__init__.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/tui/screens/chat_screen/history/agent_response.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/tui/screens/chat_screen/history/chat_history.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/tui/screens/chat_screen/history/formatters.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/tui/screens/chat_screen/history/partial_response.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/tui/screens/chat_screen/history/user_question.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/tui/screens/confirmation_dialog.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/tui/screens/directory_setup.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/tui/screens/feedback.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/tui/screens/github_issue.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/tui/screens/onboarding.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/tui/screens/pipx_migration.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/tui/screens/provider_config.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/tui/screens/shared_specs/__init__.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/tui/screens/shared_specs/create_spec_dialog.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/tui/screens/shared_specs/models.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/tui/screens/shared_specs/share_specs_dialog.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/tui/screens/shared_specs/upload_progress_screen.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/tui/screens/shotgun_auth.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/tui/screens/spec_pull.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/tui/screens/splash.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/tui/screens/welcome.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/tui/services/__init__.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/tui/services/conversation_service.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/tui/state/__init__.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/tui/state/processing_state.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/tui/styles.tcss +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/tui/utils/__init__.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/tui/utils/mode_progress.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/tui/widgets/__init__.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/tui/widgets/widget_coordinator.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/utils/__init__.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/utils/datetime_utils.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/utils/env_utils.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/utils/file_system_utils.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/utils/marketing.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/utils/source_detection.py +0 -0
- {shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/utils/update_checker.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: shotgun-sh
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.3.dev2
|
|
4
4
|
Summary: AI-powered research, planning, and task management CLI tool
|
|
5
5
|
Project-URL: Homepage, https://shotgun.sh/
|
|
6
6
|
Project-URL: Repository, https://github.com/shotgun-sh/shotgun
|
|
@@ -32,7 +32,7 @@ Requires-Dist: logfire>=2.0.0
|
|
|
32
32
|
Requires-Dist: openai>=1.0.0
|
|
33
33
|
Requires-Dist: packaging>=23.0
|
|
34
34
|
Requires-Dist: posthog>=3.0.0
|
|
35
|
-
Requires-Dist: pydantic-ai>=
|
|
35
|
+
Requires-Dist: pydantic-ai>=1.26.0
|
|
36
36
|
Requires-Dist: pydantic-settings>=2.0.0
|
|
37
37
|
Requires-Dist: pyperclip>=1.10.0
|
|
38
38
|
Requires-Dist: rich>=13.0.0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "shotgun-sh"
|
|
3
|
-
version = "0.3.
|
|
3
|
+
version = "0.3.3.dev2"
|
|
4
4
|
description = "AI-powered research, planning, and task management CLI tool"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = { text = "MIT" }
|
|
@@ -24,7 +24,7 @@ classifiers = [
|
|
|
24
24
|
dependencies = [
|
|
25
25
|
"typer>=0.12.0",
|
|
26
26
|
"rich>=13.0.0",
|
|
27
|
-
"pydantic-ai>=
|
|
27
|
+
"pydantic-ai>=1.26.0",
|
|
28
28
|
"pydantic-settings>=2.0.0",
|
|
29
29
|
"httpx>=0.27.0",
|
|
30
30
|
"jinja2>=3.1.0",
|
|
@@ -38,6 +38,7 @@ from pydantic_ai.messages import (
|
|
|
38
38
|
PartDeltaEvent,
|
|
39
39
|
PartStartEvent,
|
|
40
40
|
SystemPromptPart,
|
|
41
|
+
TextPartDelta,
|
|
41
42
|
ToolCallPart,
|
|
42
43
|
ToolCallPartDelta,
|
|
43
44
|
UserPromptPart,
|
|
@@ -174,6 +175,67 @@ class CompactionCompletedMessage(Message):
|
|
|
174
175
|
"""Event posted when conversation compaction completes."""
|
|
175
176
|
|
|
176
177
|
|
|
178
|
+
class ToolExecutionStartedMessage(Message):
|
|
179
|
+
"""Event posted when a tool starts executing.
|
|
180
|
+
|
|
181
|
+
This allows the UI to update the spinner text to provide feedback
|
|
182
|
+
during long-running tool executions.
|
|
183
|
+
"""
|
|
184
|
+
|
|
185
|
+
def __init__(self, spinner_text: str = "Processing...") -> None:
|
|
186
|
+
"""Initialize the tool execution started message.
|
|
187
|
+
|
|
188
|
+
Args:
|
|
189
|
+
spinner_text: The spinner message to display
|
|
190
|
+
"""
|
|
191
|
+
super().__init__()
|
|
192
|
+
self.spinner_text = spinner_text
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
class ToolStreamingProgressMessage(Message):
|
|
196
|
+
"""Event posted during tool call streaming to show progress.
|
|
197
|
+
|
|
198
|
+
This provides visual feedback while tool arguments are streaming,
|
|
199
|
+
especially useful for long-running writes like file content.
|
|
200
|
+
"""
|
|
201
|
+
|
|
202
|
+
def __init__(self, streamed_tokens: int, spinner_text: str) -> None:
|
|
203
|
+
"""Initialize the tool streaming progress message.
|
|
204
|
+
|
|
205
|
+
Args:
|
|
206
|
+
streamed_tokens: Approximate number of tokens streamed so far
|
|
207
|
+
spinner_text: The current spinner message to preserve
|
|
208
|
+
"""
|
|
209
|
+
super().__init__()
|
|
210
|
+
self.streamed_tokens = streamed_tokens
|
|
211
|
+
self.spinner_text = spinner_text
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
# Fun spinner messages to show during tool execution
|
|
215
|
+
SPINNER_MESSAGES = [
|
|
216
|
+
"Pontificating...",
|
|
217
|
+
"Ruminating...",
|
|
218
|
+
"Cogitating...",
|
|
219
|
+
"Deliberating...",
|
|
220
|
+
"Contemplating...",
|
|
221
|
+
"Reticulating splines...",
|
|
222
|
+
"Consulting the oracle...",
|
|
223
|
+
"Gathering thoughts...",
|
|
224
|
+
"Processing neurons...",
|
|
225
|
+
"Summoning wisdom...",
|
|
226
|
+
"Brewing ideas...",
|
|
227
|
+
"Polishing pixels...",
|
|
228
|
+
"Herding electrons...",
|
|
229
|
+
"Warming up the flux capacitor...",
|
|
230
|
+
"Consulting ancient tomes...",
|
|
231
|
+
"Channeling the muses...",
|
|
232
|
+
"Percolating possibilities...",
|
|
233
|
+
"Untangling complexity...",
|
|
234
|
+
"Shuffling priorities...",
|
|
235
|
+
"Aligning the stars...",
|
|
236
|
+
]
|
|
237
|
+
|
|
238
|
+
|
|
177
239
|
class AgentStreamingStarted(Message):
|
|
178
240
|
"""Event posted when agent starts streaming responses."""
|
|
179
241
|
|
|
@@ -210,6 +272,11 @@ class _PartialStreamState:
|
|
|
210
272
|
|
|
211
273
|
messages: list[ModelRequest | ModelResponse] = field(default_factory=list)
|
|
212
274
|
current_response: ModelResponse | None = None
|
|
275
|
+
# Token counting for tool call streaming progress
|
|
276
|
+
streamed_tokens: int = 0
|
|
277
|
+
current_spinner_text: str = "Processing..."
|
|
278
|
+
# Track last reported tokens to throttle UI updates
|
|
279
|
+
last_reported_tokens: int = 0
|
|
213
280
|
|
|
214
281
|
|
|
215
282
|
class AgentManager(Widget):
|
|
@@ -992,6 +1059,44 @@ class AgentManager(Widget):
|
|
|
992
1059
|
)
|
|
993
1060
|
continue
|
|
994
1061
|
|
|
1062
|
+
# Count tokens from the delta for progress indication
|
|
1063
|
+
delta_len = 0
|
|
1064
|
+
is_tool_call_delta = False
|
|
1065
|
+
if isinstance(event.delta, ToolCallPartDelta):
|
|
1066
|
+
is_tool_call_delta = True
|
|
1067
|
+
# args_delta can be str or dict depending on provider
|
|
1068
|
+
args_delta = event.delta.args_delta
|
|
1069
|
+
if isinstance(args_delta, str):
|
|
1070
|
+
delta_len = len(args_delta)
|
|
1071
|
+
elif isinstance(args_delta, dict):
|
|
1072
|
+
# For dict deltas, estimate from JSON representation
|
|
1073
|
+
delta_len = len(json.dumps(args_delta))
|
|
1074
|
+
# Pick a spinner message when tool streaming starts
|
|
1075
|
+
if state.current_spinner_text == "Processing...":
|
|
1076
|
+
import random
|
|
1077
|
+
|
|
1078
|
+
state.current_spinner_text = random.choice( # noqa: S311
|
|
1079
|
+
SPINNER_MESSAGES
|
|
1080
|
+
)
|
|
1081
|
+
elif isinstance(event.delta, TextPartDelta):
|
|
1082
|
+
delta_len = len(event.delta.content_delta)
|
|
1083
|
+
|
|
1084
|
+
if delta_len > 0:
|
|
1085
|
+
# Approximate tokens: len / 4 is a rough estimate
|
|
1086
|
+
state.streamed_tokens += delta_len // 4 + 1
|
|
1087
|
+
# Send progress update for tool call streaming
|
|
1088
|
+
# Throttle updates to every ~75 tokens to avoid flooding UI
|
|
1089
|
+
if is_tool_call_delta and (
|
|
1090
|
+
state.streamed_tokens - state.last_reported_tokens >= 75
|
|
1091
|
+
):
|
|
1092
|
+
state.last_reported_tokens = state.streamed_tokens
|
|
1093
|
+
self.post_message(
|
|
1094
|
+
ToolStreamingProgressMessage(
|
|
1095
|
+
state.streamed_tokens,
|
|
1096
|
+
state.current_spinner_text,
|
|
1097
|
+
)
|
|
1098
|
+
)
|
|
1099
|
+
|
|
995
1100
|
try:
|
|
996
1101
|
updated_part = event.delta.apply(
|
|
997
1102
|
cast(ModelResponsePart, partial_parts[index])
|
|
@@ -1087,6 +1192,17 @@ class AgentManager(Widget):
|
|
|
1087
1192
|
if partial_message is not None:
|
|
1088
1193
|
state.current_response = partial_message
|
|
1089
1194
|
self._post_partial_message(False)
|
|
1195
|
+
|
|
1196
|
+
# Notify UI that a tool is about to execute
|
|
1197
|
+
# This updates the spinner with a fun message during tool execution
|
|
1198
|
+
# Pick a random spinner message and store it for progress updates
|
|
1199
|
+
import random
|
|
1200
|
+
|
|
1201
|
+
spinner_text = random.choice(SPINNER_MESSAGES) # noqa: S311
|
|
1202
|
+
state.current_spinner_text = spinner_text
|
|
1203
|
+
state.streamed_tokens = 0 # Reset token count for new tool
|
|
1204
|
+
self.post_message(ToolExecutionStartedMessage(spinner_text))
|
|
1205
|
+
|
|
1090
1206
|
elif isinstance(event, FunctionToolResultEvent):
|
|
1091
1207
|
# Track tool completion event
|
|
1092
1208
|
|
|
@@ -38,7 +38,6 @@ from .tools import (
|
|
|
38
38
|
retrieve_code,
|
|
39
39
|
write_file,
|
|
40
40
|
)
|
|
41
|
-
from .tools.file_management import AGENT_DIRECTORIES
|
|
42
41
|
|
|
43
42
|
logger = get_logger(__name__)
|
|
44
43
|
|
|
@@ -352,86 +351,33 @@ async def extract_markdown_toc(agent_mode: AgentType | None) -> str | None:
|
|
|
352
351
|
|
|
353
352
|
|
|
354
353
|
def get_agent_existing_files(agent_mode: AgentType | None = None) -> list[str]:
|
|
355
|
-
"""Get list of existing files
|
|
354
|
+
"""Get list of all existing files in .shotgun directory.
|
|
355
|
+
|
|
356
|
+
All agents can read any file in .shotgun/, so we list all files regardless
|
|
357
|
+
of agent mode. This includes user-added files that agents should be aware of.
|
|
356
358
|
|
|
357
359
|
Args:
|
|
358
|
-
agent_mode:
|
|
360
|
+
agent_mode: Unused, kept for backwards compatibility.
|
|
359
361
|
|
|
360
362
|
Returns:
|
|
361
363
|
List of existing file paths relative to .shotgun directory
|
|
362
364
|
"""
|
|
363
365
|
base_path = get_shotgun_base_path()
|
|
364
|
-
existing_files = []
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
if agent_mode is None:
|
|
368
|
-
# List files in the root .shotgun directory
|
|
369
|
-
for item in base_path.iterdir():
|
|
370
|
-
if item.is_file():
|
|
371
|
-
existing_files.append(item.name)
|
|
372
|
-
elif item.is_dir():
|
|
373
|
-
# List files in first-level subdirectories
|
|
374
|
-
for subitem in item.iterdir():
|
|
375
|
-
if subitem.is_file():
|
|
376
|
-
relative_path = subitem.relative_to(base_path)
|
|
377
|
-
existing_files.append(str(relative_path))
|
|
366
|
+
existing_files: list[str] = []
|
|
367
|
+
|
|
368
|
+
if not base_path.exists():
|
|
378
369
|
return existing_files
|
|
379
370
|
|
|
380
|
-
#
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
if file_path.is_file():
|
|
390
|
-
relative_path = file_path.relative_to(base_path)
|
|
371
|
+
# List all files in .shotgun directory and subdirectories
|
|
372
|
+
for item in base_path.iterdir():
|
|
373
|
+
if item.is_file():
|
|
374
|
+
existing_files.append(item.name)
|
|
375
|
+
elif item.is_dir():
|
|
376
|
+
# List files in subdirectories (one level deep to avoid too much noise)
|
|
377
|
+
for subitem in item.iterdir():
|
|
378
|
+
if subitem.is_file():
|
|
379
|
+
relative_path = subitem.relative_to(base_path)
|
|
391
380
|
existing_files.append(str(relative_path))
|
|
392
|
-
else:
|
|
393
|
-
# For other agents, check files/directories they have access to
|
|
394
|
-
allowed_paths_raw = AGENT_DIRECTORIES[agent_mode]
|
|
395
|
-
|
|
396
|
-
# Convert single Path/string to list of Paths for uniform handling
|
|
397
|
-
if isinstance(allowed_paths_raw, str):
|
|
398
|
-
# Special case: "*" means export agent (shouldn't reach here but handle it)
|
|
399
|
-
allowed_paths = (
|
|
400
|
-
[Path(allowed_paths_raw)] if allowed_paths_raw != "*" else []
|
|
401
|
-
)
|
|
402
|
-
elif isinstance(allowed_paths_raw, Path):
|
|
403
|
-
allowed_paths = [allowed_paths_raw]
|
|
404
|
-
else:
|
|
405
|
-
# Already a list
|
|
406
|
-
allowed_paths = allowed_paths_raw
|
|
407
|
-
|
|
408
|
-
# Check each allowed path
|
|
409
|
-
for allowed_path in allowed_paths:
|
|
410
|
-
allowed_str = str(allowed_path)
|
|
411
|
-
|
|
412
|
-
# Check if it's a directory (no .md suffix)
|
|
413
|
-
if not allowed_path.suffix or not allowed_str.endswith(".md"):
|
|
414
|
-
# It's a directory - list all files within it
|
|
415
|
-
dir_path = base_path / allowed_str
|
|
416
|
-
if dir_path.exists() and dir_path.is_dir():
|
|
417
|
-
for file_path in dir_path.rglob("*"):
|
|
418
|
-
if file_path.is_file():
|
|
419
|
-
relative_path = file_path.relative_to(base_path)
|
|
420
|
-
existing_files.append(str(relative_path))
|
|
421
|
-
else:
|
|
422
|
-
# It's a file - check if it exists
|
|
423
|
-
file_path = base_path / allowed_str
|
|
424
|
-
if file_path.exists():
|
|
425
|
-
existing_files.append(allowed_str)
|
|
426
|
-
|
|
427
|
-
# Also check for associated directory (e.g., research/ for research.md)
|
|
428
|
-
base_name = allowed_str.replace(".md", "")
|
|
429
|
-
dir_path = base_path / base_name
|
|
430
|
-
if dir_path.exists() and dir_path.is_dir():
|
|
431
|
-
for file_path in dir_path.rglob("*"):
|
|
432
|
-
if file_path.is_file():
|
|
433
|
-
relative_path = file_path.relative_to(base_path)
|
|
434
|
-
existing_files.append(str(relative_path))
|
|
435
381
|
|
|
436
382
|
return existing_files
|
|
437
383
|
|
|
@@ -25,16 +25,17 @@ class KeyProvider(StrEnum):
|
|
|
25
25
|
class ModelName(StrEnum):
|
|
26
26
|
"""Available AI model names."""
|
|
27
27
|
|
|
28
|
-
GPT_5 = "gpt-5"
|
|
29
|
-
GPT_5_MINI = "gpt-5-mini"
|
|
30
28
|
GPT_5_1 = "gpt-5.1"
|
|
31
29
|
GPT_5_1_CODEX = "gpt-5.1-codex"
|
|
32
30
|
GPT_5_1_CODEX_MINI = "gpt-5.1-codex-mini"
|
|
33
|
-
|
|
31
|
+
CLAUDE_OPUS_4_5 = "claude-opus-4-5"
|
|
32
|
+
CLAUDE_SONNET_4 = "claude-sonnet-4"
|
|
34
33
|
CLAUDE_SONNET_4_5 = "claude-sonnet-4-5"
|
|
35
34
|
CLAUDE_HAIKU_4_5 = "claude-haiku-4-5"
|
|
36
35
|
GEMINI_2_5_PRO = "gemini-2.5-pro"
|
|
37
36
|
GEMINI_2_5_FLASH = "gemini-2.5-flash"
|
|
37
|
+
GEMINI_2_5_FLASH_LITE = "gemini-2.5-flash-lite"
|
|
38
|
+
GEMINI_3_PRO_PREVIEW = "gemini-3-pro-preview"
|
|
38
39
|
|
|
39
40
|
|
|
40
41
|
class ModelSpec(BaseModel):
|
|
@@ -101,22 +102,6 @@ class ModelConfig(BaseModel):
|
|
|
101
102
|
|
|
102
103
|
# Model specifications registry (static metadata)
|
|
103
104
|
MODEL_SPECS: dict[ModelName, ModelSpec] = {
|
|
104
|
-
ModelName.GPT_5: ModelSpec(
|
|
105
|
-
name=ModelName.GPT_5,
|
|
106
|
-
provider=ProviderType.OPENAI,
|
|
107
|
-
max_input_tokens=400_000,
|
|
108
|
-
max_output_tokens=128_000,
|
|
109
|
-
litellm_proxy_model_name="openai/gpt-5",
|
|
110
|
-
short_name="GPT-5",
|
|
111
|
-
),
|
|
112
|
-
ModelName.GPT_5_MINI: ModelSpec(
|
|
113
|
-
name=ModelName.GPT_5_MINI,
|
|
114
|
-
provider=ProviderType.OPENAI,
|
|
115
|
-
max_input_tokens=400_000,
|
|
116
|
-
max_output_tokens=128_000,
|
|
117
|
-
litellm_proxy_model_name="openai/gpt-5-mini",
|
|
118
|
-
short_name="GPT-5 Mini",
|
|
119
|
-
),
|
|
120
105
|
ModelName.GPT_5_1: ModelSpec(
|
|
121
106
|
name=ModelName.GPT_5_1,
|
|
122
107
|
provider=ProviderType.OPENAI,
|
|
@@ -141,14 +126,6 @@ MODEL_SPECS: dict[ModelName, ModelSpec] = {
|
|
|
141
126
|
litellm_proxy_model_name="openai/gpt-5.1-codex-mini",
|
|
142
127
|
short_name="GPT-5.1 Codex Mini",
|
|
143
128
|
),
|
|
144
|
-
ModelName.CLAUDE_OPUS_4_1: ModelSpec(
|
|
145
|
-
name=ModelName.CLAUDE_OPUS_4_1,
|
|
146
|
-
provider=ProviderType.ANTHROPIC,
|
|
147
|
-
max_input_tokens=200_000,
|
|
148
|
-
max_output_tokens=32_000,
|
|
149
|
-
litellm_proxy_model_name="anthropic/claude-opus-4-1",
|
|
150
|
-
short_name="Opus 4.1",
|
|
151
|
-
),
|
|
152
129
|
ModelName.CLAUDE_SONNET_4_5: ModelSpec(
|
|
153
130
|
name=ModelName.CLAUDE_SONNET_4_5,
|
|
154
131
|
provider=ProviderType.ANTHROPIC,
|
|
@@ -181,6 +158,38 @@ MODEL_SPECS: dict[ModelName, ModelSpec] = {
|
|
|
181
158
|
litellm_proxy_model_name="gemini/gemini-2.5-flash",
|
|
182
159
|
short_name="Gemini 2.5 Flash",
|
|
183
160
|
),
|
|
161
|
+
ModelName.CLAUDE_OPUS_4_5: ModelSpec(
|
|
162
|
+
name=ModelName.CLAUDE_OPUS_4_5,
|
|
163
|
+
provider=ProviderType.ANTHROPIC,
|
|
164
|
+
max_input_tokens=200_000,
|
|
165
|
+
max_output_tokens=64_000,
|
|
166
|
+
litellm_proxy_model_name="anthropic/claude-opus-4-5",
|
|
167
|
+
short_name="Opus 4.5",
|
|
168
|
+
),
|
|
169
|
+
ModelName.CLAUDE_SONNET_4: ModelSpec(
|
|
170
|
+
name=ModelName.CLAUDE_SONNET_4,
|
|
171
|
+
provider=ProviderType.ANTHROPIC,
|
|
172
|
+
max_input_tokens=200_000,
|
|
173
|
+
max_output_tokens=64_000,
|
|
174
|
+
litellm_proxy_model_name="anthropic/claude-sonnet-4",
|
|
175
|
+
short_name="Sonnet 4",
|
|
176
|
+
),
|
|
177
|
+
ModelName.GEMINI_2_5_FLASH_LITE: ModelSpec(
|
|
178
|
+
name=ModelName.GEMINI_2_5_FLASH_LITE,
|
|
179
|
+
provider=ProviderType.GOOGLE,
|
|
180
|
+
max_input_tokens=1_048_576,
|
|
181
|
+
max_output_tokens=65_536,
|
|
182
|
+
litellm_proxy_model_name="gemini/gemini-2.5-flash-lite",
|
|
183
|
+
short_name="Gemini 2.5 Flash Lite",
|
|
184
|
+
),
|
|
185
|
+
ModelName.GEMINI_3_PRO_PREVIEW: ModelSpec(
|
|
186
|
+
name=ModelName.GEMINI_3_PRO_PREVIEW,
|
|
187
|
+
provider=ProviderType.GOOGLE,
|
|
188
|
+
max_input_tokens=1_048_576,
|
|
189
|
+
max_output_tokens=65_536,
|
|
190
|
+
litellm_proxy_model_name="gemini/gemini-3-pro-preview",
|
|
191
|
+
short_name="Gemini 3 Pro",
|
|
192
|
+
),
|
|
184
193
|
}
|
|
185
194
|
|
|
186
195
|
|
|
@@ -271,8 +271,6 @@ async def get_provider_model(
|
|
|
271
271
|
# Check and test streaming capability for GPT-5 family models
|
|
272
272
|
supports_streaming = True # Default to True for all models
|
|
273
273
|
if model_name in (
|
|
274
|
-
ModelName.GPT_5,
|
|
275
|
-
ModelName.GPT_5_MINI,
|
|
276
274
|
ModelName.GPT_5_1,
|
|
277
275
|
ModelName.GPT_5_1_CODEX,
|
|
278
276
|
ModelName.GPT_5_1_CODEX_MINI,
|
|
@@ -5,8 +5,6 @@ tool returns before LLM-based compaction. Files are still accessible via
|
|
|
5
5
|
`retrieve_code` (codebase) or `read_file` (.shotgun/ folder).
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
-
import copy
|
|
9
|
-
import re
|
|
10
8
|
from enum import StrEnum
|
|
11
9
|
from typing import Any
|
|
12
10
|
|
|
@@ -43,40 +41,46 @@ SHOTGUN_PLACEHOLDER = (
|
|
|
43
41
|
"**Content**: [Removed for compaction - file persisted in .shotgun/ folder]"
|
|
44
42
|
)
|
|
45
43
|
|
|
46
|
-
#
|
|
47
|
-
#
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
r"\*\*Size\*\*:\s*(\d+)\s*bytes\s*\n" # Size in bytes
|
|
51
|
-
r"(?:\*\*Encoding\*\*:.*?\n)?" # Optional encoding line
|
|
52
|
-
r"\n\*\*Content\*\*:\s*\n" # Blank line + Content header
|
|
53
|
-
r"```(\w*)\n" # Language tag
|
|
54
|
-
r"(.*?)```", # Actual content
|
|
55
|
-
re.DOTALL,
|
|
56
|
-
)
|
|
44
|
+
# Simple prefix for detecting file_read output format
|
|
45
|
+
# Instead of using regex, we just check for the expected prefix and extract the file path
|
|
46
|
+
CODEBASE_FILE_PREFIX = "**File**: `"
|
|
47
|
+
|
|
57
48
|
|
|
49
|
+
def _extract_file_path(content: str) -> str | None:
|
|
50
|
+
"""Extract file path from file_read tool return content.
|
|
58
51
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
) -> tuple[str, int, str, str] | None:
|
|
62
|
-
"""Parse file_read tool return content.
|
|
52
|
+
Uses simple string operations instead of regex for maximum performance.
|
|
53
|
+
The file_read tool output format is: **File**: `path`\\n...
|
|
63
54
|
|
|
64
55
|
Args:
|
|
65
56
|
content: The tool return content string
|
|
66
57
|
|
|
67
58
|
Returns:
|
|
68
|
-
|
|
59
|
+
The file path or None if format doesn't match
|
|
69
60
|
"""
|
|
70
|
-
|
|
71
|
-
if not
|
|
61
|
+
# Fast check: content must start with expected prefix
|
|
62
|
+
if not content.startswith(CODEBASE_FILE_PREFIX):
|
|
63
|
+
return None
|
|
64
|
+
|
|
65
|
+
# Find the closing backtick after the prefix
|
|
66
|
+
prefix_len = len(CODEBASE_FILE_PREFIX)
|
|
67
|
+
backtick_pos = content.find("`", prefix_len)
|
|
68
|
+
|
|
69
|
+
if backtick_pos == -1:
|
|
72
70
|
return None
|
|
73
71
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
72
|
+
return content[prefix_len:backtick_pos]
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _get_language_from_path(file_path: str) -> str:
|
|
76
|
+
"""Infer programming language from file extension."""
|
|
77
|
+
from pathlib import Path
|
|
78
|
+
|
|
79
|
+
from shotgun.codebase.core.language_config import get_language_config
|
|
78
80
|
|
|
79
|
-
|
|
81
|
+
ext = Path(file_path).suffix
|
|
82
|
+
config = get_language_config(ext)
|
|
83
|
+
return config.name if config else "unknown"
|
|
80
84
|
|
|
81
85
|
|
|
82
86
|
def _create_codebase_placeholder(file_path: str, size_bytes: int, language: str) -> str:
|
|
@@ -110,6 +114,11 @@ def deduplicate_file_content(
|
|
|
110
114
|
This is a deterministic pre-compaction pass that reduces tokens without
|
|
111
115
|
requiring an LLM. Files remain accessible via their respective tools.
|
|
112
116
|
|
|
117
|
+
This function uses copy-on-write semantics: only messages that need
|
|
118
|
+
modification are copied, while unmodified messages are reused by reference.
|
|
119
|
+
This significantly reduces memory allocation and processing time for large
|
|
120
|
+
conversations where only a subset of messages contain file content.
|
|
121
|
+
|
|
113
122
|
Args:
|
|
114
123
|
messages: Conversation history
|
|
115
124
|
retention_window: Keep full content in last N messages (for recent context)
|
|
@@ -120,15 +129,17 @@ def deduplicate_file_content(
|
|
|
120
129
|
if not messages:
|
|
121
130
|
return messages, 0
|
|
122
131
|
|
|
123
|
-
# Deep copy to avoid modifying original
|
|
124
|
-
modified_messages = copy.deepcopy(messages)
|
|
125
132
|
total_tokens_saved = 0
|
|
126
133
|
files_deduplicated = 0
|
|
127
134
|
|
|
128
135
|
# Calculate retention boundary (keep last N messages intact)
|
|
129
|
-
retention_start = max(0, len(
|
|
136
|
+
retention_start = max(0, len(messages) - retention_window)
|
|
137
|
+
|
|
138
|
+
# Track which message indices need replacement
|
|
139
|
+
# We use a dict to store index -> new_message mappings
|
|
140
|
+
replacements: dict[int, ModelMessage] = {}
|
|
130
141
|
|
|
131
|
-
for msg_idx, message in enumerate(
|
|
142
|
+
for msg_idx, message in enumerate(messages):
|
|
132
143
|
# Skip messages in retention window
|
|
133
144
|
if msg_idx >= retention_start:
|
|
134
145
|
continue
|
|
@@ -159,18 +170,18 @@ def deduplicate_file_content(
|
|
|
159
170
|
|
|
160
171
|
# Handle codebase file reads (file_read)
|
|
161
172
|
if tool_name == FileReadTool.CODEBASE:
|
|
162
|
-
|
|
163
|
-
if
|
|
164
|
-
|
|
165
|
-
#
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
173
|
+
file_path = _extract_file_path(content)
|
|
174
|
+
if file_path:
|
|
175
|
+
# Use content length as size estimate (includes formatting overhead
|
|
176
|
+
# but close enough for deduplication purposes)
|
|
177
|
+
size_bytes = len(content)
|
|
178
|
+
language = _get_language_from_path(file_path)
|
|
179
|
+
replacement = _create_codebase_placeholder(
|
|
180
|
+
file_path, size_bytes, language
|
|
181
|
+
)
|
|
182
|
+
logger.debug(
|
|
183
|
+
f"Deduplicating codebase file: {file_path} ({size_bytes} bytes)"
|
|
184
|
+
)
|
|
174
185
|
|
|
175
186
|
# Handle .shotgun/ file reads (read_file)
|
|
176
187
|
elif tool_name == FileReadTool.SHOTGUN_FOLDER:
|
|
@@ -203,9 +214,21 @@ def deduplicate_file_content(
|
|
|
203
214
|
else:
|
|
204
215
|
new_parts.append(part)
|
|
205
216
|
|
|
206
|
-
#
|
|
217
|
+
# Only create a new message if parts were actually modified
|
|
207
218
|
if message_modified:
|
|
208
|
-
|
|
219
|
+
replacements[msg_idx] = ModelRequest(parts=new_parts)
|
|
220
|
+
|
|
221
|
+
# If no modifications were made, return original list (no allocation needed)
|
|
222
|
+
if not replacements:
|
|
223
|
+
return messages, 0
|
|
224
|
+
|
|
225
|
+
# Build result list with copy-on-write: reuse unmodified messages
|
|
226
|
+
modified_messages: list[ModelMessage] = []
|
|
227
|
+
for idx, msg in enumerate(messages):
|
|
228
|
+
if idx in replacements:
|
|
229
|
+
modified_messages.append(replacements[idx])
|
|
230
|
+
else:
|
|
231
|
+
modified_messages.append(msg)
|
|
209
232
|
|
|
210
233
|
if files_deduplicated > 0:
|
|
211
234
|
logger.info(
|
|
@@ -23,7 +23,10 @@ logger = get_logger(__name__)
|
|
|
23
23
|
# - A list of Paths: multiple allowed files/directories (e.g., [Path("specification.md"), Path("contracts")])
|
|
24
24
|
# - "*": any file except protected files (for export agent)
|
|
25
25
|
AGENT_DIRECTORIES: dict[AgentType, str | Path | list[Path]] = {
|
|
26
|
-
AgentType.RESEARCH:
|
|
26
|
+
AgentType.RESEARCH: [
|
|
27
|
+
Path("research.md"),
|
|
28
|
+
Path("research"),
|
|
29
|
+
], # Research can write main file and research folder
|
|
27
30
|
AgentType.SPECIFY: [
|
|
28
31
|
Path("specification.md"),
|
|
29
32
|
Path("contracts"),
|
{shotgun_sh-0.3.1.dev1 → shotgun_sh-0.3.3.dev2}/src/shotgun/agents/tools/web_search/gemini.py
RENAMED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""Gemini web search tool implementation."""
|
|
2
2
|
|
|
3
3
|
from opentelemetry import trace
|
|
4
|
-
from pydantic_ai.messages import ModelMessage, ModelRequest
|
|
4
|
+
from pydantic_ai.messages import ModelMessage, ModelRequest, TextPart
|
|
5
5
|
from pydantic_ai.settings import ModelSettings
|
|
6
6
|
|
|
7
7
|
from shotgun.agents.config import get_provider_model
|
|
@@ -82,8 +82,6 @@ async def gemini_web_search_tool(query: str) -> str:
|
|
|
82
82
|
)
|
|
83
83
|
|
|
84
84
|
# Extract text from response
|
|
85
|
-
from pydantic_ai.messages import TextPart
|
|
86
|
-
|
|
87
85
|
result_text = "No content returned from search"
|
|
88
86
|
if response.parts:
|
|
89
87
|
for part in response.parts:
|
|
@@ -4,6 +4,31 @@ Your extensive expertise spans, among other things:
|
|
|
4
4
|
* Software Architecture
|
|
5
5
|
* Software Development
|
|
6
6
|
|
|
7
|
+
## YOUR ROLE IN THE PIPELINE
|
|
8
|
+
|
|
9
|
+
**CRITICAL**: You are a DOCUMENTATION and PLANNING agent, NOT a coding/implementation agent.
|
|
10
|
+
|
|
11
|
+
- You produce DOCUMENTS (research, specifications, plans, tasks) that AI coding agents will consume
|
|
12
|
+
- You do NOT write production code, implement features, or make code changes
|
|
13
|
+
- NEVER offer to "move forward with implementation" or "start coding" - that's not your job
|
|
14
|
+
- NEVER ask "would you like me to implement this?" - implementation is done by separate AI coding tools
|
|
15
|
+
- Your deliverable is always a document file (.md), not code execution
|
|
16
|
+
- When your work is complete, the user will take your documents to a coding agent (Claude Code, Cursor, etc.)
|
|
17
|
+
|
|
18
|
+
## AGENT FILE PERMISSIONS
|
|
19
|
+
|
|
20
|
+
There are four agents in the pipeline, and each agent can ONLY write to specific files. The user can switch between agents using **Shift+Tab**.
|
|
21
|
+
|
|
22
|
+
The **Research agent** can only write to `research.md` and files inside the `.shotgun/research/` directory. If the user asks about specifications, plans, or tasks, tell them: "Use **Shift+Tab** to switch to the [appropriate] agent which can edit that file for you."
|
|
23
|
+
|
|
24
|
+
The **Specification agent** can only write to `specification.md` and files inside the `.shotgun/contracts/` directory. If the user asks about research, plans, or tasks, tell them which agent handles that file.
|
|
25
|
+
|
|
26
|
+
The **Plan agent** can only write to `plan.md`. If the user asks about research, specifications, or tasks, tell them which agent handles that file.
|
|
27
|
+
|
|
28
|
+
The **Tasks agent** can only write to `tasks.md`. If the user asks about research, specifications, or plans, tell them which agent handles that file.
|
|
29
|
+
|
|
30
|
+
When a user asks you to edit a file you cannot write to, you MUST tell them which agent can help and how to switch: "I can't edit [filename] - that's handled by the [agent name] agent. Use **Shift+Tab** to switch to that agent and it can edit that file for you."
|
|
31
|
+
|
|
7
32
|
## KEY RULES
|
|
8
33
|
|
|
9
34
|
{% if interactive_mode %}
|
|
@@ -6,6 +6,22 @@ Your job is to help create comprehensive, actionable plans for software projects
|
|
|
6
6
|
|
|
7
7
|
{% include 'agents/partials/common_agent_system_prompt.j2' %}
|
|
8
8
|
|
|
9
|
+
## YOUR SCOPE AND HANDOFFS
|
|
10
|
+
|
|
11
|
+
You are the **Plan agent**. Your file is `plan.md` - this is the ONLY file you can write to.
|
|
12
|
+
|
|
13
|
+
When your plan is complete, suggest the next step:
|
|
14
|
+
"I've completed the plan. Use **Shift+Tab** to switch to the tasks agent to break this plan into actionable tasks."
|
|
15
|
+
|
|
16
|
+
If the user asks you to edit other files, redirect them helpfully:
|
|
17
|
+
- For research.md: "I can't edit research.md - that's handled by the research agent. Use **Shift+Tab** to switch to that agent and it can edit that file for you."
|
|
18
|
+
- For specification.md or contracts: "I can't edit specification.md - that's handled by the specification agent. Use **Shift+Tab** to switch to that agent and it can edit that file for you."
|
|
19
|
+
- For tasks.md: "I can't edit tasks.md - that's handled by the tasks agent. Use **Shift+Tab** to switch to that agent and it can edit that file for you."
|
|
20
|
+
|
|
21
|
+
NEVER offer to do work outside your scope:
|
|
22
|
+
- Don't offer to write research, specifications, or tasks - redirect the user to the appropriate agent
|
|
23
|
+
- Don't offer to implement code - you are not a coding agent
|
|
24
|
+
|
|
9
25
|
## MEMORY MANAGEMENT PROTOCOL
|
|
10
26
|
|
|
11
27
|
- You have exclusive write access to: `plan.md`
|