EvoScientist 0.0.1.dev2__tar.gz → 0.0.1.dev3__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.
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/EvoScientist.py +45 -13
- evoscientist-0.0.1.dev3/EvoScientist/cli.py +427 -0
- evoscientist-0.0.1.dev3/EvoScientist/memory.py +715 -0
- evoscientist-0.0.1.dev3/EvoScientist/middleware.py +80 -0
- evoscientist-0.0.1.dev3/EvoScientist/paths.py +45 -0
- evoscientist-0.0.1.dev3/EvoScientist/skills_manager.py +392 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/stream/__init__.py +25 -0
- evoscientist-0.0.1.dev3/EvoScientist/stream/display.py +604 -0
- evoscientist-0.0.1.dev3/EvoScientist/stream/events.py +415 -0
- evoscientist-0.0.1.dev3/EvoScientist/stream/state.py +343 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/stream/utils.py +23 -16
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/tools.py +64 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist.egg-info/PKG-INFO +97 -3
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist.egg-info/SOURCES.txt +7 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist.egg-info/requires.txt +1 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/PKG-INFO +97 -3
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/README.md +96 -3
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/pyproject.toml +2 -1
- evoscientist-0.0.1.dev3/tests/test_skills_manager.py +345 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/tests/test_stream_utils.py +19 -2
- evoscientist-0.0.1.dev2/EvoScientist/cli.py +0 -1553
- evoscientist-0.0.1.dev2/EvoScientist/middleware.py +0 -35
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/__init__.py +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/__main__.py +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/backends.py +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/prompts.py +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/accelerate/SKILL.md +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/accelerate/references/custom-plugins.md +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/accelerate/references/megatron-integration.md +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/accelerate/references/performance.md +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/bitsandbytes/SKILL.md +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/bitsandbytes/references/memory-optimization.md +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/bitsandbytes/references/qlora-training.md +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/bitsandbytes/references/quantization-formats.md +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/find-skills/SKILL.md +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/find-skills/scripts/install_skill.py +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/flash-attention/SKILL.md +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/flash-attention/references/benchmarks.md +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/flash-attention/references/transformers-integration.md +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/llama-cpp/SKILL.md +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/llama-cpp/references/optimization.md +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/llama-cpp/references/quantization.md +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/llama-cpp/references/server.md +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/lm-evaluation-harness/SKILL.md +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/lm-evaluation-harness/references/api-evaluation.md +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/lm-evaluation-harness/references/benchmark-guide.md +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/lm-evaluation-harness/references/custom-tasks.md +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/lm-evaluation-harness/references/distributed-eval.md +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/ml-paper-writing/SKILL.md +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/ml-paper-writing/references/checklists.md +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/ml-paper-writing/references/citation-workflow.md +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/ml-paper-writing/references/reviewer-guidelines.md +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/ml-paper-writing/references/sources.md +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/ml-paper-writing/references/writing-guide.md +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/ml-paper-writing/templates/README.md +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/ml-paper-writing/templates/aaai2026/README.md +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/ml-paper-writing/templates/aaai2026/aaai2026-unified-supp.tex +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/ml-paper-writing/templates/aaai2026/aaai2026-unified-template.tex +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/ml-paper-writing/templates/aaai2026/aaai2026.bib +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/ml-paper-writing/templates/aaai2026/aaai2026.bst +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/ml-paper-writing/templates/aaai2026/aaai2026.sty +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/ml-paper-writing/templates/acl/README.md +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/ml-paper-writing/templates/acl/acl.sty +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/ml-paper-writing/templates/acl/acl_latex.tex +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/ml-paper-writing/templates/acl/acl_lualatex.tex +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/ml-paper-writing/templates/acl/acl_natbib.bst +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/ml-paper-writing/templates/acl/anthology.bib.txt +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/ml-paper-writing/templates/acl/custom.bib +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/ml-paper-writing/templates/acl/formatting.md +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/ml-paper-writing/templates/colm2025/README.md +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/ml-paper-writing/templates/colm2025/colm2025_conference.bib +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/ml-paper-writing/templates/colm2025/colm2025_conference.bst +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/ml-paper-writing/templates/colm2025/colm2025_conference.pdf +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/ml-paper-writing/templates/colm2025/colm2025_conference.sty +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/ml-paper-writing/templates/colm2025/colm2025_conference.tex +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/ml-paper-writing/templates/colm2025/fancyhdr.sty +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/ml-paper-writing/templates/colm2025/math_commands.tex +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/ml-paper-writing/templates/colm2025/natbib.sty +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/ml-paper-writing/templates/iclr2026/fancyhdr.sty +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/ml-paper-writing/templates/iclr2026/iclr2026_conference.bib +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/ml-paper-writing/templates/iclr2026/iclr2026_conference.bst +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/ml-paper-writing/templates/iclr2026/iclr2026_conference.pdf +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/ml-paper-writing/templates/iclr2026/iclr2026_conference.sty +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/ml-paper-writing/templates/iclr2026/iclr2026_conference.tex +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/ml-paper-writing/templates/iclr2026/math_commands.tex +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/ml-paper-writing/templates/iclr2026/natbib.sty +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/ml-paper-writing/templates/icml2026/algorithm.sty +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/ml-paper-writing/templates/icml2026/algorithmic.sty +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/ml-paper-writing/templates/icml2026/example_paper.bib +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/ml-paper-writing/templates/icml2026/example_paper.pdf +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/ml-paper-writing/templates/icml2026/example_paper.tex +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/ml-paper-writing/templates/icml2026/fancyhdr.sty +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/ml-paper-writing/templates/icml2026/icml2026.bst +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/ml-paper-writing/templates/icml2026/icml2026.sty +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/ml-paper-writing/templates/icml2026/icml_numpapers.pdf +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/ml-paper-writing/templates/neurips2025/Makefile +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/ml-paper-writing/templates/neurips2025/extra_pkgs.tex +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/ml-paper-writing/templates/neurips2025/main.tex +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/ml-paper-writing/templates/neurips2025/neurips.sty +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/peft/SKILL.md +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/peft/references/advanced-usage.md +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/peft/references/troubleshooting.md +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/ray-data/SKILL.md +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/ray-data/references/integration.md +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/ray-data/references/transformations.md +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/skill-creator/LICENSE.txt +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/skill-creator/SKILL.md +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/skill-creator/references/output-patterns.md +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/skill-creator/references/workflows.md +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/skill-creator/scripts/init_skill.py +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/skill-creator/scripts/package_skill.py +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/skills/skill-creator/scripts/quick_validate.py +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/stream/emitter.py +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/stream/formatter.py +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/stream/tracker.py +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/subagent.yaml +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist/utils.py +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist.egg-info/dependency_links.txt +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist.egg-info/entry_points.txt +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/EvoScientist.egg-info/top_level.txt +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/LICENSE +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/setup.cfg +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/tests/test_backends.py +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/tests/test_imports.py +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/tests/test_prompts.py +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/tests/test_stream_emitter.py +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/tests/test_stream_state.py +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/tests/test_stream_tracker.py +0 -0
- {evoscientist-0.0.1.dev2 → evoscientist-0.0.1.dev3}/tests/test_tools.py +0 -0
|
@@ -22,10 +22,16 @@ from deepagents.backends import FilesystemBackend, CompositeBackend
|
|
|
22
22
|
from langchain.chat_models import init_chat_model
|
|
23
23
|
|
|
24
24
|
from .backends import CustomSandboxBackend, MergedReadOnlyBackend
|
|
25
|
-
from .middleware import create_skills_middleware
|
|
25
|
+
from .middleware import create_skills_middleware, create_memory_middleware
|
|
26
26
|
from .prompts import RESEARCHER_INSTRUCTIONS, get_system_prompt
|
|
27
27
|
from .utils import load_subagents
|
|
28
|
-
from .tools import tavily_search, think_tool
|
|
28
|
+
from .tools import tavily_search, think_tool, skill_manager
|
|
29
|
+
from .paths import (
|
|
30
|
+
ensure_dirs,
|
|
31
|
+
default_workspace_dir,
|
|
32
|
+
MEMORY_DIR as _MEMORY_DIR_PATH,
|
|
33
|
+
USER_SKILLS_DIR as _USER_SKILLS_DIR_PATH,
|
|
34
|
+
)
|
|
29
35
|
|
|
30
36
|
# =============================================================================
|
|
31
37
|
# Configuration
|
|
@@ -39,8 +45,11 @@ MAX_CONCURRENT = 3 # Max parallel sub-agents
|
|
|
39
45
|
MAX_ITERATIONS = 3 # Max delegation rounds
|
|
40
46
|
|
|
41
47
|
# Workspace settings
|
|
42
|
-
|
|
48
|
+
ensure_dirs()
|
|
49
|
+
WORKSPACE_DIR = str(default_workspace_dir())
|
|
50
|
+
MEMORY_DIR = str(_MEMORY_DIR_PATH) # Shared across sessions (not per-session)
|
|
43
51
|
SKILLS_DIR = str(Path(__file__).parent / "skills")
|
|
52
|
+
USER_SKILLS_DIR = str(_USER_SKILLS_DIR_PATH)
|
|
44
53
|
SUBAGENTS_CONFIG = Path(__file__).parent / "subagent.yaml"
|
|
45
54
|
|
|
46
55
|
# =============================================================================
|
|
@@ -76,16 +85,25 @@ else:
|
|
|
76
85
|
virtual_mode=True,
|
|
77
86
|
)
|
|
78
87
|
|
|
79
|
-
# Skills backend: merge user-installed (
|
|
88
|
+
# Skills backend: merge user-installed (./skills/) and system (package) skills
|
|
80
89
|
_skills_backend = MergedReadOnlyBackend(
|
|
81
|
-
primary_dir=
|
|
90
|
+
primary_dir=USER_SKILLS_DIR, # user-installed, takes priority
|
|
82
91
|
secondary_dir=SKILLS_DIR, # package built-in, fallback
|
|
83
92
|
)
|
|
84
93
|
|
|
85
|
-
#
|
|
94
|
+
# Memory backend: persistent filesystem for long-term memory (shared across sessions)
|
|
95
|
+
_memory_backend = FilesystemBackend(
|
|
96
|
+
root_dir=MEMORY_DIR,
|
|
97
|
+
virtual_mode=True,
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
# Composite backend: workspace as default, skills and memory mounted
|
|
86
101
|
backend = CompositeBackend(
|
|
87
102
|
default=_workspace_backend,
|
|
88
|
-
routes={
|
|
103
|
+
routes={
|
|
104
|
+
"/skills/": _skills_backend,
|
|
105
|
+
"/memory/": _memory_backend,
|
|
106
|
+
},
|
|
89
107
|
)
|
|
90
108
|
|
|
91
109
|
tool_registry = {
|
|
@@ -107,10 +125,13 @@ subagents = load_subagents(
|
|
|
107
125
|
_AGENT_KWARGS = dict(
|
|
108
126
|
name="EvoScientist",
|
|
109
127
|
model=chat_model,
|
|
110
|
-
tools=[think_tool],
|
|
128
|
+
tools=[think_tool, skill_manager],
|
|
111
129
|
backend=backend,
|
|
112
130
|
subagents=subagents,
|
|
113
|
-
middleware=[
|
|
131
|
+
middleware=[
|
|
132
|
+
create_memory_middleware(MEMORY_DIR, extraction_model=chat_model),
|
|
133
|
+
create_skills_middleware(SKILLS_DIR, user_skills_dir=USER_SKILLS_DIR),
|
|
134
|
+
],
|
|
114
135
|
system_prompt=SYSTEM_PROMPT,
|
|
115
136
|
)
|
|
116
137
|
|
|
@@ -124,7 +145,7 @@ def create_cli_agent(workspace_dir: str | None = None):
|
|
|
124
145
|
Args:
|
|
125
146
|
workspace_dir: Optional per-session workspace directory. If provided,
|
|
126
147
|
creates a fresh backend rooted at this path. If None, uses the
|
|
127
|
-
module-level default backend (./workspace
|
|
148
|
+
module-level default backend (./workspace).
|
|
128
149
|
"""
|
|
129
150
|
from langgraph.checkpoint.memory import InMemorySaver # type: ignore[import-untyped]
|
|
130
151
|
|
|
@@ -135,14 +156,25 @@ def create_cli_agent(workspace_dir: str | None = None):
|
|
|
135
156
|
timeout=300,
|
|
136
157
|
)
|
|
137
158
|
sk_backend = MergedReadOnlyBackend(
|
|
138
|
-
primary_dir=
|
|
159
|
+
primary_dir=USER_SKILLS_DIR,
|
|
139
160
|
secondary_dir=SKILLS_DIR,
|
|
140
161
|
)
|
|
162
|
+
# Memory always uses SHARED directory (not per-session) for cross-session persistence
|
|
163
|
+
mem_backend = FilesystemBackend(
|
|
164
|
+
root_dir=MEMORY_DIR,
|
|
165
|
+
virtual_mode=True,
|
|
166
|
+
)
|
|
141
167
|
be = CompositeBackend(
|
|
142
168
|
default=ws_backend,
|
|
143
|
-
routes={
|
|
169
|
+
routes={
|
|
170
|
+
"/skills/": sk_backend,
|
|
171
|
+
"/memory/": mem_backend,
|
|
172
|
+
},
|
|
144
173
|
)
|
|
145
|
-
mw = [
|
|
174
|
+
mw = [
|
|
175
|
+
create_memory_middleware(MEMORY_DIR, extraction_model=chat_model),
|
|
176
|
+
create_skills_middleware(SKILLS_DIR, user_skills_dir=USER_SKILLS_DIR),
|
|
177
|
+
]
|
|
146
178
|
kwargs = dict(
|
|
147
179
|
_AGENT_KWARGS,
|
|
148
180
|
backend=be,
|
|
@@ -0,0 +1,427 @@
|
|
|
1
|
+
"""
|
|
2
|
+
EvoScientist Agent CLI
|
|
3
|
+
|
|
4
|
+
Command-line interface with streaming output for the EvoScientist research agent.
|
|
5
|
+
|
|
6
|
+
Features:
|
|
7
|
+
- Thinking panel (blue) - shows model reasoning
|
|
8
|
+
- Tool calls with status indicators (green/yellow/red dots)
|
|
9
|
+
- Tool results in tree format with folding
|
|
10
|
+
- Response panel (green) - shows final response
|
|
11
|
+
- Thread ID support for multi-turn conversations
|
|
12
|
+
- Interactive mode with prompt_toolkit
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import logging
|
|
16
|
+
import os
|
|
17
|
+
import sys
|
|
18
|
+
import uuid
|
|
19
|
+
from datetime import datetime
|
|
20
|
+
from typing import Any, Optional
|
|
21
|
+
|
|
22
|
+
import typer # type: ignore[import-untyped]
|
|
23
|
+
from prompt_toolkit import PromptSession # type: ignore[import-untyped]
|
|
24
|
+
from prompt_toolkit.history import FileHistory # type: ignore[import-untyped]
|
|
25
|
+
from prompt_toolkit.auto_suggest import AutoSuggestFromHistory # type: ignore[import-untyped]
|
|
26
|
+
from prompt_toolkit.formatted_text import HTML # type: ignore[import-untyped]
|
|
27
|
+
from rich.text import Text # type: ignore[import-untyped]
|
|
28
|
+
|
|
29
|
+
# Backward-compat re-exports (tests import these from EvoScientist.cli)
|
|
30
|
+
from .stream.state import SubAgentState, StreamState, _parse_todo_items, _build_todo_stats # noqa: F401
|
|
31
|
+
from .stream.display import console, _run_streaming
|
|
32
|
+
from .paths import ensure_dirs, new_run_dir
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _shorten_path(path: str) -> str:
|
|
36
|
+
"""Shorten absolute path to relative path from current directory."""
|
|
37
|
+
if not path:
|
|
38
|
+
return path
|
|
39
|
+
try:
|
|
40
|
+
cwd = os.getcwd()
|
|
41
|
+
if path.startswith(cwd):
|
|
42
|
+
# Remove cwd prefix, keep the relative part
|
|
43
|
+
rel = path[len(cwd):].lstrip(os.sep)
|
|
44
|
+
# Add current dir name for context
|
|
45
|
+
return os.path.join(os.path.basename(cwd), rel) if rel else os.path.basename(cwd)
|
|
46
|
+
return path
|
|
47
|
+
except Exception:
|
|
48
|
+
return path
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
# =============================================================================
|
|
52
|
+
# Banner
|
|
53
|
+
# =============================================================================
|
|
54
|
+
|
|
55
|
+
EVOSCIENTIST_ASCII_LINES = [
|
|
56
|
+
r" ███████╗ ██╗ ██╗ ██████╗ ███████╗ ██████╗ ██╗ ███████╗ ███╗ ██╗ ████████╗ ██╗ ███████╗ ████████╗",
|
|
57
|
+
r" ██╔════╝ ██║ ██║ ██╔═══██╗ ██╔════╝ ██╔════╝ ██║ ██╔════╝ ████╗ ██║ ╚══██╔══╝ ██║ ██╔════╝ ╚══██╔══╝",
|
|
58
|
+
r" █████╗ ██║ ██║ ██║ ██║ ███████╗ ██║ ██║ █████╗ ██╔██╗ ██║ ██║ ██║ ███████╗ ██║ ",
|
|
59
|
+
r" ██╔══╝ ╚██╗ ██╔╝ ██║ ██║ ╚════██║ ██║ ██║ ██╔══╝ ██║╚██╗██║ ██║ ██║ ╚════██║ ██║ ",
|
|
60
|
+
r" ███████╗ ╚████╔╝ ╚██████╔╝ ███████║ ╚██████╗ ██║ ███████╗ ██║ ╚████║ ██║ ██║ ███████║ ██║ ",
|
|
61
|
+
r" ╚══════╝ ╚═══╝ ╚═════╝ ╚══════╝ ╚═════╝ ╚═╝ ╚══════╝ ╚═╝ ╚═══╝ ╚═╝ ╚═╝ ╚══════╝ ╚═╝ ",
|
|
62
|
+
]
|
|
63
|
+
|
|
64
|
+
# Blue gradient: deep navy -> royal blue -> sky blue -> cyan
|
|
65
|
+
_GRADIENT_COLORS = ["#1a237e", "#1565c0", "#1e88e5", "#42a5f5", "#64b5f6", "#90caf9"]
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def print_banner(
|
|
69
|
+
thread_id: str,
|
|
70
|
+
workspace_dir: str | None = None,
|
|
71
|
+
memory_dir: str | None = None,
|
|
72
|
+
):
|
|
73
|
+
"""Print welcome banner with ASCII art logo, thread ID, and workspace path."""
|
|
74
|
+
for line, color in zip(EVOSCIENTIST_ASCII_LINES, _GRADIENT_COLORS):
|
|
75
|
+
console.print(Text(line, style=f"{color} bold"))
|
|
76
|
+
info = Text()
|
|
77
|
+
info.append(" Thread: ", style="dim")
|
|
78
|
+
info.append(thread_id, style="yellow")
|
|
79
|
+
if workspace_dir:
|
|
80
|
+
info.append("\n Workspace: ", style="dim")
|
|
81
|
+
info.append(_shorten_path(workspace_dir), style="cyan")
|
|
82
|
+
if memory_dir:
|
|
83
|
+
trimmed = memory_dir.rstrip("/").rstrip("\\")
|
|
84
|
+
info.append("\n Memory dir: ", style="dim")
|
|
85
|
+
info.append(_shorten_path(trimmed), style="cyan")
|
|
86
|
+
info.append("\n Commands: ", style="dim")
|
|
87
|
+
info.append("/exit", style="bold")
|
|
88
|
+
info.append(", ", style="dim")
|
|
89
|
+
info.append("/new", style="bold")
|
|
90
|
+
info.append(", ", style="dim")
|
|
91
|
+
info.append("/thread", style="bold")
|
|
92
|
+
info.append(", ", style="dim")
|
|
93
|
+
info.append("/skills", style="bold")
|
|
94
|
+
info.append(", ", style="dim")
|
|
95
|
+
info.append("/install-skill", style="bold")
|
|
96
|
+
info.append(", ", style="dim")
|
|
97
|
+
info.append("/uninstall-skill", style="bold")
|
|
98
|
+
console.print(info)
|
|
99
|
+
console.print()
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
# =============================================================================
|
|
103
|
+
# Skill management commands
|
|
104
|
+
# =============================================================================
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def _cmd_list_skills() -> None:
|
|
108
|
+
"""List installed user skills."""
|
|
109
|
+
from .skills_manager import list_skills
|
|
110
|
+
from .paths import USER_SKILLS_DIR
|
|
111
|
+
|
|
112
|
+
skills = list_skills(include_system=False)
|
|
113
|
+
|
|
114
|
+
if not skills:
|
|
115
|
+
console.print("[dim]No user skills installed.[/dim]")
|
|
116
|
+
console.print(f"[dim]Install with:[/dim] /install-skill <path-or-url>")
|
|
117
|
+
console.print(f"[dim]Skills directory:[/dim] [cyan]{_shorten_path(str(USER_SKILLS_DIR))}[/cyan]")
|
|
118
|
+
console.print()
|
|
119
|
+
return
|
|
120
|
+
|
|
121
|
+
console.print(f"[bold]Installed Skills[/bold] ({len(skills)}):")
|
|
122
|
+
for skill in skills:
|
|
123
|
+
console.print(f" [green]{skill.name}[/green] - {skill.description}")
|
|
124
|
+
console.print(f"\n[dim]Location:[/dim] [cyan]{_shorten_path(str(USER_SKILLS_DIR))}[/cyan]")
|
|
125
|
+
console.print()
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _cmd_install_skill(source: str) -> None:
|
|
129
|
+
"""Install a skill from local path or GitHub URL."""
|
|
130
|
+
from .skills_manager import install_skill
|
|
131
|
+
|
|
132
|
+
if not source:
|
|
133
|
+
console.print("[red]Usage:[/red] /install-skill <path-or-url>")
|
|
134
|
+
console.print("[dim]Examples:[/dim]")
|
|
135
|
+
console.print(" /install-skill ./my-skill")
|
|
136
|
+
console.print(" /install-skill https://github.com/user/repo/tree/main/skill-name")
|
|
137
|
+
console.print(" /install-skill user/repo@skill-name")
|
|
138
|
+
console.print()
|
|
139
|
+
return
|
|
140
|
+
|
|
141
|
+
console.print(f"[dim]Installing skill from:[/dim] {source}")
|
|
142
|
+
|
|
143
|
+
result = install_skill(source)
|
|
144
|
+
|
|
145
|
+
if result["success"]:
|
|
146
|
+
console.print(f"[green]Installed:[/green] {result['name']}")
|
|
147
|
+
console.print(f"[dim]Description:[/dim] {result.get('description', '(none)')}")
|
|
148
|
+
console.print(f"[dim]Path:[/dim] [cyan]{_shorten_path(result['path'])}[/cyan]")
|
|
149
|
+
console.print()
|
|
150
|
+
console.print("[dim]Reload the agent with /new to use the skill.[/dim]")
|
|
151
|
+
else:
|
|
152
|
+
console.print(f"[red]Failed:[/red] {result['error']}")
|
|
153
|
+
console.print()
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def _cmd_uninstall_skill(name: str) -> None:
|
|
157
|
+
"""Uninstall a user-installed skill."""
|
|
158
|
+
from .skills_manager import uninstall_skill
|
|
159
|
+
|
|
160
|
+
if not name:
|
|
161
|
+
console.print("[red]Usage:[/red] /uninstall-skill <skill-name>")
|
|
162
|
+
console.print("[dim]Use /skills to see installed skills.[/dim]")
|
|
163
|
+
console.print()
|
|
164
|
+
return
|
|
165
|
+
|
|
166
|
+
result = uninstall_skill(name)
|
|
167
|
+
|
|
168
|
+
if result["success"]:
|
|
169
|
+
console.print(f"[green]Uninstalled:[/green] {name}")
|
|
170
|
+
console.print("[dim]Reload the agent with /new to apply changes.[/dim]")
|
|
171
|
+
else:
|
|
172
|
+
console.print(f"[red]Failed:[/red] {result['error']}")
|
|
173
|
+
console.print()
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
# =============================================================================
|
|
177
|
+
# CLI commands
|
|
178
|
+
# =============================================================================
|
|
179
|
+
|
|
180
|
+
def cmd_interactive(
|
|
181
|
+
agent: Any,
|
|
182
|
+
show_thinking: bool = True,
|
|
183
|
+
workspace_dir: str | None = None,
|
|
184
|
+
workspace_fixed: bool = False,
|
|
185
|
+
) -> None:
|
|
186
|
+
"""Interactive conversation mode with streaming output.
|
|
187
|
+
|
|
188
|
+
Args:
|
|
189
|
+
agent: Compiled agent graph
|
|
190
|
+
show_thinking: Whether to display thinking panels
|
|
191
|
+
workspace_dir: Per-session workspace directory path
|
|
192
|
+
workspace_fixed: If True, /new keeps the same workspace directory
|
|
193
|
+
"""
|
|
194
|
+
thread_id = str(uuid.uuid4())
|
|
195
|
+
from .EvoScientist import MEMORY_DIR
|
|
196
|
+
memory_dir = MEMORY_DIR
|
|
197
|
+
print_banner(thread_id, workspace_dir, memory_dir)
|
|
198
|
+
|
|
199
|
+
history_file = str(os.path.expanduser("~/.EvoScientist_history"))
|
|
200
|
+
session = PromptSession(
|
|
201
|
+
history=FileHistory(history_file),
|
|
202
|
+
auto_suggest=AutoSuggestFromHistory(),
|
|
203
|
+
enable_history_search=True,
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
def _print_separator():
|
|
207
|
+
"""Print a horizontal separator line spanning the terminal width."""
|
|
208
|
+
width = console.size.width
|
|
209
|
+
console.print(Text("\u2500" * width, style="dim"))
|
|
210
|
+
|
|
211
|
+
_print_separator()
|
|
212
|
+
while True:
|
|
213
|
+
try:
|
|
214
|
+
user_input = session.prompt(
|
|
215
|
+
HTML('<ansiblue><b>></b></ansiblue> ')
|
|
216
|
+
).strip()
|
|
217
|
+
|
|
218
|
+
if not user_input:
|
|
219
|
+
# Erase the empty prompt line so it looks like nothing happened
|
|
220
|
+
sys.stdout.write("\033[A\033[2K\r")
|
|
221
|
+
sys.stdout.flush()
|
|
222
|
+
continue
|
|
223
|
+
|
|
224
|
+
_print_separator()
|
|
225
|
+
|
|
226
|
+
# Special commands
|
|
227
|
+
if user_input.lower() in ("/exit", "/quit", "/q"):
|
|
228
|
+
console.print("[dim]Goodbye![/dim]")
|
|
229
|
+
break
|
|
230
|
+
|
|
231
|
+
if user_input.lower() == "/new":
|
|
232
|
+
# New session: new thread; workspace only changes if not fixed
|
|
233
|
+
if not workspace_fixed:
|
|
234
|
+
workspace_dir = _create_session_workspace()
|
|
235
|
+
console.print("[dim]Loading new session...[/dim]")
|
|
236
|
+
agent = _load_agent(workspace_dir=workspace_dir)
|
|
237
|
+
thread_id = str(uuid.uuid4())
|
|
238
|
+
console.print(f"[green]New session:[/green] [yellow]{thread_id}[/yellow]")
|
|
239
|
+
if workspace_dir:
|
|
240
|
+
console.print(f"[dim]Workspace:[/dim] [cyan]{_shorten_path(workspace_dir)}[/cyan]\n")
|
|
241
|
+
continue
|
|
242
|
+
|
|
243
|
+
if user_input.lower() == "/thread":
|
|
244
|
+
console.print(f"[dim]Thread:[/dim] [yellow]{thread_id}[/yellow]")
|
|
245
|
+
if workspace_dir:
|
|
246
|
+
console.print(f"[dim]Workspace:[/dim] [cyan]{_shorten_path(workspace_dir)}[/cyan]")
|
|
247
|
+
if memory_dir:
|
|
248
|
+
console.print(f"[dim]Memory dir:[/dim] [cyan]{_shorten_path(memory_dir)}[/cyan]")
|
|
249
|
+
console.print()
|
|
250
|
+
continue
|
|
251
|
+
|
|
252
|
+
if user_input.lower() == "/skills":
|
|
253
|
+
_cmd_list_skills()
|
|
254
|
+
continue
|
|
255
|
+
|
|
256
|
+
if user_input.lower().startswith("/install-skill"):
|
|
257
|
+
source = user_input[len("/install-skill"):].strip()
|
|
258
|
+
_cmd_install_skill(source)
|
|
259
|
+
continue
|
|
260
|
+
|
|
261
|
+
if user_input.lower().startswith("/uninstall-skill"):
|
|
262
|
+
name = user_input[len("/uninstall-skill"):].strip()
|
|
263
|
+
_cmd_uninstall_skill(name)
|
|
264
|
+
continue
|
|
265
|
+
|
|
266
|
+
# Stream agent response
|
|
267
|
+
console.print()
|
|
268
|
+
_run_streaming(agent, user_input, thread_id, show_thinking, interactive=True)
|
|
269
|
+
_print_separator()
|
|
270
|
+
|
|
271
|
+
except KeyboardInterrupt:
|
|
272
|
+
console.print("\n[dim]Goodbye![/dim]")
|
|
273
|
+
break
|
|
274
|
+
except Exception as e:
|
|
275
|
+
console.print(f"[red]Error: {e}[/red]")
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
def cmd_run(agent: Any, prompt: str, thread_id: str | None = None, show_thinking: bool = True, workspace_dir: str | None = None) -> None:
|
|
279
|
+
"""Single-shot execution with streaming display.
|
|
280
|
+
|
|
281
|
+
Args:
|
|
282
|
+
agent: Compiled agent graph
|
|
283
|
+
prompt: User prompt
|
|
284
|
+
thread_id: Optional thread ID (generates new one if None)
|
|
285
|
+
show_thinking: Whether to display thinking panels
|
|
286
|
+
workspace_dir: Per-session workspace directory path
|
|
287
|
+
"""
|
|
288
|
+
thread_id = thread_id or str(uuid.uuid4())
|
|
289
|
+
|
|
290
|
+
width = console.size.width
|
|
291
|
+
sep = Text("\u2500" * width, style="dim")
|
|
292
|
+
console.print(sep)
|
|
293
|
+
console.print(Text(f"> {prompt}"))
|
|
294
|
+
console.print(sep)
|
|
295
|
+
console.print(f"[dim]Thread: {thread_id}[/dim]")
|
|
296
|
+
if workspace_dir:
|
|
297
|
+
console.print(f"[dim]Workspace: {_shorten_path(workspace_dir)}[/dim]")
|
|
298
|
+
console.print()
|
|
299
|
+
|
|
300
|
+
try:
|
|
301
|
+
_run_streaming(agent, prompt, thread_id, show_thinking, interactive=False)
|
|
302
|
+
except Exception as e:
|
|
303
|
+
console.print(f"[red]Error: {e}[/red]")
|
|
304
|
+
raise
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
# =============================================================================
|
|
308
|
+
# Agent loading helpers
|
|
309
|
+
# =============================================================================
|
|
310
|
+
|
|
311
|
+
def _create_session_workspace() -> str:
|
|
312
|
+
"""Create a per-session workspace directory and return its path."""
|
|
313
|
+
session_id = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
314
|
+
workspace_dir = str(new_run_dir(session_id))
|
|
315
|
+
os.makedirs(workspace_dir, exist_ok=True)
|
|
316
|
+
return workspace_dir
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
def _load_agent(workspace_dir: str | None = None):
|
|
320
|
+
"""Load the CLI agent (with InMemorySaver checkpointer for multi-turn).
|
|
321
|
+
|
|
322
|
+
Args:
|
|
323
|
+
workspace_dir: Optional per-session workspace directory.
|
|
324
|
+
"""
|
|
325
|
+
from .EvoScientist import create_cli_agent
|
|
326
|
+
return create_cli_agent(workspace_dir=workspace_dir)
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
# =============================================================================
|
|
330
|
+
# Typer app
|
|
331
|
+
# =============================================================================
|
|
332
|
+
|
|
333
|
+
app = typer.Typer(no_args_is_help=False, add_completion=False)
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
@app.callback(invoke_without_command=True)
|
|
337
|
+
def _main_callback(
|
|
338
|
+
ctx: typer.Context,
|
|
339
|
+
prompt: Optional[str] = typer.Argument(None, help="Query to execute (single-shot mode)"),
|
|
340
|
+
interactive: bool = typer.Option(False, "-i", "--interactive", help="Interactive conversation mode"),
|
|
341
|
+
thread_id: Optional[str] = typer.Option(None, "--thread-id", help="Thread ID for conversation persistence"),
|
|
342
|
+
no_thinking: bool = typer.Option(False, "--no-thinking", help="Disable thinking display"),
|
|
343
|
+
workdir: Optional[str] = typer.Option(None, "--workdir", help="Override workspace directory for this session"),
|
|
344
|
+
use_cwd: bool = typer.Option(False, "--use-cwd", help="Use current working directory as workspace"),
|
|
345
|
+
):
|
|
346
|
+
"""EvoScientist Agent - AI-powered research & code execution CLI."""
|
|
347
|
+
from dotenv import load_dotenv # type: ignore[import-untyped]
|
|
348
|
+
load_dotenv(override=True)
|
|
349
|
+
|
|
350
|
+
show_thinking = not no_thinking
|
|
351
|
+
|
|
352
|
+
if workdir and use_cwd:
|
|
353
|
+
raise typer.BadParameter("Use either --workdir or --use-cwd, not both.")
|
|
354
|
+
|
|
355
|
+
ensure_dirs()
|
|
356
|
+
|
|
357
|
+
# Resolve workspace directory for this session
|
|
358
|
+
if use_cwd:
|
|
359
|
+
workspace_dir = os.getcwd()
|
|
360
|
+
workspace_fixed = True
|
|
361
|
+
elif workdir:
|
|
362
|
+
workspace_dir = os.path.abspath(os.path.expanduser(workdir))
|
|
363
|
+
os.makedirs(workspace_dir, exist_ok=True)
|
|
364
|
+
workspace_fixed = True
|
|
365
|
+
else:
|
|
366
|
+
workspace_dir = _create_session_workspace()
|
|
367
|
+
workspace_fixed = False
|
|
368
|
+
|
|
369
|
+
# Load agent with session workspace
|
|
370
|
+
console.print("[dim]Loading agent...[/dim]")
|
|
371
|
+
agent = _load_agent(workspace_dir=workspace_dir)
|
|
372
|
+
|
|
373
|
+
if interactive:
|
|
374
|
+
cmd_interactive(
|
|
375
|
+
agent,
|
|
376
|
+
show_thinking=show_thinking,
|
|
377
|
+
workspace_dir=workspace_dir,
|
|
378
|
+
workspace_fixed=workspace_fixed,
|
|
379
|
+
)
|
|
380
|
+
elif prompt:
|
|
381
|
+
cmd_run(agent, prompt, thread_id=thread_id, show_thinking=show_thinking, workspace_dir=workspace_dir)
|
|
382
|
+
else:
|
|
383
|
+
# Default: interactive mode
|
|
384
|
+
cmd_interactive(
|
|
385
|
+
agent,
|
|
386
|
+
show_thinking=show_thinking,
|
|
387
|
+
workspace_dir=workspace_dir,
|
|
388
|
+
workspace_fixed=workspace_fixed,
|
|
389
|
+
)
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
def _configure_logging():
|
|
393
|
+
"""Configure logging with warning symbols for better visibility."""
|
|
394
|
+
from rich.logging import RichHandler
|
|
395
|
+
|
|
396
|
+
class DimWarningHandler(RichHandler):
|
|
397
|
+
"""Custom handler that renders warnings in dim style."""
|
|
398
|
+
|
|
399
|
+
def emit(self, record: logging.LogRecord) -> None:
|
|
400
|
+
if record.levelno == logging.WARNING:
|
|
401
|
+
# Use Rich console to print dim warning
|
|
402
|
+
msg = record.getMessage()
|
|
403
|
+
console.print(f"[dim yellow]\u26a0\ufe0f Warning:[/dim yellow] [dim]{msg}[/dim]")
|
|
404
|
+
else:
|
|
405
|
+
super().emit(record)
|
|
406
|
+
|
|
407
|
+
# Configure root logger to use our handler for WARNING and above
|
|
408
|
+
handler = DimWarningHandler(console=console, show_time=False, show_path=False, show_level=False)
|
|
409
|
+
handler.setLevel(logging.WARNING)
|
|
410
|
+
|
|
411
|
+
# Apply to root logger (catches all loggers including deepagents)
|
|
412
|
+
root_logger = logging.getLogger()
|
|
413
|
+
# Remove existing handlers to avoid duplicate output
|
|
414
|
+
for h in root_logger.handlers[:]:
|
|
415
|
+
root_logger.removeHandler(h)
|
|
416
|
+
root_logger.addHandler(handler)
|
|
417
|
+
root_logger.setLevel(logging.WARNING)
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
def main():
|
|
421
|
+
"""CLI entry point — delegates to the Typer app."""
|
|
422
|
+
_configure_logging()
|
|
423
|
+
app()
|
|
424
|
+
|
|
425
|
+
|
|
426
|
+
if __name__ == "__main__":
|
|
427
|
+
main()
|