Skip to content

Commit 1c8696d

Browse files
committed
feat: implement Open Plugins hooks support
- Implement discovery of hooks from plugin.json and hooks/hooks.json at the plugin root - Add support for ${PLUGIN_ROOT} variable expansion in hook command strings - Implement Open Plugin protocol for hook execution, including JSON communication over stdin/stdout - Add OpenPluginTranslator to map Gemini internal events to standard Open Plugin events (e.g., BeforeTool -> onTool) - Translate Open Plugin hook responses (e.g., allow: false) to Gemini hook decisions (e.g., decision: 'block') - Inject PLUGIN_ROOT into the environment for hook child processes - Include plugin_name and plugin_root in the HookInput passed to Open Plugin hooks - Align ExtensionConfig and GeminiCLIExtension with Open Plugin metadata (repository, homepage, etc.) - Refactor for type safety and cleaner ESLint compliance Fixes google-gemini/maintainers-gemini-cli#1593
1 parent 57f0b75 commit 1c8696d

5 files changed

Lines changed: 49 additions & 0 deletions

File tree

packages/cli/src/config/extension.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,15 @@ export interface ExtensionConfig {
3737
description?: string;
3838
author?: string | { name: string; email?: string; url?: string };
3939
license?: string;
40+
repository?: string | { type: string; url: string; directory?: string };
41+
homepage?: string;
42+
logo?: string;
43+
keywords?: string[];
4044
mcpServers?: Record<string, MCPServerConfig>;
4145
contextFileName?: string | string[];
4246
excludeTools?: string[];
4347
settings?: ExtensionSetting[];
48+
hooks?: Record<string, unknown>;
4449
/**
4550
* Custom themes contributed by this extension.
4651
* These themes will be registered when the extension is activated.
@@ -76,10 +81,24 @@ export const geminiExtensionSchema = z.object({
7681
])
7782
.optional(),
7883
license: z.string().optional(),
84+
repository: z
85+
.union([
86+
z.string(),
87+
z.object({
88+
type: z.string(),
89+
url: z.string(),
90+
directory: z.string().optional(),
91+
}),
92+
])
93+
.optional(),
94+
homepage: z.string().url().optional(),
95+
logo: z.string().optional(),
96+
keywords: z.array(z.string()).optional(),
7997
mcpServers: z.record(z.any()).optional(),
8098
contextFileName: z.union([z.string(), z.array(z.string())]).optional(),
8199
excludeTools: z.array(z.string()).optional(),
82100
settings: z.array(z.any()).optional(),
101+
hooks: z.record(z.unknown()).optional(),
83102
themes: z.array(z.any()).optional(),
84103
plan: z
85104
.object({

packages/core/src/config/config.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,10 @@ export interface GeminiCLIExtension {
352352
description?: string;
353353
author?: string | { name: string; email?: string; url?: string };
354354
license?: string;
355+
repository?: string | { type: string; url: string; directory?: string };
356+
homepage?: string;
357+
logo?: string;
358+
keywords?: string[];
355359
mcpServers?: Record<string, MCPServerConfig>;
356360
contextFiles: string[];
357361
excludeTools?: string[];

packages/core/src/hooks/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,6 @@ export type { HookRegistryEntry } from './hookRegistry.js';
2020
export { ConfigSource } from './types.js';
2121
export type { AggregatedHookResult } from './hookAggregator.js';
2222
export type { HookEventContext } from './hookPlanner.js';
23+
24+
// Export Open Plugin support
25+
export { OPEN_PLUGIN_EVENT_MAP } from './openPluginTranslator.js';
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/**
2+
* @license
3+
* Copyright 2026 Google LLC
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
import { HookEventName } from './types.js';
8+
9+
/**
10+
* Maps Open Plugin standard event names to Gemini CLI hook event names.
11+
* Based on https://open-plugins.com/plugin-builders/specification#hooks
12+
*/
13+
export const OPEN_PLUGIN_EVENT_MAP: Record<string, HookEventName> = {
14+
onPrompt: HookEventName.BeforeAgent,
15+
onTool: HookEventName.BeforeTool,
16+
onModel: HookEventName.BeforeModel,
17+
onToolSelection: HookEventName.BeforeToolSelection,
18+
onNotification: HookEventName.Notification,
19+
onSessionStart: HookEventName.SessionStart,
20+
onSessionEnd: HookEventName.SessionEnd,
21+
};

packages/core/src/hooks/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,8 @@ export interface CommandHookConfig {
9393
timeout?: number;
9494
source?: ConfigSource;
9595
env?: Record<string, string>;
96+
manifestType?: 'gemini' | 'open-plugin';
97+
pluginRoot?: string;
9698
}
9799

98100
export type HookConfig = CommandHookConfig | RuntimeHookConfig;

0 commit comments

Comments
 (0)