diff --git a/docs/extensions/best-practices.md b/docs/extensions/best-practices.md new file mode 100644 index 00000000000..13ec7bc281b --- /dev/null +++ b/docs/extensions/best-practices.md @@ -0,0 +1,114 @@ +# Extensions on Gemini CLI: Best practices + +This guide covers best practices for developing, securing, and maintaining +Gemini CLI extensions. + +## Development + +### Structure your extension + +While simple extensions can just be a few files, we recommend a robust structure +for complex extensions: + +``` +my-extension/ +├── package.json +├── tsconfig.json +├── gemini-extension.json +├── src/ +│ ├── index.ts +│ └── tools/ +└── dist/ +``` + +- **Use TypeScript**: We strongly recommend using TypeScript for type safety and + better tooling. +- **Separate source and build**: Keep your source code in `src` and build to + `dist`. +- **Bundle dependencies**: If your extension has many dependencies, consider + bundling them (e.g., with `esbuild` or `webpack`) to reduce install time and + potential conflicts. + +### Iterate with `link` + +Use `gemini extensions link` to develop locally without constantly reinstalling: + +```bash +cd my-extension +gemini extensions link . +``` + +Changes to your code (after rebuilding) will be immediately available in the CLI +on restart. + +### Use `GEMINI.md` effectively + +Your `GEMINI.md` file provides context to the model. Keep it focused: + +- **Do:** Explain high-level goals and how to use the provided tools. +- **Don't:** Dump your entire documentation. +- **Do:** Use clear, concise language. + +## Security + +### Minimal permissions + +When defining tools in your MCP server, only request the permissions necessary. + +### Validate inputs + +Your MCP server is running on the user's machine. Always validate inputs to your +tools to prevent arbitrary code execution or filesystem access outside the +intended scope. + +```typescript +// Good: Validating paths +if (!path.resolve(inputPath).startsWith(path.resolve(allowedDir) + path.sep)) { + throw new Error('Access denied'); +} +``` + +### Sensitive settings + +If your extension requires API keys, use the `sensitive: true` option in +`gemini-extension.json`. This ensures keys are stored securely in the system +keychain and obfuscated in the UI. + +```json +"settings": [ + { + "name": "API Key", + "envVar": "MY_API_KEY", + "sensitive": true + } +] +``` + +## Releasing + +### Semantic Versioning + +Follow [Semantic Versioning](https://semver.org/). + +- **Major**: Breaking changes (renaming tools, changing arguments). +- **Minor**: New features (new tools, commands). +- **Patch**: Bug fixes. + +### Release Channels + +Use git branches to manage release channels (e.g., `main` for stable, `dev` for +bleeding edge). This allows users to choose their stability level: + +```bash +# Stable +gemini extensions install github.com/user/repo + +# Dev +gemini extensions install github.com/user/repo --ref dev +``` + +### Clean Artifacts + +If you are using GitHub Releases, ensure your release artifacts only contain the +necessary files (`dist/`, `gemini-extension.json`, `package.json`). Exclude +`node_modules` (users will install them) and `src/` to keep downloads small. diff --git a/docs/extensions/index.md b/docs/extensions/index.md index 2d00a4f7d42..30169ca1b4f 100644 --- a/docs/extensions/index.md +++ b/docs/extensions/index.md @@ -1,7 +1,5 @@ # Gemini CLI extensions -_This documentation is up-to-date with the v0.4.0 release._ - Gemini CLI extensions package prompts, MCP servers, and custom commands into a familiar and user-friendly format. With extensions, you can expand the capabilities of Gemini CLI and share those capabilities with others. They are @@ -10,334 +8,11 @@ designed to be easily installable and shareable. To see examples of extensions, you can browse a gallery of [Gemini CLI extensions](https://geminicli.com/extensions/browse/). -See [getting started docs](getting-started-extensions.md) for a guide on -creating your first extension. - -See [releasing docs](extension-releasing.md) for an advanced guide on setting up -GitHub releases. - -## Extension management - -We offer a suite of extension management tools using `gemini extensions` -commands. - -Note that these commands are not supported from within the CLI, although you can -list installed extensions using the `/extensions list` subcommand. - -Note that all of these commands will only be reflected in active CLI sessions on -restart. - -### Installing an extension - -You can install an extension using `gemini extensions install` with either a -GitHub URL or a local path. - -Note that we create a copy of the installed extension, so you will need to run -`gemini extensions update` to pull in changes from both locally-defined -extensions and those on GitHub. - -NOTE: If you are installing an extension from GitHub, you'll need to have `git` -installed on your machine. See -[git installation instructions](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) -for help. - -``` -gemini extensions install [--ref ] [--auto-update] [--pre-release] [--consent] -``` - -- ``: The github URL or local path of the extension to install. -- `--ref`: The git ref to install from. -- `--auto-update`: Enable auto-update for this extension. -- `--pre-release`: Enable pre-release versions for this extension. -- `--consent`: Acknowledge the security risks of installing an extension and - skip the confirmation prompt. - -### Uninstalling an extension - -To uninstall one or more extensions, run -`gemini extensions uninstall `: - -``` -gemini extensions uninstall gemini-cli-security gemini-cli-another-extension -``` - -### Disabling an extension - -Extensions are, by default, enabled across all workspaces. You can disable an -extension entirely or for specific workspace. - -``` -gemini extensions disable [--scope ] -``` - -- ``: The name of the extension to disable. -- `--scope`: The scope to disable the extension in (`user` or `workspace`). - -### Enabling an extension - -You can enable extensions using `gemini extensions enable `. You can also -enable an extension for a specific workspace using -`gemini extensions enable --scope=workspace` from within that workspace. - -``` -gemini extensions enable [--scope ] -``` - -- ``: The name of the extension to enable. -- `--scope`: The scope to enable the extension in (`user` or `workspace`). - -### Updating an extension - -For extensions installed from a local path or a git repository, you can -explicitly update to the latest version (as reflected in the -`gemini-extension.json` `version` field) with `gemini extensions update `. - -You can update all extensions with: - -``` -gemini extensions update --all -``` - -### Create a boilerplate extension - -We offer several example extensions `context`, `custom-commands`, -`exclude-tools` and `mcp-server`. You can view these examples -[here](https://github.com/google-gemini/gemini-cli/tree/main/packages/cli/src/commands/extensions/examples). - -To copy one of these examples into a development directory using the type of -your choosing, run: - -``` -gemini extensions new [template] -``` - -- ``: The path to create the extension in. -- `[template]`: The boilerplate template to use. - -### Link a local extension - -The `gemini extensions link` command will create a symbolic link from the -extension installation directory to the development path. - -This is useful so you don't have to run `gemini extensions update` every time -you make changes you'd like to test. - -``` -gemini extensions link -``` - -- ``: The path of the extension to link. - -## How it works - -On startup, Gemini CLI looks for extensions in `/.gemini/extensions` - -Extensions exist as a directory that contains a `gemini-extension.json` file. -For example: - -`/.gemini/extensions/my-extension/gemini-extension.json` - -### `gemini-extension.json` - -The `gemini-extension.json` file contains the configuration for the extension. -The file has the following structure: - -```json -{ - "name": "my-extension", - "version": "1.0.0", - "mcpServers": { - "my-server": { - "command": "node my-server.js" - } - }, - "contextFileName": "GEMINI.md", - "excludeTools": ["run_shell_command"] -} -``` - -- `name`: The name of the extension. This is used to uniquely identify the - extension and for conflict resolution when extension commands have the same - name as user or project commands. The name should be lowercase or numbers and - use dashes instead of underscores or spaces. This is how users will refer to - your extension in the CLI. Note that we expect this name to match the - extension directory name. -- `version`: The version of the extension. -- `mcpServers`: A map of MCP servers to settings. The key is the name of the - server, and the value is the server configuration. These servers will be - loaded on startup just like MCP servers settingsd in a - [`settings.json` file](../get-started/configuration.md). If both an extension - and a `settings.json` file settings an MCP server with the same name, the - server defined in the `settings.json` file takes precedence. - - Note that all MCP server configuration options are supported except for - `trust`. -- `contextFileName`: The name of the file that contains the context for the - extension. This will be used to load the context from the extension directory. - If this property is not used but a `GEMINI.md` file is present in your - extension directory, then that file will be loaded. -- `excludeTools`: An array of tool names to exclude from the model. You can also - specify command-specific restrictions for tools that support it, like the - `run_shell_command` tool. For example, - `"excludeTools": ["run_shell_command(rm -rf)"]` will block the `rm -rf` - command. Note that this differs from the MCP server `excludeTools` - functionality, which can be listed in the MCP server config. - -When Gemini CLI starts, it loads all the extensions and merges their -configurations. If there are any conflicts, the workspace configuration takes -precedence. - -### Settings - -_Note: This is an experimental feature. We do not yet recommend extension -authors introduce settings as part of their core flows._ - -Extensions can define settings that the user will be prompted to provide upon -installation. This is useful for things like API keys, URLs, or other -configuration that the extension needs to function. - -To define settings, add a `settings` array to your `gemini-extension.json` file. -Each object in the array should have the following properties: - -- `name`: A user-friendly name for the setting. -- `description`: A description of the setting and what it's used for. -- `envVar`: The name of the environment variable that the setting will be stored - as. -- `sensitive`: Optional boolean. If true, obfuscates the input the user provides - and stores the secret in keychain storage. **Example** - -```json -{ - "name": "my-api-extension", - "version": "1.0.0", - "settings": [ - { - "name": "API Key", - "description": "Your API key for the service.", - "envVar": "MY_API_KEY" - } - ] -} -``` - -When a user installs this extension, they will be prompted to enter their API -key. The value will be saved to a `.env` file in the extension's directory -(e.g., `/.gemini/extensions/my-api-extension/.env`). - -You can view a list of an extension's settings by running: - -``` -gemini extensions list -``` - -and you can update a given setting using: - -``` -gemini extensions config [setting name] [--scope ] -``` - -- `--scope`: The scope to set the setting in (`user` or `workspace`). This is - optional and will default to `user`. - -### Custom commands - -Extensions can provide [custom commands](../cli/custom-commands.md) by placing -TOML files in a `commands/` subdirectory within the extension directory. These -commands follow the same format as user and project custom commands and use -standard naming conventions. - -**Example** - -An extension named `gcp` with the following structure: - -``` -.gemini/extensions/gcp/ -├── gemini-extension.json -└── commands/ - ├── deploy.toml - └── gcs/ - └── sync.toml -``` - -Would provide these commands: - -- `/deploy` - Shows as `[gcp] Custom command from deploy.toml` in help -- `/gcs:sync` - Shows as `[gcp] Custom command from sync.toml` in help - -### Hooks - -Extensions can provide [hooks](../hooks/index.md) to intercept and customize -Gemini CLI behavior at specific lifecycle events. Hooks provided by an extension -must be defined in a `hooks/hooks.json` file within the extension directory. - -> [!IMPORTANT] Hooks are not defined directly in `gemini-extension.json`. The -> CLI specifically looks for the `hooks/hooks.json` file. - -#### Directory structure - -``` -.gemini/extensions/my-extension/ -├── gemini-extension.json -└── hooks/ - └── hooks.json -``` - -#### `hooks/hooks.json` format - -The `hooks.json` file contains a `hooks` object where keys are -[event names](../hooks/reference.md#supported-events) and values are arrays of -[hook definitions](../hooks/reference.md#hook-definition). - -```json -{ - "hooks": { - "before_agent": [ - { - "hooks": [ - { - "type": "command", - "command": "node ${extensionPath}/scripts/setup.js", - "name": "Extension Setup" - } - ] - } - ] - } -} -``` - -#### Supported variables - -Just like `gemini-extension.json`, the `hooks/hooks.json` file supports -[variable substitution](#variables). This is particularly useful for referencing -scripts within the extension directory using `${extensionPath}`. - -### Conflict resolution - -Extension commands have the lowest precedence. When a conflict occurs with user -or project commands: - -1. **No conflict**: Extension command uses its natural name (e.g., `/deploy`) -2. **With conflict**: Extension command is renamed with the extension prefix - (e.g., `/gcp.deploy`) - -For example, if both a user and the `gcp` extension define a `deploy` command: - -- `/deploy` - Executes the user's deploy command -- `/gcp.deploy` - Executes the extension's deploy command (marked with `[gcp]` - tag) - -### Variables - -Gemini CLI extensions allow variable substitution in both -`gemini-extension.json` and `hooks/hooks.json`. This can be useful if e.g., you -need the current directory to run an MCP server or hook script using -`"cwd": "${extensionPath}${/}run.ts"`. - -**Supported variables:** +## Next steps -| variable | description | -| -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `${extensionPath}` | The fully-qualified path of the extension in the user's filesystem e.g., '/Users/username/.gemini/extensions/example-extension'. This will not unwrap symlinks. | -| `${workspacePath}` | The fully-qualified path of the current workspace. | -| `${/} or ${pathSeparator}` | The path separator (differs per OS). | -| `${process.execPath}` | The path to the Node.js binary executing the CLI. | +- [Writing extensions](writing-extensions.md): Learn how to create your first + extension. +- [Extensions reference](reference.md): deeply understand the extension format, + commands, and configuration. +- [Extensions releasing](releasing.md): Learn how to share your extensions with + the world. diff --git a/docs/extensions/reference.md b/docs/extensions/reference.md new file mode 100644 index 00000000000..15842484e40 --- /dev/null +++ b/docs/extensions/reference.md @@ -0,0 +1,289 @@ +# Extensions reference + +This guide covers the `gemini extensions` commands and the structure of the +`gemini-extension.json` configuration file. + +## Extension management + +We offer a suite of extension management tools using `gemini extensions` +commands. + +Note that these commands are not supported from within the CLI, although you can +list installed extensions using the `/extensions list` subcommand. + +Note that all of these commands will only be reflected in active CLI sessions on +restart. + +### Installing an extension + +You can install an extension using `gemini extensions install` with either a +GitHub URL or a local path. + +Note that we create a copy of the installed extension, so you will need to run +`gemini extensions update` to pull in changes from both locally-defined +extensions and those on GitHub. + +NOTE: If you are installing an extension from GitHub, you'll need to have `git` +installed on your machine. See +[git installation instructions](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) +for help. + +``` +gemini extensions install [--ref ] [--auto-update] [--pre-release] [--consent] +``` + +- ``: The github URL or local path of the extension to install. +- `--ref`: The git ref to install from. +- `--auto-update`: Enable auto-update for this extension. +- `--pre-release`: Enable pre-release versions for this extension. +- `--consent`: Acknowledge the security risks of installing an extension and + skip the confirmation prompt. + +### Uninstalling an extension + +To uninstall one or more extensions, run +`gemini extensions uninstall `: + +``` +gemini extensions uninstall gemini-cli-security gemini-cli-another-extension +``` + +### Disabling an extension + +Extensions are, by default, enabled across all workspaces. You can disable an +extension entirely or for specific workspace. + +``` +gemini extensions disable [--scope ] +``` + +- ``: The name of the extension to disable. +- `--scope`: The scope to disable the extension in (`user` or `workspace`). + +### Enabling an extension + +You can enable extensions using `gemini extensions enable `. You can also +enable an extension for a specific workspace using +`gemini extensions enable --scope=workspace` from within that workspace. + +``` +gemini extensions enable [--scope ] +``` + +- ``: The name of the extension to enable. +- `--scope`: The scope to enable the extension in (`user` or `workspace`). + +### Updating an extension + +For extensions installed from a local path or a git repository, you can +explicitly update to the latest version (as reflected in the +`gemini-extension.json` `version` field) with `gemini extensions update `. + +You can update all extensions with: + +``` +gemini extensions update --all +``` + +### Create a boilerplate extension + +We offer several example extensions `context`, `custom-commands`, +`exclude-tools` and `mcp-server`. You can view these examples +[here](https://github.com/google-gemini/gemini-cli/tree/main/packages/cli/src/commands/extensions/examples). + +To copy one of these examples into a development directory using the type of +your choosing, run: + +``` +gemini extensions new [template] +``` + +- ``: The path to create the extension in. +- `[template]`: The boilerplate template to use. + +### Link a local extension + +The `gemini extensions link` command will create a symbolic link from the +extension installation directory to the development path. + +This is useful so you don't have to run `gemini extensions update` every time +you make changes you'd like to test. + +``` +gemini extensions link +``` + +- ``: The path of the extension to link. + +## Extension format + +On startup, Gemini CLI looks for extensions in `/.gemini/extensions` + +Extensions exist as a directory that contains a `gemini-extension.json` file. +For example: + +`/.gemini/extensions/my-extension/gemini-extension.json` + +### `gemini-extension.json` + +The `gemini-extension.json` file contains the configuration for the extension. +The file has the following structure: + +```json +{ + "name": "my-extension", + "version": "1.0.0", + "mcpServers": { + "my-server": { + "command": "node my-server.js" + } + }, + "contextFileName": "GEMINI.md", + "excludeTools": ["run_shell_command"] +} +``` + +- `name`: The name of the extension. This is used to uniquely identify the + extension and for conflict resolution when extension commands have the same + name as user or project commands. The name should be lowercase or numbers and + use dashes instead of underscores or spaces. This is how users will refer to + your extension in the CLI. Note that we expect this name to match the + extension directory name. +- `version`: The version of the extension. +- `mcpServers`: A map of MCP servers to settings. The key is the name of the + server, and the value is the server configuration. These servers will be + loaded on startup just like MCP servers settingsd in a + [`settings.json` file](../get-started/configuration.md). If both an extension + and a `settings.json` file settings an MCP server with the same name, the + server defined in the `settings.json` file takes precedence. + - Note that all MCP server configuration options are supported except for + `trust`. +- `contextFileName`: The name of the file that contains the context for the + extension. This will be used to load the context from the extension directory. + If this property is not used but a `GEMINI.md` file is present in your + extension directory, then that file will be loaded. +- `excludeTools`: An array of tool names to exclude from the model. You can also + specify command-specific restrictions for tools that support it, like the + `run_shell_command` tool. For example, + `"excludeTools": ["run_shell_command(rm -rf)"]` will block the `rm -rf` + command. Note that this differs from the MCP server `excludeTools` + functionality, which can be listed in the MCP server config. + +When Gemini CLI starts, it loads all the extensions and merges their +configurations. If there are any conflicts, the workspace configuration takes +precedence. + +### Settings + +_Note: This is an experimental feature. We do not yet recommend extension +authors introduce settings as part of their core flows._ + +Extensions can define settings that the user will be prompted to provide upon +installation. This is useful for things like API keys, URLs, or other +configuration that the extension needs to function. + +To define settings, add a `settings` array to your `gemini-extension.json` file. +Each object in the array should have the following properties: + +- `name`: A user-friendly name for the setting. +- `description`: A description of the setting and what it's used for. +- `envVar`: The name of the environment variable that the setting will be stored + as. +- `sensitive`: Optional boolean. If true, obfuscates the input the user provides + and stores the secret in keychain storage. **Example** + +```json +{ + "name": "my-api-extension", + "version": "1.0.0", + "settings": [ + { + "name": "API Key", + "description": "Your API key for the service.", + "envVar": "MY_API_KEY" + } + ] +} +``` + +When a user installs this extension, they will be prompted to enter their API +key. The value will be saved to a `.env` file in the extension's directory +(e.g., `/.gemini/extensions/my-api-extension/.env`). + +You can view a list of an extension's settings by running: + +``` +gemini extensions list +``` + +and you can update a given setting using: + +``` +gemini extensions config [setting name] [--scope ] +``` + +- `--scope`: The scope to set the setting in (`user` or `workspace`). This is + optional and will default to `user`. + +### Custom commands + +Extensions can provide [custom commands](../cli/custom-commands.md) by placing +TOML files in a `commands/` subdirectory within the extension directory. These +commands follow the same format as user and project custom commands and use +standard naming conventions. + +**Example** + +An extension named `gcp` with the following structure: + +``` +.gemini/extensions/gcp/ +├── gemini-extension.json +└── commands/ + ├── deploy.toml + └── gcs/ + └── sync.toml +``` + +Would provide these commands: + +- `/deploy` - Shows as `[gcp] Custom command from deploy.toml` in help +- `/gcs:sync` - Shows as `[gcp] Custom command from sync.toml` in help + +### Hooks + +Extensions can provide [hooks](../hooks/index.md) to intercept and customize +Gemini CLI behavior at specific lifecycle events. Hooks provided by an extension +must be defined in a `hooks/hooks.json` file within the extension directory. + +> [!IMPORTANT] Hooks are not defined directly in `gemini-extension.json`. The +> CLI specifically looks for the `hooks/hooks.json` file. + +### Conflict resolution + +Extension commands have the lowest precedence. When a conflict occurs with user +or project commands: + +1. **No conflict**: Extension command uses its natural name (e.g., `/deploy`) +2. **With conflict**: Extension command is renamed with the extension prefix + (e.g., `/gcp.deploy`) + +For example, if both a user and the `gcp` extension define a `deploy` command: + +- `/deploy` - Executes the user's deploy command +- `/gcp.deploy` - Executes the extension's deploy command (marked with `[gcp]` + tag) + +## Variables + +Gemini CLI extensions allow variable substitution in `gemini-extension.json`. +This can be useful if e.g., you need the current directory to run an MCP server +using an argument like `"args": ["${extensionPath}${/}dist${/}server.js"]`. + +**Supported variables:** + +| variable | description | +| -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `${extensionPath}` | The fully-qualified path of the extension in the user's filesystem e.g., '/Users/username/.gemini/extensions/example-extension'. This will not unwrap symlinks. | +| `${workspacePath}` | The fully-qualified path of the current workspace. | +| `${/} or ${pathSeparator}` | The path separator (differs per OS). | diff --git a/docs/extensions/extension-releasing.md b/docs/extensions/releasing.md similarity index 100% rename from docs/extensions/extension-releasing.md rename to docs/extensions/releasing.md diff --git a/docs/extensions/getting-started-extensions.md b/docs/extensions/writing-extensions.md similarity index 99% rename from docs/extensions/getting-started-extensions.md rename to docs/extensions/writing-extensions.md index 8f174a29662..3e390079918 100644 --- a/docs/extensions/getting-started-extensions.md +++ b/docs/extensions/writing-extensions.md @@ -229,7 +229,7 @@ primary ways of releasing extensions are via a Git repository or through GitHub Releases. Using a public Git repository is the simplest method. For detailed instructions on both methods, please refer to the -[Extension Releasing Guide](./extension-releasing.md). +[Extension Releasing Guide](./releasing.md). ## Conclusion diff --git a/docs/index.md b/docs/index.md index 83e834818ed..5d276f7aab6 100644 --- a/docs/index.md +++ b/docs/index.md @@ -100,10 +100,10 @@ This documentation is organized into the following sections: - **[Introduction: Extensions](./extensions/index.md):** How to extend the CLI with new functionality. -- **[Get Started with extensions](./extensions/getting-started-extensions.md):** - Learn how to build your own extension. -- **[Extension releasing](./extensions/extension-releasing.md):** How to release - Gemini CLI extensions. +- **[Writing extensions](./extensions/writing-extensions.md):** Learn how to + build your own extension. +- **[Extension releasing](./extensions/releasing.md):** How to release Gemini + CLI extensions. ### Hooks diff --git a/docs/sidebar.json b/docs/sidebar.json index a65bade0523..442af823806 100644 --- a/docs/sidebar.json +++ b/docs/sidebar.json @@ -192,12 +192,20 @@ "slug": "docs/extensions" }, { - "label": "Get started with extensions", - "slug": "docs/extensions/getting-started-extensions" + "label": "Writing extensions", + "slug": "docs/extensions/writing-extensions" }, { - "label": "Extension releasing", - "slug": "docs/extensions/extension-releasing" + "label": "Extensions reference", + "slug": "docs/extensions/reference" + }, + { + "label": "Best practices", + "slug": "docs/extensions/best-practices" + }, + { + "label": "Extensions releasing", + "slug": "docs/extensions/releasing" } ] },