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.
Files changed (242) hide show
  1. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/PKG-INFO +4 -3
  2. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/README.md +3 -2
  3. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/pyproject.toml +1 -1
  4. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/application/runtime_factory.py +8 -1
  5. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/cli.py +74 -16
  6. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/cli_runner.py +19 -4
  7. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/core/config.py +12 -0
  8. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tui/__init__.py +4 -1
  9. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tui/app.py +13 -4
  10. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/application/test_runtime_factory.py +62 -0
  11. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/core/test_config_v2.py +38 -0
  12. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/test_cli.py +208 -26
  13. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/ui/test_confirm_screen.py +2 -0
  14. ayder_cli-2.1.4/CHANGELOG.md +0 -14
  15. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/.github/workflows/ci.yml +0 -0
  16. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/.github/workflows/python-package.yml +0 -0
  17. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/.gitignore +0 -0
  18. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/.python-version +0 -0
  19. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/AGENTS.md +0 -0
  20. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/LICENSE +0 -0
  21. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/docs/PROJECT_STRUCTURE.md +0 -0
  22. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/docs/README_AGENT_WORKFLOW.md +0 -0
  23. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/docs/README_OLLAMA.md +0 -0
  24. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/docs/cc.png +0 -0
  25. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/docs/config.toml.example +0 -0
  26. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/__init__.py +0 -0
  27. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/__main__.py +0 -0
  28. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/agents/__init__.py +0 -0
  29. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/agents/callbacks.py +0 -0
  30. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/agents/config.py +0 -0
  31. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/agents/registry.py +0 -0
  32. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/agents/run.py +0 -0
  33. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/agents/runner.py +0 -0
  34. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/agents/tool.py +0 -0
  35. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/application/README.md +0 -0
  36. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/application/__init__.py +0 -0
  37. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/application/execution_policy.py +0 -0
  38. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/application/message_contract.py +0 -0
  39. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/application/validation.py +0 -0
  40. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/cli_callbacks.py +0 -0
  41. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/console.py +0 -0
  42. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/core/cache_monitor.py +0 -0
  43. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/core/config_migration.py +0 -0
  44. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/core/context.py +0 -0
  45. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/core/context_manager.py +0 -0
  46. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/core/context_manager_factory.py +0 -0
  47. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/core/default_context_manager.py +0 -0
  48. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/core/ollama_context_manager.py +0 -0
  49. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/core/result.py +0 -0
  50. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/logging_config.py +0 -0
  51. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/loops/__init__.py +0 -0
  52. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/loops/chat_loop.py +0 -0
  53. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/parser.py +0 -0
  54. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/process_manager.py +0 -0
  55. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/prompts.py +0 -0
  56. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/providers/__init__.py +0 -0
  57. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/providers/base.py +0 -0
  58. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/providers/impl/claude.py +0 -0
  59. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/providers/impl/deepseek.py +0 -0
  60. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/providers/impl/gemini.py +0 -0
  61. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/providers/impl/glm.py +0 -0
  62. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/providers/impl/ollama.py +0 -0
  63. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/providers/impl/ollama_drivers/__init__.py +0 -0
  64. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/providers/impl/ollama_drivers/_errors.py +0 -0
  65. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/providers/impl/ollama_drivers/base.py +0 -0
  66. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/providers/impl/ollama_drivers/deepseek.py +0 -0
  67. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/providers/impl/ollama_drivers/generic_native.py +0 -0
  68. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/providers/impl/ollama_drivers/generic_xml.py +0 -0
  69. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/providers/impl/ollama_drivers/matrix.py +0 -0
  70. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/providers/impl/ollama_drivers/minimax.py +0 -0
  71. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/providers/impl/ollama_drivers/qwen3.py +0 -0
  72. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/providers/impl/ollama_drivers/registry.py +0 -0
  73. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/providers/impl/ollama_inspector.py +0 -0
  74. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/providers/impl/openai.py +0 -0
  75. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/providers/impl/qwen.py +0 -0
  76. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/providers/orchestrator.py +0 -0
  77. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/providers/retry.py +0 -0
  78. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/services/__init__.py +0 -0
  79. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/services/interactions.py +0 -0
  80. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/themes/__init__.py +0 -0
  81. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/themes/claude.py +0 -0
  82. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/themes/original.py +0 -0
  83. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tools/__init__.py +0 -0
  84. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tools/builtins/__init__.py +0 -0
  85. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tools/builtins/context.py +0 -0
  86. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tools/builtins/context_definitions.py +0 -0
  87. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tools/builtins/filesystem.py +0 -0
  88. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tools/builtins/filesystem_definitions.py +0 -0
  89. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tools/builtins/notes.py +0 -0
  90. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tools/builtins/notes_definitions.py +0 -0
  91. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tools/builtins/process_manager_definitions.py +0 -0
  92. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tools/builtins/search.py +0 -0
  93. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tools/builtins/search_definitions.py +0 -0
  94. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tools/builtins/shell.py +0 -0
  95. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tools/builtins/shell_definitions.py +0 -0
  96. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tools/builtins/tasks.py +0 -0
  97. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tools/builtins/tasks_definitions.py +0 -0
  98. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tools/builtins/utils_tools.py +0 -0
  99. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tools/builtins/utils_tools_definitions.py +0 -0
  100. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tools/builtins/web.py +0 -0
  101. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tools/builtins/web_definitions.py +0 -0
  102. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tools/definition.py +0 -0
  103. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tools/execution.py +0 -0
  104. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tools/hooks.py +0 -0
  105. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tools/normalization.py +0 -0
  106. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tools/plugin_api.py +0 -0
  107. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tools/plugin_github.py +0 -0
  108. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tools/plugin_manager.py +0 -0
  109. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tools/plugin_status.py +0 -0
  110. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tools/registry.py +0 -0
  111. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tools/schemas.py +0 -0
  112. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tools/utils.py +0 -0
  113. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tui/adapter.py +0 -0
  114. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tui/commands.py +0 -0
  115. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tui/helpers.py +0 -0
  116. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tui/keybindings.py +0 -0
  117. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tui/parser.py +0 -0
  118. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tui/screens.py +0 -0
  119. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tui/theme_manager.py +0 -0
  120. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tui/types.py +0 -0
  121. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/tui/widgets.py +0 -0
  122. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/ui.py +0 -0
  123. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/src/ayder_cli/version.py +0 -0
  124. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/COVERAGE.md +0 -0
  125. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/__init__.py +0 -0
  126. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/agents/__init__.py +0 -0
  127. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/agents/test_agent_tools.py +0 -0
  128. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/agents/test_callbacks.py +0 -0
  129. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/agents/test_config.py +0 -0
  130. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/agents/test_integration.py +0 -0
  131. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/agents/test_registry.py +0 -0
  132. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/agents/test_run.py +0 -0
  133. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/agents/test_runner.py +0 -0
  134. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/agents/test_runner_provider_unavailable.py +0 -0
  135. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/agents/test_tool.py +0 -0
  136. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/application/test_message_contract.py +0 -0
  137. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/client/test_main.py +0 -0
  138. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/convergence/__init__.py +0 -0
  139. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/convergence/test_execution_policy_parity.py +0 -0
  140. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/convergence/test_runtime_wiring.py +0 -0
  141. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/convergence/test_validation_path.py +0 -0
  142. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/core/test_cache_monitor.py +0 -0
  143. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/core/test_config.py +0 -0
  144. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/core/test_config_coverage.py +0 -0
  145. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/core/test_config_migration.py +0 -0
  146. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/core/test_config_provider_logging.py +0 -0
  147. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/core/test_config_retry.py +0 -0
  148. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/core/test_context_manager_factory.py +0 -0
  149. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/core/test_default_context_manager.py +0 -0
  150. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/core/test_default_context_manager_should_compact.py +0 -0
  151. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/core/test_ollama_context_manager.py +0 -0
  152. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/core/test_ollama_context_manager_cache_monitor.py +0 -0
  153. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/core/test_ollama_context_manager_max_history.py +0 -0
  154. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/core/test_parameter_aliasing.py +0 -0
  155. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/core/test_parser.py +0 -0
  156. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/loops/__init__.py +0 -0
  157. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/loops/test_base.py +0 -0
  158. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/loops/test_chat_loop_hook.py +0 -0
  159. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/loops/test_chat_loop_tool_truncation.py +0 -0
  160. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/providers/__init__.py +0 -0
  161. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/providers/ollama_drivers/__init__.py +0 -0
  162. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/providers/ollama_drivers/test_base.py +0 -0
  163. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/providers/ollama_drivers/test_deepseek.py +0 -0
  164. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/providers/ollama_drivers/test_errors.py +0 -0
  165. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/providers/ollama_drivers/test_fallback.py +0 -0
  166. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/providers/ollama_drivers/test_generic_native.py +0 -0
  167. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/providers/ollama_drivers/test_generic_xml.py +0 -0
  168. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/providers/ollama_drivers/test_matrix.py +0 -0
  169. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/providers/ollama_drivers/test_minimax.py +0 -0
  170. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/providers/ollama_drivers/test_provider_integration.py +0 -0
  171. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/providers/ollama_drivers/test_qwen3.py +0 -0
  172. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/providers/ollama_drivers/test_registry.py +0 -0
  173. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/providers/test_glm_async.py +0 -0
  174. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/providers/test_ollama_inspector.py +0 -0
  175. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/providers/test_ollama_native.py +0 -0
  176. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/providers/test_ollama_xml.py +0 -0
  177. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/providers/test_ollama_xml_autoroute.py +0 -0
  178. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/providers/test_ollama_xml_usage.py +0 -0
  179. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/providers/test_orchestrator_capabilities.py +0 -0
  180. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/providers/test_qwen_async.py +0 -0
  181. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/providers/test_retry_classifier.py +0 -0
  182. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/providers/test_retry_integration.py +0 -0
  183. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/providers/test_retrying_provider.py +0 -0
  184. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/services/test_boundary.py +0 -0
  185. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/services/test_interaction_sink.py +0 -0
  186. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/test_cli_callbacks.py +0 -0
  187. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/test_context.py +0 -0
  188. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/test_env_manager.py +0 -0
  189. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/test_logging_config.py +0 -0
  190. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/test_notes.py +0 -0
  191. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/test_process_manager.py +0 -0
  192. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/test_tasks.py +0 -0
  193. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/tools/__init__.py +0 -0
  194. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/tools/test_agent_notes.py +0 -0
  195. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/tools/test_definition_discovery.py +0 -0
  196. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/tools/test_di_injection.py +0 -0
  197. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/tools/test_impl.py +0 -0
  198. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/tools/test_impl_coverage.py +0 -0
  199. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/tools/test_path_security.py +0 -0
  200. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/tools/test_plugin_api.py +0 -0
  201. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/tools/test_plugin_github.py +0 -0
  202. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/tools/test_plugin_loading.py +0 -0
  203. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/tools/test_plugin_manager.py +0 -0
  204. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/tools/test_plugin_status.py +0 -0
  205. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/tools/test_registry.py +0 -0
  206. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/tools/test_registry_coverage.py +0 -0
  207. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/tools/test_result.py +0 -0
  208. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/tools/test_schemas.py +0 -0
  209. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/tools/test_search_codebase.py +0 -0
  210. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/tools/test_utils.py +0 -0
  211. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/tools/test_web.py +0 -0
  212. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/tui/__init__.py +0 -0
  213. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/tui/test_agent_list_screen.py +0 -0
  214. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/tui/test_do_clear_coordination.py +0 -0
  215. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/tui/test_follow_mode.py +0 -0
  216. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/tui/test_help_screen.py +0 -0
  217. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/tui/test_input_box.py +0 -0
  218. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/tui/test_keybindings.py +0 -0
  219. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/tui/test_multi_select_screen.py +0 -0
  220. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/tui/test_multiline_input.py +0 -0
  221. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/tui/test_paste.py +0 -0
  222. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/tui/test_pending_compact_consumer.py +0 -0
  223. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/tui/test_pull_delivery.py +0 -0
  224. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/tui/test_status_bar_badges.py +0 -0
  225. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/tui/test_turn_consumer.py +0 -0
  226. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/tui/test_widgets.py +0 -0
  227. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/ui/test_diff_preview.py +0 -0
  228. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/ui/test_plugin_autoenable.py +0 -0
  229. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/ui/test_provider_switch.py +0 -0
  230. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/ui/test_run_tui_startup.py +0 -0
  231. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/ui/test_tui_assistant_sanitization.py +0 -0
  232. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/ui/test_tui_chat_loop.py +0 -0
  233. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/ui/test_tui_commands_agent.py +0 -0
  234. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/ui/test_tui_commands_logging.py +0 -0
  235. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/ui/test_tui_commands_plugin.py +0 -0
  236. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/ui/test_tui_commands_provider.py +0 -0
  237. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/ui/test_tui_helpers.py +0 -0
  238. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/ui/test_tui_temporal_command.py +0 -0
  239. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/ui/test_tui_widgets.py +0 -0
  240. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/ui/test_ui.py +0 -0
  241. {ayder_cli-2.1.4 → ayder_cli-2.1.5}/tests/ui/test_ui_coverage.py +0 -0
  242. {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.4
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
- # Use a custom system prompt file
292
- ayder --prompt prompt-file.md "refactor this code"
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
- # Use a custom system prompt file
250
- ayder --prompt prompt-file.md "refactor this code"
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)
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "ayder-cli"
7
- version = "2.1.4"
7
+ version = "2.1.5"
8
8
  description = "AI agent for any LLMs"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
@@ -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
- base_prompt = get_system_prompt(cfg.prompt)
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="Inject custom system prompt from file",
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
- nargs="?",
97
- const="INFO",
98
- default=None,
99
- type=str.upper,
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
- # Priority: 1. --verbose level, 2. --logging-level, 3. config.logging_level
314
- effective_log_level = args.verbose or args.logging_level
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 is not None else None,
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.prompt,
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(permissions=granted, agent_mode=args.agent)
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(run_command(prompt, permissions=granted, agent_mode=args.agent))
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(prompt_tier="AGENTIC" if agent_mode else None)
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) -> int:
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(prompt, permissions=permissions, agent_mode=agent_mode)
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
- base_prompt = get_system_prompt(self.config.prompt)
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.messages[0]["content"] = (
448
- get_system_prompt(self.config.prompt) + tool_prompts + macro
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):