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,744 @@
1
+ """
2
+ Interactive process wrapper for shell command execution.
3
+
4
+ This module provides interactive process functionality including:
5
+ - Real-time input/output streaming
6
+ - Cross-platform PTY/pipe support
7
+ - Signal handling (Ctrl-C, etc.)
8
+ - Thread-safe operations
9
+ """
10
+
11
+ import os
12
+ import platform
13
+ import subprocess
14
+ import threading
15
+ import time
16
+ import queue
17
+ import select
18
+ import signal
19
+ from typing import Optional, Dict, Any, Generator, Union, List
20
+ from loguru import logger
21
+
22
+ from .exceptions import CommandExecutionError, ProcessCleanupError
23
+ from .process_cleanup import cleanup_process_tree
24
+
25
+ # Platform-specific imports
26
+ PLATFORM = platform.system()
27
+ PTY_AVAILABLE = False
28
+ WINPTY_AVAILABLE = False
29
+
30
+ try:
31
+ if PLATFORM != "Windows":
32
+ import pty
33
+ import termios
34
+ import fcntl
35
+ PTY_AVAILABLE = True
36
+ except ImportError:
37
+ logger.debug("PTY not available on this platform")
38
+
39
+ try:
40
+ if PLATFORM == "Windows":
41
+ import winpty # type: ignore
42
+ WINPTY_AVAILABLE = True
43
+ except ImportError:
44
+ logger.debug("winpty not available, falling back to pipes")
45
+
46
+
47
+ class InteractiveProcess:
48
+ """
49
+ Interactive process wrapper for real-time command execution.
50
+
51
+ This class provides a convenient interface for executing commands that
52
+ require interactive input/output, supporting both PTY and pipe modes
53
+ across different platforms.
54
+ """
55
+
56
+ def __init__(
57
+ self,
58
+ command: Union[str, List[str]],
59
+ cwd: Optional[str] = None,
60
+ env: Optional[Dict[str, str]] = None,
61
+ use_pty: Optional[bool] = None,
62
+ shell: bool = True,
63
+ encoding: str = 'utf-8',
64
+ **kwargs
65
+ ):
66
+ """
67
+ Initialize interactive process.
68
+
69
+ Args:
70
+ command: Command to execute
71
+ cwd: Working directory
72
+ env: Environment variables
73
+ use_pty: Whether to use PTY (auto-detect if None)
74
+ shell: Whether to use shell
75
+ encoding: Text encoding
76
+ **kwargs: Additional subprocess arguments
77
+ """
78
+ self.command = command
79
+ self.cwd = cwd
80
+ self.env = env
81
+ self.encoding = encoding
82
+ self.shell = shell
83
+
84
+ # Determine if we should use PTY
85
+ if use_pty is None:
86
+ self.use_pty = PTY_AVAILABLE and PLATFORM != "Windows"
87
+ else:
88
+ self.use_pty = use_pty and PTY_AVAILABLE
89
+
90
+ # State management
91
+ self.process: Optional[subprocess.Popen] = None
92
+ self.master_fd: Optional[int] = None
93
+ self.slave_fd: Optional[int] = None
94
+ self.winpty_process = None
95
+
96
+ # Threading
97
+ self.output_queue: queue.Queue = queue.Queue()
98
+ self.error_queue: queue.Queue = queue.Queue()
99
+ self.io_thread: Optional[threading.Thread] = None
100
+ self.alive_event = threading.Event()
101
+ self.started_event = threading.Event()
102
+
103
+ # Monitoring
104
+ self.start_time: Optional[float] = None
105
+ self.end_time: Optional[float] = None
106
+ self.bytes_written = 0
107
+ self.bytes_read = 0
108
+
109
+ logger.debug(f"InteractiveProcess initialized: use_pty={self.use_pty}, platform={PLATFORM}")
110
+
111
+ def start(self) -> None:
112
+ """Start the interactive process."""
113
+ if self.is_alive():
114
+ raise CommandExecutionError("Process is already running")
115
+
116
+ self.start_time = time.time()
117
+ self.alive_event.set()
118
+
119
+ try:
120
+ if self.use_pty:
121
+ self._start_with_pty()
122
+ elif PLATFORM == "Windows" and WINPTY_AVAILABLE:
123
+ self._start_with_winpty()
124
+ else:
125
+ self._start_with_pipes()
126
+
127
+ # Start I/O thread
128
+ self.io_thread = threading.Thread(target=self._io_worker, daemon=True)
129
+ self.io_thread.start()
130
+
131
+ # Give process a moment to start and check if it's valid
132
+ time.sleep(0.1)
133
+
134
+ # Check if process failed immediately (e.g., command not found)
135
+ if not self.is_alive():
136
+ exit_code = self.exit_code
137
+ if exit_code is not None and exit_code != 0:
138
+ self._cleanup()
139
+ raise CommandExecutionError(f"Interactive process failed to start: exit code {exit_code}")
140
+
141
+ self.started_event.set()
142
+ logger.info(f"Interactive process started: PID {self.pid}")
143
+
144
+ except Exception as e:
145
+ self.alive_event.clear()
146
+ self._cleanup()
147
+ raise CommandExecutionError(f"Failed to start interactive process: {e}")
148
+
149
+ def _start_with_pty(self) -> None:
150
+ """Start process with PTY (Linux/Mac)."""
151
+ if not PTY_AVAILABLE:
152
+ raise CommandExecutionError("PTY not available on this platform")
153
+
154
+ # Create PTY
155
+ self.master_fd, self.slave_fd = pty.openpty()
156
+
157
+ # Configure PTY
158
+ try:
159
+ # Get current terminal settings
160
+ attrs = termios.tcgetattr(self.slave_fd)
161
+
162
+ # Disable echo to avoid duplicate output
163
+ attrs[3] = attrs[3] & ~termios.ECHO
164
+
165
+ # Set raw mode for better control
166
+ attrs[3] = attrs[3] & ~(termios.ICANON | termios.ISIG)
167
+
168
+ termios.tcsetattr(self.slave_fd, termios.TCSANOW, attrs)
169
+
170
+ # Make master non-blocking
171
+ fcntl.fcntl(self.master_fd, fcntl.F_SETFL, os.O_NONBLOCK)
172
+
173
+ except Exception as e:
174
+ logger.warning(f"Failed to configure PTY: {e}")
175
+
176
+ # Start process with PTY
177
+ try:
178
+ self.process = subprocess.Popen(
179
+ self.command,
180
+ stdin=self.slave_fd,
181
+ stdout=self.slave_fd,
182
+ stderr=self.slave_fd,
183
+ cwd=self.cwd,
184
+ env=self.env,
185
+ shell=self.shell,
186
+ start_new_session=True # This automatically calls setsid(), so no need for preexec_fn
187
+ )
188
+ except Exception as e:
189
+ # If process creation fails, clean up PTY fds
190
+ if self.master_fd is not None:
191
+ os.close(self.master_fd)
192
+ self.master_fd = None
193
+ if self.slave_fd is not None:
194
+ os.close(self.slave_fd)
195
+ self.slave_fd = None
196
+ raise CommandExecutionError(f"Failed to start PTY process: {e}")
197
+
198
+ def _start_with_winpty(self) -> None:
199
+ """Start process with winpty (Windows)."""
200
+ if not WINPTY_AVAILABLE:
201
+ raise CommandExecutionError("winpty not available")
202
+
203
+ try:
204
+ # Convert command to string if needed
205
+ if isinstance(self.command, list):
206
+ cmd_str = ' '.join(self.command)
207
+ else:
208
+ cmd_str = self.command
209
+
210
+ # Create winpty process
211
+ self.winpty_process = winpty.PtyProcess.spawn(
212
+ cmd_str,
213
+ cwd=self.cwd,
214
+ env=self.env
215
+ )
216
+
217
+ # Create a subprocess wrapper for compatibility
218
+ winpty_proc = self.winpty_process # Capture for closure
219
+ self.process = type('MockProcess', (), {
220
+ 'pid': winpty_proc.pid,
221
+ 'poll': lambda: winpty_proc.exitstatus if not winpty_proc.isalive() else None,
222
+ 'wait': lambda timeout=None: winpty_proc.wait(),
223
+ 'terminate': lambda: winpty_proc.terminate(),
224
+ 'kill': lambda: winpty_proc.terminate(force=True),
225
+ 'returncode': property(lambda s: winpty_proc.exitstatus)
226
+ })() # type: ignore
227
+
228
+ except Exception as e:
229
+ raise CommandExecutionError(f"Failed to start winpty process: {e}")
230
+
231
+ def _start_with_pipes(self) -> None:
232
+ """Start process with regular pipes."""
233
+ self.process = subprocess.Popen(
234
+ self.command,
235
+ stdin=subprocess.PIPE,
236
+ stdout=subprocess.PIPE,
237
+ stderr=subprocess.PIPE,
238
+ cwd=self.cwd,
239
+ env=self.env,
240
+ shell=self.shell,
241
+ text=True,
242
+ encoding=self.encoding,
243
+ bufsize=0, # Unbuffered
244
+ universal_newlines=True
245
+ )
246
+
247
+ # Make stdout/stderr non-blocking on Unix
248
+ if PLATFORM != "Windows":
249
+ try:
250
+ import fcntl
251
+ if self.process.stdout:
252
+ fcntl.fcntl(self.process.stdout.fileno(), fcntl.F_SETFL, os.O_NONBLOCK)
253
+ if self.process.stderr:
254
+ fcntl.fcntl(self.process.stderr.fileno(), fcntl.F_SETFL, os.O_NONBLOCK)
255
+ except Exception as e:
256
+ logger.warning(f"Failed to set non-blocking I/O: {e}")
257
+
258
+ def _io_worker(self) -> None:
259
+ """I/O worker thread for handling input/output."""
260
+ try:
261
+ if self.use_pty:
262
+ self._pty_io_worker()
263
+ elif self.winpty_process:
264
+ self._winpty_io_worker()
265
+ else:
266
+ self._pipe_io_worker()
267
+ except Exception as e:
268
+ logger.error(f"I/O worker error: {e}")
269
+ finally:
270
+ self.alive_event.clear()
271
+
272
+ def _pty_io_worker(self) -> None:
273
+ """I/O worker for PTY mode."""
274
+ while self.alive_event.is_set():
275
+ try:
276
+ # Check if process is still alive
277
+ if self.process and self.process.poll() is not None:
278
+ self.alive_event.clear()
279
+ break
280
+
281
+ # Use select to check for available data
282
+ ready, _, _ = select.select([self.master_fd], [], [], 0.1)
283
+
284
+ if ready and self.master_fd is not None:
285
+ try:
286
+ data = os.read(self.master_fd, 1024)
287
+ if data:
288
+ text = data.decode(self.encoding, errors='replace')
289
+ self.bytes_read += len(data)
290
+ self.output_queue.put(text)
291
+ else:
292
+ # EOF
293
+ self.alive_event.clear()
294
+ break
295
+ except OSError as e:
296
+ if e.errno in (11, 35): # EAGAIN, EWOULDBLOCK
297
+ continue
298
+ else:
299
+ logger.debug(f"PTY read error: {e}")
300
+ self.alive_event.clear()
301
+ break
302
+
303
+ except Exception as e:
304
+ logger.debug(f"PTY I/O error: {e}")
305
+ break
306
+
307
+ def _winpty_io_worker(self) -> None:
308
+ """I/O worker for winpty mode."""
309
+ while self.alive_event.is_set():
310
+ try:
311
+ # Check if process is still alive
312
+ if not self.winpty_process or not self.winpty_process.isalive():
313
+ self.alive_event.clear()
314
+ break
315
+
316
+ # Read output
317
+ try:
318
+ if self.winpty_process:
319
+ data = self.winpty_process.read(timeout=100) # 100ms timeout
320
+ if data:
321
+ self.bytes_read += len(data.encode(self.encoding))
322
+ self.output_queue.put(data)
323
+ except Exception:
324
+ # Timeout or no data available
325
+ continue
326
+
327
+ except Exception as e:
328
+ logger.debug(f"Winpty I/O error: {e}")
329
+ break
330
+
331
+ def _pipe_io_worker(self) -> None:
332
+ """I/O worker for pipe mode."""
333
+ while self.alive_event.is_set():
334
+ try:
335
+ # Check if process is still alive
336
+ if self.process and self.process.poll() is not None:
337
+ # Read any remaining output
338
+ self._read_remaining_output()
339
+ self.alive_event.clear()
340
+ break
341
+
342
+ # Read stdout
343
+ if PLATFORM == "Windows":
344
+ # Windows doesn't support select on pipes
345
+ self._read_windows_output()
346
+ else:
347
+ # Unix-like systems can use select
348
+ self._read_unix_output()
349
+
350
+ except Exception as e:
351
+ logger.debug(f"Pipe I/O error: {e}")
352
+ break
353
+
354
+ def _read_windows_output(self) -> None:
355
+ """Read output on Windows (no select support)."""
356
+ if not self.process:
357
+ return
358
+
359
+ # Use peek to check if data is available without blocking
360
+ try:
361
+ import msvcrt
362
+ import sys
363
+
364
+ # This is a simplified approach - in production you might want
365
+ # to use threading for each pipe or polling
366
+ if self.process.stdout:
367
+ # Try to read with a very short timeout
368
+ line = self.process.stdout.readline()
369
+ if line:
370
+ self.bytes_read += len(line.encode(self.encoding))
371
+ self.output_queue.put(line)
372
+
373
+ if self.process.stderr:
374
+ line = self.process.stderr.readline()
375
+ if line:
376
+ self.bytes_read += len(line.encode(self.encoding))
377
+ self.error_queue.put(line)
378
+
379
+ except Exception as e:
380
+ logger.debug(f"Windows output read error: {e}")
381
+
382
+ def _read_unix_output(self) -> None:
383
+ """Read output on Unix-like systems."""
384
+ if not self.process:
385
+ return
386
+
387
+ streams = []
388
+ if self.process.stdout:
389
+ streams.append(self.process.stdout)
390
+ if self.process.stderr:
391
+ streams.append(self.process.stderr)
392
+
393
+ if not streams:
394
+ time.sleep(0.1)
395
+ return
396
+
397
+ try:
398
+ ready, _, _ = select.select(streams, [], [], 0.1)
399
+
400
+ for stream in ready:
401
+ try:
402
+ if stream == self.process.stdout:
403
+ data = stream.read(1024)
404
+ if data:
405
+ self.bytes_read += len(data.encode(self.encoding))
406
+ self.output_queue.put(data)
407
+ elif stream == self.process.stderr:
408
+ data = stream.read(1024)
409
+ if data:
410
+ self.bytes_read += len(data.encode(self.encoding))
411
+ self.error_queue.put(data)
412
+ except Exception as e:
413
+ logger.debug(f"Stream read error: {e}")
414
+
415
+ except Exception as e:
416
+ logger.debug(f"Select error: {e}")
417
+
418
+ def _read_remaining_output(self) -> None:
419
+ """Read any remaining output from terminated process."""
420
+ if not self.process:
421
+ return
422
+
423
+ try:
424
+ if self.process.stdout:
425
+ remaining = self.process.stdout.read()
426
+ if remaining:
427
+ self.bytes_read += len(remaining.encode(self.encoding))
428
+ self.output_queue.put(remaining)
429
+
430
+ if self.process.stderr:
431
+ remaining = self.process.stderr.read()
432
+ if remaining:
433
+ self.bytes_read += len(remaining.encode(self.encoding))
434
+ self.error_queue.put(remaining)
435
+
436
+ except Exception as e:
437
+ logger.debug(f"Error reading remaining output: {e}")
438
+
439
+ def write(self, data: str) -> None:
440
+ """
441
+ Write data to process stdin.
442
+
443
+ Args:
444
+ data: Data to write
445
+
446
+ Raises:
447
+ CommandExecutionError: If process is not running or write fails
448
+ """
449
+ if not self.is_alive():
450
+ raise CommandExecutionError("Process is not running")
451
+
452
+ if not self.started_event.is_set():
453
+ raise CommandExecutionError("Process has not started yet")
454
+
455
+ try:
456
+ if self.use_pty and self.master_fd is not None:
457
+ # Write to PTY master
458
+ encoded_data = data.encode(self.encoding)
459
+ os.write(self.master_fd, encoded_data)
460
+ self.bytes_written += len(encoded_data)
461
+
462
+ elif self.winpty_process:
463
+ # Write to winpty
464
+ self.winpty_process.write(data)
465
+ self.bytes_written += len(data.encode(self.encoding))
466
+
467
+ elif self.process and self.process.stdin:
468
+ # Write to pipe
469
+ self.process.stdin.write(data)
470
+ self.process.stdin.flush()
471
+ self.bytes_written += len(data.encode(self.encoding))
472
+
473
+ else:
474
+ raise CommandExecutionError("No input stream available")
475
+
476
+ except Exception as e:
477
+ raise CommandExecutionError(f"Failed to write to process: {e}")
478
+
479
+ def read_output(self, timeout: Optional[float] = None) -> Optional[str]:
480
+ """
481
+ Read output from process.
482
+
483
+ Args:
484
+ timeout: Timeout in seconds (None for non-blocking)
485
+
486
+ Returns:
487
+ Output string or None if no data available
488
+ """
489
+ try:
490
+ if timeout is None:
491
+ return self.output_queue.get_nowait()
492
+ else:
493
+ return self.output_queue.get(timeout=timeout)
494
+ except queue.Empty:
495
+ return None
496
+
497
+ def read_error(self, timeout: Optional[float] = None) -> Optional[str]:
498
+ """
499
+ Read error output from process.
500
+
501
+ Args:
502
+ timeout: Timeout in seconds (None for non-blocking)
503
+
504
+ Returns:
505
+ Error string or None if no data available
506
+ """
507
+ try:
508
+ if timeout is None:
509
+ return self.error_queue.get_nowait()
510
+ else:
511
+ return self.error_queue.get(timeout=timeout)
512
+ except queue.Empty:
513
+ return None
514
+
515
+ def read_lines(self, timeout: Optional[float] = 1.0) -> Generator[str, None, None]:
516
+ """
517
+ Generator that yields output lines.
518
+
519
+ Args:
520
+ timeout: Timeout for each read operation
521
+
522
+ Yields:
523
+ Output lines
524
+ """
525
+ buffer = ""
526
+
527
+ while self.is_alive():
528
+ try:
529
+ data = self.read_output(timeout=timeout)
530
+ if data:
531
+ buffer += data
532
+
533
+ # Yield complete lines
534
+ while '\n' in buffer:
535
+ line, buffer = buffer.split('\n', 1)
536
+ yield line + '\n'
537
+ else:
538
+ # No data available, check if process is still alive
539
+ if not self.is_alive():
540
+ break
541
+
542
+ except Exception as e:
543
+ logger.debug(f"Error reading lines: {e}")
544
+ break
545
+
546
+ # Yield any remaining buffer content
547
+ if buffer:
548
+ yield buffer
549
+
550
+ def send_signal(self, sig: int) -> None:
551
+ """
552
+ Send signal to process.
553
+
554
+ Args:
555
+ sig: Signal number
556
+ """
557
+ if not self.is_alive():
558
+ return
559
+
560
+ try:
561
+ if PLATFORM == "Windows":
562
+ if sig == signal.SIGINT:
563
+ # Send Ctrl+C to Windows process
564
+ if self.winpty_process:
565
+ # winpty handles signals differently
566
+ self.winpty_process.terminate()
567
+ elif self.process:
568
+ self.process.send_signal(signal.CTRL_C_EVENT)
569
+ else:
570
+ if self.process:
571
+ self.process.terminate()
572
+ else:
573
+ # Unix-like systems
574
+ if self.process:
575
+ try:
576
+ # Try to send signal to process group first
577
+ os.killpg(self.process.pid, sig)
578
+ except (OSError, ProcessLookupError):
579
+ # Fall back to sending signal to process only
580
+ self.process.send_signal(sig)
581
+
582
+ except Exception as e:
583
+ logger.debug(f"Error sending signal {sig}: {e}")
584
+
585
+ def terminate(self, grace_timeout: float = 5.0) -> bool:
586
+ """
587
+ Terminate the process gracefully.
588
+
589
+ Args:
590
+ grace_timeout: Time to wait for graceful termination
591
+
592
+ Returns:
593
+ True if terminated successfully
594
+ """
595
+ if not self.is_alive():
596
+ return True
597
+
598
+ logger.debug(f"Terminating interactive process PID {self.pid}")
599
+
600
+ try:
601
+ # Send SIGTERM (or equivalent)
602
+ if PLATFORM == "Windows":
603
+ if self.winpty_process:
604
+ self.winpty_process.terminate()
605
+ elif self.process:
606
+ self.process.terminate()
607
+ else:
608
+ self.send_signal(signal.SIGTERM)
609
+
610
+ # Wait for graceful termination
611
+ start_time = time.time()
612
+ while time.time() - start_time < grace_timeout:
613
+ if not self.is_alive():
614
+ break
615
+ time.sleep(0.1)
616
+
617
+ # Force kill if still alive
618
+ if self.is_alive():
619
+ logger.debug(f"Force killing process PID {self.pid}")
620
+ if self.winpty_process:
621
+ self.winpty_process.terminate(force=True)
622
+ elif self.process:
623
+ if PLATFORM == "Windows":
624
+ self.process.kill()
625
+ else:
626
+ # Use process cleanup for thorough cleanup
627
+ cleanup_process_tree(self.process.pid)
628
+
629
+ # Wait for I/O thread to finish
630
+ self.alive_event.clear()
631
+ if self.io_thread and self.io_thread.is_alive():
632
+ self.io_thread.join(timeout=2.0)
633
+
634
+ self._cleanup()
635
+ self.end_time = time.time()
636
+
637
+ return not self.is_alive()
638
+
639
+ except Exception as e:
640
+ logger.error(f"Error terminating process: {e}")
641
+ return False
642
+
643
+ def _cleanup(self) -> None:
644
+ """Clean up resources."""
645
+ try:
646
+ # Close PTY file descriptors
647
+ if self.master_fd is not None:
648
+ os.close(self.master_fd)
649
+ self.master_fd = None
650
+
651
+ if self.slave_fd is not None:
652
+ os.close(self.slave_fd)
653
+ self.slave_fd = None
654
+
655
+ # Close winpty
656
+ if self.winpty_process:
657
+ try:
658
+ self.winpty_process.terminate()
659
+ except:
660
+ pass
661
+ self.winpty_process = None
662
+
663
+ # Close pipes
664
+ if self.process:
665
+ try:
666
+ if self.process.stdin:
667
+ self.process.stdin.close()
668
+ if self.process.stdout:
669
+ self.process.stdout.close()
670
+ if self.process.stderr:
671
+ self.process.stderr.close()
672
+ except:
673
+ pass
674
+
675
+ except Exception as e:
676
+ logger.debug(f"Cleanup error: {e}")
677
+
678
+ def is_alive(self) -> bool:
679
+ """Check if process is alive."""
680
+ if self.winpty_process:
681
+ return self.winpty_process.isalive()
682
+ elif self.process:
683
+ return self.process.poll() is None
684
+ else:
685
+ return False
686
+
687
+ @property
688
+ def pid(self) -> Optional[int]:
689
+ """Get process PID."""
690
+ if self.winpty_process:
691
+ return self.winpty_process.pid
692
+ elif self.process:
693
+ return self.process.pid
694
+ else:
695
+ return None
696
+
697
+ @property
698
+ def exit_code(self) -> Optional[int]:
699
+ """Get process exit code."""
700
+ if self.winpty_process:
701
+ return self.winpty_process.exitstatus if not self.winpty_process.isalive() else None
702
+ elif self.process:
703
+ return self.process.returncode
704
+ else:
705
+ return None
706
+
707
+ @property
708
+ def duration(self) -> Optional[float]:
709
+ """Get process duration."""
710
+ if self.start_time is None:
711
+ return None
712
+
713
+ end_time = self.end_time or time.time()
714
+ return end_time - self.start_time
715
+
716
+ def get_stats(self) -> Dict[str, Any]:
717
+ """Get process statistics."""
718
+ return {
719
+ 'pid': self.pid,
720
+ 'exit_code': self.exit_code,
721
+ 'duration': self.duration,
722
+ 'bytes_written': self.bytes_written,
723
+ 'bytes_read': self.bytes_read,
724
+ 'is_alive': self.is_alive(),
725
+ 'use_pty': self.use_pty,
726
+ 'platform': PLATFORM
727
+ }
728
+
729
+ def __enter__(self) -> 'InteractiveProcess':
730
+ """Context manager entry."""
731
+ self.start()
732
+ return self
733
+
734
+ def __exit__(self, exc_type, exc_val, exc_tb) -> None:
735
+ """Context manager exit."""
736
+ self.terminate()
737
+
738
+ def __del__(self) -> None:
739
+ """Destructor - cleanup resources."""
740
+ try:
741
+ if self.is_alive():
742
+ self.terminate(grace_timeout=1.0)
743
+ except Exception:
744
+ pass