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

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

Общая цель

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


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

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

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

Цель

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

Функциональные возможности

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 command (по умолчанию true)
  • modelInvocable: boolean: может ли модель вызвать команду через tool call (по умолчанию false)

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

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

1.2 Заполнение полей source/commandType в Loader

Каждый 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 Замена жестко заданного белого списка на фильтрацию на основе capabilities

  • Удалить константу 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 удален и заменен на capability filter
  • Набор доступных команд в 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. Stateful-операции требуют пояснения семантики: при однократном неинтерактивном вызове (например, флаг CLI -p) изменения stateful-команд вроде /model set, /language set действуют только в рамках текущей session. Это необходимо указать в тексте ответа команды.
  4. Read-only vs side-effects: read-only команды (например, /about, /stats) напрямую возвращают текст текущего состояния; команды с side-effects (например, /model set, /language set) должны подтверждать результат операции в ответе.
  5. Избегать side-effects, зависящих от окружения: операции, зависящие от графического окружения, такие как открытие браузера (/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Открывает dialog выбора моделиВозвращает имя текущей модели и поясняющий текст
/approval-modeОткрывает dialog режима согласованияВозвращает текущий режим согласования и поясняющий текст

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

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

Категория C: специальная обработка

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

2.2 Настройка вызова prompt command моделью

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

2.3 Обнаружение mid-input slash command (базовая версия)

  • Обнаруживать slash token рядом с курсором в InputPrompt (не только в начале строки)
  • При обнаружении slash token показывать имя наиболее подходящей команды через inline ghost text (принимается по Tab)
  • Не включает dropdown-меню автодополнения, argument hints, 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 возвращают plain text в non-interactive/acp, /docs не открывает браузер; /insight в non_interactive синхронно генерирует и возвращает message с путем к файлу, в acp отправляет прогресс через stream_messages
  • Категория C: /clear в non-interactive/acp возвращает message-маркер границы контекста, не вызывая context.ui.clear()
  • Поведение всех расширенных команд в режиме interactive полностью совпадает с состоянием до рефакторинга (без регрессий)

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

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

2.3 mid-input slash

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

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

Цель

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

Функциональные возможности

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

source badge

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

argument hint

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

Сортировка recently used

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

Подсветка совпадений по alias

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

Выравнивание стратегии конфликтов

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

3.2 Полная версия mid-input slash command

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

3.3 Реструктуризация каталога /help

Преобразовать /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

Раскрыть дополнительные метаданные для ACP-клиента в sendAvailableCommandsUpdate():

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

3.5 Добавление отсутствующих команд из Claude Code

Добавить часто используемые команды, которые есть в Claude Code, но отсутствуют в Qwen Code:

КомандаТипОписание
/doctorlocalСамодиагностика окружения, вывод диагностики состояния конфигурации/подключений/инструментов
/release-noteslocalОтображение changelog текущей версии
/costlocalОтображение расхода токенов и оценки стоимости для текущей session

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

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

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

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

Phase 1(元数据 + 统一过滤) ├──► Phase 2(能力扩展) │ │ │ ├──► slash command 子命令拆分 │ └──► prompt command 模型调用(需要 getModelInvocableCommands()) └──► Phase 3(体验对齐) ├──► source badge(需要 Phase 1 source 字段) ├──► argument hint(需要 Phase 1 argumentHint 字段) └──► Help 分组(需要 Phase 1 source 字段)

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

Last updated on