auto-coder 1.0.0__py3-none-any.whl → 2.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.

Potentially problematic release.


This version of auto-coder might be problematic. Click here for more details.

Files changed (574) hide show
  1. auto_coder-2.0.1.dist-info/LICENSE +158 -0
  2. auto_coder-2.0.1.dist-info/METADATA +558 -0
  3. auto_coder-2.0.1.dist-info/RECORD +795 -0
  4. {auto_coder-1.0.0.dist-info → auto_coder-2.0.1.dist-info}/WHEEL +1 -1
  5. {auto_coder-1.0.0.dist-info → auto_coder-2.0.1.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 +24 -3
  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 +77 -73
  19. autocoder/auto_coder.py +31 -40
  20. autocoder/auto_coder_rag.py +11 -1084
  21. autocoder/auto_coder_runner.py +962 -2345
  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 +988 -398
  28. autocoder/chat_auto_coder_lang.py +23 -732
  29. autocoder/commands/auto_command.py +25 -8
  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 +409 -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/git_utils.py +44 -8
  119. autocoder/common/global_cancel.py +15 -6
  120. autocoder/common/ignorefiles/test_ignore_file_utils.py +1 -1
  121. autocoder/common/international/__init__.py +31 -0
  122. autocoder/common/international/demo_international.py +92 -0
  123. autocoder/common/international/message_manager.py +157 -0
  124. autocoder/common/international/messages/__init__.py +56 -0
  125. autocoder/common/international/messages/async_command_messages.py +507 -0
  126. autocoder/common/international/messages/auto_coder_messages.py +2208 -0
  127. autocoder/common/international/messages/chat_auto_coder_messages.py +1547 -0
  128. autocoder/common/international/messages/command_help_messages.py +986 -0
  129. autocoder/common/international/messages/conversation_command_messages.py +191 -0
  130. autocoder/common/international/messages/git_helper_plugin_messages.py +159 -0
  131. autocoder/common/international/messages/queue_command_messages.py +751 -0
  132. autocoder/common/international/messages/rules_command_messages.py +77 -0
  133. autocoder/common/international/messages/sdk_messages.py +1707 -0
  134. autocoder/common/international/messages/token_helper_plugin_messages.py +361 -0
  135. autocoder/common/international/messages/tool_display_messages.py +1212 -0
  136. autocoder/common/international/messages/workflow_exception_messages.py +473 -0
  137. autocoder/common/international/test_international.py +612 -0
  138. autocoder/common/linter_core/__init__.py +28 -0
  139. autocoder/common/linter_core/base_linter.py +61 -0
  140. autocoder/common/linter_core/config_loader.py +271 -0
  141. autocoder/common/linter_core/formatters/__init__.py +0 -0
  142. autocoder/common/linter_core/formatters/base_formatter.py +38 -0
  143. autocoder/common/linter_core/formatters/raw_formatter.py +17 -0
  144. autocoder/common/linter_core/linter.py +166 -0
  145. autocoder/common/linter_core/linter_factory.py +216 -0
  146. autocoder/common/linter_core/linter_manager.py +333 -0
  147. autocoder/common/linter_core/linters/__init__.py +9 -0
  148. autocoder/common/linter_core/linters/java_linter.py +342 -0
  149. autocoder/common/linter_core/linters/python_linter.py +115 -0
  150. autocoder/common/linter_core/linters/typescript_linter.py +119 -0
  151. autocoder/common/linter_core/models/__init__.py +7 -0
  152. autocoder/common/linter_core/models/lint_result.py +91 -0
  153. autocoder/common/linter_core/models.py +33 -0
  154. autocoder/common/linter_core/tests/__init__.py +3 -0
  155. autocoder/common/linter_core/tests/test_config_loader.py +323 -0
  156. autocoder/common/linter_core/tests/test_config_loading.py +308 -0
  157. autocoder/common/linter_core/tests/test_factory_manager.py +234 -0
  158. autocoder/common/linter_core/tests/test_formatters.py +147 -0
  159. autocoder/common/linter_core/tests/test_integration.py +317 -0
  160. autocoder/common/linter_core/tests/test_java_linter.py +496 -0
  161. autocoder/common/linter_core/tests/test_linters.py +265 -0
  162. autocoder/common/linter_core/tests/test_models.py +81 -0
  163. autocoder/common/linter_core/tests/verify_config_loading.py +296 -0
  164. autocoder/common/linter_core/tests/verify_fixes.py +183 -0
  165. autocoder/common/llm_friendly_package/__init__.py +31 -0
  166. autocoder/common/llm_friendly_package/base_manager.py +102 -0
  167. autocoder/common/llm_friendly_package/docs_manager.py +121 -0
  168. autocoder/common/llm_friendly_package/library_manager.py +171 -0
  169. autocoder/common/{llm_friendly_package.py → llm_friendly_package/main_manager.py} +204 -231
  170. autocoder/common/llm_friendly_package/models.py +40 -0
  171. autocoder/common/llm_friendly_package/test_llm_friendly_package.py +536 -0
  172. autocoder/common/llms/__init__.py +15 -0
  173. autocoder/common/llms/demo_error_handling.py +85 -0
  174. autocoder/common/llms/factory.py +142 -0
  175. autocoder/common/llms/manager.py +264 -0
  176. autocoder/common/llms/pricing.py +121 -0
  177. autocoder/common/llms/registry.py +316 -0
  178. autocoder/common/llms/schema.py +77 -0
  179. autocoder/common/llms/simple_demo.py +45 -0
  180. autocoder/common/llms/test_quick_model.py +116 -0
  181. autocoder/common/llms/test_remove_functionality.py +182 -0
  182. autocoder/common/llms/tests/__init__.py +1 -0
  183. autocoder/common/llms/tests/test_manager.py +330 -0
  184. autocoder/common/llms/tests/test_registry.py +364 -0
  185. autocoder/common/mcp_tools/__init__.py +62 -0
  186. autocoder/common/{mcp_tools.py → mcp_tools/executor.py} +49 -40
  187. autocoder/common/{mcp_hub.py → mcp_tools/hub.py} +42 -68
  188. autocoder/common/{mcp_server_install.py → mcp_tools/installer.py} +16 -28
  189. autocoder/common/{mcp_server.py → mcp_tools/server.py} +176 -48
  190. autocoder/common/mcp_tools/test_keyboard_interrupt.py +93 -0
  191. autocoder/common/mcp_tools/test_mcp_tools.py +391 -0
  192. autocoder/common/{mcp_server_types.py → mcp_tools/types.py} +121 -48
  193. autocoder/common/mcp_tools/verify_functionality.py +202 -0
  194. autocoder/common/model_speed_tester.py +32 -26
  195. autocoder/common/priority_directory_finder/__init__.py +142 -0
  196. autocoder/common/priority_directory_finder/examples.py +230 -0
  197. autocoder/common/priority_directory_finder/finder.py +283 -0
  198. autocoder/common/priority_directory_finder/models.py +236 -0
  199. autocoder/common/priority_directory_finder/test_priority_directory_finder.py +431 -0
  200. autocoder/common/project_scanner/__init__.py +18 -0
  201. autocoder/common/project_scanner/compat.py +77 -0
  202. autocoder/common/project_scanner/scanner.py +436 -0
  203. autocoder/common/project_tracker/__init__.py +27 -0
  204. autocoder/common/project_tracker/api.py +228 -0
  205. autocoder/common/project_tracker/demo.py +272 -0
  206. autocoder/common/project_tracker/tracker.py +487 -0
  207. autocoder/common/project_tracker/types.py +53 -0
  208. autocoder/common/pruner/__init__.py +67 -0
  209. autocoder/common/pruner/agentic_conversation_pruner.py +651 -102
  210. autocoder/common/pruner/conversation_message_ids_api.py +386 -0
  211. autocoder/common/pruner/conversation_message_ids_manager.py +347 -0
  212. autocoder/common/pruner/conversation_message_ids_pruner.py +473 -0
  213. autocoder/common/pruner/conversation_normalizer.py +347 -0
  214. autocoder/common/pruner/conversation_pruner.py +26 -6
  215. autocoder/common/pruner/test_agentic_conversation_pruner.py +554 -112
  216. autocoder/common/pruner/test_conversation_normalizer.py +502 -0
  217. autocoder/common/pruner/test_tool_content_detector.py +324 -0
  218. autocoder/common/pruner/tool_content_detector.py +227 -0
  219. autocoder/common/pruner/tools/__init__.py +18 -0
  220. autocoder/common/pruner/tools/query_message_ids.py +264 -0
  221. autocoder/common/pruner/tools/test_agentic_pruning_logic.py +432 -0
  222. autocoder/common/pruner/tools/test_message_ids_pruning_only.py +192 -0
  223. autocoder/common/pull_requests/__init__.py +9 -1
  224. autocoder/common/pull_requests/utils.py +122 -1
  225. autocoder/common/rag_manager/rag_manager.py +36 -40
  226. autocoder/common/rulefiles/__init__.py +53 -1
  227. autocoder/common/rulefiles/api.py +250 -0
  228. autocoder/common/rulefiles/core/__init__.py +14 -0
  229. autocoder/common/rulefiles/core/manager.py +241 -0
  230. autocoder/common/rulefiles/core/selector.py +805 -0
  231. autocoder/common/rulefiles/models/__init__.py +20 -0
  232. autocoder/common/rulefiles/models/index.py +16 -0
  233. autocoder/common/rulefiles/models/init_rule.py +18 -0
  234. autocoder/common/rulefiles/models/rule_file.py +18 -0
  235. autocoder/common/rulefiles/models/rule_relevance.py +14 -0
  236. autocoder/common/rulefiles/models/summary.py +16 -0
  237. autocoder/common/rulefiles/test_rulefiles.py +776 -0
  238. autocoder/common/rulefiles/utils/__init__.py +34 -0
  239. autocoder/common/rulefiles/utils/monitor.py +86 -0
  240. autocoder/common/rulefiles/utils/parser.py +230 -0
  241. autocoder/common/save_formatted_log.py +67 -10
  242. autocoder/common/search_replace.py +8 -1
  243. autocoder/common/search_replace_patch/__init__.py +24 -0
  244. autocoder/common/search_replace_patch/base.py +115 -0
  245. autocoder/common/search_replace_patch/manager.py +248 -0
  246. autocoder/common/search_replace_patch/patch_replacer.py +304 -0
  247. autocoder/common/search_replace_patch/similarity_replacer.py +306 -0
  248. autocoder/common/search_replace_patch/string_replacer.py +181 -0
  249. autocoder/common/search_replace_patch/tests/__init__.py +3 -0
  250. autocoder/common/search_replace_patch/tests/run_tests.py +126 -0
  251. autocoder/common/search_replace_patch/tests/test_base.py +188 -0
  252. autocoder/common/search_replace_patch/tests/test_empty_line_insert.py +233 -0
  253. autocoder/common/search_replace_patch/tests/test_integration.py +389 -0
  254. autocoder/common/search_replace_patch/tests/test_manager.py +351 -0
  255. autocoder/common/search_replace_patch/tests/test_patch_replacer.py +316 -0
  256. autocoder/common/search_replace_patch/tests/test_regex_replacer.py +306 -0
  257. autocoder/common/search_replace_patch/tests/test_similarity_replacer.py +384 -0
  258. autocoder/common/shell_commands/__init__.py +197 -0
  259. autocoder/common/shell_commands/background_process_notifier.py +346 -0
  260. autocoder/common/shell_commands/command_executor.py +1127 -0
  261. autocoder/common/shell_commands/error_recovery.py +541 -0
  262. autocoder/common/shell_commands/exceptions.py +120 -0
  263. autocoder/common/shell_commands/interactive_executor.py +476 -0
  264. autocoder/common/shell_commands/interactive_pexpect_process.py +623 -0
  265. autocoder/common/shell_commands/interactive_process.py +744 -0
  266. autocoder/common/shell_commands/interactive_session_manager.py +1014 -0
  267. autocoder/common/shell_commands/monitoring.py +529 -0
  268. autocoder/common/shell_commands/process_cleanup.py +386 -0
  269. autocoder/common/shell_commands/process_manager.py +606 -0
  270. autocoder/common/shell_commands/test_interactive_pexpect_process.py +281 -0
  271. autocoder/common/shell_commands/tests/__init__.py +6 -0
  272. autocoder/common/shell_commands/tests/conftest.py +118 -0
  273. autocoder/common/shell_commands/tests/test_background_process_notifier.py +703 -0
  274. autocoder/common/shell_commands/tests/test_command_executor.py +448 -0
  275. autocoder/common/shell_commands/tests/test_error_recovery.py +305 -0
  276. autocoder/common/shell_commands/tests/test_exceptions.py +299 -0
  277. autocoder/common/shell_commands/tests/test_execute_batch.py +588 -0
  278. autocoder/common/shell_commands/tests/test_indented_batch_commands.py +244 -0
  279. autocoder/common/shell_commands/tests/test_integration.py +664 -0
  280. autocoder/common/shell_commands/tests/test_monitoring.py +546 -0
  281. autocoder/common/shell_commands/tests/test_performance.py +632 -0
  282. autocoder/common/shell_commands/tests/test_process_cleanup.py +397 -0
  283. autocoder/common/shell_commands/tests/test_process_manager.py +606 -0
  284. autocoder/common/shell_commands/tests/test_timeout_config.py +343 -0
  285. autocoder/common/shell_commands/tests/test_timeout_manager.py +520 -0
  286. autocoder/common/shell_commands/timeout_config.py +315 -0
  287. autocoder/common/shell_commands/timeout_manager.py +352 -0
  288. autocoder/common/terminal_paste/__init__.py +14 -0
  289. autocoder/common/terminal_paste/demo.py +145 -0
  290. autocoder/common/terminal_paste/demo_paste_functionality.py +95 -0
  291. autocoder/common/terminal_paste/paste_handler.py +200 -0
  292. autocoder/common/terminal_paste/paste_manager.py +118 -0
  293. autocoder/common/terminal_paste/tests/__init__.py +1 -0
  294. autocoder/common/terminal_paste/tests/test_paste_handler.py +182 -0
  295. autocoder/common/terminal_paste/tests/test_paste_manager.py +126 -0
  296. autocoder/common/terminal_paste/utils.py +163 -0
  297. autocoder/common/test_autocoder_args.py +232 -0
  298. autocoder/common/test_env_manager.py +173 -0
  299. autocoder/common/test_env_manager_integration.py +159 -0
  300. autocoder/common/text_similarity/__init__.py +9 -0
  301. autocoder/common/text_similarity/demo.py +216 -0
  302. autocoder/common/text_similarity/examples.py +266 -0
  303. autocoder/common/text_similarity/test_text_similarity.py +306 -0
  304. autocoder/common/text_similarity/text_similarity.py +194 -0
  305. autocoder/common/text_similarity/utils.py +125 -0
  306. autocoder/common/todos/__init__.py +61 -0
  307. autocoder/common/todos/cache/__init__.py +16 -0
  308. autocoder/common/todos/cache/base_cache.py +89 -0
  309. autocoder/common/todos/cache/cache_manager.py +228 -0
  310. autocoder/common/todos/cache/memory_cache.py +225 -0
  311. autocoder/common/todos/config.py +155 -0
  312. autocoder/common/todos/exceptions.py +35 -0
  313. autocoder/common/todos/get_todo_manager.py +161 -0
  314. autocoder/common/todos/manager.py +537 -0
  315. autocoder/common/todos/models.py +239 -0
  316. autocoder/common/todos/storage/__init__.py +14 -0
  317. autocoder/common/todos/storage/base_storage.py +76 -0
  318. autocoder/common/todos/storage/file_storage.py +278 -0
  319. autocoder/common/tokens/counter.py +24 -2
  320. autocoder/common/tools_manager/__init__.py +17 -0
  321. autocoder/common/tools_manager/examples.py +162 -0
  322. autocoder/common/tools_manager/manager.py +385 -0
  323. autocoder/common/tools_manager/models.py +39 -0
  324. autocoder/common/tools_manager/test_tools_manager.py +303 -0
  325. autocoder/common/tools_manager/utils.py +191 -0
  326. autocoder/common/v2/agent/agentic_callbacks.py +270 -0
  327. autocoder/common/v2/agent/agentic_edit.py +2699 -1856
  328. autocoder/common/v2/agent/agentic_edit_change_manager.py +474 -0
  329. autocoder/common/v2/agent/agentic_edit_tools/__init__.py +35 -1
  330. autocoder/common/v2/agent/agentic_edit_tools/ac_mod_list_tool_resolver.py +279 -0
  331. autocoder/common/v2/agent/agentic_edit_tools/ac_mod_write_tool_resolver.py +10 -1
  332. autocoder/common/v2/agent/agentic_edit_tools/background_task_tool_resolver.py +1167 -0
  333. autocoder/common/v2/agent/agentic_edit_tools/base_tool_resolver.py +2 -2
  334. autocoder/common/v2/agent/agentic_edit_tools/conversation_message_ids_read_tool_resolver.py +214 -0
  335. autocoder/common/v2/agent/agentic_edit_tools/conversation_message_ids_write_tool_resolver.py +299 -0
  336. autocoder/common/v2/agent/agentic_edit_tools/count_tokens_tool_resolver.py +290 -0
  337. autocoder/common/v2/agent/agentic_edit_tools/execute_command_tool_resolver.py +564 -29
  338. autocoder/common/v2/agent/agentic_edit_tools/execute_workflow_tool_resolver.py +485 -0
  339. autocoder/common/v2/agent/agentic_edit_tools/extract_to_text_tool_resolver.py +225 -0
  340. autocoder/common/v2/agent/agentic_edit_tools/lint_report.py +79 -0
  341. autocoder/common/v2/agent/agentic_edit_tools/linter_config_models.py +343 -0
  342. autocoder/common/v2/agent/agentic_edit_tools/linter_enabled_tool_resolver.py +189 -0
  343. autocoder/common/v2/agent/agentic_edit_tools/list_files_tool_resolver.py +169 -101
  344. autocoder/common/v2/agent/agentic_edit_tools/load_extra_document_tool_resolver.py +356 -0
  345. autocoder/common/v2/agent/agentic_edit_tools/read_file_tool_resolver.py +243 -50
  346. autocoder/common/v2/agent/agentic_edit_tools/replace_in_file_tool_resolver.py +667 -147
  347. autocoder/common/v2/agent/agentic_edit_tools/run_named_subagents_tool_resolver.py +691 -0
  348. autocoder/common/v2/agent/agentic_edit_tools/search_files_tool_resolver.py +410 -86
  349. autocoder/common/v2/agent/agentic_edit_tools/session_interactive_tool_resolver.py +115 -0
  350. autocoder/common/v2/agent/agentic_edit_tools/session_start_tool_resolver.py +190 -0
  351. autocoder/common/v2/agent/agentic_edit_tools/session_stop_tool_resolver.py +76 -0
  352. autocoder/common/v2/agent/agentic_edit_tools/test_write_to_file_tool_resolver.py +207 -192
  353. autocoder/common/v2/agent/agentic_edit_tools/todo_read_tool_resolver.py +80 -63
  354. autocoder/common/v2/agent/agentic_edit_tools/todo_write_tool_resolver.py +237 -233
  355. autocoder/common/v2/agent/agentic_edit_tools/use_mcp_tool_resolver.py +2 -2
  356. autocoder/common/v2/agent/agentic_edit_tools/web_crawl_tool_resolver.py +557 -0
  357. autocoder/common/v2/agent/agentic_edit_tools/web_search_tool_resolver.py +600 -0
  358. autocoder/common/v2/agent/agentic_edit_tools/write_to_file_tool_resolver.py +56 -121
  359. autocoder/common/v2/agent/agentic_edit_types.py +343 -9
  360. autocoder/common/v2/agent/runner/__init__.py +3 -3
  361. autocoder/common/v2/agent/runner/base_runner.py +12 -26
  362. autocoder/common/v2/agent/runner/{event_runner.py → file_based_event_runner.py} +3 -2
  363. autocoder/common/v2/agent/runner/sdk_runner.py +150 -8
  364. autocoder/common/v2/agent/runner/terminal_runner.py +170 -57
  365. autocoder/common/v2/agent/runner/tool_display.py +557 -159
  366. autocoder/common/v2/agent/test_agentic_callbacks.py +265 -0
  367. autocoder/common/v2/agent/test_agentic_edit.py +194 -0
  368. autocoder/common/v2/agent/tool_caller/__init__.py +24 -0
  369. autocoder/common/v2/agent/tool_caller/default_tool_resolver_map.py +135 -0
  370. autocoder/common/v2/agent/tool_caller/integration_test.py +172 -0
  371. autocoder/common/v2/agent/tool_caller/plugins/__init__.py +14 -0
  372. autocoder/common/v2/agent/tool_caller/plugins/base_plugin.py +126 -0
  373. autocoder/common/v2/agent/tool_caller/plugins/examples/__init__.py +13 -0
  374. autocoder/common/v2/agent/tool_caller/plugins/examples/logging_plugin.py +164 -0
  375. autocoder/common/v2/agent/tool_caller/plugins/examples/security_filter_plugin.py +198 -0
  376. autocoder/common/v2/agent/tool_caller/plugins/plugin_interface.py +141 -0
  377. autocoder/common/v2/agent/tool_caller/test_tool_caller.py +278 -0
  378. autocoder/common/v2/agent/tool_caller/tool_call_plugin_manager.py +331 -0
  379. autocoder/common/v2/agent/tool_caller/tool_caller.py +337 -0
  380. autocoder/common/v2/agent/tool_caller/usage_example.py +193 -0
  381. autocoder/common/v2/code_agentic_editblock_manager.py +4 -4
  382. autocoder/common/v2/code_auto_generate.py +136 -78
  383. autocoder/common/v2/code_auto_generate_diff.py +135 -79
  384. autocoder/common/v2/code_auto_generate_editblock.py +174 -99
  385. autocoder/common/v2/code_auto_generate_strict_diff.py +151 -71
  386. autocoder/common/v2/code_auto_merge.py +1 -1
  387. autocoder/common/v2/code_auto_merge_editblock.py +13 -1
  388. autocoder/common/v2/code_diff_manager.py +3 -3
  389. autocoder/common/v2/code_editblock_manager.py +4 -14
  390. autocoder/common/v2/code_manager.py +1 -1
  391. autocoder/common/v2/code_strict_diff_manager.py +2 -2
  392. autocoder/common/wrap_llm_hint/__init__.py +10 -0
  393. autocoder/common/wrap_llm_hint/test_wrap_llm_hint.py +1067 -0
  394. autocoder/common/wrap_llm_hint/utils.py +432 -0
  395. autocoder/common/wrap_llm_hint/wrap_llm_hint.py +323 -0
  396. autocoder/completer/__init__.py +8 -0
  397. autocoder/completer/command_completer_v2.py +1094 -0
  398. autocoder/default_project/__init__.py +501 -0
  399. autocoder/dispacher/__init__.py +4 -12
  400. autocoder/dispacher/actions/action.py +400 -129
  401. autocoder/dispacher/actions/plugins/action_regex_project.py +2 -2
  402. autocoder/index/entry.py +117 -125
  403. autocoder/{agent → index/filter}/agentic_filter.py +322 -333
  404. autocoder/index/filter/normal_filter.py +5 -11
  405. autocoder/index/filter/quick_filter.py +1 -1
  406. autocoder/index/index.py +36 -9
  407. autocoder/index/tests/__init__.py +1 -0
  408. autocoder/index/tests/run_tests.py +195 -0
  409. autocoder/index/tests/test_entry.py +303 -0
  410. autocoder/index/tests/test_index_manager.py +314 -0
  411. autocoder/index/tests/test_module_integration.py +300 -0
  412. autocoder/index/tests/test_symbols_utils.py +183 -0
  413. autocoder/inner/__init__.py +4 -0
  414. autocoder/inner/agentic.py +923 -0
  415. autocoder/inner/async_command_handler.py +992 -0
  416. autocoder/inner/conversation_command_handlers.py +623 -0
  417. autocoder/inner/merge_command_handler.py +213 -0
  418. autocoder/inner/queue_command_handler.py +684 -0
  419. autocoder/models.py +95 -266
  420. autocoder/plugins/git_helper_plugin.py +31 -29
  421. autocoder/plugins/token_helper_plugin.py +65 -46
  422. autocoder/pyproject/__init__.py +32 -29
  423. autocoder/rag/agentic_rag.py +215 -75
  424. autocoder/rag/cache/simple_cache.py +1 -2
  425. autocoder/rag/loaders/image_loader.py +1 -1
  426. autocoder/rag/long_context_rag.py +42 -26
  427. autocoder/rag/qa_conversation_strategy.py +1 -1
  428. autocoder/rag/terminal/__init__.py +17 -0
  429. autocoder/rag/terminal/args.py +581 -0
  430. autocoder/rag/terminal/bootstrap.py +61 -0
  431. autocoder/rag/terminal/command_handlers.py +653 -0
  432. autocoder/rag/terminal/formatters/__init__.py +20 -0
  433. autocoder/rag/terminal/formatters/base.py +70 -0
  434. autocoder/rag/terminal/formatters/json_format.py +66 -0
  435. autocoder/rag/terminal/formatters/stream_json.py +95 -0
  436. autocoder/rag/terminal/formatters/text.py +28 -0
  437. autocoder/rag/terminal/init.py +120 -0
  438. autocoder/rag/terminal/utils.py +106 -0
  439. autocoder/rag/test_agentic_rag.py +389 -0
  440. autocoder/rag/test_doc_filter.py +3 -3
  441. autocoder/rag/test_long_context_rag.py +1 -1
  442. autocoder/rag/test_token_limiter.py +517 -10
  443. autocoder/rag/token_counter.py +3 -0
  444. autocoder/rag/token_limiter.py +19 -15
  445. autocoder/rag/tools/__init__.py +26 -2
  446. autocoder/rag/tools/bochaai_example.py +343 -0
  447. autocoder/rag/tools/bochaai_sdk.py +541 -0
  448. autocoder/rag/tools/metaso_example.py +268 -0
  449. autocoder/rag/tools/metaso_sdk.py +417 -0
  450. autocoder/rag/tools/recall_tool.py +28 -7
  451. autocoder/rag/tools/run_integration_tests.py +204 -0
  452. autocoder/rag/tools/test_all_providers.py +318 -0
  453. autocoder/rag/tools/test_bochaai_integration.py +482 -0
  454. autocoder/rag/tools/test_final_integration.py +215 -0
  455. autocoder/rag/tools/test_metaso_integration.py +424 -0
  456. autocoder/rag/tools/test_metaso_real.py +171 -0
  457. autocoder/rag/tools/test_web_crawl_tool.py +639 -0
  458. autocoder/rag/tools/test_web_search_tool.py +509 -0
  459. autocoder/rag/tools/todo_read_tool.py +202 -0
  460. autocoder/rag/tools/todo_write_tool.py +412 -0
  461. autocoder/rag/tools/web_crawl_tool.py +634 -0
  462. autocoder/rag/tools/web_search_tool.py +558 -0
  463. autocoder/rag/tools/web_tools_example.py +119 -0
  464. autocoder/rag/types.py +16 -0
  465. autocoder/rag/variable_holder.py +4 -2
  466. autocoder/rags.py +86 -79
  467. autocoder/regexproject/__init__.py +23 -21
  468. autocoder/sdk/__init__.py +46 -190
  469. autocoder/sdk/api.py +370 -0
  470. autocoder/sdk/async_runner/__init__.py +26 -0
  471. autocoder/sdk/async_runner/async_executor.py +650 -0
  472. autocoder/sdk/async_runner/async_handler.py +356 -0
  473. autocoder/sdk/async_runner/markdown_processor.py +595 -0
  474. autocoder/sdk/async_runner/task_metadata.py +284 -0
  475. autocoder/sdk/async_runner/worktree_manager.py +438 -0
  476. autocoder/sdk/cli/__init__.py +2 -5
  477. autocoder/sdk/cli/formatters.py +28 -204
  478. autocoder/sdk/cli/handlers.py +77 -44
  479. autocoder/sdk/cli/main.py +154 -171
  480. autocoder/sdk/cli/options.py +95 -22
  481. autocoder/sdk/constants.py +139 -51
  482. autocoder/sdk/core/auto_coder_core.py +484 -109
  483. autocoder/sdk/core/bridge.py +297 -115
  484. autocoder/sdk/exceptions.py +18 -12
  485. autocoder/sdk/formatters/__init__.py +19 -0
  486. autocoder/sdk/formatters/input.py +64 -0
  487. autocoder/sdk/formatters/output.py +247 -0
  488. autocoder/sdk/formatters/stream.py +54 -0
  489. autocoder/sdk/models/__init__.py +6 -5
  490. autocoder/sdk/models/options.py +55 -18
  491. autocoder/sdk/utils/formatters.py +27 -195
  492. autocoder/suffixproject/__init__.py +28 -25
  493. autocoder/terminal/__init__.py +14 -0
  494. autocoder/terminal/app.py +454 -0
  495. autocoder/terminal/args.py +32 -0
  496. autocoder/terminal/bootstrap.py +178 -0
  497. autocoder/terminal/command_processor.py +521 -0
  498. autocoder/terminal/command_registry.py +57 -0
  499. autocoder/terminal/help.py +97 -0
  500. autocoder/terminal/tasks/__init__.py +5 -0
  501. autocoder/terminal/tasks/background.py +77 -0
  502. autocoder/terminal/tasks/task_event.py +70 -0
  503. autocoder/terminal/ui/__init__.py +13 -0
  504. autocoder/terminal/ui/completer.py +268 -0
  505. autocoder/terminal/ui/keybindings.py +75 -0
  506. autocoder/terminal/ui/session.py +41 -0
  507. autocoder/terminal/ui/toolbar.py +64 -0
  508. autocoder/terminal/utils/__init__.py +13 -0
  509. autocoder/terminal/utils/errors.py +18 -0
  510. autocoder/terminal/utils/paths.py +19 -0
  511. autocoder/terminal/utils/shell.py +43 -0
  512. autocoder/terminal_v3/__init__.py +10 -0
  513. autocoder/terminal_v3/app.py +201 -0
  514. autocoder/terminal_v3/handlers/__init__.py +5 -0
  515. autocoder/terminal_v3/handlers/command_handler.py +131 -0
  516. autocoder/terminal_v3/models/__init__.py +6 -0
  517. autocoder/terminal_v3/models/conversation_buffer.py +214 -0
  518. autocoder/terminal_v3/models/message.py +50 -0
  519. autocoder/terminal_v3/models/tool_display.py +247 -0
  520. autocoder/terminal_v3/ui/__init__.py +7 -0
  521. autocoder/terminal_v3/ui/keybindings.py +56 -0
  522. autocoder/terminal_v3/ui/layout.py +141 -0
  523. autocoder/terminal_v3/ui/styles.py +43 -0
  524. autocoder/tsproject/__init__.py +23 -23
  525. autocoder/utils/auto_coder_utils/chat_stream_out.py +1 -1
  526. autocoder/utils/llms.py +88 -80
  527. autocoder/utils/math_utils.py +101 -0
  528. autocoder/utils/model_provider_selector.py +16 -4
  529. autocoder/utils/operate_config_api.py +33 -5
  530. autocoder/utils/thread_utils.py +2 -2
  531. autocoder/version.py +4 -2
  532. autocoder/workflow_agents/__init__.py +84 -0
  533. autocoder/workflow_agents/agent.py +143 -0
  534. autocoder/workflow_agents/exceptions.py +573 -0
  535. autocoder/workflow_agents/executor.py +665 -0
  536. autocoder/workflow_agents/loader.py +749 -0
  537. autocoder/workflow_agents/runner.py +267 -0
  538. autocoder/workflow_agents/types.py +173 -0
  539. autocoder/workflow_agents/utils.py +434 -0
  540. autocoder/workflow_agents/workflow_manager.py +211 -0
  541. auto_coder-1.0.0.dist-info/METADATA +0 -396
  542. auto_coder-1.0.0.dist-info/RECORD +0 -442
  543. auto_coder-1.0.0.dist-info/licenses/LICENSE +0 -201
  544. autocoder/auto_coder_server.py +0 -672
  545. autocoder/benchmark.py +0 -138
  546. autocoder/common/ac_style_command_parser/example.py +0 -7
  547. autocoder/common/cleaner.py +0 -31
  548. autocoder/common/command_completer_v2.py +0 -615
  549. autocoder/common/context_pruner.py +0 -477
  550. autocoder/common/conversation_pruner.py +0 -132
  551. autocoder/common/directory_cache/__init__.py +0 -1
  552. autocoder/common/directory_cache/cache.py +0 -192
  553. autocoder/common/directory_cache/test_cache.py +0 -190
  554. autocoder/common/file_checkpoint/examples.py +0 -217
  555. autocoder/common/llm_friendly_package_example.py +0 -138
  556. autocoder/common/llm_friendly_package_test.py +0 -63
  557. autocoder/common/pull_requests/test_module.py +0 -1
  558. autocoder/common/rulefiles/autocoderrules_utils.py +0 -484
  559. autocoder/common/text.py +0 -30
  560. autocoder/common/v2/agent/agentic_edit_tools/list_package_info_tool_resolver.py +0 -42
  561. autocoder/common/v2/agent/agentic_edit_tools/test_execute_command_tool_resolver.py +0 -70
  562. autocoder/common/v2/agent/agentic_edit_tools/test_search_files_tool_resolver.py +0 -163
  563. autocoder/common/v2/agent/agentic_tool_display.py +0 -183
  564. autocoder/plugins/dynamic_completion_example.py +0 -148
  565. autocoder/plugins/sample_plugin.py +0 -160
  566. autocoder/sdk/cli/__main__.py +0 -26
  567. autocoder/sdk/cli/completion_wrapper.py +0 -38
  568. autocoder/sdk/cli/install_completion.py +0 -301
  569. autocoder/sdk/models/messages.py +0 -209
  570. autocoder/sdk/session/__init__.py +0 -32
  571. autocoder/sdk/session/session.py +0 -106
  572. autocoder/sdk/session/session_manager.py +0 -56
  573. {auto_coder-1.0.0.dist-info → auto_coder-2.0.1.dist-info}/top_level.txt +0 -0
  574. /autocoder/{sdk/example.py → common/agent_query_queue/__init__.py} +0 -0
@@ -0,0 +1,541 @@
1
+ """
2
+ Error recovery module for shell command execution.
3
+
4
+ This module provides error recovery functionality including:
5
+ - Error handling strategies
6
+ - Automatic recovery mechanisms
7
+ - Retry logic with backoff
8
+ - Error categorization and logging
9
+ """
10
+
11
+ import time
12
+ import random
13
+ from typing import Dict, Type, Callable, Optional, Any, List, Union
14
+ from loguru import logger
15
+
16
+ from .exceptions import (
17
+ CommandExecutionError,
18
+ CommandTimeoutError,
19
+ ProcessCleanupError,
20
+ ProcessNotFoundError,
21
+ ErrorRecoveryFailedError
22
+ )
23
+
24
+
25
+ class ErrorRecoveryManager:
26
+ """
27
+ Manager for error recovery and handling.
28
+
29
+ This class provides comprehensive error recovery mechanisms including
30
+ retry logic, error categorization, and custom recovery strategies.
31
+
32
+ Attributes:
33
+ recovery_strategies: Dictionary mapping exception types to recovery functions
34
+ retry_config: Configuration for retry behavior
35
+ max_recovery_attempts: Maximum number of recovery attempts
36
+ """
37
+
38
+ def __init__(self, max_recovery_attempts: int = 3):
39
+ """
40
+ Initialize error recovery manager.
41
+
42
+ Args:
43
+ max_recovery_attempts: Maximum number of recovery attempts
44
+ """
45
+ self.max_recovery_attempts = max_recovery_attempts
46
+ self.recovery_strategies: Dict[Type[Exception], Callable] = {
47
+ CommandTimeoutError: self._handle_timeout_error,
48
+ ProcessCleanupError: self._handle_cleanup_error,
49
+ ProcessNotFoundError: self._handle_process_not_found_error,
50
+ CommandExecutionError: self._handle_execution_error,
51
+ }
52
+
53
+ logger.debug(f"ErrorRecoveryManager initialized with max_attempts={max_recovery_attempts}")
54
+
55
+ def register_recovery_strategy(
56
+ self,
57
+ exception_type: Type[Exception],
58
+ strategy: Callable
59
+ ) -> None:
60
+ """
61
+ Register a custom recovery strategy for an exception type.
62
+
63
+ Args:
64
+ exception_type: The exception type to handle
65
+ strategy: The recovery function to use
66
+ """
67
+ self.recovery_strategies[exception_type] = strategy
68
+ logger.debug(f"Registered recovery strategy for {exception_type.__name__}")
69
+
70
+ def handle_error(
71
+ self,
72
+ error: Exception,
73
+ context: Optional[Dict[str, Any]] = None,
74
+ retry_count: int = 0
75
+ ) -> bool:
76
+ """
77
+ Handle an error with appropriate recovery strategy.
78
+
79
+ Args:
80
+ error: The exception to handle
81
+ context: Additional context information
82
+ retry_count: Current retry attempt number
83
+
84
+ Returns:
85
+ True if recovery was successful, False otherwise
86
+
87
+ Raises:
88
+ ErrorRecoveryFailedError: If all recovery attempts fail
89
+ """
90
+ if context is None:
91
+ context = {}
92
+
93
+ error_type = type(error)
94
+
95
+ logger.warning(f"Handling error: {error_type.__name__}: {error}")
96
+
97
+ # Check if we have a recovery strategy for this error type
98
+ strategy = self._get_recovery_strategy(error_type)
99
+ if not strategy:
100
+ logger.error(f"No recovery strategy found for {error_type.__name__}")
101
+ return False
102
+
103
+ # Attempt recovery
104
+ try:
105
+ success = strategy(error, context, retry_count)
106
+
107
+ if success:
108
+ logger.info(f"Successfully recovered from {error_type.__name__}")
109
+ return True
110
+ else:
111
+ logger.warning(f"Recovery failed for {error_type.__name__}")
112
+
113
+ # Check if we should retry
114
+ if retry_count < self.max_recovery_attempts:
115
+ logger.debug(f"Retrying recovery, attempt {retry_count + 1}/{self.max_recovery_attempts}")
116
+ return self.handle_error(error, context, retry_count + 1)
117
+ else:
118
+ logger.error(f"Max recovery attempts reached for {error_type.__name__}")
119
+ raise ErrorRecoveryFailedError(error, retry_count)
120
+
121
+ except Exception as e:
122
+ logger.error(f"Error during recovery attempt: {e}")
123
+ if retry_count < self.max_recovery_attempts:
124
+ return self.handle_error(error, context, retry_count + 1)
125
+ else:
126
+ raise ErrorRecoveryFailedError(error, retry_count)
127
+
128
+ def _get_recovery_strategy(self, error_type: Type[Exception]) -> Optional[Callable]:
129
+ """
130
+ Get the appropriate recovery strategy for an error type.
131
+
132
+ Args:
133
+ error_type: The exception type
134
+
135
+ Returns:
136
+ Recovery strategy function or None
137
+ """
138
+ # Check for exact match first
139
+ if error_type in self.recovery_strategies:
140
+ return self.recovery_strategies[error_type]
141
+
142
+ # Check for parent class matches
143
+ for exception_type, strategy in self.recovery_strategies.items():
144
+ if issubclass(error_type, exception_type):
145
+ return strategy
146
+
147
+ return None
148
+
149
+ def _handle_timeout_error(
150
+ self,
151
+ error: CommandTimeoutError,
152
+ context: Dict[str, Any],
153
+ retry_count: int
154
+ ) -> bool:
155
+ """
156
+ Handle command timeout errors.
157
+
158
+ Args:
159
+ error: The timeout error
160
+ context: Context information
161
+ retry_count: Current retry attempt
162
+
163
+ Returns:
164
+ True if recovery successful
165
+ """
166
+ logger.debug(f"Handling timeout error for command: {error.command}")
167
+
168
+ # Strategy 1: Increase timeout and retry
169
+ if retry_count == 0:
170
+ new_timeout = error.timeout * 1.5
171
+ logger.debug(f"Increasing timeout to {new_timeout}s and retrying")
172
+ context['timeout'] = new_timeout
173
+ return True
174
+
175
+ # Strategy 2: Try with even longer timeout
176
+ elif retry_count == 1:
177
+ new_timeout = error.timeout * 2.0
178
+ logger.debug(f"Increasing timeout to {new_timeout}s (final attempt)")
179
+ context['timeout'] = new_timeout
180
+ return True
181
+
182
+ # Strategy 3: Give up
183
+ else:
184
+ logger.error(f"Unable to recover from timeout error after {retry_count} attempts")
185
+ return False
186
+
187
+ def _handle_cleanup_error(
188
+ self,
189
+ error: ProcessCleanupError,
190
+ context: Dict[str, Any],
191
+ retry_count: int
192
+ ) -> bool:
193
+ """
194
+ Handle process cleanup errors.
195
+
196
+ Args:
197
+ error: The cleanup error
198
+ context: Context information
199
+ retry_count: Current retry attempt
200
+
201
+ Returns:
202
+ True if recovery successful
203
+ """
204
+ logger.debug(f"Handling cleanup error for PID: {error.pid}")
205
+
206
+ # Strategy 1: Wait and retry cleanup
207
+ if retry_count == 0:
208
+ logger.debug("Waiting before retry cleanup")
209
+ time.sleep(1.0)
210
+ return True
211
+
212
+ # Strategy 2: Force cleanup with longer timeout
213
+ elif retry_count == 1:
214
+ logger.debug("Attempting force cleanup with longer timeout")
215
+ context['force_cleanup'] = True
216
+ context['cleanup_timeout'] = 15.0
217
+ return True
218
+
219
+ # Strategy 3: Log and continue (non-fatal)
220
+ else:
221
+ logger.warning(f"Unable to cleanup process {error.pid}, continuing anyway")
222
+ return True # Consider cleanup errors non-fatal
223
+
224
+ def _handle_process_not_found_error(
225
+ self,
226
+ error: ProcessNotFoundError,
227
+ context: Dict[str, Any],
228
+ retry_count: int
229
+ ) -> bool:
230
+ """
231
+ Handle process not found errors.
232
+
233
+ Args:
234
+ error: The process not found error
235
+ context: Context information
236
+ retry_count: Current retry attempt
237
+
238
+ Returns:
239
+ True if recovery successful
240
+ """
241
+ logger.debug(f"Handling process not found error for PID: {error.pid}")
242
+
243
+ # Process not found is usually not a real error - process already exited
244
+ logger.debug(f"Process {error.pid} not found, likely already exited")
245
+ return True
246
+
247
+ def _handle_execution_error(
248
+ self,
249
+ error: CommandExecutionError,
250
+ context: Dict[str, Any],
251
+ retry_count: int
252
+ ) -> bool:
253
+ """
254
+ Handle general command execution errors.
255
+
256
+ Args:
257
+ error: The execution error
258
+ context: Context information
259
+ retry_count: Current retry attempt
260
+
261
+ Returns:
262
+ True if recovery successful
263
+ """
264
+ logger.debug(f"Handling execution error: {error}")
265
+
266
+ # Strategy 1: Simple retry
267
+ if retry_count == 0:
268
+ logger.debug("Retrying command execution")
269
+ return True
270
+
271
+ # Strategy 2: Wait and retry
272
+ elif retry_count == 1:
273
+ logger.debug("Waiting before retry")
274
+ time.sleep(0.5)
275
+ return True
276
+
277
+ # Strategy 3: Give up
278
+ else:
279
+ logger.error(f"Unable to recover from execution error after {retry_count} attempts")
280
+ return False
281
+
282
+ def _validate_delays(self, delays: List[float]) -> List[float]:
283
+ """
284
+ Validate retry delays.
285
+
286
+ Args:
287
+ delays: List of delays to validate
288
+
289
+ Returns:
290
+ Validated delays
291
+
292
+ Raises:
293
+ ValueError: If delays are invalid
294
+ """
295
+ if not delays:
296
+ raise ValueError("Delays cannot be empty")
297
+
298
+ # Fix negative delays by setting them to 0
299
+ validated_delays = []
300
+ for delay in delays:
301
+ if delay < 0:
302
+ validated_delays.append(0.0) # Set negative delays to 0
303
+ else:
304
+ validated_delays.append(delay)
305
+
306
+ return validated_delays
307
+
308
+
309
+ class RetryConfig:
310
+ """
311
+ Configuration for retry behavior.
312
+
313
+ Attributes:
314
+ max_retries: Maximum number of retries (0 means no retries)
315
+ delays: Custom delay sequence (if None, uses exponential backoff)
316
+ backoff_factor: Factor for exponential backoff
317
+ jitter: Whether to add random jitter to delays
318
+ retry_on_timeout: Whether to retry on timeout
319
+ retry_on_error: Whether to retry on other errors
320
+ """
321
+
322
+ def __init__(
323
+ self,
324
+ max_retries: Optional[int] = None,
325
+ delays: Optional[List[float]] = None,
326
+ backoff_factor: float = 2.0,
327
+ jitter: bool = True,
328
+ retry_on_timeout: bool = True,
329
+ retry_on_error: bool = True,
330
+ # Backward compatibility parameters
331
+ max_attempts: Optional[int] = None,
332
+ base_delay: Optional[float] = None,
333
+ max_delay: Optional[float] = None,
334
+ retry_on_cleanup_error: Optional[bool] = None
335
+ ):
336
+ """
337
+ Initialize retry configuration.
338
+
339
+ Args:
340
+ max_retries: Maximum number of retries (0 means no retries)
341
+ delays: Custom delay sequence (if None, uses exponential backoff)
342
+ backoff_factor: Factor for exponential backoff
343
+ jitter: Whether to add random jitter to delays
344
+ retry_on_timeout: Whether to retry on timeout
345
+ retry_on_error: Whether to retry on other errors
346
+
347
+ # Backward compatibility:
348
+ max_attempts: Old name for max_retries
349
+ base_delay: Base delay for old exponential backoff
350
+ max_delay: Maximum delay for old exponential backoff
351
+ retry_on_cleanup_error: Old name for retry_on_error
352
+ """
353
+ # Handle backward compatibility
354
+ if max_attempts is not None:
355
+ max_retries = max_attempts
356
+ if max_retries is None:
357
+ max_retries = 3
358
+
359
+ if retry_on_cleanup_error is not None:
360
+ retry_on_error = retry_on_cleanup_error
361
+
362
+ if max_retries < 0:
363
+ raise ValueError("max_retries cannot be negative")
364
+
365
+ self.max_retries = max_retries
366
+ # For backward compatibility
367
+ self.max_attempts = max_retries
368
+
369
+ self.backoff_factor = backoff_factor
370
+ self.jitter = jitter
371
+ self.retry_on_timeout = retry_on_timeout
372
+ self.retry_on_error = retry_on_error
373
+ self.retry_on_cleanup_error = retry_on_error # Backward compatibility
374
+
375
+ # Handle old-style delay configuration
376
+ if base_delay is not None or max_delay is not None:
377
+ # Use old exponential backoff style
378
+ self.base_delay = base_delay if base_delay is not None else 1.0
379
+ self.max_delay = max_delay if max_delay is not None else 60.0
380
+
381
+ if max_retries == 0:
382
+ self.delays = []
383
+ else:
384
+ # Generate delays using old style
385
+ self.delays = []
386
+ for i in range(max_retries):
387
+ delay = self.base_delay * (backoff_factor ** i)
388
+ delay = min(delay, self.max_delay)
389
+ # Fix negative delays
390
+ delay = max(0.0, delay)
391
+ self.delays.append(delay)
392
+ else:
393
+ # Use new style delay configuration
394
+ self.base_delay = 1.0 # Default for backward compatibility
395
+ self.max_delay = 60.0 # Default for backward compatibility
396
+
397
+ if delays is not None:
398
+ # Validate and fix delays directly
399
+ if not delays:
400
+ raise ValueError("Delays cannot be empty")
401
+
402
+ # Fix negative delays by setting them to 0
403
+ validated_delays = []
404
+ for delay in delays:
405
+ if delay < 0:
406
+ validated_delays.append(0.0) # Set negative delays to 0
407
+ else:
408
+ validated_delays.append(delay)
409
+
410
+ self.delays = validated_delays
411
+ else:
412
+ # Generate default delays based on max_retries
413
+ if max_retries == 0:
414
+ self.delays = [] # No retries, no delays needed
415
+ else:
416
+ self.delays = [min(60.0, backoff_factor ** i) for i in range(max_retries)]
417
+
418
+ def get_delay(self, attempt: int) -> float:
419
+ """
420
+ Get the delay for a specific retry attempt.
421
+
422
+ Args:
423
+ attempt: The retry attempt number (0-based)
424
+
425
+ Returns:
426
+ Delay in seconds
427
+ """
428
+ # Ensure attempt is within bounds, use last delay for out-of-bounds
429
+ if attempt < 0:
430
+ attempt = 0
431
+ elif attempt >= len(self.delays):
432
+ attempt = len(self.delays) - 1 if self.delays else 0
433
+
434
+ # If no delays configured, return 0
435
+ if not self.delays:
436
+ return 0.0
437
+
438
+ delay = self.delays[attempt]
439
+ if self.jitter:
440
+ delay += random.uniform(-0.1 * delay, 0.1 * delay)
441
+ # Ensure delay is never negative
442
+ return max(0.0, delay)
443
+
444
+ def should_retry(self, error: Exception, attempt: int) -> bool:
445
+ """
446
+ Determine if an error should be retried.
447
+
448
+ Args:
449
+ error: The exception that occurred
450
+ attempt: The current attempt number
451
+
452
+ Returns:
453
+ True if should retry
454
+ """
455
+ if attempt >= self.max_attempts:
456
+ return False
457
+
458
+ if isinstance(error, CommandTimeoutError):
459
+ return self.retry_on_timeout
460
+
461
+ if isinstance(error, ProcessCleanupError):
462
+ return self.retry_on_error
463
+
464
+ if isinstance(error, ProcessNotFoundError):
465
+ return False # Don't retry process not found errors
466
+
467
+ if isinstance(error, CommandExecutionError):
468
+ return self.retry_on_error # Retry general execution errors
469
+
470
+ return False
471
+
472
+
473
+ def retry_with_backoff(
474
+ func: Callable,
475
+ retry_config: RetryConfig,
476
+ error_recovery_manager: Optional[ErrorRecoveryManager] = None,
477
+ *args,
478
+ **kwargs
479
+ ) -> Any:
480
+ """
481
+ Retry a function with exponential backoff.
482
+
483
+ Args:
484
+ func: Function to retry
485
+ retry_config: Retry configuration
486
+ error_recovery_manager: Optional error recovery manager
487
+ *args: Function arguments
488
+ **kwargs: Function keyword arguments
489
+
490
+ Returns:
491
+ Function result
492
+
493
+ Raises:
494
+ The last exception if all retries fail
495
+ """
496
+ last_exception = None
497
+
498
+ # Use max_attempts directly from config (which handles backward compatibility)
499
+ max_attempts = max(1, retry_config.max_attempts)
500
+
501
+ for attempt in range(max_attempts):
502
+ try:
503
+ return func(*args, **kwargs)
504
+ except Exception as e:
505
+ last_exception = e
506
+
507
+ # If this is the last attempt or we shouldn't retry, break
508
+ if attempt >= max_attempts or not retry_config.should_retry(e, attempt):
509
+ logger.debug(f"Not retrying {type(e).__name__} on attempt {attempt}")
510
+ break
511
+
512
+ # Try error recovery if available
513
+ if error_recovery_manager:
514
+ try:
515
+ if error_recovery_manager.handle_error(e, retry_count=attempt):
516
+ logger.debug(f"Error recovery successful for attempt {attempt}")
517
+ continue
518
+ except ErrorRecoveryFailedError:
519
+ logger.debug(f"Error recovery failed for attempt {attempt}")
520
+ break
521
+
522
+ # Calculate delay
523
+ delay = retry_config.get_delay(attempt)
524
+ logger.debug(f"Retrying in {delay}s (attempt {attempt + 1}/{max_attempts})")
525
+ time.sleep(delay)
526
+
527
+ # All retries failed
528
+ if last_exception:
529
+ raise last_exception
530
+ else:
531
+ raise CommandExecutionError("All retry attempts failed")
532
+
533
+
534
+ def create_default_error_recovery_manager() -> ErrorRecoveryManager:
535
+ """
536
+ Create a default error recovery manager with standard strategies.
537
+
538
+ Returns:
539
+ Configured ErrorRecoveryManager instance
540
+ """
541
+ return ErrorRecoveryManager(max_recovery_attempts=3)
@@ -0,0 +1,120 @@
1
+ """
2
+ Custom exceptions for shell command execution with timeout support.
3
+
4
+ This module defines all custom exceptions used in the shell commands module
5
+ for proper error handling and recovery.
6
+ """
7
+
8
+ from typing import Optional, Any
9
+
10
+
11
+ class CommandExecutionError(Exception):
12
+ """
13
+ Base exception for command execution errors.
14
+
15
+ This is the base class for all command execution related exceptions.
16
+ """
17
+ pass
18
+
19
+
20
+ class CommandTimeoutError(CommandExecutionError):
21
+ """
22
+ Exception raised when a command execution times out.
23
+
24
+ Attributes:
25
+ command: The command that timed out
26
+ timeout: The timeout value in seconds
27
+ pid: The process ID that timed out (if available)
28
+ """
29
+
30
+ def __init__(self, command: str, timeout: float, pid: Optional[int] = None):
31
+ self.command = command
32
+ self.timeout = timeout
33
+ self.pid = pid
34
+
35
+ message = f"Command '{command}' timed out after {timeout} seconds"
36
+ if pid:
37
+ message += f" (PID: {pid})"
38
+
39
+ super().__init__(message)
40
+
41
+ def __str__(self) -> str:
42
+ return super().__str__()
43
+
44
+ def __repr__(self) -> str:
45
+ return f"CommandTimeoutError(command={self.command!r}, timeout={self.timeout}, pid={self.pid})"
46
+
47
+
48
+ class ProcessCleanupError(CommandExecutionError):
49
+ """
50
+ Exception raised when process cleanup fails.
51
+
52
+ Attributes:
53
+ pid: The process ID that failed to cleanup
54
+ message: Detailed error message
55
+ """
56
+
57
+ def __init__(self, pid: int, message: str):
58
+ self.pid = pid
59
+ self.original_message = message
60
+ super().__init__(f"Failed to cleanup process {pid}: {message}")
61
+
62
+ def __repr__(self) -> str:
63
+ return f"ProcessCleanupError(pid={self.pid}, message={self.original_message!r})"
64
+
65
+
66
+ class ProcessNotFoundError(CommandExecutionError):
67
+ """
68
+ Exception raised when a process is not found during cleanup or management.
69
+
70
+ Attributes:
71
+ pid: The process ID that was not found
72
+ """
73
+
74
+ def __init__(self, pid: int):
75
+ self.pid = pid
76
+ super().__init__(f"Process {pid} not found")
77
+
78
+ def __repr__(self) -> str:
79
+ return f"ProcessNotFoundError(pid={self.pid})"
80
+
81
+
82
+ class TimeoutConfigError(CommandExecutionError):
83
+ """
84
+ Exception raised when there's an error in timeout configuration.
85
+
86
+ Attributes:
87
+ config_field: The configuration field that caused the error
88
+ value: The invalid value
89
+ reason: The reason why the value is invalid
90
+ """
91
+
92
+ def __init__(self, config_field: str, value: Any, reason: str):
93
+ self.config_field = config_field
94
+ self.value = value
95
+ self.reason = reason
96
+ super().__init__(f"Invalid timeout configuration for '{config_field}': {reason} (value: {value})")
97
+
98
+ def __repr__(self) -> str:
99
+ return f"TimeoutConfigError(config_field={self.config_field!r}, value={self.value!r}, reason={self.reason!r})"
100
+
101
+
102
+ class ErrorRecoveryFailedError(CommandExecutionError):
103
+ """
104
+ Exception raised when error recovery mechanisms fail.
105
+
106
+ Attributes:
107
+ original_error: The original error that triggered recovery
108
+ recovery_attempts: Number of recovery attempts made
109
+ """
110
+
111
+ def __init__(self, original_error: Exception, recovery_attempts: int):
112
+ self.original_error = original_error
113
+ self.recovery_attempts = recovery_attempts
114
+ super().__init__(
115
+ f"Error recovery failed after {recovery_attempts} attempts. "
116
+ f"Original error: {original_error}"
117
+ )
118
+
119
+ def __repr__(self) -> str:
120
+ return f"ErrorRecoveryFailedError(original_error={self.original_error!r}, recovery_attempts={self.recovery_attempts})"