package vip.mate.channel; import java.util.LinkedHashMap; import java.util.Locale; import java.util.Map; import java.util.Optional; /** * User-typed channel control commands that should be handled by the platform * instead of being sent to the agent as normal prompt text. *

* Matching rules: *

*/ final class ChannelMagicCommand { /** Platform-level command kinds, dispatched by {@link ChannelMessageRouter}. */ enum Type { CLEAR, NEW, HELP, STATUS, STOP, MODEL } /** A recognized command plus its raw (possibly empty) argument string. */ record Parsed(Type type, String args) { } /** * Alias token → command type. LinkedHashMap keeps registration ordering * stable. Every bare alias also registers its "/"-prefixed twin. */ private static final Map ALIASES = buildAliases(); private ChannelMagicCommand() { } static Optional parse(String text) { String trimmed = text == null ? "" : text.trim(); if (trimmed.isEmpty()) { return Optional.empty(); } String lower = trimmed.toLowerCase(Locale.ROOT); Type wholeMatch = ALIASES.get(lower); if (wholeMatch != null) { return Optional.of(new Parsed(wholeMatch, "")); } // Only slash-prefixed commands may carry arguments; bare words with a // trailing remainder are ordinary prompts, never commands. if (!lower.startsWith("/")) { return Optional.empty(); } int ws = indexOfWhitespace(lower); if (ws < 0) { return Optional.empty(); } Type type = ALIASES.get(lower.substring(0, ws)); if (type == null) { return Optional.empty(); } return Optional.of(new Parsed(type, trimmed.substring(ws).trim())); } static String clearConfirmation() { return "✅ 上下文已清理,后续消息会从新的上下文开始。"; } static String newConfirmation() { return "✨ 已开启新会话,之前的上下文不会带入。"; } static String stopConfirmation() { return "⏹️ 已停止当前任务。"; } static String stopNothingRunning() { return "当前没有进行中的任务。"; } static String helpText() { return """ 🪄 可用命令: /clear — 清空当前会话上下文(别名:/reset、清空上下文) /new — 开启新会话(别名:新会话) /stop — 停止当前进行中的任务(别名:停止) /status — 查看当前会话状态(别名:状态) /model — 查看可用模型;/model <名称> 切换本会话模型;/model reset 恢复默认 /help — 显示本帮助(别名:帮助)"""; } private static Map buildAliases() { Map aliases = new LinkedHashMap<>(); register(aliases, Type.CLEAR, "clear", "reset", "清空", "清空上下文", "清理上下文", "清除上下文", "重置上下文"); register(aliases, Type.NEW, "new", "新会话", "新对话"); register(aliases, Type.HELP, "help", "帮助"); register(aliases, Type.STATUS, "status", "状态"); register(aliases, Type.STOP, "stop", "停止"); // Slash-only: "model" / "模型" are common standalone words in normal // prompts ("模型是什么?"), so the bare form must never be a command. registerSlashOnly(aliases, Type.MODEL, "model", "模型"); return aliases; } private static void register(Map aliases, Type type, String... names) { for (String name : names) { aliases.put(name, type); aliases.put("/" + name, type); } } /** Register only the "/"-prefixed form — for aliases whose bare word is ordinary prose. */ private static void registerSlashOnly(Map aliases, Type type, String... names) { for (String name : names) { aliases.put("/" + name, type); } } private static int indexOfWhitespace(String text) { for (int i = 0; i < text.length(); i++) { if (Character.isWhitespace(text.charAt(i))) { return i; } } return -1; } }