auto-coder 0.1.400__py3-none-any.whl → 2.0.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 auto-coder might be problematic. Click here for more details.

Files changed (579) hide show
  1. auto_coder-2.0.0.dist-info/LICENSE +158 -0
  2. auto_coder-2.0.0.dist-info/METADATA +558 -0
  3. auto_coder-2.0.0.dist-info/RECORD +795 -0
  4. {auto_coder-0.1.400.dist-info → auto_coder-2.0.0.dist-info}/WHEEL +1 -1
  5. {auto_coder-0.1.400.dist-info → auto_coder-2.0.0.dist-info}/entry_points.txt +3 -3
  6. autocoder/__init__.py +31 -0
  7. autocoder/agent/auto_filegroup.py +32 -13
  8. autocoder/agent/auto_learn_from_commit.py +9 -1
  9. autocoder/agent/base_agentic/__init__.py +3 -0
  10. autocoder/agent/base_agentic/agent_hub.py +1 -1
  11. autocoder/agent/base_agentic/base_agent.py +235 -136
  12. autocoder/agent/base_agentic/default_tools.py +119 -118
  13. autocoder/agent/base_agentic/test_base_agent.py +1 -1
  14. autocoder/agent/base_agentic/tool_registry.py +32 -20
  15. autocoder/agent/base_agentic/tools/read_file_tool_resolver.py +25 -4
  16. autocoder/agent/base_agentic/tools/write_to_file_tool_resolver.py +24 -11
  17. autocoder/agent/base_agentic/types.py +42 -0
  18. autocoder/agent/entry_command_agent/chat.py +73 -59
  19. autocoder/auto_coder.py +31 -40
  20. autocoder/auto_coder_rag.py +11 -1084
  21. autocoder/auto_coder_runner.py +1029 -2310
  22. autocoder/auto_coder_terminal.py +26 -0
  23. autocoder/auto_coder_terminal_v3.py +190 -0
  24. autocoder/chat/conf_command.py +224 -124
  25. autocoder/chat/models_command.py +361 -299
  26. autocoder/chat/rules_command.py +79 -31
  27. autocoder/chat_auto_coder.py +1021 -372
  28. autocoder/chat_auto_coder_lang.py +23 -732
  29. autocoder/commands/auto_command.py +26 -9
  30. autocoder/commands/auto_web.py +1 -1
  31. autocoder/commands/tools.py +44 -44
  32. autocoder/common/__init__.py +150 -128
  33. autocoder/common/ac_style_command_parser/__init__.py +39 -2
  34. autocoder/common/ac_style_command_parser/config.py +422 -0
  35. autocoder/common/ac_style_command_parser/parser.py +292 -78
  36. autocoder/common/ac_style_command_parser/test_parser.py +241 -16
  37. autocoder/common/ac_style_command_parser/test_typed_parser.py +342 -0
  38. autocoder/common/ac_style_command_parser/typed_parser.py +653 -0
  39. autocoder/common/action_yml_file_manager.py +25 -13
  40. autocoder/common/agent_events/__init__.py +52 -0
  41. autocoder/common/agent_events/agent_event_emitter.py +193 -0
  42. autocoder/common/agent_events/event_factory.py +177 -0
  43. autocoder/common/agent_events/examples.py +307 -0
  44. autocoder/common/agent_events/types.py +113 -0
  45. autocoder/common/agent_events/utils.py +68 -0
  46. autocoder/common/agent_hooks/__init__.py +44 -0
  47. autocoder/common/agent_hooks/examples.py +582 -0
  48. autocoder/common/agent_hooks/hook_executor.py +217 -0
  49. autocoder/common/agent_hooks/hook_manager.py +288 -0
  50. autocoder/common/agent_hooks/types.py +133 -0
  51. autocoder/common/agent_hooks/utils.py +99 -0
  52. autocoder/common/agent_query_queue/queue_executor.py +324 -0
  53. autocoder/common/agent_query_queue/queue_manager.py +325 -0
  54. autocoder/common/agents/__init__.py +11 -0
  55. autocoder/common/agents/agent_manager.py +323 -0
  56. autocoder/common/agents/agent_parser.py +189 -0
  57. autocoder/common/agents/example_usage.py +344 -0
  58. autocoder/common/agents/integration_example.py +330 -0
  59. autocoder/common/agents/test_agent_parser.py +545 -0
  60. autocoder/common/async_utils.py +101 -0
  61. autocoder/common/auto_coder_lang.py +23 -972
  62. autocoder/common/autocoderargs_parser/__init__.py +14 -0
  63. autocoder/common/autocoderargs_parser/parser.py +184 -0
  64. autocoder/common/autocoderargs_parser/tests/__init__.py +1 -0
  65. autocoder/common/autocoderargs_parser/tests/test_args_parser.py +235 -0
  66. autocoder/common/autocoderargs_parser/tests/test_token_parser.py +195 -0
  67. autocoder/common/autocoderargs_parser/token_parser.py +290 -0
  68. autocoder/common/buildin_tokenizer.py +2 -4
  69. autocoder/common/code_auto_generate.py +149 -74
  70. autocoder/common/code_auto_generate_diff.py +163 -70
  71. autocoder/common/code_auto_generate_editblock.py +179 -89
  72. autocoder/common/code_auto_generate_strict_diff.py +167 -72
  73. autocoder/common/code_auto_merge_editblock.py +13 -6
  74. autocoder/common/code_modification_ranker.py +1 -1
  75. autocoder/common/command_completer.py +3 -3
  76. autocoder/common/command_file_manager/manager.py +183 -47
  77. autocoder/common/command_file_manager/test_command_file_manager.py +507 -0
  78. autocoder/common/command_templates.py +1 -1
  79. autocoder/common/conf_utils.py +2 -4
  80. autocoder/common/conversations/config.py +11 -3
  81. autocoder/common/conversations/get_conversation_manager.py +100 -2
  82. autocoder/common/conversations/llm_stats_models.py +264 -0
  83. autocoder/common/conversations/manager.py +112 -28
  84. autocoder/common/conversations/models.py +16 -2
  85. autocoder/common/conversations/storage/index_manager.py +134 -10
  86. autocoder/common/core_config/__init__.py +63 -0
  87. autocoder/common/core_config/agentic_mode_manager.py +109 -0
  88. autocoder/common/core_config/base_manager.py +123 -0
  89. autocoder/common/core_config/compatibility.py +151 -0
  90. autocoder/common/core_config/config_manager.py +156 -0
  91. autocoder/common/core_config/conversation_manager.py +31 -0
  92. autocoder/common/core_config/exclude_manager.py +72 -0
  93. autocoder/common/core_config/file_manager.py +177 -0
  94. autocoder/common/core_config/human_as_model_manager.py +129 -0
  95. autocoder/common/core_config/lib_manager.py +54 -0
  96. autocoder/common/core_config/main_manager.py +81 -0
  97. autocoder/common/core_config/mode_manager.py +126 -0
  98. autocoder/common/core_config/models.py +70 -0
  99. autocoder/common/core_config/test_memory_manager.py +1056 -0
  100. autocoder/common/env_manager.py +282 -0
  101. autocoder/common/env_manager_usage_example.py +211 -0
  102. autocoder/common/file_checkpoint/conversation_checkpoint.py +19 -19
  103. autocoder/common/file_checkpoint/manager.py +264 -48
  104. autocoder/common/file_checkpoint/test_backup.py +1 -18
  105. autocoder/common/file_checkpoint/test_manager.py +270 -1
  106. autocoder/common/file_checkpoint/test_store.py +1 -17
  107. autocoder/common/file_handler/__init__.py +23 -0
  108. autocoder/common/file_handler/active_context_handler.py +159 -0
  109. autocoder/common/file_handler/add_files_handler.py +409 -0
  110. autocoder/common/file_handler/chat_handler.py +180 -0
  111. autocoder/common/file_handler/coding_handler.py +401 -0
  112. autocoder/common/file_handler/commit_handler.py +200 -0
  113. autocoder/common/file_handler/lib_handler.py +156 -0
  114. autocoder/common/file_handler/list_files_handler.py +111 -0
  115. autocoder/common/file_handler/mcp_handler.py +268 -0
  116. autocoder/common/file_handler/models_handler.py +493 -0
  117. autocoder/common/file_handler/remove_files_handler.py +172 -0
  118. autocoder/common/file_monitor/test_file_monitor.py +307 -0
  119. autocoder/common/git_utils.py +51 -10
  120. autocoder/common/global_cancel.py +15 -6
  121. autocoder/common/ignorefiles/test_ignore_file_utils.py +1 -1
  122. autocoder/common/international/__init__.py +31 -0
  123. autocoder/common/international/demo_international.py +92 -0
  124. autocoder/common/international/message_manager.py +157 -0
  125. autocoder/common/international/messages/__init__.py +56 -0
  126. autocoder/common/international/messages/async_command_messages.py +507 -0
  127. autocoder/common/international/messages/auto_coder_messages.py +2208 -0
  128. autocoder/common/international/messages/chat_auto_coder_messages.py +1547 -0
  129. autocoder/common/international/messages/command_help_messages.py +986 -0
  130. autocoder/common/international/messages/conversation_command_messages.py +191 -0
  131. autocoder/common/international/messages/git_helper_plugin_messages.py +159 -0
  132. autocoder/common/international/messages/queue_command_messages.py +751 -0
  133. autocoder/common/international/messages/rules_command_messages.py +77 -0
  134. autocoder/common/international/messages/sdk_messages.py +1707 -0
  135. autocoder/common/international/messages/token_helper_plugin_messages.py +361 -0
  136. autocoder/common/international/messages/tool_display_messages.py +1212 -0
  137. autocoder/common/international/messages/workflow_exception_messages.py +473 -0
  138. autocoder/common/international/test_international.py +612 -0
  139. autocoder/common/linter_core/__init__.py +28 -0
  140. autocoder/common/linter_core/base_linter.py +61 -0
  141. autocoder/common/linter_core/config_loader.py +271 -0
  142. autocoder/common/linter_core/formatters/__init__.py +0 -0
  143. autocoder/common/linter_core/formatters/base_formatter.py +38 -0
  144. autocoder/common/linter_core/formatters/raw_formatter.py +17 -0
  145. autocoder/common/linter_core/linter.py +166 -0
  146. autocoder/common/linter_core/linter_factory.py +216 -0
  147. autocoder/common/linter_core/linter_manager.py +333 -0
  148. autocoder/common/linter_core/linters/__init__.py +9 -0
  149. autocoder/common/linter_core/linters/java_linter.py +342 -0
  150. autocoder/common/linter_core/linters/python_linter.py +115 -0
  151. autocoder/common/linter_core/linters/typescript_linter.py +119 -0
  152. autocoder/common/linter_core/models/__init__.py +7 -0
  153. autocoder/common/linter_core/models/lint_result.py +91 -0
  154. autocoder/common/linter_core/models.py +33 -0
  155. autocoder/common/linter_core/tests/__init__.py +3 -0
  156. autocoder/common/linter_core/tests/test_config_loader.py +323 -0
  157. autocoder/common/linter_core/tests/test_config_loading.py +308 -0
  158. autocoder/common/linter_core/tests/test_factory_manager.py +234 -0
  159. autocoder/common/linter_core/tests/test_formatters.py +147 -0
  160. autocoder/common/linter_core/tests/test_integration.py +317 -0
  161. autocoder/common/linter_core/tests/test_java_linter.py +496 -0
  162. autocoder/common/linter_core/tests/test_linters.py +265 -0
  163. autocoder/common/linter_core/tests/test_models.py +81 -0
  164. autocoder/common/linter_core/tests/verify_config_loading.py +296 -0
  165. autocoder/common/linter_core/tests/verify_fixes.py +183 -0
  166. autocoder/common/llm_friendly_package/__init__.py +31 -0
  167. autocoder/common/llm_friendly_package/base_manager.py +102 -0
  168. autocoder/common/llm_friendly_package/docs_manager.py +121 -0
  169. autocoder/common/llm_friendly_package/library_manager.py +171 -0
  170. autocoder/common/{llm_friendly_package.py → llm_friendly_package/main_manager.py} +204 -231
  171. autocoder/common/llm_friendly_package/models.py +40 -0
  172. autocoder/common/llm_friendly_package/test_llm_friendly_package.py +536 -0
  173. autocoder/common/llms/__init__.py +15 -0
  174. autocoder/common/llms/demo_error_handling.py +85 -0
  175. autocoder/common/llms/factory.py +142 -0
  176. autocoder/common/llms/manager.py +264 -0
  177. autocoder/common/llms/pricing.py +121 -0
  178. autocoder/common/llms/registry.py +288 -0
  179. autocoder/common/llms/schema.py +77 -0
  180. autocoder/common/llms/simple_demo.py +45 -0
  181. autocoder/common/llms/test_quick_model.py +116 -0
  182. autocoder/common/llms/test_remove_functionality.py +182 -0
  183. autocoder/common/llms/tests/__init__.py +1 -0
  184. autocoder/common/llms/tests/test_manager.py +330 -0
  185. autocoder/common/llms/tests/test_registry.py +364 -0
  186. autocoder/common/mcp_tools/__init__.py +62 -0
  187. autocoder/common/{mcp_tools.py → mcp_tools/executor.py} +49 -40
  188. autocoder/common/{mcp_hub.py → mcp_tools/hub.py} +42 -68
  189. autocoder/common/{mcp_server_install.py → mcp_tools/installer.py} +16 -28
  190. autocoder/common/{mcp_server.py → mcp_tools/server.py} +176 -48
  191. autocoder/common/mcp_tools/test_keyboard_interrupt.py +93 -0
  192. autocoder/common/mcp_tools/test_mcp_tools.py +391 -0
  193. autocoder/common/{mcp_server_types.py → mcp_tools/types.py} +121 -48
  194. autocoder/common/mcp_tools/verify_functionality.py +202 -0
  195. autocoder/common/model_speed_tester.py +32 -26
  196. autocoder/common/priority_directory_finder/__init__.py +142 -0
  197. autocoder/common/priority_directory_finder/examples.py +230 -0
  198. autocoder/common/priority_directory_finder/finder.py +283 -0
  199. autocoder/common/priority_directory_finder/models.py +236 -0
  200. autocoder/common/priority_directory_finder/test_priority_directory_finder.py +431 -0
  201. autocoder/common/project_scanner/__init__.py +18 -0
  202. autocoder/common/project_scanner/compat.py +77 -0
  203. autocoder/common/project_scanner/scanner.py +436 -0
  204. autocoder/common/project_tracker/__init__.py +27 -0
  205. autocoder/common/project_tracker/api.py +228 -0
  206. autocoder/common/project_tracker/demo.py +272 -0
  207. autocoder/common/project_tracker/tracker.py +487 -0
  208. autocoder/common/project_tracker/types.py +53 -0
  209. autocoder/common/pruner/__init__.py +67 -0
  210. autocoder/common/pruner/agentic_conversation_pruner.py +746 -0
  211. autocoder/common/{context_pruner.py → pruner/context_pruner.py} +137 -40
  212. autocoder/common/pruner/conversation_message_ids_api.py +386 -0
  213. autocoder/common/pruner/conversation_message_ids_manager.py +347 -0
  214. autocoder/common/pruner/conversation_message_ids_pruner.py +473 -0
  215. autocoder/common/pruner/conversation_normalizer.py +347 -0
  216. autocoder/common/{conversation_pruner.py → pruner/conversation_pruner.py} +26 -6
  217. autocoder/common/pruner/test_agentic_conversation_pruner.py +784 -0
  218. autocoder/common/pruner/test_context_pruner.py +546 -0
  219. autocoder/common/pruner/test_conversation_normalizer.py +502 -0
  220. autocoder/common/pruner/test_tool_content_detector.py +324 -0
  221. autocoder/common/pruner/tool_content_detector.py +227 -0
  222. autocoder/common/pruner/tools/__init__.py +18 -0
  223. autocoder/common/pruner/tools/query_message_ids.py +264 -0
  224. autocoder/common/pruner/tools/test_agentic_pruning_logic.py +432 -0
  225. autocoder/common/pruner/tools/test_message_ids_pruning_only.py +192 -0
  226. autocoder/common/pull_requests/__init__.py +9 -1
  227. autocoder/common/pull_requests/utils.py +122 -1
  228. autocoder/common/rag_manager/rag_manager.py +36 -40
  229. autocoder/common/rulefiles/__init__.py +53 -1
  230. autocoder/common/rulefiles/api.py +250 -0
  231. autocoder/common/rulefiles/core/__init__.py +14 -0
  232. autocoder/common/rulefiles/core/manager.py +241 -0
  233. autocoder/common/rulefiles/core/selector.py +805 -0
  234. autocoder/common/rulefiles/models/__init__.py +20 -0
  235. autocoder/common/rulefiles/models/index.py +16 -0
  236. autocoder/common/rulefiles/models/init_rule.py +18 -0
  237. autocoder/common/rulefiles/models/rule_file.py +18 -0
  238. autocoder/common/rulefiles/models/rule_relevance.py +14 -0
  239. autocoder/common/rulefiles/models/summary.py +16 -0
  240. autocoder/common/rulefiles/test_rulefiles.py +776 -0
  241. autocoder/common/rulefiles/utils/__init__.py +34 -0
  242. autocoder/common/rulefiles/utils/monitor.py +86 -0
  243. autocoder/common/rulefiles/utils/parser.py +230 -0
  244. autocoder/common/save_formatted_log.py +67 -10
  245. autocoder/common/search_replace.py +8 -1
  246. autocoder/common/search_replace_patch/__init__.py +24 -0
  247. autocoder/common/search_replace_patch/base.py +115 -0
  248. autocoder/common/search_replace_patch/manager.py +248 -0
  249. autocoder/common/search_replace_patch/patch_replacer.py +304 -0
  250. autocoder/common/search_replace_patch/similarity_replacer.py +306 -0
  251. autocoder/common/search_replace_patch/string_replacer.py +181 -0
  252. autocoder/common/search_replace_patch/tests/__init__.py +3 -0
  253. autocoder/common/search_replace_patch/tests/run_tests.py +126 -0
  254. autocoder/common/search_replace_patch/tests/test_base.py +188 -0
  255. autocoder/common/search_replace_patch/tests/test_empty_line_insert.py +233 -0
  256. autocoder/common/search_replace_patch/tests/test_integration.py +389 -0
  257. autocoder/common/search_replace_patch/tests/test_manager.py +351 -0
  258. autocoder/common/search_replace_patch/tests/test_patch_replacer.py +316 -0
  259. autocoder/common/search_replace_patch/tests/test_regex_replacer.py +306 -0
  260. autocoder/common/search_replace_patch/tests/test_similarity_replacer.py +384 -0
  261. autocoder/common/shell_commands/__init__.py +197 -0
  262. autocoder/common/shell_commands/background_process_notifier.py +346 -0
  263. autocoder/common/shell_commands/command_executor.py +1127 -0
  264. autocoder/common/shell_commands/error_recovery.py +541 -0
  265. autocoder/common/shell_commands/exceptions.py +120 -0
  266. autocoder/common/shell_commands/interactive_executor.py +476 -0
  267. autocoder/common/shell_commands/interactive_pexpect_process.py +623 -0
  268. autocoder/common/shell_commands/interactive_process.py +744 -0
  269. autocoder/common/shell_commands/interactive_session_manager.py +1014 -0
  270. autocoder/common/shell_commands/monitoring.py +529 -0
  271. autocoder/common/shell_commands/process_cleanup.py +386 -0
  272. autocoder/common/shell_commands/process_manager.py +606 -0
  273. autocoder/common/shell_commands/test_interactive_pexpect_process.py +281 -0
  274. autocoder/common/shell_commands/tests/__init__.py +6 -0
  275. autocoder/common/shell_commands/tests/conftest.py +118 -0
  276. autocoder/common/shell_commands/tests/test_background_process_notifier.py +703 -0
  277. autocoder/common/shell_commands/tests/test_command_executor.py +448 -0
  278. autocoder/common/shell_commands/tests/test_error_recovery.py +305 -0
  279. autocoder/common/shell_commands/tests/test_exceptions.py +299 -0
  280. autocoder/common/shell_commands/tests/test_execute_batch.py +588 -0
  281. autocoder/common/shell_commands/tests/test_indented_batch_commands.py +244 -0
  282. autocoder/common/shell_commands/tests/test_integration.py +664 -0
  283. autocoder/common/shell_commands/tests/test_monitoring.py +546 -0
  284. autocoder/common/shell_commands/tests/test_performance.py +632 -0
  285. autocoder/common/shell_commands/tests/test_process_cleanup.py +397 -0
  286. autocoder/common/shell_commands/tests/test_process_manager.py +606 -0
  287. autocoder/common/shell_commands/tests/test_timeout_config.py +343 -0
  288. autocoder/common/shell_commands/tests/test_timeout_manager.py +520 -0
  289. autocoder/common/shell_commands/timeout_config.py +315 -0
  290. autocoder/common/shell_commands/timeout_manager.py +352 -0
  291. autocoder/common/terminal_paste/__init__.py +14 -0
  292. autocoder/common/terminal_paste/demo.py +145 -0
  293. autocoder/common/terminal_paste/demo_paste_functionality.py +95 -0
  294. autocoder/common/terminal_paste/paste_handler.py +200 -0
  295. autocoder/common/terminal_paste/paste_manager.py +118 -0
  296. autocoder/common/terminal_paste/tests/__init__.py +1 -0
  297. autocoder/common/terminal_paste/tests/test_paste_handler.py +182 -0
  298. autocoder/common/terminal_paste/tests/test_paste_manager.py +126 -0
  299. autocoder/common/terminal_paste/utils.py +163 -0
  300. autocoder/common/test_autocoder_args.py +232 -0
  301. autocoder/common/test_env_manager.py +173 -0
  302. autocoder/common/test_env_manager_integration.py +159 -0
  303. autocoder/common/text_similarity/__init__.py +9 -0
  304. autocoder/common/text_similarity/demo.py +216 -0
  305. autocoder/common/text_similarity/examples.py +266 -0
  306. autocoder/common/text_similarity/test_text_similarity.py +306 -0
  307. autocoder/common/text_similarity/text_similarity.py +194 -0
  308. autocoder/common/text_similarity/utils.py +125 -0
  309. autocoder/common/todos/__init__.py +61 -0
  310. autocoder/common/todos/cache/__init__.py +16 -0
  311. autocoder/common/todos/cache/base_cache.py +89 -0
  312. autocoder/common/todos/cache/cache_manager.py +228 -0
  313. autocoder/common/todos/cache/memory_cache.py +225 -0
  314. autocoder/common/todos/config.py +155 -0
  315. autocoder/common/todos/exceptions.py +35 -0
  316. autocoder/common/todos/get_todo_manager.py +161 -0
  317. autocoder/common/todos/manager.py +537 -0
  318. autocoder/common/todos/models.py +239 -0
  319. autocoder/common/todos/storage/__init__.py +14 -0
  320. autocoder/common/todos/storage/base_storage.py +76 -0
  321. autocoder/common/todos/storage/file_storage.py +278 -0
  322. autocoder/common/tokens/__init__.py +15 -0
  323. autocoder/common/tokens/counter.py +44 -2
  324. autocoder/common/tools_manager/__init__.py +17 -0
  325. autocoder/common/tools_manager/examples.py +162 -0
  326. autocoder/common/tools_manager/manager.py +385 -0
  327. autocoder/common/tools_manager/models.py +39 -0
  328. autocoder/common/tools_manager/test_tools_manager.py +303 -0
  329. autocoder/common/tools_manager/utils.py +191 -0
  330. autocoder/common/v2/agent/agentic_callbacks.py +270 -0
  331. autocoder/common/v2/agent/agentic_edit.py +2729 -2052
  332. autocoder/common/v2/agent/agentic_edit_change_manager.py +474 -0
  333. autocoder/common/v2/agent/agentic_edit_tools/__init__.py +43 -2
  334. autocoder/common/v2/agent/agentic_edit_tools/ac_mod_list_tool_resolver.py +279 -0
  335. autocoder/common/v2/agent/agentic_edit_tools/ac_mod_read_tool_resolver.py +40 -0
  336. autocoder/common/v2/agent/agentic_edit_tools/ac_mod_write_tool_resolver.py +52 -0
  337. autocoder/common/v2/agent/agentic_edit_tools/ask_followup_question_tool_resolver.py +8 -0
  338. autocoder/common/v2/agent/agentic_edit_tools/background_task_tool_resolver.py +1167 -0
  339. autocoder/common/v2/agent/agentic_edit_tools/base_tool_resolver.py +2 -2
  340. autocoder/common/v2/agent/agentic_edit_tools/conversation_message_ids_read_tool_resolver.py +214 -0
  341. autocoder/common/v2/agent/agentic_edit_tools/conversation_message_ids_write_tool_resolver.py +299 -0
  342. autocoder/common/v2/agent/agentic_edit_tools/count_tokens_tool_resolver.py +290 -0
  343. autocoder/common/v2/agent/agentic_edit_tools/execute_command_tool_resolver.py +565 -30
  344. autocoder/common/v2/agent/agentic_edit_tools/execute_workflow_tool_resolver.py +485 -0
  345. autocoder/common/v2/agent/agentic_edit_tools/extract_to_text_tool_resolver.py +225 -0
  346. autocoder/common/v2/agent/agentic_edit_tools/lint_report.py +79 -0
  347. autocoder/common/v2/agent/agentic_edit_tools/linter_config_models.py +343 -0
  348. autocoder/common/v2/agent/agentic_edit_tools/linter_enabled_tool_resolver.py +189 -0
  349. autocoder/common/v2/agent/agentic_edit_tools/list_files_tool_resolver.py +169 -101
  350. autocoder/common/v2/agent/agentic_edit_tools/load_extra_document_tool_resolver.py +349 -0
  351. autocoder/common/v2/agent/agentic_edit_tools/read_file_tool_resolver.py +244 -51
  352. autocoder/common/v2/agent/agentic_edit_tools/replace_in_file_tool_resolver.py +667 -147
  353. autocoder/common/v2/agent/agentic_edit_tools/run_named_subagents_tool_resolver.py +691 -0
  354. autocoder/common/v2/agent/agentic_edit_tools/search_files_tool_resolver.py +409 -140
  355. autocoder/common/v2/agent/agentic_edit_tools/session_interactive_tool_resolver.py +115 -0
  356. autocoder/common/v2/agent/agentic_edit_tools/session_start_tool_resolver.py +190 -0
  357. autocoder/common/v2/agent/agentic_edit_tools/session_stop_tool_resolver.py +76 -0
  358. autocoder/common/v2/agent/agentic_edit_tools/test_write_to_file_tool_resolver.py +209 -194
  359. autocoder/common/v2/agent/agentic_edit_tools/todo_read_tool_resolver.py +135 -0
  360. autocoder/common/v2/agent/agentic_edit_tools/todo_write_tool_resolver.py +328 -0
  361. autocoder/common/v2/agent/agentic_edit_tools/use_mcp_tool_resolver.py +2 -2
  362. autocoder/common/v2/agent/agentic_edit_tools/web_crawl_tool_resolver.py +557 -0
  363. autocoder/common/v2/agent/agentic_edit_tools/web_search_tool_resolver.py +600 -0
  364. autocoder/common/v2/agent/agentic_edit_tools/write_to_file_tool_resolver.py +56 -121
  365. autocoder/common/v2/agent/agentic_edit_types.py +386 -10
  366. autocoder/common/v2/agent/runner/__init__.py +31 -0
  367. autocoder/common/v2/agent/runner/base_runner.py +92 -0
  368. autocoder/common/v2/agent/runner/file_based_event_runner.py +217 -0
  369. autocoder/common/v2/agent/runner/sdk_runner.py +182 -0
  370. autocoder/common/v2/agent/runner/terminal_runner.py +396 -0
  371. autocoder/common/v2/agent/runner/tool_display.py +589 -0
  372. autocoder/common/v2/agent/test_agentic_callbacks.py +265 -0
  373. autocoder/common/v2/agent/test_agentic_edit.py +194 -0
  374. autocoder/common/v2/agent/tool_caller/__init__.py +24 -0
  375. autocoder/common/v2/agent/tool_caller/default_tool_resolver_map.py +135 -0
  376. autocoder/common/v2/agent/tool_caller/integration_test.py +172 -0
  377. autocoder/common/v2/agent/tool_caller/plugins/__init__.py +14 -0
  378. autocoder/common/v2/agent/tool_caller/plugins/base_plugin.py +126 -0
  379. autocoder/common/v2/agent/tool_caller/plugins/examples/__init__.py +13 -0
  380. autocoder/common/v2/agent/tool_caller/plugins/examples/logging_plugin.py +164 -0
  381. autocoder/common/v2/agent/tool_caller/plugins/examples/security_filter_plugin.py +198 -0
  382. autocoder/common/v2/agent/tool_caller/plugins/plugin_interface.py +141 -0
  383. autocoder/common/v2/agent/tool_caller/test_tool_caller.py +278 -0
  384. autocoder/common/v2/agent/tool_caller/tool_call_plugin_manager.py +331 -0
  385. autocoder/common/v2/agent/tool_caller/tool_caller.py +337 -0
  386. autocoder/common/v2/agent/tool_caller/usage_example.py +193 -0
  387. autocoder/common/v2/code_agentic_editblock_manager.py +4 -4
  388. autocoder/common/v2/code_auto_generate.py +136 -78
  389. autocoder/common/v2/code_auto_generate_diff.py +135 -79
  390. autocoder/common/v2/code_auto_generate_editblock.py +174 -99
  391. autocoder/common/v2/code_auto_generate_strict_diff.py +151 -71
  392. autocoder/common/v2/code_auto_merge.py +1 -1
  393. autocoder/common/v2/code_auto_merge_editblock.py +13 -1
  394. autocoder/common/v2/code_diff_manager.py +3 -3
  395. autocoder/common/v2/code_editblock_manager.py +4 -14
  396. autocoder/common/v2/code_manager.py +1 -1
  397. autocoder/common/v2/code_strict_diff_manager.py +2 -2
  398. autocoder/common/wrap_llm_hint/__init__.py +10 -0
  399. autocoder/common/wrap_llm_hint/test_wrap_llm_hint.py +1067 -0
  400. autocoder/common/wrap_llm_hint/utils.py +432 -0
  401. autocoder/common/wrap_llm_hint/wrap_llm_hint.py +323 -0
  402. autocoder/completer/__init__.py +8 -0
  403. autocoder/completer/command_completer_v2.py +1051 -0
  404. autocoder/default_project/__init__.py +501 -0
  405. autocoder/dispacher/__init__.py +4 -12
  406. autocoder/dispacher/actions/action.py +165 -7
  407. autocoder/dispacher/actions/plugins/action_regex_project.py +2 -2
  408. autocoder/index/entry.py +117 -125
  409. autocoder/{agent → index/filter}/agentic_filter.py +323 -334
  410. autocoder/index/filter/normal_filter.py +5 -11
  411. autocoder/index/filter/quick_filter.py +1 -1
  412. autocoder/index/index.py +36 -9
  413. autocoder/index/tests/__init__.py +1 -0
  414. autocoder/index/tests/run_tests.py +195 -0
  415. autocoder/index/tests/test_entry.py +303 -0
  416. autocoder/index/tests/test_index_manager.py +314 -0
  417. autocoder/index/tests/test_module_integration.py +300 -0
  418. autocoder/index/tests/test_symbols_utils.py +183 -0
  419. autocoder/inner/__init__.py +4 -0
  420. autocoder/inner/agentic.py +932 -0
  421. autocoder/inner/async_command_handler.py +992 -0
  422. autocoder/inner/conversation_command_handlers.py +623 -0
  423. autocoder/inner/merge_command_handler.py +213 -0
  424. autocoder/inner/queue_command_handler.py +684 -0
  425. autocoder/models.py +95 -266
  426. autocoder/plugins/git_helper_plugin.py +31 -29
  427. autocoder/plugins/token_helper_plugin.py +156 -37
  428. autocoder/pyproject/__init__.py +32 -29
  429. autocoder/rag/agentic_rag.py +215 -75
  430. autocoder/rag/cache/simple_cache.py +1 -2
  431. autocoder/rag/loaders/image_loader.py +1 -1
  432. autocoder/rag/long_context_rag.py +42 -26
  433. autocoder/rag/qa_conversation_strategy.py +1 -1
  434. autocoder/rag/terminal/__init__.py +17 -0
  435. autocoder/rag/terminal/args.py +581 -0
  436. autocoder/rag/terminal/bootstrap.py +61 -0
  437. autocoder/rag/terminal/command_handlers.py +653 -0
  438. autocoder/rag/terminal/formatters/__init__.py +20 -0
  439. autocoder/rag/terminal/formatters/base.py +70 -0
  440. autocoder/rag/terminal/formatters/json_format.py +66 -0
  441. autocoder/rag/terminal/formatters/stream_json.py +95 -0
  442. autocoder/rag/terminal/formatters/text.py +28 -0
  443. autocoder/rag/terminal/init.py +120 -0
  444. autocoder/rag/terminal/utils.py +106 -0
  445. autocoder/rag/test_agentic_rag.py +389 -0
  446. autocoder/rag/test_doc_filter.py +3 -3
  447. autocoder/rag/test_long_context_rag.py +1 -1
  448. autocoder/rag/test_token_limiter.py +517 -10
  449. autocoder/rag/token_counter.py +3 -0
  450. autocoder/rag/token_limiter.py +19 -15
  451. autocoder/rag/tools/__init__.py +26 -2
  452. autocoder/rag/tools/bochaai_example.py +343 -0
  453. autocoder/rag/tools/bochaai_sdk.py +541 -0
  454. autocoder/rag/tools/metaso_example.py +268 -0
  455. autocoder/rag/tools/metaso_sdk.py +417 -0
  456. autocoder/rag/tools/recall_tool.py +28 -7
  457. autocoder/rag/tools/run_integration_tests.py +204 -0
  458. autocoder/rag/tools/test_all_providers.py +318 -0
  459. autocoder/rag/tools/test_bochaai_integration.py +482 -0
  460. autocoder/rag/tools/test_final_integration.py +215 -0
  461. autocoder/rag/tools/test_metaso_integration.py +424 -0
  462. autocoder/rag/tools/test_metaso_real.py +171 -0
  463. autocoder/rag/tools/test_web_crawl_tool.py +639 -0
  464. autocoder/rag/tools/test_web_search_tool.py +509 -0
  465. autocoder/rag/tools/todo_read_tool.py +202 -0
  466. autocoder/rag/tools/todo_write_tool.py +412 -0
  467. autocoder/rag/tools/web_crawl_tool.py +634 -0
  468. autocoder/rag/tools/web_search_tool.py +558 -0
  469. autocoder/rag/tools/web_tools_example.py +119 -0
  470. autocoder/rag/types.py +16 -0
  471. autocoder/rag/variable_holder.py +4 -2
  472. autocoder/rags.py +86 -79
  473. autocoder/regexproject/__init__.py +23 -21
  474. autocoder/run_context.py +9 -0
  475. autocoder/sdk/__init__.py +50 -161
  476. autocoder/sdk/api.py +370 -0
  477. autocoder/sdk/async_runner/__init__.py +26 -0
  478. autocoder/sdk/async_runner/async_executor.py +650 -0
  479. autocoder/sdk/async_runner/async_handler.py +356 -0
  480. autocoder/sdk/async_runner/markdown_processor.py +595 -0
  481. autocoder/sdk/async_runner/task_metadata.py +284 -0
  482. autocoder/sdk/async_runner/worktree_manager.py +438 -0
  483. autocoder/sdk/cli/__init__.py +2 -5
  484. autocoder/sdk/cli/formatters.py +28 -204
  485. autocoder/sdk/cli/handlers.py +77 -44
  486. autocoder/sdk/cli/main.py +158 -170
  487. autocoder/sdk/cli/options.py +95 -22
  488. autocoder/sdk/constants.py +139 -51
  489. autocoder/sdk/core/auto_coder_core.py +484 -267
  490. autocoder/sdk/core/bridge.py +298 -118
  491. autocoder/sdk/exceptions.py +18 -12
  492. autocoder/sdk/formatters/__init__.py +19 -0
  493. autocoder/sdk/formatters/input.py +64 -0
  494. autocoder/sdk/formatters/output.py +247 -0
  495. autocoder/sdk/formatters/stream.py +54 -0
  496. autocoder/sdk/models/__init__.py +6 -5
  497. autocoder/sdk/models/options.py +55 -18
  498. autocoder/sdk/utils/formatters.py +27 -195
  499. autocoder/suffixproject/__init__.py +28 -25
  500. autocoder/terminal/__init__.py +14 -0
  501. autocoder/terminal/app.py +454 -0
  502. autocoder/terminal/args.py +32 -0
  503. autocoder/terminal/bootstrap.py +178 -0
  504. autocoder/terminal/command_processor.py +521 -0
  505. autocoder/terminal/command_registry.py +57 -0
  506. autocoder/terminal/help.py +97 -0
  507. autocoder/terminal/tasks/__init__.py +5 -0
  508. autocoder/terminal/tasks/background.py +77 -0
  509. autocoder/terminal/tasks/task_event.py +70 -0
  510. autocoder/terminal/ui/__init__.py +13 -0
  511. autocoder/terminal/ui/completer.py +268 -0
  512. autocoder/terminal/ui/keybindings.py +75 -0
  513. autocoder/terminal/ui/session.py +41 -0
  514. autocoder/terminal/ui/toolbar.py +64 -0
  515. autocoder/terminal/utils/__init__.py +13 -0
  516. autocoder/terminal/utils/errors.py +18 -0
  517. autocoder/terminal/utils/paths.py +19 -0
  518. autocoder/terminal/utils/shell.py +43 -0
  519. autocoder/terminal_v3/__init__.py +10 -0
  520. autocoder/terminal_v3/app.py +201 -0
  521. autocoder/terminal_v3/handlers/__init__.py +5 -0
  522. autocoder/terminal_v3/handlers/command_handler.py +131 -0
  523. autocoder/terminal_v3/models/__init__.py +6 -0
  524. autocoder/terminal_v3/models/conversation_buffer.py +214 -0
  525. autocoder/terminal_v3/models/message.py +50 -0
  526. autocoder/terminal_v3/models/tool_display.py +247 -0
  527. autocoder/terminal_v3/ui/__init__.py +7 -0
  528. autocoder/terminal_v3/ui/keybindings.py +56 -0
  529. autocoder/terminal_v3/ui/layout.py +141 -0
  530. autocoder/terminal_v3/ui/styles.py +43 -0
  531. autocoder/tsproject/__init__.py +23 -23
  532. autocoder/utils/auto_coder_utils/chat_stream_out.py +1 -1
  533. autocoder/utils/llms.py +88 -80
  534. autocoder/utils/math_utils.py +101 -0
  535. autocoder/utils/model_provider_selector.py +16 -4
  536. autocoder/utils/operate_config_api.py +33 -5
  537. autocoder/utils/thread_utils.py +2 -2
  538. autocoder/version.py +4 -2
  539. autocoder/workflow_agents/__init__.py +84 -0
  540. autocoder/workflow_agents/agent.py +143 -0
  541. autocoder/workflow_agents/exceptions.py +573 -0
  542. autocoder/workflow_agents/executor.py +489 -0
  543. autocoder/workflow_agents/loader.py +737 -0
  544. autocoder/workflow_agents/runner.py +267 -0
  545. autocoder/workflow_agents/types.py +172 -0
  546. autocoder/workflow_agents/utils.py +434 -0
  547. autocoder/workflow_agents/workflow_manager.py +211 -0
  548. auto_coder-0.1.400.dist-info/METADATA +0 -396
  549. auto_coder-0.1.400.dist-info/RECORD +0 -425
  550. auto_coder-0.1.400.dist-info/licenses/LICENSE +0 -201
  551. autocoder/auto_coder_server.py +0 -672
  552. autocoder/benchmark.py +0 -138
  553. autocoder/common/ac_style_command_parser/example.py +0 -7
  554. autocoder/common/cleaner.py +0 -31
  555. autocoder/common/command_completer_v2.py +0 -615
  556. autocoder/common/directory_cache/__init__.py +0 -1
  557. autocoder/common/directory_cache/cache.py +0 -192
  558. autocoder/common/directory_cache/test_cache.py +0 -190
  559. autocoder/common/file_checkpoint/examples.py +0 -217
  560. autocoder/common/llm_friendly_package_example.py +0 -138
  561. autocoder/common/llm_friendly_package_test.py +0 -63
  562. autocoder/common/pull_requests/test_module.py +0 -1
  563. autocoder/common/rulefiles/autocoderrules_utils.py +0 -484
  564. autocoder/common/text.py +0 -30
  565. autocoder/common/v2/agent/agentic_edit_tools/list_package_info_tool_resolver.py +0 -42
  566. autocoder/common/v2/agent/agentic_edit_tools/test_execute_command_tool_resolver.py +0 -70
  567. autocoder/common/v2/agent/agentic_edit_tools/test_search_files_tool_resolver.py +0 -163
  568. autocoder/common/v2/agent/agentic_tool_display.py +0 -183
  569. autocoder/plugins/dynamic_completion_example.py +0 -148
  570. autocoder/plugins/sample_plugin.py +0 -160
  571. autocoder/sdk/cli/__main__.py +0 -26
  572. autocoder/sdk/cli/completion_wrapper.py +0 -38
  573. autocoder/sdk/cli/install_completion.py +0 -301
  574. autocoder/sdk/models/messages.py +0 -209
  575. autocoder/sdk/session/__init__.py +0 -32
  576. autocoder/sdk/session/session.py +0 -106
  577. autocoder/sdk/session/session_manager.py +0 -56
  578. {auto_coder-0.1.400.dist-info → auto_coder-2.0.0.dist-info}/top_level.txt +0 -0
  579. /autocoder/{sdk/example.py → common/agent_query_queue/__init__.py} +0 -0
@@ -2,186 +2,455 @@ import os
2
2
  import re
3
3
  import glob
4
4
  from typing import Dict, Any, Optional, List, Union
5
+
6
+ from pydantic import BaseModel, Field
5
7
  from autocoder.common.v2.agent.agentic_edit_tools.base_tool_resolver import BaseToolResolver
6
- from autocoder.common.v2.agent.agentic_edit_types import SearchFilesTool, ToolResult # Import ToolResult from types
7
- from loguru import logger
8
+ from autocoder.common.v2.agent.agentic_edit_types import SearchFilesTool, ToolResult
8
9
  from autocoder.common import AutoCoderArgs
10
+ from autocoder.common.ignorefiles.ignore_file_utils import should_ignore
11
+ from loguru import logger
9
12
  import typing
13
+ import json
10
14
 
11
- from autocoder.common.ignorefiles.ignore_file_utils import should_ignore
15
+ # Import token counter and wrap_llm_hint modules
16
+ from autocoder.common.tokens import count_string_tokens
17
+ from autocoder.common.wrap_llm_hint.utils import add_hint_to_text
12
18
 
13
19
  if typing.TYPE_CHECKING:
14
- from autocoder.common.v2.agent.agentic_edit import AgenticEdit
20
+ from autocoder.common.v2.agent.agentic_edit import AgenticEdit
21
+
22
+
23
+ class SearchParameters(BaseModel):
24
+ """Strongly typed search parameters for file searching operations.
25
+
26
+ This class ensures type safety and validation for all search-related parameters.
27
+ """
28
+ search_path_str: str = Field(..., description="Original search path string for error messages")
29
+ regex_pattern: str = Field(..., description="Regular expression pattern to search for")
30
+ file_pattern: str = Field(..., description="Glob pattern for file filtering (e.g., '*.py')")
31
+ source_dir: str = Field(..., description="Source directory path")
32
+ absolute_source_dir: str = Field(..., description="Absolute path to source directory")
33
+ absolute_search_path: str = Field(..., description="Absolute path to search directory")
34
+
35
+ class Config:
36
+ """Pydantic configuration."""
37
+ frozen = True # Make the class immutable
38
+ extra = "forbid" # Prevent extra fields
39
+
40
+
41
+ class SearchErrorInfo(BaseModel):
42
+ """Information about errors encountered during search operations."""
43
+ file_path: str = Field(..., description="Path to the file/directory that caused the error")
44
+ error_type: str = Field(..., description="Type of error (e.g., 'PermissionError', 'FileNotFoundError')")
45
+ error_message: str = Field(..., description="Detailed error message")
46
+
47
+ class Config:
48
+ """Pydantic configuration."""
49
+ frozen = True
50
+ extra = "forbid"
51
+
52
+
53
+ class SearchMatchInfo(BaseModel):
54
+ """Information about a single search match."""
55
+ path: str = Field(..., description="Relative path to the file containing the match")
56
+ line_number: int = Field(..., description="Line number where the match was found (1-indexed)")
57
+ match_line: str = Field(..., description="The line content that matched the pattern")
58
+ context: str = Field(..., description="Context lines around the match")
59
+
60
+ class Config:
61
+ """Pydantic configuration."""
62
+ frozen = True
63
+ extra = "forbid"
64
+
65
+
66
+ class SearchResultContent(BaseModel):
67
+ """Content structure for search operation results."""
68
+ matches: List[SearchMatchInfo] = Field(..., description="List of search matches found")
69
+ errors: List[SearchErrorInfo] = Field(..., description="List of errors encountered during search")
70
+
71
+ class Config:
72
+ """Pydantic configuration."""
73
+ frozen = True
74
+ extra = "forbid"
15
75
 
16
76
 
17
77
  class SearchFilesToolResolver(BaseToolResolver):
78
+ """Resolver for searching files with regex patterns.
79
+
80
+ This class provides functionality to search for text patterns within files
81
+ using regular expressions, with support for file pattern filtering and
82
+ security checks to prevent access outside the project directory.
83
+ """
84
+
85
+ # Constants for search configuration
86
+ MAX_SEARCH_RESULTS = 200
87
+ CONTEXT_LINES_BEFORE = 2
88
+ CONTEXT_LINES_AFTER = 3
89
+ DEFAULT_FILE_PATTERN = "*"
90
+ DEFAULT_SOURCE_DIR = "."
91
+
18
92
  def __init__(self, agent: Optional['AgenticEdit'], tool: SearchFilesTool, args: AutoCoderArgs):
93
+ """Initialize the search files tool resolver.
94
+
95
+ Args:
96
+ agent: Optional AgenticEdit instance
97
+ tool: SearchFilesTool configuration
98
+ args: AutoCoder arguments containing source directory
99
+ """
19
100
  super().__init__(agent, tool, args)
20
101
  self.tool: SearchFilesTool = tool
21
- self.shadow_manager = self.agent.shadow_manager if self.agent else None
22
102
 
23
- def search_in_dir(self, base_dir: str, regex_pattern: str, file_pattern: str, source_dir: str, is_shadow: bool = False, compiled_regex: Optional[re.Pattern] = None) -> List[Dict[str, Any]]:
24
- """Helper function to search in a directory"""
103
+ def search_in_dir(self, base_dir: str, regex_pattern: str, file_pattern: str,
104
+ source_dir: str, is_shadow: bool = False,
105
+ compiled_regex: Optional[re.Pattern] = None) -> tuple[List[SearchMatchInfo], List[SearchErrorInfo]]:
106
+ """Search for regex patterns in files within a directory.
107
+
108
+ Args:
109
+ base_dir: Directory to search in
110
+ regex_pattern: Regular expression pattern to search for
111
+ file_pattern: Glob pattern for file filtering (e.g., '*.py')
112
+ source_dir: Source directory for calculating relative paths
113
+ is_shadow: Whether this is a shadow directory search (legacy parameter)
114
+ compiled_regex: Pre-compiled regex pattern for efficiency
115
+
116
+ Returns:
117
+ Tuple of (search match objects, error information list)
118
+ """
25
119
  search_results = []
120
+ errors = []
26
121
  search_glob_pattern = os.path.join(base_dir, "**", file_pattern)
27
-
28
- logger.info(f"Searching for regex '{regex_pattern}' in files matching '{file_pattern}' under '{base_dir}' (shadow: {is_shadow}) with ignore rules applied.")
29
-
122
+
123
+ logger.info(
124
+ f"Searching for regex '{regex_pattern}' in files matching '{file_pattern}' "
125
+ f"under '{base_dir}' (shadow: {is_shadow}) with ignore rules applied."
126
+ )
127
+
30
128
  if compiled_regex is None:
31
129
  compiled_regex = re.compile(regex_pattern)
32
-
33
- for filepath in glob.glob(search_glob_pattern, recursive=True):
34
- abs_path = os.path.abspath(filepath)
35
- if should_ignore(abs_path):
36
- continue
37
-
38
- if os.path.isfile(filepath):
39
- try:
40
- with open(filepath, 'r', encoding='utf-8', errors='replace') as f:
41
- lines = f.readlines()
42
- for i, line in enumerate(lines):
43
- if compiled_regex.search(line):
44
- context_start = max(0, i - 2)
45
- context_end = min(len(lines), i + 3)
46
- context = "".join([f"{j+1}: {lines[j]}" for j in range(context_start, context_end)])
47
-
48
- if is_shadow and self.shadow_manager:
49
- try:
50
- abs_project_path = self.shadow_manager.from_shadow_path(filepath)
51
- relative_path = os.path.relpath(abs_project_path, source_dir)
52
- except Exception:
53
- relative_path = os.path.relpath(filepath, source_dir)
54
- else:
55
- relative_path = os.path.relpath(filepath, source_dir)
56
-
57
- search_results.append({
58
- "path": relative_path,
59
- "line_number": i + 1,
60
- "match_line": line.strip(),
61
- "context": context.strip()
62
- })
63
- except Exception as e:
64
- logger.warning(f"Could not read or process file {filepath}: {e}")
65
- continue
66
-
67
- return search_results
68
-
69
- def search_files_with_shadow(self, search_path_str: str, regex_pattern: str, file_pattern: str, source_dir: str, absolute_source_dir: str, absolute_search_path: str) -> Union[ToolResult, List[Dict[str, Any]]]:
70
- """Search files using shadow manager for path translation"""
71
- # Security check
72
- if not absolute_search_path.startswith(absolute_source_dir):
73
- return ToolResult(success=False, message=f"Error: Access denied. Attempted to search outside the project directory: {search_path_str}")
74
-
75
- # Check if shadow directory exists
76
- shadow_exists = False
77
- shadow_dir_path = None
78
- if self.shadow_manager:
79
- try:
80
- shadow_dir_path = self.shadow_manager.to_shadow_path(absolute_search_path)
81
- if os.path.exists(shadow_dir_path) and os.path.isdir(shadow_dir_path):
82
- shadow_exists = True
83
- except Exception as e:
84
- logger.warning(f"Error checking shadow path for {absolute_search_path}: {e}")
85
-
86
- # Validate that at least one of the directories exists
87
- if not os.path.exists(absolute_search_path) and not shadow_exists:
88
- return ToolResult(success=False, message=f"Error: Search path not found: {search_path_str}")
89
- if os.path.exists(absolute_search_path) and not os.path.isdir(absolute_search_path):
90
- return ToolResult(success=False, message=f"Error: Search path is not a directory: {search_path_str}")
91
- if shadow_exists and not os.path.isdir(shadow_dir_path):
92
- return ToolResult(success=False, message=f"Error: Shadow search path is not a directory: {shadow_dir_path}")
93
130
 
94
131
  try:
95
- compiled_regex = re.compile(regex_pattern)
132
+ matching_files = glob.glob(search_glob_pattern, recursive=True)
133
+ except (PermissionError, OSError) as e:
134
+ error_info = SearchErrorInfo(
135
+ file_path=base_dir,
136
+ error_type=type(e).__name__,
137
+ error_message=f"Cannot access directory for glob search: {str(e)}"
138
+ )
139
+ errors.append(error_info)
140
+ logger.warning(f"Cannot access directory {base_dir}: {e}")
141
+ return search_results, errors
142
+
143
+ for filepath in matching_files:
144
+ if not self._should_process_file(filepath):
145
+ continue
146
+
147
+ file_matches, file_errors = self._search_in_file_with_errors(filepath, compiled_regex, source_dir)
148
+ search_results.extend(file_matches)
149
+ errors.extend(file_errors)
150
+
151
+ return search_results, errors
152
+
153
+ def _should_process_file(self, filepath: str) -> bool:
154
+ """Check if a file should be processed for searching.
155
+
156
+ Args:
157
+ filepath: Path to the file to check
96
158
 
97
- # Search in both directories and merge results
98
- shadow_results = []
99
- source_results = []
159
+ Returns:
160
+ True if the file should be processed, False otherwise
161
+ """
162
+ abs_path = os.path.abspath(filepath)
163
+ return os.path.isfile(filepath) and not should_ignore(abs_path)
164
+
165
+ def _search_in_file(self, filepath: str, compiled_regex: re.Pattern,
166
+ source_dir: str) -> List[SearchMatchInfo]:
167
+ """Search for regex matches within a single file.
168
+
169
+ Args:
170
+ filepath: Path to the file to search
171
+ compiled_regex: Compiled regular expression pattern
172
+ source_dir: Source directory for calculating relative paths
100
173
 
101
- if shadow_exists:
102
- shadow_results = self.search_in_dir(shadow_dir_path, regex_pattern, file_pattern, source_dir, is_shadow=True, compiled_regex=compiled_regex)
174
+ Returns:
175
+ List of SearchMatchInfo objects for this file
176
+ """
177
+ file_matches, _ = self._search_in_file_with_errors(filepath, compiled_regex, source_dir)
178
+ return file_matches
179
+
180
+ def _search_in_file_with_errors(self, filepath: str, compiled_regex: re.Pattern,
181
+ source_dir: str) -> tuple[List[SearchMatchInfo], List[SearchErrorInfo]]:
182
+ """Search for regex matches within a single file, collecting errors.
183
+
184
+ Args:
185
+ filepath: Path to the file to search
186
+ compiled_regex: Compiled regular expression pattern
187
+ source_dir: Source directory for calculating relative paths
103
188
 
104
- if os.path.exists(absolute_search_path) and os.path.isdir(absolute_search_path):
105
- source_results = self.search_in_dir(absolute_search_path, regex_pattern, file_pattern, source_dir, is_shadow=False, compiled_regex=compiled_regex)
189
+ Returns:
190
+ Tuple of (SearchMatchInfo objects, error information list)
191
+ """
192
+ file_matches = []
193
+ errors = []
194
+
195
+ try:
196
+ with open(filepath, 'r', encoding='utf-8', errors='replace') as f:
197
+ lines = f.readlines()
198
+
199
+ for line_index, line in enumerate(lines):
200
+ if compiled_regex.search(line):
201
+ match_info = self._create_match_info(
202
+ filepath, line_index, line, lines, source_dir
203
+ )
204
+ file_matches.append(match_info)
205
+
206
+ except (PermissionError, OSError, UnicodeDecodeError) as e:
207
+ error_info = SearchErrorInfo(
208
+ file_path=filepath,
209
+ error_type=type(e).__name__,
210
+ error_message=f"Cannot read file: {str(e)}"
211
+ )
212
+ errors.append(error_info)
213
+ logger.warning(f"Could not read or process file {filepath}: {e}")
214
+ except Exception as e:
215
+ error_info = SearchErrorInfo(
216
+ file_path=filepath,
217
+ error_type=type(e).__name__,
218
+ error_message=f"Unexpected error: {str(e)}"
219
+ )
220
+ errors.append(error_info)
221
+ logger.warning(f"Unexpected error processing file {filepath}: {e}")
106
222
 
107
- # Merge results, prioritizing shadow results
108
- # Create a dictionary for quick lookup
109
- results_dict = {}
110
- for result in source_results:
111
- key = (result["path"], result["line_number"])
112
- results_dict[key] = result
223
+ return file_matches, errors
224
+
225
+ def _create_match_info(self, filepath: str, line_index: int, line: str,
226
+ all_lines: List[str], source_dir: str) -> SearchMatchInfo:
227
+ """Create a match information object.
228
+
229
+ Args:
230
+ filepath: Path to the file containing the match
231
+ line_index: Zero-based index of the matching line
232
+ line: The matching line content
233
+ all_lines: All lines in the file for context
234
+ source_dir: Source directory for calculating relative paths
113
235
 
114
- # Override with shadow results
115
- for result in shadow_results:
116
- key = (result["path"], result["line_number"])
117
- results_dict[key] = result
236
+ Returns:
237
+ SearchMatchInfo object containing match information
238
+ """
239
+ context_start = max(0, line_index - self.CONTEXT_LINES_BEFORE)
240
+ context_end = min(len(all_lines), line_index + self.CONTEXT_LINES_AFTER)
241
+
242
+ context_lines = [
243
+ f"{j + 1}: {all_lines[j]}"
244
+ for j in range(context_start, context_end)
245
+ ]
246
+ context = "".join(context_lines)
247
+
248
+ relative_path = os.path.relpath(filepath, source_dir)
249
+
250
+ return SearchMatchInfo(
251
+ path=relative_path,
252
+ line_number=line_index + 1,
253
+ match_line=line.strip(),
254
+ context=context.strip()
255
+ )
256
+
257
+ def search_files_normal(self, search_path_str: str, regex_pattern: str,
258
+ file_pattern: str, source_dir: str,
259
+ absolute_source_dir: str, absolute_search_path: str) -> Union[ToolResult, tuple[List[SearchMatchInfo], List[SearchErrorInfo]]]:
260
+ """Search files directly in the specified directory or file.
261
+
262
+ Args:
263
+ search_path_str: Original search path string (for error messages)
264
+ regex_pattern: Regular expression pattern to search for
265
+ file_pattern: Glob pattern for file filtering
266
+ source_dir: Source directory path
267
+ absolute_source_dir: Absolute path to source directory
268
+ absolute_search_path: Absolute path to search directory or file
118
269
 
119
- # Convert back to list
120
- merged_results = list(results_dict.values())
270
+ Returns:
271
+ ToolResult on error, or tuple of (SearchMatchInfo objects, error information) on success
272
+ """
273
+ # Perform security and validation checks
274
+ validation_result = self._validate_search_path(
275
+ search_path_str, absolute_source_dir, absolute_search_path
276
+ )
277
+ if validation_result:
278
+ return validation_result
121
279
 
122
- return merged_results
280
+ try:
281
+ compiled_regex = re.compile(regex_pattern)
282
+
283
+ # Check if the path is a file or directory
284
+ if os.path.isfile(absolute_search_path):
285
+ # Search in single file
286
+ logger.info(f"Searching for regex '{regex_pattern}' in file '{absolute_search_path}'")
287
+ if not self._should_process_file(absolute_search_path):
288
+ return [], []
289
+ search_results, errors = self._search_in_file_with_errors(absolute_search_path, compiled_regex, source_dir)
290
+ else:
291
+ # Search in directory
292
+ search_results, errors = self.search_in_dir(
293
+ absolute_search_path, regex_pattern, file_pattern,
294
+ source_dir, is_shadow=False, compiled_regex=compiled_regex
295
+ )
296
+
297
+ return search_results, errors
123
298
 
124
299
  except re.error as e:
300
+ error_msg = f"Invalid regex pattern: {e}"
125
301
  logger.error(f"Invalid regex pattern '{regex_pattern}': {e}")
126
- return ToolResult(success=False, message=f"Invalid regex pattern: {e}")
302
+ return ToolResult(success=False, message=error_msg)
303
+
127
304
  except Exception as e:
305
+ error_msg = f"An unexpected error occurred during search: {str(e)}"
128
306
  logger.error(f"Error during file search: {str(e)}")
129
- return ToolResult(success=False, message=f"An unexpected error occurred during search: {str(e)}")
307
+ return ToolResult(success=False, message=error_msg)
130
308
 
131
- def search_files_normal(self, search_path_str: str, regex_pattern: str, file_pattern: str, source_dir: str, absolute_source_dir: str, absolute_search_path: str) -> Union[ToolResult, List[Dict[str, Any]]]:
132
- """Search files directly without using shadow manager"""
133
- # Security check
309
+ def _validate_search_path(self, search_path_str: str, absolute_source_dir: str,
310
+ absolute_search_path: str) -> Optional[ToolResult]:
311
+ """Validate the search path for security and existence.
312
+
313
+ Args:
314
+ search_path_str: Original search path string (for error messages)
315
+ absolute_source_dir: Absolute path to source directory
316
+ absolute_search_path: Absolute path to search directory or file
317
+
318
+ Returns:
319
+ ToolResult with error if validation fails, None if validation passes
320
+ """
321
+ # Security check: prevent access outside project directory
134
322
  if not absolute_search_path.startswith(absolute_source_dir):
135
- return ToolResult(success=False, message=f"Error: Access denied. Attempted to search outside the project directory: {search_path_str}")
323
+ return ToolResult(
324
+ success=False,
325
+ message=f"Error: Access denied. Attempted to search outside the project directory: {search_path_str}"
326
+ )
136
327
 
137
- # Validate that the directory exists
328
+ # Check if path exists
138
329
  if not os.path.exists(absolute_search_path):
139
- return ToolResult(success=False, message=f"Error: Search path not found: {search_path_str}")
140
- if not os.path.isdir(absolute_search_path):
141
- return ToolResult(success=False, message=f"Error: Search path is not a directory: {search_path_str}")
142
-
143
- try:
144
- compiled_regex = re.compile(regex_pattern)
145
-
146
- # Search in the directory
147
- search_results = self.search_in_dir(absolute_search_path, regex_pattern, file_pattern, source_dir, is_shadow=False, compiled_regex=compiled_regex)
330
+ return ToolResult(
331
+ success=False,
332
+ message=f"Error: Search path not found: {search_path_str}"
333
+ )
148
334
 
149
- return search_results
150
-
151
- except re.error as e:
152
- logger.error(f"Invalid regex pattern '{regex_pattern}': {e}")
153
- return ToolResult(success=False, message=f"Invalid regex pattern: {e}")
154
- except Exception as e:
155
- logger.error(f"Error during file search: {str(e)}")
156
- return ToolResult(success=False, message=f"An unexpected error occurred during search: {str(e)}")
335
+ return None
157
336
 
158
337
  def resolve(self) -> ToolResult:
159
- """Resolve the search files tool by calling the appropriate implementation"""
338
+ """Resolve the search files tool by executing the search operation.
339
+
340
+ Returns:
341
+ ToolResult containing search results or error information
342
+ """
343
+ search_params = self._extract_search_parameters()
344
+
345
+ result = self.search_files_normal(
346
+ search_params.search_path_str,
347
+ search_params.regex_pattern,
348
+ search_params.file_pattern,
349
+ search_params.source_dir,
350
+ search_params.absolute_source_dir,
351
+ search_params.absolute_search_path
352
+ )
353
+
354
+ return self._format_search_result(result)
355
+
356
+ def _extract_search_parameters(self) -> SearchParameters:
357
+ """Extract and prepare search parameters from tool configuration.
358
+
359
+ Returns:
360
+ SearchParameters instance with strongly typed parameters
361
+ """
160
362
  search_path_str = self.tool.path
161
363
  regex_pattern = self.tool.regex
162
- file_pattern = self.tool.file_pattern or "*"
163
- source_dir = self.args.source_dir or "."
364
+ file_pattern = self.tool.file_pattern or self.DEFAULT_FILE_PATTERN
365
+ source_dir = self.args.source_dir or self.DEFAULT_SOURCE_DIR
164
366
  absolute_source_dir = os.path.abspath(source_dir)
165
- absolute_search_path = os.path.abspath(os.path.join(source_dir, search_path_str))
166
-
167
- # Choose the appropriate implementation based on whether shadow_manager is available
168
- if self.shadow_manager:
169
- result = self.search_files_with_shadow(search_path_str, regex_pattern, file_pattern, source_dir, absolute_source_dir, absolute_search_path)
170
- else:
171
- result = self.search_files_normal(search_path_str, regex_pattern, file_pattern, source_dir, absolute_source_dir, absolute_search_path)
172
-
173
- # Handle the case where the implementation returns a list instead of a ToolResult
174
- if isinstance(result, list):
175
- total_results = len(result)
176
- # Limit results to 200 if needed
177
- if total_results > 200:
178
- truncated_results = result[:200]
179
- message = f"Search completed. Found {total_results} matches, showing only the first 200."
180
- logger.info(message)
181
- return ToolResult(success=True, message=message, content=truncated_results)
367
+ absolute_search_path = os.path.abspath(
368
+ os.path.join(source_dir, search_path_str)
369
+ )
370
+
371
+ return SearchParameters(
372
+ search_path_str=search_path_str,
373
+ regex_pattern=regex_pattern,
374
+ file_pattern=file_pattern,
375
+ source_dir=source_dir,
376
+ absolute_source_dir=absolute_source_dir,
377
+ absolute_search_path=absolute_search_path
378
+ )
379
+
380
+ def _format_search_result(self, result: Union[ToolResult, tuple[List[SearchMatchInfo], List[SearchErrorInfo]]]) -> ToolResult:
381
+ """Format the search result into a standardized ToolResult.
382
+
383
+ Args:
384
+ result: Raw search result (either ToolResult or tuple of SearchMatchInfo objects and errors)
385
+
386
+ Returns:
387
+ Formatted ToolResult
388
+ """
389
+ # If result is already a ToolResult (error case), return it directly
390
+ if isinstance(result, ToolResult):
391
+ return result
392
+
393
+ # Handle successful search results (tuple of matches and errors)
394
+ search_results, errors = result
395
+ total_results = len(search_results)
396
+ total_errors = len(errors)
397
+
398
+ # Prepare error summary for message
399
+ error_summary = ""
400
+ if total_errors > 0:
401
+ error_types = {}
402
+ for error in errors:
403
+ error_types[error.error_type] = error_types.get(error.error_type, 0) + 1
404
+ error_summary = f" Encountered {total_errors} errors: " + ", ".join([f"{count} {error_type}" for error_type, count in error_types.items()])
405
+
406
+ # Prepare content with both results and errors using Pydantic model
407
+ truncated_matches = search_results[:self.MAX_SEARCH_RESULTS] if total_results > self.MAX_SEARCH_RESULTS else search_results
408
+ content_model = SearchResultContent(
409
+ matches=truncated_matches,
410
+ errors=errors
411
+ )
412
+ content = content_model.model_dump()
413
+
414
+ # Convert content to JSON string to count tokens
415
+ content_str = json.dumps(content, ensure_ascii=False, indent=2)
416
+ token_count = count_string_tokens(content_str)
417
+
418
+ # Check if content exceeds 5k tokens
419
+ MAX_TOKENS = 5000
420
+ if token_count > MAX_TOKENS:
421
+ # Truncate to first 1000 characters
422
+ truncated_content_str = content_str[:1000]
423
+
424
+ # Add hint about truncation
425
+ hint_message = "Search results truncated due to exceeding 5k tokens. Try narrowing your search by using more specific regex patterns or file patterns to reduce output."
426
+ truncated_content_str = add_hint_to_text(truncated_content_str, hint_message)
427
+
428
+ # Log the truncation
429
+ logger.warning(f"Search results truncated from {token_count} tokens to first 1000 characters")
430
+
431
+ # Create truncation message
432
+ if total_results > self.MAX_SEARCH_RESULTS:
433
+ message = (
434
+ f"Search completed. Found {total_results} matches (showing first {self.MAX_SEARCH_RESULTS}), "
435
+ f"but results were truncated due to size (original: {token_count} tokens).{error_summary}"
436
+ )
182
437
  else:
183
- message = f"Search completed. Found {total_results} matches."
184
- logger.info(message)
185
- return ToolResult(success=True, message=message, content=result)
438
+ message = (
439
+ f"Search completed. Found {total_results} matches, "
440
+ f"but results were truncated due to size (original: {token_count} tokens).{error_summary}"
441
+ )
442
+
443
+ return ToolResult(success=True, message=message, content=truncated_content_str)
444
+
445
+ # Normal case - content within token limit
446
+ if total_results > self.MAX_SEARCH_RESULTS:
447
+ message = (
448
+ f"Search completed. Found {total_results} matches, "
449
+ f"showing only the first {self.MAX_SEARCH_RESULTS}.{error_summary}"
450
+ )
451
+ logger.info(message)
452
+ return ToolResult(success=True, message=message, content=content)
186
453
  else:
187
- return result
454
+ message = f"Search completed. Found {total_results} matches.{error_summary}"
455
+ logger.info(message)
456
+ return ToolResult(success=True, message=message, content=content)