Files
wehub-resource-sync 4b6817381b
CI (OpenClaw E2E) / openclaw test (push) Has been cancelled
CI / coverage-report (push) Has been cancelled
CI / test-kubernetes (push) Has been cancelled
CI / should-run-thorough (push) Has been cancelled
CI / test-thorough (cloudwatch-demo) (push) Has been cancelled
CI / test-thorough (flink-ecs) (push) Has been cancelled
CI / test-thorough (upstream-lambda) (push) Has been cancelled
CI / test-thorough (prefect-ecs-fargate) (push) Has been cancelled
Release / build-binaries (zip, opensre.exe, onefile, windows-latest, windows-x64) (push) Has been cancelled
Benchmark image — build + push to ECR (any adapter) / build + push (push) Has been cancelled
CI / quality (ubuntu-latest) (push) Has been cancelled
CI / test (tools-runtime) (push) Has been cancelled
CI / test (e2e-general) (push) Has been cancelled
CI / test (cli-runtime) (push) Has been cancelled
CI / test (e2e-provider-and-openclaw) (push) Has been cancelled
CI / test (integrations-and-misc) (push) Has been cancelled
Release / verify (push) Has been cancelled
Release / build-python-dist (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, macos-15-intel, darwin-x64) (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, macos-latest, darwin-arm64) (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04, linux-x64) (push) Has been cancelled
Release / publish-release (push) Has been cancelled
Release / publish-main-release (push) Has been cancelled
Interactive Shell Live (PR + post-merge) / turn-checks (no-LLM) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Interactive Shell Live (PR + post-merge) / turn-live shard ${{ matrix.shard_index }} (push) Has been cancelled
Release / prepare (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04-arm, linux-arm64) (push) Has been cancelled
Synthetic Deterministic Tests / Synthetic offline (deterministic) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:10:45 +08:00

199 lines
7.1 KiB
Python

"""Slash commands: /help and /?."""
from __future__ import annotations
from collections.abc import Sequence
from rich.console import Console
from rich.markup import escape
from surfaces.interactive_shell.command_registry.types import SlashCommand
from surfaces.interactive_shell.runtime import Session
from surfaces.interactive_shell.ui import ERROR
from surfaces.interactive_shell.ui.components.choice_menu import repl_tty_interactive
from surfaces.interactive_shell.ui.help.help_menu import (
HelpSection,
choose_help_command,
render_command_detail,
render_help_index,
render_section_detail,
)
QUICK_ACCESS_COMMANDS: list[str] = [
"/investigate",
"/integrations",
"/model",
"/health",
"/watch",
"/status",
"/help",
]
def _quick_access_section() -> HelpSection:
from surfaces.interactive_shell.command_registry import SLASH_COMMANDS
cmds = [SLASH_COMMANDS[n] for n in QUICK_ACCESS_COMMANDS if n in SLASH_COMMANDS]
return ("Quick Access", cmds)
def _raw_help_sections() -> list[HelpSection]:
from surfaces.interactive_shell.command_registry.agents import COMMANDS as AGENTS_CMDS
from surfaces.interactive_shell.command_registry.alerts import COMMANDS as ALERTS_CMDS
from surfaces.interactive_shell.command_registry.background_cmds import (
COMMANDS as BACKGROUND_CMDS,
)
from surfaces.interactive_shell.command_registry.cli_parity import (
COMMANDS as PARITY_COMMANDS,
)
from surfaces.interactive_shell.command_registry.diagnostics_cmds import (
COMMANDS as DIAGNOSTICS_CMDS,
)
from surfaces.interactive_shell.command_registry.gateway_cmds import (
COMMANDS as GATEWAY_CMDS,
)
from surfaces.interactive_shell.command_registry.integrations import COMMANDS as INT_CMDS
from surfaces.interactive_shell.command_registry.investigation import COMMANDS as INV_CMDS
from surfaces.interactive_shell.command_registry.model import COMMANDS as MODEL_CMDS
from surfaces.interactive_shell.command_registry.privacy_cmds import (
COMMANDS as PRIVACY_CMDS,
)
from surfaces.interactive_shell.command_registry.rca_cmds import COMMANDS as RCA_CMDS
from surfaces.interactive_shell.command_registry.session_cmds import (
COMMANDS as SESSION_CMDS,
)
from surfaces.interactive_shell.command_registry.settings_cmds import (
COMMANDS as SETTINGS_CMDS,
)
from surfaces.interactive_shell.command_registry.system import COMMANDS as SYS_CMDS
from surfaces.interactive_shell.command_registry.tasks_cmds import COMMANDS as TASK_CMDS
from surfaces.interactive_shell.command_registry.theme import COMMANDS as THEME_CMDS
from surfaces.interactive_shell.command_registry.tools_cmds import COMMANDS as TOOLS_CMDS
from surfaces.interactive_shell.command_registry.watch_cmds import COMMANDS as WATCH_CMDS
return [
_quick_access_section(),
("Help", list(COMMANDS)),
(
"Session",
list(SESSION_CMDS)
+ list(BACKGROUND_CMDS)
+ list(SETTINGS_CMDS)
+ list(DIAGNOSTICS_CMDS),
),
("Integrations, Models & Tools", list(INT_CMDS) + list(MODEL_CMDS) + list(TOOLS_CMDS)),
("Investigation", list(INV_CMDS) + list(RCA_CMDS)),
("Privacy", list(PRIVACY_CMDS)),
("Tasks", list(TASK_CMDS) + list(WATCH_CMDS) + list(GATEWAY_CMDS)),
("Theme", list(THEME_CMDS)),
("Agents", list(AGENTS_CMDS)),
("Alerts", list(ALERTS_CMDS)),
("CLI (parity)", list(PARITY_COMMANDS)),
("System", list(SYS_CMDS)),
]
_QUICK_ACCESS_SECTION_NAME = "Quick Access"
def _help_sections() -> list[HelpSection]:
"""Return user-visible help sections with duplicate command names hidden.
The "Quick Access" section is intentionally exempted from the dedup set so
its curated commands remain visible in their canonical sections too (e.g.
``/help investigation`` still contains ``/investigate``).
"""
seen: set[str] = set()
sections: list[HelpSection] = []
for section_name, commands in _raw_help_sections():
visible: list[SlashCommand] = []
is_quick_access = section_name == _QUICK_ACCESS_SECTION_NAME
for command in commands:
if command.name in seen:
continue
visible.append(command)
if not is_quick_access:
seen.add(command.name)
sections.append((section_name, visible))
return sections
def _find_command(sections: Sequence[HelpSection], target: str) -> SlashCommand | None:
normalized = target.strip().lower()
if not normalized:
return None
if not normalized.startswith("/"):
normalized = f"/{normalized}"
for _section_name, commands in sections:
for command in commands:
if command.name.lower() == normalized:
return command
return None
def _find_section(
sections: Sequence[HelpSection],
target: str,
) -> tuple[str, Sequence[SlashCommand]] | None:
normalized = target.strip().lower().replace("-", " ")
for section_name, commands in sections:
aliases = {
section_name.lower(),
section_name.lower().replace("&", "and"),
section_name.lower().replace(" & ", " "),
}
if normalized in aliases:
return section_name, commands
return None
def _cmd_help(_session: Session, console: Console, args: list[str]) -> bool:
sections = _help_sections()
if args:
target = " ".join(args).strip()
if target.lower() in {"all", "commands"}:
render_help_index(console, sections)
return True
if not target.startswith("/"):
section = _find_section(sections, target)
if section is not None:
section_name, commands = section
render_section_detail(console, section_name, commands)
return True
command = _find_command(sections, target)
if command is not None:
render_command_detail(console, command)
return True
console.print(f"[{ERROR}]unknown help topic:[/] {escape(target)}")
console.print(
"Try [bold]/help[/bold], [bold]/help /model[/bold], or [bold]/help tasks[/bold]."
)
return True
if repl_tty_interactive():
selected = choose_help_command(sections)
if selected:
# Re-dispatch the selected slash command so Enter in the help picker
# runs the command directly instead of only opening details.
from surfaces.interactive_shell.command_registry import dispatch_slash
return dispatch_slash(selected, _session, console)
return True
render_help_index(console, sections)
return True
COMMANDS: list[SlashCommand] = [
SlashCommand(
"/help",
"Show available commands.",
_cmd_help,
usage=("/help", "/help <command>", "/help <category>"),
examples=("/help /model", "/help tasks"),
),
SlashCommand("/?", "Shortcut for /help.", _cmd_help),
]
__all__ = ["COMMANDS", "QUICK_ACCESS_COMMANDS"]