shotgun-sh 0.1.10.dev1__tar.gz → 0.1.11.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.

Files changed (133) hide show
  1. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/PKG-INFO +1 -1
  2. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/pyproject.toml +1 -1
  3. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/agents/agent_manager.py +47 -0
  4. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/agents/history/compaction.py +15 -0
  5. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/agents/history/history_processors.py +51 -0
  6. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/cli/update.py +2 -3
  7. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/posthog_telemetry.py +18 -42
  8. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/sdk/codebase.py +49 -0
  9. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/sentry_telemetry.py +1 -3
  10. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/tui/screens/chat.py +9 -0
  11. shotgun_sh-0.1.11.dev1/src/shotgun/utils/source_detection.py +16 -0
  12. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/utils/update_checker.py +4 -7
  13. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/.gitignore +0 -0
  14. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/LICENSE +0 -0
  15. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/README.md +0 -0
  16. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/hatch_build.py +0 -0
  17. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/__init__.py +0 -0
  18. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/agents/__init__.py +0 -0
  19. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/agents/common.py +0 -0
  20. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/agents/config/__init__.py +0 -0
  21. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/agents/config/constants.py +0 -0
  22. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/agents/config/manager.py +0 -0
  23. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/agents/config/models.py +0 -0
  24. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/agents/config/provider.py +0 -0
  25. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/agents/conversation_history.py +0 -0
  26. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/agents/conversation_manager.py +0 -0
  27. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/agents/export.py +0 -0
  28. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/agents/history/__init__.py +0 -0
  29. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/agents/history/constants.py +0 -0
  30. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/agents/history/context_extraction.py +0 -0
  31. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/agents/history/history_building.py +0 -0
  32. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/agents/history/message_utils.py +0 -0
  33. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/agents/history/token_counting.py +0 -0
  34. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/agents/history/token_estimation.py +0 -0
  35. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/agents/messages.py +0 -0
  36. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/agents/models.py +0 -0
  37. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/agents/plan.py +0 -0
  38. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/agents/research.py +0 -0
  39. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/agents/specify.py +0 -0
  40. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/agents/tasks.py +0 -0
  41. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/agents/tools/__init__.py +0 -0
  42. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/agents/tools/codebase/__init__.py +0 -0
  43. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/agents/tools/codebase/codebase_shell.py +0 -0
  44. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/agents/tools/codebase/directory_lister.py +0 -0
  45. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/agents/tools/codebase/file_read.py +0 -0
  46. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/agents/tools/codebase/models.py +0 -0
  47. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/agents/tools/codebase/query_graph.py +0 -0
  48. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/agents/tools/codebase/retrieve_code.py +0 -0
  49. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/agents/tools/file_management.py +0 -0
  50. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/agents/tools/user_interaction.py +0 -0
  51. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/agents/tools/web_search/__init__.py +0 -0
  52. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/agents/tools/web_search/anthropic.py +0 -0
  53. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/agents/tools/web_search/gemini.py +0 -0
  54. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/agents/tools/web_search/openai.py +0 -0
  55. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/agents/tools/web_search/utils.py +0 -0
  56. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/build_constants.py +0 -0
  57. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/cli/__init__.py +0 -0
  58. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/cli/codebase/__init__.py +0 -0
  59. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/cli/codebase/commands.py +0 -0
  60. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/cli/codebase/models.py +0 -0
  61. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/cli/config.py +0 -0
  62. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/cli/export.py +0 -0
  63. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/cli/models.py +0 -0
  64. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/cli/plan.py +0 -0
  65. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/cli/research.py +0 -0
  66. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/cli/specify.py +0 -0
  67. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/cli/tasks.py +0 -0
  68. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/cli/utils.py +0 -0
  69. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/codebase/__init__.py +0 -0
  70. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/codebase/core/__init__.py +0 -0
  71. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/codebase/core/change_detector.py +0 -0
  72. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/codebase/core/code_retrieval.py +0 -0
  73. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/codebase/core/cypher_models.py +0 -0
  74. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/codebase/core/ingestor.py +0 -0
  75. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/codebase/core/language_config.py +0 -0
  76. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/codebase/core/manager.py +0 -0
  77. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/codebase/core/nl_query.py +0 -0
  78. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/codebase/core/parser_loader.py +0 -0
  79. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/codebase/models.py +0 -0
  80. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/codebase/service.py +0 -0
  81. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/logging_config.py +0 -0
  82. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/main.py +0 -0
  83. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/prompts/__init__.py +0 -0
  84. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/prompts/agents/__init__.py +0 -0
  85. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/prompts/agents/export.j2 +0 -0
  86. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/prompts/agents/partials/codebase_understanding.j2 +0 -0
  87. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/prompts/agents/partials/common_agent_system_prompt.j2 +0 -0
  88. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/prompts/agents/partials/content_formatting.j2 +0 -0
  89. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/prompts/agents/partials/interactive_mode.j2 +0 -0
  90. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/prompts/agents/plan.j2 +0 -0
  91. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/prompts/agents/research.j2 +0 -0
  92. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/prompts/agents/specify.j2 +0 -0
  93. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/prompts/agents/state/codebase/codebase_graphs_available.j2 +0 -0
  94. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/prompts/agents/state/system_state.j2 +0 -0
  95. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/prompts/agents/tasks.j2 +0 -0
  96. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/prompts/codebase/__init__.py +0 -0
  97. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/prompts/codebase/cypher_query_patterns.j2 +0 -0
  98. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/prompts/codebase/cypher_system.j2 +0 -0
  99. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/prompts/codebase/enhanced_query_context.j2 +0 -0
  100. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/prompts/codebase/partials/cypher_rules.j2 +0 -0
  101. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/prompts/codebase/partials/graph_schema.j2 +0 -0
  102. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/prompts/codebase/partials/temporal_context.j2 +0 -0
  103. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/prompts/history/__init__.py +0 -0
  104. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/prompts/history/incremental_summarization.j2 +0 -0
  105. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/prompts/history/summarization.j2 +0 -0
  106. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/prompts/loader.py +0 -0
  107. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/py.typed +0 -0
  108. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/sdk/__init__.py +0 -0
  109. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/sdk/exceptions.py +0 -0
  110. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/sdk/models.py +0 -0
  111. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/sdk/services.py +0 -0
  112. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/telemetry.py +0 -0
  113. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/tui/__init__.py +0 -0
  114. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/tui/app.py +0 -0
  115. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/tui/commands/__init__.py +0 -0
  116. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/tui/components/prompt_input.py +0 -0
  117. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/tui/components/spinner.py +0 -0
  118. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/tui/components/splash.py +0 -0
  119. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/tui/components/vertical_tail.py +0 -0
  120. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/tui/screens/chat.tcss +0 -0
  121. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/tui/screens/chat_screen/__init__.py +0 -0
  122. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/tui/screens/chat_screen/command_providers.py +0 -0
  123. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/tui/screens/chat_screen/hint_message.py +0 -0
  124. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/tui/screens/chat_screen/history.py +0 -0
  125. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/tui/screens/directory_setup.py +0 -0
  126. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/tui/screens/provider_config.py +0 -0
  127. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/tui/screens/splash.py +0 -0
  128. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/tui/styles.tcss +0 -0
  129. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/tui/utils/__init__.py +0 -0
  130. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/tui/utils/mode_progress.py +0 -0
  131. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/utils/__init__.py +0 -0
  132. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/utils/env_utils.py +0 -0
  133. {shotgun_sh-0.1.10.dev1 → shotgun_sh-0.1.11.dev1}/src/shotgun/utils/file_system_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: shotgun-sh
3
- Version: 0.1.10.dev1
3
+ Version: 0.1.11.dev1
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.10.dev1"
3
+ version = "0.1.11.dev1"
4
4
  description = "AI-powered research, planning, and task management CLI tool"
5
5
  readme = "README.md"
6
6
  license = { text = "MIT" }
@@ -37,7 +37,9 @@ from textual.widget import Widget
37
37
 
38
38
  from shotgun.agents.common import add_system_prompt_message, add_system_status_message
39
39
  from shotgun.agents.models import AgentType, FileOperation
40
+ from shotgun.posthog_telemetry import track_event
40
41
  from shotgun.tui.screens.chat_screen.hint_message import HintMessage
42
+ from shotgun.utils.source_detection import detect_source
41
43
 
42
44
  from .export import create_export_agent
43
45
  from .history.compaction import apply_persistent_compaction
@@ -351,6 +353,17 @@ class AgentManager(Widget):
351
353
  "gpt-5" in model_name.lower()
352
354
  )
353
355
 
356
+ # Track message send event
357
+ event_name = f"message_send_{self._current_agent_type.value}"
358
+ track_event(
359
+ event_name,
360
+ {
361
+ "has_prompt": prompt is not None,
362
+ "has_deferred_results": deferred_tool_results is not None,
363
+ "model_name": model_name,
364
+ },
365
+ )
366
+
354
367
  try:
355
368
  result: AgentRunResult[
356
369
  str | DeferredToolRequests
@@ -448,6 +461,22 @@ class AgentManager(Widget):
448
461
  self._post_partial_message(False)
449
462
 
450
463
  elif isinstance(event, FunctionToolCallEvent):
464
+ # Track tool call event
465
+
466
+ # Detect source from call stack
467
+ source = detect_source()
468
+
469
+ track_event(
470
+ "tool_called",
471
+ {
472
+ "tool_name": event.part.tool_name,
473
+ "agent_mode": self._current_agent_type.value
474
+ if self._current_agent_type
475
+ else "unknown",
476
+ "source": source,
477
+ },
478
+ )
479
+
451
480
  existing_call_idx = next(
452
481
  (
453
482
  i
@@ -477,6 +506,24 @@ class AgentManager(Widget):
477
506
  state.current_response = partial_message
478
507
  self._post_partial_message(False)
479
508
  elif isinstance(event, FunctionToolResultEvent):
509
+ # Track tool completion event
510
+
511
+ # Detect source from call stack
512
+ source = detect_source()
513
+
514
+ track_event(
515
+ "tool_completed",
516
+ {
517
+ "tool_name": event.result.tool_name
518
+ if hasattr(event.result, "tool_name")
519
+ else "unknown",
520
+ "agent_mode": self._current_agent_type.value
521
+ if self._current_agent_type
522
+ else "unknown",
523
+ "source": source,
524
+ },
525
+ )
526
+
480
527
  request_message = ModelRequest(parts=[event.result])
481
528
  state.messages.append(request_message)
482
529
  ## this is what the user responded with
@@ -5,6 +5,7 @@ from pydantic_ai.usage import RequestUsage
5
5
 
6
6
  from shotgun.agents.models import AgentDeps
7
7
  from shotgun.logging_config import get_logger
8
+ from shotgun.posthog_telemetry import track_event
8
9
 
9
10
  from .token_estimation import estimate_tokens_from_messages
10
11
 
@@ -57,6 +58,20 @@ async def apply_persistent_compaction(
57
58
  f"Persistent compaction applied: {original_size} → {compacted_size} messages "
58
59
  f"({reduction_pct:.1f}% reduction)"
59
60
  )
61
+
62
+ # Track persistent compaction event
63
+ track_event(
64
+ "persistent_compaction_applied",
65
+ {
66
+ "messages_before": original_size,
67
+ "messages_after": compacted_size,
68
+ "tokens_before": estimated_tokens,
69
+ "reduction_percentage": round(reduction_pct, 2),
70
+ "agent_mode": deps.agent_mode.value
71
+ if hasattr(deps, "agent_mode") and deps.agent_mode
72
+ else "unknown",
73
+ },
74
+ )
60
75
  else:
61
76
  logger.debug(
62
77
  f"No persistent compaction needed: {original_size} messages unchanged"
@@ -14,6 +14,7 @@ from shotgun.agents.config.models import shotgun_model_request
14
14
  from shotgun.agents.messages import AgentSystemPrompt, SystemStatusPrompt
15
15
  from shotgun.agents.models import AgentDeps
16
16
  from shotgun.logging_config import get_logger
17
+ from shotgun.posthog_telemetry import track_event
17
18
  from shotgun.prompts import PromptLoader
18
19
 
19
20
  from .constants import SUMMARY_MARKER, TOKEN_LIMIT_RATIO
@@ -179,6 +180,10 @@ async def token_limit_compactor(
179
180
  "Post-summary conversation exceeds threshold, performing incremental compaction"
180
181
  )
181
182
 
183
+ # Track compaction event
184
+ messages_before = len(messages)
185
+ tokens_before = post_summary_tokens
186
+
182
187
  # Extract existing summary content
183
188
  summary_message = messages[last_summary_index]
184
189
  existing_summary_part = None
@@ -320,6 +325,31 @@ async def token_limit_compactor(
320
325
  logger.debug(
321
326
  f"Incremental compaction complete: {len(messages)} -> {len(compacted_messages)} messages"
322
327
  )
328
+
329
+ # Track compaction completion
330
+ messages_after = len(compacted_messages)
331
+ tokens_after = estimate_tokens_from_messages(compacted_messages, deps.llm_model)
332
+ reduction_percentage = (
333
+ ((messages_before - messages_after) / messages_before * 100)
334
+ if messages_before > 0
335
+ else 0
336
+ )
337
+
338
+ track_event(
339
+ "context_compaction_triggered",
340
+ {
341
+ "compaction_type": "incremental",
342
+ "messages_before": messages_before,
343
+ "messages_after": messages_after,
344
+ "tokens_before": tokens_before,
345
+ "tokens_after": tokens_after,
346
+ "reduction_percentage": round(reduction_percentage, 2),
347
+ "agent_mode": deps.agent_mode.value
348
+ if hasattr(deps, "agent_mode") and deps.agent_mode
349
+ else "unknown",
350
+ },
351
+ )
352
+
323
353
  return compacted_messages
324
354
 
325
355
  else:
@@ -423,4 +453,25 @@ async def _full_compaction(
423
453
  # Ensure history ends with ModelRequest for PydanticAI compatibility
424
454
  compacted_messages = ensure_ends_with_model_request(compacted_messages, messages)
425
455
 
456
+ # Track full compaction event
457
+ messages_before = len(messages)
458
+ messages_after = len(compacted_messages)
459
+ tokens_before = current_tokens # Already calculated above
460
+ tokens_after = summary_usage.output_tokens if summary_usage else 0
461
+
462
+ track_event(
463
+ "context_compaction_triggered",
464
+ {
465
+ "compaction_type": "full",
466
+ "messages_before": messages_before,
467
+ "messages_after": messages_after,
468
+ "tokens_before": tokens_before,
469
+ "tokens_after": tokens_after,
470
+ "reduction_percentage": round(reduction_percentage, 2),
471
+ "agent_mode": deps.agent_mode.value
472
+ if hasattr(deps, "agent_mode") and deps.agent_mode
473
+ else "unknown",
474
+ },
475
+ )
476
+
426
477
  return compacted_messages
@@ -6,8 +6,10 @@ import typer
6
6
  from rich.console import Console
7
7
  from rich.progress import Progress, SpinnerColumn, TextColumn
8
8
 
9
+ from shotgun import __version__
9
10
  from shotgun.logging_config import get_logger
10
11
  from shotgun.utils.update_checker import (
12
+ compare_versions,
11
13
  detect_installation_method,
12
14
  get_latest_version,
13
15
  is_dev_version,
@@ -70,9 +72,6 @@ def update(
70
72
  )
71
73
  raise typer.Exit(1)
72
74
 
73
- from shotgun import __version__
74
- from shotgun.utils.update_checker import compare_versions
75
-
76
75
  if compare_versions(__version__, latest):
77
76
  console.print(
78
77
  f"[green]✓[/green] Update available: [cyan]{__version__}[/cyan] → [green]{latest}[/green]",
@@ -1,8 +1,11 @@
1
1
  """PostHog analytics setup for Shotgun."""
2
2
 
3
- import os
4
3
  from typing import Any
5
4
 
5
+ import posthog
6
+
7
+ from shotgun import __version__
8
+ from shotgun.agents.config import get_config_manager
6
9
  from shotgun.logging_config import get_early_logger
7
10
 
8
11
  # Use early logger to prevent automatic StreamHandler creation
@@ -21,41 +24,15 @@ def setup_posthog_observability() -> bool:
21
24
  global _posthog_client
22
25
 
23
26
  try:
24
- import posthog
25
-
26
27
  # Check if PostHog is already initialized
27
28
  if _posthog_client is not None:
28
29
  logger.debug("PostHog is already initialized, skipping")
29
30
  return True
30
31
 
31
- # Try to get API key from build constants first (production builds)
32
- api_key = None
33
-
34
- try:
35
- from shotgun import build_constants
36
-
37
- api_key = build_constants.POSTHOG_API_KEY
38
- if api_key:
39
- logger.debug("Using PostHog configuration from build constants")
40
- except (ImportError, AttributeError):
41
- pass
42
-
43
- # Fallback to environment variables if build constants are empty or missing
44
- if not api_key:
45
- api_key = os.getenv("POSTHOG_API_KEY", "")
46
- if api_key:
47
- logger.debug("Using PostHog configuration from environment variables")
48
-
49
- if not api_key:
50
- logger.debug(
51
- "No PostHog API key configured, skipping PostHog initialization"
52
- )
53
- return False
54
-
55
- logger.debug("Found PostHog configuration, proceeding with setup")
32
+ # Hardcoded PostHog configuration
33
+ api_key = "phc_KKnChzZUKeNqZDOTJ6soCBWNQSx3vjiULdwTR9H5Mcr"
56
34
 
57
- # Get version for context
58
- from shotgun import __version__
35
+ logger.debug("Using hardcoded PostHog configuration")
59
36
 
60
37
  # Determine environment based on version
61
38
  # Dev versions contain "dev", "rc", "alpha", or "beta"
@@ -73,20 +50,25 @@ def setup_posthog_observability() -> bool:
73
50
 
74
51
  # Set user context with anonymous user ID from config
75
52
  try:
76
- from shotgun.agents.config import get_config_manager
77
-
78
53
  config_manager = get_config_manager()
79
54
  user_id = config_manager.get_user_id()
80
55
 
56
+ # Identify the user in PostHog
57
+ posthog.identify( # type: ignore[attr-defined]
58
+ distinct_id=user_id,
59
+ properties={
60
+ "version": __version__,
61
+ "environment": environment,
62
+ },
63
+ )
64
+
81
65
  # Set default properties for all events
82
66
  posthog.disabled = False
83
67
  posthog.personal_api_key = None # Not needed for event tracking
84
68
 
85
- logger.debug(
86
- "PostHog user context will be set with anonymous ID: %s", user_id
87
- )
69
+ logger.debug("PostHog user identified with anonymous ID: %s", user_id)
88
70
  except Exception as e:
89
- logger.warning("Failed to get user context: %s", e)
71
+ logger.warning("Failed to set user context: %s", e)
90
72
 
91
73
  logger.debug(
92
74
  "PostHog analytics configured successfully (environment: %s, version: %s)",
@@ -95,9 +77,6 @@ def setup_posthog_observability() -> bool:
95
77
  )
96
78
  return True
97
79
 
98
- except ImportError as e:
99
- logger.error("PostHog SDK not available: %s", e)
100
- return False
101
80
  except Exception as e:
102
81
  logger.warning("Failed to setup PostHog analytics: %s", e)
103
82
  return False
@@ -117,9 +96,6 @@ def track_event(event_name: str, properties: dict[str, Any] | None = None) -> No
117
96
  return
118
97
 
119
98
  try:
120
- from shotgun import __version__
121
- from shotgun.agents.config import get_config_manager
122
-
123
99
  # Get user ID for tracking
124
100
  config_manager = get_config_manager()
125
101
  user_id = config_manager.get_user_id()
@@ -5,6 +5,9 @@ from collections.abc import Awaitable, Callable
5
5
  from pathlib import Path
6
6
 
7
7
  from shotgun.codebase.models import CodebaseGraph, QueryType
8
+ from shotgun.logging_config import get_logger
9
+ from shotgun.posthog_telemetry import track_event
10
+ from shotgun.utils.source_detection import detect_source
8
11
 
9
12
  from .exceptions import CodebaseNotFoundError, InvalidPathError
10
13
  from .models import (
@@ -17,6 +20,8 @@ from .models import (
17
20
  )
18
21
  from .services import get_codebase_service
19
22
 
23
+ logger = get_logger(__name__)
24
+
20
25
 
21
26
  class CodebaseSDK:
22
27
  """Framework-agnostic SDK for codebase operations.
@@ -87,6 +92,28 @@ class CodebaseSDK:
87
92
  )
88
93
  file_count = sum(graph.language_stats.values()) if graph.language_stats else 0
89
94
 
95
+ # Track codebase indexing event
96
+ # Detect if called from TUI by checking the call stack
97
+ source = detect_source()
98
+
99
+ logger.debug(
100
+ "Tracking codebase_indexed event: file_count=%d, node_count=%d, relationship_count=%d, source=%s",
101
+ file_count,
102
+ graph.node_count,
103
+ graph.relationship_count,
104
+ source,
105
+ )
106
+
107
+ track_event(
108
+ "codebase_indexed",
109
+ {
110
+ "file_count": file_count,
111
+ "node_count": graph.node_count,
112
+ "relationship_count": graph.relationship_count,
113
+ "source": source,
114
+ },
115
+ )
116
+
90
117
  return IndexResult(
91
118
  graph_id=graph.graph_id,
92
119
  name=name,
@@ -212,6 +239,28 @@ class CodebaseSDK:
212
239
 
213
240
  stats = await self.service.reindex_graph(graph_id)
214
241
 
242
+ # Track codebase reindexing event
243
+ # Detect if called from TUI by checking the call stack
244
+ source = detect_source()
245
+
246
+ logger.debug(
247
+ "Tracking codebase_reindexed event: nodes_added=%d, nodes_removed=%d, source=%s",
248
+ stats.get("nodes_added", 0),
249
+ stats.get("nodes_removed", 0),
250
+ source,
251
+ )
252
+
253
+ track_event(
254
+ "codebase_reindexed",
255
+ {
256
+ "nodes_added": stats.get("nodes_added", 0),
257
+ "nodes_removed": stats.get("nodes_removed", 0),
258
+ "relationships_added": stats.get("relationships_added", 0),
259
+ "relationships_removed": stats.get("relationships_removed", 0),
260
+ "source": source,
261
+ },
262
+ )
263
+
215
264
  return ReindexResult(
216
265
  graph_id=graph_id,
217
266
  name=graph.name,
@@ -2,6 +2,7 @@
2
2
 
3
3
  import os
4
4
 
5
+ from shotgun import __version__
5
6
  from shotgun.logging_config import get_early_logger
6
7
 
7
8
  # Use early logger to prevent automatic StreamHandler creation
@@ -41,9 +42,6 @@ def setup_sentry_observability() -> bool:
41
42
 
42
43
  logger.debug("Found DSN, proceeding with Sentry setup")
43
44
 
44
- # Get version for release tracking
45
- from shotgun import __version__
46
-
47
45
  # Determine environment based on version
48
46
  # Dev versions contain "dev", "rc", "alpha", or "beta"
49
47
  if any(marker in __version__ for marker in ["dev", "rc", "alpha", "beta"]):
@@ -41,6 +41,7 @@ from shotgun.agents.models import (
41
41
  UserQuestion,
42
42
  )
43
43
  from shotgun.codebase.core.manager import CodebaseAlreadyIndexedError
44
+ from shotgun.posthog_telemetry import track_event
44
45
  from shotgun.sdk.codebase import CodebaseSDK
45
46
  from shotgun.sdk.exceptions import CodebaseNotFoundError, InvalidPathError
46
47
  from shotgun.sdk.services import get_codebase_service
@@ -388,6 +389,14 @@ class ChatScreen(Screen[None]):
388
389
  """Handle key presses for cancellation."""
389
390
  # If escape is pressed while agent is working, cancel the operation
390
391
  if event.key == "escape" and self.working and self._current_worker:
392
+ # Track ESC cancellation event
393
+ track_event(
394
+ "agent_cancelled_escape",
395
+ {
396
+ "agent_mode": self.mode.value,
397
+ },
398
+ )
399
+
391
400
  # Cancel the running agent worker
392
401
  self._current_worker.cancel()
393
402
  # Show cancellation message
@@ -0,0 +1,16 @@
1
+ """Utility for detecting the source of function calls (CLI vs TUI)."""
2
+
3
+ import inspect
4
+
5
+
6
+ def detect_source() -> str:
7
+ """Detect if the call originated from CLI or TUI by inspecting the call stack.
8
+
9
+ Returns:
10
+ "tui" if any frame in the call stack contains "tui" in the filename,
11
+ "cli" otherwise.
12
+ """
13
+ for frame_info in inspect.stack():
14
+ if "tui" in frame_info.filename.lower():
15
+ return "tui"
16
+ return "cli"
@@ -5,6 +5,10 @@ import sys
5
5
  import threading
6
6
  from pathlib import Path
7
7
 
8
+ import httpx
9
+ from packaging import version
10
+
11
+ from shotgun import __version__
8
12
  from shotgun.logging_config import get_logger
9
13
 
10
14
  logger = get_logger(__name__)
@@ -110,13 +114,6 @@ def perform_auto_update_async(no_update_check: bool = False) -> threading.Thread
110
114
  return thread
111
115
 
112
116
 
113
- # Keep these for backward compatibility with the update CLI command
114
- import httpx # noqa: E402
115
- from packaging import version # noqa: E402
116
-
117
- from shotgun import __version__ # noqa: E402
118
-
119
-
120
117
  def is_dev_version(version_str: str | None = None) -> bool:
121
118
  """Check if the current or given version is a development version.
122
119