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