/** * @license * Copyright 2025 Vybestack LLC * SPDX-License-Identifier: Apache-2.0 */ import { join } from 'node:path'; import { isDevelopment } from '../utils/installationInfo.js'; import type { SlashCommand, CommandContext, MessageActionReturn, } from '../ui/commands/types.js'; import { CommandKind } from '../ui/commands/types.js'; import type { CliUiRuntime, ExtensionEnablementSource, } from '../ui/cliUiRuntime.js'; import type { ICommandLoader } from './types.js'; import { aboutCommand } from '../ui/commands/aboutCommand.js'; import { authCommand } from '../ui/commands/authCommand.js'; import { bugCommand } from '../ui/commands/bugCommand.js'; import { chatCommand } from '../ui/commands/chatCommand.js'; import { clearCommand } from '../ui/commands/clearCommand.js'; import { compressCommand } from '../ui/commands/compressCommand.js'; import { copyCommand } from '../ui/commands/copyCommand.js'; import { docsCommand } from '../ui/commands/docsCommand.js'; import { directoryCommand } from '../ui/commands/directoryCommand.js'; import { editorCommand } from '../ui/commands/editorCommand.js'; import { extensionsCommand } from '../ui/commands/extensionsCommand.js'; import { helpCommand } from '../ui/commands/helpCommand.js'; import { ideCommand } from '../ui/commands/ideCommand.js'; import { imageCommand } from '../ui/commands/imageCommand.js'; import { initCommand } from '../ui/commands/initCommand.js'; import { mcpCommand } from '../ui/commands/mcpCommand.js'; import { memoryCommand } from '../ui/commands/memoryCommand.js'; import { privacyCommand } from '../ui/commands/privacyCommand.js'; import { loggingCommand } from '../ui/commands/loggingCommand.js'; import { uiprofileCommand } from '../ui/commands/uiprofileCommand.js'; import { mouseCommand } from '../ui/commands/mouseCommand.js'; import { quitCommand } from '../ui/commands/quitCommand.js'; import { quotaCommand } from '../ui/commands/quotaCommand.js'; import { restoreCommand } from '../ui/commands/restoreCommand.js'; import { statsCommand } from '../ui/commands/statsCommand.js'; import { themeCommand } from '../ui/commands/themeCommand.js'; import { toolsCommand } from '../ui/commands/toolsCommand.js'; import { skillsCommand } from '../ui/commands/skillsCommand.js'; import { settingsCommand } from '../ui/commands/settingsCommand.js'; import { vimCommand } from '../ui/commands/vimCommand.js'; import { providerCommand } from '../ui/commands/providerCommand.js'; import { modelCommand } from '../ui/commands/modelCommand.js'; import { keyCommand } from '../ui/commands/keyCommand.js'; import { keyfileCommand } from '../ui/commands/keyfileCommand.js'; import { toolkeyCommand } from '../ui/commands/toolkeyCommand.js'; import { toolkeyfileCommand } from '../ui/commands/toolkeyfileCommand.js'; import { baseurlCommand } from '../ui/commands/baseurlCommand.js'; import { toolformatCommand } from '../ui/commands/toolformatCommand.js'; import { setupGithubCommand } from '../ui/commands/setupGithubCommand.js'; import { setCommand } from '../ui/commands/setCommand.js'; import { profileCommand } from '../ui/commands/profileCommand.js'; import { diagnosticsCommand } from '../ui/commands/diagnosticsCommand.js'; import { terminalSetupCommand } from '../ui/commands/terminalSetupCommand.js'; import { debugCommand } from '../ui/commands/debugCommands.js'; import { logoutCommand } from '../ui/commands/logoutCommand.js'; import { subagentCommand } from '../ui/commands/subagentCommand.js'; import { permissionsCommand } from '../ui/commands/permissionsCommand.js'; import { policiesCommand } from '../ui/commands/policiesCommand.js'; import { dumpcontextCommand } from '../ui/commands/dumpcontextCommand.js'; import { todoCommand } from '../ui/commands/todoCommand.js'; import { setupCommand } from '../ui/commands/setupCommand.js'; import { tasksCommands } from '../ui/commands/tasksCommand.js'; import { hooksCommand } from '../ui/commands/hooksCommand.js'; import { createPerfCommand } from '../ui/commands/perfCommand.js'; /** * @plan PLAN-20260214-SESSIONBROWSER.P21 */ import { continueCommand } from '../ui/commands/continueCommand.js'; /** * Loads the core, hard-coded slash commands that are an integral part * of the LLxprt Code application. */ export class BuiltinCommandLoader implements ICommandLoader { private extensionEnablementManager?: ExtensionEnablementSource; constructor(private config: CliUiRuntime | null) { // Access extensionEnablementManager if available on config if (config && 'extensionEnablementManager' in config) { this.extensionEnablementManager = config.extensionEnablementManager; } } /** * Discovers and returns all built-in slash commands. * Filters out commands from disabled extensions. * @param signal An AbortSignal to allow cancellation. * @returns A promise that resolves to an array of SlashCommand objects. * * @plan:PLAN-20250117-SUBAGENTCONFIG.P15 * @requirement:REQ-010 */ async loadCommands(_signal: AbortSignal): Promise { return this.loadCommandsSync(); } loadCommandsSync(): SlashCommand[] { const allCommands = this.registerBuiltinCommands(); // Filter out commands from disabled extensions if (this.extensionEnablementManager) { return allCommands.filter((cmd) => { // Built-in commands (no extensionName) are always included if (!cmd.extensionName) { return true; } // Extension commands are filtered by enabled state // Note: isEnabled requires a path, but for session-based enablement // we use an empty string as the path since session state doesn't depend on path return this.extensionEnablementManager!.isEnabled( cmd.extensionName, '', ); }); } return allCommands; } /** * Gathers all raw built-in command definitions, injects dependencies where * needed (e.g., config) and filters out any that are not available. * * @plan:PLAN-20250117-SUBAGENTCONFIG.P15 * @requirement:REQ-010 */ private registerBuiltinCommands(): SlashCommand[] { const allDefinitions: Array = [ aboutCommand, authCommand, bugCommand, chatCommand, clearCommand, compressCommand, copyCommand, docsCommand, directoryCommand, editorCommand, extensionsCommand, helpCommand, ideCommand(this.config), imageCommand, initCommand, mcpCommand, memoryCommand, privacyCommand, loggingCommand, mouseCommand, ...(isDevelopment ? [uiprofileCommand] : []), quitCommand, restoreCommand(this.config), quotaCommand, statsCommand, themeCommand, toolsCommand, ...this.resolveSkillsCommand(), settingsCommand, vimCommand, providerCommand, modelCommand, keyCommand, keyfileCommand, toolkeyCommand, // @plan PLAN-20260206-TOOLKEY.P09 @requirement REQ-002 toolkeyfileCommand, // @plan PLAN-20260206-TOOLKEY.P09 @requirement REQ-003 baseurlCommand, toolformatCommand, setupGithubCommand, setCommand, profileCommand, diagnosticsCommand, terminalSetupCommand, debugCommand, logoutCommand, subagentCommand, permissionsCommand, policiesCommand, dumpcontextCommand, todoCommand, setupCommand, ...tasksCommands, ...(this.config?.getEnableHooksUI() === true ? [hooksCommand] : []), /** * @plan PLAN-20260214-SESSIONBROWSER.P21 */ continueCommand, createPerfCommand({ snapshotCapability: this.config?.getPerfSnapshotCapability?.() ?? null, tokenUsageDir: this.config ? join(this.config.getProjectTempDir(), 'token-usage') : undefined, }), ]; return allDefinitions.filter((cmd): cmd is SlashCommand => cmd !== null); } /** * Determine skills command based on config and admin settings. * Returns [] when skills support is disabled, a stub error command when * disabled by admin, or [skillsCommand] when enabled. */ private resolveSkillsCommand(): SlashCommand[] { if (this.config?.isSkillsSupportEnabled() !== true) { return []; } if (this.config.getSkillManager().isAdminEnabled() === false) { return [ { name: 'skills', description: 'Manage skills', kind: CommandKind.BUILT_IN, autoExecute: false, subCommands: [], action: async ( _context: CommandContext, ): Promise => ({ type: 'message', messageType: 'error', content: 'Skills are disabled by your admin.', }), }, ]; } return [skillsCommand]; } }