siada-cli 0.0.1__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.
Files changed (215) hide show
  1. agent_config.yaml +34 -0
  2. siada/__init__.py +13 -0
  3. siada/agent_hub/__init__.py +10 -0
  4. siada/agent_hub/coder/bug_fix_agent.py +398 -0
  5. siada/agent_hub/coder/code_gen_agent.py +235 -0
  6. siada/agent_hub/coder/fe_gen_agent.py +37 -0
  7. siada/agent_hub/coder/issue_review_agent.py +68 -0
  8. siada/agent_hub/coder/prompt/__init__.py +0 -0
  9. siada/agent_hub/coder/prompt/base/__init__.py +0 -0
  10. siada/agent_hub/coder/prompt/base/capabilities.py +18 -0
  11. siada/agent_hub/coder/prompt/base/non_interactive.py +14 -0
  12. siada/agent_hub/coder/prompt/base/prompt_builder.py +33 -0
  13. siada/agent_hub/coder/prompt/base/rules.py +40 -0
  14. siada/agent_hub/coder/prompt/base/tool_use.py +14 -0
  15. siada/agent_hub/coder/prompt/bug_prompt/bug_fix_prompt.py +51 -0
  16. siada/agent_hub/coder/prompt/bug_prompt/rules.py +53 -0
  17. siada/agent_hub/coder/prompt/code_gen_prompt.py +45 -0
  18. siada/agent_hub/coder/prompt/fe_gen_prompt.py +45 -0
  19. siada/agent_hub/coder/prompt/issue_review_prompt.py +121 -0
  20. siada/agent_hub/coder/tracing/__init__.py +22 -0
  21. siada/agent_hub/coder/tracing/logger_tracing_processor.py +458 -0
  22. siada/agent_hub/hooks/__init__.py +0 -0
  23. siada/agent_hub/hooks/io_hooks.py +30 -0
  24. siada/agent_hub/siada_agent.py +214 -0
  25. siada/config/__init__.py +0 -0
  26. siada/config/config_loader.py +67 -0
  27. siada/entrypoint/__init__.py +3 -0
  28. siada/entrypoint/args_parser/__init__.py +0 -0
  29. siada/entrypoint/args_parser/args.py +195 -0
  30. siada/entrypoint/interaction/__init__.py +0 -0
  31. siada/entrypoint/interaction/base_turn.py +107 -0
  32. siada/entrypoint/interaction/config.py +30 -0
  33. siada/entrypoint/interaction/controller.py +122 -0
  34. siada/entrypoint/interaction/nointeractive_controller.py +27 -0
  35. siada/entrypoint/interaction/turn/interface.py +116 -0
  36. siada/entrypoint/interaction/turn/models.py +34 -0
  37. siada/entrypoint/interaction/turn/run_turn.py +641 -0
  38. siada/entrypoint/interaction/turn_factory.py +35 -0
  39. siada/entrypoint/interaction/turn_models.py +28 -0
  40. siada/entrypoint/siadahub.py +413 -0
  41. siada/foundation/__init__.py +0 -0
  42. siada/foundation/code_agent_context.py +48 -0
  43. siada/foundation/config.py +59 -0
  44. siada/foundation/constants.py +2 -0
  45. siada/foundation/context.py +76 -0
  46. siada/foundation/logging.py +341 -0
  47. siada/foundation/tools/__init__.py +0 -0
  48. siada/foundation/tools/get_git_diff.py +105 -0
  49. siada/io/__init__.py +0 -0
  50. siada/io/banner.py +148 -0
  51. siada/io/color_settings.py +207 -0
  52. siada/io/color_utils.py +16 -0
  53. siada/io/components/__init__.py +0 -0
  54. siada/io/components/mdstream.py +204 -0
  55. siada/io/console_printer.py +68 -0
  56. siada/io/io.py +661 -0
  57. siada/io/key_bindings.py +129 -0
  58. siada/io/notification_command.py +40 -0
  59. siada/io/test_banner.py +64 -0
  60. siada/models/__init__.py +0 -0
  61. siada/models/agent.py +47 -0
  62. siada/models/coder/__init__.py +0 -0
  63. siada/models/converter.py +27 -0
  64. siada/models/model_base_config.py +121 -0
  65. siada/models/model_run_config.py +153 -0
  66. siada/provider/__init__.py +0 -0
  67. siada/provider/client_factory.py +112 -0
  68. siada/provider/llm_client.py +11 -0
  69. siada/provider/openrouter/__init__.py +0 -0
  70. siada/provider/openrouter/coverter.py +17 -0
  71. siada/provider/openrouter/openroute_provider.py +33 -0
  72. siada/provider/provider_factory.py +53 -0
  73. siada/queries/tree-sitter-language-pack/README.md +7 -0
  74. siada/queries/tree-sitter-language-pack/arduino-tags.scm +5 -0
  75. siada/queries/tree-sitter-language-pack/c-tags.scm +9 -0
  76. siada/queries/tree-sitter-language-pack/chatito-tags.scm +16 -0
  77. siada/queries/tree-sitter-language-pack/commonlisp-tags.scm +122 -0
  78. siada/queries/tree-sitter-language-pack/cpp-tags.scm +15 -0
  79. siada/queries/tree-sitter-language-pack/csharp-tags.scm +26 -0
  80. siada/queries/tree-sitter-language-pack/d-tags.scm +26 -0
  81. siada/queries/tree-sitter-language-pack/dart-tags.scm +92 -0
  82. siada/queries/tree-sitter-language-pack/elisp-tags.scm +5 -0
  83. siada/queries/tree-sitter-language-pack/elixir-tags.scm +54 -0
  84. siada/queries/tree-sitter-language-pack/elm-tags.scm +19 -0
  85. siada/queries/tree-sitter-language-pack/gleam-tags.scm +41 -0
  86. siada/queries/tree-sitter-language-pack/go-tags.scm +42 -0
  87. siada/queries/tree-sitter-language-pack/java-tags.scm +20 -0
  88. siada/queries/tree-sitter-language-pack/javascript-tags.scm +88 -0
  89. siada/queries/tree-sitter-language-pack/lua-tags.scm +34 -0
  90. siada/queries/tree-sitter-language-pack/ocaml-tags.scm +115 -0
  91. siada/queries/tree-sitter-language-pack/ocaml_interface-tags.scm +98 -0
  92. siada/queries/tree-sitter-language-pack/pony-tags.scm +39 -0
  93. siada/queries/tree-sitter-language-pack/properties-tags.scm +5 -0
  94. siada/queries/tree-sitter-language-pack/python-tags.scm +14 -0
  95. siada/queries/tree-sitter-language-pack/r-tags.scm +21 -0
  96. siada/queries/tree-sitter-language-pack/racket-tags.scm +12 -0
  97. siada/queries/tree-sitter-language-pack/ruby-tags.scm +64 -0
  98. siada/queries/tree-sitter-language-pack/rust-tags.scm +60 -0
  99. siada/queries/tree-sitter-language-pack/solidity-tags.scm +43 -0
  100. siada/queries/tree-sitter-language-pack/swift-tags.scm +51 -0
  101. siada/queries/tree-sitter-language-pack/udev-tags.scm +20 -0
  102. siada/queries/tree-sitter-languages/README.md +23 -0
  103. siada/queries/tree-sitter-languages/c-tags.scm +9 -0
  104. siada/queries/tree-sitter-languages/c_sharp-tags.scm +46 -0
  105. siada/queries/tree-sitter-languages/cpp-tags.scm +15 -0
  106. siada/queries/tree-sitter-languages/dart-tags.scm +91 -0
  107. siada/queries/tree-sitter-languages/elisp-tags.scm +8 -0
  108. siada/queries/tree-sitter-languages/elixir-tags.scm +54 -0
  109. siada/queries/tree-sitter-languages/elm-tags.scm +19 -0
  110. siada/queries/tree-sitter-languages/go-tags.scm +30 -0
  111. siada/queries/tree-sitter-languages/hcl-tags.scm +77 -0
  112. siada/queries/tree-sitter-languages/java-tags.scm +20 -0
  113. siada/queries/tree-sitter-languages/javascript-tags.scm +88 -0
  114. siada/queries/tree-sitter-languages/kotlin-tags.scm +27 -0
  115. siada/queries/tree-sitter-languages/ocaml-tags.scm +115 -0
  116. siada/queries/tree-sitter-languages/ocaml_interface-tags.scm +98 -0
  117. siada/queries/tree-sitter-languages/php-tags.scm +26 -0
  118. siada/queries/tree-sitter-languages/python-tags.scm +12 -0
  119. siada/queries/tree-sitter-languages/ql-tags.scm +26 -0
  120. siada/queries/tree-sitter-languages/ruby-tags.scm +64 -0
  121. siada/queries/tree-sitter-languages/rust-tags.scm +60 -0
  122. siada/queries/tree-sitter-languages/scala-tags.scm +65 -0
  123. siada/queries/tree-sitter-languages/typescript-tags.scm +41 -0
  124. siada/services/__init__.py +30 -0
  125. siada/services/at_file_recommendation_design_document.md +1213 -0
  126. siada/services/code_context_manager.py +136 -0
  127. siada/services/enhanced_fix_result_check.py +546 -0
  128. siada/services/execution_trace_collector.py +207 -0
  129. siada/services/file_recommendation/__init__.py +136 -0
  130. siada/services/file_recommendation/core/__init__.py +17 -0
  131. siada/services/file_recommendation/core/completion_engine.py +351 -0
  132. siada/services/file_recommendation/core/config.py +62 -0
  133. siada/services/file_recommendation/core/file_discovery.py +353 -0
  134. siada/services/file_recommendation/core/suggestion.py +110 -0
  135. siada/services/file_recommendation/utils/__init__.py +22 -0
  136. siada/services/file_recommendation/utils/path_utils.py +138 -0
  137. siada/services/file_recommendation/utils/text_utils.py +253 -0
  138. siada/services/fix_result_check.py +377 -0
  139. siada/services/handle_at_command/README.md +247 -0
  140. siada/services/handle_at_command/__init__.py +115 -0
  141. siada/services/handle_at_command/exceptions.py +46 -0
  142. siada/services/handle_at_command/models.py +92 -0
  143. siada/services/handle_at_command/parser.py +189 -0
  144. siada/services/handle_at_command/processor.py +321 -0
  145. siada/services/handle_at_command/resolver.py +318 -0
  146. siada/services/handle_at_command/utils.py +345 -0
  147. siada/services/model_info_service.py +103 -0
  148. siada/services/siada_runner.py +170 -0
  149. siada/session/__init__.py +29 -0
  150. siada/session/session_manager.py +66 -0
  151. siada/session/session_models.py +43 -0
  152. siada/support/__init__.py +0 -0
  153. siada/support/completer.py +204 -0
  154. siada/support/editor.py +146 -0
  155. siada/support/envprocessor.py +54 -0
  156. siada/support/repo.py +51 -0
  157. siada/support/slash_commands.py +410 -0
  158. siada/support/spinner.py +320 -0
  159. siada/tools/__init__.py +0 -0
  160. siada/tools/ast/README.md +345 -0
  161. siada/tools/ast/ast_tool.py +341 -0
  162. siada/tools/ast/models.py +11 -0
  163. siada/tools/coder/__init__.py +0 -0
  164. siada/tools/coder/ask_followup_question.py +70 -0
  165. siada/tools/coder/cmd_runner.py +132 -0
  166. siada/tools/coder/file_operator.py +284 -0
  167. siada/tools/coder/file_search/README.md +223 -0
  168. siada/tools/coder/file_search/__init__.py +10 -0
  169. siada/tools/coder/file_search/bin/rg-linux-arm64 +0 -0
  170. siada/tools/coder/file_search/bin/rg-linux-x64 +0 -0
  171. siada/tools/coder/file_search/bin/rg-macos-arm64 +0 -0
  172. siada/tools/coder/file_search/bin/rg-macos-x64 +0 -0
  173. siada/tools/coder/file_search/bin/rg.exe +0 -0
  174. siada/tools/coder/file_search/search.py +437 -0
  175. siada/tools/coder/file_search/setup_package.py +150 -0
  176. siada/tools/coder/file_search/test_search.py +142 -0
  177. siada/tools/coder/files.py +147 -0
  178. siada/tools/coder/fix_attempt_completion.py +58 -0
  179. siada/tools/coder/issue_review_completion.py +29 -0
  180. siada/tools/coder/observation/__init__.py +0 -0
  181. siada/tools/coder/observation/error.py +22 -0
  182. siada/tools/coder/observation/file_observation.py +216 -0
  183. siada/tools/coder/observation/observation.py +71 -0
  184. siada/tools/coder/repo_map/__init__.py +157 -0
  185. siada/tools/coder/repo_map/dump.py +29 -0
  186. siada/tools/coder/repo_map/io.py +226 -0
  187. siada/tools/coder/repo_map/repo_map.py +881 -0
  188. siada/tools/coder/repo_map/special.py +203 -0
  189. siada/tools/coder/repo_map/token_counter.py +154 -0
  190. siada/tools/coder/repo_map/waiting.py +221 -0
  191. siada/tools/coder/reproduce_completion.py +39 -0
  192. siada/tools/coder/run_cmd.py +60 -0
  193. siada/tools/coder/test_completion.py +25 -0
  194. siada/tools/coder/tool_docs.py +44 -0
  195. siada/tools/compression_tool.py +200 -0
  196. siada/tools/read_many_files/README.md +220 -0
  197. siada/tools/read_many_files/__init__.py +25 -0
  198. siada/tools/read_many_files/file_processor.py +486 -0
  199. siada/tools/read_many_files/filters.py +326 -0
  200. siada/tools/read_many_files/formatters.py +204 -0
  201. siada/tools/read_many_files/models.py +189 -0
  202. siada/tools/read_many_files_tool.py +233 -0
  203. siada/tools/tool_call_format/__init__.py +55 -0
  204. siada/tools/tool_call_format/file_to_language.py +231 -0
  205. siada/tools/tool_call_format/formatter_factory.py +80 -0
  206. siada/tools/tool_call_format/formatters.py +282 -0
  207. siada/tools/tool_call_format/tool_call_formatter.py +54 -0
  208. siada/tools/web/__init__.py +0 -0
  209. siada/tools/web/web_crawl.py +208 -0
  210. siada/utils.py +194 -0
  211. siada_cli-0.0.1.dist-info/LICENSE +201 -0
  212. siada_cli-0.0.1.dist-info/METADATA +457 -0
  213. siada_cli-0.0.1.dist-info/RECORD +215 -0
  214. siada_cli-0.0.1.dist-info/WHEEL +4 -0
  215. siada_cli-0.0.1.dist-info/entry_points.txt +3 -0
@@ -0,0 +1,351 @@
1
+ """
2
+ Completion engine for file recommendation system.
3
+ """
4
+
5
+ import asyncio
6
+ import time
7
+ from typing import List, Optional
8
+
9
+ from .config import CompletionConfig, FilterOptions
10
+ from .file_discovery import FileDiscoveryService
11
+ from .suggestion import Suggestion, sort_suggestions, limit_suggestions
12
+ from ..utils.text_utils import parse_at_command_path, extract_at_path_from_text
13
+ from ..utils.path_utils import escape_path
14
+
15
+
16
+ class CompletionEngine:
17
+ """
18
+ Main completion engine that coordinates all components
19
+ """
20
+
21
+ def __init__(self, search_directories: List[str], config: CompletionConfig):
22
+ """
23
+ Initialize completion engine
24
+
25
+ Args:
26
+ search_directories: List of directories to search in
27
+ config: Completion configuration
28
+ """
29
+ self.search_directories = search_directories
30
+ self.config = config
31
+ self.file_discovery_services = {}
32
+
33
+ # Initialize file discovery services for each directory
34
+ for directory in search_directories:
35
+ self.file_discovery_services[directory] = FileDiscoveryService(directory)
36
+
37
+ async def get_suggestions(self, text: str) -> List[Suggestion]:
38
+ """
39
+ Get file suggestions for the given text
40
+
41
+ Args:
42
+ text: Input text containing @ command
43
+
44
+ Returns:
45
+ List of file suggestions
46
+ """
47
+ start_time = time.time()
48
+
49
+ try:
50
+ # Extract @ path from text
51
+ at_path = extract_at_path_from_text(text)
52
+ if not at_path:
53
+ return []
54
+
55
+ # Parse the @ command path
56
+ base_dir, prefix, partial_path = parse_at_command_path(text)
57
+
58
+ # If no prefix, return directory listing
59
+ if not prefix and base_dir == ".":
60
+ return await self._get_directory_listing()
61
+
62
+ # Search for matching files
63
+ suggestions = await self._search_files(base_dir, prefix)
64
+
65
+ # Sort and limit results
66
+ sorted_suggestions = sort_suggestions(suggestions)
67
+ limited_suggestions = limit_suggestions(sorted_suggestions, self.config.max_results)
68
+
69
+ return limited_suggestions
70
+
71
+ except Exception as e:
72
+ # Return empty list on error, with optional logging
73
+ print(f"Error in completion engine: {e}")
74
+ return []
75
+
76
+ finally:
77
+ # Optional: track performance
78
+ elapsed_time = time.time() - start_time
79
+ if elapsed_time > self.config.search_timeout_ms / 1000:
80
+ print(f"Warning: Search took {elapsed_time:.2f}s, exceeding timeout")
81
+
82
+ async def _get_directory_listing(self) -> List[Suggestion]:
83
+ """
84
+ Get listing of current directory files
85
+
86
+ Returns:
87
+ List of suggestions for current directory
88
+ """
89
+ all_suggestions = []
90
+ filter_options = self.config.get_filter_options()
91
+
92
+ for directory in self.search_directories:
93
+ if directory in self.file_discovery_services:
94
+ service = self.file_discovery_services[directory]
95
+ suggestions = service.find_files_in_directory(
96
+ directory,
97
+ "",
98
+ filter_options,
99
+ self.config.max_results
100
+ )
101
+ all_suggestions.extend(suggestions)
102
+
103
+ return all_suggestions
104
+
105
+ async def _search_files(self, base_dir: str, prefix: str) -> List[Suggestion]:
106
+ """
107
+ Search for files matching the prefix
108
+
109
+ Args:
110
+ base_dir: Base directory to search in
111
+ prefix: Search prefix
112
+
113
+ Returns:
114
+ List of matching suggestions
115
+ """
116
+ all_suggestions = []
117
+ filter_options = self.config.get_filter_options()
118
+
119
+ # Create search tasks for all directories
120
+ search_tasks = []
121
+
122
+ for directory in self.search_directories:
123
+ if directory in self.file_discovery_services:
124
+ service = self.file_discovery_services[directory]
125
+
126
+ # Determine actual search directory
127
+ if base_dir == ".":
128
+ search_dir = directory
129
+ else:
130
+ search_dir = self._resolve_search_directory(directory, base_dir)
131
+ if not search_dir:
132
+ continue
133
+
134
+ # Create search task
135
+ if self.config.enable_recursive_search:
136
+ task = service.find_files_recursively(
137
+ search_dir,
138
+ prefix,
139
+ filter_options,
140
+ "",
141
+ 0,
142
+ self.config.max_search_depth,
143
+ self.config.max_results
144
+ )
145
+ else:
146
+ # Use synchronous method wrapped in async
147
+ task = asyncio.create_task(
148
+ asyncio.to_thread(
149
+ service.find_files_in_directory,
150
+ search_dir,
151
+ prefix,
152
+ filter_options,
153
+ self.config.max_results
154
+ )
155
+ )
156
+
157
+ search_tasks.append(task)
158
+
159
+ # Execute search tasks with timeout
160
+ try:
161
+ results = await asyncio.wait_for(
162
+ asyncio.gather(*search_tasks, return_exceptions=True),
163
+ timeout=self.config.search_timeout_ms / 1000
164
+ )
165
+
166
+ # Collect results from successful tasks
167
+ for result in results:
168
+ if isinstance(result, list):
169
+ all_suggestions.extend(result)
170
+ elif isinstance(result, Exception):
171
+ # Log exception but continue
172
+ print(f"Search task failed: {result}")
173
+
174
+ except asyncio.TimeoutError:
175
+ # Handle timeout - return partial results
176
+ print(f"Search timeout after {self.config.search_timeout_ms}ms")
177
+
178
+ # Cancel remaining tasks
179
+ for task in search_tasks:
180
+ if not task.done():
181
+ task.cancel()
182
+
183
+ # Collect results from completed tasks
184
+ for task in search_tasks:
185
+ if task.done() and not task.cancelled():
186
+ try:
187
+ result = task.result()
188
+ if isinstance(result, list):
189
+ all_suggestions.extend(result)
190
+ except Exception:
191
+ pass
192
+
193
+ return all_suggestions
194
+
195
+ def _resolve_search_directory(self, base_directory: str, relative_path: str) -> Optional[str]:
196
+ """
197
+ Resolve search directory from base directory and relative path
198
+
199
+ Args:
200
+ base_directory: Base directory
201
+ relative_path: Relative path from @ command
202
+
203
+ Returns:
204
+ Resolved directory path or None if invalid
205
+ """
206
+ import os
207
+
208
+ try:
209
+ # Remove trailing slash from relative_path if present
210
+ relative_path = relative_path.rstrip('/')
211
+
212
+ # Join paths
213
+ resolved_path = os.path.join(base_directory, relative_path)
214
+ resolved_path = os.path.abspath(resolved_path)
215
+
216
+ # Security check - ensure resolved path is within base directory
217
+ base_abs = os.path.abspath(base_directory)
218
+ if not resolved_path.startswith(base_abs):
219
+ return None
220
+
221
+ # Check if directory exists
222
+ if os.path.exists(resolved_path) and os.path.isdir(resolved_path):
223
+ return resolved_path
224
+
225
+ return None
226
+
227
+ except Exception:
228
+ return None
229
+
230
+ def get_suggestions_sync(self, text: str) -> List[Suggestion]:
231
+ """
232
+ Synchronous version of get_suggestions
233
+
234
+ Args:
235
+ text: Input text containing @ command
236
+
237
+ Returns:
238
+ List of file suggestions
239
+ """
240
+ try:
241
+ loop = asyncio.get_event_loop()
242
+ return loop.run_until_complete(self.get_suggestions(text))
243
+ except RuntimeError:
244
+ # No event loop running, create a new one
245
+ return asyncio.run(self.get_suggestions(text))
246
+
247
+ def should_show_suggestions(self, text: str) -> bool:
248
+ """
249
+ Check if suggestions should be shown for the given text
250
+
251
+ Args:
252
+ text: Input text
253
+
254
+ Returns:
255
+ bool: True if suggestions should be shown
256
+ """
257
+ # Basic check - look for @ character
258
+ at_path = extract_at_path_from_text(text)
259
+ if at_path is None:
260
+ return False
261
+
262
+ # Don't show suggestions for lone @ symbol
263
+ if at_path == '@':
264
+ return False
265
+
266
+ # Check if the path corresponds to a complete existing file
267
+ path_content = at_path[1:] # Remove @ prefix
268
+ if self._is_complete_file_path(path_content):
269
+ return False
270
+
271
+ # Show suggestions for @ with content
272
+ return True
273
+
274
+ def _is_complete_file_path(self, path: str) -> bool:
275
+ """
276
+ Check if path corresponds to a complete existing file
277
+
278
+ Args:
279
+ path: Path to check (without @ prefix)
280
+
281
+ Returns:
282
+ bool: True if path points to an existing file
283
+ """
284
+ import os
285
+
286
+ if not path:
287
+ return False
288
+
289
+ # Check in all search directories
290
+ for search_dir in self.search_directories:
291
+ full_path = os.path.join(search_dir, path)
292
+ if os.path.isfile(full_path):
293
+ return True
294
+
295
+ return False
296
+
297
+ def update_config(self, config: CompletionConfig):
298
+ """
299
+ Update completion configuration
300
+
301
+ Args:
302
+ config: New configuration
303
+ """
304
+ self.config = config
305
+
306
+ def add_search_directory(self, directory: str):
307
+ """
308
+ Add a new search directory
309
+
310
+ Args:
311
+ directory: Directory to add
312
+ """
313
+ if directory not in self.search_directories:
314
+ self.search_directories.append(directory)
315
+ self.file_discovery_services[directory] = FileDiscoveryService(directory)
316
+
317
+ def remove_search_directory(self, directory: str):
318
+ """
319
+ Remove a search directory
320
+
321
+ Args:
322
+ directory: Directory to remove
323
+ """
324
+ if directory in self.search_directories:
325
+ self.search_directories.remove(directory)
326
+ if directory in self.file_discovery_services:
327
+ del self.file_discovery_services[directory]
328
+
329
+ def clear_cache(self):
330
+ """
331
+ Clear any cached data (for future cache implementation)
332
+ """
333
+ # Placeholder for cache clearing functionality
334
+ pass
335
+
336
+ def get_stats(self) -> dict:
337
+ """
338
+ Get performance and usage statistics
339
+
340
+ Returns:
341
+ Dictionary containing stats
342
+ """
343
+ return {
344
+ 'search_directories': len(self.search_directories),
345
+ 'config': {
346
+ 'max_results': self.config.max_results,
347
+ 'max_depth': self.config.max_search_depth,
348
+ 'recursive_search': self.config.enable_recursive_search,
349
+ 'git_ignore': self.config.respect_git_ignore
350
+ }
351
+ }
@@ -0,0 +1,62 @@
1
+ """
2
+ Configuration classes for file recommendation system.
3
+ """
4
+
5
+ from dataclasses import dataclass
6
+ from typing import List
7
+
8
+
9
+ @dataclass
10
+ class FilterOptions:
11
+ """File filtering configuration options"""
12
+ respect_git_ignore: bool = True
13
+
14
+
15
+ @dataclass
16
+ class CompletionConfig:
17
+ """Auto-completion configuration"""
18
+
19
+ # Search configuration
20
+ enable_recursive_search: bool = True
21
+ max_search_depth: int = 10
22
+ max_results: int = 50
23
+ debounce_delay_ms: int = 100
24
+
25
+ # Display configuration
26
+ max_visible_suggestions: int = 8
27
+ suggestion_width: int = 60
28
+
29
+ # Filtering configuration
30
+ respect_git_ignore: bool = True
31
+
32
+ # Performance configuration
33
+ search_timeout_ms: int = 5000
34
+
35
+ # User experience configuration
36
+ show_loading_indicator: bool = True
37
+ auto_select_first: bool = True
38
+
39
+ def get_filter_options(self) -> FilterOptions:
40
+ """Get filter options from config"""
41
+ return FilterOptions(
42
+ respect_git_ignore=self.respect_git_ignore
43
+ )
44
+
45
+
46
+ @dataclass
47
+ class WorkspaceConfig:
48
+ """Workspace configuration"""
49
+ directories: List[str]
50
+ current_directory: str
51
+ project_root: str
52
+
53
+ def get_search_directories(self) -> List[str]:
54
+ """Get search directory list"""
55
+ return self.directories if self.directories else [self.current_directory]
56
+
57
+
58
+ # Default configuration instance
59
+ DEFAULT_COMPLETION_CONFIG = CompletionConfig()
60
+
61
+ # Default filter options
62
+ DEFAULT_FILTER_OPTIONS = FilterOptions()