File size: 3,850 Bytes
52efc7b | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 | /**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import { FileCommandLoader } from '../../services/FileCommandLoader.js';
import {
type CommandContext,
type SlashCommand,
type SlashCommandActionReturn,
CommandKind,
} from './types.js';
import {
MessageType,
type HistoryItemError,
type HistoryItemInfo,
} from '../types.js';
/**
* Action for the default `/commands` invocation.
* Displays a message prompting the user to use a subcommand.
*/
async function defaultAction(
_context: CommandContext,
_args: string,
): Promise<void | SlashCommandActionReturn> {
return {
type: 'message',
messageType: 'info',
content:
'Use "/commands list" to view available .toml files, or "/commands reload" to reload custom command definitions.',
};
}
/**
* Action for `/commands list`.
* Lists available .toml command files from user, project, and extension directories.
*/
async function listSubcommandAction(
context: CommandContext,
): Promise<void | SlashCommandActionReturn> {
try {
const config = context.services.agentContext?.config ?? null;
const loader = new FileCommandLoader(config);
const groups = await loader.listAvailableFiles();
const results: string[] = [];
for (const group of groups) {
results.push(`### ${group.displayName} Commands (${group.path})`);
if (group.error) {
results.push(`- (Error reading directory: ${group.error})`);
} else {
group.files.forEach((file) => results.push(`- ${file}`));
}
}
results.push(
'\n_Note: MCP prompts are dynamically loaded from configured MCP servers._',
);
if (results.length === 1) {
// Only the note is present
return {
type: 'message',
messageType: 'info',
content:
'No custom command files (.toml) found.\n\n_Note: MCP prompts are dynamically loaded from configured MCP servers._',
};
}
context.ui.addItem(
{
type: MessageType.INFO,
text: results.join('\n'),
} as HistoryItemInfo,
Date.now(),
);
} catch (error) {
context.ui.addItem(
{
type: MessageType.ERROR,
text: `Failed to list commands: ${error instanceof Error ? error.message : String(error)}`,
} as HistoryItemError,
Date.now(),
);
}
}
/**
* Action for `/commands reload`.
* Triggers a full re-discovery and reload of all slash commands, including
* user/project-level .toml files, MCP prompts, and extension commands.
*/
async function reloadAction(
context: CommandContext,
): Promise<void | SlashCommandActionReturn> {
try {
context.ui.reloadCommands();
context.ui.addItem(
{
type: MessageType.INFO,
text: 'Custom commands reloaded successfully.',
} as HistoryItemInfo,
Date.now(),
);
} catch (error) {
context.ui.addItem(
{
type: MessageType.ERROR,
text: `Failed to reload commands: ${error instanceof Error ? error.message : String(error)}`,
} as HistoryItemError,
Date.now(),
);
}
}
export const commandsCommand: SlashCommand = {
name: 'commands',
description: 'Manage custom slash commands. Usage: /commands [list|reload]',
kind: CommandKind.BUILT_IN,
autoExecute: false,
subCommands: [
{
name: 'list',
description:
'List available custom command .toml files. Usage: /commands list',
kind: CommandKind.BUILT_IN,
autoExecute: true,
action: listSubcommandAction,
},
{
name: 'reload',
altNames: ['refresh'],
description:
'Reload custom command definitions from .toml files. Usage: /commands reload',
kind: CommandKind.BUILT_IN,
autoExecute: true,
action: reloadAction,
},
],
action: defaultAction,
};
|