Docs: Clarify extensions documentation. (#19277)

This commit is contained in:
Jenna Inouye
2026-02-17 13:57:27 -08:00
committed by GitHub
parent b7004ad5ed
commit 216d7272bb
6 changed files with 443 additions and 569 deletions
+102 -53
View File
@@ -1,19 +1,19 @@
# Extensions on Gemini CLI: Best practices # Gemini CLI extension best practices
This guide covers best practices for developing, securing, and maintaining This guide covers best practices for developing, securing, and maintaining
Gemini CLI extensions. Gemini CLI extensions.
## Development ## Development
Developing extensions for Gemini CLI is intended to be a lightweight, iterative Developing extensions for Gemini CLI is a lightweight, iterative process. Use
process. these strategies to build robust and efficient extensions.
### Structure your extension ### Structure your extension
While simple extensions can just be a few files, we recommend a robust structure While simple extensions may contain only a few files, we recommend a organized
for complex extensions: structure for complex projects.
``` ```text
my-extension/ my-extension/
├── package.json ├── package.json
├── tsconfig.json ├── tsconfig.json
@@ -24,47 +24,50 @@ my-extension/
└── dist/ └── dist/
``` ```
- **Use TypeScript**: We strongly recommend using TypeScript for type safety and - **Use TypeScript:** We strongly recommend using TypeScript for type safety and
better tooling. improved developer experience.
- **Separate source and build**: Keep your source code in `src` and build to - **Separate source and build:** Keep your source code in `src/` and output
`dist`. build artifacts to `dist/`.
- **Bundle dependencies**: If your extension has many dependencies, consider - **Bundle dependencies:** If your extension has many dependencies, bundle them
bundling them (e.g., with `esbuild` or `webpack`) to reduce install time and using a tool like `esbuild` to reduce installation time and avoid conflicts.
potential conflicts.
### Iterate with `link` ### Iterate with `link`
Use `gemini extensions link` to develop locally without constantly reinstalling: Use the `gemini extensions link` command to develop locally without reinstalling
your extension after every change.
```bash ```bash
cd my-extension cd my-extension
gemini extensions link . gemini extensions link .
``` ```
Changes to your code (after rebuilding) will be immediately available in the CLI Changes to your code are immediately available in the CLI after you rebuild the
on restart. project and restart the session.
### Use `GEMINI.md` effectively ### Use `GEMINI.md` effectively
Your `GEMINI.md` file provides context to the model. Keep it focused: Your `GEMINI.md` file provides essential context to the model.
- **Do:** Explain high-level goals and how to use the provided tools. - **Focus on goals:** Explain the high-level purpose of the extension and how to
- **Don't:** Dump your entire documentation. interact with its tools.
- **Do:** Use clear, concise language. - **Be concise:** Avoid dumping exhaustive documentation into the file. Use
clear, direct language.
- **Provide examples:** Include brief examples of how the model should use
specific tools or commands.
## Security ## Security
When building a Gemini CLI extension, follow general security best practices Follow the principle of least privilege and rigorous input validation when
(such as least privilege and input validation) to reduce risk. building extensions.
### Minimal permissions ### Minimal permissions
When defining tools in your MCP server, only request the permissions necessary. Only request the permissions your MCP server needs to function. Avoid giving the
Avoid giving the model broad access (like full shell access) if a more model broad access (such as full shell access) if restricted tools are
restricted set of tools will suffice. sufficient.
If you must use powerful tools like `run_shell_command`, consider restricting If your extension uses powerful tools like `run_shell_command`, restrict them in
them to specific commands in your `gemini-extension.json`: your `gemini-extension.json` file:
```json ```json
{ {
@@ -73,27 +76,26 @@ them to specific commands in your `gemini-extension.json`:
} }
``` ```
This ensures that even if the model tries to execute a dangerous command, it This ensures the CLI blocks dangerous commands even if the model attempts to
will be blocked at the CLI level. execute them.
### Validate inputs ### Validate inputs
Your MCP server is running on the user's machine. Always validate inputs to your Your MCP server runs on the user's machine. Always validate tool inputs to
tools to prevent arbitrary code execution or filesystem access outside the prevent arbitrary code execution or unauthorized filesystem access.
intended scope.
```typescript ```typescript
// Good: Validating paths // Example: Validating paths
if (!path.resolve(inputPath).startsWith(path.resolve(allowedDir) + path.sep)) { if (!path.resolve(inputPath).startsWith(path.resolve(allowedDir) + path.sep)) {
throw new Error('Access denied'); throw new Error('Access denied');
} }
``` ```
### Sensitive settings ### Secure sensitive settings
If your extension requires API keys, use the `sensitive: true` option in If your extension requires API keys or other secrets, use the `sensitive: true`
`gemini-extension.json`. This ensures keys are stored securely in the system option in your manifest. This ensures keys are stored in the system keychain and
keychain and obfuscated in the UI. obfuscated in the CLI output.
```json ```json
"settings": [ "settings": [
@@ -105,35 +107,82 @@ keychain and obfuscated in the UI.
] ]
``` ```
## Releasing ## Release
You can upload your extension directly to GitHub to list it in the gallery. Follow standard versioning and release practices to ensure a smooth experience
Gemini CLI extensions also offers support for more complicated for your users.
[releases](releasing.md).
### Semantic versioning ### Semantic versioning
Follow [Semantic Versioning](https://semver.org/). Follow [Semantic Versioning (SemVer)](https://semver.org/) to communicate
changes clearly.
- **Major**: Breaking changes (renaming tools, changing arguments). - **Major:** Breaking changes (e.g., renaming tools or changing arguments).
- **Minor**: New features (new tools, commands). - **Minor:** New features (e.g., adding new tools or commands).
- **Patch**: Bug fixes. - **Patch:** Bug fixes and performance improvements.
### Release Channels ### Release channels
Use git branches to manage release channels (e.g., `main` for stable, `dev` for Use Git branches to manage release channels. This lets users choose between
bleeding edge). This allows users to choose their stability level: stability and the latest features.
```bash ```bash
# Stable # Install the stable version (default branch)
gemini extensions install github.com/user/repo gemini extensions install github.com/user/repo
# Dev # Install the development version
gemini extensions install github.com/user/repo --ref dev gemini extensions install github.com/user/repo --ref dev
``` ```
### Clean artifacts ### Clean artifacts
If you are using GitHub Releases, ensure your release artifacts only contain the When using GitHub Releases, ensure your archives only contain necessary files
necessary files (`dist/`, `gemini-extension.json`, `package.json`). Exclude (such as `dist/`, `gemini-extension.json`, and `package.json`). Exclude
`node_modules` (users will install them) and `src/` to keep downloads small. `node_modules/` and `src/` to minimize download size.
## Test and verify
Test your extension thoroughly before releasing it to users.
- **Manual verification:** Use `gemini extensions link` to test your extension
in a live CLI session. Verify that tools appear in the debug console (F12) and
that custom commands resolve correctly.
- **Automated testing:** If your extension includes an MCP server, write unit
tests for your tool logic using a framework like Vitest or Jest. You can test
MCP tools in isolation by mocking the transport layer.
## Troubleshooting
Use these tips to diagnose and fix common extension issues.
### Extension not loading
If your extension doesn't appear in `/extensions list`:
- **Check the manifest:** Ensure `gemini-extension.json` is in the root
directory and contains valid JSON.
- **Verify the name:** The `name` field in the manifest must match the extension
directory name exactly.
- **Restart the CLI:** Extensions are loaded at the start of a session. Restart
Gemini CLI after making changes to the manifest or linking a new extension.
### MCP server failures
If your tools aren't working as expected:
- **Check the logs:** View the CLI logs to see if the MCP server failed to
start.
- **Test the command:** Run the server's `command` and `args` directly in your
terminal to ensure it starts correctly outside of Gemini CLI.
- **Debug console:** In interactive mode, press **F12** to open the debug
console and inspect tool calls and responses.
### Command conflicts
If a custom command isn't responding:
- **Check precedence:** Remember that user and project commands take precedence
over extension commands. Use the prefixed name (e.g., `/extension.command`) to
verify the extension's version.
- **Help command:** Run `/help` to see a list of all available commands and
their sources.
+34 -18
View File
@@ -6,19 +6,44 @@ With extensions, you can expand the capabilities of Gemini CLI and share those
capabilities with others. They are designed to be easily installable and capabilities with others. They are designed to be easily installable and
shareable. shareable.
To see examples of extensions, you can browse a gallery of To see what's possible, browse the
[Gemini CLI extensions](https://geminicli.com/extensions/browse/). [Gemini CLI extension gallery](https://geminicli.com/extensions/browse/).
## Managing extensions ## Choose your path
You can verify your installed extensions and their status using the interactive Choose the guide that best fits your needs.
command:
### I want to use extensions
Learn how to discover, install, and manage extensions to enhance your Gemini CLI
experience.
- **[Manage extensions](#manage-extensions):** List and verify your installed
extensions.
- **[Install extensions](#installation):** Add new capabilities from GitHub or
local paths.
### I want to build extensions
Learn how to create, test, and share your own extensions with the community.
- **[Build extensions](writing-extensions.md):** Create your first extension
from a template.
- **[Best practices](best-practices.md):** Learn how to build secure and
reliable extensions.
- **[Publish to the gallery](releasing.md):** Share your work with the world.
## Manage extensions
Use the interactive `/extensions` command to verify your installed extensions
and their status:
```bash ```bash
/extensions list /extensions list
``` ```
or in noninteractive mode: You can also manage extensions from your terminal using the `gemini extensions`
command group:
```bash ```bash
gemini extensions list gemini extensions list
@@ -26,20 +51,11 @@ gemini extensions list
## Installation ## Installation
To install a real extension, you can use the `extensions install` command with a Install an extension by providing its GitHub repository URL. For example:
GitHub repository URL in noninteractive mode. For example:
```bash ```bash
gemini extensions install https://github.com/gemini-cli-extensions/workspace gemini extensions install https://github.com/gemini-cli-extensions/workspace
``` ```
## Next steps For more advanced installation options, see the
[Extension reference](reference.md#install-an-extension).
- [Writing extensions](writing-extensions.md): Learn how to create your first
extension.
- [Extensions reference](reference.md): Deeply understand the extension format,
commands, and configuration.
- [Best practices](best-practices.md): Learn strategies for building great
extensions.
- [Extensions releasing](releasing.md): Learn how to share your extensions with
the world.
+107 -222
View File
@@ -1,134 +1,113 @@
# Extensions reference # Extension reference
This guide covers the `gemini extensions` commands and the structure of the This guide covers the `gemini extensions` commands and the structure of the
`gemini-extension.json` configuration file. `gemini-extension.json` configuration file.
## Extension management ## Manage extensions
We offer a suite of extension management tools using `gemini extensions` Use the `gemini extensions` command group to manage your extensions from the
commands. terminal.
Note that these commands (e.g. `gemini extensions install`) are not supported Note that commands like `gemini extensions install` are not supported within the
from within the CLI's **interactive mode**, although you can list installed CLI's interactive mode. However, you can use the `/extensions list` command to
extensions using the `/extensions list` slash command. view installed extensions. All management operations, including updates to slash
commands, take effect only after you restart the CLI session.
Note that all of these management operations (including updates to slash ### Install an extension
commands) will only be reflected in active CLI sessions on **restart**.
### Installing an extension Install an extension by providing its GitHub repository URL or a local file
path.
You can install an extension using `gemini extensions install` with either a Gemini CLI creates a copy of the extension during installation. You must run
GitHub URL or a local path. `gemini extensions update` to pull changes from the source. To install from
GitHub, you must have `git` installed on your machine.
Note that we create a copy of the installed extension, so you will need to run ```bash
`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] gemini extensions install <source> [--ref <ref>] [--auto-update] [--pre-release] [--consent]
``` ```
- `<source>`: The github URL or local path of the extension to install. - `<source>`: The GitHub URL or local path of the extension.
- `--ref`: The git ref to install from. - `--ref`: The git ref (branch, tag, or commit) to install.
- `--auto-update`: Enable auto-update for this extension. - `--auto-update`: Enable automatic updates for this extension.
- `--pre-release`: Enable pre-release versions for this extension. - `--pre-release`: Enable installation of pre-release versions.
- `--consent`: Acknowledge the security risks of installing an extension and - `--consent`: Acknowledge security risks and skip the confirmation prompt.
skip the confirmation prompt.
### Uninstalling an extension ### Uninstall an extension
To uninstall one or more extensions, run To uninstall one or more extensions, use the `uninstall` command:
`gemini extensions uninstall <name...>`:
``` ```bash
gemini extensions uninstall gemini-cli-security gemini-cli-another-extension gemini extensions uninstall <name...>
``` ```
### Disabling an extension ### Disable an extension
Extensions are, by default, enabled across all workspaces. You can disable an Extensions are enabled globally by default. You can disable an extension
extension entirely or for specific workspace. entirely or for a specific workspace.
``` ```bash
gemini extensions disable <name> [--scope <scope>] gemini extensions disable <name> [--scope <scope>]
``` ```
- `<name>`: The name of the extension to disable. - `<name>`: The name of the extension to disable.
- `--scope`: The scope to disable the extension in (`user` or `workspace`). - `--scope`: The scope to disable the extension in (`user` or `workspace`).
### Enabling an extension ### Enable an extension
You can enable extensions using `gemini extensions enable <name>`. You can also Re-enable a disabled extension using the `enable` command:
enable an extension for a specific workspace using
`gemini extensions enable <name> --scope=workspace` from within that workspace.
``` ```bash
gemini extensions enable <name> [--scope <scope>] gemini extensions enable <name> [--scope <scope>]
``` ```
- `<name>`: The name of the extension to enable. - `<name>`: The name of the extension to enable.
- `--scope`: The scope to enable the extension in (`user` or `workspace`). - `--scope`: The scope to enable the extension in (`user` or `workspace`).
### Updating an extension ### Update an extension
For extensions installed from a local path or a git repository, you can Update an extension to the version specified in its `gemini-extension.json`
explicitly update to the latest version (as reflected in the file.
`gemini-extension.json` `version` field) with `gemini extensions update <name>`.
You can update all extensions with:
```bash
gemini extensions update <name>
``` ```
To update all installed extensions at once:
```bash
gemini extensions update --all gemini extensions update --all
``` ```
### Create a boilerplate extension ### Create an extension from a template
We offer several example extensions `context`, `custom-commands`, Create a new extension directory using a built-in template.
`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 ```bash
your choosing, run:
```
gemini extensions new <path> [template] gemini extensions new <path> [template]
``` ```
- `<path>`: The path to create the extension in. - `<path>`: The directory to create.
- `[template]`: The boilerplate template to use. - `[template]`: The template to use (e.g., `mcp-server`, `context`,
`custom-commands`).
### Link a local extension ### Link a local extension
The `gemini extensions link` command will create a symbolic link from the Create a symbolic link between your development directory and the Gemini CLI
extension installation directory to the development path. extensions directory. This lets you test changes immediately without
reinstalling.
This is useful so you don't have to run `gemini extensions update` every time ```bash
you make changes you'd like to test.
```
gemini extensions link <path> gemini extensions link <path>
``` ```
- `<path>`: The path of the extension to link.
## Extension format ## Extension format
On startup, Gemini CLI looks for extensions in `<home>/.gemini/extensions` Gemini CLI loads extensions from `<home>/.gemini/extensions`. Each extension
must have a `gemini-extension.json` file in its root directory.
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` ### `gemini-extension.json`
The `gemini-extension.json` file contains the configuration for the extension. The manifest file defines the extension's behavior and configuration.
The file has the following structure:
```json ```json
{ {
@@ -145,56 +124,27 @@ The file has the following structure:
} }
``` ```
- `name`: The name of the extension. This is used to uniquely identify the - `name`: A unique identifier for the extension. Use lowercase letters, numbers,
extension and for conflict resolution when extension commands have the same and dashes. This name must match the extension's directory name.
name as user or project commands. The name should be lowercase or numbers and - `version`: The current version of the extension.
use dashes instead of underscores or spaces. This is how users will refer to - `description`: A short summary shown in the extension gallery.
your extension in the CLI. Note that we expect this name to match the - <a id="mcp-servers"></a>`mcpServers`: A map of Model Context Protocol (MCP)
extension directory name. servers. Extension servers follow the same format as standard
- `version`: The version of the extension. [CLI configuration](../get-started/configuration.md).
- `description`: A short description of the extension. This will be displayed on - `contextFileName`: The name of the context file (defaults to `GEMINI.md`). Can
[geminicli.com/extensions](https://geminicli.com/extensions). also be an array of strings to load multiple context files.
- `mcpServers`: A map of MCP servers to settings. The key is the name of the - `excludeTools`: An array of tools to block from the model. You can restrict
server, and the value is the server configuration. These servers will be specific arguments, such as `run_shell_command(rm -rf)`.
loaded on startup just like MCP servers settingsd in a - `themes`: An optional list of themes provided by the extension. See
[`settings.json` file](../get-started/configuration.md). If both an extension [Themes](../cli/themes.md) for more information.
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.
- `themes`: An array of custom themes provided by the extension. Each theme is
an object that defines the color scheme for the CLI UI. See the
[Themes guide](../cli/themes.md) for more details on the theme format.
When Gemini CLI starts, it loads all the extensions and merges their ### Extension settings
configurations. If there are any conflicts, the workspace configuration takes
precedence.
### Settings Extensions can define settings that users provide during installation, such as
API keys or URLs. These values are stored in a `.env` file within the extension
directory.
Extensions can define settings that the user will be prompted to provide upon To define settings, add a `settings` array to your manifest:
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 ```json
{ {
@@ -204,106 +154,54 @@ Each object in the array should have the following properties:
{ {
"name": "API Key", "name": "API Key",
"description": "Your API key for the service.", "description": "Your API key for the service.",
"envVar": "MY_API_KEY" "envVar": "MY_API_KEY",
"sensitive": true
} }
] ]
} }
``` ```
When a user installs this extension, they will be prompted to enter their API - `name`: The setting's display name.
key. The value will be saved to a `.env` file in the extension's directory - `description`: A clear explanation of the setting.
(e.g., `<home>/.gemini/extensions/my-api-extension/.env`). - `envVar`: The environment variable name where the value is stored.
- `sensitive`: If `true`, the value is stored in the system keychain and
obfuscated in the UI.
You can view a list of an extension's settings by running: To update an extension's settings:
```bash
gemini extensions config <name> [setting] [--scope <scope>]
``` ```
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 ### Custom commands
Extensions can provide [custom commands](../cli/custom-commands.md) by placing Provide [custom commands](../cli/custom-commands.md) by placing TOML files in a
TOML files in a `commands/` subdirectory within the extension directory. These `commands/` subdirectory. Gemini CLI uses the directory structure to determine
commands follow the same format as user and project custom commands and use the command name.
standard naming conventions.
**Example** For an extension named `gcp`:
An extension named `gcp` with the following structure: - `commands/deploy.toml` becomes `/deploy`
- `commands/gcs/sync.toml` becomes `/gcs:sync` (namespaced with a colon)
```
.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 ### Hooks
Extensions can provide [hooks](../hooks/index.md) to intercept and customize Intercept and customize CLI behavior using [hooks](../hooks/index.md). Define
Gemini CLI behavior at specific lifecycle events. Hooks provided by an extension hooks in a `hooks/hooks.json` file within your extension directory. Note that
must be defined in a `hooks/hooks.json` file within the extension directory. hooks are not defined in the `gemini-extension.json` manifest.
> [!IMPORTANT] Hooks are not defined directly in `gemini-extension.json`. The ### Agent skills
> CLI specifically looks for the `hooks/hooks.json` file.
### Agent Skills Bundle [agent skills](../cli/skills.md) to provide specialized workflows. Place
skill definitions in a `skills/` directory. For example,
Extensions can bundle [Agent Skills](../cli/skills.md) to provide specialized `skills/security-audit/SKILL.md` exposes a `security-audit` skill.
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.
### Sub-agents ### Sub-agents
> **Note: Sub-agents are currently an experimental feature.** > **Note:** Sub-agents are a preview feature currently under active development.
Extensions can provide [sub-agents](../core/subagents.md) that users can Provide [sub-agents](../core/subagents.md) that users can delegate tasks to. Add
delegate tasks to. agent definition files (`.md`) to an `agents/` directory in your extension root.
To bundle sub-agents with your extension, create an `agents/` directory in your
extension's root folder and add your agent definition files (`.md`) there.
**Example**
```
.gemini/extensions/my-extension/
├── gemini-extension.json
└── agents/
├── security-auditor.md
└── database-expert.md
```
Gemini CLI will automatically discover and load these agents when the extension
is installed and enabled.
### Themes ### Themes
@@ -351,30 +249,17 @@ the theme name in parentheses, e.g., `shades-of-green (my-green-extension)`.
### Conflict resolution ### Conflict resolution
Extension commands have the lowest precedence. When a conflict occurs with user Extension commands have the lowest precedence. If an extension command name
or project commands: conflicts with a user or project command, the extension command is prefixed with
the extension name (e.g., `/gcp.deploy`) using a dot separator.
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 ## Variables
Gemini CLI extensions allow variable substitution in both Gemini CLI supports variable substitution in `gemini-extension.json` and
`gemini-extension.json` and `hooks/hooks.json`. This can be useful if e.g., you `hooks/hooks.json`.
need the current directory to run an MCP server using an argument like
`"args": ["${extensionPath}${/}dist${/}server.js"]`.
**Supported variables:** | Variable | Description |
| :----------------- | :---------------------------------------------- |
| variable | description | | `${extensionPath}` | The absolute path to the extension's directory. |
| -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `${workspacePath}` | The absolute path to the current workspace. |
| `${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. | | `${/}` | The platform-specific path separator. |
| `${workspacePath}` | The fully-qualified path of the current workspace. |
| `${/} or ${pathSeparator}` | The path separator (differs per OS). |
+74 -103
View File
@@ -1,146 +1,117 @@
# Extension releasing # Release extensions
There are two primary ways of releasing extensions to users: Release Gemini CLI extensions to your users through a Git repository or GitHub
Releases.
- [Git repository](#releasing-through-a-git-repository) Git repository releases are the simplest approach and offer the most flexibility
- [Github Releases](#releasing-through-github-releases) for managing development branches. GitHub Releases are more efficient for
initial installations because they ship as single archives rather than requiring
a full `git clone`. Use GitHub Releases if you need to include platform-specific
binary files.
Git repository releases tend to be the simplest and most flexible approach, ## List your extension in the gallery
while GitHub releases can be more efficient on initial install as they are
shipped as single archives instead of requiring a git clone which downloads each
file individually. Github releases may also contain platform specific archives
if you need to ship platform specific binary files.
## Releasing through a git repository The [Gemini CLI extension gallery](https://geminicli.com/extensions/browse/)
automatically indexes public extensions to help users discover your work. You
don't need to submit an issue or email us to list your extension.
This is the most flexible and simple option. All you need to do is create a To have your extension automatically discovered and listed:
publicly accessible git repo (such as a public github repository) and then users
can install your extension using `gemini extensions install <your-repo-uri>`.
They can optionally depend on a specific ref (branch/tag/commit) using the
`--ref=<some-ref>` argument, this defaults to the default branch.
Whenever commits are pushed to the ref that a user depends on, they will be 1. **Use a public repository:** Ensure your extension is hosted in a public
prompted to update the extension. Note that this also allows for easy rollbacks, GitHub repository.
the HEAD commit is always treated as the latest version regardless of the actual 2. **Add the GitHub topic:** Add the `gemini-cli-extension` topic to your
version in the `gemini-extension.json` file. repository's **About** section. Our crawler uses this topic to find new
extensions.
3. **Place the manifest at the root:** Ensure your `gemini-extension.json` file
is in the absolute root of the repository or the release archive.
### Managing release channels using a git repository Our system crawls tagged repositories daily. Once you tag your repository, your
extension will appear in the gallery if it passes validation.
Users can depend on any ref from your git repo, such as a branch or tag, which ## Release through a Git repository
allows you to manage multiple release channels.
For instance, you can maintain a `stable` branch, which users can install this Releasing through Git is the most flexible option. Create a public Git
way `gemini extensions install <your-repo-uri> --ref=stable`. Or, you could make repository and provide the URL to your users. They can then install your
this the default by treating your default branch as your stable release branch, extension using `gemini extensions install <your-repo-uri>`.
and doing development in a different branch (for instance called `dev`). You can
maintain as many branches or tags as you like, providing maximum flexibility for
you and your users.
Note that these `ref` arguments can be tags, branches, or even specific commits, Users can optionally depend on a specific branch, tag, or commit using the
which allows users to depend on a specific version of your extension. It is up `--ref` argument. For example:
to you how you want to manage your tags and branches.
### Example releasing flow using a git repo ```bash
gemini extensions install <your-repo-uri> --ref=stable
```
While there are many options for how you want to manage releases using a git Whenever you push commits to the referenced branch, the CLI prompts users to
flow, we recommend treating your default branch as your "stable" release branch. update their installation. The `HEAD` commit is always treated as the latest
This means that the default behavior for version.
`gemini extensions install <your-repo-uri>` is to be on the stable release
branch.
Lets say you want to maintain three standard release channels, `stable`, ### Manage release channels
`preview`, and `dev`. You would do all your standard development in the `dev`
branch. When you are ready to do a preview release, you merge that branch into
your `preview` branch. When you are ready to promote your preview branch to
stable, you merge `preview` into your stable branch (which might be your default
branch or a different branch).
You can also cherry pick changes from one branch into another using You can use branches or tags to manage different release channels, such as
`git cherry-pick`, but do note that this will result in your branches having a `stable`, `preview`, or `dev`.
slightly divergent history from each other, unless you force push changes to
your branches on each release to restore the history to a clean slate (which may
not be possible for the default branch depending on your repository settings).
If you plan on doing cherry picks, you may want to avoid having your default
branch be the stable branch to avoid force-pushing to the default branch which
should generally be avoided.
## Releasing through GitHub releases We recommend using your default branch as the stable release channel. This
ensures that the default installation command always provides the most reliable
version of your extension. You can then use a `dev` branch for active
development and merge it into the default branch when you are ready for a
release.
Gemini CLI extensions can be distributed through ## Release through GitHub Releases
[GitHub Releases](https://docs.github.com/en/repositories/releasing-projects-on-github/about-releases).
This provides a faster and more reliable initial installation experience for
users, as it avoids the need to clone the repository.
Each release includes at least one archive file, which contains the full Distributing extensions through
contents of the repo at the tag that it was linked to. Releases may also include [GitHub Releases](https://docs.github.com/en/repositories/releasing-projects-on-github/about-releases)
[pre-built archives](#custom-pre-built-archives) if your extension requires some provides a faster installation experience by avoiding a repository clone.
build step or has platform specific binaries attached to it.
When checking for updates, gemini will just look for the "latest" release on Gemini CLI checks for updates by looking for the **Latest** release on GitHub.
github (you must mark it as such when creating the release), unless the user Users can also install specific versions using the `--ref` argument with a
installed a specific release by passing `--ref=<some-release-tag>`. release tag. Use the `--pre-release` flag to install the latest version even if
it isn't marked as **Latest**.
You may also install extensions with the `--pre-release` flag in order to get
the latest release regardless of whether it has been marked as "latest". This
allows you to test that your release works before actually pushing it to all
users.
### Custom pre-built archives ### Custom pre-built archives
Custom archives must be attached directly to the github release as assets and You can attach custom archives directly to your GitHub Release as assets. This
must be fully self-contained. This means they should include the entire is useful if your extension requires a build step or includes platform-specific
extension, see [archive structure](#archive-structure). binaries.
If your extension is platform-independent, you can provide a single generic Custom archives must be fully self-contained and follow the required
asset. In this case, there should be only one asset attached to the release. [archive structure](#archive-structure). If your extension is
platform-independent, provide a single generic asset.
Custom archives may also be used if you want to develop your extension within a #### Platform-specific archives
larger repository, you can build an archive which has a different layout from
the repo itself (for instance it might just be an archive of a subdirectory
containing the extension).
#### Platform specific archives To let Gemini CLI find the correct asset for a user's platform, use the
following naming convention:
To ensure Gemini CLI can automatically find the correct release asset for each 1. **Platform and architecture-specific:**
platform, you must follow this naming convention. The CLI will search for assets
in the following order:
1. **Platform and architecture-Specific:**
`{platform}.{arch}.{name}.{extension}` `{platform}.{arch}.{name}.{extension}`
2. **Platform-specific:** `{platform}.{name}.{extension}` 2. **Platform-specific:** `{platform}.{name}.{extension}`
3. **Generic:** If only one asset is provided, it will be used as a generic 3. **Generic:** A single asset will be used as a fallback if no specific match
fallback. is found.
- `{name}`: The name of your extension. Use these values for the placeholders:
- `{platform}`: The operating system. Supported values are:
- `darwin` (macOS) - `{name}`: Your extension name.
- `linux` - `{platform}`: Use `darwin` (macOS), `linux`, or `win32` (Windows).
- `win32` (Windows) - `{arch}`: Use `x64` or `arm64`.
- `{arch}`: The architecture. Supported values are: - `{extension}`: Use `.tar.gz` or `.zip`.
- `x64`
- `arm64`
- `{extension}`: The file extension of the archive (e.g., `.tar.gz` or `.zip`).
**Examples:** **Examples:**
- `darwin.arm64.my-tool.tar.gz` (specific to Apple Silicon Macs) - `darwin.arm64.my-tool.tar.gz` (specific to Apple Silicon Macs)
- `darwin.my-tool.tar.gz` (for all Macs) - `darwin.my-tool.tar.gz` (fallback for all Macs, e.g. Intel)
- `linux.x64.my-tool.tar.gz` - `linux.x64.my-tool.tar.gz`
- `win32.my-tool.zip` - `win32.my-tool.zip`
#### Archive structure #### Archive structure
Archives must be fully contained extensions and have all the standard Archives must be fully contained extensions. The `gemini-extension.json` file
requirements - specifically the `gemini-extension.json` file must be at the root must be at the root of the archive. The rest of the layout should match a
of the archive. standard extension structure.
The rest of the layout should look exactly the same as a typical extension, see
[extensions.md](./index.md).
#### Example GitHub Actions workflow #### Example GitHub Actions workflow
Here is an example of a GitHub Actions workflow that builds and releases a Use this example workflow to build and release your extension for multiple
Gemini CLI extension for multiple platforms: platforms:
```yaml ```yaml
name: Release Extension name: Release Extension
+86 -76
View File
@@ -1,18 +1,19 @@
# Getting started with Gemini CLI extensions # Build Gemini CLI extensions
This guide will walk you through creating your first Gemini CLI extension. Gemini CLI extensions let you expand the capabilities of Gemini CLI by adding
You'll learn how to set up a new extension, add a custom tool via an MCP server, custom tools, commands, and context. This guide walks you through creating your
create a custom command, and provide context to the model with a `GEMINI.md` first extension, from setting up a template to adding custom functionality and
file. linking it for local development.
## Prerequisites ## Prerequisites
Before you start, make sure you have the Gemini CLI installed and a basic Before you start, ensure you have the Gemini CLI installed and a basic
understanding of Node.js. understanding of Node.js.
## When to use what ## Extension features
Extensions offer a variety of ways to customize Gemini CLI. Extensions offer several ways to customize Gemini CLI. Use this table to decide
which features your extension needs.
| Feature | What it is | When to use it | Invoked by | | Feature | What it is | When to use it | Invoked by |
| :------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------- | | :------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------- |
@@ -25,8 +26,8 @@ Extensions offer a variety of ways to customize Gemini CLI.
## Step 1: Create a new extension ## Step 1: Create a new extension
The easiest way to start is by using one of the built-in templates. We'll use The easiest way to start is by using a built-in template. We'll use the
the `mcp-server` example as our foundation. `mcp-server` example as our foundation.
Run the following command to create a new directory called `my-first-extension` Run the following command to create a new directory called `my-first-extension`
with the template files: with the template files:
@@ -35,7 +36,7 @@ with the template files:
gemini extensions new my-first-extension mcp-server gemini extensions new my-first-extension mcp-server
``` ```
This will create a new directory with the following structure: This creates a directory with the following structure:
``` ```
my-first-extension/ my-first-extension/
@@ -46,12 +47,11 @@ my-first-extension/
## Step 2: Understand the extension files ## Step 2: Understand the extension files
Let's look at the key files in your new extension. Your new extension contains several key files that define its behavior.
### `gemini-extension.json` ### `gemini-extension.json`
This is the manifest file for your extension. It tells Gemini CLI how to load The manifest file tells Gemini CLI how to load and use your extension.
and use your extension.
```json ```json
{ {
@@ -69,17 +69,15 @@ and use your extension.
- `name`: The unique name for your extension. - `name`: The unique name for your extension.
- `version`: The version of your extension. - `version`: The version of your extension.
- `mcpServers`: This section defines one or more Model Context Protocol (MCP) - `mcpServers`: Defines Model Context Protocol (MCP) servers to add new tools.
servers. MCP servers are how you can add new tools for the model to use. - `command`, `args`, `cwd`: Specify how to start your server. The
- `command`, `args`, `cwd`: These fields specify how to start your server. `${extensionPath}` variable is replaced with the absolute path to your
Notice the use of the `${extensionPath}` variable, which Gemini CLI replaces extension's directory.
with the absolute path to your extension's installation directory. This
allows your extension to work regardless of where it's installed.
### `example.js` ### `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 uses the
server that uses the `@modelcontextprotocol/sdk`. `@modelcontextprotocol/sdk` to define tools.
```javascript ```javascript
/** /**
@@ -121,24 +119,49 @@ server.registerTool(
}, },
); );
// ... (prompt registration omitted for brevity)
const transport = new StdioServerTransport(); const transport = new StdioServerTransport();
await server.connect(transport); await server.connect(transport);
``` ```
This server defines a single tool called `fetch_posts` that fetches data from a
public API.
### `package.json` ### `package.json`
This is the standard configuration file for a Node.js project. It defines The standard configuration file for a Node.js project. It defines dependencies
dependencies and scripts. and scripts for your extension.
## Step 3: Link your extension ## Step 3: Add extension settings
Before you can use the extension, you need to link it to your Gemini CLI Some extensions need configuration, such as API keys or user preferences. Let's
installation for local development. add a setting for an API key.
1. Open `gemini-extension.json`.
2. Add a `settings` array to the configuration:
```json
{
"name": "mcp-server-example",
"version": "1.0.0",
"settings": [
{
"name": "API Key",
"description": "The API key for the service.",
"envVar": "MY_SERVICE_API_KEY",
"sensitive": true
}
],
"mcpServers": {
// ...
}
}
```
When a user installs this extension, Gemini CLI will prompt them to enter the
"API Key". The value will be stored securely in the system keychain (because
`sensitive` is true) and injected into the MCP server's process as the
`MY_SERVICE_API_KEY` environment variable.
## Step 4: Link your extension
Link your extension to your Gemini CLI installation for local development.
1. **Install dependencies:** 1. **Install dependencies:**
@@ -150,20 +173,19 @@ installation for local development.
2. **Link the extension:** 2. **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. Changes you make are reflected
will be reflected immediately without needing to reinstall. immediately.
```bash ```bash
gemini extensions link . gemini extensions link .
``` ```
Now, restart your Gemini CLI session. The new `fetch_posts` tool will be Restart your Gemini CLI session to use the new `fetch_posts` tool. Test it by
available. You can test it by asking: "fetch posts". asking: "fetch posts".
## Step 4: Add a custom command ## Step 5: Add a custom command
Custom commands provide a way to create shortcuts for complex prompts. Let's add Custom commands create shortcuts for complex prompts.
a command that searches for a pattern in your code.
1. Create a `commands` directory and a subdirectory for your command group: 1. Create a `commands` directory and a subdirectory for your command group:
@@ -182,18 +204,17 @@ a command that searches for a pattern in your code.
""" """
``` ```
This command, `/fs:grep-code`, will take an argument, run the `grep` shell This command, `/fs:grep-code`, takes an argument, runs the `grep` shell
command with it, and pipe the results into a prompt for summarization. command, and pipes the results into a prompt for summarization.
After saving the file, restart the Gemini CLI. You can now run After saving the file, restart Gemini CLI. Run `/fs:grep-code "some pattern"` to
`/fs:grep-code "some pattern"` to use your new command. use your new command.
## Step 5: Add a custom `GEMINI.md` ## Step 6: Add a custom `GEMINI.md`
You can provide persistent context to the model by adding a `GEMINI.md` file to Provide persistent context to the model by adding a `GEMINI.md` file to your
your extension. This is useful for giving the model instructions on how to extension. This is useful for setting behavior or providing essential tool
behave or information about your extension's tools. Note that you may not always information.
need this for extensions built to expose commands and prompts.
1. Create a file named `GEMINI.md` in the root of your extension directory: 1. Create a file named `GEMINI.md` in the root of your extension directory:
@@ -204,7 +225,7 @@ need this for extensions built to expose commands and prompts.
posts, use the `fetch_posts` tool. Be concise in your responses. posts, use the `fetch_posts` tool. Be concise in your responses.
``` ```
2. Update your `gemini-extension.json` to tell the CLI to load this file: 2. Update your `gemini-extension.json` to load this file:
```json ```json
{ {
@@ -221,14 +242,13 @@ need this for extensions built to expose commands and prompts.
} }
``` ```
Restart the CLI again. The model will now have the context from your `GEMINI.md` Restart Gemini CLI. The model now has the context from your `GEMINI.md` file in
file in every session where the extension is active. every session where the extension is active.
## (Optional) Step 6: Add an Agent Skill ## (Optional) Step 7: Add an Agent Skill
[Agent Skills](../cli/skills.md) let you bundle specialized expertise and [Agent Skills](../cli/skills.md) bundle specialized expertise and workflows.
procedural workflows. Unlike `GEMINI.md`, which provides persistent context, Skills are activated only when needed, which saves context tokens.
skills are activated only when needed, saving context tokens.
1. Create a `skills` directory and a subdirectory for your skill: 1. Create a `skills` directory and a subdirectory for your skill:
@@ -255,28 +275,18 @@ skills are activated only when needed, saving context tokens.
3. Suggest remediation steps for any findings. 3. Suggest remediation steps for any findings.
``` ```
Skills bundled with your extension are automatically discovered and can be Gemini CLI automatically discovers skills bundled with your extension. The model
activated by the model during a session when it identifies a relevant task. activates them when it identifies a relevant task.
## Step 7: Release your extension ## Step 8: Release your extension
Once you're happy with your extension, you can share it with others. The two When your extension is ready, share it with others via a Git repository or
primary ways of releasing extensions are via a Git repository or through GitHub GitHub Releases. Refer to the [Extension Releasing Guide](./releasing.md) for
Releases. Using a public Git repository is the simplest method. detailed instructions and learn how to list your extension in the gallery.
For detailed instructions on both methods, please refer to the ## Next steps
[Extension Releasing Guide](./releasing.md).
## Conclusion - [Extension reference](reference.md): Deeply understand the extension format,
commands, and configuration.
You've successfully created a Gemini CLI extension! You learned how to: - [Best practices](best-practices.md): Learn strategies for building great
extensions.
- Bootstrap a new extension from a template.
- Add custom tools with an MCP server.
- Create convenient custom commands.
- Provide persistent context to the model.
- Bundle specialized Agent Skills.
- Link your extension for local development.
From here, you can explore more advanced features and build powerful new
capabilities into the Gemini CLI.
+40 -97
View File
@@ -52,115 +52,42 @@
{ {
"label": "Features", "label": "Features",
"items": [ "items": [
{ "label": "Agent Skills", "slug": "docs/cli/skills" },
{ {
"label": "/about - About Gemini CLI", "label": "Authentication",
"link": "/docs/cli/commands/#about"
},
{
"label": "/auth - Authentication",
"slug": "docs/get-started/authentication" "slug": "docs/get-started/authentication"
}, },
{ "label": "/bug - Report a bug", "link": "/docs/cli/commands/#bug" }, { "label": "Checkpointing", "slug": "docs/cli/checkpointing" },
{ "label": "/chat - Chat history", "link": "/docs/cli/commands/#chat" },
{ "label": "/clear - Clear screen", "link": "/docs/cli/commands/#clear" },
{ {
"label": "/compress - Compress context", "label": "Extensions",
"link": "/docs/cli/commands/#compress"
},
{ "label": "/copy - Copy output", "link": "/docs/cli/commands/#copy" },
{
"label": "/directory - Manage workspace",
"link": "/docs/cli/commands/#directory-or-dir"
},
{
"label": "/docs - Open documentation",
"link": "/docs/cli/commands/#docs"
},
{
"label": "/editor - Select editor",
"link": "/docs/cli/commands/#editor"
},
{
"label": "/extensions - Manage extensions",
"slug": "docs/extensions/index" "slug": "docs/extensions/index"
}, },
{ "label": "/help - Show help", "link": "/docs/cli/commands/#help-or" }, { "label": "Headless mode", "slug": "docs/cli/headless" },
{ "label": "/hooks - Hooks", "slug": "docs/hooks" }, { "label": "Help", "link": "/docs/cli/commands/#help-or" },
{ "label": "/ide - IDE integration", "slug": "docs/ide-integration" }, { "label": "Hooks", "slug": "docs/hooks" },
{ "label": "IDE integration", "slug": "docs/ide-integration" },
{ "label": "MCP servers", "slug": "docs/tools/mcp-server" },
{ {
"label": "/init - Initialize context", "label": "Memory management",
"link": "/docs/cli/commands/#init"
},
{ "label": "/mcp - MCP servers", "slug": "docs/tools/mcp-server" },
{
"label": "/memory - Manage memory",
"link": "/docs/cli/commands/#memory" "link": "/docs/cli/commands/#memory"
}, },
{ "label": "/model - Model selection", "slug": "docs/cli/model" }, { "label": "Model routing", "slug": "docs/cli/model-routing" },
{ "label": "Model selection", "slug": "docs/cli/model" },
{ "label": "Plan mode (experimental)", "slug": "docs/cli/plan-mode" },
{ "label": "Rewind", "slug": "docs/cli/rewind" },
{ "label": "Sandboxing", "slug": "docs/cli/sandbox" },
{ "label": "Settings", "slug": "docs/cli/settings" },
{ {
"label": "/policies - Manage policies", "label": "Shell",
"link": "/docs/cli/commands/#policies"
},
{
"label": "/privacy - Privacy notice",
"link": "/docs/cli/commands/#privacy"
},
{
"label": "/quit - Exit CLI",
"link": "/docs/cli/commands/#quit-or-exit"
},
{ "label": "/restore - Restore files", "slug": "docs/cli/checkpointing" },
{
"label": "/resume - Resume session",
"link": "/docs/cli/commands/#resume"
},
{ "label": "/rewind - Rewind", "slug": "docs/cli/rewind" },
{ "label": "/settings - Settings", "slug": "docs/cli/settings" },
{
"label": "/setup-github - GitHub setup",
"link": "/docs/cli/commands/#setup-github"
},
{
"label": "/shells - Manage processes",
"link": "/docs/cli/commands/#shells-or-bashes" "link": "/docs/cli/commands/#shells-or-bashes"
}, },
{ "label": "/skills - Agent skills", "slug": "docs/cli/skills" },
{ {
"label": "/stats - Session statistics", "label": "Stats",
"link": "/docs/cli/commands/#stats" "link": "/docs/cli/commands/#stats"
}, },
{
"label": "/terminal-setup - Terminal keybindings",
"link": "/docs/cli/commands/#terminal-setup"
},
{ "label": "/theme - Themes", "slug": "docs/cli/themes" },
{ "label": "/tools - List tools", "link": "/docs/cli/commands/#tools" },
{ "label": "/vim - Vim mode", "link": "/docs/cli/commands/#vim" },
{
"label": "Activate skill (tool)",
"slug": "docs/tools/activate-skill"
},
{ "label": "Ask user (tool)", "slug": "docs/tools/ask-user" },
{ "label": "Checkpointing", "slug": "docs/cli/checkpointing" },
{ "label": "File system (tool)", "slug": "docs/tools/file-system" },
{ "label": "Headless mode", "slug": "docs/cli/headless" },
{
"label": "Internal documentation (tool)",
"slug": "docs/tools/internal-docs"
},
{ "label": "Memory (tool)", "slug": "docs/tools/memory" },
{ "label": "Model routing", "slug": "docs/cli/model-routing" },
{ "label": "Plan mode (experimental)", "slug": "docs/cli/plan-mode" },
{ "label": "Sandboxing", "slug": "docs/cli/sandbox" },
{ "label": "Shell (tool)", "slug": "docs/tools/shell" },
{ "label": "Telemetry", "slug": "docs/cli/telemetry" }, { "label": "Telemetry", "slug": "docs/cli/telemetry" },
{ "label": "Todo (tool)", "slug": "docs/tools/todos" },
{ "label": "Token caching", "slug": "docs/cli/token-caching" }, { "label": "Token caching", "slug": "docs/cli/token-caching" },
{ "label": "Web fetch (tool)", "slug": "docs/tools/web-fetch" }, { "label": "Tools", "link": "/docs/cli/commands/#tools" }
{ "label": "Web search (tool)", "slug": "docs/tools/web-search" }
] ]
}, },
{ {
@@ -186,14 +113,30 @@
{ {
"label": "Extensions", "label": "Extensions",
"items": [ "items": [
{ "label": "Introduction", "slug": "docs/extensions" },
{ {
"label": "Writing extensions", "label": "Overview",
"slug": "docs/extensions"
},
{
"label": "User guide: Install and manage",
"link": "/docs/extensions/#manage-extensions"
},
{
"label": "Developer guide: Build extensions",
"slug": "docs/extensions/writing-extensions" "slug": "docs/extensions/writing-extensions"
}, },
{ "label": "Reference", "slug": "docs/extensions/reference" }, {
{ "label": "Best practices", "slug": "docs/extensions/best-practices" }, "label": "Developer guide: Best practices",
{ "label": "Releasing", "slug": "docs/extensions/releasing" } "slug": "docs/extensions/best-practices"
},
{
"label": "Developer guide: Releasing",
"slug": "docs/extensions/releasing"
},
{
"label": "Developer guide: Reference",
"slug": "docs/extensions/reference"
}
] ]
}, },
{ {