mirror of
https://github.com/siteboon/claudecodeui.git
synced 2026-07-11 08:55:42 +08: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]);
|
}, [setChatMessages]);
|
||||||
|
|
||||||
const executeCommand = useCallback(
|
const executeCommand = useCallback(
|
||||||
async (command: SlashCommand) => {
|
async (command: SlashCommand, rawInput?: string) => {
|
||||||
if (!command || !selectedProject) {
|
if (!command || !selectedProject) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
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 =
|
const args =
|
||||||
commandMatch && commandMatch[1] ? commandMatch[1].trim().split(/\s+/) : [];
|
commandMatch && commandMatch[1] ? commandMatch[1].trim().split(/\s+/) : [];
|
||||||
|
|
||||||
@@ -351,6 +352,7 @@ export function useChatComposerState({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
slashCommands,
|
||||||
slashCommandsCount,
|
slashCommandsCount,
|
||||||
filteredCommands,
|
filteredCommands,
|
||||||
frequentCommands,
|
frequentCommands,
|
||||||
@@ -473,6 +475,28 @@ export function useChatComposerState({
|
|||||||
return;
|
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;
|
let messageContent = currentInput;
|
||||||
const selectedThinkingMode = thinkingModes.find((mode: { id: string; prefix?: string }) => mode.id === thinkingMode);
|
const selectedThinkingMode = thinkingModes.find((mode: { id: string; prefix?: string }) => mode.id === thinkingMode);
|
||||||
if (selectedThinkingMode && selectedThinkingMode.prefix) {
|
if (selectedThinkingMode && selectedThinkingMode.prefix) {
|
||||||
@@ -639,6 +663,7 @@ export function useChatComposerState({
|
|||||||
codexModel,
|
codexModel,
|
||||||
currentSessionId,
|
currentSessionId,
|
||||||
cursorModel,
|
cursorModel,
|
||||||
|
executeCommand,
|
||||||
isLoading,
|
isLoading,
|
||||||
onSessionActive,
|
onSessionActive,
|
||||||
pendingViewSessionRef,
|
pendingViewSessionRef,
|
||||||
@@ -654,6 +679,7 @@ export function useChatComposerState({
|
|||||||
setClaudeStatus,
|
setClaudeStatus,
|
||||||
setIsLoading,
|
setIsLoading,
|
||||||
setIsUserScrolledUp,
|
setIsUserScrolledUp,
|
||||||
|
slashCommands,
|
||||||
thinkingMode,
|
thinkingMode,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ interface UseSlashCommandsOptions {
|
|||||||
input: string;
|
input: string;
|
||||||
setInput: Dispatch<SetStateAction<string>>;
|
setInput: Dispatch<SetStateAction<string>>;
|
||||||
textareaRef: RefObject<HTMLTextAreaElement>;
|
textareaRef: RefObject<HTMLTextAreaElement>;
|
||||||
onExecuteCommand: (command: SlashCommand) => void | Promise<void>;
|
onExecuteCommand: (command: SlashCommand, rawInput?: string) => void | Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const getCommandHistoryKey = (projectName: string) => `command_history_${projectName}`;
|
const getCommandHistoryKey = (projectName: string) => `command_history_${projectName}`;
|
||||||
|
|||||||
Reference in New Issue
Block a user