fix: correct formatting in docstrings and enhance checkbox rendering for installed items
This commit is contained in:
@@ -14,7 +14,7 @@ app.add_typer(config_app, name="config")
|
||||
|
||||
# MCP subcommand group
|
||||
_MCP_HELP = """\
|
||||
Configure and manage MCP servers.
|
||||
Configure and manage MCP servers
|
||||
|
||||
Examples:
|
||||
# Add a local MCP server (stdio auto-detected):
|
||||
|
||||
@@ -36,7 +36,7 @@ def onboard(
|
||||
help="Skip API key validation during setup"
|
||||
),
|
||||
):
|
||||
"""Interactive setup wizard for EvoScientist.
|
||||
"""Interactive setup wizard for EvoScientist
|
||||
|
||||
Guides you through configuring API keys, model selection,
|
||||
workspace settings, and agent parameters.
|
||||
@@ -51,14 +51,14 @@ def onboard(
|
||||
|
||||
@config_app.callback(invoke_without_command=True)
|
||||
def config_callback(ctx: typer.Context):
|
||||
"""Configuration management commands."""
|
||||
"""Configuration management commands"""
|
||||
if ctx.invoked_subcommand is None:
|
||||
config_list()
|
||||
|
||||
|
||||
@config_app.command("list")
|
||||
def config_list():
|
||||
"""List all configuration values."""
|
||||
"""List all configuration values"""
|
||||
from ..config import list_config, get_config_path
|
||||
|
||||
config_data = list_config()
|
||||
@@ -84,7 +84,7 @@ def config_list():
|
||||
|
||||
@config_app.command("get")
|
||||
def config_get(key: str = typer.Argument(..., help="Configuration key to get")):
|
||||
"""Get a single configuration value."""
|
||||
"""Get a single configuration value"""
|
||||
from ..config import get_config_value
|
||||
|
||||
value = get_config_value(key)
|
||||
@@ -108,7 +108,7 @@ def config_set(
|
||||
key: str = typer.Argument(..., help="Configuration key to set"),
|
||||
value: str = typer.Argument(..., help="New value"),
|
||||
):
|
||||
"""Set a single configuration value."""
|
||||
"""Set a single configuration value"""
|
||||
from ..config import set_config_value
|
||||
|
||||
if set_config_value(key, value):
|
||||
@@ -122,7 +122,7 @@ def config_set(
|
||||
def config_reset(
|
||||
yes: bool = typer.Option(False, "--yes", "-y", help="Skip confirmation prompt"),
|
||||
):
|
||||
"""Reset configuration to defaults."""
|
||||
"""Reset configuration to defaults"""
|
||||
from ..config import reset_config, get_config_path
|
||||
|
||||
config_path = get_config_path()
|
||||
@@ -143,7 +143,7 @@ def config_reset(
|
||||
|
||||
@config_app.command("path")
|
||||
def config_path():
|
||||
"""Show the configuration file path."""
|
||||
"""Show the configuration file path"""
|
||||
from ..config import get_config_path
|
||||
|
||||
path = get_config_path()
|
||||
@@ -158,14 +158,14 @@ def config_path():
|
||||
|
||||
@mcp_app.callback(invoke_without_command=True)
|
||||
def mcp_callback(ctx: typer.Context):
|
||||
"""MCP server management commands."""
|
||||
"""MCP server management commands"""
|
||||
if ctx.invoked_subcommand is None:
|
||||
mcp_list()
|
||||
|
||||
|
||||
@mcp_app.command("list")
|
||||
def mcp_list():
|
||||
"""List configured MCP servers."""
|
||||
"""List configured MCP servers"""
|
||||
_mcp_list_servers()
|
||||
|
||||
|
||||
@@ -173,7 +173,7 @@ def mcp_list():
|
||||
def mcp_config(
|
||||
name: Optional[str] = typer.Argument(None, help="Server name (omit to show all)"),
|
||||
):
|
||||
"""Show detailed configuration for MCP servers.
|
||||
"""Show detailed configuration for MCP servers
|
||||
|
||||
\b
|
||||
Examples:
|
||||
@@ -200,7 +200,7 @@ def mcp_add(
|
||||
env: Optional[list[str]] = typer.Option(None, "--env", help="Env var as KEY=VALUE for stdio (repeatable)"),
|
||||
env_ref: Optional[list[str]] = typer.Option(None, "--env-ref", help="Env var name as ${NAME} runtime ref (repeatable)"),
|
||||
):
|
||||
"""Add an MCP server to user config.
|
||||
"""Add an MCP server to user config
|
||||
|
||||
\b
|
||||
Transport is auto-detected: URLs default to http, commands default to stdio.
|
||||
@@ -249,7 +249,7 @@ def mcp_edit(
|
||||
header: Optional[list[str]] = typer.Option(None, "--header", "-H", help="HTTP header as Key:Value (repeatable)"),
|
||||
env: Optional[list[str]] = typer.Option(None, "--env", help="Env var as KEY=VALUE for stdio (repeatable)"),
|
||||
):
|
||||
"""Edit an existing MCP server in user config.
|
||||
"""Edit an existing MCP server in user config
|
||||
|
||||
\b
|
||||
Examples:
|
||||
@@ -278,7 +278,7 @@ def mcp_edit(
|
||||
def mcp_remove(
|
||||
name: str = typer.Argument(..., help="Server name to remove"),
|
||||
):
|
||||
"""Remove an MCP server from user config."""
|
||||
"""Remove an MCP server from user config"""
|
||||
if not _mcp_remove_server(name, show_reload_hint=False):
|
||||
raise typer.Exit(1)
|
||||
|
||||
@@ -308,7 +308,7 @@ def _main_callback(
|
||||
use_cwd: bool = typer.Option(False, "--use-cwd", help="Use current working directory as workspace"),
|
||||
no_thinking: bool = typer.Option(False, "--no-thinking", help="Disable thinking display"),
|
||||
):
|
||||
"""EvoScientist Agent - AI-powered research & code execution CLI."""
|
||||
"""EvoScientist Agent - AI-powered research & code execution CLI"""
|
||||
# If a subcommand was invoked, don't run the default behavior
|
||||
if ctx.invoked_subcommand is not None:
|
||||
return
|
||||
|
||||
+82
-21
@@ -9,6 +9,7 @@ from __future__ import annotations
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import questionary
|
||||
from prompt_toolkit.formatted_text import FormattedText
|
||||
@@ -42,6 +43,7 @@ WIZARD_STYLE = Style.from_dict({
|
||||
"highlighted": "noreverse bold", # No background, bold text
|
||||
"selected": "fg:#4caf50 bold", # Green ● indicator
|
||||
"separator": "fg:#6c6c6c", # Dim separator
|
||||
"disabled": "fg:#858585", # Dim disabled indicator (-)
|
||||
"instruction": "fg:#858585", # Dim instructions
|
||||
"text": "fg:#858585", # Dim gray ○ and unselected text
|
||||
})
|
||||
@@ -56,6 +58,38 @@ CONFIRM_STYLE = Style.from_dict({
|
||||
|
||||
QMARK = "❯"
|
||||
|
||||
# Installed-item indicator style for disabled checkbox choices.
|
||||
_INSTALLED_INDICATOR = ("fg:#4caf50", "✓ ")
|
||||
|
||||
|
||||
def _checkbox_ask(choices, message: str, **kwargs):
|
||||
"""``questionary.checkbox`` that renders disabled items with ✓ instead of ``-``.
|
||||
|
||||
Temporarily patches the rendering so the hard-coded ``"- "`` prefix for
|
||||
disabled choices is replaced by a green ``"✓ "`` — keeping alignment with
|
||||
the ``○`` indicator of normal choices.
|
||||
"""
|
||||
from questionary.prompts.common import InquirerControl
|
||||
|
||||
original = InquirerControl._get_choice_tokens
|
||||
|
||||
def _patched(self):
|
||||
tokens = original(self)
|
||||
return [
|
||||
_INSTALLED_INDICATOR
|
||||
if cls == "class:disabled" and text == "- "
|
||||
else (cls, text)
|
||||
for cls, text in tokens
|
||||
]
|
||||
|
||||
InquirerControl._get_choice_tokens = _patched
|
||||
try:
|
||||
return questionary.checkbox(
|
||||
message, choices=choices, style=WIZARD_STYLE, qmark=QMARK, **kwargs,
|
||||
).ask()
|
||||
finally:
|
||||
InquirerControl._get_choice_tokens = original
|
||||
|
||||
STEPS = ["Provider", "API Key", "Model", "Tavily Key", "Workspace", "Parameters", "Skills", "MCP Servers", "Channels"]
|
||||
|
||||
|
||||
@@ -757,24 +791,53 @@ def _ensure_npx(reason: str) -> bool:
|
||||
def _step_skills() -> list[str]:
|
||||
"""Step 7: Optionally install recommended skills.
|
||||
|
||||
Shows checkbox first. If user selects nothing, checks npx as an
|
||||
easter egg — confirms skill discovery is available, or offers to
|
||||
install Node.js if missing.
|
||||
Shows checkbox first. Already-installed skills are shown as disabled
|
||||
so users don't accidentally reinstall them. If user selects nothing,
|
||||
checks npx as an easter egg — confirms skill discovery is available,
|
||||
or offers to install Node.js if missing.
|
||||
|
||||
Returns:
|
||||
List of skill sources that were selected (empty if skipped).
|
||||
"""
|
||||
choices = [
|
||||
Choice(title=skill["label"], value=skill["source"])
|
||||
for skill in _RECOMMENDED_SKILLS
|
||||
]
|
||||
from .paths import USER_SKILLS_DIR
|
||||
|
||||
selected = questionary.checkbox(
|
||||
"Install predefined skills:",
|
||||
choices=choices,
|
||||
style=WIZARD_STYLE,
|
||||
qmark=QMARK,
|
||||
).ask()
|
||||
# Collect names of already-installed user skills
|
||||
skills_dir = Path(USER_SKILLS_DIR)
|
||||
installed_names: set[str] = set()
|
||||
if skills_dir.exists():
|
||||
installed_names = {e.name for e in skills_dir.iterdir() if e.is_dir()}
|
||||
|
||||
def _hint_name(source: str) -> str:
|
||||
"""Derive expected skill directory name from source URL."""
|
||||
if "@" in source and "://" not in source:
|
||||
return source.split("@", 1)[1].strip()
|
||||
return source.rstrip("/").rsplit("/", 1)[-1]
|
||||
|
||||
choices = []
|
||||
for skill in _RECOMMENDED_SKILLS:
|
||||
if _hint_name(skill["source"]) in installed_names:
|
||||
choices.append(
|
||||
Choice(
|
||||
title=[
|
||||
("", skill["label"]),
|
||||
("class:instruction", " (already installed)"),
|
||||
],
|
||||
value=skill["source"],
|
||||
disabled=True,
|
||||
)
|
||||
)
|
||||
else:
|
||||
choices.append(Choice(title=skill["label"], value=skill["source"]))
|
||||
|
||||
all_installed = all(
|
||||
_hint_name(skill["source"]) in installed_names
|
||||
for skill in _RECOMMENDED_SKILLS
|
||||
)
|
||||
if all_installed:
|
||||
console.print(" [green]✓ All recommended skills are already installed.[/green]")
|
||||
return []
|
||||
|
||||
selected = _checkbox_ask(choices, "Install predefined skills:")
|
||||
|
||||
if selected is None:
|
||||
raise KeyboardInterrupt()
|
||||
@@ -896,9 +959,12 @@ def _step_mcp_servers() -> list[str]:
|
||||
if srv["name"] in existing_config:
|
||||
choices.append(
|
||||
Choice(
|
||||
title=srv["label"],
|
||||
title=[
|
||||
("", srv["label"]),
|
||||
("class:instruction", " (already configured)"),
|
||||
],
|
||||
value=srv["name"],
|
||||
disabled="already configured",
|
||||
disabled=True,
|
||||
)
|
||||
)
|
||||
else:
|
||||
@@ -909,12 +975,7 @@ def _step_mcp_servers() -> list[str]:
|
||||
console.print(" [green]✓ All recommended MCP servers are already configured.[/green]")
|
||||
return []
|
||||
|
||||
selected = questionary.checkbox(
|
||||
"Install recommended MCP servers:",
|
||||
choices=choices,
|
||||
style=WIZARD_STYLE,
|
||||
qmark=QMARK,
|
||||
).ask()
|
||||
selected = _checkbox_ask(choices, "Install recommended MCP servers:")
|
||||
|
||||
if selected is None:
|
||||
raise KeyboardInterrupt()
|
||||
|
||||
Reference in New Issue
Block a user