mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-03-10 22:21:22 -07:00
Update extensions docs (#16093)
This commit is contained in:
139
docs/extensions/best-practices.md
Normal file
139
docs/extensions/best-practices.md
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
# Extensions on Gemini CLI: Best practices
|
||||||
|
|
||||||
|
This guide covers best practices for developing, securing, and maintaining
|
||||||
|
Gemini CLI extensions.
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
Developing extensions for Gemini CLI is intended to be a lightweight, iterative
|
||||||
|
process.
|
||||||
|
|
||||||
|
### 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
|
||||||
|
|
||||||
|
When building a Gemini CLI extension, follow general security best practices
|
||||||
|
(such as least privilege and input validation) to reduce risk.
|
||||||
|
|
||||||
|
### Minimal permissions
|
||||||
|
|
||||||
|
When defining tools in your MCP server, only request the permissions necessary.
|
||||||
|
Avoid giving the model broad access (like full shell access) if a more
|
||||||
|
restricted set of tools will suffice.
|
||||||
|
|
||||||
|
If you must use powerful tools like `run_shell_command`, consider restricting
|
||||||
|
them to specific commands in your `gemini-extension.json`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "my-safe-extension",
|
||||||
|
"excludeTools": ["run_shell_command(rm -rf *)"]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This ensures that even if the model tries to execute a dangerous command, it
|
||||||
|
will be blocked at the CLI level.
|
||||||
|
|
||||||
|
### 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
|
||||||
|
|
||||||
|
You can upload your extension directly to GitHub to list it in the gallery.
|
||||||
|
Gemini CLI extensions also offers support for more complicated
|
||||||
|
[releases](releasing.md).
|
||||||
|
|
||||||
|
### 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.
|
||||||
@@ -1,377 +1,44 @@
|
|||||||
# Gemini CLI extensions
|
# Gemini CLI extensions
|
||||||
|
|
||||||
_This documentation is up-to-date with the v0.4.0 release._
|
Gemini CLI extensions package prompts, MCP servers, custom commands, hooks, and
|
||||||
|
agent skills into a familiar and user-friendly format. With extensions, you can
|
||||||
Gemini CLI extensions package prompts, MCP servers, Agent Skills, 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.
|
expand the capabilities of Gemini CLI and share those capabilities with others.
|
||||||
They're designed to be easily installable and shareable.
|
They are designed to be easily installable and shareable.
|
||||||
|
|
||||||
To see examples of extensions, you can browse a gallery of
|
To see examples of extensions, you can browse a gallery of
|
||||||
[Gemini CLI extensions](https://geminicli.com/extensions/browse/).
|
[Gemini CLI extensions](https://geminicli.com/extensions/browse/).
|
||||||
|
|
||||||
See [getting started docs](getting-started-extensions.md) for a guide on
|
## Managing extensions
|
||||||
creating your first extension.
|
|
||||||
|
|
||||||
See [releasing docs](extension-releasing.md) for an advanced guide on setting up
|
You can verify your installed extensions and their status using the interactive
|
||||||
GitHub releases.
|
command:
|
||||||
|
|
||||||
## Extension management
|
```bash
|
||||||
|
/extensions list
|
||||||
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 <source> [--ref <ref>] [--auto-update] [--pre-release] [--consent]
|
|
||||||
```
|
```
|
||||||
|
|
||||||
- `<source>`: The github URL or local path of the extension to install.
|
or in noninteractive mode:
|
||||||
- `--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
|
```bash
|
||||||
|
|
||||||
To uninstall one or more extensions, run
|
|
||||||
`gemini extensions uninstall <name...>`:
|
|
||||||
|
|
||||||
```
|
|
||||||
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 <name> [--scope <scope>]
|
|
||||||
```
|
|
||||||
|
|
||||||
- `<name>`: 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 <name>`. You can also
|
|
||||||
enable an extension for a specific workspace using
|
|
||||||
`gemini extensions enable <name> --scope=workspace` from within that workspace.
|
|
||||||
|
|
||||||
```
|
|
||||||
gemini extensions enable <name> [--scope <scope>]
|
|
||||||
```
|
|
||||||
|
|
||||||
- `<name>`: 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 <name>`.
|
|
||||||
|
|
||||||
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 <path> [template]
|
|
||||||
```
|
|
||||||
|
|
||||||
- `<path>`: 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 <path>
|
|
||||||
```
|
|
||||||
|
|
||||||
- `<path>`: The path of the extension to link.
|
|
||||||
|
|
||||||
## How it works
|
|
||||||
|
|
||||||
On startup, Gemini CLI looks for extensions in `<home>/.gemini/extensions`
|
|
||||||
|
|
||||||
Extensions exist as a directory that contains a `gemini-extension.json` file.
|
|
||||||
For example:
|
|
||||||
|
|
||||||
`<home>/.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 settings 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., `<home>/.gemini/extensions/my-api-extension/.env`).
|
|
||||||
|
|
||||||
You can view a list of an extension's settings by running:
|
|
||||||
|
|
||||||
```
|
|
||||||
gemini extensions list
|
gemini extensions list
|
||||||
```
|
```
|
||||||
|
|
||||||
and you can update a given setting using:
|
## Installation
|
||||||
|
|
||||||
```
|
To install a real extension, you can use the `extensions install` command with a
|
||||||
gemini extensions config <extension name> [setting name] [--scope <scope>]
|
GitHub repository URL in noninteractive mode. For example:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
gemini extensions install https://github.com/gemini-cli-extensions/workspace
|
||||||
```
|
```
|
||||||
|
|
||||||
- `--scope`: The scope to set the setting in (`user` or `workspace`). This is
|
## Next steps
|
||||||
optional and will default to `user`.
|
|
||||||
|
|
||||||
### Custom commands
|
- [Writing extensions](writing-extensions.md): Learn how to create your first
|
||||||
|
extension.
|
||||||
Extensions can provide [custom commands](../cli/custom-commands.md) by placing
|
- [Extensions reference](reference.md): Deeply understand the extension format,
|
||||||
TOML files in a `commands/` subdirectory within the extension directory. These
|
commands, and configuration.
|
||||||
commands follow the same format as user and project custom commands and use
|
- [Best practices](best-practices.md): Learn strategies for building great
|
||||||
standard naming conventions.
|
extensions.
|
||||||
|
- [Extensions releasing](releasing.md): Learn how to share your extensions with
|
||||||
**Example**
|
the world.
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
### Agent Skills
|
|
||||||
|
|
||||||
_Note: This is an experimental feature enabled via `experimental.skills`._
|
|
||||||
|
|
||||||
Extensions can bundle [Agent Skills](../cli/skills.md) to provide on-demand
|
|
||||||
expertise and specialized workflows. To include skills in your extension, place
|
|
||||||
them in a `skills/` subdirectory within the extension directory. Each skill must
|
|
||||||
follow the [Agent Skills structure](../cli/skills.md#folder-structure),
|
|
||||||
including a `SKILL.md` file.
|
|
||||||
|
|
||||||
**Example**
|
|
||||||
|
|
||||||
An extension named `security-toolkit` with the following structure:
|
|
||||||
|
|
||||||
```
|
|
||||||
.gemini/extensions/security-toolkit/
|
|
||||||
├── gemini-extension.json
|
|
||||||
└── skills/
|
|
||||||
├── audit/
|
|
||||||
│ ├── SKILL.md
|
|
||||||
│ └── scripts/
|
|
||||||
│ └── scan.py
|
|
||||||
└── hardening/
|
|
||||||
└── SKILL.md
|
|
||||||
```
|
|
||||||
|
|
||||||
Upon installation, these skills will be discovered by Gemini CLI and can be
|
|
||||||
activated during a session when the model identifies a task matching their
|
|
||||||
descriptions.
|
|
||||||
|
|
||||||
Extension skills have the lowest precedence and will be overridden by user or
|
|
||||||
workspace skills of the same name. They can be viewed and managed (enabled or
|
|
||||||
disabled) using the [`/skills` command](../cli/skills.md#managing-skills).
|
|
||||||
|
|
||||||
### 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": {
|
|
||||||
"BeforeAgent": [
|
|
||||||
{
|
|
||||||
"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:**
|
|
||||||
|
|
||||||
| 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. |
|
|
||||||
|
|||||||
312
docs/extensions/reference.md
Normal file
312
docs/extensions/reference.md
Normal file
@@ -0,0 +1,312 @@
|
|||||||
|
# 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 (e.g. `gemini extensions install`) are not supported
|
||||||
|
from within the CLI's **interactive mode**, although you can list installed
|
||||||
|
extensions using the `/extensions list` slash command.
|
||||||
|
|
||||||
|
Note that all of these management operations (including updates to slash
|
||||||
|
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 <source> [--ref <ref>] [--auto-update] [--pre-release] [--consent]
|
||||||
|
```
|
||||||
|
|
||||||
|
- `<source>`: 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 <name...>`:
|
||||||
|
|
||||||
|
```
|
||||||
|
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 <name> [--scope <scope>]
|
||||||
|
```
|
||||||
|
|
||||||
|
- `<name>`: 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 <name>`. You can also
|
||||||
|
enable an extension for a specific workspace using
|
||||||
|
`gemini extensions enable <name> --scope=workspace` from within that workspace.
|
||||||
|
|
||||||
|
```
|
||||||
|
gemini extensions enable <name> [--scope <scope>]
|
||||||
|
```
|
||||||
|
|
||||||
|
- `<name>`: 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 <name>`.
|
||||||
|
|
||||||
|
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 <path> [template]
|
||||||
|
```
|
||||||
|
|
||||||
|
- `<path>`: 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 <path>
|
||||||
|
```
|
||||||
|
|
||||||
|
- `<path>`: The path of the extension to link.
|
||||||
|
|
||||||
|
## Extension format
|
||||||
|
|
||||||
|
On startup, Gemini CLI looks for extensions in `<home>/.gemini/extensions`
|
||||||
|
|
||||||
|
Extensions exist as a directory that contains a `gemini-extension.json` file.
|
||||||
|
For example:
|
||||||
|
|
||||||
|
`<home>/.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",
|
||||||
|
"description": "My awesome extension",
|
||||||
|
"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.
|
||||||
|
- `description`: A short description of the extension. This will be displayed on
|
||||||
|
[geminicli.com/extensions](https://geminicli.com/extensions).
|
||||||
|
- `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., `<home>/.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 <extension name> [setting name] [--scope <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.
|
||||||
|
|
||||||
|
### Agent Skills
|
||||||
|
|
||||||
|
Extensions can bundle [Agent Skills](../cli/skills.md) to provide specialized
|
||||||
|
workflows. Skills must be placed in a `skills/` directory within the extension.
|
||||||
|
|
||||||
|
**Example**
|
||||||
|
|
||||||
|
An extension with the following structure:
|
||||||
|
|
||||||
|
```
|
||||||
|
.gemini/extensions/my-extension/
|
||||||
|
├── gemini-extension.json
|
||||||
|
└── skills/
|
||||||
|
└── security-audit/
|
||||||
|
└── SKILL.md
|
||||||
|
```
|
||||||
|
|
||||||
|
Will expose a `security-audit` skill that the model can activate.
|
||||||
|
|
||||||
|
### 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). |
|
||||||
@@ -8,7 +8,19 @@ file.
|
|||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
Before you start, make sure you have the Gemini CLI installed and a basic
|
Before you start, make sure you have the Gemini CLI installed and a basic
|
||||||
understanding of Node.js and TypeScript.
|
understanding of Node.js.
|
||||||
|
|
||||||
|
## When to use what
|
||||||
|
|
||||||
|
Extensions offer a variety of ways to customize Gemini CLI.
|
||||||
|
|
||||||
|
| Feature | What it is | When to use it | Invoked by |
|
||||||
|
| :------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------- |
|
||||||
|
| **[MCP server](reference.md#mcp-servers)** | A standard way to expose new tools and data sources to the model. | Use this when you want the model to be able to _do_ new things, like fetching data from an internal API, querying a database, or controlling a local application. We also support MCP resources (which can replace custom commands) and system instructions (which can replace custom context) | Model |
|
||||||
|
| **[Custom commands](../cli/custom-commands.md)** | A shortcut (like `/my-cmd`) that executes a pre-defined prompt or shell command. | Use this for repetitive tasks or to save long, complex prompts that you use frequently. Great for automation. | User |
|
||||||
|
| **[Context file (`GEMINI.md`)](reference.md#contextfilename)** | A markdown file containing instructions that are loaded into the model's context at the start of every session. | Use this to define the "personality" of your extension, set coding standards, or provide essential knowledge that the model should always have. | CLI provides to model |
|
||||||
|
| **[Agent skills](../cli/skills.md)** | A specialized set of instructions and workflows that the model activates only when needed. | Use this for complex, occasional tasks (like "create a PR" or "audit security") to avoid cluttering the main context window when the skill isn't being used. | Model |
|
||||||
|
| **[Hooks](../hooks/index.md)** | A way to intercept and customize the CLI's behavior at specific lifecycle events (e.g., before/after a tool call). | Use this when you want to automate actions based on what the model is doing, like validating tool arguments, logging activity, or modifying the model's input/output. | CLI |
|
||||||
|
|
||||||
## Step 1: Create a new extension
|
## Step 1: Create a new extension
|
||||||
|
|
||||||
@@ -26,10 +38,9 @@ This will create a new directory with the following structure:
|
|||||||
|
|
||||||
```
|
```
|
||||||
my-first-extension/
|
my-first-extension/
|
||||||
├── example.ts
|
├── example.js
|
||||||
├── gemini-extension.json
|
├── gemini-extension.json
|
||||||
├── package.json
|
└── package.json
|
||||||
└── tsconfig.json
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Step 2: Understand the extension files
|
## Step 2: Understand the extension files
|
||||||
@@ -43,12 +54,12 @@ and use your extension.
|
|||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"name": "my-first-extension",
|
"name": "mcp-server-example",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"mcpServers": {
|
"mcpServers": {
|
||||||
"nodeServer": {
|
"nodeServer": {
|
||||||
"command": "node",
|
"command": "node",
|
||||||
"args": ["${extensionPath}${/}dist${/}example.js"],
|
"args": ["${extensionPath}${/}example.js"],
|
||||||
"cwd": "${extensionPath}"
|
"cwd": "${extensionPath}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -64,12 +75,12 @@ and use your extension.
|
|||||||
with the absolute path to your extension's installation directory. This
|
with the absolute path to your extension's installation directory. This
|
||||||
allows your extension to work regardless of where it's installed.
|
allows your extension to work regardless of where it's installed.
|
||||||
|
|
||||||
### `example.ts`
|
### `example.js`
|
||||||
|
|
||||||
This file contains the source code for your MCP server. It's a simple Node.js
|
This file contains the source code for your MCP server. It's a simple Node.js
|
||||||
server that uses the `@modelcontextprotocol/sdk`.
|
server that uses the `@modelcontextprotocol/sdk`.
|
||||||
|
|
||||||
```typescript
|
```javascript
|
||||||
/**
|
/**
|
||||||
* @license
|
* @license
|
||||||
* Copyright 2025 Google LLC
|
* Copyright 2025 Google LLC
|
||||||
@@ -118,16 +129,15 @@ await server.connect(transport);
|
|||||||
This server defines a single tool called `fetch_posts` that fetches data from a
|
This server defines a single tool called `fetch_posts` that fetches data from a
|
||||||
public API.
|
public API.
|
||||||
|
|
||||||
### `package.json` and `tsconfig.json`
|
### `package.json`
|
||||||
|
|
||||||
These are standard configuration files for a TypeScript project. The
|
This is the standard configuration file for a Node.js project. It defines
|
||||||
`package.json` file defines dependencies and a `build` script, and
|
dependencies and scripts.
|
||||||
`tsconfig.json` configures the TypeScript compiler.
|
|
||||||
|
|
||||||
## Step 3: Build and link your extension
|
## Step 3: Link your extension
|
||||||
|
|
||||||
Before you can use the extension, you need to compile the TypeScript code and
|
Before you can use the extension, you need to link it to your Gemini CLI
|
||||||
link the extension to your Gemini CLI installation for local development.
|
installation for local development.
|
||||||
|
|
||||||
1. **Install dependencies:**
|
1. **Install dependencies:**
|
||||||
|
|
||||||
@@ -136,16 +146,7 @@ link the extension to your Gemini CLI installation for local development.
|
|||||||
npm install
|
npm install
|
||||||
```
|
```
|
||||||
|
|
||||||
2. **Build the server:**
|
2. **Link the extension:**
|
||||||
|
|
||||||
```bash
|
|
||||||
npm run build
|
|
||||||
```
|
|
||||||
|
|
||||||
This will compile `example.ts` into `dist/example.js`, which is the file
|
|
||||||
referenced in your `gemini-extension.json`.
|
|
||||||
|
|
||||||
3. **Link the extension:**
|
|
||||||
|
|
||||||
The `link` command creates a symbolic link from the Gemini CLI extensions
|
The `link` command creates a symbolic link from the Gemini CLI extensions
|
||||||
directory to your development directory. This means any changes you make
|
directory to your development directory. This means any changes you make
|
||||||
@@ -212,7 +213,7 @@ need this for extensions built to expose commands and prompts.
|
|||||||
"mcpServers": {
|
"mcpServers": {
|
||||||
"nodeServer": {
|
"nodeServer": {
|
||||||
"command": "node",
|
"command": "node",
|
||||||
"args": ["${extensionPath}${/}dist${/}example.js"],
|
"args": ["${extensionPath}${/}example.js"],
|
||||||
"cwd": "${extensionPath}"
|
"cwd": "${extensionPath}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -265,7 +266,7 @@ primary ways of releasing extensions are via a Git repository or through GitHub
|
|||||||
Releases. Using a public Git repository is the simplest method.
|
Releases. Using a public Git repository is the simplest method.
|
||||||
|
|
||||||
For detailed instructions on both methods, please refer to the
|
For detailed instructions on both methods, please refer to the
|
||||||
[Extension Releasing Guide](./extension-releasing.md).
|
[Extension Releasing Guide](./releasing.md).
|
||||||
|
|
||||||
## Conclusion
|
## Conclusion
|
||||||
|
|
||||||
@@ -102,10 +102,10 @@ This documentation is organized into the following sections:
|
|||||||
|
|
||||||
- **[Introduction: Extensions](./extensions/index.md):** How to extend the CLI
|
- **[Introduction: Extensions](./extensions/index.md):** How to extend the CLI
|
||||||
with new functionality.
|
with new functionality.
|
||||||
- **[Get Started with extensions](./extensions/getting-started-extensions.md):**
|
- **[Writing extensions](./extensions/writing-extensions.md):** Learn how to
|
||||||
Learn how to build your own extension.
|
build your own extension.
|
||||||
- **[Extension releasing](./extensions/extension-releasing.md):** How to release
|
- **[Extension releasing](./extensions/releasing.md):** How to release Gemini
|
||||||
Gemini CLI extensions.
|
CLI extensions.
|
||||||
|
|
||||||
### Hooks
|
### Hooks
|
||||||
|
|
||||||
|
|||||||
@@ -192,12 +192,20 @@
|
|||||||
"slug": "docs/extensions"
|
"slug": "docs/extensions"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": "Get started with extensions",
|
"label": "Writing extensions",
|
||||||
"slug": "docs/extensions/getting-started-extensions"
|
"slug": "docs/extensions/writing-extensions"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": "Extension releasing",
|
"label": "Extensions reference",
|
||||||
"slug": "docs/extensions/extension-releasing"
|
"slug": "docs/extensions/reference"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Best practices",
|
||||||
|
"slug": "docs/extensions/best-practices"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Extensions releasing",
|
||||||
|
"slug": "docs/extensions/releasing"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user