shotgun-sh 0.2.6.dev5__tar.gz → 0.2.7.dev1__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.
Potentially problematic release.
This version of shotgun-sh might be problematic. Click here for more details.
- shotgun_sh-0.2.7.dev1/PKG-INFO +125 -0
- shotgun_sh-0.2.7.dev1/README_PYPI.md +71 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/pyproject.toml +3 -2
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/agents/agent_manager.py +81 -1
- shotgun_sh-0.2.7.dev1/src/shotgun/agents/conversation_history.py +227 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/agents/conversation_manager.py +24 -2
- shotgun_sh-0.2.7.dev1/src/shotgun/agents/history/context_extraction.py +195 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/tui/screens/chat.py +54 -13
- shotgun_sh-0.2.6.dev5/PKG-INFO +0 -467
- shotgun_sh-0.2.6.dev5/src/shotgun/agents/conversation_history.py +0 -106
- shotgun_sh-0.2.6.dev5/src/shotgun/agents/history/context_extraction.py +0 -108
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/.gitignore +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/LICENSE +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/README.md +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/hatch_build.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/__init__.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/agents/__init__.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/agents/common.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/agents/config/__init__.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/agents/config/constants.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/agents/config/manager.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/agents/config/models.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/agents/config/provider.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/agents/export.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/agents/history/__init__.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/agents/history/compaction.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/agents/history/constants.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/agents/history/history_building.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/agents/history/history_processors.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/agents/history/message_utils.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/agents/history/token_counting/__init__.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/agents/history/token_counting/anthropic.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/agents/history/token_counting/base.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/agents/history/token_counting/openai.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/agents/history/token_counting/sentencepiece_counter.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/agents/history/token_counting/tokenizer_cache.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/agents/history/token_counting/utils.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/agents/history/token_estimation.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/agents/llm.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/agents/messages.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/agents/models.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/agents/plan.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/agents/research.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/agents/specify.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/agents/tasks.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/agents/tools/__init__.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/agents/tools/codebase/__init__.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/agents/tools/codebase/codebase_shell.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/agents/tools/codebase/directory_lister.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/agents/tools/codebase/file_read.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/agents/tools/codebase/models.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/agents/tools/codebase/query_graph.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/agents/tools/codebase/retrieve_code.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/agents/tools/file_management.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/agents/tools/web_search/__init__.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/agents/tools/web_search/anthropic.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/agents/tools/web_search/gemini.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/agents/tools/web_search/openai.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/agents/tools/web_search/utils.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/agents/usage_manager.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/api_endpoints.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/build_constants.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/cli/__init__.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/cli/codebase/__init__.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/cli/codebase/commands.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/cli/codebase/models.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/cli/config.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/cli/export.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/cli/feedback.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/cli/models.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/cli/plan.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/cli/research.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/cli/specify.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/cli/tasks.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/cli/update.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/cli/utils.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/codebase/__init__.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/codebase/core/__init__.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/codebase/core/change_detector.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/codebase/core/code_retrieval.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/codebase/core/cypher_models.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/codebase/core/ingestor.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/codebase/core/language_config.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/codebase/core/manager.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/codebase/core/nl_query.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/codebase/core/parser_loader.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/codebase/models.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/codebase/service.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/llm_proxy/__init__.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/llm_proxy/clients.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/llm_proxy/constants.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/logging_config.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/main.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/posthog_telemetry.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/prompts/__init__.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/prompts/agents/__init__.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/prompts/agents/export.j2 +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/prompts/agents/partials/codebase_understanding.j2 +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/prompts/agents/partials/common_agent_system_prompt.j2 +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/prompts/agents/partials/content_formatting.j2 +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/prompts/agents/partials/interactive_mode.j2 +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/prompts/agents/plan.j2 +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/prompts/agents/research.j2 +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/prompts/agents/specify.j2 +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/prompts/agents/state/codebase/codebase_graphs_available.j2 +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/prompts/agents/state/system_state.j2 +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/prompts/agents/tasks.j2 +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/prompts/codebase/__init__.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/prompts/codebase/cypher_query_patterns.j2 +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/prompts/codebase/cypher_system.j2 +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/prompts/codebase/enhanced_query_context.j2 +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/prompts/codebase/partials/cypher_rules.j2 +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/prompts/codebase/partials/graph_schema.j2 +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/prompts/codebase/partials/temporal_context.j2 +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/prompts/history/__init__.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/prompts/history/incremental_summarization.j2 +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/prompts/history/summarization.j2 +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/prompts/loader.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/prompts/tools/web_search.j2 +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/py.typed +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/sdk/__init__.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/sdk/codebase.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/sdk/exceptions.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/sdk/models.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/sdk/services.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/sentry_telemetry.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/shotgun_web/__init__.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/shotgun_web/client.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/shotgun_web/constants.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/shotgun_web/models.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/telemetry.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/tui/__init__.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/tui/app.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/tui/commands/__init__.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/tui/components/prompt_input.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/tui/components/spinner.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/tui/components/splash.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/tui/components/vertical_tail.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/tui/filtered_codebase_service.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/tui/screens/chat.tcss +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/tui/screens/chat_screen/__init__.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/tui/screens/chat_screen/command_providers.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/tui/screens/chat_screen/hint_message.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/tui/screens/chat_screen/history.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/tui/screens/directory_setup.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/tui/screens/feedback.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/tui/screens/model_picker.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/tui/screens/provider_config.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/tui/screens/shotgun_auth.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/tui/screens/splash.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/tui/screens/welcome.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/tui/styles.tcss +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/tui/utils/__init__.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/tui/utils/mode_progress.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/utils/__init__.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/utils/datetime_utils.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/utils/env_utils.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/utils/file_system_utils.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/utils/source_detection.py +0 -0
- {shotgun_sh-0.2.6.dev5 → shotgun_sh-0.2.7.dev1}/src/shotgun/utils/update_checker.py +0 -0
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: shotgun-sh
|
|
3
|
+
Version: 0.2.7.dev1
|
|
4
|
+
Summary: AI-powered research, planning, and task management CLI tool
|
|
5
|
+
Project-URL: Homepage, https://shotgun.sh/
|
|
6
|
+
Project-URL: Repository, https://github.com/shotgun-sh/shotgun
|
|
7
|
+
Project-URL: Issues, https://github.com/shotgun-sh/shotgun-alpha/issues
|
|
8
|
+
Project-URL: Discord, https://discord.gg/5RmY6J2N7s
|
|
9
|
+
Author-email: "Proofs.io" <hello@proofs.io>
|
|
10
|
+
License: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: agent,ai,cli,llm,planning,productivity,pydantic-ai,research,task-management
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Environment :: Console
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
22
|
+
Classifier: Topic :: Utilities
|
|
23
|
+
Requires-Python: >=3.11
|
|
24
|
+
Requires-Dist: anthropic>=0.39.0
|
|
25
|
+
Requires-Dist: genai-prices>=0.0.27
|
|
26
|
+
Requires-Dist: httpx>=0.27.0
|
|
27
|
+
Requires-Dist: jinja2>=3.1.0
|
|
28
|
+
Requires-Dist: kuzu>=0.7.0
|
|
29
|
+
Requires-Dist: logfire[pydantic-ai]>=2.0.0
|
|
30
|
+
Requires-Dist: openai>=1.0.0
|
|
31
|
+
Requires-Dist: packaging>=23.0
|
|
32
|
+
Requires-Dist: posthog>=3.0.0
|
|
33
|
+
Requires-Dist: pydantic-ai>=0.0.14
|
|
34
|
+
Requires-Dist: rich>=13.0.0
|
|
35
|
+
Requires-Dist: sentencepiece>=0.2.0
|
|
36
|
+
Requires-Dist: sentry-sdk[pure-eval]>=2.0.0
|
|
37
|
+
Requires-Dist: textual-dev>=1.7.0
|
|
38
|
+
Requires-Dist: textual>=6.1.0
|
|
39
|
+
Requires-Dist: tiktoken>=0.7.0
|
|
40
|
+
Requires-Dist: tree-sitter-go>=0.23.0
|
|
41
|
+
Requires-Dist: tree-sitter-javascript>=0.23.0
|
|
42
|
+
Requires-Dist: tree-sitter-python>=0.23.0
|
|
43
|
+
Requires-Dist: tree-sitter-rust>=0.23.0
|
|
44
|
+
Requires-Dist: tree-sitter-typescript>=0.23.0
|
|
45
|
+
Requires-Dist: tree-sitter>=0.21.0
|
|
46
|
+
Requires-Dist: typer>=0.12.0
|
|
47
|
+
Requires-Dist: watchdog>=4.0.0
|
|
48
|
+
Provides-Extra: dev
|
|
49
|
+
Requires-Dist: commitizen>=3.13.0; extra == 'dev'
|
|
50
|
+
Requires-Dist: lefthook>=1.12.0; extra == 'dev'
|
|
51
|
+
Requires-Dist: mypy>=1.11.0; extra == 'dev'
|
|
52
|
+
Requires-Dist: ruff>=0.6.0; extra == 'dev'
|
|
53
|
+
Description-Content-Type: text/markdown
|
|
54
|
+
|
|
55
|
+
# Shotgun
|
|
56
|
+
|
|
57
|
+
**Spec-Driven Development for AI Code Generation**
|
|
58
|
+
|
|
59
|
+
Shotgun is a CLI tool that turns work with AI code-gen tools from "I want to build X" into: **research → specs → plans → tasks → implementation**. It reads your entire codebase, coordinates AI agents to do the heavy lifting, and exports clean artifacts in the agents.md format so your code-gen tools actually know what they're building.
|
|
60
|
+
|
|
61
|
+
🌐 **Learn more at [shotgun.sh](https://shotgun.sh/)**
|
|
62
|
+
|
|
63
|
+
## Features
|
|
64
|
+
|
|
65
|
+
### 📊 Complete Codebase Understanding
|
|
66
|
+
|
|
67
|
+
Before writing a single line, Shotgun reads all of it. Your patterns. Your dependencies. Your technical debt. Whether you're adding features, onboarding devs, planning migrations, or refactoring - Shotgun knows what you're working with.
|
|
68
|
+
|
|
69
|
+
### 🔄 Five Modes. One Journey. Zero Gaps.
|
|
70
|
+
|
|
71
|
+
**Research** (what exists) → **Specify** (what to build) → **Plan** (how to build) → **Tasks** (break it down) → **Export** (to any tool)
|
|
72
|
+
|
|
73
|
+
Not another chatbot. A complete workflow where each mode feeds the next.
|
|
74
|
+
|
|
75
|
+
### ➡️ Export to agents.md
|
|
76
|
+
|
|
77
|
+
Outputs plug into many code-generation tools including Codex, Cursor, Warp, Devin, opencode, Jules, and more.
|
|
78
|
+
|
|
79
|
+
### 📝 Specs That Don't Die in Slack
|
|
80
|
+
|
|
81
|
+
Every research finding, every architectural decision, every "here's why we didn't use that library" - captured as markdown in your repo. Version controlled. Searchable.
|
|
82
|
+
|
|
83
|
+
## Installation
|
|
84
|
+
|
|
85
|
+
### Using pipx (Recommended)
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
pipx install shotgun-sh
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
**Why pipx?** It installs Shotgun in an isolated environment, preventing dependency conflicts with your other Python projects.
|
|
92
|
+
|
|
93
|
+
### Using pip
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
pip install shotgun-sh
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Quick Start
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
# Research your codebase or a topic
|
|
103
|
+
shotgun research "What is our authentication flow?"
|
|
104
|
+
|
|
105
|
+
# Generate specifications
|
|
106
|
+
shotgun spec "Add OAuth2 authentication"
|
|
107
|
+
|
|
108
|
+
# Create an implementation plan
|
|
109
|
+
shotgun plan "Build user dashboard"
|
|
110
|
+
|
|
111
|
+
# Break down into tasks
|
|
112
|
+
shotgun tasks "Implement payment system"
|
|
113
|
+
|
|
114
|
+
# Export to agents.md format for your code-gen tools
|
|
115
|
+
shotgun export
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Support
|
|
119
|
+
|
|
120
|
+
Have questions? Join our community on **[Discord](https://discord.gg/5RmY6J2N7s)**
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
**License:** MIT
|
|
125
|
+
**Python:** 3.11+
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Shotgun
|
|
2
|
+
|
|
3
|
+
**Spec-Driven Development for AI Code Generation**
|
|
4
|
+
|
|
5
|
+
Shotgun is a CLI tool that turns work with AI code-gen tools from "I want to build X" into: **research → specs → plans → tasks → implementation**. It reads your entire codebase, coordinates AI agents to do the heavy lifting, and exports clean artifacts in the agents.md format so your code-gen tools actually know what they're building.
|
|
6
|
+
|
|
7
|
+
🌐 **Learn more at [shotgun.sh](https://shotgun.sh/)**
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
### 📊 Complete Codebase Understanding
|
|
12
|
+
|
|
13
|
+
Before writing a single line, Shotgun reads all of it. Your patterns. Your dependencies. Your technical debt. Whether you're adding features, onboarding devs, planning migrations, or refactoring - Shotgun knows what you're working with.
|
|
14
|
+
|
|
15
|
+
### 🔄 Five Modes. One Journey. Zero Gaps.
|
|
16
|
+
|
|
17
|
+
**Research** (what exists) → **Specify** (what to build) → **Plan** (how to build) → **Tasks** (break it down) → **Export** (to any tool)
|
|
18
|
+
|
|
19
|
+
Not another chatbot. A complete workflow where each mode feeds the next.
|
|
20
|
+
|
|
21
|
+
### ➡️ Export to agents.md
|
|
22
|
+
|
|
23
|
+
Outputs plug into many code-generation tools including Codex, Cursor, Warp, Devin, opencode, Jules, and more.
|
|
24
|
+
|
|
25
|
+
### 📝 Specs That Don't Die in Slack
|
|
26
|
+
|
|
27
|
+
Every research finding, every architectural decision, every "here's why we didn't use that library" - captured as markdown in your repo. Version controlled. Searchable.
|
|
28
|
+
|
|
29
|
+
## Installation
|
|
30
|
+
|
|
31
|
+
### Using pipx (Recommended)
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
pipx install shotgun-sh
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
**Why pipx?** It installs Shotgun in an isolated environment, preventing dependency conflicts with your other Python projects.
|
|
38
|
+
|
|
39
|
+
### Using pip
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
pip install shotgun-sh
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Quick Start
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
# Research your codebase or a topic
|
|
49
|
+
shotgun research "What is our authentication flow?"
|
|
50
|
+
|
|
51
|
+
# Generate specifications
|
|
52
|
+
shotgun spec "Add OAuth2 authentication"
|
|
53
|
+
|
|
54
|
+
# Create an implementation plan
|
|
55
|
+
shotgun plan "Build user dashboard"
|
|
56
|
+
|
|
57
|
+
# Break down into tasks
|
|
58
|
+
shotgun tasks "Implement payment system"
|
|
59
|
+
|
|
60
|
+
# Export to agents.md format for your code-gen tools
|
|
61
|
+
shotgun export
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Support
|
|
65
|
+
|
|
66
|
+
Have questions? Join our community on **[Discord](https://discord.gg/5RmY6J2N7s)**
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
**License:** MIT
|
|
71
|
+
**Python:** 3.11+
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "shotgun-sh"
|
|
3
|
-
version = "0.2.
|
|
3
|
+
version = "0.2.7.dev1"
|
|
4
4
|
description = "AI-powered research, planning, and task management CLI tool"
|
|
5
|
-
readme = "
|
|
5
|
+
readme = "README_PYPI.md"
|
|
6
6
|
license = { text = "MIT" }
|
|
7
7
|
authors = [
|
|
8
8
|
{ name = "Proofs.io", email = "hello@proofs.io" }
|
|
@@ -76,6 +76,7 @@ packages = ["src/shotgun"]
|
|
|
76
76
|
include = [
|
|
77
77
|
"/src",
|
|
78
78
|
"/README.md",
|
|
79
|
+
"/README_PYPI.md",
|
|
79
80
|
"/LICENSE",
|
|
80
81
|
"/pyproject.toml"
|
|
81
82
|
]
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
"""Agent manager for coordinating multiple AI agents with shared message history."""
|
|
2
2
|
|
|
3
|
+
import json
|
|
3
4
|
import logging
|
|
4
5
|
from collections.abc import AsyncIterable, Sequence
|
|
5
6
|
from dataclasses import dataclass, field, is_dataclass, replace
|
|
6
7
|
from typing import TYPE_CHECKING, Any, cast
|
|
7
8
|
|
|
9
|
+
import logfire
|
|
10
|
+
|
|
8
11
|
if TYPE_CHECKING:
|
|
9
12
|
from shotgun.agents.conversation_history import ConversationState
|
|
10
13
|
|
|
@@ -401,6 +404,24 @@ class AgentManager(Widget):
|
|
|
401
404
|
else None,
|
|
402
405
|
**kwargs,
|
|
403
406
|
)
|
|
407
|
+
except Exception as e:
|
|
408
|
+
# Log the error with full stack trace to shotgun.log and Logfire
|
|
409
|
+
logger.exception(
|
|
410
|
+
"Agent execution failed",
|
|
411
|
+
extra={
|
|
412
|
+
"agent_mode": self._current_agent_type.value,
|
|
413
|
+
"model_name": model_name,
|
|
414
|
+
"error_type": type(e).__name__,
|
|
415
|
+
},
|
|
416
|
+
)
|
|
417
|
+
logfire.exception(
|
|
418
|
+
"Agent execution failed",
|
|
419
|
+
agent_mode=self._current_agent_type.value,
|
|
420
|
+
model_name=model_name,
|
|
421
|
+
error_type=type(e).__name__,
|
|
422
|
+
)
|
|
423
|
+
# Re-raise to let TUI handle user messaging
|
|
424
|
+
raise
|
|
404
425
|
finally:
|
|
405
426
|
self._stream_state = None
|
|
406
427
|
|
|
@@ -468,7 +489,33 @@ class AgentManager(Widget):
|
|
|
468
489
|
|
|
469
490
|
# Apply compaction to persistent message history to prevent cascading growth
|
|
470
491
|
all_messages = result.all_messages()
|
|
471
|
-
|
|
492
|
+
try:
|
|
493
|
+
logger.debug(
|
|
494
|
+
"Starting message history compaction",
|
|
495
|
+
extra={"message_count": len(all_messages)},
|
|
496
|
+
)
|
|
497
|
+
self.message_history = await apply_persistent_compaction(all_messages, deps)
|
|
498
|
+
logger.debug(
|
|
499
|
+
"Completed message history compaction",
|
|
500
|
+
extra={
|
|
501
|
+
"original_count": len(all_messages),
|
|
502
|
+
"compacted_count": len(self.message_history),
|
|
503
|
+
},
|
|
504
|
+
)
|
|
505
|
+
except Exception as e:
|
|
506
|
+
# If compaction fails, log full error with stack trace and use uncompacted messages
|
|
507
|
+
logger.error(
|
|
508
|
+
"Failed to compact message history - using uncompacted messages",
|
|
509
|
+
exc_info=True,
|
|
510
|
+
extra={
|
|
511
|
+
"error": str(e),
|
|
512
|
+
"message_count": len(all_messages),
|
|
513
|
+
"agent_mode": self._current_agent_type.value,
|
|
514
|
+
},
|
|
515
|
+
)
|
|
516
|
+
# Fallback: use uncompacted messages to prevent data loss
|
|
517
|
+
self.message_history = all_messages
|
|
518
|
+
|
|
472
519
|
usage = result.usage()
|
|
473
520
|
deps.usage_manager.add_usage(
|
|
474
521
|
usage, model_name=deps.llm_model.name, provider=deps.llm_model.provider
|
|
@@ -554,6 +601,39 @@ class AgentManager(Widget):
|
|
|
554
601
|
# Detect source from call stack
|
|
555
602
|
source = detect_source()
|
|
556
603
|
|
|
604
|
+
# Log if tool call has incomplete args (for debugging truncated JSON)
|
|
605
|
+
if isinstance(event.part.args, str):
|
|
606
|
+
try:
|
|
607
|
+
json.loads(event.part.args)
|
|
608
|
+
except (json.JSONDecodeError, ValueError):
|
|
609
|
+
args_preview = (
|
|
610
|
+
event.part.args[:100] + "..."
|
|
611
|
+
if len(event.part.args) > 100
|
|
612
|
+
else event.part.args
|
|
613
|
+
)
|
|
614
|
+
logger.warning(
|
|
615
|
+
"FunctionToolCallEvent received with incomplete JSON args",
|
|
616
|
+
extra={
|
|
617
|
+
"tool_name": event.part.tool_name,
|
|
618
|
+
"tool_call_id": event.part.tool_call_id,
|
|
619
|
+
"args_preview": args_preview,
|
|
620
|
+
"args_length": len(event.part.args)
|
|
621
|
+
if event.part.args
|
|
622
|
+
else 0,
|
|
623
|
+
"agent_mode": self._current_agent_type.value,
|
|
624
|
+
},
|
|
625
|
+
)
|
|
626
|
+
logfire.warn(
|
|
627
|
+
"FunctionToolCallEvent received with incomplete JSON args",
|
|
628
|
+
tool_name=event.part.tool_name,
|
|
629
|
+
tool_call_id=event.part.tool_call_id,
|
|
630
|
+
args_preview=args_preview,
|
|
631
|
+
args_length=len(event.part.args)
|
|
632
|
+
if event.part.args
|
|
633
|
+
else 0,
|
|
634
|
+
agent_mode=self._current_agent_type.value,
|
|
635
|
+
)
|
|
636
|
+
|
|
557
637
|
track_event(
|
|
558
638
|
"tool_called",
|
|
559
639
|
{
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
"""Models and utilities for persisting TUI conversation history."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import logging
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
from typing import Any, cast
|
|
7
|
+
|
|
8
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
9
|
+
from pydantic_ai.messages import (
|
|
10
|
+
ModelMessage,
|
|
11
|
+
ModelMessagesTypeAdapter,
|
|
12
|
+
ModelResponse,
|
|
13
|
+
ToolCallPart,
|
|
14
|
+
)
|
|
15
|
+
from pydantic_core import to_jsonable_python
|
|
16
|
+
|
|
17
|
+
from shotgun.tui.screens.chat_screen.hint_message import HintMessage
|
|
18
|
+
|
|
19
|
+
logger = logging.getLogger(__name__)
|
|
20
|
+
|
|
21
|
+
SerializedMessage = dict[str, Any]
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def is_tool_call_complete(tool_call: ToolCallPart) -> bool:
|
|
25
|
+
"""Check if a tool call has valid, complete JSON arguments.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
tool_call: The tool call part to validate
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
True if the tool call args are valid JSON, False otherwise
|
|
32
|
+
"""
|
|
33
|
+
if tool_call.args is None:
|
|
34
|
+
return True # No args is valid
|
|
35
|
+
|
|
36
|
+
if isinstance(tool_call.args, dict):
|
|
37
|
+
return True # Already parsed dict is valid
|
|
38
|
+
|
|
39
|
+
if not isinstance(tool_call.args, str):
|
|
40
|
+
return False
|
|
41
|
+
|
|
42
|
+
# Try to parse the JSON string
|
|
43
|
+
try:
|
|
44
|
+
json.loads(tool_call.args)
|
|
45
|
+
return True
|
|
46
|
+
except (json.JSONDecodeError, ValueError) as e:
|
|
47
|
+
# Log incomplete tool call detection
|
|
48
|
+
args_preview = (
|
|
49
|
+
tool_call.args[:100] + "..."
|
|
50
|
+
if len(tool_call.args) > 100
|
|
51
|
+
else tool_call.args
|
|
52
|
+
)
|
|
53
|
+
logger.info(
|
|
54
|
+
"Detected incomplete tool call in validation",
|
|
55
|
+
extra={
|
|
56
|
+
"tool_name": tool_call.tool_name,
|
|
57
|
+
"tool_call_id": tool_call.tool_call_id,
|
|
58
|
+
"args_preview": args_preview,
|
|
59
|
+
"error": str(e),
|
|
60
|
+
},
|
|
61
|
+
)
|
|
62
|
+
return False
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def filter_incomplete_messages(messages: list[ModelMessage]) -> list[ModelMessage]:
|
|
66
|
+
"""Filter out messages with incomplete tool calls.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
messages: List of messages to filter
|
|
70
|
+
|
|
71
|
+
Returns:
|
|
72
|
+
List of messages with only complete tool calls
|
|
73
|
+
"""
|
|
74
|
+
filtered: list[ModelMessage] = []
|
|
75
|
+
filtered_count = 0
|
|
76
|
+
filtered_tool_names: list[str] = []
|
|
77
|
+
|
|
78
|
+
for message in messages:
|
|
79
|
+
# Only check ModelResponse messages for tool calls
|
|
80
|
+
if not isinstance(message, ModelResponse):
|
|
81
|
+
filtered.append(message)
|
|
82
|
+
continue
|
|
83
|
+
|
|
84
|
+
# Check if any tool calls are incomplete
|
|
85
|
+
has_incomplete_tool_call = False
|
|
86
|
+
for part in message.parts:
|
|
87
|
+
if isinstance(part, ToolCallPart) and not is_tool_call_complete(part):
|
|
88
|
+
has_incomplete_tool_call = True
|
|
89
|
+
filtered_tool_names.append(part.tool_name)
|
|
90
|
+
break
|
|
91
|
+
|
|
92
|
+
# Only include messages without incomplete tool calls
|
|
93
|
+
if not has_incomplete_tool_call:
|
|
94
|
+
filtered.append(message)
|
|
95
|
+
else:
|
|
96
|
+
filtered_count += 1
|
|
97
|
+
|
|
98
|
+
# Log if any messages were filtered
|
|
99
|
+
if filtered_count > 0:
|
|
100
|
+
logger.info(
|
|
101
|
+
"Filtered incomplete messages before saving",
|
|
102
|
+
extra={
|
|
103
|
+
"filtered_count": filtered_count,
|
|
104
|
+
"total_messages": len(messages),
|
|
105
|
+
"filtered_tool_names": filtered_tool_names,
|
|
106
|
+
},
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
return filtered
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
class ConversationState(BaseModel):
|
|
113
|
+
"""Represents the complete state of a conversation in memory."""
|
|
114
|
+
|
|
115
|
+
agent_messages: list[ModelMessage]
|
|
116
|
+
ui_messages: list[ModelMessage | HintMessage] = Field(default_factory=list)
|
|
117
|
+
agent_type: str # Will store AgentType.value
|
|
118
|
+
|
|
119
|
+
model_config = ConfigDict(arbitrary_types_allowed=True)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
class ConversationHistory(BaseModel):
|
|
123
|
+
"""Persistent conversation history for TUI sessions."""
|
|
124
|
+
|
|
125
|
+
version: int = 1
|
|
126
|
+
agent_history: list[SerializedMessage] = Field(
|
|
127
|
+
default_factory=list
|
|
128
|
+
) # Stores serialized ModelMessage objects
|
|
129
|
+
ui_history: list[SerializedMessage] = Field(
|
|
130
|
+
default_factory=list
|
|
131
|
+
) # Stores serialized ModelMessage and HintMessage objects
|
|
132
|
+
last_agent_model: str = "research"
|
|
133
|
+
updated_at: datetime = Field(default_factory=datetime.now)
|
|
134
|
+
|
|
135
|
+
model_config = ConfigDict(arbitrary_types_allowed=True)
|
|
136
|
+
|
|
137
|
+
def set_agent_messages(self, messages: list[ModelMessage]) -> None:
|
|
138
|
+
"""Set agent_history from a list of ModelMessage objects.
|
|
139
|
+
|
|
140
|
+
Args:
|
|
141
|
+
messages: List of ModelMessage objects to serialize and store
|
|
142
|
+
"""
|
|
143
|
+
# Filter out messages with incomplete tool calls to prevent corruption
|
|
144
|
+
filtered_messages = filter_incomplete_messages(messages)
|
|
145
|
+
|
|
146
|
+
# Serialize ModelMessage list to JSON-serializable format
|
|
147
|
+
self.agent_history = to_jsonable_python(
|
|
148
|
+
filtered_messages, fallback=lambda x: str(x), exclude_none=True
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
def set_ui_messages(self, messages: list[ModelMessage | HintMessage]) -> None:
|
|
152
|
+
"""Set ui_history from a list of UI messages."""
|
|
153
|
+
|
|
154
|
+
# Filter out ModelMessages with incomplete tool calls (keep all HintMessages)
|
|
155
|
+
# We need to maintain message order, so we'll check each message individually
|
|
156
|
+
filtered_messages: list[ModelMessage | HintMessage] = []
|
|
157
|
+
|
|
158
|
+
for msg in messages:
|
|
159
|
+
if isinstance(msg, HintMessage):
|
|
160
|
+
# Always keep hint messages
|
|
161
|
+
filtered_messages.append(msg)
|
|
162
|
+
elif isinstance(msg, ModelResponse):
|
|
163
|
+
# Check if this ModelResponse has incomplete tool calls
|
|
164
|
+
has_incomplete = False
|
|
165
|
+
for part in msg.parts:
|
|
166
|
+
if isinstance(part, ToolCallPart) and not is_tool_call_complete(
|
|
167
|
+
part
|
|
168
|
+
):
|
|
169
|
+
has_incomplete = True
|
|
170
|
+
break
|
|
171
|
+
|
|
172
|
+
if not has_incomplete:
|
|
173
|
+
filtered_messages.append(msg)
|
|
174
|
+
else:
|
|
175
|
+
# Keep all other ModelMessage types (ModelRequest, etc.)
|
|
176
|
+
filtered_messages.append(msg)
|
|
177
|
+
|
|
178
|
+
def _serialize_message(
|
|
179
|
+
message: ModelMessage | HintMessage,
|
|
180
|
+
) -> Any:
|
|
181
|
+
if isinstance(message, HintMessage):
|
|
182
|
+
data = message.model_dump()
|
|
183
|
+
data["message_type"] = "hint"
|
|
184
|
+
return data
|
|
185
|
+
payload = to_jsonable_python(
|
|
186
|
+
message, fallback=lambda x: str(x), exclude_none=True
|
|
187
|
+
)
|
|
188
|
+
if isinstance(payload, dict):
|
|
189
|
+
payload.setdefault("message_type", "model")
|
|
190
|
+
return payload
|
|
191
|
+
|
|
192
|
+
self.ui_history = [_serialize_message(msg) for msg in filtered_messages]
|
|
193
|
+
|
|
194
|
+
def get_agent_messages(self) -> list[ModelMessage]:
|
|
195
|
+
"""Get agent_history as a list of ModelMessage objects.
|
|
196
|
+
|
|
197
|
+
Returns:
|
|
198
|
+
List of deserialized ModelMessage objects
|
|
199
|
+
"""
|
|
200
|
+
if not self.agent_history:
|
|
201
|
+
return []
|
|
202
|
+
|
|
203
|
+
# Deserialize from JSON format back to ModelMessage objects
|
|
204
|
+
return ModelMessagesTypeAdapter.validate_python(self.agent_history)
|
|
205
|
+
|
|
206
|
+
def get_ui_messages(self) -> list[ModelMessage | HintMessage]:
|
|
207
|
+
"""Get ui_history as a list of Model or hint messages."""
|
|
208
|
+
|
|
209
|
+
if not self.ui_history:
|
|
210
|
+
# Fallback for older conversation files without UI history
|
|
211
|
+
return cast(list[ModelMessage | HintMessage], self.get_agent_messages())
|
|
212
|
+
|
|
213
|
+
messages: list[ModelMessage | HintMessage] = []
|
|
214
|
+
for item in self.ui_history:
|
|
215
|
+
message_type = item.get("message_type") if isinstance(item, dict) else None
|
|
216
|
+
if message_type == "hint":
|
|
217
|
+
messages.append(HintMessage.model_validate(item))
|
|
218
|
+
continue
|
|
219
|
+
|
|
220
|
+
# Backwards compatibility: data may not include the type marker
|
|
221
|
+
payload = item
|
|
222
|
+
if isinstance(payload, dict):
|
|
223
|
+
payload = {k: v for k, v in payload.items() if k != "message_type"}
|
|
224
|
+
deserialized = ModelMessagesTypeAdapter.validate_python([payload])
|
|
225
|
+
messages.append(deserialized[0])
|
|
226
|
+
|
|
227
|
+
return messages
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Manager for handling conversation persistence operations."""
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
|
+
import shutil
|
|
4
5
|
from pathlib import Path
|
|
5
6
|
|
|
6
7
|
from shotgun.logging_config import get_logger
|
|
@@ -77,9 +78,30 @@ class ConversationManager:
|
|
|
77
78
|
)
|
|
78
79
|
return conversation
|
|
79
80
|
|
|
80
|
-
except
|
|
81
|
+
except (json.JSONDecodeError, ValueError) as e:
|
|
82
|
+
# Handle corrupted JSON or validation errors
|
|
83
|
+
logger.error(
|
|
84
|
+
"Corrupted conversation file at %s: %s. Creating backup and starting fresh.",
|
|
85
|
+
self.conversation_path,
|
|
86
|
+
e,
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
# Create a backup of the corrupted file for debugging
|
|
90
|
+
backup_path = self.conversation_path.with_suffix(".json.backup")
|
|
91
|
+
try:
|
|
92
|
+
shutil.copy2(self.conversation_path, backup_path)
|
|
93
|
+
logger.info("Backed up corrupted conversation to %s", backup_path)
|
|
94
|
+
except Exception as backup_error: # pragma: no cover
|
|
95
|
+
logger.warning("Failed to backup corrupted file: %s", backup_error)
|
|
96
|
+
|
|
97
|
+
return None
|
|
98
|
+
|
|
99
|
+
except Exception as e: # pragma: no cover
|
|
100
|
+
# Catch-all for unexpected errors
|
|
81
101
|
logger.error(
|
|
82
|
-
"
|
|
102
|
+
"Unexpected error loading conversation from %s: %s",
|
|
103
|
+
self.conversation_path,
|
|
104
|
+
e,
|
|
83
105
|
)
|
|
84
106
|
return None
|
|
85
107
|
|