feat(cli): dispatch du callback plugin slash-command vers le runtime registry — #166 (QA verte)
Boucle le gap end-to-end de #165 : à l'exécution d'une commande slash plugin depuis le composer custom, l'UI traite l'effet pluginCallback et le dispatche au PluginRuntimeRegistry pour que la callback du plugin s'exécute réellement. - runtime/registry: runCommandStrict (échec explicite si handler absent) + propagation de la valeur de retour ; élargit les types de retour (unknown). - runtime/loader: chargement de contributes.slashCommands au registre + propagation du retour du handler. - features/plugins/usePluginMenus: runCommand retourne unknown. - features/agents/CustomAgentChatView: handler de l'effet pluginCallback -> pluginRuntime.registry.runCommandStrict, avec retour utilisateur en cas d'échec (commande indisponible / callback absente). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -59,7 +59,7 @@ export interface Disposable {
|
||||
dispose(): void;
|
||||
}
|
||||
|
||||
export type PluginCommandHandler = (...args: unknown[]) => void | Promise<void>;
|
||||
export type PluginCommandHandler = (...args: unknown[]) => unknown | Promise<unknown>;
|
||||
|
||||
/** Commands a plugin registers in `activate(ctx)`, dispatched by menu items. */
|
||||
export class PluginCommandRegistry {
|
||||
@ -74,7 +74,7 @@ export class PluginCommandRegistry {
|
||||
if (!this.declaredCommandIds.has(commandId)) {
|
||||
throw new Error(
|
||||
`plugin "${this.pluginId}" tried to register command "${commandId}" ` +
|
||||
"which is not declared by any menu item in its manifest",
|
||||
"which is not declared by any command contribution in its manifest",
|
||||
);
|
||||
}
|
||||
this.handlers.set(commandId, handler);
|
||||
@ -86,11 +86,11 @@ export class PluginCommandRegistry {
|
||||
}
|
||||
|
||||
/** Runs a registered command; a no-op (never throws) if none is registered. */
|
||||
async run(commandId: string, ...args: unknown[]): Promise<void> {
|
||||
async run(commandId: string, ...args: unknown[]): Promise<unknown> {
|
||||
const handler = this.handlers.get(commandId);
|
||||
if (!handler) return;
|
||||
try {
|
||||
await handler(...args);
|
||||
return await handler(...args);
|
||||
} catch (e) {
|
||||
console.error(
|
||||
`[plugin:${this.pluginId}] command "${commandId}" failed`,
|
||||
@ -99,6 +99,17 @@ export class PluginCommandRegistry {
|
||||
}
|
||||
}
|
||||
|
||||
/** Runs a registered command and surfaces failures to the caller. */
|
||||
async runStrict(commandId: string, ...args: unknown[]): Promise<unknown> {
|
||||
const handler = this.handlers.get(commandId);
|
||||
if (!handler) {
|
||||
throw new Error(
|
||||
`plugin "${this.pluginId}" has no registered command handler "${commandId}"`,
|
||||
);
|
||||
}
|
||||
return await handler(...args);
|
||||
}
|
||||
|
||||
has(commandId: string): boolean {
|
||||
return this.handlers.has(commandId);
|
||||
}
|
||||
@ -256,7 +267,19 @@ export class PluginRuntimeRegistry {
|
||||
);
|
||||
}
|
||||
|
||||
async runCommand(pluginId: string, commandId: string, ...args: unknown[]): Promise<void> {
|
||||
await this.loaded.get(pluginId)?.commands.run(commandId, ...args);
|
||||
async runCommand(pluginId: string, commandId: string, ...args: unknown[]): Promise<unknown> {
|
||||
return await this.loaded.get(pluginId)?.commands.run(commandId, ...args);
|
||||
}
|
||||
|
||||
async runCommandStrict(
|
||||
pluginId: string,
|
||||
commandId: string,
|
||||
...args: unknown[]
|
||||
): Promise<unknown> {
|
||||
const plugin = this.loaded.get(pluginId);
|
||||
if (!plugin) {
|
||||
throw new Error(`plugin "${pluginId}" is not loaded`);
|
||||
}
|
||||
return await plugin.commands.runStrict(commandId, ...args);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user