ayder-cli 2.4.0__tar.gz → 2.5.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 (350) hide show
  1. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/PKG-INFO +51 -9
  2. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/README.md +50 -8
  3. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/docs/GATE.md +1 -1
  4. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/docs/PROJECT_STRUCTURE.md +12 -4
  5. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/docs/config.toml.example +7 -1
  6. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/pyproject.toml +1 -1
  7. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/scripts/check_exception_handlers.py +1 -1
  8. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/scripts/check_log_arguments.py +7 -7
  9. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/scripts/log_argument_baseline.txt +5 -0
  10. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/agents/config.py +10 -2
  11. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/application/execution_policy.py +24 -3
  12. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/application/runtime_factory.py +27 -12
  13. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/application/validation.py +6 -0
  14. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/core/config.py +39 -2
  15. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/core/context.py +33 -0
  16. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/core/default_context_manager.py +75 -27
  17. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/core/ollama_context_manager.py +189 -30
  18. ayder_cli-2.5.0/src/ayder_cli/core/skills.py +498 -0
  19. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/loops/chat_loop.py +442 -236
  20. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/prompts.py +14 -106
  21. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/providers/impl/claude.py +26 -7
  22. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/providers/impl/gemini.py +26 -22
  23. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/providers/impl/ollama_drivers/generic_xml.py +1 -1
  24. ayder_cli-2.5.0/src/ayder_cli/skills/engineering/SKILL.md +35 -0
  25. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/tools/builtins/context.py +36 -5
  26. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/tools/builtins/note_tool.py +22 -0
  27. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/tools/builtins/notes.py +33 -10
  28. ayder_cli-2.5.0/src/ayder_cli/tools/builtins/skill.py +150 -0
  29. ayder_cli-2.5.0/src/ayder_cli/tools/builtins/skill_definitions.py +54 -0
  30. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/tools/builtins/task_tool.py +29 -1
  31. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/tools/builtins/tasks.py +59 -13
  32. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/tools/execution.py +4 -1
  33. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/tools/normalization.py +48 -29
  34. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/tools/registry.py +25 -9
  35. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/tui/app.py +7 -68
  36. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/tui/commands.py +62 -45
  37. ayder_cli-2.5.0/tests/application/test_argument_canonicalization.py +136 -0
  38. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/application/test_runtime_factory.py +7 -25
  39. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/conftest.py +16 -0
  40. ayder_cli-2.5.0/tests/core/test_config_skills.py +78 -0
  41. ayder_cli-2.5.0/tests/core/test_default_context_newest_unit.py +133 -0
  42. ayder_cli-2.5.0/tests/core/test_default_context_skill_resupply.py +178 -0
  43. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/core/test_event_emission.py +3 -2
  44. ayder_cli-2.5.0/tests/core/test_max_output_tokens_default.py +121 -0
  45. ayder_cli-2.5.0/tests/core/test_ollama_compaction_persistence.py +235 -0
  46. ayder_cli-2.5.0/tests/core/test_ollama_skill_resupply.py +151 -0
  47. ayder_cli-2.5.0/tests/core/test_skills.py +237 -0
  48. ayder_cli-2.5.0/tests/core/test_skills_builtin_sync.py +147 -0
  49. ayder_cli-2.5.0/tests/loops/test_tool_argument_integrity.py +247 -0
  50. ayder_cli-2.5.0/tests/loops/test_tool_batch_cancellation.py +506 -0
  51. ayder_cli-2.5.0/tests/providers/test_claude_stream_tool_calls.py +242 -0
  52. ayder_cli-2.5.0/tests/providers/test_gemini_function_calls.py +179 -0
  53. ayder_cli-2.5.0/tests/providers/test_skill_load_prefix.py +90 -0
  54. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/scripts/test_exception_gate.py +9 -5
  55. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/scripts/test_log_argument_gate.py +16 -16
  56. ayder_cli-2.5.0/tests/test_prompts_general.py +33 -0
  57. ayder_cli-2.5.0/tests/tools/test_context_storage_confinement.py +150 -0
  58. ayder_cli-2.5.0/tests/tools/test_notes_confinement.py +129 -0
  59. ayder_cli-2.5.0/tests/tools/test_skill_catalog_and_filter.py +182 -0
  60. ayder_cli-2.5.0/tests/tools/test_skill_scope_wiring.py +57 -0
  61. ayder_cli-2.5.0/tests/tools/test_skill_tool.py +178 -0
  62. ayder_cli-2.5.0/tests/tools/test_task_path_confinement.py +145 -0
  63. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/tui/test_turn_consumer.py +0 -66
  64. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/ui/test_tui_commands_provider.py +3 -0
  65. ayder_cli-2.5.0/tests/ui/test_tui_commands_skill.py +155 -0
  66. ayder_cli-2.5.0/tests/ui/test_tui_system_prompt_no_structure.py +44 -0
  67. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/uv.lock +1 -1
  68. ayder_cli-2.4.0/src/ayder_cli/tools/builtins/skill.py +0 -238
  69. ayder_cli-2.4.0/src/ayder_cli/tools/builtins/skill_definitions.py +0 -47
  70. ayder_cli-2.4.0/tests/tools/test_skill_tool.py +0 -220
  71. ayder_cli-2.4.0/tests/ui/test_tui_commands_skill.py +0 -116
  72. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/.github/workflows/ci.yml +0 -0
  73. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/.github/workflows/python-package.yml +0 -0
  74. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/.gitignore +0 -0
  75. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/.python-version +0 -0
  76. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/AGENTS.md +0 -0
  77. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/LICENSE +0 -0
  78. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/docs/README_AGENT_WORKFLOW.md +0 -0
  79. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/docs/README_OLLAMA.md +0 -0
  80. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/docs/cc.png +0 -0
  81. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/docs/superpowers/plans/2026-06-26-file-editor-improvements.md +0 -0
  82. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/docs/superpowers/plans/2026-06-26-session-save-resume.md +0 -0
  83. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/docs/superpowers/specs/2026-06-26-file-editor-improvements-design.md +0 -0
  84. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/docs/superpowers/specs/2026-06-26-session-save-resume-design.md +0 -0
  85. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/scripts/check_logging_imports.py +0 -0
  86. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/scripts/exception_baseline.txt +0 -0
  87. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/scripts/find_percent_logging.py +0 -0
  88. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/scripts/logging_gates.py +0 -0
  89. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/__init__.py +0 -0
  90. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/__main__.py +0 -0
  91. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/agents/__init__.py +0 -0
  92. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/agents/callbacks.py +0 -0
  93. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/agents/registry.py +0 -0
  94. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/agents/run.py +0 -0
  95. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/agents/runner.py +0 -0
  96. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/agents/tool.py +0 -0
  97. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/agents/worktree.py +0 -0
  98. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/application/README.md +0 -0
  99. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/application/__init__.py +0 -0
  100. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/application/message_contract.py +0 -0
  101. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/cli.py +0 -0
  102. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/cli_callbacks.py +0 -0
  103. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/cli_runner.py +0 -0
  104. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/console.py +0 -0
  105. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/core/cache_monitor.py +0 -0
  106. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/core/config_migration.py +0 -0
  107. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/core/context_manager.py +0 -0
  108. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/core/context_manager_factory.py +0 -0
  109. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/core/reasoning.py +0 -0
  110. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/core/result.py +0 -0
  111. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/core/session.py +0 -0
  112. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/diagnostics.py +0 -0
  113. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/log.py +0 -0
  114. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/logging_config.py +0 -0
  115. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/loops/__init__.py +0 -0
  116. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/masking.py +0 -0
  117. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/parser.py +0 -0
  118. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/process_manager.py +0 -0
  119. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/providers/__init__.py +0 -0
  120. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/providers/base.py +0 -0
  121. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/providers/evren.py +0 -0
  122. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/providers/impl/deepseek.py +0 -0
  123. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/providers/impl/glm.py +0 -0
  124. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/providers/impl/ollama.py +0 -0
  125. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/providers/impl/ollama_drivers/__init__.py +0 -0
  126. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/providers/impl/ollama_drivers/_errors.py +0 -0
  127. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/providers/impl/ollama_drivers/base.py +0 -0
  128. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/providers/impl/ollama_drivers/deepseek.py +0 -0
  129. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/providers/impl/ollama_drivers/generic_native.py +0 -0
  130. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/providers/impl/ollama_drivers/matrix.py +0 -0
  131. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/providers/impl/ollama_drivers/minimax.py +0 -0
  132. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/providers/impl/ollama_drivers/qwen3.py +0 -0
  133. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/providers/impl/ollama_drivers/registry.py +0 -0
  134. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/providers/impl/ollama_inspector.py +0 -0
  135. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/providers/impl/openai.py +0 -0
  136. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/providers/impl/qwen.py +0 -0
  137. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/providers/orchestrator.py +0 -0
  138. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/providers/retry.py +0 -0
  139. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/services/__init__.py +0 -0
  140. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/services/interactions.py +0 -0
  141. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/services/messaging_inbox.py +0 -0
  142. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/themes/__init__.py +0 -0
  143. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/themes/ayder.py +0 -0
  144. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/themes/claude.py +0 -0
  145. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/tools/__init__.py +0 -0
  146. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/tools/builtins/__init__.py +0 -0
  147. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/tools/builtins/background_process_tool.py +0 -0
  148. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/tools/builtins/context_definitions.py +0 -0
  149. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/tools/builtins/filesystem.py +0 -0
  150. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/tools/builtins/filesystem_definitions.py +0 -0
  151. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/tools/builtins/notes_definitions.py +0 -0
  152. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/tools/builtins/process_manager_definitions.py +0 -0
  153. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/tools/builtins/search.py +0 -0
  154. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/tools/builtins/search_definitions.py +0 -0
  155. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/tools/builtins/shell.py +0 -0
  156. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/tools/builtins/shell_definitions.py +0 -0
  157. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/tools/builtins/task_definitions.py +0 -0
  158. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/tools/builtins/utils_tools.py +0 -0
  159. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/tools/builtins/utils_tools_definitions.py +0 -0
  160. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/tools/builtins/web.py +0 -0
  161. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/tools/builtins/web_definitions.py +0 -0
  162. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/tools/definition.py +0 -0
  163. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/tools/hooks.py +0 -0
  164. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/tools/mcp.py +0 -0
  165. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/tools/mcp_client.py +0 -0
  166. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/tools/plugin_api.py +0 -0
  167. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/tools/plugin_github.py +0 -0
  168. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/tools/plugin_manager.py +0 -0
  169. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/tools/plugin_status.py +0 -0
  170. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/tools/schemas.py +0 -0
  171. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/tools/utils.py +0 -0
  172. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/tui/__init__.py +0 -0
  173. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/tui/adapter.py +0 -0
  174. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/tui/helpers.py +0 -0
  175. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/tui/keybindings.py +0 -0
  176. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/tui/parser.py +0 -0
  177. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/tui/rendering.py +0 -0
  178. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/tui/screens.py +0 -0
  179. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/tui/theme_manager.py +0 -0
  180. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/tui/types.py +0 -0
  181. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/tui/widgets.py +0 -0
  182. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/ui.py +0 -0
  183. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/src/ayder_cli/version.py +0 -0
  184. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/COVERAGE.md +0 -0
  185. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/__init__.py +0 -0
  186. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/agents/__init__.py +0 -0
  187. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/agents/test_agent_consolidated.py +0 -0
  188. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/agents/test_agent_severity.py +0 -0
  189. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/agents/test_agent_tools.py +0 -0
  190. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/agents/test_callbacks.py +0 -0
  191. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/agents/test_config.py +0 -0
  192. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/agents/test_integration.py +0 -0
  193. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/agents/test_queue.py +0 -0
  194. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/agents/test_registry.py +0 -0
  195. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/agents/test_run.py +0 -0
  196. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/agents/test_runner.py +0 -0
  197. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/agents/test_runner_provider_unavailable.py +0 -0
  198. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/agents/test_tool.py +0 -0
  199. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/agents/test_worktree_helpers.py +0 -0
  200. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/agents/test_worktree_isolation.py +0 -0
  201. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/application/test_execution_result_truthfulness.py +0 -0
  202. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/application/test_message_contract.py +0 -0
  203. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/client/test_main.py +0 -0
  204. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/convergence/__init__.py +0 -0
  205. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/convergence/test_execution_policy_parity.py +0 -0
  206. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/convergence/test_runtime_wiring.py +0 -0
  207. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/convergence/test_validation_path.py +0 -0
  208. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/core/test_asyncio_bridge.py +0 -0
  209. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/core/test_cache_monitor.py +0 -0
  210. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/core/test_config.py +0 -0
  211. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/core/test_config_coverage.py +0 -0
  212. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/core/test_config_migration.py +0 -0
  213. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/core/test_config_provider_logging.py +0 -0
  214. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/core/test_config_retry.py +0 -0
  215. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/core/test_config_v2.py +0 -0
  216. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/core/test_context_manager_factory.py +0 -0
  217. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/core/test_default_config_evren.py +0 -0
  218. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/core/test_default_context_manager.py +0 -0
  219. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/core/test_default_context_manager_should_compact.py +0 -0
  220. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/core/test_event_stream.py +0 -0
  221. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/core/test_exception_guarantee.py +0 -0
  222. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/core/test_exception_hooks.py +0 -0
  223. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/core/test_log_facade.py +0 -0
  224. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/core/test_logging_config_keys.py +0 -0
  225. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/core/test_logging_sinks.py +0 -0
  226. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/core/test_loguru_caplog.py +0 -0
  227. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/core/test_ollama_context_manager.py +0 -0
  228. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/core/test_ollama_context_manager_cache_monitor.py +0 -0
  229. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/core/test_ollama_context_manager_max_history.py +0 -0
  230. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/core/test_parameter_aliasing.py +0 -0
  231. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/core/test_parser.py +0 -0
  232. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/core/test_reasoning_config.py +0 -0
  233. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/core/test_session.py +0 -0
  234. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/core/test_session_id_threading.py +0 -0
  235. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/core/test_sink_masking.py +0 -0
  236. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/loops/__init__.py +0 -0
  237. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/loops/test_base.py +0 -0
  238. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/loops/test_chat_loop_hook.py +0 -0
  239. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/loops/test_chat_loop_tool_truncation.py +0 -0
  240. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/loops/test_loop_severity.py +0 -0
  241. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/loops/test_required_args_aliases.py +0 -0
  242. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/providers/__init__.py +0 -0
  243. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/providers/ollama_drivers/__init__.py +0 -0
  244. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/providers/ollama_drivers/test_base.py +0 -0
  245. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/providers/ollama_drivers/test_deepseek.py +0 -0
  246. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/providers/ollama_drivers/test_errors.py +0 -0
  247. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/providers/ollama_drivers/test_fallback.py +0 -0
  248. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/providers/ollama_drivers/test_generic_native.py +0 -0
  249. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/providers/ollama_drivers/test_generic_xml.py +0 -0
  250. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/providers/ollama_drivers/test_matrix.py +0 -0
  251. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/providers/ollama_drivers/test_minimax.py +0 -0
  252. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/providers/ollama_drivers/test_provider_integration.py +0 -0
  253. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/providers/ollama_drivers/test_qwen3.py +0 -0
  254. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/providers/ollama_drivers/test_registry.py +0 -0
  255. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/providers/test_evren.py +0 -0
  256. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/providers/test_glm_async.py +0 -0
  257. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/providers/test_ollama_inspector.py +0 -0
  258. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/providers/test_ollama_native.py +0 -0
  259. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/providers/test_ollama_parallel_tool_call_ids.py +0 -0
  260. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/providers/test_ollama_xml.py +0 -0
  261. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/providers/test_ollama_xml_autoroute.py +0 -0
  262. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/providers/test_ollama_xml_usage.py +0 -0
  263. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/providers/test_openai_list_models.py +0 -0
  264. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/providers/test_orchestrator_capabilities.py +0 -0
  265. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/providers/test_provider_channel_binding.py +0 -0
  266. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/providers/test_provider_severity.py +0 -0
  267. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/providers/test_qwen_async.py +0 -0
  268. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/providers/test_reasoning_effort.py +0 -0
  269. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/providers/test_retry_classifier.py +0 -0
  270. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/providers/test_retry_integration.py +0 -0
  271. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/providers/test_retrying_provider.py +0 -0
  272. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/scripts/test_logging_policy_gates.py +0 -0
  273. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/scripts/test_loguru_private_shape.py +0 -0
  274. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/services/test_boundary.py +0 -0
  275. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/services/test_interaction_sink.py +0 -0
  276. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/services/test_messaging_inbox.py +0 -0
  277. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/test_cli.py +0 -0
  278. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/test_cli_callbacks.py +0 -0
  279. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/test_context.py +0 -0
  280. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/test_env_manager.py +0 -0
  281. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/test_logging_config.py +0 -0
  282. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/test_notes.py +0 -0
  283. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/test_process_manager.py +0 -0
  284. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/test_tasks.py +0 -0
  285. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/tools/__init__.py +0 -0
  286. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/tools/test_agent_notes.py +0 -0
  287. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/tools/test_background_process_tool.py +0 -0
  288. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/tools/test_bash_tool.py +0 -0
  289. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/tools/test_definition_discovery.py +0 -0
  290. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/tools/test_di_injection.py +0 -0
  291. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/tools/test_file_editor_hardening.py +0 -0
  292. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/tools/test_file_editor_size_limit.py +0 -0
  293. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/tools/test_impl.py +0 -0
  294. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/tools/test_impl_coverage.py +0 -0
  295. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/tools/test_mcp.py +0 -0
  296. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/tools/test_mcp_client.py +0 -0
  297. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/tools/test_note_tool.py +0 -0
  298. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/tools/test_notes_api.py +0 -0
  299. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/tools/test_path_security.py +0 -0
  300. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/tools/test_plugin_api.py +0 -0
  301. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/tools/test_plugin_github.py +0 -0
  302. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/tools/test_plugin_loading.py +0 -0
  303. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/tools/test_plugin_manager.py +0 -0
  304. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/tools/test_plugin_status.py +0 -0
  305. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/tools/test_registry.py +0 -0
  306. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/tools/test_registry_coverage.py +0 -0
  307. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/tools/test_result.py +0 -0
  308. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/tools/test_schemas.py +0 -0
  309. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/tools/test_search_codebase.py +0 -0
  310. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/tools/test_search_hardening.py +0 -0
  311. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/tools/test_task_tool.py +0 -0
  312. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/tools/test_tool_severity.py +0 -0
  313. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/tools/test_unexpected_args.py +0 -0
  314. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/tools/test_utils.py +0 -0
  315. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/tools/test_web.py +0 -0
  316. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/tui/__init__.py +0 -0
  317. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/tui/test_agent_list_screen.py +0 -0
  318. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/tui/test_do_clear_coordination.py +0 -0
  319. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/tui/test_evren_terms.py +0 -0
  320. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/tui/test_follow_mode.py +0 -0
  321. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/tui/test_help_screen.py +0 -0
  322. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/tui/test_input_box.py +0 -0
  323. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/tui/test_keybindings.py +0 -0
  324. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/tui/test_multi_select_screen.py +0 -0
  325. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/tui/test_multiline_input.py +0 -0
  326. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/tui/test_paste.py +0 -0
  327. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/tui/test_peer_message_turn.py +0 -0
  328. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/tui/test_pending_compact_consumer.py +0 -0
  329. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/tui/test_pull_delivery.py +0 -0
  330. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/tui/test_resume.py +0 -0
  331. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/tui/test_status_bar_badges.py +0 -0
  332. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/tui/test_themes.py +0 -0
  333. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/tui/test_widgets.py +0 -0
  334. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/ui/test_confirm_screen.py +0 -0
  335. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/ui/test_diff_preview.py +0 -0
  336. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/ui/test_plugin_autoenable.py +0 -0
  337. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/ui/test_provider_switch.py +0 -0
  338. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/ui/test_run_tui_startup.py +0 -0
  339. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/ui/test_tui_assistant_sanitization.py +0 -0
  340. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/ui/test_tui_chat_loop.py +0 -0
  341. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/ui/test_tui_commands_agent.py +0 -0
  342. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/ui/test_tui_commands_effort.py +0 -0
  343. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/ui/test_tui_commands_logging.py +0 -0
  344. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/ui/test_tui_commands_plugin.py +0 -0
  345. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/ui/test_tui_helpers.py +0 -0
  346. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/ui/test_tui_temporal_command.py +0 -0
  347. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/ui/test_tui_widgets.py +0 -0
  348. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/ui/test_ui.py +0 -0
  349. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/tests/ui/test_ui_coverage.py +0 -0
  350. {ayder_cli-2.4.0 → ayder_cli-2.5.0}/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.4.0
3
+ Version: 2.5.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
@@ -204,10 +204,11 @@ editor = "vim" # Editor for /task-edit command
204
204
  verbose = false # Show file contents after write + LLM debug
205
205
  max_background_processes = 5 # Max concurrent background processes (1-20)
206
206
  max_iterations = 10 # Max agentic iterations per message (1-100)
207
- max_output_tokens = 4096 # Max tokens in LLM response
207
+ max_output_tokens = 32768 # Max tokens in LLM response
208
208
  max_history_messages = 30 # Messages kept in history
209
209
  prompt = "STANDARD" # System prompt tier: MINIMAL, STANDARD, EXTENDED
210
210
  tool_tags = ["core", "metadata"] # Enabled tool tags (see /plugin)
211
+ # skills = ["engineering"] # or "none"; default: the built-in skills
211
212
  agent_timeout = 300 # Seconds before a background agent is cancelled
212
213
 
213
214
  [logging]
@@ -353,7 +354,7 @@ ayder -f instructions.txt
353
354
  ayder --stdin < prompt.txt
354
355
 
355
356
  # Override the main LLM system prompt with a file
356
- # (tool instructions + project structure are still appended)
357
+ # (tool instructions are still appended)
357
358
  ayder --system-prompt prompt-file.md "refactor this code"
358
359
  ```
359
360
 
@@ -414,12 +415,13 @@ ayder-cli includes a context manager that automatically keeps your conversation
414
415
  - **Real token counts**: Uses Ollama's `prompt_eval_count` and `eval_count` from native API responses instead of estimates. Compaction decisions are based on actual usage, not heuristics.
415
416
  - **Cache monitoring**: Tracks `prompt_eval_duration / prompt_eval_count` ratio to detect KV-cache hits (hot/warm/miss). When the cache is hot, compaction is delayed (threshold raised from 70% to 90%) to avoid invalidating it.
416
417
  - **Auto-detected context length**: On startup, queries `/api/show` to get the model's real context length instead of relying on `num_ctx`. Falls back to `num_ctx` if detection fails.
417
- - **Heuristic compaction**: When the context fills up, oldest message units are replaced with a text summary — no LLM call needed.
418
+ - **Heuristic compaction**: When the context fills up, the oldest half of the message units is replaced with a text summary — no LLM call needed. The newest unit is never folded. The summary persists: every later request reuses the same summary message, so each request extends the previous one until the next compaction. A new compaction folds the previous summary into one capped summary (8,000 characters).
419
+ - **Prefix contract**: Message-level prefix stability holds while the model, tools and `max_history = 0` stay unchanged and the history is append-only. Rewriting history (`/clear`, a compact, loading a session) starts a new generation: the summary is dropped and compaction waits for a fresh token count. This guarantees a stable message prefix; whether the server reuses its cache is up to the server.
418
420
  - **Native SDK**: Uses `ollama.AsyncClient` directly (not the OpenAI compatibility layer) for full timing data, native thinking support, and `keep_alive=-1` to prevent model unloading.
419
421
 
420
422
  **General-purpose manager** (Anthropic, OpenAI, Gemini, and other drivers):
421
423
 
422
- Every message is assigned an importance tier (system > recent user > recent assistant > tool results > old history). When the conversation approaches the token budget, old tool results are compressed (JSON structurally summarized, large text head/tail truncated) and the lowest-priority messages are pruned. Tool call + result pairs are kept as atomic units.
424
+ Every message is assigned an importance tier (system > recent user > recent assistant > tool results > old history). When the conversation approaches the token budget, old tool results are compressed (JSON structurally summarized, large text head/tail truncated) and the lowest-priority messages are pruned. Tool call + result pairs are kept as atomic units. The newest unit is always kept, even when it alone exceeds the budget, so the model always sees the request it is answering.
423
425
 
424
426
  The context manager is configured under `[context_manager]` in `config.toml`:
425
427
 
@@ -451,7 +453,7 @@ You can also manually manage context with the `context` tool and its slash-comma
451
453
  | `/tasks` | Browse and implement tasks from `.ayder/tasks/` |
452
454
  | `/implement [id]` | Interactive task picker, or implement by ID (e.g., `/implement 1`) |
453
455
  | `/notes` | Browse and edit markdown notes |
454
- | `/skill` | Activate a domain skill from `.ayder/skills/` via the shared `skill` tool backend |
456
+ | `/skill` | Load a skill as a user message: `/skill <name> [request]`, or pick one |
455
457
  | `/verbose` | Toggle verbose mode |
456
458
  | `/logging` | Set log level for current session (NONE, ERROR, WARNING, INFO, DEBUG, TRACE) |
457
459
  | `/compact` | Summarize conversation, save a context snapshot, clear, and reload |
@@ -465,10 +467,47 @@ You can also manually manage context with the `context` tool and its slash-comma
465
467
  | `/agent <name> <task>` | Dispatch an agent to run a task in the background |
466
468
  | `/agent cancel <name>` | Cancel a running agent |
467
469
 
468
- ### Logging
470
+ ### Skills
471
+
472
+ A skill is a directory with a `SKILL.md` file, optionally starting with
473
+ `name:` and `description:` frontmatter. ayder looks in three places, and a
474
+ later one overrides an earlier one with the same name:
475
+
476
+ 1. built-in skills in `~/.ayder/builtin/skills/`, managed by ayder and
477
+ replaced on upgrade; to customize one, copy it to `~/.ayder/skills/<name>/`
478
+ 2. your skills in `~/.ayder/skills/<name>/`
479
+ 3. project skills in `<project>/.ayder/skills/<name>/`
480
+
481
+ ayder ships an `engineering` skill (build, change, debug or review code;
482
+ research; plan multi-step work). The system prompt is a general assistant;
483
+ when a skill fits the request, the model loads it itself.
484
+
485
+ The skills enabled for a session are listed in the system prompt under
486
+ "### Skills" (name and description), and only those can be loaded by the
487
+ model. The `skills` setting chooses them:
488
+
489
+ | `skills` | Enabled skills |
490
+ |---|---|
491
+ | not set | the built-in skills (`engineering`), including your or the project's copy of the same name |
492
+ | `"none"` or `[]` | none: no skill tool, no catalog |
493
+ | `["a", "b"]` | exactly these names, from any layer |
494
+
495
+ The main session reads `[llm.<provider>].skills`, then `[app].skills`. An
496
+ agent reads `[agents.<name>].skills`, then its provider profile's; never
497
+ `[app]`. The `/skill` command loads any discovered skill, including project
498
+ skills that are not in the catalog.
499
+
500
+ The model loads one with `skill(action="load", name=...)`; the instructions
501
+ come back as the tool result, so the conversation's earlier messages (and the
502
+ provider's KV cache) are unchanged. Several skills can be active; other files
503
+ in a skill's directory are read on demand with `skill(action="read")`.
504
+ A skill directory or `SKILL.md` that links outside its skills directory is
505
+ listed but never read or loaded; copy the skill instead of symlinking it.
506
+ A loaded skill stays in context for the whole session: when trimming or
507
+ compaction removes it, ayder adds it back right after the system prompt, or
508
+ inside the Ollama summary.
469
509
 
470
- The LLM can also call `skill(action="list" | "load" | "unload")`
471
- automatically when a request clearly matches an available project skill.
510
+ ### Logging
472
511
 
473
512
  - Errors are always captured to `.ayder/log/errors.log`, even when
474
513
  `logging_level` is `NONE`. Set `file_enabled = false` under `[logging]` to
@@ -545,6 +584,8 @@ ayder-cli includes a built-in task system:
545
584
 
546
585
  Tasks are stored as markdown files in `.ayder/tasks/` using slug-based filenames (e.g., `TASK-001-add-auth-middleware.md`).
547
586
 
587
+ The `task` tool resolves identifiers (a path, filename, ID or slug) only to files inside `.ayder/tasks/`. An identifier that points elsewhere, including through a symlink, is not resolved. If `.ayder/tasks/`, `.ayder/context/` or `.ayder/notes/` itself resolves outside the project, those tools return a security error.
588
+
548
589
  ```
549
590
  > /tasks # Interactive task browser
550
591
  > /task-edit 1 # Open TASK-001 in the in-app editor
@@ -667,6 +708,7 @@ Each agent field:
667
708
  | `system_prompt` | Yes | The agent's role and instructions |
668
709
  | `provider` | No | LLM provider profile name (inherits from `[app]` if omitted) |
669
710
  | `model` | No | Model name override (inherits from provider profile if omitted) |
711
+ | `skills` | No | `"none"` or a list of skill names (inherits from the provider profile; default: the built-in skills) |
670
712
 
671
713
  ### Using Agents
672
714
 
@@ -161,10 +161,11 @@ editor = "vim" # Editor for /task-edit command
161
161
  verbose = false # Show file contents after write + LLM debug
162
162
  max_background_processes = 5 # Max concurrent background processes (1-20)
163
163
  max_iterations = 10 # Max agentic iterations per message (1-100)
164
- max_output_tokens = 4096 # Max tokens in LLM response
164
+ max_output_tokens = 32768 # Max tokens in LLM response
165
165
  max_history_messages = 30 # Messages kept in history
166
166
  prompt = "STANDARD" # System prompt tier: MINIMAL, STANDARD, EXTENDED
167
167
  tool_tags = ["core", "metadata"] # Enabled tool tags (see /plugin)
168
+ # skills = ["engineering"] # or "none"; default: the built-in skills
168
169
  agent_timeout = 300 # Seconds before a background agent is cancelled
169
170
 
170
171
  [logging]
@@ -310,7 +311,7 @@ ayder -f instructions.txt
310
311
  ayder --stdin < prompt.txt
311
312
 
312
313
  # Override the main LLM system prompt with a file
313
- # (tool instructions + project structure are still appended)
314
+ # (tool instructions are still appended)
314
315
  ayder --system-prompt prompt-file.md "refactor this code"
315
316
  ```
316
317
 
@@ -371,12 +372,13 @@ ayder-cli includes a context manager that automatically keeps your conversation
371
372
  - **Real token counts**: Uses Ollama's `prompt_eval_count` and `eval_count` from native API responses instead of estimates. Compaction decisions are based on actual usage, not heuristics.
372
373
  - **Cache monitoring**: Tracks `prompt_eval_duration / prompt_eval_count` ratio to detect KV-cache hits (hot/warm/miss). When the cache is hot, compaction is delayed (threshold raised from 70% to 90%) to avoid invalidating it.
373
374
  - **Auto-detected context length**: On startup, queries `/api/show` to get the model's real context length instead of relying on `num_ctx`. Falls back to `num_ctx` if detection fails.
374
- - **Heuristic compaction**: When the context fills up, oldest message units are replaced with a text summary — no LLM call needed.
375
+ - **Heuristic compaction**: When the context fills up, the oldest half of the message units is replaced with a text summary — no LLM call needed. The newest unit is never folded. The summary persists: every later request reuses the same summary message, so each request extends the previous one until the next compaction. A new compaction folds the previous summary into one capped summary (8,000 characters).
376
+ - **Prefix contract**: Message-level prefix stability holds while the model, tools and `max_history = 0` stay unchanged and the history is append-only. Rewriting history (`/clear`, a compact, loading a session) starts a new generation: the summary is dropped and compaction waits for a fresh token count. This guarantees a stable message prefix; whether the server reuses its cache is up to the server.
375
377
  - **Native SDK**: Uses `ollama.AsyncClient` directly (not the OpenAI compatibility layer) for full timing data, native thinking support, and `keep_alive=-1` to prevent model unloading.
376
378
 
377
379
  **General-purpose manager** (Anthropic, OpenAI, Gemini, and other drivers):
378
380
 
379
- Every message is assigned an importance tier (system > recent user > recent assistant > tool results > old history). When the conversation approaches the token budget, old tool results are compressed (JSON structurally summarized, large text head/tail truncated) and the lowest-priority messages are pruned. Tool call + result pairs are kept as atomic units.
381
+ Every message is assigned an importance tier (system > recent user > recent assistant > tool results > old history). When the conversation approaches the token budget, old tool results are compressed (JSON structurally summarized, large text head/tail truncated) and the lowest-priority messages are pruned. Tool call + result pairs are kept as atomic units. The newest unit is always kept, even when it alone exceeds the budget, so the model always sees the request it is answering.
380
382
 
381
383
  The context manager is configured under `[context_manager]` in `config.toml`:
382
384
 
@@ -408,7 +410,7 @@ You can also manually manage context with the `context` tool and its slash-comma
408
410
  | `/tasks` | Browse and implement tasks from `.ayder/tasks/` |
409
411
  | `/implement [id]` | Interactive task picker, or implement by ID (e.g., `/implement 1`) |
410
412
  | `/notes` | Browse and edit markdown notes |
411
- | `/skill` | Activate a domain skill from `.ayder/skills/` via the shared `skill` tool backend |
413
+ | `/skill` | Load a skill as a user message: `/skill <name> [request]`, or pick one |
412
414
  | `/verbose` | Toggle verbose mode |
413
415
  | `/logging` | Set log level for current session (NONE, ERROR, WARNING, INFO, DEBUG, TRACE) |
414
416
  | `/compact` | Summarize conversation, save a context snapshot, clear, and reload |
@@ -422,10 +424,47 @@ You can also manually manage context with the `context` tool and its slash-comma
422
424
  | `/agent <name> <task>` | Dispatch an agent to run a task in the background |
423
425
  | `/agent cancel <name>` | Cancel a running agent |
424
426
 
425
- ### Logging
427
+ ### Skills
428
+
429
+ A skill is a directory with a `SKILL.md` file, optionally starting with
430
+ `name:` and `description:` frontmatter. ayder looks in three places, and a
431
+ later one overrides an earlier one with the same name:
432
+
433
+ 1. built-in skills in `~/.ayder/builtin/skills/`, managed by ayder and
434
+ replaced on upgrade; to customize one, copy it to `~/.ayder/skills/<name>/`
435
+ 2. your skills in `~/.ayder/skills/<name>/`
436
+ 3. project skills in `<project>/.ayder/skills/<name>/`
437
+
438
+ ayder ships an `engineering` skill (build, change, debug or review code;
439
+ research; plan multi-step work). The system prompt is a general assistant;
440
+ when a skill fits the request, the model loads it itself.
441
+
442
+ The skills enabled for a session are listed in the system prompt under
443
+ "### Skills" (name and description), and only those can be loaded by the
444
+ model. The `skills` setting chooses them:
445
+
446
+ | `skills` | Enabled skills |
447
+ |---|---|
448
+ | not set | the built-in skills (`engineering`), including your or the project's copy of the same name |
449
+ | `"none"` or `[]` | none: no skill tool, no catalog |
450
+ | `["a", "b"]` | exactly these names, from any layer |
451
+
452
+ The main session reads `[llm.<provider>].skills`, then `[app].skills`. An
453
+ agent reads `[agents.<name>].skills`, then its provider profile's; never
454
+ `[app]`. The `/skill` command loads any discovered skill, including project
455
+ skills that are not in the catalog.
456
+
457
+ The model loads one with `skill(action="load", name=...)`; the instructions
458
+ come back as the tool result, so the conversation's earlier messages (and the
459
+ provider's KV cache) are unchanged. Several skills can be active; other files
460
+ in a skill's directory are read on demand with `skill(action="read")`.
461
+ A skill directory or `SKILL.md` that links outside its skills directory is
462
+ listed but never read or loaded; copy the skill instead of symlinking it.
463
+ A loaded skill stays in context for the whole session: when trimming or
464
+ compaction removes it, ayder adds it back right after the system prompt, or
465
+ inside the Ollama summary.
426
466
 
427
- The LLM can also call `skill(action="list" | "load" | "unload")`
428
- automatically when a request clearly matches an available project skill.
467
+ ### Logging
429
468
 
430
469
  - Errors are always captured to `.ayder/log/errors.log`, even when
431
470
  `logging_level` is `NONE`. Set `file_enabled = false` under `[logging]` to
@@ -502,6 +541,8 @@ ayder-cli includes a built-in task system:
502
541
 
503
542
  Tasks are stored as markdown files in `.ayder/tasks/` using slug-based filenames (e.g., `TASK-001-add-auth-middleware.md`).
504
543
 
544
+ The `task` tool resolves identifiers (a path, filename, ID or slug) only to files inside `.ayder/tasks/`. An identifier that points elsewhere, including through a symlink, is not resolved. If `.ayder/tasks/`, `.ayder/context/` or `.ayder/notes/` itself resolves outside the project, those tools return a security error.
545
+
505
546
  ```
506
547
  > /tasks # Interactive task browser
507
548
  > /task-edit 1 # Open TASK-001 in the in-app editor
@@ -624,6 +665,7 @@ Each agent field:
624
665
  | `system_prompt` | Yes | The agent's role and instructions |
625
666
  | `provider` | No | LLM provider profile name (inherits from `[app]` if omitted) |
626
667
  | `model` | No | Model name override (inherits from provider profile if omitted) |
668
+ | `skills` | No | `"none"` or a list of skill names (inherits from the provider profile; default: the built-in skills) |
627
669
 
628
670
  ### Using Agents
629
671
 
@@ -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: `2494 passed, 2 skipped` on `feat/evren-terms` with the evren default profile (2026-09-29).
15
+ Last measured: `2707 passed, 2 skipped` on `chore/prompt-tests-injection-only` (2026-10-16).
16
16
 
17
17
  ## Report format
18
18
 
@@ -72,6 +72,7 @@ ayder-cli is an AI agent chat client with a modular, layered architecture:
72
72
  │ core/config.py (Pydantic Config) │
73
73
  │ core/context.py (ProjectContext, sandboxing) │
74
74
  │ core/result.py (ToolSuccess, ToolError) │
75
+ │ core/skills.py (skill discovery, delivery) │
75
76
  │ core/context_manager.py (ContextManagerProtocol) │
76
77
  │ core/default_context_manager.py │
77
78
  │ core/ollama_context_manager.py (KV-cache-aware) │
@@ -154,6 +155,8 @@ cli.py:main()
154
155
  | `core/config_migration.py` | TOML config migration helpers | migration routines for schema changes |
155
156
  | `core/context.py` | Project sandboxing | `ProjectContext` |
156
157
  | `core/result.py` | Tool result types | `ToolSuccess`, `ToolError` |
158
+ | `core/skills.py` | Skill discovery, delivery, built-in sync and the session filter | `SkillInfo`, `SkillScope`, `discover_all()`, `render_skill_block()`, `active_skills()`, `builtin_skills_root()`, `build_scope()`, `format_catalog()` |
159
+ | `skills/<name>/SKILL.md` | Packaged built-in skills (data only, shipped in the wheel); synced to `~/.ayder/builtin/skills` when `~/.ayder/builtin/STAMP` (version + SHA-256) differs | - |
157
160
  | `core/context_manager.py` | ContextManager Protocol + shared utilities | `ContextManagerProtocol`, `ContextStats`, `truncate_tool_result()` |
158
161
  | `core/default_context_manager.py` | Tiered context manager for non-Ollama providers | `DefaultContextManager`, `TokenCounter`, `MessageTier` |
159
162
  | `core/ollama_context_manager.py` | KV-cache-aware context manager for Ollama | `OllamaContextManager`, `OllamaContextStats` |
@@ -294,6 +297,7 @@ the design rationale.
294
297
  | `tools/builtins/context.py` | Session context snapshots | `context` |
295
298
  | `tools/builtins/notes.py` | Note-taking tool | `create_note()` |
296
299
  | `tools/builtins/tasks.py` | Task-management tools | `list_tasks()`, `show_task()` |
300
+ | `tools/builtins/skill.py` | Skill tool impl | `skill()`, `discover_skills()` |
297
301
  | `tools/builtins/web.py` | HTTP fetch tool | `fetch_web()` |
298
302
  | `tools/builtins/utils_tools.py` | Misc utility tools | `manage_environment_vars()` |
299
303
  | `tools/builtins/*_definitions.py` | Per-domain tool definitions (9 files) | `TOOL_DEFINITIONS` tuples auto-discovered at import |
@@ -303,7 +307,7 @@ the design rationale.
303
307
  | Module | Purpose | Key Classes/Functions |
304
308
  |--------|---------|----------------------|
305
309
  | `process_manager.py` | Background processes | `ProcessManager`, `background_process()` |
306
- | `prompts.py` | System prompts | `SYSTEM_PROMPT`, `PLANNING_PROMPT_TEMPLATE`, `get_system_prompt()`, `PROJECT_STRUCTURE_MACRO_TEMPLATE` |
310
+ | `prompts.py` | System prompts | `SYSTEM_PROMPT`, `PLANNING_PROMPT_TEMPLATE`, `get_system_prompt()` |
307
311
  | `parser.py` | XML tool-call parsing | `parse_custom_tool_calls()` |
308
312
 
309
313
  ### Theme Modules
@@ -590,7 +594,7 @@ AyderApp is the main Textual application implementing a chat-style interface:
590
594
  The application manages:
591
595
  - LLM provider setup and configuration
592
596
  - Tool registry initialization with middleware
593
- - System prompt construction with project structure
597
+ - System prompt construction (base prompt + tool prompts + skill catalog; no project tree)
594
598
  - Process manager for background operations
595
599
 
596
600
  ### Chat Loop Summary (src/ayder_cli/loops/chat_loop.py)
@@ -605,9 +609,11 @@ previously-split `AgentLoopBase` and `LoopConfig` were merged back in):
605
609
  - Routes *every* LLM response through the injected `ContextManagerProtocol` (`prepare_messages()` / `update_from_response()`); tool outputs are truncated at insertion time via `truncate_tool_result()` so the KV-cache prefix remains stable
606
610
 
607
611
  Key features:
608
- - Auto-approved tools run in parallel (`asyncio.gather`); confirmation-required tools run sequentially
612
+ - Auto-approved tools run in parallel (`asyncio.as_completed`); confirmation-required tools run sequentially
609
613
  - Confirmation gate applies to **both** OpenAI-format and XML/JSON custom tool calls
610
- - `ExecutionPolicy.execute_with_registry()` is the single execution entry (validate → permission → execute)
614
+ - `ExecutionPolicy.execute_with_registry()` is the single execution entry (object check → `canonicalize_arguments()` → validate → permission → execute). Canonicalization applies aliases and `"123"`/`"true"`/`"false"` coercion; the loop's required-argument gate uses the same function
615
+ - Argument integrity: `_parse_tool_arguments()` reports `ok`, `repaired` or `invalid`. Non-object arguments never run. Arguments repaired after being cut off (output token limit) run only for tools registered explicitly as `r`; other tools get an error asking the model to resend in smaller calls
616
+ - Cancellation: each batch owns a `_ToolBatch`. Workers refuse to start once the batch is cancelled and record their own result and `tool_call` event. When a batch ends early (Ctrl+C or a raising callback), a `finally` appends exactly one tool message per unanswered call: its result, an "interrupted, may have partially applied" notice, or a "cancelled before it ran" notice. Running workers are not awaited, so Ctrl+C returns at once
611
617
  - Token usage tracking and iteration limiting
612
618
  - Pre-iteration hook (`pre_iteration_hook`) used by the agent system to inject summaries
613
619
  - Graceful cancellation via `is_cancelled()`
@@ -717,6 +723,8 @@ Schema-driven tool definitions with auto-discovery:
717
723
  - `OllamaContextManager` (`core/ollama_context_manager.py`) — stable-prefix + real-token accounting, uses `prompt_eval_count` / `prompt_eval_duration` directly.
718
724
  - `CacheMonitor` (`core/cache_monitor.py`) — timing-based KV-cache hit detection driving adaptive compaction thresholds.
719
725
 
726
+ Both managers re-supply active skills that a trim or compaction removed (`core/skills.py` `active_skills`).
727
+
720
728
  Selection lives in `core/context_manager_factory.py` (driver → class path registry). `create_runtime()` owns instantiation and calls `freeze_system_prompt()` once.
721
729
 
722
730
  ### Phase — opus47 critical fixes (2026-04-16)
@@ -97,6 +97,10 @@ agent_timeout = 1800 # seconds before a background agent is cancel
97
97
  # tasks/notes (metadata), background process control for test suites
98
98
  # (background), and web fetch (http). See /plugin for the full tag list.
99
99
  tool_tags = ["core", "metadata", "background", "http"]
100
+ # Skills the session's model may load (listed under "### Skills" in the system
101
+ # prompt). Not set = the built-in skills; "none" = no skill tool; or a strict
102
+ # list. A [llm.*] profile's skills wins over this one for the session.
103
+ # skills = ["engineering"]
100
104
 
101
105
  [ui]
102
106
  # TUI layout (theme) — picks the chrome/layout:
@@ -180,6 +184,7 @@ driver = "ollama"
180
184
  base_url = "http://localhost:11434"
181
185
  model = "qwen3.6:35b-a3b-coding-nvfp4"
182
186
  num_ctx = 32768 # keep modest: local VRAM is the constraint
187
+ # skills = "none" # wins over [app].skills; agents on this profile inherit it
183
188
  reasoning_effort = "default"
184
189
  think = true # default; set false if the selected model rejects thinking
185
190
 
@@ -201,7 +206,7 @@ think = true # default; set false if the selected model r
201
206
  # api_key = "sk-..." # your key (or "ollama"/"none" for local servers)
202
207
  # model = "gpt-5.5"
203
208
  # num_ctx = 65536 # sizes the context-manager budget (default 65536); NOT sent to the API
204
- # max_output_tokens = 8192 # -> max_tokens / max_completion_tokens (default 4096); keep WELL below num_ctx
209
+ # max_output_tokens = 8192 # -> max_tokens / max_completion_tokens (default 32768); keep WELL below num_ctx
205
210
  # stop_sequences = [] # -> stop (default [])
206
211
  # reasoning_effort = "medium" # choose a value supported by the selected model
207
212
  # # temperature / think are NOT applied by the openai driver.
@@ -224,6 +229,7 @@ think = true # default; set false if the selected model r
224
229
  [agents.pm_spec]
225
230
  provider = "ollama_cloud"
226
231
  model = "glm-5.2:cloud"
232
+ # skills = ["engineering"] # wins over the provider profile's skills; [app] is never read
227
233
  temperature = 0.4
228
234
  max_output_tokens = 8192
229
235
  reasoning_effort = "high"
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "ayder-cli"
7
- version = "2.4.0"
7
+ version = "2.5.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 = 137
27
+ EXPECTED_BROAD = 134
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 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
206
+ # The 98 `exception=True` inputs are homogeneous, so they are governed as one
207
+ # package-wide shape/count rule rather than 98 site identities - their per-site
208
208
  # discipline is C10's.
209
- FROZEN_EXCEPTION_TRUE = 101
209
+ FROZEN_EXCEPTION_TRUE = 98
210
210
 
211
211
  # Live chain inputs, counted per method. Printed on every run.
212
- CHAIN_TALLY: dict[str, int] = {"bind": 6, "opt": 105, "patch": 0}
212
+ CHAIN_TALLY: dict[str, int] = {"bind": 6, "opt": 102, "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
@@ -218,12 +218,12 @@ CHAIN_TALLY: dict[str, int] = {"bind": 6, "opt": 105, "patch": 0}
218
218
  # unchanged - so the composition is gated too. Caller-supplied baselines (the
219
219
  # gate's own synthetic fixtures) are exempt: this is a fact about one file.
220
220
  FROZEN_TALLY: dict[str, int] = {
221
- "R-name": 96,
221
+ "R-name": 99,
222
222
  "R-id": 40,
223
223
  "R-count": 90,
224
224
  "R-status": 27,
225
- "R-class": 10,
226
- "R-path": 24,
225
+ "R-class": 11,
226
+ "R-path": 25,
227
227
  # Both 5-04 deferrals are closed: the two asyncio rows now interpolate the
228
228
  # B2-prime normalized value, and the bridge's dynamic message is masked by
229
229
  # the prose sinks it reaches.
@@ -83,6 +83,7 @@
83
83
  {"class":"R-name","site":{"arg_index":"pos:0","expr":"name","message":"Uninstalled plugin '{}'","method":"info","path":"tools/plugin_manager.py","qualname":"uninstall_plugin"}}
84
84
  {"class":"R-name","site":{"arg_index":"pos:0","expr":"name","message":"cancel: agent='{}' \u2014 no running/queued instances","method":"debug","path":"agents/registry.py","qualname":"AgentRegistry.cancel"}}
85
85
  {"class":"R-name","site":{"arg_index":"pos:0","expr":"name","message":"cancel: agent='{}' cancelled {} instance(s): {}","method":"debug","path":"agents/registry.py","qualname":"AgentRegistry.cancel"}}
86
+ {"class":"R-name","site":{"arg_index":"pos:0","expr":"name","message":"skills lists {!r}, but no skill directory provides it","method":"warning","path":"core/skills.py","qualname":"build_scope"}}
86
87
  {"class":"R-status","site":{"arg_index":"pos:0","expr":"normalized","message":"Unhandled asyncio error: {}","method":"error","path":"diagnostics.py","qualname":"_handle_asyncio"}}
87
88
  {"class":"R-status","site":{"arg_index":"pos:0","expr":"normalized","message":"Unhandled asyncio exception: {}","method":"critical","path":"diagnostics.py","qualname":"_handle_asyncio"}}
88
89
  {"class":"R-count","site":{"arg_index":"pos:0","expr":"num_ctx","message":"Context budget: {} tokens (derived from num_ctx; max_context_tokens unset)","method":"info","path":"core/default_context_manager.py","qualname":"DefaultContextManager.__init__"}}
@@ -138,7 +139,9 @@
138
139
  {"class":"R-status","site":{"arg_index":"pos:0","expr":"self.config.chat_protocol","message":"Ollama chat_protocol={!r} is not recognized; forcing generic_xml fallback","method":"warning","path":"providers/impl/ollama.py","qualname":"OllamaProvider.stream_with_tools"}}
139
140
  {"class":"R-name","site":{"arg_index":"pos:0","expr":"self.config.model","message":"LLM returned empty response (possible connection drop). model={}, provider={}","method":"error","path":"loops/chat_loop.py","qualname":"ChatLoop.run"}}
140
141
  {"class":"R-status","site":{"arg_index":"pos:0","expr":"signal.Signals(signum).name","message":"Received signal {}; flushing logs","method":"warning","path":"diagnostics.py","qualname":"install_exception_hooks._chained_signal"}}
142
+ {"class":"R-name","site":{"arg_index":"pos:0","expr":"skill_dir.name","message":"Skill directory {!r} declares name {!r}; the directory name is used","method":"debug","path":"core/skills.py","qualname":"read_skill_info"}}
141
143
  {"class":"R-name","site":{"arg_index":"pos:0","expr":"source.repo","message":"Downloading '{}' at {}","method":"info","path":"tools/plugin_github.py","qualname":"download_plugin"}}
144
+ {"class":"R-path","site":{"arg_index":"pos:0","expr":"target","message":"Built-in skills were not synced to {} ({}); reading the packaged copy","method":"warning","path":"core/skills.py","qualname":"_sync_builtin_skills"}}
142
145
  {"class":"R-name","site":{"arg_index":"pos:0","expr":"tc.function.name","message":"Tool '{}' called with missing args {} ({} chars raw)","method":"warning","path":"loops/chat_loop.py","qualname":"ChatLoop._execute_tool_calls"}}
143
146
  {"class":"R-name","site":{"arg_index":"pos:0","expr":"tc_entry['function'].get('name', '?')","message":"Malformed tool arguments for '{}': {} chars \u2014 repairing before storing in history","method":"warning","path":"loops/chat_loop.py","qualname":"ChatLoop.run"}}
144
147
  {"class":"R-name","site":{"arg_index":"pos:0","expr":"tc_obj.function.name","message":"Tool execution cancelled: {}","method":"warning","path":"loops/chat_loop.py","qualname":"ChatLoop._execute_tool_calls._safe_exec"}}
@@ -160,6 +163,7 @@
160
163
  {"class":"R-status","site":{"arg_index":"pos:1","expr":"', '.join(valid)","message":"Unsupported driver (value_chars={}); must be one of: {}. Fix the driver in your [llm.<provider>] profile.","method":"error","path":"core/config.py","qualname":"Config.validate_driver"}}
161
164
  {"class":"R-name","site":{"arg_index":"pos:1","expr":"agent_name","message":"Agent panel could not show run {} of agent {}","method":"debug","path":"tui/commands.py","qualname":"handle_agent"}}
162
165
  {"class":"R-id","site":{"arg_index":"pos:1","expr":"commit_sha[:7]","message":"Downloading '{}' at {}","method":"info","path":"tools/plugin_github.py","qualname":"download_plugin"}}
166
+ {"class":"R-name","site":{"arg_index":"pos:1","expr":"declared","message":"Skill directory {!r} declares name {!r}; the directory name is used","method":"debug","path":"core/skills.py","qualname":"read_skill_info"}}
163
167
  {"class":"R-count","site":{"arg_index":"pos:1","expr":"delay","message":"Provider stream failed ({}); retrying in {:.2f}s ({} attempts left)","method":"info","path":"providers/retry.py","qualname":"RetryingProvider.stream_with_tools"}}
164
168
  {"class":"R-status","site":{"arg_index":"pos:1","expr":"driver.mode.value","message":"Ollama driver={} mode={} for {!r}","method":"info","path":"providers/impl/ollama.py","qualname":"OllamaProvider.stream_with_tools"}}
165
169
  {"class":"R-status","site":{"arg_index":"pos:1","expr":"driver.mode.value","message":"{} ({}) failed mid-stream (error_type={}); transparently retrying with {} ({})","method":"info","path":"providers/impl/ollama.py","qualname":"OllamaProvider.stream_with_tools"}}
@@ -222,6 +226,7 @@
222
226
  {"class":"R-name","site":{"arg_index":"pos:1","expr":"tc.name","message":"Tool call id reused by a different tool ({} then {}); calls would merge","method":"warning","path":"loops/chat_loop.py","qualname":"ChatLoop.run"}}
223
227
  {"class":"R-status","site":{"arg_index":"pos:1","expr":"think","message":"Ollama model {!r} rejected think={!r}; retrying with think=False","method":"info","path":"providers/impl/ollama.py","qualname":"OllamaProvider._stream_in_content"}}
224
228
  {"class":"R-status","site":{"arg_index":"pos:1","expr":"think","message":"Ollama model {!r} rejected think={!r}; retrying with think=False","method":"info","path":"providers/impl/ollama.py","qualname":"OllamaProvider._stream_native"}}
229
+ {"class":"R-class","site":{"arg_index":"pos:1","expr":"type(exc).__name__","message":"Built-in skills were not synced to {} ({}); reading the packaged copy","method":"warning","path":"core/skills.py","qualname":"_sync_builtin_skills"}}
225
230
  {"class":"R-class","site":{"arg_index":"pos:1","expr":"type(exc).__name__","message":"Provider retry budget exhausted after {} attempts; raising {}","method":"warning","path":"providers/retry.py","qualname":"RetryingProvider.stream_with_tools"}}
226
231
  {"class":"R-class","site":{"arg_index":"pos:1","expr":"type(exc).__name__","message":"Unreadable context slot at {}: {}","method":"warning","path":"tools/builtins/context.py","qualname":"_scan_context_dir"}}
227
232
  {"class":"R-name","site":{"arg_index":"pos:2","expr":"', '.join(discovered_modules)","message":"Auto-discovered {} tool definitions from {} modules: {}","method":"info","path":"tools/definition.py","qualname":"_discover_definitions"}}
@@ -1,9 +1,10 @@
1
1
  """AgentConfig — Pydantic model for agent definitions."""
2
2
 
3
- from typing import Any
3
+ from typing import Any, Literal
4
4
 
5
- from pydantic import BaseModel, ConfigDict, model_validator
5
+ from pydantic import BaseModel, ConfigDict, field_validator, model_validator
6
6
 
7
+ from ayder_cli.core.config import _coerce_skills_setting
7
8
  from ayder_cli.core.reasoning import ReasoningEffort, ThinkOption
8
9
 
9
10
 
@@ -32,6 +33,13 @@ class AgentConfig(BaseModel):
32
33
  max_output_tokens: int | None = None
33
34
  think: ThinkOption = None
34
35
  reasoning_effort: ReasoningEffort = None
36
+ # None: the provider profile's setting; "none" or a list: see Config.skills.
37
+ skills: list[str] | Literal["none"] | None = None
38
+
39
+ @field_validator("skills", mode="before")
40
+ @classmethod
41
+ def _validate_skills(cls, v: Any) -> Any:
42
+ return _coerce_skills_setting(v)
35
43
 
36
44
  @model_validator(mode="before")
37
45
  @classmethod
@@ -158,19 +158,40 @@ class ExecutionPolicy:
158
158
  *,
159
159
  pre_approved: bool = False,
160
160
  ) -> ExecutionResult:
161
- """Single shared execution path: validate → check permission → execute.
161
+ """Single shared execution path: canonicalize → validate → permission → execute.
162
162
 
163
163
  Both CLI and TUI route through this method so registry.execute is never
164
164
  called directly from interface-specific code.
165
165
 
166
+ Order:
167
+ 1. Arguments that are not a JSON object are a validation error.
168
+ 2. ``canonicalize_arguments``: aliases, integer and boolean coercion.
169
+ 3. ``ValidationAuthority`` on the canonical arguments.
170
+ 4. Permission check (unless pre-approved), then ``registry.execute``
171
+ with the canonical arguments.
172
+
166
173
  Args:
167
174
  pre_approved: If True, skip the permission check (user already
168
175
  confirmed via the confirmation dialog).
169
176
  """
170
177
  from ayder_cli.application.validation import ValidationAuthority
178
+ from ayder_cli.tools.normalization import canonicalize_arguments
179
+
180
+ if not isinstance(request.arguments, dict):
181
+ return ExecutionResult(
182
+ success=False,
183
+ error=ValidationError(
184
+ tool_name=request.name,
185
+ message=(
186
+ "arguments must be a JSON object, got "
187
+ f"{type(request.arguments).__name__}"
188
+ ),
189
+ ),
190
+ )
191
+ canonical = canonicalize_arguments(request.name, request.arguments)
171
192
 
172
193
  authority = ValidationAuthority()
173
- v_request = ToolRequest(name=request.name, arguments=request.arguments)
194
+ v_request = ToolRequest(name=request.name, arguments=canonical)
174
195
  valid, err = authority.validate(v_request)
175
196
  if not valid:
176
197
  return ExecutionResult(success=False, error=err)
@@ -180,7 +201,7 @@ class ExecutionPolicy:
180
201
  if perm_err is not None:
181
202
  return ExecutionResult(success=False, error=perm_err)
182
203
 
183
- raw = registry.execute(request.name, request.arguments)
204
+ raw = registry.execute(request.name, v_request.arguments)
184
205
  if isinstance(raw, ToolError):
185
206
  # A tool that ran and failed is NOT a successful execution.
186
207
  return ExecutionResult(success=False, was_confirmed=pre_approved, error=raw)
@@ -7,6 +7,7 @@ eliminating the duplicate `_build_services()` / `AyderApp.__init__()` wiring.
7
7
  from __future__ import annotations
8
8
 
9
9
  from dataclasses import dataclass, field
10
+ from pathlib import Path
10
11
  from typing import TYPE_CHECKING, Any
11
12
 
12
13
  from ayder_cli.core.config import Config, load_config, load_config_for_provider
@@ -16,13 +17,13 @@ from ayder_cli.core.context_manager_factory import context_manager_factory
16
17
  if TYPE_CHECKING:
17
18
  from ayder_cli.agents.config import AgentConfig
18
19
  from ayder_cli.core.context import ProjectContext
20
+ from ayder_cli.core.skills import build_scope
19
21
  from ayder_cli.providers import AIProvider, provider_orchestrator
20
22
  from ayder_cli.providers.retry import RetryConfig, RetryingProvider
21
23
  from ayder_cli.tools.registry import ToolRegistry, create_default_registry
22
24
  from ayder_cli.process_manager import ProcessManager
23
25
  from ayder_cli.prompts import (
24
26
  get_system_prompt,
25
- PROJECT_STRUCTURE_MACRO_TEMPLATE,
26
27
  )
27
28
  from ayder_cli.log import get_logger
28
29
 
@@ -42,6 +43,25 @@ class RuntimeComponents:
42
43
  context_manager: Any = field(default=None) # ContextManagerProtocol
43
44
 
44
45
 
46
+ def apply_skill_setting(
47
+ tool_registry: ToolRegistry,
48
+ project_ctx: ProjectContext,
49
+ setting: Any,
50
+ messages: list[dict] | None = None,
51
+ ) -> None:
52
+ """Scope the registry's skills; hide the skill tool for "none", show it otherwise.
53
+
54
+ Called at runtime construction and again on a provider switch, where
55
+ ``messages`` is the live history (ChatLoop binds it only once).
56
+ """
57
+ tool_registry.skill_scope = build_scope(Path(project_ctx.root), setting)
58
+ tool_registry.skill_scope.messages = messages
59
+ if setting == "none":
60
+ tool_registry.excluded_tools.add("skill")
61
+ else:
62
+ tool_registry.excluded_tools.discard("skill")
63
+
64
+
45
65
  def create_runtime(
46
66
  *,
47
67
  config: Config | None = None,
@@ -60,7 +80,7 @@ def create_runtime(
60
80
  (e.g. "AGENTIC" for `ayder-cli --agent`). See prompts.get_system_prompt.
61
81
  system_prompt_override: When set, use this text as the system-prompt base
62
82
  instead of prompts.get_system_prompt (ayder --system-prompt FILE). Tool
63
- prompts and the project-structure macro are still appended.
83
+ prompts are still appended.
64
84
 
65
85
  Returns:
66
86
  RuntimeComponents with all dependencies wired.
@@ -84,15 +104,7 @@ def create_runtime(
84
104
  process_manager=process_manager,
85
105
  context_manager=context_mgr,
86
106
  )
87
-
88
- try:
89
- structure = tool_registry.execute("get_project_structure", {"max_depth": 3})
90
- macro = PROJECT_STRUCTURE_MACRO_TEMPLATE.format(project_structure=structure)
91
- except Exception: # noqa: BLE001 - tool boundary: the project-structure macro is optional, so runtime construction continues without it
92
- logger.opt(exception=True).warning(
93
- "Project-structure macro unavailable; continuing without it"
94
- )
95
- macro = ""
107
+ apply_skill_setting(tool_registry, project_ctx, cfg.session_skills())
96
108
 
97
109
  # Format the final prompt (protocol injection now handled dynamically by chat_loop and protocols)
98
110
  if system_prompt_override is not None:
@@ -101,7 +113,7 @@ def create_runtime(
101
113
  base_prompt = get_system_prompt(cfg.prompt)
102
114
  tool_tags = frozenset(cfg.tool_tags) if cfg.tool_tags else None
103
115
  tool_prompts = tool_registry.get_system_prompts(tags=tool_tags)
104
- system_prompt = base_prompt + tool_prompts + macro
116
+ system_prompt = base_prompt + tool_prompts
105
117
 
106
118
  # Freeze system prompt after tool schemas are known.
107
119
  tool_schemas = tool_registry.get_schemas(tags=tool_tags)
@@ -196,6 +208,9 @@ def create_agent_runtime(
196
208
  process_manager=process_manager,
197
209
  context_manager=context_mgr,
198
210
  )
211
+ # An agent reads its own setting, then its provider profile's; never [app].
212
+ skills_setting = agent_config.skills if agent_config.skills is not None else cfg.profile_skills
213
+ apply_skill_setting(tool_registry, project_ctx, skills_setting)
199
214
 
200
215
  # 5. Build agent system prompt
201
216
  identity_prefix = (