acra-cli 0.1.2__tar.gz → 0.1.4__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.
- {acra_cli-0.1.2/acra_cli.egg-info → acra_cli-0.1.4}/PKG-INFO +1 -1
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/__init__.py +1 -1
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/cli.py +41 -5
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/commands/__init__.py +2 -0
- acra_cli-0.1.4/acra/commands/ask.py +106 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/commands/research.py +16 -4
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/commands/serve.py +10 -2
- acra_cli-0.1.4/acra/ui/__pycache__/animated_banner.cpython-312.pyc +0 -0
- acra_cli-0.1.4/acra/ui/__pycache__/banner.cpython-312.pyc +0 -0
- acra_cli-0.1.4/acra/ui/__pycache__/shell.cpython-312.pyc +0 -0
- acra_cli-0.1.4/acra/ui/animated_banner.py +287 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/ui/banner.py +3 -11
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/ui/shell.py +4 -2
- {acra_cli-0.1.2 → acra_cli-0.1.4/acra_cli.egg-info}/PKG-INFO +1 -1
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra_cli.egg-info/SOURCES.txt +4 -1
- {acra_cli-0.1.2 → acra_cli-0.1.4}/pyproject.toml +1 -1
- acra_cli-0.1.4/tests/test_cli.py +91 -0
- acra_cli-0.1.4/tests/test_interactive_routing.py +53 -0
- acra_cli-0.1.2/acra/commands/ask.py +0 -49
- acra_cli-0.1.2/acra/ui/__pycache__/banner.cpython-312.pyc +0 -0
- acra_cli-0.1.2/acra/ui/__pycache__/shell.cpython-312.pyc +0 -0
- acra_cli-0.1.2/tests/test_cli.py +0 -36
- {acra_cli-0.1.2 → acra_cli-0.1.4}/LICENSE +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/MANIFEST.in +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/README.md +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/__main__.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/agents/__init__.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/agents/__pycache__/__init__.cpython-312.pyc +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/agents/__pycache__/llm.cpython-312.pyc +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/agents/__pycache__/llm_utils.cpython-312.pyc +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/agents/coder/__init__.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/agents/coder/__pycache__/__init__.cpython-312.pyc +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/agents/coder/__pycache__/coder_agent.cpython-312.pyc +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/agents/coder/__pycache__/coder_chain.cpython-312.pyc +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/agents/coder/coder_agent.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/agents/coder/coder_chain.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/agents/critic/__init__.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/agents/critic/__pycache__/__init__.cpython-312.pyc +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/agents/critic/__pycache__/critic_agent.cpython-312.pyc +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/agents/critic/__pycache__/critic_chain.cpython-312.pyc +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/agents/critic/critic_agent.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/agents/critic/critic_chain.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/agents/executor/__init__.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/agents/executor/__pycache__/__init__.cpython-312.pyc +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/agents/executor/__pycache__/docker_runner.cpython-312.pyc +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/agents/executor/__pycache__/executor_agent.cpython-312.pyc +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/agents/executor/__pycache__/sandbox_runner.cpython-312.pyc +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/agents/executor/docker_runner.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/agents/executor/executor_agent.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/agents/executor/sandbox_runner.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/agents/human/__init__.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/agents/human/__pycache__/__init__.cpython-312.pyc +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/agents/human/__pycache__/human_node.cpython-312.pyc +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/agents/human/human_node.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/agents/llm.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/agents/llm_utils.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/agents/memory/__init__.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/agents/memory/__pycache__/__init__.cpython-312.pyc +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/agents/memory/__pycache__/memory_agent.cpython-312.pyc +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/agents/memory/__pycache__/memory_manager.cpython-312.pyc +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/agents/memory/__pycache__/retrieval.cpython-312.pyc +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/agents/memory/memory_agent.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/agents/memory/memory_manager.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/agents/memory/retrieval.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/agents/planner/__init__.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/agents/planner/__pycache__/__init__.cpython-312.pyc +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/agents/planner/__pycache__/planner_agent.cpython-312.pyc +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/agents/planner/__pycache__/planner_chain.cpython-312.pyc +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/agents/planner/planner_agent.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/agents/planner/planner_chain.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/agents/researcher/__init__.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/agents/researcher/__pycache__/__init__.cpython-312.pyc +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/agents/researcher/__pycache__/researcher_agent.cpython-312.pyc +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/agents/researcher/__pycache__/researcher_chain.cpython-312.pyc +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/agents/researcher/researcher_agent.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/agents/researcher/researcher_chain.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/brain/__init__.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/brain/model_registry.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/brain/provider_manager.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/commands/brain.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/commands/config.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/commands/context.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/commands/graph.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/commands/keys.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/commands/logs.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/commands/memory.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/commands/plugin.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/commands/session.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/commands/utils.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/config/__init__.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/config/__pycache__/__init__.cpython-312.pyc +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/config/__pycache__/defaults.cpython-312.pyc +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/config/__pycache__/profile_manager.cpython-312.pyc +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/config/defaults.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/config/profile_manager.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/execution/__init__.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/execution/logs/__init__.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/execution/logs/error_logs/__init__.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/execution/logs/execution_logs/__init__.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/execution/sandbox/__init__.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/execution/sandbox/docker_executor.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/execution/sandbox/isolated_runner.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/execution/sandbox/sandbox_manager.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/graph/__init__.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/graph/checkpoint.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/graph/conditional_edges.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/graph/edges.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/graph/graph_visualizer.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/graph/nodes.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/graph/router.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/graph/state.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/graph/workflow.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/memory/__init__.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/memory/checkpoints/__init__.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/memory/checkpoints/data/__init__.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/memory/checkpoints/postgres_checkpoint.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/memory/checkpoints/sqlite_checkpoint.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/memory/chroma_db/__init__.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/memory/conversation_memory/__init__.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/memory/conversation_memory/long_term.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/memory/conversation_memory/short_term.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/memory/storage/__init__.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/memory/vector_store/__init__.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/memory/vector_store/chroma_store.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/memory/vector_store/embeddings.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/observability/__init__.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/observability/langsmith_tracing.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/observability/metrics.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/observability/monitoring.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/observability/token_tracking.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/prompts/__init__.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/schemas/__init__.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/schemas/__pycache__/__init__.cpython-312.pyc +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/schemas/__pycache__/coder_schema.cpython-312.pyc +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/schemas/__pycache__/critic_schema.cpython-312.pyc +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/schemas/__pycache__/execution_schema.cpython-312.pyc +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/schemas/__pycache__/planner_schema.cpython-312.pyc +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/schemas/__pycache__/researcher_schema.cpython-312.pyc +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/schemas/__pycache__/shared_schema.cpython-312.pyc +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/schemas/coder_schema.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/schemas/critic_schema.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/schemas/execution_schema.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/schemas/planner_schema.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/schemas/researcher_schema.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/schemas/shared_schema.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/tools/__init__.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/tools/code_tools/__init__.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/tools/code_tools/file_reader.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/tools/code_tools/file_writer.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/tools/code_tools/python_repl.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/tools/code_tools/terminal_runner.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/tools/github_tools/__init__.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/tools/github_tools/commit_generator.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/tools/github_tools/github_search.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/tools/github_tools/repo_analyzer.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/tools/integration_test_helper.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/tools/validation_tools/__init__.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/tools/validation_tools/dependency_checker.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/tools/validation_tools/environment_config_validator.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/tools/validation_tools/http_validator.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/tools/validation_tools/response_validator.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/tools/validation_tools/security_checker.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/tools/validation_tools/syntax_checker.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/tools/validation_tools/web_framework_validator.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/tools/web_tools/__init__.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/tools/web_tools/arxiv_search.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/tools/web_tools/serpapi_search.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/tools/web_tools/tavily_search.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/ui/__init__.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/ui/__pycache__/__init__.cpython-312.pyc +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/ui/__pycache__/components.cpython-312.pyc +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/ui/__pycache__/theme.cpython-312.pyc +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/ui/components.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/ui/theme.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/utils/__init__.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/utils/context_manager.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/utils/keyring_manager.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/utils/output_formatter.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra/utils/session_manager.py +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra_cli.egg-info/dependency_links.txt +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra_cli.egg-info/entry_points.txt +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra_cli.egg-info/requires.txt +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/acra_cli.egg-info/top_level.txt +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/requirements.txt +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/setup.cfg +0 -0
- {acra_cli-0.1.2 → acra_cli-0.1.4}/setup.py +0 -0
|
@@ -2,11 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
import typer
|
|
4
4
|
|
|
5
|
+
import acra
|
|
6
|
+
from acra.commands.ask import ask, build, explain, fix, review, run
|
|
7
|
+
from acra.commands.research import research
|
|
5
8
|
from acra.commands import (
|
|
6
9
|
serve_app,
|
|
7
10
|
config_app,
|
|
8
11
|
keys_app,
|
|
9
|
-
|
|
12
|
+
brain_app,
|
|
13
|
+
context_app,
|
|
14
|
+
logs_app,
|
|
15
|
+
plugin_app,
|
|
10
16
|
memory_app,
|
|
11
17
|
session_app,
|
|
12
18
|
graph_app,
|
|
@@ -19,11 +25,28 @@ app = typer.Typer(add_completion=True)
|
|
|
19
25
|
app.add_typer(serve_app, name="serve")
|
|
20
26
|
app.add_typer(config_app, name="config")
|
|
21
27
|
app.add_typer(keys_app, name="keys")
|
|
22
|
-
app.add_typer(research_app, name="research")
|
|
23
28
|
app.add_typer(memory_app, name="memory")
|
|
24
29
|
app.add_typer(session_app, name="session")
|
|
25
30
|
app.add_typer(graph_app, name="graph")
|
|
26
31
|
app.add_typer(utils_app, name="workspace")
|
|
32
|
+
app.add_typer(brain_app, name="brain", help="Brain management commands for acra.")
|
|
33
|
+
app.add_typer(context_app, name="context", help="Context management commands for acra.")
|
|
34
|
+
app.add_typer(logs_app, name="logs", help="Logging commands for acra.")
|
|
35
|
+
app.add_typer(plugin_app, name="plugin", help="Plugin commands for acra.")
|
|
36
|
+
|
|
37
|
+
app.command("ask")(ask)
|
|
38
|
+
app.command("build")(build)
|
|
39
|
+
app.command("fix")(fix)
|
|
40
|
+
app.command("review")(review)
|
|
41
|
+
app.command("explain")(explain)
|
|
42
|
+
app.command("run")(run)
|
|
43
|
+
app.command("research")(research)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _version_callback(value: bool):
|
|
47
|
+
if value:
|
|
48
|
+
typer.echo(acra.__version__)
|
|
49
|
+
raise typer.Exit()
|
|
27
50
|
|
|
28
51
|
|
|
29
52
|
def app_main() -> None:
|
|
@@ -34,19 +57,32 @@ def app_main() -> None:
|
|
|
34
57
|
@app.callback(invoke_without_command=True)
|
|
35
58
|
def main(
|
|
36
59
|
ctx: typer.Context,
|
|
60
|
+
version: bool = typer.Option(
|
|
61
|
+
False,
|
|
62
|
+
"--version",
|
|
63
|
+
callback=_version_callback,
|
|
64
|
+
is_eager=True,
|
|
65
|
+
help="Show the installed acra version and exit.",
|
|
66
|
+
),
|
|
37
67
|
profile: str = typer.Option(None, "--profile", help="Profile to use."),
|
|
38
68
|
workspace: str = typer.Option(None, "--workspace", help="Override workspace path."),
|
|
39
|
-
|
|
69
|
+
use_memory: bool = typer.Option(True, "--memory/--no-memory", help="Use memory."),
|
|
40
70
|
dry_run: bool = typer.Option(False, "--dry-run", help="Display planned actions without executing."),
|
|
41
71
|
json_output: bool = typer.Option(False, "--json", help="Output JSON-formatted results."),
|
|
42
72
|
verbose: bool = typer.Option(False, "--verbose", "-v", help="Enable verbose logging."),
|
|
43
73
|
quiet: bool = typer.Option(False, "--quiet", "-q", help="Suppress output."),
|
|
44
74
|
timeout: int = typer.Option(0, "--timeout", help="Override execution timeout in seconds."),
|
|
75
|
+
party: bool = typer.Option(
|
|
76
|
+
False,
|
|
77
|
+
"--party",
|
|
78
|
+
"--easter-egg",
|
|
79
|
+
help="Show the rainbow party-mode startup banner.",
|
|
80
|
+
),
|
|
45
81
|
):
|
|
46
82
|
"""acra: Agentic CLI for LLM powered task execution and research workflows."""
|
|
47
83
|
if ctx.invoked_subcommand is None:
|
|
48
|
-
launch_shell(profile=profile, workspace=workspace)
|
|
84
|
+
launch_shell(profile=profile, workspace=workspace, party=party)
|
|
49
85
|
|
|
50
86
|
|
|
51
87
|
if __name__ == "__main__":
|
|
52
|
-
app_main()
|
|
88
|
+
app_main()
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""acra command modules."""
|
|
2
2
|
|
|
3
3
|
from .serve import app as serve_app
|
|
4
|
+
from .ask import app as ask_app
|
|
4
5
|
from .config import app as config_app
|
|
5
6
|
from .brain import app as brain_app
|
|
6
7
|
from .research import app as research_app
|
|
@@ -15,6 +16,7 @@ from .utils import app as utils_app
|
|
|
15
16
|
|
|
16
17
|
__all__ = [
|
|
17
18
|
"serve_app",
|
|
19
|
+
"ask_app",
|
|
18
20
|
"config_app",
|
|
19
21
|
"brain_app",
|
|
20
22
|
"research_app",
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"""Agent task commands for acra."""
|
|
2
|
+
|
|
3
|
+
import uuid
|
|
4
|
+
from typing import Optional
|
|
5
|
+
|
|
6
|
+
import typer
|
|
7
|
+
|
|
8
|
+
from acra.graph.workflow import OmniAgentCallbacks, omniagent_graph
|
|
9
|
+
from acra.ui.components import render_panel
|
|
10
|
+
|
|
11
|
+
app = typer.Typer(help="Agent task commands for acra.")
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _run_task(
|
|
15
|
+
task: str,
|
|
16
|
+
task_label: str,
|
|
17
|
+
profile: Optional[str] = None,
|
|
18
|
+
use_memory: bool = True,
|
|
19
|
+
interactive: bool = False,
|
|
20
|
+
):
|
|
21
|
+
state = {
|
|
22
|
+
"user_request": task,
|
|
23
|
+
"task_label": task_label,
|
|
24
|
+
"no_memory": not use_memory,
|
|
25
|
+
"profile": profile,
|
|
26
|
+
"interactive": interactive,
|
|
27
|
+
}
|
|
28
|
+
# The compiled graph always carries a checkpointer, which requires a
|
|
29
|
+
# thread_id on every invoke call regardless of whether the caller wants
|
|
30
|
+
# persistence. A fresh id is generated per invocation; when --no-memory
|
|
31
|
+
# is set, nothing is done with it beyond satisfying the checkpointer, so
|
|
32
|
+
# no state is meaningfully persisted or reused across runs.
|
|
33
|
+
thread_id = str(uuid.uuid4())
|
|
34
|
+
config = {
|
|
35
|
+
"callbacks": [OmniAgentCallbacks()],
|
|
36
|
+
"configurable": {"thread_id": thread_id},
|
|
37
|
+
}
|
|
38
|
+
result = omniagent_graph.invoke(state, config=config)
|
|
39
|
+
render_panel(result, title=task_label)
|
|
40
|
+
return result
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@app.command()
|
|
44
|
+
def ask(
|
|
45
|
+
task: str = typer.Argument(..., help="Task or question for the agent."),
|
|
46
|
+
profile: Optional[str] = typer.Option(None, help="Profile to use."),
|
|
47
|
+
use_memory: bool = typer.Option(True, "--memory/--no-memory", help="Use memory for this task."),
|
|
48
|
+
interactive: bool = typer.Option(False, "--interactive", help="Route agent approval requests to the human node."),
|
|
49
|
+
):
|
|
50
|
+
"""Ask the assistant a question."""
|
|
51
|
+
return _run_task(task, "ask", profile, use_memory, interactive)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@app.command()
|
|
55
|
+
def build(
|
|
56
|
+
task: str = typer.Argument(..., help="Build task for the agent."),
|
|
57
|
+
profile: Optional[str] = typer.Option(None, help="Profile to use."),
|
|
58
|
+
use_memory: bool = typer.Option(True, "--memory/--no-memory", help="Use memory for this task."),
|
|
59
|
+
interactive: bool = typer.Option(False, "--interactive", help="Route agent approval requests to the human node."),
|
|
60
|
+
):
|
|
61
|
+
"""Run a build workflow for a project task."""
|
|
62
|
+
return _run_task(task, "build", profile, use_memory, interactive)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@app.command()
|
|
66
|
+
def fix(
|
|
67
|
+
task: str = typer.Argument(..., help="Code issue or bug for the agent to fix."),
|
|
68
|
+
profile: Optional[str] = typer.Option(None, help="Profile to use."),
|
|
69
|
+
use_memory: bool = typer.Option(True, "--memory/--no-memory", help="Use memory for this task."),
|
|
70
|
+
interactive: bool = typer.Option(False, "--interactive", help="Route agent approval requests to the human node."),
|
|
71
|
+
):
|
|
72
|
+
"""Run a fix workflow for a code issue."""
|
|
73
|
+
return _run_task(task, "fix", profile, use_memory, interactive)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
@app.command()
|
|
77
|
+
def review(
|
|
78
|
+
task: str = typer.Argument(..., help="Code, design, or task to review."),
|
|
79
|
+
profile: Optional[str] = typer.Option(None, help="Profile to use."),
|
|
80
|
+
use_memory: bool = typer.Option(True, "--memory/--no-memory", help="Use memory for this task."),
|
|
81
|
+
interactive: bool = typer.Option(False, "--interactive", help="Route agent approval requests to the human node."),
|
|
82
|
+
):
|
|
83
|
+
"""Review code or design with the critic pipeline."""
|
|
84
|
+
return _run_task(task, "review", profile, use_memory, interactive)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
@app.command()
|
|
88
|
+
def explain(
|
|
89
|
+
task: str = typer.Argument(..., help="Concept or code path to explain."),
|
|
90
|
+
profile: Optional[str] = typer.Option(None, help="Profile to use."),
|
|
91
|
+
use_memory: bool = typer.Option(True, "--memory/--no-memory", help="Use memory for this task."),
|
|
92
|
+
interactive: bool = typer.Option(False, "--interactive", help="Route agent approval requests to the human node."),
|
|
93
|
+
):
|
|
94
|
+
"""Explain a technical concept or code path."""
|
|
95
|
+
return _run_task(task, "explain", profile, use_memory, interactive)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
@app.command()
|
|
99
|
+
def run(
|
|
100
|
+
task: str = typer.Argument(..., help="Task for the agent to execute."),
|
|
101
|
+
profile: Optional[str] = typer.Option(None, help="Profile to use."),
|
|
102
|
+
use_memory: bool = typer.Option(True, "--memory/--no-memory", help="Use memory for this task."),
|
|
103
|
+
interactive: bool = typer.Option(False, "--interactive", help="Route agent approval requests to the human node."),
|
|
104
|
+
):
|
|
105
|
+
"""Execute a task using the executor agent."""
|
|
106
|
+
return _run_task(task, "run", profile, use_memory, interactive)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Research command module for acra."""
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
|
+
import uuid
|
|
4
5
|
import typer
|
|
5
6
|
from typing import List, Optional
|
|
6
7
|
|
|
@@ -27,7 +28,11 @@ def research(
|
|
|
27
28
|
output: Optional[str] = typer.Option(None, help="Write research output to a file."),
|
|
28
29
|
save: bool = typer.Option(False, help="Persist research output into memory."),
|
|
29
30
|
follow_up: bool = typer.Option(False, help="Keep the session open for follow-up questions."),
|
|
30
|
-
|
|
31
|
+
use_memory: bool = typer.Option(
|
|
32
|
+
True,
|
|
33
|
+
"--memory/--no-memory",
|
|
34
|
+
help="Use memory persistence for this research.",
|
|
35
|
+
),
|
|
31
36
|
profile: Optional[str] = typer.Option(None, help="Profile to use."),
|
|
32
37
|
json_output: bool = typer.Option(False, "--json", help="Output JSON instead of rich text."),
|
|
33
38
|
verbose: bool = typer.Option(False, "--verbose", "-v", help="Show detailed debug output."),
|
|
@@ -45,11 +50,18 @@ def research(
|
|
|
45
50
|
"research_depth": depth,
|
|
46
51
|
"research_sources": source_list,
|
|
47
52
|
"follow_up": follow_up,
|
|
48
|
-
"save_to_memory": save and
|
|
53
|
+
"save_to_memory": save and use_memory,
|
|
49
54
|
"profile": profile,
|
|
50
55
|
}
|
|
51
56
|
|
|
52
|
-
|
|
57
|
+
thread_id = str(uuid.uuid4())
|
|
58
|
+
result = workflow.invoke(
|
|
59
|
+
state,
|
|
60
|
+
config={
|
|
61
|
+
"callbacks": [OmniAgentCallbacks()],
|
|
62
|
+
"configurable": {"thread_id": thread_id},
|
|
63
|
+
},
|
|
64
|
+
)
|
|
53
65
|
|
|
54
66
|
report = format_research_report(result, output_format=format)
|
|
55
67
|
|
|
@@ -62,4 +74,4 @@ def research(
|
|
|
62
74
|
|
|
63
75
|
render_panel(report, title="Research Results")
|
|
64
76
|
if json_output:
|
|
65
|
-
typer.echo(json.dumps(report, indent=2))
|
|
77
|
+
typer.echo(json.dumps(report, indent=2))
|
|
@@ -7,7 +7,15 @@ app = typer.Typer(help="Interactive shell for acra.", invoke_without_command=Tru
|
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
@app.callback()
|
|
10
|
-
def serve(
|
|
10
|
+
def serve(
|
|
11
|
+
ctx: typer.Context,
|
|
12
|
+
party: bool = typer.Option(
|
|
13
|
+
False,
|
|
14
|
+
"--party",
|
|
15
|
+
"--easter-egg",
|
|
16
|
+
help="Show the rainbow party-mode startup banner.",
|
|
17
|
+
),
|
|
18
|
+
):
|
|
11
19
|
"""Launch the interactive acra shell."""
|
|
12
20
|
if ctx.invoked_subcommand is None:
|
|
13
|
-
launch_shell()
|
|
21
|
+
launch_shell(party=party)
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
"""Animated startup banner and easter eggs for acra.
|
|
2
|
+
|
|
3
|
+
This module owns the ANSI-driven intro sequence shown when the interactive
|
|
4
|
+
shell starts (`acra` with no subcommand, or `acra serve`):
|
|
5
|
+
|
|
6
|
+
1. The "ACRA" wordmark is revealed with a left-to-right glitch/scan
|
|
7
|
+
animation (or, in --party mode, a rainbow cycle with an emoji burst).
|
|
8
|
+
2. A subtitle is typed out character by character.
|
|
9
|
+
3. A tagline fades in (swapped for a holiday-specific line on a handful
|
|
10
|
+
of dates).
|
|
11
|
+
4. The version number is shown with a short spinner that resolves to a
|
|
12
|
+
"ready" checkmark, similar to Copilot CLI's startup indicator.
|
|
13
|
+
|
|
14
|
+
Everything here is pure ANSI escape codes + stdlib (no extra dependencies).
|
|
15
|
+
|
|
16
|
+
Two independent accessibility/scripting gates, matching conventions used
|
|
17
|
+
across the CLI ecosystem (see https://no-color.org, https://force-color.org,
|
|
18
|
+
and GitHub's writeup on building the Copilot CLI banner: fast-changing
|
|
19
|
+
terminal output is a real screen-reader and low-vision concern, not just a
|
|
20
|
+
cosmetic one):
|
|
21
|
+
|
|
22
|
+
* Color (`_supports_color`) is disabled by NO_COLOR or TERM=dumb, and can
|
|
23
|
+
be forced back on with FORCE_COLOR even when piped.
|
|
24
|
+
* Timed motion (`_animations_enabled`) is disabled whenever stdout isn't a
|
|
25
|
+
real interactive terminal, in CI, or when ACRA_NO_ANIMATION is set -
|
|
26
|
+
falling back to an instant, fully-resolved static print so scripting,
|
|
27
|
+
piping, and tests stay fast and deterministic.
|
|
28
|
+
|
|
29
|
+
These are independent: a NO_COLOR user on a real terminal still gets the
|
|
30
|
+
scan/typing motion, just monochrome; a CI run gets neither, instantly.
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
from __future__ import annotations
|
|
34
|
+
|
|
35
|
+
import os
|
|
36
|
+
import random
|
|
37
|
+
import sys
|
|
38
|
+
import time
|
|
39
|
+
from datetime import date
|
|
40
|
+
from typing import Iterable, List, Optional
|
|
41
|
+
|
|
42
|
+
from acra import __version__
|
|
43
|
+
from acra.ui.banner import WELCOME_TEXT
|
|
44
|
+
|
|
45
|
+
RESET = "\033[0m"
|
|
46
|
+
BOLD = "\033[1m"
|
|
47
|
+
DIM = "\033[2m"
|
|
48
|
+
|
|
49
|
+
COLORS = {
|
|
50
|
+
"black": "\033[30m",
|
|
51
|
+
"red": "\033[31m",
|
|
52
|
+
"green": "\033[32m",
|
|
53
|
+
"yellow": "\033[33m",
|
|
54
|
+
"blue": "\033[34m",
|
|
55
|
+
"magenta": "\033[35m",
|
|
56
|
+
"cyan": "\033[36m",
|
|
57
|
+
"white": "\033[37m",
|
|
58
|
+
"bright_red": "\033[91m",
|
|
59
|
+
"bright_green": "\033[92m",
|
|
60
|
+
"bright_yellow": "\033[93m",
|
|
61
|
+
"bright_blue": "\033[94m",
|
|
62
|
+
"bright_magenta": "\033[95m",
|
|
63
|
+
"bright_cyan": "\033[96m",
|
|
64
|
+
"bright_white": "\033[97m",
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
# ANSI-Shadow-style block glyphs. Only the letters ACRA needs are defined;
|
|
68
|
+
# each glyph is a fixed 6-row x 8-column block so rows line up cleanly
|
|
69
|
+
# whichever letters are combined.
|
|
70
|
+
_GLYPHS = {
|
|
71
|
+
"A": [
|
|
72
|
+
" █████╗ ",
|
|
73
|
+
"██╔══██╗",
|
|
74
|
+
"███████║",
|
|
75
|
+
"██╔══██║",
|
|
76
|
+
"██║ ██║",
|
|
77
|
+
"╚═╝ ╚═╝",
|
|
78
|
+
],
|
|
79
|
+
"C": [
|
|
80
|
+
" ██████╗",
|
|
81
|
+
"██╔════╝",
|
|
82
|
+
"██║ ",
|
|
83
|
+
"██║ ",
|
|
84
|
+
"╚██████╗",
|
|
85
|
+
" ╚═════╝",
|
|
86
|
+
],
|
|
87
|
+
"R": [
|
|
88
|
+
"██████╗ ",
|
|
89
|
+
"██╔══██╗",
|
|
90
|
+
"██████╔╝",
|
|
91
|
+
"██╔══██╗",
|
|
92
|
+
"██║ ██║",
|
|
93
|
+
"╚═╝ ╚═╝",
|
|
94
|
+
],
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
WORDMARK = "ACRA"
|
|
98
|
+
_GLYPH_HEIGHT = 6
|
|
99
|
+
|
|
100
|
+
# Vertical gradient applied to the wordmark, top row to bottom row.
|
|
101
|
+
_GRADIENT = ["bright_cyan", "bright_cyan", "cyan", "bright_magenta", "magenta", "bright_yellow"]
|
|
102
|
+
|
|
103
|
+
_RAINBOW_CYCLE = [
|
|
104
|
+
"bright_red",
|
|
105
|
+
"bright_yellow",
|
|
106
|
+
"bright_green",
|
|
107
|
+
"bright_cyan",
|
|
108
|
+
"bright_blue",
|
|
109
|
+
"bright_magenta",
|
|
110
|
+
]
|
|
111
|
+
|
|
112
|
+
_GLITCH_CHARS = "@#$%&*+=-/\\|<>?░▒▓"
|
|
113
|
+
_PARTY_EMOJIS = ["🎉", "✨", "🎊", "🚀", "🔥", "🥳", "⭐", "💥"]
|
|
114
|
+
|
|
115
|
+
# Same frame set as the "dots" spinner in the widely-used cli-spinners
|
|
116
|
+
# package (npm), the de facto standard braille spinner recognized across
|
|
117
|
+
# npm/yarn/ora-based tools.
|
|
118
|
+
_SPINNER_FRAMES = "⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏"
|
|
119
|
+
|
|
120
|
+
_HOLIDAY_TAGLINES = {
|
|
121
|
+
(1, 1): "New year, new agent runs. Let's build.",
|
|
122
|
+
(4, 1): "ACRA: (mostly) not lying to you today.",
|
|
123
|
+
(10, 31): "ACRA: coding like it's Halloween.",
|
|
124
|
+
(12, 25): "Shipping code, not presents, today.",
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _supports_color() -> bool:
|
|
129
|
+
"""https://no-color.org / https://force-color.org, checked in that order."""
|
|
130
|
+
if os.environ.get("FORCE_COLOR", "") != "":
|
|
131
|
+
return True
|
|
132
|
+
if os.environ.get("NO_COLOR", "") != "":
|
|
133
|
+
return False
|
|
134
|
+
if os.environ.get("TERM") == "dumb":
|
|
135
|
+
return False
|
|
136
|
+
return True
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def _animations_enabled() -> bool:
|
|
140
|
+
"""Whether to run the timed ANSI animations, or fall back to a static print."""
|
|
141
|
+
if os.environ.get("ACRA_NO_ANIMATION"):
|
|
142
|
+
return False
|
|
143
|
+
if os.environ.get("CI"):
|
|
144
|
+
return False
|
|
145
|
+
return sys.stdout.isatty()
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def _style(text: str, color: Optional[str] = None, bold: bool = False, dim: bool = False) -> str:
|
|
149
|
+
"""Wrap text in ANSI codes, or return it unchanged when color is disabled."""
|
|
150
|
+
if not _supports_color():
|
|
151
|
+
return text
|
|
152
|
+
codes = ""
|
|
153
|
+
if bold:
|
|
154
|
+
codes += BOLD
|
|
155
|
+
if dim:
|
|
156
|
+
codes += DIM
|
|
157
|
+
if color:
|
|
158
|
+
codes += COLORS.get(color, "")
|
|
159
|
+
return f"{codes}{text}{RESET}" if codes else text
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def _build_art(word: str = WORDMARK, spacing: str = " ") -> List[str]:
|
|
163
|
+
return [spacing.join(_GLYPHS[letter][row] for letter in word) for row in range(_GLYPH_HEIGHT)]
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def _print_art_static(art_lines: Iterable[str], gradient: List[str]) -> None:
|
|
167
|
+
for row_idx, line in enumerate(art_lines):
|
|
168
|
+
print(_style(line, color=gradient[row_idx % len(gradient)], bold=True))
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def _glitch_scan_reveal(art_lines: List[str], gradient: List[str], steps: int = 10, delay: float = 0.025) -> None:
|
|
172
|
+
"""Reveal the wordmark left-to-right, resolving glitch characters into the real ones."""
|
|
173
|
+
if not _animations_enabled():
|
|
174
|
+
_print_art_static(art_lines, gradient)
|
|
175
|
+
return
|
|
176
|
+
|
|
177
|
+
width = max(len(line) for line in art_lines)
|
|
178
|
+
height = len(art_lines)
|
|
179
|
+
|
|
180
|
+
sys.stdout.write("\n" * height)
|
|
181
|
+
for step in range(1, steps + 1):
|
|
182
|
+
threshold = int(width * step / steps)
|
|
183
|
+
sys.stdout.write(f"\033[{height}A")
|
|
184
|
+
for row_idx, line in enumerate(art_lines):
|
|
185
|
+
rendered = [
|
|
186
|
+
" " if ch == " " else (ch if col < threshold else random.choice(_GLITCH_CHARS))
|
|
187
|
+
for col, ch in enumerate(line)
|
|
188
|
+
]
|
|
189
|
+
styled = _style("".join(rendered), color=gradient[row_idx % len(gradient)], bold=True)
|
|
190
|
+
sys.stdout.write(f"\033[K{styled}\n")
|
|
191
|
+
sys.stdout.flush()
|
|
192
|
+
time.sleep(delay)
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def _party_reveal(art_lines: List[str], frames: int = 10, delay: float = 0.09) -> None:
|
|
196
|
+
"""Rainbow-cycling wordmark with a burst of flying emojis, for --party / --easter-egg."""
|
|
197
|
+
if not _animations_enabled():
|
|
198
|
+
print(_style("PARTY MODE \U0001f389", color="bright_magenta", bold=True))
|
|
199
|
+
_print_art_static(art_lines, _RAINBOW_CYCLE)
|
|
200
|
+
return
|
|
201
|
+
|
|
202
|
+
width = max(len(line) for line in art_lines)
|
|
203
|
+
height = len(art_lines)
|
|
204
|
+
|
|
205
|
+
sys.stdout.write("\n" * (height + 1))
|
|
206
|
+
for frame in range(frames):
|
|
207
|
+
sys.stdout.write(f"\033[{height + 1}A")
|
|
208
|
+
for row_idx, line in enumerate(art_lines):
|
|
209
|
+
color = _RAINBOW_CYCLE[(row_idx + frame) % len(_RAINBOW_CYCLE)]
|
|
210
|
+
sys.stdout.write(f"\033[K{_style(line, color=color, bold=True)}\n")
|
|
211
|
+
emoji_row = "".join(
|
|
212
|
+
random.choice(_PARTY_EMOJIS) if random.random() < 0.3 else " " for _ in range(width // 2)
|
|
213
|
+
)
|
|
214
|
+
sys.stdout.write(f"\033[K{emoji_row}\n")
|
|
215
|
+
sys.stdout.flush()
|
|
216
|
+
time.sleep(delay)
|
|
217
|
+
|
|
218
|
+
print(_style("\U0001f389 PARTY MODE ACTIVATED \U0001f389", color="bright_magenta", bold=True))
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def _type_line(text: str, color: Optional[str] = None, delay: float = 0.012) -> None:
|
|
222
|
+
if not _animations_enabled():
|
|
223
|
+
print(_style(text, color=color))
|
|
224
|
+
return
|
|
225
|
+
for ch in text:
|
|
226
|
+
sys.stdout.write(_style(ch, color=color))
|
|
227
|
+
sys.stdout.flush()
|
|
228
|
+
time.sleep(delay)
|
|
229
|
+
sys.stdout.write("\n")
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def _fade_in_lines(lines: Iterable[str], dim: bool = False, delay: float = 0.12) -> None:
|
|
233
|
+
for line in lines:
|
|
234
|
+
if _animations_enabled():
|
|
235
|
+
time.sleep(delay)
|
|
236
|
+
print(_style(line, dim=dim))
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def _spinner_version_line(accent: str, frames: int = 14, delay: float = 0.035) -> None:
|
|
240
|
+
"""Show 'acra vX.Y.Z' with a small spinner that resolves into a ready check."""
|
|
241
|
+
label = _style(f"acra v{__version__}", color=accent, bold=True)
|
|
242
|
+
ready = _style("\u2713 ready", color="bright_green")
|
|
243
|
+
|
|
244
|
+
if not _animations_enabled():
|
|
245
|
+
print(f"{label} {ready}")
|
|
246
|
+
return
|
|
247
|
+
|
|
248
|
+
for i in range(frames):
|
|
249
|
+
frame = _SPINNER_FRAMES[i % len(_SPINNER_FRAMES)]
|
|
250
|
+
status = _style(f"{frame} initializing\u2026", dim=True)
|
|
251
|
+
sys.stdout.write(f"\r{label} {status}\033[K")
|
|
252
|
+
sys.stdout.flush()
|
|
253
|
+
time.sleep(delay)
|
|
254
|
+
sys.stdout.write(f"\r{label} {ready}\033[K\n")
|
|
255
|
+
sys.stdout.flush()
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
def _holiday_tagline(today: Optional[date] = None) -> Optional[str]:
|
|
259
|
+
today = today or date.today()
|
|
260
|
+
return _HOLIDAY_TAGLINES.get((today.month, today.day))
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def render_intro(theme: Optional[dict] = None, party: bool = False) -> None:
|
|
264
|
+
"""Print the full animated acra startup banner.
|
|
265
|
+
|
|
266
|
+
`theme` is one of the dicts from acra.ui.theme (must contain "accent").
|
|
267
|
+
`party` triggers the rainbow/emoji easter egg instead of the normal
|
|
268
|
+
glitch-scan reveal; wired up to `acra --party` / `acra --easter-egg`.
|
|
269
|
+
"""
|
|
270
|
+
theme = theme or {}
|
|
271
|
+
accent = theme.get("accent", "bright_cyan")
|
|
272
|
+
if accent not in COLORS:
|
|
273
|
+
accent = "bright_cyan"
|
|
274
|
+
|
|
275
|
+
art = _build_art()
|
|
276
|
+
|
|
277
|
+
if party:
|
|
278
|
+
_party_reveal(art)
|
|
279
|
+
else:
|
|
280
|
+
_glitch_scan_reveal(art, _GRADIENT)
|
|
281
|
+
|
|
282
|
+
_type_line("Autonomous Coding & Research Agent", color=accent)
|
|
283
|
+
|
|
284
|
+
tagline = _holiday_tagline() or WELCOME_TEXT
|
|
285
|
+
_fade_in_lines([tagline], dim=True)
|
|
286
|
+
|
|
287
|
+
_spinner_version_line(accent)
|
|
@@ -3,14 +3,7 @@
|
|
|
3
3
|
from rich.panel import Panel
|
|
4
4
|
from rich.console import Console
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
____ ____ ____
|
|
8
|
-
/ __ \ / __ \/ __ \
|
|
9
|
-
| | | | | | | | | |
|
|
10
|
-
| | | | | | | | | |
|
|
11
|
-
| |__| | |__| | |__| |
|
|
12
|
-
\____/ \____/ \____/
|
|
13
|
-
"""
|
|
6
|
+
from acra import __version__
|
|
14
7
|
|
|
15
8
|
WELCOME_TEXT = "acra: agentic CLI for LLM task workflows"
|
|
16
9
|
|
|
@@ -18,12 +11,11 @@ WELCOME_TEXT = "acra: agentic CLI for LLM task workflows"
|
|
|
18
11
|
def render_banner(provider: str, model: str, profile: str, workspace: str, theme: dict) -> Panel:
|
|
19
12
|
console = Console()
|
|
20
13
|
lines = [
|
|
21
|
-
f"[bold {theme['accent']}]acra v0.1.2[/bold {theme['accent']}]",
|
|
22
14
|
f"Provider: {provider} · Model: {model}",
|
|
23
15
|
f"Profile: {profile} · Workspace: {workspace}",
|
|
24
16
|
]
|
|
25
17
|
content = "\n".join(lines)
|
|
26
|
-
return Panel(content, title=
|
|
18
|
+
return Panel(content, title="Session", style=theme["border"], border_style=theme["border"])
|
|
27
19
|
|
|
28
20
|
|
|
29
21
|
def render_tips(theme: dict) -> Panel:
|
|
@@ -37,4 +29,4 @@ def render_tips(theme: dict) -> Panel:
|
|
|
37
29
|
|
|
38
30
|
|
|
39
31
|
# Compatibility alias for older imports and public package API.
|
|
40
|
-
welcome_banner = render_banner
|
|
32
|
+
welcome_banner = render_banner
|
|
@@ -9,6 +9,7 @@ from prompt_toolkit import PromptSession
|
|
|
9
9
|
from prompt_toolkit.completion import Completer, Completion
|
|
10
10
|
from prompt_toolkit.shortcuts import CompleteStyle
|
|
11
11
|
from acra.ui.banner import render_banner, render_tips
|
|
12
|
+
from acra.ui.animated_banner import render_intro
|
|
12
13
|
from acra.ui.theme import get_theme
|
|
13
14
|
from acra.config.profile_manager import ProfileManager
|
|
14
15
|
from rich.console import Console
|
|
@@ -31,9 +32,10 @@ class SlashCompleter(Completer):
|
|
|
31
32
|
yield completion
|
|
32
33
|
|
|
33
34
|
|
|
34
|
-
def launch_shell(profile: str = None, workspace: str = None):
|
|
35
|
+
def launch_shell(profile: str = None, workspace: str = None, party: bool = False):
|
|
35
36
|
config = ProfileManager().load_profile(profile)
|
|
36
37
|
theme = get_theme(config.get("theme", "dark-orange"))
|
|
38
|
+
render_intro(theme, party=party)
|
|
37
39
|
banner = render_banner(
|
|
38
40
|
config.get("provider", "gemini"),
|
|
39
41
|
config.get("model", "gemini-2.5-flash"),
|
|
@@ -88,4 +90,4 @@ def launch_shell(profile: str = None, workspace: str = None):
|
|
|
88
90
|
except EOFError:
|
|
89
91
|
break
|
|
90
92
|
|
|
91
|
-
console.print(Panel("Goodbye", title="acra"))
|
|
93
|
+
console.print(Panel("Goodbye", title="acra"))
|
|
@@ -154,11 +154,13 @@ acra/tools/web_tools/arxiv_search.py
|
|
|
154
154
|
acra/tools/web_tools/serpapi_search.py
|
|
155
155
|
acra/tools/web_tools/tavily_search.py
|
|
156
156
|
acra/ui/__init__.py
|
|
157
|
+
acra/ui/animated_banner.py
|
|
157
158
|
acra/ui/banner.py
|
|
158
159
|
acra/ui/components.py
|
|
159
160
|
acra/ui/shell.py
|
|
160
161
|
acra/ui/theme.py
|
|
161
162
|
acra/ui/__pycache__/__init__.cpython-312.pyc
|
|
163
|
+
acra/ui/__pycache__/animated_banner.cpython-312.pyc
|
|
162
164
|
acra/ui/__pycache__/banner.cpython-312.pyc
|
|
163
165
|
acra/ui/__pycache__/components.cpython-312.pyc
|
|
164
166
|
acra/ui/__pycache__/shell.cpython-312.pyc
|
|
@@ -174,4 +176,5 @@ acra_cli.egg-info/dependency_links.txt
|
|
|
174
176
|
acra_cli.egg-info/entry_points.txt
|
|
175
177
|
acra_cli.egg-info/requires.txt
|
|
176
178
|
acra_cli.egg-info/top_level.txt
|
|
177
|
-
tests/test_cli.py
|
|
179
|
+
tests/test_cli.py
|
|
180
|
+
tests/test_interactive_routing.py
|