feat(cli): improve slash command discovery#3736
Conversation
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
E2E Test ReportScopeThis E2E pass focused on the user-visible slash command Phase 3 behavior:
Environment
Build used for E2Enpm run build && npm run typecheck && npm run bundleResult: passed. Known existing warnings appeared from Focused tests also runcd packages/cli && npx vitest run src/ui/components/Help.test.tsx src/ui/commands/helpCommand.test.ts
cd packages/cli && npx vitest run src/ui/hooks/useCommandCompletion.test.ts src/ui/utils/highlight.test.ts src/ui/utils/commandUtils.test.ts src/ui/hooks/slashCommandProcessor.test.ts src/ui/AppContainer.test.tsxObserved results: After resolving merge conflicts with latest cd packages/cli && npx vitest run src/ui/components/Help.test.tsx src/ui/commands/helpCommand.test.ts src/ui/hooks/useDialogClose.test.ts src/ui/AppContainer.test.tsxObserved result: TUI E2E scenarios1.
|
- aaif-goose/goose#8884 blog post: goose with peekaboo (merge-after-nits) - QwenLM/qwen-code#3736 Phase 3 slash command discovery (merge-after-nits) - INDEX.md: drip-170 section with 8 PRs, 1 merge-as-is + 7 merge-after-nits
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Code Coverage Summary
CLI Package - Full Text ReportCore Package - Full Text ReportFor detailed HTML reports, please see the 'coverage-reports-22.x-ubuntu-latest' artifact from the main CI run. |
|
|
||
| ```bash | ||
| tmux new-session -d -s qwen-slash-phase3 -x 200 -y 50 \ | ||
| "cd /tmp/qwen-slash-phase3 && /Users/mochi/code/qwen-code-test/dist/cli.js --approval-mode yolo" |
There was a problem hiding this comment.
[Suggestion] This E2E command hardcodes a developer-local CLI path, so the verification steps are not reproducible for other contributors. The preceding instructions already build the repo-local bundle and say to use node dist/cli.js; this example should use a portable path derived from the checkout instead of /Users/mochi/....
| "cd /tmp/qwen-slash-phase3 && /Users/mochi/code/qwen-code-test/dist/cli.js --approval-mode yolo" | |
| tmux new-session -d -s qwen-slash-phase3 -x 200 -y 50 \ | |
| "cd /tmp/qwen-slash-phase3 && node $(pwd)/dist/cli.js --approval-mode yolo" | |
| sleep 3 |
— gpt-5.5 via Qwen Code /review
wenshao
left a comment
There was a problem hiding this comment.
Also found 2 additional Critical issues that span whole files and cannot be mapped to individual lines:
[Critical] packages/cli/src/services/commandMetadata.ts — new 142-line service file has zero tests
getCommandSourceBadge, getCommandSourceGroup, formatSupportedModes, getCommandDisplayName, getCommandSubcommandNames and formatCommandSourceLabel are all untested. These functions are used by Help, SuggestionsDisplay and completion hooks, with 7+ branches in getCommandSourceBadge alone. Add packages/cli/src/services/commandMetadata.test.ts covering every exported function and branch.
[Critical] packages/cli/src/ui/utils/commandUtils.ts:247-292 — getBestSlashCommandMatch not directly tested
This function is the core matching engine for mid-input ghost text. Its sort logic is new in this PR, yet commandUtils.test.ts only references it in a comment. Add direct tests covering: recentCommands scoring, completionPriority ordering, argumentHint return path, alias matching, and null return on empty partialCommand.
— deepseek-v4-pro via Qwen Code /review
| ({ | ||
| name: cmd.name, | ||
| description: cmd.description, | ||
| input: cmd.argumentHint ? { hint: cmd.argumentHint } : null, |
There was a problem hiding this comment.
[Critical] This replaces PR #3618's acceptsInput logic with a simple cmd.argumentHint check, causing a functional regression in ACP mode.
Commands with subCommands but no argumentHint (e.g., /memory) will get input: null and auto-submit before the user can choose a subcommand. The same applies to commands with completion functions.
| input: cmd.argumentHint ? { hint: cmd.argumentHint } : null, | |
| input: (() => { | |
| const acceptsInput = | |
| cmd.kind !== CommandKind.BUILT_IN || | |
| cmd.completion != null || | |
| cmd.argumentHint != null || | |
| (cmd.subCommands != null && cmd.subCommands.length > 0); | |
| return acceptsInput | |
| ? { hint: cmd.argumentHint ?? '' } | |
| : null; | |
| })(), |
— deepseek-v4-pro via Qwen Code /review
| }); | ||
|
|
||
| const best = matches[0]; | ||
| if (!best) return null; |
There was a problem hiding this comment.
[Critical] Sort order in getBestSlashCommandMatch prioritizes recentOrder over completionPriority, but compareRankedCommandMatches in useSlashCompletion.ts does the opposite (completionPriority before recentScore).
This causes the mid-input ghost text to recommend a different command than what appears first in the dropdown completion menu, producing an inconsistent UX.
| if (!best) return null; | |
| return ( | |
| (right.completionPriority ?? 0) - (left.completionPriority ?? 0) || | |
| recentOrder || | |
| left.name.localeCompare(right.name) | |
| ); |
— deepseek-v4-pro via Qwen Code /review
Code ReviewOverviewPhase 3 of the slash-command experience overhaul (~2,400 LOC). The PR does four things: (1) reworks Strengths
Issues / SuggestionsCorrectness
UX / consistency
Code quality
Performance
SecurityNothing concerning. All new code is read-side display logic; no shell, FS, or network surface added. The regex changes ( RiskLow. The behavior changes are gated to UI surfaces ( VerdictApprove with the fixes above. The two correctness items worth fixing before merge are the decay-constant mismatch and the missing |
- Fix getBestSlashCommandMatch sort order: completionPriority first, recentOrder second — consistent with compareRankedCommandMatches in useSlashCompletion.ts so ghost text and dropdown agree on best match - Fix findSlashCommandTokens to index altNames into commandMap so alias tokens (e.g. /usage for /stats) are highlighted as valid instead of being marked invalid - Fix getRecentScore decay formula: 10 * Math.max(0, 1 - ageMs / RECENT_DECAY_MS) so the recent boost truly decays to 0 within the 10-minute window named by RECENT_DECAY_MS - Fix Help.tsx CommandsHelp scroll indicator to show command count range (e.g. 1-12/49) instead of raw line count (18/108), which was confusing because each command expands into 2-3 render lines - Fix Help.tsx CommandLine key prop: use stable type:text:index key instead of scrollOffset-index to avoid remounting every line on scroll - Internationalize Help.tsx tab labels via t() instead of hardcoded English strings - Add Tab/Shift+Tab to switch tabs hint in Help footer alongside Esc - Add commandMetadata.test.ts with full branch coverage for all 6 exported functions (getCommandSourceBadge, getCommandSourceGroup, formatSupportedModes, getCommandDisplayName, getCommandSubcommandNames, formatCommandSourceLabel) - Add direct unit tests for getBestSlashCommandMatch covering: null on empty input, null on no match, null for non-modelInvocable commands, completionPriority ordering, recentCommands tie-breaking, argumentHint return path, exact-match exclusion without hint, inclusion with hint - Update Session.test.ts expectation for sendAvailableCommandsUpdate to include _meta field that was added in this PR
|
Thanks for the detailed review! All feedback has been addressed in commit c8331ba. Here's a summary: Critical fixesSort order inconsistency (
Recent decay formula mismatch ( Bug fixScroll indicator shows line count instead of command count ( Code quality
Direct tests for
UXTab label i18n — tab labels now go through Footer hint — footer now shows Test update
|
wenshao
left a comment
There was a problem hiding this comment.
测试文件类型错误(6 处 Critical,无法映射到 diff 行)
tsc --noEmit 在以下位置报类型错误——这些是 PR 新增接口变更导致测试 mock 对象未同步更新:
packages/cli/src/ui/components/InputPrompt.test.tsx:184—UseCommandCompletionReturnmock 缺少midInputGhostText属性(TS2741)packages/cli/src/ui/components/InputPrompt.test.tsx:2026—vimModeEnabled不存在于InputPromptProps类型(TS2339)packages/cli/src/ui/components/InputPrompt.test.tsx:2042— 同上(TS2339)packages/cli/src/ui/components/InputPrompt.test.tsx:3480—UIState类型转换缺少isHelpDialogOpen、activeHelpTab等新字段(TS2352)packages/cli/src/ui/hooks/useCommandCompletion.test.ts:49— 找不到命名空间vi,需添加import { vi } from 'vitest'(TS2503)packages/cli/src/ui/hooks/useCommandCompletion.test.ts:65— 同上(TS2503)
测试覆盖缺失
useCommandCompletion.test.ts— 缺少midInputGhostText在只匹配到非modelInvocable命令时返回null的测试useSlashCompletion.test.ts— 缺少recentCommands在非根查询(如/mod)中影响排序的测试
— DeepSeek/deepseek-v4-pro via Qwen Code /review
|
|
||
| try { | ||
| if (commandToExecute) { | ||
| if (!commandToExecute.hidden) { |
There was a problem hiding this comment.
[Critical] setRecentCommands(本次 PR 新增的近期命令追踪逻辑)完全没有测试覆盖。这是整个近期排序功能的基础——如果 count 递增、usedAt 更新或 hidden 命令过滤逻辑出错,补全排序和 ghost text 都会异常。
[Suggestion] setRecentCommands 在 commandToExecute.action() 之前执行(504 行),即使 action 抛出异常,失败的命令也会被记为 recent。建议移到 action 成功返回之后。
— DeepSeek/deepseek-v4-pro via Qwen Code /review
| ); | ||
| } | ||
|
|
||
| export function formatCommandSourceLabel( |
There was a problem hiding this comment.
[Suggestion] formatCommandSourceLabel 已导出并有完整测试,但在整个代码库中无任何调用方,是死代码。建议:接入 SuggestionsDisplay 或 Help 组件,或从本次 PR 中移除。
— DeepSeek/deepseek-v4-pro via Qwen Code /review
| .sort((left, right) => { | ||
| const leftRecent = recentCommands?.get(left.name); | ||
| const rightRecent = recentCommands?.get(right.name); | ||
| const recentOrder = |
There was a problem hiding this comment.
[Suggestion] getBestSlashCommandMatch 与 compareRankedCommandMatches(useSlashCompletion.ts)使用两套不同的排序模型:前者用原始 usedAt 时间戳对比,后者用 count*10 + 10*decay 公式。两者服务于不同场景(ghost text vs 补全菜单)可以理解,但没有注释说明此差异。建议添加注释明确此处为何不同于 compareRankedCommandMatches。
— DeepSeek/deepseek-v4-pro via Qwen Code /review
| type = index === 0 ? 'command' : 'default'; | ||
| } else { | ||
| // Backwards-compatible fallback when no command metadata is provided. | ||
| type = 'command'; |
There was a problem hiding this comment.
[Suggestion] 当 slashCommands 未提供时的回退路径存在行为变更:mid-input 位置的 slash token 现在无条件标记为 command 类型,旧代码无 group 2 因此 mid-input token 从未被高亮。注释标注 "Backwards-compatible fallback" 但实际是新行为。当前生产调用方 InputPrompt.tsx 始终传入 slashCommands,不受影响。
— DeepSeek/deepseek-v4-pro via Qwen Code /review
| [activeTab, onTabChange], | ||
| ); | ||
|
|
||
| useKeypress( |
There was a problem hiding this comment.
[Suggestion] Help 组件(从 83 行扩展到 508 行)缺少错误边界。如果 groupCommands 因异常数据崩溃,用户被锁在 Help 模态框内,唯一恢复方式是 Ctrl+C。建议在 DialogManager 中包裹 Error Boundary,或在 groupCommands 中做 defensive null check。
— DeepSeek/deepseek-v4-pro via Qwen Code /review
- Add test: midInputGhostText is null when only non-modelInvocable commands match - Add test: recentCommands boosts non-root prefix suggestions via recentScore Both tests address coverage gaps identified in code review.
|
已处理 2026-05-08 新增 review 中的剩余两项测试覆盖问题,并已随最新提交推送:1712d946c。\n\n本次补充:\n- :新增“仅匹配到 non-modelInvocable 命令时 返回 ”测试。\n- :新增“ 在非根前缀查询下参与排序”测试(使用同等 ,验证 recent 分数生效)。\n\n本地验证:\n- + 共 51 tests 全通过。\n\n之前指出的 6 处类型错误已在更早提交中修复(当前分支 通过)。 |
|
补充说明(上一条评论因 shell 转义导致格式损坏,这里给出完整版本): 已处理 2026-05-08 新增 review 中的剩余两项测试覆盖问题,并已随最新提交推送:1712d946c。 本次补充:
本地验证:
之前指出的 6 处类型错误已在更早提交中修复(当前分支 |
Summary
/helpinto an interactive tabbed dialog with General, Commands, and Custom Commands views; command lists are scrollable with keyboard navigation._metaand documented the Phase 3 slash command design./help, tab switching, scrolling, and Esc close.Validation
/helpTabDownPageDownTabDownUpPageDownPageUpEsc/help commands/doctor/helpopens a General tab dialog rather than adding a static history item.TabandShift+Tabswitch tabs,Esccloses the dialog, and long command lists scroll with↑/↓andPageUp/PageDown./help commandsdoes not act as a help subcommand and still opens the default General dialog./doctorremains available./helpopens General,Tabswitches to Commands, command list scrolling displaysUse ↑/↓ to scroll (18/105),DownandPageDownscroll, Custom Commands resets scroll position and scrolls independently, andEsccloses the dialog./doctorreturns a Doctor Report with11 passed, 0 warnings, 0 failures.Scope / Risk
/helpsurface now behaves as an interactive dialog, so keyboard routing for Tab, Shift+Tab, arrows, PageUp/PageDown, and Esc is more important than before./helpintentionally has no subcommands;/help commandsis treated like normal/helpand does not directly select a tab./release-notesis intentionally not added.Testing Matrix
Testing matrix notes:
node dist/cli.jsTUI E2E.Linked Issues / Bugs
No linked issues
🤖 Generated with Qwen Code