shotgun-sh 0.1.0__py3-none-any.whl

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 (130) hide show
  1. shotgun/__init__.py +5 -0
  2. shotgun/agents/__init__.py +1 -0
  3. shotgun/agents/agent_manager.py +651 -0
  4. shotgun/agents/common.py +549 -0
  5. shotgun/agents/config/__init__.py +13 -0
  6. shotgun/agents/config/constants.py +17 -0
  7. shotgun/agents/config/manager.py +294 -0
  8. shotgun/agents/config/models.py +185 -0
  9. shotgun/agents/config/provider.py +206 -0
  10. shotgun/agents/conversation_history.py +106 -0
  11. shotgun/agents/conversation_manager.py +105 -0
  12. shotgun/agents/export.py +96 -0
  13. shotgun/agents/history/__init__.py +5 -0
  14. shotgun/agents/history/compaction.py +85 -0
  15. shotgun/agents/history/constants.py +19 -0
  16. shotgun/agents/history/context_extraction.py +108 -0
  17. shotgun/agents/history/history_building.py +104 -0
  18. shotgun/agents/history/history_processors.py +426 -0
  19. shotgun/agents/history/message_utils.py +84 -0
  20. shotgun/agents/history/token_counting.py +429 -0
  21. shotgun/agents/history/token_estimation.py +138 -0
  22. shotgun/agents/messages.py +35 -0
  23. shotgun/agents/models.py +275 -0
  24. shotgun/agents/plan.py +98 -0
  25. shotgun/agents/research.py +108 -0
  26. shotgun/agents/specify.py +98 -0
  27. shotgun/agents/tasks.py +96 -0
  28. shotgun/agents/tools/__init__.py +34 -0
  29. shotgun/agents/tools/codebase/__init__.py +28 -0
  30. shotgun/agents/tools/codebase/codebase_shell.py +256 -0
  31. shotgun/agents/tools/codebase/directory_lister.py +141 -0
  32. shotgun/agents/tools/codebase/file_read.py +144 -0
  33. shotgun/agents/tools/codebase/models.py +252 -0
  34. shotgun/agents/tools/codebase/query_graph.py +67 -0
  35. shotgun/agents/tools/codebase/retrieve_code.py +81 -0
  36. shotgun/agents/tools/file_management.py +218 -0
  37. shotgun/agents/tools/user_interaction.py +37 -0
  38. shotgun/agents/tools/web_search/__init__.py +60 -0
  39. shotgun/agents/tools/web_search/anthropic.py +144 -0
  40. shotgun/agents/tools/web_search/gemini.py +85 -0
  41. shotgun/agents/tools/web_search/openai.py +98 -0
  42. shotgun/agents/tools/web_search/utils.py +20 -0
  43. shotgun/build_constants.py +20 -0
  44. shotgun/cli/__init__.py +1 -0
  45. shotgun/cli/codebase/__init__.py +5 -0
  46. shotgun/cli/codebase/commands.py +202 -0
  47. shotgun/cli/codebase/models.py +21 -0
  48. shotgun/cli/config.py +275 -0
  49. shotgun/cli/export.py +81 -0
  50. shotgun/cli/models.py +10 -0
  51. shotgun/cli/plan.py +73 -0
  52. shotgun/cli/research.py +85 -0
  53. shotgun/cli/specify.py +69 -0
  54. shotgun/cli/tasks.py +78 -0
  55. shotgun/cli/update.py +152 -0
  56. shotgun/cli/utils.py +25 -0
  57. shotgun/codebase/__init__.py +12 -0
  58. shotgun/codebase/core/__init__.py +46 -0
  59. shotgun/codebase/core/change_detector.py +358 -0
  60. shotgun/codebase/core/code_retrieval.py +243 -0
  61. shotgun/codebase/core/ingestor.py +1497 -0
  62. shotgun/codebase/core/language_config.py +297 -0
  63. shotgun/codebase/core/manager.py +1662 -0
  64. shotgun/codebase/core/nl_query.py +331 -0
  65. shotgun/codebase/core/parser_loader.py +128 -0
  66. shotgun/codebase/models.py +111 -0
  67. shotgun/codebase/service.py +206 -0
  68. shotgun/logging_config.py +227 -0
  69. shotgun/main.py +167 -0
  70. shotgun/posthog_telemetry.py +158 -0
  71. shotgun/prompts/__init__.py +5 -0
  72. shotgun/prompts/agents/__init__.py +1 -0
  73. shotgun/prompts/agents/export.j2 +350 -0
  74. shotgun/prompts/agents/partials/codebase_understanding.j2 +87 -0
  75. shotgun/prompts/agents/partials/common_agent_system_prompt.j2 +37 -0
  76. shotgun/prompts/agents/partials/content_formatting.j2 +65 -0
  77. shotgun/prompts/agents/partials/interactive_mode.j2 +26 -0
  78. shotgun/prompts/agents/plan.j2 +144 -0
  79. shotgun/prompts/agents/research.j2 +69 -0
  80. shotgun/prompts/agents/specify.j2 +51 -0
  81. shotgun/prompts/agents/state/codebase/codebase_graphs_available.j2 +19 -0
  82. shotgun/prompts/agents/state/system_state.j2 +31 -0
  83. shotgun/prompts/agents/tasks.j2 +143 -0
  84. shotgun/prompts/codebase/__init__.py +1 -0
  85. shotgun/prompts/codebase/cypher_query_patterns.j2 +223 -0
  86. shotgun/prompts/codebase/cypher_system.j2 +28 -0
  87. shotgun/prompts/codebase/enhanced_query_context.j2 +10 -0
  88. shotgun/prompts/codebase/partials/cypher_rules.j2 +24 -0
  89. shotgun/prompts/codebase/partials/graph_schema.j2 +30 -0
  90. shotgun/prompts/codebase/partials/temporal_context.j2 +21 -0
  91. shotgun/prompts/history/__init__.py +1 -0
  92. shotgun/prompts/history/incremental_summarization.j2 +53 -0
  93. shotgun/prompts/history/summarization.j2 +46 -0
  94. shotgun/prompts/loader.py +140 -0
  95. shotgun/py.typed +0 -0
  96. shotgun/sdk/__init__.py +13 -0
  97. shotgun/sdk/codebase.py +219 -0
  98. shotgun/sdk/exceptions.py +17 -0
  99. shotgun/sdk/models.py +189 -0
  100. shotgun/sdk/services.py +23 -0
  101. shotgun/sentry_telemetry.py +87 -0
  102. shotgun/telemetry.py +93 -0
  103. shotgun/tui/__init__.py +0 -0
  104. shotgun/tui/app.py +116 -0
  105. shotgun/tui/commands/__init__.py +76 -0
  106. shotgun/tui/components/prompt_input.py +69 -0
  107. shotgun/tui/components/spinner.py +86 -0
  108. shotgun/tui/components/splash.py +25 -0
  109. shotgun/tui/components/vertical_tail.py +13 -0
  110. shotgun/tui/screens/chat.py +782 -0
  111. shotgun/tui/screens/chat.tcss +43 -0
  112. shotgun/tui/screens/chat_screen/__init__.py +0 -0
  113. shotgun/tui/screens/chat_screen/command_providers.py +219 -0
  114. shotgun/tui/screens/chat_screen/hint_message.py +40 -0
  115. shotgun/tui/screens/chat_screen/history.py +221 -0
  116. shotgun/tui/screens/directory_setup.py +113 -0
  117. shotgun/tui/screens/provider_config.py +221 -0
  118. shotgun/tui/screens/splash.py +31 -0
  119. shotgun/tui/styles.tcss +10 -0
  120. shotgun/tui/utils/__init__.py +5 -0
  121. shotgun/tui/utils/mode_progress.py +257 -0
  122. shotgun/utils/__init__.py +5 -0
  123. shotgun/utils/env_utils.py +35 -0
  124. shotgun/utils/file_system_utils.py +36 -0
  125. shotgun/utils/update_checker.py +375 -0
  126. shotgun_sh-0.1.0.dist-info/METADATA +466 -0
  127. shotgun_sh-0.1.0.dist-info/RECORD +130 -0
  128. shotgun_sh-0.1.0.dist-info/WHEEL +4 -0
  129. shotgun_sh-0.1.0.dist-info/entry_points.txt +2 -0
  130. shotgun_sh-0.1.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,243 @@
1
+ """Code retrieval functionality for extracting source code from the knowledge graph."""
2
+
3
+ from pathlib import Path
4
+ from typing import TYPE_CHECKING
5
+
6
+ from pydantic import BaseModel
7
+
8
+ from shotgun.logging_config import get_logger
9
+
10
+ if TYPE_CHECKING:
11
+ from shotgun.codebase.core.manager import CodebaseGraphManager
12
+
13
+ logger = get_logger(__name__)
14
+
15
+
16
+ class CodeSnippet(BaseModel):
17
+ """Model for code snippet retrieval results."""
18
+
19
+ qualified_name: str
20
+ source_code: str
21
+ file_path: str
22
+ line_start: int
23
+ line_end: int
24
+ found: bool = True
25
+ error_message: str | None = None
26
+ docstring: str | None = None
27
+
28
+
29
+ async def retrieve_code_by_qualified_name(
30
+ manager: "CodebaseGraphManager", graph_id: str, qualified_name: str
31
+ ) -> CodeSnippet:
32
+ """Retrieve code snippet by qualified name.
33
+
34
+ Args:
35
+ manager: CodebaseGraphManager instance
36
+ graph_id: Graph ID to query
37
+ qualified_name: Fully qualified name of the entity
38
+
39
+ Returns:
40
+ CodeSnippet with the retrieved code
41
+ """
42
+ logger.info(f"Retrieving code for: {qualified_name}")
43
+
44
+ # Query to find the entity and its module
45
+ # The relationships in Kuzu are:
46
+ # - Module-[:DEFINES]->Class
47
+ # - Module-[:DEFINES]->Function
48
+ # - Class-[:DEFINES_METHOD]->Method
49
+ # For methods, we need to traverse back to the module
50
+ query = """
51
+ MATCH (n)
52
+ WHERE n.qualified_name = $qualified_name
53
+ OPTIONAL MATCH (m:Module)-[:DEFINES]->(n)
54
+ OPTIONAL MATCH (c:Class)-[:DEFINES_METHOD]->(n), (m2:Module)-[:DEFINES]->(c)
55
+ RETURN
56
+ n.name AS name,
57
+ n.line_start AS start_line,
58
+ n.line_end AS end_line,
59
+ COALESCE(m.path, m2.path) AS path,
60
+ COALESCE(n.docstring, NULL) AS docstring
61
+ LIMIT 1
62
+ """
63
+
64
+ try:
65
+ # Execute the query
66
+ results = await manager._execute_query(
67
+ graph_id, query, {"qualified_name": qualified_name}
68
+ )
69
+
70
+ if not results:
71
+ return CodeSnippet(
72
+ qualified_name=qualified_name,
73
+ source_code="",
74
+ file_path="",
75
+ line_start=0,
76
+ line_end=0,
77
+ found=False,
78
+ error_message=f"Entity '{qualified_name}' not found in graph.",
79
+ )
80
+
81
+ # Extract the first (and only) result
82
+ result = results[0]
83
+ file_path_str = result.get("path")
84
+ start_line = result.get("start_line", 0)
85
+ end_line = result.get("end_line", 0)
86
+ docstring = result.get("docstring")
87
+
88
+ # Check if we have all required location data
89
+ if not all([file_path_str, start_line, end_line]):
90
+ return CodeSnippet(
91
+ qualified_name=qualified_name,
92
+ source_code="",
93
+ file_path=file_path_str or "",
94
+ line_start=start_line or 0,
95
+ line_end=end_line or 0,
96
+ found=False,
97
+ error_message="Graph entry is missing location data. The DEFINES relationship may be missing.",
98
+ )
99
+
100
+ # Get the repository path from the Project node
101
+ repo_path_query = "MATCH (p:Project) RETURN p.repo_path LIMIT 1"
102
+ repo_results = await manager._execute_query(graph_id, repo_path_query)
103
+
104
+ if not repo_results or not repo_results[0].get("p.repo_path"):
105
+ return CodeSnippet(
106
+ qualified_name=qualified_name,
107
+ source_code="",
108
+ file_path=file_path_str or "",
109
+ line_start=start_line,
110
+ line_end=end_line,
111
+ found=False,
112
+ error_message="Repository path not found in graph.",
113
+ )
114
+
115
+ repo_path = Path(repo_results[0]["p.repo_path"])
116
+
117
+ # Read the source code from the file
118
+ if file_path_str is None:
119
+ return CodeSnippet(
120
+ qualified_name=qualified_name,
121
+ source_code="",
122
+ file_path="",
123
+ line_start=start_line,
124
+ line_end=end_line,
125
+ found=False,
126
+ error_message="File path is missing from graph data.",
127
+ )
128
+
129
+ full_path = repo_path / file_path_str
130
+
131
+ if not full_path.exists():
132
+ return CodeSnippet(
133
+ qualified_name=qualified_name,
134
+ source_code="",
135
+ file_path=file_path_str,
136
+ line_start=start_line,
137
+ line_end=end_line,
138
+ found=False,
139
+ error_message=f"Source file not found: {full_path}",
140
+ )
141
+
142
+ # Read the file and extract the snippet
143
+ try:
144
+ with full_path.open("r", encoding="utf-8") as f:
145
+ all_lines = f.readlines()
146
+
147
+ # Extract the relevant lines (1-indexed to 0-indexed)
148
+ snippet_lines = all_lines[start_line - 1 : end_line]
149
+ source_code = "".join(snippet_lines)
150
+
151
+ return CodeSnippet(
152
+ qualified_name=qualified_name,
153
+ source_code=source_code,
154
+ file_path=file_path_str,
155
+ line_start=start_line,
156
+ line_end=end_line,
157
+ docstring=docstring,
158
+ found=True,
159
+ )
160
+
161
+ except Exception as e:
162
+ logger.error(f"Error reading source file: {e}")
163
+ return CodeSnippet(
164
+ qualified_name=qualified_name,
165
+ source_code="",
166
+ file_path=file_path_str,
167
+ line_start=start_line,
168
+ line_end=end_line,
169
+ found=False,
170
+ error_message=f"Error reading source file: {str(e)}",
171
+ )
172
+
173
+ except Exception as e:
174
+ logger.error(f"Error retrieving code: {e}", exc_info=True)
175
+ return CodeSnippet(
176
+ qualified_name=qualified_name,
177
+ source_code="",
178
+ file_path="",
179
+ line_start=0,
180
+ line_end=0,
181
+ found=False,
182
+ error_message=str(e),
183
+ )
184
+
185
+
186
+ async def retrieve_code_by_cypher(
187
+ manager: "CodebaseGraphManager", graph_id: str, cypher_query: str
188
+ ) -> list[CodeSnippet]:
189
+ """Retrieve code snippets for all entities matching a Cypher query.
190
+
191
+ The query should return nodes with qualified_name property.
192
+
193
+ Args:
194
+ manager: CodebaseGraphManager instance
195
+ graph_id: Graph ID to query
196
+ cypher_query: Cypher query that returns entities
197
+
198
+ Returns:
199
+ List of CodeSnippet objects
200
+ """
201
+ logger.info(f"Retrieving code using Cypher query: {cypher_query}")
202
+
203
+ try:
204
+ # Execute the user's query
205
+ results = await manager._execute_query(graph_id, cypher_query)
206
+
207
+ if not results:
208
+ logger.info("No results from Cypher query")
209
+ return []
210
+
211
+ # Extract qualified names from results
212
+ qualified_names = set()
213
+ for result in results:
214
+ # Try different possible property names
215
+ for key in [
216
+ "qualified_name",
217
+ "n.qualified_name",
218
+ "c.qualified_name",
219
+ "f.qualified_name",
220
+ "m.qualified_name",
221
+ ]:
222
+ if key in result and result[key]:
223
+ qualified_names.add(result[key])
224
+ break
225
+
226
+ if not qualified_names:
227
+ logger.warning("Query results don't contain qualified_name properties")
228
+ return []
229
+
230
+ logger.info(f"Found {len(qualified_names)} entities to retrieve code for")
231
+
232
+ # Retrieve code for each qualified name
233
+ snippets = []
234
+ for qn in qualified_names:
235
+ snippet = await retrieve_code_by_qualified_name(manager, graph_id, qn)
236
+ snippets.append(snippet)
237
+
238
+ return snippets
239
+
240
+ except Exception as e:
241
+ logger.error(f"Error executing Cypher query: {e}", exc_info=True)
242
+ # Return empty list on query error
243
+ return []