ayder-cli 2.2.8__tar.gz → 2.3.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.
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/.gitignore +0 -1
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/PKG-INFO +34 -10
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/README.md +31 -7
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/docs/PROJECT_STRUCTURE.md +2 -1
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/docs/config.toml.example +17 -4
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/pyproject.toml +22 -3
- ayder_cli-2.3.0/scripts/check_exception_handlers.py +269 -0
- ayder_cli-2.3.0/scripts/check_log_arguments.py +1518 -0
- ayder_cli-2.3.0/scripts/check_logging_imports.py +111 -0
- ayder_cli-2.3.0/scripts/find_percent_logging.py +108 -0
- ayder_cli-2.3.0/scripts/log_argument_baseline.txt +279 -0
- ayder_cli-2.3.0/scripts/logging_gates.py +510 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/agents/callbacks.py +9 -9
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/agents/registry.py +24 -11
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/agents/runner.py +21 -16
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/agents/tool.py +0 -3
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/agents/worktree.py +7 -4
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/application/execution_policy.py +5 -1
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/application/runtime_factory.py +7 -1
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/cli.py +75 -21
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/cli_runner.py +24 -8
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/core/cache_monitor.py +8 -4
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/core/config.py +58 -18
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/core/config_migration.py +25 -2
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/core/default_context_manager.py +58 -7
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/core/ollama_context_manager.py +48 -19
- ayder_cli-2.3.0/src/ayder_cli/diagnostics.py +197 -0
- ayder_cli-2.3.0/src/ayder_cli/log.py +44 -0
- ayder_cli-2.3.0/src/ayder_cli/logging_config.py +596 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/loops/chat_loop.py +339 -252
- ayder_cli-2.3.0/src/ayder_cli/masking.py +85 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/process_manager.py +9 -3
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/prompts.py +3 -3
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/providers/impl/claude.py +9 -7
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/providers/impl/gemini.py +10 -7
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/providers/impl/ollama.py +26 -15
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/providers/impl/ollama_drivers/generic_xml.py +5 -4
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/providers/impl/ollama_drivers/registry.py +11 -9
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/providers/impl/ollama_inspector.py +4 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/providers/impl/openai.py +16 -11
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/providers/impl/qwen.py +44 -20
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/providers/retry.py +19 -14
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/tools/builtins/context.py +9 -6
- ayder_cli-2.3.0/src/ayder_cli/tools/builtins/filesystem.py +386 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/tools/builtins/notes.py +7 -5
- ayder_cli-2.3.0/src/ayder_cli/tools/builtins/search.py +533 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/tools/builtins/search_definitions.py +20 -8
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/tools/builtins/shell.py +4 -1
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/tools/builtins/skill.py +7 -4
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/tools/builtins/task_tool.py +4 -1
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/tools/builtins/tasks.py +15 -9
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/tools/builtins/utils_tools.py +7 -5
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/tools/builtins/web.py +4 -1
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/tools/definition.py +15 -11
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/tools/execution.py +8 -4
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/tools/hooks.py +12 -9
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/tools/plugin_github.py +5 -4
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/tools/plugin_manager.py +18 -14
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/tools/registry.py +5 -5
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/tools/utils.py +12 -11
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/tui/__init__.py +10 -1
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/tui/app.py +116 -30
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/tui/commands.py +51 -22
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/tui/screens.py +29 -4
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/tui/widgets.py +24 -7
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/ui.py +15 -3
- ayder_cli-2.3.0/tests/agents/test_agent_severity.py +106 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/agents/test_runner.py +61 -3
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/agents/test_worktree_helpers.py +29 -0
- ayder_cli-2.3.0/tests/application/test_execution_result_truthfulness.py +124 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/application/test_runtime_factory.py +27 -0
- ayder_cli-2.3.0/tests/client/test_main.py +132 -0
- ayder_cli-2.3.0/tests/conftest.py +103 -0
- ayder_cli-2.3.0/tests/core/test_asyncio_bridge.py +597 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/core/test_config_coverage.py +4 -4
- ayder_cli-2.3.0/tests/core/test_config_provider_logging.py +54 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/core/test_default_context_manager.py +107 -13
- ayder_cli-2.3.0/tests/core/test_event_emission.py +492 -0
- ayder_cli-2.3.0/tests/core/test_event_stream.py +62 -0
- ayder_cli-2.3.0/tests/core/test_exception_guarantee.py +26 -0
- ayder_cli-2.3.0/tests/core/test_exception_hooks.py +470 -0
- ayder_cli-2.3.0/tests/core/test_log_facade.py +63 -0
- ayder_cli-2.3.0/tests/core/test_logging_config_keys.py +70 -0
- ayder_cli-2.3.0/tests/core/test_logging_sinks.py +189 -0
- ayder_cli-2.3.0/tests/core/test_loguru_caplog.py +92 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/core/test_ollama_context_manager.py +5 -7
- ayder_cli-2.3.0/tests/core/test_session_id_threading.py +300 -0
- ayder_cli-2.3.0/tests/core/test_sink_masking.py +821 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/loops/test_chat_loop_tool_truncation.py +112 -3
- ayder_cli-2.3.0/tests/loops/test_loop_severity.py +130 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/providers/ollama_drivers/test_fallback.py +16 -2
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/providers/test_ollama_native.py +24 -0
- ayder_cli-2.3.0/tests/providers/test_provider_channel_binding.py +42 -0
- ayder_cli-2.3.0/tests/providers/test_provider_severity.py +101 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/providers/test_qwen_async.py +57 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/providers/test_retrying_provider.py +31 -4
- ayder_cli-2.3.0/tests/scripts/test_exception_gate.py +339 -0
- ayder_cli-2.3.0/tests/scripts/test_log_argument_gate.py +1747 -0
- ayder_cli-2.3.0/tests/scripts/test_logging_policy_gates.py +276 -0
- ayder_cli-2.3.0/tests/scripts/test_loguru_private_shape.py +304 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/test_cli.py +262 -15
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/test_context.py +49 -2
- ayder_cli-2.3.0/tests/test_logging_config.py +88 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/test_tasks.py +37 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/tools/test_bash_tool.py +10 -6
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/tools/test_definition_discovery.py +14 -3
- ayder_cli-2.3.0/tests/tools/test_file_editor_hardening.py +155 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/tools/test_impl.py +15 -7
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/tools/test_impl_coverage.py +4 -2
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/tools/test_plugin_manager.py +37 -6
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/tools/test_search_codebase.py +24 -2
- ayder_cli-2.3.0/tests/tools/test_search_hardening.py +381 -0
- ayder_cli-2.3.0/tests/tools/test_tool_severity.py +109 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/tools/test_utils.py +56 -3
- ayder_cli-2.3.0/tests/tui/__init__.py +1 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/tui/test_input_box.py +4 -1
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/ui/test_confirm_screen.py +2 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/ui/test_tui_commands_logging.py +37 -3
- ayder_cli-2.3.0/uv.lock +2443 -0
- ayder_cli-2.2.8/src/ayder_cli/logging_config.py +0 -115
- ayder_cli-2.2.8/src/ayder_cli/tools/builtins/filesystem.py +0 -306
- ayder_cli-2.2.8/src/ayder_cli/tools/builtins/search.py +0 -335
- ayder_cli-2.2.8/tests/client/test_main.py +0 -56
- ayder_cli-2.2.8/tests/core/test_config_provider_logging.py +0 -46
- ayder_cli-2.2.8/tests/test_logging_config.py +0 -82
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/.github/workflows/ci.yml +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/.github/workflows/python-package.yml +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/.python-version +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/AGENTS.md +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/LICENSE +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/docs/README_AGENT_WORKFLOW.md +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/docs/README_OLLAMA.md +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/docs/cc.png +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/docs/superpowers/plans/2026-06-26-file-editor-improvements.md +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/docs/superpowers/plans/2026-06-26-session-save-resume.md +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/docs/superpowers/specs/2026-06-26-file-editor-improvements-design.md +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/docs/superpowers/specs/2026-06-26-session-save-resume-design.md +0 -0
- /ayder_cli-2.2.8/src/ayder_cli/providers/impl/ollama_drivers/__init__.py → /ayder_cli-2.3.0/scripts/exception_baseline.txt +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/__init__.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/__main__.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/agents/__init__.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/agents/config.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/agents/run.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/application/README.md +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/application/__init__.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/application/message_contract.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/application/validation.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/cli_callbacks.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/console.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/core/context.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/core/context_manager.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/core/context_manager_factory.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/core/result.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/core/session.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/loops/__init__.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/parser.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/providers/__init__.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/providers/base.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/providers/impl/deepseek.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/providers/impl/glm.py +0 -0
- {ayder_cli-2.2.8/tests/providers → ayder_cli-2.3.0/src/ayder_cli/providers/impl}/ollama_drivers/__init__.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/providers/impl/ollama_drivers/_errors.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/providers/impl/ollama_drivers/base.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/providers/impl/ollama_drivers/deepseek.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/providers/impl/ollama_drivers/generic_native.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/providers/impl/ollama_drivers/matrix.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/providers/impl/ollama_drivers/minimax.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/providers/impl/ollama_drivers/qwen3.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/providers/orchestrator.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/services/__init__.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/services/interactions.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/themes/__init__.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/themes/ayder.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/themes/claude.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/tools/__init__.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/tools/builtins/__init__.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/tools/builtins/background_process_tool.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/tools/builtins/context_definitions.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/tools/builtins/filesystem_definitions.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/tools/builtins/note_tool.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/tools/builtins/notes_definitions.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/tools/builtins/process_manager_definitions.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/tools/builtins/shell_definitions.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/tools/builtins/skill_definitions.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/tools/builtins/task_definitions.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/tools/builtins/utils_tools_definitions.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/tools/builtins/web_definitions.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/tools/normalization.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/tools/plugin_api.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/tools/plugin_status.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/tools/schemas.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/tui/adapter.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/tui/helpers.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/tui/keybindings.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/tui/parser.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/tui/rendering.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/tui/theme_manager.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/tui/types.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/src/ayder_cli/version.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/COVERAGE.md +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/__init__.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/agents/__init__.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/agents/test_agent_consolidated.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/agents/test_agent_tools.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/agents/test_callbacks.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/agents/test_config.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/agents/test_integration.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/agents/test_queue.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/agents/test_registry.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/agents/test_run.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/agents/test_runner_provider_unavailable.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/agents/test_tool.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/agents/test_worktree_isolation.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/application/test_message_contract.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/convergence/__init__.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/convergence/test_execution_policy_parity.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/convergence/test_runtime_wiring.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/convergence/test_validation_path.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/core/test_cache_monitor.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/core/test_config.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/core/test_config_migration.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/core/test_config_retry.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/core/test_config_v2.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/core/test_context_manager_factory.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/core/test_default_context_manager_should_compact.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/core/test_ollama_context_manager_cache_monitor.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/core/test_ollama_context_manager_max_history.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/core/test_parameter_aliasing.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/core/test_parser.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/core/test_session.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/loops/__init__.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/loops/test_base.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/loops/test_chat_loop_hook.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/providers/__init__.py +0 -0
- {ayder_cli-2.2.8/tests/tui → ayder_cli-2.3.0/tests/providers/ollama_drivers}/__init__.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/providers/ollama_drivers/test_base.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/providers/ollama_drivers/test_deepseek.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/providers/ollama_drivers/test_errors.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/providers/ollama_drivers/test_generic_native.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/providers/ollama_drivers/test_generic_xml.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/providers/ollama_drivers/test_matrix.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/providers/ollama_drivers/test_minimax.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/providers/ollama_drivers/test_provider_integration.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/providers/ollama_drivers/test_qwen3.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/providers/ollama_drivers/test_registry.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/providers/test_glm_async.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/providers/test_ollama_inspector.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/providers/test_ollama_xml.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/providers/test_ollama_xml_autoroute.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/providers/test_ollama_xml_usage.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/providers/test_orchestrator_capabilities.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/providers/test_retry_classifier.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/providers/test_retry_integration.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/services/test_boundary.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/services/test_interaction_sink.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/test_cli_callbacks.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/test_env_manager.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/test_notes.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/test_process_manager.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/tools/__init__.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/tools/test_agent_notes.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/tools/test_background_process_tool.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/tools/test_di_injection.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/tools/test_note_tool.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/tools/test_notes_api.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/tools/test_path_security.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/tools/test_plugin_api.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/tools/test_plugin_github.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/tools/test_plugin_loading.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/tools/test_plugin_status.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/tools/test_registry.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/tools/test_registry_coverage.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/tools/test_result.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/tools/test_schemas.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/tools/test_skill_tool.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/tools/test_task_tool.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/tools/test_web.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/tui/test_agent_list_screen.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/tui/test_do_clear_coordination.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/tui/test_follow_mode.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/tui/test_help_screen.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/tui/test_keybindings.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/tui/test_multi_select_screen.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/tui/test_multiline_input.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/tui/test_paste.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/tui/test_pending_compact_consumer.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/tui/test_pull_delivery.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/tui/test_resume.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/tui/test_status_bar_badges.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/tui/test_themes.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/tui/test_turn_consumer.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/tui/test_widgets.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/ui/test_diff_preview.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/ui/test_plugin_autoenable.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/ui/test_provider_switch.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/ui/test_run_tui_startup.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/ui/test_tui_assistant_sanitization.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/ui/test_tui_chat_loop.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/ui/test_tui_commands_agent.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/ui/test_tui_commands_plugin.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/ui/test_tui_commands_provider.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/ui/test_tui_commands_skill.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/ui/test_tui_helpers.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/ui/test_tui_temporal_command.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/ui/test_tui_widgets.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/ui/test_ui.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/ui/test_ui_coverage.py +0 -0
- {ayder_cli-2.2.8 → ayder_cli-2.3.0}/tests/ui/test_ui_verbose.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: ayder-cli
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.3.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
|
|
@@ -15,7 +15,7 @@ Classifier: License :: OSI Approved :: MIT License
|
|
|
15
15
|
Classifier: Programming Language :: Python :: 3
|
|
16
16
|
Requires-Python: >=3.12
|
|
17
17
|
Requires-Dist: httpx
|
|
18
|
-
Requires-Dist: loguru
|
|
18
|
+
Requires-Dist: loguru<0.8,>=0.7
|
|
19
19
|
Requires-Dist: ollama>=0.6.2
|
|
20
20
|
Requires-Dist: openai
|
|
21
21
|
Requires-Dist: python-dotenv>=1.0.0
|
|
@@ -209,10 +209,23 @@ tool_tags = ["core", "metadata"] # Enabled tool tags (see /plugin)
|
|
|
209
209
|
agent_timeout = 300 # Seconds before a background agent is cancelled
|
|
210
210
|
|
|
211
211
|
[logging]
|
|
212
|
-
file_enabled
|
|
213
|
-
file_path
|
|
214
|
-
|
|
215
|
-
|
|
212
|
+
file_enabled = true # master switch for ALL file logging
|
|
213
|
+
file_path = ".ayder/log/ayder.log"
|
|
214
|
+
error_path = ".ayder/log/errors.log" # always written unless file_enabled = false
|
|
215
|
+
trace_enabled = false
|
|
216
|
+
trace_path = ".ayder/log/trace.jsonl"
|
|
217
|
+
rotation = "10 MB"
|
|
218
|
+
retention = "7 days"
|
|
219
|
+
# level = "INFO" # optional; omitted entirely when unset, which means NONE.
|
|
220
|
+
# NONE, ERROR, WARNING, INFO, DEBUG, TRACE
|
|
221
|
+
|
|
222
|
+
[logging.channels]
|
|
223
|
+
# Per-channel level overrides. Unlisted channels inherit the global level.
|
|
224
|
+
# Channels: llm, tool, agent, context, plugin, ui, core, external
|
|
225
|
+
# llm = "TRACE" # verbose LLM traffic
|
|
226
|
+
# plugin = "WARNING" # quiet plugin discovery
|
|
227
|
+
# external = "ERROR" # throttle third-party libraries
|
|
228
|
+
# ui = "NONE" # disable this channel entirely
|
|
216
229
|
|
|
217
230
|
[context_manager]
|
|
218
231
|
enabled = true
|
|
@@ -389,7 +402,7 @@ You can also manually manage context with the `context` tool and its slash-comma
|
|
|
389
402
|
| `/notes` | Browse and edit markdown notes |
|
|
390
403
|
| `/skill` | Activate a domain skill from `.ayder/skills/` via the shared `skill` tool backend |
|
|
391
404
|
| `/verbose` | Toggle verbose mode |
|
|
392
|
-
| `/logging` | Set log level for current session (NONE, ERROR, WARNING, INFO, DEBUG) |
|
|
405
|
+
| `/logging` | Set log level for current session (NONE, ERROR, WARNING, INFO, DEBUG, TRACE) |
|
|
393
406
|
| `/compact` | Summarize conversation, save a context snapshot, clear, and reload |
|
|
394
407
|
| `/save-context` | Summarize conversation and save it to a named context slot (no clear) |
|
|
395
408
|
| `/load-context` | Load a saved context slot and restore it |
|
|
@@ -406,9 +419,20 @@ You can also manually manage context with the `context` tool and its slash-comma
|
|
|
406
419
|
The LLM can also call `skill(action="list" | "load" | "unload")`
|
|
407
420
|
automatically when a request clearly matches an available project skill.
|
|
408
421
|
|
|
409
|
-
-
|
|
422
|
+
- Errors are always captured to `.ayder/log/errors.log`, even when
|
|
423
|
+
`logging_level` is `NONE`. Set `file_enabled = false` under `[logging]` to
|
|
424
|
+
turn off all file logging including errors.
|
|
425
|
+
- Unhandled exceptions, SIGTERM and SIGINT are captured to
|
|
426
|
+
`.ayder/log/errors.log`. Records in flight are lost on `SIGKILL` (`kill -9`),
|
|
427
|
+
which no process can intercept.
|
|
428
|
+
- Narrative logs go to `.ayder/log/ayder.log` when a level is set.
|
|
429
|
+
- `--trace` writes structured events to `.ayder/log/trace.jsonl`. It is not a
|
|
430
|
+
log level.
|
|
431
|
+
- `--log-channel llm,tool` limits console and narrative output to those
|
|
432
|
+
subsystems. It never filters the error log.
|
|
410
433
|
- TUI `/logging` changes are session-only and do not modify `config.toml`.
|
|
411
|
-
- CLI `--verbose
|
|
434
|
+
- CLI `--verbose` enables stdout logging for that run; it does not narrow
|
|
435
|
+
channels.
|
|
412
436
|
|
|
413
437
|
### Keyboard Shortcuts
|
|
414
438
|
|
|
@@ -166,10 +166,23 @@ tool_tags = ["core", "metadata"] # Enabled tool tags (see /plugin)
|
|
|
166
166
|
agent_timeout = 300 # Seconds before a background agent is cancelled
|
|
167
167
|
|
|
168
168
|
[logging]
|
|
169
|
-
file_enabled
|
|
170
|
-
file_path
|
|
171
|
-
|
|
172
|
-
|
|
169
|
+
file_enabled = true # master switch for ALL file logging
|
|
170
|
+
file_path = ".ayder/log/ayder.log"
|
|
171
|
+
error_path = ".ayder/log/errors.log" # always written unless file_enabled = false
|
|
172
|
+
trace_enabled = false
|
|
173
|
+
trace_path = ".ayder/log/trace.jsonl"
|
|
174
|
+
rotation = "10 MB"
|
|
175
|
+
retention = "7 days"
|
|
176
|
+
# level = "INFO" # optional; omitted entirely when unset, which means NONE.
|
|
177
|
+
# NONE, ERROR, WARNING, INFO, DEBUG, TRACE
|
|
178
|
+
|
|
179
|
+
[logging.channels]
|
|
180
|
+
# Per-channel level overrides. Unlisted channels inherit the global level.
|
|
181
|
+
# Channels: llm, tool, agent, context, plugin, ui, core, external
|
|
182
|
+
# llm = "TRACE" # verbose LLM traffic
|
|
183
|
+
# plugin = "WARNING" # quiet plugin discovery
|
|
184
|
+
# external = "ERROR" # throttle third-party libraries
|
|
185
|
+
# ui = "NONE" # disable this channel entirely
|
|
173
186
|
|
|
174
187
|
[context_manager]
|
|
175
188
|
enabled = true
|
|
@@ -346,7 +359,7 @@ You can also manually manage context with the `context` tool and its slash-comma
|
|
|
346
359
|
| `/notes` | Browse and edit markdown notes |
|
|
347
360
|
| `/skill` | Activate a domain skill from `.ayder/skills/` via the shared `skill` tool backend |
|
|
348
361
|
| `/verbose` | Toggle verbose mode |
|
|
349
|
-
| `/logging` | Set log level for current session (NONE, ERROR, WARNING, INFO, DEBUG) |
|
|
362
|
+
| `/logging` | Set log level for current session (NONE, ERROR, WARNING, INFO, DEBUG, TRACE) |
|
|
350
363
|
| `/compact` | Summarize conversation, save a context snapshot, clear, and reload |
|
|
351
364
|
| `/save-context` | Summarize conversation and save it to a named context slot (no clear) |
|
|
352
365
|
| `/load-context` | Load a saved context slot and restore it |
|
|
@@ -363,9 +376,20 @@ You can also manually manage context with the `context` tool and its slash-comma
|
|
|
363
376
|
The LLM can also call `skill(action="list" | "load" | "unload")`
|
|
364
377
|
automatically when a request clearly matches an available project skill.
|
|
365
378
|
|
|
366
|
-
-
|
|
379
|
+
- Errors are always captured to `.ayder/log/errors.log`, even when
|
|
380
|
+
`logging_level` is `NONE`. Set `file_enabled = false` under `[logging]` to
|
|
381
|
+
turn off all file logging including errors.
|
|
382
|
+
- Unhandled exceptions, SIGTERM and SIGINT are captured to
|
|
383
|
+
`.ayder/log/errors.log`. Records in flight are lost on `SIGKILL` (`kill -9`),
|
|
384
|
+
which no process can intercept.
|
|
385
|
+
- Narrative logs go to `.ayder/log/ayder.log` when a level is set.
|
|
386
|
+
- `--trace` writes structured events to `.ayder/log/trace.jsonl`. It is not a
|
|
387
|
+
log level.
|
|
388
|
+
- `--log-channel llm,tool` limits console and narrative output to those
|
|
389
|
+
subsystems. It never filters the error log.
|
|
367
390
|
- TUI `/logging` changes are session-only and do not modify `config.toml`.
|
|
368
|
-
- CLI `--verbose
|
|
391
|
+
- CLI `--verbose` enables stdout logging for that run; it does not narrow
|
|
392
|
+
channels.
|
|
369
393
|
|
|
370
394
|
### Keyboard Shortcuts
|
|
371
395
|
|
|
@@ -160,7 +160,8 @@ cli.py:main()
|
|
|
160
160
|
| `core/context_manager_factory.py` | Registry-based factory (OCP) | `ContextManagerFactory`, `context_manager_factory` |
|
|
161
161
|
| `core/cache_monitor.py` | Timing-based KV-cache hit detection | `CacheMonitor`, `CacheStatus`, `CacheSample` |
|
|
162
162
|
| `console.py` | Rich console singleton | `console` |
|
|
163
|
-
| `
|
|
163
|
+
| `log.py` | Channel-scoped loguru facade; leaf module so `logging_config.py` and `core/config.py` can both depend on it | `get_logger()`, `LOG_LEVELS`, `CHANNELS`, `level_no()`, `flush()` |
|
|
164
|
+
| `logging_config.py` | Logger setup (loguru + stdlib bridge) | `setup_logging()`, `LoggingSettings` |
|
|
164
165
|
| `version.py` | Package version constant | `__version__` |
|
|
165
166
|
|
|
166
167
|
### Root-Level Application Modules
|
|
@@ -117,10 +117,23 @@ theme = "ayder"
|
|
|
117
117
|
palette = "auto"
|
|
118
118
|
|
|
119
119
|
[logging]
|
|
120
|
-
file_enabled
|
|
121
|
-
file_path
|
|
122
|
-
|
|
123
|
-
|
|
120
|
+
file_enabled = true # master switch for ALL file logging
|
|
121
|
+
file_path = ".ayder/log/ayder.log"
|
|
122
|
+
error_path = ".ayder/log/errors.log" # always written unless file_enabled = false
|
|
123
|
+
trace_enabled = false
|
|
124
|
+
trace_path = ".ayder/log/trace.jsonl"
|
|
125
|
+
rotation = "10 MB"
|
|
126
|
+
retention = "7 days"
|
|
127
|
+
# level = "INFO" # optional; omitted entirely when unset, which means NONE.
|
|
128
|
+
# NONE, ERROR, WARNING, INFO, DEBUG, TRACE
|
|
129
|
+
|
|
130
|
+
[logging.channels]
|
|
131
|
+
# Per-channel level overrides. Unlisted channels inherit the global level.
|
|
132
|
+
# Channels: llm, tool, agent, context, plugin, ui, core, external
|
|
133
|
+
# llm = "TRACE" # verbose LLM traffic
|
|
134
|
+
# plugin = "WARNING" # quiet plugin discovery
|
|
135
|
+
# external = "ERROR" # throttle third-party libraries
|
|
136
|
+
# ui = "NONE" # disable this channel entirely
|
|
124
137
|
|
|
125
138
|
[context_manager]
|
|
126
139
|
enabled = true
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "ayder-cli"
|
|
7
|
-
version = "2.
|
|
7
|
+
version = "2.3.0"
|
|
8
8
|
description = "AI agent for any LLMs"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.12"
|
|
@@ -22,7 +22,9 @@ classifiers = [
|
|
|
22
22
|
dependencies = [
|
|
23
23
|
"openai",
|
|
24
24
|
"ollama>=0.6.2",
|
|
25
|
-
|
|
25
|
+
# Bounded deliberately: the masking sinks wrap Loguru's private FileSink /
|
|
26
|
+
# Message / StreamSink shapes (§F5-R6), guarded by `_shape_guard()`.
|
|
27
|
+
"loguru>=0.7,<0.8",
|
|
26
28
|
"httpx",
|
|
27
29
|
"rich>=13.0.0",
|
|
28
30
|
# Pinned exactly to match the runtime: 8.2.5+ replaced the single
|
|
@@ -85,7 +87,16 @@ lint = "ruff check src/"
|
|
|
85
87
|
lint-fix = "ruff check src/ --fix"
|
|
86
88
|
typecheck = "mypy src/"
|
|
87
89
|
format = "ruff format src/"
|
|
88
|
-
check-all = ["lint", "typecheck", "test"]
|
|
90
|
+
check-all = ["lint", "typecheck", "gates", "test"]
|
|
91
|
+
|
|
92
|
+
[tool.poe.tasks.gates]
|
|
93
|
+
help = "Run the logging AST gates"
|
|
94
|
+
sequence = [
|
|
95
|
+
{ cmd = "python scripts/find_percent_logging.py" },
|
|
96
|
+
{ cmd = "python scripts/check_logging_imports.py" },
|
|
97
|
+
{ cmd = "python scripts/check_exception_handlers.py" },
|
|
98
|
+
{ cmd = "python scripts/check_log_arguments.py" },
|
|
99
|
+
]
|
|
89
100
|
|
|
90
101
|
[tool.poe.tasks.clean]
|
|
91
102
|
help = "Deep clean cache and build artifacts"
|
|
@@ -113,3 +124,11 @@ ignore_missing_imports = false
|
|
|
113
124
|
[[tool.mypy.overrides]]
|
|
114
125
|
module = ["zhipuai", "dashscope"]
|
|
115
126
|
ignore_missing_imports = true
|
|
127
|
+
|
|
128
|
+
[tool.ruff.lint]
|
|
129
|
+
# Explicit rule set: ruff 0.16 widened its defaults from 59 to 413 rules,
|
|
130
|
+
# so an implicit default + extend-select drifts with the installed version.
|
|
131
|
+
select = ["E4", "E7", "E9", "F", "TRY400", "TRY401", "BLE001", "LOG", "G"]
|
|
132
|
+
|
|
133
|
+
[tool.ruff.lint.per-file-ignores]
|
|
134
|
+
"tests/**/*.py" = ["BLE001", "G004"]
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Gate: every broad exception handler must satisfy the logging policy.
|
|
3
|
+
|
|
4
|
+
Verdicts:
|
|
5
|
+
PASS - a reachable satisfying statement was found
|
|
6
|
+
FAIL - swallows silently, or exits without satisfying
|
|
7
|
+
REPORT - branches before satisfying; needs a human
|
|
8
|
+
"""
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import argparse
|
|
12
|
+
import ast
|
|
13
|
+
import pathlib
|
|
14
|
+
import sys
|
|
15
|
+
|
|
16
|
+
LEVELS = {"trace", "debug", "info", "success", "warning", "error",
|
|
17
|
+
"exception", "critical"}
|
|
18
|
+
BROAD = {"Exception", "BaseException"}
|
|
19
|
+
|
|
20
|
+
# Resolved from this file, not the cwd: scripts/<this>.py -> repo root.
|
|
21
|
+
REPO = pathlib.Path(__file__).resolve().parent.parent
|
|
22
|
+
SRC_ROOT = REPO / "src" / "ayder_cli"
|
|
23
|
+
DEFAULT_BASELINE = REPO / "scripts" / "exception_baseline.txt"
|
|
24
|
+
|
|
25
|
+
# Frozen census (§C10). A different number means the gate is looking at the
|
|
26
|
+
# wrong tree, or the census moved without the plan being updated.
|
|
27
|
+
EXPECTED_BROAD = 131
|
|
28
|
+
BRANCHING = (ast.If, ast.Try, ast.For, ast.While, ast.With, ast.Match)
|
|
29
|
+
TERMINAL = (ast.Return, ast.Break, ast.Continue, ast.Pass)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _is_broad(handler: ast.ExceptHandler) -> bool:
|
|
33
|
+
"""Bare `except:` is NOT in scope — see CONTRACTS C10."""
|
|
34
|
+
t = handler.type
|
|
35
|
+
if t is None:
|
|
36
|
+
return False
|
|
37
|
+
if isinstance(t, ast.Name):
|
|
38
|
+
return t.id in BROAD
|
|
39
|
+
if isinstance(t, ast.Tuple):
|
|
40
|
+
return any(isinstance(e, ast.Name) and e.id in BROAD for e in t.elts)
|
|
41
|
+
return False
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
LOGGERS = {"logger", "llm_log", "tool_log"}
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _approved_receiver(node: ast.expr) -> bool:
|
|
48
|
+
"""Only the §C8 bindings satisfy the policy.
|
|
49
|
+
|
|
50
|
+
Without this, any object's `.exception()`/`.opt()` would pass — a Future's
|
|
51
|
+
`.exception()` being the obvious trap.
|
|
52
|
+
"""
|
|
53
|
+
return isinstance(node, ast.Name) and node.id in LOGGERS
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _is_exception_log(node: ast.stmt) -> bool:
|
|
57
|
+
"""Either logger.opt(exception=True).<level>(...) or logger.exception(...)."""
|
|
58
|
+
if not isinstance(node, ast.Expr) or not isinstance(node.value, ast.Call):
|
|
59
|
+
return False
|
|
60
|
+
call = node.value
|
|
61
|
+
if not isinstance(call.func, ast.Attribute):
|
|
62
|
+
return False
|
|
63
|
+
|
|
64
|
+
# Form 2: logger.exception(...) — loguru shorthand, carries the exception.
|
|
65
|
+
# ruff TRY400 actively pushes code toward this, so it MUST be accepted —
|
|
66
|
+
# but only on a real logger. `future.exception()` must not satisfy policy.
|
|
67
|
+
if call.func.attr == "exception":
|
|
68
|
+
return _approved_receiver(call.func.value)
|
|
69
|
+
|
|
70
|
+
# Form 1: logger.opt(exception=True).<level>(...)
|
|
71
|
+
if call.func.attr not in LEVELS:
|
|
72
|
+
return False
|
|
73
|
+
recv = call.func.value
|
|
74
|
+
if not (isinstance(recv, ast.Call) and isinstance(recv.func, ast.Attribute)
|
|
75
|
+
and recv.func.attr == "opt"
|
|
76
|
+
and _approved_receiver(recv.func.value)):
|
|
77
|
+
return False
|
|
78
|
+
return any(kw.arg == "exception" and getattr(kw.value, "value", None) is True
|
|
79
|
+
for kw in recv.keywords)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _is_failure_return(node: ast.stmt) -> bool:
|
|
83
|
+
"""ToolError(...) or ExecutionResult(success=False, ...) — nothing else."""
|
|
84
|
+
if not isinstance(node, ast.Return) or node.value is None:
|
|
85
|
+
return False
|
|
86
|
+
v = node.value
|
|
87
|
+
if not isinstance(v, ast.Call):
|
|
88
|
+
return False
|
|
89
|
+
name = getattr(v.func, "id", None) or getattr(v.func, "attr", None)
|
|
90
|
+
|
|
91
|
+
if name == "ToolError":
|
|
92
|
+
return True
|
|
93
|
+
if name == "ExecutionResult":
|
|
94
|
+
# success=True is a SUCCESS return and satisfies nothing.
|
|
95
|
+
return any(kw.arg == "success" and getattr(kw.value, "value", None) is False
|
|
96
|
+
for kw in v.keywords)
|
|
97
|
+
return False
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def _satisfies(node: ast.stmt) -> bool:
|
|
101
|
+
return (isinstance(node, ast.Raise)
|
|
102
|
+
or _is_failure_return(node)
|
|
103
|
+
or _is_exception_log(node))
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _has_reasoned_marker(line: str) -> bool:
|
|
107
|
+
"""`# noqa: AYDER-EXC <reason>` — the reason text is mandatory.
|
|
108
|
+
|
|
109
|
+
A bare marker records no decision, so it must not suppress.
|
|
110
|
+
"""
|
|
111
|
+
idx = line.find("AYDER-EXC")
|
|
112
|
+
if idx == -1:
|
|
113
|
+
return False
|
|
114
|
+
return bool(line[idx + len("AYDER-EXC"):].strip(" :-\t"))
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def _verdict(handler: ast.ExceptHandler) -> str:
|
|
118
|
+
for stmt in handler.body:
|
|
119
|
+
if _satisfies(stmt):
|
|
120
|
+
return "PASS"
|
|
121
|
+
if isinstance(stmt, BRANCHING):
|
|
122
|
+
return "REPORT"
|
|
123
|
+
if isinstance(stmt, TERMINAL):
|
|
124
|
+
return "FAIL" # exits without satisfying
|
|
125
|
+
return "FAIL" # fell off the end
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _broad_handlers(path: str, tree: ast.AST) -> list[tuple[ast.ExceptHandler, str]]:
|
|
129
|
+
"""Yield (handler, stable_id), id = `path::qualname::ordinal`.
|
|
130
|
+
|
|
131
|
+
NEVER key a finding by line number. Step 3-02 inserts an import into 15
|
|
132
|
+
files and every handler below it shifts; each remediation shifts the rest
|
|
133
|
+
again. A line-keyed baseline reports every untouched handler as a new
|
|
134
|
+
finding on the very next commit. This id survives both — verified: 119
|
|
135
|
+
handlers, 119 unique ids, byte-identical after prepending imports and
|
|
136
|
+
after editing a handler body.
|
|
137
|
+
"""
|
|
138
|
+
out: list[tuple[ast.ExceptHandler, str]] = []
|
|
139
|
+
stack: list[str] = []
|
|
140
|
+
counter: dict[str, int] = {}
|
|
141
|
+
|
|
142
|
+
class _V(ast.NodeVisitor):
|
|
143
|
+
def _scope(self, node):
|
|
144
|
+
stack.append(node.name)
|
|
145
|
+
self.generic_visit(node)
|
|
146
|
+
stack.pop()
|
|
147
|
+
|
|
148
|
+
visit_FunctionDef = visit_AsyncFunctionDef = visit_ClassDef = _scope
|
|
149
|
+
|
|
150
|
+
def visit_Try(self, node):
|
|
151
|
+
for h in node.handlers:
|
|
152
|
+
if _is_broad(h):
|
|
153
|
+
key = f"{path}::{'.'.join(stack) or '<module>'}"
|
|
154
|
+
counter[key] = counter.get(key, 0) + 1
|
|
155
|
+
out.append((h, f"{key}::{counter[key]}"))
|
|
156
|
+
self.generic_visit(node)
|
|
157
|
+
|
|
158
|
+
# PEP 654 `except*` groups parse to a distinct node type (ast.TryStar,
|
|
159
|
+
# not ast.Try) with the same `.handlers` shape. Without this alias they
|
|
160
|
+
# are invisible to the census: generic_visit walks straight through
|
|
161
|
+
# into the ExceptHandler children and every `except* Exception:` is
|
|
162
|
+
# silently uncounted rather than silently failing — the gate would
|
|
163
|
+
# report success over a hole in its own coverage.
|
|
164
|
+
visit_TryStar = visit_Try
|
|
165
|
+
|
|
166
|
+
_V().visit(tree)
|
|
167
|
+
return out
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def main() -> int:
|
|
171
|
+
ap = argparse.ArgumentParser()
|
|
172
|
+
ap.add_argument("--path", default=str(SRC_ROOT))
|
|
173
|
+
ap.add_argument("--baseline", default=str(DEFAULT_BASELINE))
|
|
174
|
+
ap.add_argument("--write-baseline", action="store_true")
|
|
175
|
+
ap.add_argument("--expect-broad", type=int, default=None,
|
|
176
|
+
help="fail unless exactly this many broad handlers are found")
|
|
177
|
+
args = ap.parse_args()
|
|
178
|
+
|
|
179
|
+
root = pathlib.Path(args.path)
|
|
180
|
+
if not root.is_dir():
|
|
181
|
+
print(f"ABORT: source root does not exist: {root}", file=sys.stderr)
|
|
182
|
+
return 2
|
|
183
|
+
py_files = sorted(root.rglob("*.py"))
|
|
184
|
+
if not py_files:
|
|
185
|
+
print(f"ABORT: no Python files under {root}", file=sys.stderr)
|
|
186
|
+
return 2
|
|
187
|
+
|
|
188
|
+
findings: list = []
|
|
189
|
+
problems: list[str] = []
|
|
190
|
+
broad_total = 0
|
|
191
|
+
examined = 0
|
|
192
|
+
for p in py_files:
|
|
193
|
+
text = p.read_text()
|
|
194
|
+
lines = text.splitlines()
|
|
195
|
+
try:
|
|
196
|
+
tree = ast.parse(text, filename=str(p))
|
|
197
|
+
except SyntaxError as e:
|
|
198
|
+
# Never skip silently — an unparseable file is a hole in coverage.
|
|
199
|
+
problems.append(f"UNPARSEABLE {p}: {e}")
|
|
200
|
+
continue
|
|
201
|
+
examined += 1
|
|
202
|
+
try:
|
|
203
|
+
# REPOSITORY-relative, so ids read `src/ayder_cli/...` exactly as
|
|
204
|
+
# they did before the root was resolved absolutely. The per-directory
|
|
205
|
+
# steps grep on that prefix; changing it would silently break them.
|
|
206
|
+
rel = str(p.resolve().relative_to(REPO))
|
|
207
|
+
except ValueError:
|
|
208
|
+
rel = str(p)
|
|
209
|
+
for node, stable_id in _broad_handlers(rel, tree):
|
|
210
|
+
broad_total += 1
|
|
211
|
+
line = lines[node.lineno - 1] if node.lineno <= len(lines) else ""
|
|
212
|
+
if _has_reasoned_marker(line):
|
|
213
|
+
continue
|
|
214
|
+
v = _verdict(node)
|
|
215
|
+
if v != "PASS":
|
|
216
|
+
# The KEY is verdict + stable id, and nothing else. The line
|
|
217
|
+
# number is carried alongside for humans and is never compared.
|
|
218
|
+
findings.append((f"{v} {stable_id}", f"{rel}:{node.lineno}"))
|
|
219
|
+
|
|
220
|
+
keys = {k for k, _ in findings}
|
|
221
|
+
where = dict(findings)
|
|
222
|
+
|
|
223
|
+
# Coverage summary on EVERY run, before any verdict is printed.
|
|
224
|
+
print(f"files={examined}/{len(py_files)} broad={broad_total} "
|
|
225
|
+
f"findings={len(keys)} root={root}")
|
|
226
|
+
|
|
227
|
+
if problems:
|
|
228
|
+
for x in problems:
|
|
229
|
+
print(f" {x}")
|
|
230
|
+
print(f"FAIL — {len(problems)} unparseable file(s)")
|
|
231
|
+
return 1
|
|
232
|
+
|
|
233
|
+
expect = args.expect_broad
|
|
234
|
+
if expect is None and root.resolve() == SRC_ROOT.resolve():
|
|
235
|
+
expect = EXPECTED_BROAD # full-tree run: census is frozen
|
|
236
|
+
if expect is not None and broad_total != expect:
|
|
237
|
+
print(f"FAIL — census: found {broad_total} broad handlers, expected {expect}")
|
|
238
|
+
return 1
|
|
239
|
+
|
|
240
|
+
if args.write_baseline:
|
|
241
|
+
pathlib.Path(args.baseline).write_text("\n".join(sorted(keys)) + "\n")
|
|
242
|
+
print(f"baseline written: {len(keys)} findings")
|
|
243
|
+
return 0
|
|
244
|
+
|
|
245
|
+
for k in sorted(keys):
|
|
246
|
+
print(f"{k}\t# {where[k]}") # location is display only
|
|
247
|
+
|
|
248
|
+
baseline = pathlib.Path(args.baseline)
|
|
249
|
+
if not baseline.exists():
|
|
250
|
+
print(f"\n{len(keys)} findings (no baseline)")
|
|
251
|
+
return 0
|
|
252
|
+
|
|
253
|
+
# PER-FINDING comparison on the stable id. A count check would let one
|
|
254
|
+
# finding vanish while a new one appears and still pass.
|
|
255
|
+
allowed = {line.split("\t")[0].strip() for line in baseline.read_text().splitlines()
|
|
256
|
+
if line.strip()}
|
|
257
|
+
new = sorted(keys - allowed)
|
|
258
|
+
if new:
|
|
259
|
+
print(f"\n{len(new)} NEW finding(s) not in baseline:")
|
|
260
|
+
for n in new:
|
|
261
|
+
print(f" {n}\t# {where[n]}")
|
|
262
|
+
return 1
|
|
263
|
+
fixed = len(allowed) - len(keys & allowed)
|
|
264
|
+
print(f"\n{len(keys)} findings, 0 new, {fixed} fixed since baseline")
|
|
265
|
+
return 0
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
if __name__ == "__main__":
|
|
269
|
+
sys.exit(main())
|