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,545 @@
1
+ """
2
+ Comprehensive test suite for the agents module.
3
+ """
4
+
5
+ import pytest
6
+ import tempfile
7
+ import shutil
8
+ from pathlib import Path
9
+ from unittest.mock import patch, MagicMock
10
+
11
+ from autocoder.common.agents import AgentManager, AgentParser, Agent
12
+
13
+
14
+ # Test data for agent definitions
15
+ VALID_AGENT_CONTENT = """---
16
+ name: test-agent
17
+ description: A test agent for validation
18
+ tools: read_file, write_to_file, execute_command
19
+ model: gpt-4
20
+ ---
21
+
22
+ You are a test agent designed to help with testing scenarios.
23
+ Your task is to assist users with their testing needs.
24
+ """
25
+
26
+ MINIMAL_AGENT_CONTENT = """---
27
+ name: minimal-agent
28
+ description: Minimal agent with required fields only
29
+ ---
30
+
31
+ You are a minimal agent with only the required fields.
32
+ """
33
+
34
+ WILDCARD_TOOLS_AGENT_CONTENT = """---
35
+ name: wildcard-agent
36
+ description: Agent with wildcard tools
37
+ tools: *
38
+ model: gpt-4
39
+ ---
40
+
41
+ You are an agent with access to all tools.
42
+ """
43
+
44
+ INCLUDE_RULES_AGENT_CONTENT = """---
45
+ name: rules-agent
46
+ description: Agent with include_rules enabled
47
+ tools: read_file, write_to_file
48
+ model: gpt-4
49
+ include_rules: true
50
+ ---
51
+
52
+ You are an agent that includes rules in processing.
53
+ """
54
+
55
+ INVALID_AGENT_NO_FRONTMATTER = """
56
+ This is just content without frontmatter.
57
+ """
58
+
59
+ INVALID_AGENT_NO_NAME = """---
60
+ description: Agent without name
61
+ ---
62
+
63
+ Agent content here.
64
+ """
65
+
66
+ INVALID_AGENT_YAML_ERROR = """---
67
+ name: invalid-yaml
68
+ description: Agent with invalid YAML
69
+ tools: [unclosed list
70
+ ---
71
+
72
+ Agent content here.
73
+ """
74
+
75
+
76
+ class TestAgent:
77
+ """Test cases for the Agent dataclass."""
78
+
79
+ def test_agent_creation(self):
80
+ """Test creating an agent with all fields."""
81
+ agent = Agent(
82
+ name="test-agent",
83
+ description="Test description",
84
+ tools=["read_file", "write_to_file"],
85
+ model="gpt-4",
86
+ content="Test content",
87
+ file_path=Path("/test/path.md"),
88
+ include_rules=True
89
+ )
90
+
91
+ assert agent.name == "test-agent"
92
+ assert agent.description == "Test description"
93
+ assert agent.tools == ["read_file", "write_to_file"]
94
+ assert agent.model == "gpt-4"
95
+ assert agent.content == "Test content"
96
+ assert agent.file_path == Path("/test/path.md")
97
+ assert agent.include_rules == True
98
+
99
+ def test_agent_defaults(self):
100
+ """Test agent with default values."""
101
+ agent = Agent(name="test", description="desc")
102
+
103
+ assert agent.tools == []
104
+ assert agent.model is None
105
+ assert agent.content == ""
106
+ assert agent.file_path is None
107
+ assert agent.include_rules == False
108
+
109
+ def test_agent_to_dict(self):
110
+ """Test converting agent to dictionary."""
111
+ agent = Agent(
112
+ name="test-agent",
113
+ description="Test description",
114
+ tools=["read_file", "write_to_file"],
115
+ model="gpt-4",
116
+ content="Test content",
117
+ file_path=Path("/test/path.md"),
118
+ include_rules=True
119
+ )
120
+
121
+ expected = {
122
+ 'name': 'test-agent',
123
+ 'description': 'Test description',
124
+ 'tools': ['read_file', 'write_to_file'],
125
+ 'model': 'gpt-4',
126
+ 'content': 'Test content',
127
+ 'file_path': '/test/path.md',
128
+ 'include_rules': True
129
+ }
130
+
131
+ assert agent.to_dict() == expected
132
+
133
+ def test_agent_to_dict_with_none_path(self):
134
+ """Test converting agent to dictionary with None file_path."""
135
+ agent = Agent(name="test", description="desc")
136
+ result = agent.to_dict()
137
+
138
+ assert result['file_path'] is None
139
+ assert result['include_rules'] == False
140
+
141
+
142
+ class TestAgentParser:
143
+ """Test cases for the AgentParser class."""
144
+
145
+ def test_parse_valid_agent_content(self):
146
+ """Test parsing valid agent content."""
147
+ agent = AgentParser.parse_agent_content(VALID_AGENT_CONTENT)
148
+
149
+ assert agent.name == "test-agent"
150
+ assert agent.description == "A test agent for validation"
151
+ assert agent.tools == ["read_file", "write_to_file", "execute_command"]
152
+ assert agent.model == "gpt-4"
153
+ assert "You are a test agent" in agent.content
154
+ assert agent.file_path is None
155
+
156
+ def test_parse_minimal_agent_content(self):
157
+ """Test parsing agent with minimal required fields."""
158
+ agent = AgentParser.parse_agent_content(MINIMAL_AGENT_CONTENT)
159
+
160
+ assert agent.name == "minimal-agent"
161
+ assert agent.description == "Minimal agent with required fields only"
162
+ assert agent.tools == []
163
+ assert agent.model is None
164
+ assert agent.include_rules == False # default value when not specified
165
+ assert "You are a minimal agent" in agent.content
166
+
167
+ def test_parse_wildcard_tools_agent_content(self):
168
+ """Test parsing agent with wildcard tools."""
169
+ agent = AgentParser.parse_agent_content(WILDCARD_TOOLS_AGENT_CONTENT)
170
+
171
+ assert agent.name == "wildcard-agent"
172
+ assert agent.description == "Agent with wildcard tools"
173
+ assert agent.tools == ["*"]
174
+ assert agent.model == "gpt-4"
175
+ assert agent.include_rules == False # default value
176
+ assert "You are an agent with access to all tools" in agent.content
177
+
178
+ def test_parse_include_rules_agent_content(self):
179
+ """Test parsing agent with include_rules enabled."""
180
+ agent = AgentParser.parse_agent_content(INCLUDE_RULES_AGENT_CONTENT)
181
+
182
+ assert agent.name == "rules-agent"
183
+ assert agent.description == "Agent with include_rules enabled"
184
+ assert agent.tools == ["read_file", "write_to_file"]
185
+ assert agent.model == "gpt-4"
186
+ assert agent.include_rules == True
187
+ assert "You are an agent that includes rules" in agent.content
188
+
189
+ def test_parse_invalid_no_frontmatter(self):
190
+ """Test parsing content without frontmatter."""
191
+ with pytest.raises(ValueError, match="Invalid agent file format"):
192
+ AgentParser.parse_agent_content(INVALID_AGENT_NO_FRONTMATTER)
193
+
194
+ def test_parse_invalid_no_name(self):
195
+ """Test parsing agent without name field."""
196
+ with pytest.raises(ValueError, match="Agent definition must include 'name' field"):
197
+ AgentParser.parse_agent_content(INVALID_AGENT_NO_NAME)
198
+
199
+ def test_parse_invalid_yaml(self):
200
+ """Test parsing agent with invalid YAML."""
201
+ with pytest.raises(ValueError, match="Failed to parse YAML frontmatter"):
202
+ AgentParser.parse_agent_content(INVALID_AGENT_YAML_ERROR)
203
+
204
+ def test_parse_tools_empty(self):
205
+ """Test parsing empty tools string."""
206
+ assert AgentParser._parse_tools("") == []
207
+ assert AgentParser._parse_tools(" ") == []
208
+
209
+ def test_parse_tools_single(self):
210
+ """Test parsing single tool."""
211
+ assert AgentParser._parse_tools("Read") == ["Read"]
212
+ assert AgentParser._parse_tools(" Read ") == ["Read"]
213
+
214
+ def test_parse_tools_multiple(self):
215
+ """Test parsing multiple tools."""
216
+ assert AgentParser._parse_tools("Read, Write, Bash") == ["Read", "Write", "Bash"]
217
+ assert AgentParser._parse_tools("Read,Write,Bash") == ["Read", "Write", "Bash"]
218
+ assert AgentParser._parse_tools(" Read , Write , Bash ") == ["Read", "Write", "Bash"]
219
+
220
+ def test_parse_agent_file_not_found(self):
221
+ """Test parsing non-existent file."""
222
+ with pytest.raises(ValueError, match="Agent file not found"):
223
+ AgentParser.parse_agent_file(Path("/non/existent/file.md"))
224
+
225
+ def test_parse_agent_file_valid(self):
226
+ """Test parsing valid agent file."""
227
+ with tempfile.NamedTemporaryFile(mode='w', suffix='.md', delete=False) as f:
228
+ f.write(VALID_AGENT_CONTENT)
229
+ temp_path = Path(f.name)
230
+
231
+ try:
232
+ agent = AgentParser.parse_agent_file(temp_path)
233
+ assert agent.name == "test-agent"
234
+ assert agent.file_path == temp_path
235
+ finally:
236
+ temp_path.unlink()
237
+
238
+ def test_validate_agent_valid(self):
239
+ """Test validating a valid agent."""
240
+ agent = Agent(
241
+ name="test-agent",
242
+ description="Test description",
243
+ tools=["read_file", "write_to_file"],
244
+ content="Test content"
245
+ )
246
+
247
+ errors = AgentParser.validate_agent(agent)
248
+ assert errors == []
249
+
250
+ def test_validate_agent_missing_name(self):
251
+ """Test validating agent without name."""
252
+ agent = Agent(name="", description="desc", content="content")
253
+ errors = AgentParser.validate_agent(agent)
254
+ assert "Agent name is required" in errors
255
+
256
+ def test_validate_agent_missing_description(self):
257
+ """Test validating agent without description."""
258
+ agent = Agent(name="test", description="", content="content")
259
+ errors = AgentParser.validate_agent(agent)
260
+ assert "Agent description is required" in errors
261
+
262
+ def test_validate_agent_missing_content(self):
263
+ """Test validating agent without content."""
264
+ agent = Agent(name="test", description="desc", content="")
265
+ errors = AgentParser.validate_agent(agent)
266
+ assert "Agent content (system prompt) is required" in errors
267
+
268
+ def test_validate_agent_invalid_tools(self):
269
+ """Test validating agent with invalid tools."""
270
+ agent = Agent(
271
+ name="test",
272
+ description="desc",
273
+ content="content",
274
+ tools=["read_file", "InvalidTool", "write_to_file"]
275
+ )
276
+ errors = AgentParser.validate_agent(agent)
277
+ assert any("Unknown tool: InvalidTool" in error for error in errors)
278
+
279
+ def test_validate_agent_wildcard_tools(self):
280
+ """Test validating agent with wildcard tools."""
281
+ agent = Agent(
282
+ name="test",
283
+ description="desc",
284
+ content="content",
285
+ tools=["*"]
286
+ )
287
+ errors = AgentParser.validate_agent(agent)
288
+ assert errors == [] # "*" should be valid
289
+
290
+ def test_validate_agent_multiple_errors(self):
291
+ """Test validating agent with multiple errors."""
292
+ agent = Agent(
293
+ name="",
294
+ description="",
295
+ content="",
296
+ tools=["InvalidTool"]
297
+ )
298
+ errors = AgentParser.validate_agent(agent)
299
+ assert len(errors) == 4 # name, description, content, tool
300
+
301
+
302
+ class TestAgentManager:
303
+ """Test cases for the AgentManager class."""
304
+
305
+ @pytest.fixture
306
+ def temp_project(self):
307
+ """Create a temporary project directory."""
308
+ temp_dir = tempfile.mkdtemp()
309
+ yield Path(temp_dir)
310
+ shutil.rmtree(temp_dir)
311
+
312
+ @pytest.fixture
313
+ def agents_dir(self, temp_project):
314
+ """Create agents directory with test files."""
315
+ agents_dir = temp_project / ".autocoderagents"
316
+ agents_dir.mkdir()
317
+
318
+ # Create test agent files
319
+ (agents_dir / "test-agent.md").write_text(VALID_AGENT_CONTENT)
320
+ (agents_dir / "minimal-agent.md").write_text(MINIMAL_AGENT_CONTENT)
321
+
322
+ return agents_dir
323
+
324
+ def test_agent_manager_initialization(self, temp_project):
325
+ """Test AgentManager initialization."""
326
+ manager = AgentManager(str(temp_project))
327
+ assert manager.project_root == temp_project
328
+ assert isinstance(manager.agents, dict)
329
+
330
+ def test_load_agents_from_directory(self, temp_project, agents_dir):
331
+ """Test loading agents from directory."""
332
+ with patch.object(AgentManager, '_load_agents') as mock_load:
333
+ manager = AgentManager(str(temp_project))
334
+ mock_load.reset_mock() # Reset the mock after __init__
335
+
336
+ # Manually test the loading method
337
+ manager._load_agents_from_directory(agents_dir)
338
+
339
+ assert len(manager.agents) == 2
340
+ assert "test-agent" in manager.agents
341
+ assert "minimal-agent" in manager.agents
342
+
343
+ def test_get_agent_existing(self, temp_project, agents_dir):
344
+ """Test getting an existing agent."""
345
+ manager = AgentManager(str(temp_project))
346
+ manager._load_agents_from_directory(agents_dir)
347
+
348
+ agent = manager.get_agent("test-agent")
349
+ assert agent is not None
350
+ assert agent.name == "test-agent"
351
+
352
+ def test_get_agent_non_existing(self, temp_project):
353
+ """Test getting a non-existing agent."""
354
+ manager = AgentManager(str(temp_project))
355
+ agent = manager.get_agent("non-existing")
356
+ assert agent is None
357
+
358
+ def test_list_agents(self, temp_project, agents_dir):
359
+ """Test listing all agents."""
360
+ manager = AgentManager(str(temp_project))
361
+ manager._load_agents_from_directory(agents_dir)
362
+
363
+ agents = manager.list_agents()
364
+ assert len(agents) == 2
365
+ agent_names = [agent.name for agent in agents]
366
+ assert "test-agent" in agent_names
367
+ assert "minimal-agent" in agent_names
368
+
369
+ def test_get_agent_names(self, temp_project, agents_dir):
370
+ """Test getting agent names."""
371
+ manager = AgentManager(str(temp_project))
372
+ manager._load_agents_from_directory(agents_dir)
373
+
374
+ names = manager.get_agent_names()
375
+ assert len(names) == 2
376
+ assert "test-agent" in names
377
+ assert "minimal-agent" in names
378
+
379
+ def test_reload_agents(self, temp_project, agents_dir):
380
+ """Test reloading agents."""
381
+ manager = AgentManager(str(temp_project))
382
+ manager._load_agents_from_directory(agents_dir)
383
+
384
+ # Add a new agent file
385
+ new_agent_content = """---
386
+ name: new-agent
387
+ description: Newly added agent
388
+ ---
389
+ New agent content.
390
+ """
391
+ (agents_dir / "new-agent.md").write_text(new_agent_content)
392
+
393
+ # Mock the _load_agents method to use our test directory
394
+ with patch.object(manager, '_load_agents') as mock_load:
395
+ mock_load.side_effect = lambda: manager._load_agents_from_directory(agents_dir)
396
+ manager.reload_agents()
397
+ mock_load.assert_called_once()
398
+
399
+ def test_render_sub_agents_section_no_agents(self, temp_project):
400
+ """Test rendering sub agents section when no agents are loaded."""
401
+ manager = AgentManager(str(temp_project))
402
+ result = manager.render_sub_agents_section()
403
+ # The method returns a rendered template string, so we check if it's empty or minimal
404
+ assert isinstance(result, str)
405
+ # For no agents, the template should render to empty or minimal content
406
+ assert len(result.strip()) == 0 or "Available Sub Agents" not in result
407
+
408
+ def test_render_sub_agents_section_with_agents(self, temp_project, agents_dir):
409
+ """Test rendering sub agents section with loaded agents."""
410
+ manager = AgentManager(str(temp_project))
411
+ manager._load_agents_from_directory(agents_dir)
412
+
413
+ result = manager.render_sub_agents_section(current_model="gpt-4")
414
+ # The method returns a rendered template string
415
+ assert isinstance(result, str)
416
+ # Check that the rendered template contains expected content
417
+ assert "Available Named Sub Agents" in result
418
+ assert "test-agent" in result
419
+ assert "minimal-agent" in result
420
+ assert "run_named_subagents" in result
421
+
422
+ def test_render_sub_agents_section_no_model(self, temp_project, agents_dir):
423
+ """Test rendering sub agents section without model raises error."""
424
+ manager = AgentManager(str(temp_project))
425
+ # Create agent without model
426
+ agent_content = """---
427
+ name: no-model-agent
428
+ description: Agent without model
429
+ ---
430
+ Agent content.
431
+ """
432
+ (agents_dir / "no-model.md").write_text(agent_content)
433
+ manager._load_agents_from_directory(agents_dir)
434
+
435
+ with pytest.raises(ValueError, match="has no model specified"):
436
+ manager.render_sub_agents_section()
437
+
438
+ def test_to_dict(self, temp_project, agents_dir):
439
+ """Test converting manager to dictionary."""
440
+ manager = AgentManager(str(temp_project))
441
+ manager._load_agents_from_directory(agents_dir)
442
+
443
+ result = manager.to_dict()
444
+ assert "project_root" in result
445
+ assert "search_paths" in result
446
+ assert "agents" in result
447
+ assert len(result["agents"]) == 2
448
+
449
+ def test_agent_priority_override(self, temp_project):
450
+ """Test that higher priority agents override lower priority ones."""
451
+ # Create agents in different priority directories
452
+ high_priority_dir = temp_project / ".autocoderagents"
453
+ low_priority_dir = temp_project / ".auto-coder" / ".autocoderagents"
454
+
455
+ high_priority_dir.mkdir()
456
+ low_priority_dir.mkdir(parents=True)
457
+
458
+ # Same agent name in both directories
459
+ agent_low = """---
460
+ name: conflict-agent
461
+ description: Low priority agent
462
+ ---
463
+ Low priority content.
464
+ """
465
+ agent_high = """---
466
+ name: conflict-agent
467
+ description: High priority agent
468
+ ---
469
+ High priority content.
470
+ """
471
+
472
+ (low_priority_dir / "conflict.md").write_text(agent_low)
473
+ (high_priority_dir / "conflict.md").write_text(agent_high)
474
+
475
+ manager = AgentManager(str(temp_project))
476
+
477
+ # Test fallback loading to simulate the behavior
478
+ manager._load_agents_fallback()
479
+
480
+ # High priority should win
481
+ agent = manager.get_agent("conflict-agent")
482
+ assert agent is not None
483
+ assert agent.description == "High priority agent"
484
+
485
+
486
+ @pytest.mark.integration
487
+ class TestAgentManagerIntegration:
488
+ """Integration tests for AgentManager with priority directory finder."""
489
+
490
+ @pytest.fixture
491
+ def complex_project(self):
492
+ """Create a complex project with multiple agent directories."""
493
+ temp_dir = tempfile.mkdtemp()
494
+ project_root = Path(temp_dir)
495
+
496
+ # Create different priority directories
497
+ project_agents = project_root / ".autocoderagents"
498
+ autocoder_agents = project_root / ".auto-coder" / ".autocoderagents"
499
+
500
+ project_agents.mkdir()
501
+ autocoder_agents.mkdir(parents=True)
502
+
503
+ # Add agents to each directory
504
+ project_agent = """---
505
+ name: project-agent
506
+ description: Project level agent
507
+ ---
508
+ Project level agent content.
509
+ """
510
+ autocoder_agent = """---
511
+ name: autocoder-agent
512
+ description: Auto-coder level agent
513
+ ---
514
+ Auto-coder level agent content.
515
+ """
516
+
517
+ (project_agents / "project.md").write_text(project_agent)
518
+ (autocoder_agents / "autocoder.md").write_text(autocoder_agent)
519
+
520
+ yield project_root
521
+ shutil.rmtree(temp_dir)
522
+
523
+ @patch('autocoder.common.agents.agent_manager.PriorityDirectoryFinder')
524
+ def test_priority_finder_integration(self, mock_finder_class, complex_project):
525
+ """Test integration with priority directory finder."""
526
+ # Mock the finder to return successful result
527
+ mock_finder = MagicMock()
528
+ mock_result = MagicMock()
529
+ mock_result.success = True
530
+ mock_result.primary_directory = str(complex_project / ".autocoderagents")
531
+ mock_finder.find_directories.return_value = mock_result
532
+ mock_finder_class.return_value = mock_finder
533
+
534
+ manager = AgentManager(str(complex_project))
535
+
536
+ # Verify finder was called with correct config
537
+ assert mock_finder_class.called
538
+ assert mock_finder.find_directories.called
539
+
540
+ # Should load from the primary directory
541
+ assert "project-agent" in manager.agents
542
+
543
+
544
+ if __name__ == "__main__":
545
+ pytest.main([__file__, "-v"])
@@ -0,0 +1,101 @@
1
+ """
2
+ Utility functions for handling async/sync conversions consistently.
3
+ """
4
+
5
+ import asyncio
6
+ import concurrent.futures
7
+ from typing import TypeVar, Callable, Awaitable, Any
8
+ from functools import wraps
9
+
10
+ T = TypeVar('T')
11
+
12
+
13
+ def run_async_in_sync(async_func: Callable[..., Awaitable[T]], *args, **kwargs) -> T:
14
+ """
15
+ Run an async function in a synchronous context.
16
+
17
+ This function handles both cases:
18
+ 1. When called from within an existing event loop (runs in a thread)
19
+ 2. When called from a synchronous context (creates new event loop)
20
+
21
+ Args:
22
+ async_func: The async function to run
23
+ *args: Positional arguments for the function
24
+ **kwargs: Keyword arguments for the function
25
+
26
+ Returns:
27
+ The result of the async function
28
+ """
29
+ def run_in_new_loop():
30
+ """Run the async function in a new event loop."""
31
+ loop = asyncio.new_event_loop()
32
+ asyncio.set_event_loop(loop)
33
+ try:
34
+ result = loop.run_until_complete(async_func(*args, **kwargs))
35
+ # Wait for all tasks to complete
36
+ pending = asyncio.all_tasks(loop)
37
+ if pending:
38
+ loop.run_until_complete(asyncio.gather(*pending, return_exceptions=True))
39
+ return result
40
+ finally:
41
+ # Cancel any remaining tasks
42
+ pending = asyncio.all_tasks(loop)
43
+ for task in pending:
44
+ task.cancel()
45
+ if pending:
46
+ loop.run_until_complete(asyncio.gather(*pending, return_exceptions=True))
47
+ loop.close()
48
+
49
+ try:
50
+ # Check if we're in an async context
51
+ asyncio.get_running_loop()
52
+ # We're in an async context, run in a thread
53
+ with concurrent.futures.ThreadPoolExecutor() as executor:
54
+ future = executor.submit(run_in_new_loop)
55
+ return future.result()
56
+ except RuntimeError:
57
+ # No event loop running, we can run directly
58
+ return run_in_new_loop()
59
+
60
+
61
+ def async_to_sync(async_func: Callable[..., Awaitable[T]]) -> Callable[..., T]:
62
+ """
63
+ Decorator to create a synchronous version of an async function.
64
+
65
+ Usage:
66
+ @async_to_sync
67
+ async def my_async_func():
68
+ await asyncio.sleep(1)
69
+ return "done"
70
+
71
+ # Can now be called synchronously
72
+ result = my_async_func()
73
+ """
74
+ @wraps(async_func)
75
+ def wrapper(*args, **kwargs):
76
+ return run_async_in_sync(async_func, *args, **kwargs)
77
+ return wrapper
78
+
79
+
80
+ class AsyncSyncMixin:
81
+ """
82
+ Mixin class that provides automatic sync versions of async methods.
83
+
84
+ For any async method `foo`, this mixin automatically provides a `foo_sync` method.
85
+ """
86
+
87
+ def __getattr__(self, name: str) -> Any:
88
+ if name.endswith('_sync'):
89
+ async_name = name[:-5] # Remove '_sync' suffix
90
+ async_method = getattr(self, async_name, None)
91
+
92
+ if async_method and asyncio.iscoroutinefunction(async_method):
93
+ # Create a sync wrapper
94
+ def sync_wrapper(*args, **kwargs):
95
+ return run_async_in_sync(async_method, *args, **kwargs)
96
+
97
+ # Cache it for future calls
98
+ setattr(self, name, sync_wrapper)
99
+ return sync_wrapper
100
+
101
+ raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{name}'")