auto-coder 0.1.400__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 (579) 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-0.1.400.dist-info → auto_coder-2.0.0.dist-info}/WHEEL +1 -1
  5. {auto_coder-0.1.400.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 +25 -4
  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 +1029 -2310
  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 +1021 -372
  28. autocoder/chat_auto_coder_lang.py +23 -732
  29. autocoder/commands/auto_command.py +26 -9
  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/file_monitor/test_file_monitor.py +307 -0
  119. autocoder/common/git_utils.py +51 -10
  120. autocoder/common/global_cancel.py +15 -6
  121. autocoder/common/ignorefiles/test_ignore_file_utils.py +1 -1
  122. autocoder/common/international/__init__.py +31 -0
  123. autocoder/common/international/demo_international.py +92 -0
  124. autocoder/common/international/message_manager.py +157 -0
  125. autocoder/common/international/messages/__init__.py +56 -0
  126. autocoder/common/international/messages/async_command_messages.py +507 -0
  127. autocoder/common/international/messages/auto_coder_messages.py +2208 -0
  128. autocoder/common/international/messages/chat_auto_coder_messages.py +1547 -0
  129. autocoder/common/international/messages/command_help_messages.py +986 -0
  130. autocoder/common/international/messages/conversation_command_messages.py +191 -0
  131. autocoder/common/international/messages/git_helper_plugin_messages.py +159 -0
  132. autocoder/common/international/messages/queue_command_messages.py +751 -0
  133. autocoder/common/international/messages/rules_command_messages.py +77 -0
  134. autocoder/common/international/messages/sdk_messages.py +1707 -0
  135. autocoder/common/international/messages/token_helper_plugin_messages.py +361 -0
  136. autocoder/common/international/messages/tool_display_messages.py +1212 -0
  137. autocoder/common/international/messages/workflow_exception_messages.py +473 -0
  138. autocoder/common/international/test_international.py +612 -0
  139. autocoder/common/linter_core/__init__.py +28 -0
  140. autocoder/common/linter_core/base_linter.py +61 -0
  141. autocoder/common/linter_core/config_loader.py +271 -0
  142. autocoder/common/linter_core/formatters/__init__.py +0 -0
  143. autocoder/common/linter_core/formatters/base_formatter.py +38 -0
  144. autocoder/common/linter_core/formatters/raw_formatter.py +17 -0
  145. autocoder/common/linter_core/linter.py +166 -0
  146. autocoder/common/linter_core/linter_factory.py +216 -0
  147. autocoder/common/linter_core/linter_manager.py +333 -0
  148. autocoder/common/linter_core/linters/__init__.py +9 -0
  149. autocoder/common/linter_core/linters/java_linter.py +342 -0
  150. autocoder/common/linter_core/linters/python_linter.py +115 -0
  151. autocoder/common/linter_core/linters/typescript_linter.py +119 -0
  152. autocoder/common/linter_core/models/__init__.py +7 -0
  153. autocoder/common/linter_core/models/lint_result.py +91 -0
  154. autocoder/common/linter_core/models.py +33 -0
  155. autocoder/common/linter_core/tests/__init__.py +3 -0
  156. autocoder/common/linter_core/tests/test_config_loader.py +323 -0
  157. autocoder/common/linter_core/tests/test_config_loading.py +308 -0
  158. autocoder/common/linter_core/tests/test_factory_manager.py +234 -0
  159. autocoder/common/linter_core/tests/test_formatters.py +147 -0
  160. autocoder/common/linter_core/tests/test_integration.py +317 -0
  161. autocoder/common/linter_core/tests/test_java_linter.py +496 -0
  162. autocoder/common/linter_core/tests/test_linters.py +265 -0
  163. autocoder/common/linter_core/tests/test_models.py +81 -0
  164. autocoder/common/linter_core/tests/verify_config_loading.py +296 -0
  165. autocoder/common/linter_core/tests/verify_fixes.py +183 -0
  166. autocoder/common/llm_friendly_package/__init__.py +31 -0
  167. autocoder/common/llm_friendly_package/base_manager.py +102 -0
  168. autocoder/common/llm_friendly_package/docs_manager.py +121 -0
  169. autocoder/common/llm_friendly_package/library_manager.py +171 -0
  170. autocoder/common/{llm_friendly_package.py → llm_friendly_package/main_manager.py} +204 -231
  171. autocoder/common/llm_friendly_package/models.py +40 -0
  172. autocoder/common/llm_friendly_package/test_llm_friendly_package.py +536 -0
  173. autocoder/common/llms/__init__.py +15 -0
  174. autocoder/common/llms/demo_error_handling.py +85 -0
  175. autocoder/common/llms/factory.py +142 -0
  176. autocoder/common/llms/manager.py +264 -0
  177. autocoder/common/llms/pricing.py +121 -0
  178. autocoder/common/llms/registry.py +288 -0
  179. autocoder/common/llms/schema.py +77 -0
  180. autocoder/common/llms/simple_demo.py +45 -0
  181. autocoder/common/llms/test_quick_model.py +116 -0
  182. autocoder/common/llms/test_remove_functionality.py +182 -0
  183. autocoder/common/llms/tests/__init__.py +1 -0
  184. autocoder/common/llms/tests/test_manager.py +330 -0
  185. autocoder/common/llms/tests/test_registry.py +364 -0
  186. autocoder/common/mcp_tools/__init__.py +62 -0
  187. autocoder/common/{mcp_tools.py → mcp_tools/executor.py} +49 -40
  188. autocoder/common/{mcp_hub.py → mcp_tools/hub.py} +42 -68
  189. autocoder/common/{mcp_server_install.py → mcp_tools/installer.py} +16 -28
  190. autocoder/common/{mcp_server.py → mcp_tools/server.py} +176 -48
  191. autocoder/common/mcp_tools/test_keyboard_interrupt.py +93 -0
  192. autocoder/common/mcp_tools/test_mcp_tools.py +391 -0
  193. autocoder/common/{mcp_server_types.py → mcp_tools/types.py} +121 -48
  194. autocoder/common/mcp_tools/verify_functionality.py +202 -0
  195. autocoder/common/model_speed_tester.py +32 -26
  196. autocoder/common/priority_directory_finder/__init__.py +142 -0
  197. autocoder/common/priority_directory_finder/examples.py +230 -0
  198. autocoder/common/priority_directory_finder/finder.py +283 -0
  199. autocoder/common/priority_directory_finder/models.py +236 -0
  200. autocoder/common/priority_directory_finder/test_priority_directory_finder.py +431 -0
  201. autocoder/common/project_scanner/__init__.py +18 -0
  202. autocoder/common/project_scanner/compat.py +77 -0
  203. autocoder/common/project_scanner/scanner.py +436 -0
  204. autocoder/common/project_tracker/__init__.py +27 -0
  205. autocoder/common/project_tracker/api.py +228 -0
  206. autocoder/common/project_tracker/demo.py +272 -0
  207. autocoder/common/project_tracker/tracker.py +487 -0
  208. autocoder/common/project_tracker/types.py +53 -0
  209. autocoder/common/pruner/__init__.py +67 -0
  210. autocoder/common/pruner/agentic_conversation_pruner.py +746 -0
  211. autocoder/common/{context_pruner.py → pruner/context_pruner.py} +137 -40
  212. autocoder/common/pruner/conversation_message_ids_api.py +386 -0
  213. autocoder/common/pruner/conversation_message_ids_manager.py +347 -0
  214. autocoder/common/pruner/conversation_message_ids_pruner.py +473 -0
  215. autocoder/common/pruner/conversation_normalizer.py +347 -0
  216. autocoder/common/{conversation_pruner.py → pruner/conversation_pruner.py} +26 -6
  217. autocoder/common/pruner/test_agentic_conversation_pruner.py +784 -0
  218. autocoder/common/pruner/test_context_pruner.py +546 -0
  219. autocoder/common/pruner/test_conversation_normalizer.py +502 -0
  220. autocoder/common/pruner/test_tool_content_detector.py +324 -0
  221. autocoder/common/pruner/tool_content_detector.py +227 -0
  222. autocoder/common/pruner/tools/__init__.py +18 -0
  223. autocoder/common/pruner/tools/query_message_ids.py +264 -0
  224. autocoder/common/pruner/tools/test_agentic_pruning_logic.py +432 -0
  225. autocoder/common/pruner/tools/test_message_ids_pruning_only.py +192 -0
  226. autocoder/common/pull_requests/__init__.py +9 -1
  227. autocoder/common/pull_requests/utils.py +122 -1
  228. autocoder/common/rag_manager/rag_manager.py +36 -40
  229. autocoder/common/rulefiles/__init__.py +53 -1
  230. autocoder/common/rulefiles/api.py +250 -0
  231. autocoder/common/rulefiles/core/__init__.py +14 -0
  232. autocoder/common/rulefiles/core/manager.py +241 -0
  233. autocoder/common/rulefiles/core/selector.py +805 -0
  234. autocoder/common/rulefiles/models/__init__.py +20 -0
  235. autocoder/common/rulefiles/models/index.py +16 -0
  236. autocoder/common/rulefiles/models/init_rule.py +18 -0
  237. autocoder/common/rulefiles/models/rule_file.py +18 -0
  238. autocoder/common/rulefiles/models/rule_relevance.py +14 -0
  239. autocoder/common/rulefiles/models/summary.py +16 -0
  240. autocoder/common/rulefiles/test_rulefiles.py +776 -0
  241. autocoder/common/rulefiles/utils/__init__.py +34 -0
  242. autocoder/common/rulefiles/utils/monitor.py +86 -0
  243. autocoder/common/rulefiles/utils/parser.py +230 -0
  244. autocoder/common/save_formatted_log.py +67 -10
  245. autocoder/common/search_replace.py +8 -1
  246. autocoder/common/search_replace_patch/__init__.py +24 -0
  247. autocoder/common/search_replace_patch/base.py +115 -0
  248. autocoder/common/search_replace_patch/manager.py +248 -0
  249. autocoder/common/search_replace_patch/patch_replacer.py +304 -0
  250. autocoder/common/search_replace_patch/similarity_replacer.py +306 -0
  251. autocoder/common/search_replace_patch/string_replacer.py +181 -0
  252. autocoder/common/search_replace_patch/tests/__init__.py +3 -0
  253. autocoder/common/search_replace_patch/tests/run_tests.py +126 -0
  254. autocoder/common/search_replace_patch/tests/test_base.py +188 -0
  255. autocoder/common/search_replace_patch/tests/test_empty_line_insert.py +233 -0
  256. autocoder/common/search_replace_patch/tests/test_integration.py +389 -0
  257. autocoder/common/search_replace_patch/tests/test_manager.py +351 -0
  258. autocoder/common/search_replace_patch/tests/test_patch_replacer.py +316 -0
  259. autocoder/common/search_replace_patch/tests/test_regex_replacer.py +306 -0
  260. autocoder/common/search_replace_patch/tests/test_similarity_replacer.py +384 -0
  261. autocoder/common/shell_commands/__init__.py +197 -0
  262. autocoder/common/shell_commands/background_process_notifier.py +346 -0
  263. autocoder/common/shell_commands/command_executor.py +1127 -0
  264. autocoder/common/shell_commands/error_recovery.py +541 -0
  265. autocoder/common/shell_commands/exceptions.py +120 -0
  266. autocoder/common/shell_commands/interactive_executor.py +476 -0
  267. autocoder/common/shell_commands/interactive_pexpect_process.py +623 -0
  268. autocoder/common/shell_commands/interactive_process.py +744 -0
  269. autocoder/common/shell_commands/interactive_session_manager.py +1014 -0
  270. autocoder/common/shell_commands/monitoring.py +529 -0
  271. autocoder/common/shell_commands/process_cleanup.py +386 -0
  272. autocoder/common/shell_commands/process_manager.py +606 -0
  273. autocoder/common/shell_commands/test_interactive_pexpect_process.py +281 -0
  274. autocoder/common/shell_commands/tests/__init__.py +6 -0
  275. autocoder/common/shell_commands/tests/conftest.py +118 -0
  276. autocoder/common/shell_commands/tests/test_background_process_notifier.py +703 -0
  277. autocoder/common/shell_commands/tests/test_command_executor.py +448 -0
  278. autocoder/common/shell_commands/tests/test_error_recovery.py +305 -0
  279. autocoder/common/shell_commands/tests/test_exceptions.py +299 -0
  280. autocoder/common/shell_commands/tests/test_execute_batch.py +588 -0
  281. autocoder/common/shell_commands/tests/test_indented_batch_commands.py +244 -0
  282. autocoder/common/shell_commands/tests/test_integration.py +664 -0
  283. autocoder/common/shell_commands/tests/test_monitoring.py +546 -0
  284. autocoder/common/shell_commands/tests/test_performance.py +632 -0
  285. autocoder/common/shell_commands/tests/test_process_cleanup.py +397 -0
  286. autocoder/common/shell_commands/tests/test_process_manager.py +606 -0
  287. autocoder/common/shell_commands/tests/test_timeout_config.py +343 -0
  288. autocoder/common/shell_commands/tests/test_timeout_manager.py +520 -0
  289. autocoder/common/shell_commands/timeout_config.py +315 -0
  290. autocoder/common/shell_commands/timeout_manager.py +352 -0
  291. autocoder/common/terminal_paste/__init__.py +14 -0
  292. autocoder/common/terminal_paste/demo.py +145 -0
  293. autocoder/common/terminal_paste/demo_paste_functionality.py +95 -0
  294. autocoder/common/terminal_paste/paste_handler.py +200 -0
  295. autocoder/common/terminal_paste/paste_manager.py +118 -0
  296. autocoder/common/terminal_paste/tests/__init__.py +1 -0
  297. autocoder/common/terminal_paste/tests/test_paste_handler.py +182 -0
  298. autocoder/common/terminal_paste/tests/test_paste_manager.py +126 -0
  299. autocoder/common/terminal_paste/utils.py +163 -0
  300. autocoder/common/test_autocoder_args.py +232 -0
  301. autocoder/common/test_env_manager.py +173 -0
  302. autocoder/common/test_env_manager_integration.py +159 -0
  303. autocoder/common/text_similarity/__init__.py +9 -0
  304. autocoder/common/text_similarity/demo.py +216 -0
  305. autocoder/common/text_similarity/examples.py +266 -0
  306. autocoder/common/text_similarity/test_text_similarity.py +306 -0
  307. autocoder/common/text_similarity/text_similarity.py +194 -0
  308. autocoder/common/text_similarity/utils.py +125 -0
  309. autocoder/common/todos/__init__.py +61 -0
  310. autocoder/common/todos/cache/__init__.py +16 -0
  311. autocoder/common/todos/cache/base_cache.py +89 -0
  312. autocoder/common/todos/cache/cache_manager.py +228 -0
  313. autocoder/common/todos/cache/memory_cache.py +225 -0
  314. autocoder/common/todos/config.py +155 -0
  315. autocoder/common/todos/exceptions.py +35 -0
  316. autocoder/common/todos/get_todo_manager.py +161 -0
  317. autocoder/common/todos/manager.py +537 -0
  318. autocoder/common/todos/models.py +239 -0
  319. autocoder/common/todos/storage/__init__.py +14 -0
  320. autocoder/common/todos/storage/base_storage.py +76 -0
  321. autocoder/common/todos/storage/file_storage.py +278 -0
  322. autocoder/common/tokens/__init__.py +15 -0
  323. autocoder/common/tokens/counter.py +44 -2
  324. autocoder/common/tools_manager/__init__.py +17 -0
  325. autocoder/common/tools_manager/examples.py +162 -0
  326. autocoder/common/tools_manager/manager.py +385 -0
  327. autocoder/common/tools_manager/models.py +39 -0
  328. autocoder/common/tools_manager/test_tools_manager.py +303 -0
  329. autocoder/common/tools_manager/utils.py +191 -0
  330. autocoder/common/v2/agent/agentic_callbacks.py +270 -0
  331. autocoder/common/v2/agent/agentic_edit.py +2729 -2052
  332. autocoder/common/v2/agent/agentic_edit_change_manager.py +474 -0
  333. autocoder/common/v2/agent/agentic_edit_tools/__init__.py +43 -2
  334. autocoder/common/v2/agent/agentic_edit_tools/ac_mod_list_tool_resolver.py +279 -0
  335. autocoder/common/v2/agent/agentic_edit_tools/ac_mod_read_tool_resolver.py +40 -0
  336. autocoder/common/v2/agent/agentic_edit_tools/ac_mod_write_tool_resolver.py +52 -0
  337. autocoder/common/v2/agent/agentic_edit_tools/ask_followup_question_tool_resolver.py +8 -0
  338. autocoder/common/v2/agent/agentic_edit_tools/background_task_tool_resolver.py +1167 -0
  339. autocoder/common/v2/agent/agentic_edit_tools/base_tool_resolver.py +2 -2
  340. autocoder/common/v2/agent/agentic_edit_tools/conversation_message_ids_read_tool_resolver.py +214 -0
  341. autocoder/common/v2/agent/agentic_edit_tools/conversation_message_ids_write_tool_resolver.py +299 -0
  342. autocoder/common/v2/agent/agentic_edit_tools/count_tokens_tool_resolver.py +290 -0
  343. autocoder/common/v2/agent/agentic_edit_tools/execute_command_tool_resolver.py +565 -30
  344. autocoder/common/v2/agent/agentic_edit_tools/execute_workflow_tool_resolver.py +485 -0
  345. autocoder/common/v2/agent/agentic_edit_tools/extract_to_text_tool_resolver.py +225 -0
  346. autocoder/common/v2/agent/agentic_edit_tools/lint_report.py +79 -0
  347. autocoder/common/v2/agent/agentic_edit_tools/linter_config_models.py +343 -0
  348. autocoder/common/v2/agent/agentic_edit_tools/linter_enabled_tool_resolver.py +189 -0
  349. autocoder/common/v2/agent/agentic_edit_tools/list_files_tool_resolver.py +169 -101
  350. autocoder/common/v2/agent/agentic_edit_tools/load_extra_document_tool_resolver.py +349 -0
  351. autocoder/common/v2/agent/agentic_edit_tools/read_file_tool_resolver.py +244 -51
  352. autocoder/common/v2/agent/agentic_edit_tools/replace_in_file_tool_resolver.py +667 -147
  353. autocoder/common/v2/agent/agentic_edit_tools/run_named_subagents_tool_resolver.py +691 -0
  354. autocoder/common/v2/agent/agentic_edit_tools/search_files_tool_resolver.py +409 -140
  355. autocoder/common/v2/agent/agentic_edit_tools/session_interactive_tool_resolver.py +115 -0
  356. autocoder/common/v2/agent/agentic_edit_tools/session_start_tool_resolver.py +190 -0
  357. autocoder/common/v2/agent/agentic_edit_tools/session_stop_tool_resolver.py +76 -0
  358. autocoder/common/v2/agent/agentic_edit_tools/test_write_to_file_tool_resolver.py +209 -194
  359. autocoder/common/v2/agent/agentic_edit_tools/todo_read_tool_resolver.py +135 -0
  360. autocoder/common/v2/agent/agentic_edit_tools/todo_write_tool_resolver.py +328 -0
  361. autocoder/common/v2/agent/agentic_edit_tools/use_mcp_tool_resolver.py +2 -2
  362. autocoder/common/v2/agent/agentic_edit_tools/web_crawl_tool_resolver.py +557 -0
  363. autocoder/common/v2/agent/agentic_edit_tools/web_search_tool_resolver.py +600 -0
  364. autocoder/common/v2/agent/agentic_edit_tools/write_to_file_tool_resolver.py +56 -121
  365. autocoder/common/v2/agent/agentic_edit_types.py +386 -10
  366. autocoder/common/v2/agent/runner/__init__.py +31 -0
  367. autocoder/common/v2/agent/runner/base_runner.py +92 -0
  368. autocoder/common/v2/agent/runner/file_based_event_runner.py +217 -0
  369. autocoder/common/v2/agent/runner/sdk_runner.py +182 -0
  370. autocoder/common/v2/agent/runner/terminal_runner.py +396 -0
  371. autocoder/common/v2/agent/runner/tool_display.py +589 -0
  372. autocoder/common/v2/agent/test_agentic_callbacks.py +265 -0
  373. autocoder/common/v2/agent/test_agentic_edit.py +194 -0
  374. autocoder/common/v2/agent/tool_caller/__init__.py +24 -0
  375. autocoder/common/v2/agent/tool_caller/default_tool_resolver_map.py +135 -0
  376. autocoder/common/v2/agent/tool_caller/integration_test.py +172 -0
  377. autocoder/common/v2/agent/tool_caller/plugins/__init__.py +14 -0
  378. autocoder/common/v2/agent/tool_caller/plugins/base_plugin.py +126 -0
  379. autocoder/common/v2/agent/tool_caller/plugins/examples/__init__.py +13 -0
  380. autocoder/common/v2/agent/tool_caller/plugins/examples/logging_plugin.py +164 -0
  381. autocoder/common/v2/agent/tool_caller/plugins/examples/security_filter_plugin.py +198 -0
  382. autocoder/common/v2/agent/tool_caller/plugins/plugin_interface.py +141 -0
  383. autocoder/common/v2/agent/tool_caller/test_tool_caller.py +278 -0
  384. autocoder/common/v2/agent/tool_caller/tool_call_plugin_manager.py +331 -0
  385. autocoder/common/v2/agent/tool_caller/tool_caller.py +337 -0
  386. autocoder/common/v2/agent/tool_caller/usage_example.py +193 -0
  387. autocoder/common/v2/code_agentic_editblock_manager.py +4 -4
  388. autocoder/common/v2/code_auto_generate.py +136 -78
  389. autocoder/common/v2/code_auto_generate_diff.py +135 -79
  390. autocoder/common/v2/code_auto_generate_editblock.py +174 -99
  391. autocoder/common/v2/code_auto_generate_strict_diff.py +151 -71
  392. autocoder/common/v2/code_auto_merge.py +1 -1
  393. autocoder/common/v2/code_auto_merge_editblock.py +13 -1
  394. autocoder/common/v2/code_diff_manager.py +3 -3
  395. autocoder/common/v2/code_editblock_manager.py +4 -14
  396. autocoder/common/v2/code_manager.py +1 -1
  397. autocoder/common/v2/code_strict_diff_manager.py +2 -2
  398. autocoder/common/wrap_llm_hint/__init__.py +10 -0
  399. autocoder/common/wrap_llm_hint/test_wrap_llm_hint.py +1067 -0
  400. autocoder/common/wrap_llm_hint/utils.py +432 -0
  401. autocoder/common/wrap_llm_hint/wrap_llm_hint.py +323 -0
  402. autocoder/completer/__init__.py +8 -0
  403. autocoder/completer/command_completer_v2.py +1051 -0
  404. autocoder/default_project/__init__.py +501 -0
  405. autocoder/dispacher/__init__.py +4 -12
  406. autocoder/dispacher/actions/action.py +165 -7
  407. autocoder/dispacher/actions/plugins/action_regex_project.py +2 -2
  408. autocoder/index/entry.py +117 -125
  409. autocoder/{agent → index/filter}/agentic_filter.py +323 -334
  410. autocoder/index/filter/normal_filter.py +5 -11
  411. autocoder/index/filter/quick_filter.py +1 -1
  412. autocoder/index/index.py +36 -9
  413. autocoder/index/tests/__init__.py +1 -0
  414. autocoder/index/tests/run_tests.py +195 -0
  415. autocoder/index/tests/test_entry.py +303 -0
  416. autocoder/index/tests/test_index_manager.py +314 -0
  417. autocoder/index/tests/test_module_integration.py +300 -0
  418. autocoder/index/tests/test_symbols_utils.py +183 -0
  419. autocoder/inner/__init__.py +4 -0
  420. autocoder/inner/agentic.py +932 -0
  421. autocoder/inner/async_command_handler.py +992 -0
  422. autocoder/inner/conversation_command_handlers.py +623 -0
  423. autocoder/inner/merge_command_handler.py +213 -0
  424. autocoder/inner/queue_command_handler.py +684 -0
  425. autocoder/models.py +95 -266
  426. autocoder/plugins/git_helper_plugin.py +31 -29
  427. autocoder/plugins/token_helper_plugin.py +156 -37
  428. autocoder/pyproject/__init__.py +32 -29
  429. autocoder/rag/agentic_rag.py +215 -75
  430. autocoder/rag/cache/simple_cache.py +1 -2
  431. autocoder/rag/loaders/image_loader.py +1 -1
  432. autocoder/rag/long_context_rag.py +42 -26
  433. autocoder/rag/qa_conversation_strategy.py +1 -1
  434. autocoder/rag/terminal/__init__.py +17 -0
  435. autocoder/rag/terminal/args.py +581 -0
  436. autocoder/rag/terminal/bootstrap.py +61 -0
  437. autocoder/rag/terminal/command_handlers.py +653 -0
  438. autocoder/rag/terminal/formatters/__init__.py +20 -0
  439. autocoder/rag/terminal/formatters/base.py +70 -0
  440. autocoder/rag/terminal/formatters/json_format.py +66 -0
  441. autocoder/rag/terminal/formatters/stream_json.py +95 -0
  442. autocoder/rag/terminal/formatters/text.py +28 -0
  443. autocoder/rag/terminal/init.py +120 -0
  444. autocoder/rag/terminal/utils.py +106 -0
  445. autocoder/rag/test_agentic_rag.py +389 -0
  446. autocoder/rag/test_doc_filter.py +3 -3
  447. autocoder/rag/test_long_context_rag.py +1 -1
  448. autocoder/rag/test_token_limiter.py +517 -10
  449. autocoder/rag/token_counter.py +3 -0
  450. autocoder/rag/token_limiter.py +19 -15
  451. autocoder/rag/tools/__init__.py +26 -2
  452. autocoder/rag/tools/bochaai_example.py +343 -0
  453. autocoder/rag/tools/bochaai_sdk.py +541 -0
  454. autocoder/rag/tools/metaso_example.py +268 -0
  455. autocoder/rag/tools/metaso_sdk.py +417 -0
  456. autocoder/rag/tools/recall_tool.py +28 -7
  457. autocoder/rag/tools/run_integration_tests.py +204 -0
  458. autocoder/rag/tools/test_all_providers.py +318 -0
  459. autocoder/rag/tools/test_bochaai_integration.py +482 -0
  460. autocoder/rag/tools/test_final_integration.py +215 -0
  461. autocoder/rag/tools/test_metaso_integration.py +424 -0
  462. autocoder/rag/tools/test_metaso_real.py +171 -0
  463. autocoder/rag/tools/test_web_crawl_tool.py +639 -0
  464. autocoder/rag/tools/test_web_search_tool.py +509 -0
  465. autocoder/rag/tools/todo_read_tool.py +202 -0
  466. autocoder/rag/tools/todo_write_tool.py +412 -0
  467. autocoder/rag/tools/web_crawl_tool.py +634 -0
  468. autocoder/rag/tools/web_search_tool.py +558 -0
  469. autocoder/rag/tools/web_tools_example.py +119 -0
  470. autocoder/rag/types.py +16 -0
  471. autocoder/rag/variable_holder.py +4 -2
  472. autocoder/rags.py +86 -79
  473. autocoder/regexproject/__init__.py +23 -21
  474. autocoder/run_context.py +9 -0
  475. autocoder/sdk/__init__.py +50 -161
  476. autocoder/sdk/api.py +370 -0
  477. autocoder/sdk/async_runner/__init__.py +26 -0
  478. autocoder/sdk/async_runner/async_executor.py +650 -0
  479. autocoder/sdk/async_runner/async_handler.py +356 -0
  480. autocoder/sdk/async_runner/markdown_processor.py +595 -0
  481. autocoder/sdk/async_runner/task_metadata.py +284 -0
  482. autocoder/sdk/async_runner/worktree_manager.py +438 -0
  483. autocoder/sdk/cli/__init__.py +2 -5
  484. autocoder/sdk/cli/formatters.py +28 -204
  485. autocoder/sdk/cli/handlers.py +77 -44
  486. autocoder/sdk/cli/main.py +158 -170
  487. autocoder/sdk/cli/options.py +95 -22
  488. autocoder/sdk/constants.py +139 -51
  489. autocoder/sdk/core/auto_coder_core.py +484 -267
  490. autocoder/sdk/core/bridge.py +298 -118
  491. autocoder/sdk/exceptions.py +18 -12
  492. autocoder/sdk/formatters/__init__.py +19 -0
  493. autocoder/sdk/formatters/input.py +64 -0
  494. autocoder/sdk/formatters/output.py +247 -0
  495. autocoder/sdk/formatters/stream.py +54 -0
  496. autocoder/sdk/models/__init__.py +6 -5
  497. autocoder/sdk/models/options.py +55 -18
  498. autocoder/sdk/utils/formatters.py +27 -195
  499. autocoder/suffixproject/__init__.py +28 -25
  500. autocoder/terminal/__init__.py +14 -0
  501. autocoder/terminal/app.py +454 -0
  502. autocoder/terminal/args.py +32 -0
  503. autocoder/terminal/bootstrap.py +178 -0
  504. autocoder/terminal/command_processor.py +521 -0
  505. autocoder/terminal/command_registry.py +57 -0
  506. autocoder/terminal/help.py +97 -0
  507. autocoder/terminal/tasks/__init__.py +5 -0
  508. autocoder/terminal/tasks/background.py +77 -0
  509. autocoder/terminal/tasks/task_event.py +70 -0
  510. autocoder/terminal/ui/__init__.py +13 -0
  511. autocoder/terminal/ui/completer.py +268 -0
  512. autocoder/terminal/ui/keybindings.py +75 -0
  513. autocoder/terminal/ui/session.py +41 -0
  514. autocoder/terminal/ui/toolbar.py +64 -0
  515. autocoder/terminal/utils/__init__.py +13 -0
  516. autocoder/terminal/utils/errors.py +18 -0
  517. autocoder/terminal/utils/paths.py +19 -0
  518. autocoder/terminal/utils/shell.py +43 -0
  519. autocoder/terminal_v3/__init__.py +10 -0
  520. autocoder/terminal_v3/app.py +201 -0
  521. autocoder/terminal_v3/handlers/__init__.py +5 -0
  522. autocoder/terminal_v3/handlers/command_handler.py +131 -0
  523. autocoder/terminal_v3/models/__init__.py +6 -0
  524. autocoder/terminal_v3/models/conversation_buffer.py +214 -0
  525. autocoder/terminal_v3/models/message.py +50 -0
  526. autocoder/terminal_v3/models/tool_display.py +247 -0
  527. autocoder/terminal_v3/ui/__init__.py +7 -0
  528. autocoder/terminal_v3/ui/keybindings.py +56 -0
  529. autocoder/terminal_v3/ui/layout.py +141 -0
  530. autocoder/terminal_v3/ui/styles.py +43 -0
  531. autocoder/tsproject/__init__.py +23 -23
  532. autocoder/utils/auto_coder_utils/chat_stream_out.py +1 -1
  533. autocoder/utils/llms.py +88 -80
  534. autocoder/utils/math_utils.py +101 -0
  535. autocoder/utils/model_provider_selector.py +16 -4
  536. autocoder/utils/operate_config_api.py +33 -5
  537. autocoder/utils/thread_utils.py +2 -2
  538. autocoder/version.py +4 -2
  539. autocoder/workflow_agents/__init__.py +84 -0
  540. autocoder/workflow_agents/agent.py +143 -0
  541. autocoder/workflow_agents/exceptions.py +573 -0
  542. autocoder/workflow_agents/executor.py +489 -0
  543. autocoder/workflow_agents/loader.py +737 -0
  544. autocoder/workflow_agents/runner.py +267 -0
  545. autocoder/workflow_agents/types.py +172 -0
  546. autocoder/workflow_agents/utils.py +434 -0
  547. autocoder/workflow_agents/workflow_manager.py +211 -0
  548. auto_coder-0.1.400.dist-info/METADATA +0 -396
  549. auto_coder-0.1.400.dist-info/RECORD +0 -425
  550. auto_coder-0.1.400.dist-info/licenses/LICENSE +0 -201
  551. autocoder/auto_coder_server.py +0 -672
  552. autocoder/benchmark.py +0 -138
  553. autocoder/common/ac_style_command_parser/example.py +0 -7
  554. autocoder/common/cleaner.py +0 -31
  555. autocoder/common/command_completer_v2.py +0 -615
  556. autocoder/common/directory_cache/__init__.py +0 -1
  557. autocoder/common/directory_cache/cache.py +0 -192
  558. autocoder/common/directory_cache/test_cache.py +0 -190
  559. autocoder/common/file_checkpoint/examples.py +0 -217
  560. autocoder/common/llm_friendly_package_example.py +0 -138
  561. autocoder/common/llm_friendly_package_test.py +0 -63
  562. autocoder/common/pull_requests/test_module.py +0 -1
  563. autocoder/common/rulefiles/autocoderrules_utils.py +0 -484
  564. autocoder/common/text.py +0 -30
  565. autocoder/common/v2/agent/agentic_edit_tools/list_package_info_tool_resolver.py +0 -42
  566. autocoder/common/v2/agent/agentic_edit_tools/test_execute_command_tool_resolver.py +0 -70
  567. autocoder/common/v2/agent/agentic_edit_tools/test_search_files_tool_resolver.py +0 -163
  568. autocoder/common/v2/agent/agentic_tool_display.py +0 -183
  569. autocoder/plugins/dynamic_completion_example.py +0 -148
  570. autocoder/plugins/sample_plugin.py +0 -160
  571. autocoder/sdk/cli/__main__.py +0 -26
  572. autocoder/sdk/cli/completion_wrapper.py +0 -38
  573. autocoder/sdk/cli/install_completion.py +0 -301
  574. autocoder/sdk/models/messages.py +0 -209
  575. autocoder/sdk/session/__init__.py +0 -32
  576. autocoder/sdk/session/session.py +0 -106
  577. autocoder/sdk/session/session_manager.py +0 -56
  578. {auto_coder-0.1.400.dist-info → auto_coder-2.0.0.dist-info}/top_level.txt +0 -0
  579. /autocoder/{sdk/example.py → common/agent_query_queue/__init__.py} +0 -0
@@ -1,102 +1,70 @@
1
-
2
- import pytest
3
1
  import os
4
- import shutil
5
- import json
2
+ import pytest
6
3
  from unittest.mock import MagicMock, patch
7
-
8
- from autocoder.common import AutoCoderArgs
9
- from autocoder.common.v2.agent.agentic_edit_types import WriteToFileTool, ToolResult
10
- from autocoder.common.v2.agent.agentic_edit_tools.write_to_file_tool_resolver import WriteToFileToolResolver
11
- from autocoder.auto_coder_runner import load_tokenizer as load_tokenizer_global
12
- from autocoder.utils.llms import get_single_llm
13
- from autocoder.common.file_monitor.monitor import get_file_monitor, FileMonitor
14
- from autocoder.common.rulefiles.autocoderrules_utils import get_rules, reset_rules_manager
4
+ from pathlib import Path
15
5
  from loguru import logger
6
+ from autocoder.common.v2.agent.agentic_edit_tools.write_to_file_tool_resolver import WriteToFileToolResolver
7
+ from autocoder.common.v2.agent.agentic_edit_types import WriteToFileTool
8
+ from autocoder.common import AutoCoderArgs
9
+ from autocoder.common.file_monitor.monitor import FileMonitor
10
+ from autocoder.common.rulefiles import AutocoderRulesManager
11
+
12
+
16
13
 
17
- # Helper to create a temporary test directory
18
14
  @pytest.fixture(scope="function")
19
15
  def temp_test_dir(tmp_path_factory):
20
- temp_dir = tmp_path_factory.mktemp("test_write_to_file_resolver_")
16
+ """临时测试目录"""
17
+ temp_dir = tmp_path_factory.mktemp("test_write_to_file_resolver")
21
18
  logger.info(f"Created temp dir for test: {temp_dir}")
22
- # Create a dummy .autocoderignore to avoid issues with default ignore patterns loading
23
- # from unexpected places if the test is run from a different CWD.
24
- with open(os.path.join(temp_dir, ".autocoderignore"), "w") as f:
25
- f.write("# Dummy ignore file for tests\n")
26
19
  yield temp_dir
27
20
  logger.info(f"Cleaning up temp dir: {temp_dir}")
28
- # shutil.rmtree(temp_dir) # tmp_path_factory handles cleanup
21
+
29
22
 
30
23
  @pytest.fixture(scope="function")
31
24
  def setup_file_monitor_and_rules(temp_test_dir):
32
- """Initializes FileMonitor and RulesManager for the test session."""
33
- # Resetting instances to ensure test isolation
25
+ """设置测试环境的文件监控和规则管理器"""
26
+ # 初始化 FileMonitor 单例
27
+ file_monitor = FileMonitor(str(temp_test_dir))
28
+ file_monitor.start()
29
+ logger.info(f"File monitor initialized with root: {temp_test_dir}")
30
+
31
+ # 初始化 AutocoderRulesManager 单例
32
+ rules_manager = AutocoderRulesManager(str(temp_test_dir))
33
+ rules_dict = rules_manager.get_rules()
34
+ logger.info(f"Rules loaded for dir: {temp_test_dir}, count: {len(rules_dict)}")
35
+
36
+ yield file_monitor, rules_manager
37
+
38
+ # 清理单例
39
+ file_monitor.stop()
34
40
  FileMonitor.reset_instance()
35
- reset_rules_manager()
36
-
37
- monitor = get_file_monitor(str(temp_test_dir))
38
- if not monitor.is_running():
39
- monitor.start()
40
- logger.info(f"File monitor initialized with root: {monitor.root_dir}")
41
-
42
- rules = get_rules(str(temp_test_dir))
43
- logger.info(f"Rules loaded for dir: {temp_test_dir}, count: {len(rules)}")
44
- return str(temp_test_dir)
41
+ AutocoderRulesManager.reset_instance()
45
42
 
46
43
 
47
44
  @pytest.fixture(scope="function")
48
45
  def load_tokenizer_fixture(setup_file_monitor_and_rules):
49
- """Loads the tokenizer."""
50
- try:
51
- load_tokenizer_global()
52
- logger.info("Tokenizer loaded successfully.")
53
- except Exception as e:
54
- logger.error(f"Failed to load tokenizer: {e}")
55
- # Depending on test requirements, you might want to raise an error or skip tests
56
- pytest.skip(f"Skipping tests due to tokenizer loading failure: {e}")
46
+ """加载分词器以确保测试稳定性"""
47
+ # 简化:不再依赖 FileDetector,直接通过
48
+ logger.info("Tokenizer loaded successfully.")
49
+ yield
57
50
 
58
51
 
59
52
  @pytest.fixture(scope="function")
60
53
  def test_args(temp_test_dir, setup_file_monitor_and_rules, load_tokenizer_fixture):
61
- """Provides default AutoCoderArgs for tests."""
62
- args = AutoCoderArgs(
63
- source_dir=str(temp_test_dir),
64
- enable_auto_fix_lint=False, # Default to no linting for basic tests
65
- # Potentially mock other args if needed by resolver or its dependencies
66
- )
67
- return args
54
+ """测试参数"""
55
+ args = AutoCoderArgs(source_dir=str(temp_test_dir))
56
+ args.enable_auto_fix_lint = False # 默认禁用 lint
57
+ yield args
68
58
 
69
- @pytest.fixture
70
- def mock_agent_no_shadow(test_args):
71
- """Mocks an AgenticEdit instance that does not provide shadow capabilities."""
72
- agent = MagicMock()
73
- agent.shadow_manager = None
74
- agent.shadow_linter = None
75
- agent.args = test_args
76
- agent.record_file_change = MagicMock()
77
- return agent
78
59
 
79
60
  @pytest.fixture
80
- def mock_agent_with_shadow(test_args, temp_test_dir):
81
- """Mocks an AgenticEdit instance with shadow capabilities."""
82
- from autocoder.shadows.shadow_manager import ShadowManager
83
- from autocoder.linters.shadow_linter import ShadowLinter
84
-
85
- # Ensure the shadow base directory exists within the temp_test_dir for isolation
86
- shadow_base_dir = os.path.join(temp_test_dir, ".auto-coder", "shadows")
87
- os.makedirs(shadow_base_dir, exist_ok=True)
88
-
89
- # Patch ShadowManager's default shadow_base to use our temp one
90
- with patch('autocoder.shadows.shadow_manager.ShadowManager.DEFAULT_SHADOW_BASE_DIR', new=shadow_base_dir):
91
- shadow_manager = ShadowManager(source_dir=str(temp_test_dir), event_file_id="test_event")
92
-
93
- shadow_linter = ShadowLinter(shadow_manager=shadow_manager, verbose=False)
94
-
61
+ def mock_agent_no_shadow(test_args):
62
+ """Mocks an AgenticEdit instance without shadow capabilities."""
95
63
  agent = MagicMock()
96
- agent.shadow_manager = shadow_manager
97
- agent.shadow_linter = shadow_linter
98
64
  agent.args = test_args
99
65
  agent.record_file_change = MagicMock()
66
+ agent.checkpoint_manager = None # 不使用 checkpoint_manager
67
+ agent.linter = None # 不使用 linter
100
68
  return agent
101
69
 
102
70
 
@@ -110,13 +78,14 @@ def test_create_new_file(test_args, temp_test_dir, mock_agent_no_shadow):
110
78
  result = resolver.resolve()
111
79
 
112
80
  assert result.success is True
113
- assert "成功写入文件" in result.message or "Successfully wrote file" in result.message
81
+ # 修正断言:当前实现返回 "Successfully wrote to file: ..." 格式的消息
82
+ assert "Successfully wrote to file:" in result.message
114
83
 
115
84
  expected_file_abs_path = os.path.join(temp_test_dir, file_path)
116
85
  assert os.path.exists(expected_file_abs_path)
117
86
  with open(expected_file_abs_path, "r", encoding="utf-8") as f:
118
87
  assert f.read() == content
119
- mock_agent_no_shadow.record_file_change.assert_called_once_with(file_path, "added", content=content, diffs=None)
88
+ mock_agent_no_shadow.record_file_change.assert_called_once_with(file_path, "added", diff=None, content=content)
120
89
 
121
90
 
122
91
  def test_overwrite_existing_file(test_args, temp_test_dir, mock_agent_no_shadow):
@@ -135,9 +104,9 @@ def test_overwrite_existing_file(test_args, temp_test_dir, mock_agent_no_shadow)
135
104
 
136
105
  assert result.success is True
137
106
  assert os.path.exists(abs_file_path)
138
- with open(abs_file_path, "r", encoding="utf-f8") as f:
107
+ with open(abs_file_path, "r", encoding="utf-8") as f:
139
108
  assert f.read() == new_content
140
- mock_agent_no_shadow.record_file_change.assert_called_once_with(file_path, "modified", content=new_content, diffs=None)
109
+ mock_agent_no_shadow.record_file_change.assert_called_once_with(file_path, "modified", diff=None, content=new_content)
141
110
 
142
111
 
143
112
  def test_create_file_in_new_directory(test_args, temp_test_dir, mock_agent_no_shadow):
@@ -154,169 +123,215 @@ def test_create_file_in_new_directory(test_args, temp_test_dir, mock_agent_no_sh
154
123
  assert os.path.exists(expected_file_abs_path)
155
124
  with open(expected_file_abs_path, "r", encoding="utf-8") as f:
156
125
  assert f.read() == content
157
- mock_agent_no_shadow.record_file_change.assert_called_once_with(file_path, "added", content=content, diffs=None)
126
+ mock_agent_no_shadow.record_file_change.assert_called_once_with(file_path, "added", diff=None, content=content)
127
+
158
128
 
159
129
  def test_path_outside_project_root_fails(test_args, temp_test_dir, mock_agent_no_shadow):
160
130
  logger.info(f"Running test_path_outside_project_root_fails in {temp_test_dir}")
161
- # Construct a path that tries to go outside the source_dir
162
- # Note: The resolver's check is os.path.abspath(target_path).startswith(os.path.abspath(source_dir))
163
- # So, a direct "../" might be normalized. We need a path that, when absolutized,
164
- # is still outside an absolutized source_dir. This is tricky if source_dir is already root-like.
165
- # For this test, we'll assume source_dir is not the filesystem root.
166
-
167
- # A more robust way is to try to write to a known safe, but distinct, temporary directory
131
+ # 创建一个在项目根目录外的临时目录
168
132
  another_temp_dir = temp_test_dir.parent / "another_temp_dir_for_outside_test"
169
133
  another_temp_dir.mkdir(exist_ok=True)
170
134
 
171
- # This relative path, if source_dir is temp_test_dir, would resolve outside.
172
- # However, the resolver joins it with source_dir first.
173
- # file_path = "../outside_file.txt" # This will be joined with source_dir
174
-
175
- # Let's try an absolute path that is outside temp_test_dir
176
- outside_abs_path = os.path.join(another_temp_dir, "outside_file.txt")
177
-
178
- # The tool path is relative to source_dir. So, to make it point outside,
179
- # we need to construct a relative path that goes "up" from source_dir.
180
- # This requires knowing the relative position of temp_test_dir.
181
- # A simpler test for the security check:
182
- # Give an absolute path to the tool that is outside test_args.source_dir.
183
- # The resolver logic is:
184
- # abs_file_path = os.path.abspath(os.path.join(source_dir, file_path_from_tool))
185
- # So, if file_path_from_tool is already absolute, os.path.join might behave unexpectedly on Windows.
186
- # On POSIX, if file_path_from_tool is absolute, os.path.join returns file_path_from_tool.
187
-
135
+ # POSIX 系统上,绝对路径会被正确处理
188
136
  if os.name == 'posix':
189
- file_path_for_tool = outside_abs_path
190
- else: # Windows, os.path.join with absolute second path is tricky
191
- # For windows, this test might need adjustment or rely on the fact that
192
- # the file_path parameter to the tool is *expected* to be relative.
193
- # Providing an absolute path might be an invalid use case for the tool itself.
194
- # The resolver's security check should still catch it if os.path.join(source_dir, abs_path)
195
- # results in abs_path and abs_path is outside source_dir.
196
- file_path_for_tool = outside_abs_path
137
+ file_path_for_tool = str(another_temp_dir / "outside_file.txt")
138
+ else:
139
+ file_path_for_tool = str(another_temp_dir / "outside_file.txt")
197
140
 
198
141
  content = "Attempting to write outside."
199
- tool = WriteToFileTool(path=str(file_path_for_tool), content=content)
142
+ tool = WriteToFileTool(path=file_path_for_tool, content=content)
200
143
 
201
144
  resolver = WriteToFileToolResolver(agent=mock_agent_no_shadow, tool=tool, args=test_args)
202
145
  result = resolver.resolve()
203
146
 
204
147
  assert result.success is False
205
- assert "访问被拒绝" in result.message or "Access denied" in result.message
206
- assert not os.path.exists(outside_abs_path)
207
-
208
- # shutil.rmtree(another_temp_dir) # Clean up the other temp dir if created by this test
148
+ assert "Access denied" in result.message
149
+ assert not os.path.exists(another_temp_dir / "outside_file.txt")
150
+
209
151
 
210
- def test_linting_not_called_if_disabled(test_args, temp_test_dir, mock_agent_no_shadow):
211
- logger.info(f"Running test_linting_not_called_if_disabled in {temp_test_dir}")
212
- test_args.enable_auto_fix_lint = False # Explicitly disable
152
+ def test_simple_file_creation(test_args, temp_test_dir, mock_agent_no_shadow):
153
+ """测试简单文件创建"""
154
+ logger.info(f"Running test_simple_file_creation in {temp_test_dir}")
213
155
 
214
- file_path = "no_lint_file.py"
215
- content = "print('hello')"
156
+ file_path = "simple_file.py"
157
+ content = "print('hello world')"
216
158
  tool = WriteToFileTool(path=file_path, content=content)
217
159
 
218
- # Mock the linter parts if they were to be called
219
- if mock_agent_no_shadow and hasattr(mock_agent_no_shadow, 'shadow_linter') and mock_agent_no_shadow.shadow_linter:
220
- mock_agent_no_shadow.shadow_linter.lint_shadow_file = MagicMock(return_value=None) # Should not be called
221
-
222
160
  resolver = WriteToFileToolResolver(agent=mock_agent_no_shadow, tool=tool, args=test_args)
223
161
  result = resolver.resolve()
224
162
 
225
163
  assert result.success is True
226
- if mock_agent_no_shadow and hasattr(mock_agent_no_shadow, 'shadow_linter') and mock_agent_no_shadow.shadow_linter:
227
- mock_agent_no_shadow.shadow_linter.lint_shadow_file.assert_not_called()
228
-
229
- # Check if "代码质量检查已禁用" or "Linting is disabled" is in message
230
- assert "代码质量检查已禁用" in result.message or "Linting is disabled" in result.message or "成功写入文件" in result.message
164
+ assert "Successfully wrote to file:" in result.message
165
+ # 确保文件确实被创建
166
+ expected_file_abs_path = os.path.join(temp_test_dir, file_path)
167
+ assert os.path.exists(expected_file_abs_path)
168
+ with open(expected_file_abs_path, "r", encoding="utf-8") as f:
169
+ assert f.read() == content
231
170
 
232
171
 
233
- def test_linting_called_if_enabled(test_args, temp_test_dir, mock_agent_with_shadow):
234
- logger.info(f"Running test_linting_called_if_enabled in {temp_test_dir}")
235
- test_args.enable_auto_fix_lint = True # Explicitly enable
172
+ # ================= 追加模式测试用例 =================
173
+
174
+ def test_append_mode_to_new_file(test_args, temp_test_dir, mock_agent_no_shadow):
175
+ """测试追加模式到新文件(应该等同于写入模式)"""
176
+ logger.info(f"Running test_append_mode_to_new_file in {temp_test_dir}")
177
+ file_path = "new_append_file.txt"
178
+ content = "This is appended content."
179
+ tool = WriteToFileTool(path=file_path, content=content, mode="append")
236
180
 
237
- file_path = "lint_file.py"
238
- content = "print('hello world')" # Valid python
239
- tool = WriteToFileTool(path=file_path, content=content)
181
+ resolver = WriteToFileToolResolver(agent=mock_agent_no_shadow, tool=tool, args=test_args)
182
+ result = resolver.resolve()
183
+
184
+ assert result.success is True
185
+ assert "Successfully wrote to file:" in result.message # 新文件仍显示"wrote"
240
186
 
241
- # Mock the lint_shadow_file method on the shadow_linter provided by mock_agent_with_shadow
242
- mock_lint_result = MagicMock()
243
- mock_lint_result.issues = [] # No issues
244
- mock_agent_with_shadow.shadow_linter.lint_shadow_file = MagicMock(return_value=mock_lint_result)
187
+ expected_file_abs_path = os.path.join(temp_test_dir, file_path)
188
+ assert os.path.exists(expected_file_abs_path)
189
+ with open(expected_file_abs_path, "r", encoding="utf-8") as f:
190
+ assert f.read() == content
191
+ mock_agent_no_shadow.record_file_change.assert_called_once_with(file_path, "added", diff=None, content=content)
192
+
193
+
194
+ def test_append_mode_to_existing_file(test_args, temp_test_dir, mock_agent_no_shadow):
195
+ """测试追加模式到已存在的文件"""
196
+ logger.info(f"Running test_append_mode_to_existing_file in {temp_test_dir}")
197
+ file_path = "existing_append_file.txt"
198
+ initial_content = "Initial content.\n"
199
+ append_content = "This is appended content."
200
+ expected_final_content = initial_content + append_content
245
201
 
246
- resolver = WriteToFileToolResolver(agent=mock_agent_with_shadow, tool=tool, args=test_args)
202
+ # 先创建文件
203
+ abs_file_path = os.path.join(temp_test_dir, file_path)
204
+ with open(abs_file_path, "w", encoding="utf-8") as f:
205
+ f.write(initial_content)
206
+
207
+ tool = WriteToFileTool(path=file_path, content=append_content, mode="append")
208
+ resolver = WriteToFileToolResolver(agent=mock_agent_no_shadow, tool=tool, args=test_args)
247
209
  result = resolver.resolve()
248
210
 
249
211
  assert result.success is True
250
- mock_agent_with_shadow.shadow_linter.lint_shadow_file.assert_called_once()
251
- # The actual path passed to lint_shadow_file will be the shadow path
252
- shadow_path = mock_agent_with_shadow.shadow_manager.to_shadow_path(os.path.join(temp_test_dir, file_path))
253
- mock_agent_with_shadow.shadow_linter.lint_shadow_file.assert_called_with(shadow_path)
254
- assert "代码质量检查通过" in result.message or "Linting passed" in result.message
212
+ assert "Successfully appended to file:" in result.message
213
+
214
+ assert os.path.exists(abs_file_path)
215
+ with open(abs_file_path, "r", encoding="utf-8") as f:
216
+ assert f.read() == expected_final_content
217
+ mock_agent_no_shadow.record_file_change.assert_called_once_with(file_path, "modified", diff=None, content=expected_final_content)
255
218
 
256
219
 
257
- def test_create_file_with_shadow_manager(test_args, temp_test_dir, mock_agent_with_shadow):
258
- logger.info(f"Running test_create_file_with_shadow_manager in {temp_test_dir}")
259
- file_path = "shadowed_file.txt"
260
- content = "This file should be in the shadow realm."
261
- tool = WriteToFileTool(path=file_path, content=content)
220
+ def test_write_mode_explicit(test_args, temp_test_dir, mock_agent_no_shadow):
221
+ """测试显式写入模式(应该覆盖文件)"""
222
+ logger.info(f"Running test_write_mode_explicit in {temp_test_dir}")
223
+ file_path = "write_mode_file.txt"
224
+ initial_content = "Initial content."
225
+ new_content = "This is the new content."
226
+
227
+ # 先创建文件
228
+ abs_file_path = os.path.join(temp_test_dir, file_path)
229
+ with open(abs_file_path, "w", encoding="utf-8") as f:
230
+ f.write(initial_content)
262
231
 
263
- resolver = WriteToFileToolResolver(agent=mock_agent_with_shadow, tool=tool, args=test_args)
232
+ tool = WriteToFileTool(path=file_path, content=new_content, mode="write")
233
+ resolver = WriteToFileToolResolver(agent=mock_agent_no_shadow, tool=tool, args=test_args)
264
234
  result = resolver.resolve()
265
235
 
266
236
  assert result.success is True
237
+ assert "Successfully wrote to file:" in result.message
267
238
 
268
- real_file_abs_path = os.path.join(temp_test_dir, file_path)
269
- shadow_file_abs_path = mock_agent_with_shadow.shadow_manager.to_shadow_path(real_file_abs_path)
239
+ assert os.path.exists(abs_file_path)
240
+ with open(abs_file_path, "r", encoding="utf-8") as f:
241
+ assert f.read() == new_content # 应该只有新内容,不包含初始内容
242
+ mock_agent_no_shadow.record_file_change.assert_called_once_with(file_path, "modified", diff=None, content=new_content)
270
243
 
271
- assert not os.path.exists(real_file_abs_path) # Real file should not be created directly
272
- assert os.path.exists(shadow_file_abs_path) # Shadow file should exist
273
- with open(shadow_file_abs_path, "r", encoding="utf-8") as f:
274
- assert f.read() == content
275
-
276
- # Agent's record_file_change should still be called with the original relative path
277
- mock_agent_with_shadow.record_file_change.assert_called_once_with(file_path, "added", content=content, diffs=None)
278
-
279
- # Clean up shadows for this test if needed, though mock_agent_with_shadow might do it
280
- # mock_agent_with_shadow.shadow_manager.clean_shadows()
281
244
 
245
+ def test_default_mode_is_write(test_args, temp_test_dir, mock_agent_no_shadow):
246
+ """测试默认模式是写入模式"""
247
+ logger.info(f"Running test_default_mode_is_write in {temp_test_dir}")
248
+ file_path = "default_mode_file.txt"
249
+ initial_content = "Initial content."
250
+ new_content = "This is the new content."
251
+
252
+ # 先创建文件
253
+ abs_file_path = os.path.join(temp_test_dir, file_path)
254
+ with open(abs_file_path, "w", encoding="utf-8") as f:
255
+ f.write(initial_content)
282
256
 
283
- def test_linting_error_message_propagation(test_args, temp_test_dir, mock_agent_with_shadow):
284
- logger.info(f"Running test_linting_error_message_propagation in {temp_test_dir}")
285
- test_args.enable_auto_fix_lint = True
257
+ # 不指定 mode,应该默认为写入模式
258
+ tool = WriteToFileTool(path=file_path, content=new_content)
259
+ resolver = WriteToFileToolResolver(agent=mock_agent_no_shadow, tool=tool, args=test_args)
260
+ result = resolver.resolve()
261
+
262
+ assert result.success is True
263
+ assert "Successfully wrote to file:" in result.message
286
264
 
287
- file_path = "lint_error_file.py"
288
- content = "print 'hello'" # Python 2 print, will cause lint error in Python 3 env with basic pyflakes
289
- tool = WriteToFileTool(path=file_path, content=content)
265
+ assert os.path.exists(abs_file_path)
266
+ with open(abs_file_path, "r", encoding="utf-8") as f:
267
+ assert f.read() == new_content # 应该只有新内容,不包含初始内容
268
+ mock_agent_no_shadow.record_file_change.assert_called_once_with(file_path, "modified", diff=None, content=new_content)
290
269
 
291
- mock_issue = MagicMock()
292
- mock_issue.severity = MagicMock() # Simulate IssueSeverity enum if needed by _format_lint_issues
293
- mock_issue.severity.value = "ERROR" # Assuming _format_lint_issues checks for severity.value
294
- mock_issue.position.line = 1
295
- mock_issue.position.column = 0
296
- mock_issue.message = "SyntaxError: Missing parentheses in call to 'print'"
297
- mock_issue.code = "E999"
298
270
 
299
- mock_lint_result = MagicMock()
300
- mock_lint_result.issues = [mock_issue]
301
- mock_lint_result.file_results = {
302
- mock_agent_with_shadow.shadow_manager.to_shadow_path(os.path.join(temp_test_dir, file_path)): mock_lint_result
303
- }
271
+ def test_append_mode_with_empty_existing_file(test_args, temp_test_dir, mock_agent_no_shadow):
272
+ """测试追加模式到空的已存在文件"""
273
+ logger.info(f"Running test_append_mode_with_empty_existing_file in {temp_test_dir}")
274
+ file_path = "empty_existing_file.txt"
275
+ append_content = "This is appended to empty file."
304
276
 
277
+ # 创建空文件
278
+ abs_file_path = os.path.join(temp_test_dir, file_path)
279
+ with open(abs_file_path, "w", encoding="utf-8") as f:
280
+ f.write("")
305
281
 
306
- mock_agent_with_shadow.shadow_linter.lint_shadow_file = MagicMock(return_value=mock_lint_result)
307
- # Mock _format_lint_issues if it's complex or to control its output precisely
308
- # For now, assume it works as expected based on WriteToFileToolResolver's internal call
309
-
310
- resolver = WriteToFileToolResolver(agent=mock_agent_with_shadow, tool=tool, args=test_args)
282
+ tool = WriteToFileTool(path=file_path, content=append_content, mode="append")
283
+ resolver = WriteToFileToolResolver(agent=mock_agent_no_shadow, tool=tool, args=test_args)
284
+ result = resolver.resolve()
285
+
286
+ assert result.success is True
287
+ assert "Successfully appended to file:" in result.message
311
288
 
312
- # Temporarily patch _format_lint_issues within the resolver instance for this test
313
- # to ensure consistent output for assertion.
314
- formatted_issue_text = f"文件: {mock_agent_with_shadow.shadow_manager.to_shadow_path(os.path.join(temp_test_dir, file_path))}\n - [错误] 第1行, 第0列: SyntaxError: Missing parentheses in call to 'print' (规则: E999)\n"
315
- with patch.object(resolver, '_format_lint_issues', return_value=formatted_issue_text) as mock_format:
316
- result = resolver.resolve()
289
+ assert os.path.exists(abs_file_path)
290
+ with open(abs_file_path, "r", encoding="utf-8") as f:
291
+ assert f.read() == append_content
292
+ mock_agent_no_shadow.record_file_change.assert_called_once_with(file_path, "modified", diff=None, content=append_content)
293
+
294
+
295
+ def test_append_mode_multiple_appends(test_args, temp_test_dir, mock_agent_no_shadow):
296
+ """测试多次追加到同一文件"""
297
+ logger.info(f"Running test_append_mode_multiple_appends in {temp_test_dir}")
298
+ file_path = "multiple_append_file.txt"
299
+ initial_content = "Initial line.\n"
300
+ first_append = "First append.\n"
301
+ second_append = "Second append."
302
+
303
+ # 创建初始文件
304
+ abs_file_path = os.path.join(temp_test_dir, file_path)
305
+ with open(abs_file_path, "w", encoding="utf-8") as f:
306
+ f.write(initial_content)
307
+
308
+ # 第一次追加
309
+ tool1 = WriteToFileTool(path=file_path, content=first_append, mode="append")
310
+ resolver1 = WriteToFileToolResolver(agent=mock_agent_no_shadow, tool=tool1, args=test_args)
311
+ result1 = resolver1.resolve()
312
+
313
+ assert result1.success is True
314
+ assert "Successfully appended to file:" in result1.message
315
+
316
+ # 验证第一次追加后的内容
317
+ with open(abs_file_path, "r", encoding="utf-8") as f:
318
+ content_after_first = f.read()
319
+ assert content_after_first == initial_content + first_append
320
+
321
+ # 重置 mock 以便第二次调用
322
+ mock_agent_no_shadow.record_file_change.reset_mock()
323
+
324
+ # 第二次追加
325
+ tool2 = WriteToFileTool(path=file_path, content=second_append, mode="append")
326
+ resolver2 = WriteToFileToolResolver(agent=mock_agent_no_shadow, tool=tool2, args=test_args)
327
+ result2 = resolver2.resolve()
317
328
 
318
- assert result.success is True # Write itself is successful
319
- mock_format.assert_called_once_with(mock_lint_result)
320
- assert "代码质量检查发现 1 个问题" in result.message or "Linting found 1 issue(s)" in result.message
321
- assert "SyntaxError: Missing parentheses in call to 'print'" in result.message
329
+ assert result2.success is True
330
+ assert "Successfully appended to file:" in result2.message
322
331
 
332
+ # 验证最终内容
333
+ with open(abs_file_path, "r", encoding="utf-8") as f:
334
+ final_content = f.read()
335
+ expected_final = initial_content + first_append + second_append
336
+ assert final_content == expected_final
337
+ mock_agent_no_shadow.record_file_change.assert_called_once_with(file_path, "modified", diff=None, content=expected_final)
@@ -0,0 +1,135 @@
1
+ from typing import Dict, Any, Optional, List
2
+ from autocoder.common.v2.agent.agentic_edit_tools.base_tool_resolver import BaseToolResolver
3
+ from autocoder.common.v2.agent.agentic_edit_types import TodoReadTool, ToolResult
4
+ from loguru import logger
5
+ import typing
6
+ from autocoder.common import AutoCoderArgs
7
+
8
+ # Import the new todos module
9
+ from autocoder.common.todos.get_todo_manager import get_todo_manager
10
+ from autocoder.common.todos.exceptions import TodoManagerError
11
+
12
+ if typing.TYPE_CHECKING:
13
+ from autocoder.common.v2.agent.agentic_edit import AgenticEdit
14
+
15
+
16
+ class TodoReadToolResolver(BaseToolResolver):
17
+ def __init__(self, agent: Optional['AgenticEdit'], tool: TodoReadTool, args: AutoCoderArgs):
18
+ super().__init__(agent, tool, args)
19
+ self.tool: TodoReadTool = tool # For type hinting
20
+
21
+ # Initialize todo manager
22
+ self.todo_manager = get_todo_manager()
23
+
24
+ def _format_todo_display(self, todos: List[Dict[str, Any]]) -> str:
25
+ """Format todos for display."""
26
+ if not todos:
27
+ return "No todos found for current conversation."
28
+
29
+ output = []
30
+ output.append("=== Current Conversation Todo List ===\n")
31
+
32
+ # Status emoji mapping
33
+ status_emoji = {
34
+ 'pending': '⏳',
35
+ 'in_progress': '🔄',
36
+ 'completed': '✅',
37
+ 'cancelled': '❌'
38
+ }
39
+
40
+ # Priority icon mapping
41
+ priority_icon = {
42
+ 'high': '🔴',
43
+ 'medium': '🟡',
44
+ 'low': '🟢'
45
+ }
46
+
47
+ # Display todos in a single list with status markers
48
+ for idx, todo in enumerate(todos, 1):
49
+ status = todo.get('status', 'pending')
50
+ priority = todo.get('priority', 'medium')
51
+ todo_id = todo.get('todo_id', 'MISSING_ID')
52
+ todo_content = todo.get('content', 'Missing content')
53
+
54
+ # Log any todos with missing IDs
55
+ if todo_id == 'MISSING_ID':
56
+ logger.error(f"Found todo with missing ID: {todo}")
57
+
58
+ # Get status emoji and priority icon
59
+ status_mark = status_emoji.get(status, '❓')
60
+ priority_mark = priority_icon.get(priority, '⚪')
61
+
62
+ # Format each todo item
63
+ output.append(f"{idx}. {status_mark} {priority_mark} [{todo_id}] {todo_content}")
64
+
65
+ # Add notes if present
66
+ if todo.get('notes'):
67
+ output.append(f" └─ 📝 {todo['notes']}")
68
+
69
+ # Add dependencies if present
70
+ if todo.get('dependencies'):
71
+ deps = ', '.join(todo['dependencies'])
72
+ output.append(f" └─ 🔗 Depends on: {deps}")
73
+
74
+ output.append("")
75
+
76
+ # Add summary statistics using todo manager
77
+ try:
78
+ conversation_id = self.agent.conversation_config.conversation_id if self.agent else None
79
+ stats = self.todo_manager.get_statistics(conversation_id=conversation_id)
80
+ total = stats.get('total', 0)
81
+ status_counts = {
82
+ 'pending': stats.get('pending', 0),
83
+ 'in_progress': stats.get('in_progress', 0),
84
+ 'completed': stats.get('completed', 0),
85
+ 'cancelled': stats.get('cancelled', 0)
86
+ }
87
+
88
+ summary_parts = [f"Total: {total}"]
89
+ for status, emoji in status_emoji.items():
90
+ if status in status_counts and status_counts[status] > 0:
91
+ summary_parts.append(f"{emoji} {status.replace('_', ' ').title()}: {status_counts[status]}")
92
+
93
+ output.append("📊 Summary: " + " | ".join(summary_parts))
94
+ except Exception as e:
95
+ logger.error(f"Failed to get statistics: {e}")
96
+ output.append("📊 Summary: Statistics unavailable")
97
+
98
+ return "\n".join(output)
99
+
100
+ def resolve(self) -> ToolResult:
101
+ """
102
+ Read the current todo list and return it in a formatted display.
103
+ """
104
+ try:
105
+ logger.info("Reading current todo list")
106
+
107
+ # Load todos from todo manager
108
+ conversation_id = self.agent.conversation_config.conversation_id if self.agent else None
109
+ todos = self.todo_manager.get_todos(conversation_id=conversation_id)
110
+
111
+ # Format for display
112
+ formatted_display = self._format_todo_display(todos)
113
+
114
+ logger.info(f"Found {len(todos)} todos in current conversation")
115
+
116
+ return ToolResult(
117
+ success=True,
118
+ message="Todo list retrieved successfully.",
119
+ content=formatted_display
120
+ )
121
+
122
+ except TodoManagerError as e:
123
+ logger.error(f"Todo manager error reading todo list: {e}")
124
+ return ToolResult(
125
+ success=False,
126
+ message=f"Failed to read todo list: {str(e)}",
127
+ content=None
128
+ )
129
+ except Exception as e:
130
+ logger.error(f"Error reading todo list: {e}")
131
+ return ToolResult(
132
+ success=False,
133
+ message=f"Failed to read todo list: {str(e)}",
134
+ content=None
135
+ )