auto-coder 1.0.0__py3-none-any.whl → 2.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


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

Files changed (574) hide show
  1. auto_coder-2.0.0.dist-info/LICENSE +158 -0
  2. auto_coder-2.0.0.dist-info/METADATA +558 -0
  3. auto_coder-2.0.0.dist-info/RECORD +795 -0
  4. {auto_coder-1.0.0.dist-info → auto_coder-2.0.0.dist-info}/WHEEL +1 -1
  5. {auto_coder-1.0.0.dist-info → auto_coder-2.0.0.dist-info}/entry_points.txt +3 -3
  6. autocoder/__init__.py +31 -0
  7. autocoder/agent/auto_filegroup.py +32 -13
  8. autocoder/agent/auto_learn_from_commit.py +9 -1
  9. autocoder/agent/base_agentic/__init__.py +3 -0
  10. autocoder/agent/base_agentic/agent_hub.py +1 -1
  11. autocoder/agent/base_agentic/base_agent.py +235 -136
  12. autocoder/agent/base_agentic/default_tools.py +119 -118
  13. autocoder/agent/base_agentic/test_base_agent.py +1 -1
  14. autocoder/agent/base_agentic/tool_registry.py +32 -20
  15. autocoder/agent/base_agentic/tools/read_file_tool_resolver.py +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 +73 -59
  19. autocoder/auto_coder.py +31 -40
  20. autocoder/auto_coder_rag.py +11 -1084
  21. autocoder/auto_coder_runner.py +970 -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 +401 -0
  112. autocoder/common/file_handler/commit_handler.py +200 -0
  113. autocoder/common/file_handler/lib_handler.py +156 -0
  114. autocoder/common/file_handler/list_files_handler.py +111 -0
  115. autocoder/common/file_handler/mcp_handler.py +268 -0
  116. autocoder/common/file_handler/models_handler.py +493 -0
  117. autocoder/common/file_handler/remove_files_handler.py +172 -0
  118. autocoder/common/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 +288 -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 +349 -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 +1051 -0
  398. autocoder/default_project/__init__.py +501 -0
  399. autocoder/dispacher/__init__.py +4 -12
  400. autocoder/dispacher/actions/action.py +165 -7
  401. autocoder/dispacher/actions/plugins/action_regex_project.py +2 -2
  402. autocoder/index/entry.py +116 -124
  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 +932 -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 +489 -0
  536. autocoder/workflow_agents/loader.py +737 -0
  537. autocoder/workflow_agents/runner.py +267 -0
  538. autocoder/workflow_agents/types.py +172 -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.0.dist-info}/top_level.txt +0 -0
  574. /autocoder/{sdk/example.py → common/agent_query_queue/__init__.py} +0 -0
@@ -0,0 +1,217 @@
1
+ """
2
+ Hook executor for command execution with variable substitution and process management.
3
+ """
4
+
5
+ import asyncio
6
+ import json
7
+ import os
8
+ import re
9
+ import shlex
10
+ import subprocess
11
+ import time
12
+ from typing import Dict, List, Optional, Any
13
+
14
+ from loguru import logger
15
+ from autocoder.common.async_utils import AsyncSyncMixin
16
+ from ..agent_events.types import EventMessage
17
+ from .types import Hook, HookExecutionResult, HookType
18
+
19
+
20
+ class HookExecutor(AsyncSyncMixin):
21
+ """
22
+ Command execution engine with variable substitution and environment control.
23
+
24
+ Provides both async and sync methods for all operations.
25
+ Sync methods are automatically generated with a '_sync' suffix.
26
+ """
27
+
28
+ def __init__(self, cwd: Optional[str] = None, timeout: int = 30000,
29
+ env: Optional[Dict[str, str]] = None):
30
+ """
31
+ Initialize the hook executor.
32
+
33
+ Args:
34
+ cwd: Working directory for command execution
35
+ timeout: Command timeout in milliseconds
36
+ env: Additional environment variables
37
+ """
38
+ self.cwd = cwd or os.getcwd()
39
+ self.timeout = timeout / 1000.0 # Convert to seconds
40
+ self.env = env or {}
41
+
42
+ def set_cwd(self, cwd: str) -> None:
43
+ """Set working directory for command execution."""
44
+ self.cwd = cwd
45
+
46
+ def set_timeout(self, timeout: int) -> None:
47
+ """Set command execution timeout in milliseconds."""
48
+ self.timeout = timeout / 1000.0
49
+
50
+ def set_env(self, env: Dict[str, str]) -> None:
51
+ """Set environment variables."""
52
+ self.env = env
53
+
54
+ async def execute_hooks(self, hooks: List[Hook], event_message: EventMessage) -> List[HookExecutionResult]:
55
+ """
56
+ Execute all hooks in an array.
57
+
58
+ Args:
59
+ hooks: List of hooks to execute
60
+ event_message: Event message for variable substitution
61
+
62
+ Returns:
63
+ List of execution results
64
+ """
65
+ results = []
66
+ for hook in hooks:
67
+ result = await self.execute_hook(hook, event_message)
68
+ results.append(result)
69
+ return results
70
+
71
+ async def execute_hook(self, hook: Hook, event_message: EventMessage) -> HookExecutionResult:
72
+ """
73
+ Execute a single hook.
74
+
75
+ Args:
76
+ hook: Hook to execute
77
+ event_message: Event message for variable substitution
78
+
79
+ Returns:
80
+ Execution result
81
+ """
82
+ if hook.type != HookType.COMMAND:
83
+ return HookExecutionResult(
84
+ success=False,
85
+ command=hook.command,
86
+ error_message=f"Unsupported hook type: {hook.type}"
87
+ )
88
+
89
+ # Process command variables
90
+ processed_command = self._substitute_variables(hook.command, event_message)
91
+
92
+ # Execute command
93
+ return await self._execute_command(processed_command)
94
+
95
+ def _substitute_variables(self, command: str, event_message: EventMessage) -> str:
96
+ """
97
+ Replace template variables in commands with runtime values.
98
+
99
+ Supported variables:
100
+ - {{event_type}}: The event type
101
+ - {{event_id}}: The event ID
102
+ - {{timestamp}}: The event timestamp
103
+ - {{tool_name}}: The tool name from event content
104
+ - {{event_content}}: Full event content as JSON
105
+ - {{agent_id}}: Agent ID from context (if available)
106
+ - {{conversation_id}}: Conversation ID from context (if available)
107
+ - {{context_*}}: Context metadata fields
108
+ - {{tool_*}}: Tool input fields
109
+
110
+ Args:
111
+ command: Command template with {{variable}} placeholders
112
+ event_message: Event message containing variable values
113
+
114
+ Returns:
115
+ Command with variables substituted
116
+ """
117
+ variables = {
118
+ 'event_type': event_message.event_type.value,
119
+ 'event_id': event_message.event_id,
120
+ 'timestamp': str(event_message.timestamp),
121
+ 'tool_name': event_message.content.get('tool_name', ''),
122
+ 'event_content': json.dumps(event_message.content),
123
+ 'cwd': self.cwd
124
+ }
125
+
126
+ # Add context variables if available
127
+ if event_message.context:
128
+ if event_message.context.agent_id:
129
+ variables['agent_id'] = event_message.context.agent_id
130
+ if event_message.context.conversation_id:
131
+ variables['conversation_id'] = event_message.context.conversation_id
132
+ if event_message.context.metadata:
133
+ for key, value in event_message.context.metadata.items():
134
+ variables[f'context_{key}'] = str(value)
135
+
136
+ # Add tool-specific variables
137
+ if 'tool_input' in event_message.content:
138
+ tool_input = event_message.content['tool_input']
139
+ if isinstance(tool_input, dict):
140
+ for key, value in tool_input.items():
141
+ variables[f'tool_{key}'] = str(value)
142
+
143
+ # Add additional content fields
144
+ for key, value in event_message.content.items():
145
+ if key not in ['tool_name', 'tool_input'] and isinstance(value, (str, int, float, bool)):
146
+ variables[key] = str(value)
147
+
148
+ # Substitute variables using regex
149
+ def replace_var(match):
150
+ var_name = match.group(1)
151
+ return variables.get(var_name, match.group(0))
152
+
153
+ # Replace {{variable}} patterns
154
+ result = re.sub(r'\{\{(\w+)\}\}', replace_var, command)
155
+ return result
156
+
157
+ async def _execute_command(self, command: str) -> HookExecutionResult:
158
+ """
159
+ Execute shell command with timeout and error handling.
160
+
161
+ Args:
162
+ command: Command to execute
163
+
164
+ Returns:
165
+ Execution result with output, exit code, and timing
166
+ """
167
+ start_time = time.time()
168
+ result = HookExecutionResult(
169
+ success=False,
170
+ command=command,
171
+ start_time=start_time
172
+ )
173
+
174
+ try:
175
+ # Prepare environment
176
+ exec_env = os.environ.copy()
177
+ exec_env.update(self.env)
178
+
179
+ # Execute command
180
+ process = await asyncio.create_subprocess_shell(
181
+ command,
182
+ stdout=asyncio.subprocess.PIPE,
183
+ stderr=asyncio.subprocess.PIPE,
184
+ cwd=self.cwd,
185
+ env=exec_env
186
+ )
187
+
188
+ try:
189
+ stdout, stderr = await asyncio.wait_for(
190
+ process.communicate(),
191
+ timeout=self.timeout
192
+ )
193
+
194
+ # Update result
195
+ result.stdout = stdout.decode('utf-8', errors='replace')
196
+ result.stderr = stderr.decode('utf-8', errors='replace')
197
+ result.exit_code = process.returncode
198
+ result.success = process.returncode == 0
199
+
200
+ except asyncio.TimeoutError:
201
+ # Kill the process if it times out
202
+ process.kill()
203
+ await process.wait()
204
+ result.error_message = f"Command timed out after {self.timeout}s"
205
+ result.exit_code = -1
206
+
207
+ except Exception as e:
208
+ result.error_message = str(e)
209
+ result.exit_code = -1
210
+ logger.error(f"Error executing command '{command}': {e}")
211
+
212
+ finally:
213
+ end_time = time.time()
214
+ result.end_time = end_time
215
+ result.execution_time = end_time - start_time
216
+
217
+ return result
@@ -0,0 +1,288 @@
1
+ """
2
+ Hook manager for configuration loading, caching, and event processing.
3
+ """
4
+
5
+ import asyncio
6
+ import json
7
+ import os
8
+ import re
9
+ import time
10
+ import yaml
11
+ from typing import Dict, List, Optional, Tuple
12
+
13
+ from loguru import logger
14
+ from autocoder.common.async_utils import AsyncSyncMixin
15
+ from ..agent_events.types import EventMessage
16
+ from .types import HooksConfig, HookMatcher, HookProcessingResult
17
+ from .hook_executor import HookExecutor
18
+
19
+
20
+ class HookManager(AsyncSyncMixin):
21
+ """
22
+ Core hook management and event processing.
23
+
24
+ Provides both async and sync methods for all operations.
25
+ Sync methods are automatically generated with a '_sync' suffix.
26
+ """
27
+
28
+ def __init__(self, config_path: Optional[str] = None, cwd: Optional[str] = None,
29
+ command_timeout: int = 30000):
30
+ """
31
+ Initialize the hook manager.
32
+
33
+ Args:
34
+ config_path: Path to hooks configuration file (supports .json, .yaml, .yml formats)
35
+ cwd: Working directory for command execution
36
+ command_timeout: Command timeout in milliseconds
37
+ """
38
+ self.cwd = cwd or os.getcwd()
39
+ self.config_path = config_path or self._find_default_config_path()
40
+ self.command_timeout = command_timeout
41
+
42
+ self._config: Optional[HooksConfig] = None
43
+ self._config_mtime: Optional[float] = None
44
+ self._executor = HookExecutor(cwd=self.cwd, timeout=command_timeout)
45
+ self._config_lock = asyncio.Lock()
46
+
47
+ def _find_default_config_path(self) -> str:
48
+ """
49
+ Find the default configuration file by checking for hooks.json, hooks.yaml, hooks.yml.
50
+
51
+ Returns:
52
+ Path to the first found configuration file, defaults to hooks.json if none found
53
+ """
54
+ base_dir = os.path.join(self.cwd, '.auto-coder')
55
+
56
+ # Priority order: .json first for backward compatibility, then .yaml, then .yml
57
+ candidates = [
58
+ os.path.join(base_dir, 'hooks.json'),
59
+ os.path.join(base_dir, 'hooks.yaml'),
60
+ os.path.join(base_dir, 'hooks.yml')
61
+ ]
62
+
63
+ for candidate in candidates:
64
+ if os.path.exists(candidate):
65
+ return candidate
66
+
67
+ # Default to hooks.json for backward compatibility
68
+ return candidates[0]
69
+
70
+ def _get_config_format(self, config_path: str) -> str:
71
+ """
72
+ Detect configuration file format based on file extension.
73
+
74
+ Args:
75
+ config_path: Path to configuration file
76
+
77
+ Returns:
78
+ File format: 'json', 'yaml', or 'unknown'
79
+ """
80
+ ext = os.path.splitext(config_path)[1].lower()
81
+ if ext == '.json':
82
+ return 'json'
83
+ elif ext in ['.yaml', '.yml']:
84
+ return 'yaml'
85
+ else:
86
+ return 'unknown'
87
+
88
+ def config_exists(self) -> bool:
89
+ """Check if configuration file exists."""
90
+ return os.path.exists(self.config_path)
91
+
92
+ async def load_config(self) -> Dict[str, any]:
93
+ """
94
+ Load and validate hooks configuration from file.
95
+ Supports both JSON and YAML formats.
96
+
97
+ Returns:
98
+ Dictionary with 'config' (HooksConfig or None) and 'errors' (list of error messages)
99
+ """
100
+ async with self._config_lock:
101
+ errors = []
102
+ config = None
103
+
104
+ try:
105
+ if not self.config_exists():
106
+ errors.append(f"Configuration file not found: {self.config_path}")
107
+ return {'config': None, 'errors': errors}
108
+
109
+ # Check if we need to reload based on file modification time
110
+ current_mtime = os.path.getmtime(self.config_path)
111
+ if self._config is not None and self._config_mtime == current_mtime:
112
+ return {'config': self._config, 'errors': []}
113
+
114
+ # Detect file format and load accordingly
115
+ config_format = self._get_config_format(self.config_path)
116
+
117
+ with open(self.config_path, 'r', encoding='utf-8') as f:
118
+ if config_format == 'json':
119
+ data = json.load(f)
120
+ elif config_format == 'yaml':
121
+ data = yaml.safe_load(f)
122
+ else:
123
+ errors.append(f"Unsupported configuration file format: {self.config_path}")
124
+ return {'config': None, 'errors': errors}
125
+
126
+ # Validate configuration structure
127
+ validation_errors = self._validate_config(data)
128
+ if validation_errors:
129
+ errors.extend(validation_errors)
130
+ return {'config': None, 'errors': errors}
131
+
132
+ # Create config object
133
+ config = HooksConfig.from_dict(data)
134
+
135
+ # Cache the configuration
136
+ self._config = config
137
+ self._config_mtime = current_mtime
138
+
139
+ logger.info(f"Loaded hooks configuration from {self.config_path} (format: {config_format})")
140
+
141
+ except json.JSONDecodeError as e:
142
+ errors.append(f"Invalid JSON in configuration file: {e}")
143
+ except yaml.YAMLError as e:
144
+ errors.append(f"Invalid YAML in configuration file: {e}")
145
+ except Exception as e:
146
+ errors.append(f"Error loading configuration: {e}")
147
+ logger.error(f"Failed to load hooks configuration: {e}")
148
+
149
+ return {'config': config, 'errors': errors}
150
+
151
+ def _validate_config(self, data: Dict) -> List[str]:
152
+ """Validate configuration structure."""
153
+ errors = []
154
+
155
+ if not isinstance(data, dict):
156
+ errors.append("Configuration must be a JSON object")
157
+ return errors
158
+
159
+ if 'hooks' not in data:
160
+ errors.append("Configuration must contain 'hooks' key")
161
+ return errors
162
+
163
+ hooks = data['hooks']
164
+ if not isinstance(hooks, dict):
165
+ errors.append("'hooks' must be an object")
166
+ return errors
167
+
168
+ # Validate each event type configuration
169
+ for event_type, matchers in hooks.items():
170
+ if not isinstance(matchers, list):
171
+ errors.append(f"Event type '{event_type}' must have a list of matchers")
172
+ continue
173
+
174
+ for i, matcher in enumerate(matchers):
175
+ if not isinstance(matcher, dict):
176
+ errors.append(f"Matcher {i} in '{event_type}' must be an object")
177
+ continue
178
+
179
+ if 'matcher' not in matcher:
180
+ errors.append(f"Matcher {i} in '{event_type}' must have 'matcher' field")
181
+
182
+ if 'hooks' not in matcher:
183
+ errors.append(f"Matcher {i} in '{event_type}' must have 'hooks' field")
184
+ continue
185
+
186
+ hooks_list = matcher['hooks']
187
+ if not isinstance(hooks_list, list):
188
+ errors.append(f"'hooks' in matcher {i} of '{event_type}' must be a list")
189
+ continue
190
+
191
+ for j, hook in enumerate(hooks_list):
192
+ if not isinstance(hook, dict):
193
+ errors.append(f"Hook {j} in matcher {i} of '{event_type}' must be an object")
194
+ continue
195
+
196
+ if 'type' not in hook:
197
+ errors.append(f"Hook {j} in matcher {i} of '{event_type}' must have 'type' field")
198
+
199
+ if 'command' not in hook:
200
+ errors.append(f"Hook {j} in matcher {i} of '{event_type}' must have 'command' field")
201
+
202
+ return errors
203
+
204
+ async def process_event(self, event_message: EventMessage) -> HookProcessingResult:
205
+ """
206
+ Process an EventMessage and execute matching hooks.
207
+
208
+ Args:
209
+ event_message: The event to process
210
+
211
+ Returns:
212
+ HookProcessingResult containing execution results and any errors
213
+ """
214
+ start_time = time.time()
215
+ result = HookProcessingResult(matched=False)
216
+
217
+ try:
218
+ # Load configuration
219
+ load_result = await self.load_config()
220
+ if load_result['errors']:
221
+ result.errors.extend(load_result['errors'])
222
+ return result
223
+
224
+ config = load_result['config']
225
+ if not config:
226
+ result.errors.append("No configuration available")
227
+ return result
228
+
229
+ # Extract event type and tool name
230
+ event_type = event_message.event_type.value
231
+ tool_name = event_message.content.get('tool_name', '')
232
+
233
+ # Find matching event type configuration
234
+ if event_type not in config.hooks:
235
+ logger.debug(f"No hooks configured for event type: {event_type}")
236
+ return result # No configuration for this event type
237
+
238
+ matchers = config.hooks[event_type]
239
+
240
+ # Process each matcher
241
+ for matcher in matchers:
242
+ try:
243
+ # Check if tool name matches the pattern
244
+ if re.search(matcher.matcher, tool_name):
245
+ result.matched = True
246
+ logger.info(f"Matched hook pattern '{matcher.matcher}' for tool '{tool_name}'")
247
+
248
+ # Execute hooks for this matcher
249
+ execution_results = await self._executor.execute_hooks(matcher.hooks, event_message)
250
+ result.results.extend(execution_results)
251
+
252
+ # Log execution summary
253
+ successful = sum(1 for r in execution_results if r.success)
254
+ failed = len(execution_results) - successful
255
+ logger.info(f"Executed {len(execution_results)} hooks: {successful} successful, {failed} failed")
256
+
257
+ except re.error as e:
258
+ error_msg = f"Invalid regex pattern '{matcher.matcher}': {e}"
259
+ result.errors.append(error_msg)
260
+ logger.error(error_msg)
261
+ except Exception as e:
262
+ error_msg = f"Error processing matcher '{matcher.matcher}': {e}"
263
+ result.errors.append(error_msg)
264
+ logger.error(error_msg)
265
+
266
+ except Exception as e:
267
+ error_msg = f"Error processing event: {e}"
268
+ result.errors.append(error_msg)
269
+ logger.error(error_msg)
270
+
271
+ finally:
272
+ result.processing_time = time.time() - start_time
273
+
274
+ if result.matched:
275
+ logger.info(f"Processed event {event_message.event_type.value} in {result.processing_time:.3f}s")
276
+
277
+ return result
278
+
279
+ async def reload_config(self) -> Dict[str, any]:
280
+ """Force reload configuration from file."""
281
+ async with self._config_lock:
282
+ self._config = None
283
+ self._config_mtime = None
284
+ return await self.load_config()
285
+
286
+ def get_current_config(self) -> Optional[HooksConfig]:
287
+ """Get the currently loaded configuration."""
288
+ return self._config
@@ -0,0 +1,133 @@
1
+ """
2
+ Type definitions for the Agent Hooks system.
3
+ """
4
+
5
+ from typing import Dict, List, Any, Optional
6
+ from dataclasses import dataclass, field
7
+ from enum import Enum
8
+ import time
9
+
10
+ class HookType(str, Enum):
11
+ """Types of hooks supported by the system."""
12
+ COMMAND = "command"
13
+
14
+ @dataclass
15
+ class Hook:
16
+ """Individual hook definition with type and command."""
17
+ type: HookType
18
+ command: str
19
+ description: Optional[str] = None
20
+
21
+ def to_dict(self) -> Dict[str, Any]:
22
+ """Convert hook to dictionary."""
23
+ result = {
24
+ 'type': self.type.value,
25
+ 'command': self.command
26
+ }
27
+ if self.description:
28
+ result['description'] = self.description
29
+ return result
30
+
31
+ @classmethod
32
+ def from_dict(cls, data: Dict[str, Any]) -> 'Hook':
33
+ """Create hook from dictionary."""
34
+ return cls(
35
+ type=HookType(data.get('type', HookType.COMMAND.value)),
36
+ command=data.get('command', ''),
37
+ description=data.get('description')
38
+ )
39
+
40
+ @dataclass
41
+ class HookMatcher:
42
+ """Pattern matching and hook configuration."""
43
+ matcher: str # Regular expression pattern
44
+ hooks: List[Hook] = field(default_factory=list)
45
+
46
+ def to_dict(self) -> Dict[str, Any]:
47
+ """Convert hook matcher to dictionary."""
48
+ return {
49
+ 'matcher': self.matcher,
50
+ 'hooks': [hook.to_dict() for hook in self.hooks]
51
+ }
52
+
53
+ @classmethod
54
+ def from_dict(cls, data: Dict[str, Any]) -> 'HookMatcher':
55
+ """Create hook matcher from dictionary."""
56
+ hooks = []
57
+ for hook_data in data.get('hooks', []):
58
+ hooks.append(Hook.from_dict(hook_data))
59
+
60
+ return cls(
61
+ matcher=data.get('matcher', ''),
62
+ hooks=hooks
63
+ )
64
+
65
+ @dataclass
66
+ class HooksConfig:
67
+ """Complete configuration structure with event type mapping."""
68
+ hooks: Dict[str, List[HookMatcher]] = field(default_factory=dict)
69
+
70
+ def to_dict(self) -> Dict[str, Any]:
71
+ """Convert hooks config to dictionary."""
72
+ result = {}
73
+ for event_type, matchers in self.hooks.items():
74
+ result[event_type] = [matcher.to_dict() for matcher in matchers]
75
+ return {'hooks': result}
76
+
77
+ @classmethod
78
+ def from_dict(cls, data: Dict[str, Any]) -> 'HooksConfig':
79
+ """Create hooks config from dictionary."""
80
+ hooks = {}
81
+ hooks_data = data.get('hooks', {})
82
+
83
+ for event_type, matchers_data in hooks_data.items():
84
+ matchers = []
85
+ for matcher_data in matchers_data:
86
+ matchers.append(HookMatcher.from_dict(matcher_data))
87
+ hooks[event_type] = matchers
88
+
89
+ return cls(hooks=hooks)
90
+
91
+ @dataclass
92
+ class HookExecutionResult:
93
+ """Command execution results and metrics."""
94
+ success: bool
95
+ command: str
96
+ stdout: str = ""
97
+ stderr: str = ""
98
+ exit_code: int = 0
99
+ execution_time: float = 0.0
100
+ start_time: float = field(default_factory=time.time)
101
+ end_time: Optional[float] = None
102
+ error_message: Optional[str] = None
103
+
104
+ def to_dict(self) -> Dict[str, Any]:
105
+ """Convert execution result to dictionary."""
106
+ return {
107
+ 'success': self.success,
108
+ 'command': self.command,
109
+ 'stdout': self.stdout,
110
+ 'stderr': self.stderr,
111
+ 'exit_code': self.exit_code,
112
+ 'execution_time': self.execution_time,
113
+ 'start_time': self.start_time,
114
+ 'end_time': self.end_time,
115
+ 'error_message': self.error_message
116
+ }
117
+
118
+ @dataclass
119
+ class HookProcessingResult:
120
+ """Overall event processing results."""
121
+ matched: bool
122
+ results: List[HookExecutionResult] = field(default_factory=list)
123
+ errors: List[str] = field(default_factory=list)
124
+ processing_time: float = 0.0
125
+
126
+ def to_dict(self) -> Dict[str, Any]:
127
+ """Convert processing result to dictionary."""
128
+ return {
129
+ 'matched': self.matched,
130
+ 'results': [result.to_dict() for result in self.results],
131
+ 'errors': self.errors,
132
+ 'processing_time': self.processing_time
133
+ }