128 lines
3.3 KiB
TypeScript
128 lines
3.3 KiB
TypeScript
import type { SlashCommandMeta } from '@shared/types/team';
|
|
|
|
export interface KnownSlashCommandDefinition {
|
|
name: string;
|
|
command: `/${string}`;
|
|
description: string;
|
|
}
|
|
|
|
export interface ParsedStandaloneSlashCommand {
|
|
name: string;
|
|
command: `/${string}`;
|
|
args?: string;
|
|
raw: string;
|
|
startIndex: number;
|
|
endIndex: number;
|
|
}
|
|
|
|
const SLASH_COMMAND_NAME_PATTERN = /^[a-z][a-z0-9:-]{0,63}$/i;
|
|
const STANDALONE_SLASH_COMMAND_PATTERN = /^\/([a-z][a-z0-9:-]{0,63})(?:\s+([\s\S]*\S))?$/i;
|
|
|
|
export const KNOWN_SLASH_COMMANDS: readonly KnownSlashCommandDefinition[] = [
|
|
{
|
|
name: 'compact',
|
|
command: '/compact',
|
|
description: 'Compact conversation with optional focus instructions.',
|
|
},
|
|
{
|
|
name: 'clear',
|
|
command: '/clear',
|
|
description: 'Clear conversation history and free up context.',
|
|
},
|
|
{
|
|
name: 'reset',
|
|
command: '/reset',
|
|
description: 'Alias of /clear. Clear conversation history and free up context.',
|
|
},
|
|
{
|
|
name: 'new',
|
|
command: '/new',
|
|
description: 'Alias of /clear. Start a fresh conversation.',
|
|
},
|
|
{
|
|
name: 'plan',
|
|
command: '/plan',
|
|
description: 'Enter plan mode with an optional task description.',
|
|
},
|
|
{
|
|
name: 'model',
|
|
command: '/model',
|
|
description: 'Select or change the Claude model.',
|
|
},
|
|
{
|
|
name: 'effort',
|
|
command: '/effort',
|
|
description: 'Set reasoning effort for the current session.',
|
|
},
|
|
{
|
|
name: 'fast',
|
|
command: '/fast',
|
|
description: 'Toggle fast mode on or off.',
|
|
},
|
|
{
|
|
name: 'cost',
|
|
command: '/cost',
|
|
description: 'Show token usage statistics.',
|
|
},
|
|
{
|
|
name: 'usage',
|
|
command: '/usage',
|
|
description: 'Show plan usage limits and rate-limit status.',
|
|
},
|
|
] as const;
|
|
|
|
const KNOWN_SLASH_COMMANDS_BY_NAME = new Map(
|
|
KNOWN_SLASH_COMMANDS.map((command) => [command.name.toLowerCase(), command] as const)
|
|
);
|
|
|
|
export function getKnownSlashCommand(name: string): KnownSlashCommandDefinition | null {
|
|
return KNOWN_SLASH_COMMANDS_BY_NAME.get(name.trim().toLowerCase()) ?? null;
|
|
}
|
|
|
|
export function isSupportedSlashCommandName(name: string): boolean {
|
|
return SLASH_COMMAND_NAME_PATTERN.test(name.trim());
|
|
}
|
|
|
|
export function buildSlashCommandMeta(
|
|
name: string,
|
|
args?: string,
|
|
command?: `/${string}`
|
|
): SlashCommandMeta {
|
|
const normalizedName = name.trim().toLowerCase();
|
|
const normalizedCommand = command ?? `/${normalizedName}`;
|
|
const known = getKnownSlashCommand(normalizedName);
|
|
return {
|
|
name: normalizedName,
|
|
command: normalizedCommand,
|
|
...(args ? { args } : {}),
|
|
...(known ? { knownDescription: known.description } : {}),
|
|
};
|
|
}
|
|
|
|
export function buildStandaloneSlashCommandMeta(text: string): SlashCommandMeta | null {
|
|
const parsed = parseStandaloneSlashCommand(text);
|
|
if (!parsed) return null;
|
|
return buildSlashCommandMeta(parsed.name, parsed.args, parsed.command);
|
|
}
|
|
|
|
export function parseStandaloneSlashCommand(text: string): ParsedStandaloneSlashCommand | null {
|
|
const trimmed = text.trim();
|
|
if (!trimmed) return null;
|
|
|
|
const match = STANDALONE_SLASH_COMMAND_PATTERN.exec(trimmed);
|
|
if (!match) return null;
|
|
|
|
const name = match[1].toLowerCase();
|
|
const args = match[2]?.trim();
|
|
const startIndex = text.indexOf(trimmed);
|
|
const endIndex = startIndex + trimmed.length;
|
|
|
|
return {
|
|
name,
|
|
command: `/${name}`,
|
|
args: args || undefined,
|
|
raw: trimmed,
|
|
startIndex,
|
|
endIndex,
|
|
};
|
|
}
|