Harden M8 trust and guidance workflows

This commit is contained in:
rob
2026-07-20 15:21:50 -03:00
parent d954c1823a
commit 4d22d532a3
24 changed files with 1281 additions and 121 deletions
+92 -30
View File
@@ -2,8 +2,10 @@
import sys
import os
import select
import tty
import termios
from concurrent.futures import ThreadPoolExecutor
from typing import List, Tuple, Optional
from dataclasses import dataclass
@@ -30,6 +32,7 @@ GREEN = "\033[32m"
RESET = "\033[0m"
MAX_VISIBLE = 8 # Show at most 8 items
_registry_cache = {}
# Output stream for UI (stderr when stdout is piped, stdout otherwise)
_ui_out = None
@@ -90,31 +93,60 @@ def search_registry(query: str) -> List[dict]:
"""Search the registry for tools matching the query.
Returns a list of dicts with name, desc, and registry metadata.
Returns empty list if no token configured or search fails.
Registry search is public; authentication is not required.
"""
if not query or len(query) < 2:
return []
normalized = query.strip().lower()
if normalized in _registry_cache:
return [dict(item) for item in _registry_cache[normalized]]
try:
from ..registry_client import get_client
from ..registry_client import RegistryError, get_client
client = get_client()
if not client or not client.token:
return []
results = client.search_tools(query, per_page=5)
items = results.data if hasattr(results, "data") else results
return [
client.timeout = min(getattr(client, "timeout", 3), 3)
client.max_retries = 1
semantic = client.semantic_search(query, limit=5)
if semantic.get("available") and semantic.get("data"):
items = semantic["data"]
else:
results = client.search_tools(query, per_page=5)
items = results.data if hasattr(results, "data") else results
found = [
{
"name": f"{item.get('owner', '')}/{item.get('name', '')}",
"desc": (item.get("description") or "")[:50],
"args": [],
"registry": True,
"downloads": item.get("downloads", 0),
"relevance": item.get("similarity", item.get("score")),
"quality_score": item.get("quality_score"),
"quality_coverage": item.get("quality_coverage"),
}
for item in items
]
except Exception:
_registry_cache[normalized] = found
return [dict(item) for item in found]
except RegistryError:
return []
def _install_registry_selection(tool: dict) -> PickerResult:
"""Install a registry result and retain its qualified identity for execution."""
from ..resolver import install_from_registry
install_from_registry(tool["name"])
return PickerResult(tool["name"], {})
def _local_selection(tool: dict, arguments: Optional[dict] = None) -> PickerResult:
if tool.get("deprecated"):
guidance = tool.get("deprecated_message") or "This tool is no longer maintained."
if tool.get("replacement"):
guidance += f" Use '{tool['replacement']}' instead."
_write(f"{YELLOW}Warning: {guidance}{RESET}\n")
return PickerResult(tool["name"], arguments or {})
class TTYInput:
"""Read from /dev/tty for keyboard input, even when stdin is piped."""
@@ -135,10 +167,14 @@ class TTYInput:
if self.tty:
self.tty.close()
def getch(self):
def getch(self, timeout: Optional[float] = None):
"""Read a single character."""
tty.setraw(self.fd)
try:
if timeout is not None:
ready, _, _ = select.select([self.tty], [], [], timeout)
if not ready:
return None
ch = self.tty.read(1)
if ch == '\x1b':
# Read escape sequence - bytes arrive together from terminal
@@ -164,19 +200,25 @@ def clear_dropdown(n_lines: int):
def run_picker(tty_input: TTYInput) -> Optional[PickerResult]:
"""Run inline picker."""
tools = get_tools()
if not tools:
_write("No tools. Create one: cmdforge\n")
return None
query = ""
selected = 0
scroll = 0
last_drawn = 0
registry_results = {}
registry_future = None
registry_future_query = None
registry_executor = ThreadPoolExecutor(max_workers=1)
_write(HIDE_CURSOR)
try:
while True:
if registry_future is not None and registry_future.done():
registry_results[registry_future_query] = registry_future.result()
registry_future = None
registry_future_query = None
# Filter
matches = []
for t in tools:
@@ -190,12 +232,17 @@ def run_picker(tty_input: TTYInput) -> Optional[PickerResult]:
filtered = [m[0] for m in matches]
# If no local matches, search registry
registry_results = []
if query and len(query) >= 2 and not filtered:
registry_results = search_registry(query)
for rt in registry_results:
normalized_query = query.strip().lower()
found = registry_results.get(normalized_query, [])
if registry_future is None and normalized_query not in registry_results:
registry_future_query = normalized_query
registry_future = registry_executor.submit(
search_registry, query
)
for rt in found:
rt["_registry"] = True
filtered = registry_results
filtered = found
if selected >= len(filtered):
selected = max(0, len(filtered) - 1)
@@ -232,6 +279,12 @@ def run_picker(tty_input: TTYInput) -> Optional[PickerResult]:
# Add registry marker
if is_registry:
prefix += f" {YELLOW}[registry]{RESET}"
if t.get("quality_score") is not None:
prefix += f" Q{t['quality_score']}"
if t.get("quality_coverage") is not None:
prefix += f"/{t['quality_coverage']}%"
if t.get("relevance") is not None:
prefix += f" R{float(t['relevance']):.2f}"
# Add deprecation marker
if is_deprecated:
prefix += f" {YELLOW}[deprecated]{RESET}"
@@ -251,7 +304,11 @@ def run_picker(tty_input: TTYInput) -> Optional[PickerResult]:
last_drawn = len(lines)
# Input
ch = tty_input.getch()
ch = tty_input.getch(
timeout=0.1 if registry_future is not None else None
)
if ch is None:
continue
if ch in ('\r', '\n'): # Enter - run
if filtered:
@@ -261,29 +318,32 @@ def run_picker(tty_input: TTYInput) -> Optional[PickerResult]:
# If it's a registry tool, install it first
if selected_tool.get("_registry") or selected_tool.get("registry"):
_write(f"{YELLOW}Installing {selected_tool['name']}...{RESET}\n")
import subprocess
proc = subprocess.run(
["cmdforge", "registry", "install", selected_tool["name"]],
capture_output=True, text=True
)
if proc.returncode != 0:
_write(f"Install failed: {proc.stderr}\n")
try:
return _install_registry_selection(selected_tool)
except Exception as exc:
_write(f"Install failed: {exc}\n")
return None
# Extract tool name from "owner/name" format
tool_name = selected_tool["name"].split("/")[-1]
return PickerResult(tool_name, {})
return PickerResult(selected_tool["name"], {})
return _local_selection(selected_tool)
elif ch == '\t': # Tab - configure args or run
if filtered:
tool = filtered[selected]
clear_dropdown(last_drawn)
if tool.get("_registry") or tool.get("registry"):
_write(f"{YELLOW}Installing {tool['name']}...{RESET}\n")
try:
_write(SHOW_CURSOR)
return _install_registry_selection(tool)
except Exception as exc:
_write(f"Install failed: {exc}\n")
_write(SHOW_CURSOR)
return None
if tool['args']:
args = pick_args(tty_input, tool)
_write(SHOW_CURSOR)
return PickerResult(tool["name"], args) if args is not None else None
return _local_selection(tool, args) if args is not None else None
_write(SHOW_CURSOR)
return PickerResult(tool["name"], {})
return _local_selection(tool)
elif ch == '\x1b' or ch == '\x03': # Esc or Ctrl+C
clear_dropdown(last_drawn)
@@ -308,6 +368,8 @@ def run_picker(tty_input: TTYInput) -> Optional[PickerResult]:
except Exception:
_write(SHOW_CURSOR)
raise
finally:
registry_executor.shutdown(wait=False, cancel_futures=True)
def pick_args(tty_input: TTYInput, tool: dict) -> Optional[dict]: