mirror of
https://github.com/siteboon/claudecodeui.git
synced 2026-02-23 17:17:41 +00:00
fix: slash commands with arguments bypass command execution (#392)
* fix: intercept slash commands in handleSubmit to pass arguments correctly When a user types a slash command with arguments (e.g. `/feature implement dark mode`) and presses Enter, the command was not being intercepted as a slash command. Instead, the raw text was sent as a regular message to the Claude API, which responded with "Unknown skill: feature". Root cause: the command autocomplete menu (useSlashCommands) detects commands via the regex `/(^|\s)\/(\S*)$/` which only matches when the cursor is right after the command name with no spaces. As soon as the user types a space to add arguments, the pattern stops matching, the menu closes, and pressing Enter falls through to handleSubmit which sends the text as a plain message — completely bypassing command execution. This fix adds a slash command interceptor at the top of handleSubmit: - Checks if the trimmed input starts with `/` - Extracts the command name (text before the first space) - Looks up the command in the loaded slashCommands list - If found, delegates to executeCommand() which properly extracts arguments via regex and sends them to the backend /api/commands/execute endpoint - The backend then replaces $ARGUMENTS, $1, $2 etc. in the command template Changes: - Added `slashCommands` to the destructured return of useSlashCommands hook - Added slash command interception logic in handleSubmit before message dispatch - Added `executeCommand` and `slashCommands` to handleSubmit dependency array Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: address review — pass rawInput param and cleanup UI state - Pass trimmedInput to executeCommand to avoid stale closure reads - Add UI cleanup (images, command menu, textarea) before early return - Update executeCommand signature to accept optional rawInput parameter Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -271,13 +271,14 @@ export function useChatComposerState({
|
||||
}, [setChatMessages]);
|
||||
|
||||
const executeCommand = useCallback(
|
||||
async (command: SlashCommand) => {
|
||||
async (command: SlashCommand, rawInput?: string) => {
|
||||
if (!command || !selectedProject) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const commandMatch = input.match(new RegExp(`${escapeRegExp(command.name)}\\s*(.*)`));
|
||||
const effectiveInput = rawInput ?? input;
|
||||
const commandMatch = effectiveInput.match(new RegExp(`${escapeRegExp(command.name)}\\s*(.*)`));
|
||||
const args =
|
||||
commandMatch && commandMatch[1] ? commandMatch[1].trim().split(/\s+/) : [];
|
||||
|
||||
@@ -351,6 +352,7 @@ export function useChatComposerState({
|
||||
);
|
||||
|
||||
const {
|
||||
slashCommands,
|
||||
slashCommandsCount,
|
||||
filteredCommands,
|
||||
frequentCommands,
|
||||
@@ -473,6 +475,28 @@ export function useChatComposerState({
|
||||
return;
|
||||
}
|
||||
|
||||
// Intercept slash commands: if input starts with /commandName, execute as command with args
|
||||
const trimmedInput = currentInput.trim();
|
||||
if (trimmedInput.startsWith('/')) {
|
||||
const firstSpace = trimmedInput.indexOf(' ');
|
||||
const commandName = firstSpace > 0 ? trimmedInput.slice(0, firstSpace) : trimmedInput;
|
||||
const matchedCommand = slashCommands.find((cmd: SlashCommand) => cmd.name === commandName);
|
||||
if (matchedCommand) {
|
||||
executeCommand(matchedCommand, trimmedInput);
|
||||
setInput('');
|
||||
inputValueRef.current = '';
|
||||
setAttachedImages([]);
|
||||
setUploadingImages(new Map());
|
||||
setImageErrors(new Map());
|
||||
resetCommandMenuState();
|
||||
setIsTextareaExpanded(false);
|
||||
if (textareaRef.current) {
|
||||
textareaRef.current.style.height = 'auto';
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let messageContent = currentInput;
|
||||
const selectedThinkingMode = thinkingModes.find((mode: { id: string; prefix?: string }) => mode.id === thinkingMode);
|
||||
if (selectedThinkingMode && selectedThinkingMode.prefix) {
|
||||
@@ -639,6 +663,7 @@ export function useChatComposerState({
|
||||
codexModel,
|
||||
currentSessionId,
|
||||
cursorModel,
|
||||
executeCommand,
|
||||
isLoading,
|
||||
onSessionActive,
|
||||
pendingViewSessionRef,
|
||||
@@ -654,6 +679,7 @@ export function useChatComposerState({
|
||||
setClaudeStatus,
|
||||
setIsLoading,
|
||||
setIsUserScrolledUp,
|
||||
slashCommands,
|
||||
thinkingMode,
|
||||
],
|
||||
);
|
||||
|
||||
@@ -22,7 +22,7 @@ interface UseSlashCommandsOptions {
|
||||
input: string;
|
||||
setInput: Dispatch<SetStateAction<string>>;
|
||||
textareaRef: RefObject<HTMLTextAreaElement>;
|
||||
onExecuteCommand: (command: SlashCommand) => void | Promise<void>;
|
||||
onExecuteCommand: (command: SlashCommand, rawInput?: string) => void | Promise<void>;
|
||||
}
|
||||
|
||||
const getCommandHistoryKey = (projectName: string) => `command_history_${projectName}`;
|
||||
|
||||
Reference in New Issue
Block a user