ayder-cli 2.5.2__tar.gz → 2.5.3__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 (358) hide show
  1. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/PKG-INFO +2 -1
  2. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/README.md +1 -0
  3. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/docs/GATE.md +1 -1
  4. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/docs/PROJECT_STRUCTURE.md +8 -6
  5. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/pyproject.toml +1 -1
  6. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/prompts.py +1 -0
  7. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/themes/ayder.py +17 -25
  8. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/themes/claude.py +17 -25
  9. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/tui/__init__.py +4 -3
  10. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/tui/app.py +21 -23
  11. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/tui/screens.py +6 -0
  12. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/tui/widgets.py +123 -17
  13. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/test_prompts_environment.py +81 -1
  14. ayder_cli-2.5.3/tests/tui/test_follow_mode.py +132 -0
  15. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/tui/test_help_screen.py +26 -0
  16. ayder_cli-2.5.3/tests/tui/test_inline_sync_output.py +77 -0
  17. ayder_cli-2.5.3/tests/tui/test_mouse_support.py +20 -0
  18. ayder_cli-2.5.3/tests/tui/test_panel_layout.py +490 -0
  19. ayder_cli-2.5.3/tests/tui/test_real_turn_follow.py +260 -0
  20. ayder_cli-2.5.3/tests/tui/test_status_bar_cwd.py +185 -0
  21. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/uv.lock +1 -1
  22. ayder_cli-2.5.2/tests/tui/test_follow_mode.py +0 -38
  23. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/.github/workflows/ci.yml +0 -0
  24. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/.github/workflows/python-package.yml +0 -0
  25. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/.gitignore +0 -0
  26. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/.python-version +0 -0
  27. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/AGENTS.md +0 -0
  28. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/LICENSE +0 -0
  29. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/docs/README_AGENT_WORKFLOW.md +0 -0
  30. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/docs/README_OLLAMA.md +0 -0
  31. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/docs/cc.png +0 -0
  32. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/docs/config.toml.example +0 -0
  33. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/scripts/check_exception_handlers.py +0 -0
  34. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/scripts/check_log_arguments.py +0 -0
  35. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/scripts/check_logging_imports.py +0 -0
  36. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/scripts/exception_baseline.txt +0 -0
  37. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/scripts/find_percent_logging.py +0 -0
  38. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/scripts/log_argument_baseline.txt +0 -0
  39. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/scripts/logging_gates.py +0 -0
  40. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/__init__.py +0 -0
  41. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/__main__.py +0 -0
  42. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/agents/__init__.py +0 -0
  43. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/agents/callbacks.py +0 -0
  44. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/agents/config.py +0 -0
  45. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/agents/registry.py +0 -0
  46. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/agents/run.py +0 -0
  47. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/agents/runner.py +0 -0
  48. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/agents/tool.py +0 -0
  49. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/agents/worktree.py +0 -0
  50. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/application/README.md +0 -0
  51. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/application/__init__.py +0 -0
  52. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/application/execution_policy.py +0 -0
  53. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/application/message_contract.py +0 -0
  54. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/application/runtime_factory.py +0 -0
  55. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/application/validation.py +0 -0
  56. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/cli.py +0 -0
  57. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/cli_callbacks.py +0 -0
  58. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/cli_runner.py +0 -0
  59. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/console.py +0 -0
  60. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/core/cache_monitor.py +0 -0
  61. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/core/config.py +0 -0
  62. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/core/config_migration.py +0 -0
  63. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/core/context.py +0 -0
  64. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/core/context_manager.py +0 -0
  65. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/core/context_manager_factory.py +0 -0
  66. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/core/default_context_manager.py +0 -0
  67. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/core/ollama_context_manager.py +0 -0
  68. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/core/reasoning.py +0 -0
  69. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/core/result.py +0 -0
  70. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/core/session.py +0 -0
  71. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/core/skills.py +0 -0
  72. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/diagnostics.py +0 -0
  73. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/log.py +0 -0
  74. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/logging_config.py +0 -0
  75. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/loops/__init__.py +0 -0
  76. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/loops/chat_loop.py +0 -0
  77. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/masking.py +0 -0
  78. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/parser.py +0 -0
  79. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/process_manager.py +0 -0
  80. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/providers/__init__.py +0 -0
  81. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/providers/base.py +0 -0
  82. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/providers/evren.py +0 -0
  83. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/providers/impl/claude.py +0 -0
  84. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/providers/impl/deepseek.py +0 -0
  85. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/providers/impl/gemini.py +0 -0
  86. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/providers/impl/glm.py +0 -0
  87. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/providers/impl/ollama.py +0 -0
  88. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/providers/impl/ollama_drivers/__init__.py +0 -0
  89. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/providers/impl/ollama_drivers/_errors.py +0 -0
  90. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/providers/impl/ollama_drivers/base.py +0 -0
  91. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/providers/impl/ollama_drivers/deepseek.py +0 -0
  92. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/providers/impl/ollama_drivers/generic_native.py +0 -0
  93. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/providers/impl/ollama_drivers/generic_xml.py +0 -0
  94. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/providers/impl/ollama_drivers/matrix.py +0 -0
  95. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/providers/impl/ollama_drivers/minimax.py +0 -0
  96. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/providers/impl/ollama_drivers/qwen3.py +0 -0
  97. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/providers/impl/ollama_drivers/registry.py +0 -0
  98. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/providers/impl/ollama_inspector.py +0 -0
  99. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/providers/impl/openai.py +0 -0
  100. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/providers/impl/qwen.py +0 -0
  101. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/providers/orchestrator.py +0 -0
  102. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/providers/retry.py +0 -0
  103. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/services/__init__.py +0 -0
  104. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/services/interactions.py +0 -0
  105. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/services/messaging_inbox.py +0 -0
  106. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/skills/development-workflow/SKILL.md +0 -0
  107. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/themes/__init__.py +0 -0
  108. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/tools/__init__.py +0 -0
  109. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/tools/builtins/__init__.py +0 -0
  110. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/tools/builtins/background_process_tool.py +0 -0
  111. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/tools/builtins/context.py +0 -0
  112. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/tools/builtins/context_definitions.py +0 -0
  113. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/tools/builtins/filesystem.py +0 -0
  114. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/tools/builtins/filesystem_definitions.py +0 -0
  115. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/tools/builtins/note_tool.py +0 -0
  116. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/tools/builtins/notes.py +0 -0
  117. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/tools/builtins/notes_definitions.py +0 -0
  118. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/tools/builtins/process_manager_definitions.py +0 -0
  119. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/tools/builtins/search.py +0 -0
  120. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/tools/builtins/search_definitions.py +0 -0
  121. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/tools/builtins/shell.py +0 -0
  122. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/tools/builtins/shell_definitions.py +0 -0
  123. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/tools/builtins/skill.py +0 -0
  124. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/tools/builtins/skill_definitions.py +0 -0
  125. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/tools/builtins/task_definitions.py +0 -0
  126. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/tools/builtins/task_tool.py +0 -0
  127. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/tools/builtins/tasks.py +0 -0
  128. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/tools/builtins/utils_tools.py +0 -0
  129. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/tools/builtins/utils_tools_definitions.py +0 -0
  130. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/tools/builtins/web.py +0 -0
  131. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/tools/builtins/web_definitions.py +0 -0
  132. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/tools/definition.py +0 -0
  133. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/tools/execution.py +0 -0
  134. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/tools/hooks.py +0 -0
  135. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/tools/mcp.py +0 -0
  136. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/tools/mcp_client.py +0 -0
  137. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/tools/normalization.py +0 -0
  138. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/tools/plugin_api.py +0 -0
  139. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/tools/plugin_github.py +0 -0
  140. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/tools/plugin_manager.py +0 -0
  141. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/tools/plugin_status.py +0 -0
  142. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/tools/registry.py +0 -0
  143. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/tools/schemas.py +0 -0
  144. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/tools/utils.py +0 -0
  145. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/tui/adapter.py +0 -0
  146. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/tui/commands.py +0 -0
  147. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/tui/helpers.py +0 -0
  148. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/tui/keybindings.py +0 -0
  149. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/tui/parser.py +0 -0
  150. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/tui/rendering.py +0 -0
  151. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/tui/theme_manager.py +0 -0
  152. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/tui/types.py +0 -0
  153. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/ui.py +0 -0
  154. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/src/ayder_cli/version.py +0 -0
  155. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/COVERAGE.md +0 -0
  156. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/__init__.py +0 -0
  157. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/agents/__init__.py +0 -0
  158. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/agents/test_agent_consolidated.py +0 -0
  159. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/agents/test_agent_severity.py +0 -0
  160. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/agents/test_agent_tools.py +0 -0
  161. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/agents/test_callbacks.py +0 -0
  162. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/agents/test_config.py +0 -0
  163. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/agents/test_integration.py +0 -0
  164. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/agents/test_queue.py +0 -0
  165. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/agents/test_registry.py +0 -0
  166. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/agents/test_run.py +0 -0
  167. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/agents/test_runner.py +0 -0
  168. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/agents/test_runner_provider_unavailable.py +0 -0
  169. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/agents/test_tool.py +0 -0
  170. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/agents/test_worktree_helpers.py +0 -0
  171. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/agents/test_worktree_isolation.py +0 -0
  172. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/application/test_argument_canonicalization.py +0 -0
  173. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/application/test_execution_result_truthfulness.py +0 -0
  174. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/application/test_message_contract.py +0 -0
  175. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/application/test_runtime_factory.py +0 -0
  176. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/client/test_main.py +0 -0
  177. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/conftest.py +0 -0
  178. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/convergence/__init__.py +0 -0
  179. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/convergence/test_execution_policy_parity.py +0 -0
  180. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/convergence/test_runtime_wiring.py +0 -0
  181. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/convergence/test_validation_path.py +0 -0
  182. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/core/test_asyncio_bridge.py +0 -0
  183. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/core/test_cache_monitor.py +0 -0
  184. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/core/test_config.py +0 -0
  185. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/core/test_config_coverage.py +0 -0
  186. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/core/test_config_migration.py +0 -0
  187. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/core/test_config_provider_logging.py +0 -0
  188. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/core/test_config_retry.py +0 -0
  189. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/core/test_config_skills.py +0 -0
  190. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/core/test_config_v2.py +0 -0
  191. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/core/test_context_manager_factory.py +0 -0
  192. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/core/test_default_config_evren.py +0 -0
  193. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/core/test_default_context_manager.py +0 -0
  194. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/core/test_default_context_manager_should_compact.py +0 -0
  195. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/core/test_default_context_newest_unit.py +0 -0
  196. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/core/test_default_context_skill_resupply.py +0 -0
  197. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/core/test_event_emission.py +0 -0
  198. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/core/test_event_stream.py +0 -0
  199. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/core/test_exception_guarantee.py +0 -0
  200. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/core/test_exception_hooks.py +0 -0
  201. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/core/test_log_facade.py +0 -0
  202. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/core/test_logging_config_keys.py +0 -0
  203. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/core/test_logging_sinks.py +0 -0
  204. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/core/test_loguru_caplog.py +0 -0
  205. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/core/test_max_output_tokens_default.py +0 -0
  206. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/core/test_ollama_compaction_persistence.py +0 -0
  207. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/core/test_ollama_context_manager.py +0 -0
  208. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/core/test_ollama_context_manager_cache_monitor.py +0 -0
  209. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/core/test_ollama_context_manager_max_history.py +0 -0
  210. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/core/test_ollama_skill_resupply.py +0 -0
  211. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/core/test_parameter_aliasing.py +0 -0
  212. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/core/test_parser.py +0 -0
  213. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/core/test_reasoning_config.py +0 -0
  214. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/core/test_scratch_dir.py +0 -0
  215. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/core/test_session.py +0 -0
  216. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/core/test_session_id_threading.py +0 -0
  217. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/core/test_sink_masking.py +0 -0
  218. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/core/test_skills.py +0 -0
  219. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/core/test_skills_builtin_sync.py +0 -0
  220. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/loops/__init__.py +0 -0
  221. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/loops/test_base.py +0 -0
  222. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/loops/test_chat_loop_guards.py +0 -0
  223. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/loops/test_chat_loop_hook.py +0 -0
  224. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/loops/test_chat_loop_tool_truncation.py +0 -0
  225. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/loops/test_loop_severity.py +0 -0
  226. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/loops/test_required_args_aliases.py +0 -0
  227. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/loops/test_tool_argument_integrity.py +0 -0
  228. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/loops/test_tool_batch_cancellation.py +0 -0
  229. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/loops/test_tool_batch_ordering.py +0 -0
  230. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/providers/__init__.py +0 -0
  231. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/providers/ollama_drivers/__init__.py +0 -0
  232. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/providers/ollama_drivers/test_base.py +0 -0
  233. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/providers/ollama_drivers/test_deepseek.py +0 -0
  234. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/providers/ollama_drivers/test_errors.py +0 -0
  235. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/providers/ollama_drivers/test_fallback.py +0 -0
  236. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/providers/ollama_drivers/test_generic_native.py +0 -0
  237. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/providers/ollama_drivers/test_generic_xml.py +0 -0
  238. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/providers/ollama_drivers/test_matrix.py +0 -0
  239. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/providers/ollama_drivers/test_minimax.py +0 -0
  240. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/providers/ollama_drivers/test_provider_integration.py +0 -0
  241. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/providers/ollama_drivers/test_qwen3.py +0 -0
  242. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/providers/ollama_drivers/test_registry.py +0 -0
  243. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/providers/test_claude_stream_tool_calls.py +0 -0
  244. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/providers/test_evren.py +0 -0
  245. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/providers/test_gemini_function_calls.py +0 -0
  246. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/providers/test_glm_async.py +0 -0
  247. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/providers/test_ollama_inspector.py +0 -0
  248. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/providers/test_ollama_native.py +0 -0
  249. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/providers/test_ollama_parallel_tool_call_ids.py +0 -0
  250. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/providers/test_ollama_xml.py +0 -0
  251. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/providers/test_ollama_xml_autoroute.py +0 -0
  252. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/providers/test_ollama_xml_usage.py +0 -0
  253. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/providers/test_openai_list_models.py +0 -0
  254. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/providers/test_orchestrator_capabilities.py +0 -0
  255. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/providers/test_provider_channel_binding.py +0 -0
  256. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/providers/test_provider_severity.py +0 -0
  257. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/providers/test_qwen_async.py +0 -0
  258. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/providers/test_reasoning_effort.py +0 -0
  259. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/providers/test_retry_classifier.py +0 -0
  260. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/providers/test_retry_integration.py +0 -0
  261. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/providers/test_retrying_provider.py +0 -0
  262. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/providers/test_skill_load_prefix.py +0 -0
  263. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/scripts/test_exception_gate.py +0 -0
  264. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/scripts/test_log_argument_gate.py +0 -0
  265. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/scripts/test_logging_policy_gates.py +0 -0
  266. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/scripts/test_loguru_private_shape.py +0 -0
  267. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/services/test_boundary.py +0 -0
  268. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/services/test_interaction_sink.py +0 -0
  269. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/services/test_messaging_inbox.py +0 -0
  270. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/test_agents_md.py +0 -0
  271. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/test_cli.py +0 -0
  272. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/test_cli_callbacks.py +0 -0
  273. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/test_context.py +0 -0
  274. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/test_env_manager.py +0 -0
  275. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/test_logging_config.py +0 -0
  276. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/test_notes.py +0 -0
  277. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/test_process_manager.py +0 -0
  278. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/test_prompts_general.py +0 -0
  279. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/test_tasks.py +0 -0
  280. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/tools/__init__.py +0 -0
  281. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/tools/test_agent_notes.py +0 -0
  282. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/tools/test_background_process_tool.py +0 -0
  283. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/tools/test_bash_process_safety.py +0 -0
  284. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/tools/test_bash_tool.py +0 -0
  285. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/tools/test_context_storage_confinement.py +0 -0
  286. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/tools/test_definition_discovery.py +0 -0
  287. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/tools/test_di_injection.py +0 -0
  288. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/tools/test_error_labels.py +0 -0
  289. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/tools/test_file_editor_hardening.py +0 -0
  290. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/tools/test_file_editor_size_limit.py +0 -0
  291. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/tools/test_file_tool_output.py +0 -0
  292. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/tools/test_impl.py +0 -0
  293. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/tools/test_impl_coverage.py +0 -0
  294. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/tools/test_mcp.py +0 -0
  295. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/tools/test_mcp_client.py +0 -0
  296. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/tools/test_note_tool.py +0 -0
  297. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/tools/test_notes_api.py +0 -0
  298. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/tools/test_notes_confinement.py +0 -0
  299. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/tools/test_parallel_safe.py +0 -0
  300. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/tools/test_path_security.py +0 -0
  301. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/tools/test_plugin_api.py +0 -0
  302. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/tools/test_plugin_github.py +0 -0
  303. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/tools/test_plugin_loading.py +0 -0
  304. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/tools/test_plugin_manager.py +0 -0
  305. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/tools/test_plugin_status.py +0 -0
  306. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/tools/test_registry.py +0 -0
  307. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/tools/test_registry_coverage.py +0 -0
  308. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/tools/test_result.py +0 -0
  309. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/tools/test_schemas.py +0 -0
  310. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/tools/test_search_codebase.py +0 -0
  311. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/tools/test_search_hardening.py +0 -0
  312. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/tools/test_skill_catalog_and_filter.py +0 -0
  313. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/tools/test_skill_scope_wiring.py +0 -0
  314. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/tools/test_skill_tool.py +0 -0
  315. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/tools/test_task_integrity.py +0 -0
  316. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/tools/test_task_path_confinement.py +0 -0
  317. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/tools/test_task_tool.py +0 -0
  318. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/tools/test_tool_severity.py +0 -0
  319. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/tools/test_unexpected_args.py +0 -0
  320. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/tools/test_utils.py +0 -0
  321. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/tools/test_web.py +0 -0
  322. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/tui/__init__.py +0 -0
  323. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/tui/test_agent_list_screen.py +0 -0
  324. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/tui/test_do_clear_coordination.py +0 -0
  325. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/tui/test_evren_terms.py +0 -0
  326. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/tui/test_input_box.py +0 -0
  327. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/tui/test_keybindings.py +0 -0
  328. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/tui/test_multi_select_screen.py +0 -0
  329. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/tui/test_multiline_input.py +0 -0
  330. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/tui/test_paste.py +0 -0
  331. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/tui/test_peer_message_turn.py +0 -0
  332. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/tui/test_pending_compact_consumer.py +0 -0
  333. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/tui/test_pull_delivery.py +0 -0
  334. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/tui/test_resume.py +0 -0
  335. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/tui/test_status_bar_badges.py +0 -0
  336. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/tui/test_themes.py +0 -0
  337. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/tui/test_turn_consumer.py +0 -0
  338. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/tui/test_widgets.py +0 -0
  339. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/ui/test_confirm_screen.py +0 -0
  340. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/ui/test_diff_preview.py +0 -0
  341. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/ui/test_plugin_autoenable.py +0 -0
  342. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/ui/test_provider_switch.py +0 -0
  343. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/ui/test_run_tui_startup.py +0 -0
  344. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/ui/test_tui_assistant_sanitization.py +0 -0
  345. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/ui/test_tui_chat_loop.py +0 -0
  346. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/ui/test_tui_commands_agent.py +0 -0
  347. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/ui/test_tui_commands_effort.py +0 -0
  348. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/ui/test_tui_commands_logging.py +0 -0
  349. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/ui/test_tui_commands_plugin.py +0 -0
  350. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/ui/test_tui_commands_provider.py +0 -0
  351. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/ui/test_tui_commands_skill.py +0 -0
  352. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/ui/test_tui_helpers.py +0 -0
  353. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/ui/test_tui_system_prompt_no_structure.py +0 -0
  354. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/ui/test_tui_temporal_command.py +0 -0
  355. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/ui/test_tui_widgets.py +0 -0
  356. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/ui/test_ui.py +0 -0
  357. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/ui/test_ui_coverage.py +0 -0
  358. {ayder_cli-2.5.2 → ayder_cli-2.5.3}/tests/ui/test_ui_verbose.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: ayder-cli
3
- Version: 2.5.2
3
+ Version: 2.5.3
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
@@ -535,6 +535,7 @@ inside the Ollama summary.
535
535
  | `Ctrl+L` | Clear chat |
536
536
  | `Ctrl+O` | Toggle tool panel |
537
537
  | `Ctrl+T` | Toggle thinking/reasoning blocks |
538
+ | `Shift` + drag | Select text with the mouse (Option in iTerm2 and Terminal.app) |
538
539
  | `PageUp` / `PageDown` | Scroll chat view |
539
540
  | `Tab` | Auto-complete slash commands |
540
541
 
@@ -492,6 +492,7 @@ inside the Ollama summary.
492
492
  | `Ctrl+L` | Clear chat |
493
493
  | `Ctrl+O` | Toggle tool panel |
494
494
  | `Ctrl+T` | Toggle thinking/reasoning blocks |
495
+ | `Shift` + drag | Select text with the mouse (Option in iTerm2 and Terminal.app) |
495
496
  | `PageUp` / `PageDown` | Scroll chat view |
496
497
  | `Tab` | Auto-complete slash commands |
497
498
 
@@ -12,7 +12,7 @@ Input: the candidate SHA.
12
12
  gates and the test suite, and stops at the first failing task.
13
13
  4. Copy the final pytest summary line (`=== N passed, M skipped in …s ===`) verbatim.
14
14
 
15
- Last measured: `2909 passed, 2 skipped` on `fix/docs-skill-list-2.5.2` (2026-10-22, release 2.5.2).
15
+ Last measured: `3026 passed, 2 skipped` on `feat/tui-tweaks` (2026-11-13, release 2.5.3).
16
16
 
17
17
  ## Report format
18
18
 
@@ -329,11 +329,13 @@ AyderApp (Textual App)
329
329
  ├── CSS (from theme_manager.py)
330
330
  ├── Compose:
331
331
  │ ├── Banner (Static)
332
+ │ ├── ToolPanel (custom widget) ← Tool calls; hidden until Ctrl+O, opens at the top (fixed 10% height)
333
+ │ ├── ThinkingPanel (custom widget) ← Reasoning stream; Ctrl+T, opens at the top (fixed 30% height)
334
+ │ ├── AgentPanel (custom widget) ← Agent runs; Ctrl+G, opens at the top (fixed 10% height)
332
335
  │ ├── ChatView (custom widget) ← Messages displayed here
333
- │ ├── ToolPanel (custom widget) ← Tool calls shown here
334
336
  │ ├── ActivityBar (custom widget) ← Status indicators
335
337
  │ ├── CLIInputBar (custom widget) ← User input
336
- │ └── StatusBar (custom widget) ← Model, tokens, iterations
338
+ │ └── StatusBar (custom widget) ← Working directory (~ for home, shortened from the left), model, effort, mode, context usage, files
337
339
  │
338
340
  ├── Screens (Modal):
339
341
  │ ├── CLIConfirmScreen ← Tool confirmation with diff
@@ -574,15 +576,15 @@ The TUI module provides a clean, terminal-like interface with:
574
576
  - Simple text output with prefixes
575
577
  - Minimal borders and chrome design
576
578
  - Slash command auto-completion
577
- - Status bar with contextual information
578
- - Mouse-free operation optimized for keyboard
579
+ - Status bar with contextual information, starting with the working directory (home shown as `~`; a long path is shortened from the left to at most a third of the bar, never below `…/<last component>`, regardless of the other items, which may overflow the bar's right edge)
580
+ - Keyboard-first operation, with mouse support enabled
579
581
 
580
582
  It exposes key components like AyderApp, widgets (ChatView, ToolPanel), and screens (CLIConfirmScreen) for building the terminal interface.
581
583
 
582
584
  ### TUI Application File Summary (src/ayder_cli/tui/app.py)
583
585
 
584
586
  AyderApp is the main Textual application implementing a chat-style interface:
585
- - **Layout**: Banner, ChatView, ToolPanel, ActivityBar, CLIInputBar, StatusBar
587
+ - **Layout**: ToolPanel, ThinkingPanel and AgentPanel (hidden by default; they open at the top, above the chat, at a fixed 10% / 30% / 10% of the screen height with their content scrolling inside, and toggling prints no chat message), ChatView, ActivityBar, CLIInputBar, StatusBar
586
588
  - **Keybindings**: Ctrl+Q (quit), Ctrl+C/X (cancel), Ctrl+L (clear), Ctrl+O (toggle tools)
587
589
  - **Features**:
588
590
  - Async processing with worker threads
@@ -594,7 +596,7 @@ AyderApp is the main Textual application implementing a chat-style interface:
594
596
  The application manages:
595
597
  - LLM provider setup and configuration
596
598
  - Tool registry initialization with middleware
597
- - System prompt construction (base prompt + environment section + tool prompts + skill catalog; no project tree). The environment section (`prompts.build_environment_prompt`) gives the absolute working directory and the session start date and time (ISO 8601, local time with UTC offset). It is built once per session and reused on `/model` rebuilds. A resumed session (`--resume`) restores the provider it last used (the session file stores it; a provider no longer configured falls back to the default with a warning) and its `--system-prompt` text, keeps its history, and rebuilds the system prompt for the current session, so the scratch directory, start time, tool prompts and agent capability prompts are current; `/model` rebuilds keep the capability prompts.
599
+ - System prompt construction (base prompt + environment section + tool prompts + skill catalog; no project tree). The environment section (`prompts.build_environment_prompt`) gives the absolute working directory and the session start date and time (ISO 8601, local time with UTC offset). It also tells the model that replies render as Markdown (paragraphs separated by a blank line, no fixed-width wrapping). It is built once per session and reused on `/model` rebuilds. A resumed session (`--resume`) restores the provider it last used (the session file stores it; a provider no longer configured falls back to the default with a warning) and its `--system-prompt` text, keeps its history, and rebuilds the system prompt for the current session, so the scratch directory, start time, tool prompts and agent capability prompts are current; `/model` rebuilds keep the capability prompts.
598
600
  - Process manager for background operations
599
601
 
600
602
  ### Chat Loop Summary (src/ayder_cli/loops/chat_loop.py)
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "ayder-cli"
7
- version = "2.5.2"
7
+ version = "2.5.3"
8
8
  description = "AI agent for any LLMs"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
@@ -57,6 +57,7 @@ Session start: {started_at}{scratch_line}
57
57
  Your workspace is {workspace}.
58
58
  Outside it, read only paths the user names or the task requires, and create,
59
59
  modify or initialize nothing (including `git init`) without the user's consent.{scratch_rule}
60
+ Replies are rendered as Markdown. Separate paragraphs with a blank line; do not wrap lines at a fixed width.
60
61
  """
61
62
 
62
63
  # REASON: Models put temporary files in /tmp by habit. A per-session directory
@@ -659,16 +659,15 @@ BannerWidget .banner-version {
659
659
  color: $success;
660
660
  }
661
661
 
662
- /* ToolPanel - The panel that slides up/appears above the input bar showing running tools.
663
- Targets 'ToolPanel' in widgets.py. */
662
+ /* ToolPanel - The panel that opens at the top of the screen, above the chat view, showing running tools.
663
+ Fixed at 10% of the screen height from the moment it opens, whatever it holds;
664
+ its content scrolls inside. Targets 'ToolPanel' in widgets.py. */
664
665
  ToolPanel {
665
666
  layout: vertical;
666
- height: auto;
667
- min-height: 6;
668
- max-height: 24%;
667
+ height: 10%;
669
668
  background: $background;
670
- border-top: solid ansi_bright_black;
671
- padding: 1 1;
669
+ border-bottom: solid ansi_bright_black;
670
+ padding: 0 1;
672
671
  overflow-y: scroll;
673
672
  display: none;
674
673
  }
@@ -688,14 +687,14 @@ ToolPanel .tool-item.completed {
688
687
  }
689
688
 
690
689
  /* AgentPanel - Scrollable panel for agent run history.
691
- Toggled with Ctrl+G. Targets 'AgentPanel' in widgets.py. */
690
+ Toggled with Ctrl+G. Fixed at 10% of the screen height; content scrolls inside.
691
+ Targets 'AgentPanel' in widgets.py. */
692
692
  AgentPanel {
693
693
  layout: vertical;
694
- height: auto;
695
- max-height: 40%;
694
+ height: 10%;
696
695
  background: $background;
697
- border-top: solid ansi_bright_black;
698
- padding: 1 1;
696
+ border-bottom: solid ansi_bright_black;
697
+ padding: 0 1;
699
698
  overflow-y: scroll;
700
699
  display: none;
701
700
  }
@@ -734,15 +733,15 @@ AgentPanel .agent-detail {
734
733
  }
735
734
 
736
735
  /* ThinkingPanel - Scrollable panel showing the model's reasoning stream.
737
- Toggled with Ctrl+T. Taller than the tool/agent panels because reasoning is
738
- long-form. Targets 'ThinkingPanel' in widgets.py. */
736
+ Toggled with Ctrl+T. Fixed at 30% of the screen height, three times the
737
+ tool/agent panels, because reasoning is long-form; content scrolls inside.
738
+ Targets 'ThinkingPanel' in widgets.py. */
739
739
  ThinkingPanel {
740
740
  layout: vertical;
741
- height: auto;
742
- max-height: 60%;
741
+ height: 30%;
743
742
  background: $background;
744
- border-top: solid ansi_bright_black;
745
- padding: 1 1;
743
+ border-bottom: solid ansi_bright_black;
744
+ padding: 0 1;
746
745
  overflow-y: scroll;
747
746
  display: none;
748
747
  }
@@ -778,21 +777,14 @@ Screen:inline {
778
777
  }
779
778
 
780
779
  #tool-panel {
781
- height: auto;
782
- min-height: 0;
783
- max-height: 20%;
784
780
  width: 100%;
785
781
  }
786
782
 
787
783
  #agent-panel {
788
- min-height: 0;
789
- max-height: 40%;
790
784
  width: 100%;
791
785
  }
792
786
 
793
787
  #thinking-panel {
794
- min-height: 0;
795
- max-height: 60%;
796
788
  width: 100%;
797
789
  }
798
790
 
@@ -596,16 +596,15 @@ BannerWidget .banner-version {
596
596
  color: #5cb870;
597
597
  }
598
598
 
599
- /* ToolPanel - The panel that slides up/appears above the input bar showing running tools.
600
- Targets 'ToolPanel' in widgets.py. */
599
+ /* ToolPanel - The panel that opens at the top of the screen, above the chat view, showing running tools.
600
+ Fixed at 10% of the screen height from the moment it opens, whatever it holds;
601
+ its content scrolls inside. Targets 'ToolPanel' in widgets.py. */
601
602
  ToolPanel {
602
603
  layout: vertical;
603
- height: auto;
604
- min-height: 6;
605
- max-height: 24%;
604
+ height: 10%;
606
605
  background: #0a0a18;
607
- border-top: solid #333350;
608
- padding: 1 1;
606
+ border-bottom: solid #333350;
607
+ padding: 0 1;
609
608
  overflow-y: scroll;
610
609
  display: none;
611
610
  }
@@ -625,14 +624,14 @@ ToolPanel .tool-item.completed {
625
624
  }
626
625
 
627
626
  /* AgentPanel - Scrollable panel for agent run history.
628
- Toggled with Ctrl+G. Targets 'AgentPanel' in widgets.py. */
627
+ Toggled with Ctrl+G. Fixed at 10% of the screen height; content scrolls inside.
628
+ Targets 'AgentPanel' in widgets.py. */
629
629
  AgentPanel {
630
630
  layout: vertical;
631
- height: auto;
632
- max-height: 40%;
631
+ height: 10%;
633
632
  background: #0a0a18;
634
- border-top: solid #333350;
635
- padding: 1 1;
633
+ border-bottom: solid #333350;
634
+ padding: 0 1;
636
635
  overflow-y: scroll;
637
636
  display: none;
638
637
  }
@@ -671,15 +670,15 @@ AgentPanel .agent-detail {
671
670
  }
672
671
 
673
672
  /* ThinkingPanel - Scrollable panel showing the model's reasoning stream.
674
- Toggled with Ctrl+T. Taller than the tool/agent panels because reasoning is
675
- long-form. Targets 'ThinkingPanel' in widgets.py. */
673
+ Toggled with Ctrl+T. Fixed at 30% of the screen height, three times the
674
+ tool/agent panels, because reasoning is long-form; content scrolls inside.
675
+ Targets 'ThinkingPanel' in widgets.py. */
676
676
  ThinkingPanel {
677
677
  layout: vertical;
678
- height: auto;
679
- max-height: 60%;
678
+ height: 30%;
680
679
  background: #0a0a18;
681
- border-top: solid #333350;
682
- padding: 1 1;
680
+ border-bottom: solid #333350;
681
+ padding: 0 1;
683
682
  overflow-y: scroll;
684
683
  display: none;
685
684
  }
@@ -715,21 +714,14 @@ Screen:inline {
715
714
  }
716
715
 
717
716
  #tool-panel {
718
- height: auto;
719
- min-height: 0;
720
- max-height: 20%;
721
717
  width: 100%;
722
718
  }
723
719
 
724
720
  #agent-panel {
725
- min-height: 0;
726
- max-height: 40%;
727
721
  width: 100%;
728
722
  }
729
723
 
730
724
  #thinking-panel {
731
- min-height: 0;
732
- max-height: 60%;
733
725
  width: 100%;
734
726
  }
735
727
 
@@ -81,10 +81,11 @@ def run_tui(
81
81
  raise SystemExit(1)
82
82
 
83
83
  try:
84
- app.run(inline=True, mouse=False)
84
+ app.run(inline=True, mouse=True)
85
85
  finally:
86
- # Ensure mouse reporting is disabled after exit — Textual's driver may
87
- # leave it on if the shutdown path doesn't fully complete (e.g. Ctrl+Q).
86
+ # Mouse is enabled for the session; make sure reporting is switched off
87
+ # on exit — Textual's driver may leave it on if the shutdown path doesn't
88
+ # fully complete (e.g. Ctrl+Q or a crash).
88
89
  sys.stdout.write(
89
90
  "\033[?1000l" # disable mouse click tracking
90
91
  "\033[?1002l" # disable mouse button-event tracking
@@ -10,7 +10,7 @@ from textual.app import App, ComposeResult, InvalidThemeError
10
10
  from textual.css.query import NoMatches
11
11
  from textual.widgets import Static
12
12
  from textual.timer import Timer
13
- from textual import on
13
+ from textual import messages, on
14
14
  from collections.abc import Awaitable, Callable
15
15
  from dataclasses import dataclass
16
16
  from pathlib import Path
@@ -208,7 +208,7 @@ class AyderApp(App):
208
208
  # switches App.theme (the palette) live — handy for previewing palettes on
209
209
  # the ayder layout before pinning one via [ui] palette.
210
210
  ENABLE_COMMAND_PALETTE = True
211
- ENABLE_MOUSE_SUPPORT = False
211
+ ENABLE_MOUSE_SUPPORT = True
212
212
 
213
213
  BINDINGS = [
214
214
  ("ctrl+q", "quit", "Quit"),
@@ -721,10 +721,11 @@ class AyderApp(App):
721
721
 
722
722
  def compose(self) -> ComposeResult:
723
723
  """Compose the UI layout - terminal style with scrolling content."""
724
- yield ChatView(id="chat-view")
724
+ # Toggle panels open at the top; the chat stays next to the input bar.
725
725
  yield ToolPanel(id="tool-panel")
726
726
  yield ThinkingPanel(id="thinking-panel")
727
727
  yield AgentPanel(id="agent-panel")
728
+ yield ChatView(id="chat-view")
728
729
  yield ActivityBar(id="activity-bar")
729
730
  yield CLIInputBar(commands=self.commands, id="input-bar")
730
731
  yield StatusBar(
@@ -734,6 +735,17 @@ class AyderApp(App):
734
735
  id="status-bar",
735
736
  )
736
737
 
738
+ def _on_terminal_supports_synchronized_output(
739
+ self, message: messages.TerminalSupportsSynchronizedOutput
740
+ ) -> None:
741
+ # EXPERIMENT, judged live by the operator: textual 8.2.7 leaves synchronized
742
+ # output (DEC 2026) off in inline mode, yet App._display already writes each
743
+ # inline frame as one sequence between _begin_update/_end_update, so the
744
+ # terminal can apply a whole frame atomically. Relies on private textual
745
+ # 8.2.7 API (exact pin); drop this if it does not help.
746
+ if self._driver is not None:
747
+ self._sync_available = True
748
+
737
749
  def on_mount(self) -> None:
738
750
  """Called when app is mounted."""
739
751
  install_asyncio_handler(asyncio.get_running_loop())
@@ -776,7 +788,6 @@ class AyderApp(App):
776
788
  )
777
789
  spacer_h = max(0, viewport_h - content_h)
778
790
  self._banner_spacer.styles.height = spacer_h
779
- chat_view.scroll_end(animate=False)
780
791
 
781
792
  @property
782
793
  def _is_processing(self) -> bool:
@@ -1092,9 +1103,11 @@ class AyderApp(App):
1092
1103
  def _after_turn_finished(self) -> None:
1093
1104
  """UI teardown after a turn fully exits; the consumer pulls the next one."""
1094
1105
  self._set_inbox_status("idle")
1095
- self._maybe_stop_activity_timer()
1096
1106
  try:
1107
+ # Clear first: a text-only turn ends with the bar still "generating",
1108
+ # which keeps the stop check below from ever seeing an idle bar.
1097
1109
  self.query_one("#activity-bar", ActivityBar).clear()
1110
+ self._maybe_stop_activity_timer()
1098
1111
  self.query_one("#input-bar", CLIInputBar).focus_input()
1099
1112
  except Exception: # noqa: BLE001 - teardown guard: post-turn UI cleanup is cosmetic and must not kill the app
1100
1113
  logger.opt(exception=True).debug("Post-turn UI teardown failed")
@@ -1146,28 +1159,15 @@ class AyderApp(App):
1146
1159
 
1147
1160
  def action_toggle_tools(self) -> None:
1148
1161
  """Toggle the tool context panel."""
1149
- tool_panel = self.query_one("#tool-panel", ToolPanel)
1150
- visible = tool_panel.toggle()
1151
- chat_view = self.query_one("#chat-view", ChatView)
1152
- state = "visible" if visible else "hidden"
1153
- chat_view.add_system_message(f"Tool panel {state} (Ctrl+O to toggle)")
1162
+ self.query_one("#tool-panel", ToolPanel).toggle()
1154
1163
 
1155
1164
  def action_toggle_thinking(self) -> None:
1156
1165
  """Toggle the thinking/reasoning panel (mirrors Ctrl+O / Ctrl+G)."""
1157
- thinking_panel = self.query_one("#thinking-panel", ThinkingPanel)
1158
- visible = thinking_panel.toggle()
1159
- self._show_thinking = visible
1160
- chat_view = self.query_one("#chat-view", ChatView)
1161
- state = "visible" if visible else "hidden"
1162
- chat_view.add_system_message(f"Thinking panel {state} (Ctrl+T to toggle)")
1166
+ self._show_thinking = self.query_one("#thinking-panel", ThinkingPanel).toggle()
1163
1167
 
1164
1168
  def action_toggle_agents(self) -> None:
1165
1169
  """Toggle the agent panel."""
1166
- agent_panel = self.query_one("#agent-panel", AgentPanel)
1167
- visible = agent_panel.toggle()
1168
- chat_view = self.query_one("#chat-view", ChatView)
1169
- state = "visible" if visible else "hidden"
1170
- chat_view.add_system_message(f"Agent panel {state} (Ctrl+G to toggle)")
1170
+ self.query_one("#agent-panel", AgentPanel).toggle()
1171
1171
 
1172
1172
  def action_show_help(self) -> None:
1173
1173
  """Show help modal with keybindings."""
@@ -1187,8 +1187,6 @@ class AyderApp(App):
1187
1187
  return
1188
1188
  chat_view = self.query_one("#chat-view", ChatView)
1189
1189
  chat_view.scroll_page_down(animate=False)
1190
- if chat_view.scroll_offset.y >= chat_view.max_scroll_y:
1191
- chat_view.enable_follow_mode()
1192
1190
 
1193
1191
  def _scroll_open_panel(self, motion: str) -> bool:
1194
1192
  """Scroll a visible scrollable panel — the agents (Ctrl+G) or thinking
@@ -741,6 +741,10 @@ class AgentListScreen(ModalScreen[str | None]):
741
741
  self.dismiss(None)
742
742
 
743
743
 
744
+ # Mouse support captures the mouse, so the terminal's own selection needs a modifier.
745
+ SELECT_TEXT_HINT = "Hold Shift to select text with the mouse (Option in iTerm2 and Terminal.app)."
746
+
747
+
744
748
  class CLIHelpScreen(ModalScreen[None]):
745
749
  """Centered help modal showing all keybindings, dynamically generated."""
746
750
 
@@ -758,6 +762,8 @@ class CLIHelpScreen(ModalScreen[None]):
758
762
  for key, desc in entries:
759
763
  lines.append(f" {key:<16}{desc}")
760
764
  lines.append("")
765
+ lines.append(f" {SELECT_TEXT_HINT}")
766
+ lines.append("")
761
767
  lines.append(" Press Esc to close")
762
768
  return "\n".join(lines)
763
769
 
@@ -8,10 +8,13 @@ from typing import Any
8
8
  from textual.app import ComposeResult
9
9
  from textual.containers import Horizontal, VerticalScroll, Container
10
10
  from textual.css.query import NoMatches
11
+ from textual.geometry import Size
11
12
  from textual.message import Message
12
13
  from textual.reactive import reactive
13
14
  from textual.suggester import SuggestFromList
15
+ from textual.widget import Widget
14
16
  from textual.widgets import Static, Input, Label, TextArea
17
+ from rich.cells import cell_len
15
18
  from rich.text import Text
16
19
  from rich.markdown import Markdown
17
20
  from rich.spinner import Spinner
@@ -52,15 +55,58 @@ class ChatView(VerticalScroll):
52
55
  super().__init__(**kwargs)
53
56
  self._message_widgets: list[Static] = []
54
57
  self._thinking_visible: bool = False
55
- self._follow_mode: bool = True
58
+
59
+ def on_mount(self) -> None:
60
+ """Anchor to the bottom: follow the end of the chat.
61
+
62
+ Textual re-pins an anchored widget to its end inside the same layout
63
+ pass as any resize or new content, so a panel opening or a message
64
+ arriving never draws a frame at a stale offset. Scrolling up releases
65
+ the anchor; scrolling back to the end re-arms it.
66
+ """
67
+ self.anchor()
68
+
69
+ def _size_updated(
70
+ self, size: Size, virtual_size: Size, container_size: Size, layout: bool = True
71
+ ) -> bool:
72
+ # Textual pins an anchored widget using the pre-resize container_size, so
73
+ # the first frame after a resize (a panel opening) draws at a stale offset.
74
+ # Re-pin once the new size is in, then ask for another layout: the
75
+ # compositor placed the children with the stale offset, and a silent
76
+ # set_reactive does not move them (without it every open after the first
77
+ # left the newest rows cut off). Relies on private Widget/scrollbar
78
+ # attributes of textual 8.2.7; the draw-spy and painted-rows tests in
79
+ # test_panel_layout guard it.
80
+ updated = super()._size_updated(size, virtual_size, container_size, layout)
81
+ if updated and self._anchored and not self._anchor_released:
82
+ stale = self.scroll_y
83
+ end = self.max_scroll_y
84
+ self.set_reactive(Widget.scroll_y, end)
85
+ self.set_reactive(Widget.scroll_target_y, end)
86
+ self.vertical_scrollbar._reactive_position = end # type: ignore[attr-defined]
87
+ if end != stale:
88
+ self.refresh(layout=True)
89
+ return updated
90
+
91
+ def release_anchor(self) -> None:
92
+ # Textual re-arms an anchor only in watch_scroll_y, so a scroll that lands
93
+ # where it already is (PageDown at the end) would leave it released for
94
+ # good. Re-check once the scroll has landed; a user who scrolled up stays
95
+ # released because scroll_y is then below the end.
96
+ super().release_anchor()
97
+ self.call_after_refresh(self._reanchor_at_end)
98
+
99
+ def _reanchor_at_end(self) -> None:
100
+ if self.scroll_y >= self.max_scroll_y:
101
+ self.anchor()
56
102
 
57
103
  def disable_follow_mode(self) -> None:
58
- """Disable auto-scroll on new messages (user is reading history)."""
59
- self._follow_mode = False
104
+ """Stop following the end (user is reading history)."""
105
+ self.release_anchor()
60
106
 
61
107
  def enable_follow_mode(self) -> None:
62
- """Re-enable auto-scroll on new messages."""
63
- self._follow_mode = True
108
+ """Follow the end again and jump to it."""
109
+ self.anchor()
64
110
 
65
111
  def _create_text(
66
112
  self, content: str, msg_type: MessageType, metadata: dict | None = None
@@ -137,8 +183,6 @@ class ChatView(VerticalScroll):
137
183
  text = self._create_text(full_content, msg_type, metadata)
138
184
  if text:
139
185
  last_widget.update(text)
140
- if self._follow_mode:
141
- self.scroll_end(animate=False)
142
186
  return
143
187
 
144
188
  self.messages.append(
@@ -163,9 +207,6 @@ class ChatView(VerticalScroll):
163
207
  if msg_type == MessageType.THINKING and not self._thinking_visible:
164
208
  msg_widget.display = False
165
209
 
166
- if self._follow_mode:
167
- self.scroll_end(animate=False)
168
-
169
210
  def add_user_message(self, content: str) -> None:
170
211
  """Add a user message."""
171
212
  self.add_message(content, MessageType.USER)
@@ -241,6 +282,7 @@ class ToolPanel(Container):
241
282
  widget = Static(spinner, classes="tool-item running")
242
283
  self._tools[tool_call_id] = (widget, spinner, tool_name, args_str, False)
243
284
  self.mount(widget)
285
+ self.call_after_refresh(self.scroll_end, animate=False)
244
286
 
245
287
  def complete_tool(self, tool_call_id: str, result: str = "") -> None:
246
288
  """Mark a tool as completed."""
@@ -260,6 +302,7 @@ class ToolPanel(Container):
260
302
  widget.remove_class("running")
261
303
  widget.add_class("completed")
262
304
  self._tools[tool_call_id] = (widget, None, tool_name, args_str, True)
305
+ self.call_after_refresh(self.scroll_end, animate=False)
263
306
 
264
307
  def update_spinners(self) -> None:
265
308
  """Re-render running tool spinners (they auto-advance based on time)."""
@@ -298,8 +341,9 @@ class ThinkingPanel(Container):
298
341
  Toggled with Ctrl+T, mirroring ToolPanel (Ctrl+O) and AgentPanel (Ctrl+G):
299
342
  reasoning is streamed in silently while the ActivityBar spinner shows the
300
343
  ambient "Thinking..." status, and the user opens the panel to read the full
301
- chain. It is taller than the other panels because reasoning is long-form,
302
- and shows the complete accumulated text (scrollable) rather than a tail.
344
+ chain. Its height is a fixed 30% of the screen (three times the other
345
+ panels) because reasoning is long-form; it shows the complete accumulated text,
346
+ scrolling inside the panel, rather than a tail.
303
347
  """
304
348
 
305
349
  # Minimum seconds between re-renders while the panel is open. Reasoning
@@ -934,8 +978,6 @@ class _SubmitTextArea(TextArea):
934
978
  return
935
979
  chat_view = self.app.query_one("#chat-view", ChatView)
936
980
  chat_view.scroll_page_down(animate=False)
937
- if chat_view.scroll_offset.y >= chat_view.max_scroll_y:
938
- chat_view.enable_follow_mode()
939
981
  return
940
982
 
941
983
  if event.key == "ctrl+pageup":
@@ -955,8 +997,6 @@ class _SubmitTextArea(TextArea):
955
997
  return
956
998
  chat_view = self.app.query_one("#chat-view", ChatView)
957
999
  chat_view.scroll_down(animate=False)
958
- if chat_view.scroll_offset.y >= chat_view.max_scroll_y:
959
- chat_view.enable_follow_mode()
960
1000
  return
961
1001
 
962
1002
  if event.key == "home":
@@ -976,7 +1016,6 @@ class _SubmitTextArea(TextArea):
976
1016
  return
977
1017
  chat_view = self.app.query_one("#chat-view", ChatView)
978
1018
  chat_view.enable_follow_mode()
979
- chat_view.scroll_end(animate=False)
980
1019
  return
981
1020
 
982
1021
  self._reset_tab_cycle()
@@ -1147,6 +1186,69 @@ class CLIInputBar(Horizontal):
1147
1186
  self._input.insert(text)
1148
1187
 
1149
1188
 
1189
+ _STATUS_SEP = " | "
1190
+
1191
+
1192
+ def _abbreviate_home(path: Path | str) -> str:
1193
+ """Show a path with the home directory as ``~`` (``~`` itself for home)."""
1194
+ path = Path(path)
1195
+ try:
1196
+ relative = path.relative_to(Path.home())
1197
+ except (ValueError, RuntimeError): # not under home, or no home to compare to
1198
+ return str(path)
1199
+ return "~" if not relative.parts else f"~/{relative.as_posix()}"
1200
+
1201
+
1202
+ def _shorten_left(text: str, width: int) -> str:
1203
+ """Fit a path into ``width`` cells, dropping leading components for ``…``.
1204
+
1205
+ ``~/Vscode/ayder-cli/src`` in 15 cells becomes ``…/ayder-cli/src``. When even
1206
+ the last component is too wide, its tail is kept: ``…ongname``.
1207
+ """
1208
+ if width <= 0:
1209
+ return ""
1210
+ if cell_len(text) <= width:
1211
+ return text
1212
+ parts = [part for part in text.split("/") if part]
1213
+ if width == 1 or not parts:
1214
+ return "…"
1215
+ kept: list[str] = []
1216
+ for part in reversed(parts):
1217
+ if cell_len("…/" + "/".join([part, *kept])) > width:
1218
+ break
1219
+ kept.insert(0, part)
1220
+ if kept:
1221
+ return "…/" + "/".join(kept)
1222
+ return "…" + parts[-1][-(width - 1):]
1223
+
1224
+
1225
+ class _CwdLabel(Label):
1226
+ """The working directory, shortened from the left to the width it is given."""
1227
+
1228
+ def __init__(self, display: str, **kwargs):
1229
+ super().__init__("", **kwargs)
1230
+ self.display_text = display
1231
+
1232
+ def get_content_width(self, container: Size, viewport: Size) -> int:
1233
+ """Width from the path and the bar alone, never from the other items.
1234
+
1235
+ The path is shortened from the left to at most a third of the bar, but
1236
+ never below ``…/<last component>`` (or the whole path, if shorter). The
1237
+ items on its right may overflow the bar; the cwd stays visible.
1238
+ """
1239
+ sep = cell_len(_STATUS_SEP)
1240
+ last = self.display_text.rsplit("/", 1)[-1]
1241
+ budget = max(container.width // 3, cell_len(f"…/{last}"))
1242
+ shown = _shorten_left(self.display_text, min(budget, container.width - sep))
1243
+ return cell_len(shown) + sep
1244
+
1245
+ def render(self) -> Text:
1246
+ room = self.size.width - cell_len(_STATUS_SEP)
1247
+ if room < 1:
1248
+ return Text("")
1249
+ return Text(_shorten_left(self.display_text, room) + _STATUS_SEP)
1250
+
1251
+
1150
1252
  class StatusBar(Horizontal):
1151
1253
  """
1152
1254
  CLI-style status bar showing context info.
@@ -1157,9 +1259,11 @@ class StatusBar(Horizontal):
1157
1259
  model: str = "default",
1158
1260
  permissions: set | None = None,
1159
1261
  effort: str = "default",
1262
+ cwd: Path | str | None = None,
1160
1263
  **kwargs,
1161
1264
  ):
1162
1265
  super().__init__(**kwargs)
1266
+ self._cwd_display = _abbreviate_home(cwd if cwd is not None else Path.cwd())
1163
1267
  self.model = model
1164
1268
  self.effort = effort
1165
1269
  self.token_count = 0
@@ -1168,6 +1272,7 @@ class StatusBar(Horizontal):
1168
1272
 
1169
1273
  def compose(self) -> ComposeResult:
1170
1274
  mode_str = "".join(sorted(self._permissions))
1275
+ yield _CwdLabel(self._cwd_display, id="cwd-label")
1171
1276
  yield Label(markup_or_plain(f"model: {self.model}"), id="model-label")
1172
1277
  yield Label(f" | effort: {self.effort}", id="effort-label")
1173
1278
  yield Label(markup_or_plain(f" | mode: {mode_str}"), id="mode-label")
@@ -1342,6 +1447,7 @@ class AgentPanel(Container):
1342
1447
  )
1343
1448
  self._entries[run_id] = entry
1344
1449
  self.mount(status_widget)
1450
+ self.call_after_refresh(self.scroll_end, animate=False)
1345
1451
 
1346
1452
  def complete_agent(self, run_id: int, summary: str, status: str = "completed") -> None:
1347
1453
  """Mark agent as completed with full summary. Lookup by run_id."""