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

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

Potentially problematic release.


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

Files changed (579) hide show
  1. auto_coder-2.0.0.dist-info/LICENSE +158 -0
  2. auto_coder-2.0.0.dist-info/METADATA +558 -0
  3. auto_coder-2.0.0.dist-info/RECORD +795 -0
  4. {auto_coder-0.1.400.dist-info → auto_coder-2.0.0.dist-info}/WHEEL +1 -1
  5. {auto_coder-0.1.400.dist-info → auto_coder-2.0.0.dist-info}/entry_points.txt +3 -3
  6. autocoder/__init__.py +31 -0
  7. autocoder/agent/auto_filegroup.py +32 -13
  8. autocoder/agent/auto_learn_from_commit.py +9 -1
  9. autocoder/agent/base_agentic/__init__.py +3 -0
  10. autocoder/agent/base_agentic/agent_hub.py +1 -1
  11. autocoder/agent/base_agentic/base_agent.py +235 -136
  12. autocoder/agent/base_agentic/default_tools.py +119 -118
  13. autocoder/agent/base_agentic/test_base_agent.py +1 -1
  14. autocoder/agent/base_agentic/tool_registry.py +32 -20
  15. autocoder/agent/base_agentic/tools/read_file_tool_resolver.py +25 -4
  16. autocoder/agent/base_agentic/tools/write_to_file_tool_resolver.py +24 -11
  17. autocoder/agent/base_agentic/types.py +42 -0
  18. autocoder/agent/entry_command_agent/chat.py +73 -59
  19. autocoder/auto_coder.py +31 -40
  20. autocoder/auto_coder_rag.py +11 -1084
  21. autocoder/auto_coder_runner.py +1029 -2310
  22. autocoder/auto_coder_terminal.py +26 -0
  23. autocoder/auto_coder_terminal_v3.py +190 -0
  24. autocoder/chat/conf_command.py +224 -124
  25. autocoder/chat/models_command.py +361 -299
  26. autocoder/chat/rules_command.py +79 -31
  27. autocoder/chat_auto_coder.py +1021 -372
  28. autocoder/chat_auto_coder_lang.py +23 -732
  29. autocoder/commands/auto_command.py +26 -9
  30. autocoder/commands/auto_web.py +1 -1
  31. autocoder/commands/tools.py +44 -44
  32. autocoder/common/__init__.py +150 -128
  33. autocoder/common/ac_style_command_parser/__init__.py +39 -2
  34. autocoder/common/ac_style_command_parser/config.py +422 -0
  35. autocoder/common/ac_style_command_parser/parser.py +292 -78
  36. autocoder/common/ac_style_command_parser/test_parser.py +241 -16
  37. autocoder/common/ac_style_command_parser/test_typed_parser.py +342 -0
  38. autocoder/common/ac_style_command_parser/typed_parser.py +653 -0
  39. autocoder/common/action_yml_file_manager.py +25 -13
  40. autocoder/common/agent_events/__init__.py +52 -0
  41. autocoder/common/agent_events/agent_event_emitter.py +193 -0
  42. autocoder/common/agent_events/event_factory.py +177 -0
  43. autocoder/common/agent_events/examples.py +307 -0
  44. autocoder/common/agent_events/types.py +113 -0
  45. autocoder/common/agent_events/utils.py +68 -0
  46. autocoder/common/agent_hooks/__init__.py +44 -0
  47. autocoder/common/agent_hooks/examples.py +582 -0
  48. autocoder/common/agent_hooks/hook_executor.py +217 -0
  49. autocoder/common/agent_hooks/hook_manager.py +288 -0
  50. autocoder/common/agent_hooks/types.py +133 -0
  51. autocoder/common/agent_hooks/utils.py +99 -0
  52. autocoder/common/agent_query_queue/queue_executor.py +324 -0
  53. autocoder/common/agent_query_queue/queue_manager.py +325 -0
  54. autocoder/common/agents/__init__.py +11 -0
  55. autocoder/common/agents/agent_manager.py +323 -0
  56. autocoder/common/agents/agent_parser.py +189 -0
  57. autocoder/common/agents/example_usage.py +344 -0
  58. autocoder/common/agents/integration_example.py +330 -0
  59. autocoder/common/agents/test_agent_parser.py +545 -0
  60. autocoder/common/async_utils.py +101 -0
  61. autocoder/common/auto_coder_lang.py +23 -972
  62. autocoder/common/autocoderargs_parser/__init__.py +14 -0
  63. autocoder/common/autocoderargs_parser/parser.py +184 -0
  64. autocoder/common/autocoderargs_parser/tests/__init__.py +1 -0
  65. autocoder/common/autocoderargs_parser/tests/test_args_parser.py +235 -0
  66. autocoder/common/autocoderargs_parser/tests/test_token_parser.py +195 -0
  67. autocoder/common/autocoderargs_parser/token_parser.py +290 -0
  68. autocoder/common/buildin_tokenizer.py +2 -4
  69. autocoder/common/code_auto_generate.py +149 -74
  70. autocoder/common/code_auto_generate_diff.py +163 -70
  71. autocoder/common/code_auto_generate_editblock.py +179 -89
  72. autocoder/common/code_auto_generate_strict_diff.py +167 -72
  73. autocoder/common/code_auto_merge_editblock.py +13 -6
  74. autocoder/common/code_modification_ranker.py +1 -1
  75. autocoder/common/command_completer.py +3 -3
  76. autocoder/common/command_file_manager/manager.py +183 -47
  77. autocoder/common/command_file_manager/test_command_file_manager.py +507 -0
  78. autocoder/common/command_templates.py +1 -1
  79. autocoder/common/conf_utils.py +2 -4
  80. autocoder/common/conversations/config.py +11 -3
  81. autocoder/common/conversations/get_conversation_manager.py +100 -2
  82. autocoder/common/conversations/llm_stats_models.py +264 -0
  83. autocoder/common/conversations/manager.py +112 -28
  84. autocoder/common/conversations/models.py +16 -2
  85. autocoder/common/conversations/storage/index_manager.py +134 -10
  86. autocoder/common/core_config/__init__.py +63 -0
  87. autocoder/common/core_config/agentic_mode_manager.py +109 -0
  88. autocoder/common/core_config/base_manager.py +123 -0
  89. autocoder/common/core_config/compatibility.py +151 -0
  90. autocoder/common/core_config/config_manager.py +156 -0
  91. autocoder/common/core_config/conversation_manager.py +31 -0
  92. autocoder/common/core_config/exclude_manager.py +72 -0
  93. autocoder/common/core_config/file_manager.py +177 -0
  94. autocoder/common/core_config/human_as_model_manager.py +129 -0
  95. autocoder/common/core_config/lib_manager.py +54 -0
  96. autocoder/common/core_config/main_manager.py +81 -0
  97. autocoder/common/core_config/mode_manager.py +126 -0
  98. autocoder/common/core_config/models.py +70 -0
  99. autocoder/common/core_config/test_memory_manager.py +1056 -0
  100. autocoder/common/env_manager.py +282 -0
  101. autocoder/common/env_manager_usage_example.py +211 -0
  102. autocoder/common/file_checkpoint/conversation_checkpoint.py +19 -19
  103. autocoder/common/file_checkpoint/manager.py +264 -48
  104. autocoder/common/file_checkpoint/test_backup.py +1 -18
  105. autocoder/common/file_checkpoint/test_manager.py +270 -1
  106. autocoder/common/file_checkpoint/test_store.py +1 -17
  107. autocoder/common/file_handler/__init__.py +23 -0
  108. autocoder/common/file_handler/active_context_handler.py +159 -0
  109. autocoder/common/file_handler/add_files_handler.py +409 -0
  110. autocoder/common/file_handler/chat_handler.py +180 -0
  111. autocoder/common/file_handler/coding_handler.py +401 -0
  112. autocoder/common/file_handler/commit_handler.py +200 -0
  113. autocoder/common/file_handler/lib_handler.py +156 -0
  114. autocoder/common/file_handler/list_files_handler.py +111 -0
  115. autocoder/common/file_handler/mcp_handler.py +268 -0
  116. autocoder/common/file_handler/models_handler.py +493 -0
  117. autocoder/common/file_handler/remove_files_handler.py +172 -0
  118. autocoder/common/file_monitor/test_file_monitor.py +307 -0
  119. autocoder/common/git_utils.py +51 -10
  120. autocoder/common/global_cancel.py +15 -6
  121. autocoder/common/ignorefiles/test_ignore_file_utils.py +1 -1
  122. autocoder/common/international/__init__.py +31 -0
  123. autocoder/common/international/demo_international.py +92 -0
  124. autocoder/common/international/message_manager.py +157 -0
  125. autocoder/common/international/messages/__init__.py +56 -0
  126. autocoder/common/international/messages/async_command_messages.py +507 -0
  127. autocoder/common/international/messages/auto_coder_messages.py +2208 -0
  128. autocoder/common/international/messages/chat_auto_coder_messages.py +1547 -0
  129. autocoder/common/international/messages/command_help_messages.py +986 -0
  130. autocoder/common/international/messages/conversation_command_messages.py +191 -0
  131. autocoder/common/international/messages/git_helper_plugin_messages.py +159 -0
  132. autocoder/common/international/messages/queue_command_messages.py +751 -0
  133. autocoder/common/international/messages/rules_command_messages.py +77 -0
  134. autocoder/common/international/messages/sdk_messages.py +1707 -0
  135. autocoder/common/international/messages/token_helper_plugin_messages.py +361 -0
  136. autocoder/common/international/messages/tool_display_messages.py +1212 -0
  137. autocoder/common/international/messages/workflow_exception_messages.py +473 -0
  138. autocoder/common/international/test_international.py +612 -0
  139. autocoder/common/linter_core/__init__.py +28 -0
  140. autocoder/common/linter_core/base_linter.py +61 -0
  141. autocoder/common/linter_core/config_loader.py +271 -0
  142. autocoder/common/linter_core/formatters/__init__.py +0 -0
  143. autocoder/common/linter_core/formatters/base_formatter.py +38 -0
  144. autocoder/common/linter_core/formatters/raw_formatter.py +17 -0
  145. autocoder/common/linter_core/linter.py +166 -0
  146. autocoder/common/linter_core/linter_factory.py +216 -0
  147. autocoder/common/linter_core/linter_manager.py +333 -0
  148. autocoder/common/linter_core/linters/__init__.py +9 -0
  149. autocoder/common/linter_core/linters/java_linter.py +342 -0
  150. autocoder/common/linter_core/linters/python_linter.py +115 -0
  151. autocoder/common/linter_core/linters/typescript_linter.py +119 -0
  152. autocoder/common/linter_core/models/__init__.py +7 -0
  153. autocoder/common/linter_core/models/lint_result.py +91 -0
  154. autocoder/common/linter_core/models.py +33 -0
  155. autocoder/common/linter_core/tests/__init__.py +3 -0
  156. autocoder/common/linter_core/tests/test_config_loader.py +323 -0
  157. autocoder/common/linter_core/tests/test_config_loading.py +308 -0
  158. autocoder/common/linter_core/tests/test_factory_manager.py +234 -0
  159. autocoder/common/linter_core/tests/test_formatters.py +147 -0
  160. autocoder/common/linter_core/tests/test_integration.py +317 -0
  161. autocoder/common/linter_core/tests/test_java_linter.py +496 -0
  162. autocoder/common/linter_core/tests/test_linters.py +265 -0
  163. autocoder/common/linter_core/tests/test_models.py +81 -0
  164. autocoder/common/linter_core/tests/verify_config_loading.py +296 -0
  165. autocoder/common/linter_core/tests/verify_fixes.py +183 -0
  166. autocoder/common/llm_friendly_package/__init__.py +31 -0
  167. autocoder/common/llm_friendly_package/base_manager.py +102 -0
  168. autocoder/common/llm_friendly_package/docs_manager.py +121 -0
  169. autocoder/common/llm_friendly_package/library_manager.py +171 -0
  170. autocoder/common/{llm_friendly_package.py → llm_friendly_package/main_manager.py} +204 -231
  171. autocoder/common/llm_friendly_package/models.py +40 -0
  172. autocoder/common/llm_friendly_package/test_llm_friendly_package.py +536 -0
  173. autocoder/common/llms/__init__.py +15 -0
  174. autocoder/common/llms/demo_error_handling.py +85 -0
  175. autocoder/common/llms/factory.py +142 -0
  176. autocoder/common/llms/manager.py +264 -0
  177. autocoder/common/llms/pricing.py +121 -0
  178. autocoder/common/llms/registry.py +288 -0
  179. autocoder/common/llms/schema.py +77 -0
  180. autocoder/common/llms/simple_demo.py +45 -0
  181. autocoder/common/llms/test_quick_model.py +116 -0
  182. autocoder/common/llms/test_remove_functionality.py +182 -0
  183. autocoder/common/llms/tests/__init__.py +1 -0
  184. autocoder/common/llms/tests/test_manager.py +330 -0
  185. autocoder/common/llms/tests/test_registry.py +364 -0
  186. autocoder/common/mcp_tools/__init__.py +62 -0
  187. autocoder/common/{mcp_tools.py → mcp_tools/executor.py} +49 -40
  188. autocoder/common/{mcp_hub.py → mcp_tools/hub.py} +42 -68
  189. autocoder/common/{mcp_server_install.py → mcp_tools/installer.py} +16 -28
  190. autocoder/common/{mcp_server.py → mcp_tools/server.py} +176 -48
  191. autocoder/common/mcp_tools/test_keyboard_interrupt.py +93 -0
  192. autocoder/common/mcp_tools/test_mcp_tools.py +391 -0
  193. autocoder/common/{mcp_server_types.py → mcp_tools/types.py} +121 -48
  194. autocoder/common/mcp_tools/verify_functionality.py +202 -0
  195. autocoder/common/model_speed_tester.py +32 -26
  196. autocoder/common/priority_directory_finder/__init__.py +142 -0
  197. autocoder/common/priority_directory_finder/examples.py +230 -0
  198. autocoder/common/priority_directory_finder/finder.py +283 -0
  199. autocoder/common/priority_directory_finder/models.py +236 -0
  200. autocoder/common/priority_directory_finder/test_priority_directory_finder.py +431 -0
  201. autocoder/common/project_scanner/__init__.py +18 -0
  202. autocoder/common/project_scanner/compat.py +77 -0
  203. autocoder/common/project_scanner/scanner.py +436 -0
  204. autocoder/common/project_tracker/__init__.py +27 -0
  205. autocoder/common/project_tracker/api.py +228 -0
  206. autocoder/common/project_tracker/demo.py +272 -0
  207. autocoder/common/project_tracker/tracker.py +487 -0
  208. autocoder/common/project_tracker/types.py +53 -0
  209. autocoder/common/pruner/__init__.py +67 -0
  210. autocoder/common/pruner/agentic_conversation_pruner.py +746 -0
  211. autocoder/common/{context_pruner.py → pruner/context_pruner.py} +137 -40
  212. autocoder/common/pruner/conversation_message_ids_api.py +386 -0
  213. autocoder/common/pruner/conversation_message_ids_manager.py +347 -0
  214. autocoder/common/pruner/conversation_message_ids_pruner.py +473 -0
  215. autocoder/common/pruner/conversation_normalizer.py +347 -0
  216. autocoder/common/{conversation_pruner.py → pruner/conversation_pruner.py} +26 -6
  217. autocoder/common/pruner/test_agentic_conversation_pruner.py +784 -0
  218. autocoder/common/pruner/test_context_pruner.py +546 -0
  219. autocoder/common/pruner/test_conversation_normalizer.py +502 -0
  220. autocoder/common/pruner/test_tool_content_detector.py +324 -0
  221. autocoder/common/pruner/tool_content_detector.py +227 -0
  222. autocoder/common/pruner/tools/__init__.py +18 -0
  223. autocoder/common/pruner/tools/query_message_ids.py +264 -0
  224. autocoder/common/pruner/tools/test_agentic_pruning_logic.py +432 -0
  225. autocoder/common/pruner/tools/test_message_ids_pruning_only.py +192 -0
  226. autocoder/common/pull_requests/__init__.py +9 -1
  227. autocoder/common/pull_requests/utils.py +122 -1
  228. autocoder/common/rag_manager/rag_manager.py +36 -40
  229. autocoder/common/rulefiles/__init__.py +53 -1
  230. autocoder/common/rulefiles/api.py +250 -0
  231. autocoder/common/rulefiles/core/__init__.py +14 -0
  232. autocoder/common/rulefiles/core/manager.py +241 -0
  233. autocoder/common/rulefiles/core/selector.py +805 -0
  234. autocoder/common/rulefiles/models/__init__.py +20 -0
  235. autocoder/common/rulefiles/models/index.py +16 -0
  236. autocoder/common/rulefiles/models/init_rule.py +18 -0
  237. autocoder/common/rulefiles/models/rule_file.py +18 -0
  238. autocoder/common/rulefiles/models/rule_relevance.py +14 -0
  239. autocoder/common/rulefiles/models/summary.py +16 -0
  240. autocoder/common/rulefiles/test_rulefiles.py +776 -0
  241. autocoder/common/rulefiles/utils/__init__.py +34 -0
  242. autocoder/common/rulefiles/utils/monitor.py +86 -0
  243. autocoder/common/rulefiles/utils/parser.py +230 -0
  244. autocoder/common/save_formatted_log.py +67 -10
  245. autocoder/common/search_replace.py +8 -1
  246. autocoder/common/search_replace_patch/__init__.py +24 -0
  247. autocoder/common/search_replace_patch/base.py +115 -0
  248. autocoder/common/search_replace_patch/manager.py +248 -0
  249. autocoder/common/search_replace_patch/patch_replacer.py +304 -0
  250. autocoder/common/search_replace_patch/similarity_replacer.py +306 -0
  251. autocoder/common/search_replace_patch/string_replacer.py +181 -0
  252. autocoder/common/search_replace_patch/tests/__init__.py +3 -0
  253. autocoder/common/search_replace_patch/tests/run_tests.py +126 -0
  254. autocoder/common/search_replace_patch/tests/test_base.py +188 -0
  255. autocoder/common/search_replace_patch/tests/test_empty_line_insert.py +233 -0
  256. autocoder/common/search_replace_patch/tests/test_integration.py +389 -0
  257. autocoder/common/search_replace_patch/tests/test_manager.py +351 -0
  258. autocoder/common/search_replace_patch/tests/test_patch_replacer.py +316 -0
  259. autocoder/common/search_replace_patch/tests/test_regex_replacer.py +306 -0
  260. autocoder/common/search_replace_patch/tests/test_similarity_replacer.py +384 -0
  261. autocoder/common/shell_commands/__init__.py +197 -0
  262. autocoder/common/shell_commands/background_process_notifier.py +346 -0
  263. autocoder/common/shell_commands/command_executor.py +1127 -0
  264. autocoder/common/shell_commands/error_recovery.py +541 -0
  265. autocoder/common/shell_commands/exceptions.py +120 -0
  266. autocoder/common/shell_commands/interactive_executor.py +476 -0
  267. autocoder/common/shell_commands/interactive_pexpect_process.py +623 -0
  268. autocoder/common/shell_commands/interactive_process.py +744 -0
  269. autocoder/common/shell_commands/interactive_session_manager.py +1014 -0
  270. autocoder/common/shell_commands/monitoring.py +529 -0
  271. autocoder/common/shell_commands/process_cleanup.py +386 -0
  272. autocoder/common/shell_commands/process_manager.py +606 -0
  273. autocoder/common/shell_commands/test_interactive_pexpect_process.py +281 -0
  274. autocoder/common/shell_commands/tests/__init__.py +6 -0
  275. autocoder/common/shell_commands/tests/conftest.py +118 -0
  276. autocoder/common/shell_commands/tests/test_background_process_notifier.py +703 -0
  277. autocoder/common/shell_commands/tests/test_command_executor.py +448 -0
  278. autocoder/common/shell_commands/tests/test_error_recovery.py +305 -0
  279. autocoder/common/shell_commands/tests/test_exceptions.py +299 -0
  280. autocoder/common/shell_commands/tests/test_execute_batch.py +588 -0
  281. autocoder/common/shell_commands/tests/test_indented_batch_commands.py +244 -0
  282. autocoder/common/shell_commands/tests/test_integration.py +664 -0
  283. autocoder/common/shell_commands/tests/test_monitoring.py +546 -0
  284. autocoder/common/shell_commands/tests/test_performance.py +632 -0
  285. autocoder/common/shell_commands/tests/test_process_cleanup.py +397 -0
  286. autocoder/common/shell_commands/tests/test_process_manager.py +606 -0
  287. autocoder/common/shell_commands/tests/test_timeout_config.py +343 -0
  288. autocoder/common/shell_commands/tests/test_timeout_manager.py +520 -0
  289. autocoder/common/shell_commands/timeout_config.py +315 -0
  290. autocoder/common/shell_commands/timeout_manager.py +352 -0
  291. autocoder/common/terminal_paste/__init__.py +14 -0
  292. autocoder/common/terminal_paste/demo.py +145 -0
  293. autocoder/common/terminal_paste/demo_paste_functionality.py +95 -0
  294. autocoder/common/terminal_paste/paste_handler.py +200 -0
  295. autocoder/common/terminal_paste/paste_manager.py +118 -0
  296. autocoder/common/terminal_paste/tests/__init__.py +1 -0
  297. autocoder/common/terminal_paste/tests/test_paste_handler.py +182 -0
  298. autocoder/common/terminal_paste/tests/test_paste_manager.py +126 -0
  299. autocoder/common/terminal_paste/utils.py +163 -0
  300. autocoder/common/test_autocoder_args.py +232 -0
  301. autocoder/common/test_env_manager.py +173 -0
  302. autocoder/common/test_env_manager_integration.py +159 -0
  303. autocoder/common/text_similarity/__init__.py +9 -0
  304. autocoder/common/text_similarity/demo.py +216 -0
  305. autocoder/common/text_similarity/examples.py +266 -0
  306. autocoder/common/text_similarity/test_text_similarity.py +306 -0
  307. autocoder/common/text_similarity/text_similarity.py +194 -0
  308. autocoder/common/text_similarity/utils.py +125 -0
  309. autocoder/common/todos/__init__.py +61 -0
  310. autocoder/common/todos/cache/__init__.py +16 -0
  311. autocoder/common/todos/cache/base_cache.py +89 -0
  312. autocoder/common/todos/cache/cache_manager.py +228 -0
  313. autocoder/common/todos/cache/memory_cache.py +225 -0
  314. autocoder/common/todos/config.py +155 -0
  315. autocoder/common/todos/exceptions.py +35 -0
  316. autocoder/common/todos/get_todo_manager.py +161 -0
  317. autocoder/common/todos/manager.py +537 -0
  318. autocoder/common/todos/models.py +239 -0
  319. autocoder/common/todos/storage/__init__.py +14 -0
  320. autocoder/common/todos/storage/base_storage.py +76 -0
  321. autocoder/common/todos/storage/file_storage.py +278 -0
  322. autocoder/common/tokens/__init__.py +15 -0
  323. autocoder/common/tokens/counter.py +44 -2
  324. autocoder/common/tools_manager/__init__.py +17 -0
  325. autocoder/common/tools_manager/examples.py +162 -0
  326. autocoder/common/tools_manager/manager.py +385 -0
  327. autocoder/common/tools_manager/models.py +39 -0
  328. autocoder/common/tools_manager/test_tools_manager.py +303 -0
  329. autocoder/common/tools_manager/utils.py +191 -0
  330. autocoder/common/v2/agent/agentic_callbacks.py +270 -0
  331. autocoder/common/v2/agent/agentic_edit.py +2729 -2052
  332. autocoder/common/v2/agent/agentic_edit_change_manager.py +474 -0
  333. autocoder/common/v2/agent/agentic_edit_tools/__init__.py +43 -2
  334. autocoder/common/v2/agent/agentic_edit_tools/ac_mod_list_tool_resolver.py +279 -0
  335. autocoder/common/v2/agent/agentic_edit_tools/ac_mod_read_tool_resolver.py +40 -0
  336. autocoder/common/v2/agent/agentic_edit_tools/ac_mod_write_tool_resolver.py +52 -0
  337. autocoder/common/v2/agent/agentic_edit_tools/ask_followup_question_tool_resolver.py +8 -0
  338. autocoder/common/v2/agent/agentic_edit_tools/background_task_tool_resolver.py +1167 -0
  339. autocoder/common/v2/agent/agentic_edit_tools/base_tool_resolver.py +2 -2
  340. autocoder/common/v2/agent/agentic_edit_tools/conversation_message_ids_read_tool_resolver.py +214 -0
  341. autocoder/common/v2/agent/agentic_edit_tools/conversation_message_ids_write_tool_resolver.py +299 -0
  342. autocoder/common/v2/agent/agentic_edit_tools/count_tokens_tool_resolver.py +290 -0
  343. autocoder/common/v2/agent/agentic_edit_tools/execute_command_tool_resolver.py +565 -30
  344. autocoder/common/v2/agent/agentic_edit_tools/execute_workflow_tool_resolver.py +485 -0
  345. autocoder/common/v2/agent/agentic_edit_tools/extract_to_text_tool_resolver.py +225 -0
  346. autocoder/common/v2/agent/agentic_edit_tools/lint_report.py +79 -0
  347. autocoder/common/v2/agent/agentic_edit_tools/linter_config_models.py +343 -0
  348. autocoder/common/v2/agent/agentic_edit_tools/linter_enabled_tool_resolver.py +189 -0
  349. autocoder/common/v2/agent/agentic_edit_tools/list_files_tool_resolver.py +169 -101
  350. autocoder/common/v2/agent/agentic_edit_tools/load_extra_document_tool_resolver.py +349 -0
  351. autocoder/common/v2/agent/agentic_edit_tools/read_file_tool_resolver.py +244 -51
  352. autocoder/common/v2/agent/agentic_edit_tools/replace_in_file_tool_resolver.py +667 -147
  353. autocoder/common/v2/agent/agentic_edit_tools/run_named_subagents_tool_resolver.py +691 -0
  354. autocoder/common/v2/agent/agentic_edit_tools/search_files_tool_resolver.py +409 -140
  355. autocoder/common/v2/agent/agentic_edit_tools/session_interactive_tool_resolver.py +115 -0
  356. autocoder/common/v2/agent/agentic_edit_tools/session_start_tool_resolver.py +190 -0
  357. autocoder/common/v2/agent/agentic_edit_tools/session_stop_tool_resolver.py +76 -0
  358. autocoder/common/v2/agent/agentic_edit_tools/test_write_to_file_tool_resolver.py +209 -194
  359. autocoder/common/v2/agent/agentic_edit_tools/todo_read_tool_resolver.py +135 -0
  360. autocoder/common/v2/agent/agentic_edit_tools/todo_write_tool_resolver.py +328 -0
  361. autocoder/common/v2/agent/agentic_edit_tools/use_mcp_tool_resolver.py +2 -2
  362. autocoder/common/v2/agent/agentic_edit_tools/web_crawl_tool_resolver.py +557 -0
  363. autocoder/common/v2/agent/agentic_edit_tools/web_search_tool_resolver.py +600 -0
  364. autocoder/common/v2/agent/agentic_edit_tools/write_to_file_tool_resolver.py +56 -121
  365. autocoder/common/v2/agent/agentic_edit_types.py +386 -10
  366. autocoder/common/v2/agent/runner/__init__.py +31 -0
  367. autocoder/common/v2/agent/runner/base_runner.py +92 -0
  368. autocoder/common/v2/agent/runner/file_based_event_runner.py +217 -0
  369. autocoder/common/v2/agent/runner/sdk_runner.py +182 -0
  370. autocoder/common/v2/agent/runner/terminal_runner.py +396 -0
  371. autocoder/common/v2/agent/runner/tool_display.py +589 -0
  372. autocoder/common/v2/agent/test_agentic_callbacks.py +265 -0
  373. autocoder/common/v2/agent/test_agentic_edit.py +194 -0
  374. autocoder/common/v2/agent/tool_caller/__init__.py +24 -0
  375. autocoder/common/v2/agent/tool_caller/default_tool_resolver_map.py +135 -0
  376. autocoder/common/v2/agent/tool_caller/integration_test.py +172 -0
  377. autocoder/common/v2/agent/tool_caller/plugins/__init__.py +14 -0
  378. autocoder/common/v2/agent/tool_caller/plugins/base_plugin.py +126 -0
  379. autocoder/common/v2/agent/tool_caller/plugins/examples/__init__.py +13 -0
  380. autocoder/common/v2/agent/tool_caller/plugins/examples/logging_plugin.py +164 -0
  381. autocoder/common/v2/agent/tool_caller/plugins/examples/security_filter_plugin.py +198 -0
  382. autocoder/common/v2/agent/tool_caller/plugins/plugin_interface.py +141 -0
  383. autocoder/common/v2/agent/tool_caller/test_tool_caller.py +278 -0
  384. autocoder/common/v2/agent/tool_caller/tool_call_plugin_manager.py +331 -0
  385. autocoder/common/v2/agent/tool_caller/tool_caller.py +337 -0
  386. autocoder/common/v2/agent/tool_caller/usage_example.py +193 -0
  387. autocoder/common/v2/code_agentic_editblock_manager.py +4 -4
  388. autocoder/common/v2/code_auto_generate.py +136 -78
  389. autocoder/common/v2/code_auto_generate_diff.py +135 -79
  390. autocoder/common/v2/code_auto_generate_editblock.py +174 -99
  391. autocoder/common/v2/code_auto_generate_strict_diff.py +151 -71
  392. autocoder/common/v2/code_auto_merge.py +1 -1
  393. autocoder/common/v2/code_auto_merge_editblock.py +13 -1
  394. autocoder/common/v2/code_diff_manager.py +3 -3
  395. autocoder/common/v2/code_editblock_manager.py +4 -14
  396. autocoder/common/v2/code_manager.py +1 -1
  397. autocoder/common/v2/code_strict_diff_manager.py +2 -2
  398. autocoder/common/wrap_llm_hint/__init__.py +10 -0
  399. autocoder/common/wrap_llm_hint/test_wrap_llm_hint.py +1067 -0
  400. autocoder/common/wrap_llm_hint/utils.py +432 -0
  401. autocoder/common/wrap_llm_hint/wrap_llm_hint.py +323 -0
  402. autocoder/completer/__init__.py +8 -0
  403. autocoder/completer/command_completer_v2.py +1051 -0
  404. autocoder/default_project/__init__.py +501 -0
  405. autocoder/dispacher/__init__.py +4 -12
  406. autocoder/dispacher/actions/action.py +165 -7
  407. autocoder/dispacher/actions/plugins/action_regex_project.py +2 -2
  408. autocoder/index/entry.py +117 -125
  409. autocoder/{agent → index/filter}/agentic_filter.py +323 -334
  410. autocoder/index/filter/normal_filter.py +5 -11
  411. autocoder/index/filter/quick_filter.py +1 -1
  412. autocoder/index/index.py +36 -9
  413. autocoder/index/tests/__init__.py +1 -0
  414. autocoder/index/tests/run_tests.py +195 -0
  415. autocoder/index/tests/test_entry.py +303 -0
  416. autocoder/index/tests/test_index_manager.py +314 -0
  417. autocoder/index/tests/test_module_integration.py +300 -0
  418. autocoder/index/tests/test_symbols_utils.py +183 -0
  419. autocoder/inner/__init__.py +4 -0
  420. autocoder/inner/agentic.py +932 -0
  421. autocoder/inner/async_command_handler.py +992 -0
  422. autocoder/inner/conversation_command_handlers.py +623 -0
  423. autocoder/inner/merge_command_handler.py +213 -0
  424. autocoder/inner/queue_command_handler.py +684 -0
  425. autocoder/models.py +95 -266
  426. autocoder/plugins/git_helper_plugin.py +31 -29
  427. autocoder/plugins/token_helper_plugin.py +156 -37
  428. autocoder/pyproject/__init__.py +32 -29
  429. autocoder/rag/agentic_rag.py +215 -75
  430. autocoder/rag/cache/simple_cache.py +1 -2
  431. autocoder/rag/loaders/image_loader.py +1 -1
  432. autocoder/rag/long_context_rag.py +42 -26
  433. autocoder/rag/qa_conversation_strategy.py +1 -1
  434. autocoder/rag/terminal/__init__.py +17 -0
  435. autocoder/rag/terminal/args.py +581 -0
  436. autocoder/rag/terminal/bootstrap.py +61 -0
  437. autocoder/rag/terminal/command_handlers.py +653 -0
  438. autocoder/rag/terminal/formatters/__init__.py +20 -0
  439. autocoder/rag/terminal/formatters/base.py +70 -0
  440. autocoder/rag/terminal/formatters/json_format.py +66 -0
  441. autocoder/rag/terminal/formatters/stream_json.py +95 -0
  442. autocoder/rag/terminal/formatters/text.py +28 -0
  443. autocoder/rag/terminal/init.py +120 -0
  444. autocoder/rag/terminal/utils.py +106 -0
  445. autocoder/rag/test_agentic_rag.py +389 -0
  446. autocoder/rag/test_doc_filter.py +3 -3
  447. autocoder/rag/test_long_context_rag.py +1 -1
  448. autocoder/rag/test_token_limiter.py +517 -10
  449. autocoder/rag/token_counter.py +3 -0
  450. autocoder/rag/token_limiter.py +19 -15
  451. autocoder/rag/tools/__init__.py +26 -2
  452. autocoder/rag/tools/bochaai_example.py +343 -0
  453. autocoder/rag/tools/bochaai_sdk.py +541 -0
  454. autocoder/rag/tools/metaso_example.py +268 -0
  455. autocoder/rag/tools/metaso_sdk.py +417 -0
  456. autocoder/rag/tools/recall_tool.py +28 -7
  457. autocoder/rag/tools/run_integration_tests.py +204 -0
  458. autocoder/rag/tools/test_all_providers.py +318 -0
  459. autocoder/rag/tools/test_bochaai_integration.py +482 -0
  460. autocoder/rag/tools/test_final_integration.py +215 -0
  461. autocoder/rag/tools/test_metaso_integration.py +424 -0
  462. autocoder/rag/tools/test_metaso_real.py +171 -0
  463. autocoder/rag/tools/test_web_crawl_tool.py +639 -0
  464. autocoder/rag/tools/test_web_search_tool.py +509 -0
  465. autocoder/rag/tools/todo_read_tool.py +202 -0
  466. autocoder/rag/tools/todo_write_tool.py +412 -0
  467. autocoder/rag/tools/web_crawl_tool.py +634 -0
  468. autocoder/rag/tools/web_search_tool.py +558 -0
  469. autocoder/rag/tools/web_tools_example.py +119 -0
  470. autocoder/rag/types.py +16 -0
  471. autocoder/rag/variable_holder.py +4 -2
  472. autocoder/rags.py +86 -79
  473. autocoder/regexproject/__init__.py +23 -21
  474. autocoder/run_context.py +9 -0
  475. autocoder/sdk/__init__.py +50 -161
  476. autocoder/sdk/api.py +370 -0
  477. autocoder/sdk/async_runner/__init__.py +26 -0
  478. autocoder/sdk/async_runner/async_executor.py +650 -0
  479. autocoder/sdk/async_runner/async_handler.py +356 -0
  480. autocoder/sdk/async_runner/markdown_processor.py +595 -0
  481. autocoder/sdk/async_runner/task_metadata.py +284 -0
  482. autocoder/sdk/async_runner/worktree_manager.py +438 -0
  483. autocoder/sdk/cli/__init__.py +2 -5
  484. autocoder/sdk/cli/formatters.py +28 -204
  485. autocoder/sdk/cli/handlers.py +77 -44
  486. autocoder/sdk/cli/main.py +158 -170
  487. autocoder/sdk/cli/options.py +95 -22
  488. autocoder/sdk/constants.py +139 -51
  489. autocoder/sdk/core/auto_coder_core.py +484 -267
  490. autocoder/sdk/core/bridge.py +298 -118
  491. autocoder/sdk/exceptions.py +18 -12
  492. autocoder/sdk/formatters/__init__.py +19 -0
  493. autocoder/sdk/formatters/input.py +64 -0
  494. autocoder/sdk/formatters/output.py +247 -0
  495. autocoder/sdk/formatters/stream.py +54 -0
  496. autocoder/sdk/models/__init__.py +6 -5
  497. autocoder/sdk/models/options.py +55 -18
  498. autocoder/sdk/utils/formatters.py +27 -195
  499. autocoder/suffixproject/__init__.py +28 -25
  500. autocoder/terminal/__init__.py +14 -0
  501. autocoder/terminal/app.py +454 -0
  502. autocoder/terminal/args.py +32 -0
  503. autocoder/terminal/bootstrap.py +178 -0
  504. autocoder/terminal/command_processor.py +521 -0
  505. autocoder/terminal/command_registry.py +57 -0
  506. autocoder/terminal/help.py +97 -0
  507. autocoder/terminal/tasks/__init__.py +5 -0
  508. autocoder/terminal/tasks/background.py +77 -0
  509. autocoder/terminal/tasks/task_event.py +70 -0
  510. autocoder/terminal/ui/__init__.py +13 -0
  511. autocoder/terminal/ui/completer.py +268 -0
  512. autocoder/terminal/ui/keybindings.py +75 -0
  513. autocoder/terminal/ui/session.py +41 -0
  514. autocoder/terminal/ui/toolbar.py +64 -0
  515. autocoder/terminal/utils/__init__.py +13 -0
  516. autocoder/terminal/utils/errors.py +18 -0
  517. autocoder/terminal/utils/paths.py +19 -0
  518. autocoder/terminal/utils/shell.py +43 -0
  519. autocoder/terminal_v3/__init__.py +10 -0
  520. autocoder/terminal_v3/app.py +201 -0
  521. autocoder/terminal_v3/handlers/__init__.py +5 -0
  522. autocoder/terminal_v3/handlers/command_handler.py +131 -0
  523. autocoder/terminal_v3/models/__init__.py +6 -0
  524. autocoder/terminal_v3/models/conversation_buffer.py +214 -0
  525. autocoder/terminal_v3/models/message.py +50 -0
  526. autocoder/terminal_v3/models/tool_display.py +247 -0
  527. autocoder/terminal_v3/ui/__init__.py +7 -0
  528. autocoder/terminal_v3/ui/keybindings.py +56 -0
  529. autocoder/terminal_v3/ui/layout.py +141 -0
  530. autocoder/terminal_v3/ui/styles.py +43 -0
  531. autocoder/tsproject/__init__.py +23 -23
  532. autocoder/utils/auto_coder_utils/chat_stream_out.py +1 -1
  533. autocoder/utils/llms.py +88 -80
  534. autocoder/utils/math_utils.py +101 -0
  535. autocoder/utils/model_provider_selector.py +16 -4
  536. autocoder/utils/operate_config_api.py +33 -5
  537. autocoder/utils/thread_utils.py +2 -2
  538. autocoder/version.py +4 -2
  539. autocoder/workflow_agents/__init__.py +84 -0
  540. autocoder/workflow_agents/agent.py +143 -0
  541. autocoder/workflow_agents/exceptions.py +573 -0
  542. autocoder/workflow_agents/executor.py +489 -0
  543. autocoder/workflow_agents/loader.py +737 -0
  544. autocoder/workflow_agents/runner.py +267 -0
  545. autocoder/workflow_agents/types.py +172 -0
  546. autocoder/workflow_agents/utils.py +434 -0
  547. autocoder/workflow_agents/workflow_manager.py +211 -0
  548. auto_coder-0.1.400.dist-info/METADATA +0 -396
  549. auto_coder-0.1.400.dist-info/RECORD +0 -425
  550. auto_coder-0.1.400.dist-info/licenses/LICENSE +0 -201
  551. autocoder/auto_coder_server.py +0 -672
  552. autocoder/benchmark.py +0 -138
  553. autocoder/common/ac_style_command_parser/example.py +0 -7
  554. autocoder/common/cleaner.py +0 -31
  555. autocoder/common/command_completer_v2.py +0 -615
  556. autocoder/common/directory_cache/__init__.py +0 -1
  557. autocoder/common/directory_cache/cache.py +0 -192
  558. autocoder/common/directory_cache/test_cache.py +0 -190
  559. autocoder/common/file_checkpoint/examples.py +0 -217
  560. autocoder/common/llm_friendly_package_example.py +0 -138
  561. autocoder/common/llm_friendly_package_test.py +0 -63
  562. autocoder/common/pull_requests/test_module.py +0 -1
  563. autocoder/common/rulefiles/autocoderrules_utils.py +0 -484
  564. autocoder/common/text.py +0 -30
  565. autocoder/common/v2/agent/agentic_edit_tools/list_package_info_tool_resolver.py +0 -42
  566. autocoder/common/v2/agent/agentic_edit_tools/test_execute_command_tool_resolver.py +0 -70
  567. autocoder/common/v2/agent/agentic_edit_tools/test_search_files_tool_resolver.py +0 -163
  568. autocoder/common/v2/agent/agentic_tool_display.py +0 -183
  569. autocoder/plugins/dynamic_completion_example.py +0 -148
  570. autocoder/plugins/sample_plugin.py +0 -160
  571. autocoder/sdk/cli/__main__.py +0 -26
  572. autocoder/sdk/cli/completion_wrapper.py +0 -38
  573. autocoder/sdk/cli/install_completion.py +0 -301
  574. autocoder/sdk/models/messages.py +0 -209
  575. autocoder/sdk/session/__init__.py +0 -32
  576. autocoder/sdk/session/session.py +0 -106
  577. autocoder/sdk/session/session_manager.py +0 -56
  578. {auto_coder-0.1.400.dist-info → auto_coder-2.0.0.dist-info}/top_level.txt +0 -0
  579. /autocoder/{sdk/example.py → common/agent_query_queue/__init__.py} +0 -0
@@ -0,0 +1,1051 @@
1
+ import os
2
+ import shlex
3
+ from typing import Callable, Dict, Any, List, Iterable, Optional
4
+
5
+ import pydantic
6
+ from pydantic import BaseModel, SkipValidation
7
+ from prompt_toolkit.completion import Completer, Completion, CompleteEvent
8
+ from prompt_toolkit.document import Document
9
+
10
+ from autocoder.common import AutoCoderArgs
11
+
12
+ # Only need MemoryConfig now
13
+ from autocoder.common.command_completer import MemoryConfig
14
+ from autocoder.common.llms import LLMManager
15
+ from autocoder.common.command_file_manager import CommandManager
16
+
17
+ # Define command structure in a more structured way if needed,
18
+ # but primarily rely on handlers for logic.
19
+ COMMAND_HIERARCHY = {
20
+ "/add_files": {
21
+ "/group": {"/add": {}, "/drop": {}, "/reset": {}, "/set": {}},
22
+ "/refresh": {},
23
+ },
24
+ "/remove_files": {"/all": {}},
25
+ # Added list/get for clarity
26
+ "/conf": {"/drop": {}, "/export": {}, "/import": {}, "/get": {}},
27
+ "/coding": {"/apply": {}, "/next": {}},
28
+ "/chat": {
29
+ "/new": {},
30
+ "/save": {},
31
+ "/copy": {},
32
+ "/mcp": {},
33
+ "/rag": {},
34
+ "/review": {},
35
+ "/learn": {},
36
+ "/no_context": {},
37
+ },
38
+ "/mcp": {
39
+ "/add": {},
40
+ "/remove": {},
41
+ "/list": {},
42
+ "/list_running": {},
43
+ "/refresh": {},
44
+ "/info": {},
45
+ },
46
+ "/lib": {
47
+ "/add": {},
48
+ "/remove": {},
49
+ "/list": {},
50
+ "/set-proxy": {},
51
+ "/refresh": {},
52
+ "/get": {},
53
+ },
54
+ "/models": {
55
+ "/chat": {},
56
+ "/add_provider": {},
57
+ "/remove": {},
58
+ "/list": {},
59
+ "/check": {},
60
+ "/speed": {},
61
+ "/speed-test": {},
62
+ "/input_price": {},
63
+ "/output_price": {},
64
+ },
65
+ "/auto": {
66
+ "/new": {},
67
+ "/resume": {},
68
+ "/list": {},
69
+ "/rename": {},
70
+ "/command": {},
71
+ "/async": {
72
+ "/model": {},
73
+ "/list": {},
74
+ "/task": {},
75
+ "/kill": {},
76
+ "/effect": {},
77
+ "/name": {},
78
+ "/prefix": {},
79
+ "/libs": {},
80
+ },
81
+ "/queue": {
82
+ "/add": {},
83
+ "/list": {},
84
+ "/remove": {},
85
+ "/start": {},
86
+ "/stop": {},
87
+ "/stats": {},
88
+ "/clear": {},
89
+ "/status": {},
90
+ },
91
+ },
92
+ "/shell": {"/chat": {}},
93
+ "/active_context": {"/list": {}, "/run": {}},
94
+ "/index": {"/query": {}, "/build": {}, "/export": {}, "/import": {}},
95
+ "/exclude_files": {"/list": {}, "/drop": {}},
96
+ "/exclude_dirs": {}, # No specific subcommands shown in V1, treat as simple list
97
+ "/commit": {}, # No specific subcommands shown in V1
98
+ "/revert": {},
99
+ "/ask": {},
100
+ "/design": {"/svg": {}, "/sd": {}, "/logo": {}},
101
+ "/summon": {},
102
+ "/mode": {}, # Simple value completion
103
+ "/voice_input": {},
104
+ "/exit": {},
105
+ "/help": {},
106
+ "/list_files": {},
107
+ "/clear": {},
108
+ "/cls": {},
109
+ "/debug": {},
110
+ "/rules": {
111
+ "/list": {},
112
+ "/get": {},
113
+ "/remove": {},
114
+ "/analyze": {},
115
+ "/commit": {},
116
+ "/help": {},
117
+ "/init": {},
118
+ },
119
+ }
120
+
121
+
122
+ class CommandCompleterV2(Completer):
123
+ """
124
+ A more extensible command completer using a handler-based approach.
125
+ """
126
+
127
+ def __init__(
128
+ self,
129
+ commands: List[str],
130
+ memory_model: MemoryConfig,
131
+ project_root: Optional[str] = None,
132
+ ):
133
+ self.base_commands = commands # Top-level commands starting with /
134
+ self.memory_model = memory_model
135
+
136
+ # Initialize project_scanner
137
+ from autocoder.common.project_scanner import ProjectScanner
138
+
139
+ self.project_root = project_root or os.getcwd()
140
+ self.scanner = ProjectScanner(
141
+ project_root=self.project_root,
142
+ default_exclude_dirs=[
143
+ ".git",
144
+ "node_modules",
145
+ "dist",
146
+ "build",
147
+ "__pycache__",
148
+ ".auto-coder",
149
+ ],
150
+ )
151
+
152
+ # Set extra exclude dirs if available
153
+ if hasattr(memory_model, "get_memory_func"):
154
+ memory = memory_model.get_memory_func()
155
+ extra_exclude_dirs = memory.get("exclude_dirs", [])
156
+ self.scanner.update_extra_exclude_dirs(extra_exclude_dirs)
157
+
158
+ # Data stores, initialized and refreshable
159
+ self.all_file_names: List[str] = []
160
+ self.all_files: List[str] = []
161
+ # Store relative paths with ./ prefix
162
+ self.all_file_rel_paths: List[str] = []
163
+ self.all_dir_names: List[str] = []
164
+ # Store directory relative paths with ./ prefix
165
+ self.all_dir_rel_paths: List[str] = []
166
+ self.all_files_with_dot: List[str] = []
167
+ # Use Any for SymbolItem structure from runner
168
+ self.symbol_list: List[Any] = []
169
+ self.current_file_names: List[str] = []
170
+ # Current files with ./ prefix
171
+ self.current_file_rel_paths: List[str] = []
172
+ self.config_keys = list(AutoCoderArgs.model_fields.keys())
173
+ self.group_names: List[str] = []
174
+ self.lib_names: List[str] = []
175
+ self.model_names: List[str] = [] # Assuming models can be fetched
176
+
177
+ self.refresh_files() # Initial data load
178
+ self._update_dynamic_data() # Load groups, libs etc.
179
+
180
+ # Initialize CommandManager and command files cache
181
+ commands_dir = os.path.join(self.project_root, ".autocodercommands")
182
+ self.command_manager = CommandManager(commands_dir)
183
+ self.command_files_cache: List[str] = []
184
+ self.command_files_cache_timestamp = 0
185
+ self._update_command_files_cache()
186
+
187
+ # Map command prefixes or patterns to handler methods
188
+ self.command_handlers: Dict[str, Callable] = {
189
+ "/": self._handle_base_command,
190
+ "/add_files": self._handle_add_files,
191
+ "/remove_files": self._handle_remove_files,
192
+ "/exclude_dirs": self._handle_exclude_dirs,
193
+ "/exclude_files": self._handle_exclude_files,
194
+ "/conf": self._handle_conf,
195
+ "/lib": self._handle_lib,
196
+ "/mcp": self._handle_mcp,
197
+ "/models": self._handle_models,
198
+ "/active_context": self._handle_active_context,
199
+ "/mode": self._handle_mode,
200
+ "/chat": self._handle_text_with_symbols,
201
+ "/coding": self._handle_text_with_symbols,
202
+ "/auto": self._handle_auto,
203
+ "/ask": self._handle_text_with_symbols, # Treat like chat for @/@@
204
+ "/summon": self._handle_text_with_symbols,
205
+ "/design": self._handle_design,
206
+ "/rules": self._handle_rules,
207
+ # Add handlers for other commands if they need specific logic beyond @/@@
208
+ # Default handler for plain text or commands not explicitly handled
209
+ "default": self._handle_text_with_symbols,
210
+ }
211
+
212
+ def _update_dynamic_data(self):
213
+ """Load or update data that changes during runtime (groups, libs, current files)."""
214
+ self.current_file_names = (
215
+ self.memory_model.get_memory_func()
216
+ .get("current_files", {})
217
+ .get("files", [])
218
+ )
219
+
220
+ # Generate relative paths with ./ prefix for current files
221
+ self.current_file_rel_paths = []
222
+ for f in self.current_file_names:
223
+ rel_path = os.path.relpath(f, self.project_root)
224
+ if not rel_path.startswith("."):
225
+ rel_path = f"./{rel_path}"
226
+ self.current_file_rel_paths.append(rel_path)
227
+
228
+ self.group_names = list(
229
+ self.memory_model.get_memory_func()
230
+ .get("current_files", {})
231
+ .get("groups", {})
232
+ .keys()
233
+ )
234
+ self.lib_names = list(
235
+ self.memory_model.get_memory_func().get("libs", {}).keys()
236
+ )
237
+ # In a real scenario, might fetch model names from models_module
238
+ try:
239
+ llm_manager = LLMManager()
240
+ self.model_names = list(llm_manager.get_all_models().keys())
241
+ except ImportError:
242
+ self.model_names = [] # Fallback if models module not available
243
+
244
+ # Update scanner's extra exclude dirs if changed
245
+ memory = self.memory_model.get_memory_func()
246
+ extra_exclude_dirs = memory.get("exclude_dirs", [])
247
+ self.scanner.update_extra_exclude_dirs(extra_exclude_dirs)
248
+
249
+ def refresh_files(self):
250
+ """Refresh file and symbol lists from the project scanner."""
251
+ self.all_file_names = self.scanner.get_all_file_names()
252
+ self.all_files = self.scanner.get_all_file_paths()
253
+ self.all_dir_names = self.scanner.get_all_dir_paths()
254
+ self.symbol_list = self.scanner.get_symbol_list()
255
+
256
+ # Generate all_files_with_dot (relative paths starting with ./)
257
+ self.all_files_with_dot = []
258
+ for f in self.all_files:
259
+ rel_path = os.path.relpath(f, self.project_root)
260
+ if not rel_path.startswith("."):
261
+ rel_path = f"./{rel_path}"
262
+ self.all_files_with_dot.append(rel_path)
263
+
264
+ # Generate relative paths with ./ prefix
265
+ self.all_file_rel_paths = []
266
+ for f in self.all_files:
267
+ rel_path = os.path.relpath(f, self.project_root)
268
+ if not rel_path.startswith("."):
269
+ rel_path = f"./{rel_path}"
270
+ self.all_file_rel_paths.append(rel_path)
271
+
272
+ self.all_dir_rel_paths = []
273
+ for d in self.all_dir_names:
274
+ rel_path = os.path.relpath(d, self.project_root)
275
+ if not rel_path.startswith("."):
276
+ rel_path = f"./{rel_path}"
277
+ self.all_dir_rel_paths.append(rel_path)
278
+
279
+ self._update_dynamic_data() # Also refresh dynamic data
280
+ self._update_command_files_cache() # Also refresh command files cache
281
+
282
+ def _update_command_files_cache(self):
283
+ """更新命令文件缓存"""
284
+ try:
285
+ # 检查目录是否存在
286
+ if not os.path.exists(self.command_manager.commands_dir):
287
+ self.command_files_cache = []
288
+ return
289
+
290
+ # 获取目录修改时间
291
+ dir_mtime = os.path.getmtime(self.command_manager.commands_dir)
292
+
293
+ # 如果缓存过期或为空,重新加载
294
+ if (
295
+ dir_mtime > self.command_files_cache_timestamp
296
+ or not self.command_files_cache
297
+ ):
298
+ result = self.command_manager.list_command_files(recursive=True)
299
+ if result.success:
300
+ self.command_files_cache = result.command_files
301
+ self.command_files_cache_timestamp = dir_mtime
302
+ else:
303
+ self.command_files_cache = []
304
+ except Exception:
305
+ # 如果出错,清空缓存
306
+ self.command_files_cache = []
307
+
308
+ # --- Main Completion Logic ---
309
+
310
+ def get_completions(
311
+ self, document: Document, complete_event: CompleteEvent
312
+ ) -> Iterable[Completion]:
313
+ text = document.text_before_cursor
314
+ word_before_cursor = document.get_word_before_cursor(WORD=True)
315
+
316
+ # Update dynamic data on each completion request
317
+ self._update_dynamic_data()
318
+
319
+ if not text.strip(): # Empty input
320
+ yield from self._handle_base_command(
321
+ document, complete_event, word_before_cursor, text
322
+ )
323
+ return
324
+
325
+ parts = text.split(maxsplit=1)
326
+ first_word = parts[0]
327
+
328
+ # 1. Handle Base Command Completion (e.g., typing "/")
329
+ if first_word.startswith("/") and len(parts) == 1 and not text.endswith(" "):
330
+ yield from self._handle_base_command(
331
+ document, complete_event, word_before_cursor, text
332
+ )
333
+
334
+ # 2. Dispatch to Specific Command Handlers
335
+ elif first_word in self.command_handlers:
336
+ handler = self.command_handlers[first_word]
337
+ yield from handler(document, complete_event, word_before_cursor, text)
338
+
339
+ # 3. Handle Special Prefixes within general text or unhandled commands
340
+ elif word_before_cursor.startswith("@") and not word_before_cursor.startswith(
341
+ "@@"
342
+ ):
343
+ yield from self._handle_at_completion(
344
+ document, complete_event, word_before_cursor, text
345
+ )
346
+ elif word_before_cursor.startswith("@@"):
347
+ yield from self._handle_double_at_completion(
348
+ document, complete_event, word_before_cursor, text
349
+ )
350
+ elif word_before_cursor.startswith("<"): # Potential tag completion
351
+ yield from self._handle_img_tag(
352
+ document, complete_event, word_before_cursor, text
353
+ )
354
+
355
+ # 4. Default Handler (for plain text or commands without specific handlers)
356
+ else:
357
+ handler = self.command_handlers.get("default")
358
+ if handler:
359
+ yield from handler(document, complete_event, word_before_cursor, text)
360
+
361
+ # --- Handler Methods ---
362
+
363
+ def _handle_base_command(
364
+ self, document: Document, complete_event: CompleteEvent, word: str, text: str
365
+ ) -> Iterable[Completion]:
366
+ """Handles completion for top-level commands starting with '/'."""
367
+ command_prefix = text.lstrip() # The word being typed
368
+ for cmd in self.base_commands:
369
+ if cmd.startswith(command_prefix):
370
+ yield Completion(cmd, start_position=-len(command_prefix))
371
+
372
+ def _handle_add_files(
373
+ self, document: Document, complete_event: CompleteEvent, word: str, text: str
374
+ ) -> Iterable[Completion]:
375
+ """Handles completions for /add_files command."""
376
+ args_text = text[len("/add_files") :].lstrip()
377
+ parts = args_text.split()
378
+ last_part = parts[-1] if parts and not text.endswith(" ") else ""
379
+
380
+ # Sub-command completion
381
+ if not args_text or (len(parts) == 1 and not text.endswith(" ")):
382
+ for sub_cmd in COMMAND_HIERARCHY["/add_files"]:
383
+ if sub_cmd.startswith(last_part):
384
+ yield Completion(sub_cmd, start_position=-len(last_part))
385
+
386
+ # File/Group completion based on context
387
+ if args_text.startswith("/group"):
388
+ group_args_text = args_text[len("/group") :].lstrip()
389
+ group_parts = group_args_text.split()
390
+ group_last_part = (
391
+ group_parts[-1] if group_parts and not text.endswith(" ") else ""
392
+ )
393
+
394
+ # Complete subcommands of /group
395
+ if not group_args_text or (
396
+ len(group_parts) == 1 and not text.endswith(" ")
397
+ ):
398
+ for group_sub_cmd in COMMAND_HIERARCHY["/add_files"]["/group"]:
399
+ if group_sub_cmd.startswith(group_last_part):
400
+ yield Completion(
401
+ group_sub_cmd, start_position=-len(group_last_part)
402
+ )
403
+
404
+ # Complete group names for /drop or direct use
405
+ elif (
406
+ group_parts
407
+ and group_parts[0] in ["/drop", "/set"]
408
+ or len(group_parts) >= 1
409
+ and not group_parts[0].startswith("/")
410
+ ):
411
+ current_word_for_group = group_last_part
412
+ # Handle comma-separated group names
413
+ if "," in current_word_for_group:
414
+ current_word_for_group = current_word_for_group.split(",")[-1]
415
+
416
+ yield from self._complete_items(
417
+ current_word_for_group, self.group_names
418
+ )
419
+
420
+ elif args_text.startswith("/refresh"):
421
+ pass # No further completion needed
422
+
423
+ # Default: File path completion
424
+ else:
425
+ yield from self._complete_file_paths(word, text)
426
+
427
+ def _handle_remove_files(
428
+ self, document: Document, complete_event: CompleteEvent, word: str, text: str
429
+ ) -> Iterable[Completion]:
430
+ """Handles completions for /remove_files command."""
431
+ # 'word' is document.get_word_before_cursor(WORD=True)
432
+
433
+ # Complete /all subcommand
434
+ if "/all".startswith(word):
435
+ yield Completion("/all", start_position=-len(word))
436
+
437
+ # Complete from current file paths with ./ prefix
438
+ yield from self._complete_items_with_in(word, self.current_file_rel_paths)
439
+
440
+ # Also complete from just the base filenames (but with full paths)
441
+ for rel_path in self.current_file_rel_paths:
442
+ basename = os.path.basename(rel_path)
443
+ if word in basename and word not in rel_path:
444
+ # Show full path when matching by basename
445
+ yield Completion(rel_path, start_position=-len(word))
446
+
447
+ def _handle_exclude_dirs(
448
+ self, document: Document, complete_event: CompleteEvent, word: str, text: str
449
+ ) -> Iterable[Completion]:
450
+ """Handles completions for /exclude_dirs command."""
451
+ args_text = text[len("/exclude_dirs") :].lstrip()
452
+ current_word = args_text.split(",")[-1].strip()
453
+ yield from self._complete_items(current_word, self.all_dir_names)
454
+
455
+ def _handle_exclude_files(
456
+ self, document: Document, complete_event: CompleteEvent, word: str, text: str
457
+ ) -> Iterable[Completion]:
458
+ """Handles completions for /exclude_files command."""
459
+ args_text = text[len("/exclude_files") :].lstrip()
460
+ parts = args_text.split()
461
+ last_part = parts[-1] if parts and not text.endswith(" ") else ""
462
+
463
+ if not args_text or (len(parts) == 1 and not text.endswith(" ")):
464
+ for sub_cmd in COMMAND_HIERARCHY["/exclude_files"]:
465
+ if sub_cmd.startswith(last_part):
466
+ yield Completion(sub_cmd, start_position=-len(last_part))
467
+
468
+ elif parts and parts[0] == "/drop":
469
+ current_word = last_part
470
+ yield from self._complete_items(
471
+ current_word,
472
+ self.memory_model.get_memory_func().get("exclude_files", []),
473
+ )
474
+ else:
475
+ # Suggest prefix for regex
476
+ if not last_part:
477
+ yield Completion("regex://", start_position=0)
478
+ elif "regex://".startswith(last_part):
479
+ yield Completion("regex://", start_position=-len(last_part))
480
+
481
+ def _handle_conf(
482
+ self, document: Document, complete_event: CompleteEvent, word: str, text: str
483
+ ) -> Iterable[Completion]:
484
+ """Handles completions for /conf command."""
485
+ args_text = text[len("/conf") :].lstrip()
486
+ parts = args_text.split()
487
+ last_part = parts[-1] if parts and not text.endswith(" ") else ""
488
+ # Complete subcommands like /drop, /export, /import, /list, /get
489
+ if not args_text or (
490
+ len(parts) == 1 and not text.endswith(" ") and ":" not in text
491
+ ):
492
+ for sub_cmd in COMMAND_HIERARCHY["/conf"]:
493
+ if sub_cmd.startswith(last_part):
494
+ yield Completion(sub_cmd, start_position=-len(last_part))
495
+ # Also complete config keys directly
496
+ yield from self._complete_config_keys(last_part, add_colon=False)
497
+
498
+ # Complete config keys after /drop or /get
499
+ elif parts and parts[0] in ["/drop", "/get"]:
500
+ yield from self._complete_config_keys(last_part, add_colon=False)
501
+
502
+ # Complete file paths after /export or /import
503
+ elif parts and parts[0] in ["/export", "/import"]:
504
+ # Use word here as it's likely the path
505
+ yield from self._complete_file_paths(word, text)
506
+
507
+ # Complete config keys for setting (key:value)
508
+ elif ":" not in last_part:
509
+ yield from self._complete_config_keys(last_part, add_colon=True)
510
+
511
+ # Complete values after colon
512
+ elif ":" in args_text:
513
+ key_part = args_text.split(":", 1)[0].strip()
514
+ value_part = args_text.split(":", 1)[1].strip() if ":" in args_text else ""
515
+ yield from self._complete_config_values(key_part, value_part)
516
+ # Example: Complete enum values or suggest file paths for path-like keys
517
+ pass # Placeholder for future value completions
518
+
519
+ def _complete_config_values(self, key: str, value: str) -> Iterable[Completion]:
520
+ """Helper to complete configuration values based on the key."""
521
+ start_pos = -len(value)
522
+
523
+ # Model name completion for keys containing "model"
524
+ if key.endswith("_model") or key == "model":
525
+ # Refresh model names if they can change dynamically
526
+ # self.refresh_model_names()
527
+ for model_name in self.model_names:
528
+ if model_name.startswith(value) or value == ":":
529
+ yield Completion(model_name, start_position=start_pos)
530
+ # If a model name matched, we might prioritize these completions.
531
+ # Consider returning here if model names are the only relevant values.
532
+
533
+ # Boolean value completion
534
+ field_info = AutoCoderArgs.model_fields.get(key)
535
+ if field_info and field_info.annotation == bool:
536
+ if "true".startswith(value):
537
+ yield Completion("true", start_position=start_pos)
538
+ if "false".startswith(value):
539
+ yield Completion("false", start_position=start_pos)
540
+ # If boolean matched, we might prioritize these completions.
541
+ # Consider returning here if boolean is the only relevant value type.
542
+
543
+ # Add more value completions based on key type or name here
544
+ # e.g., enums, file paths, specific string formats
545
+
546
+ def _handle_lib(
547
+ self, document: Document, complete_event: CompleteEvent, word: str, text: str
548
+ ) -> Iterable[Completion]:
549
+ """Handles completions for /lib command."""
550
+ args_text = text[len("/lib") :].lstrip()
551
+ parts = args_text.split()
552
+ last_part = parts[-1] if parts and not text.endswith(" ") else ""
553
+
554
+ # Complete subcommands
555
+ if not args_text or (len(parts) == 1 and not text.endswith(" ")):
556
+ for sub_cmd in COMMAND_HIERARCHY["/lib"]:
557
+ if sub_cmd.startswith(last_part):
558
+ yield Completion(sub_cmd, start_position=-len(last_part))
559
+
560
+ # Complete lib names for add/remove/get
561
+ elif parts and parts[0] in ["/add", "/remove", "/get"]:
562
+ yield from self._complete_items(last_part, self.lib_names)
563
+
564
+ # Complete proxy URL for set-proxy (less specific, maybe suggest http/https?)
565
+ elif parts and parts[0] == "/set-proxy":
566
+ if "http://".startswith(last_part):
567
+ yield Completion("http://", start_position=-len(last_part))
568
+ if "https://".startswith(last_part):
569
+ yield Completion("https://", start_position=-len(last_part))
570
+
571
+ def _handle_mcp(
572
+ self, document: Document, complete_event: CompleteEvent, word: str, text: str
573
+ ) -> Iterable[Completion]:
574
+ """Handles completions for /mcp command."""
575
+ args_text = text[len("/mcp") :].lstrip()
576
+ parts = args_text.split()
577
+ last_part = parts[-1] if parts and not text.endswith(" ") else ""
578
+
579
+ # Complete subcommands
580
+ if not args_text or (len(parts) == 1 and not text.endswith(" ")):
581
+ for sub_cmd in COMMAND_HIERARCHY["/mcp"]:
582
+ if sub_cmd.startswith(last_part):
583
+ yield Completion(sub_cmd, start_position=-len(last_part))
584
+ # Potentially complete server names after /remove, /refresh, /add if available
585
+
586
+ def _handle_models(
587
+ self, document: Document, complete_event: CompleteEvent, word: str, text: str
588
+ ) -> Iterable[Completion]:
589
+ """Handles completions for /models command."""
590
+ args_text = text[len("/models") :].lstrip()
591
+ parts = args_text.split()
592
+ last_part = parts[-1] if parts and not text.endswith(" ") else ""
593
+
594
+ # Complete subcommands
595
+ if not args_text or (len(parts) == 1 and not text.endswith(" ")):
596
+ for sub_cmd in COMMAND_HIERARCHY["/models"]:
597
+ if sub_cmd.startswith(last_part):
598
+ yield Completion(sub_cmd, start_position=-len(last_part))
599
+
600
+ # 如果没有子命令匹配,提示可以直接输入 provider/model_name
601
+ if not last_part.startswith("/"):
602
+ yield from self._complete_items(last_part, self.model_names)
603
+
604
+ # Complete model names for /remove/speed/input_price/output_price/chat
605
+ elif parts and parts[0] in [
606
+ "/remove",
607
+ "/speed",
608
+ "/input_price",
609
+ "/output_price",
610
+ "/chat",
611
+ ]:
612
+ yield from self._complete_items(last_part, self.model_names)
613
+
614
+ # Complete parameters for /add_provider (e.g., name=, base_url=)
615
+ elif parts and parts[0] == "/add_provider":
616
+ # Suggest common keys if the last part is empty or partially typed
617
+ common_keys = [
618
+ "name=",
619
+ "model_type=",
620
+ "model_name=",
621
+ "provider=",
622
+ "base_url=",
623
+ "api_key_path=",
624
+ "description=",
625
+ "is_reasoning=",
626
+ "input_price=",
627
+ "output_price=",
628
+ "context_window=",
629
+ "max_output_tokens=",
630
+ ]
631
+ yield from self._complete_items(last_part, common_keys)
632
+
633
+ elif parts and parts[0] == "/speed-test":
634
+ if "/long_context".startswith(last_part):
635
+ yield Completion("/long_context", start_position=-len(last_part))
636
+
637
+ # 如果第一个参数不是子命令,则是 provider/model_name 格式,补全模型名称
638
+ elif parts and not parts[0].startswith("/"):
639
+ # 用户正在输入 provider/model_name,补全第二个参数时不做提示
640
+ if len(parts) == 1 and not text.endswith(" "):
641
+ yield from self._complete_items(last_part, self.model_names)
642
+
643
+ def _handle_active_context(
644
+ self, document: Document, complete_event: CompleteEvent, word: str, text: str
645
+ ) -> Iterable[Completion]:
646
+ """Handles completions for /active_context command."""
647
+ args_text = text[len("/active_context") :].lstrip()
648
+ parts = args_text.split()
649
+ last_part = parts[-1] if parts and not text.endswith(" ") else ""
650
+
651
+ # Complete subcommands
652
+ if not args_text or (len(parts) == 1 and not text.endswith(" ")):
653
+ for sub_cmd in COMMAND_HIERARCHY["/active_context"]:
654
+ if sub_cmd.startswith(last_part):
655
+ yield Completion(sub_cmd, start_position=-len(last_part))
656
+
657
+ # Complete action file names for /run
658
+ elif parts and parts[0] == "/run":
659
+ # Assuming action files are in 'actions' dir and end with .yml
660
+ action_dir = "actions"
661
+ if os.path.isdir(action_dir):
662
+ try:
663
+ action_files = [
664
+ f for f in os.listdir(action_dir) if f.endswith(".yml")
665
+ ]
666
+ yield from self._complete_items(last_part, action_files)
667
+ except OSError:
668
+ pass # Ignore if cannot list dir
669
+
670
+ def _handle_mode(
671
+ self, document: Document, complete_event: CompleteEvent, word: str, text: str
672
+ ) -> Iterable[Completion]:
673
+ """Handles completions for /mode command."""
674
+ args_text = text[len("/mode") :].lstrip()
675
+ modes = ["normal", "auto_detect", "voice_input", "shell"]
676
+ yield from self._complete_items(args_text, modes)
677
+
678
+ def _handle_design(
679
+ self, document: Document, complete_event: CompleteEvent, word: str, text: str
680
+ ) -> Iterable[Completion]:
681
+ """Handles completions for /design command."""
682
+ args_text = text[len("/design") :].lstrip()
683
+ parts = args_text.split()
684
+ last_part = parts[-1] if parts and not text.endswith(" ") else ""
685
+
686
+ # Complete subcommands
687
+ if not args_text or (len(parts) == 1 and not text.endswith(" ")):
688
+ for sub_cmd in COMMAND_HIERARCHY["/design"]:
689
+ if sub_cmd.startswith(last_part):
690
+ yield Completion(sub_cmd, start_position=-len(last_part))
691
+
692
+ def _handle_auto(
693
+ self, document: Document, complete_event: CompleteEvent, word: str, text: str
694
+ ) -> Iterable[Completion]:
695
+ """Handles completions for /auto command."""
696
+ args_text = text[len("/auto") :].lstrip()
697
+ parts = args_text.split()
698
+ last_part = parts[-1] if parts and not text.endswith(" ") else ""
699
+
700
+ # Complete subcommands
701
+ if not args_text or (len(parts) == 1 and not text.endswith(" ")):
702
+ for sub_cmd in COMMAND_HIERARCHY["/auto"]:
703
+ if sub_cmd.startswith(last_part):
704
+ yield Completion(sub_cmd, start_position=-len(last_part))
705
+ return
706
+
707
+ # Handle /command subcommand
708
+ if args_text.startswith("/command"):
709
+ command_args_text = args_text[len("/command") :].lstrip()
710
+ # Update command files cache
711
+ self._update_command_files_cache()
712
+
713
+ # Complete command file paths
714
+ yield from self._complete_command_files(command_args_text)
715
+ return
716
+
717
+ # Handle @ and @@ symbols in other contexts
718
+ if word.startswith("@") and not word.startswith("@@"):
719
+ yield from self._handle_at_completion(document, complete_event, word, text)
720
+ elif word.startswith("@@"):
721
+ yield from self._handle_double_at_completion(
722
+ document, complete_event, word, text
723
+ )
724
+ elif word.startswith("<"):
725
+ yield from self._handle_img_tag(document, complete_event, word, text)
726
+
727
+ def _handle_text_with_symbols(
728
+ self, document: Document, complete_event: CompleteEvent, word: str, text: str
729
+ ) -> Iterable[Completion]:
730
+ """Handles general text input, including @, @@, <img> tags and command-specific subcommands."""
731
+ # Check for command-specific subcommands first
732
+ parts = text.split(maxsplit=1)
733
+ command = parts[0]
734
+ if command in COMMAND_HIERARCHY:
735
+ args_text = parts[1] if len(parts) > 1 else ""
736
+ sub_parts = args_text.split()
737
+ last_part = sub_parts[-1] if sub_parts and not text.endswith(" ") else ""
738
+
739
+ # Complete subcommands if applicable
740
+ if not args_text or (len(sub_parts) == 1 and not text.endswith(" ")):
741
+ if isinstance(COMMAND_HIERARCHY[command], dict):
742
+ for sub_cmd in COMMAND_HIERARCHY[command]:
743
+ if sub_cmd.startswith(last_part):
744
+ yield Completion(sub_cmd, start_position=-len(last_part))
745
+
746
+ # Now handle @, @@, <img> regardless of command (or if no command)
747
+ if word.startswith("@") and not word.startswith("@@"):
748
+ yield from self._handle_at_completion(document, complete_event, word, text)
749
+ elif word.startswith("@@"):
750
+ yield from self._handle_double_at_completion(
751
+ document, complete_event, word, text
752
+ )
753
+ elif word.startswith("<"): # Potential tag completion
754
+ yield from self._handle_img_tag(document, complete_event, word, text)
755
+
756
+ def _handle_rules(
757
+ self, document: Document, complete_event: CompleteEvent, word: str, text: str
758
+ ) -> Iterable[Completion]:
759
+ """处理 /rules 命令的补全,支持子命令和规则文件路径。同时支持 @ 和 @@ 符号。"""
760
+ args_text = text[len("/rules") :].lstrip()
761
+ parts = args_text.split()
762
+ last_part = parts[-1] if parts and not text.endswith(" ") else ""
763
+
764
+ # 补全子命令
765
+ if not args_text or (
766
+ len(parts) == 1 and not text.endswith(" ") and parts[0].startswith("/")
767
+ ):
768
+ for sub_cmd in COMMAND_HIERARCHY["/rules"]:
769
+ if sub_cmd.startswith(last_part):
770
+ yield Completion(sub_cmd, start_position=-len(last_part))
771
+ return
772
+
773
+ # 根据子命令补全参数
774
+ if parts and parts[0] == "/list" or parts[0] == "/get" or parts[0] == "/remove":
775
+ # 获取规则文件或目录补全,可以是通配符
776
+ # 这里可以简单地提供文件路径补全
777
+ yield from self._complete_file_paths(last_part, text)
778
+ # 也可以添加常用通配符补全
779
+ common_patterns = ["*.md", "*.rules", "*.txt"]
780
+ for pattern in common_patterns:
781
+ if pattern.startswith(last_part):
782
+ yield Completion(pattern, start_position=-len(last_part))
783
+ return
784
+
785
+ # 对于 /commit 子命令,补全 /query
786
+ if parts and parts[0] == "/commit":
787
+ if "/query".startswith(last_part):
788
+ yield Completion("/query", start_position=-len(last_part))
789
+ return
790
+
791
+ # 支持 @ 和 @@ 符号的补全,不管当前命令是什么
792
+ if word.startswith("@") and not word.startswith("@@"):
793
+ yield from self._handle_at_completion(document, complete_event, word, text)
794
+ elif word.startswith("@@"):
795
+ yield from self._handle_double_at_completion(
796
+ document, complete_event, word, text
797
+ )
798
+
799
+ # --- Symbol/Tag Handlers ---
800
+
801
+ def _handle_at_completion(
802
+ self, document: Document, complete_event: CompleteEvent, word: str, text: str
803
+ ) -> Iterable[Completion]:
804
+ """Handles completion for single '@' (file paths)."""
805
+ name = word[1:]
806
+ yield from self._complete_file_paths(name, text, is_symbol=True)
807
+
808
+ def _handle_double_at_completion(
809
+ self, document: Document, complete_event: CompleteEvent, word: str, text: str
810
+ ) -> Iterable[Completion]:
811
+ """Handles completion for double '@@' (symbols)."""
812
+ name = word[2:]
813
+ yield from self._complete_symbols(name)
814
+
815
+ def _handle_img_tag(
816
+ self, document: Document, complete_event: CompleteEvent, word: str, text: str
817
+ ) -> Iterable[Completion]:
818
+ """Handles completion for <img> tags and paths within them."""
819
+ image_extensions = (
820
+ ".png",
821
+ ".jpg",
822
+ ".jpeg",
823
+ ".gif",
824
+ ".bmp",
825
+ ".tiff",
826
+ ".tif",
827
+ ".webp",
828
+ ".svg",
829
+ ".ico",
830
+ ".heic",
831
+ ".heif",
832
+ ".raw",
833
+ ".cr2",
834
+ ".nef",
835
+ ".arw",
836
+ ".dng",
837
+ ".orf",
838
+ ".rw2",
839
+ ".pef",
840
+ ".srw",
841
+ ".eps",
842
+ ".ai",
843
+ ".psd",
844
+ ".xcf",
845
+ )
846
+
847
+ # Basic tag completion
848
+ if "<img".startswith(word):
849
+ yield Completion("<img>", start_position=-len(word))
850
+ if "</img".startswith(word):
851
+ yield Completion("</img>", start_position=-len(word))
852
+
853
+ # Path completion inside <img> tag
854
+ # Find the last opening <img> tag that isn't closed yet
855
+ last_open_img = text.rfind("<img>")
856
+ last_close_img = text.rfind("</img>")
857
+
858
+ if last_open_img != -1 and (
859
+ last_close_img == -1 or last_close_img < last_open_img
860
+ ):
861
+ path_prefix = text[last_open_img + len("<img>") :]
862
+ current_path_word = document.get_word_before_cursor(
863
+ WORD=True
864
+ ) # Path part being typed
865
+
866
+ # Only complete if cursor is within the tag content
867
+ if document.cursor_position > last_open_img + len("<img>"):
868
+
869
+ search_dir = (
870
+ os.path.dirname(path_prefix)
871
+ if os.path.dirname(path_prefix)
872
+ else "."
873
+ )
874
+ file_basename = os.path.basename(current_path_word)
875
+
876
+ try:
877
+ if os.path.isdir(search_dir):
878
+ for item in os.listdir(search_dir):
879
+ full_path = os.path.join(search_dir, item)
880
+ # Suggest directories or image files matching the prefix
881
+ if item.startswith(file_basename):
882
+ if os.path.isdir(full_path):
883
+ relative_path = os.path.relpath(
884
+ full_path, "."
885
+ ) # Use relative path
886
+ yield Completion(
887
+ relative_path + os.sep,
888
+ start_position=-len(current_path_word),
889
+ display=item + "/",
890
+ )
891
+ elif item.lower().endswith(image_extensions):
892
+ relative_path = os.path.relpath(
893
+ full_path, "."
894
+ ) # Use relative path
895
+ yield Completion(
896
+ relative_path,
897
+ start_position=-len(current_path_word),
898
+ display=item,
899
+ )
900
+ except OSError:
901
+ pass # Ignore errors listing directories
902
+
903
+ # --- Helper Methods ---
904
+
905
+ def _complete_command_files(self, word: str) -> Iterable[Completion]:
906
+ """Complete command files from .autocodercommands directory."""
907
+ if word is None:
908
+ word = ""
909
+
910
+ start_pos = -len(word)
911
+
912
+ # Complete files that start with the word
913
+ for command_file in self.command_files_cache:
914
+ if command_file.startswith(word):
915
+ display_name = command_file
916
+ # Add visual indicator for command files
917
+ if command_file.endswith(".md"):
918
+ display_name = f"{command_file} (command)"
919
+ yield Completion(
920
+ command_file, start_position=start_pos, display=display_name
921
+ )
922
+ continue
923
+
924
+ # If no exact prefix matches, try partial matches
925
+ if word:
926
+ for command_file in self.command_files_cache:
927
+ # Match by basename
928
+ basename = os.path.basename(command_file)
929
+ if word in basename and not command_file.startswith(word):
930
+ display_name = f"{command_file} (command)"
931
+ yield Completion(
932
+ command_file, start_position=start_pos, display=display_name
933
+ )
934
+
935
+ def _complete_items_with_in(
936
+ self, word: str, items: Iterable[str]
937
+ ) -> Iterable[Completion]:
938
+ """Generic helper to complete a word from a list of items."""
939
+ for item in items:
940
+ if item and word in item:
941
+ yield Completion(item, start_position=-len(word))
942
+
943
+ def _complete_items(self, word: str, items: Iterable[str]) -> Iterable[Completion]:
944
+ """Generic helper to complete a word from a list of items."""
945
+ if word is None:
946
+ word = ""
947
+ for item in items:
948
+ if item and item.startswith(word):
949
+ yield Completion(item, start_position=-len(word))
950
+
951
+ def _complete_config_keys(
952
+ self, word: str, add_colon: bool = False
953
+ ) -> Iterable[Completion]:
954
+ """Helper to complete configuration keys."""
955
+ suffix = ":" if add_colon else ""
956
+ for key in self.config_keys:
957
+ if key.startswith(word):
958
+ yield Completion(key + suffix, start_position=-len(word))
959
+
960
+ def _complete_file_paths(
961
+ self, name: str, text: str, is_symbol: bool = False
962
+ ) -> Iterable[Completion]:
963
+ """Helper to complete file paths (@ completion or general path)."""
964
+ if name is None:
965
+ name = ""
966
+ start_pos = -len(name)
967
+
968
+ # Prioritize active files if triggered by @
969
+ if is_symbol:
970
+ for rel_path in self.current_file_rel_paths:
971
+ if name in rel_path or name in os.path.basename(rel_path):
972
+ yield Completion(
973
+ rel_path,
974
+ start_position=start_pos,
975
+ display=f"{rel_path} (active)",
976
+ )
977
+
978
+ # General file path completion (relative paths with dot)
979
+ if name.startswith("."):
980
+ yield from self._complete_items(name, self.all_files_with_dot)
981
+ # Also complete directories starting with dot
982
+ for rel_path in self.all_dir_rel_paths:
983
+ if rel_path.startswith(name):
984
+ yield Completion(
985
+ rel_path + os.sep,
986
+ start_position=start_pos,
987
+ display=f"{rel_path}/ (dir)",
988
+ )
989
+ return # Don't mix with other completions if starting with .
990
+
991
+ # Complete directory names first (higher priority)
992
+ for rel_path in self.all_dir_rel_paths:
993
+ dir_basename = os.path.basename(rel_path)
994
+
995
+ # Match by basename or full path
996
+ if name and (name in dir_basename or name in rel_path):
997
+ # Always complete with full relative path
998
+ yield Completion(
999
+ rel_path + os.sep,
1000
+ start_position=start_pos,
1001
+ display=f"{rel_path}/ (dir)",
1002
+ )
1003
+ elif not name:
1004
+ # Show all directories when no filter
1005
+ yield Completion(
1006
+ rel_path + os.sep,
1007
+ start_position=start_pos,
1008
+ display=f"{rel_path}/ (dir)",
1009
+ )
1010
+
1011
+ # Complete file names with full paths
1012
+ for rel_path in self.all_file_rel_paths:
1013
+ file_basename = os.path.basename(rel_path)
1014
+
1015
+ # Match by basename or full path
1016
+ if name and (name in file_basename or name in rel_path):
1017
+ # Skip if already shown as active
1018
+ if rel_path not in self.current_file_rel_paths:
1019
+ yield Completion(
1020
+ rel_path, start_position=start_pos, display=rel_path
1021
+ )
1022
+
1023
+ def _complete_symbols(self, name: str) -> Iterable[Completion]:
1024
+ """Helper to complete symbols (@@ completion)."""
1025
+ if name is None:
1026
+ name = ""
1027
+ start_pos = -len(name)
1028
+ for symbol in self.symbol_list:
1029
+ # Assuming symbol has attributes symbol_name, file_name, symbol_type
1030
+ if name in symbol.symbol_name:
1031
+ file_name = symbol.file_name
1032
+ display_name = self._get_display_path(file_name)
1033
+ display_text = (
1034
+ f"{symbol.symbol_name} ({display_name}/{symbol.symbol_type})"
1035
+ )
1036
+ completion_text = (
1037
+ f"{symbol.symbol_name} ({display_name}/{symbol.symbol_type})"
1038
+ )
1039
+ yield Completion(
1040
+ completion_text, start_position=start_pos, display=display_text
1041
+ )
1042
+
1043
+ def _get_display_path(self, file_path: str, max_parts: int = 3) -> str:
1044
+ """Helper to create a display path. Now returns full relative path."""
1045
+ try:
1046
+ # Always return full relative path for clarity
1047
+ rel_path = os.path.relpath(file_path, self.project_root)
1048
+ return rel_path
1049
+ # Handle cases where paths are not relative (e.g., different drives on Windows)
1050
+ except ValueError:
1051
+ return file_path