aru-code 0.57.0__tar.gz → 0.59.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.
- {aru_code-0.57.0/aru_code.egg-info → aru_code-0.59.0}/PKG-INFO +10 -7
- {aru_code-0.57.0 → aru_code-0.59.0}/README.md +9 -6
- aru_code-0.59.0/aru/__init__.py +1 -0
- aru_code-0.59.0/aru/auth.py +93 -0
- aru_code-0.59.0/aru/cli.py +307 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/commands.py +385 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/display.py +0 -53
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/providers.py +84 -1
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/tui/app.py +87 -6
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/tui/screens/text_input.py +3 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/tui/ui.py +2 -1
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/tui/widgets/completer.py +1 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/ui.py +13 -2
- {aru_code-0.57.0 → aru_code-0.59.0/aru_code.egg-info}/PKG-INFO +10 -7
- {aru_code-0.57.0 → aru_code-0.59.0}/aru_code.egg-info/SOURCES.txt +4 -3
- {aru_code-0.57.0 → aru_code-0.59.0}/pyproject.toml +1 -1
- aru_code-0.59.0/tests/test_auth_store.py +143 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_cli.py +0 -190
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_cli_advanced.py +2 -127
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_cli_session.py +1 -32
- aru_code-0.59.0/tests/test_connect_command.py +256 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_main.py +0 -12
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_tui_app_boot.py +4 -39
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_tui_chat_adversarial.py +125 -73
- aru_code-0.59.0/tests/test_tui_connect_wiring.py +42 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_tui_permission_flow.py +6 -1
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_worktree_session_restore.py +1 -1
- aru_code-0.57.0/aru/__init__.py +0 -1
- aru_code-0.57.0/aru/cli.py +0 -1151
- aru_code-0.57.0/aru/completers.py +0 -383
- aru_code-0.57.0/tests/test_cli_completers.py +0 -690
- aru_code-0.57.0/tests/test_cli_run_cli.py +0 -1
- {aru_code-0.57.0 → aru_code-0.59.0}/LICENSE +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/_debug/__init__.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/_debug/analyze_trace.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/_debug/loop_tracer.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/agent_factory.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/agents/__init__.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/agents/base.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/agents/catalog.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/agents/planner.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/cache_patch.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/checkpoints.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/config.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/context.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/doom_loop.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/events.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/format/__init__.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/format/manager.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/format/runner.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/history_blocks.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/lsp/__init__.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/lsp/client.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/lsp/manager.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/lsp/protocol.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/memory/__init__.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/memory/extractor.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/memory/loader.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/memory/store.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/permissions.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/plugin_cache.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/plugins/__init__.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/plugins/custom_tools.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/plugins/hooks.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/plugins/manager.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/plugins/tool_api.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/runner.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/runtime.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/select.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/session.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/sinks.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/streaming.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/tool_policy.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/tools/__init__.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/tools/_diff.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/tools/_shared.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/tools/apply_patch.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/tools/apply_patch_prompt.txt +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/tools/ast_tools.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/tools/codebase.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/tools/delegate.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/tools/delegate_prompt.txt +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/tools/file_ops.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/tools/gitignore.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/tools/lsp.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/tools/mcp_client.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/tools/memory_tool.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/tools/plan_mode.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/tools/ranker.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/tools/registry.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/tools/search.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/tools/shell.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/tools/skill.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/tools/tasklist.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/tools/web.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/tools/worktree.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/tui/__init__.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/tui/log_bridge.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/tui/notifications.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/tui/sanitize.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/tui/screens/__init__.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/tui/screens/choice.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/tui/screens/confirm.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/tui/screens/keymap.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/tui/screens/search.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/tui/screens/session_picker.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/tui/sinks.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/tui/slash_bridge.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/tui/themes.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/tui/widgets/__init__.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/tui/widgets/chat.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/tui/widgets/context_pane.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/tui/widgets/file_link.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/tui/widgets/header.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/tui/widgets/inline_choice.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/tui/widgets/loaded_pane.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/tui/widgets/prompt_area.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/tui/widgets/prompt_queue.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/tui/widgets/status.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/tui/widgets/subagent_panel.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/tui/widgets/tasklist_panel.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/tui/widgets/thinking.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru/tui/widgets/tools.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru_code.egg-info/dependency_links.txt +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru_code.egg-info/entry_points.txt +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru_code.egg-info/requires.txt +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/aru_code.egg-info/top_level.txt +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/setup.cfg +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_agents_base.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_agents_md_coverage.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_apply_patch.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_async_tool_permission.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_cache_patch_metrics.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_cache_patch_stop_reason.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_catalog.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_chat_scrollable.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_checkpoints.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_cli_base.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_cli_new.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_cli_shell.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_codebase.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_confabulation_regression.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_config.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_context.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_context_pane.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_cwd_awareness.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_delegate.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_doom_loop.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_events_backward_compat.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_events_schema.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_fork_ctx_concurrency.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_format.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_gitignore.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_guardrails_scenarios.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_invoke_skill.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_invoked_skills.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_loaded_pane_path.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_lsp.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_lsp_rename.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_markdown_to_text.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_mcp_client.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_mcp_health.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_memory.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_memory_tool.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_microcompact.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_permission_timeout_suspension.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_permissions.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_plan_mode_refactor.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_plugin_cache.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_plugin_errors.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_plugin_hooks_v2.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_plugins.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_providers.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_ranker.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_reasoning.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_runner_interrupt.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_runner_recovery.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_runtime.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_select.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_session_free_cost.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_skill_disallowed_tools.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_status_breakdown.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_status_cost.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_streaming_sink.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_subagent_tool_events.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_tasklist.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_thread_tool_timeout.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_tool_policy.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_truncation_marker.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_tui_bindings.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_tui_bus_flow.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_tui_chat.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_tui_completer.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_tui_completer_dynamic.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_tui_copy.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_tui_error_display.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_tui_file_link.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_tui_input_behaviour.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_tui_layer12_recovery.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_tui_layer13_recovery.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_tui_mention_expand.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_tui_modals.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_tui_mode_cycle.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_tui_native_selection.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_tui_plan_task_render.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_tui_prompt_queue.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_tui_shell_bang.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_tui_sidebar_toggle.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_tui_slash_bridge.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_tui_slash_model.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_tui_snapshot_smoke.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_tui_subagent_panel.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_tui_theme.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_tui_thinking_and_boot.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_tui_widgets_visual.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_ui_adapter.py +0 -0
- {aru_code-0.57.0 → aru_code-0.59.0}/tests/test_worktree.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: aru-code
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.59.0
|
|
4
4
|
Summary: A Claude Code clone built with Agno agents
|
|
5
5
|
Author-email: Estevao <estevaofon@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -116,16 +116,19 @@ Key bindings (TUI):
|
|
|
116
116
|
Local slash commands inside the TUI: `/help`, `/clear`, `/quit`,
|
|
117
117
|
`/plan`. Any other input is sent to the agent.
|
|
118
118
|
|
|
119
|
-
###
|
|
119
|
+
### Non-interactive (one-shot)
|
|
120
120
|
|
|
121
|
-
|
|
121
|
+
For scripting and pipes, pass a prompt directly instead of launching the
|
|
122
|
+
TUI:
|
|
122
123
|
|
|
123
124
|
```bash
|
|
124
|
-
aru
|
|
125
|
+
aru "fix the failing test in auth" # one-shot, with tools
|
|
126
|
+
aru --print "explain this module" # text-only, no tools
|
|
127
|
+
echo "summarize TODOs" | aru # piped stdin
|
|
125
128
|
```
|
|
126
129
|
|
|
127
|
-
|
|
128
|
-
only presentation differs.
|
|
130
|
+
One-shot mode shares the same sessions, plugins, permissions, and tools
|
|
131
|
+
as the TUI — only presentation differs.
|
|
129
132
|
|
|
130
133
|
## Usage
|
|
131
134
|
|
|
@@ -706,7 +709,7 @@ Once a plan is stored, every following turn prepends a `<system-reminder>` listi
|
|
|
706
709
|
```
|
|
707
710
|
aru-code/
|
|
708
711
|
├── aru/
|
|
709
|
-
│ ├── cli.py #
|
|
712
|
+
│ ├── cli.py # Argument parsing, one-shot execution, and entry point
|
|
710
713
|
│ ├── agent_factory.py # Single factory — builds Agno Agents from catalog specs
|
|
711
714
|
│ ├── commands.py # Slash commands, help display, shell execution
|
|
712
715
|
│ ├── completers.py # Input completions, paste detection, @file mentions
|
|
@@ -68,16 +68,19 @@ Key bindings (TUI):
|
|
|
68
68
|
Local slash commands inside the TUI: `/help`, `/clear`, `/quit`,
|
|
69
69
|
`/plan`. Any other input is sent to the agent.
|
|
70
70
|
|
|
71
|
-
###
|
|
71
|
+
### Non-interactive (one-shot)
|
|
72
72
|
|
|
73
|
-
|
|
73
|
+
For scripting and pipes, pass a prompt directly instead of launching the
|
|
74
|
+
TUI:
|
|
74
75
|
|
|
75
76
|
```bash
|
|
76
|
-
aru
|
|
77
|
+
aru "fix the failing test in auth" # one-shot, with tools
|
|
78
|
+
aru --print "explain this module" # text-only, no tools
|
|
79
|
+
echo "summarize TODOs" | aru # piped stdin
|
|
77
80
|
```
|
|
78
81
|
|
|
79
|
-
|
|
80
|
-
only presentation differs.
|
|
82
|
+
One-shot mode shares the same sessions, plugins, permissions, and tools
|
|
83
|
+
as the TUI — only presentation differs.
|
|
81
84
|
|
|
82
85
|
## Usage
|
|
83
86
|
|
|
@@ -658,7 +661,7 @@ Once a plan is stored, every following turn prepends a `<system-reminder>` listi
|
|
|
658
661
|
```
|
|
659
662
|
aru-code/
|
|
660
663
|
├── aru/
|
|
661
|
-
│ ├── cli.py #
|
|
664
|
+
│ ├── cli.py # Argument parsing, one-shot execution, and entry point
|
|
662
665
|
│ ├── agent_factory.py # Single factory — builds Agno Agents from catalog specs
|
|
663
666
|
│ ├── commands.py # Slash commands, help display, shell execution
|
|
664
667
|
│ ├── completers.py # Input completions, paste detection, @file mentions
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.59.0"
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"""Credential store for provider API keys (OpenCode-parity ``auth.json``).
|
|
2
|
+
|
|
3
|
+
``/connect`` writes here so users no longer have to hand-edit ``aru.json``
|
|
4
|
+
to wire up a provider. Mirrors OpenCode's ``auth.json``: a flat
|
|
5
|
+
``{ "<provider>": <info> }`` map persisted under the user's home, written
|
|
6
|
+
with ``0600`` permissions so the key isn't world-readable.
|
|
7
|
+
|
|
8
|
+
Schema (``info``) — a tagged union on ``type`` so future auth methods
|
|
9
|
+
(OAuth, well-known) can slot in beside the current API-key path:
|
|
10
|
+
|
|
11
|
+
{"type": "api", "key": "sk-...", # built-in provider
|
|
12
|
+
"base_url": "...", "name": "...", # extra fields for a
|
|
13
|
+
"provider_type": "openai", "default_model": "...", # custom provider
|
|
14
|
+
"context_limit": 128000}
|
|
15
|
+
{"type": "local", "base_url": "http://..."} # keyless (e.g. Ollama)
|
|
16
|
+
|
|
17
|
+
Consumption lives in :func:`aru.providers.apply_stored_credentials`, which
|
|
18
|
+
layers these onto the in-memory provider registry at startup (and again
|
|
19
|
+
right after ``/connect``) so a stored key takes precedence over the
|
|
20
|
+
provider's ``api_key_env``.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
from __future__ import annotations
|
|
24
|
+
|
|
25
|
+
import json
|
|
26
|
+
import logging
|
|
27
|
+
import os
|
|
28
|
+
from pathlib import Path
|
|
29
|
+
from typing import Any
|
|
30
|
+
|
|
31
|
+
logger = logging.getLogger("aru.auth")
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def auth_path() -> Path:
|
|
35
|
+
"""Absolute path to the credential file (``~/.aru/auth.json``)."""
|
|
36
|
+
return Path.home() / ".aru" / "auth.json"
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def load_auth() -> dict[str, dict[str, Any]]:
|
|
40
|
+
"""Return the full credential map, or ``{}`` when missing/unreadable."""
|
|
41
|
+
path = auth_path()
|
|
42
|
+
if not path.is_file():
|
|
43
|
+
return {}
|
|
44
|
+
try:
|
|
45
|
+
data = json.loads(path.read_text(encoding="utf-8"))
|
|
46
|
+
except (OSError, UnicodeDecodeError, json.JSONDecodeError) as exc:
|
|
47
|
+
logger.warning("Failed to read %s: %s", path, exc)
|
|
48
|
+
return {}
|
|
49
|
+
if not isinstance(data, dict):
|
|
50
|
+
return {}
|
|
51
|
+
# Drop any malformed (non-dict) entries defensively.
|
|
52
|
+
return {k: v for k, v in data.items() if isinstance(v, dict)}
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def get_credential(provider_key: str) -> dict[str, Any] | None:
|
|
56
|
+
"""Return the stored credential for ``provider_key`` or ``None``."""
|
|
57
|
+
return load_auth().get(provider_key)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _write_auth(data: dict[str, dict[str, Any]]) -> None:
|
|
61
|
+
"""Write the credential map atomically-ish with ``0600`` perms."""
|
|
62
|
+
path = auth_path()
|
|
63
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
64
|
+
tmp = path.with_suffix(".json.tmp")
|
|
65
|
+
tmp.write_text(json.dumps(data, indent=2), encoding="utf-8")
|
|
66
|
+
# Lock down before the rename so the secret is never briefly group/other
|
|
67
|
+
# readable. chmod is a partial no-op on Windows but harmless.
|
|
68
|
+
try:
|
|
69
|
+
os.chmod(tmp, 0o600)
|
|
70
|
+
except OSError:
|
|
71
|
+
pass
|
|
72
|
+
os.replace(tmp, path)
|
|
73
|
+
try:
|
|
74
|
+
os.chmod(path, 0o600)
|
|
75
|
+
except OSError:
|
|
76
|
+
pass
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def set_credential(provider_key: str, info: dict[str, Any]) -> None:
|
|
80
|
+
"""Store (or replace) the credential for ``provider_key``."""
|
|
81
|
+
data = load_auth()
|
|
82
|
+
data[provider_key] = info
|
|
83
|
+
_write_auth(data)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def remove_credential(provider_key: str) -> bool:
|
|
87
|
+
"""Delete the credential for ``provider_key``. Returns ``True`` if removed."""
|
|
88
|
+
data = load_auth()
|
|
89
|
+
if provider_key not in data:
|
|
90
|
+
return False
|
|
91
|
+
del data[provider_key]
|
|
92
|
+
_write_auth(data)
|
|
93
|
+
return True
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
"""Non-interactive CLI for aru - a Claude Code clone.
|
|
2
|
+
|
|
3
|
+
This module is the slim orchestrator: arg parsing, one-shot execution, and the
|
|
4
|
+
entrypoint that launches the Textual TUI. All domain logic lives in dedicated
|
|
5
|
+
modules; public names are re-exported here for backward compatibility.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import asyncio
|
|
11
|
+
import io as _io
|
|
12
|
+
import logging as _logging
|
|
13
|
+
import os
|
|
14
|
+
import sys
|
|
15
|
+
|
|
16
|
+
# ── Re-exports for backward compatibility ─────────────────────────────
|
|
17
|
+
# Tests and external code import these from aru.cli; keep them accessible.
|
|
18
|
+
|
|
19
|
+
from aru.session import ( # noqa: F401
|
|
20
|
+
DEFAULT_MODEL,
|
|
21
|
+
PlanStep,
|
|
22
|
+
Session,
|
|
23
|
+
SessionStore,
|
|
24
|
+
SESSIONS_DIR,
|
|
25
|
+
_generate_session_id,
|
|
26
|
+
parse_plan_steps,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
from aru.display import ( # noqa: F401
|
|
30
|
+
StatusBar,
|
|
31
|
+
StreamingDisplay,
|
|
32
|
+
ToolTracker,
|
|
33
|
+
THINKING_PHRASES,
|
|
34
|
+
TOOL_DISPLAY_NAMES,
|
|
35
|
+
TOOL_PRIMARY_ARG,
|
|
36
|
+
_build_logo_with_shadow,
|
|
37
|
+
_format_tool_label,
|
|
38
|
+
aru_logo,
|
|
39
|
+
console,
|
|
40
|
+
format_duration,
|
|
41
|
+
neon_green,
|
|
42
|
+
shadow_green,
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
from aru.commands import ( # noqa: F401
|
|
46
|
+
SLASH_COMMANDS,
|
|
47
|
+
_show_help,
|
|
48
|
+
ask_yes_no,
|
|
49
|
+
run_shell,
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
from aru.runner import ( # noqa: F401
|
|
53
|
+
AgentRunResult,
|
|
54
|
+
_MUTATION_TOOLS,
|
|
55
|
+
build_env_context,
|
|
56
|
+
run_agent_capture,
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
from aru.agent_factory import ( # noqa: F401
|
|
60
|
+
create_custom_agent_instance,
|
|
61
|
+
create_general_agent,
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
# ── Platform setup ─────────────────────────────────────────────────────
|
|
65
|
+
|
|
66
|
+
if sys.platform == "win32" and not hasattr(sys, "_called_from_test"):
|
|
67
|
+
sys.stdout = _io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8")
|
|
68
|
+
sys.stderr = _io.TextIOWrapper(sys.stderr.buffer, encoding="utf-8")
|
|
69
|
+
|
|
70
|
+
_logging.getLogger("agno").setLevel(_logging.WARNING)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _restore_worktree_from_session(session) -> str:
|
|
74
|
+
"""Re-enter the worktree persisted on *session* (Tier 3 #2 R11).
|
|
75
|
+
|
|
76
|
+
Called at the top of ``run_tui`` right after the session is bound to
|
|
77
|
+
the runtime context. Three branches:
|
|
78
|
+
|
|
79
|
+
- ``worktree_path`` empty or missing → no-op, returns ``"none"``.
|
|
80
|
+
- ``worktree_path`` set AND directory exists on disk → ``enter_worktree``
|
|
81
|
+
is invoked so ``ctx.cwd`` matches the previous run. Returns
|
|
82
|
+
``"entered"``.
|
|
83
|
+
- ``worktree_path`` set but directory is gone → warn, null the
|
|
84
|
+
session fields, return ``"stale"``.
|
|
85
|
+
|
|
86
|
+
Returns a short outcome label (``"none"``/``"entered"``/``"stale"``/
|
|
87
|
+
``"error"``) so tests can pin the branch chosen without re-parsing
|
|
88
|
+
console output.
|
|
89
|
+
"""
|
|
90
|
+
saved_wt = getattr(session, "worktree_path", None)
|
|
91
|
+
saved_branch = getattr(session, "worktree_branch", None)
|
|
92
|
+
if not saved_wt:
|
|
93
|
+
return "none"
|
|
94
|
+
if os.path.isdir(saved_wt):
|
|
95
|
+
from aru.runtime import enter_worktree as _re_enter
|
|
96
|
+
try:
|
|
97
|
+
_re_enter(saved_wt, saved_branch)
|
|
98
|
+
console.print(
|
|
99
|
+
f"[dim]Resumed inside worktree: {saved_branch} ({saved_wt})[/dim]"
|
|
100
|
+
)
|
|
101
|
+
return "entered"
|
|
102
|
+
except Exception as exc:
|
|
103
|
+
console.print(
|
|
104
|
+
f"[yellow]Could not re-enter worktree {saved_wt}: {exc}[/yellow]"
|
|
105
|
+
)
|
|
106
|
+
return "error"
|
|
107
|
+
console.print(
|
|
108
|
+
f"[yellow]Saved worktree path no longer exists: {saved_wt} — "
|
|
109
|
+
f"using project root[/yellow]"
|
|
110
|
+
)
|
|
111
|
+
session.worktree_path = None
|
|
112
|
+
session.worktree_branch = None
|
|
113
|
+
return "stale"
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def _configure_plugin_logger(verbose: bool = False) -> None:
|
|
117
|
+
"""Attach a stderr handler to the ``aru.plugins`` logger.
|
|
118
|
+
|
|
119
|
+
Without this, ``logger.error(...)`` inside ``PluginManager`` (e.g. when a
|
|
120
|
+
subscriber raises) has no handler and disappears silently. Idempotent —
|
|
121
|
+
a marker attribute on the logger prevents double-registration when
|
|
122
|
+
``run_oneshot`` is invoked multiple times in the same process (tests).
|
|
123
|
+
"""
|
|
124
|
+
lg = _logging.getLogger("aru.plugins")
|
|
125
|
+
if getattr(lg, "_aru_handler_attached", False):
|
|
126
|
+
return
|
|
127
|
+
handler = _logging.StreamHandler(sys.stderr)
|
|
128
|
+
handler.setFormatter(
|
|
129
|
+
_logging.Formatter("[aru.plugins] %(levelname)s: %(message)s")
|
|
130
|
+
)
|
|
131
|
+
lg.addHandler(handler)
|
|
132
|
+
lg.setLevel(_logging.DEBUG if verbose else _logging.WARNING)
|
|
133
|
+
lg._aru_handler_attached = True # type: ignore[attr-defined]
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
# ── CLI entrypoint ─────────────────────────────────────────────────────
|
|
137
|
+
|
|
138
|
+
def _list_sessions_and_exit():
|
|
139
|
+
"""Print saved sessions and exit."""
|
|
140
|
+
store = SessionStore()
|
|
141
|
+
sessions = store.list_sessions()
|
|
142
|
+
if not sessions:
|
|
143
|
+
console.print("[dim]No saved sessions.[/dim]")
|
|
144
|
+
return
|
|
145
|
+
console.print("[bold]Recent sessions:[/bold]\n")
|
|
146
|
+
for s in sessions:
|
|
147
|
+
sid = s["session_id"]
|
|
148
|
+
title = s["title"][:50]
|
|
149
|
+
msgs = s["messages"]
|
|
150
|
+
updated = s["updated_at"]
|
|
151
|
+
model = s["model"]
|
|
152
|
+
console.print(f" [bold cyan]{sid}[/bold cyan] {title} [dim]({msgs} msgs, {model}, {updated})[/dim]")
|
|
153
|
+
console.print(f"\n[dim]Resume with: aru --resume <id>[/dim]")
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
async def run_oneshot(prompt: str, print_only: bool = False, skip_permissions: bool = False):
|
|
157
|
+
"""Run a single prompt non-interactively and exit.
|
|
158
|
+
|
|
159
|
+
Args:
|
|
160
|
+
prompt: The user prompt to execute.
|
|
161
|
+
print_only: If True, run without tools (text-only response).
|
|
162
|
+
skip_permissions: If True, skip all permission checks.
|
|
163
|
+
"""
|
|
164
|
+
from aru.runtime import init_ctx
|
|
165
|
+
from aru.config import load_config
|
|
166
|
+
from aru.cache_patch import apply_cache_patch
|
|
167
|
+
|
|
168
|
+
apply_cache_patch()
|
|
169
|
+
_configure_plugin_logger(verbose=bool(os.environ.get("ARU_VERBOSE")))
|
|
170
|
+
ctx = init_ctx(console=console, skip_permissions=skip_permissions)
|
|
171
|
+
|
|
172
|
+
# E6a: install REPL UI so migrated call sites work in one-shot mode.
|
|
173
|
+
from aru.ui import install_repl_ui_on_ctx
|
|
174
|
+
install_repl_ui_on_ctx(ctx)
|
|
175
|
+
|
|
176
|
+
config = load_config()
|
|
177
|
+
ctx.config = config
|
|
178
|
+
# Populate invoke_skill's dynamic docstring (same as interactive path)
|
|
179
|
+
from aru.tools.skill import _update_invoke_skill_docstring
|
|
180
|
+
_update_invoke_skill_docstring(config.skills)
|
|
181
|
+
session = Session()
|
|
182
|
+
if config.default_model:
|
|
183
|
+
session.model_ref = config.default_model
|
|
184
|
+
|
|
185
|
+
ctx.session = session
|
|
186
|
+
ctx.model_id = session.model_id
|
|
187
|
+
small_ref = config.model_aliases.get("small") if config else None
|
|
188
|
+
if not small_ref:
|
|
189
|
+
from aru.providers import resolve_model_ref
|
|
190
|
+
provider_key, _ = resolve_model_ref(session.model_ref)
|
|
191
|
+
_small_defaults = {
|
|
192
|
+
"anthropic": "anthropic/claude-haiku-4-5",
|
|
193
|
+
"openai": "openai/gpt-4o-mini",
|
|
194
|
+
"groq": "groq/llama-3.1-8b-instant",
|
|
195
|
+
"deepseek": "deepseek/deepseek-chat",
|
|
196
|
+
"ollama": "ollama/llama3.1",
|
|
197
|
+
}
|
|
198
|
+
small_ref = _small_defaults.get(provider_key, session.model_ref)
|
|
199
|
+
ctx.small_model_ref = small_ref
|
|
200
|
+
|
|
201
|
+
extra_instructions = config.get_extra_instructions()
|
|
202
|
+
|
|
203
|
+
if print_only:
|
|
204
|
+
# Text-only mode: no tools, just a direct LLM call
|
|
205
|
+
from agno.agent import Agent
|
|
206
|
+
from aru.providers import create_model
|
|
207
|
+
from aru.agents.base import build_instructions
|
|
208
|
+
|
|
209
|
+
agent = Agent(
|
|
210
|
+
name="Aru",
|
|
211
|
+
model=create_model(session.model_ref, reasoning_override=session.reasoning_override),
|
|
212
|
+
tools=[],
|
|
213
|
+
instructions=build_instructions("general", extra_instructions),
|
|
214
|
+
markdown=True,
|
|
215
|
+
)
|
|
216
|
+
response = await agent.arun(prompt)
|
|
217
|
+
if response and response.content:
|
|
218
|
+
# Print raw text to stdout for piping
|
|
219
|
+
print(response.content)
|
|
220
|
+
else:
|
|
221
|
+
# Full mode with tools
|
|
222
|
+
from aru.runner import build_env_context
|
|
223
|
+
env_ctx = build_env_context(session)
|
|
224
|
+
agent = await create_general_agent(session, config, env_context=env_ctx)
|
|
225
|
+
session.add_message("user", prompt)
|
|
226
|
+
await run_agent_capture(agent, prompt, session)
|
|
227
|
+
|
|
228
|
+
if session.token_summary:
|
|
229
|
+
console.print(f"[dim]{session.token_summary}[/dim]")
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def main():
|
|
233
|
+
"""Entry point for the aru CLI."""
|
|
234
|
+
from dotenv import load_dotenv
|
|
235
|
+
|
|
236
|
+
load_dotenv()
|
|
237
|
+
args = sys.argv[1:]
|
|
238
|
+
skip_permissions = "--dangerously-skip-permissions" in args
|
|
239
|
+
print_only = "--print" in args or "-p" in args
|
|
240
|
+
|
|
241
|
+
if "--list" in args:
|
|
242
|
+
_list_sessions_and_exit()
|
|
243
|
+
return
|
|
244
|
+
|
|
245
|
+
resume_id = None
|
|
246
|
+
if "--resume" in args:
|
|
247
|
+
idx = args.index("--resume")
|
|
248
|
+
if idx + 1 < len(args) and not args[idx + 1].startswith("--"):
|
|
249
|
+
resume_id = args[idx + 1]
|
|
250
|
+
else:
|
|
251
|
+
resume_id = "last"
|
|
252
|
+
|
|
253
|
+
# Collect positional arguments (non-flag, non-flag-value)
|
|
254
|
+
flags_with_value = {"--resume"}
|
|
255
|
+
positional = []
|
|
256
|
+
skip_next = False
|
|
257
|
+
for i, arg in enumerate(args):
|
|
258
|
+
if skip_next:
|
|
259
|
+
skip_next = False
|
|
260
|
+
continue
|
|
261
|
+
if arg.startswith("--") or arg.startswith("-"):
|
|
262
|
+
if arg in flags_with_value:
|
|
263
|
+
skip_next = True
|
|
264
|
+
continue
|
|
265
|
+
positional.append(arg)
|
|
266
|
+
|
|
267
|
+
# Piped stdin: echo "fix bug" | aru
|
|
268
|
+
if not sys.stdin.isatty() and not positional:
|
|
269
|
+
piped_input = sys.stdin.read().strip()
|
|
270
|
+
if piped_input:
|
|
271
|
+
positional = [piped_input]
|
|
272
|
+
|
|
273
|
+
# One-shot mode: aru "fix the bug" or aru --print "explain this"
|
|
274
|
+
if positional:
|
|
275
|
+
prompt = " ".join(positional)
|
|
276
|
+
try:
|
|
277
|
+
asyncio.run(run_oneshot(prompt, print_only=print_only, skip_permissions=skip_permissions))
|
|
278
|
+
except (KeyboardInterrupt, asyncio.CancelledError, SystemExit):
|
|
279
|
+
pass
|
|
280
|
+
except Exception as e:
|
|
281
|
+
from rich.markup import escape
|
|
282
|
+
console.print(f"\n[bold red]Fatal error: {escape(str(e))}[/bold red]")
|
|
283
|
+
return
|
|
284
|
+
|
|
285
|
+
# Interactive mode — the Textual TUI is the only interactive interface.
|
|
286
|
+
# `--repl` / `--tui` are still accepted as no-op flags for backwards compat.
|
|
287
|
+
from aru.tui import run_tui
|
|
288
|
+
try:
|
|
289
|
+
asyncio.run(run_tui(skip_permissions=skip_permissions, resume_id=resume_id))
|
|
290
|
+
except (KeyboardInterrupt, asyncio.CancelledError, SystemExit):
|
|
291
|
+
_graceful_exit()
|
|
292
|
+
except Exception as e:
|
|
293
|
+
from rich.markup import escape
|
|
294
|
+
console.print(f"\n[bold red]Fatal error: {escape(str(e))}[/bold red]")
|
|
295
|
+
_graceful_exit()
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
def _graceful_exit():
|
|
299
|
+
"""Save session and show resume hint on exit."""
|
|
300
|
+
try:
|
|
301
|
+
store = SessionStore()
|
|
302
|
+
last = store.load_last()
|
|
303
|
+
if last:
|
|
304
|
+
console.print(f"\n[dim]Session saved: {last.session_id}[/dim]")
|
|
305
|
+
console.print(f"[dim]Resume with:[/dim] [bold cyan]aru --resume {last.session_id}[/bold cyan]")
|
|
306
|
+
except Exception:
|
|
307
|
+
pass
|