6.2 KiB
spec, created, status
| spec | created | status |
|---|---|---|
| docs/specs/ui/slash-command-composer.md | 2026-07-02 | implemented |
Implementation Plan: Slash Command Composer Selection
Overview
The backend already delivers ACP slash commands through session.available_commands, and the frontend already renders them in the shared TipTap composer. The fix is frontend-only: replace the current selection callback that calls onSubmit('/cmd') with draft insertion inside the TipTap suggestion command, remove the now-dangerous submit callback plumbing, then add focused unit and E2E coverage for task chat, quick chat, and mobile touch selection.
Backend
No backend changes are planned. The existing ACP conversion and WebSocket path stays unchanged:
apps/backend/internal/agent/agentctl/transport/acp/adapter_updates.goapps/backend/internal/agent/lifecycle/events.goapps/backend/internal/backendapp/helpers.goapps/web/lib/ws/handlers/available-commands.ts
Reason:
- The bug happens after commands are already in the client store. Selection currently submits from the frontend composer callback.
Frontend
Slash Command Types and Insertion Text
Files:
apps/web/components/task/chat/slash-command-types.ts(new)apps/web/components/task/chat/tiptap-suggestion.tsxapps/web/components/task/chat/slash-command-menu.tsxapps/web/components/task/chat/tiptap-input.tsx
Changes:
- Move
SlashCommandandSlashCommandActionout of the legacyapps/web/hooks/use-inline-slash.tsfile into a chat-local type module. - Add a small pure helper, for example
formatSlashCommandInsertion(command: SlashCommand): string, that returns a slash-prefixed command plus trailing space for continued typing. - In
createSlashSuggestion().command, replace the active TipTap suggestionrangewithformatSlashCommandInsertion(cmd)and keep focus in the editor. - Remove
SlashSuggestionCallbacks.onAgentCommand; selection must not call an external submit callback. - Keep the current menu filtering, keyboard navigation, Escape close behavior, and portal-rendered
SlashCommandMenu.
Reason:
- The selection command is the only place that should mutate the draft. Sending is owned by the normal composer submit path.
Remove Auto-Submit Plumbing
Files:
apps/web/components/task/chat/tiptap-input.tsxapps/web/components/task/chat/chat-input-body.tsxapps/web/components/task/chat/chat-input-container.tsxapps/web/components/task/chat/use-chat-input-container.tsapps/web/components/task/chat/chat-input-body.test.tsxapps/web/hooks/use-inline-slash.ts
Changes:
- Remove the
onAgentCommandprop fromTipTapInput. - Remove
handleAgentCommandfromChatInputEditorAreaProps,buildEditorAreaProps, anduseChatInputContainer. - Delete
apps/web/hooks/use-inline-slash.tsafter moving the shared types ifrg "useInlineSlash"confirms no runtime consumers. If deletion is not clean, update the hook to use the same draft insertion helper and remove itsonAgentCommandcallback. - Update affected mocks and prop builders in tests.
Reason:
- Leaving a
handleAgentCommand -> onSubmit('/cmd')path in the composer keeps the original bug one import away from returning.
Tests
-
What: slash command insertion text is normalized and leaves room for arguments. File:
apps/web/components/task/chat/slash-command-types.test.tsHow: unit test the exported helper withagentCommandName: "slow", labels with and without a leading slash, and command names that contain punctuation such astool:read. -
What: Enter in a suggestion menu still defers to the suggestion plugin instead of submit, including plain-Enter submit mode. File:
apps/web/components/task/chat/use-tiptap-editor.test.tsHow: keep the existingdecideSubmitShortcutregression tests and add a case name that ties it to slash command selection if useful. -
What: removing auto-submit plumbing does not break the chat input container's disabled and submit state derivation. File:
apps/web/components/task/chat/use-chat-input-container.test.tsHow: update existing tests afterhandleAgentCommandis removed; no new container behavior is expected. -
What: TypeScript catches stale prop usage after the prop cleanup. File: no dedicated file. How: run:
cd apps pnpm --filter @kandev/web typecheck
E2E Tests
-
Scenario: GIVEN a task chat session with advertised commands, WHEN the user types
/sand presses Enter to choose/slow, THEN the composer contains/slow, focus remains in the composer, and no user message appears in.chat-message-list. File:apps/web/e2e/tests/chat/slash-command-composer.spec.tsWhat to verify: selection by Enter inserts a draft and does not auto-send. -
Scenario: GIVEN the selected
/slowdraft remains in task chat, WHEN the user types1sand clicks Send, THEN the user message/slow 1sappears and the mock agent responds through the normal command path. File:apps/web/e2e/tests/chat/slash-command-composer.spec.tsWhat to verify: explicit send still sends the edited draft. -
Scenario: GIVEN quick chat is open with an initialized mock agent, WHEN the user selects
/slowfrom the slash menu, THEN the quick chat editor keeps/slowas a draft and no message is sent until explicit send. File:apps/web/e2e/tests/chat/slash-command-composer.spec.tsWhat to verify: the shared composer behavior works in quick chat. -
Scenario: GIVEN mobile task chat has advertised commands, WHEN the user types
/sand taps/slow, THEN the mobile composer keeps/slowas draft text and the Send button remains the only send action. File:apps/web/e2e/tests/chat/mobile-slash-command-composer.spec.tsWhat to verify: touch selection has parity with desktop and no workflow depends on hover or a wide viewport.
Recommended E2E command:
cd apps/web
pnpm e2e:run tests/chat/slash-command-composer.spec.ts tests/chat/mobile-slash-command-composer.spec.ts
Implementation Waves
Wave 1:
Wave 2:
Wave 3: