shotgun-sh 0.1.0.dev24__tar.gz → 0.1.0.dev26__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.0.dev24 → shotgun_sh-0.1.0.dev26}/PKG-INFO +1 -1
  2. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/pyproject.toml +1 -1
  3. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/agents/agent_manager.py +24 -8
  4. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/agents/common.py +112 -27
  5. shotgun_sh-0.1.0.dev26/src/shotgun/agents/conversation_history.py +106 -0
  6. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/agents/models.py +17 -0
  7. shotgun_sh-0.1.0.dev26/src/shotgun/prompts/agents/export.j2 +214 -0
  8. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/prompts/agents/partials/common_agent_system_prompt.j2 +1 -1
  9. shotgun_sh-0.1.0.dev26/src/shotgun/prompts/agents/plan.j2 +144 -0
  10. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/prompts/agents/research.j2 +21 -0
  11. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/prompts/agents/specify.j2 +23 -0
  12. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/prompts/agents/state/system_state.j2 +5 -3
  13. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/prompts/agents/tasks.j2 +23 -0
  14. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/tui/screens/chat.py +10 -14
  15. shotgun_sh-0.1.0.dev26/src/shotgun/tui/screens/chat_screen/hint_message.py +40 -0
  16. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/tui/screens/chat_screen/history.py +6 -2
  17. shotgun_sh-0.1.0.dev24/src/shotgun/agents/conversation_history.py +0 -56
  18. shotgun_sh-0.1.0.dev24/src/shotgun/prompts/agents/export.j2 +0 -86
  19. shotgun_sh-0.1.0.dev24/src/shotgun/prompts/agents/plan.j2 +0 -51
  20. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/.gitignore +0 -0
  21. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/LICENSE +0 -0
  22. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/README.md +0 -0
  23. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/hatch_build.py +0 -0
  24. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/__init__.py +0 -0
  25. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/agents/__init__.py +0 -0
  26. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/agents/config/__init__.py +0 -0
  27. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/agents/config/constants.py +0 -0
  28. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/agents/config/manager.py +0 -0
  29. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/agents/config/models.py +0 -0
  30. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/agents/config/provider.py +0 -0
  31. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/agents/conversation_manager.py +0 -0
  32. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/agents/export.py +0 -0
  33. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/agents/history/__init__.py +0 -0
  34. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/agents/history/compaction.py +0 -0
  35. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/agents/history/constants.py +0 -0
  36. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/agents/history/context_extraction.py +0 -0
  37. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/agents/history/history_building.py +0 -0
  38. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/agents/history/history_processors.py +0 -0
  39. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/agents/history/message_utils.py +0 -0
  40. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/agents/history/token_counting.py +0 -0
  41. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/agents/history/token_estimation.py +0 -0
  42. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/agents/plan.py +0 -0
  43. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/agents/research.py +0 -0
  44. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/agents/specify.py +0 -0
  45. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/agents/tasks.py +0 -0
  46. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/agents/tools/__init__.py +0 -0
  47. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/agents/tools/codebase/__init__.py +0 -0
  48. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/agents/tools/codebase/codebase_shell.py +0 -0
  49. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/agents/tools/codebase/directory_lister.py +0 -0
  50. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/agents/tools/codebase/file_read.py +0 -0
  51. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/agents/tools/codebase/models.py +0 -0
  52. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/agents/tools/codebase/query_graph.py +0 -0
  53. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/agents/tools/codebase/retrieve_code.py +0 -0
  54. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/agents/tools/file_management.py +0 -0
  55. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/agents/tools/user_interaction.py +0 -0
  56. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/agents/tools/web_search/__init__.py +0 -0
  57. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/agents/tools/web_search/anthropic.py +0 -0
  58. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/agents/tools/web_search/gemini.py +0 -0
  59. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/agents/tools/web_search/openai.py +0 -0
  60. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/agents/tools/web_search/utils.py +0 -0
  61. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/build_constants.py +0 -0
  62. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/cli/__init__.py +0 -0
  63. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/cli/codebase/__init__.py +0 -0
  64. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/cli/codebase/commands.py +0 -0
  65. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/cli/codebase/models.py +0 -0
  66. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/cli/config.py +0 -0
  67. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/cli/export.py +0 -0
  68. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/cli/models.py +0 -0
  69. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/cli/plan.py +0 -0
  70. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/cli/research.py +0 -0
  71. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/cli/specify.py +0 -0
  72. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/cli/tasks.py +0 -0
  73. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/cli/update.py +0 -0
  74. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/cli/utils.py +0 -0
  75. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/codebase/__init__.py +0 -0
  76. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/codebase/core/__init__.py +0 -0
  77. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/codebase/core/change_detector.py +0 -0
  78. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/codebase/core/code_retrieval.py +0 -0
  79. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/codebase/core/ingestor.py +0 -0
  80. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/codebase/core/language_config.py +0 -0
  81. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/codebase/core/manager.py +0 -0
  82. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/codebase/core/nl_query.py +0 -0
  83. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/codebase/core/parser_loader.py +0 -0
  84. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/codebase/models.py +0 -0
  85. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/codebase/service.py +0 -0
  86. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/logging_config.py +0 -0
  87. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/main.py +0 -0
  88. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/posthog_telemetry.py +0 -0
  89. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/prompts/__init__.py +0 -0
  90. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/prompts/agents/__init__.py +0 -0
  91. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/prompts/agents/partials/codebase_understanding.j2 +0 -0
  92. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/prompts/agents/partials/content_formatting.j2 +0 -0
  93. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/prompts/agents/partials/interactive_mode.j2 +0 -0
  94. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/prompts/agents/state/codebase/codebase_graphs_available.j2 +0 -0
  95. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/prompts/codebase/__init__.py +0 -0
  96. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/prompts/codebase/cypher_query_patterns.j2 +0 -0
  97. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/prompts/codebase/cypher_system.j2 +0 -0
  98. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/prompts/codebase/enhanced_query_context.j2 +0 -0
  99. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/prompts/codebase/partials/cypher_rules.j2 +0 -0
  100. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/prompts/codebase/partials/graph_schema.j2 +0 -0
  101. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/prompts/codebase/partials/temporal_context.j2 +0 -0
  102. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/prompts/history/__init__.py +0 -0
  103. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/prompts/history/incremental_summarization.j2 +0 -0
  104. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/prompts/history/summarization.j2 +0 -0
  105. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/prompts/loader.py +0 -0
  106. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/py.typed +0 -0
  107. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/sdk/__init__.py +0 -0
  108. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/sdk/codebase.py +0 -0
  109. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/sdk/exceptions.py +0 -0
  110. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/sdk/models.py +0 -0
  111. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/sdk/services.py +0 -0
  112. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/sentry_telemetry.py +0 -0
  113. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/telemetry.py +0 -0
  114. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/tui/__init__.py +0 -0
  115. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/tui/app.py +0 -0
  116. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/tui/commands/__init__.py +0 -0
  117. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/tui/components/prompt_input.py +0 -0
  118. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/tui/components/spinner.py +0 -0
  119. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/tui/components/splash.py +0 -0
  120. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/tui/components/vertical_tail.py +0 -0
  121. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/tui/screens/chat.tcss +0 -0
  122. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/tui/screens/chat_screen/__init__.py +0 -0
  123. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/tui/screens/chat_screen/command_providers.py +0 -0
  124. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/tui/screens/directory_setup.py +0 -0
  125. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/tui/screens/provider_config.py +0 -0
  126. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/tui/screens/splash.py +0 -0
  127. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/tui/styles.tcss +0 -0
  128. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/tui/utils/__init__.py +0 -0
  129. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/tui/utils/mode_progress.py +0 -0
  130. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/utils/__init__.py +0 -0
  131. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/utils/env_utils.py +0 -0
  132. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/src/shotgun/utils/file_system_utils.py +0 -0
  133. {shotgun_sh-0.1.0.dev24 → shotgun_sh-0.1.0.dev26}/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.dev24
3
+ Version: 0.1.0.dev26
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.0.dev24"
3
+ version = "0.1.0.dev26"
4
4
  description = "AI-powered research, planning, and task management CLI tool"
5
5
  readme = "README.md"
6
6
  license = { text = "MIT" }
@@ -37,6 +37,7 @@ 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.tui.screens.chat_screen.hint_message import HintMessage
40
41
 
41
42
  from .export import create_export_agent
42
43
  from .history.compaction import apply_persistent_compaction
@@ -54,7 +55,7 @@ class MessageHistoryUpdated(Message):
54
55
 
55
56
  def __init__(
56
57
  self,
57
- messages: list[ModelMessage],
58
+ messages: list[ModelMessage | HintMessage],
58
59
  agent_type: AgentType,
59
60
  file_operations: list[FileOperation] | None = None,
60
61
  ) -> None:
@@ -143,7 +144,7 @@ class AgentManager(Widget):
143
144
  self._current_agent_type: AgentType = initial_type
144
145
 
145
146
  # Maintain shared message history
146
- self.ui_message_history: list[ModelMessage] = []
147
+ self.ui_message_history: list[ModelMessage | HintMessage] = []
147
148
  self.message_history: list[ModelMessage] = []
148
149
  self.recently_change_files: list[FileOperation] = []
149
150
  self._stream_state: _PartialStreamState | None = None
@@ -461,8 +462,8 @@ class AgentManager(Widget):
461
462
  )
462
463
 
463
464
  def _filter_system_prompts(
464
- self, messages: list[ModelMessage]
465
- ) -> list[ModelMessage]:
465
+ self, messages: list[ModelMessage | HintMessage]
466
+ ) -> list[ModelMessage | HintMessage]:
466
467
  """Filter out system prompts from messages for UI display.
467
468
 
468
469
  Args:
@@ -473,8 +474,12 @@ class AgentManager(Widget):
473
474
  """
474
475
  from pydantic_ai.messages import SystemPromptPart
475
476
 
476
- filtered_messages: list[ModelMessage] = []
477
+ filtered_messages: list[ModelMessage | HintMessage] = []
477
478
  for msg in messages:
479
+ if isinstance(msg, HintMessage):
480
+ filtered_messages.append(msg)
481
+ continue
482
+
478
483
  parts: Sequence[ModelRequestPart] | Sequence[ModelResponsePart] | None = (
479
484
  msg.parts if hasattr(msg, "parts") else None
480
485
  )
@@ -514,6 +519,7 @@ class AgentManager(Widget):
514
519
 
515
520
  return ConversationState(
516
521
  agent_messages=self.message_history.copy(),
522
+ ui_messages=self.ui_message_history.copy(),
517
523
  agent_type=self._current_agent_type.value,
518
524
  )
519
525
 
@@ -524,10 +530,16 @@ class AgentManager(Widget):
524
530
  state: ConversationState object to restore
525
531
  """
526
532
  # Restore message history for agents (includes system prompts)
527
- self.message_history = state.agent_messages.copy()
533
+ non_hint_messages = [
534
+ msg for msg in state.agent_messages if not isinstance(msg, HintMessage)
535
+ ]
536
+ self.message_history = non_hint_messages
528
537
 
529
- # Filter out system prompts for UI display
530
- self.ui_message_history = self._filter_system_prompts(state.agent_messages)
538
+ # Filter out system prompts for UI display while keeping hints
539
+ ui_source = state.ui_messages or cast(
540
+ list[ModelMessage | HintMessage], state.agent_messages
541
+ )
542
+ self.ui_message_history = self._filter_system_prompts(ui_source)
531
543
 
532
544
  # Restore agent type
533
545
  self._current_agent_type = AgentType(state.agent_type)
@@ -535,6 +547,10 @@ class AgentManager(Widget):
535
547
  # Notify listeners about the restored messages
536
548
  self._post_messages_updated()
537
549
 
550
+ def add_hint_message(self, message: HintMessage) -> None:
551
+ self.ui_message_history.append(message)
552
+ self._post_messages_updated()
553
+
538
554
 
539
555
  # Re-export AgentType for backward compatibility
540
556
  __all__ = [
@@ -2,6 +2,7 @@
2
2
 
3
3
  import asyncio
4
4
  from collections.abc import Callable
5
+ from pathlib import Path
5
6
  from typing import Any
6
7
 
7
8
  from pydantic_ai import (
@@ -28,7 +29,7 @@ from shotgun.utils.file_system_utils import get_shotgun_base_path
28
29
 
29
30
  from .history import token_limit_compactor
30
31
  from .history.compaction import apply_persistent_compaction
31
- from .models import AgentDeps, AgentRuntimeOptions
32
+ from .models import AgentDeps, AgentRuntimeOptions, PipelineConfigEntry
32
33
  from .tools import (
33
34
  append_file,
34
35
  ask_user,
@@ -197,33 +198,24 @@ def create_base_agent(
197
198
  return agent, deps
198
199
 
199
200
 
200
- def extract_markdown_toc(agent_mode: AgentType | None) -> str | None:
201
- """Extract table of contents from agent's markdown file.
201
+ def _extract_file_toc_content(
202
+ file_path: Path, max_depth: int | None = None, max_chars: int = 500
203
+ ) -> str | None:
204
+ """Extract TOC from a single file with depth and character limits.
202
205
 
203
206
  Args:
204
- agent_mode: The agent mode to extract TOC for
207
+ file_path: Path to the markdown file
208
+ max_depth: Maximum heading depth (1=#, 2=##, None=all)
209
+ max_chars: Maximum characters for the TOC
205
210
 
206
211
  Returns:
207
- Formatted TOC string (up to 2000 chars) or None if not applicable
212
+ Formatted TOC string or None if file doesn't exist
208
213
  """
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():
214
+ if not file_path.exists():
223
215
  return None
224
216
 
225
217
  try:
226
- content = md_path.read_text(encoding="utf-8")
218
+ content = file_path.read_text(encoding="utf-8")
227
219
  lines = content.split("\n")
228
220
 
229
221
  # Extract headings
@@ -239,6 +231,10 @@ def extract_markdown_toc(agent_mode: AgentType | None) -> str | None:
239
231
  else:
240
232
  break
241
233
 
234
+ # Skip if exceeds max_depth
235
+ if max_depth and level > max_depth:
236
+ continue
237
+
242
238
  # Get the heading text (remove the # symbols and clean up)
243
239
  heading_text = stripped[level:].strip()
244
240
  if heading_text:
@@ -246,21 +242,110 @@ def extract_markdown_toc(agent_mode: AgentType | None) -> str | None:
246
242
  indent = " " * (level - 1)
247
243
  toc_lines.append(f"{indent}{'#' * level} {heading_text}")
248
244
 
245
+ # Check if we're approaching the character limit
246
+ current_length = sum(len(line) + 1 for line in toc_lines)
247
+ if current_length > max_chars:
248
+ # Remove the last line and add ellipsis
249
+ toc_lines.pop()
250
+ if toc_lines:
251
+ toc_lines.append(" ...")
252
+ break
253
+
249
254
  if not toc_lines:
250
255
  return None
251
256
 
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
257
+ return "\n".join(toc_lines)
258
258
 
259
259
  except Exception as e:
260
- logger.debug(f"Failed to extract TOC from {md_file}: {e}")
260
+ logger.debug(f"Failed to extract TOC from {file_path}: {e}")
261
261
  return None
262
262
 
263
263
 
264
+ def extract_markdown_toc(agent_mode: AgentType | None) -> str | None:
265
+ """Extract TOCs from current and prior agents' files in the pipeline.
266
+
267
+ Shows full TOC of agent's own file and high-level summaries of prior agents'
268
+ files to maintain context awareness while keeping context window tight.
269
+
270
+ Args:
271
+ agent_mode: The agent mode to extract TOC for
272
+
273
+ Returns:
274
+ Formatted multi-file TOC string or None if not applicable
275
+ """
276
+ # Skip if no mode
277
+ if not agent_mode:
278
+ return None
279
+
280
+ # Define pipeline order and dependencies
281
+ pipeline_config: dict[AgentType, PipelineConfigEntry] = {
282
+ AgentType.RESEARCH: PipelineConfigEntry(
283
+ own_file="research.md",
284
+ prior_files=[], # First in pipeline
285
+ ),
286
+ AgentType.SPECIFY: PipelineConfigEntry(
287
+ own_file="specification.md",
288
+ prior_files=["research.md"],
289
+ ),
290
+ AgentType.PLAN: PipelineConfigEntry(
291
+ own_file="plan.md",
292
+ prior_files=["research.md", "specification.md"],
293
+ ),
294
+ AgentType.TASKS: PipelineConfigEntry(
295
+ own_file="tasks.md",
296
+ prior_files=["research.md", "specification.md", "plan.md"],
297
+ ),
298
+ AgentType.EXPORT: PipelineConfigEntry(
299
+ own_file=None, # Export uses directory
300
+ prior_files=["research.md", "specification.md", "plan.md", "tasks.md"],
301
+ ),
302
+ }
303
+
304
+ # Get configuration for current agent
305
+ if agent_mode not in pipeline_config:
306
+ return None
307
+
308
+ config = pipeline_config[agent_mode]
309
+ base_path = get_shotgun_base_path()
310
+ toc_sections: list[str] = []
311
+
312
+ # Extract TOCs from prior files (high-level only)
313
+ for prior_file in config.prior_files:
314
+ file_path = base_path / prior_file
315
+ # Only show # and ## headings from prior files, max 500 chars each
316
+ prior_toc = _extract_file_toc_content(file_path, max_depth=2, max_chars=500)
317
+ if prior_toc:
318
+ # Add section header
319
+ file_label = prior_file.replace(".md", "").replace("_", " ").title()
320
+ toc_sections.append(
321
+ f"=== Prior Context: {file_label} (summary) ===\n{prior_toc}"
322
+ )
323
+
324
+ # Extract TOC from own file (full detail)
325
+ if config.own_file:
326
+ own_path = base_path / config.own_file
327
+ own_toc = _extract_file_toc_content(own_path, max_depth=None, max_chars=2000)
328
+ if own_toc:
329
+ file_label = config.own_file.replace(".md", "").replace("_", " ").title()
330
+ # Put own file TOC at the beginning
331
+ toc_sections.insert(
332
+ 0, f"=== Your Current Document: {file_label} ===\n{own_toc}"
333
+ )
334
+
335
+ # Combine all sections
336
+ if not toc_sections:
337
+ return None
338
+
339
+ combined_toc = "\n\n".join(toc_sections)
340
+
341
+ # Final truncation if needed (should rarely happen with our limits)
342
+ max_total = 3500 # Conservative total limit
343
+ if len(combined_toc) > max_total:
344
+ combined_toc = combined_toc[: max_total - 3] + "..."
345
+
346
+ return combined_toc
347
+
348
+
264
349
  def get_agent_existing_files(agent_mode: AgentType | None = None) -> list[str]:
265
350
  """Get list of existing files for the given agent mode.
266
351
 
@@ -0,0 +1,106 @@
1
+ """Models and utilities for persisting TUI conversation history."""
2
+
3
+ from datetime import datetime
4
+ from typing import Any, cast
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
+ from shotgun.tui.screens.chat_screen.hint_message import HintMessage
14
+
15
+ SerializedMessage = dict[str, Any]
16
+
17
+
18
+ class ConversationState(BaseModel):
19
+ """Represents the complete state of a conversation in memory."""
20
+
21
+ agent_messages: list[ModelMessage]
22
+ ui_messages: list[ModelMessage | HintMessage] = Field(default_factory=list)
23
+ agent_type: str # Will store AgentType.value
24
+
25
+ model_config = ConfigDict(arbitrary_types_allowed=True)
26
+
27
+
28
+ class ConversationHistory(BaseModel):
29
+ """Persistent conversation history for TUI sessions."""
30
+
31
+ version: int = 1
32
+ agent_history: list[SerializedMessage] = Field(
33
+ default_factory=list
34
+ ) # Stores serialized ModelMessage objects
35
+ ui_history: list[SerializedMessage] = Field(
36
+ default_factory=list
37
+ ) # Stores serialized ModelMessage and HintMessage objects
38
+ last_agent_model: str = "research"
39
+ updated_at: datetime = Field(default_factory=datetime.now)
40
+
41
+ model_config = ConfigDict(arbitrary_types_allowed=True)
42
+
43
+ def set_agent_messages(self, messages: list[ModelMessage]) -> None:
44
+ """Set agent_history from a list of ModelMessage objects.
45
+
46
+ Args:
47
+ messages: List of ModelMessage objects to serialize and store
48
+ """
49
+ # Serialize ModelMessage list to JSON-serializable format
50
+ self.agent_history = to_jsonable_python(
51
+ messages, fallback=lambda x: str(x), exclude_none=True
52
+ )
53
+
54
+ def set_ui_messages(self, messages: list[ModelMessage | HintMessage]) -> None:
55
+ """Set ui_history from a list of UI messages."""
56
+
57
+ def _serialize_message(
58
+ message: ModelMessage | HintMessage,
59
+ ) -> Any:
60
+ if isinstance(message, HintMessage):
61
+ data = message.model_dump()
62
+ data["message_type"] = "hint"
63
+ return data
64
+ payload = to_jsonable_python(
65
+ message, fallback=lambda x: str(x), exclude_none=True
66
+ )
67
+ if isinstance(payload, dict):
68
+ payload.setdefault("message_type", "model")
69
+ return payload
70
+
71
+ self.ui_history = [_serialize_message(msg) for msg in messages]
72
+
73
+ def get_agent_messages(self) -> list[ModelMessage]:
74
+ """Get agent_history as a list of ModelMessage objects.
75
+
76
+ Returns:
77
+ List of deserialized ModelMessage objects
78
+ """
79
+ if not self.agent_history:
80
+ return []
81
+
82
+ # Deserialize from JSON format back to ModelMessage objects
83
+ return ModelMessagesTypeAdapter.validate_python(self.agent_history)
84
+
85
+ def get_ui_messages(self) -> list[ModelMessage | HintMessage]:
86
+ """Get ui_history as a list of Model or hint messages."""
87
+
88
+ if not self.ui_history:
89
+ # Fallback for older conversation files without UI history
90
+ return cast(list[ModelMessage | HintMessage], self.get_agent_messages())
91
+
92
+ messages: list[ModelMessage | HintMessage] = []
93
+ for item in self.ui_history:
94
+ message_type = item.get("message_type") if isinstance(item, dict) else None
95
+ if message_type == "hint":
96
+ messages.append(HintMessage.model_validate(item))
97
+ continue
98
+
99
+ # Backwards compatibility: data may not include the type marker
100
+ payload = item
101
+ if isinstance(payload, dict):
102
+ payload = {k: v for k, v in payload.items() if k != "message_type"}
103
+ deserialized = ModelMessagesTypeAdapter.validate_python([payload])
104
+ messages.append(deserialized[0])
105
+
106
+ return messages
@@ -27,6 +27,23 @@ class AgentType(StrEnum):
27
27
  EXPORT = "export"
28
28
 
29
29
 
30
+ class PipelineConfigEntry(BaseModel):
31
+ """Configuration for each agent in the pipeline.
32
+
33
+ This model defines what files an agent can write to and what
34
+ files from prior agents it should read for context.
35
+ """
36
+
37
+ own_file: str | None = Field(
38
+ default=None,
39
+ description="The file this agent writes to (None for export agent)",
40
+ )
41
+ prior_files: list[str] = Field(
42
+ default_factory=list,
43
+ description="Files from prior agents in pipeline to read for context",
44
+ )
45
+
46
+
30
47
  class UserAnswer(BaseModel):
31
48
  """A answer from the user."""
32
49
 
@@ -0,0 +1,214 @@
1
+ You are an experienced Export Specialist and Data Transformation Expert.
2
+
3
+ Your job is to help export project documentation and findings to various formats and destinations in the exports/ folder.
4
+
5
+ {% include 'agents/partials/common_agent_system_prompt.j2' %}
6
+
7
+ ## MEMORY MANAGEMENT PROTOCOL
8
+
9
+ - You can ONLY write to the `exports/` directory
10
+ - SHOULD READ all files: `research.md`, `specification.md`, `plan.md`, `tasks.md`
11
+ - Create new export files, don't modify source files
12
+ - Name exports descriptively with timestamps: `exports/AGENTS_[timestamp].md`
13
+ - Each export is a standalone deliverable for AI agents
14
+
15
+ ## AI AGENT PIPELINE AWARENESS
16
+
17
+ **CRITICAL**: Your exports will be consumed by AI coding agents (Claude Code, Cursor, Windsurf, etc.)
18
+ - The AGENTS.md file is THE primary deliverable for AI agents
19
+ - Consolidate all relevant information into a single, actionable document
20
+ - Include all necessary context without requiring access to source files
21
+ - Structure exports for immediate AI agent consumption
22
+ - Prioritize tasks and implementation steps from tasks.md
23
+ - Include specifications and API details from specification.md
24
+ - Add relevant research findings that affect implementation
25
+ - Format as executable instructions, not educational content
26
+
27
+ ## EXPORT WORKFLOW
28
+
29
+ For AGENTS.md exports:
30
+ 1. **MANDATORY: Read ALL pipeline files**:
31
+ - `tasks.md` - Extract actionable tasks with inputs/outputs
32
+ - `specification.md` - Get primary objective and constraints
33
+ - `plan.md` - Convert stages to success criteria
34
+ - `research.md` - Extract codebase context and open questions
35
+ 2. **Map content to template sections**:
36
+ - Primary Objective: From specification.md's main goal
37
+ - Tasks: From tasks.md, formatted as action items
38
+ - Research Needed: Unresolved questions from research.md
39
+ - Codebase Context: Technical findings from research.md
40
+ - Success Criteria: Plan.md stages as measurable outcomes
41
+ - Important Notes: Critical constraints from all files
42
+ - References: Documentation links from research.md
43
+ 3. **Transform content**: Convert to action-oriented language for AI agents
44
+ 4. **Create export**: Save as `exports/AGENTS.md` or `exports/AGENTS_[timestamp].md`
45
+ 5. **Validate**: Ensure every section has actionable content
46
+
47
+ For other export tasks:
48
+ 1. **Check existing files**: Read relevant source files
49
+ 2. **Understand requirements**: Determine format and scope
50
+ 3. **Read source content**: Load all necessary information
51
+ 4. **Transform and format**: Convert to requested format
52
+ 5. **Create export files**: Save in exports/ folder
53
+ 6. **Validate output**: Verify proper formatting
54
+
55
+ ## SUPPORTED EXPORT FORMATS
56
+
57
+ - **AGENTS.md**: See below
58
+ - **Markdown (.md)**: Nicely formatted Markdown file with everything the user wants to export
59
+ - **Multiple files**: Can create multiple export files in the exports/ folder as needed
60
+
61
+ ### AGENTS.md - Primary Export Format for AI Agents
62
+
63
+ **CRITICAL**: The AGENTS.md file is THE primary deliverable for AI coding agents. It MUST follow this exact template:
64
+
65
+ ```markdown
66
+ # [Project Name] Implementation Guide for AI Agents
67
+
68
+ ## Primary Objective
69
+ [1-2 sentences describing WHAT needs to be built, not HOW]
70
+
71
+ ## Tasks
72
+
73
+ ### Task 1: [Action-oriented title]
74
+ **Input**: [What files/data this task needs]
75
+ **Output**: [What files/code this task produces]
76
+ **Constraints**: [Security, performance, or architectural requirements]
77
+
78
+ ### Task 2: [Next action]
79
+ [Continue pattern...]
80
+
81
+ ## Research Needed
82
+ - [ ] [Specific technical question to investigate]
83
+ - [ ] [API endpoint to understand]
84
+ - [ ] [Integration pattern to review]
85
+
86
+ ## Codebase Context
87
+ - **Language**: [Primary language and version]
88
+ - **Key Files**: [Critical files to read first]
89
+ - **Patterns**: [Design patterns used in codebase]
90
+ - **Dependencies**: [Important libraries/frameworks]
91
+
92
+ ## Success Criteria
93
+ - [ ] [Measurable outcome 1]
94
+ - [ ] [Measurable outcome 2]
95
+ - [ ] [Tests pass / Coverage meets X%]
96
+
97
+ ## Important Notes
98
+ - [Critical constraint or requirement]
99
+ - [Security consideration]
100
+ - [Performance requirement]
101
+
102
+ ## References
103
+ - [Link to API documentation]
104
+ - [Link to architecture diagrams]
105
+ - [Link to test requirements]
106
+ ```
107
+
108
+ **Export Requirements**:
109
+ - Extract Primary Objective from specification.md
110
+ - Map tasks from tasks.md into action-oriented Task sections
111
+ - Pull research questions from research.md that are still relevant
112
+ - Include codebase context from research findings
113
+ - Convert plan stages into Success Criteria
114
+ - Add implementation constraints from specifications
115
+ - Format ALL content for immediate AI agent execution
116
+ - NO educational content, NO "learn X" instructions
117
+ - Every section must be actionable by Claude Code
118
+
119
+ **Example of GOOD AGENTS.md Content**:
120
+ ```markdown
121
+ # E-commerce API Implementation Guide for AI Agents
122
+
123
+ ## Primary Objective
124
+ Build a REST API for product catalog management with authentication, supporting CRUD operations and search functionality.
125
+
126
+ ## Tasks
127
+
128
+ ### Task 1: Implement Database Models
129
+ **Input**: requirements.md, existing database schema
130
+ **Output**: models/product.py, models/user.py, migrations/
131
+ **Constraints**: Use SQLAlchemy ORM, maintain backward compatibility
132
+
133
+ ### Task 2: Create Authentication Endpoints
134
+ **Input**: models/user.py, auth requirements
135
+ **Output**: api/auth.py, middleware/auth.py
136
+ **Constraints**: JWT tokens, 15-minute expiry, refresh token support
137
+
138
+ ## Research Needed
139
+ - [ ] Check if existing auth middleware supports JWT refresh tokens
140
+ - [ ] Verify PostgreSQL full-text search capabilities for product search
141
+ - [ ] Review rate limiting implementation in current middleware
142
+
143
+ ## Codebase Context
144
+ - **Language**: Python 3.11
145
+ - **Key Files**: src/api/__init__.py, src/models/base.py
146
+ - **Patterns**: Repository pattern, dependency injection
147
+ - **Dependencies**: FastAPI, SQLAlchemy, Pydantic
148
+
149
+ ## Success Criteria
150
+ - [ ] All CRUD endpoints return correct status codes
151
+ - [ ] Authentication enforced on protected routes
152
+ - [ ] Test coverage exceeds 80%
153
+ - [ ] API response times under 200ms
154
+
155
+ ## Important Notes
156
+ - Database migrations must be reversible
157
+ - All endpoints require input validation
158
+ - Search must support pagination
159
+
160
+ ## References
161
+ - FastAPI docs: https://fastapi.tiangolo.com
162
+ - Project API spec: docs/openapi.yaml
163
+ ```
164
+
165
+
166
+ ## EXPORT PRINCIPLES
167
+
168
+ - Preserve content structure and meaning during transformation
169
+ - Include proper headers, metadata, and formatting for target format
170
+ - Maintain links, references, and cross-references where applicable
171
+ - Create self-contained exports that can be used independently
172
+ - Add appropriate file extensions and format indicators
173
+ - Include export timestamp and source information
174
+ - Validate exported content is properly formatted and complete
175
+ - Handle missing or incomplete source data gracefully
176
+ - Consider target audience and use case for formatting decisions
177
+ - Always save exports in the exports/ folder
178
+
179
+ {% if interactive_mode %}
180
+ USER INTERACTION - CLARIFY EXPORT REQUIREMENTS:
181
+
182
+ - ALWAYS ask clarifying questions when export requirements are unclear
183
+ - Use ask_user tool to gather specific details about:
184
+ - Target format and file type preferences
185
+ - Intended use case and audience for the export
186
+ - Specific content sections to include/exclude from files
187
+ - Output structure and organization preferences
188
+ - Destination filename(s) in the exports/ folder
189
+ - Ask follow-up questions to ensure exports meet exact needs
190
+ - Confirm export scope and format before proceeding with large exports
191
+ - Better to ask 2-3 targeted questions than create generic exports
192
+ {% else %}
193
+ NON-INTERACTIVE MODE - MAKE REASONABLE EXPORT DECISIONS:
194
+
195
+ - Make reasonable assumptions about format based on content type
196
+ - Use standard formats and conventions for the target format
197
+ - Include comprehensive content unless scope is clearly limited
198
+ - Apply sensible default formatting and structure
199
+ - Export to commonly used and widely compatible formats
200
+ - Include standard metadata and documentation
201
+ {% endif %}
202
+
203
+ IMPORTANT RULES:
204
+ - Always verify source files exist before attempting export
205
+ - Preserve all critical information during format conversion
206
+ - Include source attribution and export metadata
207
+ - Create well-structured, properly formatted output
208
+ - Always save exports in the exports/ folder (create if needed)
209
+ {% if interactive_mode %}
210
+ - When export requirements are ambiguous, ASK before proceeding
211
+ {% else %}
212
+ - When requirements are unclear, use industry standard practices
213
+ {% endif %}
214
+ - Ensure exported content is self-contained and usable
@@ -7,7 +7,7 @@ Your extensive expertise spans, among other things:
7
7
  ## KEY RULES
8
8
 
9
9
  {% if interactive_mode %}
10
- 0. Always ask for review and go ahead to move forward after writing files.
10
+ 0. Always ask CLARIFYING QUESTIONS if the user's request is ambiguous or lacks sufficient detail. Do not make assumptions about what the user wants.
11
11
  {% endif %}
12
12
  1. Above all, prefer using tools to do the work and NEVER respond with text.
13
13
  2. IMPORTANT: Always ask for review and go ahead to move forward after using write_file().