diff --git a/docs/index.md b/docs/index.md
index c8b02d98cb..486193e823 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -31,7 +31,7 @@ Define what to build before building it. Rich templates, quality checklists, and
### Use any coding agent
-30 integrations — Copilot, Gemini, Codex, Windsurf, Claude, Forge, Kiro, and more. Switch freely between agents with a single command. No lock-in.
+31 integrations — Copilot, Gemini, Codex, Windsurf, Zed, Claude, Forge, Kiro, and more. Switch freely between agents with a single command. No lock-in.
Run `specify init` with your agent of choice and Spec Kit sets up the right command files, context rules, and directory structures automatically. If your agent isn't listed, the `generic` integration is an escape hatch for any tool.
diff --git a/docs/reference/integrations.md b/docs/reference/integrations.md
index ec6c894652..ab34bf5846 100644
--- a/docs/reference/integrations.md
+++ b/docs/reference/integrations.md
@@ -35,6 +35,7 @@ The Specify CLI supports a wide range of AI coding agents. When you run `specify
| [Tabnine CLI](https://docs.tabnine.com/main/getting-started/tabnine-cli) | `tabnine` | |
| [Trae](https://www.trae.ai/) | `trae` | Skills-based integration; skills are installed automatically |
| [Windsurf](https://windsurf.com/) | `windsurf` | |
+| [Zed](https://zed.dev/) | `zed` | Skills-based integration; installs skills into `.agents/skills` and invokes them as `/speckit-` |
| Generic | `generic` | Bring your own agent — use `--integration generic --integration-options="--commands-dir "` for AI coding agents not listed above |
## List Available Integrations
diff --git a/src/specify_cli/commands/init.py b/src/specify_cli/commands/init.py
index e5dc47e98c..e034affddf 100644
--- a/src/specify_cli/commands/init.py
+++ b/src/specify_cli/commands/init.py
@@ -726,7 +726,8 @@ def init(
cursor_agent_skill_mode = selected_ai == "cursor-agent" and (ai_skills or _is_skills_integration)
copilot_skill_mode = selected_ai == "copilot" and _is_skills_integration
devin_skill_mode = selected_ai == "devin"
- native_skill_mode = codex_skill_mode or claude_skill_mode or kimi_skill_mode or agy_skill_mode or trae_skill_mode or cursor_agent_skill_mode or copilot_skill_mode or devin_skill_mode
+ zed_skill_mode = selected_ai == "zed" and _is_skills_integration
+ native_skill_mode = codex_skill_mode or claude_skill_mode or kimi_skill_mode or agy_skill_mode or trae_skill_mode or cursor_agent_skill_mode or copilot_skill_mode or devin_skill_mode or zed_skill_mode
if codex_skill_mode and not ai_skills:
steps_lines.append(f"{step_num}. Start Codex in this project directory; spec-kit skills were installed to [cyan].agents/skills[/cyan]")
@@ -740,6 +741,9 @@ def init(
if devin_skill_mode:
steps_lines.append(f"{step_num}. Start Devin in this project directory; spec-kit skills were installed to [cyan].devin/skills[/cyan]")
step_num += 1
+ if zed_skill_mode:
+ steps_lines.append(f"{step_num}. Start Zed in this project directory; spec-kit skills were installed to [cyan].agents/skills[/cyan]")
+ step_num += 1
usage_label = "skills" if native_skill_mode else "slash commands"
def _display_cmd(name: str) -> str:
@@ -749,7 +753,7 @@ def _display_cmd(name: str) -> str:
return f"/speckit-{name}"
if kimi_skill_mode:
return f"/skill:speckit-{name}"
- if cursor_agent_skill_mode or copilot_skill_mode or devin_skill_mode:
+ if cursor_agent_skill_mode or copilot_skill_mode or devin_skill_mode or zed_skill_mode:
return f"/speckit-{name}"
return f"/speckit.{name}"
diff --git a/src/specify_cli/extensions.py b/src/specify_cli/extensions.py
index 5a595fbffa..a11eedcbdb 100644
--- a/src/specify_cli/extensions.py
+++ b/src/specify_cli/extensions.py
@@ -2413,6 +2413,7 @@ def _render_hook_invocation(self, command: Any) -> str:
claude_skill_mode = selected_ai == "claude" and bool(init_options.get("ai_skills"))
kimi_skill_mode = selected_ai == "kimi"
cursor_skill_mode = selected_ai == "cursor-agent" and bool(init_options.get("ai_skills"))
+ zed_skill_mode = selected_ai == "zed" and bool(init_options.get("ai_skills"))
skill_name = self._skill_name_from_command(command_id)
if codex_skill_mode and skill_name:
@@ -2423,6 +2424,8 @@ def _render_hook_invocation(self, command: Any) -> str:
return f"/skill:{skill_name}"
if cursor_skill_mode and skill_name:
return f"/{skill_name}"
+ if zed_skill_mode and skill_name:
+ return f"/{skill_name}"
return f"/{command_id}"
diff --git a/src/specify_cli/integrations/__init__.py b/src/specify_cli/integrations/__init__.py
index ad1440d074..c3d7499ff3 100644
--- a/src/specify_cli/integrations/__init__.py
+++ b/src/specify_cli/integrations/__init__.py
@@ -78,6 +78,7 @@ def _register_builtins() -> None:
from .trae import TraeIntegration
from .vibe import VibeIntegration
from .windsurf import WindsurfIntegration
+ from .zed import ZedIntegration
# -- Registration (alphabetical) --------------------------------------
_register(AgyIntegration())
@@ -111,6 +112,7 @@ def _register_builtins() -> None:
_register(TraeIntegration())
_register(VibeIntegration())
_register(WindsurfIntegration())
+ _register(ZedIntegration())
_register_builtins()
diff --git a/src/specify_cli/integrations/zed/__init__.py b/src/specify_cli/integrations/zed/__init__.py
new file mode 100644
index 0000000000..004cfc57f6
--- /dev/null
+++ b/src/specify_cli/integrations/zed/__init__.py
@@ -0,0 +1,41 @@
+"""Zed editor integration. — skills-based agent.
+
+Zed uses the ``.agents/skills/speckit-/SKILL.md`` layout so Spec Kit
+commands are exposed as project-local skills that can be invoked from Zed's
+slash-command menu.
+"""
+
+from __future__ import annotations
+
+from ..base import IntegrationOption, SkillsIntegration
+
+
+class ZedIntegration(SkillsIntegration):
+ """Integration for Zed editor skills."""
+
+ key = "zed"
+ config = {
+ "name": "Zed",
+ "folder": ".agents/",
+ "commands_subdir": "skills",
+ "install_url": None,
+ "requires_cli": False,
+ }
+ registrar_config = {
+ "dir": ".agents/skills",
+ "format": "markdown",
+ "args": "$ARGUMENTS",
+ "extension": "/SKILL.md",
+ }
+ context_file = "AGENTS.md"
+
+ @classmethod
+ def options(cls) -> list[IntegrationOption]:
+ return [
+ IntegrationOption(
+ "--skills",
+ is_flag=True,
+ default=True,
+ help="Install as agent skills (default for Zed)",
+ ),
+ ]
diff --git a/tests/integrations/test_integration_subcommand.py b/tests/integrations/test_integration_subcommand.py
index f40adb7ae9..42a088f541 100644
--- a/tests/integrations/test_integration_subcommand.py
+++ b/tests/integrations/test_integration_subcommand.py
@@ -93,6 +93,7 @@ def test_list_shows_available_integrations(self, tmp_path):
# Should show multiple integrations
assert "claude" in result.output
assert "gemini" in result.output
+ assert "zed" in result.output
def test_list_shows_multi_install_safe_status(self, tmp_path):
project = _init_project(tmp_path, "claude")
diff --git a/tests/integrations/test_integration_zed.py b/tests/integrations/test_integration_zed.py
new file mode 100644
index 0000000000..f3256c7fc7
--- /dev/null
+++ b/tests/integrations/test_integration_zed.py
@@ -0,0 +1,51 @@
+"""Tests for ZedIntegration."""
+
+import json
+
+from specify_cli.integrations import get_integration
+
+from .test_integration_base_skills import SkillsIntegrationTests
+
+
+class TestZedIntegration(SkillsIntegrationTests):
+ KEY = "zed"
+ FOLDER = ".agents/"
+ COMMANDS_SUBDIR = "skills"
+ REGISTRAR_DIR = ".agents/skills"
+ CONTEXT_FILE = "AGENTS.md"
+
+ def test_requires_cli_is_false(self):
+ """Zed is IDE-based; requires_cli must remain False."""
+ i = get_integration(self.KEY)
+ assert i is not None
+ assert i.config is not None
+ assert i.config["requires_cli"] is False
+
+
+class TestZedHookInvocations:
+ """Zed hook messages should reference slash-invokable skills."""
+
+ def test_hooks_render_skill_invocation(self, tmp_path):
+ from specify_cli.extensions import HookExecutor
+
+ project = tmp_path / "zed-hooks"
+ project.mkdir()
+ init_options = project / ".specify" / "init-options.json"
+ init_options.parent.mkdir(parents=True, exist_ok=True)
+ init_options.write_text(json.dumps({"ai": "zed", "ai_skills": True}))
+
+ hook_executor = HookExecutor(project)
+ message = hook_executor.format_hook_message(
+ "before_plan",
+ [
+ {
+ "extension": "test-ext",
+ "command": "speckit.plan",
+ "optional": False,
+ }
+ ],
+ )
+
+ assert "Executing: `/speckit-plan`" in message
+ assert "EXECUTE_COMMAND: speckit.plan" in message
+ assert "EXECUTE_COMMAND_INVOCATION: /speckit-plan" in message
diff --git a/tests/integrations/test_registry.py b/tests/integrations/test_registry.py
index 1b36501056..b6f7439254 100644
--- a/tests/integrations/test_registry.py
+++ b/tests/integrations/test_registry.py
@@ -27,7 +27,7 @@
# Stage 4 — TOML integrations
"gemini", "tabnine",
# Stage 5 — skills, generic & option-driven integrations
- "codex", "kimi", "agy", "generic",
+ "codex", "kimi", "agy", "zed", "generic",
]