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,606 @@
1
+ """
2
+ Process manager module for shell command execution.
3
+
4
+ This module provides process management functionality including:
5
+ - Process creation with proper configuration
6
+ - Process monitoring and lifecycle management
7
+ - Process group management
8
+ - Integration with timeout and cleanup systems
9
+ """
10
+
11
+ import os
12
+ import subprocess
13
+ import platform
14
+ import threading
15
+ import time
16
+ from typing import Dict, List, Optional, Tuple, Any, Union
17
+ from loguru import logger
18
+
19
+ from .timeout_config import TimeoutConfig
20
+ from .timeout_manager import TimeoutManager
21
+ from .process_cleanup import cleanup_process_tree, get_process_children
22
+ from .exceptions import CommandExecutionError, ProcessCleanupError
23
+
24
+
25
+ def _command_to_string(command: Union[str, List[str]]) -> str:
26
+ """
27
+ Convert command to string format for timeout configuration lookup.
28
+
29
+ Args:
30
+ command: Command as string or list
31
+
32
+ Returns:
33
+ Command as string
34
+ """
35
+ if isinstance(command, list):
36
+ if not command:
37
+ return ""
38
+ # Join list elements into a single string
39
+ return " ".join(str(arg) for arg in command)
40
+ return command
41
+
42
+
43
+ class ProcessManager:
44
+ """
45
+ Manager for process creation and lifecycle management.
46
+
47
+ This class handles process creation, monitoring, and cleanup with
48
+ proper timeout management and cross-platform support.
49
+ """
50
+
51
+ def __init__(self, config: TimeoutConfig):
52
+ """
53
+ Initialize process manager.
54
+
55
+ Args:
56
+ config: Timeout configuration
57
+ """
58
+ self.config = config
59
+ self.timeout_manager = TimeoutManager(config)
60
+ self.active_processes: Dict[int, subprocess.Popen] = {}
61
+ self.process_groups: Dict[int, List[int]] = {}
62
+ self.background_processes: Dict[int, Dict[str, Any]] = {}
63
+ self._lock = threading.Lock()
64
+
65
+ logger.debug(f"ProcessManager initialized with config: {config}")
66
+
67
+ def create_process(
68
+ self,
69
+ command: Union[str, List[str]],
70
+ timeout: Optional[float] = None,
71
+ cwd: Optional[str] = None,
72
+ env: Optional[Dict[str, str]] = None,
73
+ shell: bool = True,
74
+ capture_output: bool = True,
75
+ text: bool = True,
76
+ encoding: str = 'utf-8',
77
+ **kwargs
78
+ ) -> subprocess.Popen:
79
+ """
80
+ Create a new process with proper configuration.
81
+
82
+ Args:
83
+ command: Command to execute
84
+ timeout: Timeout in seconds
85
+ cwd: Working directory
86
+ env: Environment variables
87
+ shell: Whether to use shell
88
+ capture_output: Whether to capture stdout/stderr
89
+ text: Whether to use text mode
90
+ encoding: Text encoding
91
+ **kwargs: Additional arguments for subprocess.Popen
92
+
93
+ Returns:
94
+ Created subprocess.Popen object
95
+ """
96
+ try:
97
+ # Determine timeout
98
+ if timeout is None:
99
+ timeout = self.config.get_timeout_for_command(
100
+ _command_to_string(command))
101
+
102
+ # Prepare process arguments
103
+ popen_args = {
104
+ 'shell': shell,
105
+ 'cwd': cwd,
106
+ 'env': env,
107
+ 'text': text,
108
+ 'encoding': encoding,
109
+ 'errors': 'replace',
110
+ **kwargs
111
+ }
112
+
113
+ # Configure output capturing
114
+ if capture_output:
115
+ popen_args.update({
116
+ 'stdout': subprocess.PIPE,
117
+ 'stderr': subprocess.STDOUT,
118
+ })
119
+
120
+ # Configure process group creation
121
+ if platform.system() == "Windows":
122
+ popen_args['creationflags'] = subprocess.CREATE_NEW_PROCESS_GROUP
123
+ else:
124
+ popen_args['preexec_fn'] = os.setsid
125
+
126
+ logger.debug(f"Creating process for command: {command}")
127
+
128
+ # Create process
129
+ process = subprocess.Popen(command, **popen_args)
130
+
131
+ # Register process
132
+ self._register_process(process)
133
+
134
+ # Note: Timeout will be handled by subprocess.communicate in the executor
135
+ # No need to start separate timeout manager here
136
+
137
+ logger.debug(f"Created process with PID {process.pid}")
138
+ return process
139
+
140
+ except Exception as e:
141
+ logger.error(
142
+ f"Failed to create process for command '{command}': {e}")
143
+ raise CommandExecutionError(f"Failed to create process: {e}")
144
+
145
+ def _register_process(self, process: subprocess.Popen) -> None:
146
+ """Register a process for management."""
147
+ pid = process.pid
148
+
149
+ with self._lock:
150
+ self.active_processes[pid] = process
151
+
152
+ # Create process group entry
153
+ try:
154
+ if platform.system() != "Windows":
155
+ pgid = os.getpgid(pid)
156
+ if pgid not in self.process_groups:
157
+ self.process_groups[pgid] = []
158
+ self.process_groups[pgid].append(pid)
159
+ else:
160
+ # On Windows, each process is its own group
161
+ self.process_groups[pid] = [pid]
162
+ except OSError:
163
+ pass
164
+
165
+ logger.debug(f"Registered process {pid} for management")
166
+
167
+ def unregister_process(self, process: subprocess.Popen) -> None:
168
+ """Unregister a process from management."""
169
+ pid = process.pid
170
+
171
+ with self._lock:
172
+ # Remove from active processes
173
+ self.active_processes.pop(pid, None)
174
+
175
+ # Remove from background processes if it's a background process
176
+ self.background_processes.pop(pid, None)
177
+
178
+ # Remove from process groups
179
+ for pgid, pids in list(self.process_groups.items()):
180
+ if pid in pids:
181
+ pids.remove(pid)
182
+ if not pids: # Remove empty groups
183
+ del self.process_groups[pgid]
184
+ break
185
+
186
+ # Cancel timeout
187
+ self.timeout_manager.cancel_timeout(process)
188
+
189
+ logger.debug(f"Unregistered process {pid}")
190
+
191
+ def wait_for_process(
192
+ self,
193
+ process: subprocess.Popen,
194
+ timeout: Optional[float] = None
195
+ ) -> int:
196
+ """
197
+ Wait for a process to complete.
198
+
199
+ Args:
200
+ process: Process to wait for
201
+ timeout: Timeout in seconds
202
+
203
+ Returns:
204
+ Process exit code
205
+ """
206
+ try:
207
+ if timeout:
208
+ exit_code = process.wait(timeout=timeout)
209
+ else:
210
+ exit_code = process.wait()
211
+
212
+ # Unregister process
213
+ self.unregister_process(process)
214
+
215
+ logger.debug(
216
+ f"Process {process.pid} completed with exit code {exit_code}")
217
+ return exit_code
218
+
219
+ except subprocess.TimeoutExpired:
220
+ logger.warning(f"Process {process.pid} timed out during wait")
221
+ raise
222
+ except Exception as e:
223
+ logger.error(f"Error waiting for process {process.pid}: {e}")
224
+ raise CommandExecutionError(f"Error waiting for process: {e}")
225
+
226
+ def cleanup_process_tree(
227
+ self,
228
+ process: subprocess.Popen,
229
+ timeout: Optional[float] = None
230
+ ) -> bool:
231
+ """
232
+ Clean up a process and its entire tree.
233
+
234
+ Args:
235
+ process: Root process to cleanup
236
+ timeout: Timeout for cleanup
237
+
238
+ Returns:
239
+ True if cleanup successful
240
+ """
241
+
242
+ try:
243
+ if process.poll() is not None:
244
+ # 进程已经结束,直接注销
245
+ self.unregister_process(process)
246
+ return True
247
+ except Exception as e:
248
+ logger.error(f"Error checking process status: {e}")
249
+ pass
250
+
251
+ pid = process.pid
252
+
253
+ if timeout is None:
254
+ timeout = self.config.cleanup_timeout
255
+
256
+ try:
257
+ logger.debug(f"Cleaning up process tree for PID {pid}")
258
+
259
+ # Cancel timeout first
260
+ self.timeout_manager.cancel_timeout(process)
261
+
262
+ # Use the process cleanup module
263
+ success = cleanup_process_tree(
264
+ pid,
265
+ timeout=self.config.grace_period,
266
+ force_timeout=timeout - self.config.grace_period
267
+ )
268
+
269
+ # Unregister process
270
+ self.unregister_process(process)
271
+
272
+ if success:
273
+ logger.debug(
274
+ f"Successfully cleaned up process tree for PID {pid}")
275
+ else:
276
+ logger.warning(
277
+ f"Failed to fully cleanup process tree for PID {pid}")
278
+
279
+ return success
280
+
281
+ except Exception as e:
282
+ logger.error(f"Error cleaning up process tree for PID {pid}: {e}")
283
+ return False
284
+
285
+ def get_all_processes(self) -> Dict[int, subprocess.Popen]:
286
+ """Get all active processes."""
287
+ with self._lock:
288
+ return self.active_processes.copy()
289
+
290
+ def get_process_groups(self) -> Dict[int, List[int]]:
291
+ """Get process groups mapping."""
292
+ with self._lock:
293
+ return {pgid: pids.copy() for pgid, pids in self.process_groups.items()}
294
+
295
+ def cleanup_all_processes(self, timeout: float = 10.0) -> List[int]:
296
+ """
297
+ Clean up all managed processes.
298
+
299
+ Args:
300
+ timeout: Timeout for each process cleanup
301
+
302
+ Returns:
303
+ List of process IDs that failed to cleanup
304
+ """
305
+ failed_pids = []
306
+
307
+ # Get copy of active processes
308
+ with self._lock:
309
+ processes = list(self.active_processes.values())
310
+ background_pids = list(self.background_processes.keys())
311
+
312
+ logger.debug(f"Cleaning up {len(processes)} processes (including {len(background_pids)} background processes)")
313
+
314
+ for process in processes:
315
+ try:
316
+ if not self.cleanup_process_tree(process, timeout):
317
+ failed_pids.append(process.pid)
318
+ except Exception as e:
319
+ logger.error(f"Error cleaning up process {process.pid}: {e}")
320
+ failed_pids.append(process.pid)
321
+
322
+ # Cleanup timeout manager
323
+ self.timeout_manager.cleanup_all_timeouts()
324
+
325
+ # Clear background processes records for successful cleanups
326
+ with self._lock:
327
+ for pid in background_pids:
328
+ if pid not in failed_pids:
329
+ self.background_processes.pop(pid, None)
330
+
331
+ logger.debug(f"Cleanup completed, {len(failed_pids)} processes failed")
332
+ return failed_pids
333
+
334
+ def __del__(self):
335
+ """Cleanup when manager is destroyed."""
336
+ try:
337
+ self.cleanup_all_processes()
338
+ except Exception:
339
+ pass # Ignore errors during cleanup
340
+
341
+ def create_background_process(
342
+ self,
343
+ command: Union[str, List[str]],
344
+ cwd: Optional[str] = None,
345
+ env: Optional[Dict[str, str]] = None,
346
+ shell: bool = True,
347
+ process_uniq_id: Optional[str] = None,
348
+ **kwargs
349
+ ) -> subprocess.Popen:
350
+ """
351
+ Create a background process that runs independently.
352
+
353
+ Args:
354
+ command: Command to execute
355
+ cwd: Working directory
356
+ env: Environment variables
357
+ shell: Whether to use shell
358
+ **kwargs: Additional arguments for subprocess.Popen
359
+
360
+ Returns:
361
+ Created subprocess.Popen object for background process
362
+ """
363
+ try:
364
+ # Prepare process arguments for background execution
365
+ popen_args = {
366
+ 'shell': shell,
367
+ 'cwd': cwd,
368
+ 'env': env,
369
+ 'stdout': subprocess.PIPE,
370
+ 'stderr': subprocess.PIPE,
371
+ 'text': True,
372
+ 'encoding': 'utf-8',
373
+ 'errors': 'replace',
374
+ **kwargs
375
+ }
376
+
377
+ # Configure process group creation for proper cleanup
378
+ if platform.system() == "Windows":
379
+ popen_args['creationflags'] = subprocess.CREATE_NEW_PROCESS_GROUP
380
+ else:
381
+ popen_args['preexec_fn'] = os.setsid
382
+
383
+ logger.debug(f"Creating background process for command: {command}")
384
+
385
+ # Create process
386
+ process = subprocess.Popen(command, **popen_args)
387
+
388
+ # Register as background process
389
+ self._register_background_process(process, command, cwd, process_uniq_id)
390
+
391
+ logger.debug(f"Created background process with PID {process.pid}")
392
+ return process
393
+
394
+ except Exception as e:
395
+ logger.error(
396
+ f"Failed to create background process for command '{command}': {e}")
397
+ raise CommandExecutionError(f"Failed to create background process: {e}")
398
+
399
+ def _register_background_process(
400
+ self,
401
+ process: subprocess.Popen,
402
+ command: Union[str, List[str]],
403
+ cwd: Optional[str],
404
+ process_uniq_id: Optional[str] = None
405
+ ) -> None:
406
+ """Register a background process for tracking."""
407
+ pid = process.pid
408
+ command_str = _command_to_string(command)
409
+
410
+ # Create backgrounds directory relative to target working directory
411
+ base_dir = cwd if isinstance(cwd, str) and len(cwd) > 0 else os.getcwd()
412
+ backgrounds_dir = os.path.join(base_dir, '.auto-coder', 'backgrounds')
413
+ os.makedirs(backgrounds_dir, exist_ok=True)
414
+
415
+ with self._lock:
416
+ # Register in active processes
417
+ self.active_processes[pid] = process
418
+
419
+ # Record background process metadata
420
+ self.background_processes[pid] = {
421
+ 'command': command_str,
422
+ 'cwd': cwd,
423
+ 'start_time': time.time(),
424
+ 'process': process,
425
+ 'process_uniq_id': process_uniq_id
426
+ }
427
+
428
+ # Create process group entry
429
+ try:
430
+ if platform.system() != "Windows":
431
+ pgid = os.getpgid(pid)
432
+ if pgid not in self.process_groups:
433
+ self.process_groups[pgid] = []
434
+ self.process_groups[pgid].append(pid)
435
+ else:
436
+ # On Windows, each process is its own group
437
+ self.process_groups[pid] = [pid]
438
+ except OSError:
439
+ pass
440
+
441
+ # Start threads to capture stdout and stderr
442
+ self._start_output_capture_threads(process, process_uniq_id or str(pid), backgrounds_dir)
443
+
444
+ logger.debug(f"Registered background process {pid} for command: {command_str}")
445
+
446
+ def _start_output_capture_threads(
447
+ self,
448
+ process: subprocess.Popen,
449
+ process_id: str,
450
+ backgrounds_dir: str
451
+ ) -> None:
452
+ """Start threads to capture stdout and stderr to files."""
453
+ # Start stdout capture thread
454
+ if process.stdout:
455
+ stdout_file = os.path.join(backgrounds_dir, f"{process_id}.out")
456
+ stdout_thread = threading.Thread(
457
+ target=self._capture_output_to_file,
458
+ args=(process.stdout, stdout_file, process_id, "stdout"),
459
+ daemon=True
460
+ )
461
+ stdout_thread.start()
462
+ logger.debug(f"Started stdout capture thread for process {process_id} (PID {process.pid})")
463
+
464
+ # Start stderr capture thread
465
+ if process.stderr:
466
+ stderr_file = os.path.join(backgrounds_dir, f"{process_id}.err")
467
+ stderr_thread = threading.Thread(
468
+ target=self._capture_output_to_file,
469
+ args=(process.stderr, stderr_file, process_id, "stderr"),
470
+ daemon=True
471
+ )
472
+ stderr_thread.start()
473
+ logger.debug(f"Started stderr capture thread for process {process_id} (PID {process.pid})")
474
+
475
+ def _capture_output_to_file(
476
+ self,
477
+ stream,
478
+ filepath: str,
479
+ process_id: str,
480
+ stream_name: str
481
+ ) -> None:
482
+ """Capture output from a stream and write to file.
483
+
484
+ Handles both text and binary streams robustly by encoding text to UTF-8
485
+ before writing to the destination file.
486
+ """
487
+ try:
488
+ with open(filepath, 'wb') as f:
489
+ while True:
490
+ # Read data from stream
491
+ data = stream.read(4096)
492
+ if not data:
493
+ break
494
+
495
+ # Normalize to bytes (stream may be text when Popen(text=True))
496
+ if isinstance(data, str):
497
+ data = data.encode('utf-8', errors='replace')
498
+
499
+ # Write to file
500
+ f.write(data)
501
+ f.flush()
502
+
503
+ logger.debug(f"Finished capturing {stream_name} for process {process_id}")
504
+ except Exception as e:
505
+ logger.error(f"Error capturing {stream_name} for process {process_id}: {e}")
506
+ finally:
507
+ try:
508
+ stream.close()
509
+ except Exception:
510
+ pass
511
+
512
+ def get_background_processes(self) -> Dict[int, Dict[str, Any]]:
513
+ """Get all background processes information."""
514
+ with self._lock:
515
+ # Update status for each background process
516
+ updated_processes = {}
517
+ for pid, info in self.background_processes.items():
518
+ process = info['process']
519
+ updated_info = info.copy()
520
+
521
+ # Update process status
522
+ if process.poll() is None:
523
+ updated_info['status'] = 'running'
524
+ updated_info['exit_code'] = None
525
+ else:
526
+ updated_info['status'] = 'completed'
527
+ updated_info['exit_code'] = process.returncode
528
+ updated_info['end_time'] = time.time()
529
+
530
+ updated_processes[pid] = updated_info
531
+
532
+ return updated_processes
533
+
534
+ def is_background_process(self, pid: int) -> bool:
535
+ """Check if a process is a background process."""
536
+ with self._lock:
537
+ return pid in self.background_processes
538
+
539
+ def get_background_process_info(self, pid: int) -> Optional[Dict[str, Any]]:
540
+ """Get information about a specific background process."""
541
+ background_processes = self.get_background_processes()
542
+ return background_processes.get(pid)
543
+
544
+ def cleanup_background_process(self, pid: int, timeout: Optional[float] = None) -> bool:
545
+ """
546
+ Clean up a specific background process.
547
+
548
+ Args:
549
+ pid: Process ID to cleanup
550
+ timeout: Timeout for cleanup
551
+
552
+ Returns:
553
+ True if cleanup successful
554
+ """
555
+ with self._lock:
556
+ if pid not in self.background_processes:
557
+ logger.debug(f"Process {pid} is not a background process")
558
+ return True
559
+
560
+ process = self.background_processes[pid]['process']
561
+
562
+ # Use the existing cleanup logic
563
+ success = self.cleanup_process_tree(process, timeout)
564
+
565
+ if success:
566
+ with self._lock:
567
+ # Remove from background processes tracking
568
+ self.background_processes.pop(pid, None)
569
+
570
+ # Optionally clean up output files
571
+ # For now, we keep them for debugging/audit purposes
572
+ # self._cleanup_output_files(pid)
573
+
574
+ return success
575
+
576
+ def _cleanup_output_files(self, pid: int) -> None:
577
+ """Clean up output files for a background process."""
578
+ # Prefer the background process working directory when available
579
+ with self._lock:
580
+ info = self.background_processes.get(pid)
581
+ if not info:
582
+ return
583
+
584
+ cwd = info.get('cwd')
585
+ process_uniq_id = info.get('process_uniq_id')
586
+
587
+ base_dir = cwd if isinstance(cwd, str) and len(cwd) > 0 else os.getcwd()
588
+ backgrounds_dir = os.path.join(base_dir, '.auto-coder', 'backgrounds')
589
+
590
+ # Use process_uniq_id if available, fallback to pid
591
+ file_prefix = process_uniq_id if process_uniq_id else str(pid)
592
+ stdout_file = os.path.join(backgrounds_dir, f"{file_prefix}.out")
593
+ stderr_file = os.path.join(backgrounds_dir, f"{file_prefix}.err")
594
+
595
+ for filepath in [stdout_file, stderr_file]:
596
+ try:
597
+ if os.path.exists(filepath):
598
+ os.remove(filepath)
599
+ logger.debug(f"Removed output file: {filepath}")
600
+ except Exception as e:
601
+ logger.warning(f"Failed to remove output file {filepath}: {e}")
602
+
603
+ def get_background_process_count(self) -> int:
604
+ """Get the number of active background processes."""
605
+ with self._lock:
606
+ return len(self.background_processes)