shotgun-sh 0.1.0.dev22__tar.gz → 0.1.0.dev24__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.

Files changed (152) hide show
  1. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/PKG-INFO +8 -9
  2. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/README.md +6 -6
  3. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/pyproject.toml +3 -4
  4. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/agents/agent_manager.py +95 -15
  5. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/agents/common.py +143 -25
  6. shotgun_sh-0.1.0.dev24/src/shotgun/agents/conversation_history.py +56 -0
  7. shotgun_sh-0.1.0.dev24/src/shotgun/agents/conversation_manager.py +105 -0
  8. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/agents/export.py +5 -2
  9. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/agents/models.py +16 -7
  10. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/agents/plan.py +2 -1
  11. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/agents/research.py +2 -1
  12. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/agents/specify.py +2 -1
  13. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/agents/tasks.py +5 -2
  14. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/agents/tools/file_management.py +67 -2
  15. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/codebase/core/ingestor.py +1 -1
  16. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/codebase/core/manager.py +106 -4
  17. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/codebase/models.py +4 -0
  18. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/codebase/service.py +60 -2
  19. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/main.py +9 -1
  20. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/prompts/agents/export.j2 +14 -11
  21. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/prompts/agents/partials/common_agent_system_prompt.j2 +6 -9
  22. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/prompts/agents/plan.j2 +9 -13
  23. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/prompts/agents/research.j2 +11 -14
  24. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/prompts/agents/specify.j2 +9 -12
  25. shotgun_sh-0.1.0.dev24/src/shotgun/prompts/agents/state/system_state.j2 +31 -0
  26. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/prompts/agents/tasks.j2 +12 -12
  27. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/sdk/codebase.py +26 -2
  28. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/sdk/services.py +0 -14
  29. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/tui/app.py +9 -4
  30. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/tui/screens/chat.py +80 -19
  31. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/tui/screens/chat_screen/command_providers.py +1 -1
  32. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/tui/screens/chat_screen/history.py +6 -0
  33. shotgun_sh-0.1.0.dev24/src/shotgun/tui/utils/mode_progress.py +257 -0
  34. shotgun_sh-0.1.0.dev22/src/shotgun/agents/artifact_state.py +0 -58
  35. shotgun_sh-0.1.0.dev22/src/shotgun/agents/tools/artifact_management.py +0 -481
  36. shotgun_sh-0.1.0.dev22/src/shotgun/artifacts/__init__.py +0 -17
  37. shotgun_sh-0.1.0.dev22/src/shotgun/artifacts/exceptions.py +0 -89
  38. shotgun_sh-0.1.0.dev22/src/shotgun/artifacts/manager.py +0 -530
  39. shotgun_sh-0.1.0.dev22/src/shotgun/artifacts/models.py +0 -334
  40. shotgun_sh-0.1.0.dev22/src/shotgun/artifacts/service.py +0 -463
  41. shotgun_sh-0.1.0.dev22/src/shotgun/artifacts/templates/__init__.py +0 -10
  42. shotgun_sh-0.1.0.dev22/src/shotgun/artifacts/templates/loader.py +0 -252
  43. shotgun_sh-0.1.0.dev22/src/shotgun/artifacts/templates/models.py +0 -136
  44. shotgun_sh-0.1.0.dev22/src/shotgun/artifacts/templates/plan/delivery_and_release_plan.yaml +0 -66
  45. shotgun_sh-0.1.0.dev22/src/shotgun/artifacts/templates/research/market_research.yaml +0 -585
  46. shotgun_sh-0.1.0.dev22/src/shotgun/artifacts/templates/research/sdk_comparison.yaml +0 -257
  47. shotgun_sh-0.1.0.dev22/src/shotgun/artifacts/templates/specify/prd.yaml +0 -331
  48. shotgun_sh-0.1.0.dev22/src/shotgun/artifacts/templates/specify/product_spec.yaml +0 -301
  49. shotgun_sh-0.1.0.dev22/src/shotgun/artifacts/utils.py +0 -76
  50. shotgun_sh-0.1.0.dev22/src/shotgun/prompts/agents/partials/artifact_system.j2 +0 -32
  51. shotgun_sh-0.1.0.dev22/src/shotgun/prompts/agents/state/artifact_templates_available.j2 +0 -20
  52. shotgun_sh-0.1.0.dev22/src/shotgun/prompts/agents/state/existing_artifacts_available.j2 +0 -25
  53. shotgun_sh-0.1.0.dev22/src/shotgun/prompts/agents/state/system_state.j2 +0 -9
  54. shotgun_sh-0.1.0.dev22/src/shotgun/sdk/artifact_models.py +0 -186
  55. shotgun_sh-0.1.0.dev22/src/shotgun/sdk/artifacts.py +0 -448
  56. shotgun_sh-0.1.0.dev22/src/shotgun/tui/utils/mode_progress.py +0 -224
  57. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/.gitignore +0 -0
  58. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/LICENSE +0 -0
  59. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/hatch_build.py +0 -0
  60. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/__init__.py +0 -0
  61. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/agents/__init__.py +0 -0
  62. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/agents/config/__init__.py +0 -0
  63. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/agents/config/constants.py +0 -0
  64. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/agents/config/manager.py +0 -0
  65. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/agents/config/models.py +0 -0
  66. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/agents/config/provider.py +0 -0
  67. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/agents/history/__init__.py +0 -0
  68. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/agents/history/compaction.py +0 -0
  69. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/agents/history/constants.py +0 -0
  70. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/agents/history/context_extraction.py +0 -0
  71. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/agents/history/history_building.py +0 -0
  72. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/agents/history/history_processors.py +0 -0
  73. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/agents/history/message_utils.py +0 -0
  74. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/agents/history/token_counting.py +0 -0
  75. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/agents/history/token_estimation.py +0 -0
  76. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/agents/tools/__init__.py +0 -0
  77. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/agents/tools/codebase/__init__.py +0 -0
  78. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/agents/tools/codebase/codebase_shell.py +0 -0
  79. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/agents/tools/codebase/directory_lister.py +0 -0
  80. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/agents/tools/codebase/file_read.py +0 -0
  81. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/agents/tools/codebase/models.py +0 -0
  82. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/agents/tools/codebase/query_graph.py +0 -0
  83. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/agents/tools/codebase/retrieve_code.py +0 -0
  84. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/agents/tools/user_interaction.py +0 -0
  85. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/agents/tools/web_search/__init__.py +0 -0
  86. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/agents/tools/web_search/anthropic.py +0 -0
  87. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/agents/tools/web_search/gemini.py +0 -0
  88. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/agents/tools/web_search/openai.py +0 -0
  89. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/agents/tools/web_search/utils.py +0 -0
  90. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/build_constants.py +0 -0
  91. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/cli/__init__.py +0 -0
  92. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/cli/codebase/__init__.py +0 -0
  93. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/cli/codebase/commands.py +0 -0
  94. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/cli/codebase/models.py +0 -0
  95. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/cli/config.py +0 -0
  96. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/cli/export.py +0 -0
  97. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/cli/models.py +0 -0
  98. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/cli/plan.py +0 -0
  99. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/cli/research.py +0 -0
  100. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/cli/specify.py +0 -0
  101. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/cli/tasks.py +0 -0
  102. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/cli/update.py +0 -0
  103. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/cli/utils.py +0 -0
  104. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/codebase/__init__.py +0 -0
  105. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/codebase/core/__init__.py +0 -0
  106. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/codebase/core/change_detector.py +0 -0
  107. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/codebase/core/code_retrieval.py +0 -0
  108. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/codebase/core/language_config.py +0 -0
  109. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/codebase/core/nl_query.py +0 -0
  110. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/codebase/core/parser_loader.py +0 -0
  111. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/logging_config.py +0 -0
  112. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/posthog_telemetry.py +0 -0
  113. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/prompts/__init__.py +0 -0
  114. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/prompts/agents/__init__.py +0 -0
  115. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/prompts/agents/partials/codebase_understanding.j2 +0 -0
  116. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/prompts/agents/partials/content_formatting.j2 +0 -0
  117. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/prompts/agents/partials/interactive_mode.j2 +0 -0
  118. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/prompts/agents/state/codebase/codebase_graphs_available.j2 +0 -0
  119. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/prompts/codebase/__init__.py +0 -0
  120. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/prompts/codebase/cypher_query_patterns.j2 +0 -0
  121. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/prompts/codebase/cypher_system.j2 +0 -0
  122. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/prompts/codebase/enhanced_query_context.j2 +0 -0
  123. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/prompts/codebase/partials/cypher_rules.j2 +0 -0
  124. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/prompts/codebase/partials/graph_schema.j2 +0 -0
  125. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/prompts/codebase/partials/temporal_context.j2 +0 -0
  126. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/prompts/history/__init__.py +0 -0
  127. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/prompts/history/incremental_summarization.j2 +0 -0
  128. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/prompts/history/summarization.j2 +0 -0
  129. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/prompts/loader.py +0 -0
  130. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/py.typed +0 -0
  131. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/sdk/__init__.py +0 -0
  132. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/sdk/exceptions.py +0 -0
  133. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/sdk/models.py +0 -0
  134. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/sentry_telemetry.py +0 -0
  135. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/telemetry.py +0 -0
  136. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/tui/__init__.py +0 -0
  137. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/tui/commands/__init__.py +0 -0
  138. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/tui/components/prompt_input.py +0 -0
  139. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/tui/components/spinner.py +0 -0
  140. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/tui/components/splash.py +0 -0
  141. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/tui/components/vertical_tail.py +0 -0
  142. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/tui/screens/chat.tcss +0 -0
  143. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/tui/screens/chat_screen/__init__.py +0 -0
  144. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/tui/screens/directory_setup.py +0 -0
  145. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/tui/screens/provider_config.py +0 -0
  146. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/tui/screens/splash.py +0 -0
  147. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/tui/styles.tcss +0 -0
  148. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/tui/utils/__init__.py +0 -0
  149. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/utils/__init__.py +0 -0
  150. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/utils/env_utils.py +0 -0
  151. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/src/shotgun/utils/file_system_utils.py +0 -0
  152. {shotgun_sh-0.1.0.dev22 → shotgun_sh-0.1.0.dev24}/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.1.0.dev22
3
+ Version: 0.1.0.dev24
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
@@ -16,12 +16,11 @@ Classifier: Intended Audience :: Developers
16
16
  Classifier: License :: OSI Approved :: MIT License
17
17
  Classifier: Operating System :: OS Independent
18
18
  Classifier: Programming Language :: Python :: 3
19
- Classifier: Programming Language :: Python :: 3.10
20
19
  Classifier: Programming Language :: Python :: 3.11
21
20
  Classifier: Programming Language :: Python :: 3.12
22
21
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
23
22
  Classifier: Topic :: Utilities
24
- Requires-Python: >=3.10
23
+ Requires-Python: >=3.11
25
24
  Requires-Dist: anthropic>=0.39.0
26
25
  Requires-Dist: google-generativeai>=0.8.5
27
26
  Requires-Dist: httpx>=0.27.0
@@ -177,7 +176,7 @@ The update command automatically detects and uses the appropriate method:
177
176
 
178
177
  ### Requirements
179
178
 
180
- - **Python 3.10+** (3.13 recommended)
179
+ - **Python 3.11+** (3.13 recommended)
181
180
  - **uv** - Fast Python package installer and resolver
182
181
  - **actionlint** (optional) - For GitHub Actions workflow validation
183
182
 
@@ -289,17 +288,17 @@ go install github.com/rhysd/actionlint/cmd/actionlint@latest
289
288
 
290
289
  ### Python Version Management
291
290
 
292
- The project supports **Python 3.10+**. The `.python-version` file specifies Python 3.10 to ensure development against the minimum supported version.
291
+ The project supports **Python 3.11+**. The `.python-version` file specifies Python 3.11 to ensure development against the minimum supported version.
293
292
 
294
293
  If using **pyenv**:
295
294
  ```bash
296
- pyenv install 3.10.16 # or latest 3.10.x
295
+ pyenv install 3.11
297
296
  ```
298
297
 
299
298
  If using **uv** (recommended):
300
299
  ```bash
301
- uv python install 3.10
302
- uv sync --python 3.10
300
+ uv python install 3.11
301
+ uv sync --python 3.11
303
302
  ```
304
303
 
305
304
  ### Commit Message Convention
@@ -350,7 +349,7 @@ uv run cz commit
350
349
 
351
350
  GitHub Actions automatically:
352
351
  - Runs on pull requests and pushes to main
353
- - Tests with Python 3.10
352
+ - Tests with Python 3.11
354
353
  - Validates code with ruff, ruff-format, and mypy
355
354
  - Ensures all checks pass before merge
356
355
 
@@ -123,7 +123,7 @@ The update command automatically detects and uses the appropriate method:
123
123
 
124
124
  ### Requirements
125
125
 
126
- - **Python 3.10+** (3.13 recommended)
126
+ - **Python 3.11+** (3.13 recommended)
127
127
  - **uv** - Fast Python package installer and resolver
128
128
  - **actionlint** (optional) - For GitHub Actions workflow validation
129
129
 
@@ -235,17 +235,17 @@ go install github.com/rhysd/actionlint/cmd/actionlint@latest
235
235
 
236
236
  ### Python Version Management
237
237
 
238
- The project supports **Python 3.10+**. The `.python-version` file specifies Python 3.10 to ensure development against the minimum supported version.
238
+ The project supports **Python 3.11+**. The `.python-version` file specifies Python 3.11 to ensure development against the minimum supported version.
239
239
 
240
240
  If using **pyenv**:
241
241
  ```bash
242
- pyenv install 3.10.16 # or latest 3.10.x
242
+ pyenv install 3.11
243
243
  ```
244
244
 
245
245
  If using **uv** (recommended):
246
246
  ```bash
247
- uv python install 3.10
248
- uv sync --python 3.10
247
+ uv python install 3.11
248
+ uv sync --python 3.11
249
249
  ```
250
250
 
251
251
  ### Commit Message Convention
@@ -296,7 +296,7 @@ uv run cz commit
296
296
 
297
297
  GitHub Actions automatically:
298
298
  - Runs on pull requests and pushes to main
299
- - Tests with Python 3.10
299
+ - Tests with Python 3.11
300
300
  - Validates code with ruff, ruff-format, and mypy
301
301
  - Ensures all checks pass before merge
302
302
 
@@ -1,20 +1,19 @@
1
1
  [project]
2
2
  name = "shotgun-sh"
3
- version = "0.1.0.dev22"
3
+ version = "0.1.0.dev24"
4
4
  description = "AI-powered research, planning, and task management CLI tool"
5
5
  readme = "README.md"
6
6
  license = { text = "MIT" }
7
7
  authors = [
8
8
  { name = "Proofs.io", email = "hello@proofs.io" }
9
9
  ]
10
- requires-python = ">=3.10"
10
+ requires-python = ">=3.11"
11
11
  keywords = ["cli", "ai", "agent", "research", "planning", "task-management", "productivity", "llm", "pydantic-ai"]
12
12
  classifiers = [
13
13
  "Development Status :: 3 - Alpha",
14
14
  "Intended Audience :: Developers",
15
15
  "License :: OSI Approved :: MIT License",
16
16
  "Programming Language :: Python :: 3",
17
- "Programming Language :: Python :: 3.10",
18
17
  "Programming Language :: Python :: 3.11",
19
18
  "Programming Language :: Python :: 3.12",
20
19
  "Topic :: Software Development :: Libraries :: Python Modules",
@@ -101,7 +100,7 @@ ignore = ["E501", "T201"]
101
100
  "test/**/*.py" = ["S101", "B017"] # Allow assert statements and broad exceptions in tests
102
101
 
103
102
  [tool.mypy]
104
- python_version = "3.10"
103
+ python_version = "3.11"
105
104
  strict = true
106
105
  warn_return_any = true
107
106
  warn_unused_configs = true
@@ -1,10 +1,12 @@
1
1
  """Agent manager for coordinating multiple AI agents with shared message history."""
2
2
 
3
3
  import logging
4
- from collections.abc import AsyncIterable
5
- from dataclasses import dataclass, field
6
- from enum import Enum
7
- from typing import Any, cast
4
+ from collections.abc import AsyncIterable, Sequence
5
+ from dataclasses import dataclass, field, is_dataclass, replace
6
+ from typing import TYPE_CHECKING, Any, cast
7
+
8
+ if TYPE_CHECKING:
9
+ from shotgun.agents.conversation_history import ConversationState
8
10
 
9
11
  from pydantic_ai import (
10
12
  Agent,
@@ -21,6 +23,7 @@ from pydantic_ai.messages import (
21
23
  FunctionToolResultEvent,
22
24
  ModelMessage,
23
25
  ModelRequest,
26
+ ModelRequestPart,
24
27
  ModelResponse,
25
28
  ModelResponsePart,
26
29
  PartDeltaEvent,
@@ -33,10 +36,11 @@ from textual.message import Message
33
36
  from textual.widget import Widget
34
37
 
35
38
  from shotgun.agents.common import add_system_prompt_message, add_system_status_message
39
+ from shotgun.agents.models import AgentType, FileOperation
36
40
 
37
41
  from .export import create_export_agent
38
42
  from .history.compaction import apply_persistent_compaction
39
- from .models import AgentDeps, AgentRuntimeOptions, FileOperation
43
+ from .models import AgentDeps, AgentRuntimeOptions
40
44
  from .plan import create_plan_agent
41
45
  from .research import create_research_agent
42
46
  from .specify import create_specify_agent
@@ -45,16 +49,6 @@ from .tasks import create_tasks_agent
45
49
  logger = logging.getLogger(__name__)
46
50
 
47
51
 
48
- class AgentType(Enum):
49
- """Enumeration for available agent types (for Python < 3.11)."""
50
-
51
- RESEARCH = "research"
52
- PLAN = "plan"
53
- TASKS = "tasks"
54
- SPECIFY = "specify"
55
- EXPORT = "export"
56
-
57
-
58
52
  class MessageHistoryUpdated(Message):
59
53
  """Event posted when the message history is updated."""
60
54
 
@@ -281,6 +275,8 @@ class AgentManager(Widget):
281
275
  # Start with persistent message history
282
276
  message_history = self.message_history
283
277
 
278
+ deps.agent_mode = self._current_agent_type
279
+
284
280
  # Add a system status message so the agent knows whats going on
285
281
  message_history = await add_system_status_message(deps, message_history)
286
282
 
@@ -463,3 +459,87 @@ class AgentManager(Widget):
463
459
  file_operations=file_operations,
464
460
  )
465
461
  )
462
+
463
+ def _filter_system_prompts(
464
+ self, messages: list[ModelMessage]
465
+ ) -> list[ModelMessage]:
466
+ """Filter out system prompts from messages for UI display.
467
+
468
+ Args:
469
+ messages: List of messages that may contain system prompts
470
+
471
+ Returns:
472
+ List of messages without system prompt parts
473
+ """
474
+ from pydantic_ai.messages import SystemPromptPart
475
+
476
+ filtered_messages: list[ModelMessage] = []
477
+ for msg in messages:
478
+ parts: Sequence[ModelRequestPart] | Sequence[ModelResponsePart] | None = (
479
+ msg.parts if hasattr(msg, "parts") else None
480
+ )
481
+ if not parts:
482
+ filtered_messages.append(msg)
483
+ continue
484
+
485
+ non_system_parts = [
486
+ part for part in parts if not isinstance(part, SystemPromptPart)
487
+ ]
488
+
489
+ if not non_system_parts:
490
+ # Skip messages made up entirely of system prompt parts (e.g. system message)
491
+ continue
492
+
493
+ if len(non_system_parts) == len(parts):
494
+ # Nothing was filtered – keep original message
495
+ filtered_messages.append(msg)
496
+ continue
497
+
498
+ if is_dataclass(msg):
499
+ filtered_messages.append(
500
+ # ignore types because of the convoluted Request | Response types
501
+ replace(msg, parts=cast(Any, non_system_parts))
502
+ )
503
+ else:
504
+ filtered_messages.append(msg)
505
+ return filtered_messages
506
+
507
+ def get_conversation_state(self) -> "ConversationState":
508
+ """Get the current conversation state.
509
+
510
+ Returns:
511
+ ConversationState object containing UI and agent messages and current type
512
+ """
513
+ from shotgun.agents.conversation_history import ConversationState
514
+
515
+ return ConversationState(
516
+ agent_messages=self.message_history.copy(),
517
+ agent_type=self._current_agent_type.value,
518
+ )
519
+
520
+ def restore_conversation_state(self, state: "ConversationState") -> None:
521
+ """Restore conversation state from a saved state.
522
+
523
+ Args:
524
+ state: ConversationState object to restore
525
+ """
526
+ # Restore message history for agents (includes system prompts)
527
+ self.message_history = state.agent_messages.copy()
528
+
529
+ # Filter out system prompts for UI display
530
+ self.ui_message_history = self._filter_system_prompts(state.agent_messages)
531
+
532
+ # Restore agent type
533
+ self._current_agent_type = AgentType(state.agent_type)
534
+
535
+ # Notify listeners about the restored messages
536
+ self._post_messages_updated()
537
+
538
+
539
+ # Re-export AgentType for backward compatibility
540
+ __all__ = [
541
+ "AgentManager",
542
+ "AgentType",
543
+ "MessageHistoryUpdated",
544
+ "PartialResponseMessage",
545
+ ]
@@ -19,10 +19,12 @@ from pydantic_ai.messages import (
19
19
  )
20
20
 
21
21
  from shotgun.agents.config import ProviderType, get_config_manager, get_provider_model
22
+ from shotgun.agents.models import AgentType
22
23
  from shotgun.logging_config import get_logger
23
24
  from shotgun.prompts import PromptLoader
24
- from shotgun.sdk.services import get_artifact_service, get_codebase_service
25
+ from shotgun.sdk.services import get_codebase_service
25
26
  from shotgun.utils import ensure_shotgun_directory_exists
27
+ from shotgun.utils.file_system_utils import get_shotgun_base_path
26
28
 
27
29
  from .history import token_limit_compactor
28
30
  from .history.compaction import apply_persistent_compaction
@@ -38,14 +40,7 @@ from .tools import (
38
40
  retrieve_code,
39
41
  write_file,
40
42
  )
41
- from .tools.artifact_management import (
42
- create_artifact,
43
- list_artifact_templates,
44
- list_artifacts,
45
- read_artifact,
46
- read_artifact_section,
47
- write_artifact_section,
48
- )
43
+ from .tools.file_management import AGENT_DIRECTORIES
49
44
 
50
45
  logger = get_logger(__name__)
51
46
 
@@ -67,18 +62,23 @@ async def add_system_status_message(
67
62
  Updated message history with system status message prepended
68
63
  """
69
64
  message_history = message_history or []
70
- codebase_understanding_graphs = await deps.codebase_service.list_graphs()
65
+ # Only show graphs for the current working directory
66
+ codebase_understanding_graphs = (
67
+ await deps.codebase_service.list_graphs_for_directory()
68
+ )
71
69
 
72
- # Collect artifact state information
73
- from .artifact_state import collect_artifact_state
70
+ # Get existing files for the agent
71
+ existing_files = get_agent_existing_files(deps.agent_mode)
74
72
 
75
- artifact_state = collect_artifact_state()
73
+ # Extract table of contents from the agent's markdown file
74
+ markdown_toc = extract_markdown_toc(deps.agent_mode)
76
75
 
77
76
  system_state = prompt_loader.render(
78
77
  "agents/state/system_state.j2",
79
78
  codebase_understanding_graphs=codebase_understanding_graphs,
80
79
  is_tui_context=deps.is_tui_context,
81
- **artifact_state,
80
+ existing_files=existing_files,
81
+ markdown_toc=markdown_toc,
82
82
  )
83
83
 
84
84
  message_history.append(
@@ -97,14 +97,17 @@ def create_base_agent(
97
97
  load_codebase_understanding_tools: bool = True,
98
98
  additional_tools: list[Any] | None = None,
99
99
  provider: ProviderType | None = None,
100
+ agent_mode: AgentType | None = None,
100
101
  ) -> tuple[Agent[AgentDeps, str | DeferredToolRequests], AgentDeps]:
101
102
  """Create a base agent with common configuration.
102
103
 
103
104
  Args:
104
105
  system_prompt_fn: Function that will be decorated as system_prompt
105
106
  agent_runtime_options: Agent runtime options for the agent
107
+ load_codebase_understanding_tools: Whether to load codebase understanding tools
106
108
  additional_tools: Optional list of additional tools
107
109
  provider: Optional provider override. If None, uses configured default
110
+ agent_mode: The mode of the agent (research, plan, tasks, specify, export)
108
111
 
109
112
  Returns:
110
113
  Tuple of (Configured Pydantic AI agent, Agent dependencies)
@@ -126,13 +129,12 @@ def create_base_agent(
126
129
 
127
130
  # Create deps with model config and services
128
131
  codebase_service = get_codebase_service()
129
- artifact_service = get_artifact_service()
130
132
  deps = AgentDeps(
131
133
  **agent_runtime_options.model_dump(),
132
134
  llm_model=model_config,
133
135
  codebase_service=codebase_service,
134
- artifact_service=artifact_service,
135
136
  system_prompt_fn=system_prompt_fn,
137
+ agent_mode=agent_mode,
136
138
  )
137
139
 
138
140
  except Exception as e:
@@ -180,14 +182,6 @@ def create_base_agent(
180
182
  agent.tool(append_file)
181
183
  agent.tool(read_file)
182
184
 
183
- # Register artifact management tools (always available)
184
- agent.tool(create_artifact)
185
- agent.tool(list_artifacts)
186
- agent.tool(list_artifact_templates)
187
- agent.tool(read_artifact)
188
- agent.tool(read_artifact_section)
189
- agent.tool(write_artifact_section)
190
-
191
185
  # Register codebase understanding tools (conditional)
192
186
  if load_codebase_understanding_tools:
193
187
  agent.tool(query_graph)
@@ -199,10 +193,134 @@ def create_base_agent(
199
193
  else:
200
194
  logger.debug("🚫🧠 Codebase understanding tools not registered")
201
195
 
202
- logger.debug("✅ Agent creation complete with artifact and codebase tools")
196
+ logger.debug("✅ Agent creation complete with codebase tools")
203
197
  return agent, deps
204
198
 
205
199
 
200
+ def extract_markdown_toc(agent_mode: AgentType | None) -> str | None:
201
+ """Extract table of contents from agent's markdown file.
202
+
203
+ Args:
204
+ agent_mode: The agent mode to extract TOC for
205
+
206
+ Returns:
207
+ Formatted TOC string (up to 2000 chars) or None if not applicable
208
+ """
209
+ # Skip for EXPORT mode or no mode
210
+ if (
211
+ not agent_mode
212
+ or agent_mode == AgentType.EXPORT
213
+ or agent_mode not in AGENT_DIRECTORIES
214
+ ):
215
+ return None
216
+
217
+ base_path = get_shotgun_base_path()
218
+ md_file = AGENT_DIRECTORIES[agent_mode]
219
+ md_path = base_path / md_file
220
+
221
+ # Check if the markdown file exists
222
+ if not md_path.exists():
223
+ return None
224
+
225
+ try:
226
+ content = md_path.read_text(encoding="utf-8")
227
+ lines = content.split("\n")
228
+
229
+ # Extract headings
230
+ toc_lines = []
231
+ for line in lines:
232
+ stripped = line.strip()
233
+ if stripped.startswith("#"):
234
+ # Count the heading level
235
+ level = 0
236
+ for char in stripped:
237
+ if char == "#":
238
+ level += 1
239
+ else:
240
+ break
241
+
242
+ # Get the heading text (remove the # symbols and clean up)
243
+ heading_text = stripped[level:].strip()
244
+ if heading_text:
245
+ # Add indentation based on level
246
+ indent = " " * (level - 1)
247
+ toc_lines.append(f"{indent}{'#' * level} {heading_text}")
248
+
249
+ if not toc_lines:
250
+ return None
251
+
252
+ # Join and truncate to 2000 characters
253
+ toc = "\n".join(toc_lines)
254
+ if len(toc) > 2000:
255
+ toc = toc[:1997] + "..."
256
+
257
+ return toc
258
+
259
+ except Exception as e:
260
+ logger.debug(f"Failed to extract TOC from {md_file}: {e}")
261
+ return None
262
+
263
+
264
+ def get_agent_existing_files(agent_mode: AgentType | None = None) -> list[str]:
265
+ """Get list of existing files for the given agent mode.
266
+
267
+ Args:
268
+ agent_mode: The agent mode to check files for. If None, lists all files.
269
+
270
+ Returns:
271
+ List of existing file paths relative to .shotgun directory
272
+ """
273
+ base_path = get_shotgun_base_path()
274
+ existing_files = []
275
+
276
+ # If no agent mode, list all files in base path and first level subdirectories
277
+ if agent_mode is None:
278
+ # List files in the root .shotgun directory
279
+ for item in base_path.iterdir():
280
+ if item.is_file():
281
+ existing_files.append(item.name)
282
+ elif item.is_dir():
283
+ # List files in first-level subdirectories
284
+ for subitem in item.iterdir():
285
+ if subitem.is_file():
286
+ relative_path = subitem.relative_to(base_path)
287
+ existing_files.append(str(relative_path))
288
+ return existing_files
289
+
290
+ # Handle specific agent modes
291
+ if agent_mode not in AGENT_DIRECTORIES:
292
+ return []
293
+
294
+ if agent_mode == AgentType.EXPORT:
295
+ # For export agent, list all files in exports directory
296
+ exports_dir = base_path / "exports"
297
+ if exports_dir.exists():
298
+ for file_path in exports_dir.rglob("*"):
299
+ if file_path.is_file():
300
+ relative_path = file_path.relative_to(base_path)
301
+ existing_files.append(str(relative_path))
302
+ else:
303
+ # For other agents, check both .md file and directory with same name
304
+ allowed_file = AGENT_DIRECTORIES[agent_mode]
305
+
306
+ # Check for the .md file
307
+ md_file_path = base_path / allowed_file
308
+ if md_file_path.exists():
309
+ existing_files.append(allowed_file)
310
+
311
+ # Check for directory with same base name (e.g., research/ for research.md)
312
+ base_name = allowed_file.replace(".md", "")
313
+ dir_path = base_path / base_name
314
+ if dir_path.exists() and dir_path.is_dir():
315
+ # List all files in the directory
316
+ for file_path in dir_path.rglob("*"):
317
+ if file_path.is_file():
318
+ relative_path = file_path.relative_to(base_path)
319
+ existing_files.append(str(relative_path))
320
+
321
+ return existing_files
322
+
323
+
206
324
  def build_agent_system_prompt(
207
325
  agent_type: str,
208
326
  ctx: RunContext[AgentDeps],
@@ -0,0 +1,56 @@
1
+ """Models and utilities for persisting TUI conversation history."""
2
+
3
+ from datetime import datetime
4
+ from typing import Any
5
+
6
+ from pydantic import BaseModel, ConfigDict, Field
7
+ from pydantic_ai.messages import (
8
+ ModelMessage,
9
+ ModelMessagesTypeAdapter,
10
+ )
11
+ from pydantic_core import to_jsonable_python
12
+
13
+
14
+ class ConversationState(BaseModel):
15
+ """Represents the complete state of a conversation in memory."""
16
+
17
+ agent_messages: list[ModelMessage]
18
+ agent_type: str # Will store AgentType.value
19
+
20
+ model_config = ConfigDict(arbitrary_types_allowed=True)
21
+
22
+
23
+ class ConversationHistory(BaseModel):
24
+ """Persistent conversation history for TUI sessions."""
25
+
26
+ version: int = 1
27
+ agent_history: list[dict[str, Any]] = Field(
28
+ default_factory=list
29
+ ) # Will store serialized ModelMessage objects
30
+ last_agent_model: str = "research"
31
+ updated_at: datetime = Field(default_factory=datetime.now)
32
+
33
+ model_config = ConfigDict(arbitrary_types_allowed=True)
34
+
35
+ def set_agent_messages(self, messages: list[ModelMessage]) -> None:
36
+ """Set agent_history from a list of ModelMessage objects.
37
+
38
+ Args:
39
+ messages: List of ModelMessage objects to serialize and store
40
+ """
41
+ # Serialize ModelMessage list to JSON-serializable format
42
+ self.agent_history = to_jsonable_python(
43
+ messages, fallback=lambda x: str(x), exclude_none=True
44
+ )
45
+
46
+ def get_agent_messages(self) -> list[ModelMessage]:
47
+ """Get agent_history as a list of ModelMessage objects.
48
+
49
+ Returns:
50
+ List of deserialized ModelMessage objects
51
+ """
52
+ if not self.agent_history:
53
+ return []
54
+
55
+ # Deserialize from JSON format back to ModelMessage objects
56
+ return ModelMessagesTypeAdapter.validate_python(self.agent_history)
@@ -0,0 +1,105 @@
1
+ """Manager for handling conversation persistence operations."""
2
+
3
+ import json
4
+ from pathlib import Path
5
+
6
+ from shotgun.logging_config import get_logger
7
+ from shotgun.utils import get_shotgun_home
8
+
9
+ from .conversation_history import ConversationHistory
10
+
11
+ logger = get_logger(__name__)
12
+
13
+
14
+ class ConversationManager:
15
+ """Handles saving and loading conversation history."""
16
+
17
+ def __init__(self, conversation_path: Path | None = None):
18
+ """Initialize ConversationManager.
19
+
20
+ Args:
21
+ conversation_path: Path to conversation file.
22
+ If None, uses default ~/.shotgun-sh/conversation.json
23
+ """
24
+ if conversation_path is None:
25
+ self.conversation_path = get_shotgun_home() / "conversation.json"
26
+ else:
27
+ self.conversation_path = conversation_path
28
+
29
+ def save(self, conversation: ConversationHistory) -> None:
30
+ """Save conversation history to file.
31
+
32
+ Args:
33
+ conversation: ConversationHistory to save
34
+ """
35
+ # Ensure directory exists
36
+ self.conversation_path.parent.mkdir(parents=True, exist_ok=True)
37
+
38
+ try:
39
+ # Update timestamp
40
+ from datetime import datetime
41
+
42
+ conversation.updated_at = datetime.now()
43
+
44
+ # Serialize to JSON using Pydantic's model_dump
45
+ data = conversation.model_dump(mode="json")
46
+
47
+ with open(self.conversation_path, "w", encoding="utf-8") as f:
48
+ json.dump(data, f, indent=2, ensure_ascii=False)
49
+
50
+ logger.debug("Conversation saved to %s", self.conversation_path)
51
+
52
+ except Exception as e:
53
+ logger.error(
54
+ "Failed to save conversation to %s: %s", self.conversation_path, e
55
+ )
56
+ # Don't raise - we don't want to interrupt the user's session
57
+
58
+ def load(self) -> ConversationHistory | None:
59
+ """Load conversation history from file.
60
+
61
+ Returns:
62
+ ConversationHistory if file exists and is valid, None otherwise
63
+ """
64
+ if not self.conversation_path.exists():
65
+ logger.debug("No conversation history found at %s", self.conversation_path)
66
+ return None
67
+
68
+ try:
69
+ with open(self.conversation_path, encoding="utf-8") as f:
70
+ data = json.load(f)
71
+
72
+ conversation = ConversationHistory.model_validate(data)
73
+ logger.debug(
74
+ "Conversation loaded from %s with %d agent messages",
75
+ self.conversation_path,
76
+ len(conversation.agent_history),
77
+ )
78
+ return conversation
79
+
80
+ except Exception as e:
81
+ logger.error(
82
+ "Failed to load conversation from %s: %s", self.conversation_path, e
83
+ )
84
+ return None
85
+
86
+ def clear(self) -> None:
87
+ """Delete the conversation history file."""
88
+ if self.conversation_path.exists():
89
+ try:
90
+ self.conversation_path.unlink()
91
+ logger.debug(
92
+ "Conversation history cleared at %s", self.conversation_path
93
+ )
94
+ except Exception as e:
95
+ logger.error(
96
+ "Failed to clear conversation at %s: %s", self.conversation_path, e
97
+ )
98
+
99
+ def exists(self) -> bool:
100
+ """Check if a conversation history file exists.
101
+
102
+ Returns:
103
+ True if conversation file exists, False otherwise
104
+ """
105
+ return self.conversation_path.exists()