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
199 lines
7.1 KiB
Python
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"]
|