evo-cli 0.10.2__tar.gz → 0.10.3__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.
- {evo_cli-0.10.2 → evo_cli-0.10.3}/PKG-INFO +1 -1
- evo_cli-0.10.3/evo_cli/VERSION +1 -0
- {evo_cli-0.10.2 → evo_cli-0.10.3}/evo_cli/commands/opencode.py +62 -6
- {evo_cli-0.10.2 → evo_cli-0.10.3}/evo_cli/mcp_registry.py +5 -5
- {evo_cli-0.10.2 → evo_cli-0.10.3}/evo_cli.egg-info/PKG-INFO +1 -1
- {evo_cli-0.10.2 → evo_cli-0.10.3}/tests/test_opencode.py +26 -3
- evo_cli-0.10.2/evo_cli/VERSION +0 -1
- {evo_cli-0.10.2 → evo_cli-0.10.3}/Containerfile +0 -0
- {evo_cli-0.10.2 → evo_cli-0.10.3}/HISTORY.md +0 -0
- {evo_cli-0.10.2 → evo_cli-0.10.3}/LICENSE +0 -0
- {evo_cli-0.10.2 → evo_cli-0.10.3}/MANIFEST.in +0 -0
- {evo_cli-0.10.2 → evo_cli-0.10.3}/README.md +0 -0
- {evo_cli-0.10.2 → evo_cli-0.10.3}/evo_cli/__init__.py +0 -0
- {evo_cli-0.10.2 → evo_cli-0.10.3}/evo_cli/__main__.py +0 -0
- {evo_cli-0.10.2 → evo_cli-0.10.3}/evo_cli/base.py +0 -0
- {evo_cli-0.10.2 → evo_cli-0.10.3}/evo_cli/cli.py +0 -0
- {evo_cli-0.10.2 → evo_cli-0.10.3}/evo_cli/commands/__init__.py +0 -0
- {evo_cli-0.10.2 → evo_cli-0.10.3}/evo_cli/commands/cloudflare.py +0 -0
- {evo_cli-0.10.2 → evo_cli-0.10.3}/evo_cli/commands/fix_claude.py +0 -0
- {evo_cli-0.10.2 → evo_cli-0.10.3}/evo_cli/commands/gdrive.py +0 -0
- {evo_cli-0.10.2 → evo_cli-0.10.3}/evo_cli/commands/localproxy.py +0 -0
- {evo_cli-0.10.2 → evo_cli-0.10.3}/evo_cli/commands/mcp.py +0 -0
- {evo_cli-0.10.2 → evo_cli-0.10.3}/evo_cli/commands/miniconda.py +0 -0
- {evo_cli-0.10.2 → evo_cli-0.10.3}/evo_cli/commands/netcheck.py +0 -0
- {evo_cli-0.10.2 → evo_cli-0.10.3}/evo_cli/commands/plantuml.py +0 -0
- {evo_cli-0.10.2 → evo_cli-0.10.3}/evo_cli/commands/site2s.py +0 -0
- {evo_cli-0.10.2 → evo_cli-0.10.3}/evo_cli/commands/ssh.py +0 -0
- {evo_cli-0.10.2 → evo_cli-0.10.3}/evo_cli/commands/sysmon.py +0 -0
- {evo_cli-0.10.2 → evo_cli-0.10.3}/evo_cli/commands/wifi.py +0 -0
- {evo_cli-0.10.2 → evo_cli-0.10.3}/evo_cli/console.py +0 -0
- {evo_cli-0.10.2 → evo_cli-0.10.3}/evo_cli.egg-info/SOURCES.txt +0 -0
- {evo_cli-0.10.2 → evo_cli-0.10.3}/evo_cli.egg-info/dependency_links.txt +0 -0
- {evo_cli-0.10.2 → evo_cli-0.10.3}/evo_cli.egg-info/entry_points.txt +0 -0
- {evo_cli-0.10.2 → evo_cli-0.10.3}/evo_cli.egg-info/requires.txt +0 -0
- {evo_cli-0.10.2 → evo_cli-0.10.3}/evo_cli.egg-info/top_level.txt +0 -0
- {evo_cli-0.10.2 → evo_cli-0.10.3}/pyproject.toml +0 -0
- {evo_cli-0.10.2 → evo_cli-0.10.3}/setup.cfg +0 -0
- {evo_cli-0.10.2 → evo_cli-0.10.3}/tests/__init__.py +0 -0
- {evo_cli-0.10.2 → evo_cli-0.10.3}/tests/test_cli.py +0 -0
- {evo_cli-0.10.2 → evo_cli-0.10.3}/tests/test_console.py +0 -0
- {evo_cli-0.10.2 → evo_cli-0.10.3}/tests/test_fix_claude.py +0 -0
- {evo_cli-0.10.2 → evo_cli-0.10.3}/tests/test_mcp.py +0 -0
- {evo_cli-0.10.2 → evo_cli-0.10.3}/tests/test_plantuml.py +0 -0
- {evo_cli-0.10.2 → evo_cli-0.10.3}/tests/test_sysmon.py +0 -0
- {evo_cli-0.10.2 → evo_cli-0.10.3}/tests/test_wifi.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.10.3
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Install and configure OpenCode with common MCP servers."""
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
|
+
import os
|
|
4
5
|
import platform
|
|
5
6
|
import shutil
|
|
6
7
|
import subprocess
|
|
@@ -22,7 +23,13 @@ EPILOG = Text.from_markup(
|
|
|
22
23
|
|
|
23
24
|
# Servers bootstrapped by `evo setup opencode`, pulled from the shared library
|
|
24
25
|
# (evo_cli/mcp_registry.py). Add more servers anytime with `evo mcp add <name>`.
|
|
25
|
-
|
|
26
|
+
# Web search is handled by OpenCode's native Exa-backed `websearch` tool (enabled
|
|
27
|
+
# via OPENCODE_ENABLE_EXA, no API key), not a scraping MCP server.
|
|
28
|
+
DEFAULT_MCP_SERVERS = opencode_servers("playwright")
|
|
29
|
+
|
|
30
|
+
# OpenCode's built-in `websearch` tool only activates for the OpenCode provider or
|
|
31
|
+
# when this env var is truthy, so custom providers need it set explicitly.
|
|
32
|
+
EXA_ENV_VAR = "OPENCODE_ENABLE_EXA"
|
|
26
33
|
|
|
27
34
|
|
|
28
35
|
def _local_mcp_commands():
|
|
@@ -323,6 +330,51 @@ def verify_mcp_servers():
|
|
|
323
330
|
warning(f"Could not verify MCP server [accent]{name}[/accent]: {exc}")
|
|
324
331
|
|
|
325
332
|
|
|
333
|
+
def get_shell_rc_path():
|
|
334
|
+
"""Return the shell rc file to wire env vars into, based on the active shell."""
|
|
335
|
+
shell = os.environ.get("SHELL", "")
|
|
336
|
+
home = Path.home()
|
|
337
|
+
if shell.endswith("zsh"):
|
|
338
|
+
return home / ".zshrc"
|
|
339
|
+
if shell.endswith("bash"):
|
|
340
|
+
# macOS login shells read .bash_profile; Linux reads .bashrc.
|
|
341
|
+
return home / (".bash_profile" if platform.system() == "Darwin" else ".bashrc")
|
|
342
|
+
if shell.endswith("fish"):
|
|
343
|
+
return home / ".config" / "fish" / "config.fish"
|
|
344
|
+
return home / ".profile"
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
def enable_exa_websearch():
|
|
348
|
+
"""Enable OpenCode's built-in Exa web search via the OPENCODE_ENABLE_EXA env var.
|
|
349
|
+
|
|
350
|
+
OpenCode ships a native ``websearch`` tool backed by Exa's hosted MCP service
|
|
351
|
+
(no API key needed), but it is only active when using the OpenCode provider or
|
|
352
|
+
when ``OPENCODE_ENABLE_EXA`` is truthy. Custom providers therefore need the env
|
|
353
|
+
var, so wire it into the shell rc idempotently.
|
|
354
|
+
"""
|
|
355
|
+
step("Enabling Exa web search")
|
|
356
|
+
if is_windows():
|
|
357
|
+
info(f"Set the {EXA_ENV_VAR}=1 environment variable to enable web search.")
|
|
358
|
+
info("PowerShell: [accent]setx OPENCODE_ENABLE_EXA 1[/accent]")
|
|
359
|
+
return
|
|
360
|
+
|
|
361
|
+
rc_path = get_shell_rc_path()
|
|
362
|
+
if rc_path.exists() and EXA_ENV_VAR in rc_path.read_text(encoding="utf-8"):
|
|
363
|
+
info(f"{EXA_ENV_VAR} already set in [accent]{rc_path}[/accent]; skipping")
|
|
364
|
+
success("Exa web search already enabled")
|
|
365
|
+
return
|
|
366
|
+
|
|
367
|
+
rc_path.parent.mkdir(parents=True, exist_ok=True)
|
|
368
|
+
block = (
|
|
369
|
+
"\n# opencode web search (Exa native, no API key) - added by evo setup opencode\n"
|
|
370
|
+
f"export {EXA_ENV_VAR}=1\n"
|
|
371
|
+
)
|
|
372
|
+
with rc_path.open("a", encoding="utf-8") as fh:
|
|
373
|
+
fh.write(block)
|
|
374
|
+
success(f"Enabled Exa web search in [accent]{rc_path}[/accent]")
|
|
375
|
+
info(f"Run [accent]source {rc_path}[/accent] or open a new terminal to apply.")
|
|
376
|
+
|
|
377
|
+
|
|
326
378
|
def run_setup_opencode(global_only, project, skip_install=False):
|
|
327
379
|
step("evo setup opencode")
|
|
328
380
|
|
|
@@ -334,6 +386,7 @@ def run_setup_opencode(global_only, project, skip_install=False):
|
|
|
334
386
|
|
|
335
387
|
if not skip_install:
|
|
336
388
|
install_opencode(npm_cmd)
|
|
389
|
+
enable_exa_websearch()
|
|
337
390
|
install_mcp_servers()
|
|
338
391
|
install_playwright_browsers()
|
|
339
392
|
|
|
@@ -357,8 +410,9 @@ def run_setup_opencode(global_only, project, skip_install=False):
|
|
|
357
410
|
)
|
|
358
411
|
console.print(
|
|
359
412
|
Panel(
|
|
360
|
-
|
|
361
|
-
f"
|
|
413
|
+
"OpenCode web search enabled via Exa (native, no API key); "
|
|
414
|
+
f"playwright MCP server configured for browser automation.\n\n{paths_text}\n\n"
|
|
415
|
+
f"{opencode_note} Open a new terminal (or `source` your shell rc), then restart OpenCode.",
|
|
362
416
|
title="setup opencode complete",
|
|
363
417
|
border_style="success",
|
|
364
418
|
expand=False,
|
|
@@ -387,9 +441,11 @@ def setup_opencode(global_only, skip_install, project):
|
|
|
387
441
|
|
|
388
442
|
This command bootstraps a fresh machine with the same OpenCode + MCP setup
|
|
389
443
|
used across devices. It installs the OpenCode CLI (via npm, unless
|
|
390
|
-
--skip-install)
|
|
391
|
-
|
|
392
|
-
|
|
444
|
+
--skip-install), enables OpenCode's native Exa-backed web search (via the
|
|
445
|
+
OPENCODE_ENABLE_EXA env var, no API key required), installs the playwright MCP
|
|
446
|
+
server for browser automation, downloads the Playwright Chromium browser, and
|
|
447
|
+
writes both global (~/.config/opencode/opencode.jsonc) and project-level
|
|
448
|
+
(opencode.json) configs.
|
|
393
449
|
|
|
394
450
|
No credentials are bundled; only public MCP server references are added.
|
|
395
451
|
"""
|
|
@@ -22,12 +22,12 @@ MCP_REGISTRY = {
|
|
|
22
22
|
"homepage": "https://github.com/microsoft/playwright-mcp",
|
|
23
23
|
"description": "Playwright (Microsoft) - drive a real browser: navigate, click, snapshot.",
|
|
24
24
|
},
|
|
25
|
-
"
|
|
26
|
-
"transport": "
|
|
27
|
-
"
|
|
25
|
+
"exa": {
|
|
26
|
+
"transport": "http",
|
|
27
|
+
"url": "https://mcp.exa.ai/mcp",
|
|
28
28
|
"category": "search",
|
|
29
|
-
"homepage": "https://
|
|
30
|
-
"description": "
|
|
29
|
+
"homepage": "https://exa.ai/docs/reference/exa-mcp",
|
|
30
|
+
"description": "Exa - AI web search (hosted MCP, free tier, no API key).",
|
|
31
31
|
},
|
|
32
32
|
"sequential-thinking": {
|
|
33
33
|
"transport": "stdio",
|
|
@@ -7,8 +7,10 @@ from click.testing import CliRunner
|
|
|
7
7
|
from evo_cli.cli import cli
|
|
8
8
|
from evo_cli.commands.opencode import (
|
|
9
9
|
DEFAULT_MCP_SERVERS,
|
|
10
|
+
EXA_ENV_VAR,
|
|
10
11
|
configure_opencode_global,
|
|
11
12
|
configure_opencode_project,
|
|
13
|
+
enable_exa_websearch,
|
|
12
14
|
install_mcp_servers,
|
|
13
15
|
install_opencode,
|
|
14
16
|
load_jsonc,
|
|
@@ -55,7 +57,6 @@ def test_merge_mcp_config_preserves_existing():
|
|
|
55
57
|
existing = {"mcp": {"playwright": {"enabled": False}}}
|
|
56
58
|
merged = merge_mcp_config(existing, DEFAULT_MCP_SERVERS)
|
|
57
59
|
assert merged["mcp"]["playwright"]["enabled"] is False
|
|
58
|
-
assert merged["mcp"]["google-search"]["enabled"] is True
|
|
59
60
|
|
|
60
61
|
|
|
61
62
|
def test_configure_opencode_global(tmp_path, monkeypatch):
|
|
@@ -66,14 +67,33 @@ def test_configure_opencode_global(tmp_path, monkeypatch):
|
|
|
66
67
|
path = configure_opencode_global()
|
|
67
68
|
data = load_jsonc(path)
|
|
68
69
|
assert "mcp" in data
|
|
69
|
-
assert set(data["mcp"].keys()) == {"
|
|
70
|
+
assert set(data["mcp"].keys()) == {"playwright"}
|
|
70
71
|
|
|
71
72
|
|
|
72
73
|
def test_configure_opencode_project(tmp_path):
|
|
73
74
|
path = configure_opencode_project(tmp_path)
|
|
74
75
|
data = load_jsonc(path)
|
|
75
76
|
assert "mcp" in data
|
|
76
|
-
assert set(data["mcp"].keys()) == {"
|
|
77
|
+
assert set(data["mcp"].keys()) == {"playwright"}
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def test_enable_exa_websearch_writes_export(tmp_path, monkeypatch):
|
|
81
|
+
rc = tmp_path / ".zshrc"
|
|
82
|
+
monkeypatch.setattr("evo_cli.commands.opencode.is_windows", lambda: False)
|
|
83
|
+
monkeypatch.setattr("evo_cli.commands.opencode.get_shell_rc_path", lambda: rc)
|
|
84
|
+
|
|
85
|
+
enable_exa_websearch()
|
|
86
|
+
assert f"export {EXA_ENV_VAR}=1" in rc.read_text()
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def test_enable_exa_websearch_is_idempotent(tmp_path, monkeypatch):
|
|
90
|
+
rc = tmp_path / ".zshrc"
|
|
91
|
+
monkeypatch.setattr("evo_cli.commands.opencode.is_windows", lambda: False)
|
|
92
|
+
monkeypatch.setattr("evo_cli.commands.opencode.get_shell_rc_path", lambda: rc)
|
|
93
|
+
|
|
94
|
+
enable_exa_websearch()
|
|
95
|
+
enable_exa_websearch()
|
|
96
|
+
assert rc.read_text().count(f"export {EXA_ENV_VAR}=1") == 1
|
|
77
97
|
|
|
78
98
|
|
|
79
99
|
def test_install_mcp_servers_detaches_stdin_and_sets_timeout(monkeypatch):
|
|
@@ -141,6 +161,7 @@ def test_setup_opencode_skip_install(runner, tmp_path, monkeypatch):
|
|
|
141
161
|
monkeypatch.setattr("evo_cli.commands.opencode.install_mcp_servers", lambda: None)
|
|
142
162
|
monkeypatch.setattr("evo_cli.commands.opencode.install_playwright_browsers", lambda: None)
|
|
143
163
|
monkeypatch.setattr("evo_cli.commands.opencode.verify_mcp_servers", lambda: None)
|
|
164
|
+
monkeypatch.setattr("evo_cli.commands.opencode.enable_exa_websearch", lambda: None)
|
|
144
165
|
|
|
145
166
|
result = runner.invoke(cli, ["setup", "opencode", "--global-only", "--skip-install"])
|
|
146
167
|
assert result.exit_code == 0
|
|
@@ -159,6 +180,7 @@ def test_setup_opencode_writes_global_and_project(runner, tmp_path, monkeypatch)
|
|
|
159
180
|
monkeypatch.setattr("evo_cli.commands.opencode.install_mcp_servers", lambda: None)
|
|
160
181
|
monkeypatch.setattr("evo_cli.commands.opencode.install_playwright_browsers", lambda: None)
|
|
161
182
|
monkeypatch.setattr("evo_cli.commands.opencode.verify_mcp_servers", lambda: None)
|
|
183
|
+
monkeypatch.setattr("evo_cli.commands.opencode.enable_exa_websearch", lambda: None)
|
|
162
184
|
|
|
163
185
|
result = runner.invoke(cli, ["setup", "opencode", "--project", str(tmp_path)])
|
|
164
186
|
assert result.exit_code == 0
|
|
@@ -180,6 +202,7 @@ def test_setup_opencode_global_only(runner, tmp_path, monkeypatch):
|
|
|
180
202
|
monkeypatch.setattr("evo_cli.commands.opencode.install_mcp_servers", lambda: None)
|
|
181
203
|
monkeypatch.setattr("evo_cli.commands.opencode.install_playwright_browsers", lambda: None)
|
|
182
204
|
monkeypatch.setattr("evo_cli.commands.opencode.verify_mcp_servers", lambda: None)
|
|
205
|
+
monkeypatch.setattr("evo_cli.commands.opencode.enable_exa_websearch", lambda: None)
|
|
183
206
|
|
|
184
207
|
project = tmp_path / "project"
|
|
185
208
|
project.mkdir()
|
evo_cli-0.10.2/evo_cli/VERSION
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
0.10.2
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|