ayder-cli 2.1.4__tar.gz → 2.1.5__tar.gz
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.
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/PKG-INFO +4 -3
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/README.md +3 -2
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/pyproject.toml +1 -1
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/application/runtime_factory.py +8 -1
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/cli.py +74 -16
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/cli_runner.py +19 -4
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/core/config.py +12 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tui/__init__.py +4 -1
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tui/app.py +13 -4
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/application/test_runtime_factory.py +62 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/core/test_config_v2.py +38 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/test_cli.py +208 -26
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/ui/test_confirm_screen.py +2 -0
- ayder_cli-2.1.4/CHANGELOG.md +0 -14
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/.github/workflows/ci.yml +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/.github/workflows/python-package.yml +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/.gitignore +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/.python-version +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/AGENTS.md +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/LICENSE +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/docs/PROJECT_STRUCTURE.md +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/docs/README_AGENT_WORKFLOW.md +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/docs/README_OLLAMA.md +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/docs/cc.png +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/docs/config.toml.example +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/__init__.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/__main__.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/agents/__init__.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/agents/callbacks.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/agents/config.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/agents/registry.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/agents/run.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/agents/runner.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/agents/tool.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/application/README.md +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/application/__init__.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/application/execution_policy.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/application/message_contract.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/application/validation.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/cli_callbacks.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/console.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/core/cache_monitor.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/core/config_migration.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/core/context.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/core/context_manager.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/core/context_manager_factory.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/core/default_context_manager.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/core/ollama_context_manager.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/core/result.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/logging_config.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/loops/__init__.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/loops/chat_loop.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/parser.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/process_manager.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/prompts.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/providers/__init__.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/providers/base.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/providers/impl/claude.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/providers/impl/deepseek.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/providers/impl/gemini.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/providers/impl/glm.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/providers/impl/ollama.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/providers/impl/ollama_drivers/__init__.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/providers/impl/ollama_drivers/_errors.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/providers/impl/ollama_drivers/base.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/providers/impl/ollama_drivers/deepseek.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/providers/impl/ollama_drivers/generic_native.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/providers/impl/ollama_drivers/generic_xml.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/providers/impl/ollama_drivers/matrix.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/providers/impl/ollama_drivers/minimax.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/providers/impl/ollama_drivers/qwen3.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/providers/impl/ollama_drivers/registry.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/providers/impl/ollama_inspector.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/providers/impl/openai.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/providers/impl/qwen.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/providers/orchestrator.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/providers/retry.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/services/__init__.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/services/interactions.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/themes/__init__.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/themes/claude.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/themes/original.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tools/__init__.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tools/builtins/__init__.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tools/builtins/context.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tools/builtins/context_definitions.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tools/builtins/filesystem.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tools/builtins/filesystem_definitions.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tools/builtins/notes.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tools/builtins/notes_definitions.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tools/builtins/process_manager_definitions.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tools/builtins/search.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tools/builtins/search_definitions.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tools/builtins/shell.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tools/builtins/shell_definitions.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tools/builtins/tasks.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tools/builtins/tasks_definitions.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tools/builtins/utils_tools.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tools/builtins/utils_tools_definitions.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tools/builtins/web.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tools/builtins/web_definitions.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tools/definition.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tools/execution.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tools/hooks.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tools/normalization.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tools/plugin_api.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tools/plugin_github.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tools/plugin_manager.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tools/plugin_status.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tools/registry.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tools/schemas.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tools/utils.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tui/adapter.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tui/commands.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tui/helpers.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tui/keybindings.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tui/parser.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tui/screens.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tui/theme_manager.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tui/types.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tui/widgets.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/ui.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/version.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/COVERAGE.md +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/__init__.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/agents/__init__.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/agents/test_agent_tools.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/agents/test_callbacks.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/agents/test_config.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/agents/test_integration.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/agents/test_registry.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/agents/test_run.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/agents/test_runner.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/agents/test_runner_provider_unavailable.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/agents/test_tool.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/application/test_message_contract.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/client/test_main.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/convergence/__init__.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/convergence/test_execution_policy_parity.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/convergence/test_runtime_wiring.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/convergence/test_validation_path.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/core/test_cache_monitor.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/core/test_config.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/core/test_config_coverage.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/core/test_config_migration.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/core/test_config_provider_logging.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/core/test_config_retry.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/core/test_context_manager_factory.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/core/test_default_context_manager.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/core/test_default_context_manager_should_compact.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/core/test_ollama_context_manager.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/core/test_ollama_context_manager_cache_monitor.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/core/test_ollama_context_manager_max_history.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/core/test_parameter_aliasing.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/core/test_parser.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/loops/__init__.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/loops/test_base.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/loops/test_chat_loop_hook.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/loops/test_chat_loop_tool_truncation.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/providers/__init__.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/providers/ollama_drivers/__init__.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/providers/ollama_drivers/test_base.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/providers/ollama_drivers/test_deepseek.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/providers/ollama_drivers/test_errors.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/providers/ollama_drivers/test_fallback.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/providers/ollama_drivers/test_generic_native.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/providers/ollama_drivers/test_generic_xml.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/providers/ollama_drivers/test_matrix.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/providers/ollama_drivers/test_minimax.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/providers/ollama_drivers/test_provider_integration.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/providers/ollama_drivers/test_qwen3.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/providers/ollama_drivers/test_registry.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/providers/test_glm_async.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/providers/test_ollama_inspector.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/providers/test_ollama_native.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/providers/test_ollama_xml.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/providers/test_ollama_xml_autoroute.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/providers/test_ollama_xml_usage.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/providers/test_orchestrator_capabilities.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/providers/test_qwen_async.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/providers/test_retry_classifier.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/providers/test_retry_integration.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/providers/test_retrying_provider.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/services/test_boundary.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/services/test_interaction_sink.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/test_cli_callbacks.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/test_context.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/test_env_manager.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/test_logging_config.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/test_notes.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/test_process_manager.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/test_tasks.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/tools/__init__.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/tools/test_agent_notes.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/tools/test_definition_discovery.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/tools/test_di_injection.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/tools/test_impl.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/tools/test_impl_coverage.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/tools/test_path_security.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/tools/test_plugin_api.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/tools/test_plugin_github.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/tools/test_plugin_loading.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/tools/test_plugin_manager.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/tools/test_plugin_status.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/tools/test_registry.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/tools/test_registry_coverage.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/tools/test_result.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/tools/test_schemas.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/tools/test_search_codebase.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/tools/test_utils.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/tools/test_web.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/tui/__init__.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/tui/test_agent_list_screen.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/tui/test_do_clear_coordination.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/tui/test_follow_mode.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/tui/test_help_screen.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/tui/test_input_box.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/tui/test_keybindings.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/tui/test_multi_select_screen.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/tui/test_multiline_input.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/tui/test_paste.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/tui/test_pending_compact_consumer.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/tui/test_pull_delivery.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/tui/test_status_bar_badges.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/tui/test_turn_consumer.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/tui/test_widgets.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/ui/test_diff_preview.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/ui/test_plugin_autoenable.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/ui/test_provider_switch.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/ui/test_run_tui_startup.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/ui/test_tui_assistant_sanitization.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/ui/test_tui_chat_loop.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/ui/test_tui_commands_agent.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/ui/test_tui_commands_logging.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/ui/test_tui_commands_plugin.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/ui/test_tui_commands_provider.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/ui/test_tui_helpers.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/ui/test_tui_temporal_command.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/ui/test_tui_widgets.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/ui/test_ui.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/ui/test_ui_coverage.py +0 -0
- {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/ui/test_ui_verbose.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ayder-cli
|
|
3
|
-
Version: 2.1.
|
|
3
|
+
Version: 2.1.5
|
|
4
4
|
Summary: AI agent for any LLMs
|
|
5
5
|
Project-URL: Homepage, https://github.com/ayder/ayder-cli
|
|
6
6
|
Project-URL: Repository, https://github.com/ayder/ayder-cli.git
|
|
@@ -288,8 +288,9 @@ ayder -f instructions.txt
|
|
|
288
288
|
# Explicit stdin mode
|
|
289
289
|
ayder --stdin < prompt.txt
|
|
290
290
|
|
|
291
|
-
#
|
|
292
|
-
|
|
291
|
+
# Override the main LLM system prompt with a file
|
|
292
|
+
# (tool instructions + project structure are still appended)
|
|
293
|
+
ayder --system-prompt prompt-file.md "refactor this code"
|
|
293
294
|
```
|
|
294
295
|
|
|
295
296
|
### Task Commands (CLI Mode)
|
|
@@ -246,8 +246,9 @@ ayder -f instructions.txt
|
|
|
246
246
|
# Explicit stdin mode
|
|
247
247
|
ayder --stdin < prompt.txt
|
|
248
248
|
|
|
249
|
-
#
|
|
250
|
-
|
|
249
|
+
# Override the main LLM system prompt with a file
|
|
250
|
+
# (tool instructions + project structure are still appended)
|
|
251
|
+
ayder --system-prompt prompt-file.md "refactor this code"
|
|
251
252
|
```
|
|
252
253
|
|
|
253
254
|
### Task Commands (CLI Mode)
|
|
@@ -44,6 +44,7 @@ def create_runtime(
|
|
|
44
44
|
project_root: str = ".",
|
|
45
45
|
model_name: str | None = None,
|
|
46
46
|
prompt_tier: str | None = None,
|
|
47
|
+
system_prompt_override: str | None = None,
|
|
47
48
|
) -> RuntimeComponents:
|
|
48
49
|
"""Assemble and return all shared runtime components.
|
|
49
50
|
|
|
@@ -53,6 +54,9 @@ def create_runtime(
|
|
|
53
54
|
model_name: Override model name from config when provided.
|
|
54
55
|
prompt_tier: Override the system-prompt tier from config when provided
|
|
55
56
|
(e.g. "AGENTIC" for `ayder-cli --agent`). See prompts.get_system_prompt.
|
|
57
|
+
system_prompt_override: When set, use this text as the system-prompt base
|
|
58
|
+
instead of prompts.get_system_prompt (ayder --system-prompt FILE). Tool
|
|
59
|
+
prompts and the project-structure macro are still appended.
|
|
56
60
|
|
|
57
61
|
Returns:
|
|
58
62
|
RuntimeComponents with all dependencies wired.
|
|
@@ -84,7 +88,10 @@ def create_runtime(
|
|
|
84
88
|
macro = ""
|
|
85
89
|
|
|
86
90
|
# Format the final prompt (protocol injection now handled dynamically by chat_loop and protocols)
|
|
87
|
-
|
|
91
|
+
if system_prompt_override is not None:
|
|
92
|
+
base_prompt = system_prompt_override
|
|
93
|
+
else:
|
|
94
|
+
base_prompt = get_system_prompt(cfg.prompt)
|
|
88
95
|
tool_tags = frozenset(cfg.tool_tags) if cfg.tool_tags else None
|
|
89
96
|
tool_prompts = tool_registry.get_system_prompts(tags=tool_tags)
|
|
90
97
|
system_prompt = base_prompt + tool_prompts + macro
|
|
@@ -56,11 +56,28 @@ def _add_common_args(parser: argparse.ArgumentParser) -> None:
|
|
|
56
56
|
"--stdin", action="store_true", help="Read prompt from stdin"
|
|
57
57
|
)
|
|
58
58
|
parser.add_argument(
|
|
59
|
-
"--prompt",
|
|
59
|
+
"--temporal-prompt",
|
|
60
60
|
type=str,
|
|
61
61
|
default=None,
|
|
62
62
|
metavar="FILE",
|
|
63
|
-
help="
|
|
63
|
+
help="Prompt file for the Temporal worker (only used with --temporal-task-queue)",
|
|
64
|
+
)
|
|
65
|
+
parser.add_argument(
|
|
66
|
+
"--system-prompt",
|
|
67
|
+
type=str,
|
|
68
|
+
default=None,
|
|
69
|
+
metavar="FILE",
|
|
70
|
+
help="Use FILE's contents as the main LLM system prompt, overriding the "
|
|
71
|
+
"built-in prompt from prompts.py (tool instructions and project structure "
|
|
72
|
+
"are still appended)",
|
|
73
|
+
)
|
|
74
|
+
parser.add_argument(
|
|
75
|
+
"-c",
|
|
76
|
+
"--config",
|
|
77
|
+
type=str,
|
|
78
|
+
default=None,
|
|
79
|
+
metavar="FILE",
|
|
80
|
+
help="Path to a config.toml to use instead of ~/.ayder/config.toml",
|
|
64
81
|
)
|
|
65
82
|
|
|
66
83
|
# Permission flags: r=read, w=write, x=execute, http=web/network
|
|
@@ -93,13 +110,10 @@ def _add_common_args(parser: argparse.ArgumentParser) -> None:
|
|
|
93
110
|
|
|
94
111
|
parser.add_argument(
|
|
95
112
|
"--verbose",
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
choices=LOG_LEVELS,
|
|
101
|
-
metavar="LEVEL",
|
|
102
|
-
help="Enable verbose console output and logging; default level is INFO",
|
|
113
|
+
action="store_true",
|
|
114
|
+
help="Show logs on the console. The level comes from --logging-level (or "
|
|
115
|
+
"config); if neither is set, defaults to INFO. Does not otherwise change "
|
|
116
|
+
"the log level.",
|
|
103
117
|
)
|
|
104
118
|
parser.add_argument(
|
|
105
119
|
"--logging-level",
|
|
@@ -214,6 +228,16 @@ def main():
|
|
|
214
228
|
args = parser.parse_args()
|
|
215
229
|
args.subcommand = None
|
|
216
230
|
|
|
231
|
+
# Apply the -c/--config override before anything reads the config file.
|
|
232
|
+
if getattr(args, "config", None):
|
|
233
|
+
from ayder_cli.core.config import set_config_path
|
|
234
|
+
|
|
235
|
+
config_file = Path(args.config).expanduser()
|
|
236
|
+
if not config_file.is_file():
|
|
237
|
+
print(f"Error: Config file not found: {args.config}", file=sys.stderr)
|
|
238
|
+
sys.exit(1)
|
|
239
|
+
set_config_path(config_file)
|
|
240
|
+
|
|
217
241
|
# Handle plugin subcommands
|
|
218
242
|
if args.subcommand == "install-plugin":
|
|
219
243
|
from ayder_cli.tools.plugin_manager import (
|
|
@@ -310,13 +334,17 @@ def main():
|
|
|
310
334
|
|
|
311
335
|
cfg = load_config(notify_migration=True, output=print)
|
|
312
336
|
|
|
313
|
-
#
|
|
314
|
-
|
|
315
|
-
|
|
337
|
+
# --verbose only enables console output; the level comes from --logging-level
|
|
338
|
+
# (or config.logging_level). When --verbose is used with no level configured
|
|
339
|
+
# anywhere, default the console to INFO so it isn't empty.
|
|
340
|
+
effective_log_level = args.logging_level
|
|
341
|
+
if args.verbose and effective_log_level is None and cfg.logging_level is None:
|
|
342
|
+
effective_log_level = "INFO"
|
|
343
|
+
|
|
316
344
|
setup_logging(
|
|
317
345
|
cfg,
|
|
318
346
|
level_override=effective_log_level,
|
|
319
|
-
console_stream=sys.stdout if args.verbose
|
|
347
|
+
console_stream=sys.stdout if args.verbose else None,
|
|
320
348
|
)
|
|
321
349
|
|
|
322
350
|
# --agent: warn early if the harness has nothing to orchestrate.
|
|
@@ -342,11 +370,30 @@ def main():
|
|
|
342
370
|
sys.exit(
|
|
343
371
|
_run_temporal_queue_cli(
|
|
344
372
|
queue_name=args.temporal_task_queue,
|
|
345
|
-
prompt_path=args.
|
|
373
|
+
prompt_path=args.temporal_prompt,
|
|
346
374
|
permissions=granted,
|
|
347
375
|
)
|
|
348
376
|
)
|
|
349
377
|
|
|
378
|
+
# Read the --system-prompt override file (overrides prompts.py base prompt).
|
|
379
|
+
system_prompt_override = None
|
|
380
|
+
if args.system_prompt:
|
|
381
|
+
sp_file = Path(args.system_prompt).expanduser()
|
|
382
|
+
try:
|
|
383
|
+
system_prompt_override = sp_file.read_text()
|
|
384
|
+
except FileNotFoundError:
|
|
385
|
+
print(
|
|
386
|
+
f"Error: System prompt file not found: {args.system_prompt}",
|
|
387
|
+
file=sys.stderr,
|
|
388
|
+
)
|
|
389
|
+
sys.exit(1)
|
|
390
|
+
except Exception as e:
|
|
391
|
+
print(
|
|
392
|
+
f"Error reading system prompt file {args.system_prompt}: {e}",
|
|
393
|
+
file=sys.stderr,
|
|
394
|
+
)
|
|
395
|
+
sys.exit(1)
|
|
396
|
+
|
|
350
397
|
# Handle file/stdin input
|
|
351
398
|
context = None
|
|
352
399
|
question = args.command
|
|
@@ -376,10 +423,21 @@ def main():
|
|
|
376
423
|
# Default: TUI mode
|
|
377
424
|
from ayder_cli.tui import run_tui
|
|
378
425
|
|
|
379
|
-
run_tui(
|
|
426
|
+
run_tui(
|
|
427
|
+
permissions=granted,
|
|
428
|
+
agent_mode=args.agent,
|
|
429
|
+
system_prompt_override=system_prompt_override,
|
|
430
|
+
)
|
|
380
431
|
return
|
|
381
432
|
|
|
382
|
-
sys.exit(
|
|
433
|
+
sys.exit(
|
|
434
|
+
run_command(
|
|
435
|
+
prompt,
|
|
436
|
+
permissions=granted,
|
|
437
|
+
agent_mode=args.agent,
|
|
438
|
+
system_prompt_override=system_prompt_override,
|
|
439
|
+
)
|
|
440
|
+
)
|
|
383
441
|
|
|
384
442
|
|
|
385
443
|
if __name__ == "__main__":
|
|
@@ -36,6 +36,7 @@ def _run_loop(
|
|
|
36
36
|
prompt: str,
|
|
37
37
|
permissions: set | None = None,
|
|
38
38
|
agent_mode: bool = False,
|
|
39
|
+
system_prompt_override: str | None = None,
|
|
39
40
|
) -> int:
|
|
40
41
|
"""Create a ChatLoop with CliCallbacks and run it.
|
|
41
42
|
|
|
@@ -47,11 +48,16 @@ def _run_loop(
|
|
|
47
48
|
permissions: Granted permission categories.
|
|
48
49
|
agent_mode: When True, inject the AGENTIC orchestrator system prompt
|
|
49
50
|
(ayder-cli --agent) so the main LLM drives the multi-agent harness.
|
|
51
|
+
system_prompt_override: When set, use this text as the system prompt base
|
|
52
|
+
instead of the built-in prompts.py prompt (ayder --system-prompt FILE).
|
|
50
53
|
|
|
51
54
|
Returns:
|
|
52
55
|
Exit code (0 for success, 1 for error).
|
|
53
56
|
"""
|
|
54
|
-
rt = create_runtime(
|
|
57
|
+
rt = create_runtime(
|
|
58
|
+
prompt_tier="AGENTIC" if agent_mode else None,
|
|
59
|
+
system_prompt_override=system_prompt_override,
|
|
60
|
+
)
|
|
55
61
|
|
|
56
62
|
messages: list[dict] = [
|
|
57
63
|
{"role": "system", "content": rt.system_prompt},
|
|
@@ -119,10 +125,12 @@ def _run_loop(
|
|
|
119
125
|
class CommandRunner:
|
|
120
126
|
"""Runner for single command execution mode."""
|
|
121
127
|
|
|
122
|
-
def __init__(self, prompt: str, permissions=None, agent_mode: bool = False
|
|
128
|
+
def __init__(self, prompt: str, permissions=None, agent_mode: bool = False,
|
|
129
|
+
system_prompt_override: str | None = None):
|
|
123
130
|
self.prompt = prompt
|
|
124
131
|
self.permissions = permissions
|
|
125
132
|
self.agent_mode = agent_mode
|
|
133
|
+
self.system_prompt_override = system_prompt_override
|
|
126
134
|
|
|
127
135
|
def run(self) -> int:
|
|
128
136
|
"""Execute the command and return exit code.
|
|
@@ -135,6 +143,7 @@ class CommandRunner:
|
|
|
135
143
|
self.prompt,
|
|
136
144
|
permissions=self.permissions,
|
|
137
145
|
agent_mode=self.agent_mode,
|
|
146
|
+
system_prompt_override=self.system_prompt_override,
|
|
138
147
|
)
|
|
139
148
|
except ProviderUnavailableError as e:
|
|
140
149
|
print(str(e), file=sys.stderr) # message already starts with "Error:"
|
|
@@ -144,18 +153,24 @@ class CommandRunner:
|
|
|
144
153
|
return 1
|
|
145
154
|
|
|
146
155
|
|
|
147
|
-
def run_command(prompt: str, permissions=None, agent_mode: bool = False
|
|
156
|
+
def run_command(prompt: str, permissions=None, agent_mode: bool = False,
|
|
157
|
+
system_prompt_override: str | None = None) -> int:
|
|
148
158
|
"""Execute a single command and return exit code.
|
|
149
159
|
|
|
150
160
|
Args:
|
|
151
161
|
prompt: The command/prompt to execute
|
|
152
162
|
permissions: Set of granted permission categories (e.g. {"r", "w", "x", "http"})
|
|
153
163
|
agent_mode: When True, inject the AGENTIC orchestrator system prompt (ayder-cli --agent).
|
|
164
|
+
system_prompt_override: When set, use this text as the system prompt base
|
|
165
|
+
instead of the built-in prompts.py prompt (ayder --system-prompt FILE).
|
|
154
166
|
|
|
155
167
|
Returns:
|
|
156
168
|
Exit code (0 for success, 1 for error)
|
|
157
169
|
"""
|
|
158
|
-
runner = CommandRunner(
|
|
170
|
+
runner = CommandRunner(
|
|
171
|
+
prompt, permissions=permissions, agent_mode=agent_mode,
|
|
172
|
+
system_prompt_override=system_prompt_override,
|
|
173
|
+
)
|
|
159
174
|
return runner.run()
|
|
160
175
|
|
|
161
176
|
|
|
@@ -9,6 +9,18 @@ logger = logging.getLogger(__name__)
|
|
|
9
9
|
CONFIG_DIR = Path("~/.ayder").expanduser()
|
|
10
10
|
CONFIG_PATH = CONFIG_DIR / "config.toml"
|
|
11
11
|
|
|
12
|
+
|
|
13
|
+
def set_config_path(path: "str | Path") -> None:
|
|
14
|
+
"""Redirect the global config file location (used by the ``-c/--config`` flag).
|
|
15
|
+
|
|
16
|
+
Updates CONFIG_PATH and CONFIG_DIR so every subsequent ``load_config`` (and the
|
|
17
|
+
provider/profile loaders, which read these module globals at call time) use
|
|
18
|
+
*path* instead of the default ``~/.ayder/config.toml``.
|
|
19
|
+
"""
|
|
20
|
+
global CONFIG_PATH, CONFIG_DIR
|
|
21
|
+
CONFIG_PATH = Path(path).expanduser().resolve()
|
|
22
|
+
CONFIG_DIR = CONFIG_PATH.parent
|
|
23
|
+
|
|
12
24
|
# Maintained for test compatibility and initial file generation
|
|
13
25
|
DEFAULTS: Dict[str, Any] = {
|
|
14
26
|
"provider": "openai",
|
|
@@ -32,6 +32,7 @@ def run_tui(
|
|
|
32
32
|
safe_mode: bool = False,
|
|
33
33
|
permissions: set | None = None,
|
|
34
34
|
agent_mode: bool = False,
|
|
35
|
+
system_prompt_override: str | None = None,
|
|
35
36
|
) -> None:
|
|
36
37
|
"""
|
|
37
38
|
Run the CLI-style TUI application.
|
|
@@ -42,6 +43,8 @@ def run_tui(
|
|
|
42
43
|
permissions: Set of granted permission levels ("r", "w", "x")
|
|
43
44
|
agent_mode: When True, inject the AGENTIC orchestrator system prompt
|
|
44
45
|
so the main LLM drives the multi-agent harness (ayder-cli --agent).
|
|
46
|
+
system_prompt_override: When set, use this text as the system-prompt base
|
|
47
|
+
instead of the built-in prompts.py prompt (ayder --system-prompt FILE).
|
|
45
48
|
"""
|
|
46
49
|
import sys
|
|
47
50
|
from ayder_cli.providers import ProviderUnavailableError
|
|
@@ -49,7 +52,7 @@ def run_tui(
|
|
|
49
52
|
try:
|
|
50
53
|
app = AyderApp(
|
|
51
54
|
model=model, safe_mode=safe_mode, permissions=permissions,
|
|
52
|
-
agent_mode=agent_mode,
|
|
55
|
+
agent_mode=agent_mode, system_prompt_override=system_prompt_override,
|
|
53
56
|
)
|
|
54
57
|
except ProviderUnavailableError as e:
|
|
55
58
|
print(str(e), file=sys.stderr)
|
|
@@ -210,6 +210,7 @@ class AyderApp(App):
|
|
|
210
210
|
safe_mode: bool = False,
|
|
211
211
|
permissions: set | None = None,
|
|
212
212
|
agent_mode: bool = False,
|
|
213
|
+
system_prompt_override: str | None = None,
|
|
213
214
|
**kwargs,
|
|
214
215
|
):
|
|
215
216
|
"""
|
|
@@ -221,10 +222,13 @@ class AyderApp(App):
|
|
|
221
222
|
permissions: Set of granted permission levels ("r", "w", "x")
|
|
222
223
|
agent_mode: When True, drive the multi-agent harness — inject the
|
|
223
224
|
AGENTIC orchestrator system prompt (ayder-cli --agent).
|
|
225
|
+
system_prompt_override: When set, use this text as the system-prompt base
|
|
226
|
+
instead of the built-in prompts.py prompt (ayder --system-prompt FILE).
|
|
224
227
|
"""
|
|
225
228
|
super().__init__(**kwargs)
|
|
226
229
|
self.safe_mode = safe_mode
|
|
227
230
|
self.permissions = permissions or {"r"}
|
|
231
|
+
self._system_prompt_override = system_prompt_override
|
|
228
232
|
|
|
229
233
|
self._requests: asyncio.Queue[TurnRequest] = asyncio.Queue()
|
|
230
234
|
self._run_task: asyncio.Task | None = None
|
|
@@ -418,7 +422,10 @@ class AyderApp(App):
|
|
|
418
422
|
except Exception:
|
|
419
423
|
macro = ""
|
|
420
424
|
|
|
421
|
-
|
|
425
|
+
if self._system_prompt_override is not None:
|
|
426
|
+
base_prompt = self._system_prompt_override
|
|
427
|
+
else:
|
|
428
|
+
base_prompt = get_system_prompt(self.config.prompt)
|
|
422
429
|
raw_tags = getattr(self.config, "tool_tags", None) if not isinstance(self.config, dict) else self.config.get("tool_tags")
|
|
423
430
|
tags = frozenset(raw_tags) if raw_tags else None
|
|
424
431
|
tool_prompts = self.registry.get_system_prompts(tags=tags)
|
|
@@ -444,9 +451,11 @@ class AyderApp(App):
|
|
|
444
451
|
macro = ""
|
|
445
452
|
tags = self.chat_loop.config.tool_tags
|
|
446
453
|
tool_prompts = self.registry.get_system_prompts(tags=tags)
|
|
447
|
-
self.
|
|
448
|
-
|
|
449
|
-
|
|
454
|
+
if self._system_prompt_override is not None:
|
|
455
|
+
base_prompt = self._system_prompt_override
|
|
456
|
+
else:
|
|
457
|
+
base_prompt = get_system_prompt(self.config.prompt)
|
|
458
|
+
self.messages[0]["content"] = base_prompt + tool_prompts + macro
|
|
450
459
|
|
|
451
460
|
def inject_skill(self, skill_name: str, skill_content: str) -> None:
|
|
452
461
|
"""Inject or replace the active skill system message."""
|
|
@@ -83,6 +83,38 @@ class TestRuntimeFactoryAssembly:
|
|
|
83
83
|
assert components.config.model == "qwen3-coder:latest"
|
|
84
84
|
|
|
85
85
|
|
|
86
|
+
class TestFactorySystemPromptOverride:
|
|
87
|
+
"""Test the --system-prompt override path in create_runtime."""
|
|
88
|
+
|
|
89
|
+
def test_system_prompt_override_replaces_base_keeps_tools_and_macro(self):
|
|
90
|
+
"""Override replaces the prompts.py base but tool prompts + project macro remain."""
|
|
91
|
+
with patch('ayder_cli.application.runtime_factory.create_default_registry') as mock_create, \
|
|
92
|
+
patch('ayder_cli.application.runtime_factory.get_system_prompt') as mock_get_prompt:
|
|
93
|
+
mock_registry = Mock()
|
|
94
|
+
mock_registry.execute.return_value = "src/\n main.py"
|
|
95
|
+
mock_registry.get_system_prompts.return_value = "[TOOLS]"
|
|
96
|
+
mock_registry.get_schemas.return_value = []
|
|
97
|
+
mock_create.return_value = mock_registry
|
|
98
|
+
|
|
99
|
+
components = create_runtime(system_prompt_override="CUSTOM_BASE")
|
|
100
|
+
|
|
101
|
+
# prompts.py base prompt is NOT consulted when an override is given
|
|
102
|
+
mock_get_prompt.assert_not_called()
|
|
103
|
+
# override is the base; tool prompts + project structure macro still appended
|
|
104
|
+
assert components.system_prompt.startswith("CUSTOM_BASE")
|
|
105
|
+
assert "[TOOLS]" in components.system_prompt
|
|
106
|
+
assert "PROJECT STRUCTURE" in components.system_prompt
|
|
107
|
+
|
|
108
|
+
def test_no_override_uses_prompts_py_base(self):
|
|
109
|
+
"""Without an override, the base prompt comes from prompts.py.get_system_prompt."""
|
|
110
|
+
with patch('ayder_cli.application.runtime_factory.get_system_prompt',
|
|
111
|
+
return_value="DEFAULT_BASE") as mock_get_prompt:
|
|
112
|
+
components = create_runtime()
|
|
113
|
+
|
|
114
|
+
mock_get_prompt.assert_called_once()
|
|
115
|
+
assert components.system_prompt.startswith("DEFAULT_BASE")
|
|
116
|
+
|
|
117
|
+
|
|
86
118
|
class TestFactoryTUIIntegration:
|
|
87
119
|
"""Test TUI uses factory-built dependencies."""
|
|
88
120
|
|
|
@@ -122,6 +154,36 @@ class TestFactoryTUIIntegration:
|
|
|
122
154
|
assert app.registry is not None
|
|
123
155
|
assert app.chat_loop is not None
|
|
124
156
|
|
|
157
|
+
def test_tui_system_prompt_override(self):
|
|
158
|
+
"""AyderApp(system_prompt_override=...) uses it as the base, skipping prompts.py."""
|
|
159
|
+
with patch('ayder_cli.tui.app.create_runtime') as mock_create_runtime, \
|
|
160
|
+
patch('ayder_cli.prompts.get_system_prompt') as mock_get_prompt:
|
|
161
|
+
mock_components = Mock()
|
|
162
|
+
mock_components.config = Mock()
|
|
163
|
+
mock_components.config.model = "test-model"
|
|
164
|
+
mock_components.config.num_ctx = 65536
|
|
165
|
+
mock_components.config.max_iterations = 50
|
|
166
|
+
mock_components.config.max_background_processes = 5
|
|
167
|
+
mock_components.config.max_output_tokens = 4096
|
|
168
|
+
mock_components.config.stop_sequences = []
|
|
169
|
+
mock_components.config.tool_tags = ["core", "metadata"]
|
|
170
|
+
mock_components.config.driver = "openai"
|
|
171
|
+
mock_components.config.agents = {}
|
|
172
|
+
mock_components.llm_provider = Mock()
|
|
173
|
+
mock_components.tool_registry = Mock()
|
|
174
|
+
mock_components.tool_registry.execute.return_value = "src/\n main.py"
|
|
175
|
+
mock_components.tool_registry.get_system_prompts.return_value = "[TOOLS]"
|
|
176
|
+
mock_create_runtime.return_value = mock_components
|
|
177
|
+
|
|
178
|
+
app = AyderApp(system_prompt_override="TUI_CUSTOM")
|
|
179
|
+
|
|
180
|
+
# prompts.py base prompt is not consulted
|
|
181
|
+
mock_get_prompt.assert_not_called()
|
|
182
|
+
sys_msg = app.messages[0]
|
|
183
|
+
assert sys_msg["role"] == "system"
|
|
184
|
+
assert sys_msg["content"].startswith("TUI_CUSTOM")
|
|
185
|
+
assert "[TOOLS]" in sys_msg["content"]
|
|
186
|
+
|
|
125
187
|
|
|
126
188
|
class TestFactoryCompositionParity:
|
|
127
189
|
"""Test CLI and TUI get equivalent core dependencies."""
|
|
@@ -101,6 +101,44 @@ num_ctx = 8192
|
|
|
101
101
|
assert cfg.model == "claude-sonnet-4-5-20250929"
|
|
102
102
|
|
|
103
103
|
|
|
104
|
+
def test_set_config_path_redirects_load_config(tmp_path, monkeypatch):
|
|
105
|
+
"""set_config_path() makes load_config() read from a custom path (the -c flag)."""
|
|
106
|
+
import ayder_cli.core.config as config_mod
|
|
107
|
+
|
|
108
|
+
# Snapshot the module globals so they are restored at teardown even though
|
|
109
|
+
# set_config_path mutates them directly via `global`.
|
|
110
|
+
monkeypatch.setattr(config_mod, "CONFIG_PATH", config_mod.CONFIG_PATH)
|
|
111
|
+
monkeypatch.setattr(config_mod, "CONFIG_DIR", config_mod.CONFIG_DIR)
|
|
112
|
+
|
|
113
|
+
custom = tmp_path / "custom-config" / "ayder.toml"
|
|
114
|
+
custom.parent.mkdir()
|
|
115
|
+
custom.write_text(
|
|
116
|
+
"""config_version = "2.0"
|
|
117
|
+
|
|
118
|
+
[app]
|
|
119
|
+
provider = "deepseek"
|
|
120
|
+
|
|
121
|
+
[llm.deepseek]
|
|
122
|
+
driver = "openai"
|
|
123
|
+
base_url = "https://api.deepseek.com/v1"
|
|
124
|
+
api_key = "sk-custom"
|
|
125
|
+
model = "deepseek-custom"
|
|
126
|
+
num_ctx = 4096
|
|
127
|
+
""",
|
|
128
|
+
encoding="utf-8",
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
config_mod.set_config_path(custom)
|
|
132
|
+
|
|
133
|
+
assert config_mod.CONFIG_PATH == custom.resolve()
|
|
134
|
+
assert config_mod.CONFIG_DIR == custom.resolve().parent
|
|
135
|
+
|
|
136
|
+
cfg = config_mod.load_config()
|
|
137
|
+
assert cfg.provider == "deepseek"
|
|
138
|
+
assert cfg.model == "deepseek-custom"
|
|
139
|
+
assert cfg.api_key == "sk-custom"
|
|
140
|
+
|
|
141
|
+
|
|
104
142
|
@pytest.mark.parametrize("drv", ["openai", "ollama", "deepseek", "anthropic",
|
|
105
143
|
"google", "qwen", "dashscope", "glm", "zhipu"])
|
|
106
144
|
def test_validate_driver_accepts_all_supported(drv):
|