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,8 +1,7 @@
1
- from itertools import product
1
+ from autocoder.common.project_scanner.compat import create_scanner_functions
2
2
  from rich.console import Console
3
3
  from rich.panel import Panel
4
4
  from prompt_toolkit.formatted_text import HTML
5
- from prompt_toolkit.shortcuts import radiolist_dialog
6
5
  from prompt_toolkit import prompt
7
6
  import os
8
7
  import yaml
@@ -10,73 +9,106 @@ import json
10
9
  import sys
11
10
  import io
12
11
  import uuid
13
- import glob
14
12
  import time
15
- import datetime
16
- import hashlib
13
+ import byzerllm
14
+ import subprocess
17
15
  from contextlib import contextmanager
18
16
  from typing import List, Dict, Any, Optional
19
17
  from autocoder.common import AutoCoderArgs
18
+ from autocoder.common.autocoderargs_parser import AutoCoderArgsParser
20
19
  from pydantic import BaseModel
21
- from autocoder.common.action_yml_file_manager import ActionYmlFileManager
22
20
  from autocoder.common.result_manager import ResultManager
23
21
  from autocoder.version import __version__
24
22
  from autocoder.auto_coder import main as auto_coder_main
25
23
  from autocoder.utils import get_last_yaml_file
26
- from autocoder.commands.auto_command import CommandAutoTuner, AutoCommandRequest, CommandConfig, MemoryConfig
27
- from autocoder.common.v2.agent.agentic_edit import AgenticEdit,AgenticEditRequest
28
- from autocoder.common.v2.agent.agentic_edit_types import AgenticEditConversationConfig
24
+ from autocoder.commands.auto_command import (
25
+ CommandAutoTuner,
26
+ AutoCommandRequest,
27
+ CommandConfig,
28
+ MemoryConfig,
29
+ )
30
+ from autocoder.common.v2.agent.agentic_edit import AgenticEditRequest
31
+ from autocoder.common.v2.agent.agentic_edit_types import (
32
+ AgenticEditConversationConfig,
33
+ ConversationAction,
34
+ )
35
+ from autocoder.common.conversations.get_conversation_manager import (
36
+ get_conversation_manager,
37
+ )
29
38
  from autocoder.index.symbols_utils import (
30
- extract_symbols,
31
39
  SymbolType,
32
40
  )
33
41
  import platform
34
- import subprocess
35
- from rich.console import Console
36
42
  from rich.panel import Panel
37
43
  from rich.table import Table
38
- from rich.live import Live
39
- # Removed Text import as it's only used in the deleted print_conf
40
- from rich.live import Live
44
+ from copy import deepcopy
45
+
41
46
  from rich.markdown import Markdown
42
47
  from byzerllm.utils.nontext import Image
43
- import git
48
+ from autocoder.inner.agentic import RunAgentic
49
+
50
+ # 延迟导入git模块以避免启动异常
51
+ try:
52
+ import git
53
+
54
+ GIT_AVAILABLE = True
55
+ except ImportError:
56
+ from loguru import logger
57
+
58
+ logger.warning("Git module not available. Some git features will be disabled.")
59
+ GIT_AVAILABLE = False
60
+ git = None
44
61
  from autocoder.common import git_utils
45
- from autocoder.chat_auto_coder_lang import get_message,get_message_with_format
62
+ from autocoder.chat_auto_coder_lang import get_message, get_message_with_format
46
63
  from autocoder.agent.auto_guess_query import AutoGuessQuery
47
- from autocoder.common.mcp_server import get_mcp_server
48
- from autocoder.common.mcp_server_types import (
49
- McpRequest, McpInstallRequest, McpRemoveRequest, McpListRequest,
50
- McpListRunningRequest, McpRefreshRequest, McpServerInfoRequest
51
- )
52
- import byzerllm
53
- from byzerllm.utils import format_str_jinja2
54
- from autocoder.common.memory_manager import get_global_memory_file_paths
55
- from autocoder import models as models_module
56
- import shlex
64
+
65
+ # Do not remove these imports, they are exported to other modules e.g. chat_auto_coder.py
66
+ from autocoder.common.mcp_tools.server import get_mcp_server
67
+ from autocoder.common.memory_manager import get_global_memory_file_paths
68
+
69
+ from rich.panel import Panel
70
+ from rich.table import Table
71
+ from autocoder.common.international import get_message, get_message_with_format
72
+ from rich.prompt import Confirm
73
+ from autocoder.common.printer import Printer
57
74
  from autocoder.utils.llms import get_single_llm
58
- import fnmatch
59
- import pkg_resources
75
+ import importlib.resources as resources
60
76
  from autocoder.common.printer import Printer
61
- from autocoder.utils.thread_utils import run_in_raw_thread
62
- from autocoder.memory.active_context_manager import ActiveContextManager
63
- from autocoder.common.command_completer import CommandCompleter,FileSystemModel as CCFileSystemModel,MemoryConfig as CCMemoryModel
77
+ from autocoder.common.command_completer import MemoryConfig as CCMemoryModel
64
78
  from autocoder.common.conf_validator import ConfigValidator
65
79
  from autocoder.common.ac_style_command_parser import parse_query
66
80
  from loguru import logger as global_logger
67
81
  from autocoder.utils.project_structure import EnhancedFileAnalyzer
68
- from autocoder.common import SourceCodeList,SourceCode
82
+ from autocoder.common import SourceCode
69
83
  from autocoder.common.file_monitor import FileMonitor
70
- from filelock import FileLock
71
84
  from autocoder.common.command_file_manager import CommandManager
85
+ from autocoder.common.v2.agent.runner import (
86
+ SdkRunner,
87
+ TerminalRunner,
88
+ FileBasedEventRunner,
89
+ )
90
+ from autocoder.completer import CommandCompleterV2
91
+ from autocoder.common.core_config import get_memory_manager, load_memory as _load_memory
92
+ from autocoder.common.global_cancel import global_cancel
93
+ from autocoder.inner.async_command_handler import AsyncCommandHandler
94
+ from autocoder.inner.queue_command_handler import QueueCommandHandler
95
+ from autocoder.inner.conversation_command_handlers import (
96
+ ConversationNewCommandHandler,
97
+ ConversationResumeCommandHandler,
98
+ ConversationListCommandHandler,
99
+ ConversationRenameCommandHandler,
100
+ ConversationCommandCommandHandler,
101
+ )
102
+
103
+ # 对外API,用于第三方集成 auto-coder 使用。
72
104
 
73
105
 
74
- ## 对外API,用于第三方集成 auto-coder 使用。
75
106
  class SymbolItem(BaseModel):
76
107
  symbol_name: str
77
108
  symbol_type: SymbolType
78
109
  file_name: str
79
110
 
111
+
80
112
  class InitializeSystemRequest(BaseModel):
81
113
  product_mode: str
82
114
  skip_provider_selection: bool
@@ -92,20 +124,45 @@ if platform.system() == "Windows":
92
124
  init()
93
125
 
94
126
 
95
- memory = {
96
- "conversation": [],
97
- "current_files": {"files": [], "groups": {}},
98
- "conf": {},
99
- "exclude_dirs": [],
100
- "mode": "auto_detect", # 新增mode字段,默认为 auto_detect 模式
101
- }
102
-
127
+ # Initialize memory and project root
103
128
  project_root = os.getcwd()
104
129
 
130
+ # Initialize memory manager with project root
131
+ _memory_manager = get_memory_manager(project_root)
132
+
133
+ # Wrapper functions to sync global memory variable
134
+
135
+
136
+ def save_memory():
137
+ """Save memory - compatibility function (no-op since MemoryManager handles persistence)"""
138
+ # This function is kept for backward compatibility but does nothing
139
+ # since MemoryManager automatically handles persistence
140
+ raise NotImplementedError(
141
+ "save_memory is not supported anymore, please use autocoder.common.core_config.memory_manager instead."
142
+ )
143
+
144
+
145
+ def load_memory():
146
+ """Load memory using MemoryManager"""
147
+ return _load_memory()
148
+
149
+
150
+ def get_memory():
151
+ """Get current memory"""
152
+ return load_memory()
105
153
 
106
- base_persist_dir = os.path.join(project_root,".auto-coder", "plugins", "chat-auto-coder")
107
154
 
108
- defaut_exclude_dirs = [".git", "node_modules", "dist", "build", "__pycache__",".auto-coder"]
155
+ # Compatibility: base_persist_dir is now managed by memory manager
156
+ base_persist_dir = _memory_manager.base_persist_dir
157
+
158
+ defaut_exclude_dirs = [
159
+ ".git",
160
+ "node_modules",
161
+ "dist",
162
+ "build",
163
+ "__pycache__",
164
+ ".auto-coder",
165
+ ]
109
166
 
110
167
  commands = [
111
168
  "/add_files",
@@ -122,7 +179,7 @@ commands = [
122
179
  "/index/build",
123
180
  "/index/export",
124
181
  "/index/import",
125
- "/exclude_files",
182
+ "/exclude_files",
126
183
  "/help",
127
184
  "/shell",
128
185
  "/voice_input",
@@ -137,21 +194,65 @@ commands = [
137
194
  "/conf/export",
138
195
  "/conf/import",
139
196
  "/exclude_dirs",
197
+ "/queue",
140
198
  ]
141
199
 
200
+
142
201
  def load_tokenizer():
143
202
  from autocoder.rag.variable_holder import VariableHolder
144
- from tokenizers import Tokenizer
203
+ from tokenizers import Tokenizer
204
+
145
205
  try:
146
- tokenizer_path = pkg_resources.resource_filename(
147
- "autocoder", "data/tokenizer.json"
148
- )
206
+ tokenizer_path = str(resources.files("autocoder") / "data" / "tokenizer.json")
149
207
  VariableHolder.TOKENIZER_PATH = tokenizer_path
150
208
  VariableHolder.TOKENIZER_MODEL = Tokenizer.from_file(tokenizer_path)
151
209
  except FileNotFoundError:
152
210
  tokenizer_path = None
153
211
 
154
212
 
213
+ def configure_logger():
214
+ # 设置日志目录和文件
215
+ log_dir = os.path.join(project_root, ".auto-coder", "logs")
216
+ os.makedirs(log_dir, exist_ok=True)
217
+ log_file = os.path.join(log_dir, "auto-coder.log")
218
+
219
+ # 配置全局日志
220
+ # 默认情况下,所有日志都写入文件
221
+ # 控制台上默认不输出任何日志,除非显式配置
222
+ global_logger.configure(
223
+ handlers=[
224
+ {
225
+ "sink": log_file,
226
+ "level": "INFO",
227
+ "rotation": "10 MB",
228
+ "retention": "1 week",
229
+ "format": "{time:YYYY-MM-DD HH:mm:ss} | {level} | {name} | {message}",
230
+ },
231
+ {
232
+ "sink": sys.stdout,
233
+ "level": "INFO",
234
+ "format": "{time:YYYY-MM-DD HH:mm:ss} | {name} | {message}",
235
+ # 默认不打印任何日志到控制台
236
+ "filter": lambda record: False,
237
+ },
238
+ ]
239
+ )
240
+
241
+
242
+ def init_singleton_instances():
243
+ # 初始化文件监控系统
244
+ try:
245
+ FileMonitor(project_root).start()
246
+ except Exception as e:
247
+ global_logger.error(f"Failed to start file monitor: {e}")
248
+ global_logger.exception(e)
249
+
250
+ # 初始化忽略文件管理器
251
+ from autocoder.common.ignorefiles.ignore_file_utils import IgnoreFileManager
252
+
253
+ _ = IgnoreFileManager(project_root=project_root)
254
+
255
+
155
256
  def configure_project_type():
156
257
  from prompt_toolkit.lexers import PygmentsLexer
157
258
  from pygments.lexers.markup import MarkdownLexer
@@ -173,12 +274,10 @@ def configure_project_type():
173
274
  print_formatted_text(HTML(f"<info>{escape(text)}</info>"), style=style)
174
275
 
175
276
  def print_warning(text):
176
- print_formatted_text(
177
- HTML(f"<warning>{escape(text)}</warning>"), style=style)
277
+ print_formatted_text(HTML(f"<warning>{escape(text)}</warning>"), style=style)
178
278
 
179
279
  def print_header(text):
180
- print_formatted_text(
181
- HTML(f"<header>{escape(text)}</header>"), style=style)
280
+ print_formatted_text(HTML(f"<header>{escape(text)}</header>"), style=style)
182
281
 
183
282
  print_header(f"\n=== {get_message('project_type_config')} ===\n")
184
283
  print_info(get_message("project_type_supports"))
@@ -188,7 +287,7 @@ def configure_project_type():
188
287
  print_info(get_message("examples"))
189
288
 
190
289
  print_warning(f"{get_message('default_type')}\n")
191
-
290
+
192
291
  extensions = get_all_extensions(project_root) or "py"
193
292
  project_type = prompt(
194
293
  get_message("enter_project_type"), default=extensions, style=style
@@ -215,72 +314,27 @@ def get_all_extensions(directory: str = ".") -> str:
215
314
  target_file="",
216
315
  git_url="",
217
316
  project_type="",
218
- conversation_prune_safe_zone_tokens=0
317
+ conversation_prune_safe_zone_tokens=0,
219
318
  )
220
-
319
+
221
320
  analyzer = EnhancedFileAnalyzer(
222
321
  args=args,
223
322
  llm=None, # 如果只是获取后缀名,可以不需要LLM
224
- config=None # 使用默认配置
323
+ config=None, # 使用默认配置
225
324
  )
226
-
325
+
227
326
  # 获取分析结果
228
327
  analysis_result = analyzer.analyze_extensions()
229
-
328
+
230
329
  # 合并 code 和 config 的后缀名
231
330
  all_extensions = set(analysis_result["code"] + analysis_result["config"])
232
-
331
+
233
332
  # 转换为逗号分隔的字符串
234
333
  return ",".join(sorted(all_extensions))
235
334
 
236
- def configure_logger():
237
- # 设置日志目录和文件
238
- log_dir = os.path.join(project_root, ".auto-coder", "logs")
239
- os.makedirs(log_dir, exist_ok=True)
240
- log_file = os.path.join(log_dir, "auto-coder.log")
241
-
242
- # 配置全局日志
243
- # 默认情况下,所有日志都写入文件
244
- # 控制台上默认不输出任何日志,除非显式配置
245
- global_logger.configure(
246
- handlers=[
247
- {
248
- "sink": log_file,
249
- "level": "INFO",
250
- "rotation": "10 MB",
251
- "retention": "1 week",
252
- "format": "{time:YYYY-MM-DD HH:mm:ss} | {level} | {name} | {message}",
253
- },
254
- {
255
- "sink": sys.stdout,
256
- "level": "INFO",
257
- "format": "{time:YYYY-MM-DD HH:mm:ss} | {name} | {message}",
258
- # 默认不打印任何日志到控制台
259
- "filter": lambda record: False
260
- }
261
- ]
262
- )
263
-
264
-
265
- def init_singleton_instances():
266
- # 初始化文件监控系统
267
- try:
268
- FileMonitor(project_root).start()
269
- except Exception as e:
270
- global_logger.error(f"Failed to start file monitor: {e}")
271
- global_logger.exception(e)
272
-
273
- # 初始化规则文件管理器
274
- from autocoder.common.rulefiles.autocoderrules_utils import get_rules
275
- get_rules(project_root=project_root)
276
-
277
- # 初始化忽略文件管理器
278
- from autocoder.common.ignorefiles.ignore_file_utils import IgnoreFileManager
279
- _ = IgnoreFileManager(project_root=project_root)
280
-
281
335
 
282
- def start():
283
- if os.environ.get('autocoder_auto_init',"true") in ["true","True","True",True]:
336
+ def start():
337
+ if os.environ.get("autocoder_auto_init", "true") in ["true", "True", "True", True]:
284
338
  configure_logger()
285
339
  init_singleton_instances()
286
340
 
@@ -292,7 +346,7 @@ def start():
292
346
  # name = f"{time_str}-{str(uuid.uuid4())}"
293
347
  # conversation_id = conversation_manager.create_new_conversation(name=name,description="")
294
348
  # conversation_manager.set_current_conversation(conversation_id)
295
-
349
+
296
350
 
297
351
  def stop():
298
352
  try:
@@ -301,9 +355,11 @@ def stop():
301
355
  global_logger.error(f"Failed to stop file monitor: {e}")
302
356
  global_logger.exception(e)
303
357
 
304
- def initialize_system(args:InitializeSystemRequest):
358
+
359
+ def initialize_system(args: InitializeSystemRequest):
305
360
  from autocoder.utils.model_provider_selector import ModelProviderSelector
306
- from autocoder import models as models_module
361
+ from autocoder.common.llms import LLMManager
362
+
307
363
  print(f"\n\033[1;34m{get_message('initializing')}\033[0m")
308
364
 
309
365
  first_time = [False]
@@ -319,54 +375,42 @@ def initialize_system(args:InitializeSystemRequest):
319
375
  else:
320
376
  print(f" {message}")
321
377
 
322
- def init_project():
323
- if not os.path.exists(".auto-coder") or not os.path.exists("actions"):
324
- first_time[0] = True
325
- print_status(get_message("not_initialized"), "warning")
326
- init_choice = input(
327
- f" {get_message('init_prompt')}").strip().lower()
328
- if init_choice == "y":
329
- try:
330
- subprocess.run(
331
- ["auto-coder", "init", "--source_dir", "."], check=True
332
- )
333
- print_status(get_message("init_success"), "success")
334
- except subprocess.CalledProcessError:
335
- print_status(get_message("init_fail"), "error")
336
- print_status(get_message("init_manual"), "warning")
337
- exit(1)
338
- else:
339
- print_status(get_message("exit_no_init"), "warning")
340
- exit(1)
341
-
342
378
  if not os.path.exists(base_persist_dir):
343
379
  os.makedirs(base_persist_dir, exist_ok=True)
344
- print_status(get_message_with_format("created_dir",path=base_persist_dir), "success")
380
+ print_status(
381
+ get_message_with_format("created_dir", path=base_persist_dir), "success"
382
+ )
345
383
 
346
384
  if first_time[0]:
347
- configure_project_type()
385
+ configure("project_type:*", skip_print=True)
348
386
  configure_success[0] = True
349
387
 
350
388
  print_status(get_message("init_complete"), "success")
351
389
 
352
- init_project()
390
+ init_project_if_required(target_dir=project_root, project_type="*")
353
391
 
354
392
  if not args.skip_provider_selection and first_time[0]:
355
- if args.product_mode == "lite":
356
- ## 如果已经是配置过的项目,就无需再选择
393
+ if args.product_mode == "lite":
394
+ # 如果已经是配置过的项目,就无需再选择
357
395
  if first_time[0]:
358
- if not models_module.check_model_exists("v3_chat") or not models_module.check_model_exists("r1_chat"):
396
+ llm_manager = LLMManager()
397
+ if not llm_manager.check_model_exists(
398
+ "v3_chat"
399
+ ) or not llm_manager.check_model_exists("r1_chat"):
359
400
  model_provider_selector = ModelProviderSelector()
360
401
  model_provider_info = model_provider_selector.select_provider()
361
402
  if model_provider_info is not None:
362
- models_json_list = model_provider_selector.to_models_json(model_provider_info)
363
- models_module.add_and_activate_models(models_json_list)
403
+ models_json_list = model_provider_selector.to_models_json(
404
+ model_provider_info
405
+ )
406
+ llm_manager.add_models(models_json_list)
364
407
 
365
408
  if args.product_mode == "pro":
366
409
  # Check if Ray is running
367
410
  print_status(get_message("checking_ray"), "")
368
411
  ray_status = subprocess.run(
369
- ["ray", "status"], capture_output=True, text=True)
412
+ ["ray", "status"], capture_output=True, text=True
413
+ )
370
414
  if ray_status.returncode != 0:
371
415
  print_status(get_message("ray_not_running"), "warning")
372
416
  try:
@@ -389,7 +433,6 @@ def initialize_system(args:InitializeSystemRequest):
389
433
  )
390
434
  if result.returncode == 0:
391
435
  print_status(get_message("model_available"), "success")
392
- init_project()
393
436
  print_status(get_message("init_complete_final"), "success")
394
437
  return
395
438
  except subprocess.TimeoutExpired:
@@ -428,7 +471,6 @@ def initialize_system(args:InitializeSystemRequest):
428
471
  print_status(get_message("deploy_fail"), "error")
429
472
  return
430
473
 
431
-
432
474
  deploy_cmd = [
433
475
  "byzerllm",
434
476
  "deploy",
@@ -471,14 +513,18 @@ def initialize_system(args:InitializeSystemRequest):
471
513
  print_status(get_message("manual_start"), "warning")
472
514
  print_status("easy-byzerllm chat v3_chat 你好", "")
473
515
 
474
- print_status(get_message("init_complete_final"), "success")
516
+ print_status(get_message("init_complete_final"), "success")
475
517
  configure_success[0] = True
476
518
 
477
519
  if first_time[0] and args.product_mode == "pro" and configure_success[0]:
478
- configure(f"model:v3_chat", skip_print=True)
520
+ configure(f"model:v3_chat", skip_print=True)
479
521
 
480
- if first_time[0] and args.product_mode == "lite" and models_module.check_model_exists("v3_chat"):
481
- configure(f"model:v3_chat", skip_print=True)
522
+ if (
523
+ first_time[0]
524
+ and args.product_mode == "lite"
525
+ and LLMManager().check_model_exists("v3_chat")
526
+ ):
527
+ configure(f"model:v3_chat", skip_print=True)
482
528
 
483
529
 
484
530
  def convert_yaml_config_to_str(yaml_config):
@@ -491,92 +537,44 @@ def convert_yaml_config_to_str(yaml_config):
491
537
  return yaml_content
492
538
 
493
539
 
494
- def get_all_file_names_in_project() -> List[str]:
495
-
496
- file_names = []
497
- final_exclude_dirs = defaut_exclude_dirs + memory.get("exclude_dirs", [])
498
- for root, dirs, files in os.walk(project_root, followlinks=True):
499
- dirs[:] = [d for d in dirs if d not in final_exclude_dirs]
500
- file_names.extend(files)
501
- return file_names
502
-
503
-
504
- def get_all_file_in_project() -> List[str]:
505
-
506
- file_names = []
507
- final_exclude_dirs = defaut_exclude_dirs + memory.get("exclude_dirs", [])
508
- for root, dirs, files in os.walk(project_root, followlinks=True):
509
- dirs[:] = [d for d in dirs if d not in final_exclude_dirs]
510
- for file in files:
511
- file_names.append(os.path.join(root, file))
512
- return file_names
513
-
514
-
515
- def get_all_file_in_project_with_dot() -> List[str]:
516
- file_names = []
517
- final_exclude_dirs = defaut_exclude_dirs + memory.get("exclude_dirs", [])
518
- for root, dirs, files in os.walk(project_root, followlinks=True):
519
- dirs[:] = [d for d in dirs if d not in final_exclude_dirs]
520
- for file in files:
521
- file_names.append(os.path.join(
522
- root, file).replace(project_root, "."))
523
- return file_names
524
-
525
-
526
- def get_all_dir_names_in_project() -> List[str]:
527
- dir_names = []
528
- final_exclude_dirs = defaut_exclude_dirs + memory.get("exclude_dirs", [])
529
- for root, dirs, files in os.walk(project_root, followlinks=True):
530
- dirs[:] = [d for d in dirs if d not in final_exclude_dirs]
531
- for dir in dirs:
532
- dir_names.append(dir)
533
- return dir_names
534
-
535
-
536
- def find_files_in_project(patterns: List[str]) -> List[str]:
537
- matched_files = []
538
- final_exclude_dirs = defaut_exclude_dirs + memory.get("exclude_dirs", [])
539
-
540
- for pattern in patterns:
541
- if "*" in pattern or "?" in pattern:
542
- for file_path in glob.glob(pattern, recursive=True):
543
- if os.path.isfile(file_path):
544
- abs_path = os.path.abspath(file_path)
545
- if not any(
546
- exclude_dir in abs_path.split(os.sep)
547
- for exclude_dir in final_exclude_dirs
548
- ):
549
- matched_files.append(abs_path)
550
- else:
551
- is_added = False
552
- # add files belongs to project
553
- for root, dirs, files in os.walk(project_root, followlinks=True):
554
- dirs[:] = [d for d in dirs if d not in final_exclude_dirs]
555
- if pattern in files:
556
- matched_files.append(os.path.join(root, pattern))
557
- is_added = True
558
- else:
559
- for file in files:
560
- _pattern = os.path.abspath(pattern)
561
- if _pattern in os.path.join(root, file):
562
- matched_files.append(os.path.join(root, file))
563
- is_added = True
564
- # add files not belongs to project
565
- if not is_added:
566
- matched_files.append(pattern)
567
-
568
- return list(set(matched_files))
569
-
570
-
571
540
  def convert_config_value(key, value):
541
+ # 定义需要使用 token 解析的字段
542
+ token_fields = {
543
+ "conversation_prune_safe_zone_tokens",
544
+ "context_prune_safe_zone_tokens",
545
+ "context_prune_sliding_window_size",
546
+ "context_prune_sliding_window_overlap",
547
+ "rag_params_max_tokens",
548
+ "rag_context_window_limit",
549
+ "rag_duckdb_vector_dim",
550
+ "rag_duckdb_query_top_k",
551
+ "rag_emb_dim",
552
+ "rag_emb_text_size",
553
+ "hybrid_index_max_output_tokens",
554
+ "data_cells_max_num",
555
+ }
556
+
572
557
  field_info = AutoCoderArgs.model_fields.get(key)
573
558
  if field_info:
559
+ # 对于需要 token 解析的字段,使用 AutoCoderArgsParser
560
+ if key in token_fields:
561
+ try:
562
+ parser = AutoCoderArgsParser()
563
+ return parser.parse_token_field(key, value)
564
+ except Exception as e:
565
+ print(
566
+ f"Warning: Failed to parse token field '{key}' with value '{value}': {e}"
567
+ )
568
+ # 如果解析失败,fallback 到原有逻辑
569
+ pass
570
+
571
+ # 原有的类型转换逻辑
574
572
  if isinstance(value, str) and value.lower() in ["true", "false"]:
575
573
  return value.lower() == "true"
576
- elif "int" in str(field_info.annotation):
577
- return int(value)
578
574
  elif "float" in str(field_info.annotation):
579
575
  return float(value)
576
+ elif "int" in str(field_info.annotation):
577
+ return int(value)
580
578
  else:
581
579
  return value
582
580
  else:
@@ -594,14 +592,14 @@ def redirect_stdout():
594
592
  sys.stdout = original_stdout
595
593
 
596
594
 
597
- def configure(conf: str, skip_print=False):
595
+ def configure(conf: str, skip_print=True):
598
596
  printer = Printer()
597
+ memory_manager = get_memory_manager()
599
598
  parts = conf.split(None, 1)
600
599
  if len(parts) == 2 and parts[0] in ["/drop", "/unset", "/remove"]:
601
600
  key = parts[1].strip()
602
- if key in memory["conf"]:
603
- del memory["conf"][key]
604
- save_memory()
601
+ if memory_manager.has_config(key):
602
+ memory_manager.delete_config(key)
605
603
  printer.print_in_terminal("config_delete_success", style="green", key=key)
606
604
  else:
607
605
  printer.print_in_terminal("config_not_found", style="yellow", key=key)
@@ -616,109 +614,75 @@ def configure(conf: str, skip_print=False):
616
614
  if not value:
617
615
  printer.print_in_terminal("config_value_empty", style="red")
618
616
  return
619
- product_mode = memory["conf"].get("product_mode",None)
617
+ product_mode = memory_manager.get_config("product_mode", None)
620
618
  if product_mode:
621
- ConfigValidator.validate(key, value, product_mode)
622
- memory["conf"][key] = value
623
- save_memory()
619
+ ConfigValidator.validate(key, value, product_mode)
620
+ memory_manager.set_config(key, value)
624
621
  if not skip_print:
625
- printer.print_in_terminal("config_set_success", style="green", key=key, value=value)
622
+ printer.print_in_terminal(
623
+ "config_set_success", style="green", key=key, value=value
624
+ )
625
+
626
626
 
627
627
  # word_completer = WordCompleter(commands)
628
628
 
629
629
 
630
- def get_symbol_list() -> List[SymbolItem]:
631
- list_of_symbols = []
632
- index_file = os.path.join(".auto-coder", "index.json")
630
+ # Memory management functions are now imported from core_config module
631
+ # Helper functions to access memory without global variables
632
+ def get_current_memory():
633
+ """Get current memory as dictionary for backward compatibility"""
634
+ return get_memory()
633
635
 
634
- if os.path.exists(index_file):
635
- with open(index_file, "r",encoding="utf-8") as file:
636
- index_data = json.load(file)
637
- else:
638
- index_data = {}
639
-
640
- for item in index_data.values():
641
- symbols_str = item["symbols"]
642
- module_name = item["module_name"]
643
- info1 = extract_symbols(symbols_str)
644
- for name in info1.classes:
645
- list_of_symbols.append(
646
- SymbolItem(
647
- symbol_name=name,
648
- symbol_type=SymbolType.CLASSES,
649
- file_name=module_name,
650
- )
651
- )
652
- for name in info1.functions:
653
- list_of_symbols.append(
654
- SymbolItem(
655
- symbol_name=name,
656
- symbol_type=SymbolType.FUNCTIONS,
657
- file_name=module_name,
658
- )
659
- )
660
- for name in info1.variables:
661
- list_of_symbols.append(
662
- SymbolItem(
663
- symbol_name=name,
664
- symbol_type=SymbolType.VARIABLES,
665
- file_name=module_name,
666
- )
667
- )
668
- return list_of_symbols
636
+
637
+ def get_current_files():
638
+ """Get current files list"""
639
+ memory_manager = get_memory_manager()
640
+ return memory_manager.get_current_files()
641
+
642
+
643
+ def set_current_files(files):
644
+ """Set current files list"""
645
+ memory_manager = get_memory_manager()
646
+ memory_manager.set_current_files(files)
647
+
648
+
649
+ def get_file_groups():
650
+ """Get file groups"""
651
+ memory_manager = get_memory_manager()
652
+ return memory_manager.get_file_groups()
653
+
654
+
655
+ def get_exclude_dirs():
656
+ """Get exclude directories"""
657
+ memory_manager = get_memory_manager()
658
+ return memory_manager.get_exclude_dirs()
659
+
660
+
661
+ # 使用 project_scanner 模块创建兼容函数(供其他地方使用)
662
+ scanner_funcs = create_scanner_functions(
663
+ project_root=project_root,
664
+ default_exclude_dirs=defaut_exclude_dirs,
665
+ get_extra_exclude_dirs_func=get_exclude_dirs,
666
+ )
667
+
668
+ # 导出兼容函数
669
+ get_all_file_names_in_project = scanner_funcs["get_all_file_names_in_project"]
670
+ get_all_file_in_project = scanner_funcs["get_all_file_in_project"]
671
+ get_all_file_in_project_with_dot = scanner_funcs["get_all_file_in_project_with_dot"]
672
+ get_all_dir_names_in_project = scanner_funcs["get_all_dir_names_in_project"]
673
+ find_files_in_project = scanner_funcs["find_files_in_project"]
674
+ get_symbol_list = scanner_funcs["get_symbol_list"]
675
+
676
+ # 直接创建 CommandCompleterV2,它内部会使用 project_scanner
677
+ completer = CommandCompleterV2(
678
+ commands,
679
+ memory_model=CCMemoryModel(
680
+ get_memory_func=get_memory, save_memory_func=save_memory
681
+ ),
682
+ project_root=project_root,
683
+ )
669
684
 
670
685
 
671
- def save_memory():
672
- memory_path = os.path.join(base_persist_dir, "memory.json")
673
- lock_path = memory_path + ".lock"
674
-
675
- with FileLock(lock_path, timeout=30):
676
- with open(memory_path, "w", encoding="utf-8") as f:
677
- json.dump(memory, f, indent=2, ensure_ascii=False)
678
-
679
- load_memory()
680
-
681
-
682
- def save_memory_with_new_memory(new_memory):
683
- memory_path = os.path.join(base_persist_dir, "memory.json")
684
- lock_path = memory_path + ".lock"
685
-
686
- with FileLock(lock_path, timeout=30):
687
- with open(memory_path, "w", encoding="utf-8") as f:
688
- json.dump(new_memory, f, indent=2, ensure_ascii=False)
689
- load_memory()
690
-
691
-
692
- def load_memory():
693
- global memory
694
- memory_path = os.path.join(base_persist_dir, "memory.json")
695
- lock_path = memory_path + ".lock"
696
-
697
- if os.path.exists(memory_path):
698
- with FileLock(lock_path, timeout=30):
699
- with open(memory_path, "r", encoding="utf-8") as f:
700
- _memory = json.load(f)
701
- # clear memory
702
- memory.clear()
703
- memory.update(_memory)
704
- return memory
705
-
706
- def get_memory():
707
- return load_memory()
708
-
709
-
710
- from autocoder.common.command_completer_v2 import CommandCompleterV2
711
- completer = CommandCompleterV2(commands,
712
- file_system_model=CCFileSystemModel(project_root=project_root,
713
- defaut_exclude_dirs=defaut_exclude_dirs,
714
- get_all_file_names_in_project=get_all_file_names_in_project,
715
- get_all_file_in_project=get_all_file_in_project,
716
- get_all_dir_names_in_project=get_all_dir_names_in_project,
717
- get_all_file_in_project_with_dot=get_all_file_in_project_with_dot,
718
- get_symbol_list=get_symbol_list
719
- ),
720
- memory_model=CCMemoryModel(get_memory_func=get_memory,
721
- save_memory_func=save_memory))
722
686
  def revert():
723
687
  result_manager = ResultManager()
724
688
  last_yaml_file = get_last_yaml_file("actions")
@@ -728,57 +692,36 @@ def revert():
728
692
  with redirect_stdout() as output:
729
693
  auto_coder_main(["revert", "--file", file_path])
730
694
  s = output.getvalue()
731
-
695
+
732
696
  console = Console()
733
697
  panel = Panel(
734
698
  Markdown(s),
735
699
  title="Revert Result",
736
700
  border_style="green" if "Successfully reverted changes" in s else "red",
737
701
  padding=(1, 2),
738
- expand=False
702
+ expand=False,
739
703
  )
740
704
  console.print(panel)
741
-
705
+
742
706
  if "Successfully reverted changes" in s:
743
- result_manager.append(content=s, meta={"action": "revert","success":False, "input":{
744
- }})
707
+ result_manager.append(
708
+ content=s, meta={"action": "revert", "success": False, "input": {}}
709
+ )
745
710
  else:
746
- result_manager.append(content=s, meta={"action": "revert","success":False, "input":{
747
- }})
711
+ result_manager.append(
712
+ content=s, meta={"action": "revert", "success": False, "input": {}}
713
+ )
748
714
  else:
749
- result_manager.append(content="No previous chat action found to revert.", meta={"action": "revert","success":False, "input":{
750
- }})
751
-
752
-
753
- def add_files(args: List[str]):
754
-
755
- result_manager = ResultManager()
756
- if "groups" not in memory["current_files"]:
757
- memory["current_files"]["groups"] = {}
758
- if "groups_info" not in memory["current_files"]:
759
- memory["current_files"]["groups_info"] = {}
760
- if "current_groups" not in memory["current_files"]:
761
- memory["current_files"]["current_groups"] = []
762
- groups = memory["current_files"]["groups"]
763
- groups_info = memory["current_files"]["groups_info"]
764
-
765
- console = Console()
766
- printer = Printer()
767
-
768
- if not args:
769
- printer.print_in_terminal("add_files_no_args", style="red")
770
- result_manager.append(content=printer.get_message_from_key("add_files_no_args"),
771
- meta={"action": "add_files","success":False, "input":{ "args": args}})
772
- return
773
-
774
- if args[0] == "/refresh":
775
- completer.refresh_files()
715
+ result_manager.append(
716
+ content="No previous chat action found to revert.",
717
+ meta={"action": "revert", "success": False, "input": {}},
718
+ )
776
719
 
777
720
 
778
721
  def _handle_post_commit_and_pr(post_commit: bool, pr: bool, query: str, args, llm):
779
722
  """
780
723
  处理 post_commit 和 PR 功能
781
-
724
+
782
725
  Args:
783
726
  post_commit: 是否执行 post_commit
784
727
  pr: 是否创建 PR
@@ -787,13 +730,11 @@ def _handle_post_commit_and_pr(post_commit: bool, pr: bool, query: str, args, ll
787
730
  llm: LLM 实例
788
731
  """
789
732
  printer = Printer()
790
- console = Console()
791
-
792
733
  try:
793
734
  if post_commit:
794
735
  # 执行 post_commit 操作
795
736
  printer.print_in_terminal("post_commit_executing", style="blue")
796
-
737
+
797
738
  # 检查是否有未提交的更改
798
739
  uncommitted_changes = git_utils.get_uncommitted_changes(".")
799
740
  if uncommitted_changes:
@@ -801,46 +742,144 @@ def _handle_post_commit_and_pr(post_commit: bool, pr: bool, query: str, args, ll
801
742
  commit_message = git_utils.generate_commit_message.with_llm(llm).run(
802
743
  uncommitted_changes
803
744
  )
804
-
745
+
805
746
  # 执行提交
806
747
  commit_result = git_utils.commit_changes(".", commit_message)
807
748
  git_utils.print_commit_info(commit_result=commit_result)
808
- printer.print_in_terminal("post_commit_success", style="green", message=commit_message)
809
-
749
+ printer.print_in_terminal(
750
+ "post_commit_success", style="green", message=commit_message
751
+ )
752
+
810
753
  # 如果需要创建 PR,则继续处理
811
754
  if pr:
812
755
  _create_pull_request(commit_result, query, llm)
813
756
  else:
814
757
  printer.print_in_terminal("post_commit_no_changes", style="yellow")
815
-
758
+
816
759
  elif pr:
817
760
  # 只创建 PR,不执行 post_commit
818
761
  # 获取最后一个 commit
819
762
  try:
820
763
  repo = git.Repo(".")
821
764
  last_commit = repo.head.commit
822
-
765
+
823
766
  # 创建一个模拟的 commit_result 对象
824
767
  class MockCommitResult:
825
768
  def __init__(self, commit):
826
769
  self.commit_hash = commit.hexsha
827
770
  self.commit_message = commit.message.strip()
828
771
  self.changed_files = []
829
-
772
+
830
773
  mock_commit_result = MockCommitResult(last_commit)
831
774
  _create_pull_request(mock_commit_result, query, llm)
832
-
775
+
833
776
  except Exception as e:
834
- printer.print_in_terminal("pr_get_last_commit_failed", style="red", error=str(e))
835
-
777
+ printer.print_in_terminal(
778
+ "pr_get_last_commit_failed", style="red", error=str(e)
779
+ )
780
+
836
781
  except Exception as e:
837
782
  printer.print_in_terminal("post_commit_pr_failed", style="red", error=str(e))
838
783
 
839
784
 
785
+ def init_project_if_required(target_dir: str, project_type: str):
786
+ """
787
+ 如果项目没有初始化,则自动初始化项目
788
+
789
+ Args:
790
+ target_dir: 目标目录路径
791
+ """
792
+
793
+ # 确保目标目录是绝对路径
794
+ if not os.path.isabs(target_dir):
795
+ target_dir = os.path.abspath(target_dir)
796
+
797
+ actions_dir = os.path.join(target_dir, "actions")
798
+ auto_coder_dir = os.path.join(target_dir, ".auto-coder")
799
+
800
+ # 检查是否已经初始化
801
+ if os.path.exists(actions_dir) and os.path.exists(auto_coder_dir):
802
+ return # 已经初始化,无需再次初始化
803
+
804
+ printer = Printer()
805
+
806
+ try:
807
+ # 创建必要的目录
808
+ os.makedirs(actions_dir, exist_ok=True)
809
+ os.makedirs(auto_coder_dir, exist_ok=True)
810
+
811
+ # 导入并使用 create_actions 创建默认的 action 文件
812
+ from autocoder.common.command_templates import create_actions
813
+
814
+ create_actions(
815
+ source_dir=target_dir,
816
+ params={"project_type": project_type, "source_dir": target_dir},
817
+ )
818
+
819
+ # 初始化 git 仓库
820
+ try:
821
+ git_utils.init(target_dir)
822
+ except Exception as e:
823
+ global_logger.warning(f"Failed to initialize git repository: {e}")
824
+
825
+ # 创建或更新 .gitignore 文件
826
+ gitignore_path = os.path.join(target_dir, ".gitignore")
827
+ gitignore_entries = [
828
+ ".auto-coder/",
829
+ "/actions/",
830
+ "/output.txt",
831
+ ".autocoderrules",
832
+ ".autocodertools",
833
+ ".autocodercommands",
834
+ ".autocoderagents",
835
+ ".autocoderlinters",
836
+ ]
837
+
838
+ try:
839
+ # 读取现有的 .gitignore 内容
840
+ existing_entries = set()
841
+ if os.path.exists(gitignore_path):
842
+ with open(gitignore_path, "r", encoding="utf-8") as f:
843
+ # 将现有内容按行分割并去除空白字符,转换为集合以便快速查找
844
+ existing_entries = {line.strip() for line in f if line.strip()}
845
+
846
+ # 筛选出需要添加的新条目
847
+ new_entries = [
848
+ entry for entry in gitignore_entries if entry not in existing_entries
849
+ ]
850
+
851
+ # 如果有新条目需要添加,则写入文件
852
+ if new_entries:
853
+ with open(gitignore_path, "a", encoding="utf-8") as f:
854
+ for entry in new_entries:
855
+ f.write(f"\n{entry}")
856
+ except Exception as e:
857
+ global_logger.warning(f"Failed to update .gitignore: {e}")
858
+
859
+ # 创建 .autocoderignore 文件
860
+ try:
861
+ autocoderignore_path = os.path.join(target_dir, ".autocoderignore")
862
+ if not os.path.exists(autocoderignore_path):
863
+ autocoderignore_content = "target\n"
864
+ with open(autocoderignore_path, "w", encoding="utf-8") as f:
865
+ f.write(autocoderignore_content)
866
+ except Exception as e:
867
+ global_logger.warning(f"Failed to create .autocoderignore: {e}")
868
+
869
+ configure(f"project_type:{project_type}", skip_print=True)
870
+ global_logger.info(
871
+ f"Successfully initialized auto-coder project in {target_dir}"
872
+ )
873
+
874
+ except Exception as e:
875
+ global_logger.error(f"Failed to initialize project in {target_dir}: {e}")
876
+ printer.print_in_terminal("init_project_error", style="red", error=str(e))
877
+
878
+
840
879
  def _create_pull_request(commit_result, original_query: str, llm):
841
880
  """
842
881
  创建 Pull Request
843
-
882
+
844
883
  Args:
845
884
  commit_result: 提交结果对象
846
885
  original_query: 原始查询
@@ -848,76 +887,92 @@ def _create_pull_request(commit_result, original_query: str, llm):
848
887
  """
849
888
  printer = Printer()
850
889
  console = Console()
851
-
890
+
852
891
  try:
853
892
  # 检查是否安装了 gh CLI
854
893
  gh_check = subprocess.run(["gh", "--version"], capture_output=True, text=True)
855
894
  if gh_check.returncode != 0:
856
895
  printer.print_in_terminal("pr_gh_not_installed", style="red")
857
896
  return
858
-
897
+
859
898
  # 检查是否已经登录 GitHub
860
- auth_check = subprocess.run(["gh", "auth", "status"], capture_output=True, text=True)
899
+ auth_check = subprocess.run(
900
+ ["gh", "auth", "status"], capture_output=True, text=True
901
+ )
861
902
  if auth_check.returncode != 0:
862
903
  printer.print_in_terminal("pr_gh_not_authenticated", style="red")
863
904
  return
864
-
905
+
865
906
  # 获取当前分支名
866
907
  repo = git.Repo(".")
867
908
  current_branch = repo.active_branch.name
868
-
909
+
869
910
  # 如果在 main/master 分支,创建新分支
870
911
  if current_branch in ["main", "master"]:
871
912
  # 生成新分支名
872
913
  import re
873
- branch_name = re.sub(r'[^a-zA-Z0-9\-_]', '-', original_query.lower())
914
+
915
+ branch_name = re.sub(r"[^a-zA-Z0-9\-_]", "-", original_query.lower())
874
916
  branch_name = f"auto-coder-{branch_name[:30]}-{int(time.time())}"
875
-
917
+
876
918
  # 创建并切换到新分支
877
919
  new_branch = repo.create_head(branch_name)
878
920
  new_branch.checkout()
879
921
  current_branch = branch_name
880
-
881
- printer.print_in_terminal("pr_created_branch", style="blue", branch=branch_name)
882
-
922
+
923
+ printer.print_in_terminal(
924
+ "pr_created_branch", style="blue", branch=branch_name
925
+ )
926
+
883
927
  # 推送当前分支到远程
884
928
  try:
885
929
  origin = repo.remotes.origin
886
930
  origin.push(current_branch)
887
- printer.print_in_terminal("pr_pushed_branch", style="blue", branch=current_branch)
931
+ printer.print_in_terminal(
932
+ "pr_pushed_branch", style="blue", branch=current_branch
933
+ )
888
934
  except Exception as e:
889
935
  printer.print_in_terminal("pr_push_failed", style="red", error=str(e))
890
936
  return
891
-
937
+
892
938
  # 生成 PR 标题和描述
893
939
  pr_title, pr_body = _generate_pr_content(commit_result, original_query, llm)
894
-
940
+
895
941
  # 创建 PR
896
942
  pr_cmd = [
897
- "gh", "pr", "create",
898
- "--title", pr_title,
899
- "--body", pr_body,
900
- "--head", current_branch
943
+ "gh",
944
+ "pr",
945
+ "create",
946
+ "--title",
947
+ pr_title,
948
+ "--body",
949
+ pr_body,
950
+ "--head",
951
+ current_branch,
901
952
  ]
902
-
953
+
903
954
  pr_result = subprocess.run(pr_cmd, capture_output=True, text=True)
904
-
955
+
905
956
  if pr_result.returncode == 0:
906
957
  pr_url = pr_result.stdout.strip()
907
958
  printer.print_in_terminal("pr_created_success", style="green", url=pr_url)
908
-
959
+
909
960
  # 显示 PR 信息
910
- console.print(Panel(
911
- f"[bold green]Pull Request Created Successfully![/bold green]\n\n"
912
- f"[bold]Title:[/bold] {pr_title}\n"
913
- f"[bold]URL:[/bold] {pr_url}\n"
914
- f"[bold]Branch:[/bold] {current_branch}",
915
- title="🎉 Pull Request",
916
- border_style="green"
917
- ))
961
+ console.print(
962
+ Panel(
963
+ f"[bold green]Pull Request Created Successfully![/bold green]\n\n"
964
+ f"[bold]Title:[/bold] {pr_title}\n"
965
+ f"[bold]URL:[/bold] {pr_url}\n"
966
+ f"[bold]Branch:[/bold] {current_branch}",
967
+ title="🎉 Pull Request",
968
+ border_style="green",
969
+ )
970
+ )
918
971
  else:
919
- printer.print_in_terminal("pr_creation_failed", style="red", error=pr_result.stderr)
920
-
972
+ printer.print_in_terminal(
973
+ "pr_creation_failed", style="red", error=pr_result.stderr
974
+ )
975
+
921
976
  except Exception as e:
922
977
  printer.print_in_terminal("pr_creation_error", style="red", error=str(e))
923
978
 
@@ -926,22 +981,22 @@ def _create_pull_request(commit_result, original_query: str, llm):
926
981
  def _generate_pr_content(commit_result, original_query: str, llm) -> tuple:
927
982
  """
928
983
  生成 PR 标题和描述
929
-
984
+
930
985
  根据提交信息和原始查询生成合适的 PR 标题和描述。
931
-
986
+
932
987
  Args:
933
988
  commit_result: 提交结果,包含 commit_message 和 changed_files
934
989
  original_query: 用户的原始查询请求
935
-
990
+
936
991
  Returns:
937
992
  tuple: (pr_title, pr_body) PR标题和描述内容
938
-
993
+
939
994
  请生成简洁明了的 PR 标题(不超过72字符)和详细的描述内容。
940
995
  标题应该概括主要变更,描述应该包含:
941
996
  1. 变更的背景和目的
942
997
  2. 主要修改内容
943
998
  3. 影响的文件(如果有的话)
944
-
999
+
945
1000
  提交信息:{{ commit_result.commit_message }}
946
1001
  原始需求:{{ original_query }}
947
1002
  {% if commit_result.changed_files %}
@@ -951,7 +1006,7 @@ def _generate_pr_content(commit_result, original_query: str, llm) -> tuple:
951
1006
  {% endfor %}
952
1007
  {% endif %}
953
1008
  """
954
-
1009
+
955
1010
  # 这个函数会被 byzerllm 装饰器处理,返回 LLM 生成的内容
956
1011
  # 实际实现会在运行时由装饰器处理
957
1012
  pass
@@ -979,14 +1034,14 @@ def _generate_pr_content(commit_result, original_query: str, llm):
979
1034
  TITLE: [标题内容]
980
1035
  BODY: [描述内容]
981
1036
  """
982
-
1037
+
983
1038
  response = llm.chat([{"role": "user", "content": prompt}])
984
-
1039
+
985
1040
  # 解析响应
986
- lines = response.split('\n')
1041
+ lines = response.split("\n")
987
1042
  title = ""
988
1043
  body = ""
989
-
1044
+
990
1045
  for line in lines:
991
1046
  if line.startswith("TITLE:"):
992
1047
  title = line.replace("TITLE:", "").strip()
@@ -994,15 +1049,15 @@ BODY: [描述内容]
994
1049
  body = line.replace("BODY:", "").strip()
995
1050
  elif body: # 如果已经开始收集 body,继续添加后续行
996
1051
  body += "\n" + line
997
-
1052
+
998
1053
  # 如果解析失败,使用默认值
999
1054
  if not title:
1000
1055
  title = f"Auto-coder: {original_query[:50]}..."
1001
1056
  if not body:
1002
1057
  body = f"This PR was automatically generated by Auto-coder.\n\nOriginal request: {original_query}"
1003
-
1058
+
1004
1059
  return title, body
1005
-
1060
+
1006
1061
  except Exception as e:
1007
1062
  # 如果 LLM 生成失败,使用默认值
1008
1063
  title = f"Auto-coder: {original_query[:50]}..."
@@ -1011,351 +1066,34 @@ BODY: [描述内容]
1011
1066
 
1012
1067
 
1013
1068
  def add_files(args: List[str]):
1069
+ """
1070
+ 处理文件添加命令,使用 AddFilesHandler 进行统一处理
1014
1071
 
1015
- result_manager = ResultManager()
1016
- if "groups" not in memory["current_files"]:
1017
- memory["current_files"]["groups"] = {}
1018
- if "groups_info" not in memory["current_files"]:
1019
- memory["current_files"]["groups_info"] = {}
1020
- if "current_groups" not in memory["current_files"]:
1021
- memory["current_files"]["current_groups"] = []
1022
- groups = memory["current_files"]["groups"]
1023
- groups_info = memory["current_files"]["groups_info"]
1024
-
1025
- console = Console()
1026
- printer = Printer()
1027
-
1028
- if not args:
1029
- printer.print_in_terminal("add_files_no_args", style="red")
1030
- result_manager.append(content=printer.get_message_from_key("add_files_no_args"),
1031
- meta={"action": "add_files","success":False, "input":{ "args": args}})
1032
- return
1033
-
1034
- if args[0] == "/refresh":
1035
- completer.refresh_files()
1036
- load_memory()
1037
- console.print(
1038
- Panel("Refreshed file list.",
1039
- title="Files Refreshed", border_style="green")
1040
- )
1041
- result_manager.append(content="Files refreshed.",
1042
- meta={"action": "add_files","success":True, "input":{ "args": args}})
1043
- return
1044
-
1045
- if args[0] == "/group":
1046
- if len(args) == 1 or (len(args) == 2 and args[1] == "list"):
1047
- if not groups:
1048
- console.print(
1049
- Panel("No groups defined.", title="Groups",
1050
- border_style="yellow")
1051
- )
1052
- result_manager.append(content="No groups defined.",
1053
- meta={"action": "add_files","success":False, "input":{ "args": args}})
1054
- else:
1055
- table = Table(
1056
- title="Defined Groups",
1057
- show_header=True,
1058
- header_style="bold magenta",
1059
- show_lines=True,
1060
- )
1061
- table.add_column("Group Name", style="cyan", no_wrap=True)
1062
- table.add_column("Files", style="green")
1063
- table.add_column("Query Prefix", style="yellow")
1064
- table.add_column("Active", style="magenta")
1065
-
1066
- for i, (group_name, files) in enumerate(groups.items()):
1067
- query_prefix = groups_info.get(group_name, {}).get(
1068
- "query_prefix", ""
1069
- )
1070
- is_active = (
1071
- "✓"
1072
- if group_name in memory["current_files"]["current_groups"]
1073
- else ""
1074
- )
1075
- table.add_row(
1076
- group_name,
1077
- "\n".join([os.path.relpath(f, project_root)
1078
- for f in files]),
1079
- query_prefix,
1080
- is_active,
1081
- end_section=(i == len(groups) - 1),
1082
- )
1083
- console.print(Panel(table, border_style="blue"))
1084
- result_manager.append(content="Defined groups.",
1085
- meta={"action": "add_files","success":True, "input":{ "args": args}})
1086
- elif len(args) >= 2 and args[1] == "/reset":
1087
- memory["current_files"]["current_groups"] = []
1088
- console.print(
1089
- Panel(
1090
- "Active group names have been reset. If you want to clear the active files, you should use the command /remove_files /all.",
1091
- title="Groups Reset",
1092
- border_style="green",
1093
- )
1094
- )
1095
- result_manager.append(content="Active group names have been reset. If you want to clear the active files, you should use the command /remove_files /all.",
1096
- meta={"action": "add_files","success":True, "input":{ "args": args}})
1097
- elif len(args) >= 3 and args[1] == "/add":
1098
- group_name = args[2]
1099
- groups[group_name] = memory["current_files"]["files"].copy()
1100
- console.print(
1101
- Panel(
1102
- f"Added group '{group_name}' with current files.",
1103
- title="Group Added",
1104
- border_style="green",
1105
- )
1106
- )
1107
- result_manager.append(content=f"Added group '{group_name}' with current files.",
1108
- meta={"action": "add_files","success":True, "input":{ "args": args}})
1109
-
1110
- elif len(args) >= 3 and args[1] == "/drop":
1111
- group_name = args[2]
1112
- if group_name in groups:
1113
- del memory["current_files"]["groups"][group_name]
1114
- if group_name in groups_info:
1115
- del memory["current_files"]["groups_info"][group_name]
1116
- if group_name in memory["current_files"]["current_groups"]:
1117
- memory["current_files"]["current_groups"].remove(
1118
- group_name)
1119
- console.print(
1120
- Panel(
1121
- f"Dropped group '{group_name}'.",
1122
- title="Group Dropped",
1123
- border_style="green",
1124
- )
1125
- )
1126
- result_manager.append(content=f"Dropped group '{group_name}'.",
1127
- meta={"action": "add_files","success":True, "input":{ "args": args}})
1128
- else:
1129
- console.print(
1130
- Panel(
1131
- f"Group '{group_name}' not found.",
1132
- title="Error",
1133
- border_style="red",
1134
- )
1135
- )
1136
- result_manager.append(content=f"Group '{group_name}' not found.",
1137
- meta={"action": "add_files","success":False, "input":{ "args": args}})
1138
- elif len(args) == 3 and args[1] == "/set":
1139
- group_name = args[2]
1140
-
1141
- def multiline_edit():
1142
- from prompt_toolkit.lexers import PygmentsLexer
1143
- from pygments.lexers.markup import MarkdownLexer
1144
- from prompt_toolkit.formatted_text import HTML
1145
- from prompt_toolkit.shortcuts import print_formatted_text
1146
-
1147
- style = Style.from_dict(
1148
- {
1149
- "dialog": "bg:#88ff88",
1150
- "dialog frame.label": "bg:#ffffff #000000",
1151
- "dialog.body": "bg:#000000 #00ff00",
1152
- "dialog shadow": "bg:#00aa00",
1153
- }
1154
- )
1155
-
1156
- print_formatted_text(
1157
- HTML(
1158
- "<b>Type Atom Group Desc (Prese [Esc] + [Enter] to finish.)</b><br/>"
1159
- )
1160
- )
1161
- text = prompt(
1162
- HTML("<ansicyan>║</ansicyan> "),
1163
- multiline=True,
1164
- lexer=PygmentsLexer(MarkdownLexer),
1165
- style=style,
1166
- wrap_lines=True,
1167
- prompt_continuation=HTML("<ansicyan>║</ansicyan> "),
1168
- rprompt=HTML("<ansicyan>║</ansicyan>"),
1169
- )
1170
- return text
1171
-
1172
- query_prefix = multiline_edit()
1173
- if group_name in groups:
1174
- groups_info[group_name] = {"query_prefix": query_prefix}
1175
- console.print(
1176
- Panel(
1177
- f"Set Atom Group Desc for group '{group_name}'.",
1178
- title="Group Info Updated",
1179
- border_style="green",
1180
- )
1181
- )
1182
- else:
1183
- console.print(
1184
- Panel(
1185
- f"Group '{group_name}' not found.",
1186
- title="Error",
1187
- border_style="red",
1188
- )
1189
- )
1190
- elif len(args) >= 2:
1191
- # 支持多个组的合并,允许组名之间使用逗号或空格分隔
1192
- group_names = " ".join(args[1:]).replace(",", " ").split()
1193
- merged_files = set()
1194
- missing_groups = []
1195
- for group_name in group_names:
1196
- if group_name in groups:
1197
- merged_files.update(groups[group_name])
1198
- else:
1199
- missing_groups.append(group_name)
1200
-
1201
- if missing_groups:
1202
- console.print(
1203
- Panel(
1204
- f"Group(s) not found: {', '.join(missing_groups)}",
1205
- title="Error",
1206
- border_style="red",
1207
- )
1208
- )
1209
- result_manager.append(content=f"Group(s) not found: {', '.join(missing_groups)}",
1210
- meta={"action": "add_files","success":False, "input":{ "args": args}})
1211
-
1212
- if merged_files:
1213
- memory["current_files"]["files"] = list(merged_files)
1214
- memory["current_files"]["current_groups"] = [
1215
- name for name in group_names if name in groups
1216
- ]
1217
- console.print(
1218
- Panel(
1219
- f"Merged files from groups: {', '.join(group_names)}",
1220
- title="Files Merged",
1221
- border_style="green",
1222
- )
1223
- )
1224
- table = Table(
1225
- title="Current Files",
1226
- show_header=True,
1227
- header_style="bold magenta",
1228
- show_lines=True, # 这会在每行之间添加分割线
1229
- )
1230
- table.add_column("File", style="green")
1231
- for i, f in enumerate(memory["current_files"]["files"]):
1232
- table.add_row(
1233
- os.path.relpath(f, project_root),
1234
- end_section=(
1235
- i == len(memory["current_files"]["files"]) - 1
1236
- ), # 在最后一行之后不添加分割线
1237
- )
1238
- console.print(Panel(table, border_style="blue"))
1239
- console.print(
1240
- Panel(
1241
- f"Active groups: {', '.join(memory['current_files']['current_groups'])}",
1242
- title="Active Groups",
1243
- border_style="green",
1244
- )
1245
- )
1246
- result_manager.append(content=f"Active groups: {', '.join(memory['current_files']['current_groups'])}",
1247
- meta={"action": "add_files","success":True, "input":{ "args": args}})
1248
- elif not missing_groups:
1249
- console.print(
1250
- Panel(
1251
- "No files in the specified groups.",
1252
- title="No Files Added",
1253
- border_style="yellow",
1254
- )
1255
- )
1256
- result_manager.append(content="No files in the specified groups.",
1257
- meta={"action": "add_files","success":False, "input":{ "args": args}})
1258
- else:
1259
- existing_files = memory["current_files"]["files"]
1260
- matched_files = find_files_in_project(args)
1261
-
1262
- files_to_add = [f for f in matched_files if f not in existing_files]
1263
- if files_to_add:
1264
- memory["current_files"]["files"].extend(files_to_add)
1265
- table = Table(
1266
- title=get_message("add_files_added_files"),
1267
- show_header=True,
1268
- header_style="bold magenta",
1269
- show_lines=True, # 这会在每行之间添加分割线
1270
- )
1271
- table.add_column("File", style="green")
1272
- for i, f in enumerate(files_to_add):
1273
- table.add_row(
1274
- os.path.relpath(f, project_root),
1275
- end_section=(
1276
- i == len(files_to_add) - 1
1277
- ), # 在最后一行之后不添加分割线
1278
- )
1279
- console.print(Panel(table, border_style="green"))
1280
- result_manager.append(content=f"Added files: {', '.join(files_to_add)}",
1281
- meta={"action": "add_files","success":True, "input":{ "args": args}})
1282
- else:
1283
- printer.print_in_terminal("add_files_matched", style="yellow")
1284
- result_manager.append(content=f"No files matched.",
1285
- meta={"action": "add_files","success":False, "input":{ "args": args}})
1072
+ Args:
1073
+ args: 命令参数列表
1074
+ """
1075
+ from autocoder.common.file_handler import AddFilesHandler
1286
1076
 
1287
- save_memory()
1077
+ handler = AddFilesHandler()
1078
+ handler.handle_add_files_command(args)
1288
1079
 
1289
1080
 
1290
1081
  def remove_files(file_names: List[str]):
1291
- project_root = os.getcwd()
1292
- printer = Printer()
1293
- result_manager = ResultManager()
1082
+ """
1083
+ 处理文件删除命令,使用 RemoveFilesHandler 进行统一处理
1294
1084
 
1295
- if "/all" in file_names:
1296
- memory["current_files"]["files"] = []
1297
- memory["current_files"]["current_groups"] = []
1298
- printer.print_in_terminal("remove_files_all", style="green")
1299
- result_manager.append(content="All files removed.",
1300
- meta={"action": "remove_files","success":True, "input":{ "file_names": file_names}})
1301
- else:
1302
- files_to_remove = set()
1303
- current_files_abs = memory["current_files"]["files"]
1304
-
1305
- for pattern in file_names:
1306
- pattern = pattern.strip() # Remove leading/trailing whitespace
1307
- if not pattern:
1308
- continue
1309
-
1310
- is_wildcard = "*" in pattern or "?" in pattern
1311
-
1312
- for file_path_abs in current_files_abs:
1313
- relative_path = os.path.relpath(file_path_abs, project_root)
1314
- basename = os.path.basename(file_path_abs)
1315
-
1316
- matched = False
1317
- if is_wildcard:
1318
- # Match pattern against relative path or basename
1319
- if fnmatch.fnmatch(relative_path, pattern) or fnmatch.fnmatch(basename, pattern):
1320
- matched = True
1321
- else:
1322
- # Exact match against relative path, absolute path, or basename
1323
- if relative_path == pattern or file_path_abs == pattern or basename == pattern:
1324
- matched = True
1325
-
1326
- if matched:
1327
- files_to_remove.add(file_path_abs)
1328
-
1329
- removed_files_list = list(files_to_remove)
1330
- if removed_files_list:
1331
- # Update memory by filtering out the files to remove
1332
- memory["current_files"]["files"] = [
1333
- f for f in current_files_abs if f not in files_to_remove
1334
- ]
1085
+ Args:
1086
+ file_names: 文件名列表或模式列表
1087
+ """
1088
+ from autocoder.common.file_handler import RemoveFilesHandler
1335
1089
 
1336
- table = Table(
1337
- show_header=True,
1338
- header_style="bold magenta"
1339
- )
1340
- table.add_column(printer.get_message_from_key("file_column_title"), style="green")
1341
- for f in removed_files_list:
1342
- table.add_row(os.path.relpath(f, project_root))
1090
+ handler = RemoveFilesHandler()
1091
+ handler.handle_remove_files_command(file_names)
1343
1092
 
1344
- console = Console()
1345
- console.print(
1346
- Panel(table, border_style="green",
1347
- title=printer.get_message_from_key("files_removed")))
1348
- result_manager.append(content=f"Removed files: {', '.join(removed_files_list)}",
1349
- meta={"action": "remove_files","success":True, "input":{ "file_names": file_names}})
1350
- else:
1351
- printer.print_in_terminal("remove_files_none", style="yellow")
1352
- result_manager.append(content=printer.get_message_from_key("remove_files_none"),
1353
- meta={"action": "remove_files","success":False, "input":{ "file_names": file_names}})
1354
- save_memory()
1355
1093
 
1356
- @run_in_raw_thread()
1357
1094
  def ask(query: str):
1358
- conf = memory.get("conf", {})
1095
+ memory_manager = get_memory_manager()
1096
+ conf = memory_manager.get_all_config()
1359
1097
  yaml_config = {
1360
1098
  "include_file": ["./base/base.yml"],
1361
1099
  }
@@ -1383,7 +1121,7 @@ def ask(query: str):
1383
1121
 
1384
1122
  execute_file = os.path.join("actions", f"{uuid.uuid4()}.yml")
1385
1123
 
1386
- with open(os.path.join(execute_file), "w",encoding="utf-8") as f:
1124
+ with open(os.path.join(execute_file), "w", encoding="utf-8") as f:
1387
1125
  f.write(yaml_content)
1388
1126
 
1389
1127
  def execute_ask():
@@ -1398,50 +1136,27 @@ def ask(query: str):
1398
1136
  def get_llm_friendly_package_docs(
1399
1137
  package_name: Optional[str] = None, return_paths: bool = False
1400
1138
  ) -> List[str]:
1401
- lib_dir = os.path.join(".auto-coder", "libs")
1402
- llm_friendly_packages_dir = os.path.join(lib_dir, "llm_friendly_packages")
1403
- docs = []
1404
-
1405
- if not os.path.exists(llm_friendly_packages_dir):
1406
- return docs
1407
-
1408
- libs = list(memory.get("libs", {}).keys())
1409
-
1410
- for domain in os.listdir(llm_friendly_packages_dir):
1411
- domain_path = os.path.join(llm_friendly_packages_dir, domain)
1412
- if os.path.isdir(domain_path):
1413
- for username in os.listdir(domain_path):
1414
- username_path = os.path.join(domain_path, username)
1415
- if os.path.isdir(username_path):
1416
- for lib_name in os.listdir(username_path):
1417
- lib_path = os.path.join(username_path, lib_name)
1418
- if (
1419
- os.path.isdir(lib_path)
1420
- and (
1421
- package_name is None
1422
- or lib_name == package_name
1423
- or package_name == os.path.join(username, lib_name)
1424
- )
1425
- and lib_name in libs
1426
- ):
1427
- for root, _, files in os.walk(lib_path):
1428
- for file in files:
1429
- if file.endswith(".md"):
1430
- file_path = os.path.join(root, file)
1431
- if return_paths:
1432
- docs.append(file_path)
1433
- else:
1434
- with open(file_path, "r",encoding="utf-8") as f:
1435
- docs.append(f.read())
1436
-
1437
- return docs
1139
+ """
1140
+ Get LLM friendly package documentation using the new AC module system
1141
+
1142
+ Args:
1143
+ package_name: Specific package name to get docs for, None for all packages
1144
+ return_paths: If True, return file paths; if False, return file contents
1145
+
1146
+ Returns:
1147
+ List of documentation content or file paths
1148
+ """
1149
+ from autocoder.common.llm_friendly_package import get_package_manager
1150
+
1151
+ package_manager = get_package_manager()
1152
+ return package_manager.get_docs(package_name, return_paths)
1438
1153
 
1439
1154
 
1440
1155
  def convert_yaml_to_config(yaml_file: str):
1441
1156
  from autocoder.auto_coder import AutoCoderArgs, load_include_files, Template
1442
1157
 
1443
1158
  args = AutoCoderArgs()
1444
- with open(yaml_file, "r",encoding="utf-8") as f:
1159
+ with open(yaml_file, "r", encoding="utf-8") as f:
1445
1160
  config = yaml.safe_load(f)
1446
1161
  config = load_include_files(config, yaml_file)
1447
1162
  for key, value in config.items():
@@ -1453,150 +1168,22 @@ def convert_yaml_to_config(yaml_file: str):
1453
1168
  setattr(args, key, value)
1454
1169
  return args
1455
1170
 
1456
- @run_in_raw_thread()
1457
- def mcp(query: str):
1458
- query = query.strip()
1459
- mcp_server = get_mcp_server()
1460
- printer = Printer()
1461
-
1462
- # Handle remove command
1463
- if query.startswith("/remove"):
1464
- server_name = query.replace("/remove", "", 1).strip()
1465
- response = mcp_server.send_request(
1466
- McpRemoveRequest(server_name=server_name))
1467
- if response.error:
1468
- printer.print_in_terminal("mcp_remove_error", style="red", error=response.error)
1469
- else:
1470
- printer.print_in_terminal("mcp_remove_success", style="green", result=response.result)
1471
- return
1472
-
1473
- # Handle list command
1474
- if query.startswith("/list_running"):
1475
- response = mcp_server.send_request(McpListRunningRequest())
1476
- if response.error:
1477
- printer.print_in_terminal("mcp_list_running_error", style="red", error=response.error)
1478
- else:
1479
- printer.print_in_terminal("mcp_list_running_title")
1480
- printer.print_str_in_terminal(response.result)
1481
- return
1482
-
1483
- # Handle list command
1484
- if query.startswith("/list"):
1485
- response = mcp_server.send_request(McpListRequest())
1486
- if response.error:
1487
- printer.print_in_terminal("mcp_list_builtin_error", style="red", error=response.error)
1488
- else:
1489
- printer.print_in_terminal("mcp_list_builtin_title")
1490
- printer.print_str_in_terminal(response.result)
1491
- return
1492
-
1493
- # Handle refresh command
1494
- if query.startswith("/refresh"):
1495
- server_name = query.replace("/refresh", "", 1).strip()
1496
- response = mcp_server.send_request(McpRefreshRequest(name=server_name or None))
1497
- if response.error:
1498
- printer.print_in_terminal("mcp_refresh_error", style="red", error=response.error)
1499
- else:
1500
- printer.print_in_terminal("mcp_refresh_success", style="green")
1501
- return
1502
-
1503
- # Handle add command
1504
- if query.startswith("/add"):
1505
- query = query.replace("/add", "", 1).strip()
1506
- request = McpInstallRequest(server_name_or_config=query)
1507
- response = mcp_server.send_request(request)
1508
-
1509
- if response.error:
1510
- printer.print_in_terminal("mcp_install_error", style="red", error=response.error)
1511
- else:
1512
- printer.print_in_terminal("mcp_install_success", style="green", result=response.result)
1513
- return
1514
-
1515
- # Handle default query
1516
- conf = memory.get("conf", {})
1517
- yaml_config = {
1518
- "include_file": ["./base/base.yml"],
1519
- "auto_merge": conf.get("auto_merge", "editblock"),
1520
- "human_as_model": conf.get("human_as_model", "false") == "true",
1521
- "skip_build_index": conf.get("skip_build_index", "true") == "true",
1522
- "skip_confirm": conf.get("skip_confirm", "true") == "true",
1523
- "silence": conf.get("silence", "true") == "true",
1524
- "include_project_structure": conf.get("include_project_structure", "true")
1525
- == "true",
1526
- "exclude_files": memory.get("exclude_files", []),
1527
- }
1528
- for key, value in conf.items():
1529
- converted_value = convert_config_value(key, value)
1530
- if converted_value is not None:
1531
- yaml_config[key] = converted_value
1532
1171
 
1533
- temp_yaml = os.path.join("actions", f"{uuid.uuid4()}.yml")
1534
- try:
1535
- with open(temp_yaml, "w",encoding="utf-8") as f:
1536
- f.write(convert_yaml_config_to_str(yaml_config=yaml_config))
1537
- args = convert_yaml_to_config(temp_yaml)
1538
- finally:
1539
- if os.path.exists(temp_yaml):
1540
- os.remove(temp_yaml)
1541
-
1542
- mcp_server = get_mcp_server()
1543
-
1544
-
1545
- if query.startswith("/info"):
1546
- response = mcp_server.send_request(McpServerInfoRequest(
1547
- model=args.inference_model or args.model,
1548
- product_mode=args.product_mode
1549
- ))
1550
- if response.error:
1551
- printer.print_in_terminal("mcp_server_info_error", style="red", error=response.error)
1552
- else:
1553
- printer.print_in_terminal("mcp_server_info_title")
1554
- printer.print_str_in_terminal(response.result)
1555
- return
1556
-
1557
- response = mcp_server.send_request(
1558
- McpRequest(
1559
- query=query,
1560
- model=args.inference_model or args.model,
1561
- product_mode=args.product_mode
1562
- )
1563
- )
1172
+ def mcp(query: str):
1173
+ """
1174
+ 处理MCP命令,使用 McpHandler 进行统一处理
1564
1175
 
1565
- if response.error:
1566
- printer.print_panel(
1567
- f"Error from MCP server: {response.error}",
1568
- text_options={"justify": "left"},
1569
- panel_options={
1570
- "title": printer.get_message_from_key("mcp_error_title"),
1571
- "border_style": "red"
1572
- }
1573
- )
1574
- else:
1575
- # Save conversation
1576
- mcp_dir = os.path.join(".auto-coder", "mcp", "conversations")
1577
- os.makedirs(mcp_dir, exist_ok=True)
1578
- timestamp = str(int(time.time()))
1579
- file_path = os.path.join(mcp_dir, f"{timestamp}.md")
1176
+ Args:
1177
+ query: 查询字符串
1178
+ """
1179
+ from autocoder.common.file_handler import McpHandler
1580
1180
 
1581
- # Format response as markdown
1582
- markdown_content = response.result
1181
+ handler = McpHandler()
1182
+ handler.handle_mcp_command(query)
1583
1183
 
1584
- # Save to file
1585
- with open(file_path, "w", encoding="utf-8") as f:
1586
- f.write(markdown_content)
1587
1184
 
1588
- console = Console()
1589
- console.print(
1590
- Panel(
1591
- Markdown(markdown_content, justify="left"),
1592
- title=printer.get_message_from_key('mcp_response_title'),
1593
- border_style="green"
1594
- )
1595
- )
1596
-
1597
-
1598
- @run_in_raw_thread()
1599
1185
  def code_next(query: str):
1186
+ memory = get_current_memory()
1600
1187
  conf = memory.get("conf", {})
1601
1188
  yaml_config = {
1602
1189
  "include_file": ["./base/base.yml"],
@@ -1605,7 +1192,7 @@ def code_next(query: str):
1605
1192
  "skip_build_index": conf.get("skip_build_index", "true") == "true",
1606
1193
  "skip_confirm": conf.get("skip_confirm", "true") == "true",
1607
1194
  "silence": conf.get("silence", "true") == "true",
1608
- "include_project_structure": conf.get("include_project_structure", "true")
1195
+ "include_project_structure": conf.get("include_project_structure", "false")
1609
1196
  == "true",
1610
1197
  "exclude_files": memory.get("exclude_files", []),
1611
1198
  }
@@ -1616,7 +1203,7 @@ def code_next(query: str):
1616
1203
 
1617
1204
  temp_yaml = os.path.join("actions", f"{uuid.uuid4()}.yml")
1618
1205
  try:
1619
- with open(temp_yaml, "w",encoding="utf-8") as f:
1206
+ with open(temp_yaml, "w", encoding="utf-8") as f:
1620
1207
  f.write(convert_yaml_config_to_str(yaml_config=yaml_config))
1621
1208
  args = convert_yaml_to_config(temp_yaml)
1622
1209
  finally:
@@ -1626,8 +1213,7 @@ def code_next(query: str):
1626
1213
  product_mode = conf.get("product_mode", "lite")
1627
1214
  llm = get_single_llm(args.chat_model or args.model, product_mode=product_mode)
1628
1215
 
1629
- auto_guesser = AutoGuessQuery(
1630
- llm=llm, project_dir=os.getcwd(), skip_diff=True)
1216
+ auto_guesser = AutoGuessQuery(llm=llm, project_dir=os.getcwd(), skip_diff=True)
1631
1217
 
1632
1218
  predicted_tasks = auto_guesser.predict_next_tasks(
1633
1219
  5, is_human_as_model=args.human_as_model
@@ -1641,25 +1227,20 @@ def code_next(query: str):
1641
1227
  console = Console()
1642
1228
 
1643
1229
  # Create main panel for all predicted tasks
1644
- table = Table(show_header=True,
1645
- header_style="bold magenta", show_lines=True)
1230
+ table = Table(show_header=True, header_style="bold magenta", show_lines=True)
1646
1231
  table.add_column("Priority", style="cyan", width=8)
1647
- table.add_column("Task Description", style="green",
1648
- width=40, overflow="fold")
1232
+ table.add_column("Task Description", style="green", width=40, overflow="fold")
1649
1233
  table.add_column("Files", style="yellow", width=30, overflow="fold")
1650
1234
  table.add_column("Reason", style="blue", width=30, overflow="fold")
1651
- table.add_column("Dependencies", style="magenta",
1652
- width=30, overflow="fold")
1235
+ table.add_column("Dependencies", style="magenta", width=30, overflow="fold")
1653
1236
 
1654
1237
  for task in predicted_tasks:
1655
1238
  # Format file paths to be more readable
1656
- file_list = "\n".join([os.path.relpath(f, os.getcwd())
1657
- for f in task.urls])
1239
+ file_list = "\n".join([os.path.relpath(f, os.getcwd()) for f in task.urls])
1658
1240
 
1659
1241
  # Format dependencies to be more readable
1660
1242
  dependencies = (
1661
- "\n".join(
1662
- task.dependency_queries) if task.dependency_queries else "None"
1243
+ "\n".join(task.dependency_queries) if task.dependency_queries else "None"
1663
1244
  )
1664
1245
 
1665
1246
  table.add_row(
@@ -1676,260 +1257,42 @@ def code_next(query: str):
1676
1257
  )
1677
1258
 
1678
1259
 
1679
- @run_in_raw_thread()
1680
- def commit(query: str):
1681
- conf = memory.get("conf", {})
1682
- product_mode = conf.get("product_mode", "lite")
1683
- def prepare_commit_yaml():
1684
- auto_coder_main(["next", "chat_action"])
1685
-
1686
- prepare_commit_yaml()
1687
-
1688
- # no_diff = query.strip().startswith("/no_diff")
1689
- # if no_diff:
1690
- # query = query.replace("/no_diff", "", 1).strip()
1691
-
1692
- latest_yaml_file = get_last_yaml_file("actions")
1693
-
1694
- conf = memory.get("conf", {})
1695
- current_files = memory["current_files"]["files"]
1696
- execute_file = None
1697
-
1698
- if latest_yaml_file:
1699
- try:
1700
- execute_file = os.path.join("actions", latest_yaml_file)
1701
- yaml_config = {
1702
- "include_file": ["./base/base.yml"],
1703
- "auto_merge": conf.get("auto_merge", "editblock"),
1704
- "human_as_model": conf.get("human_as_model", "false") == "true",
1705
- "skip_build_index": conf.get("skip_build_index", "true") == "true",
1706
- "skip_confirm": conf.get("skip_confirm", "true") == "true",
1707
- "silence": conf.get("silence", "true") == "true",
1708
- "include_project_structure": conf.get("include_project_structure", "true")
1709
- == "true",
1710
- }
1711
- for key, value in conf.items():
1712
- converted_value = convert_config_value(key, value)
1713
- if converted_value is not None:
1714
- yaml_config[key] = converted_value
1715
-
1716
- yaml_config["urls"] = current_files + get_llm_friendly_package_docs(
1717
- return_paths=True
1718
- )
1719
-
1720
- if conf.get("enable_global_memory", "false") in ["true", "True",True]:
1721
- yaml_config["urls"] += get_global_memory_file_paths()
1722
-
1723
- # 临时保存yaml文件,然后读取yaml文件,转换为args
1724
- temp_yaml = os.path.join("actions", f"{uuid.uuid4()}.yml")
1725
- try:
1726
- with open(temp_yaml, "w",encoding="utf-8") as f:
1727
- f.write(convert_yaml_config_to_str(
1728
- yaml_config=yaml_config))
1729
- args = convert_yaml_to_config(temp_yaml)
1730
- finally:
1731
- if os.path.exists(temp_yaml):
1732
- os.remove(temp_yaml)
1733
-
1734
- target_model = args.commit_model or args.model
1735
- llm = get_single_llm(target_model, product_mode)
1736
- printer = Printer()
1737
- printer.print_in_terminal("commit_generating", style="yellow", model_name=target_model)
1738
- commit_message = ""
1739
-
1740
- try:
1741
- uncommitted_changes = git_utils.get_uncommitted_changes(".")
1742
- commit_message = git_utils.generate_commit_message.with_llm(llm).run(
1743
- uncommitted_changes
1744
- )
1745
- # memory["conversation"].append(
1746
- # {"role": "user", "content": commit_message})
1747
- except Exception as e:
1748
- printer.print_in_terminal("commit_failed", style="red", error=str(e), model_name=target_model)
1749
- return
1750
-
1751
- yaml_config["query"] = commit_message
1752
- yaml_content = convert_yaml_config_to_str(yaml_config=yaml_config)
1753
- with open(os.path.join(execute_file), "w",encoding="utf-8") as f:
1754
- f.write(yaml_content)
1755
-
1756
- args.file = execute_file
1757
-
1758
- file_name = os.path.basename(execute_file)
1759
- commit_result = git_utils.commit_changes(
1760
- ".", f"{commit_message}\nauto_coder_{file_name}"
1761
- )
1762
-
1763
- printer = Printer()
1764
-
1765
- action_yml_file_manager = ActionYmlFileManager(args.source_dir)
1766
- action_file_name = os.path.basename(args.file)
1767
- add_updated_urls = []
1768
- for file in commit_result.changed_files:
1769
- add_updated_urls.append(os.path.join(args.source_dir, file))
1770
-
1771
- args.add_updated_urls = add_updated_urls
1772
- update_yaml_success = action_yml_file_manager.update_yaml_field(action_file_name, "add_updated_urls", add_updated_urls)
1773
- if not update_yaml_success:
1774
- printer.print_in_terminal("yaml_save_error", style="red", yaml_file=action_file_name)
1775
-
1776
- if args.enable_active_context:
1777
- active_context_manager = ActiveContextManager(llm, args.source_dir)
1778
- task_id = active_context_manager.process_changes(args)
1779
- printer.print_in_terminal("active_context_background_task",
1780
- style="blue",
1781
- task_id=task_id)
1782
- git_utils.print_commit_info(commit_result=commit_result)
1783
- if commit_message:
1784
- printer.print_in_terminal("commit_message", style="green", model_name=target_model, message=commit_message)
1785
- except Exception as e:
1786
- import traceback
1787
- traceback.print_exc()
1788
- print(f"Failed to commit: {e}")
1789
- if execute_file:
1790
- os.remove(execute_file)
1791
-
1792
-
1793
- @run_in_raw_thread()
1794
- def coding(query: str):
1795
- console = Console()
1796
- is_apply = query.strip().startswith("/apply")
1797
- if is_apply:
1798
- query = query.replace("/apply", "", 1).strip()
1799
-
1800
- is_next = query.strip().startswith("/next")
1801
- if is_next:
1802
- query = query.replace("/next", "", 1).strip()
1803
-
1804
- if is_next:
1805
- code_next(query)
1806
- return
1807
-
1808
- # memory["conversation"].append({"role": "user", "content": query})
1809
- conf = memory.get("conf", {})
1810
-
1811
- current_files = memory["current_files"]["files"]
1812
- current_groups = memory["current_files"].get("current_groups", [])
1813
- groups = memory["current_files"].get("groups", {})
1814
- groups_info = memory["current_files"].get("groups_info", {})
1815
-
1816
- def prepare_chat_yaml():
1817
- auto_coder_main(["next", "chat_action"])
1818
-
1819
- prepare_chat_yaml()
1820
-
1821
- latest_yaml_file = get_last_yaml_file("actions")
1822
-
1823
- if latest_yaml_file:
1824
- yaml_config = {
1825
- "include_file": ["./base/base.yml"],
1826
- "auto_merge": conf.get("auto_merge", "editblock"),
1827
- "human_as_model": conf.get("human_as_model", "false") == "true",
1828
- "skip_build_index": conf.get("skip_build_index", "true") == "true",
1829
- "skip_confirm": conf.get("skip_confirm", "true") == "true",
1830
- "silence": conf.get("silence", "true") == "true",
1831
- "include_project_structure": conf.get("include_project_structure", "true")
1832
- == "true",
1833
- "exclude_files": memory.get("exclude_files", []),
1834
- }
1835
-
1836
- yaml_config["context"] = ""
1837
- yaml_config["in_code_apply"] = is_apply
1838
-
1839
- for key, value in conf.items():
1840
- converted_value = convert_config_value(key, value)
1841
- if converted_value is not None:
1842
- yaml_config[key] = converted_value
1260
+ def commit(query: Optional[str] = None):
1261
+ """
1262
+ 处理提交命令,使用 CommitHandler 进行统一处理
1843
1263
 
1844
- yaml_config["urls"] = current_files + get_llm_friendly_package_docs(
1845
- return_paths=True
1846
- )
1264
+ Args:
1265
+ query: 可选的提交消息或命令
1266
+ """
1267
+ from autocoder.common.file_handler import CommitHandler
1847
1268
 
1848
- if conf.get("enable_global_memory", "false") in ["true", "True",True]:
1849
- yaml_config["urls"] += get_global_memory_file_paths()
1269
+ handler = CommitHandler()
1270
+ handler.handle_commit_command(query)
1850
1271
 
1851
- # handle image
1852
- v = Image.convert_image_paths_from(query)
1853
- yaml_config["query"] = v
1854
1272
 
1855
- # Add context for active groups and their query prefixes
1856
- if current_groups:
1857
- active_groups_context = "下面是对上面文件按分组给到的一些描述,当用户的需求正好匹配描述的时候,参考描述来做修改:\n"
1858
- for group in current_groups:
1859
- group_files = groups.get(group, [])
1860
- query_prefix = groups_info.get(
1861
- group, {}).get("query_prefix", "")
1862
- active_groups_context += f"组名: {group}\n"
1863
- active_groups_context += f"文件列表:\n"
1864
- for file in group_files:
1865
- active_groups_context += f"- {file}\n"
1866
- active_groups_context += f"组描述: {query_prefix}\n\n"
1867
-
1868
- yaml_config["context"] = active_groups_context + "\n"
1869
-
1870
- if is_apply:
1871
- memory_dir = os.path.join(".auto-coder", "memory")
1872
- os.makedirs(memory_dir, exist_ok=True)
1873
- memory_file = os.path.join(memory_dir, "chat_history.json")
1874
-
1875
- def error_message():
1876
- console.print(
1877
- Panel(
1878
- "No chat history found to apply.",
1879
- title="Chat History",
1880
- expand=False,
1881
- border_style="yellow",
1882
- )
1883
- )
1273
+ def coding(query: str, cancel_token=None):
1274
+ """
1275
+ 处理代码生成命令,使用 CodingHandler 进行统一处理
1884
1276
 
1885
- conversations = []
1886
- if os.path.exists(memory_file):
1887
- with open(memory_file, "r",encoding="utf-8") as f:
1888
- chat_history = json.load(f)
1889
-
1890
- if not chat_history["ask_conversation"]:
1891
- error_message()
1892
- else:
1893
- conversations = chat_history["ask_conversation"]
1894
-
1895
- if conversations:
1896
- yaml_config[
1897
- "context"
1898
- ] += f"下面是我们的历史对话,参考我们的历史对话从而更好的理解需求和修改代码: \n\n<history>\n"
1899
- for conv in conversations:
1900
- if conv["role"] == "user":
1901
- yaml_config["context"] += f"用户: {conv['content']}\n"
1902
- elif conv["role"] == "assistant":
1903
- yaml_config["context"] += f"你: {conv['content']}\n"
1904
- yaml_config["context"] += "</history>\n"
1905
-
1906
- yaml_content = convert_yaml_config_to_str(yaml_config=yaml_config)
1277
+ Args:
1278
+ query: 代码生成查询字符串
1279
+ cancel_token: 可选的取消令牌
1280
+ """
1281
+ from autocoder.common.file_handler import CodingHandler
1907
1282
 
1908
- execute_file = os.path.join("actions", latest_yaml_file)
1909
- with open(os.path.join(execute_file), "w",encoding="utf-8") as f:
1910
- f.write(yaml_content)
1283
+ handler = CodingHandler()
1284
+ handler.handle_coding_command(query, cancel_token)
1911
1285
 
1912
- def execute_chat():
1913
- cmd = ["--file", execute_file]
1914
- auto_coder_main(cmd)
1915
- result_manager = ResultManager()
1916
- result_manager.append(content="", meta={"commit_message": f"auto_coder_{latest_yaml_file}","action": "coding", "input":{
1917
- "query": query
1918
- }})
1919
1286
 
1920
- execute_chat()
1921
- else:
1922
- print("Failed to create new YAML file.")
1287
+ def rules(query: str):
1288
+ from autocoder.chat.rules_command import handle_rules_command
1923
1289
 
1924
- save_memory()
1290
+ result = handle_rules_command(query, coding_func=coding)
1291
+ # 只有当结果不为空时才打印,避免重复输出
1292
+ if result and result.strip():
1293
+ print(result)
1925
1294
  completer.refresh_files()
1926
1295
 
1927
- @run_in_raw_thread()
1928
- def rules(query: str):
1929
- from autocoder.chat.rules_command import handle_rules_command
1930
- memory = get_memory()
1931
- handle_rules_command(query, memory,coding_func=coding)
1932
- completer.refresh_files()
1933
1296
 
1934
1297
  @byzerllm.prompt()
1935
1298
  def code_review(query: str) -> str:
@@ -1947,95 +1310,32 @@ def code_review(query: str) -> str:
1947
1310
  如果用户的需求包含了@一个文件名 或者 @@符号, 那么重点关注这些文件或者符号(函数,类)进行上述的review。
1948
1311
  review 过程中严格遵循上述的检查点,不要遗漏,没有发现异常的点直接跳过,只对发现的异常点,给出具体的修改后的代码。
1949
1312
  """
1313
+ return {} # type: ignore
1950
1314
 
1951
1315
 
1952
- @run_in_raw_thread()
1953
1316
  def chat(query: str):
1954
- conf = memory.get("conf", {})
1955
-
1956
- yaml_config = {
1957
- "include_file": ["./base/base.yml"],
1958
- "include_project_structure": conf.get("include_project_structure", "true")
1959
- in ["true", "True"],
1960
- "human_as_model": conf.get("human_as_model", "false") == "true",
1961
- "skip_build_index": conf.get("skip_build_index", "true") == "true",
1962
- "skip_confirm": conf.get("skip_confirm", "true") == "true",
1963
- "silence": conf.get("silence", "true") == "true",
1964
- "exclude_files": memory.get("exclude_files", []),
1965
- }
1966
-
1967
- current_files = memory["current_files"]["files"] + get_llm_friendly_package_docs(
1968
- return_paths=True
1969
- )
1970
-
1971
- if conf.get("enable_global_memory", "false") in ["true", "True",True]:
1972
- current_files += get_global_memory_file_paths()
1973
-
1974
- yaml_config["urls"] = current_files
1975
-
1976
- if "emb_model" in conf:
1977
- yaml_config["emb_model"] = conf["emb_model"]
1978
-
1979
- # 解析命令
1980
- commands_infos = parse_query(query)
1981
- if len(commands_infos) > 0:
1982
- if "query" in commands_infos:
1983
- query = " ".join(commands_infos["query"]["args"])
1984
- else:
1985
- temp_query = ""
1986
- for (command,command_info) in commands_infos.items():
1987
- if command_info["args"]:
1988
- temp_query = " ".join(command_info["args"])
1989
- query = temp_query
1990
-
1991
- is_new = "new" in commands_infos
1992
-
1993
- if "learn" in commands_infos:
1994
- commands_infos["no_context"] = {}
1995
-
1996
- if "review" in commands_infos:
1997
- commands_infos["no_context"] = {}
1998
-
1999
- yaml_config["action"] = commands_infos
2000
-
2001
- for key, value in conf.items():
2002
- converted_value = convert_config_value(key, value)
2003
- if converted_value is not None:
2004
- yaml_config[key] = converted_value
2005
-
2006
- query = Image.convert_image_paths_from(query)
2007
-
2008
- yaml_config["query"] = query
2009
-
2010
- yaml_content = convert_yaml_config_to_str(yaml_config=yaml_config)
2011
-
2012
- execute_file = os.path.join("actions", f"{uuid.uuid4()}.yml")
2013
-
2014
- with open(os.path.join(execute_file), "w",encoding="utf-8") as f:
2015
- f.write(yaml_content)
1317
+ """
1318
+ 处理聊天命令,使用 ChatHandler 进行统一处理
2016
1319
 
2017
- def execute_ask():
2018
- cmd = ["agent", "chat", "--file", execute_file]
2019
- if is_new:
2020
- cmd.append("--new_session")
2021
- auto_coder_main(cmd)
1320
+ Args:
1321
+ query: 聊天查询字符串
1322
+ """
1323
+ from autocoder.common.file_handler import ChatHandler
2022
1324
 
2023
- try:
2024
- execute_ask()
2025
- finally:
2026
- os.remove(execute_file)
1325
+ handler = ChatHandler()
1326
+ handler.handle_chat_command(query)
2027
1327
 
2028
1328
 
2029
- @run_in_raw_thread()
2030
1329
  def summon(query: str):
1330
+ memory = get_current_memory()
2031
1331
  conf = memory.get("conf", {})
2032
- current_files = memory["current_files"]["files"]
1332
+ current_files = get_current_files()
2033
1333
 
2034
1334
  file_contents = []
2035
1335
  for file in current_files:
2036
1336
  if os.path.exists(file):
2037
1337
  try:
2038
- with open(file, "r",encoding="utf-8") as f:
1338
+ with open(file, "r", encoding="utf-8") as f:
2039
1339
  content = f.read()
2040
1340
  s = f"##File: {file}\n{content}\n\n"
2041
1341
  file_contents.append(s)
@@ -2071,7 +1371,7 @@ def summon(query: str):
2071
1371
 
2072
1372
  execute_file = os.path.join("actions", f"{uuid.uuid4()}.yml")
2073
1373
 
2074
- with open(os.path.join(execute_file), "w",encoding="utf-8") as f:
1374
+ with open(os.path.join(execute_file), "w", encoding="utf-8") as f:
2075
1375
  f.write(yaml_content)
2076
1376
 
2077
1377
  def execute_summon():
@@ -2083,9 +1383,8 @@ def summon(query: str):
2083
1383
  os.remove(execute_file)
2084
1384
 
2085
1385
 
2086
- @run_in_raw_thread()
2087
1386
  def design(query: str):
2088
-
1387
+ memory = get_current_memory()
2089
1388
  conf = memory.get("conf", {})
2090
1389
  yaml_config = {
2091
1390
  "include_file": ["./base/base.yml"],
@@ -2118,7 +1417,7 @@ def design(query: str):
2118
1417
 
2119
1418
  execute_file = os.path.join("actions", f"{uuid.uuid4()}.yml")
2120
1419
 
2121
- with open(os.path.join(execute_file), "w",encoding="utf-8") as f:
1420
+ with open(os.path.join(execute_file), "w", encoding="utf-8") as f:
2122
1421
  f.write(yaml_content)
2123
1422
 
2124
1423
  def execute_design():
@@ -2130,117 +1429,21 @@ def design(query: str):
2130
1429
  os.remove(execute_file)
2131
1430
 
2132
1431
 
2133
- @run_in_raw_thread()
2134
1432
  def active_context(query: str):
2135
1433
  """
2136
- 管理活动上下文任务,支持列表、查询等操作
2137
-
1434
+ 处理活动上下文命令,使用 ActiveContextHandler 进行统一处理
1435
+
2138
1436
  Args:
2139
1437
  query: 命令参数,例如 "list" 列出所有任务
2140
- """
2141
- # 解析命令
2142
- commands_infos = parse_query(query)
2143
- command = "list" # 默认命令是列出所有任务
2144
-
2145
- if len(commands_infos) > 0:
2146
- if "list" in commands_infos:
2147
- command = "list"
2148
- if "run" in commands_infos:
2149
- command = "run"
2150
-
2151
- args = get_final_config()
2152
- printer = Printer()
2153
- # 获取LLM实例
2154
- llm = get_single_llm(args.model,product_mode=args.product_mode)
2155
- action_file_manager = ActionYmlFileManager(args.source_dir)
2156
-
2157
- # 获取配置和参数
2158
-
2159
-
2160
- active_context_manager = ActiveContextManager(llm, args.source_dir)
2161
- if command == "run":
2162
- file_name = commands_infos["run"]["args"][-1]
2163
- args.file = action_file_manager.get_full_path_by_file_name(file_name)
2164
- ## 因为更新了args.file
2165
- active_context_manager = ActiveContextManager(llm, args.source_dir)
2166
- task_id = active_context_manager.process_changes(args)
2167
- printer.print_in_terminal("active_context_background_task",
2168
- style="blue",
2169
- task_id=task_id)
2170
-
2171
- # 处理不同的命令
2172
- if command == "list":
2173
- # 获取所有任务
2174
- all_tasks = active_context_manager.get_all_tasks()
2175
-
2176
- if not all_tasks:
2177
- console = Console()
2178
- console.print("[yellow]没有找到任何活动上下文任务[/yellow]")
2179
- return
2180
-
2181
- # 创建表格
2182
- table = Table(title="活动上下文任务列表", show_lines=True, expand=True)
2183
- table.add_column("任务ID", style="cyan", no_wrap=False)
2184
- table.add_column("状态", style="green", no_wrap=False)
2185
- table.add_column("开始时间", style="yellow", no_wrap=False)
2186
- table.add_column("完成时间", style="yellow", no_wrap=False)
2187
- table.add_column("文件", style="blue", no_wrap=False)
2188
- table.add_column("Token统计", style="magenta", no_wrap=False)
2189
- table.add_column("费用", style="red", no_wrap=False)
2190
-
2191
- # 添加任务数据
2192
- for task in all_tasks:
2193
- status = task.get("status", "未知")
2194
- status_display = status
2195
-
2196
- # 根据状态设置不同的显示样式
2197
- if status == "completed":
2198
- status_display = "[green]已完成[/green]"
2199
- elif status == "running":
2200
- status_display = "[blue]运行中[/blue]"
2201
- elif status == "queued":
2202
- position = task.get("queue_position", 0)
2203
- status_display = f"[yellow]排队中 (位置: {position})[/yellow]"
2204
- elif status == "failed":
2205
- status_display = "[red]失败[/red]"
2206
-
2207
- # 格式化时间
2208
- start_time = task.get("start_time", "")
2209
- start_time_str = start_time.strftime("%Y-%m-%d %H:%M:%S") if start_time else "未知"
2210
-
2211
- completion_time = task.get("completion_time", "")
2212
- completion_time_str = completion_time.strftime("%Y-%m-%d %H:%M:%S") if completion_time else "-"
2213
-
2214
- # 获取文件名
2215
- file_name = task.get("file_name", "未知")
2216
-
2217
- # 获取token信息
2218
- total_tokens = task.get("total_tokens", 0)
2219
- input_tokens = task.get("input_tokens", 0)
2220
- output_tokens = task.get("output_tokens", 0)
2221
- token_info = f"总计: {total_tokens:,}\n输入: {input_tokens:,}\n输出: {output_tokens:,}"
2222
-
2223
- # 获取费用信息
2224
- cost = task.get("cost", 0.0)
2225
- cost_info = f"${cost:.6f}"
2226
-
2227
- # 添加到表格
2228
- table.add_row(
2229
- task.get("task_id", "未知"),
2230
- status_display,
2231
- start_time_str,
2232
- completion_time_str,
2233
- file_name,
2234
- token_info,
2235
- cost_info
2236
- )
2237
-
2238
- # 显示表格
2239
- console = Console(width=120) # 设置更宽的显示宽度
2240
- console.print(table)
1438
+ """
1439
+ from autocoder.common.file_handler import ActiveContextHandler
1440
+
1441
+ handler = ActiveContextHandler()
1442
+ handler.handle_active_context_command(query)
2241
1443
 
2242
1444
 
2243
1445
  def voice_input():
1446
+ memory = get_current_memory()
2244
1447
  conf = memory.get("conf", {})
2245
1448
  yaml_config = {
2246
1449
  "include_file": ["./base/base.yml"],
@@ -2257,7 +1460,7 @@ def voice_input():
2257
1460
 
2258
1461
  execute_file = os.path.join("actions", f"{uuid.uuid4()}.yml")
2259
1462
 
2260
- with open(os.path.join(execute_file), "w",encoding="utf-8") as f:
1463
+ with open(os.path.join(execute_file), "w", encoding="utf-8") as f:
2261
1464
  f.write(yaml_content)
2262
1465
 
2263
1466
  def execute_voice2text_command():
@@ -2265,14 +1468,16 @@ def voice_input():
2265
1468
 
2266
1469
  try:
2267
1470
  execute_voice2text_command()
2268
- with open(os.path.join(".auto-coder", "exchange.txt"), "r",encoding="utf-8") as f:
1471
+ with open(
1472
+ os.path.join(".auto-coder", "exchange.txt"), "r", encoding="utf-8"
1473
+ ) as f:
2269
1474
  return f.read()
2270
1475
  finally:
2271
1476
  os.remove(execute_file)
2272
1477
 
2273
1478
 
2274
- @run_in_raw_thread()
2275
1479
  def generate_shell_command(input_text):
1480
+ memory = get_current_memory()
2276
1481
  conf = memory.get("conf", {})
2277
1482
  yaml_config = {
2278
1483
  "include_file": ["./base/base.yml"],
@@ -2283,432 +1488,62 @@ def generate_shell_command(input_text):
2283
1488
 
2284
1489
  yaml_config["query"] = input_text
2285
1490
 
2286
- yaml_content = convert_yaml_config_to_str(yaml_config=yaml_config)
2287
-
2288
- execute_file = os.path.join("actions", f"{uuid.uuid4()}.yml")
2289
-
2290
- with open(os.path.join(execute_file), "w",encoding="utf-8") as f:
2291
- f.write(yaml_content)
2292
-
2293
- try:
2294
- auto_coder_main(["agent", "generate_command", "--file", execute_file])
2295
- with open(os.path.join(".auto-coder", "exchange.txt"), "r",encoding="utf-8") as f:
2296
- shell_script = f.read()
2297
- result_manager = ResultManager()
2298
- result_manager.add_result(content=shell_script,meta={
2299
- "action": "generate_shell_command",
2300
- "input": {
2301
- "query": input_text
2302
- }
2303
- })
2304
- return shell_script
2305
- finally:
2306
- os.remove(execute_file)
2307
-
2308
- def manage_models(query: str):
2309
- """
2310
- Handle /models subcommands:
2311
- /models /list - List all models (default + custom)
2312
- /models /add <name> <api_key> - Add model with simplified params
2313
- /models /add_model name=xxx base_url=xxx ... - Add model with custom params
2314
- /models /remove <name> - Remove model by name
2315
- """
2316
- console = Console()
2317
- printer = Printer(console=console)
2318
-
2319
- product_mode = memory.get("product_mode", "lite")
2320
- if product_mode != "lite":
2321
- printer.print_in_terminal("models_lite_only", style="red")
2322
- return
2323
-
2324
- models_data = models_module.load_models()
2325
- subcmd = ""
2326
- if "/list" in query:
2327
- subcmd = "/list"
2328
- query = query.replace("/list", "", 1).strip()
2329
-
2330
- if "/add_model" in query:
2331
- subcmd = "/add_model"
2332
- query = query.replace("/add_model", "", 1).strip()
2333
-
2334
- if "/add" in query:
2335
- subcmd = "/add"
2336
- query = query.replace("/add", "", 1).strip()
2337
-
2338
- # alias to /add
2339
- if "/activate" in query:
2340
- subcmd = "/add"
2341
- query = query.replace("/activate", "", 1).strip()
2342
-
2343
- if "/remove" in query:
2344
- subcmd = "/remove"
2345
- query = query.replace("/remove", "", 1).strip()
2346
-
2347
- if "/speed-test" in query:
2348
- subcmd = "/speed-test"
2349
- query = query.replace("/speed-test", "", 1).strip()
2350
-
2351
- if "/speed_test" in query:
2352
- subcmd = "/speed-test"
2353
- query = query.replace("/speed_test", "", 1).strip()
2354
-
2355
- if "input_price" in query:
2356
- subcmd = "/input_price"
2357
- query = query.replace("/input_price", "", 1).strip()
2358
-
2359
- if "output_price" in query:
2360
- subcmd = "/output_price"
2361
- query = query.replace("/output_price", "", 1).strip()
2362
-
2363
- if "/speed" in query:
2364
- subcmd = "/speed"
2365
- query = query.replace("/speed", "", 1).strip()
1491
+ yaml_content = convert_yaml_config_to_str(yaml_config=yaml_config)
2366
1492
 
1493
+ execute_file = os.path.join("actions", f"{uuid.uuid4()}.yml")
2367
1494
 
1495
+ with open(os.path.join(execute_file), "w", encoding="utf-8") as f:
1496
+ f.write(yaml_content)
2368
1497
 
2369
- if not subcmd:
2370
- printer.print_in_terminal("models_usage")
1498
+ try:
1499
+ auto_coder_main(["agent", "generate_command", "--file", execute_file])
1500
+ with open(
1501
+ os.path.join(".auto-coder", "exchange.txt"), "r", encoding="utf-8"
1502
+ ) as f:
1503
+ shell_script = f.read()
1504
+ result_manager = ResultManager()
1505
+ result_manager.add_result(
1506
+ content=shell_script,
1507
+ meta={"action": "generate_shell_command", "input": {"query": input_text}},
1508
+ )
1509
+ return shell_script
1510
+ finally:
1511
+ os.remove(execute_file)
2371
1512
 
2372
- result_manager = ResultManager()
2373
- if subcmd == "/list":
2374
- if models_data:
2375
- # Sort models by speed (average_speed)
2376
- sorted_models = sorted(models_data, key=lambda x: float(x.get('average_speed', 0)))
2377
- sorted_models.reverse()
2378
-
2379
- table = Table(
2380
- title=printer.get_message_from_key("models_title"),
2381
- expand=True,
2382
- show_lines=True
2383
- )
2384
- table.add_column("Name", style="cyan", width=30, overflow="fold", no_wrap=False)
2385
- table.add_column("Model Name", style="magenta", width=30, overflow="fold", no_wrap=False)
2386
- table.add_column("Base URL", style="white", width=40, overflow="fold", no_wrap=False)
2387
- table.add_column("Input Price (M)", style="magenta", width=15, overflow="fold", no_wrap=False)
2388
- table.add_column("Output Price (M)", style="magenta", width=15, overflow="fold", no_wrap=False)
2389
- table.add_column("Speed (s/req)", style="blue", width=15, overflow="fold", no_wrap=False)
2390
- for m in sorted_models:
2391
- # Check if api_key_path exists and file exists
2392
- is_api_key_set = "api_key" in m
2393
- name = m.get("name", "")
2394
- if is_api_key_set:
2395
- api_key = m.get("api_key", "").strip()
2396
- if not api_key:
2397
- printer.print_in_terminal("models_api_key_empty", style="yellow", name=name)
2398
- name = f"{name} *"
2399
-
2400
- table.add_row(
2401
- name,
2402
- m.get("model_name", ""),
2403
- m.get("base_url", ""),
2404
- f"{m.get('input_price', 0.0):.2f}",
2405
- f"{m.get('output_price', 0.0):.2f}",
2406
- f"{m.get('average_speed', 0.0):.3f}"
2407
- )
2408
- console.print(table)
2409
- result_manager.add_result(content=json.dumps(sorted_models,ensure_ascii=False),meta={
2410
- "action": "models",
2411
- "input": {
2412
- "query": query
2413
- }
2414
- })
2415
1513
 
2416
- else:
2417
- printer.print_in_terminal("models_no_models", style="yellow")
2418
- result_manager.add_result(content="No models found",meta={
2419
- "action": "models",
2420
- "input": {
2421
- "query": query
2422
- }
2423
- })
2424
-
2425
- elif subcmd == "/input_price":
2426
- args = query.strip().split()
2427
- if len(args) >= 2:
2428
- name = args[0]
2429
- try:
2430
- price = float(args[1])
2431
- if models_module.update_model_input_price(name, price):
2432
- printer.print_in_terminal("models_input_price_updated", style="green", name=name, price=price)
2433
- result_manager.add_result(content=f"models_input_price_updated: {name} {price}",meta={
2434
- "action": "models",
2435
- "input": {
2436
- "query": query
2437
- }
2438
- })
2439
- else:
2440
- printer.print_in_terminal("models_not_found", style="red", name=name)
2441
- result_manager.add_result(content=f"models_not_found: {name}",meta={
2442
- "action": "models",
2443
- "input": {
2444
- "query": query
2445
- }
2446
- })
2447
- except ValueError as e:
2448
- result_manager.add_result(content=f"models_invalid_price: {str(e)}",meta={
2449
- "action": "models",
2450
- "input": {
2451
- "query": query
2452
- }
2453
- })
2454
- printer.print_in_terminal("models_invalid_price", style="red", error=str(e))
2455
- else:
2456
- result_manager.add_result(content=printer.get_message_from_key("models_input_price_usage"),meta={
2457
- "action": "models",
2458
- "input": {
2459
- "query": query
2460
- }
2461
- })
2462
- printer.print_in_terminal("models_input_price_usage", style="red")
2463
-
2464
- elif subcmd == "/output_price":
2465
- args = query.strip().split()
2466
- if len(args) >= 2:
2467
- name = args[0]
2468
- try:
2469
- price = float(args[1])
2470
- if models_module.update_model_output_price(name, price):
2471
- printer.print_in_terminal("models_output_price_updated", style="green", name=name, price=price)
2472
- result_manager.add_result(content=f"models_output_price_updated: {name} {price}",meta={
2473
- "action": "models",
2474
- "input": {
2475
- "query": query
2476
- }
2477
- })
2478
- else:
2479
- printer.print_in_terminal("models_not_found", style="red", name=name)
2480
- result_manager.add_result(content=f"models_not_found: {name}",meta={
2481
- "action": "models",
2482
- "input": {
2483
- "query": query
2484
- }
2485
- })
2486
- except ValueError as e:
2487
- printer.print_in_terminal("models_invalid_price", style="red", error=str(e))
2488
- result_manager.add_result(content=f"models_invalid_price: {str(e)}",meta={
2489
- "action": "models",
2490
- "input": {
2491
- "query": query
2492
- }
2493
- })
2494
- else:
2495
- result_manager.add_result(content=printer.get_message_from_key("models_output_price_usage"),meta={
2496
- "action": "models",
2497
- "input": {
2498
- "query": query
2499
- }
2500
- })
2501
- printer.print_in_terminal("models_output_price_usage", style="red")
2502
-
2503
- elif subcmd == "/speed":
2504
- args = query.strip().split()
2505
- if len(args) >= 2:
2506
- name = args[0]
2507
- try:
2508
- speed = float(args[1])
2509
- if models_module.update_model_speed(name, speed):
2510
- printer.print_in_terminal("models_speed_updated", style="green", name=name, speed=speed)
2511
- result_manager.add_result(content=f"models_speed_updated: {name} {speed}",meta={
2512
- "action": "models",
2513
- "input": {
2514
- "query": query
2515
- }
2516
- })
2517
- else:
2518
- printer.print_in_terminal("models_not_found", style="red", name=name)
2519
- result_manager.add_result(content=f"models_not_found: {name}",meta={
2520
- "action": "models",
2521
- "input": {
2522
- "query": query
2523
- }
2524
- })
2525
- except ValueError as e:
2526
- printer.print_in_terminal("models_invalid_speed", style="red", error=str(e))
2527
- result_manager.add_result(content=f"models_invalid_speed: {str(e)}",meta={
2528
- "action": "models",
2529
- "input": {
2530
- "query": query
2531
- }
2532
- })
2533
- else:
2534
- result_manager.add_result(content=printer.get_message_from_key("models_speed_usage"),meta={
2535
- "action": "models",
2536
- "input": {
2537
- "query": query
2538
- }
2539
- })
2540
- printer.print_in_terminal("models_speed_usage", style="red")
2541
-
2542
- elif subcmd == "/speed-test":
2543
- from autocoder.common.model_speed_tester import render_speed_test_in_terminal
2544
- test_rounds = 1 # 默认测试轮数
2545
-
2546
- enable_long_context = False
2547
- if "/long_context" in query:
2548
- enable_long_context = True
2549
- query = query.replace("/long_context", "", 1).strip()
2550
-
2551
- if "/long-context" in query:
2552
- enable_long_context = True
2553
- query = query.replace("/long-context", "", 1).strip()
2554
-
2555
- # 解析可选的测试轮数参数
2556
- args = query.strip().split()
2557
- if args and args[0].isdigit():
2558
- test_rounds = int(args[0])
2559
-
2560
- render_speed_test_in_terminal(product_mode, test_rounds,enable_long_context=enable_long_context)
2561
- ## 等待优化,获取明细数据
2562
- result_manager.add_result(content="models test success",meta={
2563
- "action": "models",
2564
- "input": {
2565
- "query": query
2566
- }
2567
- })
2568
-
2569
- elif subcmd == "/add":
2570
- # Support both simplified and legacy formats
2571
- args = query.strip().split(" ")
2572
- if len(args) == 2:
2573
- # Simplified: /models /add <name> <api_key>
2574
- name, api_key = args[0], args[1]
2575
- result = models_module.update_model_with_api_key(name, api_key)
2576
- if result:
2577
- result_manager.add_result(content=f"models_added: {name}",meta={
2578
- "action": "models",
2579
- "input": {
2580
- "query": query
2581
- }
2582
- })
2583
- printer.print_in_terminal("models_added", style="green", name=name)
2584
- else:
2585
- result_manager.add_result(content=f"models_add_failed: {name}",meta={
2586
- "action": "models",
2587
- "input": {
2588
- "query": query
2589
- }
2590
- })
2591
- printer.print_in_terminal("models_add_failed", style="red", name=name)
2592
- else:
2593
- models_list = "\n".join([m["name"] for m in models_module.default_models_list])
2594
- printer.print_in_terminal("models_add_usage", style="red", models=models_list)
2595
- result_manager.add_result(content=printer.get_message_from_key_with_format("models_add_usage",models=models_list),meta={
2596
- "action": "models",
2597
- "input": {
2598
- "query": query
2599
- }
2600
- })
2601
-
2602
- elif subcmd == "/add_model":
2603
- # Parse key=value pairs: /models /add_model name=abc base_url=http://xx ...
2604
- # Collect key=value pairs
2605
- kv_pairs = shlex.split(query)
2606
- data_dict = {}
2607
- for pair in kv_pairs:
2608
- if '=' not in pair:
2609
- printer.print_in_terminal("models_add_model_params", style="red")
2610
- continue
2611
- k, v = pair.split('=', 1)
2612
- data_dict[k.strip()] = v.strip()
2613
-
2614
- # Name is required
2615
- if "name" not in data_dict:
2616
- printer.print_in_terminal("models_add_model_name_required", style="red")
2617
- return
1514
+ def manage_models(query: str):
1515
+ """
1516
+ 处理模型管理命令,使用 ModelsHandler 进行统一处理
2618
1517
 
2619
- # Check duplication
2620
- if any(m["name"] == data_dict["name"] for m in models_data):
2621
- printer.print_in_terminal("models_add_model_exists", style="yellow", name=data_dict["name"])
2622
- result_manager.add_result(content=printer.get_message_from_key_with_format("models_add_model_exists",name=data_dict["name"]),meta={
2623
- "action": "models",
2624
- "input": {
2625
- "query": query
2626
- }
2627
- })
2628
- return
1518
+ Args:
1519
+ query: 查询字符串,支持多种模型管理子命令
1520
+ """
1521
+ from autocoder.common.file_handler import ModelsHandler
2629
1522
 
2630
- # Create model with defaults
2631
- final_model = {
2632
- "name": data_dict["name"],
2633
- "model_type": data_dict.get("model_type", "saas/openai"),
2634
- "model_name": data_dict.get("model_name", data_dict["name"]),
2635
- "base_url": data_dict.get("base_url", "https://api.openai.com/v1"),
2636
- "api_key_path": data_dict.get("api_key_path", "api.openai.com"),
2637
- "description": data_dict.get("description", ""),
2638
- "is_reasoning": data_dict.get("is_reasoning", "false") in ["true", "True", "TRUE", "1"]
2639
- }
1523
+ handler = ModelsHandler()
1524
+ handler.handle_models_command(query)
2640
1525
 
2641
- models_data.append(final_model)
2642
- models_module.save_models(models_data)
2643
- printer.print_in_terminal("models_add_model_success", style="green", name=data_dict["name"])
2644
- result_manager.add_result(content=f"models_add_model_success: {data_dict['name']}",meta={
2645
- "action": "models",
2646
- "input": {
2647
- "query": query
2648
- }
2649
- })
2650
-
2651
- elif subcmd == "/remove":
2652
- args = query.strip().split(" ")
2653
- if len(args) < 1:
2654
- printer.print_in_terminal("models_add_usage", style="red")
2655
- result_manager.add_result(content=printer.get_message_from_key("models_add_usage"),meta={
2656
- "action": "models",
2657
- "input": {
2658
- "query": query
2659
- }
2660
- })
2661
- return
2662
- name = args[0]
2663
- filtered_models = [m for m in models_data if m["name"] != name]
2664
- if len(filtered_models) == len(models_data):
2665
- printer.print_in_terminal("models_add_model_remove", style="yellow", name=name)
2666
- result_manager.add_result(content=printer.get_message_from_key_with_format("models_add_model_remove",name=name),meta={
2667
- "action": "models",
2668
- "input": {
2669
- "query": query
2670
- }
2671
- })
2672
- return
2673
- models_module.save_models(filtered_models)
2674
- printer.print_in_terminal("models_add_model_removed", style="green", name=name)
2675
- result_manager.add_result(content=printer.get_message_from_key_with_format("models_add_model_removed",name=name),meta={
2676
- "action": "models",
2677
- "input": {
2678
- "query": query
2679
- }
2680
- })
2681
- else:
2682
- printer.print_in_terminal("models_unknown_subcmd", style="yellow", subcmd=subcmd)
2683
- result_manager.add_result(content=printer.get_message_from_key_with_format("models_unknown_subcmd",subcmd=subcmd),meta={
2684
- "action": "models",
2685
- "input": {
2686
- "query": query
2687
- }
2688
- })
2689
1526
 
2690
1527
  def exclude_dirs(dir_names: List[str]):
2691
- new_dirs = dir_names
2692
- existing_dirs = memory.get("exclude_dirs", [])
2693
- dirs_to_add = [d for d in new_dirs if d not in existing_dirs]
2694
-
2695
- if dirs_to_add:
2696
- existing_dirs.extend(dirs_to_add)
2697
- if "exclude_dirs" not in memory:
2698
- memory["exclude_dirs"] = existing_dirs
2699
- print(f"Added exclude dirs: {dirs_to_add}")
2700
- exclude_files([f"regex://.*/{d}/*." for d in dirs_to_add])
1528
+ memory_manager = get_memory_manager()
1529
+ new_dirs = memory_manager.add_exclude_dirs(dir_names)
1530
+
1531
+ if new_dirs:
1532
+ print(f"Added exclude dirs: {new_dirs}")
1533
+ exclude_files(",".join([f"regex://.*/{d}/*." for d in new_dirs]))
2701
1534
  else:
2702
1535
  print("All specified dirs are already in the exclude list.")
2703
- save_memory()
2704
1536
  completer.refresh_files()
2705
1537
 
1538
+
2706
1539
  def exclude_files(query: str):
1540
+ memory_manager = get_memory_manager()
2707
1541
  result_manager = ResultManager()
2708
1542
  printer = Printer()
1543
+
2709
1544
  if "/list" in query:
2710
1545
  query = query.replace("/list", "", 1).strip()
2711
- existing_file_patterns = memory.get("exclude_files", [])
1546
+ existing_file_patterns = memory_manager.get_exclude_files()
2712
1547
  console = Console()
2713
1548
  # 打印表格
2714
1549
  table = Table(title="Exclude Files")
@@ -2716,70 +1551,58 @@ def exclude_files(query: str):
2716
1551
  for file_pattern in existing_file_patterns:
2717
1552
  table.add_row(file_pattern)
2718
1553
  console.print(table)
2719
- result_manager.add_result(content=f"Exclude files: {existing_file_patterns}",meta={
2720
- "action": "exclude_files",
2721
- "input": {
2722
- "query": query
2723
- }
2724
- })
1554
+ result_manager.add_result(
1555
+ content=f"Exclude files: {existing_file_patterns}",
1556
+ meta={"action": "exclude_files", "input": {"query": query}},
1557
+ )
2725
1558
  return
2726
1559
 
2727
1560
  if "/drop" in query:
2728
1561
  query = query.replace("/drop", "", 1).strip()
2729
- existing_file_patterns = memory.get("exclude_files", [])
2730
- existing_file_patterns.remove(query.strip())
2731
- memory["exclude_files"] = existing_file_patterns
2732
- save_memory()
1562
+ removed_patterns = memory_manager.remove_exclude_files([query.strip()])
2733
1563
  completer.refresh_files()
2734
- result_manager.add_result(content=f"Dropped exclude files: {query}",meta={
2735
- "action": "exclude_files",
2736
- "input": {
2737
- "query": query
2738
- }
2739
- })
1564
+ result_manager.add_result(
1565
+ content=f"Dropped exclude files: {removed_patterns}",
1566
+ meta={"action": "exclude_files", "input": {"query": query}},
1567
+ )
2740
1568
  return
2741
-
1569
+
2742
1570
  new_file_patterns = query.strip().split(",")
2743
-
2744
- existing_file_patterns = memory.get("exclude_files", [])
2745
- file_patterns_to_add = [f for f in new_file_patterns if f not in existing_file_patterns]
2746
-
2747
- for file_pattern in file_patterns_to_add:
2748
- if not file_pattern.startswith("regex://"):
2749
- result_manager.add_result(content=printer.get_message_from_key_with_format("invalid_file_pattern", file_pattern=file_pattern),meta={
2750
- "action": "exclude_files",
2751
- "input": {
2752
- "query": file_pattern
2753
- }
2754
- })
2755
- raise ValueError(printer.get_message_from_key_with_format("invalid_file_pattern", file_pattern=file_pattern))
2756
-
2757
- if file_patterns_to_add:
2758
- existing_file_patterns.extend(file_patterns_to_add)
2759
- if "exclude_files" not in memory:
2760
- memory["exclude_files"] = existing_file_patterns
2761
-
2762
- result_manager.add_result(content=f"Added exclude files: {file_patterns_to_add}",meta={
2763
- "action": "exclude_files",
2764
- "input": {
2765
- "query": file_patterns_to_add
2766
- }
2767
- })
2768
- save_memory()
2769
- print(f"Added exclude files: {file_patterns_to_add}")
1571
+
1572
+ # Validate patterns
1573
+ for file_pattern in new_file_patterns:
1574
+ if not file_pattern.startswith("regex://"):
1575
+ result_manager.add_result(
1576
+ content=printer.get_message_from_key_with_format(
1577
+ "invalid_file_pattern", file_pattern=file_pattern
1578
+ ),
1579
+ meta={"action": "exclude_files", "input": {"query": file_pattern}},
1580
+ )
1581
+ raise ValueError(
1582
+ printer.get_message_from_key_with_format(
1583
+ "invalid_file_pattern", file_pattern=file_pattern
1584
+ )
1585
+ )
1586
+
1587
+ # Add new patterns
1588
+ new_patterns_added = memory_manager.add_exclude_files(new_file_patterns)
1589
+
1590
+ if new_patterns_added:
1591
+ result_manager.add_result(
1592
+ content=f"Added exclude files: {new_patterns_added}",
1593
+ meta={"action": "exclude_files", "input": {"query": new_patterns_added}},
1594
+ )
1595
+ print(f"Added exclude files: {new_patterns_added}")
2770
1596
  else:
2771
- result_manager.add_result(content=f"All specified files are already in the exclude list.",meta={
2772
- "action": "exclude_files",
2773
- "input": {
2774
- "query": file_patterns_to_add
2775
- }
2776
- })
1597
+ result_manager.add_result(
1598
+ content=f"All specified files are already in the exclude list.",
1599
+ meta={"action": "exclude_files", "input": {"query": new_file_patterns}},
1600
+ )
2777
1601
  print("All specified files are already in the exclude list.")
2778
-
2779
-
2780
1602
 
2781
- @run_in_raw_thread()
1603
+
2782
1604
  def index_build():
1605
+ memory = get_memory()
2783
1606
  conf = memory.get("conf", {})
2784
1607
  yaml_config = {
2785
1608
  "include_file": ["./base/base.yml"],
@@ -2794,17 +1617,18 @@ def index_build():
2794
1617
  yaml_content = convert_yaml_config_to_str(yaml_config=yaml_config)
2795
1618
  yaml_file = os.path.join("actions", f"{uuid.uuid4()}.yml")
2796
1619
 
2797
- with open(yaml_file, "w",encoding="utf-8") as f:
1620
+ with open(yaml_file, "w", encoding="utf-8") as f:
2798
1621
  f.write(yaml_content)
2799
- try:
2800
- auto_coder_main(["index", "--file", yaml_file])
1622
+ try:
1623
+ auto_coder_main(["index", "--file", yaml_file])
2801
1624
  completer.refresh_files()
2802
1625
  finally:
2803
1626
  os.remove(yaml_file)
2804
1627
 
2805
1628
 
2806
- def get_final_config()->AutoCoderArgs:
2807
- conf = memory.get("conf", {})
1629
+ def get_final_config() -> AutoCoderArgs:
1630
+ memory_manager = get_memory_manager()
1631
+ conf = memory_manager.get_all_config()
2808
1632
  yaml_config = {
2809
1633
  "include_file": ["./base/base.yml"],
2810
1634
  "auto_merge": conf.get("auto_merge", "editblock"),
@@ -2812,9 +1636,9 @@ def get_final_config()->AutoCoderArgs:
2812
1636
  "skip_build_index": conf.get("skip_build_index", "true") == "true",
2813
1637
  "skip_confirm": conf.get("skip_confirm", "true") == "true",
2814
1638
  "silence": conf.get("silence", "true") == "true",
2815
- "include_project_structure": conf.get("include_project_structure", "true")
1639
+ "include_project_structure": conf.get("include_project_structure", "false")
2816
1640
  == "true",
2817
- "exclude_files": memory.get("exclude_files", []),
1641
+ "exclude_files": memory_manager.get_exclude_files(),
2818
1642
  }
2819
1643
  for key, value in conf.items():
2820
1644
  converted_value = convert_config_value(key, value)
@@ -2823,7 +1647,7 @@ def get_final_config()->AutoCoderArgs:
2823
1647
 
2824
1648
  temp_yaml = os.path.join("actions", f"{uuid.uuid4()}.yml")
2825
1649
  try:
2826
- with open(temp_yaml, "w",encoding="utf-8") as f:
1650
+ with open(temp_yaml, "w", encoding="utf-8") as f:
2827
1651
  f.write(convert_yaml_config_to_str(yaml_config=yaml_config))
2828
1652
  args = convert_yaml_to_config(temp_yaml)
2829
1653
  finally:
@@ -2831,18 +1655,31 @@ def get_final_config()->AutoCoderArgs:
2831
1655
  os.remove(temp_yaml)
2832
1656
  return args
2833
1657
 
1658
+
2834
1659
  def help(query: str):
2835
- from autocoder.common.auto_configure import ConfigAutoTuner,MemoryConfig,AutoConfigRequest
1660
+ from autocoder.common.auto_configure import (
1661
+ ConfigAutoTuner,
1662
+ MemoryConfig,
1663
+ AutoConfigRequest,
1664
+ )
1665
+
1666
+ memory_manager = get_memory_manager()
1667
+ memory = get_memory()
2836
1668
  args = get_final_config()
2837
- product_mode = memory.get("product_mode", "lite")
1669
+ product_mode = memory_manager.get_config("product_mode", "lite")
2838
1670
  llm = get_single_llm(args.chat_model or args.model, product_mode=product_mode)
2839
- auto_config_tuner = ConfigAutoTuner(args=args, llm=llm, memory_config=MemoryConfig(memory=memory, save_memory_func=save_memory))
2840
- auto_config_tuner.tune(AutoConfigRequest(query=query))
1671
+ auto_config_tuner = ConfigAutoTuner(
1672
+ args=args,
1673
+ llm=llm,
1674
+ memory_config=MemoryConfig(memory=memory, save_memory_func=save_memory),
1675
+ )
1676
+ auto_config_tuner.tune(AutoConfigRequest(query=query))
1677
+
2841
1678
 
2842
- @run_in_raw_thread()
2843
1679
  def index_export(path: str):
2844
1680
  from autocoder.common.index_import_export import export_index
2845
1681
  from autocoder.common.printer import Printer
1682
+
2846
1683
  printer = Printer()
2847
1684
  project_root = os.getcwd()
2848
1685
  if export_index(project_root, path):
@@ -2850,10 +1687,11 @@ def index_export(path: str):
2850
1687
  else:
2851
1688
  printer.print_in_terminal("index_export_fail", path=path)
2852
1689
 
2853
- @run_in_raw_thread()
1690
+
2854
1691
  def index_import(path: str):
2855
1692
  from autocoder.common.index_import_export import import_index
2856
1693
  from autocoder.common.printer import Printer
1694
+
2857
1695
  printer = Printer()
2858
1696
  project_root = os.getcwd()
2859
1697
  if import_index(project_root, path):
@@ -2861,7 +1699,7 @@ def index_import(path: str):
2861
1699
  else:
2862
1700
  printer.print_in_terminal("index_import_fail", path=path)
2863
1701
 
2864
- @run_in_raw_thread()
1702
+
2865
1703
  def index_query(query: str):
2866
1704
  from autocoder.index.entry import build_index_and_filter_files
2867
1705
  from autocoder.pyproject import PyProject
@@ -2871,7 +1709,9 @@ def index_query(query: str):
2871
1709
  config = get_final_config()
2872
1710
  config.query = query
2873
1711
  config.skip_filter_index = False
2874
- llm = get_single_llm(config.chat_model or config.model, product_mode=config.product_mode)
1712
+ llm = get_single_llm(
1713
+ config.chat_model or config.model, product_mode=config.product_mode
1714
+ )
2875
1715
 
2876
1716
  if config.project_type == "ts":
2877
1717
  pp = TSProject(args=config, llm=llm)
@@ -2880,264 +1720,79 @@ def index_query(query: str):
2880
1720
  else:
2881
1721
  pp = SuffixProject(args=config, llm=llm, file_filter=None)
2882
1722
  pp.run()
2883
- sources = pp.sources
2884
- source_code_list = build_index_and_filter_files(llm=llm, args=config, sources=sources)
2885
- return source_code_list
1723
+ sources = pp.sources
1724
+ source_code_list = build_index_and_filter_files(
1725
+ llm=llm, args=config, sources=sources
1726
+ )
1727
+ return source_code_list
1728
+
2886
1729
 
2887
1730
  def list_files():
2888
- console = Console()
2889
- project_root = os.getcwd()
2890
- current_files = memory["current_files"]["files"]
1731
+ """
1732
+ 处理文件列表命令,使用 ListFilesHandler 进行统一处理
1733
+ """
1734
+ from autocoder.common.file_handler import ListFilesHandler
2891
1735
 
2892
- if current_files:
2893
- table = Table(
2894
- title="Current Files", show_header=True, header_style="bold magenta"
2895
- )
2896
- table.add_column("File", style="green")
2897
- for file in current_files:
2898
- table.add_row(os.path.relpath(file, project_root))
2899
- console.print(Panel(table, border_style="blue"))
2900
- else:
2901
- console.print(
2902
- Panel(
2903
- "No files in the current session.",
2904
- title="Current Files",
2905
- border_style="yellow",
2906
- )
2907
- )
1736
+ handler = ListFilesHandler()
1737
+ handler.handle_list_files_command()
2908
1738
 
2909
1739
 
2910
1740
  def gen_and_exec_shell_command(query: str):
2911
- from rich.prompt import Confirm
2912
- from autocoder.common.printer import Printer
2913
- from rich.console import Console
2914
-
2915
1741
  printer = Printer()
2916
1742
  console = Console()
2917
1743
  # Generate the shell script
2918
- shell_script = generate_shell_command(query)
1744
+ shell_script = generate_shell_command(query)
2919
1745
 
2920
1746
  # Ask for confirmation using rich
2921
1747
  if Confirm.ask(
2922
- printer.get_message_from_key("confirm_execute_shell_script"),
2923
- default=False
1748
+ printer.get_message_from_key("confirm_execute_shell_script"), default=False
2924
1749
  ):
2925
1750
  execute_shell_command(shell_script)
2926
1751
  else:
2927
1752
  console.print(
2928
1753
  Panel(
2929
1754
  printer.get_message_from_key("shell_script_not_executed"),
2930
- border_style="yellow"
1755
+ border_style="yellow",
2931
1756
  )
2932
1757
  )
2933
1758
 
2934
1759
 
2935
1760
  def lib_command(args: List[str]):
2936
- console = Console()
2937
- lib_dir = os.path.join(".auto-coder", "libs")
2938
- llm_friendly_packages_dir = os.path.join(lib_dir, "llm_friendly_packages")
2939
-
2940
- if not os.path.exists(lib_dir):
2941
- os.makedirs(lib_dir)
2942
-
2943
- if "libs" not in memory:
2944
- memory["libs"] = {}
2945
-
2946
- if not args:
2947
- console.print(
2948
- "Please specify a subcommand: /add, /remove, /list, /list_all, /set-proxy, /refresh, or /get"
2949
- )
2950
- return
2951
-
2952
- subcommand = args[0]
2953
-
2954
- if subcommand == "/add":
2955
- if len(args) < 2:
2956
- console.print("Please specify a library name to add")
2957
- return
2958
- lib_name = args[1].strip()
2959
-
2960
- # Clone the repository if it doesn't exist
2961
- if not os.path.exists(llm_friendly_packages_dir):
2962
- console.print("Cloning llm_friendly_packages repository...")
2963
- try:
2964
- proxy_url = memory.get(
2965
- "lib-proxy", "https://github.com/allwefantasy/llm_friendly_packages"
2966
- )
2967
- git.Repo.clone_from(
2968
- proxy_url,
2969
- llm_friendly_packages_dir,
2970
- )
2971
- console.print(
2972
- "Successfully cloned llm_friendly_packages repository")
2973
- except git.exc.GitCommandError as e:
2974
- console.print(f"Error cloning repository: {e}")
2975
-
2976
- if lib_name in memory["libs"]:
2977
- console.print(f"Library {lib_name} is already added")
2978
- else:
2979
- memory["libs"][lib_name] = {}
2980
- console.print(f"Added library: {lib_name}")
2981
-
2982
- save_memory()
2983
-
2984
- elif subcommand == "/remove":
2985
- if len(args) < 2:
2986
- console.print("Please specify a library name to remove")
2987
- return
2988
- lib_name = args[1].strip()
2989
- if lib_name in memory["libs"]:
2990
- del memory["libs"][lib_name]
2991
- console.print(f"Removed library: {lib_name}")
2992
- save_memory()
2993
- else:
2994
- console.print(f"Library {lib_name} is not in the list")
2995
-
2996
- elif subcommand == "/list":
2997
- if memory["libs"]:
2998
- table = Table(title="Added Libraries")
2999
- table.add_column("Library Name", style="cyan")
3000
- for lib_name in memory["libs"]:
3001
- table.add_row(lib_name)
3002
- console.print(table)
3003
- else:
3004
- console.print("No libraries added yet")
3005
-
3006
- elif subcommand == "/list_all":
3007
- if not os.path.exists(llm_friendly_packages_dir):
3008
- console.print("llm_friendly_packages repository does not exist. Please run /lib /add <library_name> command first to clone it.")
3009
- return
3010
-
3011
- available_libs = []
3012
-
3013
- # 遍历所有domain目录
3014
- for domain in os.listdir(llm_friendly_packages_dir):
3015
- domain_path = os.path.join(llm_friendly_packages_dir, domain)
3016
- if os.path.isdir(domain_path):
3017
- # 遍历所有username目录
3018
- for username in os.listdir(domain_path):
3019
- username_path = os.path.join(domain_path, username)
3020
- if os.path.isdir(username_path):
3021
- # 遍历所有lib_name目录
3022
- for lib_name in os.listdir(username_path):
3023
- lib_path = os.path.join(username_path, lib_name)
3024
- if os.path.isdir(lib_path):
3025
- # 检查是否有Markdown文件
3026
- has_md_files = False
3027
- for root, _, files in os.walk(lib_path):
3028
- if any(file.endswith('.md') for file in files):
3029
- has_md_files = True
3030
- break
3031
-
3032
- if has_md_files:
3033
- available_libs.append({
3034
- "domain": domain,
3035
- "username": username,
3036
- "lib_name": lib_name,
3037
- "full_path": f"{username}/{lib_name}",
3038
- "is_added": lib_name in memory["libs"]
3039
- })
3040
-
3041
- if available_libs:
3042
- table = Table(title="Available Libraries")
3043
- table.add_column("Domain", style="blue")
3044
- table.add_column("Username", style="green")
3045
- table.add_column("Library Name", style="cyan")
3046
- table.add_column("Full Path", style="magenta")
3047
- table.add_column("Status", style="yellow")
3048
-
3049
- # 按domain和username分组排序
3050
- available_libs.sort(key=lambda x: (x["domain"], x["username"], x["lib_name"]))
3051
-
3052
- for lib in available_libs:
3053
- status = "[green]Added[/green]" if lib["is_added"] else "[white]Not Added[/white]"
3054
- table.add_row(
3055
- lib["domain"],
3056
- lib["username"],
3057
- lib["lib_name"],
3058
- lib["full_path"],
3059
- status
3060
- )
3061
-
3062
- console.print(table)
3063
- else:
3064
- console.print("No available libraries found in the repository.")
3065
-
3066
- elif subcommand == "/set-proxy":
3067
- if len(args) == 1:
3068
- current_proxy = memory.get("lib-proxy", "No proxy set")
3069
- console.print(f"Current proxy: {current_proxy}")
3070
- elif len(args) == 2:
3071
- proxy_url = args[1]
3072
- memory["lib-proxy"] = proxy_url
3073
- console.print(f"Set proxy to: {proxy_url}")
3074
- save_memory()
3075
- else:
3076
- console.print("Invalid number of arguments for /set-proxy")
3077
-
3078
- elif subcommand == "/refresh":
3079
- if os.path.exists(llm_friendly_packages_dir):
3080
- try:
3081
- repo = git.Repo(llm_friendly_packages_dir)
3082
- origin = repo.remotes.origin
3083
- proxy_url = memory.get("lib-proxy")
3084
-
3085
- current_url = origin.url
3086
-
3087
- if proxy_url and proxy_url != current_url:
3088
- new_url = proxy_url
3089
- origin.set_url(new_url)
3090
- console.print(f"Updated remote URL to: {new_url}")
3091
-
3092
- origin.pull()
3093
- console.print(
3094
- "Successfully updated llm_friendly_packages repository")
3095
-
3096
- except git.exc.GitCommandError as e:
3097
- console.print(f"Error updating repository: {e}")
3098
- else:
3099
- console.print(
3100
- "llm_friendly_packages repository does not exist. Please run /lib /add <library_name> command first to clone it."
3101
- )
1761
+ """
1762
+ 处理库管理命令,使用 LibHandler 进行统一处理
3102
1763
 
3103
- elif subcommand == "/get":
3104
- if len(args) < 2:
3105
- console.print("Please specify a package name to get")
3106
- return
3107
- package_name = args[1].strip()
3108
- docs = get_llm_friendly_package_docs(package_name, return_paths=True)
3109
- if docs:
3110
- table = Table(title=f"Markdown Files for {package_name}")
3111
- table.add_column("File Path", style="cyan")
3112
- for doc in docs:
3113
- table.add_row(doc)
3114
- console.print(table)
3115
- else:
3116
- console.print(
3117
- f"No markdown files found for package: {package_name}")
1764
+ Args:
1765
+ args: 命令参数列表
1766
+ """
1767
+ from autocoder.common.file_handler import LibHandler
3118
1768
 
3119
- else:
3120
- console.print(f"Unknown subcommand: {subcommand}")
1769
+ handler = LibHandler()
1770
+ handler.handle_lib_command(args)
3121
1771
 
3122
1772
 
3123
1773
  def execute_shell_command(command: str):
3124
1774
  from autocoder.common.shells import execute_shell_command as shell_exec
1775
+
3125
1776
  shell_exec(command)
3126
1777
 
3127
1778
 
3128
1779
  def conf_export(path: str):
3129
1780
  from autocoder.common.conf_import_export import export_conf
1781
+
3130
1782
  export_conf(os.getcwd(), path)
3131
1783
 
1784
+
3132
1785
  def conf_import(path: str):
3133
1786
  from autocoder.common.conf_import_export import import_conf
1787
+
3134
1788
  import_conf(os.getcwd(), path)
3135
1789
 
1790
+
3136
1791
  def generate_new_yaml(query: str):
3137
1792
  memory = get_memory()
3138
- conf = memory.get("conf",{})
3139
- current_files = memory.get("current_files",{}).get("files",[])
3140
- auto_coder_main(["next", "chat_action"])
1793
+ conf = memory.get("conf", {})
1794
+ current_files = memory.get("current_files", {}).get("files", [])
1795
+ auto_coder_main(["next", "chat_action"])
3141
1796
  latest_yaml_file = get_last_yaml_file("actions")
3142
1797
  if latest_yaml_file:
3143
1798
  yaml_config = {
@@ -3147,11 +1802,11 @@ def generate_new_yaml(query: str):
3147
1802
  "skip_build_index": conf.get("skip_build_index", "true") == "true",
3148
1803
  "skip_confirm": conf.get("skip_confirm", "true") == "true",
3149
1804
  "silence": conf.get("silence", "true") == "true",
3150
- "include_project_structure": conf.get("include_project_structure", "true")
1805
+ "include_project_structure": conf.get("include_project_structure", "false")
3151
1806
  == "true",
3152
1807
  "exclude_files": memory.get("exclude_files", []),
3153
1808
  }
3154
- yaml_config["context"] = ""
1809
+ yaml_config["context"] = ""
3155
1810
  for key, value in conf.items():
3156
1811
  converted_value = convert_config_value(key, value)
3157
1812
  if converted_value is not None:
@@ -3162,294 +1817,356 @@ def generate_new_yaml(query: str):
3162
1817
  )
3163
1818
  # handle image
3164
1819
  v = Image.convert_image_paths_from(query)
3165
- yaml_config["query"] = v
1820
+ yaml_config["query"] = v
3166
1821
 
3167
- yaml_content = convert_yaml_config_to_str(yaml_config=yaml_config)
1822
+ yaml_content = convert_yaml_config_to_str(yaml_config=yaml_config)
3168
1823
 
3169
1824
  execute_file = os.path.join("actions", latest_yaml_file)
3170
- with open(os.path.join(execute_file), "w",encoding="utf-8") as f:
1825
+ with open(os.path.join(execute_file), "w", encoding="utf-8") as f:
3171
1826
  f.write(yaml_content)
3172
- return execute_file,convert_yaml_to_config(execute_file)
3173
-
3174
- @run_in_raw_thread()
3175
- def auto_command(query: str,extra_args: Dict[str,Any]={}):
3176
- """处理/auto指令"""
3177
- args = get_final_config()
3178
- memory = get_memory()
3179
- if args.enable_agentic_edit:
3180
- from autocoder.run_context import get_run_context,RunMode
3181
- execute_file,args = generate_new_yaml(query)
3182
- args.file = execute_file
3183
- current_files = memory.get("current_files",{}).get("files",[])
3184
- sources = []
3185
- for file in current_files:
3186
- try:
3187
- with open(file,"r",encoding="utf-8") as f:
3188
- sources.append(SourceCode(module_name=file,source_code=f.read()))
3189
- except Exception as e:
3190
- global_logger.error(f"Failed to read file {file}: {e}")
3191
-
3192
- llm = get_single_llm(args.code_model or args.model,product_mode=args.product_mode)
3193
- conversation_history = extra_args.get("conversations",[])
1827
+ return execute_file, convert_yaml_to_config(execute_file)
3194
1828
 
3195
- command_infos = parse_query(query)
3196
1829
 
3197
- # terminal 的总是接着上次对话, 所以这里总是设置为 resume
3198
- conversation_config = AgenticEditConversationConfig(
3199
- action="resume"
3200
- )
3201
-
3202
- ## web 模式会自己管理对话,所以这里总是设置为新对话
3203
- if get_run_context().mode == RunMode.WEB:
3204
- command_infos = {
3205
- "new":{
3206
- "args":[query]
3207
- }
3208
- }
3209
-
3210
- task_query = query
3211
-
3212
- if "new" in command_infos:
3213
- conversation_config.action = "new"
3214
- task_query = " ".join(command_infos["new"]["args"])
3215
-
3216
- if "resume" in command_infos:
3217
- conversation_config.action = "resume"
3218
- conversation_config.conversation_id = command_infos["resume"]["args"][0]
3219
- task_query = " ".join(command_infos["resume"]["args"][1:])
1830
+ def handle_conversation_actions(conversation_config) -> bool:
1831
+ """
1832
+ 处理对话列表和创建新对话的操作
3220
1833
 
3221
- if "list" in command_infos:
3222
- conversation_config.action = "list"
1834
+ Args:
1835
+ conversation_config: 对话配置对象
3223
1836
 
3224
- if "command" in command_infos:
3225
- conversation_config.action = "command"
3226
- task_query = render_command_file_with_variables(command_infos)
3227
-
1837
+ Returns:
1838
+ bool: 如果处理了特殊操作(LIST或NEW without input)返回True,否则返回False
1839
+ """
1840
+ if not conversation_config:
1841
+ return False
3228
1842
 
3229
- conversation_config.query = task_query
1843
+ console = Console()
3230
1844
 
3231
- agent = AgenticEdit(llm=llm,args=args,files=SourceCodeList(sources=sources),
3232
- conversation_history=conversation_history,
3233
- memory_config=MemoryConfig(memory=memory,
3234
- save_memory_func=save_memory), command_config=CommandConfig,
3235
- conversation_name="current",
3236
- conversation_config=conversation_config
3237
- )
3238
- if get_run_context().mode == RunMode.WEB:
3239
- agent.run_with_events(AgenticEditRequest(user_input=task_query))
3240
-
3241
- if get_run_context().mode == RunMode.TERMINAL:
3242
- agent.run_in_terminal(AgenticEditRequest(user_input=task_query))
3243
-
3244
- completer.refresh_files()
3245
- return
3246
-
3247
- args = get_final_config()
3248
- # 准备请求参数
3249
- request = AutoCommandRequest(
3250
- user_input=query
3251
- )
1845
+ # 处理LIST操作
1846
+ if conversation_config.action == ConversationAction.LIST:
1847
+ conversation_manager = get_conversation_manager()
1848
+ conversations = conversation_manager.list_conversations()
1849
+ # 只保留 conversation_id 和 name 字段
1850
+ filtered_conversations = []
1851
+ for conv in conversations:
1852
+ filtered_conv = {
1853
+ "conversation_id": conv.get("conversation_id"),
1854
+ "name": conv.get("name"),
1855
+ }
1856
+ filtered_conversations.append(filtered_conv)
3252
1857
 
3253
- # 初始化调优器
3254
- llm = get_single_llm(args.chat_model or args.model,product_mode=args.product_mode)
3255
- tuner = CommandAutoTuner(llm,
3256
- args=args,
3257
- memory_config=MemoryConfig(memory=memory, save_memory_func=save_memory),
3258
- command_config=CommandConfig(
3259
- add_files=add_files,
3260
- remove_files=remove_files,
3261
- list_files=list_files,
3262
- conf=configure,
3263
- revert=revert,
3264
- commit=commit,
3265
- help=help,
3266
- exclude_dirs=exclude_dirs,
3267
- exclude_files=exclude_files,
3268
- ask=ask,
3269
- chat=chat,
3270
- coding=coding,
3271
- design=design,
3272
- summon=summon,
3273
- lib=lib_command,
3274
- mcp=mcp,
3275
- models=manage_models,
3276
- index_build=index_build,
3277
- index_query=index_query,
3278
- execute_shell_command=execute_shell_command,
3279
- generate_shell_command=generate_shell_command,
3280
- conf_export=conf_export,
3281
- conf_import=conf_import,
3282
- index_export=index_export,
3283
- index_import=index_import
3284
- ))
1858
+ # 格式化 JSON 输出,使用 JSON 格式渲染而不是 Markdown
1859
+ json_str = json.dumps(filtered_conversations, ensure_ascii=False, indent=4)
1860
+ console.print(
1861
+ Panel(
1862
+ json_str,
1863
+ title="🏁 Task Completion",
1864
+ border_style="green",
1865
+ title_align="left",
1866
+ )
1867
+ )
1868
+ return True
3285
1869
 
3286
- # 生成建议
3287
- response = tuner.analyze(request)
3288
- printer = Printer()
3289
- # 显示建议
3290
- console = Console()
3291
- console.print(Panel(
3292
- Markdown(response.reasoning or ""),
3293
- title=printer.get_message_from_key_with_format("auto_command_reasoning_title"),
3294
- border_style="blue",
3295
- padding=(1, 2)
3296
- ))
3297
- completer.refresh_files()
1870
+ # 处理NEW操作且没有用户输入
1871
+ if (
1872
+ conversation_config.action == ConversationAction.NEW
1873
+ and not conversation_config.query.strip()
1874
+ ):
1875
+ conversation_manager = get_conversation_manager()
1876
+ conversation_id = conversation_manager.create_conversation(
1877
+ name=conversation_config.query or "New Conversation",
1878
+ description=conversation_config.query or "New Conversation",
1879
+ )
1880
+ conversation_manager.set_current_conversation(conversation_id)
1881
+ conversation_message = f"New conversation created: {conversation_manager.get_current_conversation_id()}"
1882
+
1883
+ # 使用safe console print的简单版本
1884
+ try:
1885
+ console.print(
1886
+ Panel(
1887
+ Markdown(conversation_message),
1888
+ title="🏁 Task Completion",
1889
+ border_style="green",
1890
+ title_align="left",
1891
+ )
1892
+ )
1893
+ except Exception:
1894
+ # fallback to plain text
1895
+ safe_content = conversation_message.replace("[", "\\[").replace("]", "\\]")
1896
+ console.print(
1897
+ Panel(
1898
+ safe_content,
1899
+ title="🏁 Task Completion",
1900
+ border_style="green",
1901
+ title_align="left",
1902
+ )
1903
+ )
1904
+ return True
1905
+
1906
+ return False
1907
+
1908
+
1909
+ # used in /auto command in terminal
1910
+ def run_agentic(
1911
+ query: str,
1912
+ cancel_token: Optional[str] = None,
1913
+ conversation_history: Optional[List[Dict[str, Any]]] = None,
1914
+ ):
1915
+ """处理/auto指令"""
1916
+ agentic = RunAgentic()
1917
+ return agentic.run(query, cancel_token, conversation_history)
1918
+
1919
+
1920
+ def run_agentic_filter(query: str, cancel_token: Optional[str] = None):
1921
+ """处理/auto指令"""
1922
+ agentic = RunAgentic()
1923
+ return agentic.filter(query, cancel_token)
1924
+
1925
+
1926
+ # used in autocoder/sdk/core/bridge.py
1927
+ def run_auto_command(
1928
+ query: str,
1929
+ pre_commit: bool = False,
1930
+ post_commit: bool = False,
1931
+ pr: bool = False,
1932
+ extra_args: Dict[str, Any] = {},
1933
+ cancel_token: Optional[str] = None,
1934
+ conversation_history: Optional[List[Dict[str, Any]]] = None,
1935
+ system_prompt: Optional[str] = None,
1936
+ conversation_action: ConversationAction = ConversationAction.NEW,
1937
+ conversation_id: Optional[str] = None,
1938
+ is_sub_agent: bool = False,
1939
+ ):
1940
+ """处理/auto指令"""
1941
+ agentic = RunAgentic()
1942
+ for event in agentic.run_with_events(
1943
+ query,
1944
+ pre_commit=pre_commit,
1945
+ post_commit=post_commit,
1946
+ pr=pr,
1947
+ extra_args=extra_args,
1948
+ cancel_token=cancel_token,
1949
+ conversation_history=conversation_history,
1950
+ system_prompt=system_prompt,
1951
+ conversation_action=conversation_action,
1952
+ conversation_id=conversation_id,
1953
+ is_sub_agent=is_sub_agent,
1954
+ ):
1955
+ yield event
3298
1956
 
3299
1957
 
1958
+ # used in auto-coder.web
1959
+ def auto_command(query: str, extra_args: Dict[str, Any] = {}):
1960
+ """处理/auto指令"""
1961
+ args = get_final_config()
1962
+ memory = get_memory()
1963
+ if args.enable_agentic_edit:
1964
+ from autocoder.run_context import get_run_context, RunMode
3300
1965
 
3301
- def run_auto_command(query: str,
3302
- pre_commit:bool=False,
3303
- post_commit:bool=False,
3304
- pr:bool=False,
3305
- extra_args: Dict[str,Any]={}
3306
- ):
3307
- """处理/auto指令"""
3308
- args = get_final_config()
3309
- memory = get_memory()
3310
- if args.enable_agentic_edit:
3311
- from autocoder.run_context import get_run_context,RunMode
3312
- execute_file,args = generate_new_yaml(query)
3313
- args.file = execute_file
3314
- current_files = memory.get("current_files",{}).get("files",[])
1966
+ execute_file, _ = generate_new_yaml(query)
1967
+ args.file = execute_file
1968
+ current_files = memory.get("current_files", {}).get("files", [])
3315
1969
  sources = []
3316
1970
  for file in current_files:
3317
1971
  try:
3318
- with open(file,"r",encoding="utf-8") as f:
3319
- sources.append(SourceCode(module_name=file,source_code=f.read()))
1972
+ with open(file, "r", encoding="utf-8") as f:
1973
+ sources.append(SourceCode(module_name=file, source_code=f.read()))
3320
1974
  except Exception as e:
3321
1975
  global_logger.error(f"Failed to read file {file}: {e}")
3322
-
3323
- llm = get_single_llm(args.code_model or args.model,product_mode=args.product_mode)
3324
- conversation_history = extra_args.get("conversations",[])
3325
1976
 
3326
- command_infos = parse_query(query)
1977
+ try:
1978
+ llm = get_single_llm(
1979
+ args.code_model or args.model, product_mode=args.product_mode
1980
+ )
1981
+ except ValueError as e:
1982
+ console = Console()
1983
+ console.print(
1984
+ Panel(
1985
+ f"[red]LLM Configuration Error:[/red]\n\n{str(e)}",
1986
+ title="[red]Error[/red]",
1987
+ border_style="red",
1988
+ padding=(1, 2),
1989
+ )
1990
+ )
1991
+ return
1992
+ conversation_history = extra_args.get("conversations", [])
1993
+
1994
+ command_infos = parse_query(query)
3327
1995
 
3328
1996
  # terminal 的总是接着上次对话, 所以这里总是设置为 resume
3329
1997
  conversation_config = AgenticEditConversationConfig(
3330
- action="resume"
1998
+ action=ConversationAction.RESUME
3331
1999
  )
3332
-
3333
- ## web 模式会自己管理对话,所以这里总是设置为新对话
3334
- if get_run_context().mode == RunMode.WEB:
3335
- command_infos = {
3336
- "new":{
3337
- "args":[query]
3338
- }
3339
- }
3340
-
3341
- task_query = query
3342
-
3343
- if "new" in command_infos:
3344
- conversation_config.action = "new"
2000
+
2001
+ task_query = query
2002
+
2003
+ if "new" in command_infos:
2004
+ conversation_config.action = ConversationAction.NEW
3345
2005
  task_query = " ".join(command_infos["new"]["args"])
3346
-
2006
+
3347
2007
  if "resume" in command_infos:
3348
- conversation_config.action = "resume"
3349
- conversation_config.conversation_id = command_infos["resume"]["args"][0]
3350
- task_query = " ".join(command_infos["resume"]["args"][1:])
2008
+ conversation_config.action = ConversationAction.RESUME
2009
+ conversation_config.conversation_id = command_infos["resume"]["args"][0]
2010
+ task_query = " ".join(command_infos["resume"]["args"][1:])
3351
2011
 
3352
2012
  if "list" in command_infos:
3353
- conversation_config.action = "list"
2013
+ conversation_config.action = ConversationAction.LIST
3354
2014
 
3355
-
3356
2015
  if "command" in command_infos:
3357
- conversation_config.action = "command"
2016
+ conversation_config.action = ConversationAction.COMMAND
3358
2017
  task_query = render_command_file_with_variables(command_infos)
3359
-
3360
- conversation_config.query = task_query
3361
- conversation_config.pull_request = pr
3362
-
3363
- agent = AgenticEdit(llm=llm,args=args,files=SourceCodeList(sources=sources),
3364
- conversation_history=conversation_history,
3365
- memory_config=MemoryConfig(memory=memory,
3366
- save_memory_func=save_memory), command_config=CommandConfig,
3367
- conversation_name="current",
3368
- conversation_config=conversation_config
3369
- )
3370
- if pre_commit:
3371
- agent.apply_pre_changes()
3372
-
3373
- events = agent.run(AgenticEditRequest(user_input=task_query))
3374
-
3375
- for event in events:
3376
- yield event
3377
-
3378
- # 处理 post_commit 和 PR 功能
3379
- if post_commit or pr:
3380
- _handle_post_commit_and_pr(post_commit, pr, query, args, llm)
3381
-
2018
+
2019
+ conversation_config.query = task_query
2020
+
2021
+ # 处理特殊的conversation操作(LIST和NEW without input)
2022
+ if handle_conversation_actions(conversation_config):
2023
+ return conversation_config.conversation_id
2024
+
2025
+ conversation_manager = get_conversation_manager()
2026
+ if conversation_config.action == ConversationAction.NEW:
2027
+ conversation_id = conversation_manager.create_conversation(
2028
+ name=conversation_config.query or "New Conversation",
2029
+ description=conversation_config.query or "New Conversation",
2030
+ )
2031
+ conversation_manager.set_current_conversation(conversation_id)
2032
+ conversation_config.conversation_id = conversation_id
2033
+
2034
+ if (
2035
+ conversation_config.action == ConversationAction.RESUME
2036
+ and conversation_config.conversation_id
2037
+ ):
2038
+ conversation_manager.set_current_conversation(
2039
+ conversation_config.conversation_id
2040
+ )
2041
+
2042
+ if (
2043
+ conversation_config.action == ConversationAction.RESUME
2044
+ and not conversation_config.conversation_id
2045
+ and conversation_manager.get_current_conversation_id()
2046
+ ):
2047
+ conversation_config.conversation_id = (
2048
+ conversation_manager.get_current_conversation_id()
2049
+ )
2050
+ conversation_manager.set_current_conversation(
2051
+ conversation_config.conversation_id
2052
+ )
2053
+
2054
+ if not conversation_config.conversation_id:
2055
+ conversation_id = conversation_manager.create_conversation(
2056
+ name=conversation_config.query or "New Conversation",
2057
+ description=conversation_config.query or "New Conversation",
2058
+ )
2059
+ conversation_manager.set_current_conversation(conversation_id)
2060
+ conversation_config.conversation_id = conversation_id
2061
+
2062
+ cancel_token = extra_args.get("event_file_id", None)
2063
+ global_logger.info(f"cancel_token: {cancel_token}")
2064
+ if cancel_token:
2065
+ global_cancel.register_token(cancel_token)
2066
+
2067
+ if get_run_context().mode == RunMode.WEB:
2068
+ runner = FileBasedEventRunner(
2069
+ llm=llm,
2070
+ args=args,
2071
+ conversation_config=conversation_config,
2072
+ cancel_token=cancel_token,
2073
+ )
2074
+ runner.run(AgenticEditRequest(user_input=task_query))
2075
+
2076
+ if get_run_context().mode == RunMode.TERMINAL:
2077
+ runner = TerminalRunner(
2078
+ llm=llm,
2079
+ args=args,
2080
+ conversation_config=conversation_config,
2081
+ cancel_token=cancel_token,
2082
+ )
2083
+ runner.run(AgenticEditRequest(user_input=task_query))
2084
+
3382
2085
  completer.refresh_files()
3383
- return
3384
-
3385
- args = get_final_config()
2086
+ return conversation_config.conversation_id
2087
+
2088
+ args = get_final_config()
3386
2089
  # 准备请求参数
3387
- request = AutoCommandRequest(
3388
- user_input=query
3389
- )
2090
+ request = AutoCommandRequest(user_input=query)
3390
2091
 
3391
2092
  # 初始化调优器
3392
- llm = get_single_llm(args.chat_model or args.model,product_mode=args.product_mode)
3393
- tuner = CommandAutoTuner(llm,
3394
- args=args,
3395
- memory_config=MemoryConfig(memory=memory, save_memory_func=save_memory),
3396
- command_config=CommandConfig(
3397
- add_files=add_files,
3398
- remove_files=remove_files,
3399
- list_files=list_files,
3400
- conf=configure,
3401
- revert=revert,
3402
- commit=commit,
3403
- help=help,
3404
- exclude_dirs=exclude_dirs,
3405
- exclude_files=exclude_files,
3406
- ask=ask,
3407
- chat=chat,
3408
- coding=coding,
3409
- design=design,
3410
- summon=summon,
3411
- lib=lib_command,
3412
- mcp=mcp,
3413
- models=manage_models,
3414
- index_build=index_build,
3415
- index_query=index_query,
3416
- execute_shell_command=execute_shell_command,
3417
- generate_shell_command=generate_shell_command,
3418
- conf_export=conf_export,
3419
- conf_import=conf_import,
3420
- index_export=index_export,
3421
- index_import=index_import
3422
- ))
2093
+ try:
2094
+ llm = get_single_llm(
2095
+ args.chat_model or args.model, product_mode=args.product_mode
2096
+ )
2097
+ except ValueError as e:
2098
+ console = Console()
2099
+ console.print(
2100
+ Panel(
2101
+ f"[red]LLM Configuration Error:[/red]\n\n{str(e)}",
2102
+ title="[red]Error[/red]",
2103
+ border_style="red",
2104
+ padding=(1, 2),
2105
+ )
2106
+ )
2107
+ return
2108
+ tuner = CommandAutoTuner(
2109
+ llm,
2110
+ args=args,
2111
+ memory_config=MemoryConfig(memory=memory, save_memory_func=save_memory),
2112
+ command_config=CommandConfig(
2113
+ add_files=add_files,
2114
+ remove_files=remove_files,
2115
+ list_files=list_files,
2116
+ conf=configure,
2117
+ revert=revert,
2118
+ commit=commit,
2119
+ help=help,
2120
+ exclude_dirs=exclude_dirs,
2121
+ exclude_files=exclude_files,
2122
+ ask=ask,
2123
+ chat=chat,
2124
+ coding=coding,
2125
+ design=design,
2126
+ summon=summon,
2127
+ lib=lib_command,
2128
+ mcp=mcp,
2129
+ models=manage_models,
2130
+ index_build=index_build,
2131
+ index_query=index_query,
2132
+ execute_shell_command=execute_shell_command,
2133
+ generate_shell_command=generate_shell_command,
2134
+ conf_export=conf_export,
2135
+ conf_import=conf_import,
2136
+ index_export=index_export,
2137
+ index_import=index_import,
2138
+ ),
2139
+ )
3423
2140
 
3424
2141
  # 生成建议
3425
2142
  response = tuner.analyze(request)
3426
2143
  printer = Printer()
3427
2144
  # 显示建议
3428
- console = Console()
3429
- console.print(Panel(
3430
- Markdown(response.reasoning or ""),
3431
- title=printer.get_message_from_key_with_format("auto_command_reasoning_title"),
3432
- border_style="blue",
3433
- padding=(1, 2)
3434
- ))
3435
-
3436
- # 处理 post_commit 和 PR 功能
3437
- if post_commit or pr:
3438
- _handle_post_commit_and_pr(post_commit, pr, query, args, llm)
3439
-
2145
+ console = Console()
2146
+ console.print(
2147
+ Panel(
2148
+ Markdown(response.reasoning or ""),
2149
+ title=printer.get_message_from_key_with_format(
2150
+ "auto_command_reasoning_title"
2151
+ ),
2152
+ border_style="blue",
2153
+ padding=(1, 2),
2154
+ )
2155
+ )
3440
2156
  completer.refresh_files()
2157
+ return None
3441
2158
 
3442
2159
 
3443
2160
  def render_command_file_with_variables(command_infos: Dict[str, Any]) -> str:
3444
2161
  """
3445
2162
  使用 CommandManager 加载并渲染命令文件
3446
-
2163
+
3447
2164
  Args:
3448
2165
  command_infos: parse_query(query) 的返回结果,包含命令和参数信息
3449
-
2166
+
3450
2167
  Returns:
3451
2168
  str: 渲染后的文件内容
3452
-
2169
+
3453
2170
  Raises:
3454
2171
  ValueError: 当参数不足或文件不存在时
3455
2172
  Exception: 当渲染过程出现错误时
@@ -3458,31 +2175,33 @@ def render_command_file_with_variables(command_infos: Dict[str, Any]) -> str:
3458
2175
  # 获取第一个命令的信息
3459
2176
  if not command_infos:
3460
2177
  raise ValueError("command_infos 为空,无法获取命令信息")
3461
-
2178
+
3462
2179
  # command 的位置参数作为路径
3463
2180
  first_command = command_infos["command"]
3464
-
2181
+
3465
2182
  # 获取位置参数(文件路径)
3466
2183
  args = first_command.get("args", [])
3467
2184
  if not args:
3468
2185
  raise ValueError("未提供文件路径参数")
3469
-
2186
+
3470
2187
  file_path = args[0] # 第一个位置参数作为文件路径
3471
-
2188
+
3472
2189
  # 获取关键字参数作为渲染参数
3473
2190
  kwargs = first_command.get("kwargs", {})
3474
-
2191
+
3475
2192
  # 初始化 CommandManager
3476
2193
  command_manager = CommandManager()
3477
-
2194
+
3478
2195
  # 使用 read_command_file_with_render 直接读取并渲染命令文件
3479
- rendered_content = command_manager.read_command_file_with_render(file_path, kwargs)
2196
+ rendered_content = command_manager.read_command_file_with_render(
2197
+ file_path, kwargs
2198
+ )
3480
2199
  if rendered_content is None:
3481
2200
  raise ValueError(f"无法读取或渲染命令文件: {file_path}")
3482
-
2201
+
3483
2202
  global_logger.info(f"成功渲染命令文件: {file_path}, 使用参数: {kwargs}")
3484
2203
  return rendered_content
3485
-
2204
+
3486
2205
  except Exception as e:
3487
2206
  global_logger.error(f"render_command_file_with_variables 执行失败: {str(e)}")
3488
- raise
2207
+ raise