This commit is contained in:
2026-01-02 02:28:59 -08:00
parent deb05c0d44
commit 6e9a0c28ff
13 changed files with 1402 additions and 2334 deletions

View File

@@ -1,10 +1,11 @@
from __future__ import annotations
from typing import Any, Dict, Sequence, List, Optional
from typing import Any, Dict, Sequence, List, Optional, Tuple
import shlex
import sys
from cmdlet._shared import Cmdlet, CmdletArg, parse_cmdlet_args
from cmdlet import REGISTRY as CMDLET_REGISTRY, ensure_cmdlet_modules_loaded
from SYS.logger import log
from SYS.result_table import ResultTable
from SYS import pipeline as ctx
@@ -27,6 +28,118 @@ def _examples_for_cmd(name: str) -> List[str]:
return lookup.get(key, [])
def _normalize_cmdlet_key(name: Optional[str]) -> str:
return str(name or "").replace("_", "-").lower().strip()
def _cmdlet_aliases(cmdlet_obj: Cmdlet) -> List[str]:
aliases: List[str] = []
for attr in ("alias", "aliases"):
raw_aliases = getattr(cmdlet_obj, attr, None)
if isinstance(raw_aliases, (list, tuple, set)):
candidates = raw_aliases
else:
candidates = (raw_aliases,)
for alias in candidates or ():
text = str(alias or "").strip()
if text:
aliases.append(text)
seen: set[str] = set()
deduped: List[str] = []
for alias in aliases:
key = alias.lower()
if key in seen:
continue
seen.add(key)
deduped.append(alias)
return deduped
def _cmdlet_arg_to_dict(arg: CmdletArg) -> Dict[str, Any]:
return {
"name": str(getattr(arg, "name", "") or ""),
"type": str(getattr(arg, "type", "") or ""),
"required": bool(getattr(arg, "required", False)),
"description": str(getattr(arg, "description", "") or ""),
"choices": [str(c) for c in list(getattr(arg, "choices", []) or [])],
"alias": str(getattr(arg, "alias", "") or ""),
"variadic": bool(getattr(arg, "variadic", False)),
"usage": str(getattr(arg, "usage", "") or ""),
"query_key": getattr(arg, "query_key", None),
"query_aliases": [str(c) for c in list(getattr(arg, "query_aliases", []) or [])],
"query_only": bool(getattr(arg, "query_only", False)),
"requires_db": bool(getattr(arg, "requires_db", False)),
}
def _build_alias_map_from_metadata(metadata: Dict[str, Dict[str, Any]]) -> Dict[str, str]:
mapping: Dict[str, str] = {}
for name, meta in metadata.items():
canonical = _normalize_cmdlet_key(name)
if canonical:
mapping[canonical] = name
for alias in meta.get("aliases", []) or []:
alias_key = _normalize_cmdlet_key(alias)
if alias_key:
mapping[alias_key] = name
return mapping
def _gather_metadata_from_cmdlet_classes() -> Tuple[Dict[str, Dict[str, Any]], Dict[str, str]]:
metadata: Dict[str, Dict[str, Any]] = {}
alias_map: Dict[str, str] = {}
try:
ensure_cmdlet_modules_loaded()
except Exception:
pass
for module in list(sys.modules.values()):
mod_name = getattr(module, "__name__", "") or ""
if not (mod_name.startswith("cmdlet.") or mod_name == "cmdlet" or mod_name.startswith("cmdnat.")):
continue
cmdlet_obj = getattr(module, "CMDLET", None)
if not isinstance(cmdlet_obj, Cmdlet):
continue
canonical_key = _normalize_cmdlet_key(getattr(cmdlet_obj, "name", None) or "")
if not canonical_key:
continue
entry = {
"name": str(getattr(cmdlet_obj, "name", "") or canonical_key),
"summary": str(getattr(cmdlet_obj, "summary", "") or ""),
"usage": str(getattr(cmdlet_obj, "usage", "") or ""),
"aliases": _cmdlet_aliases(cmdlet_obj),
"details": list(getattr(cmdlet_obj, "detail", []) or []),
"args": [_cmdlet_arg_to_dict(a) for a in getattr(cmdlet_obj, "arg", []) or []],
"raw": getattr(cmdlet_obj, "raw", None),
}
metadata[canonical_key] = entry
alias_map[canonical_key] = canonical_key
for alias in entry["aliases"]:
alias_key = _normalize_cmdlet_key(alias)
if alias_key:
alias_map[alias_key] = canonical_key
for registry_name in CMDLET_REGISTRY.keys():
normalized = _normalize_cmdlet_key(registry_name)
if not normalized or normalized in alias_map:
continue
alias_map[normalized] = normalized
metadata.setdefault(
normalized,
{
"name": normalized,
"aliases": [],
"usage": "",
"summary": "",
"details": [],
"args": [],
"raw": None,
},
)
return metadata, alias_map
def _find_cmd_metadata(name: str,
metadata: Dict[str,
Dict[str,
@@ -148,17 +261,90 @@ def _render_detail(meta: Dict[str, Any], args: Sequence[str]) -> None:
def _run(result: Any, args: Sequence[str], config: Dict[str, Any]) -> int:
catalog: Any | None = None
cmdlet_names: List[str] = []
metadata: Dict[str, Dict[str, Any]] = {}
alias_map: Dict[str, str] = {}
try:
import cmdlet_catalog as _catalog
CMDLET.arg[0].choices = _normalize_choice_list(
_catalog.list_cmdlet_names(config=config)
)
metadata = _catalog.list_cmdlet_metadata(config=config)
catalog = _catalog
except Exception:
CMDLET.arg[0].choices = []
metadata = {}
catalog = None
if catalog is not None:
try:
cmdlet_names = catalog.list_cmdlet_names(config=config)
except Exception:
cmdlet_names = []
try:
metadata = catalog.list_cmdlet_metadata(config=config)
except Exception:
metadata = {}
if metadata:
alias_map = _build_alias_map_from_metadata(metadata)
else:
metadata, alias_map = _gather_metadata_from_cmdlet_classes()
if not metadata:
fallback_names = sorted(set(cmdlet_names or list(CMDLET_REGISTRY.keys())))
if fallback_names:
base_meta: Dict[str, Dict[str, Any]] = {}
for cmdname in fallback_names:
canonical = str(cmdname or "").replace("_", "-").lower()
entry: Dict[str, Any]
candidate: Dict[str, Any] | None = None
if catalog is not None:
try:
candidate = catalog.get_cmdlet_metadata(cmdname, config=config)
except Exception:
candidate = None
if candidate:
canonical = candidate.get("name", canonical)
entry = candidate
else:
entry = {
"name": canonical,
"aliases": [],
"usage": "",
"summary": "",
"details": [],
"args": [],
"raw": None,
}
base = base_meta.setdefault(
canonical,
{
"name": canonical,
"aliases": [],
"usage": "",
"summary": "",
"details": [],
"args": [],
"raw": entry.get("raw"),
},
)
if entry.get("aliases"):
base_aliases = set(base.get("aliases", []))
base_aliases.update([a for a in entry.get("aliases", []) if a])
base["aliases"] = sorted(base_aliases)
if not base.get("usage") and entry.get("usage"):
base["usage"] = entry["usage"]
if not base.get("summary") and entry.get("summary"):
base["summary"] = entry["summary"]
if not base.get("details") and entry.get("details"):
base["details"] = entry["details"]
if not base.get("args") and entry.get("args"):
base["args"] = entry["args"]
if not base.get("raw") and entry.get("raw"):
base["raw"] = entry["raw"]
metadata = base_meta
alias_map = _build_alias_map_from_metadata(metadata)
choice_candidates = list(alias_map.keys()) if alias_map else list(metadata.keys())
CMDLET.arg[0].choices = _normalize_choice_list(choice_candidates)
parsed = parse_cmdlet_args(args, CMDLET)
filter_text = parsed.get("filter")