creating-zed-extensions
About
This skill helps developers create Zed extensions for adding custom slash commands, language support, themes, or MCP servers. It provides the Rust/WASM structure, key APIs like `run_slash_command`, and the development workflow for compiled extensions. Use it when you need to extend Zed's functionality beyond simple rules or one-off scripts.
Documentation
Creating Zed Extensions
Overview
Zed extensions are Rust programs compiled to WebAssembly that can provide slash commands, language support, themes, grammars, and MCP servers. Extensions implement the zed::Extension trait and are distributed via Zed's extension registry.
When to Use
Create a Zed extension when:
- Adding custom slash commands to the Assistant (
/deploy,/analyze,/fetch-docs) - Providing language support (syntax highlighting, LSP, formatting)
- Creating custom color themes
- Integrating external tools via slash commands
- Providing MCP server integrations
Don't create for:
- Simple rules or instructions (use
.rulesfiles) - One-time scripts (use terminal)
- Project-specific configuration (use
.zed/settings.json)
Quick Reference
Extension Structure
my-extension/
├── Cargo.toml # Rust manifest
├── extension.toml # Extension metadata
└── src/
└── lib.rs # Extension implementation
Minimal Slash Command Extension
# extension.toml
id = "my-commands"
name = "My Commands"
version = "0.1.0"
authors = ["Your Name"]
repository = "https://github.com/username/my-commands"
license = "MIT"
[slash_commands.echo]
description = "echoes the provided input"
requires_argument = true
[slash_commands.greet]
description = "greets the user"
requires_argument = false
// src/lib.rs
use zed_extension_api::{self as zed, Result, SlashCommand, SlashCommandOutput};
struct MyExtension;
impl zed::Extension for MyExtension {
fn run_slash_command(
&self,
command: SlashCommand,
args: Vec<String>,
_worktree: Option<&zed::Worktree>,
) -> Result<SlashCommandOutput> {
match command.name.as_str() {
"echo" => {
if args.is_empty() {
return Err("echo requires an argument".to_string());
}
Ok(SlashCommandOutput {
text: args.join(" "),
sections: vec![],
})
}
"greet" => {
Ok(SlashCommandOutput {
text: "Hello! How can I help you today?".to_string(),
sections: vec![],
})
}
_ => Err(format!("Unknown command: {}", command.name)),
}
}
}
zed::register_extension!(MyExtension);
# Cargo.toml
[package]
name = "my-extension"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib"]
[dependencies]
zed_extension_api = "0.1.0"
Implementation
Complete Example: Documentation Fetcher
// src/lib.rs
use zed_extension_api::{self as zed, Result, SlashCommand, SlashCommandOutput, SlashCommandOutputSection};
use std::process::Command;
struct DocsExtension;
impl zed::Extension for DocsExtension {
fn run_slash_command(
&self,
command: SlashCommand,
args: Vec<String>,
worktree: Option<&zed::Worktree>,
) -> Result<SlashCommandOutput> {
match command.name.as_str() {
"docs" => self.fetch_docs(args, worktree),
"api" => self.fetch_api_reference(args),
_ => Err(format!("Unknown command: {}", command.name)),
}
}
fn complete_slash_command_argument(
&self,
command: SlashCommand,
_args: Vec<String>,
) -> Result<Vec<zed::SlashCommandArgumentCompletion>> {
match command.name.as_str() {
"docs" => Ok(vec![
zed::SlashCommandArgumentCompletion {
label: "rust".to_string(),
new_text: "rust".to_string(),
run_command: true,
},
zed::SlashCommandArgumentCompletion {
label: "typescript".to_string(),
new_text: "typescript".to_string(),
run_command: true,
},
zed::SlashCommandArgumentCompletion {
label: "python".to_string(),
new_text: "python".to_string(),
run_command: true,
},
]),
_ => Ok(vec![]),
}
}
}
impl DocsExtension {
fn fetch_docs(
&self,
args: Vec<String>,
worktree: Option<&zed::Worktree>,
) -> Result<SlashCommandOutput> {
if args.is_empty() {
return Err("docs requires a topic (e.g., /docs rust)".to_string());
}
let topic = args.join(" ");
let docs_url = format!("https://docs.rs/{}", topic);
// Use worktree context if available
let context = if let Some(wt) = worktree {
format!("\nProject: {}", wt.root_path())
} else {
String::new()
};
let output_text = format!(
"Documentation for: {}\nURL: {}{}\n\nFetching latest docs...",
topic, docs_url, context
);
Ok(SlashCommandOutput {
text: output_text.clone(),
sections: vec![
SlashCommandOutputSection {
range: (0..output_text.len()),
label: format!("Docs: {}", topic),
},
],
})
}
fn fetch_api_reference(&self, args: Vec<String>) -> Result<SlashCommandOutput> {
if args.is_empty() {
return Err("api requires a library name".to_string());
}
let library = &args[0];
// Execute external command to fetch API docs
let output = Command::new("curl")
.args(&["-s", &format!("https://api.github.com/repos/{}/readme", library)])
.output()
.map_err(|e| format!("Failed to execute curl: {}", e))?;
if !output.status.success() {
return Err("Failed to fetch API documentation".to_string());
}
let response = String::from_utf8_lossy(&output.stdout);
Ok(SlashCommandOutput {
text: format!("API Reference for {}\n\n{}", library, response),
sections: vec![],
})
}
}
zed::register_extension!(DocsExtension);
Extension Manifest with All Fields
# extension.toml
id = "docs-fetcher"
name = "Documentation Fetcher"
description = "Fetch documentation and API references via slash commands"
version = "1.0.0"
authors = ["Developer Name <[email protected]>"]
repository = "https://github.com/username/docs-fetcher"
license = "MIT"
[slash_commands.docs]
description = "fetch documentation for a topic"
requires_argument = true
[slash_commands.api]
description = "fetch API reference for a library"
requires_argument = true
[slash_commands.help]
description = "show available documentation commands"
requires_argument = false
Development Workflow
1. Setup
# Install Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# Add WASM target
rustup target add wasm32-wasip1
# Create extension directory
mkdir -p ~/.local/share/zed/extensions/my-extension
cd ~/.local/share/zed/extensions/my-extension
2. Build
# Compile to WASM
cargo build --release --target wasm32-wasip1
# WASM output location
# target/wasm32-wasip1/release/my_extension.wasm
3. Test Locally
# Zed automatically loads extensions from:
# macOS: ~/Library/Application Support/Zed/extensions/
# Linux: ~/.local/share/zed/extensions/
# Copy extension files
cp extension.toml ~/Library/Application\ Support/Zed/extensions/my-extension/
cp target/wasm32-wasip1/release/my_extension.wasm ~/Library/Application\ Support/Zed/extensions/my-extension/extension.wasm
# Restart Zed to load extension
4. Publish
# Extensions published via PR to zed-industries/extensions
# https://github.com/zed-industries/extensions
# Fork the repository
git clone https://github.com/zed-industries/extensions
cd extensions
# Add your extension
mkdir extensions/my-extension
cp -r ~/path/to/my-extension/* extensions/my-extension/
# Create PR with extension metadata
git checkout -b add-my-extension
git add extensions/my-extension
git commit -m "Add my-extension: Custom slash commands"
git push origin add-my-extension
License Requirements
Required licenses (as of October 1st, 2025):
- MIT
- Apache-2.0
- BSD-3-Clause
- GPL-3.0
Extensions with other licenses will be rejected during review.
Slash Command API Reference
Types
// Command input
struct SlashCommand {
name: String,
// Additional metadata
}
// Command output
struct SlashCommandOutput {
text: String,
sections: Vec<SlashCommandOutputSection>,
}
struct SlashCommandOutputSection {
range: (usize, usize), // Character range in text
label: String, // Section label for UI
}
// Argument completion
struct SlashCommandArgumentCompletion {
label: String, // Display in completion menu
new_text: String, // Insert when selected
run_command: bool, // Execute immediately after selection
}
Methods
trait Extension {
// Required for slash commands
fn run_slash_command(
&self,
command: SlashCommand,
args: Vec<String>,
worktree: Option<&Worktree>,
) -> Result<SlashCommandOutput, String>;
// Optional: Argument autocompletion
fn complete_slash_command_argument(
&self,
command: SlashCommand,
args: Vec<String>,
) -> Result<Vec<SlashCommandArgumentCompletion>, String> {
Ok(vec![])
}
}
Common Mistakes
| Mistake | Why It Fails | Fix |
|---|---|---|
| Wrong crate type | WASM compilation fails | Use crate-type = ["cdylib"] in Cargo.toml |
| Missing error handling | Extension crashes | Return Err(String) for failures |
| Not validating args | Silent failures | Check args.is_empty() for required args |
| Hardcoded paths | Extension not portable | Use relative paths or worktree context |
| Missing default case | Unhandled commands crash | Add _ => Err(...) in match |
| Unlicensed extension | Rejected by registry | Include approved license in extension.toml |
| Blocking operations | Freezes Zed UI | Use async or spawn threads for long operations |
Advanced Features
Using Worktree Context
fn run_slash_command(
&self,
command: SlashCommand,
args: Vec<String>,
worktree: Option<&zed::Worktree>,
) -> Result<SlashCommandOutput> {
if let Some(wt) = worktree {
let project_root = wt.root_path();
let config_path = format!("{}/config.json", project_root);
// Read project-specific config
let config = std::fs::read_to_string(config_path)
.map_err(|e| format!("Failed to read config: {}", e))?;
// Use config in command logic
}
// Continue command execution
}
Output Sections for Structured Results
let output_text = format!(
"# Results\n\n## Section 1\nContent here\n\n## Section 2\nMore content"
);
Ok(SlashCommandOutput {
text: output_text.clone(),
sections: vec![
SlashCommandOutputSection {
range: (0..12), // "# Results"
label: "Header".to_string(),
},
SlashCommandOutputSection {
range: (14..40), // "## Section 1\nContent here"
label: "Section 1".to_string(),
},
SlashCommandOutputSection {
range: (42..output_text.len()),
label: "Section 2".to_string(),
},
],
})
Real-World Impact
Productivity: Custom /deploy command deploys directly from Assistant panel
Documentation: /docs rust Vec fetches Rust Vec documentation without leaving editor
Integration: /gh issue 123 fetches GitHub issue details inline
Workflow: /analyze-deps shows dependency tree and suggests updates
Schema Reference: packages/converters/schemas/zed-extension.schema.json
Documentation: https://zed.dev/docs/extensions/developing-extensions
Example Extension: https://github.com/zed-industries/zed/tree/main/extensions/slash-commands-example
Quick Install
/plugin add https://github.com/pr-pm/prpm/tree/main/creating-zed-extensionsCopy and paste this command in Claude Code to install this skill
GitHub 仓库
Related Skills
sglang
MetaSGLang is a high-performance LLM serving framework that specializes in fast, structured generation for JSON, regex, and agentic workflows using its RadixAttention prefix caching. It delivers significantly faster inference, especially for tasks with repeated prefixes, making it ideal for complex, structured outputs and multi-turn conversations. Choose SGLang over alternatives like vLLM when you need constrained decoding or are building applications with extensive prefix sharing.
evaluating-llms-harness
TestingThis Claude Skill runs the lm-evaluation-harness to benchmark LLMs across 60+ standardized academic tasks like MMLU and GSM8K. It's designed for developers to compare model quality, track training progress, or report academic results. The tool supports various backends including HuggingFace and vLLM models.
langchain
MetaLangChain is a framework for building LLM applications using agents, chains, and RAG pipelines. It supports multiple LLM providers, offers 500+ integrations, and includes features like tool calling and memory management. Use it for rapid prototyping and deploying production systems like chatbots, autonomous agents, and question-answering services.
Algorithmic Art Generation
MetaThis skill helps developers create algorithmic art using p5.js, focusing on generative art, computational aesthetics, and interactive visualizations. It automatically activates for topics like "generative art" or "p5.js visualization" and guides you through creating unique algorithms with features like seeded randomness, flow fields, and particle systems. Use it when you need to build reproducible, code-driven artistic patterns.
