shotgun-sh 0.1.12.dev2__tar.gz → 0.1.12.dev4__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 (134) hide show
  1. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/PKG-INFO +1 -1
  2. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/pyproject.toml +1 -1
  3. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/codebase/core/manager.py +13 -1
  4. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/codebase/service.py +4 -0
  5. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/main.py +9 -1
  6. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/tui/app.py +4 -0
  7. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/tui/screens/chat.py +12 -4
  8. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/tui/screens/chat_screen/history.py +84 -12
  9. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/.gitignore +0 -0
  10. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/LICENSE +0 -0
  11. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/README.md +0 -0
  12. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/hatch_build.py +0 -0
  13. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/__init__.py +0 -0
  14. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/agents/__init__.py +0 -0
  15. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/agents/agent_manager.py +0 -0
  16. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/agents/common.py +0 -0
  17. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/agents/config/__init__.py +0 -0
  18. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/agents/config/constants.py +0 -0
  19. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/agents/config/manager.py +0 -0
  20. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/agents/config/models.py +0 -0
  21. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/agents/config/provider.py +0 -0
  22. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/agents/conversation_history.py +0 -0
  23. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/agents/conversation_manager.py +0 -0
  24. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/agents/export.py +0 -0
  25. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/agents/history/__init__.py +0 -0
  26. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/agents/history/compaction.py +0 -0
  27. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/agents/history/constants.py +0 -0
  28. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/agents/history/context_extraction.py +0 -0
  29. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/agents/history/history_building.py +0 -0
  30. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/agents/history/history_processors.py +0 -0
  31. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/agents/history/message_utils.py +0 -0
  32. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/agents/history/token_counting.py +0 -0
  33. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/agents/history/token_estimation.py +0 -0
  34. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/agents/messages.py +0 -0
  35. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/agents/models.py +0 -0
  36. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/agents/plan.py +0 -0
  37. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/agents/research.py +0 -0
  38. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/agents/specify.py +0 -0
  39. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/agents/tasks.py +0 -0
  40. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/agents/tools/__init__.py +0 -0
  41. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/agents/tools/codebase/__init__.py +0 -0
  42. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/agents/tools/codebase/codebase_shell.py +0 -0
  43. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/agents/tools/codebase/directory_lister.py +0 -0
  44. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/agents/tools/codebase/file_read.py +0 -0
  45. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/agents/tools/codebase/models.py +0 -0
  46. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/agents/tools/codebase/query_graph.py +0 -0
  47. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/agents/tools/codebase/retrieve_code.py +0 -0
  48. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/agents/tools/file_management.py +0 -0
  49. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/agents/tools/user_interaction.py +0 -0
  50. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/agents/tools/web_search/__init__.py +0 -0
  51. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/agents/tools/web_search/anthropic.py +0 -0
  52. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/agents/tools/web_search/gemini.py +0 -0
  53. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/agents/tools/web_search/openai.py +0 -0
  54. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/agents/tools/web_search/utils.py +0 -0
  55. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/build_constants.py +0 -0
  56. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/cli/__init__.py +0 -0
  57. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/cli/codebase/__init__.py +0 -0
  58. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/cli/codebase/commands.py +0 -0
  59. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/cli/codebase/models.py +0 -0
  60. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/cli/config.py +0 -0
  61. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/cli/export.py +0 -0
  62. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/cli/models.py +0 -0
  63. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/cli/plan.py +0 -0
  64. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/cli/research.py +0 -0
  65. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/cli/specify.py +0 -0
  66. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/cli/tasks.py +0 -0
  67. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/cli/update.py +0 -0
  68. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/cli/utils.py +0 -0
  69. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/codebase/__init__.py +0 -0
  70. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/codebase/core/__init__.py +0 -0
  71. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/codebase/core/change_detector.py +0 -0
  72. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/codebase/core/code_retrieval.py +0 -0
  73. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/codebase/core/cypher_models.py +0 -0
  74. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/codebase/core/ingestor.py +0 -0
  75. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/codebase/core/language_config.py +0 -0
  76. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/codebase/core/nl_query.py +0 -0
  77. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/codebase/core/parser_loader.py +0 -0
  78. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/codebase/models.py +0 -0
  79. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/logging_config.py +0 -0
  80. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/posthog_telemetry.py +0 -0
  81. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/prompts/__init__.py +0 -0
  82. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/prompts/agents/__init__.py +0 -0
  83. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/prompts/agents/export.j2 +0 -0
  84. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/prompts/agents/partials/codebase_understanding.j2 +0 -0
  85. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/prompts/agents/partials/common_agent_system_prompt.j2 +0 -0
  86. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/prompts/agents/partials/content_formatting.j2 +0 -0
  87. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/prompts/agents/partials/interactive_mode.j2 +0 -0
  88. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/prompts/agents/plan.j2 +0 -0
  89. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/prompts/agents/research.j2 +0 -0
  90. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/prompts/agents/specify.j2 +0 -0
  91. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/prompts/agents/state/codebase/codebase_graphs_available.j2 +0 -0
  92. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/prompts/agents/state/system_state.j2 +0 -0
  93. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/prompts/agents/tasks.j2 +0 -0
  94. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/prompts/codebase/__init__.py +0 -0
  95. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/prompts/codebase/cypher_query_patterns.j2 +0 -0
  96. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/prompts/codebase/cypher_system.j2 +0 -0
  97. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/prompts/codebase/enhanced_query_context.j2 +0 -0
  98. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/prompts/codebase/partials/cypher_rules.j2 +0 -0
  99. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/prompts/codebase/partials/graph_schema.j2 +0 -0
  100. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/prompts/codebase/partials/temporal_context.j2 +0 -0
  101. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/prompts/history/__init__.py +0 -0
  102. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/prompts/history/incremental_summarization.j2 +0 -0
  103. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/prompts/history/summarization.j2 +0 -0
  104. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/prompts/loader.py +0 -0
  105. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/py.typed +0 -0
  106. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/sdk/__init__.py +0 -0
  107. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/sdk/codebase.py +0 -0
  108. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/sdk/exceptions.py +0 -0
  109. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/sdk/models.py +0 -0
  110. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/sdk/services.py +0 -0
  111. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/sentry_telemetry.py +0 -0
  112. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/telemetry.py +0 -0
  113. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/tui/__init__.py +0 -0
  114. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/tui/commands/__init__.py +0 -0
  115. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/tui/components/prompt_input.py +0 -0
  116. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/tui/components/spinner.py +0 -0
  117. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/tui/components/splash.py +0 -0
  118. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/tui/components/vertical_tail.py +0 -0
  119. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/tui/filtered_codebase_service.py +0 -0
  120. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/tui/screens/chat.tcss +0 -0
  121. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/tui/screens/chat_screen/__init__.py +0 -0
  122. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/tui/screens/chat_screen/command_providers.py +0 -0
  123. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/tui/screens/chat_screen/hint_message.py +0 -0
  124. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/tui/screens/directory_setup.py +0 -0
  125. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/tui/screens/provider_config.py +0 -0
  126. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/tui/screens/splash.py +0 -0
  127. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/tui/styles.tcss +0 -0
  128. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/tui/utils/__init__.py +0 -0
  129. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/tui/utils/mode_progress.py +0 -0
  130. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/utils/__init__.py +0 -0
  131. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/utils/env_utils.py +0 -0
  132. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/utils/file_system_utils.py +0 -0
  133. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/src/shotgun/utils/source_detection.py +0 -0
  134. {shotgun_sh-0.1.12.dev2 → shotgun_sh-0.1.12.dev4}/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.12.dev2
3
+ Version: 0.1.12.dev4
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
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "shotgun-sh"
3
- version = "0.1.12.dev2"
3
+ version = "0.1.12.dev4"
4
4
  description = "AI-powered research, planning, and task management CLI tool"
5
5
  readme = "README.md"
6
6
  license = { text = "MIT" }
@@ -353,7 +353,19 @@ class CodebaseGraphManager:
353
353
 
354
354
  # Check if graph already exists
355
355
  if graph_path.exists():
356
- raise CodebaseAlreadyIndexedError(repo_path)
356
+ # Verify it's not corrupted by checking if we can load the Project node
357
+ existing_graph = await self.get_graph(graph_id)
358
+ if existing_graph:
359
+ # Valid existing graph
360
+ raise CodebaseAlreadyIndexedError(repo_path)
361
+ else:
362
+ # Corrupted database - remove and re-index
363
+ logger.warning(
364
+ f"Found corrupted database at {graph_path}, removing for re-indexing..."
365
+ )
366
+ import shutil
367
+
368
+ shutil.rmtree(graph_path)
357
369
 
358
370
  # Import the builder from local core module
359
371
  from shotgun.codebase.core import CodebaseIngestor
@@ -69,6 +69,10 @@ class CodebaseService:
69
69
  # Otherwise, check if current directory is in the allowed list
70
70
  elif target_path in graph.indexed_from_cwds:
71
71
  filtered_graphs.append(graph)
72
+ # Also allow access if current directory IS the repository itself
73
+ # Use Path.resolve() for robust comparison (handles symlinks, etc.)
74
+ elif Path(target_path).resolve() == Path(graph.repo_path).resolve():
75
+ filtered_graphs.append(graph)
72
76
 
73
77
  return filtered_graphs
74
78
 
@@ -118,7 +118,15 @@ def main(
118
118
 
119
119
  if ctx.invoked_subcommand is None and not ctx.resilient_parsing:
120
120
  logger.debug("Launching shotgun TUI application")
121
- tui_app.run(no_update_check=no_update_check, continue_session=continue_session)
121
+ try:
122
+ tui_app.run(
123
+ no_update_check=no_update_check, continue_session=continue_session
124
+ )
125
+ finally:
126
+ # Ensure PostHog is shut down cleanly even if TUI exits unexpectedly
127
+ from shotgun.posthog_telemetry import shutdown
128
+
129
+ shutdown()
122
130
  raise typer.Exit()
123
131
 
124
132
  # For CLI commands, register PostHog shutdown handler
@@ -83,6 +83,10 @@ class ShotgunApp(App[None]):
83
83
 
84
84
  async def action_quit(self) -> None:
85
85
  """Quit the application."""
86
+ # Shut down PostHog client to prevent threading errors
87
+ from shotgun.posthog_telemetry import shutdown
88
+
89
+ shutdown()
86
90
  self.exit()
87
91
 
88
92
  def get_system_commands(self, screen: Screen[Any]) -> Iterable[SystemCommand]:
@@ -16,6 +16,7 @@ from textual import events, on, work
16
16
  from textual.app import ComposeResult
17
17
  from textual.command import CommandPalette
18
18
  from textual.containers import Container, Grid
19
+ from textual.keys import Keys
19
20
  from textual.reactive import reactive
20
21
  from textual.screen import ModalScreen, Screen
21
22
  from textual.widget import Widget
@@ -285,13 +286,18 @@ class ChatScreen(Screen[None]):
285
286
 
286
287
  async def on_key(self, event: events.Key) -> None:
287
288
  """Handle key presses for cancellation."""
288
- # If escape is pressed while agent is working, cancel the operation
289
- if event.key == "escape" and self.working and self._current_worker:
290
- # Track ESC cancellation event
289
+ # If escape or ctrl+c is pressed while agent is working, cancel the operation
290
+ if (
291
+ event.key in (Keys.Escape, Keys.ControlC)
292
+ and self.working
293
+ and self._current_worker
294
+ ):
295
+ # Track cancellation event
291
296
  track_event(
292
- "agent_cancelled_escape",
297
+ "agent_cancelled",
293
298
  {
294
299
  "agent_mode": self.mode.value,
300
+ "cancel_key": event.key,
295
301
  },
296
302
  )
297
303
 
@@ -302,6 +308,8 @@ class ChatScreen(Screen[None]):
302
308
  # Re-enable the input
303
309
  prompt_input = self.query_one(PromptInput)
304
310
  prompt_input.focus()
311
+ # Prevent the event from propagating (don't quit the app)
312
+ event.stop()
305
313
 
306
314
  @work
307
315
  async def check_if_codebase_is_indexed(self) -> None:
@@ -238,25 +238,97 @@ class AgentResponseWidget(Widget):
238
238
  continue
239
239
  return acc.strip()
240
240
 
241
+ def _truncate(self, text: str, max_length: int = 100) -> str:
242
+ """Truncate text to max_length characters, adding ellipsis if needed."""
243
+ if len(text) <= max_length:
244
+ return text
245
+ return text[: max_length - 3] + "..."
246
+
247
+ def _parse_args(self, args: dict[str, object] | str | None) -> dict[str, object]:
248
+ """Parse tool call arguments, handling both dict and JSON string formats."""
249
+ if args is None:
250
+ return {}
251
+ if isinstance(args, str):
252
+ try:
253
+ return json.loads(args) if args.strip() else {}
254
+ except json.JSONDecodeError:
255
+ return {}
256
+ return args if isinstance(args, dict) else {}
257
+
241
258
  def _format_tool_call_part(self, part: ToolCallPart) -> str:
242
259
  if part.tool_name == "ask_user":
243
260
  return self._format_ask_user_part(part)
261
+
262
+ # Parse args once (handles both JSON string and dict)
263
+ args = self._parse_args(part.args)
264
+
265
+ # Codebase tools - show friendly names
266
+ if part.tool_name == "query_graph":
267
+ if "query" in args:
268
+ query = self._truncate(str(args["query"]))
269
+ return f'Querying code: "{query}"'
270
+ return "Querying code"
271
+
272
+ if part.tool_name == "retrieve_code":
273
+ if "qualified_name" in args:
274
+ return f'Retrieving code: "{args["qualified_name"]}"'
275
+ return "Retrieving code"
276
+
277
+ if part.tool_name == "file_read":
278
+ if "file_path" in args:
279
+ return f'Reading file: "{args["file_path"]}"'
280
+ return "Reading file"
281
+
282
+ if part.tool_name == "directory_lister":
283
+ if "directory" in args:
284
+ return f'Listing directory: "{args["directory"]}"'
285
+ return "Listing directory"
286
+
287
+ if part.tool_name == "codebase_shell":
288
+ command = args.get("command", "")
289
+ cmd_args = args.get("args", [])
290
+ # Handle cmd_args as list of strings
291
+ if isinstance(cmd_args, list):
292
+ args_str = " ".join(str(arg) for arg in cmd_args)
293
+ else:
294
+ args_str = ""
295
+ full_cmd = f"{command} {args_str}".strip()
296
+ if full_cmd:
297
+ return f'Running shell: "{self._truncate(full_cmd)}"'
298
+ return "Running shell"
299
+
300
+ # File management tools
301
+ if part.tool_name == "read_file":
302
+ if "filename" in args:
303
+ return f'Reading file: "{args["filename"]}"'
304
+ return "Reading file"
305
+
306
+ # Web search tools
307
+ if part.tool_name in [
308
+ "openai_web_search_tool",
309
+ "anthropic_web_search_tool",
310
+ "gemini_web_search_tool",
311
+ ]:
312
+ if "query" in args:
313
+ query = self._truncate(str(args["query"]))
314
+ return f'Searching web: "{query}"'
315
+ return "Searching web"
316
+
244
317
  # write_file
245
318
  if part.tool_name == "write_file" or part.tool_name == "append_file":
246
- if isinstance(part.args, dict) and "filename" in part.args:
247
- return f"{part.tool_name}({part.args['filename']})"
248
- else:
249
- return f"{part.tool_name}()"
319
+ if "filename" in args:
320
+ return f"{part.tool_name}({args['filename']})"
321
+ return f"{part.tool_name}()"
322
+
250
323
  if part.tool_name == "write_artifact_section":
251
- if isinstance(part.args, dict) and "section_title" in part.args:
252
- return f"{part.tool_name}({part.args['section_title']})"
253
- else:
254
- return f"{part.tool_name}()"
324
+ if "section_title" in args:
325
+ return f"{part.tool_name}({args['section_title']})"
326
+ return f"{part.tool_name}()"
327
+
255
328
  if part.tool_name == "create_artifact":
256
- if isinstance(part.args, dict) and "name" in part.args:
257
- return f"{part.tool_name}({part.args['name']})"
258
- else:
259
- return f"▪ {part.tool_name}()"
329
+ if "name" in args:
330
+ return f"{part.tool_name}({args['name']})"
331
+ return f"▪ {part.tool_name}()"
260
332
 
261
333
  return f"{part.tool_name}({part.args})"
262
334