From 53afafe490207e10c0b5fa67a3ccc3435556c66f Mon Sep 17 00:00:00 2001 From: JoshAS Date: Sun, 21 Sep 2025 00:13:37 +0100 Subject: [PATCH 1/5] Add Zed agent support across CLI and scripts Introduces Zed as a supported agent in release packaging, context update scripts (bash and PowerShell), and CLI options. Updates documentation, agent lists, and workflow logic to handle Zed-specific rules and templates. --- .github/workflows/release.yml | 4 ++ .../scripts/create-release-packages.sh | 61 ++++++++++++++++++- scripts/bash/update-agent-context.sh | 17 ++++-- scripts/powershell/update-agent-context.ps1 | 11 ++-- src/specify_cli/__init__.py | 6 +- 5 files changed, 87 insertions(+), 12 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 675ee3b67..1c481c48d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -102,6 +102,8 @@ jobs: - spec-kit-template-windsurf-ps-${{ steps.get_tag.outputs.new_version }}.zip - spec-kit-template-codex-sh-${{ steps.get_tag.outputs.new_version }}.zip - spec-kit-template-codex-ps-${{ steps.get_tag.outputs.new_version }}.zip + - spec-kit-template-zed-sh-${{ steps.get_tag.outputs.new_version }}.zip + - spec-kit-template-zed-ps-${{ steps.get_tag.outputs.new_version }}.zip EOF echo "Generated release notes:" @@ -130,6 +132,8 @@ jobs: spec-kit-template-windsurf-ps-${{ steps.get_tag.outputs.new_version }}.zip \ spec-kit-template-codex-sh-${{ steps.get_tag.outputs.new_version }}.zip \ spec-kit-template-codex-ps-${{ steps.get_tag.outputs.new_version }}.zip \ + spec-kit-template-zed-sh-${{ steps.get_tag.outputs.new_version }}.zip \ + spec-kit-template-zed-ps-${{ steps.get_tag.outputs.new_version }}.zip \ --title "Spec Kit Templates - $VERSION_NO_V" \ --notes-file release_notes.md env: diff --git a/.github/workflows/scripts/create-release-packages.sh b/.github/workflows/scripts/create-release-packages.sh index 05b5cce3f..8286a2eca 100644 --- a/.github/workflows/scripts/create-release-packages.sh +++ b/.github/workflows/scripts/create-release-packages.sh @@ -6,7 +6,7 @@ set -euo pipefail # Usage: .github/workflows/scripts/create-release-packages.sh # Version argument should include leading 'v'. # Optionally set AGENTS and/or SCRIPTS env vars to limit what gets built. -# AGENTS : space or comma separated subset of: claude gemini copilot cursor qwen opencode windsurf codex (default: all) +# AGENTS : space or comma separated subset of: claude gemini copilot cursor qwen opencode windsurf codex zed (default: all) # SCRIPTS : space or comma separated subset of: sh ps (default: both) # Examples: # AGENTS=claude SCRIPTS=sh $0 v0.2.0 @@ -37,6 +37,60 @@ rewrite_paths() { generate_commands() { local agent=$1 ext=$2 arg_format=$3 output_dir=$4 script_variant=$5 mkdir -p "$output_dir" + + if [[ "$agent" == "zed" ]]; then + local rules_file="$output_dir/.rules" + echo "# Zed rules file" > "$rules_file" + echo "# Whenever called with / context, follow these rules" >> "$rules_file" + echo "" >> "$rules_file" + + # Iterate over all templates and append rules + for template in templates/commands/*.md; do + [[ -f "$template" ]] || continue + local name description script_command body + name=$(basename "$template" .md) + + # Normalize line endings + file_content=$(tr -d '\r' < "$template") + + # Extract description and script command for this variant + description=$(printf '%s\n' "$file_content" | awk '/^description:/ {sub(/^description:[[:space:]]*/, ""); print; exit}') + script_command=$(printf '%s\n' "$file_content" | awk -v sv="$script_variant" '/^[[:space:]]*'"$script_variant"':[[:space:]]*/ {sub(/^[[:space:]]*'"$script_variant"':[[:space:]]*/, ""); print; exit}') + + if [[ -z $script_command ]]; then + echo "Warning: no script command found for $script_variant in $template" >&2 + script_command="(Missing script command for $script_variant)" + fi + + # Replace placeholders + body=$(printf '%s\n' "$file_content" | sed "s|{SCRIPT}|${script_command}|g") + + # Remove scripts section from frontmatter + body=$(printf '%s\n' "$body" | awk ' + /^---$/ { print; if (++dash_count == 1) in_frontmatter=1; else in_frontmatter=0; next } + in_frontmatter && /^scripts:$/ { skip_scripts=1; next } + in_frontmatter && /^[a-zA-Z].*:/ && skip_scripts { skip_scripts=0 } + in_frontmatter && skip_scripts && /^[[:space:]]/ { next } + { print } + ') + + # Apply other substitutions + body=$(printf '%s\n' "$body" | sed "s/{ARGS}/$arg_format/g" | sed "s/__AGENT__/$agent/g" | rewrite_paths) + + # Append this template’s rule to the .rules file + { + echo "---" + echo "description: $description" + echo "---" + echo "$body" + echo "" + } >> "$rules_file" + done + + return 0 + fi + + # Normal agent behavior for all other agents for template in templates/commands/*.md; do [[ -f "$template" ]] || continue local name description script_command body @@ -160,13 +214,16 @@ build_variant() { codex) mkdir -p "$base_dir/.codex/commands" generate_commands codex md "\$ARGUMENTS" "$base_dir/.codex/commands" "$script" ;; + zed) + mkdir -p "$base_dir/.zed/commands" + generate_commands zed md "\$ARGUMENTS" "$base_dir/" "$script" ;; esac ( cd "$base_dir" && zip -r "../spec-kit-template-${agent}-${script}-${NEW_VERSION}.zip" . ) echo "Created spec-kit-template-${agent}-${script}-${NEW_VERSION}.zip" } # Determine agent list -ALL_AGENTS=(claude gemini copilot cursor qwen opencode windsurf codex) +ALL_AGENTS=(claude gemini copilot cursor qwen opencode windsurf codex zed) ALL_SCRIPTS=(sh ps) diff --git a/scripts/bash/update-agent-context.sh b/scripts/bash/update-agent-context.sh index 4f9e6e31c..24b4580e1 100644 --- a/scripts/bash/update-agent-context.sh +++ b/scripts/bash/update-agent-context.sh @@ -30,12 +30,12 @@ # # 5. Multi-Agent Support # - Handles agent-specific file paths and naming conventions -# - Supports: Claude, Gemini, Copilot, Cursor, Qwen, opencode, Codex, Windsurf +# - Supports: Claude, Gemini, Copilot, Cursor, Qwen, opencode, Codex, Windsurf, Zed # - Can update single agents or all existing agent files # - Creates default Claude file if no agent files exist # # Usage: ./update-agent-context.sh [agent_type] -# Agent types: claude|gemini|copilot|cursor|qwen|opencode|codex|windsurf +# Agent types: claude|gemini|copilot|cursor|qwen|opencode|codex|windsurf|zed # Leave empty to update all existing agent files set -e @@ -62,6 +62,7 @@ CURSOR_FILE="$REPO_ROOT/.cursor/rules/specify-rules.mdc" QWEN_FILE="$REPO_ROOT/QWEN.md" AGENTS_FILE="$REPO_ROOT/AGENTS.md" WINDSURF_FILE="$REPO_ROOT/.windsurf/rules/specify-rules.md" +ZED_FILE="$REPO_ROOT/.rules" # Template file TEMPLATE_FILE="$REPO_ROOT/.specify/templates/agent-file-template.md" @@ -559,9 +560,12 @@ update_specific_agent() { windsurf) update_agent_file "$WINDSURF_FILE" "Windsurf" ;; + zed) + update_agent_file "$ZED_FILE" "Zed" + ;; *) log_error "Unknown agent type '$agent_type'" - log_error "Expected: claude|gemini|copilot|cursor|qwen|opencode|codex|windsurf" + log_error "Expected: claude|gemini|copilot|cursor|qwen|opencode|codex|windsurf|zed" exit 1 ;; esac @@ -606,6 +610,11 @@ update_all_existing_agents() { found_agent=true fi + if [[ -f "$ZED_FILE" ]]; then + update_agent_file "$ZED_FILE" "Zed" + found_agent=true + fi + # If no agent files exist, create a default Claude file if [[ "$found_agent" == false ]]; then log_info "No existing agent files found, creating default Claude file..." @@ -629,7 +638,7 @@ print_summary() { fi echo - log_info "Usage: $0 [claude|gemini|copilot|cursor|qwen|opencode|codex|windsurf]" + log_info "Usage: $0 [claude|gemini|copilot|cursor|qwen|opencode|codex|windsurf|zed]" } #============================================================================== diff --git a/scripts/powershell/update-agent-context.ps1 b/scripts/powershell/update-agent-context.ps1 index 1e35b25a6..f3b606eb3 100644 --- a/scripts/powershell/update-agent-context.ps1 +++ b/scripts/powershell/update-agent-context.ps1 @@ -16,6 +16,7 @@ $cursorFile = Join-Path $repoRoot '.cursor/rules/specify-rules.mdc' $qwenFile = Join-Path $repoRoot 'QWEN.md' $agentsFile = Join-Path $repoRoot 'AGENTS.md' $windsurfFile = Join-Path $repoRoot '.windsurf/rules/specify-rules.md' +$zedFile = Join-Path $repoRoot '.rules' Write-Output "=== Updating agent context files for feature $currentBranch ===" @@ -78,6 +79,7 @@ switch ($AgentType) { 'opencode' { Update-AgentFile $agentsFile 'opencode' } 'windsurf' { Update-AgentFile $windsurfFile 'Windsurf' } 'codex' { Update-AgentFile $agentsFile 'Codex CLI' } + 'zed' { Update-AgentFile $zedFile 'Zed' } '' { foreach ($pair in @( @{file=$claudeFile; name='Claude Code'}, @@ -87,16 +89,17 @@ switch ($AgentType) { @{file=$qwenFile; name='Qwen Code'}, @{file=$agentsFile; name='opencode'}, @{file=$windsurfFile; name='Windsurf'}, - @{file=$agentsFile; name='Codex CLI'} + @{file=$agentsFile; name='Codex CLI'}, + @{file=$zedFile; name='Zed'} )) { if (Test-Path $pair.file) { Update-AgentFile $pair.file $pair.name } } - if (-not (Test-Path $claudeFile) -and -not (Test-Path $geminiFile) -and -not (Test-Path $copilotFile) -and -not (Test-Path $cursorFile) -and -not (Test-Path $qwenFile) -and -not (Test-Path $agentsFile) -and -not (Test-Path $windsurfFile)) { + if (-not (Test-Path $claudeFile) -and -not (Test-Path $geminiFile) -and -not (Test-Path $copilotFile) -and -not (Test-Path $cursorFile) -and -not (Test-Path $qwenFile) -and -not (Test-Path $agentsFile) -and -not (Test-Path $windsurfFile) -and -not (Test-Path $zedFile)) { Write-Output 'No agent context files found. Creating Claude Code context file by default.' Update-AgentFile $claudeFile 'Claude Code' } } - Default { Write-Error "ERROR: Unknown agent type '$AgentType'. Use: claude, gemini, copilot, cursor, qwen, opencode, windsurf, codex or leave empty for all."; exit 1 } + Default { Write-Error "ERROR: Unknown agent type '$AgentType'. Use: claude, gemini, copilot, cursor, qwen, opencode, windsurf, codex, zed or leave empty for all."; exit 1 } } Write-Output '' @@ -106,4 +109,4 @@ if ($newFramework) { Write-Output "- Added framework: $newFramework" } if ($newDb -and $newDb -ne 'N/A') { Write-Output "- Added database: $newDb" } Write-Output '' -Write-Output 'Usage: ./update-agent-context.ps1 [claude|gemini|copilot|cursor|qwen|opencode|windsurf|codex]' +Write-Output 'Usage: ./update-agent-context.ps1 [claude|gemini|copilot|cursor|qwen|opencode|windsurf|codex|zed]' diff --git a/src/specify_cli/__init__.py b/src/specify_cli/__init__.py index 4eb36fbdd..d486c3794 100644 --- a/src/specify_cli/__init__.py +++ b/src/specify_cli/__init__.py @@ -71,6 +71,7 @@ def _github_auth_headers(cli_token: str | None = None) -> dict: "opencode": "opencode", "codex": "Codex CLI", "windsurf": "Windsurf", + "zed": "Zed", } # Add script type choices SCRIPT_TYPE_CHOICES = {"sh": "POSIX Shell (bash/zsh)", "ps": "PowerShell"} @@ -746,7 +747,7 @@ def ensure_executable_scripts(project_path: Path, tracker: StepTracker | None = @app.command() def init( project_name: str = typer.Argument(None, help="Name for your new project directory (optional if using --here)"), - ai_assistant: str = typer.Option(None, "--ai", help="AI assistant to use: claude, gemini, copilot, cursor, qwen, opencode, codex, or windsurf"), + ai_assistant: str = typer.Option(None, "--ai", help="AI assistant to use: claude, gemini, copilot, cursor, qwen, opencode, codex, windsurf or zed"), script_type: str = typer.Option(None, "--script", help="Script type to use: sh or ps"), ignore_agent_tools: bool = typer.Option(False, "--ignore-agent-tools", help="Skip checks for AI agent tools like Claude Code"), no_git: bool = typer.Option(False, "--no-git", help="Skip git repository initialization"), @@ -760,7 +761,7 @@ def init( This command will: 1. Check that required tools are installed (git is optional) - 2. Let you choose your AI assistant (Claude Code, Gemini CLI, GitHub Copilot, Cursor, Qwen Code, opencode, Codex CLI, or Windsurf) + 2. Let you choose your AI assistant (Claude Code, Gemini CLI, GitHub Copilot, Cursor, Qwen Code, opencode, Codex CLI, Windsurf or Zed) 3. Download the appropriate template from GitHub 4. Extract the template to a new project directory or current directory 5. Initialize a fresh git repository (if not --no-git and no existing repo) @@ -776,6 +777,7 @@ def init( specify init my-project --ai opencode specify init my-project --ai codex specify init my-project --ai windsurf + specify init my-project --ai zed specify init --ignore-agent-tools my-project specify init --here --ai claude specify init --here --ai codex From 893484b627ee77663dbd458bc2adfefcb125c0a5 Mon Sep 17 00:00:00 2001 From: JoshAS Date: Sun, 21 Sep 2025 00:17:11 +0100 Subject: [PATCH 2/5] fix: remove unused directory creation --- .github/workflows/scripts/create-release-packages.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/scripts/create-release-packages.sh b/.github/workflows/scripts/create-release-packages.sh index 8286a2eca..fdb0493c1 100644 --- a/.github/workflows/scripts/create-release-packages.sh +++ b/.github/workflows/scripts/create-release-packages.sh @@ -215,7 +215,6 @@ build_variant() { mkdir -p "$base_dir/.codex/commands" generate_commands codex md "\$ARGUMENTS" "$base_dir/.codex/commands" "$script" ;; zed) - mkdir -p "$base_dir/.zed/commands" generate_commands zed md "\$ARGUMENTS" "$base_dir/" "$script" ;; esac ( cd "$base_dir" && zip -r "../spec-kit-template-${agent}-${script}-${NEW_VERSION}.zip" . ) From 3e4493acf73074232e5766e3e0f0abc4b03c4517 Mon Sep 17 00:00:00 2001 From: JoshAS Date: Sun, 21 Sep 2025 00:43:25 +0100 Subject: [PATCH 3/5] fix: zed template structure --- .../scripts/create-release-packages.sh | 40 ++++++++++++------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/.github/workflows/scripts/create-release-packages.sh b/.github/workflows/scripts/create-release-packages.sh index fdb0493c1..6fac7aca8 100644 --- a/.github/workflows/scripts/create-release-packages.sh +++ b/.github/workflows/scripts/create-release-packages.sh @@ -40,9 +40,20 @@ generate_commands() { if [[ "$agent" == "zed" ]]; then local rules_file="$output_dir/.rules" - echo "# Zed rules file" > "$rules_file" - echo "# Whenever called with / context, follow these rules" >> "$rules_file" - echo "" >> "$rules_file" + # Define slash command handling rules + cat > "$rules_file" <<'EOF' +# Agent Slash Commands +When prompted with a slash command (/command), you should consume the definition below and execute accordingly. +If no definition is found, respond that the command is undefined or prompt the user for clarification. + +# Template for defining new slash commands: +# +# name: +# description: +# body: +# + +EOF # Iterate over all templates and append rules for template in templates/commands/*.md; do @@ -65,24 +76,25 @@ generate_commands() { # Replace placeholders body=$(printf '%s\n' "$file_content" | sed "s|{SCRIPT}|${script_command}|g") - # Remove scripts section from frontmatter - body=$(printf '%s\n' "$body" | awk ' - /^---$/ { print; if (++dash_count == 1) in_frontmatter=1; else in_frontmatter=0; next } - in_frontmatter && /^scripts:$/ { skip_scripts=1; next } - in_frontmatter && /^[a-zA-Z].*:/ && skip_scripts { skip_scripts=0 } - in_frontmatter && skip_scripts && /^[[:space:]]/ { next } - { print } + # Remove entire frontmatter + body=$(printf '%s\n' "$file_content" | awk ' + BEGIN{skip=0} + /^---$/ {skip = !skip; next} + !skip {print} ') # Apply other substitutions body=$(printf '%s\n' "$body" | sed "s/{ARGS}/$arg_format/g" | sed "s/__AGENT__/$agent/g" | rewrite_paths) - # Append this template’s rule to the .rules file + # Append the command as a slash-command block following the template { - echo "---" + echo "" + echo "name: $name" echo "description: $description" - echo "---" - echo "$body" + echo "body: |" + # Indent body by two spaces for YAML-style multiline + printf '%s\n' "$body" | sed 's/^/ /' + echo "" echo "" } >> "$rules_file" done From 48bf58231b301a844049c3bf00a19937a4f6b900 Mon Sep 17 00:00:00 2001 From: JoshAS Date: Sun, 21 Sep 2025 01:18:57 +0100 Subject: [PATCH 4/5] Update README.md --- README.md | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b2dbca540..e9b199cf4 100644 --- a/README.md +++ b/README.md @@ -108,7 +108,7 @@ The `specify` command supports the following options: | Argument/Option | Type | Description | |------------------------|----------|------------------------------------------------------------------------------| | `` | Argument | Name for your new project directory (optional if using `--here`) | -| `--ai` | Option | AI assistant to use: `claude`, `gemini`, `copilot`, `cursor`, `qwen`, `opencode`, or `windsurf` | +| `--ai` | Option | AI assistant to use: `claude`, `gemini`, `copilot`, `cursor`, `qwen`, `opencode`, `windsurf` or `zed` | | `--script` | Option | Script variant to use: `sh` (bash/zsh) or `ps` (PowerShell) | | `--ignore-agent-tools` | Flag | Skip checks for AI agent tools like Claude Code | | `--no-git` | Flag | Skip git repository initialization | @@ -132,6 +132,9 @@ specify init my-project --ai cursor # Initialize with Windsurf support specify init my-project --ai windsurf +# Initialize with Zed support +specify init my-project --ai zed + # Initialize with PowerShell scripts (Windows/cross-platform) specify init my-project --ai copilot --script ps @@ -163,6 +166,13 @@ After running `specify init`, your AI coding agent will have access to these sla | `/tasks` | Generate actionable task lists for implementation | | `/implement` | Execute all tasks to build the feature according to the plan | +> **Note for Zed users:** Zed does not currently support agent slash-commands. Instead, provide the command name along with your arguments in a single message. +> **Example:** +> ``` +> specify A todo list CLI app +> ``` +> This will trigger the `/specify` workflow for Zed with the provided feature description. + ## 📚 Core philosophy Spec-Driven Development is a structured process that emphasizes: @@ -209,7 +219,7 @@ Our research and experimentation focus on: ## 🔧 Prerequisites - **Linux/macOS** (or WSL2 on Windows) -- AI coding agent: [Claude Code](https://www.anthropic.com/claude-code), [GitHub Copilot](https://code.visualstudio.com/), [Gemini CLI](https://github.com/google-gemini/gemini-cli), [Cursor](https://cursor.sh/), [Qwen CLI](https://github.com/QwenLM/qwen-code), [opencode](https://opencode.ai/), [Codex CLI](https://github.com/openai/codex), or [Windsurf](https://windsurf.com/) +- AI coding agent: [Claude Code](https://www.anthropic.com/claude-code), [GitHub Copilot](https://code.visualstudio.com/), [Gemini CLI](https://github.com/google-gemini/gemini-cli), [Cursor](https://cursor.sh/), [Qwen CLI](https://github.com/QwenLM/qwen-code), [opencode](https://opencode.ai/), [Codex CLI](https://github.com/openai/codex), [Windsurf](https://windsurf.com/) or [Zed](https://zed.dev/) - [uv](https://docs.astral.sh/uv/) for package management - [Python 3.11+](https://www.python.org/downloads/) - [Git](https://git-scm.com/downloads) @@ -251,6 +261,7 @@ specify init --ai qwen specify init --ai opencode specify init --ai codex specify init --ai windsurf +specify init --ai zed # Or in current directory: specify init --here --ai claude specify init --here --ai codex From fc58d05a9aa138e87e626d26e204a9091d093942 Mon Sep 17 00:00:00 2001 From: JoshAS Date: Sun, 21 Sep 2025 01:39:38 +0100 Subject: [PATCH 5/5] Update create-release-packages.sh Updates command prompting in .rules to fix an issue where Zed blocked executions sometimes thinking we were bypassing restrictions on commands. --- .../workflows/scripts/create-release-packages.sh | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/scripts/create-release-packages.sh b/.github/workflows/scripts/create-release-packages.sh index 6fac7aca8..daf71a2fc 100644 --- a/.github/workflows/scripts/create-release-packages.sh +++ b/.github/workflows/scripts/create-release-packages.sh @@ -42,16 +42,16 @@ generate_commands() { local rules_file="$output_dir/.rules" # Define slash command handling rules cat > "$rules_file" <<'EOF' -# Agent Slash Commands -When prompted with a slash command (/command), you should consume the definition below and execute accordingly. +# Agent Commands +When prompted with a command, you should consume the definition below and execute accordingly. If no definition is found, respond that the command is undefined or prompt the user for clarification. -# Template for defining new slash commands: -# +# Template for defining new commands: +# # name: # description: # body: -# +# EOF @@ -86,15 +86,15 @@ EOF # Apply other substitutions body=$(printf '%s\n' "$body" | sed "s/{ARGS}/$arg_format/g" | sed "s/__AGENT__/$agent/g" | rewrite_paths) - # Append the command as a slash-command block following the template + # Append the command as a command block following the template { - echo "" + echo "" echo "name: $name" echo "description: $description" echo "body: |" # Indent body by two spaces for YAML-style multiline printf '%s\n' "$body" | sed 's/^/ /' - echo "" + echo "" echo "" } >> "$rules_file" done