shotgun-sh 0.1.0.dev19__tar.gz → 0.1.0.dev22__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 (149) hide show
  1. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/PKG-INFO +1 -1
  2. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/pyproject.toml +1 -1
  3. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/agents/agent_manager.py +5 -1
  4. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/agents/common.py +3 -4
  5. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/agents/models.py +5 -0
  6. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/agents/tools/codebase/codebase_shell.py +2 -2
  7. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/agents/tools/codebase/directory_lister.py +1 -1
  8. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/agents/tools/codebase/file_read.py +1 -1
  9. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/agents/tools/codebase/query_graph.py +1 -1
  10. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/agents/tools/codebase/retrieve_code.py +1 -1
  11. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/prompts/agents/partials/codebase_understanding.j2 +9 -0
  12. shotgun_sh-0.1.0.dev22/src/shotgun/prompts/agents/state/codebase/codebase_graphs_available.j2 +19 -0
  13. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/sdk/models.py +1 -1
  14. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/tui/screens/chat.py +38 -25
  15. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/tui/screens/chat_screen/history.py +0 -1
  16. shotgun_sh-0.1.0.dev22/src/shotgun/tui/utils/__init__.py +5 -0
  17. shotgun_sh-0.1.0.dev22/src/shotgun/tui/utils/mode_progress.py +224 -0
  18. shotgun_sh-0.1.0.dev19/src/shotgun/prompts/agents/state/codebase/codebase_graphs_available.j2 +0 -15
  19. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/.gitignore +0 -0
  20. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/LICENSE +0 -0
  21. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/README.md +0 -0
  22. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/hatch_build.py +0 -0
  23. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/__init__.py +0 -0
  24. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/agents/__init__.py +0 -0
  25. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/agents/artifact_state.py +0 -0
  26. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/agents/config/__init__.py +0 -0
  27. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/agents/config/constants.py +0 -0
  28. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/agents/config/manager.py +0 -0
  29. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/agents/config/models.py +0 -0
  30. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/agents/config/provider.py +0 -0
  31. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/agents/export.py +0 -0
  32. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/agents/history/__init__.py +0 -0
  33. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/agents/history/compaction.py +0 -0
  34. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/agents/history/constants.py +0 -0
  35. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/agents/history/context_extraction.py +0 -0
  36. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/agents/history/history_building.py +0 -0
  37. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/agents/history/history_processors.py +0 -0
  38. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/agents/history/message_utils.py +0 -0
  39. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/agents/history/token_counting.py +0 -0
  40. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/agents/history/token_estimation.py +0 -0
  41. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/agents/plan.py +0 -0
  42. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/agents/research.py +0 -0
  43. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/agents/specify.py +0 -0
  44. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/agents/tasks.py +0 -0
  45. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/agents/tools/__init__.py +0 -0
  46. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/agents/tools/artifact_management.py +0 -0
  47. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/agents/tools/codebase/__init__.py +0 -0
  48. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/agents/tools/codebase/models.py +0 -0
  49. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/agents/tools/file_management.py +0 -0
  50. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/agents/tools/user_interaction.py +0 -0
  51. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/agents/tools/web_search/__init__.py +0 -0
  52. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/agents/tools/web_search/anthropic.py +0 -0
  53. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/agents/tools/web_search/gemini.py +0 -0
  54. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/agents/tools/web_search/openai.py +0 -0
  55. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/agents/tools/web_search/utils.py +0 -0
  56. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/artifacts/__init__.py +0 -0
  57. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/artifacts/exceptions.py +0 -0
  58. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/artifacts/manager.py +0 -0
  59. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/artifacts/models.py +0 -0
  60. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/artifacts/service.py +0 -0
  61. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/artifacts/templates/__init__.py +0 -0
  62. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/artifacts/templates/loader.py +0 -0
  63. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/artifacts/templates/models.py +0 -0
  64. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/artifacts/templates/plan/delivery_and_release_plan.yaml +0 -0
  65. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/artifacts/templates/research/market_research.yaml +0 -0
  66. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/artifacts/templates/research/sdk_comparison.yaml +0 -0
  67. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/artifacts/templates/specify/prd.yaml +0 -0
  68. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/artifacts/templates/specify/product_spec.yaml +0 -0
  69. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/artifacts/utils.py +0 -0
  70. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/build_constants.py +0 -0
  71. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/cli/__init__.py +0 -0
  72. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/cli/codebase/__init__.py +0 -0
  73. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/cli/codebase/commands.py +0 -0
  74. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/cli/codebase/models.py +0 -0
  75. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/cli/config.py +0 -0
  76. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/cli/export.py +0 -0
  77. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/cli/models.py +0 -0
  78. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/cli/plan.py +0 -0
  79. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/cli/research.py +0 -0
  80. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/cli/specify.py +0 -0
  81. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/cli/tasks.py +0 -0
  82. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/cli/update.py +0 -0
  83. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/cli/utils.py +0 -0
  84. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/codebase/__init__.py +0 -0
  85. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/codebase/core/__init__.py +0 -0
  86. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/codebase/core/change_detector.py +0 -0
  87. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/codebase/core/code_retrieval.py +0 -0
  88. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/codebase/core/ingestor.py +0 -0
  89. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/codebase/core/language_config.py +0 -0
  90. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/codebase/core/manager.py +0 -0
  91. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/codebase/core/nl_query.py +0 -0
  92. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/codebase/core/parser_loader.py +0 -0
  93. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/codebase/models.py +0 -0
  94. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/codebase/service.py +0 -0
  95. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/logging_config.py +0 -0
  96. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/main.py +0 -0
  97. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/posthog_telemetry.py +0 -0
  98. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/prompts/__init__.py +0 -0
  99. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/prompts/agents/__init__.py +0 -0
  100. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/prompts/agents/export.j2 +0 -0
  101. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/prompts/agents/partials/artifact_system.j2 +0 -0
  102. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/prompts/agents/partials/common_agent_system_prompt.j2 +0 -0
  103. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/prompts/agents/partials/content_formatting.j2 +0 -0
  104. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/prompts/agents/partials/interactive_mode.j2 +0 -0
  105. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/prompts/agents/plan.j2 +0 -0
  106. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/prompts/agents/research.j2 +0 -0
  107. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/prompts/agents/specify.j2 +0 -0
  108. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/prompts/agents/state/artifact_templates_available.j2 +0 -0
  109. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/prompts/agents/state/existing_artifacts_available.j2 +0 -0
  110. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/prompts/agents/state/system_state.j2 +0 -0
  111. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/prompts/agents/tasks.j2 +0 -0
  112. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/prompts/codebase/__init__.py +0 -0
  113. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/prompts/codebase/cypher_query_patterns.j2 +0 -0
  114. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/prompts/codebase/cypher_system.j2 +0 -0
  115. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/prompts/codebase/enhanced_query_context.j2 +0 -0
  116. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/prompts/codebase/partials/cypher_rules.j2 +0 -0
  117. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/prompts/codebase/partials/graph_schema.j2 +0 -0
  118. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/prompts/codebase/partials/temporal_context.j2 +0 -0
  119. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/prompts/history/__init__.py +0 -0
  120. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/prompts/history/incremental_summarization.j2 +0 -0
  121. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/prompts/history/summarization.j2 +0 -0
  122. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/prompts/loader.py +0 -0
  123. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/py.typed +0 -0
  124. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/sdk/__init__.py +0 -0
  125. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/sdk/artifact_models.py +0 -0
  126. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/sdk/artifacts.py +0 -0
  127. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/sdk/codebase.py +0 -0
  128. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/sdk/exceptions.py +0 -0
  129. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/sdk/services.py +0 -0
  130. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/sentry_telemetry.py +0 -0
  131. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/telemetry.py +0 -0
  132. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/tui/__init__.py +0 -0
  133. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/tui/app.py +0 -0
  134. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/tui/commands/__init__.py +0 -0
  135. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/tui/components/prompt_input.py +0 -0
  136. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/tui/components/spinner.py +0 -0
  137. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/tui/components/splash.py +0 -0
  138. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/tui/components/vertical_tail.py +0 -0
  139. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/tui/screens/chat.tcss +0 -0
  140. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/tui/screens/chat_screen/__init__.py +0 -0
  141. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/tui/screens/chat_screen/command_providers.py +0 -0
  142. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/tui/screens/directory_setup.py +0 -0
  143. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/tui/screens/provider_config.py +0 -0
  144. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/tui/screens/splash.py +0 -0
  145. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/tui/styles.tcss +0 -0
  146. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/utils/__init__.py +0 -0
  147. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/utils/env_utils.py +0 -0
  148. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/src/shotgun/utils/file_system_utils.py +0 -0
  149. {shotgun_sh-0.1.0.dev19 → shotgun_sh-0.1.0.dev22}/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.dev19
3
+ Version: 0.1.0.dev22
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.dev19"
3
+ version = "0.1.0.dev22"
4
4
  description = "AI-powered research, planning, and task management CLI tool"
5
5
  readme = "README.md"
6
6
  license = { text = "MIT" }
@@ -32,7 +32,7 @@ from pydantic_ai.messages import (
32
32
  from textual.message import Message
33
33
  from textual.widget import Widget
34
34
 
35
- from shotgun.agents.common import add_system_prompt_message
35
+ from shotgun.agents.common import add_system_prompt_message, add_system_status_message
36
36
 
37
37
  from .export import create_export_agent
38
38
  from .history.compaction import apply_persistent_compaction
@@ -122,6 +122,7 @@ class AgentManager(Widget):
122
122
  agent_runtime_options = AgentRuntimeOptions(
123
123
  interactive_mode=self.deps.interactive_mode,
124
124
  working_directory=self.deps.working_directory,
125
+ is_tui_context=self.deps.is_tui_context,
125
126
  max_iterations=self.deps.max_iterations,
126
127
  queue=self.deps.queue,
127
128
  tasks=self.deps.tasks,
@@ -280,6 +281,9 @@ class AgentManager(Widget):
280
281
  # Start with persistent message history
281
282
  message_history = self.message_history
282
283
 
284
+ # Add a system status message so the agent knows whats going on
285
+ message_history = await add_system_status_message(deps, message_history)
286
+
283
287
  # Check if the message history already has a system prompt
284
288
  has_system_prompt = any(
285
289
  hasattr(msg, "parts")
@@ -15,9 +15,7 @@ from pydantic_ai.agent import AgentRunResult
15
15
  from pydantic_ai.messages import (
16
16
  ModelMessage,
17
17
  ModelRequest,
18
- ModelResponse,
19
18
  SystemPromptPart,
20
- TextPart,
21
19
  )
22
20
 
23
21
  from shotgun.agents.config import ProviderType, get_config_manager, get_provider_model
@@ -79,13 +77,14 @@ async def add_system_status_message(
79
77
  system_state = prompt_loader.render(
80
78
  "agents/state/system_state.j2",
81
79
  codebase_understanding_graphs=codebase_understanding_graphs,
80
+ is_tui_context=deps.is_tui_context,
82
81
  **artifact_state,
83
82
  )
84
83
 
85
84
  message_history.append(
86
- ModelResponse(
85
+ ModelRequest(
87
86
  parts=[
88
- TextPart(content=system_state),
87
+ SystemPromptPart(content=system_state),
89
88
  ]
90
89
  )
91
90
  )
@@ -60,6 +60,11 @@ class AgentRuntimeOptions(BaseModel):
60
60
  description="Working directory for agent operations",
61
61
  )
62
62
 
63
+ is_tui_context: bool = Field(
64
+ default=False,
65
+ description="Whether the agent is running in TUI context",
66
+ )
67
+
63
68
  max_iterations: int = Field(
64
69
  default=10,
65
70
  ge=1,
@@ -87,7 +87,7 @@ async def codebase_shell(
87
87
  success=False,
88
88
  command=command,
89
89
  args=args,
90
- error="No codebase service available in context",
90
+ error="No codebase indexed",
91
91
  )
92
92
 
93
93
  # Security validation
@@ -129,7 +129,7 @@ async def codebase_shell(
129
129
  success=False,
130
130
  command=command,
131
131
  args=args,
132
- error="No codebases available. Add a codebase first using graph management tools.",
132
+ error="No codebase indexed. Index a codebase first.",
133
133
  )
134
134
 
135
135
  # Select the appropriate graph
@@ -33,7 +33,7 @@ async def directory_lister(
33
33
  success=False,
34
34
  directory=directory,
35
35
  full_path="",
36
- error="No codebase service available in context",
36
+ error="No codebase indexed",
37
37
  )
38
38
 
39
39
  # Get the graph to find the repository path
@@ -33,7 +33,7 @@ async def file_read(
33
33
  return FileReadResult(
34
34
  success=False,
35
35
  file_path=file_path,
36
- error="No codebase service available in context",
36
+ error="No codebase indexed",
37
37
  )
38
38
 
39
39
  # Get the graph to find the repository path
@@ -31,7 +31,7 @@ async def query_graph(
31
31
  return QueryGraphResult(
32
32
  success=False,
33
33
  query=query,
34
- error="No codebase service available in context",
34
+ error="No codebase indexed",
35
35
  )
36
36
 
37
37
  # Execute natural language query
@@ -34,7 +34,7 @@ async def retrieve_code(
34
34
  return CodeSnippetResult(
35
35
  found=False,
36
36
  qualified_name=qualified_name,
37
- error="No codebase service available in context",
37
+ error="No codebase indexed",
38
38
  )
39
39
 
40
40
  # Use the existing code retrieval functionality
@@ -40,6 +40,15 @@ Important:
40
40
  - Always think about a plan first and communicate this plan back to the user BEFORE calling any codebase management or codebase understanding tools.
41
41
  - **GRAPH ID vs NAME**: Every graph has both a NAME (like "Shotgun2") and an ID (like "993ec896213d"). The name is for human reference only. ALL TOOLS REQUIRE THE GRAPH ID, NOT THE NAME!
42
42
 
43
+ ## User Communication Guidelines
44
+
45
+ **CRITICAL**: When communicating with users about codebases:
46
+ - NEVER mention "graph ID" to users - use the codebase name (e.g., "Shotgun2") or path (e.g., "/Users/scott/project")
47
+ - When multiple codebases are available, ask "Which codebase would you like to analyze: Shotgun2 at /path/to/repo?"
48
+ - Internal tools still require graph_id parameters, but don't expose these IDs to users
49
+ - Say "the codebase 'ProjectName'" or "the codebase at /path" instead of "graph xyz123"
50
+ - If asking users to confirm a codebase, show the name and path, not the ID
51
+
43
52
  **CRITICAL RULES:**
44
53
  0. **ALWAYS USE GRAPH ID**: When calling ANY tool that requires a graph_id parameter, you MUST use the ID (e.g., "993ec896213d"), NOT the name (e.g., "Shotgun2"). The name is only for human reference.
45
54
  1. **TOOL-ONLY ANSWERS**: You must ONLY use information from the tools provided. Do not use external knowledge.
@@ -0,0 +1,19 @@
1
+ ## Codebase Graphs Available
2
+
3
+ {% if codebase_understanding_graphs -%}
4
+
5
+ You have access to the following codebase graphs:
6
+
7
+ {% for graph in codebase_understanding_graphs -%}
8
+ - {{ graph.name }} ID: {{ graph.graph_id }} Path: {{ graph.repo_path }}
9
+ {% endfor -%}
10
+
11
+ {% else -%}
12
+
13
+ {% if is_tui_context -%}
14
+ No codebase has been indexed yet. To enable code analysis, please tell the user to restart the TUI and follow the prompt to 'Index this codebase?' when it appears.
15
+ {% else -%}
16
+ No codebase has been indexed yet. If the user needs code analysis, ask them to index a codebase first.
17
+ {% endif -%}
18
+
19
+ {% endif %}
@@ -15,7 +15,7 @@ class ListResult(BaseModel):
15
15
  def __str__(self) -> str:
16
16
  """Format list result as plain text table."""
17
17
  if not self.graphs:
18
- return "No codebases found."
18
+ return "No indexed codebases found."
19
19
 
20
20
  lines = [
21
21
  f"{'ID':<12} {'Name':<30} {'Status':<10} {'Files':<8} {'Path'}",
@@ -42,6 +42,7 @@ from shotgun.tui.screens.chat_screen.history import ChatHistory
42
42
 
43
43
  from ..components.prompt_input import PromptInput
44
44
  from ..components.spinner import Spinner
45
+ from ..utils.mode_progress import PlaceholderHints
45
46
  from .chat_screen.command_providers import (
46
47
  AgentModeProvider,
47
48
  CodebaseCommandProvider,
@@ -116,6 +117,7 @@ class ModeIndicator(Widget):
116
117
  """
117
118
  super().__init__()
118
119
  self.mode = mode
120
+ self.progress_checker = PlaceholderHints().progress_checker
119
121
 
120
122
  def render(self) -> str:
121
123
  """Render the mode indicator."""
@@ -137,7 +139,11 @@ class ModeIndicator(Widget):
137
139
  mode_title = mode_display.get(self.mode, self.mode.value.title())
138
140
  description = mode_description.get(self.mode, "")
139
141
 
140
- return f"[bold $text-accent]{mode_title} mode[/][$foreground-muted] ({description})[/]"
142
+ # Check if mode has content
143
+ has_content = self.progress_checker.has_mode_content(self.mode)
144
+ status_icon = " ✓" if has_content else ""
145
+
146
+ return f"[bold $text-accent]{mode_title}{status_icon} mode[/][$foreground-muted] ({description})[/]"
141
147
 
142
148
 
143
149
  class FilteredDirectoryTree(DirectoryTree):
@@ -174,9 +180,11 @@ class CodebaseIndexPromptScreen(ModalScreen[bool]):
174
180
 
175
181
  def compose(self) -> ComposeResult:
176
182
  with Container(id="index-prompt-dialog"):
177
- yield Label("Index your codebase?", id="index-prompt-title")
183
+ yield Label("Index this codebase?", id="index-prompt-title")
178
184
  yield Static(
179
- "We found project files but no index yet. Indexing enables smarter chat."
185
+ f"Would you like to index the codebase at:\n{Path.cwd()}\n\n"
186
+ "This is required for the agent to understand your code and answer "
187
+ "questions about it. Without indexing, the agent cannot analyze your codebase."
180
188
  )
181
189
  with Container(id="index-prompt-buttons"):
182
190
  yield Button(
@@ -306,24 +314,6 @@ class ChatScreen(Screen[None]):
306
314
 
307
315
  COMMANDS = {AgentModeProvider, ProviderSetupProvider, CodebaseCommandProvider}
308
316
 
309
- _PLACEHOLDER_BY_MODE: dict[AgentType, str] = {
310
- AgentType.RESEARCH: (
311
- "Ask for investigations, e.g. research strengths and weaknesses of PydanticAI vs its rivals"
312
- ),
313
- AgentType.PLAN: (
314
- "Describe a goal to plan, e.g. draft a rollout plan for launching our Slack automation"
315
- ),
316
- AgentType.TASKS: (
317
- "Request actionable work, e.g. break down tasks to wire OpenTelemetry into the API"
318
- ),
319
- AgentType.SPECIFY: (
320
- "Request detailed specifications, e.g. create a comprehensive spec for user authentication system"
321
- ),
322
- AgentType.EXPORT: (
323
- "Request export tasks, e.g. export research findings to Markdown or convert tasks to CSV"
324
- ),
325
- }
326
-
327
317
  value = reactive("")
328
318
  mode = reactive(AgentType.RESEARCH)
329
319
  history: PromptHistory = PromptHistory()
@@ -350,6 +340,7 @@ class ChatScreen(Screen[None]):
350
340
 
351
341
  self.deps = AgentDeps(
352
342
  interactive_mode=True,
343
+ is_tui_context=True,
353
344
  llm_model=model_config,
354
345
  codebase_service=codebase_service,
355
346
  artifact_service=artifact_service,
@@ -357,6 +348,7 @@ class ChatScreen(Screen[None]):
357
348
  )
358
349
  self.agent_manager = AgentManager(deps=self.deps, initial_type=self.mode)
359
350
  self.command_handler = CommandHandler()
351
+ self.placeholder_hints = PlaceholderHints()
360
352
 
361
353
  def on_mount(self) -> None:
362
354
  self.query_one(PromptInput).focus(scroll_visible=True)
@@ -407,7 +399,10 @@ class ChatScreen(Screen[None]):
407
399
  mode_indicator.refresh()
408
400
 
409
401
  prompt_input = self.query_one(PromptInput)
410
- prompt_input.placeholder = self._placeholder_for_mode(new_mode)
402
+ # Force new hint selection when mode changes
403
+ prompt_input.placeholder = self._placeholder_for_mode(
404
+ new_mode, force_new=True
405
+ )
411
406
  prompt_input.refresh()
412
407
 
413
408
  def watch_working(self, is_working: bool) -> None:
@@ -503,6 +498,14 @@ class ChatScreen(Screen[None]):
503
498
  self._clear_partial_response()
504
499
  self.messages = event.messages
505
500
 
501
+ # Refresh placeholder and mode indicator in case artifacts were created
502
+ prompt_input = self.query_one(PromptInput)
503
+ prompt_input.placeholder = self._placeholder_for_mode(self.mode)
504
+ prompt_input.refresh()
505
+
506
+ mode_indicator = self.query_one(ModeIndicator)
507
+ mode_indicator.refresh()
508
+
506
509
  # If there are file operations, add a message showing the modified files
507
510
  if event.file_operations:
508
511
  chat_history = self.query_one(ChatHistory)
@@ -577,9 +580,19 @@ class ChatScreen(Screen[None]):
577
580
  prompt_input = self.query_one(PromptInput)
578
581
  prompt_input.clear()
579
582
 
580
- def _placeholder_for_mode(self, mode: AgentType) -> str:
581
- """Return the placeholder text appropriate for the current mode."""
582
- return self._PLACEHOLDER_BY_MODE.get(mode, "Type your message")
583
+ def _placeholder_for_mode(self, mode: AgentType, force_new: bool = False) -> str:
584
+ """Return the placeholder text appropriate for the current mode.
585
+
586
+ Args:
587
+ mode: The current agent mode.
588
+ force_new: If True, force selection of a new random hint.
589
+
590
+ Returns:
591
+ Dynamic placeholder hint based on mode and progress.
592
+ """
593
+ return self.placeholder_hints.get_placeholder_for_mode(
594
+ mode, force_new=force_new
595
+ )
583
596
 
584
597
  def index_codebase_command(self) -> None:
585
598
  start_path = Path.cwd()
@@ -204,7 +204,6 @@ class AgentResponseWidget(Widget):
204
204
  self,
205
205
  part: ToolCallPart,
206
206
  ) -> str:
207
- return "*Answer to continue*"
208
207
  if isinstance(part.args, str):
209
208
  try:
210
209
  _args = json.loads(part.args) if part.args.strip() else {}
@@ -0,0 +1,5 @@
1
+ """TUI utilities package."""
2
+
3
+ from .mode_progress import ModeProgressChecker, PlaceholderHints
4
+
5
+ __all__ = ["ModeProgressChecker", "PlaceholderHints"]
@@ -0,0 +1,224 @@
1
+ """Utility module for checking mode progress in .shotgun directories."""
2
+
3
+ import random
4
+ from pathlib import Path
5
+
6
+ from shotgun.agents.agent_manager import AgentType
7
+ from shotgun.artifacts.models import AgentMode
8
+ from shotgun.utils.file_system_utils import get_shotgun_base_path
9
+
10
+
11
+ class ModeProgressChecker:
12
+ """Checks progress across different agent modes based on .shotgun directory contents."""
13
+
14
+ # Minimum file size in characters to consider a mode as "started"
15
+ MIN_CONTENT_SIZE = 20
16
+
17
+ def __init__(self, base_path: Path | None = None):
18
+ """Initialize the progress checker.
19
+
20
+ Args:
21
+ base_path: Base path for .shotgun directory. Defaults to current directory.
22
+ """
23
+ self.base_path = base_path or get_shotgun_base_path()
24
+
25
+ def has_mode_content(self, mode: AgentType | AgentMode) -> bool:
26
+ """Check if a mode directory has meaningful content.
27
+
28
+ Args:
29
+ mode: The agent mode to check.
30
+
31
+ Returns:
32
+ True if the mode has at least one file with >20 characters.
33
+ """
34
+ mode_value = mode.value if hasattr(mode, "value") else str(mode)
35
+ mode_path = self.base_path / mode_value
36
+
37
+ if not mode_path.exists() or not mode_path.is_dir():
38
+ return False
39
+
40
+ # Check all subdirectories and files
41
+ for item in mode_path.rglob("*"):
42
+ if item.is_file() and not item.name.startswith("."):
43
+ try:
44
+ content = item.read_text(encoding="utf-8")
45
+ # Check if file has meaningful content
46
+ if len(content.strip()) > self.MIN_CONTENT_SIZE:
47
+ return True
48
+ except (OSError, UnicodeDecodeError):
49
+ # Skip files that can't be read
50
+ continue
51
+
52
+ return False
53
+
54
+ def get_next_suggested_mode(self, current_mode: AgentType) -> AgentType | None:
55
+ """Get the next suggested mode based on current progress.
56
+
57
+ Args:
58
+ current_mode: The current agent mode.
59
+
60
+ Returns:
61
+ The next suggested mode, or None if no suggestion.
62
+ """
63
+ mode_order = [
64
+ AgentType.RESEARCH,
65
+ AgentType.SPECIFY,
66
+ AgentType.TASKS,
67
+ AgentType.EXPORT,
68
+ ]
69
+
70
+ try:
71
+ current_index = mode_order.index(current_mode)
72
+ except ValueError:
73
+ # Mode not in standard order (e.g., PLAN mode)
74
+ return None
75
+
76
+ # Check if current mode has content
77
+ if not self.has_mode_content(current_mode):
78
+ # Current mode is empty, no suggestion for next mode
79
+ return None
80
+
81
+ # Get next mode in sequence
82
+ if current_index < len(mode_order) - 1:
83
+ return mode_order[current_index + 1]
84
+
85
+ # Export mode cycles back to Research
86
+ return mode_order[0]
87
+
88
+
89
+ class PlaceholderHints:
90
+ """Manages dynamic placeholder hints for each mode based on progress."""
91
+
92
+ # Placeholder variations for each mode and state
93
+ HINTS = {
94
+ # Research mode
95
+ AgentType.RESEARCH: {
96
+ False: [
97
+ "Research a product or idea (SHIFT+TAB to cycle modes)",
98
+ "What would you like to explore? Start your research journey here (SHIFT+TAB to switch modes)",
99
+ "Dive into discovery mode - research anything that sparks curiosity (SHIFT+TAB for mode menu)",
100
+ "Ready to investigate? Feed me your burning questions (SHIFT+TAB to explore other modes)",
101
+ " 🔍 The research rabbit hole awaits! What shall we uncover? (SHIFT+TAB for mode carousel)",
102
+ ],
103
+ True: [
104
+ "Research complete! SHIFT+TAB to move to Specify mode",
105
+ "Great research! Time to specify (SHIFT+TAB to Specify mode)",
106
+ "Research done! Ready to create specifications (SHIFT+TAB to Specify)",
107
+ "Findings gathered! Move to specifications (SHIFT+TAB for Specify mode)",
108
+ " 🎯 Research complete! Advance to Specify mode (SHIFT+TAB)",
109
+ ],
110
+ },
111
+ # Specify mode
112
+ AgentType.SPECIFY: {
113
+ False: [
114
+ "Create detailed specifications and requirements (SHIFT+TAB to switch modes)",
115
+ "Define your project specifications here (SHIFT+TAB to navigate modes)",
116
+ "Time to get specific - write comprehensive specs (SHIFT+TAB for mode options)",
117
+ "Specification station: Document requirements and designs (SHIFT+TAB to change modes)",
118
+ " 📋 Spec-tacular time! Let's architect your ideas (SHIFT+TAB for mode magic)",
119
+ ],
120
+ True: [
121
+ "Specifications complete! SHIFT+TAB to create a Plan",
122
+ "Specs ready! Time to plan (SHIFT+TAB to Plan mode)",
123
+ "Requirements defined! Move to planning (SHIFT+TAB to Plan)",
124
+ "Specifications done! Create your roadmap (SHIFT+TAB for Plan mode)",
125
+ " 🚀 Specs complete! Advance to Plan mode (SHIFT+TAB)",
126
+ ],
127
+ },
128
+ # Tasks mode
129
+ AgentType.TASKS: {
130
+ False: [
131
+ "Create actionable tasks and work items (SHIFT+TAB to switch modes)",
132
+ "Define your task list and action items (SHIFT+TAB to explore modes)",
133
+ "Task creation time - build your work breakdown (SHIFT+TAB for mode selection)",
134
+ "The task forge awaits - create doable chunks of work (SHIFT+TAB to change modes)",
135
+ " ⚡ Task mode activated! Let's define what needs doing (SHIFT+TAB for mode journey)",
136
+ ],
137
+ True: [
138
+ "Tasks complete! SHIFT+TAB to Export mode",
139
+ "Task list ready! Time to export (SHIFT+TAB to Export)",
140
+ "Work items defined! Export your artifacts (SHIFT+TAB to Export mode)",
141
+ "Tasks done! Ship them out (SHIFT+TAB for Export mode)",
142
+ " 🎉 Tasks complete! Advance to Export mode (SHIFT+TAB)",
143
+ ],
144
+ },
145
+ # Export mode
146
+ AgentType.EXPORT: {
147
+ False: [
148
+ "Export artifacts to Claude Code, Cursor, or other tools (SHIFT+TAB to switch modes)",
149
+ "Ready to export! Send work to your favorite IDE (SHIFT+TAB to navigate modes)",
150
+ "Export central - Ship artifacts to dev tools (SHIFT+TAB for mode options)",
151
+ "Time to set your work free! Export anywhere (SHIFT+TAB to change modes)",
152
+ " 🚢 Launch pad ready! Blast artifacts to Claude Code & beyond (SHIFT+TAB for mode menu)",
153
+ ],
154
+ True: [
155
+ "Exports complete! SHIFT+TAB to start new Research cycle",
156
+ "Artifacts exported! Begin fresh research (SHIFT+TAB to Research mode)",
157
+ "Export done! Start a new journey (SHIFT+TAB for Research)",
158
+ "Work shipped! New research awaits (SHIFT+TAB to Research mode)",
159
+ " 🎊 Export complete! Loop back to Research (SHIFT+TAB)",
160
+ ],
161
+ },
162
+ # Plan mode (special case - not in main flow)
163
+ AgentType.PLAN: {
164
+ False: [
165
+ "Create comprehensive plans with milestones (SHIFT+TAB to switch modes)",
166
+ "Plan your project roadmap and milestones (SHIFT+TAB to explore modes)",
167
+ "Strategic planning mode - design your journey (SHIFT+TAB for mode options)",
168
+ "The planning parlor - where ideas become roadmaps (SHIFT+TAB to navigate)",
169
+ " 📅 Planning paradise! Chart your course to success (SHIFT+TAB for modes)",
170
+ ],
171
+ True: [
172
+ "Plan complete! SHIFT+TAB to create Tasks",
173
+ "Roadmap ready! Time for tasks (SHIFT+TAB to Tasks mode)",
174
+ "Planning done! Break it down to tasks (SHIFT+TAB to Tasks)",
175
+ "Strategy set! Move to task creation (SHIFT+TAB for Tasks mode)",
176
+ " 🗺️ Plan complete! Advance to Tasks mode (SHIFT+TAB)",
177
+ ],
178
+ },
179
+ }
180
+
181
+ def __init__(self, base_path: Path | None = None):
182
+ """Initialize the placeholder hints manager.
183
+
184
+ Args:
185
+ base_path: Base path for .shotgun directory.
186
+ """
187
+ self.progress_checker = ModeProgressChecker(base_path)
188
+ self._last_hints: dict[str, str] = {} # Cache last selected hint per mode
189
+
190
+ def get_placeholder_for_mode(self, mode: AgentType, force_new: bool = False) -> str:
191
+ """Get a random placeholder hint for the given mode based on progress.
192
+
193
+ Args:
194
+ mode: The current agent mode.
195
+ force_new: If True, always select a new random hint.
196
+
197
+ Returns:
198
+ A randomly selected placeholder hint appropriate for the mode and progress.
199
+ """
200
+ # Determine if mode has content
201
+ has_content = self.progress_checker.has_mode_content(mode)
202
+
203
+ # Get hints for this mode and state
204
+ mode_hints = self.HINTS.get(mode, {})
205
+ state_hints = mode_hints.get(has_content, [])
206
+
207
+ if not state_hints:
208
+ # Fallback if mode not configured
209
+ return (
210
+ f"Type your message for {mode.value} mode (SHIFT+TAB to switch modes)"
211
+ )
212
+
213
+ # Cache key for this mode/state combination
214
+ cache_key = f"{mode.value}_{has_content}"
215
+
216
+ # If not forcing new and we have a cached hint, return it
217
+ if not force_new and cache_key in self._last_hints:
218
+ return self._last_hints[cache_key]
219
+
220
+ # Select a random hint
221
+ hint = random.choice(state_hints) # noqa: S311 - random is fine for UI hints
222
+ self._last_hints[cache_key] = hint
223
+
224
+ return hint
@@ -1,15 +0,0 @@
1
- ## Codebase Graphs Available
2
-
3
- {% if codebase_understanding_graphs -%}
4
-
5
- You have access to the following codebase graphs:
6
-
7
- {% for graph in codebase_understanding_graphs -%}
8
- - {{ graph.name }} ID: {{ graph.graph_id }} Path: {{ graph.repo_path }}
9
- {% endfor -%}
10
-
11
- {% else -%}
12
-
13
- You have no access to codebase graphs. You can optionally ask the user to load a codebase if the user asked a codebase question.
14
-
15
- {% endif %}