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..daf71a2fc 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,72 @@ 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" + # Define slash command handling rules + cat > "$rules_file" <<'EOF' +# 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 commands: +# +# name: +# description: +# body: +# + +EOF + + # 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 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 the command as a command block following the template + { + 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 "" + } >> "$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 +226,15 @@ build_variant() { codex) mkdir -p "$base_dir/.codex/commands" generate_commands codex md "\$ARGUMENTS" "$base_dir/.codex/commands" "$script" ;; + zed) + 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/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 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