hud-python 0.4.11__py3-none-any.whl → 0.4.13__py3-none-any.whl
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.
Potentially problematic release.
This version of hud-python might be problematic. Click here for more details.
- hud/__main__.py +8 -0
- hud/agents/base.py +7 -8
- hud/agents/langchain.py +2 -2
- hud/agents/tests/test_openai.py +3 -1
- hud/cli/__init__.py +114 -52
- hud/cli/build.py +121 -71
- hud/cli/debug.py +2 -2
- hud/cli/{mcp_server.py → dev.py} +101 -38
- hud/cli/eval.py +175 -90
- hud/cli/init.py +442 -64
- hud/cli/list_func.py +72 -71
- hud/cli/pull.py +1 -2
- hud/cli/push.py +35 -23
- hud/cli/remove.py +35 -41
- hud/cli/tests/test_analyze.py +2 -1
- hud/cli/tests/test_analyze_metadata.py +42 -49
- hud/cli/tests/test_build.py +28 -52
- hud/cli/tests/test_cursor.py +1 -1
- hud/cli/tests/test_debug.py +1 -1
- hud/cli/tests/test_list_func.py +75 -64
- hud/cli/tests/test_main_module.py +30 -0
- hud/cli/tests/test_mcp_server.py +3 -3
- hud/cli/tests/test_pull.py +30 -61
- hud/cli/tests/test_push.py +70 -89
- hud/cli/tests/test_registry.py +36 -38
- hud/cli/tests/test_utils.py +1 -1
- hud/cli/utils/__init__.py +1 -0
- hud/cli/{docker_utils.py → utils/docker.py} +36 -0
- hud/cli/{env_utils.py → utils/environment.py} +7 -7
- hud/cli/{interactive.py → utils/interactive.py} +91 -19
- hud/cli/{analyze_metadata.py → utils/metadata.py} +12 -8
- hud/cli/{registry.py → utils/registry.py} +28 -30
- hud/cli/{remote_runner.py → utils/remote_runner.py} +1 -1
- hud/cli/utils/runner.py +134 -0
- hud/cli/utils/server.py +250 -0
- hud/clients/base.py +1 -1
- hud/clients/fastmcp.py +5 -13
- hud/clients/mcp_use.py +6 -10
- hud/server/server.py +35 -5
- hud/shared/exceptions.py +11 -0
- hud/shared/tests/test_exceptions.py +22 -0
- hud/telemetry/tests/__init__.py +0 -0
- hud/telemetry/tests/test_replay.py +40 -0
- hud/telemetry/tests/test_trace.py +63 -0
- hud/tools/base.py +20 -3
- hud/tools/computer/hud.py +15 -6
- hud/tools/executors/tests/test_base_executor.py +27 -0
- hud/tools/response.py +12 -8
- hud/tools/tests/test_response.py +60 -0
- hud/tools/tests/test_tools_init.py +49 -0
- hud/utils/design.py +19 -8
- hud/utils/mcp.py +17 -5
- hud/utils/tests/test_mcp.py +112 -0
- hud/utils/tests/test_version.py +1 -1
- hud/version.py +1 -1
- {hud_python-0.4.11.dist-info → hud_python-0.4.13.dist-info}/METADATA +16 -13
- {hud_python-0.4.11.dist-info → hud_python-0.4.13.dist-info}/RECORD +62 -52
- hud/cli/runner.py +0 -160
- /hud/cli/{cursor.py → utils/cursor.py} +0 -0
- /hud/cli/{utils.py → utils/logging.py} +0 -0
- {hud_python-0.4.11.dist-info → hud_python-0.4.13.dist-info}/WHEEL +0 -0
- {hud_python-0.4.11.dist-info → hud_python-0.4.13.dist-info}/entry_points.txt +0 -0
- {hud_python-0.4.11.dist-info → hud_python-0.4.13.dist-info}/licenses/LICENSE +0 -0
hud/__main__.py
ADDED
hud/agents/base.py
CHANGED
|
@@ -306,7 +306,7 @@ class MCPAgent(ABC):
|
|
|
306
306
|
if decision == "STOP":
|
|
307
307
|
# Try to submit response through lifecycle tool
|
|
308
308
|
await self._maybe_submit_response(response, messages)
|
|
309
|
-
|
|
309
|
+
|
|
310
310
|
logger.info("Stopping execution")
|
|
311
311
|
final_response = response
|
|
312
312
|
break
|
|
@@ -487,7 +487,7 @@ class MCPAgent(ABC):
|
|
|
487
487
|
self._available_tools.append(tool)
|
|
488
488
|
# Simplified mapping - just tool name to tool
|
|
489
489
|
self._tool_map[tool.name] = tool
|
|
490
|
-
|
|
490
|
+
|
|
491
491
|
# Auto-detect response tool as a lifecycle tool
|
|
492
492
|
if tool.name == "response" and "response" not in self.lifecycle_tools:
|
|
493
493
|
logger.debug("Auto-detected 'response' tool as a lifecycle tool")
|
|
@@ -495,7 +495,7 @@ class MCPAgent(ABC):
|
|
|
495
495
|
|
|
496
496
|
async def _maybe_submit_response(self, response: AgentResponse, messages: list[Any]) -> None:
|
|
497
497
|
"""Submit response through lifecycle tool if available.
|
|
498
|
-
|
|
498
|
+
|
|
499
499
|
Args:
|
|
500
500
|
response: The agent's response
|
|
501
501
|
messages: The current message history (will be modified in-place)
|
|
@@ -506,17 +506,16 @@ class MCPAgent(ABC):
|
|
|
506
506
|
try:
|
|
507
507
|
# Call the response tool with the agent's response
|
|
508
508
|
response_tool_call = MCPToolCall(
|
|
509
|
-
name="response",
|
|
510
|
-
arguments={"response": response.content, "messages": messages}
|
|
509
|
+
name="response", arguments={"response": response.content, "messages": messages}
|
|
511
510
|
)
|
|
512
511
|
response_results = await self.call_tools(response_tool_call)
|
|
513
|
-
|
|
512
|
+
|
|
514
513
|
# Format and add the response tool results to messages
|
|
515
514
|
response_messages = await self.format_tool_results(
|
|
516
515
|
[response_tool_call], response_results
|
|
517
516
|
)
|
|
518
517
|
messages.extend(response_messages)
|
|
519
|
-
|
|
518
|
+
|
|
520
519
|
# Mark the task as done
|
|
521
520
|
logger.info("Response lifecycle tool executed, marking task as done")
|
|
522
521
|
except Exception as e:
|
|
@@ -579,7 +578,7 @@ class MCPAgent(ABC):
|
|
|
579
578
|
logger.warning("Failed to close auto-created trace: %s", e)
|
|
580
579
|
finally:
|
|
581
580
|
self._auto_trace_cm = None
|
|
582
|
-
|
|
581
|
+
|
|
583
582
|
# Clean up auto-created client
|
|
584
583
|
if self._auto_created_client and self.mcp_client:
|
|
585
584
|
try:
|
hud/agents/langchain.py
CHANGED
|
@@ -15,10 +15,10 @@ import hud
|
|
|
15
15
|
if TYPE_CHECKING:
|
|
16
16
|
from langchain.schema.language_model import BaseLanguageModel
|
|
17
17
|
from langchain_core.tools import BaseTool
|
|
18
|
-
from mcp_use.adapters.langchain_adapter import LangChainAdapter
|
|
18
|
+
from mcp_use.adapters.langchain_adapter import LangChainAdapter # type: ignore[attr-defined]
|
|
19
19
|
|
|
20
20
|
try:
|
|
21
|
-
from mcp_use.adapters.langchain_adapter import LangChainAdapter
|
|
21
|
+
from mcp_use.adapters.langchain_adapter import LangChainAdapter # type: ignore[attr-defined]
|
|
22
22
|
except ImportError:
|
|
23
23
|
LangChainAdapter = None # type: ignore[misc, assignment]
|
|
24
24
|
|
hud/agents/tests/test_openai.py
CHANGED
|
@@ -17,7 +17,9 @@ class TestOperatorAgent:
|
|
|
17
17
|
@pytest.fixture
|
|
18
18
|
def mock_mcp_client(self):
|
|
19
19
|
"""Create a mock MCP client."""
|
|
20
|
-
mcp_client =
|
|
20
|
+
mcp_client = AsyncMock()
|
|
21
|
+
# Set up the mcp_config attribute as a regular dict, not a coroutine
|
|
22
|
+
mcp_client.mcp_config = {"test_server": {"url": "http://test"}}
|
|
21
23
|
return mcp_client
|
|
22
24
|
|
|
23
25
|
@pytest.fixture
|
hud/cli/__init__.py
CHANGED
|
@@ -4,15 +4,15 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import asyncio
|
|
6
6
|
import json
|
|
7
|
-
import os
|
|
8
7
|
import sys
|
|
9
|
-
from pathlib import Path
|
|
8
|
+
from pathlib import Path
|
|
10
9
|
|
|
11
10
|
import typer
|
|
12
11
|
from rich.console import Console
|
|
13
12
|
from rich.panel import Panel
|
|
14
13
|
from rich.table import Table
|
|
15
14
|
|
|
15
|
+
from . import list_func as list_module
|
|
16
16
|
from .analyze import (
|
|
17
17
|
analyze_environment,
|
|
18
18
|
analyze_environment_from_config,
|
|
@@ -20,15 +20,14 @@ from .analyze import (
|
|
|
20
20
|
)
|
|
21
21
|
from .build import build_command
|
|
22
22
|
from .clone import clone_repository, get_clone_message, print_error, print_tutorial
|
|
23
|
-
from .cursor import get_cursor_config_path, list_cursor_servers, parse_cursor_config
|
|
24
23
|
from .debug import debug_mcp_stdio
|
|
24
|
+
from .dev import run_mcp_dev_server
|
|
25
25
|
from .init import create_environment
|
|
26
|
-
from . import list_func as list_module
|
|
27
|
-
from .mcp_server import run_mcp_dev_server
|
|
28
26
|
from .pull import pull_command
|
|
29
27
|
from .push import push_command
|
|
30
28
|
from .remove import remove_command
|
|
31
|
-
from .utils import
|
|
29
|
+
from .utils.cursor import get_cursor_config_path, list_cursor_servers, parse_cursor_config
|
|
30
|
+
from .utils.logging import CaptureLogger
|
|
32
31
|
|
|
33
32
|
# Create the main Typer app
|
|
34
33
|
app = typer.Typer(
|
|
@@ -113,7 +112,7 @@ def analyze(
|
|
|
113
112
|
asyncio.run(analyze_environment(docker_cmd, output_format, verbose))
|
|
114
113
|
else:
|
|
115
114
|
# Fast mode - analyze from metadata
|
|
116
|
-
from .
|
|
115
|
+
from .utils.metadata import analyze_from_metadata
|
|
117
116
|
|
|
118
117
|
asyncio.run(analyze_from_metadata(image, output_format, verbose))
|
|
119
118
|
else:
|
|
@@ -175,11 +174,17 @@ def debug(
|
|
|
175
174
|
hud debug . --max-phase 3 # Stop after phase 3
|
|
176
175
|
"""
|
|
177
176
|
# Import here to avoid circular imports
|
|
178
|
-
from .env_utils import get_image_name, is_environment_directory, build_environment, image_exists
|
|
179
177
|
from hud.utils.design import HUDDesign
|
|
180
|
-
|
|
178
|
+
|
|
179
|
+
from .utils.environment import (
|
|
180
|
+
build_environment,
|
|
181
|
+
get_image_name,
|
|
182
|
+
image_exists,
|
|
183
|
+
is_environment_directory,
|
|
184
|
+
)
|
|
185
|
+
|
|
181
186
|
design = HUDDesign()
|
|
182
|
-
|
|
187
|
+
|
|
183
188
|
# Determine the command to run
|
|
184
189
|
command = None
|
|
185
190
|
docker_args = []
|
|
@@ -202,18 +207,18 @@ def debug(
|
|
|
202
207
|
elif params:
|
|
203
208
|
first_param = params[0]
|
|
204
209
|
docker_args = params[1:] if len(params) > 1 else []
|
|
205
|
-
|
|
210
|
+
|
|
206
211
|
# Check if it's a directory
|
|
207
212
|
if Path(first_param).exists() and is_environment_directory(first_param):
|
|
208
213
|
# Directory mode - like hud dev
|
|
209
214
|
directory = first_param
|
|
210
|
-
|
|
215
|
+
|
|
211
216
|
# Get or generate image name
|
|
212
217
|
image_name, source = get_image_name(directory)
|
|
213
|
-
|
|
218
|
+
|
|
214
219
|
if source == "auto":
|
|
215
220
|
design.info(f"Auto-generated image name: {image_name}")
|
|
216
|
-
|
|
221
|
+
|
|
217
222
|
# Build if requested or if image doesn't exist
|
|
218
223
|
if build or not image_exists(image_name):
|
|
219
224
|
if not build and not image_exists(image_name):
|
|
@@ -221,11 +226,10 @@ def debug(
|
|
|
221
226
|
build = True
|
|
222
227
|
else:
|
|
223
228
|
raise typer.Exit(1)
|
|
224
|
-
|
|
225
|
-
if build:
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
+
|
|
230
|
+
if build and not build_environment(directory, image_name):
|
|
231
|
+
raise typer.Exit(1)
|
|
232
|
+
|
|
229
233
|
# Build Docker command
|
|
230
234
|
command = ["docker", "run", "--rm", "-i", *docker_args, image_name]
|
|
231
235
|
else:
|
|
@@ -233,7 +237,9 @@ def debug(
|
|
|
233
237
|
image = first_param
|
|
234
238
|
command = ["docker", "run", "--rm", "-i", *docker_args, image]
|
|
235
239
|
else:
|
|
236
|
-
console.print(
|
|
240
|
+
console.print(
|
|
241
|
+
"[red]Error: Must specify a directory, Docker image, --config, or --cursor[/red]"
|
|
242
|
+
)
|
|
237
243
|
console.print("\nExamples:")
|
|
238
244
|
console.print(" hud debug . # Debug current directory")
|
|
239
245
|
console.print(" hud debug environments/browser # Debug specific directory")
|
|
@@ -342,6 +348,11 @@ def dev(
|
|
|
342
348
|
),
|
|
343
349
|
port: int = typer.Option(8765, "--port", "-p", help="HTTP server port (ignored for stdio)"),
|
|
344
350
|
no_reload: bool = typer.Option(False, "--no-reload", help="Disable hot-reload"),
|
|
351
|
+
full_reload: bool = typer.Option(
|
|
352
|
+
False,
|
|
353
|
+
"--full-reload",
|
|
354
|
+
help="Restart entire container on file changes (instead of just server process)",
|
|
355
|
+
),
|
|
345
356
|
verbose: bool = typer.Option(False, "--verbose", "-v", help="Show server logs"),
|
|
346
357
|
inspector: bool = typer.Option(
|
|
347
358
|
False, "--inspector", help="Launch MCP Inspector (HTTP mode only)"
|
|
@@ -369,12 +380,13 @@ def dev(
|
|
|
369
380
|
hud dev . --inspector # Launch MCP Inspector (HTTP mode only)
|
|
370
381
|
hud dev . --interactive # Launch interactive testing mode (HTTP mode only)
|
|
371
382
|
hud dev . --no-logs # Disable Docker log streaming
|
|
383
|
+
hud dev . --full-reload # Restart entire container on file changes (instead of just server)
|
|
372
384
|
|
|
373
385
|
# With Docker arguments (after all options):
|
|
374
386
|
hud dev . -e BROWSER_PROVIDER=anchorbrowser -e ANCHOR_API_KEY=xxx
|
|
375
387
|
hud dev . -e API_KEY=secret -v /tmp/data:/data --network host
|
|
376
388
|
hud dev . --build -e DEBUG=true --memory 2g
|
|
377
|
-
"""
|
|
389
|
+
""" # noqa: E501
|
|
378
390
|
# Parse directory and Docker arguments
|
|
379
391
|
if params:
|
|
380
392
|
directory = params[0]
|
|
@@ -391,6 +403,7 @@ def dev(
|
|
|
391
403
|
transport,
|
|
392
404
|
port,
|
|
393
405
|
no_reload,
|
|
406
|
+
full_reload,
|
|
394
407
|
verbose,
|
|
395
408
|
inspector,
|
|
396
409
|
no_logs,
|
|
@@ -448,6 +461,11 @@ def run(
|
|
|
448
461
|
"-v",
|
|
449
462
|
help="Show detailed output",
|
|
450
463
|
),
|
|
464
|
+
interactive: bool = typer.Option(
|
|
465
|
+
False,
|
|
466
|
+
"--interactive",
|
|
467
|
+
help="Launch interactive testing mode (HTTP transport only)",
|
|
468
|
+
),
|
|
451
469
|
) -> None:
|
|
452
470
|
"""🚀 Run MCP server locally or remotely.
|
|
453
471
|
|
|
@@ -462,6 +480,10 @@ def run(
|
|
|
462
480
|
hud run --local hud-text-2048:latest
|
|
463
481
|
hud run --local my-server:v1 -e API_KEY=xxx
|
|
464
482
|
hud run --local my-server:v1 --transport http
|
|
483
|
+
|
|
484
|
+
Interactive Testing (local only):
|
|
485
|
+
hud run --local --interactive --transport http hud-text-2048:latest
|
|
486
|
+
hud run --local --interactive --transport http --port 9000 my-server:v1
|
|
465
487
|
"""
|
|
466
488
|
if not params:
|
|
467
489
|
typer.echo("❌ Docker image is required")
|
|
@@ -479,18 +501,28 @@ def run(
|
|
|
479
501
|
# Default to remote if not explicitly local
|
|
480
502
|
is_local = local and not remote
|
|
481
503
|
|
|
504
|
+
# Check for interactive mode restrictions
|
|
505
|
+
if interactive:
|
|
506
|
+
if transport != "http":
|
|
507
|
+
typer.echo("❌ Interactive mode requires HTTP transport (use --transport http)")
|
|
508
|
+
raise typer.Exit(1)
|
|
509
|
+
if not is_local:
|
|
510
|
+
typer.echo("❌ Interactive mode is only available for local execution (use --local)")
|
|
511
|
+
raise typer.Exit(1)
|
|
512
|
+
|
|
482
513
|
if is_local:
|
|
483
514
|
# Local Docker execution
|
|
484
|
-
from .runner import run_mcp_server
|
|
515
|
+
from .utils.runner import run_mcp_server
|
|
485
516
|
|
|
486
|
-
run_mcp_server(image, docker_args, transport, port, verbose)
|
|
517
|
+
run_mcp_server(image, docker_args, transport, port, verbose, interactive)
|
|
487
518
|
else:
|
|
488
519
|
# Remote execution via proxy
|
|
489
|
-
from .remote_runner import run_remote_server
|
|
520
|
+
from .utils.remote_runner import run_remote_server
|
|
490
521
|
|
|
491
522
|
# Get URL from options or environment
|
|
492
523
|
if not url:
|
|
493
524
|
from hud.settings import settings
|
|
525
|
+
|
|
494
526
|
url = settings.hud_mcp_url
|
|
495
527
|
|
|
496
528
|
run_remote_server(image, docker_args, transport, port, url, api_key, run_id, verbose)
|
|
@@ -534,9 +566,12 @@ def clone(
|
|
|
534
566
|
raise typer.Exit(1)
|
|
535
567
|
|
|
536
568
|
|
|
537
|
-
@app.command()
|
|
569
|
+
@app.command(context_settings={"allow_extra_args": True, "ignore_unknown_options": True})
|
|
538
570
|
def build(
|
|
539
|
-
|
|
571
|
+
params: list[str] = typer.Argument( # type: ignore[arg-type] # noqa: B008
|
|
572
|
+
None,
|
|
573
|
+
help="Environment directory followed by optional arguments (e.g., '. -e API_KEY=secret')",
|
|
574
|
+
),
|
|
540
575
|
tag: str | None = typer.Option(
|
|
541
576
|
None, "--tag", "-t", help="Docker image tag (default: from pyproject.toml)"
|
|
542
577
|
),
|
|
@@ -552,11 +587,47 @@ def build(
|
|
|
552
587
|
|
|
553
588
|
Examples:
|
|
554
589
|
hud build # Build current directory
|
|
555
|
-
hud build environments/text_2048
|
|
556
|
-
hud build . --tag my-env:v1.0
|
|
590
|
+
hud build environments/text_2048 -e API_KEY=secret
|
|
591
|
+
hud build . --tag my-env:v1.0 -e VAR1=value1 -e VAR2=value2
|
|
557
592
|
hud build . --no-cache # Force rebuild
|
|
558
593
|
"""
|
|
559
|
-
|
|
594
|
+
# Parse directory and extra arguments
|
|
595
|
+
if params:
|
|
596
|
+
directory = params[0]
|
|
597
|
+
extra_args = params[1:] if len(params) > 1 else []
|
|
598
|
+
else:
|
|
599
|
+
directory = "."
|
|
600
|
+
extra_args = []
|
|
601
|
+
|
|
602
|
+
# Parse environment variables from extra args
|
|
603
|
+
env_vars = {}
|
|
604
|
+
i = 0
|
|
605
|
+
while i < len(extra_args):
|
|
606
|
+
if extra_args[i] == "-e" and i + 1 < len(extra_args):
|
|
607
|
+
# Parse -e KEY=VALUE format
|
|
608
|
+
env_arg = extra_args[i + 1]
|
|
609
|
+
if "=" in env_arg:
|
|
610
|
+
key, value = env_arg.split("=", 1)
|
|
611
|
+
env_vars[key] = value
|
|
612
|
+
i += 2
|
|
613
|
+
elif extra_args[i].startswith("--env="):
|
|
614
|
+
# Parse --env=KEY=VALUE format
|
|
615
|
+
env_arg = extra_args[i][6:] # Remove --env=
|
|
616
|
+
if "=" in env_arg:
|
|
617
|
+
key, value = env_arg.split("=", 1)
|
|
618
|
+
env_vars[key] = value
|
|
619
|
+
i += 1
|
|
620
|
+
elif extra_args[i] == "--env" and i + 1 < len(extra_args):
|
|
621
|
+
# Parse --env KEY=VALUE format
|
|
622
|
+
env_arg = extra_args[i + 1]
|
|
623
|
+
if "=" in env_arg:
|
|
624
|
+
key, value = env_arg.split("=", 1)
|
|
625
|
+
env_vars[key] = value
|
|
626
|
+
i += 2
|
|
627
|
+
else:
|
|
628
|
+
i += 1
|
|
629
|
+
|
|
630
|
+
build_command(directory, tag, no_cache, verbose, env_vars)
|
|
560
631
|
|
|
561
632
|
|
|
562
633
|
@app.command()
|
|
@@ -615,20 +686,14 @@ def list_environments(
|
|
|
615
686
|
filter_name: str | None = typer.Option(
|
|
616
687
|
None, "--filter", "-f", help="Filter environments by name (case-insensitive)"
|
|
617
688
|
),
|
|
618
|
-
json_output: bool = typer.Option(
|
|
619
|
-
|
|
620
|
-
),
|
|
621
|
-
show_all: bool = typer.Option(
|
|
622
|
-
False, "--all", "-a", help="Show all columns including digest"
|
|
623
|
-
),
|
|
624
|
-
verbose: bool = typer.Option(
|
|
625
|
-
False, "--verbose", "-v", help="Show detailed output"
|
|
626
|
-
),
|
|
689
|
+
json_output: bool = typer.Option(False, "--json", help="Output as JSON"),
|
|
690
|
+
show_all: bool = typer.Option(False, "--all", "-a", help="Show all columns including digest"),
|
|
691
|
+
verbose: bool = typer.Option(False, "--verbose", "-v", help="Show detailed output"),
|
|
627
692
|
) -> None:
|
|
628
693
|
"""📋 List all HUD environments in local registry.
|
|
629
|
-
|
|
694
|
+
|
|
630
695
|
Shows environments pulled with 'hud pull' stored in ~/.hud/envs/
|
|
631
|
-
|
|
696
|
+
|
|
632
697
|
Examples:
|
|
633
698
|
hud list # List all environments
|
|
634
699
|
hud list --filter text # Filter by name
|
|
@@ -642,21 +707,16 @@ def list_environments(
|
|
|
642
707
|
@app.command()
|
|
643
708
|
def remove(
|
|
644
709
|
target: str | None = typer.Argument(
|
|
645
|
-
None,
|
|
646
|
-
help="Environment to remove (digest, name, or 'all' for all environments)"
|
|
647
|
-
),
|
|
648
|
-
yes: bool = typer.Option(
|
|
649
|
-
False, "--yes", "-y", help="Skip confirmation prompt"
|
|
650
|
-
),
|
|
651
|
-
verbose: bool = typer.Option(
|
|
652
|
-
False, "--verbose", "-v", help="Show detailed output"
|
|
710
|
+
None, help="Environment to remove (digest, name, or 'all' for all environments)"
|
|
653
711
|
),
|
|
712
|
+
yes: bool = typer.Option(False, "--yes", "-y", help="Skip confirmation prompt"),
|
|
713
|
+
verbose: bool = typer.Option(False, "--verbose", "-v", help="Show detailed output"),
|
|
654
714
|
) -> None:
|
|
655
715
|
"""🗑️ Remove HUD environments from local registry.
|
|
656
|
-
|
|
716
|
+
|
|
657
717
|
Removes environment metadata from ~/.hud/envs/
|
|
658
718
|
Note: This does not remove the Docker images.
|
|
659
|
-
|
|
719
|
+
|
|
660
720
|
Examples:
|
|
661
721
|
hud remove abc123 # Remove by digest
|
|
662
722
|
hud remove text_2048 # Remove by name
|
|
@@ -740,22 +800,24 @@ def eval(
|
|
|
740
800
|
valid_agents = ["claude", "openai"]
|
|
741
801
|
if agent not in valid_agents:
|
|
742
802
|
from hud.utils.design import HUDDesign
|
|
803
|
+
|
|
743
804
|
design = HUDDesign()
|
|
744
805
|
design.error(f"Invalid agent: {agent}. Must be one of: {', '.join(valid_agents)}")
|
|
745
806
|
raise typer.Exit(1)
|
|
746
|
-
|
|
807
|
+
|
|
747
808
|
# Import eval_command lazily to avoid importing agent dependencies
|
|
748
809
|
try:
|
|
749
810
|
from .eval import eval_command
|
|
750
811
|
except ImportError as e:
|
|
751
812
|
from hud.utils.design import HUDDesign
|
|
813
|
+
|
|
752
814
|
design = HUDDesign()
|
|
753
815
|
design.error(
|
|
754
816
|
"Evaluation dependencies are not installed. "
|
|
755
817
|
"Please install with: pip install 'hud-python[agent]'"
|
|
756
818
|
)
|
|
757
819
|
raise typer.Exit(1) from e
|
|
758
|
-
|
|
820
|
+
|
|
759
821
|
# Run the command
|
|
760
822
|
eval_command(
|
|
761
823
|
source=source,
|