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

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

Potentially problematic release.


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

Files changed (574) hide show
  1. auto_coder-2.0.0.dist-info/LICENSE +158 -0
  2. auto_coder-2.0.0.dist-info/METADATA +558 -0
  3. auto_coder-2.0.0.dist-info/RECORD +795 -0
  4. {auto_coder-1.0.0.dist-info → auto_coder-2.0.0.dist-info}/WHEEL +1 -1
  5. {auto_coder-1.0.0.dist-info → auto_coder-2.0.0.dist-info}/entry_points.txt +3 -3
  6. autocoder/__init__.py +31 -0
  7. autocoder/agent/auto_filegroup.py +32 -13
  8. autocoder/agent/auto_learn_from_commit.py +9 -1
  9. autocoder/agent/base_agentic/__init__.py +3 -0
  10. autocoder/agent/base_agentic/agent_hub.py +1 -1
  11. autocoder/agent/base_agentic/base_agent.py +235 -136
  12. autocoder/agent/base_agentic/default_tools.py +119 -118
  13. autocoder/agent/base_agentic/test_base_agent.py +1 -1
  14. autocoder/agent/base_agentic/tool_registry.py +32 -20
  15. autocoder/agent/base_agentic/tools/read_file_tool_resolver.py +24 -3
  16. autocoder/agent/base_agentic/tools/write_to_file_tool_resolver.py +24 -11
  17. autocoder/agent/base_agentic/types.py +42 -0
  18. autocoder/agent/entry_command_agent/chat.py +73 -59
  19. autocoder/auto_coder.py +31 -40
  20. autocoder/auto_coder_rag.py +11 -1084
  21. autocoder/auto_coder_runner.py +970 -2345
  22. autocoder/auto_coder_terminal.py +26 -0
  23. autocoder/auto_coder_terminal_v3.py +190 -0
  24. autocoder/chat/conf_command.py +224 -124
  25. autocoder/chat/models_command.py +361 -299
  26. autocoder/chat/rules_command.py +79 -31
  27. autocoder/chat_auto_coder.py +988 -398
  28. autocoder/chat_auto_coder_lang.py +23 -732
  29. autocoder/commands/auto_command.py +25 -8
  30. autocoder/commands/auto_web.py +1 -1
  31. autocoder/commands/tools.py +44 -44
  32. autocoder/common/__init__.py +150 -128
  33. autocoder/common/ac_style_command_parser/__init__.py +39 -2
  34. autocoder/common/ac_style_command_parser/config.py +422 -0
  35. autocoder/common/ac_style_command_parser/parser.py +292 -78
  36. autocoder/common/ac_style_command_parser/test_parser.py +241 -16
  37. autocoder/common/ac_style_command_parser/test_typed_parser.py +342 -0
  38. autocoder/common/ac_style_command_parser/typed_parser.py +653 -0
  39. autocoder/common/action_yml_file_manager.py +25 -13
  40. autocoder/common/agent_events/__init__.py +52 -0
  41. autocoder/common/agent_events/agent_event_emitter.py +193 -0
  42. autocoder/common/agent_events/event_factory.py +177 -0
  43. autocoder/common/agent_events/examples.py +307 -0
  44. autocoder/common/agent_events/types.py +113 -0
  45. autocoder/common/agent_events/utils.py +68 -0
  46. autocoder/common/agent_hooks/__init__.py +44 -0
  47. autocoder/common/agent_hooks/examples.py +582 -0
  48. autocoder/common/agent_hooks/hook_executor.py +217 -0
  49. autocoder/common/agent_hooks/hook_manager.py +288 -0
  50. autocoder/common/agent_hooks/types.py +133 -0
  51. autocoder/common/agent_hooks/utils.py +99 -0
  52. autocoder/common/agent_query_queue/queue_executor.py +324 -0
  53. autocoder/common/agent_query_queue/queue_manager.py +325 -0
  54. autocoder/common/agents/__init__.py +11 -0
  55. autocoder/common/agents/agent_manager.py +323 -0
  56. autocoder/common/agents/agent_parser.py +189 -0
  57. autocoder/common/agents/example_usage.py +344 -0
  58. autocoder/common/agents/integration_example.py +330 -0
  59. autocoder/common/agents/test_agent_parser.py +545 -0
  60. autocoder/common/async_utils.py +101 -0
  61. autocoder/common/auto_coder_lang.py +23 -972
  62. autocoder/common/autocoderargs_parser/__init__.py +14 -0
  63. autocoder/common/autocoderargs_parser/parser.py +184 -0
  64. autocoder/common/autocoderargs_parser/tests/__init__.py +1 -0
  65. autocoder/common/autocoderargs_parser/tests/test_args_parser.py +235 -0
  66. autocoder/common/autocoderargs_parser/tests/test_token_parser.py +195 -0
  67. autocoder/common/autocoderargs_parser/token_parser.py +290 -0
  68. autocoder/common/buildin_tokenizer.py +2 -4
  69. autocoder/common/code_auto_generate.py +149 -74
  70. autocoder/common/code_auto_generate_diff.py +163 -70
  71. autocoder/common/code_auto_generate_editblock.py +179 -89
  72. autocoder/common/code_auto_generate_strict_diff.py +167 -72
  73. autocoder/common/code_auto_merge_editblock.py +13 -6
  74. autocoder/common/code_modification_ranker.py +1 -1
  75. autocoder/common/command_completer.py +3 -3
  76. autocoder/common/command_file_manager/manager.py +183 -47
  77. autocoder/common/command_file_manager/test_command_file_manager.py +507 -0
  78. autocoder/common/command_templates.py +1 -1
  79. autocoder/common/conf_utils.py +2 -4
  80. autocoder/common/conversations/config.py +11 -3
  81. autocoder/common/conversations/get_conversation_manager.py +100 -2
  82. autocoder/common/conversations/llm_stats_models.py +264 -0
  83. autocoder/common/conversations/manager.py +112 -28
  84. autocoder/common/conversations/models.py +16 -2
  85. autocoder/common/conversations/storage/index_manager.py +134 -10
  86. autocoder/common/core_config/__init__.py +63 -0
  87. autocoder/common/core_config/agentic_mode_manager.py +109 -0
  88. autocoder/common/core_config/base_manager.py +123 -0
  89. autocoder/common/core_config/compatibility.py +151 -0
  90. autocoder/common/core_config/config_manager.py +156 -0
  91. autocoder/common/core_config/conversation_manager.py +31 -0
  92. autocoder/common/core_config/exclude_manager.py +72 -0
  93. autocoder/common/core_config/file_manager.py +177 -0
  94. autocoder/common/core_config/human_as_model_manager.py +129 -0
  95. autocoder/common/core_config/lib_manager.py +54 -0
  96. autocoder/common/core_config/main_manager.py +81 -0
  97. autocoder/common/core_config/mode_manager.py +126 -0
  98. autocoder/common/core_config/models.py +70 -0
  99. autocoder/common/core_config/test_memory_manager.py +1056 -0
  100. autocoder/common/env_manager.py +282 -0
  101. autocoder/common/env_manager_usage_example.py +211 -0
  102. autocoder/common/file_checkpoint/conversation_checkpoint.py +19 -19
  103. autocoder/common/file_checkpoint/manager.py +264 -48
  104. autocoder/common/file_checkpoint/test_backup.py +1 -18
  105. autocoder/common/file_checkpoint/test_manager.py +270 -1
  106. autocoder/common/file_checkpoint/test_store.py +1 -17
  107. autocoder/common/file_handler/__init__.py +23 -0
  108. autocoder/common/file_handler/active_context_handler.py +159 -0
  109. autocoder/common/file_handler/add_files_handler.py +409 -0
  110. autocoder/common/file_handler/chat_handler.py +180 -0
  111. autocoder/common/file_handler/coding_handler.py +401 -0
  112. autocoder/common/file_handler/commit_handler.py +200 -0
  113. autocoder/common/file_handler/lib_handler.py +156 -0
  114. autocoder/common/file_handler/list_files_handler.py +111 -0
  115. autocoder/common/file_handler/mcp_handler.py +268 -0
  116. autocoder/common/file_handler/models_handler.py +493 -0
  117. autocoder/common/file_handler/remove_files_handler.py +172 -0
  118. autocoder/common/git_utils.py +44 -8
  119. autocoder/common/global_cancel.py +15 -6
  120. autocoder/common/ignorefiles/test_ignore_file_utils.py +1 -1
  121. autocoder/common/international/__init__.py +31 -0
  122. autocoder/common/international/demo_international.py +92 -0
  123. autocoder/common/international/message_manager.py +157 -0
  124. autocoder/common/international/messages/__init__.py +56 -0
  125. autocoder/common/international/messages/async_command_messages.py +507 -0
  126. autocoder/common/international/messages/auto_coder_messages.py +2208 -0
  127. autocoder/common/international/messages/chat_auto_coder_messages.py +1547 -0
  128. autocoder/common/international/messages/command_help_messages.py +986 -0
  129. autocoder/common/international/messages/conversation_command_messages.py +191 -0
  130. autocoder/common/international/messages/git_helper_plugin_messages.py +159 -0
  131. autocoder/common/international/messages/queue_command_messages.py +751 -0
  132. autocoder/common/international/messages/rules_command_messages.py +77 -0
  133. autocoder/common/international/messages/sdk_messages.py +1707 -0
  134. autocoder/common/international/messages/token_helper_plugin_messages.py +361 -0
  135. autocoder/common/international/messages/tool_display_messages.py +1212 -0
  136. autocoder/common/international/messages/workflow_exception_messages.py +473 -0
  137. autocoder/common/international/test_international.py +612 -0
  138. autocoder/common/linter_core/__init__.py +28 -0
  139. autocoder/common/linter_core/base_linter.py +61 -0
  140. autocoder/common/linter_core/config_loader.py +271 -0
  141. autocoder/common/linter_core/formatters/__init__.py +0 -0
  142. autocoder/common/linter_core/formatters/base_formatter.py +38 -0
  143. autocoder/common/linter_core/formatters/raw_formatter.py +17 -0
  144. autocoder/common/linter_core/linter.py +166 -0
  145. autocoder/common/linter_core/linter_factory.py +216 -0
  146. autocoder/common/linter_core/linter_manager.py +333 -0
  147. autocoder/common/linter_core/linters/__init__.py +9 -0
  148. autocoder/common/linter_core/linters/java_linter.py +342 -0
  149. autocoder/common/linter_core/linters/python_linter.py +115 -0
  150. autocoder/common/linter_core/linters/typescript_linter.py +119 -0
  151. autocoder/common/linter_core/models/__init__.py +7 -0
  152. autocoder/common/linter_core/models/lint_result.py +91 -0
  153. autocoder/common/linter_core/models.py +33 -0
  154. autocoder/common/linter_core/tests/__init__.py +3 -0
  155. autocoder/common/linter_core/tests/test_config_loader.py +323 -0
  156. autocoder/common/linter_core/tests/test_config_loading.py +308 -0
  157. autocoder/common/linter_core/tests/test_factory_manager.py +234 -0
  158. autocoder/common/linter_core/tests/test_formatters.py +147 -0
  159. autocoder/common/linter_core/tests/test_integration.py +317 -0
  160. autocoder/common/linter_core/tests/test_java_linter.py +496 -0
  161. autocoder/common/linter_core/tests/test_linters.py +265 -0
  162. autocoder/common/linter_core/tests/test_models.py +81 -0
  163. autocoder/common/linter_core/tests/verify_config_loading.py +296 -0
  164. autocoder/common/linter_core/tests/verify_fixes.py +183 -0
  165. autocoder/common/llm_friendly_package/__init__.py +31 -0
  166. autocoder/common/llm_friendly_package/base_manager.py +102 -0
  167. autocoder/common/llm_friendly_package/docs_manager.py +121 -0
  168. autocoder/common/llm_friendly_package/library_manager.py +171 -0
  169. autocoder/common/{llm_friendly_package.py → llm_friendly_package/main_manager.py} +204 -231
  170. autocoder/common/llm_friendly_package/models.py +40 -0
  171. autocoder/common/llm_friendly_package/test_llm_friendly_package.py +536 -0
  172. autocoder/common/llms/__init__.py +15 -0
  173. autocoder/common/llms/demo_error_handling.py +85 -0
  174. autocoder/common/llms/factory.py +142 -0
  175. autocoder/common/llms/manager.py +264 -0
  176. autocoder/common/llms/pricing.py +121 -0
  177. autocoder/common/llms/registry.py +288 -0
  178. autocoder/common/llms/schema.py +77 -0
  179. autocoder/common/llms/simple_demo.py +45 -0
  180. autocoder/common/llms/test_quick_model.py +116 -0
  181. autocoder/common/llms/test_remove_functionality.py +182 -0
  182. autocoder/common/llms/tests/__init__.py +1 -0
  183. autocoder/common/llms/tests/test_manager.py +330 -0
  184. autocoder/common/llms/tests/test_registry.py +364 -0
  185. autocoder/common/mcp_tools/__init__.py +62 -0
  186. autocoder/common/{mcp_tools.py → mcp_tools/executor.py} +49 -40
  187. autocoder/common/{mcp_hub.py → mcp_tools/hub.py} +42 -68
  188. autocoder/common/{mcp_server_install.py → mcp_tools/installer.py} +16 -28
  189. autocoder/common/{mcp_server.py → mcp_tools/server.py} +176 -48
  190. autocoder/common/mcp_tools/test_keyboard_interrupt.py +93 -0
  191. autocoder/common/mcp_tools/test_mcp_tools.py +391 -0
  192. autocoder/common/{mcp_server_types.py → mcp_tools/types.py} +121 -48
  193. autocoder/common/mcp_tools/verify_functionality.py +202 -0
  194. autocoder/common/model_speed_tester.py +32 -26
  195. autocoder/common/priority_directory_finder/__init__.py +142 -0
  196. autocoder/common/priority_directory_finder/examples.py +230 -0
  197. autocoder/common/priority_directory_finder/finder.py +283 -0
  198. autocoder/common/priority_directory_finder/models.py +236 -0
  199. autocoder/common/priority_directory_finder/test_priority_directory_finder.py +431 -0
  200. autocoder/common/project_scanner/__init__.py +18 -0
  201. autocoder/common/project_scanner/compat.py +77 -0
  202. autocoder/common/project_scanner/scanner.py +436 -0
  203. autocoder/common/project_tracker/__init__.py +27 -0
  204. autocoder/common/project_tracker/api.py +228 -0
  205. autocoder/common/project_tracker/demo.py +272 -0
  206. autocoder/common/project_tracker/tracker.py +487 -0
  207. autocoder/common/project_tracker/types.py +53 -0
  208. autocoder/common/pruner/__init__.py +67 -0
  209. autocoder/common/pruner/agentic_conversation_pruner.py +651 -102
  210. autocoder/common/pruner/conversation_message_ids_api.py +386 -0
  211. autocoder/common/pruner/conversation_message_ids_manager.py +347 -0
  212. autocoder/common/pruner/conversation_message_ids_pruner.py +473 -0
  213. autocoder/common/pruner/conversation_normalizer.py +347 -0
  214. autocoder/common/pruner/conversation_pruner.py +26 -6
  215. autocoder/common/pruner/test_agentic_conversation_pruner.py +554 -112
  216. autocoder/common/pruner/test_conversation_normalizer.py +502 -0
  217. autocoder/common/pruner/test_tool_content_detector.py +324 -0
  218. autocoder/common/pruner/tool_content_detector.py +227 -0
  219. autocoder/common/pruner/tools/__init__.py +18 -0
  220. autocoder/common/pruner/tools/query_message_ids.py +264 -0
  221. autocoder/common/pruner/tools/test_agentic_pruning_logic.py +432 -0
  222. autocoder/common/pruner/tools/test_message_ids_pruning_only.py +192 -0
  223. autocoder/common/pull_requests/__init__.py +9 -1
  224. autocoder/common/pull_requests/utils.py +122 -1
  225. autocoder/common/rag_manager/rag_manager.py +36 -40
  226. autocoder/common/rulefiles/__init__.py +53 -1
  227. autocoder/common/rulefiles/api.py +250 -0
  228. autocoder/common/rulefiles/core/__init__.py +14 -0
  229. autocoder/common/rulefiles/core/manager.py +241 -0
  230. autocoder/common/rulefiles/core/selector.py +805 -0
  231. autocoder/common/rulefiles/models/__init__.py +20 -0
  232. autocoder/common/rulefiles/models/index.py +16 -0
  233. autocoder/common/rulefiles/models/init_rule.py +18 -0
  234. autocoder/common/rulefiles/models/rule_file.py +18 -0
  235. autocoder/common/rulefiles/models/rule_relevance.py +14 -0
  236. autocoder/common/rulefiles/models/summary.py +16 -0
  237. autocoder/common/rulefiles/test_rulefiles.py +776 -0
  238. autocoder/common/rulefiles/utils/__init__.py +34 -0
  239. autocoder/common/rulefiles/utils/monitor.py +86 -0
  240. autocoder/common/rulefiles/utils/parser.py +230 -0
  241. autocoder/common/save_formatted_log.py +67 -10
  242. autocoder/common/search_replace.py +8 -1
  243. autocoder/common/search_replace_patch/__init__.py +24 -0
  244. autocoder/common/search_replace_patch/base.py +115 -0
  245. autocoder/common/search_replace_patch/manager.py +248 -0
  246. autocoder/common/search_replace_patch/patch_replacer.py +304 -0
  247. autocoder/common/search_replace_patch/similarity_replacer.py +306 -0
  248. autocoder/common/search_replace_patch/string_replacer.py +181 -0
  249. autocoder/common/search_replace_patch/tests/__init__.py +3 -0
  250. autocoder/common/search_replace_patch/tests/run_tests.py +126 -0
  251. autocoder/common/search_replace_patch/tests/test_base.py +188 -0
  252. autocoder/common/search_replace_patch/tests/test_empty_line_insert.py +233 -0
  253. autocoder/common/search_replace_patch/tests/test_integration.py +389 -0
  254. autocoder/common/search_replace_patch/tests/test_manager.py +351 -0
  255. autocoder/common/search_replace_patch/tests/test_patch_replacer.py +316 -0
  256. autocoder/common/search_replace_patch/tests/test_regex_replacer.py +306 -0
  257. autocoder/common/search_replace_patch/tests/test_similarity_replacer.py +384 -0
  258. autocoder/common/shell_commands/__init__.py +197 -0
  259. autocoder/common/shell_commands/background_process_notifier.py +346 -0
  260. autocoder/common/shell_commands/command_executor.py +1127 -0
  261. autocoder/common/shell_commands/error_recovery.py +541 -0
  262. autocoder/common/shell_commands/exceptions.py +120 -0
  263. autocoder/common/shell_commands/interactive_executor.py +476 -0
  264. autocoder/common/shell_commands/interactive_pexpect_process.py +623 -0
  265. autocoder/common/shell_commands/interactive_process.py +744 -0
  266. autocoder/common/shell_commands/interactive_session_manager.py +1014 -0
  267. autocoder/common/shell_commands/monitoring.py +529 -0
  268. autocoder/common/shell_commands/process_cleanup.py +386 -0
  269. autocoder/common/shell_commands/process_manager.py +606 -0
  270. autocoder/common/shell_commands/test_interactive_pexpect_process.py +281 -0
  271. autocoder/common/shell_commands/tests/__init__.py +6 -0
  272. autocoder/common/shell_commands/tests/conftest.py +118 -0
  273. autocoder/common/shell_commands/tests/test_background_process_notifier.py +703 -0
  274. autocoder/common/shell_commands/tests/test_command_executor.py +448 -0
  275. autocoder/common/shell_commands/tests/test_error_recovery.py +305 -0
  276. autocoder/common/shell_commands/tests/test_exceptions.py +299 -0
  277. autocoder/common/shell_commands/tests/test_execute_batch.py +588 -0
  278. autocoder/common/shell_commands/tests/test_indented_batch_commands.py +244 -0
  279. autocoder/common/shell_commands/tests/test_integration.py +664 -0
  280. autocoder/common/shell_commands/tests/test_monitoring.py +546 -0
  281. autocoder/common/shell_commands/tests/test_performance.py +632 -0
  282. autocoder/common/shell_commands/tests/test_process_cleanup.py +397 -0
  283. autocoder/common/shell_commands/tests/test_process_manager.py +606 -0
  284. autocoder/common/shell_commands/tests/test_timeout_config.py +343 -0
  285. autocoder/common/shell_commands/tests/test_timeout_manager.py +520 -0
  286. autocoder/common/shell_commands/timeout_config.py +315 -0
  287. autocoder/common/shell_commands/timeout_manager.py +352 -0
  288. autocoder/common/terminal_paste/__init__.py +14 -0
  289. autocoder/common/terminal_paste/demo.py +145 -0
  290. autocoder/common/terminal_paste/demo_paste_functionality.py +95 -0
  291. autocoder/common/terminal_paste/paste_handler.py +200 -0
  292. autocoder/common/terminal_paste/paste_manager.py +118 -0
  293. autocoder/common/terminal_paste/tests/__init__.py +1 -0
  294. autocoder/common/terminal_paste/tests/test_paste_handler.py +182 -0
  295. autocoder/common/terminal_paste/tests/test_paste_manager.py +126 -0
  296. autocoder/common/terminal_paste/utils.py +163 -0
  297. autocoder/common/test_autocoder_args.py +232 -0
  298. autocoder/common/test_env_manager.py +173 -0
  299. autocoder/common/test_env_manager_integration.py +159 -0
  300. autocoder/common/text_similarity/__init__.py +9 -0
  301. autocoder/common/text_similarity/demo.py +216 -0
  302. autocoder/common/text_similarity/examples.py +266 -0
  303. autocoder/common/text_similarity/test_text_similarity.py +306 -0
  304. autocoder/common/text_similarity/text_similarity.py +194 -0
  305. autocoder/common/text_similarity/utils.py +125 -0
  306. autocoder/common/todos/__init__.py +61 -0
  307. autocoder/common/todos/cache/__init__.py +16 -0
  308. autocoder/common/todos/cache/base_cache.py +89 -0
  309. autocoder/common/todos/cache/cache_manager.py +228 -0
  310. autocoder/common/todos/cache/memory_cache.py +225 -0
  311. autocoder/common/todos/config.py +155 -0
  312. autocoder/common/todos/exceptions.py +35 -0
  313. autocoder/common/todos/get_todo_manager.py +161 -0
  314. autocoder/common/todos/manager.py +537 -0
  315. autocoder/common/todos/models.py +239 -0
  316. autocoder/common/todos/storage/__init__.py +14 -0
  317. autocoder/common/todos/storage/base_storage.py +76 -0
  318. autocoder/common/todos/storage/file_storage.py +278 -0
  319. autocoder/common/tokens/counter.py +24 -2
  320. autocoder/common/tools_manager/__init__.py +17 -0
  321. autocoder/common/tools_manager/examples.py +162 -0
  322. autocoder/common/tools_manager/manager.py +385 -0
  323. autocoder/common/tools_manager/models.py +39 -0
  324. autocoder/common/tools_manager/test_tools_manager.py +303 -0
  325. autocoder/common/tools_manager/utils.py +191 -0
  326. autocoder/common/v2/agent/agentic_callbacks.py +270 -0
  327. autocoder/common/v2/agent/agentic_edit.py +2699 -1856
  328. autocoder/common/v2/agent/agentic_edit_change_manager.py +474 -0
  329. autocoder/common/v2/agent/agentic_edit_tools/__init__.py +35 -1
  330. autocoder/common/v2/agent/agentic_edit_tools/ac_mod_list_tool_resolver.py +279 -0
  331. autocoder/common/v2/agent/agentic_edit_tools/ac_mod_write_tool_resolver.py +10 -1
  332. autocoder/common/v2/agent/agentic_edit_tools/background_task_tool_resolver.py +1167 -0
  333. autocoder/common/v2/agent/agentic_edit_tools/base_tool_resolver.py +2 -2
  334. autocoder/common/v2/agent/agentic_edit_tools/conversation_message_ids_read_tool_resolver.py +214 -0
  335. autocoder/common/v2/agent/agentic_edit_tools/conversation_message_ids_write_tool_resolver.py +299 -0
  336. autocoder/common/v2/agent/agentic_edit_tools/count_tokens_tool_resolver.py +290 -0
  337. autocoder/common/v2/agent/agentic_edit_tools/execute_command_tool_resolver.py +564 -29
  338. autocoder/common/v2/agent/agentic_edit_tools/execute_workflow_tool_resolver.py +485 -0
  339. autocoder/common/v2/agent/agentic_edit_tools/extract_to_text_tool_resolver.py +225 -0
  340. autocoder/common/v2/agent/agentic_edit_tools/lint_report.py +79 -0
  341. autocoder/common/v2/agent/agentic_edit_tools/linter_config_models.py +343 -0
  342. autocoder/common/v2/agent/agentic_edit_tools/linter_enabled_tool_resolver.py +189 -0
  343. autocoder/common/v2/agent/agentic_edit_tools/list_files_tool_resolver.py +169 -101
  344. autocoder/common/v2/agent/agentic_edit_tools/load_extra_document_tool_resolver.py +349 -0
  345. autocoder/common/v2/agent/agentic_edit_tools/read_file_tool_resolver.py +243 -50
  346. autocoder/common/v2/agent/agentic_edit_tools/replace_in_file_tool_resolver.py +667 -147
  347. autocoder/common/v2/agent/agentic_edit_tools/run_named_subagents_tool_resolver.py +691 -0
  348. autocoder/common/v2/agent/agentic_edit_tools/search_files_tool_resolver.py +410 -86
  349. autocoder/common/v2/agent/agentic_edit_tools/session_interactive_tool_resolver.py +115 -0
  350. autocoder/common/v2/agent/agentic_edit_tools/session_start_tool_resolver.py +190 -0
  351. autocoder/common/v2/agent/agentic_edit_tools/session_stop_tool_resolver.py +76 -0
  352. autocoder/common/v2/agent/agentic_edit_tools/test_write_to_file_tool_resolver.py +207 -192
  353. autocoder/common/v2/agent/agentic_edit_tools/todo_read_tool_resolver.py +80 -63
  354. autocoder/common/v2/agent/agentic_edit_tools/todo_write_tool_resolver.py +237 -233
  355. autocoder/common/v2/agent/agentic_edit_tools/use_mcp_tool_resolver.py +2 -2
  356. autocoder/common/v2/agent/agentic_edit_tools/web_crawl_tool_resolver.py +557 -0
  357. autocoder/common/v2/agent/agentic_edit_tools/web_search_tool_resolver.py +600 -0
  358. autocoder/common/v2/agent/agentic_edit_tools/write_to_file_tool_resolver.py +56 -121
  359. autocoder/common/v2/agent/agentic_edit_types.py +343 -9
  360. autocoder/common/v2/agent/runner/__init__.py +3 -3
  361. autocoder/common/v2/agent/runner/base_runner.py +12 -26
  362. autocoder/common/v2/agent/runner/{event_runner.py → file_based_event_runner.py} +3 -2
  363. autocoder/common/v2/agent/runner/sdk_runner.py +150 -8
  364. autocoder/common/v2/agent/runner/terminal_runner.py +170 -57
  365. autocoder/common/v2/agent/runner/tool_display.py +557 -159
  366. autocoder/common/v2/agent/test_agentic_callbacks.py +265 -0
  367. autocoder/common/v2/agent/test_agentic_edit.py +194 -0
  368. autocoder/common/v2/agent/tool_caller/__init__.py +24 -0
  369. autocoder/common/v2/agent/tool_caller/default_tool_resolver_map.py +135 -0
  370. autocoder/common/v2/agent/tool_caller/integration_test.py +172 -0
  371. autocoder/common/v2/agent/tool_caller/plugins/__init__.py +14 -0
  372. autocoder/common/v2/agent/tool_caller/plugins/base_plugin.py +126 -0
  373. autocoder/common/v2/agent/tool_caller/plugins/examples/__init__.py +13 -0
  374. autocoder/common/v2/agent/tool_caller/plugins/examples/logging_plugin.py +164 -0
  375. autocoder/common/v2/agent/tool_caller/plugins/examples/security_filter_plugin.py +198 -0
  376. autocoder/common/v2/agent/tool_caller/plugins/plugin_interface.py +141 -0
  377. autocoder/common/v2/agent/tool_caller/test_tool_caller.py +278 -0
  378. autocoder/common/v2/agent/tool_caller/tool_call_plugin_manager.py +331 -0
  379. autocoder/common/v2/agent/tool_caller/tool_caller.py +337 -0
  380. autocoder/common/v2/agent/tool_caller/usage_example.py +193 -0
  381. autocoder/common/v2/code_agentic_editblock_manager.py +4 -4
  382. autocoder/common/v2/code_auto_generate.py +136 -78
  383. autocoder/common/v2/code_auto_generate_diff.py +135 -79
  384. autocoder/common/v2/code_auto_generate_editblock.py +174 -99
  385. autocoder/common/v2/code_auto_generate_strict_diff.py +151 -71
  386. autocoder/common/v2/code_auto_merge.py +1 -1
  387. autocoder/common/v2/code_auto_merge_editblock.py +13 -1
  388. autocoder/common/v2/code_diff_manager.py +3 -3
  389. autocoder/common/v2/code_editblock_manager.py +4 -14
  390. autocoder/common/v2/code_manager.py +1 -1
  391. autocoder/common/v2/code_strict_diff_manager.py +2 -2
  392. autocoder/common/wrap_llm_hint/__init__.py +10 -0
  393. autocoder/common/wrap_llm_hint/test_wrap_llm_hint.py +1067 -0
  394. autocoder/common/wrap_llm_hint/utils.py +432 -0
  395. autocoder/common/wrap_llm_hint/wrap_llm_hint.py +323 -0
  396. autocoder/completer/__init__.py +8 -0
  397. autocoder/completer/command_completer_v2.py +1051 -0
  398. autocoder/default_project/__init__.py +501 -0
  399. autocoder/dispacher/__init__.py +4 -12
  400. autocoder/dispacher/actions/action.py +165 -7
  401. autocoder/dispacher/actions/plugins/action_regex_project.py +2 -2
  402. autocoder/index/entry.py +116 -124
  403. autocoder/{agent → index/filter}/agentic_filter.py +322 -333
  404. autocoder/index/filter/normal_filter.py +5 -11
  405. autocoder/index/filter/quick_filter.py +1 -1
  406. autocoder/index/index.py +36 -9
  407. autocoder/index/tests/__init__.py +1 -0
  408. autocoder/index/tests/run_tests.py +195 -0
  409. autocoder/index/tests/test_entry.py +303 -0
  410. autocoder/index/tests/test_index_manager.py +314 -0
  411. autocoder/index/tests/test_module_integration.py +300 -0
  412. autocoder/index/tests/test_symbols_utils.py +183 -0
  413. autocoder/inner/__init__.py +4 -0
  414. autocoder/inner/agentic.py +932 -0
  415. autocoder/inner/async_command_handler.py +992 -0
  416. autocoder/inner/conversation_command_handlers.py +623 -0
  417. autocoder/inner/merge_command_handler.py +213 -0
  418. autocoder/inner/queue_command_handler.py +684 -0
  419. autocoder/models.py +95 -266
  420. autocoder/plugins/git_helper_plugin.py +31 -29
  421. autocoder/plugins/token_helper_plugin.py +65 -46
  422. autocoder/pyproject/__init__.py +32 -29
  423. autocoder/rag/agentic_rag.py +215 -75
  424. autocoder/rag/cache/simple_cache.py +1 -2
  425. autocoder/rag/loaders/image_loader.py +1 -1
  426. autocoder/rag/long_context_rag.py +42 -26
  427. autocoder/rag/qa_conversation_strategy.py +1 -1
  428. autocoder/rag/terminal/__init__.py +17 -0
  429. autocoder/rag/terminal/args.py +581 -0
  430. autocoder/rag/terminal/bootstrap.py +61 -0
  431. autocoder/rag/terminal/command_handlers.py +653 -0
  432. autocoder/rag/terminal/formatters/__init__.py +20 -0
  433. autocoder/rag/terminal/formatters/base.py +70 -0
  434. autocoder/rag/terminal/formatters/json_format.py +66 -0
  435. autocoder/rag/terminal/formatters/stream_json.py +95 -0
  436. autocoder/rag/terminal/formatters/text.py +28 -0
  437. autocoder/rag/terminal/init.py +120 -0
  438. autocoder/rag/terminal/utils.py +106 -0
  439. autocoder/rag/test_agentic_rag.py +389 -0
  440. autocoder/rag/test_doc_filter.py +3 -3
  441. autocoder/rag/test_long_context_rag.py +1 -1
  442. autocoder/rag/test_token_limiter.py +517 -10
  443. autocoder/rag/token_counter.py +3 -0
  444. autocoder/rag/token_limiter.py +19 -15
  445. autocoder/rag/tools/__init__.py +26 -2
  446. autocoder/rag/tools/bochaai_example.py +343 -0
  447. autocoder/rag/tools/bochaai_sdk.py +541 -0
  448. autocoder/rag/tools/metaso_example.py +268 -0
  449. autocoder/rag/tools/metaso_sdk.py +417 -0
  450. autocoder/rag/tools/recall_tool.py +28 -7
  451. autocoder/rag/tools/run_integration_tests.py +204 -0
  452. autocoder/rag/tools/test_all_providers.py +318 -0
  453. autocoder/rag/tools/test_bochaai_integration.py +482 -0
  454. autocoder/rag/tools/test_final_integration.py +215 -0
  455. autocoder/rag/tools/test_metaso_integration.py +424 -0
  456. autocoder/rag/tools/test_metaso_real.py +171 -0
  457. autocoder/rag/tools/test_web_crawl_tool.py +639 -0
  458. autocoder/rag/tools/test_web_search_tool.py +509 -0
  459. autocoder/rag/tools/todo_read_tool.py +202 -0
  460. autocoder/rag/tools/todo_write_tool.py +412 -0
  461. autocoder/rag/tools/web_crawl_tool.py +634 -0
  462. autocoder/rag/tools/web_search_tool.py +558 -0
  463. autocoder/rag/tools/web_tools_example.py +119 -0
  464. autocoder/rag/types.py +16 -0
  465. autocoder/rag/variable_holder.py +4 -2
  466. autocoder/rags.py +86 -79
  467. autocoder/regexproject/__init__.py +23 -21
  468. autocoder/sdk/__init__.py +46 -190
  469. autocoder/sdk/api.py +370 -0
  470. autocoder/sdk/async_runner/__init__.py +26 -0
  471. autocoder/sdk/async_runner/async_executor.py +650 -0
  472. autocoder/sdk/async_runner/async_handler.py +356 -0
  473. autocoder/sdk/async_runner/markdown_processor.py +595 -0
  474. autocoder/sdk/async_runner/task_metadata.py +284 -0
  475. autocoder/sdk/async_runner/worktree_manager.py +438 -0
  476. autocoder/sdk/cli/__init__.py +2 -5
  477. autocoder/sdk/cli/formatters.py +28 -204
  478. autocoder/sdk/cli/handlers.py +77 -44
  479. autocoder/sdk/cli/main.py +154 -171
  480. autocoder/sdk/cli/options.py +95 -22
  481. autocoder/sdk/constants.py +139 -51
  482. autocoder/sdk/core/auto_coder_core.py +484 -109
  483. autocoder/sdk/core/bridge.py +297 -115
  484. autocoder/sdk/exceptions.py +18 -12
  485. autocoder/sdk/formatters/__init__.py +19 -0
  486. autocoder/sdk/formatters/input.py +64 -0
  487. autocoder/sdk/formatters/output.py +247 -0
  488. autocoder/sdk/formatters/stream.py +54 -0
  489. autocoder/sdk/models/__init__.py +6 -5
  490. autocoder/sdk/models/options.py +55 -18
  491. autocoder/sdk/utils/formatters.py +27 -195
  492. autocoder/suffixproject/__init__.py +28 -25
  493. autocoder/terminal/__init__.py +14 -0
  494. autocoder/terminal/app.py +454 -0
  495. autocoder/terminal/args.py +32 -0
  496. autocoder/terminal/bootstrap.py +178 -0
  497. autocoder/terminal/command_processor.py +521 -0
  498. autocoder/terminal/command_registry.py +57 -0
  499. autocoder/terminal/help.py +97 -0
  500. autocoder/terminal/tasks/__init__.py +5 -0
  501. autocoder/terminal/tasks/background.py +77 -0
  502. autocoder/terminal/tasks/task_event.py +70 -0
  503. autocoder/terminal/ui/__init__.py +13 -0
  504. autocoder/terminal/ui/completer.py +268 -0
  505. autocoder/terminal/ui/keybindings.py +75 -0
  506. autocoder/terminal/ui/session.py +41 -0
  507. autocoder/terminal/ui/toolbar.py +64 -0
  508. autocoder/terminal/utils/__init__.py +13 -0
  509. autocoder/terminal/utils/errors.py +18 -0
  510. autocoder/terminal/utils/paths.py +19 -0
  511. autocoder/terminal/utils/shell.py +43 -0
  512. autocoder/terminal_v3/__init__.py +10 -0
  513. autocoder/terminal_v3/app.py +201 -0
  514. autocoder/terminal_v3/handlers/__init__.py +5 -0
  515. autocoder/terminal_v3/handlers/command_handler.py +131 -0
  516. autocoder/terminal_v3/models/__init__.py +6 -0
  517. autocoder/terminal_v3/models/conversation_buffer.py +214 -0
  518. autocoder/terminal_v3/models/message.py +50 -0
  519. autocoder/terminal_v3/models/tool_display.py +247 -0
  520. autocoder/terminal_v3/ui/__init__.py +7 -0
  521. autocoder/terminal_v3/ui/keybindings.py +56 -0
  522. autocoder/terminal_v3/ui/layout.py +141 -0
  523. autocoder/terminal_v3/ui/styles.py +43 -0
  524. autocoder/tsproject/__init__.py +23 -23
  525. autocoder/utils/auto_coder_utils/chat_stream_out.py +1 -1
  526. autocoder/utils/llms.py +88 -80
  527. autocoder/utils/math_utils.py +101 -0
  528. autocoder/utils/model_provider_selector.py +16 -4
  529. autocoder/utils/operate_config_api.py +33 -5
  530. autocoder/utils/thread_utils.py +2 -2
  531. autocoder/version.py +4 -2
  532. autocoder/workflow_agents/__init__.py +84 -0
  533. autocoder/workflow_agents/agent.py +143 -0
  534. autocoder/workflow_agents/exceptions.py +573 -0
  535. autocoder/workflow_agents/executor.py +489 -0
  536. autocoder/workflow_agents/loader.py +737 -0
  537. autocoder/workflow_agents/runner.py +267 -0
  538. autocoder/workflow_agents/types.py +172 -0
  539. autocoder/workflow_agents/utils.py +434 -0
  540. autocoder/workflow_agents/workflow_manager.py +211 -0
  541. auto_coder-1.0.0.dist-info/METADATA +0 -396
  542. auto_coder-1.0.0.dist-info/RECORD +0 -442
  543. auto_coder-1.0.0.dist-info/licenses/LICENSE +0 -201
  544. autocoder/auto_coder_server.py +0 -672
  545. autocoder/benchmark.py +0 -138
  546. autocoder/common/ac_style_command_parser/example.py +0 -7
  547. autocoder/common/cleaner.py +0 -31
  548. autocoder/common/command_completer_v2.py +0 -615
  549. autocoder/common/context_pruner.py +0 -477
  550. autocoder/common/conversation_pruner.py +0 -132
  551. autocoder/common/directory_cache/__init__.py +0 -1
  552. autocoder/common/directory_cache/cache.py +0 -192
  553. autocoder/common/directory_cache/test_cache.py +0 -190
  554. autocoder/common/file_checkpoint/examples.py +0 -217
  555. autocoder/common/llm_friendly_package_example.py +0 -138
  556. autocoder/common/llm_friendly_package_test.py +0 -63
  557. autocoder/common/pull_requests/test_module.py +0 -1
  558. autocoder/common/rulefiles/autocoderrules_utils.py +0 -484
  559. autocoder/common/text.py +0 -30
  560. autocoder/common/v2/agent/agentic_edit_tools/list_package_info_tool_resolver.py +0 -42
  561. autocoder/common/v2/agent/agentic_edit_tools/test_execute_command_tool_resolver.py +0 -70
  562. autocoder/common/v2/agent/agentic_edit_tools/test_search_files_tool_resolver.py +0 -163
  563. autocoder/common/v2/agent/agentic_tool_display.py +0 -183
  564. autocoder/plugins/dynamic_completion_example.py +0 -148
  565. autocoder/plugins/sample_plugin.py +0 -160
  566. autocoder/sdk/cli/__main__.py +0 -26
  567. autocoder/sdk/cli/completion_wrapper.py +0 -38
  568. autocoder/sdk/cli/install_completion.py +0 -301
  569. autocoder/sdk/models/messages.py +0 -209
  570. autocoder/sdk/session/__init__.py +0 -32
  571. autocoder/sdk/session/session.py +0 -106
  572. autocoder/sdk/session/session_manager.py +0 -56
  573. {auto_coder-1.0.0.dist-info → auto_coder-2.0.0.dist-info}/top_level.txt +0 -0
  574. /autocoder/{sdk/example.py → common/agent_query_queue/__init__.py} +0 -0
@@ -1,8 +1,7 @@
1
- from itertools import product
1
+ from autocoder.common.project_scanner.compat import create_scanner_functions
2
2
  from rich.console import Console
3
3
  from rich.panel import Panel
4
4
  from prompt_toolkit.formatted_text import HTML
5
- from prompt_toolkit.shortcuts import radiolist_dialog
6
5
  from prompt_toolkit import prompt
7
6
  import os
8
7
  import yaml
@@ -10,73 +9,106 @@ import json
10
9
  import sys
11
10
  import io
12
11
  import uuid
13
- import glob
14
12
  import time
15
- import datetime
16
- import hashlib
13
+ import byzerllm
14
+ import subprocess
17
15
  from contextlib import contextmanager
18
16
  from typing import List, Dict, Any, Optional
19
17
  from autocoder.common import AutoCoderArgs
18
+ from autocoder.common.autocoderargs_parser import AutoCoderArgsParser
20
19
  from pydantic import BaseModel
21
- from autocoder.common.action_yml_file_manager import ActionYmlFileManager
22
20
  from autocoder.common.result_manager import ResultManager
23
21
  from autocoder.version import __version__
24
22
  from autocoder.auto_coder import main as auto_coder_main
25
23
  from autocoder.utils import get_last_yaml_file
26
- from autocoder.commands.auto_command import CommandAutoTuner, AutoCommandRequest, CommandConfig, MemoryConfig
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
+ from autocoder.inner.async_command_handler import AsyncCommandHandler
94
+ from autocoder.inner.queue_command_handler import QueueCommandHandler
95
+ from autocoder.inner.conversation_command_handlers import (
96
+ ConversationNewCommandHandler,
97
+ ConversationResumeCommandHandler,
98
+ ConversationListCommandHandler,
99
+ ConversationRenameCommandHandler,
100
+ ConversationCommandCommandHandler,
101
+ )
102
+
103
+ # 对外API,用于第三方集成 auto-coder 使用。
104
+
73
105
 
74
- ## 对外API,用于第三方集成 auto-coder 使用。
75
106
  class SymbolItem(BaseModel):
76
107
  symbol_name: str
77
108
  symbol_type: SymbolType
78
109
  file_name: str
79
110
 
111
+
80
112
  class InitializeSystemRequest(BaseModel):
81
113
  product_mode: str
82
114
  skip_provider_selection: bool
@@ -92,20 +124,45 @@ if platform.system() == "Windows":
92
124
  init()
93
125
 
94
126
 
95
- memory = {
96
- "conversation": [],
97
- "current_files": {"files": [], "groups": {}},
98
- "conf": {},
99
- "exclude_dirs": [],
100
- "mode": "auto_detect", # 新增mode字段,默认为 auto_detect 模式
101
- }
102
-
127
+ # Initialize memory and project root
103
128
  project_root = os.getcwd()
104
129
 
130
+ # Initialize memory manager with project root
131
+ _memory_manager = get_memory_manager(project_root)
132
+
133
+ # Wrapper functions to sync global memory variable
134
+
135
+
136
+ def save_memory():
137
+ """Save memory - compatibility function (no-op since MemoryManager handles persistence)"""
138
+ # This function is kept for backward compatibility but does nothing
139
+ # since MemoryManager automatically handles persistence
140
+ raise NotImplementedError(
141
+ "save_memory is not supported anymore, please use autocoder.common.core_config.memory_manager instead."
142
+ )
143
+
144
+
145
+ def load_memory():
146
+ """Load memory using MemoryManager"""
147
+ return _load_memory()
148
+
105
149
 
106
- base_persist_dir = os.path.join(project_root,".auto-coder", "plugins", "chat-auto-coder")
150
+ def get_memory():
151
+ """Get current memory"""
152
+ return load_memory()
107
153
 
108
- defaut_exclude_dirs = [".git", "node_modules", "dist", "build", "__pycache__",".auto-coder"]
154
+
155
+ # Compatibility: base_persist_dir is now managed by memory manager
156
+ base_persist_dir = _memory_manager.base_persist_dir
157
+
158
+ defaut_exclude_dirs = [
159
+ ".git",
160
+ "node_modules",
161
+ "dist",
162
+ "build",
163
+ "__pycache__",
164
+ ".auto-coder",
165
+ ]
109
166
 
110
167
  commands = [
111
168
  "/add_files",
@@ -122,7 +179,7 @@ commands = [
122
179
  "/index/build",
123
180
  "/index/export",
124
181
  "/index/import",
125
- "/exclude_files",
182
+ "/exclude_files",
126
183
  "/help",
127
184
  "/shell",
128
185
  "/voice_input",
@@ -137,21 +194,65 @@ commands = [
137
194
  "/conf/export",
138
195
  "/conf/import",
139
196
  "/exclude_dirs",
197
+ "/queue",
140
198
  ]
141
199
 
200
+
142
201
  def load_tokenizer():
143
202
  from autocoder.rag.variable_holder import VariableHolder
144
- from tokenizers import Tokenizer
203
+ from tokenizers import Tokenizer
204
+
145
205
  try:
146
- tokenizer_path = pkg_resources.resource_filename(
147
- "autocoder", "data/tokenizer.json"
148
- )
206
+ tokenizer_path = str(resources.files("autocoder") / "data" / "tokenizer.json")
149
207
  VariableHolder.TOKENIZER_PATH = tokenizer_path
150
208
  VariableHolder.TOKENIZER_MODEL = Tokenizer.from_file(tokenizer_path)
151
209
  except FileNotFoundError:
152
210
  tokenizer_path = None
153
211
 
154
212
 
213
+ def configure_logger():
214
+ # 设置日志目录和文件
215
+ log_dir = os.path.join(project_root, ".auto-coder", "logs")
216
+ os.makedirs(log_dir, exist_ok=True)
217
+ log_file = os.path.join(log_dir, "auto-coder.log")
218
+
219
+ # 配置全局日志
220
+ # 默认情况下,所有日志都写入文件
221
+ # 控制台上默认不输出任何日志,除非显式配置
222
+ global_logger.configure(
223
+ handlers=[
224
+ {
225
+ "sink": log_file,
226
+ "level": "INFO",
227
+ "rotation": "10 MB",
228
+ "retention": "1 week",
229
+ "format": "{time:YYYY-MM-DD HH:mm:ss} | {level} | {name} | {message}",
230
+ },
231
+ {
232
+ "sink": sys.stdout,
233
+ "level": "INFO",
234
+ "format": "{time:YYYY-MM-DD HH:mm:ss} | {name} | {message}",
235
+ # 默认不打印任何日志到控制台
236
+ "filter": lambda record: False,
237
+ },
238
+ ]
239
+ )
240
+
241
+
242
+ def init_singleton_instances():
243
+ # 初始化文件监控系统
244
+ try:
245
+ FileMonitor(project_root).start()
246
+ except Exception as e:
247
+ global_logger.error(f"Failed to start file monitor: {e}")
248
+ global_logger.exception(e)
249
+
250
+ # 初始化忽略文件管理器
251
+ from autocoder.common.ignorefiles.ignore_file_utils import IgnoreFileManager
252
+
253
+ _ = IgnoreFileManager(project_root=project_root)
254
+
255
+
155
256
  def configure_project_type():
156
257
  from prompt_toolkit.lexers import PygmentsLexer
157
258
  from pygments.lexers.markup import MarkdownLexer
@@ -173,12 +274,10 @@ def configure_project_type():
173
274
  print_formatted_text(HTML(f"<info>{escape(text)}</info>"), style=style)
174
275
 
175
276
  def print_warning(text):
176
- print_formatted_text(
177
- HTML(f"<warning>{escape(text)}</warning>"), style=style)
277
+ print_formatted_text(HTML(f"<warning>{escape(text)}</warning>"), style=style)
178
278
 
179
279
  def print_header(text):
180
- print_formatted_text(
181
- HTML(f"<header>{escape(text)}</header>"), style=style)
280
+ print_formatted_text(HTML(f"<header>{escape(text)}</header>"), style=style)
182
281
 
183
282
  print_header(f"\n=== {get_message('project_type_config')} ===\n")
184
283
  print_info(get_message("project_type_supports"))
@@ -188,7 +287,7 @@ def configure_project_type():
188
287
  print_info(get_message("examples"))
189
288
 
190
289
  print_warning(f"{get_message('default_type')}\n")
191
-
290
+
192
291
  extensions = get_all_extensions(project_root) or "py"
193
292
  project_type = prompt(
194
293
  get_message("enter_project_type"), default=extensions, style=style
@@ -215,72 +314,27 @@ def get_all_extensions(directory: str = ".") -> str:
215
314
  target_file="",
216
315
  git_url="",
217
316
  project_type="",
218
- conversation_prune_safe_zone_tokens=0
317
+ conversation_prune_safe_zone_tokens=0,
219
318
  )
220
-
319
+
221
320
  analyzer = EnhancedFileAnalyzer(
222
321
  args=args,
223
322
  llm=None, # 如果只是获取后缀名,可以不需要LLM
224
- config=None # 使用默认配置
323
+ config=None, # 使用默认配置
225
324
  )
226
-
325
+
227
326
  # 获取分析结果
228
327
  analysis_result = analyzer.analyze_extensions()
229
-
328
+
230
329
  # 合并 code 和 config 的后缀名
231
330
  all_extensions = set(analysis_result["code"] + analysis_result["config"])
232
-
331
+
233
332
  # 转换为逗号分隔的字符串
234
333
  return ",".join(sorted(all_extensions))
235
334
 
236
- def configure_logger():
237
- # 设置日志目录和文件
238
- log_dir = os.path.join(project_root, ".auto-coder", "logs")
239
- os.makedirs(log_dir, exist_ok=True)
240
- log_file = os.path.join(log_dir, "auto-coder.log")
241
-
242
- # 配置全局日志
243
- # 默认情况下,所有日志都写入文件
244
- # 控制台上默认不输出任何日志,除非显式配置
245
- global_logger.configure(
246
- handlers=[
247
- {
248
- "sink": log_file,
249
- "level": "INFO",
250
- "rotation": "10 MB",
251
- "retention": "1 week",
252
- "format": "{time:YYYY-MM-DD HH:mm:ss} | {level} | {name} | {message}",
253
- },
254
- {
255
- "sink": sys.stdout,
256
- "level": "INFO",
257
- "format": "{time:YYYY-MM-DD HH:mm:ss} | {name} | {message}",
258
- # 默认不打印任何日志到控制台
259
- "filter": lambda record: False
260
- }
261
- ]
262
- )
263
-
264
-
265
- def init_singleton_instances():
266
- # 初始化文件监控系统
267
- try:
268
- FileMonitor(project_root).start()
269
- except Exception as e:
270
- global_logger.error(f"Failed to start file monitor: {e}")
271
- global_logger.exception(e)
272
-
273
- # 初始化规则文件管理器
274
- from autocoder.common.rulefiles.autocoderrules_utils import get_rules
275
- get_rules(project_root=project_root)
276
-
277
- # 初始化忽略文件管理器
278
- from autocoder.common.ignorefiles.ignore_file_utils import IgnoreFileManager
279
- _ = IgnoreFileManager(project_root=project_root)
280
-
281
335
 
282
- def start():
283
- if os.environ.get('autocoder_auto_init',"true") in ["true","True","True",True]:
336
+ def start():
337
+ if os.environ.get("autocoder_auto_init", "true") in ["true", "True", "True", True]:
284
338
  configure_logger()
285
339
  init_singleton_instances()
286
340
 
@@ -292,7 +346,7 @@ def start():
292
346
  # name = f"{time_str}-{str(uuid.uuid4())}"
293
347
  # conversation_id = conversation_manager.create_new_conversation(name=name,description="")
294
348
  # conversation_manager.set_current_conversation(conversation_id)
295
-
349
+
296
350
 
297
351
  def stop():
298
352
  try:
@@ -301,9 +355,11 @@ def stop():
301
355
  global_logger.error(f"Failed to stop file monitor: {e}")
302
356
  global_logger.exception(e)
303
357
 
304
- def initialize_system(args:InitializeSystemRequest):
358
+
359
+ def initialize_system(args: InitializeSystemRequest):
305
360
  from autocoder.utils.model_provider_selector import ModelProviderSelector
306
- from autocoder import models as models_module
361
+ from autocoder.common.llms import LLMManager
362
+
307
363
  print(f"\n\033[1;34m{get_message('initializing')}\033[0m")
308
364
 
309
365
  first_time = [False]
@@ -319,54 +375,42 @@ def initialize_system(args:InitializeSystemRequest):
319
375
  else:
320
376
  print(f" {message}")
321
377
 
322
- def init_project():
323
- if not os.path.exists(".auto-coder") or not os.path.exists("actions"):
324
- first_time[0] = True
325
- print_status(get_message("not_initialized"), "warning")
326
- init_choice = input(
327
- f" {get_message('init_prompt')}").strip().lower()
328
- if init_choice == "y":
329
- try:
330
- subprocess.run(
331
- ["auto-coder", "init", "--source_dir", "."], check=True
332
- )
333
- print_status(get_message("init_success"), "success")
334
- except subprocess.CalledProcessError:
335
- print_status(get_message("init_fail"), "error")
336
- print_status(get_message("init_manual"), "warning")
337
- exit(1)
338
- else:
339
- print_status(get_message("exit_no_init"), "warning")
340
- exit(1)
341
-
342
378
  if not os.path.exists(base_persist_dir):
343
379
  os.makedirs(base_persist_dir, exist_ok=True)
344
- print_status(get_message_with_format("created_dir",path=base_persist_dir), "success")
380
+ print_status(
381
+ get_message_with_format("created_dir", path=base_persist_dir), "success"
382
+ )
345
383
 
346
384
  if first_time[0]:
347
- configure_project_type()
385
+ configure("project_type:*", skip_print=True)
348
386
  configure_success[0] = True
349
387
 
350
388
  print_status(get_message("init_complete"), "success")
351
389
 
352
- init_project()
390
+ init_project_if_required(target_dir=project_root, project_type="*")
353
391
 
354
392
  if not args.skip_provider_selection and first_time[0]:
355
- if args.product_mode == "lite":
356
- ## 如果已经是配置过的项目,就无需再选择
393
+ if args.product_mode == "lite":
394
+ # 如果已经是配置过的项目,就无需再选择
357
395
  if first_time[0]:
358
- if not models_module.check_model_exists("v3_chat") or not models_module.check_model_exists("r1_chat"):
396
+ llm_manager = LLMManager()
397
+ if not llm_manager.check_model_exists(
398
+ "v3_chat"
399
+ ) or not llm_manager.check_model_exists("r1_chat"):
359
400
  model_provider_selector = ModelProviderSelector()
360
401
  model_provider_info = model_provider_selector.select_provider()
361
402
  if model_provider_info is not None:
362
- models_json_list = model_provider_selector.to_models_json(model_provider_info)
363
- models_module.add_and_activate_models(models_json_list)
403
+ models_json_list = model_provider_selector.to_models_json(
404
+ model_provider_info
405
+ )
406
+ llm_manager.add_models(models_json_list)
364
407
 
365
408
  if args.product_mode == "pro":
366
409
  # Check if Ray is running
367
410
  print_status(get_message("checking_ray"), "")
368
411
  ray_status = subprocess.run(
369
- ["ray", "status"], capture_output=True, text=True)
412
+ ["ray", "status"], capture_output=True, text=True
413
+ )
370
414
  if ray_status.returncode != 0:
371
415
  print_status(get_message("ray_not_running"), "warning")
372
416
  try:
@@ -389,7 +433,6 @@ def initialize_system(args:InitializeSystemRequest):
389
433
  )
390
434
  if result.returncode == 0:
391
435
  print_status(get_message("model_available"), "success")
392
- init_project()
393
436
  print_status(get_message("init_complete_final"), "success")
394
437
  return
395
438
  except subprocess.TimeoutExpired:
@@ -428,7 +471,6 @@ def initialize_system(args:InitializeSystemRequest):
428
471
  print_status(get_message("deploy_fail"), "error")
429
472
  return
430
473
 
431
-
432
474
  deploy_cmd = [
433
475
  "byzerllm",
434
476
  "deploy",
@@ -471,14 +513,18 @@ def initialize_system(args:InitializeSystemRequest):
471
513
  print_status(get_message("manual_start"), "warning")
472
514
  print_status("easy-byzerllm chat v3_chat 你好", "")
473
515
 
474
- print_status(get_message("init_complete_final"), "success")
516
+ print_status(get_message("init_complete_final"), "success")
475
517
  configure_success[0] = True
476
518
 
477
519
  if first_time[0] and args.product_mode == "pro" and configure_success[0]:
478
- configure(f"model:v3_chat", skip_print=True)
520
+ configure(f"model:v3_chat", skip_print=True)
479
521
 
480
- if first_time[0] and args.product_mode == "lite" and models_module.check_model_exists("v3_chat"):
481
- configure(f"model:v3_chat", skip_print=True)
522
+ if (
523
+ first_time[0]
524
+ and args.product_mode == "lite"
525
+ and LLMManager().check_model_exists("v3_chat")
526
+ ):
527
+ configure(f"model:v3_chat", skip_print=True)
482
528
 
483
529
 
484
530
  def convert_yaml_config_to_str(yaml_config):
@@ -491,92 +537,44 @@ def convert_yaml_config_to_str(yaml_config):
491
537
  return yaml_content
492
538
 
493
539
 
494
- def get_all_file_names_in_project() -> List[str]:
495
-
496
- file_names = []
497
- final_exclude_dirs = defaut_exclude_dirs + memory.get("exclude_dirs", [])
498
- for root, dirs, files in os.walk(project_root, followlinks=True):
499
- dirs[:] = [d for d in dirs if d not in final_exclude_dirs]
500
- file_names.extend(files)
501
- return file_names
502
-
503
-
504
- def get_all_file_in_project() -> List[str]:
505
-
506
- file_names = []
507
- final_exclude_dirs = defaut_exclude_dirs + memory.get("exclude_dirs", [])
508
- for root, dirs, files in os.walk(project_root, followlinks=True):
509
- dirs[:] = [d for d in dirs if d not in final_exclude_dirs]
510
- for file in files:
511
- file_names.append(os.path.join(root, file))
512
- return file_names
513
-
514
-
515
- def get_all_file_in_project_with_dot() -> List[str]:
516
- file_names = []
517
- final_exclude_dirs = defaut_exclude_dirs + memory.get("exclude_dirs", [])
518
- for root, dirs, files in os.walk(project_root, followlinks=True):
519
- dirs[:] = [d for d in dirs if d not in final_exclude_dirs]
520
- for file in files:
521
- file_names.append(os.path.join(
522
- root, file).replace(project_root, "."))
523
- return file_names
524
-
525
-
526
- def get_all_dir_names_in_project() -> List[str]:
527
- dir_names = []
528
- final_exclude_dirs = defaut_exclude_dirs + memory.get("exclude_dirs", [])
529
- for root, dirs, files in os.walk(project_root, followlinks=True):
530
- dirs[:] = [d for d in dirs if d not in final_exclude_dirs]
531
- for dir in dirs:
532
- dir_names.append(dir)
533
- return dir_names
534
-
535
-
536
- def find_files_in_project(patterns: List[str]) -> List[str]:
537
- matched_files = []
538
- final_exclude_dirs = defaut_exclude_dirs + memory.get("exclude_dirs", [])
539
-
540
- for pattern in patterns:
541
- if "*" in pattern or "?" in pattern:
542
- for file_path in glob.glob(pattern, recursive=True):
543
- if os.path.isfile(file_path):
544
- abs_path = os.path.abspath(file_path)
545
- if not any(
546
- exclude_dir in abs_path.split(os.sep)
547
- for exclude_dir in final_exclude_dirs
548
- ):
549
- matched_files.append(abs_path)
550
- else:
551
- is_added = False
552
- # add files belongs to project
553
- for root, dirs, files in os.walk(project_root, followlinks=True):
554
- dirs[:] = [d for d in dirs if d not in final_exclude_dirs]
555
- if pattern in files:
556
- matched_files.append(os.path.join(root, pattern))
557
- is_added = True
558
- else:
559
- for file in files:
560
- _pattern = os.path.abspath(pattern)
561
- if _pattern in os.path.join(root, file):
562
- matched_files.append(os.path.join(root, file))
563
- is_added = True
564
- # add files not belongs to project
565
- if not is_added:
566
- matched_files.append(pattern)
567
-
568
- return list(set(matched_files))
569
-
570
-
571
540
  def convert_config_value(key, value):
541
+ # 定义需要使用 token 解析的字段
542
+ token_fields = {
543
+ "conversation_prune_safe_zone_tokens",
544
+ "context_prune_safe_zone_tokens",
545
+ "context_prune_sliding_window_size",
546
+ "context_prune_sliding_window_overlap",
547
+ "rag_params_max_tokens",
548
+ "rag_context_window_limit",
549
+ "rag_duckdb_vector_dim",
550
+ "rag_duckdb_query_top_k",
551
+ "rag_emb_dim",
552
+ "rag_emb_text_size",
553
+ "hybrid_index_max_output_tokens",
554
+ "data_cells_max_num",
555
+ }
556
+
572
557
  field_info = AutoCoderArgs.model_fields.get(key)
573
558
  if field_info:
559
+ # 对于需要 token 解析的字段,使用 AutoCoderArgsParser
560
+ if key in token_fields:
561
+ try:
562
+ parser = AutoCoderArgsParser()
563
+ return parser.parse_token_field(key, value)
564
+ except Exception as e:
565
+ print(
566
+ f"Warning: Failed to parse token field '{key}' with value '{value}': {e}"
567
+ )
568
+ # 如果解析失败,fallback 到原有逻辑
569
+ pass
570
+
571
+ # 原有的类型转换逻辑
574
572
  if isinstance(value, str) and value.lower() in ["true", "false"]:
575
573
  return value.lower() == "true"
576
- elif "int" in str(field_info.annotation):
577
- return int(value)
578
574
  elif "float" in str(field_info.annotation):
579
575
  return float(value)
576
+ elif "int" in str(field_info.annotation):
577
+ return int(value)
580
578
  else:
581
579
  return value
582
580
  else:
@@ -594,14 +592,14 @@ def redirect_stdout():
594
592
  sys.stdout = original_stdout
595
593
 
596
594
 
597
- def configure(conf: str, skip_print=False):
595
+ def configure(conf: str, skip_print=True):
598
596
  printer = Printer()
597
+ memory_manager = get_memory_manager()
599
598
  parts = conf.split(None, 1)
600
599
  if len(parts) == 2 and parts[0] in ["/drop", "/unset", "/remove"]:
601
600
  key = parts[1].strip()
602
- if key in memory["conf"]:
603
- del memory["conf"][key]
604
- save_memory()
601
+ if memory_manager.has_config(key):
602
+ memory_manager.delete_config(key)
605
603
  printer.print_in_terminal("config_delete_success", style="green", key=key)
606
604
  else:
607
605
  printer.print_in_terminal("config_not_found", style="yellow", key=key)
@@ -616,109 +614,75 @@ def configure(conf: str, skip_print=False):
616
614
  if not value:
617
615
  printer.print_in_terminal("config_value_empty", style="red")
618
616
  return
619
- product_mode = memory["conf"].get("product_mode",None)
617
+ product_mode = memory_manager.get_config("product_mode", None)
620
618
  if product_mode:
621
- ConfigValidator.validate(key, value, product_mode)
622
- memory["conf"][key] = value
623
- save_memory()
619
+ ConfigValidator.validate(key, value, product_mode)
620
+ memory_manager.set_config(key, value)
624
621
  if not skip_print:
625
- printer.print_in_terminal("config_set_success", style="green", key=key, value=value)
622
+ printer.print_in_terminal(
623
+ "config_set_success", style="green", key=key, value=value
624
+ )
625
+
626
626
 
627
627
  # word_completer = WordCompleter(commands)
628
628
 
629
629
 
630
- def get_symbol_list() -> List[SymbolItem]:
631
- list_of_symbols = []
632
- index_file = os.path.join(".auto-coder", "index.json")
630
+ # Memory management functions are now imported from core_config module
631
+ # Helper functions to access memory without global variables
632
+ def get_current_memory():
633
+ """Get current memory as dictionary for backward compatibility"""
634
+ return get_memory()
633
635
 
634
- if os.path.exists(index_file):
635
- with open(index_file, "r",encoding="utf-8") as file:
636
- index_data = json.load(file)
637
- else:
638
- index_data = {}
639
-
640
- for item in index_data.values():
641
- symbols_str = item["symbols"]
642
- module_name = item["module_name"]
643
- info1 = extract_symbols(symbols_str)
644
- for name in info1.classes:
645
- list_of_symbols.append(
646
- SymbolItem(
647
- symbol_name=name,
648
- symbol_type=SymbolType.CLASSES,
649
- file_name=module_name,
650
- )
651
- )
652
- for name in info1.functions:
653
- list_of_symbols.append(
654
- SymbolItem(
655
- symbol_name=name,
656
- symbol_type=SymbolType.FUNCTIONS,
657
- file_name=module_name,
658
- )
659
- )
660
- for name in info1.variables:
661
- list_of_symbols.append(
662
- SymbolItem(
663
- symbol_name=name,
664
- symbol_type=SymbolType.VARIABLES,
665
- file_name=module_name,
666
- )
667
- )
668
- return list_of_symbols
636
+
637
+ def get_current_files():
638
+ """Get current files list"""
639
+ memory_manager = get_memory_manager()
640
+ return memory_manager.get_current_files()
641
+
642
+
643
+ def set_current_files(files):
644
+ """Set current files list"""
645
+ memory_manager = get_memory_manager()
646
+ memory_manager.set_current_files(files)
647
+
648
+
649
+ def get_file_groups():
650
+ """Get file groups"""
651
+ memory_manager = get_memory_manager()
652
+ return memory_manager.get_file_groups()
653
+
654
+
655
+ def get_exclude_dirs():
656
+ """Get exclude directories"""
657
+ memory_manager = get_memory_manager()
658
+ return memory_manager.get_exclude_dirs()
659
+
660
+
661
+ # 使用 project_scanner 模块创建兼容函数(供其他地方使用)
662
+ scanner_funcs = create_scanner_functions(
663
+ project_root=project_root,
664
+ default_exclude_dirs=defaut_exclude_dirs,
665
+ get_extra_exclude_dirs_func=get_exclude_dirs,
666
+ )
667
+
668
+ # 导出兼容函数
669
+ get_all_file_names_in_project = scanner_funcs["get_all_file_names_in_project"]
670
+ get_all_file_in_project = scanner_funcs["get_all_file_in_project"]
671
+ get_all_file_in_project_with_dot = scanner_funcs["get_all_file_in_project_with_dot"]
672
+ get_all_dir_names_in_project = scanner_funcs["get_all_dir_names_in_project"]
673
+ find_files_in_project = scanner_funcs["find_files_in_project"]
674
+ get_symbol_list = scanner_funcs["get_symbol_list"]
675
+
676
+ # 直接创建 CommandCompleterV2,它内部会使用 project_scanner
677
+ completer = CommandCompleterV2(
678
+ commands,
679
+ memory_model=CCMemoryModel(
680
+ get_memory_func=get_memory, save_memory_func=save_memory
681
+ ),
682
+ project_root=project_root,
683
+ )
669
684
 
670
685
 
671
- def save_memory():
672
- memory_path = os.path.join(base_persist_dir, "memory.json")
673
- lock_path = memory_path + ".lock"
674
-
675
- with FileLock(lock_path, timeout=30):
676
- with open(memory_path, "w", encoding="utf-8") as f:
677
- json.dump(memory, f, indent=2, ensure_ascii=False)
678
-
679
- load_memory()
680
-
681
-
682
- def save_memory_with_new_memory(new_memory):
683
- memory_path = os.path.join(base_persist_dir, "memory.json")
684
- lock_path = memory_path + ".lock"
685
-
686
- with FileLock(lock_path, timeout=30):
687
- with open(memory_path, "w", encoding="utf-8") as f:
688
- json.dump(new_memory, f, indent=2, ensure_ascii=False)
689
- load_memory()
690
-
691
-
692
- def load_memory():
693
- global memory
694
- memory_path = os.path.join(base_persist_dir, "memory.json")
695
- lock_path = memory_path + ".lock"
696
-
697
- if os.path.exists(memory_path):
698
- with FileLock(lock_path, timeout=30):
699
- with open(memory_path, "r", encoding="utf-8") as f:
700
- _memory = json.load(f)
701
- # clear memory
702
- memory.clear()
703
- memory.update(_memory)
704
- return memory
705
-
706
- def get_memory():
707
- return load_memory()
708
-
709
-
710
- from autocoder.common.command_completer_v2 import CommandCompleterV2
711
- completer = CommandCompleterV2(commands,
712
- file_system_model=CCFileSystemModel(project_root=project_root,
713
- defaut_exclude_dirs=defaut_exclude_dirs,
714
- get_all_file_names_in_project=get_all_file_names_in_project,
715
- get_all_file_in_project=get_all_file_in_project,
716
- get_all_dir_names_in_project=get_all_dir_names_in_project,
717
- get_all_file_in_project_with_dot=get_all_file_in_project_with_dot,
718
- get_symbol_list=get_symbol_list
719
- ),
720
- memory_model=CCMemoryModel(get_memory_func=get_memory,
721
- save_memory_func=save_memory))
722
686
  def revert():
723
687
  result_manager = ResultManager()
724
688
  last_yaml_file = get_last_yaml_file("actions")
@@ -728,57 +692,36 @@ def revert():
728
692
  with redirect_stdout() as output:
729
693
  auto_coder_main(["revert", "--file", file_path])
730
694
  s = output.getvalue()
731
-
695
+
732
696
  console = Console()
733
697
  panel = Panel(
734
698
  Markdown(s),
735
699
  title="Revert Result",
736
700
  border_style="green" if "Successfully reverted changes" in s else "red",
737
701
  padding=(1, 2),
738
- expand=False
702
+ expand=False,
739
703
  )
740
704
  console.print(panel)
741
-
705
+
742
706
  if "Successfully reverted changes" in s:
743
- result_manager.append(content=s, meta={"action": "revert","success":False, "input":{
744
- }})
707
+ result_manager.append(
708
+ content=s, meta={"action": "revert", "success": False, "input": {}}
709
+ )
745
710
  else:
746
- result_manager.append(content=s, meta={"action": "revert","success":False, "input":{
747
- }})
711
+ result_manager.append(
712
+ content=s, meta={"action": "revert", "success": False, "input": {}}
713
+ )
748
714
  else:
749
- result_manager.append(content="No previous chat action found to revert.", meta={"action": "revert","success":False, "input":{
750
- }})
751
-
752
-
753
- def add_files(args: List[str]):
754
-
755
- result_manager = ResultManager()
756
- if "groups" not in memory["current_files"]:
757
- memory["current_files"]["groups"] = {}
758
- if "groups_info" not in memory["current_files"]:
759
- memory["current_files"]["groups_info"] = {}
760
- if "current_groups" not in memory["current_files"]:
761
- memory["current_files"]["current_groups"] = []
762
- groups = memory["current_files"]["groups"]
763
- groups_info = memory["current_files"]["groups_info"]
764
-
765
- console = Console()
766
- printer = Printer()
767
-
768
- if not args:
769
- printer.print_in_terminal("add_files_no_args", style="red")
770
- result_manager.append(content=printer.get_message_from_key("add_files_no_args"),
771
- meta={"action": "add_files","success":False, "input":{ "args": args}})
772
- return
773
-
774
- if args[0] == "/refresh":
775
- completer.refresh_files()
715
+ result_manager.append(
716
+ content="No previous chat action found to revert.",
717
+ meta={"action": "revert", "success": False, "input": {}},
718
+ )
776
719
 
777
720
 
778
721
  def _handle_post_commit_and_pr(post_commit: bool, pr: bool, query: str, args, llm):
779
722
  """
780
723
  处理 post_commit 和 PR 功能
781
-
724
+
782
725
  Args:
783
726
  post_commit: 是否执行 post_commit
784
727
  pr: 是否创建 PR
@@ -787,13 +730,11 @@ def _handle_post_commit_and_pr(post_commit: bool, pr: bool, query: str, args, ll
787
730
  llm: LLM 实例
788
731
  """
789
732
  printer = Printer()
790
- console = Console()
791
-
792
733
  try:
793
734
  if post_commit:
794
735
  # 执行 post_commit 操作
795
736
  printer.print_in_terminal("post_commit_executing", style="blue")
796
-
737
+
797
738
  # 检查是否有未提交的更改
798
739
  uncommitted_changes = git_utils.get_uncommitted_changes(".")
799
740
  if uncommitted_changes:
@@ -801,102 +742,120 @@ def _handle_post_commit_and_pr(post_commit: bool, pr: bool, query: str, args, ll
801
742
  commit_message = git_utils.generate_commit_message.with_llm(llm).run(
802
743
  uncommitted_changes
803
744
  )
804
-
745
+
805
746
  # 执行提交
806
747
  commit_result = git_utils.commit_changes(".", commit_message)
807
748
  git_utils.print_commit_info(commit_result=commit_result)
808
- printer.print_in_terminal("post_commit_success", style="green", message=commit_message)
809
-
749
+ printer.print_in_terminal(
750
+ "post_commit_success", style="green", message=commit_message
751
+ )
752
+
810
753
  # 如果需要创建 PR,则继续处理
811
754
  if pr:
812
755
  _create_pull_request(commit_result, query, llm)
813
756
  else:
814
757
  printer.print_in_terminal("post_commit_no_changes", style="yellow")
815
-
758
+
816
759
  elif pr:
817
760
  # 只创建 PR,不执行 post_commit
818
761
  # 获取最后一个 commit
819
762
  try:
820
763
  repo = git.Repo(".")
821
764
  last_commit = repo.head.commit
822
-
765
+
823
766
  # 创建一个模拟的 commit_result 对象
824
767
  class MockCommitResult:
825
768
  def __init__(self, commit):
826
769
  self.commit_hash = commit.hexsha
827
770
  self.commit_message = commit.message.strip()
828
771
  self.changed_files = []
829
-
772
+
830
773
  mock_commit_result = MockCommitResult(last_commit)
831
774
  _create_pull_request(mock_commit_result, query, llm)
832
-
775
+
833
776
  except Exception as e:
834
- printer.print_in_terminal("pr_get_last_commit_failed", style="red", error=str(e))
835
-
777
+ printer.print_in_terminal(
778
+ "pr_get_last_commit_failed", style="red", error=str(e)
779
+ )
780
+
836
781
  except Exception as e:
837
782
  printer.print_in_terminal("post_commit_pr_failed", style="red", error=str(e))
838
783
 
839
- def init_project_if_required(target_dir: str,project_type:str):
784
+
785
+ def init_project_if_required(target_dir: str, project_type: str):
840
786
  """
841
787
  如果项目没有初始化,则自动初始化项目
842
-
788
+
843
789
  Args:
844
790
  target_dir: 目标目录路径
845
- """
846
-
791
+ """
792
+
847
793
  # 确保目标目录是绝对路径
848
794
  if not os.path.isabs(target_dir):
849
795
  target_dir = os.path.abspath(target_dir)
850
-
796
+
851
797
  actions_dir = os.path.join(target_dir, "actions")
852
798
  auto_coder_dir = os.path.join(target_dir, ".auto-coder")
853
-
799
+
854
800
  # 检查是否已经初始化
855
801
  if os.path.exists(actions_dir) and os.path.exists(auto_coder_dir):
856
802
  return # 已经初始化,无需再次初始化
857
-
803
+
858
804
  printer = Printer()
859
-
860
- try:
805
+
806
+ try:
861
807
  # 创建必要的目录
862
808
  os.makedirs(actions_dir, exist_ok=True)
863
809
  os.makedirs(auto_coder_dir, exist_ok=True)
864
-
810
+
865
811
  # 导入并使用 create_actions 创建默认的 action 文件
866
812
  from autocoder.common.command_templates import create_actions
867
-
813
+
868
814
  create_actions(
869
815
  source_dir=target_dir,
870
- params={
871
- "project_type": project_type,
872
- "source_dir": target_dir
873
- },
816
+ params={"project_type": project_type, "source_dir": target_dir},
874
817
  )
875
-
818
+
876
819
  # 初始化 git 仓库
877
820
  try:
878
821
  git_utils.init(target_dir)
879
822
  except Exception as e:
880
823
  global_logger.warning(f"Failed to initialize git repository: {e}")
881
-
824
+
882
825
  # 创建或更新 .gitignore 文件
883
826
  gitignore_path = os.path.join(target_dir, ".gitignore")
884
- gitignore_entries = ["\n.auto-coder/", "\n/actions/", "\n/output.txt"]
885
-
827
+ gitignore_entries = [
828
+ ".auto-coder/",
829
+ "/actions/",
830
+ "/output.txt",
831
+ ".autocoderrules",
832
+ ".autocodertools",
833
+ ".autocodercommands",
834
+ ".autocoderagents",
835
+ ".autocoderlinters",
836
+ ]
837
+
886
838
  try:
887
- # 检查现有的 .gitignore 内容,避免重复添加
888
- existing_content = ""
839
+ # 读取现有的 .gitignore 内容
840
+ existing_entries = set()
889
841
  if os.path.exists(gitignore_path):
890
842
  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)
843
+ # 将现有内容按行分割并去除空白字符,转换为集合以便快速查找
844
+ existing_entries = {line.strip() for line in f if line.strip()}
845
+
846
+ # 筛选出需要添加的新条目
847
+ new_entries = [
848
+ entry for entry in gitignore_entries if entry not in existing_entries
849
+ ]
850
+
851
+ # 如果有新条目需要添加,则写入文件
852
+ if new_entries:
853
+ with open(gitignore_path, "a", encoding="utf-8") as f:
854
+ for entry in new_entries:
855
+ f.write(f"\n{entry}")
897
856
  except Exception as e:
898
857
  global_logger.warning(f"Failed to update .gitignore: {e}")
899
-
858
+
900
859
  # 创建 .autocoderignore 文件
901
860
  try:
902
861
  autocoderignore_path = os.path.join(target_dir, ".autocoderignore")
@@ -907,9 +866,11 @@ def init_project_if_required(target_dir: str,project_type:str):
907
866
  except Exception as e:
908
867
  global_logger.warning(f"Failed to create .autocoderignore: {e}")
909
868
 
910
- configure(f"project_type:{project_type}")
911
- global_logger.info(f"Successfully initialized auto-coder project in {target_dir}")
912
-
869
+ configure(f"project_type:{project_type}", skip_print=True)
870
+ global_logger.info(
871
+ f"Successfully initialized auto-coder project in {target_dir}"
872
+ )
873
+
913
874
  except Exception as e:
914
875
  global_logger.error(f"Failed to initialize project in {target_dir}: {e}")
915
876
  printer.print_in_terminal("init_project_error", style="red", error=str(e))
@@ -918,7 +879,7 @@ def init_project_if_required(target_dir: str,project_type:str):
918
879
  def _create_pull_request(commit_result, original_query: str, llm):
919
880
  """
920
881
  创建 Pull Request
921
-
882
+
922
883
  Args:
923
884
  commit_result: 提交结果对象
924
885
  original_query: 原始查询
@@ -926,76 +887,92 @@ def _create_pull_request(commit_result, original_query: str, llm):
926
887
  """
927
888
  printer = Printer()
928
889
  console = Console()
929
-
890
+
930
891
  try:
931
892
  # 检查是否安装了 gh CLI
932
893
  gh_check = subprocess.run(["gh", "--version"], capture_output=True, text=True)
933
894
  if gh_check.returncode != 0:
934
895
  printer.print_in_terminal("pr_gh_not_installed", style="red")
935
896
  return
936
-
897
+
937
898
  # 检查是否已经登录 GitHub
938
- auth_check = subprocess.run(["gh", "auth", "status"], capture_output=True, text=True)
899
+ auth_check = subprocess.run(
900
+ ["gh", "auth", "status"], capture_output=True, text=True
901
+ )
939
902
  if auth_check.returncode != 0:
940
903
  printer.print_in_terminal("pr_gh_not_authenticated", style="red")
941
904
  return
942
-
905
+
943
906
  # 获取当前分支名
944
907
  repo = git.Repo(".")
945
908
  current_branch = repo.active_branch.name
946
-
909
+
947
910
  # 如果在 main/master 分支,创建新分支
948
911
  if current_branch in ["main", "master"]:
949
912
  # 生成新分支名
950
913
  import re
951
- branch_name = re.sub(r'[^a-zA-Z0-9\-_]', '-', original_query.lower())
914
+
915
+ branch_name = re.sub(r"[^a-zA-Z0-9\-_]", "-", original_query.lower())
952
916
  branch_name = f"auto-coder-{branch_name[:30]}-{int(time.time())}"
953
-
917
+
954
918
  # 创建并切换到新分支
955
919
  new_branch = repo.create_head(branch_name)
956
920
  new_branch.checkout()
957
921
  current_branch = branch_name
958
-
959
- printer.print_in_terminal("pr_created_branch", style="blue", branch=branch_name)
960
-
922
+
923
+ printer.print_in_terminal(
924
+ "pr_created_branch", style="blue", branch=branch_name
925
+ )
926
+
961
927
  # 推送当前分支到远程
962
928
  try:
963
929
  origin = repo.remotes.origin
964
930
  origin.push(current_branch)
965
- printer.print_in_terminal("pr_pushed_branch", style="blue", branch=current_branch)
931
+ printer.print_in_terminal(
932
+ "pr_pushed_branch", style="blue", branch=current_branch
933
+ )
966
934
  except Exception as e:
967
935
  printer.print_in_terminal("pr_push_failed", style="red", error=str(e))
968
936
  return
969
-
937
+
970
938
  # 生成 PR 标题和描述
971
939
  pr_title, pr_body = _generate_pr_content(commit_result, original_query, llm)
972
-
940
+
973
941
  # 创建 PR
974
942
  pr_cmd = [
975
- "gh", "pr", "create",
976
- "--title", pr_title,
977
- "--body", pr_body,
978
- "--head", current_branch
943
+ "gh",
944
+ "pr",
945
+ "create",
946
+ "--title",
947
+ pr_title,
948
+ "--body",
949
+ pr_body,
950
+ "--head",
951
+ current_branch,
979
952
  ]
980
-
953
+
981
954
  pr_result = subprocess.run(pr_cmd, capture_output=True, text=True)
982
-
955
+
983
956
  if pr_result.returncode == 0:
984
957
  pr_url = pr_result.stdout.strip()
985
958
  printer.print_in_terminal("pr_created_success", style="green", url=pr_url)
986
-
959
+
987
960
  # 显示 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
- ))
961
+ console.print(
962
+ Panel(
963
+ f"[bold green]Pull Request Created Successfully![/bold green]\n\n"
964
+ f"[bold]Title:[/bold] {pr_title}\n"
965
+ f"[bold]URL:[/bold] {pr_url}\n"
966
+ f"[bold]Branch:[/bold] {current_branch}",
967
+ title="🎉 Pull Request",
968
+ border_style="green",
969
+ )
970
+ )
996
971
  else:
997
- printer.print_in_terminal("pr_creation_failed", style="red", error=pr_result.stderr)
998
-
972
+ printer.print_in_terminal(
973
+ "pr_creation_failed", style="red", error=pr_result.stderr
974
+ )
975
+
999
976
  except Exception as e:
1000
977
  printer.print_in_terminal("pr_creation_error", style="red", error=str(e))
1001
978
 
@@ -1004,22 +981,22 @@ def _create_pull_request(commit_result, original_query: str, llm):
1004
981
  def _generate_pr_content(commit_result, original_query: str, llm) -> tuple:
1005
982
  """
1006
983
  生成 PR 标题和描述
1007
-
984
+
1008
985
  根据提交信息和原始查询生成合适的 PR 标题和描述。
1009
-
986
+
1010
987
  Args:
1011
988
  commit_result: 提交结果,包含 commit_message 和 changed_files
1012
989
  original_query: 用户的原始查询请求
1013
-
990
+
1014
991
  Returns:
1015
992
  tuple: (pr_title, pr_body) PR标题和描述内容
1016
-
993
+
1017
994
  请生成简洁明了的 PR 标题(不超过72字符)和详细的描述内容。
1018
995
  标题应该概括主要变更,描述应该包含:
1019
996
  1. 变更的背景和目的
1020
997
  2. 主要修改内容
1021
998
  3. 影响的文件(如果有的话)
1022
-
999
+
1023
1000
  提交信息:{{ commit_result.commit_message }}
1024
1001
  原始需求:{{ original_query }}
1025
1002
  {% if commit_result.changed_files %}
@@ -1029,7 +1006,7 @@ def _generate_pr_content(commit_result, original_query: str, llm) -> tuple:
1029
1006
  {% endfor %}
1030
1007
  {% endif %}
1031
1008
  """
1032
-
1009
+
1033
1010
  # 这个函数会被 byzerllm 装饰器处理,返回 LLM 生成的内容
1034
1011
  # 实际实现会在运行时由装饰器处理
1035
1012
  pass
@@ -1057,14 +1034,14 @@ def _generate_pr_content(commit_result, original_query: str, llm):
1057
1034
  TITLE: [标题内容]
1058
1035
  BODY: [描述内容]
1059
1036
  """
1060
-
1037
+
1061
1038
  response = llm.chat([{"role": "user", "content": prompt}])
1062
-
1039
+
1063
1040
  # 解析响应
1064
- lines = response.split('\n')
1041
+ lines = response.split("\n")
1065
1042
  title = ""
1066
1043
  body = ""
1067
-
1044
+
1068
1045
  for line in lines:
1069
1046
  if line.startswith("TITLE:"):
1070
1047
  title = line.replace("TITLE:", "").strip()
@@ -1072,15 +1049,15 @@ BODY: [描述内容]
1072
1049
  body = line.replace("BODY:", "").strip()
1073
1050
  elif body: # 如果已经开始收集 body,继续添加后续行
1074
1051
  body += "\n" + line
1075
-
1052
+
1076
1053
  # 如果解析失败,使用默认值
1077
1054
  if not title:
1078
1055
  title = f"Auto-coder: {original_query[:50]}..."
1079
1056
  if not body:
1080
1057
  body = f"This PR was automatically generated by Auto-coder.\n\nOriginal request: {original_query}"
1081
-
1058
+
1082
1059
  return title, body
1083
-
1060
+
1084
1061
  except Exception as e:
1085
1062
  # 如果 LLM 生成失败,使用默认值
1086
1063
  title = f"Auto-coder: {original_query[:50]}..."
@@ -1089,351 +1066,34 @@ BODY: [描述内容]
1089
1066
 
1090
1067
 
1091
1068
  def add_files(args: List[str]):
1069
+ """
1070
+ 处理文件添加命令,使用 AddFilesHandler 进行统一处理
1092
1071
 
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}})
1072
+ Args:
1073
+ args: 命令参数列表
1074
+ """
1075
+ from autocoder.common.file_handler import AddFilesHandler
1364
1076
 
1365
- save_memory()
1077
+ handler = AddFilesHandler()
1078
+ handler.handle_add_files_command(args)
1366
1079
 
1367
1080
 
1368
1081
  def remove_files(file_names: List[str]):
1369
- project_root = os.getcwd()
1370
- printer = Printer()
1371
- result_manager = ResultManager()
1082
+ """
1083
+ 处理文件删除命令,使用 RemoveFilesHandler 进行统一处理
1372
1084
 
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
- ]
1085
+ Args:
1086
+ file_names: 文件名列表或模式列表
1087
+ """
1088
+ from autocoder.common.file_handler import RemoveFilesHandler
1413
1089
 
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))
1090
+ handler = RemoveFilesHandler()
1091
+ handler.handle_remove_files_command(file_names)
1421
1092
 
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
1093
 
1434
- @run_in_raw_thread()
1435
1094
  def ask(query: str):
1436
- conf = memory.get("conf", {})
1095
+ memory_manager = get_memory_manager()
1096
+ conf = memory_manager.get_all_config()
1437
1097
  yaml_config = {
1438
1098
  "include_file": ["./base/base.yml"],
1439
1099
  }
@@ -1461,7 +1121,7 @@ def ask(query: str):
1461
1121
 
1462
1122
  execute_file = os.path.join("actions", f"{uuid.uuid4()}.yml")
1463
1123
 
1464
- with open(os.path.join(execute_file), "w",encoding="utf-8") as f:
1124
+ with open(os.path.join(execute_file), "w", encoding="utf-8") as f:
1465
1125
  f.write(yaml_content)
1466
1126
 
1467
1127
  def execute_ask():
@@ -1476,50 +1136,27 @@ def ask(query: str):
1476
1136
  def get_llm_friendly_package_docs(
1477
1137
  package_name: Optional[str] = None, return_paths: bool = False
1478
1138
  ) -> 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
1139
+ """
1140
+ Get LLM friendly package documentation using the new AC module system
1141
+
1142
+ Args:
1143
+ package_name: Specific package name to get docs for, None for all packages
1144
+ return_paths: If True, return file paths; if False, return file contents
1145
+
1146
+ Returns:
1147
+ List of documentation content or file paths
1148
+ """
1149
+ from autocoder.common.llm_friendly_package import get_package_manager
1150
+
1151
+ package_manager = get_package_manager()
1152
+ return package_manager.get_docs(package_name, return_paths)
1516
1153
 
1517
1154
 
1518
1155
  def convert_yaml_to_config(yaml_file: str):
1519
1156
  from autocoder.auto_coder import AutoCoderArgs, load_include_files, Template
1520
1157
 
1521
1158
  args = AutoCoderArgs()
1522
- with open(yaml_file, "r",encoding="utf-8") as f:
1159
+ with open(yaml_file, "r", encoding="utf-8") as f:
1523
1160
  config = yaml.safe_load(f)
1524
1161
  config = load_include_files(config, yaml_file)
1525
1162
  for key, value in config.items():
@@ -1531,150 +1168,22 @@ def convert_yaml_to_config(yaml_file: str):
1531
1168
  setattr(args, key, value)
1532
1169
  return args
1533
1170
 
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
1171
 
1659
- # Format response as markdown
1660
- markdown_content = response.result
1172
+ def mcp(query: str):
1173
+ """
1174
+ 处理MCP命令,使用 McpHandler 进行统一处理
1661
1175
 
1662
- # Save to file
1663
- with open(file_path, "w", encoding="utf-8") as f:
1664
- f.write(markdown_content)
1176
+ Args:
1177
+ query: 查询字符串
1178
+ """
1179
+ from autocoder.common.file_handler import McpHandler
1665
1180
 
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
- )
1181
+ handler = McpHandler()
1182
+ handler.handle_mcp_command(query)
1674
1183
 
1675
1184
 
1676
- @run_in_raw_thread()
1677
1185
  def code_next(query: str):
1186
+ memory = get_current_memory()
1678
1187
  conf = memory.get("conf", {})
1679
1188
  yaml_config = {
1680
1189
  "include_file": ["./base/base.yml"],
@@ -1683,7 +1192,7 @@ def code_next(query: str):
1683
1192
  "skip_build_index": conf.get("skip_build_index", "true") == "true",
1684
1193
  "skip_confirm": conf.get("skip_confirm", "true") == "true",
1685
1194
  "silence": conf.get("silence", "true") == "true",
1686
- "include_project_structure": conf.get("include_project_structure", "true")
1195
+ "include_project_structure": conf.get("include_project_structure", "false")
1687
1196
  == "true",
1688
1197
  "exclude_files": memory.get("exclude_files", []),
1689
1198
  }
@@ -1694,7 +1203,7 @@ def code_next(query: str):
1694
1203
 
1695
1204
  temp_yaml = os.path.join("actions", f"{uuid.uuid4()}.yml")
1696
1205
  try:
1697
- with open(temp_yaml, "w",encoding="utf-8") as f:
1206
+ with open(temp_yaml, "w", encoding="utf-8") as f:
1698
1207
  f.write(convert_yaml_config_to_str(yaml_config=yaml_config))
1699
1208
  args = convert_yaml_to_config(temp_yaml)
1700
1209
  finally:
@@ -1704,8 +1213,7 @@ def code_next(query: str):
1704
1213
  product_mode = conf.get("product_mode", "lite")
1705
1214
  llm = get_single_llm(args.chat_model or args.model, product_mode=product_mode)
1706
1215
 
1707
- auto_guesser = AutoGuessQuery(
1708
- llm=llm, project_dir=os.getcwd(), skip_diff=True)
1216
+ auto_guesser = AutoGuessQuery(llm=llm, project_dir=os.getcwd(), skip_diff=True)
1709
1217
 
1710
1218
  predicted_tasks = auto_guesser.predict_next_tasks(
1711
1219
  5, is_human_as_model=args.human_as_model
@@ -1719,25 +1227,20 @@ def code_next(query: str):
1719
1227
  console = Console()
1720
1228
 
1721
1229
  # Create main panel for all predicted tasks
1722
- table = Table(show_header=True,
1723
- header_style="bold magenta", show_lines=True)
1230
+ table = Table(show_header=True, header_style="bold magenta", show_lines=True)
1724
1231
  table.add_column("Priority", style="cyan", width=8)
1725
- table.add_column("Task Description", style="green",
1726
- width=40, overflow="fold")
1232
+ table.add_column("Task Description", style="green", width=40, overflow="fold")
1727
1233
  table.add_column("Files", style="yellow", width=30, overflow="fold")
1728
1234
  table.add_column("Reason", style="blue", width=30, overflow="fold")
1729
- table.add_column("Dependencies", style="magenta",
1730
- width=30, overflow="fold")
1235
+ table.add_column("Dependencies", style="magenta", width=30, overflow="fold")
1731
1236
 
1732
1237
  for task in predicted_tasks:
1733
1238
  # Format file paths to be more readable
1734
- file_list = "\n".join([os.path.relpath(f, os.getcwd())
1735
- for f in task.urls])
1239
+ file_list = "\n".join([os.path.relpath(f, os.getcwd()) for f in task.urls])
1736
1240
 
1737
1241
  # Format dependencies to be more readable
1738
1242
  dependencies = (
1739
- "\n".join(
1740
- task.dependency_queries) if task.dependency_queries else "None"
1243
+ "\n".join(task.dependency_queries) if task.dependency_queries else "None"
1741
1244
  )
1742
1245
 
1743
1246
  table.add_row(
@@ -1754,260 +1257,42 @@ def code_next(query: str):
1754
1257
  )
1755
1258
 
1756
1259
 
1757
- @run_in_raw_thread()
1758
1260
  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
1261
+ """
1262
+ 处理提交命令,使用 CommitHandler 进行统一处理
1921
1263
 
1922
- yaml_config["urls"] = current_files + get_llm_friendly_package_docs(
1923
- return_paths=True
1924
- )
1264
+ Args:
1265
+ query: 可选的提交消息或命令
1266
+ """
1267
+ from autocoder.common.file_handler import CommitHandler
1925
1268
 
1926
- if conf.get("enable_global_memory", "false") in ["true", "True",True]:
1927
- yaml_config["urls"] += get_global_memory_file_paths()
1269
+ handler = CommitHandler()
1270
+ handler.handle_commit_command(query)
1928
1271
 
1929
- # handle image
1930
- v = Image.convert_image_paths_from(query)
1931
- yaml_config["query"] = v
1932
1272
 
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
- )
1273
+ def coding(query: str, cancel_token=None):
1274
+ """
1275
+ 处理代码生成命令,使用 CodingHandler 进行统一处理
1962
1276
 
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)
1277
+ Args:
1278
+ query: 代码生成查询字符串
1279
+ cancel_token: 可选的取消令牌
1280
+ """
1281
+ from autocoder.common.file_handler import CodingHandler
1985
1282
 
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)
1283
+ handler = CodingHandler()
1284
+ handler.handle_coding_command(query, cancel_token)
1989
1285
 
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
1286
 
1998
- execute_chat()
1999
- else:
2000
- print("Failed to create new YAML file.")
1287
+ def rules(query: str):
1288
+ from autocoder.chat.rules_command import handle_rules_command
2001
1289
 
2002
- save_memory()
1290
+ result = handle_rules_command(query, coding_func=coding)
1291
+ # 只有当结果不为空时才打印,避免重复输出
1292
+ if result and result.strip():
1293
+ print(result)
2003
1294
  completer.refresh_files()
2004
1295
 
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
1296
 
2012
1297
  @byzerllm.prompt()
2013
1298
  def code_review(query: str) -> str:
@@ -2025,95 +1310,32 @@ def code_review(query: str) -> str:
2025
1310
  如果用户的需求包含了@一个文件名 或者 @@符号, 那么重点关注这些文件或者符号(函数,类)进行上述的review。
2026
1311
  review 过程中严格遵循上述的检查点,不要遗漏,没有发现异常的点直接跳过,只对发现的异常点,给出具体的修改后的代码。
2027
1312
  """
1313
+ return {} # type: ignore
2028
1314
 
2029
1315
 
2030
- @run_in_raw_thread()
2031
1316
  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)
1317
+ """
1318
+ 处理聊天命令,使用 ChatHandler 进行统一处理
2094
1319
 
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)
1320
+ Args:
1321
+ query: 聊天查询字符串
1322
+ """
1323
+ from autocoder.common.file_handler import ChatHandler
2100
1324
 
2101
- try:
2102
- execute_ask()
2103
- finally:
2104
- os.remove(execute_file)
1325
+ handler = ChatHandler()
1326
+ handler.handle_chat_command(query)
2105
1327
 
2106
1328
 
2107
- @run_in_raw_thread()
2108
1329
  def summon(query: str):
1330
+ memory = get_current_memory()
2109
1331
  conf = memory.get("conf", {})
2110
- current_files = memory["current_files"]["files"]
1332
+ current_files = get_current_files()
2111
1333
 
2112
1334
  file_contents = []
2113
1335
  for file in current_files:
2114
1336
  if os.path.exists(file):
2115
1337
  try:
2116
- with open(file, "r",encoding="utf-8") as f:
1338
+ with open(file, "r", encoding="utf-8") as f:
2117
1339
  content = f.read()
2118
1340
  s = f"##File: {file}\n{content}\n\n"
2119
1341
  file_contents.append(s)
@@ -2149,7 +1371,7 @@ def summon(query: str):
2149
1371
 
2150
1372
  execute_file = os.path.join("actions", f"{uuid.uuid4()}.yml")
2151
1373
 
2152
- with open(os.path.join(execute_file), "w",encoding="utf-8") as f:
1374
+ with open(os.path.join(execute_file), "w", encoding="utf-8") as f:
2153
1375
  f.write(yaml_content)
2154
1376
 
2155
1377
  def execute_summon():
@@ -2161,9 +1383,8 @@ def summon(query: str):
2161
1383
  os.remove(execute_file)
2162
1384
 
2163
1385
 
2164
- @run_in_raw_thread()
2165
1386
  def design(query: str):
2166
-
1387
+ memory = get_current_memory()
2167
1388
  conf = memory.get("conf", {})
2168
1389
  yaml_config = {
2169
1390
  "include_file": ["./base/base.yml"],
@@ -2196,7 +1417,7 @@ def design(query: str):
2196
1417
 
2197
1418
  execute_file = os.path.join("actions", f"{uuid.uuid4()}.yml")
2198
1419
 
2199
- with open(os.path.join(execute_file), "w",encoding="utf-8") as f:
1420
+ with open(os.path.join(execute_file), "w", encoding="utf-8") as f:
2200
1421
  f.write(yaml_content)
2201
1422
 
2202
1423
  def execute_design():
@@ -2208,117 +1429,21 @@ def design(query: str):
2208
1429
  os.remove(execute_file)
2209
1430
 
2210
1431
 
2211
- @run_in_raw_thread()
2212
1432
  def active_context(query: str):
2213
1433
  """
2214
- 管理活动上下文任务,支持列表、查询等操作
2215
-
1434
+ 处理活动上下文命令,使用 ActiveContextHandler 进行统一处理
1435
+
2216
1436
  Args:
2217
1437
  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)
1438
+ """
1439
+ from autocoder.common.file_handler import ActiveContextHandler
1440
+
1441
+ handler = ActiveContextHandler()
1442
+ handler.handle_active_context_command(query)
2319
1443
 
2320
1444
 
2321
1445
  def voice_input():
1446
+ memory = get_current_memory()
2322
1447
  conf = memory.get("conf", {})
2323
1448
  yaml_config = {
2324
1449
  "include_file": ["./base/base.yml"],
@@ -2335,7 +1460,7 @@ def voice_input():
2335
1460
 
2336
1461
  execute_file = os.path.join("actions", f"{uuid.uuid4()}.yml")
2337
1462
 
2338
- with open(os.path.join(execute_file), "w",encoding="utf-8") as f:
1463
+ with open(os.path.join(execute_file), "w", encoding="utf-8") as f:
2339
1464
  f.write(yaml_content)
2340
1465
 
2341
1466
  def execute_voice2text_command():
@@ -2343,14 +1468,16 @@ def voice_input():
2343
1468
 
2344
1469
  try:
2345
1470
  execute_voice2text_command()
2346
- with open(os.path.join(".auto-coder", "exchange.txt"), "r",encoding="utf-8") as f:
1471
+ with open(
1472
+ os.path.join(".auto-coder", "exchange.txt"), "r", encoding="utf-8"
1473
+ ) as f:
2347
1474
  return f.read()
2348
1475
  finally:
2349
1476
  os.remove(execute_file)
2350
1477
 
2351
1478
 
2352
- @run_in_raw_thread()
2353
1479
  def generate_shell_command(input_text):
1480
+ memory = get_current_memory()
2354
1481
  conf = memory.get("conf", {})
2355
1482
  yaml_config = {
2356
1483
  "include_file": ["./base/base.yml"],
@@ -2365,428 +1492,58 @@ def generate_shell_command(input_text):
2365
1492
 
2366
1493
  execute_file = os.path.join("actions", f"{uuid.uuid4()}.yml")
2367
1494
 
2368
- with open(os.path.join(execute_file), "w",encoding="utf-8") as f:
1495
+ with open(os.path.join(execute_file), "w", encoding="utf-8") as f:
2369
1496
  f.write(yaml_content)
2370
1497
 
2371
1498
  try:
2372
1499
  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:
1500
+ with open(
1501
+ os.path.join(".auto-coder", "exchange.txt"), "r", encoding="utf-8"
1502
+ ) as f:
2374
1503
  shell_script = f.read()
2375
1504
  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
- })
1505
+ result_manager.add_result(
1506
+ content=shell_script,
1507
+ meta={"action": "generate_shell_command", "input": {"query": input_text}},
1508
+ )
2382
1509
  return shell_script
2383
1510
  finally:
2384
1511
  os.remove(execute_file)
2385
1512
 
1513
+
2386
1514
  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
1515
  """
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
1516
+ 处理模型管理命令,使用 ModelsHandler 进行统一处理
2696
1517
 
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
1518
+ Args:
1519
+ query: 查询字符串,支持多种模型管理子命令
1520
+ """
1521
+ from autocoder.common.file_handler import ModelsHandler
2707
1522
 
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
- }
1523
+ handler = ModelsHandler()
1524
+ handler.handle_models_command(query)
2718
1525
 
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
1526
 
2768
1527
  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])
1528
+ memory_manager = get_memory_manager()
1529
+ new_dirs = memory_manager.add_exclude_dirs(dir_names)
1530
+
1531
+ if new_dirs:
1532
+ print(f"Added exclude dirs: {new_dirs}")
1533
+ exclude_files(",".join([f"regex://.*/{d}/*." for d in new_dirs]))
2779
1534
  else:
2780
1535
  print("All specified dirs are already in the exclude list.")
2781
- save_memory()
2782
1536
  completer.refresh_files()
2783
1537
 
1538
+
2784
1539
  def exclude_files(query: str):
1540
+ memory_manager = get_memory_manager()
2785
1541
  result_manager = ResultManager()
2786
1542
  printer = Printer()
1543
+
2787
1544
  if "/list" in query:
2788
1545
  query = query.replace("/list", "", 1).strip()
2789
- existing_file_patterns = memory.get("exclude_files", [])
1546
+ existing_file_patterns = memory_manager.get_exclude_files()
2790
1547
  console = Console()
2791
1548
  # 打印表格
2792
1549
  table = Table(title="Exclude Files")
@@ -2794,70 +1551,58 @@ def exclude_files(query: str):
2794
1551
  for file_pattern in existing_file_patterns:
2795
1552
  table.add_row(file_pattern)
2796
1553
  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
- })
1554
+ result_manager.add_result(
1555
+ content=f"Exclude files: {existing_file_patterns}",
1556
+ meta={"action": "exclude_files", "input": {"query": query}},
1557
+ )
2803
1558
  return
2804
1559
 
2805
1560
  if "/drop" in query:
2806
1561
  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()
1562
+ removed_patterns = memory_manager.remove_exclude_files([query.strip()])
2811
1563
  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
- })
1564
+ result_manager.add_result(
1565
+ content=f"Dropped exclude files: {removed_patterns}",
1566
+ meta={"action": "exclude_files", "input": {"query": query}},
1567
+ )
2818
1568
  return
2819
-
1569
+
2820
1570
  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}")
1571
+
1572
+ # Validate patterns
1573
+ for file_pattern in new_file_patterns:
1574
+ if not file_pattern.startswith("regex://"):
1575
+ result_manager.add_result(
1576
+ content=printer.get_message_from_key_with_format(
1577
+ "invalid_file_pattern", file_pattern=file_pattern
1578
+ ),
1579
+ meta={"action": "exclude_files", "input": {"query": file_pattern}},
1580
+ )
1581
+ raise ValueError(
1582
+ printer.get_message_from_key_with_format(
1583
+ "invalid_file_pattern", file_pattern=file_pattern
1584
+ )
1585
+ )
1586
+
1587
+ # Add new patterns
1588
+ new_patterns_added = memory_manager.add_exclude_files(new_file_patterns)
1589
+
1590
+ if new_patterns_added:
1591
+ result_manager.add_result(
1592
+ content=f"Added exclude files: {new_patterns_added}",
1593
+ meta={"action": "exclude_files", "input": {"query": new_patterns_added}},
1594
+ )
1595
+ print(f"Added exclude files: {new_patterns_added}")
2848
1596
  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
- })
1597
+ result_manager.add_result(
1598
+ content=f"All specified files are already in the exclude list.",
1599
+ meta={"action": "exclude_files", "input": {"query": new_file_patterns}},
1600
+ )
2855
1601
  print("All specified files are already in the exclude list.")
2856
-
2857
-
2858
1602
 
2859
- @run_in_raw_thread()
1603
+
2860
1604
  def index_build():
1605
+ memory = get_memory()
2861
1606
  conf = memory.get("conf", {})
2862
1607
  yaml_config = {
2863
1608
  "include_file": ["./base/base.yml"],
@@ -2872,17 +1617,18 @@ def index_build():
2872
1617
  yaml_content = convert_yaml_config_to_str(yaml_config=yaml_config)
2873
1618
  yaml_file = os.path.join("actions", f"{uuid.uuid4()}.yml")
2874
1619
 
2875
- with open(yaml_file, "w",encoding="utf-8") as f:
1620
+ with open(yaml_file, "w", encoding="utf-8") as f:
2876
1621
  f.write(yaml_content)
2877
- try:
2878
- auto_coder_main(["index", "--file", yaml_file])
1622
+ try:
1623
+ auto_coder_main(["index", "--file", yaml_file])
2879
1624
  completer.refresh_files()
2880
1625
  finally:
2881
1626
  os.remove(yaml_file)
2882
1627
 
2883
1628
 
2884
- def get_final_config()->AutoCoderArgs:
2885
- conf = memory.get("conf", {})
1629
+ def get_final_config() -> AutoCoderArgs:
1630
+ memory_manager = get_memory_manager()
1631
+ conf = memory_manager.get_all_config()
2886
1632
  yaml_config = {
2887
1633
  "include_file": ["./base/base.yml"],
2888
1634
  "auto_merge": conf.get("auto_merge", "editblock"),
@@ -2890,9 +1636,9 @@ def get_final_config()->AutoCoderArgs:
2890
1636
  "skip_build_index": conf.get("skip_build_index", "true") == "true",
2891
1637
  "skip_confirm": conf.get("skip_confirm", "true") == "true",
2892
1638
  "silence": conf.get("silence", "true") == "true",
2893
- "include_project_structure": conf.get("include_project_structure", "true")
1639
+ "include_project_structure": conf.get("include_project_structure", "false")
2894
1640
  == "true",
2895
- "exclude_files": memory.get("exclude_files", []),
1641
+ "exclude_files": memory_manager.get_exclude_files(),
2896
1642
  }
2897
1643
  for key, value in conf.items():
2898
1644
  converted_value = convert_config_value(key, value)
@@ -2901,7 +1647,7 @@ def get_final_config()->AutoCoderArgs:
2901
1647
 
2902
1648
  temp_yaml = os.path.join("actions", f"{uuid.uuid4()}.yml")
2903
1649
  try:
2904
- with open(temp_yaml, "w",encoding="utf-8") as f:
1650
+ with open(temp_yaml, "w", encoding="utf-8") as f:
2905
1651
  f.write(convert_yaml_config_to_str(yaml_config=yaml_config))
2906
1652
  args = convert_yaml_to_config(temp_yaml)
2907
1653
  finally:
@@ -2909,18 +1655,31 @@ def get_final_config()->AutoCoderArgs:
2909
1655
  os.remove(temp_yaml)
2910
1656
  return args
2911
1657
 
1658
+
2912
1659
  def help(query: str):
2913
- from autocoder.common.auto_configure import ConfigAutoTuner,MemoryConfig,AutoConfigRequest
1660
+ from autocoder.common.auto_configure import (
1661
+ ConfigAutoTuner,
1662
+ MemoryConfig,
1663
+ AutoConfigRequest,
1664
+ )
1665
+
1666
+ memory_manager = get_memory_manager()
1667
+ memory = get_memory()
2914
1668
  args = get_final_config()
2915
- product_mode = memory.get("product_mode", "lite")
1669
+ product_mode = memory_manager.get_config("product_mode", "lite")
2916
1670
  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))
1671
+ auto_config_tuner = ConfigAutoTuner(
1672
+ args=args,
1673
+ llm=llm,
1674
+ memory_config=MemoryConfig(memory=memory, save_memory_func=save_memory),
1675
+ )
1676
+ auto_config_tuner.tune(AutoConfigRequest(query=query))
1677
+
2919
1678
 
2920
- @run_in_raw_thread()
2921
1679
  def index_export(path: str):
2922
1680
  from autocoder.common.index_import_export import export_index
2923
1681
  from autocoder.common.printer import Printer
1682
+
2924
1683
  printer = Printer()
2925
1684
  project_root = os.getcwd()
2926
1685
  if export_index(project_root, path):
@@ -2928,10 +1687,11 @@ def index_export(path: str):
2928
1687
  else:
2929
1688
  printer.print_in_terminal("index_export_fail", path=path)
2930
1689
 
2931
- @run_in_raw_thread()
1690
+
2932
1691
  def index_import(path: str):
2933
1692
  from autocoder.common.index_import_export import import_index
2934
1693
  from autocoder.common.printer import Printer
1694
+
2935
1695
  printer = Printer()
2936
1696
  project_root = os.getcwd()
2937
1697
  if import_index(project_root, path):
@@ -2939,7 +1699,7 @@ def index_import(path: str):
2939
1699
  else:
2940
1700
  printer.print_in_terminal("index_import_fail", path=path)
2941
1701
 
2942
- @run_in_raw_thread()
1702
+
2943
1703
  def index_query(query: str):
2944
1704
  from autocoder.index.entry import build_index_and_filter_files
2945
1705
  from autocoder.pyproject import PyProject
@@ -2949,7 +1709,9 @@ def index_query(query: str):
2949
1709
  config = get_final_config()
2950
1710
  config.query = query
2951
1711
  config.skip_filter_index = False
2952
- llm = get_single_llm(config.chat_model or config.model, product_mode=config.product_mode)
1712
+ llm = get_single_llm(
1713
+ config.chat_model or config.model, product_mode=config.product_mode
1714
+ )
2953
1715
 
2954
1716
  if config.project_type == "ts":
2955
1717
  pp = TSProject(args=config, llm=llm)
@@ -2958,264 +1720,79 @@ def index_query(query: str):
2958
1720
  else:
2959
1721
  pp = SuffixProject(args=config, llm=llm, file_filter=None)
2960
1722
  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
1723
+ sources = pp.sources
1724
+ source_code_list = build_index_and_filter_files(
1725
+ llm=llm, args=config, sources=sources
1726
+ )
1727
+ return source_code_list
1728
+
2964
1729
 
2965
1730
  def list_files():
2966
- console = Console()
2967
- project_root = os.getcwd()
2968
- current_files = memory["current_files"]["files"]
1731
+ """
1732
+ 处理文件列表命令,使用 ListFilesHandler 进行统一处理
1733
+ """
1734
+ from autocoder.common.file_handler import ListFilesHandler
2969
1735
 
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
- )
1736
+ handler = ListFilesHandler()
1737
+ handler.handle_list_files_command()
2986
1738
 
2987
1739
 
2988
1740
  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
1741
  printer = Printer()
2994
1742
  console = Console()
2995
1743
  # Generate the shell script
2996
- shell_script = generate_shell_command(query)
1744
+ shell_script = generate_shell_command(query)
2997
1745
 
2998
1746
  # Ask for confirmation using rich
2999
1747
  if Confirm.ask(
3000
- printer.get_message_from_key("confirm_execute_shell_script"),
3001
- default=False
1748
+ printer.get_message_from_key("confirm_execute_shell_script"), default=False
3002
1749
  ):
3003
1750
  execute_shell_command(shell_script)
3004
1751
  else:
3005
1752
  console.print(
3006
1753
  Panel(
3007
1754
  printer.get_message_from_key("shell_script_not_executed"),
3008
- border_style="yellow"
1755
+ border_style="yellow",
3009
1756
  )
3010
1757
  )
3011
1758
 
3012
1759
 
3013
1760
  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
- )
1761
+ """
1762
+ 处理库管理命令,使用 LibHandler 进行统一处理
3180
1763
 
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}")
1764
+ Args:
1765
+ args: 命令参数列表
1766
+ """
1767
+ from autocoder.common.file_handler import LibHandler
3196
1768
 
3197
- else:
3198
- console.print(f"Unknown subcommand: {subcommand}")
1769
+ handler = LibHandler()
1770
+ handler.handle_lib_command(args)
3199
1771
 
3200
1772
 
3201
1773
  def execute_shell_command(command: str):
3202
1774
  from autocoder.common.shells import execute_shell_command as shell_exec
1775
+
3203
1776
  shell_exec(command)
3204
1777
 
3205
1778
 
3206
1779
  def conf_export(path: str):
3207
1780
  from autocoder.common.conf_import_export import export_conf
1781
+
3208
1782
  export_conf(os.getcwd(), path)
3209
1783
 
1784
+
3210
1785
  def conf_import(path: str):
3211
1786
  from autocoder.common.conf_import_export import import_conf
1787
+
3212
1788
  import_conf(os.getcwd(), path)
3213
1789
 
1790
+
3214
1791
  def generate_new_yaml(query: str):
3215
1792
  memory = get_memory()
3216
- conf = memory.get("conf",{})
3217
- current_files = memory.get("current_files",{}).get("files",[])
3218
- auto_coder_main(["next", "chat_action"])
1793
+ conf = memory.get("conf", {})
1794
+ current_files = memory.get("current_files", {}).get("files", [])
1795
+ auto_coder_main(["next", "chat_action"])
3219
1796
  latest_yaml_file = get_last_yaml_file("actions")
3220
1797
  if latest_yaml_file:
3221
1798
  yaml_config = {
@@ -3225,11 +1802,11 @@ def generate_new_yaml(query: str):
3225
1802
  "skip_build_index": conf.get("skip_build_index", "true") == "true",
3226
1803
  "skip_confirm": conf.get("skip_confirm", "true") == "true",
3227
1804
  "silence": conf.get("silence", "true") == "true",
3228
- "include_project_structure": conf.get("include_project_structure", "true")
1805
+ "include_project_structure": conf.get("include_project_structure", "false")
3229
1806
  == "true",
3230
1807
  "exclude_files": memory.get("exclude_files", []),
3231
1808
  }
3232
- yaml_config["context"] = ""
1809
+ yaml_config["context"] = ""
3233
1810
  for key, value in conf.items():
3234
1811
  converted_value = convert_config_value(key, value)
3235
1812
  if converted_value is not None:
@@ -3240,310 +1817,356 @@ def generate_new_yaml(query: str):
3240
1817
  )
3241
1818
  # handle image
3242
1819
  v = Image.convert_image_paths_from(query)
3243
- yaml_config["query"] = v
1820
+ yaml_config["query"] = v
3244
1821
 
3245
- yaml_content = convert_yaml_config_to_str(yaml_config=yaml_config)
1822
+ yaml_content = convert_yaml_config_to_str(yaml_config=yaml_config)
3246
1823
 
3247
1824
  execute_file = os.path.join("actions", latest_yaml_file)
3248
- with open(os.path.join(execute_file), "w",encoding="utf-8") as f:
1825
+ with open(os.path.join(execute_file), "w", encoding="utf-8") as f:
3249
1826
  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",[])
1827
+ return execute_file, convert_yaml_to_config(execute_file)
3272
1828
 
3273
- command_infos = parse_query(query)
3274
1829
 
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:])
1830
+ def handle_conversation_actions(conversation_config) -> bool:
1831
+ """
1832
+ 处理对话列表和创建新对话的操作
3298
1833
 
3299
- if "list" in command_infos:
3300
- conversation_config.action = "list"
1834
+ Args:
1835
+ conversation_config: 对话配置对象
3301
1836
 
3302
- if "command" in command_infos:
3303
- conversation_config.action = "command"
3304
- task_query = render_command_file_with_variables(command_infos)
3305
-
1837
+ Returns:
1838
+ bool: 如果处理了特殊操作(LIST或NEW without input)返回True,否则返回False
1839
+ """
1840
+ if not conversation_config:
1841
+ return False
3306
1842
 
3307
- conversation_config.query = task_query
1843
+ console = Console()
3308
1844
 
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
- )
1845
+ # 处理LIST操作
1846
+ if conversation_config.action == ConversationAction.LIST:
1847
+ conversation_manager = get_conversation_manager()
1848
+ conversations = conversation_manager.list_conversations()
1849
+ # 只保留 conversation_id 和 name 字段
1850
+ filtered_conversations = []
1851
+ for conv in conversations:
1852
+ filtered_conv = {
1853
+ "conversation_id": conv.get("conversation_id"),
1854
+ "name": conv.get("name"),
1855
+ }
1856
+ filtered_conversations.append(filtered_conv)
3343
1857
 
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
- ))
1858
+ # 格式化 JSON 输出,使用 JSON 格式渲染而不是 Markdown
1859
+ json_str = json.dumps(filtered_conversations, ensure_ascii=False, indent=4)
1860
+ console.print(
1861
+ Panel(
1862
+ json_str,
1863
+ title="🏁 Task Completion",
1864
+ border_style="green",
1865
+ title_align="left",
1866
+ )
1867
+ )
1868
+ return True
3376
1869
 
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()
1870
+ # 处理NEW操作且没有用户输入
1871
+ if (
1872
+ conversation_config.action == ConversationAction.NEW
1873
+ and not conversation_config.query.strip()
1874
+ ):
1875
+ conversation_manager = get_conversation_manager()
1876
+ conversation_id = conversation_manager.create_conversation(
1877
+ name=conversation_config.query or "New Conversation",
1878
+ description=conversation_config.query or "New Conversation",
1879
+ )
1880
+ conversation_manager.set_current_conversation(conversation_id)
1881
+ conversation_message = f"New conversation created: {conversation_manager.get_current_conversation_id()}"
3389
1882
 
1883
+ # 使用safe console print的简单版本
1884
+ try:
1885
+ console.print(
1886
+ Panel(
1887
+ Markdown(conversation_message),
1888
+ title="🏁 Task Completion",
1889
+ border_style="green",
1890
+ title_align="left",
1891
+ )
1892
+ )
1893
+ except Exception:
1894
+ # fallback to plain text
1895
+ safe_content = conversation_message.replace("[", "\\[").replace("]", "\\]")
1896
+ console.print(
1897
+ Panel(
1898
+ safe_content,
1899
+ title="🏁 Task Completion",
1900
+ border_style="green",
1901
+ title_align="left",
1902
+ )
1903
+ )
1904
+ return True
1905
+
1906
+ return False
1907
+
1908
+
1909
+ # used in /auto command in terminal
1910
+ def run_agentic(
1911
+ query: str,
1912
+ cancel_token: Optional[str] = None,
1913
+ conversation_history: Optional[List[Dict[str, Any]]] = None,
1914
+ ):
1915
+ """处理/auto指令"""
1916
+ agentic = RunAgentic()
1917
+ return agentic.run(query, cancel_token, conversation_history)
1918
+
1919
+
1920
+ def run_agentic_filter(query: str, cancel_token: Optional[str] = None):
1921
+ """处理/auto指令"""
1922
+ agentic = RunAgentic()
1923
+ return agentic.filter(query, cancel_token)
1924
+
1925
+
1926
+ # used in autocoder/sdk/core/bridge.py
1927
+ def run_auto_command(
1928
+ query: str,
1929
+ pre_commit: bool = False,
1930
+ post_commit: bool = False,
1931
+ pr: bool = False,
1932
+ extra_args: Dict[str, Any] = {},
1933
+ cancel_token: Optional[str] = None,
1934
+ conversation_history: Optional[List[Dict[str, Any]]] = None,
1935
+ system_prompt: Optional[str] = None,
1936
+ conversation_action: ConversationAction = ConversationAction.NEW,
1937
+ conversation_id: Optional[str] = None,
1938
+ is_sub_agent: bool = False,
1939
+ ):
1940
+ """处理/auto指令"""
1941
+ agentic = RunAgentic()
1942
+ for event in agentic.run_with_events(
1943
+ query,
1944
+ pre_commit=pre_commit,
1945
+ post_commit=post_commit,
1946
+ pr=pr,
1947
+ extra_args=extra_args,
1948
+ cancel_token=cancel_token,
1949
+ conversation_history=conversation_history,
1950
+ system_prompt=system_prompt,
1951
+ conversation_action=conversation_action,
1952
+ conversation_id=conversation_id,
1953
+ is_sub_agent=is_sub_agent,
1954
+ ):
1955
+ yield event
3390
1956
 
3391
1957
 
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",[])
1958
+ # used in auto-coder.web
1959
+ def auto_command(query: str, extra_args: Dict[str, Any] = {}):
1960
+ """处理/auto指令"""
1961
+ args = get_final_config()
1962
+ memory = get_memory()
1963
+ if args.enable_agentic_edit:
1964
+ from autocoder.run_context import get_run_context, RunMode
1965
+
1966
+ execute_file, _ = generate_new_yaml(query)
1967
+ args.file = execute_file
1968
+ current_files = memory.get("current_files", {}).get("files", [])
3406
1969
  sources = []
3407
1970
  for file in current_files:
3408
1971
  try:
3409
- with open(file,"r",encoding="utf-8") as f:
3410
- sources.append(SourceCode(module_name=file,source_code=f.read()))
1972
+ with open(file, "r", encoding="utf-8") as f:
1973
+ sources.append(SourceCode(module_name=file, source_code=f.read()))
3411
1974
  except Exception as e:
3412
1975
  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
1976
 
3417
- command_infos = parse_query(query)
1977
+ try:
1978
+ llm = get_single_llm(
1979
+ args.code_model or args.model, product_mode=args.product_mode
1980
+ )
1981
+ except ValueError as e:
1982
+ console = Console()
1983
+ console.print(
1984
+ Panel(
1985
+ f"[red]LLM Configuration Error:[/red]\n\n{str(e)}",
1986
+ title="[red]Error[/red]",
1987
+ border_style="red",
1988
+ padding=(1, 2),
1989
+ )
1990
+ )
1991
+ return
1992
+ conversation_history = extra_args.get("conversations", [])
1993
+
1994
+ command_infos = parse_query(query)
3418
1995
 
3419
1996
  # terminal 的总是接着上次对话, 所以这里总是设置为 resume
3420
1997
  conversation_config = AgenticEditConversationConfig(
3421
- action="resume"
1998
+ action=ConversationAction.RESUME
3422
1999
  )
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"
2000
+
2001
+ task_query = query
2002
+
2003
+ if "new" in command_infos:
2004
+ conversation_config.action = ConversationAction.NEW
3436
2005
  task_query = " ".join(command_infos["new"]["args"])
3437
-
2006
+
3438
2007
  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:])
2008
+ conversation_config.action = ConversationAction.RESUME
2009
+ conversation_config.conversation_id = command_infos["resume"]["args"][0]
2010
+ task_query = " ".join(command_infos["resume"]["args"][1:])
3442
2011
 
3443
2012
  if "list" in command_infos:
3444
- conversation_config.action = "list"
2013
+ conversation_config.action = ConversationAction.LIST
3445
2014
 
3446
-
3447
2015
  if "command" in command_infos:
3448
- conversation_config.action = "command"
2016
+ conversation_config.action = ConversationAction.COMMAND
3449
2017
  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
-
2018
+
2019
+ conversation_config.query = task_query
2020
+
2021
+ # 处理特殊的conversation操作(LIST和NEW without input)
2022
+ if handle_conversation_actions(conversation_config):
2023
+ return conversation_config.conversation_id
2024
+
2025
+ conversation_manager = get_conversation_manager()
2026
+ if conversation_config.action == ConversationAction.NEW:
2027
+ conversation_id = conversation_manager.create_conversation(
2028
+ name=conversation_config.query or "New Conversation",
2029
+ description=conversation_config.query or "New Conversation",
2030
+ )
2031
+ conversation_manager.set_current_conversation(conversation_id)
2032
+ conversation_config.conversation_id = conversation_id
2033
+
2034
+ if (
2035
+ conversation_config.action == ConversationAction.RESUME
2036
+ and conversation_config.conversation_id
2037
+ ):
2038
+ conversation_manager.set_current_conversation(
2039
+ conversation_config.conversation_id
2040
+ )
2041
+
2042
+ if (
2043
+ conversation_config.action == ConversationAction.RESUME
2044
+ and not conversation_config.conversation_id
2045
+ and conversation_manager.get_current_conversation_id()
2046
+ ):
2047
+ conversation_config.conversation_id = (
2048
+ conversation_manager.get_current_conversation_id()
2049
+ )
2050
+ conversation_manager.set_current_conversation(
2051
+ conversation_config.conversation_id
2052
+ )
2053
+
2054
+ if not conversation_config.conversation_id:
2055
+ conversation_id = conversation_manager.create_conversation(
2056
+ name=conversation_config.query or "New Conversation",
2057
+ description=conversation_config.query or "New Conversation",
2058
+ )
2059
+ conversation_manager.set_current_conversation(conversation_id)
2060
+ conversation_config.conversation_id = conversation_id
2061
+
2062
+ cancel_token = extra_args.get("event_file_id", None)
2063
+ global_logger.info(f"cancel_token: {cancel_token}")
2064
+ if cancel_token:
2065
+ global_cancel.register_token(cancel_token)
2066
+
2067
+ if get_run_context().mode == RunMode.WEB:
2068
+ runner = FileBasedEventRunner(
2069
+ llm=llm,
2070
+ args=args,
2071
+ conversation_config=conversation_config,
2072
+ cancel_token=cancel_token,
2073
+ )
2074
+ runner.run(AgenticEditRequest(user_input=task_query))
2075
+
2076
+ if get_run_context().mode == RunMode.TERMINAL:
2077
+ runner = TerminalRunner(
2078
+ llm=llm,
2079
+ args=args,
2080
+ conversation_config=conversation_config,
2081
+ cancel_token=cancel_token,
2082
+ )
2083
+ runner.run(AgenticEditRequest(user_input=task_query))
2084
+
3476
2085
  completer.refresh_files()
3477
- return
3478
-
3479
- args = get_final_config()
2086
+ return conversation_config.conversation_id
2087
+
2088
+ args = get_final_config()
3480
2089
  # 准备请求参数
3481
- request = AutoCommandRequest(
3482
- user_input=query
3483
- )
2090
+ request = AutoCommandRequest(user_input=query)
3484
2091
 
3485
2092
  # 初始化调优器
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
- ))
2093
+ try:
2094
+ llm = get_single_llm(
2095
+ args.chat_model or args.model, product_mode=args.product_mode
2096
+ )
2097
+ except ValueError as e:
2098
+ console = Console()
2099
+ console.print(
2100
+ Panel(
2101
+ f"[red]LLM Configuration Error:[/red]\n\n{str(e)}",
2102
+ title="[red]Error[/red]",
2103
+ border_style="red",
2104
+ padding=(1, 2),
2105
+ )
2106
+ )
2107
+ return
2108
+ tuner = CommandAutoTuner(
2109
+ llm,
2110
+ args=args,
2111
+ memory_config=MemoryConfig(memory=memory, save_memory_func=save_memory),
2112
+ command_config=CommandConfig(
2113
+ add_files=add_files,
2114
+ remove_files=remove_files,
2115
+ list_files=list_files,
2116
+ conf=configure,
2117
+ revert=revert,
2118
+ commit=commit,
2119
+ help=help,
2120
+ exclude_dirs=exclude_dirs,
2121
+ exclude_files=exclude_files,
2122
+ ask=ask,
2123
+ chat=chat,
2124
+ coding=coding,
2125
+ design=design,
2126
+ summon=summon,
2127
+ lib=lib_command,
2128
+ mcp=mcp,
2129
+ models=manage_models,
2130
+ index_build=index_build,
2131
+ index_query=index_query,
2132
+ execute_shell_command=execute_shell_command,
2133
+ generate_shell_command=generate_shell_command,
2134
+ conf_export=conf_export,
2135
+ conf_import=conf_import,
2136
+ index_export=index_export,
2137
+ index_import=index_import,
2138
+ ),
2139
+ )
3517
2140
 
3518
2141
  # 生成建议
3519
2142
  response = tuner.analyze(request)
3520
2143
  printer = Printer()
3521
2144
  # 显示建议
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
-
2145
+ console = Console()
2146
+ console.print(
2147
+ Panel(
2148
+ Markdown(response.reasoning or ""),
2149
+ title=printer.get_message_from_key_with_format(
2150
+ "auto_command_reasoning_title"
2151
+ ),
2152
+ border_style="blue",
2153
+ padding=(1, 2),
2154
+ )
2155
+ )
3534
2156
  completer.refresh_files()
2157
+ return None
3535
2158
 
3536
2159
 
3537
2160
  def render_command_file_with_variables(command_infos: Dict[str, Any]) -> str:
3538
2161
  """
3539
2162
  使用 CommandManager 加载并渲染命令文件
3540
-
2163
+
3541
2164
  Args:
3542
2165
  command_infos: parse_query(query) 的返回结果,包含命令和参数信息
3543
-
2166
+
3544
2167
  Returns:
3545
2168
  str: 渲染后的文件内容
3546
-
2169
+
3547
2170
  Raises:
3548
2171
  ValueError: 当参数不足或文件不存在时
3549
2172
  Exception: 当渲染过程出现错误时
@@ -3552,31 +2175,33 @@ def render_command_file_with_variables(command_infos: Dict[str, Any]) -> str:
3552
2175
  # 获取第一个命令的信息
3553
2176
  if not command_infos:
3554
2177
  raise ValueError("command_infos 为空,无法获取命令信息")
3555
-
2178
+
3556
2179
  # command 的位置参数作为路径
3557
2180
  first_command = command_infos["command"]
3558
-
2181
+
3559
2182
  # 获取位置参数(文件路径)
3560
2183
  args = first_command.get("args", [])
3561
2184
  if not args:
3562
2185
  raise ValueError("未提供文件路径参数")
3563
-
2186
+
3564
2187
  file_path = args[0] # 第一个位置参数作为文件路径
3565
-
2188
+
3566
2189
  # 获取关键字参数作为渲染参数
3567
2190
  kwargs = first_command.get("kwargs", {})
3568
-
2191
+
3569
2192
  # 初始化 CommandManager
3570
2193
  command_manager = CommandManager()
3571
-
2194
+
3572
2195
  # 使用 read_command_file_with_render 直接读取并渲染命令文件
3573
- rendered_content = command_manager.read_command_file_with_render(file_path, kwargs)
2196
+ rendered_content = command_manager.read_command_file_with_render(
2197
+ file_path, kwargs
2198
+ )
3574
2199
  if rendered_content is None:
3575
2200
  raise ValueError(f"无法读取或渲染命令文件: {file_path}")
3576
-
2201
+
3577
2202
  global_logger.info(f"成功渲染命令文件: {file_path}, 使用参数: {kwargs}")
3578
2203
  return rendered_content
3579
-
2204
+
3580
2205
  except Exception as e:
3581
2206
  global_logger.error(f"render_command_file_with_variables 执行失败: {str(e)}")
3582
- raise
2207
+ raise