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

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

Potentially problematic release.


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

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