hud-python 0.4.48__py3-none-any.whl → 0.4.50__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/agents/base.py +40 -34
- hud/agents/grounded_openai.py +1 -1
- hud/cli/__init__.py +78 -213
- hud/cli/build.py +105 -45
- hud/cli/dev.py +614 -743
- hud/cli/flows/tasks.py +98 -17
- hud/cli/init.py +18 -14
- hud/cli/push.py +27 -9
- hud/cli/rl/local_runner.py +3 -3
- hud/cli/tests/test_eval.py +168 -119
- hud/cli/tests/test_mcp_server.py +6 -95
- hud/cli/utils/env_check.py +9 -9
- hud/cli/utils/source_hash.py +1 -1
- hud/server/__init__.py +2 -1
- hud/server/router.py +160 -0
- hud/server/server.py +246 -79
- hud/tools/base.py +9 -1
- hud/tools/bash.py +2 -2
- hud/tools/edit.py +3 -7
- hud/utils/hud_console.py +43 -0
- hud/utils/tests/test_version.py +1 -1
- hud/version.py +1 -1
- {hud_python-0.4.48.dist-info → hud_python-0.4.50.dist-info}/METADATA +1 -1
- {hud_python-0.4.48.dist-info → hud_python-0.4.50.dist-info}/RECORD +27 -26
- {hud_python-0.4.48.dist-info → hud_python-0.4.50.dist-info}/WHEEL +0 -0
- {hud_python-0.4.48.dist-info → hud_python-0.4.50.dist-info}/entry_points.txt +0 -0
- {hud_python-0.4.48.dist-info → hud_python-0.4.50.dist-info}/licenses/LICENSE +0 -0
hud/server/server.py
CHANGED
|
@@ -133,7 +133,9 @@ class MCPServer(FastMCP):
|
|
|
133
133
|
FastMCP ``FunctionTool`` interface.
|
|
134
134
|
"""
|
|
135
135
|
|
|
136
|
-
def __init__(
|
|
136
|
+
def __init__(
|
|
137
|
+
self, name: str | None = None, instructions: str | None = None, **fastmcp_kwargs: Any
|
|
138
|
+
) -> None:
|
|
137
139
|
# Store shutdown function placeholder before super().__init__
|
|
138
140
|
self._shutdown_fn: Callable | None = None
|
|
139
141
|
|
|
@@ -179,7 +181,7 @@ class MCPServer(FastMCP):
|
|
|
179
181
|
|
|
180
182
|
fastmcp_kwargs["lifespan"] = _lifespan
|
|
181
183
|
|
|
182
|
-
super().__init__(name=name, **fastmcp_kwargs)
|
|
184
|
+
super().__init__(name=name, instructions=instructions, **fastmcp_kwargs)
|
|
183
185
|
self._initializer_fn: Callable | None = None
|
|
184
186
|
self._did_init = False
|
|
185
187
|
self._replaced_server = False
|
|
@@ -382,90 +384,255 @@ class MCPServer(FastMCP):
|
|
|
382
384
|
|
|
383
385
|
return _wrapper
|
|
384
386
|
|
|
387
|
+
def include_router(
|
|
388
|
+
self,
|
|
389
|
+
router: FastMCP,
|
|
390
|
+
prefix: str | None = None,
|
|
391
|
+
hidden: bool = False,
|
|
392
|
+
**kwargs: Any,
|
|
393
|
+
) -> None:
|
|
394
|
+
"""Include a router's tools/resources with optional hidden dispatcher pattern.
|
|
395
|
+
|
|
396
|
+
Uses import_server for fast static composition (unlike mount which is slower).
|
|
397
|
+
|
|
398
|
+
Args:
|
|
399
|
+
router: FastMCP router to include
|
|
400
|
+
prefix: Optional prefix for tools/resources (ignored if hidden=True)
|
|
401
|
+
hidden: If True, wrap in HiddenRouter (single dispatcher tool that calls sub-tools)
|
|
402
|
+
**kwargs: Additional arguments passed to import_server()
|
|
403
|
+
|
|
404
|
+
Examples:
|
|
405
|
+
# Direct include - tools appear at top level
|
|
406
|
+
mcp.include_router(tools_router)
|
|
407
|
+
|
|
408
|
+
# Prefixed include - tools get prefix
|
|
409
|
+
mcp.include_router(admin_router, prefix="admin")
|
|
410
|
+
|
|
411
|
+
# Hidden include - single dispatcher tool
|
|
412
|
+
mcp.include_router(setup_router, hidden=True)
|
|
413
|
+
"""
|
|
414
|
+
if not hidden:
|
|
415
|
+
# Synchronous composition - directly copy tools/resources
|
|
416
|
+
self._sync_import_router(router, hidden=False, prefix=prefix, **kwargs)
|
|
417
|
+
return
|
|
418
|
+
|
|
419
|
+
# Hidden pattern: wrap in HiddenRouter before importing
|
|
420
|
+
from .router import HiddenRouter
|
|
421
|
+
|
|
422
|
+
# Import the hidden router (synchronous)
|
|
423
|
+
self._sync_import_router(HiddenRouter(router), hidden=True, prefix=prefix, **kwargs)
|
|
424
|
+
|
|
425
|
+
def _sync_import_router(
|
|
426
|
+
self,
|
|
427
|
+
router: FastMCP,
|
|
428
|
+
hidden: bool = False,
|
|
429
|
+
prefix: str | None = None,
|
|
430
|
+
**kwargs: Any,
|
|
431
|
+
) -> None:
|
|
432
|
+
"""Synchronously import tools/resources from a router.
|
|
433
|
+
|
|
434
|
+
This is a synchronous alternative to import_server for use at module import time.
|
|
435
|
+
"""
|
|
436
|
+
import re
|
|
437
|
+
|
|
438
|
+
# Import tools directly - use internal dict to preserve keys
|
|
439
|
+
tools = (
|
|
440
|
+
router._tool_manager._tools.items() if not hidden else router._sync_list_tools().items() # type: ignore
|
|
441
|
+
)
|
|
442
|
+
for key, tool in tools:
|
|
443
|
+
# Validate tool name
|
|
444
|
+
if not re.match(r"^[a-zA-Z0-9_-]{1,128}$", key):
|
|
445
|
+
raise ValueError(
|
|
446
|
+
f"Tool name '{key}' must match ^[a-zA-Z0-9_-]{{1,128}}$ "
|
|
447
|
+
"(letters, numbers, underscore, hyphen only, 1-128 chars)"
|
|
448
|
+
)
|
|
449
|
+
|
|
450
|
+
new_key = f"{prefix}_{key}" if prefix else key
|
|
451
|
+
self._tool_manager._tools[new_key] = tool
|
|
452
|
+
|
|
453
|
+
# Import resources directly
|
|
454
|
+
for key, resource in router._resource_manager._resources.items():
|
|
455
|
+
new_key = f"{prefix}_{key}" if prefix else key
|
|
456
|
+
self._resource_manager._resources[new_key] = resource
|
|
457
|
+
|
|
458
|
+
# Import prompts directly
|
|
459
|
+
for key, prompt in router._prompt_manager._prompts.items():
|
|
460
|
+
new_key = f"{prefix}_{key}" if prefix else key
|
|
461
|
+
self._prompt_manager._prompts[new_key] = prompt
|
|
462
|
+
# await self.import_server(hidden_router, prefix=None, **kwargs)
|
|
463
|
+
|
|
385
464
|
def _register_hud_helpers(self) -> None:
|
|
386
|
-
"""Register
|
|
465
|
+
"""Register development helper endpoints.
|
|
387
466
|
|
|
388
467
|
This adds:
|
|
389
|
-
- GET /
|
|
390
|
-
-
|
|
391
|
-
- GET /
|
|
392
|
-
- GET /hud/prompts - List all registered prompts
|
|
468
|
+
- GET /docs - Interactive documentation and tool testing
|
|
469
|
+
- POST /api/tools/{name} - REST wrappers for MCP tools
|
|
470
|
+
- GET /openapi.json - OpenAPI spec for REST endpoints
|
|
393
471
|
"""
|
|
394
472
|
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
"""
|
|
398
|
-
|
|
399
|
-
|
|
473
|
+
# Register REST wrapper for each tool
|
|
474
|
+
def create_tool_endpoint(key: str) -> Any:
|
|
475
|
+
"""Create a REST endpoint for an MCP tool."""
|
|
476
|
+
|
|
477
|
+
async def tool_endpoint(request: Request) -> Response:
|
|
478
|
+
"""Call MCP tool via REST endpoint."""
|
|
479
|
+
try:
|
|
480
|
+
data = await request.json()
|
|
481
|
+
except Exception:
|
|
482
|
+
data = {}
|
|
483
|
+
|
|
484
|
+
try:
|
|
485
|
+
result = await self._tool_manager.call_tool(key, data)
|
|
486
|
+
|
|
487
|
+
# Recursively serialize MCP objects
|
|
488
|
+
def serialize_obj(obj: Any) -> Any:
|
|
489
|
+
"""Recursively serialize MCP objects to JSON-compatible format."""
|
|
490
|
+
if obj is None or isinstance(obj, (str, int, float, bool)):
|
|
491
|
+
return obj
|
|
492
|
+
if isinstance(obj, (list, tuple)):
|
|
493
|
+
return [serialize_obj(item) for item in obj]
|
|
494
|
+
if isinstance(obj, dict):
|
|
495
|
+
return {k: serialize_obj(v) for k, v in obj.items()}
|
|
496
|
+
if hasattr(obj, "model_dump"):
|
|
497
|
+
# Pydantic v2
|
|
498
|
+
return serialize_obj(obj.model_dump())
|
|
499
|
+
if hasattr(obj, "dict"):
|
|
500
|
+
# Pydantic v1
|
|
501
|
+
return serialize_obj(obj.dict())
|
|
502
|
+
if hasattr(obj, "__dict__"):
|
|
503
|
+
# Dataclass or regular class
|
|
504
|
+
return serialize_obj(obj.__dict__)
|
|
505
|
+
# Fallback: convert to string
|
|
506
|
+
return str(obj)
|
|
507
|
+
|
|
508
|
+
serialized = serialize_obj(result)
|
|
509
|
+
return JSONResponse({"success": True, "result": serialized})
|
|
510
|
+
except Exception as e:
|
|
511
|
+
return JSONResponse({"success": False, "error": str(e)}, status_code=400)
|
|
512
|
+
|
|
513
|
+
return tool_endpoint
|
|
514
|
+
|
|
515
|
+
for tool_key in self._tool_manager._tools.keys(): # noqa: SIM118
|
|
516
|
+
endpoint = create_tool_endpoint(tool_key)
|
|
517
|
+
self.custom_route(f"/api/tools/{tool_key}", methods=["POST"])(endpoint)
|
|
518
|
+
|
|
519
|
+
@self.custom_route("/openapi.json", methods=["GET"])
|
|
520
|
+
async def openapi_spec(request: Request) -> Response:
|
|
521
|
+
"""Generate OpenAPI spec from MCP tools."""
|
|
522
|
+
spec = {
|
|
523
|
+
"openapi": "3.1.0",
|
|
524
|
+
"info": {
|
|
525
|
+
"title": f"{self.name or 'MCP Server'} - Testing API",
|
|
526
|
+
"version": "1.0.0",
|
|
527
|
+
"description": (
|
|
528
|
+
"REST API wrappers for testing MCP tools. "
|
|
529
|
+
"These endpoints are for development/testing only. "
|
|
530
|
+
"Agents should connect via MCP protocol (JSON-RPC over stdio/HTTP)."
|
|
531
|
+
),
|
|
532
|
+
},
|
|
533
|
+
"paths": {},
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
# Convert each MCP tool to an OpenAPI path
|
|
400
537
|
for tool_key, tool in self._tool_manager._tools.items():
|
|
401
|
-
tool_data = {"name": tool_key}
|
|
402
538
|
try:
|
|
403
|
-
# Prefer converting to MCP model for consistent fields
|
|
404
539
|
mcp_tool = tool.to_mcp_tool()
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
540
|
+
input_schema = mcp_tool.inputSchema or {"type": "object"}
|
|
541
|
+
|
|
542
|
+
spec["paths"][f"/api/tools/{tool_key}"] = {
|
|
543
|
+
"post": {
|
|
544
|
+
"summary": tool_key,
|
|
545
|
+
"description": mcp_tool.description or "",
|
|
546
|
+
"operationId": f"call_{tool_key}",
|
|
547
|
+
"requestBody": {
|
|
548
|
+
"required": True,
|
|
549
|
+
"content": {"application/json": {"schema": input_schema}},
|
|
550
|
+
},
|
|
551
|
+
"responses": {
|
|
552
|
+
"200": {
|
|
553
|
+
"description": "Success",
|
|
554
|
+
"content": {
|
|
555
|
+
"application/json": {
|
|
556
|
+
"schema": {
|
|
557
|
+
"type": "object",
|
|
558
|
+
"properties": {
|
|
559
|
+
"success": {"type": "boolean"},
|
|
560
|
+
"result": {"type": "object"},
|
|
561
|
+
},
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
},
|
|
565
|
+
}
|
|
566
|
+
},
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
except Exception as e:
|
|
570
|
+
logger.warning("Failed to generate spec for %s: %s", tool_key, e)
|
|
571
|
+
|
|
572
|
+
return JSONResponse(spec)
|
|
573
|
+
|
|
574
|
+
@self.custom_route("/docs", methods=["GET"])
|
|
575
|
+
async def docs_page(request: Request) -> Response:
|
|
576
|
+
"""Interactive documentation page."""
|
|
577
|
+
import base64
|
|
578
|
+
import json
|
|
436
579
|
|
|
437
|
-
@self.custom_route("/hud/prompts", methods=["GET"])
|
|
438
|
-
async def list_prompts(request: Request) -> Response:
|
|
439
|
-
"""List all registered prompts."""
|
|
440
|
-
prompts = []
|
|
441
|
-
for prompt_key, prompt in self._prompt_manager._prompts.items():
|
|
442
|
-
prompt_data = {
|
|
443
|
-
"name": prompt_key,
|
|
444
|
-
"description": prompt.description,
|
|
445
|
-
}
|
|
446
|
-
# Check if it has arguments
|
|
447
|
-
if hasattr(prompt, "arguments") and prompt.arguments:
|
|
448
|
-
prompt_data["arguments"] = [
|
|
449
|
-
{"name": arg.name, "description": arg.description, "required": arg.required}
|
|
450
|
-
for arg in prompt.arguments
|
|
451
|
-
]
|
|
452
|
-
prompts.append(prompt_data)
|
|
453
|
-
|
|
454
|
-
return JSONResponse({"server": self.name, "prompts": prompts, "count": len(prompts)})
|
|
455
|
-
|
|
456
|
-
@self.custom_route("/hud", methods=["GET"])
|
|
457
|
-
async def hud_info(request: Request) -> Response:
|
|
458
|
-
"""Show available HUD helper endpoints."""
|
|
459
580
|
base_url = str(request.base_url).rstrip("/")
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
581
|
+
tool_count = len(self._tool_manager._tools)
|
|
582
|
+
resource_count = len(self._resource_manager._resources)
|
|
583
|
+
|
|
584
|
+
# Generate Cursor deeplink
|
|
585
|
+
server_config = {"url": f"{base_url}/mcp"}
|
|
586
|
+
config_json = json.dumps(server_config, indent=2)
|
|
587
|
+
config_base64 = base64.b64encode(config_json.encode()).decode()
|
|
588
|
+
cursor_deeplink = f"cursor://anysphere.cursor-deeplink/mcp/install?name={self.name or 'mcp-server'}&config={config_base64}" # noqa: E501
|
|
589
|
+
|
|
590
|
+
html = f"""
|
|
591
|
+
<!DOCTYPE html>
|
|
592
|
+
<html lang="en">
|
|
593
|
+
<head>
|
|
594
|
+
<meta charset="UTF-8">
|
|
595
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
596
|
+
<title>{self.name or "MCP Server"} - Documentation</title>
|
|
597
|
+
<link rel="stylesheet" href="https://unpkg.com/swagger-ui-dist@5/swagger-ui.css">
|
|
598
|
+
<style>
|
|
599
|
+
body {{ margin: 0; padding: 0; font-family: monospace; }}
|
|
600
|
+
.header {{ padding: 1.5rem; border-bottom: 1px solid #e0e0e0; background: #fafafa; }}
|
|
601
|
+
.header h1 {{ margin: 0 0 0.5rem 0; font-size: 1.5rem; color: #000; }}
|
|
602
|
+
.header .info {{ margin: 0.25rem 0; color: #666; font-size: 0.9rem; }}
|
|
603
|
+
.header .warning {{ margin: 0.75rem 0 0 0; padding: 0.5rem; background: #fff3cd; border-left: 3px solid #ffc107; color: #856404; font-size: 0.85rem; }}
|
|
604
|
+
.header a {{ color: #000; text-decoration: underline; }}
|
|
605
|
+
.header a:hover {{ color: #666; }}
|
|
606
|
+
.topbar {{ display: none; }}
|
|
607
|
+
</style>
|
|
608
|
+
</head>
|
|
609
|
+
<body>
|
|
610
|
+
<div class="header">
|
|
611
|
+
<h1>{self.name or "MCP Server"} - Development Tools</h1>
|
|
612
|
+
<div class="info">MCP Endpoint (use this with agents): <a href="{base_url}/mcp">{base_url}/mcp</a></div>
|
|
613
|
+
<div class="info">Tools: {tool_count} | Resources: {resource_count}</div>
|
|
614
|
+
<div class="info">Add to Cursor: <a href="{cursor_deeplink}">Click here to install</a></div>
|
|
615
|
+
<div class="warning">
|
|
616
|
+
⚠️ The REST API below is for testing only. Agents connect via MCP protocol at <code>{base_url}/mcp</code>
|
|
617
|
+
</div>
|
|
618
|
+
</div>
|
|
619
|
+
|
|
620
|
+
<div id="swagger-ui"></div>
|
|
621
|
+
<script src="https://unpkg.com/swagger-ui-dist@5/swagger-ui-bundle.js"></script>
|
|
622
|
+
<script src="https://unpkg.com/swagger-ui-dist@5/swagger-ui-standalone-preset.js"></script>
|
|
623
|
+
<script>
|
|
624
|
+
window.onload = function() {{
|
|
625
|
+
SwaggerUIBundle({{
|
|
626
|
+
url: '/openapi.json',
|
|
627
|
+
dom_id: '#swagger-ui',
|
|
628
|
+
deepLinking: true,
|
|
629
|
+
presets: [SwaggerUIBundle.presets.apis, SwaggerUIStandalonePreset],
|
|
630
|
+
layout: "StandaloneLayout",
|
|
631
|
+
tryItOutEnabled: true
|
|
632
|
+
}})
|
|
633
|
+
}}
|
|
634
|
+
</script>
|
|
635
|
+
</body>
|
|
636
|
+
</html>
|
|
637
|
+
""" # noqa: E501
|
|
638
|
+
return Response(content=html, media_type="text/html")
|
hud/tools/base.py
CHANGED
|
@@ -144,7 +144,11 @@ _INTERNAL_PREFIX = "int_"
|
|
|
144
144
|
|
|
145
145
|
|
|
146
146
|
class BaseHub(FastMCP):
|
|
147
|
-
"""A composition-friendly FastMCP server that holds an internal tool dispatcher.
|
|
147
|
+
"""A composition-friendly FastMCP server that holds an internal tool dispatcher.
|
|
148
|
+
|
|
149
|
+
Note: BaseHub can be used standalone or to wrap existing routers. For the newer
|
|
150
|
+
FastAPI-like pattern, consider using HiddenRouter from hud.server instead.
|
|
151
|
+
"""
|
|
148
152
|
|
|
149
153
|
env: Any
|
|
150
154
|
|
|
@@ -167,6 +171,10 @@ class BaseHub(FastMCP):
|
|
|
167
171
|
Optional long-lived environment object. Stored on the server
|
|
168
172
|
instance (``layer.env``) and therefore available to every request
|
|
169
173
|
via ``ctx.fastmcp.env``.
|
|
174
|
+
title:
|
|
175
|
+
Optional title for the dispatcher tool.
|
|
176
|
+
description:
|
|
177
|
+
Optional description for the dispatcher tool.
|
|
170
178
|
meta:
|
|
171
179
|
Metadata to include in MCP tool listing.
|
|
172
180
|
"""
|
hud/tools/bash.py
CHANGED
|
@@ -3,7 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
import asyncio
|
|
4
4
|
import os
|
|
5
5
|
import sys
|
|
6
|
-
from typing import TYPE_CHECKING
|
|
6
|
+
from typing import TYPE_CHECKING
|
|
7
7
|
|
|
8
8
|
from .base import BaseTool
|
|
9
9
|
from .types import ContentResult, ToolError
|
|
@@ -140,7 +140,7 @@ class BashTool(BaseTool):
|
|
|
140
140
|
self.env = value
|
|
141
141
|
|
|
142
142
|
async def __call__(
|
|
143
|
-
self, command: str | None = None, restart: bool = False
|
|
143
|
+
self, command: str | None = None, restart: bool = False
|
|
144
144
|
) -> list[ContentBlock]:
|
|
145
145
|
if restart:
|
|
146
146
|
if self.session:
|
hud/tools/edit.py
CHANGED
|
@@ -1,16 +1,13 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
1
|
from collections import defaultdict
|
|
4
2
|
from pathlib import Path
|
|
5
|
-
from typing import
|
|
3
|
+
from typing import Literal, get_args
|
|
4
|
+
|
|
5
|
+
from mcp.types import ContentBlock
|
|
6
6
|
|
|
7
7
|
from .base import BaseTool
|
|
8
8
|
from .types import ContentResult, ToolError
|
|
9
9
|
from .utils import maybe_truncate, run
|
|
10
10
|
|
|
11
|
-
if TYPE_CHECKING:
|
|
12
|
-
from mcp.types import ContentBlock
|
|
13
|
-
|
|
14
11
|
Command = Literal[
|
|
15
12
|
"view",
|
|
16
13
|
"create",
|
|
@@ -56,7 +53,6 @@ class EditTool(BaseTool):
|
|
|
56
53
|
old_str: str | None = None,
|
|
57
54
|
new_str: str | None = None,
|
|
58
55
|
insert_line: int | None = None,
|
|
59
|
-
**kwargs: Any,
|
|
60
56
|
) -> list[ContentBlock]:
|
|
61
57
|
_path = Path(path)
|
|
62
58
|
self.validate_path(command, _path)
|
hud/utils/hud_console.py
CHANGED
|
@@ -38,9 +38,26 @@ TEXT = "bright_white" # Off-white that's readable on dark, not too bright on li
|
|
|
38
38
|
SECONDARY = "rgb(108,113,196)" # Muted blue-purple for secondary text
|
|
39
39
|
|
|
40
40
|
|
|
41
|
+
# HUD Symbol System - Minimal 3-category system with default colors
|
|
42
|
+
class Symbols:
|
|
43
|
+
"""Unicode symbols for consistent CLI output with default colors."""
|
|
44
|
+
|
|
45
|
+
# Info/Items - Use for all informational lines (gold)
|
|
46
|
+
ITEM = f"[{GOLD}]•[/{GOLD}]"
|
|
47
|
+
|
|
48
|
+
# Status - Use for state/completion (green)
|
|
49
|
+
SUCCESS = f"[{GREEN}]●[/{GREEN}]"
|
|
50
|
+
|
|
51
|
+
# Flow/Special - Use for transitions and important notes (gold)
|
|
52
|
+
FLOW = f"[{GOLD}]⟿[/{GOLD}]"
|
|
53
|
+
|
|
54
|
+
|
|
41
55
|
class HUDConsole:
|
|
42
56
|
"""Design system for HUD CLI output."""
|
|
43
57
|
|
|
58
|
+
# Make symbols easily accessible
|
|
59
|
+
sym = Symbols
|
|
60
|
+
|
|
44
61
|
def __init__(self, logger: logging.Logger | None = None) -> None:
|
|
45
62
|
"""Initialize the design system.
|
|
46
63
|
|
|
@@ -547,6 +564,32 @@ class HUDConsole:
|
|
|
547
564
|
"""
|
|
548
565
|
return questionary.confirm(message, default=default).ask()
|
|
549
566
|
|
|
567
|
+
# Symbol-based output methods
|
|
568
|
+
def symbol(self, symbol: str, message: str, color: str = GOLD, stderr: bool = True) -> None:
|
|
569
|
+
"""Print a message with a colored symbol prefix.
|
|
570
|
+
|
|
571
|
+
Args:
|
|
572
|
+
symbol: Symbol to use (use Symbols.* constants)
|
|
573
|
+
message: Message text
|
|
574
|
+
color: Color for the symbol (default: gold)
|
|
575
|
+
stderr: If True, output to stderr
|
|
576
|
+
"""
|
|
577
|
+
console = self._stderr_console if stderr else self._stdout_console
|
|
578
|
+
console.print(f"[{color}]{symbol}[/{color}] {message}")
|
|
579
|
+
|
|
580
|
+
def detail(self, message: str, stderr: bool = True) -> None:
|
|
581
|
+
"""Print an indented detail line with gold pointer symbol."""
|
|
582
|
+
console = self._stderr_console if stderr else self._stdout_console
|
|
583
|
+
console.print(f" [{GOLD}]{Symbols.ITEM}[/{GOLD}] {message}")
|
|
584
|
+
|
|
585
|
+
def flow(self, message: str, stderr: bool = True) -> None:
|
|
586
|
+
"""Print a flow/transition message with wave symbol."""
|
|
587
|
+
self.symbol(Symbols.FLOW, message, GOLD, stderr)
|
|
588
|
+
|
|
589
|
+
def note(self, message: str, stderr: bool = True) -> None:
|
|
590
|
+
"""Print an important note with asterism symbol."""
|
|
591
|
+
self.symbol(Symbols.ITEM, message, GOLD, stderr)
|
|
592
|
+
|
|
550
593
|
|
|
551
594
|
# Global design instance for convenience
|
|
552
595
|
class _ProgressContext:
|
hud/utils/tests/test_version.py
CHANGED
hud/version.py
CHANGED
|
@@ -2,11 +2,11 @@ hud/__init__.py,sha256=JMDFUE1pP0J1Xl_miBdt7ERvoffZmTzSFe8yxz512A8,552
|
|
|
2
2
|
hud/__main__.py,sha256=YR8Dq8OhINOsVfQ55PmRXXg4fEK84Rt_-rMtJ5rvhWo,145
|
|
3
3
|
hud/settings.py,sha256=disObWa-DgXzoDcCDp3y1dTPaNsbR0IvoMJL9Eg4zyo,3947
|
|
4
4
|
hud/types.py,sha256=KCVrglSG0VK-AUCFgG5CLgDcYqokvYgZG1r2Bsc-tBg,11155
|
|
5
|
-
hud/version.py,sha256=
|
|
5
|
+
hud/version.py,sha256=n0UAZxOUNvgBT83XVdXaBuxxtW1D_o4cgeK4Myy1HO8,105
|
|
6
6
|
hud/agents/__init__.py,sha256=UoIkljWdbq4bM0LD-mSaw6w826EqdEjOk7r6glNYwYQ,286
|
|
7
|
-
hud/agents/base.py,sha256=
|
|
7
|
+
hud/agents/base.py,sha256=5G8Aio50KlDMGi3RjIlzK6fNHlY8YK9y5jhhHTUN5CE,31626
|
|
8
8
|
hud/agents/claude.py,sha256=0zje6de9_H-QlyDf05BnvtwoLPFnYgXhHgQGEAjwktU,16006
|
|
9
|
-
hud/agents/grounded_openai.py,sha256=
|
|
9
|
+
hud/agents/grounded_openai.py,sha256=UC_Z0BP1fvThB95cCYqvMgaQLYXFYB_8zlawwIphruY,11247
|
|
10
10
|
hud/agents/langchain.py,sha256=1EgCy8jfjunsWxlPC5XfvfLS6_XZVrIF1ZjtHcrvhYw,9584
|
|
11
11
|
hud/agents/lite_llm.py,sha256=_3wbUiYCp7q8Vyu9rhaoJDvmb_bsyUsLYWP3iQJ2bHo,2239
|
|
12
12
|
hud/agents/openai.py,sha256=O1xV1h1l-W8lmnmXqTYr5CwnmnaniMqOxAZbl2CTTng,14576
|
|
@@ -20,29 +20,29 @@ hud/agents/tests/test_claude.py,sha256=0nZnfsbGoECvsLPdmaRnc9jVmrehVvc3kxeyiCQI2
|
|
|
20
20
|
hud/agents/tests/test_client.py,sha256=uikgh6yhjPPX2RBU4XJQMz1mNox9uXjuwsP8t93id18,13337
|
|
21
21
|
hud/agents/tests/test_grounded_openai_agent.py,sha256=VK8lUvHIjWicMX00VKPE-FZyjiJqTEhb80MuRRa9fVc,5437
|
|
22
22
|
hud/agents/tests/test_openai.py,sha256=dnAFAoBKZf-5dtDpj6UC3q7oZv2tdMFcniPU0emfImw,8020
|
|
23
|
-
hud/cli/__init__.py,sha256=
|
|
23
|
+
hud/cli/__init__.py,sha256=D32zOZKmp-KDTtXj7H2uYPTh-fOmnwGmIitHAU6eVKs,41422
|
|
24
24
|
hud/cli/__main__.py,sha256=fDH7XITyuDITwSDIVwRso06aouADO0CzTHKqp5TOwJE,143
|
|
25
25
|
hud/cli/analyze.py,sha256=4u5oYfJMquOjT9PzzRTYVcTZDxDi0ilNP_g532_hpOU,14716
|
|
26
|
-
hud/cli/build.py,sha256=
|
|
26
|
+
hud/cli/build.py,sha256=aFFWGtO1sQeTonvLsuMeDKG62IkakASbsP7FonGYuxc,22381
|
|
27
27
|
hud/cli/clone.py,sha256=AwVDIuhr8mHb1oT2Af2HrD25SiTdwATpE6zd93vzLgA,6099
|
|
28
28
|
hud/cli/debug.py,sha256=jtFW8J5F_3rhq1Hf1_SkJ7aLS3wjnyIs_LsC8k5cnzc,14200
|
|
29
|
-
hud/cli/dev.py,sha256=
|
|
29
|
+
hud/cli/dev.py,sha256=mZW66fbtnCHplwGf1UWy72qfVYXnjPBUqVFAOD4v9Hg,23012
|
|
30
30
|
hud/cli/eval.py,sha256=b91oYXBpODe_R2sRTWItjGceYSzSP5keKVLMkC7uc-Q,26926
|
|
31
31
|
hud/cli/get.py,sha256=sksKrdzBGZa7ZuSoQkc0haj-CvOGVSSikoVXeaUd3N4,6274
|
|
32
|
-
hud/cli/init.py,sha256=
|
|
32
|
+
hud/cli/init.py,sha256=Iz0fcE8ao50xChCKwbapTwjAPRY0ZDqV6XHLsvCpRC4,9952
|
|
33
33
|
hud/cli/list_func.py,sha256=EVi2Vc3Lb3glBNJxFx4MPnZknZ4xmuJz1OFg_dc8a_E,7177
|
|
34
34
|
hud/cli/pull.py,sha256=XGEZ8n60tbzLQP_8d9h7XYmzyCW0e2-Rkr3_tLG7jvw,12449
|
|
35
|
-
hud/cli/push.py,sha256=
|
|
35
|
+
hud/cli/push.py,sha256=rWJIqHebvp3DchK-00L6G0olD3-klsobLutRW4PP_ts,19488
|
|
36
36
|
hud/cli/remove.py,sha256=8vGQyXDqgtjz85_vtusoIG8zurH4RHz6z8UMevQRYM4,6861
|
|
37
37
|
hud/cli/flows/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
38
|
-
hud/cli/flows/tasks.py,sha256=
|
|
38
|
+
hud/cli/flows/tasks.py,sha256=95KyQgrYuZAkhne3O944nXxZVrquD6bR-vNVmrF5Tsg,17208
|
|
39
39
|
hud/cli/rl/__init__.py,sha256=pGx4WGaL-yGdogJNzgEE7BtjFtT4I9CTI_UdCm49h98,5376
|
|
40
40
|
hud/cli/rl/celebrate.py,sha256=trGEJn3xebexlHwFVKPJKhRujVVV8sy7TQTJvRd2p9A,5947
|
|
41
41
|
hud/cli/rl/config.py,sha256=A-4WWwAS68GRKx1cP_DJ-NZD_96cFNnGwx0P3pQT1ps,3271
|
|
42
42
|
hud/cli/rl/display.py,sha256=hqJVGmO9csYinladhZwjF-GMvppYWngxDHajTyIJ_gM,5214
|
|
43
43
|
hud/cli/rl/gpu.py,sha256=peXS-NdUF5RyuSs0aZoCzGLboneBUpCy8f9f99WMrG0,2009
|
|
44
44
|
hud/cli/rl/gpu_utils.py,sha256=0nFRrmJZzLOHh_0bjMhIsBj94PAuu95vwxLd_sa4Q5g,11202
|
|
45
|
-
hud/cli/rl/local_runner.py,sha256
|
|
45
|
+
hud/cli/rl/local_runner.py,sha256=_4_Bs8UXPz5Is6PyVg_DnrG0zL_Mr-6DJ5_6jC9o0qA,23199
|
|
46
46
|
hud/cli/rl/presets.py,sha256=DzOO82xL5QyzdVtlX-Do1CODMvDz9ILMPapjU92jcZg,3051
|
|
47
47
|
hud/cli/rl/remote_runner.py,sha256=fKmOVKSBUWfakunfe9-HAllpUJDxfRNZwL00fPw-QTI,17837
|
|
48
48
|
hud/cli/rl/rl_api.py,sha256=fvRMWQXhTSLM2zQaWWDas_u47RreH8erLgtXRKnQGeA,4350
|
|
@@ -58,10 +58,10 @@ hud/cli/tests/test_cli_main.py,sha256=0wMho9p9NcGjp0jLiUtCQh_FYdbMaCJtSY3sBbSgPw
|
|
|
58
58
|
hud/cli/tests/test_clone.py,sha256=oC2mf-41QQVc7ODJkjrWbVPNMB2fDW3nZ6jY6w93gvQ,4458
|
|
59
59
|
hud/cli/tests/test_cursor.py,sha256=ZfxAFKJesJ3UV1JBoASSRlv6BXbpvVEk_pjxUg1jnf4,9821
|
|
60
60
|
hud/cli/tests/test_debug.py,sha256=bQ76d_0HJfthHBSECmGNv499ZE57CIOKsanMlNfNHGk,18036
|
|
61
|
-
hud/cli/tests/test_eval.py,sha256=
|
|
61
|
+
hud/cli/tests/test_eval.py,sha256=URvNJBlwgbA8MTZhMbuTmGa2TETo4TPRm_G482CSQVM,22021
|
|
62
62
|
hud/cli/tests/test_list_func.py,sha256=pkG4TtJJBMi9Xk8KBNFBlGcam7kwz01IRsjfQBL2PxM,10700
|
|
63
63
|
hud/cli/tests/test_main_module.py,sha256=6RhwCcdRSN2uQV6-adti40ZcLd3u-mPR1ai6wL64c6Y,1105
|
|
64
|
-
hud/cli/tests/test_mcp_server.py,sha256=
|
|
64
|
+
hud/cli/tests/test_mcp_server.py,sha256=2RzAdhHrBOn-FLsMfZ4CJd2z7xJEA-W7Ytd1RTIP_os,890
|
|
65
65
|
hud/cli/tests/test_pull.py,sha256=ToSJrlfn13pYnrWWt3W_S7qFFjwvoZC2UisrZVrxujo,13155
|
|
66
66
|
hud/cli/tests/test_push.py,sha256=V71KP5gEDo7Z9ccFpjidBjYliFg_KCfnZoZYbBXjguE,12875
|
|
67
67
|
hud/cli/tests/test_registry.py,sha256=-o9MvQTcBElteqrg0XW8Bg59KrHCt88ZyPqeaAlyyTg,9539
|
|
@@ -70,7 +70,7 @@ hud/cli/utils/__init__.py,sha256=L6s0oNzY2LugGp9faodCPnjzM-ZUorUH05-HmYOq5hY,35
|
|
|
70
70
|
hud/cli/utils/config.py,sha256=AnsN6FEa8V3jg3EWaqUJN38-UuYC6tVZxPfBb_5LFBs,2652
|
|
71
71
|
hud/cli/utils/cursor.py,sha256=fy850p0rVp5k_1wwOCI7rK1SggbselJrywFInSQ2gio,3009
|
|
72
72
|
hud/cli/utils/docker.py,sha256=oGVzPfp0Rn89o9d6tgSEziKy9GXFrYaWn_mjBmGRHe4,6326
|
|
73
|
-
hud/cli/utils/env_check.py,sha256=
|
|
73
|
+
hud/cli/utils/env_check.py,sha256=qs_earFr9RDjnw6V4Fxeqno5ETOKFb8E1WKee3C3P54,7073
|
|
74
74
|
hud/cli/utils/environment.py,sha256=cxsNwCfwX2PtCHht9xH_Yo5jpcqANf7h0wa3gfiy5tY,4278
|
|
75
75
|
hud/cli/utils/interactive.py,sha256=sHhTjaImxlwlZ5_DTXb23Jwrjy5oJ7diB-8duhHbImU,16647
|
|
76
76
|
hud/cli/utils/local_runner.py,sha256=jnPFoJu3sCq65LSUapKCkakdlEuz__96oJU_FfOYtEg,6542
|
|
@@ -81,7 +81,7 @@ hud/cli/utils/registry.py,sha256=p6IaWmhUbf0Yh6aGa3jIPoSFT2uJPTOVBLS0jpKDUqc,437
|
|
|
81
81
|
hud/cli/utils/remote_runner.py,sha256=OOSJ6wU_gS_hJaURDfxZcyekjIIwPQKGN_Pq64tin3E,9505
|
|
82
82
|
hud/cli/utils/runner.py,sha256=7HxVGa6OTflQnO0FSuRs273wnAmbm7cFRU9RTGL3-Wo,4390
|
|
83
83
|
hud/cli/utils/server.py,sha256=EE5DJ0RAmXCEjMcZycpAsAxxCj6sOdIsXqPh38kK2ew,7416
|
|
84
|
-
hud/cli/utils/source_hash.py,sha256=
|
|
84
|
+
hud/cli/utils/source_hash.py,sha256=EDD3KC4pLGBVoSL5UTv1GvF2TAs2ThNHNOhP_5Sbub4,2979
|
|
85
85
|
hud/cli/utils/tasks.py,sha256=bpH2mQkfgKUpgh1J0NtVxMxcM1jDZk2GAAPQMcqAUz4,826
|
|
86
86
|
hud/clients/README.md,sha256=XNE3mch95ozDgVqfwCGcrhlHY9CwT1GKfNANNboowto,3826
|
|
87
87
|
hud/clients/__init__.py,sha256=N5M_gZv4nP7dLRwpAiaqqaxyaLieGW6397FszeG7JGw,364
|
|
@@ -134,10 +134,11 @@ hud/rl/tests/test_learner.py,sha256=LfTwB626gWurYfZv91wk8ASETBNqrLh9i_GCMP4CB3E,
|
|
|
134
134
|
hud/rl/utils/start_vllm_server.sh,sha256=ThPokrLK_Qm_uh916fHXXBfMlw1TC97P57-AEI5MuOc,910
|
|
135
135
|
hud/samples/__init__.py,sha256=wgcN1IOLHhR4C1fFKqyvA7Yl9lJhJFf34zfKs-UMSus,128
|
|
136
136
|
hud/samples/browser.py,sha256=7LkzGx2G5dA8RogZwORnxxpVsxMV2gF18D_hGJIEow8,973
|
|
137
|
-
hud/server/__init__.py,sha256=
|
|
137
|
+
hud/server/__init__.py,sha256=ZTxwhR7tMtSE14i1sONTz5UaMXURW1AYoFZMbWGBviU,134
|
|
138
138
|
hud/server/context.py,sha256=6bCdSzv1FGyItu9472HbbYef279H7QuMGJDR8EtYg5Y,3210
|
|
139
139
|
hud/server/low_level.py,sha256=XYs2pOJ9kN4OcJ6ahDmXM5mWkzq5wJLpKFInUYrWEok,4701
|
|
140
|
-
hud/server/
|
|
140
|
+
hud/server/router.py,sha256=c1dJ8CXqi_oI03WPDXqHH7jotxBY9BDIQsBQy5EoeSM,5336
|
|
141
|
+
hud/server/server.py,sha256=7M2tHH4wHsLfIRhbXgZVJTfJjW1cWGjeMPT_-ysVS_M,27096
|
|
141
142
|
hud/server/helper/__init__.py,sha256=ZxO8VP3RZEBBp-q65VixuhzQgqEPSVzW0hEY9J9QqDA,116
|
|
142
143
|
hud/server/tests/__init__.py,sha256=eEYYkxX5Hz9woXVOBJ2H2_CQoEih0vH6nRt3sH2Z8v8,49
|
|
143
144
|
hud/server/tests/test_add_tool.py,sha256=9Y59LJpow3BQ31Jg7fowhV7nAeyqude9Tap9tEs_vBE,1863
|
|
@@ -164,9 +165,9 @@ hud/telemetry/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSu
|
|
|
164
165
|
hud/telemetry/tests/test_replay.py,sha256=eREc6qgSJDRT1pOPdyhiEoEJ9H2yT1ospaU1RvTKlvg,1328
|
|
165
166
|
hud/telemetry/tests/test_trace.py,sha256=0rxR77CjcStat3ILA9QAswieOJ3J_386QmjmNDp34oA,2486
|
|
166
167
|
hud/tools/__init__.py,sha256=i6lE0GxYcPnlLLd-55ryCCHo7o9anC4RfqkuYFXvzMQ,1009
|
|
167
|
-
hud/tools/base.py,sha256=
|
|
168
|
-
hud/tools/bash.py,sha256=
|
|
169
|
-
hud/tools/edit.py,sha256=
|
|
168
|
+
hud/tools/base.py,sha256=IxpnEHfxUtgz1k_w-09l65KKpDV3tQu4WdF3nZDsdwU,17854
|
|
169
|
+
hud/tools/bash.py,sha256=1jl7cpB1ApGXn7Hy8zghJ2fXugEol6UeN0aYUSiM2EQ,5189
|
|
170
|
+
hud/tools/edit.py,sha256=NYQL3coPIaG_-TP6DOpsVWFg1xcaMZwM5LtFye9WgNE,12644
|
|
170
171
|
hud/tools/playwright.py,sha256=HY91JsC_6cvIa-9W14wIhjsAkAtkLPaXgUqMizTG-1c,14989
|
|
171
172
|
hud/tools/response.py,sha256=t6Oc8NM4u951A1XMCBaIkFyu3VNEQ8dcWURyTygfZmA,2228
|
|
172
173
|
hud/tools/submit.py,sha256=hJG2G3Oex4fz_3CsAUVhOhAA56UvDMhquB29xCT-C3M,1973
|
|
@@ -208,7 +209,7 @@ hud/utils/__init__.py,sha256=nk9Re6ls2RudAWnAHDWYbLG28AwNF4qMFYf5xQIJhQA,181
|
|
|
208
209
|
hud/utils/agent_factories.py,sha256=cvfXByqG6gOYHtm1VGeJjCpxoLxM4aJez8rH-AerP_A,3186
|
|
209
210
|
hud/utils/async_utils.py,sha256=5cKrJcnaHV2eJNxeyx0r7fPcdPTDBK7kM9-nLaF51X4,2409
|
|
210
211
|
hud/utils/group_eval.py,sha256=6yXEH8ZRKkR4bBy9-QWGmjlm2IbCnTUZppEFbjTvndY,8352
|
|
211
|
-
hud/utils/hud_console.py,sha256=
|
|
212
|
+
hud/utils/hud_console.py,sha256=ms2lXk3ZhYNmpGb0Dn902SKJBYfqo8Aa9DzkkC-xGSI,23597
|
|
212
213
|
hud/utils/mcp.py,sha256=pMadd7A0DH6Y_aWywKU8jVYu2pRHGPEndV2ZQFrrj60,2888
|
|
213
214
|
hud/utils/pretty_errors.py,sha256=WGeL4CTHtlA6KgPuV_JSX5l6H4-xbuTp6Y6tw1bkiFg,2430
|
|
214
215
|
hud/utils/progress.py,sha256=suikwFM8sdSfkV10nAOEaInDhG4XKgOSvFePg4jSj1A,5927
|
|
@@ -221,10 +222,10 @@ hud/utils/tests/test_init.py,sha256=2QLQSGgyP9wJhOvPCusm_zjJad0qApOZi1BXpxcdHXQ,
|
|
|
221
222
|
hud/utils/tests/test_mcp.py,sha256=0pUa16mL-bqbZDXp5NHBnt1gO5o10BOg7zTMHZ1DNPM,4023
|
|
222
223
|
hud/utils/tests/test_progress.py,sha256=QSF7Kpi03Ff_l3mAeqW9qs1nhK50j9vBiSobZq7T4f4,7394
|
|
223
224
|
hud/utils/tests/test_telemetry.py,sha256=5jl7bEx8C8b-FfFUko5pf4UY-mPOR-9HaeL98dGtVHM,2781
|
|
224
|
-
hud/utils/tests/test_version.py,sha256=
|
|
225
|
+
hud/utils/tests/test_version.py,sha256=uQT6fdCFzf7lllZDCeSWGR5Rn30hErcUg843Zmw0rLs,160
|
|
225
226
|
hud/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
226
|
-
hud_python-0.4.
|
|
227
|
-
hud_python-0.4.
|
|
228
|
-
hud_python-0.4.
|
|
229
|
-
hud_python-0.4.
|
|
230
|
-
hud_python-0.4.
|
|
227
|
+
hud_python-0.4.50.dist-info/METADATA,sha256=KE0_svesRl5c8jKKIEGoByqyaplSJNkmGj9dk4Jsan0,22275
|
|
228
|
+
hud_python-0.4.50.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
229
|
+
hud_python-0.4.50.dist-info/entry_points.txt,sha256=jJbodNFg1m0-CDofe5AHvB4zKBq7sSdP97-ohaQ3ae4,63
|
|
230
|
+
hud_python-0.4.50.dist-info/licenses/LICENSE,sha256=yIzBheVUf86FC1bztAcr7RYWWNxyd3B-UJQ3uddg1HA,1078
|
|
231
|
+
hud_python-0.4.50.dist-info/RECORD,,
|
|
File without changes
|