feat: implement custom slash commands system
Adds a comprehensive slash command system that allows users to create and manage custom commands: - Backend implementation in Rust for discovering, loading, and managing slash commands - Support for both user-level (~/.claude/commands/) and project-level (.claude/commands/) commands - YAML frontmatter support for command metadata (description, allowed-tools) - Command namespacing with directory structure (e.g., /namespace:command) - Detection of special features: bash commands (\!), file references (@), and arguments ($ARGUMENTS) Frontend enhancements: - SlashCommandPicker component with autocomplete UI and keyboard navigation - SlashCommandsManager component for CRUD operations on commands - Integration with FloatingPromptInput to trigger picker on "/" input - Visual indicators for command features (bash, files, arguments) - Grouped display by namespace with search functionality API additions: - slash_commands_list: Discover all available commands - slash_command_get: Retrieve specific command by ID - slash_command_save: Create or update commands - slash_command_delete: Remove commands This implementation provides a foundation for users to create reusable command templates and workflows. Commands are stored as markdown files with optional YAML frontmatter for metadata. Addresses #127 and #134
This commit is contained in:
110
src/lib/api.ts
110
src/lib/api.ts
@@ -383,6 +383,36 @@ export interface MCPServerConfig {
|
||||
env: Record<string, string>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a custom slash command
|
||||
*/
|
||||
export interface SlashCommand {
|
||||
/** Unique identifier for the command */
|
||||
id: string;
|
||||
/** Command name (without prefix) */
|
||||
name: string;
|
||||
/** Full command with prefix (e.g., "/project:optimize") */
|
||||
full_command: string;
|
||||
/** Command scope: "project" or "user" */
|
||||
scope: string;
|
||||
/** Optional namespace (e.g., "frontend" in "/project:frontend:component") */
|
||||
namespace?: string;
|
||||
/** Path to the markdown file */
|
||||
file_path: string;
|
||||
/** Command content (markdown body) */
|
||||
content: string;
|
||||
/** Optional description from frontmatter */
|
||||
description?: string;
|
||||
/** Allowed tools from frontmatter */
|
||||
allowed_tools: string[];
|
||||
/** Whether the command has bash commands (!) */
|
||||
has_bash_commands: boolean;
|
||||
/** Whether the command has file references (@) */
|
||||
has_file_references: boolean;
|
||||
/** Whether the command uses $ARGUMENTS placeholder */
|
||||
accepts_arguments: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Result of adding a server
|
||||
*/
|
||||
@@ -1724,5 +1754,85 @@ export const api = {
|
||||
console.error("Failed to get merged hooks config:", error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
// Slash Commands API methods
|
||||
|
||||
/**
|
||||
* Lists all available slash commands
|
||||
* @param projectPath - Optional project path to include project-specific commands
|
||||
* @returns Promise resolving to array of slash commands
|
||||
*/
|
||||
async slashCommandsList(projectPath?: string): Promise<SlashCommand[]> {
|
||||
try {
|
||||
return await invoke<SlashCommand[]>("slash_commands_list", { projectPath });
|
||||
} catch (error) {
|
||||
console.error("Failed to list slash commands:", error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets a single slash command by ID
|
||||
* @param commandId - Unique identifier of the command
|
||||
* @returns Promise resolving to the slash command
|
||||
*/
|
||||
async slashCommandGet(commandId: string): Promise<SlashCommand> {
|
||||
try {
|
||||
return await invoke<SlashCommand>("slash_command_get", { commandId });
|
||||
} catch (error) {
|
||||
console.error("Failed to get slash command:", error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates or updates a slash command
|
||||
* @param scope - Command scope: "project" or "user"
|
||||
* @param name - Command name (without prefix)
|
||||
* @param namespace - Optional namespace for organization
|
||||
* @param content - Markdown content of the command
|
||||
* @param description - Optional description
|
||||
* @param allowedTools - List of allowed tools for this command
|
||||
* @param projectPath - Required for project scope commands
|
||||
* @returns Promise resolving to the saved command
|
||||
*/
|
||||
async slashCommandSave(
|
||||
scope: string,
|
||||
name: string,
|
||||
namespace: string | undefined,
|
||||
content: string,
|
||||
description: string | undefined,
|
||||
allowedTools: string[],
|
||||
projectPath?: string
|
||||
): Promise<SlashCommand> {
|
||||
try {
|
||||
return await invoke<SlashCommand>("slash_command_save", {
|
||||
scope,
|
||||
name,
|
||||
namespace,
|
||||
content,
|
||||
description,
|
||||
allowedTools,
|
||||
projectPath
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Failed to save slash command:", error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Deletes a slash command
|
||||
* @param commandId - Unique identifier of the command to delete
|
||||
* @returns Promise resolving to deletion message
|
||||
*/
|
||||
async slashCommandDelete(commandId: string): Promise<string> {
|
||||
try {
|
||||
return await invoke<string>("slash_command_delete", { commandId });
|
||||
} catch (error) {
|
||||
console.error("Failed to delete slash command:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
Reference in New Issue
Block a user