84 lines
3.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
pipeline-core: SlashCommand — 可插拔 slash 命令注册表
slash 命令 = 产线/角色/通用 的快捷指令,用户以 "/" 开头输入时直接触发,
不进入 LLM 循环(类似 Hermes CLI 的 /new /model /tools /skills
命令可插拔:通用命令在 core 注册产线命令在能力包sdlc_ability注册
不同产线可有不同的 slash 命名。
依赖方向pipeline-core本文件纯定义 + 注册表)← pipeline-service注册通用命令
← sdlc_ability注册 SDLC 命令)
"""
import logging
from dataclasses import dataclass, field
from typing import Callable, Dict, List, Optional
logger = logging.getLogger("pipeline.slash")
@dataclass
class SlashCommand:
"""单个 slash 命令。handler 签名async def handler(args, ctx) -> str
- args: 命令后的参数字符串(不含命令名)
- ctx: AgentExecutor 上下文 dictproject_id/user_id/pipeline_id/workspace_dir/config/executor
"""
name: str # 不含 "/" 前缀,如 "new"
description: str = ""
handler: Optional[Callable] = None
scope: str = "global" # global / pipeline / role
scope_id: str = "" # pipeline_id 或 pipeline_id:role
args_hint: str = "" # 参数提示,如 "[task_id]"
# ── 全局注册表 ──
_commands: Dict[str, SlashCommand] = {}
def register_slash_command(cmd: SlashCommand):
"""注册一个 slash 命令(幂等:重复注册覆盖)。"""
_commands[cmd.name] = cmd
logger.debug(f"SlashCommand registered: /{cmd.name} ({cmd.scope})")
def get_slash_commands(pipeline_id: str = "", role: str = "") -> Dict[str, SlashCommand]:
"""取当前上下文可见的 slash 命令(通用 + 产线 + 角色)。"""
result: Dict[str, SlashCommand] = {}
for name, cmd in _commands.items():
if cmd.scope == "global":
result[name] = cmd
elif cmd.scope == "pipeline" and pipeline_id and cmd.scope_id == pipeline_id:
result[name] = cmd
elif cmd.scope == "role" and pipeline_id and role \
and cmd.scope_id == f"{pipeline_id}:{role}":
result[name] = cmd
return result
def resolve_slash_command(text: str, pipeline_id: str = "",
role: str = "") -> Optional[SlashCommand]:
"""从用户输入解析 slash 命令text 以 "/" 开头)。"""
if not text or not text.startswith("/"):
return None
body = text[1:].strip()
if not body:
return None
# 命令名 = 第一个空格前的 token
name = body.split()[0] if " " in body else body
visible = get_slash_commands(pipeline_id, role)
return visible.get(name)
def parse_slash_args(text: str) -> str:
"""提取命令名之后的参数字符串。"""
body = text[1:].strip() if text.startswith("/") else text.strip()
parts = body.split(None, 1)
return parts[1] if len(parts) > 1 else ""
def list_commands() -> List[SlashCommand]:
"""列出所有已注册命令。"""
return list(_commands.values())