basant307/AI_Governance_Project
045
1/**2 * @license3 * Copyright 2025 Qwen4 * SPDX-License-Identifier: Apache-2.05 */6 7import type { CommandSource, SlashCommand } from '../ui/commands/types.js';8import { getEffectiveSupportedModes } from './commandUtils.js';9 10export type CommandSourceGroup = {11 key: 'built-in' | 'bundled-skill' | 'custom' | 'plugin' | 'mcp' | 'other';12 title: string;13 order: number;14};15 16export function getCommandSourceBadge(17 command: Pick<SlashCommand, 'source' | 'sourceDetail'>,18): string | null {19 switch (command.source) {20 case 'bundled-skill':21 return '[Skill]';22 case 'skill-dir-command':23 if (command.sourceDetail === 'user') {24 return '[User]';25 }26 if (command.sourceDetail === 'project') {27 return '[Project]';28 }29 return '[Custom]';30 case 'plugin-command':31 return command.sourceDetail === 'extension' ? '[Extension]' : '[Plugin]';32 case 'mcp-prompt':33 return '[MCP]';34 case 'builtin-command':35 default:36 return null;37 }38}39 40export function getCommandSourceGroup(41 command: Pick<SlashCommand, 'source'>,42): CommandSourceGroup {43 switch (command.source) {44 case 'builtin-command':45 return { key: 'built-in', title: 'Built-in Commands', order: 0 };46 case 'bundled-skill':47 return { key: 'bundled-skill', title: 'Bundled Skills', order: 1 };48 case 'skill-dir-command':49 return { key: 'custom', title: 'Custom Commands', order: 2 };50 case 'plugin-command':51 return { key: 'plugin', title: 'Plugin Commands', order: 3 };52 case 'mcp-prompt':53 return { key: 'mcp', title: 'MCP Commands', order: 4 };54 default:55 return { key: 'other', title: 'Other Commands', order: 5 };56 }57}58 59export function formatSupportedModes(command: SlashCommand): string {60 const modes = getEffectiveSupportedModes(command);61 const hasInteractive = modes.includes('interactive');62 const hasNonInteractive = modes.includes('non_interactive');63 const hasAcp = modes.includes('acp');64 65 if (hasInteractive && hasNonInteractive && hasAcp) {66 return '[all]';67 }68 69 if (!hasInteractive && hasNonInteractive && hasAcp) {70 return '[headless]';71 }72 73 if (hasInteractive && !hasNonInteractive && !hasAcp) {74 return '[interactive]';75 }76 77 return modes78 .map((mode) => {79 switch (mode) {80 case 'interactive':81 return '[i]';82 case 'non_interactive':83 return '[ni]';84 case 'acp':85 return '[acp]';86 default:87 return `[${mode}]`;88 }89 })90 .join(' ');91}92 93export function getCommandDisplayName(94 command: Pick<SlashCommand, 'name' | 'altNames'>,95 options: {96 prefix?: string;97 matchedAlias?: string;98 includeAliases?: boolean;99 } = {},100): string {101 const prefix = options.prefix ?? '';102 const baseLabel = `${prefix}${command.name}`;103 104 if (options.matchedAlias) {105 return `${baseLabel} (alias: ${options.matchedAlias})`;106 }107 108 if (options.includeAliases === false) {109 return baseLabel;110 }111 112 const altNames = command.altNames?.filter(Boolean);113 if (!altNames || altNames.length === 0) {114 return baseLabel;115 }116 117 return `${baseLabel} (${altNames.join(', ')})`;118}119 120export function getCommandSubcommandNames(command: SlashCommand): string[] {121 return (122 command.subCommands123 ?.filter((subCommand) => !subCommand.hidden)124 .map((subCommand) => subCommand.name) ?? []125 );126}127 128export function formatCommandSourceLabel(129 command: Pick<SlashCommand, 'source' | 'sourceLabel'>,130): string {131 if (command.sourceLabel) {132 return command.sourceLabel;133 }134 135 const fallbackLabels: Record<CommandSource, string> = {136 'builtin-command': 'Built-in',137 'bundled-skill': 'Skill',138 'skill-dir-command': 'Custom',139 'plugin-command': 'Plugin',140 'mcp-prompt': 'MCP',141 'workflow-command': 'Workflow',142 };143 144 return command.source ? fallbackLabels[command.source] : 'Unknown';145}146 