ayder-cli 2.3.1__tar.gz → 2.4.0__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 (323) hide show
  1. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/AGENTS.md +17 -0
  2. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/PKG-INFO +7 -1
  3. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/README.md +6 -0
  4. ayder_cli-2.4.0/docs/GATE.md +28 -0
  5. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/docs/PROJECT_STRUCTURE.md +2 -1
  6. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/docs/config.toml.example +9 -0
  7. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/pyproject.toml +1 -1
  8. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/scripts/check_exception_handlers.py +1 -1
  9. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/scripts/check_log_arguments.py +4 -4
  10. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/core/config.py +10 -0
  11. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/core/config_migration.py +2 -0
  12. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/providers/base.py +2 -1
  13. ayder_cli-2.4.0/src/ayder_cli/providers/evren.py +61 -0
  14. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/providers/impl/openai.py +4 -9
  15. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/themes/ayder.py +37 -0
  16. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/themes/claude.py +37 -0
  17. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/tui/commands.py +41 -2
  18. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/tui/screens.py +38 -3
  19. ayder_cli-2.4.0/tests/core/test_default_config_evren.py +44 -0
  20. ayder_cli-2.4.0/tests/providers/test_evren.py +79 -0
  21. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/providers/test_ollama_native.py +16 -0
  22. ayder_cli-2.4.0/tests/providers/test_openai_list_models.py +47 -0
  23. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/scripts/test_exception_gate.py +7 -4
  24. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/scripts/test_log_argument_gate.py +6 -6
  25. ayder_cli-2.4.0/tests/tui/test_evren_terms.py +230 -0
  26. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/uv.lock +1 -1
  27. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/.github/workflows/ci.yml +0 -0
  28. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/.github/workflows/python-package.yml +0 -0
  29. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/.gitignore +0 -0
  30. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/.python-version +0 -0
  31. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/LICENSE +0 -0
  32. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/docs/README_AGENT_WORKFLOW.md +0 -0
  33. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/docs/README_OLLAMA.md +0 -0
  34. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/docs/cc.png +0 -0
  35. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/docs/superpowers/plans/2026-06-26-file-editor-improvements.md +0 -0
  36. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/docs/superpowers/plans/2026-06-26-session-save-resume.md +0 -0
  37. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/docs/superpowers/specs/2026-06-26-file-editor-improvements-design.md +0 -0
  38. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/docs/superpowers/specs/2026-06-26-session-save-resume-design.md +0 -0
  39. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/scripts/check_logging_imports.py +0 -0
  40. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/scripts/exception_baseline.txt +0 -0
  41. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/scripts/find_percent_logging.py +0 -0
  42. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/scripts/log_argument_baseline.txt +0 -0
  43. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/scripts/logging_gates.py +0 -0
  44. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/__init__.py +0 -0
  45. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/__main__.py +0 -0
  46. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/agents/__init__.py +0 -0
  47. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/agents/callbacks.py +0 -0
  48. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/agents/config.py +0 -0
  49. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/agents/registry.py +0 -0
  50. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/agents/run.py +0 -0
  51. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/agents/runner.py +0 -0
  52. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/agents/tool.py +0 -0
  53. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/agents/worktree.py +0 -0
  54. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/application/README.md +0 -0
  55. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/application/__init__.py +0 -0
  56. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/application/execution_policy.py +0 -0
  57. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/application/message_contract.py +0 -0
  58. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/application/runtime_factory.py +0 -0
  59. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/application/validation.py +0 -0
  60. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/cli.py +0 -0
  61. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/cli_callbacks.py +0 -0
  62. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/cli_runner.py +0 -0
  63. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/console.py +0 -0
  64. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/core/cache_monitor.py +0 -0
  65. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/core/context.py +0 -0
  66. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/core/context_manager.py +0 -0
  67. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/core/context_manager_factory.py +0 -0
  68. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/core/default_context_manager.py +0 -0
  69. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/core/ollama_context_manager.py +0 -0
  70. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/core/reasoning.py +0 -0
  71. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/core/result.py +0 -0
  72. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/core/session.py +0 -0
  73. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/diagnostics.py +0 -0
  74. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/log.py +0 -0
  75. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/logging_config.py +0 -0
  76. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/loops/__init__.py +0 -0
  77. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/loops/chat_loop.py +0 -0
  78. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/masking.py +0 -0
  79. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/parser.py +0 -0
  80. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/process_manager.py +0 -0
  81. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/prompts.py +0 -0
  82. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/providers/__init__.py +0 -0
  83. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/providers/impl/claude.py +0 -0
  84. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/providers/impl/deepseek.py +0 -0
  85. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/providers/impl/gemini.py +0 -0
  86. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/providers/impl/glm.py +0 -0
  87. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/providers/impl/ollama.py +0 -0
  88. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/providers/impl/ollama_drivers/__init__.py +0 -0
  89. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/providers/impl/ollama_drivers/_errors.py +0 -0
  90. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/providers/impl/ollama_drivers/base.py +0 -0
  91. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/providers/impl/ollama_drivers/deepseek.py +0 -0
  92. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/providers/impl/ollama_drivers/generic_native.py +0 -0
  93. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/providers/impl/ollama_drivers/generic_xml.py +0 -0
  94. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/providers/impl/ollama_drivers/matrix.py +0 -0
  95. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/providers/impl/ollama_drivers/minimax.py +0 -0
  96. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/providers/impl/ollama_drivers/qwen3.py +0 -0
  97. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/providers/impl/ollama_drivers/registry.py +0 -0
  98. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/providers/impl/ollama_inspector.py +0 -0
  99. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/providers/impl/qwen.py +0 -0
  100. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/providers/orchestrator.py +0 -0
  101. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/providers/retry.py +0 -0
  102. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/services/__init__.py +0 -0
  103. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/services/interactions.py +0 -0
  104. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/services/messaging_inbox.py +0 -0
  105. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/themes/__init__.py +0 -0
  106. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/tools/__init__.py +0 -0
  107. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/tools/builtins/__init__.py +0 -0
  108. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/tools/builtins/background_process_tool.py +0 -0
  109. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/tools/builtins/context.py +0 -0
  110. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/tools/builtins/context_definitions.py +0 -0
  111. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/tools/builtins/filesystem.py +0 -0
  112. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/tools/builtins/filesystem_definitions.py +0 -0
  113. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/tools/builtins/note_tool.py +0 -0
  114. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/tools/builtins/notes.py +0 -0
  115. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/tools/builtins/notes_definitions.py +0 -0
  116. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/tools/builtins/process_manager_definitions.py +0 -0
  117. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/tools/builtins/search.py +0 -0
  118. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/tools/builtins/search_definitions.py +0 -0
  119. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/tools/builtins/shell.py +0 -0
  120. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/tools/builtins/shell_definitions.py +0 -0
  121. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/tools/builtins/skill.py +0 -0
  122. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/tools/builtins/skill_definitions.py +0 -0
  123. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/tools/builtins/task_definitions.py +0 -0
  124. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/tools/builtins/task_tool.py +0 -0
  125. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/tools/builtins/tasks.py +0 -0
  126. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/tools/builtins/utils_tools.py +0 -0
  127. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/tools/builtins/utils_tools_definitions.py +0 -0
  128. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/tools/builtins/web.py +0 -0
  129. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/tools/builtins/web_definitions.py +0 -0
  130. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/tools/definition.py +0 -0
  131. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/tools/execution.py +0 -0
  132. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/tools/hooks.py +0 -0
  133. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/tools/mcp.py +0 -0
  134. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/tools/mcp_client.py +0 -0
  135. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/tools/normalization.py +0 -0
  136. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/tools/plugin_api.py +0 -0
  137. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/tools/plugin_github.py +0 -0
  138. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/tools/plugin_manager.py +0 -0
  139. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/tools/plugin_status.py +0 -0
  140. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/tools/registry.py +0 -0
  141. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/tools/schemas.py +0 -0
  142. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/tools/utils.py +0 -0
  143. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/tui/__init__.py +0 -0
  144. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/tui/adapter.py +0 -0
  145. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/tui/app.py +0 -0
  146. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/tui/helpers.py +0 -0
  147. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/tui/keybindings.py +0 -0
  148. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/tui/parser.py +0 -0
  149. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/tui/rendering.py +0 -0
  150. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/tui/theme_manager.py +0 -0
  151. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/tui/types.py +0 -0
  152. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/tui/widgets.py +0 -0
  153. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/ui.py +0 -0
  154. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/src/ayder_cli/version.py +0 -0
  155. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/COVERAGE.md +0 -0
  156. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/__init__.py +0 -0
  157. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/agents/__init__.py +0 -0
  158. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/agents/test_agent_consolidated.py +0 -0
  159. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/agents/test_agent_severity.py +0 -0
  160. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/agents/test_agent_tools.py +0 -0
  161. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/agents/test_callbacks.py +0 -0
  162. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/agents/test_config.py +0 -0
  163. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/agents/test_integration.py +0 -0
  164. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/agents/test_queue.py +0 -0
  165. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/agents/test_registry.py +0 -0
  166. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/agents/test_run.py +0 -0
  167. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/agents/test_runner.py +0 -0
  168. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/agents/test_runner_provider_unavailable.py +0 -0
  169. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/agents/test_tool.py +0 -0
  170. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/agents/test_worktree_helpers.py +0 -0
  171. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/agents/test_worktree_isolation.py +0 -0
  172. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/application/test_execution_result_truthfulness.py +0 -0
  173. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/application/test_message_contract.py +0 -0
  174. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/application/test_runtime_factory.py +0 -0
  175. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/client/test_main.py +0 -0
  176. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/conftest.py +0 -0
  177. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/convergence/__init__.py +0 -0
  178. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/convergence/test_execution_policy_parity.py +0 -0
  179. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/convergence/test_runtime_wiring.py +0 -0
  180. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/convergence/test_validation_path.py +0 -0
  181. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/core/test_asyncio_bridge.py +0 -0
  182. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/core/test_cache_monitor.py +0 -0
  183. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/core/test_config.py +0 -0
  184. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/core/test_config_coverage.py +0 -0
  185. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/core/test_config_migration.py +0 -0
  186. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/core/test_config_provider_logging.py +0 -0
  187. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/core/test_config_retry.py +0 -0
  188. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/core/test_config_v2.py +0 -0
  189. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/core/test_context_manager_factory.py +0 -0
  190. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/core/test_default_context_manager.py +0 -0
  191. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/core/test_default_context_manager_should_compact.py +0 -0
  192. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/core/test_event_emission.py +0 -0
  193. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/core/test_event_stream.py +0 -0
  194. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/core/test_exception_guarantee.py +0 -0
  195. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/core/test_exception_hooks.py +0 -0
  196. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/core/test_log_facade.py +0 -0
  197. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/core/test_logging_config_keys.py +0 -0
  198. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/core/test_logging_sinks.py +0 -0
  199. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/core/test_loguru_caplog.py +0 -0
  200. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/core/test_ollama_context_manager.py +0 -0
  201. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/core/test_ollama_context_manager_cache_monitor.py +0 -0
  202. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/core/test_ollama_context_manager_max_history.py +0 -0
  203. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/core/test_parameter_aliasing.py +0 -0
  204. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/core/test_parser.py +0 -0
  205. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/core/test_reasoning_config.py +0 -0
  206. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/core/test_session.py +0 -0
  207. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/core/test_session_id_threading.py +0 -0
  208. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/core/test_sink_masking.py +0 -0
  209. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/loops/__init__.py +0 -0
  210. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/loops/test_base.py +0 -0
  211. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/loops/test_chat_loop_hook.py +0 -0
  212. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/loops/test_chat_loop_tool_truncation.py +0 -0
  213. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/loops/test_loop_severity.py +0 -0
  214. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/loops/test_required_args_aliases.py +0 -0
  215. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/providers/__init__.py +0 -0
  216. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/providers/ollama_drivers/__init__.py +0 -0
  217. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/providers/ollama_drivers/test_base.py +0 -0
  218. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/providers/ollama_drivers/test_deepseek.py +0 -0
  219. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/providers/ollama_drivers/test_errors.py +0 -0
  220. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/providers/ollama_drivers/test_fallback.py +0 -0
  221. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/providers/ollama_drivers/test_generic_native.py +0 -0
  222. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/providers/ollama_drivers/test_generic_xml.py +0 -0
  223. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/providers/ollama_drivers/test_matrix.py +0 -0
  224. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/providers/ollama_drivers/test_minimax.py +0 -0
  225. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/providers/ollama_drivers/test_provider_integration.py +0 -0
  226. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/providers/ollama_drivers/test_qwen3.py +0 -0
  227. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/providers/ollama_drivers/test_registry.py +0 -0
  228. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/providers/test_glm_async.py +0 -0
  229. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/providers/test_ollama_inspector.py +0 -0
  230. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/providers/test_ollama_parallel_tool_call_ids.py +0 -0
  231. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/providers/test_ollama_xml.py +0 -0
  232. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/providers/test_ollama_xml_autoroute.py +0 -0
  233. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/providers/test_ollama_xml_usage.py +0 -0
  234. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/providers/test_orchestrator_capabilities.py +0 -0
  235. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/providers/test_provider_channel_binding.py +0 -0
  236. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/providers/test_provider_severity.py +0 -0
  237. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/providers/test_qwen_async.py +0 -0
  238. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/providers/test_reasoning_effort.py +0 -0
  239. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/providers/test_retry_classifier.py +0 -0
  240. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/providers/test_retry_integration.py +0 -0
  241. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/providers/test_retrying_provider.py +0 -0
  242. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/scripts/test_logging_policy_gates.py +0 -0
  243. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/scripts/test_loguru_private_shape.py +0 -0
  244. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/services/test_boundary.py +0 -0
  245. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/services/test_interaction_sink.py +0 -0
  246. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/services/test_messaging_inbox.py +0 -0
  247. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/test_cli.py +0 -0
  248. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/test_cli_callbacks.py +0 -0
  249. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/test_context.py +0 -0
  250. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/test_env_manager.py +0 -0
  251. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/test_logging_config.py +0 -0
  252. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/test_notes.py +0 -0
  253. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/test_process_manager.py +0 -0
  254. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/test_tasks.py +0 -0
  255. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/tools/__init__.py +0 -0
  256. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/tools/test_agent_notes.py +0 -0
  257. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/tools/test_background_process_tool.py +0 -0
  258. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/tools/test_bash_tool.py +0 -0
  259. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/tools/test_definition_discovery.py +0 -0
  260. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/tools/test_di_injection.py +0 -0
  261. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/tools/test_file_editor_hardening.py +0 -0
  262. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/tools/test_file_editor_size_limit.py +0 -0
  263. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/tools/test_impl.py +0 -0
  264. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/tools/test_impl_coverage.py +0 -0
  265. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/tools/test_mcp.py +0 -0
  266. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/tools/test_mcp_client.py +0 -0
  267. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/tools/test_note_tool.py +0 -0
  268. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/tools/test_notes_api.py +0 -0
  269. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/tools/test_path_security.py +0 -0
  270. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/tools/test_plugin_api.py +0 -0
  271. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/tools/test_plugin_github.py +0 -0
  272. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/tools/test_plugin_loading.py +0 -0
  273. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/tools/test_plugin_manager.py +0 -0
  274. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/tools/test_plugin_status.py +0 -0
  275. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/tools/test_registry.py +0 -0
  276. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/tools/test_registry_coverage.py +0 -0
  277. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/tools/test_result.py +0 -0
  278. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/tools/test_schemas.py +0 -0
  279. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/tools/test_search_codebase.py +0 -0
  280. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/tools/test_search_hardening.py +0 -0
  281. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/tools/test_skill_tool.py +0 -0
  282. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/tools/test_task_tool.py +0 -0
  283. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/tools/test_tool_severity.py +0 -0
  284. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/tools/test_unexpected_args.py +0 -0
  285. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/tools/test_utils.py +0 -0
  286. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/tools/test_web.py +0 -0
  287. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/tui/__init__.py +0 -0
  288. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/tui/test_agent_list_screen.py +0 -0
  289. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/tui/test_do_clear_coordination.py +0 -0
  290. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/tui/test_follow_mode.py +0 -0
  291. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/tui/test_help_screen.py +0 -0
  292. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/tui/test_input_box.py +0 -0
  293. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/tui/test_keybindings.py +0 -0
  294. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/tui/test_multi_select_screen.py +0 -0
  295. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/tui/test_multiline_input.py +0 -0
  296. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/tui/test_paste.py +0 -0
  297. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/tui/test_peer_message_turn.py +0 -0
  298. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/tui/test_pending_compact_consumer.py +0 -0
  299. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/tui/test_pull_delivery.py +0 -0
  300. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/tui/test_resume.py +0 -0
  301. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/tui/test_status_bar_badges.py +0 -0
  302. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/tui/test_themes.py +0 -0
  303. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/tui/test_turn_consumer.py +0 -0
  304. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/tui/test_widgets.py +0 -0
  305. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/ui/test_confirm_screen.py +0 -0
  306. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/ui/test_diff_preview.py +0 -0
  307. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/ui/test_plugin_autoenable.py +0 -0
  308. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/ui/test_provider_switch.py +0 -0
  309. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/ui/test_run_tui_startup.py +0 -0
  310. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/ui/test_tui_assistant_sanitization.py +0 -0
  311. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/ui/test_tui_chat_loop.py +0 -0
  312. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/ui/test_tui_commands_agent.py +0 -0
  313. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/ui/test_tui_commands_effort.py +0 -0
  314. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/ui/test_tui_commands_logging.py +0 -0
  315. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/ui/test_tui_commands_plugin.py +0 -0
  316. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/ui/test_tui_commands_provider.py +0 -0
  317. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/ui/test_tui_commands_skill.py +0 -0
  318. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/ui/test_tui_helpers.py +0 -0
  319. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/ui/test_tui_temporal_command.py +0 -0
  320. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/ui/test_tui_widgets.py +0 -0
  321. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/ui/test_ui.py +0 -0
  322. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/ui/test_ui_coverage.py +0 -0
  323. {ayder_cli-2.3.1 → ayder_cli-2.4.0}/tests/ui/test_ui_verbose.py +0 -0
@@ -87,6 +87,7 @@ ayder-cli/
87
87
  │ ├── providers/ # LLM provider implementations
88
88
  │ │ ├── base.py # AIProvider protocol, NormalizedStreamChunk
89
89
  │ │ ├── orchestrator.py # Factory: create(config) → provider
90
+ │ │ ├── evren.py # evren gateway terms-of-use acceptance (/model)
90
91
  │ │ └── impl/ # Individual provider drivers
91
92
  │ │ ├── openai.py # OpenAIProvider (base for most drivers)
92
93
  │ │ ├── ollama.py # OllamaProvider (native + XML fallback)
@@ -385,6 +386,22 @@ uv run poe test-cov # With coverage report
385
386
  - **Coverage**: Maintain existing coverage levels
386
387
  - **Timeout**: Use `--timeout=30` to prevent hanging tests
387
388
 
389
+ ### Test Definitions
390
+
391
+ Plans and reviews use these values.
392
+
393
+ - **Kinds**
394
+ - `baseline regression` — fails on the parent commit for a named reason, passes on the fix.
395
+ - `new interface` — fails at its assertion once a minimal callable version exists (never on an `ImportError`).
396
+ - `preservation` — passes before and after the change; its assertion depends on the behaviour it preserves.
397
+ - **Layers**
398
+ - `unit` — a function or class, without a Textual app.
399
+ - `tui` — a Textual app driven under `App.run_test()`.
400
+ - **Environment**
401
+ - `local-faked` — the only one. No network: HTTP is faked with `httpx.MockTransport` passed to the SDK client; other I/O uses `unittest.mock`. No test reaches a real provider.
402
+
403
+ The gate procedure lives in [docs/GATE.md](docs/GATE.md).
404
+
388
405
  ### Writing Tests
389
406
 
390
407
  ```python
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: ayder-cli
3
- Version: 2.3.1
3
+ Version: 2.4.0
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
@@ -155,6 +155,8 @@ in the core install.
155
155
 
156
156
  ayder-cli uses a flexible profile-based configuration system. On the first run, it creates a config file at `~/.ayder/config.toml`.
157
157
 
158
+ The generated file includes an `[llm.evren]` profile for the evren gateway (`https://evren-llmapi.ssyz.org.tr/v1`) with an empty `api_key`. Fill in your key and switch to it with `/provider evren`.
159
+
158
160
  **Key Concepts:**
159
161
  - **Profile Name:** A custom named section (e.g., `[llm.my_ollama]`). You can define as many profiles as you want.
160
162
  - **Driver:** The underlying native SDK or adapter used by the profile (`ollama`, `openai`, `anthropic`, `google`, `deepseek`, `qwen`, or `glm`). Each driver guarantees full support for native tool calling and streaming.
@@ -319,6 +321,10 @@ Once a profile is active, use `/model` in the TUI to swap models on the fly:
319
321
  - **Interactive Picker:** `/model` with no arguments queries the active driver for available models and opens a picker.
320
322
  - **Direct Switch:** `/model <model-name>` immediately switches to that model.
321
323
 
324
+ If the provider rejects the listing request, `/model` shows the server's error.
325
+
326
+ For the evren gateway (`evren-llmapi.ssyz.org.tr`), if your account has not accepted the current terms of use, `/model` shows them and asks you to Approve or Deny; Approve accepts them on the server.
327
+
322
328
  Changes made with `/model` apply to the current session only. To make a model the permanent default, update `model = "..."` in your `config.toml`.
323
329
 
324
330
  ## Usage
@@ -112,6 +112,8 @@ in the core install.
112
112
 
113
113
  ayder-cli uses a flexible profile-based configuration system. On the first run, it creates a config file at `~/.ayder/config.toml`.
114
114
 
115
+ The generated file includes an `[llm.evren]` profile for the evren gateway (`https://evren-llmapi.ssyz.org.tr/v1`) with an empty `api_key`. Fill in your key and switch to it with `/provider evren`.
116
+
115
117
  **Key Concepts:**
116
118
  - **Profile Name:** A custom named section (e.g., `[llm.my_ollama]`). You can define as many profiles as you want.
117
119
  - **Driver:** The underlying native SDK or adapter used by the profile (`ollama`, `openai`, `anthropic`, `google`, `deepseek`, `qwen`, or `glm`). Each driver guarantees full support for native tool calling and streaming.
@@ -276,6 +278,10 @@ Once a profile is active, use `/model` in the TUI to swap models on the fly:
276
278
  - **Interactive Picker:** `/model` with no arguments queries the active driver for available models and opens a picker.
277
279
  - **Direct Switch:** `/model <model-name>` immediately switches to that model.
278
280
 
281
+ If the provider rejects the listing request, `/model` shows the server's error.
282
+
283
+ For the evren gateway (`evren-llmapi.ssyz.org.tr`), if your account has not accepted the current terms of use, `/model` shows them and asks you to Approve or Deny; Approve accepts them on the server.
284
+
279
285
  Changes made with `/model` apply to the current session only. To make a model the permanent default, update `model = "..."` in your `config.toml`.
280
286
 
281
287
  ## Usage
@@ -0,0 +1,28 @@
1
+ # Gate
2
+
3
+ The procedure an agent follows to gate a candidate commit. Follow the steps in order and do
4
+ not interpret the results. Never edit a file, fix a failure, skip a step or create a worktree.
5
+ Test definitions live in [AGENTS.md](../AGENTS.md#test-definitions).
6
+
7
+ Input: the candidate SHA.
8
+
9
+ 1. `git rev-parse HEAD` → expected: exactly the candidate SHA.
10
+ 2. `git status --porcelain` → expected: empty output.
11
+ 3. `uv run poe check-all` → expected: exit code 0. This runs lint, typecheck, the logging
12
+ gates and the test suite, and stops at the first failing task.
13
+ 4. Copy the final pytest summary line (`=== N passed, M skipped in …s ===`) verbatim.
14
+
15
+ Last measured: `2494 passed, 2 skipped` on `feat/evren-terms` with the evren default profile (2026-09-29).
16
+
17
+ ## Report format
18
+
19
+ ```text
20
+ SHA: <candidate SHA>
21
+ 1 git rev-parse HEAD: <output> (match|MISMATCH)
22
+ 2 git status --porcelain: <empty|output>
23
+ 3 uv run poe check-all: exit <code>
24
+ 4 summary: <pytest summary line verbatim>
25
+ GATE COMPLETE | GATE FAILED at step <n>
26
+ ```
27
+
28
+ `GATE COMPLETE` only when steps 1–3 meet their expected results.
@@ -220,7 +220,7 @@ cli.py:main()
220
220
  | `tui/adapter.py` | Adapter glue between `AyderApp` and `ChatLoop` | callback wiring helpers |
221
221
  | `tui/commands.py` | Slash command handlers | `COMMAND_MAP`, `handle_*()` |
222
222
  | `tui/widgets.py` | Custom widgets | `ChatView`, `ToolPanel`, `CLIInputBar`, `StatusBar`, `AutoCompleteInput` |
223
- | `tui/screens.py` | Modal screens | `AgentListScreen`, `CLIConfirmScreen`, `CLIHelpScreen`, `CLIMultiSelectScreen`, `CLIPermissionScreen`, `CLISafeModeScreen`, `CLISelectScreen`, `TaskEditScreen` |
223
+ | `tui/screens.py` | Modal screens | `AgentListScreen`, `CLIConfirmScreen`, `CLIHelpScreen`, `CLIMultiSelectScreen`, `CLIPermissionScreen`, `CLISafeModeScreen`, `CLISelectScreen`, `EvrenTermsScreen`, `TaskEditScreen` |
224
224
  | `tui/parser.py` | TUI-specific parsing | `content_processor()` |
225
225
  | `tui/helpers.py` | UI helpers | `create_tui_banner()` |
226
226
  | `tui/keybindings.py` | Keybinding declarations | `BINDINGS` |
@@ -242,6 +242,7 @@ All LLM provider implementations live here. Previously some sat under `services/
242
242
  | `providers/__init__.py` | Re-exports | `AIProvider`, `NormalizedStreamChunk`, `ToolCallDef`, `provider_orchestrator` |
243
243
  | `providers/base.py` | Provider protocol + shared DTOs | `AIProvider`, `NormalizedStreamChunk`, `ToolCallDef`, `_ToolCall`, `_FunctionCall` |
244
244
  | `providers/orchestrator.py` | Driver-keyed provider factory | `ProviderOrchestrator`, `provider_orchestrator` |
245
+ | `providers/evren.py` | evren gateway terms-of-use acceptance (used by `/model`) | `EVREN_HOST`, `EvrenTerms`, `is_evren_terms_error`, `fetch_terms`, `accept_terms` |
245
246
  | `providers/impl/ollama.py` | Native Ollama provider (ollama SDK) | `OllamaProvider` |
246
247
  | `providers/impl/ollama_inspector.py` | Ollama model introspection | `OllamaInspector`, `ModelInfo`, `RuntimeState` |
247
248
  | `providers/impl/openai.py` | OpenAI / OpenAI-compatible backend | `OpenAIProvider` |
@@ -206,6 +206,15 @@ think = true # default; set false if the selected model r
206
206
  # reasoning_effort = "medium" # choose a value supported by the selected model
207
207
  # # temperature / think are NOT applied by the openai driver.
208
208
 
209
+ # evren gateway (OpenAI-compatible). A fresh config.toml ships this profile with
210
+ # an empty api_key. The first `/model` asks you to accept evren's terms of use.
211
+ # [llm.evren]
212
+ # driver = "openai"
213
+ # base_url = "https://evren-llmapi.ssyz.org.tr/v1"
214
+ # api_key = "" # your evren key
215
+ # model = "deepseek-v4-flash"
216
+ # num_ctx = 131072
217
+
209
218
  # =============================================================================
210
219
  # Agents
211
220
  # =============================================================================
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "ayder-cli"
7
- version = "2.3.1"
7
+ version = "2.4.0"
8
8
  description = "AI agent for any LLMs"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
@@ -24,7 +24,7 @@ DEFAULT_BASELINE = REPO / "scripts" / "exception_baseline.txt"
24
24
 
25
25
  # Frozen census (§C10). A different number means the gate is looking at the
26
26
  # wrong tree, or the census moved without the plan being updated.
27
- EXPECTED_BROAD = 138
27
+ EXPECTED_BROAD = 137
28
28
  BRANCHING = (ast.If, ast.Try, ast.For, ast.While, ast.With, ast.Match)
29
29
  TERMINAL = (ast.Return, ast.Break, ast.Continue, ast.Pass)
30
30
 
@@ -203,13 +203,13 @@ FROZEN_CHAIN_SITES: dict[tuple[str, str, str, str], tuple[str, int]] = {
203
203
  ("log.py", "emit_event", "bind", "**"): ("Name(id='fields', ctx=Load())", 1),
204
204
  }
205
205
 
206
- # The 100 `exception=True` inputs are homogeneous, so they are governed as one
207
- # package-wide shape/count rule rather than 100 site identities - their per-site
206
+ # The 101 `exception=True` inputs are homogeneous, so they are governed as one
207
+ # package-wide shape/count rule rather than 101 site identities - their per-site
208
208
  # discipline is C10's.
209
- FROZEN_EXCEPTION_TRUE = 100
209
+ FROZEN_EXCEPTION_TRUE = 101
210
210
 
211
211
  # Live chain inputs, counted per method. Printed on every run.
212
- CHAIN_TALLY: dict[str, int] = {"bind": 6, "opt": 104, "patch": 0}
212
+ CHAIN_TALLY: dict[str, int] = {"bind": 6, "opt": 105, "patch": 0}
213
213
 
214
214
  # The reviewed composition of THE COMMITTED baseline, frozen by the Phase 5
215
215
  # census: 252 retained non-path + 22 paths + 2 deferred interpolated rows, plus
@@ -45,6 +45,16 @@ DEFAULTS: Dict[str, Any] = {
45
45
  "model": "gemini-3-flash",
46
46
  "num_ctx": 65536,
47
47
  },
48
+ # Extra [llm.<name>] profiles written into a freshly created config.toml.
49
+ "extra_llm_profiles": {
50
+ "evren": {
51
+ "driver": "openai",
52
+ "base_url": "https://evren-llmapi.ssyz.org.tr/v1",
53
+ "api_key": "",
54
+ "model": "deepseek-v4-flash",
55
+ "num_ctx": 131072,
56
+ },
57
+ },
48
58
  "editor": "vim",
49
59
  "verbose": False,
50
60
  "logging": {
@@ -74,6 +74,8 @@ def render_v2_config(
74
74
  section = dict(defaults.get(provider, {}))
75
75
  section.setdefault("driver", _DRIVER_BY_PROVIDER[provider])
76
76
  llm_profiles[provider] = section
77
+ for name, section in defaults.get("extra_llm_profiles", {}).items():
78
+ llm_profiles[name] = dict(section)
77
79
  if llm_overrides:
78
80
  for name, section in llm_overrides.items():
79
81
  merged = dict(llm_profiles.get(name, {}))
@@ -56,7 +56,8 @@ class AIProvider(ABC):
56
56
  List available models from the provider.
57
57
 
58
58
  Returns:
59
- List of model name strings. Empty list if not supported or on error.
59
+ List of model name strings. Empty list if the driver does not
60
+ support listing; drivers may raise on server errors.
60
61
  """
61
62
  return []
62
63
 
@@ -0,0 +1,61 @@
1
+ """evren LLM gateway: terms-of-use acceptance.
2
+
3
+ The gateway (https://evren-llmapi.ssyz.org.tr) is OpenAI-compatible but refuses every
4
+ call with HTTP 403 ``terms_not_accepted`` until the account accepts the current terms.
5
+ """
6
+
7
+ from dataclasses import dataclass
8
+ from typing import Final
9
+ from urllib.parse import urlsplit
10
+
11
+ from openai import AsyncOpenAI, PermissionDeniedError
12
+
13
+ from ayder_cli.core.config import Config
14
+
15
+ EVREN_HOST: Final[str] = "evren-llmapi.ssyz.org.tr"
16
+
17
+
18
+ @dataclass(frozen=True)
19
+ class EvrenTerms:
20
+ """One published version of the evren terms of use."""
21
+
22
+ version: int
23
+ content: str
24
+
25
+
26
+ def is_evren_terms_error(base_url: str | None, exc: BaseException) -> bool:
27
+ """True when ``exc`` is evren refusing a call until its terms are accepted."""
28
+ if not base_url or urlsplit(base_url).hostname != EVREN_HOST:
29
+ return False
30
+ return isinstance(exc, PermissionDeniedError) and exc.code == "terms_not_accepted"
31
+
32
+
33
+ def make_client(config: Config) -> AsyncOpenAI:
34
+ """Build a client for the provider's configured base URL and key."""
35
+ return AsyncOpenAI(base_url=config.base_url, api_key=config.api_key)
36
+
37
+
38
+ async def fetch_terms(client: AsyncOpenAI) -> EvrenTerms:
39
+ """Fetch the current terms text.
40
+
41
+ Raises the SDK's ``APIError`` on a failed request and ``ValueError`` on a
42
+ response without an integer ``version`` and a string ``content``.
43
+ """
44
+ data = await client.get("terms/text", cast_to=object)
45
+ if not isinstance(data, dict):
46
+ raise ValueError(f"unexpected evren terms response: {data!r}")
47
+ version, content = data.get("version"), data.get("content")
48
+ if not isinstance(version, int) or not isinstance(content, str):
49
+ raise ValueError(f"unexpected evren terms response: {data!r}")
50
+ return EvrenTerms(version, content)
51
+
52
+
53
+ async def accept_terms(client: AsyncOpenAI, version: int) -> int:
54
+ """Accept terms ``version``; return the version the server recorded.
55
+
56
+ Falls back to ``version`` when the response does not name one. Raises the
57
+ SDK's ``APIError`` on a failed request.
58
+ """
59
+ data = await client.post("terms/accept", body={"version": version}, cast_to=object)
60
+ accepted = data.get("accepted_version") if isinstance(data, dict) else None
61
+ return accepted if isinstance(accepted, int) else version
@@ -36,16 +36,11 @@ class OpenAIProvider(AIProvider):
36
36
  async def list_models(self) -> List[str]:
37
37
  """
38
38
  List available models from the OpenAI-compatible API.
39
-
40
- Returns empty list if the API doesn't support model listing.
39
+
40
+ Server and transport errors propagate so the caller can report them.
41
41
  """
42
- try:
43
- response = await self.client.models.list()
44
- # Extract model IDs from the response
45
- return [model.id for model in response.data if hasattr(model, "id")]
46
- except Exception: # noqa: BLE001 - OpenAI SDK boundary: servers that omit model listing raise arbitrary client errors
47
- logger.opt(exception=True).warning("Failed to list models")
48
- return []
42
+ response = await self.client.models.list()
43
+ return [model.id for model in response.data if hasattr(model, "id")]
49
44
 
50
45
  async def chat(
51
46
  self,
@@ -358,6 +358,43 @@ CLIHelpScreen #help-content {
358
358
  height: auto;
359
359
  }
360
360
 
361
+ /* EvrenTermsScreen - evren terms-of-use modal with Approve / Deny.
362
+ Targets 'EvrenTermsScreen' in screens.py. */
363
+ EvrenTermsScreen {
364
+ align: center middle;
365
+ }
366
+
367
+ EvrenTermsScreen > Vertical {
368
+ width: 90%;
369
+ height: 85%;
370
+ background: ansi_black;
371
+ border: solid ansi_bright_blue;
372
+ padding: 1 2;
373
+ }
374
+
375
+ EvrenTermsScreen .prompt {
376
+ text-style: bold;
377
+ color: ansi_bright_blue;
378
+ text-align: center;
379
+ width: 100%;
380
+ margin-bottom: 1;
381
+ }
382
+
383
+ EvrenTermsScreen #terms-scroll {
384
+ height: 1fr;
385
+ color: ansi_white;
386
+ }
387
+
388
+ EvrenTermsScreen #terms-buttons {
389
+ height: auto;
390
+ align: center middle;
391
+ margin-top: 1;
392
+ }
393
+
394
+ EvrenTermsScreen Button {
395
+ margin: 0 2;
396
+ }
397
+
361
398
  /* Generic text selection highlight color. */
362
399
  .selection {
363
400
  background: ansi_blue;
@@ -314,6 +314,43 @@ CLIHelpScreen #help-content {
314
314
  height: auto;
315
315
  }
316
316
 
317
+ /* EvrenTermsScreen - evren terms-of-use modal with Approve / Deny.
318
+ Targets 'EvrenTermsScreen' in screens.py. */
319
+ EvrenTermsScreen {
320
+ align: center middle;
321
+ }
322
+
323
+ EvrenTermsScreen > Vertical {
324
+ width: 90%;
325
+ height: 85%;
326
+ background: #12122a;
327
+ border: solid #5eaff5;
328
+ padding: 1 2;
329
+ }
330
+
331
+ EvrenTermsScreen .prompt {
332
+ text-style: bold;
333
+ color: #5eaff5;
334
+ text-align: center;
335
+ width: 100%;
336
+ margin-bottom: 1;
337
+ }
338
+
339
+ EvrenTermsScreen #terms-scroll {
340
+ height: 1fr;
341
+ color: #b8b8c8;
342
+ }
343
+
344
+ EvrenTermsScreen #terms-buttons {
345
+ height: auto;
346
+ align: center middle;
347
+ margin-top: 1;
348
+ }
349
+
350
+ EvrenTermsScreen Button {
351
+ margin: 0 2;
352
+ }
353
+
317
354
  /* Generic text selection highlight color. */
318
355
  .selection {
319
356
  background: #264f78;
@@ -15,11 +15,14 @@ from ayder_cli.core.context import ProjectContext
15
15
  from ayder_cli.core.reasoning import get_effort_label
16
16
  from ayder_cli.log import LOG_LEVELS, get_logger
17
17
  from ayder_cli.logging_config import setup_logging
18
- from ayder_cli.providers import provider_orchestrator, ProviderUnavailableError
18
+ import openai
19
+
20
+ from ayder_cli.providers import evren, provider_orchestrator, ProviderUnavailableError
19
21
  from ayder_cli.tools.builtins.skill import SkillInfo, discover_skills, skill
20
22
  from ayder_cli.tui.screens import (
21
23
  AgentListScreen,
22
24
  CLISelectScreen,
25
+ EvrenTermsScreen,
23
26
  CLIMultiSelectScreen,
24
27
  CLIPermissionScreen,
25
28
  TaskEditScreen,
@@ -136,7 +139,9 @@ def _apply_provider_switch(
136
139
  app.request_turn(prepare=_prepare, run_loop=False)
137
140
 
138
141
 
139
- async def _list_and_show_models(app: AyderApp, chat_view: ChatView) -> None:
142
+ async def _list_and_show_models(
143
+ app: AyderApp, chat_view: ChatView, *, terms_prompted: bool = False
144
+ ) -> None:
140
145
  """Async helper to list models and show selector."""
141
146
  try:
142
147
  models = await app.llm.list_models()
@@ -173,9 +178,43 @@ async def _list_and_show_models(app: AyderApp, chat_view: ChatView) -> None:
173
178
  )
174
179
  except Exception as e: # noqa: BLE001 - slash-command guard: /model reports listing failures in chat rather than killing the app
175
180
  logger.opt(exception=True).error("Model listing failed")
181
+ if not terms_prompted and evren.is_evren_terms_error(app.config.base_url, e):
182
+ await _prompt_evren_terms(app, chat_view)
183
+ return
176
184
  chat_view.add_system_message(f"Error listing models: {e}")
177
185
 
178
186
 
187
+ async def _prompt_evren_terms(app: AyderApp, chat_view: ChatView) -> None:
188
+ """Show the evren terms; on Approve accept them and list models again."""
189
+ client = evren.make_client(app.config)
190
+ try:
191
+ terms = await evren.fetch_terms(client)
192
+ except (openai.APIError, ValueError) as e:
193
+ logger.opt(exception=True).warning("Fetching evren terms failed")
194
+ chat_view.add_system_message(f"Could not load evren terms: {e}")
195
+ return
196
+
197
+ async def _accept_and_relist() -> None:
198
+ try:
199
+ accepted = await evren.accept_terms(client, terms.version)
200
+ except openai.APIError as e:
201
+ logger.opt(exception=True).warning("Accepting evren terms failed")
202
+ chat_view.add_system_message(f"Could not accept evren terms: {e}")
203
+ return
204
+ chat_view.add_system_message(f"Accepted evren terms v{accepted}.")
205
+ await _list_and_show_models(app, chat_view, terms_prompted=True)
206
+
207
+ def on_decision(approved: bool | None) -> None:
208
+ if approved:
209
+ app.run_worker(_accept_and_relist(), exclusive=False)
210
+ else:
211
+ chat_view.add_system_message(
212
+ "evren terms not accepted; models cannot be listed until they are."
213
+ )
214
+
215
+ app.push_screen(EvrenTermsScreen(terms), on_decision)
216
+
217
+
179
218
  def handle_model(app: AyderApp, args: str, chat_view: ChatView) -> None:
180
219
  """Handle /model command."""
181
220
  if not args.strip():
@@ -1,12 +1,13 @@
1
- """Modal screens for the TUI: confirm, permission, safe mode, select, task edit, help."""
1
+ """Modal screens for the TUI: confirm, permission, safe mode, select, task edit, help, evren terms."""
2
2
 
3
3
  from textual.app import ComposeResult
4
- from textual.containers import Vertical, VerticalScroll
4
+ from textual.containers import Horizontal, Vertical, VerticalScroll
5
5
  from textual.css.query import NoMatches
6
- from textual.widgets import Static, Input, Label, TextArea
6
+ from textual.widgets import Button, Input, Label, Markdown, Static, TextArea
7
7
  from textual.screen import ModalScreen
8
8
  from rich.text import Text
9
9
 
10
+ from ayder_cli.providers.evren import EvrenTerms
10
11
  from ayder_cli.tui.keybindings import get_keybindings_by_category
11
12
  from ayder_cli.tui.types import ConfirmResult
12
13
  from ayder_cli.log import get_logger
@@ -767,3 +768,37 @@ class CLIHelpScreen(ModalScreen[None]):
767
768
 
768
769
  def action_dismiss_help(self) -> None:
769
770
  self.dismiss(None)
771
+
772
+
773
+ class EvrenTermsScreen(ModalScreen[bool]):
774
+ """Shows the evren terms of use; dismisses True on Approve, False on Deny.
775
+
776
+ Deny holds focus when the modal opens so a stray Enter never accepts.
777
+ """
778
+
779
+ BINDINGS = [("escape", "deny", "Deny")]
780
+
781
+ def __init__(self, terms: EvrenTerms) -> None:
782
+ super().__init__()
783
+ self.terms = terms
784
+
785
+ def compose(self) -> ComposeResult:
786
+ with Vertical():
787
+ yield Label(
788
+ f"evren — Terms of Use v{self.terms.version}", classes="prompt", markup=False
789
+ )
790
+ with VerticalScroll(id="terms-scroll"):
791
+ yield Markdown(self.terms.content, id="terms-content")
792
+ with Horizontal(id="terms-buttons"):
793
+ yield Button("Approve", id="terms-approve", variant="success")
794
+ yield Button("Deny", id="terms-deny", variant="error")
795
+
796
+ def on_mount(self) -> None:
797
+ self.query_one("#terms-deny", Button).focus()
798
+
799
+ def on_button_pressed(self, event: Button.Pressed) -> None:
800
+ event.stop()
801
+ self.dismiss(event.button.id == "terms-approve")
802
+
803
+ def action_deny(self) -> None:
804
+ self.dismiss(False)
@@ -0,0 +1,44 @@
1
+ """The first-run config.toml ships an evren profile with an empty API key."""
2
+
3
+ import tomllib
4
+
5
+ import pytest
6
+
7
+ from ayder_cli.core import config as config_module
8
+ from ayder_cli.core.config import DEFAULTS, load_config_for_provider
9
+ from ayder_cli.core.config_migration import ensure_latest_config
10
+
11
+ EVREN_PROFILE = {
12
+ "driver": "openai",
13
+ "base_url": "https://evren-llmapi.ssyz.org.tr/v1",
14
+ "api_key": "",
15
+ "model": "deepseek-v4-flash",
16
+ "num_ctx": 131072,
17
+ }
18
+
19
+
20
+ @pytest.fixture
21
+ def fresh_config(tmp_path, monkeypatch):
22
+ path = tmp_path / ".ayder" / "config.toml"
23
+ monkeypatch.setattr(config_module, "CONFIG_DIR", path.parent)
24
+ monkeypatch.setattr(config_module, "CONFIG_PATH", path)
25
+ ensure_latest_config(path, defaults=DEFAULTS)
26
+ return path
27
+
28
+
29
+ def test_first_run_config_has_evren_profile(fresh_config):
30
+ data = tomllib.loads(fresh_config.read_text())
31
+
32
+ assert data["llm"]["evren"] == EVREN_PROFILE
33
+ assert data["app"]["provider"] == "openai"
34
+
35
+
36
+ def test_evren_profile_loads_with_empty_key(fresh_config):
37
+ cfg = load_config_for_provider("evren")
38
+
39
+ assert (cfg.driver, cfg.base_url, cfg.api_key, cfg.model) == (
40
+ "openai",
41
+ "https://evren-llmapi.ssyz.org.tr/v1",
42
+ "",
43
+ "deepseek-v4-flash",
44
+ )
@@ -0,0 +1,79 @@
1
+ """evren terms helpers: error detection, terms fetch, terms acceptance."""
2
+
3
+ import json
4
+
5
+ import httpx
6
+ import openai
7
+ import pytest
8
+
9
+ from ayder_cli.providers.evren import EvrenTerms, accept_terms, fetch_terms, is_evren_terms_error
10
+
11
+ EVREN = "https://evren-llmapi.ssyz.org.tr/v1"
12
+ TERMS_CONTENT = "# EVREN Kullanım Şartları\n\nMadde 1."
13
+
14
+
15
+ def _status_error(cls: type[openai.APIStatusError], status: int, code: str) -> openai.APIStatusError:
16
+ request = httpx.Request("GET", f"{EVREN}/models")
17
+ body = {"message": "refused", "type": "permission_error", "param": None, "code": code}
18
+ return cls("refused", response=httpx.Response(status, request=request), body=body)
19
+
20
+
21
+ TERMS_403 = _status_error(openai.PermissionDeniedError, 403, "terms_not_accepted")
22
+
23
+
24
+ @pytest.mark.parametrize(
25
+ ("base_url", "exc", "expected"),
26
+ [
27
+ (EVREN, TERMS_403, True),
28
+ ("https://evren-llmapi.ssyz.org.tr/", TERMS_403, True),
29
+ ("https://api.example.com/v1", TERMS_403, False),
30
+ (EVREN, _status_error(openai.PermissionDeniedError, 403, "forbidden"), False),
31
+ (EVREN, _status_error(openai.NotFoundError, 404, "terms_not_accepted"), False),
32
+ (EVREN, RuntimeError("terms_not_accepted"), False),
33
+ (None, TERMS_403, False),
34
+ ],
35
+ )
36
+ def test_is_evren_terms_error(base_url, exc, expected):
37
+ assert is_evren_terms_error(base_url, exc) is expected
38
+
39
+
40
+ def _client(responses: dict[str, httpx.Response], requests: list[httpx.Request]) -> openai.AsyncOpenAI:
41
+ def handler(request: httpx.Request) -> httpx.Response:
42
+ requests.append(request)
43
+ return responses[request.url.path]
44
+
45
+ return openai.AsyncOpenAI(
46
+ base_url=EVREN,
47
+ api_key="k",
48
+ max_retries=0,
49
+ http_client=httpx.AsyncClient(transport=httpx.MockTransport(handler)),
50
+ )
51
+
52
+
53
+ @pytest.mark.asyncio
54
+ async def test_fetch_terms():
55
+ requests: list[httpx.Request] = []
56
+ body = {"version": 1, "doc_path": "docs/legal/x.md", "content": TERMS_CONTENT}
57
+ client = _client({"/v1/terms/text": httpx.Response(200, json=body)}, requests)
58
+
59
+ assert await fetch_terms(client) == EvrenTerms(version=1, content=TERMS_CONTENT)
60
+ assert [(r.method, r.url.path) for r in requests] == [("GET", "/v1/terms/text")]
61
+ assert requests[0].headers["Authorization"] == "Bearer k"
62
+
63
+
64
+ @pytest.mark.asyncio
65
+ @pytest.mark.parametrize(
66
+ "response_body",
67
+ [
68
+ {"accepted_version": 1, "accepted_at": "2026-09-29T11:27:40.291459+00:00"},
69
+ {},
70
+ ],
71
+ )
72
+ async def test_accept_terms(response_body):
73
+ requests: list[httpx.Request] = []
74
+ client = _client({"/v1/terms/accept": httpx.Response(200, json=response_body)}, requests)
75
+
76
+ assert await accept_terms(client, 1) == 1
77
+ assert [(r.method, r.url.path) for r in requests] == [("POST", "/v1/terms/accept")]
78
+ assert json.loads(requests[0].content) == {"version": 1}
79
+