Skip to Content
ДизайнSlash CommandSlash Command: дорожная карта рефакторинга

Slash Command: дорожная карта рефакторинга

Общая цель

Создать платформу команд, которая по внешнему восприятию на 95% соответствует Claude Code, используя внутреннюю архитектурную стилистику Qwen, и одновременно устранить три ключевые проблемы: расщепление на три режима, единый источник команд, невозможность вызова prompt command моделью.


Основные принципы проектирования

  1. Каждый Phase может быть выпущен независимо: поведение после завершения самосогласованно, не требует последующих Phases для работы.
  2. Phase 1 — чистая инфраструктура: не меняет ни один из доступных наборов команд, за исключением исправления ошибочного перехвата MCP_PROMPT.
  3. Изменения поведения и архитектуры разделены: Phase 1 занимается архитектурой, Phase 2 — расширением возможностей.
  4. Не копируем внутреннюю архитектуру Claude Code: но выравниваемся по воспринимаемым пользователем возможностям.

Phase 1: Перестройка инфраструктуры (чистая архитектура, ноль изменений поведения)

Цель

Создать единую модель метаданных команд и механизм управления между режимами, обеспечивающий фундамент для всех последующих Phases.

Функциональные пункты

1.1 Расширение модели метаданных SlashCommand

Добавить в существующий интерфейс SlashCommand следующие поля:

Поля источника

  • source: CommandSource — перечисление источника команды (builtin-command / bundled-skill / skill-dir-command / plugin-command / mcp-prompt и т.д.)
  • sourceLabel?: string — отображаемая метка источника (например, "Built-in" / "MCP: github-server")

Поля режимов

  • supportedModes: ExecutionMode[] — декларация, в каких режимах выполнения команда доступна (interactive / non_interactive / acp)

Поля типа выполнения

  • commandType: CommandType — декларация типа выполнения (prompt / local / local-jsx)

Поля видимости

  • userInvocable: boolean — может ли пользователь вызвать команду через slash (по умолчанию true)
  • modelInvocable: boolean — может ли модель вызвать команду через tool call (по умолчанию false)

Вспомогательные поля метаданных (зарезервированы для Phase 3, в Phase 1 только определение, без использования)

  • argumentHint?: string — подсказка по аргументам, например, "<model-id>" / "show|list|set"
  • whenToUse?: string — описание, когда вызывать команду (для модели)
  • examples?: string[] — примеры использования

1.2 Loader заполняет поля source/commandType

Каждый Loader при построении SlashCommand обязан заполнять source и commandType:

LoadersourcecommandType
BuiltinCommandLoaderbuiltin-commandДекларируется каждой командой (local / local-jsx)
BundledSkillLoaderbundled-skillprompt
FileCommandLoader (пользователь/проект)skill-dir-commandprompt
FileCommandLoader (плагин)plugin-commandprompt
McpPromptLoadermcp-promptprompt

1.3 Встроенные команды декларируют supportedModes и commandType

Для всех built-in команд явно декларировать:

  • commandType: local (без зависимости от UI) или local-jsx (зависит от dialog/React)
  • supportedModes: команды класса local декларируют ['interactive', 'non_interactive', 'acp']; команды класса local-jsx декларируют ['interactive']

1.4 Замена жёстко заданного белого списка на фильтрацию на основе возможностей

  • Удалить константу ALLOWED_BUILTIN_COMMANDS_NON_INTERACTIVE
  • Удалить функцию filterCommandsForNonInteractive
  • Добавить функцию filterCommandsForMode(commands, mode), которая фильтрует на основе поля supportedModes
  • Добавить вспомогательную функцию getEffectiveSupportedModes(cmd) (учитывает стратегию по умолчанию для CommandKind)
  • Изменить сигнатуры функций handleSlashCommand / getAvailableCommands, убрав параметр allowedBuiltinCommandNames

1.5 CommandService превращается в единый Registry

  • Добавить метод getCommandsForMode(mode: ExecutionMode)
  • Добавить метод getModelInvocableCommands() (используется в Phase 2/3, в Phase 1 предоставляет интерфейс)
  • Существующий getCommands() остаётся без изменений (используется в interactive)

Критерии приёмки

  • Интерфейс SlashCommand содержит все новые поля, TypeScript компилируется без ошибок
  • Все Loader заполняют поля source и commandType
  • Все built-in команды декларируют commandType и supportedModes
  • ALLOWED_BUILTIN_COMMANDS_NON_INTERACTIVE удалена, заменена на фильтр возможностей
  • Набор команд, доступных в non-interactive, полностью совпадает с состоянием до рефакторинга (существующие тесты не ломаются)
  • MCP prompt commands корректно выполняются в режимах non-interactive/acp (исправлено некорректное ограничение)
  • CommandService.getCommandsForMode('non_interactive') возвращает правильный набор команд
  • Все существующие тесты проходят

Phase 2: Расширение возможностей (упорядочивание команд и вызов prompt command моделью)

Цель

На основе метаданных из Phase 1 расширить доступность команд в трёх режимах и обеспечить канал вызова prompt command моделью.

Функциональные пункты

2.1 Расширение набора команд, доступных в non-interactive / acp

Принципы проектирования семантики ACP

Перед расширением команд на режимы ACP/non-interactive необходимо соблюдать следующие принципы:

  1. Разный получатель: в режиме ACP сообщения адресованы IDE (плагины Zed/VS Code), а не пользователю терминала. Содержимое должно быть в формате plain text или Markdown, не должно содержать ANSI-стилей, специфичных для терминала.
  2. Стратегия реализации — добавление ветки режима, а не замена: правильный подход — добавить проверку режима внутри action команды — interactive-путь сохраняет существующую логику отрисовки UI, non_interactive/acp-путь возвращает message или submit_prompt, пригодные для машинного потребления. Оба пути сосуществуют в одной функции action.
  3. Для операций с состоянием необходимо пояснять семантику: при однократном неинтерактивном вызове (например, параметр CLI -p) изменения в командах с состоянием (/model set, /language set) действуют только в рамках текущей сессии. Это должно быть указано в тексте ответа команды.
  4. Только чтение vs c побочными эффектами: команды только для чтения (/about, /stats) возвращают текущий текст состояния; команды с побочными эффектами (/model set, /language set) должны в ответе подтвердить результат операции.
  5. Избегать побочных эффектов, зависящих от окружения: операции, требующие графического окружения (открытие браузера (/docs, /insight), работа с буфером обмена (/copy)), в пути non_interactive/acp должны быть пропущены, вместо этого в тексте ответа возвращаться соответствующий URL или само содержимое.

Обзор команд, подлежащих расширению

Примечание: btw, bug, compress, context, init, summary уже расширены на все режимы в Phase 1 и не входят в список данного этапа.

Следующие 13 команд будут расширены на режимы non_interactive и acp в Phase 2:

Класс A: action уже возвращает message или submit_prompt, нужно только расширить supportedModes и спроектировать содержимое сообщения ACP

КомандаТип возвратаОсобенности обработки ACP/non-interactive
/copymessageВ ACP нет буфера обмена — вместо этого в тексте ответа возвращается само содержимое или подсказка
/exportmessageВозвращается полный путь к экспортированному файлу
/plansubmit_promptИзменения не требуются, просто расширение режимов
/restoremessageВозвращается описание результата операции восстановления
/languagemessageВозвращается текущая настройка языка или текст подтверждения изменения
/statuslinesubmit_promptИзменения не требуются, просто расширение режимов

Класс A’: с аргументами нормальное выполнение, без аргументов — открытие dialog (требуется добавить обработку без аргументов для non-interactive)

КомандаПоведение без аргументов в interactiveПоведение без аргументов в non_interactive/acp
/modelОткрыть диалог выбора моделиВернуть текущее имя модели и пояснительный текст
/approval-modeОткрыть диалог режима утвержденияВернуть текущий режим утверждения и пояснительный текст

Класс B: внутри action используется context.ui.addItem() для рендеринга React-компонентов, требуется добавить ветку режима, возвращающую plain text

КомандаПоведение в interactiveВозвращаемое содержимое в non_interactive/acp
/aboutРендеринг React-компонента версии/конфигурацииВерсия, текущая модель, ключевые настройки в формате plain text
/statsРендеринг компонента статистики токенов/затратСтатистика сессии в формате plain text
/insightРендеринг компонента анализа + открытие браузераnon_interactive — синхронная генерация, возвращает путь к файлу; acp — отправка прогресса и результата через stream_messages
/docsРендеринг точки входа в документацию + открытие браузераВозвращает URL документации, браузер не открывается

Класс C: особая обработка

КомандаПоведение в interactiveПоведение в non_interactive/acp
/clearВызов context.ui.clear() для очистки отображения терминалаВозвращает сообщение-маркер границы контекста, содержимое: "Context cleared. Previous messages are no longer in context."

2.2 Вызов prompt command моделью

  • В CommandService (или CommandRegistry) реализовать getModelInvocableCommands() — возвращает все команды с modelInvocable: true
  • Команды, загруженные BundledSkillLoader, FileCommandLoader (пользовательские/проектные), пометить как modelInvocable: true
  • MCP prompt не помечаются как modelInvocable: MCP prompt вызываются моделью через независимый механизм MCP tool call, без посредничества SkillTool
  • Переработать SkillTool: перейти от потребления только SkillManager.listSkills() к одновременному потреблению CommandService.getModelInvocableCommands()
  • Построить единое описание вызываемых моделью команд и внедрить его в description SkillTool

2.3 Обнаружение slash-команды в середине ввода (базовая версия)

  • В InputPrompt обнаруживать токен slash рядом с курсором (не только в начале строки)
  • При обнаружении токена slash отображать через inline ghost text имя наиболее подходящей команды (Tab для принятия)
  • Не включает выпадающее меню автодополнения, подсказки аргументов, source badge и т.д. (это в Phase 3)
  • Набор кандидатов для ghost text ограничен командами с modelInvocable: true (skill / file command)

Критерии приёмки

2.1 Расширение команд

  • Класс A: /copy, /export, /plan, /restore, /language, /statusline корректно выполняются в режимах non-interactive и acp и возвращают осмысленный текстовый вывод
  • Класс A’: /model, /approval-mode без аргументов в non-interactive/acp возвращают текущий текст состояния (не открывают dialog); с аргументами выполняют изменение и возвращают текст подтверждения
  • Класс B: /about, /stats, /docs в non-interactive/acp возвращают plain text, /docs не открывает браузер; /insight в non_interactive синхронно генерирует и возвращает message с путём к файлу, в acp отправляет прогресс через stream_messages
  • Класс C: /clear в non-interactive/acp возвращает message-маркер границы контекста, не вызывает context.ui.clear()
  • Все расширенные команды в interactive режиме ведут себя полностью идентично состоянию до рефакторинга (без регрессии)

2.2 Вызов моделью

  • Модель в диалоге может через SkillTool вызывать bundled skill и file command (пользовательские/проектные)
  • MCP prompt не проходят через SkillTool, вызываются моделью нативно через механизм MCP tool call
  • Модель не может вызывать built-in команды (userInvocable: true, modelInvocable: false)
  • Description SkillTool содержит описания всех команд с modelInvocable

2.3 Slash в середине ввода

  • Slash в середине ввода: при вводе / в тексте через inline ghost text отображается наиболее подходящая команда (Tab для принятия)

Phase 3: Выравнивание опыта (улучшение автодополнения + дополнение команд Claude Code)

Цель

На основе метаданных и возможностей команд из Phase 1/2 дополнить опыт автодополнения и добавить команды, присутствующие в Claude Code, но отсутствующие в Qwen Code.

Функциональные пункты

3.1 Улучшение опыта автодополнения

Source badge

  • Отображать в меню автодополнения метку источника команды ([MCP] уже есть, расширить на [Skill], [Custom] и т.д.)
  • Использовать поля source / sourceLabel для рендеринга

Подсказка аргументов

  • Отображать argumentHint после имени команды в меню автодополнения (например, set <model-id>)
  • argumentHint берётся из поля метаданных Phase 1

Сортировка по недавно использованным

  • Записывать недавно использованные пользователем команды (на уровне сессии, без персистентности)
  • При сортировке автодополнений отдавать приоритет недавно использованным командам

Подсветка совпадения по псевдониму

  • Когда автодополнение совпадает по altNames, а не по основному имени, указывать это при отображении (например, help (alias: ?))

Стратегия разрешения конфликтов

  • Явный приоритет: built-in > bundled/skill-dir > plugin > mcp
  • При конфликте команда с низким приоритетом переименовывается (например, pluginName.commandName)

3.2 Slash-команда в середине ввода (полная версия)

  • На основе базовой версии Phase 2 добавить отображение подсказок аргументов и source badge
  • Подсказка ghost text (при вводе /he отображается приглушённая подсказка /help)
  • Подсветка токена команды (уже совпавший slash command отображается другим цветом)

3.3 Реструктуризация справки

Изменить /help с плоского списка на группированный:

  • Built-in Commands (local + local-jsx, с указанием mode)
  • Bundled Skills
  • Custom Commands (пользовательские/проектные file commands)
  • Plugin Commands
  • MCP Commands

Каждая команда отображает: имя, argumentHint, description, source, метку supportedModes

3.4 Расширение метаданных ACP available commands

В sendAvailableCommandsUpdate() предоставить ACP-клиенту больше метаданных:

  • argumentHint
  • source
  • supportedModes
  • subcommands (список имён)
  • modelInvocable

3.5 Дополнение недостающих команд Claude Code

Подтвердить и вернуть уже существующую в Qwen Code команду /doctor; /release-notes не включать в этот этап, чтобы не вводить built-in команду без явной продуктовой потребности.

КомандаТипОписание
/doctorlocalСамодиагностика окружения: вывод состояния конфигурации/подключений/инструментов

Примечание: команды задач, такие как /review, /commit, предоставляются в виде bundled skill, не входят в данный список.

Критерии приёмки

  • Меню автодополнения отображает source badge ([MCP], [Skill], [Custom])
  • Меню автодополнения отображает argumentHint (например, set <model-id>)
  • Недавно использованные команды отображаются в списке автодополнений с приоритетом
  • При совпадении по псевдониму в элементе автодополнения указывается исходное имя
  • Slash в середине ввода: ghost text отображается корректно
  • /help отображается в стиле Claude Code с разделением на вкладки, без нагромождения команд, и на странице команд отображаются метки поддерживаемых режимов
  • ACP available commands содержит поля argumentHint, source, subcommands
  • Команда /doctor доступна
  • Команда /doctor выполняется в режиме non-interactive (возвращает message)
  • Команда /release-notes не добавляется

Зависимости между Phases

Phase 1 (метаданные + единая фильтрация) ├──► Phase 2 (расширение возможностей) │ │ │ ├──► разделение подкоманд slash command │ └──► вызов prompt command моделью (требует getModelInvocableCommands()) └──► Phase 3 (выравнивание опыта) ├──► source badge (требует поле source из Phase 1) ├──► argument hint (требует поле argumentHint из Phase 1) └──► группировка Help (требует поле source из Phase 1)

Phase 2 и Phase 3 не зависят друг от друга и могут выполняться параллельно (или порядок некоторых подпунктов может быть изменён в зависимости от приоритетов).

Last updated on