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.
- auto_coder-2.0.0.dist-info/LICENSE +158 -0
- auto_coder-2.0.0.dist-info/METADATA +558 -0
- auto_coder-2.0.0.dist-info/RECORD +795 -0
- {auto_coder-0.1.400.dist-info → auto_coder-2.0.0.dist-info}/WHEEL +1 -1
- {auto_coder-0.1.400.dist-info → auto_coder-2.0.0.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 +25 -4
- 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 +73 -59
- autocoder/auto_coder.py +31 -40
- autocoder/auto_coder_rag.py +11 -1084
- autocoder/auto_coder_runner.py +1029 -2310
- 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 +1021 -372
- autocoder/chat_auto_coder_lang.py +23 -732
- autocoder/commands/auto_command.py +26 -9
- 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 +401 -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/file_monitor/test_file_monitor.py +307 -0
- autocoder/common/git_utils.py +51 -10
- 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 +288 -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 +746 -0
- autocoder/common/{context_pruner.py → pruner/context_pruner.py} +137 -40
- 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/{conversation_pruner.py → pruner/conversation_pruner.py} +26 -6
- autocoder/common/pruner/test_agentic_conversation_pruner.py +784 -0
- autocoder/common/pruner/test_context_pruner.py +546 -0
- 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/__init__.py +15 -0
- autocoder/common/tokens/counter.py +44 -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 +2729 -2052
- autocoder/common/v2/agent/agentic_edit_change_manager.py +474 -0
- autocoder/common/v2/agent/agentic_edit_tools/__init__.py +43 -2
- autocoder/common/v2/agent/agentic_edit_tools/ac_mod_list_tool_resolver.py +279 -0
- autocoder/common/v2/agent/agentic_edit_tools/ac_mod_read_tool_resolver.py +40 -0
- autocoder/common/v2/agent/agentic_edit_tools/ac_mod_write_tool_resolver.py +52 -0
- autocoder/common/v2/agent/agentic_edit_tools/ask_followup_question_tool_resolver.py +8 -0
- 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 +565 -30
- 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 +349 -0
- autocoder/common/v2/agent/agentic_edit_tools/read_file_tool_resolver.py +244 -51
- 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 +409 -140
- 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 +209 -194
- autocoder/common/v2/agent/agentic_edit_tools/todo_read_tool_resolver.py +135 -0
- autocoder/common/v2/agent/agentic_edit_tools/todo_write_tool_resolver.py +328 -0
- 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 +386 -10
- autocoder/common/v2/agent/runner/__init__.py +31 -0
- autocoder/common/v2/agent/runner/base_runner.py +92 -0
- autocoder/common/v2/agent/runner/file_based_event_runner.py +217 -0
- autocoder/common/v2/agent/runner/sdk_runner.py +182 -0
- autocoder/common/v2/agent/runner/terminal_runner.py +396 -0
- autocoder/common/v2/agent/runner/tool_display.py +589 -0
- 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 +1051 -0
- autocoder/default_project/__init__.py +501 -0
- autocoder/dispacher/__init__.py +4 -12
- autocoder/dispacher/actions/action.py +165 -7
- autocoder/dispacher/actions/plugins/action_regex_project.py +2 -2
- autocoder/index/entry.py +117 -125
- autocoder/{agent → index/filter}/agentic_filter.py +323 -334
- 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 +932 -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 +156 -37
- 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/run_context.py +9 -0
- autocoder/sdk/__init__.py +50 -161
- 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 +158 -170
- autocoder/sdk/cli/options.py +95 -22
- autocoder/sdk/constants.py +139 -51
- autocoder/sdk/core/auto_coder_core.py +484 -267
- autocoder/sdk/core/bridge.py +298 -118
- 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 +489 -0
- autocoder/workflow_agents/loader.py +737 -0
- autocoder/workflow_agents/runner.py +267 -0
- autocoder/workflow_agents/types.py +172 -0
- autocoder/workflow_agents/utils.py +434 -0
- autocoder/workflow_agents/workflow_manager.py +211 -0
- auto_coder-0.1.400.dist-info/METADATA +0 -396
- auto_coder-0.1.400.dist-info/RECORD +0 -425
- auto_coder-0.1.400.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/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-0.1.400.dist-info → auto_coder-2.0.0.dist-info}/top_level.txt +0 -0
- /autocoder/{sdk/example.py → common/agent_query_queue/__init__.py} +0 -0
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Hook executor for command execution with variable substitution and process management.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
import json
|
|
7
|
+
import os
|
|
8
|
+
import re
|
|
9
|
+
import shlex
|
|
10
|
+
import subprocess
|
|
11
|
+
import time
|
|
12
|
+
from typing import Dict, List, Optional, Any
|
|
13
|
+
|
|
14
|
+
from loguru import logger
|
|
15
|
+
from autocoder.common.async_utils import AsyncSyncMixin
|
|
16
|
+
from ..agent_events.types import EventMessage
|
|
17
|
+
from .types import Hook, HookExecutionResult, HookType
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class HookExecutor(AsyncSyncMixin):
|
|
21
|
+
"""
|
|
22
|
+
Command execution engine with variable substitution and environment control.
|
|
23
|
+
|
|
24
|
+
Provides both async and sync methods for all operations.
|
|
25
|
+
Sync methods are automatically generated with a '_sync' suffix.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def __init__(self, cwd: Optional[str] = None, timeout: int = 30000,
|
|
29
|
+
env: Optional[Dict[str, str]] = None):
|
|
30
|
+
"""
|
|
31
|
+
Initialize the hook executor.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
cwd: Working directory for command execution
|
|
35
|
+
timeout: Command timeout in milliseconds
|
|
36
|
+
env: Additional environment variables
|
|
37
|
+
"""
|
|
38
|
+
self.cwd = cwd or os.getcwd()
|
|
39
|
+
self.timeout = timeout / 1000.0 # Convert to seconds
|
|
40
|
+
self.env = env or {}
|
|
41
|
+
|
|
42
|
+
def set_cwd(self, cwd: str) -> None:
|
|
43
|
+
"""Set working directory for command execution."""
|
|
44
|
+
self.cwd = cwd
|
|
45
|
+
|
|
46
|
+
def set_timeout(self, timeout: int) -> None:
|
|
47
|
+
"""Set command execution timeout in milliseconds."""
|
|
48
|
+
self.timeout = timeout / 1000.0
|
|
49
|
+
|
|
50
|
+
def set_env(self, env: Dict[str, str]) -> None:
|
|
51
|
+
"""Set environment variables."""
|
|
52
|
+
self.env = env
|
|
53
|
+
|
|
54
|
+
async def execute_hooks(self, hooks: List[Hook], event_message: EventMessage) -> List[HookExecutionResult]:
|
|
55
|
+
"""
|
|
56
|
+
Execute all hooks in an array.
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
hooks: List of hooks to execute
|
|
60
|
+
event_message: Event message for variable substitution
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
List of execution results
|
|
64
|
+
"""
|
|
65
|
+
results = []
|
|
66
|
+
for hook in hooks:
|
|
67
|
+
result = await self.execute_hook(hook, event_message)
|
|
68
|
+
results.append(result)
|
|
69
|
+
return results
|
|
70
|
+
|
|
71
|
+
async def execute_hook(self, hook: Hook, event_message: EventMessage) -> HookExecutionResult:
|
|
72
|
+
"""
|
|
73
|
+
Execute a single hook.
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
hook: Hook to execute
|
|
77
|
+
event_message: Event message for variable substitution
|
|
78
|
+
|
|
79
|
+
Returns:
|
|
80
|
+
Execution result
|
|
81
|
+
"""
|
|
82
|
+
if hook.type != HookType.COMMAND:
|
|
83
|
+
return HookExecutionResult(
|
|
84
|
+
success=False,
|
|
85
|
+
command=hook.command,
|
|
86
|
+
error_message=f"Unsupported hook type: {hook.type}"
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
# Process command variables
|
|
90
|
+
processed_command = self._substitute_variables(hook.command, event_message)
|
|
91
|
+
|
|
92
|
+
# Execute command
|
|
93
|
+
return await self._execute_command(processed_command)
|
|
94
|
+
|
|
95
|
+
def _substitute_variables(self, command: str, event_message: EventMessage) -> str:
|
|
96
|
+
"""
|
|
97
|
+
Replace template variables in commands with runtime values.
|
|
98
|
+
|
|
99
|
+
Supported variables:
|
|
100
|
+
- {{event_type}}: The event type
|
|
101
|
+
- {{event_id}}: The event ID
|
|
102
|
+
- {{timestamp}}: The event timestamp
|
|
103
|
+
- {{tool_name}}: The tool name from event content
|
|
104
|
+
- {{event_content}}: Full event content as JSON
|
|
105
|
+
- {{agent_id}}: Agent ID from context (if available)
|
|
106
|
+
- {{conversation_id}}: Conversation ID from context (if available)
|
|
107
|
+
- {{context_*}}: Context metadata fields
|
|
108
|
+
- {{tool_*}}: Tool input fields
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
command: Command template with {{variable}} placeholders
|
|
112
|
+
event_message: Event message containing variable values
|
|
113
|
+
|
|
114
|
+
Returns:
|
|
115
|
+
Command with variables substituted
|
|
116
|
+
"""
|
|
117
|
+
variables = {
|
|
118
|
+
'event_type': event_message.event_type.value,
|
|
119
|
+
'event_id': event_message.event_id,
|
|
120
|
+
'timestamp': str(event_message.timestamp),
|
|
121
|
+
'tool_name': event_message.content.get('tool_name', ''),
|
|
122
|
+
'event_content': json.dumps(event_message.content),
|
|
123
|
+
'cwd': self.cwd
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
# Add context variables if available
|
|
127
|
+
if event_message.context:
|
|
128
|
+
if event_message.context.agent_id:
|
|
129
|
+
variables['agent_id'] = event_message.context.agent_id
|
|
130
|
+
if event_message.context.conversation_id:
|
|
131
|
+
variables['conversation_id'] = event_message.context.conversation_id
|
|
132
|
+
if event_message.context.metadata:
|
|
133
|
+
for key, value in event_message.context.metadata.items():
|
|
134
|
+
variables[f'context_{key}'] = str(value)
|
|
135
|
+
|
|
136
|
+
# Add tool-specific variables
|
|
137
|
+
if 'tool_input' in event_message.content:
|
|
138
|
+
tool_input = event_message.content['tool_input']
|
|
139
|
+
if isinstance(tool_input, dict):
|
|
140
|
+
for key, value in tool_input.items():
|
|
141
|
+
variables[f'tool_{key}'] = str(value)
|
|
142
|
+
|
|
143
|
+
# Add additional content fields
|
|
144
|
+
for key, value in event_message.content.items():
|
|
145
|
+
if key not in ['tool_name', 'tool_input'] and isinstance(value, (str, int, float, bool)):
|
|
146
|
+
variables[key] = str(value)
|
|
147
|
+
|
|
148
|
+
# Substitute variables using regex
|
|
149
|
+
def replace_var(match):
|
|
150
|
+
var_name = match.group(1)
|
|
151
|
+
return variables.get(var_name, match.group(0))
|
|
152
|
+
|
|
153
|
+
# Replace {{variable}} patterns
|
|
154
|
+
result = re.sub(r'\{\{(\w+)\}\}', replace_var, command)
|
|
155
|
+
return result
|
|
156
|
+
|
|
157
|
+
async def _execute_command(self, command: str) -> HookExecutionResult:
|
|
158
|
+
"""
|
|
159
|
+
Execute shell command with timeout and error handling.
|
|
160
|
+
|
|
161
|
+
Args:
|
|
162
|
+
command: Command to execute
|
|
163
|
+
|
|
164
|
+
Returns:
|
|
165
|
+
Execution result with output, exit code, and timing
|
|
166
|
+
"""
|
|
167
|
+
start_time = time.time()
|
|
168
|
+
result = HookExecutionResult(
|
|
169
|
+
success=False,
|
|
170
|
+
command=command,
|
|
171
|
+
start_time=start_time
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
try:
|
|
175
|
+
# Prepare environment
|
|
176
|
+
exec_env = os.environ.copy()
|
|
177
|
+
exec_env.update(self.env)
|
|
178
|
+
|
|
179
|
+
# Execute command
|
|
180
|
+
process = await asyncio.create_subprocess_shell(
|
|
181
|
+
command,
|
|
182
|
+
stdout=asyncio.subprocess.PIPE,
|
|
183
|
+
stderr=asyncio.subprocess.PIPE,
|
|
184
|
+
cwd=self.cwd,
|
|
185
|
+
env=exec_env
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
try:
|
|
189
|
+
stdout, stderr = await asyncio.wait_for(
|
|
190
|
+
process.communicate(),
|
|
191
|
+
timeout=self.timeout
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
# Update result
|
|
195
|
+
result.stdout = stdout.decode('utf-8', errors='replace')
|
|
196
|
+
result.stderr = stderr.decode('utf-8', errors='replace')
|
|
197
|
+
result.exit_code = process.returncode
|
|
198
|
+
result.success = process.returncode == 0
|
|
199
|
+
|
|
200
|
+
except asyncio.TimeoutError:
|
|
201
|
+
# Kill the process if it times out
|
|
202
|
+
process.kill()
|
|
203
|
+
await process.wait()
|
|
204
|
+
result.error_message = f"Command timed out after {self.timeout}s"
|
|
205
|
+
result.exit_code = -1
|
|
206
|
+
|
|
207
|
+
except Exception as e:
|
|
208
|
+
result.error_message = str(e)
|
|
209
|
+
result.exit_code = -1
|
|
210
|
+
logger.error(f"Error executing command '{command}': {e}")
|
|
211
|
+
|
|
212
|
+
finally:
|
|
213
|
+
end_time = time.time()
|
|
214
|
+
result.end_time = end_time
|
|
215
|
+
result.execution_time = end_time - start_time
|
|
216
|
+
|
|
217
|
+
return result
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Hook manager for configuration loading, caching, and event processing.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
import json
|
|
7
|
+
import os
|
|
8
|
+
import re
|
|
9
|
+
import time
|
|
10
|
+
import yaml
|
|
11
|
+
from typing import Dict, List, Optional, Tuple
|
|
12
|
+
|
|
13
|
+
from loguru import logger
|
|
14
|
+
from autocoder.common.async_utils import AsyncSyncMixin
|
|
15
|
+
from ..agent_events.types import EventMessage
|
|
16
|
+
from .types import HooksConfig, HookMatcher, HookProcessingResult
|
|
17
|
+
from .hook_executor import HookExecutor
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class HookManager(AsyncSyncMixin):
|
|
21
|
+
"""
|
|
22
|
+
Core hook management and event processing.
|
|
23
|
+
|
|
24
|
+
Provides both async and sync methods for all operations.
|
|
25
|
+
Sync methods are automatically generated with a '_sync' suffix.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def __init__(self, config_path: Optional[str] = None, cwd: Optional[str] = None,
|
|
29
|
+
command_timeout: int = 30000):
|
|
30
|
+
"""
|
|
31
|
+
Initialize the hook manager.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
config_path: Path to hooks configuration file (supports .json, .yaml, .yml formats)
|
|
35
|
+
cwd: Working directory for command execution
|
|
36
|
+
command_timeout: Command timeout in milliseconds
|
|
37
|
+
"""
|
|
38
|
+
self.cwd = cwd or os.getcwd()
|
|
39
|
+
self.config_path = config_path or self._find_default_config_path()
|
|
40
|
+
self.command_timeout = command_timeout
|
|
41
|
+
|
|
42
|
+
self._config: Optional[HooksConfig] = None
|
|
43
|
+
self._config_mtime: Optional[float] = None
|
|
44
|
+
self._executor = HookExecutor(cwd=self.cwd, timeout=command_timeout)
|
|
45
|
+
self._config_lock = asyncio.Lock()
|
|
46
|
+
|
|
47
|
+
def _find_default_config_path(self) -> str:
|
|
48
|
+
"""
|
|
49
|
+
Find the default configuration file by checking for hooks.json, hooks.yaml, hooks.yml.
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
Path to the first found configuration file, defaults to hooks.json if none found
|
|
53
|
+
"""
|
|
54
|
+
base_dir = os.path.join(self.cwd, '.auto-coder')
|
|
55
|
+
|
|
56
|
+
# Priority order: .json first for backward compatibility, then .yaml, then .yml
|
|
57
|
+
candidates = [
|
|
58
|
+
os.path.join(base_dir, 'hooks.json'),
|
|
59
|
+
os.path.join(base_dir, 'hooks.yaml'),
|
|
60
|
+
os.path.join(base_dir, 'hooks.yml')
|
|
61
|
+
]
|
|
62
|
+
|
|
63
|
+
for candidate in candidates:
|
|
64
|
+
if os.path.exists(candidate):
|
|
65
|
+
return candidate
|
|
66
|
+
|
|
67
|
+
# Default to hooks.json for backward compatibility
|
|
68
|
+
return candidates[0]
|
|
69
|
+
|
|
70
|
+
def _get_config_format(self, config_path: str) -> str:
|
|
71
|
+
"""
|
|
72
|
+
Detect configuration file format based on file extension.
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
config_path: Path to configuration file
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
File format: 'json', 'yaml', or 'unknown'
|
|
79
|
+
"""
|
|
80
|
+
ext = os.path.splitext(config_path)[1].lower()
|
|
81
|
+
if ext == '.json':
|
|
82
|
+
return 'json'
|
|
83
|
+
elif ext in ['.yaml', '.yml']:
|
|
84
|
+
return 'yaml'
|
|
85
|
+
else:
|
|
86
|
+
return 'unknown'
|
|
87
|
+
|
|
88
|
+
def config_exists(self) -> bool:
|
|
89
|
+
"""Check if configuration file exists."""
|
|
90
|
+
return os.path.exists(self.config_path)
|
|
91
|
+
|
|
92
|
+
async def load_config(self) -> Dict[str, any]:
|
|
93
|
+
"""
|
|
94
|
+
Load and validate hooks configuration from file.
|
|
95
|
+
Supports both JSON and YAML formats.
|
|
96
|
+
|
|
97
|
+
Returns:
|
|
98
|
+
Dictionary with 'config' (HooksConfig or None) and 'errors' (list of error messages)
|
|
99
|
+
"""
|
|
100
|
+
async with self._config_lock:
|
|
101
|
+
errors = []
|
|
102
|
+
config = None
|
|
103
|
+
|
|
104
|
+
try:
|
|
105
|
+
if not self.config_exists():
|
|
106
|
+
errors.append(f"Configuration file not found: {self.config_path}")
|
|
107
|
+
return {'config': None, 'errors': errors}
|
|
108
|
+
|
|
109
|
+
# Check if we need to reload based on file modification time
|
|
110
|
+
current_mtime = os.path.getmtime(self.config_path)
|
|
111
|
+
if self._config is not None and self._config_mtime == current_mtime:
|
|
112
|
+
return {'config': self._config, 'errors': []}
|
|
113
|
+
|
|
114
|
+
# Detect file format and load accordingly
|
|
115
|
+
config_format = self._get_config_format(self.config_path)
|
|
116
|
+
|
|
117
|
+
with open(self.config_path, 'r', encoding='utf-8') as f:
|
|
118
|
+
if config_format == 'json':
|
|
119
|
+
data = json.load(f)
|
|
120
|
+
elif config_format == 'yaml':
|
|
121
|
+
data = yaml.safe_load(f)
|
|
122
|
+
else:
|
|
123
|
+
errors.append(f"Unsupported configuration file format: {self.config_path}")
|
|
124
|
+
return {'config': None, 'errors': errors}
|
|
125
|
+
|
|
126
|
+
# Validate configuration structure
|
|
127
|
+
validation_errors = self._validate_config(data)
|
|
128
|
+
if validation_errors:
|
|
129
|
+
errors.extend(validation_errors)
|
|
130
|
+
return {'config': None, 'errors': errors}
|
|
131
|
+
|
|
132
|
+
# Create config object
|
|
133
|
+
config = HooksConfig.from_dict(data)
|
|
134
|
+
|
|
135
|
+
# Cache the configuration
|
|
136
|
+
self._config = config
|
|
137
|
+
self._config_mtime = current_mtime
|
|
138
|
+
|
|
139
|
+
logger.info(f"Loaded hooks configuration from {self.config_path} (format: {config_format})")
|
|
140
|
+
|
|
141
|
+
except json.JSONDecodeError as e:
|
|
142
|
+
errors.append(f"Invalid JSON in configuration file: {e}")
|
|
143
|
+
except yaml.YAMLError as e:
|
|
144
|
+
errors.append(f"Invalid YAML in configuration file: {e}")
|
|
145
|
+
except Exception as e:
|
|
146
|
+
errors.append(f"Error loading configuration: {e}")
|
|
147
|
+
logger.error(f"Failed to load hooks configuration: {e}")
|
|
148
|
+
|
|
149
|
+
return {'config': config, 'errors': errors}
|
|
150
|
+
|
|
151
|
+
def _validate_config(self, data: Dict) -> List[str]:
|
|
152
|
+
"""Validate configuration structure."""
|
|
153
|
+
errors = []
|
|
154
|
+
|
|
155
|
+
if not isinstance(data, dict):
|
|
156
|
+
errors.append("Configuration must be a JSON object")
|
|
157
|
+
return errors
|
|
158
|
+
|
|
159
|
+
if 'hooks' not in data:
|
|
160
|
+
errors.append("Configuration must contain 'hooks' key")
|
|
161
|
+
return errors
|
|
162
|
+
|
|
163
|
+
hooks = data['hooks']
|
|
164
|
+
if not isinstance(hooks, dict):
|
|
165
|
+
errors.append("'hooks' must be an object")
|
|
166
|
+
return errors
|
|
167
|
+
|
|
168
|
+
# Validate each event type configuration
|
|
169
|
+
for event_type, matchers in hooks.items():
|
|
170
|
+
if not isinstance(matchers, list):
|
|
171
|
+
errors.append(f"Event type '{event_type}' must have a list of matchers")
|
|
172
|
+
continue
|
|
173
|
+
|
|
174
|
+
for i, matcher in enumerate(matchers):
|
|
175
|
+
if not isinstance(matcher, dict):
|
|
176
|
+
errors.append(f"Matcher {i} in '{event_type}' must be an object")
|
|
177
|
+
continue
|
|
178
|
+
|
|
179
|
+
if 'matcher' not in matcher:
|
|
180
|
+
errors.append(f"Matcher {i} in '{event_type}' must have 'matcher' field")
|
|
181
|
+
|
|
182
|
+
if 'hooks' not in matcher:
|
|
183
|
+
errors.append(f"Matcher {i} in '{event_type}' must have 'hooks' field")
|
|
184
|
+
continue
|
|
185
|
+
|
|
186
|
+
hooks_list = matcher['hooks']
|
|
187
|
+
if not isinstance(hooks_list, list):
|
|
188
|
+
errors.append(f"'hooks' in matcher {i} of '{event_type}' must be a list")
|
|
189
|
+
continue
|
|
190
|
+
|
|
191
|
+
for j, hook in enumerate(hooks_list):
|
|
192
|
+
if not isinstance(hook, dict):
|
|
193
|
+
errors.append(f"Hook {j} in matcher {i} of '{event_type}' must be an object")
|
|
194
|
+
continue
|
|
195
|
+
|
|
196
|
+
if 'type' not in hook:
|
|
197
|
+
errors.append(f"Hook {j} in matcher {i} of '{event_type}' must have 'type' field")
|
|
198
|
+
|
|
199
|
+
if 'command' not in hook:
|
|
200
|
+
errors.append(f"Hook {j} in matcher {i} of '{event_type}' must have 'command' field")
|
|
201
|
+
|
|
202
|
+
return errors
|
|
203
|
+
|
|
204
|
+
async def process_event(self, event_message: EventMessage) -> HookProcessingResult:
|
|
205
|
+
"""
|
|
206
|
+
Process an EventMessage and execute matching hooks.
|
|
207
|
+
|
|
208
|
+
Args:
|
|
209
|
+
event_message: The event to process
|
|
210
|
+
|
|
211
|
+
Returns:
|
|
212
|
+
HookProcessingResult containing execution results and any errors
|
|
213
|
+
"""
|
|
214
|
+
start_time = time.time()
|
|
215
|
+
result = HookProcessingResult(matched=False)
|
|
216
|
+
|
|
217
|
+
try:
|
|
218
|
+
# Load configuration
|
|
219
|
+
load_result = await self.load_config()
|
|
220
|
+
if load_result['errors']:
|
|
221
|
+
result.errors.extend(load_result['errors'])
|
|
222
|
+
return result
|
|
223
|
+
|
|
224
|
+
config = load_result['config']
|
|
225
|
+
if not config:
|
|
226
|
+
result.errors.append("No configuration available")
|
|
227
|
+
return result
|
|
228
|
+
|
|
229
|
+
# Extract event type and tool name
|
|
230
|
+
event_type = event_message.event_type.value
|
|
231
|
+
tool_name = event_message.content.get('tool_name', '')
|
|
232
|
+
|
|
233
|
+
# Find matching event type configuration
|
|
234
|
+
if event_type not in config.hooks:
|
|
235
|
+
logger.debug(f"No hooks configured for event type: {event_type}")
|
|
236
|
+
return result # No configuration for this event type
|
|
237
|
+
|
|
238
|
+
matchers = config.hooks[event_type]
|
|
239
|
+
|
|
240
|
+
# Process each matcher
|
|
241
|
+
for matcher in matchers:
|
|
242
|
+
try:
|
|
243
|
+
# Check if tool name matches the pattern
|
|
244
|
+
if re.search(matcher.matcher, tool_name):
|
|
245
|
+
result.matched = True
|
|
246
|
+
logger.info(f"Matched hook pattern '{matcher.matcher}' for tool '{tool_name}'")
|
|
247
|
+
|
|
248
|
+
# Execute hooks for this matcher
|
|
249
|
+
execution_results = await self._executor.execute_hooks(matcher.hooks, event_message)
|
|
250
|
+
result.results.extend(execution_results)
|
|
251
|
+
|
|
252
|
+
# Log execution summary
|
|
253
|
+
successful = sum(1 for r in execution_results if r.success)
|
|
254
|
+
failed = len(execution_results) - successful
|
|
255
|
+
logger.info(f"Executed {len(execution_results)} hooks: {successful} successful, {failed} failed")
|
|
256
|
+
|
|
257
|
+
except re.error as e:
|
|
258
|
+
error_msg = f"Invalid regex pattern '{matcher.matcher}': {e}"
|
|
259
|
+
result.errors.append(error_msg)
|
|
260
|
+
logger.error(error_msg)
|
|
261
|
+
except Exception as e:
|
|
262
|
+
error_msg = f"Error processing matcher '{matcher.matcher}': {e}"
|
|
263
|
+
result.errors.append(error_msg)
|
|
264
|
+
logger.error(error_msg)
|
|
265
|
+
|
|
266
|
+
except Exception as e:
|
|
267
|
+
error_msg = f"Error processing event: {e}"
|
|
268
|
+
result.errors.append(error_msg)
|
|
269
|
+
logger.error(error_msg)
|
|
270
|
+
|
|
271
|
+
finally:
|
|
272
|
+
result.processing_time = time.time() - start_time
|
|
273
|
+
|
|
274
|
+
if result.matched:
|
|
275
|
+
logger.info(f"Processed event {event_message.event_type.value} in {result.processing_time:.3f}s")
|
|
276
|
+
|
|
277
|
+
return result
|
|
278
|
+
|
|
279
|
+
async def reload_config(self) -> Dict[str, any]:
|
|
280
|
+
"""Force reload configuration from file."""
|
|
281
|
+
async with self._config_lock:
|
|
282
|
+
self._config = None
|
|
283
|
+
self._config_mtime = None
|
|
284
|
+
return await self.load_config()
|
|
285
|
+
|
|
286
|
+
def get_current_config(self) -> Optional[HooksConfig]:
|
|
287
|
+
"""Get the currently loaded configuration."""
|
|
288
|
+
return self._config
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Type definitions for the Agent Hooks system.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Dict, List, Any, Optional
|
|
6
|
+
from dataclasses import dataclass, field
|
|
7
|
+
from enum import Enum
|
|
8
|
+
import time
|
|
9
|
+
|
|
10
|
+
class HookType(str, Enum):
|
|
11
|
+
"""Types of hooks supported by the system."""
|
|
12
|
+
COMMAND = "command"
|
|
13
|
+
|
|
14
|
+
@dataclass
|
|
15
|
+
class Hook:
|
|
16
|
+
"""Individual hook definition with type and command."""
|
|
17
|
+
type: HookType
|
|
18
|
+
command: str
|
|
19
|
+
description: Optional[str] = None
|
|
20
|
+
|
|
21
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
22
|
+
"""Convert hook to dictionary."""
|
|
23
|
+
result = {
|
|
24
|
+
'type': self.type.value,
|
|
25
|
+
'command': self.command
|
|
26
|
+
}
|
|
27
|
+
if self.description:
|
|
28
|
+
result['description'] = self.description
|
|
29
|
+
return result
|
|
30
|
+
|
|
31
|
+
@classmethod
|
|
32
|
+
def from_dict(cls, data: Dict[str, Any]) -> 'Hook':
|
|
33
|
+
"""Create hook from dictionary."""
|
|
34
|
+
return cls(
|
|
35
|
+
type=HookType(data.get('type', HookType.COMMAND.value)),
|
|
36
|
+
command=data.get('command', ''),
|
|
37
|
+
description=data.get('description')
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
@dataclass
|
|
41
|
+
class HookMatcher:
|
|
42
|
+
"""Pattern matching and hook configuration."""
|
|
43
|
+
matcher: str # Regular expression pattern
|
|
44
|
+
hooks: List[Hook] = field(default_factory=list)
|
|
45
|
+
|
|
46
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
47
|
+
"""Convert hook matcher to dictionary."""
|
|
48
|
+
return {
|
|
49
|
+
'matcher': self.matcher,
|
|
50
|
+
'hooks': [hook.to_dict() for hook in self.hooks]
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
@classmethod
|
|
54
|
+
def from_dict(cls, data: Dict[str, Any]) -> 'HookMatcher':
|
|
55
|
+
"""Create hook matcher from dictionary."""
|
|
56
|
+
hooks = []
|
|
57
|
+
for hook_data in data.get('hooks', []):
|
|
58
|
+
hooks.append(Hook.from_dict(hook_data))
|
|
59
|
+
|
|
60
|
+
return cls(
|
|
61
|
+
matcher=data.get('matcher', ''),
|
|
62
|
+
hooks=hooks
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
@dataclass
|
|
66
|
+
class HooksConfig:
|
|
67
|
+
"""Complete configuration structure with event type mapping."""
|
|
68
|
+
hooks: Dict[str, List[HookMatcher]] = field(default_factory=dict)
|
|
69
|
+
|
|
70
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
71
|
+
"""Convert hooks config to dictionary."""
|
|
72
|
+
result = {}
|
|
73
|
+
for event_type, matchers in self.hooks.items():
|
|
74
|
+
result[event_type] = [matcher.to_dict() for matcher in matchers]
|
|
75
|
+
return {'hooks': result}
|
|
76
|
+
|
|
77
|
+
@classmethod
|
|
78
|
+
def from_dict(cls, data: Dict[str, Any]) -> 'HooksConfig':
|
|
79
|
+
"""Create hooks config from dictionary."""
|
|
80
|
+
hooks = {}
|
|
81
|
+
hooks_data = data.get('hooks', {})
|
|
82
|
+
|
|
83
|
+
for event_type, matchers_data in hooks_data.items():
|
|
84
|
+
matchers = []
|
|
85
|
+
for matcher_data in matchers_data:
|
|
86
|
+
matchers.append(HookMatcher.from_dict(matcher_data))
|
|
87
|
+
hooks[event_type] = matchers
|
|
88
|
+
|
|
89
|
+
return cls(hooks=hooks)
|
|
90
|
+
|
|
91
|
+
@dataclass
|
|
92
|
+
class HookExecutionResult:
|
|
93
|
+
"""Command execution results and metrics."""
|
|
94
|
+
success: bool
|
|
95
|
+
command: str
|
|
96
|
+
stdout: str = ""
|
|
97
|
+
stderr: str = ""
|
|
98
|
+
exit_code: int = 0
|
|
99
|
+
execution_time: float = 0.0
|
|
100
|
+
start_time: float = field(default_factory=time.time)
|
|
101
|
+
end_time: Optional[float] = None
|
|
102
|
+
error_message: Optional[str] = None
|
|
103
|
+
|
|
104
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
105
|
+
"""Convert execution result to dictionary."""
|
|
106
|
+
return {
|
|
107
|
+
'success': self.success,
|
|
108
|
+
'command': self.command,
|
|
109
|
+
'stdout': self.stdout,
|
|
110
|
+
'stderr': self.stderr,
|
|
111
|
+
'exit_code': self.exit_code,
|
|
112
|
+
'execution_time': self.execution_time,
|
|
113
|
+
'start_time': self.start_time,
|
|
114
|
+
'end_time': self.end_time,
|
|
115
|
+
'error_message': self.error_message
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
@dataclass
|
|
119
|
+
class HookProcessingResult:
|
|
120
|
+
"""Overall event processing results."""
|
|
121
|
+
matched: bool
|
|
122
|
+
results: List[HookExecutionResult] = field(default_factory=list)
|
|
123
|
+
errors: List[str] = field(default_factory=list)
|
|
124
|
+
processing_time: float = 0.0
|
|
125
|
+
|
|
126
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
127
|
+
"""Convert processing result to dictionary."""
|
|
128
|
+
return {
|
|
129
|
+
'matched': self.matched,
|
|
130
|
+
'results': [result.to_dict() for result in self.results],
|
|
131
|
+
'errors': self.errors,
|
|
132
|
+
'processing_time': self.processing_time
|
|
133
|
+
}
|